[
  {
    "path": ".deepsource.toml",
    "content": "version = 1\n\ntest_patterns = [\"**/*_test.go\"]\n\nexclude_patterns = [\n    \"_examples/**\",\n    \"_benchmarks/**\",\n    \".github/**\"\n]\n\n[[analyzers]]\nname = \"go\"\nenabled = true\n\n  [analyzers.meta]\n  import_paths = [\"github.com/kataras/iris/v12\"]\n"
  },
  {
    "path": ".fossa.yml",
    "content": "version: 3\ncli:\n  server: https://app.fossa.com\n  fetcher: git\n  package: github.com/kataras/iris\n  project: github.com/kataras/iris\nanalyze:\n  modules:\n    - name: iris\n      type: go\n      target: .\n      path: ."
  },
  {
    "path": ".gitattributes",
    "content": "*.go linguist-language=Go\n_examples/* linguist-documentation\n_benchmarks/* linguist-documentation\ngo.sum linguist-generated\n# Set the default behavior, in case people don't have core.autocrlf set.\n# if from windows: \n# git config --global core.autocrlf true\n# if from unix:\n# git config --global core.autocrlf input\n# https://help.github.com/articles/dealing-with-line-endings/#per-repository-settings\n* text=auto\n# ignore perms\n# git config core.filemode false\n"
  },
  {
    "path": ".github/CODEOWNERS",
    "content": "# These owners will be the default owners for everything in the repo.\r\n*       @kataras\r\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# patreon: # Replace with a single Patreon username\n# open_collective: # Replace with a single Open Collective username\n# ko_fi: # Replace with a single Ko-fi username\n# tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel\n# custom: https://iris-go.com/donate # Replace with a single custom sponsorship URL\ngithub: kataras"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: \"[BUG]\"\nlabels: type:bug\nassignees: kataras\n\n---\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**To Reproduce**\nSteps to reproduce the behavior:\n1. [...]\n\n**Expected behavior**\nA clear and concise description of what you expected to happen.\n\n**Screenshots**\nIf applicable, add screenshots to help explain your problem.\n\n**Desktop (please complete the following information):**\n - OS: [e.g. ubuntu, windows]\n\n**iris.Version**\n- e.g. v12.2.5 or main\n\nPlease make sure the bug is reproducible over the `main` branch:\n\n```sh\n$ cd PROJECT\n$ go get -u github.com/kataras/iris/v12@main\n$ go run .\n```\n\n**Additional context**\nAdd any other context about the problem here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/custom.md",
    "content": "---\nname: Custom issue template\nabout: Other\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\nDescribe the issue you are facing or ask for help\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: \"[FEATURE REQUEST]\"\nlabels: type:idea\nassignees: kataras\n\n---\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE.md",
    "content": "Examples for the Iris project can be found at\r\n<https://github.com/kataras/iris/tree/main/_examples>.\r\n\r\nDocumentation for the Iris project can be found at\r\n<https://www.iris-go.com/docs>.\r\n\r\nLove iris? Please consider supporting the project:\r\n👉  https://iris-go.com/donate\r\n\r\nCare to be part of a larger community? Fill our user experience form:\r\n👉  https://goo.gl/forms/lnRbVgA6ICTkPyk02\r\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "# We'd love to see more contributions\r\n\r\nRead how you can [contribute to the project](https://github.com/kataras/iris/blob/main/CONTRIBUTING.md).\r\n\r\n> Please attach an [issue](https://github.com/kataras/iris/issues) link which your PR solves otherwise your work may be rejected.\r\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: \"gomod\"\n    directory: \"/\"\n    exclude-paths:\n      - \"_examples/\"\n    schedule:\n      interval: \"monthly\"\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"monthly\""
  },
  {
    "path": ".github/scripts/setup_examples_test.bash",
    "content": "#!/usr/bin/env bash\n\nfor f in ../../_examples/*; do\n    if [ -d \"$f\" ]; then\n        # Will not run if no directories are available\n        go mod init\n        go get -u github.com/kataras/iris/v12@main\n        go mod download\n        go run .\n    fi\ndone\n\n# git update-index --chmod=+x ./.github/scripts/setup_examples_test.bash"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: CI\n\non:\n  push:\n    branches: [main]\n  pull_request:\n    branches: [main]\n\npermissions:\n  contents: read\n\njobs:\n\n  test:\n    name: Test\n    runs-on: ubuntu-latest\n\n    strategy:\n      matrix:\n        go_version: [1.25.x]\n    steps:\n\n    - name: Check out code into the Go module directory\n      uses: actions/checkout@v6\n\n    - name: Set up Go 1.x\n      uses: actions/setup-go@v6\n      with:\n        go-version-file: './go.mod'\n        check-latest: true\n    - run: go version\n\n    - name: Test\n      run: go test -v ./...\n\n    - name: Setup examples for testing\n      run: ./.github/scripts/setup_examples_test.bash\n\n    - name: Test examples\n      continue-on-error: true\n      working-directory: _examples\n      run: go test -v -mod=mod -cover -race ./...\n"
  },
  {
    "path": ".github/workflows/codeql-analysis.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: [ main ]\n  pull_request:\n    # The branches below must be a subset of the branches above\n    branches: [ main ]\n  schedule:\n    - cron: '24 11 * * 6'\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    strategy:\n      fail-fast: false\n      matrix:\n        language: [ 'go']\n        # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]\n        # Learn more:\n        # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed\n\n    steps:\n    - name: Checkout repository\n      uses: actions/checkout@v6\n\n    - name: Set up Go 1.x\n      uses: actions/setup-go@v6\n      with:\n        go-version-file: './go.mod'\n        check-latest: true\n    - run: go version\n\n    # Initializes the CodeQL tools for scanning.\n    - name: Initialize CodeQL\n      uses: github/codeql-action/init@v3\n      with:\n        languages: ${{ matrix.language }}\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        # queries: ./path/to/local/query, your-org/your-repo/queries@main\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@v3\n\n    # ℹ️ Command-line programs to run using the OS shell.\n    # 📚 https://git.io/JvXDl\n\n    # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines\n    #    and modify them (or add more) to build your code if your project\n    #    uses a compiled language\n\n    #- run: |\n    #   make bootstrap\n    #   make release\n\n    - name: Perform CodeQL Analysis\n      uses: github/codeql-action/analyze@v3\n"
  },
  {
    "path": ".gitignore",
    "content": ".idea\n.vscode\n.directory\ncoverage.out\npackage-lock.json\naccess.log\nnode_modules\nissue-*/\ninternalcode-*/\n/_examples/feature-*/\n_examples/**/uploads/*\n_issues/**\n.DS_STORE\n"
  },
  {
    "path": "AUTHORS",
    "content": "# This is the official list of Iris authors for copyright\r\n# purposes.\r\n\r\nGerasimos Maropoulos <kataras2006@hotmail.com>\r\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\r\n\r\n## Our Pledge\r\n\r\nIn the interest of fostering an open and welcoming environment, we as\r\ncontributors and maintainers pledge to making participation in our project and\r\nour community a harassment-free experience for everyone, regardless of age, body\r\nsize, disability, ethnicity, gender identity and expression, level of experience,\r\nnationality, personal appearance, race, religion, or sexual identity and\r\norientation.\r\n\r\n## Our Standards\r\n\r\nExamples of behavior that contributes to creating a positive environment\r\ninclude:\r\n\r\n* Using welcoming and inclusive language\r\n* Being respectful of differing viewpoints and experiences\r\n* Gracefully accepting constructive criticism\r\n* Focusing on what is best for the community\r\n* Showing empathy towards other community members\r\n\r\nExamples of unacceptable behavior by participants include:\r\n\r\n* The use of sexualized language or imagery and unwelcome sexual attention or\r\nadvances\r\n* Trolling, insulting/derogatory comments, and personal or political attacks\r\n* Public or private harassment\r\n* Publishing others' private information, such as a physical or electronic\r\n  address, without explicit permission\r\n* Other conduct which could reasonably be considered inappropriate in a\r\n  professional setting\r\n\r\n## Our Responsibilities\r\n\r\nProject maintainers are responsible for clarifying the standards of acceptable\r\nbehavior and are expected to take appropriate and fair corrective action in\r\nresponse to any instances of unacceptable behavior.\r\n\r\nProject maintainers have the right and responsibility to remove, edit, or\r\nreject comments, commits, code, wiki edits, issues, and other contributions\r\nthat are not aligned to this Code of Conduct, or to ban temporarily or\r\npermanently any contributor for other behaviors that they deem inappropriate,\r\nthreatening, offensive, or harmful.\r\n\r\n## Scope\r\n\r\nThis Code of Conduct applies both within project spaces and in public spaces\r\nwhen an individual is representing the project or its community. Examples of\r\nrepresenting a project or community include using an official project e-mail\r\naddress, posting via an official social media account, or acting as an appointed\r\nrepresentative at an online or offline event. Representation of a project may be\r\nfurther defined and clarified by project maintainers.\r\n\r\n## Enforcement\r\n\r\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\r\nreported by contacting the project team at kataras2006@hotmail.com. All\r\ncomplaints will be reviewed and investigated and will result in a response that\r\nis deemed necessary and appropriate to the circumstances. The project team is\r\nobligated to maintain confidentiality with regard to the reporter of an incident.\r\nFurther details of specific enforcement policies may be posted separately.\r\n\r\nProject maintainers who do not follow or enforce the Code of Conduct in good\r\nfaith may face temporary or permanent repercussions as determined by other\r\nmembers of the project's leadership.\r\n\r\n## Attribution\r\n\r\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,\r\navailable at [http://contributor-covenant.org/version/1/4][version]\r\n\r\n[homepage]: http://contributor-covenant.org\r\n[version]: http://contributor-covenant.org/version/1/4/"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing\r\n\r\nFirst of all read our [Code of Conduct](https://github.com/kataras/iris/blob/main/CODE_OF_CONDUCT.md).\r\n\r\n## PR\r\n\r\n1. Open a new [issue](https://github.com/kataras/iris/issues/new)\r\n    * Write version of your local Iris.\r\n    * Write version of your local Go programming language.\r\n    * Describe your problem, what did you expect to see and what you see instead.\r\n        * If it's a feature request, describe your idea as better as you can\r\n          * optionally, navigate to the [chat](https://chat.iris-go.com) to push other members to participate and share their thoughts about your brilliant idea.\r\n2. Fork the [repository](https://github.com/kataras/iris).\r\n3. Make your changes.\r\n4. Compare & Push the PR from [here](https://github.com/kataras/iris/compare).\r\n\r\n## Translate\r\n\r\nWe need your help with translations into your native language.\r\n\r\nIris needs your help, please think about contributing to the translation of the [README](README.md) and https://iris-go.com, you will be rewarded.\r\n\r\nInstructions can be found at: https://github.com/kataras/iris/issues/796\r\n\r\n## Share\r\n\r\n### Writing\r\n\r\nWrite an article about Iris in https://medium.com, https://dev.to or if you're being a hackathon at https://hackernoon.com and send us the link on iris-go@outlook.com.\r\n\r\n### Social networks\r\n\r\nIf you're part of any social network, do a post(or tweet if twitter) about Iris and what you love about it, many examples can be found, the most recent one is [that](https://twitter.com/DorMoshe/status/1154486477247508480).\r\n"
  },
  {
    "path": "FAQ.md",
    "content": "# FAQ\n\n## [![iris](https://img.shields.io/badge/iris-powered-2196f3.svg?style=for-the-badge)](https://github.com/kataras/iris)\n\nAdd a `badge` to your open-source projects powered by [Iris](https://iris-go.com) by pasting the below code snippet to the project repo's README.md:\n\n```md\n[![iris](https://img.shields.io/badge/iris-powered-2196f3.svg?style=for-the-badge)](https://github.com/kataras/iris)\n```\n\n## Editors & IDEs Extensions\n\n### Visual Studio Code <a href=\"https://marketplace.visualstudio.com/items?itemName=kataras2006.iris\"><img src=\"https://upload.wikimedia.org/wikipedia/commons/thumb/2/2d/Visual_Studio_Code_1.18_icon.svg/2000px-Visual_Studio_Code_1.18_icon.svg.png\" height=\"20px\" width=\"20px\" /></a>\n\n<https://marketplace.visualstudio.com/items?itemName=kataras2006.iris>\n\n> Please feel free to list your own Iris extension(s) here by [PR](https://github.com/kataras/iris/pulls)\n\n## How to upgrade\n\n```sh\ngo get github.com/kataras/iris/v12@latest\n```\n\nGo version 1.20 and above is required.\n\n## Learning\n\nMore than 280 practical examples, tutorials and articles at:\n\n- https://www.iris-go.com/docs\n- https://www.iris-go.com/#ebookDonateForm\n<!-- - https://github.com/kataras/iris/wiki/Starter-kits -->\n- https://github.com/kataras/iris/tree/main/_examples\n- https://pkg.go.dev/github.com/kataras/iris/v12@main\n\n## Active development mode\n\nIris may have reached version 12, but we're not stopping there. We have many feature ideas on our board that we're anxious to add and other innovative web development solutions that we're planning to build into Iris.\n\n## Can I find a job if I learn how to use Iris?\n\nYes, not only because you will learn Golang in the same time, but there are some positions\nopen for Iris-specific developers the time we speak.\n\nGo to our facebook page, like it and receive notifications about new job offers, we already have couple of them stay at the top of the page: https://www.facebook.com/iris.framework\n\n## Do we have a Community chat?\n\nYes, https://chat.iris-go.com\n\n## How is the development of Iris economically supported?\n\nBy people like you, who help us by donating small or large amounts of money.\n\nHelp this project deliver awesome and unique features with the highest possible code quality by donating any amount via [PayPal or Stripe](https://iris-go.com/donate). Your name will be published [here](https://www.iris-go.com/#review) after your approval via e-mail.\n"
  },
  {
    "path": "HISTORY.md",
    "content": "# Changelog\r\n\r\n### Looking for free and real-time support?\r\n\r\n    https://github.com/kataras/iris/issues\r\n    https://chat.iris-go.com\r\n\r\n### Looking for previous versions?\r\n\r\n    https://github.com/kataras/iris/releases\r\n\r\n### Want to be hired?\r\n\r\n    https://facebook.com/iris.framework\r\n\r\n### Should I upgrade my Iris?\r\n\r\nDevelopers are not forced to upgrade if they don't really need it. Upgrade whenever you feel ready.\r\n\r\n**How to upgrade**: Open your command-line and execute this command: `go get github.com/kataras/iris/v12@latest` and `go mod tidy -compat=1.21`.\r\n\r\n# Next\r\n\r\nChanges apply to `main` branch.\r\n\r\n# Thu, 25 April 2024 | v12.2.11\r\n\r\nDear Iris Community,\r\n\r\nYou might have noticed a recent lull in activity on the Iris repository. I want to assure you that this silence is not without reason. For the past **3-4 months**, I've been diligently working on the next major release of Iris.\r\n\r\nThis upcoming version is poised to be a significant leap forward, fully embracing the **Generics** feature introduced in Go. We're not just stopping at Generics, though. Expect a suite of **new features**, **enhancements**, and **optimizations** that will elevate your development experience to new heights.\r\n\r\nMy journey with Go spans over **8 years**, and with each year, my expertise and understanding of the language deepen. This accumulated knowledge is being poured into Iris, ensuring that the framework not only evolves with the language but also with the community's growing needs.\r\n\r\nStay tuned for more updates, and thank you for your continued support and patience. The wait will be worth it.\r\n\r\nWarm regards,<br/>\r\nGerasimos (Makis) Maropoulos\r\n\r\n### This is the last release for the version 12 family.\r\n\r\n- Security improvements and dependencies upgrade.\r\n\r\n- New `Application/Party.MiddlewareExists(handlerNameOrHandler)` method added, example:\r\n\r\n```go\r\npackage main\r\n\r\nimport (\r\n\t\"fmt\"\r\n\r\n\t\"github.com/kataras/iris/v12\"\r\n\t\"github.com/kataras/iris/v12/x/errors\"\r\n)\r\n\r\nfunc main() {\r\n\tapp := iris.New()\r\n\r\n\tapp.UseRouter(errors.RecoveryHandler)\r\n\r\n\tif app.MiddlewareExists(errors.RecoveryHandler) { // <- HERE.\r\n\t\tfmt.Println(\"errors.RecoveryHandler exists\")\r\n\t}\r\n\t// OR:\r\n\t// if app.MiddlewareExists(\"iris.errors.recover\") {\r\n\t// \tfmt.Println(\"Iris.Errors.Recovery exists\")\r\n\t// }\r\n\r\n\tapp.Get(\"/\", indexHandler)\r\n\r\n\tapp.Listen(\":8080\")\r\n}\r\n\r\nfunc indexHandler(ctx iris.Context) {\r\n\tpanic(\"an error here\")\r\n\tctx.HTML(\"<h1>Index</h1>\")\r\n}\r\n\r\n```\r\n- New `x/errors.Intercept(func(ctx iris.Context, req *CreateRequest, resp *CreateResponse) error{ ... })` package-level function.\r\n\r\n```go\r\nfunc main() {\r\n    app := iris.New()\r\n\r\n    // Create a new service and pass it to the handlers.\r\n    service := new(myService)\r\n\r\n    app.Post(\"/\", errors.Intercept(responseHandler), errors.CreateHandler(service.Create))\r\n\r\n    // [...]\r\n}\r\n\r\nfunc responseHandler(ctx iris.Context, req *CreateRequest, resp *CreateResponse) error {\r\n    fmt.Printf(\"intercept: request got: %+v\\nresponse sent: %#+v\\n\", req, resp)\r\n    return nil\r\n}\r\n```\r\n\r\n- Rename `x/errors/ContextValidator.ValidateContext(iris.Context) error` to `x/errors/RequestHandler.HandleRequest(iris.Context) error`.\r\n\r\n# Thu, 18 Jan 2024 | v12.2.10\r\n\r\n- Simplify the `/core/host` subpackage and remove its `DeferFlow` and `RestoreFlow` methods. These methods are replaced with: `Supervisor.Configure(host.NonBlocking())` before `Serve` and ` Supervisor.Wait(context.Context) error` after `Serve`.\r\n- Fix internal `trimHandlerName` and other minor stuff.\r\n- New `iris.NonBlocking()` configuration option to run the server without blocking the main routine, `Application.Wait(context.Context) error` method can be used to block and wait for the server to be up and running. Example:\r\n\r\n```go\r\nfunc main() {\r\n    app := iris.New()\r\n    app.Get(\"/\", func(ctx iris.Context) {\r\n        ctx.Writef(\"Hello, %s!\", \"World\")\r\n    })\r\n\r\n    app.Listen(\":8080\", iris.NonBlocking(), iris.WithoutServerError(iris.ErrServerClosed))\r\n\r\n    ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)\r\n    defer cancel()\r\n\r\n    if err := app.Wait(ctx); err != nil {\r\n        log.Fatal(err)\r\n    }\r\n\r\n    // [Server is up and running now, you may continue with other functions below].\r\n}\r\n```\r\n\r\n- Add `x/mathx.RoundToInteger` math helper function.\r\n\r\n# Wed, 10 Jan 2024 | v12.2.9\r\n\r\n- Add `x/errors.RecoveryHandler` package-level function.\r\n- Add `x/errors.Validation` package-level function to add one or more validations for the request payload before a service call of the below methods.\r\n- Add `x/errors.Handler`, `CreateHandler`, `NoContentHandler`, `NoContentOrNotModifiedHandler` and `ListHandler` ready-to-use handlers for service method calls to Iris Handler.\r\n- Add `x/errors.List` package-level function to support `ListObjects(ctx context.Context, opts pagination.ListOptions, f Filter) ([]Object, int64, error)` type of service calls.\r\n- Simplify how validation errors on `/x/errors` package works. A new `x/errors/validation` sub-package added to make your life easier (using the powerful Generics feature).\r\n- Add `x/errors.OK`, `Create`, `NoContent` and `NoContentOrNotModified` package-level generic functions as custom service method caller helpers. Example can be found [here](_examples/routing/http-wire-errors/service/main.go).\r\n- Add `x/errors.ReadPayload`, `ReadQuery`, `ReadPaginationOptions`, `Handle`, `HandleCreate`, `HandleCreateResponse`, `HandleUpdate` and `HandleDelete` package-level functions as helpers for common actions.\r\n- Add `x/jsonx.GetSimpleDateRange(date, jsonx.WeekRange, time.Monday, time.Sunday)` which returns all dates between the given range and start/end weekday values for WeekRange.\r\n- Add `x/timex.GetMonthDays` and `x/timex.GetMonthEnd` functions.\r\n- Add `iris.CookieDomain` and `iris.CookieOverride` cookie options to handle [#2309](https://github.com/kataras/iris/issues/2309).\r\n- New `x/errors.ErrorCodeName.MapErrorFunc`, `MapErrors`, `Wrap` methods and `x/errors.HandleError` package-level function.\r\n\r\n# Sun, 05 Nov 2023 | v12.2.8\r\n\r\n- A new way to customize the handler's parameter among with the `hero` and `mvc` packages. New `iris.NewContextWrapper` and\r\n `iris.NewContextPool` methods were added to wrap a handler (`.Handler`, `.Handlers`, `.HandlerReturnError`, `HandlerReturnDuration`, `Filter` and `FallbackViewFunc` methods) and use a custom context instead of the iris.Context directly. Example at: https://github.com/kataras/iris/tree/main/_examples/routing/custom-context.\r\n\r\n- The `cache` sub-package has an update, 4 years after:\r\n\r\n    - Add support for custom storage on `cache` package, through the `Handler#Store` method.\r\n    - Add support for custom expiration duration on `cache` package, trough the `Handler#MaxAge` method.\r\n    - Improve the overral performance of the `cache` package.\r\n    - The `cache.Handler` input and output arguments remain as it is.\r\n    - The `cache.Cache` input argument changed from `time.Duration` to `func(iris.Context) time.Duration`.\r\n\r\n# Mon, 25 Sep 2023 | v12.2.7\r\n\r\nMinor bug fixes and support of multiple `block` and `define` directives in multiple layouts and templates in the `Blocks` view engine.\r\n\r\n# Mon, 21 Aug 2023 | v12.2.5\r\n\r\n- Add optional `Singleton() bool` method to controllers to mark them as singleton, will panic with a specific error if a controller expects dynamic dependencies. This behavior is idendical to the app-driven `app.EnsureStaticBindings()`.\r\n\r\n- Non-zero fields of a controller that are marked as ignored, with `ignore:\"true\"` field tag, they are not included in the dependencies at all now.\r\n\r\n- Re-add error log on context rich write (e.g. JSON) failures when the application is running under debug mode (with `app.Logger().SetLevel(\"debug\")`) and there is no a registered context error handler at place.\r\n\r\n- `master` branch finally renamed to `main`. Don't worry GitHub will still navigate any `master` request to `main` automatically. Examples, Documentation and other Pages are refactored too.\r\n\r\n# Sat, 12 Aug 2023 | v12.2.4\r\n\r\n- Add new `iris.WithDynamicHandler` option (`EnableDynamicHandler` setting) to work with `iris.Application.RefreshRouter` method. It allows to change the entire router while your server is up and running. Handles [issue #2167](https://github.com/kataras/iris/issues/2167). Example at [_examples/routing/route-state/main.go](_examples/routing/route-state/main.go).\r\n\r\n> We jumped v12.2.2 and v12.2.3.\r\n\r\n# Mon, 17 July 2023 | v12.2.1\r\n\r\n- Add `mvc.Application.EnableStructDependents()` method to handle [#2158](https://github.com/kataras/iris/issues/2158).\r\n\r\n- Fix [iris-premium#17](https://github.com/kataras/iris-premium/issues/17).\r\n\r\n- Replace [russross/blackfriday](github.com/russross/blackfriday/v2) with [gomarkdown](https://github.com/gomarkdown/markdown) as requested at [#2098](https://github.com/kataras/iris/issues/2098).\r\n\r\n- Add `mvc.IgnoreEmbedded` option to handle [#2103](https://github.com/kataras/iris/issues/2103). Example Code:\r\n\r\n```go\r\nfunc configure(m *mvc.Application) {\r\n\tm.Router.Use(cacheHandler)\r\n\tm.Handle(&exampleController{\r\n\t\ttimeFormat: \"Mon, Jan 02 2006 15:04:05\",\r\n\t}, mvc.IgnoreEmbedded /* BaseController.GetDoSomething will not be parsed at all */)\r\n}\r\n\r\ntype BaseController struct {\r\n\tCtx iris.Context\r\n}\r\n\r\nfunc (c *BaseController) GetDoSomething(i any) error {\r\n\treturn nil\r\n}\r\n\r\ntype exampleController struct {\r\n\tBaseController\r\n\r\n\ttimeFormat string\r\n}\r\n\r\nfunc (c *exampleController) Get() string {\r\n\tnow := time.Now().Format(c.timeFormat)\r\n\treturn \"last time executed without cache: \" + now\r\n}\r\n```\r\n\r\n- Add `LoadKV` method on `Iris.Application.I18N` instance. It should be used when no locale files are available. It loads locales via pure Go Map (or database decoded values).\r\n\r\n- Remove [ace](https://github.com/eknkc/amber) template parser support, as it was discontinued by its author more than five years ago.\r\n\r\n# Sa, 11 March 2023 | v12.2.0\r\n\r\nThis release introduces new features and some breaking changes.\r\nThe codebase for Dependency Injection, Internationalization and localization and more have been simplified a lot (fewer LOCs and easier to read and follow up).\r\n\r\n## 24 Dec 2022\r\n\r\nAll new features have been tested in production and seem to work fine. Fixed all reported and reproducible bugs. The `v12.2.0-beta7` is the latest beta release of v12.2.0. Expect the final public and stable release of v12.2.0 shortly after February 2023.\r\n\r\n## Fixes and Improvements\r\n \r\n- Add `iris.TrimParamFilePart` to handle cases like [#2024](https://github.com/kataras/iris/issues/2024) and improve the [_examples/routing/dynamic-path/main.go](_examples/routing/dynamic-path/main.go#L356) example to include that case as well.\r\n\r\n- **Breaking-change**: HTML template functions `yield`, `part`, `partial`, `partial_r` and `render` now accept (and require for some cases) a second argument of the binding data context too. Convert: `{{ yield }}` to `{{ yield . }}`, `{{ render \"templates/mytemplate.html\" }}` to `{{ render \"templates/mytemplate.html\" . }}`, `{{ partial \"partials/mypartial.html\" }}` to `{{ partial \"partials/mypartial.html\" . }}` and so on.\r\n\r\n- Add new `URLParamSeparator` to the configuration. Defaults to \",\" but can be set to an empty string to disable splitting query values on `Context.URLParamSlice` method.\r\n\r\n- [PR #1992](https://github.com/kataras/iris/pull/1992): Added support for third party packages on [httptest](https://github.com/kataras/iris/tree/main/httptest). An example using 3rd-party module named [Ginkgo](github.com/onsi/ginkgo) can be found [here](https://github.com/kataras/iris/blob/main/_examples/testing/ginkgotest). \r\n\r\n- Add `Context.Render` method for compatibility.\r\n\r\n- Support of embedded [locale files](https://github.com/kataras/iris/blob/main/_examples/i18n/template-embedded/main.go) using standard `embed.FS` with the new `LoadFS` function.\r\n- Support of direct embedded view engines (`HTML, Blocks, Django, Handlebars, Pug, Jet` and `Ace`) with `embed.FS` or `fs.FS` (in addition to `string` and `http.FileSystem` types).\r\n- Add support for `embed.FS` and `fs.FS` on `app.HandleDir`.\r\n\r\n- Add `iris.Patches()` package-level function to customize Iris Request Context REST (and more to come) behavior.\r\n- Minor fixes.\r\n\r\n- Enable setting a custom \"go-redis\" client through `SetClient` go redis driver method or `Client` struct field on sessions/database/redis driver as requested at [chat](https://chat.iris-go.com).\r\n- Ignore `\"csrf.token\"` form data key when missing on `ctx.ReadForm` by default as requested at [#1941](https://github.com/kataras/iris/issues/1941).\r\n\r\n- Fix [CVE-2020-5398](https://github.com/advisories/GHSA-8wx2-9q48-vm9r).\r\n\r\n- New `{x:weekday}` path parameter type, example code:\r\n\r\n```go\r\n// 0 to 7 (leading zeros don't matter) or \"Sunday\" to \"Monday\" or \"sunday\" to \"monday\".\r\n// http://localhost:8080/schedule/monday or http://localhost:8080/schedule/Monday or\r\n// http://localhost:8080/schedule/1 or http://localhost:8080/schedule/0001.\r\napp.Get(\"/schedule/{day:weekday}\", func(ctx iris.Context) {\r\n    day, _ := ctx.Params().GetWeekday(\"day\")\r\n    ctx.Writef(\"Weekday requested was: %v\\n\", day)\r\n})\r\n```\r\n\r\n- Make the `Context.JSON` method customizable by modifying the `context.WriteJSON` package-level function.\r\n- Add new `iris.NewGuide` which helps you build a simple and nice JSON API with services as dependencies and better design pattern.\r\n- Make `Context.Domain()` customizable by letting developers to modify the `Context.GetDomain` package-level function.\r\n- Remove Request Context-based Transaction feature as its usage can be replaced with just the Iris Context (as of go1.7+) and better [project](_examples/project) structure.\r\n- Fix [#1882](https://github.com/kataras/iris/issues/1882)\r\n- Fix [#1877](https://github.com/kataras/iris/issues/1877)\r\n- Fix [#1876](https://github.com/kataras/iris/issues/1876)\r\n\r\n- New `date` dynamic path parameter type. E.g. `/blog/{param:date}` matches to `\"/blog/2022/04/21\"`.\r\n\r\n- Add `iris.AllowQuerySemicolons` and `iris.WithoutServerError(iris.ErrURLQuerySemicolon)` to handle golang.org/issue/25192 as reported at: https://github.com/kataras/iris/issues/1875. \r\n- Add new `Application.SetContextErrorHandler` to globally customize the default behavior (status code 500 without body) on `JSON`, `JSONP`, `Protobuf`, `MsgPack`, `XML`, `YAML` and `Markdown` method call write errors instead of catching the error on each handler.\r\n- Add new [x/pagination](x/pagination/pagination.go) sub-package which supports generics code (go 1.18+).\r\n- Add new [middleware/modrevision](middleware/modrevision) middleware (example at [_examples/project/api/router.go]_examples/project/api/router.go).\r\n- Add `iris.BuildRevision` and `iris.BuildTime` to embrace the new go's 1.18 debug build information.\r\n\r\n- ~Add `Context.SetJSONOptions` to customize on a higher level the JSON options on `Context.JSON` calls.~ update: remains as it's, per JSON call.\r\n- Add new [auth](auth) sub-package which helps on any user type auth using JWT (access & refresh tokens) and a cookie (optional).\r\n\r\n- Add `Party.EnsureStaticBindings` which, if called, the MVC binder panics if a struct's input binding depends on the HTTP request data instead of a static dependency. This is useful to make sure your API crafted through `Party.PartyConfigure` depends only on struct values you already defined at `Party.RegisterDependency` == will never use reflection at serve-time (maximum performance).\r\n\r\n- Add a new [x/sqlx](/x/sqlx/) sub-package ([example](_examples/database/sqlx/main.go)).\r\n\r\n- Add a new [x/reflex](/x/reflex) sub-package. \r\n\r\n- Add `Context.ReadMultipartRelated` as requested at: [issues/#1787](https://github.com/kataras/iris/issues/1787).\r\n\r\n- Add `Container.DependencyMatcher` and `Dependency.Match` to implement the feature requested at [issues/#1842](https://github.com/kataras/iris/issues/1842).\r\n\r\n- Register [CORS middleware](middleware/cors) to the Application by default when `iris.Default()` is used instead of `iris.New()`.\r\n\r\n- Add [x/jsonx: DayTime](/x/jsonx/day_time.go) for JSON marshal and unmarshal of \"15:04:05\" (hour, minute, second).\r\n\r\n- Fix a bug of `WithoutBodyConsumptionOnUnmarshal` configurator and a minor dependency injection issue caused by the previous alpha version between 20 and 26 February of 2022.\r\n\r\n- New basic [cors middleware](middleware/cors).\r\n- New `httptest.NewServer` helper.\r\n- New [x/errors](x/errors) sub-package, helps with HTTP Wire Errors. Example can be found [here](_examples/routing/http-wire-errors/main.go).\r\n\r\n- New [x/timex](x/timex) sub-package, helps working with weekdays.\r\n\r\n- Minor improvements to the [JSON Kitchen Time](x/jsonx/kitchen_time.go).\r\n- A session database can now implement the `EndRequest(ctx *context.Context, session *Session)` method which will be fired at the end of the request-response lifecycle. \r\n- Improvements on JSON and ReadJSON when `Iris.Configuration.EnableOptimizations` is true. The request's Context is used whenever is necessary.\r\n- New [monitor](_examples/monitor/monitor-middleware/main.go) middleware.\r\n\r\n- New `RegisterRequestHandler` package-level and client methods to the new `x/client` package. Control or log the request-response lifecycle.\r\n- New `RateLimit` and `Debug` HTTP Client options to the new `x/client` package.\r\n\r\n- Push a security fix reported by [Kirill Efimov](https://github.com/kirill89) for older go runtimes.\r\n\r\n- New `Configuration.Timeout` and `Configuration.TimeoutMessage` fields. Use it to set HTTP timeouts. Note that your http server's (`Application.ConfigureHost`) Read/Write timeouts should be a bit higher than the `Configuration.Timeout` in order to give some time to http timeout handler to kick in and be able to send the `Configuration.TimeoutMessage` properly.\r\n\r\n- New `apps.OnApplicationRegistered` method which listens on new Iris applications hosted under the same binary. Use it on your `init` functions to configure Iris applications by any spot in your project's files.\r\n\r\n- `Context.JSON` respects any object implements the `easyjson.Marshaler` interface and renders the result using the [easyjon](https://github.com/mailru/easyjson)'s writer. **Set** the `Configuration.EnableProtoJSON` and `Configuration.EnableEasyJSON` to true in order to enable this feature.\r\n\r\n- minor: `Context` structure implements the standard go Context interface now (includes: Deadline, Done, Err and Value methods). Handlers can now just pass the `ctx iris.Context` as a shortcut of `ctx.Request().Context()` when needed.\r\n\r\n- New [x/jsonx](x/jsonx) sub-package for JSON type helpers.\r\n\r\n- New [x/mathx](x/mathx) sub-package for math related functions.\r\n\r\n- New [/x/client](x/client) HTTP Client sub-package.\r\n\r\n- New `email` builtin path parameter type. Example:\r\n\r\n```go\r\n// +------------------------+\r\n// | {param:email}           |\r\n// +------------------------+\r\n// Email + mx look up path parameter validation. Use it on production.\r\n\r\n// http://localhost:8080/user/kataras2006@hotmail.com -> OK\r\n// http://localhost:8080/user/b-c@invalid_domain      -> NOT FOUND\r\napp.Get(\"/user/{user_email:email}\", func(ctx iris.Context) {\r\n    email := ctx.Params().Get(\"user_email\")\r\n    ctx.WriteString(email)\r\n})\r\n\r\n// +------------------------+\r\n// | {param:mail}           |\r\n// +------------------------+\r\n// Simple email path parameter validation.\r\n\r\n// http://localhost:8080/user/kataras2006@hotmail.com    -> OK\r\n// http://localhost:8080/user/b-c@invalid_domainxxx1.com -> NOT FOUND\r\napp.Get(\"/user/{local_email:mail}\", func(ctx iris.Context) {\r\n    email := ctx.Params().Get(\"local_email\")\r\n    ctx.WriteString(email)\r\n})\r\n```\r\n\r\n- New `iris.IsErrEmptyJSON(err) bool` which reports whether the given \"err\" is caused by a\r\n`Context.ReadJSON` call when the request body didn't start with { (or it was totally empty). \r\n\r\nExample Code:\r\n\r\n```go\r\nfunc handler(ctx iris.Context) {\r\n    var opts SearchOptions\r\n    if err := ctx.ReadJSON(&opts); err != nil && !iris.IsErrEmptyJSON(err) {\r\n        ctx.StopWithJSON(iris.StatusBadRequest, iris.Map{\"message\": \"unable to parse body\"})\r\n        return\r\n    }\r\n\r\n    // [...continue with default values of \"opts\" struct if the client didn't provide some]\r\n}\r\n```\r\n\r\nThat means that the client can optionally set a JSON body.\r\n\t\r\n- New `APIContainer.EnableStrictMode(bool)` to disable automatic payload binding and panic on missing dependencies for exported struct'sfields or function's input parameters on MVC controller or hero function or PartyConfigurator.\r\n\r\n- New `Party.PartyConfigure(relativePath string, partyReg ...PartyConfigurator) Party` helper, registers a children Party like `Party` and `PartyFunc` but instead it accepts a structure value which may contain one or more of the dependencies registered by `RegisterDependency` or `ConfigureContainer().RegisterDependency` methods and fills the unset/zero exported struct's fields respectfully (useful when the api's dependencies amount are too much to pass on a function).\r\n\r\n- **New feature:** add the ability to set custom error handlers on path type parameters errors (existing or custom ones). Example Code:\r\n\r\n```go\r\napp.Macros().Get(\"uuid\").HandleError(func(ctx iris.Context, paramIndex int, err error) {\r\n    ctx.StatusCode(iris.StatusBadRequest)\r\n\r\n    param := ctx.Params().GetEntryAt(paramIndex)\r\n    ctx.JSON(iris.Map{\r\n        \"error\":     err.Error(),\r\n        \"message\":   \"invalid path parameter\",\r\n        \"parameter\": param.Key,\r\n        \"value\":     param.ValueRaw,\r\n    })\r\n})\r\n\r\napp.Get(\"/users/{id:uuid}\", getUser)\r\n```\r\n\r\n- Improve the performance and fix `:int, :int8, :int16, :int32, :int64, :uint, :uint8, :uint16, :uint32, :uint64` path type parameters couldn't accept a positive number written with the plus symbol or with a leading zeroes, e.g. `+42` and `021`.\r\n\r\n- The `iris.WithEmptyFormError` option is respected on `context.ReadQuery` method too, as requested at [#1727](https://github.com/kataras/iris/issues/1727). [Example comments](https://github.com/kataras/iris/blob/main/_examples/request-body/read-query/main.go) were updated.\r\n\r\n- New `httptest.Strict` option setter to enable the `httpexpect.RequireReporter` instead of the default `httpexpect.AssetReporter. Use that to enable complete test failure on the first error. As requested at: [#1722](https://github.com/kataras/iris/issues/1722).\r\n\r\n- New `uuid` builtin path parameter type. Example:\r\n\r\n```go\r\n// +------------------------+\r\n// | {param:uuid}           |\r\n// +------------------------+\r\n// UUIDv4 (and v1) path parameter validation.\r\n\r\n// http://localhost:8080/user/bb4f33e4-dc08-40d8-9f2b-e8b2bb615c0e -> OK\r\n// http://localhost:8080/user/dsadsa-invalid-uuid                  -> NOT FOUND\r\napp.Get(\"/user/{id:uuid}\", func(ctx iris.Context) {\r\n    id := ctx.Params().Get(\"id\")\r\n    ctx.WriteString(id)\r\n})\r\n```\r\n\r\n- New `Configuration.KeepAlive` and `iris.WithKeepAlive(time.Duration) Configurator` added as helpers to start the server using a tcp listener featured with keep-alive.\r\n\r\n- New `DirOptions.ShowHidden bool` is added by [@tuhao1020](https://github.com/tuhao1020) at [PR #1717](https://github.com/kataras/iris/pull/1717) to show or hide the hidden files when `ShowList` is set to true.\r\n\r\n- New `Context.ReadJSONStream` method and `JSONReader` options for `Context.ReadJSON` and `Context.ReadJSONStream`, see the [example](_examples/request-body/read-json-stream/main.go).\r\n\r\n- New `FallbackView` feature, per-party or per handler chain. Example can be found at: [_examples/view/fallback](_examples/view/fallback).\r\n\r\n```go\r\n    app.FallbackView(iris.FallbackViewFunc(func(ctx iris.Context, err iris.ErrViewNotExist) error {\r\n        // err.Name is the previous template name.\r\n        // err.IsLayout reports whether the failure came from the layout template.\r\n        // err.Data is the template data provided to the previous View call.\r\n        // [...custom logic e.g. ctx.View(\"fallback.html\", err.Data)]\r\n        return err\r\n    }))\r\n```\r\n\r\n- New `versioning.Aliases` middleware and up to 80% faster version resolve. Example Code:\r\n\r\n```go\r\napp := iris.New()\r\n\r\napi := app.Party(\"/api\")\r\napi.Use(Aliases(map[string]string{\r\n    versioning.Empty: \"1\", // when no version was provided by the client.\r\n    \"beta\": \"4.0.0\",\r\n    \"stage\": \"5.0.0-alpha\"\r\n}))\r\n\r\nv1 := NewGroup(api, \">=1.0.0 <2.0.0\")\r\nv1.Get/Post...\r\n\r\nv4 := NewGroup(api, \">=4.0.0 <5.0.0\")\r\nv4.Get/Post...\r\n\r\nstage := NewGroup(api, \"5.0.0-alpha\")\r\nstage.Get/Post...\r\n```\r\n\r\n- New [Basic Authentication](https://github.com/kataras/iris/tree/main/middleware/basicauth) middleware. Its `Default` function has not changed, however, the rest, e.g. `New` contains breaking changes as the new middleware features new functionalities.\r\n- Add `iris.DirOptions.SPA bool` field to allow [Single Page Applications](https://github.com/kataras/iris/tree/main/_examples/file-server/single-page-application/basic/main.go) under a file server.\r\n- A generic User interface, see the `Context.SetUser/User` methods in the New Context Methods section for more. In-short, the basicauth middleware's stored user can now be retrieved through `Context.User()` which provides more information than the native `ctx.Request().BasicAuth()` method one. Third-party authentication middleware creators can benefit of these two methods, plus the Logout below. \r\n- A `Context.Logout` method is added, can be used to invalidate [basicauth](https://github.com/kataras/iris/blob/main/_examples/auth/basicauth/basic/main.go) or [jwt](https://github.com/kataras/iris/blob/main/_examples/auth/jwt/blocklist/main.go) client credentials.\r\n- Add the ability to [share functions](https://github.com/kataras/iris/tree/main/_examples/routing/writing-a-middleware/share-funcs) between handlers chain and add an [example](https://github.com/kataras/iris/tree/main/_examples/routing/writing-a-middleware/share-services) on sharing Go structures (aka services).\r\n\r\n- Add the new `Party.UseOnce` method to the `*Route`\r\n- Add a new `*Route.RemoveHandler(...any) int` and `Party.RemoveHandler(...any) Party` methods, delete a handler based on its name or the handler pc function.\r\n\r\n```go\r\nfunc middleware(ctx iris.Context) {\r\n    // [...]\r\n}\r\n\r\nfunc main() {\r\n    app := iris.New()\r\n\r\n    // Register the middleware to all matched routes.\r\n    app.Use(middleware)\r\n\r\n    // Handlers = middleware, other\r\n    app.Get(\"/\", index)\r\n\r\n    // Handlers = other\r\n    app.Get(\"/other\", other).RemoveHandler(middleware)\r\n}\r\n```\r\n\r\n- Redis Driver is now based on the [go-redis](https://github.com/go-redis/redis/) module. Radix and redigo removed entirely. Sessions are now stored in hashes which fixes [issue #1610](https://github.com/kataras/iris/issues/1610). The only breaking change on default configuration is that the `redis.Config.Delim` option was removed. The redis sessions database driver is now defaults to the `&redis.GoRedisDriver{}`. End-developers can implement their own implementations too. The `Database#Close` is now automatically called on interrupt signals, no need to register it by yourself.\r\n\r\n- Add builtin support for **[i18n pluralization](https://github.com/kataras/iris/tree/main/_examples/i18n/plurals)**. Please check out the [following yaml locale example](https://github.com/kataras/iris/tree/main/_examples/i18n/plurals/locales/en-US/welcome.yml) to see an overview of the supported formats.\r\n- Fix [#1650](https://github.com/kataras/iris/issues/1650)\r\n- Fix [#1649](https://github.com/kataras/iris/issues/1649)\r\n- Fix [#1648](https://github.com/kataras/iris/issues/1648)\r\n- Fix [#1641](https://github.com/kataras/iris/issues/1641)\r\n\r\n- Add `Party.SetRoutesNoLog(disable bool) Party` to disable (the new) verbose logging of next routes.\r\n- Add `mvc.Application.SetControllersNoLog(disable bool) *mvc.Application` to disable (the new) verbose logging of next controllers. As requested at [#1630](https://github.com/kataras/iris/issues/1630).\r\n\r\n- Fix [#1621](https://github.com/kataras/iris/issues/1621) and add a new `cache.WithKey` to customize the cached entry key.\r\n\r\n- Add a `Response() *http.Response` to the Response Recorder.\r\n- Fix Response Recorder `Flush` when transfer-encoding is `chunked`.\r\n- Fix Response Recorder `Clone` concurrent access afterwards.\r\n\r\n- Add a `ParseTemplate` method on view engines to manually parse and add a template from a text as [requested](https://github.com/kataras/iris/issues/1617). [Examples](https://github.com/kataras/iris/tree/main/_examples/view/parse-template).\r\n- Full `http.FileSystem` interface support for all **view** engines as [requested](https://github.com/kataras/iris/issues/1575). The first argument of the functions(`HTML`, `Blocks`, `Pug`, `Ace`, `Jet`, `Django`, `Handlebars`) can now be either a directory of `string` type (like before) or a value which completes the `http.FileSystem` interface. The `.Binary` method of all view engines was removed: pass the go-bindata's latest version `AssetFile()` exported function as the first argument instead of string.\r\n\r\n- Add `Route.ExcludeSitemap() *Route` to exclude a route from sitemap as requested in [chat](https://chat.iris-go.com), also offline routes are excluded automatically now.\r\n\r\n- Improved tracing (with `app.Logger().SetLevel(\"debug\")`) for routes. Screens:\r\n\r\n#### DBUG Routes (1)\r\n\r\n![DBUG routes 1](https://iris-go.com/static/images/v12.2.0-dbug.png?v=0)\r\n\r\n#### DBUG Routes (2)\r\n\r\n![DBUG routes 2](https://iris-go.com/static/images/v12.2.0-dbug2.png?v=0)\r\n\r\n#### DBUG Routes (3)\r\n\r\n![DBUG routes with Controllers](https://iris-go.com/static/images/v12.2.0-dbug3.png?v=0)\r\n\r\n- Update the [pprof middleware](https://github.com/kataras/iris/tree/main/middleware/pprof).\r\n\r\n- New `Controller.HandleHTTPError(mvc.Code) <T>` optional Controller method to handle http errors as requested at: [MVC - More Elegent OnErrorCode registration?](https://github.com/kataras/iris/issues/1595). Example can be found [here](https://github.com/kataras/iris/tree/main/_examples/mvc/error-handler-http/main.go).\r\n\r\n![MVC: HTTP Error Handler Method](https://user-images.githubusercontent.com/22900943/90948989-e04cd300-e44c-11ea-8c97-54d90fb0cbb6.png)\r\n\r\n- New [Rewrite Engine Middleware](https://github.com/kataras/iris/tree/main/middleware/rewrite). Set up redirection rules for path patterns using the syntax we all know. [Example Code](https://github.com/kataras/iris/tree/main/_examples/routing/rewrite).\r\n\r\n```yml\r\nRedirectMatch: # REDIRECT_CODE_DIGITS | PATTERN_REGEX | TARGET_REPL\r\n  # Redirects /seo/* to /*\r\n  - 301 /seo/(.*) /$1\r\n\r\n  # Redirects /docs/v12* to /docs\r\n  - 301 /docs/v12(.*) /docs\r\n\r\n  # Redirects /old(.*) to /\r\n  - 301 /old(.*) /\r\n\r\n  # Redirects http or https://test.* to http or https://newtest.*\r\n  - 301 ^(http|https)://test.(.*) $1://newtest.$2\r\n\r\n  # Handles /*.json or .xml as *?format=json or xml,\r\n  # without redirect. See /users route.\r\n  # When Code is 0 then it does not redirect the request,\r\n  # instead it changes the request URL\r\n  # and leaves a route handle the request.\r\n  - 0 /(.*).(json|xml) /$1?format=$2\r\n\r\n# Redirects root domain to www.\r\n# Creation of a www subdomain inside the Application is unnecessary,\r\n# all requests are handled by the root Application itself.\r\nPrimarySubdomain: www\r\n```\r\n\r\n- New `TraceRoute bool` on [middleware/logger](https://github.com/kataras/iris/tree/main/middleware/logger) middleware. Displays information about the executed route. Also marks the handlers executed. Screenshot:\r\n\r\n![logger middleware: TraceRoute screenshot](https://iris-go.com/static/images/github/logger-trace-route.png)\r\n\r\n- Implement feature request [Log when I18n Translation Fails?](https://github.com/kataras/iris/issues/1593) by using the new `Application.I18n.DefaultMessageFunc` field **before** `I18n.Load`. [Example of usage](https://github.com/kataras/iris/blob/main/_examples/i18n/basic/main.go#L28-L50).\r\n\r\n- Fix [#1594](https://github.com/kataras/iris/issues/1594) and add a new `PathAfterHandler` which can be set to true to enable the old behavior (not recommended though).\r\n\r\n- New [apps](https://github.com/kataras/iris/tree/main/apps) subpackage. [Example of usage](https://github.com/kataras/iris/tree/main/_examples/routing/subdomains/redirect/multi-instances).\r\n\r\n![apps image example](https://user-images.githubusercontent.com/22900943/90459288-8a54f400-e109-11ea-8dea-20631975c9fc.png)\r\n\r\n- Fix `AutoTLS` when used with `iris.TLSNoRedirect` [*](https://github.com/kataras/iris/issues/1577). The `AutoTLS` runner can be customized through the new `iris.AutoTLSNoRedirect` instead, read its go documentation. Example of having both TLS and non-TLS versions of the same application without conflicts with letsencrypt `./well-known` path:\r\n\r\n![](https://iris-go.com/static/images/github/autotls-1.png)\r\n\r\n```go\r\npackage main\r\n\r\nimport (\r\n\t\"net/http\"\r\n\t\"time\"\r\n\r\n\t\"github.com/kataras/iris/v12\"\r\n)\r\n\r\nfunc main() {\r\n\tapp := iris.New()\r\n\tapp.Logger().SetLevel(\"debug\")\r\n\r\n\tapp.Get(\"/\", func(ctx iris.Context) {\r\n\t\tctx.JSON(iris.Map{\r\n\t\t\t\"time\": time.Now().Unix(),\r\n\t\t\t\"tls\":  ctx.Request().TLS != nil,\r\n\t\t})\r\n\t})\r\n\r\n\tvar fallbackServer = func(acme func(http.Handler) http.Handler) *http.Server {\r\n\t\tsrv := &http.Server{Handler: acme(app)}\r\n\t\tgo srv.ListenAndServe()\r\n\t\treturn srv\r\n\t}\r\n\r\n\tapp.Run(iris.AutoTLS(\":443\", \"example.com\", \"mail@example.com\",\r\n\t\tiris.AutoTLSNoRedirect(fallbackServer)))\r\n}\r\n```\r\n\r\n- `iris.Minify` middleware to minify responses based on their media/content-type.\r\n\r\n- `Context.OnCloseErr` and `Context.OnConnectionCloseErr` - to call a function of `func() error`  instead of an `iris.Handler` when request is closed or manually canceled.\r\n\r\n- `Party.UseError(...Handler)` - to register handlers to run before any http errors (e.g. before `OnErrorCode/OnAnyErrorCode` or default error codes when no handler is responsible to handle a specific http status code).\r\n\r\n- `Party.UseRouter(...Handler) and Party.ResetRouterFilters()` - to register handlers before the main router, useful on handlers that should control whether the router itself should ran or not. Independently of the incoming request's method and path values. These handlers will be executed ALWAYS against ALL incoming matched requests. Example of use-case: CORS.\r\n\r\n- `*versioning.Group` type is a full `Party` now.\r\n\r\n- `Party.UseOnce` - either inserts a middleware, or on the basis of the middleware already existing, replace that existing middleware instead.\r\n\r\n- Ability to register a view engine per group of routes or for the current chain of handlers through `Party.RegisterView` and `Context.ViewEngine` respectfully.\r\n\r\n- Add [Blocks](_examples/view/template_blocks_0) template engine. <!-- Reminder for @kataras: follow https://github.com/flosch/pongo2/pull/236#issuecomment-668950566 discussion so we can get back on using the original pongo2 repository as they fixed the issue about an incompatible 3rd party package (although they need more fixes, that's why I commented there) -->\r\n\r\n- Add [Ace](_examples/view/template_ace_0) template parser to the view engine and other minor improvements.\r\n\r\n- Fix huge repo size of 55.7MB, which slows down the overall Iris installation experience. Now, go-get performs ~3 times faster. I 've managed it using the [bfg-repo-cleaner](https://github.com/rtyley/bfg-repo-cleaner) tool - an alternative to  git-filter-branch command. Watch the small gif below to learn how:\r\n\r\n[![](https://media.giphy.com/media/U8560aiWTurW4iAOLn/giphy.gif)](https://media.giphy.com/media/U8560aiWTurW4iAOLn/giphy.gif)\r\n\r\n- [gRPC](https://grpc.io/) features:\r\n    - New Router [Wrapper](middleware/grpc).\r\n    - New MVC `.Handle(ctrl, mvc.GRPC{...})` option which allows to register gRPC services per-party (without the requirement of a full wrapper) and optionally strict access to gRPC clients only, see the [example here](_examples/mvc/grpc-compatible).\r\n\r\n- Add `Configuration.RemoteAddrHeadersForce bool` to force `Context.RemoteAddr() string` to return the first entry of request headers as a fallback instead of the `Request.RemoteAddr` one, as requested at: [1567#issuecomment-663972620](https://github.com/kataras/iris/issues/1567#issuecomment-663972620).\r\n\r\n- Fix [#1569#issuecomment-663739177](https://github.com/kataras/iris/issues/1569#issuecomment-663739177).\r\n\r\n- Fix [#1564](https://github.com/kataras/iris/issues/1564).\r\n\r\n- Fix [#1553](https://github.com/kataras/iris/issues/1553).\r\n\r\n- New `DirOptions.Cache` to cache assets in-memory among with their compressed contents (in order to be ready to served if client ask). Learn more about this feature by reading [all #1556 comments](https://github.com/kataras/iris/issues/1556#issuecomment-661057446). Usage:\r\n\r\n```go\r\nvar dirOpts = DirOptions{\r\n    // [...other options]\r\n    Cache: DirCacheOptions{\r\n        Enable: true,\r\n        // Don't compress files smaller than 300 bytes.\r\n        CompressMinSize: 300,\r\n        // Ignore compress already compressed file types\r\n        // (some images and pdf).\r\n        CompressIgnore: iris.MatchImagesAssets,\r\n        // Gzip, deflate, br(brotli), snappy.\r\n        Encodings: []string{\"gzip\", \"deflate\", \"br\", \"snappy\"},\r\n        // Log to the stdout the total reduced file size.\r\n        Verbose: 1,\r\n    },\r\n}\r\n```\r\n\r\n- New `DirOptions.PushTargets` and `PushTargetsRegexp` to push index' assets to the client without additional requests. Inspirated by issue [#1562](https://github.com/kataras/iris/issues/1562). Example matching all `.js, .css and .ico` files (recursively):\r\n\r\n```go\r\nvar dirOpts = iris.DirOptions{\r\n    // [...other options]\r\n    IndexName: \"/index.html\",\r\n    PushTargetsRegexp: map[string]*regexp.Regexp{\r\n        \"/\": regexp.MustCompile(\"((.*).js|(.*).css|(.*).ico)$\"),\r\n        // OR:\r\n        // \"/\": iris.MatchCommonAssets,\r\n    },\r\n    Compress: true,\r\n}\r\n```\r\n\r\n- Update jet parser to v5.0.2, closes [#1551](https://github.com/kataras/iris/issues/1551). It contains two breaking changes by its author:\r\n    - Relative paths on `extends, import, include...` tmpl functions, e.g. `{{extends \"../layouts/application.jet\"}}` instead of `layouts/application.jet`\r\n    - the new [jet.Ranger](https://github.com/CloudyKit/jet/pull/165) interface now requires a `ProvidesIndex() bool` method too\r\n    - Example has been [updated](https://github.com/kataras/iris/tree/main/_examples/view/template_jet_0)\r\n\r\n- Fix [#1552](https://github.com/kataras/iris/issues/1552).\r\n\r\n- Proper listing of root directories on `Party.HandleDir` when its `DirOptions.ShowList` was set to true.\r\n    - Customize the file/directory listing page through views, see [example](https://github.com/kataras/iris/tree/main/_examples/file-server/file-server).\r\n\r\n- Socket Sharding as requested at [#1544](https://github.com/kataras/iris/issues/1544). New `iris.WithSocketSharding` Configurator and `SocketSharding bool` setting.\r\n\r\n- Versioned Controllers feature through the new `mvc.Version` option. See [_examples/mvc/versioned-controller](https://github.com/kataras/iris/blob/main/_examples/mvc/versioned-controller/main.go).\r\n\r\n- Fix [#1539](https://github.com/kataras/iris/issues/1539).\r\n\r\n- New [rollbar example](https://github.com/kataras/iris/blob/main/_examples/logging/rollbar/main.go).\r\n\r\n- New builtin [requestid](https://github.com/kataras/iris/tree/main/middleware/requestid) middleware.\r\n\r\n- New builtin [JWT](https://github.com/kataras/iris/tree/main/middleware/jwt) middleware based on the fastest JWT implementation; [kataras/jwt](https://github.com/kataras/jwt) featured with optional wire encryption to set claims with sensitive data when necessary.\r\n\r\n- New `iris.RouteOverlap` route registration rule. `Party.SetRegisterRule(iris.RouteOverlap)` to allow overlapping across multiple routes for the same request subdomain, method, path. See [1536#issuecomment-643719922](https://github.com/kataras/iris/issues/1536#issuecomment-643719922). This allows two or more **MVC Controllers** to listen on the same path based on one or more registered dependencies (see [_examples/mvc/authenticated-controller](https://github.com/kataras/iris/tree/main/_examples/mvc/authenticated-controller)).\r\n\r\n- `Context.ReadForm` now can return an `iris.ErrEmptyForm` instead of `nil` when the new `Configuration.FireEmptyFormError` is true  (when `iris.WithEmptyFormError` is set) on missing form body to read from.\r\n\r\n- `Configuration.EnablePathIntelligence | iris.WithPathIntelligence` to enable path intelligence automatic path redirection on the most closest path (if any), [example]((https://github.com/kataras/iris/blob/main/_examples/routing/intelligence/main.go)\r\n\r\n- Enhanced cookie security and management through new `Context.AddCookieOptions` method and new cookie options (look on New Package-level functions section below), [securecookie](https://github.com/kataras/iris/tree/main/_examples/cookies/securecookie) example has been updated.\r\n- `Context.RemoveCookie` removes also the Request's specific cookie of the same request lifecycle when `iris.CookieAllowReclaim` is set to cookie options, [example](https://github.com/kataras/iris/tree/main/_examples/cookies/options).\r\n\r\n- `iris.TLS` can now accept certificates in form of raw `[]byte` contents too.\r\n- `iris.TLS` registers a secondary http server which redirects \"http://\" to their \"https://\" equivalent requests, unless the new `iris.TLSNoRedirect` host Configurator is provided on `iris.TLS`, e.g. `app.Run(iris.TLS(\"127.0.0.1:443\", \"mycert.cert\", \"mykey.key\", iris.TLSNoRedirect))`. There is `iris.AutoTLSNoRedirect` option for `AutoTLS` too.\r\n\r\n- Fix an [issue](https://github.com/kataras/i18n/issues/1) about i18n loading from path which contains potential language code.\r\n\r\n- Server will not return neither log the `ErrServerClosed` error if `app.Shutdown` was called manually via interrupt signal(CTRL/CMD+C), note that if the server closed by any other reason the error will be fired as previously (unless `iris.WithoutServerError(iris.ErrServerClosed)`).\r\n\r\n- Finally, Log level's and Route debug information colorization is respected across outputs. Previously if the application used more than one output destination (e.g. a file through `app.Logger().AddOutput`) the color support was automatically disabled from all, including the terminal one, this problem is fixed now. Developers can now see colors in their terminals while log files are kept with clear text.\r\n\r\n- New `iris.WithLowercaseRouting` option which forces all routes' paths to be lowercase and converts request paths to their lowercase for matching.\r\n\r\n- New `app.Validator { Struct(any) error }` field and `app.Validate` method were added. The `app.Validator = ` can be used to integrate a 3rd-party package such as [go-playground/validator](https://github.com/go-playground/validator). If set-ed then Iris `Context`'s `ReadJSON`, `ReadXML`, `ReadMsgPack`, `ReadYAML`, `ReadForm`, `ReadQuery`, `ReadBody` methods will return the validation error on data validation failures. The [read-json-struct-validation](_examples/request-body/read-json-struct-validation) example was updated.\r\n\r\n- A result of <T> can implement the new `hero.PreflightResult` interface which contains a single method of `Preflight(iris.Context) error`. If this method exists on a custom struct value which is returned from a handler then it will fire that `Preflight` first and if not errored then it will cotninue by sending the struct value as JSON(by-default) response body.\r\n\r\n- `ctx.JSON, JSONP, XML`: if `iris.WithOptimizations` is NOT passed on `app.Run/Listen` then the indentation defaults to `\"    \"` (four spaces) and `\"  \"` respectfully otherwise it is empty or the provided value.\r\n\r\n- Hero Handlers (and `app.ConfigureContainer().Handle`) do not have to require `iris.Context` just to call `ctx.Next()` anymore, this is done automatically now.\r\n\r\n- Improve Remote Address parsing as requested at: [#1453](https://github.com/kataras/iris/issues/1453). Add `Configuration.RemoteAddrPrivateSubnets` to exclude those addresses when fetched by `Configuration.RemoteAddrHeaders` through `context.RemoteAddr() string`.\r\n\r\n- Fix [#1487](https://github.com/kataras/iris/issues/1487).\r\n\r\n- Fix [#1473](https://github.com/kataras/iris/issues/1473).\r\n\r\n## New Package-level Variables\r\n\r\n- `iris.DirListRichOptions` to pass on `iris.DirListRich` method.\r\n- `iris.DirListRich` to override the default look and feel if the `DirOptions.ShowList` was set to true, can be passed to `DirOptions.DirList` field.\r\n- `DirOptions.PushTargets` for http/2 push on index [*](https://github.com/kataras/iris/tree/main/_examples/file-server/http2push/main.go).\r\n- `iris.Compression` middleware to compress responses and decode compressed request data respectfully.\r\n- `iris.B, KB, MB, GB, TB, PB, EB` for byte units.\r\n- `TLSNoRedirect` to disable automatic \"http://\" to \"https://\" redirections (see below)\r\n- `CookieAllowReclaim`, `CookieAllowSubdomains`, `CookieSameSite`, `CookieSecure` and `CookieEncoding` to bring previously sessions-only features to all cookies in the request.\r\n\r\n## New Context Methods\r\n\r\n- `Context.FormFiles(key string, before ...func(*Context, *multipart.FileHeader) bool) (files []multipart.File, headers []*multipart.FileHeader, err error)` method.\r\n- `Context.ReadURL(ptr any) error` shortcut of `ReadParams` and `ReadQuery`. Binds URL dynamic path parameters and URL query parameters to the given \"ptr\" pointer of a struct value.\r\n- `Context.SetUser(User)` and `Context.User() User` to store and retrieve an authenticated client. Read more [here](https://github.com/iris-contrib/middleware/issues/63).\r\n- `Context.SetLogoutFunc(fn any, persistenceArgs ...any)` and `Logout(args ...any) error` methods to allow different kind of auth middlewares to be able to set a \"logout\" a user/client feature with a single function, the route handler may not be aware of the implementation of the authentication used.\r\n- `Context.SetFunc(name string, fn any, persistenceArgs ...any)` and `Context.CallFunc(name string, args ...any) ([]reflect.Value, error)` to allow middlewares to share functions dynamically when the type of the function is not predictable, see the [example](https://github.com/kataras/iris/tree/main/_examples/routing/writing-a-middleware/share-funcs) for more.\r\n- `Context.TextYAML(any) error` same as `Context.YAML` but with set the Content-Type to `text/yaml` instead (Google Chrome renders it as text). \r\n- `Context.IsDebug() bool` reports whether the application is running under debug/development mode. It is a shortcut of Application.Logger().Level >= golog.DebugLevel.\r\n- `Context.IsRecovered() bool` reports whether the current request was recovered from the [recover middleware](https://github.com/kataras/iris/tree/main/middleware/recover). Also the `Context.GetErrPublic() (bool, error)`, `Context.SetErrPrivate(err error)` methods and `iris.ErrPrivate` interface have been introduced. \r\n- `Context.RecordRequestBody(bool)` same as the Application's `DisableBodyConsumptionOnUnmarshal` configuration field but registers per chain of handlers. It makes the request body readable more than once.\r\n- `Context.IsRecordingBody() bool` reports whether the request body can be readen multiple times.\r\n- `Context.ReadHeaders(ptr any) error` binds request headers to \"ptr\". [Example](https://github.com/kataras/iris/blob/main/_examples/request-body/read-headers/main.go).\r\n- `Context.ReadParams(ptr any) error` binds dynamic path parameters to \"ptr\". [Example](https://github.com/kataras/iris/blob/main/_examples/request-body/read-params/main.go).\r\n- `Context.SaveFormFile(fh *multipart.FileHeader, dest string) (int64, error)` previously unexported. Accepts a result file of `Context.FormFile` and saves it to the disk.\r\n- `Context.URLParamSlice(name string) []string` is a a shortcut of `ctx.Request().URL.Query()[name]`. Like `URLParam` but it returns all values as a string slice instead of a single string separated by commas. Note that it skips any empty values (e.g. https://iris-go.com?values=).\r\n- `Context.PostValueMany(name string) (string, error)` returns the post data of a given key. The returned value is a single string separated by commas on multiple values. It also reports whether the form was empty or when the \"name\" does not exist or whether the available values are empty. It strips any empty key-values from the slice before return. See `ErrEmptyForm`, `ErrNotFound` and `ErrEmptyFormField` respectfully. The `PostValueInt`, `PostValueInt64`, `PostValueFloat64` and `PostValueBool` now respect the above errors too (the `PostValues` method now returns a second output argument of `error` too, see breaking changes below). \r\n- `Context.URLParamsSorted() []memstore.StringEntry` returns a sorted (by key) slice of key-value entries of the URL Query parameters.\r\n- `Context.ViewEngine(ViewEngine)` to set a view engine on-fly for the current chain of handlers, responsible to render templates through `ctx.View`. [Example](_examples/view/context-view-engine).\r\n- `Context.SetErr(error)` and `Context.GetErr() error` helpers.\r\n- `Context.CompressWriter(bool) error` and `Context.CompressReader(bool) error`.\r\n- `Context.Clone() Context` returns a copy of the Context safe for concurrent access.\r\n- `Context.IsCanceled() bool` reports whether the request has been canceled by the client.\r\n- `Context.IsSSL() bool` reports whether the request is under HTTPS SSL (New `Configuration.SSLProxyHeaders` and `HostProxyHeaders` fields too).\r\n- `Context.CompressReader(enable bool)` method and `iris.CompressReader` middleware to enable future request read body calls to decompress data, [example](_examples/compression/main.go).\r\n- `Context.RegisterDependency(v any)` and `Context.UnregisterDependency(typ reflect.Type)` to register/remove struct dependencies on serve-time through a middleware.\r\n- `Context.SetID(id any)` and `Context.GetID() any` added to register a custom unique indetifier to the Context, if necessary.\r\n- `Context.Scheme() string` returns the full scheme of the request URL.\r\n- `Context.SubdomainFull() string` returns the full subdomain(s) part of the host (`host[0:rootLevelDomain]`).\r\n- `Context.Domain() string` returns the root level domain.\r\n- `Context.AddCookieOptions(...CookieOption)` adds options for `SetCookie`, `SetCookieKV, UpsertCookie` and `RemoveCookie` methods for the current request.\r\n- `Context.ClearCookieOptions()` clears any cookie options registered through `AddCookieOptions`.\r\n- `Context.SetLanguage(langCode string)` force-sets a language code from inside a middleare, similar to the `app.I18n.ExtractFunc`\r\n- `Context.ServeContentWithRate`, `ServeFileWithRate` and `SendFileWithRate` methods to throttle the \"download\" speed of the client\r\n- `Context.IsHTTP2() bool` reports whether the protocol version for incoming request was HTTP/2\r\n- `Context.IsGRPC() bool` reports whether the request came from a gRPC client\r\n- `Context.UpsertCookie(*http.Cookie, cookieOptions ...context.CookieOption)` upserts a cookie, fixes [#1485](https://github.com/kataras/iris/issues/1485) too\r\n- `Context.StopWithStatus(int)` stops the handlers chain and writes the status code\r\n- `StopWithText(statusCode int, format string, args ...any)` stops the handlers chain, writes thre status code and a plain text message\r\n- `Context.StopWithError(int, error)` stops the handlers chain, writes thre status code and the error's message\r\n- `Context.StopWithJSON(int, any)` stops the handlers chain, writes the status code and sends a JSON response\r\n- `Context.StopWithProblem(int, iris.Problem)` stops the handlers, writes the status code and sends an `application/problem+json` response\r\n- `Context.Protobuf(proto.Message)` sends protobuf to the client (note that the `Context.JSON` is able to send protobuf as JSON)\r\n- `Context.MsgPack(any)` sends msgpack format data to the client\r\n- `Context.ReadProtobuf(ptr)` binds request body to a proto message\r\n- `Context.ReadJSONProtobuf(ptr, ...options)` binds JSON request body to a proto message\r\n- `Context.ReadMsgPack(ptr)` binds request body of a msgpack format to a struct\r\n- `Context.ReadBody(ptr)` binds the request body to the \"ptr\" depending on the request's Method and Content-Type\r\n- `Context.ReflectValue() []reflect.Value` stores and returns the `[]reflect.ValueOf(ctx)`\r\n- `Context.Controller() reflect.Value` returns the current MVC Controller value.\r\n\r\n## MVC & Dependency Injection\r\n\r\nThe new release contains a fresh new and awesome feature....**a function dependency can accept previous registered dependencies and update or return a new value of any type**.\r\n\r\nThe new implementation is **faster** on both design and serve-time.\r\n\r\nThe most common scenario from a route to handle is to:\r\n- accept one or more path parameters and request data, a payload\r\n- send back a response, a payload (JSON, XML,...)\r\n\r\nThe new Iris Dependency Injection feature is about **33.2% faster** than its predecessor on the above case. This drops down even more the performance cost between native handlers and dynamic handlers with dependencies. This reason itself brings us, with safety and performance-wise, to the new `Party.ConfigureContainer(builder ...func(*iris.APIContainer)) *APIContainer` method which returns methods such as `Handle(method, relativePath string, handlersFn ...any) *Route` and `RegisterDependency`.\r\n\r\nLook how clean your codebase can be when using Iris':\r\n\r\n```go\r\npackage main\r\n\r\nimport \"github.com/kataras/iris/v12\"\r\n\r\ntype (\r\n    testInput struct {\r\n        Email string `json:\"email\"`\r\n    }\r\n\r\n    testOutput struct {\r\n        ID   int    `json:\"id\"`\r\n        Name string `json:\"name\"`\r\n    }\r\n)\r\n\r\nfunc handler(id int, in testInput) testOutput {\r\n    return testOutput{\r\n        ID:   id,\r\n        Name: in.Email,\r\n    }\r\n}\r\n\r\nfunc main() {\r\n    app := iris.New()\r\n    app.ConfigureContainer(func(api *iris.APIContainer) {\r\n        api.Post(\"/{id:int}\", handler)\r\n    })\r\n    app.Listen(\":5000\", iris.WithOptimizations)\r\n}\r\n```\r\n\r\nYour eyes don't lie you. You read well, no `ctx.ReadJSON(&v)` and `ctx.JSON(send)` neither `error` handling are presented. It is a huge relief but if you ever need, you still have the control over those, even errors from dependencies. Here is a quick list of the new Party.ConfigureContainer()'s fields and methods:\r\n\r\n```go\r\n// Container holds the DI Container of this Party featured Dependency Injection.\r\n// Use it to manually convert functions or structs(controllers) to a Handler.\r\nContainer *hero.Container\r\n```\r\n\r\n```go\r\n// OnError adds an error handler for this Party's DI Hero Container and its handlers (or controllers).\r\n// The \"errorHandler\" handles any error may occurred and returned\r\n// during dependencies injection of the Party's hero handlers or from the handlers themselves.\r\nOnError(errorHandler func(iris.Context, error))\r\n```\r\n\r\n```go\r\n// RegisterDependency adds a dependency.\r\n// The value can be a single struct value or a function.\r\n// Follow the rules:\r\n// * <T> {structValue}\r\n// * func(accepts <T>)                                 returns <D> or (<D>, error)\r\n// * func(accepts iris.Context)                        returns <D> or (<D>, error)\r\n//\r\n// A Dependency can accept a previous registered dependency and return a new one or the same updated.\r\n// * func(accepts1 <D>, accepts2 <T>)                  returns <E> or (<E>, error) or error\r\n// * func(acceptsPathParameter1 string, id uint64)     returns <T> or (<T>, error)\r\n//\r\n// Usage:\r\n//\r\n// - RegisterDependency(loggerService{prefix: \"dev\"})\r\n// - RegisterDependency(func(ctx iris.Context) User {...})\r\n// - RegisterDependency(func(User) OtherResponse {...})\r\nRegisterDependency(dependency any)\r\n\r\n// UseResultHandler adds a result handler to the Container.\r\n// A result handler can be used to inject the returned struct value\r\n// from a request handler or to replace the default renderer.\r\nUseResultHandler(handler func(next iris.ResultHandler) iris.ResultHandler)\r\n```\r\n\r\n<details><summary>ResultHandler</summary>\r\n\r\n```go\r\ntype ResultHandler func(ctx iris.Context, v any) error\r\n```\r\n</details>\r\n\r\n```go\r\n// Use same as a common Party's \"Use\" but it accepts dynamic functions as its \"handlersFn\" input.\r\nUse(handlersFn ...any)\r\n// Done same as a common Party's but it accepts dynamic functions as its \"handlersFn\" input.\r\nDone(handlersFn ...any)\r\n```\r\n\r\n```go\r\n// Handle same as a common Party's `Handle` but it accepts one or more \"handlersFn\" functions which each one of them\r\n// can accept any input arguments that match with the Party's registered Container's `Dependencies` and\r\n// any output result; like custom structs <T>, string, []byte, int, error,\r\n// a combination of the above, hero.Result(hero.View | hero.Response) and more.\r\n//\r\n// It's common from a hero handler to not even need to accept a `Context`, for that reason,\r\n// the \"handlersFn\" will call `ctx.Next()` automatically when not called manually.\r\n// To stop the execution and not continue to the next \"handlersFn\"\r\n// the end-developer should output an error and return `iris.ErrStopExecution`.\r\nHandle(method, relativePath string, handlersFn ...any) *Route\r\n\r\n// Get registers a GET route, same as `Handle(\"GET\", relativePath, handlersFn....)`.\r\nGet(relativePath string, handlersFn ...any) *Route\r\n// and so on...\r\n```\r\n\r\nPrior to this version the `iris.Context` was the only one dependency that has been automatically binded to the handler's input or a controller's fields and methods, read below to see what types are automatically binded:\r\n\r\n| Type | Maps To |\r\n|------|:---------|\r\n| [*mvc.Application](https://pkg.go.dev/github.com/kataras/iris/v12/mvc?tab=doc#Application) | Current MVC Application |\r\n| [iris.Context](https://pkg.go.dev/github.com/kataras/iris/v12/context?tab=doc#Context) | Current Iris Context |\r\n| [*sessions.Session](https://pkg.go.dev/github.com/kataras/iris/v12/sessions?tab=doc#Session) | Current Iris Session |\r\n| [context.Context](https://golang.org/pkg/context/#Context) | [ctx.Request().Context()](https://golang.org/pkg/net/http/#Request.Context) |\r\n| [*http.Request](https://golang.org/pkg/net/http/#Request) | `ctx.Request()` |\r\n| [http.ResponseWriter](https://golang.org/pkg/net/http/#ResponseWriter) | `ctx.ResponseWriter()` |\r\n| [http.Header](https://golang.org/pkg/net/http/#Header) | `ctx.Request().Header` |\r\n| [time.Time](https://golang.org/pkg/time/#Time) | `time.Now()` |\r\n| [*golog.Logger](https://pkg.go.dev/github.com/kataras/golog) | Iris Logger |\r\n| [net.IP](https://golang.org/pkg/net/#IP) | `net.ParseIP(ctx.RemoteAddr())` |\r\n| [mvc.Code](https://pkg.go.dev/github.com/kataras/iris/v12/mvc?tab=doc#Code) | `ctx.GetStatusCode() int` |\r\n| [mvc.Err](https://pkg.go.dev/github.com/kataras/iris/v12/mvc?tab=doc#Err) | `ctx.GetErr() error` |\r\n| [iris/context.User](https://pkg.go.dev/github.com/kataras/iris/v12/context?tab=doc#User) | `ctx.User()` |\r\n| `string`, | |\r\n| `int, int8, int16, int32, int64`, | |\r\n| `uint, uint8, uint16, uint32, uint64`, | |\r\n| `float, float32, float64`, | |\r\n| `bool`, | |\r\n| `slice` | [Path Parameter](https://github.com/kataras/iris/blob/main/_examples/routing/dynamic-path/main.go#L20) |\r\n| Struct | [Request Body](https://github.com/kataras/iris/tree/main/_examples/request-body) of `JSON`, `XML`, `YAML`, `Form`, `URL Query`, `Protobuf`, `MsgPack` |\r\n\r\nHere is a preview of what the new Hero handlers look like:\r\n\r\n### Request & Response & Path Parameters\r\n\r\n**1.** Declare Go types for client's request body and a server's response.\r\n\r\n```go\r\ntype (\r\n\trequest struct {\r\n\t\tFirstname string `json:\"firstname\"`\r\n\t\tLastname  string `json:\"lastname\"`\r\n\t}\r\n\r\n\tresponse struct {\r\n\t\tID      uint64 `json:\"id\"`\r\n\t\tMessage string `json:\"message\"`\r\n\t}\r\n)\r\n```\r\n\r\n**2.** Create the route handler.\r\n\r\nPath parameters and request body are binded automatically.\r\n- **id uint64** binds to \"id:uint64\"\r\n- **input request** binds to client request data such as JSON\r\n\r\n```go\r\nfunc updateUser(id uint64, input request) response {\r\n\treturn response{\r\n\t\tID:      id,\r\n\t\tMessage: \"User updated successfully\",\r\n\t}\r\n}\r\n```\r\n\r\n**3.** Configure the container per group and register the route.\r\n\r\n```go\r\napp.Party(\"/user\").ConfigureContainer(container)\r\n\r\nfunc container(api *iris.APIContainer) {\r\n    api.Put(\"/{id:uint64}\", updateUser)\r\n}\r\n```\r\n\r\n**4.** Simulate a [client](https://curl.haxx.se/download.html) request which sends data to the server and displays the response.\r\n\r\n```sh\r\ncurl --request PUT -d '{\"firstanme\":\"John\",\"lastname\":\"Doe\"}' http://localhost:8080/user/42\r\n```\r\n\r\n```json\r\n{\r\n    \"id\": 42,\r\n    \"message\": \"User updated successfully\"\r\n}\r\n```\r\n\r\n### Custom Preflight\r\n\r\nBefore we continue to the next section, register dependencies, you may want to learn how a response can be customized through the `iris.Context` right before sent to the client.\r\n\r\nThe server will automatically execute the `Preflight(iris.Context) error` method of a function's output struct value right before send the response to the client.\r\n\r\nTake for example that you want to fire different HTTP status codes depending on the custom logic inside your handler and also modify the value(response body) itself before sent to the client. Your response type should contain a `Preflight` method like below.\r\n\r\n```go\r\ntype response struct {\r\n    ID      uint64 `json:\"id,omitempty\"`\r\n    Message string `json:\"message\"`\r\n    Code    int    `json:\"code\"`\r\n    Timestamp int64 `json:\"timestamp,omitempty\"`\r\n}\r\n\r\nfunc (r *response) Preflight(ctx iris.Context) error {\r\n    if r.ID > 0 {\r\n        r.Timestamp = time.Now().Unix()\r\n    }\r\n\r\n    if r.Code > 0 {\r\n        ctx.StatusCode(r.Code)\r\n    }\r\n\r\n    return nil\r\n}\r\n```\r\n\r\nNow, each handler that returns a `*response` value will call the `response.Preflight` method automatically.\r\n\r\n```go\r\nfunc deleteUser(db *sql.DB, id uint64) *response {\r\n    // [...custom logic]\r\n\r\n    return &response{\r\n        Message: \"User has been marked for deletion\",\r\n        Code: iris.StatusAccepted,\r\n    }\r\n}\r\n```\r\n\r\nIf you register the route and fire a request you should see an output like this, the timestamp is filled and the HTTP status code of the response that the client will receive is 202 (Status Accepted).\r\n\r\n```json\r\n{\r\n  \"message\": \"User has been marked for deletion\",\r\n  \"code\": 202,\r\n  \"timestamp\": 1583313026\r\n}\r\n```\r\n\r\n### Register Dependencies\r\n\r\n**1.** Import packages to interact with a database.\r\nThe go-sqlite3 package is a database driver for [SQLite](https://www.sqlite.org/index.html).\r\n\r\n```go\r\nimport \"database/sql\"\r\nimport _ \"github.com/mattn/go-sqlite3\"\r\n```\r\n\r\n**2.** Configure the container ([see above](#request--response--path-parameters)), register your dependencies. Handler expects an *sql.DB instance.\r\n\r\n```go\r\nlocalDB, _ := sql.Open(\"sqlite3\", \"./foo.db\")\r\napi.RegisterDependency(localDB)\r\n```\r\n\r\n**3.** Register a route to create a user.\r\n\r\n```go\r\napi.Post(\"/{id:uint64}\", createUser)\r\n```\r\n\r\n**4.** The create user Handler.\r\n\r\nThe handler accepts a database and some client request data such as JSON, Protobuf, Form, URL Query and e.t.c. It Returns a response.\r\n\r\n```go\r\nfunc createUser(db *sql.DB, user request) *response {\r\n    // [custom logic using the db]\r\n    userID, err := db.CreateUser(user)\r\n    if err != nil {\r\n        return &response{\r\n            Message: err.Error(),\r\n            Code: iris.StatusInternalServerError,\r\n        }\r\n    }\r\n\r\n\treturn &response{\r\n\t\tID:      userID,\r\n\t\tMessage: \"User created\",\r\n\t\tCode:    iris.StatusCreated,\r\n\t}\r\n}\r\n```\r\n\r\n**5.** Simulate a [client](https://curl.haxx.se/download.html) to create a user.\r\n\r\n```sh\r\n# JSON\r\ncurl --request POST -d '{\"firstname\":\"John\",\"lastname\":\"Doe\"}' \\\r\n--header 'Content-Type: application/json' \\\r\nhttp://localhost:8080/user\r\n```\r\n\r\n```sh\r\n# Form (multipart)\r\ncurl --request POST 'http://localhost:8080/users' \\\r\n--header 'Content-Type: multipart/form-data' \\\r\n--form 'firstname=John' \\\r\n--form 'lastname=Doe'\r\n```\r\n\r\n```sh\r\n# Form (URL-encoded)\r\ncurl --request POST 'http://localhost:8080/users' \\\r\n--header 'Content-Type: application/x-www-form-urlencoded' \\\r\n--data-urlencode 'firstname=John' \\\r\n--data-urlencode 'lastname=Doe'\r\n```\r\n\r\n```sh\r\n# URL Query\r\ncurl --request POST 'http://localhost:8080/users?firstname=John&lastname=Doe'\r\n```\r\n\r\nResponse: \r\n\r\n```json\r\n{\r\n    \"id\": 42,\r\n    \"message\": \"User created\",\r\n    \"code\": 201,\r\n    \"timestamp\": 1583313026\r\n}\r\n```\r\n\r\n## Breaking Changes\r\n\r\n- The `versioning.NewMatcher` has been removed entirely in favor of `NewGroup`. Strict versions format on `versioning.NewGroup` is required. E.g. `\"1\"` is not valid anymore, you have to specify `\"1.0.0\"`. Example: `NewGroup(api, \">=1.0.0 <2.0.0\")`. The [routing/versioning](_examples/routing/versioning) examples have been updated.\r\n- Now that `RegisterView` can be used to register different view engines per-Party, there is no need to support registering multiple engines under the same Party. The `app.RegisterView` now upserts the given Engine instead of append. You can now render templates **without file extension**, e.g. `index` instead of `index.ace`, both forms are valid now.\r\n- The `Context.ContentType` does not accept filenames to resolve the mime type anymore (caused issues with  vendor-specific(vnd) MIME types).\r\n- The `Configuration.RemoteAddrPrivateSubnets.IPRange.Start and End` are now type of `string` instead of `net.IP`. The `WithRemoteAddrPrivateSubnet` option remains as it is, already accepts `string`s.\r\n- The `i18n#LoaderConfig.FuncMap template.FuncMap` field was replaced with `Funcs func(iris.Locale) template.FuncMap` in order to give current locale access to the template functions. A new `app.I18n.Loader` was introduced too, in order to make it easier for end-developers to customize the translation key values.\r\n- Request Logger's `Columns bool` field has been removed. Use the new [accesslog](https://github.com/kataras/iris/tree/main/_examples/logging/request-logger/accesslog/main.go) middleware instead.\r\n- The `.Binary` method of all view engines was removed: pass the go-bindata's latest version `AssetFile()` exported function as the first argument instead of string. All examples updated.\r\n- `ContextUploadFormFiles(destDirectory string, before ...func(*Context, *multipart.FileHeader) bool) (uploaded []*multipart.FileHeader, n int64, err error)` now returns the total files uploaded too (as its first parameter) and the \"before\" variadic option should return a boolean, if false then the specific file is skipped.\r\n- `Context.PostValues(name string) ([]string, error)` now returns a second output argument of `error` type too, which reports `ErrEmptyForm` or `ErrNotFound` or `ErrEmptyFormField`. The single post value getters now returns the **last value** if multiple was given instead of the first one (this allows clients to append values on flow updates).\r\n- `Party.GetReporter()` **removed**. The `Application.Build` returns the first error now and the API's errors are logged, this allows the server to run even if some of the routes are invalid but not fatal to the entire application (it was a request from a company).\r\n- `versioning.NewGroup(string)` now accepts a `Party` as its first input argument: `NewGroup(Party, string)`.\r\n- `versioning.RegisterGroups` is **removed** as it is no longer necessary.\r\n- `Configuration.RemoteAddrHeaders` from `map[string]bool` to `[]string`. If you used `With(out)RemoteAddrHeader` then you are ready to proceed without any code changes for that one.\r\n- `ctx.Gzip(boolean)` replaced with `ctx.CompressWriter(boolean) error`.\r\n- `ctx.GzipReader(boolean) error` replaced with `ctx.CompressReader(boolean) error`.\r\n- `iris.Gzip` and `iris.GzipReader` replaced with `iris.Compression` (middleware).\r\n- `ctx.ClientSupportsGzip() bool` replaced with `ctx.ClientSupportsEncoding(\"gzip\", \"br\" ...) bool`.\r\n- `ctx.GzipResponseWriter()` is **removed**.\r\n- `Party.HandleDir/iris.FileServer` now accepts both `http.FileSystem` and `string` and returns a list of `[]*Route` (GET and HEAD) instead of GET only. You can write: both `app.HandleDir(\"/\", iris.Dir(\"./assets\"))` and `app.HandleDir(\"/\", \"./assets\")` and `DirOptions.Asset, AssetNames, AssetInfo` removed, use `go-bindata -fs [..]` and `app.HandleDir(\"/\", AssetFile())` instead.\r\n- `Context.OnClose` and `Context.OnCloseConnection` now both accept an `iris.Handler` instead of a simple `func()` as their callback.\r\n- `Context.StreamWriter(writer func(w io.Writer) bool)` changed to `StreamWriter(writer func(w io.Writer) error) error` and it's now the `Context.Request().Context().Done()` channel that is used to receive any close connection/manual cancel signals, instead of the deprecated `ResponseWriter().CloseNotify()` one. Same for the `Context.OnClose` and `Context.OnCloseConnection` methods.\r\n- Fixed handler's error response not be respected when response recorder was used instead of the common writer. Fixes [#1531](https://github.com/kataras/iris/issues/1531). It contains a **BREAKING CHANGE** of: the new `Configuration.ResetOnFireErrorCode` field should be set **to true** in order to behave as it used before this update (to reset the contents on recorder).\r\n- `Context.String()` (rarely used by end-developers) it does not return a unique string anymore, to achieve the old representation you must call the new `Context.SetID` method first.\r\n- `iris.CookieEncode` and `CookieDecode` are replaced with the `iris.CookieEncoding`.\r\n- `sessions#Config.Encode` and `Decode` are removed in favor of (the existing) `Encoding` field.\r\n- `versioning.GetVersion` now returns an empty string if version wasn't found.\r\n- Change the MIME type of `Javascript .js` and `JSONP` as the HTML specification now recommends to `\"text/javascript\"` instead of the obselete `\"application/javascript\"`. This change was pushed to the `Go` language itself as well. See <https://go-review.googlesource.com/c/go/+/186927/>.\r\n- Remove the last input argument of `enableGzipCompression` in `Context.ServeContent`, `ServeFile` methods. This was deprecated a few versions ago. A middleware (`app.Use(iris.CompressWriter)`) or a prior call to `Context.CompressWriter(true)` will enable compression. Also these two methods and `Context.SendFile` one now support `Content-Range` and `Accept-Ranges` correctly out of the box (`net/http` had a bug, which is now fixed).\r\n- `Context.ServeContent` no longer returns an error, see `ServeContentWithRate`, `ServeFileWithRate` and `SendFileWithRate` new methods too.\r\n- `route.Trace() string` changed to `route.Trace(w io.Writer)`, to achieve the same result just pass a `bytes.Buffer`\r\n- `var mvc.AutoBinding` removed as the default behavior now resolves such dependencies automatically (see [[FEATURE REQUEST] MVC serving gRPC-compatible controller](https://github.com/kataras/iris/issues/1449)).\r\n- `mvc#Application.SortByNumMethods()` removed as the default behavior now binds the \"thinnest\"  empty `any` automatically (see [MVC: service injecting fails](https://github.com/kataras/iris/issues/1343)).\r\n- `mvc#BeforeActivation.Dependencies().Add` should be replaced with `mvc#BeforeActivation.Dependencies().Register` instead\r\n- **REMOVE** the `kataras/iris/v12/typescript` package in favor of the new [iris-cli](https://github.com/kataras/iris-cli). Also, the alm typescript online editor was removed as it is deprecated by its author, please consider using the [designtsx](https://designtsx.com/) instead.\r\n\r\n# Su, 16 February 2020 | v12.1.8\r\n\r\nNew Features:\r\n\r\n-  [[FEATURE REQUEST] MVC serving gRPC-compatible controller](https://github.com/kataras/iris/issues/1449)\r\n\r\nFixes:\r\n\r\n- [App can't find embedded pug template files by go-bindata](https://github.com/kataras/iris/issues/1450)\r\n\r\nNew Examples:\r\n\r\n- [_examples/mvc/grpc-compatible](_examples/mvc/grpc-compatible)\r\n\r\n# Mo, 10 February 2020 | v12.1.7\r\n\r\nImplement **new** `SetRegisterRule(iris.RouteOverride, RouteSkip, RouteError)` to resolve: https://github.com/kataras/iris/issues/1448\r\n\r\nNew Examples:\r\n\r\n- [_examples/routing/route-register-rule](_examples/routing/route-register-rule)\r\n\r\n# We, 05 February 2020 | v12.1.6\r\n\r\nFixes:\r\n\r\n- [jet.View - urlpath error](https://github.com/kataras/iris/issues/1438)\r\n- [Context.ServeFile send 'application/wasm' with a wrong extra field](https://github.com/kataras/iris/issues/1440)\r\n\r\n# Su, 02 February 2020 | v12.1.5\r\n\r\nVarious improvements and linting.\r\n\r\n# Su, 29 December 2019 | v12.1.4\r\n\r\nMinor fix on serving embedded files.\r\n\r\n# We, 25 December 2019 | v12.1.3\r\n\r\nFix [[BUG] [iris.Default] RegisterView](https://github.com/kataras/iris/issues/1410)\r\n\r\n# Th, 19 December 2019 | v12.1.2\r\n\r\nFix [[BUG]Session works incorrectly when meets the multi-level TLDs](https://github.com/kataras/iris/issues/1407).\r\n\r\n# Mo, 16 December 2019 | v12.1.1\r\n\r\nAdd [Context.FindClosest(n int) []string](https://github.com/kataras/iris/blob/main/_examples/routing/intelligence/manual/main.go#L22)\r\n\r\n```go\r\napp := iris.New()\r\napp.OnErrorCode(iris.StatusNotFound, notFound)\r\n```\r\n\r\n```go\r\nfunc notFound(ctx iris.Context) {\r\n    suggestPaths := ctx.FindClosest(3)\r\n    if len(suggestPaths) == 0 {\r\n        ctx.WriteString(\"404 not found\")\r\n        return\r\n    }\r\n\r\n    ctx.HTML(\"Did you mean?<ul>\")\r\n    for _, s := range suggestPaths {\r\n        ctx.HTML(`<li><a href=\"%s\">%s</a></li>`, s, s)\r\n    }\r\n    ctx.HTML(\"</ul>\")\r\n}\r\n```\r\n\r\n![](https://iris-go.com/static/images/iris-not-found-suggests.png)\r\n\r\n# Fr, 13 December 2019 | v12.1.0\r\n\r\n## Breaking Changes\r\n\r\nMinor as many of you don't even use them but, indeed, they need to be covered here.\r\n\r\n- Old i18n middleware(iris/middleware/i18n) was replaced by the [i18n](i18n) sub-package which lives as field at your application: `app.I18n.Load(globPathPattern string, languages ...string)` (see below)\r\n- Community-driven i18n middleware(iris-contrib/middleware/go-i18n) has a `NewLoader` function which returns a loader which can be passed at `app.I18n.Reset(loader i18n.Loader, languages ...string)` to change the locales parser\r\n- The Configuration's `TranslateFunctionContextKey` was replaced by `LocaleContextKey` which Context store's value (if i18n is used) returns the current Locale which contains the translate function, the language code, the language tag and the index position of it\r\n- The `context.Translate` method was replaced by `context.Tr` as a shortcut for the new `context.GetLocale().GetMessage(format, args...)` method and it matches the view's function `{{tr format args}}` too\r\n- If you used [Iris Django](https://github.com/kataras/iris/tree/main/_examples/view/template_django_0) view engine with `import _ github.com/flosch/pongo2-addons` you **must change** the import path to `_ github.com/iris-contrib/pongo2-addons` or add a [go mod replace](https://github.com/golang/go/wiki/Modules#when-should-i-use-the-replace-directive) to your `go.mod` file, e.g. `replace github.com/flosch/pongo2-addons => github.com/iris-contrib/pongo2-addons v0.0.1`.\r\n\r\n## Fixes\r\n\r\nAll known issues.\r\n\r\n1. [#1395](https://github.com/kataras/iris/issues/1395) \r\n2. [#1369](https://github.com/kataras/iris/issues/1369)\r\n3. [#1399](https://github.com/kataras/iris/issues/1399) with PR [#1400](https://github.com/kataras/iris/pull/1400)\r\n4. [#1401](https://github.com/kataras/iris/issues/1401) \r\n5. [#1406](https://github.com/kataras/iris/issues/1406)\r\n6. [neffos/#20](https://github.com/kataras/neffos/issues/20)\r\n7. [pio/#5](https://github.com/kataras/pio/issues/5)\r\n\r\n## New Features\r\n\r\n### Internationalization and localization\r\n\r\nSupport for i18n is now a **builtin feature** and is being respected across your entire application, per say [sitemap](https://github.com/kataras/iris/blob/main/_examples/routing/sitemap/main.go) and [views](https://github.com/kataras/iris/blob/main/_examples/i18n/basic/main.go#L50).\r\n\r\n### Sitemaps\r\n\r\nIris generates and serves one or more [sitemap.xml](https://www.sitemaps.org/protocol.html) for your static routes.\r\n\r\nNavigate through: https://github.com/kataras/iris/blob/main/_examples/routing/sitemap/main.go for more.\r\n\r\n## New Examples\r\n\r\n2. [_examples/i18n](_examples/i18n)\r\n1. [_examples/sitemap](_examples/routing/sitemap)\r\n3. [_examples/desktop/blink](_examples/desktop/blink)\r\n4. [_examples/desktop/lorca](_examples/desktop/lorca)\r\n5. [_examples/desktop/webview](_examples/desktop/webview)\r\n\r\n# Sa, 26 October 2019 | v12.0.0\r\n\r\n- Add version suffix of the **import path**, learn why and see what people voted at [issue #1370](https://github.com/kataras/iris/issues/1370)\r\n\r\n![](https://iris-go.com/static/images/vote-v12-version-suffix_26_oct_2019.png)\r\n\r\n- All errors are now compatible with go1.13 `errors.Is`, `errors.As` and `fmt.Errorf` and a new `core/errgroup` package created\r\n- Fix [#1383](https://github.com/kataras/iris/issues/1383)\r\n- Report whether system couldn't find the directory of view templates\r\n- Remove the `Party#GetReport` method, keep `Party#GetReporter` which is an `error` and an `errgroup.Group`.\r\n- Remove the router's deprecated methods such as StaticWeb and StaticEmbedded_XXX\r\n- The `Context#CheckIfModifiedSince` now returns an `context.ErrPreconditionFailed` type of error when client conditions are not met. Usage: `if errors.Is(err, context.ErrPreconditionFailed) { ... }`\r\n- Add `SourceFileName` and `SourceLineNumber` to the `Route`, reports the exact position of its registration inside your project's source code.\r\n- Fix a bug about the MVC package route binding, see [PR #1364](https://github.com/kataras/iris/pull/1364)\r\n- Add `mvc/Application#SortByNumMethods` as requested at [#1343](https://github.com/kataras/iris/issues/1343#issuecomment-524868164)\r\n- Add status code `103 Early Hints`\r\n- Fix performance of session.UpdateExpiration on 200 thousands+ keys with new radix as reported at [issue #1328](https://github.com/kataras/iris/issues/1328)\r\n- New redis session database configuration field: `Driver: redis.Redigo()` or `redis.Radix()`, see [updated examples](_examples/sessions/database/redis/)\r\n- Add Clusters support for redis:radix session database (`Driver: redis:Radix()`) as requested at [issue #1339](https://github.com/kataras/iris/issues/1339)\r\n- Create Iranian [README_FA](README_FA.md) translation with [PR #1360](https://github.com/kataras/iris/pull/1360) \r\n- Create Korean [README_KO](README_KO.md) translation with [PR #1356](https://github.com/kataras/iris/pull/1356)\r\n- Create Spanish [README_ES](README_ES.md) and [HISTORY_ES](HISTORY_ES.md) translations with [PR #1344](https://github.com/kataras/iris/pull/1344).\r\n\r\nThe iris-contrib/middleare and examples are updated to use the new `github.com/kataras/iris/v12` import path.\r\n\r\n# Fr, 16 August 2019 | v11.2.8\r\n\r\n- Set `Cookie.SameSite` to `Lax` when subdomains sessions share is enabled[*](https://github.com/kataras/iris/commit/6bbdd3db9139f9038641ce6f00f7b4bab6e62550)\r\n- Add and update all [experimental handlers](https://github.com/iris-contrib/middleware) \r\n- New `XMLMap` function which wraps a `map[string]any` and converts it to a valid xml content to render through `Context.XML` method\r\n- Add new `ProblemOptions.XML` and `RenderXML` fields to render the `Problem` as XML(application/problem+xml) instead of JSON(\"application/problem+json) and enrich the `Negotiate` to easily accept the `application/problem+xml` mime.\r\n\r\nCommit log: https://github.com/kataras/iris/compare/v11.2.7...v11.2.8\r\n\r\n# Th, 15 August 2019 | v11.2.7\r\n\r\nThis minor version contains improvements on the Problem Details for HTTP APIs implemented on [v11.2.5](#mo-12-august-2019--v1125).\r\n\r\n- Fix https://github.com/kataras/iris/issues/1335#issuecomment-521319721\r\n- Add `ProblemOptions` with `RetryAfter` as requested at: https://github.com/kataras/iris/issues/1335#issuecomment-521330994.\r\n- Add `iris.JSON` alias for `context#JSON` options type.\r\n\r\n[Example](https://github.com/kataras/iris/blob/45d7c6fedb5adaef22b9730592255f7bb375e809/_examples/routing/http-errors/main.go#L85) updated. \r\n\r\nReferences:\r\n\r\n- https://tools.ietf.org/html/rfc7231#section-7.1.3\r\n- https://tools.ietf.org/html/rfc7807\r\n\r\nCommit log: https://github.com/kataras/iris/compare/v11.2.6...v11.2.7\r\n\r\n# We, 14 August 2019 | v11.2.6\r\n\r\nAllow [handle more than one route with the same paths and parameter types but different macro validation functions](https://github.com/kataras/iris/issues/1058#issuecomment-521110639).\r\n\r\n```go\r\napp.Get(\"/{alias:string regexp(^[a-z0-9]{1,10}\\\\.xml$)}\", PanoXML)\r\napp.Get(\"/{alias:string regexp(^[a-z0-9]{1,10}$)}\", Tour)\r\n```\r\n\r\nCommit log: https://github.com/kataras/iris/compare/v11.2.5...v11.2.6\r\n\r\n# Mo, 12 August 2019 | v11.2.5\r\n\r\n- [New Feature: Problem Details for HTTP APIs](https://github.com/kataras/iris/pull/1336)\r\n- [Add Context.AbsoluteURI](https://github.com/kataras/iris/pull/1336/files#diff-15cce7299aae8810bcab9b0bf9a2fdb1R2368)\r\n\r\nCommit log: https://github.com/kataras/iris/compare/v11.2.4...v11.2.5\r\n\r\n# Fr, 09 August 2019 | v11.2.4\r\n\r\n- Fixes [iris.Jet: no view engine found for '.jet' or '.html'](https://github.com/kataras/iris/issues/1327)\r\n- Fixes [ctx.ViewData not work with JetEngine](https://github.com/kataras/iris/issues/1330)\r\n- **New Feature**: [HTTP Method Override](https://github.com/kataras/iris/issues/1325)\r\n- Fixes [Poor performance of session.UpdateExpiration on 200 thousands+ keys with new radix lib](https://github.com/kataras/iris/issues/1328) by introducing the `sessions.Config.Driver` configuration field which defaults to `Redigo()` but can be set to `Radix()` too, future additions are welcomed.\r\n\r\nCommit log: https://github.com/kataras/iris/compare/v11.2.3...v11.2.4\r\n\r\n# Tu, 30 July 2019 | v11.2.3\r\n\r\n- [New Feature: Handle different parameter types in the same path](https://github.com/kataras/iris/issues/1315)\r\n- [New Feature: Content Negotiation](https://github.com/kataras/iris/issues/1319)\r\n- [Context.ReadYAML](https://github.com/kataras/iris/tree/main/_examples/request-body/read-yaml)\r\n- Fixes https://github.com/kataras/neffos/issues/1#issuecomment-515698536\r\n\r\n# We, 24 July 2019 | v11.2.2\r\n\r\nSessions as middleware:\r\n\r\n```go\r\nimport \"github.com/kataras/iris/v12/sessions\"\r\n// [...]\r\n\r\napp := iris.New()\r\nsess := sessions.New(sessions.Config{...})\r\n\r\napp.Get(\"/path\", func(ctx iris.Context){\r\n    session := sessions.Get(ctx)\r\n    // [work with session...]\r\n})\r\n```\r\n\r\n- Add `Session.Len() int` to return the total number of stored values/entries.\r\n- Make `Context.HTML` and `Context.Text` to accept an optional, variadic, `args ...any` input arg(s) too.\r\n\r\n## v11.1.1\r\n\r\n- https://github.com/kataras/iris/issues/1298\r\n- https://github.com/kataras/iris/issues/1207\r\n\r\n# Tu, 23 July 2019 | v11.2.0\r\n\r\nRead about the new release at: https://www.facebook.com/iris.framework/posts/3276606095684693\r\n"
  },
  {
    "path": "LICENSE",
    "content": "BSD 3-Clause License\r\n\r\nCopyright (c) 2016-2026, Gerasimos (Makis) Maropoulos\r\nAll rights reserved.\r\n\r\nRedistribution and use in source and binary forms, with or without\r\nmodification, are permitted provided that the following conditions are met:\r\n\r\n1. Redistributions of source code must retain the above copyright notice, this\r\n   list of conditions and the following disclaimer.\r\n\r\n2. Redistributions in binary form must reproduce the above copyright notice,\r\n   this list of conditions and the following disclaimer in the documentation\r\n   and/or other materials provided with the distribution.\r\n\r\n3. Neither the name of the copyright holder nor the names of its\r\n   contributors may be used to endorse or promote products derived from\r\n   this software without specific prior written permission.\r\n\r\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\r\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\r\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\r\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\r\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\r\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\r\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\r\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\r\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r\n"
  },
  {
    "path": "NOTICE",
    "content": "================================================================================\n\n                         Third-Party Software for iris\n\n================================================================================\n\nThe following 3rd-party software packages may be used by or distributed with iris. This document was automatically generated by FOSSA on 4 Oct 2020; any information relevant to third-party vendors listed below are collected using common, reasonable means.\n\nRevision ID: 5fc50a00491616d5cd0cbce3abd8b699838e25ca\n\n-----------------   -----------------   ------------------------------------------\n Package             Version             Website                                  \n-----------------   -----------------   ------------------------------------------       \n ace                 ea038f4770b6746     https://github.com/yosssi/ace       \n                     c3f8f84f14fa60d                                              \n                     9fe1205b56                                   \n badger              536fed1846d0f4d     https://github.com/dgraph-io/badger      \n                     b9579bcff679761                                              \n                     4e134eadfa                                                   \n bbolt               a8af23b57f672fe     https://github.com/etcd-io/bbolt         \n                     f05637de531bba5                                              \n                     aa00013364                                          \n markdown            2ced44d5b58482a     https://github.com/gomarkdown/markdown        \n                     9b77d1abad4c3d3                              \n                     4b190880fe                                                 \n bluemonday          0a75d7616912ab9     https://github.com/microcosm-cc/bluemonday         \n                     beb9cc6f7283ec1                                    \n                     917c61b135\n blocks              2782010d7127295     https://github.com/kataras/blocks         \n                     d72dc16632c7c0c                                    \n                     01dfbf6ceb   \n brotli              c3da72aa01ed78f     https://github.com/andybalholm/brotli       \n                     164593b9624fd91                                              \n                     d25082d2d2                                                    \n closestmatch        1fbe626be92eb4c     https://github.com/schollz/closestmatch  \n                     347d182cae9f8f0                                              \n                     0a046bf2f4                                                                                                      \n compress            4a2d40e4b07e5b3     https://github.com/klauspost/compress    \n                     d333bc0569facd0                                              \n                     f2dbf4ef39                                                   \n crypto              4b2356b1ed79e6b     https://go.googlesource.com/crypto       \n                     e3deca3737a3db3                                              \n                     d132d2847a                                                                                                   \n easyjson            8ab5ff9cd8e4e43     https://github.com/mailru/easyjson\n                     2e8b79f6c47d324\n                     a31dd803cf\n\n securecookie        e59506cc896acb7    https://github.com/gorilla/securecookie\n                     f7bf732d4fdf5e2\n                     5f7ccd8983\n semver              4487282d78122a2     https://github.com/blang/semver \n                     45e413d7515e7c5                                              \n                     16b70c33fd                                                                                                        \n golog               f7561df84e64ab9     https://github.com/kataras/golog         \n                     212f021923ce4ff                                              \n                     db5df5594d                                                           \n goreferrer          ec9c9a553398739     https://github.com/Shopify/goreferrer    \n                     f0dcf817e0ad5e0                                              \n                     1c4e7dcd08                                                   \n httpexpect          bfc40287c2c3ad4     https://github.com/iris-contrib/         \n                     cdf8c4f40e7908a     httpexpect                               \n                     b137f9227d                                                   \n ini.v1              39bc4ddcb8b9d01     https://gopkg.in/ini.v1                  \n                     00f7a040816380c                                              \n                     cda878b94a                                                   \n jade                92f294662063510     https://github.com/iris-contrib/jade     \n                     cac723decad0fba                                              \n                     2ebc878560                                                   \n jet                 305ebcf60d48fce     https://github.com/CloudyKit/jet         \n                     5905bdf00159a0e                                              \n                     1029f1d962                                                   \n msgpack             911bfe50493ebbc     https://github.com/vmihailenco/msgpack   \n                     b6e0af9e6f36451                                              \n                     255746ff46       \n minify              119ab8b676c60a6     https://github.com/tdewolff/minify   \n                     12b9cc824e6a84a                                              \n                     865191aabb                                                      \n neffos              2221a9afc8392b9     https://github.com/kataras/neffos        \n                     a3e984473dd34f8                                              \n                     380ce80840                                                     \n pio                 2e3d576cc65913a     https://github.com/kataras/pio           \n                     dd6106f1ce02837                                              \n                     2c7e6d943c                                                   \n pongo2              f946812ec8d53b7     https://github.com/flosch/pongo2   \n                     24e4daeb888c95a                                              \n                     b63c98b3c0                               \n protobuf            6c66de79d66478d     https://github.com/golang/protobuf       \n                     166c7ea05f5d2cc                                              \n                     af016fbd6b                                                                                                    \n raymond             cd18a09da5ea49b     https://github.com/mailgun/raymond      \n                     b9a8cbec107a22a                                               \n                     9c0e792c21      \n                                                                 \n go-redis            7125bf611e5d7d9     https://github.com/go-redis/redis\n                     bb4487dd6cb80d8\n                     88bad92d23                                                            \n schema              1f5dc3fa1ac5179     https://github.com/iris-contrib/schema   \n                     78c014cb1df9954                                              \n                     0fa5b17f7e                                                   \n sitemap             f223711fe0c7a64     https://github.com/kataras/sitemap       \n                     2bb2d8f2645f0ed                                              \n                     386ba577bc                                                   \n structs             878a968ab225483     https://github.com/fatih/structs         \n                     62a09bdb3322f98                                              \n                     b00f470d46                                                                                                  \n toml                3012a1dbe2e4bd1     https://github.com/BurntSushi/toml       \n                     391d42b32f0577c                                              \n                     b7bbc7f005                                                   \n jwt                 933b4a74659b074     https://github.com/kataras/jwt      \n                     00070920d0700b9                                              \n                     63fa545d6c                                                                                                                            \n uuid                cb32006e483f2a2     https://github.com/google/uuid       \n                     3230e24209cf185                                              \n                     c65b477dbf\n"
  },
  {
    "path": "README.md",
    "content": "<!--<h1><img width=\"24\" height=\"25\" src =\"https://www.iris-go.com/images/logo-new-lq-45.png\"/> News</h1>\n\n Iris version **12.2.0** has been [released](HISTORY.md#sa-11-march-2023--v1220)! As always, the latest version of Iris comes with the promise of lifetime active maintenance.\n\nTry the official [Iris Command Line Interface](https://github.com/kataras/iris-cli) today! -->\n\n# <a href=\"https://iris-go.com\"><img src=\"https://iris-go.com/iris-terminal-55.png\" width=\"50px\" height=\"50px\" style=\"margin-bottom: -5px\" ></a> Iris Web Framework <a href=\"README_GR.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-greece.svg\" /> <a href=\"README_JA.md\"><img width=\"20px\" height=\"20px\" src=\"https://iris-go.com/static/images/flag-japan.svg\" /></a> </a> <a href=\"README_FR.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-france.svg\" /></a> <a href=\"README_ZH_HANT.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-taiwan.svg\" /></a> <a href=\"README_ZH_HANS.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-china.svg\" /></a> <a href=\"README_ES.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-spain.png\" /></a> <a href=\"README_FA.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-iran.svg\" /></a> <a href=\"README_RU.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-russia.svg\" /></a> <a href=\"README_KO.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-south-korea.svg?v=12\" /></a> <a href=\"README_PT_BR.md\"><img width=\"20px\" height=\"20px\" src=\"https://iris-go.com/static/images/flag-brazil.svg\" /></a> <a href=\"README_VN.md\"><img width=\"20px\" height=\"20px\" src=\"https://iris-go.com/static/images/flag-vietnam.svg\" /></a>\n\n[![build status](https://img.shields.io/github/actions/workflow/status/kataras/iris/ci.yml?branch=main&style=for-the-badge)](https://github.com/kataras/iris/actions/workflows/ci.yml) [![view examples](https://img.shields.io/badge/examples%20-285-a83adf.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/main/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=cc2b5e&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community) <!--[![FOSSA Status](https://img.shields.io/badge/LICENSE%20SCAN-PASSING❤️-CD2956?style=for-the-badge&logo=fossa)](https://app.fossa.io/projects/git%2Bgithub.com%2Fkataras%2Firis?ref=badge_shield)--> [![donate](https://img.shields.io/badge/support-Iris-blue.svg?style=for-the-badge&logo=paypal)](https://iris-go.com/donate) <!--[![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=for-the-badge)](https://goreportcard.com/report/github.com/kataras/iris)--><!--[![godocs](https://img.shields.io/badge/go-%20docs-488AC7.svg?style=for-the-badge)](https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.11)--> <!-- [![release](https://img.shields.io/badge/release%20-v12.0-0077b3.svg?style=for-the-badge)](https://github.com/kataras/iris/releases) -->\n\n🎁 Iris is a fast, simple yet fully featured and very efficient web framework for Go - the perfect gift for developers this holiday season!\n\n✨ It provides a beautifully expressive and easy to use foundation for your next website or API, wrapped with care and decorated with powerful features.\n\n🌟 Learn what [others saying about Iris](https://www.iris-go.com/#review) and **[star](https://github.com/kataras/iris/stargazers)** this open-source project\n\n[![](https://iris-go.com/static/images/reviews.gif)](https://iris-go.com/testimonials/)\n\n[![Benchmarks: Jul 18, 2020 at 10:46am (UTC)](https://iris-go.com/static/images/benchmarks.svg)](https://github.com/kataras/server-benchmarks)\n\n```go\npackage main\n\nimport \"github.com/kataras/iris/v12\"\n\nfunc main() {\n  app := iris.New()\n  app.Use(iris.Compression)\n\n  app.Get(\"/\", func(ctx iris.Context) {\n    ctx.HTML(\"Happy New Year <strong>%s</strong>! 🎅\", \"World\")\n  })\n\n  app.Listen(\":8080\")\n}\n```\n\n<!-- <details><summary>More with simple Handler</summary>\n\n```go\npackage main\n\nimport \"github.com/kataras/iris/v12\"\n\ntype (\n  request struct {\n    Firstname string `json:\"firstname\"`\n    Lastname  string `json:\"lastname\"`\n  }\n\n  response struct {\n    ID      string `json:\"id\"`\n    Message string `json:\"message\"`\n  }\n)\n\nfunc main() {\n  app := iris.New()\n  app.Handle(\"PUT\", \"/users/{id:uuid}\", updateUser)\n  app.Listen(\":8080\")\n}\n\nfunc updateUser(ctx iris.Context) {\n  id := ctx.Params().Get(\"id\")\n\n  var req request\n  if err := ctx.ReadJSON(&req); err != nil {\n    ctx.StopWithError(iris.StatusBadRequest, err)\n    return\n  }\n\n  resp := response{\n    ID:      id,\n    Message: req.Firstname + \" updated successfully\",\n  }\n  ctx.JSON(resp)\n}\n```\n\n> Read the [routing examples](https://github.com/kataras/iris/blob/main/_examples/routing) for more!\n\n</details>\n\n<details><summary>Handler with custom input and output arguments</summary>\n\n[![https://github.com/kataras/iris/blob/main/_examples/dependency-injection/basic/main.go](https://user-images.githubusercontent.com/22900943/105253731-b8db6d00-5b88-11eb-90c1-0c92a5581c86.png)](https://twitter.com/iris_framework/status/1234783655408668672)\n\n> Interesting? Read the [examples](https://github.com/kataras/iris/blob/main/_examples/dependency-injection).\n\n</details>\n\n<details><summary>Party Controller (NEW)</summary>\n\n> Head over to the [full running example](https://github.com/kataras/iris/blob/main/_examples/routing/party-controller)!\n\n</details>\n\n<details><summary>MVC</summary>\n\n```go\npackage main\n\nimport (\n  \"github.com/kataras/iris/v12\"\n  \"github.com/kataras/iris/v12/mvc\"\n)\n\ntype (\n  request struct {\n    Firstname string `json:\"firstname\"`\n    Lastname  string `json:\"lastname\"`\n  }\n\n  response struct {\n    ID      uint64 `json:\"id\"`\n    Message string `json:\"message\"`\n  }\n)\n\nfunc main() {\n  app := iris.New()\n  mvc.Configure(app.Party(\"/users\"), configureMVC)\n  app.Listen(\":8080\")\n}\n\nfunc configureMVC(app *mvc.Application) {\n  app.Handle(new(userController))\n}\n\ntype userController struct {\n  // [...dependencies]\n}\n\nfunc (c *userController) PutBy(id uint64, req request) response {\n  return response{\n    ID:      id,\n    Message: req.Firstname + \" updated successfully\",\n  }\n}\n```\n\nWant to see more? Navigate through [mvc examples](_examples/mvc)!\n</details>\n\n\n<details><summary>API Guide <strong>HOT</strong></summary>\n\n```go\npackage main\n\nimport (\n  // [other packages...]\n\n  \"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n  iris.NewGuide().\n    AllowOrigin(\"*\").\n    Compression(true).\n    Health(true, \"development\", \"kataras\").\n    Timeout(0, 20*time.Second, 20*time.Second).\n    Middlewares(basicauth.New(...)).\n    Services(\n        // NewDatabase(),\n        // NewPostgresRepositoryRegistry,\n        // NewUserService,\n    ).\n    API(\"/users\", new(UsersAPI)).\n    Listen(\":80\")\n}\n```\n\n</details>\n\n<br/>\n\n-->\n\nAs one [Go developer](https://twitter.com/dkuye/status/1532087942696554497) once said, **Iris got you covered all-round and standing strong over the years** ⭐\n\nSome of the features Iris offers:\n\n* HTTP/2 (Push, even Embedded data)\n* Middleware (Accesslog, Basicauth, CORS, gRPC, Anti-Bot hCaptcha, JWT, MethodOverride, ModRevision, Monitor, PPROF, Ratelimit, Anti-Bot reCaptcha, Recovery, RequestID, Rewrite)\n* API Versioning\n* Model-View-Controller\n* Websockets\n* gRPC\n* Auto-HTTPS\n* Builtin support for ngrok to put your app on the internet, the fastest way\n* Unique Router with dynamic path as parameter with standard types like :uuid, :string, :int... and the ability to create your own\n* Compression\n* View Engines (HTML, Django, Handlebars, Pug/Jade and more)\n* Create your own File Server and host your own WebDAV server\n* Cache\n* Localization (i18n, sitemap)\n* Sessions\n* Rich Responses (HTML, Text, Markdown, XML, YAML, Binary, JSON, JSONP, Protocol Buffers, MessagePack, Content Negotiation, Streaming, Server-Sent Events and more)\n* Response Compression (gzip, deflate, brotli, snappy, s2)\n* Rich Requests (Bind URL Query, Headers, Form, Text, XML, YAML, Binary, JSON, Validation, Protocol Buffers, MessagePack and more)\n* Dependency Injection (MVC, Handlers, API Routers)\n* Testing Suite\n* And the most important... you get fast answers and support from the 1st day until now - that's six full years!\n\n## 👑 <a href=\"https://iris-go.com/donate\">Supporters</a>\n\nWith your help, we can improve Open Source web development for everyone!\n\n<p>\n  <a href=\"https://github.com/getsentry\"><img src=\"https://avatars1.githubusercontent.com/u/1396951?v=4\" alt=\"getsentry\" title=\"getsentry\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/github\"><img src=\"https://avatars1.githubusercontent.com/u/9919?v=4\" alt=\"github\" title=\"github\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/lensesio\"><img src=\"https://avatars1.githubusercontent.com/u/11728472?v=4\" alt=\"lensesio\" title=\"lensesio\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/thepunterbot\"><img src=\"https://avatars1.githubusercontent.com/u/111136029?v=4\" alt=\"thepunterbot\" title=\"thepunterbot\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/h4rdc0m\"><img src=\"https://avatars1.githubusercontent.com/u/682256?v=4\" alt=\"h4rdc0m\" title=\"h4rdc0m\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/draFWM\"><img src=\"https://avatars1.githubusercontent.com/u/5765340?v=4\" alt=\"draFWM\" title=\"draFWM\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/gf3\"><img src=\"https://avatars1.githubusercontent.com/u/18397?v=4\" alt=\"gf3\" title=\"gf3\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/trading-peter\"><img src=\"https://avatars1.githubusercontent.com/u/11567985?v=4\" alt=\"trading-peter\" title=\"trading-peter\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/AlbinoGeek\"><img src=\"https://avatars1.githubusercontent.com/u/1910461?v=4\" alt=\"AlbinoGeek\" title=\"AlbinoGeek\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/basilarchia\"><img src=\"https://avatars1.githubusercontent.com/u/926033?v=4\" alt=\"basilarchia\" title=\"basilarchia\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/sumjoe\"><img src=\"https://avatars1.githubusercontent.com/u/32655210?v=4\" alt=\"sumjoe\" title=\"sumjoe\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/simpleittools\"><img src=\"https://avatars1.githubusercontent.com/u/42871067?v=4\" alt=\"simpleittools\" title=\"simpleittools\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/xiaozhuai\"><img src=\"https://avatars1.githubusercontent.com/u/4773701?v=4\" alt=\"xiaozhuai\" title=\"xiaozhuai\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Remydeme\"><img src=\"https://avatars1.githubusercontent.com/u/22757039?v=4\" alt=\"Remydeme\" title=\"Remydeme\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/celsosz\"><img src=\"https://avatars1.githubusercontent.com/u/3466493?v=4\" alt=\"celsosz\" title=\"celsosz\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/linxcoder\"><img src=\"https://avatars1.githubusercontent.com/u/1050802?v=4\" alt=\"linxcoder\" title=\"linxcoder\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/jnelle\"><img src=\"https://avatars1.githubusercontent.com/u/36324542?v=4\" alt=\"jnelle\" title=\"jnelle\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/TechMaster\"><img src=\"https://avatars1.githubusercontent.com/u/1491686?v=4\" alt=\"TechMaster\" title=\"TechMaster\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/janwebdev\"><img src=\"https://avatars1.githubusercontent.com/u/6725905?v=4\" alt=\"janwebdev\" title=\"janwebdev\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/altafino\"><img src=\"https://avatars1.githubusercontent.com/u/24539467?v=4\" alt=\"altafino\" title=\"altafino\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/jakoubek\"><img src=\"https://avatars1.githubusercontent.com/u/179566?v=4\" alt=\"jakoubek\" title=\"jakoubek\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/alekperos\"><img src=\"https://avatars1.githubusercontent.com/u/683938?v=4\" alt=\"alekperos\" title=\"alekperos\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/day0ng\"><img src=\"https://avatars1.githubusercontent.com/u/15760418?v=4\" alt=\"day0ng\" title=\"day0ng\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/hengestone\"><img src=\"https://avatars1.githubusercontent.com/u/362587?v=4\" alt=\"hengestone\" title=\"hengestone\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/thomasfr\"><img src=\"https://avatars1.githubusercontent.com/u/287432?v=4\" alt=\"thomasfr\" title=\"thomasfr\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/code-chimp\"><img src=\"https://avatars1.githubusercontent.com/u/50490?v=4\" alt=\"code-chimp\" title=\"code-chimp\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/CetinBasoz\"><img src=\"https://avatars1.githubusercontent.com/u/3152637?v=4\" alt=\"CetinBasoz\" title=\"CetinBasoz\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/International\"><img src=\"https://avatars1.githubusercontent.com/u/1022918?v=4\" alt=\"International\" title=\"International\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Juanses\"><img src=\"https://avatars1.githubusercontent.com/u/6137970?v=4\" alt=\"Juanses\" title=\"Juanses\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/SometimesMage\"><img src=\"https://avatars1.githubusercontent.com/u/1435257?v=4\" alt=\"SometimesMage\" title=\"SometimesMage\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/ansrivas\"><img src=\"https://avatars1.githubusercontent.com/u/1695056?v=4\" alt=\"ansrivas\" title=\"ansrivas\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/boreevyuri\"><img src=\"https://avatars1.githubusercontent.com/u/10973128?v=4\" alt=\"boreevyuri\" title=\"boreevyuri\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/brentwilson\"><img src=\"https://avatars1.githubusercontent.com/u/11813757?v=4\" alt=\"brentwilson\" title=\"brentwilson\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/camilbinas\"><img src=\"https://avatars1.githubusercontent.com/u/42443219?v=4\" alt=\"camilbinas\" title=\"camilbinas\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/ekobayong\"><img src=\"https://avatars1.githubusercontent.com/u/878170?v=4\" alt=\"ekobayong\" title=\"ekobayong\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/lexrus\"><img src=\"https://avatars1.githubusercontent.com/u/219689?v=4\" alt=\"lexrus\" title=\"lexrus\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/li3p\"><img src=\"https://avatars1.githubusercontent.com/u/55519?v=4\" alt=\"li3p\" title=\"li3p\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/madhu72\"><img src=\"https://avatars1.githubusercontent.com/u/10324127?v=4\" alt=\"madhu72\" title=\"madhu72\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/mosorize\"><img src=\"https://avatars1.githubusercontent.com/u/107021151?v=4\" alt=\"mosorize\" title=\"mosorize\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/se77en\"><img src=\"https://avatars1.githubusercontent.com/u/1468284?v=4\" alt=\"se77en\" title=\"se77en\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/tstangenberg\"><img src=\"https://avatars1.githubusercontent.com/u/736160?v=4\" alt=\"tstangenberg\" title=\"tstangenberg\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/vincent-li\"><img src=\"https://avatars1.githubusercontent.com/u/765470?v=4\" alt=\"vincent-li\" title=\"vincent-li\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/DavidShaw\"><img src=\"https://avatars1.githubusercontent.com/u/356970?v=4\" alt=\"DavidShaw\" title=\"DavidShaw\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/sascha11110\"><img src=\"https://avatars1.githubusercontent.com/u/15168372?v=4\" alt=\"sascha11110\" title=\"sascha11110\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/clichi2002\"><img src=\"https://avatars1.githubusercontent.com/u/5856121?v=4\" alt=\"clichi2002\" title=\"clichi2002\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/derReineke\"><img src=\"https://avatars1.githubusercontent.com/u/35681013?v=4\" alt=\"derReineke\" title=\"derReineke\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Sirisap22\"><img src=\"https://avatars1.githubusercontent.com/u/58851659?v=4\" alt=\"Sirisap22\" title=\"Sirisap22\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/primadi\"><img src=\"https://avatars1.githubusercontent.com/u/7625413?v=4\" alt=\"primadi\" title=\"primadi\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/agoncecelia\"><img src=\"https://avatars1.githubusercontent.com/u/10442924?v=4\" alt=\"agoncecelia\" title=\"agoncecelia\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/chrisliang12\"><img src=\"https://avatars1.githubusercontent.com/u/97201988?v=4\" alt=\"chrisliang12\" title=\"chrisliang12\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/zyu\"><img src=\"https://avatars1.githubusercontent.com/u/807397?v=4\" alt=\"zyu\" title=\"zyu\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/hobysmith\"><img src=\"https://avatars1.githubusercontent.com/u/6063391?v=4\" alt=\"hobysmith\" title=\"hobysmith\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/pluja\"><img src=\"https://avatars1.githubusercontent.com/u/64632615?v=4\" alt=\"pluja\" title=\"pluja\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/antonio-pedrazzini\"><img src=\"https://avatars1.githubusercontent.com/u/83503326?v=4\" alt=\"antonio-pedrazzini\" title=\"antonio-pedrazzini\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/clacroix\"><img src=\"https://avatars1.githubusercontent.com/u/611064?v=4\" alt=\"clacroix\" title=\"clacroix\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/njeff3\"><img src=\"https://avatars1.githubusercontent.com/u/9838120?v=4\" alt=\"njeff3\" title=\"njeff3\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/ixalender\"><img src=\"https://avatars1.githubusercontent.com/u/877376?v=4\" alt=\"ixalender\" title=\"ixalender\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/mubariz-ahmed\"><img src=\"https://avatars1.githubusercontent.com/u/18215455?v=4\" alt=\"mubariz-ahmed\" title=\"mubariz-ahmed\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Cesar\"><img src=\"https://avatars1.githubusercontent.com/u/1581870?v=4\" alt=\"Cesar\" title=\"Cesar\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/th31nitiate\"><img src=\"https://avatars1.githubusercontent.com/u/14749635?v=4\" alt=\"th31nitiate\" title=\"th31nitiate\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/stgrosshh\"><img src=\"https://avatars1.githubusercontent.com/u/8356082?v=4\" alt=\"stgrosshh\" title=\"stgrosshh\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Didainius\"><img src=\"https://avatars1.githubusercontent.com/u/15804230?v=4\" alt=\"Didainius\" title=\"Didainius\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/DmarshalTU\"><img src=\"https://avatars1.githubusercontent.com/u/59089266?v=4\" alt=\"DmarshalTU\" title=\"DmarshalTU\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/IwateKyle\"><img src=\"https://avatars1.githubusercontent.com/u/658799?v=4\" alt=\"IwateKyle\" title=\"IwateKyle\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Little-YangYang\"><img src=\"https://avatars1.githubusercontent.com/u/10755202?v=4\" alt=\"Little-YangYang\" title=\"Little-YangYang\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Major2828\"><img src=\"https://avatars1.githubusercontent.com/u/19783402?v=4\" alt=\"Major2828\" title=\"Major2828\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/MatejLach\"><img src=\"https://avatars1.githubusercontent.com/u/531930?v=4\" alt=\"MatejLach\" title=\"MatejLach\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/amritpal042\"><img src=\"https://avatars1.githubusercontent.com/u/60704162?v=4\" alt=\"amritpal042\" title=\"amritpal042\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/andrefiorot\"><img src=\"https://avatars1.githubusercontent.com/u/13743098?v=4\" alt=\"andrefiorot\" title=\"andrefiorot\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/boomhut\"><img src=\"https://avatars1.githubusercontent.com/u/56619040?v=4\" alt=\"boomhut\" title=\"boomhut\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/cshum\"><img src=\"https://avatars1.githubusercontent.com/u/293790?v=4\" alt=\"cshum\" title=\"cshum\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/dtrifonov\"><img src=\"https://avatars1.githubusercontent.com/u/1520118?v=4\" alt=\"dtrifonov\" title=\"dtrifonov\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/gadokrisztian\"><img src=\"https://avatars1.githubusercontent.com/u/85160134?v=4\" alt=\"gadokrisztian\" title=\"gadokrisztian\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/geordee\"><img src=\"https://avatars1.githubusercontent.com/u/83303?v=4\" alt=\"geordee\" title=\"geordee\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/guanting112\"><img src=\"https://avatars1.githubusercontent.com/u/11306350?v=4\" alt=\"guanting112\" title=\"guanting112\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/iantuan\"><img src=\"https://avatars1.githubusercontent.com/u/4869968?v=4\" alt=\"iantuan\" title=\"iantuan\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/ichenhe\"><img src=\"https://avatars1.githubusercontent.com/u/10266066?v=4\" alt=\"ichenhe\" title=\"ichenhe\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/rodrigoghm\"><img src=\"https://avatars1.githubusercontent.com/u/66917643?v=4\" alt=\"rodrigoghm\" title=\"rodrigoghm\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/icibiri\"><img src=\"https://avatars1.githubusercontent.com/u/32684966?v=4\" alt=\"icibiri\" title=\"icibiri\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/jewe11er\"><img src=\"https://avatars1.githubusercontent.com/u/47153959?v=4\" alt=\"jewe11er\" title=\"jewe11er\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/jfloresremar\"><img src=\"https://avatars1.githubusercontent.com/u/10441071?v=4\" alt=\"jfloresremar\" title=\"jfloresremar\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/jingtianfeng\"><img src=\"https://avatars1.githubusercontent.com/u/19503202?v=4\" alt=\"jingtianfeng\" title=\"jingtianfeng\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/kilarusravankumar\"><img src=\"https://avatars1.githubusercontent.com/u/13055113?v=4\" alt=\"kilarusravankumar\" title=\"kilarusravankumar\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/leandrobraga\"><img src=\"https://avatars1.githubusercontent.com/u/506699?v=4\" alt=\"leandrobraga\" title=\"leandrobraga\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/lfbos\"><img src=\"https://avatars1.githubusercontent.com/u/5703286?v=4\" alt=\"lfbos\" title=\"lfbos\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/lpintes\"><img src=\"https://avatars1.githubusercontent.com/u/2546783?v=4\" alt=\"lpintes\" title=\"lpintes\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/macropas\"><img src=\"https://avatars1.githubusercontent.com/u/7488502?v=4\" alt=\"macropas\" title=\"macropas\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/marcmmx\"><img src=\"https://avatars1.githubusercontent.com/u/7670546?v=4\" alt=\"marcmmx\" title=\"marcmmx\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/mark2b\"><img src=\"https://avatars1.githubusercontent.com/u/539063?v=4\" alt=\"mark2b\" title=\"mark2b\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/miguel-devs\"><img src=\"https://avatars1.githubusercontent.com/u/89543510?v=4\" alt=\"miguel-devs\" title=\"miguel-devs\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/mihado\"><img src=\"https://avatars1.githubusercontent.com/u/940981?v=4\" alt=\"mihado\" title=\"mihado\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/mmckeen75\"><img src=\"https://avatars1.githubusercontent.com/u/49529489?v=4\" alt=\"mmckeen75\" title=\"mmckeen75\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/narven\"><img src=\"https://avatars1.githubusercontent.com/u/123594?v=4\" alt=\"narven\" title=\"narven\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/odas0r\"><img src=\"https://avatars1.githubusercontent.com/u/32167770?v=4\" alt=\"odas0r\" title=\"odas0r\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/olaf-lexemo\"><img src=\"https://avatars1.githubusercontent.com/u/51406599?v=4\" alt=\"olaf-lexemo\" title=\"olaf-lexemo\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/pitexplore\"><img src=\"https://avatars1.githubusercontent.com/u/11956562?v=4\" alt=\"pitexplore\" title=\"pitexplore\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/pr123\"><img src=\"https://avatars1.githubusercontent.com/u/23333176?v=4\" alt=\"pr123\" title=\"pr123\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/rsousacode\"><img src=\"https://avatars1.githubusercontent.com/u/34067397?v=4\" alt=\"rsousacode\" title=\"rsousacode\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/sankethpb\"><img src=\"https://avatars1.githubusercontent.com/u/16034868?v=4\" alt=\"sankethpb\" title=\"sankethpb\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/wixregiga\"><img src=\"https://avatars1.githubusercontent.com/u/30182903?v=4\" alt=\"wixregiga\" title=\"wixregiga\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/GeorgeFourikis\"><img src=\"https://avatars1.githubusercontent.com/u/17906313?v=4\" alt=\"GeorgeFourikis\" title=\"GeorgeFourikis\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/saz59\"><img src=\"https://avatars1.githubusercontent.com/u/9706793?v=4\" alt=\"saz59\" title=\"saz59\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/shadowfiga\"><img src=\"https://avatars1.githubusercontent.com/u/42721390?v=4\" alt=\"shadowfiga\" title=\"shadowfiga\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/siriushaha\"><img src=\"https://avatars1.githubusercontent.com/u/7924311?v=4\" alt=\"siriushaha\" title=\"siriushaha\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/skurtz97\"><img src=\"https://avatars1.githubusercontent.com/u/71720714?v=4\" alt=\"skurtz97\" title=\"skurtz97\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/srinivasganti\"><img src=\"https://avatars1.githubusercontent.com/u/2057165?v=4\" alt=\"srinivasganti\" title=\"srinivasganti\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/syrm\"><img src=\"https://avatars1.githubusercontent.com/u/155406?v=4\" alt=\"syrm\" title=\"syrm\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/tuhao1020\"><img src=\"https://avatars1.githubusercontent.com/u/26807520?v=4\" alt=\"tuhao1020\" title=\"tuhao1020\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/BlackHole1\"><img src=\"https://avatars1.githubusercontent.com/u/8198408?v=4\" alt=\"BlackHole1\" title=\"BlackHole1\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/L-M-Sherlock\"><img src=\"https://avatars1.githubusercontent.com/u/32575846?v=4\" alt=\"L-M-Sherlock\" title=\"L-M-Sherlock\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/claudemuller\"><img src=\"https://avatars1.githubusercontent.com/u/8104894?v=4\" alt=\"claudemuller\" title=\"claudemuller\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/keymanye\"><img src=\"https://avatars1.githubusercontent.com/u/9495010?v=4\" alt=\"keymanye\" title=\"keymanye\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/wahyuief\"><img src=\"https://avatars1.githubusercontent.com/u/20138856?v=4\" alt=\"wahyuief\" title=\"wahyuief\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/xuyan2018\"><img src=\"https://avatars1.githubusercontent.com/u/38712502?v=4\" alt=\"xuyan2018\" title=\"xuyan2018\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/xvalen\"><img src=\"https://avatars1.githubusercontent.com/u/2307513?v=4\" alt=\"xvalen\" title=\"xvalen\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/xytis\"><img src=\"https://avatars1.githubusercontent.com/u/78025?v=4\" alt=\"xytis\" title=\"xytis\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/ElNovi\"><img src=\"https://avatars1.githubusercontent.com/u/14199592?v=4\" alt=\"ElNovi\" title=\"ElNovi\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/IpastorSan\"><img src=\"https://avatars1.githubusercontent.com/u/54788305?v=4\" alt=\"IpastorSan\" title=\"IpastorSan\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/KKP4\"><img src=\"https://avatars1.githubusercontent.com/u/24271790?v=4\" alt=\"KKP4\" title=\"KKP4\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Lernakow\"><img src=\"https://avatars1.githubusercontent.com/u/46821665?v=4\" alt=\"Lernakow\" title=\"Lernakow\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/ernestocolombo\"><img src=\"https://avatars1.githubusercontent.com/u/485538?v=4\" alt=\"ernestocolombo\" title=\"ernestocolombo\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/francisstephan\"><img src=\"https://avatars1.githubusercontent.com/u/15109897?v=4\" alt=\"francisstephan\" title=\"francisstephan\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/pixelheresy\"><img src=\"https://avatars1.githubusercontent.com/u/2491944?v=4\" alt=\"pixelheresy\" title=\"pixelheresy\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/rcapraro\"><img src=\"https://avatars1.githubusercontent.com/u/245490?v=4\" alt=\"rcapraro\" title=\"rcapraro\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/soiestad\"><img src=\"https://avatars1.githubusercontent.com/u/9642036?v=4\" alt=\"soiestad\" title=\"soiestad\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/spkarason\"><img src=\"https://avatars1.githubusercontent.com/u/100413497?v=4\" alt=\"spkarason\" title=\"spkarason\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/thanasolykos\"><img src=\"https://avatars1.githubusercontent.com/u/35801329?v=4\" alt=\"thanasolykos\" title=\"thanasolykos\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/ukitzmann\"><img src=\"https://avatars1.githubusercontent.com/u/153834?v=4\" alt=\"ukitzmann\" title=\"ukitzmann\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/DanielKirkwood\"><img src=\"https://avatars1.githubusercontent.com/u/22101308?v=4\" alt=\"DanielKirkwood\" title=\"DanielKirkwood\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/colinf\"><img src=\"https://avatars1.githubusercontent.com/u/530815?v=4\" alt=\"colinf\" title=\"colinf\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/simonproctor\"><img src=\"https://avatars1.githubusercontent.com/u/203916?v=4\" alt=\"simonproctor\" title=\"simonproctor\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/FernandoLangOFC\"><img src=\"https://avatars1.githubusercontent.com/u/84889316?v=4\" alt=\"FernandoLangOFC\" title=\"FernandoLangOFC\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Firdavs9512\"><img src=\"https://avatars1.githubusercontent.com/u/102187486?v=4\" alt=\"Firdavs9512\" title=\"Firdavs9512\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Flammable-Duck\"><img src=\"https://avatars1.githubusercontent.com/u/59183206?v=4\" alt=\"Flammable-Duck\" title=\"Flammable-Duck\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Gepetdo\"><img src=\"https://avatars1.githubusercontent.com/u/5978138?v=4\" alt=\"Gepetdo\" title=\"Gepetdo\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Hongjian0619\"><img src=\"https://avatars1.githubusercontent.com/u/25712119?v=4\" alt=\"Hongjian0619\" title=\"Hongjian0619\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/JoeD\"><img src=\"https://avatars1.githubusercontent.com/u/247821?v=4\" alt=\"JoeD\" title=\"JoeD\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Jude-X\"><img src=\"https://avatars1.githubusercontent.com/u/66228813?v=4\" alt=\"Jude-X\" title=\"Jude-X\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Kartoffelbot\"><img src=\"https://avatars1.githubusercontent.com/u/130631591?v=4\" alt=\"Kartoffelbot\" title=\"Kartoffelbot\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/KevinZhouRafael\"><img src=\"https://avatars1.githubusercontent.com/u/16298046?v=4\" alt=\"KevinZhouRafael\" title=\"KevinZhouRafael\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/KrishManohar\"><img src=\"https://avatars1.githubusercontent.com/u/1992857?v=4\" alt=\"KrishManohar\" title=\"KrishManohar\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Laotanling\"><img src=\"https://avatars1.githubusercontent.com/u/28570289?v=4\" alt=\"Laotanling\" title=\"Laotanling\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Longf99999\"><img src=\"https://avatars1.githubusercontent.com/u/21210800?v=4\" alt=\"Longf99999\" title=\"Longf99999\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Lyansun\"><img src=\"https://avatars1.githubusercontent.com/u/17959642?v=4\" alt=\"Lyansun\" title=\"Lyansun\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/MihaiPopescu1985\"><img src=\"https://avatars1.githubusercontent.com/u/34679869?v=4\" alt=\"MihaiPopescu1985\" title=\"MihaiPopescu1985\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/TBNilles\"><img src=\"https://avatars1.githubusercontent.com/u/88231081?v=4\" alt=\"TBNilles\" title=\"TBNilles\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/ajanicij\"><img src=\"https://avatars1.githubusercontent.com/u/1755297?v=4\" alt=\"ajanicij\" title=\"ajanicij\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/aprinslo1\"><img src=\"https://avatars1.githubusercontent.com/u/711650?v=4\" alt=\"aprinslo1\" title=\"aprinslo1\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Mohammed8960\"><img src=\"https://avatars1.githubusercontent.com/u/5219371?v=4\" alt=\"Mohammed8960\" title=\"Mohammed8960\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/NA\"><img src=\"https://avatars1.githubusercontent.com/u/1600?v=4\" alt=\"NA\" title=\"NA\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Neulhan\"><img src=\"https://avatars1.githubusercontent.com/u/52434903?v=4\" alt=\"Neulhan\" title=\"Neulhan\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/kyoukhana\"><img src=\"https://avatars1.githubusercontent.com/u/756849?v=4\" alt=\"kyoukhana\" title=\"kyoukhana\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/spazzymoto\"><img src=\"https://avatars1.githubusercontent.com/u/2951012?v=4\" alt=\"spazzymoto\" title=\"spazzymoto\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/victorgrey\"><img src=\"https://avatars1.githubusercontent.com/u/207128?v=4\" alt=\"victorgrey\" title=\"victorgrey\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/ArishSultan\"><img src=\"https://avatars1.githubusercontent.com/u/31086233?v=4\" alt=\"ArishSultan\" title=\"ArishSultan\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/ehayun\"><img src=\"https://avatars1.githubusercontent.com/u/39870648?v=4\" alt=\"ehayun\" title=\"ehayun\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/kukaki\"><img src=\"https://avatars1.githubusercontent.com/u/4849535?v=4\" alt=\"kukaki\" title=\"kukaki\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/oshirokazuhide\"><img src=\"https://avatars1.githubusercontent.com/u/89958891?v=4\" alt=\"oshirokazuhide\" title=\"oshirokazuhide\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/t6tg\"><img src=\"https://avatars1.githubusercontent.com/u/33445861?v=4\" alt=\"t6tg\" title=\"t6tg\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/15189573255\"><img src=\"https://avatars1.githubusercontent.com/u/18551476?v=4\" alt=\"15189573255\" title=\"15189573255\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/AGPDev\"><img src=\"https://avatars1.githubusercontent.com/u/5721341?v=4\" alt=\"AGPDev\" title=\"AGPDev\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/AnatolyUA\"><img src=\"https://avatars1.githubusercontent.com/u/1446703?v=4\" alt=\"AnatolyUA\" title=\"AnatolyUA\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/AwsIT\"><img src=\"https://avatars1.githubusercontent.com/u/40926862?v=4\" alt=\"AwsIT\" title=\"AwsIT\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/NguyenPhuoc\"><img src=\"https://avatars1.githubusercontent.com/u/11747677?v=4\" alt=\"NguyenPhuoc\" title=\"NguyenPhuoc\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Oka00\"><img src=\"https://avatars1.githubusercontent.com/u/72302007?v=4\" alt=\"Oka00\" title=\"Oka00\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/PaddyFrenchman\"><img src=\"https://avatars1.githubusercontent.com/u/55139902?v=4\" alt=\"PaddyFrenchman\" title=\"PaddyFrenchman\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/RainerGevers\"><img src=\"https://avatars1.githubusercontent.com/u/32453861?v=4\" alt=\"RainerGevers\" title=\"RainerGevers\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Ramblestsad\"><img src=\"https://avatars1.githubusercontent.com/u/45003009?v=4\" alt=\"Ramblestsad\" title=\"Ramblestsad\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/SamuelNeves\"><img src=\"https://avatars1.githubusercontent.com/u/10797137?v=4\" alt=\"SamuelNeves\" title=\"SamuelNeves\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Scorpio69t\"><img src=\"https://avatars1.githubusercontent.com/u/24680141?v=4\" alt=\"Scorpio69t\" title=\"Scorpio69t\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Serissa4000\"><img src=\"https://avatars1.githubusercontent.com/u/122253262?v=4\" alt=\"Serissa4000\" title=\"Serissa4000\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/TianJIANG\"><img src=\"https://avatars1.githubusercontent.com/u/158459?v=4\" alt=\"TianJIANG\" title=\"TianJIANG\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Ubun1\"><img src=\"https://avatars1.githubusercontent.com/u/13261595?v=4\" alt=\"Ubun1\" title=\"Ubun1\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/WangYajun39\"><img src=\"https://avatars1.githubusercontent.com/u/27052258?v=4\" alt=\"WangYajun39\" title=\"WangYajun39\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/XinYoungCN\"><img src=\"https://avatars1.githubusercontent.com/u/18415580?v=4\" alt=\"XinYoungCN\" title=\"XinYoungCN\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/YukinaMochizuki\"><img src=\"https://avatars1.githubusercontent.com/u/26710554?v=4\" alt=\"YukinaMochizuki\" title=\"YukinaMochizuki\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/a112121788\"><img src=\"https://avatars1.githubusercontent.com/u/1457920?v=4\" alt=\"a112121788\" title=\"a112121788\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/acdias\"><img src=\"https://avatars1.githubusercontent.com/u/11966653?v=4\" alt=\"acdias\" title=\"acdias\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/aeonsthorn\"><img src=\"https://avatars1.githubusercontent.com/u/53945065?v=4\" alt=\"aeonsthorn\" title=\"aeonsthorn\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/agent3bood\"><img src=\"https://avatars1.githubusercontent.com/u/771902?v=4\" alt=\"agent3bood\" title=\"agent3bood\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/ajb-neodynamics-io\"><img src=\"https://avatars1.githubusercontent.com/u/115384296?v=4\" alt=\"ajb-neodynamics-io\" title=\"ajb-neodynamics-io\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/alessandromarotta\"><img src=\"https://avatars1.githubusercontent.com/u/17084152?v=4\" alt=\"alessandromarotta\" title=\"alessandromarotta\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/algobot76\"><img src=\"https://avatars1.githubusercontent.com/u/20016835?v=4\" alt=\"algobot76\" title=\"algobot76\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/algoflows\"><img src=\"https://avatars1.githubusercontent.com/u/65465380?v=4\" alt=\"algoflows\" title=\"algoflows\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/angelaahhu\"><img src=\"https://avatars1.githubusercontent.com/u/128401549?v=4\" alt=\"angelaahhu\" title=\"angelaahhu\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/anhxuanpham\"><img src=\"https://avatars1.githubusercontent.com/u/101174797?v=4\" alt=\"anhxuanpham\" title=\"anhxuanpham\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/annieruci\"><img src=\"https://avatars1.githubusercontent.com/u/49377699?v=4\" alt=\"annieruci\" title=\"annieruci\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/antoniejiao\"><img src=\"https://avatars1.githubusercontent.com/u/17450960?v=4\" alt=\"antoniejiao\" title=\"antoniejiao\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/artman328\"><img src=\"https://avatars1.githubusercontent.com/u/5415792?v=4\" alt=\"artman328\" title=\"artman328\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/b2cbd\"><img src=\"https://avatars1.githubusercontent.com/u/6870050?v=4\" alt=\"b2cbd\" title=\"b2cbd\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/baoch254\"><img src=\"https://avatars1.githubusercontent.com/u/74555344?v=4\" alt=\"baoch254\" title=\"baoch254\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/bastengao\"><img src=\"https://avatars1.githubusercontent.com/u/785335?v=4\" alt=\"bastengao\" title=\"bastengao\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/beytullahakyuz\"><img src=\"https://avatars1.githubusercontent.com/u/10866179?v=4\" alt=\"beytullahakyuz\" title=\"beytullahakyuz\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/bjoroen\"><img src=\"https://avatars1.githubusercontent.com/u/31513139?v=4\" alt=\"bjoroen\" title=\"bjoroen\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/blackHoleNgc1277\"><img src=\"https://avatars1.githubusercontent.com/u/41342763?v=4\" alt=\"blackHoleNgc1277\" title=\"blackHoleNgc1277\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/bunnycodego\"><img src=\"https://avatars1.githubusercontent.com/u/81451316?v=4\" alt=\"bunnycodego\" title=\"bunnycodego\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/carlos-enginner\"><img src=\"https://avatars1.githubusercontent.com/u/59775876?v=4\" alt=\"carlos-enginner\" title=\"carlos-enginner\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/centratelemedia\"><img src=\"https://avatars1.githubusercontent.com/u/99481333?v=4\" alt=\"centratelemedia\" title=\"centratelemedia\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/chrismalek\"><img src=\"https://avatars1.githubusercontent.com/u/9403?v=4\" alt=\"chrismalek\" title=\"chrismalek\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/civicwar\"><img src=\"https://avatars1.githubusercontent.com/u/1858104?v=4\" alt=\"civicwar\" title=\"civicwar\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/cnzhangquan\"><img src=\"https://avatars1.githubusercontent.com/u/5462876?v=4\" alt=\"cnzhangquan\" title=\"cnzhangquan\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/cuong48d\"><img src=\"https://avatars1.githubusercontent.com/u/456049?v=4\" alt=\"cuong48d\" title=\"cuong48d\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/damiensy\"><img src=\"https://avatars1.githubusercontent.com/u/147525?v=4\" alt=\"damiensy\" title=\"damiensy\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/danlanxiaohei\"><img src=\"https://avatars1.githubusercontent.com/u/3272530?v=4\" alt=\"danlanxiaohei\" title=\"danlanxiaohei\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/dextercai\"><img src=\"https://avatars1.githubusercontent.com/u/12377850?v=4\" alt=\"dextercai\" title=\"dextercai\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/dfaugusto\"><img src=\"https://avatars1.githubusercontent.com/u/1554920?v=4\" alt=\"dfaugusto\" title=\"dfaugusto\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/dkzhang\"><img src=\"https://avatars1.githubusercontent.com/u/1091431?v=4\" alt=\"dkzhang\" title=\"dkzhang\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/dloprodu\"><img src=\"https://avatars1.githubusercontent.com/u/664947?v=4\" alt=\"dloprodu\" title=\"dloprodu\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/donam-givita\"><img src=\"https://avatars1.githubusercontent.com/u/107529604?v=4\" alt=\"donam-givita\" title=\"donam-givita\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/dph0899\"><img src=\"https://avatars1.githubusercontent.com/u/124650663?v=4\" alt=\"dph0899\" title=\"dph0899\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/dvitale\"><img src=\"https://avatars1.githubusercontent.com/u/17982034?v=4\" alt=\"dvitale\" title=\"dvitale\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/ec0629\"><img src=\"https://avatars1.githubusercontent.com/u/7861125?v=4\" alt=\"ec0629\" title=\"ec0629\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/edwindna2\"><img src=\"https://avatars1.githubusercontent.com/u/5441354?v=4\" alt=\"edwindna2\" title=\"edwindna2\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/ekiyooka\"><img src=\"https://avatars1.githubusercontent.com/u/44221187?v=4\" alt=\"ekiyooka\" title=\"ekiyooka\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/ekofedriyanto\"><img src=\"https://avatars1.githubusercontent.com/u/1669439?v=4\" alt=\"ekofedriyanto\" title=\"ekofedriyanto\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/eli-yip\"><img src=\"https://avatars1.githubusercontent.com/u/40079533?v=4\" alt=\"eli-yip\" title=\"eli-yip\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/eljefedelrodeodeljefe\"><img src=\"https://avatars1.githubusercontent.com/u/3899684?v=4\" alt=\"eljefedelrodeodeljefe\" title=\"eljefedelrodeodeljefe\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/fenriz07\"><img src=\"https://avatars1.githubusercontent.com/u/9199380?v=4\" alt=\"fenriz07\" title=\"fenriz07\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/ffelipelimao\"><img src=\"https://avatars1.githubusercontent.com/u/28612817?v=4\" alt=\"ffelipelimao\" title=\"ffelipelimao\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/frenchmajesty\"><img src=\"https://avatars1.githubusercontent.com/u/24761660?v=4\" alt=\"frenchmajesty\" title=\"frenchmajesty\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/gastropulgite\"><img src=\"https://avatars1.githubusercontent.com/u/85067528?v=4\" alt=\"gastropulgite\" title=\"gastropulgite\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/geGao123\"><img src=\"https://avatars1.githubusercontent.com/u/6398228?v=4\" alt=\"geGao123\" title=\"geGao123\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/globalflea\"><img src=\"https://avatars1.githubusercontent.com/u/127675?v=4\" alt=\"globalflea\" title=\"globalflea\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/gloudx\"><img src=\"https://avatars1.githubusercontent.com/u/6920756?v=4\" alt=\"gloudx\" title=\"gloudx\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/gnosthi\"><img src=\"https://avatars1.githubusercontent.com/u/17650528?v=4\" alt=\"gnosthi\" title=\"gnosthi\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/gogoswift\"><img src=\"https://avatars1.githubusercontent.com/u/14092975?v=4\" alt=\"gogoswift\" title=\"gogoswift\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/goten002\"><img src=\"https://avatars1.githubusercontent.com/u/5025060?v=4\" alt=\"goten002\" title=\"goten002\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/guanzi008\"><img src=\"https://avatars1.githubusercontent.com/u/20619190?v=4\" alt=\"guanzi008\" title=\"guanzi008\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/hdezoscar93\"><img src=\"https://avatars1.githubusercontent.com/u/21270107?v=4\" alt=\"hdezoscar93\" title=\"hdezoscar93\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/hieungm\"><img src=\"https://avatars1.githubusercontent.com/u/85067528?v=4\" alt=\"hieungm\" title=\"hieungm\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/hieunmg\"><img src=\"https://avatars1.githubusercontent.com/u/85067528?v=4\" alt=\"hieunmg\" title=\"hieunmg\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/homerious\"><img src=\"https://avatars1.githubusercontent.com/u/22523525?v=4\" alt=\"homerious\" title=\"homerious\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/hzxd\"><img src=\"https://avatars1.githubusercontent.com/u/3376231?v=4\" alt=\"hzxd\" title=\"hzxd\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/inyellowbus\"><img src=\"https://avatars1.githubusercontent.com/u/8218128?v=4\" alt=\"inyellowbus\" title=\"inyellowbus\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/iuliancarnaru\"><img src=\"https://avatars1.githubusercontent.com/u/35683015?v=4\" alt=\"iuliancarnaru\" title=\"iuliancarnaru\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/iysaleh\"><img src=\"https://avatars1.githubusercontent.com/u/13583253?v=4\" alt=\"iysaleh\" title=\"iysaleh\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/jackptoke\"><img src=\"https://avatars1.githubusercontent.com/u/54049012?v=4\" alt=\"jackptoke\" title=\"jackptoke\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/jackysywk\"><img src=\"https://avatars1.githubusercontent.com/u/61909173?v=4\" alt=\"jackysywk\" title=\"jackysywk\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/jeff2go\"><img src=\"https://avatars1.githubusercontent.com/u/6629280?v=4\" alt=\"jeff2go\" title=\"jeff2go\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/jeremiahyan\"><img src=\"https://avatars1.githubusercontent.com/u/2705359?v=4\" alt=\"jeremiahyan\" title=\"jeremiahyan\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/joelywz\"><img src=\"https://avatars1.githubusercontent.com/u/43310636?v=4\" alt=\"joelywz\" title=\"joelywz\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/kamolcu\"><img src=\"https://avatars1.githubusercontent.com/u/5095235?v=4\" alt=\"kamolcu\" title=\"kamolcu\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/kana99\"><img src=\"https://avatars1.githubusercontent.com/u/3714069?v=4\" alt=\"kana99\" title=\"kana99\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/edsongley\"><img src=\"https://avatars1.githubusercontent.com/u/35545454?v=4\" alt=\"edsongley\" title=\"edsongley\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/katsubushiken\"><img src=\"https://avatars1.githubusercontent.com/u/43208445?v=4\" alt=\"katsubushiken\" title=\"katsubushiken\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/kattaprasanth\"><img src=\"https://avatars1.githubusercontent.com/u/13375911?v=4\" alt=\"kattaprasanth\" title=\"kattaprasanth\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/keeio\"><img src=\"https://avatars1.githubusercontent.com/u/147525?v=4\" alt=\"keeio\" title=\"keeio\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/keval6706\"><img src=\"https://avatars1.githubusercontent.com/u/36534030?v=4\" alt=\"keval6706\" title=\"keval6706\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/khasanovrs\"><img src=\"https://avatars1.githubusercontent.com/u/6076966?v=4\" alt=\"khasanovrs\" title=\"khasanovrs\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/kkdaypenny\"><img src=\"https://avatars1.githubusercontent.com/u/47559431?v=4\" alt=\"kkdaypenny\" title=\"kkdaypenny\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/knavels\"><img src=\"https://avatars1.githubusercontent.com/u/57287952?v=4\" alt=\"knavels\" title=\"knavels\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/kohakuhubo\"><img src=\"https://avatars1.githubusercontent.com/u/32786755?v=4\" alt=\"kohakuhubo\" title=\"kohakuhubo\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/korowiov\"><img src=\"https://avatars1.githubusercontent.com/u/5020824?v=4\" alt=\"korowiov\" title=\"korowiov\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/kostasvk\"><img src=\"https://avatars1.githubusercontent.com/u/8888490?v=4\" alt=\"kostasvk\" title=\"kostasvk\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/lafayetteDan\"><img src=\"https://avatars1.githubusercontent.com/u/26064396?v=4\" alt=\"lafayetteDan\" title=\"lafayetteDan\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/lbsubash\"><img src=\"https://avatars1.githubusercontent.com/u/101740735?v=4\" alt=\"lbsubash\" title=\"lbsubash\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/leki75\"><img src=\"https://avatars1.githubusercontent.com/u/9675379?v=4\" alt=\"leki75\" title=\"leki75\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/lemuelroberto\"><img src=\"https://avatars1.githubusercontent.com/u/322159?v=4\" alt=\"lemuelroberto\" title=\"lemuelroberto\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/liheyuan\"><img src=\"https://avatars1.githubusercontent.com/u/776423?v=4\" alt=\"liheyuan\" title=\"liheyuan\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/lingyingtan\"><img src=\"https://avatars1.githubusercontent.com/u/15610136?v=4\" alt=\"lingyingtan\" title=\"lingyingtan\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/linuxluigi\"><img src=\"https://avatars1.githubusercontent.com/u/8136842?v=4\" alt=\"linuxluigi\" title=\"linuxluigi\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/lipatti\"><img src=\"https://avatars1.githubusercontent.com/u/38935867?v=4\" alt=\"lipatti\" title=\"lipatti\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/maikelcoke\"><img src=\"https://avatars1.githubusercontent.com/u/51384?v=4\" alt=\"maikelcoke\" title=\"maikelcoke\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/marek-kuticka\"><img src=\"https://avatars1.githubusercontent.com/u/1578756?v=4\" alt=\"marek-kuticka\" title=\"marek-kuticka\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/marman-hp\"><img src=\"https://avatars1.githubusercontent.com/u/2398413?v=4\" alt=\"marman-hp\" title=\"marman-hp\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/mattbowen\"><img src=\"https://avatars1.githubusercontent.com/u/46803?v=4\" alt=\"mattbowen\" title=\"mattbowen\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/maxgozou\"><img src=\"https://avatars1.githubusercontent.com/u/54620900?v=4\" alt=\"maxgozou\" title=\"maxgozou\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/maxgozzz\"><img src=\"https://avatars1.githubusercontent.com/u/54620900?v=4\" alt=\"maxgozzz\" title=\"maxgozzz\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/mitas\"><img src=\"https://avatars1.githubusercontent.com/u/954460?v=4\" alt=\"mitas\" title=\"mitas\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/mizzlespot\"><img src=\"https://avatars1.githubusercontent.com/u/2654538?v=4\" alt=\"mizzlespot\" title=\"mizzlespot\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/mkell43\"><img src=\"https://avatars1.githubusercontent.com/u/362697?v=4\" alt=\"mkell43\" title=\"mkell43\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/mnievesco\"><img src=\"https://avatars1.githubusercontent.com/u/78430169?v=4\" alt=\"mnievesco\" title=\"mnievesco\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/mo3lyana\"><img src=\"https://avatars1.githubusercontent.com/u/4528809?v=4\" alt=\"mo3lyana\" title=\"mo3lyana\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/motogo\"><img src=\"https://avatars1.githubusercontent.com/u/1704958?v=4\" alt=\"motogo\" title=\"motogo\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/mtrense\"><img src=\"https://avatars1.githubusercontent.com/u/1008285?v=4\" alt=\"mtrense\" title=\"mtrense\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/mukunhao\"><img src=\"https://avatars1.githubusercontent.com/u/45845255?v=4\" alt=\"mukunhao\" title=\"mukunhao\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/mulyawansentosa\"><img src=\"https://avatars1.githubusercontent.com/u/29946673?v=4\" alt=\"mulyawansentosa\" title=\"mulyawansentosa\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/nasoma\"><img src=\"https://avatars1.githubusercontent.com/u/19878418?v=4\" alt=\"nasoma\" title=\"nasoma\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/ngseiyu\"><img src=\"https://avatars1.githubusercontent.com/u/44496936?v=4\" alt=\"ngseiyu\" title=\"ngseiyu\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/nikharsaxena\"><img src=\"https://avatars1.githubusercontent.com/u/8684362?v=4\" alt=\"nikharsaxena\" title=\"nikharsaxena\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/nronzel\"><img src=\"https://avatars1.githubusercontent.com/u/86695181?v=4\" alt=\"nronzel\" title=\"nronzel\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/odelanno\"><img src=\"https://avatars1.githubusercontent.com/u/63109824?v=4\" alt=\"odelanno\" title=\"odelanno\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/onlysumitg\"><img src=\"https://avatars1.githubusercontent.com/u/1676132?v=4\" alt=\"onlysumitg\" title=\"onlysumitg\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/xPoppa\"><img src=\"https://avatars1.githubusercontent.com/u/119574198?v=4\" alt=\"xPoppa\" title=\"xPoppa\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/yesudeep\"><img src=\"https://avatars1.githubusercontent.com/u/3874?v=4\" alt=\"yesudeep\" title=\"yesudeep\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/ymonk\"><img src=\"https://avatars1.githubusercontent.com/u/13493968?v=4\" alt=\"ymonk\" title=\"ymonk\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/yonson2\"><img src=\"https://avatars1.githubusercontent.com/u/1192599?v=4\" alt=\"yonson2\" title=\"yonson2\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/yshengliao\"><img src=\"https://avatars1.githubusercontent.com/u/13849858?v=4\" alt=\"yshengliao\" title=\"yshengliao\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/ytxmobile98\"><img src=\"https://avatars1.githubusercontent.com/u/5900105?v=4\" alt=\"ytxmobile98\" title=\"ytxmobile98\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/yusong-offx\"><img src=\"https://avatars1.githubusercontent.com/u/75306828?v=4\" alt=\"yusong-offx\" title=\"yusong-offx\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/zhenggangpku\"><img src=\"https://avatars1.githubusercontent.com/u/18161030?v=4\" alt=\"zhenggangpku\" title=\"zhenggangpku\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/zou8944\"><img src=\"https://avatars1.githubusercontent.com/u/18495995?v=4\" alt=\"zou8944\" title=\"zou8944\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/SergeShin\"><img src=\"https://avatars1.githubusercontent.com/u/402395?v=4\" alt=\"SergeShin\" title=\"SergeShin\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/-\"><img src=\"https://avatars1.githubusercontent.com/u/75544?v=4\" alt=\"-\" title=\"-\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/BelmonduS\"><img src=\"https://avatars1.githubusercontent.com/u/159350?v=4\" alt=\"BelmonduS\" title=\"BelmonduS\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Diewald\"><img src=\"https://avatars1.githubusercontent.com/u/6187336?v=4\" alt=\"Diewald\" title=\"Diewald\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/cty4ka\"><img src=\"https://avatars1.githubusercontent.com/u/29261879?v=4\" alt=\"cty4ka\" title=\"cty4ka\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/martinjanda\"><img src=\"https://avatars1.githubusercontent.com/u/122393?v=4\" alt=\"martinjanda\" title=\"martinjanda\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/evan\"><img src=\"https://avatars1.githubusercontent.com/u/210?v=4\" alt=\"evan\" title=\"evan\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/hazmi-e205\"><img src=\"https://avatars1.githubusercontent.com/u/12555465?v=4\" alt=\"hazmi-e205\" title=\"hazmi-e205\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/jtgoral\"><img src=\"https://avatars1.githubusercontent.com/u/19780595?v=4\" alt=\"jtgoral\" title=\"jtgoral\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/ky2s\"><img src=\"https://avatars1.githubusercontent.com/u/19502125?v=4\" alt=\"ky2s\" title=\"ky2s\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/lauweliam\"><img src=\"https://avatars1.githubusercontent.com/u/4064517?v=4\" alt=\"lauweliam\" title=\"lauweliam\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/ozfive\"><img src=\"https://avatars1.githubusercontent.com/u/4494266?v=4\" alt=\"ozfive\" title=\"ozfive\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/paulcockrell\"><img src=\"https://avatars1.githubusercontent.com/u/260514?v=4\" alt=\"paulcockrell\" title=\"paulcockrell\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/paulxu21\"><img src=\"https://avatars1.githubusercontent.com/u/6261758?v=4\" alt=\"paulxu21\" title=\"paulxu21\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/pesquive\"><img src=\"https://avatars1.githubusercontent.com/u/6610140?v=4\" alt=\"pesquive\" title=\"pesquive\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/petros9282\"><img src=\"https://avatars1.githubusercontent.com/u/3861890?v=4\" alt=\"petros9282\" title=\"petros9282\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/phil535\"><img src=\"https://avatars1.githubusercontent.com/u/7596830?v=4\" alt=\"phil535\" title=\"phil535\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/pitt134\"><img src=\"https://avatars1.githubusercontent.com/u/13091629?v=4\" alt=\"pitt134\" title=\"pitt134\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/poscard\"><img src=\"https://avatars1.githubusercontent.com/u/3023318?v=4\" alt=\"poscard\" title=\"poscard\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/qiepeipei\"><img src=\"https://avatars1.githubusercontent.com/u/16110628?v=4\" alt=\"qiepeipei\" title=\"qiepeipei\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/qiuzhanghua\"><img src=\"https://avatars1.githubusercontent.com/u/478393?v=4\" alt=\"qiuzhanghua\" title=\"qiuzhanghua\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/rapita\"><img src=\"https://avatars1.githubusercontent.com/u/22305375?v=4\" alt=\"rapita\" title=\"rapita\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/rbondi\"><img src=\"https://avatars1.githubusercontent.com/u/81764?v=4\" alt=\"rbondi\" title=\"rbondi\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/relaera\"><img src=\"https://avatars1.githubusercontent.com/u/26012106?v=4\" alt=\"relaera\" title=\"relaera\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/remopavithran\"><img src=\"https://avatars1.githubusercontent.com/u/50388068?v=4\" alt=\"remopavithran\" title=\"remopavithran\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/rfunix\"><img src=\"https://avatars1.githubusercontent.com/u/6026357?v=4\" alt=\"rfunix\" title=\"rfunix\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/rhernandez-itemsoft\"><img src=\"https://avatars1.githubusercontent.com/u/4327356?v=4\" alt=\"rhernandez-itemsoft\" title=\"rhernandez-itemsoft\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/rikoriswandha\"><img src=\"https://avatars1.githubusercontent.com/u/2549929?v=4\" alt=\"rikoriswandha\" title=\"rikoriswandha\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/risallaw\"><img src=\"https://avatars1.githubusercontent.com/u/15353146?v=4\" alt=\"risallaw\" title=\"risallaw\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/robivictor\"><img src=\"https://avatars1.githubusercontent.com/u/761041?v=4\" alt=\"robivictor\" title=\"robivictor\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/rubiagatra\"><img src=\"https://avatars1.githubusercontent.com/u/7299491?v=4\" alt=\"rubiagatra\" title=\"rubiagatra\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/rubyangxg\"><img src=\"https://avatars1.githubusercontent.com/u/3069914?v=4\" alt=\"rubyangxg\" title=\"rubyangxg\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/rxrw\"><img src=\"https://avatars1.githubusercontent.com/u/9566402?v=4\" alt=\"rxrw\" title=\"rxrw\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/saleebm\"><img src=\"https://avatars1.githubusercontent.com/u/34875122?v=4\" alt=\"saleebm\" title=\"saleebm\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/sbenimeli\"><img src=\"https://avatars1.githubusercontent.com/u/46652122?v=4\" alt=\"sbenimeli\" title=\"sbenimeli\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/sebyno\"><img src=\"https://avatars1.githubusercontent.com/u/15988169?v=4\" alt=\"sebyno\" title=\"sebyno\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/seun-otosho\"><img src=\"https://avatars1.githubusercontent.com/u/74518370?v=4\" alt=\"seun-otosho\" title=\"seun-otosho\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/shobhitsinghal77\"><img src=\"https://avatars1.githubusercontent.com/u/26848221?v=4\" alt=\"shobhitsinghal77\" title=\"shobhitsinghal77\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/solohiroshi\"><img src=\"https://avatars1.githubusercontent.com/u/96872274?v=4\" alt=\"solohiroshi\" title=\"solohiroshi\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/su1gen\"><img src=\"https://avatars1.githubusercontent.com/u/86298730?v=4\" alt=\"su1gen\" title=\"su1gen\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/sukiejosh\"><img src=\"https://avatars1.githubusercontent.com/u/44656210?v=4\" alt=\"sukiejosh\" title=\"sukiejosh\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/suresh16671\"><img src=\"https://avatars1.githubusercontent.com/u/57644510?v=4\" alt=\"suresh16671\" title=\"suresh16671\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/svirmi\"><img src=\"https://avatars1.githubusercontent.com/u/52601346?v=4\" alt=\"svirmi\" title=\"svirmi\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/terjelafton\"><img src=\"https://avatars1.githubusercontent.com/u/12574755?v=4\" alt=\"terjelafton\" title=\"terjelafton\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/thiennguyen93\"><img src=\"https://avatars1.githubusercontent.com/u/60094052?v=4\" alt=\"thiennguyen93\" title=\"thiennguyen93\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/unixedia\"><img src=\"https://avatars1.githubusercontent.com/u/70646128?v=4\" alt=\"unixedia\" title=\"unixedia\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/vadgun\"><img src=\"https://avatars1.githubusercontent.com/u/22282464?v=4\" alt=\"vadgun\" title=\"vadgun\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/valsorym\"><img src=\"https://avatars1.githubusercontent.com/u/4440262?v=4\" alt=\"valsorym\" title=\"valsorym\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/vguhesan\"><img src=\"https://avatars1.githubusercontent.com/u/193960?v=4\" alt=\"vguhesan\" title=\"vguhesan\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/vpiduri\"><img src=\"https://avatars1.githubusercontent.com/u/19339398?v=4\" alt=\"vpiduri\" title=\"vpiduri\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/vrocadev\"><img src=\"https://avatars1.githubusercontent.com/u/50081969?v=4\" alt=\"vrocadev\" title=\"vrocadev\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/vuhoanglam\"><img src=\"https://avatars1.githubusercontent.com/u/59502855?v=4\" alt=\"vuhoanglam\" title=\"vuhoanglam\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/walter-wang\"><img src=\"https://avatars1.githubusercontent.com/u/7950295?v=4\" alt=\"walter-wang\" title=\"walter-wang\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/martinlindhe\"><img src=\"https://avatars1.githubusercontent.com/u/181531?v=4\" alt=\"martinlindhe\" title=\"martinlindhe\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/mdamschen\"><img src=\"https://avatars1.githubusercontent.com/u/40914728?v=4\" alt=\"mdamschen\" title=\"mdamschen\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/letmestudy\"><img src=\"https://avatars1.githubusercontent.com/u/31943708?v=4\" alt=\"letmestudy\" title=\"letmestudy\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/michaelsmanley\"><img src=\"https://avatars1.githubusercontent.com/u/93241?v=4\" alt=\"michaelsmanley\" title=\"michaelsmanley\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Curtman\"><img src=\"https://avatars1.githubusercontent.com/u/543481?v=4\" alt=\"Curtman\" title=\"Curtman\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/SridarDhandapani\"><img src=\"https://avatars1.githubusercontent.com/u/18103118?v=4\" alt=\"SridarDhandapani\" title=\"SridarDhandapani\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/madrigaltenor\"><img src=\"https://avatars1.githubusercontent.com/u/168838315?v=4\" alt=\"madrigaltenor\" title=\"madrigaltenor\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/opusmagna\"><img src=\"https://avatars1.githubusercontent.com/u/33766678?v=4\" alt=\"opusmagna\" title=\"opusmagna\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/ShahramMebashar\"><img src=\"https://avatars1.githubusercontent.com/u/25268287?v=4\" alt=\"ShahramMebashar\" title=\"ShahramMebashar\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/b4zz4r\"><img src=\"https://avatars1.githubusercontent.com/u/7438782?v=4\" alt=\"b4zz4r\" title=\"b4zz4r\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/bobmcallan\"><img src=\"https://avatars1.githubusercontent.com/u/8773580?v=4\" alt=\"bobmcallan\" title=\"bobmcallan\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/fangli\"><img src=\"https://avatars1.githubusercontent.com/u/3032639?v=4\" alt=\"fangli\" title=\"fangli\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/galois-tnp\"><img src=\"https://avatars1.githubusercontent.com/u/41128011?v=4\" alt=\"galois-tnp\" title=\"galois-tnp\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/mblandr\"><img src=\"https://avatars1.githubusercontent.com/u/42862020?v=4\" alt=\"mblandr\" title=\"mblandr\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/midhubalan\"><img src=\"https://avatars1.githubusercontent.com/u/13059634?v=4\" alt=\"midhubalan\" title=\"midhubalan\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/netbaalzovf\"><img src=\"https://avatars1.githubusercontent.com/u/98529711?v=4\" alt=\"netbaalzovf\" title=\"netbaalzovf\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/oliverjosefzimmer\"><img src=\"https://avatars1.githubusercontent.com/u/24566297?v=4\" alt=\"oliverjosefzimmer\" title=\"oliverjosefzimmer\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/peacememories\"><img src=\"https://avatars1.githubusercontent.com/u/1326334?v=4\" alt=\"peacememories\" title=\"peacememories\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/talebisinan\"><img src=\"https://avatars1.githubusercontent.com/u/42139005?v=4\" alt=\"talebisinan\" title=\"talebisinan\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/valkuere\"><img src=\"https://avatars1.githubusercontent.com/u/7230144?v=4\" alt=\"valkuere\" title=\"valkuere\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/lfaynman\"><img src=\"https://avatars1.githubusercontent.com/u/16815068?v=4\" alt=\"lfaynman\" title=\"lfaynman\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/ArturWierzbicki\"><img src=\"https://avatars1.githubusercontent.com/u/23451458?v=4\" alt=\"ArturWierzbicki\" title=\"ArturWierzbicki\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/aaxx\"><img src=\"https://avatars1.githubusercontent.com/u/476416?v=4\" alt=\"aaxx\" title=\"aaxx\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/crashCoder\"><img src=\"https://avatars1.githubusercontent.com/u/1144298?v=4\" alt=\"crashCoder\" title=\"crashCoder\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/derekslenk\"><img src=\"https://avatars1.githubusercontent.com/u/42957?v=4\" alt=\"derekslenk\" title=\"derekslenk\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/dochoaj\"><img src=\"https://avatars1.githubusercontent.com/u/1789678?v=4\" alt=\"dochoaj\" title=\"dochoaj\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/evillgenius75\"><img src=\"https://avatars1.githubusercontent.com/u/22817701?v=4\" alt=\"evillgenius75\" title=\"evillgenius75\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/gog200921\"><img src=\"https://avatars1.githubusercontent.com/u/101519620?v=4\" alt=\"gog200921\" title=\"gog200921\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/mauricedcastro\"><img src=\"https://avatars1.githubusercontent.com/u/6446532?v=4\" alt=\"mauricedcastro\" title=\"mauricedcastro\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/mwiater\"><img src=\"https://avatars1.githubusercontent.com/u/5323591?v=4\" alt=\"mwiater\" title=\"mwiater\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/sj671\"><img src=\"https://avatars1.githubusercontent.com/u/7363652?v=4\" alt=\"sj671\" title=\"sj671\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/statik\"><img src=\"https://avatars1.githubusercontent.com/u/983?v=4\" alt=\"statik\" title=\"statik\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/supersherm5\"><img src=\"https://avatars1.githubusercontent.com/u/7953550?v=4\" alt=\"supersherm5\" title=\"supersherm5\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/thejones\"><img src=\"https://avatars1.githubusercontent.com/u/682850?v=4\" alt=\"thejones\" title=\"thejones\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/CSRaghunandan\"><img src=\"https://avatars1.githubusercontent.com/u/5226809?v=4\" alt=\"CSRaghunandan\" title=\"CSRaghunandan\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/ndimorle\"><img src=\"https://avatars1.githubusercontent.com/u/76732415?v=4\" alt=\"ndimorle\" title=\"ndimorle\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/rosales-stephanie\"><img src=\"https://avatars1.githubusercontent.com/u/43592017?v=4\" alt=\"rosales-stephanie\" title=\"rosales-stephanie\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/shyyawn\"><img src=\"https://avatars1.githubusercontent.com/u/6064438?v=4\" alt=\"shyyawn\" title=\"shyyawn\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/vcruzato\"><img src=\"https://avatars1.githubusercontent.com/u/3864151?v=4\" alt=\"vcruzato\" title=\"vcruzato\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/wangbl11\"><img src=\"https://avatars1.githubusercontent.com/u/14358532?v=4\" alt=\"wangbl11\" title=\"wangbl11\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/wofka72\"><img src=\"https://avatars1.githubusercontent.com/u/10855340?v=4\" alt=\"wofka72\" title=\"wofka72\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/geoshan\"><img src=\"https://avatars1.githubusercontent.com/u/10161131?v=4\" alt=\"geoshan\" title=\"geoshan\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/juanxme\"><img src=\"https://avatars1.githubusercontent.com/u/661043?v=4\" alt=\"juanxme\" title=\"juanxme\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/nguyentamvinhlong\"><img src=\"https://avatars1.githubusercontent.com/u/1875916?v=4\" alt=\"nguyentamvinhlong\" title=\"nguyentamvinhlong\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/yoru74\"><img src=\"https://avatars1.githubusercontent.com/u/7745866?v=4\" alt=\"yoru74\" title=\"yoru74\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/xsokev\"><img src=\"https://avatars1.githubusercontent.com/u/28113?v=4\" alt=\"xsokev\" title=\"xsokev\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/oleang\"><img src=\"https://avatars1.githubusercontent.com/u/142615?v=4\" alt=\"oleang\" title=\"oleang\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/michalsz\"><img src=\"https://avatars1.githubusercontent.com/u/187477?v=4\" alt=\"michalsz\" title=\"michalsz\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/pomland-94\"><img src=\"https://avatars1.githubusercontent.com/u/96850116?v=4\" alt=\"pomland-94\" title=\"pomland-94\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/tejzpr\"><img src=\"https://avatars1.githubusercontent.com/u/2813811?v=4\" alt=\"tejzpr\" title=\"tejzpr\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/theantichris\"><img src=\"https://avatars1.githubusercontent.com/u/1486502?v=4\" alt=\"theantichris\" title=\"theantichris\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/tuxaanand\"><img src=\"https://avatars1.githubusercontent.com/u/9750371?v=4\" alt=\"tuxaanand\" title=\"tuxaanand\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/raphael-brand\"><img src=\"https://avatars1.githubusercontent.com/u/4279168?v=4\" alt=\"raphael-brand\" title=\"raphael-brand\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/willypuzzle\"><img src=\"https://avatars1.githubusercontent.com/u/18305386?v=4\" alt=\"willypuzzle\" title=\"willypuzzle\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/dmcbane\"><img src=\"https://avatars1.githubusercontent.com/u/5453862?v=4\" alt=\"dmcbane\" title=\"dmcbane\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/malcolm-white-dti\"><img src=\"https://avatars1.githubusercontent.com/u/109724322?v=4\" alt=\"malcolm-white-dti\" title=\"malcolm-white-dti\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/HieuLsw\"><img src=\"https://avatars1.githubusercontent.com/u/1675478?v=4\" alt=\"HieuLsw\" title=\"HieuLsw\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/carlosmoran092\"><img src=\"https://avatars1.githubusercontent.com/u/10361754?v=4\" alt=\"carlosmoran092\" title=\"carlosmoran092\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/yangxianglong\"><img src=\"https://avatars1.githubusercontent.com/u/55280276?v=4\" alt=\"yangxianglong\" title=\"yangxianglong\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n</p>\n\n## 📖 Learning Iris\n\n### Installation\n\nThe only requirement is the [Go Programming Language](https://go.dev/dl/).\n\n#### Create a new project\n\n```sh\n$ mkdir myapp\n$ cd myapp\n$ go mod init myapp\n$ go get github.com/kataras/iris/v12@latest # or @v12.2.11\n```\n\n<details><summary>Install on existing project</summary>\n\n```sh\n$ cd myapp\n$ go get github.com/kataras/iris/v12@latest\n```\n\n**Run**\n\n```sh\n$ go mod tidy -compat=1.23 # -compat=\"1.23\" for windows.\n$ go run .\n```\n\n</details>\n\n![](https://www.iris-go.com/images/gifs/install-create-iris.gif)\n\nIris contains extensive and thorough **[documentation](https://www.iris-go.com/docs)** making it easy to get started with the framework.\n\n<!-- Iris contains extensive and thorough **[wiki](https://github.com/kataras/iris/wiki)** making it easy to get started with the framework. -->\n\n<!-- ![](https://media.giphy.com/media/Ur8iqy9FQfmPuyQpgy/giphy.gif) -->\n\nFor a more detailed technical documentation you can head over to our [godocs](https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.11). And for executable code you can always visit the [./_examples](_examples) repository's subdirectory.\n\n### Do you like to read while traveling?\n\n<a href=\"https://iris-go.com/#book\"> <img alt=\"Book cover\" src=\"https://iris-go.com/static/images/iris-book-cover-sm.jpg?v=12\" /> </a>\n\n[![follow author on twitter](https://img.shields.io/twitter/follow/makismaropoulos?color=3D8AA3&logoColor=3D8AA3&style=for-the-badge&logo=twitter)](https://twitter.com/intent/follow?screen_name=makismaropoulos)\n\n[![follow Iris web framework on twitter](https://img.shields.io/twitter/follow/iris_framework?color=ee7506&logoColor=ee7506&style=for-the-badge&logo=twitter)](https://twitter.com/intent/follow?screen_name=iris_framework)\n\n[![follow Iris web framework on facebook](https://img.shields.io/badge/Follow%20%40Iris.framework-569-2D88FF.svg?style=for-the-badge&logo=facebook)](https://www.facebook.com/iris.framework)\n\nYou can [request](https://www.iris-go.com/#ebookDonateForm) a PDF and online access of the **Iris E-Book** (New Edition, **future v12.2.0+**) today and be participated in the development of Iris.\n\n## 🙌 Contributing\n\nWe'd love to see your contribution to the Iris Web Framework! For more information about contributing to the Iris project please check the [CONTRIBUTING.md](CONTRIBUTING.md) file.\n\n[List of all Contributors](https://github.com/kataras/iris/graphs/contributors)\n\n## 🛡 Security Vulnerabilities\n\nIf you discover a security vulnerability within Iris, please send an e-mail to [iris-go@outlook.com](mailto:iris-go@outlook.com). All security vulnerabilities will be promptly addressed.\n\n## 📝 License\n\nThis project is licensed under the [BSD 3-clause license](LICENSE), just like the Go project itself.\n\nThe project name \"Iris\" was inspired by the Greek mythology.\n<!-- ## Stargazers over time\n\n[![Stargazers over time](https://starchart.cc/kataras/iris.svg)](https://starchart.cc/kataras/iris) -->\n"
  },
  {
    "path": "README_AR.md",
    "content": "أعزائي أعضاء مجتمع Iris,\n\nقد تكون قد لاحظت تراجعًا مؤخرًا في مستوى النشاط على مستودع Iris، ولكن هذا الهدوء له غاية مقصودة. خلال **الأشهر الثمانية إلى التسعة الماضية،** كنت أعمل بجد على الإعداد للإصدار الرئيسي الجديد من Iris، والذي يعتمد بشكل كامل على **الأنواع العامة (Generics)**، إلى جانب تقديم مجموعة من **الميزات الجديدة،** و **التحسينات**، والابتكارات. هذا التطوير يستند إلى خبرتي الممتدة على مدى **ثماني سنوات** في لغة Go، لضمان أن يستمر تطور Iris بما يتماشى مع احتياجاتكم المتنامية.\n\nمع أطيب التحيات,<br/>\nGerasimos (Makis) Maropoulos\n\n<div style=\"text-align: right;\">\n<h1><a href=\"https://iris-go.com\"><img src=\"https://iris-go.com/iris-terminal-55.png\" width=\"50px\" height=\"50px\" style=\"margin-bottom: -5px\" ></a> Iris Web Framework إطار العمل إريس <a href=\"README_GR.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-greece.svg\" /> <a href=\"README_JA.md\"><img width=\"20px\" height=\"20px\" src=\"https://iris-go.com/static/images/flag-japan.svg\" /></a> </a> <a href=\"README_FR.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-france.svg\" /></a> <a href=\"README_ZH_HANT.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-taiwan.svg\" /></a> <a href=\"README_ZH_HANS.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-china.svg\" /></a> <a href=\"README_ES.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-spain.png\" /></a> <a href=\"README_FA.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-iran.svg\" /></a> <a href=\"README_RU.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-russia.svg\" /></a> <a href=\"README_KO.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-south-korea.svg?v=12\" /></a> <a href=\"README_PT_BR.md\"><img width=\"20px\" height=\"20px\" src=\"https://iris-go.com/static/images/flag-brazil.svg\" /></a> <a href=\"README_VN.md\"><img width=\"20px\" height=\"20px\" src=\"https://iris-go.com/static/images/flag-vietnam.svg\" /></a></h1>\n\n[![build status](https://img.shields.io/github/actions/workflow/status/kataras/iris/ci.yml?branch=main&style=for-the-badge)](https://github.com/kataras/iris/actions/workflows/ci.yml) [![view examples](https://img.shields.io/badge/examples%20-285-a83adf.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/main/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=cc2b5e&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community) <!--[![FOSSA Status](https://img.shields.io/badge/LICENSE%20SCAN-PASSING❤️-CD2956?style=for-the-badge&logo=fossa)](https://app.fossa.io/projects/git%2Bgithub.com%2Fkataras%2Firis?ref=badge_shield)--> [![donate](https://img.shields.io/badge/support-Iris-blue.svg?style=for-the-badge&logo=paypal)](https://iris-go.com/donate) <!--[![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=for-the-badge)](https://goreportcard.com/report/github.com/kataras/iris)--><!--[![godocs](https://img.shields.io/badge/go-%20docs-488AC7.svg?style=for-the-badge)](https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.11)--> <!-- [![release](https://img.shields.io/badge/release%20-v12.0-0077b3.svg?style=for-the-badge)](https://github.com/kataras/iris/releases) -->\n</div>\n\n<div  style=\"text-align: right;\">\nIris هو إطار عمل ويب سريع، بسيط، ومع ذلك غني بالميزات وفعّال للغاية بلغة Go.\n\nيوفّر أساسًا مرنًا، سهل الاستخدام، وذا تعبيرية رائعة لبناء موقعك الإلكتروني أو واجهة برمجة التطبيقات (API) القادمة.\n\n**تعرف** على [آراء اللآخرين حول Iris](https://www.iris-go.com/#review) و **[امنح المشروع نجمة](https://github.com/kataras/iris/stargazers)** لدعم إمكاناته كمشروع مفتوح المصدر.\n\n</div>\n\n[![](https://iris-go.com/static/images/reviews.gif)](https://iris-go.com/testimonials/)\n\n[![Benchmarks: Jul 18, 2020 at 10:46am (UTC)](https://iris-go.com/static/images/benchmarks.svg)](https://github.com/kataras/server-benchmarks)\n\n```go\npackage main\n\nimport \"github.com/kataras/iris/v12\"\n\nfunc main() {\n  app := iris.New()\n  app.Use(iris.Compression)\n\n  app.Get(\"/\", func(ctx iris.Context) {\n    ctx.HTML(\"Hello <strong>%s</strong>!\", \"World\")\n  })\n\n  app.Listen(\":8080\")\n}\n```\n\nكما قال أحد [مطوري Go](https://twitter.com/dkuye/status/1532087942696554497) ذات مرة: **Iris يغطي جميع الجوانب، وظل صامدًا وقويًا على مدار السنوات**.\n\nبعض الميزات التي يقدّمها Iris:\n\n* <p style=\"text-align: right\">دعم كامل لبروتوكول HTTP/2 (يشمل Push وبيانات مضمنة)</p>\n* <p style=\"text-align: right\">البرمجيات الوسيطة (Middleware): Accesslog، Basicauth، CORS، gRPC، Anti-Bot hCaptcha، JWT، MethodOverride، ModRevision، Monitor، PPROF، Ratelimit، Anti-Bot reCaptcha، Recovery، RequestID، Rewrite</p>\n* <p style=\"text-align: right\">إصدار واجهات برمجة التطبيقات (API Versioning)</p>\n* <p style=\"text-align: right\">نموذج-عرض-تحكم (Model-View-Controller)</p>\n* <p style=\"text-align: right\">دعم كامل لبروتوكول WebSockets</p>\n* <p style=\"text-align: right\">دعم gRPC</p>\n* <p style=\"text-align: right\">دعم تلقائي لHTTPS</p>\n* <p style=\"text-align: right\">دعم مدمج لـ ngrok لعرض تطبيقك على الإنترنت بأسرع طريقة</p>\n* <p style=\"text-align: right\">موجّه (Router) فريد يدعم المسارات الديناميكية مع أنواع قياسية مثل: :uuid، :string، :int، وإمكانية إنشاء أنواعك الخاصة</p>\n* <p style=\"text-align: right\">ضغط البيانات</p>\n* <p style=\"text-align: right\">محركات العرض: HTML، Django، Handlebars، Pug/Jade وغيرها</p>\n* <p style=\"text-align: right\">إنشاء خادم ملفات خاص بك واستضافة خادم WebDAV</p>\n* <p style=\"text-align: right\">التخزين المؤقت</p>\n* <p style=\"text-align: right\">التوطين (i18n, sitemap)</p>\n* <p style=\"text-align: right\">الجلسات</p>\n* <p style=\"text-align: right\">استجابات غنية تشمل: HTML، نص، Markdown، XML، YAML، ثنائي، JSON، JSONP، Protocol Buffers، MessagePack، Content Negotiation، البث المتدفق، وأحداث مرسلة من الخادم والمزيد</p>\n* <p style=\"text-align: right\">ضغط الاستجابات (gzip, deflate, brotli, snappy, s2)</p>\n* <p style=\"text-align: right\">طلبات غنية (Rich Requests) تدعم: ربط استعلامات URL، الرؤوس، النماذج، النصوص، XML، YAML، ثنائي، JSON، التحقق، Protocol Buffers، MessagePack والمزيد</p>\n* <p style=\"text-align: right\">الحقن التلقائي: لمعماريات MVC، المعالجات، وواجهات برمجة التطبيقات</p>\n* <p style=\"text-align: right\">مجموعة اختبارات</p>\n* <p style=\"text-align: right\">والأهم... تحصل على دعم سريع وإجابات فورية منذ اليوم الأول وحتى الآن، على مدار ست سنوات متواصلة!</p>\n\n## <p style=\"text-align: right\">👑 <a href=\"https://iris-go.com/donate\">الداعمين</a></p>\n\nبدعمكم، سنتمكن معًا من الارتقاء بتطوير الويب مفتوح المصدر وجعله أفضل للجميع!\n\n<p>\n  <a href=\"https://github.com/getsentry\"><img src=\"https://avatars1.githubusercontent.com/u/1396951?v=4\" alt=\"getsentry\" title=\"getsentry\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/github\"><img src=\"https://avatars1.githubusercontent.com/u/9919?v=4\" alt=\"github\" title=\"github\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/lensesio\"><img src=\"https://avatars1.githubusercontent.com/u/11728472?v=4\" alt=\"lensesio\" title=\"lensesio\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/thepunterbot\"><img src=\"https://avatars1.githubusercontent.com/u/111136029?v=4\" alt=\"thepunterbot\" title=\"thepunterbot\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/h4rdc0m\"><img src=\"https://avatars1.githubusercontent.com/u/682256?v=4\" alt=\"h4rdc0m\" title=\"h4rdc0m\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/draFWM\"><img src=\"https://avatars1.githubusercontent.com/u/5765340?v=4\" alt=\"draFWM\" title=\"draFWM\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/gf3\"><img src=\"https://avatars1.githubusercontent.com/u/18397?v=4\" alt=\"gf3\" title=\"gf3\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/trading-peter\"><img src=\"https://avatars1.githubusercontent.com/u/11567985?v=4\" alt=\"trading-peter\" title=\"trading-peter\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/AlbinoGeek\"><img src=\"https://avatars1.githubusercontent.com/u/1910461?v=4\" alt=\"AlbinoGeek\" title=\"AlbinoGeek\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/basilarchia\"><img src=\"https://avatars1.githubusercontent.com/u/926033?v=4\" alt=\"basilarchia\" title=\"basilarchia\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/sumjoe\"><img src=\"https://avatars1.githubusercontent.com/u/32655210?v=4\" alt=\"sumjoe\" title=\"sumjoe\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/simpleittools\"><img src=\"https://avatars1.githubusercontent.com/u/42871067?v=4\" alt=\"simpleittools\" title=\"simpleittools\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/xiaozhuai\"><img src=\"https://avatars1.githubusercontent.com/u/4773701?v=4\" alt=\"xiaozhuai\" title=\"xiaozhuai\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Remydeme\"><img src=\"https://avatars1.githubusercontent.com/u/22757039?v=4\" alt=\"Remydeme\" title=\"Remydeme\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/celsosz\"><img src=\"https://avatars1.githubusercontent.com/u/3466493?v=4\" alt=\"celsosz\" title=\"celsosz\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/linxcoder\"><img src=\"https://avatars1.githubusercontent.com/u/1050802?v=4\" alt=\"linxcoder\" title=\"linxcoder\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/jnelle\"><img src=\"https://avatars1.githubusercontent.com/u/36324542?v=4\" alt=\"jnelle\" title=\"jnelle\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/TechMaster\"><img src=\"https://avatars1.githubusercontent.com/u/1491686?v=4\" alt=\"TechMaster\" title=\"TechMaster\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/janwebdev\"><img src=\"https://avatars1.githubusercontent.com/u/6725905?v=4\" alt=\"janwebdev\" title=\"janwebdev\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/altafino\"><img src=\"https://avatars1.githubusercontent.com/u/24539467?v=4\" alt=\"altafino\" title=\"altafino\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/jakoubek\"><img src=\"https://avatars1.githubusercontent.com/u/179566?v=4\" alt=\"jakoubek\" title=\"jakoubek\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/alekperos\"><img src=\"https://avatars1.githubusercontent.com/u/683938?v=4\" alt=\"alekperos\" title=\"alekperos\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/day0ng\"><img src=\"https://avatars1.githubusercontent.com/u/15760418?v=4\" alt=\"day0ng\" title=\"day0ng\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/hengestone\"><img src=\"https://avatars1.githubusercontent.com/u/362587?v=4\" alt=\"hengestone\" title=\"hengestone\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/thomasfr\"><img src=\"https://avatars1.githubusercontent.com/u/287432?v=4\" alt=\"thomasfr\" title=\"thomasfr\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/code-chimp\"><img src=\"https://avatars1.githubusercontent.com/u/50490?v=4\" alt=\"code-chimp\" title=\"code-chimp\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/CetinBasoz\"><img src=\"https://avatars1.githubusercontent.com/u/3152637?v=4\" alt=\"CetinBasoz\" title=\"CetinBasoz\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/International\"><img src=\"https://avatars1.githubusercontent.com/u/1022918?v=4\" alt=\"International\" title=\"International\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Juanses\"><img src=\"https://avatars1.githubusercontent.com/u/6137970?v=4\" alt=\"Juanses\" title=\"Juanses\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/SometimesMage\"><img src=\"https://avatars1.githubusercontent.com/u/1435257?v=4\" alt=\"SometimesMage\" title=\"SometimesMage\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/ansrivas\"><img src=\"https://avatars1.githubusercontent.com/u/1695056?v=4\" alt=\"ansrivas\" title=\"ansrivas\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/boreevyuri\"><img src=\"https://avatars1.githubusercontent.com/u/10973128?v=4\" alt=\"boreevyuri\" title=\"boreevyuri\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/brentwilson\"><img src=\"https://avatars1.githubusercontent.com/u/11813757?v=4\" alt=\"brentwilson\" title=\"brentwilson\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/camilbinas\"><img src=\"https://avatars1.githubusercontent.com/u/42443219?v=4\" alt=\"camilbinas\" title=\"camilbinas\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/ekobayong\"><img src=\"https://avatars1.githubusercontent.com/u/878170?v=4\" alt=\"ekobayong\" title=\"ekobayong\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/lexrus\"><img src=\"https://avatars1.githubusercontent.com/u/219689?v=4\" alt=\"lexrus\" title=\"lexrus\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/li3p\"><img src=\"https://avatars1.githubusercontent.com/u/55519?v=4\" alt=\"li3p\" title=\"li3p\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/madhu72\"><img src=\"https://avatars1.githubusercontent.com/u/10324127?v=4\" alt=\"madhu72\" title=\"madhu72\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/mosorize\"><img src=\"https://avatars1.githubusercontent.com/u/107021151?v=4\" alt=\"mosorize\" title=\"mosorize\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/se77en\"><img src=\"https://avatars1.githubusercontent.com/u/1468284?v=4\" alt=\"se77en\" title=\"se77en\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/tstangenberg\"><img src=\"https://avatars1.githubusercontent.com/u/736160?v=4\" alt=\"tstangenberg\" title=\"tstangenberg\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/vincent-li\"><img src=\"https://avatars1.githubusercontent.com/u/765470?v=4\" alt=\"vincent-li\" title=\"vincent-li\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/DavidShaw\"><img src=\"https://avatars1.githubusercontent.com/u/356970?v=4\" alt=\"DavidShaw\" title=\"DavidShaw\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/sascha11110\"><img src=\"https://avatars1.githubusercontent.com/u/15168372?v=4\" alt=\"sascha11110\" title=\"sascha11110\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/clichi2002\"><img src=\"https://avatars1.githubusercontent.com/u/5856121?v=4\" alt=\"clichi2002\" title=\"clichi2002\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/derReineke\"><img src=\"https://avatars1.githubusercontent.com/u/35681013?v=4\" alt=\"derReineke\" title=\"derReineke\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Sirisap22\"><img src=\"https://avatars1.githubusercontent.com/u/58851659?v=4\" alt=\"Sirisap22\" title=\"Sirisap22\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/primadi\"><img src=\"https://avatars1.githubusercontent.com/u/7625413?v=4\" alt=\"primadi\" title=\"primadi\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/agoncecelia\"><img src=\"https://avatars1.githubusercontent.com/u/10442924?v=4\" alt=\"agoncecelia\" title=\"agoncecelia\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/chrisliang12\"><img src=\"https://avatars1.githubusercontent.com/u/97201988?v=4\" alt=\"chrisliang12\" title=\"chrisliang12\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/zyu\"><img src=\"https://avatars1.githubusercontent.com/u/807397?v=4\" alt=\"zyu\" title=\"zyu\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/hobysmith\"><img src=\"https://avatars1.githubusercontent.com/u/6063391?v=4\" alt=\"hobysmith\" title=\"hobysmith\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/pluja\"><img src=\"https://avatars1.githubusercontent.com/u/64632615?v=4\" alt=\"pluja\" title=\"pluja\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/antonio-pedrazzini\"><img src=\"https://avatars1.githubusercontent.com/u/83503326?v=4\" alt=\"antonio-pedrazzini\" title=\"antonio-pedrazzini\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/clacroix\"><img src=\"https://avatars1.githubusercontent.com/u/611064?v=4\" alt=\"clacroix\" title=\"clacroix\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/njeff3\"><img src=\"https://avatars1.githubusercontent.com/u/9838120?v=4\" alt=\"njeff3\" title=\"njeff3\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/ixalender\"><img src=\"https://avatars1.githubusercontent.com/u/877376?v=4\" alt=\"ixalender\" title=\"ixalender\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/mubariz-ahmed\"><img src=\"https://avatars1.githubusercontent.com/u/18215455?v=4\" alt=\"mubariz-ahmed\" title=\"mubariz-ahmed\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Cesar\"><img src=\"https://avatars1.githubusercontent.com/u/1581870?v=4\" alt=\"Cesar\" title=\"Cesar\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/th31nitiate\"><img src=\"https://avatars1.githubusercontent.com/u/14749635?v=4\" alt=\"th31nitiate\" title=\"th31nitiate\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/stgrosshh\"><img src=\"https://avatars1.githubusercontent.com/u/8356082?v=4\" alt=\"stgrosshh\" title=\"stgrosshh\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Didainius\"><img src=\"https://avatars1.githubusercontent.com/u/15804230?v=4\" alt=\"Didainius\" title=\"Didainius\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/DmarshalTU\"><img src=\"https://avatars1.githubusercontent.com/u/59089266?v=4\" alt=\"DmarshalTU\" title=\"DmarshalTU\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/IwateKyle\"><img src=\"https://avatars1.githubusercontent.com/u/658799?v=4\" alt=\"IwateKyle\" title=\"IwateKyle\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Little-YangYang\"><img src=\"https://avatars1.githubusercontent.com/u/10755202?v=4\" alt=\"Little-YangYang\" title=\"Little-YangYang\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Major2828\"><img src=\"https://avatars1.githubusercontent.com/u/19783402?v=4\" alt=\"Major2828\" title=\"Major2828\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/MatejLach\"><img src=\"https://avatars1.githubusercontent.com/u/531930?v=4\" alt=\"MatejLach\" title=\"MatejLach\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/amritpal042\"><img src=\"https://avatars1.githubusercontent.com/u/60704162?v=4\" alt=\"amritpal042\" title=\"amritpal042\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/andrefiorot\"><img src=\"https://avatars1.githubusercontent.com/u/13743098?v=4\" alt=\"andrefiorot\" title=\"andrefiorot\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/boomhut\"><img src=\"https://avatars1.githubusercontent.com/u/56619040?v=4\" alt=\"boomhut\" title=\"boomhut\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/cshum\"><img src=\"https://avatars1.githubusercontent.com/u/293790?v=4\" alt=\"cshum\" title=\"cshum\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/dtrifonov\"><img src=\"https://avatars1.githubusercontent.com/u/1520118?v=4\" alt=\"dtrifonov\" title=\"dtrifonov\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/gadokrisztian\"><img src=\"https://avatars1.githubusercontent.com/u/85160134?v=4\" alt=\"gadokrisztian\" title=\"gadokrisztian\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/geordee\"><img src=\"https://avatars1.githubusercontent.com/u/83303?v=4\" alt=\"geordee\" title=\"geordee\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/guanting112\"><img src=\"https://avatars1.githubusercontent.com/u/11306350?v=4\" alt=\"guanting112\" title=\"guanting112\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/iantuan\"><img src=\"https://avatars1.githubusercontent.com/u/4869968?v=4\" alt=\"iantuan\" title=\"iantuan\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/ichenhe\"><img src=\"https://avatars1.githubusercontent.com/u/10266066?v=4\" alt=\"ichenhe\" title=\"ichenhe\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/rodrigoghm\"><img src=\"https://avatars1.githubusercontent.com/u/66917643?v=4\" alt=\"rodrigoghm\" title=\"rodrigoghm\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/icibiri\"><img src=\"https://avatars1.githubusercontent.com/u/32684966?v=4\" alt=\"icibiri\" title=\"icibiri\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/jewe11er\"><img src=\"https://avatars1.githubusercontent.com/u/47153959?v=4\" alt=\"jewe11er\" title=\"jewe11er\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/jfloresremar\"><img src=\"https://avatars1.githubusercontent.com/u/10441071?v=4\" alt=\"jfloresremar\" title=\"jfloresremar\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/jingtianfeng\"><img src=\"https://avatars1.githubusercontent.com/u/19503202?v=4\" alt=\"jingtianfeng\" title=\"jingtianfeng\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/kilarusravankumar\"><img src=\"https://avatars1.githubusercontent.com/u/13055113?v=4\" alt=\"kilarusravankumar\" title=\"kilarusravankumar\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/leandrobraga\"><img src=\"https://avatars1.githubusercontent.com/u/506699?v=4\" alt=\"leandrobraga\" title=\"leandrobraga\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/lfbos\"><img src=\"https://avatars1.githubusercontent.com/u/5703286?v=4\" alt=\"lfbos\" title=\"lfbos\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/lpintes\"><img src=\"https://avatars1.githubusercontent.com/u/2546783?v=4\" alt=\"lpintes\" title=\"lpintes\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/macropas\"><img src=\"https://avatars1.githubusercontent.com/u/7488502?v=4\" alt=\"macropas\" title=\"macropas\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/marcmmx\"><img src=\"https://avatars1.githubusercontent.com/u/7670546?v=4\" alt=\"marcmmx\" title=\"marcmmx\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/mark2b\"><img src=\"https://avatars1.githubusercontent.com/u/539063?v=4\" alt=\"mark2b\" title=\"mark2b\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/miguel-devs\"><img src=\"https://avatars1.githubusercontent.com/u/89543510?v=4\" alt=\"miguel-devs\" title=\"miguel-devs\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/mihado\"><img src=\"https://avatars1.githubusercontent.com/u/940981?v=4\" alt=\"mihado\" title=\"mihado\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/mmckeen75\"><img src=\"https://avatars1.githubusercontent.com/u/49529489?v=4\" alt=\"mmckeen75\" title=\"mmckeen75\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/narven\"><img src=\"https://avatars1.githubusercontent.com/u/123594?v=4\" alt=\"narven\" title=\"narven\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/odas0r\"><img src=\"https://avatars1.githubusercontent.com/u/32167770?v=4\" alt=\"odas0r\" title=\"odas0r\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/olaf-lexemo\"><img src=\"https://avatars1.githubusercontent.com/u/51406599?v=4\" alt=\"olaf-lexemo\" title=\"olaf-lexemo\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/pitexplore\"><img src=\"https://avatars1.githubusercontent.com/u/11956562?v=4\" alt=\"pitexplore\" title=\"pitexplore\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/pr123\"><img src=\"https://avatars1.githubusercontent.com/u/23333176?v=4\" alt=\"pr123\" title=\"pr123\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/rsousacode\"><img src=\"https://avatars1.githubusercontent.com/u/34067397?v=4\" alt=\"rsousacode\" title=\"rsousacode\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/sankethpb\"><img src=\"https://avatars1.githubusercontent.com/u/16034868?v=4\" alt=\"sankethpb\" title=\"sankethpb\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/wixregiga\"><img src=\"https://avatars1.githubusercontent.com/u/30182903?v=4\" alt=\"wixregiga\" title=\"wixregiga\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/GeorgeFourikis\"><img src=\"https://avatars1.githubusercontent.com/u/17906313?v=4\" alt=\"GeorgeFourikis\" title=\"GeorgeFourikis\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/saz59\"><img src=\"https://avatars1.githubusercontent.com/u/9706793?v=4\" alt=\"saz59\" title=\"saz59\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/shadowfiga\"><img src=\"https://avatars1.githubusercontent.com/u/42721390?v=4\" alt=\"shadowfiga\" title=\"shadowfiga\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/siriushaha\"><img src=\"https://avatars1.githubusercontent.com/u/7924311?v=4\" alt=\"siriushaha\" title=\"siriushaha\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/skurtz97\"><img src=\"https://avatars1.githubusercontent.com/u/71720714?v=4\" alt=\"skurtz97\" title=\"skurtz97\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/srinivasganti\"><img src=\"https://avatars1.githubusercontent.com/u/2057165?v=4\" alt=\"srinivasganti\" title=\"srinivasganti\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/syrm\"><img src=\"https://avatars1.githubusercontent.com/u/155406?v=4\" alt=\"syrm\" title=\"syrm\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/tuhao1020\"><img src=\"https://avatars1.githubusercontent.com/u/26807520?v=4\" alt=\"tuhao1020\" title=\"tuhao1020\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/BlackHole1\"><img src=\"https://avatars1.githubusercontent.com/u/8198408?v=4\" alt=\"BlackHole1\" title=\"BlackHole1\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/L-M-Sherlock\"><img src=\"https://avatars1.githubusercontent.com/u/32575846?v=4\" alt=\"L-M-Sherlock\" title=\"L-M-Sherlock\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/claudemuller\"><img src=\"https://avatars1.githubusercontent.com/u/8104894?v=4\" alt=\"claudemuller\" title=\"claudemuller\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/keymanye\"><img src=\"https://avatars1.githubusercontent.com/u/9495010?v=4\" alt=\"keymanye\" title=\"keymanye\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/wahyuief\"><img src=\"https://avatars1.githubusercontent.com/u/20138856?v=4\" alt=\"wahyuief\" title=\"wahyuief\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/xuyan2018\"><img src=\"https://avatars1.githubusercontent.com/u/38712502?v=4\" alt=\"xuyan2018\" title=\"xuyan2018\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/xvalen\"><img src=\"https://avatars1.githubusercontent.com/u/2307513?v=4\" alt=\"xvalen\" title=\"xvalen\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/xytis\"><img src=\"https://avatars1.githubusercontent.com/u/78025?v=4\" alt=\"xytis\" title=\"xytis\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/ElNovi\"><img src=\"https://avatars1.githubusercontent.com/u/14199592?v=4\" alt=\"ElNovi\" title=\"ElNovi\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/IpastorSan\"><img src=\"https://avatars1.githubusercontent.com/u/54788305?v=4\" alt=\"IpastorSan\" title=\"IpastorSan\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/KKP4\"><img src=\"https://avatars1.githubusercontent.com/u/24271790?v=4\" alt=\"KKP4\" title=\"KKP4\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Lernakow\"><img src=\"https://avatars1.githubusercontent.com/u/46821665?v=4\" alt=\"Lernakow\" title=\"Lernakow\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/ernestocolombo\"><img src=\"https://avatars1.githubusercontent.com/u/485538?v=4\" alt=\"ernestocolombo\" title=\"ernestocolombo\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/francisstephan\"><img src=\"https://avatars1.githubusercontent.com/u/15109897?v=4\" alt=\"francisstephan\" title=\"francisstephan\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/pixelheresy\"><img src=\"https://avatars1.githubusercontent.com/u/2491944?v=4\" alt=\"pixelheresy\" title=\"pixelheresy\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/rcapraro\"><img src=\"https://avatars1.githubusercontent.com/u/245490?v=4\" alt=\"rcapraro\" title=\"rcapraro\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/soiestad\"><img src=\"https://avatars1.githubusercontent.com/u/9642036?v=4\" alt=\"soiestad\" title=\"soiestad\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/spkarason\"><img src=\"https://avatars1.githubusercontent.com/u/100413497?v=4\" alt=\"spkarason\" title=\"spkarason\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/thanasolykos\"><img src=\"https://avatars1.githubusercontent.com/u/35801329?v=4\" alt=\"thanasolykos\" title=\"thanasolykos\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/ukitzmann\"><img src=\"https://avatars1.githubusercontent.com/u/153834?v=4\" alt=\"ukitzmann\" title=\"ukitzmann\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/DanielKirkwood\"><img src=\"https://avatars1.githubusercontent.com/u/22101308?v=4\" alt=\"DanielKirkwood\" title=\"DanielKirkwood\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/colinf\"><img src=\"https://avatars1.githubusercontent.com/u/530815?v=4\" alt=\"colinf\" title=\"colinf\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/simonproctor\"><img src=\"https://avatars1.githubusercontent.com/u/203916?v=4\" alt=\"simonproctor\" title=\"simonproctor\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/FernandoLangOFC\"><img src=\"https://avatars1.githubusercontent.com/u/84889316?v=4\" alt=\"FernandoLangOFC\" title=\"FernandoLangOFC\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Firdavs9512\"><img src=\"https://avatars1.githubusercontent.com/u/102187486?v=4\" alt=\"Firdavs9512\" title=\"Firdavs9512\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Flammable-Duck\"><img src=\"https://avatars1.githubusercontent.com/u/59183206?v=4\" alt=\"Flammable-Duck\" title=\"Flammable-Duck\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Gepetdo\"><img src=\"https://avatars1.githubusercontent.com/u/5978138?v=4\" alt=\"Gepetdo\" title=\"Gepetdo\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Hongjian0619\"><img src=\"https://avatars1.githubusercontent.com/u/25712119?v=4\" alt=\"Hongjian0619\" title=\"Hongjian0619\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/JoeD\"><img src=\"https://avatars1.githubusercontent.com/u/247821?v=4\" alt=\"JoeD\" title=\"JoeD\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Jude-X\"><img src=\"https://avatars1.githubusercontent.com/u/66228813?v=4\" alt=\"Jude-X\" title=\"Jude-X\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Kartoffelbot\"><img src=\"https://avatars1.githubusercontent.com/u/130631591?v=4\" alt=\"Kartoffelbot\" title=\"Kartoffelbot\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/KevinZhouRafael\"><img src=\"https://avatars1.githubusercontent.com/u/16298046?v=4\" alt=\"KevinZhouRafael\" title=\"KevinZhouRafael\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/KrishManohar\"><img src=\"https://avatars1.githubusercontent.com/u/1992857?v=4\" alt=\"KrishManohar\" title=\"KrishManohar\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Laotanling\"><img src=\"https://avatars1.githubusercontent.com/u/28570289?v=4\" alt=\"Laotanling\" title=\"Laotanling\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Longf99999\"><img src=\"https://avatars1.githubusercontent.com/u/21210800?v=4\" alt=\"Longf99999\" title=\"Longf99999\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Lyansun\"><img src=\"https://avatars1.githubusercontent.com/u/17959642?v=4\" alt=\"Lyansun\" title=\"Lyansun\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/MihaiPopescu1985\"><img src=\"https://avatars1.githubusercontent.com/u/34679869?v=4\" alt=\"MihaiPopescu1985\" title=\"MihaiPopescu1985\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/TBNilles\"><img src=\"https://avatars1.githubusercontent.com/u/88231081?v=4\" alt=\"TBNilles\" title=\"TBNilles\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/ajanicij\"><img src=\"https://avatars1.githubusercontent.com/u/1755297?v=4\" alt=\"ajanicij\" title=\"ajanicij\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/aprinslo1\"><img src=\"https://avatars1.githubusercontent.com/u/711650?v=4\" alt=\"aprinslo1\" title=\"aprinslo1\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Mohammed8960\"><img src=\"https://avatars1.githubusercontent.com/u/5219371?v=4\" alt=\"Mohammed8960\" title=\"Mohammed8960\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/NA\"><img src=\"https://avatars1.githubusercontent.com/u/1600?v=4\" alt=\"NA\" title=\"NA\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Neulhan\"><img src=\"https://avatars1.githubusercontent.com/u/52434903?v=4\" alt=\"Neulhan\" title=\"Neulhan\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/kyoukhana\"><img src=\"https://avatars1.githubusercontent.com/u/756849?v=4\" alt=\"kyoukhana\" title=\"kyoukhana\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/spazzymoto\"><img src=\"https://avatars1.githubusercontent.com/u/2951012?v=4\" alt=\"spazzymoto\" title=\"spazzymoto\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/victorgrey\"><img src=\"https://avatars1.githubusercontent.com/u/207128?v=4\" alt=\"victorgrey\" title=\"victorgrey\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/ArishSultan\"><img src=\"https://avatars1.githubusercontent.com/u/31086233?v=4\" alt=\"ArishSultan\" title=\"ArishSultan\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/ehayun\"><img src=\"https://avatars1.githubusercontent.com/u/39870648?v=4\" alt=\"ehayun\" title=\"ehayun\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/kukaki\"><img src=\"https://avatars1.githubusercontent.com/u/4849535?v=4\" alt=\"kukaki\" title=\"kukaki\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/oshirokazuhide\"><img src=\"https://avatars1.githubusercontent.com/u/89958891?v=4\" alt=\"oshirokazuhide\" title=\"oshirokazuhide\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/t6tg\"><img src=\"https://avatars1.githubusercontent.com/u/33445861?v=4\" alt=\"t6tg\" title=\"t6tg\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/15189573255\"><img src=\"https://avatars1.githubusercontent.com/u/18551476?v=4\" alt=\"15189573255\" title=\"15189573255\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/AGPDev\"><img src=\"https://avatars1.githubusercontent.com/u/5721341?v=4\" alt=\"AGPDev\" title=\"AGPDev\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/AnatolyUA\"><img src=\"https://avatars1.githubusercontent.com/u/1446703?v=4\" alt=\"AnatolyUA\" title=\"AnatolyUA\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/AwsIT\"><img src=\"https://avatars1.githubusercontent.com/u/40926862?v=4\" alt=\"AwsIT\" title=\"AwsIT\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/NguyenPhuoc\"><img src=\"https://avatars1.githubusercontent.com/u/11747677?v=4\" alt=\"NguyenPhuoc\" title=\"NguyenPhuoc\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Oka00\"><img src=\"https://avatars1.githubusercontent.com/u/72302007?v=4\" alt=\"Oka00\" title=\"Oka00\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/PaddyFrenchman\"><img src=\"https://avatars1.githubusercontent.com/u/55139902?v=4\" alt=\"PaddyFrenchman\" title=\"PaddyFrenchman\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/RainerGevers\"><img src=\"https://avatars1.githubusercontent.com/u/32453861?v=4\" alt=\"RainerGevers\" title=\"RainerGevers\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Ramblestsad\"><img src=\"https://avatars1.githubusercontent.com/u/45003009?v=4\" alt=\"Ramblestsad\" title=\"Ramblestsad\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/SamuelNeves\"><img src=\"https://avatars1.githubusercontent.com/u/10797137?v=4\" alt=\"SamuelNeves\" title=\"SamuelNeves\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Scorpio69t\"><img src=\"https://avatars1.githubusercontent.com/u/24680141?v=4\" alt=\"Scorpio69t\" title=\"Scorpio69t\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Serissa4000\"><img src=\"https://avatars1.githubusercontent.com/u/122253262?v=4\" alt=\"Serissa4000\" title=\"Serissa4000\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/TianJIANG\"><img src=\"https://avatars1.githubusercontent.com/u/158459?v=4\" alt=\"TianJIANG\" title=\"TianJIANG\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Ubun1\"><img src=\"https://avatars1.githubusercontent.com/u/13261595?v=4\" alt=\"Ubun1\" title=\"Ubun1\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/WangYajun39\"><img src=\"https://avatars1.githubusercontent.com/u/27052258?v=4\" alt=\"WangYajun39\" title=\"WangYajun39\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/XinYoungCN\"><img src=\"https://avatars1.githubusercontent.com/u/18415580?v=4\" alt=\"XinYoungCN\" title=\"XinYoungCN\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/YukinaMochizuki\"><img src=\"https://avatars1.githubusercontent.com/u/26710554?v=4\" alt=\"YukinaMochizuki\" title=\"YukinaMochizuki\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/a112121788\"><img src=\"https://avatars1.githubusercontent.com/u/1457920?v=4\" alt=\"a112121788\" title=\"a112121788\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/acdias\"><img src=\"https://avatars1.githubusercontent.com/u/11966653?v=4\" alt=\"acdias\" title=\"acdias\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/aeonsthorn\"><img src=\"https://avatars1.githubusercontent.com/u/53945065?v=4\" alt=\"aeonsthorn\" title=\"aeonsthorn\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/agent3bood\"><img src=\"https://avatars1.githubusercontent.com/u/771902?v=4\" alt=\"agent3bood\" title=\"agent3bood\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/ajb-neodynamics-io\"><img src=\"https://avatars1.githubusercontent.com/u/115384296?v=4\" alt=\"ajb-neodynamics-io\" title=\"ajb-neodynamics-io\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/alessandromarotta\"><img src=\"https://avatars1.githubusercontent.com/u/17084152?v=4\" alt=\"alessandromarotta\" title=\"alessandromarotta\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/algobot76\"><img src=\"https://avatars1.githubusercontent.com/u/20016835?v=4\" alt=\"algobot76\" title=\"algobot76\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/algoflows\"><img src=\"https://avatars1.githubusercontent.com/u/65465380?v=4\" alt=\"algoflows\" title=\"algoflows\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/angelaahhu\"><img src=\"https://avatars1.githubusercontent.com/u/128401549?v=4\" alt=\"angelaahhu\" title=\"angelaahhu\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/anhxuanpham\"><img src=\"https://avatars1.githubusercontent.com/u/101174797?v=4\" alt=\"anhxuanpham\" title=\"anhxuanpham\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/annieruci\"><img src=\"https://avatars1.githubusercontent.com/u/49377699?v=4\" alt=\"annieruci\" title=\"annieruci\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/antoniejiao\"><img src=\"https://avatars1.githubusercontent.com/u/17450960?v=4\" alt=\"antoniejiao\" title=\"antoniejiao\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/artman328\"><img src=\"https://avatars1.githubusercontent.com/u/5415792?v=4\" alt=\"artman328\" title=\"artman328\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/b2cbd\"><img src=\"https://avatars1.githubusercontent.com/u/6870050?v=4\" alt=\"b2cbd\" title=\"b2cbd\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/baoch254\"><img src=\"https://avatars1.githubusercontent.com/u/74555344?v=4\" alt=\"baoch254\" title=\"baoch254\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/bastengao\"><img src=\"https://avatars1.githubusercontent.com/u/785335?v=4\" alt=\"bastengao\" title=\"bastengao\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/beytullahakyuz\"><img src=\"https://avatars1.githubusercontent.com/u/10866179?v=4\" alt=\"beytullahakyuz\" title=\"beytullahakyuz\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/bjoroen\"><img src=\"https://avatars1.githubusercontent.com/u/31513139?v=4\" alt=\"bjoroen\" title=\"bjoroen\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/blackHoleNgc1277\"><img src=\"https://avatars1.githubusercontent.com/u/41342763?v=4\" alt=\"blackHoleNgc1277\" title=\"blackHoleNgc1277\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/bunnycodego\"><img src=\"https://avatars1.githubusercontent.com/u/81451316?v=4\" alt=\"bunnycodego\" title=\"bunnycodego\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/carlos-enginner\"><img src=\"https://avatars1.githubusercontent.com/u/59775876?v=4\" alt=\"carlos-enginner\" title=\"carlos-enginner\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/centratelemedia\"><img src=\"https://avatars1.githubusercontent.com/u/99481333?v=4\" alt=\"centratelemedia\" title=\"centratelemedia\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/chrismalek\"><img src=\"https://avatars1.githubusercontent.com/u/9403?v=4\" alt=\"chrismalek\" title=\"chrismalek\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/civicwar\"><img src=\"https://avatars1.githubusercontent.com/u/1858104?v=4\" alt=\"civicwar\" title=\"civicwar\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/cnzhangquan\"><img src=\"https://avatars1.githubusercontent.com/u/5462876?v=4\" alt=\"cnzhangquan\" title=\"cnzhangquan\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/cuong48d\"><img src=\"https://avatars1.githubusercontent.com/u/456049?v=4\" alt=\"cuong48d\" title=\"cuong48d\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/damiensy\"><img src=\"https://avatars1.githubusercontent.com/u/147525?v=4\" alt=\"damiensy\" title=\"damiensy\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/danlanxiaohei\"><img src=\"https://avatars1.githubusercontent.com/u/3272530?v=4\" alt=\"danlanxiaohei\" title=\"danlanxiaohei\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/dextercai\"><img src=\"https://avatars1.githubusercontent.com/u/12377850?v=4\" alt=\"dextercai\" title=\"dextercai\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/dfaugusto\"><img src=\"https://avatars1.githubusercontent.com/u/1554920?v=4\" alt=\"dfaugusto\" title=\"dfaugusto\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/dkzhang\"><img src=\"https://avatars1.githubusercontent.com/u/1091431?v=4\" alt=\"dkzhang\" title=\"dkzhang\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/dloprodu\"><img src=\"https://avatars1.githubusercontent.com/u/664947?v=4\" alt=\"dloprodu\" title=\"dloprodu\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/donam-givita\"><img src=\"https://avatars1.githubusercontent.com/u/107529604?v=4\" alt=\"donam-givita\" title=\"donam-givita\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/dph0899\"><img src=\"https://avatars1.githubusercontent.com/u/124650663?v=4\" alt=\"dph0899\" title=\"dph0899\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/dvitale\"><img src=\"https://avatars1.githubusercontent.com/u/17982034?v=4\" alt=\"dvitale\" title=\"dvitale\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/ec0629\"><img src=\"https://avatars1.githubusercontent.com/u/7861125?v=4\" alt=\"ec0629\" title=\"ec0629\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/edwindna2\"><img src=\"https://avatars1.githubusercontent.com/u/5441354?v=4\" alt=\"edwindna2\" title=\"edwindna2\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/ekiyooka\"><img src=\"https://avatars1.githubusercontent.com/u/44221187?v=4\" alt=\"ekiyooka\" title=\"ekiyooka\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/ekofedriyanto\"><img src=\"https://avatars1.githubusercontent.com/u/1669439?v=4\" alt=\"ekofedriyanto\" title=\"ekofedriyanto\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/eli-yip\"><img src=\"https://avatars1.githubusercontent.com/u/40079533?v=4\" alt=\"eli-yip\" title=\"eli-yip\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/eljefedelrodeodeljefe\"><img src=\"https://avatars1.githubusercontent.com/u/3899684?v=4\" alt=\"eljefedelrodeodeljefe\" title=\"eljefedelrodeodeljefe\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/fenriz07\"><img src=\"https://avatars1.githubusercontent.com/u/9199380?v=4\" alt=\"fenriz07\" title=\"fenriz07\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/ffelipelimao\"><img src=\"https://avatars1.githubusercontent.com/u/28612817?v=4\" alt=\"ffelipelimao\" title=\"ffelipelimao\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/frenchmajesty\"><img src=\"https://avatars1.githubusercontent.com/u/24761660?v=4\" alt=\"frenchmajesty\" title=\"frenchmajesty\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/gastropulgite\"><img src=\"https://avatars1.githubusercontent.com/u/85067528?v=4\" alt=\"gastropulgite\" title=\"gastropulgite\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/geGao123\"><img src=\"https://avatars1.githubusercontent.com/u/6398228?v=4\" alt=\"geGao123\" title=\"geGao123\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/globalflea\"><img src=\"https://avatars1.githubusercontent.com/u/127675?v=4\" alt=\"globalflea\" title=\"globalflea\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/gloudx\"><img src=\"https://avatars1.githubusercontent.com/u/6920756?v=4\" alt=\"gloudx\" title=\"gloudx\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/gnosthi\"><img src=\"https://avatars1.githubusercontent.com/u/17650528?v=4\" alt=\"gnosthi\" title=\"gnosthi\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/gogoswift\"><img src=\"https://avatars1.githubusercontent.com/u/14092975?v=4\" alt=\"gogoswift\" title=\"gogoswift\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/goten002\"><img src=\"https://avatars1.githubusercontent.com/u/5025060?v=4\" alt=\"goten002\" title=\"goten002\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/guanzi008\"><img src=\"https://avatars1.githubusercontent.com/u/20619190?v=4\" alt=\"guanzi008\" title=\"guanzi008\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/hdezoscar93\"><img src=\"https://avatars1.githubusercontent.com/u/21270107?v=4\" alt=\"hdezoscar93\" title=\"hdezoscar93\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/hieungm\"><img src=\"https://avatars1.githubusercontent.com/u/85067528?v=4\" alt=\"hieungm\" title=\"hieungm\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/hieunmg\"><img src=\"https://avatars1.githubusercontent.com/u/85067528?v=4\" alt=\"hieunmg\" title=\"hieunmg\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/homerious\"><img src=\"https://avatars1.githubusercontent.com/u/22523525?v=4\" alt=\"homerious\" title=\"homerious\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/hzxd\"><img src=\"https://avatars1.githubusercontent.com/u/3376231?v=4\" alt=\"hzxd\" title=\"hzxd\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/inyellowbus\"><img src=\"https://avatars1.githubusercontent.com/u/8218128?v=4\" alt=\"inyellowbus\" title=\"inyellowbus\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/iuliancarnaru\"><img src=\"https://avatars1.githubusercontent.com/u/35683015?v=4\" alt=\"iuliancarnaru\" title=\"iuliancarnaru\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/iysaleh\"><img src=\"https://avatars1.githubusercontent.com/u/13583253?v=4\" alt=\"iysaleh\" title=\"iysaleh\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/jackptoke\"><img src=\"https://avatars1.githubusercontent.com/u/54049012?v=4\" alt=\"jackptoke\" title=\"jackptoke\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/jackysywk\"><img src=\"https://avatars1.githubusercontent.com/u/61909173?v=4\" alt=\"jackysywk\" title=\"jackysywk\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/jeff2go\"><img src=\"https://avatars1.githubusercontent.com/u/6629280?v=4\" alt=\"jeff2go\" title=\"jeff2go\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/jeremiahyan\"><img src=\"https://avatars1.githubusercontent.com/u/2705359?v=4\" alt=\"jeremiahyan\" title=\"jeremiahyan\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/joelywz\"><img src=\"https://avatars1.githubusercontent.com/u/43310636?v=4\" alt=\"joelywz\" title=\"joelywz\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/kamolcu\"><img src=\"https://avatars1.githubusercontent.com/u/5095235?v=4\" alt=\"kamolcu\" title=\"kamolcu\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/kana99\"><img src=\"https://avatars1.githubusercontent.com/u/3714069?v=4\" alt=\"kana99\" title=\"kana99\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/edsongley\"><img src=\"https://avatars1.githubusercontent.com/u/35545454?v=4\" alt=\"edsongley\" title=\"edsongley\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/katsubushiken\"><img src=\"https://avatars1.githubusercontent.com/u/43208445?v=4\" alt=\"katsubushiken\" title=\"katsubushiken\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/kattaprasanth\"><img src=\"https://avatars1.githubusercontent.com/u/13375911?v=4\" alt=\"kattaprasanth\" title=\"kattaprasanth\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/keeio\"><img src=\"https://avatars1.githubusercontent.com/u/147525?v=4\" alt=\"keeio\" title=\"keeio\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/keval6706\"><img src=\"https://avatars1.githubusercontent.com/u/36534030?v=4\" alt=\"keval6706\" title=\"keval6706\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/khasanovrs\"><img src=\"https://avatars1.githubusercontent.com/u/6076966?v=4\" alt=\"khasanovrs\" title=\"khasanovrs\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/kkdaypenny\"><img src=\"https://avatars1.githubusercontent.com/u/47559431?v=4\" alt=\"kkdaypenny\" title=\"kkdaypenny\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/knavels\"><img src=\"https://avatars1.githubusercontent.com/u/57287952?v=4\" alt=\"knavels\" title=\"knavels\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/kohakuhubo\"><img src=\"https://avatars1.githubusercontent.com/u/32786755?v=4\" alt=\"kohakuhubo\" title=\"kohakuhubo\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/korowiov\"><img src=\"https://avatars1.githubusercontent.com/u/5020824?v=4\" alt=\"korowiov\" title=\"korowiov\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/kostasvk\"><img src=\"https://avatars1.githubusercontent.com/u/8888490?v=4\" alt=\"kostasvk\" title=\"kostasvk\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/lafayetteDan\"><img src=\"https://avatars1.githubusercontent.com/u/26064396?v=4\" alt=\"lafayetteDan\" title=\"lafayetteDan\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/lbsubash\"><img src=\"https://avatars1.githubusercontent.com/u/101740735?v=4\" alt=\"lbsubash\" title=\"lbsubash\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/leki75\"><img src=\"https://avatars1.githubusercontent.com/u/9675379?v=4\" alt=\"leki75\" title=\"leki75\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/lemuelroberto\"><img src=\"https://avatars1.githubusercontent.com/u/322159?v=4\" alt=\"lemuelroberto\" title=\"lemuelroberto\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/liheyuan\"><img src=\"https://avatars1.githubusercontent.com/u/776423?v=4\" alt=\"liheyuan\" title=\"liheyuan\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/lingyingtan\"><img src=\"https://avatars1.githubusercontent.com/u/15610136?v=4\" alt=\"lingyingtan\" title=\"lingyingtan\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/linuxluigi\"><img src=\"https://avatars1.githubusercontent.com/u/8136842?v=4\" alt=\"linuxluigi\" title=\"linuxluigi\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/lipatti\"><img src=\"https://avatars1.githubusercontent.com/u/38935867?v=4\" alt=\"lipatti\" title=\"lipatti\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/maikelcoke\"><img src=\"https://avatars1.githubusercontent.com/u/51384?v=4\" alt=\"maikelcoke\" title=\"maikelcoke\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/marek-kuticka\"><img src=\"https://avatars1.githubusercontent.com/u/1578756?v=4\" alt=\"marek-kuticka\" title=\"marek-kuticka\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/marman-hp\"><img src=\"https://avatars1.githubusercontent.com/u/2398413?v=4\" alt=\"marman-hp\" title=\"marman-hp\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/mattbowen\"><img src=\"https://avatars1.githubusercontent.com/u/46803?v=4\" alt=\"mattbowen\" title=\"mattbowen\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/maxgozou\"><img src=\"https://avatars1.githubusercontent.com/u/54620900?v=4\" alt=\"maxgozou\" title=\"maxgozou\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/maxgozzz\"><img src=\"https://avatars1.githubusercontent.com/u/54620900?v=4\" alt=\"maxgozzz\" title=\"maxgozzz\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/mitas\"><img src=\"https://avatars1.githubusercontent.com/u/954460?v=4\" alt=\"mitas\" title=\"mitas\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/mizzlespot\"><img src=\"https://avatars1.githubusercontent.com/u/2654538?v=4\" alt=\"mizzlespot\" title=\"mizzlespot\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/mkell43\"><img src=\"https://avatars1.githubusercontent.com/u/362697?v=4\" alt=\"mkell43\" title=\"mkell43\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/mnievesco\"><img src=\"https://avatars1.githubusercontent.com/u/78430169?v=4\" alt=\"mnievesco\" title=\"mnievesco\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/mo3lyana\"><img src=\"https://avatars1.githubusercontent.com/u/4528809?v=4\" alt=\"mo3lyana\" title=\"mo3lyana\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/motogo\"><img src=\"https://avatars1.githubusercontent.com/u/1704958?v=4\" alt=\"motogo\" title=\"motogo\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/mtrense\"><img src=\"https://avatars1.githubusercontent.com/u/1008285?v=4\" alt=\"mtrense\" title=\"mtrense\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/mukunhao\"><img src=\"https://avatars1.githubusercontent.com/u/45845255?v=4\" alt=\"mukunhao\" title=\"mukunhao\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/mulyawansentosa\"><img src=\"https://avatars1.githubusercontent.com/u/29946673?v=4\" alt=\"mulyawansentosa\" title=\"mulyawansentosa\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/nasoma\"><img src=\"https://avatars1.githubusercontent.com/u/19878418?v=4\" alt=\"nasoma\" title=\"nasoma\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/ngseiyu\"><img src=\"https://avatars1.githubusercontent.com/u/44496936?v=4\" alt=\"ngseiyu\" title=\"ngseiyu\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/nikharsaxena\"><img src=\"https://avatars1.githubusercontent.com/u/8684362?v=4\" alt=\"nikharsaxena\" title=\"nikharsaxena\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/nronzel\"><img src=\"https://avatars1.githubusercontent.com/u/86695181?v=4\" alt=\"nronzel\" title=\"nronzel\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/odelanno\"><img src=\"https://avatars1.githubusercontent.com/u/63109824?v=4\" alt=\"odelanno\" title=\"odelanno\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/onlysumitg\"><img src=\"https://avatars1.githubusercontent.com/u/1676132?v=4\" alt=\"onlysumitg\" title=\"onlysumitg\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/xPoppa\"><img src=\"https://avatars1.githubusercontent.com/u/119574198?v=4\" alt=\"xPoppa\" title=\"xPoppa\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/yesudeep\"><img src=\"https://avatars1.githubusercontent.com/u/3874?v=4\" alt=\"yesudeep\" title=\"yesudeep\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/ymonk\"><img src=\"https://avatars1.githubusercontent.com/u/13493968?v=4\" alt=\"ymonk\" title=\"ymonk\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/yonson2\"><img src=\"https://avatars1.githubusercontent.com/u/1192599?v=4\" alt=\"yonson2\" title=\"yonson2\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/yshengliao\"><img src=\"https://avatars1.githubusercontent.com/u/13849858?v=4\" alt=\"yshengliao\" title=\"yshengliao\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/ytxmobile98\"><img src=\"https://avatars1.githubusercontent.com/u/5900105?v=4\" alt=\"ytxmobile98\" title=\"ytxmobile98\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/yusong-offx\"><img src=\"https://avatars1.githubusercontent.com/u/75306828?v=4\" alt=\"yusong-offx\" title=\"yusong-offx\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/zhenggangpku\"><img src=\"https://avatars1.githubusercontent.com/u/18161030?v=4\" alt=\"zhenggangpku\" title=\"zhenggangpku\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/zou8944\"><img src=\"https://avatars1.githubusercontent.com/u/18495995?v=4\" alt=\"zou8944\" title=\"zou8944\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/SergeShin\"><img src=\"https://avatars1.githubusercontent.com/u/402395?v=4\" alt=\"SergeShin\" title=\"SergeShin\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/-\"><img src=\"https://avatars1.githubusercontent.com/u/75544?v=4\" alt=\"-\" title=\"-\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/BelmonduS\"><img src=\"https://avatars1.githubusercontent.com/u/159350?v=4\" alt=\"BelmonduS\" title=\"BelmonduS\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Diewald\"><img src=\"https://avatars1.githubusercontent.com/u/6187336?v=4\" alt=\"Diewald\" title=\"Diewald\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/cty4ka\"><img src=\"https://avatars1.githubusercontent.com/u/29261879?v=4\" alt=\"cty4ka\" title=\"cty4ka\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/martinjanda\"><img src=\"https://avatars1.githubusercontent.com/u/122393?v=4\" alt=\"martinjanda\" title=\"martinjanda\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/evan\"><img src=\"https://avatars1.githubusercontent.com/u/210?v=4\" alt=\"evan\" title=\"evan\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/hazmi-e205\"><img src=\"https://avatars1.githubusercontent.com/u/12555465?v=4\" alt=\"hazmi-e205\" title=\"hazmi-e205\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/jtgoral\"><img src=\"https://avatars1.githubusercontent.com/u/19780595?v=4\" alt=\"jtgoral\" title=\"jtgoral\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/ky2s\"><img src=\"https://avatars1.githubusercontent.com/u/19502125?v=4\" alt=\"ky2s\" title=\"ky2s\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/lauweliam\"><img src=\"https://avatars1.githubusercontent.com/u/4064517?v=4\" alt=\"lauweliam\" title=\"lauweliam\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/ozfive\"><img src=\"https://avatars1.githubusercontent.com/u/4494266?v=4\" alt=\"ozfive\" title=\"ozfive\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/paulcockrell\"><img src=\"https://avatars1.githubusercontent.com/u/260514?v=4\" alt=\"paulcockrell\" title=\"paulcockrell\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/paulxu21\"><img src=\"https://avatars1.githubusercontent.com/u/6261758?v=4\" alt=\"paulxu21\" title=\"paulxu21\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/pesquive\"><img src=\"https://avatars1.githubusercontent.com/u/6610140?v=4\" alt=\"pesquive\" title=\"pesquive\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/petros9282\"><img src=\"https://avatars1.githubusercontent.com/u/3861890?v=4\" alt=\"petros9282\" title=\"petros9282\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/phil535\"><img src=\"https://avatars1.githubusercontent.com/u/7596830?v=4\" alt=\"phil535\" title=\"phil535\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/pitt134\"><img src=\"https://avatars1.githubusercontent.com/u/13091629?v=4\" alt=\"pitt134\" title=\"pitt134\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/poscard\"><img src=\"https://avatars1.githubusercontent.com/u/3023318?v=4\" alt=\"poscard\" title=\"poscard\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/qiepeipei\"><img src=\"https://avatars1.githubusercontent.com/u/16110628?v=4\" alt=\"qiepeipei\" title=\"qiepeipei\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/qiuzhanghua\"><img src=\"https://avatars1.githubusercontent.com/u/478393?v=4\" alt=\"qiuzhanghua\" title=\"qiuzhanghua\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/rapita\"><img src=\"https://avatars1.githubusercontent.com/u/22305375?v=4\" alt=\"rapita\" title=\"rapita\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/rbondi\"><img src=\"https://avatars1.githubusercontent.com/u/81764?v=4\" alt=\"rbondi\" title=\"rbondi\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/relaera\"><img src=\"https://avatars1.githubusercontent.com/u/26012106?v=4\" alt=\"relaera\" title=\"relaera\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/remopavithran\"><img src=\"https://avatars1.githubusercontent.com/u/50388068?v=4\" alt=\"remopavithran\" title=\"remopavithran\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/rfunix\"><img src=\"https://avatars1.githubusercontent.com/u/6026357?v=4\" alt=\"rfunix\" title=\"rfunix\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/rhernandez-itemsoft\"><img src=\"https://avatars1.githubusercontent.com/u/4327356?v=4\" alt=\"rhernandez-itemsoft\" title=\"rhernandez-itemsoft\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/rikoriswandha\"><img src=\"https://avatars1.githubusercontent.com/u/2549929?v=4\" alt=\"rikoriswandha\" title=\"rikoriswandha\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/risallaw\"><img src=\"https://avatars1.githubusercontent.com/u/15353146?v=4\" alt=\"risallaw\" title=\"risallaw\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/robivictor\"><img src=\"https://avatars1.githubusercontent.com/u/761041?v=4\" alt=\"robivictor\" title=\"robivictor\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/rubiagatra\"><img src=\"https://avatars1.githubusercontent.com/u/7299491?v=4\" alt=\"rubiagatra\" title=\"rubiagatra\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/rubyangxg\"><img src=\"https://avatars1.githubusercontent.com/u/3069914?v=4\" alt=\"rubyangxg\" title=\"rubyangxg\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/rxrw\"><img src=\"https://avatars1.githubusercontent.com/u/9566402?v=4\" alt=\"rxrw\" title=\"rxrw\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/saleebm\"><img src=\"https://avatars1.githubusercontent.com/u/34875122?v=4\" alt=\"saleebm\" title=\"saleebm\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/sbenimeli\"><img src=\"https://avatars1.githubusercontent.com/u/46652122?v=4\" alt=\"sbenimeli\" title=\"sbenimeli\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/sebyno\"><img src=\"https://avatars1.githubusercontent.com/u/15988169?v=4\" alt=\"sebyno\" title=\"sebyno\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/seun-otosho\"><img src=\"https://avatars1.githubusercontent.com/u/74518370?v=4\" alt=\"seun-otosho\" title=\"seun-otosho\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/shobhitsinghal77\"><img src=\"https://avatars1.githubusercontent.com/u/26848221?v=4\" alt=\"shobhitsinghal77\" title=\"shobhitsinghal77\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/solohiroshi\"><img src=\"https://avatars1.githubusercontent.com/u/96872274?v=4\" alt=\"solohiroshi\" title=\"solohiroshi\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/su1gen\"><img src=\"https://avatars1.githubusercontent.com/u/86298730?v=4\" alt=\"su1gen\" title=\"su1gen\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/sukiejosh\"><img src=\"https://avatars1.githubusercontent.com/u/44656210?v=4\" alt=\"sukiejosh\" title=\"sukiejosh\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/suresh16671\"><img src=\"https://avatars1.githubusercontent.com/u/57644510?v=4\" alt=\"suresh16671\" title=\"suresh16671\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/svirmi\"><img src=\"https://avatars1.githubusercontent.com/u/52601346?v=4\" alt=\"svirmi\" title=\"svirmi\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/terjelafton\"><img src=\"https://avatars1.githubusercontent.com/u/12574755?v=4\" alt=\"terjelafton\" title=\"terjelafton\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/thiennguyen93\"><img src=\"https://avatars1.githubusercontent.com/u/60094052?v=4\" alt=\"thiennguyen93\" title=\"thiennguyen93\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/unixedia\"><img src=\"https://avatars1.githubusercontent.com/u/70646128?v=4\" alt=\"unixedia\" title=\"unixedia\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/vadgun\"><img src=\"https://avatars1.githubusercontent.com/u/22282464?v=4\" alt=\"vadgun\" title=\"vadgun\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/valsorym\"><img src=\"https://avatars1.githubusercontent.com/u/4440262?v=4\" alt=\"valsorym\" title=\"valsorym\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/vguhesan\"><img src=\"https://avatars1.githubusercontent.com/u/193960?v=4\" alt=\"vguhesan\" title=\"vguhesan\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/vpiduri\"><img src=\"https://avatars1.githubusercontent.com/u/19339398?v=4\" alt=\"vpiduri\" title=\"vpiduri\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/vrocadev\"><img src=\"https://avatars1.githubusercontent.com/u/50081969?v=4\" alt=\"vrocadev\" title=\"vrocadev\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/vuhoanglam\"><img src=\"https://avatars1.githubusercontent.com/u/59502855?v=4\" alt=\"vuhoanglam\" title=\"vuhoanglam\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/walter-wang\"><img src=\"https://avatars1.githubusercontent.com/u/7950295?v=4\" alt=\"walter-wang\" title=\"walter-wang\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/martinlindhe\"><img src=\"https://avatars1.githubusercontent.com/u/181531?v=4\" alt=\"martinlindhe\" title=\"martinlindhe\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/mdamschen\"><img src=\"https://avatars1.githubusercontent.com/u/40914728?v=4\" alt=\"mdamschen\" title=\"mdamschen\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/letmestudy\"><img src=\"https://avatars1.githubusercontent.com/u/31943708?v=4\" alt=\"letmestudy\" title=\"letmestudy\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/michaelsmanley\"><img src=\"https://avatars1.githubusercontent.com/u/93241?v=4\" alt=\"michaelsmanley\" title=\"michaelsmanley\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/Curtman\"><img src=\"https://avatars1.githubusercontent.com/u/543481?v=4\" alt=\"Curtman\" title=\"Curtman\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/SridarDhandapani\"><img src=\"https://avatars1.githubusercontent.com/u/18103118?v=4\" alt=\"SridarDhandapani\" title=\"SridarDhandapani\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/madrigaltenor\"><img src=\"https://avatars1.githubusercontent.com/u/168838315?v=4\" alt=\"madrigaltenor\" title=\"madrigaltenor\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/opusmagna\"><img src=\"https://avatars1.githubusercontent.com/u/33766678?v=4\" alt=\"opusmagna\" title=\"opusmagna\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/ShahramMebashar\"><img src=\"https://avatars1.githubusercontent.com/u/25268287?v=4\" alt=\"ShahramMebashar\" title=\"ShahramMebashar\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/b4zz4r\"><img src=\"https://avatars1.githubusercontent.com/u/7438782?v=4\" alt=\"b4zz4r\" title=\"b4zz4r\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/bobmcallan\"><img src=\"https://avatars1.githubusercontent.com/u/8773580?v=4\" alt=\"bobmcallan\" title=\"bobmcallan\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/fangli\"><img src=\"https://avatars1.githubusercontent.com/u/3032639?v=4\" alt=\"fangli\" title=\"fangli\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/galois-tnp\"><img src=\"https://avatars1.githubusercontent.com/u/41128011?v=4\" alt=\"galois-tnp\" title=\"galois-tnp\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/mblandr\"><img src=\"https://avatars1.githubusercontent.com/u/42862020?v=4\" alt=\"mblandr\" title=\"mblandr\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/midhubalan\"><img src=\"https://avatars1.githubusercontent.com/u/13059634?v=4\" alt=\"midhubalan\" title=\"midhubalan\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/netbaalzovf\"><img src=\"https://avatars1.githubusercontent.com/u/98529711?v=4\" alt=\"netbaalzovf\" title=\"netbaalzovf\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/oliverjosefzimmer\"><img src=\"https://avatars1.githubusercontent.com/u/24566297?v=4\" alt=\"oliverjosefzimmer\" title=\"oliverjosefzimmer\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/peacememories\"><img src=\"https://avatars1.githubusercontent.com/u/1326334?v=4\" alt=\"peacememories\" title=\"peacememories\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/talebisinan\"><img src=\"https://avatars1.githubusercontent.com/u/42139005?v=4\" alt=\"talebisinan\" title=\"talebisinan\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/valkuere\"><img src=\"https://avatars1.githubusercontent.com/u/7230144?v=4\" alt=\"valkuere\" title=\"valkuere\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/lfaynman\"><img src=\"https://avatars1.githubusercontent.com/u/16815068?v=4\" alt=\"lfaynman\" title=\"lfaynman\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/ArturWierzbicki\"><img src=\"https://avatars1.githubusercontent.com/u/23451458?v=4\" alt=\"ArturWierzbicki\" title=\"ArturWierzbicki\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/aaxx\"><img src=\"https://avatars1.githubusercontent.com/u/476416?v=4\" alt=\"aaxx\" title=\"aaxx\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/crashCoder\"><img src=\"https://avatars1.githubusercontent.com/u/1144298?v=4\" alt=\"crashCoder\" title=\"crashCoder\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/derekslenk\"><img src=\"https://avatars1.githubusercontent.com/u/42957?v=4\" alt=\"derekslenk\" title=\"derekslenk\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/dochoaj\"><img src=\"https://avatars1.githubusercontent.com/u/1789678?v=4\" alt=\"dochoaj\" title=\"dochoaj\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/evillgenius75\"><img src=\"https://avatars1.githubusercontent.com/u/22817701?v=4\" alt=\"evillgenius75\" title=\"evillgenius75\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/gog200921\"><img src=\"https://avatars1.githubusercontent.com/u/101519620?v=4\" alt=\"gog200921\" title=\"gog200921\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/mauricedcastro\"><img src=\"https://avatars1.githubusercontent.com/u/6446532?v=4\" alt=\"mauricedcastro\" title=\"mauricedcastro\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/mwiater\"><img src=\"https://avatars1.githubusercontent.com/u/5323591?v=4\" alt=\"mwiater\" title=\"mwiater\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/sj671\"><img src=\"https://avatars1.githubusercontent.com/u/7363652?v=4\" alt=\"sj671\" title=\"sj671\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/statik\"><img src=\"https://avatars1.githubusercontent.com/u/983?v=4\" alt=\"statik\" title=\"statik\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/supersherm5\"><img src=\"https://avatars1.githubusercontent.com/u/7953550?v=4\" alt=\"supersherm5\" title=\"supersherm5\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/thejones\"><img src=\"https://avatars1.githubusercontent.com/u/682850?v=4\" alt=\"thejones\" title=\"thejones\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/CSRaghunandan\"><img src=\"https://avatars1.githubusercontent.com/u/5226809?v=4\" alt=\"CSRaghunandan\" title=\"CSRaghunandan\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/ndimorle\"><img src=\"https://avatars1.githubusercontent.com/u/76732415?v=4\" alt=\"ndimorle\" title=\"ndimorle\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/rosales-stephanie\"><img src=\"https://avatars1.githubusercontent.com/u/43592017?v=4\" alt=\"rosales-stephanie\" title=\"rosales-stephanie\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/shyyawn\"><img src=\"https://avatars1.githubusercontent.com/u/6064438?v=4\" alt=\"shyyawn\" title=\"shyyawn\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/vcruzato\"><img src=\"https://avatars1.githubusercontent.com/u/3864151?v=4\" alt=\"vcruzato\" title=\"vcruzato\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/wangbl11\"><img src=\"https://avatars1.githubusercontent.com/u/14358532?v=4\" alt=\"wangbl11\" title=\"wangbl11\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/wofka72\"><img src=\"https://avatars1.githubusercontent.com/u/10855340?v=4\" alt=\"wofka72\" title=\"wofka72\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/geoshan\"><img src=\"https://avatars1.githubusercontent.com/u/10161131?v=4\" alt=\"geoshan\" title=\"geoshan\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/juanxme\"><img src=\"https://avatars1.githubusercontent.com/u/661043?v=4\" alt=\"juanxme\" title=\"juanxme\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/nguyentamvinhlong\"><img src=\"https://avatars1.githubusercontent.com/u/1875916?v=4\" alt=\"nguyentamvinhlong\" title=\"nguyentamvinhlong\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/yoru74\"><img src=\"https://avatars1.githubusercontent.com/u/7745866?v=4\" alt=\"yoru74\" title=\"yoru74\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/xsokev\"><img src=\"https://avatars1.githubusercontent.com/u/28113?v=4\" alt=\"xsokev\" title=\"xsokev\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/oleang\"><img src=\"https://avatars1.githubusercontent.com/u/142615?v=4\" alt=\"oleang\" title=\"oleang\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/michalsz\"><img src=\"https://avatars1.githubusercontent.com/u/187477?v=4\" alt=\"michalsz\" title=\"michalsz\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/pomland-94\"><img src=\"https://avatars1.githubusercontent.com/u/96850116?v=4\" alt=\"pomland-94\" title=\"pomland-94\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/tejzpr\"><img src=\"https://avatars1.githubusercontent.com/u/2813811?v=4\" alt=\"tejzpr\" title=\"tejzpr\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/theantichris\"><img src=\"https://avatars1.githubusercontent.com/u/1486502?v=4\" alt=\"theantichris\" title=\"theantichris\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/tuxaanand\"><img src=\"https://avatars1.githubusercontent.com/u/9750371?v=4\" alt=\"tuxaanand\" title=\"tuxaanand\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/raphael-brand\"><img src=\"https://avatars1.githubusercontent.com/u/4279168?v=4\" alt=\"raphael-brand\" title=\"raphael-brand\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/willypuzzle\"><img src=\"https://avatars1.githubusercontent.com/u/18305386?v=4\" alt=\"willypuzzle\" title=\"willypuzzle\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/dmcbane\"><img src=\"https://avatars1.githubusercontent.com/u/5453862?v=4\" alt=\"dmcbane\" title=\"dmcbane\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/malcolm-white-dti\"><img src=\"https://avatars1.githubusercontent.com/u/109724322?v=4\" alt=\"malcolm-white-dti\" title=\"malcolm-white-dti\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/HieuLsw\"><img src=\"https://avatars1.githubusercontent.com/u/1675478?v=4\" alt=\"HieuLsw\" title=\"HieuLsw\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/carlosmoran092\"><img src=\"https://avatars1.githubusercontent.com/u/10361754?v=4\" alt=\"carlosmoran092\" title=\"carlosmoran092\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n  <a href=\"https://github.com/yangxianglong\"><img src=\"https://avatars1.githubusercontent.com/u/55280276?v=4\" alt=\"yangxianglong\" title=\"yangxianglong\" width=\"75\" height=\"75\" style=\"width:75px;max-width:75px;height:75px\" /></a>\n</p>\n\n## <p style=\"text-align: right;\">تعلّم Iris</p>\n\n### <p style=\"text-align: right;\">التثبيت</p>\n\nالمتطلب الوحيد هو [لغة البرمجة Go](https://go.dev/dl/).\n\n#### <p style=\"text-align: right;\">إنشاء مشروع جديد</p>\n\n```sh\n$ mkdir myapp\n$ cd myapp\n$ go mod init myapp\n$ go get github.com/kataras/iris/v12@latest # or @v12.2.11\n```\n\n<details><summary style=\"text-align: right;\">التثبيت على مشروع قائم</summary>\n\n```sh\n$ cd myapp\n$ go get github.com/kataras/iris/v12@latest\n```\n\n**تشغيل**\n\n```sh\n$ go mod tidy -compat=1.23 # -compat=\"1.23\" for windows.\n$ go run .\n```\n\n</details>\n\n![](https://www.iris-go.com/images/gifs/install-create-iris.gif)\n\nيحتوي Iris على **[وثائق](https://www.iris-go.com/docs)** شاملة ومفصّلة تسهّل عليك البدء باستخدام الإطار.\n\nللاطلاع على توثيق تقني أكثر تفصيلاً، يمكنك زيارة [godocs](https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.11). الخاص بنا، وللاطلاع على أمثلة قابلة للتنفيذ، يمكنك دائمًا زيارة المجلد الفرعي للمستودع [./_examples](_examples).\n\n### <p style=\"text-align: right;\">هل تحب القراءة أثناء السفر؟</p>\n\n<a href=\"https://iris-go.com/#book\"> <img alt=\"Book cover\" src=\"https://iris-go.com/static/images/iris-book-cover-sm.jpg?v=12\" /> </a>\n\n[![follow author on twitter](https://img.shields.io/twitter/follow/makismaropoulos?color=3D8AA3&logoColor=3D8AA3&style=for-the-badge&logo=twitter)](https://twitter.com/intent/follow?screen_name=makismaropoulos)\n\n[![follow Iris web framework on twitter](https://img.shields.io/twitter/follow/iris_framework?color=ee7506&logoColor=ee7506&style=for-the-badge&logo=twitter)](https://twitter.com/intent/follow?screen_name=iris_framework)\n\n[![follow Iris web framework on facebook](https://img.shields.io/badge/Follow%20%40Iris.framework-569-2D88FF.svg?style=for-the-badge&logo=facebook)](https://www.facebook.com/iris.framework)\n\n\nيمكنك [طلب نسخة](https://www.iris-go.com/#ebookDonateForm) PDF أو الوصول إلى **النسخة الإلكترونية** من كتاب Iris (الإصدار الجديد **v12.2.0+**) اليوم والمساهمة في تطوير الإطار.\n\n## <p style=\"text-align: right;\">🙌 المساهمة</p>\n\nنسعد برؤية مساهمتك في تطوير إطار عمل Iris! لمزيد من التفاصيل حول كيفية المساهمة، يُرجى الاطلاع على ملف [CONTRIBUTING.md](CONTRIBUTING.md).\n\n[قائمة جميع المساهمين](https://github.com/kataras/iris/graphs/contributors)\n\n## <p style=\"text-align: right;\">🛡 الثغرات الأمنية</p>\n\nإذا اكتشفت ثغرة أمنية في Iris، يُرجى إرسال بريد إلكتروني إلى [iris-go@outlook.com](mailto:iris-go@outlook.com). سيتم التعامل مع جميع الثغرات الأمنية بسرعة.\n\n## <p style=\"text-align: right;\">📝 الرخصة</p>\n\nهذا المشروع مرخّص بموجب رخصة [BSD بثلاثة بنود](LICENSE), تمامًا كحال مشروع Go نفسه.\n\nاسم المشروع \"Iris\" مستوحى من الأساطير اليونانية.\n"
  },
  {
    "path": "README_ES.md",
    "content": "# Iris Web Framework\n\n[![build status](https://img.shields.io/github/actions/workflow/status/kataras/iris/ci.yml?branch=main&style=for-the-badge)](https://github.com/kataras/iris/actions/workflows/ci.yml) [![FOSSA Status](https://img.shields.io/badge/LICENSE%20SCAN-PASSING❤️-CD2956?style=for-the-badge&logo=fossa)](https://app.fossa.io/projects/git%2Bgithub.com%2Fkataras%2Firis?ref=badge_shield)<!--[![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=for-the-badge)](https://goreportcard.com/report/github.com/kataras/iris)--><!--[![godocs](https://img.shields.io/badge/go-%20docs-488AC7.svg?style=for-the-badge)](https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.11)--> [![view examples](https://img.shields.io/badge/learn%20by-examples-0C8EC5.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/main/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=7E18DD&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community)<!--[![donate on PayPal](https://img.shields.io/badge/support-PayPal-blue.svg?style=for-the-badge)](https://iris-go.com/donate)--><!-- [![release](https://img.shields.io/badge/release%20-v12.0-0077b3.svg?style=for-the-badge)](https://github.com/kataras/iris/releases) -->\n\nIris es un framework web rápido, simple pero con muchas funcionalidades y muy eficiente para Go. Proporciona una base bellamente expresiva y fácil de usar para su próximo sitio web o API.\n\nDescubra lo que [otros dicen sobre Iris](https://iris-go.com/testimonials/) y **siga** :star: este repositorio github.\n\n[![](https://media.giphy.com/media/j5WLmtvwn98VPrm7li/giphy.gif)](https://iris-go.com/testimonials/)\n\n[![Benchmarks: Apr 2, 2020 at 12:13pm (UTC)](https://iris-go.com/static/images/benchmarks.svg)](https://github.com/kataras/server-benchmarks)\n\n## Aprende Iris\n\n<details>\n<summary>Inicio rapido</summary>\n\n```sh\n# agrega el siguiente código en el archivo ejemplo.go\n$ cat ejemplo.go\n```\n\n```go\npackage main\n\nimport \"github.com/kataras/iris/v12\"\n\nfunc main() {\n    app := iris.Default()\n    app.Get(\"/ping\", func(ctx iris.Context) {\n        ctx.JSON(iris.Map{\n            \"message\": \"pong\",\n        })\n    })\n\n    app.Listen(\":8080\")\n}\n```\n\n```sh\n# ejecuta ejemplo.go y\n# visita http://localhost:8080/ping en el navegador\n$ go run ejemplo.go\n```\n\n> El enrutamiento es impulsado por [muxie](https://github.com/kataras/muxie), el software basado en trie más potente y rápido escrito en Go.\n\n</details>\n\nIris contiene un extenso y completo **[wiki](https://www.iris-go.com/#ebookDonateForm)** que facilita comenzar con el framework.\n\nPara obtener una documentación técnica más detallada, puede dirigirse a nuestros [godocs](https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.11). Y para código ejecutable siempre puede visitar el subdirectorio del repositorio [\\_examples](_examples/).\n\n### ¿Te gusta leer mientras viajas?\n\n<a href=\"https://iris-go.com/#book\"> <img alt=\"Book cover\" src=\"https://iris-go.com/static/images/iris-book-cover-sm.jpg?v=12\" /> </a>\n\n[![follow author](https://img.shields.io/twitter/follow/makismaropoulos.svg?style=for-the-badge)](https://twitter.com/intent/follow?screen_name=makismaropoulos)\n\nPuedes [solicitar](https://www.iris-go.com/#ebookDonateForm) una versión en PDF y acceso en línea del **E-Book** hoy y participar en el desarrollo de Iris.\n\n## Contribuir\n\n¡Nos encantaría ver su contribución al Framework Web Iris! Para obtener más información sobre cómo contribuir al proyecto Iris, consulte el archivo [CONTRIBUTING.md](CONTRIBUTING).\n\n[Lista de todos los contribuyentes](https://github.com/kataras/iris/graphs/contributors)\n\n## Vulnerabilidades de seguridad\n\nSi descubres una vulnerabilidad de seguridad dentro de Iris, envíe un correo electrónico a [iris-go@outlook.com](mailto:iris-go@outlook.com). Todas las vulnerabilidades de seguridad serán tratadas de inmediato.\n\n## Licencia\n\nEl nombre del proyecto \"Iris\" se inspiró en la mitología griega.\n\nEl Web Framework Iris es un software gratuito y de código abierto con licencia bajo la [Licencia BSD 3 cláusulas](LICENSE).\n"
  },
  {
    "path": "README_FA.md",
    "content": "<div dir=\"rtl\">\n\n## خبرها\n    \n> این شاخه تحت توسعه است. برای رفتن به شاخه نسخه بعدی [v12.2.0](HISTORY.md#Next) یا اگر به دنبال یک انتشار پایدار هستید, به جای آن به شاخه [v12.1.8 branch](https://github.com/kataras/iris/tree/v12.1.8) مراجعه کنید.\n    \n> ![](https://iris-go.com/static/images/cli.png) همین امروز برنامه رسمی [Iris Command Line Interface](https://github.com/kataras/iris-cli) را امتحان کنید.\n\n> با توجه به بالا بودن حجم کار، ممکن است در پاسخ به [سوالات](https://github.com/kataras/iris/issues) شما تاخیری وجود داشته باشد.\n\n# Iris Web Framework\n    \n[![build status](https://img.shields.io/github/actions/workflow/status/kataras/iris/ci.yml?branch=main&style=for-the-badge)](https://github.com/kataras/iris/actions/workflows/ci.yml) [![FOSSA Status](https://img.shields.io/badge/LICENSE%20SCAN-PASSING❤️-CD2956?style=for-the-badge&logo=fossa)](https://app.fossa.io/projects/git%2Bgithub.com%2Fkataras%2Firis?ref=badge_shield)<!--[![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=for-the-badge)](https://goreportcard.com/report/github.com/kataras/iris)--><!--[![godocs](https://img.shields.io/badge/go-%20docs-488AC7.svg?style=for-the-badge)](https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.11)--> [![view examples](https://img.shields.io/badge/learn%20by-examples-0C8EC5.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/main/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=7E18DD&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community)<!--[![donate on PayPal](https://img.shields.io/badge/support-PayPal-blue.svg?style=for-the-badge)](https://iris-go.com/donate)--><!-- [![release](https://img.shields.io/badge/release%20-v12.0-0077b3.svg?style=for-the-badge)](https://github.com/kataras/iris/releases) -->\n    \nآیریس یک چارچوب وب پر سرعت ، ساده و در عین حال کاملاً برجسته و بسیار کارآمد برای Go است.\n</div>\n\n<details><summary>Simple Handler</summary>\n\n```go\npackage main\n\nimport \"github.com/kataras/iris/v12\"\n\ntype (\n  request struct {\n    Firstname string `json:\"firstname\"`\n    Lastname  string `json:\"lastname\"`\n  }\n\n  response struct {\n    ID      uint64 `json:\"id\"`\n    Message string `json:\"message\"`\n  }\n)\n\nfunc main() {\n  app := iris.New()\n  app.Handle(\"PUT\", \"/users/{id:uint64}\", updateUser)\n  app.Listen(\":8080\")\n}\n\nfunc updateUser(ctx iris.Context) {\n  id, _ := ctx.Params().GetUint64(\"id\")\n\n  var req request\n  if err := ctx.ReadJSON(&req); err != nil {\n    ctx.StopWithError(iris.StatusBadRequest, err)\n    return\n  }\n\n  resp := response{\n    ID:      id,\n    Message: req.Firstname + \" updated successfully\",\n  }\n  ctx.JSON(resp)\n}\n```\n> !برای اطلاعات بیشتر ، [مثال های مسیریابی](https://github.com/kataras/iris/blob/main/_examples/routing) را بخوانید\n\n</details>\n\n<details><summary>Handler with custom input and output arguments</summary>\n\n[![https://github.com/kataras/iris/blob/main/_examples/dependency-injection/basic/main.go](https://user-images.githubusercontent.com/22900943/105253731-b8db6d00-5b88-11eb-90c1-0c92a5581c86.png)](https://twitter.com/iris_framework/status/1234783655408668672)\n\n> اگر برایتان جالب بود [مثال های دیگری](https://github.com/kataras/iris/blob/main/_examples/dependency-injection) را مطالعه کنید\n\n</details>\n\n<details><summary>MVC</summary>\n\n```go\npackage main\n\nimport (\n  \"github.com/kataras/iris/v12\"\n  \"github.com/kataras/iris/v12/mvc\"\n)\n\ntype (\n  request struct {\n    Firstname string `json:\"firstname\"`\n    Lastname  string `json:\"lastname\"`\n  }\n\n  response struct {\n    ID      uint64 `json:\"id\"`\n    Message string `json:\"message\"`\n  }\n)\n\nfunc main() {\n  app := iris.New()\n  mvc.Configure(app.Party(\"/users\"), configureMVC)\n  app.Listen(\":8080\")\n}\n\nfunc configureMVC(app *mvc.Application) {\n  app.Handle(new(userController))\n}\n\ntype userController struct {\n  // [...dependencies]\n}\n\nfunc (c *userController) PutBy(id uint64, req request) response {\n  return response{\n    ID:      id,\n    Message: req.Firstname + \" updated successfully\",\n  }\n}\n```\nاگر به دنبال مثال‌های بیشتری هستید می‌توانید در [اینجا](_examples/mvc) مطالعه کنید\n</details>\n<div dir=\"rtl\">\n    \n> دیگران درباره آیریس چه می گویند و برای پشتیبانی از پتانسیل‌های  این پروژه متن باز  می‌توانید از آن حمایت کنید\n\n[![](https://iris-go.com/static/images/reviews.gif)](https://iris-go.com/testimonials/)\n\n[![Benchmarks: Jul 18, 2020 at 10:46am (UTC)](https://iris-go.com/static/images/benchmarks.svg)](https://github.com/kataras/server-benchmarks)\n\n## 👑 <a href=\"https://iris-go.com/donate\">حامیان</a>\n    \nبا کمک شما, ما می‌توانیم توسعه وب متن باز را برای همه بهبود ببخشیم !\n\n> کمک هایی که تا حالا دریافت شده است !\n \n## اموزش آیریس\n    \n### ساخت یک پروژه جدید\n\n</div>\n    \n```sh\n$ mkdir myapp\n$ cd myapp\n$ go mod init myapp\n$ go get github.com/kataras/iris/v12@latest # or @v12.2.11\n```\n\n<div dir=\"rtl\">\n<summary>نصب بر روی پروژه موجود</summary>\n</div>\n\n```sh\n$ cd myapp\n$ go get github.com/kataras/iris/v12@latest\n```\n\n<div dir=\"rtl\">\n<summary>نصب با پرونده go.mod</summary>\n</div>\n\n```txt\nmodule myapp\n\ngo 1.20\n\nrequire github.com/kataras/iris/v12 v12.2.0-beta4.0.20220920072528-ff81f370625a\n```\n![](https://www.iris-go.com/images/gifs/install-create-iris.gif)\n\n<div dir=\"rtl\">\nآیریس شامل مستندات گسترده و کاملی است که کار با چارچوب را آسان می کند.\n\n> [مستندات](https://www.iris-go.com/docs)\n    \nبرای اطلاعات بیشتر در مورد اسناد فنی می توانید به مستندات اصلی ما مراجعه کنید. \n\n> [مستندات اصلی](https://pkg.go.dev/github.com/kataras/iris/v12@main)\n    \n## دوست دارید در حین مسافرت کتاب بخوانید ?\n    \n <a href=\"https://iris-go.com/#book\"> <img alt=\"Book cover\" src=\"https://iris-go.com/static/images/iris-book-cover-sm.jpg?v=12\" /> </a>\n\n[![follow author on twitter](https://img.shields.io/twitter/follow/makismaropoulos?color=3D8AA3&logoColor=3D8AA3&style=for-the-badge&logo=twitter)](https://twitter.com/intent/follow?screen_name=makismaropoulos)\n\n[![follow Iris web framework on twitter](https://img.shields.io/twitter/follow/iris_framework?color=ee7506&logoColor=ee7506&style=for-the-badge&logo=twitter)](https://twitter.com/intent/follow?screen_name=iris_framework)\n\n[![follow Iris web framework on facebook](https://img.shields.io/badge/Follow%20%40Iris.framework-522-2D88FF.svg?style=for-the-badge&logo=facebook)](https://www.facebook.com/iris.framework)\n    \n امروز می توانید از طریق کتاب الکترونیکی آیریس (نسخه جدید ، آینده v12.2.0 +) دسترسی PDF و دسترسی آنلاین داشته باشید و در توسعه آیریس شرکت کنید.\n    \n ## 🙌 مشارکت\n    \n ما خیلی دوست داریم شما سهمی در توسعه چارچوب آیریس داشته باشید! برای دریافت اطلاعات بیشتر در مورد مشارکت در پروژه آیریس لطفاً پرونده [CONTRIBUTING.md](CONTRIBUTING.md) را مطالعه کنید.  \n    \n[لیست همه شرکت کنندگان](https://github.com/kataras/iris/graphs/contributors)\n    \n## 🛡 آسیب‌پذیری‌های امنیتی\n    \nاگر آسیب‌پذیری امنیتی در درون آیریس مشاهده کردید, لطفاً ایمیلی به [iris-go@outlook.com](mailto:iris-go@outlook.com) بفرستید. کلیه ضعف‌های امنیتی بلافاصله مورد توجه قرار خواهند گرفت.\n    \n## 📝 مجوز\n    \nاین پروژه تحت پروانه [BSD 3-clause license](LICENSE) مجوز دارد ، دقیقاً مانند پروژه Go.    \n    \nنام پروژه \"آیریس\" از اساطیر یونانی الهام‌گرفته شده است.\n\n</div>\n  \n"
  },
  {
    "path": "README_FR.md",
    "content": "# Iris Web Framework\n\n[![build status](https://img.shields.io/github/actions/workflow/status/kataras/iris/ci.yml?branch=main&style=for-the-badge)](https://github.com/kataras/iris/actions/workflows/ci.yml) [![FOSSA Status](https://img.shields.io/badge/LICENSE%20SCAN-PASSING❤️-CD2956?style=for-the-badge&logo=fossa)](https://app.fossa.io/projects/git%2Bgithub.com%2Fkataras%2Firis?ref=badge_shield)<!--[![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=for-the-badge)](https://goreportcard.com/report/github.com/kataras/iris)--><!--[![godocs](https://img.shields.io/badge/go-%20docs-488AC7.svg?style=for-the-badge)](https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.11)--> [![view examples](https://img.shields.io/badge/learn%20by-examples-0C8EC5.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/main/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=7E18DD&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community)<!--[![donate on PayPal](https://img.shields.io/badge/support-PayPal-blue.svg?style=for-the-badge)](https://iris-go.com/donate)--><!-- [![release](https://img.shields.io/badge/release%20-v12.0-0077b3.svg?style=for-the-badge)](https://github.com/kataras/iris/releases) -->\n\n<a href=\"https://iris-go.com\"> <img align=\"right\" src=\"https://iris-go.com/static/images/logo-w169.png\"></a>\n\nIris est un framework open-source pour Go à la fois simple, rapide et pourvu de nombreuses fonctionnalités.\n\nIl fournit des moyens simples et élégants de construire les bases et fonctionnalités de votre site, application backend ou API Rest.\n\nLisez [ce que les développeurs pensent d'Iris](https://iris-go.com/testimonials/) et si l'envie vous prend **[étoilez](https://github.com/kataras/iris/stargazers)** le projet pour faire monter son potentiel.\n\n[![](https://media.giphy.com/media/j5WLmtvwn98VPrm7li/giphy.gif)](https://iris-go.com/testimonials/)\n\n[![Benchmarks: Apr 2, 2020 at 12:13pm (UTC)](https://iris-go.com/static/images/benchmarks.svg)](https://github.com/kataras/server-benchmarks)\n\n## 📖 Démarrer avec Iris\n\n<details>\n<summary>Un simple Hello World</summary>\n\n```sh\n# https://www.iris-go.com/#ebookDonateForm\n$ go get github.com/kataras/iris/v12@latest\n# assume the following code in example.go file\n$ cat example.go\n```\n\n```go\npackage main\n\nimport \"github.com/kataras/iris/v12\"\n\nfunc main() {\n    app := iris.New()\n    app.Get(\"/ping\", func(ctx iris.Context) {\n        ctx.JSON(iris.Map{\n            \"message\": \"pong\",\n        })\n    })\n\n    app.Listen(\":8080\")  // port d'écoute\n}\n```\n\n```sh\n# compile et execute example.go\n$ go run example.go\n# maintenant visitez http://localhost:8080/ping\n```\n\n> Le routing est géré par [muxie](https://github.com/kataras/muxie), la librairie Go la plus rapide et complète.\n\n</details>\n\nIris possède un **[wiki](https://www.iris-go.com/#ebookDonateForm)** complet et précis qui vous permettra d'implémenter ses fonctionnalités rapidement et facilement.\n\n<!-- ![](https://media.giphy.com/media/Ur8iqy9FQfmPuyQpgy/giphy.gif) -->\n\nPour une documentation encore plus complète vous pouvez visiter notre [godocs](https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.11) (en Anglais). Et vous trouverez du code executable dans le dossier [\\_examples](_examples/).\n\n### Vous préférez une version PDF?\n\n<a href=\"https://iris-go.com/#book\"> <img alt=\"Book cover\" src=\"https://iris-go.com/static/images/iris-book-cover-sm.jpg?v=12\"/> </a>\n\n[![follow author](https://img.shields.io/twitter/follow/makismaropoulos.svg?style=for-the-badge)](https://twitter.com/intent/follow?screen_name=makismaropoulos)\n\nVous pouvez [demander](https://www.iris-go.com/#ebookDonateForm) une version **E-Book** (en Anglais) de la documentation et contribuer au développement d'Iris.\n\n## 🙌 Contribuer\n\nToute contribution à Iris est la bienvenue ! Pour plus d'informations sur la contribution au projet référez-vous au fichier [CONTRIBUTING.md](CONTRIBUTING.md).\n\n[Liste des contributeurs](https://github.com/kataras/iris/graphs/contributors)\n\n## 🛡 Sécurité et vulnérabilités\n\nSi vous trouvez une vulnérabilité dans Iris, envoyez un e-mail à [iris-go@outlook.com](mailto:iris-go@outlook.com). Toute vulnérabilité sera corrigée aussi rapidement que possible.\n\n## 📝 Licence\n\nLe projet est sous licence [licence BSD 3](LICENSE), tout comme le langage Go lui même.\n\nLe nom \"Iris\" est inspiré de la mythologie Grecque.\n<!-- ## Stargazers over time\n\n[![Stargazers over time](https://starchart.cc/kataras/iris.svg)](https://starchart.cc/kataras/iris) -->\n"
  },
  {
    "path": "README_GR.md",
    "content": "# Iris Web Framework\n\n[![build status](https://img.shields.io/github/actions/workflow/status/kataras/iris/ci.yml?branch=main&style=for-the-badge)](https://github.com/kataras/iris/actions/workflows/ci.yml) [![FOSSA Status](https://img.shields.io/badge/LICENSE%20SCAN-PASSING❤️-CD2956?style=for-the-badge&logo=fossa)](https://app.fossa.io/projects/git%2Bgithub.com%2Fkataras%2Firis?ref=badge_shield)<!--[![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=for-the-badge)](https://goreportcard.com/report/github.com/kataras/iris)--><!--[![godocs](https://img.shields.io/badge/go-%20docs-488AC7.svg?style=for-the-badge)](https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.11)--> [![view examples](https://img.shields.io/badge/learn%20by-examples-0C8EC5.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/main/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=7E18DD&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community)<!--[![donate on PayPal](https://img.shields.io/badge/support-PayPal-blue.svg?style=for-the-badge)](https://iris-go.com/donate)--><!-- [![release](https://img.shields.io/badge/release%20-v12.0-0077b3.svg?style=for-the-badge)](https://github.com/kataras/iris/releases) -->\n\nΤο Iris είναι ένα γρήγορο, απλό αλλά και πλήρως λειτουργικό και πολύ αποδοτικό web framework για τη Go γλώσσα προγραμματισμού. Παρέχει ένα εκφραστικό και εύχρηστο υπόβαθρο για την επόμενη ιστοσελίδα σας.\n\nΜάθετε τι [λένε οι άλλοι για το Iris](https://iris-go.com/testimonials/) και δώστε ένα **αστεράκι** στο GitHub.\n\n[![](https://media.giphy.com/media/j5WLmtvwn98VPrm7li/giphy.gif)](https://iris-go.com/testimonials/)\n\n[![Benchmarks: Apr 2, 2020 at 12:13pm (UTC)](https://iris-go.com/static/images/benchmarks.svg)](https://github.com/kataras/server-benchmarks)\n\n## Μαθαίνοντας το Iris\n\n<details>\n<summary>Γρήγορο ξεκίνημα</summary>\n\n```sh\n# υποθέτοντας ότι ο παρακάτω κώδικας\n# βρίσκεται στο example.go αρχείο\n#\n$ cat example.go\n```\n\n```go\npackage main\n\nimport \"github.com/kataras/iris/v12\"\n\nfunc main() {\n    app := iris.Default()\n    app.Get(\"/ping\", func(ctx iris.Context) {\n        ctx.JSON(iris.Map{\n            \"message\": \"pong\",\n        })\n    })\n\n    app.Listen(\":8080\")\n}\n```\n\n```sh\n# τρέξτε το example.go και\n# επισκεφτείτε την σελίδα http://localhost:8080/ping\n# στο πρόγραμμα περιήγησης σας\n#\n$ go run example.go\n```\n\n> Η δρομολόγηση τροφοδοτείται από το [muxie](https://github.com/kataras/muxie), το πιο ισχυρό και ταχύτερο λογισμικό βασισμένο σε trie αλγόριθμο που γράφτηκε σε Go.\n\n</details>\n\nΤο Iris περιέχει εκτενείς και λεπτομερείς **[book](https://www.iris-go.com/#ebookDonateForm)** καθιστώντας το εύκολο στην εκμάθηση.\n\nΓια λεπτομερέστερη τεχνική τεκμηρίωση μπορείτε να κατευθυνθείτε προς τα [godocs](https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.11) μας. Και για εκτελέσιμο κώδικα μπορείτε πάντα να επισκέπτεστε τα [παραδείγματα](_examples/).\n\n### Σας αρέσει να διαβάζετε ενώ ταξιδεύετε;\n\nΜπορείτε να [ζητήσετε](https://www.iris-go.com/#ebookDonateForm) σήμερα την PDF έκδοση και την online πρόσβαση στο Ηλεκτρονικό μας **Βιβλίο(E-Book)** και να συμμετάσχετε στην ανάπτυξη του Iris.\n\n[![https://iris-go.com/static/images/iris-book-cover-sm.jpg](https://iris-go.com/static/images/iris-book-cover-sm.jpg)](https://www.iris-go.com/#ebookDonateForm)\n\n[![follow author](https://img.shields.io/twitter/follow/makismaropoulos.svg?style=for-the-badge)](https://twitter.com/intent/follow?screen_name=makismaropoulos)\n\n## Συνεισφορά\n\nΘα θέλαμε να δούμε τη συμβολή σας στο Iris Web Framework! Για περισσότερες πληροφορίες σχετικά με το πως μπορείτε να συμβάλετε, δείτε το [CONTRIBUTING.md](CONTRIBUTING.md) αρχείο.\n\n[Κατάλογος όλων των συνεισφορών](https://github.com/kataras/iris/graphs/contributors).\n\n## Αδυναμίες Ασφάλειας\n\nΕάν εντοπίσετε κάποια αδυναμία ασφαλείας του Iris, στείλτε ένα μήνυμα ηλεκτρονικού ταχυδρομείου στο [iris-go@outlook.com](mailto:iris-go@outlook.com). Όλες οι τυχών αδυναμίες ασφαλείας θα αντιμετωπιστούν άμεσα.\n\n## Άδεια Χρήσης\n\nΤο όνομα \"Iris\" εμπνεύστηκε από την ελληνική μυθολογία, συγκεκριμένα από την θεά Ίριδα.\n\nΤο Iris Web Framework είναι δωρεάν λογισμικό ανοιχτού λογισμικού με άδεια χρήσης [3-Clause BSD](LICENSE).\n"
  },
  {
    "path": "README_JA.md",
    "content": "<!--<h1><img width=\"24\" height=\"25\" src =\"https://www.iris-go.com/images/logo-new-lq-45.png\"/> News</h1>\n\n Iris version **12.2.0** has been [released](HISTORY.md#sa-11-march-2023--v1220)! As always, the latest version of Iris comes with the promise of lifetime active maintenance.\n\nTry the official [Iris Command Line Interface](https://github.com/kataras/iris-cli) today! -->\n\n# <a href=\"https://iris-go.com\"><img src=\"https://iris-go.com/iris-terminal-55.png\" width=\"50px\" height=\"50px\" style=\"margin-bottom: -5px\" ></a> Iris Web Framework <a href=\"README_GR.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-greece.svg\" /></a> <a href=\"README_FR.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-france.svg\" /></a> <a href=\"README_ZH_HANT.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-taiwan.svg\" /></a> <a href=\"README_ZH_HANS.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-china.svg\" /></a> <a href=\"README_ES.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-spain.png\" /></a> <a href=\"README_FA.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-iran.svg\" /></a> <a href=\"README_RU.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-russia.svg\" /></a> <a href=\"README_KO.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-south-korea.svg?v=12\" /></a> <a href=\"README_PT_BR.md\"><img width=\"20px\" height=\"20px\" src=\"https://iris-go.com/static/images/flag-brazil.svg\" /></a> <a href=\"README_JA.md\"><img width=\"20px\" height=\"20px\" src=\"https://iris-go.com/static/images/flag-japan.svg\" /></a>\n\n[![build status](https://img.shields.io/github/actions/workflow/status/kataras/iris/ci.yml?branch=main&style=for-the-badge)](https://github.com/kataras/iris/actions/workflows/ci.yml) [![view examples](https://img.shields.io/badge/examples%20-285-a83adf.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/main/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=cc2b5e&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community) <!--[![FOSSA Status](https://img.shields.io/badge/LICENSE%20SCAN-PASSING❤️-CD2956?style=for-the-badge&logo=fossa)](https://app.fossa.io/projects/git%2Bgithub.com%2Fkataras%2Firis?ref=badge_shield)--> [![donate](https://img.shields.io/badge/support-Iris-blue.svg?style=for-the-badge&logo=paypal)](https://iris-go.com/donate) <!--[![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=for-the-badge)](https://goreportcard.com/report/github.com/kataras/iris)--><!--[![godocs](https://img.shields.io/badge/go-%20docs-488AC7.svg?style=for-the-badge)](https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.11)--> <!-- [![release](https://img.shields.io/badge/release%20-v12.0-0077b3.svg?style=for-the-badge)](https://github.com/kataras/iris/releases) -->\n\nIrisは、高速でシンプルでありながら、十分な機能を備えた、非常に効率的なGo用Webフレームワークです。\n\nあなたの次のウェブサイトやAPIのために、美しく表現力豊かで使いやすい基盤を提供します。\n\n[Irisについての他の人々の意見](https://www.iris-go.com/#review)を学び、このオープンソースプロジェクトに **[スターをつけて](https://github.com/kataras/iris/stargazers)** 、その可能性を応援しましょう。\n\n[![](https://iris-go.com/static/images/reviews.gif)](https://iris-go.com/testimonials/)\n\n[![Benchmarks: Jul 18, 2020 at 10:46am (UTC)](https://iris-go.com/static/images/benchmarks.svg)](https://github.com/kataras/server-benchmarks)\n\n```go\npackage main\n\nimport \"github.com/kataras/iris/v12\"\n\nfunc main() {\n  app := iris.New()\n  app.Use(iris.Compression)\n\n  app.Get(\"/\", func(ctx iris.Context) {\n    ctx.HTML(\"Hello <strong>%s</strong>!\", \"World\")\n  })\n\n  app.Listen(\":8080\")\n}\n```\n\n<!-- <details><summary>More with simple Handler</summary>\n\n```go\npackage main\n\nimport \"github.com/kataras/iris/v12\"\n\ntype (\n  request struct {\n    Firstname string `json:\"firstname\"`\n    Lastname  string `json:\"lastname\"`\n  }\n\n  response struct {\n    ID      string `json:\"id\"`\n    Message string `json:\"message\"`\n  }\n)\n\nfunc main() {\n  app := iris.New()\n  app.Handle(\"PUT\", \"/users/{id:uuid}\", updateUser)\n  app.Listen(\":8080\")\n}\n\nfunc updateUser(ctx iris.Context) {\n  id := ctx.Params().Get(\"id\")\n\n  var req request\n  if err := ctx.ReadJSON(&req); err != nil {\n    ctx.StopWithError(iris.StatusBadRequest, err)\n    return\n  }\n\n  resp := response{\n    ID:      id,\n    Message: req.Firstname + \" updated successfully\",\n  }\n  ctx.JSON(resp)\n}\n```\n\n> Read the [routing examples](https://github.com/kataras/iris/blob/main/_examples/routing) for more!\n\n</details>\n\n<details><summary>Handler with custom input and output arguments</summary>\n\n[![https://github.com/kataras/iris/blob/main/_examples/dependency-injection/basic/main.go](https://user-images.githubusercontent.com/22900943/105253731-b8db6d00-5b88-11eb-90c1-0c92a5581c86.png)](https://twitter.com/iris_framework/status/1234783655408668672)\n\n> Interesting? Read the [examples](https://github.com/kataras/iris/blob/main/_examples/dependency-injection).\n\n</details>\n\n<details><summary>Party Controller (NEW)</summary>\n\n> Head over to the [full running example](https://github.com/kataras/iris/blob/main/_examples/routing/party-controller)!\n\n</details>\n\n<details><summary>MVC</summary>\n\n```go\npackage main\n\nimport (\n  \"github.com/kataras/iris/v12\"\n  \"github.com/kataras/iris/v12/mvc\"\n)\n\ntype (\n  request struct {\n    Firstname string `json:\"firstname\"`\n    Lastname  string `json:\"lastname\"`\n  }\n\n  response struct {\n    ID      uint64 `json:\"id\"`\n    Message string `json:\"message\"`\n  }\n)\n\nfunc main() {\n  app := iris.New()\n  mvc.Configure(app.Party(\"/users\"), configureMVC)\n  app.Listen(\":8080\")\n}\n\nfunc configureMVC(app *mvc.Application) {\n  app.Handle(new(userController))\n}\n\ntype userController struct {\n  // [...dependencies]\n}\n\nfunc (c *userController) PutBy(id uint64, req request) response {\n  return response{\n    ID:      id,\n    Message: req.Firstname + \" updated successfully\",\n  }\n}\n```\n\nWant to see more? Navigate through [mvc examples](_examples/mvc)!\n</details>\n\n\n<details><summary>API Guide <strong>HOT</strong></summary>\n\n```go\npackage main\n\nimport (\n  // [other packages...]\n\n  \"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n  iris.NewGuide().\n    AllowOrigin(\"*\").\n    Compression(true).\n    Health(true, \"development\", \"kataras\").\n    Timeout(0, 20*time.Second, 20*time.Second).\n    Middlewares(basicauth.New(...)).\n    Services(\n        // NewDatabase(),\n        // NewPostgresRepositoryRegistry,\n        // NewUserService,\n    ).\n    API(\"/users\", new(UsersAPI)).\n    Listen(\":80\")\n}\n```\n\n</details>\n\n<br/>\n\n-->\n\nある[Go開発者](https://twitter.com/dkuye/status/1532087942696554497)が言ったように、 **Irisはあなたをあらゆる面でサポートし、長年にわたって力強さを保ち続けています** 。\n\nIrisが提供する機能の一部:\n\n* HTTP/2 (Push, Embedded data)\n* Middleware (Accesslog, Basicauth, CORS, gRPC, Anti-Bot hCaptcha, JWT, MethodOverride, ModRevision, Monitor, PPROF, Ratelimit, Anti-Bot reCaptcha, Recovery, RequestID, Rewrite)\n* API バージョニング\n* Model-View-Controller\n* Websockets\n* gRPC\n* Auto-HTTPS\n* ngrokの組み込みサポートにより、最速の方法でアプリをインターネットに公開できる\n* :uuid、:string、:int のような標準的な型を持つダイナミック・パスをパラメータとするユニークなルーター\n* Compression\n* View Engines (HTML, Django, Handlebars, Pug/Jade and more)\n* 独自のファイルサーバーを作成し、WebDAVサーバーをホストする\n* Cache\n* Localization (i18n, sitemap)\n* Sessions\n* 豊富な Response (HTML, Text, Markdown, XML, YAML, Binary, JSON, JSONP, Protocol Buffers, MessagePack, Content Negotiation, Streaming, Server-Sent Events など)\n* Response Compression (gzip, deflate, brotli, snappy, s2)\n* 豊富な Requests (Bind URL Query, Headers, Form, Text, XML, YAML, Binary, JSON, Validation, Protocol Buffers, MessagePack など)\n* Dependency Injection (MVC, Handlers, API Routers)\n* Testing Suite\n* そして最も重要なのは、初日から現在に至るまで、つまり丸6年間、迅速な回答とサポートを受けられることです！\n\n## 👑 <a href=\"https://iris-go.com/donate\">サポーター</a>\n\n皆様のご協力により、オープンソース・ウェブ開発をより良いものにすることができます！\n\n## 📖 Irisを学ぶ\n\n### インストール\n\n必要なのは [Goプログラミング言語](https://go.dev/dl/) だけです。\n\n#### 新規プロジェクトの作成\n\n```sh\n$ mkdir myapp\n$ cd myapp\n$ go mod init myapp\n$ go get github.com/kataras/iris/v12@latest # or @v12.2.11\n```\n\n<details><summary>既存のプロジェクトにインストールする場合</summary>\n\n```sh\n$ cd myapp\n$ go get github.com/kataras/iris/v12@latest\n```\n\n**実行**\n\n```sh\n$ go mod tidy -compat=1.20 # -compat=\"1.20\" for windows.\n$ go run .\n```\n\n</details>\n\n![](https://www.iris-go.com/images/gifs/install-create-iris.gif)\n\nIris には広範で詳細な **[ドキュメント](https://www.iris-go.com/docs)** が含まれているので、フレームワークを簡単に使い始めることができます。\n\n<!-- Iris contains extensive and thorough **[wiki](https://github.com/kataras/iris/wiki)** making it easy to get started with the framework. -->\n\n<!-- ![](https://media.giphy.com/media/Ur8iqy9FQfmPuyQpgy/giphy.gif) -->\n\nより詳細な技術文書については [godocs](https://pkg.go.dev/github.com/kataras/iris/v12@main) をご覧ください。また、実行可能なコードについては、いつでもリポジトリのサブディレクトリ  [./_examples](_examples)  にアクセスできます。\n\n### 旅行中に本を読むのは好きですか？\n\n<a href=\"https://iris-go.com/#book\"> <img alt=\"Book cover\" src=\"https://iris-go.com/static/images/iris-book-cover-sm.jpg?v=12\" /> </a>\n\n[![follow author on twitter](https://img.shields.io/twitter/follow/makismaropoulos?color=3D8AA3&logoColor=3D8AA3&style=for-the-badge&logo=twitter)](https://twitter.com/intent/follow?screen_name=makismaropoulos)\n\n[![follow Iris web framework on twitter](https://img.shields.io/twitter/follow/iris_framework?color=ee7506&logoColor=ee7506&style=for-the-badge&logo=twitter)](https://twitter.com/intent/follow?screen_name=iris_framework)\n\n[![follow Iris web framework on facebook](https://img.shields.io/badge/Follow%20%40Iris.framework-569-2D88FF.svg?style=for-the-badge&logo=facebook)](https://www.facebook.com/iris.framework)\n\n**Iris E-Book**（新版、**将来のv12.2.0+**）のPDFとオンライン・アクセスを今すぐ [リクエスト](https://www.iris-go.com/#ebookDonateForm) して、Irisの開発に参加してください。\n\n## 🙌 貢献する\n\nIrisウェブ・フレームワークへの貢献をお待ちしています！Iris プロジェクトへの貢献についての詳細は、 [CONTRIBUTING.md](CONTRIBUTING.md) ファイルをご覧ください。\n\n[全貢献者のリスト](https://github.com/kataras/iris/graphs/contributors)\n\n## 🛡 セキュリティの脆弱性\n\nIris にセキュリティ上の脆弱性を発見した場合は、 [iris-go@outlook.com](mailto:iris-go@outlook.com) にメールを送ってください。すべてのセキュリティ脆弱性は、速やかに対処されます。\n\n## 📝 ライセンス\n\nこのプロジェクトのライセンスは、Goプロジェクトと同様、 [BSD 3-clause license](LICENSE) です。\n\nプロジェクト名の \"Iris\" はギリシャ神話からインスピレーションを得たものです。\n\n<!-- ## Stargazers over time\n\n[![Stargazers over time](https://starchart.cc/kataras/iris.svg)](https://starchart.cc/kataras/iris) -->\n"
  },
  {
    "path": "README_KO.md",
    "content": "# Iris Web Framework\n\n[![build status](https://img.shields.io/github/actions/workflow/status/kataras/iris/ci.yml?branch=main&style=for-the-badge)](https://github.com/kataras/iris/actions/workflows/ci.yml) [![FOSSA Status](https://img.shields.io/badge/LICENSE%20SCAN-PASSING❤️-CD2956?style=for-the-badge&logo=fossa)](https://app.fossa.io/projects/git%2Bgithub.com%2Fkataras%2Firis?ref=badge_shield)<!--[![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=for-the-badge)](https://goreportcard.com/report/github.com/kataras/iris)--><!--[![godocs](https://img.shields.io/badge/go-%20docs-488AC7.svg?style=for-the-badge)](https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.11)--> [![view examples](https://img.shields.io/badge/learn%20by-examples-0C8EC5.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/main/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=7E18DD&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community)<!--[![donate on PayPal](https://img.shields.io/badge/support-PayPal-blue.svg?style=for-the-badge)](https://iris-go.com/donate)--><!-- [![release](https://img.shields.io/badge/release%20-v12.0-0077b3.svg?style=for-the-badge)](https://github.com/kataras/iris/releases) -->\n\nIris는 단순하고 빠르며 좋은 성능과 모든 기능을 갖춘 Go언어용 웹 프레임워크입니다. 당신의 웹사이트나 API를 위해서 아름답고 사용하기 쉬운 기반을 제공합니다.\n\n[여러 사람들의 의견](https://iris-go.com/testimonials/)을 둘러보세요. 그리고 이 github repository을 **star**하세요.\n\n[![](https://media.giphy.com/media/j5WLmtvwn98VPrm7li/giphy.gif)](https://iris-go.com/testimonials/)\n\n[![Benchmarks: Apr 2, 2020 at 12:13pm (UTC)](https://iris-go.com/static/images/benchmarks.svg)](https://github.com/kataras/server-benchmarks)\n\n## Iris 배우기\n\n<details>\n<summary>일단 해보기</summary>\n\n```sh\n# 다음 코드를 example.go 화일에 입력하세요.\n$ cat example.go\n```\n\n```go\npackage main\n\nimport \"github.com/kataras/iris/v12\"\n\nfunc main() {\n    app := iris.Default()\n    app.Get(\"/ping\", func(ctx iris.Context) {\n        ctx.JSON(iris.Map{\n            \"message\": \"pong\",\n        })\n    })\n\n    app.Listen(\":8080\")\n}\n```\n\n```sh\n# example.go 를 실행하고,\n# 웹브라우저에서 http://localhost:8080/ping 를 열어보세요.\n$ go run example.go\n```\n\n> 라우팅은 Go로 작성한 가장 강력하고 빠른 trie기반의 소프트웨어인 [muxie](https://github.com/kataras/muxie)로 처리합니다.\n\n</details>\n\nIris는 광범위하고 꼼꼼한 **[wiki](https://www.iris-go.com/#ebookDonateForm)** 를 가지고 있기 때문에 쉽게 프레임워크를 시작할 수 있습니다.\n\n더 자세한 기술문서를 보시려면 [godocs](https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.11)를 방문하세요. 그리고 실행가능한 예제코드는 [\\_examples](_examples/) 하위 디렉토리에 있습니다.\n\n### 여행하면서 독서를 즐기세요?\n\n<a href=\"https://iris-go.com/#book\"> <img alt=\"Book cover\" src=\"https://iris-go.com/static/images/iris-book-cover-sm.jpg?v=12\"/> </a>\n\n[![follow author](https://img.shields.io/twitter/follow/makismaropoulos.svg?style=for-the-badge)](https://twitter.com/intent/follow?screen_name=makismaropoulos)\n\nPDF 버전과 **E-Book** 에 대한 온라인 접근을 [요청](https://www.iris-go.com/#ebookDonateForm)하시고 Iris의 개발에 참가하실 수 있습니다.\n\n## 기여하기\n\nIris 웹 프레임워크에 대한 여러분의 기여를 환영합니다! Iris 프로젝트에 기여하는 방법에 대한 자세한 내용은 [CONTRIBUTING.md](CONTRIBUTING.md) 파일을 참조하십시오.\n\n[기여자 리스트](https://github.com/kataras/iris/graphs/contributors)\n\n## 보안 취약점\n\n만약 Iris에서 보안 취약점을 발견하시면 [iris-go@outlook.com](mailto:iris-go@outlook.com) 로 메일을 보내주세요. 모든 보안 취약점은 즉 해결할 것입니다.\n\n## 라이센스\n\n이 프로젝트의 이름 \"Iris\"는 그리스 신화에서 영감을 받았습니다.\n\nIris 웹 프레임워크는 [3-Clause BSD License](LICENSE)를 가지는 무료 오픈소스 소프트웨어입니다.\n"
  },
  {
    "path": "README_PT_BR.md",
    "content": "<!-- [![Black Lives Matter](https://iris-go.com/static/images/blacklivesmatter_banner.png)](https://support.eji.org/give/153413/#!/donation/checkout)-->\n\n# Iris Web Framework <a href=\"README_GR.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-greece.svg\" /></a> <a href=\"README_FR.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-france.svg\" /></a> <a href=\"README_ZH.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-china.svg\" /></a> <a href=\"README_ES.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-spain.png\" /></a> <a href=\"README_FA.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-iran.svg\" /></a> <a href=\"README_RU.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-russia.svg\" /></a> <a href=\"README_KO.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-south-korea.svg?v=12\" /></a> <a href=\"README_PT_BR.md\"><img width=\"20px\" align=\"center\" src=\"https://www.iris-go.com/images/flag-brazil.svg\" /></a> <a href=\"README_JA.md\"><img width=\"20px\" height=\"20px\" src=\"https://iris-go.com/static/images/flag-japan.svg\" /></a>\n\n[![build status](https://img.shields.io/github/actions/workflow/status/kataras/iris/ci.yml?branch=main&style=for-the-badge)](https://github.com/kataras/iris/actions/workflows/ci.yml) [![view examples](https://img.shields.io/badge/examples%20-270-a83adf.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/main/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=cc2b5e&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community) <!--[![FOSSA Status](https://img.shields.io/badge/LICENSE%20SCAN-PASSING❤️-CD2956?style=for-the-badge&logo=fossa)](https://app.fossa.io/projects/git%2Bgithub.com%2Fkataras%2Firis?ref=badge_shield)--> [![donate](https://img.shields.io/badge/support-Iris-blue.svg?style=for-the-badge&logo=paypal)](https://iris-go.com/donate) <!--[![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=for-the-badge)](https://goreportcard.com/report/github.com/kataras/iris)--><!--[![godocs](https://img.shields.io/badge/go-%20docs-488AC7.svg?style=for-the-badge)](https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.11)--> <!-- [![release](https://img.shields.io/badge/release%20-v12.0-0077b3.svg?style=for-the-badge)](https://github.com/kataras/iris/releases) -->\n\n<!-- <a href=\"https://iris-go.com\"> <img align=\"right\" src=\"https://iris-go.com/static/images/logo-w169.png\"></a> -->\n\nIris é um framework web rápido, simples, mas completo e muito eficiente para Go.\n\nEle fornece uma base lindamente expressiva e fácil de usar para seu próximo site ou API.\n\n\n```go\npackage main\n\nimport \"github.com/kataras/iris/v12\"\n\nfunc main() {\n  app := iris.New()\n  app.Use(iris.Compression)\n\n  app.Get(\"/\", func(ctx iris.Context) {\n    ctx.HTML(\"Hello <strong>%s</strong>!\", \"World\")\n  })\n\n  app.Listen(\":8080\")\n}\n```\n\n<!-- <details><summary>More with simple Handler</summary>\n\n```go\npackage main\n\nimport \"github.com/kataras/iris/v12\"\n\ntype (\n  request struct {\n    Firstname string `json:\"firstname\"`\n    Lastname  string `json:\"lastname\"`\n  }\n\n  response struct {\n    ID      string `json:\"id\"`\n    Message string `json:\"message\"`\n  }\n)\n\nfunc main() {\n  app := iris.New()\n  app.Handle(\"PUT\", \"/users/{id:uuid}\", updateUser)\n  app.Listen(\":8080\")\n}\n\nfunc updateUser(ctx iris.Context) {\n  id := ctx.Params().Get(\"id\")\n\n  var req request\n  if err := ctx.ReadJSON(&req); err != nil {\n    ctx.StopWithError(iris.StatusBadRequest, err)\n    return\n  }\n\n  resp := response{\n    ID:      id,\n    Message: req.Firstname + \" updated successfully\",\n  }\n  ctx.JSON(resp)\n}\n```\n\n> Read the [routing examples](https://github.com/kataras/iris/blob/main/_examples/routing) for more!\n\n</details>\n\n<details><summary>Handler with custom input and output arguments</summary>\n\n[![https://github.com/kataras/iris/blob/main/_examples/dependency-injection/basic/main.go](https://user-images.githubusercontent.com/22900943/105253731-b8db6d00-5b88-11eb-90c1-0c92a5581c86.png)](https://twitter.com/iris_framework/status/1234783655408668672)\n\n> Interesting? Read the [examples](https://github.com/kataras/iris/blob/main/_examples/dependency-injection).\n\n</details>\n\n<details><summary>Party Controller (NEW)</summary>\n\n> Head over to the [full running example](https://github.com/kataras/iris/blob/main/_examples/routing/party-controller)!\n\n</details>\n\n<details><summary>MVC</summary>\n\n```go\npackage main\n\nimport (\n  \"github.com/kataras/iris/v12\"\n  \"github.com/kataras/iris/v12/mvc\"\n)\n\ntype (\n  request struct {\n    Firstname string `json:\"firstname\"`\n    Lastname  string `json:\"lastname\"`\n  }\n\n  response struct {\n    ID      uint64 `json:\"id\"`\n    Message string `json:\"message\"`\n  }\n)\n\nfunc main() {\n  app := iris.New()\n  mvc.Configure(app.Party(\"/users\"), configureMVC)\n  app.Listen(\":8080\")\n}\n\nfunc configureMVC(app *mvc.Application) {\n  app.Handle(new(userController))\n}\n\ntype userController struct {\n  // [...dependencies]\n}\n\nfunc (c *userController) PutBy(id uint64, req request) response {\n  return response{\n    ID:      id,\n    Message: req.Firstname + \" updated successfully\",\n  }\n}\n```\n\nWant to see more? Navigate through [mvc examples](_examples/mvc)!\n</details>\n\n\n<details><summary>API Guide <strong>HOT</strong></summary>\n\n```go\npackage main\n\nimport (\n  // [other packages...]\n\n  \"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n  iris.NewGuide().\n    AllowOrigin(\"*\").\n    Compression(true).\n    Health(true, \"development\", \"kataras\").\n    Timeout(0, 20*time.Second, 20*time.Second).\n    Middlewares(basicauth.New(...)).\n    Services(\n        // NewDatabase(),\n        // NewPostgresRepositoryRegistry,\n        // NewUserService,\n    ).\n    API(\"/users\", new(UsersAPI)).\n    Listen(\":80\")\n}\n```\n\n</details>\n\n<br/>\n\n-->\n\nComo um [Desenvolvedor Go](https://twitter.com/dkuye/status/1532087942696554497) disse uma vez, **Iris abrangeu tudo e se manteve forte ao longo dos anos**.\n\nAlguns dos recursos que o Iris Web Framework oferece:\n\n* HTTP/2 (Push, mesmo para dados incorporados)\n* Middleware (Accesslog, Basicauth, CORS, gRPC, Anti-Bot hCaptcha, JWT, MethodOverride, ModRevision, Monitor, PPROF, Ratelimit, Anti-Bot reCaptcha, Recovery, RequestID, Rewrite)\n* Versionamento de API\n* Model-View-Controller\n* Websockets\n* gRPC\n* Auto-HTTPS\n* Suporte integrado para ngrok para colocar seu aplicativo na internet da maneira mais rápida\n* Router único com caminho dinâmico como parametro com tipos padrões como :uuid, :string, :int... e a habilidade de criar o seu próprio router\n* Compressão\n* View Engines (HTML, Django, Handlebars, Pug/Jade e mais)\n* Cria seu próprio Servidor de Arquivo e hospeda seu próprio servidor WebDAV\n* Cache\n* Localização (i18n, sitemap)\n* Sessões\n* Respostas Ricas (HTML, Text, Markdown, XML, YAML, Binary, JSON, JSONP, Protocol Buffers, MessagePack, Content Negotiation, Streaming, Server-Sent Events e mais)\n* Compressão de resposta (gzip, deflate, brotli, snappy, s2)\n* Requisições Ricas (Bind URL Query, Headers, Form, Text, XML, YAML, Binary, JSON, Validation, Protocol Buffers, MessagePack e mais)\n* Injeção de dependência (MVC, Handlers, API Routers)\n* Suite de testes\n* E o mais importante... você obtém respostas rápidas e suporte desde o 1º dia até agora - são seis anos completos!\n\nAprenda com [o que os outros falam sobre Iris](https://www.iris-go.com/#review) e **[marque com uma estrela](https://github.com/kataras/iris/stargazers)** esse projeto de código aberto para apoiar o seu potencial.\n\n[![](https://iris-go.com/static/images/reviews.gif)](https://iris-go.com/testimonials/)\n\n[![Benchmarks: Jul 18, 2020 at 10:46am (UTC)](https://iris-go.com/static/images/benchmarks.svg)](https://github.com/kataras/server-benchmarks)\n\n## 👑 <a href=\"https://iris-go.com/donate\">Apoiadores</a>\n\nCom a sua ajuda, nós podemos melhorar o desenvolvimento web de Código Aberto para todos !\n\n> [@github](https://github.com/github) agora está patrocinando você por $550,00 uma vez.\n>\n> Uma nota do seu novo patrocinador: \n>\n> Para comemorar o Mês do Mantenedor, queremos agradecer por tudo que você faz pela comunidade de código aberto. Confira nossa postagem no blog para saber mais sobre como o GitHub está investindo em mantenedores\n\n> Doações direto da [China](https://github.com/kataras/iris/issues/1870#issuecomment-1101418349) agora são aceitas!\n\n## 📖 Aprenda sobre o Iris Web Framework\n\n### Instalação\n\nO único requisito é a [Linguagem de programação Go](https://go.dev/dl/).\n\n#### Criar um novo projeto\n\n```sh\n$ mkdir myapp\n$ cd myapp\n$ go mod init myapp\n$ go get github.com/kataras/iris/v12@latest # or @v12.2.11\n```\n\n<details><summary>Instalar num projeto existente</summary>\n\n```sh\n$ cd myapp\n$ go get github.com/kataras/iris/v12@latest\n```\n\n**Run**\n\n```sh\n$ go mod tidy -compat=1.20 # -compat=\"1.20\" for windows.\n$ go run .\n```\n\n</details>\n\n![](https://www.iris-go.com/images/gifs/install-create-iris.gif)\n\nIris contém extensa e completa **[documentação](https://www.iris-go.com/docs)**, o que torna fácil o começo com o framework.\n\n<!-- Iris contains extensive and thorough **[wiki](https://github.com/kataras/iris/wiki)** making it easy to get started with the framework. -->\n\n<!-- ![](https://media.giphy.com/media/Ur8iqy9FQfmPuyQpgy/giphy.gif) -->\n\nPara obter uma documentação técnica mais detalhada, você pode acessar nosso [godocs](https://pkg.go.dev/github.com/kataras/iris/v12@main). E para executar o código você sempre pode visitar os subdiretórios do diretório [./_examples](_examples).\n\n### Você gosta de ler enquanto viaja ?\n\n<a href=\"https://iris-go.com/#book\"> <img alt=\"Book cover\" src=\"https://iris-go.com/static/images/iris-book-cover-sm.jpg?v=12\" /> </a>\n\n[![follow author on twitter](https://img.shields.io/twitter/follow/makismaropoulos?color=3D8AA3&logoColor=3D8AA3&style=for-the-badge&logo=twitter)](https://twitter.com/intent/follow?screen_name=makismaropoulos)\n\n[![follow Iris web framework on twitter](https://img.shields.io/twitter/follow/iris_framework?color=ee7506&logoColor=ee7506&style=for-the-badge&logo=twitter)](https://twitter.com/intent/follow?screen_name=iris_framework)\n\n[![follow Iris web framework on facebook](https://img.shields.io/badge/Follow%20%40Iris.framework-569-2D88FF.svg?style=for-the-badge&logo=facebook)](https://www.facebook.com/iris.framework)\n\nVocê pode [solicitar](https://www.iris-go.com/#ebookDonateForm) o acesso ao **Iris E-Book** de forma online e também no formato PDF (Nova edição, **future v12.2.0+**) hoje today e se antecipar no desenvolvimento do Iris.\n\n## 🙌 Contribuidores\n\nAdoraríamos ver sua contribuição para o Iris Web Framework! Para mais informações sobre como contribuir com o projeto Iris, consulte o arquivo [CONTRIBUTING.md](CONTRIBUTING.md).\n\n[Lista de todos os Contribuidores](https://github.com/kataras/iris/graphs/contributors)\n\n## 🛡Vulnerabilidades de segurança\n\nSe você descobrir alguma vulnerabilidade de segurança dentro do Iris, por favor, envie um email para [iris-go@outlook.com](mailto:iris-go@outlook.com). Todas as vulnerabilidades de segurança serão prontamente tratadas.\n\n## 📝 Licença\nEste projeto está licenciado sob a [Licença BSD 3-clause](LICENSE), assim como o próprio projeto Go.\n\nO nome do projeto \"Iris\" foi inspirado pela mitologia Grega.\n<!-- ## Stargazers over time\n\n[![Stargazers over time](https://starchart.cc/kataras/iris.svg)](https://starchart.cc/kataras/iris) -->\n"
  },
  {
    "path": "README_RU.md",
    "content": "# Iris Web Framework\n\n[![build status](https://img.shields.io/github/actions/workflow/status/kataras/iris/ci.yml?branch=main&style=for-the-badge)](https://github.com/kataras/iris/actions/workflows/ci.yml) [![FOSSA Status](https://img.shields.io/badge/LICENSE%20SCAN-PASSING❤️-CD2956?style=for-the-badge&logo=fossa)](https://app.fossa.io/projects/git%2Bgithub.com%2Fkataras%2Firis?ref=badge_shield)<!--[![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=for-the-badge)](https://goreportcard.com/report/github.com/kataras/iris)--><!--[![godocs](https://img.shields.io/badge/go-%20docs-488AC7.svg?style=for-the-badge)](https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.11)--> [![view examples](https://img.shields.io/badge/learn%20by-examples-0C8EC5.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/main/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=7E18DD&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community)<!--[![donate on PayPal](https://img.shields.io/badge/support-PayPal-blue.svg?style=for-the-badge)](https://iris-go.com/donate)--><!-- [![release](https://img.shields.io/badge/release%20-v12.0-0077b3.svg?style=for-the-badge)](https://github.com/kataras/iris/releases) -->\nIris — это быстрый, простой, но полнофункциональный и эффективный веб-фреймворк для Go. Он обеспечивает красивую, выразительную и простую в использовании основу для вашего следующего веб-сайта или API.\n\nУзнайте, что [говорят другие люди об Iris](https://iris-go.com/testimonials/) и поставьте **[звёздочку](https://github.com/kataras/iris/stargazers)** этому проекту с открытым исходным кодом, чтобы поддержать его потенциал.\n\n[![](https://media.giphy.com/media/j5WLmtvwn98VPrm7li/giphy.gif)](https://iris-go.com/testimonials/)\n\n[![Benchmarks: Apr 2, 2020 at 12:13pm (UTC)](https://iris-go.com/static/images/benchmarks.svg)](https://github.com/kataras/server-benchmarks)\n\n## Изучение Iris\n\n<details>\n<summary>Быстрый старт</summary>\n\n```sh\n# например, код в файле example.go будет таким:\n$ cat example.go\n```\n\n```go\npackage main\n\nimport \"github.com/kataras/iris/v12\"\n\nfunc main() {\n    app := iris.Default()\n    app.Get(\"/ping\", func(ctx iris.Context) {\n        ctx.JSON(iris.Map{\n            \"message\": \"pong\",\n        })\n    })\n\n    app.Listen(\":8080\")\n}\n```\n\n```sh\n# запустите example.go и перейдите в браузер\n# по адресу http://localhost:8080/ping\n$ go run example.go\n```\n\n> Система роутинга запросов работает на [muxie](https://github.com/kataras/muxie), мощное и быстрое trie-based ПО, написанное на Go.\n\n</details>\n\nУ Iris есть исчерпывающий и тщательный **[wiki](https://www.iris-go.com/#ebookDonateForm)**, который позволит вам быстрее начать работу с фреймворком.\n\n<!-- ![](https://media.giphy.com/media/Ur8iqy9FQfmPuyQpgy/giphy.gif) -->\n\nДля получения более подробной технической документации вы можете обратиться к нашему [godoc](https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.11). А для живых примеров кода — вы всегда можете посетить [\\_examples](_examples/) в поддиректории этого репозитория.\n\n### Вы любите читать во время путешествий?\n\n<a href=\"https://iris-go.com/#book\"> <img alt=\"Book cover\" src=\"https://iris-go.com/static/images/iris-book-cover-sm.jpg?v=12\" /> </a>\n\n<!-- [![follow author](https://img.shields.io/twitter/follow/makismaropoulos.svg?style=for-the-badge)](https://twitter.com/intent/follow?screen_name=makismaropoulos) -->\n\nВы можете [запросить](https://www.iris-go.com/#ebookDonateForm) PDF версию и онлайн-доступ к **E-Book** сегодня и принять участие в разработке Iris.\n\n## Содействие\n\nМы будем рады видеть ваш вклад в веб-фреймворк Iris! Для получения дополнительной информации о содействии проекту Iris, пожалуйста, проверьте файл [CONTRIBUTING.md](CONTRIBUTING.md).\n\n[Список всех участников](https://github.com/kataras/iris/graphs/contributors)\n\n## Уязвимость безопасности\n\nЕсли вы обнаружите уязвимость безопасности в Iris, отправьте электронное письмо по адресу [iris-go@outlook.com](mailto:iris-go@outlook.com). Все уязвимости безопасности будут оперативно устранены.\n\n## Лицензия\n\nНазвание проекта «Iris» было вдохновлено греческой мифологией.\n\nВеб-фреймворк Iris — это ПО с открытым исходным кодом под лицензией [3-Clause BSD License](LICENSE).\n\n## Накопление звёзд со временем\n\n[![Stargazers over time](https://starchart.cc/kataras/iris.svg)](https://starchart.cc/kataras/iris)\n"
  },
  {
    "path": "README_VN.md",
    "content": "<!--<h1><img width=\"24\" height=\"25\" src =\"https://www.iris-go.com/images/logo-new-lq-45.png\"/> News</h1>\n\n Iris version **12.2.0** has been [released](HISTORY.md#sa-11-march-2023--v1220)! As always, the latest version of Iris comes with the promise of lifetime active maintenance.\n\nTry the official [Iris Command Line Interface](https://github.com/kataras/iris-cli) today! -->\n\n# <a href=\"https://iris-go.com\"><img src=\"https://iris-go.com/iris-terminal-55.png\" width=\"50px\" height=\"50px\" style=\"margin-bottom: -5px\" ></a> Iris Web Framework <a href=\"README_GR.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-greece.svg\" /></a> <a href=\"README_FR.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-france.svg\" /></a> <a href=\"README_ZH_HANT.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-taiwan.svg\" /></a> <a href=\"README_ZH_HANS.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-china.svg\" /></a> <a href=\"README_ES.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-spain.png\" /></a> <a href=\"README_FA.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-iran.svg\" /></a> <a href=\"README_RU.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-russia.svg\" /></a> <a href=\"README_KO.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-south-korea.svg?v=12\" /></a> <a href=\"README_PT_BR.md\"><img width=\"20px\" height=\"20px\" src=\"https://iris-go.com/static/images/flag-brazil.svg\" /></a>\n\n[![build status](https://img.shields.io/github/actions/workflow/status/kataras/iris/ci.yml?branch=main&style=for-the-badge)](https://github.com/kataras/iris/actions/workflows/ci.yml) [![view examples](https://img.shields.io/badge/examples%20-285-a83adf.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/main/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=cc2b5e&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community) <!--[![FOSSA Status](https://img.shields.io/badge/LICENSE%20SCAN-PASSING❤️-CD2956?style=for-the-badge&logo=fossa)](https://app.fossa.io/projects/git%2Bgithub.com%2Fkataras%2Firis?ref=badge_shield)--> [![donate](https://img.shields.io/badge/support-Iris-blue.svg?style=for-the-badge&logo=paypal)](https://iris-go.com/donate) <!--[![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=for-the-badge)](https://goreportcard.com/report/github.com/kataras/iris)--><!--[![godocs](https://img.shields.io/badge/go-%20docs-488AC7.svg?style=for-the-badge)](https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.11)--> <!-- [![release](https://img.shields.io/badge/release%20-v12.0-0077b3.svg?style=for-the-badge)](https://github.com/kataras/iris/releases) -->\n\nIris là một khung web nhanh, đơn giản nhưng đầy đủ tính năng và rất hiệu quả dành cho Go.\n\nNó cung cấp một nền tảng đẹp mắt và dễ sử dụng cho trang web hoặc API tiếp theo của bạn.\n\n\nTìm hiểu xem [những người khác nói gì về Iris](https://www.iris-go.com/#review) và **[gắn sao](https://github.com/kataras/iris/stargazers)** dự án mã nguồn mở này để phát huy tiềm năng của nó.\n\n[![](https://iris-go.com/static/images/reviews.gif)](https://iris-go.com/testimonials/)\n\n[![Benchmarks: Jul 18, 2020 at 10:46am (UTC)](https://iris-go.com/static/images/benchmarks.svg)](https://github.com/kataras/server-benchmarks)\n\n```go\npackage main\n\nimport \"github.com/kataras/iris/v12\"\n\nfunc main() {\n  app := iris.New()\n  app.Use(iris.Compression)\n\n  app.Get(\"/\", func(ctx iris.Context) {\n    ctx.HTML(\"Xin chào <strong>%s</strong>!\", \"Thế Giới\")\n  })\n\n  app.Listen(\":8080\")\n}\n```\n\n<!-- <details><summary>More with simple Handler</summary>\n\n```go\npackage main\n\nimport \"github.com/kataras/iris/v12\"\n\ntype (\n  request struct {\n    Firstname string `json:\"firstname\"`\n    Lastname  string `json:\"lastname\"`\n  }\n\n  response struct {\n    ID      string `json:\"id\"`\n    Message string `json:\"message\"`\n  }\n)\n\nfunc main() {\n  app := iris.New()\n  app.Handle(\"PUT\", \"/users/{id:uuid}\", updateUser)\n  app.Listen(\":8080\")\n}\n\nfunc updateUser(ctx iris.Context) {\n  id := ctx.Params().Get(\"id\")\n\n  var req request\n  if err := ctx.ReadJSON(&req); err != nil {\n    ctx.StopWithError(iris.StatusBadRequest, err)\n    return\n  }\n\n  resp := response{\n    ID:      id,\n    Message: req.Firstname + \" updated successfully\",\n  }\n  ctx.JSON(resp)\n}\n```\n\n> Read the [routing examples](https://github.com/kataras/iris/blob/main/_examples/routing) for more!\n\n</details>\n\n<details><summary>Handler with custom input and output arguments</summary>\n\n[![https://github.com/kataras/iris/blob/main/_examples/dependency-injection/basic/main.go](https://user-images.githubusercontent.com/22900943/105253731-b8db6d00-5b88-11eb-90c1-0c92a5581c86.png)](https://twitter.com/iris_framework/status/1234783655408668672)\n\n> Interesting? Read the [examples](https://github.com/kataras/iris/blob/main/_examples/dependency-injection).\n\n</details>\n\n<details><summary>Party Controller (NEW)</summary>\n\n> Head over to the [full running example](https://github.com/kataras/iris/blob/main/_examples/routing/party-controller)!\n\n</details>\n\n<details><summary>MVC</summary>\n\n```go\npackage main\n\nimport (\n  \"github.com/kataras/iris/v12\"\n  \"github.com/kataras/iris/v12/mvc\"\n)\n\ntype (\n  request struct {\n    Firstname string `json:\"firstname\"`\n    Lastname  string `json:\"lastname\"`\n  }\n\n  response struct {\n    ID      uint64 `json:\"id\"`\n    Message string `json:\"message\"`\n  }\n)\n\nfunc main() {\n  app := iris.New()\n  mvc.Configure(app.Party(\"/users\"), configureMVC)\n  app.Listen(\":8080\")\n}\n\nfunc configureMVC(app *mvc.Application) {\n  app.Handle(new(userController))\n}\n\ntype userController struct {\n  // [...dependencies]\n}\n\nfunc (c *userController) PutBy(id uint64, req request) response {\n  return response{\n    ID:      id,\n    Message: req.Firstname + \" updated successfully\",\n  }\n}\n```\n\nWant to see more? Navigate through [mvc examples](_examples/mvc)!\n</details>\n\n\n<details><summary>API Guide <strong>HOT</strong></summary>\n\n```go\npackage main\n\nimport (\n  // [other packages...]\n\n  \"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n  iris.NewGuide().\n    AllowOrigin(\"*\").\n    Compression(true).\n    Health(true, \"development\", \"kataras\").\n    Timeout(0, 20*time.Second, 20*time.Second).\n    Middlewares(basicauth.New(...)).\n    Services(\n        // NewDatabase(),\n        // NewPostgresRepositoryRegistry,\n        // NewUserService,\n    ).\n    API(\"/users\", new(UsersAPI)).\n    Listen(\":80\")\n}\n```\n\n</details>\n\n<br/>\n\n-->\n\nNhư một [nhà phát triển Go](https://twitter.com/dkuye/status/1532087942696554497) đã từng nói, **Iris giúp bạn bảo vệ toàn diện và đứng vững qua nhiều năm**.\n\nMột số tính năng Iris cung cấp:\n\n* HTTP/2 (Push, cả những Embedded data)\n* Middleware (Accesslog, Basicauth, CORS, gRPC, Anti-Bot hCaptcha, JWT, MethodOverride, ModRevision, Monitor, PPROF, Ratelimit, Anti-Bot reCaptcha, Recovery, RequestID, Rewrite)\n* API Versioning\n* Model-View-Controller\n* Websockets\n* gRPC\n* Auto-HTTPS\n* Tích hợp hỗ trợ ngrok để đưa ứng dụng của bạn lên internet một cách nhanh nhất\n* Unique Router với đường dẫn động làm tham số với các loại tiêu chuẩn như :uuid, :string, :int... và khả năng tạo của riêng bạn\n* Compression\n* View Engines (HTML, Django, Handlebars, Pug/Jade and more)\n* Tạo Máy chủ tệp của riêng bạn và lưu trữ máy chủ WebDAV của riêng bạn\n* Cache\n* Localization (i18n, sitemap)\n* Sessions\n* Rich Responses (HTML, Text, Markdown, XML, YAML, Binary, JSON, JSONP, Protocol Buffers, MessagePack, Content Negotiation, Streaming, Server-Sent Events and more)\n* Response Compression (gzip, deflate, brotli, snappy, s2)\n* Rich Requests (Bind URL Query, Headers, Form, Text, XML, YAML, Binary, JSON, Validation, Protocol Buffers, MessagePack and more)\n* Dependency Injection (MVC, Handlers, API Routers)\n* Testing Suite\n* Và điều quan trọng nhất... bạn nhận được câu trả lời và hỗ trợ nhanh chóng từ ngày đầu tiên cho đến bây giờ - đó là sáu năm đầy đủ!\n\n## 👑 <a href=\"https://iris-go.com/donate\">Người ủng hộ</a>\n\nVới sự giúp đỡ của bạn, chúng tôi có thể cải thiện việc phát triển web Nguồn mở cho mọi người!\n\n## 📖 Học Iris\n\n### Cài đặt\n\nYêu cầu duy nhất là [Ngôn ngữ lập trình Go](https://go.dev/dl/).\n\n#### Tạo một dự án mới\n\n```sh\n$ mkdir myapp\n$ cd myapp\n$ go mod init myapp\n$ go get github.com/kataras/iris/v12@latest # or @v12.2.11\n```\n\n<details><summary>Cài đặt trên dự án hiện có</summary>\n\n```sh\n$ cd myapp\n$ go get github.com/kataras/iris/v12@latest\n```\n\n**Run**\n\n```sh\n$ go mod tidy -compat=1.20 # -compat=\"1.20\" for windows.\n$ go run .\n```\n\n</details>\n\n![](https://www.iris-go.com/images/gifs/install-create-iris.gif)\n\nIris chứa **[tài liệu](https://www.iris-go.com/docs)** phong phú và kỹ lưỡng giúp bạn dễ dàng bắt đầu với khung.\n\n<!-- Iris contains extensive and thorough **[wiki](https://github.com/kataras/iris/wiki)** making it easy to get started with the framework. -->\n\n<!-- ![](https://media.giphy.com/media/Ur8iqy9FQfmPuyQpgy/giphy.gif) -->\n\nĐể có tài liệu kỹ thuật chi tiết hơn, bạn có thể truy cập [godocs](https://pkg.go.dev/github.com/kataras/iris/v12@main) của chúng tôi. Và đối với mã thực thi, bạn luôn có thể truy cập thư mục con của kho lưu trữ [./_examples](_examples).\n\n### Bạn có thích đọc khi đi du lịch không?\n\n<a href=\"https://iris-go.com/#book\"> <img alt=\"Book cover\" src=\"https://iris-go.com/static/images/iris-book-cover-sm.jpg?v=12\" /> </a>\n\n[![follow author on twitter](https://img.shields.io/twitter/follow/makismaropoulos?color=3D8AA3&logoColor=3D8AA3&style=for-the-badge&logo=twitter)](https://twitter.com/intent/follow?screen_name=makismaropoulos)\n\n[![follow Iris web framework on twitter](https://img.shields.io/twitter/follow/iris_framework?color=ee7506&logoColor=ee7506&style=for-the-badge&logo=twitter)](https://twitter.com/intent/follow?screen_name=iris_framework)\n\n[![follow Iris web framework on facebook](https://img.shields.io/badge/Follow%20%40Iris.framework-569-2D88FF.svg?style=for-the-badge&logo=facebook)](https://www.facebook.com/iris.framework)\n\nBạn có thể [yêu cầu](https://www.iris-go.com/#ebookDonateForm) PDF và truy cập trực tuyến **Sách điện tử Iris** (Phiên bản mới, **tương lai v12.2.0+**) hôm nay và được tham gia vào sự phát triển của Iris.\n\n## 🙌 Đóng góp\n\nChúng tôi muốn thấy sự đóng góp của bạn cho Iris Web Framework! Để biết thêm thông tin về việc đóng góp cho dự án Iris, vui lòng kiểm tra tệp [CONTRIBUTING.md](CONTRIBUTING.md).\n\n[Danh sách những người đóng góp](https://github.com/kataras/iris/graphs/contributors)\n\n## 🛡 Lỗ hổng bảo mật\n\nNếu bạn phát hiện ra lỗ hổng bảo mật trong Iris, vui lòng gửi e-mail tới [iris-go@outlook.com](mailto:iris-go@outlook.com). Tất cả các lỗ hổng bảo mật sẽ được giải quyết kịp thời.\n\n## 📝 Giấy phép\n\nDự án này được cấp phép theo [BSD 3-clause license](LICENSE), giống như chính dự án Go.\n\nTên dự án \"Iris\" được lấy cảm hứng từ thần thoại Hy Lạp.\n<!-- ## Stargazers over time\n\n[![Stargazers over time](https://starchart.cc/kataras/iris.svg)](https://starchart.cc/kataras/iris) -->\n"
  },
  {
    "path": "README_ZH.md",
    "content": "# Iris Web Framework <a href=\"README_GR.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-greece.svg\" /></a> <a href=\"README_FR.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-france.svg\" /></a> <a href=\"README_ZH_HANT.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-taiwan.svg\" /></a> <a href=\"README_ZH_HANS.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-china.svg\" /></a> <a href=\"README_ES.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-spain.png\" /></a> <a href=\"README_FA.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-iran.svg\" /></a> <a href=\"README_RU.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-russia.svg\" /></a> <a href=\"README_KO.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-south-korea.svg?v=12\" /></a> <a href=\"README_PT_BR.md\"><img width=\"20px\" height=\"20px\" src=\"https://iris-go.com/static/images/flag-brazil.svg\" /></a> <a href=\"README_JA.md\"><img width=\"20px\" height=\"20px\" src=\"https://iris-go.com/static/images/flag-japan.svg\" /></a>\n\n<img width=\"16px\" src=\"https://iris-go.com/static/images/flag-chinese-zht.png\" /> [Traditional Chinese (zht / zh-Hant)](README_ZH_HANT.md)\n\n<img width=\"16px\" src=\"https://iris-go.com/static/images/flag-chinese-zhs.png\" /> [Simplified Chinese (zhs / zh-Hans)](README_ZH_HANS.md)\n"
  },
  {
    "path": "README_ZH_HANS.md",
    "content": "[![黑人的命也是命](https://iris-go.com/static/images/blacklivesmatter_banner.png)](https://support.eji.org/give/153413/#!/donation/checkout)\n\n<!-- # News -->\n\n> 这是一个**开发中的版本**。敬请关注即将发布的版本 [v12.2.0](HISTORY.md#Next)。如果想使用稳定版本，请查看 [v12.1.8 分支](https://github.com/kataras/iris/tree/v12.1.8) 。\n>\n> ![](https://iris-go.com/static/images/cli.png) 立即尝试官方的[Iris命令行工具](https://github.com/kataras/iris-cli)！\n\n<!-- ![](https://iris-go.com/static/images/release.png) Iris version **12.1.8** has been [released](HISTORY.md#su-16-february-2020--v1218)! -->\n\n# Iris Web Framework <a href=\"README_GR.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-greece.svg\" /></a> <a href=\"README_FR.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-france.svg\" /></a> <a href=\"README_ZH.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-china.svg\" /></a> <a href=\"README_ES.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-spain.png\" /></a> <a href=\"README_FA.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-iran.svg\" /></a> <a href=\"README_RU.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-russia.svg\" /></a> <a href=\"README_KO.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-south-korea.svg?v=12\" /></a> <a href=\"README_JA.md\"><img width=\"20px\" height=\"20px\" src=\"https://iris-go.com/static/images/flag-japan.svg\" /></a>\n\n[![build status](https://img.shields.io/github/actions/workflow/status/kataras/iris/ci.yml?branch=main&style=for-the-badge)](https://github.com/kataras/iris/actions/workflows/ci.yml) [![view examples](https://img.shields.io/badge/examples%20-253-a83adf.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/main/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=cc2b5e&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community) <!--[![FOSSA Status](https://img.shields.io/badge/LICENSE%20SCAN-PASSING❤️-CD2956?style=for-the-badge&logo=fossa)](https://app.fossa.io/projects/git%2Bgithub.com%2Fkataras%2Firis?ref=badge_shield)--> [![donate](https://img.shields.io/badge/support-Iris-blue.svg?style=for-the-badge&logo=paypal)](https://iris-go.com/donate) <!--[![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=for-the-badge)](https://goreportcard.com/report/github.com/kataras/iris)--><!--[![godocs](https://img.shields.io/badge/go-%20docs-488AC7.svg?style=for-the-badge)](https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.11)--> <!-- [![release](https://img.shields.io/badge/release%20-v12.0-0077b3.svg?style=for-the-badge)](https://github.com/kataras/iris/releases) -->\n\n<!-- <a href=\"https://iris-go.com\"> <img align=\"right\" src=\"https://iris-go.com/static/images/logo-w169.png\"></a> -->\n\nIris 是基于 Go 编写的一个快速，简单但功能齐全且非常高效的 Web 框架。 \n\n它为您的下一个网站或 API 提供了一个非常富有表现力且易于使用的基础。\n\n看看 [其他人如何评价 Iris](https://iris-go.com/testimonials/)，同时欢迎各位为此开源项目点亮 **[star](https://github.com/kataras/iris/stargazers)**。\n\n[![](https://iris-go.com/static/images/reviews.gif)](https://iris-go.com/testimonials/)\n\n[![Benchmarks: Jul 18, 2020 at 10:46am (UTC)](https://iris-go.com/static/images/benchmarks.svg)](https://github.com/kataras/server-benchmarks)\n\n## 📖 开始学习 Iris\n\n```sh\n# 安装Iris：https://www.iris-go.com/#ebookDonateForm\n$ go get github.com/kataras/iris/v12@latest\n# 假设main.go文件中已存在以下代码\n$ cat main.go\n```\n\n```go\npackage main\n\nimport \"github.com/kataras/iris/v12\"\n\nfunc main() {\n\tapp := iris.New()\n\n\tbooksAPI := app.Party(\"/books\")\n\t{\n\t\tbooksAPI.Use(iris.Compression)\n\n\t\t// GET: http://localhost:8080/books\n\t\tbooksAPI.Get(\"/\", list)\n\t\t// POST: http://localhost:8080/books\n\t\tbooksAPI.Post(\"/\", create)\n\t}\n\n\tapp.Listen(\":8080\")\n}\n\n// Book example.\ntype Book struct {\n\tTitle string `json:\"title\"`\n}\n\nfunc list(ctx iris.Context) {\n\tbooks := []Book{\n\t\t{\"Mastering Concurrency in Go\"},\n\t\t{\"Go Design Patterns\"},\n\t\t{\"Black Hat Go\"},\n\t}\n\n\tctx.JSON(books)\n\t// 提示: 在服务器优先级和客户端请求中进行响应协商，\n\t// 以此来代替 ctx.JSON:\n\t// ctx.Negotiation().JSON().MsgPack().Protobuf()\n\t// ctx.Negotiate(books)\n}\n\nfunc create(ctx iris.Context) {\n\tvar b Book\n\terr := ctx.ReadJSON(&b)\n\t// 提示: 使用 ctx.ReadBody(&b) 代替，来绑定所有类型的入参\n\tif err != nil {\n\t\tctx.StopWithProblem(iris.StatusBadRequest, iris.NewProblem().\n\t\t\tTitle(\"Book creation failure\").DetailErr(err))\n\t\t// 提示: 如果仅有纯文本（plain text）错误响应，\n        // 可使用 ctx.StopWithError(code, err) \n\t\treturn\n\t}\n\n\tprintln(\"Received Book: \" + b.Title)\n\n\tctx.StatusCode(iris.StatusCreated)\n}\n```\n\n同样地，在**MVC**中 :\n\n```go\nimport \"github.com/kataras/iris/v12/mvc\"\n```\n\n```go\nm := mvc.New(booksAPI)\nm.Handle(new(BookController))\n```\n\n```go\ntype BookController struct {\n\t/* dependencies */\n}\n\n// GET: http://localhost:8080/books\nfunc (c *BookController) Get() []Book {\n\treturn []Book{\n\t\t{\"Mastering Concurrency in Go\"},\n\t\t{\"Go Design Patterns\"},\n\t\t{\"Black Hat Go\"},\n\t}\n}\n\n// POST: http://localhost:8080/books\nfunc (c *BookController) Post(b Book) int {\n\tprintln(\"Received Book: \" + b.Title)\n\n\treturn iris.StatusCreated\n}\n```\n\n**启动** 您的 Iris web 服务:\n\n```sh\n$ go run main.go\n> Now listening on: http://localhost:8080\n> Application started. Press CTRL+C to shut down.\n```\n\nBooks **列表查询** :\n\n```sh\n$ curl --header 'Accept-Encoding:gzip' http://localhost:8080/books\n\n[\n  {\n    \"title\": \"Mastering Concurrency in Go\"\n  },\n  {\n    \"title\": \"Go Design Patterns\"\n  },\n  {\n    \"title\": \"Black Hat Go\"\n  }\n]\n```\n\n**创建** 新的Book:\n\n```sh\n$ curl -i -X POST \\\n--header 'Content-Encoding:gzip' \\\n--header 'Content-Type:application/json' \\\n--data \"{\\\"title\\\":\\\"Writing An Interpreter In Go\\\"}\" \\\nhttp://localhost:8080/books\n\n> HTTP/1.1 201 Created\n```\n\n这是**错误**响应所展示的样子：\n\n```sh\n$ curl -X POST --data \"{\\\"title\\\" \\\"not valid one\\\"}\" \\\nhttp://localhost:8080/books\n\n> HTTP/1.1 400 Bad Request\n\n{\n  \"status\": 400,\n  \"title\": \"Book creation failure\"\n  \"detail\": \"invalid character '\\\"' after object key\",\n}\n```\n\n</details>\n\n[![run in the browser](https://img.shields.io/badge/Run-in%20the%20Browser-348798.svg?style=for-the-badge&logo=repl.it)](https://replit.com/@kataras/Iris-Hello-World-v1220?v=1)\n\nIris 有完整且详尽的 **[使用文档](https://www.iris-go.com/#ebookDonateForm)** ，让您可以轻松地使用此框架。\n\n<!-- ![](https://media.giphy.com/media/Ur8iqy9FQfmPuyQpgy/giphy.gif) -->\n\n要了解更详细的技术文档，请访问我们的 [godocs](https://pkg.go.dev/github.com/kataras/iris/v12@main)。如果想要寻找代码示例，您可以到仓库的 [./_examples](_examples) 子目录下获取。\n\n### 你喜欢在旅行时阅读吗？\n\n<a href=\"https://iris-go.com/#book\"> <img alt=\"Book cover\" src=\"https://iris-go.com/static/images/iris-book-cover-sm.jpg?v=12\" /> </a>\n\n[![follow Iris web framework on twitter](https://img.shields.io/twitter/follow/iris_framework?color=ee7506&logoColor=ee7506&style=for-the-badge)](https://twitter.com/intent/follow?screen_name=iris_framework)\n\n您可以[获取](https://www.iris-go.com/#ebookDonateForm)PDF版本或在线访问**电子图书**，并参与到Iris的开发中。\n\n## 🙌 贡献\n\n我们欢迎您为Iris框架做出贡献！想要知道如何为Iris项目做贡献，请查看[CONTRIBUTING.md](CONTRIBUTING.md)。\n\n[贡献者名单](https://github.com/kataras/iris/graphs/contributors)\n\n## 🛡 安全漏洞\n\n如果您发现在 Iris 存在安全漏洞，请发送电子邮件至 [iris-go@outlook.com](mailto:iris-go@outlook.com)。所有安全漏洞将会得到及时解决。\n\n## 📝 开源协议（License）\n\n就像Go语言的协议一样，此项目也采用 [BSD 3-clause license](LICENSE)。\n\n项目名称 \"Iris\" 的灵感来自于希腊神话。\n\n<!-- ## Stargazers over time\n\n[![Stargazers over time](https://starchart.cc/kataras/iris.svg)](https://starchart.cc/kataras/iris) -->\n"
  },
  {
    "path": "README_ZH_HANT.md",
    "content": "<!-- [![黑人的命也是命](https://iris-go.com/static/images/blacklivesmatter_banner.png)](https://support.eji.org/give/153413/#!/donation/checkout)\n\n# 新聞\n\n> 此為 **開發中** 分支——功能不僅最新，而且最好。敬請期待接下來的發行版本 [v12.2.0](HISTORY.md#Next)。若需比較穩定的分支，請改前往 [v12.1.8 分支](https://github.com/kataras/iris/tree/v12.1.8)。\n>\n> ![](https://iris-go.com/static/images/cli.png) 立刻試試看官方的 [Iris 命令列介面 (CLI)](https://github.com/kataras/iris-cli)！\n\n> 因為工作量過大，[問題](https://github.com/kataras/iris/issues) 解答的速度可能會有所延宕。 -->\n\n<!-- ![](https://iris-go.com/static/images/release.png) Iris 的 **12.1.8** 版本已經 [釋出](HISTORY.md#su-16-february-2020--v1218)! -->\n\n# Iris Web 框架 <a href=\"README_GR.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-greece.svg\" /></a> <a href=\"README_FR.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-france.svg\" /></a> <a href=\"README_ZH.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-china.svg\" /></a> <a href=\"README_ES.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-spain.png\" /></a> <a href=\"README_FA.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-iran.svg\" /></a> <a href=\"README_RU.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-russia.svg\" /></a> <a href=\"README_KO.md\"><img width=\"20px\" src=\"https://iris-go.com/static/images/flag-south-korea.svg?v=12\" /></a> <a href=\"README_PT_BR.md\"><img width=\"20px\" height=\"20px\" src=\"https://iris-go.com/static/images/flag-brazil.svg\" /></a> <a href=\"README_JA.md\"><img width=\"20px\" height=\"20px\" src=\"https://iris-go.com/static/images/flag-japan.svg\" /></a>\n\n[![組建狀態](https://img.shields.io/github/actions/workflow/status/kataras/iris/ci.yml?branch=main&style=for-the-badge)](https://github.com/kataras/iris/actions/workflows/ci.yml) [![查看範例](https://img.shields.io/badge/examples%20-285-a83adf.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/main/_examples) [![聊天室](https://img.shields.io/gitter/room/iris_go/community.svg?color=cc2b5e&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community) <!--[![FOSSA Status](https://img.shields.io/badge/LICENSE%20SCAN-PASSING❤️-CD2956?style=for-the-badge&logo=fossa)](https://app.fossa.io/projects/git%2Bgithub.com%2Fkataras%2Firis?ref=badge_shield)--> [![捐助](https://img.shields.io/badge/support-Iris-blue.svg?style=for-the-badge&logo=paypal)](https://iris-go.com/donate) <!--[![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=for-the-badge)](https://goreportcard.com/report/github.com/kataras/iris)--><!--[![godocs](https://img.shields.io/badge/go-%20docs-488AC7.svg?style=for-the-badge)](https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.11)--> <!-- [![release](https://img.shields.io/badge/release%20-v12.0-0077b3.svg?style=for-the-badge)](https://github.com/kataras/iris/releases) -->\n\n<!-- <a href=\"https://iris-go.com\"> <img align=\"right\" src=\"https://iris-go.com/static/images/logo-w169.png\"></a> -->\n\nIris 是款不僅迅速、簡捷，並且功能完善、高效率的 Go 語言 Web 框架。**與 Go 生態系統中其它人提供的免費軟體套件不同，這個軟體保證終身主動維護。**\n\n> 想要取得接下來 **v12.2.0** 穩定版本（正在逐步推進 (2023🎅)）的新消息，請收藏 🌟 並關注 👀 這個儲存庫！\n\nIris 能為你的下一個網站或 API，立下漂亮、富有表達性，且易於使用的基礎。\n\n```go\npackage main\n\nimport \"github.com/kataras/iris/v12\"\n\nfunc main() {\n  app := iris.New()\n  app.Use(iris.Compression)\n\n  app.Get(\"/\", func(ctx iris.Context) {\n    ctx.HTML(\"哈囉，<strong>%s</strong>！\", \"世界\")\n  })\n\n  app.Listen(\":8080\")\n}\n```\n\n<!-- <details><summary>More with simple Handler</summary>\n\n```go\npackage main\n\nimport \"github.com/kataras/iris/v12\"\n\ntype (\n  request struct {\n    Firstname string `json:\"firstname\"`\n    Lastname  string `json:\"lastname\"`\n  }\n\n  response struct {\n    ID      string `json:\"id\"`\n    Message string `json:\"message\"`\n  }\n)\n\nfunc main() {\n  app := iris.New()\n  app.Handle(\"PUT\", \"/users/{id:uuid}\", updateUser)\n  app.Listen(\":8080\")\n}\n\nfunc updateUser(ctx iris.Context) {\n  id := ctx.Params().Get(\"id\")\n\n  var req request\n  if err := ctx.ReadJSON(&req); err != nil {\n    ctx.StopWithError(iris.StatusBadRequest, err)\n    return\n  }\n\n  resp := response{\n    ID:      id,\n    Message: req.Firstname + \" updated successfully\",\n  }\n  ctx.JSON(resp)\n}\n```\n\n> Read the [routing examples](https://github.com/kataras/iris/blob/main/_examples/routing) for more!\n\n</details>\n\n<details><summary>Handler with custom input and output arguments</summary>\n\n[![https://github.com/kataras/iris/blob/main/_examples/dependency-injection/basic/main.go](https://user-images.githubusercontent.com/22900943/105253731-b8db6d00-5b88-11eb-90c1-0c92a5581c86.png)](https://twitter.com/iris_framework/status/1234783655408668672)\n\n> Interesting? Read the [examples](https://github.com/kataras/iris/blob/main/_examples/dependency-injection).\n\n</details>\n\n<details><summary>Party Controller (NEW)</summary>\n\n> Head over to the [full running example](https://github.com/kataras/iris/blob/main/_examples/routing/party-controller)!\n\n</details>\n\n<details><summary>MVC</summary>\n\n```go\npackage main\n\nimport (\n  \"github.com/kataras/iris/v12\"\n  \"github.com/kataras/iris/v12/mvc\"\n)\n\ntype (\n  request struct {\n    Firstname string `json:\"firstname\"`\n    Lastname  string `json:\"lastname\"`\n  }\n\n  response struct {\n    ID      uint64 `json:\"id\"`\n    Message string `json:\"message\"`\n  }\n)\n\nfunc main() {\n  app := iris.New()\n  mvc.Configure(app.Party(\"/users\"), configureMVC)\n  app.Listen(\":8080\")\n}\n\nfunc configureMVC(app *mvc.Application) {\n  app.Handle(new(userController))\n}\n\ntype userController struct {\n  // [...dependencies]\n}\n\nfunc (c *userController) PutBy(id uint64, req request) response {\n  return response{\n    ID:      id,\n    Message: req.Firstname + \" updated successfully\",\n  }\n}\n```\n\nWant to see more? Navigate through [mvc examples](_examples/mvc)!\n</details>\n\n\n<details><summary>API Guide <strong>HOT</strong></summary>\n\n```go\npackage main\n\nimport (\n  // [other packages...]\n\n  \"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n  iris.NewGuide().\n    AllowOrigin(\"*\").\n    Compression(true).\n    Health(true, \"development\", \"kataras\").\n    Timeout(0, 20*time.Second, 20*time.Second).\n    Middlewares(basicauth.New(...)).\n    Services(\n        // NewDatabase(),\n        // NewPostgresRepositoryRegistry,\n        // NewUserService,\n    ).\n    API(\"/users\", new(UsersAPI)).\n    Listen(\":80\")\n}\n```\n\n</details>\n\n<br/>\n\n-->\n\n據一位 [Go 開發者](https://twitter.com/dkuye/status/1532087942696554497) 所言，**Iris 能向您提供全方位的服務，並地位多年來屹立不搖**。\n\nIris 提供了至少這些功能：\n\n- HTTP/2 (Push, 甚至是 Embedded 資料)\n- 中介模組（存取日誌、基礎認證、CORS、gRPC、防機器人 hCaptcha、JWT、方法覆寫、模組版本顯示、監控、PPROF、速率限制、防機器人 reCaptcha、panic 救援、請求識別碼、重寫請求）\n- API 分版 (versioning)\n- MVC (Model-View-Controller) 模式\n- Websocket\n- gRPC\n- 自動啟用 HTTPS\n- 內建 ngrok 支援，讓您可以把 app 以最快速的方式推上網際網路\n- 包含動態路徑、具唯一性的路由，支援如 :uuid、:string、:int 等等的標準類型，並且可以自己建立\n- 壓縮功能\n- 檢視 (View) 算繪引擎 (HTML、Django、Handlebars、Pug/Jade 等等）\n- 建立自己的檔案伺服器，並寄存您自己的 WebDAV 伺服器\n- 快取\n- 本地化 (i18n、sitemap）\n- 連線階段管理\n- 豐富的回應格式（HTML、純文字、Markdown、XML、YAML、二進位、JSON、JSONP、Protocol Buffers、MessagePack、(HTTP) 內容協商、串流、Server-Sent Events 等）\n- 回應壓縮功能（gzip、deflate、brotli、snappy、s2）\n- 豐富的請求方式（綁定 URL 查詢、標頭、文字、XML、YAML、二進位、JSON、資料驗證、Protocol Buffers、MessagePack 等）\n- 依賴注入（MVC、處理常式 (handler)、API 路由）\n- 測試套件\n- 最重要的是…… 從發行第一天到現在（已經整整六年），解答與支援一直都十分迅速！\n\n看看別人 [是如何評價 Iris 的](https://www.iris-go.com/#review)，並且 **[給這個開放原始碼專案一顆小星星](https://github.com/kataras/iris/stargazers)**，支持專案的潛力。\n\n[![](https://iris-go.com/static/images/reviews.gif)](https://iris-go.com/testimonials/)\n\n[![Benchmarks: Jul 18, 2020 at 10:46am (UTC)](https://iris-go.com/static/images/benchmarks.svg)](https://github.com/kataras/server-benchmarks)\n\n## 👑 <a href=\"https://iris-go.com/donate\">支援者</a>\n\n你的一臂之力，能夠為大家帶來更好的開放原始碼 Web 開發體驗！\n\n> [@github](https://github.com/github) is now sponsoring you for $550.00 one time.\n>\n> A note from your new sponsor:\n>\n> To celebrate Maintainer Month we want to thank you for all you do for the open source community. Check out our blog post to learn more about how GitHub is investing in maintainers. https://github.blog/2022-06-24-thank-you-to-our-maintainers/\n\n> 現已支援來自 [中國](https://github.com/kataras/iris/issues/1870#issuecomment-1101418349) 的捐款！\n\n## 📖 學習 Iris\n\n### 安裝\n\n只要先安裝好 [Go 程式語言](https://go.dev/dl/) 即可。\n\n#### 建立新專案\n\n```sh\n$ mkdir myapp\n$ cd myapp\n$ go mod init myapp\n$ go get github.com/kataras/iris/v12@latest # 或 @v12.2.11\n```\n\n<details><summary>在現有專案安裝</summary>\n\n```sh\n$ cd myapp\n$ go get github.com/kataras/iris/v12@latest\n```\n\n**執行**\n\n```sh\n$ go mod tidy -compat=1.20 # Windows 的話，請試試 -compat=\"1.20\"\n$ go run .\n```\n\n</details>\n\n![](https://www.iris-go.com/images/gifs/install-create-iris.gif)\n\nIris 包含極其豐富且透徹的 **[文件](https://www.iris-go.com/docs)**，讓框架的入門觸手可及。\n\n<!-- Iris contains extensive and thorough **[wiki](https://github.com/kataras/iris/wiki)** making it easy to get started with the framework. -->\n\n<!-- ![](https://media.giphy.com/media/Ur8iqy9FQfmPuyQpgy/giphy.gif) -->\n\n如需更為詳細的技術性文件，您可以前往我們的 [godocs](https://pkg.go.dev/github.com/kataras/iris/v12@main)。如果要可以直接執行的程式碼，可以到 [./\\_examples](_examples) 儲存庫的子目錄參閱。\n\n### 想一邊旅行、一邊閱讀嗎？\n\n<a href=\"https://iris-go.com/#book\"> <img alt=\"Book cover\" src=\"https://iris-go.com/static/images/iris-book-cover-sm.jpg?v=12\" /> </a>\n\n[![在 Twitter 上追蹤作者](https://img.shields.io/twitter/follow/makismaropoulos?color=3D8AA3&logoColor=3D8AA3&style=for-the-badge&logo=twitter)](https://twitter.com/intent/follow?screen_name=makismaropoulos)\n\n[![在 Twitter 上追蹤 Iris Web 框架](https://img.shields.io/twitter/follow/iris_framework?color=ee7506&logoColor=ee7506&style=for-the-badge&logo=twitter)](https://twitter.com/intent/follow?screen_name=iris_framework)\n\n[![在 Facebook 上追蹤 Iris Web 框架](https://img.shields.io/badge/Follow%20%40Iris.framework-569-2D88FF.svg?style=for-the-badge&logo=facebook)](https://www.facebook.com/iris.framework)\n\n您現在可以 [請求索取](https://www.iris-go.com/#ebookDonateForm) **Iris 電子書**（新版，**針對未來版本 v12.2.0+**) 的 PDF 和線上閱讀存取權限，並共同參與 Iris 的開發。\n\n## 🙌 貢獻\n\n我們殷切期盼你對 Iris Web 框架的貢獻！有關貢獻 Iris 專案的更多資訊，請參閱 [CONTRIBUTING.md](CONTRIBUTING.md) 檔案。\n\n[所有貢獻者名單](https://github.com/kataras/iris/graphs/contributors)\n\n## 🛡 安全性漏洞\n\n如果你發現 Iris 中有安全性漏洞，請寄一封電子郵件至 [iris-go@outlook.com](mailto:iris-go@outlook.com)。我們會儘速解決所有安全性漏洞。\n\n## 📝 授權條款\n\n本專案和 Go 語言相同，皆採 [BSD 3-clause 授權條款](LICENSE) 授權。\n\n專案的名稱「Iris」取材自希臘神話。\n\n<!-- ## Stargazers over time\n\n[![Stargazers over time](https://starchart.cc/kataras/iris.svg)](https://starchart.cc/kataras/iris) -->\n"
  },
  {
    "path": "SECURITY.md",
    "content": "# Security Policy\n\n## Supported Versions\n\nWe are focusing on fixing issues and security vulnerabilities to the latest and greatest version of Iris.\nIris users SHOULD always update their backend code to the latest version of Iris API, there is a complete [HISTORY](https://github.com/kataras/iris/blob/main/HISTORY.md) file\nwhich acts as a code migration assistant.\n\n## Reporting a Vulnerability\n\nPlease report (suspected) security vulnerabilities to\n**[iris-go@outlook.com](mailto:iris-go@outlook.com)**. You will receive a response from\nus within 3-4 working days. If the issue is confirmed, we will release a new minor version as soon\nas possible depending on complexity but historically within a few hours or days.\n"
  },
  {
    "path": "VERSION",
    "content": "v12.2.0:https://github.com/kataras/iris/tree/v12.2.0"
  },
  {
    "path": "_benchmarks/README.md",
    "content": "# Benchmarks\r\n\r\n- [HTTP/2 Benchmarks](https://github.com/kataras/server-benchmarks#benchmarks)\r\n- [View Engine Benchmarks](./view)\r\n"
  },
  {
    "path": "_benchmarks/view/README.md",
    "content": "# View Engine Benchmarks\n\nBenchmark between all 7 supported template parsers.\n\nAce and Pug parsers minifies the template before render. So, to have a fair benchmark, we must make sure that the byte amount of the total response body is exactly the same across all. Therefore, all other template files are minified too.\n\n![Benchmarks Chart Graph](chart.png)\n\n> Last updated: Mar 17, 2023 at 10:31am (UTC)\n\n## System\n\n|    |    |\n|----|:---|\n| Processor | 12th Gen Intel(R) Core(TM) i7-12700H |\n| RAM | 15.68 GB |\n| OS | Microsoft Windows 11 Pro |\n| [Bombardier](https://github.com/codesenberg/bombardier) | v1.2.4 |\n| [Go](https://golang.org) | go1.20.2 |\n| [Node.js](https://nodejs.org/) | v19.5.0 |\n\n## Terminology\n\n**Name** is the name of the framework(or router) used under a particular test.\n\n**Reqs/sec** is the avg number of total requests could be processed per second (the higher the better).\n\n**Latency** is the amount of time it takes from when a request is made by the client to the time it takes for the response to get back to that client (the smaller the better).\n\n**Throughput** is the rate of production or the rate at which data are transferred (the higher the better, it depends from response length (body + headers).\n\n**Time To Complete** is the total time (in seconds) the test completed (the smaller the better).\n\n## Results\n\n### Test:Template Layout, Partial and Data\n\n📖 Fires 1000000 requests with 125 concurrent clients. It receives HTML response. The server handler sets some template **data** and renders a template file which consists of a **layout** and a **partial** footer.\n\n| Name | Language | Reqs/sec | Latency | Throughput | Time To Complete |\n|------|:---------|:---------|:--------|:-----------|:-----------------|\n| [Jet](./jet) | Go |248957 |500.81us |88.26MB |4.02s |\n| [Blocks](./blocks) | Go |238854 |521.76us |84.74MB |4.19s |\n| [Pug](./pug) | Go |238153 |523.74us |85.07MB |4.20s |\n| [Django](./django) | Go |224448 |555.40us |79.61MB |4.46s |\n| [Handlebars](./handlebars) | Go |197267 |631.99us |69.96MB |5.07s |\n| [Ace](./ace) | Go |157415 |792.53us |55.83MB |6.35s |\n| [HTML](./html) | Go |120811 |1.03ms |42.82MB |8.29s |\n\n## How to Run\n\n```sh\n$ go install github.com/kataras/server-benchmarks@master\n$ go install github.com/codesenberg/bombardier@master\n$ server-benchmarks --wait-run=3s -o ./results\n```\n"
  },
  {
    "path": "_benchmarks/view/ace/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\t// By default Ace engine minifies the template before render.\n\tapp.RegisterView(iris.Ace(\"./views\", \".ace\").SetIndent(\"\"))\n\n\tapp.Get(\"/\", index)\n\n\tapp.Listen(\":8080\")\n}\n\nfunc index(ctx iris.Context) {\n\tdata := iris.Map{\n\t\t\"Title\":      \"Page Title\",\n\t\t\"FooterText\": \"Footer contents\",\n\t\t\"Message\":    \"Main contents\",\n\t}\n\n\tctx.ViewLayout(\"layouts/main\")\n\tif err := ctx.View(\"index\", data); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "_benchmarks/view/ace/views/index.ace",
    "content": "h1 Index Body\nh3 Message: {{.Message}}"
  },
  {
    "path": "_benchmarks/view/ace/views/layouts/main.ace",
    "content": "= doctype html\nhtml\n  head\n    title {{.Title}}\n  body\n    {{ yield . }}\n    footer\n      = include partials/footer.ace .\n"
  },
  {
    "path": "_benchmarks/view/ace/views/partials/footer.ace",
    "content": "h3 Footer Partial\nh4 {{.FooterText}}"
  },
  {
    "path": "_benchmarks/view/blocks/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.RegisterView(iris.Blocks(\"./views\", \".html\"))\n\t// Note, in Blocks engine, layouts\n\t// are used by their base names, the\n\t// blocks.LayoutDir(layoutDir) defaults to \"./layouts\".\n\n\tapp.Get(\"/\", index)\n\n\tapp.Listen(\":8080\")\n}\n\nfunc index(ctx iris.Context) {\n\tdata := iris.Map{\n\t\t\"Title\":      \"Page Title\",\n\t\t\"FooterText\": \"Footer contents\",\n\t\t\"Message\":    \"Main contents\",\n\t}\n\n\tctx.ViewLayout(\"main\")\n\tif err := ctx.View(\"index\", data); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "_benchmarks/view/blocks/views/index.html",
    "content": "<h1>Index Body</h1><h3>Message: {{.Message}}</h3>"
  },
  {
    "path": "_benchmarks/view/blocks/views/layouts/main.html",
    "content": "<!DOCTYPE html><html><head><title>{{.Title}}</title></head><body>{{ template \"content\" . }}<footer>{{ partial \"partials/footer\" . }}</footer></body></html>"
  },
  {
    "path": "_benchmarks/view/blocks/views/partials/footer.html",
    "content": "<h3>Footer Partial</h3><h4>{{.FooterText}}</h4>"
  },
  {
    "path": "_benchmarks/view/django/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.RegisterView(iris.Django(\"./views\", \".html\"))\n\n\tapp.Get(\"/\", index)\n\n\tapp.Listen(\":8080\")\n}\n\nfunc index(ctx iris.Context) {\n\tdata := iris.Map{\n\t\t\"Title\":      \"Page Title\",\n\t\t\"FooterText\": \"Footer contents\",\n\t\t\"Message\":    \"Main contents\",\n\t}\n\n\t// On Django this is ignored:  ctx.ViewLayout(\"layouts/main\")\n\t// Layouts are only rendered from inside the index page itself\n\t// using the \"extends\" keyword.\n\tif err := ctx.View(\"index\", data); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "_benchmarks/view/django/views/index.html",
    "content": "{% extends \"layouts/main.html\" %}{% block content %}<h1>Index Body</h1><h3>Message: {{Message}}</h3>{% endblock %}"
  },
  {
    "path": "_benchmarks/view/django/views/layouts/main.html",
    "content": "<!DOCTYPE html><html><head><title>{{Title}}</title></head><body>{% block content %} {% endblock %}<footer>{% include \"../partials/footer.html\" %}</footer></body></html>"
  },
  {
    "path": "_benchmarks/view/django/views/partials/footer.html",
    "content": "<h3>Footer Partial</h3><h4>{{FooterText}}</h4>"
  },
  {
    "path": "_benchmarks/view/handlebars/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.RegisterView(iris.Handlebars(\"./views\", \".html\"))\n\n\tapp.Get(\"/\", index)\n\n\tapp.Listen(\":8080\")\n}\n\nfunc index(ctx iris.Context) {\n\tdata := iris.Map{\n\t\t\"Title\":      \"Page Title\",\n\t\t\"FooterText\": \"Footer contents\",\n\t\t\"Message\":    \"Main contents\",\n\t}\n\n\tctx.ViewLayout(\"layouts/main\")\n\tif err := ctx.View(\"index\", data); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "_benchmarks/view/handlebars/views/index.html",
    "content": "<h1>Index Body</h1><h3>Message: {{Message}}</h3>"
  },
  {
    "path": "_benchmarks/view/handlebars/views/layouts/main.html",
    "content": "<!DOCTYPE html><html><head><title>{{Title}}</title></head><body>{{ yield . }}<footer>{{ render \"partials/footer.html\" .}}</footer></body></html>"
  },
  {
    "path": "_benchmarks/view/handlebars/views/partials/footer.html",
    "content": "<h3>Footer Partial</h3><h4>{{FooterText}}</h4>"
  },
  {
    "path": "_benchmarks/view/html/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\tapp.RegisterView(iris.HTML(\"./views\", \".html\"))\n\n\tapp.Get(\"/\", index)\n\n\tapp.Listen(\":8080\")\n}\n\nfunc index(ctx iris.Context) {\n\tdata := iris.Map{\n\t\t\"Title\":      \"Page Title\",\n\t\t\"FooterText\": \"Footer contents\",\n\t\t\"Message\":    \"Main contents\",\n\t}\n\n\tctx.ViewLayout(\"layouts/main\")\n\tif err := ctx.View(\"index\", data); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "_benchmarks/view/html/views/index.html",
    "content": "<h1>Index Body</h1><h3>Message: {{.Message}}</h3>"
  },
  {
    "path": "_benchmarks/view/html/views/layouts/main.html",
    "content": "<!DOCTYPE html><html><head><title>{{.Title}}</title></head><body>{{ yield . }}<footer>{{ render \"partials/footer.html\" . }}</footer></body></html>"
  },
  {
    "path": "_benchmarks/view/html/views/partials/footer.html",
    "content": "<h3>Footer Partial</h3><h4>{{.FooterText}}</h4>"
  },
  {
    "path": "_benchmarks/view/jet/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.RegisterView(iris.Jet(\"./views\", \".jet\"))\n\n\tapp.Get(\"/\", index)\n\n\tapp.Listen(\":8080\")\n}\n\nfunc index(ctx iris.Context) {\n\tdata := iris.Map{\n\t\t\"Title\":      \"Page Title\",\n\t\t\"FooterText\": \"Footer contents\",\n\t\t\"Message\":    \"Main contents\",\n\t}\n\n\t// On Jet this is ignored:  ctx.ViewLayout(\"layouts/main\")\n\t// Layouts are only rendered from inside the index page itself\n\t// using the \"extends\" keyword.\n\tif err := ctx.View(\"index\", data); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "_benchmarks/view/jet/views/index.jet",
    "content": "{{ extends \"../layouts/main.jet\" }}{{ block documentBody() }}<h1>Index Body</h1><h3>Message: {{.Message}}</h3>{{ end }}"
  },
  {
    "path": "_benchmarks/view/jet/views/layouts/main.jet",
    "content": "<!DOCTYPE html><html><head><title>{{.Title}}</title></head><body>{{ yield documentBody() }}<footer>{{ include \"../partials/footer.jet\" . }}</footer></body></html>"
  },
  {
    "path": "_benchmarks/view/jet/views/partials/footer.jet",
    "content": "<h3>Footer Partial</h3><h4>{{.FooterText}}</h4>"
  },
  {
    "path": "_benchmarks/view/pug/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\t// Ace engine minifies the template before render.\n\tapp.RegisterView(iris.Pug(\"./views\", \".pug\"))\n\n\tapp.Get(\"/\", index)\n\n\tapp.Listen(\":8080\")\n}\n\nfunc index(ctx iris.Context) {\n\tdata := iris.Map{\n\t\t\"Title\":      \"Page Title\",\n\t\t\"FooterText\": \"Footer contents\",\n\t\t\"Message\":    \"Main contents\",\n\t}\n\n\t// On Pug this is ignored:  ctx.ViewLayout(\"layouts/main\")\n\t// Layouts are only rendered from inside the index page itself\n\t// using the \"extends\" keyword.\n\tif err := ctx.View(\"index\", data); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "_benchmarks/view/pug/views/index.pug",
    "content": "extends layouts/main.pug\n\nblock content\n  h1 Index Body\n  h3 Message: {{.Message}}"
  },
  {
    "path": "_benchmarks/view/pug/views/layouts/main.pug",
    "content": "doctype html\nhtml\n  head\n    title {{.Title}}\n  body\n    block content\n    footer\n      include ../partials/footer.pug"
  },
  {
    "path": "_benchmarks/view/pug/views/partials/footer.pug",
    "content": "h3 Footer Partial\nh4 {{.FooterText}}"
  },
  {
    "path": "_benchmarks/view/tests.yml",
    "content": "- Name: Template Layout, Partial and Data\n  Description: >\n   Fires {{.NumberOfRequests}} requests with {{.NumberOfConnections}} concurrent clients.\n   It receives HTML response.\n   The server handler sets some template **data** and renders a\n   template file which consists of a **layout** and a **partial** footer.\n  NumberOfRequests: 1000000\n  NumberOfConnections: 125\n  Method: GET\n  URL: http://localhost:8080\n  Envs:\n    - Name: Ace\n      Dir: ./ace\n    - Name: Blocks\n      Dir: ./blocks\n    - Name: Django\n      Dir: ./django\n    - Name: Handlebars\n      Dir: ./handlebars\n    - Name: HTML\n      Dir: ./html\n    - Name: Jet\n      Dir: ./jet\n    - Name: Pug\n      Dir: ./pug\n"
  },
  {
    "path": "_examples/README.md",
    "content": "# Table of Contents <a href=\"./README_ZH_HANT.md\"> <img width=\"20px\" src=\"https://iris-go.com/static/images/flag-china.svg?v=10\" /> </a>\r\n\r\n🎁 **Welcome to the Iris Examples**\r\n\r\nUnwrap the magic of Iris with these festive examples, each one a present waiting to help you build amazing applications ✨\r\n\r\n* [Serverless](https://github.com/iris-contrib/gateway#netlify)\r\n* [REST API for Apache Kafka](kafka-api)\r\n* [URL Shortener](url-shortener)\r\n* [Dropzone.js](dropzonejs)\r\n* [Caddy](caddy)\r\n* [Bootstrapper](bootstrapper)\r\n* [Project Structure](project) :fire:\r\n* Monitor\r\n    * [Simple Process Monitor (includes UI)](monitor/monitor-middleware/main.go) **NEW**\r\n    * [Heap, MSpan/MCache, Size Classes, Objects, Goroutines, GC/CPU fraction (includes UI)](monitor/statsviz/main.go) **NEW**\r\n* Database\r\n    * [MySQL, Groupcache & Docker](database/mysql)\r\n    * [MongoDB](database/mongodb)\r\n    * [Sqlx](database/orm/sqlx/main.go)\r\n    * [Gorm](database/orm/gorm/main.go)\r\n    * [Reform](database/orm/reform/main.go)\r\n    * [x/sqlx](database/sqlx/main.go) **NEW**\r\n* GraphQL\r\n    * [GraphQL: schema-first](graphql/schema-first) **NEW**\r\n* HTTP Server\r\n    * [HOST:PORT](http-server/listen-addr/main.go)\r\n    * [Public Test Domain](http-server/listen-addr-public/main.go)\r\n    * [UNIX socket file](http-server/listen-unix/main.go)\r\n    * [TLS](http-server/listen-tls/main.go)\r\n    * [Letsencrypt (Automatic Certifications)](http-server/listen-letsencrypt/main.go)\r\n    * [Socket Sharding (SO_REUSEPORT)](http-server/socket-sharding/main.go)\r\n    * [Graceful Shutdown](http-server/graceful-shutdown/default-notifier/main.go)\r\n    * [Notify on shutdown](http-server/notify-on-shutdown/main.go)\r\n    * Custom TCP Listener\r\n        * [Common net.Listener](http-server/custom-listener/main.go)\r\n    * Custom HTTP Server\r\n        * [Pass a custom Server](http-server/custom-httpserver/easy-way/main.go)\r\n        * [Use Iris as a single http.Handler](http-server/custom-httpserver/std-way/main.go)\r\n        * [Multi Instances](http-server/custom-httpserver/multi/main.go)\r\n        * [HTTP/3 Quic](http-server/http3-quic)\r\n        * [H2C](http-server/h2c/main.go) **NEW**\r\n    * [Timeout](http-server/timeout/main.go)\r\n* HTTP Client\r\n    * [Weather Client](http-client/weatherapi)\r\n* Configuration\r\n    * [Functional](configuration/functional/main.go)\r\n    * [Configuration Struct](configuration/from-configuration-structure/main.go)\r\n    * [Using Viper](configuration/viper)\r\n    * [Import from YAML](configuration/from-yaml-file/main.go)\r\n        * [Share Configuration across instances](configuration/from-yaml-file/shared-configuration/main.go)\r\n    * [Import from TOML](configuration/from-toml-file/main.go)\r\n    * [Multi Environment Configuration](configuration/multi-environments) **NEW**\r\n* Routing\r\n    * [Custom Context](routing/custom-context/main.go) **HOT/NEW**\r\n    * [Party Controller](routing/party-controller) **NEW**\r\n    * [Overview](routing/overview/main.go)\r\n    * [Basic](routing/basic/main.go)\r\n    * [Custom HTTP Errors](routing/http-errors/main.go)\r\n    * [HTTP Wire Errors](routing/http-wire-errors/main.go) **NEW**\r\n        * [Service and Validation](routing/http-wire-errors/service/main.go) **NEW**\r\n    * [Not Found - Intelligence](routing/intelligence/main.go)\r\n        * [Not Found - Suggest Closest Paths](routing/intelligence/manual/main.go)\r\n    * [Dynamic Path](routing/dynamic-path/main.go)\r\n        * [At-username](routing/dynamic-path/at-username/main.go)\r\n        * [Root Wildcard](routing/dynamic-path/root-wildcard/main.go)\r\n        * [Implement a Parameter Type](routing/macros/main.go)\r\n        * [Same Path Pattern but Func](routing/dynamic-path/same-pattern-different-func/main.go)\r\n    * Middleware\r\n        * [Per Route](routing/writing-a-middleware/per-route/main.go)\r\n        * [Globally](routing/writing-a-middleware/globally/main.go)\r\n        * [Remove a Handler](routing/remove-handler/main.go)\r\n        * Share Values\r\n            * [Share Services](routing/writing-a-middleware/share-services/main.go)\r\n            * [Share Functions](routing/writing-a-middleware/share-funcs/main.go)\r\n        * [Handlers Execution Rule](routing/route-handlers-execution-rules/main.go)\r\n        * [Route Register Rule](routing/route-register-rule/main.go)\r\n        * Convert net/http Handlers\r\n            * [From func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc)](convert-handlers/negroni-like/main.go)\r\n            * [From http.Handler or http.HandlerFunc](convert-handlers/nethttp/main.go)\r\n            * [From func(http.Handler) http.Handler](convert-handlers/wrapper/main.go)\r\n            * [Convert by your own: sentry/raven middleware](convert-handlers/real-usecase-raven/writing-middleware/main.go)\r\n    * [Rewrite Middleware](routing/rewrite/main.go)\r\n    * [Route State](routing/route-state/main.go)\r\n    * [Remove Route](routing/remove-route/main.go)\r\n    * [Reverse Routing](routing/reverse/main.go)\r\n    * [Router Wrapper](routing/custom-wrapper/main.go)\r\n    * [Custom Router](routing/custom-router/main.go)\r\n    * Subdomains\r\n        * [Single](routing/subdomains/single/main.go)\r\n        * [Multi](routing/subdomains/multi/main.go)\r\n        * [Wildcard](routing/subdomains/wildcard/main.go)\r\n        * [WWW](routing/subdomains/www/main.go)\r\n            * [WWW Method](routing/subdomains/www/www-method/main.go)\r\n        * [Redirection](routing/subdomains/redirect/main.go)\r\n            * [Multi Instances](routing/subdomains/redirect/multi-instances/main.go)\r\n        * [HTTP Errors View](routing/subdomains/http-errors-view/main.go)\r\n    * [HTTP Method Override](https://github.com/kataras/iris/blob/main/middleware/methodoverride/methodoverride_test.go)\r\n    * [API Versioning](routing/versioning/main.go)\r\n    * [Sitemap](routing/sitemap/main.go)\r\n* Logging\r\n    * [Application File Logger](logging/file-logger/main.go)\r\n    * [Application JSON Logger](logging/json-logger/main.go)\r\n    * [Rollbar](logging/rollbar/main.go)\r\n    * AccessLog\r\n        * [Log Requests to a JSON File](logging/request-logger/accesslog-simple/main.go)\r\n        * [Using Log Rotation and more](logging/request-logger/accesslog)\r\n        * [Custom Fields and Template](logging/request-logger/accesslog-template/main.go)\r\n        * [Listen and render Logs to a Client](logging/request-logger/accesslog-broker/main.go)\r\n        * [The CSV Formatter](logging/request-logger/accesslog-csv/main.go)\r\n        * [Create your own Formatter](logging/request-logger/accesslog-formatter/main.go)\r\n        * [Root and Proxy AccessLog instances](logging/request-logger/accesslog-proxy/main.go)\r\n        * [Slack integration example](logging/request-logger/accesslog-slack/main.go)\r\n* API Documentation\r\n    * [Swagger](https://github.com/iris-contrib/swagger/tree/master/_examples/basic)\r\n* Testing\r\n    * [Testing with httptest](testing/httptest/main_test.go)\r\n    * [Testing with ginkgo](testing/ginkgotest)\r\n* [Recovery](recover/main.go)\r\n    * [Panic and custom Error Handler with Compression](recover/panic-and-custom-error-handler-with-compression/main.go)\r\n* [Profiling](pprof/main.go)\r\n* File Server\r\n    * [File Server](file-server/file-server/main.go)\r\n    * [HTTP/2 Push Targets](file-server/http2push/main.go)\r\n    * [HTTP/2 Push Targets (Embedded)](file-server/http2push-embedded/main.go)\r\n    * [HTTP/2 Push Targets (Gzipped Embedded)](file-server/http2push-embedded-gzipped/main.go)\r\n    * [Favicon](file-server/favicon/main.go)\r\n    * [Basic](file-server/basic/main.go)\r\n    * [Embedding Files Into App Executable File](file-server/embedding-files-into-app/main.go)\r\n    * [Embedding Files Into App Executable File (Bindata)](file-server/embedding-files-into-app-bindata/main.go)\r\n    * [Embedding Gzipped Files Into App Executable File (Bindata)](file-server/embedding-gzipped-files-into-app-bindata/main.go)\r\n    * [Send Files (rate limiter included)](file-server/send-files/main.go)\r\n    * Single Page Applications\r\n        * [Vue Router](file-server/spa-vue-router)\r\n        * [Basic SPA](file-server/single-page-application/basic/main.go)\r\n        * [Embedded Single Page Application and `iris.PrefixDir`](file-server/single-page-application/embedded-single-page-application/main.go)\r\n        * [Embedded Single Page Application with other routes](file-server/single-page-application/embedded-single-page-application-with-other-routes/main.go)\r\n    * [Upload File](file-server/upload-file/main.go)\r\n    * [Upload Multiple Files](file-server/upload-files/main.go)\r\n    * [WebDAV](file-server/webdav/main.go)\r\n* View\r\n    * [Overview](view/overview/main.go)\r\n    * [Layout](view/layout)\r\n        * [Ace](view/layout/ace)\r\n        * [Blocks](view/layout/blocks)\r\n        * [Django](view/layout/django)\r\n        * [Handlebars](view/layout/handlebars)\r\n        * [HTML](view/layout/html)\r\n        * [Jet](view/layout/jet)\r\n        * [Pug](view/layout/pug)\r\n    * [Basic](view/template_html_0/main.go)\r\n    * [A simple Layout](view/template_html_1/main.go)\r\n    * [Layouts: `yield` and `render` tmpl funcs](view/template_html_2/main.go)\r\n    * The `urlpath` template func\r\n        * [HTML](view/template_html_3/main.go)\r\n        * [Django](view/template_django_1/main.go)\r\n    * [The `url` template func](view/template_html_4/main.go)\r\n    * [Inject Data Between Handlers](view/context-view-data/main.go)\r\n    * [Inject Engine Between Handlers](view/context-view-engine/main.go)\r\n    * [Embedding Templates Into App Executable File](view/embedding-templates-into-app/main.go)\r\n    * [Embedding Templates Into App Executable File (Bindata)](view/embedding-templates-into-app-bindata/main.go)\r\n    * [Write to a custom `io.Writer`](view/write-to)\r\n    * Parse a Template from Text\r\n        * [HTML, Pug and Ace](view/parse-parse/main.go)\r\n        * [Django](view/parse-parse/django/main.go)\r\n        * [Jet](view/parse-parse/jet/main.go)\r\n        * [Handlebars](view/parse-parse/handlebars/main.go)\r\n    * [Blocks](view/template_blocks_0)\r\n    * [Blocks Embedded](view/template_blocks_1_embedded)\r\n    * [Pug: `Actions`](view/template_pug_0)\r\n    * [Pug: `Includes`](view/template_pug_1)\r\n    * [Pug Embedded`](view/template_pug_2_embedded)\r\n    * [Ace](view/template_ace_0)\r\n    * [Django](view/template_django_0)\r\n    * [Jet](view/template_jet_0)\r\n    * [Jet Embedded](view/template_jet_1_embedded)\r\n    * [Jet 'urlpath' tmpl func](view/template_jet_2)\r\n    * [Jet Template Funcs from Struct](view/template_jet_3)\r\n    * [Handlebars](view/template_handlebars_0)\r\n    * Third-Parties\r\n        * [Render `valyala/quicktemplate` templates](view/quicktemplate)\r\n        * [Render `shiyanhui/hero` templates](view/herotemplate)\r\n        * [Render `a-h/templ` templates](view/templ) **NEW**\r\n* [Request ID](https://github.com/kataras/iris/blob/main/middleware/requestid/requestid_test.go)\r\n* [Request Rate Limit](request-ratelimit/main.go)\r\n* [Request Referrer](request-referrer/main.go)\r\n* [Webassembly](webassembly/main.go)\r\n* Request Body\r\n    * [Bind JSON](request-body/read-json/main.go)\r\n    *   * [JSON Stream and disable unknown fields](request-body/read-json-stream/main.go)\r\n    *   * [Struct Validation](request-body/read-json-struct-validation/main.go)\r\n    * [Bind XML](request-body/read-xml/main.go)\r\n    * [Bind MsgPack](request-body/read-msgpack/main.go)\r\n    * [Bind YAML](request-body/read-yaml/main.go)\r\n    * [Bind Form](request-body/read-form/main.go)\r\n        * [Checkboxes](request-body/read-form/checkboxes/main.go)\r\n    * [Bind Query](request-body/read-query/main.go)\r\n    * [Bind Params](request-body/read-params/main.go)\r\n    * [Bind URL](request-body/read-url/main.go)\r\n    * [Bind Headers](request-body/read-headers/main.go)\r\n    * [Bind Body](request-body/read-body/main.go)\r\n    * [Add Converter](request-body/form-query-headers-params-decoder/main.go)\r\n    * [Bind Custom per type](request-body/read-custom-per-type/main.go)\r\n    * [Bind Custom via Unmarshaler](request-body/read-custom-via-unmarshaler/main.go)\r\n    * [Bind Many times](request-body/read-many/main.go)\r\n* Response Writer\r\n    * [Content Negotiation](response-writer/content-negotiation)\r\n    * [Text, Markdown, YAML, HTML, JSON, JSONP, Msgpack, XML and Binary](response-writer/write-rest/main.go)\r\n    * [Third-party JSON Encoder](response-writer/json-third-party/main.go)\r\n    * [Protocol Buffers](response-writer/protobuf/main.go)\r\n    * [HTTP/2 Server Push](response-writer/http2push/main.go)\r\n    * [Stream Writer](response-writer/stream-writer/main.go)\r\n    * [Server-Sent Events](response-writer/sse/main.go)\r\n        * [SSE 3rd-party (r3labs/sse)](response-writer/sse-third-party/main.go)\r\n        * [SSE 3rd-party (alexandrevicenzi/go-sse)](response-writer/sse-third-party-2/main.go)\r\n    * Cache\r\n        * [Simple](response-writer/cache/simple/main.go)\r\n        * [Client-Side (304)](response-writer/cache/client-side/main.go)\r\n* Compression\r\n    * [Server-Side](compression/main.go)\r\n    * [Client-Side](compression/client/main.go)\r\n    * [Client-Side (using Iris)](compress/client-using-iris/main.go)\r\n* Localization and Internationalization\r\n    * [Basic](i18n/basic)\r\n    * [Ttemplates and Functions](i18n/template)\r\n    * [Ttemplates and Functions (Embedded)](i18n/template-embedded)\r\n    * [Pluralization and Variables](i18n/plurals)\r\n* Authentication, Authorization & Bot Detection\r\n    * [Recommended: Auth package and Single-Sign-On](auth/auth) **NEW (GO 1.18 Generics required)**\r\n    * Basic Authentication\r\n        * [Basic](auth/basicauth/basic)\r\n        * [Load from a slice of Users](auth/basicauth/users_list)\r\n        * [Load from a file & encrypted passwords](auth/basicauth/users_file_bcrypt)\r\n        * [Fetch & validate a User from a Database (MySQL)](auth/basicauth/database)\r\n    * [CORS](auth/cors)\r\n    * JSON Web Tokens\r\n        * [Basic](auth/jwt/basic/main.go)\r\n        * [Middleware](auth/jwt/midleware/main.go)\r\n        * [Blocklist](auth/jwt/blocklist/main.go)\r\n        * [Refresh Token](auth/jwt/refresh-token/main.go)\r\n        * [Tutorial](auth/jwt/tutorial)\r\n    * [JWT (community edition)](https://github.com/iris-contrib/middleware/tree/v12/jwt/_example/main.go)\r\n    * [OAUth2](auth/goth/main.go)\r\n    * [Manage Permissions](auth/permissions/main.go)\r\n    * [Google reCAPTCHA](auth/recaptcha/main.go)\r\n    * [hCaptcha](auth/hcaptcha/main.go)\r\n* Cookies\r\n    * [Basic](cookies/basic/main.go)\r\n    * [Options](cookies/options/main.go)\r\n    * [Encode/Decode (with `securecookie`)](cookies/securecookie/main.go)\r\n* Sessions\r\n    * [Overview: Config](sessions/overview/main.go)\r\n        * [Overview: Routes](sessions/overview/example/example.go)\r\n    * [Basic](sessions/basic/main.go)\r\n    * [Secure Cookie](sessions/securecookie/main.go)\r\n    * [Flash Messages](sessions/flash-messages/main.go)\r\n    * [Databases](sessions/database)\r\n        * [Badger](sessions/database/badger/main.go)\r\n        * [BoltDB](sessions/database/boltdb/main.go)\r\n        * [Redis](sessions/database/redis/main.go)\r\n    * [View Data](sessions/viewdata)\r\n* Websocket\r\n    * [Gorilla FileWatch (3rd-party)](websocket/gorilla-filewatch/main.go)\r\n    * [Basic](websocket/basic)\r\n        * [Server](websocket/basic/server.go)\r\n        * [Go Client](websocket/basic/go-client/client.go)\r\n        * [Browser Client](websocket/basic/browser/index.html)\r\n        * [Browser NPM Client (browserify)](websocket/basic/browserify/app.js)\r\n    * [Native Messages](websocket/native-messages/main.go)\r\n    * [TLS](websocket/secure/README.md)\r\n    * [Online Visitors](websocket/online-visitors/main.go)\r\n* Dependency Injection\r\n    * [Overview (Movies Service)](ependency-injection/overview/main.go)\r\n    * [Basic](dependency-injection/basic/main.go)\r\n        * [Middleware](dependency-injection/basic/middleware/main.go)\r\n    * [Sessions](dependency-injection/sessions/main.go)\r\n    * [Smart Contract](dependency-injection/smart-contract/main.go)\r\n    * [JWT](dependency-injection/jwt/main.go)\r\n        * [JWT (iris-contrib)](dependency-injection/jwt/contrib/main.go)\r\n    * [Register Dependency from Context](dependency-injection/context-register-dependency/main.go)\r\n* MVC\r\n    * [Overview](mvc/overview)\r\n    * [Repository and Service layers](mvc/repository)\r\n    * [Hello world](mvc/hello-world/main.go)\r\n    * [Basic](mvc/basic/main.go)\r\n        * [Wildcard](mvc/basic/wildcard/main.go)\r\n    * [Default request values](mvc/request-default-values/main.go)\r\n    * [Singleton](mvc/singleton)\r\n    * [Regexp](mvc/regexp/main.go)\r\n    * [Session Controller](mvc/session-controller/main.go)\r\n    * [Authenticated Controller](mvc/authenticated-controller/main.go)\r\n    * [Versioned Controller](mvc/versioned-controller/main.go)\r\n    * [Websocket Controller](mvc/websocket)\r\n        * [Websocket + Authentication (Single-Sign-On)](mvc/websocket-auth) **NEW (GO 1.18 Generics required)**\r\n    * [Register Middleware](mvc/middleware)\r\n    * [gRPC](mvc/grpc-compatible)\r\n    * [gRPC Bidirectional Stream](mvc/grpc-compatible-bidirectional-stream)\r\n    * [Login (Repository and Service layers)](mvc/login)\r\n    * [Login (Single Responsibility)](mvc/login-mvc-single-responsibility)\r\n    * [Vue.js Todo App](mvc/vuejs-todo-mvc)\r\n    * [HTTP Error Handler](mvc/error-handler-http)\r\n    * [Error Handler](mvc/error-handler)\r\n    * [Handle errors using mvc.Result](mvc/error-handler-custom-result)\r\n    * [Handle errors using PreflightResult](mvc/error-handler-preflight)\r\n    * [Handle errors by hijacking the result](mvc/error-handler-hijack)\r\n* Desktop Applications\r\n    * [The blink package](desktop/blink)\r\n    * [The lorca package](desktop/lorca)\r\n    * [The webview package](desktop/webview)\r\n* Middlewares [(Community)](https://github.com/iris-contrib/middleware)\r\n"
  },
  {
    "path": "_examples/README_ZH_HANT.md",
    "content": "# 章節目錄\n\n- [無伺服器 (Serverless)](https://github.com/iris-contrib/gateway#netlify)\n- [適用 Apache Kafka 的 REST API](kafka-api)\n- [縮網址服務](url-shortener)\n- [Dropzone.js](dropzonejs)\n- [Caddy](caddy)\n- [初始化工具](bootstrapper)\n- [專案結構](project) :fire:\n- 監控\n  - [簡易處理程序監控工具 (含 UI)](monitor/monitor-middleware/main.go) **新範例**\n  - [堆積、MSpan/MCache、Size Classes、物件、Goroutines、GC/CPU 分數 (含 UI)](monitor/statsviz/main.go) **新範例**\n- 資料庫\n  - [MySQL、Groupcache 和 Docker](database/mysql)\n  - [MongoDB](database/mongodb)\n  - [Sqlx](database/orm/sqlx/main.go)\n  - [Gorm](database/orm/gorm/main.go)\n  - [Reform](database/orm/reform/main.go)\n  - [x/sqlx](database/sqlx/main.go) **新範例**\n- HTTP 伺服器\n  - [主機:連線埠](http-server/listen-addr/main.go)\n  - [公開測試網域](http-server/listen-addr-public/main.go)\n  - [UNIX 通訊端 (socket) 檔案](http-server/listen-unix/main.go)\n  - [TLS](http-server/listen-tls/main.go)\n  - [Let's Encrypt（自動發證）](http-server/listen-letsencrypt/main.go)\n  - [通訊端切分 (SO_REUSEPORT)](http-server/socket-sharding/main.go)\n  - [優雅關閉伺服器](http-server/graceful-shutdown/default-notifier/main.go)\n  - [關閉伺服器時通知](http-server/notify-on-shutdown/main.go)\n  - 自訂 TCP 監聽\n    - [通用 net.Listener](http-server/custom-listener/main.go)\n  - 自訂 HTTP 伺服器\n    - [傳入自訂 Server 實體](http-server/custom-httpserver/easy-way/main.go)\n    - [將 Iris 用作單一 http.Handler](http-server/custom-httpserver/std-way/main.go)\n    - [多實體](http-server/custom-httpserver/multi/main.go)\n    - [HTTP/3 QUIC](http-server/http3-quic)\n    - [H2C](http-server/h2c/main.go) **新範例**\n  - [延時 (timeout)](http-server/timeout/main.go)\n- HTTP 用戶端\n  - [天氣用戶端](http-client/weatherapi)\n- 設定\n  - [Functional](configuration/functional/main.go)\n  - [Configuration Struct](configuration/from-configuration-structure/main.go)\n  - [Using Viper](configuration/viper)\n  - [Import from YAML](configuration/from-yaml-file/main.go)\n    - [Share Configuration across instances](configuration/from-yaml-file/shared-configuration/main.go)\n  - [Import from TOML](configuration/from-toml-file/main.go)\n  - [Multi Environment Configuration](configuration/multi-environments) **新範例**\n- 路由\n  - [Party Controller](routing/party-controller) **新範例**\n  - [Overview](routing/overview/main.go)\n  - [Basic](routing/basic/main.go)\n  - [Custom HTTP Errors](routing/http-errors/main.go)\n  - [HTTP Wire Errors](routing/http-wire-errors/main.go) **新範例**\n  - [Not Found - Intelligence](routing/intelligence/main.go)\n    - [Not Found - Suggest Closest Paths](routing/intelligence/manual/main.go)\n  - [Dynamic Path](routing/dynamic-path/main.go)\n    - [At-username](routing/dynamic-path/at-username/main.go)\n    - [Root Wildcard](routing/dynamic-path/root-wildcard/main.go)\n    - [Implement a Parameter Type](routing/macros/main.go)\n    - [Same Path Pattern but Func](routing/dynamic-path/same-pattern-different-func/main.go)\n  - Middleware\n    - [Per Route](routing/writing-a-middleware/per-route/main.go)\n    - [Globally](routing/writing-a-middleware/globally/main.go)\n    - [Remove a Handler](routing/remove-handler/main.go)\n    - Share Values\n      - [Share Services](routing/writing-a-middleware/share-services/main.go)\n      - [Share Functions](routing/writing-a-middleware/share-funcs/main.go)\n    - [Handlers Execution Rule](routing/route-handlers-execution-rules/main.go)\n    - [Route Register Rule](routing/route-register-rule/main.go)\n    - Convert net/http Handlers\n      - [From func(w http.ResponseWriter, r \\*http.Request, next http.HandlerFunc)](convert-handlers/negroni-like/main.go)\n      - [From http.Handler or http.HandlerFunc](convert-handlers/nethttp/main.go)\n      - [From func(http.Handler) http.Handler](convert-handlers/wrapper/main.go)\n      - [Convert by your own: sentry/raven middleware](convert-handlers/real-usecase-raven/writing-middleware/main.go)\n  - [Rewrite Middleware](routing/rewrite/main.go)\n  - [Route State](routing/route-state/main.go)\n  - [Remove Route](routing/remove-route/main.go)\n  - [Reverse Routing](routing/reverse/main.go)\n  - [Router Wrapper](routing/custom-wrapper/main.go)\n  - [Custom Router](routing/custom-router/main.go)\n  - Subdomains\n    - [Single](routing/subdomains/single/main.go)\n    - [Multi](routing/subdomains/multi/main.go)\n    - [Wildcard](routing/subdomains/wildcard/main.go)\n    - [WWW](routing/subdomains/www/main.go)\n      - [WWW Method](routing/subdomains/www/www-method/main.go)\n    - [Redirection](routing/subdomains/redirect/main.go)\n      - [Multi Instances](routing/subdomains/redirect/multi-instances/main.go)\n    - [HTTP Errors View](routing/subdomains/http-errors-view/main.go)\n  - [HTTP Method Override](https://github.com/kataras/iris/blob/main/middleware/methodoverride/methodoverride_test.go)\n  - [API Versioning](routing/versioning/main.go)\n  - [Sitemap](routing/sitemap/main.go)\n- 日誌\n  - [Application File Logger](logging/file-logger/main.go)\n  - [Application JSON Logger](logging/json-logger/main.go)\n  - [Rollbar](logging/rollbar/main.go)\n  - AccessLog\n    - [Log Requests to a JSON File](logging/request-logger/accesslog-simple/main.go)\n    - [Using Log Rotation and more](logging/request-logger/accesslog)\n    - [Custom Fields and Template](logging/request-logger/accesslog-template/main.go)\n    - [Listen and render Logs to a Client](logging/request-logger/accesslog-broker/main.go)\n    - [The CSV Formatter](logging/request-logger/accesslog-csv/main.go)\n    - [Create your own Formatter](logging/request-logger/accesslog-formatter/main.go)\n    - [Root and Proxy AccessLog instances](logging/request-logger/accesslog-proxy/main.go)\n    - [Slack integration example](logging/request-logger/accesslog-slack/main.go)\n- API 文件\n  - [Swagger](https://github.com/iris-contrib/swagger/tree/master/_examples/basic)\n- 測試\n  - [Testing with httptest](testing/httptest/main_test.go)\n  - [Testing with ginkgo](testing/ginkgotest)\n- [救援錯誤](recover/main.go)\n  - [Panic and custom Error Handler with Compression](recover/panic-and-custom-error-handler-with-compression/main.go)\n- [效能分析 (Profiling)](pprof/main.go)\n- 檔案伺服器\n  - [檔案伺服器](file-server/file-server/main.go)\n  - [HTTP/2 Push Targets](file-server/http2push/main.go)\n  - [HTTP/2 Push Targets (Embedded)](file-server/http2push-embedded/main.go)\n  - [HTTP/2 Push Targets (Gzipped Embedded)](file-server/http2push-embedded-gzipped/main.go)\n  - [Favicon](file-server/favicon/main.go)\n  - [Basic](file-server/basic/main.go)\n  - [Embedding Files Into App Executable File](file-server/embedding-files-into-app/main.go)\n  - [Embedding Files Into App Executable File (Bindata)](file-server/embedding-files-into-app-bindata/main.go)\n  - [Embedding Gzipped Files Into App Executable File (Bindata)](file-server/embedding-gzipped-files-into-app-bindata/main.go)\n  - [Send Files (rate limiter included)](file-server/send-files/main.go)\n  - 單頁面應用程式\n    - [Vue Router](file-server/spa-vue-router)\n    - [Basic SPA](file-server/single-page-application/basic/main.go)\n    - [Embedded Single Page Application and `iris.PrefixDir`](file-server/single-page-application/embedded-single-page-application/main.go)\n    - [Embedded Single Page Application with other routes](file-server/single-page-application/embedded-single-page-application-with-other-routes/main.go)\n  - [Upload File](file-server/upload-file/main.go)\n  - [Upload Multiple Files](file-server/upload-files/main.go)\n  - [WebDAV](file-server/webdav/main.go)\n- 檢視\n  - [概覽](view/overview/main.go)\n  - [排版引擎](view/layout)\n    - [Ace](view/layout/ace)\n    - [Blocks](view/layout/blocks)\n    - [Django](view/layout/django)\n    - [Handlebars](view/layout/handlebars)\n    - [HTML](view/layout/html)\n    - [Jet](view/layout/jet)\n    - [Pug](view/layout/pug)\n  - [基礎](view/template_html_0/main.go)\n  - [A simple Layout](view/template_html_1/main.go)\n  - [Layouts: `yield` and `render` tmpl funcs](view/template_html_2/main.go)\n  - `urlpath` 樣板函式\n    - [HTML](view/template_html_3/main.go)\n    - [Django](view/template_django_1/main.go)\n  - [`url` 樣板函式](view/template_html_4/main.go)\n  - [Inject Data Between Handlers](view/context-view-data/main.go)\n  - [Inject Engine Between Handlers](view/context-view-engine/main.go)\n  - [Embedding Templates Into App Executable File](view/embedding-templates-into-app/main.go)\n  - [Embedding Templates Into App Executable File (Bindata)](view/embedding-templates-into-app-bindata/main.go)\n  - [Write to a custom `io.Writer`](view/write-to)\n  - 從文字解析樣板\n    - [HTML, Pug and Ace](view/parse-parse/main.go)\n    - [Django](view/parse-parse/django/main.go)\n    - [Jet](view/parse-parse/jet/main.go)\n    - [Handlebars](view/parse-parse/handlebars/main.go)\n  - [Blocks](view/template_blocks_0)\n  - [Blocks Embedded](view/template_blocks_1_embedded)\n  - [Pug: `Actions`](view/template_pug_0)\n  - [Pug: `Includes`](view/template_pug_1)\n  - [Pug Embedded`](view/template_pug_2_embedded)\n  - [Ace](view/template_ace_0)\n  - [Django](view/template_django_0)\n  - [Jet](view/template_jet_0)\n  - [Jet Embedded](view/template_jet_1_embedded)\n  - [Jet 'urlpath' tmpl func](view/template_jet_2)\n  - [Jet Template Funcs from Struct](view/template_jet_3)\n  - [Handlebars](view/template_handlebars_0)\n  - 第三方引擎\n    - [Render `valyala/quicktemplate` templates](view/quicktemplate)\n    - [Render `shiyanhui/hero` templates](view/herotemplate)\n- [請求 ID](https://github.com/kataras/iris/blob/main/middleware/requestid/requestid_test.go)\n- [請求速率限制](request-ratelimit/main.go)\n- [請求 Referrer](request-referrer/main.go)\n- [Webassembly](webassembly/main.go)\n- 請求本文\n  - [綁定 JSON](request-body/read-json/main.go)\n    - [JSON Stream and disable unknown fields](request-body/read-json-stream/main.go)\n    - [Struct Validation](request-body/read-json-struct-validation/main.go)\n  - [Bind XML](request-body/read-xml/main.go)\n  - [Bind MsgPack](request-body/read-msgpack/main.go)\n  - [Bind YAML](request-body/read-yaml/main.go)\n  - [Bind Form](request-body/read-form/main.go)\n    - [Checkboxes](request-body/read-form/checkboxes/main.go)\n  - [Bind Query](request-body/read-query/main.go)\n  - [Bind Params](request-body/read-params/main.go)\n  - [Bind URL](request-body/read-url/main.go)\n  - [Bind Headers](request-body/read-headers/main.go)\n  - [Bind Body](request-body/read-body/main.go)\n  - [Add Converter](request-body/form-query-headers-params-decoder/main.go)\n  - [Bind Custom per type](request-body/read-custom-per-type/main.go)\n  - [Bind Custom via Unmarshaler](request-body/read-custom-via-unmarshaler/main.go)\n  - [Bind Many times](request-body/read-many/main.go)\n- 請求寫入器\n  - [Content Negotiation](response-writer/content-negotiation)\n  - [Text, Markdown, YAML, HTML, JSON, JSONP, Msgpack, XML and Binary](response-writer/write-rest/main.go)\n  - [Third-party JSON Encoder](response-writer/json-third-party/main.go)\n  - [Protocol Buffers](response-writer/protobuf/main.go)\n  - [HTTP/2 Server Push](response-writer/http2push/main.go)\n  - [Stream Writer](response-writer/stream-writer/main.go)\n  - [Server-Sent Events](response-writer/sse/main.go)\n    - [SSE 3rd-party (r3labs/sse)](response-writer/sse-third-party/main.go)\n    - [SSE 3rd-party (alexandrevicenzi/go-sse)](response-writer/sse-third-party-2/main.go)\n  - 快取\n    - [Simple](response-writer/cache/simple/main.go)\n    - [Client-Side (304)](response-writer/cache/client-side/main.go)\n- 壓縮\n  - [Server-Side](compression/main.go)\n  - [Client-Side](compression/client/main.go)\n  - [Client-Side (using Iris)](compress/client-using-iris/main.go)\n- 本地化與國際化\n  - [基礎](i18n/basic)\n  - [樣板與函式](i18n/template)\n  - [樣板與函式 (嵌入式)](i18n/template-embedded)\n  - [複數形與變數](i18n/plurals)\n- 認證、授權與機器人偵測\n  - [推薦：Auth 套件與單點登入](auth/auth) **新範例（需要 GO 1.18 的泛型功能）**\n  - 基礎認證\n    - [Basic](auth/basicauth/basic)\n    - [Load from a slice of Users](auth/basicauth/users_list)\n    - [Load from a file & encrypted passwords](auth/basicauth/users_file_bcrypt)\n    - [Fetch & validate a User from a Database (MySQL)](auth/basicauth/database)\n  - [CORS](auth/cors)\n  - JSON Web Tokens\n    - [Basic](auth/jwt/basic/main.go)\n    - [Middleware](auth/jwt/midleware/main.go)\n    - [Blocklist](auth/jwt/blocklist/main.go)\n    - [Refresh Token](auth/jwt/refresh-token/main.go)\n    - [Tutorial](auth/jwt/tutorial)\n  - [JWT (community edition)](https://github.com/iris-contrib/middleware/tree/v12/jwt/_example/main.go)\n  - [OAUth2](auth/goth/main.go)\n  - [Manage Permissions](auth/permissions/main.go)\n  - [Google reCAPTCHA](auth/recaptcha/main.go)\n  - [hCaptcha](auth/hcaptcha/main.go)\n- Cookies\n  - [Basic](cookies/basic/main.go)\n  - [Options](cookies/options/main.go)\n  - [Encode/Decode (with `securecookie`)](cookies/securecookie/main.go)\n- 連線階段\n  - [概觀：組態設定](sessions/overview/main.go)\n    - [概觀：路由](sessions/overview/example/example.go)\n  - [Basic](sessions/basic/main.go)\n  - [Secure Cookie](sessions/securecookie/main.go)\n  - [Flash Messages](sessions/flash-messages/main.go)\n  - [Databases](sessions/database)\n    - [Badger](sessions/database/badger/main.go)\n    - [BoltDB](sessions/database/boltdb/main.go)\n    - [Redis](sessions/database/redis/main.go)\n  - [View Data](sessions/viewdata)\n- Websocket\n  - [Gorilla FileWatch (3rd-party)](websocket/gorilla-filewatch/main.go)\n  - [Basic](websocket/basic)\n    - [Server](websocket/basic/server.go)\n    - [Go Client](websocket/basic/go-client/client.go)\n    - [Browser Client](websocket/basic/browser/index.html)\n    - [Browser NPM Client (browserify)](websocket/basic/browserify/app.js)\n  - [Native Messages](websocket/native-messages/main.go)\n  - [TLS](websocket/secure/README.md)\n  - [Online Visitors](websocket/online-visitors/main.go)\n- 依賴注入\n  - [概觀 (電影服務)](ependency-injection/overview/main.go)\n  - [Basic](dependency-injection/basic/main.go)\n    - [Middleware](dependency-injection/basic/middleware/main.go)\n  - [Sessions](dependency-injection/sessions/main.go)\n  - [Smart Contract](dependency-injection/smart-contract/main.go)\n  - [JWT](dependency-injection/jwt/main.go)\n    - [JWT (iris-contrib)](dependency-injection/jwt/contrib/main.go)\n  - [Register Dependency from Context](dependency-injection/context-register-dependency/main.go)\n- MVC\n  - [Overview](mvc/overview)\n  - [Repository and Service layers](mvc/repository)\n  - [Hello world](mvc/hello-world/main.go)\n  - [Basic](mvc/basic/main.go)\n    - [Wildcard](mvc/basic/wildcard/main.go)\n  - [Default request values](mvc/request-default-values/main.go)\n  - [Singleton](mvc/singleton)\n  - [Regexp](mvc/regexp/main.go)\n  - [Session Controller](mvc/session-controller/main.go)\n  - [Authenticated Controller](mvc/authenticated-controller/main.go)\n  - [Versioned Controller](mvc/versioned-controller/main.go)\n  - [Websocket Controller](mvc/websocket)\n    - [Websocket + Authentication (Single-Sign-On)](mvc/websocket-auth) **新範例（需要 GO 1.18 的泛型功能）**\n  - [Register Middleware](mvc/middleware)\n  - [gRPC](mvc/grpc-compatible)\n  - [gRPC Bidirectional Stream](mvc/grpc-compatible-bidirectional-stream)\n  - [Login (Repository and Service layers)](mvc/login)\n  - [Login (Single Responsibility)](mvc/login-mvc-single-responsibility)\n  - [Vue.js Todo App](mvc/vuejs-todo-mvc)\n  - [HTTP Error Handler](mvc/error-handler-http)\n  - [Error Handler](mvc/error-handler)\n  - [Handle errors using mvc.Result](mvc/error-handler-custom-result)\n  - [Handle errors using PreflightResult](mvc/error-handler-preflight)\n  - [Handle errors by hijacking the result](mvc/error-handler-hijack)\n- 桌面應用程式\n  - [blink 套件](desktop/blink)\n  - [lorca 套件](desktop/lorca)\n  - [webview 套件](desktop/webview)\n- 中介模組 [(社群)](https://github.com/iris-contrib/middleware)\n"
  },
  {
    "path": "_examples/apidoc/swagger/README.md",
    "content": "# Swagger 2.0\n\nVisit https://github.com/iris-contrib/swagger instead.\n"
  },
  {
    "path": "_examples/auth/auth/README.md",
    "content": "# Auth Package (+ Single Sign On)\n\n```sh\n$ go run .\n```\n\n1. GET/POST: http://localhost:8080/signin\n2. GET: http://localhost:8080/member\n3. GET: http://localhost:8080/owner\n4. POST: http://localhost:8080/refresh\n5. GET: http://localhost:8080/signout\n6. GET: http://localhost:8080/signout-all"
  },
  {
    "path": "_examples/auth/auth/auth.yml",
    "content": "Headers: # required.\n  - \"Authorization\"\n  - \"X-Authorization\"\nCookie: # optional.\n  Name: \"iris_auth_cookie\"\n  Secure: false\n  Hash: \"D*G-KaPdSgUkXp2s5v8y/B?E(H+MbQeThWmYq3t6w9z$C&F)J@NcRfUjXn2r4u7x\" # length of 64 characters (512-bit).\n  Block: \"VkYp3s6v9y$B&E)H@McQfTjWmZq4t7w!\" # length of 32 characters (256-bit).\nKeys:\n  - ID: IRIS_AUTH_ACCESS # required.\n    Alg: EdDSA\n    MaxAge: 2h # 2 hours lifetime for access tokens. \n    Private: |+\n      -----BEGIN PRIVATE KEY-----\n      MC4CAQAwBQYDK2VwBCIEIFdZWoDdFny5SMnP9Fyfr8bafi/B527EVZh8JJjDTIFO\n      -----END PRIVATE KEY-----\n    Public: |+\n      -----BEGIN PUBLIC KEY-----\n      MCowBQYDK2VwAyEAzpgjKSr9E032DX+foiOxq1QDsbzjLxagTN+yVpGWZB4=\n      -----END PUBLIC KEY-----\n  - ID: IRIS_AUTH_REFRESH # optional. Good practise to have it though.\n    Alg: EdDSA\n    # 1 month lifetime for refresh tokens,\n    # after that period the user has to signin again.\n    MaxAge: 720h\n    Private: |+\n      -----BEGIN PRIVATE KEY-----\n      MC4CAQAwBQYDK2VwBCIEIHJ1aoIjA2sRp5eqGjGR3/UMucrHbBdBv9p8uwfzZ1KZ\n      -----END PRIVATE KEY-----\n    Public: |+\n      -----BEGIN PUBLIC KEY-----\n      MCowBQYDK2VwAyEAsKKAr+kDtfAqwG7cZdoEAfh9jHt9W8qi9ur5AA1KQAQ=\n      -----END PUBLIC KEY-----\n    # Example of setting a binary form of the encryption key for refresh tokens,\n    # it could be a \"string\" as well.\n    EncryptionKey: !!binary stSNLTu91YyihPxzeEOXKwGVMG00CjcC/68G8nMgmqA=\n"
  },
  {
    "path": "_examples/auth/auth/main.go",
    "content": "//go:build go1.18\n// +build go1.18\n\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/auth\"\n)\n\nfunc allowRole(role AccessRole) auth.VerifyUserFunc[User] {\n\treturn func(u User) error {\n\t\tif !u.Role.Allow(role) {\n\t\t\treturn fmt.Errorf(\"invalid role\")\n\t\t}\n\n\t\treturn nil\n\t}\n}\n\nconst configFilename = \"./auth.yml\"\n\nfunc main() {\n\tapp := iris.New()\n\tapp.RegisterView(iris.Blocks(iris.Dir(\"./views\"), \".html\").\n\t\tLayoutDir(\"layouts\").\n\t\tLayout(\"main\"))\n\n\t/*\n\t\t// Easiest 1-liner way, load from configuration and initialize a new auth instance:\n\t\ts := auth.MustLoad[User](\"./auth.yml\")\n\t\t// Bind a configuration from file:\n\t\tvar c auth.Configuration\n\t\tc.BindFile(\"./auth.yml\")\n\t\ts, err := auth.New[User](c)\n\t\t// OR create new programmatically configuration:\n\t\tconfig := auth.Configuration{\n\t\t\t...fields\n\t\t}\n\t\ts, err := auth.New[User](config)\n\t\t// OR generate a new configuration:\n\t\tconfig := auth.MustGenerateConfiguration()\n\t\ts, err := auth.New[User](config)\n\t\t// OR generate a new config and save it if cannot open the config file.\n\t\tif _, err := os.Stat(configFilename); err != nil {\n\t\t    generatedConfig := auth.MustGenerateConfiguration()\n\t\t    configContents, err := generatedConfig.ToYAML()\n\t\t    if err != nil {\n\t\t        panic(err)\n\t\t    }\n\n\t\t    err = os.WriteFile(configFilename, configContents, 0600)\n\t\t    if err != nil {\n\t\t        panic(err)\n\t\t    }\n\t\t}\n\t*/\n\n\t// 1. Load configuration from a file.\n\tauthConfig, err := auth.LoadConfiguration(configFilename)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// 2. Initialize a new auth instance for \"User\" claims (generics: go1.18 +).\n\ts, err := auth.New[User](authConfig)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// 3. Add a custom provider, in our case is just a memory-based one.\n\ts.AddProvider(NewProvider())\n\t// 3.1. Optionally set a custom error handler.\n\t// s.SetErrorHandler(new(auth.DefaultErrorHandler))\n\n\tapp.Get(\"/signin\", renderSigninForm)\n\t// 4. generate token pairs.\n\tapp.Post(\"/signin\", s.SigninHandler)\n\t// 5. refresh token pairs.\n\tapp.Post(\"/refresh\", s.RefreshHandler)\n\t// 6. calls the provider's InvalidateToken method.\n\tapp.Get(\"/signout\", s.SignoutHandler)\n\t// 7. calls the provider's InvalidateTokens method.\n\tapp.Get(\"/signout-all\", s.SignoutAllHandler)\n\n\t// 8.1. allow access for users with \"Member\" role.\n\tapp.Get(\"/member\", s.VerifyHandler(allowRole(Member)), renderMemberPage(s))\n\t// 8.2. allow access for users with \"Owner\" role.\n\tapp.Get(\"/owner\", s.VerifyHandler(allowRole(Owner)), renderOwnerPage(s))\n\n\t/* Subdomain user verify:\n\tapp.Subdomain(\"owner\", s.VerifyHandler(allowRole(Owner))).Get(\"/\", renderOwnerPage(s))\n\t*/\n\tapp.Listen(\":8080\", iris.WithOptimizations) // Setup HTTPS/TLS for production instead.\n\t/* Test subdomain user verify, one way is ingrok,\n\t   add the below to the arguments above:\n\t, iris.WithConfiguration(iris.Configuration{\n\t\tEnableOptmizations: true,\n\t\tTunneling: iris.TunnelingConfiguration{\n\t\t\tAuthToken: \"YOUR_AUTH_TOKEN\",\n\t\t\tRegion:    \"us\",\n\t\t\tTunnels: []tunnel.Tunnel{\n\t\t\t\t{\n\t\t\t\t\tName:     \"Iris Auth (Test)\",\n\t\t\t\t\tAddr:     \":8080\",\n\t\t\t\t\tHostname: \"YOUR_DOMAIN\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tName:     \"Iris Auth (Test Subdomain)\",\n\t\t\t\t\tAddr:     \":8080\",\n\t\t\t\t\tHostname: \"owner.YOUR_DOMAIN\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t})*/\n}\n\nfunc renderSigninForm(ctx iris.Context) {\n\tif err := ctx.View(\"signin\", iris.Map{\"Title\": \"Signin Page\"}); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n}\n\nfunc renderMemberPage(s *auth.Auth[User]) iris.Handler {\n\treturn func(ctx iris.Context) {\n\t\tuser := s.GetUser(ctx)\n\t\tctx.Writef(\"Hello member: %s\\n\", user.Email)\n\t}\n}\n\nfunc renderOwnerPage(s *auth.Auth[User]) iris.Handler {\n\treturn func(ctx iris.Context) {\n\t\tuser := s.GetUser(ctx)\n\t\tctx.Writef(\"Hello owner: %s\\n\", user.Email)\n\t}\n}\n"
  },
  {
    "path": "_examples/auth/auth/user.go",
    "content": "//go:build go1.18\n// +build go1.18\n\npackage main\n\ntype AccessRole uint16\n\nfunc (r AccessRole) Is(v AccessRole) bool {\n\treturn r&v != 0\n}\n\nfunc (r AccessRole) Allow(v AccessRole) bool {\n\treturn r&v >= v\n}\n\nconst (\n\tInvalidAccessRole AccessRole = 1 << iota\n\tRead\n\tWrite\n\tDelete\n\n\tOwner  = Read | Write | Delete\n\tMember = Read | Write\n)\n\ntype User struct {\n\tID    string     `json:\"id\"`\n\tEmail string     `json:\"email\"`\n\tRole  AccessRole `json:\"role\"`\n}\n\nfunc (u User) GetID() string {\n\treturn u.ID\n}\n"
  },
  {
    "path": "_examples/auth/auth/user_provider.go",
    "content": "//go:build go1.18\n// +build go1.18\n\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/auth\"\n)\n\ntype Provider struct {\n\tdataset []User\n\n\tinvalidated    map[string]struct{} // key = token. Entry is blocked.\n\tinvalidatedAll map[string]int64    // key = user id, value = timestamp. Issued before is consider invalid.\n\tmu             sync.RWMutex\n}\n\nfunc NewProvider() *Provider {\n\treturn &Provider{\n\t\tdataset: []User{\n\t\t\t{\n\t\t\t\tID:    \"id-1\",\n\t\t\t\tEmail: \"kataras2006@hotmail.com\",\n\t\t\t\tRole:  Owner,\n\t\t\t},\n\t\t\t{\n\t\t\t\tID:    \"id-2\",\n\t\t\t\tEmail: \"example@example.com\",\n\t\t\t\tRole:  Member,\n\t\t\t},\n\t\t},\n\t\tinvalidated:    make(map[string]struct{}),\n\t\tinvalidatedAll: make(map[string]int64),\n\t}\n}\n\nfunc (p *Provider) Signin(ctx context.Context, username, password string) (User, error) { // fired on SigninHandler.\n\t// your database...\n\tfor _, user := range p.dataset {\n\t\tif user.Email == username {\n\t\t\treturn user, nil\n\t\t}\n\t}\n\n\treturn User{}, fmt.Errorf(\"user not found\")\n}\n\nfunc (p *Provider) ValidateToken(ctx context.Context, standardClaims auth.StandardClaims, u User) error { // fired on VerifyHandler.\n\t// your database and checks of blocked tokens...\n\n\t// check for specific token ids.\n\tp.mu.RLock()\n\t_, tokenBlocked := p.invalidated[standardClaims.ID]\n\tif !tokenBlocked {\n\t\t// this will disallow refresh tokens with origin jwt token id as the blocked access token as well.\n\t\tif standardClaims.OriginID != \"\" {\n\t\t\t_, tokenBlocked = p.invalidated[standardClaims.OriginID]\n\t\t}\n\t}\n\tp.mu.RUnlock()\n\n\tif tokenBlocked {\n\t\treturn fmt.Errorf(\"token was invalidated\")\n\t}\n\t//\n\n\t// check all tokens issuet before the \"InvalidateToken\" method was fired for this user.\n\tp.mu.RLock()\n\tts, oldUserBlocked := p.invalidatedAll[u.ID]\n\tp.mu.RUnlock()\n\n\tif oldUserBlocked && standardClaims.IssuedAt <= ts {\n\t\treturn fmt.Errorf(\"token was invalidated\")\n\t}\n\t//\n\n\treturn nil // else valid.\n}\n\nfunc (p *Provider) InvalidateToken(ctx context.Context, standardClaims auth.StandardClaims, u User) error { // fired on SignoutHandler.\n\t// invalidate this specific token.\n\tp.mu.Lock()\n\tp.invalidated[standardClaims.ID] = struct{}{}\n\tp.mu.Unlock()\n\n\treturn nil\n}\n\nfunc (p *Provider) InvalidateTokens(ctx context.Context, u User) error { // fired on SignoutAllHandler.\n\t// invalidate all previous tokens came from \"u\".\n\tp.mu.Lock()\n\tp.invalidatedAll[u.ID] = time.Now().Unix()\n\tp.mu.Unlock()\n\n\treturn nil\n}\n"
  },
  {
    "path": "_examples/auth/auth/views/layouts/main.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>{{ if .Title }}{{ .Title }}{{ else }}Default Main Title{{ end }}</title>\n</head>\n<style>\n    body {\n        margin: 0;\n        display: flex;\n        min-height: 100vh;\n        flex-direction: column;\n    }\n    main {\n        display: block;\n        flex: 1 0 auto;\n    }\n    .container {\n        max-width: 500px;\n        margin: auto;\n    }\n</style>\n<body>\n    <div class=\"container\">\n        <main>{{ template \"content\" . }}</main>\n        <footer style=\"position: fixed; bottom: 0; width: 100%;\">{{ partial \"partials/footer\" . }}</footer>\n    </div>\n</body>\n</html>"
  },
  {
    "path": "_examples/auth/auth/views/partials/footer.html",
    "content": "<i>Iris Web Framework &copy; 2022</i>"
  },
  {
    "path": "_examples/auth/auth/views/signin.html",
    "content": "<div class=\"user_signin\">\n    <form action=\"\" method=\"post\">\n        <label for=\"username\">Email:</label>\n        <input name=\"username\" type=\"email\" />\n        <label for=\"password\">Password:</label>\n        <input name=\"password\" type=\"password\" />\n        <input type=\"submit\" value=\"Sign in\" />\n    </form>\n</div>"
  },
  {
    "path": "_examples/auth/basicauth/basic/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/x/errors\"\n\n\t\"github.com/kataras/iris/v12/middleware/basicauth\"\n)\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\n\t/*\n\t\topts := basicauth.Options{\n\t\t\tRealm:  \"Authorization Required\",\n\t\t\tMaxAge: 30 * time.Minute,\n\t\t\tGC: basicauth.GC{\n\t\t\t\tEvery: 2 * time.Hour,\n\t\t\t},\n\t\t\tAllow: basicauth.AllowUsers(map[string]string{\n\t\t\t\t\"myusername\":       \"mypassword\",\n\t\t\t\t\"mySecondusername\": \"mySecondpassword\",\n\t\t\t}),\n\t\t\tMaxTries: 2,\n\t\t}\n\t\tauth := basicauth.New(opts)\n\n\t\tOR simply:\n\t*/\n\n\tauth := basicauth.Default(map[string]string{\n\t\t\"myusername\":       \"mypassword\",\n\t\t\"mySecondusername\": \"mySecondpassword\",\n\t})\n\n\t// To the next routes of a party (group of routes):\n\t/*\n\t\tapp.Use(auth)\n\t*/\n\n\t// For global effect, including not founds:\n\t/*\n\t\tapp.UseRouter(auth)\n\t*/\n\n\t// For global effect, excluding http errors such as not founds:\n\t/*\n\t\tapp.UseGlobal(auth) or app.Use(auth) before any route registered.\n\t*/\n\n\t// For single/per routes:\n\t/*\n\t\tapp.Get(\"/mysecret\", auth, h)\n\t*/\n\n\tapp.Get(\"/\", func(ctx iris.Context) { ctx.Redirect(\"/admin\") })\n\n\t// to party\n\n\tneedAuth := app.Party(\"/admin\", auth)\n\t{\n\t\t//http://localhost:8080/admin\n\t\tneedAuth.Get(\"/\", handler)\n\t\t// http://localhost:8080/admin/profile\n\t\tneedAuth.Get(\"/profile\", handler)\n\n\t\t// http://localhost:8080/admin/settings\n\t\tneedAuth.Get(\"/settings\", handler)\n\n\t\tneedAuth.Get(\"/logout\", logout)\n\t}\n\n\treturn app\n}\n\nfunc main() {\n\tapp := newApp()\n\t// open http://localhost:8080/admin\n\tapp.Listen(\":8080\")\n}\n\nfunc handler(ctx iris.Context) {\n\t// user := ctx.User().(*myUserType)\n\t// or ctx.User().GetRaw().(*myUserType)\n\t// ctx.Writef(\"%s %s:%s\", ctx.Path(), user.Username, user.Password)\n\t// OR if you don't have registered custom User structs:\n\tusername, password, _ := ctx.Request().BasicAuth()\n\tctx.Writef(\"%s %s:%s\", ctx.Path(), username, password)\n}\n\nfunc logout(ctx iris.Context) {\n\t// fires 401, invalidates the basic auth,\n\t// logout through javascript and ajax is a better solution though.\n\terr := ctx.Logout()\n\tif err != nil {\n\t\terrors.Internal.Err(ctx, err)\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "_examples/auth/basicauth/basic/main_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nfunc TestBasicAuth(t *testing.T) {\n\tapp := newApp()\n\te := httptest.New(t, app)\n\n\t// redirects to /admin without basic auth\n\te.GET(\"/\").Expect().Status(httptest.StatusUnauthorized)\n\t// without basic auth\n\te.GET(\"/admin\").Expect().Status(httptest.StatusUnauthorized)\n\n\t// with valid basic auth\n\te.GET(\"/admin\").WithBasicAuth(\"myusername\", \"mypassword\").Expect().\n\t\tStatus(httptest.StatusOK).Body().IsEqual(\"/admin myusername:mypassword\")\n\te.GET(\"/admin/profile\").WithBasicAuth(\"myusername\", \"mypassword\").Expect().\n\t\tStatus(httptest.StatusOK).Body().IsEqual(\"/admin/profile myusername:mypassword\")\n\te.GET(\"/admin/settings\").WithBasicAuth(\"myusername\", \"mypassword\").Expect().\n\t\tStatus(httptest.StatusOK).Body().IsEqual(\"/admin/settings myusername:mypassword\")\n\n\t// with invalid basic auth\n\te.GET(\"/admin/settings\").WithBasicAuth(\"invalidusername\", \"invalidpassword\").\n\t\tExpect().Status(httptest.StatusUnauthorized)\n}\n"
  },
  {
    "path": "_examples/auth/basicauth/database/Dockerfile",
    "content": "# docker build -t myapp . \n# docker run --rm -it -p 8080:8080 myapp:latest\nFROM golang:latest AS builder\nRUN apt-get update\nENV GO111MODULE=on \\\n    CGO_ENABLED=0 \\\n    GOOS=linux \\\n    GOARCH=amd64\nWORKDIR /go/src/app\nCOPY go.mod .\nRUN go mod download\n# cache step\nCOPY . .\nRUN go install\n\nFROM scratch\nCOPY --from=builder /go/bin/myapp .\nENTRYPOINT [\"./myapp\"]"
  },
  {
    "path": "_examples/auth/basicauth/database/README.md",
    "content": "# BasicAuth + MySQL & Docker Example\n\n## ⚡ Get Started\n\nDownload the folder.\n\n### Install (Docker)\n\nInstall [Docker](https://www.docker.com/) and execute the command below\n\n```sh\n$ docker-compose up --build\n```\n\n### Install (Manually)\n\nRun `go build -mod=mod` or `go run -mod=mod main.go` and read below.\n\n#### MySQL\n\nEnvironment variables:\n\n```sh\nMYSQL_USER=user_myapp\nMYSQL_PASSWORD=dbpassword\nMYSQL_HOST=localhost\nMYSQL_DATABASE=myapp\n```\n\nDownload the schema from [migration/db.sql](migration/db.sql) and execute it against your MySQL server instance.\n\n<http://localhost:8080>\n\n```sh\nusername: admin\npassword: admin\n```\n\n```sh\nusername: iris\npassword: iris_password\n```\n\nThe example does not contain code to add a user to the database, as this is out of the scope of this middleware. More features can be implemented by end-developers.\n"
  },
  {
    "path": "_examples/auth/basicauth/database/docker-compose.yml",
    "content": "version: '3.1'\n\nservices:\n  db:\n    image: mysql\n    command: --default-authentication-plugin=mysql_native_password\n    environment:\n      MYSQL_ROOT_PASSWORD: dbpassword\n      MYSQL_DATABASE: myapp\n      MYSQL_USER: user_myapp\n      MYSQL_PASSWORD: dbpassword\n    tty: true\n    volumes:\n    - ./migration:/docker-entrypoint-initdb.d\n  app:\n    build: .\n    ports:\n      - 8080:8080\n    environment:\n      PORT: 8080\n      MYSQL_USER: user_myapp\n      MYSQL_PASSWORD: dbpassword\n      MYSQL_DATABASE: myapp\n      MYSQL_HOST: db\n    restart: on-failure\n    healthcheck:\n        test: [\"CMD\", \"curl\", \"-f\", \"tcp://db:3306\"]\n        interval: 30s\n        timeout: 10s\n        retries: 10\n    depends_on:\n      - db"
  },
  {
    "path": "_examples/auth/basicauth/database/go.mod",
    "content": "module myapp\n\ngo 1.25\n\nrequire (\n\tgithub.com/go-sql-driver/mysql v1.9.3\n\tgithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd\n)\n\nrequire (\n\tfilippo.io/edwards25519 v1.1.0 // indirect\n\tgithub.com/BurntSushi/toml v1.6.0 // indirect\n\tgithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect\n\tgithub.com/CloudyKit/jet/v6 v6.3.1 // indirect\n\tgithub.com/Joker/jade v1.1.3 // indirect\n\tgithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 // indirect\n\tgithub.com/andybalholm/brotli v1.2.0 // indirect\n\tgithub.com/aymerick/douceur v0.2.0 // indirect\n\tgithub.com/fatih/structs v1.1.0 // indirect\n\tgithub.com/flosch/pongo2/v4 v4.0.2 // indirect\n\tgithub.com/golang/snappy v1.0.0 // indirect\n\tgithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a // indirect\n\tgithub.com/google/uuid v1.6.0 // indirect\n\tgithub.com/gorilla/css v1.0.1 // indirect\n\tgithub.com/iris-contrib/schema v0.0.6 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/kataras/blocks v0.0.8 // indirect\n\tgithub.com/kataras/golog v0.1.12 // indirect\n\tgithub.com/kataras/pio v0.0.13 // indirect\n\tgithub.com/kataras/sitemap v0.0.6 // indirect\n\tgithub.com/kataras/tunnel v0.0.4 // indirect\n\tgithub.com/klauspost/compress v1.18.2 // indirect\n\tgithub.com/kr/pretty v0.3.1 // indirect\n\tgithub.com/mailgun/raymond/v2 v2.0.48 // indirect\n\tgithub.com/mailru/easyjson v0.9.1 // indirect\n\tgithub.com/microcosm-cc/bluemonday v1.0.27 // indirect\n\tgithub.com/rogpeppe/go-internal v1.10.0 // indirect\n\tgithub.com/russross/blackfriday/v2 v2.1.0 // indirect\n\tgithub.com/schollz/closestmatch v2.1.0+incompatible // indirect\n\tgithub.com/sirupsen/logrus v1.8.1 // indirect\n\tgithub.com/stretchr/testify v1.11.1 // indirect\n\tgithub.com/tdewolff/minify/v2 v2.24.8 // indirect\n\tgithub.com/tdewolff/parse/v2 v2.8.5 // indirect\n\tgithub.com/valyala/bytebufferpool v1.0.0 // indirect\n\tgithub.com/vmihailenco/msgpack/v5 v5.4.1 // indirect\n\tgithub.com/vmihailenco/tagparser/v2 v2.0.0 // indirect\n\tgithub.com/yosssi/ace v0.0.5 // indirect\n\tgolang.org/x/crypto v0.47.0 // indirect\n\tgolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 // indirect\n\tgolang.org/x/net v0.49.0 // indirect\n\tgolang.org/x/sys v0.40.0 // indirect\n\tgolang.org/x/text v0.33.0 // indirect\n\tgolang.org/x/time v0.14.0 // indirect\n\tgoogle.golang.org/protobuf v1.36.11 // indirect\n\tgopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect\n\tgopkg.in/ini.v1 v1.67.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n"
  },
  {
    "path": "_examples/auth/basicauth/database/go.sum",
    "content": "filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=\nfilippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=\ngithub.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=\ngithub.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=\ngithub.com/CloudyKit/jet/v6 v6.3.1 h1:6IAo5Cx21xrHVaR8zzXN5gJatKV/wO7Nf6bfCnCSbUw=\ngithub.com/CloudyKit/jet/v6 v6.3.1/go.mod h1:lf8ksdNsxZt7/yH/3n4vJQWA9RUq4wpaHtArHhGVMOw=\ngithub.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc=\ngithub.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=\ngithub.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=\ngithub.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 h1:dG7/gLroJGht/jSQtHiLvT48Hxn+crbmvyItZC8cWOs=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05/go.mod h1:NYezi6wtnJtBm5btoprXc5SvAdqH0XTXWnUup0MptAI=\ngithub.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=\ngithub.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=\ngithub.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=\ngithub.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=\ngithub.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=\ngithub.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=\ngithub.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=\ngithub.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=\ngithub.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=\ngithub.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw=\ngithub.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8=\ngithub.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=\ngithub.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=\ngithub.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=\ngithub.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=\ngithub.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=\ngithub.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a h1:l7A0loSszR5zHd/qK53ZIHMO8b3bBSmENnQ6eKnUT0A=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=\ngithub.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=\ngithub.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=\ngithub.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=\ngithub.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=\ngithub.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2 h1:T9THsdP1woyAqKHwjkEsbCnMefsAFvk8iJJKokcJ3Go=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2/go.mod h1:JLDgIqnFy5loDSUv1OA2j0mb6p/rDhiCqigP22Uq9xE=\ngithub.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw=\ngithub.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/kataras/blocks v0.0.8 h1:MrpVhoFTCR2v1iOOfGng5VJSILKeZZI+7NGfxEh3SUM=\ngithub.com/kataras/blocks v0.0.8/go.mod h1:9Jm5zx6BB+06NwA+OhTbHW1xkMOYxahnqTN5DveZ2Yg=\ngithub.com/kataras/golog v0.1.12 h1:Bu7I/G4ilJlbfzjmU39O9N+2uO1pBcMK045fzZ4ytNg=\ngithub.com/kataras/golog v0.1.12/go.mod h1:wrGSbOiBqbQSQznleVNX4epWM8rl9SJ/rmEacl0yqy4=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd h1:oMsQgqKACbUdlaIWnN+EZzzAnHkw90hE/y/R0BSij2U=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd/go.mod h1:yucjtDl9Vn3OctWM1fi0MuM9OckemC7kEreFa9QtPF8=\ngithub.com/kataras/pio v0.0.13 h1:x0rXVX0fviDTXOOLOmr4MUxOabu1InVSTu5itF8CXCM=\ngithub.com/kataras/pio v0.0.13/go.mod h1:k3HNuSw+eJ8Pm2lA4lRhg3DiCjVgHlP8hmXApSej3oM=\ngithub.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY=\ngithub.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4=\ngithub.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA=\ngithub.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw=\ngithub.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=\ngithub.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=\ngithub.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=\ngithub.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8=\ngithub.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=\ngithub.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=\ngithub.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=\ngithub.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=\ngithub.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=\ngithub.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=\ngithub.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=\ngithub.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=\ngithub.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=\ngithub.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=\ngithub.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=\ngithub.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=\ngithub.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk=\ngithub.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=\ngithub.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=\ngithub.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=\ngithub.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=\ngithub.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/tdewolff/minify/v2 v2.24.8 h1:58/VjsbevI4d5FGV0ZSuBrHMSSkH4MCH0sIz/eKIauE=\ngithub.com/tdewolff/minify/v2 v2.24.8/go.mod h1:0Ukj0CRpo/sW/nd8uZ4ccXaV1rEVIWA3dj8U7+Shhfw=\ngithub.com/tdewolff/parse/v2 v2.8.5 h1:ZmBiA/8Do5Rpk7bDye0jbbDUpXXbCdc3iah4VeUvwYU=\ngithub.com/tdewolff/parse/v2 v2.8.5/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=\ngithub.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE=\ngithub.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=\ngithub.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=\ngithub.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=\ngithub.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=\ngithub.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=\ngithub.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=\ngithub.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=\ngithub.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA=\ngithub.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0=\ngithub.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=\ngithub.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=\ngithub.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=\ngolang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=\ngolang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=\ngolang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=\ngolang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=\ngolang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=\ngolang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=\ngolang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=\ngolang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=\ngoogle.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=\ngopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nmoul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs=\nmoul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE=\n"
  },
  {
    "path": "_examples/auth/basicauth/database/main.go",
    "content": "package main // Look README.md\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/middleware/basicauth\"\n\n\t_ \"github.com/go-sql-driver/mysql\" // lint: mysql driver.\n)\n\n// User is just an example structure of a user,\n// it MUST contain a Username and Password exported fields\n// or/and complete the basicauth.User interface.\ntype User struct {\n\tID       int64  `db:\"id\" json:\"id\"`\n\tUsername string `db:\"username\" json:\"username\"`\n\tPassword string `db:\"password\" json:\"password\"`\n\tEmail    string `db:\"email\" json:\"email\"`\n}\n\n// GetUsername returns the Username field.\nfunc (u User) GetUsername() string {\n\treturn u.Username\n}\n\n// GetPassword returns the Password field.\nfunc (u User) GetPassword() string {\n\treturn u.Password\n}\n\nfunc main() {\n\tdsn := fmt.Sprintf(\"%s:%s@tcp(%s:3306)/%s?parseTime=true&charset=utf8mb4&collation=utf8mb4_unicode_ci\",\n\t\tgetenv(\"MYSQL_USER\", \"user_myapp\"),\n\t\tgetenv(\"MYSQL_PASSWORD\", \"dbpassword\"),\n\t\tgetenv(\"MYSQL_HOST\", \"localhost\"),\n\t\tgetenv(\"MYSQL_DATABASE\", \"myapp\"),\n\t)\n\tdb, err := connect(dsn)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Validate a user from database.\n\tallowFunc := func(ctx iris.Context, username, password string) (any, bool) {\n\t\tuser, err := db.getUserByUsernameAndPassword(context.Background(), username, password)\n\t\treturn user, err == nil\n\t}\n\n\topts := basicauth.Options{\n\t\tRealm:        basicauth.DefaultRealm,\n\t\tErrorHandler: basicauth.DefaultErrorHandler,\n\t\tAllow:        allowFunc,\n\t}\n\n\tauth := basicauth.New(opts)\n\n\tapp := iris.New()\n\tapp.Use(auth)\n\tapp.Get(\"/\", index)\n\tapp.Listen(\":8080\")\n}\n\nfunc index(ctx iris.Context) {\n\tuser, _ := ctx.User().GetRaw()\n\t// user is a type of main.User\n\tctx.JSON(user)\n}\n\nfunc getenv(key string, def string) string {\n\tv := os.Getenv(key)\n\tif v == \"\" {\n\t\treturn def\n\t}\n\n\treturn v\n}\n\ntype database struct {\n\t*sql.DB\n}\n\nfunc connect(dsn string) (*database, error) {\n\tconn, err := sql.Open(\"mysql\", dsn)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\terr = conn.Ping()\n\tif err != nil {\n\t\tconn.Close()\n\t\treturn nil, err\n\t}\n\n\treturn &database{conn}, nil\n}\n\nfunc (db *database) getUserByUsernameAndPassword(ctx context.Context, username, password string) (User, error) {\n\tquery := fmt.Sprintf(\"SELECT * FROM %s WHERE %s = ? AND %s = ? LIMIT 1\", \"users\", \"username\", \"password\")\n\trows, err := db.QueryContext(ctx, query, username, password)\n\tif err != nil {\n\t\treturn User{}, err\n\t}\n\tdefer rows.Close()\n\tif !rows.Next() {\n\t\treturn User{}, sql.ErrNoRows\n\t}\n\n\tvar user User\n\terr = rows.Scan(&user.ID, &user.Username, &user.Password, &user.Email)\n\treturn user, err\n}\n"
  },
  {
    "path": "_examples/auth/basicauth/database/migration/db.sql",
    "content": "CREATE DATABASE IF NOT EXISTS myapp DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;\n\nUSE myapp;\n\nSET NAMES utf8mb4;\nSET FOREIGN_KEY_CHECKS = 0;\n\nDROP TABLE IF EXISTS users;\nCREATE TABLE users  (\n  id int(11) NOT NULL AUTO_INCREMENT,\n  username varchar(255) NOT NULL,\n  password varchar(255) NOT NULL,\n  email varchar(255) NOT NULL,\n  PRIMARY KEY (id)\n);\n\nINSERT INTO users (username,password,email)\nVALUES\n\t('admin', 'admin', 'kataras2006@hotmail.com'),\n\t(\"iris\", 'iris_password', 'iris-go@outlook.com');\n\nSET FOREIGN_KEY_CHECKS = 1;"
  },
  {
    "path": "_examples/auth/basicauth/users_file_bcrypt/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/middleware/basicauth\"\n)\n\nfunc main() {\n\tauth := basicauth.Load(\"users.yml\", basicauth.BCRYPT)\n\t/* Same as:\n\topts := basicauth.Options{\n\t\tRealm: basicauth.DefaultRealm,\n\t\tAllow: basicauth.AllowUsersFile(\"users.yml\", basicauth.BCRYPT),\n\t}\n\n\tauth := basicauth.New(opts)\n\t*/\n\n\tapp := iris.New()\n\tapp.Use(auth)\n\tapp.Get(\"/\", index)\n\t// kataras:kataras_pass\n\t// makis:makis_pass\n\tapp.Listen(\":8080\")\n}\n\nfunc index(ctx iris.Context) {\n\tuser := ctx.User()\n\tctx.JSON(user)\n}\n"
  },
  {
    "path": "_examples/auth/basicauth/users_file_bcrypt/users.yml",
    "content": "# The file cannot be modified during the serve time.\n# To support real-time users changes please use the Options.Allow instead,\n# (see the database example for that).\n#\n# Again, the username and password (or capitalized) fields are required,\n# the rest are optional, depending on your application needs.\n- username: kataras\n  password: $2a$10$Irg8k8HWkDlvL0YDBKLCYee6j6zzIFTplJcvZYKA.B8/clHPZn2Ey # encrypted of kataras_pass\n  age: 27\n  role: admin\n- username: makis\n  password: $2a$10$3GXzp3J5GhHThGisbpvpZuftbmzPivDMo94XPnkTnDe7254x7sJ3O # encrypted of makis_pass\n"
  },
  {
    "path": "_examples/auth/basicauth/users_list/main.go",
    "content": "package main\n\nimport (\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/middleware/basicauth\"\n)\n\n// User is just an example structure of a user,\n// it MUST contain a Username and Password exported fields\n// or complete the basicauth.User interface.\ntype User struct {\n\tUsername string   `json:\"username\"`\n\tPassword string   `json:\"password\"`\n\tRoles    []string `json:\"roles\"`\n}\n\nvar users = []User{\n\t{\"admin\", \"admin\", []string{\"admin\"}},\n\t{\"kataras\", \"kataras_pass\", []string{\"manager\", \"author\"}},\n\t{\"george\", \"george_pass\", []string{\"member\"}},\n\t{\"john\", \"john_pass\", []string{}},\n}\n\nfunc main() {\n\topts := basicauth.Options{\n\t\tRealm: basicauth.DefaultRealm,\n\t\t// Defaults to 0, no expiration.\n\t\t// Prompt for new credentials on a client's request\n\t\t// made after 10 minutes the user has logged in:\n\t\tMaxAge: 10 * time.Minute,\n\t\t// Clear any expired users from the memory every one hour,\n\t\t// note that the user's expiration time will be\n\t\t// reseted on the next valid request (when Allow passed).\n\t\tGC: basicauth.GC{\n\t\t\tEvery: 2 * time.Hour,\n\t\t},\n\t\t// The users can be a slice of custom users structure\n\t\t// or a map[string]string (username:password)\n\t\t// or []map[string]any with username and passwords required fields,\n\t\t// read the godocs for more.\n\t\tAllow: basicauth.AllowUsers(users),\n\t}\n\n\tauth := basicauth.New(opts)\n\t// OR: basicauth.Default(users)\n\n\tapp := iris.New()\n\tapp.Use(auth)\n\tapp.Get(\"/\", index)\n\tapp.Listen(\":8080\")\n}\n\nfunc index(ctx iris.Context) {\n\tuser, _ := ctx.User().GetRaw()\n\tctx.JSON(user)\n}\n"
  },
  {
    "path": "_examples/auth/cors/main.go",
    "content": "// Package main integrates the \"rs/cors\" net/http middleware into Iris.\n// That cors third-party middleware cannot be registered through `iris.FromStd`\n// as a common middleware because it should be injected before the Iris Router itself,\n// it allows/dissallows HTTP Methods too.\n//\n// This is just an example you can use to run something, based on custom logic,\n// before the Iris Router itself.\n//\n// In the \"routing/custom-wrapper\" example\n// we learn how we can acquire and release an Iris context to fire an Iris Handler\n// based on custom logic, before the Iris Router itself. In that example\n// we will fire a net/http handler (the \"rs/cors\" handler one) instead.\n//\n// https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS\npackage main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/rs/cors\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tc := cors.New(cors.Options{\n\t\tAllowedOrigins:   []string{\"*\"},\n\t\tAllowCredentials: true,\n\t\t// Enable Debugging for testing, consider disabling in production\n\t\tDebug: true,\n\t})\n\t// app.WrapRouter(func(w http.ResponseWriter, r *http.Request, router http.HandlerFunc) {\n\t// \t[custom logic...]\n\t//  if shouldFireNetHTTPHandler {\n\t//    ...ServeHTTP(w,r)\n\t//    return\n\t//  }\n\t//  router(w,r)\n\t// })\n\t// In our case, the cors package has a ServeHTTP\n\t// of the same form of app.WrapRouter's accept input argument,\n\t// so we can just do:\n\tapp.WrapRouter(c.ServeHTTP)\n\n\t// Serve ./public/index.html, main.js.\n\tapp.HandleDir(\"/\", iris.Dir(\"./public\"))\n\n\t// Register routes here...\n\tapp.Get(\"/data\", listData)\n\n\t// http://localhost:8080 and click the \"fetch data\" button.\n\tapp.Listen(\":8080\")\n}\n\ntype item struct {\n\tTitle string `json:\"title\"`\n}\n\nfunc listData(ctx iris.Context) {\n\tctx.JSON([]item{\n\t\t{\"Item 1\"},\n\t\t{\"Item 2\"},\n\t\t{\"Item 3\"},\n\t})\n}\n"
  },
  {
    "path": "_examples/auth/cors/public/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Iris Cors Example</title>\n</head>\n\n<body>\n    <ul id=\"list\">\n    </ul>\n\n    <input type=\"button\" value=\"Fetch Data\"  id=\"fetchBtn\" />\n\n    <script src=\"main.js\"></script>\n    </body>\n</html>"
  },
  {
    "path": "_examples/auth/cors/public/main.js",
    "content": "// https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch\nasync function doRequest(method = 'GET', url = '', data = {}) {\n    // Default options are marked with *\n\n    const request = {\n        method: method, // *GET, POST, PUT, DELETE, etc.\n        mode: 'cors', // no-cors, *cors, same-origin\n        cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached\n        credentials: 'same-origin', // include, *same-origin, omit\n        redirect: 'follow', // manual, *follow, error\n        referrerPolicy: 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url\n    };\n\n    if (data !== undefined && method !== 'GET' && method !== 'HEAD') {\n        request.headers = {\n            'Content-Type': 'application/json'\n            // 'Content-Type': 'application/x-www-form-urlencoded',\n        };\n        // body data type must match \"Content-Type\" header.\n        request.body = JSON.stringify(data);\n    }\n\n    const response = await fetch(url, request);\n    return response.json(); // parses JSON response into native JavaScript objects.\n}\n\nconst ul = document.getElementById(\"list\");\n\nfunction fetchData() {\n    console.log(\"sending request...\")\n\n    doRequest('GET', '/data').then(data => {\n        data.forEach(item => {\n            var li = document.createElement(\"li\");\n            li.appendChild(document.createTextNode(item.title));\n            ul.appendChild(li);\n        });\n\n        console.log(data); // JSON data parsed by `response.json()` call.\n    });\n}\n\ndocument.getElementById(\"fetchBtn\").onclick = fetchData;"
  },
  {
    "path": "_examples/auth/goth/main.go",
    "content": "package main\n\n// Any OAuth2 (even the pure golang/x/net/oauth2) package\n// can be used with iris but at this example we will see the markbates' goth:\n//\n// $ go get github.com/markbates/goth/...\n//\n// This OAuth2 example works with sessions, so we will need\n// to attach a session manager.\n// Optionally: for even more secure session values,\n// developers can use any third-party package to add a custom cookie encoder/decoder.\n// At this example we will use the gorilla's securecookie:\n//\n// $ go get github.com/gorilla/securecookie\n// Example of securecookie can be found at \"sessions/securecookie\" example folder.\n\n// Notes:\n// The whole example is converted by markbates/goth/example/main.go.\n// It's tested with my own TWITTER application and it worked, even for localhost.\n// I guess that everything else works as expected, all bugs reported by goth library's community\n// are fixed in the time I wrote that example, have fun!\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"sort\"\n\n\t\"github.com/kataras/iris/v12\"\n\n\t\"github.com/kataras/iris/v12/sessions\"\n\n\t\"github.com/gorilla/securecookie\" // optionally, used for session's encoder/decoder\n\n\t\"github.com/markbates/goth\"\n\t\"github.com/markbates/goth/providers/amazon\"\n\t\"github.com/markbates/goth/providers/auth0\"\n\t\"github.com/markbates/goth/providers/bitbucket\"\n\t\"github.com/markbates/goth/providers/box\"\n\t\"github.com/markbates/goth/providers/dailymotion\"\n\t\"github.com/markbates/goth/providers/deezer\"\n\t\"github.com/markbates/goth/providers/digitalocean\"\n\t\"github.com/markbates/goth/providers/discord\"\n\t\"github.com/markbates/goth/providers/dropbox\"\n\t\"github.com/markbates/goth/providers/facebook\"\n\t\"github.com/markbates/goth/providers/fitbit\"\n\t\"github.com/markbates/goth/providers/github\"\n\t\"github.com/markbates/goth/providers/gitlab\"\n\t\"github.com/markbates/goth/providers/gplus\"\n\t\"github.com/markbates/goth/providers/heroku\"\n\t\"github.com/markbates/goth/providers/instagram\"\n\t\"github.com/markbates/goth/providers/intercom\"\n\t\"github.com/markbates/goth/providers/lastfm\"\n\t\"github.com/markbates/goth/providers/linkedin\"\n\t\"github.com/markbates/goth/providers/meetup\"\n\t\"github.com/markbates/goth/providers/onedrive\"\n\t\"github.com/markbates/goth/providers/openidConnect\"\n\t\"github.com/markbates/goth/providers/paypal\"\n\t\"github.com/markbates/goth/providers/salesforce\"\n\t\"github.com/markbates/goth/providers/slack\"\n\t\"github.com/markbates/goth/providers/soundcloud\"\n\t\"github.com/markbates/goth/providers/spotify\"\n\t\"github.com/markbates/goth/providers/steam\"\n\t\"github.com/markbates/goth/providers/stripe\"\n\t\"github.com/markbates/goth/providers/twitch\"\n\t\"github.com/markbates/goth/providers/twitter\"\n\t\"github.com/markbates/goth/providers/uber\"\n\t\"github.com/markbates/goth/providers/wepay\"\n\t\"github.com/markbates/goth/providers/xero\"\n\t\"github.com/markbates/goth/providers/yahoo\"\n\t\"github.com/markbates/goth/providers/yammer\"\n)\n\nvar sessionsManager *sessions.Sessions\n\nfunc init() {\n\t// attach a session manager\n\tcookieName := \"mycustomsessionid\"\n\thashKey := securecookie.GenerateRandomKey(64)\n\tblockKey := securecookie.GenerateRandomKey(32)\n\tsecureCookie := securecookie.New(hashKey, blockKey)\n\n\tsessionsManager = sessions.New(sessions.Config{\n\t\tCookie:       cookieName,\n\t\tEncoding:     secureCookie,\n\t\tAllowReclaim: true,\n\t})\n}\n\n// These are some function helpers that you may use if you want\n\n// GetProviderName is a function used to get the name of a provider\n// for a given request. By default, this provider is fetched from\n// the URL query string. If you provide it in a different way,\n// assign your own function to this variable that returns the provider\n// name for your request.\nvar GetProviderName = func(ctx iris.Context) (string, error) {\n\t// try to get it from the url param \"provider\"\n\tif p := ctx.URLParam(\"provider\"); p != \"\" {\n\t\treturn p, nil\n\t}\n\n\t// try to get it from the url PATH parameter \"{provider} or :provider or {provider:string} or {provider:alphabetical}\"\n\tif p := ctx.Params().Get(\"provider\"); p != \"\" {\n\t\treturn p, nil\n\t}\n\n\t// try to get it from context's per-request storage\n\tif p := ctx.Values().GetString(\"provider\"); p != \"\" {\n\t\treturn p, nil\n\t}\n\t// if not found then return an empty string with the corresponding error\n\treturn \"\", errors.New(\"you must select a provider\")\n}\n\n/*\nBeginAuthHandler is a convenience handler for starting the authentication process.\nIt expects to be able to get the name of the provider from the query parameters\nas either \"provider\" or \":provider\".\n\nBeginAuthHandler will redirect the user to the appropriate authentication end-point\nfor the requested provider.\n\nSee https://github.com/markbates/goth/examples/main.go to see this in action.\n*/\nfunc BeginAuthHandler(ctx iris.Context) {\n\turl, err := GetAuthURL(ctx)\n\tif err != nil {\n\t\tctx.StopWithError(iris.StatusBadRequest, err)\n\t\treturn\n\t}\n\n\tctx.Redirect(url, iris.StatusTemporaryRedirect)\n}\n\n/*\nGetAuthURL starts the authentication process with the requested provided.\nIt will return a URL that should be used to send users to.\n\nIt expects to be able to get the name of the provider from the query parameters\nas either \"provider\" or \":provider\" or from the context's value of \"provider\" key.\n\nI would recommend using the BeginAuthHandler instead of doing all of these steps\nyourself, but that's entirely up to you.\n*/\nfunc GetAuthURL(ctx iris.Context) (string, error) {\n\tproviderName, err := GetProviderName(ctx)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tprovider, err := goth.GetProvider(providerName)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tsess, err := provider.BeginAuth(SetState(ctx))\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\turl, err := sess.GetAuthURL()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tsession := sessionsManager.Start(ctx)\n\tsession.Set(providerName, sess.Marshal())\n\treturn url, nil\n}\n\n// SetState sets the state string associated with the given request.\n// If no state string is associated with the request, one will be generated.\n// This state is sent to the provider and can be retrieved during the\n// callback.\nvar SetState = func(ctx iris.Context) string {\n\tstate := ctx.URLParam(\"state\")\n\tif len(state) > 0 {\n\t\treturn state\n\t}\n\n\treturn \"state\"\n}\n\n// GetState gets the state returned by the provider during the callback.\n// This is used to prevent CSRF attacks, see\n// http://tools.ietf.org/html/rfc6749#section-10.12\nvar GetState = func(ctx iris.Context) string {\n\treturn ctx.URLParam(\"state\")\n}\n\n/*\nCompleteUserAuth does what it says on the tin. It completes the authentication\nprocess and fetches all of the basic information about the user from the provider.\n\nIt expects to be able to get the name of the provider from the query parameters\nas either \"provider\" or \"{provider}\" path parameter.\n\nSee https://github.com/markbates/goth/examples/main.go to see this in action.\n*/\nvar CompleteUserAuth = func(ctx iris.Context) (goth.User, error) {\n\tproviderName, err := GetProviderName(ctx)\n\tif err != nil {\n\t\treturn goth.User{}, err\n\t}\n\n\tprovider, err := goth.GetProvider(providerName)\n\tif err != nil {\n\t\treturn goth.User{}, err\n\t}\n\tsession := sessionsManager.Start(ctx)\n\tvalue := session.GetString(providerName)\n\tif value == \"\" {\n\t\treturn goth.User{}, errors.New(\"session value for \" + providerName + \" not found\")\n\t}\n\n\tsess, err := provider.UnmarshalSession(value)\n\tif err != nil {\n\t\treturn goth.User{}, err\n\t}\n\n\tuser, err := provider.FetchUser(sess)\n\tif err == nil {\n\t\t// user can be found with existing session data\n\t\treturn user, err\n\t}\n\n\t// get new token and retry fetch\n\t_, err = sess.Authorize(provider, ctx.Request().URL.Query())\n\tif err != nil {\n\t\treturn goth.User{}, err\n\t}\n\n\tsession.Set(providerName, sess.Marshal())\n\treturn provider.FetchUser(sess)\n}\n\n// Logout invalidates a user session.\nfunc Logout(ctx iris.Context) error {\n\tproviderName, err := GetProviderName(ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\tsession := sessionsManager.Start(ctx)\n\tsession.Delete(providerName)\n\treturn nil\n}\n\n// End of the \"some function helpers\".\n\nfunc main() {\n\tgoth.UseProviders(\n\t\ttwitter.New(os.Getenv(\"TWITTER_KEY\"), os.Getenv(\"TWITTER_SECRET\"), \"http://localhost:3000/auth/twitter/callback\"),\n\t\t// If you'd like to use authenticate instead of authorize in Twitter provider, use this instead.\n\t\t// twitter.NewAuthenticate(os.Getenv(\"TWITTER_KEY\"), os.Getenv(\"TWITTER_SECRET\"), \"http://localhost:3000/auth/twitter/callback\"),\n\n\t\tfacebook.New(os.Getenv(\"FACEBOOK_KEY\"), os.Getenv(\"FACEBOOK_SECRET\"), \"http://localhost:3000/auth/facebook/callback\"),\n\t\tfitbit.New(os.Getenv(\"FITBIT_KEY\"), os.Getenv(\"FITBIT_SECRET\"), \"http://localhost:3000/auth/fitbit/callback\"),\n\t\tgplus.New(os.Getenv(\"GPLUS_KEY\"), os.Getenv(\"GPLUS_SECRET\"), \"http://localhost:3000/auth/gplus/callback\"),\n\t\tgithub.New(os.Getenv(\"GITHUB_KEY\"), os.Getenv(\"GITHUB_SECRET\"), \"http://localhost:3000/auth/github/callback\"),\n\t\tspotify.New(os.Getenv(\"SPOTIFY_KEY\"), os.Getenv(\"SPOTIFY_SECRET\"), \"http://localhost:3000/auth/spotify/callback\"),\n\t\tlinkedin.New(os.Getenv(\"LINKEDIN_KEY\"), os.Getenv(\"LINKEDIN_SECRET\"), \"http://localhost:3000/auth/linkedin/callback\"),\n\t\tlastfm.New(os.Getenv(\"LASTFM_KEY\"), os.Getenv(\"LASTFM_SECRET\"), \"http://localhost:3000/auth/lastfm/callback\"),\n\t\ttwitch.New(os.Getenv(\"TWITCH_KEY\"), os.Getenv(\"TWITCH_SECRET\"), \"http://localhost:3000/auth/twitch/callback\"),\n\t\tdropbox.New(os.Getenv(\"DROPBOX_KEY\"), os.Getenv(\"DROPBOX_SECRET\"), \"http://localhost:3000/auth/dropbox/callback\"),\n\t\tdigitalocean.New(os.Getenv(\"DIGITALOCEAN_KEY\"), os.Getenv(\"DIGITALOCEAN_SECRET\"), \"http://localhost:3000/auth/digitalocean/callback\", \"read\"),\n\t\tbitbucket.New(os.Getenv(\"BITBUCKET_KEY\"), os.Getenv(\"BITBUCKET_SECRET\"), \"http://localhost:3000/auth/bitbucket/callback\"),\n\t\tinstagram.New(os.Getenv(\"INSTAGRAM_KEY\"), os.Getenv(\"INSTAGRAM_SECRET\"), \"http://localhost:3000/auth/instagram/callback\"),\n\t\tintercom.New(os.Getenv(\"INTERCOM_KEY\"), os.Getenv(\"INTERCOM_SECRET\"), \"http://localhost:3000/auth/intercom/callback\"),\n\t\tbox.New(os.Getenv(\"BOX_KEY\"), os.Getenv(\"BOX_SECRET\"), \"http://localhost:3000/auth/box/callback\"),\n\t\tsalesforce.New(os.Getenv(\"SALESFORCE_KEY\"), os.Getenv(\"SALESFORCE_SECRET\"), \"http://localhost:3000/auth/salesforce/callback\"),\n\t\tamazon.New(os.Getenv(\"AMAZON_KEY\"), os.Getenv(\"AMAZON_SECRET\"), \"http://localhost:3000/auth/amazon/callback\"),\n\t\tyammer.New(os.Getenv(\"YAMMER_KEY\"), os.Getenv(\"YAMMER_SECRET\"), \"http://localhost:3000/auth/yammer/callback\"),\n\t\tonedrive.New(os.Getenv(\"ONEDRIVE_KEY\"), os.Getenv(\"ONEDRIVE_SECRET\"), \"http://localhost:3000/auth/onedrive/callback\"),\n\n\t\t// Pointed localhost.com to http://localhost:3000/auth/yahoo/callback through proxy as yahoo\n\t\t// does not allow to put custom ports in redirection uri\n\t\tyahoo.New(os.Getenv(\"YAHOO_KEY\"), os.Getenv(\"YAHOO_SECRET\"), \"http://localhost.com\"),\n\t\tslack.New(os.Getenv(\"SLACK_KEY\"), os.Getenv(\"SLACK_SECRET\"), \"http://localhost:3000/auth/slack/callback\"),\n\t\tstripe.New(os.Getenv(\"STRIPE_KEY\"), os.Getenv(\"STRIPE_SECRET\"), \"http://localhost:3000/auth/stripe/callback\"),\n\t\twepay.New(os.Getenv(\"WEPAY_KEY\"), os.Getenv(\"WEPAY_SECRET\"), \"http://localhost:3000/auth/wepay/callback\", \"view_user\"),\n\t\t// By default paypal production auth urls will be used, please set PAYPAL_ENV=sandbox as environment variable for testing\n\t\t// in sandbox environment\n\t\tpaypal.New(os.Getenv(\"PAYPAL_KEY\"), os.Getenv(\"PAYPAL_SECRET\"), \"http://localhost:3000/auth/paypal/callback\"),\n\t\tsteam.New(os.Getenv(\"STEAM_KEY\"), \"http://localhost:3000/auth/steam/callback\"),\n\t\theroku.New(os.Getenv(\"HEROKU_KEY\"), os.Getenv(\"HEROKU_SECRET\"), \"http://localhost:3000/auth/heroku/callback\"),\n\t\tuber.New(os.Getenv(\"UBER_KEY\"), os.Getenv(\"UBER_SECRET\"), \"http://localhost:3000/auth/uber/callback\"),\n\t\tsoundcloud.New(os.Getenv(\"SOUNDCLOUD_KEY\"), os.Getenv(\"SOUNDCLOUD_SECRET\"), \"http://localhost:3000/auth/soundcloud/callback\"),\n\t\tgitlab.New(os.Getenv(\"GITLAB_KEY\"), os.Getenv(\"GITLAB_SECRET\"), \"http://localhost:3000/auth/gitlab/callback\"),\n\t\tdailymotion.New(os.Getenv(\"DAILYMOTION_KEY\"), os.Getenv(\"DAILYMOTION_SECRET\"), \"http://localhost:3000/auth/dailymotion/callback\", \"email\"),\n\t\tdeezer.New(os.Getenv(\"DEEZER_KEY\"), os.Getenv(\"DEEZER_SECRET\"), \"http://localhost:3000/auth/deezer/callback\", \"email\"),\n\t\tdiscord.New(os.Getenv(\"DISCORD_KEY\"), os.Getenv(\"DISCORD_SECRET\"), \"http://localhost:3000/auth/discord/callback\", discord.ScopeIdentify, discord.ScopeEmail),\n\t\tmeetup.New(os.Getenv(\"MEETUP_KEY\"), os.Getenv(\"MEETUP_SECRET\"), \"http://localhost:3000/auth/meetup/callback\"),\n\n\t\t// Auth0 allocates domain per customer, a domain must be provided for auth0 to work\n\t\tauth0.New(os.Getenv(\"AUTH0_KEY\"), os.Getenv(\"AUTH0_SECRET\"), \"http://localhost:3000/auth/auth0/callback\", os.Getenv(\"AUTH0_DOMAIN\")),\n\t\txero.New(os.Getenv(\"XERO_KEY\"), os.Getenv(\"XERO_SECRET\"), \"http://localhost:3000/auth/xero/callback\"),\n\t)\n\n\t// OpenID Connect is based on OpenID Connect Auto Discovery URL (https://openid.net/specs/openid-connect-discovery-1_0-17.html)\n\t// because the OpenID Connect provider initialize it self in the New(), it can return an error which should be handled or ignored\n\t// ignore the error for now\n\topenidConnect, _ := openidConnect.New(os.Getenv(\"OPENID_CONNECT_KEY\"), os.Getenv(\"OPENID_CONNECT_SECRET\"), \"http://localhost:3000/auth/openid-connect/callback\", os.Getenv(\"OPENID_CONNECT_DISCOVERY_URL\"))\n\tif openidConnect != nil {\n\t\tgoth.UseProviders(openidConnect)\n\t}\n\n\tm := map[string]string{\n\t\t\"amazon\":         \"Amazon\",\n\t\t\"bitbucket\":      \"Bitbucket\",\n\t\t\"box\":            \"Box\",\n\t\t\"dailymotion\":    \"Dailymotion\",\n\t\t\"deezer\":         \"Deezer\",\n\t\t\"digitalocean\":   \"Digital Ocean\",\n\t\t\"discord\":        \"Discord\",\n\t\t\"dropbox\":        \"Dropbox\",\n\t\t\"facebook\":       \"Facebook\",\n\t\t\"fitbit\":         \"Fitbit\",\n\t\t\"github\":         \"Github\",\n\t\t\"gitlab\":         \"Gitlab\",\n\t\t\"soundcloud\":     \"SoundCloud\",\n\t\t\"spotify\":        \"Spotify\",\n\t\t\"steam\":          \"Steam\",\n\t\t\"stripe\":         \"Stripe\",\n\t\t\"twitch\":         \"Twitch\",\n\t\t\"uber\":           \"Uber\",\n\t\t\"wepay\":          \"Wepay\",\n\t\t\"yahoo\":          \"Yahoo\",\n\t\t\"yammer\":         \"Yammer\",\n\t\t\"gplus\":          \"Google Plus\",\n\t\t\"heroku\":         \"Heroku\",\n\t\t\"instagram\":      \"Instagram\",\n\t\t\"intercom\":       \"Intercom\",\n\t\t\"lastfm\":         \"Last FM\",\n\t\t\"linkedin\":       \"Linkedin\",\n\t\t\"onedrive\":       \"Onedrive\",\n\t\t\"paypal\":         \"Paypal\",\n\t\t\"twitter\":        \"Twitter\",\n\t\t\"salesforce\":     \"Salesforce\",\n\t\t\"slack\":          \"Slack\",\n\t\t\"meetup\":         \"Meetup.com\",\n\t\t\"auth0\":          \"Auth0\",\n\t\t\"openid-connect\": \"OpenID Connect\",\n\t\t\"xero\":           \"Xero\",\n\t}\n\n\tkeys := make([]string, 0, len(m))\n\tfor k := range m {\n\t\tkeys = append(keys, k)\n\t}\n\tsort.Strings(keys)\n\n\tproviderIndex := &ProviderIndex{Providers: keys, ProvidersMap: m}\n\n\t// create our app,\n\t// set a view\n\t// set sessions\n\t// and setup the router for the showcase\n\tapp := iris.New()\n\tapp.Logger().SetLevel(\"debug\")\n\n\t// attach and build our templates\n\tapp.RegisterView(iris.HTML(\"./templates\", \".html\"))\n\n\t// start of the router\n\n\tapp.Get(\"/auth/{provider}/callback\", func(ctx iris.Context) {\n\t\tuser, err := CompleteUserAuth(ctx)\n\t\tif err != nil {\n\t\t\tctx.StopWithError(iris.StatusInternalServerError, err)\n\t\t\treturn\n\t\t}\n\n\t\t// Between handlers (user as root .):\n\t\t// ctx.ViewData(\"\", user)\n\t\t//\n\t\t// Between handlers (user as .user variable):\n\t\t// ctx.ViewData(\"user\", user)\n\t\t// ----\n\t\t// Directly (user as root):\n\t\t// ctx.View(\"user.html\", user)\n\t\t//\n\t\t// Directly (user as .user variable):\n\t\tif err := ctx.View(\"user.html\", iris.Map{\n\t\t\t\"user\": user,\n\t\t}); err != nil {\n\t\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\t\treturn\n\t\t}\n\t})\n\n\tapp.Get(\"/logout/{provider}\", func(ctx iris.Context) {\n\t\tLogout(ctx)\n\t\tctx.Redirect(\"/\", iris.StatusTemporaryRedirect)\n\t})\n\n\tapp.Get(\"/auth/{provider}\", func(ctx iris.Context) {\n\t\t// try to get the user without re-authenticating\n\t\tgothUser, err := CompleteUserAuth(ctx)\n\t\tif err != nil {\n\t\t\tBeginAuthHandler(ctx)\n\t\t\treturn\n\t\t}\n\n\t\tif err := ctx.View(\"user.html\", gothUser); err != nil {\n\t\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\t\treturn\n\t\t}\n\t})\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tif err := ctx.View(\"index.html\", providerIndex); err != nil {\n\t\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\t\treturn\n\t\t}\n\t})\n\n\t// http://localhost:3000\n\tapp.Listen(\"localhost:3000\")\n}\n\ntype ProviderIndex struct {\n\tProviders    []string\n\tProvidersMap map[string]string\n}\n"
  },
  {
    "path": "_examples/auth/goth/templates/index.html",
    "content": "{{range $key,$value:=.Providers}}\n    <p><a href=\"/auth/{{$value}}\">Log in with {{index $.ProvidersMap $value}}</a></p>\n{{end}}"
  },
  {
    "path": "_examples/auth/goth/templates/user.html",
    "content": "<p><a href=\"/logout/{{.Provider}}\">logout</a></p>\n<p>Name: {{.Name}} [{{.LastName}}, {{.FirstName}}]</p>\n<p>Email: {{.Email}}</p>\n<p>NickName: {{.NickName}}</p>\n<p>Location: {{.Location}}</p>\n<p>AvatarURL: {{.AvatarURL}} <img src=\"{{.AvatarURL}}\"></p>\n<p>Description: {{.Description}}</p>\n<p>UserID: {{.UserID}}</p>\n<p>AccessToken: {{.AccessToken}}</p>\n<p>ExpiresAt: {{.ExpiresAt}}</p>\n<p>RefreshToken: {{.RefreshToken}}</p>\n\n<hr/>\n\n<h3>Iterate all properties</h3>\n\n{{range $key, $value := .RawData}}\n    {{ $key }} => {{ $value }} <br/>\n{{end}}\n"
  },
  {
    "path": "_examples/auth/hcaptcha/hosts",
    "content": "# https://docs.hcaptcha.com/#localdev\n# Add to the end of your hosts file, e.g. on windows: C:/windows/system32/drivers/etc/hosts\n127.0.0.1 yourdomain.com\n"
  },
  {
    "path": "_examples/auth/hcaptcha/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/middleware/hcaptcha\"\n)\n\n// Get the following values from: https://dashboard.hcaptcha.com\n// Also, check: https://docs.hcaptcha.com/#localdev to test on local environment.\nvar (\n\tsiteKey   = os.Getenv(\"HCAPTCHA-SITE-KEY\")\n\tsecretKey = os.Getenv(\"HCAPTCHA-SECRET-KEY\")\n)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.RegisterView(iris.HTML(\"./templates\", \".html\"))\n\n\thCaptcha := hcaptcha.New(secretKey)\n\tapp.Get(\"/register\", registerForm)\n\tapp.Post(\"/register\", hCaptcha, register) // See `hcaptcha.SiteVerify` for manual validation too.\n\n\tapp.Logger().Infof(\"SiteKey = %s\\tSecretKey = %s\",\n\t\tsiteKey, secretKey)\n\n\t// GET: http://yourdomain.com/register\n\tapp.Listen(\":80\")\n}\n\nfunc register(ctx iris.Context) {\n\thcaptchaResp, ok := hcaptcha.Get(ctx)\n\tif !ok {\n\t\tctx.StatusCode(iris.StatusUnauthorized)\n\t\tctx.WriteString(\"Are you a bot?\")\n\t\treturn\n\t}\n\n\tctx.Writef(\"Register action here...action was asked by a Human.\\nResponse value is: %#+v\", hcaptchaResp)\n}\n\nfunc registerForm(ctx iris.Context) {\n\tctx.ViewData(\"SiteKey\", siteKey)\n\tif err := ctx.View(\"register_form.html\"); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "_examples/auth/hcaptcha/templates/register_form.html",
    "content": "<html>\n\n<head>\n  <title>hCaptcha Demo</title>\n  <script src=\"https://hcaptcha.com/1/api.js\" async defer></script>\n</head>\n\n<body>\n  <form action=\"/register\" method=\"POST\">\n    <input type=\"text\" name=\"email\" placeholder=\"Email\" />\n    <input type=\"password\" name=\"password\" placeholder=\"Password\" />\n    <div class=\"h-captcha\" data-sitekey=\"{{ .SiteKey }}\"></div>\n    <br />\n    <input type=\"submit\" value=\"Submit\" />\n  </form>\n</body>\n\n</html>"
  },
  {
    "path": "_examples/auth/jwt/basic/main.go",
    "content": "package main\n\nimport (\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/middleware/jwt\"\n)\n\n/*\nDocumentation:\n    https://github.com/kataras/jwt#table-of-contents\n*/\n\n// Replace with your own key and keep them secret.\n// The \"signatureSharedKey\" is used for the HMAC(HS256) signature algorithm.\nvar signatureSharedKey = []byte(\"sercrethatmaycontainch@r32length\")\n\nfunc main() {\n\tapp := iris.New()\n\n\tapp.Get(\"/\", generateToken)\n\tapp.Get(\"/protected\", protected)\n\n\tapp.Listen(\":8080\")\n}\n\ntype fooClaims struct {\n\tFoo string `json:\"foo\"`\n}\n\nfunc generateToken(ctx iris.Context) {\n\tclaims := fooClaims{\n\t\tFoo: \"bar\",\n\t}\n\n\t// Sign and generate compact form token.\n\ttoken, err := jwt.Sign(jwt.HS256, signatureSharedKey, claims, jwt.MaxAge(10*time.Minute))\n\tif err != nil {\n\t\tctx.StopWithStatus(iris.StatusInternalServerError)\n\t\treturn\n\t}\n\n\ttokenString := string(token) // or jwt.BytesToString\n\tctx.HTML(`Token: ` + tokenString + `<br/><br/>\n\t\t<a href=\"/protected?token=` + tokenString + `\">/protected?token=` + tokenString + `</a>`)\n}\n\nfunc protected(ctx iris.Context) {\n\t// Extract the token, e.g. cookie, Authorization: Bearer $token\n\t// or URL query.\n\ttoken := ctx.URLParam(\"token\")\n\t// Verify the token.\n\tverifiedToken, err := jwt.Verify(jwt.HS256, signatureSharedKey, []byte(token))\n\tif err != nil {\n\t\tctx.StopWithStatus(iris.StatusUnauthorized)\n\t\treturn\n\t}\n\n\tctx.Writef(\"This is an authenticated request.\\n\\n\")\n\n\t// Decode the custom claims.\n\tvar claims fooClaims\n\tverifiedToken.Claims(&claims)\n\n\t// Just an example on how you can retrieve all the standard claims (set by jwt.MaxAge, \"exp\").\n\tstandardClaims := jwt.GetVerifiedToken(ctx).StandardClaims\n\n\texpiresAtString := standardClaims.ExpiresAt().Format(ctx.Application().ConfigurationReadOnly().GetTimeFormat())\n\ttimeLeft := standardClaims.Timeleft()\n\n\tctx.Writef(\"foo=%s\\nexpires at: %s\\ntime left: %s\\n\", claims.Foo, expiresAtString, timeLeft)\n}\n"
  },
  {
    "path": "_examples/auth/jwt/blocklist/main.go",
    "content": "package main\n\nimport (\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/middleware/jwt\"\n\t\"github.com/kataras/iris/v12/middleware/jwt/blocklist/redis\"\n\n\t// Optionally to set token identifier.\n\t\"github.com/google/uuid\"\n)\n\nvar (\n\tsignatureSharedKey = []byte(\"sercrethatmaycontainch@r32length\")\n\n\tsigner   = jwt.NewSigner(jwt.HS256, signatureSharedKey, 15*time.Minute)\n\tverifier = jwt.NewVerifier(jwt.HS256, signatureSharedKey)\n)\n\ntype userClaims struct {\n\tUsername string `json:\"username\"`\n}\n\nfunc main() {\n\tapp := iris.New()\n\n\t// IMPORTANT\n\t//\n\t// To use the in-memory blocklist just:\n\t// verifier.WithDefaultBlocklist()\n\t// To use a persistence blocklist, e.g. redis,\n\t// start your redis-server and:\n\tblocklist := redis.NewBlocklist()\n\t// To configure single client or a cluster one:\n\t// blocklist.ClientOptions.Addr = \"127.0.0.1:6379\"\n\t// blocklist.ClusterOptions.Addrs = []string{...}\n\t// To set a prefix for jwt ids:\n\t// blocklist.Prefix = \"myapp-\"\n\t//\n\t// To manually connect and check its error before continue:\n\t// err := blocklist.Connect()\n\t// By default the verifier will try to connect, if failed then it will throw http error.\n\t//\n\t// And then register it:\n\tverifier.Blocklist = blocklist\n\tverifyMiddleware := verifier.Verify(func() any {\n\t\treturn new(userClaims)\n\t})\n\n\tapp.Get(\"/\", authenticate)\n\n\tprotectedAPI := app.Party(\"/protected\", verifyMiddleware)\n\tprotectedAPI.Get(\"/\", protected)\n\tprotectedAPI.Get(\"/logout\", logout)\n\n\t// http://localhost:8080\n\t// http://localhost:8080/protected?token=$token\n\t// http://localhost:8080/logout?token=$token\n\t// http://localhost:8080/protected?token=$token (401)\n\tapp.Listen(\":8080\")\n}\n\nfunc authenticate(ctx iris.Context) {\n\tclaims := userClaims{\n\t\tUsername: \"kataras\",\n\t}\n\n\t// Generate JWT ID.\n\trandom, err := uuid.NewRandom()\n\tif err != nil {\n\t\tctx.StopWithError(iris.StatusInternalServerError, err)\n\t\treturn\n\t}\n\tid := random.String()\n\n\t// Set the ID with the jwt.ID.\n\ttoken, err := signer.Sign(claims, jwt.ID(id))\n\n\tif err != nil {\n\t\tctx.StopWithError(iris.StatusInternalServerError, err)\n\t\treturn\n\t}\n\n\tctx.Write(token)\n}\n\nfunc protected(ctx iris.Context) {\n\tclaims := jwt.Get(ctx).(*userClaims)\n\n\t// To the standard claims, e.g. the generated ID:\n\t// jwt.GetVerifiedToken(ctx).StandardClaims.ID\n\n\tctx.WriteString(claims.Username)\n}\n\nfunc logout(ctx iris.Context) {\n\tctx.Logout()\n\n\tctx.Redirect(\"/\", iris.StatusTemporaryRedirect)\n}\n"
  },
  {
    "path": "_examples/auth/jwt/middleware/main.go",
    "content": "package main\n\nimport (\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/middleware/jwt\"\n)\n\nvar (\n\tsigKey = []byte(\"signature_hmac_secret_shared_key\")\n\t// encKey = []byte(\"GCM_AES_256_secret_shared_key_32\")\n)\n\ntype fooClaims struct {\n\tFoo string `json:\"foo\"`\n}\n\n/*\nIn this example you will learn the essentials\nof the Iris builtin JWT middleware based on the github.com/kataras/jwt package.\n*/\n\nfunc main() {\n\tapp := iris.New()\n\n\tsigner := jwt.NewSigner(jwt.HS256, sigKey, 10*time.Minute)\n\t// Enable payload encryption with:\n\t// signer.WithEncryption(encKey, nil)\n\tapp.Get(\"/\", generateToken(signer))\n\n\tverifier := jwt.NewVerifier(jwt.HS256, sigKey)\n\t// Enable server-side token block feature (even before its expiration time):\n\tverifier.WithDefaultBlocklist()\n\t// Enable payload decryption with:\n\t// verifier.WithDecryption(encKey, nil)\n\tverifyMiddleware := verifier.Verify(func() any {\n\t\treturn new(fooClaims)\n\t})\n\n\tprotectedAPI := app.Party(\"/protected\")\n\t// Register the verify middleware to allow access only to authorized clients.\n\tprotectedAPI.Use(verifyMiddleware)\n\t// ^ or UseRouter(verifyMiddleware) to disallow unauthorized http error handlers too.\n\n\tprotectedAPI.Get(\"/\", protected)\n\t// Invalidate the token through server-side, even if it's not expired yet.\n\tprotectedAPI.Get(\"/logout\", logout)\n\n\t// http://localhost:8080\n\t// http://localhost:8080/protected?token=$token (or Authorization: Bearer $token)\n\t// http://localhost:8080/protected/logout?token=$token\n\t// http://localhost:8080/protected?token=$token (401)\n\tapp.Listen(\":8080\")\n}\n\nfunc generateToken(signer *jwt.Signer) iris.Handler {\n\treturn func(ctx iris.Context) {\n\t\tclaims := fooClaims{Foo: \"bar\"}\n\n\t\ttoken, err := signer.Sign(claims)\n\t\tif err != nil {\n\t\t\tctx.StopWithStatus(iris.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\n\t\tctx.Write(token)\n\t}\n}\n\nfunc protected(ctx iris.Context) {\n\t// Get the verified and decoded claims.\n\tclaims := jwt.Get(ctx).(*fooClaims)\n\n\t// Optionally, get token information if you want to work with them.\n\t// Just an example on how you can retrieve all the standard claims (set by signer's max age, \"exp\").\n\tstandardClaims := jwt.GetVerifiedToken(ctx).StandardClaims\n\texpiresAtString := standardClaims.ExpiresAt().Format(ctx.Application().ConfigurationReadOnly().GetTimeFormat())\n\ttimeLeft := standardClaims.Timeleft()\n\n\tctx.Writef(\"foo=%s\\nexpires at: %s\\ntime left: %s\\n\", claims.Foo, expiresAtString, timeLeft)\n}\n\nfunc logout(ctx iris.Context) {\n\terr := ctx.Logout()\n\tif err != nil {\n\t\tctx.WriteString(err.Error())\n\t} else {\n\t\tctx.Writef(\"token invalidated, a new token is required to access the protected API\")\n\t}\n}\n"
  },
  {
    "path": "_examples/auth/jwt/refresh-token/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/middleware/jwt\"\n)\n\nconst (\n\taccessTokenMaxAge  = 10 * time.Minute\n\trefreshTokenMaxAge = time.Hour\n)\n\nvar (\n\tprivateKey, publicKey = jwt.MustLoadRSA(\"rsa_private_key.pem\", \"rsa_public_key.pem\")\n\n\tsigner   = jwt.NewSigner(jwt.RS256, privateKey, accessTokenMaxAge)\n\tverifier = jwt.NewVerifier(jwt.RS256, publicKey)\n)\n\n// UserClaims a custom access claims structure.\ntype UserClaims struct {\n\tID string `json:\"user_id\"`\n\t// Do: `json:\"username,required\"` to have this field required\n\t// or see the Validate method below instead.\n\tUsername string `json:\"username\"`\n}\n\n// GetID implements the partial context user's ID interface.\n// Note that if claims were a map then the claims value converted to UserClaims\n// and no need to implement any method.\n//\n// This is useful when multiple auth methods are used (e.g. basic auth, jwt)\n// but they all share a couple of methods.\nfunc (u *UserClaims) GetID() string {\n\treturn u.ID\n}\n\n// GetUsername implements the partial context user's Username interface.\nfunc (u *UserClaims) GetUsername() string {\n\treturn u.Username\n}\n\n// Validate completes the middleware's custom ClaimsValidator.\n// It will not accept a token which its claims missing the username field\n// (useful to not accept refresh tokens generated by the same algorithm).\nfunc (u *UserClaims) Validate() error {\n\tif u.Username == \"\" {\n\t\treturn fmt.Errorf(\"username field is missing\")\n\t}\n\n\treturn nil\n}\n\n// For refresh token, we will just use the jwt.Claims\n// structure which contains the standard JWT fields.\n\nfunc main() {\n\tapp := iris.New()\n\tapp.OnErrorCode(iris.StatusUnauthorized, handleUnauthorized)\n\n\tapp.Get(\"/authenticate\", generateTokenPair)\n\tapp.Get(\"/refresh\", refreshToken)\n\n\tprotectedAPI := app.Party(\"/protected\")\n\t{\n\t\tverifyMiddleware := verifier.Verify(func() any {\n\t\t\treturn new(UserClaims)\n\t\t})\n\n\t\tprotectedAPI.Use(verifyMiddleware)\n\n\t\tprotectedAPI.Get(\"/\", func(ctx iris.Context) {\n\t\t\t// Access the claims through: jwt.Get:\n\t\t\t// claims := jwt.Get(ctx).(*UserClaims)\n\t\t\t// ctx.Writef(\"Username: %s\\n\", claims.Username)\n\t\t\t//\n\t\t\t// OR through context's user (if at least one method was implement by our UserClaims):\n\t\t\tuser := ctx.User()\n\t\t\tid, _ := user.GetID()\n\t\t\tusername, _ := user.GetUsername()\n\t\t\tctx.Writef(\"ID: %s\\nUsername: %s\\n\", id, username)\n\t\t})\n\t}\n\n\t// http://localhost:8080/protected (401)\n\t// http://localhost:8080/authenticate (200) (response JSON {access_token, refresh_token})\n\t// http://localhost:8080/protected?token={access_token} (200)\n\t// http://localhost:8080/protected?token={refresh_token} (401)\n\t// http://localhost:8080/refresh?refresh_token={refresh_token}\n\t// OR http://localhost:8080/refresh (request JSON{refresh_token = {refresh_token}}) (200) (response JSON {access_token, refresh_token})\n\t// http://localhost:8080/refresh?refresh_token={access_token} (401)\n\tapp.Listen(\":8080\")\n}\n\nfunc generateTokenPair(ctx iris.Context) {\n\t// Simulate a user...\n\tuserID := \"53afcf05-38a3-43c3-82af-8bbbe0e4a149\"\n\n\t// Map the current user with the refresh token,\n\t// so we make sure, on refresh route, that this refresh token owns\n\t// to that user before re-generate.\n\trefreshClaims := jwt.Claims{Subject: userID}\n\n\taccessClaims := UserClaims{\n\t\tID:       userID,\n\t\tUsername: \"kataras\",\n\t}\n\n\t// Generates a Token Pair, long-live for refresh tokens, e.g. 1 hour.\n\t// First argument is the access claims,\n\t// second argument is the refresh claims,\n\t// third argument is the refresh max age.\n\ttokenPair, err := signer.NewTokenPair(accessClaims, refreshClaims, refreshTokenMaxAge)\n\tif err != nil {\n\t\tctx.Application().Logger().Errorf(\"token pair: %v\", err)\n\t\tctx.StopWithStatus(iris.StatusInternalServerError)\n\t\treturn\n\t}\n\n\t// Send the generated token pair to the client.\n\t// The tokenPair looks like: {\"access_token\": $token, \"refresh_token\": $token}\n\tctx.JSON(tokenPair)\n}\n\n// There are various methods of refresh token, depending on the application requirements.\n// In this example we will accept a refresh token only, we will verify only a refresh token\n// and we re-generate a whole new pair. An alternative would be to accept a token pair\n// of both access and refresh tokens, verify the refresh, verify the access with a Leeway time\n// and check if its going to expire soon, then generate a single access token.\nfunc refreshToken(ctx iris.Context) {\n\t// Assuming you have access to the current user, e.g. sessions.\n\t//\n\t// Simulate a database call against our jwt subject\n\t// to make sure that this refresh token is a pair generated by this user.\n\t// * Note: You can remove the ExpectSubject and do this validation later on by yourself.\n\tcurrentUserID := \"53afcf05-38a3-43c3-82af-8bbbe0e4a149\"\n\n\t// Get the refresh token from ?refresh_token=$token OR\n\t// the request body's JSON{\"refresh_token\": \"$token\"}.\n\trefreshToken := []byte(ctx.URLParam(\"refresh_token\"))\n\tif len(refreshToken) == 0 {\n\t\t// You can read the whole body with ctx.GetBody/ReadBody too.\n\t\tvar tokenPair jwt.TokenPair\n\t\tif err := ctx.ReadJSON(&tokenPair); err != nil {\n\t\t\tctx.StopWithError(iris.StatusBadRequest, err)\n\t\t\treturn\n\t\t}\n\n\t\trefreshToken = tokenPair.RefreshToken\n\t}\n\n\t// Verify the refresh token, which its subject MUST match the \"currentUserID\".\n\t_, err := verifier.VerifyToken(refreshToken, jwt.Expected{Subject: currentUserID})\n\tif err != nil {\n\t\tctx.Application().Logger().Errorf(\"verify refresh token: %v\", err)\n\t\tctx.StatusCode(iris.StatusUnauthorized)\n\t\treturn\n\t}\n\n\t/* Custom validation checks can be performed after Verify calls too:\n\tcurrentUserID := \"53afcf05-38a3-43c3-82af-8bbbe0e4a149\"\n\tuserID := verifiedToken.StandardClaims.Subject\n\tif userID != currentUserID {\n\t\tctx.StopWithStatus(iris.StatusUnauthorized)\n\t\treturn\n\t}\n\t*/\n\n\t// All OK, re-generate the new pair and send to client,\n\t// we could only generate an access token as well.\n\tgenerateTokenPair(ctx)\n}\n\nfunc handleUnauthorized(ctx iris.Context) {\n\tif err := ctx.GetErr(); err != nil {\n\t\tctx.Application().Logger().Errorf(\"unauthorized: %v\", err)\n\t}\n\n\tctx.WriteString(\"Unauthorized\")\n}\n"
  },
  {
    "path": "_examples/auth/jwt/refresh-token/rsa_private_key.pem",
    "content": "-----BEGIN PRIVATE KEY-----\nMIIEowIBAAKCAQEArwO0q8WbBvrplz3lTQjsWu66HC7M3mVAjmjLq8Wj/ipqVtiJ\nMrUL9t/0q9PNO/KX9u+HayFNYM4TnYkXVZX3M5E31W8fPPy74D/XpqFwrwT7bAEw\npT51JJyxkoBAyOh08lmR2EYvpGF7qErra7qbkk4LGFbhoFCXdMLXguT4rPymkzFH\ndQrmGYOBS+v9imSuJddCZpXyv6Ko7AKB4mhzg4RC5RJZO5GEHVUrSMHxZB0syF8c\nU+28iL8A7SlGKTNZPZiHmCQVRqA6WlllL/YV/t6p24kaNZBUp9JGbAzOeKuVUv2u\nvfNKwB/aBwnFKauM9I6RmC4bnI1nGHjETlNNWwIDAQABAoIBAHBPKHmybTGlgpET\nnzo4J7SSzcuYHM/6mdrJVSn9wqcwAN2KR0DK/cqHHTPGz0VRAEPuojAVRtqAZAYM\nG3VIr0HgRrwoextf9BCL549+uhkWUWGVwenIktPT2f/xXaGPyrxazkTDhX8vL3Nn\n4HtZXMweWPBdkJyYGxlKj5Hn7czTpG3VKpvpHeFlY4caF+FT2as1jcQ1MjPnGslH\nSs+sYPBp/70w2T114Z4wlR4OryI1LeuFeje9obrn0HAmJd0ZKYM21awp/YWJ/y8J\nwIH6XQ4AGR9iTRhuffK1XRM/Iec3K/YhOn4PtKdT7OsIujAKY7A9WcqSFif+/E1g\njom3eMECgYEAw5Zdqt2uZ19FuDlDTW4Kw8Z2NyXgWp33LkAXG1mJw7bqDhfPeB1c\nxTPs4i4RubGuDusygxZ3GgJAO7tLGzNQfWNoi03mM7Q/BJGkA9VZr+U28zsSRQOQ\n+J9xNsdgUMP1js7X/NNM2bxTC8zy9wEsWr9JwNo1C7uHTE9WXAumBI8CgYEA5RKV\nniSbyko36W3Vi0ZnGBrRhy0Eiq85V2mhWzHN+txcv+8aISow2wioTUzrpR0aVZ4j\nv9+siJENlALVzdUFihy0lPxHqLJT746Cixz95WRTLkdHeNllV0DMfOph2x3j1Hjd\n3PgTv+jqb6npY0/2Vb2pp4t/zVikGaObsAalSHUCgYBne8B1bjMfqI3n6gxNBIMX\nkILtrNGmwFuPEgPnyZkVf0sZR8nSwJ5cDJwyE7P3LyZr6E9igllj3nsD35Xef2j/\n3r/qrL2275BEJ5bDHHgGk91eFgwVjcx/b0TkedrhAL2E4LXwpA/OSFEcNkT7IZjJ\nLtqj+hAE9CSi4HtN2i/tywKBgBotKn28zzSpkIQTMgDNVcCSZ/kbctZqOZI8lty1\n70TIY6znJMQ/bv/ImHrk3FSs47J+9LTbWXrtoHCWdlokCpMCvrv7rDCh2Cea0F4X\nPQg2k67JJGix5vu2guePXQlN/Bfui+PRUWhvtEJ4VxwrKgoYN0fXEA6mH3JymLrf\nt4l1AoGBALk4o9swGjw7MnByYJmOidlJ0p9Wj1BWWJJYoYX2VfjIuvZj6BNxkEb0\naVmYRC+40e9L1rOyrlyaO/TiQaIPE4ljVs/AmMKGz8sIcVfwdyERH3nDrXxvlAav\nlSvfKoYM3J+5c63CDuU45gztpmavNerzCczqYTLOEMx1eCLHOQlx\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "_examples/auth/jwt/refresh-token/rsa_public_key.pem",
    "content": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArwO0q8WbBvrplz3lTQjs\nWu66HC7M3mVAjmjLq8Wj/ipqVtiJMrUL9t/0q9PNO/KX9u+HayFNYM4TnYkXVZX3\nM5E31W8fPPy74D/XpqFwrwT7bAEwpT51JJyxkoBAyOh08lmR2EYvpGF7qErra7qb\nkk4LGFbhoFCXdMLXguT4rPymkzFHdQrmGYOBS+v9imSuJddCZpXyv6Ko7AKB4mhz\ng4RC5RJZO5GEHVUrSMHxZB0syF8cU+28iL8A7SlGKTNZPZiHmCQVRqA6WlllL/YV\n/t6p24kaNZBUp9JGbAzOeKuVUv2uvfNKwB/aBwnFKauM9I6RmC4bnI1nGHjETlNN\nWwIDAQAB\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "_examples/auth/jwt/tutorial/README.md",
    "content": "# Iris JWT Tutorial\n\nThis example show how to use JWT with domain-driven design pattern with Iris. There is also a simple Go client which describes how you can use Go to authorize a user and use the server's API.\n\n## Run the server\n\n```sh\n$ go run main.go\n```\n\n## Authenticate, get the token\n\n```sh\n$ curl --location --request POST 'http://localhost:8080/signin' \\\n--header 'Content-Type: application/x-www-form-urlencoded' \\\n--data-urlencode 'username=admin' \\\n--data-urlencode 'password=admin'\n\n> $token\n```\n\n## Get all TODOs for this User\n\n```sh\n$ curl --location --request GET 'http://localhost:8080/todos' \\\n--header 'Authorization: Bearer $token'\n\n> $todos\n```\n\n## Get a specific User's TODO \n\n```sh\n$ curl --location --request GET 'http://localhost:8080/todos/$id' \\\n--header 'Authorization: Bearer $token'\n\n> $todo\n```\n\n## Get all TODOs for all Users (admin role)\n\n```sh\n$ curl --location --request GET 'http://localhost:8080/admin/todos' \\\n--header 'Authorization: Bearer $token'\n\n> $todos\n```\n\n## Create a new TODO\n\n```sh\n$ curl --location --request POST 'http://localhost:8080/todos' \\\n--header 'Authorization: Bearer $token' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{\n    \"title\": \"test titlte\",\n    \"body\": \"test body\"\n}'\n\n> Status Created\n> $todo\n```\n"
  },
  {
    "path": "_examples/auth/jwt/tutorial/api/auth.go",
    "content": "package api\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"time\"\n\n\t\"myapp/domain/model\"\n\t\"myapp/domain/repository\"\n\t\"myapp/util\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/middleware/jwt\"\n)\n\nconst defaultSecretKey = \"sercrethatmaycontainch@r$32chars\"\n\nfunc getSecretKey() string {\n\tsecret := os.Getenv(util.AppName + \"_SECRET\")\n\tif secret == \"\" {\n\t\treturn defaultSecretKey\n\t}\n\n\treturn secret\n}\n\n// UserClaims represents the user token claims.\ntype UserClaims struct {\n\tUserID string       `json:\"user_id\"`\n\tRoles  []model.Role `json:\"roles\"`\n}\n\n// Validate implements the custom struct claims validator,\n// this is totally optionally and maybe unnecessary but good to know how.\nfunc (u *UserClaims) Validate() error {\n\tif u.UserID == \"\" {\n\t\treturn fmt.Errorf(\"%w: %s\", jwt.ErrMissingKey, \"user_id\")\n\t}\n\n\treturn nil\n}\n\n// Verify allows only authorized clients.\nfunc Verify() iris.Handler {\n\tsecret := getSecretKey()\n\n\tverifier := jwt.NewVerifier(jwt.HS256, []byte(secret), jwt.Expected{Issuer: util.AppName})\n\tverifier.Extractors = []jwt.TokenExtractor{jwt.FromHeader} // extract token only from Authorization: Bearer $token\n\treturn verifier.Verify(func() any {\n\t\treturn new(UserClaims)\n\t})\n}\n\n// AllowAdmin allows only authorized clients with \"admin\" access role.\n// Should be registered after Verify.\nfunc AllowAdmin(ctx iris.Context) {\n\tif !IsAdmin(ctx) {\n\t\tctx.StopWithText(iris.StatusForbidden, \"admin access required\")\n\t\treturn\n\t}\n\n\tctx.Next()\n}\n\n// SignIn accepts the user form data and returns a token to authorize a client.\nfunc SignIn(repo repository.UserRepository) iris.Handler {\n\tsecret := getSecretKey()\n\tsigner := jwt.NewSigner(jwt.HS256, []byte(secret), 15*time.Minute)\n\n\treturn func(ctx iris.Context) {\n\t\t/*\n\t\t\ttype LoginForm struct {\n\t\t\t\tUsername string `form:\"username\"`\n\t\t\t\tPassword string `form:\"password\"`\n\t\t\t}\n\t\t\tand ctx.ReadForm OR use the ctx.FormValue(s) method.\n\t\t*/\n\n\t\tvar (\n\t\t\tusername = ctx.FormValue(\"username\")\n\t\t\tpassword = ctx.FormValue(\"password\")\n\t\t)\n\n\t\tuser, ok := repo.GetByUsernameAndPassword(username, password)\n\t\tif !ok {\n\t\t\tctx.StopWithText(iris.StatusBadRequest, \"wrong username or password\")\n\t\t\treturn\n\t\t}\n\n\t\tclaims := UserClaims{\n\t\t\tUserID: user.ID,\n\t\t\tRoles:  user.Roles,\n\t\t}\n\n\t\t// Optionally, generate a JWT ID.\n\t\tjti, err := util.GenerateUUID()\n\t\tif err != nil {\n\t\t\tctx.StopWithError(iris.StatusInternalServerError, err)\n\t\t\treturn\n\t\t}\n\n\t\ttoken, err := signer.Sign(claims, jwt.Claims{\n\t\t\tID:     jti,\n\t\t\tIssuer: util.AppName,\n\t\t})\n\t\tif err != nil {\n\t\t\tctx.StopWithError(iris.StatusInternalServerError, err)\n\t\t\treturn\n\t\t}\n\n\t\tctx.Write(token)\n\t}\n}\n\n// SignOut invalidates a user from server-side using the jwt Blocklist.\nfunc SignOut(ctx iris.Context) {\n\tctx.Logout() // this is automatically binded to a function which invalidates the current request token by the JWT Verifier above.\n}\n\n// GetClaims returns the current authorized client claims.\nfunc GetClaims(ctx iris.Context) *UserClaims {\n\tclaims := jwt.Get(ctx).(*UserClaims)\n\treturn claims\n}\n\n// GetUserID returns the current authorized client's user id extracted from claims.\nfunc GetUserID(ctx iris.Context) string {\n\treturn GetClaims(ctx).UserID\n}\n\n// IsAdmin reports whether the current client has admin access.\nfunc IsAdmin(ctx iris.Context) bool {\n\tfor _, role := range GetClaims(ctx).Roles {\n\t\tif role == model.Admin {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n"
  },
  {
    "path": "_examples/auth/jwt/tutorial/api/router.go",
    "content": "package api\n\nimport (\n\t\"myapp/domain/repository\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\n// NewRouter accepts some dependencies\n// and returns a function which returns the routes on the given Iris Party (group of routes).\nfunc NewRouter(userRepo repository.UserRepository, todoRepo repository.TodoRepository) func(iris.Party) {\n\treturn func(router iris.Party) {\n\t\trouter.Post(\"/signin\", SignIn(userRepo))\n\n\t\trouter.Use(Verify()) // protect the next routes with JWT.\n\n\t\trouter.Post(\"/todos\", CreateTodo(todoRepo))\n\t\trouter.Get(\"/todos\", ListTodos(todoRepo))\n\t\trouter.Get(\"/todos/{id}\", GetTodo(todoRepo))\n\n\t\trouter.Get(\"/admin/todos\", AllowAdmin, ListAllTodos(todoRepo))\n\t}\n}\n"
  },
  {
    "path": "_examples/auth/jwt/tutorial/api/todo.go",
    "content": "package api\n\nimport (\n\t\"errors\"\n\t\"myapp/domain/repository\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\n// TodoRequest represents a Todo HTTP request.\ntype TodoRequest struct {\n\tTitle string `json:\"title\" form:\"title\" url:\"title\"`\n\tBody  string `json:\"body\" form:\"body\" url:\"body\"`\n}\n\n// CreateTodo handles the creation of a Todo entry.\nfunc CreateTodo(repo repository.TodoRepository) iris.Handler {\n\treturn func(ctx iris.Context) {\n\t\tvar req TodoRequest\n\t\terr := ctx.ReadBody(&req) // will bind the \"req\" to a JSON, form or url query request data.\n\t\tif err != nil {\n\t\t\tctx.StopWithError(iris.StatusBadRequest, err)\n\t\t\treturn\n\t\t}\n\n\t\tuserID := GetUserID(ctx)\n\t\ttodo, err := repo.Create(userID, req.Title, req.Body)\n\t\tif err != nil {\n\t\t\tctx.StopWithError(iris.StatusInternalServerError, err)\n\t\t\treturn\n\t\t}\n\n\t\tctx.StatusCode(iris.StatusCreated)\n\t\tctx.JSON(todo)\n\t}\n}\n\n// GetTodo lists all users todos.\n// Parameter: {id}.\nfunc GetTodo(repo repository.TodoRepository) iris.Handler {\n\treturn func(ctx iris.Context) {\n\t\tid := ctx.Params().Get(\"id\")\n\t\tuserID := GetUserID(ctx)\n\n\t\ttodo, err := repo.GetByID(id)\n\t\tif err != nil {\n\t\t\tcode := iris.StatusInternalServerError\n\t\t\tif errors.Is(err, repository.ErrNotFound) {\n\t\t\t\tcode = iris.StatusNotFound\n\t\t\t}\n\n\t\t\tctx.StopWithError(code, err)\n\t\t\treturn\n\t\t}\n\n\t\tif !IsAdmin(ctx) { // admin can access any user's todos.\n\t\t\tif todo.UserID != userID {\n\t\t\t\tctx.StopWithStatus(iris.StatusForbidden)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tctx.JSON(todo)\n\t}\n}\n\n// ListTodos lists todos of the current user.\nfunc ListTodos(repo repository.TodoRepository) iris.Handler {\n\treturn func(ctx iris.Context) {\n\t\tuserID := GetUserID(ctx)\n\t\ttodos, err := repo.GetAllByUser(userID)\n\t\tif err != nil {\n\t\t\tctx.StopWithError(iris.StatusInternalServerError, err)\n\t\t\treturn\n\t\t}\n\n\t\t// if len(todos) == 0 {\n\t\t// \tctx.StopWithError(iris.StatusNotFound, fmt.Errorf(\"no entries found\"))\n\t\t// \treturn\n\t\t// }\n\t\t// Or let the client decide what to do on empty list.\n\t\tctx.JSON(todos)\n\t}\n}\n\n// ListAllTodos lists all users todos.\n// Access: admin.\n// Middleware: AllowAdmin.\nfunc ListAllTodos(repo repository.TodoRepository) iris.Handler {\n\treturn func(ctx iris.Context) {\n\t\ttodos, err := repo.GetAll()\n\t\tif err != nil {\n\t\t\tctx.StopWithError(iris.StatusInternalServerError, err)\n\t\t\treturn\n\t\t}\n\n\t\tctx.JSON(todos)\n\t}\n}\n\n/* Leave as exercise: use filtering instead...\n\n// ListTodosByUser lists all todos by a specific user.\n// Access: admin.\n// Middleware: AllowAdmin.\n// Parameter: {id}.\nfunc ListTodosByUser(repo repository.TodoRepository) iris.Handler {\n\treturn func(ctx iris.Context) {\n\t\tuserID := ctx.Params().Get(\"id\")\n\t\ttodos, err := repo.GetAllByUser(userID)\n\t\tif err != nil {\n\t\t\tctx.StopWithError(iris.StatusInternalServerError, err)\n\t\t\treturn\n\t\t}\n\n\t\tctx.JSON(todos)\n\t}\n}\n*/\n"
  },
  {
    "path": "_examples/auth/jwt/tutorial/domain/model/role.go",
    "content": "package model\n\n// Role represents a role.\ntype Role string\n\nconst (\n\t// Admin represents the Admin access role.\n\tAdmin Role = \"admin\"\n)\n"
  },
  {
    "path": "_examples/auth/jwt/tutorial/domain/model/todo.go",
    "content": "package model\n\n// Todo represents the Todo model.\ntype Todo struct {\n\tID        string `json:\"id\"`\n\tUserID    string `json:\"user_id\"`\n\tTitle     string `json:\"title\"`\n\tBody      string `json:\"body\"`\n\tCreatedAt int64  `json:\"created_at\"` // unix seconds.\n}\n"
  },
  {
    "path": "_examples/auth/jwt/tutorial/domain/model/user.go",
    "content": "package model\n\n// User represents our User model.\ntype User struct {\n\tID             string `json:\"id\"`\n\tUsername       string `json:\"username\"`\n\tHashedPassword []byte `json:\"-\"`\n\tRoles          []Role `json:\"roles\"`\n}\n"
  },
  {
    "path": "_examples/auth/jwt/tutorial/domain/repository/samples.go",
    "content": "package repository\n\nimport (\n\t\"fmt\"\n\n\t\"myapp/domain/model\"\n)\n\n// GenerateSamples generates data samples.\nfunc GenerateSamples(userRepo UserRepository, todoRepo TodoRepository) error {\n\t// Create users.\n\tfor _, username := range []string{\"vasiliki\", \"george\", \"kwstas\"} {\n\t\t// My grandmother.\n\t\t// My young brother.\n\t\t// My youngest brother.\n\t\tpassword := fmt.Sprintf(\"%s_pass\", username)\n\t\tif _, err := userRepo.Create(username, password); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\t// Create a user with admin role.\n\tif _, err := userRepo.Create(\"admin\", \"admin\", model.Admin); err != nil {\n\t\treturn err\n\t}\n\n\t// Create two todos per user.\n\tusers, err := userRepo.GetAll()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor i, u := range users {\n\t\tfor j := 0; j < 2; j++ {\n\t\t\ttitle := fmt.Sprintf(\"%s todo %d:%d title\", u.Username, i, j)\n\t\t\tbody := fmt.Sprintf(\"%s todo %d:%d body\", u.Username, i, j)\n\t\t\t_, err := todoRepo.Create(u.ID, title, body)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "_examples/auth/jwt/tutorial/domain/repository/todo_repository.go",
    "content": "package repository\n\nimport (\n\t\"errors\"\n\t\"sync\"\n\n\t\"myapp/domain/model\"\n\t\"myapp/util\"\n)\n\n// ErrNotFound indicates that an entry was not found.\n// Usage: errors.Is(err, ErrNotFound)\nvar ErrNotFound = errors.New(\"not found\")\n\n// TodoRepository is responsible for Todo CRUD operations,\n// however, for the sake of the example we only implement the Create and Read ones.\ntype TodoRepository interface {\n\tCreate(userID, title, body string) (model.Todo, error)\n\tGetByID(id string) (model.Todo, error)\n\tGetAll() ([]model.Todo, error)\n\tGetAllByUser(userID string) ([]model.Todo, error)\n}\n\nvar (\n\t_ TodoRepository = (*memoryTodoRepository)(nil)\n)\n\ntype memoryTodoRepository struct {\n\ttodos []model.Todo // map[string]model.Todo\n\tmu    sync.RWMutex\n}\n\n// NewMemoryTodoRepository returns the default in-memory todo repository.\nfunc NewMemoryTodoRepository() TodoRepository {\n\tr := new(memoryTodoRepository)\n\treturn r\n}\n\nfunc (r *memoryTodoRepository) Create(userID, title, body string) (model.Todo, error) {\n\tid, err := util.GenerateUUID()\n\tif err != nil {\n\t\treturn model.Todo{}, err\n\t}\n\n\ttodo := model.Todo{\n\t\tID:        id,\n\t\tUserID:    userID,\n\t\tTitle:     title,\n\t\tBody:      body,\n\t\tCreatedAt: util.Now().Unix(),\n\t}\n\n\tr.mu.Lock()\n\tr.todos = append(r.todos, todo)\n\tr.mu.Unlock()\n\n\treturn todo, nil\n}\n\nfunc (r *memoryTodoRepository) GetByID(id string) (model.Todo, error) {\n\tr.mu.RLock()\n\tdefer r.mu.RUnlock()\n\n\tfor _, todo := range r.todos {\n\t\tif todo.ID == id {\n\t\t\treturn todo, nil\n\t\t}\n\t}\n\n\treturn model.Todo{}, ErrNotFound\n}\n\nfunc (r *memoryTodoRepository) GetAll() ([]model.Todo, error) {\n\tr.mu.RLock()\n\ttmp := make([]model.Todo, len(r.todos))\n\tcopy(tmp, r.todos)\n\tr.mu.RUnlock()\n\treturn tmp, nil\n}\n\nfunc (r *memoryTodoRepository) GetAllByUser(userID string) ([]model.Todo, error) {\n\t// initialize a slice, so we don't have \"null\" at empty response.\n\ttodos := make([]model.Todo, 0)\n\n\tr.mu.RLock()\n\tfor _, todo := range r.todos {\n\t\tif todo.UserID == userID {\n\t\t\ttodos = append(todos, todo)\n\t\t}\n\t}\n\tr.mu.RUnlock()\n\n\treturn todos, nil\n}\n"
  },
  {
    "path": "_examples/auth/jwt/tutorial/domain/repository/user_repository.go",
    "content": "package repository\n\nimport (\n\t\"sync\"\n\n\t\"myapp/domain/model\"\n\t\"myapp/util\"\n)\n\n// UserRepository is responsible for User CRUD operations,\n// however, for the sake of the example we only implement the Read one.\ntype UserRepository interface {\n\tCreate(username, password string, roles ...model.Role) (model.User, error)\n\t// GetByUsernameAndPassword should return a User based on the given input.\n\tGetByUsernameAndPassword(username, password string) (model.User, bool)\n\tGetAll() ([]model.User, error)\n}\n\nvar (\n\t_ UserRepository = (*memoryUserRepository)(nil)\n)\n\ntype memoryUserRepository struct {\n\t// Users represents a user database.\n\t// For the sake of the tutorial we use a simple slice of users.\n\tusers []model.User\n\tmu    sync.RWMutex\n}\n\n// NewMemoryUserRepository returns the default in-memory user repository.\nfunc NewMemoryUserRepository() UserRepository {\n\tr := new(memoryUserRepository)\n\treturn r\n}\n\nfunc (r *memoryUserRepository) Create(username, password string, roles ...model.Role) (model.User, error) {\n\tid, err := util.GenerateUUID()\n\tif err != nil {\n\t\treturn model.User{}, err\n\t}\n\n\thashedPassword, err := util.GeneratePassword(password)\n\tif err != nil {\n\t\treturn model.User{}, err\n\t}\n\n\tuser := model.User{\n\t\tID:             id,\n\t\tUsername:       username,\n\t\tHashedPassword: hashedPassword,\n\t\tRoles:          roles,\n\t}\n\n\tr.mu.Lock()\n\tr.users = append(r.users, user)\n\tr.mu.Unlock()\n\n\treturn user, nil\n}\n\n// GetByUsernameAndPassword returns a user from the storage based on the given \"username\" and \"password\".\nfunc (r *memoryUserRepository) GetByUsernameAndPassword(username, password string) (model.User, bool) {\n\tfor _, u := range r.users { // our example uses a static slice.\n\t\tif u.Username == username {\n\t\t\t// we compare the user input and the stored hashed password.\n\t\t\tok := util.ValidatePassword(password, u.HashedPassword)\n\t\t\tif ok {\n\t\t\t\treturn u, true\n\t\t\t}\n\t\t}\n\t}\n\n\treturn model.User{}, false\n}\n\nfunc (r *memoryUserRepository) GetAll() ([]model.User, error) {\n\tr.mu.RLock()\n\ttmp := make([]model.User, len(r.users))\n\tcopy(tmp, r.users)\n\tr.mu.RUnlock()\n\treturn tmp, nil\n}\n"
  },
  {
    "path": "_examples/auth/jwt/tutorial/go-client/README.md",
    "content": "# Go Client\n\n```sh\n$ go run .\n```\n\n```sh\n2020/11/04 21:08:40 Access Token:\n\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiYTAwYzI3ZDEtYjVhYS00NjU0LWFmMTYtYjExNzNkZTY1NjI5Iiwicm9sZXMiOlsiYWRtaW4iXSwiaWF0IjoxNjA0NTE2OTIwLCJleHAiOjE2MDQ1MTc4MjAsImp0aSI6IjYzNmVmMDc0LTE2MzktNGJhZi1hNGNiLTQ4ZDM4NGMxMzliYSIsImlzcyI6Im15YXBwIn0.T9B0zG0AHShO5JfQgrMQBlToH33KHgp8nLMPFpN6QmM\"\n2020/11/04 21:08:40 Todo Created:\nmodel.Todo{ID:\"cfa38d7a-c556-4301-ae1f-fb90f705071c\", UserID:\"a00c27d1-b5aa-4654-af16-b1173de65629\", Title:\"test todo title\", Body:\"test todo body contents\", CreatedAt:1604516920}\n```\n"
  },
  {
    "path": "_examples/auth/jwt/tutorial/go-client/client.go",
    "content": "package main\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strconv\"\n\t\"strings\"\n)\n\n// Client is the default http client instance used by the following methods.\nvar Client = http.DefaultClient\n\n// RequestOption is a function which can be used to modify\n// a request instance before Do.\ntype RequestOption func(*http.Request) error\n\n// WithAccessToken sets the given \"token\" to the authorization request header.\nfunc WithAccessToken(token []byte) RequestOption {\n\tbearer := \"Bearer \" + string(token)\n\treturn func(req *http.Request) error {\n\t\treq.Header.Add(\"Authorization\", bearer)\n\t\treturn nil\n\t}\n}\n\n// WithContentType sets the content-type request header.\nfunc WithContentType(cType string) RequestOption {\n\treturn func(req *http.Request) error {\n\t\treq.Header.Set(\"Content-Type\", cType)\n\t\treturn nil\n\t}\n}\n\n// WithContentLength sets the content-length request header.\nfunc WithContentLength(length int) RequestOption {\n\treturn func(req *http.Request) error {\n\t\treq.Header.Set(\"Content-Length\", strconv.Itoa(length))\n\t\treturn nil\n\t}\n}\n\n// Do fires a request to the server.\nfunc Do(method, url string, body io.Reader, opts ...RequestOption) (*http.Response, error) {\n\treq, err := http.NewRequest(method, url, body)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tfor _, opt := range opts {\n\t\tif err = opt(req); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\treturn Client.Do(req)\n}\n\n// JSON fires a request with \"v\" as client json data.\nfunc JSON(method, url string, v any, opts ...RequestOption) (*http.Response, error) {\n\tbuf := new(bytes.Buffer)\n\terr := json.NewEncoder(buf).Encode(v)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\topts = append(opts, WithContentType(\"application/json; charset=utf-8\"))\n\treturn Do(method, url, buf, opts...)\n}\n\n// Form fires a request with \"formData\" as client form data.\nfunc Form(method, url string, formData url.Values, opts ...RequestOption) (*http.Response, error) {\n\tencoded := formData.Encode()\n\tbody := strings.NewReader(encoded)\n\n\topts = append([]RequestOption{\n\t\tWithContentType(\"application/x-www-form-urlencoded\"),\n\t\tWithContentLength(len(encoded)),\n\t}, opts...)\n\n\treturn Do(method, url, body, opts...)\n}\n\n// BindResponse binds a response body to the \"dest\" pointer and closes the body.\nfunc BindResponse(resp *http.Response, dest any) error {\n\tcontentType := resp.Header.Get(\"Content-Type\")\n\tif idx := strings.IndexRune(contentType, ';'); idx > 0 {\n\t\tcontentType = contentType[0:idx]\n\t}\n\n\tswitch contentType {\n\tcase \"application/json\":\n\t\tdefer resp.Body.Close()\n\t\treturn json.NewDecoder(resp.Body).Decode(dest)\n\tdefault:\n\t\treturn fmt.Errorf(\"unsupported content type: %s\", contentType)\n\t}\n}\n\n// RawResponse simply returns the raw response body.\nfunc RawResponse(resp *http.Response) ([]byte, error) {\n\tdefer resp.Body.Close()\n\n\treturn io.ReadAll(resp.Body)\n}\n"
  },
  {
    "path": "_examples/auth/jwt/tutorial/go-client/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\t\"net/url\"\n\n\t\"myapp/api\"\n\t\"myapp/domain/model\"\n)\n\nconst base = \"http://localhost:8080\"\n\nfunc main() {\n\taccessToken, err := authenticate(\"admin\", \"admin\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tlog.Printf(\"Access Token:\\n%q\", accessToken)\n\n\ttodo, err := createTodo(accessToken, \"test todo title\", \"test todo body contents\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tlog.Printf(\"Todo Created:\\n%#+v\", todo)\n}\n\nfunc authenticate(username, password string) ([]byte, error) {\n\tendpoint := base + \"/signin\"\n\n\tdata := make(url.Values)\n\tdata.Set(\"username\", username)\n\tdata.Set(\"password\", password)\n\n\tresp, err := Form(http.MethodPost, endpoint, data)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\taccessToken, err := RawResponse(resp)\n\treturn accessToken, err\n}\n\nfunc createTodo(accessToken []byte, title, body string) (model.Todo, error) {\n\tvar todo model.Todo\n\n\tendpoint := base + \"/todos\"\n\n\treq := api.TodoRequest{\n\t\tTitle: title,\n\t\tBody:  body,\n\t}\n\n\tresp, err := JSON(http.MethodPost, endpoint, req, WithAccessToken(accessToken))\n\tif err != nil {\n\t\treturn todo, err\n\t}\n\n\tif resp.StatusCode != http.StatusCreated {\n\t\trawData, _ := RawResponse(resp)\n\t\treturn todo, fmt.Errorf(\"failed to create a todo: %s\", string(rawData))\n\t}\n\n\terr = BindResponse(resp, &todo)\n\treturn todo, err\n}\n"
  },
  {
    "path": "_examples/auth/jwt/tutorial/go.mod",
    "content": "module myapp\n\ngo 1.25\n\nrequire (\n\tgithub.com/google/uuid v1.6.0\n\tgithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd\n\tgolang.org/x/crypto v0.47.0\n)\n\nrequire (\n\tgithub.com/BurntSushi/toml v1.6.0 // indirect\n\tgithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect\n\tgithub.com/CloudyKit/jet/v6 v6.3.1 // indirect\n\tgithub.com/Joker/jade v1.1.3 // indirect\n\tgithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 // indirect\n\tgithub.com/andybalholm/brotli v1.2.0 // indirect\n\tgithub.com/aymerick/douceur v0.2.0 // indirect\n\tgithub.com/fatih/structs v1.1.0 // indirect\n\tgithub.com/flosch/pongo2/v4 v4.0.2 // indirect\n\tgithub.com/golang/snappy v1.0.0 // indirect\n\tgithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a // indirect\n\tgithub.com/gorilla/css v1.0.1 // indirect\n\tgithub.com/iris-contrib/schema v0.0.6 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/kataras/blocks v0.0.8 // indirect\n\tgithub.com/kataras/golog v0.1.12 // indirect\n\tgithub.com/kataras/jwt v0.1.17 // indirect\n\tgithub.com/kataras/pio v0.0.13 // indirect\n\tgithub.com/kataras/sitemap v0.0.6 // indirect\n\tgithub.com/kataras/tunnel v0.0.4 // indirect\n\tgithub.com/klauspost/compress v1.18.2 // indirect\n\tgithub.com/kr/pretty v0.3.1 // indirect\n\tgithub.com/mailgun/raymond/v2 v2.0.48 // indirect\n\tgithub.com/mailru/easyjson v0.9.1 // indirect\n\tgithub.com/microcosm-cc/bluemonday v1.0.27 // indirect\n\tgithub.com/rogpeppe/go-internal v1.10.0 // indirect\n\tgithub.com/russross/blackfriday/v2 v2.1.0 // indirect\n\tgithub.com/schollz/closestmatch v2.1.0+incompatible // indirect\n\tgithub.com/sirupsen/logrus v1.8.1 // indirect\n\tgithub.com/stretchr/testify v1.11.1 // indirect\n\tgithub.com/tdewolff/minify/v2 v2.24.8 // indirect\n\tgithub.com/tdewolff/parse/v2 v2.8.5 // indirect\n\tgithub.com/valyala/bytebufferpool v1.0.0 // indirect\n\tgithub.com/vmihailenco/msgpack/v5 v5.4.1 // indirect\n\tgithub.com/vmihailenco/tagparser/v2 v2.0.0 // indirect\n\tgithub.com/yosssi/ace v0.0.5 // indirect\n\tgolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 // indirect\n\tgolang.org/x/net v0.49.0 // indirect\n\tgolang.org/x/sys v0.40.0 // indirect\n\tgolang.org/x/text v0.33.0 // indirect\n\tgolang.org/x/time v0.14.0 // indirect\n\tgoogle.golang.org/protobuf v1.36.11 // indirect\n\tgopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect\n\tgopkg.in/ini.v1 v1.67.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n"
  },
  {
    "path": "_examples/auth/jwt/tutorial/go.sum",
    "content": "github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=\ngithub.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=\ngithub.com/CloudyKit/jet/v6 v6.3.1 h1:6IAo5Cx21xrHVaR8zzXN5gJatKV/wO7Nf6bfCnCSbUw=\ngithub.com/CloudyKit/jet/v6 v6.3.1/go.mod h1:lf8ksdNsxZt7/yH/3n4vJQWA9RUq4wpaHtArHhGVMOw=\ngithub.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc=\ngithub.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=\ngithub.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=\ngithub.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 h1:dG7/gLroJGht/jSQtHiLvT48Hxn+crbmvyItZC8cWOs=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05/go.mod h1:NYezi6wtnJtBm5btoprXc5SvAdqH0XTXWnUup0MptAI=\ngithub.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=\ngithub.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=\ngithub.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=\ngithub.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=\ngithub.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=\ngithub.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=\ngithub.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=\ngithub.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=\ngithub.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=\ngithub.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw=\ngithub.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8=\ngithub.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=\ngithub.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=\ngithub.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=\ngithub.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a h1:l7A0loSszR5zHd/qK53ZIHMO8b3bBSmENnQ6eKnUT0A=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=\ngithub.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=\ngithub.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=\ngithub.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=\ngithub.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=\ngithub.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2 h1:T9THsdP1woyAqKHwjkEsbCnMefsAFvk8iJJKokcJ3Go=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2/go.mod h1:JLDgIqnFy5loDSUv1OA2j0mb6p/rDhiCqigP22Uq9xE=\ngithub.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw=\ngithub.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/kataras/blocks v0.0.8 h1:MrpVhoFTCR2v1iOOfGng5VJSILKeZZI+7NGfxEh3SUM=\ngithub.com/kataras/blocks v0.0.8/go.mod h1:9Jm5zx6BB+06NwA+OhTbHW1xkMOYxahnqTN5DveZ2Yg=\ngithub.com/kataras/golog v0.1.12 h1:Bu7I/G4ilJlbfzjmU39O9N+2uO1pBcMK045fzZ4ytNg=\ngithub.com/kataras/golog v0.1.12/go.mod h1:wrGSbOiBqbQSQznleVNX4epWM8rl9SJ/rmEacl0yqy4=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd h1:oMsQgqKACbUdlaIWnN+EZzzAnHkw90hE/y/R0BSij2U=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd/go.mod h1:yucjtDl9Vn3OctWM1fi0MuM9OckemC7kEreFa9QtPF8=\ngithub.com/kataras/jwt v0.1.17 h1:dYjemzcdYqA4ylwq9/56MslCr/pNOyVUZ2bl3hYNHgc=\ngithub.com/kataras/jwt v0.1.17/go.mod h1:HUnU5HDBCDanVF8zrPVSE2VK8HicospKefZDD4DzOKU=\ngithub.com/kataras/pio v0.0.13 h1:x0rXVX0fviDTXOOLOmr4MUxOabu1InVSTu5itF8CXCM=\ngithub.com/kataras/pio v0.0.13/go.mod h1:k3HNuSw+eJ8Pm2lA4lRhg3DiCjVgHlP8hmXApSej3oM=\ngithub.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY=\ngithub.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4=\ngithub.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA=\ngithub.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw=\ngithub.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=\ngithub.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=\ngithub.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=\ngithub.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8=\ngithub.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=\ngithub.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=\ngithub.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=\ngithub.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=\ngithub.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=\ngithub.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=\ngithub.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=\ngithub.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=\ngithub.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=\ngithub.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=\ngithub.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=\ngithub.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=\ngithub.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk=\ngithub.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=\ngithub.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=\ngithub.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=\ngithub.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=\ngithub.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/tdewolff/minify/v2 v2.24.8 h1:58/VjsbevI4d5FGV0ZSuBrHMSSkH4MCH0sIz/eKIauE=\ngithub.com/tdewolff/minify/v2 v2.24.8/go.mod h1:0Ukj0CRpo/sW/nd8uZ4ccXaV1rEVIWA3dj8U7+Shhfw=\ngithub.com/tdewolff/parse/v2 v2.8.5 h1:ZmBiA/8Do5Rpk7bDye0jbbDUpXXbCdc3iah4VeUvwYU=\ngithub.com/tdewolff/parse/v2 v2.8.5/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=\ngithub.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE=\ngithub.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=\ngithub.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=\ngithub.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=\ngithub.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=\ngithub.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=\ngithub.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=\ngithub.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=\ngithub.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA=\ngithub.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0=\ngithub.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=\ngithub.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=\ngithub.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=\ngolang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=\ngolang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=\ngolang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=\ngolang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=\ngolang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=\ngolang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=\ngolang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=\ngolang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=\ngoogle.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=\ngopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nmoul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs=\nmoul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE=\n"
  },
  {
    "path": "_examples/auth/jwt/tutorial/main.go",
    "content": "package main\n\nimport (\n\t\"myapp/api\"\n\t\"myapp/domain/repository\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nvar (\n\tuserRepo = repository.NewMemoryUserRepository()\n\ttodoRepo = repository.NewMemoryTodoRepository()\n)\n\nfunc main() {\n\tif err := repository.GenerateSamples(userRepo, todoRepo); err != nil {\n\t\tpanic(err)\n\t}\n\n\tapp := iris.New()\n\tapp.PartyFunc(\"/\", api.NewRouter(userRepo, todoRepo))\n\n\t// POST http://localhost:8080/signin (Form: username, password)\n\t// GET  http://localhost:8080/todos\n\t// GET  http://localhost:8080/todos/{id}\n\t// POST http://localhost:8080/todos (JSON, Form or URL: title, body)\n\t// GET  http://localhost:8080/admin/todos\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/auth/jwt/tutorial/util/app.go",
    "content": "package util\n\n// Constants for the application.\nconst (\n\tVersion = \"0.0.1\"\n\tAppName = \"myapp\"\n)\n"
  },
  {
    "path": "_examples/auth/jwt/tutorial/util/clock.go",
    "content": "package util\n\nimport \"time\"\n\n// Now is the default current time for the whole application.\n// Can be modified for testing or custom timezone.\nvar Now = time.Now\n"
  },
  {
    "path": "_examples/auth/jwt/tutorial/util/password.go",
    "content": "package util\n\nimport \"golang.org/x/crypto/bcrypt\"\n\n// MustGeneratePassword same as GeneratePassword but panics on errors.\nfunc MustGeneratePassword(userPassword string) []byte {\n\thashed, err := GeneratePassword(userPassword)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treturn hashed\n}\n\n// GeneratePassword will generate a hashed password for us based on the\n// user's input.\nfunc GeneratePassword(userPassword string) ([]byte, error) {\n\treturn bcrypt.GenerateFromPassword([]byte(userPassword), bcrypt.DefaultCost)\n}\n\n// ValidatePassword will check if passwords are matched.\nfunc ValidatePassword(userPassword string, hashed []byte) bool {\n\terr := bcrypt.CompareHashAndPassword(hashed, []byte(userPassword))\n\treturn err == nil\n}\n"
  },
  {
    "path": "_examples/auth/jwt/tutorial/util/uuid.go",
    "content": "package util\n\nimport \"github.com/google/uuid\"\n\n// MustGenerateUUID returns a new v4 UUID or panics.\nfunc MustGenerateUUID() string {\n\tid, err := GenerateUUID()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treturn id\n}\n\n// GenerateUUID returns a new v4 UUID.\nfunc GenerateUUID() (string, error) {\n\tid, err := uuid.NewRandom()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\treturn id.String(), nil\n}\n"
  },
  {
    "path": "_examples/auth/permissions/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"strings\"\n\n\t\"github.com/kataras/iris/v12\"\n\n\tpermissions \"github.com/xyproto/permissionbolt\"\n\t// * PostgreSQL support:\n\t//   permissions \"github.com/xyproto/pstore\" and\n\t//   perm, err := permissions.New(...)\n\t//\n\t// * MariaDB/MySQL support:\n\t//   permissions \"github.com/xyproto/permissionsql\" and\n\t//   perm, err := permissions.New/NewWithDSN(...)\n\t// * Redis support:\n\t//   permissions \"github.com/xyproto/permissions2\"\n\t//   perm, err := permissions.New2()\n\t// * Bolt support (this one):\n\t//   permissions \"github.com/xyproto/permissionbolt\" and\n\t//   perm, err := permissions.New(...)\n)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.Logger().SetLevel(\"debug\")\n\n\t// New permissions middleware.\n\tperm, err := permissions.New()\n\tif err != nil {\n\t\tlog.Fatalln(err)\n\t}\n\n\t// Blank slate, no default permissions\n\t// perm.Clear()\n\n\t// Set up a middleware handler for Iris, with a custom \"permission denied\" message.\n\tpermissionHandler := func(ctx iris.Context) {\n\t\t// Check if the user has the right admin/user rights\n\t\tif perm.Rejected(ctx.ResponseWriter(), ctx.Request()) {\n\t\t\t// Deny the request, don't call other middleware handlers\n\t\t\tctx.StopWithText(iris.StatusForbidden, \"Permission denied!\")\n\t\t\treturn\n\t\t}\n\t\t// Call the next middleware handler\n\t\tctx.Next()\n\t}\n\n\t// Register the permissions middleware\n\tapp.Use(permissionHandler)\n\n\t// Get the userstate, used in the handlers below\n\tuserstate := perm.UserState()\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tmsg := \"\"\n\t\tmsg += fmt.Sprintf(\"Has user bob: %v\\n\", userstate.HasUser(\"bob\"))\n\t\tmsg += fmt.Sprintf(\"Logged in on server: %v\\n\", userstate.IsLoggedIn(\"bob\"))\n\t\tmsg += fmt.Sprintf(\"Is confirmed: %v\\n\", userstate.IsConfirmed(\"bob\"))\n\t\tmsg += fmt.Sprintf(\"Username stored in cookies (or blank): %v\\n\", userstate.Username(ctx.Request()))\n\t\tmsg += fmt.Sprintf(\"Current user is logged in, has a valid cookie and *user rights*: %v\\n\", userstate.UserRights(ctx.Request()))\n\t\tmsg += fmt.Sprintf(\"Current user is logged in, has a valid cookie and *admin rights*: %v\\n\", userstate.AdminRights(ctx.Request()))\n\t\tmsg += fmt.Sprintln(\"\\nTry: /register, /confirm, /remove, /login, /logout, /makeadmin, /clear, /data and /admin\")\n\t\tctx.WriteString(msg)\n\t})\n\n\tapp.Get(\"/register\", func(ctx iris.Context) {\n\t\tuserstate.AddUser(\"bob\", \"hunter1\", \"bob@zombo.com\")\n\t\tctx.Writef(\"User bob was created: %v\\n\", userstate.HasUser(\"bob\"))\n\t})\n\n\tapp.Get(\"/confirm\", func(ctx iris.Context) {\n\t\tuserstate.MarkConfirmed(\"bob\")\n\t\tctx.Writef(\"User bob was confirmed: %v\\n\", userstate.IsConfirmed(\"bob\"))\n\t})\n\n\tapp.Get(\"/remove\", func(ctx iris.Context) {\n\t\tuserstate.RemoveUser(\"bob\")\n\t\tctx.Writef(\"User bob was removed: %v\\n\", !userstate.HasUser(\"bob\"))\n\t})\n\n\tapp.Get(\"/login\", func(ctx iris.Context) {\n\t\t// Headers will be written, for storing a cookie\n\t\tuserstate.Login(ctx.ResponseWriter(), \"bob\")\n\t\tctx.Writef(\"bob is now logged in: %v\\n\", userstate.IsLoggedIn(\"bob\"))\n\t})\n\n\tapp.Get(\"/logout\", func(ctx iris.Context) {\n\t\tuserstate.Logout(\"bob\")\n\t\tctx.Writef(\"bob is now logged out: %v\\n\", !userstate.IsLoggedIn(\"bob\"))\n\t})\n\n\tapp.Get(\"/makeadmin\", func(ctx iris.Context) {\n\t\tuserstate.SetAdminStatus(\"bob\")\n\t\tctx.Writef(\"bob is now administrator: %v\\n\", userstate.IsAdmin(\"bob\"))\n\t})\n\n\tapp.Get(\"/clear\", func(ctx iris.Context) {\n\t\tuserstate.ClearCookie(ctx.ResponseWriter())\n\t\tctx.WriteString(\"Clearing cookie\")\n\t})\n\n\tapp.Get(\"/data\", func(ctx iris.Context) {\n\t\tctx.WriteString(\"user page that only logged in users must see!\")\n\t})\n\n\tapp.Get(\"/admin\", func(ctx iris.Context) {\n\t\tctx.WriteString(\"super secret information that only logged in administrators must see!\\n\\n\")\n\t\tif usernames, err := userstate.AllUsernames(); err == nil {\n\t\t\tctx.Writef(\"list of all users: %s\", strings.Join(usernames, \", \"))\n\t\t}\n\t})\n\n\t// Serve\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/auth/recaptcha/custom_form/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n\n\t\"github.com/kataras/iris/v12/middleware/recaptcha\"\n)\n\n// keys should be obtained by https://www.google.com/recaptcha\nconst (\n\trecaptchaPublic = \"6Lf3WywUAAAAAKNfAm5DP2J5ahqedtZdHTYaKkJ6\"\n\trecaptchaSecret = \"6Lf3WywUAAAAAJpArb8nW_LCL_PuPuokmEABFfgw\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\tr := recaptcha.New(recaptchaSecret)\n\n\tapp.Get(\"/comment\", showRecaptchaForm)\n\n\t// pass the middleware before the main handler or use the `recaptcha.SiteVerify`.\n\tapp.Post(\"/comment\", r, postComment)\n\n\tapp.Listen(\":8080\")\n}\n\nvar htmlForm = `<form action=\"/comment\" method=\"POST\">\n\t    <script src=\"https://www.google.com/recaptcha/api.js\"></script>\n\t\t<div class=\"g-recaptcha\" data-sitekey=\"%s\"></div>\n    \t<input type=\"submit\" name=\"button\" value=\"Verify\">\n</form>`\n\nfunc showRecaptchaForm(ctx iris.Context) {\n\tcontents := fmt.Sprintf(htmlForm, recaptchaPublic)\n\tctx.HTML(contents)\n}\n\nfunc postComment(ctx iris.Context) {\n\t// [...]\n\tctx.JSON(iris.Map{\"success\": true})\n}\n"
  },
  {
    "path": "_examples/auth/recaptcha/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/middleware/recaptcha\"\n)\n\n// keys should be obtained by https://www.google.com/recaptcha\nconst (\n\trecaptchaPublic = \"\"\n\trecaptchaSecret = \"\"\n)\n\nfunc showRecaptchaForm(ctx iris.Context, path string) {\n\tctx.HTML(recaptcha.GetFormHTML(recaptchaPublic, path))\n}\n\nfunc main() {\n\tapp := iris.New()\n\n\t// On both Get and Post on this example, so you can easly\n\t// use a single route to show a form and the main subject if recaptcha's validation result succeed.\n\tapp.HandleMany(\"GET POST\", \"/\", func(ctx iris.Context) {\n\t\tif ctx.Method() == iris.MethodGet {\n\t\t\tshowRecaptchaForm(ctx, \"/\")\n\t\t\treturn\n\t\t}\n\n\t\tresult := recaptcha.SiteVerify(ctx, recaptchaSecret)\n\t\tif !result.Success {\n\t\t\t/* redirect here if u want or do nothing */\n\t\t\tctx.HTML(\"<b> failed please try again </b>\")\n\t\t\treturn\n\t\t}\n\n\t\tctx.Writef(\"succeed.\")\n\t})\n\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/bootstrapper/bootstrap/bootstrapper.go",
    "content": "package bootstrap\n\nimport (\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/gorilla/securecookie\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/middleware/logger\"\n\t\"github.com/kataras/iris/v12/middleware/recover\"\n\t\"github.com/kataras/iris/v12/sessions\"\n\t\"github.com/kataras/iris/v12/websocket\"\n)\n\ntype Configurator func(*Bootstrapper)\n\ntype Bootstrapper struct {\n\t*iris.Application\n\tAppName      string\n\tAppOwner     string\n\tAppSpawnDate time.Time\n\n\tSessions *sessions.Sessions\n}\n\n// New returns a new Bootstrapper.\nfunc New(appName, appOwner string, cfgs ...Configurator) *Bootstrapper {\n\tb := &Bootstrapper{\n\t\tAppName:      appName,\n\t\tAppOwner:     appOwner,\n\t\tAppSpawnDate: time.Now(),\n\t\tApplication:  iris.New(),\n\t}\n\n\tfor _, cfg := range cfgs {\n\t\tcfg(b)\n\t}\n\n\treturn b\n}\n\n// SetupViews loads the templates.\nfunc (b *Bootstrapper) SetupViews(viewsDir string) {\n\tb.RegisterView(iris.HTML(viewsDir, \".html\").Layout(\"shared/layout.html\"))\n}\n\n// SetupSessions initializes the sessions, optionally.\nfunc (b *Bootstrapper) SetupSessions(expires time.Duration, cookieHashKey, cookieBlockKey []byte) {\n\tb.Sessions = sessions.New(sessions.Config{\n\t\tCookie:   \"SECRET_SESS_COOKIE_\" + b.AppName,\n\t\tExpires:  expires,\n\t\tEncoding: securecookie.New(cookieHashKey, cookieBlockKey),\n\t})\n}\n\n// SetupWebsockets prepares the websocket server.\nfunc (b *Bootstrapper) SetupWebsockets(endpoint string, handler websocket.ConnHandler) {\n\tws := websocket.New(websocket.DefaultGorillaUpgrader, handler)\n\n\tb.Get(endpoint, websocket.Handler(ws))\n}\n\n// SetupErrorHandlers prepares the http error handlers\n// `(context.StatusCodeNotSuccessful`,  which defaults to >=400 (but you can change it).\nfunc (b *Bootstrapper) SetupErrorHandlers() {\n\tb.OnAnyErrorCode(func(ctx iris.Context) {\n\t\terr := iris.Map{\n\t\t\t\"app\":     b.AppName,\n\t\t\t\"status\":  ctx.GetStatusCode(),\n\t\t\t\"message\": ctx.Values().GetString(\"message\"),\n\t\t}\n\n\t\tif jsonOutput := ctx.URLParamExists(\"json\"); jsonOutput {\n\t\t\tctx.JSON(err)\n\t\t\treturn\n\t\t}\n\n\t\tctx.ViewData(\"Err\", err)\n\t\tctx.ViewData(\"Title\", \"Error\")\n\t\tif err := ctx.View(\"shared/error.html\"); err != nil {\n\t\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\t\treturn\n\t\t}\n\t})\n}\n\nconst (\n\t// StaticAssets is the root directory for public assets like images, css, js.\n\tStaticAssets = \"./public/\"\n\t// Favicon is the relative 9to the \"StaticAssets\") favicon path for our app.\n\tFavicon = \"favicon.ico\"\n)\n\n// Configure accepts configurations and runs them inside the Bootstraper's context.\nfunc (b *Bootstrapper) Configure(cs ...Configurator) {\n\tfor _, c := range cs {\n\t\tc(b)\n\t}\n}\n\n// Bootstrap prepares our application.\n//\n// Returns itself.\nfunc (b *Bootstrapper) Bootstrap() *Bootstrapper {\n\tb.SetupViews(\"./views\")\n\tb.SetupSessions(24*time.Hour,\n\t\t[]byte(\"the-big-and-secret-fash-key-here\"),\n\t\t[]byte(\"lot-secret-of-characters-big-too\"),\n\t)\n\tb.SetupErrorHandlers()\n\n\t// static files\n\tb.Favicon(StaticAssets + Favicon)\n\tb.HandleDir(\"/public\", iris.Dir(StaticAssets))\n\n\t// middleware, after static files\n\tb.Use(recover.New())\n\tb.Use(logger.New())\n\n\treturn b\n}\n\n// Listen starts the http server with the specified \"addr\".\nfunc (b *Bootstrapper) Listen(addr string, cfgs ...iris.Configurator) {\n\tb.Run(iris.Addr(addr), cfgs...)\n}\n"
  },
  {
    "path": "_examples/bootstrapper/go.mod",
    "content": "module github.com/kataras/iris/v12/_examples/bootstrapper\n\ngo 1.25\n\nrequire (\n\tgithub.com/gorilla/securecookie v1.1.2\n\tgithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd\n)\n\nrequire (\n\tgithub.com/BurntSushi/toml v1.6.0 // indirect\n\tgithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect\n\tgithub.com/CloudyKit/jet/v6 v6.3.1 // indirect\n\tgithub.com/Joker/jade v1.1.3 // indirect\n\tgithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 // indirect\n\tgithub.com/ajg/form v1.5.1 // indirect\n\tgithub.com/andybalholm/brotli v1.2.0 // indirect\n\tgithub.com/aymerick/douceur v0.2.0 // indirect\n\tgithub.com/davecgh/go-spew v1.1.1 // indirect\n\tgithub.com/fatih/color v1.15.0 // indirect\n\tgithub.com/fatih/structs v1.1.0 // indirect\n\tgithub.com/flosch/pongo2/v4 v4.0.2 // indirect\n\tgithub.com/gobwas/glob v0.2.3 // indirect\n\tgithub.com/gobwas/httphead v0.1.0 // indirect\n\tgithub.com/gobwas/pool v0.2.1 // indirect\n\tgithub.com/gobwas/ws v1.4.0 // indirect\n\tgithub.com/golang/snappy v1.0.0 // indirect\n\tgithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a // indirect\n\tgithub.com/google/go-querystring v1.1.0 // indirect\n\tgithub.com/google/uuid v1.6.0 // indirect\n\tgithub.com/gorilla/css v1.0.1 // indirect\n\tgithub.com/gorilla/websocket v1.5.3 // indirect\n\tgithub.com/imkira/go-interpol v1.1.0 // indirect\n\tgithub.com/iris-contrib/httpexpect/v2 v2.15.2 // indirect\n\tgithub.com/iris-contrib/schema v0.0.6 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/kataras/blocks v0.0.8 // indirect\n\tgithub.com/kataras/golog v0.1.12 // indirect\n\tgithub.com/kataras/neffos v0.0.24 // indirect\n\tgithub.com/kataras/pio v0.0.13 // indirect\n\tgithub.com/kataras/sitemap v0.0.6 // indirect\n\tgithub.com/kataras/tunnel v0.0.4 // indirect\n\tgithub.com/klauspost/compress v1.18.2 // indirect\n\tgithub.com/kr/pretty v0.3.1 // indirect\n\tgithub.com/mailgun/raymond/v2 v2.0.48 // indirect\n\tgithub.com/mailru/easyjson v0.9.1 // indirect\n\tgithub.com/mattn/go-colorable v0.1.13 // indirect\n\tgithub.com/mattn/go-isatty v0.0.19 // indirect\n\tgithub.com/mediocregopher/radix/v3 v3.8.1 // indirect\n\tgithub.com/microcosm-cc/bluemonday v1.0.27 // indirect\n\tgithub.com/mitchellh/go-wordwrap v1.0.1 // indirect\n\tgithub.com/nats-io/nats.go v1.40.1 // indirect\n\tgithub.com/nats-io/nkeys v0.4.9 // indirect\n\tgithub.com/nats-io/nuid v1.0.1 // indirect\n\tgithub.com/pmezard/go-difflib v1.0.0 // indirect\n\tgithub.com/rogpeppe/go-internal v1.10.0 // indirect\n\tgithub.com/russross/blackfriday/v2 v2.1.0 // indirect\n\tgithub.com/sanity-io/litter v1.5.5 // indirect\n\tgithub.com/schollz/closestmatch v2.1.0+incompatible // indirect\n\tgithub.com/sergi/go-diff v1.0.0 // indirect\n\tgithub.com/sirupsen/logrus v1.8.1 // indirect\n\tgithub.com/stretchr/testify v1.11.1 // indirect\n\tgithub.com/tdewolff/minify/v2 v2.24.8 // indirect\n\tgithub.com/tdewolff/parse/v2 v2.8.5 // indirect\n\tgithub.com/valyala/bytebufferpool v1.0.0 // indirect\n\tgithub.com/vmihailenco/msgpack/v5 v5.4.1 // indirect\n\tgithub.com/vmihailenco/tagparser/v2 v2.0.0 // indirect\n\tgithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect\n\tgithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect\n\tgithub.com/xeipuuv/gojsonschema v1.2.0 // indirect\n\tgithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 // indirect\n\tgithub.com/yosssi/ace v0.0.5 // indirect\n\tgithub.com/yudai/gojsondiff v1.0.0 // indirect\n\tgithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect\n\tgolang.org/x/crypto v0.47.0 // indirect\n\tgolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 // indirect\n\tgolang.org/x/net v0.49.0 // indirect\n\tgolang.org/x/sys v0.40.0 // indirect\n\tgolang.org/x/text v0.33.0 // indirect\n\tgolang.org/x/time v0.14.0 // indirect\n\tgolang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect\n\tgoogle.golang.org/protobuf v1.36.11 // indirect\n\tgopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect\n\tgopkg.in/ini.v1 v1.67.0 // indirect\n\tgopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n\tmoul.io/http2curl/v2 v2.3.0 // indirect\n)\n"
  },
  {
    "path": "_examples/bootstrapper/go.sum",
    "content": "github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=\ngithub.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=\ngithub.com/CloudyKit/jet/v6 v6.3.1 h1:6IAo5Cx21xrHVaR8zzXN5gJatKV/wO7Nf6bfCnCSbUw=\ngithub.com/CloudyKit/jet/v6 v6.3.1/go.mod h1:lf8ksdNsxZt7/yH/3n4vJQWA9RUq4wpaHtArHhGVMOw=\ngithub.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc=\ngithub.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=\ngithub.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=\ngithub.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 h1:dG7/gLroJGht/jSQtHiLvT48Hxn+crbmvyItZC8cWOs=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05/go.mod h1:NYezi6wtnJtBm5btoprXc5SvAdqH0XTXWnUup0MptAI=\ngithub.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=\ngithub.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=\ngithub.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=\ngithub.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=\ngithub.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=\ngithub.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=\ngithub.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=\ngithub.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=\ngithub.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=\ngithub.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw=\ngithub.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8=\ngithub.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=\ngithub.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=\ngithub.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=\ngithub.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=\ngithub.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=\ngithub.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=\ngithub.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=\ngithub.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=\ngithub.com/gobwas/ws v1.4.0 h1:CTaoG1tojrh4ucGPcoJFiAQUAsEWekEWvLy7GsVNqGs=\ngithub.com/gobwas/ws v1.4.0/go.mod h1:G3gNqMNtPppf5XUz7O4shetPpcZ1VJ7zt18dlUeakrc=\ngithub.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=\ngithub.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a h1:l7A0loSszR5zHd/qK53ZIHMO8b3bBSmENnQ6eKnUT0A=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=\ngithub.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=\ngithub.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=\ngithub.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=\ngithub.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=\ngithub.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=\ngithub.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=\ngithub.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=\ngithub.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=\ngithub.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=\ngithub.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2 h1:T9THsdP1woyAqKHwjkEsbCnMefsAFvk8iJJKokcJ3Go=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2/go.mod h1:JLDgIqnFy5loDSUv1OA2j0mb6p/rDhiCqigP22Uq9xE=\ngithub.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw=\ngithub.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/kataras/blocks v0.0.8 h1:MrpVhoFTCR2v1iOOfGng5VJSILKeZZI+7NGfxEh3SUM=\ngithub.com/kataras/blocks v0.0.8/go.mod h1:9Jm5zx6BB+06NwA+OhTbHW1xkMOYxahnqTN5DveZ2Yg=\ngithub.com/kataras/golog v0.1.12 h1:Bu7I/G4ilJlbfzjmU39O9N+2uO1pBcMK045fzZ4ytNg=\ngithub.com/kataras/golog v0.1.12/go.mod h1:wrGSbOiBqbQSQznleVNX4epWM8rl9SJ/rmEacl0yqy4=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd h1:oMsQgqKACbUdlaIWnN+EZzzAnHkw90hE/y/R0BSij2U=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd/go.mod h1:yucjtDl9Vn3OctWM1fi0MuM9OckemC7kEreFa9QtPF8=\ngithub.com/kataras/neffos v0.0.24 h1:S3lHqJopCfXN285VdlbGeOj+Id83u4xdQKToa+w1vW0=\ngithub.com/kataras/neffos v0.0.24/go.mod h1:/3K9zQ0yEC5/xUiSQx46ToWa3xneGfUo/nMit/F5g+U=\ngithub.com/kataras/pio v0.0.13 h1:x0rXVX0fviDTXOOLOmr4MUxOabu1InVSTu5itF8CXCM=\ngithub.com/kataras/pio v0.0.13/go.mod h1:k3HNuSw+eJ8Pm2lA4lRhg3DiCjVgHlP8hmXApSej3oM=\ngithub.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY=\ngithub.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4=\ngithub.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA=\ngithub.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw=\ngithub.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=\ngithub.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=\ngithub.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=\ngithub.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8=\ngithub.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=\ngithub.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=\ngithub.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=\ngithub.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=\ngithub.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=\ngithub.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/mediocregopher/radix/v3 v3.8.1 h1:rOkHflVuulFKlwsLY01/M2cM2tWCjDoETcMqKbAWu1M=\ngithub.com/mediocregopher/radix/v3 v3.8.1/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8=\ngithub.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=\ngithub.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=\ngithub.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=\ngithub.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=\ngithub.com/nats-io/nats.go v1.40.1 h1:MLjDkdsbGUeCMKFyCFoLnNn/HDTqcgVa3EQm+pMNDPk=\ngithub.com/nats-io/nats.go v1.40.1/go.mod h1:wV73x0FSI/orHPSYoyMeJB+KajMDoWyXmFaRrrYaaTo=\ngithub.com/nats-io/nkeys v0.4.9 h1:qe9Faq2Gxwi6RZnZMXfmGMZkg3afLLOtrU+gDZJ35b0=\ngithub.com/nats-io/nkeys v0.4.9/go.mod h1:jcMqs+FLG+W5YO36OX6wFIFcmpdAns+w1Wm6D3I/evE=\ngithub.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=\ngithub.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY=\ngithub.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc=\ngithub.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=\ngithub.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=\ngithub.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=\ngithub.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M=\ngithub.com/pkg/diff v0.0.0-20200914180035-5b29258ca4f7/go.mod h1:zO8QMzTeZd5cpnIkz/Gn6iK0jDfGicM1nynOkkPIl28=\ngithub.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=\ngithub.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=\ngithub.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=\ngithub.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=\ngithub.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=\ngithub.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=\ngithub.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk=\ngithub.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=\ngithub.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=\ngithub.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=\ngithub.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=\ngithub.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502/go.mod h1:p9lPsd+cx33L3H9nNoecRRxPssFKUwwI50I3pZ0yT+8=\ngithub.com/tdewolff/minify/v2 v2.24.8 h1:58/VjsbevI4d5FGV0ZSuBrHMSSkH4MCH0sIz/eKIauE=\ngithub.com/tdewolff/minify/v2 v2.24.8/go.mod h1:0Ukj0CRpo/sW/nd8uZ4ccXaV1rEVIWA3dj8U7+Shhfw=\ngithub.com/tdewolff/parse/v2 v2.8.5 h1:ZmBiA/8Do5Rpk7bDye0jbbDUpXXbCdc3iah4VeUvwYU=\ngithub.com/tdewolff/parse/v2 v2.8.5/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=\ngithub.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE=\ngithub.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=\ngithub.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=\ngithub.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=\ngithub.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=\ngithub.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=\ngithub.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=\ngithub.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=\ngithub.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA=\ngithub.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0=\ngithub.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=\ngithub.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=\ngithub.com/yudai/pp v2.0.1+incompatible h1:Q4//iY4pNF6yPLZIigmvcl7k/bPgrcTPIFIcmawg5bI=\ngithub.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=\ngithub.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=\ngolang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=\ngolang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=\ngolang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=\ngolang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=\ngolang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=\ngolang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=\ngolang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=\ngolang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=\ngolang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20201211185031-d93e913c1a58/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY=\ngolang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=\ngoogle.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=\ngoogle.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=\ngopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nmoul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs=\nmoul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE=\n"
  },
  {
    "path": "_examples/bootstrapper/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12/_examples/bootstrapper/bootstrap\"\n\t\"github.com/kataras/iris/v12/_examples/bootstrapper/middleware/identity\"\n\t\"github.com/kataras/iris/v12/_examples/bootstrapper/routes\"\n)\n\nfunc newApp() *bootstrap.Bootstrapper {\n\tapp := bootstrap.New(\"Awesome App\", \"kataras2006@hotmail.com\")\n\tapp.Bootstrap()\n\tapp.Configure(identity.Configure, routes.Configure)\n\treturn app\n}\n\nfunc main() {\n\tapp := newApp()\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/bootstrapper/main_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\n// go test -v\nfunc TestApp(t *testing.T) {\n\tapp := newApp()\n\te := httptest.New(t, app.Application)\n\n\t// test our routes\n\te.GET(\"/\").Expect().Status(httptest.StatusOK)\n\te.GET(\"/follower/42\").Expect().Status(httptest.StatusOK).\n\t\tBody().IsEqual(\"from /follower/{id:int64} with ID: 42\")\n\te.GET(\"/following/52\").Expect().Status(httptest.StatusOK).\n\t\tBody().IsEqual(\"from /following/{id:int64} with ID: 52\")\n\te.GET(\"/like/64\").Expect().Status(httptest.StatusOK).\n\t\tBody().IsEqual(\"from /like/{id:int64} with ID: 64\")\n\n\t// test not found\n\te.GET(\"/notfound\").Expect().Status(httptest.StatusNotFound)\n\texpectedErr := map[string]any{\n\t\t\"app\":     app.AppName,\n\t\t\"status\":  httptest.StatusNotFound,\n\t\t\"message\": \"\",\n\t}\n\te.GET(\"/anotfoundwithjson\").WithQuery(\"json\", nil).\n\t\tExpect().Status(httptest.StatusNotFound).JSON().IsEqual(expectedErr)\n}\n"
  },
  {
    "path": "_examples/bootstrapper/middleware/identity/identity.go",
    "content": "package identity\n\nimport (\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n\n\t\"github.com/kataras/iris/v12/_examples/bootstrapper/bootstrap\"\n)\n\n// New returns a new handler which adds some headers and view data\n// describing the application, i.e the owner, the startup time.\nfunc New(b *bootstrap.Bootstrapper) iris.Handler {\n\treturn func(ctx iris.Context) {\n\t\t// response headers\n\t\tctx.Header(\"App-Name\", b.AppName)\n\t\tctx.Header(\"App-Owner\", b.AppOwner)\n\t\tctx.Header(\"App-Since\", time.Since(b.AppSpawnDate).String())\n\n\t\tctx.Header(\"Server\", \"Iris: https://iris-go.com\")\n\n\t\t// view data if ctx.View or c.Tmpl = \"$page.html\" will be called next.\n\t\tctx.ViewData(\"AppName\", b.AppName)\n\t\tctx.ViewData(\"AppOwner\", b.AppOwner)\n\t\tctx.Next()\n\t}\n}\n\n// Configure creates a new identity middleware and registers that to the app.\nfunc Configure(b *bootstrap.Bootstrapper) {\n\th := New(b)\n\tb.UseGlobal(h)\n}\n"
  },
  {
    "path": "_examples/bootstrapper/routes/follower.go",
    "content": "package routes\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n)\n\n// GetFollowerHandler handles the GET: /follower/{id}\nfunc GetFollowerHandler(ctx iris.Context) {\n\tid, _ := ctx.Params().GetInt64(\"id\")\n\tctx.Writef(\"from \"+ctx.GetCurrentRoute().Path()+\" with ID: %d\", id)\n}\n"
  },
  {
    "path": "_examples/bootstrapper/routes/following.go",
    "content": "package routes\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n)\n\n// GetFollowingHandler handles the GET: /following/{id}\nfunc GetFollowingHandler(ctx iris.Context) {\n\tid, _ := ctx.Params().GetInt64(\"id\")\n\tctx.Writef(\"from \"+ctx.GetCurrentRoute().Path()+\" with ID: %d\", id)\n}\n"
  },
  {
    "path": "_examples/bootstrapper/routes/index.go",
    "content": "package routes\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\n// GetIndexHandler handles the GET: /\nfunc GetIndexHandler(ctx iris.Context) {\n\tctx.ViewData(\"Title\", \"Index Page\")\n\tif err := ctx.View(\"index.html\"); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "_examples/bootstrapper/routes/like.go",
    "content": "package routes\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n)\n\n// GetLikeHandler handles the GET: /like/{id}\nfunc GetLikeHandler(ctx iris.Context) {\n\tid, _ := ctx.Params().GetInt64(\"id\")\n\tctx.Writef(\"from \"+ctx.GetCurrentRoute().Path()+\" with ID: %d\", id)\n}\n"
  },
  {
    "path": "_examples/bootstrapper/routes/routes.go",
    "content": "package routes\n\nimport (\n\t\"github.com/kataras/iris/v12/_examples/bootstrapper/bootstrap\"\n)\n\n// Configure registers the necessary routes to the app.\nfunc Configure(b *bootstrap.Bootstrapper) {\n\tb.Get(\"/\", GetIndexHandler)\n\tb.Get(\"/follower/{id:int64}\", GetFollowerHandler)\n\tb.Get(\"/following/{id:int64}\", GetFollowingHandler)\n\tb.Get(\"/like/{id:int64}\", GetLikeHandler)\n}\n"
  },
  {
    "path": "_examples/bootstrapper/views/index.html",
    "content": "<h1>Welcome!!</h1>"
  },
  {
    "path": "_examples/bootstrapper/views/shared/error.html",
    "content": "﻿<h1 class=\"text-danger\">Error.</h1>\n<h2 class=\"text-danger\">An error occurred while processing your request.</h2>\n\n<h3>{{.Err.status}}</h3>\n<h4>{{.Err.message}}</h4>"
  },
  {
    "path": "_examples/bootstrapper/views/shared/layout.html",
    "content": "﻿<!DOCTYPE html>\n<html>\n\n<head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <link rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/favicon.ico\" />\n    <title>{{.Title}} - {{.AppName}}</title>\n\n</head>\n\n<body>\n    <div>\n        <!-- Render the current template here -->\n        {{ yield . }}\n        <hr />\n        <footer>\n            <p>&copy; 2017 - {{.AppOwner}}</p>\n        </footer>\n    </div>\n</body>\n\n</html>"
  },
  {
    "path": "_examples/caddy/Caddyfile",
    "content": "example.com {\n\theader / Server \"Iris\"\n\tproxy / example.com:9091 # localhost:9091\n}\n\napi.example.com {\n\theader / Server \"Iris\"\n\tproxy / api.example.com:9092 # localhost:9092\n}"
  },
  {
    "path": "_examples/caddy/README.md",
    "content": "# Caddy loves Iris\n\nThe `Caddyfile` shows how you can use caddy to listen on ports 80 & 443 and sit in front of iris webserver(s) that serving on a different port (9091 and 9092 in this case; see Caddyfile).\n\n## Running our two web servers\n\n1. Go to `$GOPATH/src/github.com/kataras/iris/_examples/caddy/server1`\n2. Open a terminal window and execute `go run main.go`\n3. Go to `$GOPATH/src/github.com/kataras/iris/_examples/caddy/server2`\n4. Open a new terminal window and execute `go run main.go`\n\n## Caddy installation\n\n1. Download caddy: https://caddyserver.com/download\n2. Extract its contents where the `Caddyfile` is located, the `$GOPATH/src/github.com/kataras/iris/_examples/caddy` in this case\n3. Open, read and modify the `Caddyfile` to see by yourself how easy it is to configure the servers\n4. Run `caddy` directly or open a terminal window and execute `caddy`\n5. Go to `https://example.com` and `https://api.example.com/user/42`\n\n\n## Notes\n\nIris has the `app.Run(iris.AutoTLS(\":443\", \"example.com\", \"mail@example.com\"))` which does\nthe exactly same thing but caddy is a great tool that helps you when you run multiple web servers from one host machine, i.e iris, apache, tomcat."
  },
  {
    "path": "_examples/caddy/server1/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/mvc\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\ttemplates := iris.HTML(\"./views\", \".html\").Layout(\"shared/layout.html\")\n\tapp.RegisterView(templates)\n\n\tmvc.New(app).Handle(new(Controller))\n\n\t// http://localhost:9091\n\tapp.Listen(\":9091\")\n}\n\n// Layout contains all the binding properties for the shared/layout.html\ntype Layout struct {\n\tTitle string\n}\n\n// Controller is our example controller, request-scoped, each request has its own instance.\ntype Controller struct {\n\tLayout Layout\n}\n\n// BeginRequest is the first method fired when client requests from this Controller's root path.\nfunc (c *Controller) BeginRequest(ctx iris.Context) {\n\tc.Layout.Title = \"Home Page\"\n}\n\n// EndRequest is the last method fired.\n// It's here just to complete the BaseController\n// in order to be tell iris to call the `BeginRequest` before the main method.\nfunc (c *Controller) EndRequest(ctx iris.Context) {}\n\n// Get handles GET http://localhost:9091\nfunc (c *Controller) Get() mvc.View {\n\treturn mvc.View{\n\t\tName: \"index.html\",\n\t\tData: iris.Map{\n\t\t\t\"Layout\":  c.Layout,\n\t\t\t\"Message\": \"Welcome to my website!\",\n\t\t},\n\t}\n}\n"
  },
  {
    "path": "_examples/caddy/server1/views/index.html",
    "content": "<div>\n    {{.Message}}\n</div>"
  },
  {
    "path": "_examples/caddy/server1/views/shared/layout.html",
    "content": "<html>\n\n<head>\n    <title>{{.Layout.Title}}</title>\n</head>\n\n<body>\n    {{ yield . }}\n</body>\n\n</html>"
  },
  {
    "path": "_examples/caddy/server2/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/mvc\"\n)\n\ntype postValue func(string) string\n\nfunc main() {\n\tapp := iris.New()\n\n\tmvc.New(app.Party(\"/user\")).Register(\n\t\tfunc(ctx iris.Context) postValue {\n\t\t\treturn ctx.PostValue\n\t\t}).Handle(new(UserController))\n\n\t// GET http://localhost:9092/user\n\t// GET http://localhost:9092/user/42\n\t// POST http://localhost:9092/user\n\t// PUT http://localhost:9092/user/42\n\t// DELETE http://localhost:9092/user/42\n\t// GET http://localhost:9092/user/followers/42\n\tapp.Listen(\":9092\")\n}\n\n// UserController is our user example controller.\ntype UserController struct{}\n\n// Get handles GET /user\nfunc (c *UserController) Get() string {\n\treturn \"Select all users\"\n}\n\n// User is our test User model, nothing tremendous here.\ntype User struct{ ID int64 }\n\n// GetBy handles GET /user/42, equal to .Get(\"/user/{id:int64}\")\nfunc (c *UserController) GetBy(id int64) User {\n\t// Select User by ID == $id.\n\treturn User{id}\n}\n\n// Post handles POST /user\nfunc (c *UserController) Post(post postValue) string {\n\tusername := post(\"username\")\n\treturn \"Create by user with username: \" + username\n}\n\n// PutBy handles PUT /user/42\nfunc (c *UserController) PutBy(id int) string {\n\t// Update user by ID == $id\n\treturn \"User updated\"\n}\n\n// DeleteBy handles DELETE /user/42\nfunc (c *UserController) DeleteBy(id int) bool {\n\t// Delete user by ID == %id\n\t//\n\t// when boolean then true = iris.StatusOK, false = iris.StatusNotFound\n\treturn true\n}\n\n// GetFollowersBy handles GET /user/followers/42\nfunc (c *UserController) GetFollowersBy(id int) []User {\n\t// Select all followers by user ID == $id\n\treturn []User{ /* ... */ }\n}\n"
  },
  {
    "path": "_examples/compression/client/main.go",
    "content": "package main\n\nimport (\n\t\"bytes\"\n\t\"compress/gzip\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nvar client = http.DefaultClient\n\nconst baseURL = \"http://localhost:8080\"\n\nfunc main() {\n\tfmt.Printf(\"Running client example on: %s\\n\", baseURL)\n\n\tgetExample()\n\tpostExample()\n}\n\nfunc getExample() {\n\tendpoint := baseURL + \"/\"\n\treq, err := http.NewRequest(http.MethodGet, endpoint, nil)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\t// Required to receive server's compressed data.\n\treq.Header.Set(\"Accept-Encoding\", \"gzip\")\n\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer resp.Body.Close()\n\n\t// decompress server's compressed reply.\n\tr, err := gzip.NewReader(resp.Body)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer r.Close()\n\n\tbody, err := io.ReadAll(r)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Printf(\"Received from server: %s\", string(body))\n}\n\ntype payload struct {\n\tUsername string `json:\"username\"`\n}\n\nfunc postExample() {\n\tbuf := new(bytes.Buffer)\n\n\t// Compress client's data.\n\tw := gzip.NewWriter(buf)\n\n\tb, err := json.Marshal(payload{Username: \"Edward\"})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tw.Write(b)\n\tw.Close()\n\n\tendpoint := baseURL + \"/\"\n\n\treq, err := http.NewRequest(http.MethodPost, endpoint, buf)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\t// Required to send gzip compressed data to the server.\n\treq.Header.Set(\"Content-Encoding\", \"gzip\")\n\t// Required to receive server's compressed data.\n\treq.Header.Set(\"Accept-Encoding\", \"gzip\")\n\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer resp.Body.Close()\n\n\t// Decompress server's compressed reply.\n\tr, err := gzip.NewReader(resp.Body)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer r.Close()\n\n\tbody, err := io.ReadAll(r)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Printf(\"Server replied with: %s\", string(body))\n}\n"
  },
  {
    "path": "_examples/compression/client-using-iris/main.go",
    "content": "package main\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\n\t\"github.com/kataras/iris/v12/context\"\n)\n\nconst baseURL = \"http://localhost:8080\"\n\n// Available options:\n// - \"gzip\",\n// - \"deflate\",\n// - \"br\" (for brotli),\n// - \"snappy\" and\n// - \"s2\"\nconst encoding = context.BROTLI\n\nvar client = http.DefaultClient\n\nfunc main() {\n\tfmt.Printf(\"Running client example on: %s\\n\", baseURL)\n\n\tgetExample()\n\tpostExample()\n}\n\nfunc getExample() {\n\tendpoint := baseURL + \"/\"\n\treq, err := http.NewRequest(http.MethodGet, endpoint, nil)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\t// Required to receive server's compressed data.\n\treq.Header.Set(\"Accept-Encoding\", encoding)\n\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer resp.Body.Close()\n\n\t// decompress server's compressed reply.\n\tcr, err := context.NewCompressReader(resp.Body, encoding)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer cr.Close()\n\n\tbody, err := io.ReadAll(cr)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Printf(\"Received from server: %s\", string(body))\n}\n\ntype payload struct {\n\tUsername string `json:\"username\"`\n}\n\nfunc postExample() {\n\tbuf := new(bytes.Buffer)\n\n\t// Compress client's data.\n\tcw, err := context.NewCompressWriter(buf, encoding, -1)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tjson.NewEncoder(cw).Encode(payload{Username: \"Edward\"})\n\n\t// `Close` or `Flush` required before `NewRequest` call.\n\tcw.Close()\n\n\tendpoint := baseURL + \"/\"\n\n\treq, err := http.NewRequest(http.MethodPost, endpoint, buf)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\t// Required to send gzip compressed data to the server.\n\treq.Header.Set(\"Content-Encoding\", encoding)\n\t// Required to receive server's compressed data.\n\treq.Header.Set(\"Accept-Encoding\", encoding)\n\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer resp.Body.Close()\n\n\t// Decompress server's compressed reply.\n\tcr, err := context.NewCompressReader(resp.Body, encoding)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer cr.Close()\n\n\tbody, err := io.ReadAll(cr)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Printf(\"Server replied with: %s\", string(body))\n}\n"
  },
  {
    "path": "_examples/compression/main.go",
    "content": "package main\n\nimport \"github.com/kataras/iris/v12\"\n\nfunc main() {\n\tapp := newApp()\n\tapp.Logger().SetLevel(\"debug\")\n\tapp.Listen(\":8080\")\n}\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\t// HERE and you are ready to GO:\n\tapp.Use(iris.Compression)\n\n\tapp.Get(\"/\", send)\n\tapp.Post(\"/\", receive)\n\n\treturn app\n}\n\ntype payload struct {\n\tUsername string `json:\"username\"`\n}\n\nfunc send(ctx iris.Context) {\n\tctx.JSON(payload{\n\t\tUsername: \"Makis\",\n\t})\n}\n\nfunc receive(ctx iris.Context) {\n\tvar p payload\n\tif err := ctx.ReadJSON(&p); err != nil {\n\t\tctx.Application().Logger().Debugf(\"ReadJSON: %v\", err)\n\t}\n\n\tctx.WriteString(p.Username)\n}\n\n/* Manually:\nfunc enableCompression(ctx iris.Context) {\n\t// Enable writing using compression (deflate, gzip, brotli, snappy, s2):\n\terr := ctx.CompressWriter(true)\n\tif err != nil {\n\t\tctx.Application().Logger().Debugf(\"writer: %v\", err)\n\t\t// if you REQUIRE server to SEND compressed data then `return` here.\n\t\t// return\n\t}\n\n\t// Enable reading and binding request's compressed data:\n\terr = ctx.CompressReader(true)\n\tif err != nil &&\n\t\t// on GET we don't expect writing with gzip from client\n\t\tctx.Method() != iris.MethodGet  {\n\t\tctx.Application().Logger().Debugf(\"reader: %v\", err)\n\t\t// if you REQUIRE server to RECEIVE only\n\t\t// compressed data then `return` here.\n\t\t// return\n\t}\n\n\tctx.Next()\n}\n*/\n"
  },
  {
    "path": "_examples/compression/main_test.go",
    "content": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nfunc TestCompression(t *testing.T) {\n\tapp := newApp()\n\te := httptest.New(t, app)\n\n\tvar expectedReply = payload{Username: \"Makis\"}\n\ttestBody(t, e.GET(\"/\"), expectedReply)\n}\n\nfunc TestCompressionAfterRecorder(t *testing.T) {\n\tvar expectedReply = payload{Username: \"Makis\"}\n\n\tapp := iris.New()\n\tapp.Use(func(ctx iris.Context) {\n\t\tctx.Record()\n\t\tctx.Next()\n\t})\n\tapp.Use(iris.Compression)\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.JSON(expectedReply)\n\t})\n\n\te := httptest.New(t, app)\n\ttestBody(t, e.GET(\"/\"), expectedReply)\n}\n\nfunc TestCompressionBeforeRecorder(t *testing.T) {\n\tvar expectedReply = payload{Username: \"Makis\"}\n\n\tapp := iris.New()\n\tapp.Use(iris.Compression)\n\tapp.Use(func(ctx iris.Context) {\n\t\tctx.Record()\n\t\tctx.Next()\n\t})\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.JSON(expectedReply)\n\t})\n\n\te := httptest.New(t, app)\n\ttestBody(t, e.GET(\"/\"), expectedReply)\n}\n\nfunc testBody(t *testing.T, req *httptest.Request, expectedReply any) {\n\tt.Helper()\n\n\tbody := req.WithHeader(context.AcceptEncodingHeaderKey, context.GZIP).Expect().\n\t\tStatus(httptest.StatusOK).\n\t\tContentEncoding(context.GZIP).\n\t\tContentType(context.ContentJSONHeaderValue).Body().Raw()\n\n\t// Note that .Expect() consumes the response body\n\t// and stores it to unexported \"contents\" field\n\t// therefore, we retrieve it as string and put it to a new buffer.\n\tr := strings.NewReader(body)\n\tcr, err := context.NewCompressReader(r, context.GZIP)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer cr.Close()\n\n\tvar got payload\n\tif err = json.NewDecoder(cr).Decode(&got); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif !reflect.DeepEqual(expectedReply, got) {\n\t\tt.Fatalf(\"expected %#+v but got %#+v\", expectedReply, got)\n\t}\n}\n"
  },
  {
    "path": "_examples/configuration/from-configuration-structure/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.HTML(\"<b>Hello!</b>\")\n\t})\n\t// [...]\n\n\t// Good when you want to modify the whole configuration.\n\tapp.Listen(\":8080\", iris.WithConfiguration(iris.Configuration{ // default configuration:\n\t\tDisableStartupLog:                 false,\n\t\tDisableInterruptHandler:           false,\n\t\tDisablePathCorrection:             false,\n\t\tEnablePathEscape:                  false,\n\t\tFireMethodNotAllowed:              false,\n\t\tDisableBodyConsumptionOnUnmarshal: false,\n\t\tDisableAutoFireStatusCode:         false,\n\t\tTimeFormat:                        \"Mon, 02 Jan 2006 15:04:05 GMT\",\n\t\tCharset:                           \"utf-8\",\n\t}))\n\n\t// or before Run:\n\t// app.Configure(iris.WithConfiguration(iris.Configuration{...}))\n}\n"
  },
  {
    "path": "_examples/configuration/from-toml-file/configs/iris.tml",
    "content": "DisablePathCorrection = false\r\nEnablePathEscape = false\r\nFireMethodNotAllowed = true\r\nDisableBodyConsumptionOnUnmarshal = false\r\nTimeFormat = \"Mon, 01 Jan 2006 15:04:05 GMT\"\r\nCharset = \"utf-8\"\r\nRemoteAddrHeaders = [\"X-Real-Ip\", \"X-Forwarded-For\", \"CF-Connecting-IP\"]\r\n[Other]\r\n\tMyServerName = \"iris\"\r\n"
  },
  {
    "path": "_examples/configuration/from-toml-file/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.HTML(\"<b>Hello!</b>\")\n\t})\n\t// [...]\n\n\t// Good when you have two configurations, one for development and a different one for production use.\n\tapp.Listen(\":8080\", iris.WithConfiguration(iris.TOML(\"./configs/iris.tml\")))\n\n\t// or before run:\n\t// app.Configure(iris.WithConfiguration(iris.TOML(\"./configs/iris.tml\")))\n\t// app.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/configuration/from-yaml-file/configs/iris.yml",
    "content": "DisablePathCorrection: false\r\nEnablePathEscape: false\r\nFireMethodNotAllowed: true\r\nDisableBodyConsumptionOnUnmarshal: true\r\nTimeFormat: Mon, 01 Jan 2006 15:04:05 GMT\r\nCharset: UTF-8\r\nSSLProxyHeaders:\r\n  X-Forwarded-Proto: https\r\nHostProxyHeaders:\r\n  X-Host: true\r\nRemoteAddrHeaders:\r\n  - X-Real-Ip\r\n  - X-Forwarded-For\r\n  - CF-Connecting-IP\r\nOther:\r\n  Addr: :8080\r\n"
  },
  {
    "path": "_examples/configuration/from-yaml-file/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.HTML(\"<b>Hello!</b>\")\n\t})\n\t// [...]\n\n\t// Good when you have two configurations, one for development and a different one for production use.\n\t// If iris.YAML's input string argument is \"~\" then it loads the configuration from the home directory\n\t// and can be shared between many iris instances.\n\tcfg := iris.YAML(\"./configs/iris.yml\")\n\taddr := cfg.Other[\"Addr\"].(string)\n\tapp.Listen(addr, iris.WithConfiguration(cfg))\n\n\t// or before run:\n\t// app.Configure(iris.WithConfiguration(iris.YAML(\"./configs/iris.yml\")))\n\t// app.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/configuration/from-yaml-file/shared-configuration/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.HTML(\"<b>Hello!</b>\")\n\t})\n\t// [...]\n\n\t// Good when you share configuration between multiple iris instances.\n\t// This configuration file lives in your $HOME/iris.yml for unix hosts\n\t// or %HOMEDRIVE%+%HOMEPATH%/iris.yml for windows hosts, and you can modify it.\n\tapp.Listen(\":8080\", iris.WithGlobalConfiguration)\n\t// or before run:\n\t// app.Configure(iris.WithGlobalConfiguration)\n\t// app.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/configuration/functional/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.HTML(\"<b>Hello!</b>\")\n\t})\n\t// [...]\n\n\t// Good when you want to change some of the configuration's field.\n\t// Prefix: \"With\", code editors will help you navigate through all\n\t// configuration options without even a glitch to the documentation.\n\n\tapp.Listen(\":8080\", iris.WithoutStartupLog, iris.WithCharset(\"utf-8\"))\n\n\t// or before run:\n\t// app.Configure(iris.WithoutStartupLog, iris.WithCharset(\"utf-8\"))\n\t// app.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/configuration/multi-environments/README.md",
    "content": "# Environment-based Configuration\n\n## Run production server\n\n```sh\n$ go run main.go --config=server.yml\n```\n\n## Run development server\n\n```sh\n$ go run main.go --config=server.dev.yml\n```\n"
  },
  {
    "path": "_examples/configuration/multi-environments/api/configuration.go",
    "content": "package api\n\nimport (\n\t\"os\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"gopkg.in/yaml.v3\"\n)\n\ntype Configuration struct {\n\tHost              string `yaml:\"Host\"`\n\tPort              int    `yaml:\"Port\"`\n\tEnableCompression bool   `yaml:\"EnableCompression\"`\n\tAllowOrigin       string `yaml:\"AllowOrigin\"`\n\t// Iris specific configuration.\n\tIris iris.Configuration `yaml:\"Iris\"`\n}\n\n// BindFile binds the yaml file's contents to this Configuration.\nfunc (c *Configuration) BindFile(filename string) error {\n\tcontents, err := os.ReadFile(filename)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn yaml.Unmarshal(contents, c)\n}\n"
  },
  {
    "path": "_examples/configuration/multi-environments/api/server.go",
    "content": "package api\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/middleware/cors\"\n\t\"github.com/kataras/iris/v12/x/errors\"\n)\n\ntype Server struct {\n\t*iris.Application\n\n\tconfig  Configuration\n\tclosers []func() // See \"AddCloser\" method.\n}\n\n// NewServer returns a new server instance.\n// See its \"Start\" method.\nfunc NewServer(congig Configuration) *Server {\n\tapp := iris.New()\n\tapp.Configure(iris.WithConfiguration(congig.Iris))\n\n\ts := &Server{\n\t\tconfig:      congig,\n\t\tApplication: app,\n\t}\n\n\treturn s\n}\n\nfunc (s *Server) Build() error {\n\tif err := s.Application.Build(); err != nil {\n\t\treturn err\n\t}\n\n\t// Register your 3rd-party drivers.\n\t// if err := s.registerDatabase(); err != nil {\n\t// \treturn err\n\t// }s\n\n\treturn s.configureRouter()\n}\n\nfunc (s *Server) configureRouter() error {\n\ts.SetContextErrorHandler(errors.DefaultContextErrorHandler)\n\ts.Macros().SetErrorHandler(errors.DefaultPathParameterTypeErrorHandler)\n\n\ts.registerMiddlewares()\n\ts.registerRoutes()\n\n\treturn nil\n}\n\nfunc (s *Server) registerMiddlewares() {\n\ts.UseRouter(cors.New().AllowOrigin(s.config.AllowOrigin).Handler())\n\n\ts.UseRouter(func(ctx iris.Context) {\n\t\tctx.Header(\"Server\", \"Iris\")\n\t\tctx.Header(\"X-Developer\", \"kataras\")\n\t\tctx.Next()\n\t})\n\n\tif s.config.EnableCompression {\n\t\ts.Use(iris.Compression) // .Use instead of .UseRouter to let it run only on registered routes and not on 404 errors and e.t.c.\n\t}\n}\n\nfunc (s *Server) registerRoutes() {\n\t// register your routes...\n\n\ts.Get(\"/health\", func(ctx iris.Context) {\n\t\tctx.WriteString(\"OK\")\n\t})\n\n}\n\n// AddCloser adds one or more function that should be called on\n// manual server shutdown or OS interrupt signals.\nfunc (s *Server) AddCloser(closers ...func()) {\n\tfor _, closer := range closers {\n\t\tif closer == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Terminate any opened connections on OS interrupt signals.\n\t\tiris.RegisterOnInterrupt(closer)\n\t}\n\n\ts.closers = append(s.closers, closers...)\n}\n\n// Shutdown gracefully terminates the HTTP server and calls the closers afterwards.\nfunc (s *Server) Shutdown() error {\n\tctx, cancelCtx := context.WithTimeout(context.Background(), 5*time.Second)\n\terr := s.Application.Shutdown(ctx)\n\tcancelCtx()\n\n\tfor _, closer := range s.closers {\n\t\tif closer == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tcloser()\n\t}\n\n\treturn err\n}\n\n// Start starts the http server based on the config's host and port.\nfunc (s *Server) Listen() error {\n\tif err := s.Build(); err != nil {\n\t\treturn err\n\t}\n\n\taddr := fmt.Sprintf(\"%s:%d\", s.config.Host, s.config.Port)\n\treturn s.Application.Listen(addr)\n}\n"
  },
  {
    "path": "_examples/configuration/multi-environments/cmd/root_command.go",
    "content": "package cmd\n\nimport (\n\t\"github.com/kataras/my-iris-app/api\"\n\n\t\"github.com/spf13/cobra\"\n)\n\nconst defaultConfigFilename = \"server.dev.yml\"\n\nvar (\n\tserverConfig api.Configuration\n)\n\n// New returns a new root command.\n// Usage:\n// $ my-iris-app --config=server.yml\nfunc New() *cobra.Command {\n\tconfigFile := defaultConfigFilename\n\n\trootCmd := &cobra.Command{\n\t\tUse:                        \"my-iris-app\",\n\t\tShort:                      \"My Command Line App\",\n\t\tLong:                       \"The root command will start the HTTP server.\",\n\t\tVersion:                    \"v0.0.1\",\n\t\tSilenceErrors:              true,\n\t\tSilenceUsage:               true,\n\t\tTraverseChildren:           true,\n\t\tSuggestionsMinimumDistance: 1,\n\t\tPersistentPreRunE: func(cmd *cobra.Command, args []string) error {\n\t\t\treturn serverConfig.BindFile(configFile)\n\t\t},\n\t\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\t\treturn startServer()\n\t\t},\n\t}\n\n\t// Shared flags.\n\tflags := rootCmd.PersistentFlags()\n\tflags.StringVar(&configFile, \"config\", configFile, \"--config=server.yml a filepath which contains the YAML config format\")\n\n\t// Subcommands here.\n\n\treturn rootCmd\n}\n\nfunc startServer() error {\n\tsrv := api.NewServer(serverConfig)\n\treturn srv.Listen()\n}\n"
  },
  {
    "path": "_examples/configuration/multi-environments/go.mod",
    "content": "module github.com/kataras/my-iris-app\n\ngo 1.25\n\nrequire (\n\tgithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd\n\tgithub.com/spf13/cobra v1.10.2\n\tgopkg.in/yaml.v3 v3.0.1\n)\n\nrequire (\n\tgithub.com/BurntSushi/toml v1.6.0 // indirect\n\tgithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect\n\tgithub.com/CloudyKit/jet/v6 v6.3.1 // indirect\n\tgithub.com/Joker/jade v1.1.3 // indirect\n\tgithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 // indirect\n\tgithub.com/andybalholm/brotli v1.2.0 // indirect\n\tgithub.com/aymerick/douceur v0.2.0 // indirect\n\tgithub.com/fatih/structs v1.1.0 // indirect\n\tgithub.com/flosch/pongo2/v4 v4.0.2 // indirect\n\tgithub.com/golang/snappy v1.0.0 // indirect\n\tgithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a // indirect\n\tgithub.com/google/uuid v1.6.0 // indirect\n\tgithub.com/gorilla/css v1.0.1 // indirect\n\tgithub.com/inconshreveable/mousetrap v1.1.0 // indirect\n\tgithub.com/iris-contrib/schema v0.0.6 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/kataras/blocks v0.0.8 // indirect\n\tgithub.com/kataras/golog v0.1.12 // indirect\n\tgithub.com/kataras/pio v0.0.13 // indirect\n\tgithub.com/kataras/sitemap v0.0.6 // indirect\n\tgithub.com/kataras/tunnel v0.0.4 // indirect\n\tgithub.com/klauspost/compress v1.18.2 // indirect\n\tgithub.com/kr/pretty v0.3.1 // indirect\n\tgithub.com/mailgun/raymond/v2 v2.0.48 // indirect\n\tgithub.com/mailru/easyjson v0.9.1 // indirect\n\tgithub.com/microcosm-cc/bluemonday v1.0.27 // indirect\n\tgithub.com/rogpeppe/go-internal v1.10.0 // indirect\n\tgithub.com/russross/blackfriday/v2 v2.1.0 // indirect\n\tgithub.com/schollz/closestmatch v2.1.0+incompatible // indirect\n\tgithub.com/sirupsen/logrus v1.8.1 // indirect\n\tgithub.com/spf13/pflag v1.0.9 // indirect\n\tgithub.com/stretchr/testify v1.11.1 // indirect\n\tgithub.com/tdewolff/minify/v2 v2.24.8 // indirect\n\tgithub.com/tdewolff/parse/v2 v2.8.5 // indirect\n\tgithub.com/valyala/bytebufferpool v1.0.0 // indirect\n\tgithub.com/vmihailenco/msgpack/v5 v5.4.1 // indirect\n\tgithub.com/vmihailenco/tagparser/v2 v2.0.0 // indirect\n\tgithub.com/yosssi/ace v0.0.5 // indirect\n\tgolang.org/x/crypto v0.47.0 // indirect\n\tgolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 // indirect\n\tgolang.org/x/net v0.49.0 // indirect\n\tgolang.org/x/sys v0.40.0 // indirect\n\tgolang.org/x/text v0.33.0 // indirect\n\tgolang.org/x/time v0.14.0 // indirect\n\tgoogle.golang.org/protobuf v1.36.11 // indirect\n\tgopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect\n\tgopkg.in/ini.v1 v1.67.0 // indirect\n)\n"
  },
  {
    "path": "_examples/configuration/multi-environments/go.sum",
    "content": "github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=\ngithub.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=\ngithub.com/CloudyKit/jet/v6 v6.3.1 h1:6IAo5Cx21xrHVaR8zzXN5gJatKV/wO7Nf6bfCnCSbUw=\ngithub.com/CloudyKit/jet/v6 v6.3.1/go.mod h1:lf8ksdNsxZt7/yH/3n4vJQWA9RUq4wpaHtArHhGVMOw=\ngithub.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc=\ngithub.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=\ngithub.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=\ngithub.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 h1:dG7/gLroJGht/jSQtHiLvT48Hxn+crbmvyItZC8cWOs=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05/go.mod h1:NYezi6wtnJtBm5btoprXc5SvAdqH0XTXWnUup0MptAI=\ngithub.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=\ngithub.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=\ngithub.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=\ngithub.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=\ngithub.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=\ngithub.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=\ngithub.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=\ngithub.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=\ngithub.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=\ngithub.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw=\ngithub.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8=\ngithub.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=\ngithub.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=\ngithub.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=\ngithub.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a h1:l7A0loSszR5zHd/qK53ZIHMO8b3bBSmENnQ6eKnUT0A=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=\ngithub.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=\ngithub.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=\ngithub.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=\ngithub.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=\ngithub.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=\ngithub.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=\ngithub.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2 h1:T9THsdP1woyAqKHwjkEsbCnMefsAFvk8iJJKokcJ3Go=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2/go.mod h1:JLDgIqnFy5loDSUv1OA2j0mb6p/rDhiCqigP22Uq9xE=\ngithub.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw=\ngithub.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/kataras/blocks v0.0.8 h1:MrpVhoFTCR2v1iOOfGng5VJSILKeZZI+7NGfxEh3SUM=\ngithub.com/kataras/blocks v0.0.8/go.mod h1:9Jm5zx6BB+06NwA+OhTbHW1xkMOYxahnqTN5DveZ2Yg=\ngithub.com/kataras/golog v0.1.12 h1:Bu7I/G4ilJlbfzjmU39O9N+2uO1pBcMK045fzZ4ytNg=\ngithub.com/kataras/golog v0.1.12/go.mod h1:wrGSbOiBqbQSQznleVNX4epWM8rl9SJ/rmEacl0yqy4=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd h1:oMsQgqKACbUdlaIWnN+EZzzAnHkw90hE/y/R0BSij2U=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd/go.mod h1:yucjtDl9Vn3OctWM1fi0MuM9OckemC7kEreFa9QtPF8=\ngithub.com/kataras/pio v0.0.13 h1:x0rXVX0fviDTXOOLOmr4MUxOabu1InVSTu5itF8CXCM=\ngithub.com/kataras/pio v0.0.13/go.mod h1:k3HNuSw+eJ8Pm2lA4lRhg3DiCjVgHlP8hmXApSej3oM=\ngithub.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY=\ngithub.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4=\ngithub.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA=\ngithub.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw=\ngithub.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=\ngithub.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=\ngithub.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=\ngithub.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8=\ngithub.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=\ngithub.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=\ngithub.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=\ngithub.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=\ngithub.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=\ngithub.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=\ngithub.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=\ngithub.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=\ngithub.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=\ngithub.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=\ngithub.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=\ngithub.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=\ngithub.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk=\ngithub.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=\ngithub.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=\ngithub.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=\ngithub.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=\ngithub.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=\ngithub.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=\ngithub.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=\ngithub.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/tdewolff/minify/v2 v2.24.8 h1:58/VjsbevI4d5FGV0ZSuBrHMSSkH4MCH0sIz/eKIauE=\ngithub.com/tdewolff/minify/v2 v2.24.8/go.mod h1:0Ukj0CRpo/sW/nd8uZ4ccXaV1rEVIWA3dj8U7+Shhfw=\ngithub.com/tdewolff/parse/v2 v2.8.5 h1:ZmBiA/8Do5Rpk7bDye0jbbDUpXXbCdc3iah4VeUvwYU=\ngithub.com/tdewolff/parse/v2 v2.8.5/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=\ngithub.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE=\ngithub.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=\ngithub.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=\ngithub.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=\ngithub.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=\ngithub.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=\ngithub.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=\ngithub.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=\ngithub.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA=\ngithub.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0=\ngithub.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=\ngithub.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=\ngithub.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngo.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=\ngolang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=\ngolang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=\ngolang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=\ngolang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=\ngolang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=\ngolang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=\ngolang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=\ngolang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=\ngoogle.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=\ngopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nmoul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs=\nmoul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE=\n"
  },
  {
    "path": "_examples/configuration/multi-environments/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/kataras/my-iris-app/cmd\"\n)\n\nfunc main() {\n\tapp := cmd.New()\n\tif err := app.Execute(); err != nil {\n\t\tfmt.Println(err)\n\t\tos.Exit(1)\n\t}\n}\n"
  },
  {
    "path": "_examples/configuration/multi-environments/server.dev.yml",
    "content": "ServerName: \"my-iris-app\"\n\nHost: 0.0.0.0\nPort: 8080\nEnableCompression: false\nAllowOrigin: \"*\"\n\n# Your development environment's \n# database connection configuration here...\n\n####\n\n# Iris configuration.\nIris:\n  LogLevel: info\n  EnableOptimizations: true\n  RemoteAddrHeaders:\n  - \"X-Real-Ip\"\n  - \"X-Forwarded-For\"\n  - \"CF-Connecting-IP\"\n  - \"True-Client-Ip\"\n  - \"X-Appengine-Remote-Addr\"\n"
  },
  {
    "path": "_examples/configuration/multi-environments/server.yml",
    "content": "Host: 0.0.0.0\nPort: 80\nEnableCompression: true\nAllowOrigin: \"*\"\n\n# Your production's database connection configuration here...\n\n####\n\n# Iris configuration.\nIris:\n  LogLevel: info\n  EnableOptimizations: true\n  RemoteAddrHeaders:\n  - \"X-Real-Ip\"\n  - \"X-Forwarded-For\"\n  - \"CF-Connecting-IP\"\n  - \"True-Client-Ip\"\n  - \"X-Appengine-Remote-Addr\"\n"
  },
  {
    "path": "_examples/configuration/viper/config/config.go",
    "content": "package config\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n\t// Viper is a third-party package:\n\t// go get github.com/spf13/viper\n\t\"github.com/spf13/viper\"\n)\n\nfunc init() {\n\tloadConfiguration()\n}\n\n// C is the configuration of the app.\nvar C = struct {\n\tIris iris.Configuration\n\tAddr struct {\n\t\tInternal struct {\n\t\t\tIP     string\n\t\t\tPlain  int\n\t\t\tSecure int\n\t\t}\n\t}\n\tLocale struct {\n\t\tPattern   string\n\t\tDefault   string\n\t\tSupported []string\n\t}\n}{\n\tIris: iris.DefaultConfiguration(),\n\t// other default values...\n}\n\nfunc loadConfiguration() {\n\tviper.SetConfigName(\"config\")     // name of config file (without extension)\n\tviper.SetConfigType(\"yaml\")       // REQUIRED if the config file does not have the extension in the name\n\tviper.AddConfigPath(\"/etc/app/\")  // path to look for the config file in\n\tviper.AddConfigPath(\"$HOME/.app\") // call multiple times to add many search paths\n\tviper.AddConfigPath(\".\")          // optionally look for config in the working directory\n\terr := viper.ReadInConfig()       // Find and read the config file\n\tif err := viper.ReadInConfig(); err != nil {\n\t\tif _, ok := err.(viper.ConfigFileNotFoundError); ok {\n\t\t} else {\n\t\t\tpanic(fmt.Errorf(\"load configuration: %w\", err))\n\t\t}\n\t}\n\n\terr = viper.Unmarshal(&C)\n\tif err != nil {\n\t\tpanic(fmt.Errorf(\"load configuration: unmarshal: %w\", err))\n\t}\n}\n"
  },
  {
    "path": "_examples/configuration/viper/config.yml",
    "content": "Addr:\n  Internal:\n    IP: 127.0.0.1\n    Plain: 8080\n    Secure: 443\nLocale:\n  Pattern: \"./locales/*/*.ini\"\n  Default: \"en-US\"\n  Supported:\n  - \"en-US\"\n  - \"el-GR\"\nIris:\n  LogLevel: debug\n  SocketSharding: true\n  EnableOptimizations: true\n  DisableStartupLog: false\n  FireMethodNotAllowed: true\n  ForceLowercaseRouting: true\n  EnablePathIntelligence: true\n  Charset: \"utf-8\"\n  TimeFormat: \"2006-01-02 15:04:05\"\n  DisableBodyConsumptionOnUnmarshal: true\n  FireEmptyFormError: true\n  PostMaxMemory: 67108864\n  RemoteAddrHeaders:\n    - \"X-Real-Ip\"\n    - \"X-Forwarded-For\"\n    - \"CF-Connecting-IP\"\n    - \"True-Client-Ip\"\n  IgnoreServerErrors:\n    - \"http: Server closed\"\n  # Tunneling:\n  #   WebInterface: \"http://127.0.0.1:4040\"\n  #   AuthToken: \"<secret>\"\n  #   Tunnels:\n  #     - Name: \"My awesome App\"\n  #       Addr: \"localhost:8080\"\n  #     - Name:  \"My Second awesome App\"\n  #       Addr: \"localhost:9090\"\n  RemoteAddrPrivateSubnets:\n    - Start: \"192.168.0.0\"\n      End: \"192.168.255.255\"\n    - Start: \"198.18.0.0\"\n      End: \"198.19.255.255\"\n  SSLProxyHeaders:\n    X-Forwarded-Proto: \"https\"\n  HostProxyHeaders:\n    X-Host: true\n  Other:\n    ServerName: \"My awesome Iris web server\""
  },
  {
    "path": "_examples/configuration/viper/go.mod",
    "content": "module app\n\ngo 1.25\n\nrequire (\n\tgithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd\n\tgithub.com/spf13/viper v1.21.0\n)\n\nrequire (\n\tgithub.com/BurntSushi/toml v1.6.0 // indirect\n\tgithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect\n\tgithub.com/CloudyKit/jet/v6 v6.3.1 // indirect\n\tgithub.com/Joker/jade v1.1.3 // indirect\n\tgithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 // indirect\n\tgithub.com/andybalholm/brotli v1.2.0 // indirect\n\tgithub.com/aymerick/douceur v0.2.0 // indirect\n\tgithub.com/fatih/structs v1.1.0 // indirect\n\tgithub.com/flosch/pongo2/v4 v4.0.2 // indirect\n\tgithub.com/fsnotify/fsnotify v1.9.0 // indirect\n\tgithub.com/go-viper/mapstructure/v2 v2.4.0 // indirect\n\tgithub.com/golang/snappy v1.0.0 // indirect\n\tgithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a // indirect\n\tgithub.com/google/uuid v1.6.0 // indirect\n\tgithub.com/gorilla/css v1.0.1 // indirect\n\tgithub.com/iris-contrib/schema v0.0.6 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/kataras/blocks v0.0.8 // indirect\n\tgithub.com/kataras/golog v0.1.12 // indirect\n\tgithub.com/kataras/pio v0.0.13 // indirect\n\tgithub.com/kataras/sitemap v0.0.6 // indirect\n\tgithub.com/kataras/tunnel v0.0.4 // indirect\n\tgithub.com/klauspost/compress v1.18.2 // indirect\n\tgithub.com/mailgun/raymond/v2 v2.0.48 // indirect\n\tgithub.com/mailru/easyjson v0.9.1 // indirect\n\tgithub.com/microcosm-cc/bluemonday v1.0.27 // indirect\n\tgithub.com/pelletier/go-toml/v2 v2.2.4 // indirect\n\tgithub.com/rogpeppe/go-internal v1.10.0 // indirect\n\tgithub.com/russross/blackfriday/v2 v2.1.0 // indirect\n\tgithub.com/sagikazarmark/locafero v0.11.0 // indirect\n\tgithub.com/schollz/closestmatch v2.1.0+incompatible // indirect\n\tgithub.com/sirupsen/logrus v1.8.1 // indirect\n\tgithub.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect\n\tgithub.com/spf13/afero v1.15.0 // indirect\n\tgithub.com/spf13/cast v1.10.0 // indirect\n\tgithub.com/spf13/pflag v1.0.10 // indirect\n\tgithub.com/subosito/gotenv v1.6.0 // indirect\n\tgithub.com/tdewolff/minify/v2 v2.24.8 // indirect\n\tgithub.com/tdewolff/parse/v2 v2.8.5 // indirect\n\tgithub.com/valyala/bytebufferpool v1.0.0 // indirect\n\tgithub.com/vmihailenco/msgpack/v5 v5.4.1 // indirect\n\tgithub.com/vmihailenco/tagparser/v2 v2.0.0 // indirect\n\tgithub.com/yosssi/ace v0.0.5 // indirect\n\tgo.yaml.in/yaml/v3 v3.0.4 // indirect\n\tgolang.org/x/crypto v0.47.0 // indirect\n\tgolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 // indirect\n\tgolang.org/x/net v0.49.0 // indirect\n\tgolang.org/x/sys v0.40.0 // indirect\n\tgolang.org/x/text v0.33.0 // indirect\n\tgolang.org/x/time v0.14.0 // indirect\n\tgoogle.golang.org/protobuf v1.36.11 // indirect\n\tgopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect\n\tgopkg.in/ini.v1 v1.67.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n"
  },
  {
    "path": "_examples/configuration/viper/go.sum",
    "content": "github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=\ngithub.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=\ngithub.com/CloudyKit/jet/v6 v6.3.1 h1:6IAo5Cx21xrHVaR8zzXN5gJatKV/wO7Nf6bfCnCSbUw=\ngithub.com/CloudyKit/jet/v6 v6.3.1/go.mod h1:lf8ksdNsxZt7/yH/3n4vJQWA9RUq4wpaHtArHhGVMOw=\ngithub.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc=\ngithub.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=\ngithub.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=\ngithub.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 h1:dG7/gLroJGht/jSQtHiLvT48Hxn+crbmvyItZC8cWOs=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05/go.mod h1:NYezi6wtnJtBm5btoprXc5SvAdqH0XTXWnUup0MptAI=\ngithub.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=\ngithub.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=\ngithub.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=\ngithub.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=\ngithub.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=\ngithub.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=\ngithub.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=\ngithub.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=\ngithub.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=\ngithub.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw=\ngithub.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8=\ngithub.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=\ngithub.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=\ngithub.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=\ngithub.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=\ngithub.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=\ngithub.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=\ngithub.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=\ngithub.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=\ngithub.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=\ngithub.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a h1:l7A0loSszR5zHd/qK53ZIHMO8b3bBSmENnQ6eKnUT0A=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=\ngithub.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=\ngithub.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=\ngithub.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=\ngithub.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=\ngithub.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2 h1:T9THsdP1woyAqKHwjkEsbCnMefsAFvk8iJJKokcJ3Go=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2/go.mod h1:JLDgIqnFy5loDSUv1OA2j0mb6p/rDhiCqigP22Uq9xE=\ngithub.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw=\ngithub.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/kataras/blocks v0.0.8 h1:MrpVhoFTCR2v1iOOfGng5VJSILKeZZI+7NGfxEh3SUM=\ngithub.com/kataras/blocks v0.0.8/go.mod h1:9Jm5zx6BB+06NwA+OhTbHW1xkMOYxahnqTN5DveZ2Yg=\ngithub.com/kataras/golog v0.1.12 h1:Bu7I/G4ilJlbfzjmU39O9N+2uO1pBcMK045fzZ4ytNg=\ngithub.com/kataras/golog v0.1.12/go.mod h1:wrGSbOiBqbQSQznleVNX4epWM8rl9SJ/rmEacl0yqy4=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd h1:oMsQgqKACbUdlaIWnN+EZzzAnHkw90hE/y/R0BSij2U=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd/go.mod h1:yucjtDl9Vn3OctWM1fi0MuM9OckemC7kEreFa9QtPF8=\ngithub.com/kataras/pio v0.0.13 h1:x0rXVX0fviDTXOOLOmr4MUxOabu1InVSTu5itF8CXCM=\ngithub.com/kataras/pio v0.0.13/go.mod h1:k3HNuSw+eJ8Pm2lA4lRhg3DiCjVgHlP8hmXApSej3oM=\ngithub.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY=\ngithub.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4=\ngithub.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA=\ngithub.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw=\ngithub.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=\ngithub.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=\ngithub.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=\ngithub.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8=\ngithub.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=\ngithub.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=\ngithub.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=\ngithub.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=\ngithub.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=\ngithub.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=\ngithub.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=\ngithub.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=\ngithub.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=\ngithub.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=\ngithub.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=\ngithub.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=\ngithub.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=\ngithub.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=\ngithub.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk=\ngithub.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=\ngithub.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=\ngithub.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=\ngithub.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=\ngithub.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=\ngithub.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=\ngithub.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=\ngithub.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=\ngithub.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=\ngithub.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=\ngithub.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=\ngithub.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=\ngithub.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=\ngithub.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=\ngithub.com/tdewolff/minify/v2 v2.24.8 h1:58/VjsbevI4d5FGV0ZSuBrHMSSkH4MCH0sIz/eKIauE=\ngithub.com/tdewolff/minify/v2 v2.24.8/go.mod h1:0Ukj0CRpo/sW/nd8uZ4ccXaV1rEVIWA3dj8U7+Shhfw=\ngithub.com/tdewolff/parse/v2 v2.8.5 h1:ZmBiA/8Do5Rpk7bDye0jbbDUpXXbCdc3iah4VeUvwYU=\ngithub.com/tdewolff/parse/v2 v2.8.5/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=\ngithub.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE=\ngithub.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=\ngithub.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=\ngithub.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=\ngithub.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=\ngithub.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=\ngithub.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=\ngithub.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=\ngithub.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA=\ngithub.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0=\ngithub.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=\ngithub.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=\ngithub.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngo.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=\ngo.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=\ngolang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=\ngolang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=\ngolang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=\ngolang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=\ngolang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=\ngolang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=\ngolang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=\ngolang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=\ngoogle.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=\ngopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nmoul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs=\nmoul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE=\n"
  },
  {
    "path": "_examples/configuration/viper/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"app/config\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.TextYAML(config.C)\n\t})\n\n\taddr := fmt.Sprintf(\"%s:%d\", config.C.Addr.Internal.IP, config.C.Addr.Internal.Plain)\n\tapp.Listen(addr, iris.WithConfiguration(config.C.Iris))\n}\n"
  },
  {
    "path": "_examples/convert-handlers/negroni-like/main.go",
    "content": "package main\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tirisMiddleware := iris.FromStd(negronilikeTestMiddleware)\n\tapp.Use(irisMiddleware)\n\n\t// Method GET: http://localhost:8080/\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.HTML(\"<h1> Home </h1>\")\n\t\t// this will print an error,\n\t\t// this route's handler will never be executed because the middleware's criteria not passed.\n\t})\n\n\t// Method GET: http://localhost:8080/ok\n\tapp.Get(\"/ok\", func(ctx iris.Context) {\n\t\tctx.Writef(\"Hello world!\")\n\t\t// this will print \"OK. Hello world!\".\n\t})\n\n\t// http://localhost:8080\n\t// http://localhost:8080/ok\n\tapp.Listen(\":8080\")\n}\n\nfunc negronilikeTestMiddleware(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {\n\tif r.URL.Path == \"/ok\" && r.Method == \"GET\" {\n\t\tw.Write([]byte(\"OK. \"))\n\t\tnext(w, r) // go to the next route's handler\n\t\treturn\n\t}\n\t// else print an error and do not forward to the route's handler.\n\tw.WriteHeader(iris.StatusBadRequest)\n\tw.Write([]byte(\"Bad request\"))\n}\n"
  },
  {
    "path": "_examples/convert-handlers/nethttp/main.go",
    "content": "package main\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tirisMiddleware := iris.FromStd(nativeTestMiddleware)\n\tapp.Use(irisMiddleware)\n\n\t// Method GET: http://localhost:8080/\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.HTML(\"Home\")\n\t})\n\n\t// Method GET: http://localhost:8080/ok\n\tapp.Get(\"/ok\", func(ctx iris.Context) {\n\t\tctx.HTML(\"<b>Hello world!</b>\")\n\t})\n\n\t// http://localhost:8080\n\t// http://localhost:8080/ok\n\tapp.Listen(\":8080\")\n}\n\nfunc nativeTestMiddleware(w http.ResponseWriter, r *http.Request) {\n\tprintln(\"Request path: \" + r.URL.Path)\n}\n"
  },
  {
    "path": "_examples/convert-handlers/nethttp/wrapper/main.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\thttpThirdPartyWrapper := StandardWrapper(Options{\n\t\tMessage: \"test_value\",\n\t})\n\n\t// This case\n\tapp.WrapRouter(func(w http.ResponseWriter, r *http.Request, router http.HandlerFunc) {\n\t\thttpThirdPartyWrapper(router).ServeHTTP(w, r)\n\t\t// If was func(http.HandlerFunc) http.HandlerFunc:\n\t\t// httpThirdPartyWrapper(router.ServeHTTP).ServeHTTP(w, r)\n\t})\n\n\tapp.Get(\"/\", index)\n\tapp.Listen(\":8080\")\n}\n\nfunc index(ctx iris.Context) {\n\tctx.Writef(\"Message: %s\\n\", ctx.Value(msgContextKey))\n}\n\ntype Options struct {\n\tMessage string\n}\n\ntype contextKey uint8\n\nvar (\n\tmsgContextKey contextKey = 1\n)\n\nfunc StandardWrapper(opts Options) func(http.Handler) http.Handler {\n\treturn func(next http.Handler) http.Handler {\n\t\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t\t// ...\n\t\t\treq := r.WithContext(context.WithValue(r.Context(), msgContextKey, opts.Message))\n\t\t\tnext.ServeHTTP(w, req)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "_examples/convert-handlers/real-usecase-raven/wrapping-the-router/main.go",
    "content": "package main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"runtime/debug\"\n\n\t\"github.com/kataras/iris/v12\"\n\n\t\"github.com/getsentry/raven-go\"\n)\n\n// https://docs.sentry.io/clients/go/integrations/http/\nfunc init() {\n\traven.SetDSN(\"https://<key>:<secret>@sentry.io/<project>\")\n}\n\nfunc main() {\n\tapp := iris.New()\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.Writef(\"Hi\")\n\t})\n\n\t// Example for WrapRouter is already here:\n\t// https://github.com/kataras/iris/blob/main/_examples/routing/custom-wrapper/main.go#L53\n\tapp.WrapRouter(func(w http.ResponseWriter, r *http.Request, irisRouter http.HandlerFunc) {\n\t\t// Exactly the same source code:\n\t\t// https://github.com/getsentry/raven-go/blob/379f8d0a68ca237cf8893a1cdfd4f574125e2c51/http.go#L70\n\n\t\tdefer func() {\n\t\t\tif rval := recover(); rval != nil {\n\t\t\t\tdebug.PrintStack()\n\t\t\t\trvalStr := fmt.Sprint(rval)\n\t\t\t\tpacket := raven.NewPacket(rvalStr, raven.NewException(errors.New(rvalStr), raven.NewStacktrace(2, 3, nil)), raven.NewHttp(r))\n\t\t\t\traven.Capture(packet, nil)\n\t\t\t\tw.WriteHeader(http.StatusInternalServerError)\n\t\t\t}\n\t\t}()\n\n\t\tirisRouter(w, r)\n\t})\n\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/convert-handlers/real-usecase-raven/writing-middleware/main.go",
    "content": "package main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"runtime/debug\"\n\n\t\"github.com/kataras/iris/v12\"\n\n\t\"github.com/getsentry/raven-go\"\n)\n\n// At this example you will see how to convert any net/http middleware\n// that has the form of `(HandlerFunc) HandlerFunc`.\n// If the `raven.RecoveryHandler` had the form of\n// `(http.HandlerFunc)` or `(http.HandlerFunc, next http.HandlerFunc)`\n// you could just use the `irisMiddleware := iris.FromStd(nativeHandler)`\n// but it doesn't, however as you already know Iris can work with net/http directly\n// because of the `ctx.ResponseWriter()` and `ctx.Request()` are the original\n// http.ResponseWriter and *http.Request.\n// (this one is a big advantage, as a result you can use Iris for ever :)).\n//\n// The source code of the native middleware does not change at all.\n// https://github.com/getsentry/raven-go/blob/379f8d0a68ca237cf8893a1cdfd4f574125e2c51/http.go#L70\n// The only addition is the Line 18 and Line 39 (instead of handler(w,r))\n// and you have a new iris middleware ready to use!\nfunc irisRavenMiddleware(ctx iris.Context) {\n\tw, r := ctx.ResponseWriter(), ctx.Request()\n\n\tdefer func() {\n\t\tif rval := recover(); rval != nil {\n\t\t\tdebug.PrintStack()\n\t\t\trvalStr := fmt.Sprint(rval)\n\t\t\tpacket := raven.NewPacket(rvalStr, raven.NewException(errors.New(rvalStr), raven.NewStacktrace(2, 3, nil)), raven.NewHttp(r))\n\t\t\traven.Capture(packet, nil)\n\t\t\tw.WriteHeader(iris.StatusInternalServerError)\n\t\t}\n\t}()\n\n\tctx.Next()\n}\n\n// https://docs.sentry.io/clients/go/integrations/http/\nfunc init() {\n\traven.SetDSN(\"https://<key>:<secret>@sentry.io/<project>\")\n}\n\nfunc main() {\n\tapp := iris.New()\n\tapp.Use(irisRavenMiddleware)\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.Writef(\"Hi\")\n\t})\n\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/cookies/basic/main.go",
    "content": "package main\n\nimport \"github.com/kataras/iris/v12\"\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\n\t// Set A Cookie.\n\tapp.Get(\"/cookies/{name}/{value}\", func(ctx iris.Context) {\n\t\tname := ctx.Params().Get(\"name\")\n\t\tvalue := ctx.Params().Get(\"value\")\n\n\t\tctx.SetCookieKV(name, value) // <--\n\t\t// Alternatively: ctx.SetCookie(&http.Cookie{...})\n\t\t//\n\t\t// If you want to set custom the path:\n\t\t// ctx.SetCookieKV(name, value, iris.CookiePath(\"/custom/path/cookie/will/be/stored\"))\n\t\t//\n\t\t// If you want to be visible only to current request path:\n\t\t// (note that client should be responsible for that if server sent an empty cookie's path, all browsers are compatible)\n\t\t// ctx.SetCookieKV(name, value, iris.CookieCleanPath /* or iris.CookiePath(\"\") */)\n\t\t// More:\n\t\t//                              iris.CookieExpires(time.Duration)\n\t\t//                              iris.CookieHTTPOnly(false)\n\n\t\tctx.Writef(\"cookie added: %s = %s\", name, value)\n\t})\n\n\t// Retrieve A Cookie.\n\tapp.Get(\"/cookies/{name}\", func(ctx iris.Context) {\n\t\tname := ctx.Params().Get(\"name\")\n\n\t\tvalue := ctx.GetCookie(name) // <--\n\t\t// If you want more than the value then:\n\t\t// cookie, err := ctx.Request().Cookie(name)\n\t\t// if err != nil {\n\t\t//  handle error.\n\t\t// }\n\n\t\tctx.WriteString(value)\n\t})\n\n\t// Delete A Cookie.\n\tapp.Delete(\"/cookies/{name}\", func(ctx iris.Context) {\n\t\tname := ctx.Params().Get(\"name\")\n\n\t\tctx.RemoveCookie(name) // <--\n\t\t// If you want to set custom the path:\n\t\t// ctx.SetCookieKV(name, value, iris.CookiePath(\"/custom/path/cookie/will/be/stored\"))\n\n\t\tctx.Writef(\"cookie %s removed\", name)\n\t})\n\n\treturn app\n}\n\nfunc main() {\n\tapp := newApp()\n\n\t// GET:    http://localhost:8080/cookies/my_name/my_value\n\t// GET:    http://localhost:8080/cookies/my_name\n\t// DELETE: http://localhost:8080/cookies/my_name\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/cookies/basic/main_test.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nfunc TestCookiesBasic(t *testing.T) {\n\tapp := newApp()\n\te := httptest.New(t, app, httptest.URL(\"http://example.com\"))\n\n\tcookieName, cookieValue := \"my_cookie_name\", \"my_cookie_value\"\n\n\t// Test set a Cookie.\n\tt1 := e.GET(fmt.Sprintf(\"/cookies/%s/%s\", cookieName, cookieValue)).Expect().Status(httptest.StatusOK)\n\tt1.Cookie(cookieName).Value().Equal(cookieValue) // validate cookie's existence, it should be there now.\n\tt1.Body().Contains(cookieValue)\n\n\t// Test retrieve a Cookie.\n\tt2 := e.GET(fmt.Sprintf(\"/cookies/%s\", cookieName)).Expect().Status(httptest.StatusOK)\n\tt2.Body().IsEqual(cookieValue)\n\n\t// Test remove a Cookie.\n\tt3 := e.DELETE(fmt.Sprintf(\"/cookies/%s\", cookieName)).Expect().Status(httptest.StatusOK)\n\tt3.Body().Contains(cookieName)\n\n\tt4 := e.GET(fmt.Sprintf(\"/cookies/%s\", cookieName)).Expect().Status(httptest.StatusOK)\n\tt4.Cookies().Empty()\n\tt4.Body().IsEmpty()\n}\n"
  },
  {
    "path": "_examples/cookies/options/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := newApp()\n\n\t// http://localhost:8080/set/name1/value1\n\t// http://localhost:8080/get/name1\n\t// http://localhost:8080/remove/name1\n\tapp.Listen(\":8080\", iris.WithLogLevel(\"debug\"))\n}\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\tapp.Use(withCookieOptions)\n\n\tapp.Get(\"/set/{name}/{value}\", setCookie)\n\tapp.Get(\"/get/{name}\", getCookie)\n\tapp.Get(\"/remove/{name}\", removeCookie)\n\n\treturn app\n}\n\nfunc withCookieOptions(ctx iris.Context) {\n\t// Register cookie options for request-lifecycle.\n\t// To register per cookie, just add the CookieOption\n\t// on the last variadic input argument of\n\t// SetCookie, SetCookieKV, UpsertCookie, RemoveCookie\n\t// and GetCookie Context methods.\n\t//\n\t//  * CookieAllowReclaim\n\t//  * CookieAllowSubdomains\n\t//  * CookieSecure\n\t//  * CookieHTTPOnly\n\t//  * CookieSameSite\n\t//  * CookiePath\n\t//  * CookieCleanPath\n\t//  * CookieExpires\n\t//  * CookieEncoding\n\tctx.AddCookieOptions(iris.CookieAllowReclaim())\n\t// ctx.AddCookieOptions(iris.CookieSecure)\n\t// OR for a specific cookie:\n\t// ctx.SetCookieKV(\"cookie_name\", \"cookie_value\", iris.CookieScure)\n\t// OR by passing a a &http.Cookie:\n\t// ctx.SetCookie(&http.Cookie{\n\t// \tName: \"cookie_name\",\n\t// \tValue: \"cookie_value\",\n\t// \tSecure: true,\n\t// })\n\tctx.Next()\n}\n\nfunc setCookie(ctx iris.Context) {\n\tname := ctx.Params().Get(\"name\")\n\tvalue := ctx.Params().Get(\"value\")\n\n\tctx.SetCookieKV(name, value)\n\n\t// By-default net/http does not remove or set the Cookie on the Request object.\n\t//\n\t// With the `CookieAllowReclaim` option, whenever you set or remove a cookie\n\t// it will be also reflected in the Request object immediately (of the same request lifecycle)\n\t// therefore, any of the next handlers in the chain are not holding the old value.\n\tvalueIsAvailableInRequestObject := ctx.GetCookie(name)\n\tctx.Writef(\"cookie %s=%s\", name, valueIsAvailableInRequestObject)\n}\n\nfunc getCookie(ctx iris.Context) {\n\tname := ctx.Params().Get(\"name\")\n\n\tvalue := ctx.GetCookie(name)\n\tctx.WriteString(value)\n}\n\nfunc removeCookie(ctx iris.Context) {\n\tname := ctx.Params().Get(\"name\")\n\n\tctx.RemoveCookie(name)\n\n\tremovedFromRequestObject := ctx.GetCookie(name) // CookieAllowReclaim feature.\n\tctx.Writef(\"cookie %s removed, value should be empty=%s\", name, removedFromRequestObject)\n}\n"
  },
  {
    "path": "_examples/cookies/options/main_test.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nfunc TestCookieOptions(t *testing.T) {\n\tapp := newApp()\n\te := httptest.New(t, app, httptest.URL(\"http://example.com\"))\n\n\tcookieName, cookieValue := \"my_cookie_name\", \"my_cookie_value\"\n\n\t// Test set a Cookie.\n\tt1 := e.GET(fmt.Sprintf(\"/set/%s/%s\", cookieName, cookieValue)).Expect().Status(httptest.StatusOK)\n\tt1.Cookie(cookieName).Value().Equal(cookieValue)\n\tt1.Body().Contains(fmt.Sprintf(\"%s=%s\", cookieName, cookieValue))\n\n\t// Test retrieve a Cookie.\n\tt2 := e.GET(fmt.Sprintf(\"/get/%s\", cookieName)).Expect().Status(httptest.StatusOK)\n\tt2.Body().IsEqual(cookieValue)\n\n\t// Test remove a Cookie.\n\tt3 := e.GET(fmt.Sprintf(\"/remove/%s\", cookieName)).Expect().Status(httptest.StatusOK)\n\tt3.Body().Contains(fmt.Sprintf(\"cookie %s removed, value should be empty=%s\", cookieName, \"\"))\n\n\tt4 := e.GET(fmt.Sprintf(\"/get/%s\", cookieName)).Expect().Status(httptest.StatusOK)\n\tt4.Cookies().Empty()\n\tt4.Body().IsEmpty()\n}\n"
  },
  {
    "path": "_examples/cookies/securecookie/main.go",
    "content": "package main\n\n// developers can use any library to add a custom cookie encoder/decoder.\n// At this example we use the gorilla's securecookie package:\n// $ go get github.com/gorilla/securecookie\n// $ go run main.go\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n\n\t\"github.com/gorilla/securecookie\"\n)\n\nfunc main() {\n\tapp := newApp()\n\t// http://localhost:8080/cookies/name/value\n\t// http://localhost:8080/cookies/name\n\t// http://localhost:8080/cookies/remove/name\n\tapp.Listen(\":8080\")\n}\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\n\tr := app.Party(\"/cookies\")\n\t{\n\t\tr.Use(useSecureCookies())\n\n\t\t// Set A Cookie.\n\t\tr.Get(\"/{name}/{value}\", func(ctx iris.Context) {\n\t\t\tname := ctx.Params().Get(\"name\")\n\t\t\tvalue := ctx.Params().Get(\"value\")\n\n\t\t\tctx.SetCookieKV(name, value)\n\n\t\t\tctx.Writef(\"cookie added: %s = %s\", name, value)\n\t\t})\n\n\t\t// Retrieve A Cookie.\n\t\tr.Get(\"/{name}\", func(ctx iris.Context) {\n\t\t\tname := ctx.Params().Get(\"name\")\n\n\t\t\tvalue := ctx.GetCookie(name)\n\n\t\t\tctx.WriteString(value)\n\t\t})\n\n\t\tr.Get(\"/remove/{name}\", func(ctx iris.Context) {\n\t\t\tname := ctx.Params().Get(\"name\")\n\n\t\t\tctx.RemoveCookie(name)\n\n\t\t\tctx.Writef(\"cookie %s removed\", name)\n\t\t})\n\t}\n\n\treturn app\n}\n\nfunc useSecureCookies() iris.Handler {\n\tvar (\n\t\thashKey  = securecookie.GenerateRandomKey(64)\n\t\tblockKey = securecookie.GenerateRandomKey(32)\n\n\t\ts = securecookie.New(hashKey, blockKey)\n\t)\n\n\treturn func(ctx iris.Context) {\n\t\tctx.AddCookieOptions(iris.CookieEncoding(s))\n\t\tctx.Next()\n\t}\n}\n"
  },
  {
    "path": "_examples/cookies/securecookie/main_test.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nfunc TestSecureCookie(t *testing.T) {\n\tapp := newApp()\n\te := httptest.New(t, app, httptest.URL(\"http://example.com\"))\n\n\tcookieName, cookieValue := \"my_cookie_name\", \"my_cookie_value\"\n\n\t// Test set a Cookie.\n\tt1 := e.GET(fmt.Sprintf(\"/cookies/%s/%s\", cookieName, cookieValue)).Expect().Status(httptest.StatusOK)\n\t// note that this will not work because it doesn't always returns the same value:\n\t// cookieValueEncoded, _ := sc.Encode(cookieName, cookieValue)\n\tt1.Cookie(cookieName).Value().NotEqual(cookieValue) // validate cookie's existence and value is not on its raw form.\n\tt1.Body().Contains(cookieValue)\n\n\t// Test retrieve a Cookie.\n\tt2 := e.GET(fmt.Sprintf(\"/cookies/%s\", cookieName)).Expect().Status(httptest.StatusOK)\n\tt2.Body().IsEqual(cookieValue)\n\n\t// Test remove a Cookie.\n\tt3 := e.GET(fmt.Sprintf(\"/cookies/remove/%s\", cookieName)).Expect().Status(httptest.StatusOK)\n\tt3.Body().Contains(cookieName)\n\n\tt4 := e.GET(fmt.Sprintf(\"/cookies/%s\", cookieName)).Expect().Status(httptest.StatusOK)\n\tt4.Cookies().Empty()\n\tt4.Body().IsEmpty()\n}\n"
  },
  {
    "path": "_examples/database/mongodb/Dockerfile",
    "content": "# docker build -t myapp . \n# docker run --rm -it -p 8080:8080 myapp:latest\nFROM golang:latest AS builder\nRUN apt-get update\nENV GO111MODULE=on \\\n    CGO_ENABLED=0 \\\n    GOOS=linux \\\n    GOARCH=amd64\nWORKDIR /go/src/app\nCOPY go.mod .\nRUN go mod download\nCOPY . .\nRUN go install\n\nFROM scratch\nCOPY --from=builder /go/bin/myapp .\nENTRYPOINT [\"./myapp\"]"
  },
  {
    "path": "_examples/database/mongodb/README.md",
    "content": "# Build RESTful API with the official MongoDB Go Driver and Iris \n\nArticle is coming soon, follow and stay tuned\n\n- <https://medium.com/@kataras>\n- <https://dev.to/kataras>\n\nRead [the fully functional example](main.go).\n\n## Run\n\n### Docker\n\nInstall [Docker](https://www.docker.com/) and execute the command below\n\n```sh\n$ docker-compose up\n```\n\n### Manually\n\n```sh\n# .env file contents\nPORT=8080\nDSN=mongodb://localhost:27017\n```\n\n```sh\n$ go run main.go\n> 2019/01/28 05:17:59 Loading environment variables from file: .env\n> 2019/01/28 05:17:59 ◽ Port=8080\n> 2019/01/28 05:17:59 ◽ DSN=mongodb://localhost:27017\n> Now listening on: http://localhost:8080\n```\n\n```sh\nGET    :  http://localhost:8080/api/store/movies\nPOST   :  http://localhost:8080/api/store/movies\nGET    :  http://localhost:8080/api/store/movies/{id}\nPUT    :  http://localhost:8080/api/store/movies/{id}\nDELETE :  http://localhost:8080/api/store/movies/{id}\n```\n\n## Screens\n\n### Add a Movie\n![](0_create_movie.png)\n\n### Update a Movie\n\n![](1_update_movie.png)\n\n### Get all Movies\n\n![](2_get_all_movies.png)\n\n### Get a Movie by its ID\n\n![](3_get_movie.png)\n\n### Delete a Movie by its ID\n\n![](4_delete_movie.png)\n\n"
  },
  {
    "path": "_examples/database/mongodb/api/store/movie.go",
    "content": "package storeapi\n\nimport (\n\t\"myapp/httputil\"\n\t\"myapp/store\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\ntype MovieHandler struct {\n\tservice store.MovieService\n}\n\nfunc NewMovieHandler(service store.MovieService) *MovieHandler {\n\treturn &MovieHandler{service: service}\n}\n\nfunc (h *MovieHandler) GetAll(ctx iris.Context) {\n\tmovies, err := h.service.GetAll(nil)\n\tif err != nil {\n\t\thttputil.InternalServerErrorJSON(ctx, err, \"Server was unable to retrieve all movies\")\n\t\treturn\n\t}\n\n\tif movies == nil {\n\t\t// will return \"null\" if empty, with this \"trick\" we return \"[]\" json.\n\t\tmovies = make([]store.Movie, 0)\n\t}\n\n\tctx.JSON(movies)\n}\n\nfunc (h *MovieHandler) Get(ctx iris.Context) {\n\tid := ctx.Params().Get(\"id\")\n\n\tm, err := h.service.GetByID(nil, id)\n\tif err != nil {\n\t\tif err == store.ErrNotFound {\n\t\t\tctx.NotFound()\n\t\t} else {\n\t\t\thttputil.InternalServerErrorJSON(ctx, err, \"Server was unable to retrieve movie [%s]\", id)\n\t\t}\n\t\treturn\n\t}\n\n\tctx.JSON(m)\n}\n\nfunc (h *MovieHandler) Add(ctx iris.Context) {\n\tm := new(store.Movie)\n\n\terr := ctx.ReadJSON(m)\n\tif err != nil {\n\t\thttputil.FailJSON(ctx, iris.StatusBadRequest, err, \"Malformed request payload\")\n\t\treturn\n\t}\n\n\terr = h.service.Create(nil, m)\n\tif err != nil {\n\t\thttputil.InternalServerErrorJSON(ctx, err, \"Server was unable to create a movie\")\n\t\treturn\n\t}\n\n\tctx.StatusCode(iris.StatusCreated)\n\tctx.JSON(m)\n}\n\nfunc (h *MovieHandler) Update(ctx iris.Context) {\n\tid := ctx.Params().Get(\"id\")\n\n\tvar m store.Movie\n\terr := ctx.ReadJSON(&m)\n\tif err != nil {\n\t\thttputil.FailJSON(ctx, iris.StatusBadRequest, err, \"Malformed request payload\")\n\t\treturn\n\t}\n\n\terr = h.service.Update(nil, id, m)\n\tif err != nil {\n\t\tif err == store.ErrNotFound {\n\t\t\tctx.NotFound()\n\t\t\treturn\n\t\t}\n\t\thttputil.InternalServerErrorJSON(ctx, err, \"Server was unable to update movie [%s]\", id)\n\t\treturn\n\t}\n}\n\nfunc (h *MovieHandler) Delete(ctx iris.Context) {\n\tid := ctx.Params().Get(\"id\")\n\n\terr := h.service.Delete(nil, id)\n\tif err != nil {\n\t\tif err == store.ErrNotFound {\n\t\t\tctx.NotFound()\n\t\t\treturn\n\t\t}\n\t\thttputil.InternalServerErrorJSON(ctx, err, \"Server was unable to delete movie [%s]\", id)\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "_examples/database/mongodb/docker-compose.yml",
    "content": "version: \"3.1\"\n\nservices:\n  app:\n    build: .\n    environment: \n      Port: 8080\n      DSN: db:27017\n    ports:\n      - 8080:8080\n    depends_on:\n      - db\n  db:\n    image: mongo\n    environment:\n      MONGO_INITDB_DATABASE: store\n    ports:\n      - 27017:27017\n"
  },
  {
    "path": "_examples/database/mongodb/env/env.go",
    "content": "package env\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/joho/godotenv\"\n)\n\nvar (\n\t// Port is the PORT environment variable or 8080 if missing.\n\t// Used to open the tcp listener for our web server.\n\tPort string\n\t// DSN is the DSN environment variable or mongodb://localhost:27017 if missing.\n\t// Used to connect to the mongodb.\n\tDSN string\n)\n\nfunc parse() {\n\tPort = getDefault(\"PORT\", \"8080\")\n\tDSN = getDefault(\"DSN\", \"mongodb://localhost:27017\")\n\n\tlog.Printf(\"• Port=%s\\n\", Port)\n\tlog.Printf(\"• DSN=%s\\n\", DSN)\n}\n\n// Load loads environment variables that are being used across the whole app.\n// Loading from file(s), i.e .env or dev.env\n//\n// Example of a 'dev.env':\n// PORT=8080\n// DSN=mongodb://localhost:27017\n//\n// After `Load` the callers can get an environment variable via `os.Getenv`.\nfunc Load(envFileName string) {\n\tif args := os.Args; len(args) > 1 && args[1] == \"help\" {\n\t\tfmt.Fprintln(os.Stderr, \"https://github.com/kataras/iris/blob/main/_examples/database/mongodb/README.md\")\n\t\tos.Exit(-1)\n\t}\n\n\t// If more than one filename passed with comma separated then load from all\n\t// of these, a env file can be a partial too.\n\tenvFiles := strings.Split(envFileName, \",\")\n\tfor _, envFile := range envFiles {\n\t\tif filepath.Ext(envFile) == \"\" {\n\t\t\tenvFile += \".env\"\n\t\t}\n\n\t\tif fileExists(envFile) {\n\t\t\tlog.Printf(\"Loading environment variables from file: %s\\n\", envFile)\n\n\t\t\tif err := godotenv.Load(envFile); err != nil {\n\t\t\t\tpanic(fmt.Sprintf(\"error loading environment variables from [%s]: %v\", envFile, err))\n\t\t\t}\n\t\t}\n\t}\n\n\t// envMap, _ := godotenv.Read(envFiles...)\n\t// for k, v := range envMap {\n\t// \tlog.Printf(\"◽ %s=%s\\n\", k, v)\n\t// }\n\n\tparse()\n}\n\nfunc getDefault(key string, def string) string {\n\tvalue := os.Getenv(key)\n\tif value == \"\" {\n\t\tos.Setenv(key, def)\n\t\tvalue = def\n\t}\n\n\treturn value\n}\n\nfunc fileExists(filename string) bool {\n\tinfo, err := os.Stat(filename)\n\tif os.IsNotExist(err) {\n\t\treturn false\n\t}\n\treturn !info.IsDir()\n}\n"
  },
  {
    "path": "_examples/database/mongodb/go.mod",
    "content": "module myapp\n\ngo 1.25\n\nrequire (\n\tgithub.com/joho/godotenv v1.5.1\n\tgithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd\n\tgo.mongodb.org/mongo-driver v1.17.6\n)\n\nrequire (\n\tgithub.com/BurntSushi/toml v1.6.0 // indirect\n\tgithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect\n\tgithub.com/CloudyKit/jet/v6 v6.3.1 // indirect\n\tgithub.com/Joker/jade v1.1.3 // indirect\n\tgithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 // indirect\n\tgithub.com/andybalholm/brotli v1.2.0 // indirect\n\tgithub.com/aymerick/douceur v0.2.0 // indirect\n\tgithub.com/fatih/structs v1.1.0 // indirect\n\tgithub.com/flosch/pongo2/v4 v4.0.2 // indirect\n\tgithub.com/golang/snappy v1.0.0 // indirect\n\tgithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a // indirect\n\tgithub.com/google/uuid v1.6.0 // indirect\n\tgithub.com/gorilla/css v1.0.1 // indirect\n\tgithub.com/iris-contrib/schema v0.0.6 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/kataras/blocks v0.0.8 // indirect\n\tgithub.com/kataras/golog v0.1.12 // indirect\n\tgithub.com/kataras/pio v0.0.13 // indirect\n\tgithub.com/kataras/sitemap v0.0.6 // indirect\n\tgithub.com/kataras/tunnel v0.0.4 // indirect\n\tgithub.com/klauspost/compress v1.18.2 // indirect\n\tgithub.com/kr/pretty v0.3.1 // indirect\n\tgithub.com/mailgun/raymond/v2 v2.0.48 // indirect\n\tgithub.com/mailru/easyjson v0.9.1 // indirect\n\tgithub.com/microcosm-cc/bluemonday v1.0.27 // indirect\n\tgithub.com/montanaflynn/stats v0.7.1 // indirect\n\tgithub.com/rogpeppe/go-internal v1.10.0 // indirect\n\tgithub.com/russross/blackfriday/v2 v2.1.0 // indirect\n\tgithub.com/schollz/closestmatch v2.1.0+incompatible // indirect\n\tgithub.com/sirupsen/logrus v1.8.1 // indirect\n\tgithub.com/stretchr/testify v1.11.1 // indirect\n\tgithub.com/tdewolff/minify/v2 v2.24.8 // indirect\n\tgithub.com/tdewolff/parse/v2 v2.8.5 // indirect\n\tgithub.com/valyala/bytebufferpool v1.0.0 // indirect\n\tgithub.com/vmihailenco/msgpack/v5 v5.4.1 // indirect\n\tgithub.com/vmihailenco/tagparser/v2 v2.0.0 // indirect\n\tgithub.com/xdg-go/pbkdf2 v1.0.0 // indirect\n\tgithub.com/xdg-go/scram v1.1.2 // indirect\n\tgithub.com/xdg-go/stringprep v1.0.4 // indirect\n\tgithub.com/yosssi/ace v0.0.5 // indirect\n\tgithub.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect\n\tgolang.org/x/crypto v0.47.0 // indirect\n\tgolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 // indirect\n\tgolang.org/x/net v0.49.0 // indirect\n\tgolang.org/x/sync v0.19.0 // indirect\n\tgolang.org/x/sys v0.40.0 // indirect\n\tgolang.org/x/text v0.33.0 // indirect\n\tgolang.org/x/time v0.14.0 // indirect\n\tgoogle.golang.org/protobuf v1.36.11 // indirect\n\tgopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect\n\tgopkg.in/ini.v1 v1.67.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n"
  },
  {
    "path": "_examples/database/mongodb/go.sum",
    "content": "github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=\ngithub.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=\ngithub.com/CloudyKit/jet/v6 v6.3.1 h1:6IAo5Cx21xrHVaR8zzXN5gJatKV/wO7Nf6bfCnCSbUw=\ngithub.com/CloudyKit/jet/v6 v6.3.1/go.mod h1:lf8ksdNsxZt7/yH/3n4vJQWA9RUq4wpaHtArHhGVMOw=\ngithub.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc=\ngithub.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=\ngithub.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=\ngithub.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 h1:dG7/gLroJGht/jSQtHiLvT48Hxn+crbmvyItZC8cWOs=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05/go.mod h1:NYezi6wtnJtBm5btoprXc5SvAdqH0XTXWnUup0MptAI=\ngithub.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=\ngithub.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=\ngithub.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=\ngithub.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=\ngithub.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=\ngithub.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=\ngithub.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=\ngithub.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=\ngithub.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=\ngithub.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw=\ngithub.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8=\ngithub.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=\ngithub.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=\ngithub.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=\ngithub.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a h1:l7A0loSszR5zHd/qK53ZIHMO8b3bBSmENnQ6eKnUT0A=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=\ngithub.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=\ngithub.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=\ngithub.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=\ngithub.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=\ngithub.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2 h1:T9THsdP1woyAqKHwjkEsbCnMefsAFvk8iJJKokcJ3Go=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2/go.mod h1:JLDgIqnFy5loDSUv1OA2j0mb6p/rDhiCqigP22Uq9xE=\ngithub.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw=\ngithub.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA=\ngithub.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=\ngithub.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/kataras/blocks v0.0.8 h1:MrpVhoFTCR2v1iOOfGng5VJSILKeZZI+7NGfxEh3SUM=\ngithub.com/kataras/blocks v0.0.8/go.mod h1:9Jm5zx6BB+06NwA+OhTbHW1xkMOYxahnqTN5DveZ2Yg=\ngithub.com/kataras/golog v0.1.12 h1:Bu7I/G4ilJlbfzjmU39O9N+2uO1pBcMK045fzZ4ytNg=\ngithub.com/kataras/golog v0.1.12/go.mod h1:wrGSbOiBqbQSQznleVNX4epWM8rl9SJ/rmEacl0yqy4=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd h1:oMsQgqKACbUdlaIWnN+EZzzAnHkw90hE/y/R0BSij2U=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd/go.mod h1:yucjtDl9Vn3OctWM1fi0MuM9OckemC7kEreFa9QtPF8=\ngithub.com/kataras/pio v0.0.13 h1:x0rXVX0fviDTXOOLOmr4MUxOabu1InVSTu5itF8CXCM=\ngithub.com/kataras/pio v0.0.13/go.mod h1:k3HNuSw+eJ8Pm2lA4lRhg3DiCjVgHlP8hmXApSej3oM=\ngithub.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY=\ngithub.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4=\ngithub.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA=\ngithub.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw=\ngithub.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=\ngithub.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=\ngithub.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=\ngithub.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8=\ngithub.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=\ngithub.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=\ngithub.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=\ngithub.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=\ngithub.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=\ngithub.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=\ngithub.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=\ngithub.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=\ngithub.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=\ngithub.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=\ngithub.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=\ngithub.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=\ngithub.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=\ngithub.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=\ngithub.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk=\ngithub.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=\ngithub.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=\ngithub.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=\ngithub.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=\ngithub.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/tdewolff/minify/v2 v2.24.8 h1:58/VjsbevI4d5FGV0ZSuBrHMSSkH4MCH0sIz/eKIauE=\ngithub.com/tdewolff/minify/v2 v2.24.8/go.mod h1:0Ukj0CRpo/sW/nd8uZ4ccXaV1rEVIWA3dj8U7+Shhfw=\ngithub.com/tdewolff/parse/v2 v2.8.5 h1:ZmBiA/8Do5Rpk7bDye0jbbDUpXXbCdc3iah4VeUvwYU=\ngithub.com/tdewolff/parse/v2 v2.8.5/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=\ngithub.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE=\ngithub.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=\ngithub.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=\ngithub.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=\ngithub.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=\ngithub.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=\ngithub.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=\ngithub.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=\ngithub.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=\ngithub.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=\ngithub.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=\ngithub.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=\ngithub.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=\ngithub.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=\ngithub.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA=\ngithub.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0=\ngithub.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=\ngithub.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=\ngithub.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=\ngithub.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=\ngithub.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngithub.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=\ngo.mongodb.org/mongo-driver v1.17.6 h1:87JUG1wZfWsr6rIz3ZmpH90rL5tea7O3IHuSwHUpsss=\ngo.mongodb.org/mongo-driver v1.17.6/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=\ngolang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=\ngolang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=\ngolang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=\ngolang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=\ngolang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=\ngolang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=\ngolang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=\ngolang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=\ngolang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=\ngolang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=\ngolang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=\ngolang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=\ngolang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=\ngoogle.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=\ngopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nmoul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs=\nmoul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE=\n"
  },
  {
    "path": "_examples/database/mongodb/httputil/error.go",
    "content": "package httputil\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"runtime\"\n\t\"runtime/debug\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nvar validStackFuncs = []func(string) bool{\n\tfunc(file string) bool {\n\t\treturn strings.Contains(file, \"/mongodb/api/\")\n\t},\n}\n\n// RuntimeCallerStack returns the app's `file:line` stacktrace\n// to give more information about an error cause.\nfunc RuntimeCallerStack() (s string) {\n\tvar pcs [10]uintptr\n\tn := runtime.Callers(1, pcs[:])\n\tframes := runtime.CallersFrames(pcs[:n])\n\n\tfor {\n\t\tframe, more := frames.Next()\n\t\tfor _, fn := range validStackFuncs {\n\t\t\tif fn(frame.File) {\n\t\t\t\ts += fmt.Sprintf(\"\\n\\t\\t\\t%s:%d\", frame.File, frame.Line)\n\t\t\t}\n\t\t}\n\n\t\tif !more {\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn s\n}\n\n// HTTPError describes an HTTP error.\ntype HTTPError struct {\n\terror\n\tStack       string    `json:\"-\"` // the whole stacktrace.\n\tCallerStack string    `json:\"-\"` // the caller, file:lineNumber\n\tWhen        time.Time `json:\"-\"` // the time that the error occurred.\n\t// ErrorCode int: maybe a collection of known error codes.\n\tStatusCode int `json:\"statusCode\"`\n\t// could be named as \"reason\" as well\n\t//  it's the message of the error.\n\tDescription string `json:\"description\"`\n}\n\nfunc newError(statusCode int, err error, format string, args ...any) HTTPError {\n\tif format == \"\" {\n\t\tformat = http.StatusText(statusCode)\n\t}\n\n\tdesc := fmt.Sprintf(format, args...)\n\tif err == nil {\n\t\terr = errors.New(desc)\n\t}\n\n\treturn HTTPError{\n\t\terr,\n\t\tstring(debug.Stack()),\n\t\tRuntimeCallerStack(),\n\t\ttime.Now(),\n\t\tstatusCode,\n\t\tdesc,\n\t}\n}\n\nfunc (err HTTPError) writeHeaders(ctx iris.Context) {\n\tctx.StatusCode(err.StatusCode)\n\tctx.Header(\"X-Content-Type-Options\", \"nosniff\")\n}\n\n// LogFailure will print out the failure to the \"logger\".\nfunc LogFailure(logger io.Writer, ctx iris.Context, err HTTPError) {\n\ttimeFmt := err.When.Format(\"2006/01/02 15:04:05\")\n\tfirstLine := fmt.Sprintf(\"%s %s: %s\", timeFmt, http.StatusText(err.StatusCode), err.Error())\n\twhitespace := strings.Repeat(\" \", len(timeFmt)+1)\n\tfmt.Fprintf(logger, \"%s\\n%sIP: %s\\n%sURL: %s\\n%sSource: %s\\n\",\n\t\tfirstLine, whitespace, ctx.RemoteAddr(), whitespace, ctx.FullRequestURI(), whitespace, err.CallerStack)\n}\n\n// Fail will send the status code, write the error's reason\n// and return the HTTPError for further use, i.e logging, see `InternalServerError`.\nfunc Fail(ctx iris.Context, statusCode int, err error, format string, args ...any) HTTPError {\n\thttpErr := newError(statusCode, err, format, args...)\n\thttpErr.writeHeaders(ctx)\n\n\tctx.WriteString(httpErr.Description)\n\treturn httpErr\n}\n\n// FailJSON will send to the client the error data as JSON.\n// Useful for APIs.\nfunc FailJSON(ctx iris.Context, statusCode int, err error, format string, args ...any) HTTPError {\n\thttpErr := newError(statusCode, err, format, args...)\n\thttpErr.writeHeaders(ctx)\n\n\tctx.JSON(httpErr)\n\n\treturn httpErr\n}\n\n// InternalServerError logs to the server's terminal\n// and dispatches to the client the 500 Internal Server Error.\n// Internal Server errors are critical, so we log them to the `os.Stderr`.\nfunc InternalServerError(ctx iris.Context, err error, format string, args ...any) {\n\tLogFailure(os.Stderr, ctx, Fail(ctx, iris.StatusInternalServerError, err, format, args...))\n}\n\n// InternalServerErrorJSON acts exactly like `InternalServerError` but instead it sends the data as JSON.\n// Useful for APIs.\nfunc InternalServerErrorJSON(ctx iris.Context, err error, format string, args ...any) {\n\tLogFailure(os.Stderr, ctx, FailJSON(ctx, iris.StatusInternalServerError, err, format, args...))\n}\n\n// UnauthorizedJSON sends JSON format of StatusUnauthorized(401) HTTPError value.\nfunc UnauthorizedJSON(ctx iris.Context, err error, format string, args ...any) HTTPError {\n\treturn FailJSON(ctx, iris.StatusUnauthorized, err, format, args...)\n}\n"
  },
  {
    "path": "_examples/database/mongodb/main.go",
    "content": "package main\n\n// go get -u go.mongodb.org/mongo-driver\n// go get -u github.com/joho/godotenv\n\nimport (\n\t\"context\"\n\t\"flag\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\t// APIs\n\tstoreapi \"myapp/api/store\"\n\n\t//\n\t\"myapp/env\"\n\t\"myapp/store\"\n\n\t\"github.com/kataras/iris/v12\"\n\n\t\"go.mongodb.org/mongo-driver/mongo\"\n\t\"go.mongodb.org/mongo-driver/mongo/options\"\n)\n\nconst version = \"0.0.1\"\n\nfunc init() {\n\tenvFileName := \".env\"\n\n\tflagset := flag.CommandLine\n\tflagset.StringVar(&envFileName, \"env\", envFileName, \"the env file which web app will use to extract its environment variables\")\n\tflagset.Parse(os.Args[1:])\n\n\tenv.Load(envFileName)\n}\n\nfunc main() {\n\tclientOptions := options.Client().SetHosts([]string{env.DSN})\n\tclient, err := mongo.Connect(context.Background(), clientOptions)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\terr = client.Ping(context.Background(), nil)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tdefer client.Disconnect(context.TODO())\n\n\tdb := client.Database(\"store\")\n\n\tvar (\n\t\t// Collections.\n\t\tmoviesCollection = db.Collection(\"movies\")\n\n\t\t// Services.\n\t\tmovieService = store.NewMovieService(moviesCollection)\n\t)\n\n\tapp := iris.New()\n\tapp.Use(func(ctx iris.Context) {\n\t\tctx.Header(\"Server\", \"Iris MongoDB/\"+version)\n\t\tctx.Next()\n\t})\n\n\tstoreAPI := app.Party(\"/api/store\")\n\t{\n\t\tmovieHandler := storeapi.NewMovieHandler(movieService)\n\t\tstoreAPI.Get(\"/movies\", movieHandler.GetAll)\n\t\tstoreAPI.Post(\"/movies\", movieHandler.Add)\n\t\tstoreAPI.Get(\"/movies/{id}\", movieHandler.Get)\n\t\tstoreAPI.Put(\"/movies/{id}\", movieHandler.Update)\n\t\tstoreAPI.Delete(\"/movies/{id}\", movieHandler.Delete)\n\t}\n\n\t// GET: http://localhost:8080/api/store/movies\n\t// POST: http://localhost:8080/api/store/movies\n\t// GET: http://localhost:8080/api/store/movies/{id}\n\t// PUT: http://localhost:8080/api/store/movies/{id}\n\t// DELETE: http://localhost:8080/api/store/movies/{id}\n\tapp.Listen(fmt.Sprintf(\":%s\", env.Port), iris.WithOptimizations)\n}\n"
  },
  {
    "path": "_examples/database/mongodb/store/movie.go",
    "content": "package store\n\nimport (\n\t\"context\"\n\t\"errors\"\n\n\t\"go.mongodb.org/mongo-driver/bson\"\n\t\"go.mongodb.org/mongo-driver/bson/primitive\"\n\t\"go.mongodb.org/mongo-driver/mongo\"\n\t// up to you:\n\t// \"go.mongodb.org/mongo-driver/mongo/options\"\n)\n\ntype Movie struct {\n\tID          primitive.ObjectID `json:\"_id\" bson:\"_id\"` /* you need the bson:\"_id\" to be able to retrieve with ID filled */\n\tName        string             `json:\"name\"`\n\tCover       string             `json:\"cover\"`\n\tDescription string             `json:\"description\"`\n}\n\ntype MovieService interface {\n\tGetAll(ctx context.Context) ([]Movie, error)\n\tGetByID(ctx context.Context, id string) (Movie, error)\n\tCreate(ctx context.Context, m *Movie) error\n\tUpdate(ctx context.Context, id string, m Movie) error\n\tDelete(ctx context.Context, id string) error\n}\n\ntype movieService struct {\n\tC *mongo.Collection\n}\n\nvar _ MovieService = (*movieService)(nil)\n\nfunc NewMovieService(collection *mongo.Collection) MovieService {\n\t// up to you:\n\t// indexOpts := new(options.IndexOptions)\n\t// indexOpts.SetName(\"movieIndex\").\n\t// \tSetUnique(true).\n\t// \tSetBackground(true).\n\t// \tSetSparse(true)\n\n\t// collection.Indexes().CreateOne(context.Background(), mongo.IndexModel{\n\t// \tKeys:    []string{\"_id\", \"name\"},\n\t// \tOptions: indexOpts,\n\t// })\n\n\treturn &movieService{C: collection}\n}\n\nfunc (s *movieService) GetAll(ctx context.Context) ([]Movie, error) {\n\t// Note:\n\t// The mongodb's go-driver's docs says that you can pass `nil` to \"find all\" but this gives NilDocument error,\n\t// probably it's a bug or a documentation's mistake, you have to pass `bson.D{}` instead.\n\tcur, err := s.C.Find(ctx, bson.D{})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer cur.Close(ctx)\n\n\tvar results []Movie\n\n\tfor cur.Next(ctx) {\n\t\tif err = cur.Err(); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\t//\telem := bson.D{}\n\t\tvar elem Movie\n\t\terr = cur.Decode(&elem)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\t// results = append(results, Movie{ID: elem[0].Value.(primitive.ObjectID)})\n\n\t\tresults = append(results, elem)\n\t}\n\n\treturn results, nil\n}\n\nfunc matchID(id string) (bson.D, error) {\n\tobjectID, err := primitive.ObjectIDFromHex(id)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tfilter := bson.D{{Key: \"_id\", Value: objectID}}\n\treturn filter, nil\n}\n\nvar ErrNotFound = errors.New(\"not found\")\n\nfunc (s *movieService) GetByID(ctx context.Context, id string) (Movie, error) {\n\tvar movie Movie\n\tfilter, err := matchID(id)\n\tif err != nil {\n\t\treturn movie, err\n\t}\n\n\terr = s.C.FindOne(ctx, filter).Decode(&movie)\n\tif err == mongo.ErrNoDocuments {\n\t\treturn movie, ErrNotFound\n\t}\n\treturn movie, err\n}\n\nfunc (s *movieService) Create(ctx context.Context, m *Movie) error {\n\tif m.ID.IsZero() {\n\t\tm.ID = primitive.NewObjectID()\n\t}\n\n\t_, err := s.C.InsertOne(ctx, m)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// The following doesn't work if you have the `bson:\"_id` on Movie.ID field,\n\t// therefore we manually generate a new ID (look above).\n\t// res, err := ...InsertOne\n\t// objectID := res.InsertedID.(primitive.ObjectID)\n\t// m.ID = objectID\n\treturn nil\n}\n\nfunc (s *movieService) Update(ctx context.Context, id string, m Movie) error {\n\tfilter, err := matchID(id)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// update := bson.D{\n\t// \t{Key: \"$set\", Value: m},\n\t// }\n\t// ^ this will override all fields, you can do that, depending on your design. but let's check each field:\n\telem := bson.D{}\n\n\tif m.Name != \"\" {\n\t\telem = append(elem, bson.E{Key: \"name\", Value: m.Name})\n\t}\n\n\tif m.Description != \"\" {\n\t\telem = append(elem, bson.E{Key: \"description\", Value: m.Description})\n\t}\n\n\tif m.Cover != \"\" {\n\t\telem = append(elem, bson.E{Key: \"cover\", Value: m.Cover})\n\t}\n\n\tupdate := bson.D{\n\t\t{Key: \"$set\", Value: elem},\n\t}\n\n\t_, err = s.C.UpdateOne(ctx, filter, update)\n\tif err != nil {\n\t\tif err == mongo.ErrNoDocuments {\n\t\t\treturn ErrNotFound\n\t\t}\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (s *movieService) Delete(ctx context.Context, id string) error {\n\tfilter, err := matchID(id)\n\tif err != nil {\n\t\treturn err\n\t}\n\t_, err = s.C.DeleteOne(ctx, filter)\n\tif err != nil {\n\t\tif err == mongo.ErrNoDocuments {\n\t\t\treturn ErrNotFound\n\t\t}\n\t\treturn err\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "_examples/database/mysql/Dockerfile",
    "content": "# docker build -t myapp . \n# docker run --rm -it -p 8080:8080 myapp:latest\nFROM golang:latest AS builder\nRUN apt-get update\nENV GO111MODULE=on \\\n    CGO_ENABLED=0 \\\n    GOOS=linux \\\n    GOARCH=amd64\nWORKDIR /go/src/app\nCOPY go.mod .\nRUN go mod download\nCOPY . .\nRUN go install\n\nFROM scratch\nCOPY --from=builder /go/bin/myapp .\nENTRYPOINT [\"./myapp\"]"
  },
  {
    "path": "_examples/database/mysql/README.md",
    "content": "# Iris, MySQL, Groupcache & Docker Example\n\n## 📘 Endpoints\n\n| Method | Path                | Description            | URL Parameters | Body                       | Auth Required |\n|--------|---------------------|------------------------|--------------- |----------------------------|---------------|\n| ANY    | /token              | Prints a new JWT Token | -              | -                          | -             |\n| GET    | /category           | Lists a set of Categories    | offset, limit, order | -                    | Token         |\n| POST   | /category           | Creates a Category      | -              | JSON [Full Category](migration/api_category/create_category.json)              | Token      |\n| PUT    | /category           | Fully-Updates a Category | -              | JSON [Full Category](migration/api_category/update_category.json)              | Token      |\n| PATCH  | /category/{id}      | Partially-Updates a Category | -              | JSON [Partial Category](migration/api_category/update_partial_category.json)              | Token      |\n| GET    | /category/{id}      | Prints a Category         | -              | -      | Token      |\n| DELETE | /category/{id}      | Deletes a Category      | -              | -      | Token      |\n| GET    | /category/{id}/products | Lists all Products from a Category      | offset, limit, order | -      | Token      |\n| POST   | /category/{id}/products | (Batch) Assigns one or more Products to a Category      | -              | JSON [Products](migration/api_category/insert_products_category.json)      | Token      |\n| GET    | /product           | Lists a set of Products (cache)     | offset, limit, order | -                    | Token      |\n| POST   | /product           | Creates a Product      | -              | JSON [Full Product](migration/api_product/create_product.json)              | Token      |\n| PUT    | /product           | Fully-Updates a Product | -              | JSON [Full Product](migration/api_product/update_product.json)              | Token      |\n| PATCH  | /product/{id}      | Partially-Updates a Product | -              | JSON [Partial Product](migration/api_product/update_partial_product.json)              | Token      |\n| GET    | /product/{id}      | Prints a Product (cache)         | -              | -      | Token      |\n| DELETE | /product/{id}      | Deletes a Product        | -              | -      | Token      |\n\n\n\n## 📑 Responses\n\n* **Content-Type** of `\"application/json;charset=utf-8\"`, snake_case naming (identical to the database columns)\n* **Status Codes**\n    * 500 for server(db) errors,\n    * 422 for validation errors, e.g.\n    ```json\n    {\n        \"code\": 422,\n        \"message\": \"required fields are missing\",\n        \"timestamp\": 1589306271\n    }\n    ```\n    * 400 for malformed syntax, e.g.\n    ```json\n    {\n        \"code\": 400,\n        \"message\": \"json: cannot unmarshal number -2 into Go struct field Category.position of type uint64\",\n        \"timestamp\": 1589306325\n    }\n    ```\n    ```json\n    {\n        \"code\": 400,\n        \"message\": \"json: unknown field \\\"field_not_exists\\\"\",\n        \"timestamp\": 1589306367\n    }\n    ```\n    * 404 for entity not found, e.g.\n    ```json\n    {\n        \"code\": 404,\n        \"message\": \"entity does not exist\",\n        \"timestamp\": 1589306199\n    }\n    ```\n    * 304 for unaffected UPDATE or DELETE,\n    * 201 for CREATE with the last inserted ID,\n    * 200 for GET, UPDATE and DELETE\n\n## ⚡ Get Started\n\nDownload the folder.\n\n### Install (Docker)\n\nInstall [Docker](https://www.docker.com/) and execute the command below\n\n```sh\n$ docker-compose up --build\n```\n\n### Install (Manually)\n\nRun `go build -mod=mod` or `go run -mod=mod main.go` and read below.\n\n#### MySQL\n\nEnvironment variables:\n\n```sh\nMYSQL_USER=user_myapp\nMYSQL_PASSWORD=dbpassword\nMYSQL_HOST=localhost\nMYSQL_DATABASE=myapp\n```\n\nDownload the schema from [migration/db.sql](migration/db.sql) and execute it against your MySQL server instance.\n\n```sql\nCREATE DATABASE IF NOT EXISTS myapp DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;\n\nUSE myapp;\n\nSET NAMES utf8mb4;\nSET FOREIGN_KEY_CHECKS = 0;\n\nDROP TABLE IF EXISTS categories;\nCREATE TABLE categories  (\n  id int(11) NOT NULL AUTO_INCREMENT,\n  title varchar(255) NOT NULL,\n  position int(11) NOT NULL,\n  image_url varchar(255) NOT NULL,\n  created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (id)\n);\n\nDROP TABLE IF EXISTS products;\nCREATE TABLE products  (\n  id int(11) NOT NULL AUTO_INCREMENT,\n  category_id int,\n  title varchar(255) NOT NULL,\n  image_url varchar(255) NOT NULL,\n  price decimal(10,2) NOT NULL,\n  description text NOT NULL,\n  created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (id),\n  FOREIGN KEY (category_id) REFERENCES categories(id)\n);\n\nSET FOREIGN_KEY_CHECKS = 1;\n```\n\n### Requests\n\nSome request bodies can be found at: [migration/api_category](migration/api_category) and [migration/api_product](migration/api_product). **However** I've provided a [postman.json](migration/myapp_postman.json) Collection that you can import to your [POSTMAN](https://learning.postman.com/docs/postman/collections/importing-and-exporting-data/#collections) and start playing with the API.\n\nAll write-access endpoints are \"protected\" via JWT, a client should \"verify\" itself. You'll need to manually take the **token** from the `http://localhost:8080/token` and put it on url parameter `?token=$token` or to the `Authentication: Bearer $token` request header.\n\n### Unit or End-To-End Testing?\n\nTesting is important. The code is written in a way that testing should be trivial (Pseudo/memory Database or SQLite local file could be integrated as well, for end-to-end tests a Docker image with MySQL and fire tests against that server). However, there is [nothing(?)](service/category_service_test.go) to see here.\n\n## Packages\n\n- https://github.com/kataras/jwt (JWT parsing)\n- https://github.com/go-sql-driver/mysql (Go Driver for MySQL)\n- https://github.com/DATA-DOG/go-sqlmock (Testing DB see [service/category_service_test.go](service/category_service_test.go))\n- https://github.com/kataras/iris (HTTP)\n- https://github.com/mailgun/groupcache (Caching)\n"
  },
  {
    "path": "_examples/database/mysql/api/api.go",
    "content": "// Package api contains the handlers for our HTTP Endpoints.\npackage api\n\nimport (\n\t\"time\"\n\n\t\"myapp/service\"\n\t\"myapp/sql\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/middleware/jwt\"\n\t\"github.com/kataras/iris/v12/middleware/requestid\"\n)\n\n// Router accepts any required dependencies and returns the main server's handler.\nfunc Router(db sql.Database, secret string) func(iris.Party) {\n\treturn func(r iris.Party) {\n\t\tr.Use(requestid.New())\n\n\t\tsigner := jwt.NewSigner(jwt.HS256, secret, 15*time.Minute)\n\t\tr.Get(\"/token\", writeToken(signer))\n\n\t\tverify := jwt.NewVerifier(jwt.HS256, secret).Verify(nil)\n\t\tr.Use(verify)\n\t\t// Generate a token for testing by navigating to\n\t\t// http://localhost:8080/token endpoint.\n\t\t// Copy-paste it to a ?token=$token url parameter or\n\t\t// open postman and put an Authentication: Bearer $token to get\n\t\t// access on create, update and delete endpoinds.\n\n\t\tvar (\n\t\t\tcategoryService = service.NewCategoryService(db)\n\t\t\tproductService  = service.NewProductService(db)\n\t\t)\n\n\t\tcat := r.Party(\"/category\")\n\t\t{\n\t\t\t// TODO: new Use to add middlewares to specific\n\t\t\t// routes per METHOD ( we already have the per path through parties.)\n\t\t\thandler := NewCategoryHandler(categoryService)\n\n\t\t\tcat.Get(\"/\", handler.List)\n\t\t\tcat.Post(\"/\", handler.Create)\n\t\t\tcat.Put(\"/\", handler.Update)\n\n\t\t\tcat.Get(\"/{id:int64}\", handler.GetByID)\n\t\t\tcat.Patch(\"/{id:int64}\", handler.PartialUpdate)\n\t\t\tcat.Delete(\"/{id:int64}\", handler.Delete)\n\t\t\t/* You can also do something like that:\n\t\t\tcat.PartyFunc(\"/{id:int64}\", func(c iris.Party) {\n\t\t\t\tc.Get(\"/\", handler.GetByID)\n\t\t\t\tc.Post(\"/\", handler.PartialUpdate)\n\t\t\t\tc.Delete(\"/\", handler.Delete)\n\t\t\t})\n\t\t\t*/\n\n\t\t\tcat.Get(\"/{id:int64}/products\", handler.ListProducts)\n\t\t\tcat.Post(\"/{id:int64}/products\", handler.InsertProducts(productService))\n\t\t}\n\n\t\tprod := r.Party(\"/product\")\n\t\t{\n\t\t\thandler := NewProductHandler(productService)\n\n\t\t\tprod.Get(\"/\", handler.List)\n\t\t\tprod.Post(\"/\", handler.Create)\n\t\t\tprod.Put(\"/\", handler.Update)\n\n\t\t\tprod.Get(\"/{id:int64}\", handler.GetByID)\n\t\t\tprod.Patch(\"/{id:int64}\", handler.PartialUpdate)\n\t\t\tprod.Delete(\"/{id:int64}\", handler.Delete)\n\t\t}\n\n\t}\n}\n\nfunc writeToken(signer *jwt.Signer) iris.Handler {\n\treturn func(ctx iris.Context) {\n\t\tclaims := jwt.Claims{\n\t\t\tIssuer:   \"https://iris-go.com\",\n\t\t\tAudience: []string{requestid.Get(ctx)},\n\t\t}\n\n\t\ttoken, err := signer.Sign(claims)\n\t\tif err != nil {\n\t\t\tctx.StopWithStatus(iris.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\n\t\tctx.Write(token)\n\t}\n}\n"
  },
  {
    "path": "_examples/database/mysql/api/category_handler.go",
    "content": "package api\n\nimport (\n\t\"myapp/entity\"\n\t\"myapp/service\"\n\t\"myapp/sql\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\n// CategoryHandler is the http mux for categories.\ntype CategoryHandler struct {\n\t// [...options]\n\n\tservice *service.CategoryService\n}\n\n// NewCategoryHandler returns the main controller for the categories API.\nfunc NewCategoryHandler(service *service.CategoryService) *CategoryHandler {\n\treturn &CategoryHandler{service}\n}\n\n// GetByID fetches a single record from the database and sends it to the client.\n// Method: GET.\nfunc (h *CategoryHandler) GetByID(ctx iris.Context) {\n\tid := ctx.Params().GetInt64Default(\"id\", 0)\n\n\tvar cat entity.Category\n\terr := h.service.GetByID(ctx.Request().Context(), &cat, id)\n\tif err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\twriteEntityNotFound(ctx)\n\t\t\treturn\n\t\t}\n\n\t\tdebugf(\"CategoryHandler.GetByID(id=%d): %v\", id, err)\n\t\twriteInternalServerError(ctx)\n\t\treturn\n\t}\n\n\tctx.JSON(cat)\n}\n\n/*\n\ntype (\n\tList struct {\n\t\tData  any `json:\"data\"`\n\t\tOrder string      `json:\"order\"`\n\t\tNext  Range       `json:\"next,omitempty\"`\n\t\tPrev  Range       `json:\"prev,omitempty\"`\n\t}\n\n\tRange struct {\n\t\tOffset int64 `json:\"offset\"`\n\t\tLimit  int64 `json:\"limit`\n\t}\n)\n*/\n\n// List lists a set of records from the database.\n// Method: GET.\nfunc (h *CategoryHandler) List(ctx iris.Context) {\n\tq := ctx.Request().URL.Query()\n\topts := sql.ParseListOptions(q)\n\n\t// initialize here in order to return an empty json array `[]` instead of `null`.\n\tcategories := entity.Categories{}\n\terr := h.service.List(ctx.Request().Context(), &categories, opts)\n\tif err != nil && err != sql.ErrNoRows {\n\t\tdebugf(\"CategoryHandler.List(DB) (limit=%d offset=%d where=%s=%v): %v\",\n\t\t\topts.Limit, opts.Offset, opts.WhereColumn, opts.WhereValue, err)\n\n\t\twriteInternalServerError(ctx)\n\t\treturn\n\t}\n\n\tctx.JSON(categories)\n}\n\n// Create adds a record to the database.\n// Method: POST.\nfunc (h *CategoryHandler) Create(ctx iris.Context) {\n\tvar cat entity.Category\n\tif err := ctx.ReadJSON(&cat); err != nil {\n\t\treturn\n\t}\n\n\tid, err := h.service.Insert(ctx.Request().Context(), cat)\n\tif err != nil {\n\t\tif err == sql.ErrUnprocessable {\n\t\t\tctx.StopWithJSON(iris.StatusUnprocessableEntity, newError(iris.StatusUnprocessableEntity, ctx.Request().Method, ctx.Path(), \"required fields are missing\"))\n\t\t\treturn\n\t\t}\n\n\t\tdebugf(\"CategoryHandler.Create(DB): %v\", err)\n\t\twriteInternalServerError(ctx)\n\t\treturn\n\t}\n\n\t// Send 201 with body of {\"id\":$last_inserted_id\"}.\n\tctx.StatusCode(iris.StatusCreated)\n\tctx.JSON(iris.Map{cat.PrimaryKey(): id})\n}\n\n// Update performs a full-update of a record in the database.\n// Method: PUT.\nfunc (h *CategoryHandler) Update(ctx iris.Context) {\n\tvar cat entity.Category\n\tif err := ctx.ReadJSON(&cat); err != nil {\n\t\treturn\n\t}\n\n\taffected, err := h.service.Update(ctx.Request().Context(), cat)\n\tif err != nil {\n\t\tif err == sql.ErrUnprocessable {\n\t\t\tctx.StopWithJSON(iris.StatusUnprocessableEntity, newError(iris.StatusUnprocessableEntity, ctx.Request().Method, ctx.Path(), \"required fields are missing\"))\n\t\t\treturn\n\t\t}\n\n\t\tdebugf(\"CategoryHandler.Update(DB): %v\", err)\n\t\twriteInternalServerError(ctx)\n\t\treturn\n\t}\n\n\tstatus := iris.StatusOK\n\tif affected == 0 {\n\t\tstatus = iris.StatusNotModified\n\t}\n\n\tctx.StatusCode(status)\n}\n\n// PartialUpdate is the handler for partially update one or more fields of the record.\n// Method: PATCH.\nfunc (h *CategoryHandler) PartialUpdate(ctx iris.Context) {\n\tid := ctx.Params().GetInt64Default(\"id\", 0)\n\n\tvar attrs map[string]any\n\tif err := ctx.ReadJSON(&attrs); err != nil {\n\t\treturn\n\t}\n\n\taffected, err := h.service.PartialUpdate(ctx.Request().Context(), id, attrs)\n\tif err != nil {\n\t\tif err == sql.ErrUnprocessable {\n\t\t\tctx.StopWithJSON(iris.StatusUnprocessableEntity, newError(iris.StatusUnprocessableEntity, ctx.Request().Method, ctx.Path(), \"unsupported value(s)\"))\n\t\t\treturn\n\t\t}\n\n\t\tdebugf(\"CategoryHandler.PartialUpdate(DB): %v\", err)\n\t\twriteInternalServerError(ctx)\n\t\treturn\n\t}\n\n\tstatus := iris.StatusOK\n\tif affected == 0 {\n\t\tstatus = iris.StatusNotModified\n\t}\n\n\tctx.StatusCode(status)\n}\n\n// Delete removes a record from the database.\n// Method: DELETE.\nfunc (h *CategoryHandler) Delete(ctx iris.Context) {\n\tid := ctx.Params().GetInt64Default(\"id\", 0)\n\n\taffected, err := h.service.DeleteByID(ctx.Request().Context(), id)\n\tif err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\twriteEntityNotFound(ctx)\n\t\t\treturn\n\t\t}\n\n\t\tdebugf(\"CategoryHandler.Delete(DB): %v\", err)\n\t\twriteInternalServerError(ctx)\n\t\treturn\n\t}\n\n\tstatus := iris.StatusOK // StatusNoContent\n\tif affected == 0 {\n\t\tstatus = iris.StatusNotModified\n\t}\n\n\tctx.StatusCode(status)\n}\n\n// Products.\n\n// ListProducts lists products of a Category.\n// Example: from cheap to expensive:\n// http://localhost:8080/category/3/products?offset=0&limit=30&by=price&order=asc\n// Method: GET.\nfunc (h *CategoryHandler) ListProducts(ctx iris.Context) {\n\tid := ctx.Params().GetInt64Default(\"id\", 0)\n\n\t// NOTE: could add cache here too.\n\n\tq := ctx.Request().URL.Query()\n\topts := sql.ParseListOptions(q).Where(\"category_id\", id)\n\topts.Table = \"products\"\n\tif opts.OrderByColumn == \"\" {\n\t\topts.OrderByColumn = \"updated_at\"\n\t}\n\n\tvar products entity.Products\n\terr := h.service.List(ctx.Request().Context(), &products, opts)\n\tif err != nil && err != sql.ErrNoRows {\n\t\tdebugf(\"CategoryHandler.ListProducts(DB) (table=%s where=%s=%v limit=%d offset=%d): %v\",\n\t\t\topts.Table, opts.WhereColumn, opts.WhereValue, opts.Limit, opts.Offset, err)\n\n\t\twriteInternalServerError(ctx)\n\t\treturn\n\t}\n\n\tctx.JSON(products)\n}\n\n// InsertProducts assigns new products to a Category (accepts a list of products).\n// Method: POST.\nfunc (h *CategoryHandler) InsertProducts(productService *service.ProductService) iris.Handler {\n\treturn func(ctx iris.Context) {\n\t\tcategoryID := ctx.Params().GetInt64Default(\"id\", 0)\n\n\t\tvar products []entity.Product\n\t\tif err := ctx.ReadJSON(&products); err != nil {\n\t\t\treturn\n\t\t}\n\n\t\tfor i := range products {\n\t\t\tproducts[i].CategoryID = categoryID\n\t\t}\n\n\t\tinserted, err := productService.BatchInsert(ctx.Request().Context(), products)\n\t\tif err != nil {\n\t\t\tif err == sql.ErrUnprocessable {\n\t\t\t\tctx.StopWithJSON(iris.StatusUnprocessableEntity, newError(iris.StatusUnprocessableEntity, ctx.Request().Method, ctx.Path(), \"required fields are missing\"))\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tdebugf(\"CategoryHandler.InsertProducts(DB): %v\", err)\n\t\t\twriteInternalServerError(ctx)\n\t\t\treturn\n\t\t}\n\n\t\tif inserted == 0 {\n\t\t\tctx.StatusCode(iris.StatusNotModified)\n\t\t\treturn\n\t\t}\n\n\t\t// Send 201 with body of {\"inserted\":$inserted\"}.\n\t\tctx.StatusCode(iris.StatusCreated)\n\t\tctx.JSON(iris.Map{\"inserted\": inserted})\n\t}\n}\n"
  },
  {
    "path": "_examples/database/mysql/api/helper.go",
    "content": "package api\n\nimport (\n\t\"log\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nconst debug = true\n\nfunc debugf(format string, args ...any) {\n\tif !debug {\n\t\treturn\n\t}\n\n\tlog.Printf(format, args...)\n}\n\nfunc writeInternalServerError(ctx iris.Context) {\n\tctx.StopWithJSON(iris.StatusInternalServerError, newError(iris.StatusInternalServerError, ctx.Request().Method, ctx.Path(), \"\"))\n}\n\nfunc writeEntityNotFound(ctx iris.Context) {\n\tctx.StopWithJSON(iris.StatusNotFound, newError(iris.StatusNotFound, ctx.Request().Method, ctx.Path(), \"entity does not exist\"))\n}\n"
  },
  {
    "path": "_examples/database/mysql/api/httperror.go",
    "content": "package api\n\nimport (\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\n// Error holds the error sent by server to clients (JSON).\ntype Error struct {\n\tStatusCode int    `json:\"code\"`\n\tMethod     string `json:\"-\"`\n\tPath       string `json:\"-\"`\n\tMessage    string `json:\"message\"`\n\tTimestamp  int64  `json:\"timestamp\"`\n}\n\nfunc newError(statusCode int, method, path, format string, args ...any) Error {\n\tmsg := format\n\tif len(args) > 0 {\n\t\t// why we check for that? If the original error message came from our database\n\t\t// and it contains fmt-reserved words\n\t\t// like %s or %d we will get MISSING(=...) in our error message and we don't want that.\n\t\tmsg = fmt.Sprintf(msg, args...)\n\t}\n\n\tif msg == \"\" {\n\t\tmsg = iris.StatusText(statusCode)\n\t}\n\n\treturn Error{\n\t\tStatusCode: statusCode,\n\t\tMethod:     method,\n\t\tPath:       path,\n\t\tMessage:    msg,\n\t\tTimestamp:  time.Now().Unix(),\n\t}\n}\n\n// Error implements the internal Go error interface.\nfunc (e Error) Error() string {\n\treturn fmt.Sprintf(\"[%d] %s: %s: %s\", e.StatusCode, e.Method, e.Path, e.Message)\n}\n\n// Is implements the standard `errors.Is` internal interface.\n// Usage: errors.Is(e, target)\nfunc (e Error) Is(target error) bool {\n\tif target == nil {\n\t\treturn false\n\t}\n\n\terr, ok := target.(Error)\n\tif !ok {\n\t\treturn false\n\t}\n\n\treturn (err.StatusCode == e.StatusCode || e.StatusCode == 0) &&\n\t\t(err.Message == e.Message || e.Message == \"\")\n}\n"
  },
  {
    "path": "_examples/database/mysql/api/middleware/.gitkeep",
    "content": ""
  },
  {
    "path": "_examples/database/mysql/api/product_handler.go",
    "content": "package api\n\nimport (\n\t\"time\"\n\n\t\"myapp/cache\"\n\t\"myapp/entity\"\n\t\"myapp/service\"\n\t\"myapp/sql\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\n// ProductHandler is the http mux for products.\ntype ProductHandler struct {\n\tservice *service.ProductService\n\tcache   *cache.Cache\n}\n\n// NewProductHandler returns the main controller for the products API.\nfunc NewProductHandler(service *service.ProductService) *ProductHandler {\n\treturn &ProductHandler{\n\t\tservice: service,\n\t\tcache:   cache.New(service, \"products\", time.Minute),\n\t}\n}\n\n// GetByID fetches a single record from the database and sends it to the client.\n// Method: GET.\nfunc (h *ProductHandler) GetByID(ctx iris.Context) {\n\tid := ctx.Params().GetString(\"id\")\n\n\tvar product []byte\n\terr := h.cache.GetByID(ctx.Request().Context(), id, &product)\n\tif err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\twriteEntityNotFound(ctx)\n\t\t\treturn\n\t\t}\n\n\t\tdebugf(\"ProductHandler.GetByID(id=%v): %v\", id, err)\n\t\twriteInternalServerError(ctx)\n\t\treturn\n\t}\n\n\tctx.ContentType(\"application/json\")\n\tctx.Write(product)\n\n\t// ^ Could use our simple `noCache` or implement a Cache-Control (see kataras/iris/cache for that)\n\t// but let's keep it simple.\n}\n\n// List lists a set of records from the database.\n// Method: GET.\nfunc (h *ProductHandler) List(ctx iris.Context) {\n\tkey := ctx.Request().URL.RawQuery\n\n\tproducts := []byte(\"[]\")\n\terr := h.cache.List(ctx.Request().Context(), key, &products)\n\tif err != nil && err != sql.ErrNoRows {\n\t\tdebugf(\"ProductHandler.List(DB) (%s): %v\",\n\t\t\tkey, err)\n\n\t\twriteInternalServerError(ctx)\n\t\treturn\n\t}\n\n\tctx.ContentType(\"application/json\")\n\tctx.Write(products)\n}\n\n// Create adds a record to the database.\n// Method: POST.\nfunc (h *ProductHandler) Create(ctx iris.Context) {\n\tvar product entity.Product\n\tif err := ctx.ReadJSON(&product); err != nil {\n\t\treturn\n\t}\n\n\tid, err := h.service.Insert(ctx.Request().Context(), product)\n\tif err != nil {\n\t\tif err == sql.ErrUnprocessable {\n\t\t\tctx.StopWithJSON(iris.StatusUnprocessableEntity, newError(iris.StatusUnprocessableEntity, ctx.Request().Method, ctx.Path(), \"required fields are missing\"))\n\t\t\treturn\n\t\t}\n\n\t\tdebugf(\"ProductHandler.Create(DB): %v\", err)\n\t\twriteInternalServerError(ctx)\n\t\treturn\n\t}\n\n\t// Send 201 with body of {\"id\":$last_inserted_id\"}.\n\tctx.StatusCode(iris.StatusCreated)\n\tctx.JSON(iris.Map{product.PrimaryKey(): id})\n}\n\n// Update performs a full-update of a record in the database.\n// Method: PUT.\nfunc (h *ProductHandler) Update(ctx iris.Context) {\n\tvar product entity.Product\n\tif err := ctx.ReadJSON(&product); err != nil {\n\t\treturn\n\t}\n\n\taffected, err := h.service.Update(ctx.Request().Context(), product)\n\tif err != nil {\n\t\tif err == sql.ErrUnprocessable {\n\t\t\tctx.StopWithJSON(iris.StatusUnprocessableEntity, newError(iris.StatusUnprocessableEntity, ctx.Request().Method, ctx.Path(), \"required fields are missing\"))\n\t\t\treturn\n\t\t}\n\n\t\tdebugf(\"ProductHandler.Update(DB): %v\", err)\n\t\twriteInternalServerError(ctx)\n\t\treturn\n\t}\n\n\tstatus := iris.StatusOK\n\tif affected == 0 {\n\t\tstatus = iris.StatusNotModified\n\t}\n\n\tctx.StatusCode(status)\n}\n\n// PartialUpdate is the handler for partially update one or more fields of the record.\n// Method: PATCH.\nfunc (h *ProductHandler) PartialUpdate(ctx iris.Context) {\n\tid := ctx.Params().GetInt64Default(\"id\", 0)\n\n\tvar attrs map[string]any\n\tif err := ctx.ReadJSON(&attrs); err != nil {\n\t\treturn\n\t}\n\n\taffected, err := h.service.PartialUpdate(ctx.Request().Context(), id, attrs)\n\tif err != nil {\n\t\tif err == sql.ErrUnprocessable {\n\t\t\tctx.StopWithJSON(iris.StatusUnprocessableEntity, newError(iris.StatusUnprocessableEntity, ctx.Request().Method, ctx.Path(), \"unsupported value(s)\"))\n\t\t\treturn\n\t\t}\n\n\t\tdebugf(\"ProductHandler.PartialUpdate(DB): %v\", err)\n\t\twriteInternalServerError(ctx)\n\t\treturn\n\t}\n\n\tstatus := iris.StatusOK\n\tif affected == 0 {\n\t\tstatus = iris.StatusNotModified\n\t}\n\n\tctx.StatusCode(status)\n}\n\n// Delete removes a record from the database.\n// Method: DELETE.\nfunc (h *ProductHandler) Delete(ctx iris.Context) {\n\tid := ctx.Params().GetInt64Default(\"id\", 0)\n\n\taffected, err := h.service.DeleteByID(ctx.Request().Context(), id)\n\tif err != nil {\n\t\tdebugf(\"ProductHandler.Delete(DB): %v\", err)\n\t\twriteInternalServerError(ctx)\n\t\treturn\n\t}\n\n\tstatus := iris.StatusOK // StatusNoContent\n\tif affected == 0 {\n\t\tstatus = iris.StatusNotModified\n\t}\n\n\tctx.StatusCode(status)\n}\n"
  },
  {
    "path": "_examples/database/mysql/cache/groupcache.go",
    "content": "package cache\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/url\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"myapp/entity\"\n\t\"myapp/sql\"\n\n\t\"github.com/mailgun/groupcache/v2\"\n)\n\n// Service that cache will use to retrieve data.\ntype Service interface {\n\tRecordInfo() sql.Record\n\tGetByID(ctx context.Context, dest any, id int64) error\n\tList(ctx context.Context, dest any, opts sql.ListOptions) error\n}\n\n// Cache is a simple structure which holds the groupcache and the database service, exposes\n// `GetByID` and `List` which returns cached (or stores new) items.\ntype Cache struct {\n\tservice Service\n\tmaxAge  time.Duration\n\tgroup   *groupcache.Group\n}\n\n// Size default size to use on groupcache, defaults to 3MB.\nvar Size int64 = 3 << (10 * 3)\n\n// New returns a new cache service which exposes `GetByID` and `List` methods to work with.\n// The \"name\" should be unique, \"maxAge\" for cache expiration.\nfunc New(service Service, name string, maxAge time.Duration) *Cache {\n\tc := new(Cache)\n\tc.service = service\n\tc.maxAge = maxAge\n\tc.group = groupcache.NewGroup(name, Size, c)\n\treturn c\n}\n\nconst (\n\tprefixID   = \"#\"\n\tprefixList = \"[\"\n)\n\n// Get implements the groupcache.Getter interface.\n// Use `GetByID` and `List` instead.\nfunc (c *Cache) Get(ctx context.Context, key string, dest groupcache.Sink) error {\n\tif len(key) < 2 { // empty or missing prefix+key, should never happen.\n\t\treturn sql.ErrUnprocessable\n\t}\n\n\tvar v any\n\n\tprefix := key[0:1]\n\tkey = key[1:]\n\tswitch prefix {\n\tcase prefixID:\n\t\t// Get by ID.\n\t\tid, err := strconv.ParseInt(key, 10, 64)\n\t\tif err != nil || id <= 0 {\n\t\t\treturn err\n\t\t}\n\n\t\tswitch c.service.RecordInfo().(type) {\n\t\tcase *entity.Category:\n\t\t\tv = new(entity.Category)\n\t\tcase *entity.Product:\n\t\t\tv = new(entity.Product)\n\t\t}\n\n\t\terr = c.service.GetByID(ctx, v, id)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\tcase prefixList:\n\t\t// Get a set of records, list.\n\t\tq, err := url.ParseQuery(key)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\topts := sql.ParseListOptions(q)\n\n\t\tswitch c.service.RecordInfo().(type) {\n\t\tcase *entity.Category:\n\t\t\tv = new(entity.Categories)\n\t\tcase *entity.Product:\n\t\t\tv = new(entity.Products)\n\t\t}\n\n\t\terr = c.service.List(ctx, v, opts)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\tdefault:\n\t\treturn sql.ErrUnprocessable\n\t}\n\n\tb, err := json.Marshal(v)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn dest.SetBytes(b, time.Now().Add(c.maxAge))\n}\n\n// GetByID binds an item to \"dest\" an item based on its \"id\".\nfunc (c *Cache) GetByID(ctx context.Context, id string, dest *[]byte) error {\n\treturn c.group.Get(ctx, prefixID+id, groupcache.AllocatingByteSliceSink(dest))\n}\n\n// List binds item to \"dest\" based on the \"rawQuery\" of `url.Values` for `ListOptions`.\nfunc (c *Cache) List(ctx context.Context, rawQuery string, dest *[]byte) error {\n\treturn c.group.Get(ctx, prefixList+rawQuery, groupcache.AllocatingByteSliceSink(dest))\n}\n"
  },
  {
    "path": "_examples/database/mysql/docker-compose.yml",
    "content": "version: '3.1'\n\nservices:\n  db:\n    image: mysql\n    command: --default-authentication-plugin=mysql_native_password\n    environment:\n      MYSQL_ROOT_PASSWORD: dbpassword\n      MYSQL_DATABASE: myapp\n      MYSQL_USER: user_myapp\n      MYSQL_PASSWORD: dbpassword\n    tty: true\n    volumes:\n    - ./migration:/docker-entrypoint-initdb.d\n  app:\n    build: .\n    ports:\n      - 8080:8080\n    environment:\n      PORT: 8080\n      MYSQL_USER: user_myapp\n      MYSQL_PASSWORD: dbpassword\n      MYSQL_DATABASE: myapp\n      MYSQL_HOST: db\n    restart: on-failure\n    healthcheck:\n        test: [\"CMD\", \"curl\", \"-f\", \"tcp://db:3306\"]\n        interval: 30s\n        timeout: 10s\n        retries: 5\n    depends_on:\n      - db"
  },
  {
    "path": "_examples/database/mysql/entity/category.go",
    "content": "package entity\n\nimport (\n\t\"database/sql\"\n\t\"time\"\n)\n\n// Category represents the categories entity.\n// Each product belongs to a category, see `Product.CategoryID` field.\n// It implements the `sql.Record` and `sql.Sorted` interfaces.\ntype Category struct {\n\tID       int64  `db:\"id\" json:\"id\"`\n\tTitle    string `db:\"title\" json:\"title\"`\n\tPosition uint64 `db:\"position\" json:\"position\"`\n\tImageURL string `db:\"image_url\" json:\"image_url\"`\n\n\t// We could use: sql.NullTime or unix time seconds (as int64),\n\t// note that the dsn parameter \"parseTime=true\" is required now in order to fill this field correctly.\n\tCreatedAt *time.Time `db:\"created_at\" json:\"created_at\"`\n\tUpdatedAt *time.Time `db:\"updated_at\" json:\"updated_at\"`\n}\n\n// TableName returns the database table name of a Category.\nfunc (c *Category) TableName() string {\n\treturn \"categories\"\n}\n\n// PrimaryKey returns the primary key of a Category.\nfunc (c *Category) PrimaryKey() string {\n\treturn \"id\"\n}\n\n// SortBy returns the column name that\n// should be used as a fallback for sorting a set of Category.\nfunc (c *Category) SortBy() string {\n\treturn \"position\"\n}\n\n// Scan binds mysql rows to this Category.\nfunc (c *Category) Scan(rows *sql.Rows) error {\n\tc.CreatedAt = new(time.Time)\n\tc.UpdatedAt = new(time.Time)\n\treturn rows.Scan(&c.ID, &c.Title, &c.Position, &c.ImageURL, &c.CreatedAt, &c.UpdatedAt)\n}\n\n// Categories a list of categories. Implements the `Scannable` interface.\ntype Categories []*Category\n\n// Scan binds mysql rows to this Categories.\nfunc (cs *Categories) Scan(rows *sql.Rows) (err error) {\n\tcp := *cs\n\tfor rows.Next() {\n\t\tc := new(Category)\n\t\tif err = c.Scan(rows); err != nil {\n\t\t\treturn\n\t\t}\n\t\tcp = append(cp, c)\n\t}\n\n\tif len(cp) == 0 {\n\t\treturn sql.ErrNoRows\n\t}\n\n\t*cs = cp\n\n\treturn rows.Err()\n}\n\n/*\n// The requests.\ntype (\n\tCreateCategoryRequest struct {\n\t\tTitle    string `json:\"title\"` // all required.\n\t\tPosition uint64 `json:\"position\"`\n\t\tImageURL string `json:\"imageURL\"`\n\t}\n\n\tUpdateCategoryRequest CreateCategoryRequest // at least 1 required.\n\n\tGetCategoryRequest struct {\n\t\tID int64 `json:\"id\"` // required.\n\t}\n\n\tDeleteCategoryRequest GetCategoryRequest\n\n\tGetCategoriesRequest struct {\n\t\t// [limit, offset...]\n\t}\n)*/\n"
  },
  {
    "path": "_examples/database/mysql/entity/product.go",
    "content": "package entity\n\nimport (\n\t\"database/sql\"\n\t\"time\"\n)\n\n// Product represents the products entity.\n// It implements the `sql.Record` and `sql.Sorted` interfaces.\ntype Product struct {\n\tID          int64      `db:\"id\" json:\"id\"`\n\tCategoryID  int64      `db:\"category_id\" json:\"category_id\"`\n\tTitle       string     `db:\"title\" json:\"title\"`\n\tImageURL    string     `db:\"image_url\" json:\"image_url\"`\n\tPrice       float32    `db:\"price\" json:\"price\"`\n\tDescription string     `db:\"description\" json:\"description\"`\n\tCreatedAt   *time.Time `db:\"created_at\" json:\"created_at\"`\n\tUpdatedAt   *time.Time `db:\"updated_at\" json:\"updated_at\"`\n}\n\n// TableName returns the database table name of a Product.\nfunc (p Product) TableName() string {\n\treturn \"products\"\n}\n\n// PrimaryKey returns the primary key of a Product.\nfunc (p *Product) PrimaryKey() string {\n\treturn \"id\"\n}\n\n// SortBy returns the column name that\n// should be used as a fallback for sorting a set of Product.\nfunc (p *Product) SortBy() string {\n\treturn \"updated_at\"\n}\n\n// ValidateInsert simple check for empty fields that should be required.\nfunc (p *Product) ValidateInsert() bool {\n\treturn p.CategoryID > 0 && p.Title != \"\" && p.ImageURL != \"\" && p.Price > 0 /* decimal* */ && p.Description != \"\"\n}\n\n// Scan binds mysql rows to this Product.\nfunc (p *Product) Scan(rows *sql.Rows) error {\n\tp.CreatedAt = new(time.Time)\n\tp.UpdatedAt = new(time.Time)\n\treturn rows.Scan(&p.ID, &p.CategoryID, &p.Title, &p.ImageURL, &p.Price, &p.Description, &p.CreatedAt, &p.UpdatedAt)\n}\n\n// Products is a list of products. Implements the `Scannable` interface.\ntype Products []*Product\n\n// Scan binds mysql rows to this Categories.\nfunc (ps *Products) Scan(rows *sql.Rows) (err error) {\n\tcp := *ps\n\tfor rows.Next() {\n\t\tp := new(Product)\n\t\tif err = p.Scan(rows); err != nil {\n\t\t\treturn\n\t\t}\n\t\tcp = append(cp, p)\n\t}\n\n\tif len(cp) == 0 {\n\t\treturn sql.ErrNoRows\n\t}\n\n\t*ps = cp\n\n\treturn rows.Err()\n}\n\n/*\n// The requests.\ntype (\n\tCreateProductRequest struct { // all required.\n\t\tCategoryID  int64  `json:\"categoryID\"`\n\t\tTitle       string `json:\"title\"`\n\t\tImageURL    string `json:\"imageURL\"`\n\t\tPrice       float32 `json:\"price\"`\n\t\tDescription string `json:\"description\"`\n\t}\n\n\tUpdateProductRequest CreateProductRequest // at least 1 required.\n\n\tGetProductRequest struct {\n\t\tID int64 `json:\"id\"` // required.\n\t}\n\n\tDeleteProductRequest GetProductRequest\n\n\tGetProductsRequest struct {\n\t\t// [page, offset...]\n\t}\n)\n*/\n"
  },
  {
    "path": "_examples/database/mysql/go.mod",
    "content": "module myapp\n\ngo 1.25\n\nrequire (\n\tgithub.com/DATA-DOG/go-sqlmock v1.5.2\n\tgithub.com/go-sql-driver/mysql v1.9.3\n\tgithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd\n\tgithub.com/mailgun/groupcache/v2 v2.6.0\n)\n\nrequire (\n\tfilippo.io/edwards25519 v1.1.0 // indirect\n\tgithub.com/BurntSushi/toml v1.6.0 // indirect\n\tgithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect\n\tgithub.com/CloudyKit/jet/v6 v6.3.1 // indirect\n\tgithub.com/Joker/jade v1.1.3 // indirect\n\tgithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 // indirect\n\tgithub.com/andybalholm/brotli v1.2.0 // indirect\n\tgithub.com/aymerick/douceur v0.2.0 // indirect\n\tgithub.com/fatih/structs v1.1.0 // indirect\n\tgithub.com/flosch/pongo2/v4 v4.0.2 // indirect\n\tgithub.com/golang/protobuf v1.5.4 // indirect\n\tgithub.com/golang/snappy v1.0.0 // indirect\n\tgithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a // indirect\n\tgithub.com/google/uuid v1.6.0 // indirect\n\tgithub.com/gorilla/css v1.0.1 // indirect\n\tgithub.com/iris-contrib/schema v0.0.6 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/kataras/blocks v0.0.8 // indirect\n\tgithub.com/kataras/golog v0.1.12 // indirect\n\tgithub.com/kataras/jwt v0.1.17 // indirect\n\tgithub.com/kataras/pio v0.0.13 // indirect\n\tgithub.com/kataras/sitemap v0.0.6 // indirect\n\tgithub.com/kataras/tunnel v0.0.4 // indirect\n\tgithub.com/klauspost/compress v1.18.2 // indirect\n\tgithub.com/kr/pretty v0.3.1 // indirect\n\tgithub.com/mailgun/raymond/v2 v2.0.48 // indirect\n\tgithub.com/mailru/easyjson v0.9.1 // indirect\n\tgithub.com/microcosm-cc/bluemonday v1.0.27 // indirect\n\tgithub.com/rogpeppe/go-internal v1.10.0 // indirect\n\tgithub.com/russross/blackfriday/v2 v2.1.0 // indirect\n\tgithub.com/schollz/closestmatch v2.1.0+incompatible // indirect\n\tgithub.com/segmentio/fasthash v1.0.3 // indirect\n\tgithub.com/sirupsen/logrus v1.9.2 // indirect\n\tgithub.com/stretchr/testify v1.11.1 // indirect\n\tgithub.com/tdewolff/minify/v2 v2.24.8 // indirect\n\tgithub.com/tdewolff/parse/v2 v2.8.5 // indirect\n\tgithub.com/valyala/bytebufferpool v1.0.0 // indirect\n\tgithub.com/vmihailenco/msgpack/v5 v5.4.1 // indirect\n\tgithub.com/vmihailenco/tagparser/v2 v2.0.0 // indirect\n\tgithub.com/yosssi/ace v0.0.5 // indirect\n\tgolang.org/x/crypto v0.47.0 // indirect\n\tgolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 // indirect\n\tgolang.org/x/net v0.49.0 // indirect\n\tgolang.org/x/sys v0.40.0 // indirect\n\tgolang.org/x/text v0.33.0 // indirect\n\tgolang.org/x/time v0.14.0 // indirect\n\tgoogle.golang.org/protobuf v1.36.11 // indirect\n\tgopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect\n\tgopkg.in/ini.v1 v1.67.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n"
  },
  {
    "path": "_examples/database/mysql/go.sum",
    "content": "filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=\nfilippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=\ngithub.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=\ngithub.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=\ngithub.com/CloudyKit/jet/v6 v6.3.1 h1:6IAo5Cx21xrHVaR8zzXN5gJatKV/wO7Nf6bfCnCSbUw=\ngithub.com/CloudyKit/jet/v6 v6.3.1/go.mod h1:lf8ksdNsxZt7/yH/3n4vJQWA9RUq4wpaHtArHhGVMOw=\ngithub.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=\ngithub.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=\ngithub.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc=\ngithub.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=\ngithub.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=\ngithub.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 h1:dG7/gLroJGht/jSQtHiLvT48Hxn+crbmvyItZC8cWOs=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05/go.mod h1:NYezi6wtnJtBm5btoprXc5SvAdqH0XTXWnUup0MptAI=\ngithub.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=\ngithub.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=\ngithub.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=\ngithub.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=\ngithub.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=\ngithub.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=\ngithub.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=\ngithub.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=\ngithub.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=\ngithub.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw=\ngithub.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8=\ngithub.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=\ngithub.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=\ngithub.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=\ngithub.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=\ngithub.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=\ngithub.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=\ngithub.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=\ngithub.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a h1:l7A0loSszR5zHd/qK53ZIHMO8b3bBSmENnQ6eKnUT0A=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=\ngithub.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=\ngithub.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=\ngithub.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=\ngithub.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=\ngithub.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2 h1:T9THsdP1woyAqKHwjkEsbCnMefsAFvk8iJJKokcJ3Go=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2/go.mod h1:JLDgIqnFy5loDSUv1OA2j0mb6p/rDhiCqigP22Uq9xE=\ngithub.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw=\ngithub.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/kataras/blocks v0.0.8 h1:MrpVhoFTCR2v1iOOfGng5VJSILKeZZI+7NGfxEh3SUM=\ngithub.com/kataras/blocks v0.0.8/go.mod h1:9Jm5zx6BB+06NwA+OhTbHW1xkMOYxahnqTN5DveZ2Yg=\ngithub.com/kataras/golog v0.1.12 h1:Bu7I/G4ilJlbfzjmU39O9N+2uO1pBcMK045fzZ4ytNg=\ngithub.com/kataras/golog v0.1.12/go.mod h1:wrGSbOiBqbQSQznleVNX4epWM8rl9SJ/rmEacl0yqy4=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd h1:oMsQgqKACbUdlaIWnN+EZzzAnHkw90hE/y/R0BSij2U=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd/go.mod h1:yucjtDl9Vn3OctWM1fi0MuM9OckemC7kEreFa9QtPF8=\ngithub.com/kataras/jwt v0.1.17 h1:dYjemzcdYqA4ylwq9/56MslCr/pNOyVUZ2bl3hYNHgc=\ngithub.com/kataras/jwt v0.1.17/go.mod h1:HUnU5HDBCDanVF8zrPVSE2VK8HicospKefZDD4DzOKU=\ngithub.com/kataras/pio v0.0.13 h1:x0rXVX0fviDTXOOLOmr4MUxOabu1InVSTu5itF8CXCM=\ngithub.com/kataras/pio v0.0.13/go.mod h1:k3HNuSw+eJ8Pm2lA4lRhg3DiCjVgHlP8hmXApSej3oM=\ngithub.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY=\ngithub.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4=\ngithub.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA=\ngithub.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw=\ngithub.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE=\ngithub.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=\ngithub.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/mailgun/groupcache/v2 v2.6.0 h1:w7+5ltoEwbrCk2LWyRRa7Ui9sRlvyBUaIqEU97kdh+0=\ngithub.com/mailgun/groupcache/v2 v2.6.0/go.mod h1:s509cRKQkn9+FUC42BG7A8kbTAywikZUOJtr1guhOkY=\ngithub.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=\ngithub.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=\ngithub.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8=\ngithub.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=\ngithub.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=\ngithub.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=\ngithub.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=\ngithub.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=\ngithub.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=\ngithub.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=\ngithub.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=\ngithub.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=\ngithub.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=\ngithub.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=\ngithub.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=\ngithub.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk=\ngithub.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=\ngithub.com/segmentio/fasthash v1.0.3 h1:EI9+KE1EwvMLBWwjpRDc+fEM+prwxDYbslddQGtrmhM=\ngithub.com/segmentio/fasthash v1.0.3/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY=\ngithub.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=\ngithub.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=\ngithub.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/sirupsen/logrus v1.9.2 h1:oxx1eChJGI6Uks2ZC4W1zpLlVgqB8ner4EuQwV4Ik1Y=\ngithub.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/tdewolff/minify/v2 v2.24.8 h1:58/VjsbevI4d5FGV0ZSuBrHMSSkH4MCH0sIz/eKIauE=\ngithub.com/tdewolff/minify/v2 v2.24.8/go.mod h1:0Ukj0CRpo/sW/nd8uZ4ccXaV1rEVIWA3dj8U7+Shhfw=\ngithub.com/tdewolff/parse/v2 v2.8.5 h1:ZmBiA/8Do5Rpk7bDye0jbbDUpXXbCdc3iah4VeUvwYU=\ngithub.com/tdewolff/parse/v2 v2.8.5/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=\ngithub.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE=\ngithub.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=\ngithub.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=\ngithub.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=\ngithub.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=\ngithub.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=\ngithub.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=\ngithub.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=\ngithub.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA=\ngithub.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0=\ngithub.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=\ngithub.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=\ngithub.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=\ngolang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=\ngolang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=\ngolang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=\ngolang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=\ngolang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=\ngolang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=\ngolang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=\ngolang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=\ngoogle.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=\ngopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nmoul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs=\nmoul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE=\n"
  },
  {
    "path": "_examples/database/mysql/main.go",
    "content": "package main // Look README.md\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\t\"myapp/api\"\n\t\"myapp/sql\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tdsn := fmt.Sprintf(\"%s:%s@tcp(%s:3306)/%s?parseTime=true&charset=utf8mb4&collation=utf8mb4_unicode_ci\",\n\t\tgetenv(\"MYSQL_USER\", \"user_myapp\"),\n\t\tgetenv(\"MYSQL_PASSWORD\", \"dbpassword\"),\n\t\tgetenv(\"MYSQL_HOST\", \"localhost\"),\n\t\tgetenv(\"MYSQL_DATABASE\", \"myapp\"),\n\t)\n\n\tdb, err := sql.ConnectMySQL(dsn)\n\tif err != nil {\n\t\tlog.Fatalf(\"error connecting to the MySQL database: %v\", err)\n\t}\n\n\tsecret := getenv(\"JWT_SECRET\", \"EbnJO3bwmX\")\n\n\tapp := iris.New()\n\tsubRouter := api.Router(db, secret)\n\tapp.PartyFunc(\"/\", subRouter)\n\n\taddr := fmt.Sprintf(\":%s\", getenv(\"PORT\", \"8080\"))\n\tapp.Listen(addr)\n}\n\nfunc getenv(key string, def string) string {\n\tv := os.Getenv(key)\n\tif v == \"\" {\n\t\treturn def\n\t}\n\n\treturn v\n}\n"
  },
  {
    "path": "_examples/database/mysql/migration/api_category/create_category.json",
    "content": "{\n    \"title\": \"computer-internet\",\n    \"position\": 2,\n    \"image_url\": \"https://bp.pstatic.gr/public/dist/images/1mOPxYtw1k.webp\"\n}"
  },
  {
    "path": "_examples/database/mysql/migration/api_category/insert_products_category.json",
    "content": "[{\n    \"title\": \"product-1\",\n    \"image_url\": \"https://images.product1.png\",\n    \"price\": 42.42,\n    \"description\": \"a description for product-1\"\n}, {\n    \"title\": \"product-2\",\n    \"image_url\": \"https://images.product2.png\",\n    \"price\": 32.1,\n    \"description\": \"a description for product-2\"\n}, {\n    \"title\": \"product-3\",\n    \"image_url\": \"https://images.product3.png\",\n    \"price\": 52321321.32,\n    \"description\": \"a description for product-3\"\n}, {\n    \"title\": \"product-4\",\n    \"image_url\": \"https://images.product4.png\",\n    \"price\": 77.4221,\n    \"description\": \"a description for product-4\"\n}, {\n    \"title\": \"product-5\",\n    \"image_url\": \"https://images.product5.png\",\n    \"price\": 55.1,\n    \"description\": \"a description for product-5\"\n}, {\n    \"title\": \"product-6\",\n    \"image_url\": \"https://images.product6.png\",\n    \"price\": 53.32,\n    \"description\": \"a description for product-6\"\n}]"
  },
  {
    "path": "_examples/database/mysql/migration/api_category/update_category.json",
    "content": "{\n    \"id\": 2,\n    \"position\": 1,\n    \"title\": \"computers\",\n    \"image_url\": \"https://upload.wikimedia.org/wikipedia/commons/thumb/d/d7/Desktop_computer_clipart_-_Yellow_theme.svg/1200px-Desktop_computer_clipart_-_Yellow_theme.svg.png\"\n}"
  },
  {
    "path": "_examples/database/mysql/migration/api_category/update_partial_category.json",
    "content": "{\n    \"title\": \"computers-technology\"\n}"
  },
  {
    "path": "_examples/database/mysql/migration/api_postman.json",
    "content": "{\n\t\"info\": {\n\t\t\"_postman_id\": \"7b8b53f8-859a-425a-aa9c-28bc2a2d5ef7\",\n\t\t\"name\": \"myapp (api-test)\",\n\t\t\"schema\": \"https://schema.getpostman.com/json/collection/v2.1.0/collection.json\"\n\t},\n\t\"item\": [\n\t\t{\n\t\t\t\"name\": \"Category\",\n\t\t\t\"item\": [\n\t\t\t\t{\n\t\t\t\t\t\"name\": \"Create\",\n\t\t\t\t\t\"request\": {\n\t\t\t\t\t\t\"method\": \"POST\",\n\t\t\t\t\t\t\"header\": [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"key\": \"Authorization\",\n\t\t\t\t\t\t\t\t\"value\": \"Bearer {{token}}\",\n\t\t\t\t\t\t\t\t\"type\": \"text\",\n\t\t\t\t\t\t\t\t\"disabled\": true\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"body\": {\n\t\t\t\t\t\t\t\"mode\": \"raw\",\n\t\t\t\t\t\t\t\"raw\": \"{\\r\\n    \\\"title\\\": \\\"computer-internet\\\",\\r\\n    \\\"position\\\": 1,\\r\\n    \\\"image_url\\\": \\\"https://bp.pstatic.gr/public/dist/images/1mOPxYtw1k.webp\\\"\\r\\n}\",\n\t\t\t\t\t\t\t\"options\": {\n\t\t\t\t\t\t\t\t\"raw\": {\n\t\t\t\t\t\t\t\t\t\"language\": \"json\"\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"url\": {\n\t\t\t\t\t\t\t\"raw\": \"http://localhost:8080/category\",\n\t\t\t\t\t\t\t\"protocol\": \"http\",\n\t\t\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\t\t\"category\"\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"description\": \"Create a Category\"\n\t\t\t\t\t},\n\t\t\t\t\t\"response\": []\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"name\": \"Get By ID\",\n\t\t\t\t\t\"request\": {\n\t\t\t\t\t\t\"method\": \"GET\",\n\t\t\t\t\t\t\"header\": [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"key\": \"Authorization\",\n\t\t\t\t\t\t\t\t\"value\": \"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2lyaXMtZ28uY29tIiwiYXVkIjpbIjljOTY5ZDg5LTBkZGYtNDlkMC1hYzcxLTI3MmU3YmY5NjkwMCJdLCJpYXQiOjE2MDYyNzE1NDYsImV4cCI6MTYwNjI3MjQ0Nn0.l2_5iqfEaC68UySTQNoDx2sfzn031tHiTdm2kZoNkWQ\",\n\t\t\t\t\t\t\t\t\"type\": \"text\",\n\t\t\t\t\t\t\t\t\"disabled\": true\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"url\": {\n\t\t\t\t\t\t\t\"raw\": \"http://localhost:8080/category/1\",\n\t\t\t\t\t\t\t\"protocol\": \"http\",\n\t\t\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\t\t\"category\",\n\t\t\t\t\t\t\t\t\"1\"\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"description\": \"Get By ID\"\n\t\t\t\t\t},\n\t\t\t\t\t\"response\": []\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"name\": \"List\",\n\t\t\t\t\t\"protocolProfileBehavior\": {\n\t\t\t\t\t\t\"disableBodyPruning\": true\n\t\t\t\t\t},\n\t\t\t\t\t\"request\": {\n\t\t\t\t\t\t\"method\": \"GET\",\n\t\t\t\t\t\t\"header\": [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"key\": \"Authorization\",\n\t\t\t\t\t\t\t\t\"value\": \"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2lyaXMtZ28uY29tIiwiYXVkIjpbIjljOTY5ZDg5LTBkZGYtNDlkMC1hYzcxLTI3MmU3YmY5NjkwMCJdLCJpYXQiOjE2MDYyNzE1NDYsImV4cCI6MTYwNjI3MjQ0Nn0.l2_5iqfEaC68UySTQNoDx2sfzn031tHiTdm2kZoNkWQ\",\n\t\t\t\t\t\t\t\t\"type\": \"text\",\n\t\t\t\t\t\t\t\t\"disabled\": true\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"body\": {\n\t\t\t\t\t\t\t\"mode\": \"raw\",\n\t\t\t\t\t\t\t\"raw\": \"\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"url\": {\n\t\t\t\t\t\t\t\"raw\": \"http://localhost:8080/category?offset=0&limit=30&order=asc\",\n\t\t\t\t\t\t\t\"protocol\": \"http\",\n\t\t\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\t\t\"category\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"query\": [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\"key\": \"offset\",\n\t\t\t\t\t\t\t\t\t\"value\": \"0\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\"key\": \"limit\",\n\t\t\t\t\t\t\t\t\t\"value\": \"30\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\"key\": \"order\",\n\t\t\t\t\t\t\t\t\t\"value\": \"asc\"\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"description\": \"Get many with limit offset\"\n\t\t\t\t\t},\n\t\t\t\t\t\"response\": []\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"name\": \"Update (Full)\",\n\t\t\t\t\t\"request\": {\n\t\t\t\t\t\t\"method\": \"PUT\",\n\t\t\t\t\t\t\"header\": [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"key\": \"Authorization\",\n\t\t\t\t\t\t\t\t\"value\": \"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1ODk1ODU1NjN9.PtfDS1niGoZ7pV6kplI-_q1fVKLnknQ3IwcrLZhoVCU\",\n\t\t\t\t\t\t\t\t\"type\": \"text\",\n\t\t\t\t\t\t\t\t\"disabled\": true\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"body\": {\n\t\t\t\t\t\t\t\"mode\": \"raw\",\n\t\t\t\t\t\t\t\"raw\": \"{\\r\\n\\t\\\"id\\\": 1,\\r\\n\\t\\\"position\\\": 3,\\r\\n    \\\"title\\\": \\\"computers\\\",\\r\\n    \\\"image_url\\\":\\\"https://upload.wikimedia.org/wikipedia/commons/thumb/d/d7/Desktop_computer_clipart_-_Yellow_theme.svg/1200px-Desktop_computer_clipart_-_Yellow_theme.svg.png\\\"\\r\\n}\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"url\": {\n\t\t\t\t\t\t\t\"raw\": \"http://localhost:8080/category\",\n\t\t\t\t\t\t\t\"protocol\": \"http\",\n\t\t\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\t\t\"category\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"query\": [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\"key\": \"\",\n\t\t\t\t\t\t\t\t\t\"value\": null,\n\t\t\t\t\t\t\t\t\t\"disabled\": true\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"description\": \"Update a Category (full update)\"\n\t\t\t\t\t},\n\t\t\t\t\t\"response\": []\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"name\": \"Delete By ID\",\n\t\t\t\t\t\"request\": {\n\t\t\t\t\t\t\"method\": \"DELETE\",\n\t\t\t\t\t\t\"header\": [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"key\": \"Authorization\",\n\t\t\t\t\t\t\t\t\"value\": \"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1ODk1ODU1NjN9.PtfDS1niGoZ7pV6kplI-_q1fVKLnknQ3IwcrLZhoVCU\",\n\t\t\t\t\t\t\t\t\"type\": \"text\",\n\t\t\t\t\t\t\t\t\"disabled\": true\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"url\": {\n\t\t\t\t\t\t\t\"raw\": \"http://localhost:8080/category/1\",\n\t\t\t\t\t\t\t\"protocol\": \"http\",\n\t\t\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\t\t\"category\",\n\t\t\t\t\t\t\t\t\"1\"\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"description\": \"Delete a Category\"\n\t\t\t\t\t},\n\t\t\t\t\t\"response\": []\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"name\": \"Update (Partial)\",\n\t\t\t\t\t\"request\": {\n\t\t\t\t\t\t\"method\": \"PATCH\",\n\t\t\t\t\t\t\"header\": [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"key\": \"Authorization\",\n\t\t\t\t\t\t\t\t\"value\": \"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1ODk1ODU1NjN9.PtfDS1niGoZ7pV6kplI-_q1fVKLnknQ3IwcrLZhoVCU\",\n\t\t\t\t\t\t\t\t\"type\": \"text\",\n\t\t\t\t\t\t\t\t\"disabled\": true\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"body\": {\n\t\t\t\t\t\t\t\"mode\": \"raw\",\n\t\t\t\t\t\t\t\"raw\": \"{\\r\\n    \\\"title\\\": \\\"computers-technology\\\"\\r\\n}\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"url\": {\n\t\t\t\t\t\t\t\"raw\": \"http://localhost:8080/category/3\",\n\t\t\t\t\t\t\t\"protocol\": \"http\",\n\t\t\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\t\t\"category\",\n\t\t\t\t\t\t\t\t\"3\"\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"description\": \"Update a Category partially, e.g. title only\"\n\t\t\t\t\t},\n\t\t\t\t\t\"response\": []\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"name\": \"List Products\",\n\t\t\t\t\t\"request\": {\n\t\t\t\t\t\t\"method\": \"GET\",\n\t\t\t\t\t\t\"header\": [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"key\": \"Authorization\",\n\t\t\t\t\t\t\t\t\"value\": \"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2lyaXMtZ28uY29tIiwiYXVkIjpbIjljOTY5ZDg5LTBkZGYtNDlkMC1hYzcxLTI3MmU3YmY5NjkwMCJdLCJpYXQiOjE2MDYyNzE1NDYsImV4cCI6MTYwNjI3MjQ0Nn0.l2_5iqfEaC68UySTQNoDx2sfzn031tHiTdm2kZoNkWQ\",\n\t\t\t\t\t\t\t\t\"type\": \"text\",\n\t\t\t\t\t\t\t\t\"disabled\": true\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"url\": {\n\t\t\t\t\t\t\t\"raw\": \"http://localhost:8080/category/1/products?offset=0&limit=30&by=price&order=asc\",\n\t\t\t\t\t\t\t\"protocol\": \"http\",\n\t\t\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\t\t\"category\",\n\t\t\t\t\t\t\t\t\"1\",\n\t\t\t\t\t\t\t\t\"products\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"query\": [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\"key\": \"offset\",\n\t\t\t\t\t\t\t\t\t\"value\": \"0\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\"key\": \"limit\",\n\t\t\t\t\t\t\t\t\t\"value\": \"30\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\"key\": \"by\",\n\t\t\t\t\t\t\t\t\t\"value\": \"price\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\"key\": \"order\",\n\t\t\t\t\t\t\t\t\t\"value\": \"asc\"\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"description\": \"Get products from cheap to expensive\"\n\t\t\t\t\t},\n\t\t\t\t\t\"response\": []\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"name\": \"Insert Products\",\n\t\t\t\t\t\"request\": {\n\t\t\t\t\t\t\"method\": \"POST\",\n\t\t\t\t\t\t\"header\": [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"key\": \"Authorization\",\n\t\t\t\t\t\t\t\t\"value\": \"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1ODk1ODU1NjN9.PtfDS1niGoZ7pV6kplI-_q1fVKLnknQ3IwcrLZhoVCU\",\n\t\t\t\t\t\t\t\t\"type\": \"text\",\n\t\t\t\t\t\t\t\t\"disabled\": true\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"body\": {\n\t\t\t\t\t\t\t\"mode\": \"raw\",\n\t\t\t\t\t\t\t\"raw\": \"[{\\r\\n    \\\"title\\\": \\\"product-1\\\",\\r\\n    \\\"image_url\\\": \\\"https://images.product1.png\\\",\\r\\n    \\\"price\\\": 42.42,\\r\\n    \\\"description\\\": \\\"a description for product-1\\\"\\r\\n}, {\\r\\n    \\\"title\\\": \\\"product-2\\\",\\r\\n    \\\"image_url\\\": \\\"https://images.product2.png\\\",\\r\\n    \\\"price\\\": 32.1,\\r\\n    \\\"description\\\": \\\"a description for product-2\\\"\\r\\n}, {\\r\\n    \\\"title\\\": \\\"product-3\\\",\\r\\n    \\\"image_url\\\": \\\"https://images.product3.png\\\",\\r\\n    \\\"price\\\": 52321321.32,\\r\\n    \\\"description\\\": \\\"a description for product-3\\\"\\r\\n}, {\\r\\n    \\\"title\\\": \\\"product-4\\\",\\r\\n    \\\"image_url\\\": \\\"https://images.product4.png\\\",\\r\\n    \\\"price\\\": 77.4221,\\r\\n    \\\"description\\\": \\\"a description for product-4\\\"\\r\\n}, {\\r\\n    \\\"title\\\": \\\"product-5\\\",\\r\\n    \\\"image_url\\\": \\\"https://images.product5.png\\\",\\r\\n    \\\"price\\\": 55.1,\\r\\n    \\\"description\\\": \\\"a description for product-5\\\"\\r\\n}, {\\r\\n    \\\"title\\\": \\\"product-6\\\",\\r\\n    \\\"image_url\\\": \\\"https://images.product6.png\\\",\\r\\n    \\\"price\\\": 53.32,\\r\\n    \\\"description\\\": \\\"a description for product-6\\\"\\r\\n}]\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"url\": {\n\t\t\t\t\t\t\t\"raw\": \"http://localhost:8080/category/3/products\",\n\t\t\t\t\t\t\t\"protocol\": \"http\",\n\t\t\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\t\t\"category\",\n\t\t\t\t\t\t\t\t\"3\",\n\t\t\t\t\t\t\t\t\"products\"\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"description\": \"Batch Insert Products to a Category\"\n\t\t\t\t\t},\n\t\t\t\t\t\"response\": []\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"protocolProfileBehavior\": {}\n\t\t},\n\t\t{\n\t\t\t\"name\": \"Product\",\n\t\t\t\"item\": [\n\t\t\t\t{\n\t\t\t\t\t\"name\": \"List\",\n\t\t\t\t\t\"request\": {\n\t\t\t\t\t\t\"method\": \"GET\",\n\t\t\t\t\t\t\"header\": [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"key\": \"Authorization\",\n\t\t\t\t\t\t\t\t\"value\": \"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2lyaXMtZ28uY29tIiwiYXVkIjpbIjljOTY5ZDg5LTBkZGYtNDlkMC1hYzcxLTI3MmU3YmY5NjkwMCJdLCJpYXQiOjE2MDYyNzE1NDYsImV4cCI6MTYwNjI3MjQ0Nn0.l2_5iqfEaC68UySTQNoDx2sfzn031tHiTdm2kZoNkWQ\",\n\t\t\t\t\t\t\t\t\"type\": \"text\",\n\t\t\t\t\t\t\t\t\"disabled\": true\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"url\": {\n\t\t\t\t\t\t\t\"raw\": \"http://localhost:8080/product?offset=0&limit=30&by=price&order=asc\",\n\t\t\t\t\t\t\t\"protocol\": \"http\",\n\t\t\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\t\t\"product\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"query\": [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\"key\": \"offset\",\n\t\t\t\t\t\t\t\t\t\"value\": \"0\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\"key\": \"limit\",\n\t\t\t\t\t\t\t\t\t\"value\": \"30\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\"key\": \"by\",\n\t\t\t\t\t\t\t\t\t\"value\": \"price\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\"key\": \"order\",\n\t\t\t\t\t\t\t\t\t\"value\": \"asc\"\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"description\": \"List products\"\n\t\t\t\t\t},\n\t\t\t\t\t\"response\": []\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"name\": \"Get By ID\",\n\t\t\t\t\t\"request\": {\n\t\t\t\t\t\t\"method\": \"GET\",\n\t\t\t\t\t\t\"header\": [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"key\": \"Authorization\",\n\t\t\t\t\t\t\t\t\"value\": \"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2lyaXMtZ28uY29tIiwiYXVkIjpbIjljOTY5ZDg5LTBkZGYtNDlkMC1hYzcxLTI3MmU3YmY5NjkwMCJdLCJpYXQiOjE2MDYyNzE1NDYsImV4cCI6MTYwNjI3MjQ0Nn0.l2_5iqfEaC68UySTQNoDx2sfzn031tHiTdm2kZoNkWQ\",\n\t\t\t\t\t\t\t\t\"type\": \"text\",\n\t\t\t\t\t\t\t\t\"disabled\": true\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"url\": {\n\t\t\t\t\t\t\t\"raw\": \"http://localhost:8080/product/1\",\n\t\t\t\t\t\t\t\"protocol\": \"http\",\n\t\t\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\t\t\"product\",\n\t\t\t\t\t\t\t\t\"1\"\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"description\": \"Get a Product\"\n\t\t\t\t\t},\n\t\t\t\t\t\"response\": []\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"name\": \"Delete By ID\",\n\t\t\t\t\t\"request\": {\n\t\t\t\t\t\t\"method\": \"DELETE\",\n\t\t\t\t\t\t\"header\": [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"key\": \"Authorization\",\n\t\t\t\t\t\t\t\t\"value\": \"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1ODk1ODU1NjN9.PtfDS1niGoZ7pV6kplI-_q1fVKLnknQ3IwcrLZhoVCU\",\n\t\t\t\t\t\t\t\t\"type\": \"text\",\n\t\t\t\t\t\t\t\t\"disabled\": true\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"url\": {\n\t\t\t\t\t\t\t\"raw\": \"http://localhost:8080/product/3\",\n\t\t\t\t\t\t\t\"protocol\": \"http\",\n\t\t\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\t\t\"product\",\n\t\t\t\t\t\t\t\t\"3\"\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"description\": \"Delete a Product\"\n\t\t\t\t\t},\n\t\t\t\t\t\"response\": []\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"name\": \"Create\",\n\t\t\t\t\t\"request\": {\n\t\t\t\t\t\t\"method\": \"POST\",\n\t\t\t\t\t\t\"header\": [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"key\": \"Authorization\",\n\t\t\t\t\t\t\t\t\"value\": \"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1ODk1ODU1NjN9.PtfDS1niGoZ7pV6kplI-_q1fVKLnknQ3IwcrLZhoVCU\",\n\t\t\t\t\t\t\t\t\"type\": \"text\",\n\t\t\t\t\t\t\t\t\"disabled\": true\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"body\": {\n\t\t\t\t\t\t\t\"mode\": \"raw\",\n\t\t\t\t\t\t\t\"raw\": \"{\\r\\n    \\\"title\\\": \\\"product-1\\\",\\r\\n    \\\"category_id\\\": 1,\\r\\n    \\\"image_url\\\": \\\"https://images.product1.png\\\",\\r\\n    \\\"price\\\": 42.42,\\r\\n    \\\"description\\\": \\\"a description for product-1\\\"\\r\\n}\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"url\": {\n\t\t\t\t\t\t\t\"raw\": \"http://localhost:8080/product\",\n\t\t\t\t\t\t\t\"protocol\": \"http\",\n\t\t\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\t\t\"product\"\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"description\": \"Create a Product (and assign a category)\"\n\t\t\t\t\t},\n\t\t\t\t\t\"response\": []\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"name\": \"Update (Full)\",\n\t\t\t\t\t\"request\": {\n\t\t\t\t\t\t\"method\": \"PUT\",\n\t\t\t\t\t\t\"header\": [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"key\": \"Authorization\",\n\t\t\t\t\t\t\t\t\"value\": \"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1ODk1ODU1NjN9.PtfDS1niGoZ7pV6kplI-_q1fVKLnknQ3IwcrLZhoVCU\",\n\t\t\t\t\t\t\t\t\"type\": \"text\",\n\t\t\t\t\t\t\t\t\"disabled\": true\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"body\": {\n\t\t\t\t\t\t\t\"mode\": \"raw\",\n\t\t\t\t\t\t\t\"raw\": \"{\\r\\n\\t\\\"id\\\":19,\\r\\n    \\\"title\\\": \\\"product-9-new\\\",\\r\\n    \\\"category_id\\\": 1,\\r\\n    \\\"image_url\\\": \\\"https://images.product19.png\\\",\\r\\n    \\\"price\\\": 20,\\r\\n    \\\"description\\\": \\\"a description for product-9-new\\\"\\r\\n}\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"url\": {\n\t\t\t\t\t\t\t\"raw\": \"http://localhost:8080/product\",\n\t\t\t\t\t\t\t\"protocol\": \"http\",\n\t\t\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\t\t\"product\"\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"description\": \"Update a Product (full-update)\"\n\t\t\t\t\t},\n\t\t\t\t\t\"response\": []\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"name\": \"Update (Partial)\",\n\t\t\t\t\t\"request\": {\n\t\t\t\t\t\t\"method\": \"PATCH\",\n\t\t\t\t\t\t\"header\": [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"key\": \"Authorization\",\n\t\t\t\t\t\t\t\t\"value\": \"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1ODk1ODU1NjN9.PtfDS1niGoZ7pV6kplI-_q1fVKLnknQ3IwcrLZhoVCU\",\n\t\t\t\t\t\t\t\t\"type\": \"text\",\n\t\t\t\t\t\t\t\t\"disabled\": true\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"body\": {\n\t\t\t\t\t\t\t\"mode\": \"raw\",\n\t\t\t\t\t\t\t\"raw\": \"{\\r\\n    \\\"title\\\": \\\"product-9-new-title\\\"\\r\\n}\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"url\": {\n\t\t\t\t\t\t\t\"raw\": \"http://localhost:8080/product/9\",\n\t\t\t\t\t\t\t\"protocol\": \"http\",\n\t\t\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\t\t\"product\",\n\t\t\t\t\t\t\t\t\"9\"\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"description\": \"Update a Product (partially)\"\n\t\t\t\t\t},\n\t\t\t\t\t\"response\": []\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"description\": \"Product Client API\",\n\t\t\t\"protocolProfileBehavior\": {}\n\t\t},\n\t\t{\n\t\t\t\"name\": \"Get Token\",\n\t\t\t\"request\": {\n\t\t\t\t\"method\": \"GET\",\n\t\t\t\t\"header\": [],\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"raw\": \"http://localhost:8080/token\",\n\t\t\t\t\t\"protocol\": \"http\",\n\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t],\n\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\"token\"\n\t\t\t\t\t]\n\t\t\t\t},\n\t\t\t\t\"description\": \"Get Token to access \\\"write\\\" (create, update and delete) endpoints\"\n\t\t\t},\n\t\t\t\"response\": []\n\t\t}\n\t],\n\t\"auth\": {\n\t\t\"type\": \"bearer\",\n\t\t\"bearer\": [\n\t\t\t{\n\t\t\t\t\"key\": \"token\",\n\t\t\t\t\"value\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2lyaXMtZ28uY29tIiwiYXVkIjpbIjljOTY5ZDg5LTBkZGYtNDlkMC1hYzcxLTI3MmU3YmY5NjkwMCJdLCJpYXQiOjE2MDYyNzE1NDYsImV4cCI6MTYwNjI3MjQ0Nn0.l2_5iqfEaC68UySTQNoDx2sfzn031tHiTdm2kZoNkWQ\",\n\t\t\t\t\"type\": \"string\"\n\t\t\t}\n\t\t]\n\t},\n\t\"event\": [\n\t\t{\n\t\t\t\"listen\": \"prerequest\",\n\t\t\t\"script\": {\n\t\t\t\t\"id\": \"f27c3c2d-efdc-4922-b70c-258784a1d59b\",\n\t\t\t\t\"type\": \"text/javascript\",\n\t\t\t\t\"exec\": [\n\t\t\t\t\t\"\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"listen\": \"test\",\n\t\t\t\"script\": {\n\t\t\t\t\"id\": \"44d94797-9cc6-4ecd-adc5-7ad5329d79c4\",\n\t\t\t\t\"type\": \"text/javascript\",\n\t\t\t\t\"exec\": [\n\t\t\t\t\t\"\"\n\t\t\t\t]\n\t\t\t}\n\t\t}\n\t],\n\t\"variable\": [\n\t\t{\n\t\t\t\"id\": \"38156b9f-e623-4974-a315-51c931670f23\",\n\t\t\t\"key\": \"token\",\n\t\t\t\"value\": \"token\"\n\t\t}\n\t],\n\t\"protocolProfileBehavior\": {}\n}"
  },
  {
    "path": "_examples/database/mysql/migration/api_product/create_product.json",
    "content": "{\n    \"title\": \"product-1\",\n    \"category_id\": 3,\n    \"image_url\": \"https://images.product1.png\",\n    \"price\": 42.42,\n    \"description\": \"a description for product-1\"\n}"
  },
  {
    "path": "_examples/database/mysql/migration/api_product/update_partial_product.json",
    "content": "{\n    \"title\": \"product-19-new-title\"\n}"
  },
  {
    "path": "_examples/database/mysql/migration/api_product/update_product.json",
    "content": "{\n\t\"id\":19,\n    \"title\": \"product-19\",\n    \"category_id\": 3,\n    \"image_url\": \"https://images.product19.png\",\n    \"price\": 20,\n    \"description\": \"a description for product-19\"\n}"
  },
  {
    "path": "_examples/database/mysql/migration/db.sql",
    "content": "CREATE DATABASE IF NOT EXISTS myapp DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;\n\nUSE myapp;\n\nSET NAMES utf8mb4;\nSET FOREIGN_KEY_CHECKS = 0;\n\nDROP TABLE IF EXISTS categories;\nCREATE TABLE categories  (\n  id int(11) NOT NULL AUTO_INCREMENT,\n  title varchar(255) NOT NULL,\n  position int(11) NOT NULL,\n  image_url varchar(255) NOT NULL,\n  created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (id)\n);\n\nDROP TABLE IF EXISTS products;\nCREATE TABLE products  (\n  id int(11) NOT NULL AUTO_INCREMENT,\n  category_id int,\n  title varchar(255) NOT NULL,\n  image_url varchar(255) NOT NULL,\n  price decimal(10,2) NOT NULL,\n  description text NOT NULL,\n  created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (id),\n  FOREIGN KEY (category_id) REFERENCES categories(id)\n);\n\nSET FOREIGN_KEY_CHECKS = 1;"
  },
  {
    "path": "_examples/database/mysql/service/category_service.go",
    "content": "package service\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"reflect\"\n\n\t\"myapp/entity\"\n\t\"myapp/sql\"\n)\n\n// CategoryService represents the category entity service.\n// Note that the given entity (request) should be already validated\n// before service's calls.\ntype CategoryService struct {\n\t*sql.Service\n}\n\n// NewCategoryService returns a new category service to communicate with the database.\nfunc NewCategoryService(db sql.Database) *CategoryService {\n\treturn &CategoryService{Service: sql.NewService(db, new(entity.Category))}\n}\n\n// Insert stores a category to the database and returns its ID.\nfunc (s *CategoryService) Insert(ctx context.Context, e entity.Category) (int64, error) {\n\tif e.Title == \"\" || e.ImageURL == \"\" {\n\t\treturn 0, sql.ErrUnprocessable\n\t}\n\n\tq := fmt.Sprintf(`INSERT INTO %s (title, position, image_url)\n\tVALUES (?,?,?);`, e.TableName())\n\n\tres, err := s.DB().Exec(ctx, q, e.Title, e.Position, e.ImageURL)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn res.LastInsertId()\n}\n\n// Update updates a category based on its `ID`.\nfunc (s *CategoryService) Update(ctx context.Context, e entity.Category) (int, error) {\n\tif e.ID == 0 || e.Title == \"\" || e.ImageURL == \"\" {\n\t\treturn 0, sql.ErrUnprocessable\n\t}\n\n\tq := fmt.Sprintf(`UPDATE %s\n    SET\n\t    title = ?,\n\t    position = ?,\n\t    image_url = ?\n\tWHERE %s = ?;`, e.TableName(), e.PrimaryKey())\n\n\tres, err := s.DB().Exec(ctx, q, e.Title, e.Position, e.ImageURL, e.ID)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\tn := sql.GetAffectedRows(res)\n\treturn n, nil\n}\n\n// The updatable fields, separately from that we create for any possible future necessities.\nvar categoryUpdateSchema = map[string]reflect.Kind{\n\t\"title\":     reflect.String,\n\t\"image_url\": reflect.String,\n\t\"position\":  reflect.Int,\n}\n\n// PartialUpdate accepts a key-value map to\n// update the record based on the given \"id\".\nfunc (s *CategoryService) PartialUpdate(ctx context.Context, id int64, attrs map[string]any) (int, error) {\n\treturn s.Service.PartialUpdate(ctx, id, categoryUpdateSchema, attrs)\n}\n"
  },
  {
    "path": "_examples/database/mysql/service/category_service_test.go",
    "content": "package service\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"myapp/entity\"\n\t\"myapp/sql\"\n\n\t\"github.com/DATA-DOG/go-sqlmock\"\n)\n\nfunc TestCategoryServiceInsert(t *testing.T) {\n\tconn, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer conn.Close()\n\n\tdb := &sql.MySQL{Conn: conn}\n\tservice := NewCategoryService(db)\n\tnewCategory := entity.Category{\n\t\tTitle:    \"computer-internet\",\n\t\tPosition: 2,\n\t\tImageURL: \"https://animage\",\n\t}\n\tmock.ExpectExec(\"INSERT INTO categories (title, position, image_url) VALUES (?,?,?);\").\n\t\tWithArgs(newCategory.Title, newCategory.Position, newCategory.ImageURL).WillReturnResult(sqlmock.NewResult(1, 1))\n\n\tid, err := service.Insert(context.TODO(), newCategory)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif id != 1 {\n\t\tt.Fatalf(\"expected ID to be 1 as this is the first entry\")\n\t}\n\n\tif err = mock.ExpectationsWereMet(); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n"
  },
  {
    "path": "_examples/database/mysql/service/product_service.go",
    "content": "package service\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n\n\t\"myapp/entity\"\n\t\"myapp/sql\"\n)\n\n// ProductService represents the product entity service.\n// Note that the given entity (request) should be already validated\n// before service's calls.\ntype ProductService struct {\n\t*sql.Service\n\trec sql.Record\n}\n\n// NewProductService returns a new product service to communicate with the database.\nfunc NewProductService(db sql.Database) *ProductService {\n\treturn &ProductService{Service: sql.NewService(db, new(entity.Product))}\n}\n\n// Insert stores a product to the database and returns its ID.\nfunc (s *ProductService) Insert(ctx context.Context, e entity.Product) (int64, error) {\n\tif !e.ValidateInsert() {\n\t\treturn 0, sql.ErrUnprocessable\n\t}\n\n\tq := fmt.Sprintf(`INSERT INTO %s (category_id, title, image_url, price, description)\n\tVALUES (?,?,?,?,?);`, e.TableName())\n\n\tres, err := s.DB().Exec(ctx, q, e.CategoryID, e.Title, e.ImageURL, e.Price, e.Description)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn res.LastInsertId()\n}\n\n// BatchInsert inserts one or more products at once and returns the total length created.\nfunc (s *ProductService) BatchInsert(ctx context.Context, products []entity.Product) (int, error) {\n\tif len(products) == 0 {\n\t\treturn 0, nil\n\t}\n\n\tvar (\n\t\tvaluesLines []string\n\t\targs        []any\n\t)\n\n\tfor _, p := range products {\n\t\tif !p.ValidateInsert() {\n\t\t\t// all products should be \"valid\", we don't skip, we cancel.\n\t\t\treturn 0, sql.ErrUnprocessable\n\t\t}\n\n\t\tvaluesLines = append(valuesLines, \"(?,?,?,?,?)\")\n\t\targs = append(args, []any{p.CategoryID, p.Title, p.ImageURL, p.Price, p.Description}...)\n\t}\n\n\tq := fmt.Sprintf(\"INSERT INTO %s (category_id, title, image_url, price, description) VALUES %s;\",\n\t\ts.RecordInfo().TableName(),\n\t\tstrings.Join(valuesLines, \", \"))\n\n\tres, err := s.DB().Exec(ctx, q, args...)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\tn := sql.GetAffectedRows(res)\n\treturn n, nil\n}\n\n// Update updates a product based on its `ID` from the database\n// and returns the affected numbrer (0 when nothing changed otherwise 1).\nfunc (s *ProductService) Update(ctx context.Context, e entity.Product) (int, error) {\n\tq := fmt.Sprintf(`UPDATE %s\n    SET\n\t    category_id = ?,\n\t    title = ?,\n\t    image_url = ?,\n\t    price = ?,\n\t    description = ?\n\tWHERE %s = ?;`, e.TableName(), e.PrimaryKey())\n\n\tres, err := s.DB().Exec(ctx, q, e.CategoryID, e.Title, e.ImageURL, e.Price, e.Description, e.ID)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\tn := sql.GetAffectedRows(res)\n\treturn n, nil\n}\n\nvar productUpdateSchema = map[string]reflect.Kind{\n\t\"category_id\": reflect.Int,\n\t\"title\":       reflect.String,\n\t\"image_url\":   reflect.String,\n\t\"price\":       reflect.Float32,\n\t\"description\": reflect.String,\n}\n\n// PartialUpdate accepts a key-value map to\n// update the record based on the given \"id\".\nfunc (s *ProductService) PartialUpdate(ctx context.Context, id int64, attrs map[string]any) (int, error) {\n\treturn s.Service.PartialUpdate(ctx, id, productUpdateSchema, attrs)\n}\n"
  },
  {
    "path": "_examples/database/mysql/sql/mysql.go",
    "content": "package sql\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"fmt\"\n\n\t_ \"github.com/go-sql-driver/mysql\" // lint: mysql driver.\n)\n\n// MySQL holds the underline connection of a MySQL (or MariaDB) database.\n// See the `ConnectMySQL` package-level function.\ntype MySQL struct {\n\tConn *sql.DB\n}\n\nvar _ Database = (*MySQL)(nil)\n\nvar (\n\t// DefaultCharset default charset parameter for new databases.\n\tDefaultCharset = \"utf8mb4\"\n\t// DefaultCollation default collation parameter for new databases.\n\tDefaultCollation = \"utf8mb4_unicode_ci\"\n)\n\n// ConnectMySQL returns a new ready to use MySQL Database instance.\n// Accepts a single argument of \"dsn\", i.e:\n// username:password@tcp(localhost:3306)/myapp?parseTime=true&charset=utf8mb4&collation=utf8mb4_unicode_ci\nfunc ConnectMySQL(dsn string) (*MySQL, error) {\n\tconn, err := sql.Open(\"mysql\", dsn)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\terr = conn.Ping()\n\tif err != nil {\n\t\tconn.Close()\n\t\treturn nil, err\n\t}\n\n\treturn &MySQL{\n\t\tConn: conn,\n\t}, nil\n}\n\n// CreateDatabase executes the CREATE DATABASE query.\nfunc (db *MySQL) CreateDatabase(database string) error {\n\tq := fmt.Sprintf(\"CREATE DATABASE %s DEFAULT CHARSET = %s COLLATE = %s;\", database, DefaultCharset, DefaultCollation)\n\t_, err := db.Conn.Exec(q)\n\treturn err\n}\n\n// Drop executes the DROP DATABASE query.\nfunc (db *MySQL) Drop(database string) error {\n\tq := fmt.Sprintf(\"DROP DATABASE %s;\", database)\n\t_, err := db.Conn.Exec(q)\n\treturn err\n}\n\n// Select performs the SELECT query for this database (dsn database name is required).\nfunc (db *MySQL) Select(ctx context.Context, dest any, query string, args ...any) error {\n\trows, err := db.Conn.QueryContext(ctx, query, args...)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer rows.Close()\n\n\tif scannable, ok := dest.(Scannable); ok {\n\t\treturn scannable.Scan(rows)\n\t}\n\n\tif !rows.Next() {\n\t\treturn ErrNoRows\n\t}\n\treturn rows.Scan(dest)\n\n\t/* Uncomment this and pass a slice if u want to see reflection powers <3\n\tv, ok := dest.(reflect.Value)\n\tif !ok {\n\t\tv = reflect.Indirect(reflect.ValueOf(dest))\n\t}\n\n\tsliceTyp := v.Type()\n\n\tif sliceTyp.Kind() != reflect.Slice {\n\t\tsliceTyp = reflect.SliceOf(sliceTyp)\n\t}\n\n\tsliceElementTyp := deref(sliceTyp.Elem())\n\tfor rows.Next() {\n\t\tobj := reflect.New(sliceElementTyp)\n\t\tobj.Interface().(Scannable).Scan(rows)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tv.Set(reflect.Append(v, reflect.Indirect(obj)))\n\t}\n\t*/\n}\n\n// Get same as `Select` but it moves the cursor to the first result.\nfunc (db *MySQL) Get(ctx context.Context, dest any, query string, args ...any) error {\n\trows, err := db.Conn.QueryContext(ctx, query, args...)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer rows.Close()\n\tif !rows.Next() {\n\t\treturn ErrNoRows\n\t}\n\n\tif scannable, ok := dest.(Scannable); ok {\n\t\treturn scannable.Scan(rows)\n\t}\n\n\treturn rows.Scan(dest)\n}\n\n// Exec executes a query. It does not return any rows.\n// Use the first output parameter to count the affected rows on UPDATE, INSERT, or DELETE.\nfunc (db *MySQL) Exec(ctx context.Context, query string, args ...any) (sql.Result, error) {\n\treturn db.Conn.ExecContext(ctx, query, args...)\n}\n"
  },
  {
    "path": "_examples/database/mysql/sql/service.go",
    "content": "package sql\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net/url\"\n\t\"reflect\"\n\t\"strconv\"\n\t\"strings\"\n)\n\n// Service holder for common queries.\n// Note: each entity service keeps its own base Service instance.\ntype Service struct {\n\tdb  Database\n\trec Record // see `Count`, `List` and `DeleteByID` methods.\n}\n\n// NewService returns a new (SQL) base service for common operations.\nfunc NewService(db Database, of Record) *Service {\n\treturn &Service{db: db, rec: of}\n}\n\n// DB exposes the database instance.\nfunc (s *Service) DB() Database {\n\treturn s.db\n}\n\n// RecordInfo returns the record info provided through `NewService`.\nfunc (s *Service) RecordInfo() Record {\n\treturn s.rec\n}\n\n// ErrNoRows is returned when GET doesn't return a row.\n// A shortcut of sql.ErrNoRows.\nvar ErrNoRows = sql.ErrNoRows\n\n// GetByID binds a single record from the databases to the \"dest\".\nfunc (s *Service) GetByID(ctx context.Context, dest any, id int64) error {\n\tq := fmt.Sprintf(\"SELECT * FROM %s WHERE %s = ? LIMIT 1\", s.rec.TableName(), s.rec.PrimaryKey())\n\terr := s.db.Get(ctx, dest, q, id)\n\treturn err\n\t// if err != nil {\n\t// \tif err == sql.ErrNoRows {\n\t// \t\treturn false, nil\n\t// \t}\n\n\t// \treturn false, err\n\t// }\n\n\t// return true, nil\n}\n\n// Count returns the total records count in the table.\nfunc (s *Service) Count(ctx context.Context) (total int64, err error) {\n\tq := fmt.Sprintf(\"SELECT COUNT(DISTINCT %s) FROM %s\", s.rec.PrimaryKey(), s.rec.TableName())\n\tif err = s.db.Select(ctx, &total, q); err == sql.ErrNoRows {\n\t\terr = nil\n\t}\n\treturn\n}\n\n// ListOptions holds the options to be passed on the `Service.List` method.\ntype ListOptions struct {\n\tTable         string // the table name.\n\tOffset        uint64 // inclusive.\n\tLimit         uint64\n\tOrderByColumn string\n\tOrder         string // \"ASC\" or \"DESC\" (could be a bool type instead).\n\tWhereColumn   string\n\tWhereValue    any\n}\n\n// Where accepts a column name and column value to set\n// on the WHERE clause of the result query.\n// It returns a new `ListOptions` value.\n// Note that this is a basic implementation which just takes care our current needs.\nfunc (opt ListOptions) Where(colName string, colValue any) ListOptions {\n\topt.WhereColumn = colName\n\topt.WhereValue = colValue\n\treturn opt\n}\n\n// BuildQuery returns the query and the arguments that\n// should be form a SELECT command.\nfunc (opt ListOptions) BuildQuery() (q string, args []any) {\n\tq = fmt.Sprintf(\"SELECT * FROM %s\", opt.Table)\n\n\tif opt.WhereColumn != \"\" && opt.WhereValue != nil {\n\t\tq += fmt.Sprintf(\" WHERE %s = ?\", opt.WhereColumn)\n\t\targs = append(args, opt.WhereValue)\n\t}\n\n\tif opt.OrderByColumn != \"\" {\n\t\tq += fmt.Sprintf(\" ORDER BY %s %s\", opt.OrderByColumn, ParseOrder(opt.Order))\n\t}\n\n\tif opt.Limit > 0 {\n\t\tq += fmt.Sprintf(\" LIMIT %d\", opt.Limit) // offset below.\n\t}\n\n\tif opt.Offset > 0 {\n\t\tq += fmt.Sprintf(\" OFFSET %d\", opt.Offset)\n\t}\n\n\treturn\n}\n\n// const defaultLimit = 30 // default limit if not set.\n\n// ParseListOptions returns a `ListOptions` from a map[string][]string.\nfunc ParseListOptions(q url.Values) ListOptions {\n\toffset, _ := strconv.ParseUint(q.Get(\"offset\"), 10, 64)\n\tlimit, _ := strconv.ParseUint(q.Get(\"limit\"), 10, 64)\n\torder := q.Get(\"order\") // empty, asc(...) or desc(...).\n\n\treturn ListOptions{Offset: offset, Limit: limit, Order: order}\n}\n\n// List binds one or more records from the database to the \"dest\".\n// If the record supports ordering then it will sort by the `Sorted.OrderBy` column name(s).\n// Use the \"order\" input parameter to set a descending order (\"DESC\").\nfunc (s *Service) List(ctx context.Context, dest any, opts ListOptions) error {\n\t// Set table and order by column from record info for `List` by options\n\t// so it can be more flexible to perform read-only calls of other table's too.\n\tif opts.Table == \"\" {\n\t\t// If missing then try to set it by record info.\n\t\topts.Table = s.rec.TableName()\n\t}\n\n\tif b, ok := s.rec.(Sorted); ok {\n\t\topts.OrderByColumn = b.SortBy()\n\t}\n\n\tq, args := opts.BuildQuery()\n\treturn s.db.Select(ctx, dest, q, args...)\n}\n\n// DeleteByID removes a single record of \"dest\" from the database.\nfunc (s *Service) DeleteByID(ctx context.Context, id int64) (int, error) {\n\tq := fmt.Sprintf(\"DELETE FROM %s WHERE %s = ? LIMIT 1\", s.rec.TableName(), s.rec.PrimaryKey())\n\tres, err := s.db.Exec(ctx, q, id)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn GetAffectedRows(res), nil\n}\n\n// ErrUnprocessable indicates error caused by invalid entity (entity's key-values).\n// The syntax of the request entity is correct, but it was unable to process the contained instructions\n// e.g. empty or unsupported value.\n//\n// See `../service/XService.Insert` and `../service/XService.Update`\n// and `PartialUpdate`.\nvar ErrUnprocessable = errors.New(\"invalid entity\")\n\n// PartialUpdate accepts a columns schema and a key-value map to\n// update the record based on the given \"id\".\n// Note: Trivial string, int and boolean type validations are performed here.\nfunc (s *Service) PartialUpdate(ctx context.Context, id int64, schema map[string]reflect.Kind, attrs map[string]any) (int, error) {\n\tif len(schema) == 0 || len(attrs) == 0 {\n\t\treturn 0, nil\n\t}\n\n\tvar (\n\t\tkeyLines []string\n\t\tvalues   []any\n\t)\n\n\tfor key, kind := range schema {\n\t\tv, ok := attrs[key]\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\n\t\tswitch v.(type) {\n\t\tcase string:\n\t\t\tif kind != reflect.String {\n\t\t\t\treturn 0, ErrUnprocessable\n\t\t\t}\n\t\tcase int:\n\t\t\tif kind != reflect.Int {\n\t\t\t\treturn 0, ErrUnprocessable\n\t\t\t}\n\t\tcase bool:\n\t\t\tif kind != reflect.Bool {\n\t\t\t\treturn 0, ErrUnprocessable\n\t\t\t}\n\t\t}\n\n\t\tkeyLines = append(keyLines, fmt.Sprintf(\"%s = ?\", key))\n\t\tvalues = append(values, v)\n\t}\n\n\tif len(values) == 0 {\n\t\treturn 0, nil\n\t}\n\n\tq := fmt.Sprintf(\"UPDATE %s SET %s WHERE %s = ?;\",\n\t\ts.rec.TableName(), strings.Join(keyLines, \", \"), s.rec.PrimaryKey())\n\n\tres, err := s.DB().Exec(ctx, q, append(values, id)...)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\tn := GetAffectedRows(res)\n\treturn n, nil\n}\n\n// GetAffectedRows returns the number of affected rows after\n// a DELETE or UPDATE operation.\nfunc GetAffectedRows(result sql.Result) int {\n\tif result == nil {\n\t\treturn 0\n\t}\n\n\tn, _ := result.RowsAffected()\n\treturn int(n)\n}\n\nconst (\n\tascending  = \"ASC\"\n\tdescending = \"DESC\"\n)\n\n// ParseOrder accept an order string and returns a valid mysql ORDER clause.\n// Defaults to \"ASC\". Two possible outputs: \"ASC\" and \"DESC\".\nfunc ParseOrder(order string) string {\n\torder = strings.TrimSpace(order)\n\tif len(order) >= 4 {\n\t\tif strings.HasPrefix(strings.ToUpper(order), descending) {\n\t\t\treturn descending\n\t\t}\n\t}\n\n\treturn ascending\n}\n"
  },
  {
    "path": "_examples/database/mysql/sql/sql.go",
    "content": "package sql\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n)\n\n// Database is an interface which a database(sql) should implement.\ntype Database interface {\n\tGet(ctx context.Context, dest any, q string, args ...any) error\n\tSelect(ctx context.Context, dest any, q string, args ...any) error\n\tExec(ctx context.Context, q string, args ...any) (sql.Result, error)\n}\n\n// Record should represent a database record.\n// It holds the table name and the primary key.\n// Entities should implement that\n// in order to use the BaseService's methods.\ntype Record interface {\n\tTableName() string  // the table name which record belongs to.\n\tPrimaryKey() string // the primary key of the record.\n}\n\n// Sorted should represent a set of database records\n// that should be rendered with order.\n//\n// It does NOT support the form of\n// column1 ASC,\n// column2 DESC\n// The OrderBy method should return text in form of:\n// column1\n// or column1, column2\ntype Sorted interface {\n\tSortBy() string // column names separated by comma.\n}\n\n// Scannable for go structs to bind their fields.\ntype Scannable interface {\n\tScan(*sql.Rows) error\n}\n"
  },
  {
    "path": "_examples/database/orm/gorm/REAMDE.md",
    "content": "# GORM\n\nThis example is pull by [#1275 PR](https://github.com/kataras/iris/pull/1275) by [@wuxiaoxiaoshen](https://github.com/wuxiaoxiaoshen).\n\nA more complete and real-world example can be found at the <https://github.com/snowlyg/IrisApiProject> project created by [@snowlyg](https://github.com/snowlyg).\n"
  },
  {
    "path": "_examples/database/orm/gorm/main.go",
    "content": "package main\n\nimport (\n\t\"net/http\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/jinzhu/gorm\"\n\t\"github.com/kataras/iris/v12\"\n\t_ \"github.com/mattn/go-sqlite3\"\n)\n\ntype User struct {\n\tgorm.Model\n\tSalt      string `gorm:\"type:varchar(255)\" json:\"salt\"`\n\tUsername  string `gorm:\"type:varchar(32)\" json:\"username\"`\n\tPassword  string `gorm:\"type:varchar(200);column:password\" json:\"-\"`\n\tLanguages string `gorm:\"type:varchar(200);column:languages\" json:\"languages\"`\n}\n\nfunc (u User) TableName() string {\n\treturn \"gorm_user\"\n}\n\ntype UserSerializer struct {\n\tID        uint      `json:\"id\"`\n\tCreatedAt time.Time `json:\"created_at\"`\n\tUpdatedAt time.Time `json:\"updated_at\"`\n\tSalt      string    `json:\"salt\"`\n\tUserName  string    `json:\"user_name\"`\n\tPassword  string    `json:\"-\"`\n\tLanguages string    `json:\"languages\"`\n}\n\nfunc (self User) Serializer() UserSerializer {\n\treturn UserSerializer{\n\t\tID:        self.ID,\n\t\tCreatedAt: self.CreatedAt.Truncate(time.Second),\n\t\tUpdatedAt: self.UpdatedAt.Truncate(time.Second),\n\t\tSalt:      self.Salt,\n\t\tPassword:  self.Password,\n\t\tLanguages: self.Languages,\n\t\tUserName:  self.Username,\n\t}\n}\n\nfunc main() {\n\tapp := iris.Default()\n\tdb, err := gorm.Open(\"sqlite3\", \"test.db\")\n\tdb.LogMode(true) // show SQL logger\n\tif err != nil {\n\t\tapp.Logger().Fatalf(\"connect to sqlite3 failed\")\n\t\treturn\n\t}\n\tiris.RegisterOnInterrupt(func() {\n\t\tdefer db.Close()\n\t})\n\n\tif os.Getenv(\"ENV\") != \"\" {\n\t\tdb.DropTableIfExists(&User{}) // drop table\n\t}\n\tdb.AutoMigrate(&User{}) // create table: // AutoMigrate run auto migration for given models, will only add missing fields, won't delete/change current data\n\n\tapp.Post(\"/post_user\", func(ctx iris.Context) {\n\t\tvar user User\n\t\tuser = User{\n\t\t\tUsername:  \"gorm\",\n\t\t\tSalt:      \"hash---\",\n\t\t\tPassword:  \"admin\",\n\t\t\tLanguages: \"gorm\",\n\t\t}\n\t\tif err := db.FirstOrCreate(&user); err == nil {\n\t\t\tapp.Logger().Fatalf(\"created one record failed: %s\", err.Error)\n\t\t\tctx.JSON(iris.Map{\n\t\t\t\t\"code\":  http.StatusBadRequest,\n\t\t\t\t\"error\": err.Error,\n\t\t\t})\n\t\t\treturn\n\t\t}\n\t\tctx.JSON(\n\t\t\tiris.Map{\n\t\t\t\t\"code\": http.StatusOK,\n\t\t\t\t\"data\": user.Serializer(),\n\t\t\t})\n\t})\n\n\tapp.Get(\"/get_user/{id:uint}\", func(ctx iris.Context) {\n\t\tvar user User\n\t\tid, _ := ctx.Params().GetUint(\"id\")\n\t\tapp.Logger().Println(id)\n\t\tif err := db.Where(\"id = ?\", int(id)).First(&user).Error; err != nil {\n\t\t\tapp.Logger().Fatalf(\"find one record failed: %t\", err == nil)\n\t\t\tctx.JSON(iris.Map{\n\t\t\t\t\"code\":  http.StatusBadRequest,\n\t\t\t\t\"error\": err.Error,\n\t\t\t})\n\t\t\treturn\n\t\t}\n\t\tctx.JSON(iris.Map{\n\t\t\t\"code\": http.StatusOK,\n\t\t\t\"data\": user.Serializer(),\n\t\t})\n\t})\n\n\tapp.Delete(\"/delete_user/{id:uint}\", func(ctx iris.Context) {\n\t\tid, _ := ctx.Params().GetUint(\"id\")\n\t\tif id == 0 {\n\t\t\tctx.JSON(iris.Map{\n\t\t\t\t\"code\":   http.StatusOK,\n\t\t\t\t\"detail\": \"query param id should not be nil\",\n\t\t\t})\n\t\t\treturn\n\t\t}\n\t\tvar user User\n\t\tif err := db.Where(\"id = ?\", id).First(&user).Error; err != nil {\n\t\t\tapp.Logger().Fatalf(\"record not found\")\n\t\t\tctx.JSON(iris.Map{\n\t\t\t\t\"code\":   http.StatusOK,\n\t\t\t\t\"detail\": err.Error,\n\t\t\t})\n\t\t\treturn\n\t\t}\n\t\tdb.Delete(&user)\n\t\tctx.JSON(iris.Map{\n\t\t\t\"code\": http.StatusOK,\n\t\t\t\"data\": user.Serializer(),\n\t\t})\n\t})\n\n\tapp.Patch(\"/patch_user/{id:uint}\", func(ctx iris.Context) {\n\t\tid, _ := ctx.Params().GetUint(\"id\")\n\t\tif id == 0 {\n\t\t\tctx.JSON(iris.Map{\n\t\t\t\t\"code\":   http.StatusOK,\n\t\t\t\t\"detail\": \"query param id should not be nil\",\n\t\t\t})\n\t\t\treturn\n\t\t}\n\t\tvar user User\n\t\ttx := db.Begin()\n\t\tif err := tx.Where(\"id = ?\", id).First(&user).Error; err != nil {\n\t\t\tapp.Logger().Fatalf(\"record not found\")\n\t\t\tctx.JSON(iris.Map{\n\t\t\t\t\"code\":   http.StatusOK,\n\t\t\t\t\"detail\": err.Error,\n\t\t\t})\n\t\t\treturn\n\t\t}\n\n\t\tvar body patchParam\n\t\tctx.ReadJSON(&body)\n\t\tapp.Logger().Println(body)\n\t\tif err := tx.Model(&user).Updates(map[string]any{\"username\": body.Data.UserName, \"password\": body.Data.Password}).Error; err != nil {\n\t\t\tapp.Logger().Fatalf(\"update record failed\")\n\t\t\ttx.Rollback()\n\t\t\tctx.JSON(iris.Map{\n\t\t\t\t\"code\":  http.StatusBadRequest,\n\t\t\t\t\"error\": err.Error,\n\t\t\t})\n\t\t\treturn\n\t\t}\n\t\ttx.Commit()\n\t\tctx.JSON(iris.Map{\n\t\t\t\"code\": http.StatusOK,\n\t\t\t\"data\": user.Serializer(),\n\t\t})\n\t})\n\n\tapp.Listen(\":8080\")\n}\n\ntype patchParam struct {\n\tData struct {\n\t\tUserName string `json:\"user_name\" form:\"user_name\"`\n\t\tPassword string `json:\"password\" form:\"password\"`\n\t} `json:\"data\"`\n}\n"
  },
  {
    "path": "_examples/database/orm/reform/controllers/person_controller.go",
    "content": "package controllers\n\nimport (\n\t\"net\"\n\t\"time\"\n\n\t\"myapp/models\"\n\n\t\"github.com/kataras/golog\"\n\t\"gopkg.in/reform.v1\"\n)\n\n// PersonController is the model.Person's web controller.\ntype PersonController struct {\n\tDB *reform.DB\n\n\t// Logger and IP fields are automatically binded by the framework.\n\tLogger *golog.Logger // binds to the application's logger.\n\tIP     net.IP        // binds to the client's IP.\n}\n\n// Get handles\n// GET /persons\nfunc (c *PersonController) Get() ([]reform.Struct, error) {\n\treturn c.DB.SelectAllFrom(models.PersonTable, \"\")\n}\n\n// GetBy handles\n// GET /persons/{ID}\nfunc (c *PersonController) GetBy(id int32) (reform.Record, error) {\n\treturn c.DB.FindByPrimaryKeyFrom(models.PersonTable, id)\n}\n\n// Post handles\n// POST /persons with JSON request body of model.Person.\nfunc (c *PersonController) Post(p *models.Person) int {\n\tp.CreatedAt = time.Now()\n\n\tif err := c.DB.Save(p); err != nil {\n\t\tc.Logger.Errorf(\"[%s] create person: %v\", c.IP.String(), err)\n\t\treturn 500 // iris.StatusInternalServerError\n\t}\n\n\tc.Logger.Debugf(\"[%s] create person [%s] succeed\", c.IP.String(), p.Name)\n\n\treturn 201 // iris.StatusCreated\n}\n"
  },
  {
    "path": "_examples/database/orm/reform/go.mod",
    "content": "module myapp\n\ngo 1.25\n\nrequire (\n\tgithub.com/kataras/golog v0.1.12\n\tgithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd\n\tgithub.com/mattn/go-sqlite3 v1.14.33\n\tgopkg.in/reform.v1 v1.5.1\n)\n\nrequire (\n\tgithub.com/BurntSushi/toml v1.6.0 // indirect\n\tgithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect\n\tgithub.com/CloudyKit/jet/v6 v6.3.1 // indirect\n\tgithub.com/Joker/jade v1.1.3 // indirect\n\tgithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 // indirect\n\tgithub.com/andybalholm/brotli v1.2.0 // indirect\n\tgithub.com/aymerick/douceur v0.2.0 // indirect\n\tgithub.com/blang/semver/v4 v4.0.0 // indirect\n\tgithub.com/fatih/structs v1.1.0 // indirect\n\tgithub.com/flosch/pongo2/v4 v4.0.2 // indirect\n\tgithub.com/gobwas/httphead v0.1.0 // indirect\n\tgithub.com/gobwas/pool v0.2.1 // indirect\n\tgithub.com/gobwas/ws v1.4.0 // indirect\n\tgithub.com/golang/snappy v1.0.0 // indirect\n\tgithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a // indirect\n\tgithub.com/google/uuid v1.6.0 // indirect\n\tgithub.com/gorilla/css v1.0.1 // indirect\n\tgithub.com/gorilla/websocket v1.5.3 // indirect\n\tgithub.com/iris-contrib/schema v0.0.6 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/kataras/blocks v0.0.8 // indirect\n\tgithub.com/kataras/neffos v0.0.24 // indirect\n\tgithub.com/kataras/pio v0.0.13 // indirect\n\tgithub.com/kataras/sitemap v0.0.6 // indirect\n\tgithub.com/kataras/tunnel v0.0.4 // indirect\n\tgithub.com/klauspost/compress v1.18.2 // indirect\n\tgithub.com/kr/pretty v0.3.1 // indirect\n\tgithub.com/mailgun/raymond/v2 v2.0.48 // indirect\n\tgithub.com/mailru/easyjson v0.9.1 // indirect\n\tgithub.com/mediocregopher/radix/v3 v3.8.1 // indirect\n\tgithub.com/microcosm-cc/bluemonday v1.0.27 // indirect\n\tgithub.com/nats-io/nats.go v1.40.1 // indirect\n\tgithub.com/nats-io/nkeys v0.4.9 // indirect\n\tgithub.com/nats-io/nuid v1.0.1 // indirect\n\tgithub.com/rogpeppe/go-internal v1.10.0 // indirect\n\tgithub.com/russross/blackfriday/v2 v2.1.0 // indirect\n\tgithub.com/schollz/closestmatch v2.1.0+incompatible // indirect\n\tgithub.com/sirupsen/logrus v1.8.1 // indirect\n\tgithub.com/stretchr/testify v1.11.1 // indirect\n\tgithub.com/tdewolff/minify/v2 v2.24.8 // indirect\n\tgithub.com/tdewolff/parse/v2 v2.8.5 // indirect\n\tgithub.com/valyala/bytebufferpool v1.0.0 // indirect\n\tgithub.com/vmihailenco/msgpack/v5 v5.4.1 // indirect\n\tgithub.com/vmihailenco/tagparser/v2 v2.0.0 // indirect\n\tgithub.com/yosssi/ace v0.0.5 // indirect\n\tgolang.org/x/crypto v0.47.0 // indirect\n\tgolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 // indirect\n\tgolang.org/x/net v0.49.0 // indirect\n\tgolang.org/x/sys v0.40.0 // indirect\n\tgolang.org/x/text v0.33.0 // indirect\n\tgolang.org/x/time v0.14.0 // indirect\n\tgolang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect\n\tgoogle.golang.org/protobuf v1.36.11 // indirect\n\tgopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect\n\tgopkg.in/ini.v1 v1.67.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n"
  },
  {
    "path": "_examples/database/orm/reform/go.sum",
    "content": "github.com/AlekSi/pointer v1.1.0 h1:SSDMPcXD9jSl8FPy9cRzoRaMJtm9g9ggGTxecRUbQoI=\ngithub.com/AlekSi/pointer v1.1.0/go.mod h1:y7BvfRI3wXPWKXEBhU71nbnIEEZX0QTSB2Bj48UJIZE=\ngithub.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=\ngithub.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=\ngithub.com/CloudyKit/jet/v6 v6.3.1 h1:6IAo5Cx21xrHVaR8zzXN5gJatKV/wO7Nf6bfCnCSbUw=\ngithub.com/CloudyKit/jet/v6 v6.3.1/go.mod h1:lf8ksdNsxZt7/yH/3n4vJQWA9RUq4wpaHtArHhGVMOw=\ngithub.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc=\ngithub.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=\ngithub.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=\ngithub.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=\ngithub.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 h1:dG7/gLroJGht/jSQtHiLvT48Hxn+crbmvyItZC8cWOs=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05/go.mod h1:NYezi6wtnJtBm5btoprXc5SvAdqH0XTXWnUup0MptAI=\ngithub.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=\ngithub.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=\ngithub.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=\ngithub.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=\ngithub.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=\ngithub.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=\ngithub.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=\ngithub.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=\ngithub.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=\ngithub.com/brianvoe/gofakeit v3.18.0+incompatible h1:wDOmHc9DLG4nRjUVVaxA+CEglKOW72Y5+4WNxUIkjM8=\ngithub.com/brianvoe/gofakeit v3.18.0+incompatible/go.mod h1:kfwdRA90vvNhPutZWfH7WPaDzUjz+CZFqG+rPkOjGOc=\ngithub.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/denisenkom/go-mssqldb v0.9.0 h1:RSohk2RsiZqLZ0zCjtfn3S4Gp4exhpBWHyQ7D0yGjAk=\ngithub.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=\ngithub.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=\ngithub.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=\ngithub.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=\ngithub.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=\ngithub.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw=\ngithub.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8=\ngithub.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=\ngithub.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=\ngithub.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=\ngithub.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=\ngithub.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=\ngithub.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=\ngithub.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=\ngithub.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=\ngithub.com/gobwas/ws v1.4.0 h1:CTaoG1tojrh4ucGPcoJFiAQUAsEWekEWvLy7GsVNqGs=\ngithub.com/gobwas/ws v1.4.0/go.mod h1:G3gNqMNtPppf5XUz7O4shetPpcZ1VJ7zt18dlUeakrc=\ngithub.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=\ngithub.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=\ngithub.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=\ngithub.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=\ngithub.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a h1:l7A0loSszR5zHd/qK53ZIHMO8b3bBSmENnQ6eKnUT0A=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=\ngithub.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=\ngithub.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=\ngithub.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=\ngithub.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=\ngithub.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2 h1:T9THsdP1woyAqKHwjkEsbCnMefsAFvk8iJJKokcJ3Go=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2/go.mod h1:JLDgIqnFy5loDSUv1OA2j0mb6p/rDhiCqigP22Uq9xE=\ngithub.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw=\ngithub.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA=\ngithub.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ=\ngithub.com/jackc/pgx v3.6.2+incompatible h1:2zP5OD7kiyR3xzRYMhOcXVvkDZsImVXfj+yIyTQf3/o=\ngithub.com/jackc/pgx v3.6.2+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/kataras/blocks v0.0.8 h1:MrpVhoFTCR2v1iOOfGng5VJSILKeZZI+7NGfxEh3SUM=\ngithub.com/kataras/blocks v0.0.8/go.mod h1:9Jm5zx6BB+06NwA+OhTbHW1xkMOYxahnqTN5DveZ2Yg=\ngithub.com/kataras/golog v0.1.12 h1:Bu7I/G4ilJlbfzjmU39O9N+2uO1pBcMK045fzZ4ytNg=\ngithub.com/kataras/golog v0.1.12/go.mod h1:wrGSbOiBqbQSQznleVNX4epWM8rl9SJ/rmEacl0yqy4=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd h1:oMsQgqKACbUdlaIWnN+EZzzAnHkw90hE/y/R0BSij2U=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd/go.mod h1:yucjtDl9Vn3OctWM1fi0MuM9OckemC7kEreFa9QtPF8=\ngithub.com/kataras/neffos v0.0.24 h1:S3lHqJopCfXN285VdlbGeOj+Id83u4xdQKToa+w1vW0=\ngithub.com/kataras/neffos v0.0.24/go.mod h1:/3K9zQ0yEC5/xUiSQx46ToWa3xneGfUo/nMit/F5g+U=\ngithub.com/kataras/pio v0.0.13 h1:x0rXVX0fviDTXOOLOmr4MUxOabu1InVSTu5itF8CXCM=\ngithub.com/kataras/pio v0.0.13/go.mod h1:k3HNuSw+eJ8Pm2lA4lRhg3DiCjVgHlP8hmXApSej3oM=\ngithub.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY=\ngithub.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4=\ngithub.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA=\ngithub.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw=\ngithub.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=\ngithub.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/lib/pq v1.8.0 h1:9xohqzkUwzR4Ga4ivdTcawVS89YSDVxXMa3xJX3cGzg=\ngithub.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=\ngithub.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=\ngithub.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=\ngithub.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8=\ngithub.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=\ngithub.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=\ngithub.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=\ngithub.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=\ngithub.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=\ngithub.com/mattn/go-sqlite3 v1.14.33 h1:A5blZ5ulQo2AtayQ9/limgHEkFreKj1Dv226a1K73s0=\ngithub.com/mattn/go-sqlite3 v1.14.33/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=\ngithub.com/mediocregopher/radix/v3 v3.8.1 h1:rOkHflVuulFKlwsLY01/M2cM2tWCjDoETcMqKbAWu1M=\ngithub.com/mediocregopher/radix/v3 v3.8.1/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8=\ngithub.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=\ngithub.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=\ngithub.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=\ngithub.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=\ngithub.com/nats-io/nats.go v1.40.1 h1:MLjDkdsbGUeCMKFyCFoLnNn/HDTqcgVa3EQm+pMNDPk=\ngithub.com/nats-io/nats.go v1.40.1/go.mod h1:wV73x0FSI/orHPSYoyMeJB+KajMDoWyXmFaRrrYaaTo=\ngithub.com/nats-io/nkeys v0.4.9 h1:qe9Faq2Gxwi6RZnZMXfmGMZkg3afLLOtrU+gDZJ35b0=\ngithub.com/nats-io/nkeys v0.4.9/go.mod h1:jcMqs+FLG+W5YO36OX6wFIFcmpdAns+w1Wm6D3I/evE=\ngithub.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=\ngithub.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=\ngithub.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=\ngithub.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=\ngithub.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=\ngithub.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=\ngithub.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=\ngithub.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk=\ngithub.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=\ngithub.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=\ngithub.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=\ngithub.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=\ngithub.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=\ngithub.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/tdewolff/minify/v2 v2.24.8 h1:58/VjsbevI4d5FGV0ZSuBrHMSSkH4MCH0sIz/eKIauE=\ngithub.com/tdewolff/minify/v2 v2.24.8/go.mod h1:0Ukj0CRpo/sW/nd8uZ4ccXaV1rEVIWA3dj8U7+Shhfw=\ngithub.com/tdewolff/parse/v2 v2.8.5 h1:ZmBiA/8Do5Rpk7bDye0jbbDUpXXbCdc3iah4VeUvwYU=\ngithub.com/tdewolff/parse/v2 v2.8.5/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=\ngithub.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE=\ngithub.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=\ngithub.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=\ngithub.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=\ngithub.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=\ngithub.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=\ngithub.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=\ngithub.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=\ngithub.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA=\ngithub.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0=\ngithub.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=\ngithub.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=\ngithub.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=\ngolang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=\ngolang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=\ngolang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=\ngolang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=\ngolang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=\ngolang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=\ngolang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=\ngolang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=\ngolang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY=\ngolang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=\ngoogle.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=\ngoogle.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=\ngopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/reform.v1 v1.5.1 h1:7vhDFW1n1xAPC6oDSvIvVvpRkaRpXlxgJ4QB4s3aDdo=\ngopkg.in/reform.v1 v1.5.1/go.mod h1:AIv0CbDRJ0ljQwptGeaIXfpDRo02uJwTq92aMFELEeU=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nmoul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs=\nmoul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE=\n"
  },
  {
    "path": "_examples/database/orm/reform/main.go",
    "content": "package main\n\n/*\n$ go get gopkg.in/reform.v1/reform\n$ go generate ./models\n$ go run .\n\nRead more at: https://github.com/go-reform/reform\n*/\n\nimport (\n\t\"database/sql\"\n\n\t\"myapp/controllers\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/mvc\"\n\n\t_ \"github.com/mattn/go-sqlite3\"\n\t\"gopkg.in/reform.v1\"\n\t\"gopkg.in/reform.v1/dialects/sqlite3\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.Logger().SetLevel(\"debug\")\n\n\tsqlDB, err := sql.Open(\"sqlite3\", \"./myapp.db\")\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer sqlDB.Close()\n\tsqlStmt := `\n\tdrop table people;\n\tcreate table people (id integer not null primary key, name text, email text, created_at datetime not null, updated_at datetime null);\n\tdelete from people;\n\t`\n\t_, err = sqlDB.Exec(sqlStmt)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tdb := reform.NewDB(sqlDB, sqlite3.Dialect, reform.NewPrintfLogger(app.Logger().Debugf))\n\n\tmvcApp := mvc.New(app.Party(\"/persons\"))\n\tmvcApp.Register(db)\n\tmvcApp.Handle(new(controllers.PersonController))\n\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/database/orm/reform/models/person.go",
    "content": "//go:generate reform\npackage models\n\nimport \"time\"\n\n//reform:people\ntype Person struct {\n\tID        int32      `reform:\"id,pk\" json:\"id\"`\n\tName      string     `reform:\"name\" json:\"name\"`\n\tEmail     *string    `reform:\"email\" json:\"email\"`\n\tCreatedAt time.Time  `reform:\"created_at\" json:\"created_at\"`\n\tUpdatedAt *time.Time `reform:\"updated_at\" json:\"updated_at\"`\n}\n"
  },
  {
    "path": "_examples/database/orm/reform/models/person_reform.go",
    "content": "// Code generated by gopkg.in/reform.v1. DO NOT EDIT.\n\npackage models\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\n\t\"gopkg.in/reform.v1\"\n\t\"gopkg.in/reform.v1/parse\"\n)\n\ntype personTableType struct {\n\ts parse.StructInfo\n\tz []any\n}\n\n// Schema returns a schema name in SQL database (\"\").\nfunc (v *personTableType) Schema() string {\n\treturn v.s.SQLSchema\n}\n\n// Name returns a view or table name in SQL database (\"people\").\nfunc (v *personTableType) Name() string {\n\treturn v.s.SQLName\n}\n\n// Columns returns a new slice of column names for that view or table in SQL database.\nfunc (v *personTableType) Columns() []string {\n\treturn []string{\"id\", \"name\", \"email\", \"created_at\", \"updated_at\"}\n}\n\n// NewStruct makes a new struct for that view or table.\nfunc (v *personTableType) NewStruct() reform.Struct {\n\treturn new(Person)\n}\n\n// NewRecord makes a new record for that table.\nfunc (v *personTableType) NewRecord() reform.Record {\n\treturn new(Person)\n}\n\n// PKColumnIndex returns an index of primary key column for that table in SQL database.\nfunc (v *personTableType) PKColumnIndex() uint {\n\treturn uint(v.s.PKFieldIndex)\n}\n\n// PersonTable represents people view or table in SQL database.\nvar PersonTable = &personTableType{\n\ts: parse.StructInfo{Type: \"Person\", SQLSchema: \"\", SQLName: \"people\", Fields: []parse.FieldInfo{{Name: \"ID\", Type: \"int32\", Column: \"id\"}, {Name: \"Name\", Type: \"string\", Column: \"name\"}, {Name: \"Email\", Type: \"*string\", Column: \"email\"}, {Name: \"CreatedAt\", Type: \"time.Time\", Column: \"created_at\"}, {Name: \"UpdatedAt\", Type: \"*time.Time\", Column: \"updated_at\"}}, PKFieldIndex: 0},\n\tz: new(Person).Values(),\n}\n\n// String returns a string representation of this struct or record.\nfunc (s Person) String() string {\n\tres := make([]string, 5)\n\tres[0] = \"ID: \" + reform.Inspect(s.ID, true)\n\tres[1] = \"Name: \" + reform.Inspect(s.Name, true)\n\tres[2] = \"Email: \" + reform.Inspect(s.Email, true)\n\tres[3] = \"CreatedAt: \" + reform.Inspect(s.CreatedAt, true)\n\tres[4] = \"UpdatedAt: \" + reform.Inspect(s.UpdatedAt, true)\n\treturn strings.Join(res, \", \")\n}\n\n// Values returns a slice of struct or record field values.\n// Returned any values are never untyped nils.\nfunc (s *Person) Values() []any {\n\treturn []any{\n\t\ts.ID,\n\t\ts.Name,\n\t\ts.Email,\n\t\ts.CreatedAt,\n\t\ts.UpdatedAt,\n\t}\n}\n\n// Pointers returns a slice of pointers to struct or record fields.\n// Returned any values are never untyped nils.\nfunc (s *Person) Pointers() []any {\n\treturn []any{\n\t\t&s.ID,\n\t\t&s.Name,\n\t\t&s.Email,\n\t\t&s.CreatedAt,\n\t\t&s.UpdatedAt,\n\t}\n}\n\n// View returns View object for that struct.\nfunc (s *Person) View() reform.View {\n\treturn PersonTable\n}\n\n// Table returns Table object for that record.\nfunc (s *Person) Table() reform.Table {\n\treturn PersonTable\n}\n\n// PKValue returns a value of primary key for that record.\n// Returned any value is never untyped nil.\nfunc (s *Person) PKValue() any {\n\treturn s.ID\n}\n\n// PKPointer returns a pointer to primary key field for that record.\n// Returned any value is never untyped nil.\nfunc (s *Person) PKPointer() any {\n\treturn &s.ID\n}\n\n// HasPK returns true if record has non-zero primary key set, false otherwise.\nfunc (s *Person) HasPK() bool {\n\treturn s.ID != PersonTable.z[PersonTable.s.PKFieldIndex]\n}\n\n// SetPK sets record primary key.\nfunc (s *Person) SetPK(pk any) {\n\tif i64, ok := pk.(int64); ok {\n\t\ts.ID = int32(i64)\n\t} else {\n\t\ts.ID = pk.(int32)\n\t}\n}\n\n// check interfaces\nvar (\n\t_ reform.View   = PersonTable\n\t_ reform.Struct = (*Person)(nil)\n\t_ reform.Table  = PersonTable\n\t_ reform.Record = (*Person)(nil)\n\t_ fmt.Stringer  = (*Person)(nil)\n)\n\nfunc init() {\n\tparse.AssertUpToDate(&PersonTable.s, new(Person))\n}\n"
  },
  {
    "path": "_examples/database/orm/reform/postman_collection.json",
    "content": "{\n\t\"info\": {\n\t\t\"_postman_id\": \"6b66000d-9c04-4d0a-b55c-8a493bf59015\",\n\t\t\"name\": \"iris-reform-example\",\n\t\t\"description\": \"Example API calls for iris reform example.\",\n\t\t\"schema\": \"https://schema.getpostman.com/json/collection/v2.1.0/collection.json\"\n\t},\n\t\"item\": [\n\t\t{\n\t\t\t\"name\": \"http://localhost:8080/persons\",\n\t\t\t\"request\": {\n\t\t\t\t\"method\": \"POST\",\n\t\t\t\t\"header\": [],\n\t\t\t\t\"body\": {\n\t\t\t\t\t\"mode\": \"raw\",\n\t\t\t\t\t\"raw\": \"{\\r\\n    \\\"name\\\": \\\"John\\\",\\r\\n    \\\"email\\\": \\\"example@example.com\\\"\\r\\n}\",\n\t\t\t\t\t\"options\": {\n\t\t\t\t\t\t\"raw\": {\n\t\t\t\t\t\t\t\"language\": \"json\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"raw\": \"http://localhost:8080/persons\",\n\t\t\t\t\t\"protocol\": \"http\",\n\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t],\n\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\"persons\"\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"response\": []\n\t\t},\n\t\t{\n\t\t\t\"name\": \"http://localhost:8080/persons\",\n\t\t\t\"request\": {\n\t\t\t\t\"method\": \"GET\",\n\t\t\t\t\"header\": [],\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"raw\": \"http://localhost:8080/persons\",\n\t\t\t\t\t\"protocol\": \"http\",\n\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t],\n\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\"persons\"\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"response\": []\n\t\t}\n\t],\n\t\"protocolProfileBehavior\": {}\n}"
  },
  {
    "path": "_examples/database/orm/sqlx/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n\n\t\"github.com/jmoiron/sqlx\"\n\t_ \"github.com/mattn/go-sqlite3\"\n)\n\n/*\n\tgo get -u github.com/mattn/go-sqlite3\n\tgo get -u github.com/jmoiron/sqlx\n\n\tIf you're on win64 and you can't install go-sqlite3:\n\t\t1. Download: https://sourceforge.net/projects/mingw-w64/files/latest/download\n\t\t2. Select \"x86_x64\" and \"posix\"\n\t\t3. Add C:\\Program Files\\mingw-w64\\x86_64-7.1.0-posix-seh-rt_v5-rev1\\mingw64\\bin\n\t\tto your PATH env variable.\n\n\tDocs: https://github.com/jmoiron/sqlx\n*/\n\n// Person is our person table structure.\ntype Person struct {\n\tID        int64  `db:\"person_id\"`\n\tFirstName string `db:\"first_name\"`\n\tLastName  string `db:\"last_name\"`\n\tEmail     string\n}\n\nconst schema = `\nCREATE TABLE IF NOT EXISTS person (\n\tperson_id INTEGER PRIMARY KEY,\n\tfirst_name text,\n\tlast_name text,\n\temail text\n);`\n\nfunc main() {\n\tapp := iris.New()\n\n\tdb, err := sqlx.Connect(\"sqlite3\", \"./test.db\")\n\tif err != nil {\n\t\tapp.Logger().Fatalf(\"db failed to initialized: %v\", err)\n\t}\n\tiris.RegisterOnInterrupt(func() {\n\t\tdb.Close()\n\t})\n\n\tdb.MustExec(schema)\n\n\tapp.Get(\"/insert\", func(ctx iris.Context) {\n\t\tres, err := db.NamedExec(`INSERT INTO person (first_name,last_name,email) VALUES (:first,:last,:email)`,\n\t\t\tmap[string]any{\n\t\t\t\t\"first\": \"John\",\n\t\t\t\t\"last\":  \"Doe\",\n\t\t\t\t\"email\": \"johndoe@example.com\",\n\t\t\t})\n\n\t\tif err != nil {\n\t\t\t// Note: on production, don't give the error back to the user.\n\t\t\t// However for the sake of the example we do:\n\t\t\tctx.StopWithError(iris.StatusInternalServerError, err)\n\t\t\treturn\n\t\t}\n\n\t\tid, err := res.LastInsertId()\n\t\tif err != nil {\n\t\t\tctx.StopWithError(iris.StatusInternalServerError, err)\n\t\t\treturn\n\t\t}\n\n\t\tctx.Writef(\"person inserted: id: %d\", id)\n\t})\n\n\tapp.Get(\"/get\", func(ctx iris.Context) {\n\t\t// Select all persons.\n\t\tpeople := []Person{}\n\t\tdb.Select(&people, \"SELECT * FROM person ORDER BY first_name ASC\")\n\t\tif err != nil {\n\t\t\tctx.StopWithError(iris.StatusInternalServerError, err)\n\t\t\treturn\n\t\t}\n\n\t\tif len(people) == 0 {\n\t\t\tctx.Writef(\"no persons found, use /insert first.\")\n\t\t\treturn\n\t\t}\n\n\t\tctx.Writef(\"persons found: %#v\", people)\n\t\t/* Select a single or more with a first name of John from the database:\n\t\tperson := Person{FirstName: \"John\"}\n\t\trows, err := db.NamedQuery(`SELECT * FROM person WHERE first_name=:first_name`, person)\n\t\tif err != nil { ... }\n\t\tdefer rows.Close()\n\t\tfor rows.Next() {\n\t\t\tif err := rows.StructScan(&person); err != nil {\n\t\t\t\tif err == sql.ErrNoRows {\n\t\t\t\t\tctx.StopWithText(iris.StatusNotFound, \"Person: %s not found\", person.FirstName)\n\t\t\t\t} else {\n\t\t\t\t\tctx.StopWithError(iris.StatusInternalServerError, err)\n\t\t\t\t}\n\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\t*/\n\t})\n\n\t// http://localhost:8080/insert\n\t// http://localhost:8080/get\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/database/sqlx/main.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/x/errors\"\n\t\"github.com/kataras/iris/v12/x/sqlx\"\n\n\t_ \"github.com/lib/pq\"\n)\n\nconst (\n\thost     = \"localhost\"\n\tport     = 5432\n\tuser     = \"postgres\"\n\tpassword = \"admin!123\"\n\tdbname   = \"test\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\tdb := mustConnectDB()\n\tmustCreateExtensions(context.Background(), db)\n\tmustCreateTables(context.Background(), db)\n\n\tapp.Post(\"/\", insert(db))\n\tapp.Get(\"/\", list(db))\n\tapp.Get(\"/{event_id:uuid}\", getByID(db))\n\n\t/*\n\t\tcurl --location --request POST 'http://localhost:8080' \\\n\t\t--header 'Content-Type: application/json' \\\n\t\t--data-raw '{\n\t\t    \"name\": \"second_test_event\",\n\t\t    \"data\": {\n\t\t        \"key\": \"value\",\n\t\t\t\t\"year\": 2022\n\t\t    }\n\t\t}'\n\n\t\tcurl --location --request GET 'http://localhost:8080'\n\n\t\tcurl --location --request GET 'http://localhost:8080/4fc0363f-1d1f-4a43-8608-5ed266485645'\n\t*/\n\tapp.Listen(\":8080\")\n}\n\nfunc mustConnectDB() *sql.DB {\n\tconnString := fmt.Sprintf(\"host=%s port=%d user=%s password=%s dbname=%s sslmode=disable\",\n\t\thost, port, user, password, dbname)\n\tdb, err := sql.Open(\"postgres\", connString)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\terr = db.Ping()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treturn db\n}\n\nfunc mustCreateExtensions(ctx context.Context, db *sql.DB) {\n\tquery := `CREATE EXTENSION IF NOT EXISTS pgcrypto;`\n\t_, err := db.ExecContext(ctx, query)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n\nfunc mustCreateTables(ctx context.Context, db *sql.DB) {\n\tquery := `CREATE TABLE IF NOT EXISTS \"events\" (\n\t\t\"id\" uuid PRIMARY KEY NOT NULL DEFAULT gen_random_uuid(),\n\t\t\"created_at\" timestamp(6) DEFAULT now(),\n\t\t\"name\" text COLLATE \"pg_catalog\".\"default\",\n\t\t\"data\" jsonb\n\t  );`\n\n\t_, err := db.ExecContext(ctx, query)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tsqlx.Register(\"events\", Event{})\n}\n\ntype Event struct {\n\tID        string          `json:\"id\"`\n\tCreatedAt time.Time       `json:\"created_at\"`\n\tName      string          `json:\"name\"`\n\tData      json.RawMessage `json:\"data\"`\n\n\tPresenter string `db:\"-\" json:\"-\"`\n}\n\nfunc insertEvent(ctx context.Context, db *sql.DB, evt Event) (id string, err error) {\n\tquery := `INSERT INTO events(name,data) VALUES($1,$2) RETURNING id;`\n\terr = db.QueryRowContext(ctx, query, evt.Name, evt.Data).Scan(&id)\n\treturn\n}\n\nfunc listEvents(ctx context.Context, db *sql.DB) ([]Event, error) {\n\tlist := make([]Event, 0)\n\tquery := `SELECT * FROM events ORDER BY created_at;`\n\trows, err := db.QueryContext(ctx, query)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\t// Not required. See sqlx.DefaultSchema.AutoCloseRows field.\n\t// defer rows.Close()\n\n\tif err = sqlx.Bind(&list, rows); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn list, nil\n}\n\nfunc getEvent(ctx context.Context, db *sql.DB, id string) (evt Event, err error) {\n\tquery := `SELECT * FROM events WHERE id = $1 LIMIT 1;`\n\terr = sqlx.Query(ctx, db, &evt, query, id)\n\treturn\n\t//\n\t// Same as:\n\t//\n\t// rows, err := db.QueryContext(ctx, query, id)\n\t// if err != nil {\n\t// \treturn Event{}, err\n\t// }\n\t//\n\t// var evt Event\n\t// err = sqlx.Bind(&evt, rows)\n\t//\n\t// return evt, err\n}\n\nfunc insert(db *sql.DB) iris.Handler {\n\treturn func(ctx iris.Context) {\n\t\tvar evt Event\n\t\tif err := ctx.ReadJSON(&evt); err != nil {\n\t\t\terrors.InvalidArgument.Details(ctx, \"unable to read body\", err.Error())\n\t\t\treturn\n\t\t}\n\n\t\tid, err := insertEvent(ctx, db, evt)\n\t\tif err != nil {\n\t\t\terrors.Internal.LogErr(ctx, err)\n\t\t\treturn\n\t\t}\n\n\t\tctx.JSON(iris.Map{\"id\": id})\n\t}\n}\n\nfunc list(db *sql.DB) iris.Handler {\n\treturn func(ctx iris.Context) {\n\t\tevents, err := listEvents(ctx, db)\n\t\tif err != nil {\n\t\t\terrors.Internal.LogErr(ctx, err)\n\t\t\treturn\n\t\t}\n\n\t\tctx.JSON(events)\n\t}\n}\n\nfunc getByID(db *sql.DB) iris.Handler {\n\treturn func(ctx iris.Context) {\n\t\teventID := ctx.Params().Get(\"event_id\")\n\n\t\tevt, err := getEvent(ctx, db, eventID)\n\t\tif err != nil {\n\t\t\terrors.Internal.LogErr(ctx, err)\n\t\t\treturn\n\t\t}\n\n\t\tctx.JSON(evt)\n\t}\n}\n"
  },
  {
    "path": "_examples/dependency-injection/basic/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n\n\t\"github.com/go-playground/validator/v10\"\n)\n\ntype (\n\ttestInput struct {\n\t\tEmail string `json:\"email\" validate:\"required\"`\n\t}\n\n\ttestOutput struct {\n\t\tID   int    `json:\"id\"`\n\t\tName string `json:\"name\"`\n\t}\n)\n\nfunc handler(id int, in testInput) testOutput {\n\treturn testOutput{\n\t\tID:   id,\n\t\tName: in.Email,\n\t}\n}\n\nfunc configureAPI(api *iris.APIContainer) {\n\t/* Here is how you can inject a return value from a handler,\n\t   in this case the \"testOutput\":\n\tapi.UseResultHandler(func(next iris.ResultHandler) iris.ResultHandler {\n\t\treturn func(ctx iris.Context, v any) error {\n\t\t\treturn next(ctx, map[string]any{\"injected\": true})\n\t\t}\n\t})\n\t*/\n\n\tapi.Post(\"/{id:int}\", handler)\n}\n\nfunc main() {\n\tapp := iris.New()\n\tapp.Validator = validator.New()\n\tapp.Logger().SetLevel(\"debug\")\n\n\tapp.ConfigureContainer(configureAPI)\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/dependency-injection/basic/middleware/main.go",
    "content": "package main\n\nimport (\n\t\"errors\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\ntype (\n\ttestInput struct {\n\t\tEmail string `json:\"email\"`\n\t}\n\n\ttestOutput struct {\n\t\tID   int    `json:\"id\"`\n\t\tName string `json:\"name\"`\n\t}\n)\n\nfunc handler(id int, in testInput) testOutput {\n\treturn testOutput{\n\t\tID:   id,\n\t\tName: in.Email,\n\t}\n}\n\nvar errCustom = errors.New(\"my_error\")\n\nfunc middleware(in testInput) (int, error) {\n\tif in.Email == \"invalid\" {\n\t\t// stop the execution and don't continue to \"handler\"\n\t\t// without firing an error.\n\t\treturn iris.StatusAccepted, iris.ErrStopExecution\n\t} else if in.Email == \"error\" {\n\t\t// stop the execution and fire a custom error.\n\t\treturn iris.StatusConflict, errCustom\n\t}\n\n\treturn iris.StatusOK, nil\n}\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\n\t// handle the route, respond with\n\t// a JSON and 200 status code\n\t// or 202 status code and empty body\n\t// or a 409 status code and \"my_error\" body.\n\tapp.ConfigureContainer(func(api *iris.APIContainer) {\n\t\t// Enable execution of middlewares without ctx.Next requirement.\n\t\tapi.Self.SetExecutionRules(iris.ExecutionRules{\n\t\t\tBegin: iris.ExecutionOptions{\n\t\t\t\tForce: true,\n\t\t\t},\n\t\t})\n\t\tapi.Use(middleware)\n\t\tapi.Post(\"/{id:int}\", handler)\n\t})\n\n\tapp.Configure(\n\t\tiris.WithOptimizations,                 /* optional */\n\t\tiris.WithoutBodyConsumptionOnUnmarshal, /* required when more than one handler is consuming request payload(testInput) */\n\t)\n\n\treturn app\n}\n\nfunc main() {\n\tapp := newApp()\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/dependency-injection/basic/middleware/main_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nfunc TestDependencyInjectionBasic_Middleware(t *testing.T) {\n\tapp := newApp()\n\n\te := httptest.New(t, app)\n\te.POST(\"/42\").WithJSON(testInput{Email: \"my_email\"}).Expect().\n\t\tStatus(httptest.StatusOK).\n\t\tJSON().IsEqual(testOutput{ID: 42, Name: \"my_email\"})\n\n\t// it should stop the execution at the middleware and return the middleware's status code,\n\t// because the error is `ErrStopExecution`.\n\te.POST(\"/42\").WithJSON(testInput{Email: \"invalid\"}).Expect().\n\t\tStatus(httptest.StatusAccepted).Body().IsEmpty()\n\n\t// it should stop the execution at the middleware and return the error's text.\n\te.POST(\"/42\").WithJSON(testInput{Email: \"error\"}).Expect().\n\t\tStatus(httptest.StatusConflict).Body().IsEqual(\"my_error\")\n}\n"
  },
  {
    "path": "_examples/dependency-injection/context-register-dependency/main.go",
    "content": "package main\n\nimport \"github.com/kataras/iris/v12\"\n\nfunc main() {\n\tapp := iris.New()\n\tapp.Use(RoleMiddleware)\n\n\tapp.Get(\"/\", commonHandler)\n\tc := app.ConfigureContainer()\n\t/*\n\t\tWhen you do NOT have access to the middleware code itself\n\t\tthen you can register a request dependency\n\t\twhich retrieves the value from the Context\n\t\tand returns it, so handler/function's input arguments\n\t\twith that `Role` type can be binded.\n\n\t\tc.RegisterDependency(func(ctx iris.Context) Role {\n\t\t\trole, ok := GetRole(ctx)\n\t\t\tif !ok {\n\t\t\t\t// This codeblock will never be executed here\n\t\t\t\t// but you can stop executing a handler which depends on\n\t\t\t\t// that dependency with `ctx.StopExecution/ctx.StopWithXXX` methods\n\t\t\t\t// or by returning a second output argument of `error` type.\n\t\t\t\tctx.StopExecution()\n\t\t\t\treturn Role{}\n\t\t\t}\n\n\t\t\treturn role\n\t\t})\n\t*/\n\tc.Get(\"/dep\", handlerWithDependencies)\n\n\t// http://localhost:8080?name=kataras\n\t// http://localhost:8080/dep?name=kataras\n\tapp.Listen(\":8080\")\n}\n\nfunc commonHandler(ctx iris.Context) {\n\trole, _ := GetRole(ctx)\n\tctx.WriteString(role.Name)\n}\n\nfunc handlerWithDependencies(role Role) string {\n\treturn role.Name\n}\n\n// Code for an example middleware.\n\n// Role struct value example.\ntype Role struct {\n\tName string\n}\n\nconst roleContextKey = \"myapp.role\"\n\n// RoleMiddleware example of a custom middleware.\nfunc RoleMiddleware(ctx iris.Context) {\n\t// [do it yourself: extract the role from the request...]\n\tif ctx.URLParam(\"name\") != \"kataras\" {\n\t\tctx.StopWithStatus(iris.StatusUnauthorized)\n\t\treturn\n\t}\n\t//\n\n\trole := Role{Name: \"admin\"}\n\n\tctx.Values().Set(roleContextKey, role)\n\n\t// When you have access to the middleware itself:\n\t// Use the `RegisterDependency` to register\n\t// struct type values as dependencies at request-time for\n\t// any potential dependency injection-ed user handler.\n\t// This way the user of your middleware can get rid of\n\t// manually register a dependency for that `Role` type with calls of\n\t// `APIContainer.RegisterDependency` (and `mvc.Application.Register`).\n\tctx.RegisterDependency(role)\n\n\tctx.Next()\n}\n\n// GetRole returns the role inside the context values,\n// the `roleMiddleware` should be executed first.\nfunc GetRole(ctx iris.Context) (Role, bool) {\n\tv := ctx.Values().Get(roleContextKey)\n\tif v != nil {\n\t\tif role, ok := v.(Role); ok {\n\t\t\treturn role, true\n\t\t}\n\t}\n\n\treturn Role{}, false\n}\n\n// End Code of our example middleware.\n"
  },
  {
    "path": "_examples/dependency-injection/jwt/contrib/go.mod",
    "content": "module github.com/kataras/iris/_examples/dependency-injection/jwt/contrib\n\ngo 1.25\n\nrequire (\n\tgithub.com/iris-contrib/middleware/jwt v0.0.0-20251225090426-92c6f28facda\n\tgithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd\n)\n\nrequire (\n\tgithub.com/BurntSushi/toml v1.6.0 // indirect\n\tgithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect\n\tgithub.com/CloudyKit/jet/v6 v6.3.1 // indirect\n\tgithub.com/Joker/jade v1.1.3 // indirect\n\tgithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 // indirect\n\tgithub.com/andybalholm/brotli v1.2.0 // indirect\n\tgithub.com/aymerick/douceur v0.2.0 // indirect\n\tgithub.com/fatih/structs v1.1.0 // indirect\n\tgithub.com/flosch/pongo2/v4 v4.0.2 // indirect\n\tgithub.com/golang-jwt/jwt/v4 v4.5.2 // indirect\n\tgithub.com/golang/snappy v1.0.0 // indirect\n\tgithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a // indirect\n\tgithub.com/google/uuid v1.6.0 // indirect\n\tgithub.com/gorilla/css v1.0.1 // indirect\n\tgithub.com/iris-contrib/schema v0.0.6 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/kataras/blocks v0.0.8 // indirect\n\tgithub.com/kataras/golog v0.1.12 // indirect\n\tgithub.com/kataras/pio v0.0.13 // indirect\n\tgithub.com/kataras/sitemap v0.0.6 // indirect\n\tgithub.com/kataras/tunnel v0.0.4 // indirect\n\tgithub.com/klauspost/compress v1.18.2 // indirect\n\tgithub.com/kr/text v0.2.0 // indirect\n\tgithub.com/mailgun/raymond/v2 v2.0.48 // indirect\n\tgithub.com/mailru/easyjson v0.9.1 // indirect\n\tgithub.com/microcosm-cc/bluemonday v1.0.27 // indirect\n\tgithub.com/russross/blackfriday/v2 v2.1.0 // indirect\n\tgithub.com/schollz/closestmatch v2.1.0+incompatible // indirect\n\tgithub.com/sirupsen/logrus v1.8.1 // indirect\n\tgithub.com/tdewolff/minify/v2 v2.24.8 // indirect\n\tgithub.com/tdewolff/parse/v2 v2.8.5 // indirect\n\tgithub.com/valyala/bytebufferpool v1.0.0 // indirect\n\tgithub.com/vmihailenco/msgpack/v5 v5.4.1 // indirect\n\tgithub.com/vmihailenco/tagparser/v2 v2.0.0 // indirect\n\tgithub.com/yosssi/ace v0.0.5 // indirect\n\tgolang.org/x/crypto v0.47.0 // indirect\n\tgolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 // indirect\n\tgolang.org/x/net v0.49.0 // indirect\n\tgolang.org/x/sys v0.40.0 // indirect\n\tgolang.org/x/text v0.33.0 // indirect\n\tgolang.org/x/time v0.14.0 // indirect\n\tgoogle.golang.org/protobuf v1.36.11 // indirect\n\tgopkg.in/ini.v1 v1.67.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n"
  },
  {
    "path": "_examples/dependency-injection/jwt/contrib/go.sum",
    "content": "github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=\ngithub.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=\ngithub.com/CloudyKit/jet/v6 v6.3.1 h1:6IAo5Cx21xrHVaR8zzXN5gJatKV/wO7Nf6bfCnCSbUw=\ngithub.com/CloudyKit/jet/v6 v6.3.1/go.mod h1:lf8ksdNsxZt7/yH/3n4vJQWA9RUq4wpaHtArHhGVMOw=\ngithub.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc=\ngithub.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=\ngithub.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=\ngithub.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 h1:dG7/gLroJGht/jSQtHiLvT48Hxn+crbmvyItZC8cWOs=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05/go.mod h1:NYezi6wtnJtBm5btoprXc5SvAdqH0XTXWnUup0MptAI=\ngithub.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=\ngithub.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=\ngithub.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=\ngithub.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=\ngithub.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=\ngithub.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=\ngithub.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=\ngithub.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=\ngithub.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=\ngithub.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw=\ngithub.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8=\ngithub.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=\ngithub.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=\ngithub.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=\ngithub.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=\ngithub.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=\ngithub.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a h1:l7A0loSszR5zHd/qK53ZIHMO8b3bBSmENnQ6eKnUT0A=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=\ngithub.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=\ngithub.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=\ngithub.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=\ngithub.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=\ngithub.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2 h1:T9THsdP1woyAqKHwjkEsbCnMefsAFvk8iJJKokcJ3Go=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2/go.mod h1:JLDgIqnFy5loDSUv1OA2j0mb6p/rDhiCqigP22Uq9xE=\ngithub.com/iris-contrib/middleware/jwt v0.0.0-20251225090426-92c6f28facda h1:1VquMwe3hyozXkURE0uJLqMCiTmJpTYwLFCEZSkXzs0=\ngithub.com/iris-contrib/middleware/jwt v0.0.0-20251225090426-92c6f28facda/go.mod h1:gvZR1e7IFVaT5ph6WFdleMXYSsocncDG+0BvKyJerTc=\ngithub.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw=\ngithub.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/kataras/blocks v0.0.8 h1:MrpVhoFTCR2v1iOOfGng5VJSILKeZZI+7NGfxEh3SUM=\ngithub.com/kataras/blocks v0.0.8/go.mod h1:9Jm5zx6BB+06NwA+OhTbHW1xkMOYxahnqTN5DveZ2Yg=\ngithub.com/kataras/golog v0.1.12 h1:Bu7I/G4ilJlbfzjmU39O9N+2uO1pBcMK045fzZ4ytNg=\ngithub.com/kataras/golog v0.1.12/go.mod h1:wrGSbOiBqbQSQznleVNX4epWM8rl9SJ/rmEacl0yqy4=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd h1:oMsQgqKACbUdlaIWnN+EZzzAnHkw90hE/y/R0BSij2U=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd/go.mod h1:yucjtDl9Vn3OctWM1fi0MuM9OckemC7kEreFa9QtPF8=\ngithub.com/kataras/pio v0.0.13 h1:x0rXVX0fviDTXOOLOmr4MUxOabu1InVSTu5itF8CXCM=\ngithub.com/kataras/pio v0.0.13/go.mod h1:k3HNuSw+eJ8Pm2lA4lRhg3DiCjVgHlP8hmXApSej3oM=\ngithub.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY=\ngithub.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4=\ngithub.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA=\ngithub.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw=\ngithub.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=\ngithub.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=\ngithub.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=\ngithub.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8=\ngithub.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=\ngithub.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=\ngithub.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=\ngithub.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=\ngithub.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=\ngithub.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=\ngithub.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=\ngithub.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=\ngithub.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=\ngithub.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=\ngithub.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=\ngithub.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk=\ngithub.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=\ngithub.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=\ngithub.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=\ngithub.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=\ngithub.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/tdewolff/minify/v2 v2.24.8 h1:58/VjsbevI4d5FGV0ZSuBrHMSSkH4MCH0sIz/eKIauE=\ngithub.com/tdewolff/minify/v2 v2.24.8/go.mod h1:0Ukj0CRpo/sW/nd8uZ4ccXaV1rEVIWA3dj8U7+Shhfw=\ngithub.com/tdewolff/parse/v2 v2.8.5 h1:ZmBiA/8Do5Rpk7bDye0jbbDUpXXbCdc3iah4VeUvwYU=\ngithub.com/tdewolff/parse/v2 v2.8.5/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=\ngithub.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE=\ngithub.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=\ngithub.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=\ngithub.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=\ngithub.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=\ngithub.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=\ngithub.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=\ngithub.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=\ngithub.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA=\ngithub.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0=\ngithub.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=\ngithub.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=\ngithub.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=\ngolang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=\ngolang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=\ngolang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=\ngolang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=\ngolang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=\ngolang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=\ngolang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=\ngolang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=\ngoogle.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=\ngopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nmoul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs=\nmoul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE=\n"
  },
  {
    "path": "_examples/dependency-injection/jwt/contrib/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n\n\t\"github.com/iris-contrib/middleware/jwt\"\n)\n\nvar secret = []byte(\"My Secret Key\")\n\nfunc main() {\n\tapp := iris.New()\n\tapp.ConfigureContainer(register)\n\n\tapp.Listen(\":8080\")\n}\n\nfunc register(api *iris.APIContainer) {\n\tj := jwt.New(jwt.Config{\n\t\t// Extract by \"token\" url parameter.\n\t\tExtractor: jwt.FromFirst(jwt.FromParameter(\"token\"), jwt.FromAuthHeader),\n\t\tValidationKeyGetter: func(token *jwt.Token) (any, error) {\n\t\t\treturn secret, nil\n\t\t},\n\t\tSigningMethod: jwt.SigningMethodHS256,\n\t})\n\n\tapi.Get(\"/authenticate\", writeToken)\n\t// This works as usually:\n\tapi.Get(\"/restricted\", j.Serve, restrictedPage)\n\n\t// You can also bind the *jwt.Token (see `verifiedWithBindedTokenPage`)\n\t// by registering a *jwt.Token dependency.\n\t//\n\t// api.RegisterDependency(func(ctx iris.Context) *jwt.Token {\n\t// \tif err := j.CheckJWT(ctx); err != nil {\n\t// \t\tctx.StopWithStatus(iris.StatusUnauthorized)\n\t// \t\treturn nil\n\t// \t}\n\t//\n\t// \ttoken := j.Get(ctx)\n\t// \treturn token\n\t// })\n\t// ^ You can do the same with MVC too, as the container is shared and works\n\t// the same way in both functions-as-handlers and structs-as-controllers.\n\t//\n\t// api.Get(\"/\", restrictedPageWithBindedTokenPage)\n}\n\nfunc writeToken() string {\n\ttoken := jwt.NewTokenWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{\n\t\t\"foo\": \"bar\",\n\t})\n\n\ttokenString, _ := token.SignedString(secret)\n\treturn tokenString\n}\n\nfunc restrictedPage() string {\n\treturn \"This page can only be seen by verified clients\"\n}\n\nfunc restrictedPageWithBindedTokenPage(token *jwt.Token) string {\n\t// Token[foo] value: bar\n\treturn \"Token[foo] value: \" + token.Claims.(jwt.MapClaims)[\"foo\"].(string)\n}\n"
  },
  {
    "path": "_examples/dependency-injection/jwt/main.go",
    "content": "package main\n\nimport (\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/middleware/jwt\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.ConfigureContainer(register)\n\n\t// http://localhost:8080/authenticate\n\t// http://localhost:8080/restricted (Header: Authorization = Bearer $token)\n\tapp.Listen(\":8080\")\n}\n\nvar secret = []byte(\"secret\")\n\nfunc register(api *iris.APIContainer) {\n\tapi.RegisterDependency(func(ctx iris.Context) (claims userClaims) {\n\t\t/* Using the middleware:\n\t\tif ctx.Proceed(verify) {\n\t\t\t// ^ the \"verify\" middleware will stop the execution if it's failed to verify the request.\n\t\t\t// Map the input parameter of \"restricted\" function with the claims.\n\t\t\treturn jwt.Get(ctx).(*userClaims)\n\t\t}*/\n\t\ttoken := jwt.FromHeader(ctx)\n\t\tif token == \"\" {\n\t\t\tctx.StopWithError(iris.StatusUnauthorized, jwt.ErrMissing)\n\t\t\treturn\n\t\t}\n\n\t\tverifiedToken, err := jwt.Verify(jwt.HS256, secret, []byte(token))\n\t\tif err != nil {\n\t\t\tctx.StopWithError(iris.StatusUnauthorized, err)\n\t\t\treturn\n\t\t}\n\n\t\tverifiedToken.Claims(&claims)\n\t\treturn\n\t})\n\n\tapi.Get(\"/authenticate\", writeToken)\n\tapi.Get(\"/restricted\", restrictedPage)\n}\n\ntype userClaims struct {\n\tUsername string `json:\"username\"`\n}\n\nfunc writeToken(ctx iris.Context) {\n\tclaims := userClaims{\n\t\tUsername: \"kataras\",\n\t}\n\n\ttoken, err := jwt.Sign(jwt.HS256, secret, claims, jwt.MaxAge(1*time.Minute))\n\tif err != nil {\n\t\tctx.StopWithError(iris.StatusInternalServerError, err)\n\t\treturn\n\t}\n\n\tctx.Write(token)\n}\n\nfunc restrictedPage(claims userClaims) string {\n\t// userClaims.Username: kataras\n\treturn \"userClaims.Username: \" + claims.Username\n}\n"
  },
  {
    "path": "_examples/dependency-injection/overview/datamodels/movie.go",
    "content": "// file: datamodels/movie.go\n\npackage datamodels\n\n// Movie is our sample data structure.\n// Keep note that the tags for public-use (for our web app)\n// should be kept in other file like \"web/viewmodels/movie.go\"\n// which could wrap by embedding the datamodels.Movie or\n// declare new fields instead butwe will use this datamodel\n// as the only one Movie model in our application,\n// for the sake of simplicty.\ntype Movie struct {\n\tID     uint64 `json:\"id\"`\n\tName   string `json:\"name\"`\n\tYear   int    `json:\"year\"`\n\tGenre  string `json:\"genre\"`\n\tPoster string `json:\"poster\"`\n}\n"
  },
  {
    "path": "_examples/dependency-injection/overview/datasource/movies.go",
    "content": "// file: datasource/movies.go\n\npackage datasource\n\nimport \"github.com/kataras/iris/v12/_examples/dependency-injection/overview/datamodels\"\n\n// Movies is our imaginary data source.\nvar Movies = map[uint64]datamodels.Movie{\n\t1: {\n\t\tID:     1,\n\t\tName:   \"Casablanca\",\n\t\tYear:   1942,\n\t\tGenre:  \"Romance\",\n\t\tPoster: \"https://iris-go.com/static/images/examples/mvc-movies/1.jpg\",\n\t},\n\t2: {\n\t\tID:     2,\n\t\tName:   \"Gone with the Wind\",\n\t\tYear:   1939,\n\t\tGenre:  \"Romance\",\n\t\tPoster: \"https://iris-go.com/static/images/examples/mvc-movies/2.jpg\",\n\t},\n\t3: {\n\t\tID:     3,\n\t\tName:   \"Citizen Kane\",\n\t\tYear:   1941,\n\t\tGenre:  \"Mystery\",\n\t\tPoster: \"https://iris-go.com/static/images/examples/mvc-movies/3.jpg\",\n\t},\n\t4: {\n\t\tID:     4,\n\t\tName:   \"The Wizard of Oz\",\n\t\tYear:   1939,\n\t\tGenre:  \"Fantasy\",\n\t\tPoster: \"https://iris-go.com/static/images/examples/mvc-movies/4.jpg\",\n\t},\n\t5: {\n\t\tID:     5,\n\t\tName:   \"North by Northwest\",\n\t\tYear:   1959,\n\t\tGenre:  \"Thriller\",\n\t\tPoster: \"https://iris-go.com/static/images/examples/mvc-movies/5.jpg\",\n\t},\n}\n"
  },
  {
    "path": "_examples/dependency-injection/overview/main.go",
    "content": "// file: main.go\n\npackage main\n\nimport (\n\t\"github.com/kataras/iris/v12/_examples/dependency-injection/overview/datasource\"\n\t\"github.com/kataras/iris/v12/_examples/dependency-injection/overview/repositories\"\n\t\"github.com/kataras/iris/v12/_examples/dependency-injection/overview/services\"\n\t\"github.com/kataras/iris/v12/_examples/dependency-injection/overview/web/middleware\"\n\t\"github.com/kataras/iris/v12/_examples/dependency-injection/overview/web/routes\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.Logger().SetLevel(\"debug\")\n\n\t// Load the template files.\n\tapp.RegisterView(iris.HTML(\"./web/views\", \".html\"))\n\n\t// Create our movie repository with some (memory) data from the datasource.\n\trepo := repositories.NewMovieRepository(datasource.Movies)\n\n\tapp.Party(\"/hello\").ConfigureContainer(func(r *iris.APIContainer) {\n\t\tr.Get(\"/\", routes.Hello)\n\t\tr.Get(\"/{name}\", routes.HelloName)\n\t})\n\n\tapp.Party(\"/movies\").ConfigureContainer(func(r *iris.APIContainer) {\n\t\t// Create our movie service, we will bind it to the movie app's dependencies.\n\t\tmovieService := services.NewMovieService(repo)\n\t\tr.RegisterDependency(movieService)\n\n\t\t// Add the basic authentication(admin:password) middleware\n\t\t// for the /movies based requests.\n\t\tr.Use(middleware.BasicAuth)\n\n\t\tr.Get(\"/\", routes.Movies)\n\t\tr.Get(\"/{id:uint64}\", routes.MovieByID)\n\t\tr.Put(\"/{id:uint64}\", routes.UpdateMovieByID)\n\t\tr.Delete(\"/{id:uint64}\", routes.DeleteMovieByID)\n\t})\n\n\t// http://localhost:8080/hello\n\t// http://localhost:8080/hello/iris\n\t// http://localhost:8080/movies (\"admin\": \"password\")\n\t// http://localhost:8080/movies/1\n\tapp.Listen(\n\t\t// Start the web server at localhost:8080\n\t\t\"localhost:8080\",\n\t\t// enables faster json serialization and more:\n\t\tiris.WithOptimizations,\n\t)\n}\n"
  },
  {
    "path": "_examples/dependency-injection/overview/repositories/movie_repository.go",
    "content": "// file: repositories/movie_repository.go\n\npackage repositories\n\nimport (\n\t\"errors\"\n\t\"sync\"\n\n\t\"github.com/kataras/iris/v12/_examples/dependency-injection/overview/datamodels\"\n)\n\n// Query represents the visitor and action queries.\ntype Query func(datamodels.Movie) bool\n\n// MovieRepository handles the basic operations of a movie entity/model.\n// It's an interface in order to be testable, i.e a memory movie repository or\n// a connected to an sql database.\ntype MovieRepository interface {\n\tExec(query Query, action Query, limit int, mode int) (ok bool)\n\n\tSelect(query Query) (movie datamodels.Movie, found bool)\n\tSelectMany(query Query, limit int) (results []datamodels.Movie)\n\n\tInsertOrUpdate(movie datamodels.Movie) (updatedMovie datamodels.Movie, err error)\n\tDelete(query Query, limit int) (deleted bool)\n}\n\n// NewMovieRepository returns a new movie memory-based repository,\n// the one and only repository type in our example.\nfunc NewMovieRepository(source map[uint64]datamodels.Movie) MovieRepository {\n\treturn &movieMemoryRepository{source: source}\n}\n\n// movieMemoryRepository is a \"MovieRepository\"\n// which manages the movies using the memory data source (map).\ntype movieMemoryRepository struct {\n\tsource map[uint64]datamodels.Movie\n\tmu     sync.RWMutex\n}\n\nconst (\n\t// ReadOnlyMode will RLock(read) the data .\n\tReadOnlyMode = iota\n\t// ReadWriteMode will Lock(read/write) the data.\n\tReadWriteMode\n)\n\nfunc (r *movieMemoryRepository) Exec(query Query, action Query, actionLimit int, mode int) (ok bool) {\n\tloops := 0\n\n\tif mode == ReadOnlyMode {\n\t\tr.mu.RLock()\n\t\tdefer r.mu.RUnlock()\n\t} else {\n\t\tr.mu.Lock()\n\t\tdefer r.mu.Unlock()\n\t}\n\n\tfor _, movie := range r.source {\n\t\tok = query(movie)\n\t\tif ok {\n\t\t\tif action(movie) {\n\t\t\t\tloops++\n\t\t\t\tif actionLimit >= loops {\n\t\t\t\t\tbreak // break\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn\n}\n\n// Select receives a query function\n// which is fired for every single movie model inside\n// our imaginary data source.\n// When that function returns true then it stops the iteration.\n//\n// It returns the query's return last known \"found\" value\n// and the last known movie model\n// to help callers to reduce the LOC.\n//\n// It's actually a simple but very clever prototype function\n// I'm using everywhere since I firstly think of it,\n// hope you'll find it very useful as well.\nfunc (r *movieMemoryRepository) Select(query Query) (movie datamodels.Movie, found bool) {\n\tfound = r.Exec(query, func(m datamodels.Movie) bool {\n\t\tmovie = m\n\t\treturn true\n\t}, 1, ReadOnlyMode)\n\n\t// set an empty datamodels.Movie if not found at all.\n\tif !found {\n\t\tmovie = datamodels.Movie{}\n\t}\n\n\treturn\n}\n\n// SelectMany same as Select but returns one or more datamodels.Movie as a slice.\n// If limit <=0 then it returns everything.\nfunc (r *movieMemoryRepository) SelectMany(query Query, limit int) (results []datamodels.Movie) {\n\tr.Exec(query, func(m datamodels.Movie) bool {\n\t\tresults = append(results, m)\n\t\treturn true\n\t}, limit, ReadOnlyMode)\n\n\treturn\n}\n\n// InsertOrUpdate adds or updates a movie to the (memory) storage.\n//\n// Returns the new movie and an error if any.\nfunc (r *movieMemoryRepository) InsertOrUpdate(movie datamodels.Movie) (datamodels.Movie, error) {\n\tid := movie.ID\n\n\tif id == 0 { // Create new action\n\t\tvar lastID uint64\n\t\t// find the biggest ID in order to not have duplications\n\t\t// in productions apps you can use a third-party\n\t\t// library to generate a UUID as string.\n\t\tr.mu.RLock()\n\t\tfor _, item := range r.source {\n\t\t\tif item.ID > lastID {\n\t\t\t\tlastID = item.ID\n\t\t\t}\n\t\t}\n\t\tr.mu.RUnlock()\n\n\t\tid = lastID + 1\n\t\tmovie.ID = id\n\n\t\t// map-specific thing\n\t\tr.mu.Lock()\n\t\tr.source[id] = movie\n\t\tr.mu.Unlock()\n\n\t\treturn movie, nil\n\t}\n\n\t// Update action based on the movie.ID,\n\t// here we will allow updating the poster and genre if not empty.\n\t// Alternatively we could do pure replace instead:\n\t// r.source[id] = movie\n\t// and comment the code below;\n\tcurrent, exists := r.Select(func(m datamodels.Movie) bool {\n\t\treturn m.ID == id\n\t})\n\n\tif !exists { // ID is not a real one, return an error.\n\t\treturn datamodels.Movie{}, errors.New(\"failed to update a nonexistent movie\")\n\t}\n\n\t// or comment these and r.source[id] = m for pure replace\n\tif movie.Poster != \"\" {\n\t\tcurrent.Poster = movie.Poster\n\t}\n\n\tif movie.Genre != \"\" {\n\t\tcurrent.Genre = movie.Genre\n\t}\n\n\t// map-specific thing\n\tr.mu.Lock()\n\tr.source[id] = current\n\tr.mu.Unlock()\n\n\treturn movie, nil\n}\n\nfunc (r *movieMemoryRepository) Delete(query Query, limit int) bool {\n\treturn r.Exec(query, func(m datamodels.Movie) bool {\n\t\tdelete(r.source, m.ID)\n\t\treturn true\n\t}, limit, ReadWriteMode)\n}\n"
  },
  {
    "path": "_examples/dependency-injection/overview/services/movie_service.go",
    "content": "// file: services/movie_service.go\n\npackage services\n\nimport (\n\t\"github.com/kataras/iris/v12/_examples/dependency-injection/overview/datamodels\"\n\t\"github.com/kataras/iris/v12/_examples/dependency-injection/overview/repositories\"\n)\n\n// MovieService handles some of the CRUID operations of the movie datamodel.\n// It depends on a movie repository for its actions.\n// It's here to decouple the data source from the higher level compoments.\n// As a result a different repository type can be used with the same logic without any aditional changes.\n// It's an interface and it's used as interface everywhere\n// because we may need to change or try an experimental different domain logic at the future.\ntype MovieService interface {\n\tGetAll() []datamodels.Movie\n\tGetByID(id uint64) (datamodels.Movie, bool)\n\tDeleteByID(id uint64) bool\n\tUpdatePosterAndGenreByID(id uint64, poster string, genre string) (datamodels.Movie, error)\n}\n\n// NewMovieService returns the default movie service.\nfunc NewMovieService(repo repositories.MovieRepository) MovieService {\n\treturn &movieService{\n\t\trepo: repo,\n\t}\n}\n\ntype movieService struct {\n\trepo repositories.MovieRepository\n}\n\n// GetAll returns all movies.\nfunc (s *movieService) GetAll() []datamodels.Movie {\n\treturn s.repo.SelectMany(func(_ datamodels.Movie) bool {\n\t\treturn true\n\t}, -1)\n}\n\n// GetByID returns a movie based on its id.\nfunc (s *movieService) GetByID(id uint64) (datamodels.Movie, bool) {\n\treturn s.repo.Select(func(m datamodels.Movie) bool {\n\t\treturn m.ID == id\n\t})\n}\n\n// UpdatePosterAndGenreByID updates a movie's poster and genre.\nfunc (s *movieService) UpdatePosterAndGenreByID(id uint64, poster string, genre string) (datamodels.Movie, error) {\n\t// update the movie and return it.\n\treturn s.repo.InsertOrUpdate(datamodels.Movie{\n\t\tID:     id,\n\t\tPoster: poster,\n\t\tGenre:  genre,\n\t})\n}\n\n// DeleteByID deletes a movie by its id.\n//\n// Returns true if deleted otherwise false.\nfunc (s *movieService) DeleteByID(id uint64) bool {\n\treturn s.repo.Delete(func(m datamodels.Movie) bool {\n\t\treturn m.ID == id\n\t}, 1)\n}\n"
  },
  {
    "path": "_examples/dependency-injection/overview/web/middleware/basicauth.go",
    "content": "// file: web/middleware/basicauth.go\n\npackage middleware\n\nimport \"github.com/kataras/iris/v12/middleware/basicauth\"\n\n// BasicAuth middleware sample.\nvar BasicAuth = basicauth.Default(map[string]string{\n\t\"admin\": \"password\",\n})\n"
  },
  {
    "path": "_examples/dependency-injection/overview/web/routes/hello.go",
    "content": "// file: web/routes/hello.go\n\npackage routes\n\nimport (\n\t\"errors\"\n\n\t\"github.com/kataras/iris/v12/hero\"\n)\n\nvar helloView = hero.View{\n\tName: \"hello/index.html\",\n\tData: map[string]any{\n\t\t\"Title\":     \"Hello Page\",\n\t\t\"MyMessage\": \"Welcome to my awesome website\",\n\t},\n}\n\n// Hello will return a predefined view with bind data.\n//\n// `hero.Result` is just an interface with a `Dispatch` function.\n// `hero.Response` and `hero.View` are the builtin result type dispatchers\n// you can even create custom response dispatchers by\n// implementing the `github.com/kataras/iris/hero#Result` interface.\nfunc Hello() hero.Result {\n\treturn helloView\n}\n\n// you can define a standard error in order to re-use anywhere in your app.\nvar errBadName = errors.New(\"bad name\")\n\n// you can just return it as error or even better\n// wrap this error with an hero.Response to make it an hero.Result compatible type.\nvar badName = hero.Response{Err: errBadName, Code: 400}\n\n// HelloName returns a \"Hello {name}\" response.\n// Demos:\n// curl -i http://localhost:8080/hello/iris\n// curl -i http://localhost:8080/hello/anything\nfunc HelloName(name string) hero.Result {\n\tif name != \"iris\" {\n\t\treturn badName\n\t}\n\n\t// return hero.Response{Text: \"Hello \" + name} OR:\n\treturn hero.View{\n\t\tName: \"hello/name.html\",\n\t\tData: name,\n\t}\n}\n"
  },
  {
    "path": "_examples/dependency-injection/overview/web/routes/movies.go",
    "content": "// file: web/routes/movie.go\n\npackage routes\n\nimport (\n\t\"errors\"\n\n\t\"github.com/kataras/iris/v12/_examples/dependency-injection/overview/datamodels\"\n\t\"github.com/kataras/iris/v12/_examples/dependency-injection/overview/services\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\n// Movies returns list of the movies.\n// Demo:\n// curl -i http://localhost:8080/movies\nfunc Movies(service services.MovieService) (results []datamodels.Movie) {\n\treturn service.GetAll()\n}\n\n// MovieByID returns a movie.\n// Demo:\n// curl -i http://localhost:8080/movies/1\nfunc MovieByID(service services.MovieService, id uint64) (movie datamodels.Movie, found bool) {\n\treturn service.GetByID(id) // it will throw 404 if not found.\n}\n\n// UpdateMovieByID updates a movie.\n// Demo:\n// curl -i -X PUT -F \"genre=Thriller\" -F \"poster=@/Users/kataras/Downloads/out.gif\" http://localhost:8080/movies/1\nfunc UpdateMovieByID(ctx iris.Context, service services.MovieService, id uint64) (datamodels.Movie, error) {\n\t// get the request data for poster and genre\n\tfile, info, err := ctx.FormFile(\"poster\")\n\tif err != nil {\n\t\treturn datamodels.Movie{}, errors.New(\"failed due form file 'poster' missing\")\n\t}\n\t// we don't need the file so close it now.\n\tfile.Close()\n\n\t// imagine that is the url of the uploaded file...\n\tposter := info.Filename\n\tgenre := ctx.FormValue(\"genre\")\n\n\treturn service.UpdatePosterAndGenreByID(id, poster, genre)\n}\n\n// DeleteMovieByID deletes a movie.\n// Demo:\n// curl -i -X DELETE -u admin:password http://localhost:8080/movies/1\nfunc DeleteMovieByID(service services.MovieService, id uint64) any {\n\twasDel := service.DeleteByID(id)\n\tif wasDel {\n\t\t// return the deleted movie's ID\n\t\treturn iris.Map{\"deleted\": id}\n\t}\n\t// right here we can see that a method function can return any of those two types(map or int),\n\t// we don't have to specify the return type to a specific type.\n\treturn iris.StatusBadRequest\n}\n"
  },
  {
    "path": "_examples/dependency-injection/overview/web/views/hello/index.html",
    "content": "<!-- file: web/views/hello/index.html -->\n<html>\n\n<head>\n    <title>{{.Title}} - My App</title>\n</head>\n\n<body>\n    <p>{{.MyMessage}}</p>\n</body>\n\n</html>"
  },
  {
    "path": "_examples/dependency-injection/overview/web/views/hello/name.html",
    "content": "<!-- file: web/views/hello/name.html -->\n<html>\n\n<head>\n    <title>{{.}}' Portfolio - My App</title>\n</head>\n\n<body>\n    <h1>Hello {{.}}</h1>\n</body>\n\n</html>"
  },
  {
    "path": "_examples/dependency-injection/sessions/main.go",
    "content": "package main\n\nimport (\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/_examples/dependency-injection/sessions/routes\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/sessions\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tsessionManager := sessions.New(sessions.Config{\n\t\tCookie:       \"site_session_id\",\n\t\tExpires:      60 * time.Minute,\n\t\tAllowReclaim: true,\n\t})\n\n\t// Session is automatically binded through `sessions.Get(ctx)`\n\t// if a *sessions.Session input argument is present on the handler's function,\n\t// which `routes.Index` does.\n\tapp.Use(sessionManager.Handler())\n\n\t// Method: GET\n\t// Path: http://localhost:8080\n\tapp.ConfigureContainer(registerRoutes)\n\n\tapp.Listen(\":8080\")\n}\n\nfunc registerRoutes(api *iris.APIContainer) {\n\tapi.Get(\"/\", routes.Index)\n}\n"
  },
  {
    "path": "_examples/dependency-injection/sessions/routes/index.go",
    "content": "package routes\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12/sessions\"\n)\n\n// Index will increment a simple int version based on the visits that this user/session did.\nfunc Index(session *sessions.Session) string {\n\t// it increments a \"visits\" value of integer by one,\n\t// if the entry with key 'visits' doesn't exist it will create it for you.\n\tvisits := session.Increment(\"visits\", 1)\n\n\t// write the current, updated visits.\n\treturn fmt.Sprintf(\"%d visit(s) from my current session\", visits)\n}\n"
  },
  {
    "path": "_examples/dependency-injection/smart-contract/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/kataras/iris/v12\"\n\n\t// External package to optionally filter JSON responses before sent,\n\t// see `sendJSON` for more.\n\t\"github.com/jmespath/go-jmespath\"\n)\n\n/*\n\t$ go get github.com/jmespath/go-jmespath\n*/\n\nfunc main() {\n\tapp := newApp()\n\tapp.Logger().SetLevel(\"debug\")\n\n\t// http://localhost:8080/users?query=[?Name == 'John Doe'].Age\n\t// <- client will receive the age of a user which his name is \"John Doe\".\n\t// You can also test query=[0].Name to retrieve the first user's name.\n\t// Or even query=[0:3].Age to print the first three ages.\n\t// Learn more about jmespath and how to filter:\n\t// http://jmespath.readthedocs.io/en/latest/ and\n\t// https://github.com/jmespath/go-jmespath/tree/master/fuzz/testdata\n\t//\n\t// http://localhost:8080/users\n\t// http://localhost:8080/users/William%20Woe\n\t// http://localhost:8080/users/William%20Woe/age\n\tapp.Listen(\":8080\")\n}\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\n\t// PartyFunc is the same as usersRouter := app.Party(\"/users\")\n\t// but it gives us an easy way to call router's registration functions,\n\t// i.e functions from another package that can handle this group of routes.\n\tapp.PartyFunc(\"/users\", registerUsersRoutes)\n\n\treturn app\n}\n\n/*\n\tSTART OF USERS ROUTER\n*/\n\nfunc registerUsersRoutes(usersRouter iris.Party) {\n\t// GET: /users\n\tusersRouter.Get(\"/\", getAllUsersHandler)\n\tusersRouter.Party(\"/{name}\").ConfigureContainer(registerUserRoutes)\n}\n\ntype user struct {\n\tName string `json:\"name\"`\n\tAge  int    `json:\"age\"`\n}\n\nvar usersSample = []*user{\n\t{\"William Woe\", 25},\n\t{\"Mary Moe\", 15},\n\t{\"John Doe\", 17},\n}\n\nfunc getAllUsersHandler(ctx iris.Context) {\n\terr := sendJSON(ctx, usersSample)\n\tif err != nil {\n\t\tfail(ctx, iris.StatusInternalServerError, \"unable to send a list of all users: %v\", err)\n\t\treturn\n\t}\n}\n\n/*\n\tSTART OF USERS.USER SUB ROUTER\n*/\n\nfunc registerUserRoutes(userRouter *iris.APIContainer) {\n\tuserRouter.RegisterDependency(userDependency)\n\t// GET: /users/{name:string}\n\tuserRouter.Get(\"/\", getUserHandler)\n\t// GET: /users/{name:string}/age\n\tuserRouter.Get(\"/age\", getUserAgeHandler)\n}\n\nvar userDependency = func(ctx iris.Context) *user {\n\tname := strings.Title(ctx.Params().Get(\"name\"))\n\tfor _, u := range usersSample {\n\t\tif u.Name == name {\n\t\t\treturn u\n\t\t}\n\t}\n\n\t// you may want or no to handle the error here, either way the main route handler\n\t// is going to be executed, always. A dynamic dependency(per-request) is not a middleware, so things like `ctx.Next()` or `ctx.StopExecution()`\n\t// do not apply here, look the `getUserHandler`'s first lines; we stop/exit the handler manually\n\t// if the received user is nil but depending on your app's needs, it is possible to do other things too.\n\t// A dynamic dependency like this can return more output values, i.e (*user, bool).\n\tfail(ctx, iris.StatusNotFound, \"user with name '%s' not found\", name)\n\treturn nil\n}\n\nfunc getUserHandler(ctx iris.Context, u *user) {\n\tsendJSON(ctx, u)\n}\n\nfunc getUserAgeHandler(u *user) string {\n\treturn fmt.Sprintf(\"%d\", u.Age)\n}\n\n/* END OF USERS.USER SUB ROUTER */\n\n/* END OF USERS ROUTER */\n\n// common JSON response for manual HTTP errors, optionally.\ntype httpError struct {\n\tCode   int    `json:\"code\"`\n\tReason string `json:\"reason\"`\n}\n\nfunc (h httpError) Error() string {\n\treturn fmt.Sprintf(\"Status Code: %d\\nReason: %s\", h.Code, h.Reason)\n}\n\nfunc fail(ctx iris.Context, statusCode int, format string, a ...any) {\n\terr := httpError{\n\t\tCode:   statusCode,\n\t\tReason: fmt.Sprintf(format, a...),\n\t}\n\n\t// log all the >= 500 internal errors.\n\tif statusCode >= 500 {\n\t\tctx.Application().Logger().Error(err)\n\t}\n\n\t// no next handlers will run.\n\tctx.StopWithJSON(statusCode, err)\n}\n\n// JSON helper to give end-user the ability to put indention chars or filtering the response, you can do that, optionally.\n// If you'd like to see that function inside the Iris' Context itself raise a [Feature Request] issue and link this example.\nfunc sendJSON(ctx iris.Context, resp any) (err error) {\n\tindent := ctx.URLParamDefault(\"indent\", \"  \")\n\t// i.e [?Name == 'John Doe'].Age # to output the [age] of a user which his name is \"John Doe\".\n\tif query := ctx.URLParam(\"query\"); query != \"\" && query != \"[]\" {\n\t\tresp, err = jmespath.Search(query, resp)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\terr = ctx.JSON(resp, iris.JSON{Indent: indent, UnescapeHTML: true})\n\treturn err\n}\n"
  },
  {
    "path": "_examples/desktop/blink/main.go",
    "content": "//go:build windows\n// +build windows\n\npackage main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/raintean/blink\"\n)\n\nconst addr = \"127.0.0.1:8080\"\n\n/*\n$ go build -mod=mod -ldflags -H=windowsgui -o myapp.exe\n$ ./myapp.exe # run the app\n*/\nfunc main() {\n\tgo runServer()\n\tshowAndWaitWindow()\n}\n\nfunc runServer() {\n\tapp := iris.New()\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.HTML(\"<h1> Hello Desktop</h1>\")\n\t})\n\tapp.Listen(addr)\n}\n\nfunc showAndWaitWindow() {\n\tblink.SetDebugMode(true)\n\tif err := blink.InitBlink(); err != nil {\n\t\tpanic(err)\n\t}\n\n\tview := blink.NewWebView(false, 800, 600)\n\tview.LoadURL(addr)\n\tview.SetWindowTitle(\"My App\")\n\tview.MoveToCenter()\n\tview.ShowWindow()\n\n\t<-view.Destroy\n}\n"
  },
  {
    "path": "_examples/desktop/lorca/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/zserge/lorca\"\n)\n\nconst addr = \"127.0.0.1:8080\"\n\n/*\n$ go build -mod=mod -ldflags=\"-H windowsgui\" -o myapp.exe # build for windows\n$ ./myapp.exe # run\n*/\nfunc main() {\n\tgo runServer()\n\tshowAndWaitWindow()\n}\n\nfunc runServer() {\n\tapp := iris.New()\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.HTML(\"<head><title>My App</title></head><body><h1>Hello Desktop</h1></body>\")\n\t})\n\tapp.Listen(addr)\n}\n\nfunc showAndWaitWindow() {\n\twebview, err := lorca.New(\"http://\"+addr, \"\", 800, 600)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer webview.Close()\n\n\t// webview.SetBounds(lorca.Bounds{\n\t// \tWindowState: lorca.WindowStateFullscreen,\n\t// })\n\n\t// Wait for the browser window to be closed\n\t<-webview.Done()\n}\n"
  },
  {
    "path": "_examples/desktop/webview/go.mod",
    "content": "module github.com/kataras/iris/_examples/desktop/webview\n\ngo 1.25\n\nrequire (\n\tgithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd\n\tgithub.com/webview/webview_go v0.0.0-20240831120633-6173450d4dd6\n)\n\nrequire (\n\tgithub.com/BurntSushi/toml v1.6.0 // indirect\n\tgithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect\n\tgithub.com/CloudyKit/jet/v6 v6.3.1 // indirect\n\tgithub.com/Joker/jade v1.1.3 // indirect\n\tgithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 // indirect\n\tgithub.com/andybalholm/brotli v1.2.0 // indirect\n\tgithub.com/aymerick/douceur v0.2.0 // indirect\n\tgithub.com/fatih/structs v1.1.0 // indirect\n\tgithub.com/flosch/pongo2/v4 v4.0.2 // indirect\n\tgithub.com/golang/snappy v1.0.0 // indirect\n\tgithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a // indirect\n\tgithub.com/google/uuid v1.6.0 // indirect\n\tgithub.com/gorilla/css v1.0.1 // indirect\n\tgithub.com/iris-contrib/schema v0.0.6 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/kataras/blocks v0.0.8 // indirect\n\tgithub.com/kataras/golog v0.1.12 // indirect\n\tgithub.com/kataras/pio v0.0.13 // indirect\n\tgithub.com/kataras/sitemap v0.0.6 // indirect\n\tgithub.com/kataras/tunnel v0.0.4 // indirect\n\tgithub.com/klauspost/compress v1.18.2 // indirect\n\tgithub.com/kr/pretty v0.3.1 // indirect\n\tgithub.com/mailgun/raymond/v2 v2.0.48 // indirect\n\tgithub.com/mailru/easyjson v0.9.1 // indirect\n\tgithub.com/microcosm-cc/bluemonday v1.0.27 // indirect\n\tgithub.com/rogpeppe/go-internal v1.10.0 // indirect\n\tgithub.com/russross/blackfriday/v2 v2.1.0 // indirect\n\tgithub.com/schollz/closestmatch v2.1.0+incompatible // indirect\n\tgithub.com/sirupsen/logrus v1.8.1 // indirect\n\tgithub.com/stretchr/testify v1.11.1 // indirect\n\tgithub.com/tdewolff/minify/v2 v2.24.8 // indirect\n\tgithub.com/tdewolff/parse/v2 v2.8.5 // indirect\n\tgithub.com/valyala/bytebufferpool v1.0.0 // indirect\n\tgithub.com/vmihailenco/msgpack/v5 v5.4.1 // indirect\n\tgithub.com/vmihailenco/tagparser/v2 v2.0.0 // indirect\n\tgithub.com/yosssi/ace v0.0.5 // indirect\n\tgolang.org/x/crypto v0.47.0 // indirect\n\tgolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 // indirect\n\tgolang.org/x/net v0.49.0 // indirect\n\tgolang.org/x/sys v0.40.0 // indirect\n\tgolang.org/x/text v0.33.0 // indirect\n\tgolang.org/x/time v0.14.0 // indirect\n\tgoogle.golang.org/protobuf v1.36.11 // indirect\n\tgopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect\n\tgopkg.in/ini.v1 v1.67.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n"
  },
  {
    "path": "_examples/desktop/webview/go.sum",
    "content": "github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=\ngithub.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=\ngithub.com/CloudyKit/jet/v6 v6.3.1 h1:6IAo5Cx21xrHVaR8zzXN5gJatKV/wO7Nf6bfCnCSbUw=\ngithub.com/CloudyKit/jet/v6 v6.3.1/go.mod h1:lf8ksdNsxZt7/yH/3n4vJQWA9RUq4wpaHtArHhGVMOw=\ngithub.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc=\ngithub.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=\ngithub.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=\ngithub.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 h1:dG7/gLroJGht/jSQtHiLvT48Hxn+crbmvyItZC8cWOs=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05/go.mod h1:NYezi6wtnJtBm5btoprXc5SvAdqH0XTXWnUup0MptAI=\ngithub.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=\ngithub.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=\ngithub.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=\ngithub.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=\ngithub.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=\ngithub.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=\ngithub.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=\ngithub.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=\ngithub.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=\ngithub.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw=\ngithub.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8=\ngithub.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=\ngithub.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=\ngithub.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=\ngithub.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a h1:l7A0loSszR5zHd/qK53ZIHMO8b3bBSmENnQ6eKnUT0A=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=\ngithub.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=\ngithub.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=\ngithub.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=\ngithub.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=\ngithub.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2 h1:T9THsdP1woyAqKHwjkEsbCnMefsAFvk8iJJKokcJ3Go=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2/go.mod h1:JLDgIqnFy5loDSUv1OA2j0mb6p/rDhiCqigP22Uq9xE=\ngithub.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw=\ngithub.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/kataras/blocks v0.0.8 h1:MrpVhoFTCR2v1iOOfGng5VJSILKeZZI+7NGfxEh3SUM=\ngithub.com/kataras/blocks v0.0.8/go.mod h1:9Jm5zx6BB+06NwA+OhTbHW1xkMOYxahnqTN5DveZ2Yg=\ngithub.com/kataras/golog v0.1.12 h1:Bu7I/G4ilJlbfzjmU39O9N+2uO1pBcMK045fzZ4ytNg=\ngithub.com/kataras/golog v0.1.12/go.mod h1:wrGSbOiBqbQSQznleVNX4epWM8rl9SJ/rmEacl0yqy4=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd h1:oMsQgqKACbUdlaIWnN+EZzzAnHkw90hE/y/R0BSij2U=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd/go.mod h1:yucjtDl9Vn3OctWM1fi0MuM9OckemC7kEreFa9QtPF8=\ngithub.com/kataras/pio v0.0.13 h1:x0rXVX0fviDTXOOLOmr4MUxOabu1InVSTu5itF8CXCM=\ngithub.com/kataras/pio v0.0.13/go.mod h1:k3HNuSw+eJ8Pm2lA4lRhg3DiCjVgHlP8hmXApSej3oM=\ngithub.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY=\ngithub.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4=\ngithub.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA=\ngithub.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw=\ngithub.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=\ngithub.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=\ngithub.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=\ngithub.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8=\ngithub.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=\ngithub.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=\ngithub.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=\ngithub.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=\ngithub.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=\ngithub.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=\ngithub.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=\ngithub.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=\ngithub.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=\ngithub.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=\ngithub.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=\ngithub.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=\ngithub.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk=\ngithub.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=\ngithub.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=\ngithub.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=\ngithub.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=\ngithub.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/tdewolff/minify/v2 v2.24.8 h1:58/VjsbevI4d5FGV0ZSuBrHMSSkH4MCH0sIz/eKIauE=\ngithub.com/tdewolff/minify/v2 v2.24.8/go.mod h1:0Ukj0CRpo/sW/nd8uZ4ccXaV1rEVIWA3dj8U7+Shhfw=\ngithub.com/tdewolff/parse/v2 v2.8.5 h1:ZmBiA/8Do5Rpk7bDye0jbbDUpXXbCdc3iah4VeUvwYU=\ngithub.com/tdewolff/parse/v2 v2.8.5/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=\ngithub.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE=\ngithub.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=\ngithub.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=\ngithub.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=\ngithub.com/webview/webview_go v0.0.0-20240831120633-6173450d4dd6 h1:VQpB2SpK88C6B5lPHTuSZKb2Qee1QWwiFlC5CKY4AW0=\ngithub.com/webview/webview_go v0.0.0-20240831120633-6173450d4dd6/go.mod h1:yE65LFCeWf4kyWD5re+h4XNvOHJEXOCOuJZ4v8l5sgk=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=\ngithub.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=\ngithub.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=\ngithub.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=\ngithub.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=\ngithub.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA=\ngithub.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0=\ngithub.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=\ngithub.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=\ngithub.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=\ngolang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=\ngolang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=\ngolang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=\ngolang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=\ngolang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=\ngolang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=\ngolang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=\ngolang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=\ngoogle.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=\ngopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nmoul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs=\nmoul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE=\n"
  },
  {
    "path": "_examples/desktop/webview/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/webview/webview_go\"\n)\n\nconst addr = \"127.0.0.1:8080\"\n\n/*\n# Windows requires special linker flags for GUI apps.\n# It's also recommended to use TDM-GCC-64 compiler for CGo.\n# http://tdm-gcc.tdragon.net/download\n#\n#\n$ go build -mod=mod -ldflags=\"-H windowsgui\" -o myapp.exe # build for windows\n$ ./myapp.exe # run\n#\n# MacOS uses app bundles for GUI apps\n$ mkdir -p example.app/Contents/MacOS\n$ go build -o example.app/Contents/MacOS/example\n$ open example.app # Or click on the app in Finder\n#\n# Note: if you see \"use option -std=c99 or -std=gnu99 to compile your code\"\n# please refer to: https://github.com/webview/webview/issues/188.\n# New repository: https://github.com/webview/webview_go.\n*/\nfunc main() {\n\tgo runServer()\n\tshowAndWaitWindow()\n}\n\nfunc runServer() {\n\tapp := iris.New()\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.HTML(\"<h1>Hello Desktop</h1>\")\n\t})\n\tapp.Listen(addr)\n}\n\nfunc showAndWaitWindow() {\n\tdebug := true\n\n\tw := webview.New(debug)\n\tdefer w.Destroy()\n\tw.SetTitle(\"Minimal webview example\")\n\tw.SetSize(800, 600, webview.HintNone)\n\tw.Navigate(\"http://\" + addr)\n\tw.Run()\n}\n"
  },
  {
    "path": "_examples/dropzonejs/README.md",
    "content": "# Articles\n\n* [How to build a file upload form using DropzoneJS and Go](https://hackernoon.com/how-to-build-a-file-upload-form-using-dropzonejs-and-go-8fb9f258a991)\n* [How to display existing files on server using DropzoneJS and Go](https://hackernoon.com/how-to-display-existing-files-on-server-using-dropzonejs-and-go-53e24b57ba19)\n\n# Content\n\nThis is the part 1 of 2 in DropzoneJS + Go series.\n\n- [Part 1: How to build a file upload form](README.md)\n- [Part 2: How to display existing files on server](README_PART2.md)\n\n# DropzoneJS + Go: How to build a file upload form\n\n[DropzoneJS](https://github.com/enyo/dropzone) is an open source library that provides drag'n'drop file uploads with image previews. It is a great JavaScript library which actually does not even rely on JQuery. \nIn this tutorial, we are building a multiple file upload form using DropzoneJS, and the backend will be handled by Go and [Iris](https://iris-go.com).\n\n## Table Of Content\n\n- [Preparation](#preparation)\n- [Work with DropzoneJS](#work-with-dropzonejs)\n- [Work with Go](#work-with-go)\n\n## Preparation\n\n1. Download [Go(Golang)](https://golang.org/dl), setup your computer as shown there and continue to 2.\n2. Install [Iris](https://github.com/kataras/iris); open a terminal and execute `go get -u github.com/kataras/iris`\n3. Download DropzoneJS from [this URL](https://raw.githubusercontent.com/enyo/dropzone/master/dist/dropzone.js). DropzoneJS does not rely on JQuery, you will not have to worry that, upgrading JQuery version breaks your application.\n4. Download dropzone.css from [this URL](https://raw.githubusercontent.com/enyo/dropzone/master/dist/dropzone.css), if you want some already made css.\n5. Create a folder \"./public/uploads\", this is for storing uploaded files.\n6. Create a file \"./views/upload.html\", this is for the front form page.\n7. Create a file \"./main.go\", this is for handling backend file upload process.\n\nYour folder&file structure should look like this after the preparation:\n\n![folder&file structure](folder_structure.png)\n\n## Work with DropzoneJS\n\nOpen file \"./views/upload.html\" and let us create a DropzoneJs form.\n\nCopy the content below to \"./views/upload.html\" and we will go through each line of code individually.\n\n```html\n<!-- /views/upload.html -->\n<html>\n\n<head>\n    <title>DropzoneJS Uploader</title>\n\n    <!-- 1 -->\n    <link href=\"/public/css/dropzone.css\" type=\"text/css\" rel=\"stylesheet\" />\n\n    <!-- 2 -->\n    <script src=\"/public/js/dropzone.js\"></script>\n</head>\n\n<body>\n\n    <!-- 3 -->\n    <form action=\"/upload\" method=\"POST\" class=\"dropzone\" id=\"my-dropzone\">\n        <div class=\"fallback\">\n            <input name=\"file\" type=\"file\" multiple />\n            <input type=\"submit\" value=\"Upload\" />\n        </div>\n    </form>\n</body>\n\n</html>\n```\n\n1. Include the CSS Stylesheet.\n2. Include DropzoneJS JavaScript library.\n3. Create an upload form with css class \"dropzone\" and \"action\" is the route path \"/upload\". Note that we did create an input filed for fallback mode. This is all handled by DropzoneJS library itself. All we need to do is assign css class \"dropzone\" to the form. By default, DropzoneJS will find all forms with class \"dropzone\" and automatically attach itself to it.\n\n## Work with Go\n\nNow you have come to Last part of the tutorial. In this section, we will store files sent from DropzoneJS to the \"./public/uploads\" folder.\n\nOpen \"main.go\" and copy the code below:\n\n```go\n// main.go\n\npackage main\n\nimport (\n    \"os\"\n    \"io\"\n    \"strings\"\n\n    \"github.com/kataras/iris/v12\"\n)\n\nconst uploadsDir = \"./public/uploads/\"\n\nfunc main() {\n    app := iris.New()\n\n    // Register templates\n    app.RegisterView(iris.HTML(\"./views\", \".html\"))\n\n    // Make the /public route path to statically serve the ./public/... contents\n    app.HandleDir(\"/public\", iris.Dir(\"./public\"))\n\n    // Render the actual form\n    // GET: http://localhost:8080\n    app.Get(\"/\", func(ctx iris.Context) {\n        if err := ctx.View(\"upload.html\"); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n    })\n\n    // Upload the file to the server\n    // POST: http://localhost:8080/upload\n    app.Post(\"/upload\", iris.LimitRequestBodySize(10<<20), func(ctx iris.Context) {\n        // Get the file from the dropzone request\n        file, info, err := ctx.FormFile(\"file\")\n        if err != nil {\n            ctx.StatusCode(iris.StatusInternalServerError)\n            ctx.Application().Logger().Warnf(\"Error while uploading: %v\", err.Error())\n            return\n        }\n\n        defer file.Close()\n        fname := info.Filename\n\n        // Create a file with the same name\n        // assuming that you have a folder named 'uploads'\n        out, err := os.OpenFile(uploadsDir+fname,\n            os.O_WRONLY|os.O_CREATE, 0666)\n\n        if err != nil {\n            ctx.StatusCode(iris.StatusInternalServerError)\n            ctx.Application().Logger().Warnf(\"Error while preparing the new file: %v\", err.Error())\n            return\n        }\n        defer out.Close()\n\n        io.Copy(out, file)\n    })\n\n    // Start the server at http://localhost:8080\n    app.Listen(\":8080\")\n}\n```\n\n1. Create a new Iris app.\n2. Register and load templates from the \"views\" folder.\n3. Make the \"/public\" route path to statically serve the ./public/... folder's contents\n4. Create a route to serve the upload form.\n5. Create a route to handle the POST form data from the DropzoneJS' form \n6. Declare a variable for destination folder.\n7. If file is sent to the page, store the file object to a temporary \"file\" variable.\n8. Move uploaded file to destination based on the uploadsDir+uploaded file's name.\n\n### Running the server\n\nOpen the terminal at the current project's folder and execute:\n\n```bash\n$ go run main.go\nNow listening on: http://localhost:8080\nApplication started. Press CTRL+C to shut down.\n```\n\nNow go to browser, and navigate to http://localhost:8080, you should be able to see a page as below:\n\n![no files screenshot](no_files.png)\n![with uploaded files screenshot](with_files.png)"
  },
  {
    "path": "_examples/dropzonejs/README_PART2.md",
    "content": "# Articles\n\n* [How to build a file upload form using DropzoneJS and Go](https://hackernoon.com/how-to-build-a-file-upload-form-using-dropzonejs-and-go-8fb9f258a991)\n* [How to display existing files on server using DropzoneJS and Go](https://hackernoon.com/how-to-display-existing-files-on-server-using-dropzonejs-and-go-53e24b57ba19)\n\n# Content\n\nThis is the part 2 of 2 in DropzoneJS + Go series.\n\n- [Part 1: How to build a file upload form](README.md)\n- [Part 2: How to display existing files on server](README_PART2.md)\n\n# DropzoneJS + Go: How to display existing files on server\n\nIn this tutorial, we will show you how to display existing files on the server when using DropzoneJS and Go. This tutorial is based on [How to build a file upload form using DropzoneJS and Go](README.md). Make sure you have read it before proceeding to content in this tutorial.\n\n## Table Of Content\n\n- [Preparation](#preparation)\n- [Modify the Server side](#modify-the-server-side)\n- [Modify the Client side](#modify-the-client-side)\n- [References](#references)\n- [The End](#the-end)\n\n## Preparation\n\nInstall the go package \"github.com/nfnt/resize\" with `go get github.com/nfnt/resize`, we need it to create thumbnails.\n\nIn previous [tutorial](README.md). We have already set up a proper working DropzoneJs upload form. There is no additional file needed for this tutorial. What we need to do is to make some modifications to file below:\n\n1. main.go\n2. views/upload.html\n\nLet us get started!\n\n## Modify the Server side\n\nIn previous tutorial. All \"/upload\" does is to store uploaded files to the server directory \"./public/uploads\". So we need to add a piece of code to retrieve stored files' information (name and size), and return it in JSON format.\n\nCopy the content below to \"main.go\". Read comments for details.\n\n```go\n// main.go\n\npackage main\n\nimport (\n    \"image/jpeg\"\n    \"image/png\"\n    \"io\"\n    \"os\"\n    \"path\"\n    \"path/filepath\"\n    \"strings\"\n    \"sync\"\n\n    \"github.com/kataras/iris/v12\"\n\n    \"github.com/nfnt/resize\" // $ go get -u github.com/nfnt/resize\n)\n\nconst uploadsDir = \"./public/uploads/\"\n\ntype uploadedFile struct {\n    // {name: \"\", size: } are the dropzone's only requirements.\n    Name string `json:\"name\"`\n    Size int64  `json:\"size\"`\n}\n\ntype uploadedFiles struct {\n    dir   string\n    items []uploadedFile\n    mu    sync.RWMutex // slices are safe but RWMutex is a good practise for you.\n}\n\n// scan the ./public/uploads folder for any files\n// add them to a new  uploadedFiles list.\nfunc scanUploads(dir string) *uploadedFiles {\n    f := new(uploadedFiles)\n\n    lindex := dir[len(dir)-1]\n    if lindex != os.PathSeparator && lindex != '/' {\n        dir += string(os.PathSeparator)\n    }\n\n    // create directories if necessary\n    // and if, then return empty uploaded files; skipping the scan.\n    if err := os.MkdirAll(dir, os.FileMode(0666)); err != nil {\n        return f\n    }\n\n    // otherwise scan the given \"dir\" for files.\n    f.scan(dir)\n    return f\n}\n\nfunc (f *uploadedFiles) scan(dir string) {\n    f.dir = dir\n    filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {\n\n        // if it's directory or a thumbnail we saved earlier, skip it.\n        if info.IsDir() || strings.HasPrefix(info.Name(), \"thumbnail_\") {\n            return nil\n        }\n\n        f.add(info.Name(), info.Size())\n        return nil\n    })\n}\n\n// add the file's Name and Size to the uploadedFiles memory list\nfunc (f *uploadedFiles) add(name string, size int64) uploadedFile {\n    uf := uploadedFile{\n        Name: name,\n        Size: size,\n    }\n    f.mu.Lock()\n    f.items = append(f.items, uf)\n    f.mu.Unlock()\n\n    return uf\n}\n\n// create thumbnail 100x100\n// and save that to the ./public/uploads/thumbnail_$FILENAME\nfunc (f *uploadedFiles) createThumbnail(uf uploadedFile) {\n    file, err := os.Open(path.Join(f.dir, uf.Name))\n    if err != nil {\n        return\n    }\n    defer file.Close()\n\n    name := strings.ToLower(uf.Name)\n\n    out, err := os.OpenFile(f.dir+\"thumbnail_\"+uf.Name,\n        os.O_WRONLY|os.O_CREATE, 0666)\n    if err != nil {\n        return\n    }\n    defer out.Close()\n\n    if strings.HasSuffix(name, \".jpg\") {\n        // decode jpeg into image.Image\n        img, err := jpeg.Decode(file)\n        if err != nil {\n            return\n        }\n\n        // write new image to file\n        resized := resize.Thumbnail(180, 180, img, resize.Lanczos3)\n        jpeg.Encode(out, resized,\n            &jpeg.Options{Quality: jpeg.DefaultQuality})\n\n    } else if strings.HasSuffix(name, \".png\") {\n        img, err := png.Decode(file)\n        if err != nil {\n            return\n        }\n\n        // write new image to file\n        resized := resize.Thumbnail(180, 180, img, resize.Lanczos3) // slower but better res\n        png.Encode(out, resized)\n    }\n    // and so on... you got the point, this code can be simplify, as a practise.\n}\n\nfunc main() {\n    app := iris.New()\n    app.RegisterView(iris.HTML(\"./views\", \".html\"))\n\n    app.HandleDir(\"/public\", iris.Dir(\"./public\"))\n\n    app.Get(\"/\", func(ctx iris.Context) {\n        if err := ctx.View(\"upload.html\"); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n    })\n\n    files := scanUploads(uploadsDir)\n\n    app.Get(\"/uploads\", func(ctx iris.Context) {\n        ctx.JSON(files.items)\n    })\n\n    app.Post(\"/upload\", iris.LimitRequestBodySize(10<<20), func(ctx iris.Context) {\n        // Get the file from the dropzone request\n        file, info, err := ctx.FormFile(\"file\")\n        if err != nil {\n            ctx.StatusCode(iris.StatusInternalServerError)\n            ctx.Application().Logger().Warnf(\"Error while uploading: %v\", err.Error())\n            return\n        }\n\n        defer file.Close()\n        fname := info.Filename\n\n        // Create a file with the same name\n        // assuming that you have a folder named 'uploads'\n        out, err := os.OpenFile(uploadsDir+fname,\n            os.O_WRONLY|os.O_CREATE, 0666)\n\n        if err != nil {\n            ctx.StatusCode(iris.StatusInternalServerError)\n            ctx.Application().Logger().Warnf(\"Error while preparing the new file: %v\", err.Error())\n            return\n        }\n        defer out.Close()\n\n        io.Copy(out, file)\n\n        // optionally, add that file to the list in order to be visible when refresh.\n        uploadedFile := files.add(fname, info.Size)\n        go files.createThumbnail(uploadedFile)\n    })\n\n    // start the server at http://localhost:8080\n    app.Listen(\":8080\")\n}\n```\n\n## Modify the Client side\n\nCopy content below to \"./views/upload.html\". We will go through modifications individually.\n\n```html\n<!-- /views/upload.html -->\n<html>\n\n<head>\n    <title>DropzoneJS Uploader</title>\n\n    <!-- 1 -->\n    <link href=\"/public/css/dropzone.css\" type=\"text/css\" rel=\"stylesheet\" />\n\n    <!-- 2 -->\n    <script src=\"/public/js/dropzone.js\"></script>\n    <!-- 4 -->\n    <script src=\"//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js\"></script>\n    <!-- 5 -->\n    <script>\n        Dropzone.options.myDropzone = {\n            paramName: \"file\", // The name that will be used to transfer the file\n            init: function () {\n                thisDropzone = this;\n                // 6\n                $.get('/uploads', function (data) {\n\n                    if (data == null) {\n                        return;\n                    }\n                    // 7\n                    $.each(data, function (key, value) {\n                        var mockFile = { name: value.name, size: value.size };\n\n                        thisDropzone.emit(\"addedfile\", mockFile);\n                        thisDropzone.options.thumbnail.call(thisDropzone, mockFile, '/public/uploads/thumbnail_' + value.name);\n\n                        // Make sure that there is no progress bar, etc...\n                        thisDropzone.emit(\"complete\", mockFile);\n                    });\n\n                });\n            }\n        };\n    </script>\n</head>\n\n<body>\n\n    <!-- 3 -->\n    <form action=\"/upload\" method=\"POST\" class=\"dropzone\" id=\"my-dropzone\">\n        <div class=\"fallback\">\n            <input name=\"file\" type=\"file\" multiple />\n            <input type=\"submit\" value=\"Upload\" />\n        </div>\n    </form>\n</body>\n\n</html>\n```\n\n1. We added Jquery library into our page. This actually not for DropzoneJs directly. We are using Jquery's ajax function **$.get** only. You will see below\n2. We added an ID element (my-dropzone) to the form. This is needed because we need to pass configuration values to Dropzone. And to do it, we must have an ID reference of it. So that we can configure it by assigning values to Dropzone.options.myDropzone. A lot of people face confusion when configuring Dropzone. To put it in a simple way. Do not take Dropzone as a Jquery plugin, it has its own syntax and you need to follow it.\n3. This starts the main part of modification. What we did here is to pass a function to listen to Dropzone's init event. This event is called when Dropzone is initialized.\n4. Retrieve files details from the new \"/uploads\" via ajax.\n5. Create mockFile using values from server. mockFile is simply JavaScript objects with properties of name and size. Then we call Dropzone's **addedfile** and **thumbnail** functions explicitly to put existing files to Dropzone upload area and generate its thumbnail.\n\n### Running the server\n\nOpen the terminal at the current project's folder and execute:\n\n```bash\n$ go run main.go\nNow listening on: http://localhost:8080\nApplication started. Press CTRL+C to shut down.\n```\n\nIf you have done it successfully. Now go and upload some images and reload the upload page. Already uploaded files should auto display in Dropzone area.\n\n![with uploaded files screenshot](with_files.png)\n\n## References\n\n- http://www.dropzonejs.com/#server-side-implementation\n- https://www.startutorial.com/articles/view/how-to-build-a-file-upload-form-using-dropzonejs-and-php\n- https://docs.iris-go.com\n- https://github.com/kataras/iris/tree/main/_examples/dropzonejs\n\n## The end\n\nHopefully this simple tutorial helped you with your development.\nIf you like my post, please follow me on [Twitter](https://twitter.com/makismaropoulos) and help spread the word. I need your support to continue."
  },
  {
    "path": "_examples/dropzonejs/meta.yml",
    "content": "Name: DropzoneJS\nArticles:\n    - Title: How to build a file upload form using DropzoneJS and Go\n      Source: https://medium.com/hackernoon/how-to-build-a-file-upload-form-using-dropzonejs-and-go-8fb9f258a991\n      Author: https://twitter.com/@kataras\n    - Title: How to display existing files on server using DropzoneJS and Go\n      Source: https://medium.com/@kataras/how-to-display-existing-files-on-server-using-dropzonejs-and-go-53e24b57ba19\n      Author: https://twitter.com/@kataras"
  },
  {
    "path": "_examples/dropzonejs/src/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"image/jpeg\"\n\t\"image/png\"\n\t\"io\"\n\t\"os\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/kataras/iris/v12\"\n\n\t\"github.com/nfnt/resize\"\n)\n\n// $ go get -u github.com/nfnt/resize\n\nconst uploadsDir = \"./public/uploads/\"\n\ntype uploadedFile struct {\n\t// {name: \"\", size: } are the dropzone's only requirements.\n\tName string `json:\"name\"`\n\tSize int64  `json:\"size\"`\n}\n\ntype uploadedFiles struct {\n\tdir   string\n\titems []uploadedFile\n\tmu    sync.RWMutex // slices are safe but RWMutex is a good practise for you.\n}\n\nfunc scanUploads(dir string) *uploadedFiles {\n\tf := new(uploadedFiles)\n\n\tlindex := dir[len(dir)-1]\n\tif lindex != os.PathSeparator && lindex != '/' {\n\t\tdir += string(os.PathSeparator)\n\t}\n\n\t// create directories if necessary\n\t// and if, then return empty uploaded files; skipping the scan.\n\tif err := os.MkdirAll(dir, os.FileMode(0666)); err != nil {\n\t\treturn f\n\t}\n\n\t// otherwise scan the given \"dir\" for files.\n\tf.scan(dir)\n\treturn f\n}\n\nfunc (f *uploadedFiles) scan(dir string) {\n\tf.dir = dir\n\tfilepath.Walk(dir, func(path string, info os.FileInfo, err error) error {\n\t\t// if it's directory or a thumbnail we saved earlier, skip it.\n\t\tif info.IsDir() || strings.HasPrefix(info.Name(), \"thumbnail_\") {\n\t\t\treturn nil\n\t\t}\n\n\t\tf.add(info.Name(), info.Size())\n\t\treturn nil\n\t})\n}\n\nfunc (f *uploadedFiles) add(name string, size int64) uploadedFile {\n\tuf := uploadedFile{\n\t\tName: name,\n\t\tSize: size,\n\t}\n\n\tf.mu.Lock()\n\tf.items = append(f.items, uf)\n\tf.mu.Unlock()\n\n\treturn uf\n}\n\nfunc (f *uploadedFiles) createThumbnail(uf uploadedFile) {\n\tfile, err := os.Open(path.Join(f.dir, uf.Name))\n\tif err != nil {\n\t\treturn\n\t}\n\tdefer file.Close()\n\n\tname := strings.ToLower(uf.Name)\n\n\tout, err := os.OpenFile(f.dir+\"thumbnail_\"+uf.Name,\n\t\tos.O_WRONLY|os.O_CREATE, 0666)\n\tif err != nil {\n\t\treturn\n\t}\n\tdefer out.Close()\n\n\tif strings.HasSuffix(name, \".jpg\") {\n\t\t// decode jpeg into image.Image\n\t\timg, err := jpeg.Decode(file)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\n\t\t// write new image to file\n\t\tresized := resize.Thumbnail(180, 180, img, resize.Lanczos3)\n\t\tjpeg.Encode(out, resized,\n\t\t\t&jpeg.Options{Quality: jpeg.DefaultQuality})\n\n\t} else if strings.HasSuffix(name, \".png\") {\n\t\timg, err := png.Decode(file)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\n\t\t// write new image to file\n\t\tresized := resize.Thumbnail(180, 180, img, resize.Lanczos3) // slower but better res\n\t\tpng.Encode(out, resized)\n\t}\n\t// and so on... you got the point, this code can be simplify, as a practise.\n}\n\nfunc main() {\n\tapp := iris.New()\n\tapp.RegisterView(iris.HTML(\"./views\", \".html\"))\n\n\tapp.HandleDir(\"/public\", iris.Dir(\"./public\"))\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tif err := ctx.View(\"upload.html\"); err != nil {\n\t\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\t\treturn\n\t\t}\n\t})\n\n\tfiles := scanUploads(uploadsDir)\n\n\tapp.Get(\"/uploads\", func(ctx iris.Context) {\n\t\tctx.JSON(files.items)\n\t})\n\n\tapp.Post(\"/upload\", iris.LimitRequestBodySize(10<<20), func(ctx iris.Context) {\n\t\t// Get the file from the dropzone request\n\t\tfile, info, err := ctx.FormFile(\"file\")\n\t\tif err != nil {\n\t\t\tctx.StatusCode(iris.StatusInternalServerError)\n\t\t\tctx.Application().Logger().Warnf(\"Error while uploading: %v\", err.Error())\n\t\t\treturn\n\t\t}\n\n\t\tdefer file.Close()\n\t\tfname := info.Filename\n\n\t\t// Create a file with the same name\n\t\t// assuming that you have a folder named 'uploads'\n\t\tout, err := os.OpenFile(uploadsDir+fname,\n\t\t\tos.O_WRONLY|os.O_CREATE, 0666)\n\t\tif err != nil {\n\t\t\tctx.StatusCode(iris.StatusInternalServerError)\n\t\t\tctx.Application().Logger().Warnf(\"Error while preparing the new file: %v\", err.Error())\n\t\t\treturn\n\t\t}\n\t\tdefer out.Close()\n\n\t\tio.Copy(out, file)\n\n\t\t// optionally, add that file to the list in order to be visible when refresh.\n\t\tuploadedFile := files.add(fname, info.Size)\n\t\tgo files.createThumbnail(uploadedFile)\n\t})\n\n\t// start the server at http://localhost:8080\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/dropzonejs/src/public/css/dropzone.css",
    "content": "/*\n * The MIT License\n * Copyright (c) 2012 Matias Meno <m@tias.me>\n */\n@-webkit-keyframes passing-through {\n  0% {\n    opacity: 0;\n    -webkit-transform: translateY(40px);\n    -moz-transform: translateY(40px);\n    -ms-transform: translateY(40px);\n    -o-transform: translateY(40px);\n    transform: translateY(40px); }\n  30%, 70% {\n    opacity: 1;\n    -webkit-transform: translateY(0px);\n    -moz-transform: translateY(0px);\n    -ms-transform: translateY(0px);\n    -o-transform: translateY(0px);\n    transform: translateY(0px); }\n  100% {\n    opacity: 0;\n    -webkit-transform: translateY(-40px);\n    -moz-transform: translateY(-40px);\n    -ms-transform: translateY(-40px);\n    -o-transform: translateY(-40px);\n    transform: translateY(-40px); } }\n@-moz-keyframes passing-through {\n  0% {\n    opacity: 0;\n    -webkit-transform: translateY(40px);\n    -moz-transform: translateY(40px);\n    -ms-transform: translateY(40px);\n    -o-transform: translateY(40px);\n    transform: translateY(40px); }\n  30%, 70% {\n    opacity: 1;\n    -webkit-transform: translateY(0px);\n    -moz-transform: translateY(0px);\n    -ms-transform: translateY(0px);\n    -o-transform: translateY(0px);\n    transform: translateY(0px); }\n  100% {\n    opacity: 0;\n    -webkit-transform: translateY(-40px);\n    -moz-transform: translateY(-40px);\n    -ms-transform: translateY(-40px);\n    -o-transform: translateY(-40px);\n    transform: translateY(-40px); } }\n@keyframes passing-through {\n  0% {\n    opacity: 0;\n    -webkit-transform: translateY(40px);\n    -moz-transform: translateY(40px);\n    -ms-transform: translateY(40px);\n    -o-transform: translateY(40px);\n    transform: translateY(40px); }\n  30%, 70% {\n    opacity: 1;\n    -webkit-transform: translateY(0px);\n    -moz-transform: translateY(0px);\n    -ms-transform: translateY(0px);\n    -o-transform: translateY(0px);\n    transform: translateY(0px); }\n  100% {\n    opacity: 0;\n    -webkit-transform: translateY(-40px);\n    -moz-transform: translateY(-40px);\n    -ms-transform: translateY(-40px);\n    -o-transform: translateY(-40px);\n    transform: translateY(-40px); } }\n@-webkit-keyframes slide-in {\n  0% {\n    opacity: 0;\n    -webkit-transform: translateY(40px);\n    -moz-transform: translateY(40px);\n    -ms-transform: translateY(40px);\n    -o-transform: translateY(40px);\n    transform: translateY(40px); }\n  30% {\n    opacity: 1;\n    -webkit-transform: translateY(0px);\n    -moz-transform: translateY(0px);\n    -ms-transform: translateY(0px);\n    -o-transform: translateY(0px);\n    transform: translateY(0px); } }\n@-moz-keyframes slide-in {\n  0% {\n    opacity: 0;\n    -webkit-transform: translateY(40px);\n    -moz-transform: translateY(40px);\n    -ms-transform: translateY(40px);\n    -o-transform: translateY(40px);\n    transform: translateY(40px); }\n  30% {\n    opacity: 1;\n    -webkit-transform: translateY(0px);\n    -moz-transform: translateY(0px);\n    -ms-transform: translateY(0px);\n    -o-transform: translateY(0px);\n    transform: translateY(0px); } }\n@keyframes slide-in {\n  0% {\n    opacity: 0;\n    -webkit-transform: translateY(40px);\n    -moz-transform: translateY(40px);\n    -ms-transform: translateY(40px);\n    -o-transform: translateY(40px);\n    transform: translateY(40px); }\n  30% {\n    opacity: 1;\n    -webkit-transform: translateY(0px);\n    -moz-transform: translateY(0px);\n    -ms-transform: translateY(0px);\n    -o-transform: translateY(0px);\n    transform: translateY(0px); } }\n@-webkit-keyframes pulse {\n  0% {\n    -webkit-transform: scale(1);\n    -moz-transform: scale(1);\n    -ms-transform: scale(1);\n    -o-transform: scale(1);\n    transform: scale(1); }\n  10% {\n    -webkit-transform: scale(1.1);\n    -moz-transform: scale(1.1);\n    -ms-transform: scale(1.1);\n    -o-transform: scale(1.1);\n    transform: scale(1.1); }\n  20% {\n    -webkit-transform: scale(1);\n    -moz-transform: scale(1);\n    -ms-transform: scale(1);\n    -o-transform: scale(1);\n    transform: scale(1); } }\n@-moz-keyframes pulse {\n  0% {\n    -webkit-transform: scale(1);\n    -moz-transform: scale(1);\n    -ms-transform: scale(1);\n    -o-transform: scale(1);\n    transform: scale(1); }\n  10% {\n    -webkit-transform: scale(1.1);\n    -moz-transform: scale(1.1);\n    -ms-transform: scale(1.1);\n    -o-transform: scale(1.1);\n    transform: scale(1.1); }\n  20% {\n    -webkit-transform: scale(1);\n    -moz-transform: scale(1);\n    -ms-transform: scale(1);\n    -o-transform: scale(1);\n    transform: scale(1); } }\n@keyframes pulse {\n  0% {\n    -webkit-transform: scale(1);\n    -moz-transform: scale(1);\n    -ms-transform: scale(1);\n    -o-transform: scale(1);\n    transform: scale(1); }\n  10% {\n    -webkit-transform: scale(1.1);\n    -moz-transform: scale(1.1);\n    -ms-transform: scale(1.1);\n    -o-transform: scale(1.1);\n    transform: scale(1.1); }\n  20% {\n    -webkit-transform: scale(1);\n    -moz-transform: scale(1);\n    -ms-transform: scale(1);\n    -o-transform: scale(1);\n    transform: scale(1); } }\n.dropzone, .dropzone * {\n  box-sizing: border-box; }\n\n.dropzone {\n  min-height: 150px;\n  border: 2px solid rgba(0, 0, 0, 0.3);\n  background: white;\n  padding: 20px 20px; }\n  .dropzone.dz-clickable {\n    cursor: pointer; }\n    .dropzone.dz-clickable * {\n      cursor: default; }\n    .dropzone.dz-clickable .dz-message, .dropzone.dz-clickable .dz-message * {\n      cursor: pointer; }\n  .dropzone.dz-started .dz-message {\n    display: none; }\n  .dropzone.dz-drag-hover {\n    border-style: solid; }\n    .dropzone.dz-drag-hover .dz-message {\n      opacity: 0.5; }\n  .dropzone .dz-message {\n    text-align: center;\n    margin: 2em 0; }\n  .dropzone .dz-preview {\n    position: relative;\n    display: inline-block;\n    vertical-align: top;\n    margin: 16px;\n    min-height: 100px; }\n    .dropzone .dz-preview:hover {\n      z-index: 1000; }\n      .dropzone .dz-preview:hover .dz-details {\n        opacity: 1; }\n    .dropzone .dz-preview.dz-file-preview .dz-image {\n      border-radius: 20px;\n      background: #999;\n      background: linear-gradient(to bottom, #eee, #ddd); }\n    .dropzone .dz-preview.dz-file-preview .dz-details {\n      opacity: 1; }\n    .dropzone .dz-preview.dz-image-preview {\n      background: white; }\n      .dropzone .dz-preview.dz-image-preview .dz-details {\n        -webkit-transition: opacity 0.2s linear;\n        -moz-transition: opacity 0.2s linear;\n        -ms-transition: opacity 0.2s linear;\n        -o-transition: opacity 0.2s linear;\n        transition: opacity 0.2s linear; }\n    .dropzone .dz-preview .dz-remove {\n      font-size: 14px;\n      text-align: center;\n      display: block;\n      cursor: pointer;\n      border: none; }\n      .dropzone .dz-preview .dz-remove:hover {\n        text-decoration: underline; }\n    .dropzone .dz-preview:hover .dz-details {\n      opacity: 1; }\n    .dropzone .dz-preview .dz-details {\n      z-index: 20;\n      position: absolute;\n      top: 0;\n      left: 0;\n      opacity: 0;\n      font-size: 13px;\n      min-width: 100%;\n      max-width: 100%;\n      padding: 2em 1em;\n      text-align: center;\n      color: rgba(0, 0, 0, 0.9);\n      line-height: 150%; }\n      .dropzone .dz-preview .dz-details .dz-size {\n        margin-bottom: 1em;\n        font-size: 16px; }\n      .dropzone .dz-preview .dz-details .dz-filename {\n        white-space: nowrap; }\n        .dropzone .dz-preview .dz-details .dz-filename:hover span {\n          border: 1px solid rgba(200, 200, 200, 0.8);\n          background-color: rgba(255, 255, 255, 0.8); }\n        .dropzone .dz-preview .dz-details .dz-filename:not(:hover) {\n          overflow: hidden;\n          text-overflow: ellipsis; }\n          .dropzone .dz-preview .dz-details .dz-filename:not(:hover) span {\n            border: 1px solid transparent; }\n      .dropzone .dz-preview .dz-details .dz-filename span, .dropzone .dz-preview .dz-details .dz-size span {\n        background-color: rgba(255, 255, 255, 0.4);\n        padding: 0 0.4em;\n        border-radius: 3px; }\n    .dropzone .dz-preview:hover .dz-image img {\n      -webkit-transform: scale(1.05, 1.05);\n      -moz-transform: scale(1.05, 1.05);\n      -ms-transform: scale(1.05, 1.05);\n      -o-transform: scale(1.05, 1.05);\n      transform: scale(1.05, 1.05);\n      -webkit-filter: blur(8px);\n      filter: blur(8px); }\n    .dropzone .dz-preview .dz-image {\n      border-radius: 20px;\n      overflow: hidden;\n      width: 120px;\n      height: 120px;\n      position: relative;\n      display: block;\n      z-index: 10; }\n      .dropzone .dz-preview .dz-image img {\n        display: block; }\n    .dropzone .dz-preview.dz-success .dz-success-mark {\n      -webkit-animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1);\n      -moz-animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1);\n      -ms-animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1);\n      -o-animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1);\n      animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1); }\n    .dropzone .dz-preview.dz-error .dz-error-mark {\n      opacity: 1;\n      -webkit-animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1);\n      -moz-animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1);\n      -ms-animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1);\n      -o-animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1);\n      animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1); }\n    .dropzone .dz-preview .dz-success-mark, .dropzone .dz-preview .dz-error-mark {\n      pointer-events: none;\n      opacity: 0;\n      z-index: 500;\n      position: absolute;\n      display: block;\n      top: 50%;\n      left: 50%;\n      margin-left: -27px;\n      margin-top: -27px; }\n      .dropzone .dz-preview .dz-success-mark svg, .dropzone .dz-preview .dz-error-mark svg {\n        display: block;\n        width: 54px;\n        height: 54px; }\n    .dropzone .dz-preview.dz-processing .dz-progress {\n      opacity: 1;\n      -webkit-transition: all 0.2s linear;\n      -moz-transition: all 0.2s linear;\n      -ms-transition: all 0.2s linear;\n      -o-transition: all 0.2s linear;\n      transition: all 0.2s linear; }\n    .dropzone .dz-preview.dz-complete .dz-progress {\n      opacity: 0;\n      -webkit-transition: opacity 0.4s ease-in;\n      -moz-transition: opacity 0.4s ease-in;\n      -ms-transition: opacity 0.4s ease-in;\n      -o-transition: opacity 0.4s ease-in;\n      transition: opacity 0.4s ease-in; }\n    .dropzone .dz-preview:not(.dz-processing) .dz-progress {\n      -webkit-animation: pulse 6s ease infinite;\n      -moz-animation: pulse 6s ease infinite;\n      -ms-animation: pulse 6s ease infinite;\n      -o-animation: pulse 6s ease infinite;\n      animation: pulse 6s ease infinite; }\n    .dropzone .dz-preview .dz-progress {\n      opacity: 1;\n      z-index: 1000;\n      pointer-events: none;\n      position: absolute;\n      height: 16px;\n      left: 50%;\n      top: 50%;\n      margin-top: -8px;\n      width: 80px;\n      margin-left: -40px;\n      background: rgba(255, 255, 255, 0.9);\n      -webkit-transform: scale(1);\n      border-radius: 8px;\n      overflow: hidden; }\n      .dropzone .dz-preview .dz-progress .dz-upload {\n        background: #333;\n        background: linear-gradient(to bottom, #666, #444);\n        position: absolute;\n        top: 0;\n        left: 0;\n        bottom: 0;\n        width: 0;\n        -webkit-transition: width 300ms ease-in-out;\n        -moz-transition: width 300ms ease-in-out;\n        -ms-transition: width 300ms ease-in-out;\n        -o-transition: width 300ms ease-in-out;\n        transition: width 300ms ease-in-out; }\n    .dropzone .dz-preview.dz-error .dz-error-message {\n      display: block; }\n    .dropzone .dz-preview.dz-error:hover .dz-error-message {\n      opacity: 1;\n      pointer-events: auto; }\n    .dropzone .dz-preview .dz-error-message {\n      pointer-events: none;\n      z-index: 1000;\n      position: absolute;\n      display: block;\n      display: none;\n      opacity: 0;\n      -webkit-transition: opacity 0.3s ease;\n      -moz-transition: opacity 0.3s ease;\n      -ms-transition: opacity 0.3s ease;\n      -o-transition: opacity 0.3s ease;\n      transition: opacity 0.3s ease;\n      border-radius: 8px;\n      font-size: 13px;\n      top: 130px;\n      left: -10px;\n      width: 140px;\n      background: #be2626;\n      background: linear-gradient(to bottom, #be2626, #a92222);\n      padding: 0.5em 1.2em;\n      color: white; }\n      .dropzone .dz-preview .dz-error-message:after {\n        content: '';\n        position: absolute;\n        top: -6px;\n        left: 64px;\n        width: 0;\n        height: 0;\n        border-left: 6px solid transparent;\n        border-right: 6px solid transparent;\n        border-bottom: 6px solid #be2626; }\n"
  },
  {
    "path": "_examples/dropzonejs/src/public/js/dropzone.js",
    "content": "\n/*\n *\n * More info at [www.dropzonejs.com](http://www.dropzonejs.com)\n *\n * Copyright (c) 2012, Matias Meno\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n */\n\n(function() {\n  var Dropzone, Emitter, ExifRestore, camelize, contentLoaded, detectVerticalSquash, drawImageIOSFix, noop, without,\n    slice = [].slice,\n    extend1 = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },\n    hasProp = {}.hasOwnProperty;\n\n  noop = function() {};\n\n  Emitter = (function() {\n    function Emitter() {}\n\n    Emitter.prototype.addEventListener = Emitter.prototype.on;\n\n    Emitter.prototype.on = function(event, fn) {\n      this._callbacks = this._callbacks || {};\n      if (!this._callbacks[event]) {\n        this._callbacks[event] = [];\n      }\n      this._callbacks[event].push(fn);\n      return this;\n    };\n\n    Emitter.prototype.emit = function() {\n      var args, callback, callbacks, event, j, len;\n      event = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];\n      this._callbacks = this._callbacks || {};\n      callbacks = this._callbacks[event];\n      if (callbacks) {\n        for (j = 0, len = callbacks.length; j < len; j++) {\n          callback = callbacks[j];\n          callback.apply(this, args);\n        }\n      }\n      return this;\n    };\n\n    Emitter.prototype.removeListener = Emitter.prototype.off;\n\n    Emitter.prototype.removeAllListeners = Emitter.prototype.off;\n\n    Emitter.prototype.removeEventListener = Emitter.prototype.off;\n\n    Emitter.prototype.off = function(event, fn) {\n      var callback, callbacks, i, j, len;\n      if (!this._callbacks || arguments.length === 0) {\n        this._callbacks = {};\n        return this;\n      }\n      callbacks = this._callbacks[event];\n      if (!callbacks) {\n        return this;\n      }\n      if (arguments.length === 1) {\n        delete this._callbacks[event];\n        return this;\n      }\n      for (i = j = 0, len = callbacks.length; j < len; i = ++j) {\n        callback = callbacks[i];\n        if (callback === fn) {\n          callbacks.splice(i, 1);\n          break;\n        }\n      }\n      return this;\n    };\n\n    return Emitter;\n\n  })();\n\n  Dropzone = (function(superClass) {\n    var extend, resolveOption;\n\n    extend1(Dropzone, superClass);\n\n    Dropzone.prototype.Emitter = Emitter;\n\n\n    /*\n    This is a list of all available events you can register on a dropzone object.\n    \n    You can register an event handler like this:\n    \n        dropzone.on(\"dragEnter\", function() { });\n     */\n\n    Dropzone.prototype.events = [\"drop\", \"dragstart\", \"dragend\", \"dragenter\", \"dragover\", \"dragleave\", \"addedfile\", \"addedfiles\", \"removedfile\", \"thumbnail\", \"error\", \"errormultiple\", \"processing\", \"processingmultiple\", \"uploadprogress\", \"totaluploadprogress\", \"sending\", \"sendingmultiple\", \"success\", \"successmultiple\", \"canceled\", \"canceledmultiple\", \"complete\", \"completemultiple\", \"reset\", \"maxfilesexceeded\", \"maxfilesreached\", \"queuecomplete\"];\n\n    Dropzone.prototype.defaultOptions = {\n      url: null,\n      method: \"post\",\n      withCredentials: false,\n      timeout: 30000,\n      parallelUploads: 2,\n      uploadMultiple: false,\n      maxFilesize: 256,\n      paramName: \"file\",\n      createImageThumbnails: true,\n      maxThumbnailFilesize: 10,\n      thumbnailWidth: 120,\n      thumbnailHeight: 120,\n      thumbnailMethod: 'crop',\n      resizeWidth: null,\n      resizeHeight: null,\n      resizeMimeType: null,\n      resizeQuality: 0.8,\n      resizeMethod: 'contain',\n      filesizeBase: 1000,\n      maxFiles: null,\n      params: {},\n      headers: null,\n      clickable: true,\n      ignoreHiddenFiles: true,\n      acceptedFiles: null,\n      acceptedMimeTypes: null,\n      autoProcessQueue: true,\n      autoQueue: true,\n      addRemoveLinks: false,\n      previewsContainer: null,\n      hiddenInputContainer: \"body\",\n      capture: null,\n      renameFilename: null,\n      renameFile: null,\n      forceFallback: false,\n      dictDefaultMessage: \"Drop files here to upload\",\n      dictFallbackMessage: \"Your browser does not support drag'n'drop file uploads.\",\n      dictFallbackText: \"Please use the fallback form below to upload your files like in the olden days.\",\n      dictFileTooBig: \"File is too big ({{filesize}}MiB). Max filesize: {{maxFilesize}}MiB.\",\n      dictInvalidFileType: \"You can't upload files of this type.\",\n      dictResponseError: \"Server responded with {{statusCode}} code.\",\n      dictCancelUpload: \"Cancel upload\",\n      dictCancelUploadConfirmation: \"Are you sure you want to cancel this upload?\",\n      dictRemoveFile: \"Remove file\",\n      dictRemoveFileConfirmation: null,\n      dictMaxFilesExceeded: \"You can not upload any more files.\",\n      dictFileSizeUnits: {\n        tb: \"TB\",\n        gb: \"GB\",\n        mb: \"MB\",\n        kb: \"KB\",\n        b: \"b\"\n      },\n      init: function() {\n        return noop;\n      },\n      accept: function(file, done) {\n        return done();\n      },\n      fallback: function() {\n        var child, j, len, messageElement, ref, span;\n        this.element.className = this.element.className + \" dz-browser-not-supported\";\n        ref = this.element.getElementsByTagName(\"div\");\n        for (j = 0, len = ref.length; j < len; j++) {\n          child = ref[j];\n          if (/(^| )dz-message($| )/.test(child.className)) {\n            messageElement = child;\n            child.className = \"dz-message\";\n            continue;\n          }\n        }\n        if (!messageElement) {\n          messageElement = Dropzone.createElement(\"<div class=\\\"dz-message\\\"><span></span></div>\");\n          this.element.appendChild(messageElement);\n        }\n        span = messageElement.getElementsByTagName(\"span\")[0];\n        if (span) {\n          if (span.textContent != null) {\n            span.textContent = this.options.dictFallbackMessage;\n          } else if (span.innerText != null) {\n            span.innerText = this.options.dictFallbackMessage;\n          }\n        }\n        return this.element.appendChild(this.getFallbackForm());\n      },\n      resize: function(file, width, height, resizeMethod) {\n        var info, srcRatio, trgRatio;\n        info = {\n          srcX: 0,\n          srcY: 0,\n          srcWidth: file.width,\n          srcHeight: file.height\n        };\n        srcRatio = file.width / file.height;\n        if ((width == null) && (height == null)) {\n          width = info.srcWidth;\n          height = info.srcHeight;\n        } else if (width == null) {\n          width = height * srcRatio;\n        } else if (height == null) {\n          height = width / srcRatio;\n        }\n        width = Math.min(width, info.srcWidth);\n        height = Math.min(height, info.srcHeight);\n        trgRatio = width / height;\n        if (info.srcWidth > width || info.srcHeight > height) {\n          if (resizeMethod === 'crop') {\n            if (srcRatio > trgRatio) {\n              info.srcHeight = file.height;\n              info.srcWidth = info.srcHeight * trgRatio;\n            } else {\n              info.srcWidth = file.width;\n              info.srcHeight = info.srcWidth / trgRatio;\n            }\n          } else if (resizeMethod === 'contain') {\n            if (srcRatio > trgRatio) {\n              height = width / srcRatio;\n            } else {\n              width = height * srcRatio;\n            }\n          } else {\n            throw new Error(\"Unknown resizeMethod '\" + resizeMethod + \"'\");\n          }\n        }\n        info.srcX = (file.width - info.srcWidth) / 2;\n        info.srcY = (file.height - info.srcHeight) / 2;\n        info.trgWidth = width;\n        info.trgHeight = height;\n        return info;\n      },\n      transformFile: function(file, done) {\n        if ((this.options.resizeWidth || this.options.resizeHeight) && file.type.match(/image.*/)) {\n          return this.resizeImage(file, this.options.resizeWidth, this.options.resizeHeight, this.options.resizeMethod, done);\n        } else {\n          return done(file);\n        }\n      },\n      previewTemplate: \"<div class=\\\"dz-preview dz-file-preview\\\">\\n  <div class=\\\"dz-image\\\"><img data-dz-thumbnail /></div>\\n  <div class=\\\"dz-details\\\">\\n    <div class=\\\"dz-size\\\"><span data-dz-size></span></div>\\n    <div class=\\\"dz-filename\\\"><span data-dz-name></span></div>\\n  </div>\\n  <div class=\\\"dz-progress\\\"><span class=\\\"dz-upload\\\" data-dz-uploadprogress></span></div>\\n  <div class=\\\"dz-error-message\\\"><span data-dz-errormessage></span></div>\\n  <div class=\\\"dz-success-mark\\\">\\n    <svg width=\\\"54px\\\" height=\\\"54px\\\" viewBox=\\\"0 0 54 54\\\" version=\\\"1.1\\\" xmlns=\\\"http://www.w3.org/2000/svg\\\" xmlns:xlink=\\\"http://www.w3.org/1999/xlink\\\" xmlns:sketch=\\\"http://www.bohemiancoding.com/sketch/ns\\\">\\n      <title>Check</title>\\n      <defs></defs>\\n      <g id=\\\"Page-1\\\" stroke=\\\"none\\\" stroke-width=\\\"1\\\" fill=\\\"none\\\" fill-rule=\\\"evenodd\\\" sketch:type=\\\"MSPage\\\">\\n        <path d=\\\"M23.5,31.8431458 L17.5852419,25.9283877 C16.0248253,24.3679711 13.4910294,24.366835 11.9289322,25.9289322 C10.3700136,27.4878508 10.3665912,30.0234455 11.9283877,31.5852419 L20.4147581,40.0716123 C20.5133999,40.1702541 20.6159315,40.2626649 20.7218615,40.3488435 C22.2835669,41.8725651 24.794234,41.8626202 26.3461564,40.3106978 L43.3106978,23.3461564 C44.8771021,21.7797521 44.8758057,19.2483887 43.3137085,17.6862915 C41.7547899,16.1273729 39.2176035,16.1255422 37.6538436,17.6893022 L23.5,31.8431458 Z M27,53 C41.3594035,53 53,41.3594035 53,27 C53,12.6405965 41.3594035,1 27,1 C12.6405965,1 1,12.6405965 1,27 C1,41.3594035 12.6405965,53 27,53 Z\\\" id=\\\"Oval-2\\\" stroke-opacity=\\\"0.198794158\\\" stroke=\\\"#747474\\\" fill-opacity=\\\"0.816519475\\\" fill=\\\"#FFFFFF\\\" sketch:type=\\\"MSShapeGroup\\\"></path>\\n      </g>\\n    </svg>\\n  </div>\\n  <div class=\\\"dz-error-mark\\\">\\n    <svg width=\\\"54px\\\" height=\\\"54px\\\" viewBox=\\\"0 0 54 54\\\" version=\\\"1.1\\\" xmlns=\\\"http://www.w3.org/2000/svg\\\" xmlns:xlink=\\\"http://www.w3.org/1999/xlink\\\" xmlns:sketch=\\\"http://www.bohemiancoding.com/sketch/ns\\\">\\n      <title>Error</title>\\n      <defs></defs>\\n      <g id=\\\"Page-1\\\" stroke=\\\"none\\\" stroke-width=\\\"1\\\" fill=\\\"none\\\" fill-rule=\\\"evenodd\\\" sketch:type=\\\"MSPage\\\">\\n        <g id=\\\"Check-+-Oval-2\\\" sketch:type=\\\"MSLayerGroup\\\" stroke=\\\"#747474\\\" stroke-opacity=\\\"0.198794158\\\" fill=\\\"#FFFFFF\\\" fill-opacity=\\\"0.816519475\\\">\\n          <path d=\\\"M32.6568542,29 L38.3106978,23.3461564 C39.8771021,21.7797521 39.8758057,19.2483887 38.3137085,17.6862915 C36.7547899,16.1273729 34.2176035,16.1255422 32.6538436,17.6893022 L27,23.3431458 L21.3461564,17.6893022 C19.7823965,16.1255422 17.2452101,16.1273729 15.6862915,17.6862915 C14.1241943,19.2483887 14.1228979,21.7797521 15.6893022,23.3461564 L21.3431458,29 L15.6893022,34.6538436 C14.1228979,36.2202479 14.1241943,38.7516113 15.6862915,40.3137085 C17.2452101,41.8726271 19.7823965,41.8744578 21.3461564,40.3106978 L27,34.6568542 L32.6538436,40.3106978 C34.2176035,41.8744578 36.7547899,41.8726271 38.3137085,40.3137085 C39.8758057,38.7516113 39.8771021,36.2202479 38.3106978,34.6538436 L32.6568542,29 Z M27,53 C41.3594035,53 53,41.3594035 53,27 C53,12.6405965 41.3594035,1 27,1 C12.6405965,1 1,12.6405965 1,27 C1,41.3594035 12.6405965,53 27,53 Z\\\" id=\\\"Oval-2\\\" sketch:type=\\\"MSShapeGroup\\\"></path>\\n        </g>\\n      </g>\\n    </svg>\\n  </div>\\n</div>\",\n\n      /*\n      Those functions register themselves to the events on init and handle all\n      the user interface specific stuff. Overwriting them won't break the upload\n      but can break the way it's displayed.\n      You can overwrite them if you don't like the default behavior. If you just\n      want to add an additional event handler, register it on the dropzone object\n      and don't overwrite those options.\n       */\n      drop: function(e) {\n        return this.element.classList.remove(\"dz-drag-hover\");\n      },\n      dragstart: noop,\n      dragend: function(e) {\n        return this.element.classList.remove(\"dz-drag-hover\");\n      },\n      dragenter: function(e) {\n        return this.element.classList.add(\"dz-drag-hover\");\n      },\n      dragover: function(e) {\n        return this.element.classList.add(\"dz-drag-hover\");\n      },\n      dragleave: function(e) {\n        return this.element.classList.remove(\"dz-drag-hover\");\n      },\n      paste: noop,\n      reset: function() {\n        return this.element.classList.remove(\"dz-started\");\n      },\n      addedfile: function(file) {\n        var j, k, l, len, len1, len2, node, ref, ref1, ref2, removeFileEvent, removeLink, results;\n        if (this.element === this.previewsContainer) {\n          this.element.classList.add(\"dz-started\");\n        }\n        if (this.previewsContainer) {\n          file.previewElement = Dropzone.createElement(this.options.previewTemplate.trim());\n          file.previewTemplate = file.previewElement;\n          this.previewsContainer.appendChild(file.previewElement);\n          ref = file.previewElement.querySelectorAll(\"[data-dz-name]\");\n          for (j = 0, len = ref.length; j < len; j++) {\n            node = ref[j];\n            node.textContent = file.name;\n          }\n          ref1 = file.previewElement.querySelectorAll(\"[data-dz-size]\");\n          for (k = 0, len1 = ref1.length; k < len1; k++) {\n            node = ref1[k];\n            node.innerHTML = this.filesize(file.size);\n          }\n          if (this.options.addRemoveLinks) {\n            file._removeLink = Dropzone.createElement(\"<a class=\\\"dz-remove\\\" href=\\\"javascript:undefined;\\\" data-dz-remove>\" + this.options.dictRemoveFile + \"</a>\");\n            file.previewElement.appendChild(file._removeLink);\n          }\n          removeFileEvent = (function(_this) {\n            return function(e) {\n              e.preventDefault();\n              e.stopPropagation();\n              if (file.status === Dropzone.UPLOADING) {\n                return Dropzone.confirm(_this.options.dictCancelUploadConfirmation, function() {\n                  return _this.removeFile(file);\n                });\n              } else {\n                if (_this.options.dictRemoveFileConfirmation) {\n                  return Dropzone.confirm(_this.options.dictRemoveFileConfirmation, function() {\n                    return _this.removeFile(file);\n                  });\n                } else {\n                  return _this.removeFile(file);\n                }\n              }\n            };\n          })(this);\n          ref2 = file.previewElement.querySelectorAll(\"[data-dz-remove]\");\n          results = [];\n          for (l = 0, len2 = ref2.length; l < len2; l++) {\n            removeLink = ref2[l];\n            results.push(removeLink.addEventListener(\"click\", removeFileEvent));\n          }\n          return results;\n        }\n      },\n      removedfile: function(file) {\n        var ref;\n        if (file.previewElement) {\n          if ((ref = file.previewElement) != null) {\n            ref.parentNode.removeChild(file.previewElement);\n          }\n        }\n        return this._updateMaxFilesReachedClass();\n      },\n      thumbnail: function(file, dataUrl) {\n        var j, len, ref, thumbnailElement;\n        if (file.previewElement) {\n          file.previewElement.classList.remove(\"dz-file-preview\");\n          ref = file.previewElement.querySelectorAll(\"[data-dz-thumbnail]\");\n          for (j = 0, len = ref.length; j < len; j++) {\n            thumbnailElement = ref[j];\n            thumbnailElement.alt = file.name;\n            thumbnailElement.src = dataUrl;\n          }\n          return setTimeout(((function(_this) {\n            return function() {\n              return file.previewElement.classList.add(\"dz-image-preview\");\n            };\n          })(this)), 1);\n        }\n      },\n      error: function(file, message) {\n        var j, len, node, ref, results;\n        if (file.previewElement) {\n          file.previewElement.classList.add(\"dz-error\");\n          if (typeof message !== \"String\" && message.error) {\n            message = message.error;\n          }\n          ref = file.previewElement.querySelectorAll(\"[data-dz-errormessage]\");\n          results = [];\n          for (j = 0, len = ref.length; j < len; j++) {\n            node = ref[j];\n            results.push(node.textContent = message);\n          }\n          return results;\n        }\n      },\n      errormultiple: noop,\n      processing: function(file) {\n        if (file.previewElement) {\n          file.previewElement.classList.add(\"dz-processing\");\n          if (file._removeLink) {\n            return file._removeLink.textContent = this.options.dictCancelUpload;\n          }\n        }\n      },\n      processingmultiple: noop,\n      uploadprogress: function(file, progress, bytesSent) {\n        var j, len, node, ref, results;\n        if (file.previewElement) {\n          ref = file.previewElement.querySelectorAll(\"[data-dz-uploadprogress]\");\n          results = [];\n          for (j = 0, len = ref.length; j < len; j++) {\n            node = ref[j];\n            if (node.nodeName === 'PROGRESS') {\n              results.push(node.value = progress);\n            } else {\n              results.push(node.style.width = progress + \"%\");\n            }\n          }\n          return results;\n        }\n      },\n      totaluploadprogress: noop,\n      sending: noop,\n      sendingmultiple: noop,\n      success: function(file) {\n        if (file.previewElement) {\n          return file.previewElement.classList.add(\"dz-success\");\n        }\n      },\n      successmultiple: noop,\n      canceled: function(file) {\n        return this.emit(\"error\", file, \"Upload canceled.\");\n      },\n      canceledmultiple: noop,\n      complete: function(file) {\n        if (file._removeLink) {\n          file._removeLink.textContent = this.options.dictRemoveFile;\n        }\n        if (file.previewElement) {\n          return file.previewElement.classList.add(\"dz-complete\");\n        }\n      },\n      completemultiple: noop,\n      maxfilesexceeded: noop,\n      maxfilesreached: noop,\n      queuecomplete: noop,\n      addedfiles: noop\n    };\n\n    extend = function() {\n      var j, key, len, object, objects, target, val;\n      target = arguments[0], objects = 2 <= arguments.length ? slice.call(arguments, 1) : [];\n      for (j = 0, len = objects.length; j < len; j++) {\n        object = objects[j];\n        for (key in object) {\n          val = object[key];\n          target[key] = val;\n        }\n      }\n      return target;\n    };\n\n    function Dropzone(element1, options) {\n      var elementOptions, fallback, ref;\n      this.element = element1;\n      this.version = Dropzone.version;\n      this.defaultOptions.previewTemplate = this.defaultOptions.previewTemplate.replace(/\\n*/g, \"\");\n      this.clickableElements = [];\n      this.listeners = [];\n      this.files = [];\n      if (typeof this.element === \"string\") {\n        this.element = document.querySelector(this.element);\n      }\n      if (!(this.element && (this.element.nodeType != null))) {\n        throw new Error(\"Invalid dropzone element.\");\n      }\n      if (this.element.dropzone) {\n        throw new Error(\"Dropzone already attached.\");\n      }\n      Dropzone.instances.push(this);\n      this.element.dropzone = this;\n      elementOptions = (ref = Dropzone.optionsForElement(this.element)) != null ? ref : {};\n      this.options = extend({}, this.defaultOptions, elementOptions, options != null ? options : {});\n      if (this.options.forceFallback || !Dropzone.isBrowserSupported()) {\n        return this.options.fallback.call(this);\n      }\n      if (this.options.url == null) {\n        this.options.url = this.element.getAttribute(\"action\");\n      }\n      if (!this.options.url) {\n        throw new Error(\"No URL provided.\");\n      }\n      if (this.options.acceptedFiles && this.options.acceptedMimeTypes) {\n        throw new Error(\"You can't provide both 'acceptedFiles' and 'acceptedMimeTypes'. 'acceptedMimeTypes' is deprecated.\");\n      }\n      if (this.options.acceptedMimeTypes) {\n        this.options.acceptedFiles = this.options.acceptedMimeTypes;\n        delete this.options.acceptedMimeTypes;\n      }\n      if (this.options.renameFilename != null) {\n        this.options.renameFile = (function(_this) {\n          return function(file) {\n            return _this.options.renameFilename.call(_this, file.name, file);\n          };\n        })(this);\n      }\n      this.options.method = this.options.method.toUpperCase();\n      if ((fallback = this.getExistingFallback()) && fallback.parentNode) {\n        fallback.parentNode.removeChild(fallback);\n      }\n      if (this.options.previewsContainer !== false) {\n        if (this.options.previewsContainer) {\n          this.previewsContainer = Dropzone.getElement(this.options.previewsContainer, \"previewsContainer\");\n        } else {\n          this.previewsContainer = this.element;\n        }\n      }\n      if (this.options.clickable) {\n        if (this.options.clickable === true) {\n          this.clickableElements = [this.element];\n        } else {\n          this.clickableElements = Dropzone.getElements(this.options.clickable, \"clickable\");\n        }\n      }\n      this.init();\n    }\n\n    Dropzone.prototype.getAcceptedFiles = function() {\n      var file, j, len, ref, results;\n      ref = this.files;\n      results = [];\n      for (j = 0, len = ref.length; j < len; j++) {\n        file = ref[j];\n        if (file.accepted) {\n          results.push(file);\n        }\n      }\n      return results;\n    };\n\n    Dropzone.prototype.getRejectedFiles = function() {\n      var file, j, len, ref, results;\n      ref = this.files;\n      results = [];\n      for (j = 0, len = ref.length; j < len; j++) {\n        file = ref[j];\n        if (!file.accepted) {\n          results.push(file);\n        }\n      }\n      return results;\n    };\n\n    Dropzone.prototype.getFilesWithStatus = function(status) {\n      var file, j, len, ref, results;\n      ref = this.files;\n      results = [];\n      for (j = 0, len = ref.length; j < len; j++) {\n        file = ref[j];\n        if (file.status === status) {\n          results.push(file);\n        }\n      }\n      return results;\n    };\n\n    Dropzone.prototype.getQueuedFiles = function() {\n      return this.getFilesWithStatus(Dropzone.QUEUED);\n    };\n\n    Dropzone.prototype.getUploadingFiles = function() {\n      return this.getFilesWithStatus(Dropzone.UPLOADING);\n    };\n\n    Dropzone.prototype.getAddedFiles = function() {\n      return this.getFilesWithStatus(Dropzone.ADDED);\n    };\n\n    Dropzone.prototype.getActiveFiles = function() {\n      var file, j, len, ref, results;\n      ref = this.files;\n      results = [];\n      for (j = 0, len = ref.length; j < len; j++) {\n        file = ref[j];\n        if (file.status === Dropzone.UPLOADING || file.status === Dropzone.QUEUED) {\n          results.push(file);\n        }\n      }\n      return results;\n    };\n\n    Dropzone.prototype.init = function() {\n      var eventName, j, len, noPropagation, ref, ref1, setupHiddenFileInput;\n      if (this.element.tagName === \"form\") {\n        this.element.setAttribute(\"enctype\", \"multipart/form-data\");\n      }\n      if (this.element.classList.contains(\"dropzone\") && !this.element.querySelector(\".dz-message\")) {\n        this.element.appendChild(Dropzone.createElement(\"<div class=\\\"dz-default dz-message\\\"><span>\" + this.options.dictDefaultMessage + \"</span></div>\"));\n      }\n      if (this.clickableElements.length) {\n        setupHiddenFileInput = (function(_this) {\n          return function() {\n            if (_this.hiddenFileInput) {\n              _this.hiddenFileInput.parentNode.removeChild(_this.hiddenFileInput);\n            }\n            _this.hiddenFileInput = document.createElement(\"input\");\n            _this.hiddenFileInput.setAttribute(\"type\", \"file\");\n            if ((_this.options.maxFiles == null) || _this.options.maxFiles > 1) {\n              _this.hiddenFileInput.setAttribute(\"multiple\", \"multiple\");\n            }\n            _this.hiddenFileInput.className = \"dz-hidden-input\";\n            if (_this.options.acceptedFiles != null) {\n              _this.hiddenFileInput.setAttribute(\"accept\", _this.options.acceptedFiles);\n            }\n            if (_this.options.capture != null) {\n              _this.hiddenFileInput.setAttribute(\"capture\", _this.options.capture);\n            }\n            _this.hiddenFileInput.style.visibility = \"hidden\";\n            _this.hiddenFileInput.style.position = \"absolute\";\n            _this.hiddenFileInput.style.top = \"0\";\n            _this.hiddenFileInput.style.left = \"0\";\n            _this.hiddenFileInput.style.height = \"0\";\n            _this.hiddenFileInput.style.width = \"0\";\n            document.querySelector(_this.options.hiddenInputContainer).appendChild(_this.hiddenFileInput);\n            return _this.hiddenFileInput.addEventListener(\"change\", function() {\n              var file, files, j, len;\n              files = _this.hiddenFileInput.files;\n              if (files.length) {\n                for (j = 0, len = files.length; j < len; j++) {\n                  file = files[j];\n                  _this.addFile(file);\n                }\n              }\n              _this.emit(\"addedfiles\", files);\n              return setupHiddenFileInput();\n            });\n          };\n        })(this);\n        setupHiddenFileInput();\n      }\n      this.URL = (ref = window.URL) != null ? ref : window.webkitURL;\n      ref1 = this.events;\n      for (j = 0, len = ref1.length; j < len; j++) {\n        eventName = ref1[j];\n        this.on(eventName, this.options[eventName]);\n      }\n      this.on(\"uploadprogress\", (function(_this) {\n        return function() {\n          return _this.updateTotalUploadProgress();\n        };\n      })(this));\n      this.on(\"removedfile\", (function(_this) {\n        return function() {\n          return _this.updateTotalUploadProgress();\n        };\n      })(this));\n      this.on(\"canceled\", (function(_this) {\n        return function(file) {\n          return _this.emit(\"complete\", file);\n        };\n      })(this));\n      this.on(\"complete\", (function(_this) {\n        return function(file) {\n          if (_this.getAddedFiles().length === 0 && _this.getUploadingFiles().length === 0 && _this.getQueuedFiles().length === 0) {\n            return setTimeout((function() {\n              return _this.emit(\"queuecomplete\");\n            }), 0);\n          }\n        };\n      })(this));\n      noPropagation = function(e) {\n        e.stopPropagation();\n        if (e.preventDefault) {\n          return e.preventDefault();\n        } else {\n          return e.returnValue = false;\n        }\n      };\n      this.listeners = [\n        {\n          element: this.element,\n          events: {\n            \"dragstart\": (function(_this) {\n              return function(e) {\n                return _this.emit(\"dragstart\", e);\n              };\n            })(this),\n            \"dragenter\": (function(_this) {\n              return function(e) {\n                noPropagation(e);\n                return _this.emit(\"dragenter\", e);\n              };\n            })(this),\n            \"dragover\": (function(_this) {\n              return function(e) {\n                var efct;\n                try {\n                  efct = e.dataTransfer.effectAllowed;\n                } catch (undefined) {}\n                e.dataTransfer.dropEffect = 'move' === efct || 'linkMove' === efct ? 'move' : 'copy';\n                noPropagation(e);\n                return _this.emit(\"dragover\", e);\n              };\n            })(this),\n            \"dragleave\": (function(_this) {\n              return function(e) {\n                return _this.emit(\"dragleave\", e);\n              };\n            })(this),\n            \"drop\": (function(_this) {\n              return function(e) {\n                noPropagation(e);\n                return _this.drop(e);\n              };\n            })(this),\n            \"dragend\": (function(_this) {\n              return function(e) {\n                return _this.emit(\"dragend\", e);\n              };\n            })(this)\n          }\n        }\n      ];\n      this.clickableElements.forEach((function(_this) {\n        return function(clickableElement) {\n          return _this.listeners.push({\n            element: clickableElement,\n            events: {\n              \"click\": function(evt) {\n                if ((clickableElement !== _this.element) || (evt.target === _this.element || Dropzone.elementInside(evt.target, _this.element.querySelector(\".dz-message\")))) {\n                  _this.hiddenFileInput.click();\n                }\n                return true;\n              }\n            }\n          });\n        };\n      })(this));\n      this.enable();\n      return this.options.init.call(this);\n    };\n\n    Dropzone.prototype.destroy = function() {\n      var ref;\n      this.disable();\n      this.removeAllFiles(true);\n      if ((ref = this.hiddenFileInput) != null ? ref.parentNode : void 0) {\n        this.hiddenFileInput.parentNode.removeChild(this.hiddenFileInput);\n        this.hiddenFileInput = null;\n      }\n      delete this.element.dropzone;\n      return Dropzone.instances.splice(Dropzone.instances.indexOf(this), 1);\n    };\n\n    Dropzone.prototype.updateTotalUploadProgress = function() {\n      var activeFiles, file, j, len, ref, totalBytes, totalBytesSent, totalUploadProgress;\n      totalBytesSent = 0;\n      totalBytes = 0;\n      activeFiles = this.getActiveFiles();\n      if (activeFiles.length) {\n        ref = this.getActiveFiles();\n        for (j = 0, len = ref.length; j < len; j++) {\n          file = ref[j];\n          totalBytesSent += file.upload.bytesSent;\n          totalBytes += file.upload.total;\n        }\n        totalUploadProgress = 100 * totalBytesSent / totalBytes;\n      } else {\n        totalUploadProgress = 100;\n      }\n      return this.emit(\"totaluploadprogress\", totalUploadProgress, totalBytes, totalBytesSent);\n    };\n\n    Dropzone.prototype._getParamName = function(n) {\n      if (typeof this.options.paramName === \"function\") {\n        return this.options.paramName(n);\n      } else {\n        return \"\" + this.options.paramName + (this.options.uploadMultiple ? \"[\" + n + \"]\" : \"\");\n      }\n    };\n\n    Dropzone.prototype._renameFile = function(file) {\n      if (typeof this.options.renameFile !== \"function\") {\n        return file.name;\n      }\n      return this.options.renameFile(file);\n    };\n\n    Dropzone.prototype.getFallbackForm = function() {\n      var existingFallback, fields, fieldsString, form;\n      if (existingFallback = this.getExistingFallback()) {\n        return existingFallback;\n      }\n      fieldsString = \"<div class=\\\"dz-fallback\\\">\";\n      if (this.options.dictFallbackText) {\n        fieldsString += \"<p>\" + this.options.dictFallbackText + \"</p>\";\n      }\n      fieldsString += \"<input type=\\\"file\\\" name=\\\"\" + (this._getParamName(0)) + \"\\\" \" + (this.options.uploadMultiple ? 'multiple=\"multiple\"' : void 0) + \" /><input type=\\\"submit\\\" value=\\\"Upload!\\\"></div>\";\n      fields = Dropzone.createElement(fieldsString);\n      if (this.element.tagName !== \"FORM\") {\n        form = Dropzone.createElement(\"<form action=\\\"\" + this.options.url + \"\\\" enctype=\\\"multipart/form-data\\\" method=\\\"\" + this.options.method + \"\\\"></form>\");\n        form.appendChild(fields);\n      } else {\n        this.element.setAttribute(\"enctype\", \"multipart/form-data\");\n        this.element.setAttribute(\"method\", this.options.method);\n      }\n      return form != null ? form : fields;\n    };\n\n    Dropzone.prototype.getExistingFallback = function() {\n      var fallback, getFallback, j, len, ref, tagName;\n      getFallback = function(elements) {\n        var el, j, len;\n        for (j = 0, len = elements.length; j < len; j++) {\n          el = elements[j];\n          if (/(^| )fallback($| )/.test(el.className)) {\n            return el;\n          }\n        }\n      };\n      ref = [\"div\", \"form\"];\n      for (j = 0, len = ref.length; j < len; j++) {\n        tagName = ref[j];\n        if (fallback = getFallback(this.element.getElementsByTagName(tagName))) {\n          return fallback;\n        }\n      }\n    };\n\n    Dropzone.prototype.setupEventListeners = function() {\n      var elementListeners, event, j, len, listener, ref, results;\n      ref = this.listeners;\n      results = [];\n      for (j = 0, len = ref.length; j < len; j++) {\n        elementListeners = ref[j];\n        results.push((function() {\n          var ref1, results1;\n          ref1 = elementListeners.events;\n          results1 = [];\n          for (event in ref1) {\n            listener = ref1[event];\n            results1.push(elementListeners.element.addEventListener(event, listener, false));\n          }\n          return results1;\n        })());\n      }\n      return results;\n    };\n\n    Dropzone.prototype.removeEventListeners = function() {\n      var elementListeners, event, j, len, listener, ref, results;\n      ref = this.listeners;\n      results = [];\n      for (j = 0, len = ref.length; j < len; j++) {\n        elementListeners = ref[j];\n        results.push((function() {\n          var ref1, results1;\n          ref1 = elementListeners.events;\n          results1 = [];\n          for (event in ref1) {\n            listener = ref1[event];\n            results1.push(elementListeners.element.removeEventListener(event, listener, false));\n          }\n          return results1;\n        })());\n      }\n      return results;\n    };\n\n    Dropzone.prototype.disable = function() {\n      var file, j, len, ref, results;\n      this.clickableElements.forEach(function(element) {\n        return element.classList.remove(\"dz-clickable\");\n      });\n      this.removeEventListeners();\n      ref = this.files;\n      results = [];\n      for (j = 0, len = ref.length; j < len; j++) {\n        file = ref[j];\n        results.push(this.cancelUpload(file));\n      }\n      return results;\n    };\n\n    Dropzone.prototype.enable = function() {\n      this.clickableElements.forEach(function(element) {\n        return element.classList.add(\"dz-clickable\");\n      });\n      return this.setupEventListeners();\n    };\n\n    Dropzone.prototype.filesize = function(size) {\n      var cutoff, i, j, len, selectedSize, selectedUnit, unit, units;\n      selectedSize = 0;\n      selectedUnit = \"b\";\n      if (size > 0) {\n        units = ['tb', 'gb', 'mb', 'kb', 'b'];\n        for (i = j = 0, len = units.length; j < len; i = ++j) {\n          unit = units[i];\n          cutoff = Math.pow(this.options.filesizeBase, 4 - i) / 10;\n          if (size >= cutoff) {\n            selectedSize = size / Math.pow(this.options.filesizeBase, 4 - i);\n            selectedUnit = unit;\n            break;\n          }\n        }\n        selectedSize = Math.round(10 * selectedSize) / 10;\n      }\n      return \"<strong>\" + selectedSize + \"</strong> \" + this.options.dictFileSizeUnits[selectedUnit];\n    };\n\n    Dropzone.prototype._updateMaxFilesReachedClass = function() {\n      if ((this.options.maxFiles != null) && this.getAcceptedFiles().length >= this.options.maxFiles) {\n        if (this.getAcceptedFiles().length === this.options.maxFiles) {\n          this.emit('maxfilesreached', this.files);\n        }\n        return this.element.classList.add(\"dz-max-files-reached\");\n      } else {\n        return this.element.classList.remove(\"dz-max-files-reached\");\n      }\n    };\n\n    Dropzone.prototype.drop = function(e) {\n      var files, items;\n      if (!e.dataTransfer) {\n        return;\n      }\n      this.emit(\"drop\", e);\n      files = e.dataTransfer.files;\n      this.emit(\"addedfiles\", files);\n      if (files.length) {\n        items = e.dataTransfer.items;\n        if (items && items.length && (items[0].webkitGetAsEntry != null)) {\n          this._addFilesFromItems(items);\n        } else {\n          this.handleFiles(files);\n        }\n      }\n    };\n\n    Dropzone.prototype.paste = function(e) {\n      var items, ref;\n      if ((e != null ? (ref = e.clipboardData) != null ? ref.items : void 0 : void 0) == null) {\n        return;\n      }\n      this.emit(\"paste\", e);\n      items = e.clipboardData.items;\n      if (items.length) {\n        return this._addFilesFromItems(items);\n      }\n    };\n\n    Dropzone.prototype.handleFiles = function(files) {\n      var file, j, len, results;\n      results = [];\n      for (j = 0, len = files.length; j < len; j++) {\n        file = files[j];\n        results.push(this.addFile(file));\n      }\n      return results;\n    };\n\n    Dropzone.prototype._addFilesFromItems = function(items) {\n      var entry, item, j, len, results;\n      results = [];\n      for (j = 0, len = items.length; j < len; j++) {\n        item = items[j];\n        if ((item.webkitGetAsEntry != null) && (entry = item.webkitGetAsEntry())) {\n          if (entry.isFile) {\n            results.push(this.addFile(item.getAsFile()));\n          } else if (entry.isDirectory) {\n            results.push(this._addFilesFromDirectory(entry, entry.name));\n          } else {\n            results.push(void 0);\n          }\n        } else if (item.getAsFile != null) {\n          if ((item.kind == null) || item.kind === \"file\") {\n            results.push(this.addFile(item.getAsFile()));\n          } else {\n            results.push(void 0);\n          }\n        } else {\n          results.push(void 0);\n        }\n      }\n      return results;\n    };\n\n    Dropzone.prototype._addFilesFromDirectory = function(directory, path) {\n      var dirReader, errorHandler, readEntries;\n      dirReader = directory.createReader();\n      errorHandler = function(error) {\n        return typeof console !== \"undefined\" && console !== null ? typeof console.log === \"function\" ? console.log(error) : void 0 : void 0;\n      };\n      readEntries = (function(_this) {\n        return function() {\n          return dirReader.readEntries(function(entries) {\n            var entry, j, len;\n            if (entries.length > 0) {\n              for (j = 0, len = entries.length; j < len; j++) {\n                entry = entries[j];\n                if (entry.isFile) {\n                  entry.file(function(file) {\n                    if (_this.options.ignoreHiddenFiles && file.name.substring(0, 1) === '.') {\n                      return;\n                    }\n                    file.fullPath = path + \"/\" + file.name;\n                    return _this.addFile(file);\n                  });\n                } else if (entry.isDirectory) {\n                  _this._addFilesFromDirectory(entry, path + \"/\" + entry.name);\n                }\n              }\n              readEntries();\n            }\n            return null;\n          }, errorHandler);\n        };\n      })(this);\n      return readEntries();\n    };\n\n    Dropzone.prototype.accept = function(file, done) {\n      if (file.size > this.options.maxFilesize * 1024 * 1024) {\n        return done(this.options.dictFileTooBig.replace(\"{{filesize}}\", Math.round(file.size / 1024 / 10.24) / 100).replace(\"{{maxFilesize}}\", this.options.maxFilesize));\n      } else if (!Dropzone.isValidFile(file, this.options.acceptedFiles)) {\n        return done(this.options.dictInvalidFileType);\n      } else if ((this.options.maxFiles != null) && this.getAcceptedFiles().length >= this.options.maxFiles) {\n        done(this.options.dictMaxFilesExceeded.replace(\"{{maxFiles}}\", this.options.maxFiles));\n        return this.emit(\"maxfilesexceeded\", file);\n      } else {\n        return this.options.accept.call(this, file, done);\n      }\n    };\n\n    Dropzone.prototype.addFile = function(file) {\n      file.upload = {\n        progress: 0,\n        total: file.size,\n        bytesSent: 0,\n        filename: this._renameFile(file)\n      };\n      this.files.push(file);\n      file.status = Dropzone.ADDED;\n      this.emit(\"addedfile\", file);\n      this._enqueueThumbnail(file);\n      return this.accept(file, (function(_this) {\n        return function(error) {\n          if (error) {\n            file.accepted = false;\n            _this._errorProcessing([file], error);\n          } else {\n            file.accepted = true;\n            if (_this.options.autoQueue) {\n              _this.enqueueFile(file);\n            }\n          }\n          return _this._updateMaxFilesReachedClass();\n        };\n      })(this));\n    };\n\n    Dropzone.prototype.enqueueFiles = function(files) {\n      var file, j, len;\n      for (j = 0, len = files.length; j < len; j++) {\n        file = files[j];\n        this.enqueueFile(file);\n      }\n      return null;\n    };\n\n    Dropzone.prototype.enqueueFile = function(file) {\n      if (file.status === Dropzone.ADDED && file.accepted === true) {\n        file.status = Dropzone.QUEUED;\n        if (this.options.autoProcessQueue) {\n          return setTimeout(((function(_this) {\n            return function() {\n              return _this.processQueue();\n            };\n          })(this)), 0);\n        }\n      } else {\n        throw new Error(\"This file can't be queued because it has already been processed or was rejected.\");\n      }\n    };\n\n    Dropzone.prototype._thumbnailQueue = [];\n\n    Dropzone.prototype._processingThumbnail = false;\n\n    Dropzone.prototype._enqueueThumbnail = function(file) {\n      if (this.options.createImageThumbnails && file.type.match(/image.*/) && file.size <= this.options.maxThumbnailFilesize * 1024 * 1024) {\n        this._thumbnailQueue.push(file);\n        return setTimeout(((function(_this) {\n          return function() {\n            return _this._processThumbnailQueue();\n          };\n        })(this)), 0);\n      }\n    };\n\n    Dropzone.prototype._processThumbnailQueue = function() {\n      var file;\n      if (this._processingThumbnail || this._thumbnailQueue.length === 0) {\n        return;\n      }\n      this._processingThumbnail = true;\n      file = this._thumbnailQueue.shift();\n      return this.createThumbnail(file, this.options.thumbnailWidth, this.options.thumbnailHeight, this.options.thumbnailMethod, true, (function(_this) {\n        return function(dataUrl) {\n          _this.emit(\"thumbnail\", file, dataUrl);\n          _this._processingThumbnail = false;\n          return _this._processThumbnailQueue();\n        };\n      })(this));\n    };\n\n    Dropzone.prototype.removeFile = function(file) {\n      if (file.status === Dropzone.UPLOADING) {\n        this.cancelUpload(file);\n      }\n      this.files = without(this.files, file);\n      this.emit(\"removedfile\", file);\n      if (this.files.length === 0) {\n        return this.emit(\"reset\");\n      }\n    };\n\n    Dropzone.prototype.removeAllFiles = function(cancelIfNecessary) {\n      var file, j, len, ref;\n      if (cancelIfNecessary == null) {\n        cancelIfNecessary = false;\n      }\n      ref = this.files.slice();\n      for (j = 0, len = ref.length; j < len; j++) {\n        file = ref[j];\n        if (file.status !== Dropzone.UPLOADING || cancelIfNecessary) {\n          this.removeFile(file);\n        }\n      }\n      return null;\n    };\n\n    Dropzone.prototype.resizeImage = function(file, width, height, resizeMethod, callback) {\n      return this.createThumbnail(file, width, height, resizeMethod, false, (function(_this) {\n        return function(dataUrl, canvas) {\n          var resizeMimeType, resizedDataURL;\n          if (canvas === null) {\n            return callback(file);\n          } else {\n            resizeMimeType = _this.options.resizeMimeType;\n            if (resizeMimeType == null) {\n              resizeMimeType = file.type;\n            }\n            resizedDataURL = canvas.toDataURL(resizeMimeType, _this.options.resizeQuality);\n            if (resizeMimeType === 'image/jpeg' || resizeMimeType === 'image/jpg') {\n              resizedDataURL = ExifRestore.restore(file.dataURL, resizedDataURL);\n            }\n            return callback(Dropzone.dataURItoBlob(resizedDataURL));\n          }\n        };\n      })(this));\n    };\n\n    Dropzone.prototype.createThumbnail = function(file, width, height, resizeMethod, fixOrientation, callback) {\n      var fileReader;\n      fileReader = new FileReader;\n      fileReader.onload = (function(_this) {\n        return function() {\n          file.dataURL = fileReader.result;\n          if (file.type === \"image/svg+xml\") {\n            if (callback != null) {\n              callback(fileReader.result);\n            }\n            return;\n          }\n          return _this.createThumbnailFromUrl(file, width, height, resizeMethod, fixOrientation, callback);\n        };\n      })(this);\n      return fileReader.readAsDataURL(file);\n    };\n\n    Dropzone.prototype.createThumbnailFromUrl = function(file, width, height, resizeMethod, fixOrientation, callback, crossOrigin) {\n      var img;\n      img = document.createElement(\"img\");\n      if (crossOrigin) {\n        img.crossOrigin = crossOrigin;\n      }\n      img.onload = (function(_this) {\n        return function() {\n          var loadExif;\n          loadExif = function(callback) {\n            return callback(1);\n          };\n          if ((typeof EXIF !== \"undefined\" && EXIF !== null) && fixOrientation) {\n            loadExif = function(callback) {\n              return EXIF.getData(img, function() {\n                return callback(EXIF.getTag(this, 'Orientation'));\n              });\n            };\n          }\n          return loadExif(function(orientation) {\n            var canvas, ctx, ref, ref1, ref2, ref3, resizeInfo, thumbnail;\n            file.width = img.width;\n            file.height = img.height;\n            resizeInfo = _this.options.resize.call(_this, file, width, height, resizeMethod);\n            canvas = document.createElement(\"canvas\");\n            ctx = canvas.getContext(\"2d\");\n            canvas.width = resizeInfo.trgWidth;\n            canvas.height = resizeInfo.trgHeight;\n            if (orientation > 4) {\n              canvas.width = resizeInfo.trgHeight;\n              canvas.height = resizeInfo.trgWidth;\n            }\n            switch (orientation) {\n              case 2:\n                ctx.translate(canvas.width, 0);\n                ctx.scale(-1, 1);\n                break;\n              case 3:\n                ctx.translate(canvas.width, canvas.height);\n                ctx.rotate(Math.PI);\n                break;\n              case 4:\n                ctx.translate(0, canvas.height);\n                ctx.scale(1, -1);\n                break;\n              case 5:\n                ctx.rotate(0.5 * Math.PI);\n                ctx.scale(1, -1);\n                break;\n              case 6:\n                ctx.rotate(0.5 * Math.PI);\n                ctx.translate(0, -canvas.height);\n                break;\n              case 7:\n                ctx.rotate(0.5 * Math.PI);\n                ctx.translate(canvas.width, -canvas.height);\n                ctx.scale(-1, 1);\n                break;\n              case 8:\n                ctx.rotate(-0.5 * Math.PI);\n                ctx.translate(-canvas.width, 0);\n            }\n            drawImageIOSFix(ctx, img, (ref = resizeInfo.srcX) != null ? ref : 0, (ref1 = resizeInfo.srcY) != null ? ref1 : 0, resizeInfo.srcWidth, resizeInfo.srcHeight, (ref2 = resizeInfo.trgX) != null ? ref2 : 0, (ref3 = resizeInfo.trgY) != null ? ref3 : 0, resizeInfo.trgWidth, resizeInfo.trgHeight);\n            thumbnail = canvas.toDataURL(\"image/png\");\n            if (callback != null) {\n              return callback(thumbnail, canvas);\n            }\n          });\n        };\n      })(this);\n      if (callback != null) {\n        img.onerror = callback;\n      }\n      return img.src = file.dataURL;\n    };\n\n    Dropzone.prototype.processQueue = function() {\n      var i, parallelUploads, processingLength, queuedFiles;\n      parallelUploads = this.options.parallelUploads;\n      processingLength = this.getUploadingFiles().length;\n      i = processingLength;\n      if (processingLength >= parallelUploads) {\n        return;\n      }\n      queuedFiles = this.getQueuedFiles();\n      if (!(queuedFiles.length > 0)) {\n        return;\n      }\n      if (this.options.uploadMultiple) {\n        return this.processFiles(queuedFiles.slice(0, parallelUploads - processingLength));\n      } else {\n        while (i < parallelUploads) {\n          if (!queuedFiles.length) {\n            return;\n          }\n          this.processFile(queuedFiles.shift());\n          i++;\n        }\n      }\n    };\n\n    Dropzone.prototype.processFile = function(file) {\n      return this.processFiles([file]);\n    };\n\n    Dropzone.prototype.processFiles = function(files) {\n      var file, j, len;\n      for (j = 0, len = files.length; j < len; j++) {\n        file = files[j];\n        file.processing = true;\n        file.status = Dropzone.UPLOADING;\n        this.emit(\"processing\", file);\n      }\n      if (this.options.uploadMultiple) {\n        this.emit(\"processingmultiple\", files);\n      }\n      return this.uploadFiles(files);\n    };\n\n    Dropzone.prototype._getFilesWithXhr = function(xhr) {\n      var file, files;\n      return files = (function() {\n        var j, len, ref, results;\n        ref = this.files;\n        results = [];\n        for (j = 0, len = ref.length; j < len; j++) {\n          file = ref[j];\n          if (file.xhr === xhr) {\n            results.push(file);\n          }\n        }\n        return results;\n      }).call(this);\n    };\n\n    Dropzone.prototype.cancelUpload = function(file) {\n      var groupedFile, groupedFiles, j, k, len, len1, ref;\n      if (file.status === Dropzone.UPLOADING) {\n        groupedFiles = this._getFilesWithXhr(file.xhr);\n        for (j = 0, len = groupedFiles.length; j < len; j++) {\n          groupedFile = groupedFiles[j];\n          groupedFile.status = Dropzone.CANCELED;\n        }\n        file.xhr.abort();\n        for (k = 0, len1 = groupedFiles.length; k < len1; k++) {\n          groupedFile = groupedFiles[k];\n          this.emit(\"canceled\", groupedFile);\n        }\n        if (this.options.uploadMultiple) {\n          this.emit(\"canceledmultiple\", groupedFiles);\n        }\n      } else if ((ref = file.status) === Dropzone.ADDED || ref === Dropzone.QUEUED) {\n        file.status = Dropzone.CANCELED;\n        this.emit(\"canceled\", file);\n        if (this.options.uploadMultiple) {\n          this.emit(\"canceledmultiple\", [file]);\n        }\n      }\n      if (this.options.autoProcessQueue) {\n        return this.processQueue();\n      }\n    };\n\n    resolveOption = function() {\n      var args, option;\n      option = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];\n      if (typeof option === 'function') {\n        return option.apply(this, args);\n      }\n      return option;\n    };\n\n    Dropzone.prototype.uploadFile = function(file) {\n      return this.uploadFiles([file]);\n    };\n\n    Dropzone.prototype.uploadFiles = function(files) {\n      var doneCounter, doneFunction, file, formData, handleError, headerName, headerValue, headers, i, input, inputName, inputType, j, k, key, l, len, len1, len2, len3, m, method, o, option, progressObj, ref, ref1, ref2, ref3, ref4, ref5, response, results, updateProgress, url, value, xhr;\n      xhr = new XMLHttpRequest();\n      for (j = 0, len = files.length; j < len; j++) {\n        file = files[j];\n        file.xhr = xhr;\n      }\n      method = resolveOption(this.options.method, files);\n      url = resolveOption(this.options.url, files);\n      xhr.open(method, url, true);\n      xhr.timeout = resolveOption(this.options.timeout, files);\n      xhr.withCredentials = !!this.options.withCredentials;\n      response = null;\n      handleError = (function(_this) {\n        return function() {\n          var k, len1, results;\n          results = [];\n          for (k = 0, len1 = files.length; k < len1; k++) {\n            file = files[k];\n            results.push(_this._errorProcessing(files, response || _this.options.dictResponseError.replace(\"{{statusCode}}\", xhr.status), xhr));\n          }\n          return results;\n        };\n      })(this);\n      updateProgress = (function(_this) {\n        return function(e) {\n          var allFilesFinished, k, l, len1, len2, len3, m, progress, results;\n          if (e != null) {\n            progress = 100 * e.loaded / e.total;\n            for (k = 0, len1 = files.length; k < len1; k++) {\n              file = files[k];\n              file.upload.progress = progress;\n              file.upload.total = e.total;\n              file.upload.bytesSent = e.loaded;\n            }\n          } else {\n            allFilesFinished = true;\n            progress = 100;\n            for (l = 0, len2 = files.length; l < len2; l++) {\n              file = files[l];\n              if (!(file.upload.progress === 100 && file.upload.bytesSent === file.upload.total)) {\n                allFilesFinished = false;\n              }\n              file.upload.progress = progress;\n              file.upload.bytesSent = file.upload.total;\n            }\n            if (allFilesFinished) {\n              return;\n            }\n          }\n          results = [];\n          for (m = 0, len3 = files.length; m < len3; m++) {\n            file = files[m];\n            results.push(_this.emit(\"uploadprogress\", file, progress, file.upload.bytesSent));\n          }\n          return results;\n        };\n      })(this);\n      xhr.onload = (function(_this) {\n        return function(e) {\n          var error1, ref;\n          if (files[0].status === Dropzone.CANCELED) {\n            return;\n          }\n          if (xhr.readyState !== 4) {\n            return;\n          }\n          if (xhr.responseType !== 'arraybuffer' && xhr.responseType !== 'blob') {\n            response = xhr.responseText;\n            if (xhr.getResponseHeader(\"content-type\") && ~xhr.getResponseHeader(\"content-type\").indexOf(\"application/json\")) {\n              try {\n                response = JSON.parse(response);\n              } catch (error1) {\n                e = error1;\n                response = \"Invalid JSON response from server.\";\n              }\n            }\n          }\n          updateProgress();\n          if (!((200 <= (ref = xhr.status) && ref < 300))) {\n            return handleError();\n          } else {\n            return _this._finished(files, response, e);\n          }\n        };\n      })(this);\n      xhr.onerror = (function(_this) {\n        return function() {\n          if (files[0].status === Dropzone.CANCELED) {\n            return;\n          }\n          return handleError();\n        };\n      })(this);\n      progressObj = (ref = xhr.upload) != null ? ref : xhr;\n      progressObj.onprogress = updateProgress;\n      headers = {\n        \"Accept\": \"application/json\",\n        \"Cache-Control\": \"no-cache\",\n        \"X-Requested-With\": \"XMLHttpRequest\"\n      };\n      if (this.options.headers) {\n        extend(headers, this.options.headers);\n      }\n      for (headerName in headers) {\n        headerValue = headers[headerName];\n        if (headerValue) {\n          xhr.setRequestHeader(headerName, headerValue);\n        }\n      }\n      formData = new FormData();\n      if (this.options.params) {\n        ref1 = this.options.params;\n        for (key in ref1) {\n          value = ref1[key];\n          formData.append(key, value);\n        }\n      }\n      for (k = 0, len1 = files.length; k < len1; k++) {\n        file = files[k];\n        this.emit(\"sending\", file, xhr, formData);\n      }\n      if (this.options.uploadMultiple) {\n        this.emit(\"sendingmultiple\", files, xhr, formData);\n      }\n      if (this.element.tagName === \"FORM\") {\n        ref2 = this.element.querySelectorAll(\"input, textarea, select, button\");\n        for (l = 0, len2 = ref2.length; l < len2; l++) {\n          input = ref2[l];\n          inputName = input.getAttribute(\"name\");\n          inputType = input.getAttribute(\"type\");\n          if (input.tagName === \"SELECT\" && input.hasAttribute(\"multiple\")) {\n            ref3 = input.options;\n            for (m = 0, len3 = ref3.length; m < len3; m++) {\n              option = ref3[m];\n              if (option.selected) {\n                formData.append(inputName, option.value);\n              }\n            }\n          } else if (!inputType || ((ref4 = inputType.toLowerCase()) !== \"checkbox\" && ref4 !== \"radio\") || input.checked) {\n            formData.append(inputName, input.value);\n          }\n        }\n      }\n      doneCounter = 0;\n      results = [];\n      for (i = o = 0, ref5 = files.length - 1; 0 <= ref5 ? o <= ref5 : o >= ref5; i = 0 <= ref5 ? ++o : --o) {\n        doneFunction = (function(_this) {\n          return function(file, paramName, fileName) {\n            return function(transformedFile) {\n              formData.append(paramName, transformedFile, fileName);\n              if (++doneCounter === files.length) {\n                return _this.submitRequest(xhr, formData, files);\n              }\n            };\n          };\n        })(this);\n        results.push(this.options.transformFile.call(this, files[i], doneFunction(files[i], this._getParamName(i), files[i].upload.filename)));\n      }\n      return results;\n    };\n\n    Dropzone.prototype.submitRequest = function(xhr, formData, files) {\n      return xhr.send(formData);\n    };\n\n    Dropzone.prototype._finished = function(files, responseText, e) {\n      var file, j, len;\n      for (j = 0, len = files.length; j < len; j++) {\n        file = files[j];\n        file.status = Dropzone.SUCCESS;\n        this.emit(\"success\", file, responseText, e);\n        this.emit(\"complete\", file);\n      }\n      if (this.options.uploadMultiple) {\n        this.emit(\"successmultiple\", files, responseText, e);\n        this.emit(\"completemultiple\", files);\n      }\n      if (this.options.autoProcessQueue) {\n        return this.processQueue();\n      }\n    };\n\n    Dropzone.prototype._errorProcessing = function(files, message, xhr) {\n      var file, j, len;\n      for (j = 0, len = files.length; j < len; j++) {\n        file = files[j];\n        file.status = Dropzone.ERROR;\n        this.emit(\"error\", file, message, xhr);\n        this.emit(\"complete\", file);\n      }\n      if (this.options.uploadMultiple) {\n        this.emit(\"errormultiple\", files, message, xhr);\n        this.emit(\"completemultiple\", files);\n      }\n      if (this.options.autoProcessQueue) {\n        return this.processQueue();\n      }\n    };\n\n    return Dropzone;\n\n  })(Emitter);\n\n  Dropzone.version = \"5.1.1\";\n\n  Dropzone.options = {};\n\n  Dropzone.optionsForElement = function(element) {\n    if (element.getAttribute(\"id\")) {\n      return Dropzone.options[camelize(element.getAttribute(\"id\"))];\n    } else {\n      return void 0;\n    }\n  };\n\n  Dropzone.instances = [];\n\n  Dropzone.forElement = function(element) {\n    if (typeof element === \"string\") {\n      element = document.querySelector(element);\n    }\n    if ((element != null ? element.dropzone : void 0) == null) {\n      throw new Error(\"No Dropzone found for given element. This is probably because you're trying to access it before Dropzone had the time to initialize. Use the `init` option to setup any additional observers on your Dropzone.\");\n    }\n    return element.dropzone;\n  };\n\n  Dropzone.autoDiscover = true;\n\n  Dropzone.discover = function() {\n    var checkElements, dropzone, dropzones, j, len, results;\n    if (document.querySelectorAll) {\n      dropzones = document.querySelectorAll(\".dropzone\");\n    } else {\n      dropzones = [];\n      checkElements = function(elements) {\n        var el, j, len, results;\n        results = [];\n        for (j = 0, len = elements.length; j < len; j++) {\n          el = elements[j];\n          if (/(^| )dropzone($| )/.test(el.className)) {\n            results.push(dropzones.push(el));\n          } else {\n            results.push(void 0);\n          }\n        }\n        return results;\n      };\n      checkElements(document.getElementsByTagName(\"div\"));\n      checkElements(document.getElementsByTagName(\"form\"));\n    }\n    results = [];\n    for (j = 0, len = dropzones.length; j < len; j++) {\n      dropzone = dropzones[j];\n      if (Dropzone.optionsForElement(dropzone) !== false) {\n        results.push(new Dropzone(dropzone));\n      } else {\n        results.push(void 0);\n      }\n    }\n    return results;\n  };\n\n  Dropzone.blacklistedBrowsers = [/opera.*Macintosh.*version\\/12/i];\n\n  Dropzone.isBrowserSupported = function() {\n    var capableBrowser, j, len, ref, regex;\n    capableBrowser = true;\n    if (window.File && window.FileReader && window.FileList && window.Blob && window.FormData && document.querySelector) {\n      if (!(\"classList\" in document.createElement(\"a\"))) {\n        capableBrowser = false;\n      } else {\n        ref = Dropzone.blacklistedBrowsers;\n        for (j = 0, len = ref.length; j < len; j++) {\n          regex = ref[j];\n          if (regex.test(navigator.userAgent)) {\n            capableBrowser = false;\n            continue;\n          }\n        }\n      }\n    } else {\n      capableBrowser = false;\n    }\n    return capableBrowser;\n  };\n\n  Dropzone.dataURItoBlob = function(dataURI) {\n    var ab, byteString, i, ia, j, mimeString, ref;\n    byteString = atob(dataURI.split(',')[1]);\n    mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];\n    ab = new ArrayBuffer(byteString.length);\n    ia = new Uint8Array(ab);\n    for (i = j = 0, ref = byteString.length; 0 <= ref ? j <= ref : j >= ref; i = 0 <= ref ? ++j : --j) {\n      ia[i] = byteString.charCodeAt(i);\n    }\n    return new Blob([ab], {\n      type: mimeString\n    });\n  };\n\n  without = function(list, rejectedItem) {\n    var item, j, len, results;\n    results = [];\n    for (j = 0, len = list.length; j < len; j++) {\n      item = list[j];\n      if (item !== rejectedItem) {\n        results.push(item);\n      }\n    }\n    return results;\n  };\n\n  camelize = function(str) {\n    return str.replace(/[\\-_](\\w)/g, function(match) {\n      return match.charAt(1).toUpperCase();\n    });\n  };\n\n  Dropzone.createElement = function(string) {\n    var div;\n    div = document.createElement(\"div\");\n    div.innerHTML = string;\n    return div.childNodes[0];\n  };\n\n  Dropzone.elementInside = function(element, container) {\n    if (element === container) {\n      return true;\n    }\n    while (element = element.parentNode) {\n      if (element === container) {\n        return true;\n      }\n    }\n    return false;\n  };\n\n  Dropzone.getElement = function(el, name) {\n    var element;\n    if (typeof el === \"string\") {\n      element = document.querySelector(el);\n    } else if (el.nodeType != null) {\n      element = el;\n    }\n    if (element == null) {\n      throw new Error(\"Invalid `\" + name + \"` option provided. Please provide a CSS selector or a plain HTML element.\");\n    }\n    return element;\n  };\n\n  Dropzone.getElements = function(els, name) {\n    var e, el, elements, error1, j, k, len, len1, ref;\n    if (els instanceof Array) {\n      elements = [];\n      try {\n        for (j = 0, len = els.length; j < len; j++) {\n          el = els[j];\n          elements.push(this.getElement(el, name));\n        }\n      } catch (error1) {\n        e = error1;\n        elements = null;\n      }\n    } else if (typeof els === \"string\") {\n      elements = [];\n      ref = document.querySelectorAll(els);\n      for (k = 0, len1 = ref.length; k < len1; k++) {\n        el = ref[k];\n        elements.push(el);\n      }\n    } else if (els.nodeType != null) {\n      elements = [els];\n    }\n    if (!((elements != null) && elements.length)) {\n      throw new Error(\"Invalid `\" + name + \"` option provided. Please provide a CSS selector, a plain HTML element or a list of those.\");\n    }\n    return elements;\n  };\n\n  Dropzone.confirm = function(question, accepted, rejected) {\n    if (window.confirm(question)) {\n      return accepted();\n    } else if (rejected != null) {\n      return rejected();\n    }\n  };\n\n  Dropzone.isValidFile = function(file, acceptedFiles) {\n    var baseMimeType, j, len, mimeType, validType;\n    if (!acceptedFiles) {\n      return true;\n    }\n    acceptedFiles = acceptedFiles.split(\",\");\n    mimeType = file.type;\n    baseMimeType = mimeType.replace(/\\/.*$/, \"\");\n    for (j = 0, len = acceptedFiles.length; j < len; j++) {\n      validType = acceptedFiles[j];\n      validType = validType.trim();\n      if (validType.charAt(0) === \".\") {\n        if (file.name.toLowerCase().indexOf(validType.toLowerCase(), file.name.length - validType.length) !== -1) {\n          return true;\n        }\n      } else if (/\\/\\*$/.test(validType)) {\n        if (baseMimeType === validType.replace(/\\/.*$/, \"\")) {\n          return true;\n        }\n      } else {\n        if (mimeType === validType) {\n          return true;\n        }\n      }\n    }\n    return false;\n  };\n\n  if (typeof jQuery !== \"undefined\" && jQuery !== null) {\n    jQuery.fn.dropzone = function(options) {\n      return this.each(function() {\n        return new Dropzone(this, options);\n      });\n    };\n  }\n\n  if (typeof module !== \"undefined\" && module !== null) {\n    module.exports = Dropzone;\n  } else {\n    window.Dropzone = Dropzone;\n  }\n\n  Dropzone.ADDED = \"added\";\n\n  Dropzone.QUEUED = \"queued\";\n\n  Dropzone.ACCEPTED = Dropzone.QUEUED;\n\n  Dropzone.UPLOADING = \"uploading\";\n\n  Dropzone.PROCESSING = Dropzone.UPLOADING;\n\n  Dropzone.CANCELED = \"canceled\";\n\n  Dropzone.ERROR = \"error\";\n\n  Dropzone.SUCCESS = \"success\";\n\n\n  /*\n  \n  Bugfix for iOS 6 and 7\n  Source: http://stackoverflow.com/questions/11929099/html5-canvas-drawimage-ratio-bug-ios\n  based on the work of https://github.com/stomita/ios-imagefile-megapixel\n   */\n\n  detectVerticalSquash = function(img) {\n    var alpha, canvas, ctx, data, ey, ih, iw, py, ratio, sy;\n    iw = img.naturalWidth;\n    ih = img.naturalHeight;\n    canvas = document.createElement(\"canvas\");\n    canvas.width = 1;\n    canvas.height = ih;\n    ctx = canvas.getContext(\"2d\");\n    ctx.drawImage(img, 0, 0);\n    data = ctx.getImageData(1, 0, 1, ih).data;\n    sy = 0;\n    ey = ih;\n    py = ih;\n    while (py > sy) {\n      alpha = data[(py - 1) * 4 + 3];\n      if (alpha === 0) {\n        ey = py;\n      } else {\n        sy = py;\n      }\n      py = (ey + sy) >> 1;\n    }\n    ratio = py / ih;\n    if (ratio === 0) {\n      return 1;\n    } else {\n      return ratio;\n    }\n  };\n\n  drawImageIOSFix = function(ctx, img, sx, sy, sw, sh, dx, dy, dw, dh) {\n    var vertSquashRatio;\n    vertSquashRatio = detectVerticalSquash(img);\n    return ctx.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh / vertSquashRatio);\n  };\n\n  ExifRestore = (function() {\n    function ExifRestore() {}\n\n    ExifRestore.KEY_STR = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';\n\n    ExifRestore.encode64 = function(input) {\n      var chr1, chr2, chr3, enc1, enc2, enc3, enc4, i, output;\n      output = '';\n      chr1 = void 0;\n      chr2 = void 0;\n      chr3 = '';\n      enc1 = void 0;\n      enc2 = void 0;\n      enc3 = void 0;\n      enc4 = '';\n      i = 0;\n      while (true) {\n        chr1 = input[i++];\n        chr2 = input[i++];\n        chr3 = input[i++];\n        enc1 = chr1 >> 2;\n        enc2 = (chr1 & 3) << 4 | chr2 >> 4;\n        enc3 = (chr2 & 15) << 2 | chr3 >> 6;\n        enc4 = chr3 & 63;\n        if (isNaN(chr2)) {\n          enc3 = enc4 = 64;\n        } else if (isNaN(chr3)) {\n          enc4 = 64;\n        }\n        output = output + this.KEY_STR.charAt(enc1) + this.KEY_STR.charAt(enc2) + this.KEY_STR.charAt(enc3) + this.KEY_STR.charAt(enc4);\n        chr1 = chr2 = chr3 = '';\n        enc1 = enc2 = enc3 = enc4 = '';\n        if (!(i < input.length)) {\n          break;\n        }\n      }\n      return output;\n    };\n\n    ExifRestore.restore = function(origFileBase64, resizedFileBase64) {\n      var image, rawImage, segments;\n      if (!origFileBase64.match('data:image/jpeg;base64,')) {\n        return resizedFileBase64;\n      }\n      rawImage = this.decode64(origFileBase64.replace('data:image/jpeg;base64,', ''));\n      segments = this.slice2Segments(rawImage);\n      image = this.exifManipulation(resizedFileBase64, segments);\n      return 'data:image/jpeg;base64,' + this.encode64(image);\n    };\n\n    ExifRestore.exifManipulation = function(resizedFileBase64, segments) {\n      var aBuffer, exifArray, newImageArray;\n      exifArray = this.getExifArray(segments);\n      newImageArray = this.insertExif(resizedFileBase64, exifArray);\n      aBuffer = new Uint8Array(newImageArray);\n      return aBuffer;\n    };\n\n    ExifRestore.getExifArray = function(segments) {\n      var seg, x;\n      seg = void 0;\n      x = 0;\n      while (x < segments.length) {\n        seg = segments[x];\n        if (seg[0] === 255 & seg[1] === 225) {\n          return seg;\n        }\n        x++;\n      }\n      return [];\n    };\n\n    ExifRestore.insertExif = function(resizedFileBase64, exifArray) {\n      var array, ato, buf, imageData, mae, separatePoint;\n      imageData = resizedFileBase64.replace('data:image/jpeg;base64,', '');\n      buf = this.decode64(imageData);\n      separatePoint = buf.indexOf(255, 3);\n      mae = buf.slice(0, separatePoint);\n      ato = buf.slice(separatePoint);\n      array = mae;\n      array = array.concat(exifArray);\n      array = array.concat(ato);\n      return array;\n    };\n\n    ExifRestore.slice2Segments = function(rawImageArray) {\n      var endPoint, head, length, seg, segments;\n      head = 0;\n      segments = [];\n      while (true) {\n        if (rawImageArray[head] === 255 & rawImageArray[head + 1] === 218) {\n          break;\n        }\n        if (rawImageArray[head] === 255 & rawImageArray[head + 1] === 216) {\n          head += 2;\n        } else {\n          length = rawImageArray[head + 2] * 256 + rawImageArray[head + 3];\n          endPoint = head + length + 2;\n          seg = rawImageArray.slice(head, endPoint);\n          segments.push(seg);\n          head = endPoint;\n        }\n        if (head > rawImageArray.length) {\n          break;\n        }\n      }\n      return segments;\n    };\n\n    ExifRestore.decode64 = function(input) {\n      var base64test, buf, chr1, chr2, chr3, enc1, enc2, enc3, enc4, i, output;\n      output = '';\n      chr1 = void 0;\n      chr2 = void 0;\n      chr3 = '';\n      enc1 = void 0;\n      enc2 = void 0;\n      enc3 = void 0;\n      enc4 = '';\n      i = 0;\n      buf = [];\n      base64test = /[^A-Za-z0-9\\+\\/\\=]/g;\n      if (base64test.exec(input)) {\n        console.warning('There were invalid base64 characters in the input text.\\n' + 'Valid base64 characters are A-Z, a-z, 0-9, \\'+\\', \\'/\\',and \\'=\\'\\n' + 'Expect errors in decoding.');\n      }\n      input = input.replace(/[^A-Za-z0-9\\+\\/\\=]/g, '');\n      while (true) {\n        enc1 = this.KEY_STR.indexOf(input.charAt(i++));\n        enc2 = this.KEY_STR.indexOf(input.charAt(i++));\n        enc3 = this.KEY_STR.indexOf(input.charAt(i++));\n        enc4 = this.KEY_STR.indexOf(input.charAt(i++));\n        chr1 = enc1 << 2 | enc2 >> 4;\n        chr2 = (enc2 & 15) << 4 | enc3 >> 2;\n        chr3 = (enc3 & 3) << 6 | enc4;\n        buf.push(chr1);\n        if (enc3 !== 64) {\n          buf.push(chr2);\n        }\n        if (enc4 !== 64) {\n          buf.push(chr3);\n        }\n        chr1 = chr2 = chr3 = '';\n        enc1 = enc2 = enc3 = enc4 = '';\n        if (!(i < input.length)) {\n          break;\n        }\n      }\n      return buf;\n    };\n\n    return ExifRestore;\n\n  })();\n\n\n  /*\n   * contentloaded.js\n   *\n   * Author: Diego Perini (diego.perini at gmail.com)\n   * Summary: cross-browser wrapper for DOMContentLoaded\n   * Updated: 20101020\n   * License: MIT\n   * Version: 1.2\n   *\n   * URL:\n   * http://javascript.nwbox.com/ContentLoaded/\n   * http://javascript.nwbox.com/ContentLoaded/MIT-LICENSE\n   */\n\n  contentLoaded = function(win, fn) {\n    var add, doc, done, init, poll, pre, rem, root, top;\n    done = false;\n    top = true;\n    doc = win.document;\n    root = doc.documentElement;\n    add = (doc.addEventListener ? \"addEventListener\" : \"attachEvent\");\n    rem = (doc.addEventListener ? \"removeEventListener\" : \"detachEvent\");\n    pre = (doc.addEventListener ? \"\" : \"on\");\n    init = function(e) {\n      if (e.type === \"readystatechange\" && doc.readyState !== \"complete\") {\n        return;\n      }\n      (e.type === \"load\" ? win : doc)[rem](pre + e.type, init, false);\n      if (!done && (done = true)) {\n        return fn.call(win, e.type || e);\n      }\n    };\n    poll = function() {\n      var e, error1;\n      try {\n        root.doScroll(\"left\");\n      } catch (error1) {\n        e = error1;\n        setTimeout(poll, 50);\n        return;\n      }\n      return init(\"poll\");\n    };\n    if (doc.readyState !== \"complete\") {\n      if (doc.createEventObject && root.doScroll) {\n        try {\n          top = !win.frameElement;\n        } catch (undefined) {}\n        if (top) {\n          poll();\n        }\n      }\n      doc[add](pre + \"DOMContentLoaded\", init, false);\n      doc[add](pre + \"readystatechange\", init, false);\n      return win[add](pre + \"load\", init, false);\n    }\n  };\n\n  Dropzone._autoDiscoverFunction = function() {\n    if (Dropzone.autoDiscover) {\n      return Dropzone.discover();\n    }\n  };\n\n  contentLoaded(window, Dropzone._autoDiscoverFunction);\n\n}).call(this);\n"
  },
  {
    "path": "_examples/dropzonejs/src/views/upload.html",
    "content": "<html>\n\n<head>\n    <title>DropzoneJS Uploader</title>\n\n    <!-- 1 -->\n    <link href=\"/public/css/dropzone.css\" type=\"text/css\" rel=\"stylesheet\" />\n\n    <!-- 2 -->\n    <script src=\"/public/js/dropzone.js\"></script>\n    <!-- 4 -->\n    <script src=\"//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js\"></script>\n    <!-- 5 -->\n    <script>\n        Dropzone.options.myDropzone = {\n            paramName: \"file\", // The name that will be used to transfer the file\n            init: function () {\n                thisDropzone = this;\n                // 6\n                $.get('/uploads', function (data) {\n\n                    if (data == null) {\n                        return;\n                    }\n                    // 7\n                    $.each(data, function (key, value) {\n                        var mockFile = { name: value.name, size: value.size };\n\n                        thisDropzone.emit(\"addedfile\", mockFile);\n                        thisDropzone.options.thumbnail.call(thisDropzone, mockFile, '/public/uploads/thumbnail_' + value.name);\n                        // thisDropzone.createThumbnailFromUrl(mockFile, '/public/uploads/' + value.name); <- doesn't work...\n                        // Make sure that there is no progress bar, etc...\n                        thisDropzone.emit(\"complete\", mockFile);\n                    });\n\n                });\n            }\n        };\n    </script>\n</head>\n\n<body>\n\n    <!-- 3 -->\n    <form action=\"/upload\" method=\"POST\" class=\"dropzone\" id=\"my-dropzone\">\n        <div class=\"fallback\">\n            <input name=\"file\" type=\"file\" multiple />\n            <input type=\"submit\" value=\"Upload\" />\n        </div>\n    </form>\n</body>\n\n</html>"
  },
  {
    "path": "_examples/file-server/basic/assets/app2/app22/just_a_text_no_index.txt",
    "content": "just a text."
  },
  {
    "path": "_examples/file-server/basic/assets/app2/app2app3/index.html",
    "content": "<h1>Hello App2App3 index</h1>"
  },
  {
    "path": "_examples/file-server/basic/assets/app2/index.html",
    "content": "<h1>Hello App2 index</h1>"
  },
  {
    "path": "_examples/file-server/basic/assets/css/main.css",
    "content": "body {\r\n    background-color: black;\r\n}\r\n"
  },
  {
    "path": "_examples/file-server/basic/assets/index.html",
    "content": "<h1>Hello index</h1>"
  },
  {
    "path": "_examples/file-server/basic/assets/js/main.js",
    "content": "console.log(\"example\");"
  },
  {
    "path": "_examples/file-server/basic/assets.system/css/main.css",
    "content": "body {\n    background-color: black;\n}\n"
  },
  {
    "path": "_examples/file-server/basic/assets.system/test.txt",
    "content": "main_test.go#TestHandleDirDot"
  },
  {
    "path": "_examples/file-server/basic/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\n\tapp.Favicon(\"./assets/favicon.ico\")\n\n\t// first parameter is the request path\n\t// second is the system directory\n\t//\n\t// app.HandleDir(\"/css\", iris.Dir(\"./assets/css\"))\n\t// app.HandleDir(\"/js\",  iris.Dir(\"./assets/js\"))\n\n\tv1 := app.Party(\"/v1\")\n\tv1.HandleDir(\"/static\", iris.Dir(\"./assets\"), iris.DirOptions{\n\t\t// Defaults to \"/index.html\", if request path is ending with **/*/$IndexName\n\t\t// then it redirects to **/*(/) which another handler is handling it,\n\t\t// that another handler, called index handler, is auto-registered by the framework\n\t\t// if end developer does not managed to handle it by hand.\n\t\tIndexName: \"/index.html\",\n\t\t// When files should served under compression.\n\t\tCompress: false,\n\t\t// List the files inside the current requested directory if `IndexName` not found.\n\t\tShowList: false,\n\t\t// When ShowList is true you can configure if you want to show or hide hidden files.\n\t\tShowHidden: false,\n\t\tCache: iris.DirCacheOptions{\n\t\t\t// enable in-memory cache and pre-compress the files.\n\t\t\tEnable: true,\n\t\t\t// ignore image types (and pdf).\n\t\t\tCompressIgnore: iris.MatchImagesAssets,\n\t\t\t// do not compress files smaller than size.\n\t\t\tCompressMinSize: 300,\n\t\t\t// available encodings that will be negotiated with client's needs.\n\t\t\tEncodings: []string{\"gzip\", \"br\" /* you can also add: deflate, snappy */},\n\t\t},\n\t\tDirList: iris.DirListRich(),\n\t\t// If `ShowList` is true then this function will be used instead of the default\n\t\t// one to show the list of files of a current requested directory(dir).\n\t\t// DirList: func(ctx iris.Context, dirName string, dir http.File) error { ... }\n\t\t//\n\t\t// Optional validator that loops through each requested resource.\n\t\t// AssetValidator:  func(ctx iris.Context, name string) bool { ... }\n\t})\n\n\t// You can also register any index handler manually, order of registration does not matter:\n\t// v1.Get(\"/static\", [...custom middleware...], func(ctx iris.Context) {\n\t//  [...custom code...]\n\t// \tctx.ServeFile(\"./assets/index.html\")\n\t// })\n\n\t// http://localhost:8080/v1/static\n\t// http://localhost:8080/v1/static/css/main.css\n\t// http://localhost:8080/v1/static/js/jquery-2.1.1.js\n\t// http://localhost:8080/v1/static/favicon.ico\n\treturn app\n}\n\nfunc main() {\n\tapp := newApp()\n\tapp.Logger().SetLevel(\"debug\")\n\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/file-server/basic/main_test.go",
    "content": "package main\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\ntype resource string\n\nfunc (r resource) contentType() string {\n\tswitch filepath.Ext(r.String()) {\n\tcase \".js\":\n\t\treturn \"text/javascript\"\n\tcase \".css\":\n\t\treturn \"text/css\"\n\tcase \".ico\":\n\t\treturn \"image/x-icon\"\n\tcase \".html\", \"\":\n\t\treturn \"text/html\"\n\tdefault:\n\t\treturn \"text/plain\"\n\t}\n}\n\nfunc (r resource) String() string {\n\treturn string(r)\n}\n\nfunc (r resource) strip(strip string) string {\n\ts := r.String()\n\treturn strings.TrimPrefix(s, strip)\n}\n\nfunc (r resource) loadFromBase(dir string, strip string) string {\n\tfilename := r.String()\n\n\tfilename = r.strip(strip)\n\tif filepath.Ext(filename) == \"\" {\n\t\t// root /.\n\t\tfilename = filename + \"/index.html\"\n\t}\n\n\tfullpath := filepath.Join(dir, filename)\n\n\tb, err := os.ReadFile(fullpath)\n\tif err != nil {\n\t\tpanic(fullpath + \" failed with error: \" + err.Error())\n\t}\n\n\tresult := string(b)\n\n\treturn result\n}\n\nfunc TestFileServerBasic(t *testing.T) {\n\turls := []resource{\n\t\t\"/v1/static/css/main.css\",\n\t\t\"/v1/static/js/main.js\",\n\t\t\"/v1/static/favicon.ico\",\n\t\t\"/v1/static/app2\",\n\t\t\"/v1/static/app2/app2app3\",\n\t\t\"/v1/static\",\n\t}\n\n\tapp := newApp()\n\t// route := app.GetRouteReadOnly(\"GET/{file:path}\")\n\t// if route == nil {\n\t// \tapp.Logger().Fatalf(\"expected a route to serve files\")\n\t// }\n\n\t// if expected, got := \"./assets\", route.StaticDir(); expected != got {\n\t// \tapp.Logger().Fatalf(\"expected route's static directory to be: '%s' but got: '%s'\", expected, got)\n\t// }\n\n\t// if !route.StaticDirContainsIndex() {\n\t// \tapp.Logger().Fatalf(\"epxected ./assets to contain an %s file\", \"/index.html\")\n\t// }\n\n\te := httptest.New(t, app)\n\tfor _, u := range urls {\n\t\turl := u.String()\n\t\tcontents := u.loadFromBase(\"./assets\", \"/v1/static\")\n\n\t\te.GET(url).Expect().\n\t\t\tStatus(httptest.StatusOK).\n\t\t\tContentType(u.contentType(), app.ConfigurationReadOnly().GetCharset()).\n\t\t\tBody().IsEqual(contents)\n\t}\n}\n\n// Tests subdomain + request path and system directory with a name that contains a dot(.)\nfunc TestHandleDirDot(t *testing.T) {\n\turls := []resource{\n\t\t\"/v1/assets.system/css/main.css\",\n\t}\n\tapp := newApp()\n\tapp.Subdomain(\"test\").Party(\"/v1\").HandleDir(\"/assets.system\", iris.Dir(\"./assets.system\"))\n\n\te := httptest.New(t, app, httptest.URL(\"http://test.example.com\"))\n\tfor _, u := range urls {\n\t\turl := u.String()\n\t\tcontents := u.loadFromBase(\"./assets.system\", \"/v1/assets.system\")\n\n\t\te.GET(url).Expect().\n\t\t\tStatus(httptest.StatusOK).\n\t\t\tContentType(u.contentType(), app.ConfigurationReadOnly().GetCharset()).\n\t\t\tBody().IsEqual(contents)\n\t}\n}\n"
  },
  {
    "path": "_examples/file-server/embedding-files-into-app/assets/css/main.css",
    "content": "html {\n\tfont-family: sans-serif;\n\t-webkit-text-size-adjust: 100%;\n\t-ms-text-size-adjust: 100%\n}"
  },
  {
    "path": "_examples/file-server/embedding-files-into-app/assets/js/main.js",
    "content": "console.log(\"example\");"
  },
  {
    "path": "_examples/file-server/embedding-files-into-app/main.go",
    "content": "package main\n\nimport (\n\t\"embed\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\n//go:embed assets/*\nvar fs embed.FS\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\tapp.Logger().SetLevel(\"debug\")\n\n\tapp.HandleDir(\"/static\", fs)\n\n\t/*\n\t\tOr if you need to cache them inside the memory (requires the assets folder\n\t\tto be located near the executable program):\n\t\tapp.HandleDir(\"/static\", iris.Dir(\"./assets\"), iris.DirOptions{\n\t\t\tIndexName: \"index.html\",\n\t\t\tCache: iris.DirCacheOptions{\n\t\t\t\tEnable:          true,\n\t\t\t\tEncodings:       []string{\"gzip\"},\n\t\t\t\tCompressIgnore:  iris.MatchImagesAssets,\n\t\t\t\tCompressMinSize: 30 * iris.B,\n\t\t\t},\n\t\t})\n\t*/\n\treturn app\n}\n\nfunc main() {\n\tapp := newApp()\n\n\t// http://localhost:8080/static/css/main.css\n\t// http://localhost:8080/static/js/main.js\n\t// http://localhost:8080/static/favicon.ico\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/file-server/embedding-files-into-app/main_test.go",
    "content": "package main\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\ntype resource string\n\n// content types that are used in the ./assets,\n// we could use the detectContentType that iris do but it's better\n// to do it manually so we can test if that returns the correct result on embedding files.\nfunc (r resource) contentType() string {\n\tswitch filepath.Ext(r.String()) {\n\tcase \".js\":\n\t\treturn \"text/javascript\"\n\tcase \".css\":\n\t\treturn \"text/css\"\n\tcase \".ico\":\n\t\treturn \"image/x-icon\"\n\tcase \".html\":\n\t\treturn \"text/html\"\n\tdefault:\n\t\treturn \"text/plain\"\n\t}\n}\n\nfunc (r resource) String() string {\n\treturn string(r)\n}\n\nfunc (r resource) strip(strip string) string {\n\ts := r.String()\n\treturn strings.TrimPrefix(s, strip)\n}\n\nfunc (r resource) loadFromBase(dir string) string {\n\tfilename := r.strip(\"/static\")\n\n\tfullpath := filepath.Join(dir, filename)\n\n\tb, err := os.ReadFile(fullpath)\n\tif err != nil {\n\t\tpanic(fullpath + \" failed with error: \" + err.Error())\n\t}\n\n\tresult := string(b)\n\tif runtime.GOOS != \"windows\" {\n\t\tresult = strings.ReplaceAll(result, \"\\n\", \"\\r\\n\")\n\t\tresult = strings.ReplaceAll(result, \"\\r\\r\", \"\")\n\t}\n\n\treturn result\n}\n\nvar urls = []resource{\n\t\"/static/css/main.css\",\n\t\"/static/js/main.js\",\n\t\"/static/favicon.ico\",\n}\n\n// if bindata's values matches with the assets/... contents\n// and secondly if the HandleDir had successfully registered\n// the routes and gave the correct response.\nfunc TestEmbeddingFilesIntoApp(t *testing.T) {\n\tapp := newApp()\n\te := httptest.New(t, app)\n\n\troute := app.GetRouteReadOnly(\"GET/static/{file:path}\")\n\tif route == nil {\n\t\tt.Fatalf(\"expected a route to serve embedded files\")\n\t}\n\n\tif runtime.GOOS != \"windows\" {\n\t\t// remove the embedded static favicon for !windows,\n\t\t// it should be built for unix-specific in order to be work\n\t\turls = urls[0 : len(urls)-1]\n\t}\n\n\tfor _, u := range urls {\n\t\turl := u.String()\n\t\tcontents := u.loadFromBase(\"./assets\")\n\n\t\te.GET(url).Expect().\n\t\t\tStatus(httptest.StatusOK).\n\t\t\tContentType(u.contentType()).\n\t\t\tBody().IsEqual(contents)\n\t}\n}\n"
  },
  {
    "path": "_examples/file-server/embedding-files-into-app-bindata/assets/css/main.css",
    "content": "html {\n\tfont-family: sans-serif;\n\t-webkit-text-size-adjust: 100%;\n\t-ms-text-size-adjust: 100%\n}"
  },
  {
    "path": "_examples/file-server/embedding-files-into-app-bindata/assets/js/main.js",
    "content": "console.log(\"example\");"
  },
  {
    "path": "_examples/file-server/embedding-files-into-app-bindata/bindata.go",
    "content": "// Code generated by go-bindata. (@generated) DO NOT EDIT.\n\n// Package main generated by go-bindata.// sources:\n// assets/css/main.css\n// assets/favicon.ico\n// assets/js/main.js\npackage main\n\nimport (\n\t\"bytes\"\n\t\"compress/gzip\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"time\"\n)\n\nfunc bindataRead(data []byte, name string) ([]byte, error) {\n\tgz, err := gzip.NewReader(bytes.NewBuffer(data))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"read %q: %v\", name, err)\n\t}\n\n\tvar buf bytes.Buffer\n\t_, err = io.Copy(&buf, gz)\n\tclErr := gz.Close()\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"read %q: %v\", name, err)\n\t}\n\tif clErr != nil {\n\t\treturn nil, err\n\t}\n\n\treturn buf.Bytes(), nil\n}\n\ntype asset struct {\n\tbytes []byte\n\tinfo  os.FileInfo\n}\n\ntype bindataFileInfo struct {\n\tname    string\n\tsize    int64\n\tmode    os.FileMode\n\tmodTime time.Time\n}\n\n// Name return file name\nfunc (fi bindataFileInfo) Name() string {\n\treturn fi.name\n}\n\n// Size return file size\nfunc (fi bindataFileInfo) Size() int64 {\n\treturn fi.size\n}\n\n// Mode return file mode\nfunc (fi bindataFileInfo) Mode() os.FileMode {\n\treturn fi.mode\n}\n\n// ModTime return file modify time\nfunc (fi bindataFileInfo) ModTime() time.Time {\n\treturn fi.modTime\n}\n\n// IsDir return file whether a directory\nfunc (fi bindataFileInfo) IsDir() bool {\n\treturn fi.mode&os.ModeDir != 0\n}\n\n// Sys return file is sys mode\nfunc (fi bindataFileInfo) Sys() any {\n\treturn nil\n}\n\ntype assetFile struct {\n\t*bytes.Reader\n\tname            string\n\tchildInfos      []os.FileInfo\n\tchildInfoOffset int\n}\n\ntype assetOperator struct{}\n\n// Open implement http.FileSystem interface\nfunc (f *assetOperator) Open(name string) (http.File, error) {\n\tvar err error\n\tif len(name) > 0 && name[0] == '/' {\n\t\tname = name[1:]\n\t}\n\tcontent, err := Asset(name)\n\tif err == nil {\n\t\treturn &assetFile{name: name, Reader: bytes.NewReader(content)}, nil\n\t}\n\tchildren, err := AssetDir(name)\n\tif err == nil {\n\t\tchildInfos := make([]os.FileInfo, 0, len(children))\n\t\tfor _, child := range children {\n\t\t\tchildPath := filepath.Join(name, child)\n\t\t\tinfo, errInfo := AssetInfo(filepath.Join(name, child))\n\t\t\tif errInfo == nil {\n\t\t\t\tchildInfos = append(childInfos, info)\n\t\t\t} else {\n\t\t\t\tchildInfos = append(childInfos, newDirFileInfo(childPath))\n\t\t\t}\n\t\t}\n\t\treturn &assetFile{name: name, childInfos: childInfos}, nil\n\t} else {\n\t\t// If the error is not found, return an error that will\n\t\t// result in a 404 error. Otherwise the server returns\n\t\t// a 500 error for files not found.\n\t\tif strings.Contains(err.Error(), \"not found\") {\n\t\t\treturn nil, os.ErrNotExist\n\t\t}\n\t\treturn nil, err\n\t}\n}\n\n// Close no need do anything\nfunc (f *assetFile) Close() error {\n\treturn nil\n}\n\n// Readdir read dir's children file info\nfunc (f *assetFile) Readdir(count int) ([]os.FileInfo, error) {\n\tif len(f.childInfos) == 0 {\n\t\treturn nil, os.ErrNotExist\n\t}\n\tif count <= 0 {\n\t\treturn f.childInfos, nil\n\t}\n\tif f.childInfoOffset+count > len(f.childInfos) {\n\t\tcount = len(f.childInfos) - f.childInfoOffset\n\t}\n\toffset := f.childInfoOffset\n\tf.childInfoOffset += count\n\treturn f.childInfos[offset : offset+count], nil\n}\n\n// Stat read file info from asset item\nfunc (f *assetFile) Stat() (os.FileInfo, error) {\n\tif len(f.childInfos) != 0 {\n\t\treturn newDirFileInfo(f.name), nil\n\t}\n\treturn AssetInfo(f.name)\n}\n\n// newDirFileInfo return default dir file info\nfunc newDirFileInfo(name string) os.FileInfo {\n\treturn &bindataFileInfo{\n\t\tname:    name,\n\t\tsize:    0,\n\t\tmode:    os.FileMode(2147484068), // equal os.FileMode(0644)|os.ModeDir\n\t\tmodTime: time.Time{}}\n}\n\n// AssetFile return a http.FileSystem instance that data backend by asset\nfunc AssetFile() http.FileSystem {\n\treturn &assetOperator{}\n}\n\nvar _cssMainCss = []byte(\"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\xca\\x28\\xc9\\xcd\\x51\\xa8\\xe6\\xe5\\xe2\\x4c\\xcb\\xcf\\x2b\\xd1\\x4d\\x4b\\xcc\\xcd\\xcc\\xa9\\xb4\\x52\\x28\\x4e\\xcc\\x2b\\xd6\\x2d\\x4e\\x2d\\xca\\x4c\\xb3\\xe6\\xe5\\xe2\\xd4\\x2d\\x4f\\x4d\\xca\\xce\\x2c\\xd1\\x2d\\x49\\xad\\x28\\xd1\\x2d\\xce\\xac\\x4a\\xd5\\x4d\\x4c\\xc9\\x2a\\x2d\\x2e\\xb1\\x52\\x30\\x34\\x30\\x50\\x05\\xab\\xc8\\x2d\\xc6\\x21\\xcb\\xcb\\x55\\x0b\\x08\\x00\\x00\\xff\\xff\\x32\\x4c\\x06\\xc6\\x63\\x00\\x00\\x00\")\n\nfunc cssMainCssBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_cssMainCss,\n\t\t\"css/main.css\",\n\t)\n}\n\nfunc cssMainCss() (*asset, error) {\n\tbytes, err := cssMainCssBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"css/main.css\", size: 99, mode: os.FileMode(438), modTime: time.Unix(1613718750, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\nvar _faviconIco = []byte(\"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\xec\\x5a\\x0b\\x70\\x55\\xc7\\x79\\xde\\x7b\\xce\\x45\\x12\\xb2\\x90\\xc4\\xc3\\xe6\\x61\\x3b\\x90\\xf8\\x31\\xc4\\x19\\x6c\\x32\\xe3\\xc4\\x34\\xe3\\xc6\\x34\\x6d\\xed\\xd4\\x69\\x62\\x32\\x49\\x9a\\xa6\\x75\\xea\\x36\\x33\\xee\\xd8\\x9e\\xb4\\x75\\xdc\\x7a\\xa6\\xc5\\x31\\x02\\xa6\\xd3\\x84\\x47\\x28\\x6f\\x3b\\xc6\\x3c\\xcc\\xeb\\x9e\\xbd\\x08\\x21\\x04\\x92\\x28\\x12\\x0e\\x08\\x5b\\x3c\\xec\\x02\\x92\\x78\\x08\\x21\\x09\\x41\\x25\\x24\\x10\\xe8\\x71\\xb5\\xe7\\xdc\\xd7\\xb9\\xf7\\xef\\xfc\\xff\\x9e\\x73\\x74\\x25\\xdd\\x97\\x24\\x82\\x33\\x1e\\xce\\xcc\\x3f\\x7b\\xee\\x9e\\xdd\\xff\\xff\\xf6\\xdf\\x7f\\xff\\xfd\\xf7\\xdf\\xcb\\x98\\x8b\\xa9\\x2c\\x3f\\x1f\\xcb\\x19\\xec\\x15\\x37\\x63\\x5f\\x67\\x8c\\xcd\\x98\\x21\\x7f\\x6b\\xf9\\x8c\\x6d\\x72\\x33\\x36\\x7b\\xb6\\xf5\\xfb\\x11\\xc6\\x9e\\xbd\\x97\\xb1\\x99\\x8c\\xb1\\x7c\\x6c\\xc7\\x64\\x3d\\x3d\\x6e\\x76\\xdb\\x1f\\xe1\\x75\\xab\\x42\\x53\\x7e\\x22\\x3c\\x6c\\x91\\xf0\\xb0\\x02\\x22\\xae\\x14\\x08\\x8f\\xab\\x40\\xec\\x64\\x92\\xf0\\x5d\\x53\\xfe\\x59\\xec\\x64\\x5f\\x15\\x1e\\xa6\\x08\\x4f\\x7f\\x7f\\xdf\\x7a\\x96\\xa1\\x97\\x3c\\xb8\\x3f\\x78\\xfa\\x0d\\x08\\x9e\\x5d\\x08\\xc1\\xda\\x5f\\x82\\x51\\xf6\\x65\\x30\\xca\\x67\\x41\\xf0\\xcc\\xbf\\x11\\x19\\x07\\x1e\\x07\\xbd\\x78\\x2a\\x18\\x65\\x8f\\xb5\\x0b\\x4d\\x7d\\xad\\x6f\\x07\\x73\\x0b\\xcd\\x45\\xfd\\xbb\\x17\\xb3\\x0c\\xa3\\x62\\xce\\xfe\\xa8\\xd1\\x0a\\xf8\\x98\\x6d\\x25\\x10\\x38\\xfe\\x53\\x88\\xdc\\x3a\\x01\\x66\\x6b\\x11\\x11\\xbe\\x07\\x8e\\xfd\\x2d\\x84\\x2e\\x2c\\x05\\xff\\xd1\\x79\\x3d\\xc2\\xc3\\x7e\\x20\\x78\\x06\\x13\\x5c\\x89\\xe9\\xdf\\x06\\xd1\\x50\\x2f\\x04\\x4e\\xfc\\x0c\\xcc\\x6b\\xa5\\x60\\x1c\\xfd\\x21\\x74\\x6c\\x1e\\x47\\x84\\xef\\x58\\x17\\xf8\\xe4\\x65\\x30\\x6f\\x54\\x81\\x71\\xe0\\x89\\xea\\xbe\\x2d\\x6c\\xd2\\x80\\xfe\\xfe\\x76\\x30\\x6f\\x1c\\x81\\x60\\xed\\x7c\\x08\\x9e\\x5d\\x0c\\x2d\\xeb\\xc7\\x41\\xed\\xd2\\x29\\x50\\xbb\\x74\\x32\\xbd\\x63\\x1d\\x7e\\xc3\\x36\\xa1\\x73\\xff\\x19\\x14\\x1e\\x36\\x4f\\x78\\xc7\\xc4\\xf4\\xef\\x80\\x70\\xd3\\xfb\\x10\\x6e\\x5c\\x0f\\xdd\\x65\\x73\\xe1\\xdc\\xf2\\x89\\xd0\\xb1\\x25\\x0f\\x6e\\x7c\\x90\\x07\\xe7\\x7f\\x33\\x81\\xea\\xf0\\x5b\\xb8\\x79\\x13\\x61\\xd0\\x8b\\xa7\\x2c\\x09\\x7c\\xfc\\x57\\xac\\x7b\\x11\\xf6\\x7f\\x6a\\x5f\\xd4\\xb8\\x06\\xa1\\xfa\\x15\\x10\\x6e\\x7a\\x0f\\x3a\\xb4\\xc7\\xa0\\x61\\x55\\x3e\\x04\\x8f\\xfc\\x39\\x84\\x3e\\xfa\\x4b\\x68\\x5c\\x93\\x4f\\x75\\xf8\\x2d\\x74\\x71\\x05\\x44\\x45\\x13\\xea\\x54\\xeb\\x7c\\x85\\xa9\\x48\\xf8\\x1e\\x15\\xcd\\xf4\\xcd\\xee\\x7f\\x71\\x65\\x3e\\x04\\x3e\\x7c\\x86\\x78\\x5c\\x5a\\x1d\\xd3\\xbf\\x7e\\x05\\xa0\\x2c\\x94\\x89\\xb2\\x03\\xd5\\x3f\\x66\\x88\\x05\\x31\\x21\\x36\\x1b\\xff\\xd9\\xe5\\x13\\xe1\\xda\\xc6\\x5c\\x68\\xdf\\x94\\x4b\\x63\\x71\\xf0\\x37\\xbd\\x0f\\x38\\x56\\x1c\\x33\\x8e\\x1d\\x75\\x80\\xba\\x40\\x9d\\x0c\\xd6\\x5f\\xcd\\x92\\x29\\x44\\x83\\xf5\\x87\\xba\\x76\\xfa\\x73\\x85\\xe1\\x5c\\x18\\x07\\x9e\\x38\\x86\\x18\\x68\\x8e\\xac\\xf9\\xbb\\xbe\\x39\\x87\\xc8\\x99\\xbf\\x13\\x3f\\xa3\\x39\\xc6\\xb9\\x8e\\xed\\x4f\\xb6\\xe0\\x61\\x3f\\x40\\xdb\\x40\\x1b\\x41\\x5b\\x19\\x62\\x3f\\xc7\\x7f\\x4a\\xb6\\x85\\x0f\\xda\\x9a\\xdd\\x9f\\xd6\\x80\\xe6\\x62\\xd2\\x26\\xd5\\xd7\\xd0\\x46\\xc9\\x56\\x0f\\x3c\\xde\\x6f\\xbf\\xe5\\xb3\\xc8\\xa6\\xd1\\xb6\\xc9\\xc6\\x4f\\xbf\\x01\\x68\\xf3\\x68\\xfb\\xce\\x3a\\xf2\\x30\\x26\\x76\\x32\\x85\\xd6\\x08\\xae\\x95\\xc1\\xeb\\x87\\xd6\\x14\\xb3\\x69\\x11\\xad\\x39\\xaf\\x5b\\x1d\\xed\\xfa\\x05\\x60\\x8c\\x65\\x32\\xc6\\x54\\x8b\\x5c\\x31\\x64\\x3d\\x0b\\x63\\xe8\\xb0\\x45\\x2d\\x56\\xdf\\x99\\x96\\x8f\\x99\\x1b\\xeb\\x67\\xf2\\x47\\x8b\\xea\\xf3\\xf9\\x08\\xae\\x22\\xe5\\x09\\xae\\x4e\\x17\\x5c\\xfd\\xc2\\x28\\x69\\x8a\\xe0\\xca\\x58\\xb4\\x3d\\xdb\\x17\\xa6\\x29\\xff\\x5f\\x04\\x57\\x5b\\x85\\xe6\\xba\\x92\\x90\\xb8\\x1a\\x43\\x4a\\x4c\\xbd\\x62\\xd7\\xb7\\x08\\xae\\xd6\\x0b\\xae\\xfe\\x8f\\xe0\\xea\\x9b\\x38\\x1e\\x1f\\x1f\\x4b\\xfe\\x34\\xb5\\x7c\\xa5\\x40\\xec\\xca\\x02\\xbd\\x64\\x3a\\xe8\\xfb\\xbe\\x38\\x94\\x4a\\xa6\\x83\\xf0\\x66\\x82\\xd0\\x14\\x10\\x9a\\x0b\\x44\\xe1\\x38\\x5c\\x2b\\x44\\xf8\\x4e\\x75\\x5c\\x01\\xe1\\x75\\x03\\xf2\\x11\\x9a\\x2b\\x2a\\xb8\\x52\\x2b\\xb8\\x3a\\x4f\\x68\\x8a\\x92\\x0c\\x03\\xc9\\xf7\\xb0\\x02\\xa3\\xfc\\x2b\\x10\\xe9\\xae\\x85\\xa8\\x7e\\x15\\xa2\\xfa\\x95\\x18\\xba\\x0a\\xa1\\xb3\\x8b\\x49\\xbe\\xbe\\xe7\\x5e\\x08\\xfe\\xef\\x3f\\x81\\xd9\\x51\\x09\\x91\\xbe\\x4b\\x44\\xf8\\x8e\\x75\\xf8\\x4d\\x70\\x37\\x04\\x3e\\xfe\\x11\\xf9\\x14\\xbd\\x68\\x3c\\xe2\\xe8\\x14\\x5c\\x7d\\x49\\x78\\x99\\x2b\\x11\\x06\\x47\\xfe\\x81\\xd9\\x10\\x0d\\xde\\x82\\xc1\\x8f\\x79\\xe3\\x30\\xe8\\x7b\\xa7\\x81\\x51\\xfa\\x28\\x98\\xd7\\xca\\x00\\xa2\\xa6\\xfc\\x80\\x65\\xcc\\x3b\\x7e\\xc3\\x36\\xfa\\xde\\x07\\x08\\x93\\xd9\\x5a\\x0c\\xfa\\xfe\\x87\\x41\\x68\\xac\\x43\\x70\\xf5\\x59\\x92\\xe3\\x1d\\xea\\x1a\\x92\\xc9\\x8f\\x06\\x3a\\xc1\\xff\\xe1\\x5c\\xda\\x67\\x91\\x27\\x3e\\x91\\xde\\x0b\\xe4\\xaf\\xfc\\x47\\x5f\\x20\\xc2\\x77\\xac\\x23\\xac\\x1d\\x95\\xd4\\xd6\\xff\\xe1\\x9f\\x10\\x2f\\xfa\\x5d\\xf2\\x05\\xc4\\x70\\x4c\\x70\\xf5\\x7e\\x94\\x35\\x1c\\xf9\\xa1\\xc6\\x77\\x68\\x3e\\x43\\x17\\x57\\x4a\\xfe\\xe8\\xc3\\xcb\\xbe\\x8c\\xfc\\xa0\\xcf\\x23\\x09\\xdf\\xb1\\x0e\\xbf\\x51\\x9f\\x8b\\x2b\\x65\\x9f\\xc6\\x77\\xe8\\x37\\xee\\x4f\\x62\\xd7\\x58\\xb4\\x8f\\x05\\x3e\\x9e\\x35\\xc4\\x1e\\x13\\xc9\\x8f\\x06\\xbb\\xc0\\xa8\\xfc\\x23\\xf0\\x1f\\xfa\\x63\\x88\\x86\\xba\\x21\\x72\\xeb\\x24\\x18\\xfb\\x1f\\x82\\xde\\x1d\\x2e\\x68\\xdd\\x30\\x0e\\x1a\\x56\\x4f\\x24\\xc2\\x77\\xac\\xc3\\x6f\\xd8\\x06\\xdb\\x62\\x1f\\xec\\x8b\\xef\\x10\\xd6\\xc1\\xff\\xd1\\xf7\\x11\\x67\\x83\\xe0\\xea\\x97\\x06\\xeb\\x20\\x91\\x7c\\xd2\\xdd\\xee\\x3c\\x08\\x37\\x6f\\x04\\x30\\x0d\\x08\\x7c\\x34\\x8f\\xe4\\x34\\xae\\x99\\x00\\xa7\\x7f\\x3d\\x0d\\x4e\\xfd\\xea\\x7e\\x22\\x7c\\xc7\\x3a\\xfc\\x86\\x6d\\xb0\\x6d\\xb8\\x69\\x23\\xf5\\xb5\\xe7\\xcc\\x6c\\x3f\\x00\\x62\\x77\\x6e\\x54\\x68\\xae\\x57\\xad\\xf5\\x96\\x52\\x7e\\xb0\\x6e\\x01\\xe8\\xfb\\x66\\xd0\\x1a\\x30\\xaf\\x95\\x83\\x28\\xcc\\x81\\x2b\\xef\\xe6\\x92\\x3c\\x24\\x8a\\x63\\x96\\x4d\\x71\\x7e\\xe3\\x37\\x6c\\x83\\x6d\\xb1\\x0f\\xf6\\x0d\\xd6\\x15\\x48\\x5d\\x86\\x7c\\x64\\x47\\xc2\\xc3\\xf6\\x0a\\xae\\x66\\xc6\\xea\\x20\\xae\\xfc\\x48\\x08\\xfc\\x55\\xdf\\x05\\x7f\\xd5\\x77\\x00\\x22\\x41\\x08\\x7e\\xfa\\x2a\\x74\\x6d\\x53\\xa1\\x6e\\xf9\\x64\\x92\\x75\\x69\\xcd\\x44\\xb8\\xb9\\x35\\x13\\x6e\\x6d\\xcb\\x80\\xe6\\x75\\xe3\\xa9\\x0e\\xbf\\x61\\x1b\\x6c\\x8b\\x7d\\xb0\\xaf\\xbf\\xea\\x7b\\xc4\\x8b\\xec\\xe2\\xec\\x22\\x9c\\x83\\x66\\xc1\\xd5\\x19\\xa9\\xe4\\xd3\\xdc\\x1f\\x98\\x0d\\xc1\\x33\\x6f\\x42\\x34\\xec\\x03\\x7f\\xe5\\x1c\\x68\\xdb\\x30\\x96\\xe4\\x9c\\xfb\\xcd\\x7d\\xd0\\xbd\\x63\\x0c\\xe8\\x85\\x63\\x41\\x2f\\xbc\\x07\\x7a\\x3d\\x6e\\xa8\\x5f\\x39\\x89\\xbe\\x61\\x1b\\x6c\\x8b\\x7d\\xb0\\xaf\\xe4\\xd9\\x65\\xd9\\x6e\\x19\\x88\\x5d\\xd9\\xba\\xd0\\x5c\\x73\\x53\\xca\\xd7\\xff\\x0f\\xf4\\xfd\\x0f\\x41\\xa8\\x61\\x35\\xf9\\x1f\\x7d\\xdf\\x74\\x68\\x5e\\x97\\x47\\xf3\\xdd\\xf2\\x4e\\x1e\\xc9\\x0d\\x9f\\xfc\\x3b\\x30\\x4f\\xbd\\x02\\xc6\\x9e\\x09\\xd0\\xba\\x21\\x07\\x4e\\xfd\\x6a\\x1a\\xb5\\xc1\\xb6\\xe4\\xb3\\x1a\\x56\\x13\\x0f\\xe4\\x25\\xd7\\xed\\x39\\x5c\\x9b\\x51\\xa1\\xb1\\x17\\xe3\\xc8\\x7f\\x0b\\x63\\x13\\x5c\\xef\\xd4\\xb6\\xaf\\x91\\x7c\\x6b\\xf8\\xf2\\x16\\x5a\\xdb\\x62\\xcf\\x64\\xb8\\xb4\\x7a\\x3c\\xc9\\x68\\xdb\\x90\\x0d\\xfe\\xb2\\x87\\x01\\x2e\\x2d\\x07\\x68\\x5a\\x05\\x81\\xca\\x27\\xe1\\xfa\\xe6\\x4c\\x39\\x2f\\xab\\xc7\\x53\\xdb\\x88\\xaf\\x9e\\xfa\\x22\\x0f\\xe4\\xe5\\xc4\\x47\\xa5\\x8f\\xa2\\x0d\\xfc\\x22\\x8e\\xfc\\x5f\\xe0\\x37\\x3b\\x5e\\x4f\\x2e\\xff\\x1e\\xf0\\x97\\x7e\\x11\\xa2\\x0d\\x4b\\x00\\x9a\\x56\\x42\\xe0\\xe0\\x13\\x70\\x7d\\x53\\x56\\x6a\\xf9\\x81\\x4e\\x8a\\xbf\\x70\\xac\\x43\\xe4\\x6b\\xec\\x45\\xd4\\x0d\\xea\\x28\\xbe\\xfe\\x67\\x38\\xfa\\x6f\\x5e\\x97\\x2f\\xfd\\xcb\\xd1\\xe7\\x21\\x74\\xec\\x87\\xa0\\xef\\xce\\x81\\xab\\xbf\\x1d\\x67\\x7d\\xcb\\xb3\\xd6\\xcc\\x50\\xfd\\xe3\\xdc\\xe2\\x1c\\xcb\\xb3\\xd0\\x60\\xf9\\xae\\xb9\\x68\\x1b\\xe4\\xdf\\x53\\xd8\\x1f\\xda\\x39\\xda\\xbe\\xee\\x55\\x41\\xf7\\xba\\xc9\\x16\\xcf\\xaf\\xb8\\x2f\\xa5\\xfd\\x25\\x95\\x8f\\x6b\\x42\\x63\\xcd\\xb8\\xcf\\x25\\x5c\\x7f\\x5b\\xe5\\xfa\\xc3\\x39\\xb8\\xf0\\xdf\\xf7\\x42\\xfb\\xc6\\x7b\\xa0\\x63\\x53\\x36\\x5c\\x5c\\x35\\x29\\xad\\xf5\\x97\\x42\\x7e\\x26\\xfa\\x06\\xda\\x37\\xc2\\xbe\\xb4\\xfc\\x0f\\xd2\\x99\\x25\\xd3\\xd2\\xf2\\x3f\\xc9\\xe5\\x2b\\xf6\\x1c\\xbc\\x86\\x3e\\x12\\x7d\\xe5\\x50\\xff\\xeb\\x8f\\xe3\\x7f\\x25\\x0d\\xf5\\xbf\\xfe\\x21\\xfe\\x37\\x99\\xfc\\x18\\x1d\\x7c\\x09\\xf7\\x08\\xdc\\x2b\\xc0\\xd4\\x69\\xef\\x30\\x2a\\x46\\xb9\\xff\\x58\\x73\\x9f\\x5a\\xbe\\xc2\\x7c\\x1a\\xed\\x8d\\x0b\\x70\\xaf\\xc4\\x3d\\x53\\xee\\xbf\\xef\\x8e\\x7a\\xff\\x4d\\x47\\x7e\\x8c\\x0e\\xee\\xc7\\x58\\x01\\x63\\x06\\xd4\\x1d\\xf6\\x41\\x9b\\x18\\x51\\xfc\\x61\\xf9\\xb2\\xb4\\xe5\\x7b\\x55\\x1b\\xc3\\xb3\\x18\\x33\\x61\\xec\\x84\\x31\\x14\\xf1\\xdc\\xfb\\x40\\x5a\\xf1\\x97\\x4e\\xf1\\xd7\\x34\\x8a\\xd9\\x06\\x3f\\xa9\\xe4\\x3b\\xb6\\x48\\xb1\\xa2\\xfa\\x12\\xc6\\x8e\\x18\\x43\\x62\\x2c\\x89\\x31\\x25\\xc6\\x96\\x29\\xe3\\x4f\\x6f\\x26\\xc5\\xaa\\xf1\\x62\\x58\\x8c\\x6d\\x31\\xc6\\x4d\\x26\\xbf\\x7f\\x3d\\x60\\xcc\\xac\\xce\\xa3\\x18\\x1a\\x63\\x69\\x8c\\xa9\\xb9\\x5b\\xc6\\xd8\\x14\\x7f\\xe7\\xc4\\xc4\\xdf\\x39\\xb2\\x0e\\x63\\x73\\x8c\\x91\\x93\\xc5\\xf0\\xc4\\x47\\x49\\x2a\\x9f\\x30\\x68\\x0a\\xf3\\x15\\x65\\x31\\x6b\\xaf\\x7e\\xd3\\x3a\\x53\\xd4\\x5b\\x67\\x8c\\x44\\xe7\\x0f\\x49\\xc9\\xcf\\x30\\xad\\xd6\\x59\\x27\\xa9\\xfc\\x58\\x5d\\xf4\\x69\\x14\\x2f\\x8d\\x95\\x67\\xab\\x51\\x9f\\xcf\\xa6\\x5b\\x67\\xbd\\xb4\\xe4\\x7f\\xde\\x1f\\x7b\\x6d\\x1c\\x66\\x2a\\x1c\\x66\\x0c\\xe9\\x99\\xc3\\x8c\\x4d\\xb7\\x28\\xcf\\xa2\\x4c\\x8b\\xd4\\x74\\xa8\\xc5\\xa2\\x1e\\x8b\\x02\\x16\\x99\\x8c\\xa9\\xc0\\x48\\x90\\x6a\\xcb\\x9d\\xc9\\x18\\x9b\\xcd\\x18\\xfb\\xfb\\xd8\\x3c\\xc5\\xc3\\x9f\\xb5\\x56\\xee\\x3e\\x77\\x9f\\x3f\\xcc\\xc7\\xda\\x1f\\x1f\\x16\\x5c\\x7d\\x5b\\x70\\x75\\xa1\\xe0\\x6a\\xc1\\xef\\x89\\x6c\\xde\\xff\\x2a\\xb8\\xfa\\x37\\x82\\x2b\\xb3\\x05\\x57\\x72\\x7a\\x35\\xc6\\x0c\\x2d\\x79\\x3e\\x29\\x0d\\xfc\\xdf\\x16\\x5c\\x0d\\x0a\\xae\\xc2\\x1d\\x22\\x53\\x70\\xb5\\x53\\x70\\xf5\\x20\\xed\\xeb\\x5c\\xc9\\xc7\\x58\\xc3\\x48\\x33\\x3f\\x97\\x18\\xbf\\x42\\xb1\\x57\\x5a\\x84\\x6d\\x07\\x60\\x4a\\xd2\\x37\\x6e\\x5b\\x97\\xfd\\x1b\\xf5\\x56\\x2a\\xb8\\xfa\\x24\\xec\\x67\\x4c\\xe7\\xc3\\x1b\\x83\\x83\\x5f\\x73\\x05\\x31\\xc6\\x0b\\xd6\\xbe\\x45\\xe7\\xf2\\x64\\x14\\x3c\\xbb\\x08\\x8c\\x8a\\xa7\\xfa\\x71\\x21\\xc6\\xc2\\x1c\\x30\\x0e\\x7e\\x0d\\x02\\x27\\xfe\\x81\\x78\\x20\\xe1\\x3b\\xd6\\xc9\\x78\\x84\\x39\\xd8\\xf5\\xe2\\x29\\x60\\x94\\xcd\\x04\\xe1\\xcd\\x8a\\x1d\\x47\\xa3\\xe0\\xea\\x0b\\x7e\\xce\\x5c\\x7a\\x8a\\xfc\\x64\\x7c\\xfc\\x2c\\x48\\xf1\\x75\\xb8\\x6f\\x48\\x9c\\x37\\xf8\\x31\\xdb\\x4a\\x64\\x9c\\x84\\xb2\\xbd\\x19\\xe0\\x3f\\xf2\\x17\\x60\\xb6\\xee\\x85\\x68\\xe0\\x26\\x40\\x34\\x12\\x13\\x20\\x46\\xa8\\x0e\\xbf\\x61\\x1b\\x6c\\x8b\\x7d\\xf4\\xe2\\xc9\\x10\\x6a\\x58\\x0b\\xe1\\xcb\\x5b\\xc1\\xa8\\xfc\\x46\\xff\\x9c\\x70\\xb5\\x0d\\xc7\\xd0\\xa3\\x65\\x33\\x91\\xe6\\x3c\\x0c\\x17\\x7f\\xa4\\xeb\\x14\\xc5\\xb4\\xc2\\xc3\\xe8\\x3c\\x43\\xb1\\x65\\xb0\\x3b\\xe5\\x98\\xb1\\x0d\\xb6\\xc5\\x3e\\xf2\\x7c\\x30\\x13\\x22\\xdd\\x35\\x10\\xf5\\x5f\\xa7\\x73\\xa9\\x28\\xbc\\xc7\\x1e\\x43\\x93\\xe0\\xca\\x53\\x14\\x73\\x16\\xa6\\x8e\\x89\\x86\\x83\\x9f\\xce\\x41\\x55\\xdf\\x23\\xec\\x62\\x77\\x2e\\x9d\\xc7\\xed\\xb3\\xa8\\x1c\\x5c\\x10\\xa2\\xa2\\x05\\x22\\xb7\\x3e\\x21\\xc2\\x77\\xac\\xeb\\xff\\x1e\\xa2\\x3e\\xd8\\x17\\x79\\x20\\xaf\\x68\\xa8\\x87\\xda\\x84\\xea\\x97\\xcb\\xbc\\xb3\\x1c\\x43\\xa5\\xe0\\xea\\xe4\\x74\\x62\\xba\\xe1\\xe0\\x0f\\x35\\xac\\x91\\x36\\xc0\\xdd\\x10\\xac\\x7b\\xbb\\x1f\\x7b\\x34\\x0c\\x66\\x47\\x05\\xdd\\x3d\\xe9\\xa5\\x8f\\x80\\x5e\\x34\\x51\\x52\\xe9\\x23\\xf2\\x3e\\xaa\\xa3\\x82\\xda\\xd8\\x63\\x08\\xd6\\xfe\\x52\\xc6\\xee\\xde\\x0c\\xe2\\x69\\x8f\\x1d\\xcf\\x4d\\x92\\xbf\\x1a\\x45\\x7f\\xeb\\xf3\\x32\\x57\\x5f\\x0a\\x3b\\x4a\\x17\\x3f\\xea\\xd2\\x28\\x9f\\x25\\xf5\\x76\\xe4\\x3b\\xfd\\x79\\xad\\x70\\x1f\\x84\\xce\\xff\\x17\\xe8\\x7b\\x26\\x59\\xfe\\xc6\\xca\\xdb\\xdb\\x3e\\x46\\x63\\xf4\\x0d\\xdb\\xd8\\xbc\\xe9\\x0c\\x78\\xe4\\x79\\xe2\\x85\\x3c\\x69\\x9e\\x68\\x7e\\x7b\\x21\\x50\\xfd\\x63\\x7b\\x4d\\x5f\\x13\\x5c\\xfd\\x7a\\xca\\x73\\x45\\x9a\\xf8\\x69\\x7e\\xad\\xb5\\x67\\xde\\xa8\\xea\\xd7\\x59\\xdd\\x02\\x79\\x0f\\xc1\\xa5\\x1f\\xe9\\xd9\\x39\\x06\\x6e\\x6e\\xcd\\x22\\xc2\\x77\\xb9\\x36\\x5d\\xd4\\x06\\xdb\\xda\\xf6\\x64\\xde\\x38\\x42\\xbc\\x90\\x27\\xf2\\x76\\x4c\\xac\\xbb\\x86\\x72\\x44\\xd6\\x18\\xde\\xd3\\xb9\\xcb\\x9d\\xcc\\x1f\\xa5\\x83\\x9f\\xf2\\x5c\\x15\\x73\\x48\\x5f\\x81\\x4f\\x5f\\x75\\xce\\xa8\\xe1\\xcb\\x5b\\x1c\\x9b\\x45\\xac\\x57\\xde\\xcd\\xa3\\x5c\\x66\\xcd\\xd2\\x29\\x44\\xf8\\x8e\\x75\\x72\\x1c\\x0a\\xb5\\xc5\\x3e\\x92\\xa9\\x49\\xbc\\x68\\x0e\\x2a\\xe6\\x0c\\x38\\x5b\\x87\\x2e\\x2c\\xb1\\xce\\x86\\x6a\\xbb\\xe0\\xea\\x57\\x93\\x9f\\x2d\\x53\\xe3\\xa7\\x5c\\xcf\\xae\\x6c\\xb2\\x03\\xb3\\xb3\\x5a\\xea\\xa9\\xaf\\x51\\x9e\\x5d\\x35\\x06\\xdd\\xdb\\x33\\x9c\\x9c\\x53\\x3c\\xc2\\x6f\\xd8\\x86\\xfc\\x4e\\xf9\\x57\\x9c\\xfc\\x9f\\xd9\\xf9\\xb1\\xb4\\xbb\\xc2\\x6c\\x30\\xdb\\xcb\\xfb\\xf5\\xa5\\x5f\\x95\\x79\\x41\\xb9\\x67\\x2c\\x12\\xdc\\x9d\\xf0\\xce\\x2a\\x1d\\xfc\\xe4\\xdf\\xc8\\x5f\\x7c\\x97\\x72\\xf1\\x54\\x57\\xf3\\xef\\x64\\x17\\xbd\\x1e\\x37\\xe5\\x6c\\x12\\x61\\xb7\\x09\\xdb\\x60\\x5b\\xec\\x83\\x7d\\xe5\\x00\\x0c\\xe2\\x89\\xbc\\x51\\xc6\\x00\\x99\\x75\\x0b\\x6d\\x1b\\x3a\\x2e\\xb8\\x32\\x61\\xa4\\xf8\\xf1\\x37\\xe5\\xe9\\x35\\x06\\xa1\\x86\\x55\\x96\\x7e\\x5a\\xc0\\x28\\x9d\\x49\\x75\\xad\\xef\\x8d\\x1b\\x82\\xb5\\x76\\xd9\\xc0\\x7b\\x00\\x9b\\xb0\\xad\\xed\\xfb\\xa3\\xfa\\x15\\x69\\x2b\\x17\\x57\\x51\\x1d\\xca\\x88\\x95\\x8d\\xfe\\x57\\xde\\x9f\\x29\\x3e\\xc1\\xd5\\x6f\\x26\\xce\\x8f\\x24\\xc7\\x1f\\xf1\\x35\\x80\\x5e\\xf2\\x00\\xe8\\xbb\\xf3\\x21\\x72\\xf3\\x98\\x65\\xf7\\x1f\\x80\\xf0\\x8e\\x81\\x9e\\x1d\\x63\\xc8\\xc6\\x6d\\x7c\\x35\\x4b\\xa7\\x92\\xbd\\xdf\\xda\\x96\\x09\\x5d\\xdb\\x33\\xa1\\x6d\\x43\\x8e\\x73\\xbf\\x60\\xe7\\xf9\\xb1\\x0f\\xfa\\x48\\xdc\\x7b\\x89\\xff\\xcd\\x6a\\xe2\\x4d\\x79\\x65\\x5f\\xc3\\x40\\xbd\\xfd\\xee\\x5b\\xb6\\x0d\\x51\\x7e\\x3b\\xde\\x3a\\x4e\\x85\\xdf\\xec\\x38\\x44\\xb6\\x6f\\x94\\x3d\\x46\\xff\\x67\\xc0\\x27\\x70\\xf2\\x65\\xe2\\x7b\\xed\\xfd\\x9c\\x98\\xbc\\xe7\\x54\\xa9\\x5f\\x27\\x16\\x50\\x40\\xe7\\x2a\\x5c\\xdf\\x9c\\x4d\\xf7\\x32\\x76\\x3b\\xec\\x83\\x7d\\x03\\x9f\\xfc\\xa3\\xc4\\xe9\\x6f\\x27\\xde\\x28\\x03\\x65\\x0d\\xb0\\xa1\\x9a\\xff\\xb0\\xf1\\x6f\\xd3\\xb9\\x2b\\xee\\x9d\\x69\\x2a\\xfc\\xa4\\x6b\\x9c\\xdf\\xc3\\xcf\\x01\\x44\\x02\\x94\\xcb\\x35\\x2a\\x9f\\xa6\\xba\\xa6\\xb5\\x13\\x9c\\x7c\\x29\\xae\\x51\\x9f\\x47\\xe6\\x93\\xf4\\xc2\\x6c\\xd0\\x77\\x8f\\x73\\xe2\\xcd\\xcb\\xeb\\xf3\\x9d\\xfc\\x2a\\xf6\\x21\\x7e\\x87\\x9e\\x96\\x79\\xe1\\x48\\x80\\x78\\x63\\x1d\\xca\\x1a\\x20\\xfb\\x2a\\xb7\\xf7\\xb3\\x6a\\xc1\\x95\\xdc\\x91\\xe0\\x0f\\x5d\\x58\\x26\\xfd\\xe6\\xf1\\x97\\xa4\\xbe\\xc4\\x65\\xca\\x5d\\xf5\\xee\\x54\\x29\\xe7\\x6d\\xeb\\xb5\\x0d\\xf5\\xca\\x5d\\x60\\xec\\x9d\\x4a\\x77\\x30\\x91\\xda\\x37\\xc0\\x5f\\x3e\\x93\\xc6\\xd3\\xf9\\xc1\\x58\\xb2\\x2d\\x3b\\x4f\\xee\\xdb\\xa9\\x12\\x0f\\xe4\\x45\\xf3\\x79\\xfc\\x25\\x92\\x81\\xb2\\x06\\xd8\\xee\\xcd\\x13\\xf2\\xce\\x98\\x2b\\x4d\\x82\\xab\\x0f\\x26\\xc9\\x91\\x3e\\x97\\x08\\x3f\\xed\\x4f\\x3b\\x19\\x04\\x4f\\xbd\\x2e\\x79\\xf6\\xd4\\xd2\\xbe\\xd3\\xbd\\x7d\\x0c\\x9c\\xb5\\x6c\\x1b\\xb1\\xe1\\x7e\\xa5\\x7b\\x55\\x08\\x1d\\xfb\\x11\\x40\\xf3\\x5a\\x22\\xf3\\xf4\\xcf\\x69\\x2e\\xd0\\xff\\xa3\\xed\\x23\\x7e\\xec\\xd3\\xbd\\xdd\\x4d\\x31\\x74\\xa4\\xa7\\x4e\\xca\\x38\\xf5\\xba\\x94\\x81\\xfb\\x5b\\xac\\xef\\x10\\xcd\\x74\\xf7\\x64\\xdd\\x79\\xcf\\x4a\\x82\\xff\\x5b\\x42\\x63\\x86\\x71\\xf0\\xc9\\x21\\xb1\\x24\\xf9\\x4e\\xe4\\x5d\\x3b\\xdf\\xd2\\xc9\\x71\\xd0\\x8b\\xf2\\xa1\\x6b\\x5b\\x86\\xe3\\x63\\xb0\\xec\\xda\\x9e\\x41\\xf7\\x6f\\x66\\xcd\\xeb\\x00\\xcd\\x6b\\x00\\x9a\\x56\\x43\\xb4\\x7e\\x31\\x18\\x7b\\x27\\x83\\xcf\\x23\\xe7\\x0a\\xf1\\x3b\\x6d\\x8b\\xf2\\xe9\\xff\\x3a\\x24\\xa3\\x76\\xbe\\x94\\x31\\xc8\\x87\\x62\\x6c\\x4a\\x31\\x8b\\xc6\\xfa\\x04\\x57\\x9f\\x4e\\x82\\xff\\x69\\x6c\\x43\\xb1\\x88\\xff\\xfa\\xc8\\xf0\\x6f\\xcb\\x00\\x7d\\x57\\x16\\x98\\x67\\x7e\\xde\\x8f\\xff\\x42\\x01\\x18\\xc5\\x93\\x08\\xff\\xf9\\x91\\xe0\\x0f\\x76\\x03\\xea\\x14\\x75\\x4b\\x3a\\x4e\\x8c\\x7f\\x16\\xe5\\x96\\xf7\\xcd\\xa0\\x39\\x4b\\x6e\\x3f\\x75\\x34\\xf7\\x68\\x03\\xb6\\xfd\\xa0\\xef\\xb9\\xb1\\x85\\xee\\xd3\\x21\\x78\\xe4\\xcf\\xe8\\x0e\\x10\\xed\\x27\\x7c\\xe2\\x45\\xd0\\xbd\\x19\\xd0\\xbd\\x23\\x83\\xda\\x0e\\xdb\\x7e\\xc2\\x7d\\x74\\x67\\x81\\xb6\\x4d\\x36\\x9e\\x18\\xff\\x83\\xb8\\x46\\xf4\\xa2\\x09\\x74\\xdf\\x31\\x92\\xf5\\x7b\\xf5\\xb7\\xb9\\x96\\xef\\xc9\\x81\\x60\\xd5\\x73\\x10\\xaa\\xfe\\x3e\\xe8\\xc5\\xf7\\x82\\xce\\x15\\xba\\xd3\\xc2\\x31\\x9e\\xfa\\xb5\\xbd\\x7e\\x95\\xb4\\xd6\\xef\\x20\\xfc\\xdf\\x8e\\x8f\\x9f\\x72\\xeb\\xb9\\xe4\\xa3\\x70\\x5f\\xb9\\xca\\x07\\xf0\\x48\\xd7\\x7f\\x9e\\x5b\\x81\\x7b\\x53\\x46\\x7f\\xec\\x6c\\x91\\x4f\\xeb\\x8f\\x2f\\xb0\\x6d\\xe3\\x30\\xfc\\x67\\xba\\xf8\\xe5\\xde\\xa0\\x6e\\x47\\x1e\\xc1\\x9a\\xf9\\x03\\x78\\xa4\\xbb\\x7f\\x21\\x35\\xad\\x1b\\x0f\\xbd\\x9e\\x31\\xd6\\x3d\\xa6\\x0a\\x7d\\x9a\\x9b\\xe6\\x05\\x75\\x3f\\x64\\xff\\x3a\\xf9\\xb2\\xb5\\x46\\x13\\xef\\x5f\\xe9\\xe0\\xd7\\xed\\xbb\\x3e\\xdc\\xa3\\x51\\x2f\\xbf\\xfb\\xd3\\x81\\x71\\x48\\xdc\\xf8\\x61\\x6b\\xdc\\xf8\\xc1\\xde\\xc7\\x30\\x6e\\x40\\x9c\\xa8\\xeb\\x58\\xec\\xc3\\x89\\x1f\\xd2\\xc5\\x1f\\xb3\\x06\\xbe\\x29\\xb8\\xd2\\xa7\\xef\\xb9\\x0f\\x22\\x5d\\x9f\\x0e\\xe0\\xe1\\xc4\\x6f\\x17\\xd3\\x8b\\xdf\\x12\\xd1\\x70\\xe2\\xb7\\xe1\\xe1\\xa7\\x35\\x30\\x81\\x62\\x55\\xcd\\x45\\xe7\\xd0\\xd8\\xe7\\xb3\\x88\\x9f\\x87\\x85\\x5f\\x53\\x18\\xfd\\x3f\\x95\\xab\\x8b\\xe5\\x19\\xe3\\x71\\x3a\\x43\\x38\\x6b\\xe0\\xf7\\x76\\x7e\\xa9\\x8e\\x7b\\x7e\\x19\\x2e\\xfe\\x18\\x1b\\xc2\\xb3\\x5a\\x07\\x9e\\xdd\\xf0\\x0c\\xe7\\xf0\\xb9\\x23\\xe7\\xc7\\x9b\\xa3\\xc4\\x4f\\x7e\\xc8\\x8d\\x67\\x66\\x3a\\xa7\\xef\\x7f\\x88\\xce\\xd2\\xf6\\x93\\xf6\\xf9\\x7d\\x47\\xcc\\xf9\\x7d\\x47\\xb2\\xf3\\x7b\\x55\\xdc\\xf3\\xfb\\x48\\xf1\\xc7\\xcc\\xc1\\xd7\\x28\\x77\\x81\\x3e\\xae\\xfa\\xaf\\x29\\xa7\\x01\\x43\\xf2\\x27\\xcf\\x0f\\xcc\\x9f\\x9c\\xbb\\x3d\\xf9\\x93\\xd1\\xe2\\xef\\xe3\\x2e\\xe6\\xe3\\x4c\\x91\\x67\\x66\\x35\\x8a\\x7e\\x8e\\xd6\\xb2\\xa5\\xb3\\xd4\\xf9\\xab\\x17\\xc1\\x28\\x7d\\x04\\xf4\\x3d\\x13\\x89\\x8c\\x44\\xf9\\xab\\xba\\xb7\\x87\\xe6\\xaf\\x6e\\x03\\xfe\\x98\\x39\\x98\\x2c\\xb8\\x7a\\xc8\\xb6\\xd9\\x50\\xfd\\x32\\x99\\x1b\\x0c\\xf5\\x38\\xfe\\xe2\\xf6\\xe5\\x0f\\x13\\xe7\\x4e\\x47\\x84\\xbf\\x50\\xb5\\xfd\\xe9\\x1c\\x2b\\x97\\x4a\\xb9\\x55\\xfa\\xdf\\x50\\xe0\\x3a\\x44\\xba\\xcf\\xc8\\xdc\\x37\\xda\\x8a\\x9d\\xbf\\x4d\\x82\\xc1\\xc1\\x62\\xe5\\x6f\\xb1\\x0f\\xe5\\x7e\\x4b\\x1f\\xa5\\x5c\\x70\\xd2\\x3e\\x23\\xc0\\x4f\\x63\\xf0\\xba\\x58\\x8f\\x27\\x1b\\xe7\\xe1\\x05\\x99\\xd3\\x56\\x68\\xbe\\x8d\\xca\\x6f\\xd0\\xbe\\x19\\x6a\\x58\\xeb\\xac\\x3d\\x27\\x7f\\xde\\x56\\x92\\x7e\\xfe\\xbc\\xe4\\x41\\x30\\xdb\\xf6\\xa5\\x1e\\xf3\\x08\\xf1\\xe3\\xa3\\x7b\\x15\\x66\\xec\\xa2\\xff\\x6d\\xbc\\x60\\xdd\\x2d\\x48\\xbc\\xbb\\xb2\\xe8\\xbf\\x31\\x7a\\xf1\\xd4\\x24\\xf7\\x17\\xf3\\x89\\x12\\xdd\\x5f\\x18\\x15\\x4f\\xd1\\x1d\\x48\\xca\\x7b\\x92\\xda\\xb7\\xac\\xff\\xda\\xba\\x86\\x8d\\x9f\\xc6\\xc0\\x5d\\x0c\\xbc\\xe4\\x5b\\xd1\\x27\\x95\\x39\\x77\\x64\\x8e\\x8f\\x19\\xcd\\xfd\\xd1\\xb0\\xee\\xa9\\x46\\x84\\x1f\\x1f\\x43\\x73\\xd9\\x6b\\x7a\\xbc\\xbc\\x6b\\xa3\\x3b\\xb7\\x4e\\xeb\\x0e\\xee\\x4e\\xdd\\xf7\\x8d\\x18\\xbf\\x33\\x0e\\x8f\\xc2\\x7a\\x8b\\x54\\x8c\\x35\\x72\\x04\\x57\\x67\\x0b\\xae\\xfe\\xc4\\xba\\x0b\\x2d\\xb8\\x03\\xf7\\xae\\x6f\\x5b\\x77\\xbc\\x23\\xc6\\x7f\\xf7\\xb9\\xfb\\x7c\\x9e\\x1e\\xb9\\x43\\x24\\x2e\\x5b\\x18\\x63\\xcf\\x58\\x65\\x9e\\x55\\x66\\x5a\\xa5\\x6b\\x50\\xc9\\xec\\xb2\\xc0\\x2a\\x9f\\x19\\x54\\x4e\\x4f\\x50\\xe6\\x25\\x28\\x33\\x6f\\x5f\\xd9\\x93\\xa0\\x0c\\x24\\x28\\xcd\\x41\\x65\\xd4\\x2a\\xc1\\x2e\\x17\\x0e\\x2a\\x5b\\xac\\xb2\\xc7\\x2a\\x4d\\xab\\x4c\\xa1\\xdf\\xff\\x0f\\x00\\x00\\xff\\xff\\xc6\\xb9\\x24\\x2f\\xee\\x3a\\x00\\x00\")\n\nfunc faviconIcoBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_faviconIco,\n\t\t\"favicon.ico\",\n\t)\n}\n\nfunc faviconIco() (*asset, error) {\n\tbytes, err := faviconIcoBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"favicon.ico\", size: 15086, mode: os.FileMode(438), modTime: time.Unix(1612640817, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\nvar _jsMainJs = []byte(\"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\x4a\\xce\\xcf\\x2b\\xce\\xcf\\x49\\xd5\\xcb\\xc9\\x4f\\xd7\\x50\\x4a\\xad\\x48\\xcc\\x2d\\xc8\\x49\\x55\\xd2\\xb4\\x06\\x04\\x00\\x00\\xff\\xff\\xc8\\x9f\\xbd\\x5f\\x17\\x00\\x00\\x00\")\n\nfunc jsMainJsBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_jsMainJs,\n\t\t\"js/main.js\",\n\t)\n}\n\nfunc jsMainJs() (*asset, error) {\n\tbytes, err := jsMainJsBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"js/main.js\", size: 23, mode: os.FileMode(438), modTime: time.Unix(1613718745, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\n// Asset loads and returns the asset for the given name.\n// It returns an error if the asset could not be found or\n// could not be loaded.\nfunc Asset(name string) ([]byte, error) {\n\tcannonicalName := strings.Replace(name, \"\\\\\", \"/\", -1)\n\tif f, ok := _bindata[cannonicalName]; ok {\n\t\ta, err := f()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"Asset %s can't read by error: %v\", name, err)\n\t\t}\n\t\treturn a.bytes, nil\n\t}\n\treturn nil, fmt.Errorf(\"Asset %s not found\", name)\n}\n\n// MustAsset is like Asset but panics when Asset would return an error.\n// It simplifies safe initialization of global variables.\nfunc MustAsset(name string) []byte {\n\ta, err := Asset(name)\n\tif err != nil {\n\t\tpanic(\"asset: Asset(\" + name + \"): \" + err.Error())\n\t}\n\n\treturn a\n}\n\n// AssetInfo loads and returns the asset info for the given name.\n// It returns an error if the asset could not be found or\n// could not be loaded.\nfunc AssetInfo(name string) (os.FileInfo, error) {\n\tcannonicalName := strings.Replace(name, \"\\\\\", \"/\", -1)\n\tif f, ok := _bindata[cannonicalName]; ok {\n\t\ta, err := f()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"AssetInfo %s can't read by error: %v\", name, err)\n\t\t}\n\t\treturn a.info, nil\n\t}\n\treturn nil, fmt.Errorf(\"AssetInfo %s not found\", name)\n}\n\n// AssetNames returns the names of the assets.\nfunc AssetNames() []string {\n\tnames := make([]string, 0, len(_bindata))\n\tfor name := range _bindata {\n\t\tnames = append(names, name)\n\t}\n\treturn names\n}\n\n// _bindata is a table, holding each asset generator, mapped to its name.\nvar _bindata = map[string]func() (*asset, error){\n\t\"css/main.css\": cssMainCss,\n\t\"favicon.ico\":  faviconIco,\n\t\"js/main.js\":   jsMainJs,\n}\n\n// AssetDir returns the file names below a certain\n// directory embedded in the file by go-bindata.\n// For example if you run go-bindata on data/... and data contains the\n// following hierarchy:\n//\n//\tdata/\n//\t  foo.txt\n//\t  img/\n//\t    a.png\n//\t    b.png\n//\n// then AssetDir(\"data\") would return []string{\"foo.txt\", \"img\"}\n// AssetDir(\"data/img\") would return []string{\"a.png\", \"b.png\"}\n// AssetDir(\"foo.txt\") and AssetDir(\"notexist\") would return an error\n// AssetDir(\"\") will return []string{\"data\"}.\nfunc AssetDir(name string) ([]string, error) {\n\tnode := _bintree\n\tif len(name) != 0 {\n\t\tcannonicalName := strings.Replace(name, \"\\\\\", \"/\", -1)\n\t\tpathList := strings.Split(cannonicalName, \"/\")\n\t\tfor _, p := range pathList {\n\t\t\tnode = node.Children[p]\n\t\t\tif node == nil {\n\t\t\t\treturn nil, fmt.Errorf(\"Asset %s not found\", name)\n\t\t\t}\n\t\t}\n\t}\n\tif node.Func != nil {\n\t\treturn nil, fmt.Errorf(\"Asset %s not found\", name)\n\t}\n\trv := make([]string, 0, len(node.Children))\n\tfor childName := range node.Children {\n\t\trv = append(rv, childName)\n\t}\n\treturn rv, nil\n}\n\ntype bintree struct {\n\tFunc     func() (*asset, error)\n\tChildren map[string]*bintree\n}\n\nvar _bintree = &bintree{nil, map[string]*bintree{\n\t\"css\": {nil, map[string]*bintree{\n\t\t\"main.css\": {cssMainCss, map[string]*bintree{}},\n\t}},\n\t\"favicon.ico\": {faviconIco, map[string]*bintree{}},\n\t\"js\": {nil, map[string]*bintree{\n\t\t\"main.js\": {jsMainJs, map[string]*bintree{}},\n\t}},\n}}\n\n// RestoreAsset restores an asset under the given directory\nfunc RestoreAsset(dir, name string) error {\n\tdata, err := Asset(name)\n\tif err != nil {\n\t\treturn err\n\t}\n\tinfo, err := AssetInfo(name)\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = os.WriteFile(_filePath(dir, name), data, info.Mode())\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// RestoreAssets restores an asset under the given directory recursively\nfunc RestoreAssets(dir, name string) error {\n\tchildren, err := AssetDir(name)\n\t// File\n\tif err != nil {\n\t\treturn RestoreAsset(dir, name)\n\t}\n\t// Dir\n\tfor _, child := range children {\n\t\terr = RestoreAssets(dir, filepath.Join(name, child))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc _filePath(dir, name string) string {\n\tcannonicalName := strings.Replace(name, \"\\\\\", \"/\", -1)\n\treturn filepath.Join(append([]string{dir}, strings.Split(cannonicalName, \"/\")...)...)\n}\n"
  },
  {
    "path": "_examples/file-server/embedding-files-into-app-bindata/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n)\n\n// Follow these steps first:\n// $ go install github.com/go-bindata/go-bindata/v3/go-bindata@latest\n// $ go-bindata -prefix \"assets\" -fs ./assets/...\n// $ go run .\n// \"physical\" files are not used, you can delete the \"assets\" folder and run the example.\n//\n// See `file-server/embedding-gzipped-files-into-app-bindata` and\n// 'file-server/embedding-files-into-app` examples as well.\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\tapp.Logger().SetLevel(\"debug\")\n\n\tapp.HandleDir(\"/static\", AssetFile())\n\n\t/*\n\t\tOr if you need to cache them inside the memory (requires the assets folder\n\t\tto be located near the executable program):\n\t\tapp.HandleDir(\"/static\", iris.Dir(\"./assets\"), iris.DirOptions{\n\t\t\tIndexName: \"index.html\",\n\t\t\tCache: iris.DirCacheOptions{\n\t\t\t\tEnable:          true,\n\t\t\t\tEncodings:       []string{\"gzip\"},\n\t\t\t\tCompressIgnore:  iris.MatchImagesAssets,\n\t\t\t\tCompressMinSize: 30 * iris.B,\n\t\t\t},\n\t\t})\n\t*/\n\treturn app\n}\n\nfunc main() {\n\tapp := newApp()\n\n\t// http://localhost:8080/static/css/main.css\n\t// http://localhost:8080/static/js/main.js\n\t// http://localhost:8080/static/favicon.ico\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/file-server/embedding-files-into-app-bindata/main_test.go",
    "content": "package main\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\ntype resource string\n\n// content types that are used in the ./assets,\n// we could use the detectContentType that iris do but it's better\n// to do it manually so we can test if that returns the correct result on embedding files.\nfunc (r resource) contentType() string {\n\tswitch filepath.Ext(r.String()) {\n\tcase \".js\":\n\t\treturn \"text/javascript\"\n\tcase \".css\":\n\t\treturn \"text/css\"\n\tcase \".ico\":\n\t\treturn \"image/x-icon\"\n\tcase \".html\":\n\t\treturn \"text/html\"\n\tdefault:\n\t\treturn \"text/plain\"\n\t}\n}\n\nfunc (r resource) String() string {\n\treturn string(r)\n}\n\nfunc (r resource) strip(strip string) string {\n\ts := r.String()\n\treturn strings.TrimPrefix(s, strip)\n}\n\nfunc (r resource) loadFromBase(dir string) string {\n\tfilename := r.strip(\"/static\")\n\n\tfullpath := filepath.Join(dir, filename)\n\n\tb, err := os.ReadFile(fullpath)\n\tif err != nil {\n\t\tpanic(fullpath + \" failed with error: \" + err.Error())\n\t}\n\n\tresult := string(b)\n\tif runtime.GOOS != \"windows\" {\n\t\tresult = strings.ReplaceAll(result, \"\\n\", \"\\r\\n\")\n\t\tresult = strings.ReplaceAll(result, \"\\r\\r\", \"\")\n\t}\n\n\treturn result\n}\n\nvar urls = []resource{\n\t\"/static/css/main.css\",\n\t\"/static/js/main.js\",\n\t\"/static/favicon.ico\",\n}\n\n// if bindata's values matches with the assets/... contents\n// and secondly if the HandleDir had successfully registered\n// the routes and gave the correct response.\nfunc TestEmbeddingFilesIntoApp(t *testing.T) {\n\tapp := newApp()\n\te := httptest.New(t, app)\n\n\troute := app.GetRouteReadOnly(\"GET/static/{file:path}\")\n\tif route == nil {\n\t\tt.Fatalf(\"expected a route to serve embedded files\")\n\t}\n\n\tif runtime.GOOS != \"windows\" {\n\t\t// remove the embedded static favicon for !windows,\n\t\t// it should be built for unix-specific in order to be work\n\t\turls = urls[0 : len(urls)-1]\n\t}\n\n\tfor _, u := range urls {\n\t\turl := u.String()\n\t\tcontents := u.loadFromBase(\"./assets\")\n\n\t\te.GET(url).Expect().\n\t\t\tStatus(httptest.StatusOK).\n\t\t\tContentType(u.contentType()).\n\t\t\tBody().IsEqual(contents)\n\t}\n}\n"
  },
  {
    "path": "_examples/file-server/embedding-gzipped-files-into-app-bindata/bindata.go",
    "content": "// Code generated by go-bindata. (@generated) DO NOT EDIT.\n\n// Package main generated by go-bindata.// sources:\n// ../embedding-files-into-app-bindata/assets/css/main.css\n// ../embedding-files-into-app-bindata/assets/favicon.ico\n// ../embedding-files-into-app-bindata/assets/js/main.js\npackage main\n\nimport (\n\t\"bytes\"\n\t\"compress/gzip\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"time\"\n)\n\nfunc bindataRead(data []byte, name string) ([]byte, error) {\n\tgz, err := gzip.NewReader(bytes.NewBuffer(data))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"read %q: %v\", name, err)\n\t}\n\n\tvar buf bytes.Buffer\n\t_, err = io.Copy(&buf, gz)\n\tclErr := gz.Close()\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"read %q: %v\", name, err)\n\t}\n\tif clErr != nil {\n\t\treturn nil, err\n\t}\n\n\treturn buf.Bytes(), nil\n}\n\ntype asset struct {\n\tbytes []byte\n\tinfo  os.FileInfo\n}\n\ntype bindataFileInfo struct {\n\tname    string\n\tsize    int64\n\tmode    os.FileMode\n\tmodTime time.Time\n}\n\n// Name return file name\nfunc (fi bindataFileInfo) Name() string {\n\treturn fi.name\n}\n\n// Size return file size\nfunc (fi bindataFileInfo) Size() int64 {\n\treturn fi.size\n}\n\n// Mode return file mode\nfunc (fi bindataFileInfo) Mode() os.FileMode {\n\treturn fi.mode\n}\n\n// ModTime return file modify time\nfunc (fi bindataFileInfo) ModTime() time.Time {\n\treturn fi.modTime\n}\n\n// IsDir return file whether a directory\nfunc (fi bindataFileInfo) IsDir() bool {\n\treturn fi.mode&os.ModeDir != 0\n}\n\n// Sys return file is sys mode\nfunc (fi bindataFileInfo) Sys() any {\n\treturn nil\n}\n\ntype assetFile struct {\n\t*bytes.Reader\n\tname            string\n\tchildInfos      []os.FileInfo\n\tchildInfoOffset int\n}\n\ntype assetOperator struct{}\n\n// Open implement http.FileSystem interface\nfunc (f *assetOperator) Open(name string) (http.File, error) {\n\tvar err error\n\tif len(name) > 0 && name[0] == '/' {\n\t\tname = name[1:]\n\t}\n\tcontent, err := Asset(name)\n\tif err == nil {\n\t\treturn &assetFile{name: name, Reader: bytes.NewReader(content)}, nil\n\t}\n\tchildren, err := AssetDir(name)\n\tif err == nil {\n\t\tchildInfos := make([]os.FileInfo, 0, len(children))\n\t\tfor _, child := range children {\n\t\t\tchildPath := filepath.Join(name, child)\n\t\t\tinfo, errInfo := AssetInfo(filepath.Join(name, child))\n\t\t\tif errInfo == nil {\n\t\t\t\tchildInfos = append(childInfos, info)\n\t\t\t} else {\n\t\t\t\tchildInfos = append(childInfos, newDirFileInfo(childPath))\n\t\t\t}\n\t\t}\n\t\treturn &assetFile{name: name, childInfos: childInfos}, nil\n\t} else {\n\t\t// If the error is not found, return an error that will\n\t\t// result in a 404 error. Otherwise the server returns\n\t\t// a 500 error for files not found.\n\t\tif strings.Contains(err.Error(), \"not found\") {\n\t\t\treturn nil, os.ErrNotExist\n\t\t}\n\t\treturn nil, err\n\t}\n}\n\n// Close no need do anything\nfunc (f *assetFile) Close() error {\n\treturn nil\n}\n\n// Readdir read dir's children file info\nfunc (f *assetFile) Readdir(count int) ([]os.FileInfo, error) {\n\tif len(f.childInfos) == 0 {\n\t\treturn nil, os.ErrNotExist\n\t}\n\tif count <= 0 {\n\t\treturn f.childInfos, nil\n\t}\n\tif f.childInfoOffset+count > len(f.childInfos) {\n\t\tcount = len(f.childInfos) - f.childInfoOffset\n\t}\n\toffset := f.childInfoOffset\n\tf.childInfoOffset += count\n\treturn f.childInfos[offset : offset+count], nil\n}\n\n// Stat read file info from asset item\nfunc (f *assetFile) Stat() (os.FileInfo, error) {\n\tif len(f.childInfos) != 0 {\n\t\treturn newDirFileInfo(f.name), nil\n\t}\n\treturn AssetInfo(f.name)\n}\n\n// newDirFileInfo return default dir file info\nfunc newDirFileInfo(name string) os.FileInfo {\n\treturn &bindataFileInfo{\n\t\tname:    name,\n\t\tsize:    0,\n\t\tmode:    os.FileMode(2147484068), // equal os.FileMode(0644)|os.ModeDir\n\t\tmodTime: time.Time{}}\n}\n\n// AssetFile return a http.FileSystem instance that data backend by asset\nfunc AssetFile() http.FileSystem {\n\treturn &assetOperator{}\n}\n\nvar _cssMainCss = []byte(\"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\xca\\x28\\xc9\\xcd\\x51\\xa8\\xe6\\xe5\\xe2\\x4c\\xcb\\xcf\\x2b\\xd1\\x4d\\x4b\\xcc\\xcd\\xcc\\xa9\\xb4\\x52\\x28\\x4e\\xcc\\x2b\\xd6\\x2d\\x4e\\x2d\\xca\\x4c\\xb3\\xe6\\xe5\\xe2\\xd4\\x2d\\x4f\\x4d\\xca\\xce\\x2c\\xd1\\x2d\\x49\\xad\\x28\\xd1\\x2d\\xce\\xac\\x4a\\xd5\\x4d\\x4c\\xc9\\x2a\\x2d\\x2e\\xb1\\x52\\x30\\x34\\x30\\x50\\x05\\xab\\xc8\\x2d\\xc6\\x21\\xcb\\xcb\\x55\\x0b\\x08\\x00\\x00\\xff\\xff\\x32\\x4c\\x06\\xc6\\x63\\x00\\x00\\x00\")\n\nfunc cssMainCssBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_cssMainCss,\n\t\t\"css/main.css\",\n\t)\n}\n\nfunc cssMainCss() (*asset, error) {\n\tbytes, err := cssMainCssBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"css/main.css\", size: 99, mode: os.FileMode(438), modTime: time.Unix(1613718750, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\nvar _faviconIco = []byte(\"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\xec\\x5a\\x0b\\x70\\x55\\xc7\\x79\\xde\\x7b\\xce\\x45\\x12\\xb2\\x90\\xc4\\xc3\\xe6\\x61\\x3b\\x90\\xf8\\x31\\xc4\\x19\\x6c\\x32\\xe3\\xc4\\x34\\xe3\\xc6\\x34\\x6d\\xed\\xd4\\x69\\x62\\x32\\x49\\x9a\\xa6\\x75\\xea\\x36\\x33\\xee\\xd8\\x9e\\xb4\\x75\\xdc\\x7a\\xa6\\xc5\\x31\\x02\\xa6\\xd3\\x84\\x47\\x28\\x6f\\x3b\\xc6\\x3c\\xcc\\xeb\\x9e\\xbd\\x08\\x21\\x04\\x92\\x28\\x12\\x0e\\x08\\x5b\\x3c\\xec\\x02\\x92\\x78\\x08\\x21\\x09\\x41\\x25\\x24\\x10\\xe8\\x71\\xb5\\xe7\\xdc\\xd7\\xb9\\xf7\\xef\\xfc\\xff\\x9e\\x73\\x74\\x25\\xdd\\x97\\x24\\x82\\x33\\x1e\\xce\\xcc\\x3f\\x7b\\xee\\x9e\\xdd\\xff\\xff\\xf6\\xdf\\x7f\\xff\\xfd\\xf7\\xdf\\xcb\\x98\\x8b\\xa9\\x2c\\x3f\\x1f\\xcb\\x19\\xec\\x15\\x37\\x63\\x5f\\x67\\x8c\\xcd\\x98\\x21\\x7f\\x6b\\xf9\\x8c\\x6d\\x72\\x33\\x36\\x7b\\xb6\\xf5\\xfb\\x11\\xc6\\x9e\\xbd\\x97\\xb1\\x99\\x8c\\xb1\\x7c\\x6c\\xc7\\x64\\x3d\\x3d\\x6e\\x76\\xdb\\x1f\\xe1\\x75\\xab\\x42\\x53\\x7e\\x22\\x3c\\x6c\\x91\\xf0\\xb0\\x02\\x22\\xae\\x14\\x08\\x8f\\xab\\x40\\xec\\x64\\x92\\xf0\\x5d\\x53\\xfe\\x59\\xec\\x64\\x5f\\x15\\x1e\\xa6\\x08\\x4f\\x7f\\x7f\\xdf\\x7a\\x96\\xa1\\x97\\x3c\\xb8\\x3f\\x78\\xfa\\x0d\\x08\\x9e\\x5d\\x08\\xc1\\xda\\x5f\\x82\\x51\\xf6\\x65\\x30\\xca\\x67\\x41\\xf0\\xcc\\xbf\\x11\\x19\\x07\\x1e\\x07\\xbd\\x78\\x2a\\x18\\x65\\x8f\\xb5\\x0b\\x4d\\x7d\\xad\\x6f\\x07\\x73\\x0b\\xcd\\x45\\xfd\\xbb\\x17\\xb3\\x0c\\xa3\\x62\\xce\\xfe\\xa8\\xd1\\x0a\\xf8\\x98\\x6d\\x25\\x10\\x38\\xfe\\x53\\x88\\xdc\\x3a\\x01\\x66\\x6b\\x11\\x11\\xbe\\x07\\x8e\\xfd\\x2d\\x84\\x2e\\x2c\\x05\\xff\\xd1\\x79\\x3d\\xc2\\xc3\\x7e\\x20\\x78\\x06\\x13\\x5c\\x89\\xe9\\xdf\\x06\\xd1\\x50\\x2f\\x04\\x4e\\xfc\\x0c\\xcc\\x6b\\xa5\\x60\\x1c\\xfd\\x21\\x74\\x6c\\x1e\\x47\\x84\\xef\\x58\\x17\\xf8\\xe4\\x65\\x30\\x6f\\x54\\x81\\x71\\xe0\\x89\\xea\\xbe\\x2d\\x6c\\xd2\\x80\\xfe\\xfe\\x76\\x30\\x6f\\x1c\\x81\\x60\\xed\\x7c\\x08\\x9e\\x5d\\x0c\\x2d\\xeb\\xc7\\x41\\xed\\xd2\\x29\\x50\\xbb\\x74\\x32\\xbd\\x63\\x1d\\x7e\\xc3\\x36\\xa1\\x73\\xff\\x19\\x14\\x1e\\x36\\x4f\\x78\\xc7\\xc4\\xf4\\xef\\x80\\x70\\xd3\\xfb\\x10\\x6e\\x5c\\x0f\\xdd\\x65\\x73\\xe1\\xdc\\xf2\\x89\\xd0\\xb1\\x25\\x0f\\x6e\\x7c\\x90\\x07\\xe7\\x7f\\x33\\x81\\xea\\xf0\\x5b\\xb8\\x79\\x13\\x61\\xd0\\x8b\\xa7\\x2c\\x09\\x7c\\xfc\\x57\\xac\\x7b\\x11\\xf6\\x7f\\x6a\\x5f\\xd4\\xb8\\x06\\xa1\\xfa\\x15\\x10\\x6e\\x7a\\x0f\\x3a\\xb4\\xc7\\xa0\\x61\\x55\\x3e\\x04\\x8f\\xfc\\x39\\x84\\x3e\\xfa\\x4b\\x68\\x5c\\x93\\x4f\\x75\\xf8\\x2d\\x74\\x71\\x05\\x44\\x45\\x13\\xea\\x54\\xeb\\x7c\\x85\\xa9\\x48\\xf8\\x1e\\x15\\xcd\\xf4\\xcd\\xee\\x7f\\x71\\x65\\x3e\\x04\\x3e\\x7c\\x86\\x78\\x5c\\x5a\\x1d\\xd3\\xbf\\x7e\\x05\\xa0\\x2c\\x94\\x89\\xb2\\x03\\xd5\\x3f\\x66\\x88\\x05\\x31\\x21\\x36\\x1b\\xff\\xd9\\xe5\\x13\\xe1\\xda\\xc6\\x5c\\x68\\xdf\\x94\\x4b\\x63\\x71\\xf0\\x37\\xbd\\x0f\\x38\\x56\\x1c\\x33\\x8e\\x1d\\x75\\x80\\xba\\x40\\x9d\\x0c\\xd6\\x5f\\xcd\\x92\\x29\\x44\\x83\\xf5\\x87\\xba\\x76\\xfa\\x73\\x85\\xe1\\x5c\\x18\\x07\\x9e\\x38\\x86\\x18\\x68\\x8e\\xac\\xf9\\xbb\\xbe\\x39\\x87\\xc8\\x99\\xbf\\x13\\x3f\\xa3\\x39\\xc6\\xb9\\x8e\\xed\\x4f\\xb6\\xe0\\x61\\x3f\\x40\\xdb\\x40\\x1b\\x41\\x5b\\x19\\x62\\x3f\\xc7\\x7f\\x4a\\xb6\\x85\\x0f\\xda\\x9a\\xdd\\x9f\\xd6\\x80\\xe6\\x62\\xd2\\x26\\xd5\\xd7\\xd0\\x46\\xc9\\x56\\x0f\\x3c\\xde\\x6f\\xbf\\xe5\\xb3\\xc8\\xa6\\xd1\\xb6\\xc9\\xc6\\x4f\\xbf\\x01\\x68\\xf3\\x68\\xfb\\xce\\x3a\\xf2\\x30\\x26\\x76\\x32\\x85\\xd6\\x08\\xae\\x95\\xc1\\xeb\\x87\\xd6\\x14\\xb3\\x69\\x11\\xad\\x39\\xaf\\x5b\\x1d\\xed\\xfa\\x05\\x60\\x8c\\x65\\x32\\xc6\\x54\\x8b\\x5c\\x31\\x64\\x3d\\x0b\\x63\\xe8\\xb0\\x45\\x2d\\x56\\xdf\\x99\\x96\\x8f\\x99\\x1b\\xeb\\x67\\xf2\\x47\\x8b\\xea\\xf3\\xf9\\x08\\xae\\x22\\xe5\\x09\\xae\\x4e\\x17\\x5c\\xfd\\xc2\\x28\\x69\\x8a\\xe0\\xca\\x58\\xb4\\x3d\\xdb\\x17\\xa6\\x29\\xff\\x5f\\x04\\x57\\x5b\\x85\\xe6\\xba\\x92\\x90\\xb8\\x1a\\x43\\x4a\\x4c\\xbd\\x62\\xd7\\xb7\\x08\\xae\\xd6\\x0b\\xae\\xfe\\x8f\\xe0\\xea\\x9b\\x38\\x1e\\x1f\\x1f\\x4b\\xfe\\x34\\xb5\\x7c\\xa5\\x40\\xec\\xca\\x02\\xbd\\x64\\x3a\\xe8\\xfb\\xbe\\x38\\x94\\x4a\\xa6\\x83\\xf0\\x66\\x82\\xd0\\x14\\x10\\x9a\\x0b\\x44\\xe1\\x38\\x5c\\x2b\\x44\\xf8\\x4e\\x75\\x5c\\x01\\xe1\\x75\\x03\\xf2\\x11\\x9a\\x2b\\x2a\\xb8\\x52\\x2b\\xb8\\x3a\\x4f\\x68\\x8a\\x92\\x0c\\x03\\xc9\\xf7\\xb0\\x02\\xa3\\xfc\\x2b\\x10\\xe9\\xae\\x85\\xa8\\x7e\\x15\\xa2\\xfa\\x95\\x18\\xba\\x0a\\xa1\\xb3\\x8b\\x49\\xbe\\xbe\\xe7\\x5e\\x08\\xfe\\xef\\x3f\\x81\\xd9\\x51\\x09\\x91\\xbe\\x4b\\x44\\xf8\\x8e\\x75\\xf8\\x4d\\x70\\x37\\x04\\x3e\\xfe\\x11\\xf9\\x14\\xbd\\x68\\x3c\\xe2\\xe8\\x14\\x5c\\x7d\\x49\\x78\\x99\\x2b\\x11\\x06\\x47\\xfe\\x81\\xd9\\x10\\x0d\\xde\\x82\\xc1\\x8f\\x79\\xe3\\x30\\xe8\\x7b\\xa7\\x81\\x51\\xfa\\x28\\x98\\xd7\\xca\\x00\\xa2\\xa6\\xfc\\x80\\x65\\xcc\\x3b\\x7e\\xc3\\x36\\xfa\\xde\\x07\\x08\\x93\\xd9\\x5a\\x0c\\xfa\\xfe\\x87\\x41\\x68\\xac\\x43\\x70\\xf5\\x59\\x92\\xe3\\x1d\\xea\\x1a\\x92\\xc9\\x8f\\x06\\x3a\\xc1\\xff\\xe1\\x5c\\xda\\x67\\x91\\x27\\x3e\\x91\\xde\\x0b\\xe4\\xaf\\xfc\\x47\\x5f\\x20\\xc2\\x77\\xac\\x23\\xac\\x1d\\x95\\xd4\\xd6\\xff\\xe1\\x9f\\x10\\x2f\\xfa\\x5d\\xf2\\x05\\xc4\\x70\\x4c\\x70\\xf5\\x7e\\x94\\x35\\x1c\\xf9\\xa1\\xc6\\x77\\x68\\x3e\\x43\\x17\\x57\\x4a\\xfe\\xe8\\xc3\\xcb\\xbe\\x8c\\xfc\\xa0\\xcf\\x23\\x09\\xdf\\xb1\\x0e\\xbf\\x51\\x9f\\x8b\\x2b\\x65\\x9f\\xc6\\x77\\xe8\\x37\\xee\\x4f\\x62\\xd7\\x58\\xb4\\x8f\\x05\\x3e\\x9e\\x35\\xc4\\x1e\\x13\\xc9\\x8f\\x06\\xbb\\xc0\\xa8\\xfc\\x23\\xf0\\x1f\\xfa\\x63\\x88\\x86\\xba\\x21\\x72\\xeb\\x24\\x18\\xfb\\x1f\\x82\\xde\\x1d\\x2e\\x68\\xdd\\x30\\x0e\\x1a\\x56\\x4f\\x24\\xc2\\x77\\xac\\xc3\\x6f\\xd8\\x06\\xdb\\x62\\x1f\\xec\\x8b\\xef\\x10\\xd6\\xc1\\xff\\xd1\\xf7\\x11\\x67\\x83\\xe0\\xea\\x97\\x06\\xeb\\x20\\x91\\x7c\\xd2\\xdd\\xee\\x3c\\x08\\x37\\x6f\\x04\\x30\\x0d\\x08\\x7c\\x34\\x8f\\xe4\\x34\\xae\\x99\\x00\\xa7\\x7f\\x3d\\x0d\\x4e\\xfd\\xea\\x7e\\x22\\x7c\\xc7\\x3a\\xfc\\x86\\x6d\\xb0\\x6d\\xb8\\x69\\x23\\xf5\\xb5\\xe7\\xcc\\x6c\\x3f\\x00\\x62\\x77\\x6e\\x54\\x68\\xae\\x57\\xad\\xf5\\x96\\x52\\x7e\\xb0\\x6e\\x01\\xe8\\xfb\\x66\\xd0\\x1a\\x30\\xaf\\x95\\x83\\x28\\xcc\\x81\\x2b\\xef\\xe6\\x92\\x3c\\x24\\x8a\\x63\\x96\\x4d\\x71\\x7e\\xe3\\x37\\x6c\\x83\\x6d\\xb1\\x0f\\xf6\\x0d\\xd6\\x15\\x48\\x5d\\x86\\x7c\\x64\\x47\\xc2\\xc3\\xf6\\x0a\\xae\\x66\\xc6\\xea\\x20\\xae\\xfc\\x48\\x08\\xfc\\x55\\xdf\\x05\\x7f\\xd5\\x77\\x00\\x22\\x41\\x08\\x7e\\xfa\\x2a\\x74\\x6d\\x53\\xa1\\x6e\\xf9\\x64\\x92\\x75\\x69\\xcd\\x44\\xb8\\xb9\\x35\\x13\\x6e\\x6d\\xcb\\x80\\xe6\\x75\\xe3\\xa9\\x0e\\xbf\\x61\\x1b\\x6c\\x8b\\x7d\\xb0\\xaf\\xbf\\xea\\x7b\\xc4\\x8b\\xec\\xe2\\xec\\x22\\x9c\\x83\\x66\\xc1\\xd5\\x19\\xa9\\xe4\\xd3\\xdc\\x1f\\x98\\x0d\\xc1\\x33\\x6f\\x42\\x34\\xec\\x03\\x7f\\xe5\\x1c\\x68\\xdb\\x30\\x96\\xe4\\x9c\\xfb\\xcd\\x7d\\xd0\\xbd\\x63\\x0c\\xe8\\x85\\x63\\x41\\x2f\\xbc\\x07\\x7a\\x3d\\x6e\\xa8\\x5f\\x39\\x89\\xbe\\x61\\x1b\\x6c\\x8b\\x7d\\xb0\\xaf\\xe4\\xd9\\x65\\xd9\\x6e\\x19\\x88\\x5d\\xd9\\xba\\xd0\\x5c\\x73\\x53\\xca\\xd7\\xff\\x0f\\xf4\\xfd\\x0f\\x41\\xa8\\x61\\x35\\xf9\\x1f\\x7d\\xdf\\x74\\x68\\x5e\\x97\\x47\\xf3\\xdd\\xf2\\x4e\\x1e\\xc9\\x0d\\x9f\\xfc\\x3b\\x30\\x4f\\xbd\\x02\\xc6\\x9e\\x09\\xd0\\xba\\x21\\x07\\x4e\\xfd\\x6a\\x1a\\xb5\\xc1\\xb6\\xe4\\xb3\\x1a\\x56\\x13\\x0f\\xe4\\x25\\xd7\\xed\\x39\\x5c\\x9b\\x51\\xa1\\xb1\\x17\\xe3\\xc8\\x7f\\x0b\\x63\\x13\\x5c\\xef\\xd4\\xb6\\xaf\\x91\\x7c\\x6b\\xf8\\xf2\\x16\\x5a\\xdb\\x62\\xcf\\x64\\xb8\\xb4\\x7a\\x3c\\xc9\\x68\\xdb\\x90\\x0d\\xfe\\xb2\\x87\\x01\\x2e\\x2d\\x07\\x68\\x5a\\x05\\x81\\xca\\x27\\xe1\\xfa\\xe6\\x4c\\x39\\x2f\\xab\\xc7\\x53\\xdb\\x88\\xaf\\x9e\\xfa\\x22\\x0f\\xe4\\xe5\\xc4\\x47\\xa5\\x8f\\xa2\\x0d\\xfc\\x22\\x8e\\xfc\\x5f\\xe0\\x37\\x3b\\x5e\\x4f\\x2e\\xff\\x1e\\xf0\\x97\\x7e\\x11\\xa2\\x0d\\x4b\\x00\\x9a\\x56\\x42\\xe0\\xe0\\x13\\x70\\x7d\\x53\\x56\\x6a\\xf9\\x81\\x4e\\x8a\\xbf\\x70\\xac\\x43\\xe4\\x6b\\xec\\x45\\xd4\\x0d\\xea\\x28\\xbe\\xfe\\x67\\x38\\xfa\\x6f\\x5e\\x97\\x2f\\xfd\\xcb\\xd1\\xe7\\x21\\x74\\xec\\x87\\xa0\\xef\\xce\\x81\\xab\\xbf\\x1d\\x67\\x7d\\xcb\\xb3\\xd6\\xcc\\x50\\xfd\\xe3\\xdc\\xe2\\x1c\\xcb\\xb3\\xd0\\x60\\xf9\\xae\\xb9\\x68\\x1b\\xe4\\xdf\\x53\\xd8\\x1f\\xda\\x39\\xda\\xbe\\xee\\x55\\x41\\xf7\\xba\\xc9\\x16\\xcf\\xaf\\xb8\\x2f\\xa5\\xfd\\x25\\x95\\x8f\\x6b\\x42\\x63\\xcd\\xb8\\xcf\\x25\\x5c\\x7f\\x5b\\xe5\\xfa\\xc3\\x39\\xb8\\xf0\\xdf\\xf7\\x42\\xfb\\xc6\\x7b\\xa0\\x63\\x53\\x36\\x5c\\x5c\\x35\\x29\\xad\\xf5\\x97\\x42\\x7e\\x26\\xfa\\x06\\xda\\x37\\xc2\\xbe\\xb4\\xfc\\x0f\\xd2\\x99\\x25\\xd3\\xd2\\xf2\\x3f\\xc9\\xe5\\x2b\\xf6\\x1c\\xbc\\x86\\x3e\\x12\\x7d\\xe5\\x50\\xff\\xeb\\x8f\\xe3\\x7f\\x25\\x0d\\xf5\\xbf\\xfe\\x21\\xfe\\x37\\x99\\xfc\\x18\\x1d\\x7c\\x09\\xf7\\x08\\xdc\\x2b\\xc0\\xd4\\x69\\xef\\x30\\x2a\\x46\\xb9\\xff\\x58\\x73\\x9f\\x5a\\xbe\\xc2\\x7c\\x1a\\xed\\x8d\\x0b\\x70\\xaf\\xc4\\x3d\\x53\\xee\\xbf\\xef\\x8e\\x7a\\xff\\x4d\\x47\\x7e\\x8c\\x0e\\xee\\xc7\\x58\\x01\\x63\\x06\\xd4\\x1d\\xf6\\x41\\x9b\\x18\\x51\\xfc\\x61\\xf9\\xb2\\xb4\\xe5\\x7b\\x55\\x1b\\xc3\\xb3\\x18\\x33\\x61\\xec\\x84\\x31\\x14\\xf1\\xdc\\xfb\\x40\\x5a\\xf1\\x97\\x4e\\xf1\\xd7\\x34\\x8a\\xd9\\x06\\x3f\\xa9\\xe4\\x3b\\xb6\\x48\\xb1\\xa2\\xfa\\x12\\xc6\\x8e\\x18\\x43\\x62\\x2c\\x89\\x31\\x25\\xc6\\x96\\x29\\xe3\\x4f\\x6f\\x26\\xc5\\xaa\\xf1\\x62\\x58\\x8c\\x6d\\x31\\xc6\\x4d\\x26\\xbf\\x7f\\x3d\\x60\\xcc\\xac\\xce\\xa3\\x18\\x1a\\x63\\x69\\x8c\\xa9\\xb9\\x5b\\xc6\\xd8\\x14\\x7f\\xe7\\xc4\\xc4\\xdf\\x39\\xb2\\x0e\\x63\\x73\\x8c\\x91\\x93\\xc5\\xf0\\xc4\\x47\\x49\\x2a\\x9f\\x30\\x68\\x0a\\xf3\\x15\\x65\\x31\\x6b\\xaf\\x7e\\xd3\\x3a\\x53\\xd4\\x5b\\x67\\x8c\\x44\\xe7\\x0f\\x49\\xc9\\xcf\\x30\\xad\\xd6\\x59\\x27\\xa9\\xfc\\x58\\x5d\\xf4\\x69\\x14\\x2f\\x8d\\x95\\x67\\xab\\x51\\x9f\\xcf\\xa6\\x5b\\x67\\xbd\\xb4\\xe4\\x7f\\xde\\x1f\\x7b\\x6d\\x1c\\x66\\x2a\\x1c\\x66\\x0c\\xe9\\x99\\xc3\\x8c\\x4d\\xb7\\x28\\xcf\\xa2\\x4c\\x8b\\xd4\\x74\\xa8\\xc5\\xa2\\x1e\\x8b\\x02\\x16\\x99\\x8c\\xa9\\xc0\\x48\\x90\\x6a\\xcb\\x9d\\xc9\\x18\\x9b\\xcd\\x18\\xfb\\xfb\\xd8\\x3c\\xc5\\xc3\\x9f\\xb5\\x56\\xee\\x3e\\x77\\x9f\\x3f\\xcc\\xc7\\xda\\x1f\\x1f\\x16\\x5c\\x7d\\x5b\\x70\\x75\\xa1\\xe0\\x6a\\xc1\\xef\\x89\\x6c\\xde\\xff\\x2a\\xb8\\xfa\\x37\\x82\\x2b\\xb3\\x05\\x57\\x72\\x7a\\x35\\xc6\\x0c\\x2d\\x79\\x3e\\x29\\x0d\\xfc\\xdf\\x16\\x5c\\x0d\\x0a\\xae\\xc2\\x1d\\x22\\x53\\x70\\xb5\\x53\\x70\\xf5\\x20\\xed\\xeb\\x5c\\xc9\\xc7\\x58\\xc3\\x48\\x33\\x3f\\x97\\x18\\xbf\\x42\\xb1\\x57\\x5a\\x84\\x6d\\x07\\x60\\x4a\\xd2\\x37\\x6e\\x5b\\x97\\xfd\\x1b\\xf5\\x56\\x2a\\xb8\\xfa\\x24\\xec\\x67\\x4c\\xe7\\xc3\\x1b\\x83\\x83\\x5f\\x73\\x05\\x31\\xc6\\x0b\\xd6\\xbe\\x45\\xe7\\xf2\\x64\\x14\\x3c\\xbb\\x08\\x8c\\x8a\\xa7\\xfa\\x71\\x21\\xc6\\xc2\\x1c\\x30\\x0e\\x7e\\x0d\\x02\\x27\\xfe\\x81\\x78\\x20\\xe1\\x3b\\xd6\\xc9\\x78\\x84\\x39\\xd8\\xf5\\xe2\\x29\\x60\\x94\\xcd\\x04\\xe1\\xcd\\x8a\\x1d\\x47\\xa3\\xe0\\xea\\x0b\\x7e\\xce\\x5c\\x7a\\x8a\\xfc\\x64\\x7c\\xfc\\x2c\\x48\\xf1\\x75\\xb8\\x6f\\x48\\x9c\\x37\\xf8\\x31\\xdb\\x4a\\x64\\x9c\\x84\\xb2\\xbd\\x19\\xe0\\x3f\\xf2\\x17\\x60\\xb6\\xee\\x85\\x68\\xe0\\x26\\x40\\x34\\x12\\x13\\x20\\x46\\xa8\\x0e\\xbf\\x61\\x1b\\x6c\\x8b\\x7d\\xf4\\xe2\\xc9\\x10\\x6a\\x58\\x0b\\xe1\\xcb\\x5b\\xc1\\xa8\\xfc\\x46\\xff\\x9c\\x70\\xb5\\x0d\\xc7\\xd0\\xa3\\x65\\x33\\x91\\xe6\\x3c\\x0c\\x17\\x7f\\xa4\\xeb\\x14\\xc5\\xb4\\xc2\\xc3\\xe8\\x3c\\x43\\xb1\\x65\\xb0\\x3b\\xe5\\x98\\xb1\\x0d\\xb6\\xc5\\x3e\\xf2\\x7c\\x30\\x13\\x22\\xdd\\x35\\x10\\xf5\\x5f\\xa7\\x73\\xa9\\x28\\xbc\\xc7\\x1e\\x43\\x93\\xe0\\xca\\x53\\x14\\x73\\x16\\xa6\\x8e\\x89\\x86\\x83\\x9f\\xce\\x41\\x55\\xdf\\x23\\xec\\x62\\x77\\x2e\\x9d\\xc7\\xed\\xb3\\xa8\\x1c\\x5c\\x10\\xa2\\xa2\\x05\\x22\\xb7\\x3e\\x21\\xc2\\x77\\xac\\xeb\\xff\\x1e\\xa2\\x3e\\xd8\\x17\\x79\\x20\\xaf\\x68\\xa8\\x87\\xda\\x84\\xea\\x97\\xcb\\xbc\\xb3\\x1c\\x43\\xa5\\xe0\\xea\\xe4\\x74\\x62\\xba\\xe1\\xe0\\x0f\\x35\\xac\\x91\\x36\\xc0\\xdd\\x10\\xac\\x7b\\xbb\\x1f\\x7b\\x34\\x0c\\x66\\x47\\x05\\xdd\\x3d\\xe9\\xa5\\x8f\\x80\\x5e\\x34\\x51\\x52\\xe9\\x23\\xf2\\x3e\\xaa\\xa3\\x82\\xda\\xd8\\x63\\x08\\xd6\\xfe\\x52\\xc6\\xee\\xde\\x0c\\xe2\\x69\\x8f\\x1d\\xcf\\x4d\\x92\\xbf\\x1a\\x45\\x7f\\xeb\\xf3\\x32\\x57\\x5f\\x0a\\x3b\\x4a\\x17\\x3f\\xea\\xd2\\x28\\x9f\\x25\\xf5\\x76\\xe4\\x3b\\xfd\\x79\\xad\\x70\\x1f\\x84\\xce\\xff\\x17\\xe8\\x7b\\x26\\x59\\xfe\\xc6\\xca\\xdb\\xdb\\x3e\\x46\\x63\\xf4\\x0d\\xdb\\xd8\\xbc\\xe9\\x0c\\x78\\xe4\\x79\\xe2\\x85\\x3c\\x69\\x9e\\x68\\x7e\\x7b\\x21\\x50\\xfd\\x63\\x7b\\x4d\\x5f\\x13\\x5c\\xfd\\x7a\\xca\\x73\\x45\\x9a\\xf8\\x69\\x7e\\xad\\xb5\\x67\\xde\\xa8\\xea\\xd7\\x59\\xdd\\x02\\x79\\x0f\\xc1\\xa5\\x1f\\xe9\\xd9\\x39\\x06\\x6e\\x6e\\xcd\\x22\\xc2\\x77\\xb9\\x36\\x5d\\xd4\\x06\\xdb\\xda\\xf6\\x64\\xde\\x38\\x42\\xbc\\x90\\x27\\xf2\\x76\\x4c\\xac\\xbb\\x86\\x72\\x44\\xd6\\x18\\xde\\xd3\\xb9\\xcb\\x9d\\xcc\\x1f\\xa5\\x83\\x9f\\xf2\\x5c\\x15\\x73\\x48\\x5f\\x81\\x4f\\x5f\\x75\\xce\\xa8\\xe1\\xcb\\x5b\\x1c\\x9b\\x45\\xac\\x57\\xde\\xcd\\xa3\\x5c\\x66\\xcd\\xd2\\x29\\x44\\xf8\\x8e\\x75\\x72\\x1c\\x0a\\xb5\\xc5\\x3e\\x92\\xa9\\x49\\xbc\\x68\\x0e\\x2a\\xe6\\x0c\\x38\\x5b\\x87\\x2e\\x2c\\xb1\\xce\\x86\\x6a\\xbb\\xe0\\xea\\x57\\x93\\x9f\\x2d\\x53\\xe3\\xa7\\x5c\\xcf\\xae\\x6c\\xb2\\x03\\xb3\\xb3\\x5a\\xea\\xa9\\xaf\\x51\\x9e\\x5d\\x35\\x06\\xdd\\xdb\\x33\\x9c\\x9c\\x53\\x3c\\xc2\\x6f\\xd8\\x86\\xfc\\x4e\\xf9\\x57\\x9c\\xfc\\x9f\\xd9\\xf9\\xb1\\xb4\\xbb\\xc2\\x6c\\x30\\xdb\\xcb\\xfb\\xf5\\xa5\\x5f\\x95\\x79\\x41\\xb9\\x67\\x2c\\x12\\xdc\\x9d\\xf0\\xce\\x2a\\x1d\\xfc\\xe4\\xdf\\xc8\\x5f\\x7c\\x97\\x72\\xf1\\x54\\x57\\xf3\\xef\\x64\\x17\\xbd\\x1e\\x37\\xe5\\x6c\\x12\\x61\\xb7\\x09\\xdb\\x60\\x5b\\xec\\x83\\x7d\\xe5\\x00\\x0c\\xe2\\x89\\xbc\\x51\\xc6\\x00\\x99\\x75\\x0b\\x6d\\x1b\\x3a\\x2e\\xb8\\x32\\x61\\xa4\\xf8\\xf1\\x37\\xe5\\xe9\\x35\\x06\\xa1\\x86\\x55\\x96\\x7e\\x5a\\xc0\\x28\\x9d\\x49\\x75\\xad\\xef\\x8d\\x1b\\x82\\xb5\\x76\\xd9\\xc0\\x7b\\x00\\x9b\\xb0\\xad\\xed\\xfb\\xa3\\xfa\\x15\\x69\\x2b\\x17\\x57\\x51\\x1d\\xca\\x88\\x95\\x8d\\xfe\\x57\\xde\\x9f\\x29\\x3e\\xc1\\xd5\\x6f\\x26\\xce\\x8f\\x24\\xc7\\x1f\\xf1\\x35\\x80\\x5e\\xf2\\x00\\xe8\\xbb\\xf3\\x21\\x72\\xf3\\x98\\x65\\xf7\\x1f\\x80\\xf0\\x8e\\x81\\x9e\\x1d\\x63\\xc8\\xc6\\x6d\\x7c\\x35\\x4b\\xa7\\x92\\xbd\\xdf\\xda\\x96\\x09\\x5d\\xdb\\x33\\xa1\\x6d\\x43\\x8e\\x73\\xbf\\x60\\xe7\\xf9\\xb1\\x0f\\xfa\\x48\\xdc\\x7b\\x89\\xff\\xcd\\x6a\\xe2\\x4d\\x79\\x65\\x5f\\xc3\\x40\\xbd\\xfd\\xee\\x5b\\xb6\\x0d\\x51\\x7e\\x3b\\xde\\x3a\\x4e\\x85\\xdf\\xec\\x38\\x44\\xb6\\x6f\\x94\\x3d\\x46\\xff\\x67\\xc0\\x27\\x70\\xf2\\x65\\xe2\\x7b\\xed\\xfd\\x9c\\x98\\xbc\\xe7\\x54\\xa9\\x5f\\x27\\x16\\x50\\x40\\xe7\\x2a\\x5c\\xdf\\x9c\\x4d\\xf7\\x32\\x76\\x3b\\xec\\x83\\x7d\\x03\\x9f\\xfc\\xa3\\xc4\\xe9\\x6f\\x27\\xde\\x28\\x03\\x65\\x0d\\xb0\\xa1\\x9a\\xff\\xb0\\xf1\\x6f\\xd3\\xb9\\x2b\\xee\\x9d\\x69\\x2a\\xfc\\xa4\\x6b\\x9c\\xdf\\xc3\\xcf\\x01\\x44\\x02\\x94\\xcb\\x35\\x2a\\x9f\\xa6\\xba\\xa6\\xb5\\x13\\x9c\\x7c\\x29\\xae\\x51\\x9f\\x47\\xe6\\x93\\xf4\\xc2\\x6c\\xd0\\x77\\x8f\\x73\\xe2\\xcd\\xcb\\xeb\\xf3\\x9d\\xfc\\x2a\\xf6\\x21\\x7e\\x87\\x9e\\x96\\x79\\xe1\\x48\\x80\\x78\\x63\\x1d\\xca\\x1a\\x20\\xfb\\x2a\\xb7\\xf7\\xb3\\x6a\\xc1\\x95\\xdc\\x91\\xe0\\x0f\\x5d\\x58\\x26\\xfd\\xe6\\xf1\\x97\\xa4\\xbe\\xc4\\x65\\xca\\x5d\\xf5\\xee\\x54\\x29\\xe7\\x6d\\xeb\\xb5\\x0d\\xf5\\xca\\x5d\\x60\\xec\\x9d\\x4a\\x77\\x30\\x91\\xda\\x37\\xc0\\x5f\\x3e\\x93\\xc6\\xd3\\xf9\\xc1\\x58\\xb2\\x2d\\x3b\\x4f\\xee\\xdb\\xa9\\x12\\x0f\\xe4\\x45\\xf3\\x79\\xfc\\x25\\x92\\x81\\xb2\\x06\\xd8\\xee\\xcd\\x13\\xf2\\xce\\x98\\x2b\\x4d\\x82\\xab\\x0f\\x26\\xc9\\x91\\x3e\\x97\\x08\\x3f\\xed\\x4f\\x3b\\x19\\x04\\x4f\\xbd\\x2e\\x79\\xf6\\xd4\\xd2\\xbe\\xd3\\xbd\\x7d\\x0c\\x9c\\xb5\\x6c\\x1b\\xb1\\xe1\\x7e\\xa5\\x7b\\x55\\x08\\x1d\\xfb\\x11\\x40\\xf3\\x5a\\x22\\xf3\\xf4\\xcf\\x69\\x2e\\xd0\\xff\\xa3\\xed\\x23\\x7e\\xec\\xd3\\xbd\\xdd\\x4d\\x31\\x74\\xa4\\xa7\\x4e\\xca\\x38\\xf5\\xba\\x94\\x81\\xfb\\x5b\\xac\\xef\\x10\\xcd\\x74\\xf7\\x64\\xdd\\x79\\xcf\\x4a\\x82\\xff\\x5b\\x42\\x63\\x86\\x71\\xf0\\xc9\\x21\\xb1\\x24\\xf9\\x4e\\xe4\\x5d\\x3b\\xdf\\xd2\\xc9\\x71\\xd0\\x8b\\xf2\\xa1\\x6b\\x5b\\x86\\xe3\\x63\\xb0\\xec\\xda\\x9e\\x41\\xf7\\x6f\\x66\\xcd\\xeb\\x00\\xcd\\x6b\\x00\\x9a\\x56\\x43\\xb4\\x7e\\x31\\x18\\x7b\\x27\\x83\\xcf\\x23\\xe7\\x0a\\xf1\\x3b\\x6d\\x8b\\xf2\\xe9\\xff\\x3a\\x24\\xa3\\x76\\xbe\\x94\\x31\\xc8\\x87\\x62\\x6c\\x4a\\x31\\x8b\\xc6\\xfa\\x04\\x57\\x9f\\x4e\\x82\\xff\\x69\\x6c\\x43\\xb1\\x88\\xff\\xfa\\xc8\\xf0\\x6f\\xcb\\x00\\x7d\\x57\\x16\\x98\\x67\\x7e\\xde\\x8f\\xff\\x42\\x01\\x18\\xc5\\x93\\x08\\xff\\xf9\\x91\\xe0\\x0f\\x76\\x03\\xea\\x14\\x75\\x4b\\x3a\\x4e\\x8c\\x7f\\x16\\xe5\\x96\\xf7\\xcd\\xa0\\x39\\x4b\\x6e\\x3f\\x75\\x34\\xf7\\x68\\x03\\xb6\\xfd\\xa0\\xef\\xb9\\xb1\\x85\\xee\\xd3\\x21\\x78\\xe4\\xcf\\xe8\\x0e\\x10\\xed\\x27\\x7c\\xe2\\x45\\xd0\\xbd\\x19\\xd0\\xbd\\x23\\x83\\xda\\x0e\\xdb\\x7e\\xc2\\x7d\\x74\\x67\\x81\\xb6\\x4d\\x36\\x9e\\x18\\xff\\x83\\xb8\\x46\\xf4\\xa2\\x09\\x74\\xdf\\x31\\x92\\xf5\\x7b\\xf5\\xb7\\xb9\\x96\\xef\\xc9\\x81\\x60\\xd5\\x73\\x10\\xaa\\xfe\\x3e\\xe8\\xc5\\xf7\\x82\\xce\\x15\\xba\\xd3\\xc2\\x31\\x9e\\xfa\\xb5\\xbd\\x7e\\x95\\xb4\\xd6\\xef\\x20\\xfc\\xdf\\x8e\\x8f\\x9f\\x72\\xeb\\xb9\\xe4\\xa3\\x70\\x5f\\xb9\\xca\\x07\\xf0\\x48\\xd7\\x7f\\x9e\\x5b\\x81\\x7b\\x53\\x46\\x7f\\xec\\x6c\\x91\\x4f\\xeb\\x8f\\x2f\\xb0\\x6d\\xe3\\x30\\xfc\\x67\\xba\\xf8\\xe5\\xde\\xa0\\x6e\\x47\\x1e\\xc1\\x9a\\xf9\\x03\\x78\\xa4\\xbb\\x7f\\x21\\x35\\xad\\x1b\\x0f\\xbd\\x9e\\x31\\xd6\\x3d\\xa6\\x0a\\x7d\\x9a\\x9b\\xe6\\x05\\x75\\x3f\\x64\\xff\\x3a\\xf9\\xb2\\xb5\\x46\\x13\\xef\\x5f\\xe9\\xe0\\xd7\\xed\\xbb\\x3e\\xdc\\xa3\\x51\\x2f\\xbf\\xfb\\xd3\\x81\\x71\\x48\\xdc\\xf8\\x61\\x6b\\xdc\\xf8\\xc1\\xde\\xc7\\x30\\x6e\\x40\\x9c\\xa8\\xeb\\x58\\xec\\xc3\\x89\\x1f\\xd2\\xc5\\x1f\\xb3\\x06\\xbe\\x29\\xb8\\xd2\\xa7\\xef\\xb9\\x0f\\x22\\x5d\\x9f\\x0e\\xe0\\xe1\\xc4\\x6f\\x17\\xd3\\x8b\\xdf\\x12\\xd1\\x70\\xe2\\xb7\\xe1\\xe1\\xa7\\x35\\x30\\x81\\x62\\x55\\xcd\\x45\\xe7\\xd0\\xd8\\xe7\\xb3\\x88\\x9f\\x87\\x85\\x5f\\x53\\x18\\xfd\\x3f\\x95\\xab\\x8b\\xe5\\x19\\xe3\\x71\\x3a\\x43\\x38\\x6b\\xe0\\xf7\\x76\\x7e\\xa9\\x8e\\x7b\\x7e\\x19\\x2e\\xfe\\x18\\x1b\\xc2\\xb3\\x5a\\x07\\x9e\\xdd\\xf0\\x0c\\xe7\\xf0\\xb9\\x23\\xe7\\xc7\\x9b\\xa3\\xc4\\x4f\\x7e\\xc8\\x8d\\x67\\x66\\x3a\\xa7\\xef\\x7f\\x88\\xce\\xd2\\xf6\\x93\\xf6\\xf9\\x7d\\x47\\xcc\\xf9\\x7d\\x47\\xb2\\xf3\\x7b\\x55\\xdc\\xf3\\xfb\\x48\\xf1\\xc7\\xcc\\xc1\\xd7\\x28\\x77\\x81\\x3e\\xae\\xfa\\xaf\\x29\\xa7\\x01\\x43\\xf2\\x27\\xcf\\x0f\\xcc\\x9f\\x9c\\xbb\\x3d\\xf9\\x93\\xd1\\xe2\\xef\\xe3\\x2e\\xe6\\xe3\\x4c\\x91\\x67\\x66\\x35\\x8a\\x7e\\x8e\\xd6\\xb2\\xa5\\xb3\\xd4\\xf9\\xab\\x17\\xc1\\x28\\x7d\\x04\\xf4\\x3d\\x13\\x89\\x8c\\x44\\xf9\\xab\\xba\\xb7\\x87\\xe6\\xaf\\x6e\\x03\\xfe\\x98\\x39\\x98\\x2c\\xb8\\x7a\\xc8\\xb6\\xd9\\x50\\xfd\\x32\\x99\\x1b\\x0c\\xf5\\x38\\xfe\\xe2\\xf6\\xe5\\x0f\\x13\\xe7\\x4e\\x47\\x84\\xbf\\x50\\xb5\\xfd\\xe9\\x1c\\x2b\\x97\\x4a\\xb9\\x55\\xfa\\xdf\\x50\\xe0\\x3a\\x44\\xba\\xcf\\xc8\\xdc\\x37\\xda\\x8a\\x9d\\xbf\\x4d\\x82\\xc1\\xc1\\x62\\xe5\\x6f\\xb1\\x0f\\xe5\\x7e\\x4b\\x1f\\xa5\\x5c\\x70\\xd2\\x3e\\x23\\xc0\\x4f\\x63\\xf0\\xba\\x58\\x8f\\x27\\x1b\\xe7\\xe1\\x05\\x99\\xd3\\x56\\x68\\xbe\\x8d\\xca\\x6f\\xd0\\xbe\\x19\\x6a\\x58\\xeb\\xac\\x3d\\x27\\x7f\\xde\\x56\\x92\\x7e\\xfe\\xbc\\xe4\\x41\\x30\\xdb\\xf6\\xa5\\x1e\\xf3\\x08\\xf1\\xe3\\xa3\\x7b\\x15\\x66\\xec\\xa2\\xff\\x6d\\xbc\\x60\\xdd\\x2d\\x48\\xbc\\xbb\\xb2\\xe8\\xbf\\x31\\x7a\\xf1\\xd4\\x24\\xf7\\x17\\xf3\\x89\\x12\\xdd\\x5f\\x18\\x15\\x4f\\xd1\\x1d\\x48\\xca\\x7b\\x92\\xda\\xb7\\xac\\xff\\xda\\xba\\x86\\x8d\\x9f\\xc6\\xc0\\x5d\\x0c\\xbc\\xe4\\x5b\\xd1\\x27\\x95\\x39\\x77\\x64\\x8e\\x8f\\x19\\xcd\\xfd\\xd1\\xb0\\xee\\xa9\\x46\\x84\\x1f\\x1f\\x43\\x73\\xd9\\x6b\\x7a\\xbc\\xbc\\x6b\\xa3\\x3b\\xb7\\x4e\\xeb\\x0e\\xee\\x4e\\xdd\\xf7\\x8d\\x18\\xbf\\x33\\x0e\\x8f\\xc2\\x7a\\x8b\\x54\\x8c\\x35\\x72\\x04\\x57\\x67\\x0b\\xae\\xfe\\xc4\\xba\\x0b\\x2d\\xb8\\x03\\xf7\\xae\\x6f\\x5b\\x77\\xbc\\x23\\xc6\\x7f\\xf7\\xb9\\xfb\\x7c\\x9e\\x1e\\xb9\\x43\\x24\\x2e\\x5b\\x18\\x63\\xcf\\x58\\x65\\x9e\\x55\\x66\\x5a\\xa5\\x6b\\x50\\xc9\\xec\\xb2\\xc0\\x2a\\x9f\\x19\\x54\\x4e\\x4f\\x50\\xe6\\x25\\x28\\x33\\x6f\\x5f\\xd9\\x93\\xa0\\x0c\\x24\\x28\\xcd\\x41\\x65\\xd4\\x2a\\xc1\\x2e\\x17\\x0e\\x2a\\x5b\\xac\\xb2\\xc7\\x2a\\x4d\\xab\\x4c\\xa1\\xdf\\xff\\x0f\\x00\\x00\\xff\\xff\\xc6\\xb9\\x24\\x2f\\xee\\x3a\\x00\\x00\")\n\nfunc faviconIcoBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_faviconIco,\n\t\t\"favicon.ico\",\n\t)\n}\n\nfunc faviconIco() (*asset, error) {\n\tbytes, err := faviconIcoBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"favicon.ico\", size: 15086, mode: os.FileMode(438), modTime: time.Unix(1612640817, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\nvar _jsMainJs = []byte(\"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\x4a\\xce\\xcf\\x2b\\xce\\xcf\\x49\\xd5\\xcb\\xc9\\x4f\\xd7\\x50\\x4a\\xad\\x48\\xcc\\x2d\\xc8\\x49\\x55\\xd2\\xb4\\x06\\x04\\x00\\x00\\xff\\xff\\xc8\\x9f\\xbd\\x5f\\x17\\x00\\x00\\x00\")\n\nfunc jsMainJsBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_jsMainJs,\n\t\t\"js/main.js\",\n\t)\n}\n\nfunc jsMainJs() (*asset, error) {\n\tbytes, err := jsMainJsBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"js/main.js\", size: 23, mode: os.FileMode(438), modTime: time.Unix(1613718745, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\n// Asset loads and returns the asset for the given name.\n// It returns an error if the asset could not be found or\n// could not be loaded.\nfunc Asset(name string) ([]byte, error) {\n\tcannonicalName := strings.Replace(name, \"\\\\\", \"/\", -1)\n\tif f, ok := _bindata[cannonicalName]; ok {\n\t\ta, err := f()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"Asset %s can't read by error: %v\", name, err)\n\t\t}\n\t\treturn a.bytes, nil\n\t}\n\treturn nil, fmt.Errorf(\"Asset %s not found\", name)\n}\n\n// MustAsset is like Asset but panics when Asset would return an error.\n// It simplifies safe initialization of global variables.\nfunc MustAsset(name string) []byte {\n\ta, err := Asset(name)\n\tif err != nil {\n\t\tpanic(\"asset: Asset(\" + name + \"): \" + err.Error())\n\t}\n\n\treturn a\n}\n\n// AssetInfo loads and returns the asset info for the given name.\n// It returns an error if the asset could not be found or\n// could not be loaded.\nfunc AssetInfo(name string) (os.FileInfo, error) {\n\tcannonicalName := strings.Replace(name, \"\\\\\", \"/\", -1)\n\tif f, ok := _bindata[cannonicalName]; ok {\n\t\ta, err := f()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"AssetInfo %s can't read by error: %v\", name, err)\n\t\t}\n\t\treturn a.info, nil\n\t}\n\treturn nil, fmt.Errorf(\"AssetInfo %s not found\", name)\n}\n\n// AssetNames returns the names of the assets.\nfunc AssetNames() []string {\n\tnames := make([]string, 0, len(_bindata))\n\tfor name := range _bindata {\n\t\tnames = append(names, name)\n\t}\n\treturn names\n}\n\n// _bindata is a table, holding each asset generator, mapped to its name.\nvar _bindata = map[string]func() (*asset, error){\n\t\"css/main.css\": cssMainCss,\n\t\"favicon.ico\":  faviconIco,\n\t\"js/main.js\":   jsMainJs,\n}\n\n// AssetDir returns the file names below a certain\n// directory embedded in the file by go-bindata.\n// For example if you run go-bindata on data/... and data contains the\n// following hierarchy:\n//\n//\tdata/\n//\t  foo.txt\n//\t  img/\n//\t    a.png\n//\t    b.png\n//\n// then AssetDir(\"data\") would return []string{\"foo.txt\", \"img\"}\n// AssetDir(\"data/img\") would return []string{\"a.png\", \"b.png\"}\n// AssetDir(\"foo.txt\") and AssetDir(\"notexist\") would return an error\n// AssetDir(\"\") will return []string{\"data\"}.\nfunc AssetDir(name string) ([]string, error) {\n\tnode := _bintree\n\tif len(name) != 0 {\n\t\tcannonicalName := strings.Replace(name, \"\\\\\", \"/\", -1)\n\t\tpathList := strings.Split(cannonicalName, \"/\")\n\t\tfor _, p := range pathList {\n\t\t\tnode = node.Children[p]\n\t\t\tif node == nil {\n\t\t\t\treturn nil, fmt.Errorf(\"Asset %s not found\", name)\n\t\t\t}\n\t\t}\n\t}\n\tif node.Func != nil {\n\t\treturn nil, fmt.Errorf(\"Asset %s not found\", name)\n\t}\n\trv := make([]string, 0, len(node.Children))\n\tfor childName := range node.Children {\n\t\trv = append(rv, childName)\n\t}\n\treturn rv, nil\n}\n\ntype bintree struct {\n\tFunc     func() (*asset, error)\n\tChildren map[string]*bintree\n}\n\nvar _bintree = &bintree{nil, map[string]*bintree{\n\t\"css\": {nil, map[string]*bintree{\n\t\t\"main.css\": {cssMainCss, map[string]*bintree{}},\n\t}},\n\t\"favicon.ico\": {faviconIco, map[string]*bintree{}},\n\t\"js\": {nil, map[string]*bintree{\n\t\t\"main.js\": {jsMainJs, map[string]*bintree{}},\n\t}},\n}}\n\n// RestoreAsset restores an asset under the given directory\nfunc RestoreAsset(dir, name string) error {\n\tdata, err := Asset(name)\n\tif err != nil {\n\t\treturn err\n\t}\n\tinfo, err := AssetInfo(name)\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = os.WriteFile(_filePath(dir, name), data, info.Mode())\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// RestoreAssets restores an asset under the given directory recursively\nfunc RestoreAssets(dir, name string) error {\n\tchildren, err := AssetDir(name)\n\t// File\n\tif err != nil {\n\t\treturn RestoreAsset(dir, name)\n\t}\n\t// Dir\n\tfor _, child := range children {\n\t\terr = RestoreAssets(dir, filepath.Join(name, child))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc _filePath(dir, name string) string {\n\tcannonicalName := strings.Replace(name, \"\\\\\", \"/\", -1)\n\treturn filepath.Join(append([]string{dir}, strings.Split(cannonicalName, \"/\")...)...)\n}\n"
  },
  {
    "path": "_examples/file-server/embedding-gzipped-files-into-app-bindata/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n)\n\n// How to run:\n//\n// $ go install github.com/go-bindata/go-bindata/v3/go-bindata@latest\n// $ go-bindata -prefix \"../embedding-files-into-app-bindata/assets/\" -fs ../embedding-files-into-app-bindata/assets/...\n// $ go run -mod=mod .\n// Time to complete the compression and caching of [2/3] files: 31.9998ms\n// Total size reduced from 156.6 kB to:\n// br      (22.9 kB) [85.37%]\n// snappy  (41.7 kB) [73.37%]\n// gzip    (27.9 kB) [82.16%]\n// deflate (27.9 kB) [82.19%]\n\nvar dirOptions = iris.DirOptions{\n\tIndexName: \"index.html\",\n\t// The `Compress` field is ignored\n\t// when the file is cached (when Cache.Enable is true),\n\t// because the cache file has a map of pre-compressed contents for each encoding\n\t// that is served based on client's accept-encoding.\n\tCompress: true, // true or false does not matter here.\n\tCache: iris.DirCacheOptions{\n\t\tEnable:         true,\n\t\tCompressIgnore: iris.MatchImagesAssets,\n\t\t// Here, define the encodings that the cached files should be pre-compressed\n\t\t// and served based on client's needs.\n\t\tEncodings:       []string{\"gzip\", \"deflate\", \"br\", \"snappy\"},\n\t\tCompressMinSize: 50, // files smaller than this size will NOT be compressed.\n\t\tVerbose:         1,\n\t},\n}\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\tapp.HandleDir(\"/static\", AssetFile(), dirOptions)\n\treturn app\n}\n\nfunc main() {\n\tapp := newApp()\n\n\t// http://localhost:8080/static/css/main.css\n\t// http://localhost:8080/static/js/main.js\n\t// http://localhost:8080/static/favicon.ico\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/file-server/embedding-gzipped-files-into-app-bindata/main_test.go",
    "content": "package main\n\nimport (\n\t\"bytes\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/httptest\"\n\t\"github.com/klauspost/compress/gzip\"\n)\n\ntype resource string\n\n// content types that are used in the ./assets,\n// we could use the detectContentType that iris do but it's better\n// to do it manually so we can test if that returns the correct result on embedding files.\nfunc (r resource) contentType() string {\n\tswitch filepath.Ext(r.String()) {\n\tcase \".js\":\n\t\treturn \"text/javascript\"\n\tcase \".css\":\n\t\treturn \"text/css\"\n\tcase \".ico\":\n\t\treturn \"image/x-icon\"\n\tcase \".html\":\n\t\treturn \"text/html\"\n\tdefault:\n\t\treturn \"text/plain\"\n\t}\n}\n\nfunc (r resource) String() string {\n\treturn string(r)\n}\n\nfunc (r resource) strip(strip string) string {\n\ts := r.String()\n\treturn strings.TrimPrefix(s, strip)\n}\n\nfunc (r resource) loadFromBase(dir string) string {\n\tfilename := r.String()\n\n\tfilename = r.strip(\"/static\")\n\n\tfullpath := filepath.Join(dir, filename)\n\n\tb, err := os.ReadFile(fullpath)\n\tif err != nil {\n\t\tpanic(fullpath + \" failed with error: \" + err.Error())\n\t}\n\tresult := string(b)\n\n\tif runtime.GOOS != \"windows\" {\n\t\tresult = strings.ReplaceAll(result, \"\\n\", \"\\r\\n\")\n\t\tresult = strings.ReplaceAll(result, \"\\r\\r\", \"\")\n\t}\n\treturn result\n}\n\nvar urls = []resource{\n\t\"/static/css/main.css\",\n\t\"/static/js/main.js\",\n\t\"/static/favicon.ico\",\n}\n\n// if bindata's values matches with the assets/... contents\n// and secondly if the HandleDir had successfully registered\n// the routes and gave the correct response.\nfunc TestEmbeddingGzipFilesIntoApp(t *testing.T) {\n\tdirOptions.Cache.Verbose = 0\n\tapp := newApp()\n\n\te := httptest.New(t, app)\n\n\tif runtime.GOOS != \"windows\" {\n\t\t// remove the embedded static favicon for !windows,\n\t\t// it should be built for unix-specific in order to be work\n\t\turls = urls[0 : len(urls)-1]\n\t}\n\n\tfor i, u := range urls {\n\t\turl := u.String()\n\t\trawContents := u.loadFromBase(\"../embedding-files-into-app-bindata/assets\")\n\t\tshouldBeCompressed := int64(len(rawContents)) >= dirOptions.Cache.CompressMinSize\n\n\t\trequest := e.GET(url)\n\t\tif shouldBeCompressed {\n\t\t\trequest.WithHeader(\"Accept-Encoding\", \"gzip\")\n\t\t}\n\t\tresponse := request.Expect()\n\t\tresponse.ContentType(u.contentType(), app.ConfigurationReadOnly().GetCharset())\n\t\tif shouldBeCompressed {\n\t\t\tresponse.ContentEncoding(\"gzip\")\n\t\t}\n\n\t\tif expected, got := response.Raw().StatusCode, httptest.StatusOK; expected != got {\n\t\t\tt.Fatalf(\"[%d] of '%s': expected %d status code but got %d\", i, url, expected, got)\n\t\t}\n\t\trawBody := response.Body().Raw()\n\n\t\tif shouldBeCompressed {\n\t\t\treader, err := gzip.NewReader(strings.NewReader(rawBody))\n\t\t\tdefer reader.Close()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"[%d] of '%s': %v\", i, url, err)\n\t\t\t}\n\t\t\tbuf := new(bytes.Buffer)\n\t\t\treader.WriteTo(buf)\n\t\t\tif expected, got := rawContents, buf.String(); expected != got {\n\t\t\t\t// t.Fatalf(\"[%d] of '%s': expected body:\\n%s but got:\\n%s\", i, url, expected, got)\n\t\t\t\t// let's reduce the output here...\n\t\t\t\t// they are big files, no need to check for length here.\n\t\t\t\tt.Fatalf(\"[%d] %s, expected body to look like: '%s...%s' but got '%s...%s'\", i, url, expected[:40], expected[len(rawContents)-40:], got[:40], got[len(got)-40:])\n\t\t\t}\n\t\t} else {\n\t\t\tif expected, got := rawContents, rawBody; expected != got {\n\t\t\t\tt.Fatalf(\"[%d] %s, expected body to look like: '%s...%s' but got '%s...%s'\", i, url, expected[:40], expected[len(rawContents)-40:], got[:40], got[len(got)-40:])\n\t\t\t}\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "_examples/file-server/favicon/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\t// This will serve the ./static/favicons/favicon.ico to: localhost:8080/favicon.ico\n\tapp.Favicon(\"./static/favicons/favicon.ico\")\n\n\t// app.Favicon(\"./static/favicons/favicon.\\\\.ico\", \"/favicon_16_16.ico\")\n\t// This will serve the ./static/favicons/favicon.ico to: localhost:8080/favicon_16_16.ico\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.HTML(`<a href=\"/favicon.ico\"> press here to see the favicon.ico</a>.\n\t\t At some browsers like chrome, it should be visible at the top-left side of the browser's window,\n\t\t because some browsers make requests to the /favicon.ico automatically,\n\t\t  so iris serves your favicon in that path too (you can change it).`)\n\t}) // if favicon doesn't show to you, try to clear your browser's cache.\n\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/file-server/file-server/main.go",
    "content": "package main\n\nimport (\n\t\"crypto/md5\"\n\t\"fmt\"\n\t\"io\"\n\t\"mime/multipart\"\n\t\"os\"\n\t\"path\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/middleware/basicauth\"\n)\n\nfunc init() {\n\tos.Mkdir(\"./uploads\", 0700)\n}\n\nconst (\n\tmaxSize   = 1 * iris.GB\n\tuploadDir = \"./uploads\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\tview := iris.HTML(\"./views\", \".html\")\n\tview.AddFunc(\"formatBytes\", func(b int64) string {\n\t\tconst unit = 1000\n\t\tif b < unit {\n\t\t\treturn fmt.Sprintf(\"%d B\", b)\n\t\t}\n\t\tdiv, exp := int64(unit), 0\n\t\tfor n := b / unit; n >= unit; n /= unit {\n\t\t\tdiv *= unit\n\t\t\texp++\n\t\t}\n\t\treturn fmt.Sprintf(\"%.1f %cB\",\n\t\t\tfloat64(b)/float64(div), \"kMGTPE\"[exp])\n\t})\n\tapp.RegisterView(view)\n\n\t// Serve assets (e.g. javascript, css).\n\t// app.HandleDir(\"/public\", iris.Dir(\"./public\"))\n\n\tapp.Get(\"/\", index)\n\n\tapp.Get(\"/upload\", uploadView)\n\tapp.Post(\"/upload\", upload)\n\n\tfilesRouter := app.Party(\"/files\")\n\t{\n\t\tfilesRouter.HandleDir(\"/\", iris.Dir(uploadDir), iris.DirOptions{\n\t\t\tCompress: true,\n\t\t\tShowList: true,\n\n\t\t\t// Optionally, force-send files to the client inside of showing to the browser.\n\t\t\tAttachments: iris.Attachments{\n\t\t\t\tEnable: true,\n\t\t\t\t// Optionally, control data sent per second:\n\t\t\t\tLimit: 50.0 * iris.KB,\n\t\t\t\tBurst: 100 * iris.KB,\n\t\t\t\t// Change the destination name through:\n\t\t\t\t// NameFunc: func(systemName string) string {...}\n\t\t\t},\n\n\t\t\tDirList: iris.DirListRich(iris.DirListRichOptions{\n\t\t\t\t// Optionally, use a custom template for listing:\n\t\t\t\t// Tmpl: dirListRichTemplate,\n\t\t\t\tTmplName: \"dirlist.html\",\n\t\t\t}),\n\t\t})\n\n\t\tauth := basicauth.Default(map[string]string{\n\t\t\t\"myusername\": \"mypassword\",\n\t\t})\n\n\t\tfilesRouter.Delete(\"/{file:path}\", auth, deleteFile)\n\t}\n\n\tapp.Listen(\":8080\")\n}\n\nfunc index(ctx iris.Context) {\n\tctx.Redirect(\"/upload\")\n}\n\nfunc uploadView(ctx iris.Context) {\n\tnow := time.Now().Unix()\n\th := md5.New()\n\tio.WriteString(h, strconv.FormatInt(now, 10))\n\ttoken := fmt.Sprintf(\"%x\", h.Sum(nil))\n\n\tif err := ctx.View(\"upload.html\", token); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n}\n\nfunc upload(ctx iris.Context) {\n\tctx.SetMaxRequestBodySize(maxSize)\n\n\t_, _, err := ctx.UploadFormFiles(uploadDir, beforeSave)\n\tif err != nil {\n\t\tctx.StopWithError(iris.StatusRequestEntityTooLarge, err)\n\t\treturn\n\t}\n\n\tctx.Redirect(\"/files\")\n}\n\nfunc beforeSave(ctx iris.Context, file *multipart.FileHeader) bool {\n\tip := ctx.RemoteAddr()\n\tip = strings.ReplaceAll(ip, \".\", \"_\")\n\tip = strings.ReplaceAll(ip, \":\", \"_\")\n\n\tfile.Filename = ip + \"-\" + file.Filename\n\treturn true\n}\n\nfunc deleteFile(ctx iris.Context) {\n\t// It does not contain the system path,\n\t// as we are not exposing it to the user.\n\tfileName := ctx.Params().Get(\"file\")\n\n\tfilePath := path.Join(uploadDir, fileName)\n\n\tif err := os.RemoveAll(filePath); err != nil {\n\t\tctx.StopWithError(iris.StatusInternalServerError, err)\n\t\treturn\n\t}\n\n\tctx.Redirect(\"/files\")\n}\n"
  },
  {
    "path": "_examples/file-server/file-server/views/dirlist.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>{{.Title}}</title>\n    <style>\n        a {\n            padding: 8px 8px;\n            text-decoration: none;\n            cursor: pointer;\n            color: #10a2ff;\n        }\n\n        table {\n            position: absolute;\n            top: 0;\n            bottom: 0;\n            left: 0;\n            right: 0;\n            height: 100%;\n            width: 100%;\n            border-collapse: collapse;\n            border-spacing: 0;\n            empty-cells: show;\n            border: 1px solid #cbcbcb;\n        }\n\n        table caption {\n            color: #000;\n            font: italic 85%/1 arial, sans-serif;\n            padding: 1em 0;\n            text-align: center;\n        }\n\n        table td,\n        table th {\n            border-left: 1px solid #cbcbcb;\n            border-width: 0 0 0 1px;\n            font-size: inherit;\n            margin: 0;\n            overflow: visible;\n            padding: 0.5em 1em;\n        }\n\n        table thead {\n            background-color: #10a2ff;\n            color: #fff;\n            text-align: left;\n            vertical-align: bottom;\n        }\n\n        table td {\n            background-color: transparent;\n        }\n\n        .table-odd td {\n            background-color: #f2f2f2;\n        }\n\n        .table-bordered td {\n            border-bottom: 1px solid #cbcbcb;\n        }\n\n        .table-bordered tbody>tr:last-child>td {\n            border-bottom-width: 0;\n        }\n    </style>\n</head>\n\n<body>\n    <table class=\"table-bordered table-odd\">\n        <thead>\n            <tr>\n                <th>#</th>\n                <th>Name</th>\n                <th>Size</th>\n                <th>Actions</th>\n            </tr>\n        </thead>\n        <tbody>\n            {{ range $idx, $file := .Files }}\n            <tr>\n                <td>{{ $idx }}</td>\n                {{ if $file.Download }}\n                <td><a href=\"{{ $file.Path }}\" title=\"{{ $file.ModTime }}\" download>{{ $file.Name }}</a></td> \n                {{ else }}\n                <td><a href=\"{{ $file.Path }}\" title=\"{{ $file.ModTime }}\">{{ $file.Name }}</a></td>\n                {{ end }}\n                {{ if $file.Info.IsDir }}\n                <td>Dir</td>\n                {{ else }}\n                <td>{{ formatBytes $file.Info.Size }}</td>\n                {{ end }}\n\n                <td><input type=\"button\" style=\"background-color:transparent;border:0px;cursor:pointer;\" value=\"❌\"\n                        onclick=\"deleteFile({{ $file.RelPath }})\" /></td>\n            </tr>\n            {{ end }}\n        </tbody>\n    </table>\n    <script type=\"text/javascript\">\n        function deleteFile(filename) {\n            if (!confirm(\"Are you sure you want to delete \" + filename + \" ?\")) {\n                return;\n            }\n\n            fetch('/files/' + filename,\n                {\n                    method: \"DELETE\",\n                    // If you don't want server to prompt for username/password:\n                    // credentials:\"include\",\n                    headers: {\n                        // \"Authorization\": \"Basic \" + btoa(\"myusername:mypassword\")\n                        \"X-Requested-With\": \"XMLHttpRequest\",\n                    },\n                }).\n                then(data => location.reload()).\n                catch(e => alert(e));\n        }\n    </script>\n</body>\n\n</html>"
  },
  {
    "path": "_examples/file-server/file-server/views/upload.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Upload Files</title>\n</head>\n\n<body>\n    <form enctype=\"multipart/form-data\" action=\"/upload\" method=\"POST\">\n        <input type=\"file\" id=\"upload_files\" name=\"upload_files\" multiple /> <input type=\"hidden\" name=\"token\"\n            value=\"{{.}}\" />\n\n        <input type=\"button\" value=\"Upload Using Javascript\" onclick=\"uploadFiles()\" />\n        <input type=\"submit\" value=\"Upload by submiting the form\" />\n    </form>\n\n    <script type=\"text/javascript\">\n        function uploadFiles() {\n            let files = document.getElementById(\"upload_files\").files;\n            let formData = new FormData();\n            for (var i = 0; i < files.length; i++) {\n                formData.append(\"files[]\", files[i]);\n            }\n\n            fetch('/upload',\n                {\n                    method: \"POST\",\n                    body: formData\n                }).\n                then(data => window.location = \"/files\").\n                catch(e => window.alert(\"upload failed: file too large\"));\n        }\n    </script>\n</body>\n\n</html>"
  },
  {
    "path": "_examples/file-server/http2push/assets/app2/app2app3/css/main.css",
    "content": "body {\n    background-color: blue;\n}"
  },
  {
    "path": "_examples/file-server/http2push/assets/app2/app2app3/dirs/dir1/text.txt",
    "content": "app2/app2app3/dirs/dir1/text.txt"
  },
  {
    "path": "_examples/file-server/http2push/assets/app2/app2app3/dirs/dir2/text.txt",
    "content": "app2/app2app3/dirs/dir2/text.txt"
  },
  {
    "path": "_examples/file-server/http2push/assets/app2/app2app3/dirs/text.txt",
    "content": "app2/app2app3/dirs/text.txt"
  },
  {
    "path": "_examples/file-server/http2push/assets/app2/app2app3/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <link rel=\"stylesheet\" href=\"/public/app2/app2app3/css/main.css\" />\n    <title>App2App3</title>\n</head>\n\n<body>\n    <h1>Hello App2App3 index</h1>\n</body>\n\n</html>"
  },
  {
    "path": "_examples/file-server/http2push/assets/app2/index.html",
    "content": "<h1>Hello App2 index</h1>"
  },
  {
    "path": "_examples/file-server/http2push/assets/app2/mydir/text.txt",
    "content": "just a text."
  },
  {
    "path": "_examples/file-server/http2push/assets/css/main.css",
    "content": "body {\n    background-color: black;\n}\n"
  },
  {
    "path": "_examples/file-server/http2push/assets/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <link rel=\"icon\" href=\"/public/favicon.ico\" type=\"image/x-icon\" />\n    <link rel=\"stylesheet\" href=\"/public/css/main.css\" />\n    <title>File Server</title>\n</head>\n\n<body>\n    <input type=\"button\" onclick=\"onClick()\" value=\"Click me!\" />\n\n    <script type=\"text/javascript\" src=\"/public/js/main.js\"></script>\n</body>\n\n</html>"
  },
  {
    "path": "_examples/file-server/http2push/assets/js/main.js",
    "content": "console.log(\"example\");\n\nfunction onClick() {\n    window.alert(\"button clicked\");\n}"
  },
  {
    "path": "_examples/file-server/http2push/main.go",
    "content": "package main\n\nimport (\n\t\"regexp\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nvar opts = iris.DirOptions{\n\tIndexName: \"index.html\",\n\t// Optionally register files (map's values) to be served\n\t// when a specific path (map's key WITHOUT prefix) is requested\n\t// is fired before client asks (HTTP/2 Push).\n\t// E.g. \"/\" (which serves the `IndexName` if not empty).\n\t//\n\t// Note: Requires running server under TLS,\n\t// that's why we use `iris.TLS` below.\n\t// PushTargets: map[string][]string{\n\t// \t\"/\": { // Relative path without prefix.\n\t// \t\t\"favicon.ico\",\n\t// \t\t\"js/main.js\",\n\t// \t\t\"css/main.css\",\n\t// \t\t// ^ Relative to the index, if need absolute ones start with a slash ('/').\n\t// \t},\n\t// },\n\t// OR use regexp:\n\tPushTargetsRegexp: map[string]*regexp.Regexp{\n\t\t// Match all js, css and ico files\n\t\t// from all files (recursively).\n\t\t// \"/\": regexp.MustCompile(\"((.*).js|(.*).css|(.*).ico)$\"),\n\t\t// OR:\n\t\t\"/\":              iris.MatchCommonAssets,\n\t\t\"/app2/app2app3\": iris.MatchCommonAssets,\n\t},\n\tCompress: true,\n\tShowList: true,\n}\n\nfunc main() {\n\tapp := iris.New()\n\tapp.HandleDir(\"/public\", iris.Dir(\"./assets\"), opts)\n\n\t// Open your browser's Network tools,\n\t// navigate to https://127.0.0.1/public.\n\t// you should see `Initiator` tab:  \"Push / public\".\n\t//\n\t// https://127.0.0.1/public\n\t// https://127.0.0.1/public/app2\n\t// https://127.0.0.1/public/app2/app2app3\n\t// https://127.0.0.1/public/app2/app2app3/dirs\n\tapp.Run(iris.TLS(\":443\", \"mycert.crt\", \"mykey.key\"))\n}\n"
  },
  {
    "path": "_examples/file-server/http2push/mycert.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIIFazCCA1OgAwIBAgIUfwMd9auWixp19UnXOmyxJ9Jkv7IwDQYJKoZIhvcNAQEL\nBQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM\nGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMDA2MjUwOTUxNDdaFw0yMTA2\nMjUwOTUxNDdaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw\nHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggIiMA0GCSqGSIb3DQEB\nAQUAA4ICDwAwggIKAoICAQDlVGyGAQ9uyfNbwZyrtYOSjLpxf5NpNToh2OzU7gy2\nOexBji5lmWBQ3oYDG+FjAkbHORPzOMNpeMwje+IjGZBw8x6E+8WoGdSzbrEZ6pUV\nwKJGKEuDlx6g6HEmtv3ZwgGe20gvPjjW+oCO888dwK/mbIHrHTq4nO3o0gAdAJwu\namn9BlHU5O4RW7BQ4tLF+j/fBCACWRG1NHXA0AT8eg544GyCdyteAH11oCDsHS8/\nDAPsM6t+tZrMCIt9+9dzPdVoOmQNaMMrcz8eJohddRTK6zHe9ixZTt/soayOF7OS\nQQeekbr3HPYhD450zRVplLMHx7wnph/+O+Po6bqDnUzdnkqAAwwymQapHMuHXZKN\nrhdfKau3rVo1GeXLIRgeWLUoxFSm4TYshrgt+0AidLRH+dCY7MS9Ngga/sAK3vID\ngSF75mFgOhY+q7nvY9Ecao6TnoNNRY29hUat4y0VwSyysUy887vHr6lMK5CrAT/l\nCh8fuu20HUCoiLwMJvA6+wpivZkuiIvWY7bVGYsEYrrW+bCNN9wCGYTZEyX++os9\nv/38wdOqGUT00ewXkjIUFCWbrnxxSr98kF3w3wPf9K4Y40MNxeR90nyX4zjXGF1/\n91msUh+iivsz9mcN9DK83fgTyOsoVLX5cm/L2UBwMacsfjBbN4djOc5IuYMar/VN\nGQIDAQABo1MwUTAdBgNVHQ4EFgQUtkf+yAvqgZC8f22iJny9hFEDolMwHwYDVR0j\nBBgwFoAUtkf+yAvqgZC8f22iJny9hFEDolMwDwYDVR0TAQH/BAUwAwEB/zANBgkq\nhkiG9w0BAQsFAAOCAgEAE2QasBVru618rxupyJgEHw6r4iv7sz1Afz3Q5qJ4oSA9\nxVsrVCjr3iHRFSw8Rf670E8Ffk/JjzS65mHw6zeZj/ANBKQWLjRlqzYXeetq5HzG\nSIgaG7p1RFvvzz3+leFGzjinZ6sKbfB4OB72o2YN+fO8DsDxgGKll0W4KAazizSe\nHY9Pgu437tWnwF16rFO3IL47n5HzYlRoGIPOpzFoNX5+fyn9GlnKEtONF2QBKTjY\nrdjvqFRByDiC74d8z/Yx8IiDRn1mTcG90JLR9+c6M7fruha9Y/rJfw+4AhVh5ZDz\nBl9rGPjwEs5zwutYvVAJzs7AVcighYP1lHKoJ7DxBDQeyBsYlUNk2l6bmZgLgGUZ\n+2OyWlqc/jD2GdDsIaZ4i7QqhTI/6aYZIf5zUkblKV1aMSaDulKxRv//OwW28Jax\n9EEoV7VaFb3sOkB/tZGhusXeQVtdrhahT3KkZLNwmNXoXWKJ5LjeUlFWJyV6JbDe\ny/PIWWCwWqyuFCSZS+Cg3RDgAzfSxkI8uVZ+IKKJS3UluDX45lxXtbRrvTQ+oDrA\n6ga5c1Vz9C4kn1K5yW4d7QIvg6vPiy7gvl+//sz9oxUM3yswInDBY0HKLgT0Uq9b\nYzLDh2RSaHsgHMPy2BKqR+q2N+lpg7inAWuJM1Huq6eHFqhiyQkzsfscBd1Dpm8=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "_examples/file-server/http2push/mykey.key",
    "content": "-----BEGIN PRIVATE KEY-----\nMIIJRAIBADANBgkqhkiG9w0BAQEFAASCCS4wggkqAgEAAoICAQDlVGyGAQ9uyfNb\nwZyrtYOSjLpxf5NpNToh2OzU7gy2OexBji5lmWBQ3oYDG+FjAkbHORPzOMNpeMwj\ne+IjGZBw8x6E+8WoGdSzbrEZ6pUVwKJGKEuDlx6g6HEmtv3ZwgGe20gvPjjW+oCO\n888dwK/mbIHrHTq4nO3o0gAdAJwuamn9BlHU5O4RW7BQ4tLF+j/fBCACWRG1NHXA\n0AT8eg544GyCdyteAH11oCDsHS8/DAPsM6t+tZrMCIt9+9dzPdVoOmQNaMMrcz8e\nJohddRTK6zHe9ixZTt/soayOF7OSQQeekbr3HPYhD450zRVplLMHx7wnph/+O+Po\n6bqDnUzdnkqAAwwymQapHMuHXZKNrhdfKau3rVo1GeXLIRgeWLUoxFSm4TYshrgt\n+0AidLRH+dCY7MS9Ngga/sAK3vIDgSF75mFgOhY+q7nvY9Ecao6TnoNNRY29hUat\n4y0VwSyysUy887vHr6lMK5CrAT/lCh8fuu20HUCoiLwMJvA6+wpivZkuiIvWY7bV\nGYsEYrrW+bCNN9wCGYTZEyX++os9v/38wdOqGUT00ewXkjIUFCWbrnxxSr98kF3w\n3wPf9K4Y40MNxeR90nyX4zjXGF1/91msUh+iivsz9mcN9DK83fgTyOsoVLX5cm/L\n2UBwMacsfjBbN4djOc5IuYMar/VNGQIDAQABAoICAQCtWx1SSxjkcerxsLEDKApW\nzOTfiUXgoOjZz0ZwS6b2VWDfyWAPU1r4ps39KaU+F+lzDhWjpYQqhbMjG7G9QMTs\nbQvkEQLAaQ5duU5NPgQG1oCUsj8rMSBpGGz4jBnm834QHMk7VTjYYbKu3WTyo8cU\nU2/+UDEkfxRlC+IkCmMFv1FxgMZ5PbktC/eDnYMhP2Pq7Q5ZWAVHymk9IMK0LHwm\nKdg842K4A3zTXwGkGwetDCMm+YQpG5TxqX/w82BRcCuTR5h8fnYSsWLEIvKwWyIl\nppcjaUnrFPG2yhxLqWUIKPpehuEjjhQMt9rDNoh6MHsJZZY5Dp5eq91EIvLoLQ99\nhXBmD4P8LDop4r0jniPZJi/ACsaD0jBooA4525+Kouq7RP28Jp/pek7lVOOcBgRv\nD3zyESbKfqoaOfyfQ2ff4sILnTAr4V2nq3ekphGEYJrWN0ZoADcLdnr1cZ8L+VBI\no/4mi5/3HID/UEDliHSa97hxxGBEqTto0ZuXuNwfwx5ho33uVT6zNwRgiJ62Bgu3\nFhk/wVGuZxWvb1KHUNInG9cvsslhO4Vu9wJvYj91BnRq36rsyKKid5DrU+PNgmog\nlw3IXQpTojyRCYPuG9TKqEZ6b+so7GTKhBOjiwaupMOletVRGSAdbE81VN6HtxNW\naj39+FnxzMAlsieib+PBAQKCAQEA+t1fOYSaZBo7pZUmo2S0zulUEJjrYRGKJlWJ\n4psWSwFu/7/3UL4q0RBQaSRew9u/YSpaNlBYfcpnFVOjiLwHq5Hx46Eq0BuKsNlJ\n1/qxw9qjHqcrOre6K4/7NaWLPuM9fEmV+3MhFVXgv+WC5BHOowRTlOG30vIcC1J2\nL5xsBUsxDDY13cD1bLKRmFcyMFM8y7wMZmo7H/WfVmyoPKQaC43pTcmIXH0Jr2Ws\nWsfh18mhjtamaOPEFx5K0x4d0PI8tW5ouiUUkVIDaue27XfS969qEChv768/44eX\nWeqcekaG9jv2noMClt79rYd3Lne9HkgY6IT9FT+JqXfu+KYwuQKCAQEA6gYzUsGB\n9GQO8DE8AYn7JwNOtg1X4zKakXiGxH+nuZb7wJjAeGdYqTHySxPBXg0A2nDwoyz5\n4sAdLAr3FZoIvTzo7M5KIKFDzfyDmQDavhroH1mBAEiqKGNniP+RND3nWBBqDK1R\nqcqbhI3Kj5Ycany6a4nP+hZRBIyT9sfJ0S0YruSY8IGXgDwhlJrZ7bsWMZylrgD/\n1qnPL0KqVBY8YR8msRj88h72IlD5o0kwvisOIvyhA0YgwGBb6lg7A+DifiF03ZlS\n2yELbIkKDVr+p3jC7MBh4B+OJY68AMl6wVjAaDM1AZnpjKE5YmZg5+Ks5823zILo\nPrSB9hn0+DIPYQKCAQEAh9x+JuNmzhHa/dkiHNl8hpadHYQD7gUWwZ4P1/bQAv0a\nxU2MvmDPRXxFYDv/SqlnI1NRmhq3YiDM5SLv7SyQJt4al4IAcsaHvTFgqaSuw3hU\nYVR9uAYqwE7w6OPn3r4o3Xfoz05Ru4FP//1nfucZ9vVv4rC/4nGWuJcHRM+9PLy1\nKnztfVR0VlL7QPrwRnW99kS4nnqn3K4khiTAlF73cAyCLsuXmydoqGIzDtMzv68G\nXRpo82NvHmoccevcj/2w3T2XYECWvAEjsrEdQ8xiKBwLIAcWYEOUIUCcumiyKBKs\nIwzkioI/U8AeuO0lobfdZ1n6i2sCuZA4mNxIQseWmQKCAQEA5YkfXdQeuq5JWJ1x\n1bCYfjNoSHfd9CH2KSimRqVOxWGpm8Y3QeFbvNgYZjsCNlVauOZ9oA7FKfp0onY+\n0xk56SKM83eCjW6fKrK6AKAt7LhHZDhNpxGek+6r5luE+FCfUGkJG1YD+x2WW/UW\n8K6zQF8GGeQZ8Zlh7axUlIBxGpG43BGrUHpLNqPD7BXWGq6dnhufBYRFay8y34/r\nsH3+yuPa92ki7/geQppZwCZRgLSKMRbIdoWaKhZZEQlpGOzCOiRmk9OGyRcoNVRU\nX7UYgPqZdc1cMo/AxGWzULJNjMaYMZvIKcHkqOKZfkIcWlSictn7pMPhN1+k+NWM\nyMORAQKCAQAyXl02h/c2ihx6cjKlnNeDr2ZfzkoiAvFuKaoAR+KVvb9F9X7ZgKSi\nwudZyelTglIVCYXeRmG09uX3rNGCzFrweRwgn6x/8DnN5pMRJVZOXFdgR+V9uKep\nK6F7DYbPyggvLOAsezB+09i9lwxM+XdA2whVpL5NFR1rGfFglnE1EQHcEvNONkcv\n0h8x9cNSptJyRDLiTIKI9EhonuzwzkGpvjULQE8MLbT8PbjoLFINcE9ZWhwtyw0V\nXO32KE8iLKt3KzHz9CfTRCI3M7DwD752AC6zRr8ZS/HXzs+5WTkdVVEtRC7Abd3y\nW2TzuSMYNDu876twbTVQJED3mwOAQ3J7\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "_examples/file-server/http2push-embedded/bindata.go",
    "content": "// Code generated by go-bindata. (@generated) DO NOT EDIT.\n\n// Package main generated by go-bindata.// sources:\n// ../http2push/assets/app2/app2app3/css/main.css\n// ../http2push/assets/app2/app2app3/dirs/dir1/text.txt\n// ../http2push/assets/app2/app2app3/dirs/dir2/text.txt\n// ../http2push/assets/app2/app2app3/dirs/text.txt\n// ../http2push/assets/app2/app2app3/index.html\n// ../http2push/assets/app2/index.html\n// ../http2push/assets/app2/mydir/text.txt\n// ../http2push/assets/css/main.css\n// ../http2push/assets/favicon.ico\n// ../http2push/assets/index.html\n// ../http2push/assets/js/main.js\npackage main\n\nimport (\n\t\"bytes\"\n\t\"compress/gzip\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"time\"\n)\n\nfunc bindataRead(data, name string) ([]byte, error) {\n\tgz, err := gzip.NewReader(strings.NewReader(data))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"read %q: %v\", name, err)\n\t}\n\n\tvar buf bytes.Buffer\n\t_, err = io.Copy(&buf, gz)\n\tclErr := gz.Close()\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"read %q: %v\", name, err)\n\t}\n\tif clErr != nil {\n\t\treturn nil, err\n\t}\n\n\treturn buf.Bytes(), nil\n}\n\ntype asset struct {\n\tbytes []byte\n\tinfo  os.FileInfo\n}\n\ntype bindataFileInfo struct {\n\tname    string\n\tsize    int64\n\tmode    os.FileMode\n\tmodTime time.Time\n}\n\n// Name return file name\nfunc (fi bindataFileInfo) Name() string {\n\treturn fi.name\n}\n\n// Size return file size\nfunc (fi bindataFileInfo) Size() int64 {\n\treturn fi.size\n}\n\n// Mode return file mode\nfunc (fi bindataFileInfo) Mode() os.FileMode {\n\treturn fi.mode\n}\n\n// ModTime return file modify time\nfunc (fi bindataFileInfo) ModTime() time.Time {\n\treturn fi.modTime\n}\n\n// IsDir return file whether a directory\nfunc (fi bindataFileInfo) IsDir() bool {\n\treturn fi.mode&os.ModeDir != 0\n}\n\n// Sys return file is sys mode\nfunc (fi bindataFileInfo) Sys() any {\n\treturn nil\n}\n\ntype assetFile struct {\n\t*bytes.Reader\n\tname            string\n\tchildInfos      []os.FileInfo\n\tchildInfoOffset int\n}\n\ntype assetOperator struct{}\n\n// Open implement http.FileSystem interface\nfunc (f *assetOperator) Open(name string) (http.File, error) {\n\tvar err error\n\tif len(name) > 0 && name[0] == '/' {\n\t\tname = name[1:]\n\t}\n\tcontent, err := Asset(name)\n\tif err == nil {\n\t\treturn &assetFile{name: name, Reader: bytes.NewReader(content)}, nil\n\t}\n\tchildren, err := AssetDir(name)\n\tif err == nil {\n\t\tchildInfos := make([]os.FileInfo, 0, len(children))\n\t\tfor _, child := range children {\n\t\t\tchildPath := filepath.Join(name, child)\n\t\t\tinfo, errInfo := AssetInfo(filepath.Join(name, child))\n\t\t\tif errInfo == nil {\n\t\t\t\tchildInfos = append(childInfos, info)\n\t\t\t} else {\n\t\t\t\tchildInfos = append(childInfos, newDirFileInfo(childPath))\n\t\t\t}\n\t\t}\n\t\treturn &assetFile{name: name, childInfos: childInfos}, nil\n\t} else {\n\t\t// If the error is not found, return an error that will\n\t\t// result in a 404 error. Otherwise the server returns\n\t\t// a 500 error for files not found.\n\t\tif strings.Contains(err.Error(), \"not found\") {\n\t\t\treturn nil, os.ErrNotExist\n\t\t}\n\t\treturn nil, err\n\t}\n}\n\n// Close no need do anything\nfunc (f *assetFile) Close() error {\n\treturn nil\n}\n\n// Readdir read dir's children file info\nfunc (f *assetFile) Readdir(count int) ([]os.FileInfo, error) {\n\tif len(f.childInfos) == 0 {\n\t\treturn nil, os.ErrNotExist\n\t}\n\tif count <= 0 {\n\t\treturn f.childInfos, nil\n\t}\n\tif f.childInfoOffset+count > len(f.childInfos) {\n\t\tcount = len(f.childInfos) - f.childInfoOffset\n\t}\n\toffset := f.childInfoOffset\n\tf.childInfoOffset += count\n\treturn f.childInfos[offset : offset+count], nil\n}\n\n// Stat read file info from asset item\nfunc (f *assetFile) Stat() (os.FileInfo, error) {\n\tif len(f.childInfos) != 0 {\n\t\treturn newDirFileInfo(f.name), nil\n\t}\n\treturn AssetInfo(f.name)\n}\n\n// newDirFileInfo return default dir file info\nfunc newDirFileInfo(name string) os.FileInfo {\n\treturn &bindataFileInfo{\n\t\tname:    name,\n\t\tsize:    0,\n\t\tmode:    os.FileMode(2147484068), // equal os.FileMode(0644)|os.ModeDir\n\t\tmodTime: time.Time{}}\n}\n\n// AssetFile return a http.FileSystem instance that data backend by asset\nfunc AssetFile() http.FileSystem {\n\treturn &assetOperator{}\n}\n\nvar _app2App2app3CssMainCss = \"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\x4a\\xca\\x4f\\xa9\\x54\\xa8\\xe6\\xe5\\x52\\x50\\x50\\x50\\x48\\x4a\\x4c\\xce\\x4e\\x2f\\xca\\x2f\\xcd\\x4b\\xd1\\x4d\\xce\\xcf\\xc9\\x2f\\xb2\\x52\\x48\\xca\\x29\\x4d\\xb5\\xe6\\xe5\\xaa\\x05\\x04\\x00\\x00\\xff\\xff\\x52\\xd7\\xbb\\x8b\\x26\\x00\\x00\\x00\"\n\nfunc app2App2app3CssMainCssBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_app2App2app3CssMainCss,\n\t\t\"app2/app2app3/css/main.css\",\n\t)\n}\n\nfunc app2App2app3CssMainCss() (*asset, error) {\n\tbytes, err := app2App2app3CssMainCssBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"app2/app2app3/css/main.css\", size: 38, mode: os.FileMode(438), modTime: time.Unix(1595043712, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\nvar _app2App2app3DirsDir1TextTxt = \"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\x4a\\x2c\\x28\\x30\\xd2\\x07\\x11\\x89\\x05\\x05\\xc6\\xfa\\x29\\x99\\x45\\xc5\\x20\\xc2\\x50\\xbf\\x24\\xb5\\xa2\\x44\\xaf\\xa4\\xa2\\x04\\x10\\x00\\x00\\xff\\xff\\x87\\xaf\\x9d\\x00\\x20\\x00\\x00\\x00\"\n\nfunc app2App2app3DirsDir1TextTxtBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_app2App2app3DirsDir1TextTxt,\n\t\t\"app2/app2app3/dirs/dir1/text.txt\",\n\t)\n}\n\nfunc app2App2app3DirsDir1TextTxt() (*asset, error) {\n\tbytes, err := app2App2app3DirsDir1TextTxtBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"app2/app2app3/dirs/dir1/text.txt\", size: 32, mode: os.FileMode(438), modTime: time.Unix(1594843207, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\nvar _app2App2app3DirsDir2TextTxt = \"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\x4a\\x2c\\x28\\x30\\xd2\\x07\\x11\\x89\\x05\\x05\\xc6\\xfa\\x29\\x99\\x45\\xc5\\x20\\xc2\\x48\\xbf\\x24\\xb5\\xa2\\x44\\xaf\\xa4\\xa2\\x04\\x10\\x00\\x00\\xff\\xff\\x84\\x14\\xaa\\xeb\\x20\\x00\\x00\\x00\"\n\nfunc app2App2app3DirsDir2TextTxtBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_app2App2app3DirsDir2TextTxt,\n\t\t\"app2/app2app3/dirs/dir2/text.txt\",\n\t)\n}\n\nfunc app2App2app3DirsDir2TextTxt() (*asset, error) {\n\tbytes, err := app2App2app3DirsDir2TextTxtBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"app2/app2app3/dirs/dir2/text.txt\", size: 32, mode: os.FileMode(438), modTime: time.Unix(1594843207, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\nvar _app2App2app3DirsTextTxt = \"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\x4a\\x2c\\x28\\x30\\xd2\\x07\\x11\\x89\\x05\\x05\\xc6\\xfa\\x29\\x99\\x45\\xc5\\xfa\\x25\\xa9\\x15\\x25\\x7a\\x25\\x15\\x25\\x80\\x00\\x00\\x00\\xff\\xff\\x64\\xfe\\x96\\xd6\\x1b\\x00\\x00\\x00\"\n\nfunc app2App2app3DirsTextTxtBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_app2App2app3DirsTextTxt,\n\t\t\"app2/app2app3/dirs/text.txt\",\n\t)\n}\n\nfunc app2App2app3DirsTextTxt() (*asset, error) {\n\tbytes, err := app2App2app3DirsTextTxtBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"app2/app2app3/dirs/text.txt\", size: 27, mode: os.FileMode(438), modTime: time.Unix(1594843207, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\nvar _app2App2app3IndexHtml = \"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\x54\\x90\\x3f\\x4f\\xc3\\x40\\x0c\\xc5\\xf7\\x48\\xf9\\x0e\\xc6\\x33\\xed\\x91\\x76\\x61\\xb8\\x8b\\x54\\xf1\\x47\\x6c\\x30\\x94\\x81\\xf1\\x7a\\x31\\x9c\\x85\\x73\\x39\\xe5\\x4c\\x4b\\xbf\\x3d\\x4a\\x68\\x90\\x58\\x6c\\x3d\\xfb\\xbd\\x9f\\x64\\xdb\\xab\\xfb\\xe7\\xbb\\xfd\\xdb\\xcb\\x03\\x44\\xed\\xa5\\xad\\x2b\\x3b\\x75\\x10\\x9f\\x3e\\x1c\\x52\\xc2\\xb6\\xae\\xa6\\x19\\xf9\\xae\\xad\\x2b\\x00\\x00\\xdb\\x93\\x7a\\x08\\xd1\\x8f\\x85\\xd4\\xe1\\xeb\\xfe\\x71\\x75\\x8b\\xff\\x76\\xc9\\xf7\\xe4\\xf0\\xc8\\x74\\xca\\xc3\\xa8\\x08\\x61\\x48\\x4a\\x49\\x1d\\x9e\\xb8\\xd3\\xe8\\x3a\\x3a\\x72\\xa0\\xd5\\x2c\\xae\\x81\\x13\\x2b\\x7b\\x59\\x95\\xe0\\x85\\x5c\\xb3\\xbe\\xf9\\x63\\x09\\xa7\\x4f\\x18\\x49\\x1c\\x16\\x3d\\x0b\\x95\\x48\\xa4\\x08\\x71\\xa4\\x77\\x87\\x26\\x7f\\x1d\\x84\\x83\\xf1\\x39\\x6f\\xe6\\xe2\\x73\\xde\\x9a\\x50\\x8a\\xe9\\x3d\\xa7\\x75\\x28\\x05\\xc1\\x2c\\x20\\x65\\x15\\x6a\\x77\\x39\\x6f\\x76\\x39\\x6f\\xad\\xf9\\xd5\\x75\\x65\\xcd\\xe5\\xac\\xba\\xb2\\x87\\xa1\\x3b\\x2f\\xfe\\xd8\\xb4\\x4f\\x24\\x32\\xc0\\x12\\x01\\x4e\\x1d\\x7d\\x5b\\x13\\x9b\\x39\\x75\\xf1\\xce\\x80\\xe9\\x67\\x3f\\x01\\x00\\x00\\xff\\xff\\xef\\x25\\x54\\xc8\\x43\\x01\\x00\\x00\"\n\nfunc app2App2app3IndexHtmlBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_app2App2app3IndexHtml,\n\t\t\"app2/app2app3/index.html\",\n\t)\n}\n\nfunc app2App2app3IndexHtml() (*asset, error) {\n\tbytes, err := app2App2app3IndexHtmlBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"app2/app2app3/index.html\", size: 323, mode: os.FileMode(438), modTime: time.Unix(1595043725, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\nvar _app2IndexHtml = \"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\xb2\\xc9\\x30\\xb4\\xf3\\x48\\xcd\\xc9\\xc9\\x57\\x70\\x2c\\x28\\x30\\x52\\xc8\\xcc\\x4b\\x49\\xad\\xb0\\xd1\\xcf\\x30\\xb4\\x03\\x04\\x00\\x00\\xff\\xff\\x75\\x17\\xab\\xfa\\x19\\x00\\x00\\x00\"\n\nfunc app2IndexHtmlBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_app2IndexHtml,\n\t\t\"app2/index.html\",\n\t)\n}\n\nfunc app2IndexHtml() (*asset, error) {\n\tbytes, err := app2IndexHtmlBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"app2/index.html\", size: 25, mode: os.FileMode(438), modTime: time.Unix(1565946440, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\nvar _app2MydirTextTxt = \"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\xca\\x2a\\x2d\\x2e\\x51\\x48\\x54\\x28\\x49\\xad\\x28\\xd1\\x03\\x04\\x00\\x00\\xff\\xff\\x2f\\xf9\\x22\\x98\\x0c\\x00\\x00\\x00\"\n\nfunc app2MydirTextTxtBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_app2MydirTextTxt,\n\t\t\"app2/mydir/text.txt\",\n\t)\n}\n\nfunc app2MydirTextTxt() (*asset, error) {\n\tbytes, err := app2MydirTextTxtBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"app2/mydir/text.txt\", size: 12, mode: os.FileMode(438), modTime: time.Unix(1594787248, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\nvar _cssMainCss = \"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\x4a\\xca\\x4f\\xa9\\x54\\xa8\\xe6\\xe5\\x52\\x50\\x50\\x50\\x48\\x4a\\x4c\\xce\\x4e\\x2f\\xca\\x2f\\xcd\\x4b\\xd1\\x4d\\xce\\xcf\\xc9\\x2f\\xb2\\x52\\x48\\xca\\x49\\x4c\\xce\\xb6\\xe6\\xe5\\xaa\\xe5\\xe5\\x02\\x04\\x00\\x00\\xff\\xff\\x03\\x25\\x9c\\x89\\x29\\x00\\x00\\x00\"\n\nfunc cssMainCssBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_cssMainCss,\n\t\t\"css/main.css\",\n\t)\n}\n\nfunc cssMainCss() (*asset, error) {\n\tbytes, err := cssMainCssBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"css/main.css\", size: 41, mode: os.FileMode(438), modTime: time.Unix(1565946440, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\nvar _faviconIco = \"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\xec\\x5a\\x0b\\x70\\x55\\xc7\\x79\\xde\\x7b\\xce\\x45\\x12\\xb2\\x90\\xc4\\xc3\\xe6\\x61\\x3b\\x90\\xf8\\x31\\xc4\\x19\\x6c\\x32\\xe3\\xc4\\x34\\xe3\\xc6\\x34\\x6d\\xed\\xd4\\x69\\x62\\x32\\x49\\x9a\\xa6\\x75\\xea\\x36\\x33\\xee\\xd8\\x9e\\xb4\\x75\\xdc\\x7a\\xa6\\xc5\\x31\\x02\\xa6\\xd3\\x84\\x47\\x28\\x6f\\x3b\\xc6\\x3c\\xcc\\xeb\\x9e\\xbd\\x08\\x21\\x04\\x92\\x28\\x12\\x0e\\x08\\x5b\\x3c\\xec\\x02\\x92\\x78\\x08\\x21\\x09\\x41\\x25\\x24\\x10\\xe8\\x71\\xb5\\xe7\\xdc\\xd7\\xb9\\xf7\\xef\\xfc\\xff\\x9e\\x73\\x74\\x25\\xdd\\x97\\x24\\x82\\x33\\x1e\\xce\\xcc\\x3f\\x7b\\xee\\x9e\\xdd\\xff\\xff\\xf6\\xdf\\x7f\\xff\\xfd\\xf7\\xdf\\xcb\\x98\\x8b\\xa9\\x2c\\x3f\\x1f\\xcb\\x19\\xec\\x15\\x37\\x63\\x5f\\x67\\x8c\\xcd\\x98\\x21\\x7f\\x6b\\xf9\\x8c\\x6d\\x72\\x33\\x36\\x7b\\xb6\\xf5\\xfb\\x11\\xc6\\x9e\\xbd\\x97\\xb1\\x99\\x8c\\xb1\\x7c\\x6c\\xc7\\x64\\x3d\\x3d\\x6e\\x76\\xdb\\x1f\\xe1\\x75\\xab\\x42\\x53\\x7e\\x22\\x3c\\x6c\\x91\\xf0\\xb0\\x02\\x22\\xae\\x14\\x08\\x8f\\xab\\x40\\xec\\x64\\x92\\xf0\\x5d\\x53\\xfe\\x59\\xec\\x64\\x5f\\x15\\x1e\\xa6\\x08\\x4f\\x7f\\x7f\\xdf\\x7a\\x96\\xa1\\x97\\x3c\\xb8\\x3f\\x78\\xfa\\x0d\\x08\\x9e\\x5d\\x08\\xc1\\xda\\x5f\\x82\\x51\\xf6\\x65\\x30\\xca\\x67\\x41\\xf0\\xcc\\xbf\\x11\\x19\\x07\\x1e\\x07\\xbd\\x78\\x2a\\x18\\x65\\x8f\\xb5\\x0b\\x4d\\x7d\\xad\\x6f\\x07\\x73\\x0b\\xcd\\x45\\xfd\\xbb\\x17\\xb3\\x0c\\xa3\\x62\\xce\\xfe\\xa8\\xd1\\x0a\\xf8\\x98\\x6d\\x25\\x10\\x38\\xfe\\x53\\x88\\xdc\\x3a\\x01\\x66\\x6b\\x11\\x11\\xbe\\x07\\x8e\\xfd\\x2d\\x84\\x2e\\x2c\\x05\\xff\\xd1\\x79\\x3d\\xc2\\xc3\\x7e\\x20\\x78\\x06\\x13\\x5c\\x89\\xe9\\xdf\\x06\\xd1\\x50\\x2f\\x04\\x4e\\xfc\\x0c\\xcc\\x6b\\xa5\\x60\\x1c\\xfd\\x21\\x74\\x6c\\x1e\\x47\\x84\\xef\\x58\\x17\\xf8\\xe4\\x65\\x30\\x6f\\x54\\x81\\x71\\xe0\\x89\\xea\\xbe\\x2d\\x6c\\xd2\\x80\\xfe\\xfe\\x76\\x30\\x6f\\x1c\\x81\\x60\\xed\\x7c\\x08\\x9e\\x5d\\x0c\\x2d\\xeb\\xc7\\x41\\xed\\xd2\\x29\\x50\\xbb\\x74\\x32\\xbd\\x63\\x1d\\x7e\\xc3\\x36\\xa1\\x73\\xff\\x19\\x14\\x1e\\x36\\x4f\\x78\\xc7\\xc4\\xf4\\xef\\x80\\x70\\xd3\\xfb\\x10\\x6e\\x5c\\x0f\\xdd\\x65\\x73\\xe1\\xdc\\xf2\\x89\\xd0\\xb1\\x25\\x0f\\x6e\\x7c\\x90\\x07\\xe7\\x7f\\x33\\x81\\xea\\xf0\\x5b\\xb8\\x79\\x13\\x61\\xd0\\x8b\\xa7\\x2c\\x09\\x7c\\xfc\\x57\\xac\\x7b\\x11\\xf6\\x7f\\x6a\\x5f\\xd4\\xb8\\x06\\xa1\\xfa\\x15\\x10\\x6e\\x7a\\x0f\\x3a\\xb4\\xc7\\xa0\\x61\\x55\\x3e\\x04\\x8f\\xfc\\x39\\x84\\x3e\\xfa\\x4b\\x68\\x5c\\x93\\x4f\\x75\\xf8\\x2d\\x74\\x71\\x05\\x44\\x45\\x13\\xea\\x54\\xeb\\x7c\\x85\\xa9\\x48\\xf8\\x1e\\x15\\xcd\\xf4\\xcd\\xee\\x7f\\x71\\x65\\x3e\\x04\\x3e\\x7c\\x86\\x78\\x5c\\x5a\\x1d\\xd3\\xbf\\x7e\\x05\\xa0\\x2c\\x94\\x89\\xb2\\x03\\xd5\\x3f\\x66\\x88\\x05\\x31\\x21\\x36\\x1b\\xff\\xd9\\xe5\\x13\\xe1\\xda\\xc6\\x5c\\x68\\xdf\\x94\\x4b\\x63\\x71\\xf0\\x37\\xbd\\x0f\\x38\\x56\\x1c\\x33\\x8e\\x1d\\x75\\x80\\xba\\x40\\x9d\\x0c\\xd6\\x5f\\xcd\\x92\\x29\\x44\\x83\\xf5\\x87\\xba\\x76\\xfa\\x73\\x85\\xe1\\x5c\\x18\\x07\\x9e\\x38\\x86\\x18\\x68\\x8e\\xac\\xf9\\xbb\\xbe\\x39\\x87\\xc8\\x99\\xbf\\x13\\x3f\\xa3\\x39\\xc6\\xb9\\x8e\\xed\\x4f\\xb6\\xe0\\x61\\x3f\\x40\\xdb\\x40\\x1b\\x41\\x5b\\x19\\x62\\x3f\\xc7\\x7f\\x4a\\xb6\\x85\\x0f\\xda\\x9a\\xdd\\x9f\\xd6\\x80\\xe6\\x62\\xd2\\x26\\xd5\\xd7\\xd0\\x46\\xc9\\x56\\x0f\\x3c\\xde\\x6f\\xbf\\xe5\\xb3\\xc8\\xa6\\xd1\\xb6\\xc9\\xc6\\x4f\\xbf\\x01\\x68\\xf3\\x68\\xfb\\xce\\x3a\\xf2\\x30\\x26\\x76\\x32\\x85\\xd6\\x08\\xae\\x95\\xc1\\xeb\\x87\\xd6\\x14\\xb3\\x69\\x11\\xad\\x39\\xaf\\x5b\\x1d\\xed\\xfa\\x05\\x60\\x8c\\x65\\x32\\xc6\\x54\\x8b\\x5c\\x31\\x64\\x3d\\x0b\\x63\\xe8\\xb0\\x45\\x2d\\x56\\xdf\\x99\\x96\\x8f\\x99\\x1b\\xeb\\x67\\xf2\\x47\\x8b\\xea\\xf3\\xf9\\x08\\xae\\x22\\xe5\\x09\\xae\\x4e\\x17\\x5c\\xfd\\xc2\\x28\\x69\\x8a\\xe0\\xca\\x58\\xb4\\x3d\\xdb\\x17\\xa6\\x29\\xff\\x5f\\x04\\x57\\x5b\\x85\\xe6\\xba\\x92\\x90\\xb8\\x1a\\x43\\x4a\\x4c\\xbd\\x62\\xd7\\xb7\\x08\\xae\\xd6\\x0b\\xae\\xfe\\x8f\\xe0\\xea\\x9b\\x38\\x1e\\x1f\\x1f\\x4b\\xfe\\x34\\xb5\\x7c\\xa5\\x40\\xec\\xca\\x02\\xbd\\x64\\x3a\\xe8\\xfb\\xbe\\x38\\x94\\x4a\\xa6\\x83\\xf0\\x66\\x82\\xd0\\x14\\x10\\x9a\\x0b\\x44\\xe1\\x38\\x5c\\x2b\\x44\\xf8\\x4e\\x75\\x5c\\x01\\xe1\\x75\\x03\\xf2\\x11\\x9a\\x2b\\x2a\\xb8\\x52\\x2b\\xb8\\x3a\\x4f\\x68\\x8a\\x92\\x0c\\x03\\xc9\\xf7\\xb0\\x02\\xa3\\xfc\\x2b\\x10\\xe9\\xae\\x85\\xa8\\x7e\\x15\\xa2\\xfa\\x95\\x18\\xba\\x0a\\xa1\\xb3\\x8b\\x49\\xbe\\xbe\\xe7\\x5e\\x08\\xfe\\xef\\x3f\\x81\\xd9\\x51\\x09\\x91\\xbe\\x4b\\x44\\xf8\\x8e\\x75\\xf8\\x4d\\x70\\x37\\x04\\x3e\\xfe\\x11\\xf9\\x14\\xbd\\x68\\x3c\\xe2\\xe8\\x14\\x5c\\x7d\\x49\\x78\\x99\\x2b\\x11\\x06\\x47\\xfe\\x81\\xd9\\x10\\x0d\\xde\\x82\\xc1\\x8f\\x79\\xe3\\x30\\xe8\\x7b\\xa7\\x81\\x51\\xfa\\x28\\x98\\xd7\\xca\\x00\\xa2\\xa6\\xfc\\x80\\x65\\xcc\\x3b\\x7e\\xc3\\x36\\xfa\\xde\\x07\\x08\\x93\\xd9\\x5a\\x0c\\xfa\\xfe\\x87\\x41\\x68\\xac\\x43\\x70\\xf5\\x59\\x92\\xe3\\x1d\\xea\\x1a\\x92\\xc9\\x8f\\x06\\x3a\\xc1\\xff\\xe1\\x5c\\xda\\x67\\x91\\x27\\x3e\\x91\\xde\\x0b\\xe4\\xaf\\xfc\\x47\\x5f\\x20\\xc2\\x77\\xac\\x23\\xac\\x1d\\x95\\xd4\\xd6\\xff\\xe1\\x9f\\x10\\x2f\\xfa\\x5d\\xf2\\x05\\xc4\\x70\\x4c\\x70\\xf5\\x7e\\x94\\x35\\x1c\\xf9\\xa1\\xc6\\x77\\x68\\x3e\\x43\\x17\\x57\\x4a\\xfe\\xe8\\xc3\\xcb\\xbe\\x8c\\xfc\\xa0\\xcf\\x23\\x09\\xdf\\xb1\\x0e\\xbf\\x51\\x9f\\x8b\\x2b\\x65\\x9f\\xc6\\x77\\xe8\\x37\\xee\\x4f\\x62\\xd7\\x58\\xb4\\x8f\\x05\\x3e\\x9e\\x35\\xc4\\x1e\\x13\\xc9\\x8f\\x06\\xbb\\xc0\\xa8\\xfc\\x23\\xf0\\x1f\\xfa\\x63\\x88\\x86\\xba\\x21\\x72\\xeb\\x24\\x18\\xfb\\x1f\\x82\\xde\\x1d\\x2e\\x68\\xdd\\x30\\x0e\\x1a\\x56\\x4f\\x24\\xc2\\x77\\xac\\xc3\\x6f\\xd8\\x06\\xdb\\x62\\x1f\\xec\\x8b\\xef\\x10\\xd6\\xc1\\xff\\xd1\\xf7\\x11\\x67\\x83\\xe0\\xea\\x97\\x06\\xeb\\x20\\x91\\x7c\\xd2\\xdd\\xee\\x3c\\x08\\x37\\x6f\\x04\\x30\\x0d\\x08\\x7c\\x34\\x8f\\xe4\\x34\\xae\\x99\\x00\\xa7\\x7f\\x3d\\x0d\\x4e\\xfd\\xea\\x7e\\x22\\x7c\\xc7\\x3a\\xfc\\x86\\x6d\\xb0\\x6d\\xb8\\x69\\x23\\xf5\\xb5\\xe7\\xcc\\x6c\\x3f\\x00\\x62\\x77\\x6e\\x54\\x68\\xae\\x57\\xad\\xf5\\x96\\x52\\x7e\\xb0\\x6e\\x01\\xe8\\xfb\\x66\\xd0\\x1a\\x30\\xaf\\x95\\x83\\x28\\xcc\\x81\\x2b\\xef\\xe6\\x92\\x3c\\x24\\x8a\\x63\\x96\\x4d\\x71\\x7e\\xe3\\x37\\x6c\\x83\\x6d\\xb1\\x0f\\xf6\\x0d\\xd6\\x15\\x48\\x5d\\x86\\x7c\\x64\\x47\\xc2\\xc3\\xf6\\x0a\\xae\\x66\\xc6\\xea\\x20\\xae\\xfc\\x48\\x08\\xfc\\x55\\xdf\\x05\\x7f\\xd5\\x77\\x00\\x22\\x41\\x08\\x7e\\xfa\\x2a\\x74\\x6d\\x53\\xa1\\x6e\\xf9\\x64\\x92\\x75\\x69\\xcd\\x44\\xb8\\xb9\\x35\\x13\\x6e\\x6d\\xcb\\x80\\xe6\\x75\\xe3\\xa9\\x0e\\xbf\\x61\\x1b\\x6c\\x8b\\x7d\\xb0\\xaf\\xbf\\xea\\x7b\\xc4\\x8b\\xec\\xe2\\xec\\x22\\x9c\\x83\\x66\\xc1\\xd5\\x19\\xa9\\xe4\\xd3\\xdc\\x1f\\x98\\x0d\\xc1\\x33\\x6f\\x42\\x34\\xec\\x03\\x7f\\xe5\\x1c\\x68\\xdb\\x30\\x96\\xe4\\x9c\\xfb\\xcd\\x7d\\xd0\\xbd\\x63\\x0c\\xe8\\x85\\x63\\x41\\x2f\\xbc\\x07\\x7a\\x3d\\x6e\\xa8\\x5f\\x39\\x89\\xbe\\x61\\x1b\\x6c\\x8b\\x7d\\xb0\\xaf\\xe4\\xd9\\x65\\xd9\\x6e\\x19\\x88\\x5d\\xd9\\xba\\xd0\\x5c\\x73\\x53\\xca\\xd7\\xff\\x0f\\xf4\\xfd\\x0f\\x41\\xa8\\x61\\x35\\xf9\\x1f\\x7d\\xdf\\x74\\x68\\x5e\\x97\\x47\\xf3\\xdd\\xf2\\x4e\\x1e\\xc9\\x0d\\x9f\\xfc\\x3b\\x30\\x4f\\xbd\\x02\\xc6\\x9e\\x09\\xd0\\xba\\x21\\x07\\x4e\\xfd\\x6a\\x1a\\xb5\\xc1\\xb6\\xe4\\xb3\\x1a\\x56\\x13\\x0f\\xe4\\x25\\xd7\\xed\\x39\\x5c\\x9b\\x51\\xa1\\xb1\\x17\\xe3\\xc8\\x7f\\x0b\\x63\\x13\\x5c\\xef\\xd4\\xb6\\xaf\\x91\\x7c\\x6b\\xf8\\xf2\\x16\\x5a\\xdb\\x62\\xcf\\x64\\xb8\\xb4\\x7a\\x3c\\xc9\\x68\\xdb\\x90\\x0d\\xfe\\xb2\\x87\\x01\\x2e\\x2d\\x07\\x68\\x5a\\x05\\x81\\xca\\x27\\xe1\\xfa\\xe6\\x4c\\x39\\x2f\\xab\\xc7\\x53\\xdb\\x88\\xaf\\x9e\\xfa\\x22\\x0f\\xe4\\xe5\\xc4\\x47\\xa5\\x8f\\xa2\\x0d\\xfc\\x22\\x8e\\xfc\\x5f\\xe0\\x37\\x3b\\x5e\\x4f\\x2e\\xff\\x1e\\xf0\\x97\\x7e\\x11\\xa2\\x0d\\x4b\\x00\\x9a\\x56\\x42\\xe0\\xe0\\x13\\x70\\x7d\\x53\\x56\\x6a\\xf9\\x81\\x4e\\x8a\\xbf\\x70\\xac\\x43\\xe4\\x6b\\xec\\x45\\xd4\\x0d\\xea\\x28\\xbe\\xfe\\x67\\x38\\xfa\\x6f\\x5e\\x97\\x2f\\xfd\\xcb\\xd1\\xe7\\x21\\x74\\xec\\x87\\xa0\\xef\\xce\\x81\\xab\\xbf\\x1d\\x67\\x7d\\xcb\\xb3\\xd6\\xcc\\x50\\xfd\\xe3\\xdc\\xe2\\x1c\\xcb\\xb3\\xd0\\x60\\xf9\\xae\\xb9\\x68\\x1b\\xe4\\xdf\\x53\\xd8\\x1f\\xda\\x39\\xda\\xbe\\xee\\x55\\x41\\xf7\\xba\\xc9\\x16\\xcf\\xaf\\xb8\\x2f\\xa5\\xfd\\x25\\x95\\x8f\\x6b\\x42\\x63\\xcd\\xb8\\xcf\\x25\\x5c\\x7f\\x5b\\xe5\\xfa\\xc3\\x39\\xb8\\xf0\\xdf\\xf7\\x42\\xfb\\xc6\\x7b\\xa0\\x63\\x53\\x36\\x5c\\x5c\\x35\\x29\\xad\\xf5\\x97\\x42\\x7e\\x26\\xfa\\x06\\xda\\x37\\xc2\\xbe\\xb4\\xfc\\x0f\\xd2\\x99\\x25\\xd3\\xd2\\xf2\\x3f\\xc9\\xe5\\x2b\\xf6\\x1c\\xbc\\x86\\x3e\\x12\\x7d\\xe5\\x50\\xff\\xeb\\x8f\\xe3\\x7f\\x25\\x0d\\xf5\\xbf\\xfe\\x21\\xfe\\x37\\x99\\xfc\\x18\\x1d\\x7c\\x09\\xf7\\x08\\xdc\\x2b\\xc0\\xd4\\x69\\xef\\x30\\x2a\\x46\\xb9\\xff\\x58\\x73\\x9f\\x5a\\xbe\\xc2\\x7c\\x1a\\xed\\x8d\\x0b\\x70\\xaf\\xc4\\x3d\\x53\\xee\\xbf\\xef\\x8e\\x7a\\xff\\x4d\\x47\\x7e\\x8c\\x0e\\xee\\xc7\\x58\\x01\\x63\\x06\\xd4\\x1d\\xf6\\x41\\x9b\\x18\\x51\\xfc\\x61\\xf9\\xb2\\xb4\\xe5\\x7b\\x55\\x1b\\xc3\\xb3\\x18\\x33\\x61\\xec\\x84\\x31\\x14\\xf1\\xdc\\xfb\\x40\\x5a\\xf1\\x97\\x4e\\xf1\\xd7\\x34\\x8a\\xd9\\x06\\x3f\\xa9\\xe4\\x3b\\xb6\\x48\\xb1\\xa2\\xfa\\x12\\xc6\\x8e\\x18\\x43\\x62\\x2c\\x89\\x31\\x25\\xc6\\x96\\x29\\xe3\\x4f\\x6f\\x26\\xc5\\xaa\\xf1\\x62\\x58\\x8c\\x6d\\x31\\xc6\\x4d\\x26\\xbf\\x7f\\x3d\\x60\\xcc\\xac\\xce\\xa3\\x18\\x1a\\x63\\x69\\x8c\\xa9\\xb9\\x5b\\xc6\\xd8\\x14\\x7f\\xe7\\xc4\\xc4\\xdf\\x39\\xb2\\x0e\\x63\\x73\\x8c\\x91\\x93\\xc5\\xf0\\xc4\\x47\\x49\\x2a\\x9f\\x30\\x68\\x0a\\xf3\\x15\\x65\\x31\\x6b\\xaf\\x7e\\xd3\\x3a\\x53\\xd4\\x5b\\x67\\x8c\\x44\\xe7\\x0f\\x49\\xc9\\xcf\\x30\\xad\\xd6\\x59\\x27\\xa9\\xfc\\x58\\x5d\\xf4\\x69\\x14\\x2f\\x8d\\x95\\x67\\xab\\x51\\x9f\\xcf\\xa6\\x5b\\x67\\xbd\\xb4\\xe4\\x7f\\xde\\x1f\\x7b\\x6d\\x1c\\x66\\x2a\\x1c\\x66\\x0c\\xe9\\x99\\xc3\\x8c\\x4d\\xb7\\x28\\xcf\\xa2\\x4c\\x8b\\xd4\\x74\\xa8\\xc5\\xa2\\x1e\\x8b\\x02\\x16\\x99\\x8c\\xa9\\xc0\\x48\\x90\\x6a\\xcb\\x9d\\xc9\\x18\\x9b\\xcd\\x18\\xfb\\xfb\\xd8\\x3c\\xc5\\xc3\\x9f\\xb5\\x56\\xee\\x3e\\x77\\x9f\\x3f\\xcc\\xc7\\xda\\x1f\\x1f\\x16\\x5c\\x7d\\x5b\\x70\\x75\\xa1\\xe0\\x6a\\xc1\\xef\\x89\\x6c\\xde\\xff\\x2a\\xb8\\xfa\\x37\\x82\\x2b\\xb3\\x05\\x57\\x72\\x7a\\x35\\xc6\\x0c\\x2d\\x79\\x3e\\x29\\x0d\\xfc\\xdf\\x16\\x5c\\x0d\\x0a\\xae\\xc2\\x1d\\x22\\x53\\x70\\xb5\\x53\\x70\\xf5\\x20\\xed\\xeb\\x5c\\xc9\\xc7\\x58\\xc3\\x48\\x33\\x3f\\x97\\x18\\xbf\\x42\\xb1\\x57\\x5a\\x84\\x6d\\x07\\x60\\x4a\\xd2\\x37\\x6e\\x5b\\x97\\xfd\\x1b\\xf5\\x56\\x2a\\xb8\\xfa\\x24\\xec\\x67\\x4c\\xe7\\xc3\\x1b\\x83\\x83\\x5f\\x73\\x05\\x31\\xc6\\x0b\\xd6\\xbe\\x45\\xe7\\xf2\\x64\\x14\\x3c\\xbb\\x08\\x8c\\x8a\\xa7\\xfa\\x71\\x21\\xc6\\xc2\\x1c\\x30\\x0e\\x7e\\x0d\\x02\\x27\\xfe\\x81\\x78\\x20\\xe1\\x3b\\xd6\\xc9\\x78\\x84\\x39\\xd8\\xf5\\xe2\\x29\\x60\\x94\\xcd\\x04\\xe1\\xcd\\x8a\\x1d\\x47\\xa3\\xe0\\xea\\x0b\\x7e\\xce\\x5c\\x7a\\x8a\\xfc\\x64\\x7c\\xfc\\x2c\\x48\\xf1\\x75\\xb8\\x6f\\x48\\x9c\\x37\\xf8\\x31\\xdb\\x4a\\x64\\x9c\\x84\\xb2\\xbd\\x19\\xe0\\x3f\\xf2\\x17\\x60\\xb6\\xee\\x85\\x68\\xe0\\x26\\x40\\x34\\x12\\x13\\x20\\x46\\xa8\\x0e\\xbf\\x61\\x1b\\x6c\\x8b\\x7d\\xf4\\xe2\\xc9\\x10\\x6a\\x58\\x0b\\xe1\\xcb\\x5b\\xc1\\xa8\\xfc\\x46\\xff\\x9c\\x70\\xb5\\x0d\\xc7\\xd0\\xa3\\x65\\x33\\x91\\xe6\\x3c\\x0c\\x17\\x7f\\xa4\\xeb\\x14\\xc5\\xb4\\xc2\\xc3\\xe8\\x3c\\x43\\xb1\\x65\\xb0\\x3b\\xe5\\x98\\xb1\\x0d\\xb6\\xc5\\x3e\\xf2\\x7c\\x30\\x13\\x22\\xdd\\x35\\x10\\xf5\\x5f\\xa7\\x73\\xa9\\x28\\xbc\\xc7\\x1e\\x43\\x93\\xe0\\xca\\x53\\x14\\x73\\x16\\xa6\\x8e\\x89\\x86\\x83\\x9f\\xce\\x41\\x55\\xdf\\x23\\xec\\x62\\x77\\x2e\\x9d\\xc7\\xed\\xb3\\xa8\\x1c\\x5c\\x10\\xa2\\xa2\\x05\\x22\\xb7\\x3e\\x21\\xc2\\x77\\xac\\xeb\\xff\\x1e\\xa2\\x3e\\xd8\\x17\\x79\\x20\\xaf\\x68\\xa8\\x87\\xda\\x84\\xea\\x97\\xcb\\xbc\\xb3\\x1c\\x43\\xa5\\xe0\\xea\\xe4\\x74\\x62\\xba\\xe1\\xe0\\x0f\\x35\\xac\\x91\\x36\\xc0\\xdd\\x10\\xac\\x7b\\xbb\\x1f\\x7b\\x34\\x0c\\x66\\x47\\x05\\xdd\\x3d\\xe9\\xa5\\x8f\\x80\\x5e\\x34\\x51\\x52\\xe9\\x23\\xf2\\x3e\\xaa\\xa3\\x82\\xda\\xd8\\x63\\x08\\xd6\\xfe\\x52\\xc6\\xee\\xde\\x0c\\xe2\\x69\\x8f\\x1d\\xcf\\x4d\\x92\\xbf\\x1a\\x45\\x7f\\xeb\\xf3\\x32\\x57\\x5f\\x0a\\x3b\\x4a\\x17\\x3f\\xea\\xd2\\x28\\x9f\\x25\\xf5\\x76\\xe4\\x3b\\xfd\\x79\\xad\\x70\\x1f\\x84\\xce\\xff\\x17\\xe8\\x7b\\x26\\x59\\xfe\\xc6\\xca\\xdb\\xdb\\x3e\\x46\\x63\\xf4\\x0d\\xdb\\xd8\\xbc\\xe9\\x0c\\x78\\xe4\\x79\\xe2\\x85\\x3c\\x69\\x9e\\x68\\x7e\\x7b\\x21\\x50\\xfd\\x63\\x7b\\x4d\\x5f\\x13\\x5c\\xfd\\x7a\\xca\\x73\\x45\\x9a\\xf8\\x69\\x7e\\xad\\xb5\\x67\\xde\\xa8\\xea\\xd7\\x59\\xdd\\x02\\x79\\x0f\\xc1\\xa5\\x1f\\xe9\\xd9\\x39\\x06\\x6e\\x6e\\xcd\\x22\\xc2\\x77\\xb9\\x36\\x5d\\xd4\\x06\\xdb\\xda\\xf6\\x64\\xde\\x38\\x42\\xbc\\x90\\x27\\xf2\\x76\\x4c\\xac\\xbb\\x86\\x72\\x44\\xd6\\x18\\xde\\xd3\\xb9\\xcb\\x9d\\xcc\\x1f\\xa5\\x83\\x9f\\xf2\\x5c\\x15\\x73\\x48\\x5f\\x81\\x4f\\x5f\\x75\\xce\\xa8\\xe1\\xcb\\x5b\\x1c\\x9b\\x45\\xac\\x57\\xde\\xcd\\xa3\\x5c\\x66\\xcd\\xd2\\x29\\x44\\xf8\\x8e\\x75\\x72\\x1c\\x0a\\xb5\\xc5\\x3e\\x92\\xa9\\x49\\xbc\\x68\\x0e\\x2a\\xe6\\x0c\\x38\\x5b\\x87\\x2e\\x2c\\xb1\\xce\\x86\\x6a\\xbb\\xe0\\xea\\x57\\x93\\x9f\\x2d\\x53\\xe3\\xa7\\x5c\\xcf\\xae\\x6c\\xb2\\x03\\xb3\\xb3\\x5a\\xea\\xa9\\xaf\\x51\\x9e\\x5d\\x35\\x06\\xdd\\xdb\\x33\\x9c\\x9c\\x53\\x3c\\xc2\\x6f\\xd8\\x86\\xfc\\x4e\\xf9\\x57\\x9c\\xfc\\x9f\\xd9\\xf9\\xb1\\xb4\\xbb\\xc2\\x6c\\x30\\xdb\\xcb\\xfb\\xf5\\xa5\\x5f\\x95\\x79\\x41\\xb9\\x67\\x2c\\x12\\xdc\\x9d\\xf0\\xce\\x2a\\x1d\\xfc\\xe4\\xdf\\xc8\\x5f\\x7c\\x97\\x72\\xf1\\x54\\x57\\xf3\\xef\\x64\\x17\\xbd\\x1e\\x37\\xe5\\x6c\\x12\\x61\\xb7\\x09\\xdb\\x60\\x5b\\xec\\x83\\x7d\\xe5\\x00\\x0c\\xe2\\x89\\xbc\\x51\\xc6\\x00\\x99\\x75\\x0b\\x6d\\x1b\\x3a\\x2e\\xb8\\x32\\x61\\xa4\\xf8\\xf1\\x37\\xe5\\xe9\\x35\\x06\\xa1\\x86\\x55\\x96\\x7e\\x5a\\xc0\\x28\\x9d\\x49\\x75\\xad\\xef\\x8d\\x1b\\x82\\xb5\\x76\\xd9\\xc0\\x7b\\x00\\x9b\\xb0\\xad\\xed\\xfb\\xa3\\xfa\\x15\\x69\\x2b\\x17\\x57\\x51\\x1d\\xca\\x88\\x95\\x8d\\xfe\\x57\\xde\\x9f\\x29\\x3e\\xc1\\xd5\\x6f\\x26\\xce\\x8f\\x24\\xc7\\x1f\\xf1\\x35\\x80\\x5e\\xf2\\x00\\xe8\\xbb\\xf3\\x21\\x72\\xf3\\x98\\x65\\xf7\\x1f\\x80\\xf0\\x8e\\x81\\x9e\\x1d\\x63\\xc8\\xc6\\x6d\\x7c\\x35\\x4b\\xa7\\x92\\xbd\\xdf\\xda\\x96\\x09\\x5d\\xdb\\x33\\xa1\\x6d\\x43\\x8e\\x73\\xbf\\x60\\xe7\\xf9\\xb1\\x0f\\xfa\\x48\\xdc\\x7b\\x89\\xff\\xcd\\x6a\\xe2\\x4d\\x79\\x65\\x5f\\xc3\\x40\\xbd\\xfd\\xee\\x5b\\xb6\\x0d\\x51\\x7e\\x3b\\xde\\x3a\\x4e\\x85\\xdf\\xec\\x38\\x44\\xb6\\x6f\\x94\\x3d\\x46\\xff\\x67\\xc0\\x27\\x70\\xf2\\x65\\xe2\\x7b\\xed\\xfd\\x9c\\x98\\xbc\\xe7\\x54\\xa9\\x5f\\x27\\x16\\x50\\x40\\xe7\\x2a\\x5c\\xdf\\x9c\\x4d\\xf7\\x32\\x76\\x3b\\xec\\x83\\x7d\\x03\\x9f\\xfc\\xa3\\xc4\\xe9\\x6f\\x27\\xde\\x28\\x03\\x65\\x0d\\xb0\\xa1\\x9a\\xff\\xb0\\xf1\\x6f\\xd3\\xb9\\x2b\\xee\\x9d\\x69\\x2a\\xfc\\xa4\\x6b\\x9c\\xdf\\xc3\\xcf\\x01\\x44\\x02\\x94\\xcb\\x35\\x2a\\x9f\\xa6\\xba\\xa6\\xb5\\x13\\x9c\\x7c\\x29\\xae\\x51\\x9f\\x47\\xe6\\x93\\xf4\\xc2\\x6c\\xd0\\x77\\x8f\\x73\\xe2\\xcd\\xcb\\xeb\\xf3\\x9d\\xfc\\x2a\\xf6\\x21\\x7e\\x87\\x9e\\x96\\x79\\xe1\\x48\\x80\\x78\\x63\\x1d\\xca\\x1a\\x20\\xfb\\x2a\\xb7\\xf7\\xb3\\x6a\\xc1\\x95\\xdc\\x91\\xe0\\x0f\\x5d\\x58\\x26\\xfd\\xe6\\xf1\\x97\\xa4\\xbe\\xc4\\x65\\xca\\x5d\\xf5\\xee\\x54\\x29\\xe7\\x6d\\xeb\\xb5\\x0d\\xf5\\xca\\x5d\\x60\\xec\\x9d\\x4a\\x77\\x30\\x91\\xda\\x37\\xc0\\x5f\\x3e\\x93\\xc6\\xd3\\xf9\\xc1\\x58\\xb2\\x2d\\x3b\\x4f\\xee\\xdb\\xa9\\x12\\x0f\\xe4\\x45\\xf3\\x79\\xfc\\x25\\x92\\x81\\xb2\\x06\\xd8\\xee\\xcd\\x13\\xf2\\xce\\x98\\x2b\\x4d\\x82\\xab\\x0f\\x26\\xc9\\x91\\x3e\\x97\\x08\\x3f\\xed\\x4f\\x3b\\x19\\x04\\x4f\\xbd\\x2e\\x79\\xf6\\xd4\\xd2\\xbe\\xd3\\xbd\\x7d\\x0c\\x9c\\xb5\\x6c\\x1b\\xb1\\xe1\\x7e\\xa5\\x7b\\x55\\x08\\x1d\\xfb\\x11\\x40\\xf3\\x5a\\x22\\xf3\\xf4\\xcf\\x69\\x2e\\xd0\\xff\\xa3\\xed\\x23\\x7e\\xec\\xd3\\xbd\\xdd\\x4d\\x31\\x74\\xa4\\xa7\\x4e\\xca\\x38\\xf5\\xba\\x94\\x81\\xfb\\x5b\\xac\\xef\\x10\\xcd\\x74\\xf7\\x64\\xdd\\x79\\xcf\\x4a\\x82\\xff\\x5b\\x42\\x63\\x86\\x71\\xf0\\xc9\\x21\\xb1\\x24\\xf9\\x4e\\xe4\\x5d\\x3b\\xdf\\xd2\\xc9\\x71\\xd0\\x8b\\xf2\\xa1\\x6b\\x5b\\x86\\xe3\\x63\\xb0\\xec\\xda\\x9e\\x41\\xf7\\x6f\\x66\\xcd\\xeb\\x00\\xcd\\x6b\\x00\\x9a\\x56\\x43\\xb4\\x7e\\x31\\x18\\x7b\\x27\\x83\\xcf\\x23\\xe7\\x0a\\xf1\\x3b\\x6d\\x8b\\xf2\\xe9\\xff\\x3a\\x24\\xa3\\x76\\xbe\\x94\\x31\\xc8\\x87\\x62\\x6c\\x4a\\x31\\x8b\\xc6\\xfa\\x04\\x57\\x9f\\x4e\\x82\\xff\\x69\\x6c\\x43\\xb1\\x88\\xff\\xfa\\xc8\\xf0\\x6f\\xcb\\x00\\x7d\\x57\\x16\\x98\\x67\\x7e\\xde\\x8f\\xff\\x42\\x01\\x18\\xc5\\x93\\x08\\xff\\xf9\\x91\\xe0\\x0f\\x76\\x03\\xea\\x14\\x75\\x4b\\x3a\\x4e\\x8c\\x7f\\x16\\xe5\\x96\\xf7\\xcd\\xa0\\x39\\x4b\\x6e\\x3f\\x75\\x34\\xf7\\x68\\x03\\xb6\\xfd\\xa0\\xef\\xb9\\xb1\\x85\\xee\\xd3\\x21\\x78\\xe4\\xcf\\xe8\\x0e\\x10\\xed\\x27\\x7c\\xe2\\x45\\xd0\\xbd\\x19\\xd0\\xbd\\x23\\x83\\xda\\x0e\\xdb\\x7e\\xc2\\x7d\\x74\\x67\\x81\\xb6\\x4d\\x36\\x9e\\x18\\xff\\x83\\xb8\\x46\\xf4\\xa2\\x09\\x74\\xdf\\x31\\x92\\xf5\\x7b\\xf5\\xb7\\xb9\\x96\\xef\\xc9\\x81\\x60\\xd5\\x73\\x10\\xaa\\xfe\\x3e\\xe8\\xc5\\xf7\\x82\\xce\\x15\\xba\\xd3\\xc2\\x31\\x9e\\xfa\\xb5\\xbd\\x7e\\x95\\xb4\\xd6\\xef\\x20\\xfc\\xdf\\x8e\\x8f\\x9f\\x72\\xeb\\xb9\\xe4\\xa3\\x70\\x5f\\xb9\\xca\\x07\\xf0\\x48\\xd7\\x7f\\x9e\\x5b\\x81\\x7b\\x53\\x46\\x7f\\xec\\x6c\\x91\\x4f\\xeb\\x8f\\x2f\\xb0\\x6d\\xe3\\x30\\xfc\\x67\\xba\\xf8\\xe5\\xde\\xa0\\x6e\\x47\\x1e\\xc1\\x9a\\xf9\\x03\\x78\\xa4\\xbb\\x7f\\x21\\x35\\xad\\x1b\\x0f\\xbd\\x9e\\x31\\xd6\\x3d\\xa6\\x0a\\x7d\\x9a\\x9b\\xe6\\x05\\x75\\x3f\\x64\\xff\\x3a\\xf9\\xb2\\xb5\\x46\\x13\\xef\\x5f\\xe9\\xe0\\xd7\\xed\\xbb\\x3e\\xdc\\xa3\\x51\\x2f\\xbf\\xfb\\xd3\\x81\\x71\\x48\\xdc\\xf8\\x61\\x6b\\xdc\\xf8\\xc1\\xde\\xc7\\x30\\x6e\\x40\\x9c\\xa8\\xeb\\x58\\xec\\xc3\\x89\\x1f\\xd2\\xc5\\x1f\\xb3\\x06\\xbe\\x29\\xb8\\xd2\\xa7\\xef\\xb9\\x0f\\x22\\x5d\\x9f\\x0e\\xe0\\xe1\\xc4\\x6f\\x17\\xd3\\x8b\\xdf\\x12\\xd1\\x70\\xe2\\xb7\\xe1\\xe1\\xa7\\x35\\x30\\x81\\x62\\x55\\xcd\\x45\\xe7\\xd0\\xd8\\xe7\\xb3\\x88\\x9f\\x87\\x85\\x5f\\x53\\x18\\xfd\\x3f\\x95\\xab\\x8b\\xe5\\x19\\xe3\\x71\\x3a\\x43\\x38\\x6b\\xe0\\xf7\\x76\\x7e\\xa9\\x8e\\x7b\\x7e\\x19\\x2e\\xfe\\x18\\x1b\\xc2\\xb3\\x5a\\x07\\x9e\\xdd\\xf0\\x0c\\xe7\\xf0\\xb9\\x23\\xe7\\xc7\\x9b\\xa3\\xc4\\x4f\\x7e\\xc8\\x8d\\x67\\x66\\x3a\\xa7\\xef\\x7f\\x88\\xce\\xd2\\xf6\\x93\\xf6\\xf9\\x7d\\x47\\xcc\\xf9\\x7d\\x47\\xb2\\xf3\\x7b\\x55\\xdc\\xf3\\xfb\\x48\\xf1\\xc7\\xcc\\xc1\\xd7\\x28\\x77\\x81\\x3e\\xae\\xfa\\xaf\\x29\\xa7\\x01\\x43\\xf2\\x27\\xcf\\x0f\\xcc\\x9f\\x9c\\xbb\\x3d\\xf9\\x93\\xd1\\xe2\\xef\\xe3\\x2e\\xe6\\xe3\\x4c\\x91\\x67\\x66\\x35\\x8a\\x7e\\x8e\\xd6\\xb2\\xa5\\xb3\\xd4\\xf9\\xab\\x17\\xc1\\x28\\x7d\\x04\\xf4\\x3d\\x13\\x89\\x8c\\x44\\xf9\\xab\\xba\\xb7\\x87\\xe6\\xaf\\x6e\\x03\\xfe\\x98\\x39\\x98\\x2c\\xb8\\x7a\\xc8\\xb6\\xd9\\x50\\xfd\\x32\\x99\\x1b\\x0c\\xf5\\x38\\xfe\\xe2\\xf6\\xe5\\x0f\\x13\\xe7\\x4e\\x47\\x84\\xbf\\x50\\xb5\\xfd\\xe9\\x1c\\x2b\\x97\\x4a\\xb9\\x55\\xfa\\xdf\\x50\\xe0\\x3a\\x44\\xba\\xcf\\xc8\\xdc\\x37\\xda\\x8a\\x9d\\xbf\\x4d\\x82\\xc1\\xc1\\x62\\xe5\\x6f\\xb1\\x0f\\xe5\\x7e\\x4b\\x1f\\xa5\\x5c\\x70\\xd2\\x3e\\x23\\xc0\\x4f\\x63\\xf0\\xba\\x58\\x8f\\x27\\x1b\\xe7\\xe1\\x05\\x99\\xd3\\x56\\x68\\xbe\\x8d\\xca\\x6f\\xd0\\xbe\\x19\\x6a\\x58\\xeb\\xac\\x3d\\x27\\x7f\\xde\\x56\\x92\\x7e\\xfe\\xbc\\xe4\\x41\\x30\\xdb\\xf6\\xa5\\x1e\\xf3\\x08\\xf1\\xe3\\xa3\\x7b\\x15\\x66\\xec\\xa2\\xff\\x6d\\xbc\\x60\\xdd\\x2d\\x48\\xbc\\xbb\\xb2\\xe8\\xbf\\x31\\x7a\\xf1\\xd4\\x24\\xf7\\x17\\xf3\\x89\\x12\\xdd\\x5f\\x18\\x15\\x4f\\xd1\\x1d\\x48\\xca\\x7b\\x92\\xda\\xb7\\xac\\xff\\xda\\xba\\x86\\x8d\\x9f\\xc6\\xc0\\x5d\\x0c\\xbc\\xe4\\x5b\\xd1\\x27\\x95\\x39\\x77\\x64\\x8e\\x8f\\x19\\xcd\\xfd\\xd1\\xb0\\xee\\xa9\\x46\\x84\\x1f\\x1f\\x43\\x73\\xd9\\x6b\\x7a\\xbc\\xbc\\x6b\\xa3\\x3b\\xb7\\x4e\\xeb\\x0e\\xee\\x4e\\xdd\\xf7\\x8d\\x18\\xbf\\x33\\x0e\\x8f\\xc2\\x7a\\x8b\\x54\\x8c\\x35\\x72\\x04\\x57\\x67\\x0b\\xae\\xfe\\xc4\\xba\\x0b\\x2d\\xb8\\x03\\xf7\\xae\\x6f\\x5b\\x77\\xbc\\x23\\xc6\\x7f\\xf7\\xb9\\xfb\\x7c\\x9e\\x1e\\xb9\\x43\\x24\\x2e\\x5b\\x18\\x63\\xcf\\x58\\x65\\x9e\\x55\\x66\\x5a\\xa5\\x6b\\x50\\xc9\\xec\\xb2\\xc0\\x2a\\x9f\\x19\\x54\\x4e\\x4f\\x50\\xe6\\x25\\x28\\x33\\x6f\\x5f\\xd9\\x93\\xa0\\x0c\\x24\\x28\\xcd\\x41\\x65\\xd4\\x2a\\xc1\\x2e\\x17\\x0e\\x2a\\x5b\\xac\\xb2\\xc7\\x2a\\x4d\\xab\\x4c\\xa1\\xdf\\xff\\x0f\\x00\\x00\\xff\\xff\\xc6\\xb9\\x24\\x2f\\xee\\x3a\\x00\\x00\"\n\nfunc faviconIcoBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_faviconIco,\n\t\t\"favicon.ico\",\n\t)\n}\n\nfunc faviconIco() (*asset, error) {\n\tbytes, err := faviconIcoBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"favicon.ico\", size: 15086, mode: os.FileMode(438), modTime: time.Unix(1565946440, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\nvar _indexHtml = \"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\x64\\x91\\x3d\\x4f\\xc3\\x30\\x10\\x86\\xf7\\x48\\xf9\\x0f\\xd7\\x9b\\x40\\x22\\x35\\x6c\\x0c\\x71\\x96\\x42\\x57\\x90\\x28\\x03\\xa3\\xeb\\x5c\\x9b\\x6b\\x1d\\x27\\xb2\\x2f\\x69\\xfb\\xef\\x51\\x3e\\xca\\xe7\\x64\\xdf\\xeb\\xbb\\xc7\\x8f\\xec\\x7c\\xf1\\xf4\\xb2\\xda\\x7c\\xbc\\x3e\\x43\\x25\\xb5\\x2b\\xd2\\x24\\x1f\\x56\\x70\\xc6\\xef\\x35\\x92\\xc7\\x22\\x4d\\x86\\x8c\\x4c\\x59\\xa4\\x09\\x00\\x40\\x5e\\x93\\x18\\xb0\\x95\\x09\\x91\\x44\\xe3\\xfb\\x66\\x9d\\x3d\\xe2\\xaf\\x33\\x6f\\x6a\\xd2\\xd8\\x33\\x9d\\xda\\x26\\x08\\x82\\x6d\\xbc\\x90\\x17\\x8d\\x27\\x2e\\xa5\\xd2\\x25\\xf5\\x6c\\x29\\x1b\\x8b\\x3b\\x60\\xcf\\xc2\\xc6\\x65\\xd1\\x1a\\x47\\xfa\\x61\\x79\\xff\\xc5\\x72\\xec\\x8f\\x10\\xc8\\x69\\x64\\xdb\\x78\\x84\\x2a\\xd0\\x4e\\xa3\\x6a\\xbb\\xad\\x63\\xab\\x76\\xa6\\x1f\\xe2\\x25\\xdb\\x06\\x41\\x2e\\x2d\\x69\\xe4\\xda\\xec\\x49\\x9d\\xb3\\xa9\\x5d\\xfd\\xe7\\x44\\xb9\\x38\\x8a\\x15\\x91\\xfc\\xa5\\xd9\\x18\\x55\\x6d\\xd8\\x2f\\x6d\\x8c\\x3f\\x46\\x85\\xc5\\x51\\xb1\\x66\\x47\\xf0\\x46\\xa1\\xa7\\x90\\xab\\x29\\x4a\\x93\\x5c\\xcd\\x6f\\x92\\x26\\xf9\\xb6\\x29\\x2f\\xd7\\x11\\xf6\\x6d\\x27\\xb3\\xd0\\xb6\\x13\\x19\\x54\\x1a\\x6f\\x1d\\xdb\\xa3\\xc6\\xc6\\xaf\\x86\\xcd\\xcd\\x2d\\x42\\x6f\\x5c\\x47\\x1a\\xc7\\x1a\\x6a\\x5a\\x4c\\xb7\\xce\\x90\\x68\\x03\\xb7\\x57\\x8a\\xd0\\x59\\xd4\\xc1\\xf4\\x66\\x4a\\x11\\x62\\xb0\\xdf\\xe6\\x87\\x59\\xfc\\x10\\xb1\\xc8\\xd5\\xd4\\x32\\xea\\xcd\\x52\\xa3\\xe9\\xf0\\xb3\\x9f\\x01\\x00\\x00\\xff\\xff\\x00\\xcf\\x96\\xa8\\xe9\\x01\\x00\\x00\"\n\nfunc indexHtmlBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_indexHtml,\n\t\t\"index.html\",\n\t)\n}\n\nfunc indexHtml() (*asset, error) {\n\tbytes, err := indexHtmlBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"index.html\", size: 489, mode: os.FileMode(438), modTime: time.Unix(1594886229, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\nvar _jsMainJs = \"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\x4a\\xce\\xcf\\x2b\\xce\\xcf\\x49\\xd5\\xcb\\xc9\\x4f\\xd7\\x50\\x4a\\xad\\x48\\xcc\\x2d\\xc8\\x49\\x55\\xd2\\xb4\\xe6\\xe5\\xe2\\xe5\\x4a\\x2b\\xcd\\x4b\\x2e\\xc9\\xcc\\xcf\\x53\\xc8\\xcf\\x73\\xce\\xc9\\x4c\\xce\\xd6\\xd0\\x54\\xa8\\xe6\\xe5\\x52\\x50\\x50\\x50\\x28\\xcf\\xcc\\x4b\\xc9\\x2f\\xd7\\x4b\\xcc\\x49\\x2d\\x2a\\xd1\\x50\\x4a\\x2a\\x2d\\x29\\xc9\\xcf\\x53\\x48\\x06\\xa9\\x49\\x4d\\x01\\x6b\\xae\\x05\\x04\\x00\\x00\\xff\\xff\\xa4\\xb7\\x99\\x52\\x57\\x00\\x00\\x00\"\n\nfunc jsMainJsBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_jsMainJs,\n\t\t\"js/main.js\",\n\t)\n}\n\nfunc jsMainJs() (*asset, error) {\n\tbytes, err := jsMainJsBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"js/main.js\", size: 87, mode: os.FileMode(438), modTime: time.Unix(1594787764, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\n// Asset loads and returns the asset for the given name.\n// It returns an error if the asset could not be found or\n// could not be loaded.\nfunc Asset(name string) ([]byte, error) {\n\tcannonicalName := strings.Replace(name, \"\\\\\", \"/\", -1)\n\tif f, ok := _bindata[cannonicalName]; ok {\n\t\ta, err := f()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"Asset %s can't read by error: %v\", name, err)\n\t\t}\n\t\treturn a.bytes, nil\n\t}\n\treturn nil, fmt.Errorf(\"Asset %s not found\", name)\n}\n\n// MustAsset is like Asset but panics when Asset would return an error.\n// It simplifies safe initialization of global variables.\nfunc MustAsset(name string) []byte {\n\ta, err := Asset(name)\n\tif err != nil {\n\t\tpanic(\"asset: Asset(\" + name + \"): \" + err.Error())\n\t}\n\n\treturn a\n}\n\n// AssetInfo loads and returns the asset info for the given name.\n// It returns an error if the asset could not be found or\n// could not be loaded.\nfunc AssetInfo(name string) (os.FileInfo, error) {\n\tcannonicalName := strings.Replace(name, \"\\\\\", \"/\", -1)\n\tif f, ok := _bindata[cannonicalName]; ok {\n\t\ta, err := f()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"AssetInfo %s can't read by error: %v\", name, err)\n\t\t}\n\t\treturn a.info, nil\n\t}\n\treturn nil, fmt.Errorf(\"AssetInfo %s not found\", name)\n}\n\n// AssetNames returns the names of the assets.\nfunc AssetNames() []string {\n\tnames := make([]string, 0, len(_bindata))\n\tfor name := range _bindata {\n\t\tnames = append(names, name)\n\t}\n\treturn names\n}\n\n// _bindata is a table, holding each asset generator, mapped to its name.\nvar _bindata = map[string]func() (*asset, error){\n\t\"app2/app2app3/css/main.css\":       app2App2app3CssMainCss,\n\t\"app2/app2app3/dirs/dir1/text.txt\": app2App2app3DirsDir1TextTxt,\n\t\"app2/app2app3/dirs/dir2/text.txt\": app2App2app3DirsDir2TextTxt,\n\t\"app2/app2app3/dirs/text.txt\":      app2App2app3DirsTextTxt,\n\t\"app2/app2app3/index.html\":         app2App2app3IndexHtml,\n\t\"app2/index.html\":                  app2IndexHtml,\n\t\"app2/mydir/text.txt\":              app2MydirTextTxt,\n\t\"css/main.css\":                     cssMainCss,\n\t\"favicon.ico\":                      faviconIco,\n\t\"index.html\":                       indexHtml,\n\t\"js/main.js\":                       jsMainJs,\n}\n\n// AssetDir returns the file names below a certain\n// directory embedded in the file by go-bindata.\n// For example if you run go-bindata on data/... and data contains the\n// following hierarchy:\n//\n//\tdata/\n//\t  foo.txt\n//\t  img/\n//\t    a.png\n//\t    b.png\n//\n// then AssetDir(\"data\") would return []string{\"foo.txt\", \"img\"}\n// AssetDir(\"data/img\") would return []string{\"a.png\", \"b.png\"}\n// AssetDir(\"foo.txt\") and AssetDir(\"notexist\") would return an error\n// AssetDir(\"\") will return []string{\"data\"}.\nfunc AssetDir(name string) ([]string, error) {\n\tnode := _bintree\n\tif len(name) != 0 {\n\t\tcannonicalName := strings.Replace(name, \"\\\\\", \"/\", -1)\n\t\tpathList := strings.Split(cannonicalName, \"/\")\n\t\tfor _, p := range pathList {\n\t\t\tnode = node.Children[p]\n\t\t\tif node == nil {\n\t\t\t\treturn nil, fmt.Errorf(\"Asset %s not found\", name)\n\t\t\t}\n\t\t}\n\t}\n\tif node.Func != nil {\n\t\treturn nil, fmt.Errorf(\"Asset %s not found\", name)\n\t}\n\trv := make([]string, 0, len(node.Children))\n\tfor childName := range node.Children {\n\t\trv = append(rv, childName)\n\t}\n\treturn rv, nil\n}\n\ntype bintree struct {\n\tFunc     func() (*asset, error)\n\tChildren map[string]*bintree\n}\n\nvar _bintree = &bintree{nil, map[string]*bintree{\n\t\"app2\": {nil, map[string]*bintree{\n\t\t\"app2app3\": {nil, map[string]*bintree{\n\t\t\t\"css\": {nil, map[string]*bintree{\n\t\t\t\t\"main.css\": {app2App2app3CssMainCss, map[string]*bintree{}},\n\t\t\t}},\n\t\t\t\"dirs\": {nil, map[string]*bintree{\n\t\t\t\t\"dir1\": {nil, map[string]*bintree{\n\t\t\t\t\t\"text.txt\": {app2App2app3DirsDir1TextTxt, map[string]*bintree{}},\n\t\t\t\t}},\n\t\t\t\t\"dir2\": {nil, map[string]*bintree{\n\t\t\t\t\t\"text.txt\": {app2App2app3DirsDir2TextTxt, map[string]*bintree{}},\n\t\t\t\t}},\n\t\t\t\t\"text.txt\": {app2App2app3DirsTextTxt, map[string]*bintree{}},\n\t\t\t}},\n\t\t\t\"index.html\": {app2App2app3IndexHtml, map[string]*bintree{}},\n\t\t}},\n\t\t\"index.html\": {app2IndexHtml, map[string]*bintree{}},\n\t\t\"mydir\": {nil, map[string]*bintree{\n\t\t\t\"text.txt\": {app2MydirTextTxt, map[string]*bintree{}},\n\t\t}},\n\t}},\n\t\"css\": {nil, map[string]*bintree{\n\t\t\"main.css\": {cssMainCss, map[string]*bintree{}},\n\t}},\n\t\"favicon.ico\": {faviconIco, map[string]*bintree{}},\n\t\"index.html\":  {indexHtml, map[string]*bintree{}},\n\t\"js\": {nil, map[string]*bintree{\n\t\t\"main.js\": {jsMainJs, map[string]*bintree{}},\n\t}},\n}}\n\n// RestoreAsset restores an asset under the given directory\nfunc RestoreAsset(dir, name string) error {\n\tdata, err := Asset(name)\n\tif err != nil {\n\t\treturn err\n\t}\n\tinfo, err := AssetInfo(name)\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = os.WriteFile(_filePath(dir, name), data, info.Mode())\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// RestoreAssets restores an asset under the given directory recursively\nfunc RestoreAssets(dir, name string) error {\n\tchildren, err := AssetDir(name)\n\t// File\n\tif err != nil {\n\t\treturn RestoreAsset(dir, name)\n\t}\n\t// Dir\n\tfor _, child := range children {\n\t\terr = RestoreAssets(dir, filepath.Join(name, child))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc _filePath(dir, name string) string {\n\tcannonicalName := strings.Replace(name, \"\\\\\", \"/\", -1)\n\treturn filepath.Join(append([]string{dir}, strings.Split(cannonicalName, \"/\")...)...)\n}\n"
  },
  {
    "path": "_examples/file-server/http2push-embedded/main.go",
    "content": "package main\n\nimport (\n\t\"regexp\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\n// How to run:\n// $ go install github.com/go-bindata/go-bindata/v3/go-bindata@latesta\n// $ go-bindata -nomemcopy -fs -prefix \"../http2push/assets\" ../http2push/assets/...\n// # OR if the ./assets directory was inside this example foder:\n// # go-bindata -nomemcopy -refix \"assets\" ./assets/...\n//\n// $ go run .\n// Physical files are not used, you can delete the \"assets\" folder and run the example.\n\nvar opts = iris.DirOptions{\n\tIndexName: \"index.html\",\n\tPushTargetsRegexp: map[string]*regexp.Regexp{\n\t\t\"/\":              iris.MatchCommonAssets,\n\t\t\"/app2/app2app3\": iris.MatchCommonAssets,\n\t},\n\tCompress: false,\n\tShowList: true,\n}\n\nfunc main() {\n\tapp := iris.New()\n\tapp.HandleDir(\"/public\", AssetFile(), opts)\n\n\t// https://127.0.0.1/public\n\t// https://127.0.0.1/public/app2\n\t// https://127.0.0.1/public/app2/app2app3\n\t// https://127.0.0.1/public/app2/app2app3/dirs\n\tapp.Run(iris.TLS(\":443\", \"../http2push/mycert.crt\", \"../http2push/mykey.key\"))\n}\n"
  },
  {
    "path": "_examples/file-server/http2push-embedded-gzipped/bindata.go",
    "content": "// Code generated by go-bindata. (@generated) DO NOT EDIT.\n\n// Package main generated by go-bindata.// sources:\n// ../http2push/assets/app2/app2app3/css/main.css\n// ../http2push/assets/app2/app2app3/dirs/dir1/text.txt\n// ../http2push/assets/app2/app2app3/dirs/dir2/text.txt\n// ../http2push/assets/app2/app2app3/dirs/text.txt\n// ../http2push/assets/app2/app2app3/index.html\n// ../http2push/assets/app2/index.html\n// ../http2push/assets/app2/mydir/text.txt\n// ../http2push/assets/css/main.css\n// ../http2push/assets/favicon.ico\n// ../http2push/assets/index.html\n// ../http2push/assets/js/main.js\npackage main\n\nimport (\n\t\"bytes\"\n\t\"compress/gzip\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"time\"\n)\n\nfunc bindataRead(data, name string) ([]byte, error) {\n\tgz, err := gzip.NewReader(strings.NewReader(data))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"read %q: %v\", name, err)\n\t}\n\n\tvar buf bytes.Buffer\n\t_, err = io.Copy(&buf, gz)\n\tclErr := gz.Close()\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"read %q: %v\", name, err)\n\t}\n\tif clErr != nil {\n\t\treturn nil, err\n\t}\n\n\treturn buf.Bytes(), nil\n}\n\ntype asset struct {\n\tbytes []byte\n\tinfo  os.FileInfo\n}\n\ntype bindataFileInfo struct {\n\tname    string\n\tsize    int64\n\tmode    os.FileMode\n\tmodTime time.Time\n}\n\n// Name return file name\nfunc (fi bindataFileInfo) Name() string {\n\treturn fi.name\n}\n\n// Size return file size\nfunc (fi bindataFileInfo) Size() int64 {\n\treturn fi.size\n}\n\n// Mode return file mode\nfunc (fi bindataFileInfo) Mode() os.FileMode {\n\treturn fi.mode\n}\n\n// ModTime return file modify time\nfunc (fi bindataFileInfo) ModTime() time.Time {\n\treturn fi.modTime\n}\n\n// IsDir return file whether a directory\nfunc (fi bindataFileInfo) IsDir() bool {\n\treturn fi.mode&os.ModeDir != 0\n}\n\n// Sys return file is sys mode\nfunc (fi bindataFileInfo) Sys() any {\n\treturn nil\n}\n\ntype assetFile struct {\n\t*bytes.Reader\n\tname            string\n\tchildInfos      []os.FileInfo\n\tchildInfoOffset int\n}\n\ntype assetOperator struct{}\n\n// Open implement http.FileSystem interface\nfunc (f *assetOperator) Open(name string) (http.File, error) {\n\tvar err error\n\tif len(name) > 0 && name[0] == '/' {\n\t\tname = name[1:]\n\t}\n\tcontent, err := Asset(name)\n\tif err == nil {\n\t\treturn &assetFile{name: name, Reader: bytes.NewReader(content)}, nil\n\t}\n\tchildren, err := AssetDir(name)\n\tif err == nil {\n\t\tchildInfos := make([]os.FileInfo, 0, len(children))\n\t\tfor _, child := range children {\n\t\t\tchildPath := filepath.Join(name, child)\n\t\t\tinfo, errInfo := AssetInfo(filepath.Join(name, child))\n\t\t\tif errInfo == nil {\n\t\t\t\tchildInfos = append(childInfos, info)\n\t\t\t} else {\n\t\t\t\tchildInfos = append(childInfos, newDirFileInfo(childPath))\n\t\t\t}\n\t\t}\n\t\treturn &assetFile{name: name, childInfos: childInfos}, nil\n\t} else {\n\t\t// If the error is not found, return an error that will\n\t\t// result in a 404 error. Otherwise the server returns\n\t\t// a 500 error for files not found.\n\t\tif strings.Contains(err.Error(), \"not found\") {\n\t\t\treturn nil, os.ErrNotExist\n\t\t}\n\t\treturn nil, err\n\t}\n}\n\n// Close no need do anything\nfunc (f *assetFile) Close() error {\n\treturn nil\n}\n\n// Readdir read dir's children file info\nfunc (f *assetFile) Readdir(count int) ([]os.FileInfo, error) {\n\tif len(f.childInfos) == 0 {\n\t\treturn nil, os.ErrNotExist\n\t}\n\tif count <= 0 {\n\t\treturn f.childInfos, nil\n\t}\n\tif f.childInfoOffset+count > len(f.childInfos) {\n\t\tcount = len(f.childInfos) - f.childInfoOffset\n\t}\n\toffset := f.childInfoOffset\n\tf.childInfoOffset += count\n\treturn f.childInfos[offset : offset+count], nil\n}\n\n// Stat read file info from asset item\nfunc (f *assetFile) Stat() (os.FileInfo, error) {\n\tif len(f.childInfos) != 0 {\n\t\treturn newDirFileInfo(f.name), nil\n\t}\n\treturn AssetInfo(f.name)\n}\n\n// newDirFileInfo return default dir file info\nfunc newDirFileInfo(name string) os.FileInfo {\n\treturn &bindataFileInfo{\n\t\tname:    name,\n\t\tsize:    0,\n\t\tmode:    os.FileMode(2147484068), // equal os.FileMode(0644)|os.ModeDir\n\t\tmodTime: time.Time{}}\n}\n\n// AssetFile return a http.FileSystem instance that data backend by asset\nfunc AssetFile() http.FileSystem {\n\treturn &assetOperator{}\n}\n\nvar _app2App2app3CssMainCss = \"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\x4a\\xca\\x4f\\xa9\\x54\\xa8\\xe6\\xe5\\x52\\x50\\x50\\x50\\x48\\x4a\\x4c\\xce\\x4e\\x2f\\xca\\x2f\\xcd\\x4b\\xd1\\x4d\\xce\\xcf\\xc9\\x2f\\xb2\\x52\\x48\\xca\\x29\\x4d\\xb5\\xe6\\xe5\\xaa\\x05\\x04\\x00\\x00\\xff\\xff\\x52\\xd7\\xbb\\x8b\\x26\\x00\\x00\\x00\"\n\nfunc app2App2app3CssMainCssBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_app2App2app3CssMainCss,\n\t\t\"app2/app2app3/css/main.css\",\n\t)\n}\n\nfunc app2App2app3CssMainCss() (*asset, error) {\n\tbytes, err := app2App2app3CssMainCssBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"app2/app2app3/css/main.css\", size: 38, mode: os.FileMode(438), modTime: time.Unix(1595043712, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\nvar _app2App2app3DirsDir1TextTxt = \"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\x4a\\x2c\\x28\\x30\\xd2\\x07\\x11\\x89\\x05\\x05\\xc6\\xfa\\x29\\x99\\x45\\xc5\\x20\\xc2\\x50\\xbf\\x24\\xb5\\xa2\\x44\\xaf\\xa4\\xa2\\x04\\x10\\x00\\x00\\xff\\xff\\x87\\xaf\\x9d\\x00\\x20\\x00\\x00\\x00\"\n\nfunc app2App2app3DirsDir1TextTxtBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_app2App2app3DirsDir1TextTxt,\n\t\t\"app2/app2app3/dirs/dir1/text.txt\",\n\t)\n}\n\nfunc app2App2app3DirsDir1TextTxt() (*asset, error) {\n\tbytes, err := app2App2app3DirsDir1TextTxtBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"app2/app2app3/dirs/dir1/text.txt\", size: 32, mode: os.FileMode(438), modTime: time.Unix(1594843207, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\nvar _app2App2app3DirsDir2TextTxt = \"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\x4a\\x2c\\x28\\x30\\xd2\\x07\\x11\\x89\\x05\\x05\\xc6\\xfa\\x29\\x99\\x45\\xc5\\x20\\xc2\\x48\\xbf\\x24\\xb5\\xa2\\x44\\xaf\\xa4\\xa2\\x04\\x10\\x00\\x00\\xff\\xff\\x84\\x14\\xaa\\xeb\\x20\\x00\\x00\\x00\"\n\nfunc app2App2app3DirsDir2TextTxtBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_app2App2app3DirsDir2TextTxt,\n\t\t\"app2/app2app3/dirs/dir2/text.txt\",\n\t)\n}\n\nfunc app2App2app3DirsDir2TextTxt() (*asset, error) {\n\tbytes, err := app2App2app3DirsDir2TextTxtBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"app2/app2app3/dirs/dir2/text.txt\", size: 32, mode: os.FileMode(438), modTime: time.Unix(1594843207, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\nvar _app2App2app3DirsTextTxt = \"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\x4a\\x2c\\x28\\x30\\xd2\\x07\\x11\\x89\\x05\\x05\\xc6\\xfa\\x29\\x99\\x45\\xc5\\xfa\\x25\\xa9\\x15\\x25\\x7a\\x25\\x15\\x25\\x80\\x00\\x00\\x00\\xff\\xff\\x64\\xfe\\x96\\xd6\\x1b\\x00\\x00\\x00\"\n\nfunc app2App2app3DirsTextTxtBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_app2App2app3DirsTextTxt,\n\t\t\"app2/app2app3/dirs/text.txt\",\n\t)\n}\n\nfunc app2App2app3DirsTextTxt() (*asset, error) {\n\tbytes, err := app2App2app3DirsTextTxtBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"app2/app2app3/dirs/text.txt\", size: 27, mode: os.FileMode(438), modTime: time.Unix(1594843207, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\nvar _app2App2app3IndexHtml = \"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\x54\\x90\\x3f\\x4f\\xc3\\x40\\x0c\\xc5\\xf7\\x48\\xf9\\x0e\\xc6\\x33\\xed\\x91\\x76\\x61\\xb8\\x8b\\x54\\xf1\\x47\\x6c\\x30\\x94\\x81\\xf1\\x7a\\x31\\x9c\\x85\\x73\\x39\\xe5\\x4c\\x4b\\xbf\\x3d\\x4a\\x68\\x90\\x58\\x6c\\x3d\\xfb\\xbd\\x9f\\x64\\xdb\\xab\\xfb\\xe7\\xbb\\xfd\\xdb\\xcb\\x03\\x44\\xed\\xa5\\xad\\x2b\\x3b\\x75\\x10\\x9f\\x3e\\x1c\\x52\\xc2\\xb6\\xae\\xa6\\x19\\xf9\\xae\\xad\\x2b\\x00\\x00\\xdb\\x93\\x7a\\x08\\xd1\\x8f\\x85\\xd4\\xe1\\xeb\\xfe\\x71\\x75\\x8b\\xff\\x76\\xc9\\xf7\\xe4\\xf0\\xc8\\x74\\xca\\xc3\\xa8\\x08\\x61\\x48\\x4a\\x49\\x1d\\x9e\\xb8\\xd3\\xe8\\x3a\\x3a\\x72\\xa0\\xd5\\x2c\\xae\\x81\\x13\\x2b\\x7b\\x59\\x95\\xe0\\x85\\x5c\\xb3\\xbe\\xf9\\x63\\x09\\xa7\\x4f\\x18\\x49\\x1c\\x16\\x3d\\x0b\\x95\\x48\\xa4\\x08\\x71\\xa4\\x77\\x87\\x26\\x7f\\x1d\\x84\\x83\\xf1\\x39\\x6f\\xe6\\xe2\\x73\\xde\\x9a\\x50\\x8a\\xe9\\x3d\\xa7\\x75\\x28\\x05\\xc1\\x2c\\x20\\x65\\x15\\x6a\\x77\\x39\\x6f\\x76\\x39\\x6f\\xad\\xf9\\xd5\\x75\\x65\\xcd\\xe5\\xac\\xba\\xb2\\x87\\xa1\\x3b\\x2f\\xfe\\xd8\\xb4\\x4f\\x24\\x32\\xc0\\x12\\x01\\x4e\\x1d\\x7d\\x5b\\x13\\x9b\\x39\\x75\\xf1\\xce\\x80\\xe9\\x67\\x3f\\x01\\x00\\x00\\xff\\xff\\xef\\x25\\x54\\xc8\\x43\\x01\\x00\\x00\"\n\nfunc app2App2app3IndexHtmlBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_app2App2app3IndexHtml,\n\t\t\"app2/app2app3/index.html\",\n\t)\n}\n\nfunc app2App2app3IndexHtml() (*asset, error) {\n\tbytes, err := app2App2app3IndexHtmlBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"app2/app2app3/index.html\", size: 323, mode: os.FileMode(438), modTime: time.Unix(1595043725, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\nvar _app2IndexHtml = \"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\xb2\\xc9\\x30\\xb4\\xf3\\x48\\xcd\\xc9\\xc9\\x57\\x70\\x2c\\x28\\x30\\x52\\xc8\\xcc\\x4b\\x49\\xad\\xb0\\xd1\\xcf\\x30\\xb4\\x03\\x04\\x00\\x00\\xff\\xff\\x75\\x17\\xab\\xfa\\x19\\x00\\x00\\x00\"\n\nfunc app2IndexHtmlBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_app2IndexHtml,\n\t\t\"app2/index.html\",\n\t)\n}\n\nfunc app2IndexHtml() (*asset, error) {\n\tbytes, err := app2IndexHtmlBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"app2/index.html\", size: 25, mode: os.FileMode(438), modTime: time.Unix(1565946440, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\nvar _app2MydirTextTxt = \"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\xca\\x2a\\x2d\\x2e\\x51\\x48\\x54\\x28\\x49\\xad\\x28\\xd1\\x03\\x04\\x00\\x00\\xff\\xff\\x2f\\xf9\\x22\\x98\\x0c\\x00\\x00\\x00\"\n\nfunc app2MydirTextTxtBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_app2MydirTextTxt,\n\t\t\"app2/mydir/text.txt\",\n\t)\n}\n\nfunc app2MydirTextTxt() (*asset, error) {\n\tbytes, err := app2MydirTextTxtBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"app2/mydir/text.txt\", size: 12, mode: os.FileMode(438), modTime: time.Unix(1594787248, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\nvar _cssMainCss = \"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\x4a\\xca\\x4f\\xa9\\x54\\xa8\\xe6\\xe5\\x52\\x50\\x50\\x50\\x48\\x4a\\x4c\\xce\\x4e\\x2f\\xca\\x2f\\xcd\\x4b\\xd1\\x4d\\xce\\xcf\\xc9\\x2f\\xb2\\x52\\x48\\xca\\x49\\x4c\\xce\\xb6\\xe6\\xe5\\xaa\\xe5\\xe5\\x02\\x04\\x00\\x00\\xff\\xff\\x03\\x25\\x9c\\x89\\x29\\x00\\x00\\x00\"\n\nfunc cssMainCssBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_cssMainCss,\n\t\t\"css/main.css\",\n\t)\n}\n\nfunc cssMainCss() (*asset, error) {\n\tbytes, err := cssMainCssBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"css/main.css\", size: 41, mode: os.FileMode(438), modTime: time.Unix(1565946440, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\nvar _faviconIco = \"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\xec\\x5a\\x0b\\x70\\x55\\xc7\\x79\\xde\\x7b\\xce\\x45\\x12\\xb2\\x90\\xc4\\xc3\\xe6\\x61\\x3b\\x90\\xf8\\x31\\xc4\\x19\\x6c\\x32\\xe3\\xc4\\x34\\xe3\\xc6\\x34\\x6d\\xed\\xd4\\x69\\x62\\x32\\x49\\x9a\\xa6\\x75\\xea\\x36\\x33\\xee\\xd8\\x9e\\xb4\\x75\\xdc\\x7a\\xa6\\xc5\\x31\\x02\\xa6\\xd3\\x84\\x47\\x28\\x6f\\x3b\\xc6\\x3c\\xcc\\xeb\\x9e\\xbd\\x08\\x21\\x04\\x92\\x28\\x12\\x0e\\x08\\x5b\\x3c\\xec\\x02\\x92\\x78\\x08\\x21\\x09\\x41\\x25\\x24\\x10\\xe8\\x71\\xb5\\xe7\\xdc\\xd7\\xb9\\xf7\\xef\\xfc\\xff\\x9e\\x73\\x74\\x25\\xdd\\x97\\x24\\x82\\x33\\x1e\\xce\\xcc\\x3f\\x7b\\xee\\x9e\\xdd\\xff\\xff\\xf6\\xdf\\x7f\\xff\\xfd\\xf7\\xdf\\xcb\\x98\\x8b\\xa9\\x2c\\x3f\\x1f\\xcb\\x19\\xec\\x15\\x37\\x63\\x5f\\x67\\x8c\\xcd\\x98\\x21\\x7f\\x6b\\xf9\\x8c\\x6d\\x72\\x33\\x36\\x7b\\xb6\\xf5\\xfb\\x11\\xc6\\x9e\\xbd\\x97\\xb1\\x99\\x8c\\xb1\\x7c\\x6c\\xc7\\x64\\x3d\\x3d\\x6e\\x76\\xdb\\x1f\\xe1\\x75\\xab\\x42\\x53\\x7e\\x22\\x3c\\x6c\\x91\\xf0\\xb0\\x02\\x22\\xae\\x14\\x08\\x8f\\xab\\x40\\xec\\x64\\x92\\xf0\\x5d\\x53\\xfe\\x59\\xec\\x64\\x5f\\x15\\x1e\\xa6\\x08\\x4f\\x7f\\x7f\\xdf\\x7a\\x96\\xa1\\x97\\x3c\\xb8\\x3f\\x78\\xfa\\x0d\\x08\\x9e\\x5d\\x08\\xc1\\xda\\x5f\\x82\\x51\\xf6\\x65\\x30\\xca\\x67\\x41\\xf0\\xcc\\xbf\\x11\\x19\\x07\\x1e\\x07\\xbd\\x78\\x2a\\x18\\x65\\x8f\\xb5\\x0b\\x4d\\x7d\\xad\\x6f\\x07\\x73\\x0b\\xcd\\x45\\xfd\\xbb\\x17\\xb3\\x0c\\xa3\\x62\\xce\\xfe\\xa8\\xd1\\x0a\\xf8\\x98\\x6d\\x25\\x10\\x38\\xfe\\x53\\x88\\xdc\\x3a\\x01\\x66\\x6b\\x11\\x11\\xbe\\x07\\x8e\\xfd\\x2d\\x84\\x2e\\x2c\\x05\\xff\\xd1\\x79\\x3d\\xc2\\xc3\\x7e\\x20\\x78\\x06\\x13\\x5c\\x89\\xe9\\xdf\\x06\\xd1\\x50\\x2f\\x04\\x4e\\xfc\\x0c\\xcc\\x6b\\xa5\\x60\\x1c\\xfd\\x21\\x74\\x6c\\x1e\\x47\\x84\\xef\\x58\\x17\\xf8\\xe4\\x65\\x30\\x6f\\x54\\x81\\x71\\xe0\\x89\\xea\\xbe\\x2d\\x6c\\xd2\\x80\\xfe\\xfe\\x76\\x30\\x6f\\x1c\\x81\\x60\\xed\\x7c\\x08\\x9e\\x5d\\x0c\\x2d\\xeb\\xc7\\x41\\xed\\xd2\\x29\\x50\\xbb\\x74\\x32\\xbd\\x63\\x1d\\x7e\\xc3\\x36\\xa1\\x73\\xff\\x19\\x14\\x1e\\x36\\x4f\\x78\\xc7\\xc4\\xf4\\xef\\x80\\x70\\xd3\\xfb\\x10\\x6e\\x5c\\x0f\\xdd\\x65\\x73\\xe1\\xdc\\xf2\\x89\\xd0\\xb1\\x25\\x0f\\x6e\\x7c\\x90\\x07\\xe7\\x7f\\x33\\x81\\xea\\xf0\\x5b\\xb8\\x79\\x13\\x61\\xd0\\x8b\\xa7\\x2c\\x09\\x7c\\xfc\\x57\\xac\\x7b\\x11\\xf6\\x7f\\x6a\\x5f\\xd4\\xb8\\x06\\xa1\\xfa\\x15\\x10\\x6e\\x7a\\x0f\\x3a\\xb4\\xc7\\xa0\\x61\\x55\\x3e\\x04\\x8f\\xfc\\x39\\x84\\x3e\\xfa\\x4b\\x68\\x5c\\x93\\x4f\\x75\\xf8\\x2d\\x74\\x71\\x05\\x44\\x45\\x13\\xea\\x54\\xeb\\x7c\\x85\\xa9\\x48\\xf8\\x1e\\x15\\xcd\\xf4\\xcd\\xee\\x7f\\x71\\x65\\x3e\\x04\\x3e\\x7c\\x86\\x78\\x5c\\x5a\\x1d\\xd3\\xbf\\x7e\\x05\\xa0\\x2c\\x94\\x89\\xb2\\x03\\xd5\\x3f\\x66\\x88\\x05\\x31\\x21\\x36\\x1b\\xff\\xd9\\xe5\\x13\\xe1\\xda\\xc6\\x5c\\x68\\xdf\\x94\\x4b\\x63\\x71\\xf0\\x37\\xbd\\x0f\\x38\\x56\\x1c\\x33\\x8e\\x1d\\x75\\x80\\xba\\x40\\x9d\\x0c\\xd6\\x5f\\xcd\\x92\\x29\\x44\\x83\\xf5\\x87\\xba\\x76\\xfa\\x73\\x85\\xe1\\x5c\\x18\\x07\\x9e\\x38\\x86\\x18\\x68\\x8e\\xac\\xf9\\xbb\\xbe\\x39\\x87\\xc8\\x99\\xbf\\x13\\x3f\\xa3\\x39\\xc6\\xb9\\x8e\\xed\\x4f\\xb6\\xe0\\x61\\x3f\\x40\\xdb\\x40\\x1b\\x41\\x5b\\x19\\x62\\x3f\\xc7\\x7f\\x4a\\xb6\\x85\\x0f\\xda\\x9a\\xdd\\x9f\\xd6\\x80\\xe6\\x62\\xd2\\x26\\xd5\\xd7\\xd0\\x46\\xc9\\x56\\x0f\\x3c\\xde\\x6f\\xbf\\xe5\\xb3\\xc8\\xa6\\xd1\\xb6\\xc9\\xc6\\x4f\\xbf\\x01\\x68\\xf3\\x68\\xfb\\xce\\x3a\\xf2\\x30\\x26\\x76\\x32\\x85\\xd6\\x08\\xae\\x95\\xc1\\xeb\\x87\\xd6\\x14\\xb3\\x69\\x11\\xad\\x39\\xaf\\x5b\\x1d\\xed\\xfa\\x05\\x60\\x8c\\x65\\x32\\xc6\\x54\\x8b\\x5c\\x31\\x64\\x3d\\x0b\\x63\\xe8\\xb0\\x45\\x2d\\x56\\xdf\\x99\\x96\\x8f\\x99\\x1b\\xeb\\x67\\xf2\\x47\\x8b\\xea\\xf3\\xf9\\x08\\xae\\x22\\xe5\\x09\\xae\\x4e\\x17\\x5c\\xfd\\xc2\\x28\\x69\\x8a\\xe0\\xca\\x58\\xb4\\x3d\\xdb\\x17\\xa6\\x29\\xff\\x5f\\x04\\x57\\x5b\\x85\\xe6\\xba\\x92\\x90\\xb8\\x1a\\x43\\x4a\\x4c\\xbd\\x62\\xd7\\xb7\\x08\\xae\\xd6\\x0b\\xae\\xfe\\x8f\\xe0\\xea\\x9b\\x38\\x1e\\x1f\\x1f\\x4b\\xfe\\x34\\xb5\\x7c\\xa5\\x40\\xec\\xca\\x02\\xbd\\x64\\x3a\\xe8\\xfb\\xbe\\x38\\x94\\x4a\\xa6\\x83\\xf0\\x66\\x82\\xd0\\x14\\x10\\x9a\\x0b\\x44\\xe1\\x38\\x5c\\x2b\\x44\\xf8\\x4e\\x75\\x5c\\x01\\xe1\\x75\\x03\\xf2\\x11\\x9a\\x2b\\x2a\\xb8\\x52\\x2b\\xb8\\x3a\\x4f\\x68\\x8a\\x92\\x0c\\x03\\xc9\\xf7\\xb0\\x02\\xa3\\xfc\\x2b\\x10\\xe9\\xae\\x85\\xa8\\x7e\\x15\\xa2\\xfa\\x95\\x18\\xba\\x0a\\xa1\\xb3\\x8b\\x49\\xbe\\xbe\\xe7\\x5e\\x08\\xfe\\xef\\x3f\\x81\\xd9\\x51\\x09\\x91\\xbe\\x4b\\x44\\xf8\\x8e\\x75\\xf8\\x4d\\x70\\x37\\x04\\x3e\\xfe\\x11\\xf9\\x14\\xbd\\x68\\x3c\\xe2\\xe8\\x14\\x5c\\x7d\\x49\\x78\\x99\\x2b\\x11\\x06\\x47\\xfe\\x81\\xd9\\x10\\x0d\\xde\\x82\\xc1\\x8f\\x79\\xe3\\x30\\xe8\\x7b\\xa7\\x81\\x51\\xfa\\x28\\x98\\xd7\\xca\\x00\\xa2\\xa6\\xfc\\x80\\x65\\xcc\\x3b\\x7e\\xc3\\x36\\xfa\\xde\\x07\\x08\\x93\\xd9\\x5a\\x0c\\xfa\\xfe\\x87\\x41\\x68\\xac\\x43\\x70\\xf5\\x59\\x92\\xe3\\x1d\\xea\\x1a\\x92\\xc9\\x8f\\x06\\x3a\\xc1\\xff\\xe1\\x5c\\xda\\x67\\x91\\x27\\x3e\\x91\\xde\\x0b\\xe4\\xaf\\xfc\\x47\\x5f\\x20\\xc2\\x77\\xac\\x23\\xac\\x1d\\x95\\xd4\\xd6\\xff\\xe1\\x9f\\x10\\x2f\\xfa\\x5d\\xf2\\x05\\xc4\\x70\\x4c\\x70\\xf5\\x7e\\x94\\x35\\x1c\\xf9\\xa1\\xc6\\x77\\x68\\x3e\\x43\\x17\\x57\\x4a\\xfe\\xe8\\xc3\\xcb\\xbe\\x8c\\xfc\\xa0\\xcf\\x23\\x09\\xdf\\xb1\\x0e\\xbf\\x51\\x9f\\x8b\\x2b\\x65\\x9f\\xc6\\x77\\xe8\\x37\\xee\\x4f\\x62\\xd7\\x58\\xb4\\x8f\\x05\\x3e\\x9e\\x35\\xc4\\x1e\\x13\\xc9\\x8f\\x06\\xbb\\xc0\\xa8\\xfc\\x23\\xf0\\x1f\\xfa\\x63\\x88\\x86\\xba\\x21\\x72\\xeb\\x24\\x18\\xfb\\x1f\\x82\\xde\\x1d\\x2e\\x68\\xdd\\x30\\x0e\\x1a\\x56\\x4f\\x24\\xc2\\x77\\xac\\xc3\\x6f\\xd8\\x06\\xdb\\x62\\x1f\\xec\\x8b\\xef\\x10\\xd6\\xc1\\xff\\xd1\\xf7\\x11\\x67\\x83\\xe0\\xea\\x97\\x06\\xeb\\x20\\x91\\x7c\\xd2\\xdd\\xee\\x3c\\x08\\x37\\x6f\\x04\\x30\\x0d\\x08\\x7c\\x34\\x8f\\xe4\\x34\\xae\\x99\\x00\\xa7\\x7f\\x3d\\x0d\\x4e\\xfd\\xea\\x7e\\x22\\x7c\\xc7\\x3a\\xfc\\x86\\x6d\\xb0\\x6d\\xb8\\x69\\x23\\xf5\\xb5\\xe7\\xcc\\x6c\\x3f\\x00\\x62\\x77\\x6e\\x54\\x68\\xae\\x57\\xad\\xf5\\x96\\x52\\x7e\\xb0\\x6e\\x01\\xe8\\xfb\\x66\\xd0\\x1a\\x30\\xaf\\x95\\x83\\x28\\xcc\\x81\\x2b\\xef\\xe6\\x92\\x3c\\x24\\x8a\\x63\\x96\\x4d\\x71\\x7e\\xe3\\x37\\x6c\\x83\\x6d\\xb1\\x0f\\xf6\\x0d\\xd6\\x15\\x48\\x5d\\x86\\x7c\\x64\\x47\\xc2\\xc3\\xf6\\x0a\\xae\\x66\\xc6\\xea\\x20\\xae\\xfc\\x48\\x08\\xfc\\x55\\xdf\\x05\\x7f\\xd5\\x77\\x00\\x22\\x41\\x08\\x7e\\xfa\\x2a\\x74\\x6d\\x53\\xa1\\x6e\\xf9\\x64\\x92\\x75\\x69\\xcd\\x44\\xb8\\xb9\\x35\\x13\\x6e\\x6d\\xcb\\x80\\xe6\\x75\\xe3\\xa9\\x0e\\xbf\\x61\\x1b\\x6c\\x8b\\x7d\\xb0\\xaf\\xbf\\xea\\x7b\\xc4\\x8b\\xec\\xe2\\xec\\x22\\x9c\\x83\\x66\\xc1\\xd5\\x19\\xa9\\xe4\\xd3\\xdc\\x1f\\x98\\x0d\\xc1\\x33\\x6f\\x42\\x34\\xec\\x03\\x7f\\xe5\\x1c\\x68\\xdb\\x30\\x96\\xe4\\x9c\\xfb\\xcd\\x7d\\xd0\\xbd\\x63\\x0c\\xe8\\x85\\x63\\x41\\x2f\\xbc\\x07\\x7a\\x3d\\x6e\\xa8\\x5f\\x39\\x89\\xbe\\x61\\x1b\\x6c\\x8b\\x7d\\xb0\\xaf\\xe4\\xd9\\x65\\xd9\\x6e\\x19\\x88\\x5d\\xd9\\xba\\xd0\\x5c\\x73\\x53\\xca\\xd7\\xff\\x0f\\xf4\\xfd\\x0f\\x41\\xa8\\x61\\x35\\xf9\\x1f\\x7d\\xdf\\x74\\x68\\x5e\\x97\\x47\\xf3\\xdd\\xf2\\x4e\\x1e\\xc9\\x0d\\x9f\\xfc\\x3b\\x30\\x4f\\xbd\\x02\\xc6\\x9e\\x09\\xd0\\xba\\x21\\x07\\x4e\\xfd\\x6a\\x1a\\xb5\\xc1\\xb6\\xe4\\xb3\\x1a\\x56\\x13\\x0f\\xe4\\x25\\xd7\\xed\\x39\\x5c\\x9b\\x51\\xa1\\xb1\\x17\\xe3\\xc8\\x7f\\x0b\\x63\\x13\\x5c\\xef\\xd4\\xb6\\xaf\\x91\\x7c\\x6b\\xf8\\xf2\\x16\\x5a\\xdb\\x62\\xcf\\x64\\xb8\\xb4\\x7a\\x3c\\xc9\\x68\\xdb\\x90\\x0d\\xfe\\xb2\\x87\\x01\\x2e\\x2d\\x07\\x68\\x5a\\x05\\x81\\xca\\x27\\xe1\\xfa\\xe6\\x4c\\x39\\x2f\\xab\\xc7\\x53\\xdb\\x88\\xaf\\x9e\\xfa\\x22\\x0f\\xe4\\xe5\\xc4\\x47\\xa5\\x8f\\xa2\\x0d\\xfc\\x22\\x8e\\xfc\\x5f\\xe0\\x37\\x3b\\x5e\\x4f\\x2e\\xff\\x1e\\xf0\\x97\\x7e\\x11\\xa2\\x0d\\x4b\\x00\\x9a\\x56\\x42\\xe0\\xe0\\x13\\x70\\x7d\\x53\\x56\\x6a\\xf9\\x81\\x4e\\x8a\\xbf\\x70\\xac\\x43\\xe4\\x6b\\xec\\x45\\xd4\\x0d\\xea\\x28\\xbe\\xfe\\x67\\x38\\xfa\\x6f\\x5e\\x97\\x2f\\xfd\\xcb\\xd1\\xe7\\x21\\x74\\xec\\x87\\xa0\\xef\\xce\\x81\\xab\\xbf\\x1d\\x67\\x7d\\xcb\\xb3\\xd6\\xcc\\x50\\xfd\\xe3\\xdc\\xe2\\x1c\\xcb\\xb3\\xd0\\x60\\xf9\\xae\\xb9\\x68\\x1b\\xe4\\xdf\\x53\\xd8\\x1f\\xda\\x39\\xda\\xbe\\xee\\x55\\x41\\xf7\\xba\\xc9\\x16\\xcf\\xaf\\xb8\\x2f\\xa5\\xfd\\x25\\x95\\x8f\\x6b\\x42\\x63\\xcd\\xb8\\xcf\\x25\\x5c\\x7f\\x5b\\xe5\\xfa\\xc3\\x39\\xb8\\xf0\\xdf\\xf7\\x42\\xfb\\xc6\\x7b\\xa0\\x63\\x53\\x36\\x5c\\x5c\\x35\\x29\\xad\\xf5\\x97\\x42\\x7e\\x26\\xfa\\x06\\xda\\x37\\xc2\\xbe\\xb4\\xfc\\x0f\\xd2\\x99\\x25\\xd3\\xd2\\xf2\\x3f\\xc9\\xe5\\x2b\\xf6\\x1c\\xbc\\x86\\x3e\\x12\\x7d\\xe5\\x50\\xff\\xeb\\x8f\\xe3\\x7f\\x25\\x0d\\xf5\\xbf\\xfe\\x21\\xfe\\x37\\x99\\xfc\\x18\\x1d\\x7c\\x09\\xf7\\x08\\xdc\\x2b\\xc0\\xd4\\x69\\xef\\x30\\x2a\\x46\\xb9\\xff\\x58\\x73\\x9f\\x5a\\xbe\\xc2\\x7c\\x1a\\xed\\x8d\\x0b\\x70\\xaf\\xc4\\x3d\\x53\\xee\\xbf\\xef\\x8e\\x7a\\xff\\x4d\\x47\\x7e\\x8c\\x0e\\xee\\xc7\\x58\\x01\\x63\\x06\\xd4\\x1d\\xf6\\x41\\x9b\\x18\\x51\\xfc\\x61\\xf9\\xb2\\xb4\\xe5\\x7b\\x55\\x1b\\xc3\\xb3\\x18\\x33\\x61\\xec\\x84\\x31\\x14\\xf1\\xdc\\xfb\\x40\\x5a\\xf1\\x97\\x4e\\xf1\\xd7\\x34\\x8a\\xd9\\x06\\x3f\\xa9\\xe4\\x3b\\xb6\\x48\\xb1\\xa2\\xfa\\x12\\xc6\\x8e\\x18\\x43\\x62\\x2c\\x89\\x31\\x25\\xc6\\x96\\x29\\xe3\\x4f\\x6f\\x26\\xc5\\xaa\\xf1\\x62\\x58\\x8c\\x6d\\x31\\xc6\\x4d\\x26\\xbf\\x7f\\x3d\\x60\\xcc\\xac\\xce\\xa3\\x18\\x1a\\x63\\x69\\x8c\\xa9\\xb9\\x5b\\xc6\\xd8\\x14\\x7f\\xe7\\xc4\\xc4\\xdf\\x39\\xb2\\x0e\\x63\\x73\\x8c\\x91\\x93\\xc5\\xf0\\xc4\\x47\\x49\\x2a\\x9f\\x30\\x68\\x0a\\xf3\\x15\\x65\\x31\\x6b\\xaf\\x7e\\xd3\\x3a\\x53\\xd4\\x5b\\x67\\x8c\\x44\\xe7\\x0f\\x49\\xc9\\xcf\\x30\\xad\\xd6\\x59\\x27\\xa9\\xfc\\x58\\x5d\\xf4\\x69\\x14\\x2f\\x8d\\x95\\x67\\xab\\x51\\x9f\\xcf\\xa6\\x5b\\x67\\xbd\\xb4\\xe4\\x7f\\xde\\x1f\\x7b\\x6d\\x1c\\x66\\x2a\\x1c\\x66\\x0c\\xe9\\x99\\xc3\\x8c\\x4d\\xb7\\x28\\xcf\\xa2\\x4c\\x8b\\xd4\\x74\\xa8\\xc5\\xa2\\x1e\\x8b\\x02\\x16\\x99\\x8c\\xa9\\xc0\\x48\\x90\\x6a\\xcb\\x9d\\xc9\\x18\\x9b\\xcd\\x18\\xfb\\xfb\\xd8\\x3c\\xc5\\xc3\\x9f\\xb5\\x56\\xee\\x3e\\x77\\x9f\\x3f\\xcc\\xc7\\xda\\x1f\\x1f\\x16\\x5c\\x7d\\x5b\\x70\\x75\\xa1\\xe0\\x6a\\xc1\\xef\\x89\\x6c\\xde\\xff\\x2a\\xb8\\xfa\\x37\\x82\\x2b\\xb3\\x05\\x57\\x72\\x7a\\x35\\xc6\\x0c\\x2d\\x79\\x3e\\x29\\x0d\\xfc\\xdf\\x16\\x5c\\x0d\\x0a\\xae\\xc2\\x1d\\x22\\x53\\x70\\xb5\\x53\\x70\\xf5\\x20\\xed\\xeb\\x5c\\xc9\\xc7\\x58\\xc3\\x48\\x33\\x3f\\x97\\x18\\xbf\\x42\\xb1\\x57\\x5a\\x84\\x6d\\x07\\x60\\x4a\\xd2\\x37\\x6e\\x5b\\x97\\xfd\\x1b\\xf5\\x56\\x2a\\xb8\\xfa\\x24\\xec\\x67\\x4c\\xe7\\xc3\\x1b\\x83\\x83\\x5f\\x73\\x05\\x31\\xc6\\x0b\\xd6\\xbe\\x45\\xe7\\xf2\\x64\\x14\\x3c\\xbb\\x08\\x8c\\x8a\\xa7\\xfa\\x71\\x21\\xc6\\xc2\\x1c\\x30\\x0e\\x7e\\x0d\\x02\\x27\\xfe\\x81\\x78\\x20\\xe1\\x3b\\xd6\\xc9\\x78\\x84\\x39\\xd8\\xf5\\xe2\\x29\\x60\\x94\\xcd\\x04\\xe1\\xcd\\x8a\\x1d\\x47\\xa3\\xe0\\xea\\x0b\\x7e\\xce\\x5c\\x7a\\x8a\\xfc\\x64\\x7c\\xfc\\x2c\\x48\\xf1\\x75\\xb8\\x6f\\x48\\x9c\\x37\\xf8\\x31\\xdb\\x4a\\x64\\x9c\\x84\\xb2\\xbd\\x19\\xe0\\x3f\\xf2\\x17\\x60\\xb6\\xee\\x85\\x68\\xe0\\x26\\x40\\x34\\x12\\x13\\x20\\x46\\xa8\\x0e\\xbf\\x61\\x1b\\x6c\\x8b\\x7d\\xf4\\xe2\\xc9\\x10\\x6a\\x58\\x0b\\xe1\\xcb\\x5b\\xc1\\xa8\\xfc\\x46\\xff\\x9c\\x70\\xb5\\x0d\\xc7\\xd0\\xa3\\x65\\x33\\x91\\xe6\\x3c\\x0c\\x17\\x7f\\xa4\\xeb\\x14\\xc5\\xb4\\xc2\\xc3\\xe8\\x3c\\x43\\xb1\\x65\\xb0\\x3b\\xe5\\x98\\xb1\\x0d\\xb6\\xc5\\x3e\\xf2\\x7c\\x30\\x13\\x22\\xdd\\x35\\x10\\xf5\\x5f\\xa7\\x73\\xa9\\x28\\xbc\\xc7\\x1e\\x43\\x93\\xe0\\xca\\x53\\x14\\x73\\x16\\xa6\\x8e\\x89\\x86\\x83\\x9f\\xce\\x41\\x55\\xdf\\x23\\xec\\x62\\x77\\x2e\\x9d\\xc7\\xed\\xb3\\xa8\\x1c\\x5c\\x10\\xa2\\xa2\\x05\\x22\\xb7\\x3e\\x21\\xc2\\x77\\xac\\xeb\\xff\\x1e\\xa2\\x3e\\xd8\\x17\\x79\\x20\\xaf\\x68\\xa8\\x87\\xda\\x84\\xea\\x97\\xcb\\xbc\\xb3\\x1c\\x43\\xa5\\xe0\\xea\\xe4\\x74\\x62\\xba\\xe1\\xe0\\x0f\\x35\\xac\\x91\\x36\\xc0\\xdd\\x10\\xac\\x7b\\xbb\\x1f\\x7b\\x34\\x0c\\x66\\x47\\x05\\xdd\\x3d\\xe9\\xa5\\x8f\\x80\\x5e\\x34\\x51\\x52\\xe9\\x23\\xf2\\x3e\\xaa\\xa3\\x82\\xda\\xd8\\x63\\x08\\xd6\\xfe\\x52\\xc6\\xee\\xde\\x0c\\xe2\\x69\\x8f\\x1d\\xcf\\x4d\\x92\\xbf\\x1a\\x45\\x7f\\xeb\\xf3\\x32\\x57\\x5f\\x0a\\x3b\\x4a\\x17\\x3f\\xea\\xd2\\x28\\x9f\\x25\\xf5\\x76\\xe4\\x3b\\xfd\\x79\\xad\\x70\\x1f\\x84\\xce\\xff\\x17\\xe8\\x7b\\x26\\x59\\xfe\\xc6\\xca\\xdb\\xdb\\x3e\\x46\\x63\\xf4\\x0d\\xdb\\xd8\\xbc\\xe9\\x0c\\x78\\xe4\\x79\\xe2\\x85\\x3c\\x69\\x9e\\x68\\x7e\\x7b\\x21\\x50\\xfd\\x63\\x7b\\x4d\\x5f\\x13\\x5c\\xfd\\x7a\\xca\\x73\\x45\\x9a\\xf8\\x69\\x7e\\xad\\xb5\\x67\\xde\\xa8\\xea\\xd7\\x59\\xdd\\x02\\x79\\x0f\\xc1\\xa5\\x1f\\xe9\\xd9\\x39\\x06\\x6e\\x6e\\xcd\\x22\\xc2\\x77\\xb9\\x36\\x5d\\xd4\\x06\\xdb\\xda\\xf6\\x64\\xde\\x38\\x42\\xbc\\x90\\x27\\xf2\\x76\\x4c\\xac\\xbb\\x86\\x72\\x44\\xd6\\x18\\xde\\xd3\\xb9\\xcb\\x9d\\xcc\\x1f\\xa5\\x83\\x9f\\xf2\\x5c\\x15\\x73\\x48\\x5f\\x81\\x4f\\x5f\\x75\\xce\\xa8\\xe1\\xcb\\x5b\\x1c\\x9b\\x45\\xac\\x57\\xde\\xcd\\xa3\\x5c\\x66\\xcd\\xd2\\x29\\x44\\xf8\\x8e\\x75\\x72\\x1c\\x0a\\xb5\\xc5\\x3e\\x92\\xa9\\x49\\xbc\\x68\\x0e\\x2a\\xe6\\x0c\\x38\\x5b\\x87\\x2e\\x2c\\xb1\\xce\\x86\\x6a\\xbb\\xe0\\xea\\x57\\x93\\x9f\\x2d\\x53\\xe3\\xa7\\x5c\\xcf\\xae\\x6c\\xb2\\x03\\xb3\\xb3\\x5a\\xea\\xa9\\xaf\\x51\\x9e\\x5d\\x35\\x06\\xdd\\xdb\\x33\\x9c\\x9c\\x53\\x3c\\xc2\\x6f\\xd8\\x86\\xfc\\x4e\\xf9\\x57\\x9c\\xfc\\x9f\\xd9\\xf9\\xb1\\xb4\\xbb\\xc2\\x6c\\x30\\xdb\\xcb\\xfb\\xf5\\xa5\\x5f\\x95\\x79\\x41\\xb9\\x67\\x2c\\x12\\xdc\\x9d\\xf0\\xce\\x2a\\x1d\\xfc\\xe4\\xdf\\xc8\\x5f\\x7c\\x97\\x72\\xf1\\x54\\x57\\xf3\\xef\\x64\\x17\\xbd\\x1e\\x37\\xe5\\x6c\\x12\\x61\\xb7\\x09\\xdb\\x60\\x5b\\xec\\x83\\x7d\\xe5\\x00\\x0c\\xe2\\x89\\xbc\\x51\\xc6\\x00\\x99\\x75\\x0b\\x6d\\x1b\\x3a\\x2e\\xb8\\x32\\x61\\xa4\\xf8\\xf1\\x37\\xe5\\xe9\\x35\\x06\\xa1\\x86\\x55\\x96\\x7e\\x5a\\xc0\\x28\\x9d\\x49\\x75\\xad\\xef\\x8d\\x1b\\x82\\xb5\\x76\\xd9\\xc0\\x7b\\x00\\x9b\\xb0\\xad\\xed\\xfb\\xa3\\xfa\\x15\\x69\\x2b\\x17\\x57\\x51\\x1d\\xca\\x88\\x95\\x8d\\xfe\\x57\\xde\\x9f\\x29\\x3e\\xc1\\xd5\\x6f\\x26\\xce\\x8f\\x24\\xc7\\x1f\\xf1\\x35\\x80\\x5e\\xf2\\x00\\xe8\\xbb\\xf3\\x21\\x72\\xf3\\x98\\x65\\xf7\\x1f\\x80\\xf0\\x8e\\x81\\x9e\\x1d\\x63\\xc8\\xc6\\x6d\\x7c\\x35\\x4b\\xa7\\x92\\xbd\\xdf\\xda\\x96\\x09\\x5d\\xdb\\x33\\xa1\\x6d\\x43\\x8e\\x73\\xbf\\x60\\xe7\\xf9\\xb1\\x0f\\xfa\\x48\\xdc\\x7b\\x89\\xff\\xcd\\x6a\\xe2\\x4d\\x79\\x65\\x5f\\xc3\\x40\\xbd\\xfd\\xee\\x5b\\xb6\\x0d\\x51\\x7e\\x3b\\xde\\x3a\\x4e\\x85\\xdf\\xec\\x38\\x44\\xb6\\x6f\\x94\\x3d\\x46\\xff\\x67\\xc0\\x27\\x70\\xf2\\x65\\xe2\\x7b\\xed\\xfd\\x9c\\x98\\xbc\\xe7\\x54\\xa9\\x5f\\x27\\x16\\x50\\x40\\xe7\\x2a\\x5c\\xdf\\x9c\\x4d\\xf7\\x32\\x76\\x3b\\xec\\x83\\x7d\\x03\\x9f\\xfc\\xa3\\xc4\\xe9\\x6f\\x27\\xde\\x28\\x03\\x65\\x0d\\xb0\\xa1\\x9a\\xff\\xb0\\xf1\\x6f\\xd3\\xb9\\x2b\\xee\\x9d\\x69\\x2a\\xfc\\xa4\\x6b\\x9c\\xdf\\xc3\\xcf\\x01\\x44\\x02\\x94\\xcb\\x35\\x2a\\x9f\\xa6\\xba\\xa6\\xb5\\x13\\x9c\\x7c\\x29\\xae\\x51\\x9f\\x47\\xe6\\x93\\xf4\\xc2\\x6c\\xd0\\x77\\x8f\\x73\\xe2\\xcd\\xcb\\xeb\\xf3\\x9d\\xfc\\x2a\\xf6\\x21\\x7e\\x87\\x9e\\x96\\x79\\xe1\\x48\\x80\\x78\\x63\\x1d\\xca\\x1a\\x20\\xfb\\x2a\\xb7\\xf7\\xb3\\x6a\\xc1\\x95\\xdc\\x91\\xe0\\x0f\\x5d\\x58\\x26\\xfd\\xe6\\xf1\\x97\\xa4\\xbe\\xc4\\x65\\xca\\x5d\\xf5\\xee\\x54\\x29\\xe7\\x6d\\xeb\\xb5\\x0d\\xf5\\xca\\x5d\\x60\\xec\\x9d\\x4a\\x77\\x30\\x91\\xda\\x37\\xc0\\x5f\\x3e\\x93\\xc6\\xd3\\xf9\\xc1\\x58\\xb2\\x2d\\x3b\\x4f\\xee\\xdb\\xa9\\x12\\x0f\\xe4\\x45\\xf3\\x79\\xfc\\x25\\x92\\x81\\xb2\\x06\\xd8\\xee\\xcd\\x13\\xf2\\xce\\x98\\x2b\\x4d\\x82\\xab\\x0f\\x26\\xc9\\x91\\x3e\\x97\\x08\\x3f\\xed\\x4f\\x3b\\x19\\x04\\x4f\\xbd\\x2e\\x79\\xf6\\xd4\\xd2\\xbe\\xd3\\xbd\\x7d\\x0c\\x9c\\xb5\\x6c\\x1b\\xb1\\xe1\\x7e\\xa5\\x7b\\x55\\x08\\x1d\\xfb\\x11\\x40\\xf3\\x5a\\x22\\xf3\\xf4\\xcf\\x69\\x2e\\xd0\\xff\\xa3\\xed\\x23\\x7e\\xec\\xd3\\xbd\\xdd\\x4d\\x31\\x74\\xa4\\xa7\\x4e\\xca\\x38\\xf5\\xba\\x94\\x81\\xfb\\x5b\\xac\\xef\\x10\\xcd\\x74\\xf7\\x64\\xdd\\x79\\xcf\\x4a\\x82\\xff\\x5b\\x42\\x63\\x86\\x71\\xf0\\xc9\\x21\\xb1\\x24\\xf9\\x4e\\xe4\\x5d\\x3b\\xdf\\xd2\\xc9\\x71\\xd0\\x8b\\xf2\\xa1\\x6b\\x5b\\x86\\xe3\\x63\\xb0\\xec\\xda\\x9e\\x41\\xf7\\x6f\\x66\\xcd\\xeb\\x00\\xcd\\x6b\\x00\\x9a\\x56\\x43\\xb4\\x7e\\x31\\x18\\x7b\\x27\\x83\\xcf\\x23\\xe7\\x0a\\xf1\\x3b\\x6d\\x8b\\xf2\\xe9\\xff\\x3a\\x24\\xa3\\x76\\xbe\\x94\\x31\\xc8\\x87\\x62\\x6c\\x4a\\x31\\x8b\\xc6\\xfa\\x04\\x57\\x9f\\x4e\\x82\\xff\\x69\\x6c\\x43\\xb1\\x88\\xff\\xfa\\xc8\\xf0\\x6f\\xcb\\x00\\x7d\\x57\\x16\\x98\\x67\\x7e\\xde\\x8f\\xff\\x42\\x01\\x18\\xc5\\x93\\x08\\xff\\xf9\\x91\\xe0\\x0f\\x76\\x03\\xea\\x14\\x75\\x4b\\x3a\\x4e\\x8c\\x7f\\x16\\xe5\\x96\\xf7\\xcd\\xa0\\x39\\x4b\\x6e\\x3f\\x75\\x34\\xf7\\x68\\x03\\xb6\\xfd\\xa0\\xef\\xb9\\xb1\\x85\\xee\\xd3\\x21\\x78\\xe4\\xcf\\xe8\\x0e\\x10\\xed\\x27\\x7c\\xe2\\x45\\xd0\\xbd\\x19\\xd0\\xbd\\x23\\x83\\xda\\x0e\\xdb\\x7e\\xc2\\x7d\\x74\\x67\\x81\\xb6\\x4d\\x36\\x9e\\x18\\xff\\x83\\xb8\\x46\\xf4\\xa2\\x09\\x74\\xdf\\x31\\x92\\xf5\\x7b\\xf5\\xb7\\xb9\\x96\\xef\\xc9\\x81\\x60\\xd5\\x73\\x10\\xaa\\xfe\\x3e\\xe8\\xc5\\xf7\\x82\\xce\\x15\\xba\\xd3\\xc2\\x31\\x9e\\xfa\\xb5\\xbd\\x7e\\x95\\xb4\\xd6\\xef\\x20\\xfc\\xdf\\x8e\\x8f\\x9f\\x72\\xeb\\xb9\\xe4\\xa3\\x70\\x5f\\xb9\\xca\\x07\\xf0\\x48\\xd7\\x7f\\x9e\\x5b\\x81\\x7b\\x53\\x46\\x7f\\xec\\x6c\\x91\\x4f\\xeb\\x8f\\x2f\\xb0\\x6d\\xe3\\x30\\xfc\\x67\\xba\\xf8\\xe5\\xde\\xa0\\x6e\\x47\\x1e\\xc1\\x9a\\xf9\\x03\\x78\\xa4\\xbb\\x7f\\x21\\x35\\xad\\x1b\\x0f\\xbd\\x9e\\x31\\xd6\\x3d\\xa6\\x0a\\x7d\\x9a\\x9b\\xe6\\x05\\x75\\x3f\\x64\\xff\\x3a\\xf9\\xb2\\xb5\\x46\\x13\\xef\\x5f\\xe9\\xe0\\xd7\\xed\\xbb\\x3e\\xdc\\xa3\\x51\\x2f\\xbf\\xfb\\xd3\\x81\\x71\\x48\\xdc\\xf8\\x61\\x6b\\xdc\\xf8\\xc1\\xde\\xc7\\x30\\x6e\\x40\\x9c\\xa8\\xeb\\x58\\xec\\xc3\\x89\\x1f\\xd2\\xc5\\x1f\\xb3\\x06\\xbe\\x29\\xb8\\xd2\\xa7\\xef\\xb9\\x0f\\x22\\x5d\\x9f\\x0e\\xe0\\xe1\\xc4\\x6f\\x17\\xd3\\x8b\\xdf\\x12\\xd1\\x70\\xe2\\xb7\\xe1\\xe1\\xa7\\x35\\x30\\x81\\x62\\x55\\xcd\\x45\\xe7\\xd0\\xd8\\xe7\\xb3\\x88\\x9f\\x87\\x85\\x5f\\x53\\x18\\xfd\\x3f\\x95\\xab\\x8b\\xe5\\x19\\xe3\\x71\\x3a\\x43\\x38\\x6b\\xe0\\xf7\\x76\\x7e\\xa9\\x8e\\x7b\\x7e\\x19\\x2e\\xfe\\x18\\x1b\\xc2\\xb3\\x5a\\x07\\x9e\\xdd\\xf0\\x0c\\xe7\\xf0\\xb9\\x23\\xe7\\xc7\\x9b\\xa3\\xc4\\x4f\\x7e\\xc8\\x8d\\x67\\x66\\x3a\\xa7\\xef\\x7f\\x88\\xce\\xd2\\xf6\\x93\\xf6\\xf9\\x7d\\x47\\xcc\\xf9\\x7d\\x47\\xb2\\xf3\\x7b\\x55\\xdc\\xf3\\xfb\\x48\\xf1\\xc7\\xcc\\xc1\\xd7\\x28\\x77\\x81\\x3e\\xae\\xfa\\xaf\\x29\\xa7\\x01\\x43\\xf2\\x27\\xcf\\x0f\\xcc\\x9f\\x9c\\xbb\\x3d\\xf9\\x93\\xd1\\xe2\\xef\\xe3\\x2e\\xe6\\xe3\\x4c\\x91\\x67\\x66\\x35\\x8a\\x7e\\x8e\\xd6\\xb2\\xa5\\xb3\\xd4\\xf9\\xab\\x17\\xc1\\x28\\x7d\\x04\\xf4\\x3d\\x13\\x89\\x8c\\x44\\xf9\\xab\\xba\\xb7\\x87\\xe6\\xaf\\x6e\\x03\\xfe\\x98\\x39\\x98\\x2c\\xb8\\x7a\\xc8\\xb6\\xd9\\x50\\xfd\\x32\\x99\\x1b\\x0c\\xf5\\x38\\xfe\\xe2\\xf6\\xe5\\x0f\\x13\\xe7\\x4e\\x47\\x84\\xbf\\x50\\xb5\\xfd\\xe9\\x1c\\x2b\\x97\\x4a\\xb9\\x55\\xfa\\xdf\\x50\\xe0\\x3a\\x44\\xba\\xcf\\xc8\\xdc\\x37\\xda\\x8a\\x9d\\xbf\\x4d\\x82\\xc1\\xc1\\x62\\xe5\\x6f\\xb1\\x0f\\xe5\\x7e\\x4b\\x1f\\xa5\\x5c\\x70\\xd2\\x3e\\x23\\xc0\\x4f\\x63\\xf0\\xba\\x58\\x8f\\x27\\x1b\\xe7\\xe1\\x05\\x99\\xd3\\x56\\x68\\xbe\\x8d\\xca\\x6f\\xd0\\xbe\\x19\\x6a\\x58\\xeb\\xac\\x3d\\x27\\x7f\\xde\\x56\\x92\\x7e\\xfe\\xbc\\xe4\\x41\\x30\\xdb\\xf6\\xa5\\x1e\\xf3\\x08\\xf1\\xe3\\xa3\\x7b\\x15\\x66\\xec\\xa2\\xff\\x6d\\xbc\\x60\\xdd\\x2d\\x48\\xbc\\xbb\\xb2\\xe8\\xbf\\x31\\x7a\\xf1\\xd4\\x24\\xf7\\x17\\xf3\\x89\\x12\\xdd\\x5f\\x18\\x15\\x4f\\xd1\\x1d\\x48\\xca\\x7b\\x92\\xda\\xb7\\xac\\xff\\xda\\xba\\x86\\x8d\\x9f\\xc6\\xc0\\x5d\\x0c\\xbc\\xe4\\x5b\\xd1\\x27\\x95\\x39\\x77\\x64\\x8e\\x8f\\x19\\xcd\\xfd\\xd1\\xb0\\xee\\xa9\\x46\\x84\\x1f\\x1f\\x43\\x73\\xd9\\x6b\\x7a\\xbc\\xbc\\x6b\\xa3\\x3b\\xb7\\x4e\\xeb\\x0e\\xee\\x4e\\xdd\\xf7\\x8d\\x18\\xbf\\x33\\x0e\\x8f\\xc2\\x7a\\x8b\\x54\\x8c\\x35\\x72\\x04\\x57\\x67\\x0b\\xae\\xfe\\xc4\\xba\\x0b\\x2d\\xb8\\x03\\xf7\\xae\\x6f\\x5b\\x77\\xbc\\x23\\xc6\\x7f\\xf7\\xb9\\xfb\\x7c\\x9e\\x1e\\xb9\\x43\\x24\\x2e\\x5b\\x18\\x63\\xcf\\x58\\x65\\x9e\\x55\\x66\\x5a\\xa5\\x6b\\x50\\xc9\\xec\\xb2\\xc0\\x2a\\x9f\\x19\\x54\\x4e\\x4f\\x50\\xe6\\x25\\x28\\x33\\x6f\\x5f\\xd9\\x93\\xa0\\x0c\\x24\\x28\\xcd\\x41\\x65\\xd4\\x2a\\xc1\\x2e\\x17\\x0e\\x2a\\x5b\\xac\\xb2\\xc7\\x2a\\x4d\\xab\\x4c\\xa1\\xdf\\xff\\x0f\\x00\\x00\\xff\\xff\\xc6\\xb9\\x24\\x2f\\xee\\x3a\\x00\\x00\"\n\nfunc faviconIcoBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_faviconIco,\n\t\t\"favicon.ico\",\n\t)\n}\n\nfunc faviconIco() (*asset, error) {\n\tbytes, err := faviconIcoBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"favicon.ico\", size: 15086, mode: os.FileMode(438), modTime: time.Unix(1565946440, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\nvar _indexHtml = \"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\x64\\x91\\x3d\\x4f\\xc3\\x30\\x10\\x86\\xf7\\x48\\xf9\\x0f\\xd7\\x9b\\x40\\x22\\x35\\x6c\\x0c\\x71\\x96\\x42\\x57\\x90\\x28\\x03\\xa3\\xeb\\x5c\\x9b\\x6b\\x1d\\x27\\xb2\\x2f\\x69\\xfb\\xef\\x51\\x3e\\xca\\xe7\\x64\\xdf\\xeb\\xbb\\xc7\\x8f\\xec\\x7c\\xf1\\xf4\\xb2\\xda\\x7c\\xbc\\x3e\\x43\\x25\\xb5\\x2b\\xd2\\x24\\x1f\\x56\\x70\\xc6\\xef\\x35\\x92\\xc7\\x22\\x4d\\x86\\x8c\\x4c\\x59\\xa4\\x09\\x00\\x40\\x5e\\x93\\x18\\xb0\\x95\\x09\\x91\\x44\\xe3\\xfb\\x66\\x9d\\x3d\\xe2\\xaf\\x33\\x6f\\x6a\\xd2\\xd8\\x33\\x9d\\xda\\x26\\x08\\x82\\x6d\\xbc\\x90\\x17\\x8d\\x27\\x2e\\xa5\\xd2\\x25\\xf5\\x6c\\x29\\x1b\\x8b\\x3b\\x60\\xcf\\xc2\\xc6\\x65\\xd1\\x1a\\x47\\xfa\\x61\\x79\\xff\\xc5\\x72\\xec\\x8f\\x10\\xc8\\x69\\x64\\xdb\\x78\\x84\\x2a\\xd0\\x4e\\xa3\\x6a\\xbb\\xad\\x63\\xab\\x76\\xa6\\x1f\\xe2\\x25\\xdb\\x06\\x41\\x2e\\x2d\\x69\\xe4\\xda\\xec\\x49\\x9d\\xb3\\xa9\\x5d\\xfd\\xe7\\x44\\xb9\\x38\\x8a\\x15\\x91\\xfc\\xa5\\xd9\\x18\\x55\\x6d\\xd8\\x2f\\x6d\\x8c\\x3f\\x46\\x85\\xc5\\x51\\xb1\\x66\\x47\\xf0\\x46\\xa1\\xa7\\x90\\xab\\x29\\x4a\\x93\\x5c\\xcd\\x6f\\x92\\x26\\xf9\\xb6\\x29\\x2f\\xd7\\x11\\xf6\\x6d\\x27\\xb3\\xd0\\xb6\\x13\\x19\\x54\\x1a\\x6f\\x1d\\xdb\\xa3\\xc6\\xc6\\xaf\\x86\\xcd\\xcd\\x2d\\x42\\x6f\\x5c\\x47\\x1a\\xc7\\x1a\\x6a\\x5a\\x4c\\xb7\\xce\\x90\\x68\\x03\\xb7\\x57\\x8a\\xd0\\x59\\xd4\\xc1\\xf4\\x66\\x4a\\x11\\x62\\xb0\\xdf\\xe6\\x87\\x59\\xfc\\x10\\xb1\\xc8\\xd5\\xd4\\x32\\xea\\xcd\\x52\\xa3\\xe9\\xf0\\xb3\\x9f\\x01\\x00\\x00\\xff\\xff\\x00\\xcf\\x96\\xa8\\xe9\\x01\\x00\\x00\"\n\nfunc indexHtmlBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_indexHtml,\n\t\t\"index.html\",\n\t)\n}\n\nfunc indexHtml() (*asset, error) {\n\tbytes, err := indexHtmlBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"index.html\", size: 489, mode: os.FileMode(438), modTime: time.Unix(1594886229, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\nvar _jsMainJs = \"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\x4a\\xce\\xcf\\x2b\\xce\\xcf\\x49\\xd5\\xcb\\xc9\\x4f\\xd7\\x50\\x4a\\xad\\x48\\xcc\\x2d\\xc8\\x49\\x55\\xd2\\xb4\\xe6\\xe5\\xe2\\xe5\\x4a\\x2b\\xcd\\x4b\\x2e\\xc9\\xcc\\xcf\\x53\\xc8\\xcf\\x73\\xce\\xc9\\x4c\\xce\\xd6\\xd0\\x54\\xa8\\xe6\\xe5\\x52\\x50\\x50\\x50\\x28\\xcf\\xcc\\x4b\\xc9\\x2f\\xd7\\x4b\\xcc\\x49\\x2d\\x2a\\xd1\\x50\\x4a\\x2a\\x2d\\x29\\xc9\\xcf\\x53\\x48\\x06\\xa9\\x49\\x4d\\x01\\x6b\\xae\\x05\\x04\\x00\\x00\\xff\\xff\\xa4\\xb7\\x99\\x52\\x57\\x00\\x00\\x00\"\n\nfunc jsMainJsBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_jsMainJs,\n\t\t\"js/main.js\",\n\t)\n}\n\nfunc jsMainJs() (*asset, error) {\n\tbytes, err := jsMainJsBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"js/main.js\", size: 87, mode: os.FileMode(438), modTime: time.Unix(1594787764, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\n// Asset loads and returns the asset for the given name.\n// It returns an error if the asset could not be found or\n// could not be loaded.\nfunc Asset(name string) ([]byte, error) {\n\tcannonicalName := strings.Replace(name, \"\\\\\", \"/\", -1)\n\tif f, ok := _bindata[cannonicalName]; ok {\n\t\ta, err := f()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"Asset %s can't read by error: %v\", name, err)\n\t\t}\n\t\treturn a.bytes, nil\n\t}\n\treturn nil, fmt.Errorf(\"Asset %s not found\", name)\n}\n\n// MustAsset is like Asset but panics when Asset would return an error.\n// It simplifies safe initialization of global variables.\nfunc MustAsset(name string) []byte {\n\ta, err := Asset(name)\n\tif err != nil {\n\t\tpanic(\"asset: Asset(\" + name + \"): \" + err.Error())\n\t}\n\n\treturn a\n}\n\n// AssetInfo loads and returns the asset info for the given name.\n// It returns an error if the asset could not be found or\n// could not be loaded.\nfunc AssetInfo(name string) (os.FileInfo, error) {\n\tcannonicalName := strings.Replace(name, \"\\\\\", \"/\", -1)\n\tif f, ok := _bindata[cannonicalName]; ok {\n\t\ta, err := f()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"AssetInfo %s can't read by error: %v\", name, err)\n\t\t}\n\t\treturn a.info, nil\n\t}\n\treturn nil, fmt.Errorf(\"AssetInfo %s not found\", name)\n}\n\n// AssetNames returns the names of the assets.\nfunc AssetNames() []string {\n\tnames := make([]string, 0, len(_bindata))\n\tfor name := range _bindata {\n\t\tnames = append(names, name)\n\t}\n\treturn names\n}\n\n// _bindata is a table, holding each asset generator, mapped to its name.\nvar _bindata = map[string]func() (*asset, error){\n\t\"app2/app2app3/css/main.css\":       app2App2app3CssMainCss,\n\t\"app2/app2app3/dirs/dir1/text.txt\": app2App2app3DirsDir1TextTxt,\n\t\"app2/app2app3/dirs/dir2/text.txt\": app2App2app3DirsDir2TextTxt,\n\t\"app2/app2app3/dirs/text.txt\":      app2App2app3DirsTextTxt,\n\t\"app2/app2app3/index.html\":         app2App2app3IndexHtml,\n\t\"app2/index.html\":                  app2IndexHtml,\n\t\"app2/mydir/text.txt\":              app2MydirTextTxt,\n\t\"css/main.css\":                     cssMainCss,\n\t\"favicon.ico\":                      faviconIco,\n\t\"index.html\":                       indexHtml,\n\t\"js/main.js\":                       jsMainJs,\n}\n\n// AssetDir returns the file names below a certain\n// directory embedded in the file by go-bindata.\n// For example if you run go-bindata on data/... and data contains the\n// following hierarchy:\n//\n//\tdata/\n//\t  foo.txt\n//\t  img/\n//\t    a.png\n//\t    b.png\n//\n// then AssetDir(\"data\") would return []string{\"foo.txt\", \"img\"}\n// AssetDir(\"data/img\") would return []string{\"a.png\", \"b.png\"}\n// AssetDir(\"foo.txt\") and AssetDir(\"notexist\") would return an error\n// AssetDir(\"\") will return []string{\"data\"}.\nfunc AssetDir(name string) ([]string, error) {\n\tnode := _bintree\n\tif len(name) != 0 {\n\t\tcannonicalName := strings.Replace(name, \"\\\\\", \"/\", -1)\n\t\tpathList := strings.Split(cannonicalName, \"/\")\n\t\tfor _, p := range pathList {\n\t\t\tnode = node.Children[p]\n\t\t\tif node == nil {\n\t\t\t\treturn nil, fmt.Errorf(\"Asset %s not found\", name)\n\t\t\t}\n\t\t}\n\t}\n\tif node.Func != nil {\n\t\treturn nil, fmt.Errorf(\"Asset %s not found\", name)\n\t}\n\trv := make([]string, 0, len(node.Children))\n\tfor childName := range node.Children {\n\t\trv = append(rv, childName)\n\t}\n\treturn rv, nil\n}\n\ntype bintree struct {\n\tFunc     func() (*asset, error)\n\tChildren map[string]*bintree\n}\n\nvar _bintree = &bintree{nil, map[string]*bintree{\n\t\"app2\": {nil, map[string]*bintree{\n\t\t\"app2app3\": {nil, map[string]*bintree{\n\t\t\t\"css\": {nil, map[string]*bintree{\n\t\t\t\t\"main.css\": {app2App2app3CssMainCss, map[string]*bintree{}},\n\t\t\t}},\n\t\t\t\"dirs\": {nil, map[string]*bintree{\n\t\t\t\t\"dir1\": {nil, map[string]*bintree{\n\t\t\t\t\t\"text.txt\": {app2App2app3DirsDir1TextTxt, map[string]*bintree{}},\n\t\t\t\t}},\n\t\t\t\t\"dir2\": {nil, map[string]*bintree{\n\t\t\t\t\t\"text.txt\": {app2App2app3DirsDir2TextTxt, map[string]*bintree{}},\n\t\t\t\t}},\n\t\t\t\t\"text.txt\": {app2App2app3DirsTextTxt, map[string]*bintree{}},\n\t\t\t}},\n\t\t\t\"index.html\": {app2App2app3IndexHtml, map[string]*bintree{}},\n\t\t}},\n\t\t\"index.html\": {app2IndexHtml, map[string]*bintree{}},\n\t\t\"mydir\": {nil, map[string]*bintree{\n\t\t\t\"text.txt\": {app2MydirTextTxt, map[string]*bintree{}},\n\t\t}},\n\t}},\n\t\"css\": {nil, map[string]*bintree{\n\t\t\"main.css\": {cssMainCss, map[string]*bintree{}},\n\t}},\n\t\"favicon.ico\": {faviconIco, map[string]*bintree{}},\n\t\"index.html\":  {indexHtml, map[string]*bintree{}},\n\t\"js\": {nil, map[string]*bintree{\n\t\t\"main.js\": {jsMainJs, map[string]*bintree{}},\n\t}},\n}}\n\n// RestoreAsset restores an asset under the given directory\nfunc RestoreAsset(dir, name string) error {\n\tdata, err := Asset(name)\n\tif err != nil {\n\t\treturn err\n\t}\n\tinfo, err := AssetInfo(name)\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = os.WriteFile(_filePath(dir, name), data, info.Mode())\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// RestoreAssets restores an asset under the given directory recursively\nfunc RestoreAssets(dir, name string) error {\n\tchildren, err := AssetDir(name)\n\t// File\n\tif err != nil {\n\t\treturn RestoreAsset(dir, name)\n\t}\n\t// Dir\n\tfor _, child := range children {\n\t\terr = RestoreAssets(dir, filepath.Join(name, child))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc _filePath(dir, name string) string {\n\tcannonicalName := strings.Replace(name, \"\\\\\", \"/\", -1)\n\treturn filepath.Join(append([]string{dir}, strings.Split(cannonicalName, \"/\")...)...)\n}\n"
  },
  {
    "path": "_examples/file-server/http2push-embedded-gzipped/main.go",
    "content": "package main\n\nimport (\n\t\"regexp\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\n// How to run:\n// $ go install github.com/go-bindata/go-bindata/v3/go-bindata@latest\n// $ go-bindata -nomemcopy -fs -prefix \"../http2push/assets\" ../http2push/assets/...\n// $ go run .\n\nvar opts = iris.DirOptions{\n\tIndexName: \"index.html\",\n\tPushTargetsRegexp: map[string]*regexp.Regexp{\n\t\t\"/\":              iris.MatchCommonAssets,\n\t\t\"/app2/app2app3\": iris.MatchCommonAssets,\n\t},\n\tShowList: true,\n\tCache: iris.DirCacheOptions{\n\t\tEnable:         true,\n\t\tCompressIgnore: iris.MatchImagesAssets,\n\t\t// Here, define the encodings that the cached files should be pre-compressed\n\t\t// and served based on client's needs.\n\t\tEncodings:       []string{\"gzip\", \"deflate\", \"br\", \"snappy\"},\n\t\tCompressMinSize: 50, // files smaller than this size will NOT be compressed.\n\t\tVerbose:         1,\n\t},\n}\n\nfunc main() {\n\tapp := iris.New()\n\tapp.HandleDir(\"/public\", AssetFile(), opts)\n\n\t// https://127.0.0.1/public\n\t// https://127.0.0.1/public/app2\n\t// https://127.0.0.1/public/app2/app2app3\n\t// https://127.0.0.1/public/app2/app2app3/dirs\n\tapp.Run(iris.TLS(\":443\", \"../http2push/mycert.crt\", \"../http2push/mykey.key\"))\n}\n"
  },
  {
    "path": "_examples/file-server/send-files/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.Logger().SetLevel(\"debug\")\n\n\tapp.Get(\"/\", download)\n\tapp.Get(\"/download\", downloadWithRateLimit)\n\n\tapp.Listen(\":8080\")\n}\n\nfunc download(ctx iris.Context) {\n\tsrc := \"./files/first.zip\"\n\tctx.SendFile(src, \"client.zip\")\n}\n\nfunc downloadWithRateLimit(ctx iris.Context) {\n\t// REPLACE THAT WITH A BIG LOCAL FILE OF YOUR OWN.\n\tsrc := \"./files/first.zip\"\n\tdest := \"\" /* optionally, keep it empty to resolve the filename based on the \"src\" */\n\n\t// Limit download speed to ~50Kb/s with a burst of 100KB.\n\tlimit := 50.0 * iris.KB\n\tburst := 100 * iris.KB\n\tctx.SendFileWithRate(src, dest, limit, burst)\n}\n"
  },
  {
    "path": "_examples/file-server/single-page-application/basic/main.go",
    "content": "package main\n\nimport \"github.com/kataras/iris/v12\"\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\n\tapp.HandleDir(\"/\", iris.Dir(\"./public\"), iris.DirOptions{\n\t\tIndexName: \"index.html\",\n\t\tSPA:       true,\n\t})\n\n\treturn app\n}\n\nfunc main() {\n\tapp := newApp()\n\n\t// http://localhost:8080\n\t// http://localhost:8080/about\n\t// http://localhost:8080/a_notfound\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/file-server/single-page-application/basic/public/index.html",
    "content": "<!DOCTYPE html>\r\n<html lang=\"en\">\r\n\r\n<head>\r\n    <meta charset=\"UTF-8\">\r\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n    <title>Iris SPA Router Example</title>\r\n</head>\r\n\r\n<body>\r\n\r\n    <div id=\"app\">\r\n    </div>\r\n\r\n    <script src=\"https://cdn.jsdelivr.net/npm/vue@2\"></script>\r\n    <script src=\"./index.js\"></script>\r\n</body>\r\n\r\n</html>"
  },
  {
    "path": "_examples/file-server/single-page-application/basic/public/index.js",
    "content": "const NotFound = { template: '<p>Page not found</p>' }\nconst Home = { template: '<p>home page</p>' }\nconst About = { template: '<p>about page</p>' }\n\nconst routes = {\n  '/': Home,\n  '/about': About\n}\n\nconst app = new Vue({\n  el: '#app',\n  data: {\n    currentRoute: window.location.pathname\n  },\n  computed: {\n    ViewComponent () {\n      return routes[this.currentRoute] || NotFound\n    }\n  },\n  render (h) { return h(this.ViewComponent) }\n})\n"
  },
  {
    "path": "_examples/file-server/single-page-application/embedded-single-page-application/bindata.go",
    "content": "// Code generated by go-bindata. (@generated) DO NOT EDIT.\n\n// Package main generated by go-bindata.// sources:\n// data/public/app.js\n// data/public/app2/index.html\n// data/public/css/main.css\n// data/views/index.html\npackage main\n\nimport (\n\t\"bytes\"\n\t\"compress/gzip\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"time\"\n)\n\nfunc bindataRead(data []byte, name string) ([]byte, error) {\n\tgz, err := gzip.NewReader(bytes.NewBuffer(data))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"read %q: %v\", name, err)\n\t}\n\n\tvar buf bytes.Buffer\n\t_, err = io.Copy(&buf, gz)\n\tclErr := gz.Close()\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"read %q: %v\", name, err)\n\t}\n\tif clErr != nil {\n\t\treturn nil, err\n\t}\n\n\treturn buf.Bytes(), nil\n}\n\ntype asset struct {\n\tbytes []byte\n\tinfo  os.FileInfo\n}\n\ntype bindataFileInfo struct {\n\tname    string\n\tsize    int64\n\tmode    os.FileMode\n\tmodTime time.Time\n}\n\n// Name return file name\nfunc (fi bindataFileInfo) Name() string {\n\treturn fi.name\n}\n\n// Size return file size\nfunc (fi bindataFileInfo) Size() int64 {\n\treturn fi.size\n}\n\n// Mode return file mode\nfunc (fi bindataFileInfo) Mode() os.FileMode {\n\treturn fi.mode\n}\n\n// ModTime return file modify time\nfunc (fi bindataFileInfo) ModTime() time.Time {\n\treturn fi.modTime\n}\n\n// IsDir return file whether a directory\nfunc (fi bindataFileInfo) IsDir() bool {\n\treturn fi.mode&os.ModeDir != 0\n}\n\n// Sys return file is sys mode\nfunc (fi bindataFileInfo) Sys() any {\n\treturn nil\n}\n\ntype assetFile struct {\n\t*bytes.Reader\n\tname            string\n\tchildInfos      []os.FileInfo\n\tchildInfoOffset int\n}\n\ntype assetOperator struct{}\n\n// Open implement http.FileSystem interface\nfunc (f *assetOperator) Open(name string) (http.File, error) {\n\tvar err error\n\tif len(name) > 0 && name[0] == '/' {\n\t\tname = name[1:]\n\t}\n\tcontent, err := Asset(name)\n\tif err == nil {\n\t\treturn &assetFile{name: name, Reader: bytes.NewReader(content)}, nil\n\t}\n\tchildren, err := AssetDir(name)\n\tif err == nil {\n\t\tchildInfos := make([]os.FileInfo, 0, len(children))\n\t\tfor _, child := range children {\n\t\t\tchildPath := filepath.Join(name, child)\n\t\t\tinfo, errInfo := AssetInfo(filepath.Join(name, child))\n\t\t\tif errInfo == nil {\n\t\t\t\tchildInfos = append(childInfos, info)\n\t\t\t} else {\n\t\t\t\tchildInfos = append(childInfos, newDirFileInfo(childPath))\n\t\t\t}\n\t\t}\n\t\treturn &assetFile{name: name, childInfos: childInfos}, nil\n\t} else {\n\t\t// If the error is not found, return an error that will\n\t\t// result in a 404 error. Otherwise the server returns\n\t\t// a 500 error for files not found.\n\t\tif strings.Contains(err.Error(), \"not found\") {\n\t\t\treturn nil, os.ErrNotExist\n\t\t}\n\t\treturn nil, err\n\t}\n}\n\n// Close no need do anything\nfunc (f *assetFile) Close() error {\n\treturn nil\n}\n\n// Readdir read dir's children file info\nfunc (f *assetFile) Readdir(count int) ([]os.FileInfo, error) {\n\tif len(f.childInfos) == 0 {\n\t\treturn nil, os.ErrNotExist\n\t}\n\tif count <= 0 {\n\t\treturn f.childInfos, nil\n\t}\n\tif f.childInfoOffset+count > len(f.childInfos) {\n\t\tcount = len(f.childInfos) - f.childInfoOffset\n\t}\n\toffset := f.childInfoOffset\n\tf.childInfoOffset += count\n\treturn f.childInfos[offset : offset+count], nil\n}\n\n// Stat read file info from asset item\nfunc (f *assetFile) Stat() (os.FileInfo, error) {\n\tif len(f.childInfos) != 0 {\n\t\treturn newDirFileInfo(f.name), nil\n\t}\n\treturn AssetInfo(f.name)\n}\n\n// newDirFileInfo return default dir file info\nfunc newDirFileInfo(name string) os.FileInfo {\n\treturn &bindataFileInfo{\n\t\tname:    name,\n\t\tsize:    0,\n\t\tmode:    os.FileMode(2147484068), // equal os.FileMode(0644)|os.ModeDir\n\t\tmodTime: time.Time{}}\n}\n\n// AssetFile return a http.FileSystem instance that data backend by asset\nfunc AssetFile() http.FileSystem {\n\treturn &assetOperator{}\n}\n\nvar _publicAppJs = []byte(\"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\x2a\\xcf\\xcc\\x4b\\xc9\\x2f\\xd7\\x4b\\xcc\\x49\\x2d\\x2a\\xd1\\x50\\x4a\\x2c\\x28\\xd0\\xcb\\x2a\\x56\\xc8\\xc9\\x4f\\x4c\\x49\\x4d\\x51\\x48\\x2b\\xca\\xcf\\x55\\x88\\x51\\xd2\\x57\\xd2\\xb4\\x06\\x04\\x00\\x00\\xff\\xff\\xa9\\x06\\xf7\\xa3\\x27\\x00\\x00\\x00\")\n\nfunc publicAppJsBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_publicAppJs,\n\t\t\"public/app.js\",\n\t)\n}\n\nfunc publicAppJs() (*asset, error) {\n\tbytes, err := publicAppJsBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"public/app.js\", size: 39, mode: os.FileMode(420), modTime: time.Unix(1663416115, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\nvar _publicApp2IndexHtml = []byte(\"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\x2c\\xcd\\x31\\x0a\\x02\\x31\\x10\\x85\\xe1\\x7e\\x4e\\xf1\\x4a\\x6d\\x0c\\xbb\\xf5\\x10\\xb0\\xdb\\x42\\x41\\xd0\\x0b\\x44\\x33\\x9a\\x40\\xd6\\x0c\\x32\\x85\\xde\\x5e\\x86\\x6c\\xf9\\xe0\\xf1\\xfd\\x5c\\x6c\\x6d\\x91\\x88\\x8b\\xa4\\x1c\\x09\\x00\\xd8\\xaa\\x35\\x89\\x47\\x55\\xcc\\x1c\\xc6\\x20\\x0e\\xe3\\x40\\x7c\\xef\\xf9\\xb7\\x1d\\xcb\\x14\\xb1\\xbb\\x5a\\xb2\\xfa\\xc0\\x72\\x3b\\x9f\\x70\\x49\\x2f\\xd9\\x63\\x91\\xd6\\x3a\\x9e\\x9f\\xbe\\x22\\xa9\\xce\\xa1\\xbe\\xb3\\x7c\\x0f\\x1e\\x02\\x87\\x32\\x39\\x36\\x10\\x57\\x3d\\xff\\x0f\\x00\\x00\\xff\\xff\\xdd\\xbe\\x30\\x69\\x85\\x00\\x00\\x00\")\n\nfunc publicApp2IndexHtmlBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_publicApp2IndexHtml,\n\t\t\"public/app2/index.html\",\n\t)\n}\n\nfunc publicApp2IndexHtml() (*asset, error) {\n\tbytes, err := publicApp2IndexHtmlBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"public/app2/index.html\", size: 133, mode: os.FileMode(420), modTime: time.Unix(1663416115, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\nvar _publicCssMainCss = []byte(\"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\x4a\\xca\\x4f\\xa9\\x54\\xa8\\xe6\\x52\\x50\\x50\\x50\\x48\\x4a\\x4c\\xce\\x4e\\x2f\\xca\\x2f\\xcd\\x4b\\xd1\\x4d\\xce\\xcf\\xc9\\x2f\\xb2\\x52\\x48\\xca\\x49\\x4c\\xce\\xb6\\xe6\\xaa\\xe5\\x02\\x04\\x00\\x00\\xff\\xff\\x96\\x97\\xac\\xb1\\x26\\x00\\x00\\x00\")\n\nfunc publicCssMainCssBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_publicCssMainCss,\n\t\t\"public/css/main.css\",\n\t)\n}\n\nfunc publicCssMainCss() (*asset, error) {\n\tbytes, err := publicCssMainCssBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"public/css/main.css\", size: 38, mode: os.FileMode(420), modTime: time.Unix(1663416115, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\nvar _viewsIndexHtml = []byte(\"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\x2c\\xce\\xbd\\x0e\\xc2\\x30\\x0c\\x04\\xe0\\xdd\\x4f\\x71\\xea\\x04\\x4b\\xa2\\xee\\xc6\\x33\\x23\\x43\\x5f\\x20\\xb4\\x86\\x04\\xa5\\x34\\x6a\\x22\\x7e\\x54\\xf5\\xdd\\x51\\x14\\x46\\xeb\\x7c\\xfa\\x8e\\x7d\\x99\\xa3\\x10\\xb1\\x57\\x37\\x09\\x01\\x00\\x97\\x50\\xa2\\xca\\xb6\\xc1\\x5c\\xdc\\x5d\\xcd\\x50\\x4f\\xec\\x3b\\xdb\\x16\\x10\\xdb\\xf6\\x4c\\x7c\\x5d\\xa6\\xef\\xbf\\xe4\\x7b\\xc1\\x61\\xd0\\x39\\x45\\x57\\xf4\\x88\\xb3\\xc6\\xb8\\xe0\\xb6\\x2e\\x33\\x5e\\x41\\xdf\\xd9\\x86\\xe7\\xa4\\x1f\\x53\\x35\\xb0\\xf5\\xbd\\x10\\xb5\\x5e\\x1e\\xd7\\x90\\x0a\\xf2\\x3a\\x9e\\x3a\\xeb\\x52\\x32\\x8f\\xdc\\x09\\xc0\\xb6\\x05\\x55\\x6b\\x4a\\x65\\xeb\\xd6\\x5f\\x00\\x00\\x00\\xff\\xff\\xd6\\xa4\\xa5\\x16\\xb2\\x00\\x00\\x00\")\n\nfunc viewsIndexHtmlBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_viewsIndexHtml,\n\t\t\"views/index.html\",\n\t)\n}\n\nfunc viewsIndexHtml() (*asset, error) {\n\tbytes, err := viewsIndexHtmlBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"views/index.html\", size: 178, mode: os.FileMode(420), modTime: time.Unix(1663416115, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\n// Asset loads and returns the asset for the given name.\n// It returns an error if the asset could not be found or\n// could not be loaded.\nfunc Asset(name string) ([]byte, error) {\n\tcannonicalName := strings.Replace(name, \"\\\\\", \"/\", -1)\n\tif f, ok := _bindata[cannonicalName]; ok {\n\t\ta, err := f()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"Asset %s can't read by error: %v\", name, err)\n\t\t}\n\t\treturn a.bytes, nil\n\t}\n\treturn nil, fmt.Errorf(\"Asset %s not found\", name)\n}\n\n// MustAsset is like Asset but panics when Asset would return an error.\n// It simplifies safe initialization of global variables.\nfunc MustAsset(name string) []byte {\n\ta, err := Asset(name)\n\tif err != nil {\n\t\tpanic(\"asset: Asset(\" + name + \"): \" + err.Error())\n\t}\n\n\treturn a\n}\n\n// AssetInfo loads and returns the asset info for the given name.\n// It returns an error if the asset could not be found or\n// could not be loaded.\nfunc AssetInfo(name string) (os.FileInfo, error) {\n\tcannonicalName := strings.Replace(name, \"\\\\\", \"/\", -1)\n\tif f, ok := _bindata[cannonicalName]; ok {\n\t\ta, err := f()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"AssetInfo %s can't read by error: %v\", name, err)\n\t\t}\n\t\treturn a.info, nil\n\t}\n\treturn nil, fmt.Errorf(\"AssetInfo %s not found\", name)\n}\n\n// AssetNames returns the names of the assets.\nfunc AssetNames() []string {\n\tnames := make([]string, 0, len(_bindata))\n\tfor name := range _bindata {\n\t\tnames = append(names, name)\n\t}\n\treturn names\n}\n\n// _bindata is a table, holding each asset generator, mapped to its name.\nvar _bindata = map[string]func() (*asset, error){\n\t\"public/app.js\":          publicAppJs,\n\t\"public/app2/index.html\": publicApp2IndexHtml,\n\t\"public/css/main.css\":    publicCssMainCss,\n\t\"views/index.html\":       viewsIndexHtml,\n}\n\n// AssetDir returns the file names below a certain\n// directory embedded in the file by go-bindata.\n// For example if you run go-bindata on data/... and data contains the\n// following hierarchy:\n//\n//\tdata/\n//\t  foo.txt\n//\t  img/\n//\t    a.png\n//\t    b.png\n//\n// then AssetDir(\"data\") would return []string{\"foo.txt\", \"img\"}\n// AssetDir(\"data/img\") would return []string{\"a.png\", \"b.png\"}\n// AssetDir(\"foo.txt\") and AssetDir(\"notexist\") would return an error\n// AssetDir(\"\") will return []string{\"data\"}.\nfunc AssetDir(name string) ([]string, error) {\n\tnode := _bintree\n\tif len(name) != 0 {\n\t\tcannonicalName := strings.Replace(name, \"\\\\\", \"/\", -1)\n\t\tpathList := strings.Split(cannonicalName, \"/\")\n\t\tfor _, p := range pathList {\n\t\t\tnode = node.Children[p]\n\t\t\tif node == nil {\n\t\t\t\treturn nil, fmt.Errorf(\"Asset %s not found\", name)\n\t\t\t}\n\t\t}\n\t}\n\tif node.Func != nil {\n\t\treturn nil, fmt.Errorf(\"Asset %s not found\", name)\n\t}\n\trv := make([]string, 0, len(node.Children))\n\tfor childName := range node.Children {\n\t\trv = append(rv, childName)\n\t}\n\treturn rv, nil\n}\n\ntype bintree struct {\n\tFunc     func() (*asset, error)\n\tChildren map[string]*bintree\n}\n\nvar _bintree = &bintree{nil, map[string]*bintree{\n\t\"public\": {nil, map[string]*bintree{\n\t\t\"app.js\": {publicAppJs, map[string]*bintree{}},\n\t\t\"app2\": {nil, map[string]*bintree{\n\t\t\t\"index.html\": {publicApp2IndexHtml, map[string]*bintree{}},\n\t\t}},\n\t\t\"css\": {nil, map[string]*bintree{\n\t\t\t\"main.css\": {publicCssMainCss, map[string]*bintree{}},\n\t\t}},\n\t}},\n\t\"views\": {nil, map[string]*bintree{\n\t\t\"index.html\": {viewsIndexHtml, map[string]*bintree{}},\n\t}},\n}}\n\n// RestoreAsset restores an asset under the given directory\nfunc RestoreAsset(dir, name string) error {\n\tdata, err := Asset(name)\n\tif err != nil {\n\t\treturn err\n\t}\n\tinfo, err := AssetInfo(name)\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = ioutil.WriteFile(_filePath(dir, name), data, info.Mode())\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// RestoreAssets restores an asset under the given directory recursively\nfunc RestoreAssets(dir, name string) error {\n\tchildren, err := AssetDir(name)\n\t// File\n\tif err != nil {\n\t\treturn RestoreAsset(dir, name)\n\t}\n\t// Dir\n\tfor _, child := range children {\n\t\terr = RestoreAssets(dir, filepath.Join(name, child))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc _filePath(dir, name string) string {\n\tcannonicalName := strings.Replace(name, \"\\\\\", \"/\", -1)\n\treturn filepath.Join(append([]string{dir}, strings.Split(cannonicalName, \"/\")...)...)\n}\n"
  },
  {
    "path": "_examples/file-server/single-page-application/embedded-single-page-application/data/public/app.js",
    "content": "window.alert(\"app.js loaded from \\\"/\");"
  },
  {
    "path": "_examples/file-server/single-page-application/embedded-single-page-application/data/public/app2/index.html",
    "content": "<html>\n\n<head>\n    <title>App 2</title>\n</head>\n\n<body>\n    <h1> (Static HTML Page) Hello from app2/index.html </h1>\n</body>\n\n</html>"
  },
  {
    "path": "_examples/file-server/single-page-application/embedded-single-page-application/data/public/css/main.css",
    "content": "body {\n    background-color: black;\n}\n"
  },
  {
    "path": "_examples/file-server/single-page-application/embedded-single-page-application/data/views/index.html",
    "content": "<html>\n\n<head>\n    <title>{{ .Page.Title }}</title>\n</head>\n\n<body>\n    <h1> (Template) Hello from views/index.html </h1>\n\n\n    <script src=\"/app.js\">  </script>\n</body>\n\n</html>"
  },
  {
    "path": "_examples/file-server/single-page-application/embedded-single-page-application/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\n// $ go install github.com/go-bindata/go-bindata/v3/go-bindata@latest\n// $ go-bindata -prefix \"data\" -fs ./data/...\n// $ go run .\n\nvar page = struct {\n\tTitle string\n}{\"Welcome\"}\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\n\tapp.RegisterView(iris.HTML(AssetFile(), \".html\").RootDir(\"views\"))\n\n\t// Using the iris.PrefixDir you can select\n\t// which directories to use under a particular file system,\n\t// e.g. for views the ./public:\n\t// publicFS := iris.PrefixDir(\"./public\", AssetFile())\n\tpublicFS := iris.PrefixDir(\"./public\", AssetFile())\n\tapp.HandleDir(\"/\", publicFS)\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.ViewData(\"Page\", page)\n\t\tif err := ctx.View(\"index.html\"); err != nil {\n\t\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\t\treturn\n\t\t}\n\t})\n\n\treturn app\n}\n\nfunc main() {\n\tapp := newApp()\n\n\t// http://localhost:8080\n\t// http://localhost:8080/app.js\n\t// http://localhost:8080/css/main.css\n\t// http://localhost:8080/app2\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/file-server/single-page-application/embedded-single-page-application/main_test.go",
    "content": "package main\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\ntype resource string\n\nfunc (r resource) contentType() string {\n\tswitch filepath.Ext(r.String()) {\n\tcase \".js\":\n\t\treturn \"text/javascript\"\n\tcase \".css\":\n\t\treturn \"text/css\"\n\tdefault:\n\t\treturn \"text/html\"\n\t}\n}\n\nfunc (r resource) String() string {\n\treturn string(r)\n}\n\nfunc (r resource) strip(strip string) string {\n\ts := r.String()\n\treturn strings.TrimPrefix(s, strip)\n}\n\nfunc (r resource) loadFromBase(dir string) string {\n\tfilename := r.String()\n\n\tif strings.HasSuffix(filename, \"/\") {\n\t\tfilename = filename + \"index.html\"\n\t}\n\n\tfullpath := filepath.Join(dir, filename)\n\n\tb, err := os.ReadFile(fullpath)\n\tif err != nil {\n\t\tpanic(fullpath + \" failed with error: \" + err.Error())\n\t}\n\tresult := string(b)\n\tif runtime.GOOS != \"windows\" {\n\t\tresult = strings.ReplaceAll(result, \"\\n\", \"\\r\\n\")\n\t\tresult = strings.ReplaceAll(result, \"\\r\\r\", \"\")\n\t}\n\treturn result\n}\n\nvar urls = []resource{\n\t\"/\",\n\t\"/app.js\",\n\t\"/css/main.css\",\n\t\"/app2/\",\n\t\"/app2/index.html\",\n}\n\nfunc TestSPAEmbedded(t *testing.T) {\n\tapp := newApp()\n\te := httptest.New(t, app)\n\n\tfor _, u := range urls {\n\t\turl := u.String()\n\t\tbase := \"./data/public\"\n\t\tif u == \"/\" || u == \"/index.html\" {\n\t\t\tbase = \"./data/views\"\n\t\t}\n\t\tcontents := u.loadFromBase(base)\n\t\tcontents = strings.Replace(contents, \"{{ .Page.Title }}\", page.Title, 1)\n\n\t\te.GET(url).Expect().\n\t\t\tStatus(httptest.StatusOK).\n\t\t\tContentType(u.contentType(), app.ConfigurationReadOnly().GetCharset()).\n\t\t\tBody().IsEqual(contents)\n\t}\n\n\te.GET(\"/index.html\").Expect().Status(httptest.StatusNotFound) // only root is served.\n}\n"
  },
  {
    "path": "_examples/file-server/single-page-application/embedded-single-page-application-with-other-routes/bindata.go",
    "content": "// Code generated by go-bindata. (@generated) DO NOT EDIT.\n\n// Package main generated by go-bindata.// sources:\n// public/app.js\n// public/css/main.css\n// public/index.html\npackage main\n\nimport (\n\t\"bytes\"\n\t\"compress/gzip\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"time\"\n)\n\nfunc bindataRead(data []byte, name string) ([]byte, error) {\n\tgz, err := gzip.NewReader(bytes.NewBuffer(data))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"read %q: %v\", name, err)\n\t}\n\n\tvar buf bytes.Buffer\n\t_, err = io.Copy(&buf, gz)\n\tclErr := gz.Close()\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"read %q: %v\", name, err)\n\t}\n\tif clErr != nil {\n\t\treturn nil, err\n\t}\n\n\treturn buf.Bytes(), nil\n}\n\ntype asset struct {\n\tbytes []byte\n\tinfo  os.FileInfo\n}\n\ntype bindataFileInfo struct {\n\tname    string\n\tsize    int64\n\tmode    os.FileMode\n\tmodTime time.Time\n}\n\n// Name return file name\nfunc (fi bindataFileInfo) Name() string {\n\treturn fi.name\n}\n\n// Size return file size\nfunc (fi bindataFileInfo) Size() int64 {\n\treturn fi.size\n}\n\n// Mode return file mode\nfunc (fi bindataFileInfo) Mode() os.FileMode {\n\treturn fi.mode\n}\n\n// ModTime return file modify time\nfunc (fi bindataFileInfo) ModTime() time.Time {\n\treturn fi.modTime\n}\n\n// IsDir return file whether a directory\nfunc (fi bindataFileInfo) IsDir() bool {\n\treturn fi.mode&os.ModeDir != 0\n}\n\n// Sys return file is sys mode\nfunc (fi bindataFileInfo) Sys() any {\n\treturn nil\n}\n\ntype assetFile struct {\n\t*bytes.Reader\n\tname            string\n\tchildInfos      []os.FileInfo\n\tchildInfoOffset int\n}\n\ntype assetOperator struct{}\n\n// Open implement http.FileSystem interface\nfunc (f *assetOperator) Open(name string) (http.File, error) {\n\tvar err error\n\tif len(name) > 0 && name[0] == '/' {\n\t\tname = name[1:]\n\t}\n\tcontent, err := Asset(name)\n\tif err == nil {\n\t\treturn &assetFile{name: name, Reader: bytes.NewReader(content)}, nil\n\t}\n\tchildren, err := AssetDir(name)\n\tif err == nil {\n\t\tchildInfos := make([]os.FileInfo, 0, len(children))\n\t\tfor _, child := range children {\n\t\t\tchildPath := filepath.Join(name, child)\n\t\t\tinfo, errInfo := AssetInfo(filepath.Join(name, child))\n\t\t\tif errInfo == nil {\n\t\t\t\tchildInfos = append(childInfos, info)\n\t\t\t} else {\n\t\t\t\tchildInfos = append(childInfos, newDirFileInfo(childPath))\n\t\t\t}\n\t\t}\n\t\treturn &assetFile{name: name, childInfos: childInfos}, nil\n\t} else {\n\t\t// If the error is not found, return an error that will\n\t\t// result in a 404 error. Otherwise the server returns\n\t\t// a 500 error for files not found.\n\t\tif strings.Contains(err.Error(), \"not found\") {\n\t\t\treturn nil, os.ErrNotExist\n\t\t}\n\t\treturn nil, err\n\t}\n}\n\n// Close no need do anything\nfunc (f *assetFile) Close() error {\n\treturn nil\n}\n\n// Readdir read dir's children file info\nfunc (f *assetFile) Readdir(count int) ([]os.FileInfo, error) {\n\tif len(f.childInfos) == 0 {\n\t\treturn nil, os.ErrNotExist\n\t}\n\tif count <= 0 {\n\t\treturn f.childInfos, nil\n\t}\n\tif f.childInfoOffset+count > len(f.childInfos) {\n\t\tcount = len(f.childInfos) - f.childInfoOffset\n\t}\n\toffset := f.childInfoOffset\n\tf.childInfoOffset += count\n\treturn f.childInfos[offset : offset+count], nil\n}\n\n// Stat read file info from asset item\nfunc (f *assetFile) Stat() (os.FileInfo, error) {\n\tif len(f.childInfos) != 0 {\n\t\treturn newDirFileInfo(f.name), nil\n\t}\n\treturn AssetInfo(f.name)\n}\n\n// newDirFileInfo return default dir file info\nfunc newDirFileInfo(name string) os.FileInfo {\n\treturn &bindataFileInfo{\n\t\tname:    name,\n\t\tsize:    0,\n\t\tmode:    os.FileMode(2147484068), // equal os.FileMode(0644)|os.ModeDir\n\t\tmodTime: time.Time{}}\n}\n\n// AssetFile return a http.FileSystem instance that data backend by asset\nfunc AssetFile() http.FileSystem {\n\treturn &assetOperator{}\n}\n\nvar _appJs = []byte(\"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\x2a\\xcf\\xcc\\x4b\\xc9\\x2f\\xd7\\x4b\\xcc\\x49\\x2d\\x2a\\xd1\\x50\\x4a\\x2c\\x28\\xd0\\xcb\\x2a\\x56\\xc8\\xc9\\x4f\\x4c\\x49\\x4d\\x51\\x48\\x2b\\xca\\xcf\\x55\\x88\\x51\\xd2\\x57\\xd2\\xb4\\x06\\x04\\x00\\x00\\xff\\xff\\xa9\\x06\\xf7\\xa3\\x27\\x00\\x00\\x00\")\n\nfunc appJsBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_appJs,\n\t\t\"app.js\",\n\t)\n}\n\nfunc appJs() (*asset, error) {\n\tbytes, err := appJsBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"app.js\", size: 39, mode: os.FileMode(438), modTime: time.Unix(1565946441, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\nvar _cssMainCss = []byte(\"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\x4a\\xca\\x4f\\xa9\\x54\\xa8\\xe6\\xe5\\x52\\x50\\x50\\x50\\x48\\x4a\\x4c\\xce\\x4e\\x2f\\xca\\x2f\\xcd\\x4b\\xd1\\x4d\\xce\\xcf\\xc9\\x2f\\xb2\\x52\\x48\\xca\\x49\\x4c\\xce\\xb6\\xe6\\xe5\\xaa\\xe5\\xe5\\x02\\x04\\x00\\x00\\xff\\xff\\x03\\x25\\x9c\\x89\\x29\\x00\\x00\\x00\")\n\nfunc cssMainCssBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_cssMainCss,\n\t\t\"css/main.css\",\n\t)\n}\n\nfunc cssMainCss() (*asset, error) {\n\tbytes, err := cssMainCssBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"css/main.css\", size: 41, mode: os.FileMode(438), modTime: time.Unix(1565946441, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\nvar _indexHtml = []byte(\"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\x4c\\x8e\\x41\\x0e\\xc2\\x20\\x10\\x45\\xf7\\x24\\xdc\\xe1\\xa7\\x07\\x28\\xe9\\x7e\\x64\\xed\\x35\\x10\\x46\\xc1\\x50\\x21\\x30\\x0b\\xbd\\xbd\\x29\\xc5\\xc4\\xf5\\x7f\\x6f\\xde\\x50\\x94\\x3d\\x5b\\xad\\xb4\\xa2\\xc8\\x2e\\x58\\xad\\x00\\x80\\x24\\x49\\x66\\x7b\\xe5\\x9c\\x0b\\xee\\xad\\xec\\xe8\\xe2\\x24\\x79\\x54\\xf7\\x60\\x32\\xe7\\xaa\\x15\\x99\\xe9\\x68\\x45\\xb7\\x12\\x3e\\x3f\\x3b\\x6e\\x16\\x7f\\x6e\\x7a\\x05\\x7e\\xaf\\x47\\x08\\x64\\xe2\\x36\\xf8\\x49\\x76\\xdf\\x52\\x15\\xf4\\xe6\\x2f\\x8b\\x71\\xb5\\xae\\xcf\\xbe\\x58\\x80\\xcc\\x39\\x8c\\xc6\\xbc\\x3c\\x72\\xc7\\xb3\\xdf\\x00\\x00\\x00\\xff\\xff\\x7e\\xad\\xd1\\x97\\xb3\\x00\\x00\\x00\")\n\nfunc indexHtmlBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_indexHtml,\n\t\t\"index.html\",\n\t)\n}\n\nfunc indexHtml() (*asset, error) {\n\tbytes, err := indexHtmlBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"index.html\", size: 179, mode: os.FileMode(438), modTime: time.Unix(1565946441, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\n// Asset loads and returns the asset for the given name.\n// It returns an error if the asset could not be found or\n// could not be loaded.\nfunc Asset(name string) ([]byte, error) {\n\tcannonicalName := strings.Replace(name, \"\\\\\", \"/\", -1)\n\tif f, ok := _bindata[cannonicalName]; ok {\n\t\ta, err := f()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"Asset %s can't read by error: %v\", name, err)\n\t\t}\n\t\treturn a.bytes, nil\n\t}\n\treturn nil, fmt.Errorf(\"Asset %s not found\", name)\n}\n\n// MustAsset is like Asset but panics when Asset would return an error.\n// It simplifies safe initialization of global variables.\nfunc MustAsset(name string) []byte {\n\ta, err := Asset(name)\n\tif err != nil {\n\t\tpanic(\"asset: Asset(\" + name + \"): \" + err.Error())\n\t}\n\n\treturn a\n}\n\n// AssetInfo loads and returns the asset info for the given name.\n// It returns an error if the asset could not be found or\n// could not be loaded.\nfunc AssetInfo(name string) (os.FileInfo, error) {\n\tcannonicalName := strings.Replace(name, \"\\\\\", \"/\", -1)\n\tif f, ok := _bindata[cannonicalName]; ok {\n\t\ta, err := f()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"AssetInfo %s can't read by error: %v\", name, err)\n\t\t}\n\t\treturn a.info, nil\n\t}\n\treturn nil, fmt.Errorf(\"AssetInfo %s not found\", name)\n}\n\n// AssetNames returns the names of the assets.\nfunc AssetNames() []string {\n\tnames := make([]string, 0, len(_bindata))\n\tfor name := range _bindata {\n\t\tnames = append(names, name)\n\t}\n\treturn names\n}\n\n// _bindata is a table, holding each asset generator, mapped to its name.\nvar _bindata = map[string]func() (*asset, error){\n\t\"app.js\":       appJs,\n\t\"css/main.css\": cssMainCss,\n\t\"index.html\":   indexHtml,\n}\n\n// AssetDir returns the file names below a certain\n// directory embedded in the file by go-bindata.\n// For example if you run go-bindata on data/... and data contains the\n// following hierarchy:\n//\n//\tdata/\n//\t  foo.txt\n//\t  img/\n//\t    a.png\n//\t    b.png\n//\n// then AssetDir(\"data\") would return []string{\"foo.txt\", \"img\"}\n// AssetDir(\"data/img\") would return []string{\"a.png\", \"b.png\"}\n// AssetDir(\"foo.txt\") and AssetDir(\"notexist\") would return an error\n// AssetDir(\"\") will return []string{\"data\"}.\nfunc AssetDir(name string) ([]string, error) {\n\tnode := _bintree\n\tif len(name) != 0 {\n\t\tcannonicalName := strings.Replace(name, \"\\\\\", \"/\", -1)\n\t\tpathList := strings.Split(cannonicalName, \"/\")\n\t\tfor _, p := range pathList {\n\t\t\tnode = node.Children[p]\n\t\t\tif node == nil {\n\t\t\t\treturn nil, fmt.Errorf(\"Asset %s not found\", name)\n\t\t\t}\n\t\t}\n\t}\n\tif node.Func != nil {\n\t\treturn nil, fmt.Errorf(\"Asset %s not found\", name)\n\t}\n\trv := make([]string, 0, len(node.Children))\n\tfor childName := range node.Children {\n\t\trv = append(rv, childName)\n\t}\n\treturn rv, nil\n}\n\ntype bintree struct {\n\tFunc     func() (*asset, error)\n\tChildren map[string]*bintree\n}\n\nvar _bintree = &bintree{nil, map[string]*bintree{\n\t\"app.js\": {appJs, map[string]*bintree{}},\n\t\"css\": {nil, map[string]*bintree{\n\t\t\"main.css\": {cssMainCss, map[string]*bintree{}},\n\t}},\n\t\"index.html\": {indexHtml, map[string]*bintree{}},\n}}\n\n// RestoreAsset restores an asset under the given directory\nfunc RestoreAsset(dir, name string) error {\n\tdata, err := Asset(name)\n\tif err != nil {\n\t\treturn err\n\t}\n\tinfo, err := AssetInfo(name)\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = os.WriteFile(_filePath(dir, name), data, info.Mode())\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// RestoreAssets restores an asset under the given directory recursively\nfunc RestoreAssets(dir, name string) error {\n\tchildren, err := AssetDir(name)\n\t// File\n\tif err != nil {\n\t\treturn RestoreAsset(dir, name)\n\t}\n\t// Dir\n\tfor _, child := range children {\n\t\terr = RestoreAssets(dir, filepath.Join(name, child))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc _filePath(dir, name string) string {\n\tcannonicalName := strings.Replace(name, \"\\\\\", \"/\", -1)\n\treturn filepath.Join(append([]string{dir}, strings.Split(cannonicalName, \"/\")...)...)\n}\n"
  },
  {
    "path": "_examples/file-server/single-page-application/embedded-single-page-application-with-other-routes/main.go",
    "content": "package main\n\nimport \"github.com/kataras/iris/v12\"\n\n// $ go install github.com/go-bindata/go-bindata/v3/go-bindata@latest\n// $ go-bindata -fs -prefix \"public\" ./public/...\n// $ go run .\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\tapp.OnErrorCode(iris.StatusNotFound, func(ctx iris.Context) {\n\t\tctx.Writef(\"404 not found here\")\n\t})\n\n\tapp.HandleDir(\"/\", AssetFile())\n\n\t// Note:\n\t// if you want a dynamic index page then see the file-server/embedded-single-page-application\n\t// which is registering a view engine based on bindata as well and a root route.\n\n\tapp.Get(\"/ping\", func(ctx iris.Context) {\n\t\tctx.WriteString(\"pong\")\n\t})\n\tapp.Get(\"/.well-known\", func(ctx iris.Context) {\n\t\tctx.WriteString(\"well-known\")\n\t})\n\tapp.Get(\".well-known/ready\", func(ctx iris.Context) {\n\t\tctx.WriteString(\"ready\")\n\t})\n\tapp.Get(\".well-known/live\", func(ctx iris.Context) {\n\t\tctx.WriteString(\"live\")\n\t})\n\tapp.Get(\".well-known/metrics\", func(ctx iris.Context) {\n\t\tctx.Writef(\"metrics\")\n\t})\n\treturn app\n}\n\nfunc main() {\n\tapp := newApp()\n\n\t// http://localhost:8080/index.html\n\t// http://localhost:8080/app.js\n\t// http://localhost:8080/css/main.css\n\t//\n\t// http://localhost:8080/ping\n\t// http://localhost:8080/.well-known\n\t// http://localhost:8080/.well-known/ready\n\t// http://localhost:8080/.well-known/live\n\t// http://localhost:8080/.well-known/metrics\n\t//\n\t// Remember: we could use the root wildcard `app.Get(\"/{param:path}\")` and serve the files manually as well.\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/file-server/single-page-application/embedded-single-page-application-with-other-routes/public/app.js",
    "content": "window.alert(\"app.js loaded from static page of \\\"/\");"
  },
  {
    "path": "_examples/file-server/single-page-application/embedded-single-page-application-with-other-routes/public/css/main.css",
    "content": "body {\n    background-color: black;\n}\n"
  },
  {
    "path": "_examples/file-server/single-page-application/embedded-single-page-application-with-other-routes/public/index.html",
    "content": "<html>\n\n<head>\n    <title>Hello from static page</title>\n</head>\n\n<body>\n    <h1> Hello from index.html </h1>\n\n\n    <script src=\"/app.js\">  </script>\n</body>\n\n</html>"
  },
  {
    "path": "_examples/file-server/spa-vue-router/frontend/css/page.css",
    "content": ".router-link-active {\n    color: red;\n  }\n  "
  },
  {
    "path": "_examples/file-server/spa-vue-router/frontend/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>Iris + Vue Router</title>\n  <script src=\"https://unpkg.com/vue/dist/vue.js\"></script>\n  <script src=\"https://unpkg.com/vue-router/dist/vue-router.js\"></script>\n  <link rel=\"stylesheet\" href=\"/css/page.css\">\n</head>\n\n<body>\n  <div id=\"app\">\n    <h1>Hello App!</h1>\n    <p>\n      <!-- use router-link component for navigation. -->\n      <!-- specify the link by passing the `to` prop. -->\n      <!-- `<router-link>` will be rendered as an `<a>` tag by default -->\n      <router-link to=\"/foo\">Go to Foo</router-link>\n      <router-link to=\"/bar\">Go to Bar</router-link>\n    </p>\n    <!-- route outlet -->\n    <!-- component matched by the route will render here -->\n    <router-view></router-view>\n  </div>\n  <script src=\"/js/app.js\"></script>\n</body>\n\n</html>"
  },
  {
    "path": "_examples/file-server/spa-vue-router/frontend/js/app.js",
    "content": "// 0. If using a module system (e.g. via vue-cli), import Vue and VueRouter\n// and then call `Vue.use(VueRouter)`.\n\n// 1. Define route components.\n// These can be imported from other files\nconst Foo = { template: '<div>foo</div>' }\nconst Bar = { template: '<div>bar</div>' }\n\n// 2. Define some routes\n// Each route should map to a component. The \"component\" can\n// either be an actual component constructor created via\n// `Vue.extend()`, or just a component options object.\n// We'll talk about nested routes later.\nconst routes = [\n  { path: '/foo', component: Foo },\n  { path: '/bar', component: Bar }\n]\n\n// 3. Create the router instance and pass the `routes` option\n// You can pass in additional options here, but let's\n// keep it simple for now.\nconst router = new VueRouter({\n  routes // short for `routes: routes`\n})\n\n// 4. Create and mount the root instance.\n// Make sure to inject the router with the router option to make the\n// whole app router-aware.\nconst app = new Vue({\n  router\n}).$mount('#app')\n\n// Now the app has started!"
  },
  {
    "path": "_examples/file-server/spa-vue-router/main.go",
    "content": "// Package main simply shows how you can getting started with Iris and Vue Router.\n// Read more at: https://router.vuejs.org/guide/#html\npackage main\n\nimport \"github.com/kataras/iris/v12\"\n\nfunc main() {\n\tapp := iris.New()\n\tapp.HandleDir(\"/\", \"./frontend\")\n\n\tapp.Listen(\":8080\")\n}\n\n/* For those who want to use HTML template as the index page\n   and serve static files in the root request path\n   and use vue router as the main router of the entire application,\n   please follow the below code example:\n\nfunc fullVueRouter() {\n\tapp := iris.New()\n\tapp.RegisterView(iris.HTML(\"./views\", \".html\"))\n\tapp.OnAnyErrorCode(index)\n\tapp.HandleDir(\"/\", \"./frontend\")\n\tapp.Get(\"/\", index)\n\n\tapp.Listen(\":8080\")\n}\n\nfunc index(ctx iris.Context) {\n\tif err := ctx.View(\"index.html\"); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n}\n*/\n"
  },
  {
    "path": "_examples/file-server/subdomain/assets/app2/app22/just_a_text_no_index.txt",
    "content": "just a text."
  },
  {
    "path": "_examples/file-server/subdomain/assets/app2/app2app3/index.html",
    "content": "<h1>Hello App2App3 index</h1>"
  },
  {
    "path": "_examples/file-server/subdomain/assets/app2/index.html",
    "content": "<h1>Hello App2 index</h1>"
  },
  {
    "path": "_examples/file-server/subdomain/assets/css/main.css",
    "content": "body {\n    background-color: black;\n}\n"
  },
  {
    "path": "_examples/file-server/subdomain/assets/index.html",
    "content": "<h1>Hello index</h1>"
  },
  {
    "path": "_examples/file-server/subdomain/assets/js/jquery-2.1.1.js",
    "content": "console.log(\"example\");"
  },
  {
    "path": "_examples/file-server/subdomain/hosts",
    "content": "127.0.0.1 examle.com\n127.0.0.1 v1.examle.com"
  },
  {
    "path": "_examples/file-server/subdomain/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n)\n\nconst (\n\taddr      = \"example.com:80\"\n\tsubdomain = \"v1\"\n)\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\tapp.Favicon(\"./assets/favicon.ico\")\n\n\tv1 := app.Subdomain(subdomain)\n\tv1.HandleDir(\"/\", iris.Dir(\"./assets\"))\n\n\t// http://v1.example.com\n\t// http://v1.example.com/css/main.css\n\t// http://v1.example.com/js/jquery-2.1.1.js\n\t// http://v1.example.com/favicon.ico\n\treturn app\n}\n\nfunc main() {\n\tapp := newApp()\n\tapp.Listen(addr)\n}\n"
  },
  {
    "path": "_examples/file-server/subdomain/main_test.go",
    "content": "package main\n\nimport (\n\t\"net\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\ntype resource string\n\nfunc (r resource) contentType() string {\n\tswitch filepath.Ext(r.String()) {\n\tcase \".js\":\n\t\treturn \"text/javascript\"\n\tcase \".css\":\n\t\treturn \"text/css\"\n\tcase \".ico\":\n\t\treturn \"image/x-icon\"\n\tcase \".html\", \"\":\n\t\treturn \"text/html\"\n\tdefault:\n\t\treturn \"text/plain\"\n\t}\n}\n\nfunc (r resource) String() string {\n\treturn string(r)\n}\n\nfunc (r resource) loadFromBase(dir string) string {\n\tfilename := r.String()\n\n\tif filepath.Ext(filename) == \"\" {\n\t\t// root /.\n\t\tfilename = filename + \"/index.html\"\n\t}\n\n\tfullpath := filepath.Join(dir, filename)\n\n\tb, err := os.ReadFile(fullpath)\n\tif err != nil {\n\t\tpanic(fullpath + \" failed with error: \" + err.Error())\n\t}\n\n\tresult := string(b)\n\n\treturn result\n}\n\nfunc TestFileServerSubdomainBasic(t *testing.T) {\n\turls := []resource{\n\t\t\"/css/main.css\",\n\t\t\"/js/jquery-2.1.1.js\",\n\t\t\"/favicon.ico\",\n\t\t\"/app2\",\n\t\t\"/app2/app2app3\",\n\t\t\"/\",\n\t}\n\n\tapp := newApp()\n\te := httptest.New(t, app)\n\n\thost, _, err := net.SplitHostPort(addr)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\thost = \"http://\" + subdomain + \".\" + host\n\n\tfor _, u := range urls {\n\t\turl := u.String()\n\t\tcontents := u.loadFromBase(\"./assets\")\n\n\t\te.GET(url).WithURL(host).Expect().\n\t\t\tStatus(httptest.StatusOK).\n\t\t\tContentType(u.contentType(), app.ConfigurationReadOnly().GetCharset()).\n\t\t\tBody().IsEqual(contents)\n\t}\n}\n"
  },
  {
    "path": "_examples/file-server/upload-file/main.go",
    "content": "package main\n\nimport (\n\t\"crypto/md5\"\n\t\"fmt\"\n\t\"io\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nconst maxSize = 5 << 20 // 5MB\n\nfunc main() {\n\tapp := iris.New()\n\n\tapp.RegisterView(iris.HTML(\"./templates\", \".html\"))\n\n\t// Serve the upload_form.html to the client.\n\tapp.Get(\"/upload\", func(ctx iris.Context) {\n\t\t// create a token (optionally).\n\n\t\tnow := time.Now().Unix()\n\t\th := md5.New()\n\t\tio.WriteString(h, strconv.FormatInt(now, 10))\n\t\ttoken := fmt.Sprintf(\"%x\", h.Sum(nil))\n\n\t\t// render the form with the token for any use you'd like.\n\t\t// ctx.ViewData(\"\", token)\n\t\t// or add second argument to the `View` method.\n\t\t// Token will be passed as {{.}} in the template.\n\t\tif err := ctx.View(\"upload_form.html\", token); err != nil {\n\t\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\t\treturn\n\t\t}\n\t})\n\n\t/* Read before continue.\n\n\t0. The default post max size is 32MB,\n\tyou can extend it to read more data using the `iris.WithPostMaxMemory(maxSize)` configurator at `app.Run`,\n\tnote that this will not be enough for your needs, read below.\n\n\t1. The faster way to check the size is using the `ctx.GetContentLength()` which returns the whole request's size\n\t(plus a logical number like 2MB or even 10MB for the rest of the size like headers). You can create a\n\tmiddleware to adapt this to any necessary handler.\n\n\tmyLimiter := func(ctx iris.Context) {\n\t\tif ctx.GetContentLength() > maxSize { // + 2 << 20 {\n\t\t\tctx.StatusCode(iris.StatusRequestEntityTooLarge)\n\t\t\treturn\n\t\t}\n\t\tctx.Next()\n\t}\n\n\tapp.Post(\"/upload\", myLimiter, myUploadHandler)\n\n\tMost clients will set the \"Content-Length\" header (like browsers) but it's always better to make sure that any client\n\tcan't send data that your server can't or doesn't want to handle. This can be happen using\n\tthe `app.Use(LimitRequestBodySize(maxSize))` (as app or route middleware)\n\tor the `ctx.SetMaxRequestBodySize(maxSize)` to limit the request based on a customized logic inside a particular handler, they're the same,\n\tread below.\n\n\t2. You can force-limit the request body size inside a handler using the `ctx.SetMaxRequestBodySize(maxSize)`,\n\tthis will force the connection to close if the incoming data are larger (most clients will receive it as \"connection reset\"),\n\tuse that to make sure that the client will not send data that your server can't or doesn't want to accept, as a fallback.\n\n\tapp.Post(\"/upload\", iris.LimitRequestBodySize(maxSize), myUploadHandler)\n\n\tOR\n\n\tapp.Post(\"/upload\", func(ctx iris.Context){\n\t\tctx.SetMaxRequestBodySize(maxSize)\n\n\t\t// [...]\n\t})\n\n\t3. Another way is to receive the data and check the second return value's `Size` value of the `ctx.FormFile`, i.e `info.Size`, this will give you\n\tthe exact file size, not the whole incoming request data length.\n\n\tapp.Post(\"/\", func(ctx iris.Context){\n\t\tfile, info, err := ctx.FormFile(\"uploadfile\")\n\t\tif err != nil {\n\t\t\tctx.StatusCode(iris.StatusInternalServerError)\n\t\t\tctx.HTML(\"Error while uploading: <b>\" + err.Error() + \"</b>\")\n\t\t\treturn\n\t\t}\n\n\t\tdefer file.Close()\n\n\t\tif info.Size > maxSize {\n\t\t\tctx.StatusCode(iris.StatusRequestEntityTooLarge)\n\t\t\treturn\n\t\t}\n\n\t\t// [...]\n\t})\n\t*/\n\n\t// Handle the post request from the upload_form.html to the server\n\tapp.Post(\"/upload\", iris.LimitRequestBodySize(maxSize+1<<20), func(ctx iris.Context) {\n\t\t// Get the file from the request.\n\t\tf, fh, err := ctx.FormFile(\"uploadfile\")\n\t\tif err != nil {\n\t\t\tctx.StatusCode(iris.StatusInternalServerError)\n\t\t\tctx.HTML(\"Error while uploading: <b>\" + err.Error() + \"</b>\")\n\t\t\treturn\n\t\t}\n\t\tdefer f.Close()\n\n\t\t_, err = ctx.SaveFormFile(fh, filepath.Join(\"./uploads\", fh.Filename))\n\t\tif err != nil {\n\t\t\tctx.StatusCode(iris.StatusInternalServerError)\n\t\t\tctx.HTML(\"Error while uploading: <b>\" + err.Error() + \"</b>\")\n\t\t\treturn\n\t\t}\n\t})\n\n\t// start the server at http://localhost:8080 with post limit at 5 MB.\n\tapp.Listen(\":8080\" /* 0.*/, iris.WithPostMaxMemory(maxSize))\n}\n"
  },
  {
    "path": "_examples/file-server/upload-file/templates/upload_form.html",
    "content": "<html>\n\n<head>\n\t<title>Upload file</title>\n</head>\n\n<body>\n\t<form enctype=\"multipart/form-data\" action=\"http://127.0.0.1:8080/upload\" method=\"POST\">\n\t\t<input type=\"file\" name=\"uploadfile\" />\n\t\t<input type=\"hidden\" name=\"token\" value=\"{{.}}\" />\n\t\t<input type=\"submit\" value=\"upload\" />\n\t</form>\n</body>\n\n</html>"
  },
  {
    "path": "_examples/file-server/upload-files/main.go",
    "content": "package main\n\nimport (\n\t\"crypto/md5\"\n\t\"fmt\"\n\t\"io\"\n\t\"mime/multipart\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := newApp()\n\t// start the server at http://localhost:8080 with post limit at 32 MB.\n\tapp.Listen(\":8080\", iris.WithPostMaxMemory(32<<20 /* same as 32 * iris.MB */))\n}\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\tapp.RegisterView(iris.HTML(\"./templates\", \".html\"))\n\n\t// Serve the upload_form.html to the client.\n\tapp.Get(\"/upload\", func(ctx iris.Context) {\n\t\t// create a token (optionally).\n\n\t\tnow := time.Now().Unix()\n\t\th := md5.New()\n\t\tio.WriteString(h, strconv.FormatInt(now, 10))\n\t\ttoken := fmt.Sprintf(\"%x\", h.Sum(nil))\n\n\t\t// render the form with the token for any use you'd like.\n\t\tif err := ctx.View(\"upload_form.html\", token); err != nil {\n\t\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\t\treturn\n\t\t}\n\t})\n\n\t// Handle the post request from the upload_form.html to the server.\n\tapp.Post(\"/upload\", func(ctx iris.Context) {\n\t\t//\n\t\t// UploadFormFiles\n\t\t// uploads any number of incoming files (\"multiple\" property on the form input).\n\t\t//\n\n\t\t// second argument is optional,\n\t\t// it can be used to change a file's name based on the request,\n\t\t// at this example we will showcase how to use it\n\t\t// by prefixing the uploaded file with the current user's ip.\n\t\t_, _, err := ctx.UploadFormFiles(\"./uploads\", beforeSave)\n\t\tif err != nil {\n\t\t\tctx.StopWithError(iris.StatusBadRequest, err)\n\t\t\treturn\n\t\t}\n\t})\n\n\tapp.Post(\"/upload_manual\", func(ctx iris.Context) {\n\t\t// Get the max post value size passed via iris.WithPostMaxMemory.\n\t\tmaxSize := ctx.Application().ConfigurationReadOnly().GetPostMaxMemory()\n\n\t\terr := ctx.Request().ParseMultipartForm(maxSize)\n\t\tif err != nil {\n\t\t\tctx.StopWithError(iris.StatusInternalServerError, err)\n\t\t\treturn\n\t\t}\n\n\t\tform := ctx.Request().MultipartForm\n\n\t\tfiles := form.File[\"files[]\"]\n\t\tfailures := 0\n\t\tfor _, file := range files {\n\t\t\t_, err = ctx.SaveFormFile(file, \"./uploads/\"+file.Filename)\n\t\t\tif err != nil {\n\t\t\t\tfailures++\n\t\t\t\tctx.Writef(\"failed to upload: %s\\n\", file.Filename)\n\t\t\t}\n\t\t}\n\t\tctx.Writef(\"%d files uploaded\", len(files)-failures)\n\t})\n\n\treturn app\n}\n\nfunc beforeSave(ctx iris.Context, file *multipart.FileHeader) bool {\n\tip := ctx.RemoteAddr()\n\t// make sure you format the ip in a way\n\t// that can be used for a file name (simple case):\n\tip = strings.ReplaceAll(ip, \".\", \"_\")\n\tip = strings.ReplaceAll(ip, \":\", \"_\")\n\n\t// you can use the time.Now, to prefix or suffix the files\n\t// based on the current time as well, as an exercise.\n\t// i.e unixTime :=\ttime.Now().Unix()\n\t// prefix the Filename with the $IP-\n\t// no need for more actions, internal uploader will use this\n\t// name to save the file into the \"./uploads\" folder.\n\tif ip == \"\" {\n\t\treturn true // don't change the file but continue saving it.\n\t}\n\n\t_ = ip\n\t// file.Filename = ip + \"-\" + file.Filename\n\treturn true\n}\n"
  },
  {
    "path": "_examples/file-server/upload-files/main_test.go",
    "content": "package main\n\nimport (\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nfunc TestUploadFiles(t *testing.T) {\n\tapp := newApp()\n\te := httptest.New(t, app)\n\n\t// upload the file itself.\n\tfh, err := os.Open(\"main.go\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer fh.Close()\n\n\te.POST(\"/upload\").WithMultipart().WithFile(\"files\", \"main.go\", fh).\n\t\tExpect().Status(httptest.StatusOK)\n\n\tf, err := os.Open(\"uploads/main.go\")\n\tif err != nil {\n\t\tt.Fatalf(\"expected file to get actually uploaded on the system directory but: %v\", err)\n\t}\n\tf.Close()\n\n\tos.Remove(f.Name())\n}\n"
  },
  {
    "path": "_examples/file-server/upload-files/templates/upload_form.html",
    "content": "<html>\n<head>\n<title>Upload file</title>\n</head>\n<body>\n\t<form enctype=\"multipart/form-data\"\n\t\taction=\"http://127.0.0.1:8080/upload\" method=\"POST\">\n\t\t<input type=\"file\" name=\"uploadfile\" multiple/> <input type=\"hidden\"\n\t\t\tname=\"token\" value=\"{{.}}\" /> <input type=\"submit\" value=\"upload\" />\n\t</form>\n</body>\n</html>\n"
  },
  {
    "path": "_examples/file-server/webdav/main.go",
    "content": "package main\n\nimport (\n\t\"net/http\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/middleware/accesslog\"\n\t\"github.com/kataras/iris/v12/middleware/recover\"\n\n\t\"golang.org/x/net/webdav\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\tapp.Logger().SetLevel(\"debug\")\n\tapp.Use(recover.New())\n\tapp.Use(accesslog.New(os.Stdout).Handler)\n\n\twebdavHandler := &webdav.Handler{\n\t\tFileSystem: webdav.Dir(\"./\"),\n\t\tLockSystem: webdav.NewMemLS(),\n\t\tLogger: func(r *http.Request, err error) {\n\t\t\tif err != nil {\n\t\t\t\tapp.Logger().Error(err)\n\t\t\t}\n\t\t},\n\t}\n\n\tapp.HandleMany(strings.Join(iris.WebDAVMethods, \" \"), \"/{p:path}\", iris.FromStd(webdavHandler))\n\n\tapp.Listen(\":8080\",\n\t\tiris.WithoutServerError(iris.ErrServerClosed, iris.ErrURLQuerySemicolon),\n\t\tiris.WithoutPathCorrection,\n\t)\n}\n\n/* Test with cURL or postman:\n\n* List files:\n\tcurl --location --request PROPFIND 'http://localhost:8080'\n* Get File:\n\tcurl --location --request GET 'http://localhost:8080/test.txt'\n* Upload File:\n\tcurl --location --request PUT 'http://localhost:8080/newfile.txt' \\\n\t--header 'Content-Type: text/plain' \\\n\t--data-raw 'This is a new file!'\n* Copy File:\n\tcurl --location --request COPY 'http://localhost:8080/test.txt' \\\n\t--header 'Destination: newdir/test.txt'\n* Create New Directory:\n\tcurl --location --request MKCOL 'http://localhost:8080/anewdir/'\n\nAnd e.t.c.\n*/\n"
  },
  {
    "path": "_examples/file-server/webdav/newdir/.gitkeep",
    "content": ""
  },
  {
    "path": "_examples/file-server/webdav/test.txt",
    "content": "Hello, world!\n"
  },
  {
    "path": "_examples/graphql/schema-first/README.md",
    "content": "# outerbanks-api\n\nA graphql api where we can store and get information on characters in Outerbanks.\n\n> This example is an updated version (**2023**) of outerbanks-api and it is based on: https://www.apollographql.com/blog/graphql/golang/using-graphql-with-golang.\n\n![](https://www.iris-go.com/images/graphql_playground.png)\n\n## Getting Started\n\n```sh\n$ go install github.com/99designs/gqlgen@latest\n```\n\nAdd `gqlgen` to your project's `tools.go` file\n\n```sh\n$ printf '// +build tools\\npackage tools\\nimport _ \"github.com/99designs/gqlgen\"' | gofmt > tools.go\n$ go get github.com/kataras/iris/v12@latest\n$ go mod tidy -compat=1.20\n```\n\nStart the graphql server\n\n```\n$ go run .\n```\n\n## Mutation\n\nOpen http://localhost:8080\n\nOn the editor panel paste:\n\n```graphql\nmutation upsertCharacter($input:CharacterInput!){\n  upsertCharacter(input:$input) {\n  \tname\n    id\n  }\n}\n```\n\nAnd in the variables panel below, paste:\n\n```json\n{\n  \"input\":{\n   \t\"name\": \"kataras\",\n    \"cliqueType\": \"POGUES\"\n  }\n}\n```\n\nHit Ctrl+Enter to apply the mutation.\n\n\n## Query\n\nQuery:\n\n```graphql\nquery character($id:ID!) {\n  character(id:$id) {\n    id\n    name\n  }\n}\n```\n\nVariables:\n\n```json\n{\n \"id\":1\n}\n```\n\n## Re-generate code\n\n```sh\n$ cd graph\n$ rm -f graph/schema.resolvers.go\n$ touch schema.graphql # make your updates here\n$ gqlgen generate\n```\n"
  },
  {
    "path": "_examples/graphql/schema-first/go.mod",
    "content": "module github.com/iris-contrib/outerbanks-api\n\ngo 1.25\n\nrequire (\n\tgithub.com/99designs/gqlgen v0.17.86\n\tgithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd\n\tgithub.com/vektah/gqlparser/v2 v2.5.31\n)\n\nrequire (\n\tgithub.com/BurntSushi/toml v1.6.0 // indirect\n\tgithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect\n\tgithub.com/CloudyKit/jet/v6 v6.3.1 // indirect\n\tgithub.com/Joker/jade v1.1.3 // indirect\n\tgithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 // indirect\n\tgithub.com/agnivade/levenshtein v1.2.1 // indirect\n\tgithub.com/andybalholm/brotli v1.2.0 // indirect\n\tgithub.com/aymerick/douceur v0.2.0 // indirect\n\tgithub.com/fatih/structs v1.1.0 // indirect\n\tgithub.com/flosch/pongo2/v4 v4.0.2 // indirect\n\tgithub.com/go-viper/mapstructure/v2 v2.4.0 // indirect\n\tgithub.com/goccy/go-yaml v1.19.2 // indirect\n\tgithub.com/golang/snappy v1.0.0 // indirect\n\tgithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a // indirect\n\tgithub.com/google/uuid v1.6.0 // indirect\n\tgithub.com/gorilla/css v1.0.1 // indirect\n\tgithub.com/gorilla/websocket v1.5.3 // indirect\n\tgithub.com/hashicorp/golang-lru/v2 v2.0.7 // indirect\n\tgithub.com/iris-contrib/schema v0.0.6 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/kataras/blocks v0.0.8 // indirect\n\tgithub.com/kataras/golog v0.1.12 // indirect\n\tgithub.com/kataras/pio v0.0.13 // indirect\n\tgithub.com/kataras/sitemap v0.0.6 // indirect\n\tgithub.com/kataras/tunnel v0.0.4 // indirect\n\tgithub.com/klauspost/compress v1.18.2 // indirect\n\tgithub.com/kr/pretty v0.3.1 // indirect\n\tgithub.com/mailgun/raymond/v2 v2.0.48 // indirect\n\tgithub.com/mailru/easyjson v0.9.1 // indirect\n\tgithub.com/microcosm-cc/bluemonday v1.0.27 // indirect\n\tgithub.com/rogpeppe/go-internal v1.10.0 // indirect\n\tgithub.com/russross/blackfriday/v2 v2.1.0 // indirect\n\tgithub.com/schollz/closestmatch v2.1.0+incompatible // indirect\n\tgithub.com/sirupsen/logrus v1.8.1 // indirect\n\tgithub.com/sosodev/duration v1.3.1 // indirect\n\tgithub.com/tdewolff/minify/v2 v2.24.8 // indirect\n\tgithub.com/tdewolff/parse/v2 v2.8.5 // indirect\n\tgithub.com/urfave/cli/v3 v3.6.1 // indirect\n\tgithub.com/valyala/bytebufferpool v1.0.0 // indirect\n\tgithub.com/vmihailenco/msgpack/v5 v5.4.1 // indirect\n\tgithub.com/vmihailenco/tagparser/v2 v2.0.0 // indirect\n\tgithub.com/yosssi/ace v0.0.5 // indirect\n\tgolang.org/x/crypto v0.47.0 // indirect\n\tgolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 // indirect\n\tgolang.org/x/mod v0.31.0 // indirect\n\tgolang.org/x/net v0.49.0 // indirect\n\tgolang.org/x/sync v0.19.0 // indirect\n\tgolang.org/x/sys v0.40.0 // indirect\n\tgolang.org/x/text v0.33.0 // indirect\n\tgolang.org/x/time v0.14.0 // indirect\n\tgolang.org/x/tools v0.40.0 // indirect\n\tgoogle.golang.org/protobuf v1.36.11 // indirect\n\tgopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect\n\tgopkg.in/ini.v1 v1.67.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n"
  },
  {
    "path": "_examples/graphql/schema-first/go.sum",
    "content": "github.com/99designs/gqlgen v0.17.86 h1:C8N3UTa5heXX6twl+b0AJyGkTwYL6dNmFrgZNLRcU6w=\ngithub.com/99designs/gqlgen v0.17.86/go.mod h1:KTrPl+vHA1IUzNlh4EYkl7+tcErL3MgKnhHrBcV74Fw=\ngithub.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=\ngithub.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=\ngithub.com/CloudyKit/jet/v6 v6.3.1 h1:6IAo5Cx21xrHVaR8zzXN5gJatKV/wO7Nf6bfCnCSbUw=\ngithub.com/CloudyKit/jet/v6 v6.3.1/go.mod h1:lf8ksdNsxZt7/yH/3n4vJQWA9RUq4wpaHtArHhGVMOw=\ngithub.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc=\ngithub.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=\ngithub.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=\ngithub.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=\ngithub.com/PuerkitoBio/goquery v1.11.0 h1:jZ7pwMQXIITcUXNH83LLk+txlaEy6NVOfTuP43xxfqw=\ngithub.com/PuerkitoBio/goquery v1.11.0/go.mod h1:wQHgxUOU3JGuj3oD/QFfxUdlzW6xPHfqyHre6VMY4DQ=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 h1:dG7/gLroJGht/jSQtHiLvT48Hxn+crbmvyItZC8cWOs=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05/go.mod h1:NYezi6wtnJtBm5btoprXc5SvAdqH0XTXWnUup0MptAI=\ngithub.com/agnivade/levenshtein v1.2.1 h1:EHBY3UOn1gwdy/VbFwgo4cxecRznFk7fKWN1KOX7eoM=\ngithub.com/agnivade/levenshtein v1.2.1/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU=\ngithub.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=\ngithub.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=\ngithub.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=\ngithub.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=\ngithub.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=\ngithub.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=\ngithub.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM=\ngithub.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA=\ngithub.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=\ngithub.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=\ngithub.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=\ngithub.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54 h1:SG7nF6SRlWhcT7cNTs5R6Hk4V2lcmLz2NsG2VnInyNo=\ngithub.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=\ngithub.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=\ngithub.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=\ngithub.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=\ngithub.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=\ngithub.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw=\ngithub.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8=\ngithub.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=\ngithub.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=\ngithub.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=\ngithub.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=\ngithub.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=\ngithub.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=\ngithub.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=\ngithub.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a h1:l7A0loSszR5zHd/qK53ZIHMO8b3bBSmENnQ6eKnUT0A=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=\ngithub.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=\ngithub.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=\ngithub.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=\ngithub.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=\ngithub.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=\ngithub.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=\ngithub.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2 h1:T9THsdP1woyAqKHwjkEsbCnMefsAFvk8iJJKokcJ3Go=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2/go.mod h1:JLDgIqnFy5loDSUv1OA2j0mb6p/rDhiCqigP22Uq9xE=\ngithub.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw=\ngithub.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/kataras/blocks v0.0.8 h1:MrpVhoFTCR2v1iOOfGng5VJSILKeZZI+7NGfxEh3SUM=\ngithub.com/kataras/blocks v0.0.8/go.mod h1:9Jm5zx6BB+06NwA+OhTbHW1xkMOYxahnqTN5DveZ2Yg=\ngithub.com/kataras/golog v0.1.12 h1:Bu7I/G4ilJlbfzjmU39O9N+2uO1pBcMK045fzZ4ytNg=\ngithub.com/kataras/golog v0.1.12/go.mod h1:wrGSbOiBqbQSQznleVNX4epWM8rl9SJ/rmEacl0yqy4=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd h1:oMsQgqKACbUdlaIWnN+EZzzAnHkw90hE/y/R0BSij2U=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd/go.mod h1:yucjtDl9Vn3OctWM1fi0MuM9OckemC7kEreFa9QtPF8=\ngithub.com/kataras/pio v0.0.13 h1:x0rXVX0fviDTXOOLOmr4MUxOabu1InVSTu5itF8CXCM=\ngithub.com/kataras/pio v0.0.13/go.mod h1:k3HNuSw+eJ8Pm2lA4lRhg3DiCjVgHlP8hmXApSej3oM=\ngithub.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY=\ngithub.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4=\ngithub.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA=\ngithub.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw=\ngithub.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=\ngithub.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=\ngithub.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=\ngithub.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8=\ngithub.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=\ngithub.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=\ngithub.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=\ngithub.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=\ngithub.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=\ngithub.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=\ngithub.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=\ngithub.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=\ngithub.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=\ngithub.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=\ngithub.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=\ngithub.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=\ngithub.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk=\ngithub.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=\ngithub.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=\ngithub.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=\ngithub.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=\ngithub.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/sosodev/duration v1.3.1 h1:qtHBDMQ6lvMQsL15g4aopM4HEfOaYuhWBw3NPTtlqq4=\ngithub.com/sosodev/duration v1.3.1/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/tdewolff/minify/v2 v2.24.8 h1:58/VjsbevI4d5FGV0ZSuBrHMSSkH4MCH0sIz/eKIauE=\ngithub.com/tdewolff/minify/v2 v2.24.8/go.mod h1:0Ukj0CRpo/sW/nd8uZ4ccXaV1rEVIWA3dj8U7+Shhfw=\ngithub.com/tdewolff/parse/v2 v2.8.5 h1:ZmBiA/8Do5Rpk7bDye0jbbDUpXXbCdc3iah4VeUvwYU=\ngithub.com/tdewolff/parse/v2 v2.8.5/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=\ngithub.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE=\ngithub.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=\ngithub.com/urfave/cli/v3 v3.6.1 h1:j8Qq8NyUawj/7rTYdBGrxcH7A/j7/G8Q5LhWEW4G3Mo=\ngithub.com/urfave/cli/v3 v3.6.1/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso=\ngithub.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=\ngithub.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=\ngithub.com/vektah/gqlparser/v2 v2.5.31 h1:YhWGA1mfTjID7qJhd1+Vxhpk5HTgydrGU9IgkWBTJ7k=\ngithub.com/vektah/gqlparser/v2 v2.5.31/go.mod h1:c1I28gSOVNzlfc4WuDlqU7voQnsqI6OG2amkBAFmgts=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=\ngithub.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=\ngithub.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=\ngithub.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=\ngithub.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=\ngithub.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA=\ngithub.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0=\ngithub.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=\ngithub.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=\ngithub.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=\ngolang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=\ngolang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=\ngolang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=\ngolang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=\ngolang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=\ngolang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=\ngolang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=\ngolang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=\ngolang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=\ngolang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=\ngolang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=\ngolang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA=\ngolang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=\ngoogle.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=\ngopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nmoul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs=\nmoul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE=\n"
  },
  {
    "path": "_examples/graphql/schema-first/gqlgen.yml",
    "content": "# Where are all the schema files located? globs are supported eg  src/**/*.graphqls\nschema:\n  - graph/*.graphqls\n\n# Where should the generated server code go?\nexec:\n  filename: graph/generated.go\n  package: graph\n\n# Uncomment to enable federation\n# federation:\n#   filename: graph/federation.go\n#   package: graph\n\n# Where should any generated models go?\nmodel:\n  filename: graph/model/models_gen.go\n  package: model\n\n# Where should the resolver implementations go?\nresolver:\n  layout: follow-schema\n  dir: graph\n  package: graph\n\n# Optional: turn on use ` + \"`\" + `gqlgen:\"fieldName\"` + \"`\" + ` tags in your models\n# struct_tag: json\n\n# Optional: turn on to use []Thing instead of []*Thing\n# omit_slice_element_pointers: false\n\n# Optional: turn off to make struct-type struct fields not use pointers\n# e.g. type Thing struct { FieldA OtherThing } instead of { FieldA *OtherThing }\n# struct_fields_always_pointers: true\n\n# Optional: turn off to make resolvers return values instead of pointers for structs\n# resolvers_always_return_pointers: true\n\n# Optional: set to speed up generation time by not performing a final validation pass.\n# skip_validation: true\n\n# gqlgen will search for any type names in the schema in these go packages\n# if they match it will use them, otherwise it will generate them.\nautobind:\n#  - \"github.com/iris-contrib/outerbanks-api/graph/model\"\n\n# This section declares type mapping between the GraphQL and go type systems\n#\n# The first line in each type will be used as defaults for resolver arguments and\n# modelgen, the others will be allowed when binding to fields. Configure them to\n# your liking\nmodels:\n  ID:\n    model:\n      - github.com/99designs/gqlgen/graphql.ID\n      - github.com/99designs/gqlgen/graphql.Int\n      - github.com/99designs/gqlgen/graphql.Int64\n      - github.com/99designs/gqlgen/graphql.Int32\n  Int:\n    model:\n      - github.com/99designs/gqlgen/graphql.Int\n      - github.com/99designs/gqlgen/graphql.Int64\n      - github.com/99designs/gqlgen/graphql.Int32\n"
  },
  {
    "path": "_examples/graphql/schema-first/graph/generated.go",
    "content": "// Code generated by github.com/99designs/gqlgen, DO NOT EDIT.\n\npackage graph\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"embed\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"sync\"\n\n\t\"github.com/99designs/gqlgen/graphql\"\n\t\"github.com/99designs/gqlgen/graphql/introspection\"\n\t\"github.com/iris-contrib/outerbanks-api/graph/model\"\n\tgqlparser \"github.com/vektah/gqlparser/v2\"\n\t\"github.com/vektah/gqlparser/v2/ast\"\n)\n\n// region    ************************** generated!.gotpl **************************\n\n// NewExecutableSchema creates an ExecutableSchema from the ResolverRoot interface.\nfunc NewExecutableSchema(cfg Config) graphql.ExecutableSchema {\n\treturn &executableSchema{\n\t\tresolvers:  cfg.Resolvers,\n\t\tdirectives: cfg.Directives,\n\t\tcomplexity: cfg.Complexity,\n\t}\n}\n\ntype Config struct {\n\tResolvers  ResolverRoot\n\tDirectives DirectiveRoot\n\tComplexity ComplexityRoot\n}\n\ntype ResolverRoot interface {\n\tMutation() MutationResolver\n\tQuery() QueryResolver\n}\n\ntype DirectiveRoot struct {\n}\n\ntype ComplexityRoot struct {\n\tCharacter struct {\n\t\tCliqueType func(childComplexity int) int\n\t\tID         func(childComplexity int) int\n\t\tIsHero     func(childComplexity int) int\n\t\tName       func(childComplexity int) int\n\t}\n\n\tMutation struct {\n\t\tUpsertCharacter func(childComplexity int, input model.CharacterInput) int\n\t}\n\n\tQuery struct {\n\t\tCharacter  func(childComplexity int, id string) int\n\t\tCharacters func(childComplexity int, cliqueType model.CliqueType) int\n\t}\n}\n\ntype MutationResolver interface {\n\tUpsertCharacter(ctx context.Context, input model.CharacterInput) (*model.Character, error)\n}\ntype QueryResolver interface {\n\tCharacter(ctx context.Context, id string) (*model.Character, error)\n\tCharacters(ctx context.Context, cliqueType model.CliqueType) ([]*model.Character, error)\n}\n\ntype executableSchema struct {\n\tresolvers  ResolverRoot\n\tdirectives DirectiveRoot\n\tcomplexity ComplexityRoot\n}\n\nfunc (e *executableSchema) Schema() *ast.Schema {\n\treturn parsedSchema\n}\n\nfunc (e *executableSchema) Complexity(typeName, field string, childComplexity int, rawArgs map[string]any) (int, bool) {\n\tec := executionContext{nil, e}\n\t_ = ec\n\tswitch typeName + \".\" + field {\n\n\tcase \"Character.cliqueType\":\n\t\tif e.complexity.Character.CliqueType == nil {\n\t\t\tbreak\n\t\t}\n\n\t\treturn e.complexity.Character.CliqueType(childComplexity), true\n\n\tcase \"Character.id\":\n\t\tif e.complexity.Character.ID == nil {\n\t\t\tbreak\n\t\t}\n\n\t\treturn e.complexity.Character.ID(childComplexity), true\n\n\tcase \"Character.isHero\":\n\t\tif e.complexity.Character.IsHero == nil {\n\t\t\tbreak\n\t\t}\n\n\t\treturn e.complexity.Character.IsHero(childComplexity), true\n\n\tcase \"Character.name\":\n\t\tif e.complexity.Character.Name == nil {\n\t\t\tbreak\n\t\t}\n\n\t\treturn e.complexity.Character.Name(childComplexity), true\n\n\tcase \"Mutation.upsertCharacter\":\n\t\tif e.complexity.Mutation.UpsertCharacter == nil {\n\t\t\tbreak\n\t\t}\n\n\t\targs, err := ec.field_Mutation_upsertCharacter_args(context.TODO(), rawArgs)\n\t\tif err != nil {\n\t\t\treturn 0, false\n\t\t}\n\n\t\treturn e.complexity.Mutation.UpsertCharacter(childComplexity, args[\"input\"].(model.CharacterInput)), true\n\n\tcase \"Query.character\":\n\t\tif e.complexity.Query.Character == nil {\n\t\t\tbreak\n\t\t}\n\n\t\targs, err := ec.field_Query_character_args(context.TODO(), rawArgs)\n\t\tif err != nil {\n\t\t\treturn 0, false\n\t\t}\n\n\t\treturn e.complexity.Query.Character(childComplexity, args[\"id\"].(string)), true\n\n\tcase \"Query.characters\":\n\t\tif e.complexity.Query.Characters == nil {\n\t\t\tbreak\n\t\t}\n\n\t\targs, err := ec.field_Query_characters_args(context.TODO(), rawArgs)\n\t\tif err != nil {\n\t\t\treturn 0, false\n\t\t}\n\n\t\treturn e.complexity.Query.Characters(childComplexity, args[\"cliqueType\"].(model.CliqueType)), true\n\n\t}\n\treturn 0, false\n}\n\nfunc (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler {\n\trc := graphql.GetOperationContext(ctx)\n\tec := executionContext{rc, e}\n\tinputUnmarshalMap := graphql.BuildUnmarshalerMap(\n\t\tec.unmarshalInputCharacterInput,\n\t)\n\tfirst := true\n\n\tswitch rc.Operation.Operation {\n\tcase ast.Query:\n\t\treturn func(ctx context.Context) *graphql.Response {\n\t\t\tif !first {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tfirst = false\n\t\t\tctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap)\n\t\t\tdata := ec._Query(ctx, rc.Operation.SelectionSet)\n\t\t\tvar buf bytes.Buffer\n\t\t\tdata.MarshalGQL(&buf)\n\n\t\t\treturn &graphql.Response{\n\t\t\t\tData: buf.Bytes(),\n\t\t\t}\n\t\t}\n\tcase ast.Mutation:\n\t\treturn func(ctx context.Context) *graphql.Response {\n\t\t\tif !first {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tfirst = false\n\t\t\tctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap)\n\t\t\tdata := ec._Mutation(ctx, rc.Operation.SelectionSet)\n\t\t\tvar buf bytes.Buffer\n\t\t\tdata.MarshalGQL(&buf)\n\n\t\t\treturn &graphql.Response{\n\t\t\t\tData: buf.Bytes(),\n\t\t\t}\n\t\t}\n\n\tdefault:\n\t\treturn graphql.OneShot(graphql.ErrorResponse(ctx, \"unsupported GraphQL operation\"))\n\t}\n}\n\ntype executionContext struct {\n\t*graphql.OperationContext\n\t*executableSchema\n}\n\nfunc (ec *executionContext) introspectSchema() (*introspection.Schema, error) {\n\tif ec.DisableIntrospection {\n\t\treturn nil, errors.New(\"introspection disabled\")\n\t}\n\treturn introspection.WrapSchema(parsedSchema), nil\n}\n\nfunc (ec *executionContext) introspectType(name string) (*introspection.Type, error) {\n\tif ec.DisableIntrospection {\n\t\treturn nil, errors.New(\"introspection disabled\")\n\t}\n\treturn introspection.WrapTypeFromDef(parsedSchema, parsedSchema.Types[name]), nil\n}\n\n//go:embed \"schema.graphqls\"\nvar sourcesFS embed.FS\n\nfunc sourceData(filename string) string {\n\tdata, err := sourcesFS.ReadFile(filename)\n\tif err != nil {\n\t\tpanic(fmt.Sprintf(\"codegen problem: %s not available\", filename))\n\t}\n\treturn string(data)\n}\n\nvar sources = []*ast.Source{\n\t{Name: \"schema.graphqls\", Input: sourceData(\"schema.graphqls\"), BuiltIn: false},\n}\nvar parsedSchema = gqlparser.MustLoadSchema(sources...)\n\n// endregion ************************** generated!.gotpl **************************\n\n// region    ***************************** args.gotpl *****************************\n\nfunc (ec *executionContext) field_Mutation_upsertCharacter_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {\n\tvar err error\n\targs := map[string]any{}\n\tvar arg0 model.CharacterInput\n\tif tmp, ok := rawArgs[\"input\"]; ok {\n\t\tctx := graphql.WithPathContext(ctx, graphql.NewPathWithField(\"input\"))\n\t\targ0, err = ec.unmarshalNCharacterInput2githubᚗcomᚋirisᚑcontribᚋouterbanksᚑapiᚋgraphᚋgraphᚋmodelᚐCharacterInput(ctx, tmp)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\targs[\"input\"] = arg0\n\treturn args, nil\n}\n\nfunc (ec *executionContext) field_Query___type_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {\n\tvar err error\n\targs := map[string]any{}\n\tvar arg0 string\n\tif tmp, ok := rawArgs[\"name\"]; ok {\n\t\tctx := graphql.WithPathContext(ctx, graphql.NewPathWithField(\"name\"))\n\t\targ0, err = ec.unmarshalNString2string(ctx, tmp)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\targs[\"name\"] = arg0\n\treturn args, nil\n}\n\nfunc (ec *executionContext) field_Query_character_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {\n\tvar err error\n\targs := map[string]any{}\n\tvar arg0 string\n\tif tmp, ok := rawArgs[\"id\"]; ok {\n\t\tctx := graphql.WithPathContext(ctx, graphql.NewPathWithField(\"id\"))\n\t\targ0, err = ec.unmarshalNID2string(ctx, tmp)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\targs[\"id\"] = arg0\n\treturn args, nil\n}\n\nfunc (ec *executionContext) field_Query_characters_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {\n\tvar err error\n\targs := map[string]any{}\n\tvar arg0 model.CliqueType\n\tif tmp, ok := rawArgs[\"cliqueType\"]; ok {\n\t\tctx := graphql.WithPathContext(ctx, graphql.NewPathWithField(\"cliqueType\"))\n\t\targ0, err = ec.unmarshalNCliqueType2githubᚗcomᚋirisᚑcontribᚋouterbanksᚑapiᚋgraphᚋgraphᚋmodelᚐCliqueType(ctx, tmp)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\targs[\"cliqueType\"] = arg0\n\treturn args, nil\n}\n\nfunc (ec *executionContext) field___Type_enumValues_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {\n\tvar err error\n\targs := map[string]any{}\n\tvar arg0 bool\n\tif tmp, ok := rawArgs[\"includeDeprecated\"]; ok {\n\t\tctx := graphql.WithPathContext(ctx, graphql.NewPathWithField(\"includeDeprecated\"))\n\t\targ0, err = ec.unmarshalOBoolean2bool(ctx, tmp)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\targs[\"includeDeprecated\"] = arg0\n\treturn args, nil\n}\n\nfunc (ec *executionContext) field___Type_fields_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {\n\tvar err error\n\targs := map[string]any{}\n\tvar arg0 bool\n\tif tmp, ok := rawArgs[\"includeDeprecated\"]; ok {\n\t\tctx := graphql.WithPathContext(ctx, graphql.NewPathWithField(\"includeDeprecated\"))\n\t\targ0, err = ec.unmarshalOBoolean2bool(ctx, tmp)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\targs[\"includeDeprecated\"] = arg0\n\treturn args, nil\n}\n\n// endregion ***************************** args.gotpl *****************************\n\n// region    ************************** directives.gotpl **************************\n\n// endregion ************************** directives.gotpl **************************\n\n// region    **************************** field.gotpl *****************************\n\nfunc (ec *executionContext) _Character_id(ctx context.Context, field graphql.CollectedField, obj *model.Character) (ret graphql.Marshaler) {\n\tfc, err := ec.fieldContext_Character_id(ctx, field)\n\tif err != nil {\n\t\treturn graphql.Null\n\t}\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\tret = graphql.Null\n\t\t}\n\t}()\n\tresTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {\n\t\tctx = rctx // use context from middleware stack in children\n\t\treturn obj.ID, nil\n\t})\n\tif err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn graphql.Null\n\t}\n\tif resTmp == nil {\n\t\tif !graphql.HasFieldError(ctx, fc) {\n\t\t\tec.Errorf(ctx, \"must not be null\")\n\t\t}\n\t\treturn graphql.Null\n\t}\n\tres := resTmp.(string)\n\tfc.Result = res\n\treturn ec.marshalNID2string(ctx, field.Selections, res)\n}\n\nfunc (ec *executionContext) fieldContext_Character_id(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {\n\tfc = &graphql.FieldContext{\n\t\tObject:     \"Character\",\n\t\tField:      field,\n\t\tIsMethod:   false,\n\t\tIsResolver: false,\n\t\tChild: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {\n\t\t\treturn nil, errors.New(\"field of type ID does not have child fields\")\n\t\t},\n\t}\n\treturn fc, nil\n}\n\nfunc (ec *executionContext) _Character_name(ctx context.Context, field graphql.CollectedField, obj *model.Character) (ret graphql.Marshaler) {\n\tfc, err := ec.fieldContext_Character_name(ctx, field)\n\tif err != nil {\n\t\treturn graphql.Null\n\t}\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\tret = graphql.Null\n\t\t}\n\t}()\n\tresTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {\n\t\tctx = rctx // use context from middleware stack in children\n\t\treturn obj.Name, nil\n\t})\n\tif err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn graphql.Null\n\t}\n\tif resTmp == nil {\n\t\tif !graphql.HasFieldError(ctx, fc) {\n\t\t\tec.Errorf(ctx, \"must not be null\")\n\t\t}\n\t\treturn graphql.Null\n\t}\n\tres := resTmp.(string)\n\tfc.Result = res\n\treturn ec.marshalNString2string(ctx, field.Selections, res)\n}\n\nfunc (ec *executionContext) fieldContext_Character_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {\n\tfc = &graphql.FieldContext{\n\t\tObject:     \"Character\",\n\t\tField:      field,\n\t\tIsMethod:   false,\n\t\tIsResolver: false,\n\t\tChild: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {\n\t\t\treturn nil, errors.New(\"field of type String does not have child fields\")\n\t\t},\n\t}\n\treturn fc, nil\n}\n\nfunc (ec *executionContext) _Character_isHero(ctx context.Context, field graphql.CollectedField, obj *model.Character) (ret graphql.Marshaler) {\n\tfc, err := ec.fieldContext_Character_isHero(ctx, field)\n\tif err != nil {\n\t\treturn graphql.Null\n\t}\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\tret = graphql.Null\n\t\t}\n\t}()\n\tresTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {\n\t\tctx = rctx // use context from middleware stack in children\n\t\treturn obj.IsHero, nil\n\t})\n\tif err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn graphql.Null\n\t}\n\tif resTmp == nil {\n\t\tif !graphql.HasFieldError(ctx, fc) {\n\t\t\tec.Errorf(ctx, \"must not be null\")\n\t\t}\n\t\treturn graphql.Null\n\t}\n\tres := resTmp.(bool)\n\tfc.Result = res\n\treturn ec.marshalNBoolean2bool(ctx, field.Selections, res)\n}\n\nfunc (ec *executionContext) fieldContext_Character_isHero(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {\n\tfc = &graphql.FieldContext{\n\t\tObject:     \"Character\",\n\t\tField:      field,\n\t\tIsMethod:   false,\n\t\tIsResolver: false,\n\t\tChild: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {\n\t\t\treturn nil, errors.New(\"field of type Boolean does not have child fields\")\n\t\t},\n\t}\n\treturn fc, nil\n}\n\nfunc (ec *executionContext) _Character_cliqueType(ctx context.Context, field graphql.CollectedField, obj *model.Character) (ret graphql.Marshaler) {\n\tfc, err := ec.fieldContext_Character_cliqueType(ctx, field)\n\tif err != nil {\n\t\treturn graphql.Null\n\t}\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\tret = graphql.Null\n\t\t}\n\t}()\n\tresTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {\n\t\tctx = rctx // use context from middleware stack in children\n\t\treturn obj.CliqueType, nil\n\t})\n\tif err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn graphql.Null\n\t}\n\tif resTmp == nil {\n\t\tif !graphql.HasFieldError(ctx, fc) {\n\t\t\tec.Errorf(ctx, \"must not be null\")\n\t\t}\n\t\treturn graphql.Null\n\t}\n\tres := resTmp.(model.CliqueType)\n\tfc.Result = res\n\treturn ec.marshalNCliqueType2githubᚗcomᚋirisᚑcontribᚋouterbanksᚑapiᚋgraphᚋgraphᚋmodelᚐCliqueType(ctx, field.Selections, res)\n}\n\nfunc (ec *executionContext) fieldContext_Character_cliqueType(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {\n\tfc = &graphql.FieldContext{\n\t\tObject:     \"Character\",\n\t\tField:      field,\n\t\tIsMethod:   false,\n\t\tIsResolver: false,\n\t\tChild: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {\n\t\t\treturn nil, errors.New(\"field of type CliqueType does not have child fields\")\n\t\t},\n\t}\n\treturn fc, nil\n}\n\nfunc (ec *executionContext) _Mutation_upsertCharacter(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {\n\tfc, err := ec.fieldContext_Mutation_upsertCharacter(ctx, field)\n\tif err != nil {\n\t\treturn graphql.Null\n\t}\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\tret = graphql.Null\n\t\t}\n\t}()\n\tresTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {\n\t\tctx = rctx // use context from middleware stack in children\n\t\treturn ec.resolvers.Mutation().UpsertCharacter(rctx, fc.Args[\"input\"].(model.CharacterInput))\n\t})\n\tif err != nil {\n\t\tec.Error(ctx, err)\n\t}\n\tif resTmp == nil {\n\t\tif !graphql.HasFieldError(ctx, fc) {\n\t\t\tec.Errorf(ctx, \"must not be null\")\n\t\t}\n\t\treturn graphql.Null\n\t}\n\tres := resTmp.(*model.Character)\n\tfc.Result = res\n\treturn ec.marshalNCharacter2ᚖgithubᚗcomᚋirisᚑcontribᚋouterbanksᚑapiᚋgraphᚋgraphᚋmodelᚐCharacter(ctx, field.Selections, res)\n}\n\nfunc (ec *executionContext) fieldContext_Mutation_upsertCharacter(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {\n\tfc = &graphql.FieldContext{\n\t\tObject:     \"Mutation\",\n\t\tField:      field,\n\t\tIsMethod:   true,\n\t\tIsResolver: true,\n\t\tChild: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {\n\t\t\tswitch field.Name {\n\t\t\tcase \"id\":\n\t\t\t\treturn ec.fieldContext_Character_id(ctx, field)\n\t\t\tcase \"name\":\n\t\t\t\treturn ec.fieldContext_Character_name(ctx, field)\n\t\t\tcase \"isHero\":\n\t\t\t\treturn ec.fieldContext_Character_isHero(ctx, field)\n\t\t\tcase \"cliqueType\":\n\t\t\t\treturn ec.fieldContext_Character_cliqueType(ctx, field)\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"no field named %q was found under type Character\", field.Name)\n\t\t},\n\t}\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\terr = ec.Recover(ctx, r)\n\t\t\tec.Error(ctx, err)\n\t\t}\n\t}()\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tif fc.Args, err = ec.field_Mutation_upsertCharacter_args(ctx, field.ArgumentMap(ec.Variables)); err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn\n\t}\n\treturn fc, nil\n}\n\nfunc (ec *executionContext) _Query_character(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {\n\tfc, err := ec.fieldContext_Query_character(ctx, field)\n\tif err != nil {\n\t\treturn graphql.Null\n\t}\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\tret = graphql.Null\n\t\t}\n\t}()\n\tresTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {\n\t\tctx = rctx // use context from middleware stack in children\n\t\treturn ec.resolvers.Query().Character(rctx, fc.Args[\"id\"].(string))\n\t})\n\tif err != nil {\n\t\tec.Error(ctx, err)\n\t}\n\tif resTmp == nil {\n\t\treturn graphql.Null\n\t}\n\tres := resTmp.(*model.Character)\n\tfc.Result = res\n\treturn ec.marshalOCharacter2ᚖgithubᚗcomᚋirisᚑcontribᚋouterbanksᚑapiᚋgraphᚋgraphᚋmodelᚐCharacter(ctx, field.Selections, res)\n}\n\nfunc (ec *executionContext) fieldContext_Query_character(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {\n\tfc = &graphql.FieldContext{\n\t\tObject:     \"Query\",\n\t\tField:      field,\n\t\tIsMethod:   true,\n\t\tIsResolver: true,\n\t\tChild: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {\n\t\t\tswitch field.Name {\n\t\t\tcase \"id\":\n\t\t\t\treturn ec.fieldContext_Character_id(ctx, field)\n\t\t\tcase \"name\":\n\t\t\t\treturn ec.fieldContext_Character_name(ctx, field)\n\t\t\tcase \"isHero\":\n\t\t\t\treturn ec.fieldContext_Character_isHero(ctx, field)\n\t\t\tcase \"cliqueType\":\n\t\t\t\treturn ec.fieldContext_Character_cliqueType(ctx, field)\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"no field named %q was found under type Character\", field.Name)\n\t\t},\n\t}\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\terr = ec.Recover(ctx, r)\n\t\t\tec.Error(ctx, err)\n\t\t}\n\t}()\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tif fc.Args, err = ec.field_Query_character_args(ctx, field.ArgumentMap(ec.Variables)); err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn\n\t}\n\treturn fc, nil\n}\n\nfunc (ec *executionContext) _Query_characters(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {\n\tfc, err := ec.fieldContext_Query_characters(ctx, field)\n\tif err != nil {\n\t\treturn graphql.Null\n\t}\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\tret = graphql.Null\n\t\t}\n\t}()\n\tresTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {\n\t\tctx = rctx // use context from middleware stack in children\n\t\treturn ec.resolvers.Query().Characters(rctx, fc.Args[\"cliqueType\"].(model.CliqueType))\n\t})\n\tif err != nil {\n\t\tec.Error(ctx, err)\n\t}\n\tif resTmp == nil {\n\t\treturn graphql.Null\n\t}\n\tres := resTmp.([]*model.Character)\n\tfc.Result = res\n\treturn ec.marshalOCharacter2ᚕᚖgithubᚗcomᚋirisᚑcontribᚋouterbanksᚑapiᚋgraphᚋgraphᚋmodelᚐCharacterᚄ(ctx, field.Selections, res)\n}\n\nfunc (ec *executionContext) fieldContext_Query_characters(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {\n\tfc = &graphql.FieldContext{\n\t\tObject:     \"Query\",\n\t\tField:      field,\n\t\tIsMethod:   true,\n\t\tIsResolver: true,\n\t\tChild: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {\n\t\t\tswitch field.Name {\n\t\t\tcase \"id\":\n\t\t\t\treturn ec.fieldContext_Character_id(ctx, field)\n\t\t\tcase \"name\":\n\t\t\t\treturn ec.fieldContext_Character_name(ctx, field)\n\t\t\tcase \"isHero\":\n\t\t\t\treturn ec.fieldContext_Character_isHero(ctx, field)\n\t\t\tcase \"cliqueType\":\n\t\t\t\treturn ec.fieldContext_Character_cliqueType(ctx, field)\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"no field named %q was found under type Character\", field.Name)\n\t\t},\n\t}\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\terr = ec.Recover(ctx, r)\n\t\t\tec.Error(ctx, err)\n\t\t}\n\t}()\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tif fc.Args, err = ec.field_Query_characters_args(ctx, field.ArgumentMap(ec.Variables)); err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn\n\t}\n\treturn fc, nil\n}\n\nfunc (ec *executionContext) _Query___type(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {\n\tfc, err := ec.fieldContext_Query___type(ctx, field)\n\tif err != nil {\n\t\treturn graphql.Null\n\t}\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\tret = graphql.Null\n\t\t}\n\t}()\n\tresTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {\n\t\tctx = rctx // use context from middleware stack in children\n\t\treturn ec.introspectType(fc.Args[\"name\"].(string))\n\t})\n\tif err != nil {\n\t\tec.Error(ctx, err)\n\t}\n\tif resTmp == nil {\n\t\treturn graphql.Null\n\t}\n\tres := resTmp.(*introspection.Type)\n\tfc.Result = res\n\treturn ec.marshalO__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res)\n}\n\nfunc (ec *executionContext) fieldContext_Query___type(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {\n\tfc = &graphql.FieldContext{\n\t\tObject:     \"Query\",\n\t\tField:      field,\n\t\tIsMethod:   true,\n\t\tIsResolver: false,\n\t\tChild: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {\n\t\t\tswitch field.Name {\n\t\t\tcase \"kind\":\n\t\t\t\treturn ec.fieldContext___Type_kind(ctx, field)\n\t\t\tcase \"name\":\n\t\t\t\treturn ec.fieldContext___Type_name(ctx, field)\n\t\t\tcase \"description\":\n\t\t\t\treturn ec.fieldContext___Type_description(ctx, field)\n\t\t\tcase \"fields\":\n\t\t\t\treturn ec.fieldContext___Type_fields(ctx, field)\n\t\t\tcase \"interfaces\":\n\t\t\t\treturn ec.fieldContext___Type_interfaces(ctx, field)\n\t\t\tcase \"possibleTypes\":\n\t\t\t\treturn ec.fieldContext___Type_possibleTypes(ctx, field)\n\t\t\tcase \"enumValues\":\n\t\t\t\treturn ec.fieldContext___Type_enumValues(ctx, field)\n\t\t\tcase \"inputFields\":\n\t\t\t\treturn ec.fieldContext___Type_inputFields(ctx, field)\n\t\t\tcase \"ofType\":\n\t\t\t\treturn ec.fieldContext___Type_ofType(ctx, field)\n\t\t\tcase \"specifiedByURL\":\n\t\t\t\treturn ec.fieldContext___Type_specifiedByURL(ctx, field)\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"no field named %q was found under type __Type\", field.Name)\n\t\t},\n\t}\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\terr = ec.Recover(ctx, r)\n\t\t\tec.Error(ctx, err)\n\t\t}\n\t}()\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tif fc.Args, err = ec.field_Query___type_args(ctx, field.ArgumentMap(ec.Variables)); err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn\n\t}\n\treturn fc, nil\n}\n\nfunc (ec *executionContext) _Query___schema(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {\n\tfc, err := ec.fieldContext_Query___schema(ctx, field)\n\tif err != nil {\n\t\treturn graphql.Null\n\t}\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\tret = graphql.Null\n\t\t}\n\t}()\n\tresTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {\n\t\tctx = rctx // use context from middleware stack in children\n\t\treturn ec.introspectSchema()\n\t})\n\tif err != nil {\n\t\tec.Error(ctx, err)\n\t}\n\tif resTmp == nil {\n\t\treturn graphql.Null\n\t}\n\tres := resTmp.(*introspection.Schema)\n\tfc.Result = res\n\treturn ec.marshalO__Schema2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐSchema(ctx, field.Selections, res)\n}\n\nfunc (ec *executionContext) fieldContext_Query___schema(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {\n\tfc = &graphql.FieldContext{\n\t\tObject:     \"Query\",\n\t\tField:      field,\n\t\tIsMethod:   true,\n\t\tIsResolver: false,\n\t\tChild: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {\n\t\t\tswitch field.Name {\n\t\t\tcase \"description\":\n\t\t\t\treturn ec.fieldContext___Schema_description(ctx, field)\n\t\t\tcase \"types\":\n\t\t\t\treturn ec.fieldContext___Schema_types(ctx, field)\n\t\t\tcase \"queryType\":\n\t\t\t\treturn ec.fieldContext___Schema_queryType(ctx, field)\n\t\t\tcase \"mutationType\":\n\t\t\t\treturn ec.fieldContext___Schema_mutationType(ctx, field)\n\t\t\tcase \"subscriptionType\":\n\t\t\t\treturn ec.fieldContext___Schema_subscriptionType(ctx, field)\n\t\t\tcase \"directives\":\n\t\t\t\treturn ec.fieldContext___Schema_directives(ctx, field)\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"no field named %q was found under type __Schema\", field.Name)\n\t\t},\n\t}\n\treturn fc, nil\n}\n\nfunc (ec *executionContext) ___Directive_name(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) (ret graphql.Marshaler) {\n\tfc, err := ec.fieldContext___Directive_name(ctx, field)\n\tif err != nil {\n\t\treturn graphql.Null\n\t}\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\tret = graphql.Null\n\t\t}\n\t}()\n\tresTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {\n\t\tctx = rctx // use context from middleware stack in children\n\t\treturn obj.Name, nil\n\t})\n\tif err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn graphql.Null\n\t}\n\tif resTmp == nil {\n\t\tif !graphql.HasFieldError(ctx, fc) {\n\t\t\tec.Errorf(ctx, \"must not be null\")\n\t\t}\n\t\treturn graphql.Null\n\t}\n\tres := resTmp.(string)\n\tfc.Result = res\n\treturn ec.marshalNString2string(ctx, field.Selections, res)\n}\n\nfunc (ec *executionContext) fieldContext___Directive_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {\n\tfc = &graphql.FieldContext{\n\t\tObject:     \"__Directive\",\n\t\tField:      field,\n\t\tIsMethod:   false,\n\t\tIsResolver: false,\n\t\tChild: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {\n\t\t\treturn nil, errors.New(\"field of type String does not have child fields\")\n\t\t},\n\t}\n\treturn fc, nil\n}\n\nfunc (ec *executionContext) ___Directive_description(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) (ret graphql.Marshaler) {\n\tfc, err := ec.fieldContext___Directive_description(ctx, field)\n\tif err != nil {\n\t\treturn graphql.Null\n\t}\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\tret = graphql.Null\n\t\t}\n\t}()\n\tresTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {\n\t\tctx = rctx // use context from middleware stack in children\n\t\treturn obj.Description(), nil\n\t})\n\tif err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn graphql.Null\n\t}\n\tif resTmp == nil {\n\t\treturn graphql.Null\n\t}\n\tres := resTmp.(*string)\n\tfc.Result = res\n\treturn ec.marshalOString2ᚖstring(ctx, field.Selections, res)\n}\n\nfunc (ec *executionContext) fieldContext___Directive_description(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {\n\tfc = &graphql.FieldContext{\n\t\tObject:     \"__Directive\",\n\t\tField:      field,\n\t\tIsMethod:   true,\n\t\tIsResolver: false,\n\t\tChild: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {\n\t\t\treturn nil, errors.New(\"field of type String does not have child fields\")\n\t\t},\n\t}\n\treturn fc, nil\n}\n\nfunc (ec *executionContext) ___Directive_locations(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) (ret graphql.Marshaler) {\n\tfc, err := ec.fieldContext___Directive_locations(ctx, field)\n\tif err != nil {\n\t\treturn graphql.Null\n\t}\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\tret = graphql.Null\n\t\t}\n\t}()\n\tresTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {\n\t\tctx = rctx // use context from middleware stack in children\n\t\treturn obj.Locations, nil\n\t})\n\tif err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn graphql.Null\n\t}\n\tif resTmp == nil {\n\t\tif !graphql.HasFieldError(ctx, fc) {\n\t\t\tec.Errorf(ctx, \"must not be null\")\n\t\t}\n\t\treturn graphql.Null\n\t}\n\tres := resTmp.([]string)\n\tfc.Result = res\n\treturn ec.marshalN__DirectiveLocation2ᚕstringᚄ(ctx, field.Selections, res)\n}\n\nfunc (ec *executionContext) fieldContext___Directive_locations(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {\n\tfc = &graphql.FieldContext{\n\t\tObject:     \"__Directive\",\n\t\tField:      field,\n\t\tIsMethod:   false,\n\t\tIsResolver: false,\n\t\tChild: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {\n\t\t\treturn nil, errors.New(\"field of type __DirectiveLocation does not have child fields\")\n\t\t},\n\t}\n\treturn fc, nil\n}\n\nfunc (ec *executionContext) ___Directive_args(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) (ret graphql.Marshaler) {\n\tfc, err := ec.fieldContext___Directive_args(ctx, field)\n\tif err != nil {\n\t\treturn graphql.Null\n\t}\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\tret = graphql.Null\n\t\t}\n\t}()\n\tresTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {\n\t\tctx = rctx // use context from middleware stack in children\n\t\treturn obj.Args, nil\n\t})\n\tif err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn graphql.Null\n\t}\n\tif resTmp == nil {\n\t\tif !graphql.HasFieldError(ctx, fc) {\n\t\t\tec.Errorf(ctx, \"must not be null\")\n\t\t}\n\t\treturn graphql.Null\n\t}\n\tres := resTmp.([]introspection.InputValue)\n\tfc.Result = res\n\treturn ec.marshalN__InputValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValueᚄ(ctx, field.Selections, res)\n}\n\nfunc (ec *executionContext) fieldContext___Directive_args(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {\n\tfc = &graphql.FieldContext{\n\t\tObject:     \"__Directive\",\n\t\tField:      field,\n\t\tIsMethod:   false,\n\t\tIsResolver: false,\n\t\tChild: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {\n\t\t\tswitch field.Name {\n\t\t\tcase \"name\":\n\t\t\t\treturn ec.fieldContext___InputValue_name(ctx, field)\n\t\t\tcase \"description\":\n\t\t\t\treturn ec.fieldContext___InputValue_description(ctx, field)\n\t\t\tcase \"type\":\n\t\t\t\treturn ec.fieldContext___InputValue_type(ctx, field)\n\t\t\tcase \"defaultValue\":\n\t\t\t\treturn ec.fieldContext___InputValue_defaultValue(ctx, field)\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"no field named %q was found under type __InputValue\", field.Name)\n\t\t},\n\t}\n\treturn fc, nil\n}\n\nfunc (ec *executionContext) ___Directive_isRepeatable(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) (ret graphql.Marshaler) {\n\tfc, err := ec.fieldContext___Directive_isRepeatable(ctx, field)\n\tif err != nil {\n\t\treturn graphql.Null\n\t}\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\tret = graphql.Null\n\t\t}\n\t}()\n\tresTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {\n\t\tctx = rctx // use context from middleware stack in children\n\t\treturn obj.IsRepeatable, nil\n\t})\n\tif err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn graphql.Null\n\t}\n\tif resTmp == nil {\n\t\tif !graphql.HasFieldError(ctx, fc) {\n\t\t\tec.Errorf(ctx, \"must not be null\")\n\t\t}\n\t\treturn graphql.Null\n\t}\n\tres := resTmp.(bool)\n\tfc.Result = res\n\treturn ec.marshalNBoolean2bool(ctx, field.Selections, res)\n}\n\nfunc (ec *executionContext) fieldContext___Directive_isRepeatable(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {\n\tfc = &graphql.FieldContext{\n\t\tObject:     \"__Directive\",\n\t\tField:      field,\n\t\tIsMethod:   false,\n\t\tIsResolver: false,\n\t\tChild: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {\n\t\t\treturn nil, errors.New(\"field of type Boolean does not have child fields\")\n\t\t},\n\t}\n\treturn fc, nil\n}\n\nfunc (ec *executionContext) ___EnumValue_name(ctx context.Context, field graphql.CollectedField, obj *introspection.EnumValue) (ret graphql.Marshaler) {\n\tfc, err := ec.fieldContext___EnumValue_name(ctx, field)\n\tif err != nil {\n\t\treturn graphql.Null\n\t}\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\tret = graphql.Null\n\t\t}\n\t}()\n\tresTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {\n\t\tctx = rctx // use context from middleware stack in children\n\t\treturn obj.Name, nil\n\t})\n\tif err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn graphql.Null\n\t}\n\tif resTmp == nil {\n\t\tif !graphql.HasFieldError(ctx, fc) {\n\t\t\tec.Errorf(ctx, \"must not be null\")\n\t\t}\n\t\treturn graphql.Null\n\t}\n\tres := resTmp.(string)\n\tfc.Result = res\n\treturn ec.marshalNString2string(ctx, field.Selections, res)\n}\n\nfunc (ec *executionContext) fieldContext___EnumValue_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {\n\tfc = &graphql.FieldContext{\n\t\tObject:     \"__EnumValue\",\n\t\tField:      field,\n\t\tIsMethod:   false,\n\t\tIsResolver: false,\n\t\tChild: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {\n\t\t\treturn nil, errors.New(\"field of type String does not have child fields\")\n\t\t},\n\t}\n\treturn fc, nil\n}\n\nfunc (ec *executionContext) ___EnumValue_description(ctx context.Context, field graphql.CollectedField, obj *introspection.EnumValue) (ret graphql.Marshaler) {\n\tfc, err := ec.fieldContext___EnumValue_description(ctx, field)\n\tif err != nil {\n\t\treturn graphql.Null\n\t}\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\tret = graphql.Null\n\t\t}\n\t}()\n\tresTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {\n\t\tctx = rctx // use context from middleware stack in children\n\t\treturn obj.Description(), nil\n\t})\n\tif err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn graphql.Null\n\t}\n\tif resTmp == nil {\n\t\treturn graphql.Null\n\t}\n\tres := resTmp.(*string)\n\tfc.Result = res\n\treturn ec.marshalOString2ᚖstring(ctx, field.Selections, res)\n}\n\nfunc (ec *executionContext) fieldContext___EnumValue_description(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {\n\tfc = &graphql.FieldContext{\n\t\tObject:     \"__EnumValue\",\n\t\tField:      field,\n\t\tIsMethod:   true,\n\t\tIsResolver: false,\n\t\tChild: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {\n\t\t\treturn nil, errors.New(\"field of type String does not have child fields\")\n\t\t},\n\t}\n\treturn fc, nil\n}\n\nfunc (ec *executionContext) ___EnumValue_isDeprecated(ctx context.Context, field graphql.CollectedField, obj *introspection.EnumValue) (ret graphql.Marshaler) {\n\tfc, err := ec.fieldContext___EnumValue_isDeprecated(ctx, field)\n\tif err != nil {\n\t\treturn graphql.Null\n\t}\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\tret = graphql.Null\n\t\t}\n\t}()\n\tresTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {\n\t\tctx = rctx // use context from middleware stack in children\n\t\treturn obj.IsDeprecated(), nil\n\t})\n\tif err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn graphql.Null\n\t}\n\tif resTmp == nil {\n\t\tif !graphql.HasFieldError(ctx, fc) {\n\t\t\tec.Errorf(ctx, \"must not be null\")\n\t\t}\n\t\treturn graphql.Null\n\t}\n\tres := resTmp.(bool)\n\tfc.Result = res\n\treturn ec.marshalNBoolean2bool(ctx, field.Selections, res)\n}\n\nfunc (ec *executionContext) fieldContext___EnumValue_isDeprecated(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {\n\tfc = &graphql.FieldContext{\n\t\tObject:     \"__EnumValue\",\n\t\tField:      field,\n\t\tIsMethod:   true,\n\t\tIsResolver: false,\n\t\tChild: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {\n\t\t\treturn nil, errors.New(\"field of type Boolean does not have child fields\")\n\t\t},\n\t}\n\treturn fc, nil\n}\n\nfunc (ec *executionContext) ___EnumValue_deprecationReason(ctx context.Context, field graphql.CollectedField, obj *introspection.EnumValue) (ret graphql.Marshaler) {\n\tfc, err := ec.fieldContext___EnumValue_deprecationReason(ctx, field)\n\tif err != nil {\n\t\treturn graphql.Null\n\t}\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\tret = graphql.Null\n\t\t}\n\t}()\n\tresTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {\n\t\tctx = rctx // use context from middleware stack in children\n\t\treturn obj.DeprecationReason(), nil\n\t})\n\tif err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn graphql.Null\n\t}\n\tif resTmp == nil {\n\t\treturn graphql.Null\n\t}\n\tres := resTmp.(*string)\n\tfc.Result = res\n\treturn ec.marshalOString2ᚖstring(ctx, field.Selections, res)\n}\n\nfunc (ec *executionContext) fieldContext___EnumValue_deprecationReason(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {\n\tfc = &graphql.FieldContext{\n\t\tObject:     \"__EnumValue\",\n\t\tField:      field,\n\t\tIsMethod:   true,\n\t\tIsResolver: false,\n\t\tChild: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {\n\t\t\treturn nil, errors.New(\"field of type String does not have child fields\")\n\t\t},\n\t}\n\treturn fc, nil\n}\n\nfunc (ec *executionContext) ___Field_name(ctx context.Context, field graphql.CollectedField, obj *introspection.Field) (ret graphql.Marshaler) {\n\tfc, err := ec.fieldContext___Field_name(ctx, field)\n\tif err != nil {\n\t\treturn graphql.Null\n\t}\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\tret = graphql.Null\n\t\t}\n\t}()\n\tresTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {\n\t\tctx = rctx // use context from middleware stack in children\n\t\treturn obj.Name, nil\n\t})\n\tif err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn graphql.Null\n\t}\n\tif resTmp == nil {\n\t\tif !graphql.HasFieldError(ctx, fc) {\n\t\t\tec.Errorf(ctx, \"must not be null\")\n\t\t}\n\t\treturn graphql.Null\n\t}\n\tres := resTmp.(string)\n\tfc.Result = res\n\treturn ec.marshalNString2string(ctx, field.Selections, res)\n}\n\nfunc (ec *executionContext) fieldContext___Field_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {\n\tfc = &graphql.FieldContext{\n\t\tObject:     \"__Field\",\n\t\tField:      field,\n\t\tIsMethod:   false,\n\t\tIsResolver: false,\n\t\tChild: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {\n\t\t\treturn nil, errors.New(\"field of type String does not have child fields\")\n\t\t},\n\t}\n\treturn fc, nil\n}\n\nfunc (ec *executionContext) ___Field_description(ctx context.Context, field graphql.CollectedField, obj *introspection.Field) (ret graphql.Marshaler) {\n\tfc, err := ec.fieldContext___Field_description(ctx, field)\n\tif err != nil {\n\t\treturn graphql.Null\n\t}\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\tret = graphql.Null\n\t\t}\n\t}()\n\tresTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {\n\t\tctx = rctx // use context from middleware stack in children\n\t\treturn obj.Description(), nil\n\t})\n\tif err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn graphql.Null\n\t}\n\tif resTmp == nil {\n\t\treturn graphql.Null\n\t}\n\tres := resTmp.(*string)\n\tfc.Result = res\n\treturn ec.marshalOString2ᚖstring(ctx, field.Selections, res)\n}\n\nfunc (ec *executionContext) fieldContext___Field_description(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {\n\tfc = &graphql.FieldContext{\n\t\tObject:     \"__Field\",\n\t\tField:      field,\n\t\tIsMethod:   true,\n\t\tIsResolver: false,\n\t\tChild: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {\n\t\t\treturn nil, errors.New(\"field of type String does not have child fields\")\n\t\t},\n\t}\n\treturn fc, nil\n}\n\nfunc (ec *executionContext) ___Field_args(ctx context.Context, field graphql.CollectedField, obj *introspection.Field) (ret graphql.Marshaler) {\n\tfc, err := ec.fieldContext___Field_args(ctx, field)\n\tif err != nil {\n\t\treturn graphql.Null\n\t}\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\tret = graphql.Null\n\t\t}\n\t}()\n\tresTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {\n\t\tctx = rctx // use context from middleware stack in children\n\t\treturn obj.Args, nil\n\t})\n\tif err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn graphql.Null\n\t}\n\tif resTmp == nil {\n\t\tif !graphql.HasFieldError(ctx, fc) {\n\t\t\tec.Errorf(ctx, \"must not be null\")\n\t\t}\n\t\treturn graphql.Null\n\t}\n\tres := resTmp.([]introspection.InputValue)\n\tfc.Result = res\n\treturn ec.marshalN__InputValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValueᚄ(ctx, field.Selections, res)\n}\n\nfunc (ec *executionContext) fieldContext___Field_args(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {\n\tfc = &graphql.FieldContext{\n\t\tObject:     \"__Field\",\n\t\tField:      field,\n\t\tIsMethod:   false,\n\t\tIsResolver: false,\n\t\tChild: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {\n\t\t\tswitch field.Name {\n\t\t\tcase \"name\":\n\t\t\t\treturn ec.fieldContext___InputValue_name(ctx, field)\n\t\t\tcase \"description\":\n\t\t\t\treturn ec.fieldContext___InputValue_description(ctx, field)\n\t\t\tcase \"type\":\n\t\t\t\treturn ec.fieldContext___InputValue_type(ctx, field)\n\t\t\tcase \"defaultValue\":\n\t\t\t\treturn ec.fieldContext___InputValue_defaultValue(ctx, field)\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"no field named %q was found under type __InputValue\", field.Name)\n\t\t},\n\t}\n\treturn fc, nil\n}\n\nfunc (ec *executionContext) ___Field_type(ctx context.Context, field graphql.CollectedField, obj *introspection.Field) (ret graphql.Marshaler) {\n\tfc, err := ec.fieldContext___Field_type(ctx, field)\n\tif err != nil {\n\t\treturn graphql.Null\n\t}\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\tret = graphql.Null\n\t\t}\n\t}()\n\tresTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {\n\t\tctx = rctx // use context from middleware stack in children\n\t\treturn obj.Type, nil\n\t})\n\tif err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn graphql.Null\n\t}\n\tif resTmp == nil {\n\t\tif !graphql.HasFieldError(ctx, fc) {\n\t\t\tec.Errorf(ctx, \"must not be null\")\n\t\t}\n\t\treturn graphql.Null\n\t}\n\tres := resTmp.(*introspection.Type)\n\tfc.Result = res\n\treturn ec.marshalN__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res)\n}\n\nfunc (ec *executionContext) fieldContext___Field_type(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {\n\tfc = &graphql.FieldContext{\n\t\tObject:     \"__Field\",\n\t\tField:      field,\n\t\tIsMethod:   false,\n\t\tIsResolver: false,\n\t\tChild: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {\n\t\t\tswitch field.Name {\n\t\t\tcase \"kind\":\n\t\t\t\treturn ec.fieldContext___Type_kind(ctx, field)\n\t\t\tcase \"name\":\n\t\t\t\treturn ec.fieldContext___Type_name(ctx, field)\n\t\t\tcase \"description\":\n\t\t\t\treturn ec.fieldContext___Type_description(ctx, field)\n\t\t\tcase \"fields\":\n\t\t\t\treturn ec.fieldContext___Type_fields(ctx, field)\n\t\t\tcase \"interfaces\":\n\t\t\t\treturn ec.fieldContext___Type_interfaces(ctx, field)\n\t\t\tcase \"possibleTypes\":\n\t\t\t\treturn ec.fieldContext___Type_possibleTypes(ctx, field)\n\t\t\tcase \"enumValues\":\n\t\t\t\treturn ec.fieldContext___Type_enumValues(ctx, field)\n\t\t\tcase \"inputFields\":\n\t\t\t\treturn ec.fieldContext___Type_inputFields(ctx, field)\n\t\t\tcase \"ofType\":\n\t\t\t\treturn ec.fieldContext___Type_ofType(ctx, field)\n\t\t\tcase \"specifiedByURL\":\n\t\t\t\treturn ec.fieldContext___Type_specifiedByURL(ctx, field)\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"no field named %q was found under type __Type\", field.Name)\n\t\t},\n\t}\n\treturn fc, nil\n}\n\nfunc (ec *executionContext) ___Field_isDeprecated(ctx context.Context, field graphql.CollectedField, obj *introspection.Field) (ret graphql.Marshaler) {\n\tfc, err := ec.fieldContext___Field_isDeprecated(ctx, field)\n\tif err != nil {\n\t\treturn graphql.Null\n\t}\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\tret = graphql.Null\n\t\t}\n\t}()\n\tresTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {\n\t\tctx = rctx // use context from middleware stack in children\n\t\treturn obj.IsDeprecated(), nil\n\t})\n\tif err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn graphql.Null\n\t}\n\tif resTmp == nil {\n\t\tif !graphql.HasFieldError(ctx, fc) {\n\t\t\tec.Errorf(ctx, \"must not be null\")\n\t\t}\n\t\treturn graphql.Null\n\t}\n\tres := resTmp.(bool)\n\tfc.Result = res\n\treturn ec.marshalNBoolean2bool(ctx, field.Selections, res)\n}\n\nfunc (ec *executionContext) fieldContext___Field_isDeprecated(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {\n\tfc = &graphql.FieldContext{\n\t\tObject:     \"__Field\",\n\t\tField:      field,\n\t\tIsMethod:   true,\n\t\tIsResolver: false,\n\t\tChild: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {\n\t\t\treturn nil, errors.New(\"field of type Boolean does not have child fields\")\n\t\t},\n\t}\n\treturn fc, nil\n}\n\nfunc (ec *executionContext) ___Field_deprecationReason(ctx context.Context, field graphql.CollectedField, obj *introspection.Field) (ret graphql.Marshaler) {\n\tfc, err := ec.fieldContext___Field_deprecationReason(ctx, field)\n\tif err != nil {\n\t\treturn graphql.Null\n\t}\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\tret = graphql.Null\n\t\t}\n\t}()\n\tresTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {\n\t\tctx = rctx // use context from middleware stack in children\n\t\treturn obj.DeprecationReason(), nil\n\t})\n\tif err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn graphql.Null\n\t}\n\tif resTmp == nil {\n\t\treturn graphql.Null\n\t}\n\tres := resTmp.(*string)\n\tfc.Result = res\n\treturn ec.marshalOString2ᚖstring(ctx, field.Selections, res)\n}\n\nfunc (ec *executionContext) fieldContext___Field_deprecationReason(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {\n\tfc = &graphql.FieldContext{\n\t\tObject:     \"__Field\",\n\t\tField:      field,\n\t\tIsMethod:   true,\n\t\tIsResolver: false,\n\t\tChild: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {\n\t\t\treturn nil, errors.New(\"field of type String does not have child fields\")\n\t\t},\n\t}\n\treturn fc, nil\n}\n\nfunc (ec *executionContext) ___InputValue_name(ctx context.Context, field graphql.CollectedField, obj *introspection.InputValue) (ret graphql.Marshaler) {\n\tfc, err := ec.fieldContext___InputValue_name(ctx, field)\n\tif err != nil {\n\t\treturn graphql.Null\n\t}\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\tret = graphql.Null\n\t\t}\n\t}()\n\tresTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {\n\t\tctx = rctx // use context from middleware stack in children\n\t\treturn obj.Name, nil\n\t})\n\tif err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn graphql.Null\n\t}\n\tif resTmp == nil {\n\t\tif !graphql.HasFieldError(ctx, fc) {\n\t\t\tec.Errorf(ctx, \"must not be null\")\n\t\t}\n\t\treturn graphql.Null\n\t}\n\tres := resTmp.(string)\n\tfc.Result = res\n\treturn ec.marshalNString2string(ctx, field.Selections, res)\n}\n\nfunc (ec *executionContext) fieldContext___InputValue_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {\n\tfc = &graphql.FieldContext{\n\t\tObject:     \"__InputValue\",\n\t\tField:      field,\n\t\tIsMethod:   false,\n\t\tIsResolver: false,\n\t\tChild: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {\n\t\t\treturn nil, errors.New(\"field of type String does not have child fields\")\n\t\t},\n\t}\n\treturn fc, nil\n}\n\nfunc (ec *executionContext) ___InputValue_description(ctx context.Context, field graphql.CollectedField, obj *introspection.InputValue) (ret graphql.Marshaler) {\n\tfc, err := ec.fieldContext___InputValue_description(ctx, field)\n\tif err != nil {\n\t\treturn graphql.Null\n\t}\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\tret = graphql.Null\n\t\t}\n\t}()\n\tresTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {\n\t\tctx = rctx // use context from middleware stack in children\n\t\treturn obj.Description(), nil\n\t})\n\tif err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn graphql.Null\n\t}\n\tif resTmp == nil {\n\t\treturn graphql.Null\n\t}\n\tres := resTmp.(*string)\n\tfc.Result = res\n\treturn ec.marshalOString2ᚖstring(ctx, field.Selections, res)\n}\n\nfunc (ec *executionContext) fieldContext___InputValue_description(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {\n\tfc = &graphql.FieldContext{\n\t\tObject:     \"__InputValue\",\n\t\tField:      field,\n\t\tIsMethod:   true,\n\t\tIsResolver: false,\n\t\tChild: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {\n\t\t\treturn nil, errors.New(\"field of type String does not have child fields\")\n\t\t},\n\t}\n\treturn fc, nil\n}\n\nfunc (ec *executionContext) ___InputValue_type(ctx context.Context, field graphql.CollectedField, obj *introspection.InputValue) (ret graphql.Marshaler) {\n\tfc, err := ec.fieldContext___InputValue_type(ctx, field)\n\tif err != nil {\n\t\treturn graphql.Null\n\t}\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\tret = graphql.Null\n\t\t}\n\t}()\n\tresTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {\n\t\tctx = rctx // use context from middleware stack in children\n\t\treturn obj.Type, nil\n\t})\n\tif err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn graphql.Null\n\t}\n\tif resTmp == nil {\n\t\tif !graphql.HasFieldError(ctx, fc) {\n\t\t\tec.Errorf(ctx, \"must not be null\")\n\t\t}\n\t\treturn graphql.Null\n\t}\n\tres := resTmp.(*introspection.Type)\n\tfc.Result = res\n\treturn ec.marshalN__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res)\n}\n\nfunc (ec *executionContext) fieldContext___InputValue_type(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {\n\tfc = &graphql.FieldContext{\n\t\tObject:     \"__InputValue\",\n\t\tField:      field,\n\t\tIsMethod:   false,\n\t\tIsResolver: false,\n\t\tChild: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {\n\t\t\tswitch field.Name {\n\t\t\tcase \"kind\":\n\t\t\t\treturn ec.fieldContext___Type_kind(ctx, field)\n\t\t\tcase \"name\":\n\t\t\t\treturn ec.fieldContext___Type_name(ctx, field)\n\t\t\tcase \"description\":\n\t\t\t\treturn ec.fieldContext___Type_description(ctx, field)\n\t\t\tcase \"fields\":\n\t\t\t\treturn ec.fieldContext___Type_fields(ctx, field)\n\t\t\tcase \"interfaces\":\n\t\t\t\treturn ec.fieldContext___Type_interfaces(ctx, field)\n\t\t\tcase \"possibleTypes\":\n\t\t\t\treturn ec.fieldContext___Type_possibleTypes(ctx, field)\n\t\t\tcase \"enumValues\":\n\t\t\t\treturn ec.fieldContext___Type_enumValues(ctx, field)\n\t\t\tcase \"inputFields\":\n\t\t\t\treturn ec.fieldContext___Type_inputFields(ctx, field)\n\t\t\tcase \"ofType\":\n\t\t\t\treturn ec.fieldContext___Type_ofType(ctx, field)\n\t\t\tcase \"specifiedByURL\":\n\t\t\t\treturn ec.fieldContext___Type_specifiedByURL(ctx, field)\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"no field named %q was found under type __Type\", field.Name)\n\t\t},\n\t}\n\treturn fc, nil\n}\n\nfunc (ec *executionContext) ___InputValue_defaultValue(ctx context.Context, field graphql.CollectedField, obj *introspection.InputValue) (ret graphql.Marshaler) {\n\tfc, err := ec.fieldContext___InputValue_defaultValue(ctx, field)\n\tif err != nil {\n\t\treturn graphql.Null\n\t}\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\tret = graphql.Null\n\t\t}\n\t}()\n\tresTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {\n\t\tctx = rctx // use context from middleware stack in children\n\t\treturn obj.DefaultValue, nil\n\t})\n\tif err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn graphql.Null\n\t}\n\tif resTmp == nil {\n\t\treturn graphql.Null\n\t}\n\tres := resTmp.(*string)\n\tfc.Result = res\n\treturn ec.marshalOString2ᚖstring(ctx, field.Selections, res)\n}\n\nfunc (ec *executionContext) fieldContext___InputValue_defaultValue(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {\n\tfc = &graphql.FieldContext{\n\t\tObject:     \"__InputValue\",\n\t\tField:      field,\n\t\tIsMethod:   false,\n\t\tIsResolver: false,\n\t\tChild: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {\n\t\t\treturn nil, errors.New(\"field of type String does not have child fields\")\n\t\t},\n\t}\n\treturn fc, nil\n}\n\nfunc (ec *executionContext) ___Schema_description(ctx context.Context, field graphql.CollectedField, obj *introspection.Schema) (ret graphql.Marshaler) {\n\tfc, err := ec.fieldContext___Schema_description(ctx, field)\n\tif err != nil {\n\t\treturn graphql.Null\n\t}\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\tret = graphql.Null\n\t\t}\n\t}()\n\tresTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {\n\t\tctx = rctx // use context from middleware stack in children\n\t\treturn obj.Description(), nil\n\t})\n\tif err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn graphql.Null\n\t}\n\tif resTmp == nil {\n\t\treturn graphql.Null\n\t}\n\tres := resTmp.(*string)\n\tfc.Result = res\n\treturn ec.marshalOString2ᚖstring(ctx, field.Selections, res)\n}\n\nfunc (ec *executionContext) fieldContext___Schema_description(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {\n\tfc = &graphql.FieldContext{\n\t\tObject:     \"__Schema\",\n\t\tField:      field,\n\t\tIsMethod:   true,\n\t\tIsResolver: false,\n\t\tChild: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {\n\t\t\treturn nil, errors.New(\"field of type String does not have child fields\")\n\t\t},\n\t}\n\treturn fc, nil\n}\n\nfunc (ec *executionContext) ___Schema_types(ctx context.Context, field graphql.CollectedField, obj *introspection.Schema) (ret graphql.Marshaler) {\n\tfc, err := ec.fieldContext___Schema_types(ctx, field)\n\tif err != nil {\n\t\treturn graphql.Null\n\t}\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\tret = graphql.Null\n\t\t}\n\t}()\n\tresTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {\n\t\tctx = rctx // use context from middleware stack in children\n\t\treturn obj.Types(), nil\n\t})\n\tif err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn graphql.Null\n\t}\n\tif resTmp == nil {\n\t\tif !graphql.HasFieldError(ctx, fc) {\n\t\t\tec.Errorf(ctx, \"must not be null\")\n\t\t}\n\t\treturn graphql.Null\n\t}\n\tres := resTmp.([]introspection.Type)\n\tfc.Result = res\n\treturn ec.marshalN__Type2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐTypeᚄ(ctx, field.Selections, res)\n}\n\nfunc (ec *executionContext) fieldContext___Schema_types(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {\n\tfc = &graphql.FieldContext{\n\t\tObject:     \"__Schema\",\n\t\tField:      field,\n\t\tIsMethod:   true,\n\t\tIsResolver: false,\n\t\tChild: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {\n\t\t\tswitch field.Name {\n\t\t\tcase \"kind\":\n\t\t\t\treturn ec.fieldContext___Type_kind(ctx, field)\n\t\t\tcase \"name\":\n\t\t\t\treturn ec.fieldContext___Type_name(ctx, field)\n\t\t\tcase \"description\":\n\t\t\t\treturn ec.fieldContext___Type_description(ctx, field)\n\t\t\tcase \"fields\":\n\t\t\t\treturn ec.fieldContext___Type_fields(ctx, field)\n\t\t\tcase \"interfaces\":\n\t\t\t\treturn ec.fieldContext___Type_interfaces(ctx, field)\n\t\t\tcase \"possibleTypes\":\n\t\t\t\treturn ec.fieldContext___Type_possibleTypes(ctx, field)\n\t\t\tcase \"enumValues\":\n\t\t\t\treturn ec.fieldContext___Type_enumValues(ctx, field)\n\t\t\tcase \"inputFields\":\n\t\t\t\treturn ec.fieldContext___Type_inputFields(ctx, field)\n\t\t\tcase \"ofType\":\n\t\t\t\treturn ec.fieldContext___Type_ofType(ctx, field)\n\t\t\tcase \"specifiedByURL\":\n\t\t\t\treturn ec.fieldContext___Type_specifiedByURL(ctx, field)\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"no field named %q was found under type __Type\", field.Name)\n\t\t},\n\t}\n\treturn fc, nil\n}\n\nfunc (ec *executionContext) ___Schema_queryType(ctx context.Context, field graphql.CollectedField, obj *introspection.Schema) (ret graphql.Marshaler) {\n\tfc, err := ec.fieldContext___Schema_queryType(ctx, field)\n\tif err != nil {\n\t\treturn graphql.Null\n\t}\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\tret = graphql.Null\n\t\t}\n\t}()\n\tresTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {\n\t\tctx = rctx // use context from middleware stack in children\n\t\treturn obj.QueryType(), nil\n\t})\n\tif err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn graphql.Null\n\t}\n\tif resTmp == nil {\n\t\tif !graphql.HasFieldError(ctx, fc) {\n\t\t\tec.Errorf(ctx, \"must not be null\")\n\t\t}\n\t\treturn graphql.Null\n\t}\n\tres := resTmp.(*introspection.Type)\n\tfc.Result = res\n\treturn ec.marshalN__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res)\n}\n\nfunc (ec *executionContext) fieldContext___Schema_queryType(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {\n\tfc = &graphql.FieldContext{\n\t\tObject:     \"__Schema\",\n\t\tField:      field,\n\t\tIsMethod:   true,\n\t\tIsResolver: false,\n\t\tChild: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {\n\t\t\tswitch field.Name {\n\t\t\tcase \"kind\":\n\t\t\t\treturn ec.fieldContext___Type_kind(ctx, field)\n\t\t\tcase \"name\":\n\t\t\t\treturn ec.fieldContext___Type_name(ctx, field)\n\t\t\tcase \"description\":\n\t\t\t\treturn ec.fieldContext___Type_description(ctx, field)\n\t\t\tcase \"fields\":\n\t\t\t\treturn ec.fieldContext___Type_fields(ctx, field)\n\t\t\tcase \"interfaces\":\n\t\t\t\treturn ec.fieldContext___Type_interfaces(ctx, field)\n\t\t\tcase \"possibleTypes\":\n\t\t\t\treturn ec.fieldContext___Type_possibleTypes(ctx, field)\n\t\t\tcase \"enumValues\":\n\t\t\t\treturn ec.fieldContext___Type_enumValues(ctx, field)\n\t\t\tcase \"inputFields\":\n\t\t\t\treturn ec.fieldContext___Type_inputFields(ctx, field)\n\t\t\tcase \"ofType\":\n\t\t\t\treturn ec.fieldContext___Type_ofType(ctx, field)\n\t\t\tcase \"specifiedByURL\":\n\t\t\t\treturn ec.fieldContext___Type_specifiedByURL(ctx, field)\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"no field named %q was found under type __Type\", field.Name)\n\t\t},\n\t}\n\treturn fc, nil\n}\n\nfunc (ec *executionContext) ___Schema_mutationType(ctx context.Context, field graphql.CollectedField, obj *introspection.Schema) (ret graphql.Marshaler) {\n\tfc, err := ec.fieldContext___Schema_mutationType(ctx, field)\n\tif err != nil {\n\t\treturn graphql.Null\n\t}\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\tret = graphql.Null\n\t\t}\n\t}()\n\tresTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {\n\t\tctx = rctx // use context from middleware stack in children\n\t\treturn obj.MutationType(), nil\n\t})\n\tif err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn graphql.Null\n\t}\n\tif resTmp == nil {\n\t\treturn graphql.Null\n\t}\n\tres := resTmp.(*introspection.Type)\n\tfc.Result = res\n\treturn ec.marshalO__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res)\n}\n\nfunc (ec *executionContext) fieldContext___Schema_mutationType(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {\n\tfc = &graphql.FieldContext{\n\t\tObject:     \"__Schema\",\n\t\tField:      field,\n\t\tIsMethod:   true,\n\t\tIsResolver: false,\n\t\tChild: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {\n\t\t\tswitch field.Name {\n\t\t\tcase \"kind\":\n\t\t\t\treturn ec.fieldContext___Type_kind(ctx, field)\n\t\t\tcase \"name\":\n\t\t\t\treturn ec.fieldContext___Type_name(ctx, field)\n\t\t\tcase \"description\":\n\t\t\t\treturn ec.fieldContext___Type_description(ctx, field)\n\t\t\tcase \"fields\":\n\t\t\t\treturn ec.fieldContext___Type_fields(ctx, field)\n\t\t\tcase \"interfaces\":\n\t\t\t\treturn ec.fieldContext___Type_interfaces(ctx, field)\n\t\t\tcase \"possibleTypes\":\n\t\t\t\treturn ec.fieldContext___Type_possibleTypes(ctx, field)\n\t\t\tcase \"enumValues\":\n\t\t\t\treturn ec.fieldContext___Type_enumValues(ctx, field)\n\t\t\tcase \"inputFields\":\n\t\t\t\treturn ec.fieldContext___Type_inputFields(ctx, field)\n\t\t\tcase \"ofType\":\n\t\t\t\treturn ec.fieldContext___Type_ofType(ctx, field)\n\t\t\tcase \"specifiedByURL\":\n\t\t\t\treturn ec.fieldContext___Type_specifiedByURL(ctx, field)\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"no field named %q was found under type __Type\", field.Name)\n\t\t},\n\t}\n\treturn fc, nil\n}\n\nfunc (ec *executionContext) ___Schema_subscriptionType(ctx context.Context, field graphql.CollectedField, obj *introspection.Schema) (ret graphql.Marshaler) {\n\tfc, err := ec.fieldContext___Schema_subscriptionType(ctx, field)\n\tif err != nil {\n\t\treturn graphql.Null\n\t}\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\tret = graphql.Null\n\t\t}\n\t}()\n\tresTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {\n\t\tctx = rctx // use context from middleware stack in children\n\t\treturn obj.SubscriptionType(), nil\n\t})\n\tif err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn graphql.Null\n\t}\n\tif resTmp == nil {\n\t\treturn graphql.Null\n\t}\n\tres := resTmp.(*introspection.Type)\n\tfc.Result = res\n\treturn ec.marshalO__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res)\n}\n\nfunc (ec *executionContext) fieldContext___Schema_subscriptionType(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {\n\tfc = &graphql.FieldContext{\n\t\tObject:     \"__Schema\",\n\t\tField:      field,\n\t\tIsMethod:   true,\n\t\tIsResolver: false,\n\t\tChild: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {\n\t\t\tswitch field.Name {\n\t\t\tcase \"kind\":\n\t\t\t\treturn ec.fieldContext___Type_kind(ctx, field)\n\t\t\tcase \"name\":\n\t\t\t\treturn ec.fieldContext___Type_name(ctx, field)\n\t\t\tcase \"description\":\n\t\t\t\treturn ec.fieldContext___Type_description(ctx, field)\n\t\t\tcase \"fields\":\n\t\t\t\treturn ec.fieldContext___Type_fields(ctx, field)\n\t\t\tcase \"interfaces\":\n\t\t\t\treturn ec.fieldContext___Type_interfaces(ctx, field)\n\t\t\tcase \"possibleTypes\":\n\t\t\t\treturn ec.fieldContext___Type_possibleTypes(ctx, field)\n\t\t\tcase \"enumValues\":\n\t\t\t\treturn ec.fieldContext___Type_enumValues(ctx, field)\n\t\t\tcase \"inputFields\":\n\t\t\t\treturn ec.fieldContext___Type_inputFields(ctx, field)\n\t\t\tcase \"ofType\":\n\t\t\t\treturn ec.fieldContext___Type_ofType(ctx, field)\n\t\t\tcase \"specifiedByURL\":\n\t\t\t\treturn ec.fieldContext___Type_specifiedByURL(ctx, field)\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"no field named %q was found under type __Type\", field.Name)\n\t\t},\n\t}\n\treturn fc, nil\n}\n\nfunc (ec *executionContext) ___Schema_directives(ctx context.Context, field graphql.CollectedField, obj *introspection.Schema) (ret graphql.Marshaler) {\n\tfc, err := ec.fieldContext___Schema_directives(ctx, field)\n\tif err != nil {\n\t\treturn graphql.Null\n\t}\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\tret = graphql.Null\n\t\t}\n\t}()\n\tresTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {\n\t\tctx = rctx // use context from middleware stack in children\n\t\treturn obj.Directives(), nil\n\t})\n\tif err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn graphql.Null\n\t}\n\tif resTmp == nil {\n\t\tif !graphql.HasFieldError(ctx, fc) {\n\t\t\tec.Errorf(ctx, \"must not be null\")\n\t\t}\n\t\treturn graphql.Null\n\t}\n\tres := resTmp.([]introspection.Directive)\n\tfc.Result = res\n\treturn ec.marshalN__Directive2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐDirectiveᚄ(ctx, field.Selections, res)\n}\n\nfunc (ec *executionContext) fieldContext___Schema_directives(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {\n\tfc = &graphql.FieldContext{\n\t\tObject:     \"__Schema\",\n\t\tField:      field,\n\t\tIsMethod:   true,\n\t\tIsResolver: false,\n\t\tChild: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {\n\t\t\tswitch field.Name {\n\t\t\tcase \"name\":\n\t\t\t\treturn ec.fieldContext___Directive_name(ctx, field)\n\t\t\tcase \"description\":\n\t\t\t\treturn ec.fieldContext___Directive_description(ctx, field)\n\t\t\tcase \"locations\":\n\t\t\t\treturn ec.fieldContext___Directive_locations(ctx, field)\n\t\t\tcase \"args\":\n\t\t\t\treturn ec.fieldContext___Directive_args(ctx, field)\n\t\t\tcase \"isRepeatable\":\n\t\t\t\treturn ec.fieldContext___Directive_isRepeatable(ctx, field)\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"no field named %q was found under type __Directive\", field.Name)\n\t\t},\n\t}\n\treturn fc, nil\n}\n\nfunc (ec *executionContext) ___Type_kind(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) {\n\tfc, err := ec.fieldContext___Type_kind(ctx, field)\n\tif err != nil {\n\t\treturn graphql.Null\n\t}\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\tret = graphql.Null\n\t\t}\n\t}()\n\tresTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {\n\t\tctx = rctx // use context from middleware stack in children\n\t\treturn obj.Kind(), nil\n\t})\n\tif err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn graphql.Null\n\t}\n\tif resTmp == nil {\n\t\tif !graphql.HasFieldError(ctx, fc) {\n\t\t\tec.Errorf(ctx, \"must not be null\")\n\t\t}\n\t\treturn graphql.Null\n\t}\n\tres := resTmp.(string)\n\tfc.Result = res\n\treturn ec.marshalN__TypeKind2string(ctx, field.Selections, res)\n}\n\nfunc (ec *executionContext) fieldContext___Type_kind(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {\n\tfc = &graphql.FieldContext{\n\t\tObject:     \"__Type\",\n\t\tField:      field,\n\t\tIsMethod:   true,\n\t\tIsResolver: false,\n\t\tChild: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {\n\t\t\treturn nil, errors.New(\"field of type __TypeKind does not have child fields\")\n\t\t},\n\t}\n\treturn fc, nil\n}\n\nfunc (ec *executionContext) ___Type_name(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) {\n\tfc, err := ec.fieldContext___Type_name(ctx, field)\n\tif err != nil {\n\t\treturn graphql.Null\n\t}\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\tret = graphql.Null\n\t\t}\n\t}()\n\tresTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {\n\t\tctx = rctx // use context from middleware stack in children\n\t\treturn obj.Name(), nil\n\t})\n\tif err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn graphql.Null\n\t}\n\tif resTmp == nil {\n\t\treturn graphql.Null\n\t}\n\tres := resTmp.(*string)\n\tfc.Result = res\n\treturn ec.marshalOString2ᚖstring(ctx, field.Selections, res)\n}\n\nfunc (ec *executionContext) fieldContext___Type_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {\n\tfc = &graphql.FieldContext{\n\t\tObject:     \"__Type\",\n\t\tField:      field,\n\t\tIsMethod:   true,\n\t\tIsResolver: false,\n\t\tChild: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {\n\t\t\treturn nil, errors.New(\"field of type String does not have child fields\")\n\t\t},\n\t}\n\treturn fc, nil\n}\n\nfunc (ec *executionContext) ___Type_description(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) {\n\tfc, err := ec.fieldContext___Type_description(ctx, field)\n\tif err != nil {\n\t\treturn graphql.Null\n\t}\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\tret = graphql.Null\n\t\t}\n\t}()\n\tresTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {\n\t\tctx = rctx // use context from middleware stack in children\n\t\treturn obj.Description(), nil\n\t})\n\tif err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn graphql.Null\n\t}\n\tif resTmp == nil {\n\t\treturn graphql.Null\n\t}\n\tres := resTmp.(*string)\n\tfc.Result = res\n\treturn ec.marshalOString2ᚖstring(ctx, field.Selections, res)\n}\n\nfunc (ec *executionContext) fieldContext___Type_description(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {\n\tfc = &graphql.FieldContext{\n\t\tObject:     \"__Type\",\n\t\tField:      field,\n\t\tIsMethod:   true,\n\t\tIsResolver: false,\n\t\tChild: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {\n\t\t\treturn nil, errors.New(\"field of type String does not have child fields\")\n\t\t},\n\t}\n\treturn fc, nil\n}\n\nfunc (ec *executionContext) ___Type_fields(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) {\n\tfc, err := ec.fieldContext___Type_fields(ctx, field)\n\tif err != nil {\n\t\treturn graphql.Null\n\t}\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\tret = graphql.Null\n\t\t}\n\t}()\n\tresTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {\n\t\tctx = rctx // use context from middleware stack in children\n\t\treturn obj.Fields(fc.Args[\"includeDeprecated\"].(bool)), nil\n\t})\n\tif err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn graphql.Null\n\t}\n\tif resTmp == nil {\n\t\treturn graphql.Null\n\t}\n\tres := resTmp.([]introspection.Field)\n\tfc.Result = res\n\treturn ec.marshalO__Field2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐFieldᚄ(ctx, field.Selections, res)\n}\n\nfunc (ec *executionContext) fieldContext___Type_fields(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {\n\tfc = &graphql.FieldContext{\n\t\tObject:     \"__Type\",\n\t\tField:      field,\n\t\tIsMethod:   true,\n\t\tIsResolver: false,\n\t\tChild: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {\n\t\t\tswitch field.Name {\n\t\t\tcase \"name\":\n\t\t\t\treturn ec.fieldContext___Field_name(ctx, field)\n\t\t\tcase \"description\":\n\t\t\t\treturn ec.fieldContext___Field_description(ctx, field)\n\t\t\tcase \"args\":\n\t\t\t\treturn ec.fieldContext___Field_args(ctx, field)\n\t\t\tcase \"type\":\n\t\t\t\treturn ec.fieldContext___Field_type(ctx, field)\n\t\t\tcase \"isDeprecated\":\n\t\t\t\treturn ec.fieldContext___Field_isDeprecated(ctx, field)\n\t\t\tcase \"deprecationReason\":\n\t\t\t\treturn ec.fieldContext___Field_deprecationReason(ctx, field)\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"no field named %q was found under type __Field\", field.Name)\n\t\t},\n\t}\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\terr = ec.Recover(ctx, r)\n\t\t\tec.Error(ctx, err)\n\t\t}\n\t}()\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tif fc.Args, err = ec.field___Type_fields_args(ctx, field.ArgumentMap(ec.Variables)); err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn\n\t}\n\treturn fc, nil\n}\n\nfunc (ec *executionContext) ___Type_interfaces(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) {\n\tfc, err := ec.fieldContext___Type_interfaces(ctx, field)\n\tif err != nil {\n\t\treturn graphql.Null\n\t}\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\tret = graphql.Null\n\t\t}\n\t}()\n\tresTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {\n\t\tctx = rctx // use context from middleware stack in children\n\t\treturn obj.Interfaces(), nil\n\t})\n\tif err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn graphql.Null\n\t}\n\tif resTmp == nil {\n\t\treturn graphql.Null\n\t}\n\tres := resTmp.([]introspection.Type)\n\tfc.Result = res\n\treturn ec.marshalO__Type2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐTypeᚄ(ctx, field.Selections, res)\n}\n\nfunc (ec *executionContext) fieldContext___Type_interfaces(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {\n\tfc = &graphql.FieldContext{\n\t\tObject:     \"__Type\",\n\t\tField:      field,\n\t\tIsMethod:   true,\n\t\tIsResolver: false,\n\t\tChild: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {\n\t\t\tswitch field.Name {\n\t\t\tcase \"kind\":\n\t\t\t\treturn ec.fieldContext___Type_kind(ctx, field)\n\t\t\tcase \"name\":\n\t\t\t\treturn ec.fieldContext___Type_name(ctx, field)\n\t\t\tcase \"description\":\n\t\t\t\treturn ec.fieldContext___Type_description(ctx, field)\n\t\t\tcase \"fields\":\n\t\t\t\treturn ec.fieldContext___Type_fields(ctx, field)\n\t\t\tcase \"interfaces\":\n\t\t\t\treturn ec.fieldContext___Type_interfaces(ctx, field)\n\t\t\tcase \"possibleTypes\":\n\t\t\t\treturn ec.fieldContext___Type_possibleTypes(ctx, field)\n\t\t\tcase \"enumValues\":\n\t\t\t\treturn ec.fieldContext___Type_enumValues(ctx, field)\n\t\t\tcase \"inputFields\":\n\t\t\t\treturn ec.fieldContext___Type_inputFields(ctx, field)\n\t\t\tcase \"ofType\":\n\t\t\t\treturn ec.fieldContext___Type_ofType(ctx, field)\n\t\t\tcase \"specifiedByURL\":\n\t\t\t\treturn ec.fieldContext___Type_specifiedByURL(ctx, field)\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"no field named %q was found under type __Type\", field.Name)\n\t\t},\n\t}\n\treturn fc, nil\n}\n\nfunc (ec *executionContext) ___Type_possibleTypes(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) {\n\tfc, err := ec.fieldContext___Type_possibleTypes(ctx, field)\n\tif err != nil {\n\t\treturn graphql.Null\n\t}\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\tret = graphql.Null\n\t\t}\n\t}()\n\tresTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {\n\t\tctx = rctx // use context from middleware stack in children\n\t\treturn obj.PossibleTypes(), nil\n\t})\n\tif err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn graphql.Null\n\t}\n\tif resTmp == nil {\n\t\treturn graphql.Null\n\t}\n\tres := resTmp.([]introspection.Type)\n\tfc.Result = res\n\treturn ec.marshalO__Type2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐTypeᚄ(ctx, field.Selections, res)\n}\n\nfunc (ec *executionContext) fieldContext___Type_possibleTypes(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {\n\tfc = &graphql.FieldContext{\n\t\tObject:     \"__Type\",\n\t\tField:      field,\n\t\tIsMethod:   true,\n\t\tIsResolver: false,\n\t\tChild: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {\n\t\t\tswitch field.Name {\n\t\t\tcase \"kind\":\n\t\t\t\treturn ec.fieldContext___Type_kind(ctx, field)\n\t\t\tcase \"name\":\n\t\t\t\treturn ec.fieldContext___Type_name(ctx, field)\n\t\t\tcase \"description\":\n\t\t\t\treturn ec.fieldContext___Type_description(ctx, field)\n\t\t\tcase \"fields\":\n\t\t\t\treturn ec.fieldContext___Type_fields(ctx, field)\n\t\t\tcase \"interfaces\":\n\t\t\t\treturn ec.fieldContext___Type_interfaces(ctx, field)\n\t\t\tcase \"possibleTypes\":\n\t\t\t\treturn ec.fieldContext___Type_possibleTypes(ctx, field)\n\t\t\tcase \"enumValues\":\n\t\t\t\treturn ec.fieldContext___Type_enumValues(ctx, field)\n\t\t\tcase \"inputFields\":\n\t\t\t\treturn ec.fieldContext___Type_inputFields(ctx, field)\n\t\t\tcase \"ofType\":\n\t\t\t\treturn ec.fieldContext___Type_ofType(ctx, field)\n\t\t\tcase \"specifiedByURL\":\n\t\t\t\treturn ec.fieldContext___Type_specifiedByURL(ctx, field)\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"no field named %q was found under type __Type\", field.Name)\n\t\t},\n\t}\n\treturn fc, nil\n}\n\nfunc (ec *executionContext) ___Type_enumValues(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) {\n\tfc, err := ec.fieldContext___Type_enumValues(ctx, field)\n\tif err != nil {\n\t\treturn graphql.Null\n\t}\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\tret = graphql.Null\n\t\t}\n\t}()\n\tresTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {\n\t\tctx = rctx // use context from middleware stack in children\n\t\treturn obj.EnumValues(fc.Args[\"includeDeprecated\"].(bool)), nil\n\t})\n\tif err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn graphql.Null\n\t}\n\tif resTmp == nil {\n\t\treturn graphql.Null\n\t}\n\tres := resTmp.([]introspection.EnumValue)\n\tfc.Result = res\n\treturn ec.marshalO__EnumValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐEnumValueᚄ(ctx, field.Selections, res)\n}\n\nfunc (ec *executionContext) fieldContext___Type_enumValues(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {\n\tfc = &graphql.FieldContext{\n\t\tObject:     \"__Type\",\n\t\tField:      field,\n\t\tIsMethod:   true,\n\t\tIsResolver: false,\n\t\tChild: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {\n\t\t\tswitch field.Name {\n\t\t\tcase \"name\":\n\t\t\t\treturn ec.fieldContext___EnumValue_name(ctx, field)\n\t\t\tcase \"description\":\n\t\t\t\treturn ec.fieldContext___EnumValue_description(ctx, field)\n\t\t\tcase \"isDeprecated\":\n\t\t\t\treturn ec.fieldContext___EnumValue_isDeprecated(ctx, field)\n\t\t\tcase \"deprecationReason\":\n\t\t\t\treturn ec.fieldContext___EnumValue_deprecationReason(ctx, field)\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"no field named %q was found under type __EnumValue\", field.Name)\n\t\t},\n\t}\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\terr = ec.Recover(ctx, r)\n\t\t\tec.Error(ctx, err)\n\t\t}\n\t}()\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tif fc.Args, err = ec.field___Type_enumValues_args(ctx, field.ArgumentMap(ec.Variables)); err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn\n\t}\n\treturn fc, nil\n}\n\nfunc (ec *executionContext) ___Type_inputFields(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) {\n\tfc, err := ec.fieldContext___Type_inputFields(ctx, field)\n\tif err != nil {\n\t\treturn graphql.Null\n\t}\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\tret = graphql.Null\n\t\t}\n\t}()\n\tresTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {\n\t\tctx = rctx // use context from middleware stack in children\n\t\treturn obj.InputFields(), nil\n\t})\n\tif err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn graphql.Null\n\t}\n\tif resTmp == nil {\n\t\treturn graphql.Null\n\t}\n\tres := resTmp.([]introspection.InputValue)\n\tfc.Result = res\n\treturn ec.marshalO__InputValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValueᚄ(ctx, field.Selections, res)\n}\n\nfunc (ec *executionContext) fieldContext___Type_inputFields(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {\n\tfc = &graphql.FieldContext{\n\t\tObject:     \"__Type\",\n\t\tField:      field,\n\t\tIsMethod:   true,\n\t\tIsResolver: false,\n\t\tChild: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {\n\t\t\tswitch field.Name {\n\t\t\tcase \"name\":\n\t\t\t\treturn ec.fieldContext___InputValue_name(ctx, field)\n\t\t\tcase \"description\":\n\t\t\t\treturn ec.fieldContext___InputValue_description(ctx, field)\n\t\t\tcase \"type\":\n\t\t\t\treturn ec.fieldContext___InputValue_type(ctx, field)\n\t\t\tcase \"defaultValue\":\n\t\t\t\treturn ec.fieldContext___InputValue_defaultValue(ctx, field)\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"no field named %q was found under type __InputValue\", field.Name)\n\t\t},\n\t}\n\treturn fc, nil\n}\n\nfunc (ec *executionContext) ___Type_ofType(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) {\n\tfc, err := ec.fieldContext___Type_ofType(ctx, field)\n\tif err != nil {\n\t\treturn graphql.Null\n\t}\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\tret = graphql.Null\n\t\t}\n\t}()\n\tresTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {\n\t\tctx = rctx // use context from middleware stack in children\n\t\treturn obj.OfType(), nil\n\t})\n\tif err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn graphql.Null\n\t}\n\tif resTmp == nil {\n\t\treturn graphql.Null\n\t}\n\tres := resTmp.(*introspection.Type)\n\tfc.Result = res\n\treturn ec.marshalO__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res)\n}\n\nfunc (ec *executionContext) fieldContext___Type_ofType(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {\n\tfc = &graphql.FieldContext{\n\t\tObject:     \"__Type\",\n\t\tField:      field,\n\t\tIsMethod:   true,\n\t\tIsResolver: false,\n\t\tChild: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {\n\t\t\tswitch field.Name {\n\t\t\tcase \"kind\":\n\t\t\t\treturn ec.fieldContext___Type_kind(ctx, field)\n\t\t\tcase \"name\":\n\t\t\t\treturn ec.fieldContext___Type_name(ctx, field)\n\t\t\tcase \"description\":\n\t\t\t\treturn ec.fieldContext___Type_description(ctx, field)\n\t\t\tcase \"fields\":\n\t\t\t\treturn ec.fieldContext___Type_fields(ctx, field)\n\t\t\tcase \"interfaces\":\n\t\t\t\treturn ec.fieldContext___Type_interfaces(ctx, field)\n\t\t\tcase \"possibleTypes\":\n\t\t\t\treturn ec.fieldContext___Type_possibleTypes(ctx, field)\n\t\t\tcase \"enumValues\":\n\t\t\t\treturn ec.fieldContext___Type_enumValues(ctx, field)\n\t\t\tcase \"inputFields\":\n\t\t\t\treturn ec.fieldContext___Type_inputFields(ctx, field)\n\t\t\tcase \"ofType\":\n\t\t\t\treturn ec.fieldContext___Type_ofType(ctx, field)\n\t\t\tcase \"specifiedByURL\":\n\t\t\t\treturn ec.fieldContext___Type_specifiedByURL(ctx, field)\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"no field named %q was found under type __Type\", field.Name)\n\t\t},\n\t}\n\treturn fc, nil\n}\n\nfunc (ec *executionContext) ___Type_specifiedByURL(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) {\n\tfc, err := ec.fieldContext___Type_specifiedByURL(ctx, field)\n\tif err != nil {\n\t\treturn graphql.Null\n\t}\n\tctx = graphql.WithFieldContext(ctx, fc)\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\tret = graphql.Null\n\t\t}\n\t}()\n\tresTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {\n\t\tctx = rctx // use context from middleware stack in children\n\t\treturn obj.SpecifiedByURL(), nil\n\t})\n\tif err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn graphql.Null\n\t}\n\tif resTmp == nil {\n\t\treturn graphql.Null\n\t}\n\tres := resTmp.(*string)\n\tfc.Result = res\n\treturn ec.marshalOString2ᚖstring(ctx, field.Selections, res)\n}\n\nfunc (ec *executionContext) fieldContext___Type_specifiedByURL(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {\n\tfc = &graphql.FieldContext{\n\t\tObject:     \"__Type\",\n\t\tField:      field,\n\t\tIsMethod:   true,\n\t\tIsResolver: false,\n\t\tChild: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {\n\t\t\treturn nil, errors.New(\"field of type String does not have child fields\")\n\t\t},\n\t}\n\treturn fc, nil\n}\n\n// endregion **************************** field.gotpl *****************************\n\n// region    **************************** input.gotpl *****************************\n\nfunc (ec *executionContext) unmarshalInputCharacterInput(ctx context.Context, obj any) (model.CharacterInput, error) {\n\tvar it model.CharacterInput\n\tasMap := map[string]any{}\n\tfor k, v := range obj.(map[string]any) {\n\t\tasMap[k] = v\n\t}\n\n\tfieldsInOrder := [...]string{\"name\", \"id\", \"isHero\", \"cliqueType\"}\n\tfor _, k := range fieldsInOrder {\n\t\tv, ok := asMap[k]\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\t\tswitch k {\n\t\tcase \"name\":\n\t\t\tvar err error\n\n\t\t\tctx := graphql.WithPathContext(ctx, graphql.NewPathWithField(\"name\"))\n\t\t\tit.Name, err = ec.unmarshalNString2string(ctx, v)\n\t\t\tif err != nil {\n\t\t\t\treturn it, err\n\t\t\t}\n\t\tcase \"id\":\n\t\t\tvar err error\n\n\t\t\tctx := graphql.WithPathContext(ctx, graphql.NewPathWithField(\"id\"))\n\t\t\tit.ID, err = ec.unmarshalOString2ᚖstring(ctx, v)\n\t\t\tif err != nil {\n\t\t\t\treturn it, err\n\t\t\t}\n\t\tcase \"isHero\":\n\t\t\tvar err error\n\n\t\t\tctx := graphql.WithPathContext(ctx, graphql.NewPathWithField(\"isHero\"))\n\t\t\tit.IsHero, err = ec.unmarshalOBoolean2ᚖbool(ctx, v)\n\t\t\tif err != nil {\n\t\t\t\treturn it, err\n\t\t\t}\n\t\tcase \"cliqueType\":\n\t\t\tvar err error\n\n\t\t\tctx := graphql.WithPathContext(ctx, graphql.NewPathWithField(\"cliqueType\"))\n\t\t\tit.CliqueType, err = ec.unmarshalNCliqueType2githubᚗcomᚋirisᚑcontribᚋouterbanksᚑapiᚋgraphᚋgraphᚋmodelᚐCliqueType(ctx, v)\n\t\t\tif err != nil {\n\t\t\t\treturn it, err\n\t\t\t}\n\t\t}\n\t}\n\n\treturn it, nil\n}\n\n// endregion **************************** input.gotpl *****************************\n\n// region    ************************** interface.gotpl ***************************\n\n// endregion ************************** interface.gotpl ***************************\n\n// region    **************************** object.gotpl ****************************\n\nvar characterImplementors = []string{\"Character\"}\n\nfunc (ec *executionContext) _Character(ctx context.Context, sel ast.SelectionSet, obj *model.Character) graphql.Marshaler {\n\tfields := graphql.CollectFields(ec.OperationContext, sel, characterImplementors)\n\tout := graphql.NewFieldSet(fields)\n\tvar invalids uint32\n\tfor i, field := range fields {\n\t\tswitch field.Name {\n\t\tcase \"__typename\":\n\t\t\tout.Values[i] = graphql.MarshalString(\"Character\")\n\t\tcase \"id\":\n\n\t\t\tout.Values[i] = ec._Character_id(ctx, field, obj)\n\n\t\t\tif out.Values[i] == graphql.Null {\n\t\t\t\tinvalids++\n\t\t\t}\n\t\tcase \"name\":\n\n\t\t\tout.Values[i] = ec._Character_name(ctx, field, obj)\n\n\t\t\tif out.Values[i] == graphql.Null {\n\t\t\t\tinvalids++\n\t\t\t}\n\t\tcase \"isHero\":\n\n\t\t\tout.Values[i] = ec._Character_isHero(ctx, field, obj)\n\n\t\t\tif out.Values[i] == graphql.Null {\n\t\t\t\tinvalids++\n\t\t\t}\n\t\tcase \"cliqueType\":\n\n\t\t\tout.Values[i] = ec._Character_cliqueType(ctx, field, obj)\n\n\t\t\tif out.Values[i] == graphql.Null {\n\t\t\t\tinvalids++\n\t\t\t}\n\t\tdefault:\n\t\t\tpanic(\"unknown field \" + strconv.Quote(field.Name))\n\t\t}\n\t}\n\tout.Dispatch()\n\tif invalids > 0 {\n\t\treturn graphql.Null\n\t}\n\treturn out\n}\n\nvar mutationImplementors = []string{\"Mutation\"}\n\nfunc (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) graphql.Marshaler {\n\tfields := graphql.CollectFields(ec.OperationContext, sel, mutationImplementors)\n\tctx = graphql.WithFieldContext(ctx, &graphql.FieldContext{\n\t\tObject: \"Mutation\",\n\t})\n\n\tout := graphql.NewFieldSet(fields)\n\tfor i, field := range fields {\n\t\tinnerCtx := graphql.WithRootFieldContext(ctx, &graphql.RootFieldContext{\n\t\t\tObject: field.Name,\n\t\t\tField:  field,\n\t\t})\n\n\t\tswitch field.Name {\n\t\tcase \"__typename\":\n\t\t\tout.Values[i] = graphql.MarshalString(\"Mutation\")\n\t\tcase \"upsertCharacter\":\n\n\t\t\tout.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) {\n\t\t\t\treturn ec._Mutation_upsertCharacter(ctx, field)\n\t\t\t})\n\n\t\tdefault:\n\t\t\tpanic(\"unknown field \" + strconv.Quote(field.Name))\n\t\t}\n\t}\n\tout.Dispatch()\n\treturn out\n}\n\nvar queryImplementors = []string{\"Query\"}\n\nfunc (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) graphql.Marshaler {\n\tfields := graphql.CollectFields(ec.OperationContext, sel, queryImplementors)\n\tctx = graphql.WithFieldContext(ctx, &graphql.FieldContext{\n\t\tObject: \"Query\",\n\t})\n\n\tout := graphql.NewFieldSet(fields)\n\tfor i, field := range fields {\n\t\tinnerCtx := graphql.WithRootFieldContext(ctx, &graphql.RootFieldContext{\n\t\t\tObject: field.Name,\n\t\t\tField:  field,\n\t\t})\n\n\t\tswitch field.Name {\n\t\tcase \"__typename\":\n\t\t\tout.Values[i] = graphql.MarshalString(\"Query\")\n\t\tcase \"character\":\n\t\t\tfield := field\n\n\t\t\tinnerFunc := func(ctx context.Context) (res graphql.Marshaler) {\n\t\t\t\tdefer func() {\n\t\t\t\t\tif r := recover(); r != nil {\n\t\t\t\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\t\t\t}\n\t\t\t\t}()\n\t\t\t\tres = ec._Query_character(ctx, field)\n\t\t\t\treturn res\n\t\t\t}\n\n\t\t\trrm := func(ctx context.Context) graphql.Marshaler {\n\t\t\t\treturn ec.OperationContext.RootResolverMiddleware(ctx, innerFunc)\n\t\t\t}\n\n\t\t\tout.Concurrently(i, func() graphql.Marshaler {\n\t\t\t\treturn rrm(innerCtx)\n\t\t\t})\n\t\tcase \"characters\":\n\t\t\tfield := field\n\n\t\t\tinnerFunc := func(ctx context.Context) (res graphql.Marshaler) {\n\t\t\t\tdefer func() {\n\t\t\t\t\tif r := recover(); r != nil {\n\t\t\t\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\t\t\t}\n\t\t\t\t}()\n\t\t\t\tres = ec._Query_characters(ctx, field)\n\t\t\t\treturn res\n\t\t\t}\n\n\t\t\trrm := func(ctx context.Context) graphql.Marshaler {\n\t\t\t\treturn ec.OperationContext.RootResolverMiddleware(ctx, innerFunc)\n\t\t\t}\n\n\t\t\tout.Concurrently(i, func() graphql.Marshaler {\n\t\t\t\treturn rrm(innerCtx)\n\t\t\t})\n\t\tcase \"__type\":\n\n\t\t\tout.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) {\n\t\t\t\treturn ec._Query___type(ctx, field)\n\t\t\t})\n\n\t\tcase \"__schema\":\n\n\t\t\tout.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) {\n\t\t\t\treturn ec._Query___schema(ctx, field)\n\t\t\t})\n\n\t\tdefault:\n\t\t\tpanic(\"unknown field \" + strconv.Quote(field.Name))\n\t\t}\n\t}\n\tout.Dispatch()\n\treturn out\n}\n\nvar __DirectiveImplementors = []string{\"__Directive\"}\n\nfunc (ec *executionContext) ___Directive(ctx context.Context, sel ast.SelectionSet, obj *introspection.Directive) graphql.Marshaler {\n\tfields := graphql.CollectFields(ec.OperationContext, sel, __DirectiveImplementors)\n\tout := graphql.NewFieldSet(fields)\n\tvar invalids uint32\n\tfor i, field := range fields {\n\t\tswitch field.Name {\n\t\tcase \"__typename\":\n\t\t\tout.Values[i] = graphql.MarshalString(\"__Directive\")\n\t\tcase \"name\":\n\n\t\t\tout.Values[i] = ec.___Directive_name(ctx, field, obj)\n\n\t\t\tif out.Values[i] == graphql.Null {\n\t\t\t\tinvalids++\n\t\t\t}\n\t\tcase \"description\":\n\n\t\t\tout.Values[i] = ec.___Directive_description(ctx, field, obj)\n\n\t\tcase \"locations\":\n\n\t\t\tout.Values[i] = ec.___Directive_locations(ctx, field, obj)\n\n\t\t\tif out.Values[i] == graphql.Null {\n\t\t\t\tinvalids++\n\t\t\t}\n\t\tcase \"args\":\n\n\t\t\tout.Values[i] = ec.___Directive_args(ctx, field, obj)\n\n\t\t\tif out.Values[i] == graphql.Null {\n\t\t\t\tinvalids++\n\t\t\t}\n\t\tcase \"isRepeatable\":\n\n\t\t\tout.Values[i] = ec.___Directive_isRepeatable(ctx, field, obj)\n\n\t\t\tif out.Values[i] == graphql.Null {\n\t\t\t\tinvalids++\n\t\t\t}\n\t\tdefault:\n\t\t\tpanic(\"unknown field \" + strconv.Quote(field.Name))\n\t\t}\n\t}\n\tout.Dispatch()\n\tif invalids > 0 {\n\t\treturn graphql.Null\n\t}\n\treturn out\n}\n\nvar __EnumValueImplementors = []string{\"__EnumValue\"}\n\nfunc (ec *executionContext) ___EnumValue(ctx context.Context, sel ast.SelectionSet, obj *introspection.EnumValue) graphql.Marshaler {\n\tfields := graphql.CollectFields(ec.OperationContext, sel, __EnumValueImplementors)\n\tout := graphql.NewFieldSet(fields)\n\tvar invalids uint32\n\tfor i, field := range fields {\n\t\tswitch field.Name {\n\t\tcase \"__typename\":\n\t\t\tout.Values[i] = graphql.MarshalString(\"__EnumValue\")\n\t\tcase \"name\":\n\n\t\t\tout.Values[i] = ec.___EnumValue_name(ctx, field, obj)\n\n\t\t\tif out.Values[i] == graphql.Null {\n\t\t\t\tinvalids++\n\t\t\t}\n\t\tcase \"description\":\n\n\t\t\tout.Values[i] = ec.___EnumValue_description(ctx, field, obj)\n\n\t\tcase \"isDeprecated\":\n\n\t\t\tout.Values[i] = ec.___EnumValue_isDeprecated(ctx, field, obj)\n\n\t\t\tif out.Values[i] == graphql.Null {\n\t\t\t\tinvalids++\n\t\t\t}\n\t\tcase \"deprecationReason\":\n\n\t\t\tout.Values[i] = ec.___EnumValue_deprecationReason(ctx, field, obj)\n\n\t\tdefault:\n\t\t\tpanic(\"unknown field \" + strconv.Quote(field.Name))\n\t\t}\n\t}\n\tout.Dispatch()\n\tif invalids > 0 {\n\t\treturn graphql.Null\n\t}\n\treturn out\n}\n\nvar __FieldImplementors = []string{\"__Field\"}\n\nfunc (ec *executionContext) ___Field(ctx context.Context, sel ast.SelectionSet, obj *introspection.Field) graphql.Marshaler {\n\tfields := graphql.CollectFields(ec.OperationContext, sel, __FieldImplementors)\n\tout := graphql.NewFieldSet(fields)\n\tvar invalids uint32\n\tfor i, field := range fields {\n\t\tswitch field.Name {\n\t\tcase \"__typename\":\n\t\t\tout.Values[i] = graphql.MarshalString(\"__Field\")\n\t\tcase \"name\":\n\n\t\t\tout.Values[i] = ec.___Field_name(ctx, field, obj)\n\n\t\t\tif out.Values[i] == graphql.Null {\n\t\t\t\tinvalids++\n\t\t\t}\n\t\tcase \"description\":\n\n\t\t\tout.Values[i] = ec.___Field_description(ctx, field, obj)\n\n\t\tcase \"args\":\n\n\t\t\tout.Values[i] = ec.___Field_args(ctx, field, obj)\n\n\t\t\tif out.Values[i] == graphql.Null {\n\t\t\t\tinvalids++\n\t\t\t}\n\t\tcase \"type\":\n\n\t\t\tout.Values[i] = ec.___Field_type(ctx, field, obj)\n\n\t\t\tif out.Values[i] == graphql.Null {\n\t\t\t\tinvalids++\n\t\t\t}\n\t\tcase \"isDeprecated\":\n\n\t\t\tout.Values[i] = ec.___Field_isDeprecated(ctx, field, obj)\n\n\t\t\tif out.Values[i] == graphql.Null {\n\t\t\t\tinvalids++\n\t\t\t}\n\t\tcase \"deprecationReason\":\n\n\t\t\tout.Values[i] = ec.___Field_deprecationReason(ctx, field, obj)\n\n\t\tdefault:\n\t\t\tpanic(\"unknown field \" + strconv.Quote(field.Name))\n\t\t}\n\t}\n\tout.Dispatch()\n\tif invalids > 0 {\n\t\treturn graphql.Null\n\t}\n\treturn out\n}\n\nvar __InputValueImplementors = []string{\"__InputValue\"}\n\nfunc (ec *executionContext) ___InputValue(ctx context.Context, sel ast.SelectionSet, obj *introspection.InputValue) graphql.Marshaler {\n\tfields := graphql.CollectFields(ec.OperationContext, sel, __InputValueImplementors)\n\tout := graphql.NewFieldSet(fields)\n\tvar invalids uint32\n\tfor i, field := range fields {\n\t\tswitch field.Name {\n\t\tcase \"__typename\":\n\t\t\tout.Values[i] = graphql.MarshalString(\"__InputValue\")\n\t\tcase \"name\":\n\n\t\t\tout.Values[i] = ec.___InputValue_name(ctx, field, obj)\n\n\t\t\tif out.Values[i] == graphql.Null {\n\t\t\t\tinvalids++\n\t\t\t}\n\t\tcase \"description\":\n\n\t\t\tout.Values[i] = ec.___InputValue_description(ctx, field, obj)\n\n\t\tcase \"type\":\n\n\t\t\tout.Values[i] = ec.___InputValue_type(ctx, field, obj)\n\n\t\t\tif out.Values[i] == graphql.Null {\n\t\t\t\tinvalids++\n\t\t\t}\n\t\tcase \"defaultValue\":\n\n\t\t\tout.Values[i] = ec.___InputValue_defaultValue(ctx, field, obj)\n\n\t\tdefault:\n\t\t\tpanic(\"unknown field \" + strconv.Quote(field.Name))\n\t\t}\n\t}\n\tout.Dispatch()\n\tif invalids > 0 {\n\t\treturn graphql.Null\n\t}\n\treturn out\n}\n\nvar __SchemaImplementors = []string{\"__Schema\"}\n\nfunc (ec *executionContext) ___Schema(ctx context.Context, sel ast.SelectionSet, obj *introspection.Schema) graphql.Marshaler {\n\tfields := graphql.CollectFields(ec.OperationContext, sel, __SchemaImplementors)\n\tout := graphql.NewFieldSet(fields)\n\tvar invalids uint32\n\tfor i, field := range fields {\n\t\tswitch field.Name {\n\t\tcase \"__typename\":\n\t\t\tout.Values[i] = graphql.MarshalString(\"__Schema\")\n\t\tcase \"description\":\n\n\t\t\tout.Values[i] = ec.___Schema_description(ctx, field, obj)\n\n\t\tcase \"types\":\n\n\t\t\tout.Values[i] = ec.___Schema_types(ctx, field, obj)\n\n\t\t\tif out.Values[i] == graphql.Null {\n\t\t\t\tinvalids++\n\t\t\t}\n\t\tcase \"queryType\":\n\n\t\t\tout.Values[i] = ec.___Schema_queryType(ctx, field, obj)\n\n\t\t\tif out.Values[i] == graphql.Null {\n\t\t\t\tinvalids++\n\t\t\t}\n\t\tcase \"mutationType\":\n\n\t\t\tout.Values[i] = ec.___Schema_mutationType(ctx, field, obj)\n\n\t\tcase \"subscriptionType\":\n\n\t\t\tout.Values[i] = ec.___Schema_subscriptionType(ctx, field, obj)\n\n\t\tcase \"directives\":\n\n\t\t\tout.Values[i] = ec.___Schema_directives(ctx, field, obj)\n\n\t\t\tif out.Values[i] == graphql.Null {\n\t\t\t\tinvalids++\n\t\t\t}\n\t\tdefault:\n\t\t\tpanic(\"unknown field \" + strconv.Quote(field.Name))\n\t\t}\n\t}\n\tout.Dispatch()\n\tif invalids > 0 {\n\t\treturn graphql.Null\n\t}\n\treturn out\n}\n\nvar __TypeImplementors = []string{\"__Type\"}\n\nfunc (ec *executionContext) ___Type(ctx context.Context, sel ast.SelectionSet, obj *introspection.Type) graphql.Marshaler {\n\tfields := graphql.CollectFields(ec.OperationContext, sel, __TypeImplementors)\n\tout := graphql.NewFieldSet(fields)\n\tvar invalids uint32\n\tfor i, field := range fields {\n\t\tswitch field.Name {\n\t\tcase \"__typename\":\n\t\t\tout.Values[i] = graphql.MarshalString(\"__Type\")\n\t\tcase \"kind\":\n\n\t\t\tout.Values[i] = ec.___Type_kind(ctx, field, obj)\n\n\t\t\tif out.Values[i] == graphql.Null {\n\t\t\t\tinvalids++\n\t\t\t}\n\t\tcase \"name\":\n\n\t\t\tout.Values[i] = ec.___Type_name(ctx, field, obj)\n\n\t\tcase \"description\":\n\n\t\t\tout.Values[i] = ec.___Type_description(ctx, field, obj)\n\n\t\tcase \"fields\":\n\n\t\t\tout.Values[i] = ec.___Type_fields(ctx, field, obj)\n\n\t\tcase \"interfaces\":\n\n\t\t\tout.Values[i] = ec.___Type_interfaces(ctx, field, obj)\n\n\t\tcase \"possibleTypes\":\n\n\t\t\tout.Values[i] = ec.___Type_possibleTypes(ctx, field, obj)\n\n\t\tcase \"enumValues\":\n\n\t\t\tout.Values[i] = ec.___Type_enumValues(ctx, field, obj)\n\n\t\tcase \"inputFields\":\n\n\t\t\tout.Values[i] = ec.___Type_inputFields(ctx, field, obj)\n\n\t\tcase \"ofType\":\n\n\t\t\tout.Values[i] = ec.___Type_ofType(ctx, field, obj)\n\n\t\tcase \"specifiedByURL\":\n\n\t\t\tout.Values[i] = ec.___Type_specifiedByURL(ctx, field, obj)\n\n\t\tdefault:\n\t\t\tpanic(\"unknown field \" + strconv.Quote(field.Name))\n\t\t}\n\t}\n\tout.Dispatch()\n\tif invalids > 0 {\n\t\treturn graphql.Null\n\t}\n\treturn out\n}\n\n// endregion **************************** object.gotpl ****************************\n\n// region    ***************************** type.gotpl *****************************\n\nfunc (ec *executionContext) unmarshalNBoolean2bool(ctx context.Context, v any) (bool, error) {\n\tres, err := graphql.UnmarshalBoolean(v)\n\treturn res, graphql.ErrorOnPath(ctx, err)\n}\n\nfunc (ec *executionContext) marshalNBoolean2bool(ctx context.Context, sel ast.SelectionSet, v bool) graphql.Marshaler {\n\tres := graphql.MarshalBoolean(v)\n\tif res == graphql.Null {\n\t\tif !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {\n\t\t\tec.Errorf(ctx, \"the requested element is null which the schema does not allow\")\n\t\t}\n\t}\n\treturn res\n}\n\nfunc (ec *executionContext) marshalNCharacter2githubᚗcomᚋirisᚑcontribᚋouterbanksᚑapiᚋgraphᚋgraphᚋmodelᚐCharacter(ctx context.Context, sel ast.SelectionSet, v model.Character) graphql.Marshaler {\n\treturn ec._Character(ctx, sel, &v)\n}\n\nfunc (ec *executionContext) marshalNCharacter2ᚖgithubᚗcomᚋirisᚑcontribᚋouterbanksᚑapiᚋgraphᚋgraphᚋmodelᚐCharacter(ctx context.Context, sel ast.SelectionSet, v *model.Character) graphql.Marshaler {\n\tif v == nil {\n\t\tif !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {\n\t\t\tec.Errorf(ctx, \"the requested element is null which the schema does not allow\")\n\t\t}\n\t\treturn graphql.Null\n\t}\n\treturn ec._Character(ctx, sel, v)\n}\n\nfunc (ec *executionContext) unmarshalNCharacterInput2githubᚗcomᚋirisᚑcontribᚋouterbanksᚑapiᚋgraphᚋgraphᚋmodelᚐCharacterInput(ctx context.Context, v any) (model.CharacterInput, error) {\n\tres, err := ec.unmarshalInputCharacterInput(ctx, v)\n\treturn res, graphql.ErrorOnPath(ctx, err)\n}\n\nfunc (ec *executionContext) unmarshalNCliqueType2githubᚗcomᚋirisᚑcontribᚋouterbanksᚑapiᚋgraphᚋgraphᚋmodelᚐCliqueType(ctx context.Context, v any) (model.CliqueType, error) {\n\tvar res model.CliqueType\n\terr := res.UnmarshalGQL(v)\n\treturn res, graphql.ErrorOnPath(ctx, err)\n}\n\nfunc (ec *executionContext) marshalNCliqueType2githubᚗcomᚋirisᚑcontribᚋouterbanksᚑapiᚋgraphᚋgraphᚋmodelᚐCliqueType(ctx context.Context, sel ast.SelectionSet, v model.CliqueType) graphql.Marshaler {\n\treturn v\n}\n\nfunc (ec *executionContext) unmarshalNID2string(ctx context.Context, v any) (string, error) {\n\tres, err := graphql.UnmarshalID(v)\n\treturn res, graphql.ErrorOnPath(ctx, err)\n}\n\nfunc (ec *executionContext) marshalNID2string(ctx context.Context, sel ast.SelectionSet, v string) graphql.Marshaler {\n\tres := graphql.MarshalID(v)\n\tif res == graphql.Null {\n\t\tif !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {\n\t\t\tec.Errorf(ctx, \"the requested element is null which the schema does not allow\")\n\t\t}\n\t}\n\treturn res\n}\n\nfunc (ec *executionContext) unmarshalNString2string(ctx context.Context, v any) (string, error) {\n\tres, err := graphql.UnmarshalString(v)\n\treturn res, graphql.ErrorOnPath(ctx, err)\n}\n\nfunc (ec *executionContext) marshalNString2string(ctx context.Context, sel ast.SelectionSet, v string) graphql.Marshaler {\n\tres := graphql.MarshalString(v)\n\tif res == graphql.Null {\n\t\tif !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {\n\t\t\tec.Errorf(ctx, \"the requested element is null which the schema does not allow\")\n\t\t}\n\t}\n\treturn res\n}\n\nfunc (ec *executionContext) marshalN__Directive2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐDirective(ctx context.Context, sel ast.SelectionSet, v introspection.Directive) graphql.Marshaler {\n\treturn ec.___Directive(ctx, sel, &v)\n}\n\nfunc (ec *executionContext) marshalN__Directive2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐDirectiveᚄ(ctx context.Context, sel ast.SelectionSet, v []introspection.Directive) graphql.Marshaler {\n\tret := make(graphql.Array, len(v))\n\tvar wg sync.WaitGroup\n\tisLen1 := len(v) == 1\n\tif !isLen1 {\n\t\twg.Add(len(v))\n\t}\n\tfor i := range v {\n\t\ti := i\n\t\tfc := &graphql.FieldContext{\n\t\t\tIndex:  &i,\n\t\t\tResult: &v[i],\n\t\t}\n\t\tctx := graphql.WithFieldContext(ctx, fc)\n\t\tf := func(i int) {\n\t\t\tdefer func() {\n\t\t\t\tif r := recover(); r != nil {\n\t\t\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\t\t\tret = nil\n\t\t\t\t}\n\t\t\t}()\n\t\t\tif !isLen1 {\n\t\t\t\tdefer wg.Done()\n\t\t\t}\n\t\t\tret[i] = ec.marshalN__Directive2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐDirective(ctx, sel, v[i])\n\t\t}\n\t\tif isLen1 {\n\t\t\tf(i)\n\t\t} else {\n\t\t\tgo f(i)\n\t\t}\n\n\t}\n\twg.Wait()\n\n\tfor _, e := range ret {\n\t\tif e == graphql.Null {\n\t\t\treturn graphql.Null\n\t\t}\n\t}\n\n\treturn ret\n}\n\nfunc (ec *executionContext) unmarshalN__DirectiveLocation2string(ctx context.Context, v any) (string, error) {\n\tres, err := graphql.UnmarshalString(v)\n\treturn res, graphql.ErrorOnPath(ctx, err)\n}\n\nfunc (ec *executionContext) marshalN__DirectiveLocation2string(ctx context.Context, sel ast.SelectionSet, v string) graphql.Marshaler {\n\tres := graphql.MarshalString(v)\n\tif res == graphql.Null {\n\t\tif !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {\n\t\t\tec.Errorf(ctx, \"the requested element is null which the schema does not allow\")\n\t\t}\n\t}\n\treturn res\n}\n\nfunc (ec *executionContext) unmarshalN__DirectiveLocation2ᚕstringᚄ(ctx context.Context, v any) ([]string, error) {\n\tvar vSlice []any\n\tif v != nil {\n\t\tvSlice = graphql.CoerceList(v)\n\t}\n\tvar err error\n\tres := make([]string, len(vSlice))\n\tfor i := range vSlice {\n\t\tctx := graphql.WithPathContext(ctx, graphql.NewPathWithIndex(i))\n\t\tres[i], err = ec.unmarshalN__DirectiveLocation2string(ctx, vSlice[i])\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\treturn res, nil\n}\n\nfunc (ec *executionContext) marshalN__DirectiveLocation2ᚕstringᚄ(ctx context.Context, sel ast.SelectionSet, v []string) graphql.Marshaler {\n\tret := make(graphql.Array, len(v))\n\tvar wg sync.WaitGroup\n\tisLen1 := len(v) == 1\n\tif !isLen1 {\n\t\twg.Add(len(v))\n\t}\n\tfor i := range v {\n\t\ti := i\n\t\tfc := &graphql.FieldContext{\n\t\t\tIndex:  &i,\n\t\t\tResult: &v[i],\n\t\t}\n\t\tctx := graphql.WithFieldContext(ctx, fc)\n\t\tf := func(i int) {\n\t\t\tdefer func() {\n\t\t\t\tif r := recover(); r != nil {\n\t\t\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\t\t\tret = nil\n\t\t\t\t}\n\t\t\t}()\n\t\t\tif !isLen1 {\n\t\t\t\tdefer wg.Done()\n\t\t\t}\n\t\t\tret[i] = ec.marshalN__DirectiveLocation2string(ctx, sel, v[i])\n\t\t}\n\t\tif isLen1 {\n\t\t\tf(i)\n\t\t} else {\n\t\t\tgo f(i)\n\t\t}\n\n\t}\n\twg.Wait()\n\n\tfor _, e := range ret {\n\t\tif e == graphql.Null {\n\t\t\treturn graphql.Null\n\t\t}\n\t}\n\n\treturn ret\n}\n\nfunc (ec *executionContext) marshalN__EnumValue2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐEnumValue(ctx context.Context, sel ast.SelectionSet, v introspection.EnumValue) graphql.Marshaler {\n\treturn ec.___EnumValue(ctx, sel, &v)\n}\n\nfunc (ec *executionContext) marshalN__Field2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐField(ctx context.Context, sel ast.SelectionSet, v introspection.Field) graphql.Marshaler {\n\treturn ec.___Field(ctx, sel, &v)\n}\n\nfunc (ec *executionContext) marshalN__InputValue2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValue(ctx context.Context, sel ast.SelectionSet, v introspection.InputValue) graphql.Marshaler {\n\treturn ec.___InputValue(ctx, sel, &v)\n}\n\nfunc (ec *executionContext) marshalN__InputValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValueᚄ(ctx context.Context, sel ast.SelectionSet, v []introspection.InputValue) graphql.Marshaler {\n\tret := make(graphql.Array, len(v))\n\tvar wg sync.WaitGroup\n\tisLen1 := len(v) == 1\n\tif !isLen1 {\n\t\twg.Add(len(v))\n\t}\n\tfor i := range v {\n\t\ti := i\n\t\tfc := &graphql.FieldContext{\n\t\t\tIndex:  &i,\n\t\t\tResult: &v[i],\n\t\t}\n\t\tctx := graphql.WithFieldContext(ctx, fc)\n\t\tf := func(i int) {\n\t\t\tdefer func() {\n\t\t\t\tif r := recover(); r != nil {\n\t\t\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\t\t\tret = nil\n\t\t\t\t}\n\t\t\t}()\n\t\t\tif !isLen1 {\n\t\t\t\tdefer wg.Done()\n\t\t\t}\n\t\t\tret[i] = ec.marshalN__InputValue2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValue(ctx, sel, v[i])\n\t\t}\n\t\tif isLen1 {\n\t\t\tf(i)\n\t\t} else {\n\t\t\tgo f(i)\n\t\t}\n\n\t}\n\twg.Wait()\n\n\tfor _, e := range ret {\n\t\tif e == graphql.Null {\n\t\t\treturn graphql.Null\n\t\t}\n\t}\n\n\treturn ret\n}\n\nfunc (ec *executionContext) marshalN__Type2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx context.Context, sel ast.SelectionSet, v introspection.Type) graphql.Marshaler {\n\treturn ec.___Type(ctx, sel, &v)\n}\n\nfunc (ec *executionContext) marshalN__Type2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐTypeᚄ(ctx context.Context, sel ast.SelectionSet, v []introspection.Type) graphql.Marshaler {\n\tret := make(graphql.Array, len(v))\n\tvar wg sync.WaitGroup\n\tisLen1 := len(v) == 1\n\tif !isLen1 {\n\t\twg.Add(len(v))\n\t}\n\tfor i := range v {\n\t\ti := i\n\t\tfc := &graphql.FieldContext{\n\t\t\tIndex:  &i,\n\t\t\tResult: &v[i],\n\t\t}\n\t\tctx := graphql.WithFieldContext(ctx, fc)\n\t\tf := func(i int) {\n\t\t\tdefer func() {\n\t\t\t\tif r := recover(); r != nil {\n\t\t\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\t\t\tret = nil\n\t\t\t\t}\n\t\t\t}()\n\t\t\tif !isLen1 {\n\t\t\t\tdefer wg.Done()\n\t\t\t}\n\t\t\tret[i] = ec.marshalN__Type2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, sel, v[i])\n\t\t}\n\t\tif isLen1 {\n\t\t\tf(i)\n\t\t} else {\n\t\t\tgo f(i)\n\t\t}\n\n\t}\n\twg.Wait()\n\n\tfor _, e := range ret {\n\t\tif e == graphql.Null {\n\t\t\treturn graphql.Null\n\t\t}\n\t}\n\n\treturn ret\n}\n\nfunc (ec *executionContext) marshalN__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx context.Context, sel ast.SelectionSet, v *introspection.Type) graphql.Marshaler {\n\tif v == nil {\n\t\tif !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {\n\t\t\tec.Errorf(ctx, \"the requested element is null which the schema does not allow\")\n\t\t}\n\t\treturn graphql.Null\n\t}\n\treturn ec.___Type(ctx, sel, v)\n}\n\nfunc (ec *executionContext) unmarshalN__TypeKind2string(ctx context.Context, v any) (string, error) {\n\tres, err := graphql.UnmarshalString(v)\n\treturn res, graphql.ErrorOnPath(ctx, err)\n}\n\nfunc (ec *executionContext) marshalN__TypeKind2string(ctx context.Context, sel ast.SelectionSet, v string) graphql.Marshaler {\n\tres := graphql.MarshalString(v)\n\tif res == graphql.Null {\n\t\tif !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {\n\t\t\tec.Errorf(ctx, \"the requested element is null which the schema does not allow\")\n\t\t}\n\t}\n\treturn res\n}\n\nfunc (ec *executionContext) unmarshalOBoolean2bool(ctx context.Context, v any) (bool, error) {\n\tres, err := graphql.UnmarshalBoolean(v)\n\treturn res, graphql.ErrorOnPath(ctx, err)\n}\n\nfunc (ec *executionContext) marshalOBoolean2bool(ctx context.Context, sel ast.SelectionSet, v bool) graphql.Marshaler {\n\tres := graphql.MarshalBoolean(v)\n\treturn res\n}\n\nfunc (ec *executionContext) unmarshalOBoolean2ᚖbool(ctx context.Context, v any) (*bool, error) {\n\tif v == nil {\n\t\treturn nil, nil\n\t}\n\tres, err := graphql.UnmarshalBoolean(v)\n\treturn &res, graphql.ErrorOnPath(ctx, err)\n}\n\nfunc (ec *executionContext) marshalOBoolean2ᚖbool(ctx context.Context, sel ast.SelectionSet, v *bool) graphql.Marshaler {\n\tif v == nil {\n\t\treturn graphql.Null\n\t}\n\tres := graphql.MarshalBoolean(*v)\n\treturn res\n}\n\nfunc (ec *executionContext) marshalOCharacter2ᚕᚖgithubᚗcomᚋirisᚑcontribᚋouterbanksᚑapiᚋgraphᚋgraphᚋmodelᚐCharacterᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.Character) graphql.Marshaler {\n\tif v == nil {\n\t\treturn graphql.Null\n\t}\n\tret := make(graphql.Array, len(v))\n\tvar wg sync.WaitGroup\n\tisLen1 := len(v) == 1\n\tif !isLen1 {\n\t\twg.Add(len(v))\n\t}\n\tfor i := range v {\n\t\ti := i\n\t\tfc := &graphql.FieldContext{\n\t\t\tIndex:  &i,\n\t\t\tResult: &v[i],\n\t\t}\n\t\tctx := graphql.WithFieldContext(ctx, fc)\n\t\tf := func(i int) {\n\t\t\tdefer func() {\n\t\t\t\tif r := recover(); r != nil {\n\t\t\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\t\t\tret = nil\n\t\t\t\t}\n\t\t\t}()\n\t\t\tif !isLen1 {\n\t\t\t\tdefer wg.Done()\n\t\t\t}\n\t\t\tret[i] = ec.marshalNCharacter2ᚖgithubᚗcomᚋirisᚑcontribᚋouterbanksᚑapiᚋgraphᚋgraphᚋmodelᚐCharacter(ctx, sel, v[i])\n\t\t}\n\t\tif isLen1 {\n\t\t\tf(i)\n\t\t} else {\n\t\t\tgo f(i)\n\t\t}\n\n\t}\n\twg.Wait()\n\n\tfor _, e := range ret {\n\t\tif e == graphql.Null {\n\t\t\treturn graphql.Null\n\t\t}\n\t}\n\n\treturn ret\n}\n\nfunc (ec *executionContext) marshalOCharacter2ᚖgithubᚗcomᚋirisᚑcontribᚋouterbanksᚑapiᚋgraphᚋgraphᚋmodelᚐCharacter(ctx context.Context, sel ast.SelectionSet, v *model.Character) graphql.Marshaler {\n\tif v == nil {\n\t\treturn graphql.Null\n\t}\n\treturn ec._Character(ctx, sel, v)\n}\n\nfunc (ec *executionContext) unmarshalOString2ᚖstring(ctx context.Context, v any) (*string, error) {\n\tif v == nil {\n\t\treturn nil, nil\n\t}\n\tres, err := graphql.UnmarshalString(v)\n\treturn &res, graphql.ErrorOnPath(ctx, err)\n}\n\nfunc (ec *executionContext) marshalOString2ᚖstring(ctx context.Context, sel ast.SelectionSet, v *string) graphql.Marshaler {\n\tif v == nil {\n\t\treturn graphql.Null\n\t}\n\tres := graphql.MarshalString(*v)\n\treturn res\n}\n\nfunc (ec *executionContext) marshalO__EnumValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐEnumValueᚄ(ctx context.Context, sel ast.SelectionSet, v []introspection.EnumValue) graphql.Marshaler {\n\tif v == nil {\n\t\treturn graphql.Null\n\t}\n\tret := make(graphql.Array, len(v))\n\tvar wg sync.WaitGroup\n\tisLen1 := len(v) == 1\n\tif !isLen1 {\n\t\twg.Add(len(v))\n\t}\n\tfor i := range v {\n\t\ti := i\n\t\tfc := &graphql.FieldContext{\n\t\t\tIndex:  &i,\n\t\t\tResult: &v[i],\n\t\t}\n\t\tctx := graphql.WithFieldContext(ctx, fc)\n\t\tf := func(i int) {\n\t\t\tdefer func() {\n\t\t\t\tif r := recover(); r != nil {\n\t\t\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\t\t\tret = nil\n\t\t\t\t}\n\t\t\t}()\n\t\t\tif !isLen1 {\n\t\t\t\tdefer wg.Done()\n\t\t\t}\n\t\t\tret[i] = ec.marshalN__EnumValue2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐEnumValue(ctx, sel, v[i])\n\t\t}\n\t\tif isLen1 {\n\t\t\tf(i)\n\t\t} else {\n\t\t\tgo f(i)\n\t\t}\n\n\t}\n\twg.Wait()\n\n\tfor _, e := range ret {\n\t\tif e == graphql.Null {\n\t\t\treturn graphql.Null\n\t\t}\n\t}\n\n\treturn ret\n}\n\nfunc (ec *executionContext) marshalO__Field2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐFieldᚄ(ctx context.Context, sel ast.SelectionSet, v []introspection.Field) graphql.Marshaler {\n\tif v == nil {\n\t\treturn graphql.Null\n\t}\n\tret := make(graphql.Array, len(v))\n\tvar wg sync.WaitGroup\n\tisLen1 := len(v) == 1\n\tif !isLen1 {\n\t\twg.Add(len(v))\n\t}\n\tfor i := range v {\n\t\ti := i\n\t\tfc := &graphql.FieldContext{\n\t\t\tIndex:  &i,\n\t\t\tResult: &v[i],\n\t\t}\n\t\tctx := graphql.WithFieldContext(ctx, fc)\n\t\tf := func(i int) {\n\t\t\tdefer func() {\n\t\t\t\tif r := recover(); r != nil {\n\t\t\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\t\t\tret = nil\n\t\t\t\t}\n\t\t\t}()\n\t\t\tif !isLen1 {\n\t\t\t\tdefer wg.Done()\n\t\t\t}\n\t\t\tret[i] = ec.marshalN__Field2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐField(ctx, sel, v[i])\n\t\t}\n\t\tif isLen1 {\n\t\t\tf(i)\n\t\t} else {\n\t\t\tgo f(i)\n\t\t}\n\n\t}\n\twg.Wait()\n\n\tfor _, e := range ret {\n\t\tif e == graphql.Null {\n\t\t\treturn graphql.Null\n\t\t}\n\t}\n\n\treturn ret\n}\n\nfunc (ec *executionContext) marshalO__InputValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValueᚄ(ctx context.Context, sel ast.SelectionSet, v []introspection.InputValue) graphql.Marshaler {\n\tif v == nil {\n\t\treturn graphql.Null\n\t}\n\tret := make(graphql.Array, len(v))\n\tvar wg sync.WaitGroup\n\tisLen1 := len(v) == 1\n\tif !isLen1 {\n\t\twg.Add(len(v))\n\t}\n\tfor i := range v {\n\t\ti := i\n\t\tfc := &graphql.FieldContext{\n\t\t\tIndex:  &i,\n\t\t\tResult: &v[i],\n\t\t}\n\t\tctx := graphql.WithFieldContext(ctx, fc)\n\t\tf := func(i int) {\n\t\t\tdefer func() {\n\t\t\t\tif r := recover(); r != nil {\n\t\t\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\t\t\tret = nil\n\t\t\t\t}\n\t\t\t}()\n\t\t\tif !isLen1 {\n\t\t\t\tdefer wg.Done()\n\t\t\t}\n\t\t\tret[i] = ec.marshalN__InputValue2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValue(ctx, sel, v[i])\n\t\t}\n\t\tif isLen1 {\n\t\t\tf(i)\n\t\t} else {\n\t\t\tgo f(i)\n\t\t}\n\n\t}\n\twg.Wait()\n\n\tfor _, e := range ret {\n\t\tif e == graphql.Null {\n\t\t\treturn graphql.Null\n\t\t}\n\t}\n\n\treturn ret\n}\n\nfunc (ec *executionContext) marshalO__Schema2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐSchema(ctx context.Context, sel ast.SelectionSet, v *introspection.Schema) graphql.Marshaler {\n\tif v == nil {\n\t\treturn graphql.Null\n\t}\n\treturn ec.___Schema(ctx, sel, v)\n}\n\nfunc (ec *executionContext) marshalO__Type2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐTypeᚄ(ctx context.Context, sel ast.SelectionSet, v []introspection.Type) graphql.Marshaler {\n\tif v == nil {\n\t\treturn graphql.Null\n\t}\n\tret := make(graphql.Array, len(v))\n\tvar wg sync.WaitGroup\n\tisLen1 := len(v) == 1\n\tif !isLen1 {\n\t\twg.Add(len(v))\n\t}\n\tfor i := range v {\n\t\ti := i\n\t\tfc := &graphql.FieldContext{\n\t\t\tIndex:  &i,\n\t\t\tResult: &v[i],\n\t\t}\n\t\tctx := graphql.WithFieldContext(ctx, fc)\n\t\tf := func(i int) {\n\t\t\tdefer func() {\n\t\t\t\tif r := recover(); r != nil {\n\t\t\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\t\t\tret = nil\n\t\t\t\t}\n\t\t\t}()\n\t\t\tif !isLen1 {\n\t\t\t\tdefer wg.Done()\n\t\t\t}\n\t\t\tret[i] = ec.marshalN__Type2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, sel, v[i])\n\t\t}\n\t\tif isLen1 {\n\t\t\tf(i)\n\t\t} else {\n\t\t\tgo f(i)\n\t\t}\n\n\t}\n\twg.Wait()\n\n\tfor _, e := range ret {\n\t\tif e == graphql.Null {\n\t\t\treturn graphql.Null\n\t\t}\n\t}\n\n\treturn ret\n}\n\nfunc (ec *executionContext) marshalO__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx context.Context, sel ast.SelectionSet, v *introspection.Type) graphql.Marshaler {\n\tif v == nil {\n\t\treturn graphql.Null\n\t}\n\treturn ec.___Type(ctx, sel, v)\n}\n\n// endregion ***************************** type.gotpl *****************************\n"
  },
  {
    "path": "_examples/graphql/schema-first/graph/model/models_gen.go",
    "content": "// Code generated by github.com/99designs/gqlgen, DO NOT EDIT.\n\npackage model\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"strconv\"\n)\n\ntype Character struct {\n\tID         string     `json:\"id\"`\n\tName       string     `json:\"name\"`\n\tIsHero     bool       `json:\"isHero\"`\n\tCliqueType CliqueType `json:\"cliqueType\"`\n}\n\ntype CharacterInput struct {\n\tName       string     `json:\"name\"`\n\tID         *string    `json:\"id\"`\n\tIsHero     *bool      `json:\"isHero\"`\n\tCliqueType CliqueType `json:\"cliqueType\"`\n}\n\ntype CliqueType string\n\nconst (\n\t// People who are elite with parents having money\n\tCliqueTypeKooks CliqueType = \"KOOKS\"\n\t// People who desperate to move up the social ladder to become new versions of themselves and establish new beginnings\n\tCliqueTypePogues CliqueType = \"POGUES\"\n)\n\nvar AllCliqueType = []CliqueType{\n\tCliqueTypeKooks,\n\tCliqueTypePogues,\n}\n\nfunc (e CliqueType) IsValid() bool {\n\tswitch e {\n\tcase CliqueTypeKooks, CliqueTypePogues:\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc (e CliqueType) String() string {\n\treturn string(e)\n}\n\nfunc (e *CliqueType) UnmarshalGQL(v any) error {\n\tstr, ok := v.(string)\n\tif !ok {\n\t\treturn fmt.Errorf(\"enums must be strings\")\n\t}\n\n\t*e = CliqueType(str)\n\tif !e.IsValid() {\n\t\treturn fmt.Errorf(\"%s is not a valid CliqueType\", str)\n\t}\n\treturn nil\n}\n\nfunc (e CliqueType) MarshalGQL(w io.Writer) {\n\tfmt.Fprint(w, strconv.Quote(e.String()))\n}\n"
  },
  {
    "path": "_examples/graphql/schema-first/graph/resolver.go",
    "content": "package graph\n\nimport \"github.com/iris-contrib/outerbanks-api/graph/model\"\n\n// This file will not be regenerated automatically.\n//\n// It serves as dependency injection for your app, add any dependencies you require here.\n\ntype Resolver struct {\n\tCharacterStore map[string]model.Character\n}\n"
  },
  {
    "path": "_examples/graphql/schema-first/graph/schema.graphqls",
    "content": "# GraphQL schema example\n#\n# https://gqlgen.com/getting-started/\n\nenum CliqueType {\n  \"People who are elite with parents having money\"\n  KOOKS\n  \"People who desperate to move up the social ladder to become new versions of themselves and establish new beginnings\"\n  POGUES\n}\n\ntype Character {\n  id: ID!\n  name: String!\n  isHero: Boolean!\n  cliqueType: CliqueType!\n}\n\ninput CharacterInput {\n  name: String!\n  id: String\n  isHero: Boolean\n  cliqueType: CliqueType!\n}\n\ntype Mutation {\n  upsertCharacter(input: CharacterInput!): Character!\n}\n\ntype Query {\n  character(id:ID!): Character\n  characters(cliqueType:CliqueType!): [Character!]\n}"
  },
  {
    "path": "_examples/graphql/schema-first/graph/schema.resolvers.go",
    "content": "package graph\n\n// This file will be automatically regenerated based on the schema, any resolver implementations\n// will be copied through when generating and any unknown code will be moved to the end.\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"strconv\"\n\n\t\"github.com/iris-contrib/outerbanks-api/graph/model\"\n)\n\nfunc (r *mutationResolver) UpsertCharacter(ctx context.Context, input model.CharacterInput) (*model.Character, error) {\n\tid := input.ID\n\tvar character model.Character\n\tcharacter.Name = input.Name\n\tcharacter.CliqueType = input.CliqueType\n\n\tn := len(r.Resolver.CharacterStore)\n\tif n == 0 {\n\t\tr.Resolver.CharacterStore = make(map[string]model.Character)\n\t}\n\n\tif id != nil {\n\t\tcs, ok := r.Resolver.CharacterStore[*id]\n\t\tif !ok {\n\t\t\treturn nil, fmt.Errorf(\"not found\")\n\t\t}\n\t\tif input.IsHero != nil {\n\t\t\tcharacter.IsHero = *input.IsHero\n\t\t} else {\n\t\t\tcharacter.IsHero = cs.IsHero\n\t\t}\n\t\tr.Resolver.CharacterStore[*id] = character\n\t} else {\n\t\t// generate unique id\n\t\tnid := strconv.Itoa(n + 1)\n\t\tcharacter.ID = nid\n\t\tif input.IsHero != nil {\n\t\t\tcharacter.IsHero = *input.IsHero\n\t\t}\n\t\tr.Resolver.CharacterStore[nid] = character\n\t}\n\n\treturn &character, nil\n}\n\nfunc (r *queryResolver) Character(ctx context.Context, id string) (*model.Character, error) {\n\tcharacter, ok := r.Resolver.CharacterStore[id]\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"not found\")\n\t}\n\treturn &character, nil\n}\n\nfunc (r *queryResolver) Characters(ctx context.Context, cliqueType model.CliqueType) ([]*model.Character, error) {\n\tcharacters := make([]*model.Character, 0)\n\tfor idx := range r.Resolver.CharacterStore {\n\t\tcharacter := r.Resolver.CharacterStore[idx]\n\t\tif character.CliqueType == cliqueType {\n\n\t\t\tcharacters = append(characters, &character)\n\t\t}\n\t}\n\n\treturn characters, nil\n}\n\n// Mutation returns generated.MutationResolver implementation.\nfunc (r *Resolver) Mutation() MutationResolver { return &mutationResolver{r} }\n\n// Query returns generated.QueryResolver implementation.\nfunc (r *Resolver) Query() QueryResolver { return &queryResolver{r} }\n\ntype mutationResolver struct{ *Resolver }\ntype queryResolver struct{ *Resolver }\n"
  },
  {
    "path": "_examples/graphql/schema-first/server.go",
    "content": "package main\n\nimport (\n\t\"os\"\n\n\t\"github.com/iris-contrib/outerbanks-api/graph\"\n\n\t\"github.com/99designs/gqlgen/graphql/handler\"\n\t\"github.com/99designs/gqlgen/graphql/playground\"\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\tgraphServer := handler.NewDefaultServer(graph.NewExecutableSchema(graph.Config{Resolvers: &graph.Resolver{}}))\n\tplaygroundHandler := playground.Handler(\"GraphQL playground\", \"/query\")\n\n\tapp.Get(\"/\", iris.FromStd(playgroundHandler))          // We use iris.FromStd to convert a standard http.Handler to an iris.Handler.\n\tapp.Any(\"/query\", iris.FromStd(graphServer.ServeHTTP)) // GET, POST, PUT...\n\n\tport := os.Getenv(\"PORT\")\n\tif port == \"\" {\n\t\tport = \"8080\"\n\t}\n\tapp.Listen(\":\" + port)\n}\n"
  },
  {
    "path": "_examples/graphql/schema-first/tools.go",
    "content": "//go:build tools\n// +build tools\n\npackage tools\n\nimport _ \"github.com/99designs/gqlgen\"\n"
  },
  {
    "path": "_examples/http-client/weatherapi/client/client.go",
    "content": "package client\n\nimport (\n\t\"context\"\n\t\"net/url\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/x/client\"\n)\n\n// The BaseURL of our API client.\nconst BaseURL = \"https://api.weatherapi.com/v1\"\n\ntype (\n\tOptions struct {\n\t\tAPIKey string `json:\"api_key\" yaml:\"APIKey\" toml:\"APIKey\"`\n\t}\n\n\tClient struct {\n\t\t*client.Client\n\t}\n)\n\nfunc NewClient(opts Options) *Client {\n\tapiKeyParameterSetter := client.RequestParam(\"key\", opts.APIKey)\n\n\tc := client.New(\n\t\tclient.Debug,\n\t\tclient.BaseURL(BaseURL),\n\t\tclient.PersistentRequestOptions(apiKeyParameterSetter),\n\t)\n\n\treturn &Client{c}\n}\n\nfunc (c *Client) GetCurrentByCity(ctx context.Context, city string) (resp Response, err error) {\n\turlpath := \"/current.json\"\n\t// ?q=Athens&aqi=no\n\tparams := client.RequestQuery(url.Values{\n\t\t\"q\":   []string{city},\n\t\t\"aqi\": []string{\"no\"},\n\t})\n\n\terr = c.Client.ReadJSON(ctx, &resp, iris.MethodGet, urlpath, nil, params)\n\treturn\n}\n"
  },
  {
    "path": "_examples/http-client/weatherapi/client/response.go",
    "content": "package client\n\ntype Response struct {\n\tLocation struct {\n\t\tName           string  `json:\"name\"`\n\t\tRegion         string  `json:\"region\"`\n\t\tCountry        string  `json:\"country\"`\n\t\tLat            float64 `json:\"lat\"`\n\t\tLon            float64 `json:\"lon\"`\n\t\tTzID           string  `json:\"tz_id\"`\n\t\tLocaltimeEpoch int     `json:\"localtime_epoch\"`\n\t\tLocaltime      string  `json:\"localtime\"`\n\t} `json:\"location\"`\n\tCurrent struct {\n\t\tLastUpdatedEpoch int     `json:\"last_updated_epoch\"`\n\t\tLastUpdated      string  `json:\"last_updated\"`\n\t\tTempC            float64 `json:\"temp_c\"`\n\t\tTempF            float64 `json:\"temp_f\"`\n\t\tIsDay            int     `json:\"is_day\"`\n\t\tCondition        struct {\n\t\t\tText string `json:\"text\"`\n\t\t\tIcon string `json:\"icon\"`\n\t\t\tCode int    `json:\"code\"`\n\t\t} `json:\"condition\"`\n\t\tWindMph    float64 `json:\"wind_mph\"`\n\t\tWindKph    float64 `json:\"wind_kph\"`\n\t\tWindDegree int     `json:\"wind_degree\"`\n\t\tWindDir    string  `json:\"wind_dir\"`\n\t\tPressureMb float64 `json:\"pressure_mb\"`\n\t\tPressureIn float64 `json:\"pressure_in\"`\n\t\tPrecipMm   float64 `json:\"precip_mm\"`\n\t\tPrecipIn   float64 `json:\"precip_in\"`\n\t\tHumidity   int     `json:\"humidity\"`\n\t\tCloud      int     `json:\"cloud\"`\n\t\tFeelslikeC float64 `json:\"feelslike_c\"`\n\t\tFeelslikeF float64 `json:\"feelslike_f\"`\n\t\tVisKm      float64 `json:\"vis_km\"`\n\t\tVisMiles   float64 `json:\"vis_miles\"`\n\t\tUv         float64 `json:\"uv\"`\n\t\tGustMph    float64 `json:\"gust_mph\"`\n\t\tGustKph    float64 `json:\"gust_kph\"`\n\t} `json:\"current\"`\n}\n"
  },
  {
    "path": "_examples/http-client/weatherapi/main.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12/_examples/http-client/weatherapi/client\"\n)\n\nfunc main() {\n\tc := client.NewClient(client.Options{\n\t\tAPIKey: \"{YOUR_API_KEY_HERE}\",\n\t})\n\n\tresp, err := c.GetCurrentByCity(context.Background(), \"Xanthi/GR\")\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Printf(\"Temp: %.2f(C), %.2f(F)\\n\", resp.Current.TempC, resp.Current.TempF)\n}\n"
  },
  {
    "path": "_examples/http-server/README.md",
    "content": "# Hosts\n\n## Listen and Serve\n\nYou can start the server(s) listening to any type of `net.Listener` or even `http.Server` instance.\nThe method for initialization of the server should be passed at the end, via `Run` function.\n\nThe most common method that Go developers use to serve their servers are\nby passing a network address with form of \"hostname:ip\". With Iris\nwe use the `iris.Addr` which is an `iris.Runner` type\n\n```go\n// Listening on tcp with network address 0.0.0.0:8080\n// app.Listen(\":8080\") is just a shortcut of:\napp.Run(iris.Addr(\":8080\"))\n```\n\nSometimes you have created a standard net/http server somewhere else in your app and want to use that to serve the Iris web app\n\n```go\n// Same as before but using a custom http.Server which may being used somewhere else too\napp.Run(iris.Server(&http.Server{Addr:\":8080\"}))\n```\n\nThe most advanced usage is to create a custom or a standard `net.Listener` and pass that to `app.Run`\n\n```go\n// Using a custom net.Listener\nl, err := net.Listen(\"tcp4\", \":8080\")\nif err != nil {\n    panic(err)\n}\napp.Run(iris.Listener(l))\n```\n\nA more complete example, using the unix-only socket files feature \n\n```go\npackage main\n\nimport (\n    \"os\"\n    \"net\"\n\n    \"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n    app := iris.New()\n\n    // UNIX socket\n    if errOs := os.Remove(socketFile); errOs != nil && !os.IsNotExist(errOs) {\n        app.Logger().Fatal(errOs)\n    }\n\n    l, err := net.Listen(\"unix\", socketFile)\n\n    if err != nil {\n        app.Logger().Fatal(err)\n    }\n\n    if err = os.Chmod(socketFile, mode); err != nil {\n        app.Logger().Fatal(err)\n    }\n\n    app.Run(iris.Listener(l))\n}\n```\n\n### HTTP/2 and Secure\n\nIf you have signed file keys you can use the `iris.TLS` to serve `https` based on those certification keys\n\n```go\n// TLS using files\napp.Run(iris.TLS(\"127.0.0.1:443\", \"mycert.cert\", \"mykey.key\"))\n```\n\nThe method you should use when your app is ready for **production** is the `iris.AutoTLS` which starts a secure server with automated certifications provided by https://letsencrypt.org for **free**\n\n```go\n// Automatic TLS\napp.Run(iris.AutoTLS(\":443\", \"example.com\", \"admin@example.com\"))\n```\n\n### Any `iris.Runner`\n\nThere may be times that you want something very special to listen on, which is not a type of `net.Listener`. You are able to do that by `iris.Raw`, but you're responsible of that method\n\n```go\n// Using any func() error,\n// the responsibility of starting up a listener is up to you with this way,\n// for the sake of simplicity we will use the\n// ListenAndServe function of the `net/http` package.\napp.Run(iris.Raw(&http.Server{Addr:\":8080\"}).ListenAndServe)\n```\n\n## Host configurators\n\nAll the above forms of listening are accepting a last, variadic argument of `func(*iris.Supervisor)`. This is used to add configurators for that specific host you passed via those functions.\n\nFor example let's say that we want to add a callback which is fired when\nthe server is shutdown\n\n```go\napp.Run(iris.Addr(\":8080\", func(h *iris.Supervisor) {\n    h.RegisterOnShutdown(func() {\n        println(\"server terminated\")\n    })\n}))\n```\n\nYou can even do that before `app.Run` method, but the difference is that\nthese host configurators will be executed to all hosts that you may use to serve your web app (via `app.NewHost` we'll see that in a minute)\n\n```go\napp := iris.New()\napp.ConfigureHost(func(h *iris.Supervisor) {\n    h.RegisterOnShutdown(func() {\n        println(\"server terminated\")\n    })\n})\napp.Listen(\":8080\")\n```\n\nAccess to all hosts that serve your application can be provided by\nthe `Application#Hosts` field, after the `Run` method.\n\nBut the most common scenario is that you may need access to the host before the `app.Run` method,\nthere are two ways of gain access to the host supervisor, read below.\n\nWe have already saw how to configure all application's hosts by second argument of `app.Run` or `app.ConfigureHost`. There is one more way which suits better for simple scenarios and that is to use the `app.NewHost` to create a new host\nand use one of its `Serve` or `Listen` functions\nto start the application via the `iris#Raw` Runner.\n\nNote that this way needs an extra import of the `net/http` package.\n\nExample Code:\n\n```go\nh := app.NewHost(&http.Server{Addr:\":8080\"})\nh.RegisterOnShutdown(func(){\n    println(\"server terminated\")\n})\n\napp.Run(iris.Raw(h.ListenAndServe))\n```\n\n## Multi hosts\n\nYou can serve your Iris web app using more than one server, the `iris.Router` is compatible with the `net/http/Handler` function therefore, as you can understand, it can be used to be adapted at any `net/http` server, however there is an easier way, by using the `app.NewHost` which is also copying all the host configurators and it closes all the hosts attached to the particular web app on `app.Shutdown`.\n\n```go\napp := iris.New()\napp.Get(\"/\", indexHandler)\n\n// run in different goroutine in order to not block the main \"goroutine\".\ngo app.Listen(\":8080\")\n// start a second server which is listening on tcp 0.0.0.0:9090,\n// without \"go\" keyword because we want to block at the last server-run.\napp.NewHost(&http.Server{Addr:\":9090\"}).ListenAndServe()\n```\n\n## Shutdown (Gracefully)\n\nLet's continue by learning how to catch CONTROL+C/COMMAND+C or unix kill command and shutdown the server gracefully.\n\n> Gracefully Shutdown on CONTROL+C/COMMAND+C or when kill command sent is ENABLED BY-DEFAULT.\n\nIn order to manually manage what to do when app is interrupted,\nwe have to disable the default behavior with the option `WithoutInterruptHandler`\nand register a new interrupt handler (globally, across all possible hosts).\n\n\nExample code:\n\n```go\npackage main\n\nimport (\n    \"context\"\n    \"time\"\n\n    \"github.com/kataras/iris/v12\"\n)\n\n\nfunc main() {\n    app := iris.New()\n\n    iris.RegisterOnInterrupt(func() {\n        timeout := 10 * time.Second\n        ctx, cancel := context.WithTimeout(context.Background(), timeout)\n        defer cancel()\n        // close all hosts\n        app.Shutdown(ctx)\n    })\n\n    app.Get(\"/\", func(ctx iris.Context) {\n        ctx.HTML(\" <h1>hi, I just exist in order to see if the server is closed</h1>\")\n    })\n\n    app.Listen(\":8080\", iris.WithoutInterruptHandler)\n}\n```\n"
  },
  {
    "path": "_examples/http-server/custom-httpserver/easy-way/main.go",
    "content": "package main\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.Writef(\"Hello from the server\")\n\t})\n\n\tapp.Get(\"/mypath\", func(ctx iris.Context) {\n\t\tctx.Writef(\"Hello from %s\", ctx.Path())\n\t})\n\n\t// Any custom fields here. Handler and ErrorLog are set to the server automatically\n\tsrv := &http.Server{Addr: \":8080\"}\n\n\t// http://localhost:8080/\n\t// http://localhost:8080/mypath\n\tapp.Run(iris.Server(srv)) // same as app.Listen(\":8080\")\n\n\t// More:\n\t// see \"multi\" if you need to use more than one server at the same app.\n\t//\n\t// for a custom listener use: iris.Listener(net.Listener) or\n\t// iris.TLS(cert,key) or iris.AutoTLS(), see \"custom-listener\" example for those.\n}\n"
  },
  {
    "path": "_examples/http-server/custom-httpserver/multi/main.go",
    "content": "package main\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.Writef(\"Hello from the server\")\n\t})\n\n\tapp.Get(\"/mypath\", func(ctx iris.Context) {\n\t\tctx.Writef(\"Hello from %s\", ctx.Path())\n\t})\n\n\t// Note: It's not needed if the first action is \"go app.Run\".\n\tif err := app.Build(); err != nil {\n\t\tpanic(err)\n\t}\n\n\t// start a secondary server listening on localhost:9090.\n\t// use \"go\" keyword for Listen functions if you need to use more than one server at the same app.\n\t//\n\t// http://localhost:9090/\n\t// http://localhost:9090/mypath\n\tsrv1 := &http.Server{Addr: \":9090\", Handler: app}\n\tgo srv1.ListenAndServe()\n\tprintln(\"Start a server listening on http://localhost:9090\")\n\n\t// start a \"second-secondary\" server listening on localhost:5050.\n\t//\n\t// http://localhost:5050/\n\t// http://localhost:5050/mypath\n\tsrv2 := &http.Server{Addr: \":5050\", Handler: app}\n\tgo srv2.ListenAndServe()\n\tprintln(\"Start a server listening on http://localhost:5050\")\n\n\t// Note: app.Run is totally optional, we have already built the app with app.Build,\n\t// you can just make a new http.Server instead.\n\t// http://localhost:8080/\n\t// http://localhost:8080/mypath\n\tapp.Listen(\":8080\") // Block here.\n}\n"
  },
  {
    "path": "_examples/http-server/custom-httpserver/std-way/main.go",
    "content": "package main\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.Writef(\"Hello from the server\")\n\t})\n\n\tapp.Get(\"/mypath\", func(ctx iris.Context) {\n\t\tctx.Writef(\"Hello from %s\", ctx.Path())\n\t})\n\n\t// call .Build before use the 'app' as a http.Handler on a custom http.Server\n\tapp.Build()\n\n\t// create our custom server and assign the Handler/Router\n\tsrv := &http.Server{Handler: app, Addr: \":8080\"} // you have to set Handler:app and Addr, see \"iris-way\" which does this automatically.\n\t// http://localhost:8080/\n\t// http://localhost:8080/mypath\n\tprintln(\"Start a server listening on http://localhost:8080\")\n\tsrv.ListenAndServe() // same as app.Listen(\":8080\")\n\n\t// Notes:\n\t// Banner is not shown at all. Same for the Interrupt Handler, even if app's configuration allows them.\n\t//\n\t// `.Run` is the only one function that cares about those three.\n\n\t// More:\n\t// see \"multi\" if you need to use more than one server at the same app.\n\t//\n\t// for a custom listener use: iris.Listener(net.Listener) or\n\t// iris.TLS(cert,key) or iris.AutoTLS(), see \"custom-listener\" example for those.\n}\n"
  },
  {
    "path": "_examples/http-server/custom-listener/main.go",
    "content": "package main\n\nimport (\n\t\"net\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.Writef(\"Hello from the server\")\n\t})\n\n\tapp.Get(\"/mypath\", func(ctx iris.Context) {\n\t\tctx.Writef(\"Hello from %s\", ctx.Path())\n\t})\n\n\t// create any custom tcp listener, unix sock file or tls tcp listener.\n\tl, err := net.Listen(\"tcp4\", \":8080\")\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// use of the custom listener\n\tapp.Run(iris.Listener(l))\n}\n"
  },
  {
    "path": "_examples/http-server/graceful-shutdown/custom-notifier/main.go",
    "content": "package main\n\nimport (\n\tstdContext \"context\"\n\t\"os\"\n\t\"os/signal\"\n\t\"syscall\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.HTML(\"<h1>hi, I just exist in order to see if the server is closed</h1>\")\n\t})\n\n\tidleConnsClosed := make(chan struct{})\n\tgo func() {\n\t\tch := make(chan os.Signal, 1)\n\t\tsignal.Notify(ch,\n\t\t\t// kill -SIGINT XXXX or Ctrl+c\n\t\t\tos.Interrupt,\n\t\t\tsyscall.SIGINT, // register that too, it should be ok\n\t\t\t// os.Kill  is equivalent with the syscall.Kill\n\t\t\tos.Kill,\n\t\t\tsyscall.SIGKILL, // register that too, it should be ok\n\t\t\t// kill -SIGTERM XXXX\n\t\t\tsyscall.SIGTERM,\n\t\t)\n\t\tselect {\n\t\tcase <-ch:\n\t\t\tprintln(\"shutdown...\")\n\n\t\t\ttimeout := 10 * time.Second\n\t\t\tctx, cancel := stdContext.WithTimeout(stdContext.Background(), timeout)\n\t\t\tdefer cancel()\n\t\t\tapp.Shutdown(ctx)\n\t\t\tclose(idleConnsClosed)\n\t\t}\n\t}()\n\n\t// Start the server and disable the default interrupt handler in order to\n\t// handle it clear and simple by our own, without any issues.\n\tapp.Listen(\":8080\", iris.WithoutInterruptHandler)\n\t<-idleConnsClosed\n}\n"
  },
  {
    "path": "_examples/http-server/graceful-shutdown/default-notifier/main.go",
    "content": "package main\n\nimport (\n\tstdContext \"context\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\n// Before continue:\n//\n// Gracefully Shutdown on control+C/command+C or when kill command sent is ENABLED BY-DEFAULT.\n//\n// In order to manually manage what to do when app is interrupted,\n// We have to disable the default behavior with the option `WithoutInterruptHandler`\n// and register a new interrupt handler (globally, across all possible hosts).\nfunc main() {\n\tapp := iris.New()\n\n\tidleConnsClosed := make(chan struct{})\n\tiris.RegisterOnInterrupt(func() {\n\t\ttimeout := 10 * time.Second\n\t\tctx, cancel := stdContext.WithTimeout(stdContext.Background(), timeout)\n\t\tdefer cancel()\n\t\t// close all hosts\n\t\tapp.Shutdown(ctx)\n\t\tclose(idleConnsClosed)\n\t})\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.HTML(\" <h1>hi, I just exist in order to see if the server is closed</h1>\")\n\t})\n\n\t// http://localhost:8080\n\tapp.Listen(\":8080\", iris.WithoutInterruptHandler, iris.WithoutServerError(iris.ErrServerClosed))\n\t<-idleConnsClosed\n}\n"
  },
  {
    "path": "_examples/http-server/h2c/go.mod",
    "content": "module github.com/kataras/iris/_examples/http-server/h2c\n\ngo 1.25\n\nrequire (\n\tgithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd\n\tgolang.org/x/net v0.49.0\n)\n\nrequire (\n\tgithub.com/BurntSushi/toml v1.6.0 // indirect\n\tgithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect\n\tgithub.com/CloudyKit/jet/v6 v6.3.1 // indirect\n\tgithub.com/Joker/jade v1.1.3 // indirect\n\tgithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 // indirect\n\tgithub.com/andybalholm/brotli v1.2.0 // indirect\n\tgithub.com/aymerick/douceur v0.2.0 // indirect\n\tgithub.com/fatih/structs v1.1.0 // indirect\n\tgithub.com/flosch/pongo2/v4 v4.0.2 // indirect\n\tgithub.com/golang/snappy v1.0.0 // indirect\n\tgithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a // indirect\n\tgithub.com/google/uuid v1.6.0 // indirect\n\tgithub.com/gorilla/css v1.0.1 // indirect\n\tgithub.com/iris-contrib/schema v0.0.6 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/kataras/blocks v0.0.8 // indirect\n\tgithub.com/kataras/golog v0.1.12 // indirect\n\tgithub.com/kataras/pio v0.0.13 // indirect\n\tgithub.com/kataras/sitemap v0.0.6 // indirect\n\tgithub.com/kataras/tunnel v0.0.4 // indirect\n\tgithub.com/klauspost/compress v1.18.2 // indirect\n\tgithub.com/kr/pretty v0.3.1 // indirect\n\tgithub.com/mailgun/raymond/v2 v2.0.48 // indirect\n\tgithub.com/mailru/easyjson v0.9.1 // indirect\n\tgithub.com/microcosm-cc/bluemonday v1.0.27 // indirect\n\tgithub.com/rogpeppe/go-internal v1.10.0 // indirect\n\tgithub.com/russross/blackfriday/v2 v2.1.0 // indirect\n\tgithub.com/schollz/closestmatch v2.1.0+incompatible // indirect\n\tgithub.com/sirupsen/logrus v1.8.1 // indirect\n\tgithub.com/stretchr/testify v1.11.1 // indirect\n\tgithub.com/tdewolff/minify/v2 v2.24.8 // indirect\n\tgithub.com/tdewolff/parse/v2 v2.8.5 // indirect\n\tgithub.com/valyala/bytebufferpool v1.0.0 // indirect\n\tgithub.com/vmihailenco/msgpack/v5 v5.4.1 // indirect\n\tgithub.com/vmihailenco/tagparser/v2 v2.0.0 // indirect\n\tgithub.com/yosssi/ace v0.0.5 // indirect\n\tgolang.org/x/crypto v0.47.0 // indirect\n\tgolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 // indirect\n\tgolang.org/x/sys v0.40.0 // indirect\n\tgolang.org/x/text v0.33.0 // indirect\n\tgolang.org/x/time v0.14.0 // indirect\n\tgoogle.golang.org/protobuf v1.36.11 // indirect\n\tgopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect\n\tgopkg.in/ini.v1 v1.67.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n"
  },
  {
    "path": "_examples/http-server/h2c/go.sum",
    "content": "github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=\ngithub.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=\ngithub.com/CloudyKit/jet/v6 v6.3.1 h1:6IAo5Cx21xrHVaR8zzXN5gJatKV/wO7Nf6bfCnCSbUw=\ngithub.com/CloudyKit/jet/v6 v6.3.1/go.mod h1:lf8ksdNsxZt7/yH/3n4vJQWA9RUq4wpaHtArHhGVMOw=\ngithub.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc=\ngithub.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=\ngithub.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=\ngithub.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 h1:dG7/gLroJGht/jSQtHiLvT48Hxn+crbmvyItZC8cWOs=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05/go.mod h1:NYezi6wtnJtBm5btoprXc5SvAdqH0XTXWnUup0MptAI=\ngithub.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=\ngithub.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=\ngithub.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=\ngithub.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=\ngithub.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=\ngithub.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=\ngithub.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=\ngithub.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=\ngithub.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=\ngithub.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw=\ngithub.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8=\ngithub.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=\ngithub.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=\ngithub.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=\ngithub.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a h1:l7A0loSszR5zHd/qK53ZIHMO8b3bBSmENnQ6eKnUT0A=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=\ngithub.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=\ngithub.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=\ngithub.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=\ngithub.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=\ngithub.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2 h1:T9THsdP1woyAqKHwjkEsbCnMefsAFvk8iJJKokcJ3Go=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2/go.mod h1:JLDgIqnFy5loDSUv1OA2j0mb6p/rDhiCqigP22Uq9xE=\ngithub.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw=\ngithub.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/kataras/blocks v0.0.8 h1:MrpVhoFTCR2v1iOOfGng5VJSILKeZZI+7NGfxEh3SUM=\ngithub.com/kataras/blocks v0.0.8/go.mod h1:9Jm5zx6BB+06NwA+OhTbHW1xkMOYxahnqTN5DveZ2Yg=\ngithub.com/kataras/golog v0.1.12 h1:Bu7I/G4ilJlbfzjmU39O9N+2uO1pBcMK045fzZ4ytNg=\ngithub.com/kataras/golog v0.1.12/go.mod h1:wrGSbOiBqbQSQznleVNX4epWM8rl9SJ/rmEacl0yqy4=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd h1:oMsQgqKACbUdlaIWnN+EZzzAnHkw90hE/y/R0BSij2U=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd/go.mod h1:yucjtDl9Vn3OctWM1fi0MuM9OckemC7kEreFa9QtPF8=\ngithub.com/kataras/pio v0.0.13 h1:x0rXVX0fviDTXOOLOmr4MUxOabu1InVSTu5itF8CXCM=\ngithub.com/kataras/pio v0.0.13/go.mod h1:k3HNuSw+eJ8Pm2lA4lRhg3DiCjVgHlP8hmXApSej3oM=\ngithub.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY=\ngithub.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4=\ngithub.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA=\ngithub.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw=\ngithub.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=\ngithub.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=\ngithub.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=\ngithub.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8=\ngithub.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=\ngithub.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=\ngithub.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=\ngithub.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=\ngithub.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=\ngithub.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=\ngithub.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=\ngithub.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=\ngithub.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=\ngithub.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=\ngithub.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=\ngithub.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=\ngithub.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk=\ngithub.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=\ngithub.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=\ngithub.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=\ngithub.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=\ngithub.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/tdewolff/minify/v2 v2.24.8 h1:58/VjsbevI4d5FGV0ZSuBrHMSSkH4MCH0sIz/eKIauE=\ngithub.com/tdewolff/minify/v2 v2.24.8/go.mod h1:0Ukj0CRpo/sW/nd8uZ4ccXaV1rEVIWA3dj8U7+Shhfw=\ngithub.com/tdewolff/parse/v2 v2.8.5 h1:ZmBiA/8Do5Rpk7bDye0jbbDUpXXbCdc3iah4VeUvwYU=\ngithub.com/tdewolff/parse/v2 v2.8.5/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=\ngithub.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE=\ngithub.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=\ngithub.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=\ngithub.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=\ngithub.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=\ngithub.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=\ngithub.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=\ngithub.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=\ngithub.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA=\ngithub.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0=\ngithub.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=\ngithub.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=\ngithub.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=\ngolang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=\ngolang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=\ngolang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=\ngolang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=\ngolang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=\ngolang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=\ngolang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=\ngolang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=\ngoogle.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=\ngopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nmoul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs=\nmoul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE=\n"
  },
  {
    "path": "_examples/http-server/h2c/main.go",
    "content": "package main\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/kataras/iris/v12\"\n\n\t\"golang.org/x/net/http2\"\n)\n\n/*\n\tServe HTTP/2 without TLS and keep support for HTTP/1.1\n*/\n\n// $ go get golang.org/x/net/http2\n// # Take a look at the golang.org/x/net/http2/h2c package as well,\n// # you may want to use it.\n// $ go run main.go\n// $ brew install curl-openssl\n// # Add curl-openssl to the front of your path.\n// Test with the following commands:\n// $ curl -v --http2 http://localhost:8080\n// $ curl -v --http1.1 http://localhost:8080\nfunc main() {\n\t// Initialize Iris Application.\n\tapp := iris.New()\n\n\t// Build the API.\n\tapp.Any(\"/\", index)\n\n\t// Finally, listen and serve on port 8080 using h2c.\n\tapp.Run(iris.Raw(func() error {\n\t\thost := app.NewHost(&http.Server{\n\t\t\tAddr: \":8080\",\n\t\t})\n\n\t\terr := http2.ConfigureServer(host.Server, &http2.Server{\n\t\t\tMaxConcurrentStreams:         250,\n\t\t\tPermitProhibitedCipherSuites: true,\n\t\t})\n\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\treturn host.ListenAndServe()\n\t}))\n}\n\nfunc index(ctx iris.Context) {\n\tctx.WriteString(\"Hello, World!\\n\")\n}\n"
  },
  {
    "path": "_examples/http-server/http3-quic/go.mod",
    "content": "module github.com/kataras/iris/_examples/http-server/http3-quic\n\ngo 1.25\n\nrequire (\n\tgithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd\n\tgithub.com/quic-go/quic-go v0.59.0\n)\n\nrequire (\n\tgithub.com/BurntSushi/toml v1.6.0 // indirect\n\tgithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect\n\tgithub.com/CloudyKit/jet/v6 v6.3.1 // indirect\n\tgithub.com/Joker/jade v1.1.3 // indirect\n\tgithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 // indirect\n\tgithub.com/andybalholm/brotli v1.2.0 // indirect\n\tgithub.com/aymerick/douceur v0.2.0 // indirect\n\tgithub.com/fatih/structs v1.1.0 // indirect\n\tgithub.com/flosch/pongo2/v4 v4.0.2 // indirect\n\tgithub.com/golang/snappy v1.0.0 // indirect\n\tgithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a // indirect\n\tgithub.com/google/uuid v1.6.0 // indirect\n\tgithub.com/gorilla/css v1.0.1 // indirect\n\tgithub.com/iris-contrib/schema v0.0.6 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/kataras/blocks v0.0.8 // indirect\n\tgithub.com/kataras/golog v0.1.12 // indirect\n\tgithub.com/kataras/pio v0.0.13 // indirect\n\tgithub.com/kataras/sitemap v0.0.6 // indirect\n\tgithub.com/kataras/tunnel v0.0.4 // indirect\n\tgithub.com/klauspost/compress v1.18.2 // indirect\n\tgithub.com/mailgun/raymond/v2 v2.0.48 // indirect\n\tgithub.com/mailru/easyjson v0.9.1 // indirect\n\tgithub.com/microcosm-cc/bluemonday v1.0.27 // indirect\n\tgithub.com/quic-go/qpack v0.6.0 // indirect\n\tgithub.com/russross/blackfriday/v2 v2.1.0 // indirect\n\tgithub.com/schollz/closestmatch v2.1.0+incompatible // indirect\n\tgithub.com/sirupsen/logrus v1.8.1 // indirect\n\tgithub.com/tdewolff/minify/v2 v2.24.8 // indirect\n\tgithub.com/tdewolff/parse/v2 v2.8.5 // indirect\n\tgithub.com/valyala/bytebufferpool v1.0.0 // indirect\n\tgithub.com/vmihailenco/msgpack/v5 v5.4.1 // indirect\n\tgithub.com/vmihailenco/tagparser/v2 v2.0.0 // indirect\n\tgithub.com/yosssi/ace v0.0.5 // indirect\n\tgolang.org/x/crypto v0.47.0 // indirect\n\tgolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 // indirect\n\tgolang.org/x/net v0.49.0 // indirect\n\tgolang.org/x/sys v0.40.0 // indirect\n\tgolang.org/x/text v0.33.0 // indirect\n\tgolang.org/x/time v0.14.0 // indirect\n\tgoogle.golang.org/protobuf v1.36.11 // indirect\n\tgopkg.in/ini.v1 v1.67.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n"
  },
  {
    "path": "_examples/http-server/http3-quic/go.sum",
    "content": "github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=\ngithub.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=\ngithub.com/CloudyKit/jet/v6 v6.3.1 h1:6IAo5Cx21xrHVaR8zzXN5gJatKV/wO7Nf6bfCnCSbUw=\ngithub.com/CloudyKit/jet/v6 v6.3.1/go.mod h1:lf8ksdNsxZt7/yH/3n4vJQWA9RUq4wpaHtArHhGVMOw=\ngithub.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc=\ngithub.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=\ngithub.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=\ngithub.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 h1:dG7/gLroJGht/jSQtHiLvT48Hxn+crbmvyItZC8cWOs=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05/go.mod h1:NYezi6wtnJtBm5btoprXc5SvAdqH0XTXWnUup0MptAI=\ngithub.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=\ngithub.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=\ngithub.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=\ngithub.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=\ngithub.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=\ngithub.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=\ngithub.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=\ngithub.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=\ngithub.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=\ngithub.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw=\ngithub.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8=\ngithub.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=\ngithub.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=\ngithub.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=\ngithub.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a h1:l7A0loSszR5zHd/qK53ZIHMO8b3bBSmENnQ6eKnUT0A=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=\ngithub.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=\ngithub.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=\ngithub.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=\ngithub.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=\ngithub.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2 h1:T9THsdP1woyAqKHwjkEsbCnMefsAFvk8iJJKokcJ3Go=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2/go.mod h1:JLDgIqnFy5loDSUv1OA2j0mb6p/rDhiCqigP22Uq9xE=\ngithub.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw=\ngithub.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/kataras/blocks v0.0.8 h1:MrpVhoFTCR2v1iOOfGng5VJSILKeZZI+7NGfxEh3SUM=\ngithub.com/kataras/blocks v0.0.8/go.mod h1:9Jm5zx6BB+06NwA+OhTbHW1xkMOYxahnqTN5DveZ2Yg=\ngithub.com/kataras/golog v0.1.12 h1:Bu7I/G4ilJlbfzjmU39O9N+2uO1pBcMK045fzZ4ytNg=\ngithub.com/kataras/golog v0.1.12/go.mod h1:wrGSbOiBqbQSQznleVNX4epWM8rl9SJ/rmEacl0yqy4=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd h1:oMsQgqKACbUdlaIWnN+EZzzAnHkw90hE/y/R0BSij2U=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd/go.mod h1:yucjtDl9Vn3OctWM1fi0MuM9OckemC7kEreFa9QtPF8=\ngithub.com/kataras/pio v0.0.13 h1:x0rXVX0fviDTXOOLOmr4MUxOabu1InVSTu5itF8CXCM=\ngithub.com/kataras/pio v0.0.13/go.mod h1:k3HNuSw+eJ8Pm2lA4lRhg3DiCjVgHlP8hmXApSej3oM=\ngithub.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY=\ngithub.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4=\ngithub.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA=\ngithub.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw=\ngithub.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=\ngithub.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=\ngithub.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=\ngithub.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8=\ngithub.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=\ngithub.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=\ngithub.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=\ngithub.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=\ngithub.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=\ngithub.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=\ngithub.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=\ngithub.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=\ngithub.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=\ngithub.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw=\ngithub.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU=\ngithub.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=\ngithub.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=\ngithub.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=\ngithub.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=\ngithub.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk=\ngithub.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=\ngithub.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=\ngithub.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=\ngithub.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=\ngithub.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/tdewolff/minify/v2 v2.24.8 h1:58/VjsbevI4d5FGV0ZSuBrHMSSkH4MCH0sIz/eKIauE=\ngithub.com/tdewolff/minify/v2 v2.24.8/go.mod h1:0Ukj0CRpo/sW/nd8uZ4ccXaV1rEVIWA3dj8U7+Shhfw=\ngithub.com/tdewolff/parse/v2 v2.8.5 h1:ZmBiA/8Do5Rpk7bDye0jbbDUpXXbCdc3iah4VeUvwYU=\ngithub.com/tdewolff/parse/v2 v2.8.5/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=\ngithub.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE=\ngithub.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=\ngithub.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=\ngithub.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=\ngithub.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=\ngithub.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=\ngithub.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=\ngithub.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=\ngithub.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA=\ngithub.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0=\ngithub.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=\ngithub.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=\ngithub.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngo.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=\ngo.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=\ngolang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=\ngolang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=\ngolang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=\ngolang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=\ngolang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=\ngolang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=\ngolang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=\ngolang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=\ngoogle.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=\ngopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nmoul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs=\nmoul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE=\n"
  },
  {
    "path": "_examples/http-server/http3-quic/localhost.cert",
    "content": "-----BEGIN CERTIFICATE-----\nMIIC+zCCAeOgAwIBAgIJAI5gi8BzcdQgMA0GCSqGSIb3DQEBBQUAMBQxEjAQBgNV\nBAMMCWxvY2FsaG9zdDAeFw0xOTA3MDkxMjExNDBaFw0yOTA3MDYxMjExNDBaMBQx\nEjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\nggEBAM1eSGwoYznwYtt+/hrv9o4FYxJFlxIMX6WN3c2y3rr8uOwExkz2RuU9SzgF\nqn0ctP1DoIKWNO0/L5j5Bjy2do0k8wHYPbTqb9zG64NGZj1lhkHgYXWyCD9U41DX\nV1DiJ2JiCRBadowFRRf3/KIPf3xnrCBSCoQwdfIeJJtAF9El2/TnTrGq9N98FJqR\ndCNyi+zY/iuymcA3aDOyYNjxSiuV//7ONEql5dxvRlhkjCHgrQ/rIbH/lSFAS+NG\nH/6ksEBX2+Q1LlQBaFiGeEnjVpCymrRfADw7bKyqMav39Nndw4UZ1r5MSG20YtXM\ndE4lA6VfAKzIZs2n87WF7OO8Y2sCAwEAAaNQME4wHQYDVR0OBBYEFKYItamcYz4Z\nNiDy3I2zflU4A7ywMB8GA1UdIwQYMBaAFKYItamcYz4ZNiDy3I2zflU4A7ywMAwG\nA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBACSoSrEArlgPJ1ftlSMkThUR\natTqJ/yB0rSyRxsct0C04qX880VP7avnKc0UhaDruXRjdAVn4X8KI+j6azQSKT40\nKRSVBinnonE0D4DBMCUVDFtkBW3FZJXAYyIYdF/6J3Khn/ksm7VDcVxYI1rjg87B\nU6aJytOkoGA2WGQOB1L0HtnTsarg/SKP/LSDUFT+XK6zTE7uogAUrpbwlpIaxc+8\n3jXvgxEdPj9Rq9Nt8/zjCkCGB2EusPPnqxcbqZb5WcGPCIlg3ChKq7vpaQld6KqG\n70jT7BZ2fqWSVJ5szRoS8WpKy1SZEx/+AA7VojMzkkw4RLb66zr1v7029b51ol4=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "_examples/http-server/http3-quic/localhost.key",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAzV5IbChjOfBi237+Gu/2jgVjEkWXEgxfpY3dzbLeuvy47ATG\nTPZG5T1LOAWqfRy0/UOggpY07T8vmPkGPLZ2jSTzAdg9tOpv3Mbrg0ZmPWWGQeBh\ndbIIP1TjUNdXUOInYmIJEFp2jAVFF/f8og9/fGesIFIKhDB18h4km0AX0SXb9OdO\nsar033wUmpF0I3KL7Nj+K7KZwDdoM7Jg2PFKK5X//s40SqXl3G9GWGSMIeCtD+sh\nsf+VIUBL40Yf/qSwQFfb5DUuVAFoWIZ4SeNWkLKatF8APDtsrKoxq/f02d3DhRnW\nvkxIbbRi1cx0TiUDpV8ArMhmzafztYXs47xjawIDAQABAoIBADvaKok7DBAquuT1\nkeEP5m9lqoX8uhaMfKOnQOleJAOi+9HtYk2zyN2ui2l8XT+xSh41w2XLmQk7zQds\nLCEtnEduaVQ0TWeYm5lgb+sGbW2fVQ2F82F1zWmHt+grmkr8XjYSFEor0zjjoEtn\n/rzMf38mR8fzoRT9eqJhnpGQkGBnfo0SyDKIDu9yYFM7yJ5s4KOTVsMGfavjQYgJ\nssQm0KQTo8HbYHieS6drEYFRwAgT8U1NFoVq24yU+Voyy7CR5rjfOsO9gSyStVSH\nnTkePmSIcpeQBfpfT3jh+STSS12eqhFCx1SqAUptUehQpOJhnWAyeSGjVLlrHqDR\nbCtSWCECgYEA62J2AC8bRQ8omoEOc5h1/kwPTcLYjhOCxwwU8ky7qve173MYZUeT\ndxWZx3haahccPyUKtsiGBdKYyYKSOIJMSMmwkG4uy6r5nhwNV+btEIL6Npj+XKMe\nPaATA4gBLRQNwcbUlZWLYc0Y6CXFnPA+atEa7EOEBfdgUqHOym1HsAUCgYEA31rU\nGPyv8R7Z1UXmxu70M8RwxKS4XhlP7RRg3Zuuqx6WWX4M85Np3wS4UWgQjGKICwoM\nD4XKZQOya5p+v7a1RUZt/OD6eJ0TjypW5fBmK2yvBUQkQCsVcFjBL3F+yv2Yk1sh\nKlPky4wXpDcmWXGmesgmyhpKIwL+qXEAJEaL0K8CgYABWqKlI6A7iHfKU726ioD7\nQoLABsPqJVCWRoqETk6yEBS62OWmB4Bgqf4leJrEi3d9IYBrRsIGnIyGdDrVGmLH\n9GkQm6GnSEeBUlX9UHXCp4467CxiagnNfvM9DPY8xSXDHJqydZbErEJda4I0gelK\nAgPuogDLa/3g289tuK015QKBgQDKcj9Qrqiiur3jC8rTgX8i9OjptAvQbsz9LL1n\n4FZ/j+fjEdeXZ4RMurB+SP7G4ABDUUYBQ9lhmeo8kfpUtryzH9VNonYkoOs7lrrR\nDAbvUUGKWmspJmP2QtxHrm2ofBexaKY1AXmd7Ur4c2x1IggtvgE6qn2MIojE+EGS\nn8bWzQKBgQClc/j4GYNNMUYOknxXMH/ec8PBDUtn098YuFL+s7DmRHimtkkjRo1A\nBtV7F8KpLruWohxXWy4QZ6HsAO255gIJ8DCbEAFCj96EHNx8KADSm3qbslu2fIB0\nzCsVaETGNAjV/d5hAEdYmgynCY49gNXV55ehV1UqzFoEfZVM9j5hUg==\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "_examples/http-server/http3-quic/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n\n\t\"github.com/quic-go/quic-go/http3\"\n)\n\n/*\n\t$ go get github.com/quic-go/quic-go@master\n*/\n\nfunc main() {\n\tapp := iris.New()\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.Writef(\"Hello from Index\")\n\t})\n\n\t// app.Configure(iris.WithOptimizations, or any other core config here)\n\t// app.Build()\n\t// http3.ListenAndServe(\":443\", \"./localhost.cert\", \"./localhost.key\", app)\n\t// OR:\n\tapp.Run(iris.Raw(func() error {\n\t\treturn http3.ListenAndServe(\":443\", \"./localhost.cert\", \"./localhost.key\", app)\n\t}))\n}\n"
  },
  {
    "path": "_examples/http-server/iris-configurator-and-host-configurator/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\tapp.ConfigureHost(func(host *iris.Supervisor) { // <- HERE: IMPORTANT\n\t\t// You can control the flow or defer something using some of the host's methods:\n\t\t// host.RegisterOnError\n\t\t// host.RegisterOnServe\n\t\thost.RegisterOnShutdown(func() {\n\t\t\tapp.Logger().Infof(\"Application shutdown on signal\")\n\t\t})\n\t})\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.HTML(\"<h1>Hello</h1>\\n\")\n\t})\n\n\tapp.Listen(\":8080\", iris.WithoutServerError(iris.ErrServerClosed))\n\n\t/* There are more easy ways to notify for global shutdown using the `iris.RegisterOnInterrupt` for default signal interrupt events.\n\tYou can even go it even further by looking at the: \"graceful-shutdown\" example.\n\t*/\n}\n"
  },
  {
    "path": "_examples/http-server/listen-addr/main.go",
    "content": "package main\n\nimport \"github.com/kataras/iris/v12\"\n\nfunc main() {\n\tapp := iris.New()\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.HTML(\"<h1>Hello World!</h1>\")\n\t})\n\n\t// http://localhost:8080\n\t// Identical to: app.Run(iris.Addr(\":8080\"))\n\n\tapp.Listen(\":8080\")\n\t// To listen using keep alive tcp connection listener,\n\t// set the KeepAlive duration configuration instead:\n\t// app.Listen(\":8080\", iris.WithKeepAlive(3*time.Minute))\n}\n"
  },
  {
    "path": "_examples/http-server/listen-addr/omit-server-errors/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.HTML(\"<h1>Hello World!</h1>\")\n\t})\n\n\terr := app.Listen(\":8080\", iris.WithoutServerError(iris.ErrServerClosed))\n\tif err != nil {\n\t\t// do something\n\t}\n\t// same as:\n\t// err := app.Listen(\":8080\")\n\t// import \"errors\"\n\t// if errors.Is(err, iris.ErrServerClosed) {\n\t//     [...]\n\t// }\n}\n"
  },
  {
    "path": "_examples/http-server/listen-addr/omit-server-errors/main_test.go",
    "content": "package main\n\nimport (\n\t\"bytes\"\n\tstdContext \"context\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc logger(app *iris.Application) *bytes.Buffer {\n\tbuf := &bytes.Buffer{}\n\n\tapp.Logger().SetOutput(buf)\n\n\t// disable the \"Now running at....\" in order to have a clean log of the error.\n\t// we could attach that on `Run` but better to keep things simple here.\n\tapp.Configure(iris.WithoutStartupLog)\n\treturn buf\n}\n\nfunc TestListenAddr(t *testing.T) {\n\tapp := iris.New()\n\t// we keep the logger running as well but in a controlled way.\n\tlog := logger(app)\n\n\t// close the server at 3-6 seconds\n\tgo func() {\n\t\ttime.Sleep(3 * time.Second)\n\t\tctx, cancel := stdContext.WithTimeout(stdContext.TODO(), 3*time.Second)\n\t\tdefer cancel()\n\t\tapp.Shutdown(ctx)\n\t}()\n\n\terr := app.Listen(\":9829\")\n\t// in this case the error should be logged and return as well.\n\tif err != iris.ErrServerClosed {\n\t\tt.Fatalf(\"expecting err to be `iris.ErrServerClosed` but got: %v\", err)\n\t}\n\n\texpectedMessage := iris.ErrServerClosed.Error()\n\n\tif got := log.String(); !strings.Contains(got, expectedMessage) {\n\t\tt.Fatalf(\"expecting to log to contains the:\\n'%s'\\ninstead of:\\n'%s'\", expectedMessage, got)\n\t}\n}\n\nfunc TestListenAddrWithoutServerErr(t *testing.T) {\n\tapp := iris.New()\n\t// we keep the logger running as well but in a controlled way.\n\tlog := logger(app)\n\n\t// close the server at 3-6 seconds\n\tgo func() {\n\t\ttime.Sleep(3 * time.Second)\n\t\tctx, cancel := stdContext.WithTimeout(stdContext.TODO(), 3*time.Second)\n\t\tdefer cancel()\n\t\tapp.Shutdown(ctx)\n\t}()\n\n\t// we disable the ErrServerClosed, so the error should be nil when server is closed by `app.Shutdown`\n\t// or by an external issue.\n\terr := app.Listen(\":9827\", iris.WithoutServerError(iris.ErrServerClosed))\n\tif err != nil {\n\t\tt.Fatalf(\"expecting err to be nil but got: %v\", err)\n\t}\n\n\tif got := log.String(); got != \"\" {\n\t\tt.Fatalf(\"expecting to log nothing but logged: '%s'\", got)\n\t}\n}\n"
  },
  {
    "path": "_examples/http-server/listen-addr-public/main.go",
    "content": "package main\n\nimport \"github.com/kataras/iris/v12\"\n\nfunc main() {\n\tapp := iris.New()\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.HTML(\"<h1>Hello World!</h1>\")\n\n\t\t// Will print the ngrok public domain\n\t\t// that your app is using to be served online.\n\t\tctx.Writef(\"From: %s\",\n\t\t\tctx.Application().ConfigurationReadOnly().GetVHost())\n\t})\n\n\tapp.Listen(\":8080\", iris.WithTunneling, iris.WithLogLevel(\"debug\"))\n\n\t/* The full configuration can be set as:\n\tapp.Listen(\":8080\", iris.WithConfiguration(\n\t\tiris.Configuration{\n\t\t\tTunneling: iris.TunnelingConfiguration{\n\t\t\t\tAuthToken:    \"my-ngrok-auth-client-token\",\n\t\t\t\tBin:          \"/bin/path/for/ngrok\",\n\t\t\t\tRegion:       \"eu\",\n\t\t\t\tWebInterface: \"127.0.0.1:4040\",\n\t\t\t\tTunnels: []iris.Tunnel{\n\t\t\t\t\t{\n\t\t\t\t\t\tName: \"MyApp\",\n\t\t\t\t\t\tAddr: \":8080\",\n\t\t\t\t\t\tHostname: \"your-custom-sub-domain.ngrok.io\", // optionally\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t}))\n\t*/\n}\n"
  },
  {
    "path": "_examples/http-server/listen-letsencrypt/main.go",
    "content": "// Package main provide one-line integration with letsencrypt.org\npackage main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.Writef(\"Hello from SECURE SERVER!\")\n\t})\n\n\tapp.Get(\"/test2\", func(ctx iris.Context) {\n\t\tctx.Writef(\"Welcome to secure server from /test2!\")\n\t})\n\n\tapp.Get(\"/redirect\", func(ctx iris.Context) {\n\t\tctx.Redirect(\"/test2\")\n\t})\n\n\t// NOTE: This will not work on domains like this,\n\t// use real whitelisted domain(or domains split by whitespaces)\n\t// and a non-public e-mail instead or edit your hosts file.\n\tapp.Run(iris.AutoTLS(\":443\", \"example.com\", \"mail@example.com\"))\n\n\t// Note: to disable automatic \"http://\" to \"https://\" redirections pass\n\t// the `iris.AutoTLSNoRedirect` host configurator to AutoTLS function, example:\n\t/*\n\t\tvar fallbackServer = func(acme func(http.Handler) http.Handler) *http.Server {\n\t\t\t// Use any http.Server and Handler, as long as it's wrapped by `acme` one.\n\t\t\t// In that case we share the application through non-tls users too:\n\t\t\tsrv := &http.Server{Handler: acme(app)}\n\t\t\tgo srv.ListenAndServe()\n\t\t\treturn srv\n\t\t}\n\n\t\tapp.Run(iris.AutoTLS(\":443\", \"example.com myip\", \"mail@example.com\",\n\t\t\tiris.AutoTLSNoRedirect(fallbackServer)))\n\t*/\n}\n"
  },
  {
    "path": "_examples/http-server/listen-tls/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.Writef(\"Hello from the SECURE server\")\n\t})\n\n\tapp.Get(\"/mypath\", func(ctx iris.Context) {\n\t\tctx.Writef(\"Hello from the SECURE server on path /mypath\")\n\t})\n\n\t// Start the server (HTTPS) on port 443,\n\t// and a secondary of (HTTP) on port :80 which redirects requests to their HTTPS version.\n\t// This is a blocking func.\n\tapp.Run(iris.TLS(\"127.0.0.1:443\", \"mycert.crt\", \"mykey.key\"))\n\n\t// Note: to disable automatic \"http://\" to \"https://\" redirections pass the `iris.TLSNoRedirect`\n\t// host configurator to TLS function, example:\n\t//\n\t// app.Run(iris.TLS(\"127.0.0.1:443\", \"mycert.crt\", \"mykey.key\", iris.TLSNoRedirect))\n}\n"
  },
  {
    "path": "_examples/http-server/listen-tls/mycert.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIIFazCCA1OgAwIBAgIUfwMd9auWixp19UnXOmyxJ9Jkv7IwDQYJKoZIhvcNAQEL\nBQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM\nGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMDA2MjUwOTUxNDdaFw0yMTA2\nMjUwOTUxNDdaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw\nHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggIiMA0GCSqGSIb3DQEB\nAQUAA4ICDwAwggIKAoICAQDlVGyGAQ9uyfNbwZyrtYOSjLpxf5NpNToh2OzU7gy2\nOexBji5lmWBQ3oYDG+FjAkbHORPzOMNpeMwje+IjGZBw8x6E+8WoGdSzbrEZ6pUV\nwKJGKEuDlx6g6HEmtv3ZwgGe20gvPjjW+oCO888dwK/mbIHrHTq4nO3o0gAdAJwu\namn9BlHU5O4RW7BQ4tLF+j/fBCACWRG1NHXA0AT8eg544GyCdyteAH11oCDsHS8/\nDAPsM6t+tZrMCIt9+9dzPdVoOmQNaMMrcz8eJohddRTK6zHe9ixZTt/soayOF7OS\nQQeekbr3HPYhD450zRVplLMHx7wnph/+O+Po6bqDnUzdnkqAAwwymQapHMuHXZKN\nrhdfKau3rVo1GeXLIRgeWLUoxFSm4TYshrgt+0AidLRH+dCY7MS9Ngga/sAK3vID\ngSF75mFgOhY+q7nvY9Ecao6TnoNNRY29hUat4y0VwSyysUy887vHr6lMK5CrAT/l\nCh8fuu20HUCoiLwMJvA6+wpivZkuiIvWY7bVGYsEYrrW+bCNN9wCGYTZEyX++os9\nv/38wdOqGUT00ewXkjIUFCWbrnxxSr98kF3w3wPf9K4Y40MNxeR90nyX4zjXGF1/\n91msUh+iivsz9mcN9DK83fgTyOsoVLX5cm/L2UBwMacsfjBbN4djOc5IuYMar/VN\nGQIDAQABo1MwUTAdBgNVHQ4EFgQUtkf+yAvqgZC8f22iJny9hFEDolMwHwYDVR0j\nBBgwFoAUtkf+yAvqgZC8f22iJny9hFEDolMwDwYDVR0TAQH/BAUwAwEB/zANBgkq\nhkiG9w0BAQsFAAOCAgEAE2QasBVru618rxupyJgEHw6r4iv7sz1Afz3Q5qJ4oSA9\nxVsrVCjr3iHRFSw8Rf670E8Ffk/JjzS65mHw6zeZj/ANBKQWLjRlqzYXeetq5HzG\nSIgaG7p1RFvvzz3+leFGzjinZ6sKbfB4OB72o2YN+fO8DsDxgGKll0W4KAazizSe\nHY9Pgu437tWnwF16rFO3IL47n5HzYlRoGIPOpzFoNX5+fyn9GlnKEtONF2QBKTjY\nrdjvqFRByDiC74d8z/Yx8IiDRn1mTcG90JLR9+c6M7fruha9Y/rJfw+4AhVh5ZDz\nBl9rGPjwEs5zwutYvVAJzs7AVcighYP1lHKoJ7DxBDQeyBsYlUNk2l6bmZgLgGUZ\n+2OyWlqc/jD2GdDsIaZ4i7QqhTI/6aYZIf5zUkblKV1aMSaDulKxRv//OwW28Jax\n9EEoV7VaFb3sOkB/tZGhusXeQVtdrhahT3KkZLNwmNXoXWKJ5LjeUlFWJyV6JbDe\ny/PIWWCwWqyuFCSZS+Cg3RDgAzfSxkI8uVZ+IKKJS3UluDX45lxXtbRrvTQ+oDrA\n6ga5c1Vz9C4kn1K5yW4d7QIvg6vPiy7gvl+//sz9oxUM3yswInDBY0HKLgT0Uq9b\nYzLDh2RSaHsgHMPy2BKqR+q2N+lpg7inAWuJM1Huq6eHFqhiyQkzsfscBd1Dpm8=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "_examples/http-server/listen-tls/mykey.key",
    "content": "-----BEGIN PRIVATE KEY-----\nMIIJRAIBADANBgkqhkiG9w0BAQEFAASCCS4wggkqAgEAAoICAQDlVGyGAQ9uyfNb\nwZyrtYOSjLpxf5NpNToh2OzU7gy2OexBji5lmWBQ3oYDG+FjAkbHORPzOMNpeMwj\ne+IjGZBw8x6E+8WoGdSzbrEZ6pUVwKJGKEuDlx6g6HEmtv3ZwgGe20gvPjjW+oCO\n888dwK/mbIHrHTq4nO3o0gAdAJwuamn9BlHU5O4RW7BQ4tLF+j/fBCACWRG1NHXA\n0AT8eg544GyCdyteAH11oCDsHS8/DAPsM6t+tZrMCIt9+9dzPdVoOmQNaMMrcz8e\nJohddRTK6zHe9ixZTt/soayOF7OSQQeekbr3HPYhD450zRVplLMHx7wnph/+O+Po\n6bqDnUzdnkqAAwwymQapHMuHXZKNrhdfKau3rVo1GeXLIRgeWLUoxFSm4TYshrgt\n+0AidLRH+dCY7MS9Ngga/sAK3vIDgSF75mFgOhY+q7nvY9Ecao6TnoNNRY29hUat\n4y0VwSyysUy887vHr6lMK5CrAT/lCh8fuu20HUCoiLwMJvA6+wpivZkuiIvWY7bV\nGYsEYrrW+bCNN9wCGYTZEyX++os9v/38wdOqGUT00ewXkjIUFCWbrnxxSr98kF3w\n3wPf9K4Y40MNxeR90nyX4zjXGF1/91msUh+iivsz9mcN9DK83fgTyOsoVLX5cm/L\n2UBwMacsfjBbN4djOc5IuYMar/VNGQIDAQABAoICAQCtWx1SSxjkcerxsLEDKApW\nzOTfiUXgoOjZz0ZwS6b2VWDfyWAPU1r4ps39KaU+F+lzDhWjpYQqhbMjG7G9QMTs\nbQvkEQLAaQ5duU5NPgQG1oCUsj8rMSBpGGz4jBnm834QHMk7VTjYYbKu3WTyo8cU\nU2/+UDEkfxRlC+IkCmMFv1FxgMZ5PbktC/eDnYMhP2Pq7Q5ZWAVHymk9IMK0LHwm\nKdg842K4A3zTXwGkGwetDCMm+YQpG5TxqX/w82BRcCuTR5h8fnYSsWLEIvKwWyIl\nppcjaUnrFPG2yhxLqWUIKPpehuEjjhQMt9rDNoh6MHsJZZY5Dp5eq91EIvLoLQ99\nhXBmD4P8LDop4r0jniPZJi/ACsaD0jBooA4525+Kouq7RP28Jp/pek7lVOOcBgRv\nD3zyESbKfqoaOfyfQ2ff4sILnTAr4V2nq3ekphGEYJrWN0ZoADcLdnr1cZ8L+VBI\no/4mi5/3HID/UEDliHSa97hxxGBEqTto0ZuXuNwfwx5ho33uVT6zNwRgiJ62Bgu3\nFhk/wVGuZxWvb1KHUNInG9cvsslhO4Vu9wJvYj91BnRq36rsyKKid5DrU+PNgmog\nlw3IXQpTojyRCYPuG9TKqEZ6b+so7GTKhBOjiwaupMOletVRGSAdbE81VN6HtxNW\naj39+FnxzMAlsieib+PBAQKCAQEA+t1fOYSaZBo7pZUmo2S0zulUEJjrYRGKJlWJ\n4psWSwFu/7/3UL4q0RBQaSRew9u/YSpaNlBYfcpnFVOjiLwHq5Hx46Eq0BuKsNlJ\n1/qxw9qjHqcrOre6K4/7NaWLPuM9fEmV+3MhFVXgv+WC5BHOowRTlOG30vIcC1J2\nL5xsBUsxDDY13cD1bLKRmFcyMFM8y7wMZmo7H/WfVmyoPKQaC43pTcmIXH0Jr2Ws\nWsfh18mhjtamaOPEFx5K0x4d0PI8tW5ouiUUkVIDaue27XfS969qEChv768/44eX\nWeqcekaG9jv2noMClt79rYd3Lne9HkgY6IT9FT+JqXfu+KYwuQKCAQEA6gYzUsGB\n9GQO8DE8AYn7JwNOtg1X4zKakXiGxH+nuZb7wJjAeGdYqTHySxPBXg0A2nDwoyz5\n4sAdLAr3FZoIvTzo7M5KIKFDzfyDmQDavhroH1mBAEiqKGNniP+RND3nWBBqDK1R\nqcqbhI3Kj5Ycany6a4nP+hZRBIyT9sfJ0S0YruSY8IGXgDwhlJrZ7bsWMZylrgD/\n1qnPL0KqVBY8YR8msRj88h72IlD5o0kwvisOIvyhA0YgwGBb6lg7A+DifiF03ZlS\n2yELbIkKDVr+p3jC7MBh4B+OJY68AMl6wVjAaDM1AZnpjKE5YmZg5+Ks5823zILo\nPrSB9hn0+DIPYQKCAQEAh9x+JuNmzhHa/dkiHNl8hpadHYQD7gUWwZ4P1/bQAv0a\nxU2MvmDPRXxFYDv/SqlnI1NRmhq3YiDM5SLv7SyQJt4al4IAcsaHvTFgqaSuw3hU\nYVR9uAYqwE7w6OPn3r4o3Xfoz05Ru4FP//1nfucZ9vVv4rC/4nGWuJcHRM+9PLy1\nKnztfVR0VlL7QPrwRnW99kS4nnqn3K4khiTAlF73cAyCLsuXmydoqGIzDtMzv68G\nXRpo82NvHmoccevcj/2w3T2XYECWvAEjsrEdQ8xiKBwLIAcWYEOUIUCcumiyKBKs\nIwzkioI/U8AeuO0lobfdZ1n6i2sCuZA4mNxIQseWmQKCAQEA5YkfXdQeuq5JWJ1x\n1bCYfjNoSHfd9CH2KSimRqVOxWGpm8Y3QeFbvNgYZjsCNlVauOZ9oA7FKfp0onY+\n0xk56SKM83eCjW6fKrK6AKAt7LhHZDhNpxGek+6r5luE+FCfUGkJG1YD+x2WW/UW\n8K6zQF8GGeQZ8Zlh7axUlIBxGpG43BGrUHpLNqPD7BXWGq6dnhufBYRFay8y34/r\nsH3+yuPa92ki7/geQppZwCZRgLSKMRbIdoWaKhZZEQlpGOzCOiRmk9OGyRcoNVRU\nX7UYgPqZdc1cMo/AxGWzULJNjMaYMZvIKcHkqOKZfkIcWlSictn7pMPhN1+k+NWM\nyMORAQKCAQAyXl02h/c2ihx6cjKlnNeDr2ZfzkoiAvFuKaoAR+KVvb9F9X7ZgKSi\nwudZyelTglIVCYXeRmG09uX3rNGCzFrweRwgn6x/8DnN5pMRJVZOXFdgR+V9uKep\nK6F7DYbPyggvLOAsezB+09i9lwxM+XdA2whVpL5NFR1rGfFglnE1EQHcEvNONkcv\n0h8x9cNSptJyRDLiTIKI9EhonuzwzkGpvjULQE8MLbT8PbjoLFINcE9ZWhwtyw0V\nXO32KE8iLKt3KzHz9CfTRCI3M7DwD752AC6zRr8ZS/HXzs+5WTkdVVEtRC7Abd3y\nW2TzuSMYNDu876twbTVQJED3mwOAQ3J7\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "_examples/http-server/listen-unix/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/core/netutil\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\tl, err := netutil.UNIX(\"/tmpl/srv.sock\", 0666) // see its code to see how you can manually create a new file listener, it's easy.\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tapp.Run(iris.Listener(l))\n}\n"
  },
  {
    "path": "_examples/http-server/notify-on-shutdown/main.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.HTML(\"<h1>Hello, try to refresh the page after ~5 secs</h1>\")\n\t})\n\n\tapp.Logger().Info(\"Wait 5 seconds and check your terminal again\")\n\t// simulate a shutdown action here...\n\tgo func() {\n\t\t<-time.After(5 * time.Second)\n\t\ttimeout := 10 * time.Second\n\t\tctx, cancel := context.WithTimeout(context.Background(), timeout)\n\t\tdefer cancel()\n\t\t// close all hosts, this will notify the callback we had register\n\t\t// inside the `configureHost` func.\n\t\tapp.Shutdown(ctx)\n\t}()\n\n\t// app.ConfigureHost(configureHost) -> or pass \"configureHost\" as `app.Addr` argument, same result.\n\n\t// start the server as usual, the only difference is that\n\t// we're adding a second (optional) function\n\t// to configure the just-created host supervisor.\n\t//\n\t// http://localhost:8080\n\t// wait 10 seconds and check your terminal.\n\tapp.Run(iris.Addr(\":8080\", configureHost), iris.WithoutServerError(iris.ErrServerClosed))\n\n\ttime.Sleep(500 * time.Millisecond) // give time to the separate go routine(`onServerShutdown`) to finish.\n\n\t/* See\n\tiris.RegisterOnInterrupt(callback) for global catch of the CTRL/CMD+C and OS events.\n\tLook at the \"graceful-shutdown\" example for more.\n\t*/\n}\n\nfunc onServerShutdown() {\n\tprintln(\"server is closed\")\n}\n\nfunc configureHost(su *iris.Supervisor) {\n\t// here we have full access to the host that will be created\n\t// inside the `app.Run` function or `NewHost`.\n\t//\n\t// we're registering a shutdown \"event\" callback here:\n\tsu.RegisterOnShutdown(onServerShutdown)\n\t// su.RegisterOnError\n\t// su.RegisterOnServe\n}\n"
  },
  {
    "path": "_examples/http-server/socket-sharding/main.go",
    "content": "package main\n\nimport (\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tstartup := time.Now()\n\n\tapp := iris.New()\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\ts := startup.Format(ctx.Application().ConfigurationReadOnly().GetTimeFormat())\n\t\tctx.Writef(\"This server started at: %s\\n\", s)\n\t})\n\n\t// This option allows linear scaling server performance on multi-CPU servers.\n\t// See https://www.nginx.com/blog/socket-sharding-nginx-release-1-9-1/ for details.\n\tapp.Listen(\":8080\", iris.WithSocketSharding)\n}\n"
  },
  {
    "path": "_examples/http-server/timeout/main.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\tapp.Get(\"/test\", func(ctx iris.Context) {\n\t\tw := new(worker)\n\t\tresult := w.Work(ctx)\n\t\tctx.WriteString(result)\n\t})\n\n\tapp.Listen(\":8080\", iris.WithTimeout(4*time.Second))\n}\n\ntype worker struct{}\n\nfunc (w *worker) Work(ctx context.Context) string {\n\tt := time.Tick(time.Second)\n\ttimes := 0\n\tfor {\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\tprintln(\"context.Done: canceled\")\n\t\t\treturn \"Work canceled\"\n\t\tcase <-t:\n\t\t\ttimes++\n\t\t\tprintln(\"Doing some work...\")\n\n\t\t\tif times > 5 {\n\t\t\t\treturn \"Work is done with success\"\n\t\t\t}\n\t\t}\n\t}\n\n\treturn \"nothing to do here\"\n}\n"
  },
  {
    "path": "_examples/i18n/basic/hosts",
    "content": "# Copyright (c) 1993-2009 Microsoft Corp.\n#\n# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.\n#\n# This file contains the mappings of IP addresses to host names. Each\n# entry should be kept on an individual line. The IP address should\n# be placed in the first column followed by the corresponding host name.\n# The IP address and the host name should be separated by at least one\n# space.\n#\n# Additionally, comments (such as these) may be inserted on individual\n# lines or following the machine name denoted by a '#' symbol.\n#\n# For example:\n#\n#      102.54.94.97     rhino.acme.com          # source server\n#       38.25.63.10     x.acme.com              # x client host\n\n# localhost name resolution is handled within DNS itself.\n#\t127.0.0.1       localhost\n#\t::1             localhost\n127.0.0.1\t\tmydomain.com\n127.0.0.1\t\ten.mydomain.com\n127.0.0.1\t\tel.mydomain.com\n127.0.0.1\t\tel-gr.mydomain.com\n127.0.0.1\t\tzh.mydomain.com"
  },
  {
    "path": "_examples/i18n/basic/locales/el-GR/locale_el-GR.ini",
    "content": "﻿hi = γεια, %s\nuserProfilePublicDescription = <a href=\"%s\">περιγραφή προφιλ</a>"
  },
  {
    "path": "_examples/i18n/basic/locales/el-GR/locale_multi_first_el-GR.yml",
    "content": "key1: \"αυτό είναι μια τιμή από το πρώτο αρχείο: locale_multi_first\""
  },
  {
    "path": "_examples/i18n/basic/locales/el-GR/locale_multi_second_el-GR.ini",
    "content": "key2 = αυτό είναι μια τιμή από το δεύτερο αρχείο μετάφρασης: locale_multi_second"
  },
  {
    "path": "_examples/i18n/basic/locales/en-US/locale_en-US.ini",
    "content": "hi = hello, %s\nuserProfilePublicDescription = <a href=\"%s\">profile description</a>"
  },
  {
    "path": "_examples/i18n/basic/locales/en-US/locale_multi_first_en-US.yml",
    "content": "key1: \"this is a value from the first file: locale_multi_first\""
  },
  {
    "path": "_examples/i18n/basic/locales/en-US/locale_multi_second_en-US.ini",
    "content": "key2 = this is a value from the second file: locale_multi_second"
  },
  {
    "path": "_examples/i18n/basic/locales/zh-CN/locale_zh-CN.ini",
    "content": "﻿hi = 您好，%s\nuserProfilePublicDescription = <a href=\"%s\">个人资料描述</a>"
  },
  {
    "path": "_examples/i18n/basic/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"html/template\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\n/*\n\tSee i18n-template for a more advanced translation key-values.\n*/\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\n\t// Configure i18n.\n\t//\n\t// app.I18n.Subdomain = false to disable resolve lang code from subdomain.\n\t// app.I18n.LoadAssets for go-bindata.\n\n\t// Default values:\n\t// app.I18n.URLParameter = \"lang\"\n\t// app.I18n.Subdomain = true\n\t//\n\t// Set to false to disallow path (local) redirects,\n\t// see https://github.com/kataras/iris/issues/1369.\n\t// app.I18n.PathRedirect = true\n\t//\n\t// See `app.I18n.ExtractFunc = func(ctx iris.Context) string` or\n\t// `ctx.SetLanguage(langCode string)` to change the extracted language from a request.\n\t//\n\t// Use DefaultMessageFunc to customize the return value of a not found key or lang.\n\t// All language inputs fallback to the default locale if not matched.\n\t// This is why this one accepts both input and matched languages,\n\t// so the caller can be more expressful knowing those.\n\t// Defaults to nil.\n\tapp.I18n.DefaultMessageFunc = func(langInput, langMatched, key string, args ...any) string {\n\t\tmsg := fmt.Sprintf(\"user language input: %s: matched as: %s: not found key: %s: args: %v\", langInput, langMatched, key, args)\n\t\tapp.Logger().Warn(msg)\n\t\treturn msg\n\t}\n\t// Load i18n when customizations are set in place.\n\t//\n\t// First parameter: Glob filpath patern,\n\t// Second variadic parameter: Optional language tags, the first one is the default/fallback one.\n\terr := app.I18n.Load(\"./locales/*/*\", \"en-US\", \"el-GR\", \"zh-CN\")\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tapp.Get(\"/not-matched\", func(ctx iris.Context) {\n\t\ttext := ctx.Tr(\"not_found_key\", \"some\", \"values\", 42)\n\t\tctx.WriteString(text)\n\t\t// user language input: en-gb: matched as: en-US: not found key: not_found_key: args: [some values 42]\n\t})\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\thi := ctx.Tr(\"hi\", \"iris\")\n\t\tlocale := ctx.GetLocale()\n\n\t\tctx.Writef(\"From the language %s translated output: %s\", locale.Language(), hi)\n\t})\n\n\tapp.Get(\"/some-path\", func(ctx iris.Context) {\n\t\tctx.Writef(\"%s\", ctx.Tr(\"hi\", \"iris\"))\n\t})\n\n\tapp.Get(\"/other\", func(ctx iris.Context) {\n\t\tlanguage := ctx.GetLocale().Language()\n\n\t\tfromFirstFileValue := ctx.Tr(\"key1\")\n\t\tfromSecondFileValue := ctx.Tr(\"key2\")\n\t\tctx.Writef(\"From the language: %s, translated output:\\n%s=%s\\n%s=%s\",\n\t\t\tlanguage, \"key1\", fromFirstFileValue,\n\t\t\t\"key2\", fromSecondFileValue)\n\t})\n\n\t// using in inside your views:\n\tview := iris.HTML(\"./views\", \".html\")\n\tapp.RegisterView(view)\n\n\tapp.Get(\"/templates\", func(ctx iris.Context) {\n\t\tif err := ctx.View(\"index.html\", iris.Map{\n\t\t\t\"tr\": ctx.Tr, // word, arguments... {call .tr \"hi\" \"iris\"}}\n\t\t\t\"trUnsafe\": func(message string, args ...any) template.HTML {\n\t\t\t\treturn template.HTML(ctx.Tr(message, args...))\n\t\t\t},\n\t\t}); err != nil {\n\t\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\t\treturn\n\t\t}\n\n\t\t// Note that,\n\t\t// Iris automatically adds a \"tr\" global template function as well,\n\t\t// the only difference is the way you call it inside your templates and\n\t\t// that it accepts a language code as its first argument: {{ tr \"el-GR\" \"hi\" \"iris\"}}\n\t})\n\t//\n\n\treturn app\n}\n\nfunc main() {\n\tapp := newApp()\n\n\t// go to http://localhost:8080/el-gr/some-path\n\t// ^ (by path prefix)\n\t//\n\t// or http://el.mydomain.com8080/some-path\n\t// ^ (by subdomain - test locally with the hosts file)\n\t//\n\t// or http://localhost:8080/zh-CN/templates\n\t// ^ (by path prefix with uppercase)\n\t//\n\t// or http://localhost:8080/some-path?lang=el-GR\n\t// ^ (by url parameter)\n\t//\n\t// or http://localhost:8080 (default is en-US)\n\t// or http://localhost:8080/?lang=zh-CN\n\t//\n\t// go to http://localhost:8080/other?lang=el-GR\n\t// or http://localhost:8080/other (default is en-US)\n\t// or http://localhost:8080/other?lang=en-US\n\t//\n\t// or use cookies to set the language.\n\tapp.Listen(\":8080\", iris.WithSitemap(\"http://localhost:8080\"))\n}\n"
  },
  {
    "path": "_examples/i18n/basic/main_test.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nfunc TestI18n(t *testing.T) {\n\tapp := newApp()\n\n\tconst (\n\t\texpectedf = \"From the language %s translated output: %s\"\n\n\t\tenUS = \"hello, iris\"\n\t\telGR = \"γεια, iris\"\n\t\tzhCN = \"您好，iris\"\n\t)\n\n\tvar (\n\t\ttests = map[string]string{\n\t\t\t\"en-US\": fmt.Sprintf(expectedf, \"en-US\", enUS),\n\t\t\t\"el-GR\": fmt.Sprintf(expectedf, \"el-GR\", elGR),\n\t\t\t\"zh-CN\": fmt.Sprintf(expectedf, \"zh-CN\", zhCN),\n\t\t}\n\n\t\telgrMulti = fmt.Sprintf(\"From the language: %s, translated output:\\n%s=%s\\n%s=%s\", \"el-GR\",\n\t\t\t\"key1\",\n\t\t\t\"αυτό είναι μια τιμή από το πρώτο αρχείο: locale_multi_first\",\n\t\t\t\"key2\",\n\t\t\t\"αυτό είναι μια τιμή από το δεύτερο αρχείο μετάφρασης: locale_multi_second\")\n\t\tenusMulti = fmt.Sprintf(\"From the language: %s, translated output:\\n%s=%s\\n%s=%s\", \"en-US\",\n\t\t\t\"key1\",\n\t\t\t\"this is a value from the first file: locale_multi_first\",\n\t\t\t\"key2\",\n\t\t\t\"this is a value from the second file: locale_multi_second\")\n\t)\n\n\te := httptest.New(t, app)\n\t// default should be en-US.\n\te.GET(\"/\").Expect().Status(httptest.StatusOK).Body().IsEqual(tests[\"en-US\"])\n\n\tfor lang, body := range tests {\n\t\te.GET(\"/\").WithQueryString(\"lang=\" + lang).Expect().Status(httptest.StatusOK).\n\t\t\tBody().IsEqual(body)\n\n\t\t// test lowercase.\n\t\te.GET(\"/\").WithQueryString(\"lang=\" + strings.ToLower(lang)).Expect().Status(httptest.StatusOK).\n\t\t\tBody().IsEqual(body)\n\n\t\t// test first part (e.g. en instead of en-US).\n\t\tlangFirstPart := strings.Split(lang, \"-\")[0]\n\t\te.GET(\"/\").WithQueryString(\"lang=\" + langFirstPart).Expect().Status(httptest.StatusOK).\n\t\t\tBody().IsEqual(body)\n\n\t\t// test accept-language header prefix (i18n wrapper).\n\t\te.GET(\"/\"+lang).WithHeader(\"Accept-Language\", lang).Expect().Status(httptest.StatusOK).\n\t\t\tBody().IsEqual(body)\n\n\t\t// test path prefix (i18n router wrapper).\n\t\te.GET(\"/\" + lang).Expect().Status(httptest.StatusOK).\n\t\t\tBody().IsEqual(body)\n\n\t\t// test path prefix with first part.\n\t\te.GET(\"/\" + langFirstPart).Expect().Status(httptest.StatusOK).\n\t\t\tBody().IsEqual(body)\n\t}\n\n\te.GET(\"/other\").WithQueryString(\"lang=el-GR\").Expect().Status(httptest.StatusOK).\n\t\tBody().IsEqual(elgrMulti)\n\te.GET(\"/other\").WithQueryString(\"lang=en-US\").Expect().Status(httptest.StatusOK).\n\t\tBody().IsEqual(enusMulti)\n\n\t// test path prefix (i18n router wrapper).\n\te.GET(\"/el-gr/other\").Expect().Status(httptest.StatusOK).\n\t\tBody().IsEqual(elgrMulti)\n\te.GET(\"/en/other\").Expect().Status(httptest.StatusOK).\n\t\tBody().IsEqual(enusMulti)\n\n\te.GET(\"/el-GRtemplates\").Expect().Status(httptest.StatusNotFound)\n\te.GET(\"/el-templates\").Expect().Status(httptest.StatusNotFound)\n\n\te.GET(\"/el/templates\").Expect().Status(httptest.StatusOK).Body().Contains(elGR).Contains(zhCN)\n\n\te.GET(\"/not-matched\").WithQuery(\"lang\", \"en-gb\").Expect().Status(httptest.StatusOK).Body().IsEqual(\"user language input: en-gb: matched as: en-US: not found key: not_found_key: args: [some values 42]\")\n}\n"
  },
  {
    "path": "_examples/i18n/basic/views/index.html",
    "content": "<h3>Test translate current locale template function <i>[dynamic]</i> (\"word\", arguments...) <br/> <code>call .tr \"hi\" \"iris\"</code></h3>\n\n{{call .tr \"hi\" \"iris\"}}\n\n<hr/>\n\n<h3>Test translate of any language template function <i>[static]</i> (\"language\", \"word\", arguments...) <br/> <code>tr \"zh-CN\" \"hi\" \"iris\"</code></h3>\n\n{{tr \"zh-CN\" \"hi\" \"iris\"}}\n\n<h3>Test HTML link (\"word\", arguments...) <br/> <code>call .trUnsafe \"trUnsafe\" \"userProfilePublicDescription\" \"https://iris-go.com\"</code></h3>\n\n{{call .trUnsafe \"userProfilePublicDescription\" \"https://iris-go.com\"}}"
  },
  {
    "path": "_examples/i18n/plurals/locales/en-US/1648.ini",
    "content": "[message]\nEncrypted = Encrypted\nMessage = Message\nEncryptedMessage = {{tr \"message.Encrypted\"}} {{tr \"message.Message\"}}\nHostResult = Store {{tr \"message.EncryptedMessage\"}} Online"
  },
  {
    "path": "_examples/i18n/plurals/locales/en-US/welcome.yml",
    "content": "# Locale variables\n#\n# Unlike normal keys, the variables\n# have limitations of: no \">x\", \"zero\", \"two\" and template functions are supported.\n# This is done to force developers to use small and easy to read variables for easier maintain process. \nVars:\n  - Minutes:\n      # possible keys:\n      # one\n      # \"=x\"  - where x is a number\n      # \"<x\"\n      # other\n      # format - to customize the format, which defaults to %d .\n      one: \"minute\"\n      other: \"minutes\"\n      format: \"%d\" # defaults to that.\n  - Dogs:\n      \"=5\": \"dogsssss\"\n      one: \"dog\"\n      other: \"dogs\"\n  - Houses:\n      one: \"house\"\n      other: \"houses\"\n  - Gender:\n      \"=1\": \"She\" # 1 for female\n      \"=2\": \"He\" # 2 for male\n\n# Using variables in raw string\nYouLate: \"You are %[1]d ${Minutes} late.\"\n# Just a simple raw value\nClassic: \"classic\"\n\n# Pluralization, translate based on the plural count\n# including the variables and their counts\nFreeDay:\n  # possible keys:\n  # zero\n  # one\n  # two\n  # \"=x\"\n  # \"<x\"\n  # \">x\"\n  # other\n  \"=3\": \"You have three days and %[2]d ${Minutes} off.\" # \"FreeDay\" 3, 15 (plurals + variable pluralization)\n  one:  \"You have a day off\" # \"FreeDay\", 1\n  other: \"You have %[1]d free days\" # \"FreeDay\", 5\n\n# Sprintf-like raw translation\nHeIsHome: \"%s is home\"\n\n# Value without plural of its self but variables except pluralization\nHouseCount: \"${Gender} (%[3]s) has %[2]d ${Houses}\"\n# Same as above but with a template instead\nVarTemplate: (${Gender}) {{tr \"HeIsHome\" .Name}}\n# Template and non template with variables in the same plural key\nVarTemplatePlural:\n  one: \"${Gender} is awesome\"\n  other: \"other (${Gender}) has %[3]d ${Houses}\"\n  \"=5\": \"{{call .InlineJoin .Names}} are awesome\"\nTemplatePlural:\n  one: \"{{.Name}} is unique\"\n  \"=5\": \"{{call .InlineJoin .Names}} are awesome\"\n# Same as above but it takes the variable counting through the map argument\nTemplateVarTemplatePlural:\n  other: \"These {{.PluralCount}} are wonderful, feeding {{.DogsCount}} ${Dogs} in total!\"\n\n# Local variables and section.\nLocalVarsHouseCount:\n   Text: \"${Gender} has %[2]d ${Houses}\"\n   Vars:\n    - Gender:\n        \"=3\": \"She\"\n        \"=4\": \"He\"\n    - Houses:\n        one: \"house\"\n        other: \"houses\"\n\n# Sections:\nroot:\n  user: Account\n\nnav:\n  home: Home # nav.home\n  user: '{{tr \"root.user\"}}' # nav.user\n  more:\n    what: \"this\" # nav.more.what\n    even:\n      more: \"yes\" # nav.more.even.more\n      aplural: \"You are %[1]d ${Minutes} late.\" # Tr(\"nav.more.even.aplural\", 15)\n"
  },
  {
    "path": "_examples/i18n/plurals/main.go",
    "content": "package main\n\nimport (\n\t\"strings\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nconst (\n\tfemale = iota + 1\n\tmale\n)\n\nconst tableStyle = `\n<style>\na {\n    padding: 8px 8px;\n    text-decoration:none;\n    cursor:pointer;\n    color: #10a2ff;\n}\ntable {\n    position: absolute;\n    top: 0;\n    bottom: 0;\n    left: 0;\n    right: 0;\n    height: 100%;\n    width: 100%;\n    border-collapse: collapse;\n    border-spacing: 0;\n    empty-cells: show;\n    border: 1px solid #cbcbcb;\n}\n\ntable caption {\n    color: #000;\n    font: italic 85%/1 arial, sans-serif;\n    padding: 1em 0;\n    text-align: center;\n}\n\ntable td,\ntable th {\n    border-left: 1px solid #cbcbcb;\n    border-width: 0 0 0 1px;\n    font-size: inherit;\n    margin: 0;\n    overflow: visible;\n    padding: 0.5em 1em;\n}\n\ntable thead {\n    background-color: #10a2ff;\n    color: #fff;\n    text-align: left;\n    vertical-align: bottom;\n}\n\ntable td {\n    background-color: transparent;\n}\n\n.table-odd td {\n    background-color: #f2f2f2;\n}\n\n.table-bordered td {\n    border-bottom: 1px solid #cbcbcb;\n}\n.table-bordered tbody > tr:last-child > td {\n    border-bottom-width: 0;\n}\n</style>\n`\n\n/*\n$ go run .\nVisit http://localhost:8080\n*/\nfunc main() {\n\tapp := iris.New()\n\terr := app.I18n.Load(\"./locales/*/*\", \"en-US\")\n\t// ^ here we only use a single locale for the sake of the example,\n\t// on a real app you can register as many languages as you want to support.\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.HTML(\"<html><body>\\n\")\n\t\tctx.WriteString(tableStyle)\n\t\tctx.WriteString(`<table class=\"table-bordered table-odd\">\n<thead>\n  <tr>\n    <th>Key</th>\n    <th>Translation</th>\n    <th>Arguments</th>\n  </tr>\n</thead><tbody>\n`)\n\t\tdefer ctx.WriteString(\"</tbody></table></body></html>\")\n\n\t\ttr(ctx, \"Classic\")\n\n\t\ttr(ctx, \"YouLate\", 1)\n\t\ttr(ctx, \"YouLate\", 2)\n\n\t\ttr(ctx, \"FreeDay\", 1)\n\t\ttr(ctx, \"FreeDay\", 5)\n\n\t\ttr(ctx, \"FreeDay\", 3, 15)\n\n\t\ttr(ctx, \"HeIsHome\", \"Peter\")\n\n\t\ttr(ctx, \"HouseCount\", female, 2, \"Maria\")\n\t\ttr(ctx, \"HouseCount\", male, 1, \"Peter\")\n\n\t\ttr(ctx, \"nav.home\")\n\t\ttr(ctx, \"nav.user\")\n\t\ttr(ctx, \"nav.more.what\")\n\t\ttr(ctx, \"nav.more.even.more\")\n\t\ttr(ctx, \"nav.more.even.aplural\", 1)\n\t\ttr(ctx, \"nav.more.even.aplural\", 15)\n\n\t\ttr(ctx, \"VarTemplate\", iris.Map{\n\t\t\t\"Name\":        \"Peter\",\n\t\t\t\"GenderCount\": male,\n\t\t})\n\n\t\ttr(ctx, \"VarTemplatePlural\", 1, female)\n\t\ttr(ctx, \"VarTemplatePlural\", 2, female, 1)\n\t\ttr(ctx, \"VarTemplatePlural\", 2, female, 5)\n\t\ttr(ctx, \"VarTemplatePlural\", 1, male)\n\t\ttr(ctx, \"VarTemplatePlural\", 2, male, 1)\n\t\ttr(ctx, \"VarTemplatePlural\", 2, male, 2)\n\n\t\ttr(ctx, \"VarTemplatePlural\", iris.Map{\n\t\t\t\"PluralCount\": 5,\n\t\t\t\"Names\":       []string{\"Makis\", \"Peter\"},\n\t\t\t\"InlineJoin\": func(arr []string) string {\n\t\t\t\treturn strings.Join(arr, \", \")\n\t\t\t},\n\t\t})\n\n\t\ttr(ctx, \"TemplatePlural\", iris.Map{\n\t\t\t\"PluralCount\": 1,\n\t\t\t\"Name\":        \"Peter\",\n\t\t})\n\t\ttr(ctx, \"TemplatePlural\", iris.Map{\n\t\t\t\"PluralCount\": 5,\n\t\t\t\"Names\":       []string{\"Makis\", \"Peter\"},\n\t\t\t\"InlineJoin\": func(arr []string) string {\n\t\t\t\treturn strings.Join(arr, \", \")\n\t\t\t},\n\t\t})\n\t\ttr(ctx, \"VarTemplatePlural\", 2, male, 4)\n\n\t\ttr(ctx, \"TemplateVarTemplatePlural\", iris.Map{\n\t\t\t\"PluralCount\": 3,\n\t\t\t\"DogsCount\":   5,\n\t\t})\n\n\t\ttr(ctx, \"message.HostResult\")\n\n\t\ttr(ctx, \"LocalVarsHouseCount.Text\", 3, 4)\n\t})\n\n\tapp.Listen(\":8080\")\n}\n\nfunc tr(ctx iris.Context, key string, args ...any) {\n\ttranslation := ctx.Tr(key, args...)\n\tctx.Writef(\"<tr><td>%s</td><td>%s</td><td>%v</td></tr>\\n\", key, translation, args)\n}\n"
  },
  {
    "path": "_examples/i18n/plurals/main_test.go",
    "content": "package main_test\n\nimport (\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nconst (\n\tfemale = iota + 1\n\tmale\n)\n\nfunc TestI18nPlurals(t *testing.T) {\n\thandler := func(ctx iris.Context) {\n\t\ttr(ctx, \"Classic\")\n\n\t\ttr(ctx, \"YouLate\", 1)\n\t\ttr(ctx, \"YouLate\", 2)\n\n\t\ttr(ctx, \"FreeDay\", 1)\n\t\ttr(ctx, \"FreeDay\", 5)\n\n\t\ttr(ctx, \"FreeDay\", 3, 15)\n\n\t\ttr(ctx, \"HeIsHome\", \"Peter\")\n\n\t\ttr(ctx, \"HouseCount\", female, 2, \"Maria\")\n\t\ttr(ctx, \"HouseCount\", male, 1, \"Peter\")\n\n\t\ttr(ctx, \"nav.home\")\n\t\ttr(ctx, \"nav.user\")\n\t\ttr(ctx, \"nav.more.what\")\n\t\ttr(ctx, \"nav.more.even.more\")\n\t\ttr(ctx, \"nav.more.even.aplural\", 1)\n\t\ttr(ctx, \"nav.more.even.aplural\", 15)\n\n\t\ttr(ctx, \"VarTemplate\", iris.Map{\n\t\t\t\"Name\":        \"Peter\",\n\t\t\t\"GenderCount\": male,\n\t\t})\n\n\t\ttr(ctx, \"VarTemplatePlural\", 1, female)\n\t\ttr(ctx, \"VarTemplatePlural\", 2, female, 1)\n\t\ttr(ctx, \"VarTemplatePlural\", 2, female, 5)\n\t\ttr(ctx, \"VarTemplatePlural\", 1, male)\n\t\ttr(ctx, \"VarTemplatePlural\", 2, male, 1)\n\t\ttr(ctx, \"VarTemplatePlural\", 2, male, 2)\n\n\t\ttr(ctx, \"VarTemplatePlural\", iris.Map{\n\t\t\t\"PluralCount\": 5,\n\t\t\t\"Names\":       []string{\"Makis\", \"Peter\"},\n\t\t\t\"InlineJoin\": func(arr []string) string {\n\t\t\t\treturn strings.Join(arr, \", \")\n\t\t\t},\n\t\t})\n\n\t\ttr(ctx, \"TemplatePlural\", iris.Map{\n\t\t\t\"PluralCount\": 1,\n\t\t\t\"Name\":        \"Peter\",\n\t\t})\n\t\ttr(ctx, \"TemplatePlural\", iris.Map{\n\t\t\t\"PluralCount\": 5,\n\t\t\t\"Names\":       []string{\"Makis\", \"Peter\"},\n\t\t\t\"InlineJoin\": func(arr []string) string {\n\t\t\t\treturn strings.Join(arr, \", \")\n\t\t\t},\n\t\t})\n\t\ttr(ctx, \"VarTemplatePlural\", 2, male, 4)\n\n\t\ttr(ctx, \"TemplateVarTemplatePlural\", iris.Map{\n\t\t\t\"PluralCount\": 3,\n\t\t\t\"DogsCount\":   5,\n\t\t})\n\n\t\ttr(ctx, \"message.HostResult\")\n\n\t\ttr(ctx, \"LocalVarsHouseCount.Text\", 3, 4)\n\t}\n\n\tw := httptest.NewRecorder()\n\tr := httptest.NewRequest(\"GET\", \"/\", nil)\n\tdefer r.Body.Close()\n\thttptest.Do(w, r, handler, func(app *iris.Application) {\n\t\terr := app.I18n.Load(\"./locales/*/*\", \"en-US\", \"el-GR\")\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t})\n\n\texpected := `Classic=classic\nYouLate=You are 1 minute late.\nYouLate=You are 2 minutes late.\nFreeDay=You have a day off\nFreeDay=You have 5 free days\nFreeDay=You have three days and 15 minutes off.\nHeIsHome=Peter is home\nHouseCount=She (Maria) has 2 houses\nHouseCount=He (Peter) has 1 house\nnav.home=Home\nnav.user=Account\nnav.more.what=this\nnav.more.even.more=yes\nnav.more.even.aplural=You are 1 minute late.\nnav.more.even.aplural=You are 15 minutes late.\nVarTemplate=(He) Peter is home\nVarTemplatePlural=She is awesome\nVarTemplatePlural=other (She) has 1 house\nVarTemplatePlural=other (She) has 5 houses\nVarTemplatePlural=He is awesome\nVarTemplatePlural=other (He) has 1 house\nVarTemplatePlural=other (He) has 2 houses\nVarTemplatePlural=Makis, Peter are awesome\nTemplatePlural=Peter is unique\nTemplatePlural=Makis, Peter are awesome\nVarTemplatePlural=other (He) has 4 houses\nTemplateVarTemplatePlural=These 3 are wonderful, feeding 5 dogsssss in total!\nmessage.HostResult=Store Encrypted Message Online\nLocalVarsHouseCount.Text=She has 4 houses\n`\n\tif got := w.Body.String(); expected != got {\n\t\tt.Fatalf(\"expected:\\n'%s'\\n\\nbut got:\\n'%s'\", expected, got)\n\t}\n}\n\nfunc tr(ctx iris.Context, key string, args ...any) {\n\ttranslation := ctx.Tr(key, args...)\n\tctx.Writef(\"%s=%s\\n\", key, translation)\n}\n"
  },
  {
    "path": "_examples/i18n/template/locales/el-GR/other.ini",
    "content": "[nav]\nUser = Λογαριασμός\n\n[debug]\nTitle = Μενού προγραμματιστή\nAccessLog = Πρόσβαση στο αρχείο καταγραφής\nAccessLogClear = Καθαρισμός {{tr \"debug.AccessLog\"}}\n\n[user.connections]\nTitle = {{tr \"nav.User\"}} Συνδέσεις"
  },
  {
    "path": "_examples/i18n/template/locales/el-GR/user.ini",
    "content": "[forms]\nmember = μέλος\nregister = Γίνε {{uppercase (tr \"forms.member\") }}\nregistered = εγγεγραμμένοι"
  },
  {
    "path": "_examples/i18n/template/locales/en-US/other.ini",
    "content": "# just an example of some more nested keys,\n# see /other endpoint.\n[nav]\nUser = Account\n\n[debug]\nTitle = Developer Menu\nAccessLog = Access Log\nAccessLogClear = Clear {{tr \"debug.AccessLog\"}}\n\n[user.connections]\nTitle = {{tr \"nav.User\"}} Connections"
  },
  {
    "path": "_examples/i18n/template/locales/en-US/user.ini",
    "content": "[forms]\nmember = member\nregister = Become a {{uppercase (tr \"forms.member\") }}\nregistered = registered"
  },
  {
    "path": "_examples/i18n/template/main.go",
    "content": "package main\n\nimport (\n\t\"strings\"\n\t\"text/template\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\n/*\n Iris I18n supports text/template inside the translation values.\n Follow this example to learn how to use that feature.\n*/\n\nfunc main() {\n\tapp := newApp()\n\tapp.Listen(\":8080\")\n}\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\n\t// Set custom functions per locale!\n\tapp.I18n.Loader.Funcs = func(current iris.Locale) template.FuncMap {\n\t\treturn template.FuncMap{\n\t\t\t\"uppercase\": func(word string) string {\n\t\t\t\treturn strings.ToUpper(word)\n\t\t\t},\n\t\t}\n\t}\n\n\terr := app.I18n.Load(\"./locales/*/*.ini\", \"en-US\", \"el-GR\")\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\ttext := ctx.Tr(\"forms.register\") // en-US: prints \"Become a MEMBER\".\n\t\tctx.WriteString(text)\n\t})\n\n\tapp.Get(\"/title\", func(ctx iris.Context) {\n\t\ttext := ctx.Tr(\"user.connections.Title\") // en-US: prints \"Accounts Connections\".\n\t\tctx.WriteString(text)\n\t})\n\n\treturn app\n}\n"
  },
  {
    "path": "_examples/i18n/template/main_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nfunc TestI18nLoaderFuncMap(t *testing.T) {\n\tapp := newApp()\n\n\te := httptest.New(t, app)\n\te.GET(\"/\").Expect().Status(httptest.StatusOK).\n\t\tBody().IsEqual(\"Become a MEMBER\")\n\te.GET(\"/title\").Expect().Status(httptest.StatusOK).\n\t\tBody().IsEqual(\"Account Connections\")\n\te.GET(\"/\").WithHeader(\"Accept-Language\", \"el\").Expect().Status(httptest.StatusOK).\n\t\tBody().IsEqual(\"Γίνε ΜΈΛΟΣ\")\n\te.GET(\"/title\").WithHeader(\"Accept-Language\", \"el\").Expect().Status(httptest.StatusOK).\n\t\tBody().IsEqual(\"Λογαριασμός Συνδέσεις\")\n}\n"
  },
  {
    "path": "_examples/i18n/template-embedded/embedded/locales/el-GR/other.ini",
    "content": "[nav]\nUser = Λογαριασμός\n\n[debug]\nTitle = Μενού προγραμματιστή\nAccessLog = Πρόσβαση στο αρχείο καταγραφής\nAccessLogClear = Καθαρισμός {{ tr \"debug.AccessLog\" }}\n\n[user.connections]\nTitle = {{ tr \"nav.User\" }} Συνδέσεις"
  },
  {
    "path": "_examples/i18n/template-embedded/embedded/locales/el-GR/user.ini",
    "content": "[forms]\nmember = μέλος\nregister = Γίνε {{ uppercase (tr \"forms.member\") }}\nregistered = εγγεγραμμένοι"
  },
  {
    "path": "_examples/i18n/template-embedded/embedded/locales/en-US/other.ini",
    "content": "# just an example of some more nested keys,\n# see /other endpoint.\n[nav]\nUser = Account\n\n[debug]\nTitle = Developer Menu\nAccessLog = Access Log\nAccessLogClear = Clear {{ tr \"debug.AccessLog\" }}\n\n[user.connections]\nTitle = {{ tr \"nav.User\" }} Connections"
  },
  {
    "path": "_examples/i18n/template-embedded/embedded/locales/en-US/user.ini",
    "content": "[forms]\nmember = member\nregister = Become a {{ uppercase (tr \"forms.member\") }}\nregistered = registered"
  },
  {
    "path": "_examples/i18n/template-embedded/main.go",
    "content": "package main\n\nimport (\n\t\"embed\"\n\t\"strings\"\n\t\"text/template\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\n//go:embed embedded/locales/*\nvar embeddedFS embed.FS\n\nfunc main() {\n\tapp := newApp()\n\t// http://localhost:8080\n\t// http://localhost:8080?lang=el\n\t// http://localhost:8080?lang=el\n\t// http://localhost:8080?lang=el-GR\n\t// http://localhost:8080?lang=en\n\t// http://localhost:8080?lang=en-US\n\t//\n\t// http://localhost:8080/title\n\t// http://localhost:8080/title?lang=el-GR\n\t// ...\n\tapp.Listen(\":8080\")\n}\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\n\t// Set custom functions per locale!\n\tapp.I18n.Loader.Funcs = func(current iris.Locale) template.FuncMap {\n\t\treturn template.FuncMap{\n\t\t\t\"uppercase\": func(word string) string {\n\t\t\t\treturn strings.ToUpper(word)\n\t\t\t},\n\t\t}\n\t}\n\n\t// Instead of:\n\t// err := app.I18n.Load(\"./locales/*/*.ini\", \"en-US\", \"el-GR\")\n\t// apply the below in order to build with embedded locales inside your executable binary.\n\terr := app.I18n.LoadFS(embeddedFS, \"./embedded/locales/*/*.ini\", \"en-US\", \"el-GR\")\n\tif err != nil {\n\t\tpanic(err)\n\t} // OR to load all languages by filename:\n\t// app.I18n.LoadFS(embeddedFS, \"./embedded/locales/*/*.ini\")\n\t// Then set the default language using:\n\t// app.I18n.SetDefault(\"en-US\")\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\ttext := ctx.Tr(\"forms.register\") // en-US: prints \"Become a MEMBER\".\n\t\tctx.WriteString(text)\n\t})\n\n\tapp.Get(\"/title\", func(ctx iris.Context) {\n\t\ttext := ctx.Tr(\"user.connections.Title\") // en-US: prints \"Accounts Connections\".\n\t\tctx.WriteString(text)\n\t})\n\n\treturn app\n}\n"
  },
  {
    "path": "_examples/i18n/template-embedded/main_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nfunc TestI18nLoaderFuncMap(t *testing.T) {\n\tapp := newApp()\n\n\te := httptest.New(t, app)\n\te.GET(\"/\").Expect().Status(httptest.StatusOK).\n\t\tBody().IsEqual(\"Become a MEMBER\")\n\te.GET(\"/title\").Expect().Status(httptest.StatusOK).\n\t\tBody().IsEqual(\"Account Connections\")\n\te.GET(\"/\").WithHeader(\"Accept-Language\", \"el\").Expect().Status(httptest.StatusOK).\n\t\tBody().IsEqual(\"Γίνε ΜΈΛΟΣ\")\n\te.GET(\"/title\").WithHeader(\"Accept-Language\", \"el\").Expect().Status(httptest.StatusOK).\n\t\tBody().IsEqual(\"Λογαριασμός Συνδέσεις\")\n}\n"
  },
  {
    "path": "_examples/kafka-api/Dockerfile",
    "content": "# docker build -t myapp . \n# docker run --rm -it -p 8080:8080 myapp:latest\nFROM golang:latest AS builder\nRUN apt-get update\nENV GO111MODULE=on \\\n    CGO_ENABLED=0 \\\n    GOOS=linux \\\n    GOARCH=amd64\nWORKDIR /go/src/app\nCOPY go.mod .\nRUN go mod download\nCOPY . .\nRUN go install\n\nFROM scratch\nCOPY --from=builder /go/bin/myapp .\nENTRYPOINT [\"./myapp\"]"
  },
  {
    "path": "_examples/kafka-api/README.md",
    "content": "# Writing an API for Apache Kafka with Iris\n\nRead the [code](main.go).\n\n## Docker\n\n1. Open [docker-compose.yml](docker-compose.yml) and replace `KAFKA_ADVERTISED_HOST_NAME` with your own local address\n2. Install [Docker](https://www.docker.com/)\n3. Execute the command below to start kafka stack and the go application:\n\n```sh\n$ docker-compose up\n```\n\n### Troubleshooting\n\nOn windows, if you get an error of `An attempt was made to access a socket in a way forbidden by its access permissions`\n\nSolution:\n\n1. Stop Docker\n2. Open CMD with Administrator privileges and execute the following commands:\n\n```sh\n$ dism.exe /Online /Disable-Feature:Microsoft-Hyper-V\n$ netsh int ipv4 add excludedportrange protocol=tcp startport=2181 numberofports=1\n$ dism.exe /Online /Enable-Feature:Microsoft-Hyper-V /All\n$ docker-compose up --build\n```\n\n## Manually\n\nInstall & run Kafka and Zookeper locally and then:\n\n```sh\ngo run main.go\n```\n\n## Screens\n\n![](0_docs.png)\n\n![](1_create_topic.png)\n\n![](2_list_topics.png)\n\n![](3_store_to_topic.png)\n\n![](4_retrieve_from_topic_real_time.png)\n"
  },
  {
    "path": "_examples/kafka-api/docker-compose.yml",
    "content": "version: '3.1'\n\nservices:\n  zookeeper:\n    image: wurstmeister/zookeeper\n    ports:\n      - 2181:2181\n  kafka:\n    image: wurstmeister/kafka\n    ports:\n      - 9092:9092\n    environment:\n      KAFKA_ADVERTISED_HOST_NAME: 10.122.1.142 # replace that with your own local ipv4 addr.\n      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181\n  # kafka:\n  #   image: confluentinc/cp-kafka:5.5.0\n  #   hostname: kafka\n  #   ports:\n  #     - 9092:9092\n  #   environment:\n  #     KAFKA_ADVERTISED_LISTENERS: LISTENER_DOCKER_INTERNAL://kafka:19092,LISTENER_DOCKER_EXTERNAL://${DOCKER_HOST_IP:-127.0.0.1}:9092\n  app:\n    build: .\n    ports:\n      - 8080:8080\n    environment: \n      KAFKA_1: kafka:9092\n    depends_on:\n      - kafka"
  },
  {
    "path": "_examples/kafka-api/go.mod",
    "content": "module myapp\n\ngo 1.25\n\nrequire (\n\tgithub.com/IBM/sarama v1.46.3\n\tgithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd\n)\n\nrequire (\n\tgithub.com/BurntSushi/toml v1.6.0 // indirect\n\tgithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect\n\tgithub.com/CloudyKit/jet/v6 v6.3.1 // indirect\n\tgithub.com/Joker/jade v1.1.3 // indirect\n\tgithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 // indirect\n\tgithub.com/andybalholm/brotli v1.2.0 // indirect\n\tgithub.com/aymerick/douceur v0.2.0 // indirect\n\tgithub.com/davecgh/go-spew v1.1.1 // indirect\n\tgithub.com/eapache/go-resiliency v1.7.0 // indirect\n\tgithub.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3 // indirect\n\tgithub.com/eapache/queue v1.1.0 // indirect\n\tgithub.com/fatih/structs v1.1.0 // indirect\n\tgithub.com/flosch/pongo2/v4 v4.0.2 // indirect\n\tgithub.com/golang/snappy v1.0.0 // indirect\n\tgithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a // indirect\n\tgithub.com/google/uuid v1.6.0 // indirect\n\tgithub.com/gorilla/css v1.0.1 // indirect\n\tgithub.com/hashicorp/go-uuid v1.0.3 // indirect\n\tgithub.com/iris-contrib/schema v0.0.6 // indirect\n\tgithub.com/jcmturner/aescts/v2 v2.0.0 // indirect\n\tgithub.com/jcmturner/dnsutils/v2 v2.0.0 // indirect\n\tgithub.com/jcmturner/gofork v1.7.6 // indirect\n\tgithub.com/jcmturner/gokrb5/v8 v8.4.4 // indirect\n\tgithub.com/jcmturner/rpc/v2 v2.0.3 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/kataras/blocks v0.0.8 // indirect\n\tgithub.com/kataras/golog v0.1.12 // indirect\n\tgithub.com/kataras/pio v0.0.13 // indirect\n\tgithub.com/kataras/sitemap v0.0.6 // indirect\n\tgithub.com/kataras/tunnel v0.0.4 // indirect\n\tgithub.com/klauspost/compress v1.18.2 // indirect\n\tgithub.com/kr/pretty v0.3.1 // indirect\n\tgithub.com/mailgun/raymond/v2 v2.0.48 // indirect\n\tgithub.com/mailru/easyjson v0.9.1 // indirect\n\tgithub.com/microcosm-cc/bluemonday v1.0.27 // indirect\n\tgithub.com/pierrec/lz4/v4 v4.1.22 // indirect\n\tgithub.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9 // indirect\n\tgithub.com/rogpeppe/go-internal v1.10.0 // indirect\n\tgithub.com/russross/blackfriday/v2 v2.1.0 // indirect\n\tgithub.com/schollz/closestmatch v2.1.0+incompatible // indirect\n\tgithub.com/sirupsen/logrus v1.8.1 // indirect\n\tgithub.com/tdewolff/minify/v2 v2.24.8 // indirect\n\tgithub.com/tdewolff/parse/v2 v2.8.5 // indirect\n\tgithub.com/valyala/bytebufferpool v1.0.0 // indirect\n\tgithub.com/vmihailenco/msgpack/v5 v5.4.1 // indirect\n\tgithub.com/vmihailenco/tagparser/v2 v2.0.0 // indirect\n\tgithub.com/yosssi/ace v0.0.5 // indirect\n\tgolang.org/x/crypto v0.47.0 // indirect\n\tgolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 // indirect\n\tgolang.org/x/net v0.49.0 // indirect\n\tgolang.org/x/sys v0.40.0 // indirect\n\tgolang.org/x/text v0.33.0 // indirect\n\tgolang.org/x/time v0.14.0 // indirect\n\tgoogle.golang.org/protobuf v1.36.11 // indirect\n\tgopkg.in/ini.v1 v1.67.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n"
  },
  {
    "path": "_examples/kafka-api/go.sum",
    "content": "github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=\ngithub.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=\ngithub.com/CloudyKit/jet/v6 v6.3.1 h1:6IAo5Cx21xrHVaR8zzXN5gJatKV/wO7Nf6bfCnCSbUw=\ngithub.com/CloudyKit/jet/v6 v6.3.1/go.mod h1:lf8ksdNsxZt7/yH/3n4vJQWA9RUq4wpaHtArHhGVMOw=\ngithub.com/IBM/sarama v1.46.3 h1:njRsX6jNlnR+ClJ8XmkO+CM4unbrNr/2vB5KK6UA+IE=\ngithub.com/IBM/sarama v1.46.3/go.mod h1:GTUYiF9DMOZVe3FwyGT+dtSPceGFIgA+sPc5u6CBwko=\ngithub.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc=\ngithub.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=\ngithub.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=\ngithub.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 h1:dG7/gLroJGht/jSQtHiLvT48Hxn+crbmvyItZC8cWOs=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05/go.mod h1:NYezi6wtnJtBm5btoprXc5SvAdqH0XTXWnUup0MptAI=\ngithub.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=\ngithub.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=\ngithub.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=\ngithub.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=\ngithub.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=\ngithub.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/eapache/go-resiliency v1.7.0 h1:n3NRTnBn5N0Cbi/IeOHuQn9s2UwVUH7Ga0ZWcP+9JTA=\ngithub.com/eapache/go-resiliency v1.7.0/go.mod h1:5yPzW0MIvSe0JDsv0v+DvcjEv2FyD6iZYSs1ZI+iQho=\ngithub.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3 h1:Oy0F4ALJ04o5Qqpdz8XLIpNA3WM/iSIXqxtqo7UGVws=\ngithub.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3/go.mod h1:YvSRo5mw33fLEx1+DlK6L2VV43tJt5Eyel9n9XBcR+0=\ngithub.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc=\ngithub.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=\ngithub.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=\ngithub.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=\ngithub.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=\ngithub.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=\ngithub.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw=\ngithub.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8=\ngithub.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=\ngithub.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=\ngithub.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=\ngithub.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=\ngithub.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=\ngithub.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a h1:l7A0loSszR5zHd/qK53ZIHMO8b3bBSmENnQ6eKnUT0A=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=\ngithub.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=\ngithub.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=\ngithub.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=\ngithub.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=\ngithub.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=\ngithub.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=\ngithub.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=\ngithub.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2 h1:T9THsdP1woyAqKHwjkEsbCnMefsAFvk8iJJKokcJ3Go=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2/go.mod h1:JLDgIqnFy5loDSUv1OA2j0mb6p/rDhiCqigP22Uq9xE=\ngithub.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw=\ngithub.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA=\ngithub.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8=\ngithub.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=\ngithub.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo=\ngithub.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM=\ngithub.com/jcmturner/gofork v1.7.6 h1:QH0l3hzAU1tfT3rZCnW5zXl+orbkNMMRGJfdJjHVETg=\ngithub.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo=\ngithub.com/jcmturner/goidentity/v6 v6.0.1 h1:VKnZd2oEIMorCTsFBnJWbExfNN7yZr3EhJAxwOkZg6o=\ngithub.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg=\ngithub.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh687T8=\ngithub.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs=\ngithub.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY=\ngithub.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/kataras/blocks v0.0.8 h1:MrpVhoFTCR2v1iOOfGng5VJSILKeZZI+7NGfxEh3SUM=\ngithub.com/kataras/blocks v0.0.8/go.mod h1:9Jm5zx6BB+06NwA+OhTbHW1xkMOYxahnqTN5DveZ2Yg=\ngithub.com/kataras/golog v0.1.12 h1:Bu7I/G4ilJlbfzjmU39O9N+2uO1pBcMK045fzZ4ytNg=\ngithub.com/kataras/golog v0.1.12/go.mod h1:wrGSbOiBqbQSQznleVNX4epWM8rl9SJ/rmEacl0yqy4=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd h1:oMsQgqKACbUdlaIWnN+EZzzAnHkw90hE/y/R0BSij2U=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd/go.mod h1:yucjtDl9Vn3OctWM1fi0MuM9OckemC7kEreFa9QtPF8=\ngithub.com/kataras/pio v0.0.13 h1:x0rXVX0fviDTXOOLOmr4MUxOabu1InVSTu5itF8CXCM=\ngithub.com/kataras/pio v0.0.13/go.mod h1:k3HNuSw+eJ8Pm2lA4lRhg3DiCjVgHlP8hmXApSej3oM=\ngithub.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY=\ngithub.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4=\ngithub.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA=\ngithub.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw=\ngithub.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=\ngithub.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=\ngithub.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=\ngithub.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8=\ngithub.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=\ngithub.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=\ngithub.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=\ngithub.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=\ngithub.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=\ngithub.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=\ngithub.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=\ngithub.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=\ngithub.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=\ngithub.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9 h1:bsUq1dX0N8AOIL7EB/X911+m4EHsnWEHeJ0c+3TTBrg=\ngithub.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=\ngithub.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=\ngithub.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=\ngithub.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=\ngithub.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=\ngithub.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=\ngithub.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk=\ngithub.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=\ngithub.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=\ngithub.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=\ngithub.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=\ngithub.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=\ngithub.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=\ngithub.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/tdewolff/minify/v2 v2.24.8 h1:58/VjsbevI4d5FGV0ZSuBrHMSSkH4MCH0sIz/eKIauE=\ngithub.com/tdewolff/minify/v2 v2.24.8/go.mod h1:0Ukj0CRpo/sW/nd8uZ4ccXaV1rEVIWA3dj8U7+Shhfw=\ngithub.com/tdewolff/parse/v2 v2.8.5 h1:ZmBiA/8Do5Rpk7bDye0jbbDUpXXbCdc3iah4VeUvwYU=\ngithub.com/tdewolff/parse/v2 v2.8.5/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=\ngithub.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE=\ngithub.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=\ngithub.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=\ngithub.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=\ngithub.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=\ngithub.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=\ngithub.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=\ngithub.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=\ngithub.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA=\ngithub.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0=\ngithub.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=\ngithub.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=\ngithub.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngithub.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=\ngolang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=\ngolang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=\ngolang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=\ngolang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=\ngolang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=\ngolang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=\ngolang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=\ngolang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=\ngolang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=\ngolang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=\ngolang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\ngolang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=\ngolang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=\ngolang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=\ngolang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=\ngolang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=\ngolang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=\ngoogle.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=\ngopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nmoul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs=\nmoul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE=\n"
  },
  {
    "path": "_examples/kafka-api/main.go",
    "content": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/IBM/sarama\"\n\t\"github.com/kataras/iris/v12\"\n)\n\n/*\nFirst of all, read about Apache Kafka, install and run it, if you didn't already: https://kafka.apache.org/quickstart\n\nSecondly, install your favourite Go library for Apache Kafka communication.\nI have chosen the shopify's one although I really loved the `segmentio/kafka-go` as well but it needs more to be done there\nand you will be bored to read all the necessary code required to get started with it, so:\n\t$ go get -u github.com/IBM/sarama\n\nThe minimum Apache Kafka broker(s) version required is 0.10.0.0 but 0.11.x+ is recommended (tested with 2.5.0).\n\nResources:\n\t- https://github.com/apache/kafka\n\t- https://github.com/IBM/sarama/blob/master/examples/http_server/http_server.go\n\t- DIY\n*/\n\n// package-level variables for the sake of the example\n// but you can define them inside your main func\n// and pass around this config whenever you need to create a client or a producer or a consumer or use a cluster.\nvar (\n\t// The Kafka brokers to connect to, as a comma separated list.\n\tbrokers = []string{getenv(\"KAFKA_1\", \"localhost:9092\")}\n\t// The config which makes our live easier when passing around, it pre-mades a lot of things for us.\n\tconfig *sarama.Config\n)\n\nfunc getenv(key string, def string) string {\n\tif value := os.Getenv(key); value != \"\" {\n\t\treturn value\n\t}\n\n\treturn def\n}\n\nfunc init() {\n\tconfig = sarama.NewConfig()\n\tconfig.ClientID = \"iris-example-client\"\n\tconfig.Version = sarama.V0_11_0_2\n\t// config.Producer.RequiredAcks = sarama.WaitForAll // Wait for all in-sync replicas to ack the message.\n\tconfig.Producer.Compression = sarama.CompressionSnappy\n\tconfig.Producer.Flush.Frequency = 500 * time.Millisecond\n\tconfig.Producer.Retry.Max = 10 // Retry up to 10 times to produce the message.\n\tconfig.Producer.Return.Successes = true\n\n\t// for SASL/basic plain text authentication: config.Net.SASL.\n\t// config.Net.SASL.Enable = true\n\t// config.Net.SASL.Handshake = false\n\t// config.Net.SASL.User = \"myuser\"\n\t// config.Net.SASL.Password = \"mypass\"\n\n\tconfig.Consumer.Return.Errors = true\n}\n\nfunc main() {\n\tapp := iris.New()\n\tapp.OnErrorCode(iris.StatusNotFound, handleNotFound)\n\n\tv1 := app.Party(\"/api/v1\")\n\t{\n\t\ttopicsAPI := v1.Party(\"/topics\")\n\t\t{\n\t\t\ttopicsAPI.Post(\"/\", postTopicsHandler) // create a topic.\n\t\t\ttopicsAPI.Get(\"/\", getTopicsHandler)   // list all topics.\n\n\t\t\ttopicsAPI.Post(\"/{topic}/produce\", postTopicProduceHandler)  // store to a topic.\n\t\t\ttopicsAPI.Get(\"/{topic}/consume\", getTopicConsumeSSEHandler) // retrieve all messages from a topic.\n\t\t}\n\t}\n\n\tapp.Get(\"/\", docsHandler)\n\n\tapp.Logger().Infof(\"Brokers: %s\", strings.Join(brokers, \", \"))\n\t// GET      : http://localhost:8080\n\t// POST, GET: http://localhost:8080/api/v1/topics\n\t// POST     : http://localhost:8080/api/v1/topics/{topic}/produce?key=my-key\n\t// GET      : http://localhost:8080/api/v1/topics/{topic}/consume?partition=0&offset=0\n\tapp.Listen(\":8080\")\n}\n\n// simple use-case, you can use templates and views obviously, see the \"_examples/views\" examples.\nfunc docsHandler(ctx iris.Context) {\n\tctx.ContentType(\"text/html\") // or ctx.HTML(fmt.Sprintf(...))\n\tctx.Writef(`<!DOCTYPE html>\n\t<html>\n\t\t<head>\n\t\t\t<style>\n\t\t\t\tth, td {\n\t\t\t\t\tborder: 1px solid black;\n\t\t\t\t\tpadding: 15px;\n\t\t\t\t\ttext-align: left;\n\t\t\t\t}\n\t\t\t</style>\n\t\t</head>`)\n\tdefer ctx.Writef(\"</html>\")\n\n\tctx.Writef(\"<body>\")\n\tdefer ctx.Writef(\"</body>\")\n\n\tctx.Writef(`\n\t<table>\n\t\t<tr>\n\t\t\t<th>Method</th>\n\t\t\t<th>Path</th>\n\t\t\t<th>Handler</th>\n\t\t</tr>\n\t`)\n\tdefer ctx.Writef(`</table>`)\n\n\tregisteredRoutes := ctx.Application().GetRoutesReadOnly()\n\tfor _, r := range registeredRoutes {\n\t\tif r.Path() == \"/\" { // don't list the root, current one.\n\t\t\tcontinue\n\t\t}\n\n\t\tctx.Writef(`\n\t\t\t<tr>\n\t\t\t\t<td>%s</td>\n\t\t\t\t<td>%s%s</td>\n\t\t\t\t<td>%s</td>\n\t\t\t</tr>\n\t\t`, r.Method(), ctx.Host(), r.Path(), r.MainHandlerName())\n\t}\n}\n\ntype httpError struct {\n\tCode   int    `json:\"code\"`\n\tReason string `json:\"reason,omitempty\"`\n}\n\nfunc (h httpError) Error() string {\n\treturn fmt.Sprintf(\"Status Code: %d\\nReason: %s\", h.Code, h.Reason)\n}\n\nfunc fail(ctx iris.Context, statusCode int, format string, a ...any) {\n\treason := \"unspecified\"\n\tif format != \"\" {\n\t\treason = fmt.Sprintf(format, a...)\n\t}\n\n\terr := httpError{\n\t\tCode:   statusCode,\n\t\tReason: reason,\n\t}\n\n\tctx.StopWithJSON(statusCode, err)\n}\n\nfunc handleNotFound(ctx iris.Context) {\n\tsuggestPaths := ctx.FindClosest(3)\n\tif len(suggestPaths) == 0 {\n\t\tctx.WriteString(\"not found\")\n\t\treturn\n\t}\n\n\tctx.HTML(\"Did you mean?<ul>\")\n\tfor _, s := range suggestPaths {\n\t\tctx.HTML(`<li><a href=\"%s\">%s</a></li>`, s, s)\n\t}\n\tctx.HTML(\"</ul>\")\n}\n\n// Topic the payload for a kafka topic creation.\ntype Topic struct {\n\tTopic             string `json:\"topic\"`\n\tPartitions        int32  `json:\"partitions\"`\n\tReplicationFactor int16  `json:\"replication\"`\n\tConfigs           []kv   `json:\"configs,omitempty\"`\n}\n\ntype kv struct {\n\tKey   string `json:\"key\"`\n\tValue string `json:\"value\"`\n}\n\nfunc createKafkaTopic(t Topic) error {\n\tcluster, err := sarama.NewClusterAdmin(brokers, config)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer cluster.Close()\n\n\ttopicName := t.Topic\n\ttopicDetail := sarama.TopicDetail{\n\t\tNumPartitions:     t.Partitions,\n\t\tReplicationFactor: t.ReplicationFactor,\n\t}\n\n\tif len(t.Configs) > 0 {\n\t\ttopicDetail.ConfigEntries = make(map[string]*string, len(t.Configs))\n\t\tfor _, c := range t.Configs {\n\t\t\ttopicDetail.ConfigEntries[c.Key] = &c.Value // generate a ptr, or fill a new(string) with it and use that.\n\t\t}\n\t}\n\n\treturn cluster.CreateTopic(topicName, &topicDetail, false)\n}\n\nfunc postTopicsHandler(ctx iris.Context) {\n\tvar t Topic\n\terr := ctx.ReadJSON(&t)\n\tif err != nil {\n\t\tfail(ctx, iris.StatusBadRequest,\n\t\t\t\"received invalid topic payload: %v\", err)\n\t\treturn\n\t}\n\n\t// try to create the topic inside kafka.\n\terr = createKafkaTopic(t)\n\tif err != nil {\n\t\tfail(ctx, iris.StatusInternalServerError,\n\t\t\t\"unable to create topic: %v\", err)\n\t\treturn\n\t}\n\n\tctx.StatusCode(iris.StatusCreated)\n\tctx.Writef(\"Topic %q created\", t.Topic)\n}\n\nfunc getKafkaTopics() ([]string, error) {\n\tclient, err := sarama.NewClient(brokers, config)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer client.Close()\n\n\treturn client.Topics()\n}\n\nfunc getTopicsHandler(ctx iris.Context) {\n\ttopics, err := getKafkaTopics()\n\tif err != nil {\n\t\tfail(ctx, iris.StatusInternalServerError,\n\t\t\t\"unable to retrieve topics: %v\", err)\n\t\treturn\n\t}\n\n\tctx.JSON(topics)\n}\n\nfunc produceKafkaMessage(toTopic string, key string, value []byte) (partition int32, offset int64, err error) {\n\t// On the broker side, you may want to change the following settings to get\n\t// stronger consistency guarantees:\n\t// - For your broker, set `unclean.leader.election.enable` to false\n\t// - For the topic, you could increase `min.insync.replicas`.\n\n\tproducer, err := sarama.NewSyncProducer(brokers, config)\n\tif err != nil {\n\t\treturn -1, -1, err\n\t}\n\tdefer producer.Close()\n\n\t// We are not setting a message key, which means that all messages will\n\t// be distributed randomly over the different partitions.\n\treturn producer.SendMessage(&sarama.ProducerMessage{\n\t\tTopic: toTopic,\n\t\tKey:   sarama.StringEncoder(key),\n\t\tValue: sarama.ByteEncoder(value),\n\t})\n}\n\nfunc postTopicProduceHandler(ctx iris.Context) {\n\ttopicName := ctx.Params().Get(\"topic\")\n\tkey := ctx.URLParamDefault(\"key\", \"default\")\n\n\t// read the request data and store them as they are (not recommended in production ofcourse, do your own checks here).\n\tbody, err := ctx.GetBody()\n\tif err != nil {\n\t\tfail(ctx, iris.StatusUnprocessableEntity, \"unable to read your data: %v\", err)\n\t\treturn\n\t}\n\n\tpartition, offset, err := produceKafkaMessage(topicName, key, body)\n\tif err != nil {\n\t\tfail(ctx, iris.StatusInternalServerError, \"failed to store your data: %v\", err)\n\t\treturn\n\t}\n\n\t// The tuple (topic, partition, offset) can be used as a unique identifier\n\t// for a message in a Kafka cluster.\n\tctx.Writef(\"Your data is stored with unique identifier: %s/%d/%d\", topicName, partition, offset)\n}\n\ntype message struct {\n\tTime time.Time `json:\"time\"`\n\tKey  string    `json:\"key\"`\n\t// Value []byte/json.RawMessage(if you are sure that you are sending only JSON)    `json:\"value\"`\n\t// or:\n\tValue string `json:\"value\"` // for simple key-value storage.\n}\n\nfunc getTopicConsumeSSEHandler(ctx iris.Context) {\n\tflusher, ok := ctx.ResponseWriter().Flusher()\n\tif !ok {\n\t\tctx.StopWithText(iris.StatusHTTPVersionNotSupported, \"streaming unsupported\")\n\t\treturn\n\t}\n\n\tctx.ContentType(\"application/json, text/event-stream\")\n\tctx.Header(\"Cache-Control\", \"no-cache\")\n\tctx.Header(\"Connection\", \"keep-alive\")\n\n\tmaster, err := sarama.NewConsumer(brokers, config)\n\tif err != nil {\n\t\tfail(ctx, iris.StatusInternalServerError, \"unable to start master consumer: %v\", err)\n\t\treturn\n\t}\n\n\tfromTopic := ctx.Params().Get(\"topic\")\n\t// take the partition, defaults to the first found if not url query parameter \"partition\" passed.\n\tvar partition int32\n\tpartitions, err := master.Partitions(fromTopic)\n\tif err != nil {\n\t\tmaster.Close()\n\t\tfail(ctx, iris.StatusInternalServerError, \"unable to get partitions for topic: '%s': %v\", fromTopic, err)\n\t\treturn\n\t}\n\n\tif len(partitions) > 0 {\n\t\tpartition = partitions[0]\n\t}\n\n\tpartition = ctx.URLParamInt32Default(\"partition\", partition)\n\toffset := ctx.URLParamInt64Default(\"offset\", sarama.OffsetOldest)\n\n\tconsumer, err := master.ConsumePartition(fromTopic, partition, offset)\n\tif err != nil {\n\t\tctx.Application().Logger().Error(err)\n\t\tmaster.Close() // close the master here to avoid any leaks, we will exit.\n\t\tfail(ctx, iris.StatusInternalServerError, \"unable to start partition consumer: %v\", err)\n\t\treturn\n\t}\n\n\t// `OnClose` fires when the request is finally done (all data read and handler exits) or interrupted by the user.\n\tctx.OnClose(func(_ iris.Context) {\n\t\tctx.Application().Logger().Warnf(\"a client left\")\n\n\t\t// Close shuts down the consumer. It must be called after all child\n\t\t// PartitionConsumers have already been closed. <-- That is what\n\t\t// godocs says but it doesn't work like this.\n\t\t// if err = consumer.Close(); err != nil {\n\t\t// \tctx.Application().Logger().Errorf(\"[%s] unable to close partition consumer: %v\", ctx.RemoteAddr(), err)\n\t\t// }\n\t\t// so close the master only and omit the first ^ consumer.Close:\n\t\tif err = master.Close(); err != nil {\n\t\t\tctx.Application().Logger().Errorf(\"[%s] unable to close master consumer: %v\", ctx.RemoteAddr(), err)\n\t\t}\n\t})\n\n\tfor {\n\t\tselect {\n\t\tcase consumerErr, ok := <-consumer.Errors():\n\t\t\tif !ok {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tctx.Writef(\"data: error: {\\\"reason\\\": \\\"%s\\\"}\\n\\n\", consumerErr.Error())\n\t\t\tflusher.Flush()\n\t\tcase incoming, ok := <-consumer.Messages():\n\t\t\tif !ok {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tmsg := message{\n\t\t\t\tTime:  incoming.Timestamp,\n\t\t\t\tKey:   string(incoming.Key),\n\t\t\t\tValue: string(incoming.Value),\n\t\t\t}\n\n\t\t\tb, err := json.Marshal(msg)\n\t\t\tif err != nil {\n\t\t\t\tctx.Application().Logger().Error(err)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tctx.Writef(\"data: %s\\n\\n\", b)\n\t\t\tflusher.Flush()\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "_examples/kafka-api/postman_collection.json",
    "content": "{\n\t\"info\": {\n\t\t\"_postman_id\": \"8b135d95-ea8c-4dd5-a127-4b83cb735504\",\n\t\t\"name\": \"iris-kafka-postman\",\n\t\t\"description\": \"Postman API Requests for Iris + Kafka example\",\n\t\t\"schema\": \"https://schema.getpostman.com/json/collection/v2.1.0/collection.json\"\n\t},\n\t\"item\": [\n\t\t{\n\t\t\t\"name\": \"Create Topic\",\n\t\t\t\"request\": {\n\t\t\t\t\"method\": \"POST\",\n\t\t\t\t\"header\": [],\n\t\t\t\t\"body\": {\n\t\t\t\t\t\"mode\": \"raw\",\n\t\t\t\t\t\"raw\": \"{\\r\\n    \\\"topic\\\":\\\"mytopic\\\",\\r\\n    \\\"partitions\\\": 1,\\r\\n    \\\"replication\\\":1\\r\\n}\",\n\t\t\t\t\t\"options\": {\n\t\t\t\t\t\t\"raw\": {\n\t\t\t\t\t\t\t\"language\": \"json\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"raw\": \"http://localhost:8080/api/v1/topics\",\n\t\t\t\t\t\"protocol\": \"http\",\n\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t],\n\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\"api\",\n\t\t\t\t\t\t\"v1\",\n\t\t\t\t\t\t\"topics\"\n\t\t\t\t\t]\n\t\t\t\t},\n\t\t\t\t\"description\": \"Create a new kafka topic\"\n\t\t\t},\n\t\t\t\"response\": []\n\t\t},\n\t\t{\n\t\t\t\"name\": \"List all Topics\",\n\t\t\t\"request\": {\n\t\t\t\t\"method\": \"GET\",\n\t\t\t\t\"header\": [],\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"raw\": \"http://localhost:8080/api/v1/topics\",\n\t\t\t\t\t\"protocol\": \"http\",\n\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t],\n\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\"api\",\n\t\t\t\t\t\t\"v1\",\n\t\t\t\t\t\t\"topics\"\n\t\t\t\t\t]\n\t\t\t\t},\n\t\t\t\t\"description\": \"List all topics\"\n\t\t\t},\n\t\t\t\"response\": []\n\t\t},\n\t\t{\n\t\t\t\"name\": \"Store data to Topic\",\n\t\t\t\"request\": {\n\t\t\t\t\"method\": \"POST\",\n\t\t\t\t\"header\": [],\n\t\t\t\t\"body\": {\n\t\t\t\t\t\"mode\": \"raw\",\n\t\t\t\t\t\"raw\": \"{\\r\\n    \\\"username\\\":\\\"kataras\\\",\\r\\n    \\\"repo\\\":\\\"iris\\\"\\r\\n}\",\n\t\t\t\t\t\"options\": {\n\t\t\t\t\t\t\"raw\": {\n\t\t\t\t\t\t\t\"language\": \"json\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"raw\": \"http://localhost:8080/api/v1/topics/mytopic/produce?key=mykey\",\n\t\t\t\t\t\"protocol\": \"http\",\n\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t],\n\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\"api\",\n\t\t\t\t\t\t\"v1\",\n\t\t\t\t\t\t\"topics\",\n\t\t\t\t\t\t\"mytopic\",\n\t\t\t\t\t\t\"produce\"\n\t\t\t\t\t],\n\t\t\t\t\t\"query\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"key\": \"key\",\n\t\t\t\t\t\t\t\"value\": \"mykey\"\n\t\t\t\t\t\t}\n\t\t\t\t\t]\n\t\t\t\t},\n\t\t\t\t\"description\": \"Produce some data to a Topic\"\n\t\t\t},\n\t\t\t\"response\": []\n\t\t},\n\t\t{\n\t\t\t\"name\": \"(Open in Browser) Consume data from a Topic\",\n\t\t\t\"request\": {\n\t\t\t\t\"method\": \"GET\",\n\t\t\t\t\"header\": [],\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"raw\": \"http://localhost:8080/api/v1/topics/mytopic/consume?partition=0&offset=0\",\n\t\t\t\t\t\"protocol\": \"http\",\n\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t],\n\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\"api\",\n\t\t\t\t\t\t\"v1\",\n\t\t\t\t\t\t\"topics\",\n\t\t\t\t\t\t\"mytopic\",\n\t\t\t\t\t\t\"consume\"\n\t\t\t\t\t],\n\t\t\t\t\t\"query\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"key\": \"partition\",\n\t\t\t\t\t\t\t\"value\": \"0\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"key\": \"offset\",\n\t\t\t\t\t\t\t\"value\": \"0\"\n\t\t\t\t\t\t}\n\t\t\t\t\t]\n\t\t\t\t},\n\t\t\t\t\"description\": \"Note that, you have to open this one at your browser. Postman does not support SSE testing, see: https://github.com/postmanlabs/postman-app-support/issues/6682\"\n\t\t\t},\n\t\t\t\"response\": []\n\t\t}\n\t],\n\t\"protocolProfileBehavior\": {}\n}"
  },
  {
    "path": "_examples/logging/file-logger/main.go",
    "content": "package main\n\nimport (\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tf := newLogFile()\n\tdefer f.Close()\n\n\tapp := iris.New()\n\t// Attach the file as logger, remember, iris' app logger is just an io.Writer.\n\t// Use the following code if you need to write the logs to file and console at the same time.\n\t// app.Logger().SetOutput(io.MultiWriter(f, os.Stdout))\n\tapp.Logger().SetOutput(f)\n\n\tapp.Get(\"/ping\", func(ctx iris.Context) {\n\t\t// for the sake of simplicity, in order see the logs at the ./_today_.txt\n\t\tctx.Application().Logger().Infof(\"Request path: %s\", ctx.Path())\n\t\tctx.WriteString(\"pong\")\n\t})\n\n\t// Navigate to http://localhost:8080/ping\n\t// and open the ./logs{TODAY}.txt file.\n\tif err := app.Listen(\":8080\", iris.WithoutBanner); err != nil {\n\t\tapp.Logger().Warn(\"Shutdown with error: \" + err.Error())\n\t}\n}\n\n// Get a filename based on the date, just for the sugar.\nfunc todayFilename() string {\n\ttoday := time.Now().Format(\"Jan 02 2006\")\n\treturn today + \".txt\"\n}\n\nfunc newLogFile() *os.File {\n\tfilename := todayFilename()\n\t// Open the file, this will append to the today's file if server restarted.\n\tf, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treturn f\n}\n"
  },
  {
    "path": "_examples/logging/json-logger/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/middleware/requestid\"\n\n\t\"github.com/kataras/golog\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.Logger().SetLevel(\"debug\")\n\tapp.Logger().SetFormat(\"json\", \"    \")\n\t// to register a custom Formatter:\n\t// app.Logger().RegisterFormatter(golog.Formatter...)\n\n\t// Also, see app.Logger().SetLevelOutput(level string, w io.Writer)\n\t// to set a custom writer for a specific level.\n\n\tapp.Use(requestid.New())\n\n\t/* Example Output:\n\t{\n\t    \"timestamp\": 1591422944,\n\t    \"level\": \"debug\",\n\t    \"message\": \"This is a message with data\",\n\t    \"fields\": {\n\t        \"username\": \"kataras\"\n\t    },\n\t    \"stacktrace\": [\n\t        {\n\t            \"function\": \"main.main\",\n\t            \"source\": \"C:/mygopath/src/github.com/kataras/iris/_examples/logging/json-logger/main.go:16\"\n\t        }\n\t    ]\n\t}\n\t*/\n\tapp.Logger().Debugf(\"This is a %s with data (debug prints the stacktrace too)\", \"message\", golog.Fields{\n\t\t\"username\": \"kataras\",\n\t})\n\n\t/* Example Output:\n\t{\n\t    \"timestamp\": 1591422944,\n\t    \"level\": \"info\",\n\t    \"message\": \"An info message\",\n\t    \"fields\": {\n\t        \"home\": \"https://iris-go.com\"\n\t    }\n\t}\n\t*/\n\tapp.Logger().Infof(\"An info message\", golog.Fields{\"home\": \"https://iris-go.com\"})\n\n\tapp.Get(\"/ping\", ping)\n\n\t// Navigate to http://localhost:8080/ping.\n\tapp.Listen(\":8080\" /*, iris.WithoutBanner*/)\n}\n\nfunc ping(ctx iris.Context) {\n\t/* Example Output:\n\t{\n\t    \"timestamp\": 1591423046,\n\t    \"level\": \"debug\",\n\t    \"message\": \"Request path: /ping\",\n\t    \"fields\": {\n\t        \"request_id\": \"fc12d88a-a338-4bb9-aa5e-126f2104365c\"\n\t    },\n\t    \"stacktrace\": [\n\t        {\n\t            \"function\": \"main.ping\",\n\t            \"source\": \"C:/mygopath/src/github.com/kataras/iris/_examples/logging/json-logger/main.go:82\"\n\t        },\n\t       ...\n\t    ]\n\t}\n\t*/\n\tctx.Application().Logger().Debugf(\"Request path: %s\", ctx.Path(), golog.Fields{\n\t\t\"request_id\": ctx.GetID(),\n\t})\n\n\tctx.WriteString(\"pong\")\n}\n"
  },
  {
    "path": "_examples/logging/json-logger/main_test.go",
    "content": "package main\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"path\"\n\t\"runtime\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\n\t\"github.com/kataras/golog\"\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nfunc TestJSONLogger(t *testing.T) {\n\titers := 500\n\n\tout := new(bytes.Buffer)\n\n\tapp := iris.New()\n\tapp.Logger().SetTimeFormat(\"\")     // disable timestamps.\n\tapp.Logger().SetStacktraceLimit(1) // limit debug stacktrace to 1, show only the first caller.\n\tapp.Logger().SetOutput(out)\n\n\tapp.Logger().Handle(func(l *golog.Log) bool {\n\t\tenc := json.NewEncoder(l.Logger.Printer) // you can change the output to a file as well.\n\t\terr := enc.Encode(l)\n\t\treturn err == nil\n\t})\n\n\tapp.Get(\"/ping\", ping)\n\n\texpectedSourceDir := getSourceDirPath()\n\texpectedLogStr := fmt.Sprintf(`{\"level\":\"debug\",\"message\":\"Request path: /ping\",\"fields\":{\"request_id\":null},\"stacktrace\":[{\"function\":\"json-logger/ping\",\"source\":\"%s/main.go:78\"}]}`, expectedSourceDir) // gh actions-specific.\n\te := httptest.New(t, app, httptest.LogLevel(\"debug\"))\n\twg := new(sync.WaitGroup)\n\twg.Add(iters)\n\tfor i := 0; i < iters; i++ {\n\t\tgo func() {\n\t\t\te.GET(\"/ping\").Expect().Status(httptest.StatusOK).Body().IsEqual(\"pong\")\n\t\t\twg.Done()\n\t\t}()\n\t}\n\n\twg.Wait()\n\texpected := \"\"\n\tfor i := 0; i < iters; i++ {\n\t\texpected += expectedLogStr + \"\\n\"\n\t}\n\n\tgot := out.String()\n\tgot = got[strings.Index(got, \"{\"):] // take only the json we care and after.\n\tif expected != got {\n\t\tif !strings.HasSuffix(got, expected) {\n\t\t\t// C:/mygopath vs /home/travis vs any file system,\n\t\t\t// pure check but it does the job.\n\t\t\tt.Fatalf(\"expected:\\n%s\\nbut got:\\n%s\", expected, got)\n\t\t}\n\t}\n}\n\nfunc getSourceDirPath() string {\n\t_, file, _, ok := runtime.Caller(1) // get the caller's file.\n\tif !ok {\n\t\treturn \"unknown source\"\n\t}\n\n\treturn path.Dir(file) // get the directory of the file (delimiter: /).\n}\n"
  },
  {
    "path": "_examples/logging/request-logger/accesslog/access.log.sample",
    "content": "2020-09-13 13:37:42 2.0057ms 200 POST /read_body ::1 61 B 94 B {\"name\":\"John\",\"email\":\"example@example.com\"} \n2020-09-13 13:37:50 0s 400 POST /read_body ::1 0 B 0 B error(invalid character '\\r' in string literal)\n2020-09-13 13:38:03 0s 404 GET /favicon.ico ::1 0 B 9 B\n2020-09-13 13:38:07 0s 404 GET /public ::1 0 B 9 B\n2020-09-13 13:38:09 1.0021ms 200 GET /user/kataras ::1 username=kataras 0 B 15 B\n2020-09-13 13:38:14 0s 200 GET /user/kataras ::1 username=kataras a_query_parameter=name 0 B 15 B\n2020-09-13 13:38:18 0s 401 GET /admin ::1 0 B 0 B\n2020-09-13 13:38:19 0s 200 GET /admin ::1 auth=admin:admin 0 B 48 B\n2020-09-13 13:38:22 0s 200 GET /session ::1 session_id=23fe763f-c9d5-4d65-9e1a-2cc8d23d1aa3 session_test_key=session_test_value auth=admin:admin 0 B 2 B\n2020-09-13 13:38:25 2.0001204s 200 GET /fields ::1 job_latency=2s auth=admin:admin user=user-id:user-name 0 B 2 B\n"
  },
  {
    "path": "_examples/logging/request-logger/accesslog/main.go",
    "content": "package main // See https://github.com/kataras/iris/issues/1601\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/middleware/accesslog\"\n\t\"github.com/kataras/iris/v12/middleware/basicauth\"\n\t\"github.com/kataras/iris/v12/middleware/requestid\"\n\t\"github.com/kataras/iris/v12/sessions\"\n\n\trotatelogs \"github.com/iproj/file-rotatelogs\"\n)\n\n// Default line format:\n// Time|Latency|Code|Method|Path|IP|Path Params Query Fields|Bytes Received|Bytes Sent|Request|Response|\n//\n// Read the example and its comments carefully.\nfunc makeAccessLog() *accesslog.AccessLog {\n\t// Optionally, let's Go with log rotation.\n\tpathToAccessLog := \"./access_log.%Y%m%d%H%M\"\n\tw, err := rotatelogs.New(\n\t\tpathToAccessLog,\n\t\trotatelogs.WithMaxAge(24*time.Hour),\n\t\trotatelogs.WithRotationTime(time.Hour))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Initialize a new access log middleware.\n\t// Accepts an `io.Writer`.\n\tac := accesslog.New(bufio.NewWriter(w))\n\tac.Delim = ' ' // change the separator from '|' to space.\n\t// ac.TimeFormat = \"2006-01-02 15:04:05\" // default\n\n\t// Example of adding more than one field to the logger.\n\t// Here we logging all the session values this request has.\n\t//\n\t// You can also add fields per request handler,\n\t// look below to the `fieldsHandler` function.\n\t// Note that this method can override a key stored by a handler's fields.\n\tac.AddFields(func(ctx iris.Context, fields *accesslog.Fields) {\n\t\tif sess := sessions.Get(ctx); sess != nil {\n\t\t\tfields.Set(\"session_id\", sess.ID())\n\n\t\t\tsess.Visit(func(k string, v any) {\n\t\t\t\tfields.Set(k, v)\n\t\t\t})\n\t\t}\n\t})\n\t// Add a custom field of \"auth\" when basic auth is available.\n\tac.AddFields(func(ctx iris.Context, fields *accesslog.Fields) {\n\t\tif username, password, ok := ctx.Request().BasicAuth(); ok {\n\t\t\tfields.Set(\"auth\", username+\":\"+password)\n\t\t}\n\t})\n\n\treturn ac\n\n\t/*\n\t\tUse a file directly:\n\t\tac := accesslog.File(\"./access.log\")\n\n\t\tLog after the response was sent (defaults to false):\n\t\tac.Async = true\n\n\t\tForce-protect writer with locks.\n\t\tOn this example this is not required:\n\t\tac.LockWriter = true\"\n\n\t\t// To disable request and response calculations\n\t\t// (enabled by default but slows down the whole operation if Async is false):\n\t\tac.RequestBody = false\n\t\tac.ResponseBody = false\n\t\tac.BytesReceived = false\n\t\tac.BytesSent = false\n\t\tac.BytesReceivedBody = false\n\t\tac.BytesSentBody = false\n\n\t\tAdd second output:\n\t\tac.AddOutput(app.Logger().Printer)\n\t\tOR:\n\t\taccesslog.New(io.MultiWriter(w, os.Stdout))\n\n\t\tChange format (after output was set):\n\t\tac.SetFormatter(&accesslog.JSON{Indent: \"  \"})\n\n\t\tModify the output format and customize the order\n\t\twith the Template formatter:\n\t\tac.SetFormatter(&accesslog.Template{\n\t\t    Text: \"{{.Now.Format .TimeFormat}}|{{.Latency}}|{{.Code}}|{{.Method}}|{{.Path}}|{{.IP}}|{{.RequestValuesLine}}|{{.BytesReceivedLine}}|{{.BytesSentLine}}|{{.Request}}|{{.Response}}|\\n\",\n\t\t    // Default ^\n\t\t})\n\t*/\n}\n\nfunc main() {\n\tac := makeAccessLog()\n\tdefer ac.Close()\n\n\tapp := iris.New()\n\t// Register the middleware (UseRouter to catch http errors too).\n\tapp.UseRouter(ac.Handler)\n\t//\n\n\t// Register other middlewares...\n\tapp.UseRouter(requestid.New())\n\n\t// Register some routes...\n\tapp.HandleDir(\"/\", iris.Dir(\"./public\"))\n\n\tapp.Get(\"/user/{username}\", userHandler)\n\tapp.Post(\"/read_body\", readBodyHandler)\n\tapp.Get(\"/html_response\", htmlResponse)\n\n\tbasicAuth := basicauth.Default(map[string]string{\n\t\t\"admin\": \"admin\",\n\t})\n\tapp.Get(\"/admin\", basicAuth, adminHandler)\n\n\tsess := sessions.New(sessions.Config{Cookie: \"my_session_id\", AllowReclaim: true})\n\tapp.Get(\"/session\", sess.Handler(), sessionHandler)\n\n\tapp.Get(\"/fields\", fieldsHandler)\n\t//\n\n\tapp.Listen(\":8080\")\n}\n\nfunc readBodyHandler(ctx iris.Context) {\n\tvar request any\n\tif err := ctx.ReadBody(&request); err != nil {\n\t\tctx.StopWithPlainError(iris.StatusBadRequest, err)\n\t\treturn\n\t}\n\n\tctx.JSON(iris.Map{\"message\": \"OK\", \"data\": request})\n}\n\nfunc userHandler(ctx iris.Context) {\n\tctx.Writef(\"Hello, %s!\", ctx.Params().Get(\"username\"))\n}\n\nfunc htmlResponse(ctx iris.Context) {\n\tctx.HTML(\"<h1>HTML Response</h1>\")\n}\n\nfunc adminHandler(ctx iris.Context) {\n\tusername, password, _ := ctx.Request().BasicAuth()\n\t// of course you don't want that in production:\n\tctx.HTML(fmt.Sprintf(\"<h2>Username: %s</h2><h3>Password: %s</h3>\", username, password))\n}\n\nfunc sessionHandler(ctx iris.Context) {\n\tsess := sessions.Get(ctx)\n\tsess.Set(\"session_test_key\", \"session_test_value\")\n\n\tctx.WriteString(\"OK\")\n}\n\ntype user struct {\n\tID       string\n\tUsername string\n}\n\n// Log custom structs, they can implement the fmt.Stringer interface too.\nfunc (u user) String() string {\n\treturn u.ID + \":\" + u.Username\n}\n\nfunc fieldsHandler(ctx iris.Context) {\n\tstart := time.Now()\n\t// simulate a heavy job...\n\ttime.Sleep(2 * time.Second)\n\tend := time.Since(start)\n\t// Get the current fields instance\n\t// and use it to set custom log values.\n\tlogFields := accesslog.GetFields(ctx)\n\tlogFields.Set(\"job_latency\", end.Round(time.Second))\n\n\t// Simulate a database fetch or anything\n\t// to get a \"user\" and log it:\n\tu := user{\n\t\tID:       \"user-id\",\n\t\tUsername: \"user-name\",\n\t}\n\tlogFields.Set(\"user\", u)\n\n\tctx.WriteString(\"OK\")\n}\n"
  },
  {
    "path": "_examples/logging/request-logger/accesslog/public/index.html",
    "content": "<h1>Hello index</h1>"
  },
  {
    "path": "_examples/logging/request-logger/accesslog-broker/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/middleware/accesslog\"\n\t\"github.com/kataras/iris/v12/middleware/recover\"\n)\n\nfunc main() {\n\t/*\n\t\tOn this example we will make use of the logs broker.\n\t\tA handler will listen for any incoming logs and render\n\t\tthose logs as chunks of JSON to the client (e.g. browser) at real-time.\n\t\tNote that this ^ can be done with Server-Sent Events but for the\n\t\tsake of the example we'll do it using Transfer-Encoding: chunked.\n\t*/\n\n\tac := accesslog.File(\"./access.log\")\n\tdefer ac.Close()\n\tac.AddOutput(os.Stdout)\n\n\tac.RequestBody = true\n\t// Set to false to print errors as one line:\n\t// ac.KeepMultiLineError = false\n\t// Set the \"depth\" of a panic trace:\n\tac.PanicLog = accesslog.LogHandler // or LogCallers or LogStack\n\n\t// Optionally run logging after response has sent:\n\t// ac.Async = true\n\tbroker := ac.Broker() // <- IMPORTANT\n\n\tapp := iris.New()\n\tapp.UseRouter(ac.Handler)\n\tapp.UseRouter(recover.New())\n\n\tapp.OnErrorCode(iris.StatusNotFound, notFoundHandler)\n\n\tapp.Get(\"/panic\", testPanic)\n\tapp.Get(\"/\", indexHandler)\n\tapp.Get(\"/profile/{username}\", profileHandler)\n\tapp.Post(\"/read_body\", readBodyHandler)\n\n\t// register the /logs route,\n\t// registers a listener and prints the incoming logs.\n\t// Optionally, skip logging this handler.\n\tapp.Get(\"/logs\", accesslog.SkipHandler, logsHandler(broker))\n\n\t// http://localhost:8080/logs to see the logs at real-time.\n\tapp.Listen(\":8080\")\n}\n\nfunc notFoundHandler(ctx iris.Context) {\n\t// ctx.Application().Logger().Infof(\"Not Found Handler for: %s\", ctx.Path())\n\n\tsuggestPaths := ctx.FindClosest(3)\n\tif len(suggestPaths) == 0 {\n\t\tctx.WriteString(\"The page you're looking does not exist.\")\n\t\treturn\n\t}\n\n\tctx.HTML(\"Did you mean?<ul>\")\n\tfor _, s := range suggestPaths {\n\t\tctx.HTML(fmt.Sprintf(`<li><a href=\"%s\">%s</a></li>`, s, s))\n\t}\n\tctx.HTML(\"</ul>\")\n}\n\nfunc indexHandler(ctx iris.Context) {\n\tctx.HTML(\"<h1>Index</h1>\")\n}\n\nfunc profileHandler(ctx iris.Context) {\n\tusername := ctx.Params().Get(\"username\")\n\tctx.HTML(fmt.Sprintf(\"Hello, <strong>%s</strong>!\", username))\n}\n\nfunc readBodyHandler(ctx iris.Context) {\n\tvar request any\n\tif err := ctx.ReadBody(&request); err != nil {\n\t\tctx.StopWithPlainError(iris.StatusBadRequest, err)\n\t\treturn\n\t}\n\n\tctx.JSON(iris.Map{\"message\": \"OK\", \"data\": request})\n}\n\nfunc testPanic(ctx iris.Context) {\n\tpanic(\"PANIC HERE\")\n}\n\nfunc logsHandler(b *accesslog.Broker) iris.Handler {\n\treturn func(ctx iris.Context) {\n\t\t// accesslog.Skip(ctx) // or inline skip.\n\t\tlogs := b.NewListener() // <- IMPORTANT\n\n\t\tctx.Header(\"Transfer-Encoding\", \"chunked\")\n\t\tnotifyClose := ctx.Request().Context().Done()\n\t\tfor {\n\t\t\tselect {\n\t\t\tcase <-notifyClose:\n\t\t\t\tb.CloseListener(logs) // <- IMPORTANT\n\n\t\t\t\terr := ctx.Request().Context().Err()\n\t\t\t\tctx.Application().Logger().Infof(\"Listener closed [%v], loop end.\", err)\n\t\t\t\treturn\n\t\t\tcase log := <-logs: // <- IMPORTANT\n\t\t\t\tctx.JSON(log, iris.JSON{Indent: \"  \", UnescapeHTML: true})\n\t\t\t\tctx.ResponseWriter().Flush()\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "_examples/logging/request-logger/accesslog-csv/access_log.csv.sample",
    "content": "Timestamp,Latency,Code,Method,Path,IP,Req Values,In,Out,Request,Response\n1599996265254,0s,200,GET,/,::1,a=1 b=2,0,5,,Index\n1599996266138,0s,200,GET,/,::1,sleep=32ms,0,5,,Index\n1599996266778,1s,200,GET,/,::1,sleep=1s,0,5,,Index\n1599996267780,1s,200,GET,/,::1,sleep=1s,0,5,,Index\n"
  },
  {
    "path": "_examples/logging/request-logger/accesslog-csv/main.go",
    "content": "package main\n\nimport (\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/middleware/accesslog\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tac := accesslog.File(\"access_log.csv\")\n\tac.ResponseBody = true\n\tac.LatencyRound = time.Second\n\tac.SetFormatter(&accesslog.CSV{\n\t\tHeader: true,\n\t\t// DateScript:   \"FROM_UNIX\",\n\t})\n\n\tapp.UseRouter(ac.Handler)\n\tapp.Get(\"/\", index)\n\n\tapp.Listen(\":8080\")\n}\n\nfunc index(ctx iris.Context) {\n\tif sleepDur := ctx.URLParam(\"sleep\"); sleepDur != \"\" {\n\t\tif d, err := time.ParseDuration(sleepDur); err == nil {\n\t\t\ttime.Sleep(d)\n\t\t}\n\t}\n\n\tctx.WriteString(\"Index\")\n}\n"
  },
  {
    "path": "_examples/logging/request-logger/accesslog-formatter/main.go",
    "content": "// Package main shows how to create a quite fast custom Log Formatter.\n// Note that, this example requires a little more knowledge about Go.\npackage main\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"strconv\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/middleware/accesslog\"\n\t\"github.com/kataras/iris/v12/middleware/requestid\"\n)\n\nfunc logFields(ctx iris.Context, fields *accesslog.Fields) {\n\tfields.Set(\"reqid\", ctx.GetID())\n}\n\nfunc main() {\n\tapp := iris.New()\n\n\tac := accesslog.File(\"./access.log\").\n\t\tAddFields(logFields).\n\t\tSetFormatter(newCustomFormatter(' ', \"-\\t\\t\\t\\t\\t\"))\n\tac.RequestBody = false\n\tac.BytesReceivedBody = false\n\tac.BytesSentBody = false\n\tdefer ac.Close()\n\n\tapp.UseRouter(ac.Handler)\n\tapp.UseRouter(requestid.New())\n\n\tapp.OnErrorCode(iris.StatusNotFound, notFound)\n\tapp.Get(\"/\", index)\n\n\tapp.Listen(\":8080\")\n}\n\nfunc notFound(ctx iris.Context) {\n\tctx.WriteString(\"The page you're looking for does not exist!\")\n}\n\nfunc index(ctx iris.Context) {\n\tctx.WriteString(\"OK Index\")\n}\n\ntype customFormatter struct {\n\tw       io.Writer\n\tbufPool *sync.Pool\n\n\tdelim byte\n\tblank string\n}\n\nvar _ accesslog.Formatter = (*customFormatter)(nil)\n\nfunc newCustomFormatter(delim byte, blank string) *customFormatter {\n\treturn &customFormatter{delim: delim, blank: blank}\n}\n\nfunc (f *customFormatter) SetOutput(dest io.Writer) {\n\tf.w = dest\n\tf.bufPool = &sync.Pool{\n\t\tNew: func() any {\n\t\t\treturn new(bytes.Buffer)\n\t\t},\n\t}\n\n\tif f.delim == 0 {\n\t\tf.delim = ' '\n\t}\n}\n\nconst newLine = '\\n'\n\nfunc (f *customFormatter) Format(log *accesslog.Log) (bool, error) {\n\tbuf := f.bufPool.Get().(*bytes.Buffer)\n\n\tbuf.WriteString(log.Now.Format(log.TimeFormat))\n\tbuf.WriteByte(f.delim)\n\n\treqid := log.Fields.GetString(\"reqid\")\n\tf.writeTextOrBlank(buf, reqid)\n\n\tbuf.WriteString(uniformDuration(log.Latency))\n\tbuf.WriteByte(f.delim)\n\n\tbuf.WriteString(log.IP)\n\tbuf.WriteByte(f.delim)\n\n\tbuf.WriteString(strconv.Itoa(log.Code))\n\tbuf.WriteByte(f.delim)\n\n\tbuf.WriteString(log.Method)\n\tbuf.WriteByte(f.delim)\n\n\tbuf.WriteString(log.Path)\n\n\tbuf.WriteByte(newLine)\n\n\t// _, err := buf.WriteTo(f.w)\n\t// or (to make sure that it resets on errors too):\n\t_, err := f.w.Write(buf.Bytes())\n\tbuf.Reset()\n\tf.bufPool.Put(buf)\n\n\treturn true, err\n}\n\nfunc (f *customFormatter) writeTextOrBlank(buf *bytes.Buffer, s string) {\n\tif len(s) == 0 {\n\t\tif len(f.blank) == 0 {\n\t\t\treturn\n\t\t}\n\n\t\tbuf.WriteString(f.blank)\n\t} else {\n\t\tbuf.WriteString(s)\n\t}\n\n\tbuf.WriteByte(f.delim)\n}\n\nfunc uniformDuration(t time.Duration) string {\n\treturn fmt.Sprintf(\"%*s\", 12, t.String())\n}\n"
  },
  {
    "path": "_examples/logging/request-logger/accesslog-proxy/main.go",
    "content": "/*\nPackage main is a proxy + accesslog example.\nIn this example we will make a small proxy which listens requests on \"/proxy/+path\".\nWith two accesslog instances, one for the main application and one for the /proxy/ requests.\nOf cource, you could a single accesslog for the whole application, but for the sake of the example\nlet's log them separately.\n\nWe will make use of iris.StripPrefix and host.ProxyHandler.\n*/\npackage main\n\nimport (\n\t\"net/url\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/core/host\"\n\t\"github.com/kataras/iris/v12/middleware/accesslog\"\n\t\"github.com/kataras/iris/v12/middleware/recover\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.Get(\"/\", index)\n\n\tac := accesslog.File(\"access.log\")\n\tdefer ac.Close()\n\tac.Async = true\n\tac.RequestBody = true\n\tac.ResponseBody = true\n\tac.BytesReceived = false\n\tac.BytesSent = false\n\n\tapp.UseRouter(ac.Handler)\n\tapp.UseRouter(recover.New())\n\n\tproxy := app.Party(\"/proxy\")\n\t{\n\t\tacProxy := accesslog.File(\"proxy_access.log\")\n\t\tdefer acProxy.Close()\n\t\tacProxy.Async = true\n\t\tacProxy.RequestBody = true\n\t\tacProxy.ResponseBody = true\n\t\tacProxy.BytesReceived = false\n\t\tacProxy.BytesSent = false\n\n\t\t// Unlike Use, the UseRouter method replaces any duplications automatically.\n\t\t// (see UseOnce for the same behavior on Use).\n\t\t// Therefore, this statement removes the parent's accesslog and registers this new one.\n\t\tproxy.UseRouter(acProxy.Handler)\n\t\tproxy.UseRouter(recover.New())\n\t\tproxy.Use(func(ctx iris.Context) {\n\t\t\tctx.CompressReader(true)\n\t\t\tctx.Next()\n\t\t})\n\n\t\t/* Listen for specific proxy paths:\n\t\t// Listen on \"/proxy\" for \"http://localhost:9090/read-write\"\n\t\tproxy.Any(\"/\", iris.StripPrefix(\"/proxy\",\n\t\t\tnewProxyHandler(\"http://localhost:9090/read-write\")))\n\t\t*/\n\n\t\t// You can register an access log only for proxied requests, e.g. proxy_access.log:\n\t\t// proxy.UseRouter(ac2.Handler)\n\n\t\t// Listen for any proxy path.\n\t\t// Proxies the \"/proxy/+$path\" to \"http://localhost:9090/$path\".\n\t\tproxy.Any(\"/{p:path}\", iris.StripPrefix(\"/proxy\",\n\t\t\tnewProxyHandler(\"http://localhost:9090\")))\n\t}\n\n\t// $ go run target/main.go\n\t// open new terminal\n\t// $ go run main.go\n\tapp.Listen(\":8080\")\n}\n\nfunc index(ctx iris.Context) {\n\tctx.WriteString(\"OK\")\n}\n\nfunc newProxyHandler(proxyURL string) iris.Handler {\n\ttarget, err := url.Parse(proxyURL)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treverseProxy := host.ProxyHandler(target, nil)\n\treturn iris.FromStd(reverseProxy)\n}\n"
  },
  {
    "path": "_examples/logging/request-logger/accesslog-proxy/target/main.go",
    "content": "package main\n\nimport \"github.com/kataras/iris/v12\"\n\n// The target server, can be written using any programming language and any web framework, of course.\nfunc main() {\n\tapp := iris.New()\n\tapp.Logger().SetLevel(\"debug\")\n\n\t// Just a test route which reads some data and responds back with json.\n\tapp.Post(\"/read-write\", readWriteHandler)\n\n\tapp.Get(\"/get\", getHandler)\n\n\t// The target ip:port.\n\tapp.Listen(\":9090\")\n}\n\nfunc readWriteHandler(ctx iris.Context) {\n\tvar req any\n\tctx.ReadBody(&req)\n\n\tctx.JSON(iris.Map{\n\t\t\"message\": \"OK\",\n\t\t\"request\": req,\n\t})\n}\n\nfunc getHandler(ctx iris.Context) {\n\t// ctx.CompressWriter(true)\n\tctx.WriteString(\"Compressed data\")\n}\n"
  },
  {
    "path": "_examples/logging/request-logger/accesslog-simple/access.log.sample",
    "content": "{\"timestamp\":1599993744664,\"latency\":0,\"code\":404,\"method\":\"GET\",\"path\":\"/favicon.ico\",\"ip\":\"::1\",\"request\":\"\",\"bytes_sent\":9}\n{\"timestamp\":1599993774018,\"latency\":0,\"code\":200,\"method\":\"GET\",\"path\":\"/\",\"ip\":\"::1\",\"query\":[{\"key\":\"a\",\"value\":\"1\"},{\"key\":\"b\",\"value\":\"2\"}],\"request\":\"\",\"bytes_sent\":2}\n"
  },
  {
    "path": "_examples/logging/request-logger/accesslog-simple/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/middleware/accesslog\"\n)\n\n// Read the example and its comments carefully.\nfunc makeAccessLog() *accesslog.AccessLog {\n\t// Initialize a new access log middleware.\n\tac := accesslog.File(\"./access.log\")\n\n\t// The default configuration:\n\tac.Delim = '|'\n\tac.TimeFormat = \"2006-01-02 15:04:05\"\n\tac.Async = false\n\tac.IP = true\n\tac.BytesReceivedBody = true\n\tac.BytesSentBody = true\n\tac.BytesReceived = false\n\tac.BytesSent = false\n\tac.BodyMinify = true\n\tac.RequestBody = true\n\tac.ResponseBody = false\n\tac.KeepMultiLineError = true\n\tac.PanicLog = accesslog.LogHandler\n\n\t// Default line format if formatter is missing:\n\t// Time|Latency|Code|Method|Path|IP|Path Params Query Fields|Bytes Received|Bytes Sent|Request|Response|\n\t//\n\t// Set Custom Formatter:\n\tac.SetFormatter(&accesslog.JSON{\n\t\tIndent:    \"  \",\n\t\tHumanTime: true,\n\t})\n\t// ac.SetFormatter(&accesslog.CSV{})\n\t// ac.SetFormatter(&accesslog.Template{Text: \"{{.Code}}\"})\n\n\treturn ac\n}\n\nfunc main() {\n\tac := makeAccessLog()\n\tdefer ac.Close() // Close the underline file.\n\n\tapp := iris.New()\n\t// Register the middleware (UseRouter to catch http errors too).\n\tapp.UseRouter(ac.Handler)\n\n\tapp.Get(\"/\", indexHandler)\n\n\tapp.Listen(\":8080\")\n}\n\nfunc indexHandler(ctx iris.Context) {\n\tctx.WriteString(\"OK\")\n}\n"
  },
  {
    "path": "_examples/logging/request-logger/accesslog-slack/go.mod",
    "content": "module github.com/kataras/iris/_examples/logging/accesslog-slack\n\ngo 1.25\n\nrequire (\n\tgithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd\n\tgithub.com/slack-go/slack v0.17.3\n)\n\nrequire (\n\tgithub.com/BurntSushi/toml v1.6.0 // indirect\n\tgithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect\n\tgithub.com/CloudyKit/jet/v6 v6.3.1 // indirect\n\tgithub.com/Joker/jade v1.1.3 // indirect\n\tgithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 // indirect\n\tgithub.com/andybalholm/brotli v1.2.0 // indirect\n\tgithub.com/aymerick/douceur v0.2.0 // indirect\n\tgithub.com/fatih/structs v1.1.0 // indirect\n\tgithub.com/flosch/pongo2/v4 v4.0.2 // indirect\n\tgithub.com/golang/snappy v1.0.0 // indirect\n\tgithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a // indirect\n\tgithub.com/google/uuid v1.6.0 // indirect\n\tgithub.com/gorilla/css v1.0.1 // indirect\n\tgithub.com/gorilla/websocket v1.5.3 // indirect\n\tgithub.com/iris-contrib/schema v0.0.6 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/json-iterator/go v1.1.12 // indirect\n\tgithub.com/kataras/blocks v0.0.8 // indirect\n\tgithub.com/kataras/golog v0.1.12 // indirect\n\tgithub.com/kataras/pio v0.0.13 // indirect\n\tgithub.com/kataras/sitemap v0.0.6 // indirect\n\tgithub.com/kataras/tunnel v0.0.4 // indirect\n\tgithub.com/klauspost/compress v1.18.2 // indirect\n\tgithub.com/kr/pretty v0.3.1 // indirect\n\tgithub.com/mailgun/raymond/v2 v2.0.48 // indirect\n\tgithub.com/mailru/easyjson v0.9.1 // indirect\n\tgithub.com/microcosm-cc/bluemonday v1.0.27 // indirect\n\tgithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect\n\tgithub.com/modern-go/reflect2 v1.0.2 // indirect\n\tgithub.com/rogpeppe/go-internal v1.10.0 // indirect\n\tgithub.com/russross/blackfriday/v2 v2.1.0 // indirect\n\tgithub.com/schollz/closestmatch v2.1.0+incompatible // indirect\n\tgithub.com/sirupsen/logrus v1.8.1 // indirect\n\tgithub.com/stretchr/testify v1.11.1 // indirect\n\tgithub.com/tdewolff/minify/v2 v2.24.8 // indirect\n\tgithub.com/tdewolff/parse/v2 v2.8.5 // indirect\n\tgithub.com/valyala/bytebufferpool v1.0.0 // indirect\n\tgithub.com/vmihailenco/msgpack/v5 v5.4.1 // indirect\n\tgithub.com/vmihailenco/tagparser/v2 v2.0.0 // indirect\n\tgithub.com/yosssi/ace v0.0.5 // indirect\n\tgolang.org/x/crypto v0.47.0 // indirect\n\tgolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 // indirect\n\tgolang.org/x/net v0.49.0 // indirect\n\tgolang.org/x/sys v0.40.0 // indirect\n\tgolang.org/x/text v0.33.0 // indirect\n\tgolang.org/x/time v0.14.0 // indirect\n\tgoogle.golang.org/protobuf v1.36.11 // indirect\n\tgopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect\n\tgopkg.in/ini.v1 v1.67.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n"
  },
  {
    "path": "_examples/logging/request-logger/accesslog-slack/go.sum",
    "content": "github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=\ngithub.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=\ngithub.com/CloudyKit/jet/v6 v6.3.1 h1:6IAo5Cx21xrHVaR8zzXN5gJatKV/wO7Nf6bfCnCSbUw=\ngithub.com/CloudyKit/jet/v6 v6.3.1/go.mod h1:lf8ksdNsxZt7/yH/3n4vJQWA9RUq4wpaHtArHhGVMOw=\ngithub.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc=\ngithub.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=\ngithub.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=\ngithub.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 h1:dG7/gLroJGht/jSQtHiLvT48Hxn+crbmvyItZC8cWOs=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05/go.mod h1:NYezi6wtnJtBm5btoprXc5SvAdqH0XTXWnUup0MptAI=\ngithub.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=\ngithub.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=\ngithub.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=\ngithub.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=\ngithub.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=\ngithub.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=\ngithub.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=\ngithub.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=\ngithub.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=\ngithub.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw=\ngithub.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8=\ngithub.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U=\ngithub.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=\ngithub.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=\ngithub.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=\ngithub.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=\ngithub.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a h1:l7A0loSszR5zHd/qK53ZIHMO8b3bBSmENnQ6eKnUT0A=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=\ngithub.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=\ngithub.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=\ngithub.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=\ngithub.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=\ngithub.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2 h1:T9THsdP1woyAqKHwjkEsbCnMefsAFvk8iJJKokcJ3Go=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2/go.mod h1:JLDgIqnFy5loDSUv1OA2j0mb6p/rDhiCqigP22Uq9xE=\ngithub.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw=\ngithub.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=\ngithub.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=\ngithub.com/kataras/blocks v0.0.8 h1:MrpVhoFTCR2v1iOOfGng5VJSILKeZZI+7NGfxEh3SUM=\ngithub.com/kataras/blocks v0.0.8/go.mod h1:9Jm5zx6BB+06NwA+OhTbHW1xkMOYxahnqTN5DveZ2Yg=\ngithub.com/kataras/golog v0.1.12 h1:Bu7I/G4ilJlbfzjmU39O9N+2uO1pBcMK045fzZ4ytNg=\ngithub.com/kataras/golog v0.1.12/go.mod h1:wrGSbOiBqbQSQznleVNX4epWM8rl9SJ/rmEacl0yqy4=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd h1:oMsQgqKACbUdlaIWnN+EZzzAnHkw90hE/y/R0BSij2U=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd/go.mod h1:yucjtDl9Vn3OctWM1fi0MuM9OckemC7kEreFa9QtPF8=\ngithub.com/kataras/pio v0.0.13 h1:x0rXVX0fviDTXOOLOmr4MUxOabu1InVSTu5itF8CXCM=\ngithub.com/kataras/pio v0.0.13/go.mod h1:k3HNuSw+eJ8Pm2lA4lRhg3DiCjVgHlP8hmXApSej3oM=\ngithub.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY=\ngithub.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4=\ngithub.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA=\ngithub.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw=\ngithub.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=\ngithub.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=\ngithub.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=\ngithub.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8=\ngithub.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=\ngithub.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=\ngithub.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=\ngithub.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=\ngithub.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=\ngithub.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=\ngithub.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=\ngithub.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=\ngithub.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=\ngithub.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=\ngithub.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=\ngithub.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=\ngithub.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=\ngithub.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk=\ngithub.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=\ngithub.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=\ngithub.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=\ngithub.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=\ngithub.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/slack-go/slack v0.17.3 h1:zV5qO3Q+WJAQ/XwbGfNFrRMaJ5T/naqaonyPV/1TP4g=\ngithub.com/slack-go/slack v0.17.3/go.mod h1:X+UqOufi3LYQHDnMG1vxf0J8asC6+WllXrVrhl8/Prk=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/tdewolff/minify/v2 v2.24.8 h1:58/VjsbevI4d5FGV0ZSuBrHMSSkH4MCH0sIz/eKIauE=\ngithub.com/tdewolff/minify/v2 v2.24.8/go.mod h1:0Ukj0CRpo/sW/nd8uZ4ccXaV1rEVIWA3dj8U7+Shhfw=\ngithub.com/tdewolff/parse/v2 v2.8.5 h1:ZmBiA/8Do5Rpk7bDye0jbbDUpXXbCdc3iah4VeUvwYU=\ngithub.com/tdewolff/parse/v2 v2.8.5/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=\ngithub.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE=\ngithub.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=\ngithub.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=\ngithub.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=\ngithub.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=\ngithub.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=\ngithub.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=\ngithub.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=\ngithub.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA=\ngithub.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0=\ngithub.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=\ngithub.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=\ngithub.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=\ngolang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=\ngolang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=\ngolang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=\ngolang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=\ngolang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=\ngolang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=\ngolang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=\ngolang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=\ngoogle.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=\ngopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nmoul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs=\nmoul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE=\n"
  },
  {
    "path": "_examples/logging/request-logger/accesslog-slack/main.go",
    "content": "package main\n\nimport (\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/middleware/accesslog\"\n)\n\nvar (\n\t// https://api.slack.com/apps/your_app_id/oauth\n\ttoken = os.Getenv(\"SLACK_BOT_TOKEN\")\n\t// on slack app: right click on the channel -> view channel details -> on bottom, copy the channel id.\n\tchannelID = os.Getenv(\"SLACK_CHANNEL_ID\")\n)\n\n// $ go run .\nfunc main() {\n\tapp := iris.New()\n\n\tac := accesslog.New(os.Stdout) // or app.Logger().Printer\n\tac.LatencyRound = time.Second\n\tac.SetFormatter(&Slack{\n\t\tToken:         token,\n\t\tChannelIDs:    []string{channelID},\n\t\tHandleMessage: true,\n\t})\n\n\tapp.UseRouter(ac.Handler)\n\tapp.Get(\"/\", index)\n\n\tapp.Listen(\":8080\")\n}\n\nfunc index(ctx iris.Context) {\n\tif sleepDur := ctx.URLParam(\"sleep\"); sleepDur != \"\" {\n\t\tif d, err := time.ParseDuration(sleepDur); err == nil {\n\t\t\ttime.Sleep(d)\n\t\t}\n\t}\n\n\tctx.WriteString(\"Index\")\n}\n"
  },
  {
    "path": "_examples/logging/request-logger/accesslog-slack/slack_formatter.go",
    "content": "package main\n\nimport (\n\t\"io\"\n\n\t\"github.com/kataras/iris/v12/middleware/accesslog\"\n\n\t\"github.com/slack-go/slack\"\n)\n\ntype Slack struct {\n\t// Client is the underline slack slack.\n\t// This or Token are required.\n\tClient *slack.Client\n\t// Token is the oauth slack client token.\n\t// Read more at: https://api.slack.com/web#authentication.\n\t//\n\t// If Client is not null then this is used to initialize the Slack.Client field.\n\tToken string\n\n\t// ChannelIDs specifies one or more channel the request logs\n\t// will be printed to.\n\tChannelIDs []string\n\n\t// HandleMessage set to true whether the slack formatter\n\t// should just send the log to the slack channel(s) and\n\t// stop printing the log to the accesslog's io.Writer output.\n\tHandleMessage bool\n\n\t// Template is the underline text template format of the logs.\n\t// Set to a custom one if you want to customize the template (how the text is rended).\n\tTemplate *accesslog.Template\n}\n\nfunc (f *Slack) SetOutput(dest io.Writer) {\n\tif f.Client == nil && f.Token == \"\" {\n\t\tpanic(\"client or token fields must be provided\")\n\t}\n\n\tif len(f.ChannelIDs) == 0 {\n\t\tpanic(\"channel ids field is required\")\n\t}\n\n\tif f.Token != \"\" {\n\t\tc := slack.New(f.Token)\n\t\tf.Client = c\n\t}\n\n\tif f.Template == nil {\n\t\tf.Template = &accesslog.Template{}\n\t}\n\n\tf.Template.SetOutput(dest)\n}\n\nfunc (f *Slack) Format(log *accesslog.Log) (bool, error) {\n\ttext, err := f.Template.LogText(log)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\tfor _, channelID := range f.ChannelIDs {\n\t\t_, _, err := f.Client.PostMessage(\n\t\t\tchannelID,\n\t\t\tslack.MsgOptionText(text, false),\n\t\t\tslack.MsgOptionAsUser(true),\n\t\t)\n\n\t\tif err != nil {\n\t\t\treturn false, err\n\t\t}\n\t}\n\n\treturn f.HandleMessage, nil\n}\n"
  },
  {
    "path": "_examples/logging/request-logger/accesslog-template/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/middleware/accesslog\"\n\t\"github.com/kataras/iris/v12/middleware/requestid\"\n)\n\nfunc main() {\n\t/*\n\t\tThis example will show you how you can\n\t\tregister custom fields and log them separately\n\t\twith a custom format through the Template formatter.\n\t*/\n\n\tapp := iris.New()\n\tac := accesslog.File(\"./access.log\").AddOutput(app.Logger().Printer)\n\tdefer ac.Close()\n\n\t// 1. Register a field.\n\tac.AddFields(func(ctx iris.Context, fields *accesslog.Fields) {\n\t\tfields.Set(\"Request ID\", ctx.GetID())\n\t})\n\n\t// 2. Use Template formatter's `Text` value\n\t// to define a log line format.\n\tac.SetFormatter(&accesslog.Template{\n\t\tText: `{{.Now.Format .TimeFormat}} {{.Path}} {{.Code}} {{.IP}} {{.Fields.Get \"Request ID\" }}\n`, /*             2020-09-11 09:30:10 / 200 ::1 050a0979-c5e4-4c2b-9f08-cb456628edb1 */\n\t})\n\t// 3. Register the middleware. That's all.\n\tapp.UseRouter(ac.Handler)\n\t// Register the request id middleware, after the logger,  this maps the Context.GetID().\n\t// Remember: the accesslog runs the next handlers before itself to provide some fields.\n\tapp.UseRouter(requestid.New())\n\n\tapp.Get(\"/\", index)\n\n\tapp.Listen(\":8080\")\n}\n\nfunc index(ctx iris.Context) {\n\tctx.WriteString(\"Index\")\n}\n\n/* Use a custom *template.Template:\n\n// 2.1 The log line format:\ntext := `{{.Now.Format .TimeFormat}} {{.Path}} {{.Code}} {{.IP}} {{.Fields.Get \"Request ID\" }}\n`\n//\n// 2.2 Parse the template, optionally using custom Template Functions.\ntmpl := template.Must(template.New(\"\").Funcs(template.FuncMap{\n\t// Custom functions you may want to use inside \"text\",\n\t// e.g. prefixFields .Fields \"my_prefix\"\n\t// to get a slice of fields starts with \"my_prefix\"\n\t// and later, in the template, loop through them and render their values.\n\t// \"key\": func(input) string { return ... }\n}).Parse(text))\n//\n// 3. Use Template formatter's `Text` value\n// or the `Tmpl` field to customize the look & feel of a log.\nac.SetFormatter(&accesslog.Template{\n\tTmpl: tmpl,\n})\n\n*/\n"
  },
  {
    "path": "_examples/logging/rollbar/go.mod",
    "content": "module github.com/kataras/iris/examples/logging/rollbar\n\ngo 1.25\n\nrequire (\n\tgithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd\n\tgithub.com/rollbar/rollbar-go v1.4.8\n)\n\nrequire (\n\tgithub.com/BurntSushi/toml v1.6.0 // indirect\n\tgithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect\n\tgithub.com/CloudyKit/jet/v6 v6.3.1 // indirect\n\tgithub.com/Joker/jade v1.1.3 // indirect\n\tgithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 // indirect\n\tgithub.com/andybalholm/brotli v1.2.0 // indirect\n\tgithub.com/aymerick/douceur v0.2.0 // indirect\n\tgithub.com/fatih/structs v1.1.0 // indirect\n\tgithub.com/flosch/pongo2/v4 v4.0.2 // indirect\n\tgithub.com/golang/snappy v1.0.0 // indirect\n\tgithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a // indirect\n\tgithub.com/google/uuid v1.6.0 // indirect\n\tgithub.com/gorilla/css v1.0.1 // indirect\n\tgithub.com/iris-contrib/schema v0.0.6 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/kataras/blocks v0.0.8 // indirect\n\tgithub.com/kataras/golog v0.1.12 // indirect\n\tgithub.com/kataras/pio v0.0.13 // indirect\n\tgithub.com/kataras/sitemap v0.0.6 // indirect\n\tgithub.com/kataras/tunnel v0.0.4 // indirect\n\tgithub.com/klauspost/compress v1.18.2 // indirect\n\tgithub.com/kr/pretty v0.3.1 // indirect\n\tgithub.com/mailgun/raymond/v2 v2.0.48 // indirect\n\tgithub.com/mailru/easyjson v0.9.1 // indirect\n\tgithub.com/microcosm-cc/bluemonday v1.0.27 // indirect\n\tgithub.com/rogpeppe/go-internal v1.10.0 // indirect\n\tgithub.com/russross/blackfriday/v2 v2.1.0 // indirect\n\tgithub.com/schollz/closestmatch v2.1.0+incompatible // indirect\n\tgithub.com/sirupsen/logrus v1.8.1 // indirect\n\tgithub.com/stretchr/testify v1.11.1 // indirect\n\tgithub.com/tdewolff/minify/v2 v2.24.8 // indirect\n\tgithub.com/tdewolff/parse/v2 v2.8.5 // indirect\n\tgithub.com/valyala/bytebufferpool v1.0.0 // indirect\n\tgithub.com/vmihailenco/msgpack/v5 v5.4.1 // indirect\n\tgithub.com/vmihailenco/tagparser/v2 v2.0.0 // indirect\n\tgithub.com/yosssi/ace v0.0.5 // indirect\n\tgolang.org/x/crypto v0.47.0 // indirect\n\tgolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 // indirect\n\tgolang.org/x/net v0.49.0 // indirect\n\tgolang.org/x/sys v0.40.0 // indirect\n\tgolang.org/x/text v0.33.0 // indirect\n\tgolang.org/x/time v0.14.0 // indirect\n\tgoogle.golang.org/protobuf v1.36.11 // indirect\n\tgopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect\n\tgopkg.in/ini.v1 v1.67.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n"
  },
  {
    "path": "_examples/logging/rollbar/go.sum",
    "content": "github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=\ngithub.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=\ngithub.com/CloudyKit/jet/v6 v6.3.1 h1:6IAo5Cx21xrHVaR8zzXN5gJatKV/wO7Nf6bfCnCSbUw=\ngithub.com/CloudyKit/jet/v6 v6.3.1/go.mod h1:lf8ksdNsxZt7/yH/3n4vJQWA9RUq4wpaHtArHhGVMOw=\ngithub.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc=\ngithub.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=\ngithub.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=\ngithub.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 h1:dG7/gLroJGht/jSQtHiLvT48Hxn+crbmvyItZC8cWOs=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05/go.mod h1:NYezi6wtnJtBm5btoprXc5SvAdqH0XTXWnUup0MptAI=\ngithub.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=\ngithub.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=\ngithub.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=\ngithub.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=\ngithub.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=\ngithub.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=\ngithub.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=\ngithub.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=\ngithub.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=\ngithub.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw=\ngithub.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8=\ngithub.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=\ngithub.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=\ngithub.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=\ngithub.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a h1:l7A0loSszR5zHd/qK53ZIHMO8b3bBSmENnQ6eKnUT0A=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=\ngithub.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=\ngithub.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=\ngithub.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=\ngithub.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=\ngithub.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2 h1:T9THsdP1woyAqKHwjkEsbCnMefsAFvk8iJJKokcJ3Go=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2/go.mod h1:JLDgIqnFy5loDSUv1OA2j0mb6p/rDhiCqigP22Uq9xE=\ngithub.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw=\ngithub.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/kataras/blocks v0.0.8 h1:MrpVhoFTCR2v1iOOfGng5VJSILKeZZI+7NGfxEh3SUM=\ngithub.com/kataras/blocks v0.0.8/go.mod h1:9Jm5zx6BB+06NwA+OhTbHW1xkMOYxahnqTN5DveZ2Yg=\ngithub.com/kataras/golog v0.1.12 h1:Bu7I/G4ilJlbfzjmU39O9N+2uO1pBcMK045fzZ4ytNg=\ngithub.com/kataras/golog v0.1.12/go.mod h1:wrGSbOiBqbQSQznleVNX4epWM8rl9SJ/rmEacl0yqy4=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd h1:oMsQgqKACbUdlaIWnN+EZzzAnHkw90hE/y/R0BSij2U=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd/go.mod h1:yucjtDl9Vn3OctWM1fi0MuM9OckemC7kEreFa9QtPF8=\ngithub.com/kataras/pio v0.0.13 h1:x0rXVX0fviDTXOOLOmr4MUxOabu1InVSTu5itF8CXCM=\ngithub.com/kataras/pio v0.0.13/go.mod h1:k3HNuSw+eJ8Pm2lA4lRhg3DiCjVgHlP8hmXApSej3oM=\ngithub.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY=\ngithub.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4=\ngithub.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA=\ngithub.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw=\ngithub.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=\ngithub.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=\ngithub.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=\ngithub.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8=\ngithub.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=\ngithub.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=\ngithub.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=\ngithub.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=\ngithub.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=\ngithub.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=\ngithub.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=\ngithub.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=\ngithub.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=\ngithub.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=\ngithub.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=\ngithub.com/rollbar/rollbar-go v1.4.8 h1:SAKy97CHXSFZjxQUxmuBnQmfzCjX54kvQGEQZHEqwuQ=\ngithub.com/rollbar/rollbar-go v1.4.8/go.mod h1:I/jSI5yHNj7Uy8oxntmCeBSZ1ILvypqRKlFQvZTINgA=\ngithub.com/rollbar/rollbar-go/errors v1.0.0/go.mod h1:Ie0xEc1Cyj+T4XMO8s0Vf7pMfvSAAy1sb4AYc8aJsao=\ngithub.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=\ngithub.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=\ngithub.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk=\ngithub.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=\ngithub.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=\ngithub.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=\ngithub.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=\ngithub.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/tdewolff/minify/v2 v2.24.8 h1:58/VjsbevI4d5FGV0ZSuBrHMSSkH4MCH0sIz/eKIauE=\ngithub.com/tdewolff/minify/v2 v2.24.8/go.mod h1:0Ukj0CRpo/sW/nd8uZ4ccXaV1rEVIWA3dj8U7+Shhfw=\ngithub.com/tdewolff/parse/v2 v2.8.5 h1:ZmBiA/8Do5Rpk7bDye0jbbDUpXXbCdc3iah4VeUvwYU=\ngithub.com/tdewolff/parse/v2 v2.8.5/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=\ngithub.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE=\ngithub.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=\ngithub.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=\ngithub.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=\ngithub.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=\ngithub.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=\ngithub.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=\ngithub.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=\ngithub.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA=\ngithub.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0=\ngithub.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=\ngithub.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=\ngithub.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=\ngolang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=\ngolang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=\ngolang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=\ngolang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=\ngolang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=\ngolang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=\ngolang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=\ngolang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=\ngoogle.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=\ngopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nmoul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs=\nmoul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE=\n"
  },
  {
    "path": "_examples/logging/rollbar/main.go",
    "content": "package main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"runtime/debug\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/middleware/requestid\"\n\n\t\"github.com/rollbar/rollbar-go\"\n)\n\n// * https://rollbar.com/signup\n// * https://docs.rollbar.com/docs/go\nfunc init() {\n\ttoken := os.Getenv(\"ROLLBAR_TOKEN\") // replace that with your token.\n\tif token == \"\" {\n\t\tpanic(\"ROLLBAR_TOKEN is missing\")\n\t}\n\n\t// rb := rollbar.NewAsync(token, \"production\", \"\", hostname, \"github.com/kataras/iris\")\n\t// Or use the package-level instance:\n\trollbar.SetToken(token)\n\t// defaults to \"development\"\n\trollbar.SetEnvironment(\"production\")\n\t// optional Git hash/branch/tag (required for GitHub integration)\n\t// rollbar.SetCodeVersion(\"v2\")\n\t// optional override; defaults to hostname\n\t// rollbar.SetServerHost(\"web.1\")\n\t// path of project (required for GitHub integration and non-project stacktrace collapsing)\n\trollbar.SetServerRoot(\"github.com/kataras/iris\")\n\n}\n\nfunc main() {\n\tapp := iris.New()\n\t// A middleware which sets the ctx.GetID (or requestid.Get(ctx)).\n\tapp.Use(requestid.New())\n\t// A recover middleware which sends the error trace to the rollbar.\n\tapp.Use(func(ctx iris.Context) {\n\t\tdefer func() {\n\t\t\tif r := recover(); r != nil {\n\t\t\t\tdebug.PrintStack()\n\n\t\t\t\tfile, line := ctx.HandlerFileLine() // the failed handler's source code position.\n\n\t\t\t\t//\t\t\t\t\tcause\t\t\t\t   other info\n\t\t\t\trollbar.Critical(errors.New(fmt.Sprint(r)), iris.Map{\n\t\t\t\t\t\"request_id\":  ctx.GetID(),\n\t\t\t\t\t\"request_ip\":  ctx.RemoteAddr(),\n\t\t\t\t\t\"request_uri\": ctx.FullRequestURI(),\n\t\t\t\t\t\"handler\": iris.Map{\n\t\t\t\t\t\t\"name\": ctx.HandlerName(), // the handler which failed.\n\t\t\t\t\t\t\"file\": fmt.Sprintf(\"%s:%d\", file, line),\n\t\t\t\t\t},\n\t\t\t\t})\n\n\t\t\t\tctx.StopWithStatus(iris.StatusInternalServerError)\n\t\t\t}\n\t\t}()\n\n\t\tctx.Next()\n\t})\n\n\tapp.Get(\"/\", index)\n\tapp.Get(\"/panic\", panicMe)\n\n\t// http://localhost:8080 should add an info message to the rollbar's \"Items\" dashboard.\n\t// http://localhost:8080/panic should add a critical message to the rollbar's \"Items\" dashboard,\n\t// with the corresponding information appending on its \"Occurrences\" tab item, e.g:\n\t// Timestamp (PDT)\n\t// * 2020-06-08 04:47 pm\n\t//\n\t// server.host\n\t// * DESKTOP-HOSTNAME\n\t//\n\t// trace_chain.0.exception.message\n\t// * a critical error message here\n\t//\n\t// custom.handler.file\n\t// * C:/mygopath/src/github.com/kataras/iris/_examples/logging/rollbar/main.go:76\n\t//\n\t// custom.handler.name\n\t// * main.panicMe\n\t//\n\t// custom.request_id\n\t// * cce61665-0c1b-4fb5-8547-06a3537e477c\n\t//\n\t// custom.request_ip\n\t// * ::1\n\t//\n\t// custom.request_uri\n\t// * http://localhost:8080/panic\n\tapp.Listen(\":8080\")\n}\n\nfunc index(ctx iris.Context) {\n\trollbar.Info(fmt.Sprintf(\"Index page requested by %s\", ctx.RemoteAddr()))\n\n\tctx.HTML(\"<h1> Index Page </h1>\")\n}\n\nfunc panicMe(ctx iris.Context) {\n\tpanic(\"a critical error message here\")\n}\n"
  },
  {
    "path": "_examples/monitor/monitor-middleware/main.go",
    "content": "package main\n\nimport (\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n\n\t\"github.com/kataras/iris/v12/middleware/monitor\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\t// Initialize and start the monitor middleware.\n\tm := monitor.New(monitor.Options{\n\t\tRefreshInterval:     2 * time.Second,\n\t\tViewRefreshInterval: 2 * time.Second,\n\t\tViewTitle:           \"MyServer Monitor\",\n\t})\n\t// Manually stop monitoring on CMD/CTRL+C.\n\tiris.RegisterOnInterrupt(m.Stop)\n\n\t// Serve the actual server's process and operating system statistics as JSON.\n\tapp.Post(\"/monitor\", m.Stats)\n\t// Render with the default page.\n\tapp.Get(\"/monitor\", m.View)\n\n\t/* You can protect the /monitor under an /admin group of routes\n\twith basic authentication or any type authorization and authentication system.\n\tExample Code:\n\n\t  app.Post(\"/monitor\", myProtectMiddleware, m.Stats)\n\t  app.Get(\"/monitor\", myProtectMiddleware, m.View)\n\t*/\n\n\t/* You can also get the OS statistics using the Holder.GetStats method.\n\tExample Code:\n\tfor {\n\t\tstats := m.Holder.GetStats()\n\t\tfmt.Printf(\"%#+v\\n\", stats)\n\t\ttime.Sleep(time.Second)\n\t}\n\n\tNote that the same stats are also stored in the expvar metrics:\n\t\t- pid_cpu\n\t\t- pid_ram\n\t\t- pid_conns\n\t\t- os_cpu\n\t\t- os_ram\n\t\t- os_total_ram\n\t\t- os_load_avg\n\t\t- os_conns\n\tCheck https://github.com/iris-contrib/middleware/tree/master/expmetric\n\twhich can be integrated with datadog or other platforms.\n\t*/\n\n\tapp.Get(\"/\", handler)\n\n\tapp.Listen(\":8080\")\n}\n\nfunc handler(ctx iris.Context) {\n\tctx.WriteString(\"Test Index Handler\")\n}\n"
  },
  {
    "path": "_examples/monitor/statsviz/go.mod",
    "content": "module github.com/kataras/iris/_examples/statsviz\n\ngo 1.25\n\nrequire (\n\tgithub.com/arl/statsviz v0.8.0\n\tgithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd\n)\n\nrequire (\n\tgithub.com/BurntSushi/toml v1.6.0 // indirect\n\tgithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect\n\tgithub.com/CloudyKit/jet/v6 v6.3.1 // indirect\n\tgithub.com/Joker/jade v1.1.3 // indirect\n\tgithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 // indirect\n\tgithub.com/andybalholm/brotli v1.2.0 // indirect\n\tgithub.com/aymerick/douceur v0.2.0 // indirect\n\tgithub.com/fatih/structs v1.1.0 // indirect\n\tgithub.com/flosch/pongo2/v4 v4.0.2 // indirect\n\tgithub.com/golang/snappy v1.0.0 // indirect\n\tgithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a // indirect\n\tgithub.com/google/uuid v1.6.0 // indirect\n\tgithub.com/gorilla/css v1.0.1 // indirect\n\tgithub.com/gorilla/websocket v1.5.3 // indirect\n\tgithub.com/iris-contrib/schema v0.0.6 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/kataras/blocks v0.0.8 // indirect\n\tgithub.com/kataras/golog v0.1.12 // indirect\n\tgithub.com/kataras/pio v0.0.13 // indirect\n\tgithub.com/kataras/sitemap v0.0.6 // indirect\n\tgithub.com/kataras/tunnel v0.0.4 // indirect\n\tgithub.com/klauspost/compress v1.18.2 // indirect\n\tgithub.com/kr/pretty v0.3.1 // indirect\n\tgithub.com/mailgun/raymond/v2 v2.0.48 // indirect\n\tgithub.com/mailru/easyjson v0.9.1 // indirect\n\tgithub.com/microcosm-cc/bluemonday v1.0.27 // indirect\n\tgithub.com/russross/blackfriday/v2 v2.1.0 // indirect\n\tgithub.com/schollz/closestmatch v2.1.0+incompatible // indirect\n\tgithub.com/sirupsen/logrus v1.8.1 // indirect\n\tgithub.com/stretchr/testify v1.11.1 // indirect\n\tgithub.com/tdewolff/minify/v2 v2.24.8 // indirect\n\tgithub.com/tdewolff/parse/v2 v2.8.5 // indirect\n\tgithub.com/valyala/bytebufferpool v1.0.0 // indirect\n\tgithub.com/vmihailenco/msgpack/v5 v5.4.1 // indirect\n\tgithub.com/vmihailenco/tagparser/v2 v2.0.0 // indirect\n\tgithub.com/yosssi/ace v0.0.5 // indirect\n\tgolang.org/x/crypto v0.47.0 // indirect\n\tgolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 // indirect\n\tgolang.org/x/net v0.49.0 // indirect\n\tgolang.org/x/sys v0.40.0 // indirect\n\tgolang.org/x/text v0.33.0 // indirect\n\tgolang.org/x/time v0.14.0 // indirect\n\tgoogle.golang.org/protobuf v1.36.11 // indirect\n\tgopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect\n\tgopkg.in/ini.v1 v1.67.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n"
  },
  {
    "path": "_examples/monitor/statsviz/go.sum",
    "content": "github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=\ngithub.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=\ngithub.com/CloudyKit/jet/v6 v6.3.1 h1:6IAo5Cx21xrHVaR8zzXN5gJatKV/wO7Nf6bfCnCSbUw=\ngithub.com/CloudyKit/jet/v6 v6.3.1/go.mod h1:lf8ksdNsxZt7/yH/3n4vJQWA9RUq4wpaHtArHhGVMOw=\ngithub.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc=\ngithub.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=\ngithub.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=\ngithub.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 h1:dG7/gLroJGht/jSQtHiLvT48Hxn+crbmvyItZC8cWOs=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05/go.mod h1:NYezi6wtnJtBm5btoprXc5SvAdqH0XTXWnUup0MptAI=\ngithub.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=\ngithub.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=\ngithub.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=\ngithub.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=\ngithub.com/arl/statsviz v0.8.0 h1:O6GjjVxEDxcByAucOSl29HaGYLXsuwA3ujJw8H9E7/U=\ngithub.com/arl/statsviz v0.8.0/go.mod h1:XlrbiT7xYT03xaW9JMMfD8KFUhBOESJwfyNJu83PbB0=\ngithub.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=\ngithub.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=\ngithub.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=\ngithub.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=\ngithub.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=\ngithub.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw=\ngithub.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8=\ngithub.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=\ngithub.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=\ngithub.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=\ngithub.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a h1:l7A0loSszR5zHd/qK53ZIHMO8b3bBSmENnQ6eKnUT0A=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=\ngithub.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=\ngithub.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=\ngithub.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=\ngithub.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=\ngithub.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2 h1:T9THsdP1woyAqKHwjkEsbCnMefsAFvk8iJJKokcJ3Go=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2/go.mod h1:JLDgIqnFy5loDSUv1OA2j0mb6p/rDhiCqigP22Uq9xE=\ngithub.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw=\ngithub.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/kataras/blocks v0.0.8 h1:MrpVhoFTCR2v1iOOfGng5VJSILKeZZI+7NGfxEh3SUM=\ngithub.com/kataras/blocks v0.0.8/go.mod h1:9Jm5zx6BB+06NwA+OhTbHW1xkMOYxahnqTN5DveZ2Yg=\ngithub.com/kataras/golog v0.1.12 h1:Bu7I/G4ilJlbfzjmU39O9N+2uO1pBcMK045fzZ4ytNg=\ngithub.com/kataras/golog v0.1.12/go.mod h1:wrGSbOiBqbQSQznleVNX4epWM8rl9SJ/rmEacl0yqy4=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd h1:oMsQgqKACbUdlaIWnN+EZzzAnHkw90hE/y/R0BSij2U=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd/go.mod h1:yucjtDl9Vn3OctWM1fi0MuM9OckemC7kEreFa9QtPF8=\ngithub.com/kataras/pio v0.0.13 h1:x0rXVX0fviDTXOOLOmr4MUxOabu1InVSTu5itF8CXCM=\ngithub.com/kataras/pio v0.0.13/go.mod h1:k3HNuSw+eJ8Pm2lA4lRhg3DiCjVgHlP8hmXApSej3oM=\ngithub.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY=\ngithub.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4=\ngithub.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA=\ngithub.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw=\ngithub.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=\ngithub.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=\ngithub.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=\ngithub.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8=\ngithub.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=\ngithub.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=\ngithub.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=\ngithub.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=\ngithub.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=\ngithub.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=\ngithub.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=\ngithub.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=\ngithub.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=\ngithub.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=\ngithub.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=\ngithub.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=\ngithub.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk=\ngithub.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=\ngithub.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=\ngithub.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=\ngithub.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=\ngithub.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/tdewolff/minify/v2 v2.24.8 h1:58/VjsbevI4d5FGV0ZSuBrHMSSkH4MCH0sIz/eKIauE=\ngithub.com/tdewolff/minify/v2 v2.24.8/go.mod h1:0Ukj0CRpo/sW/nd8uZ4ccXaV1rEVIWA3dj8U7+Shhfw=\ngithub.com/tdewolff/parse/v2 v2.8.5 h1:ZmBiA/8Do5Rpk7bDye0jbbDUpXXbCdc3iah4VeUvwYU=\ngithub.com/tdewolff/parse/v2 v2.8.5/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=\ngithub.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE=\ngithub.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=\ngithub.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=\ngithub.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=\ngithub.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=\ngithub.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=\ngithub.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=\ngithub.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=\ngithub.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA=\ngithub.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0=\ngithub.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=\ngithub.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=\ngithub.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=\ngolang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=\ngolang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=\ngolang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=\ngolang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=\ngolang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=\ngolang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=\ngolang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=\ngolang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=\ngolang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA=\ngolang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=\ngoogle.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=\ngopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nmoul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs=\nmoul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE=\n"
  },
  {
    "path": "_examples/monitor/statsviz/main.go",
    "content": "package main\n\nimport (\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n\n\t\"github.com/arl/statsviz\"\n)\n\n// $ go get github.com/arl/statsviz\nfunc main() {\n\tapp := iris.New()\n\t// Register a router wrapper for this one.\n\tstatsvizPath := \"/debug/statsviz\"\n\tserveRoot := statsviz.IndexAtRoot(statsvizPath)\n\tserveWS := statsviz.NewWsHandler(time.Second)\n\tapp.UseRouter(func(ctx iris.Context) {\n\t\t// You can optimize this if branch, I leave it to you as an exercise.\n\t\tif strings.HasPrefix(ctx.Path(), statsvizPath+\"/ws\") {\n\t\t\tserveWS(ctx.ResponseWriter(), ctx.Request())\n\t\t} else if strings.HasPrefix(ctx.Path(), statsvizPath) {\n\t\t\tserveRoot(ctx.ResponseWriter(), ctx.Request())\n\t\t} else {\n\t\t\tctx.Next()\n\t\t}\n\t})\n\t//\n\n\t// Register other routes.\n\tapp.Get(\"/\", index)\n\n\t// Navigate to: http://localhost:8080/debug/statsviz/\n\tapp.Listen(\":8080\")\n}\n\nfunc index(ctx iris.Context) {\n\tctx.WriteString(\"Hello, World!\")\n}\n"
  },
  {
    "path": "_examples/mvc/authenticated-controller/main.go",
    "content": "// Package main shows how to use a dependency to check if a user is logged in\n// using a special custom Go type `Authenticated`, which when,\n// present on a controller's method or a field then\n// it limits the visibility to \"authenticated\" users only.\n//\n// The same result could be done through a middleware as well, however using a static type\n// any person reads your code can see that an \"X\" controller or method\n// should only be used by \"authenticated\" users.\npackage main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/mvc\"\n\t\"github.com/kataras/iris/v12/sessions\"\n)\n\nfunc main() {\n\tapp := newApp()\n\t// app.UseRouter(iris.Compression)\n\tapp.Logger().SetLevel(\"debug\")\n\n\t// Open a client, e.g. Postman and visit the below endpoints.\n\t// GET: http://localhost:8080/user (UnauthenticatedUserController.Get)\n\t// POST: http://localhost:8080/user/login (UnauthenticatedUserController.PostLogin)\n\t// GET: http://localhost:8080/user (UserController.Get)\n\t// POST: http://localhost:8080/user/logout (UserController.PostLogout)\n\tapp.Listen(\":8080\")\n}\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\tsess := sessions.New(sessions.Config{\n\t\tCookie:       \"myapp_session_id\",\n\t\tAllowReclaim: true,\n\t})\n\tapp.Use(sess.Handler())\n\n\tuserRouter := app.Party(\"/user\")\n\t{\n\t\t// Use that in order to be able to register a route twice,\n\t\t// last one will be executed if the previous route's handler(s) stopped and the response can be reset-ed.\n\t\t// See core/router/route_register_rule_test.go#TestRegisterRuleOverlap.\n\t\tuserRouter.SetRegisterRule(iris.RouteOverlap)\n\n\t\t// Initialize a new MVC application on top of the \"userRouter\".\n\t\tuserApp := mvc.New(userRouter)\n\t\t// Register Dependencies.\n\t\tuserApp.Register(authDependency)\n\n\t\t// Register Controllers.\n\t\tuserApp.Handle(new(UserController))\n\t\tuserApp.Handle(new(UnauthenticatedUserController))\n\t}\n\n\treturn app\n}\n\n// Authenticated is a custom type used as \"annotation\" for resources that requires authentication,\n// its value should be the logged user ID.\ntype Authenticated uint64\n\nfunc authDependency(ctx iris.Context, session *sessions.Session) Authenticated {\n\tuserID := session.GetUint64Default(\"user_id\", 0)\n\tif userID == 0 {\n\t\t// If execution was stopped\n\t\t// any controller's method will not be executed at all.\n\t\t//\n\t\t// Note that, the below will not fire the error to the user:\n\t\t// ctx.StopWithStatus(iris.StatusUnauthorized)\n\t\t// because of the imaginary:\n\t\t// UnauthenticatedUserController.Get() (string, int) {\n\t\t// \treturn \"...\", iris.StatusOK\n\t\t// }\n\t\t//\n\t\t// OR\n\t\t// If you don't want to set a status code at all:\n\t\tctx.StopExecution()\n\t\treturn 0\n\t}\n\n\treturn Authenticated(userID)\n}\n\n// UnauthenticatedUserController serves the \"public\" Unauthorized User API.\ntype UnauthenticatedUserController struct{}\n\n// Get registers a route that will be executed when authentication is not passed\n// (see UserController.Get) too.\nfunc (c *UnauthenticatedUserController) Get() string {\n\treturn \"custom action to redirect on authentication page\"\n}\n\n// PostLogin serves\n// POST: /user/login\nfunc (c *UnauthenticatedUserController) PostLogin(session *sessions.Session) mvc.Response {\n\tsession.Set(\"user_id\", 1)\n\n\t// Redirect (you can still use the Context.Redirect if you want so).\n\treturn mvc.Response{\n\t\tPath: \"/user\",\n\t\tCode: iris.StatusFound,\n\t}\n}\n\n// UserController serves the \"public\" User API.\ntype UserController struct {\n\tCurrentUserID Authenticated\n}\n\n// Get returns a message for the sake of the example.\n// GET: /user\nfunc (c *UserController) Get() string {\n\treturn `UserController.Get: The Authenticated type\ncan be used to secure a controller's method too.`\n}\n\n// PostLogout serves\n// POST: /user/logout\nfunc (c *UserController) PostLogout(ctx iris.Context) {\n\tsessions.Get(ctx).Man.Destroy(ctx)\n}\n"
  },
  {
    "path": "_examples/mvc/authenticated-controller/main_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nfunc TestMVCOverlapping(t *testing.T) {\n\tapp := newApp()\n\n\te := httptest.New(t, app, httptest.URL(\"http://example.com\"))\n\t// unauthenticated.\n\te.GET(\"/user\").Expect().Status(httptest.StatusOK).Body().IsEqual(\"custom action to redirect on authentication page\")\n\t// login.\n\te.POST(\"/user/login\").Expect().Status(httptest.StatusOK)\n\t// authenticated.\n\te.GET(\"/user\").Expect().Status(httptest.StatusOK).Body().IsEqual(`UserController.Get: The Authenticated type\ncan be used to secure a controller's method too.`)\n\t// logout.\n\te.POST(\"/user/logout\").Expect().Status(httptest.StatusOK)\n\t// unauthenticated.\n\te.GET(\"/user\").Expect().Status(httptest.StatusOK).Body().IsEqual(\"custom action to redirect on authentication page\")\n}\n"
  },
  {
    "path": "_examples/mvc/basic/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/middleware/accesslog\"\n\t\"github.com/kataras/iris/v12/middleware/recover\"\n\t\"github.com/kataras/iris/v12/sessions\"\n\n\t\"github.com/kataras/iris/v12/mvc\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.Logger().SetLevel(\"debug\")\n\n\tbasic := app.Party(\"/basic\")\n\t{\n\t\t// Register middlewares to run under the /basic path prefix.\n\t\tac := accesslog.File(\"./basic_access.log\")\n\t\tdefer ac.Close()\n\n\t\tbasic.UseRouter(ac.Handler)\n\t\tbasic.UseRouter(recover.New())\n\n\t\tmvc.Configure(basic, basicMVC)\n\t}\n\n\tapp.Listen(\":8080\")\n}\n\nfunc basicMVC(app *mvc.Application) {\n\t// Disable verbose logging of controllers for this and its children mvc apps\n\t// when the log level is \"debug\":\n\tapp.SetControllersNoLog(true)\n\n\t// You can still register middlewares at MVC apps of course.\n\t// The app.Router returns the Party that this MVC\n\t// was registered on.\n\t// app.Router.UseRouter/Use/....\n\n\t// Register dependencies which will be binding to the controller(s),\n\t// can be either a function which accepts an iris.Context and returns a single value (dynamic binding)\n\t// or a static struct value (service).\n\tapp.Register(\n\t\tsessions.New(sessions.Config{}).Start,\n\t\t&prefixedLogger{prefix: \"DEV\"},\n\t\taccesslog.GetFields, // Set custom fields through a controller or controller's methods.\n\t)\n\n\t// GET: http://localhost:8080/basic\n\t// GET: http://localhost:8080/basic/custom\n\t// GET: http://localhost:8080/basic/custom2\n\tapp.Handle(new(basicController))\n\n\t// All dependencies of the parent *mvc.Application\n\t// are cloned to this new child,\n\t// thefore it has access to the same session as well.\n\t// GET: http://localhost:8080/basic/sub\n\tapp.Party(\"/sub\").\n\t\tHandle(new(basicSubController))\n}\n\n// If controller's fields (or even its functions) expecting an interface\n// but a struct value is binded then it will check\n// if that struct value implements\n// the interface and if true then it will add this to the\n// available bindings, as expected, before the server ran of course,\n// remember? Iris always uses the best possible way to reduce load\n// on serving web resources.\n\ntype LoggerService interface {\n\tLog(string)\n}\n\ntype prefixedLogger struct {\n\tprefix string\n}\n\nfunc (s *prefixedLogger) Log(msg string) {\n\tfmt.Printf(\"%s: %s\\n\", s.prefix, msg)\n}\n\ntype basicController struct {\n\tLogger    LoggerService     // the static logger service attached to this app.\n\tSession   *sessions.Session // current HTTP session.\n\tLogFields *accesslog.Fields // accesslog middleware custom fields.\n}\n\nfunc (c *basicController) BeforeActivation(b mvc.BeforeActivation) {\n\tb.HandleMany(\"GET\", \"/custom /custom2\", \"Custom\")\n}\n\nfunc (c *basicController) AfterActivation(a mvc.AfterActivation) {\n\tif a.Singleton() {\n\t\tpanic(\"basicController should be stateless, a request-scoped, we have a 'Session' which depends on the context.\")\n\t}\n}\n\nfunc (c *basicController) Get() string {\n\tcount := c.Session.Increment(\"count\", 1)\n\tc.LogFields.Set(\"count\", count)\n\n\tbody := fmt.Sprintf(\"Hello from basicController\\nTotal visits from you: %d\", count)\n\tc.Logger.Log(body)\n\treturn body\n}\n\nfunc (c *basicController) Custom() string {\n\treturn \"custom\"\n}\n\ntype basicSubController struct {\n\tSession *sessions.Session\n}\n\nfunc (c *basicSubController) Get() string {\n\tcount := c.Session.GetIntDefault(\"count\", 1)\n\treturn fmt.Sprintf(\"Hello from basicSubController.\\nRead-only visits count: %d\", count)\n}\n"
  },
  {
    "path": "_examples/mvc/basic/wildcard/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/mvc\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tusersRouter := app.Party(\"/users\")\n\tmvc.New(usersRouter).Handle(new(myController))\n\t// Same as:\n\t// usersRouter.Get(\"/{p:path}\", func(ctx iris.Context) {\n\t// \twildcardPathParameter := ctx.Params().Get(\"p\")\n\t// \tctx.JSON(response{\n\t// \t\tMessage: \"The path parameter is: \" + wildcardPathParameter,\n\t// \t})\n\t// })\n\n\t/*\n\t\tcurl --location --request GET 'http://localhost:8080/users/path_segment_1/path_segment_2'\n\n\t\tExpected Output:\n\t\t{\n\t\t  \"message\": \"The wildcard is: path_segment_1/path_segment_2\"\n\t\t}\n\t*/\n\tapp.Listen(\":8080\")\n}\n\ntype myController struct{}\n\ntype response struct {\n\tMessage string `json:\"message\"`\n}\n\nfunc (c *myController) GetByWildcard(wildcardPathParameter string) response {\n\treturn response{\n\t\tMessage: \"The path parameter is: \" + wildcardPathParameter,\n\t}\n}\n"
  },
  {
    "path": "_examples/mvc/error-handler/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n\n\t\"github.com/kataras/iris/v12/mvc\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.Logger().SetLevel(\"debug\")\n\n\tmvcApp := mvc.New(app)\n\t// To all controllers, it can optionally be overridden per-controller\n\t// if the controller contains the `HandleError(ctx iris.Context, err error)` function.\n\t//\n\tmvcApp.HandleError(func(ctx iris.Context, err error) {\n\t\tctx.HTML(fmt.Sprintf(\"<b>%s</b>\", err.Error()))\n\t})\n\t//\n\tmvcApp.Handle(new(myController))\n\n\t// http://localhost:8080\n\tapp.Listen(\":8080\")\n}\n\ntype myController struct {\n}\n\n// overriddes the mvcApp.HandleError function.\nfunc (c *myController) HandleError(ctx iris.Context, err error) {\n\tctx.HTML(fmt.Sprintf(\"<i>%s</i>\", err.Error()))\n}\n\nfunc (c *myController) Get() error {\n\treturn fmt.Errorf(\"error here\")\n}\n"
  },
  {
    "path": "_examples/mvc/error-handler-custom-result/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/mvc\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.RegisterView(iris.HTML(\"./views\", \".html\"))\n\n\tm := mvc.New(app)\n\tm.Handle(new(controller))\n\n\tapp.Listen(\":8080\")\n}\n\ntype errorResponse struct {\n\tCode    int\n\tMessage string\n}\n\n/*\n// Note: if a struct implements the standard go error, so it's an error\n// and its Error() is not empty, then its text will be rendered instead,\n// override any Dispatch method.\nfunc (e errorResponse) Error() string {\n\treturn e.Message\n}\n*/\n\n// implements mvc.Result.\nfunc (e errorResponse) Dispatch(ctx iris.Context) {\n\t// If u want to use mvc.Result on any method without an output return value\n\t// go for it:\n\t//\n\tview := mvc.View{Code: e.Code, Data: e} // use Code and Message as the template data.\n\tswitch e.Code {\n\tcase iris.StatusNotFound:\n\t\tview.Name = \"404\"\n\tdefault:\n\t\tview.Name = \"500\"\n\t}\n\tview.Dispatch(ctx)\n\n\t// Otherwise use ctx methods:\n\t//\n\t// ctx.StatusCode(e.Code)\n\t// switch e.Code {\n\t// case iris.StatusNotFound:\n\t// \t// use Code and Message as the template data.\n\t// if err := ctx.View(\"404.html\", e)\n\t// default:\n\t// if err := ctx.View(\"500.html\", e)\n\t// }\n}\n\ntype controller struct{}\n\ntype user struct {\n\tID uint64 `json:\"id\"`\n}\n\nfunc (c *controller) GetBy(userid uint64) mvc.Result {\n\tif userid != 1 {\n\t\treturn errorResponse{\n\t\t\tCode:    iris.StatusNotFound,\n\t\t\tMessage: \"User Not Found\",\n\t\t}\n\t}\n\n\treturn mvc.Response{\n\t\tObject: user{ID: userid},\n\t}\n}\n"
  },
  {
    "path": "_examples/mvc/error-handler-custom-result/views/404.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Client Error Page</title>\n</head>\n<body>\n    <h2>{{.Code}}</h2>\n    <h3>{{.Message}}</h3>\n</body>\n</html>"
  },
  {
    "path": "_examples/mvc/error-handler-custom-result/views/500.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Server Error Page</title>\n</head>\n<body>\n    <h2>{{.Code}}</h2>\n    <h3>{{.Message}}</h3>\n</body>\n</html>"
  },
  {
    "path": "_examples/mvc/error-handler-hijack/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/mvc\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.RegisterView(iris.HTML(\"./views\", \".html\"))\n\n\t// Hijack each output value of a method (can be used per-party too).\n\tapp.ConfigureContainer().\n\t\tUseResultHandler(func(next iris.ResultHandler) iris.ResultHandler {\n\t\t\treturn func(ctx iris.Context, v any) error {\n\t\t\t\tswitch val := v.(type) {\n\t\t\t\tcase errorResponse:\n\t\t\t\t\treturn next(ctx, errorView(val))\n\t\t\t\tdefault:\n\t\t\t\t\treturn next(ctx, v)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\n\tm := mvc.New(app)\n\tm.Handle(new(controller))\n\n\tapp.Listen(\":8080\")\n}\n\nfunc errorView(e errorResponse) mvc.Result {\n\tswitch e.Code {\n\tcase iris.StatusNotFound:\n\t\treturn mvc.View{Code: e.Code, Name: \"404.html\", Data: e}\n\tdefault:\n\t\treturn mvc.View{Code: e.Code, Name: \"500.html\", Data: e}\n\t}\n}\n\ntype errorResponse struct {\n\tCode    int\n\tMessage string\n}\n\ntype controller struct{}\n\ntype user struct {\n\tID uint64 `json:\"id\"`\n}\n\nfunc (c *controller) GetBy(userid uint64) any {\n\tif userid != 1 {\n\t\treturn errorResponse{\n\t\t\tCode:    iris.StatusNotFound,\n\t\t\tMessage: \"User Not Found\",\n\t\t}\n\t}\n\n\treturn user{ID: userid}\n}\n"
  },
  {
    "path": "_examples/mvc/error-handler-hijack/views/404.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Client Error Page</title>\n</head>\n<body>\n    <h2>{{.Code}}</h2>\n    <h3>{{.Message}}</h3>\n</body>\n</html>"
  },
  {
    "path": "_examples/mvc/error-handler-hijack/views/500.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Server Error Page</title>\n</head>\n<body>\n    <h2>{{.Code}}</h2>\n    <h3>{{.Message}}</h3>\n</body>\n</html>"
  },
  {
    "path": "_examples/mvc/error-handler-http/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/mvc\"\n)\n\nfunc main() {\n\tapp := newApp()\n\tapp.Logger().SetLevel(\"debug\")\n\tapp.Listen(\":8080\")\n}\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\tapp.RegisterView(iris.HTML(\"./views\", \".html\"))\n\n\tm := mvc.New(app)\n\tm.Handle(new(controller))\n\n\treturn app\n}\n\ntype controller struct{}\n\nfunc (c *controller) Get() string {\n\treturn \"Hello!\"\n}\n\nfunc (c *controller) GetError() mvc.Result {\n\treturn mvc.View{\n\t\t// Map to mvc.Code and mvc.Err respectfully on HandleHTTPError method.\n\t\tCode: iris.StatusBadRequest,\n\t\tErr:  fmt.Errorf(\"custom error\"),\n\t}\n}\n\n// The input parameter of mvc.Code is optional but a good practise to follow.\n// You could  register a Context and get its error code through ctx.GetStatusCode().\n//\n// This can accept dependencies and output values like any other Controller Method,\n// however be careful if your registered dependencies depend only on successful(200...) requests.\n//\n// Also note that, if you register more than one controller.HandleHTTPError\n// in the same Party, you need to use the RouteOverlap feature as shown\n// in the \"authenticated-controller\" example, and a dependency on\n// a controller's field (or method's input argument) is required\n// to select which, between those two controllers, is responsible\n// to handle http errors.\nfunc (c *controller) HandleHTTPError(statusCode mvc.Code, err mvc.Err) mvc.View {\n\tif err != nil {\n\t\t// Do something with that error,\n\t\t// e.g. view.Data = MyPageData{Message: err.Error()}\n\t}\n\n\tcode := int(statusCode) // cast it to int.\n\n\tview := mvc.View{\n\t\tCode: code,\n\t\tName: \"unexpected-error.html\",\n\t}\n\n\tswitch code {\n\tcase 404:\n\t\tview.Name = \"404.html\"\n\t\t// [...]\n\tcase 500:\n\t\tview.Name = \"500.html\"\n\t}\n\n\treturn view\n}\n"
  },
  {
    "path": "_examples/mvc/error-handler-http/main_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nfunc TestControllerHandleHTTPError(t *testing.T) {\n\tconst (\n\t\texpectedIndex    = \"Hello!\"\n\t\texpectedNotFound = \"<h3>Not Found Custom Page Rendered through Controller's HandleHTTPError</h3>\"\n\t)\n\n\tapp := newApp()\n\n\te := httptest.New(t, app)\n\te.GET(\"/\").Expect().Status(httptest.StatusOK).Body().IsEqual(expectedIndex)\n\te.GET(\"/a_notefound\").Expect().Status(httptest.StatusNotFound).ContentType(\"text/html\").Body().IsEqual(expectedNotFound)\n}\n"
  },
  {
    "path": "_examples/mvc/error-handler-http/views/404.html",
    "content": "<h3>Not Found Custom Page Rendered through Controller's HandleHTTPError</h3>"
  },
  {
    "path": "_examples/mvc/error-handler-http/views/500.html",
    "content": "<h3>Internal Server Err</h3>"
  },
  {
    "path": "_examples/mvc/error-handler-http/views/unexpected-error.html",
    "content": "<h1>Unexpected Error</h1>"
  },
  {
    "path": "_examples/mvc/error-handler-preflight/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/mvc\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.RegisterView(iris.HTML(\"./views\", \".html\"))\n\n\tm := mvc.New(app)\n\tm.Handle(new(controller))\n\n\tapp.Listen(\":8080\")\n}\n\ntype controller struct{}\n\n// Generic response type for JSON results.\ntype response struct {\n\tID        uint64 `json:\"id,omitempty\"`\n\tData      any    `json:\"data,omitempty\"` // {data: result } on fetch actions.\n\tCode      int    `json:\"code,omitempty\"`\n\tMessage   string `json:\"message,omitempty\"`\n\tTimestamp int64  `json:\"timestamp,omitempty\"`\n}\n\nfunc (r *response) Preflight(ctx iris.Context) error {\n\tif r.ID > 0 {\n\t\tr.Timestamp = time.Now().Unix()\n\t}\n\n\tif code := r.Code; code > 0 {\n\t\t// You can call ctx.View or mvc.View{...}.Dispatch\n\t\t// to render HTML on Code != 200\n\t\t// but in order to not proceed with the response resulting\n\t\t// as JSON you MUST return the iris.ErrStopExecution error.\n\t\t// Example:\n\t\tif code != 200 {\n\t\t\tmvc.View{\n\t\t\t\t/* calls the ctx.StatusCode */\n\t\t\t\tCode: code,\n\t\t\t\t/* use any r.Data as the template data\n\t\t\t\tOR the whole \"response\" as its data. */\n\t\t\t\tData: r,\n\t\t\t\t/* automatically pick the template per error (just for the sake of the example) */\n\t\t\t\tName: fmt.Sprintf(\"%d\", code),\n\t\t\t}.Dispatch(ctx)\n\n\t\t\treturn iris.ErrStopExecution\n\t\t}\n\n\t\tctx.StatusCode(r.Code)\n\t}\n\n\treturn nil\n}\n\ntype user struct {\n\tID uint64 `json:\"id\"`\n}\n\nfunc (c *controller) GetBy(userid uint64) *response {\n\tif userid != 1 {\n\t\treturn &response{\n\t\t\tCode:    iris.StatusNotFound,\n\t\t\tMessage: \"User Not Found\",\n\t\t}\n\t}\n\n\treturn &response{\n\t\tID:   userid,\n\t\tData: user{ID: userid},\n\t}\n}\n\n/*\n\nYou can use that `response` structure on non-mvc applications too, using handlers:\n\nc := app.ConfigureContainer()\nc.Get(\"/{id:uint64}\", getUserByID)\nfunc getUserByID(id uint64) response {\n\tif userid != 1 {\n\t\treturn response{\n\t\t\tCode:    iris.StatusNotFound,\n\t\t\tMessage: \"User Not Found\",\n\t\t}\n\t}\n\n\treturn response{\n\t\tID:   userid,\n\t\tData: user{ID: userid},\n\t}\n}\n\n*/\n"
  },
  {
    "path": "_examples/mvc/error-handler-preflight/views/404.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Client Error Page</title>\n</head>\n<body>\n    <h2>{{.Code}}</h2>\n    <h3>{{.Message}}</h3>\n</body>\n</html>"
  },
  {
    "path": "_examples/mvc/error-handler-preflight/views/500.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Server Error Page</title>\n</head>\n<body>\n    <h2>{{.Code}}</h2>\n    <h3>{{.Message}}</h3>\n</body>\n</html>"
  },
  {
    "path": "_examples/mvc/grpc-compatible/README.md",
    "content": "# gRPC Iris Example\n\n## Generate TLS Keys\n\n```sh\n$ openssl genrsa -out server.key 2048\n$ openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650\n```\n\n## Install the protoc Go plugin\n\n```sh\n$ go get -u github.com/golang/protobuf/protoc-gen-go\n```\n\n## Generate proto\n\n```sh\n$ protoc -I helloworld/ helloworld/helloworld.proto --go_out=plugins=grpc:helloworld\n```\n"
  },
  {
    "path": "_examples/mvc/grpc-compatible/grpc-client/main.go",
    "content": "// Package main implements a client for Greeter service.\npackage main\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"log\"\n\t\"os\"\n\t\"time\"\n\n\tpb \"github.com/kataras/iris/v12/_examples/mvc/grpc-compatible/helloworld\"\n\n\t\"google.golang.org/grpc\"\n\t\"google.golang.org/grpc/credentials\"\n)\n\nconst (\n\taddress     = \"localhost:443\"\n\tdefaultName = \"world\"\n)\n\nfunc main() {\n\t// Set up a connection to the server.\n\t// cred, err := credentials.NewClientTLSFromFile(\"../server.crt\", \"localhost\")\n\t// if err != nil {\n\t// \tlog.Fatal(err)\n\t// }\n\tcred := credentials.NewTLS(&tls.Config{\n\t\tInsecureSkipVerify: true,\n\t\tRenegotiation:      tls.RenegotiateNever,\n\t})\n\n\tconn, err := grpc.Dial(address, grpc.WithTransportCredentials(cred), grpc.WithBlock())\n\tif err != nil {\n\t\tlog.Fatalf(\"did not connect: %v\", err)\n\t}\n\tdefer conn.Close()\n\tc := pb.NewGreeterClient(conn)\n\n\t// Contact the server and print out its response.\n\tname := defaultName\n\tif len(os.Args) > 1 {\n\t\tname = os.Args[1]\n\t}\n\tctx, cancel := context.WithTimeout(context.Background(), time.Second)\n\tdefer cancel()\n\tr, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})\n\tif err != nil {\n\t\tlog.Fatalf(\"could not greet: %v\", err)\n\t}\n\tlog.Printf(\"Greeting: %s\", r.GetMessage())\n}\n"
  },
  {
    "path": "_examples/mvc/grpc-compatible/helloworld/README.md",
    "content": "# Helloworld gRPC Example\n\nhttps://github.com/grpc/grpc-go/tree/master/examples/helloworld"
  },
  {
    "path": "_examples/mvc/grpc-compatible/helloworld/helloworld.pb.go",
    "content": "// Copyright 2015 gRPC authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.21.0\n// \tprotoc        v3.11.1\n// source: helloworld.proto\n\npackage helloworld\n\nimport (\n\tcontext \"context\"\n\tgrpc \"google.golang.org/grpc\"\n\tcodes \"google.golang.org/grpc/codes\"\n\tstatus \"google.golang.org/grpc/status\"\n\tprotoreflect \"google.golang.org/protobuf/reflect/protoreflect\"\n\tprotoimpl \"google.golang.org/protobuf/runtime/protoimpl\"\n\treflect \"reflect\"\n\tsync \"sync\"\n)\n\nconst (\n\t// Verify that this generated code is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)\n\t// Verify that runtime/protoimpl is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)\n)\n\n// The request message containing the user's name.\ntype HelloRequest struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tName string `protobuf:\"bytes,1,opt,name=name,proto3\" json:\"name,omitempty\"`\n}\n\nfunc (x *HelloRequest) Reset() {\n\t*x = HelloRequest{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_helloworld_proto_msgTypes[0]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *HelloRequest) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*HelloRequest) ProtoMessage() {}\n\nfunc (x *HelloRequest) ProtoReflect() protoreflect.Message {\n\tmi := &file_helloworld_proto_msgTypes[0]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use HelloRequest.ProtoReflect.Descriptor instead.\nfunc (*HelloRequest) Descriptor() ([]byte, []int) {\n\treturn file_helloworld_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *HelloRequest) GetName() string {\n\tif x != nil {\n\t\treturn x.Name\n\t}\n\treturn \"\"\n}\n\n// The response message containing the greetings\ntype HelloReply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tMessage string `protobuf:\"bytes,1,opt,name=message,proto3\" json:\"message,omitempty\"`\n}\n\nfunc (x *HelloReply) Reset() {\n\t*x = HelloReply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_helloworld_proto_msgTypes[1]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *HelloReply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*HelloReply) ProtoMessage() {}\n\nfunc (x *HelloReply) ProtoReflect() protoreflect.Message {\n\tmi := &file_helloworld_proto_msgTypes[1]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use HelloReply.ProtoReflect.Descriptor instead.\nfunc (*HelloReply) Descriptor() ([]byte, []int) {\n\treturn file_helloworld_proto_rawDescGZIP(), []int{1}\n}\n\nfunc (x *HelloReply) GetMessage() string {\n\tif x != nil {\n\t\treturn x.Message\n\t}\n\treturn \"\"\n}\n\nvar File_helloworld_proto protoreflect.FileDescriptor\n\nvar file_helloworld_proto_rawDesc = []byte{\n\t0x0a, 0x10, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x70, 0x72, 0x6f,\n\t0x74, 0x6f, 0x12, 0x0a, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x22, 0x22,\n\t0x0a, 0x0c, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12,\n\t0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,\n\t0x6d, 0x65, 0x22, 0x26, 0x0a, 0x0a, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79,\n\t0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,\n\t0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x32, 0x49, 0x0a, 0x07, 0x47, 0x72,\n\t0x65, 0x65, 0x74, 0x65, 0x72, 0x12, 0x3e, 0x0a, 0x08, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c,\n\t0x6f, 0x12, 0x18, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48,\n\t0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x68, 0x65,\n\t0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65,\n\t0x70, 0x6c, 0x79, 0x22, 0x00, 0x42, 0x30, 0x0a, 0x1b, 0x69, 0x6f, 0x2e, 0x67, 0x72, 0x70, 0x63,\n\t0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77,\n\t0x6f, 0x72, 0x6c, 0x64, 0x42, 0x0f, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x57, 0x6f, 0x72, 0x6c, 0x64,\n\t0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,\n}\n\nvar (\n\tfile_helloworld_proto_rawDescOnce sync.Once\n\tfile_helloworld_proto_rawDescData = file_helloworld_proto_rawDesc\n)\n\nfunc file_helloworld_proto_rawDescGZIP() []byte {\n\tfile_helloworld_proto_rawDescOnce.Do(func() {\n\t\tfile_helloworld_proto_rawDescData = protoimpl.X.CompressGZIP(file_helloworld_proto_rawDescData)\n\t})\n\treturn file_helloworld_proto_rawDescData\n}\n\nvar file_helloworld_proto_msgTypes = make([]protoimpl.MessageInfo, 2)\nvar file_helloworld_proto_goTypes = []any{\n\t(*HelloRequest)(nil), // 0: helloworld.HelloRequest\n\t(*HelloReply)(nil),   // 1: helloworld.HelloReply\n}\nvar file_helloworld_proto_depIdxs = []int32{\n\t0, // 0: helloworld.Greeter.SayHello:input_type -> helloworld.HelloRequest\n\t1, // 1: helloworld.Greeter.SayHello:output_type -> helloworld.HelloReply\n\t1, // [1:2] is the sub-list for method output_type\n\t0, // [0:1] is the sub-list for method input_type\n\t0, // [0:0] is the sub-list for extension type_name\n\t0, // [0:0] is the sub-list for extension extendee\n\t0, // [0:0] is the sub-list for field type_name\n}\n\nfunc init() { file_helloworld_proto_init() }\nfunc file_helloworld_proto_init() {\n\tif File_helloworld_proto != nil {\n\t\treturn\n\t}\n\tif !protoimpl.UnsafeEnabled {\n\t\tfile_helloworld_proto_msgTypes[0].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*HelloRequest); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_helloworld_proto_msgTypes[1].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*HelloReply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n\ttype x struct{}\n\tout := protoimpl.TypeBuilder{\n\t\tFile: protoimpl.DescBuilder{\n\t\t\tGoPackagePath: reflect.TypeOf(x{}).PkgPath(),\n\t\t\tRawDescriptor: file_helloworld_proto_rawDesc,\n\t\t\tNumEnums:      0,\n\t\t\tNumMessages:   2,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   1,\n\t\t},\n\t\tGoTypes:           file_helloworld_proto_goTypes,\n\t\tDependencyIndexes: file_helloworld_proto_depIdxs,\n\t\tMessageInfos:      file_helloworld_proto_msgTypes,\n\t}.Build()\n\tFile_helloworld_proto = out.File\n\tfile_helloworld_proto_rawDesc = nil\n\tfile_helloworld_proto_goTypes = nil\n\tfile_helloworld_proto_depIdxs = nil\n}\n\n// Reference imports to suppress errors if they are not otherwise used.\nvar _ context.Context\nvar _ grpc.ClientConnInterface\n\n// This is a compile-time assertion to ensure that this generated file\n// is compatible with the grpc package it is being compiled against.\nconst _ = grpc.SupportPackageIsVersion6\n\n// GreeterClient is the client API for Greeter service.\n//\n// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.\ntype GreeterClient interface {\n\t// Sends a greeting\n\tSayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error)\n}\n\ntype greeterClient struct {\n\tcc grpc.ClientConnInterface\n}\n\nfunc NewGreeterClient(cc grpc.ClientConnInterface) GreeterClient {\n\treturn &greeterClient{cc}\n}\n\nfunc (c *greeterClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) {\n\tout := new(HelloReply)\n\terr := c.cc.Invoke(ctx, \"/helloworld.Greeter/SayHello\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\n// GreeterServer is the server API for Greeter service.\ntype GreeterServer interface {\n\t// Sends a greeting\n\tSayHello(context.Context, *HelloRequest) (*HelloReply, error)\n}\n\n// UnimplementedGreeterServer can be embedded to have forward compatible implementations.\ntype UnimplementedGreeterServer struct {\n}\n\nfunc (*UnimplementedGreeterServer) SayHello(context.Context, *HelloRequest) (*HelloReply, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method SayHello not implemented\")\n}\n\nfunc RegisterGreeterServer(s *grpc.Server, srv GreeterServer) {\n\ts.RegisterService(&_Greeter_serviceDesc, srv)\n}\n\nfunc _Greeter_SayHello_Handler(srv any, ctx context.Context, dec func(any) error, interceptor grpc.UnaryServerInterceptor) (any, error) {\n\tin := new(HelloRequest)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(GreeterServer).SayHello(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/helloworld.Greeter/SayHello\",\n\t}\n\thandler := func(ctx context.Context, req any) (any, error) {\n\t\treturn srv.(GreeterServer).SayHello(ctx, req.(*HelloRequest))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nvar _Greeter_serviceDesc = grpc.ServiceDesc{\n\tServiceName: \"helloworld.Greeter\",\n\tHandlerType: (*GreeterServer)(nil),\n\tMethods: []grpc.MethodDesc{\n\t\t{\n\t\t\tMethodName: \"SayHello\",\n\t\t\tHandler:    _Greeter_SayHello_Handler,\n\t\t},\n\t},\n\tStreams:  []grpc.StreamDesc{},\n\tMetadata: \"helloworld.proto\",\n}\n"
  },
  {
    "path": "_examples/mvc/grpc-compatible/helloworld/helloworld.proto",
    "content": "// Copyright 2015 gRPC authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax = \"proto3\";\n\npackage helloworld;\n\n// The greeting service definition.\nservice Greeter {\n  // Sends a greeting\n  rpc SayHello (HelloRequest) returns (HelloReply) {}\n}\n\n// The request message containing the user's name.\nmessage HelloRequest {\n  string name = 1;\n}\n\n// The response message containing the greetings\nmessage HelloReply {\n  string message = 1;\n}"
  },
  {
    "path": "_examples/mvc/grpc-compatible/http-client/main.go",
    "content": "package main\n\nimport (\n\t\"bytes\"\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"encoding/json\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\n\tpb \"github.com/kataras/iris/v12/_examples/mvc/grpc-compatible/helloworld\"\n)\n\nfunc main() {\n\tb, err := os.ReadFile(\"../server.crt\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tcp := x509.NewCertPool()\n\tif !cp.AppendCertsFromPEM(b) {\n\t\tlog.Fatal(\"credentials: failed to append certificates\")\n\t}\n\n\ttransport := &http.Transport{\n\t\tTLSClientConfig: &tls.Config{\n\t\t\tRootCAs: cp,\n\t\t},\n\t}\n\n\tclient := http.Client{Transport: transport}\n\tbuf := new(bytes.Buffer)\n\terr = json.NewEncoder(buf).Encode(pb.HelloRequest{Name: \"world\"})\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tresp, err := client.Post(\"https://localhost/helloworld.Greeter/SayHello\", \"application/json\", buf)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tdefer resp.Body.Close()\n\n\tvar reply pb.HelloReply\n\terr = json.NewDecoder(resp.Body).Decode(&reply)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tlog.Printf(\"Greeting: %s\", reply.GetMessage())\n}\n"
  },
  {
    "path": "_examples/mvc/grpc-compatible/main.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"log\"\n\n\tpb \"github.com/kataras/iris/v12/_examples/mvc/grpc-compatible/helloworld\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/mvc\"\n\n\t\"google.golang.org/grpc\"\n)\n\n// See https://github.com/kataras/iris/issues/1449\n// Iris automatically binds the standard \"context\" context.Context to `iris.Context.Request().Context()`\n// and any other structure that is not mapping to a registered dependency\n// as a payload depends on the request, e.g XML, YAML, Query, Form, JSON.\n//\n// Useful to use gRPC services as Iris controllers fast and without wrappers.\n\nfunc main() {\n\tapp := newApp()\n\tapp.Logger().SetLevel(\"debug\")\n\n\t// The Iris server should ran under TLS (it's a gRPC requirement).\n\t// POST: https://localhost:443/helloworld.Greeter/SayHello\n\t// with request data: {\"name\": \"John\"}\n\t// and expected output: {\"message\": \"Hello John\"}\n\tapp.Run(iris.TLS(\":443\", \"server.crt\", \"server.key\"))\n}\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\t// app.Configure(iris.WithLowercaseRouting) // OPTIONAL.\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.HTML(\"<h1>Index Page</h1>\")\n\t})\n\n\tctrl := &myController{}\n\t// Register gRPC server.\n\tgrpcServer := grpc.NewServer()\n\tpb.RegisterGreeterServer(grpcServer, ctrl)\n\n\t// serviceName := pb.File_helloworld_proto.Services().Get(0).FullName()\n\n\t// Register MVC application controller for gRPC services.\n\t// You can bind as many mvc gRpc services in the same Party or app,\n\t// as the ServiceName differs.\n\tmvc.New(app).\n\t\tRegister(new(myService)).\n\t\tHandle(ctrl, mvc.GRPC{\n\t\t\tServer:      grpcServer,           // Required.\n\t\t\tServiceName: \"helloworld.Greeter\", // Required.\n\t\t\tStrict:      false,\n\t\t})\n\n\treturn app\n}\n\ntype service interface {\n\tDoSomething() error\n}\n\ntype myService struct{}\n\nfunc (s *myService) DoSomething() error {\n\tlog.Println(\"service: DoSomething\")\n\treturn nil\n}\n\ntype myController struct {\n\t// Ctx iris.Context\n\n\tSingletonDependency service\n}\n\n// SayHello implements helloworld.GreeterServer.\n// See https://github.com/kataras/iris/issues/1449#issuecomment-625570442\n// for the comments below (https://github.com/iris-contrib/swagger).\n//\n// @Description greet service\n// @Accept  json\n// @Produce  json\n// @Success 200 {string} string\t\"Hello {name}\"\n// @Router /helloworld.Greeter/SayHello [post]\nfunc (c *myController) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {\n\terr := c.SingletonDependency.DoSomething()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &pb.HelloReply{Message: \"Hello \" + in.GetName()}, nil\n}\n"
  },
  {
    "path": "_examples/mvc/grpc-compatible/main_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nfunc TestGRPCCompatible(t *testing.T) {\n\tapp := newApp()\n\n\te := httptest.New(t, app)\n\te.POST(\"/helloworld.Greeter/SayHello\").WithJSON(map[string]string{\"name\": \"makis\"}).Expect().\n\t\tStatus(httptest.StatusOK).\n\t\tJSON().IsEqual(map[string]string{\"message\": \"Hello makis\"})\n}\n"
  },
  {
    "path": "_examples/mvc/grpc-compatible/server.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIIDKjCCAhICCQDpz77z0oyjCDANBgkqhkiG9w0BAQsFADBXMQswCQYDVQQGEwJH\nUjEPMA0GA1UECAwGQXRoZW5zMQ8wDQYDVQQHDAZBdGhlbnMxJjAkBgkqhkiG9w0B\nCQEWF2thdGFyYXMyMDA2QGhvdG1haWwuY29tMB4XDTIyMDMwMzExMjczM1oXDTMy\nMDIyOTExMjczM1owVzELMAkGA1UEBhMCR1IxDzANBgNVBAgMBkF0aGVuczEPMA0G\nA1UEBwwGQXRoZW5zMSYwJAYJKoZIhvcNAQkBFhdrYXRhcmFzMjAwNkBob3RtYWls\nLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALu4Uqpcf0AnRDwW\nQGDh//v2wDNQ2EP/jn2Y4YxYJXtvWZ7dWcoX4fPA03mOAEmEPXWGxkhe8DYFh8PY\n0zpZW5sFY7ae0AcpcjvlyxjNvHsqjhnh7M1gKZuhfyxvvLv7afC5Gs64tyg+f4C3\nEqvwKHV+fjC8eKiASs4FsEEi89uv2AZdD9Vp0Zvbz6/1mRQQWBMpsPiIO7/WsaW4\nDPq/zUZVY15WaX5B0Fg57O5Uv8aHyV4A+AQN7XzadCpYBp8XUb/cVcN9Azj2F6d3\nWODyYT7y/RMuuih9HJQMxuAYPNB5UAkW6syTdfjbdyqbXe45z5iKPcotE4KaXYJf\nhTkj6osCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAsb3QGQUGNGs8zv2y4QmsDCB9\n+mkzwH9BpqSo40Zs19kT35nH0u//UuXiNG3U8d2WH1J7kk64cdrETuVay5TwW9jJ\nEagdJl4zAZbfLQuDUCI+cVpjbywbO//GXzi8Q/aO7viboU0OR+MPbabubtKE7f+X\nTHIjnMj3OhFM4zzrFyEL8gsRMLzRRCV18wTsiCl9bi4bZ1Ssvr6MwqyXHs3uK+fx\nJpxQVb3MckoIgUKwwU4MlXwufDkOmslGFsRg4e1iiwlkJ0HgRdf5lSuDmnBjH4T+\nfWvRqCeHDTjOI09+7IAK1EF7rw4TZjHv+RbPl5+b1TJcanUC9EDg4z4/rR0XOg==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "_examples/mvc/grpc-compatible/server.key",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAu7hSqlx/QCdEPBZAYOH/+/bAM1DYQ/+OfZjhjFgle29Znt1Z\nyhfh88DTeY4ASYQ9dYbGSF7wNgWHw9jTOllbmwVjtp7QBylyO+XLGM28eyqOGeHs\nzWApm6F/LG+8u/tp8Lkazri3KD5/gLcSq/AodX5+MLx4qIBKzgWwQSLz26/YBl0P\n1WnRm9vPr/WZFBBYEymw+Ig7v9axpbgM+r/NRlVjXlZpfkHQWDns7lS/xofJXgD4\nBA3tfNp0KlgGnxdRv9xVw30DOPYXp3dY4PJhPvL9Ey66KH0clAzG4Bg80HlQCRbq\nzJN1+Nt3Kptd7jnPmIo9yi0Tgppdgl+FOSPqiwIDAQABAoIBAQCg0XM4cc+uVTV2\nyJVUqqjT4fucusjb0EbxQJUR174ctjMwD2/J25X+bhZ9z3JdiQXh9plODM97aFd8\nJ/glx8Hb180p+Xo8eHxd5iqNUEwFtFpSwCNPeu+KXduGZR9qaCPFT78wlDyNJKW0\nzqIXXMI8jiZreDtiF65+O49Y7im97Lqwhw8fEABcr3rpOpjK79nPHkf0X3CKj1TC\nIhW5Av4j41uv8+1BgObjVkeYFHK4O+vRWiSId9OiXzZT9ZaG56ntGOap9KuugTym\n2ngbIBm2DdUDmbY3vL9eUzhfY75VIjFrgnf/EYN+7kymbymo3vnv5OmB3OByX7iw\nbXTmxQgxAoGBAOLLx6EzcQJ83g85Gtd3WNiNW414ynDPyIB/XAEcyygZGYqrVfsH\nHfGhxI787WqzjIZxl987HTPOkrX5Xc/GhHNeTVa1+neyUUqx46DxessU3/s53Vg4\nX1AmnmL6+Wl1qrlTJiUQINDyOZkHsbycR8okDlrzSS1rwO90wW6K5snDAoGBANPk\na/NH2t+KrAboPtXZk2DCUMkCoyDhzXnPU4HARO0BRa3Qhul8keV8u54MtNsej1y+\nTcatUQYpuT4Pgpl1kdtyHrLNiEhd0ThBgy5RmS/rLk9XnuWgfeth5bGxbEIlHXMN\nHvoxL34UhGxwWFCHKr5vV2OYVcPLkbdDBhLneweZAoGAaL6tCGp1uyxocqdxGipo\nwjsnGYO8G7YbaB1qJKljurU88qqHH1T+I2cPHOr7y9f5Au7bsaHfrtmtMJZnGVsa\nOR5Ioc+SSk309YaLFv3wNHMDr0feTqxaeO4dIKHBJ0/M9aLNbzivr1DwARlooS+c\niGN2rdLG7U9i4DUQUTmdtXkCgYEAllEJM8DZyJN7jjrbuKFtJ8sxvCeeygjl12/4\n8acQPoIUiEXSL3krlv1xq6Gf+4ImeciXLEZvoEuhGiGuqGb7Xg4LMRUVhSDo91ui\nUA2a+p+AbtDd7FB6g60jYXdYMWRbC+9W9m5GHs83UiYwwI/jBs291O2QiiGz8aoe\nePK2GKECgYEAg7PYp1uOWEuA39gqrKmuLA5f/i2bGgBrajk90m+9kgNwZ9RqeZJG\nV97hRwO86EwBBqhMUjaFv/zwSjKhCZUE8vOt5DF4sH5Ppa9j/kqrzsZjlKjajj/U\ntH1iFSXz/+PwQVeNssfjFMUPwWWsGSN5pvqx/5Ru/DIvPLHOg3YbEQc=\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "_examples/mvc/grpc-compatible-bidirectional-stream/client/main.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\tpb \"grpcexample/helloworld\"\n\n\t\"google.golang.org/grpc\"\n\t\"google.golang.org/grpc/credentials\"\n)\n\nfunc main() {\n\t// Set up a connection to the server.\n\tcred, err := credentials.NewClientTLSFromFile(\"../server.crt\", \"localhost\")\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tconn, err := grpc.Dial(\"localhost:443\", grpc.WithTransportCredentials(cred), grpc.WithBlock())\n\tdefer conn.Close()\n\n\tclient := pb.NewGreeterBidirectionalStreamClient(conn)\n\tstream, err := client.SayHello(context.Background())\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\twaitCh := make(chan struct{})\n\n\t// Implement the send channel.\n\t// As an exercise you can implement the read channel one (reading from server, see the server/main.go).\n\tgo func() {\n\t\tfor {\n\t\t\tprintln(\"Sleeping for 2 seconds...\")\n\t\t\ttime.Sleep(2 * time.Second)\n\t\t\tprintln(\"Sending a <test> msg...\")\n\t\t\tmsg := &pb.HelloRequest{Name: \"test\"}\n\n\t\t\terr = stream.Send(msg)\n\t\t\tif err != nil {\n\t\t\t\tpanic(\"stream.Send: \" + err.Error())\n\t\t\t}\n\t\t}\n\t}()\n\n\t<-waitCh\n\tstream.CloseSend()\n}\n"
  },
  {
    "path": "_examples/mvc/grpc-compatible-bidirectional-stream/go.mod",
    "content": "module grpcexample\n\ngo 1.25\n\nrequire (\n\tgithub.com/golang/protobuf v1.5.4\n\tgithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd\n\tgoogle.golang.org/grpc v1.78.0\n\tgoogle.golang.org/protobuf v1.36.11\n)\n\nrequire (\n\tgithub.com/BurntSushi/toml v1.6.0 // indirect\n\tgithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect\n\tgithub.com/CloudyKit/jet/v6 v6.3.1 // indirect\n\tgithub.com/Joker/jade v1.1.3 // indirect\n\tgithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 // indirect\n\tgithub.com/andybalholm/brotli v1.2.0 // indirect\n\tgithub.com/aymerick/douceur v0.2.0 // indirect\n\tgithub.com/blang/semver/v4 v4.0.0 // indirect\n\tgithub.com/fatih/structs v1.1.0 // indirect\n\tgithub.com/flosch/pongo2/v4 v4.0.2 // indirect\n\tgithub.com/gobwas/httphead v0.1.0 // indirect\n\tgithub.com/gobwas/pool v0.2.1 // indirect\n\tgithub.com/gobwas/ws v1.4.0 // indirect\n\tgithub.com/golang/snappy v1.0.0 // indirect\n\tgithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a // indirect\n\tgithub.com/google/uuid v1.6.0 // indirect\n\tgithub.com/gorilla/css v1.0.1 // indirect\n\tgithub.com/gorilla/websocket v1.5.3 // indirect\n\tgithub.com/iris-contrib/schema v0.0.6 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/kataras/blocks v0.0.8 // indirect\n\tgithub.com/kataras/golog v0.1.12 // indirect\n\tgithub.com/kataras/neffos v0.0.24 // indirect\n\tgithub.com/kataras/pio v0.0.13 // indirect\n\tgithub.com/kataras/sitemap v0.0.6 // indirect\n\tgithub.com/kataras/tunnel v0.0.4 // indirect\n\tgithub.com/klauspost/compress v1.18.2 // indirect\n\tgithub.com/kr/pretty v0.3.1 // indirect\n\tgithub.com/mailgun/raymond/v2 v2.0.48 // indirect\n\tgithub.com/mailru/easyjson v0.9.1 // indirect\n\tgithub.com/mediocregopher/radix/v3 v3.8.1 // indirect\n\tgithub.com/microcosm-cc/bluemonday v1.0.27 // indirect\n\tgithub.com/nats-io/nats.go v1.40.1 // indirect\n\tgithub.com/nats-io/nkeys v0.4.9 // indirect\n\tgithub.com/nats-io/nuid v1.0.1 // indirect\n\tgithub.com/rogpeppe/go-internal v1.10.0 // indirect\n\tgithub.com/russross/blackfriday/v2 v2.1.0 // indirect\n\tgithub.com/schollz/closestmatch v2.1.0+incompatible // indirect\n\tgithub.com/sirupsen/logrus v1.8.1 // indirect\n\tgithub.com/stretchr/testify v1.11.1 // indirect\n\tgithub.com/tdewolff/minify/v2 v2.24.8 // indirect\n\tgithub.com/tdewolff/parse/v2 v2.8.5 // indirect\n\tgithub.com/valyala/bytebufferpool v1.0.0 // indirect\n\tgithub.com/vmihailenco/msgpack/v5 v5.4.1 // indirect\n\tgithub.com/vmihailenco/tagparser/v2 v2.0.0 // indirect\n\tgithub.com/yosssi/ace v0.0.5 // indirect\n\tgolang.org/x/crypto v0.47.0 // indirect\n\tgolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 // indirect\n\tgolang.org/x/net v0.49.0 // indirect\n\tgolang.org/x/sys v0.40.0 // indirect\n\tgolang.org/x/text v0.33.0 // indirect\n\tgolang.org/x/time v0.14.0 // indirect\n\tgolang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect\n\tgoogle.golang.org/genproto/googleapis/rpc v0.0.0-20251029180050-ab9386a59fda // indirect\n\tgopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect\n\tgopkg.in/ini.v1 v1.67.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n"
  },
  {
    "path": "_examples/mvc/grpc-compatible-bidirectional-stream/go.sum",
    "content": "github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=\ngithub.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=\ngithub.com/CloudyKit/jet/v6 v6.3.1 h1:6IAo5Cx21xrHVaR8zzXN5gJatKV/wO7Nf6bfCnCSbUw=\ngithub.com/CloudyKit/jet/v6 v6.3.1/go.mod h1:lf8ksdNsxZt7/yH/3n4vJQWA9RUq4wpaHtArHhGVMOw=\ngithub.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc=\ngithub.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=\ngithub.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=\ngithub.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 h1:dG7/gLroJGht/jSQtHiLvT48Hxn+crbmvyItZC8cWOs=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05/go.mod h1:NYezi6wtnJtBm5btoprXc5SvAdqH0XTXWnUup0MptAI=\ngithub.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=\ngithub.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=\ngithub.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=\ngithub.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=\ngithub.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=\ngithub.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=\ngithub.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=\ngithub.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=\ngithub.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=\ngithub.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=\ngithub.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=\ngithub.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw=\ngithub.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8=\ngithub.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=\ngithub.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=\ngithub.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=\ngithub.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=\ngithub.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=\ngithub.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=\ngithub.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=\ngithub.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=\ngithub.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=\ngithub.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=\ngithub.com/gobwas/ws v1.4.0 h1:CTaoG1tojrh4ucGPcoJFiAQUAsEWekEWvLy7GsVNqGs=\ngithub.com/gobwas/ws v1.4.0/go.mod h1:G3gNqMNtPppf5XUz7O4shetPpcZ1VJ7zt18dlUeakrc=\ngithub.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=\ngithub.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=\ngithub.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=\ngithub.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a h1:l7A0loSszR5zHd/qK53ZIHMO8b3bBSmENnQ6eKnUT0A=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=\ngithub.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=\ngithub.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=\ngithub.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=\ngithub.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=\ngithub.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2 h1:T9THsdP1woyAqKHwjkEsbCnMefsAFvk8iJJKokcJ3Go=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2/go.mod h1:JLDgIqnFy5loDSUv1OA2j0mb6p/rDhiCqigP22Uq9xE=\ngithub.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw=\ngithub.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/kataras/blocks v0.0.8 h1:MrpVhoFTCR2v1iOOfGng5VJSILKeZZI+7NGfxEh3SUM=\ngithub.com/kataras/blocks v0.0.8/go.mod h1:9Jm5zx6BB+06NwA+OhTbHW1xkMOYxahnqTN5DveZ2Yg=\ngithub.com/kataras/golog v0.1.12 h1:Bu7I/G4ilJlbfzjmU39O9N+2uO1pBcMK045fzZ4ytNg=\ngithub.com/kataras/golog v0.1.12/go.mod h1:wrGSbOiBqbQSQznleVNX4epWM8rl9SJ/rmEacl0yqy4=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd h1:oMsQgqKACbUdlaIWnN+EZzzAnHkw90hE/y/R0BSij2U=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd/go.mod h1:yucjtDl9Vn3OctWM1fi0MuM9OckemC7kEreFa9QtPF8=\ngithub.com/kataras/neffos v0.0.24 h1:S3lHqJopCfXN285VdlbGeOj+Id83u4xdQKToa+w1vW0=\ngithub.com/kataras/neffos v0.0.24/go.mod h1:/3K9zQ0yEC5/xUiSQx46ToWa3xneGfUo/nMit/F5g+U=\ngithub.com/kataras/pio v0.0.13 h1:x0rXVX0fviDTXOOLOmr4MUxOabu1InVSTu5itF8CXCM=\ngithub.com/kataras/pio v0.0.13/go.mod h1:k3HNuSw+eJ8Pm2lA4lRhg3DiCjVgHlP8hmXApSej3oM=\ngithub.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY=\ngithub.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4=\ngithub.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA=\ngithub.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw=\ngithub.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=\ngithub.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=\ngithub.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=\ngithub.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8=\ngithub.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=\ngithub.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=\ngithub.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=\ngithub.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=\ngithub.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/mediocregopher/radix/v3 v3.8.1 h1:rOkHflVuulFKlwsLY01/M2cM2tWCjDoETcMqKbAWu1M=\ngithub.com/mediocregopher/radix/v3 v3.8.1/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8=\ngithub.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=\ngithub.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=\ngithub.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=\ngithub.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=\ngithub.com/nats-io/nats.go v1.40.1 h1:MLjDkdsbGUeCMKFyCFoLnNn/HDTqcgVa3EQm+pMNDPk=\ngithub.com/nats-io/nats.go v1.40.1/go.mod h1:wV73x0FSI/orHPSYoyMeJB+KajMDoWyXmFaRrrYaaTo=\ngithub.com/nats-io/nkeys v0.4.9 h1:qe9Faq2Gxwi6RZnZMXfmGMZkg3afLLOtrU+gDZJ35b0=\ngithub.com/nats-io/nkeys v0.4.9/go.mod h1:jcMqs+FLG+W5YO36OX6wFIFcmpdAns+w1Wm6D3I/evE=\ngithub.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=\ngithub.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=\ngithub.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=\ngithub.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=\ngithub.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=\ngithub.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=\ngithub.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk=\ngithub.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=\ngithub.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=\ngithub.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=\ngithub.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=\ngithub.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/tdewolff/minify/v2 v2.24.8 h1:58/VjsbevI4d5FGV0ZSuBrHMSSkH4MCH0sIz/eKIauE=\ngithub.com/tdewolff/minify/v2 v2.24.8/go.mod h1:0Ukj0CRpo/sW/nd8uZ4ccXaV1rEVIWA3dj8U7+Shhfw=\ngithub.com/tdewolff/parse/v2 v2.8.5 h1:ZmBiA/8Do5Rpk7bDye0jbbDUpXXbCdc3iah4VeUvwYU=\ngithub.com/tdewolff/parse/v2 v2.8.5/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=\ngithub.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE=\ngithub.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=\ngithub.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=\ngithub.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=\ngithub.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=\ngithub.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=\ngithub.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=\ngithub.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=\ngithub.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA=\ngithub.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0=\ngithub.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=\ngithub.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=\ngithub.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngo.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=\ngo.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=\ngo.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=\ngo.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=\ngo.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=\ngo.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=\ngo.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=\ngo.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=\ngo.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=\ngo.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=\ngo.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=\ngo.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=\ngolang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=\ngolang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=\ngolang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=\ngolang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=\ngolang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=\ngolang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=\ngolang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=\ngolang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=\ngolang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY=\ngolang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=\ngonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=\ngonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20251029180050-ab9386a59fda h1:i/Q+bfisr7gq6feoJnS/DlpdwEL4ihp41fvRiM3Ork0=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20251029180050-ab9386a59fda/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=\ngoogle.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc=\ngoogle.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U=\ngoogle.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=\ngoogle.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=\ngopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nmoul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs=\nmoul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE=\n"
  },
  {
    "path": "_examples/mvc/grpc-compatible-bidirectional-stream/helloworld/helloworld.pb.go",
    "content": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.25.0\n// \tprotoc        v3.12.3\n// source: helloworld.proto\n\npackage helloworld\n\nimport (\n\tproto \"github.com/golang/protobuf/proto\"\n\tprotoreflect \"google.golang.org/protobuf/reflect/protoreflect\"\n\tprotoimpl \"google.golang.org/protobuf/runtime/protoimpl\"\n\treflect \"reflect\"\n\tsync \"sync\"\n)\n\nconst (\n\t// Verify that this generated code is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)\n\t// Verify that runtime/protoimpl is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)\n)\n\n// This is a compile-time assertion that a sufficiently up-to-date version\n// of the legacy proto package is being used.\nconst _ = proto.ProtoPackageIsVersion4\n\n// The request message containing the user's name.\ntype HelloRequest struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tName string `protobuf:\"bytes,1,opt,name=name,proto3\" json:\"name,omitempty\"`\n}\n\nfunc (x *HelloRequest) Reset() {\n\t*x = HelloRequest{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_helloworld_proto_msgTypes[0]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *HelloRequest) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*HelloRequest) ProtoMessage() {}\n\nfunc (x *HelloRequest) ProtoReflect() protoreflect.Message {\n\tmi := &file_helloworld_proto_msgTypes[0]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use HelloRequest.ProtoReflect.Descriptor instead.\nfunc (*HelloRequest) Descriptor() ([]byte, []int) {\n\treturn file_helloworld_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *HelloRequest) GetName() string {\n\tif x != nil {\n\t\treturn x.Name\n\t}\n\treturn \"\"\n}\n\n// The response message containing the greetings\ntype HelloReply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tMessage string `protobuf:\"bytes,1,opt,name=message,proto3\" json:\"message,omitempty\"`\n}\n\nfunc (x *HelloReply) Reset() {\n\t*x = HelloReply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_helloworld_proto_msgTypes[1]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *HelloReply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*HelloReply) ProtoMessage() {}\n\nfunc (x *HelloReply) ProtoReflect() protoreflect.Message {\n\tmi := &file_helloworld_proto_msgTypes[1]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use HelloReply.ProtoReflect.Descriptor instead.\nfunc (*HelloReply) Descriptor() ([]byte, []int) {\n\treturn file_helloworld_proto_rawDescGZIP(), []int{1}\n}\n\nfunc (x *HelloReply) GetMessage() string {\n\tif x != nil {\n\t\treturn x.Message\n\t}\n\treturn \"\"\n}\n\nvar File_helloworld_proto protoreflect.FileDescriptor\n\nvar file_helloworld_proto_rawDesc = []byte{\n\t0x0a, 0x10, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x70, 0x72, 0x6f,\n\t0x74, 0x6f, 0x12, 0x0a, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x22, 0x22,\n\t0x0a, 0x0c, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12,\n\t0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,\n\t0x6d, 0x65, 0x22, 0x26, 0x0a, 0x0a, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79,\n\t0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,\n\t0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x32, 0x49, 0x0a, 0x07, 0x47, 0x72,\n\t0x65, 0x65, 0x74, 0x65, 0x72, 0x12, 0x3e, 0x0a, 0x08, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c,\n\t0x6f, 0x12, 0x18, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48,\n\t0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x68, 0x65,\n\t0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65,\n\t0x70, 0x6c, 0x79, 0x22, 0x00, 0x32, 0x5c, 0x0a, 0x18, 0x47, 0x72, 0x65, 0x65, 0x74, 0x65, 0x72,\n\t0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x69, 0x64, 0x65, 0x53, 0x53, 0x74, 0x72, 0x65, 0x61,\n\t0x6d, 0x12, 0x40, 0x0a, 0x08, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x18, 0x2e,\n\t0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f,\n\t0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77,\n\t0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22,\n\t0x00, 0x30, 0x01, 0x32, 0x5b, 0x0a, 0x17, 0x47, 0x72, 0x65, 0x65, 0x74, 0x65, 0x72, 0x43, 0x6c,\n\t0x69, 0x65, 0x6e, 0x74, 0x53, 0x69, 0x64, 0x65, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x40,\n\t0x0a, 0x08, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x18, 0x2e, 0x68, 0x65, 0x6c,\n\t0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71,\n\t0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c,\n\t0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x28, 0x01,\n\t0x32, 0x60, 0x0a, 0x1a, 0x47, 0x72, 0x65, 0x65, 0x74, 0x65, 0x72, 0x42, 0x69, 0x64, 0x69, 0x72,\n\t0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x42,\n\t0x0a, 0x08, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x18, 0x2e, 0x68, 0x65, 0x6c,\n\t0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71,\n\t0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c,\n\t0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x28, 0x01,\n\t0x30, 0x01, 0x42, 0x0e, 0x5a, 0x0c, 0x2e, 0x3b, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72,\n\t0x6c, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,\n}\n\nvar (\n\tfile_helloworld_proto_rawDescOnce sync.Once\n\tfile_helloworld_proto_rawDescData = file_helloworld_proto_rawDesc\n)\n\nfunc file_helloworld_proto_rawDescGZIP() []byte {\n\tfile_helloworld_proto_rawDescOnce.Do(func() {\n\t\tfile_helloworld_proto_rawDescData = protoimpl.X.CompressGZIP(file_helloworld_proto_rawDescData)\n\t})\n\treturn file_helloworld_proto_rawDescData\n}\n\nvar file_helloworld_proto_msgTypes = make([]protoimpl.MessageInfo, 2)\nvar file_helloworld_proto_goTypes = []any{\n\t(*HelloRequest)(nil), // 0: helloworld.HelloRequest\n\t(*HelloReply)(nil),   // 1: helloworld.HelloReply\n}\nvar file_helloworld_proto_depIdxs = []int32{\n\t0, // 0: helloworld.Greeter.SayHello:input_type -> helloworld.HelloRequest\n\t0, // 1: helloworld.GreeterServerSideSStream.SayHello:input_type -> helloworld.HelloRequest\n\t0, // 2: helloworld.GreeterClientSideStream.SayHello:input_type -> helloworld.HelloRequest\n\t0, // 3: helloworld.GreeterBidirectionalStream.SayHello:input_type -> helloworld.HelloRequest\n\t1, // 4: helloworld.Greeter.SayHello:output_type -> helloworld.HelloReply\n\t1, // 5: helloworld.GreeterServerSideSStream.SayHello:output_type -> helloworld.HelloReply\n\t1, // 6: helloworld.GreeterClientSideStream.SayHello:output_type -> helloworld.HelloReply\n\t1, // 7: helloworld.GreeterBidirectionalStream.SayHello:output_type -> helloworld.HelloReply\n\t4, // [4:8] is the sub-list for method output_type\n\t0, // [0:4] is the sub-list for method input_type\n\t0, // [0:0] is the sub-list for extension type_name\n\t0, // [0:0] is the sub-list for extension extendee\n\t0, // [0:0] is the sub-list for field type_name\n}\n\nfunc init() { file_helloworld_proto_init() }\nfunc file_helloworld_proto_init() {\n\tif File_helloworld_proto != nil {\n\t\treturn\n\t}\n\tif !protoimpl.UnsafeEnabled {\n\t\tfile_helloworld_proto_msgTypes[0].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*HelloRequest); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_helloworld_proto_msgTypes[1].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*HelloReply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n\ttype x struct{}\n\tout := protoimpl.TypeBuilder{\n\t\tFile: protoimpl.DescBuilder{\n\t\t\tGoPackagePath: reflect.TypeOf(x{}).PkgPath(),\n\t\t\tRawDescriptor: file_helloworld_proto_rawDesc,\n\t\t\tNumEnums:      0,\n\t\t\tNumMessages:   2,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   4,\n\t\t},\n\t\tGoTypes:           file_helloworld_proto_goTypes,\n\t\tDependencyIndexes: file_helloworld_proto_depIdxs,\n\t\tMessageInfos:      file_helloworld_proto_msgTypes,\n\t}.Build()\n\tFile_helloworld_proto = out.File\n\tfile_helloworld_proto_rawDesc = nil\n\tfile_helloworld_proto_goTypes = nil\n\tfile_helloworld_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "_examples/mvc/grpc-compatible-bidirectional-stream/helloworld/helloworld_grpc.pb.go",
    "content": "// Code generated by protoc-gen-go-grpc. DO NOT EDIT.\n\npackage helloworld\n\nimport (\n\tcontext \"context\"\n\tgrpc \"google.golang.org/grpc\"\n\tcodes \"google.golang.org/grpc/codes\"\n\tstatus \"google.golang.org/grpc/status\"\n)\n\n// This is a compile-time assertion to ensure that this generated file\n// is compatible with the grpc package it is being compiled against.\nconst _ = grpc.SupportPackageIsVersion7\n\n// GreeterClient is the client API for Greeter service.\n//\n// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.\ntype GreeterClient interface {\n\t// Sends a greeting\n\tSayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error)\n}\n\ntype greeterClient struct {\n\tcc grpc.ClientConnInterface\n}\n\nfunc NewGreeterClient(cc grpc.ClientConnInterface) GreeterClient {\n\treturn &greeterClient{cc}\n}\n\nfunc (c *greeterClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) {\n\tout := new(HelloReply)\n\terr := c.cc.Invoke(ctx, \"/helloworld.Greeter/SayHello\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\n// GreeterServer is the server API for Greeter service.\n// All implementations must embed UnimplementedGreeterServer\n// for forward compatibility\ntype GreeterServer interface {\n\t// Sends a greeting\n\tSayHello(context.Context, *HelloRequest) (*HelloReply, error)\n\tmustEmbedUnimplementedGreeterServer()\n}\n\n// UnimplementedGreeterServer must be embedded to have forward compatible implementations.\ntype UnimplementedGreeterServer struct {\n}\n\nfunc (UnimplementedGreeterServer) SayHello(context.Context, *HelloRequest) (*HelloReply, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method SayHello not implemented\")\n}\nfunc (UnimplementedGreeterServer) mustEmbedUnimplementedGreeterServer() {}\n\n// UnsafeGreeterServer may be embedded to opt out of forward compatibility for this service.\n// Use of this interface is not recommended, as added methods to GreeterServer will\n// result in compilation errors.\ntype UnsafeGreeterServer interface {\n\tmustEmbedUnimplementedGreeterServer()\n}\n\nfunc RegisterGreeterServer(s grpc.ServiceRegistrar, srv GreeterServer) {\n\ts.RegisterService(&_Greeter_serviceDesc, srv)\n}\n\nfunc _Greeter_SayHello_Handler(srv any, ctx context.Context, dec func(any) error, interceptor grpc.UnaryServerInterceptor) (any, error) {\n\tin := new(HelloRequest)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(GreeterServer).SayHello(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/helloworld.Greeter/SayHello\",\n\t}\n\thandler := func(ctx context.Context, req any) (any, error) {\n\t\treturn srv.(GreeterServer).SayHello(ctx, req.(*HelloRequest))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\nvar _Greeter_serviceDesc = grpc.ServiceDesc{\n\tServiceName: \"helloworld.Greeter\",\n\tHandlerType: (*GreeterServer)(nil),\n\tMethods: []grpc.MethodDesc{\n\t\t{\n\t\t\tMethodName: \"SayHello\",\n\t\t\tHandler:    _Greeter_SayHello_Handler,\n\t\t},\n\t},\n\tStreams:  []grpc.StreamDesc{},\n\tMetadata: \"helloworld.proto\",\n}\n\n// GreeterServerSideSStreamClient is the client API for GreeterServerSideSStream service.\n//\n// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.\ntype GreeterServerSideSStreamClient interface {\n\t// Sends a greeting\n\tSayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (GreeterServerSideSStream_SayHelloClient, error)\n}\n\ntype greeterServerSideSStreamClient struct {\n\tcc grpc.ClientConnInterface\n}\n\nfunc NewGreeterServerSideSStreamClient(cc grpc.ClientConnInterface) GreeterServerSideSStreamClient {\n\treturn &greeterServerSideSStreamClient{cc}\n}\n\nfunc (c *greeterServerSideSStreamClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (GreeterServerSideSStream_SayHelloClient, error) {\n\tstream, err := c.cc.NewStream(ctx, &_GreeterServerSideSStream_serviceDesc.Streams[0], \"/helloworld.GreeterServerSideSStream/SayHello\", opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tx := &greeterServerSideSStreamSayHelloClient{stream}\n\tif err := x.ClientStream.SendMsg(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif err := x.ClientStream.CloseSend(); err != nil {\n\t\treturn nil, err\n\t}\n\treturn x, nil\n}\n\ntype GreeterServerSideSStream_SayHelloClient interface {\n\tRecv() (*HelloReply, error)\n\tgrpc.ClientStream\n}\n\ntype greeterServerSideSStreamSayHelloClient struct {\n\tgrpc.ClientStream\n}\n\nfunc (x *greeterServerSideSStreamSayHelloClient) Recv() (*HelloReply, error) {\n\tm := new(HelloReply)\n\tif err := x.ClientStream.RecvMsg(m); err != nil {\n\t\treturn nil, err\n\t}\n\treturn m, nil\n}\n\n// GreeterServerSideSStreamServer is the server API for GreeterServerSideSStream service.\n// All implementations must embed UnimplementedGreeterServerSideSStreamServer\n// for forward compatibility\ntype GreeterServerSideSStreamServer interface {\n\t// Sends a greeting\n\tSayHello(*HelloRequest, GreeterServerSideSStream_SayHelloServer) error\n\tmustEmbedUnimplementedGreeterServerSideSStreamServer()\n}\n\n// UnimplementedGreeterServerSideSStreamServer must be embedded to have forward compatible implementations.\ntype UnimplementedGreeterServerSideSStreamServer struct {\n}\n\nfunc (UnimplementedGreeterServerSideSStreamServer) SayHello(*HelloRequest, GreeterServerSideSStream_SayHelloServer) error {\n\treturn status.Errorf(codes.Unimplemented, \"method SayHello not implemented\")\n}\nfunc (UnimplementedGreeterServerSideSStreamServer) mustEmbedUnimplementedGreeterServerSideSStreamServer() {\n}\n\n// UnsafeGreeterServerSideSStreamServer may be embedded to opt out of forward compatibility for this service.\n// Use of this interface is not recommended, as added methods to GreeterServerSideSStreamServer will\n// result in compilation errors.\ntype UnsafeGreeterServerSideSStreamServer interface {\n\tmustEmbedUnimplementedGreeterServerSideSStreamServer()\n}\n\nfunc RegisterGreeterServerSideSStreamServer(s grpc.ServiceRegistrar, srv GreeterServerSideSStreamServer) {\n\ts.RegisterService(&_GreeterServerSideSStream_serviceDesc, srv)\n}\n\nfunc _GreeterServerSideSStream_SayHello_Handler(srv any, stream grpc.ServerStream) error {\n\tm := new(HelloRequest)\n\tif err := stream.RecvMsg(m); err != nil {\n\t\treturn err\n\t}\n\treturn srv.(GreeterServerSideSStreamServer).SayHello(m, &greeterServerSideSStreamSayHelloServer{stream})\n}\n\ntype GreeterServerSideSStream_SayHelloServer interface {\n\tSend(*HelloReply) error\n\tgrpc.ServerStream\n}\n\ntype greeterServerSideSStreamSayHelloServer struct {\n\tgrpc.ServerStream\n}\n\nfunc (x *greeterServerSideSStreamSayHelloServer) Send(m *HelloReply) error {\n\treturn x.ServerStream.SendMsg(m)\n}\n\nvar _GreeterServerSideSStream_serviceDesc = grpc.ServiceDesc{\n\tServiceName: \"helloworld.GreeterServerSideSStream\",\n\tHandlerType: (*GreeterServerSideSStreamServer)(nil),\n\tMethods:     []grpc.MethodDesc{},\n\tStreams: []grpc.StreamDesc{\n\t\t{\n\t\t\tStreamName:    \"SayHello\",\n\t\t\tHandler:       _GreeterServerSideSStream_SayHello_Handler,\n\t\t\tServerStreams: true,\n\t\t},\n\t},\n\tMetadata: \"helloworld.proto\",\n}\n\n// GreeterClientSideStreamClient is the client API for GreeterClientSideStream service.\n//\n// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.\ntype GreeterClientSideStreamClient interface {\n\t// Sends a greeting\n\tSayHello(ctx context.Context, opts ...grpc.CallOption) (GreeterClientSideStream_SayHelloClient, error)\n}\n\ntype greeterClientSideStreamClient struct {\n\tcc grpc.ClientConnInterface\n}\n\nfunc NewGreeterClientSideStreamClient(cc grpc.ClientConnInterface) GreeterClientSideStreamClient {\n\treturn &greeterClientSideStreamClient{cc}\n}\n\nfunc (c *greeterClientSideStreamClient) SayHello(ctx context.Context, opts ...grpc.CallOption) (GreeterClientSideStream_SayHelloClient, error) {\n\tstream, err := c.cc.NewStream(ctx, &_GreeterClientSideStream_serviceDesc.Streams[0], \"/helloworld.GreeterClientSideStream/SayHello\", opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tx := &greeterClientSideStreamSayHelloClient{stream}\n\treturn x, nil\n}\n\ntype GreeterClientSideStream_SayHelloClient interface {\n\tSend(*HelloRequest) error\n\tCloseAndRecv() (*HelloReply, error)\n\tgrpc.ClientStream\n}\n\ntype greeterClientSideStreamSayHelloClient struct {\n\tgrpc.ClientStream\n}\n\nfunc (x *greeterClientSideStreamSayHelloClient) Send(m *HelloRequest) error {\n\treturn x.ClientStream.SendMsg(m)\n}\n\nfunc (x *greeterClientSideStreamSayHelloClient) CloseAndRecv() (*HelloReply, error) {\n\tif err := x.ClientStream.CloseSend(); err != nil {\n\t\treturn nil, err\n\t}\n\tm := new(HelloReply)\n\tif err := x.ClientStream.RecvMsg(m); err != nil {\n\t\treturn nil, err\n\t}\n\treturn m, nil\n}\n\n// GreeterClientSideStreamServer is the server API for GreeterClientSideStream service.\n// All implementations must embed UnimplementedGreeterClientSideStreamServer\n// for forward compatibility\ntype GreeterClientSideStreamServer interface {\n\t// Sends a greeting\n\tSayHello(GreeterClientSideStream_SayHelloServer) error\n\tmustEmbedUnimplementedGreeterClientSideStreamServer()\n}\n\n// UnimplementedGreeterClientSideStreamServer must be embedded to have forward compatible implementations.\ntype UnimplementedGreeterClientSideStreamServer struct {\n}\n\nfunc (UnimplementedGreeterClientSideStreamServer) SayHello(GreeterClientSideStream_SayHelloServer) error {\n\treturn status.Errorf(codes.Unimplemented, \"method SayHello not implemented\")\n}\nfunc (UnimplementedGreeterClientSideStreamServer) mustEmbedUnimplementedGreeterClientSideStreamServer() {\n}\n\n// UnsafeGreeterClientSideStreamServer may be embedded to opt out of forward compatibility for this service.\n// Use of this interface is not recommended, as added methods to GreeterClientSideStreamServer will\n// result in compilation errors.\ntype UnsafeGreeterClientSideStreamServer interface {\n\tmustEmbedUnimplementedGreeterClientSideStreamServer()\n}\n\nfunc RegisterGreeterClientSideStreamServer(s grpc.ServiceRegistrar, srv GreeterClientSideStreamServer) {\n\ts.RegisterService(&_GreeterClientSideStream_serviceDesc, srv)\n}\n\nfunc _GreeterClientSideStream_SayHello_Handler(srv any, stream grpc.ServerStream) error {\n\treturn srv.(GreeterClientSideStreamServer).SayHello(&greeterClientSideStreamSayHelloServer{stream})\n}\n\ntype GreeterClientSideStream_SayHelloServer interface {\n\tSendAndClose(*HelloReply) error\n\tRecv() (*HelloRequest, error)\n\tgrpc.ServerStream\n}\n\ntype greeterClientSideStreamSayHelloServer struct {\n\tgrpc.ServerStream\n}\n\nfunc (x *greeterClientSideStreamSayHelloServer) SendAndClose(m *HelloReply) error {\n\treturn x.ServerStream.SendMsg(m)\n}\n\nfunc (x *greeterClientSideStreamSayHelloServer) Recv() (*HelloRequest, error) {\n\tm := new(HelloRequest)\n\tif err := x.ServerStream.RecvMsg(m); err != nil {\n\t\treturn nil, err\n\t}\n\treturn m, nil\n}\n\nvar _GreeterClientSideStream_serviceDesc = grpc.ServiceDesc{\n\tServiceName: \"helloworld.GreeterClientSideStream\",\n\tHandlerType: (*GreeterClientSideStreamServer)(nil),\n\tMethods:     []grpc.MethodDesc{},\n\tStreams: []grpc.StreamDesc{\n\t\t{\n\t\t\tStreamName:    \"SayHello\",\n\t\t\tHandler:       _GreeterClientSideStream_SayHello_Handler,\n\t\t\tClientStreams: true,\n\t\t},\n\t},\n\tMetadata: \"helloworld.proto\",\n}\n\n// GreeterBidirectionalStreamClient is the client API for GreeterBidirectionalStream service.\n//\n// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.\ntype GreeterBidirectionalStreamClient interface {\n\t// Sends a greeting\n\tSayHello(ctx context.Context, opts ...grpc.CallOption) (GreeterBidirectionalStream_SayHelloClient, error)\n}\n\ntype greeterBidirectionalStreamClient struct {\n\tcc grpc.ClientConnInterface\n}\n\nfunc NewGreeterBidirectionalStreamClient(cc grpc.ClientConnInterface) GreeterBidirectionalStreamClient {\n\treturn &greeterBidirectionalStreamClient{cc}\n}\n\nfunc (c *greeterBidirectionalStreamClient) SayHello(ctx context.Context, opts ...grpc.CallOption) (GreeterBidirectionalStream_SayHelloClient, error) {\n\tstream, err := c.cc.NewStream(ctx, &_GreeterBidirectionalStream_serviceDesc.Streams[0], \"/helloworld.GreeterBidirectionalStream/SayHello\", opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tx := &greeterBidirectionalStreamSayHelloClient{stream}\n\treturn x, nil\n}\n\ntype GreeterBidirectionalStream_SayHelloClient interface {\n\tSend(*HelloRequest) error\n\tRecv() (*HelloReply, error)\n\tgrpc.ClientStream\n}\n\ntype greeterBidirectionalStreamSayHelloClient struct {\n\tgrpc.ClientStream\n}\n\nfunc (x *greeterBidirectionalStreamSayHelloClient) Send(m *HelloRequest) error {\n\treturn x.ClientStream.SendMsg(m)\n}\n\nfunc (x *greeterBidirectionalStreamSayHelloClient) Recv() (*HelloReply, error) {\n\tm := new(HelloReply)\n\tif err := x.ClientStream.RecvMsg(m); err != nil {\n\t\treturn nil, err\n\t}\n\treturn m, nil\n}\n\n// GreeterBidirectionalStreamServer is the server API for GreeterBidirectionalStream service.\n// All implementations must embed UnimplementedGreeterBidirectionalStreamServer\n// for forward compatibility\ntype GreeterBidirectionalStreamServer interface {\n\t// Sends a greeting\n\tSayHello(GreeterBidirectionalStream_SayHelloServer) error\n\tmustEmbedUnimplementedGreeterBidirectionalStreamServer()\n}\n\n// UnimplementedGreeterBidirectionalStreamServer must be embedded to have forward compatible implementations.\ntype UnimplementedGreeterBidirectionalStreamServer struct {\n}\n\nfunc (UnimplementedGreeterBidirectionalStreamServer) SayHello(GreeterBidirectionalStream_SayHelloServer) error {\n\treturn status.Errorf(codes.Unimplemented, \"method SayHello not implemented\")\n}\nfunc (UnimplementedGreeterBidirectionalStreamServer) mustEmbedUnimplementedGreeterBidirectionalStreamServer() {\n}\n\n// UnsafeGreeterBidirectionalStreamServer may be embedded to opt out of forward compatibility for this service.\n// Use of this interface is not recommended, as added methods to GreeterBidirectionalStreamServer will\n// result in compilation errors.\ntype UnsafeGreeterBidirectionalStreamServer interface {\n\tmustEmbedUnimplementedGreeterBidirectionalStreamServer()\n}\n\nfunc RegisterGreeterBidirectionalStreamServer(s grpc.ServiceRegistrar, srv GreeterBidirectionalStreamServer) {\n\ts.RegisterService(&_GreeterBidirectionalStream_serviceDesc, srv)\n}\n\nfunc _GreeterBidirectionalStream_SayHello_Handler(srv any, stream grpc.ServerStream) error {\n\treturn srv.(GreeterBidirectionalStreamServer).SayHello(&greeterBidirectionalStreamSayHelloServer{stream})\n}\n\ntype GreeterBidirectionalStream_SayHelloServer interface {\n\tSend(*HelloReply) error\n\tRecv() (*HelloRequest, error)\n\tgrpc.ServerStream\n}\n\ntype greeterBidirectionalStreamSayHelloServer struct {\n\tgrpc.ServerStream\n}\n\nfunc (x *greeterBidirectionalStreamSayHelloServer) Send(m *HelloReply) error {\n\treturn x.ServerStream.SendMsg(m)\n}\n\nfunc (x *greeterBidirectionalStreamSayHelloServer) Recv() (*HelloRequest, error) {\n\tm := new(HelloRequest)\n\tif err := x.ServerStream.RecvMsg(m); err != nil {\n\t\treturn nil, err\n\t}\n\treturn m, nil\n}\n\nvar _GreeterBidirectionalStream_serviceDesc = grpc.ServiceDesc{\n\tServiceName: \"helloworld.GreeterBidirectionalStream\",\n\tHandlerType: (*GreeterBidirectionalStreamServer)(nil),\n\tMethods:     []grpc.MethodDesc{},\n\tStreams: []grpc.StreamDesc{\n\t\t{\n\t\t\tStreamName:    \"SayHello\",\n\t\t\tHandler:       _GreeterBidirectionalStream_SayHello_Handler,\n\t\t\tServerStreams: true,\n\t\t\tClientStreams: true,\n\t\t},\n\t},\n\tMetadata: \"helloworld.proto\",\n}\n"
  },
  {
    "path": "_examples/mvc/grpc-compatible-bidirectional-stream/helloworld.proto",
    "content": "syntax = \"proto3\";\n\npackage helloworld;\n\noption go_package = \".;helloworld\";\n\n// The request message containing the user's name.\nmessage HelloRequest {\n  string name = 1;\n}\n\n// The response message containing the greetings\nmessage HelloReply {\n  string message = 1;\n}\n\n// The greeting service definition.\nservice Greeter {\n  // Sends a greeting\n  rpc SayHello (HelloRequest) returns (HelloReply) {}\n}\n\n// The greeting service definition. (Server-side streaming RPC)\nservice GreeterServerSideSStream {\n  // Sends a greeting\n  rpc SayHello (HelloRequest) returns (stream HelloReply) {}\n}\n\n// The greeting service definition. (Client-side streaming RPC)\nservice GreeterClientSideStream {\n  // Sends a greeting\n  rpc SayHello (stream HelloRequest) returns (HelloReply) {}\n}\n\n// The greeting service definition. (Bidirectional streaming RPC)\nservice GreeterBidirectionalStream {\n  // Sends a greeting\n  rpc SayHello (stream HelloRequest) returns (stream HelloReply) {}\n}\n"
  },
  {
    "path": "_examples/mvc/grpc-compatible-bidirectional-stream/server/main.go",
    "content": "package main\n\nimport (\n\t\"io\"\n\n\tpb \"grpcexample/helloworld\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/mvc\"\n\n\t\"google.golang.org/grpc\"\n)\n\ntype Greeter struct {\n\tpb.UnimplementedGreeterBidirectionalStreamServer\n}\n\n// SayHello implements the proto Bidirectional Stream Greeter service.\nfunc (g *Greeter) SayHello(stream pb.GreeterBidirectionalStream_SayHelloServer) error {\n\tfor {\n\t\tin, err := stream.Recv()\n\t\tif err == io.EOF {\n\t\t\treturn nil\n\t\t}\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tprintln(\"Received input: \" + in.Name)\n\t\t// On client side you can implement the 'read' operation too.\n\t\tstream.Send(&pb.HelloReply{Message: \"Hello \" + in.Name})\n\t}\n}\n\nfunc main() {\n\tapp := iris.New()\n\n\tgrpcServer := grpc.NewServer()\n\n\tmyService := &Greeter{}\n\tpb.RegisterGreeterBidirectionalStreamServer(grpcServer, myService)\n\n\trootApp := mvc.New(app)\n\trootApp.Handle(myService, mvc.GRPC{\n\t\tServer:      grpcServer,                              // Required.\n\t\tServiceName: \"helloworld.GreeterBidirectionalStream\", // Required.\n\t\tStrict:      true,                                    // Set it to true on gRPC streaming.\n\t})\n\n\tapp.Run(iris.TLS(\":443\", \"../server.crt\", \"../server.key\"))\n}\n"
  },
  {
    "path": "_examples/mvc/grpc-compatible-bidirectional-stream/server.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIICwzCCAaugAwIBAgIJANAsSlhuqzW/MA0GCSqGSIb3DQEBBQUAMBQxEjAQBgNV\nBAMTCWxvY2FsaG9zdDAeFw0yMDEwMzEwMjM4MTNaFw0zMDEwMjkwMjM4MTNaMBQx\nEjAQBgNVBAMTCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\nggEBAKY8mFh0nvXa8V6Vx5TVVbfq9Zx81hiIKeXN977JaQGrPfCMRU61smaDJAzR\nMBEFjHT37Zj5sZr6qvQmt3Dfst8bNcFBsZ7s3bBknuevD3gPJBy1r5KFwSkPZekH\nT+0DF0Drq1/iIZQXE13udWzDnzxABUBOmIzEmn+rfp2j3xJJGLoO1O4Wu2YpXhtj\nyNJfswoztGGie6u61mAN7Rw4JQUkDKa3hAKkYtfOoGHZBs6Y0RIKZQikjxlVpMMc\nfCvUMHkY51GP8ycgZ8+0n/cjm13KkqfvoyRZ1rQqCd24YNIEl18t0FClpUAHkxD/\naUPCqV3bqpFBbDPQcYybIGjfNIMCAwEAAaMYMBYwFAYDVR0RBA0wC4IJbG9jYWxo\nb3N0MA0GCSqGSIb3DQEBBQUAA4IBAQAd6F6tG/jcUu9pm/5XeH1Iof5PB9tFuBvv\nw9HFjBI5S7ItnTepneoOP26lx2iOE0GwkvrhseZcHkGDZPdncBn12PuCHUbcO2Ux\nbEDyDMsAHooKLvmAfAWeiOmeJAdFl8DOTgD/lTHK1n/mEwCUOtGozmfm/Y0pnfc7\nnMgbQ+yNsj1X7HVJHRUdOfOCGjiWofo6v7V7YyZJC2jpn3K7O4126jic2ibWYKQl\nfLLjgS0N5Wcun117e1mRYV41jtPOkeAvxJbJGf0IP7Os0VfG1UQhodwjkV4s8GcG\nnaB5qC4Y508cB7Lq58kbGgZ0nfMCcpnrKobliIZVEpH7aa0Po1lm\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "_examples/mvc/grpc-compatible-bidirectional-stream/server.key",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEApjyYWHSe9drxXpXHlNVVt+r1nHzWGIgp5c33vslpAas98IxF\nTrWyZoMkDNEwEQWMdPftmPmxmvqq9Ca3cN+y3xs1wUGxnuzdsGSe568PeA8kHLWv\nkoXBKQ9l6QdP7QMXQOurX+IhlBcTXe51bMOfPEAFQE6YjMSaf6t+naPfEkkYug7U\n7ha7ZileG2PI0l+zCjO0YaJ7q7rWYA3tHDglBSQMpreEAqRi186gYdkGzpjREgpl\nCKSPGVWkwxx8K9QweRjnUY/zJyBnz7Sf9yObXcqSp++jJFnWtCoJ3bhg0gSXXy3Q\nUKWlQAeTEP9pQ8KpXduqkUFsM9BxjJsgaN80gwIDAQABAoIBACDVNwHBhuPoKmQU\nESdEO3nn3jraLS8LNbs9wwDbpvG9cK5iBg5VtLaqkCQ37NZv0h4IGdVs+7cwazNt\nsi2JATsvlJ5m6z4IaoC8XuZDnTqJQwiomdTGti/16prr5s1ZHu6jnWWCtD8bj6et\nwWOJ/5lWy7K300l6S0mMBaX9B8IEfGR+YPGF/bfsIlf+9iuVulBjH5NPIozwl4c9\nVUafESEZX0DM/KDHqFvvWdMaw9e+3QNi+l0t3VJz6JuEcUfRztT6lbzYbRGBYqKr\nDmUNxYUThd717NJklxr9D+GMm5FnfrzY8vcJ16SILBQyDthsz1s6gdT0yeRFgjJf\nafDObAECgYEA1WQdayAeqT7sE0T0jHAWHdVhRd9sGUTZ4LjVuGbMjbJWJVUPn/fz\nahBQh77xutmNCbDSUD0srT+zHtRRJUVhl+ojFS1CMyyL/UAfH24gDwUnXIyq/DPA\nheXKD8NwGoTPbHKjdmy+89qVbes3oxBc5tDKpzAuBoPY4iBMVwfqSmECgYEAx24Y\ni1lhxuG+g2YSjiIhmD/ytZSXamP3+KmnqWWXbEjJd2oYmCPr9yBCfdk9jCUXZJsg\ntYsRduMajTjmTNkYmGyZNVtmjXfrq/vaJgf7bZhhLOFxWoNmLlKQfmbuJW5fkV1b\nhY8oid+YbBob4xseFzKvm5j0aKwufi3v7XLUEWMCgYEAs7rqKFNaX9SWhDhc/Xhe\nuGwDzRU8eCAMnwEvSWyUN3iQpEr7qRHvXFM3cM47zdP0vcfHrDuKSLXRSVMssYa5\nh3l2aRzAmFeZ5Qk/7XoU2HHP0FzOmzN/oYeE5DgJUNyx1DbORS2cu8lMeNNX/ikH\nBoWvWpfy/BvK7dKkWd1Z0aECgYEAm1+QKcjqX5ty5UZ6AFhhGhAAVS2+Rfo6sHXl\nFRn8PjX7GFkFbkrWRUPR6eB9jhk7v3sIocgGRDytbAc/jfG5ss8xEhvyqxcZ+nUO\nQYEIhxsn4mKGAMHMsxxKTOB+e5UhScyVSFn/eGNGijpRLb/r0qD/pdcl3AMBefbq\nLXG//QcCgYBrYTCaKYDg/TSMt6fTSZgJRaui8PvvlLjZy63vrVCexVUibiCpbUt9\nsHe9gbSDPr/uCyNgKnZjDbVabmZboVYqHBDQnXPUYYx9dBSL2DzE3JXA6Q8RVnOY\nzwCPzVxYbE9h02ishAgc6k9w61XUaDnASmBZAnlxt9/8cB7+IZAlfg==\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "_examples/mvc/hello-world/main.go",
    "content": "package main\n\nimport (\n\t\"strings\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/middleware/logger\"\n\t\"github.com/kataras/iris/v12/middleware/recover\"\n\t\"github.com/kataras/iris/v12/mvc\"\n)\n\n// This example is equivalent to the\n// https://github.com/kataras/iris/blob/main/_examples/hello-world/main.go\n//\n// It seems that additional code you\n// have to write doesn't worth it\n// but remember that, this example\n// does not make use of iris mvc features like\n// the Model, Persistence or the View engine neither the Session,\n// it's very simple for learning purposes,\n// probably you'll never use such\n// as simple controller anywhere in your app.\n//\n// The cost we have on this example for using MVC\n// on the \"/hello\" path which serves JSON\n// is ~2MB per 20MB throughput on my personal laptop,\n// it's tolerated for the majority of the applications\n// but you can choose\n// what suits you best with Iris, low-level handlers: performance\n// or high-level controllers: easier to maintain and smaller codebase on large applications.\n\n// Of course you can put all these to main func, it's just a separate function\n// for the main_test.go.\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\t// Optionally, add two builtin handlers\n\t// that can recover from any http-relative panics\n\t// and log the requests to the terminal.\n\tapp.Use(recover.New())\n\tapp.Use(logger.New())\n\n\t// Serve a controller based on the root Router, \"/\".\n\tmvc.New(app).Handle(new(ExampleController))\n\t// Add custom path func\n\tmvc.New(app).SetCustomPathWordFunc(func(path, w string, wordIndex int) string {\n\n\t\tif wordIndex == 0 {\n\t\t\tw = strings.ToLower(w)\n\t\t}\n\t\tpath += w\n\t\treturn path\n\n\t}).Handle(new(ExampleControllerCustomPath))\n\treturn app\n}\n\nfunc main() {\n\tapp := newApp()\n\n\t// http://localhost:8080\n\t// http://localhost:8080/ping\n\t// http://localhost:8080/hello\n\t// http://localhost:8080/custom_path\n\tapp.Listen(\":8080\")\n}\n\n// ExampleController serves the \"/\", \"/ping\" and \"/hello\".\ntype ExampleController struct{}\n\n// Get serves\n// Method:   GET\n// Resource: http://localhost:8080\nfunc (c *ExampleController) Get() mvc.Result {\n\treturn mvc.Response{\n\t\tContentType: \"text/html\",\n\t\tText:        \"<h1>Welcome</h1>\",\n\t}\n}\n\n// GetPing serves\n// Method:   GET\n// Resource: http://localhost:8080/ping\nfunc (c *ExampleController) GetPing() string {\n\treturn \"pong\"\n}\n\n// GetHello serves\n// Method:   GET\n// Resource: http://localhost:8080/hello\nfunc (c *ExampleController) GetHello() any {\n\treturn map[string]string{\"message\": \"Hello Iris!\"}\n}\n\n// GetHelloWorld serves\n// Method:   GET\n// Resource: http://localhost:8080/hello/world\nfunc (c *ExampleController) GetHelloWorld() any {\n\treturn map[string]string{\"message\": \"Hello Iris! DefaultPath\"}\n}\n\n// BeforeActivation called once, before the controller adapted to the main application\n// and of course before the server ran.\n// After version 9 you can also add custom routes for a specific controller's methods.\n// Here you can register custom method's handlers\n// use the standard router with `ca.Router` to do something that you can do without mvc as well,\n// and add dependencies that will be binded to a controller's fields or method function's input arguments.\nfunc (c *ExampleController) BeforeActivation(b mvc.BeforeActivation) {\n\tanyMiddlewareHere := func(ctx iris.Context) {\n\t\tctx.Application().Logger().Warnf(\"Inside /custom_path\")\n\t\tctx.Next()\n\t}\n\tb.Handle(\"GET\", \"/custom_path\", \"CustomHandlerWithoutFollowingTheNamingGuide\", anyMiddlewareHere)\n\n\t// or even add a global middleware based on this controller's router,\n\t// which in this example is the root \"/\":\n\t// b.Router().Use(myMiddleware)\n}\n\n// CustomHandlerWithoutFollowingTheNamingGuide serves\n// Method:   GET\n// Resource: http://localhost:8080/custom_path\nfunc (c *ExampleController) CustomHandlerWithoutFollowingTheNamingGuide() string {\n\treturn \"hello from the custom handler without following the naming guide\"\n}\n\ntype ExampleControllerCustomPath struct{}\n\n// GetHelloWorld serves\n// Method:   GET\n// Resource: http://localhost:8080/helloWorld\nfunc (c *ExampleControllerCustomPath) GetHelloWorld() any {\n\treturn map[string]string{\"message\": \"Hello Iris! CustomPath\"}\n}\n\n// GetUserBy serves\n// Method:   GET\n// Resource: http://localhost:8080/user/{username:string}\n// By is a reserved \"keyword\" to tell the framework that you're going to\n// bind path parameters in the function's input arguments, and it also\n// helps to have \"Get\" and \"GetBy\" in the same controller.\n//\n// func (c *ExampleController) GetUserBy(username string) mvc.Result {\n// \treturn mvc.View{\n// \t\tName: \"user/username.html\",\n// \t\tData: username,\n// \t}\n// }\n\n/* Can use more than one, the factory will make sure\nthat the correct http methods are being registered for each route\nfor this controller, uncomment these if you want:\n\nfunc (c *ExampleController) Post() {}\nfunc (c *ExampleController) Put() {}\nfunc (c *ExampleController) Delete() {}\nfunc (c *ExampleController) Connect() {}\nfunc (c *ExampleController) Head() {}\nfunc (c *ExampleController) Patch() {}\nfunc (c *ExampleController) Options() {}\nfunc (c *ExampleController) Trace() {}\n*/\n\n/*\nfunc (c *ExampleController) All() {}\n//        OR\nfunc (c *ExampleController) Any() {}\n\n\n\nfunc (c *ExampleController) BeforeActivation(b mvc.BeforeActivation) {\n\t// 1 -> the HTTP Method\n\t// 2 -> the route's path\n\t// 3 -> this controller's method name that should be handler for that route.\n\tb.Handle(\"GET\", \"/mypath/{param}\", \"DoIt\", optionalMiddlewareHere...)\n}\n\n// After activation, all dependencies are set-ed - so read only access on them\n// but still possible to add custom controller or simple standard handlers.\nfunc (c *ExampleController) AfterActivation(a mvc.AfterActivation) {}\n\n*/\n"
  },
  {
    "path": "_examples/mvc/hello-world/main_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nfunc TestMVCHelloWorld(t *testing.T) {\n\te := httptest.New(t, newApp())\n\n\te.GET(\"/\").Expect().Status(httptest.StatusOK).\n\t\tContentType(\"text/html\", \"utf-8\").Body().IsEqual(\"<h1>Welcome</h1>\")\n\n\te.GET(\"/ping\").Expect().Status(httptest.StatusOK).\n\t\tBody().IsEqual(\"pong\")\n\n\te.GET(\"/hello\").Expect().Status(httptest.StatusOK).\n\t\tJSON().Object().Value(\"message\").Equal(\"Hello Iris!\")\n\n\te.GET(\"/custom_path\").Expect().Status(httptest.StatusOK).\n\t\tBody().IsEqual(\"hello from the custom handler without following the naming guide\")\n}\n"
  },
  {
    "path": "_examples/mvc/login/datamodels/user.go",
    "content": "package datamodels\n\nimport (\n\t\"time\"\n\n\t\"golang.org/x/crypto/bcrypt\"\n)\n\n// User is our User example model.\n// Keep note that the tags for public-use (for our web app)\n// should be kept in other file like \"web/viewmodels/user.go\"\n// which could wrap by embedding the datamodels.User or\n// define completely new fields instead but for the sake\n// of the example, we will use this datamodel\n// as the only one User model in our application.\ntype User struct {\n\tID             int64     `json:\"id\" form:\"id\"`\n\tFirstname      string    `json:\"firstname\" form:\"firstname\"`\n\tUsername       string    `json:\"username\" form:\"username\"`\n\tHashedPassword []byte    `json:\"-\" form:\"-\"`\n\tCreatedAt      time.Time `json:\"created_at\" form:\"created_at\"`\n}\n\n// IsValid can do some very very simple \"low-level\" data validations.\nfunc (u User) IsValid() bool {\n\treturn u.ID > 0\n}\n\n// GeneratePassword will generate a hashed password for us based on the\n// user's input.\nfunc GeneratePassword(userPassword string) ([]byte, error) {\n\treturn bcrypt.GenerateFromPassword([]byte(userPassword), bcrypt.DefaultCost)\n}\n\n// ValidatePassword will check if passwords are matched.\nfunc ValidatePassword(userPassword string, hashed []byte) (bool, error) {\n\tif err := bcrypt.CompareHashAndPassword(hashed, []byte(userPassword)); err != nil {\n\t\treturn false, err\n\t}\n\treturn true, nil\n}\n"
  },
  {
    "path": "_examples/mvc/login/datasource/users.go",
    "content": "// file: datasource/users.go\n\npackage datasource\n\nimport (\n\t\"errors\"\n\n\t\"github.com/kataras/iris/v12/_examples/mvc/login/datamodels\"\n)\n\n// Engine is from where to fetch the data, in this case the users.\ntype Engine uint32\n\nconst (\n\t// Memory stands for simple memory location;\n\t// map[int64] datamodels.User ready to use, it's our source in this example.\n\tMemory Engine = iota\n\t// Bolt for boltdb source location.\n\tBolt\n\t// MySQL for mysql-compatible source location.\n\tMySQL\n)\n\n// LoadUsers returns all users(empty map) from the memory, for the sake of simplicty.\nfunc LoadUsers(engine Engine) (map[int64]datamodels.User, error) {\n\tif engine != Memory {\n\t\treturn nil, errors.New(\"for the sake of simplicity we're using a simple map as the data source\")\n\t}\n\n\treturn make(map[int64]datamodels.User), nil\n}\n"
  },
  {
    "path": "_examples/mvc/login/main.go",
    "content": "// file: main.go\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/_examples/mvc/login/datasource\"\n\t\"github.com/kataras/iris/v12/_examples/mvc/login/repositories\"\n\t\"github.com/kataras/iris/v12/_examples/mvc/login/services\"\n\t\"github.com/kataras/iris/v12/_examples/mvc/login/web/controllers\"\n\t\"github.com/kataras/iris/v12/_examples/mvc/login/web/middleware\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/mvc\"\n\t\"github.com/kataras/iris/v12/sessions\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\t// You got full debug messages, useful when using MVC and you want to make\n\t// sure that your code is aligned with the Iris' MVC Architecture.\n\tapp.Logger().SetLevel(\"debug\")\n\n\t// Load the template files.\n\ttmpl := iris.HTML(\"./web/views\", \".html\").\n\t\tLayout(\"shared/layout.html\").\n\t\tReload(true)\n\tapp.RegisterView(tmpl)\n\n\tapp.HandleDir(\"/public\", iris.Dir(\"./web/public\"))\n\n\tapp.OnAnyErrorCode(func(ctx iris.Context) {\n\t\tctx.ViewData(\"Message\", ctx.Values().\n\t\t\tGetStringDefault(\"message\", \"The page you're looking for doesn't exist\"))\n\t\tif err := ctx.View(\"shared/error.html\"); err != nil {\n\t\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\t\treturn\n\t\t}\n\t})\n\n\t// ---- Serve our controllers. ----\n\n\t// Prepare our repositories and services.\n\tdb, err := datasource.LoadUsers(datasource.Memory)\n\tif err != nil {\n\t\tapp.Logger().Fatalf(\"error while loading the users: %v\", err)\n\t\treturn\n\t}\n\trepo := repositories.NewUserRepository(db)\n\tuserService := services.NewUserService(repo)\n\n\t// \"/users\" based mvc application.\n\tusers := mvc.New(app.Party(\"/users\"))\n\t// Add the basic authentication(admin:password) middleware\n\t// for the /users based requests.\n\tusers.Router.Use(middleware.BasicAuth)\n\t// Bind the \"userService\" to the UserController's Service (interface) field.\n\tusers.Register(userService)\n\tusers.Handle(new(controllers.UsersController))\n\n\t// \"/user\" based mvc application.\n\tsessManager := sessions.New(sessions.Config{\n\t\tCookie:  \"sessioncookiename\",\n\t\tExpires: 24 * time.Hour,\n\t})\n\tuser := mvc.New(app.Party(\"/user\"))\n\tuser.Register(\n\t\tuserService,\n\t\tsessManager.Start,\n\t)\n\tuser.Handle(new(controllers.UserController))\n\n\t// http://localhost:8080/noexist\n\t// and all controller's methods like\n\t// http://localhost:8080/users/1\n\t// http://localhost:8080/user/register\n\t// http://localhost:8080/user/login\n\t// http://localhost:8080/user/me\n\t// http://localhost:8080/user/logout\n\t// basic auth: \"admin\", \"password\", see \"./middleware/basicauth.go\" source file.\n\n\t// Starts the web server at localhost:8080\n\t// Enables faster json serialization and more.\n\tapp.Listen(\":8080\", iris.WithOptimizations)\n}\n"
  },
  {
    "path": "_examples/mvc/login/repositories/user_repository.go",
    "content": "package repositories\n\nimport (\n\t\"errors\"\n\t\"sync\"\n\n\t\"github.com/kataras/iris/v12/_examples/mvc/login/datamodels\"\n)\n\n// Query represents the visitor and action queries.\ntype Query func(datamodels.User) bool\n\n// UserRepository handles the basic operations of a user entity/model.\n// It's an interface in order to be testable, i.e a memory user repository or\n// a connected to an sql database.\ntype UserRepository interface {\n\tExec(query Query, action Query, limit int, mode int) (ok bool)\n\n\tSelect(query Query) (user datamodels.User, found bool)\n\tSelectMany(query Query, limit int) (results []datamodels.User)\n\n\tInsertOrUpdate(user datamodels.User) (updatedUser datamodels.User, err error)\n\tDelete(query Query, limit int) (deleted bool)\n}\n\n// NewUserRepository returns a new user memory-based repository,\n// the one and only repository type in our example.\nfunc NewUserRepository(source map[int64]datamodels.User) UserRepository {\n\treturn &userMemoryRepository{source: source}\n}\n\n// userMemoryRepository is a \"UserRepository\"\n// which manages the users using the memory data source (map).\ntype userMemoryRepository struct {\n\tsource map[int64]datamodels.User\n\tmu     sync.RWMutex\n}\n\nconst (\n\t// ReadOnlyMode will RLock(read) the data .\n\tReadOnlyMode = iota\n\t// ReadWriteMode will Lock(read/write) the data.\n\tReadWriteMode\n)\n\nfunc (r *userMemoryRepository) Exec(query Query, action Query, actionLimit int, mode int) (ok bool) {\n\tloops := 0\n\n\tif mode == ReadOnlyMode {\n\t\tr.mu.RLock()\n\t\tdefer r.mu.RUnlock()\n\t} else {\n\t\tr.mu.Lock()\n\t\tdefer r.mu.Unlock()\n\t}\n\n\tfor _, user := range r.source {\n\t\tok = query(user)\n\t\tif ok {\n\t\t\tif action(user) {\n\t\t\t\tloops++\n\t\t\t\tif actionLimit >= loops {\n\t\t\t\t\tbreak // break\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn\n}\n\n// Select receives a query function\n// which is fired for every single user model inside\n// our imaginary data source.\n// When that function returns true then it stops the iteration.\n//\n// It returns the query's return last known boolean value\n// and the last known user model\n// to help callers to reduce the LOC.\n//\n// It's actually a simple but very clever prototype function\n// I'm using everywhere since I firstly think of it,\n// hope you'll find it very useful as well.\nfunc (r *userMemoryRepository) Select(query Query) (user datamodels.User, found bool) {\n\tfound = r.Exec(query, func(m datamodels.User) bool {\n\t\tuser = m\n\t\treturn true\n\t}, 1, ReadOnlyMode)\n\n\t// set an empty datamodels.User if not found at all.\n\tif !found {\n\t\tuser = datamodels.User{}\n\t}\n\n\treturn\n}\n\n// SelectMany same as Select but returns one or more datamodels.User as a slice.\n// If limit <=0 then it returns everything.\nfunc (r *userMemoryRepository) SelectMany(query Query, limit int) (results []datamodels.User) {\n\tr.Exec(query, func(m datamodels.User) bool {\n\t\tresults = append(results, m)\n\t\treturn true\n\t}, limit, ReadOnlyMode)\n\n\treturn\n}\n\n// InsertOrUpdate adds or updates a user to the (memory) storage.\n//\n// Returns the new user and an error if any.\nfunc (r *userMemoryRepository) InsertOrUpdate(user datamodels.User) (datamodels.User, error) {\n\tid := user.ID\n\n\tif id == 0 { // Create new action\n\t\tvar lastID int64\n\t\t// find the biggest ID in order to not have duplications\n\t\t// in productions apps you can use a third-party\n\t\t// library to generate a UUID as string.\n\t\tr.mu.RLock()\n\t\tfor _, item := range r.source {\n\t\t\tif item.ID > lastID {\n\t\t\t\tlastID = item.ID\n\t\t\t}\n\t\t}\n\t\tr.mu.RUnlock()\n\n\t\tid = lastID + 1\n\t\tuser.ID = id\n\n\t\t// map-specific thing\n\t\tr.mu.Lock()\n\t\tr.source[id] = user\n\t\tr.mu.Unlock()\n\n\t\treturn user, nil\n\t}\n\n\t// Update action based on the user.ID,\n\t// here we will allow updating the poster and genre if not empty.\n\t// Alternatively we could do pure replace instead:\n\t// r.source[id] = user\n\t// and comment the code below;\n\tcurrent, exists := r.Select(func(m datamodels.User) bool {\n\t\treturn m.ID == id\n\t})\n\n\tif !exists { // ID is not a real one, return an error.\n\t\treturn datamodels.User{}, errors.New(\"failed to update a nonexistent user\")\n\t}\n\n\t// or comment these and r.source[id] = user for pure replace\n\tif user.Username != \"\" {\n\t\tcurrent.Username = user.Username\n\t}\n\n\tif user.Firstname != \"\" {\n\t\tcurrent.Firstname = user.Firstname\n\t}\n\n\t// map-specific thing\n\tr.mu.Lock()\n\tr.source[id] = current\n\tr.mu.Unlock()\n\n\treturn user, nil\n}\n\nfunc (r *userMemoryRepository) Delete(query Query, limit int) bool {\n\treturn r.Exec(query, func(m datamodels.User) bool {\n\t\tdelete(r.source, m.ID)\n\t\treturn true\n\t}, limit, ReadWriteMode)\n}\n"
  },
  {
    "path": "_examples/mvc/login/services/user_service.go",
    "content": "package services\n\nimport (\n\t\"errors\"\n\n\t\"github.com/kataras/iris/v12/_examples/mvc/login/datamodels\"\n\t\"github.com/kataras/iris/v12/_examples/mvc/login/repositories\"\n)\n\n// UserService handles CRUID operations of a user datamodel,\n// it depends on a user repository for its actions.\n// It's here to decouple the data source from the higher level compoments.\n// As a result a different repository type can be used with the same logic without any aditional changes.\n// It's an interface and it's used as interface everywhere\n// because we may need to change or try an experimental different domain logic at the future.\ntype UserService interface {\n\tGetAll() []datamodels.User\n\tGetByID(id int64) (datamodels.User, bool)\n\tGetByUsernameAndPassword(username, userPassword string) (datamodels.User, bool)\n\tDeleteByID(id int64) bool\n\n\tUpdate(id int64, user datamodels.User) (datamodels.User, error)\n\tUpdatePassword(id int64, newPassword string) (datamodels.User, error)\n\tUpdateUsername(id int64, newUsername string) (datamodels.User, error)\n\n\tCreate(userPassword string, user datamodels.User) (datamodels.User, error)\n}\n\n// NewUserService returns the default user service.\nfunc NewUserService(repo repositories.UserRepository) UserService {\n\treturn &userService{\n\t\trepo: repo,\n\t}\n}\n\ntype userService struct {\n\trepo repositories.UserRepository\n}\n\n// GetAll returns all users.\nfunc (s *userService) GetAll() []datamodels.User {\n\treturn s.repo.SelectMany(func(_ datamodels.User) bool {\n\t\treturn true\n\t}, -1)\n}\n\n// GetByID returns a user based on its id.\nfunc (s *userService) GetByID(id int64) (datamodels.User, bool) {\n\treturn s.repo.Select(func(m datamodels.User) bool {\n\t\treturn m.ID == id\n\t})\n}\n\n// GetByUsernameAndPassword returns a user based on its username and password,\n// used for authentication.\nfunc (s *userService) GetByUsernameAndPassword(username, userPassword string) (datamodels.User, bool) {\n\tif username == \"\" || userPassword == \"\" {\n\t\treturn datamodels.User{}, false\n\t}\n\n\treturn s.repo.Select(func(m datamodels.User) bool {\n\t\tif m.Username == username {\n\t\t\thashed := m.HashedPassword\n\t\t\tif ok, _ := datamodels.ValidatePassword(userPassword, hashed); ok {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t\treturn false\n\t})\n}\n\n// Update updates every field from an existing User,\n// it's not safe to be used via public API,\n// however we will use it on the web/controllers/user_controller.go#PutBy\n// in order to show you how it works.\nfunc (s *userService) Update(id int64, user datamodels.User) (datamodels.User, error) {\n\tuser.ID = id\n\treturn s.repo.InsertOrUpdate(user)\n}\n\n// UpdatePassword updates a user's password.\nfunc (s *userService) UpdatePassword(id int64, newPassword string) (datamodels.User, error) {\n\t// update the user and return it.\n\thashed, err := datamodels.GeneratePassword(newPassword)\n\tif err != nil {\n\t\treturn datamodels.User{}, err\n\t}\n\n\treturn s.Update(id, datamodels.User{\n\t\tHashedPassword: hashed,\n\t})\n}\n\n// UpdateUsername updates a user's username.\nfunc (s *userService) UpdateUsername(id int64, newUsername string) (datamodels.User, error) {\n\treturn s.Update(id, datamodels.User{\n\t\tUsername: newUsername,\n\t})\n}\n\n// Create inserts a new User,\n// the userPassword is the client-typed password\n// it will be hashed before the insertion to our repository.\nfunc (s *userService) Create(userPassword string, user datamodels.User) (datamodels.User, error) {\n\tif user.ID > 0 || userPassword == \"\" || user.Firstname == \"\" || user.Username == \"\" {\n\t\treturn datamodels.User{}, errors.New(\"unable to create this user\")\n\t}\n\n\thashed, err := datamodels.GeneratePassword(userPassword)\n\tif err != nil {\n\t\treturn datamodels.User{}, err\n\t}\n\tuser.HashedPassword = hashed\n\n\treturn s.repo.InsertOrUpdate(user)\n}\n\n// DeleteByID deletes a user by its id.\n//\n// Returns true if deleted otherwise false.\nfunc (s *userService) DeleteByID(id int64) bool {\n\treturn s.repo.Delete(func(m datamodels.User) bool {\n\t\treturn m.ID == id\n\t}, 1)\n}\n"
  },
  {
    "path": "_examples/mvc/login/web/controllers/user_controller.go",
    "content": "// file: controllers/user_controller.go\n\npackage controllers\n\nimport (\n\t\"github.com/kataras/iris/v12/_examples/mvc/login/datamodels\"\n\t\"github.com/kataras/iris/v12/_examples/mvc/login/services\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/mvc\"\n\t\"github.com/kataras/iris/v12/sessions\"\n)\n\n// UserController is our /user controller.\n// UserController is responsible to handle the following requests:\n// GET  \t\t\t/user/register\n// POST \t\t\t/user/register\n// GET \t\t\t\t/user/login\n// POST \t\t\t/user/login\n// GET \t\t\t\t/user/me\n// All HTTP Methods /user/logout\ntype UserController struct {\n\t// context is auto-binded by Iris on each request,\n\t// remember that on each incoming request iris creates a new UserController each time,\n\t// so all fields are request-scoped by-default, only dependency injection is able to set\n\t// custom fields like the Service which is the same for all requests (static binding)\n\t// and the Session which depends on the current context (dynamic binding).\n\tCtx iris.Context\n\n\t// Our UserService, it's an interface which\n\t// is binded from the main application.\n\tService services.UserService\n\n\t// Session, binded using dependency injection from the main.go.\n\tSession *sessions.Session\n}\n\nconst userIDKey = \"UserID\"\n\nfunc (c *UserController) getCurrentUserID() int64 {\n\tuserID := c.Session.GetInt64Default(userIDKey, 0)\n\treturn userID\n}\n\nfunc (c *UserController) isLoggedIn() bool {\n\treturn c.getCurrentUserID() > 0\n}\n\nfunc (c *UserController) logout() {\n\tc.Session.Destroy()\n}\n\nvar registerStaticView = mvc.View{\n\tName: \"user/register.html\",\n\tData: iris.Map{\"Title\": \"User Registration\"},\n}\n\n// GetRegister handles GET: http://localhost:8080/user/register.\nfunc (c *UserController) GetRegister() mvc.Result {\n\tif c.isLoggedIn() {\n\t\tc.logout()\n\t}\n\n\treturn registerStaticView\n}\n\n// PostRegister handles POST: http://localhost:8080/user/register.\nfunc (c *UserController) PostRegister() mvc.Result {\n\t// get firstname, username and password from the form.\n\tvar (\n\t\tfirstname = c.Ctx.FormValue(\"firstname\")\n\t\tusername  = c.Ctx.FormValue(\"username\")\n\t\tpassword  = c.Ctx.FormValue(\"password\")\n\t)\n\n\t// create the new user, the password will be hashed by the service.\n\tu, err := c.Service.Create(password, datamodels.User{\n\t\tUsername:  username,\n\t\tFirstname: firstname,\n\t})\n\n\t// set the user's id to this session even if err != nil,\n\t// the zero id doesn't matters because .getCurrentUserID() checks for that.\n\t// If err != nil then it will be shown, see below on mvc.Response.Err: err.\n\tc.Session.Set(userIDKey, u.ID)\n\n\treturn mvc.Response{\n\t\t// if not nil then this error will be shown instead.\n\t\tErr: err,\n\t\t// redirect to /user/me.\n\t\tPath: \"/user/me\",\n\t\t// When redirecting from POST to GET request you -should- use this HTTP status code,\n\t\t// however there're some (complicated) alternatives if you\n\t\t// search online or even the HTTP RFC.\n\t\t// Status \"See Other\" RFC 7231, however iris can automatically fix that\n\t\t// but it's good to know you can set a custom code;\n\t\t// Code: 303,\n\t}\n}\n\nvar loginStaticView = mvc.View{\n\tName: \"user/login.html\",\n\tData: iris.Map{\"Title\": \"User Login\"},\n}\n\n// GetLogin handles GET: http://localhost:8080/user/login.\nfunc (c *UserController) GetLogin() mvc.Result {\n\tif c.isLoggedIn() {\n\t\t// if it's already logged in then destroy the previous session.\n\t\tc.logout()\n\t}\n\n\treturn loginStaticView\n}\n\n// PostLogin handles POST: http://localhost:8080/user/register.\nfunc (c *UserController) PostLogin() mvc.Result {\n\tvar (\n\t\tusername = c.Ctx.FormValue(\"username\")\n\t\tpassword = c.Ctx.FormValue(\"password\")\n\t)\n\n\tu, found := c.Service.GetByUsernameAndPassword(username, password)\n\n\tif !found {\n\t\treturn mvc.Response{\n\t\t\tPath: \"/user/register\",\n\t\t}\n\t}\n\n\tc.Session.Set(userIDKey, u.ID)\n\n\treturn mvc.Response{\n\t\tPath: \"/user/me\",\n\t}\n}\n\n// GetMe handles GET: http://localhost:8080/user/me.\nfunc (c *UserController) GetMe() mvc.Result {\n\tif !c.isLoggedIn() {\n\t\t// if it's not logged in then redirect user to the login page.\n\t\treturn mvc.Response{Path: \"/user/login\"}\n\t}\n\n\tu, found := c.Service.GetByID(c.getCurrentUserID())\n\tif !found {\n\t\t// if the  session exists but for some reason the user doesn't exist in the \"database\"\n\t\t// then logout and re-execute the function, it will redirect the client to the\n\t\t// /user/login page.\n\t\tc.logout()\n\t\treturn c.GetMe()\n\t}\n\n\treturn mvc.View{\n\t\tName: \"user/me.html\",\n\t\tData: iris.Map{\n\t\t\t\"Title\": \"Profile of \" + u.Username,\n\t\t\t\"User\":  u,\n\t\t},\n\t}\n}\n\n// AnyLogout handles All/Any HTTP Methods for: http://localhost:8080/user/logout.\nfunc (c *UserController) AnyLogout() {\n\tif c.isLoggedIn() {\n\t\tc.logout()\n\t}\n\n\tc.Ctx.Redirect(\"/user/login\")\n}\n"
  },
  {
    "path": "_examples/mvc/login/web/controllers/users_controller.go",
    "content": "package controllers\n\nimport (\n\t\"github.com/kataras/iris/v12/_examples/mvc/login/datamodels\"\n\t\"github.com/kataras/iris/v12/_examples/mvc/login/services\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\n// UsersController is our /users API controller.\n// GET\t\t\t\t/users  | get all\n// GET\t\t\t\t/users/{id:int64} | get by id\n// PUT\t\t\t\t/users/{id:int64} | update by id\n// DELETE\t\t\t/users/{id:int64} | delete by id\n// Requires basic authentication.\ntype UsersController struct {\n\t// Optionally: context is auto-binded by Iris on each request,\n\t// remember that on each incoming request iris creates a new UserController each time,\n\t// so all fields are request-scoped by-default, only dependency injection is able to set\n\t// custom fields like the Service which is the same for all requests (static binding).\n\tCtx iris.Context\n\n\t// Our UserService, it's an interface which\n\t// is binded from the main application.\n\tService services.UserService\n}\n\n// Get returns list of the users.\n// Demo:\n// curl -i -u admin:password http://localhost:8080/users\n//\n// The correct way if you have sensitive data:\n//\n//\tfunc (c *UsersController) Get() (results []viewmodels.User) {\n//\t\tdata := c.Service.GetAll()\n//\n//\t\tfor _, user := range data {\n//\t\t\tresults = append(results, viewmodels.User{user})\n//\t\t}\n//\t\treturn\n//\t}\n//\n// otherwise just return the datamodels.\nfunc (c *UsersController) Get() (results []datamodels.User) {\n\treturn c.Service.GetAll()\n}\n\n// GetBy returns a user.\n// Demo:\n// curl -i -u admin:password http://localhost:8080/users/1\nfunc (c *UsersController) GetBy(id int64) (user datamodels.User, found bool) {\n\tu, found := c.Service.GetByID(id)\n\tif !found {\n\t\t// this message will be binded to the\n\t\t// main.go -> app.OnAnyErrorCode -> NotFound -> shared/error.html -> .Message text.\n\t\tc.Ctx.Values().Set(\"message\", \"User couldn't be found!\")\n\t}\n\treturn u, found // it will throw/emit 404 if found == false.\n}\n\n// PutBy updates a user.\n// Demo:\n// curl -i -X PUT -u admin:password -F \"username=kataras\"\n// -F \"password=rawPasswordIsNotSafeIfOrNotHTTPs_You_Should_Use_A_client_side_lib_for_hash_as_well\"\n// http://localhost:8080/users/1\nfunc (c *UsersController) PutBy(id int64) (datamodels.User, error) {\n\t// username := c.Ctx.FormValue(\"username\")\n\t// password := c.Ctx.FormValue(\"password\")\n\tu := datamodels.User{}\n\tif err := c.Ctx.ReadForm(&u); err != nil {\n\t\treturn u, err\n\t}\n\n\treturn c.Service.Update(id, u)\n}\n\n// DeleteBy deletes a user.\n// Demo:\n// curl -i -X DELETE -u admin:password http://localhost:8080/users/1\nfunc (c *UsersController) DeleteBy(id int64) any {\n\twasDel := c.Service.DeleteByID(id)\n\tif wasDel {\n\t\t// return the deleted user's ID\n\t\treturn map[string]any{\"deleted\": id}\n\t}\n\t// right here we can see that a method function\n\t// can return any of those two types(map or int),\n\t// we don't have to specify the return type to a specific type.\n\treturn iris.StatusBadRequest // same as 400.\n}\n"
  },
  {
    "path": "_examples/mvc/login/web/middleware/basicauth.go",
    "content": "// file: middleware/basicauth.go\n\npackage middleware\n\nimport \"github.com/kataras/iris/v12/middleware/basicauth\"\n\n// BasicAuth middleware sample.\nvar BasicAuth = basicauth.Default(map[string]string{\n\t\"admin\": \"password\",\n})\n"
  },
  {
    "path": "_examples/mvc/login/web/public/css/site.css",
    "content": "/* Bordered form */\r\nform {\r\n    border: 3px solid #f1f1f1;\r\n}\r\n\r\n/* Full-width inputs */\r\ninput[type=text], input[type=password] {\r\n    width: 100%;\r\n    padding: 12px 20px;\r\n    margin: 8px 0;\r\n    display: inline-block;\r\n    border: 1px solid #ccc;\r\n    box-sizing: border-box;\r\n}\r\n\r\n/* Set a style for all buttons */\r\nbutton {\r\n    background-color: #4CAF50;\r\n    color: white;\r\n    padding: 14px 20px;\r\n    margin: 8px 0;\r\n    border: none;\r\n    cursor: pointer;\r\n    width: 100%;\r\n}\r\n\r\n/* Add a hover effect for buttons */\r\nbutton:hover {\r\n    opacity: 0.8;\r\n}\r\n\r\n/* Extra style for the cancel button (red) */\r\n.cancelbtn {\r\n    width: auto;\r\n    padding: 10px 18px;\r\n    background-color: #f44336;\r\n}\r\n\r\n/* Center the container */\r\n\r\n/* Add padding to containers */\r\n.container {\r\n    padding: 16px;\r\n}\r\n\r\n/* The \"Forgot password\" text */\r\nspan.psw {\r\n    float: right;\r\n    padding-top: 16px;\r\n}\r\n\r\n/* Change styles for span and cancel button on extra small screens */\r\n@media screen and (max-width: 300px) {\r\n    span.psw {\r\n        display: block;\r\n        float: none;\r\n    }\r\n    .cancelbtn {\r\n        width: 100%;\r\n    }\r\n}"
  },
  {
    "path": "_examples/mvc/login/web/viewmodels/README.md",
    "content": "# View Models\r\n\r\nThere should be the view models, the structure that the client will be able to see.\r\n\r\nExample:\r\n\r\n```go\r\nimport (\r\n    \"github.com/kataras/iris/v12/_examples/mvc/login/datamodels\"\r\n\r\n    \"github.com/kataras/iris/v12\"\r\n    \"github.com/kataras/iris/v12/context\"\r\n)\r\n\r\ntype User struct {\r\n    datamodels.User\r\n}\r\n\r\nfunc (m User) IsValid() bool {\r\n    /* do some checks and return true if it's valid... */\r\n    return m.ID > 0\r\n}\r\n```\r\n\r\nIris is able to convert any custom data Structure into an HTTP Response Dispatcher,\r\nso theoretically, something like the following is permitted if it's really necessary;\r\n\r\n```go\r\n// Dispatch completes the `kataras/iris/mvc#Result` interface.\r\n// Sends a `User` as a controlled http response.\r\n// If its ID is zero or less then it returns a 404 not found error\r\n// else it returns its json representation,\r\n// (just like the controller's functions do for custom types by default).\r\n//\r\n// Don't overdo it, the application's logic should not be here.\r\n// It's just one more step of validation before the response,\r\n// simple checks can be added here.\r\n//\r\n// It's just a showcase,\r\n// imagine the potentials this feature gives when designing a bigger application.\r\n//\r\n// This is called where the return value from a controller's method functions\r\n// is type of `User`.\r\n// For example the `controllers/user_controller.go#GetBy`.\r\nfunc (m User) Dispatch(ctx iris.Context) {\r\n    if !m.IsValid() {\r\n        ctx.NotFound()\r\n        return\r\n    }\r\n    ctx.JSON(m, context.JSON{Indent: \" \"})\r\n}\r\n```\r\n\r\nHowever, we will use the \"datamodels\" as the only one models package because\r\nUser structure doesn't contain any sensitive data, clients are able to see all of its fields\r\nand we don't need any extra functionality or validation inside it."
  },
  {
    "path": "_examples/mvc/login/web/views/shared/error.html",
    "content": "<h1>Error.</h1>\r\n<h2>An error occurred while processing your request.</h2>\r\n\r\n<h3>{{.Message}}</h3>\r\n\r\n<footer>\r\n    <h2>Sitemap</h2>\r\n    <a href=\"http://localhost:8080/user/register\">/user/register</a><br/>\r\n    <a href=\"http://localhost:8080/user/login\">/user/login</a><br/>\r\n    <a href=\"http://localhost:8080/user/logout\">/user/logout</a><br/>\r\n    <a href=\"http://localhost:8080/user/me\">/user/me</a><br/>\r\n    <h3>requires authentication</h3><br/>\r\n    <a href=\"http://localhost:8080/users\">/users</a><br/>\r\n    <a href=\"http://localhost:8080/users/1\">/users/{id}</a><br/>\r\n</footer>"
  },
  {
    "path": "_examples/mvc/login/web/views/shared/layout.html",
    "content": "<html>\r\n\r\n<head>\r\n    <title>{{.Title}}</title>\r\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"/public/css/site.css\" />\r\n</head>\r\n\r\n<body>\r\n    {{ yield . }}\r\n</body>\r\n\r\n</html>"
  },
  {
    "path": "_examples/mvc/login/web/views/user/login.html",
    "content": "<form action=\"/user/login\" method=\"POST\">\r\n    <div class=\"container\">\r\n        <label><b>Username</b></label>\r\n        <input type=\"text\" placeholder=\"Enter Username\" name=\"username\" required>\r\n\r\n        <label><b>Password</b></label>\r\n        <input type=\"password\" placeholder=\"Enter Password\" name=\"password\" required>\r\n\r\n        <button type=\"submit\">Login</button>\r\n    </div>\r\n</form>"
  },
  {
    "path": "_examples/mvc/login/web/views/user/me.html",
    "content": "<p>\r\n    Welcome back <strong>{{.User.Firstname}}</strong>!\r\n</p>"
  },
  {
    "path": "_examples/mvc/login/web/views/user/register.html",
    "content": "<form action=\"/user/register\" method=\"POST\">\r\n    <div class=\"container\">\r\n        <label><b>Firstname</b></label>\r\n        <input type=\"text\" placeholder=\"Enter Firstname\" name=\"firstname\" required>\r\n\r\n        <label><b>Username</b></label>\r\n        <input type=\"text\" placeholder=\"Enter Username\" name=\"username\" required>\r\n\r\n        <label><b>Password</b></label>\r\n        <input type=\"password\" placeholder=\"Enter Password\" name=\"password\" required>\r\n\r\n        <button type=\"submit\">Register</button>\r\n    </div>\r\n</form>"
  },
  {
    "path": "_examples/mvc/login-mvc-single-responsibility/main.go",
    "content": "package main\n\nimport (\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/_examples/mvc/login-mvc-single-responsibility/user\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/mvc\"\n\t\"github.com/kataras/iris/v12/sessions\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\t// You got full debug messages, useful when using MVC and you want to make\n\t// sure that your code is aligned with the Iris' MVC Architecture.\n\tapp.Logger().SetLevel(\"debug\")\n\n\tapp.RegisterView(iris.HTML(\"./views\", \".html\").Layout(\"shared/layout.html\"))\n\n\tapp.HandleDir(\"/public\", iris.Dir(\"./public\"))\n\n\tuserRouter := app.Party(\"/user\")\n\t{\n\t\tmanager := sessions.New(sessions.Config{\n\t\t\tCookie:  \"sessioncookiename\",\n\t\t\tExpires: 24 * time.Hour,\n\t\t})\n\t\tuserRouter.Use(manager.Handler())\n\t\tmvc.Configure(userRouter, configureUserMVC)\n\t}\n\n\t// http://localhost:8080/user/register\n\t// http://localhost:8080/user/login\n\t// http://localhost:8080/user/me\n\t// http://localhost:8080/user/logout\n\t// http://localhost:8080/user/1\n\tapp.Listen(\":8080\", configure)\n}\n\nfunc configureUserMVC(userApp *mvc.Application) {\n\tuserApp.Register(\n\t\tuser.NewDataSource(),\n\t)\n\tuserApp.Handle(new(user.Controller))\n}\n\nfunc configure(app *iris.Application) {\n\tapp.Configure(\n\t\tiris.WithOptimizations,\n\t\tiris.WithFireMethodNotAllowed,\n\t\tiris.WithLowercaseRouting,\n\t\tiris.WithPathIntelligence,\n\t\tiris.WithTunneling,\n\t)\n}\n"
  },
  {
    "path": "_examples/mvc/login-mvc-single-responsibility/public/css/site.css",
    "content": "/* Bordered form */\nform {\n    border: 3px solid #f1f1f1;\n}\n\n/* Full-width inputs */\ninput[type=text], input[type=password] {\n    width: 100%;\n    padding: 12px 20px;\n    margin: 8px 0;\n    display: inline-block;\n    border: 1px solid #ccc;\n    box-sizing: border-box;\n}\n\n/* Set a style for all buttons */\nbutton {\n    background-color: #4CAF50;\n    color: white;\n    padding: 14px 20px;\n    margin: 8px 0;\n    border: none;\n    cursor: pointer;\n    width: 100%;\n}\n\n/* Add a hover effect for buttons */\nbutton:hover {\n    opacity: 0.8;\n}\n\n/* Extra style for the cancel button (red) */\n.cancelbtn {\n    width: auto;\n    padding: 10px 18px;\n    background-color: #f44336;\n}\n\n/* Center the container */\n\n/* Add padding to containers */\n.container {\n    padding: 16px;\n}\n\n/* The \"Forgot password\" text */\nspan.psw {\n    float: right;\n    padding-top: 16px;\n}\n\n/* Change styles for span and cancel button on extra small screens */\n@media screen and (max-width: 300px) {\n    span.psw {\n        display: block;\n        float: none;\n    }\n    .cancelbtn {\n        width: 100%;\n    }\n}"
  },
  {
    "path": "_examples/mvc/login-mvc-single-responsibility/user/auth.go",
    "content": "package user\n\nimport (\n\t\"errors\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/mvc\"\n\t\"github.com/kataras/iris/v12/sessions\"\n)\n\nconst sessionIDKey = \"UserID\"\n\n// paths\nvar (\n\tPathLogin  = mvc.Response{Path: \"/user/login\"}\n\tPathLogout = mvc.Response{Path: \"/user/logout\"}\n)\n\n// AuthController is the user authentication controller, a custom shared controller.\ntype AuthController struct {\n\t// context is auto-binded if struct depends on this,\n\t// in this controller we don't we do everything with mvc-style,\n\t// and that's neither the 30% of its features.\n\t// Ctx iris.Context\n\n\tSource  *DataSource\n\tSession *sessions.Session\n\n\t// the whole controller is request-scoped because we already depend on Session, so\n\t// this will be new for each new incoming request, BeginRequest sets that based on the session.\n\tUserID int64\n}\n\n// BeginRequest saves login state to the context, the user id.\nfunc (c *AuthController) BeginRequest(ctx iris.Context) {\n\tc.UserID, _ = c.Session.GetInt64(sessionIDKey)\n}\n\n// EndRequest is here just to complete the BaseController\n// in order to be tell iris to call the `BeginRequest` before the main method.\nfunc (c *AuthController) EndRequest(ctx iris.Context) {}\n\nfunc (c *AuthController) fireError(err error) mvc.View {\n\treturn mvc.View{\n\t\tCode: iris.StatusBadRequest,\n\t\tName: \"shared/error.html\",\n\t\tData: iris.Map{\"Title\": \"User Error\", \"Message\": strings.ToUpper(err.Error())},\n\t}\n}\n\nfunc (c *AuthController) redirectTo(id int64) mvc.Response {\n\treturn mvc.Response{Path: \"/user/\" + strconv.Itoa(int(id))}\n}\n\nfunc (c *AuthController) createOrUpdate(firstname, username, password string) (user Model, err error) {\n\tusername = strings.Trim(username, \" \")\n\tif username == \"\" || password == \"\" || firstname == \"\" {\n\t\treturn user, errors.New(\"empty firstname, username or/and password\")\n\t}\n\n\tuserToInsert := Model{\n\t\tFirstname: firstname,\n\t\tUsername:  username,\n\t\tpassword:  password,\n\t} // password is hashed by the Source.\n\n\tnewUser, err := c.Source.InsertOrUpdate(userToInsert)\n\tif err != nil {\n\t\treturn user, err\n\t}\n\n\treturn newUser, nil\n}\n\nfunc (c *AuthController) isLoggedIn() bool {\n\t// we don't search by session, we have the user id\n\t// already by the `BeginRequest` middleware.\n\treturn c.UserID > 0\n}\n\nfunc (c *AuthController) verify(username, password string) (user Model, err error) {\n\tif username == \"\" || password == \"\" {\n\t\treturn user, errors.New(\"please fill both username and password fields\")\n\t}\n\n\tu, found := c.Source.GetByUsername(username)\n\tif !found {\n\t\t// if user found with that username not found at all.\n\t\treturn user, errors.New(\"user with that username does not exist\")\n\t}\n\n\tif ok, err := ValidatePassword(password, u.HashedPassword); err != nil || !ok {\n\t\t// if user found but an error occurred or the password is not valid.\n\t\treturn user, errors.New(\"please try to login with valid credentials\")\n\t}\n\n\treturn u, nil\n}\n\n// if logged in then destroy the session\n// and redirect to the login page\n// otherwise redirect to the registration page.\nfunc (c *AuthController) logout() mvc.Response {\n\tif c.isLoggedIn() {\n\t\tc.Session.Destroy()\n\t}\n\treturn PathLogin\n}\n"
  },
  {
    "path": "_examples/mvc/login-mvc-single-responsibility/user/controller.go",
    "content": "package user\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/mvc\"\n)\n\nvar (\n\t// About Code: iris.StatusSeeOther ->\n\t// When redirecting from POST to GET request you -should- use this HTTP status code,\n\t// however there're some (complicated) alternatives if you\n\t// search online or even the HTTP RFC.\n\t// \"See Other\" RFC 7231\n\tpathMyProfile = mvc.Response{Path: \"/user/me\", Code: iris.StatusSeeOther}\n\tpathRegister  = mvc.Response{Path: \"/user/register\"}\n)\n\n// Controller is responsible to handle the following requests:\n// GET  \t\t\t/user/register\n// POST \t\t\t/user/register\n// GET \t\t\t\t/user/login\n// POST \t\t\t/user/login\n// GET \t\t\t\t/user/me\n// GET\t\t\t\t/user/{id:int64}\n// All HTTP Methods /user/logout\ntype Controller struct {\n\tAuthController\n}\n\ntype formValue func(string) string\n\n// BeforeActivation called once before the server start\n// and before the controller's registration, here you can add\n// dependencies, to this controller and only, that the main caller may skip.\nfunc (c *Controller) BeforeActivation(b mvc.BeforeActivation) {\n\t// bind the context's `FormValue` as well in order to be\n\t// acceptable on the controller or its methods' input arguments (NEW feature as well).\n\tb.Dependencies().Register(func(ctx iris.Context) formValue { return ctx.FormValue })\n}\n\ntype page struct {\n\tTitle string\n}\n\n// GetRegister handles GET:/user/register.\n// mvc.Result can accept any struct which contains a `Dispatch(ctx iris.Context)` method.\n// Both mvc.Response and mvc.View are mvc.Result.\nfunc (c *Controller) GetRegister() mvc.Result {\n\tif c.isLoggedIn() {\n\t\treturn c.logout()\n\t}\n\n\t// You could just use it as a variable to win some time in serve-time,\n\t// this is an exersise for you :)\n\treturn mvc.View{\n\t\tName: pathRegister.Path + \".html\",\n\t\tData: page{\"User Registration\"},\n\t}\n}\n\n// PostRegister handles POST:/user/register.\nfunc (c *Controller) PostRegister(form formValue) mvc.Result {\n\t// we can either use the `c.Ctx.ReadForm` or read values one by one.\n\tvar (\n\t\tfirstname = form(\"firstname\")\n\t\tusername  = form(\"username\")\n\t\tpassword  = form(\"password\")\n\t)\n\n\tuser, err := c.createOrUpdate(firstname, username, password)\n\tif err != nil {\n\t\treturn c.fireError(err)\n\t}\n\n\t// setting a session value was never easier.\n\tc.Session.Set(sessionIDKey, user.ID)\n\t// succeed, nothing more to do here, just redirect to the /user/me.\n\treturn pathMyProfile\n}\n\n// with these static views,\n// you can use variables-- that are initialized before server start\n// so you can win some time on serving.\n// You can do it else where as well but I let them as pracise for you,\n// essentially you can understand by just looking below.\nvar userLoginView = mvc.View{\n\tName: PathLogin.Path + \".html\",\n\tData: page{\"User Login\"},\n}\n\n// GetLogin handles GET:/user/login.\nfunc (c *Controller) GetLogin() mvc.Result {\n\tif c.isLoggedIn() {\n\t\treturn c.logout()\n\t}\n\treturn userLoginView\n}\n\n// PostLogin handles POST:/user/login.\nfunc (c *Controller) PostLogin(form formValue) mvc.Result {\n\tvar (\n\t\tusername = form(\"username\")\n\t\tpassword = form(\"password\")\n\t)\n\n\tuser, err := c.verify(username, password)\n\tif err != nil {\n\t\treturn c.fireError(err)\n\t}\n\n\tc.Session.Set(sessionIDKey, user.ID)\n\treturn pathMyProfile\n}\n\n// AnyLogout handles any method on path /user/logout.\nfunc (c *Controller) AnyLogout() {\n\tc.logout()\n}\n\n// GetMe handles GET:/user/me.\nfunc (c *Controller) GetMe() mvc.Result {\n\tid, err := c.Session.GetInt64(sessionIDKey)\n\tif err != nil || id <= 0 {\n\t\t// when not already logged in, redirect to login.\n\t\treturn PathLogin\n\t}\n\n\tu, found := c.Source.GetByID(id)\n\tif !found {\n\t\t// if the  session exists but for some reason the user doesn't exist in the \"database\"\n\t\t// then logout him and redirect to the register page.\n\t\treturn c.logout()\n\t}\n\n\t// set the model and render the view template.\n\treturn mvc.View{\n\t\tName: pathMyProfile.Path + \".html\",\n\t\tData: iris.Map{\n\t\t\t\"Title\": \"Profile of \" + u.Username,\n\t\t\t\"User\":  u,\n\t\t},\n\t}\n}\n\nfunc (c *Controller) renderNotFound(id int64) mvc.View {\n\treturn mvc.View{\n\t\tCode: iris.StatusNotFound,\n\t\tName: \"user/notfound.html\",\n\t\tData: iris.Map{\n\t\t\t\"Title\": \"User Not Found\",\n\t\t\t\"ID\":    id,\n\t\t},\n\t}\n}\n\n// Dispatch completes the `mvc.Result` interface\n// in order to be able to return a type of `Model`\n// as mvc.Result.\n// If this function didn't exist then\n// we should explicit set the output result to that Model or to an any.\nfunc (u Model) Dispatch(ctx iris.Context) {\n\tctx.JSON(u)\n}\n\n// GetBy handles GET:/user/{id:int64},\n// i.e http://localhost:8080/user/1\nfunc (c *Controller) GetBy(userID int64) mvc.Result {\n\t// we have /user/{id}\n\t// fetch and render user json.\n\tuser, found := c.Source.GetByID(userID)\n\tif !found {\n\t\t// not user found with that ID.\n\t\treturn c.renderNotFound(userID)\n\t}\n\n\t// Q: how the hell Model can be return as mvc.Result?\n\t// A: I told you before on some comments and the docs,\n\t// any struct that has a `Dispatch(ctx iris.Context)`\n\t// can be returned as an mvc.Result(see ~20 lines above),\n\t// therefore we are able to combine many type of results in the same method.\n\t// For example, here, we return either an mvc.View to render a not found custom template\n\t// either a user which returns the Model as JSON via its Dispatch.\n\t//\n\t// We could also return just a struct value that is not an mvc.Result,\n\t// if the output result of the `GetBy` was that struct's type or an any\n\t// and iris would render that with JSON as well, but here we can't do that without complete the `Dispatch`\n\t// function, because we may return an mvc.View which is an mvc.Result.\n\treturn user\n}\n"
  },
  {
    "path": "_examples/mvc/login-mvc-single-responsibility/user/datasource.go",
    "content": "package user\n\nimport (\n\t\"errors\"\n\t\"sync\"\n\t\"time\"\n)\n\n// IDGenerator would be our user ID generator\n// but here we keep the order of users by their IDs\n// so we will use numbers that can be easly written\n// to the browser to get results back from the REST API.\n// var IDGenerator = func() string {\n// \treturn uuid.NewV4().String()\n// }\n\n// DataSource is our data store example.\ntype DataSource struct {\n\tUsers map[int64]Model\n\tmu    sync.RWMutex\n}\n\n// NewDataSource returns a new user data source.\nfunc NewDataSource() *DataSource {\n\treturn &DataSource{\n\t\tUsers: make(map[int64]Model),\n\t}\n}\n\n// GetBy receives a query function\n// which is fired for every single user model inside\n// our imaginary database.\n// When that function returns true then it stops the iteration.\n//\n// It returns the query's return last known boolean value\n// and the last known user model\n// to help callers to reduce the loc.\n//\n// But be carefully, the caller should always check for the \"found\"\n// because it may be false but the user model has actually real data inside it.\n//\n// It's actually a simple but very clever prototype function\n// I'm think of and using everywhere since then,\n// hope you find it very useful too.\nfunc (d *DataSource) GetBy(query func(Model) bool) (user Model, found bool) {\n\td.mu.RLock()\n\tfor _, user = range d.Users {\n\t\tfound = query(user)\n\t\tif found {\n\t\t\tbreak\n\t\t}\n\t}\n\td.mu.RUnlock()\n\treturn\n}\n\n// GetByID returns a user model based on its ID.\nfunc (d *DataSource) GetByID(id int64) (Model, bool) {\n\treturn d.GetBy(func(u Model) bool {\n\t\treturn u.ID == id\n\t})\n}\n\n// GetByUsername returns a user model based on the Username.\nfunc (d *DataSource) GetByUsername(username string) (Model, bool) {\n\treturn d.GetBy(func(u Model) bool {\n\t\treturn u.Username == username\n\t})\n}\n\nfunc (d *DataSource) getLastID() (lastID int64) {\n\td.mu.RLock()\n\tfor id := range d.Users {\n\t\tif id > lastID {\n\t\t\tlastID = id\n\t\t}\n\t}\n\td.mu.RUnlock()\n\n\treturn lastID\n}\n\n// InsertOrUpdate adds or updates a user to the (memory) storage.\nfunc (d *DataSource) InsertOrUpdate(user Model) (Model, error) {\n\t// no matter what we will update the password hash\n\t// for both update and insert actions.\n\thashedPassword, err := GeneratePassword(user.password)\n\tif err != nil {\n\t\treturn user, err\n\t}\n\tuser.HashedPassword = hashedPassword\n\n\t// update\n\tif id := user.ID; id > 0 {\n\t\t_, found := d.GetByID(id)\n\t\tif !found {\n\t\t\treturn user, errors.New(\"ID should be zero or a valid one that maps to an existing User\")\n\t\t}\n\t\td.mu.Lock()\n\t\td.Users[id] = user\n\t\td.mu.Unlock()\n\t\treturn user, nil\n\t}\n\n\t// insert\n\tid := d.getLastID() + 1\n\tuser.ID = id\n\td.mu.Lock()\n\tuser.CreatedAt = time.Now()\n\td.Users[id] = user\n\td.mu.Unlock()\n\n\treturn user, nil\n}\n"
  },
  {
    "path": "_examples/mvc/login-mvc-single-responsibility/user/model.go",
    "content": "package user\n\nimport (\n\t\"time\"\n\n\t\"golang.org/x/crypto/bcrypt\"\n)\n\n// Model is our User example model.\ntype Model struct {\n\tID        int64  `json:\"id\"`\n\tFirstname string `json:\"firstname\"`\n\tUsername  string `json:\"username\"`\n\t// password is the client-given password\n\t// which will not be stored anywhere in the server.\n\t// It's here only for actions like registration and update password,\n\t// because we caccept a Model instance\n\t// inside the `DataSource#InsertOrUpdate` function.\n\tpassword       string\n\tHashedPassword []byte    `json:\"-\"`\n\tCreatedAt      time.Time `json:\"created_at\"`\n}\n\n// GeneratePassword will generate a hashed password for us based on the\n// user's input.\nfunc GeneratePassword(userPassword string) ([]byte, error) {\n\treturn bcrypt.GenerateFromPassword([]byte(userPassword), bcrypt.DefaultCost)\n}\n\n// ValidatePassword will check if passwords are matched.\nfunc ValidatePassword(userPassword string, hashed []byte) (bool, error) {\n\tif err := bcrypt.CompareHashAndPassword(hashed, []byte(userPassword)); err != nil {\n\t\treturn false, err\n\t}\n\treturn true, nil\n}\n"
  },
  {
    "path": "_examples/mvc/login-mvc-single-responsibility/views/shared/error.html",
    "content": "<h1>Error.</h1>\n<h2>An error occurred while processing your request.</h2>\n\n<h3>{{.Message}}</h3>"
  },
  {
    "path": "_examples/mvc/login-mvc-single-responsibility/views/shared/layout.html",
    "content": "<html>\n\n<head>\n    <title>{{.Title}}</title>\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"/public/css/site.css\" />\n</head>\n\n<body>\n    {{ yield . }}\n</body>\n\n</html>"
  },
  {
    "path": "_examples/mvc/login-mvc-single-responsibility/views/user/login.html",
    "content": "<form action=\"/user/login\" method=\"POST\">\n    <div class=\"container\">\n        <label><b>Username</b></label>\n        <input type=\"text\" placeholder=\"Enter Username\" name=\"username\" required>\n\n        <label><b>Password</b></label>\n        <input type=\"password\" placeholder=\"Enter Password\" name=\"password\" required>\n\n        <button type=\"submit\">Login</button>\n    </div>\n</form>"
  },
  {
    "path": "_examples/mvc/login-mvc-single-responsibility/views/user/me.html",
    "content": "<p>\n    Welcome back <strong>{{.User.Firstname}}</strong>!\n</p>"
  },
  {
    "path": "_examples/mvc/login-mvc-single-responsibility/views/user/notfound.html",
    "content": "<p>\n    User with ID <strong>{{.ID}}</strong> does not exist.\n</p>"
  },
  {
    "path": "_examples/mvc/login-mvc-single-responsibility/views/user/register.html",
    "content": "<form action=\"/user/register\" method=\"POST\">\n    <div class=\"container\">\n        <label><b>Firstname</b></label>\n        <input type=\"text\" placeholder=\"Enter Firstname\" name=\"firstname\" required>\n\n        <label><b>Username</b></label>\n        <input type=\"text\" placeholder=\"Enter Username\" name=\"username\" required>\n\n        <label><b>Password</b></label>\n        <input type=\"password\" placeholder=\"Enter Password\" name=\"password\" required>\n\n        <button type=\"submit\">Register</button>\n    </div>\n</form>"
  },
  {
    "path": "_examples/mvc/middleware/main.go",
    "content": "// Package main shows how you can add middleware to an mvc Application, simply\n// by using its `Router` which is a sub router(an iris.Party) of the main iris app.\npackage main\n\nimport (\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/cache\"\n\t\"github.com/kataras/iris/v12/mvc\"\n)\n\nvar cacheHandler = cache.Handler(10 * time.Second)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.Logger().SetLevel(\"debug\")\n\n\tmvc.Configure(app, configure)\n\n\t// http://localhost:8080\n\t// http://localhost:8080/other\n\t//\n\t// refresh every 10 seconds and you'll see different time output.\n\tapp.Listen(\":8080\")\n}\n\nfunc configure(m *mvc.Application) {\n\tm.Router.Use(cacheHandler)\n\tm.Handle(&exampleController{\n\t\ttimeFormat: \"Mon, Jan 02 2006 15:04:05\",\n\t} /* ,mvc.IgnoreEmbedded --- Can be used to ignore any embedded struct method handlers */)\n}\n\ntype exampleController struct {\n\ttimeFormat string\n}\n\nfunc (c *exampleController) Get() string {\n\tnow := time.Now().Format(c.timeFormat)\n\treturn \"last time executed without cache: \" + now\n}\n\nfunc (c *exampleController) GetOther() string {\n\tnow := time.Now().Format(c.timeFormat)\n\treturn \"/other: \" + now\n}\n"
  },
  {
    "path": "_examples/mvc/middleware/per-method/main.go",
    "content": "/*\nIf you want to use it as middleware for the entire controller\nyou can use its router which is just a sub router to add it as you normally do with standard API:\n\nI'll show you 4 different methods for adding a middleware into an mvc application,\nall of those 4 do exactly the same thing, select what you prefer,\nI prefer the last code-snippet when I need the middleware to be registered somewhere\nelse as well, otherwise I am going with the first one:\n\n```go\n// 1\n\n\tmvc.Configure(app.Party(\"/user\"), func(m *mvc.Application) {\n\t     m.Router.Use(cache.Handler(10*time.Second))\n\t})\n\n```\n\n```go\n// 2\n// same:\nuserRouter := app.Party(\"/user\")\nuserRouter.Use(cache.Handler(10*time.Second))\nmvc.Configure(userRouter, ...)\n```\n\n```go\n// 3\n// same:\nuserRouter := app.Party(\"/user\", cache.Handler(10*time.Second))\nmvc.Configure(userRouter, ...)\n```\n\n```go\n// 4\n// same:\n\n\tapp.PartyFunc(\"/user\", func(r iris.Party){\n\t    r.Use(cache.Handler(10*time.Second))\n\t    mvc.Configure(r, ...)\n\t})\n\n```\n\nIf you want to use a middleware for a single route,\nfor a single controller's method that is already registered by the engine\nand not by custom `Handle` (which you can add\nthe middleware there on the last parameter) and it's not depend on the `Next Handler` to do its job\nthen you just call it on the method:\n\n```go\nvar myMiddleware := myMiddleware.New(...) // this should return an iris/context.Handler\n\ntype UserController struct{}\n\n\tfunc (c *UserController) GetSomething(ctx iris.Context) {\n\t    // ctx.Proceed checks if myMiddleware called `ctx.Next()`\n\t    // inside it and returns true if so, otherwise false.\n\t    nextCalled := ctx.Proceed(myMiddleware)\n\t    if !nextCalled {\n\t        return\n\t    }\n\n\t    // else do the job here, it's allowed\n\t}\n\n```\n\nAnd last, if you want to add a middleware on a specific method\nand it depends on the next and the whole chain then you have to do it\nusing the `AfterActivation` like the example below:\n*/\npackage main\n\nimport (\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/cache\"\n\t\"github.com/kataras/iris/v12/mvc\"\n)\n\nvar cacheHandler = cache.Handler(10 * time.Second)\n\nfunc main() {\n\tapp := iris.New()\n\t// You don't have to use .Configure if you do it all in the main func\n\t// mvc.Configure and mvc.New(...).Configure() are just helpers to split\n\t// your code better, here we use the simplest form:\n\tm := mvc.New(app)\n\tm.Handle(&exampleController{})\n\n\tapp.Listen(\":8080\")\n}\n\ntype exampleController struct{}\n\nfunc (c *exampleController) AfterActivation(a mvc.AfterActivation) {\n\t// select the route based on the method name you want to\n\t// modify.\n\tindex := a.GetRoute(\"Get\")\n\t// just prepend the handler(s) as middleware(s) you want to use.\n\t// or append for \"done\" handlers.\n\tindex.Handlers = append([]iris.Handler{cacheHandler}, index.Handlers...)\n}\n\nfunc (c *exampleController) Get() string {\n\t// refresh every 10 seconds and you will see different time output.\n\tnow := time.Now().Format(\"Mon, Jan 02 2006 15:04:05\")\n\treturn \"last time executed without cache: \" + now\n}\n"
  },
  {
    "path": "_examples/mvc/middleware/without-ctx-next/main.go",
    "content": "/*\nPackage main shows how to add done handlers in an MVC application without\nthe necessity of `ctx.Next()` inside the controller's methods.\n\nWhen we want the `Done` handlers of that specific mvc app's `Party`\nto be executed but we don't want to add `ctx.Next()` on the `exampleController#EndRequest`\n*/\npackage main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/mvc\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.Get(\"/\", func(ctx iris.Context) { ctx.Redirect(\"/example\") })\n\n\texampleRouter := app.Party(\"/example\")\n\t{\n\t\texampleRouter.SetExecutionRules(iris.ExecutionRules{\n\t\t\tDone: iris.ExecutionOptions{Force: true},\n\t\t})\n\n\t\texampleRouter.Done(doneHandler)\n\n\t\tm := mvc.New(exampleRouter)\n\t\tm.Handle(&exampleController{})\n\t}\n\n\tapp.Listen(\":8080\")\n}\n\nfunc doneHandler(ctx iris.Context) {\n\tctx.WriteString(\"\\nFrom Done Handler\")\n}\n\ntype exampleController struct{}\n\nfunc (c *exampleController) Get() string {\n\treturn \"From Main Handler\"\n\t// Note that here we don't binding the `Context`, and we don't call its `Next()`\n\t// function in order to call the `doneHandler`,\n\t// this is done automatically for us because we changed the execution rules with the\n\t// `SetExecutionRules`.\n\t//\n\t// Therefore the final output is:\n\t// From Main Handler\n\t// From Done Handler\n}\n"
  },
  {
    "path": "_examples/mvc/overview/Dockerfile",
    "content": "# docker build -t app . \n# docker run --rm -it -p 8080:8080 app:latest\nFROM golang:latest AS builder\nRUN apt-get update\nENV GO111MODULE=on \\\n    CGO_ENABLED=0 \\\n    GOOS=linux \\\n    GOARCH=amd64\nWORKDIR /go/src/app\nCOPY go.mod .\nRUN go mod download\nCOPY . .\nRUN go install\n\nFROM scratch\nCOPY --from=builder /go/bin/app .\nENTRYPOINT [\"./app\"]"
  },
  {
    "path": "_examples/mvc/overview/README.md",
    "content": "# Quick start\n\nThe following guide is just a simple example of usage of some of the **Iris MVC** features. You are not limited to that data structure or code flow. \n\nCreate a folder, let's assume its path is `app`. The structure should look like that:\n\n```\n│   main.go\n│\tgo.mod\n│\tgo.sum\n└───environment\n│      environment.go\n└───model\n│      request.go\n│      response.go\n└───database\n│       database.go\n│       mysql.go\n│       sqlite.go\n└───service\n│       greet_service.go\n└───controller\n        greet_controller.go\n```\n\nNavigate to that `app` folder and execute the following command:\n\n```sh\n$ go init app\n$ go get github.com/kataras/iris/v12@main\n#\t\t\t\t\t\t\t\t \tor @latest for the latest official release.\n```\n\n## Environment\n\nLet's start by defining the available environments that our web-application can behave on.\n\nWe'll just work on two available environments, the \"development\" and the \"production\", as they define the two most common scenarios. The `ReadEnv` will read from the `Env` type of a system's environment variable (see `main.go` in the end of the page).\n\nCreate a `environment/environment.go` file and put the following contents:\n\n```go\npackage environment\n\nimport (\n    \"os\"\n    \"strings\"\n)\n\nconst (\n\tPROD Env = \"production\"\n\tDEV  Env = \"development\"\n)\n\ntype Env string\n\nfunc (e Env) String() string {\n    return string(e)\n}\n\nfunc ReadEnv(key string, def Env) Env {\n    v := Getenv(key, def.String())\n    if v == \"\" {\n        return def\n    }\n\n    env := Env(strings.ToLower(v))\n    switch env {\n    case PROD, DEV: // allowed.\n    default:\n        panic(\"unexpected environment \" + v)\n    }\n\n    return env\n}\n\nfunc Getenv(key string, def string) string {\n    if v := os.Getenv(key); v != \"\" {\n        return v\n    }\n\n    return def\n}\n```\n\n## Database\n\nWe will use two database management systems, the `MySQL` and the `SQLite`. The first one for \"production\" use and the other for \"development\".\n\nCreate a `database/database.go` file and copy-paste the following:\n\n```go\npackage database\n\nimport \"app/environment\"\n\ntype DB interface {\n\tExec(q string) error\n}\n\nfunc NewDB(env environment.Env) DB {\n\tswitch env {\n\tcase environment.PROD:\n\t\treturn &mysql{}\n\tcase environment.DEV:\n\t\treturn &sqlite{}\n\tdefault:\n\t\tpanic(\"unknown environment\")\n\t}\n}\n```\n\nLet's simulate our MySQL and SQLite `DB` instances. Create a `database/mysql.go` file which looks like the following one:\n\n```go\npackage database\n\nimport \"fmt\"\n\ntype mysql struct{}\n\nfunc (db *mysql) Exec(q string) error {\n\treturn fmt.Errorf(\"mysql: not implemented <%s>\", q)\n}\n```\n\nAnd a `database/sqlite.go` file.\n\n```go\npackage database\n\ntype sqlite struct{}\n\nfunc (db *sqlite) Exec(q string) error { return nil }\n```\n\nThe `DB` depends on the `Environment.\n\n> A practical and operational database example, including Docker images, can be found at the following guide: https://github.com/kataras/iris/tree/main/_examples/database/mysql\n\n## Service\n\nWe'll need a service that will communicate with a database instance in behalf of our Controller(s).\n\nIn our case we will only need a single service, the Greet Service.\n\nFor the sake of the example, let's use two implementations of a greet service based on the `Environment`. The `GreetService` interface contains a single method of `Say(input string) (output string, err error)`. Create a `./service/greet_service.go` file and write the following code:\n\n```go\npackage service\n\nimport (\n    \"fmt\"\n\n    \"app/database\"\n    \"app/environment\"\n)\n\n// GreetService example service.\ntype GreetService interface {\n\tSay(input string) (string, error)\n}\n\n// NewGreetService returns a service backed with a \"db\" based on \"env\".\nfunc NewGreetService(env environment.Env, db database.DB) GreetService {\n\tservice := &greeter{db: db, prefix: \"Hello\"}\n\n\tswitch env {\n\tcase environment.PROD:\n\t\treturn service\n\tcase environment.DEV:\n\t\treturn &greeterWithLogging{service}\n\tdefault:\n\t\tpanic(\"unknown environment\")\n\t}\n}\n\ntype greeter struct {\n\tprefix string\n\tdb     database.DB\n}\n\nfunc (s *greeter) Say(input string) (string, error) {\n\tif err := s.db.Exec(\"simulate a query...\"); err != nil {\n\t\treturn \"\", err\n\t}\n\n\tresult := s.prefix + \" \" + input\n\treturn result, nil\n}\n\ntype greeterWithLogging struct {\n\t*greeter\n}\n\nfunc (s *greeterWithLogging) Say(input string) (string, error) {\n\tresult, err := s.greeter.Say(input)\n\tfmt.Printf(\"result: %s\\nerror: %v\\n\", result, err)\n\treturn result, err\n}\n\n```\n\nThe `greeter` will be used on \"production\" and the `greeterWithLogging` on \"development\". The `GreetService` depends on the `Environment` and the `DB`.\n\n## Models\n\nContinue by creating our HTTP request and response models.\n\nCreate a `model/request.go` file and copy-paste the following code:\n\n```go\npackage model\n\ntype Request struct {\n    Name string `url:\"name\"`\n}\n```\n\nSame for the `model/response.go` file.\n\n```go\npackage model\n\ntype Response struct {\n    Message string `json:\"msg\"`\n}\n```\n\nThe server will accept a URL Query Parameter of `name` (e.g. `/greet?name=kataras`) and will reply back with a JSON message.\n\n## Controller\n\nMVC Controllers are responsible for controlling the flow of the application execution. When you make a request (means request a page) to MVC Application, a controller is responsible for returning the response to that request.\n\nWe will only need the `GreetController` for our mini web-application. Create a file at `controller/greet_controller.go` which looks like that:\n\n```go\npackage controller\n\nimport (\n\t\"app/model\"\n\t\"app/service\"\n)\n\ntype GreetController struct {\n\tService service.GreetService\n\t// Ctx iris.Context\n}\n\nfunc (c *GreetController) Get(req model.Request) (model.Response, error) {\n\tmessage, err := c.Service.Say(req.Name)\n\tif err != nil {\n\t\treturn model.Response{}, err\n\t}\n\n\treturn model.Response{Message: message}, nil\n}\n```\n\nThe `GreetController` depends on the `GreetService`. It serves the `GET: /greet` index path through its `Get` method. The `Get` method accepts a `model.Request` which contains a single field name of `Name` which will be extracted from the `URL Query Parameter 'name'` (because it's a `GET` requst and its `url:\"name\"` struct field).\n\n## Wrap up\n\n```sh\n                                         +-------------------+\n                                         |  Env (DEV, PROD)  |\n                                         +---------+---------+\n                                         |         |         |\n                                         |         |         |\n                                         |         |         |\n                                    DEV  |         |         |  PROD\n-------------------+---------------------+         |         +----------------------+-------------------\n                   |                               |                                |\n                   |                               |                                |\n               +---+-----+        +----------------v------------------+        +----+----+\n               | sqlite  |        |         NewDB(Env) DB             |        |  mysql  |\n               +---+-----+        +----------------+---+--------------+        +----+----+\n                   |                               |   |                            |\n                   |                               |   |                            |\n                   |                               |   |                            |\n    +--------------+-----+     +-------------------v---v-----------------+     +----+------+\n    | greeterWithLogging |     |  NewGreetService(Env, DB) GreetService  |     |  greeter  |\n    +--------------+-----+     +---------------------------+-------------+     +----+------+\n                   |                                       |                        |\n                   |                                       |                        |\n                   |           +-----------------------------------------+          |\n                   |           |  GreetController          |             |          |\n                   |           |                           |             |          |\n                   |           |  - Service GreetService <--             |          |\n                   |           |                                         |          |\n                   |           +-------------------+---------------------+          |\n                   |                               |                                |\n                   |                               |                                |\n                   |                               |                                |\n                   |                   +-----------+-----------+                    |\n                   |                   |      HTTP Request     |                    |\n                   |                   +-----------------------+                    |\n                   |                   |  /greet?name=kataras  |                    |\n                   |                   +-----------+-----------+                    |\n                   |                               |                                |\n+------------------+--------+         +------------+------------+           +-------+------------------+\n|  model.Response (JSON)    |         |  Response (JSON, error) |           |  Bad Request             |\n+---------------------------+         +-------------------------+           +--------------------------+\n|  {                        |                                               |  mysql: not implemented  |\n|    \"msg\": \"Hello kataras\" |                                               +--------------------------+\n|  }                        |\n+---------------------------+\n```\n\nNow it's the time to wrap all the above into our `main.go` file. Copy-paste the following code:\n\n```go\npackage main\n\nimport (\n\t\"app/controller\"\n\t\"app/database\"\n\t\"app/environment\"\n\t\"app/service\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/mvc\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.Get(\"/ping\", pong).Describe(\"healthcheck\")\n\n\tmvc.Configure(app.Party(\"/greet\"), setup)\n\n\t// http://localhost:8080/greet?name=kataras\n\tapp.Listen(\":8080\", iris.WithLogLevel(\"debug\"))\n}\n\nfunc pong(ctx iris.Context) {\n\tctx.WriteString(\"pong\")\n}\n\nfunc setup(app *mvc.Application) {\n\t// Register Dependencies.\n\tapp.Register(\n\t\tenvironment.DEV,         // DEV, PROD\n\t\tdatabase.NewDB,          // sqlite, mysql\n\t\tservice.NewGreetService, // greeterWithLogging, greeter\n\t)\n\n\t// Register Controllers.\n\tapp.Handle(new(controller.GreetController))\n}\n```\n\nThe `mvc.Application.Register` method registers one more dependencies, dependencies can depend on previously registered dependencies too. Thats the reason we pass, first, the `Environment(DEV)`, then the `NewDB` which depends on that `Environment`, following by the `NewGreetService` function which depends on both the `Environment(DEV)` and the `DB`.\n\nThe `mvc.Application.Handle` registers a new controller, which depends on the `GreetService`, for the targeted sub-router of `Party(\"/greet\")`.\n\n## Run\n\nInstall [Go](https://golang.org/dl) and run the application with:\n\n```sh\ngo run main.go\n```\n\n<details><summary>Docker</summary>\n\nDownload the [Dockerfile](https://raw.githubusercontent.com/kataras/iris/9b93c0dbb491dcedf49c91e89ca13bab884d116f/_examples/mvc/overview/Dockerfile) and [docker-compose.yml](https://raw.githubusercontent.com/kataras/iris/9b93c0dbb491dcedf49c91e89ca13bab884d116f/_examples/mvc/overview/docker-compose.yml) files to the `app` folder.\n\nInstall [Docker](https://www.docker.com/) and execute the following command:\n\n```sh\n$ docker-compose up\n```\n</details>\n\nVisit http://localhost:8080?name=kataras.\n\nOptionally, replace the `main.go`'s `app.Register(environment.DEV` with `environment.PROD`, restart the application and refresh. You will see that a new database (`sqlite`) and another service of (`greeterWithLogging`) will be binded to the `GreetController`. With **a single change** you achieve to completety change the result.\n"
  },
  {
    "path": "_examples/mvc/overview/controller/greet_controller.go",
    "content": "package controller\n\nimport (\n\t\"app/model\"\n\t\"app/service\"\n)\n\n// GreetController handles the index.\ntype GreetController struct {\n\tService service.GreetService\n\t// Ctx iris.Context\n}\n\n// Get serves [GET] /.\n// Query: name\nfunc (c *GreetController) Get(req model.Request) (model.Response, error) {\n\tmessage, err := c.Service.Say(req.Name)\n\tif err != nil {\n\t\treturn model.Response{}, err\n\t}\n\n\treturn model.Response{Message: message}, nil\n}\n"
  },
  {
    "path": "_examples/mvc/overview/database/database.go",
    "content": "package database\n\nimport \"app/environment\"\n\n// DB example database interface.\ntype DB interface {\n\tExec(q string) error\n}\n\n// NewDB returns a database based on \"env\".\nfunc NewDB(env environment.Env) DB {\n\tswitch env {\n\tcase environment.PROD:\n\t\treturn &mysql{}\n\tcase environment.DEV:\n\t\treturn &sqlite{}\n\tdefault:\n\t\tpanic(\"unknown environment\")\n\t}\n}\n"
  },
  {
    "path": "_examples/mvc/overview/database/mysql.go",
    "content": "package database\n\nimport \"fmt\"\n\ntype mysql struct{}\n\nfunc (db *mysql) Exec(q string) error {\n\t// simulate an error response.\n\treturn fmt.Errorf(\"mysql: not implemented <%s>\", q)\n}\n"
  },
  {
    "path": "_examples/mvc/overview/database/sqlite.go",
    "content": "package database\n\ntype sqlite struct{}\n\nfunc (db *sqlite) Exec(q string) error { return nil }\n"
  },
  {
    "path": "_examples/mvc/overview/docker-compose.yml",
    "content": "version: '3.1'\n\nservices:\n  app:\n    build: .\n    ports:\n      - 8080:8080\n    environment:\n      PORT: 8080\n      ENVIRONMENT: development\n    restart: on-failure\n    healthcheck:\n        test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8080/ping\"]\n        interval: 30s\n        timeout: 10s\n        retries: 5\n"
  },
  {
    "path": "_examples/mvc/overview/environment/environment.go",
    "content": "package environment\n\nimport (\n\t\"os\"\n\t\"strings\"\n)\n\n// Available environments example.\nconst (\n\tPROD Env = \"production\"\n\tDEV  Env = \"development\"\n)\n\n// Env is the environment type.\ntype Env string\n\n// String just returns the string representation of the Env.\nfunc (e Env) String() string {\n\treturn string(e)\n}\n\n// ReadEnv returns the environment of the system environment variable of \"key\".\n// Returns the \"def\" if not found.\n// Reports a panic message if the environment variable found\n// but the Env is unknown.\nfunc ReadEnv(key string, def Env) Env {\n\tv := Getenv(key, def.String())\n\tif v == \"\" {\n\t\treturn def\n\t}\n\n\tenv := Env(strings.ToLower(v))\n\tswitch env {\n\tcase PROD, DEV: // allowed.\n\tdefault:\n\t\tpanic(\"unexpected environment \" + v)\n\t}\n\n\treturn env\n}\n\n// Getenv returns the value of a system environment variable \"key\".\n// Defaults to \"def\" if not found.\nfunc Getenv(key string, def string) string {\n\tif v := os.Getenv(key); v != \"\" {\n\t\treturn v\n\t}\n\n\treturn def\n}\n"
  },
  {
    "path": "_examples/mvc/overview/go.mod",
    "content": "module app\n\ngo 1.25\n\nrequire github.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd\n\nrequire (\n\tgithub.com/BurntSushi/toml v1.6.0 // indirect\n\tgithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect\n\tgithub.com/CloudyKit/jet/v6 v6.3.1 // indirect\n\tgithub.com/Joker/jade v1.1.3 // indirect\n\tgithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 // indirect\n\tgithub.com/andybalholm/brotli v1.2.0 // indirect\n\tgithub.com/aymerick/douceur v0.2.0 // indirect\n\tgithub.com/blang/semver/v4 v4.0.0 // indirect\n\tgithub.com/fatih/structs v1.1.0 // indirect\n\tgithub.com/flosch/pongo2/v4 v4.0.2 // indirect\n\tgithub.com/gobwas/httphead v0.1.0 // indirect\n\tgithub.com/gobwas/pool v0.2.1 // indirect\n\tgithub.com/gobwas/ws v1.4.0 // indirect\n\tgithub.com/golang/snappy v1.0.0 // indirect\n\tgithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a // indirect\n\tgithub.com/google/uuid v1.6.0 // indirect\n\tgithub.com/gorilla/css v1.0.1 // indirect\n\tgithub.com/gorilla/websocket v1.5.3 // indirect\n\tgithub.com/iris-contrib/schema v0.0.6 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/kataras/blocks v0.0.8 // indirect\n\tgithub.com/kataras/golog v0.1.12 // indirect\n\tgithub.com/kataras/neffos v0.0.24 // indirect\n\tgithub.com/kataras/pio v0.0.13 // indirect\n\tgithub.com/kataras/sitemap v0.0.6 // indirect\n\tgithub.com/kataras/tunnel v0.0.4 // indirect\n\tgithub.com/klauspost/compress v1.18.2 // indirect\n\tgithub.com/kr/pretty v0.3.1 // indirect\n\tgithub.com/mailgun/raymond/v2 v2.0.48 // indirect\n\tgithub.com/mailru/easyjson v0.9.1 // indirect\n\tgithub.com/mediocregopher/radix/v3 v3.8.1 // indirect\n\tgithub.com/microcosm-cc/bluemonday v1.0.27 // indirect\n\tgithub.com/nats-io/nats.go v1.40.1 // indirect\n\tgithub.com/nats-io/nkeys v0.4.9 // indirect\n\tgithub.com/nats-io/nuid v1.0.1 // indirect\n\tgithub.com/rogpeppe/go-internal v1.10.0 // indirect\n\tgithub.com/russross/blackfriday/v2 v2.1.0 // indirect\n\tgithub.com/schollz/closestmatch v2.1.0+incompatible // indirect\n\tgithub.com/sirupsen/logrus v1.8.1 // indirect\n\tgithub.com/stretchr/testify v1.11.1 // indirect\n\tgithub.com/tdewolff/minify/v2 v2.24.8 // indirect\n\tgithub.com/tdewolff/parse/v2 v2.8.5 // indirect\n\tgithub.com/valyala/bytebufferpool v1.0.0 // indirect\n\tgithub.com/vmihailenco/msgpack/v5 v5.4.1 // indirect\n\tgithub.com/vmihailenco/tagparser/v2 v2.0.0 // indirect\n\tgithub.com/yosssi/ace v0.0.5 // indirect\n\tgolang.org/x/crypto v0.47.0 // indirect\n\tgolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 // indirect\n\tgolang.org/x/net v0.49.0 // indirect\n\tgolang.org/x/sys v0.40.0 // indirect\n\tgolang.org/x/text v0.33.0 // indirect\n\tgolang.org/x/time v0.14.0 // indirect\n\tgolang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect\n\tgoogle.golang.org/protobuf v1.36.11 // indirect\n\tgopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect\n\tgopkg.in/ini.v1 v1.67.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n"
  },
  {
    "path": "_examples/mvc/overview/go.sum",
    "content": "github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=\ngithub.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=\ngithub.com/CloudyKit/jet/v6 v6.3.1 h1:6IAo5Cx21xrHVaR8zzXN5gJatKV/wO7Nf6bfCnCSbUw=\ngithub.com/CloudyKit/jet/v6 v6.3.1/go.mod h1:lf8ksdNsxZt7/yH/3n4vJQWA9RUq4wpaHtArHhGVMOw=\ngithub.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc=\ngithub.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=\ngithub.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=\ngithub.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 h1:dG7/gLroJGht/jSQtHiLvT48Hxn+crbmvyItZC8cWOs=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05/go.mod h1:NYezi6wtnJtBm5btoprXc5SvAdqH0XTXWnUup0MptAI=\ngithub.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=\ngithub.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=\ngithub.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=\ngithub.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=\ngithub.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=\ngithub.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=\ngithub.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=\ngithub.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=\ngithub.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=\ngithub.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=\ngithub.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=\ngithub.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw=\ngithub.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8=\ngithub.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=\ngithub.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=\ngithub.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=\ngithub.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=\ngithub.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=\ngithub.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=\ngithub.com/gobwas/ws v1.4.0 h1:CTaoG1tojrh4ucGPcoJFiAQUAsEWekEWvLy7GsVNqGs=\ngithub.com/gobwas/ws v1.4.0/go.mod h1:G3gNqMNtPppf5XUz7O4shetPpcZ1VJ7zt18dlUeakrc=\ngithub.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=\ngithub.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a h1:l7A0loSszR5zHd/qK53ZIHMO8b3bBSmENnQ6eKnUT0A=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=\ngithub.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=\ngithub.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=\ngithub.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=\ngithub.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=\ngithub.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2 h1:T9THsdP1woyAqKHwjkEsbCnMefsAFvk8iJJKokcJ3Go=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2/go.mod h1:JLDgIqnFy5loDSUv1OA2j0mb6p/rDhiCqigP22Uq9xE=\ngithub.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw=\ngithub.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/kataras/blocks v0.0.8 h1:MrpVhoFTCR2v1iOOfGng5VJSILKeZZI+7NGfxEh3SUM=\ngithub.com/kataras/blocks v0.0.8/go.mod h1:9Jm5zx6BB+06NwA+OhTbHW1xkMOYxahnqTN5DveZ2Yg=\ngithub.com/kataras/golog v0.1.12 h1:Bu7I/G4ilJlbfzjmU39O9N+2uO1pBcMK045fzZ4ytNg=\ngithub.com/kataras/golog v0.1.12/go.mod h1:wrGSbOiBqbQSQznleVNX4epWM8rl9SJ/rmEacl0yqy4=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd h1:oMsQgqKACbUdlaIWnN+EZzzAnHkw90hE/y/R0BSij2U=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd/go.mod h1:yucjtDl9Vn3OctWM1fi0MuM9OckemC7kEreFa9QtPF8=\ngithub.com/kataras/neffos v0.0.24 h1:S3lHqJopCfXN285VdlbGeOj+Id83u4xdQKToa+w1vW0=\ngithub.com/kataras/neffos v0.0.24/go.mod h1:/3K9zQ0yEC5/xUiSQx46ToWa3xneGfUo/nMit/F5g+U=\ngithub.com/kataras/pio v0.0.13 h1:x0rXVX0fviDTXOOLOmr4MUxOabu1InVSTu5itF8CXCM=\ngithub.com/kataras/pio v0.0.13/go.mod h1:k3HNuSw+eJ8Pm2lA4lRhg3DiCjVgHlP8hmXApSej3oM=\ngithub.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY=\ngithub.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4=\ngithub.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA=\ngithub.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw=\ngithub.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=\ngithub.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=\ngithub.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=\ngithub.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8=\ngithub.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=\ngithub.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=\ngithub.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=\ngithub.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=\ngithub.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/mediocregopher/radix/v3 v3.8.1 h1:rOkHflVuulFKlwsLY01/M2cM2tWCjDoETcMqKbAWu1M=\ngithub.com/mediocregopher/radix/v3 v3.8.1/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8=\ngithub.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=\ngithub.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=\ngithub.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=\ngithub.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=\ngithub.com/nats-io/nats.go v1.40.1 h1:MLjDkdsbGUeCMKFyCFoLnNn/HDTqcgVa3EQm+pMNDPk=\ngithub.com/nats-io/nats.go v1.40.1/go.mod h1:wV73x0FSI/orHPSYoyMeJB+KajMDoWyXmFaRrrYaaTo=\ngithub.com/nats-io/nkeys v0.4.9 h1:qe9Faq2Gxwi6RZnZMXfmGMZkg3afLLOtrU+gDZJ35b0=\ngithub.com/nats-io/nkeys v0.4.9/go.mod h1:jcMqs+FLG+W5YO36OX6wFIFcmpdAns+w1Wm6D3I/evE=\ngithub.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=\ngithub.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=\ngithub.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=\ngithub.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=\ngithub.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=\ngithub.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=\ngithub.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk=\ngithub.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=\ngithub.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=\ngithub.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=\ngithub.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=\ngithub.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/tdewolff/minify/v2 v2.24.8 h1:58/VjsbevI4d5FGV0ZSuBrHMSSkH4MCH0sIz/eKIauE=\ngithub.com/tdewolff/minify/v2 v2.24.8/go.mod h1:0Ukj0CRpo/sW/nd8uZ4ccXaV1rEVIWA3dj8U7+Shhfw=\ngithub.com/tdewolff/parse/v2 v2.8.5 h1:ZmBiA/8Do5Rpk7bDye0jbbDUpXXbCdc3iah4VeUvwYU=\ngithub.com/tdewolff/parse/v2 v2.8.5/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=\ngithub.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE=\ngithub.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=\ngithub.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=\ngithub.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=\ngithub.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=\ngithub.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=\ngithub.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=\ngithub.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=\ngithub.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA=\ngithub.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0=\ngithub.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=\ngithub.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=\ngithub.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=\ngolang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=\ngolang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=\ngolang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=\ngolang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=\ngolang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=\ngolang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=\ngolang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=\ngolang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=\ngolang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY=\ngolang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=\ngoogle.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=\ngoogle.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=\ngopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nmoul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs=\nmoul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE=\n"
  },
  {
    "path": "_examples/mvc/overview/main.go",
    "content": "package main\n\nimport (\n\t\"app/controller\"\n\t\"app/database\"\n\t\"app/environment\"\n\t\"app/service\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/mvc\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.Logger().SetLevel(\"debug\")\n\n\tapp.Get(\"/ping\", pong).Describe(\"healthcheck\")\n\n\tmvc.Configure(app.Party(\"/greet\"), setup)\n\n\t// http://localhost:8080/greet?name=kataras\n\taddr := \":\" + environment.Getenv(\"PORT\", \"8080\")\n\tapp.Listen(addr)\n}\n\nfunc pong(ctx iris.Context) {\n\tctx.WriteString(\"pong\")\n}\n\n/*\n\t     +-------------------+\n\t     |  Env (DEV, PROD)  |\n\t     +---------+---------+\n\t     |         |         |\n\t     |         |         |\n\t     |         |         |\n\tDEV  |         |         |  PROD\n\n-------------------+---------------------+         |         +----------------------+-------------------\n\n\t               |                               |                                |\n\t               |                               |                                |\n\t           +---+-----+        +----------------v------------------+        +----+----+\n\t           | sqlite  |        |         NewDB(Env) DB             |        |  mysql  |\n\t           +---+-----+        +----------------+---+--------------+        +----+----+\n\t               |                               |   |                            |\n\t               |                               |   |                            |\n\t               |                               |   |                            |\n\t+--------------+-----+     +-------------------v---v-----------------+     +----+------+\n\t| greeterWithLogging |     |  NewGreetService(Env, DB) GreetService  |     |  greeter  |\n\t+--------------+-----+     +---------------------------+-------------+     +----+------+\n\t               |                                       |                        |\n\t               |                                       |                        |\n\t               |           +-----------------------------------------+          |\n\t               |           |  GreetController          |             |          |\n\t               |           |                           |             |          |\n\t               |           |  - Service GreetService <--             |          |\n\t               |           |                                         |          |\n\t               |           +-------------------+---------------------+          |\n\t               |                               |                                |\n\t               |                               |                                |\n\t               |                               |                                |\n\t               |                   +-----------+-----------+                    |\n\t               |                   |      HTTP Request     |                    |\n\t               |                   +-----------------------+                    |\n\t               |                   |  /greet?name=kataras  |                    |\n\t               |                   +-----------+-----------+                    |\n\t               |                               |                                |\n\n+------------------+--------+         +------------+------------+           +-------+------------------+\n|  model.Response (JSON)    |         |  Response (JSON, error) |           |  Bad Request             |\n+---------------------------+         +-------------------------+           +--------------------------+\n|  {                        |                                               |  mysql: not implemented  |\n|    \"msg\": \"Hello kataras\" |                                               +--------------------------+\n|  }                        |\n+---------------------------+\n*/\nfunc setup(app *mvc.Application) {\n\t// Register Dependencies.\n\t// Tip: A dependency can depend on other dependencies too.\n\tenv := environment.ReadEnv(\"ENVIRONMENT\", environment.DEV)\n\tapp.Register(\n\t\tenv,                     // DEV, PROD\n\t\tdatabase.NewDB,          // sqlite, mysql\n\t\tservice.NewGreetService, // greeterWithLogging, greeter\n\t)\n\n\t// Register Controllers.\n\tapp.Handle(new(controller.GreetController))\n}\n"
  },
  {
    "path": "_examples/mvc/overview/model/request.go",
    "content": "package model\n\n// Request example incoming request.\ntype Request struct {\n\tName string `json:\"name\" url:\"name\"`\n}\n"
  },
  {
    "path": "_examples/mvc/overview/model/response.go",
    "content": "package model\n\n// Response example server's response.\ntype Response struct {\n\tMessage string `json:\"msg\"`\n}\n"
  },
  {
    "path": "_examples/mvc/overview/service/greet_service.go",
    "content": "package service\n\nimport (\n\t\"fmt\"\n\n\t\"app/database\"\n\t\"app/environment\"\n)\n\n// GreetService example service.\ntype GreetService interface {\n\tSay(input string) (string, error)\n}\n\n// NewGreetService returns a service backed with a \"db\" based on \"env\".\nfunc NewGreetService(env environment.Env, db database.DB) GreetService {\n\tservice := &greeter{db: db, prefix: \"Hello\"}\n\n\tswitch env {\n\tcase environment.PROD:\n\t\treturn service\n\tcase environment.DEV:\n\t\treturn &greeterWithLogging{service}\n\tdefault:\n\t\tpanic(\"unknown environment\")\n\t}\n}\n\ntype greeter struct {\n\tprefix string\n\tdb     database.DB\n}\n\nfunc (s *greeter) Say(input string) (string, error) {\n\tif err := s.db.Exec(\"simulate a query...\"); err != nil {\n\t\treturn \"\", err\n\t}\n\n\tresult := s.prefix + \" \" + input\n\treturn result, nil\n}\n\ntype greeterWithLogging struct {\n\t*greeter\n}\n\nfunc (s *greeterWithLogging) Say(input string) (string, error) {\n\tresult, err := s.greeter.Say(input)\n\tfmt.Printf(\"result: %s\\nerror: %v\\n\", result, err)\n\treturn result, err\n}\n"
  },
  {
    "path": "_examples/mvc/regexp/main.go",
    "content": "// Package main shows how to match \"/xxx.json\" in MVC handler.\npackage main\n\n/*\nThere is no MVC naming pattern for such these things,you can imagine the limitations of that.\nInstead you can use the `BeforeActivation` on your controller to add more advanced routing features\n(https://github.com/kataras/iris/tree/main/_examples/routing).\n\nYou can also create your own macro,\ni.e: /{file:json} or macro function of a specific parameter type i.e: (/{file:string json()}).\nRead the routing examples and you will gain a deeper view, there are all covered.\n*/\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/mvc\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\tmvcApp := mvc.New(app.Party(\"/module\"))\n\tmvcApp.Handle(new(myController))\n\n\t// http://localhost:8080/module/xxx.json (OK)\n\t// http://localhost:8080/module/xxx.xml  (Not Found)\n\tapp.Listen(\":8080\")\n}\n\ntype myController struct{}\n\nfunc (m *myController) BeforeActivation(b mvc.BeforeActivation) {\n\t// b.Dependencies().Register\n\t// b.Router().Use/UseGlobal/Done // and any standard API call you already know\n\n\t// 1-> Method\n\t// 2-> Path\n\t// 3-> The controller's function name to be parsed as handler\n\t// 4-> Any handlers that should run before the HandleJSON\n\n\t// \"^[a-zA-Z0-9_.-]+.json$)\" to validate file-name pattern and json\n\t// or just:  \".json$\" to validate suffix.\n\n\tb.Handle(\"GET\", \"/{file:string regexp(^[a-zA-Z0-9_.-]+.json$))}\", \"HandleJSON\" /*optionalMiddleware*/)\n}\n\nfunc (m *myController) HandleJSON(file string) string {\n\treturn \"custom serving of json: \" + file\n}\n"
  },
  {
    "path": "_examples/mvc/repository/datamodels/README.md",
    "content": "# Data Model Layer"
  },
  {
    "path": "_examples/mvc/repository/datamodels/movie.go",
    "content": "// file: datamodels/movie.go\n\npackage datamodels\n\n// Movie is our sample data structure.\n// Keep note that the tags for public-use (for our web app)\n// should be kept in other file like \"web/viewmodels/movie.go\"\n// which could wrap by embedding the datamodels.Movie or\n// declare new fields instead butwe will use this datamodel\n// as the only one Movie model in our application,\n// for the sake of simplicty.\ntype Movie struct {\n\tID     int64  `json:\"id\"`\n\tName   string `json:\"name\"`\n\tYear   int    `json:\"year\"`\n\tGenre  string `json:\"genre\"`\n\tPoster string `json:\"poster\"`\n}\n"
  },
  {
    "path": "_examples/mvc/repository/datasource/README.md",
    "content": "# Data Source / Data Store Layer"
  },
  {
    "path": "_examples/mvc/repository/datasource/movies.go",
    "content": "// file: datasource/movies.go\n\npackage datasource\n\nimport \"github.com/kataras/iris/v12/_examples/mvc/repository/datamodels\"\n\n// Movies is our imaginary data source.\nvar Movies = map[int64]datamodels.Movie{\n\t1: {\n\t\tID:     1,\n\t\tName:   \"Casablanca\",\n\t\tYear:   1942,\n\t\tGenre:  \"Romance\",\n\t\tPoster: \"https://iris-go.com/static/images/examples/mvc-movies/1.jpg\",\n\t},\n\t2: {\n\t\tID:     2,\n\t\tName:   \"Gone with the Wind\",\n\t\tYear:   1939,\n\t\tGenre:  \"Romance\",\n\t\tPoster: \"https://iris-go.com/static/images/examples/mvc-movies/2.jpg\",\n\t},\n\t3: {\n\t\tID:     3,\n\t\tName:   \"Citizen Kane\",\n\t\tYear:   1941,\n\t\tGenre:  \"Mystery\",\n\t\tPoster: \"https://iris-go.com/static/images/examples/mvc-movies/3.jpg\",\n\t},\n\t4: {\n\t\tID:     4,\n\t\tName:   \"The Wizard of Oz\",\n\t\tYear:   1939,\n\t\tGenre:  \"Fantasy\",\n\t\tPoster: \"https://iris-go.com/static/images/examples/mvc-movies/4.jpg\",\n\t},\n\t5: {\n\t\tID:     5,\n\t\tName:   \"North by Northwest\",\n\t\tYear:   1959,\n\t\tGenre:  \"Thriller\",\n\t\tPoster: \"https://iris-go.com/static/images/examples/mvc-movies/5.jpg\",\n\t},\n}\n"
  },
  {
    "path": "_examples/mvc/repository/main.go",
    "content": "// file: main.go\n\npackage main\n\nimport (\n\t\"github.com/kataras/iris/v12/_examples/mvc/repository/datasource\"\n\t\"github.com/kataras/iris/v12/_examples/mvc/repository/repositories\"\n\t\"github.com/kataras/iris/v12/_examples/mvc/repository/services\"\n\t\"github.com/kataras/iris/v12/_examples/mvc/repository/web/controllers\"\n\t\"github.com/kataras/iris/v12/_examples/mvc/repository/web/middleware\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/mvc\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.Logger().SetLevel(\"debug\")\n\n\t// Load the template files.\n\tapp.RegisterView(iris.HTML(\"./web/views\", \".html\"))\n\n\t// Serve our controllers.\n\tmvc.New(app.Party(\"/hello\")).Handle(new(controllers.HelloController))\n\t// You can also split the code you write to configure an mvc.Application\n\t// using the `mvc.Configure` method, as shown below.\n\tmvc.Configure(app.Party(\"/movies\"), movies)\n\n\t// http://localhost:8080/hello\n\t// http://localhost:8080/hello/iris\n\t// http://localhost:8080/movies\n\t// http://localhost:8080/movies/1\n\tapp.Listen(\":8080\", iris.WithOptimizations)\n}\n\n// note the mvc.Application, it's not iris.Application.\nfunc movies(app *mvc.Application) {\n\t// Add the basic authentication(admin:password) middleware\n\t// for the /movies based requests.\n\tapp.Router.Use(middleware.BasicAuth)\n\n\t// Create our movie repository with some (memory) data from the datasource.\n\trepo := repositories.NewMovieRepository(datasource.Movies)\n\t// Create our movie service, we will bind it to the movie app's dependencies.\n\tmovieService := services.NewMovieService(repo)\n\tapp.Register(movieService)\n\n\t// serve our movies controller.\n\t// Note that you can serve more than one controller\n\t// you can also create child mvc apps using the `movies.Party(relativePath)` or `movies.Clone(app.Party(...))`\n\t// if you want.\n\tapp.Handle(new(controllers.MovieController))\n}\n"
  },
  {
    "path": "_examples/mvc/repository/models/README.md",
    "content": "# Domain Models\n\nThere should be the domain/business-level models.\n\nExample:\n\n```go\nimport \"github.com/kataras/iris/v12/_examples/mvc/repository/datamodels\"\n\ntype Movie struct {\n    datamodels.Movie\n}\n\nfunc (m Movie) Validate() (Movie, error) {\n    /* do some checks and return an error if that Movie is not valid */\n}\n```\n\nHowever, we will use the \"datamodels\" as the only one models package because\nMovie structure we don't need any extra functionality or validation inside it."
  },
  {
    "path": "_examples/mvc/repository/repositories/README.md",
    "content": "# Repositories\n\nThe package which has direct access to the \"datasource\" and can manipulate data directly."
  },
  {
    "path": "_examples/mvc/repository/repositories/movie_repository.go",
    "content": "// file: repositories/movie_repository.go\n\npackage repositories\n\nimport (\n\t\"errors\"\n\t\"sync\"\n\n\t\"github.com/kataras/iris/v12/_examples/mvc/repository/datamodels\"\n)\n\n// Query represents the visitor and action queries.\ntype Query func(datamodels.Movie) bool\n\n// MovieRepository handles the basic operations of a movie entity/model.\n// It's an interface in order to be testable, i.e a memory movie repository or\n// a connected to an sql database.\ntype MovieRepository interface {\n\tExec(query Query, action Query, limit int, mode int) (ok bool)\n\n\tSelect(query Query) (movie datamodels.Movie, found bool)\n\tSelectMany(query Query, limit int) (results []datamodels.Movie)\n\n\tInsertOrUpdate(movie datamodels.Movie) (updatedMovie datamodels.Movie, err error)\n\tDelete(query Query, limit int) (deleted bool)\n}\n\n// NewMovieRepository returns a new movie memory-based repository,\n// the one and only repository type in our example.\nfunc NewMovieRepository(source map[int64]datamodels.Movie) MovieRepository {\n\treturn &movieMemoryRepository{source: source}\n}\n\n// movieMemoryRepository is a \"MovieRepository\"\n// which manages the movies using the memory data source (map).\ntype movieMemoryRepository struct {\n\tsource map[int64]datamodels.Movie\n\tmu     sync.RWMutex\n}\n\nconst (\n\t// ReadOnlyMode will RLock(read) the data .\n\tReadOnlyMode = iota\n\t// ReadWriteMode will Lock(read/write) the data.\n\tReadWriteMode\n)\n\nfunc (r *movieMemoryRepository) Exec(query Query, action Query, actionLimit int, mode int) (ok bool) {\n\tloops := 0\n\n\tif mode == ReadOnlyMode {\n\t\tr.mu.RLock()\n\t\tdefer r.mu.RUnlock()\n\t} else {\n\t\tr.mu.Lock()\n\t\tdefer r.mu.Unlock()\n\t}\n\n\tfor _, movie := range r.source {\n\t\tok = query(movie)\n\t\tif ok {\n\t\t\tif action(movie) {\n\t\t\t\tloops++\n\t\t\t\tif actionLimit >= loops {\n\t\t\t\t\tbreak // break\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn\n}\n\n// Select receives a query function\n// which is fired for every single movie model inside\n// our imaginary data source.\n// When that function returns true then it stops the iteration.\n//\n// It returns the query's return last known \"found\" value\n// and the last known movie model\n// to help callers to reduce the LOC.\n//\n// It's actually a simple but very clever prototype function\n// I'm using everywhere since I firstly think of it,\n// hope you'll find it very useful as well.\nfunc (r *movieMemoryRepository) Select(query Query) (movie datamodels.Movie, found bool) {\n\tfound = r.Exec(query, func(m datamodels.Movie) bool {\n\t\tmovie = m\n\t\treturn true\n\t}, 1, ReadOnlyMode)\n\n\t// set an empty datamodels.Movie if not found at all.\n\tif !found {\n\t\tmovie = datamodels.Movie{}\n\t}\n\n\treturn\n}\n\n// SelectMany same as Select but returns one or more datamodels.Movie as a slice.\n// If limit <=0 then it returns everything.\nfunc (r *movieMemoryRepository) SelectMany(query Query, limit int) (results []datamodels.Movie) {\n\tr.Exec(query, func(m datamodels.Movie) bool {\n\t\tresults = append(results, m)\n\t\treturn true\n\t}, limit, ReadOnlyMode)\n\n\treturn\n}\n\n// InsertOrUpdate adds or updates a movie to the (memory) storage.\n//\n// Returns the new movie and an error if any.\nfunc (r *movieMemoryRepository) InsertOrUpdate(movie datamodels.Movie) (datamodels.Movie, error) {\n\tid := movie.ID\n\n\tif id == 0 { // Create new action\n\t\tvar lastID int64\n\t\t// find the biggest ID in order to not have duplications\n\t\t// in productions apps you can use a third-party\n\t\t// library to generate a UUID as string.\n\t\tr.mu.RLock()\n\t\tfor _, item := range r.source {\n\t\t\tif item.ID > lastID {\n\t\t\t\tlastID = item.ID\n\t\t\t}\n\t\t}\n\t\tr.mu.RUnlock()\n\n\t\tid = lastID + 1\n\t\tmovie.ID = id\n\n\t\t// map-specific thing\n\t\tr.mu.Lock()\n\t\tr.source[id] = movie\n\t\tr.mu.Unlock()\n\n\t\treturn movie, nil\n\t}\n\n\t// Update action based on the movie.ID,\n\t// here we will allow updating the poster and genre if not empty.\n\t// Alternatively we could do pure replace instead:\n\t// r.source[id] = movie\n\t// and comment the code below;\n\tcurrent, exists := r.Select(func(m datamodels.Movie) bool {\n\t\treturn m.ID == id\n\t})\n\n\tif !exists { // ID is not a real one, return an error.\n\t\treturn datamodels.Movie{}, errors.New(\"failed to update a nonexistent movie\")\n\t}\n\n\t// or comment these and r.source[id] = m for pure replace\n\tif movie.Poster != \"\" {\n\t\tcurrent.Poster = movie.Poster\n\t}\n\n\tif movie.Genre != \"\" {\n\t\tcurrent.Genre = movie.Genre\n\t}\n\n\t// map-specific thing\n\tr.mu.Lock()\n\tr.source[id] = current\n\tr.mu.Unlock()\n\n\treturn movie, nil\n}\n\nfunc (r *movieMemoryRepository) Delete(query Query, limit int) bool {\n\treturn r.Exec(query, func(m datamodels.Movie) bool {\n\t\tdelete(r.source, m.ID)\n\t\treturn true\n\t}, limit, ReadWriteMode)\n}\n"
  },
  {
    "path": "_examples/mvc/repository/services/README.md",
    "content": "# Service Layer\n\nThe package which has access to call functions from the \"repositories\" and \"models\" (\"datamodels\" only in that simple example). It should contain the domain logic."
  },
  {
    "path": "_examples/mvc/repository/services/movie_service.go",
    "content": "// file: services/movie_service.go\n\npackage services\n\nimport (\n\t\"github.com/kataras/iris/v12/_examples/mvc/repository/datamodels\"\n\t\"github.com/kataras/iris/v12/_examples/mvc/repository/repositories\"\n)\n\n// MovieService handles some of the CRUID operations of the movie datamodel.\n// It depends on a movie repository for its actions.\n// It's here to decouple the data source from the higher level compoments.\n// As a result a different repository type can be used with the same logic without any aditional changes.\n// It's an interface and it's used as interface everywhere\n// because we may need to change or try an experimental different domain logic at the future.\ntype MovieService interface {\n\tGetAll() []datamodels.Movie\n\tGetByID(id int64) (datamodels.Movie, bool)\n\tDeleteByID(id int64) bool\n\tUpdatePosterAndGenreByID(id int64, poster string, genre string) (datamodels.Movie, error)\n}\n\n// NewMovieService returns the default movie service.\nfunc NewMovieService(repo repositories.MovieRepository) MovieService {\n\treturn &movieService{\n\t\trepo: repo,\n\t}\n}\n\ntype movieService struct {\n\trepo repositories.MovieRepository\n}\n\n// GetAll returns all movies.\nfunc (s *movieService) GetAll() []datamodels.Movie {\n\treturn s.repo.SelectMany(func(_ datamodels.Movie) bool {\n\t\treturn true\n\t}, -1)\n}\n\n// GetByID returns a movie based on its id.\nfunc (s *movieService) GetByID(id int64) (datamodels.Movie, bool) {\n\treturn s.repo.Select(func(m datamodels.Movie) bool {\n\t\treturn m.ID == id\n\t})\n}\n\n// UpdatePosterAndGenreByID updates a movie's poster and genre.\nfunc (s *movieService) UpdatePosterAndGenreByID(id int64, poster string, genre string) (datamodels.Movie, error) {\n\t// update the movie and return it.\n\treturn s.repo.InsertOrUpdate(datamodels.Movie{\n\t\tID:     id,\n\t\tPoster: poster,\n\t\tGenre:  genre,\n\t})\n}\n\n// DeleteByID deletes a movie by its id.\n//\n// Returns true if deleted otherwise false.\nfunc (s *movieService) DeleteByID(id int64) bool {\n\treturn s.repo.Delete(func(m datamodels.Movie) bool {\n\t\treturn m.ID == id\n\t}, 1)\n}\n"
  },
  {
    "path": "_examples/mvc/repository/web/controllers/hello_controller.go",
    "content": "// file: web/controllers/hello_controller.go\n\npackage controllers\n\nimport (\n\t\"errors\"\n\n\t\"github.com/kataras/iris/v12/mvc\"\n)\n\n// HelloController is our sample controller\n// it handles GET: /hello and GET: /hello/{name}\ntype HelloController struct{}\n\nvar helloView = mvc.View{\n\tName: \"hello/index.html\",\n\tData: map[string]any{\n\t\t\"Title\":     \"Hello Page\",\n\t\t\"MyMessage\": \"Welcome to my awesome website\",\n\t},\n}\n\n// Get will return a predefined view with bind data.\n//\n// `mvc.Result` is just an interface with a `Dispatch` function.\n// `mvc.Response` and `mvc.View` are the builtin result type dispatchers\n// you can even create custom response dispatchers by\n// implementing the `github.com/kataras/iris/hero#Result` interface.\nfunc (c *HelloController) Get() mvc.Result {\n\treturn helloView\n}\n\n// you can define a standard error in order to re-use anywhere in your app.\nvar errBadName = errors.New(\"bad name\")\n\n// you can just return it as error or even better\n// wrap this error with an mvc.Response to make it an mvc.Result compatible type.\nvar badName = mvc.Response{Err: errBadName, Code: 400}\n\n// GetBy returns a \"Hello {name}\" response.\n// Demos:\n// curl -i http://localhost:8080/hello/iris\n// curl -i http://localhost:8080/hello/anything\nfunc (c *HelloController) GetBy(name string) mvc.Result {\n\tif name != \"iris\" {\n\t\treturn badName\n\t\t// or\n\t\t// GetBy(name string) (mvc.Result, error) {\n\t\t//\treturn nil, errBadName\n\t\t// }\n\t}\n\n\t// return mvc.Response{Text: \"Hello \" + name} OR:\n\treturn mvc.View{\n\t\tName: \"hello/name.html\",\n\t\tData: name,\n\t}\n}\n"
  },
  {
    "path": "_examples/mvc/repository/web/controllers/movie_controller.go",
    "content": "// file: web/controllers/movie_controller.go\n\npackage controllers\n\nimport (\n\t\"errors\"\n\n\t\"github.com/kataras/iris/v12/_examples/mvc/repository/datamodels\"\n\t\"github.com/kataras/iris/v12/_examples/mvc/repository/services\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\n// MovieController is our /movies controller.\ntype MovieController struct {\n\t// Our MovieService, it's an interface which\n\t// is binded from the main application.\n\tService services.MovieService\n}\n\n// Get returns list of the movies.\n// Demo:\n// curl -i http://localhost:8080/movies\n//\n// The correct way if you have sensitive data:\n//\n//\tfunc (c *MovieController) Get() (results []viewmodels.Movie) {\n//\t\tdata := c.Service.GetAll()\n//\n//\t\tfor _, movie := range data {\n//\t\t\tresults = append(results, viewmodels.Movie{movie})\n//\t\t}\n//\t\treturn\n//\t}\n//\n// otherwise just return the datamodels.\nfunc (c *MovieController) Get() (results []datamodels.Movie) {\n\treturn c.Service.GetAll()\n}\n\n// GetBy returns a movie.\n// Demo:\n// curl -i http://localhost:8080/movies/1\nfunc (c *MovieController) GetBy(id int64) (movie datamodels.Movie, found bool) {\n\treturn c.Service.GetByID(id) // it will throw 404 if not found.\n}\n\n// PutBy updates a movie.\n// Demo:\n// curl -i -X PUT -F \"genre=Thriller\" -F \"poster=@/Users/kataras/Downloads/out.gif\" http://localhost:8080/movies/1\nfunc (c *MovieController) PutBy(ctx iris.Context, id int64) (datamodels.Movie, error) {\n\t// get the request data for poster and genre\n\tfile, info, err := ctx.FormFile(\"poster\")\n\tif err != nil {\n\t\treturn datamodels.Movie{}, errors.New(\"failed due form file 'poster' missing\")\n\t}\n\t// we don't need the file so close it now.\n\tfile.Close()\n\n\t// imagine that is the url of the uploaded file...\n\tposter := info.Filename\n\tgenre := ctx.FormValue(\"genre\")\n\n\treturn c.Service.UpdatePosterAndGenreByID(id, poster, genre)\n}\n\n// DeleteBy deletes a movie.\n// Demo:\n// curl -i -X DELETE -u admin:password http://localhost:8080/movies/1\nfunc (c *MovieController) DeleteBy(id int64) any {\n\twasDel := c.Service.DeleteByID(id)\n\tif wasDel {\n\t\t// return the deleted movie's ID\n\t\treturn iris.Map{\"deleted\": id}\n\t}\n\t// right here we can see that a method function can return any of those two types(map or int),\n\t// we don't have to specify the return type to a specific type.\n\treturn iris.StatusBadRequest\n}\n"
  },
  {
    "path": "_examples/mvc/repository/web/middleware/basicauth.go",
    "content": "// file: web/middleware/basicauth.go\n\npackage middleware\n\nimport \"github.com/kataras/iris/v12/middleware/basicauth\"\n\n// BasicAuth middleware sample.\nvar BasicAuth = basicauth.Default(map[string]string{\n\t\"admin\": \"password\",\n})\n"
  },
  {
    "path": "_examples/mvc/repository/web/viewmodels/README.md",
    "content": "# View Models\n\nThere should be the view models, the structure that the client will be able to see.\n\nExample:\n\n```go\nimport (\n    \"github.com/kataras/iris/v12/_examples/mvc/repository/datamodels\"\n\n    \"github.com/kataras/iris/v12\"\n    \"github.com/kataras/iris/v12/context\"\n)\n\ntype Movie struct {\n    datamodels.Movie\n}\n\nfunc (m Movie) IsValid() bool {\n    /* do some checks and return true if it's valid... */\n    return m.ID > 0\n}\n```\n\nIris is able to convert any custom data Structure into an HTTP Response Dispatcher,\nso theoretically, something like the following is permitted if it's really necessary;\n\n```go\n// Dispatch completes the `kataras/iris/mvc#Result` interface.\n// Sends a `Movie` as a controlled http response.\n// If its ID is zero or less then it returns a 404 not found error\n// else it returns its json representation,\n// (just like the controller's functions do for custom types by default).\n//\n// Don't overdo it, the application's logic should not be here.\n// It's just one more step of validation before the response,\n// simple checks can be added here.\n//\n// It's just a showcase,\n// imagine the potentials this feature gives when designing a bigger application.\n//\n// This is called where the return value from a controller's method functions\n// is type of `Movie`.\n// For example the `controllers/movie_controller.go#GetBy`.\nfunc (m Movie) Dispatch(ctx iris.Context) {\n    if !m.IsValid() {\n        ctx.NotFound()\n        return\n    }\n    ctx.JSON(m, context.JSON{Indent: \" \"})\n}\n```\n\nHowever, we will use the \"datamodels\" as the only one models package because\nMovie structure doesn't contain any sensitive data, clients are able to see all of its fields\nand we don't need any extra functionality or validation inside it."
  },
  {
    "path": "_examples/mvc/repository/web/views/hello/index.html",
    "content": "<!-- file: web/views/hello/index.html -->\n<html>\n\n<head>\n    <title>{{.Title}} - My App</title>\n</head>\n\n<body>\n    <p>{{.MyMessage}}</p>\n</body>\n\n</html>"
  },
  {
    "path": "_examples/mvc/repository/web/views/hello/name.html",
    "content": "<!-- file: web/views/hello/name.html -->\n<html>\n\n<head>\n    <title>{{.}}' Portfolio - My App</title>\n</head>\n\n<body>\n    <h1>Hello {{.}}</h1>\n</body>\n\n</html>"
  },
  {
    "path": "_examples/mvc/request-default-values/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/mvc\"\n)\n\n// https://github.com/kataras/iris/issues/1706\n// When you need that type of logic behind a request input,\n// e.g. set default values, the right way to do that is\n// to register a request-scoped dependency for that type.\n// We have the `Context.ReadQuery(pointer)`\n// which you can use to bind a struct value, that value can have default values.\n// Here is how you could do that:\nfunc main() {\n\tapp := iris.New()\n\n\tmvcApp := mvc.New(app)\n\t{\n\t\tmvcApp.Register(paramsDependency)\n\n\t\tmvcApp.Handle(new(controller))\n\t}\n\n\t// http://localhost:8080/records?phone=random&order_by=DESC\n\t// http://localhost:8080/records?phone=random\n\tapp.Listen(\":8080\")\n}\n\ntype params struct {\n\tCallID     string `url:\"phone\"`\n\tComDir     int    `url:\"dir\"`\n\tCaseUserID string `url:\"on\"`\n\tStartYear  int    `url:\"sy\"`\n\tEndYear    int    `url:\"ey\"`\n\tOrderBy    string `url:\"order_by\"`\n\tOffset     int    `url:\"offset\"`\n}\n\n// As we've read in the previous examples, the paramsDependency\n// describes a request-scoped dependency.\n// It should accept the iris context (or any previously registered or builtin dependency)\n// and it should return the value which will be binded to the\n// controller's methods (or fields) - see `GetRecords`.\nvar paramsDependency = func(ctx iris.Context) params {\n\tp := params{\n\t\tOrderBy: \"ASC\", // default value.\n\t}\n\t// Bind the URL values to the \"p\":\n\tctx.ReadQuery(&p)\n\t// Or bind a specific URL value by giving a default value:\n\t// p.OrderBy = ctx.URLParamDefault(\"order_by\", \"ASC\")\n\t//\n\t// OR make checks for default values after ReadXXX,\n\t// e.g. if p.OrderBy == \"\" {...}\n\n\t/* More options to read a request:\n\t// Bind the JSON request body to the \"p\":\n\tctx.ReadJSON(&p)\n\t// Bind the Form to the \"p\":\n\tctx.ReadForm(&p)\n\t// Bind any, based on the client's content-type header:\n\tctx.ReadBody(&p)\n\t// Bind the http requests to a struct value:\n\tctx.ReadHeader(&h)\n\t*/\n\n\treturn p\n}\n\ntype controller struct{}\n\nfunc (c *controller) GetRecords(stru params) string {\n\treturn fmt.Sprintf(\"%#+v\\n\", stru)\n}\n"
  },
  {
    "path": "_examples/mvc/session-controller/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/mvc\"\n\t\"github.com/kataras/iris/v12/sessions\"\n)\n\n// VisitController handles the root route.\ntype VisitController struct {\n\t// the current request session, automatically binded.\n\tSession *sessions.Session\n\n\t// A time.time which is binded from the MVC application manually.\n\tStartTime time.Time\n}\n\n// Get handles index\n// Method: GET\n// Path: http://localhost:8080\nfunc (c *VisitController) Get() string {\n\t// it increments a \"visits\" value of integer by one,\n\t// if the entry with key 'visits' doesn't exist it will create it for you.\n\tvisits := c.Session.Increment(\"visits\", 1)\n\t// write the current, updated visits.\n\tsince := time.Now().Sub(c.StartTime).Seconds()\n\treturn fmt.Sprintf(\"%d visit(s) from my current session in %0.1f seconds of server's up-time\",\n\t\tvisits, since)\n}\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\t// Configure sessions manager as we used to.\n\tsess := sessions.New(sessions.Config{Cookie: \"mysession_cookie_name\"})\n\tapp.Use(sess.Handler())\n\n\tvisitApp := mvc.New(app)\n\tvisitApp.Register(time.Now())\n\t// The `VisitController.Session` is automatically binded to the current `sessions.Session`.\n\n\tvisitApp.Handle(new(VisitController))\n\n\treturn app\n}\n\nfunc main() {\n\tapp := newApp()\n\n\t// 1. Prepare a client, e.g. your browser\n\t// 2. navigate to http://localhost:8080\n\t// 3. refresh the page some times\n\t// 4. close the browser\n\t// 5. re-open the browser (if it wasn't in private mode) and re-play 2.\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/mvc/session-controller/main_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nfunc TestMVCSession(t *testing.T) {\n\te := httptest.New(t, newApp(), httptest.URL(\"http://example.com\"))\n\n\te1 := e.GET(\"/\").Expect().Status(httptest.StatusOK)\n\te1.Cookies().NotEmpty()\n\te1.Body().Contains(\"1 visit\")\n\n\te.GET(\"/\").Expect().Status(httptest.StatusOK).\n\t\tBody().Contains(\"2 visit\")\n\n\te.GET(\"/\").Expect().Status(httptest.StatusOK).\n\t\tBody().Contains(\"3 visit\")\n}\n"
  },
  {
    "path": "_examples/mvc/singleton/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"sync/atomic\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/mvc\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tmvc.New(app.Party(\"/\")).Handle(&globalVisitorsController{visits: 0})\n\n\t// http://localhost:8080\n\tapp.Listen(\":8080\")\n}\n\ntype globalVisitorsController struct {\n\t// When a singleton controller is used then concurent safe access is up to the developers, because\n\t// all clients share the same controller instance instead.\n\t// Note that any controller's methods\n\t// are per-client, but the struct's field can be shared across multiple clients if the structure\n\t// does not have any dynamic struct field dependencies that depend on the iris.Context\n\t// and ALL field's values are NOT zero, at this case we use uint64 which it's no zero (even if we didn't set it\n\t// manually ease-of-understand reasons) because it's a value of &{0}.\n\t// All the above declares a Singleton, note that you don't have to write a single line of code to do this, Iris is smart enough.\n\t//\n\t// see `Get`.\n\tvisits uint64\n}\n\nfunc (c *globalVisitorsController) Get() string {\n\tcount := atomic.AddUint64(&c.visits, 1)\n\treturn fmt.Sprintf(\"Total visitors: %d\", count)\n}\n"
  },
  {
    "path": "_examples/mvc/versioned-controller/main.go",
    "content": "package main\n\nimport (\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/mvc\"\n)\n\n// Optional deprecated X-API-XXX headers for version 1.\nvar opts = mvc.DeprecationOptions{\n\tWarnMessage:     \"deprecated, see <this link>\",\n\tDeprecationDate: time.Now().UTC(),\n\tDeprecationInfo: \"a bigger version is available, see <this link> for more information\",\n}\n\nfunc main() {\n\tapp := newApp()\n\n\t// See main_test.go for request examples.\n\tapp.Listen(\":8080\")\n}\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\n\tdataRouter := app.Party(\"/data\")\n\t{\n\t\tm := mvc.New(dataRouter)\n\n\t\tm.Handle(new(v1Controller), mvc.Version(\"1.0.0\"), mvc.Deprecated(opts))\n\t\tm.Handle(new(v2Controller), mvc.Version(\"2.3.0\"))\n\t\tm.Handle(new(v3Controller), mvc.Version(\">=3.0.0 <4.0.0\"))\n\t\tm.Handle(new(noVersionController)) // or if missing it will respond with 501 version not found.\n\t}\n\n\treturn app\n}\n\ntype v1Controller struct{}\n\nfunc (c *v1Controller) Get() string {\n\treturn \"data (v1.x)\"\n}\n\ntype v2Controller struct{}\n\nfunc (c *v2Controller) Get() string {\n\treturn \"data (v2.x)\"\n}\n\ntype v3Controller struct{}\n\nfunc (c *v3Controller) Get() string {\n\treturn \"data (v3.x)\"\n}\n\ntype noVersionController struct{}\n\nfunc (c *noVersionController) Get() string {\n\treturn \"data\"\n}\n"
  },
  {
    "path": "_examples/mvc/versioned-controller/main_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/httptest\"\n\t\"github.com/kataras/iris/v12/versioning\"\n)\n\nfunc TestVersionedController(t *testing.T) {\n\tapp := newApp()\n\n\te := httptest.New(t, app)\n\te.GET(\"/data\").WithHeader(versioning.AcceptVersionHeaderKey, \"1.0.0\").Expect().\n\t\tStatus(iris.StatusOK).Body().IsEqual(\"data (v1.x)\")\n\te.GET(\"/data\").WithHeader(versioning.AcceptVersionHeaderKey, \"2.3.0\").Expect().\n\t\tStatus(iris.StatusOK).Body().IsEqual(\"data (v2.x)\")\n\te.GET(\"/data\").WithHeader(versioning.AcceptVersionHeaderKey, \"3.1.0\").Expect().\n\t\tStatus(iris.StatusOK).Body().IsEqual(\"data (v3.x)\")\n\n\t// Test invalid version or no version at all.\n\te.GET(\"/data\").WithHeader(versioning.AcceptVersionHeaderKey, \"4.0.0\").Expect().\n\t\tStatus(iris.StatusOK).Body().IsEqual(\"data\")\n\te.GET(\"/data\").Expect().\n\t\tStatus(iris.StatusOK).Body().IsEqual(\"data\")\n\n\t// Test Deprecated (v1)\n\tex := e.GET(\"/data\").WithHeader(versioning.AcceptVersionHeaderKey, \"1.0.0\").Expect()\n\tex.Status(iris.StatusOK).Body().IsEqual(\"data (v1.x)\")\n\tex.Header(\"X-API-Warn\").Equal(opts.WarnMessage)\n\texpectedDateStr := opts.DeprecationDate.Format(app.ConfigurationReadOnly().GetTimeFormat())\n\tex.Header(\"X-API-Deprecation-Date\").Equal(expectedDateStr)\n}\n"
  },
  {
    "path": "_examples/mvc/vuejs-todo-mvc/README.md",
    "content": "# A Todo MVC Application using Iris and Vue.js\n\n## Hackernoon Article: https://medium.com/hackernoon/a-todo-mvc-application-using-iris-and-vue-js-5019ff870064\n\nVue.js is a front-end framework for building web applications using javascript. It has a blazing fast Virtual DOM renderer.\n\nIris is a back-end framework for building web applications using The Go Programming Language (disclaimer: author here). It's one of the fastest and featured web frameworks out there. We wanna use this to serve our \"todo service\".\n\n## The Tools\n\nProgramming Languages are just tools for us, but we need a safe, fast and “cross-platform” programming language to power our service.\n\n[Go](https://golang.org) is a [rapidly growing](https://www.tiobe.com/tiobe-index/) open source programming language designed for building simple, fast, and reliable software. Take a look [here](https://github.com/golang/go/wiki/GoUsers) which great companies use Go to power their services.\n\n### Install the Go Programming Language\n\nExtensive information about downloading & installing Go can be found [here](https://golang.org/dl/).\n\n[![](https://i3.ytimg.com/vi/9x-pG3lvLi0/hqdefault.jpg)](https://youtu.be/9x-pG3lvLi0)\n\n> Maybe [Windows](https://www.youtube.com/watch?v=WT5mTznJBS0) or [Mac OS X](https://www.youtube.com/watch?v=5qI8z_lB5Lw) user?\n\n> The article does not contain an introduction to the language itself, if you’re a newcomer I recommend you to bookmark this article, [learn](https://github.com/golang/go/wiki/Learn) the language’s fundamentals and come back later on.\n\n## The Dependencies\n\nMany articles have been written, in the past, that lead developers not to use a web framework because they are useless and \"bad\". I have to tell you that there is no such thing, it always depends on the (web) framework that you’re going to use. At production environment,  we don’t have the time or the experience to code everything that we wanna use in the applications, and if we could are we sure that we can do better and safely than others? In short term: **Good frameworks are helpful tools for any developer, company or startup and \"bad\" frameworks are waste of time, crystal clear.**\n\nYou’ll need two dependencies:\n\n1. Vue.js, for our client-side requirements. Download it from [here](https://vuejs.org/), latest v2.\n2. The Iris Web Framework, for our server-side requirements. Can be found [here](https://github.com/kataras/iris), latest v12.\n\n> If you have Go already installed then just execute `go get github.com/kataras/iris/v12@latest` to install the Iris Web Framework.\n\n## Start\n\nIf we are all in the same page, it’s time to learn how we can create a live todo application that will be easy to deploy and extend even more!\n\nWe're going to use a vue.js todo application which uses browser'\ns local storage and doesn't have any user-specified features like live sync between browser's tabs, you can find the original version inside the vue's [docs](https://vuejs.org/v2/examples/todomvc.html).\n\nAssuming that you know how %GOPATH% works, create an empty folder, i.e \"vuejs-todo-mvc\" in the %GOPATH%/src directory, there you will create those files:\n\n- web/public/js/app.js\n- web/public/index.html\n- todo/item.go\n- todo/service.go\n- web/controllers/todo_controller.go\n- web/main.go\n\n_Read the comments in the source code, they may be very helpful_\n\n### The client-side (vue.js)\n\n```js\n/* file: vuejs-todo-mvc/web/public/js/app.js */\n// Full spec-compliant TodoMVC with Iris\n// and hash-based routing in ~200 effective lines of JavaScript.\n\nvar ws;\n\n((async () => {\n  const events = {\n    todos: {\n      saved: function (ns, msg) {\n        app.todos = msg.unmarshal()\n        // or make a new http fetch\n        // fetchTodos(function (items) {\n        //   app.todos = msg.unmarshal()\n        // });\n      }\n    }\n  };\n\n  const conn = await neffos.dial(\"ws://localhost:8080/todos/sync\", events);\n  ws = await conn.connect(\"todos\");\n})()).catch(console.error);\n\nfunction fetchTodos(onComplete) {\n  axios.get(\"/todos\").then(response => {\n    if (response.data === null) {\n      return;\n    }\n\n    onComplete(response.data);\n  });\n}\n\nvar todoStorage = {\n  fetch: function () {\n    var todos = [];\n    fetchTodos(function (items) {\n      for (var i = 0; i < items.length; i++) {\n        todos.push(items[i]);\n      }\n    });\n    return todos;\n  },\n  save: function (todos) {\n    axios.post(\"/todos\", JSON.stringify(todos)).then(response => {\n      if (!response.data.success) {\n        window.alert(\"saving had a failure\");\n        return;\n      }\n      // console.log(\"send: save\");\n      ws.emit(\"save\")\n    });\n  }\n}\n\n// visibility filters\nvar filters = {\n  all: function (todos) {\n    return todos\n  },\n  active: function (todos) {\n    return todos.filter(function (todo) {\n      return !todo.completed\n    })\n  },\n  completed: function (todos) {\n    return todos.filter(function (todo) {\n      return todo.completed\n    })\n  }\n}\n\n// app Vue instance\nvar app = new Vue({\n  // app initial state\n  data: {\n    todos: todoStorage.fetch(),\n    newTodo: '',\n    editedTodo: null,\n    visibility: 'all'\n  },\n\n  // we will not use the \"watch\" as it works with the fields like \"hasChanges\"\n  // and callbacks to make it true but let's keep things very simple as it's just a small getting started. \n  // // watch todos change for persistence\n  // watch: {\n  //   todos: {\n  //     handler: function (todos) {\n  //       if (app.hasChanges) {\n  //         todoStorage.save(todos);\n  //         app.hasChanges = false;\n  //       }\n\n  //     },\n  //     deep: true\n  //   }\n  // },\n\n  // computed properties\n  // http://vuejs.org/guide/computed.html\n  computed: {\n    filteredTodos: function () {\n      return filters[this.visibility](this.todos)\n    },\n    remaining: function () {\n      return filters.active(this.todos).length\n    },\n    allDone: {\n      get: function () {\n        return this.remaining === 0\n      },\n      set: function (value) {\n        this.todos.forEach(function (todo) {\n          todo.completed = value\n        })\n        this.notifyChange();\n      }\n    }\n  },\n\n  filters: {\n    pluralize: function (n) {\n      return n === 1 ? 'item' : 'items'\n    }\n  },\n\n  // methods that implement data logic.\n  // note there's no DOM manipulation here at all.\n  methods: {\n    notifyChange: function () {\n      todoStorage.save(this.todos)\n    },\n    addTodo: function () {\n      var value = this.newTodo && this.newTodo.trim()\n      if (!value) {\n        return\n      }\n      this.todos.push({\n        id: this.todos.length + 1, // just for the client-side.\n        title: value,\n        completed: false\n      })\n      this.newTodo = ''\n      this.notifyChange();\n    },\n\n    completeTodo: function (todo) {\n      if (todo.completed) {\n        todo.completed = false;\n      } else {\n        todo.completed = true;\n      }\n      this.notifyChange();\n    },\n    removeTodo: function (todo) {\n      this.todos.splice(this.todos.indexOf(todo), 1)\n      this.notifyChange();\n    },\n\n    editTodo: function (todo) {\n      this.beforeEditCache = todo.title\n      this.editedTodo = todo\n    },\n\n    doneEdit: function (todo) {\n      if (!this.editedTodo) {\n        return\n      }\n      this.editedTodo = null\n      todo.title = todo.title.trim();\n      if (!todo.title) {\n        this.removeTodo(todo);\n      }\n      this.notifyChange();\n    },\n\n    cancelEdit: function (todo) {\n      this.editedTodo = null\n      todo.title = this.beforeEditCache\n    },\n\n    removeCompleted: function () {\n      this.todos = filters.active(this.todos);\n      this.notifyChange();\n    }\n  },\n\n  // a custom directive to wait for the DOM to be updated\n  // before focusing on the input field.\n  // http://vuejs.org/guide/custom-directive.html\n  directives: {\n    'todo-focus': function (el, binding) {\n      if (binding.value) {\n        el.focus()\n      }\n    }\n  }\n})\n\n// handle routing\nfunction onHashChange() {\n  var visibility = window.location.hash.replace(/#\\/?/, '')\n  if (filters[visibility]) {\n    app.visibility = visibility\n  } else {\n    window.location.hash = ''\n    app.visibility = 'all'\n  }\n}\n\nwindow.addEventListener('hashchange', onHashChange)\nonHashChange()\n\n// mount\napp.$mount('.todoapp');\n```\n\nLet's add our view, the static html.\n\n```html\n<!-- file: vuejs-todo-mvc/web/public/index.html -->\n<!doctype html>\n<html data-framework=\"vue\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <title>Iris + Vue.js • TodoMVC</title>\n  <link rel=\"stylesheet\" href=\"https://unpkg.com/todomvc-app-css@2.0.4/index.css\">\n  <!-- this needs to be loaded before guide's inline scripts -->\n  <script src=\"https://unpkg.com/vue@3/dist/vue.global.js\"></script>\n  <!-- $http -->\n  <script src=\"https://unpkg.com/axios/dist/axios.min.js\"></script>\n  <!-- -->\n  <script src=\"https://unpkg.com/director@1.2.8/build/director.js\"></script>\n  <script src=\"https://cdn.jsdelivr.net/npm/neffos.js@0.1.27/dist/neffos.min.js\"></script>\n\n  <style>\n    [v-cloak] {\n      display: none;\n    }\n  </style>\n</head>\n\n<body>\n  <section class=\"todoapp\">\n    <header class=\"header\">\n      <h1>todos</h1>\n      <input class=\"new-todo\" autofocus autocomplete=\"off\" placeholder=\"What needs to be done?\" v-model=\"newTodo\"\n        @keyup.enter=\"addTodo\">\n    </header>\n    <section class=\"main\" v-show=\"todos.length\" v-cloak>\n      <input class=\"toggle-all\" type=\"checkbox\" v-model=\"allDone\">\n      <ul class=\"todo-list\">\n        <li v-for=\"todo in filteredTodos\" class=\"todo\" :key=\"todo.id\"\n          :class=\"{ completed: todo.completed, editing: todo == editedTodo }\">\n          <div class=\"view\">\n            <!-- v-model=\"todo.completed\" -->\n            <input class=\"toggle\" type=\"checkbox\" @click=\"completeTodo(todo)\">\n            <label @dblclick=\"editTodo(todo)\">{{ todo.title }}</label>\n            <button class=\"destroy\" @click=\"removeTodo(todo)\"></button>\n          </div>\n          <input class=\"edit\" type=\"text\" v-model=\"todo.title\" v-todo-focus=\"todo == editedTodo\" @blur=\"doneEdit(todo)\"\n            @keyup.enter=\"doneEdit(todo)\" @keyup.esc=\"cancelEdit(todo)\">\n        </li>\n      </ul>\n    </section>\n    <footer class=\"footer\" v-show=\"todos.length\" v-cloak>\n      <span class=\"todo-count\">\n        <strong>{{ remaining }}</strong> {{ remaining | pluralize }} left\n      </span>\n      <ul class=\"filters\">\n        <li>\n          <a href=\"#/all\" :class=\"{ selected: visibility == 'all' }\">All</a>\n        </li>\n        <li>\n          <a href=\"#/active\" :class=\"{ selected: visibility == 'active' }\">Active</a>\n        </li>\n        <li>\n          <a href=\"#/completed\" :class=\"{ selected: visibility == 'completed' }\">Completed</a>\n        </li>\n      </ul>\n      <button class=\"clear-completed\" @click=\"removeCompleted\" v-show=\"todos.length > remaining\">\n        Clear completed\n      </button>\n    </footer>\n  </section>\n  <footer class=\"info\">\n    <p>Double-click to edit a todo</p>\n  </footer>\n\n  <script src=\"/js/app.js\"></script>\n</body>\n\n</html>\n```\n\n### The server-side (iris)\n\nOur view model.\n\n```go\n// file: vuejs-todo-mvc/todo/item.go\npackage todo\n\ntype Item struct {\n    SessionID string `json:\"-\"`\n    ID        int64  `json:\"id,omitempty\"`\n    Title     string `json:\"title\"`\n    Completed bool   `json:\"completed\"`\n}\n```\n\nOur service.\n\n```go\n// file: vuejs-todo-mvc/todo/service.go\npackage todo\n\nimport (\n    \"sync\"\n)\n\ntype Service interface {\n    Get(owner string) []Item\n    Save(owner string, newItems []Item) error\n}\n\ntype MemoryService struct {\n    // key = session id, value the list of todo items that this session id has.\n    items map[string][]Item\n    // protected by locker for concurrent access.\n    mu sync.RWMutex\n}\n\nfunc NewMemoryService() *MemoryService {\n    return &MemoryService{\n        items: make(map[string][]Item, 0),\n    }\n}\n\nfunc (s *MemoryService) Get(sessionOwner string) []Item {\n    s.mu.RLock()\n    items := s.items[sessionOwner]\n    s.mu.RUnlock()\n\n    return items\n}\n\nfunc (s *MemoryService) Save(sessionOwner string, newItems []Item) error {\n    var prevID int64\n    for i := range newItems {\n        if newItems[i].ID == 0 {\n            newItems[i].ID = prevID\n            prevID++\n        }\n    }\n\n    s.mu.Lock()\n    s.items[sessionOwner] = newItems\n    s.mu.Unlock()\n    return nil\n}\n```\n\nWe are going to use some of the MVC functionalities of the iris web framework here but you can do the same with the standard API as well.\n\n```go\n// file: vuejs-todo-mvc/web/controllers/todo_controller.go\npackage controllers\n\nimport (\n\t\"vuejs-todo-mvc/todo\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/mvc\"\n\t\"github.com/kataras/iris/v12/sessions\"\n\t\"github.com/kataras/iris/v12/websocket\"\n)\n\n// TodoController is our TODO app's web controller.\ntype TodoController struct {\n\tService todo.Service\n\n\tSession *sessions.Session\n\n\tNS *websocket.NSConn\n}\n\n// BeforeActivation called once before the server ran, and before\n// the routes and dependencies binded.\n// You can bind custom things to the controller, add new methods, add middleware,\n// add dependencies to the struct or the method(s) and more.\nfunc (c *TodoController) BeforeActivation(b mvc.BeforeActivation) {\n\t// this could be binded to a controller's function input argument\n\t// if any, or struct field if any:\n\tb.Dependencies().Add(func(ctx iris.Context) (items []todo.Item) {\n\t\tctx.ReadJSON(&items)\n\t\treturn\n\t})\n}\n\n// Get handles the GET: /todos route.\nfunc (c *TodoController) Get() []todo.Item {\n\treturn c.Service.Get(c.Session.ID())\n}\n\n// PostItemResponse the response data that will be returned as json\n// after a post save action of all todo items.\ntype PostItemResponse struct {\n\tSuccess bool `json:\"success\"`\n}\n\nvar emptyResponse = PostItemResponse{Success: false}\n\n// Post handles the POST: /todos route.\nfunc (c *TodoController) Post(newItems []todo.Item) PostItemResponse {\n\tif err := c.Service.Save(c.Session.ID(), newItems); err != nil {\n\t\treturn emptyResponse\n\t}\n\n\treturn PostItemResponse{Success: true}\n}\n\nfunc (c *TodoController) Save(msg websocket.Message) error {\n\tid := c.Session.ID()\n\tc.NS.Conn.Server().Broadcast(nil, websocket.Message{\n\t\tNamespace: msg.Namespace,\n\t\tEvent:     \"saved\",\n\t\tTo:        id,\n\t\tBody:      websocket.Marshal(c.Service.Get(id)),\n\t})\n\n\treturn nil\n}\n\n```\n\nAnd finally our main application's endpoint.\n\n```go\n// file: web/main.go\npackage main\n\nimport (\n\t\"strings\"\n\n\t\"github.com/kataras/iris/v12/_examples/mvc/vuejs-todo-mvc/src/todo\"\n\t\"github.com/kataras/iris/v12/_examples/mvc/vuejs-todo-mvc/src/web/controllers\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/mvc\"\n\t\"github.com/kataras/iris/v12/sessions\"\n\t\"github.com/kataras/iris/v12/websocket\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\t// serve our app in public, public folder\n\t// contains the client-side vue.js application,\n\t// no need for any server-side template here,\n\t// actually if you're going to just use vue without any\n\t// back-end services, you can just stop afer this line and start the server.\n\tapp.HandleDir(\"/\", iris.Dir(\"./public\"))\n\n\t// configure the http sessions.\n\tsess := sessions.New(sessions.Config{\n\t\tCookie: \"iris_session\",\n\t})\n\n\t// create a sub router and register the http controllers.\n\ttodosRouter := app.Party(\"/todos\")\n\n\t// create our mvc application targeted to /todos relative sub path.\n\ttodosApp := mvc.New(todosRouter)\n\n\t// any dependencies bindings here...\n\ttodosApp.Register(\n\t\ttodo.NewMemoryService(),\n\t)\n\n\ttodosController := new(controllers.TodoController)\n\t// controllers registration here...\n\ttodosApp.Handle(todosController)\n\n\t// Create a sub mvc app for websocket controller.\n\t// Inherit the parent's dependencies.\n\ttodosWebsocketApp := todosApp.Party(\"/sync\")\n\ttodosWebsocketApp.HandleWebsocket(todosController).\n\t\tSetNamespace(\"todos\").\n\t\tSetEventMatcher(func(methodName string) (string, bool) {\n\t\t\treturn strings.ToLower(methodName), true\n\t\t})\n\n\twebsocketServer := websocket.New(websocket.DefaultGorillaUpgrader, todosWebsocketApp)\n\tidGenerator := func(ctx iris.Context) string {\n\t\tid := sess.Start(ctx).ID()\n\t\treturn id\n\t}\n\ttodosWebsocketApp.Router.Get(\"/\", websocket.Handler(websocketServer, idGenerator))\n\n\t// start the web server at http://localhost:8080\n\tapp.Listen(\":8080\")\n}\n```\n\nRun the Iris web server you've just created by executing `go run main.go` from your current path (%GOPATH%/src/%your_folder%/web/).\n\n```sh\n$ go run main.go\nNow listening on: http://localhost:8080\nApplication Started. Press CTRL+C to shut down.\n_\n```\n\nOpen one or more browser tabs at: http://localhost:8080 and have fun!\n\n![](screen.png)\n\n### Download the Source Code\n\nThe whole project, all the files you saw in this article are located at: https://github.com/kataras/iris/tree/main/_examples/mvc/vuejs-todo-mvc\n\n## References\n\nhttps://vuejs.org/v2/examples/todomvc.html (using browser's local storage)\n\nhttps://github.com/kataras/iris/tree/main/_examples/mvc (mvc examples and features overview repository)\n\n## Thank you, once again\n\nHappy new year and thank you for your pattience, once again:) Don't hesitate to post any questions and provide feedback(I'm very active dev therefore you will be heard here!)\n\nDon't forget to check out my medium profile and twitter as well, I'm posting some (useful) stuff there too:)\n\n- https://medium.com/@kataras \n- https://twitter.com/MakisMaropoulos\n"
  },
  {
    "path": "_examples/mvc/vuejs-todo-mvc/src/go.mod",
    "content": "module github.com/kataras/iris/v12/_examples/mvc/vuejs-todo-mvc/src\n\ngo 1.25\n\nrequire github.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd\n\nrequire (\n\tgithub.com/BurntSushi/toml v1.6.0 // indirect\n\tgithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect\n\tgithub.com/CloudyKit/jet/v6 v6.3.1 // indirect\n\tgithub.com/Joker/jade v1.1.3 // indirect\n\tgithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 // indirect\n\tgithub.com/andybalholm/brotli v1.2.0 // indirect\n\tgithub.com/aymerick/douceur v0.2.0 // indirect\n\tgithub.com/blang/semver/v4 v4.0.0 // indirect\n\tgithub.com/fatih/structs v1.1.0 // indirect\n\tgithub.com/flosch/pongo2/v4 v4.0.2 // indirect\n\tgithub.com/gobwas/httphead v0.1.0 // indirect\n\tgithub.com/gobwas/pool v0.2.1 // indirect\n\tgithub.com/gobwas/ws v1.4.0 // indirect\n\tgithub.com/golang/snappy v1.0.0 // indirect\n\tgithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a // indirect\n\tgithub.com/google/uuid v1.6.0 // indirect\n\tgithub.com/gorilla/css v1.0.1 // indirect\n\tgithub.com/gorilla/websocket v1.5.3 // indirect\n\tgithub.com/iris-contrib/schema v0.0.6 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/kataras/blocks v0.0.8 // indirect\n\tgithub.com/kataras/golog v0.1.12 // indirect\n\tgithub.com/kataras/neffos v0.0.24 // indirect\n\tgithub.com/kataras/pio v0.0.13 // indirect\n\tgithub.com/kataras/sitemap v0.0.6 // indirect\n\tgithub.com/kataras/tunnel v0.0.4 // indirect\n\tgithub.com/klauspost/compress v1.18.2 // indirect\n\tgithub.com/kr/pretty v0.3.1 // indirect\n\tgithub.com/mailgun/raymond/v2 v2.0.48 // indirect\n\tgithub.com/mailru/easyjson v0.9.1 // indirect\n\tgithub.com/mediocregopher/radix/v3 v3.8.1 // indirect\n\tgithub.com/microcosm-cc/bluemonday v1.0.27 // indirect\n\tgithub.com/nats-io/nats.go v1.40.1 // indirect\n\tgithub.com/nats-io/nkeys v0.4.9 // indirect\n\tgithub.com/nats-io/nuid v1.0.1 // indirect\n\tgithub.com/rogpeppe/go-internal v1.10.0 // indirect\n\tgithub.com/russross/blackfriday/v2 v2.1.0 // indirect\n\tgithub.com/schollz/closestmatch v2.1.0+incompatible // indirect\n\tgithub.com/sirupsen/logrus v1.8.1 // indirect\n\tgithub.com/stretchr/testify v1.11.1 // indirect\n\tgithub.com/tdewolff/minify/v2 v2.24.8 // indirect\n\tgithub.com/tdewolff/parse/v2 v2.8.5 // indirect\n\tgithub.com/valyala/bytebufferpool v1.0.0 // indirect\n\tgithub.com/vmihailenco/msgpack/v5 v5.4.1 // indirect\n\tgithub.com/vmihailenco/tagparser/v2 v2.0.0 // indirect\n\tgithub.com/yosssi/ace v0.0.5 // indirect\n\tgolang.org/x/crypto v0.47.0 // indirect\n\tgolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 // indirect\n\tgolang.org/x/net v0.49.0 // indirect\n\tgolang.org/x/sys v0.40.0 // indirect\n\tgolang.org/x/text v0.33.0 // indirect\n\tgolang.org/x/time v0.14.0 // indirect\n\tgolang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect\n\tgoogle.golang.org/protobuf v1.36.11 // indirect\n\tgopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect\n\tgopkg.in/ini.v1 v1.67.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n"
  },
  {
    "path": "_examples/mvc/vuejs-todo-mvc/src/go.sum",
    "content": "github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=\ngithub.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=\ngithub.com/CloudyKit/jet/v6 v6.3.1 h1:6IAo5Cx21xrHVaR8zzXN5gJatKV/wO7Nf6bfCnCSbUw=\ngithub.com/CloudyKit/jet/v6 v6.3.1/go.mod h1:lf8ksdNsxZt7/yH/3n4vJQWA9RUq4wpaHtArHhGVMOw=\ngithub.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc=\ngithub.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=\ngithub.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=\ngithub.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 h1:dG7/gLroJGht/jSQtHiLvT48Hxn+crbmvyItZC8cWOs=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05/go.mod h1:NYezi6wtnJtBm5btoprXc5SvAdqH0XTXWnUup0MptAI=\ngithub.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=\ngithub.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=\ngithub.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=\ngithub.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=\ngithub.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=\ngithub.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=\ngithub.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=\ngithub.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=\ngithub.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=\ngithub.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=\ngithub.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=\ngithub.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw=\ngithub.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8=\ngithub.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=\ngithub.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=\ngithub.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=\ngithub.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=\ngithub.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=\ngithub.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=\ngithub.com/gobwas/ws v1.4.0 h1:CTaoG1tojrh4ucGPcoJFiAQUAsEWekEWvLy7GsVNqGs=\ngithub.com/gobwas/ws v1.4.0/go.mod h1:G3gNqMNtPppf5XUz7O4shetPpcZ1VJ7zt18dlUeakrc=\ngithub.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=\ngithub.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a h1:l7A0loSszR5zHd/qK53ZIHMO8b3bBSmENnQ6eKnUT0A=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=\ngithub.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=\ngithub.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=\ngithub.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=\ngithub.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=\ngithub.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2 h1:T9THsdP1woyAqKHwjkEsbCnMefsAFvk8iJJKokcJ3Go=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2/go.mod h1:JLDgIqnFy5loDSUv1OA2j0mb6p/rDhiCqigP22Uq9xE=\ngithub.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw=\ngithub.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/kataras/blocks v0.0.8 h1:MrpVhoFTCR2v1iOOfGng5VJSILKeZZI+7NGfxEh3SUM=\ngithub.com/kataras/blocks v0.0.8/go.mod h1:9Jm5zx6BB+06NwA+OhTbHW1xkMOYxahnqTN5DveZ2Yg=\ngithub.com/kataras/golog v0.1.12 h1:Bu7I/G4ilJlbfzjmU39O9N+2uO1pBcMK045fzZ4ytNg=\ngithub.com/kataras/golog v0.1.12/go.mod h1:wrGSbOiBqbQSQznleVNX4epWM8rl9SJ/rmEacl0yqy4=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd h1:oMsQgqKACbUdlaIWnN+EZzzAnHkw90hE/y/R0BSij2U=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd/go.mod h1:yucjtDl9Vn3OctWM1fi0MuM9OckemC7kEreFa9QtPF8=\ngithub.com/kataras/neffos v0.0.24 h1:S3lHqJopCfXN285VdlbGeOj+Id83u4xdQKToa+w1vW0=\ngithub.com/kataras/neffos v0.0.24/go.mod h1:/3K9zQ0yEC5/xUiSQx46ToWa3xneGfUo/nMit/F5g+U=\ngithub.com/kataras/pio v0.0.13 h1:x0rXVX0fviDTXOOLOmr4MUxOabu1InVSTu5itF8CXCM=\ngithub.com/kataras/pio v0.0.13/go.mod h1:k3HNuSw+eJ8Pm2lA4lRhg3DiCjVgHlP8hmXApSej3oM=\ngithub.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY=\ngithub.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4=\ngithub.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA=\ngithub.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw=\ngithub.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=\ngithub.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=\ngithub.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=\ngithub.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8=\ngithub.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=\ngithub.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=\ngithub.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=\ngithub.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=\ngithub.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/mediocregopher/radix/v3 v3.8.1 h1:rOkHflVuulFKlwsLY01/M2cM2tWCjDoETcMqKbAWu1M=\ngithub.com/mediocregopher/radix/v3 v3.8.1/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8=\ngithub.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=\ngithub.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=\ngithub.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=\ngithub.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=\ngithub.com/nats-io/nats.go v1.40.1 h1:MLjDkdsbGUeCMKFyCFoLnNn/HDTqcgVa3EQm+pMNDPk=\ngithub.com/nats-io/nats.go v1.40.1/go.mod h1:wV73x0FSI/orHPSYoyMeJB+KajMDoWyXmFaRrrYaaTo=\ngithub.com/nats-io/nkeys v0.4.9 h1:qe9Faq2Gxwi6RZnZMXfmGMZkg3afLLOtrU+gDZJ35b0=\ngithub.com/nats-io/nkeys v0.4.9/go.mod h1:jcMqs+FLG+W5YO36OX6wFIFcmpdAns+w1Wm6D3I/evE=\ngithub.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=\ngithub.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=\ngithub.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=\ngithub.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=\ngithub.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=\ngithub.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=\ngithub.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk=\ngithub.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=\ngithub.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=\ngithub.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=\ngithub.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=\ngithub.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/tdewolff/minify/v2 v2.24.8 h1:58/VjsbevI4d5FGV0ZSuBrHMSSkH4MCH0sIz/eKIauE=\ngithub.com/tdewolff/minify/v2 v2.24.8/go.mod h1:0Ukj0CRpo/sW/nd8uZ4ccXaV1rEVIWA3dj8U7+Shhfw=\ngithub.com/tdewolff/parse/v2 v2.8.5 h1:ZmBiA/8Do5Rpk7bDye0jbbDUpXXbCdc3iah4VeUvwYU=\ngithub.com/tdewolff/parse/v2 v2.8.5/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=\ngithub.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE=\ngithub.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=\ngithub.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=\ngithub.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=\ngithub.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=\ngithub.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=\ngithub.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=\ngithub.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=\ngithub.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA=\ngithub.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0=\ngithub.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=\ngithub.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=\ngithub.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=\ngolang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=\ngolang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=\ngolang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=\ngolang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=\ngolang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=\ngolang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=\ngolang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=\ngolang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=\ngolang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY=\ngolang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=\ngoogle.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=\ngoogle.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=\ngopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nmoul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs=\nmoul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE=\n"
  },
  {
    "path": "_examples/mvc/vuejs-todo-mvc/src/todo/item.go",
    "content": "package todo\n\ntype Item struct {\n\tSessionID string `json:\"-\"`\n\tID        int64  `json:\"id,omitempty\"`\n\tTitle     string `json:\"title\"`\n\tCompleted bool   `json:\"completed\"`\n}\n"
  },
  {
    "path": "_examples/mvc/vuejs-todo-mvc/src/todo/service.go",
    "content": "package todo\n\nimport (\n\t\"sync\"\n)\n\ntype Service interface {\n\tGet(owner string) []Item\n\tSave(owner string, newItems []Item) error\n}\n\ntype MemoryService struct {\n\t// key = session id, value the list of todo items that this session id has.\n\titems map[string][]Item\n\t// protected by locker for concurrent access.\n\tmu sync.RWMutex\n}\n\nfunc NewMemoryService() *MemoryService {\n\treturn &MemoryService{\n\t\titems: make(map[string][]Item),\n\t}\n}\n\nfunc (s *MemoryService) Get(sessionOwner string) []Item {\n\ts.mu.RLock()\n\titems := s.items[sessionOwner]\n\ts.mu.RUnlock()\n\n\treturn items\n}\n\nfunc (s *MemoryService) Save(sessionOwner string, newItems []Item) error {\n\tvar prevID int64\n\tfor i := range newItems {\n\t\tif newItems[i].ID == 0 {\n\t\t\tnewItems[i].ID = prevID\n\t\t\tprevID++\n\t\t}\n\t}\n\n\ts.mu.Lock()\n\ts.items[sessionOwner] = newItems\n\ts.mu.Unlock()\n\treturn nil\n}\n"
  },
  {
    "path": "_examples/mvc/vuejs-todo-mvc/src/web/controllers/todo_controller.go",
    "content": "package controllers\n\nimport (\n\t\"github.com/kataras/iris/v12/_examples/mvc/vuejs-todo-mvc/src/todo\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/mvc\"\n\t\"github.com/kataras/iris/v12/sessions\"\n\t\"github.com/kataras/iris/v12/websocket\"\n)\n\n// TodoController is our TODO app's web controller.\ntype TodoController struct {\n\tService todo.Service\n\n\tSession *sessions.Session\n\n\tNS *websocket.NSConn\n}\n\n// BeforeActivation called once before the server ran, and before\n// the routes and dependencies binded.\n// You can bind custom things to the controller, add new methods, add middleware,\n// add dependencies to the struct or the method(s) and more.\nfunc (c *TodoController) BeforeActivation(b mvc.BeforeActivation) {\n\t// this could be binded to a controller's function input argument\n\t// if any, or struct field if any:\n\tb.Dependencies().Register(func(ctx iris.Context) (items []todo.Item) {\n\t\tctx.ReadJSON(&items)\n\t\treturn\n\t}) // Note: from Iris v12.2 these type of dependencies are automatically resolved.\n}\n\n// Get handles the GET: /todos route.\nfunc (c *TodoController) Get() []todo.Item {\n\treturn c.Service.Get(c.Session.ID())\n}\n\n// PostItemResponse the response data that will be returned as json\n// after a post save action of all todo items.\ntype PostItemResponse struct {\n\tSuccess bool `json:\"success\"`\n}\n\nvar emptyResponse = PostItemResponse{Success: false}\n\n// Post handles the POST: /todos route.\nfunc (c *TodoController) Post(newItems []todo.Item) PostItemResponse {\n\tif err := c.Service.Save(c.Session.ID(), newItems); err != nil {\n\t\treturn emptyResponse\n\t}\n\n\treturn PostItemResponse{Success: true}\n}\n\nfunc (c *TodoController) Save(msg websocket.Message) error {\n\tid := c.Session.ID()\n\tc.NS.Conn.Server().Broadcast(nil, websocket.Message{\n\t\tNamespace: msg.Namespace,\n\t\tEvent:     \"saved\",\n\t\tTo:        id,\n\t\tBody:      websocket.Marshal(c.Service.Get(id)),\n\t})\n\n\treturn nil\n}\n"
  },
  {
    "path": "_examples/mvc/vuejs-todo-mvc/src/web/main.go",
    "content": "package main\n\nimport (\n\t\"strings\"\n\n\t\"github.com/kataras/iris/v12/_examples/mvc/vuejs-todo-mvc/src/todo\"\n\t\"github.com/kataras/iris/v12/_examples/mvc/vuejs-todo-mvc/src/web/controllers\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/mvc\"\n\t\"github.com/kataras/iris/v12/sessions\"\n\t\"github.com/kataras/iris/v12/websocket\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\t// serve our app in public, public folder\n\t// contains the client-side vue.js application,\n\t// no need for any server-side template here,\n\t// actually if you're going to just use vue without any\n\t// back-end services, you can just stop afer this line and start the server.\n\tapp.HandleDir(\"/\", iris.Dir(\"./public\"))\n\n\t// configure the http sessions.\n\tsess := sessions.New(sessions.Config{\n\t\tCookie: \"iris_session\",\n\t})\n\n\t// create a sub router and register the http controllers.\n\ttodosRouter := app.Party(\"/todos\")\n\n\t// Register sessions handler.\n\t// TodoController.Session will automatically\n\t// filled with the current request's session.\n\ttodosRouter.Use(sess.Handler())\n\n\t// create our mvc application targeted to /todos relative sub path.\n\ttodosApp := mvc.New(todosRouter)\n\n\t// any dependencies bindings here...\n\ttodosApp.Register(\n\t\ttodo.NewMemoryService(),\n\t)\n\n\ttodosController := new(controllers.TodoController)\n\t// controllers registration here...\n\ttodosApp.Handle(todosController)\n\n\t// Create a sub mvc app for websocket controller.\n\t// Inherit the parent's dependencies.\n\ttodosWebsocketApp := todosApp.Party(\"/sync\")\n\ttodosWebsocketApp.HandleWebsocket(todosController).\n\t\tSetNamespace(\"todos\").\n\t\tSetEventMatcher(func(methodName string) (string, bool) {\n\t\t\treturn strings.ToLower(methodName), true\n\t\t})\n\n\twebsocketServer := websocket.New(websocket.DefaultGorillaUpgrader, todosWebsocketApp)\n\tidGenerator := func(ctx iris.Context) string {\n\t\tid := sess.Start(ctx).ID()\n\t\treturn id\n\t}\n\ttodosWebsocketApp.Router.Get(\"/\", websocket.Handler(websocketServer, idGenerator))\n\n\t// start the web server at http://localhost:8080\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/mvc/vuejs-todo-mvc/src/web/public/css/index",
    "content": "index.css is not here to reduce the disk space for the examples.\nhttps://unpkg.com/todomvc-app-css@2.0.4/index.css is used instead."
  },
  {
    "path": "_examples/mvc/vuejs-todo-mvc/src/web/public/index.html",
    "content": "<!doctype html>\n<html data-framework=\"vue\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <title>Iris + Vue.js • TodoMVC</title>\n  <link rel=\"stylesheet\" href=\"https://unpkg.com/todomvc-app-css@2.0.4/index.css\">\n  <!-- this needs to be loaded before guide's inline scripts -->\n  <script src=\"https://unpkg.com/vue@3/dist/vue.global.js\"></script>\n  <!-- $http -->\n  <script src=\"https://unpkg.com/axios/dist/axios.min.js\"></script>\n  <!-- -->\n  <script src=\"https://unpkg.com/director@1.2.8/build/director.js\"></script>\n  <script src=\"https://cdn.jsdelivr.net/npm/neffos.js@0.1.27/dist/neffos.min.js\"></script>\n\n  <style>\n    [v-cloak] {\n      display: none;\n    }\n  </style>\n</head>\n\n<body>\n  <section class=\"todoapp\">\n    <header class=\"header\">\n      <h1>todos</h1>\n      <input class=\"new-todo\" autofocus autocomplete=\"off\" placeholder=\"What needs to be done?\" v-model=\"newTodo\"\n        @keyup.enter=\"addTodo\">\n    </header>\n    <section class=\"main\" v-show=\"todos.length\" v-cloak>\n      <input class=\"toggle-all\" type=\"checkbox\" v-model=\"allDone\">\n      <ul class=\"todo-list\">\n        <li v-for=\"todo in filteredTodos\" class=\"todo\" :key=\"todo.id\"\n          :class=\"{ completed: todo.completed, editing: todo == editedTodo }\">\n          <div class=\"view\">\n            <!-- v-model=\"todo.completed\" -->\n            <input class=\"toggle\" type=\"checkbox\" @click=\"completeTodo(todo)\">\n            <label @dblclick=\"editTodo(todo)\">{{ todo.title }}</label>\n            <button class=\"destroy\" @click=\"removeTodo(todo)\"></button>\n          </div>\n          <input class=\"edit\" type=\"text\" v-model=\"todo.title\" v-todo-focus=\"todo == editedTodo\" @blur=\"doneEdit(todo)\"\n            @keyup.enter=\"doneEdit(todo)\" @keyup.esc=\"cancelEdit(todo)\">\n        </li>\n      </ul>\n    </section>\n    <footer class=\"footer\" v-show=\"todos.length\" v-cloak>\n      <span class=\"todo-count\">\n        <strong>{{ remaining }}</strong> {{ remaining | pluralize }} left\n      </span>\n      <ul class=\"filters\">\n        <li>\n          <a href=\"#/all\" :class=\"{ selected: visibility == 'all' }\">All</a>\n        </li>\n        <li>\n          <a href=\"#/active\" :class=\"{ selected: visibility == 'active' }\">Active</a>\n        </li>\n        <li>\n          <a href=\"#/completed\" :class=\"{ selected: visibility == 'completed' }\">Completed</a>\n        </li>\n      </ul>\n      <button class=\"clear-completed\" @click=\"removeCompleted\" v-show=\"todos.length > remaining\">\n        Clear completed\n      </button>\n    </footer>\n  </section>\n  <footer class=\"info\">\n    <p>Double-click to edit a todo</p>\n  </footer>\n\n  <script src=\"/js/app.js\"></script>\n</body>\n\n</html>"
  },
  {
    "path": "_examples/mvc/vuejs-todo-mvc/src/web/public/js/app.js",
    "content": "// Full spec-compliant TodoMVC with Iris\n// and hash-based routing in ~200 effective lines of JavaScript.\n\nvar ws;\n\n((async () => {\n  const events = {\n    todos: {\n      saved: function (ns, msg) {\n        app.todos = msg.unmarshal()\n        // or make a new http fetch\n        // fetchTodos(function (items) {\n        //   app.todos = msg.unmarshal()\n        // });\n      }\n    }\n  };\n\n  const conn = await neffos.dial(\"ws://localhost:8080/todos/sync\", events);\n  ws = await conn.connect(\"todos\");\n})()).catch(console.error);\n\nfunction fetchTodos(onComplete) {\n  axios.get(\"/todos\").then(response => {\n    if (response.data === null) {\n      return;\n    }\n\n    onComplete(response.data);\n  });\n}\n\nvar todoStorage = {\n  fetch: function () {\n    var todos = [];\n    fetchTodos(function (items) {\n      for (var i = 0; i < items.length; i++) {\n        todos.push(items[i]);\n      }\n    });\n    return todos;\n  },\n  save: function (todos) {\n    axios.post(\"/todos\", JSON.stringify(todos)).then(response => {\n      if (!response.data.success) {\n        window.alert(\"saving had a failure\");\n        return;\n      }\n      // console.log(\"send: save\");\n      ws.emit(\"save\")\n    });\n  }\n}\n\n// visibility filters\nvar filters = {\n  all: function (todos) {\n    return todos\n  },\n  active: function (todos) {\n    return todos.filter(function (todo) {\n      return !todo.completed\n    })\n  },\n  completed: function (todos) {\n    return todos.filter(function (todo) {\n      return todo.completed\n    })\n  }\n}\n\n// app Vue instance\nvar app = new Vue({\n  // app initial state\n  data: {\n    todos: todoStorage.fetch(),\n    newTodo: '',\n    editedTodo: null,\n    visibility: 'all'\n  },\n\n  // we will not use the \"watch\" as it works with the fields like \"hasChanges\"\n  // and callbacks to make it true but let's keep things very simple as it's just a small getting started. \n  // // watch todos change for persistence\n  // watch: {\n  //   todos: {\n  //     handler: function (todos) {\n  //       if (app.hasChanges) {\n  //         todoStorage.save(todos);\n  //         app.hasChanges = false;\n  //       }\n\n  //     },\n  //     deep: true\n  //   }\n  // },\n\n  // computed properties\n  // http://vuejs.org/guide/computed.html\n  computed: {\n    filteredTodos: function () {\n      return filters[this.visibility](this.todos)\n    },\n    remaining: function () {\n      return filters.active(this.todos).length\n    },\n    allDone: {\n      get: function () {\n        return this.remaining === 0\n      },\n      set: function (value) {\n        this.todos.forEach(function (todo) {\n          todo.completed = value\n        })\n        this.notifyChange();\n      }\n    }\n  },\n\n  filters: {\n    pluralize: function (n) {\n      return n === 1 ? 'item' : 'items'\n    }\n  },\n\n  // methods that implement data logic.\n  // note there's no DOM manipulation here at all.\n  methods: {\n    notifyChange: function () {\n      todoStorage.save(this.todos)\n    },\n    addTodo: function () {\n      var value = this.newTodo && this.newTodo.trim()\n      if (!value) {\n        return\n      }\n      this.todos.push({\n        id: this.todos.length + 1, // just for the client-side.\n        title: value,\n        completed: false\n      })\n      this.newTodo = ''\n      this.notifyChange();\n    },\n\n    completeTodo: function (todo) {\n      if (todo.completed) {\n        todo.completed = false;\n      } else {\n        todo.completed = true;\n      }\n      this.notifyChange();\n    },\n    removeTodo: function (todo) {\n      this.todos.splice(this.todos.indexOf(todo), 1)\n      this.notifyChange();\n    },\n\n    editTodo: function (todo) {\n      this.beforeEditCache = todo.title\n      this.editedTodo = todo\n    },\n\n    doneEdit: function (todo) {\n      if (!this.editedTodo) {\n        return\n      }\n      this.editedTodo = null\n      todo.title = todo.title.trim();\n      if (!todo.title) {\n        this.removeTodo(todo);\n      }\n      this.notifyChange();\n    },\n\n    cancelEdit: function (todo) {\n      this.editedTodo = null\n      todo.title = this.beforeEditCache\n    },\n\n    removeCompleted: function () {\n      this.todos = filters.active(this.todos);\n      this.notifyChange();\n    }\n  },\n\n  // a custom directive to wait for the DOM to be updated\n  // before focusing on the input field.\n  // http://vuejs.org/guide/custom-directive.html\n  directives: {\n    'todo-focus': function (el, binding) {\n      if (binding.value) {\n        el.focus()\n      }\n    }\n  }\n})\n\n// handle routing\nfunction onHashChange() {\n  var visibility = window.location.hash.replace(/#\\/?/, '')\n  if (filters[visibility]) {\n    app.visibility = visibility\n  } else {\n    window.location.hash = ''\n    app.visibility = 'all'\n  }\n}\n\nwindow.addEventListener('hashchange', onHashChange)\nonHashChange()\n\n// mount\napp.$mount('.todoapp');"
  },
  {
    "path": "_examples/mvc/vuejs-todo-mvc/src/web/public/js/lib/.gitkeep",
    "content": ""
  },
  {
    "path": "_examples/mvc/websocket/browser/index.html",
    "content": "<html>\n\n<head>\n    <title>Online visitors MVC example</title>\n    <style>\n        body {\n            margin: 0;\n            font-family: -apple-system, \"San Francisco\", \"Helvetica Neue\", \"Noto\", \"Roboto\", \"Calibri Light\", sans-serif;\n            color: #212121;\n            font-size: 1.0em;\n            line-height: 1.6;\n        }\n\n        .container {\n            max-width: 750px;\n            margin: auto;\n            padding: 15px;\n        }\n\n        #online_visitors {\n            font-weight: bold;\n            font-size: 18px;\n        }\n    </style>\n</head>\n\n<body>\n    <div class=\"container\">\n        <span id=\"online_visitors\">1 online visitor</span>\n    </div>\n\n    <!-- the message's input -->\n    <input id=\"input\" type=\"text\" />\n\n    <!-- when clicked then a websocket event will be sent to the server, at this example we registered the 'chat' -->\n    <button id=\"sendBtn\" disabled>Send</button>\n\n    <!-- the messages will be shown here -->\n    <pre id=\"output\"></pre>\n    <!-- import the iris client-side library for browser from a CDN or locally.\n     However, `neffos.(min.)js` is a NPM package too so alternatively,\n     you can use it as dependency on your package.json and all nodejs-npm tooling become available:\n     see the \"browserify\" example for more-->\n    <script src=\"https://cdn.jsdelivr.net/npm/neffos.js@0.1.27/dist/neffos.min.js\"></script>\n    <script type=\"text/javascript\">\n        const wsURL = \"ws://localhost:8080/websocket\"\n        var outputTxt = document.getElementById(\"output\");\n        function addMessage(msg) {\n            outputTxt.innerHTML += msg + \"\\n\";\n        }\n\n        async function runExample() {\n            try {\n                const conn = await neffos.dial(wsURL, {\n                    default: { // \"default\" namespace.\n                        _OnNamespaceConnected: function (nsConn, msg) {\n                            if (nsConn.conn.wasReconnected()) {\n                                addMessage(\"re-connected after \" + nsConn.conn.reconnectTries.toString() + \" trie(s)\");\n                            }\n\n                            let inputTxt = document.getElementById(\"input\");\n                            let sendBtn = document.getElementById(\"sendBtn\");\n\n                            sendBtn.disabled = false;\n                            sendBtn.onclick = function () {\n                                const input = inputTxt.value;\n                                inputTxt.value = \"\";\n                                nsConn.emit(\"OnChat\", input);\n                                addMessage(\"Me: \" + input);\n                            };\n\n                            addMessage(\"connected to namespace: \" + msg.Namespace);\n                        },\n                        _OnNamespaceDisconnect: function (nsConn, msg) {\n                            addMessage(\"disconnected from namespace: \" + msg.Namespace);\n                        },\n                        OnChat: function (nsConn, msg) { // \"OnChat\" event.\n                            console.log(msg);\n\n                            addMessage(msg.Body);\n                        },\n                        OnVisit: function (nsConn, msg) {\n                            const newCount = Number(msg.Body); // or parseInt.\n                            console.log(\"visit websocket event with newCount of: \", newCount);\n\n                            var text = \"1 online visitor\";\n                            if (newCount > 1) {\n                                text = newCount + \" online visitors\";\n                            }\n                            document.getElementById(\"online_visitors\").innerHTML = text;\n                        },\n                    }\n                });\n\n                conn.connect(\"default\");\n            } catch (err) {\n                console.log(err)\n            }\n        }\n\n        runExample();\n    </script>\n\n</body>\n\n</html>"
  },
  {
    "path": "_examples/mvc/websocket/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"sync/atomic\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/mvc\"\n\t\"github.com/kataras/iris/v12/websocket\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.Logger().SetLevel(\"debug\")\n\n\t// load templates.\n\t// app.RegisterView(iris.HTML(\"./views\", \".html\"))\n\n\t// render the ./browser/index.html.\n\tapp.HandleDir(\"/\", iris.Dir(\"./browser\"))\n\n\twebsocketAPI := app.Party(\"/websocket\")\n\n\tm := mvc.New(websocketAPI)\n\tm.Register(\n\t\t&prefixedLogger{prefix: \"DEV\"},\n\t)\n\tm.HandleWebsocket(&websocketController{Namespace: \"default\", Age: 42, Otherstring: \"other string\"})\n\n\twebsocketServer := websocket.New(websocket.DefaultGorillaUpgrader, m)\n\n\twebsocketAPI.Get(\"/\", websocket.Handler(websocketServer))\n\t// http://localhost:8080\n\tapp.Listen(\":8080\")\n}\n\nvar visits uint64\n\nfunc increment() uint64 {\n\treturn atomic.AddUint64(&visits, 1)\n}\n\nfunc decrement() uint64 {\n\treturn atomic.AddUint64(&visits, ^uint64(0))\n}\n\ntype websocketController struct {\n\t*websocket.NSConn `stateless:\"true\"`\n\tNamespace         string\n\tAge               int\n\tOtherstring       string\n\n\tLogger LoggerService\n}\n\n// or\n// func (c *websocketController) Namespace() string {\n// \treturn \"default\"\n// }\n\nfunc (c *websocketController) OnNamespaceDisconnect(msg websocket.Message) error {\n\tc.Logger.Log(\"Disconnected\")\n\t// visits--\n\tnewCount := decrement()\n\t// This will call the \"OnVisit\" event on all clients, except the current one,\n\t// (it can't because it's left but for any case use this type of design)\n\tc.Conn.Server().Broadcast(nil, websocket.Message{\n\t\tNamespace: msg.Namespace,\n\t\tEvent:     \"OnVisit\",\n\t\tBody:      []byte(fmt.Sprintf(\"%d\", newCount)),\n\t})\n\n\treturn nil\n}\n\nfunc (c *websocketController) OnNamespaceConnected(msg websocket.Message) error {\n\t// println(\"Broadcast prefix is: \" + c.BroadcastPrefix)\n\tc.Logger.Log(\"Connected\")\n\n\t// visits++\n\tnewCount := increment()\n\n\t// This will call the \"OnVisit\" event on all clients, including the current one,\n\t// with the 'newCount' variable.\n\t//\n\t// There are many ways that u can do it and faster, for example u can just send a new visitor\n\t// and client can increment itself, but here we are just \"showcasing\" the websocket controller.\n\tc.Conn.Server().Broadcast(nil, websocket.Message{\n\t\tNamespace: msg.Namespace,\n\t\tEvent:     \"OnVisit\",\n\t\tBody:      []byte(fmt.Sprintf(\"%d\", newCount)),\n\t})\n\n\treturn nil\n}\n\nfunc (c *websocketController) OnChat(msg websocket.Message) error {\n\tctx := websocket.GetContext(c.Conn)\n\n\tctx.Application().Logger().Infof(\"[IP: %s] [ID: %s]  broadcast to other clients the message [%s]\",\n\t\tctx.RemoteAddr(), c, string(msg.Body))\n\n\tc.Conn.Server().Broadcast(c, msg)\n\n\treturn nil\n}\n\ntype LoggerService interface {\n\tLog(string)\n}\n\ntype prefixedLogger struct {\n\tprefix string\n}\n\nfunc (s *prefixedLogger) Log(msg string) {\n\tfmt.Printf(\"%s: %s\\n\", s.prefix, msg)\n}\n"
  },
  {
    "path": "_examples/mvc/websocket-auth/auth.yml",
    "content": "Headers: # required.\n  - \"Authorization\"\n  - \"X-Authorization\"\nCookie: # optional.\n  Name: \"iris_auth_cookie\"\n  Secure: false\n  Hash: \"D*G-KaPdSgUkXp2s5v8y/B?E(H+MbQeThWmYq3t6w9z$C&F)J@NcRfUjXn2r4u7x\" # length of 64 characters (512-bit).\n  Block: \"VkYp3s6v9y$B&E)H@McQfTjWmZq4t7w!\" # length of 32 characters (256-bit).\nKeys:\n  - ID: IRIS_AUTH_ACCESS # required.\n    Alg: EdDSA\n    MaxAge: 2h # 2 hours lifetime for access tokens. \n    Private: |+\n      -----BEGIN PRIVATE KEY-----\n      MC4CAQAwBQYDK2VwBCIEIFdZWoDdFny5SMnP9Fyfr8bafi/B527EVZh8JJjDTIFO\n      -----END PRIVATE KEY-----\n    Public: |+\n      -----BEGIN PUBLIC KEY-----\n      MCowBQYDK2VwAyEAzpgjKSr9E032DX+foiOxq1QDsbzjLxagTN+yVpGWZB4=\n      -----END PUBLIC KEY-----\n  - ID: IRIS_AUTH_REFRESH # optional. Good practise to have it though.\n    Alg: EdDSA\n    # 1 month lifetime for refresh tokens,\n    # after that period the user has to signin again.\n    MaxAge: 720h\n    Private: |+\n      -----BEGIN PRIVATE KEY-----\n      MC4CAQAwBQYDK2VwBCIEIHJ1aoIjA2sRp5eqGjGR3/UMucrHbBdBv9p8uwfzZ1KZ\n      -----END PRIVATE KEY-----\n    Public: |+\n      -----BEGIN PUBLIC KEY-----\n      MCowBQYDK2VwAyEAsKKAr+kDtfAqwG7cZdoEAfh9jHt9W8qi9ur5AA1KQAQ=\n      -----END PUBLIC KEY-----\n    # Example of setting a binary form of the encryption key for refresh tokens,\n    # it could be a \"string\" as well.\n    EncryptionKey: !!binary stSNLTu91YyihPxzeEOXKwGVMG00CjcC/68G8nMgmqA=\n"
  },
  {
    "path": "_examples/mvc/websocket-auth/browser/index.html",
    "content": "<html>\n\n<head>\n    <title>Online visitors MVC example</title>\n    <style>\n        body {\n            margin: 0;\n            font-family: -apple-system, \"San Francisco\", \"Helvetica Neue\", \"Noto\", \"Roboto\", \"Calibri Light\", sans-serif;\n            color: #212121;\n            font-size: 1.0em;\n            line-height: 1.6;\n        }\n\n        .container {\n            max-width: 750px;\n            margin: auto;\n            padding: 15px;\n        }\n\n        #online_visitors {\n            font-weight: bold;\n            font-size: 18px;\n        }\n    </style>\n</head>\n\n<body>\n    <div class=\"container\">\n        <span id=\"online_visitors\">1 online visitor</span>\n    </div>\n\n    <!-- the message's input -->\n    <input id=\"input\" type=\"text\" />\n\n    <!-- when clicked then a websocket event will be sent to the server, at this example we registered the 'chat' -->\n    <button id=\"sendBtn\" disabled>Send</button>\n\n    <!-- the messages will be shown here -->\n    <pre id=\"output\"></pre>\n    <!-- import the iris client-side library for browser from a CDN or locally.\n     However, `neffos.(min.)js` is a NPM package too so alternatively,\n     you can use it as dependency on your package.json and all nodejs-npm tooling become available:\n     see the \"browserify\" example for more-->\n    <script src=\"https://cdn.jsdelivr.net/npm/neffos.js@0.1.27/dist/neffos.min.js\"></script>\n    <script type=\"text/javascript\">\n        const wsURL = \"ws://localhost:8080/protected/ws\"\n        var outputTxt = document.getElementById(\"output\");\n        function addMessage(msg) {\n            outputTxt.innerHTML += msg + \"\\n\";\n        }\n\n        async function runExample() {\n            try {\n                const conn = await neffos.dial(wsURL, {\n                    default: { // \"default\" namespace.\n                        _OnNamespaceConnected: function (nsConn, msg) {\n                            if (nsConn.conn.wasReconnected()) {\n                                addMessage(\"re-connected after \" + nsConn.conn.reconnectTries.toString() + \" trie(s)\");\n                            }\n\n                            let inputTxt = document.getElementById(\"input\");\n                            let sendBtn = document.getElementById(\"sendBtn\");\n\n                            sendBtn.disabled = false;\n                            sendBtn.onclick = function () {\n                                const input = inputTxt.value;\n                                inputTxt.value = \"\";\n                                nsConn.emit(\"OnChat\", input);\n                                addMessage(\"Me: \" + input);\n                            };\n\n                            addMessage(\"connected to namespace: \" + msg.Namespace);\n                        },\n                        _OnNamespaceDisconnect: function (nsConn, msg) {\n                            addMessage(\"disconnected from namespace: \" + msg.Namespace);\n                        },\n                        OnChat: function (nsConn, msg) { // \"OnChat\" event.\n                            console.log(msg);\n\n                            addMessage(msg.Body);\n                        },\n                        OnVisit: function (nsConn, msg) {\n                            const newCount = Number(msg.Body); // or parseInt.\n                            console.log(\"visit websocket event with newCount of: \", newCount);\n\n                            var text = \"1 online visitor\";\n                            if (newCount > 1) {\n                                text = newCount + \" online visitors\";\n                            }\n                            document.getElementById(\"online_visitors\").innerHTML = text;\n                        },\n                    }\n                });\n\n                conn.connect(\"default\");\n            } catch (err) {\n                console.log(err)\n            }\n        }\n\n        runExample();\n    </script>\n\n</body>\n\n</html>"
  },
  {
    "path": "_examples/mvc/websocket-auth/main.go",
    "content": "//go:build go1.18\n// +build go1.18\n\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/auth\"\n\t\"github.com/kataras/iris/v12/mvc\"\n\t\"github.com/kataras/iris/v12/websocket\"\n)\n\n// $ go run .\nfunc main() {\n\tapp := newApp()\n\n\t// http://localhost:8080/signin (creds: kataras2006@hotmail.com 123456)\n\t// http://localhost:8080/protected\n\t// http://localhost:8080/signout\n\tapp.Listen(\":8080\")\n}\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\n\t// Auth part.\n\tapp.RegisterView(iris.Blocks(\"./views\", \".html\").\n\t\tLayoutDir(\"layouts\").\n\t\tLayout(\"main\"))\n\n\ts := auth.MustLoad[User](\"./auth.yml\")\n\ts.AddProvider(NewProvider())\n\n\tapp.Get(\"/signin\", renderSigninForm)\n\tapp.Post(\"/signin\", s.SigninHandler)\n\tapp.Get(\"/signout\", s.SignoutHandler)\n\t//\n\n\twebsocketAPI := app.Party(\"/protected\")\n\twebsocketAPI.Use(s.VerifyHandler())\n\twebsocketAPI.HandleDir(\"/\", iris.Dir(\"./browser\")) // render the ./browser/index.html.\n\n\twebsocketMVC := mvc.New(websocketAPI)\n\twebsocketMVC.HandleWebsocket(new(websocketController))\n\twebsocketServer := websocket.New(websocket.DefaultGorillaUpgrader, websocketMVC)\n\twebsocketAPI.Get(\"/ws\", s.VerifyHandler() /* optional */, websocket.Handler(websocketServer))\n\n\treturn app\n}\n\nfunc renderSigninForm(ctx iris.Context) {\n\tif err := ctx.View(\"signin\", iris.Map{\"Title\": \"Signin Page\"}); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n}\n\ntype websocketController struct {\n\t*websocket.NSConn `stateless:\"true\"`\n}\n\nfunc (c *websocketController) Namespace() string {\n\treturn \"default\"\n}\n\nfunc (c *websocketController) OnChat(msg websocket.Message) error {\n\tctx := websocket.GetContext(c.Conn)\n\tuser := auth.GetUser[User](ctx)\n\n\tmsg.Body = []byte(fmt.Sprintf(\"%s: %s\", user.Email, string(msg.Body)))\n\tc.Conn.Server().Broadcast(c, msg)\n\n\treturn nil\n}\n"
  },
  {
    "path": "_examples/mvc/websocket-auth/user.go",
    "content": "//go:build go1.18\n// +build go1.18\n\npackage main\n\ntype AccessRole uint16\n\nfunc (r AccessRole) Is(v AccessRole) bool {\n\treturn r&v != 0\n}\n\nfunc (r AccessRole) Allow(v AccessRole) bool {\n\treturn r&v >= v\n}\n\nconst (\n\tInvalidAccessRole AccessRole = 1 << iota\n\tRead\n\tWrite\n\tDelete\n\n\tOwner  = Read | Write | Delete\n\tMember = Read | Write\n)\n\ntype User struct {\n\tID    string     `json:\"id\"`\n\tEmail string     `json:\"email\"`\n\tRole  AccessRole `json:\"role\"`\n}\n\nfunc (u User) GetID() string {\n\treturn u.ID\n}\n"
  },
  {
    "path": "_examples/mvc/websocket-auth/user_provider.go",
    "content": "//go:build go1.18\n// +build go1.18\n\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/auth\"\n)\n\ntype Provider struct {\n\tdataset []User\n\n\tinvalidated    map[string]struct{} // key = token. Entry is blocked.\n\tinvalidatedAll map[string]int64    // key = user id, value = timestamp. Issued before is consider invalid.\n\tmu             sync.RWMutex\n}\n\nfunc NewProvider() *Provider {\n\treturn &Provider{\n\t\tdataset: []User{\n\t\t\t{\n\t\t\t\tID:    \"id-1\",\n\t\t\t\tEmail: \"kataras2006@hotmail.com\",\n\t\t\t\tRole:  Owner,\n\t\t\t},\n\t\t\t{\n\t\t\t\tID:    \"id-2\",\n\t\t\t\tEmail: \"example@example.com\",\n\t\t\t\tRole:  Member,\n\t\t\t},\n\t\t},\n\t\tinvalidated:    make(map[string]struct{}),\n\t\tinvalidatedAll: make(map[string]int64),\n\t}\n}\n\nfunc (p *Provider) Signin(ctx context.Context, username, password string) (User, error) { // fired on SigninHandler.\n\t// your database...\n\tfor _, user := range p.dataset {\n\t\tif user.Email == username {\n\t\t\treturn user, nil\n\t\t}\n\t}\n\n\treturn User{}, fmt.Errorf(\"user not found\")\n}\n\nfunc (p *Provider) ValidateToken(ctx context.Context, standardClaims auth.StandardClaims, u User) error { // fired on VerifyHandler.\n\t// your database and checks of blocked tokens...\n\n\t// check for specific token ids.\n\tp.mu.RLock()\n\t_, tokenBlocked := p.invalidated[standardClaims.ID]\n\tif !tokenBlocked {\n\t\t// this will disallow refresh tokens with issuer as the blocked access token as well.\n\t\tif standardClaims.Issuer != \"\" {\n\t\t\t_, tokenBlocked = p.invalidated[standardClaims.Issuer]\n\t\t}\n\t}\n\tp.mu.RUnlock()\n\n\tif tokenBlocked {\n\t\treturn fmt.Errorf(\"token was invalidated\")\n\t}\n\t//\n\n\t// check all tokens issuet before the \"InvalidateToken\" method was fired for this user.\n\tp.mu.RLock()\n\tts, oldUserBlocked := p.invalidatedAll[u.ID]\n\tp.mu.RUnlock()\n\n\tif oldUserBlocked && standardClaims.IssuedAt <= ts {\n\t\treturn fmt.Errorf(\"token was invalidated\")\n\t}\n\t//\n\n\treturn nil // else valid.\n}\n\nfunc (p *Provider) InvalidateToken(ctx context.Context, standardClaims auth.StandardClaims, u User) error { // fired on SignoutHandler.\n\t// invalidate this specific token.\n\tp.mu.Lock()\n\tp.invalidated[standardClaims.ID] = struct{}{}\n\tp.mu.Unlock()\n\n\treturn nil\n}\n\nfunc (p *Provider) InvalidateTokens(ctx context.Context, u User) error { // fired on SignoutAllHandler.\n\t// invalidate all previous tokens came from \"u\".\n\tp.mu.Lock()\n\tp.invalidatedAll[u.ID] = time.Now().Unix()\n\tp.mu.Unlock()\n\n\treturn nil\n}\n"
  },
  {
    "path": "_examples/mvc/websocket-auth/views/layouts/main.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>{{ if .Title }}{{ .Title }}{{ else }}Default Main Title{{ end }}</title>\n</head>\n<style>\n    body {\n        margin: 0;\n        display: flex;\n        min-height: 100vh;\n        flex-direction: column;\n    }\n    main {\n        display: block;\n        flex: 1 0 auto;\n    }\n    .container {\n        max-width: 500px;\n        margin: auto;\n    }\n</style>\n<body>\n    <div class=\"container\">\n        <main>{{ template \"content\" . }}</main>\n        <footer style=\"position: fixed; bottom: 0; width: 100%;\">{{ partial \"partials/footer\" . }}</footer>\n    </div>\n</body>\n</html>"
  },
  {
    "path": "_examples/mvc/websocket-auth/views/partials/footer.html",
    "content": "<i>Iris Web Framework &copy; 2023</i>"
  },
  {
    "path": "_examples/mvc/websocket-auth/views/signin.html",
    "content": "<div class=\"user_signin\">\n    <form action=\"\" method=\"post\">\n        <label for=\"username\">Email:</label>\n        <input name=\"username\" type=\"email\" />\n        <label for=\"password\">Password:</label>\n        <input name=\"password\" type=\"password\" />\n        <input type=\"submit\" value=\"Sign in\" />\n    </form>\n</div>"
  },
  {
    "path": "_examples/pprof/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n\n\t\"github.com/kataras/iris/v12/middleware/pprof\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.HTML(\"<h1> Please click <a href='/debug/pprof'>here</a>\")\n\t})\n\n\tp := pprof.New()\n\tapp.Any(\"/debug/pprof\", p)\n\tapp.Any(\"/debug/pprof/{action:path}\", p)\n\t//                              ___________\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/project/README.md",
    "content": "# Project Structure\n\n```sh\n$ go run main.go --config=server.yml\n```"
  },
  {
    "path": "_examples/project/api/configuration.go",
    "content": "package api\n\nimport (\n\t\"os\"\n\n\t\"github.com/kataras/iris/v12\"\n\n\t\"gopkg.in/yaml.v3\"\n)\n\n// Configuration holds the necessary information\n// for our server, including the Iris one.\ntype Configuration struct {\n\tServerName string `yaml:\"ServerName\"`\n\tEnv        string `yaml:\"Env\"`\n\t// The server's host, if empty, defaults to 0.0.0.0\n\tHost string `yaml:\"Host\"`\n\t// The server's port, e.g. 80\n\tPort int `yaml:\"Port\"`\n\t// If not empty runs under tls with this domain using letsencrypt.\n\tDomain string `yaml:\"Domain\"`\n\t// Enables write response and read request compression.\n\tEnableCompression bool `yaml:\"EnableCompression\"`\n\t// Defines the \"Access-Control-Allow-Origin\" header of the CORS middleware.\n\t// Many can be separated by comma.\n\t// Defaults to \"*\" (allow all).\n\tAllowOrigin string `yaml:\"AllowOrigin\"`\n\t// If not empty a request logger is registered,\n\t// note that this will cost a lot in performance, use it only for debug.\n\tRequestLog string `yaml:\"RequestLog\"`\n\t// The database connection string.\n\tConnString string `yaml:\"ConnString\"`\n\t// Iris specific configuration.\n\tIris iris.Configuration `yaml:\"Iris\"`\n}\n\n// BindFile binds the yaml file's contents to this Configuration.\nfunc (c *Configuration) BindFile(filename string) error {\n\tcontents, err := os.ReadFile(filename)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn yaml.Unmarshal(contents, c)\n}\n"
  },
  {
    "path": "_examples/project/api/router.go",
    "content": "package api\n\nimport (\n\t\"time\"\n\n\t\"github.com/username/project/api/users\"\n\t\"github.com/username/project/pkg/database\"\n\t\"github.com/username/project/user\"\n\n\t\"github.com/kataras/iris/v12/middleware/modrevision\"\n)\n\n// buildRouter is the most important part of your server.\n// All root endpoints are registered here.\nfunc (srv *Server) buildRouter() {\n\t// Add a simple health route.\n\tsrv.Any(\"/health\", modrevision.New(modrevision.Options{\n\t\tServerName:   srv.config.ServerName,\n\t\tEnv:          srv.config.Env,\n\t\tDeveloper:    \"kataras\",\n\t\tTimeLocation: time.FixedZone(\"Greece/Athens\", 7200),\n\t}))\n\n\tapi := srv.Party(\"/api\")\n\tapi.RegisterDependency(\n\t\tdatabase.Open(srv.config.ConnString),\n\t\tuser.NewRepository,\n\t)\n\n\tapi.PartyConfigure(\"/user\", new(users.API))\n}\n"
  },
  {
    "path": "_examples/project/api/server.go",
    "content": "package api\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/username/project/pkg/http/handlers\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/middleware/accesslog\"\n\n\t\"github.com/kataras/golog\"\n)\n\n// Server is a wrapper of the main iris application and our project's custom configuration fields.\ntype Server struct {\n\t*iris.Application\n\tconfig Configuration\n\n\t// Here you can keep an instance of the database too.\n\t// db      *mydatabase_pkg.DB\n\tclosers []func() // See `AddCloser` method.\n}\n\n// NewServer initializes a new HTTP/2 server.\n// Use its Run/Listen methods to start it based on network options.\nfunc NewServer(c Configuration) *Server {\n\tapp := iris.New().SetName(c.ServerName)\n\tapp.Configure(iris.WithConfiguration(c.Iris), iris.WithLowercaseRouting)\n\n\tsrv := &Server{\n\t\tApplication: app,\n\t\tconfig:      c,\n\t}\n\n\tif err := srv.prepare(); err != nil {\n\t\tsrv.Logger().Fatal(err)\n\t\treturn nil\n\t}\n\n\treturn srv\n}\n\nfunc (srv *Server) prepare() error {\n\t// Here you can register the database instance\n\t// and prepare any project-relative fields.\n\n\tif srv.Logger().Level == golog.DebugLevel {\n\t\tsrv.registerDebugFeatures()\n\t}\n\n\tsrv.registerMiddlewares()\n\tsrv.buildRouter()\n\treturn nil\n}\n\n// registers application-level middlewares.\nfunc (srv *Server) registerMiddlewares() {\n\tif srv.config.RequestLog != \"\" {\n\t\tsrv.registerAccessLogger()\n\t}\n\n\tsrv.UseRouter(handlers.CORS(srv.config.AllowOrigin))\n\n\tif srv.config.EnableCompression {\n\t\tsrv.Use(iris.Compression)\n\t}\n}\n\nfunc (srv *Server) registerDebugFeatures() {}\n\nfunc (srv *Server) registerAccessLogger() {\n\t// Initialize a new request access log middleware,\n\t// note that we use unbuffered data so we can have the results as fast as possible,\n\t// this has its cost use it only on debug.\n\t// Also, in the future see the iris example to\n\t// enable log rotation (date eand filesize-based files).\n\tac := accesslog.FileUnbuffered(srv.config.RequestLog)\n\n\t// The default configuration:\n\tac.Delim = '|'\n\tac.TimeFormat = \"2006-01-02 15:04:05\"\n\tac.Async = false\n\tac.IP = true\n\tac.BytesReceivedBody = true\n\tac.BytesSentBody = true\n\tac.BytesReceived = false\n\tac.BytesSent = false\n\tac.BodyMinify = false\n\tac.RequestBody = true\n\tac.ResponseBody = false\n\tac.KeepMultiLineError = true\n\tac.PanicLog = accesslog.LogHandler\n\n\t// Default line format if formatter is missing:\n\t// Time|Latency|Code|Method|Path|IP|Path Params Query Fields|Bytes Received|Bytes Sent|Request|Response|\n\t//\n\t// Set Custom Formatter:\n\tac.SetFormatter(&accesslog.JSON{\n\t\tIndent:    \"  \",\n\t\tHumanTime: true,\n\t})\n\n\t// ac.SetFormatter(&accesslog.CSV{})\n\t// ac.SetFormatter(&accesslog.Template{Text: \"{{.Code}}\"})\n\n\tsrv.UseRouter(ac.Handler)\n}\n\n// Start runs the server on the TCP network address \"0.0.0.0:port\" which\n// handles HTTP/1.1 & 2 requests on incoming connections.\nfunc (srv *Server) Start() error {\n\tif srv.config.Domain != \"\" {\n\t\tsrv.config.Port = 80 // not required but let's force-modify it.\n\t\treturn srv.Application.Run(iris.AutoTLS(\n\t\t\t\":443\",\n\t\t\tsrv.config.Domain,\n\t\t\t\"kataras2006@hotmail.com\",\n\t\t))\n\t}\n\n\tsrv.ConfigureHost(func(su *iris.Supervisor) {\n\t\t// Set timeouts. More than enough, normally we use 20-30 seconds.\n\t\tsu.Server.ReadTimeout = 5 * time.Minute\n\t\tsu.Server.WriteTimeout = 5 * time.Minute\n\t\tsu.Server.IdleTimeout = 10 * time.Minute\n\t\tsu.Server.ReadHeaderTimeout = 2 * time.Minute\n\t})\n\n\taddr := fmt.Sprintf(\"%s:%d\", srv.config.Host, srv.config.Port)\n\treturn srv.Listen(addr)\n}\n\n// AddCloser adds one or more function that should be called on\n// manual server shutdown or OS interrupt signals.\nfunc (srv *Server) AddCloser(closers ...func()) {\n\tfor _, closer := range closers {\n\t\tif closer == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Terminate any opened connections on OS interrupt signals.\n\t\tiris.RegisterOnInterrupt(closer)\n\t}\n\n\tsrv.closers = append(srv.closers, closers...)\n}\n\n// Close gracefully terminates the HTTP server and calls the closers afterwards.\nfunc (srv *Server) Close() error {\n\tctx, cancelCtx := context.WithTimeout(context.Background(), 5*time.Second)\n\terr := srv.Shutdown(ctx)\n\tcancelCtx()\n\n\tfor _, closer := range srv.closers {\n\t\tif closer == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tcloser()\n\t}\n\n\treturn err\n}\n"
  },
  {
    "path": "_examples/project/api/users/api.go",
    "content": "package users\n\nimport (\n\t\"github.com/username/project/user\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\ntype API struct {\n\tUsers user.Repository // exported field so api/router.go#api.RegisterDependency can bind it.\n}\n\nfunc (api *API) Configure(r iris.Party) {\n\tr.Post(\"/signup\", api.signUp)\n\tr.Post(\"/signin\", api.signIn)\n\t// Add middlewares such as user verification by bearer token here.\n\n\t// Authenticated routes...\n\tr.Get(\"/\", api.getInfo)\n}\n\nfunc (api *API) getInfo(ctx iris.Context) {\n\tctx.WriteString(\"...\")\n}\n\nfunc (api *API) signUp(ctx iris.Context) {}\nfunc (api *API) signIn(ctx iris.Context) {}\n"
  },
  {
    "path": "_examples/project/cmd/cmd.go",
    "content": "package cmd\n\nimport (\n\t\"github.com/username/project/api\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/spf13/cobra\"\n)\n\nconst defaultConfigFilename = \"server.yml\"\n\nvar serverConfig api.Configuration\n\n// New returns a new CLI app.\n// Build with:\n// $ go build -ldflags=\"-s -w\"\nfunc New() *cobra.Command {\n\tconfigFile := defaultConfigFilename\n\n\trootCmd := &cobra.Command{\n\t\tUse:                        \"project\",\n\t\tShort:                      \"Command line interface for project.\",\n\t\tLong:                       \"The root command will start the HTTP server.\",\n\t\tVersion:                    \"v0.0.1\",\n\t\tSilenceErrors:              true,\n\t\tSilenceUsage:               true,\n\t\tTraverseChildren:           true,\n\t\tSuggestionsMinimumDistance: 1,\n\t\tPersistentPreRunE: func(cmd *cobra.Command, args []string) error {\n\t\t\t// Read configuration from file before any of the commands run functions.\n\t\t\treturn serverConfig.BindFile(configFile)\n\t\t},\n\t\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\t\treturn startServer()\n\t\t},\n\t}\n\n\thelpTemplate := HelpTemplate{\n\t\tBuildRevision:        iris.BuildRevision,\n\t\tBuildTime:            iris.BuildTime,\n\t\tShowGoRuntimeVersion: true,\n\t}\n\trootCmd.SetHelpTemplate(helpTemplate.String())\n\n\t// Shared flags.\n\tflags := rootCmd.PersistentFlags()\n\tflags.StringVar(&configFile, \"config\", configFile, \"--config=server.yml a filepath which contains the YAML config format\")\n\n\t// Subcommands here.\n\t// rootCmd.AddCommand(...)\n\n\treturn rootCmd\n}\n\nfunc startServer() error {\n\tsrv := api.NewServer(serverConfig)\n\treturn srv.Start()\n}\n"
  },
  {
    "path": "_examples/project/cmd/help.go",
    "content": "package cmd\n\nimport (\n\t\"fmt\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n)\n\n// HelpTemplate is the structure which holds the necessary information for the help command.\ntype HelpTemplate struct {\n\tBuildTime            string\n\tBuildRevision        string\n\tShowGoRuntimeVersion bool\n\n\tTemplate fmt.Stringer\n}\n\nfunc (h HelpTemplate) String() string {\n\ttmpl := `{{with (or .Long .Short)}}{{. | trimTrailingWhitespaces}}\n\t{{end}}{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}`\n\n\tif h.BuildRevision != \"\" {\n\t\tbuildTitle := \">>>> build\" // if we ever want an emoji, there is one: \\U0001f4bb\n\t\ttab := strings.Repeat(\" \", len(buildTitle))\n\n\t\tn, _ := strconv.ParseInt(h.BuildTime, 10, 64)\n\t\tbuildTimeStr := time.Unix(n, 0).Format(time.UnixDate)\n\n\t\tbuildTmpl := fmt.Sprintf(\"\\n%s\\n\", buildTitle) +\n\t\t\tfmt.Sprintf(\"%s revision      %s\\n\", tab, h.BuildRevision) +\n\t\t\tfmt.Sprintf(\"%s datetime      %s\\n\", tab, buildTimeStr)\n\n\t\tif h.ShowGoRuntimeVersion {\n\t\t\tbuildTmpl += fmt.Sprintf(\"%s runtime       %s\\n\", tab, runtime.Version())\n\t\t}\n\n\t\ttmpl += buildTmpl\n\t}\n\n\treturn tmpl\n}\n"
  },
  {
    "path": "_examples/project/go.mod",
    "content": "module github.com/username/project\n\ngo 1.25\n\nrequire (\n\tgithub.com/kataras/golog v0.1.12\n\tgithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd\n\tgithub.com/spf13/cobra v1.10.2\n\tgopkg.in/yaml.v3 v3.0.1\n)\n\nrequire (\n\tgithub.com/BurntSushi/toml v1.6.0 // indirect\n\tgithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect\n\tgithub.com/CloudyKit/jet/v6 v6.3.1 // indirect\n\tgithub.com/Joker/jade v1.1.3 // indirect\n\tgithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 // indirect\n\tgithub.com/andybalholm/brotli v1.2.0 // indirect\n\tgithub.com/aymerick/douceur v0.2.0 // indirect\n\tgithub.com/fatih/structs v1.1.0 // indirect\n\tgithub.com/flosch/pongo2/v4 v4.0.2 // indirect\n\tgithub.com/golang/snappy v1.0.0 // indirect\n\tgithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a // indirect\n\tgithub.com/google/uuid v1.6.0 // indirect\n\tgithub.com/gorilla/css v1.0.1 // indirect\n\tgithub.com/inconshreveable/mousetrap v1.1.0 // indirect\n\tgithub.com/iris-contrib/schema v0.0.6 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/json-iterator/go v1.1.12 // indirect\n\tgithub.com/kataras/blocks v0.0.8 // indirect\n\tgithub.com/kataras/pio v0.0.13 // indirect\n\tgithub.com/kataras/sitemap v0.0.6 // indirect\n\tgithub.com/kataras/tunnel v0.0.4 // indirect\n\tgithub.com/klauspost/compress v1.18.2 // indirect\n\tgithub.com/kr/pretty v0.3.1 // indirect\n\tgithub.com/mailgun/raymond/v2 v2.0.48 // indirect\n\tgithub.com/mailru/easyjson v0.9.1 // indirect\n\tgithub.com/microcosm-cc/bluemonday v1.0.27 // indirect\n\tgithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect\n\tgithub.com/modern-go/reflect2 v1.0.2 // indirect\n\tgithub.com/rogpeppe/go-internal v1.10.0 // indirect\n\tgithub.com/russross/blackfriday/v2 v2.1.0 // indirect\n\tgithub.com/schollz/closestmatch v2.1.0+incompatible // indirect\n\tgithub.com/sirupsen/logrus v1.8.1 // indirect\n\tgithub.com/spf13/pflag v1.0.9 // indirect\n\tgithub.com/stretchr/testify v1.11.1 // indirect\n\tgithub.com/tdewolff/minify/v2 v2.24.8 // indirect\n\tgithub.com/tdewolff/parse/v2 v2.8.5 // indirect\n\tgithub.com/valyala/bytebufferpool v1.0.0 // indirect\n\tgithub.com/vmihailenco/msgpack/v5 v5.4.1 // indirect\n\tgithub.com/vmihailenco/tagparser/v2 v2.0.0 // indirect\n\tgithub.com/yosssi/ace v0.0.5 // indirect\n\tgolang.org/x/crypto v0.47.0 // indirect\n\tgolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 // indirect\n\tgolang.org/x/net v0.49.0 // indirect\n\tgolang.org/x/sys v0.40.0 // indirect\n\tgolang.org/x/text v0.33.0 // indirect\n\tgolang.org/x/time v0.14.0 // indirect\n\tgoogle.golang.org/protobuf v1.36.11 // indirect\n\tgopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect\n\tgopkg.in/ini.v1 v1.67.0 // indirect\n)\n"
  },
  {
    "path": "_examples/project/go.sum",
    "content": "github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=\ngithub.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=\ngithub.com/CloudyKit/jet/v6 v6.3.1 h1:6IAo5Cx21xrHVaR8zzXN5gJatKV/wO7Nf6bfCnCSbUw=\ngithub.com/CloudyKit/jet/v6 v6.3.1/go.mod h1:lf8ksdNsxZt7/yH/3n4vJQWA9RUq4wpaHtArHhGVMOw=\ngithub.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc=\ngithub.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=\ngithub.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=\ngithub.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 h1:dG7/gLroJGht/jSQtHiLvT48Hxn+crbmvyItZC8cWOs=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05/go.mod h1:NYezi6wtnJtBm5btoprXc5SvAdqH0XTXWnUup0MptAI=\ngithub.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=\ngithub.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=\ngithub.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=\ngithub.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=\ngithub.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=\ngithub.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=\ngithub.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=\ngithub.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=\ngithub.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=\ngithub.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw=\ngithub.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8=\ngithub.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=\ngithub.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=\ngithub.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=\ngithub.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a h1:l7A0loSszR5zHd/qK53ZIHMO8b3bBSmENnQ6eKnUT0A=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=\ngithub.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=\ngithub.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=\ngithub.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=\ngithub.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=\ngithub.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=\ngithub.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=\ngithub.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2 h1:T9THsdP1woyAqKHwjkEsbCnMefsAFvk8iJJKokcJ3Go=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2/go.mod h1:JLDgIqnFy5loDSUv1OA2j0mb6p/rDhiCqigP22Uq9xE=\ngithub.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw=\ngithub.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=\ngithub.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=\ngithub.com/kataras/blocks v0.0.8 h1:MrpVhoFTCR2v1iOOfGng5VJSILKeZZI+7NGfxEh3SUM=\ngithub.com/kataras/blocks v0.0.8/go.mod h1:9Jm5zx6BB+06NwA+OhTbHW1xkMOYxahnqTN5DveZ2Yg=\ngithub.com/kataras/golog v0.1.12 h1:Bu7I/G4ilJlbfzjmU39O9N+2uO1pBcMK045fzZ4ytNg=\ngithub.com/kataras/golog v0.1.12/go.mod h1:wrGSbOiBqbQSQznleVNX4epWM8rl9SJ/rmEacl0yqy4=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd h1:oMsQgqKACbUdlaIWnN+EZzzAnHkw90hE/y/R0BSij2U=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd/go.mod h1:yucjtDl9Vn3OctWM1fi0MuM9OckemC7kEreFa9QtPF8=\ngithub.com/kataras/pio v0.0.13 h1:x0rXVX0fviDTXOOLOmr4MUxOabu1InVSTu5itF8CXCM=\ngithub.com/kataras/pio v0.0.13/go.mod h1:k3HNuSw+eJ8Pm2lA4lRhg3DiCjVgHlP8hmXApSej3oM=\ngithub.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY=\ngithub.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4=\ngithub.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA=\ngithub.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw=\ngithub.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=\ngithub.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=\ngithub.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=\ngithub.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8=\ngithub.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=\ngithub.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=\ngithub.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=\ngithub.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=\ngithub.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=\ngithub.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=\ngithub.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=\ngithub.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=\ngithub.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=\ngithub.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=\ngithub.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=\ngithub.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=\ngithub.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=\ngithub.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk=\ngithub.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=\ngithub.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=\ngithub.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=\ngithub.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=\ngithub.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=\ngithub.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=\ngithub.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=\ngithub.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/tdewolff/minify/v2 v2.24.8 h1:58/VjsbevI4d5FGV0ZSuBrHMSSkH4MCH0sIz/eKIauE=\ngithub.com/tdewolff/minify/v2 v2.24.8/go.mod h1:0Ukj0CRpo/sW/nd8uZ4ccXaV1rEVIWA3dj8U7+Shhfw=\ngithub.com/tdewolff/parse/v2 v2.8.5 h1:ZmBiA/8Do5Rpk7bDye0jbbDUpXXbCdc3iah4VeUvwYU=\ngithub.com/tdewolff/parse/v2 v2.8.5/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=\ngithub.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE=\ngithub.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=\ngithub.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=\ngithub.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=\ngithub.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=\ngithub.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=\ngithub.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=\ngithub.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=\ngithub.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA=\ngithub.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0=\ngithub.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=\ngithub.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=\ngithub.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngo.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=\ngolang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=\ngolang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=\ngolang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=\ngolang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=\ngolang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=\ngolang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=\ngolang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=\ngolang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=\ngoogle.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=\ngopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nmoul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs=\nmoul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE=\n"
  },
  {
    "path": "_examples/project/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/username/project/cmd\"\n)\n\nfunc main() {\n\tapp := cmd.New()\n\tif err := app.Execute(); err != nil {\n\t\tfmt.Println(err)\n\t\tos.Exit(1)\n\t}\n}\n"
  },
  {
    "path": "_examples/project/pkg/database/database.go",
    "content": "package database\n\ntype DB struct {\n\t/* ... */\n}\n\nfunc Open(connString string) *DB {\n\treturn &DB{}\n}\n"
  },
  {
    "path": "_examples/project/pkg/http/handlers/cors.go",
    "content": "package handlers\n\nimport \"github.com/kataras/iris/v12\"\n\n// CORS set ups a cors allow-all.\n// We may need to edit it before deployment.\nfunc CORS(allowedOrigin string) iris.Handler { // or \"github.com/iris-contrib/middleware/cors\"\n\tif allowedOrigin == \"\" {\n\t\tallowedOrigin = \"*\"\n\t}\n\n\treturn func(ctx iris.Context) {\n\t\tctx.Header(\"Access-Control-Allow-Origin\", allowedOrigin)\n\t\tctx.Header(\"Access-Control-Allow-Credentials\", \"true\")\n\t\t// July 2021 Mozzila updated the following document: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy\n\t\tctx.Header(\"Referrer-Policy\", \"no-referrer-when-downgrade\")\n\t\tctx.Header(\"Access-Control-Expose-Headers\", \"*, Authorization, X-Authorization\")\n\t\tif ctx.Method() == iris.MethodOptions {\n\t\t\tctx.Header(\"Access-Control-Allow-Methods\", \"*\")\n\t\t\tctx.Header(\"Access-Control-Allow-Headers\", \"*\")\n\t\t\tctx.Header(\"Access-Control-Max-Age\", \"86400\")\n\t\t\tctx.StatusCode(iris.StatusNoContent)\n\t\t\treturn\n\t\t}\n\n\t\tctx.Next()\n\t}\n}\n"
  },
  {
    "path": "_examples/project/server.yml",
    "content": "ServerName: My Project\nEnv: development\nHost: 0.0.0.0\nPort: 80\nEnableCompression: true\nAllowOrigin: \"*\"\nIris:\n  # Defaults to info.\n  LogLevel: info\n  # Defaults to false.\n  EnableOptimizations: true\n  # Defaults to empty.\n  RemoteAddrHeaders:\n  - \"X-Real-Ip\"\n  - \"X-Forwarded-For\"\n  - \"CF-Connecting-IP\"\n  - \"True-Client-Ip\"\n  - \"X-Appengine-Remote-Addr\"\n"
  },
  {
    "path": "_examples/project/user/repository.go",
    "content": "package user\n\nimport \"github.com/username/project/pkg/database\"\n\ntype Repository interface { // Repo methods here...\n}\n\ntype repo struct { // Hold database instance here: e.g.\n\tdb *database.DB\n}\n\nfunc NewRepository(db *database.DB) Repository {\n\treturn &repo{db: db}\n}\n"
  },
  {
    "path": "_examples/project/user/user.go",
    "content": "package user\n\ntype User struct { /* ... */\n}\n"
  },
  {
    "path": "_examples/recover/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n\n\t\"github.com/kataras/iris/v12/middleware/recover\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.Use(recover.New())\n\n\ti := 0\n\t// let's simulate a panic every next request\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\ti++\n\t\tif i%2 == 0 {\n\t\t\tpanic(\"a panic here\")\n\t\t}\n\t\tctx.Writef(\"Hello, refresh one time more to get panic!\")\n\t})\n\n\t// http://localhost:8080, refresh it 5-6 times.\n\tapp.Listen(\":8080\")\n}\n\n// Note:\n// app := iris.Default() instead of iris.New() makes use of the recovery middleware automatically.\n"
  },
  {
    "path": "_examples/recover/panic-and-custom-error-handler-with-compression/main.go",
    "content": "package main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n\t//\t\"github.com/kataras/iris/v12/context\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.UseRouter(iris.Compression)\n\tapp.UseRouter(myErrorHandler)\n\n\tapp.Get(\"/\", handler)\n\n\tapp.Listen(\":8080\")\n}\n\nfunc myErrorHandler(ctx iris.Context) {\n\trecorder := ctx.Recorder()\n\n\tdefer func() {\n\t\tvar err error\n\n\t\tif v := recover(); v != nil { // panic\n\t\t\tif panicErr, ok := v.(error); ok {\n\t\t\t\terr = panicErr\n\t\t\t} else {\n\t\t\t\terr = errors.New(fmt.Sprint(v))\n\t\t\t}\n\t\t} else { // custom error.\n\t\t\terr = ctx.GetErr()\n\t\t}\n\n\t\tif err != nil {\n\t\t\t// To keep compression after reset:\n\t\t\t// clear body and any headers created between recorder and handler.\n\t\t\trecorder.ResetBody()\n\t\t\trecorder.ResetHeaders()\n\t\t\t//\n\n\t\t\t// To disable compression after reset:\n\t\t\t// recorder.Reset()\n\t\t\t// recorder.ResponseWriter.(*context.CompressResponseWriter).Disabled = true\n\t\t\t//\n\n\t\t\tctx.StopWithJSON(iris.StatusInternalServerError, iris.Map{\n\t\t\t\t\"message\": err.Error(),\n\t\t\t})\n\t\t}\n\t}()\n\n\tctx.Next()\n}\n\nfunc handler(ctx iris.Context) {\n\tctx.WriteString(\"Content may fall\")\n\tctx.Header(\"X-Test\", \"value\")\n\n\t// ctx.SetErr(fmt.Errorf(\"custom error message\"))\n\tpanic(\"errr!\")\n}\n"
  },
  {
    "path": "_examples/request-body/form-query-headers-params-decoder/main.go",
    "content": "// package main contains an example on how to register a custom decoder\n// for a custom type when using the `ReadQuery/ReadParams/ReadHeaders/ReadForm` methods.\n//\n// Let's take for example the mongo-driver/primite.ObjectID:\n//\n// ObjectID is type ObjectID [12]byte.\n// You have to register a converter for that custom type.\n// ReadJSON works because the ObjectID has a MarshalJSON method\n// which the encoding/json pakcage uses as a converter.\n// See here: https://godoc.org/go.mongodb.org/mongo-driver/bson/primitive#ObjectID.MarshalJSON.\n//\n// To register a converter import the github.com/iris-contrib/schema and call\n// schema.Query.RegisterConverter(value any, converterFunc Converter).\n//\n// The Converter is just a type of func(string) reflect.Value.\n//\n// There is another way, but requires introducing a custom type which will wrap the mongo's ObjectID\n// and implements the encoding.TextUnmarshaler e.g.\n// func(id *ID) UnmarshalText(text []byte) error){ ...}.\npackage main\n\nimport (\n\t\"reflect\"\n\n\t\"go.mongodb.org/mongo-driver/bson/primitive\"\n\n\t\"github.com/iris-contrib/schema\"\n\t\"github.com/kataras/iris/v12\"\n)\n\ntype MyType struct {\n\tID primitive.ObjectID `url:\"id\"`\n}\n\nfunc main() {\n\t// Register on initialization.\n\tschema.Query.RegisterConverter(primitive.ObjectID{}, func(value string) reflect.Value {\n\t\tid, err := primitive.ObjectIDFromHex(value)\n\t\tif err != nil {\n\t\t\treturn reflect.Value{}\n\t\t}\n\t\treturn reflect.ValueOf(id)\n\t})\n\n\tapp := iris.New()\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tvar t MyType\n\t\terr := ctx.ReadQuery(&t)\n\t\tif err != nil && !iris.IsErrPath(err) {\n\t\t\tctx.StopWithError(iris.StatusInternalServerError, err)\n\t\t\treturn\n\t\t}\n\n\t\tctx.Writef(\"MyType.ID: %q\", t.ID.Hex())\n\t})\n\n\t// http://localhost:8080?id=507f1f77bcf86cd799439011\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/request-body/read-body/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := newApp()\n\t// See main_test.go for usage.\n\tapp.Listen(\":8080\")\n}\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\t// To automatically decompress using gzip:\n\t// app.Use(iris.GzipReader)\n\n\tapp.Use(setAllowedResponses)\n\n\tapp.Post(\"/\", readBody)\n\n\treturn app\n}\n\ntype payload struct {\n\tMessage string `json:\"message\" xml:\"message\" msgpack:\"message\" yaml:\"Message\" url:\"message\" form:\"message\"`\n}\n\nfunc readBody(ctx iris.Context) {\n\tvar p payload\n\n\t// Bind request body to \"p\" depending on the content-type that client sends the data,\n\t// e.g. JSON, XML, YAML, MessagePack, Protobuf, Form and URL Query.\n\terr := ctx.ReadBody(&p)\n\tif err != nil {\n\t\tctx.StopWithProblem(iris.StatusBadRequest,\n\t\t\tiris.NewProblem().Title(\"Parser issue\").Detail(err.Error()))\n\t\treturn\n\t}\n\n\t// For the sake of the example, log the received payload.\n\tctx.Application().Logger().Infof(\"Received: %#+v\", p)\n\n\t// Send back the payload depending on the accept content type and accept-encoding of the client,\n\t// e.g. JSON, XML and so on.\n\tctx.Negotiate(p)\n}\n\nfunc setAllowedResponses(ctx iris.Context) {\n\t// Indicate that the Server can send JSON, XML, YAML and MessagePack for this request.\n\tctx.Negotiation().JSON().XML().YAML().MsgPack()\n\t// Add more, allowed by the server format of responses, mime types here...\n\n\t// If client is missing an \"Accept: \" header then default it to JSON.\n\tctx.Negotiation().Accept.JSON()\n\n\tctx.Next()\n}\n"
  },
  {
    "path": "_examples/request-body/read-body/main_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nfunc TestReadBodyAndNegotiate(t *testing.T) {\n\tapp := newApp()\n\n\te := httptest.New(t, app)\n\n\tvar (\n\t\texpectedPayload        = payload{Message: \"a message\"}\n\t\texpectedMsgPackPayload = \"\\x81\\xa7message\\xa9a message\"\n\t\texpectedXMLPayload     = `<payload><message>a message</message></payload>`\n\t\texpectedYAMLPayload    = \"Message: a message\\n\"\n\t)\n\n\t// Test send JSON and receive JSON.\n\te.POST(\"/\").WithJSON(expectedPayload).Expect().Status(httptest.StatusOK).\n\t\tJSON().IsEqual(expectedPayload)\n\n\t// Test send Form and receive XML.\n\te.POST(\"/\").WithForm(expectedPayload).\n\t\tWithHeader(\"Accept\", \"application/xml\").\n\t\tExpect().Status(httptest.StatusOK).\n\t\tBody().IsEqual(expectedXMLPayload)\n\n\t// Test send URL Query and receive MessagePack.\n\te.POST(\"/\").WithQuery(\"message\", expectedPayload.Message).\n\t\tWithHeader(\"Accept\", \"application/msgpack\").\n\t\tExpect().Status(httptest.StatusOK).ContentType(\"application/msgpack\").\n\t\tBody().IsEqual(expectedMsgPackPayload)\n\n\t// Test send MessagePack and receive MessagePack.\n\te.POST(\"/\").WithBytes([]byte(expectedMsgPackPayload)).\n\t\tWithHeader(\"Content-Type\", \"application/msgpack\").\n\t\tWithHeader(\"Accept\", \"application/msgpack\").\n\t\tExpect().Status(httptest.StatusOK).\n\t\tContentType(\"application/msgpack\").Body().IsEqual(expectedMsgPackPayload)\n\n\t// Test send YAML and receive YAML.\n\te.POST(\"/\").WithBytes([]byte(expectedYAMLPayload)).\n\t\tWithHeader(\"Content-Type\", \"application/x-yaml\").\n\t\tWithHeader(\"Accept\", \"application/x-yaml\").\n\t\tExpect().Status(httptest.StatusOK).\n\t\tContentType(\"application/x-yaml\").Body().IsEqual(expectedYAMLPayload)\n}\n"
  },
  {
    "path": "_examples/request-body/read-custom-per-type/main.go",
    "content": "package main\n\nimport (\n\t\"gopkg.in/yaml.v3\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := newApp()\n\n\t// use Postman or whatever to do a POST request\n\t// (however you are always free to use app.Get and GET http method requests to read body of course)\n\t// to the http://localhost:8080 with RAW BODY:\n\t/*\n\t\taddr: localhost:8080\n\t\tserverName: Iris\n\t*/\n\t//\n\t// The response should be:\n\t// Received: main.config{Addr:\"localhost:8080\", ServerName:\"Iris\"}\n\tapp.Listen(\":8080\", iris.WithOptimizations)\n}\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\tapp.Post(\"/\", handler)\n\n\treturn app\n}\n\n// simple yaml stuff, read more at https://github.com/go-yaml/yaml\ntype config struct {\n\tAddr       string `yaml:\"addr\"`\n\tServerName string `yaml:\"serverName\"`\n}\n\n// Decode implements the `kataras/iris/context#BodyDecoder` optional interface\n// that any go type can implement in order to be self-decoded when reading the request's body.\nfunc (c *config) Decode(body []byte) error {\n\treturn yaml.Unmarshal(body, c)\n}\n\nfunc handler(ctx iris.Context) {\n\tvar c config\n\n\t//\n\t// Note:\n\t// second parameter is nil because our &c implements the `context#BodyDecoder`\n\t// which has a priority over the context#Unmarshaler (which can be a more global option for reading request's body)\n\t// see the `request-body/read-custom-via-unmarshaler/main.go` example to learn how to use the context#Unmarshaler too.\n\t//\n\t// Note 2:\n\t// If you need to read the body again for any reason\n\t// you should disable the body consumption via `app.Run(..., iris.WithoutBodyConsumptionOnUnmarshal)`.\n\t//\n\n\tif err := ctx.UnmarshalBody(&c, nil); err != nil {\n\t\tctx.StopWithError(iris.StatusBadRequest, err)\n\t\treturn\n\t}\n\n\tctx.Writef(\"Received: %#+v\", c)\n}\n"
  },
  {
    "path": "_examples/request-body/read-custom-per-type/main_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nfunc TestReadCustomPerType(t *testing.T) {\n\tapp := newApp()\n\te := httptest.New(t, app)\n\n\texpectedResponse := `Received: main.config{Addr:\"localhost:8080\", ServerName:\"Iris\"}`\n\n\te.POST(\"/\").WithText(\"addr: localhost:8080\\nserverName: Iris\").Expect().\n\t\tStatus(httptest.StatusOK).Body().IsEqual(expectedResponse)\n}\n"
  },
  {
    "path": "_examples/request-body/read-custom-via-unmarshaler/main.go",
    "content": "package main\n\nimport (\n\t\"gopkg.in/yaml.v3\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := newApp()\n\n\t// use Postman or whatever to do a POST request\n\t// (however you are always free to use app.Get and GET http method requests to read body of course)\n\t// to the http://localhost:8080 with RAW BODY:\n\t/*\n\t\taddr: localhost:8080\n\t\tserverName: Iris\n\t*/\n\t//\n\t// The response should be:\n\t// Received: main.config{Addr:\"localhost:8080\", ServerName:\"Iris\"}\n\tapp.Listen(\":8080\", iris.WithOptimizations)\n}\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\tapp.Post(\"/\", handler)\n\n\treturn app\n}\n\n// simple yaml stuff, read more at https://github.com/go-yaml/yaml\ntype config struct {\n\tAddr       string `yaml:\"addr\"`\n\tServerName string `yaml:\"serverName\"`\n}\n\n/*\ntype myBodyDecoder struct{}\n\nvar DefaultBodyDecoder = myBodyDecoder{}\n\n// Implements the `kataras/iris/context#Unmarshaler` but at our example\n// we will use the simplest `context#UnmarshalerFunc` to pass just the yaml.Unmarshal.\n//\n// Can be used as: ctx.UnmarshalBody(&c, DefaultBodyDecoder)\nfunc (r *myBodyDecoder) Unmarshal(data []byte, outPtr any) error {\n\treturn yaml.Unmarshal(data, outPtr)\n}\n*/\n\nfunc handler(ctx iris.Context) {\n\tvar c config\n\n\t//\n\t// Note:\n\t// yaml.Unmarshal already implements the `context#Unmarshaler`\n\t// so we can use it directly, like the json.Unmarshal(ctx.ReadJSON), xml.Unmarshal(ctx.ReadXML)\n\t// and every library which follows the best practises and is aligned with the Go standards.\n\t//\n\t// Note 2:\n\t// If you need to read the body again for any reason\n\t// you should disable the body consumption via `app.Run(..., iris.WithoutBodyConsumptionOnUnmarshal)`.\n\t//\n\n\tif err := ctx.UnmarshalBody(&c, iris.UnmarshalerFunc(yaml.Unmarshal)); err != nil {\n\t\tctx.StopWithError(iris.StatusBadRequest, err)\n\t\treturn\n\t}\n\n\tctx.Writef(\"Received: %#+v\", c)\n}\n"
  },
  {
    "path": "_examples/request-body/read-custom-via-unmarshaler/main_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nfunc TestReadCustomViaUnmarshaler(t *testing.T) {\n\tapp := newApp()\n\te := httptest.New(t, app)\n\n\texpectedResponse := `Received: main.config{Addr:\"localhost:8080\", ServerName:\"Iris\"}`\n\n\te.POST(\"/\").WithText(\"addr: localhost:8080\\nserverName: Iris\").Expect().\n\t\tStatus(httptest.StatusOK).Body().IsEqual(expectedResponse)\n}\n"
  },
  {
    "path": "_examples/request-body/read-form/checkboxes/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.RegisterView(iris.HTML(\"./templates\", \".html\"))\n\n\tapp.Get(\"/\", showForm)\n\tapp.Post(\"/\", handleForm)\n\n\tapp.Listen(\":8080\")\n}\n\nfunc showForm(ctx iris.Context) {\n\tif err := ctx.View(\"form.html\"); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n}\n\ntype formExample struct {\n\tColors []string `form:\"colors[]\"` // or just colors, it'll work as expected.\n}\n\nfunc handleForm(ctx iris.Context) {\n\tvar form formExample\n\terr := ctx.ReadForm(&form)\n\tif err != nil {\n\t\tctx.StopWithError(iris.StatusBadRequest, err)\n\t\treturn\n\t}\n\n\tctx.JSON(iris.Map{\"Colors\": form.Colors})\n}\n"
  },
  {
    "path": "_examples/request-body/read-form/checkboxes/templates/form.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Select a color</title>\n</head>\n<body>\n    <form action=\"/\" method=\"POST\">\n        <p>Select one or more colors</p>\n    \n        <label for=\"red\">Red</label>\n        <!-- name can be \"colors\" too -->\n        <input type=\"checkbox\" name=\"colors[]\" value=\"red\" id=\"red\">\n        <label for=\"green\">Green</label>\n        <input type=\"checkbox\" name=\"colors[]\" value=\"green\" id=\"green\">\n        <label for=\"blue\">Blue</label>\n        <input type=\"checkbox\" name=\"colors[]\" value=\"blue\" id=\"blue\">\n        <input type=\"submit\">\n    </form>\n</body>\n</html>"
  },
  {
    "path": "_examples/request-body/read-form/main.go",
    "content": "// package main contains an example on how to use the ReadForm, but with the same way you can do the ReadJSON & ReadJSON\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\ntype Visitor struct {\n\tUsername string\n\tMail     string\n\tData     []string `form:\"mydata\"`\n}\n\nfunc main() {\n\tapp := iris.New()\n\n\t// set the view html template engine\n\tapp.RegisterView(iris.HTML(\"./templates\", \".html\").Reload(true))\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tif err := ctx.View(\"form.html\"); err != nil {\n\t\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\t\treturn\n\t\t}\n\t})\n\n\tapp.Post(\"/form_action\", func(ctx iris.Context) {\n\t\tvisitor := Visitor{}\n\t\terr := ctx.ReadForm(&visitor)\n\t\tif err != nil {\n\t\t\tif !iris.IsErrPath(err) /* see: https://github.com/kataras/iris/issues/1157 */ ||\n\t\t\t\terr == iris.ErrEmptyForm {\n\t\t\t\tctx.StopWithError(iris.StatusInternalServerError, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tctx.Writef(\"Visitor: %#v\", visitor)\n\t})\n\n\tapp.Post(\"/post_value\", func(ctx iris.Context) {\n\t\tusername := ctx.PostValueDefault(\"Username\", \"iris\")\n\t\tctx.Writef(\"Username: %s\", username)\n\t})\n\n\tapp.Listen(\":8080\", iris.WithEmptyFormError /* returns ErrEmptyForm if the request form body was empty */)\n}\n"
  },
  {
    "path": "_examples/request-body/read-form/templates/form.html",
    "content": "<!DOCTYPE html>\n<head>\n<meta charset=\"utf-8\">\n</head>\n<body>\n\t<form action=\"/form_action\" method=\"post\">\n\t\tUsername: <input type=\"text\" name=\"Username\" /> <br />\n\t\tMail: <input type=\"text\" name=\"Mail\" /> <br /> \n\t\tSelect one or more:  <br/> \n\t\t<select multiple=\"multiple\" name=\"mydata\">\n\t\t\t<option value='one'>One</option>\n\t\t\t<option value='two'>Two</option>\n\t\t\t<option value='three'>Three</option>\n\t\t\t<option value='four'>Four</option>\n\t\t</select>\n\n\t\t<hr />\n\t\t<input type=\"submit\" value=\"Send data\" />\n\n\t</form>\n</body>\n</html>\n"
  },
  {
    "path": "_examples/request-body/read-headers/main.go",
    "content": "// package main contains an example on how to use the ReadHeaders,\n// same way you can do the ReadQuery, ReadJSON, ReadProtobuf and e.t.c.\npackage main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n)\n\ntype myHeaders struct {\n\tRequestID      string `header:\"X-Request-Id,required\"`\n\tAuthentication string `header:\"Authentication,required\"`\n}\n\nfunc main() {\n\tapp := newApp()\n\n\t// http://localhost:8080\n\t/*\n\t\tmyHeaders: main.myHeaders{\n\t\t\tRequestID: \"373713f0-6b4b-42ea-ab9f-e2e04bc38e73\",\n\t\t\tAuthentication: \"Bearer my-token\",\n\t\t}\n\t*/\n\tapp.Listen(\":8080\")\n}\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tvar hs myHeaders\n\t\tif err := ctx.ReadHeaders(&hs); err != nil {\n\t\t\tctx.StopWithError(iris.StatusInternalServerError, err)\n\t\t\treturn\n\t\t}\n\n\t\tctx.Writef(\"myHeaders: %#v\", hs)\n\t})\n\n\treturn app\n}\n"
  },
  {
    "path": "_examples/request-body/read-headers/main_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nfunc TestReadHeaders(t *testing.T) {\n\tapp := newApp()\n\n\te := httptest.New(t, app)\n\n\texpectedOKBody := `myHeaders: main.myHeaders{RequestID:\"373713f0-6b4b-42ea-ab9f-e2e04bc38e73\", Authentication:\"Bearer my-token\"}`\n\n\ttests := []struct {\n\t\theaders map[string]string\n\t\tcode    int\n\t\tbody    string\n\t\tregex   bool\n\t}{\n\t\t{headers: map[string]string{\n\t\t\t\"X-Request-Id\":   \"373713f0-6b4b-42ea-ab9f-e2e04bc38e73\",\n\t\t\t\"Authentication\": \"Bearer my-token\",\n\t\t}, code: 200, body: expectedOKBody, regex: false},\n\t\t{headers: map[string]string{\n\t\t\t\"x-request-id\":   \"373713f0-6b4b-42ea-ab9f-e2e04bc38e73\",\n\t\t\t\"authentication\": \"Bearer my-token\",\n\t\t}, code: 200, body: expectedOKBody, regex: false},\n\t\t{headers: map[string]string{\n\t\t\t\"X-Request-Id\":   \"373713f0-6b4b-42ea-ab9f-e2e04bc38e73\",\n\t\t\t\"Authentication\": \"Bearer my-token\",\n\t\t}, code: 200, body: expectedOKBody, regex: false},\n\t\t{headers: map[string]string{\n\t\t\t\"Authentication\": \"Bearer my-token\",\n\t\t}, code: 500, body: \"X-Request-Id is empty\", regex: false},\n\t\t{headers: map[string]string{\n\t\t\t\"X-Request-Id\": \"373713f0-6b4b-42ea-ab9f-e2e04bc38e73\",\n\t\t}, code: 500, body: \"Authentication is empty\", regex: false},\n\t\t{headers: map[string]string{}, code: 500, body: \".*\\\\(and 1 other error\\\\)$\", regex: true},\n\t}\n\n\tfor _, tt := range tests {\n\t\tte := e.GET(\"/\").WithHeaders(tt.headers).Expect().Status(tt.code).Body()\n\t\tif tt.regex {\n\t\t\tte.Match(tt.body)\n\t\t} else {\n\t\t\tte.Equal(tt.body)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "_examples/request-body/read-json/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n)\n\ntype Company struct {\n\tName  string\n\tCity  string\n\tOther string\n}\n\nfunc MyHandler(ctx iris.Context) {\n\tvar c Company\n\n\tif err := ctx.ReadJSON(&c); err != nil {\n\t\tctx.StopWithError(iris.StatusBadRequest, err)\n\t\treturn\n\t}\n\n\tctx.Writef(\"Received: %#+v\\n\", c)\n}\n\n// simple json stuff, read more at https://golang.org/pkg/encoding/json\ntype Person struct {\n\tName string `json:\"name\"`\n\tAge  int    `json:\"age\"`\n}\n\n// MyHandler2 reads a collection of Person from JSON post body.\nfunc MyHandler2(ctx iris.Context) {\n\tvar persons []Person\n\terr := ctx.ReadJSON(&persons)\n\tif err != nil {\n\t\tctx.StopWithError(iris.StatusBadRequest, err)\n\t\treturn\n\t}\n\n\tctx.Writef(\"Received: %#+v\\n\", persons)\n}\n\nfunc main() {\n\tapp := iris.New()\n\n\tapp.Post(\"/\", MyHandler)\n\tapp.Post(\"/slice\", MyHandler2)\n\n\t// use Postman or whatever to do a POST request\n\t// to the http://localhost:8080 with RAW BODY:\n\t/*\n\t\t{\n\t\t\t\"Name\": \"iris-Go\",\n\t\t\t\"City\": \"New York\",\n\t\t\t\"Other\": \"Something here\"\n\t\t}\n\t*/\n\t// and Content-Type to application/json (optionally but good practise)\n\t//\n\t// The response should be:\n\t// Received: main.Company{Name:\"iris-Go\", City:\"New York\", Other:\"Something here\"}\n\tapp.Listen(\":8080\", iris.WithOptimizations)\n}\n"
  },
  {
    "path": "_examples/request-body/read-json-stream/main.go",
    "content": "package main\n\nimport \"github.com/kataras/iris/v12\"\n\nfunc main() {\n\tapp := iris.New()\n\tapp.Post(\"/\", postIndex)\n\n\tapp.Post(\"/stream\", postIndexStream)\n\n\t/*\n\t\tcurl -L -X POST \"http://localhost:8080/\" \\\n\t\t-H 'Content-Type: application/json' \\\n\t\t--data-raw '{\"username\":\"john\"}'\n\n\t\tcurl -L -X POST \"http://localhost:8080/stream\" \\\n\t\t-H 'Content-Type: application/json' \\\n\t\t--data-raw '{\"username\":\"john\"}\n\t\t{\"username\":\"makis\"}\n\t\t{\"username\":\"george\"}\n\t\t{\"username\":\"michael\"}\n\t\t'\n\n\t\tIf JSONReader.ArrayStream was true then you must provide an array of objects instead, e.g.\n\t\t[{\"username\":\"john\"},\n\t\t{\"username\":\"makis\"},\n\t\t{\"username\":\"george\"},\n\t\t{\"username\":\"michael\"}]\n\n\t*/\n\n\tapp.Listen(\":8080\")\n}\n\ntype User struct {\n\tUsername string `json:\"username\"`\n}\n\nfunc postIndex(ctx iris.Context) {\n\tvar u User\n\terr := ctx.ReadJSON(&u, iris.JSONReader{\n\t\t// To throw an error on unknown request payload json fields.\n\t\tDisallowUnknownFields: true,\n\t\tOptimize:              true,\n\t})\n\tif err != nil {\n\t\tctx.StopWithError(iris.StatusBadRequest, err)\n\t\treturn\n\t}\n\n\tctx.JSON(iris.Map{\n\t\t\"code\":     iris.StatusOK,\n\t\t\"username\": u.Username,\n\t})\n}\n\nfunc postIndexStream(ctx iris.Context) {\n\tvar users []User\n\tjob := func(decode iris.DecodeFunc) error {\n\t\tvar u User\n\t\tif err := decode(&u); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tusers = append(users, u)\n\t\t// When the returned error is not nil the decode operation\n\t\t// is terminated and the error is received by the ReadJSONStream method below,\n\t\t// otherwise it continues to read the next available object.\n\t\treturn nil\n\t}\n\n\terr := ctx.ReadJSONStream(job, iris.JSONReader{\n\t\tOptimize:              true,\n\t\tDisallowUnknownFields: true,\n\t\tArrayStream:           false,\n\t})\n\tif err != nil {\n\t\tctx.StopWithError(iris.StatusBadRequest, err)\n\t\treturn\n\t}\n\n\tctx.JSON(iris.Map{\n\t\t\"code\":        iris.StatusOK,\n\t\t\"users_count\": len(users),\n\t\t\"users\":       users,\n\t})\n}\n"
  },
  {
    "path": "_examples/request-body/read-json-struct-validation/main.go",
    "content": "// Package main shows the validator(latest, version 10) integration with Iris' Context methods of\n// `ReadJSON`, `ReadXML`, `ReadMsgPack`, `ReadYAML`, `ReadForm`, `ReadQuery`, `ReadBody`.\n//\n// You can find more examples of this 3rd-party library at:\n// https://github.com/go-playground/validator/blob/master/_examples\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n\n\t// $ go get github.com/go-playground/validator/v10@latest\n\t\"github.com/go-playground/validator/v10\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.Validator = validator.New()\n\n\tuserRouter := app.Party(\"/user\")\n\t{\n\t\tuserRouter.Get(\"/validation-errors\", resolveErrorsDocumentation)\n\t\tuserRouter.Post(\"/\", postUser)\n\t}\n\n\t// Use Postman or any tool to perform a POST request\n\t// to the http://localhost:8080/user with RAW BODY of:\n\t/*\n\t\t{\n\t\t\t\"fname\": \"\",\n\t\t\t\"lname\": \"\",\n\t\t\t\"age\": 45,\n\t\t\t\"email\": \"mail@example.com\",\n\t\t\t\"favColor\": \"#000\",\n\t\t\t\"addresses\": [{\n\t\t\t\t\"street\": \"Eavesdown Docks\",\n\t\t\t\t\"planet\": \"Persphone\",\n\t\t\t\t\"phone\": \"none\",\n\t\t\t\t\"city\": \"Unknown\"\n\t\t\t}]\n\t\t}\n\t*/\n\t/* The response should be:\n\t\t{\n\t\t  \"title\": \"Validation error\",\n\t      \"detail\": \"One or more fields failed to be validated\",\n\t      \"type\": \"http://localhost:8080/user/validation-errors\",\n\t      \"status\": 400,\n\t\t  \"fields\": [\n\t\t    {\n\t\t      \"tag\": \"required\",\n\t\t      \"namespace\": \"User.FirstName\",\n\t\t      \"kind\": \"string\",\n\t\t      \"type\": \"string\",\n\t\t      \"value\": \"\",\n\t\t      \"param\": \"\"\n\t\t    },\n\t\t    {\n\t\t      \"tag\": \"required\",\n\t\t      \"namespace\": \"User.LastName\",\n\t\t      \"kind\": \"string\",\n\t\t      \"type\": \"string\",\n\t\t      \"value\": \"\",\n\t\t      \"param\": \"\"\n\t\t    }\n\t\t  ]\n\t\t}\n\t*/\n\tapp.Listen(\":8080\")\n}\n\n// User contains user information.\ntype User struct {\n\tFirstName      string     `json:\"fname\" validate:\"required\"`\n\tLastName       string     `json:\"lname\" validate:\"required\"`\n\tAge            uint8      `json:\"age\" validate:\"gte=0,lte=130\"`\n\tEmail          string     `json:\"email\" validate:\"required,email\"`\n\tFavouriteColor string     `json:\"favColor\" validate:\"hexcolor|rgb|rgba\"`\n\tAddresses      []*Address `json:\"addresses\" validate:\"required,dive,required\"` // a User can have a home and cottage...\n}\n\n// Address houses a users address information.\ntype Address struct {\n\tStreet string `json:\"street\" validate:\"required\"`\n\tCity   string `json:\"city\" validate:\"required\"`\n\tPlanet string `json:\"planet\" validate:\"required\"`\n\tPhone  string `json:\"phone\" validate:\"required\"`\n}\n\ntype validationError struct {\n\tActualTag string `json:\"tag\"`\n\tNamespace string `json:\"namespace\"`\n\tKind      string `json:\"kind\"`\n\tType      string `json:\"type\"`\n\tValue     string `json:\"value\"`\n\tParam     string `json:\"param\"`\n}\n\nfunc wrapValidationErrors(errs validator.ValidationErrors) []validationError {\n\tvalidationErrors := make([]validationError, 0, len(errs))\n\tfor _, validationErr := range errs {\n\t\tvalidationErrors = append(validationErrors, validationError{\n\t\t\tActualTag: validationErr.ActualTag(),\n\t\t\tNamespace: validationErr.Namespace(),\n\t\t\tKind:      validationErr.Kind().String(),\n\t\t\tType:      validationErr.Type().String(),\n\t\t\tValue:     fmt.Sprintf(\"%v\", validationErr.Value()),\n\t\t\tParam:     validationErr.Param(),\n\t\t})\n\t}\n\n\treturn validationErrors\n}\n\nfunc postUser(ctx iris.Context) {\n\tvar user User\n\terr := ctx.ReadJSON(&user)\n\tif err != nil {\n\t\t// Handle the error, below you will find the right way to do that...\n\n\t\tif errs, ok := err.(validator.ValidationErrors); ok {\n\t\t\t// Wrap the errors with JSON format, the underline library returns the errors as interface.\n\t\t\tvalidationErrors := wrapValidationErrors(errs)\n\n\t\t\t// Fire an application/json+problem response and stop the handlers chain.\n\t\t\tctx.StopWithProblem(iris.StatusBadRequest, iris.NewProblem().\n\t\t\t\tTitle(\"Validation error\").\n\t\t\t\tDetail(\"One or more fields failed to be validated\").\n\t\t\t\tType(\"/user/validation-errors\").\n\t\t\t\tKey(\"errors\", validationErrors))\n\n\t\t\treturn\n\t\t}\n\n\t\t// It's probably an internal JSON error, let's dont give more info here.\n\t\tctx.StopWithStatus(iris.StatusInternalServerError)\n\t\treturn\n\t}\n\n\tctx.JSON(iris.Map{\"message\": \"OK\"})\n}\n\nfunc resolveErrorsDocumentation(ctx iris.Context) {\n\tctx.WriteString(\"A page that should document to web developers or users of the API on how to resolve the validation errors\")\n}\n"
  },
  {
    "path": "_examples/request-body/read-many/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\tapp.Post(\"/\", logAllBody, logJSON, logFormValues, func(ctx iris.Context) {\n\t\t// body, err := io.ReadAll(ctx.Request().Body) once or\n\t\tbody, err := ctx.GetBody() // as many times as you need.\n\t\tif err != nil {\n\t\t\tctx.StopWithError(iris.StatusInternalServerError, err)\n\t\t\treturn\n\t\t}\n\n\t\tif len(body) == 0 {\n\t\t\tctx.WriteString(`The body was empty\nor iris.WithoutBodyConsumptionOnUnmarshal option is missing from app.Run.\nCheck the terminal window for any queries logs.`)\n\t\t} else {\n\t\t\tctx.WriteString(\"OK body is still:\\n\")\n\t\t\tctx.Write(body)\n\t\t}\n\t})\n\n\t// With ctx.UnmarshalBody, ctx.ReadJSON, ctx.ReadXML, ctx.ReadForm, ctx.FormValues\n\t// and ctx.GetBody methods the default golang and net/http behavior\n\t// is to consume the readen data - they are not available on any next handlers in the chain -\n\t// to change that behavior just pass the `WithoutBodyConsumptionOnUnmarshal` option.\n\tapp.Listen(\":8080\", iris.WithoutBodyConsumptionOnUnmarshal)\n}\n\nfunc logAllBody(ctx iris.Context) {\n\tbody, err := ctx.GetBody()\n\tif err == nil && len(body) > 0 {\n\t\tctx.Application().Logger().Infof(\"logAllBody: %s\", string(body))\n\t}\n\n\tctx.Next()\n}\n\nfunc logJSON(ctx iris.Context) {\n\tvar p any\n\tif err := ctx.ReadJSON(&p); err == nil {\n\t\tctx.Application().Logger().Infof(\"logJSON: %#+v\", p)\n\t}\n\n\tctx.Next()\n}\n\nfunc logFormValues(ctx iris.Context) {\n\tvalues := ctx.FormValues()\n\tif values != nil {\n\t\tctx.Application().Logger().Infof(\"logFormValues: %v\", values)\n\t}\n\n\tctx.Next()\n}\n"
  },
  {
    "path": "_examples/request-body/read-msgpack/main.go",
    "content": "package main\n\nimport \"github.com/kataras/iris/v12\"\n\n// User example struct to bind to.\ntype User struct {\n\tFirstname string `msgpack:\"firstname\"`\n\tLastname  string `msgpack:\"lastname\"`\n\tCity      string `msgpack:\"city\"`\n\tAge       int    `msgpack:\"age\"`\n}\n\n// readMsgPack reads a `User` from MsgPack post body.\nfunc readMsgPack(ctx iris.Context) {\n\tvar u User\n\terr := ctx.ReadMsgPack(&u)\n\tif err != nil {\n\t\tctx.StopWithError(iris.StatusBadRequest, err)\n\t\treturn\n\t}\n\n\tctx.Writef(\"Received: %#+v\\n\", u)\n}\n\nfunc main() {\n\tapp := iris.New()\n\tapp.Post(\"/\", readMsgPack)\n\n\t// POST: http://localhost:8080\n\t//\n\t// To run the example, use a tool like Postman:\n\t// 1. Body: Binary\n\t// 2. Select File, select the one from \"_examples/response-writer/write-rest\" example.\n\t// The output should be:\n\t// Received: main.User{Firstname:\"John\", Lastname:\"Doe\", City:\"Neither FBI knows!!!\", Age:25}\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/request-body/read-params/main.go",
    "content": "// package main contains an example on how to use the ReadParams,\n// same way you can do the ReadQuery, ReadJSON, ReadProtobuf and e.t.c.\npackage main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n)\n\ntype myParams struct {\n\tName string   `param:\"name\"`\n\tAge  int      `param:\"age\"`\n\tTail []string `param:\"tail\"`\n}\n\nfunc main() {\n\tapp := newApp()\n\n\t// http://localhost:8080/kataras/27/iris/web/framework\n\t// myParams: main.myParams{Name:\"kataras\", Age:27, Tail:[]string{\"iris\", \"web\", \"framework\"}}\n\tapp.Listen(\":8080\")\n}\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\n\tapp.Get(\"/{name}/{age:int}/{tail:path}\", func(ctx iris.Context) {\n\t\tvar p myParams\n\t\tif err := ctx.ReadParams(&p); err != nil {\n\t\t\tctx.StopWithError(iris.StatusInternalServerError, err)\n\t\t\treturn\n\t\t}\n\n\t\tctx.Writef(\"myParams: %#v\", p)\n\t})\n\n\treturn app\n}\n"
  },
  {
    "path": "_examples/request-body/read-params/main_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nfunc TestReadParams(t *testing.T) {\n\tapp := newApp()\n\n\te := httptest.New(t, app)\n\n\texpectedBody := `myParams: main.myParams{Name:\"kataras\", Age:27, Tail:[]string{\"iris\", \"web\", \"framework\"}}`\n\te.GET(\"/kataras/27/iris/web/framework\").Expect().Status(httptest.StatusOK).Body().IsEqual(expectedBody)\n}\n"
  },
  {
    "path": "_examples/request-body/read-query/main.go",
    "content": "// package main contains an example on how to use the ReadQuery,\n// same way you can do the ReadJSON & ReadProtobuf and e.t.c.\npackage main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n)\n\ntype MyType struct {\n\tName string `url:\"name,required\"`\n\tAge  int    `url:\"age\"`\n}\n\nfunc main() {\n\tapp := iris.New()\n\tapp.UseRouter(iris.AllowQuerySemicolons) // Optionally: to restore pre go1.17 behavior of url parsing.\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tvar t MyType\n\t\terr := ctx.ReadQuery(&t)\n\n\t\t// To ignore errors of \"required\" or when unexpected values are passed to the query,\n\t\t// use the iris.IsErrPath.\n\t\t// It can be ignored, e.g:\n\t\t// if err!=nil && !iris.IsErrPath(err) { ... return }\n\t\t//\n\t\t// To receive an error on EMPTY query when ReadQuery is called\n\t\t// you should enable the `FireEmptyFormError/WithEmptyFormError` ( see below).\n\t\t// To check for the empty error you simple compare the error with the ErrEmptyForm, e.g.:\n\t\t// err == iris.ErrEmptyForm, so, to ignore both path and empty errors, you do:\n\t\t// if err!=nil && err != iris.ErrEmptyForm && !iris.IsErrPath(err) { ctx.StopWithError(...); return }\n\t\tif err != nil {\n\t\t\tctx.StopWithError(iris.StatusInternalServerError, err)\n\t\t\treturn\n\t\t}\n\n\t\tctx.Writef(\"MyType: %#v\", t)\n\t})\n\n\tapp.Get(\"/simple\", func(ctx iris.Context) {\n\t\tnames := ctx.URLParamSlice(\"name\")\n\t\tctx.Writef(\"names: %v\", names)\n\t})\n\n\t// http://localhost:8080?name=iris&age=3\n\t// http://localhost:8080/simple?name=john&name=doe&name=kataras\n\t//\n\t// Note: this `WithEmptyFormError` will give an error if the query was empty.\n\tapp.Listen(\":8080\", iris.WithEmptyFormError,\n\t\tiris.WithoutServerError(iris.ErrServerClosed, iris.ErrURLQuerySemicolon))\n}\n"
  },
  {
    "path": "_examples/request-body/read-url/main.go",
    "content": "// package main contains an example on how to use the ReadURL,\n// same way you can do the ReadQuery, ReadParams, ReadJSON, ReadProtobuf and e.t.c.\npackage main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n)\n\ntype myURL struct {\n\tName string   `url:\"name\"` // or `param:\"name\"`\n\tAge  int      `url:\"age\"`  // >> >>\n\tTail []string `url:\"tail\"` // >> >>\n}\n\nfunc main() {\n\tapp := newApp()\n\n\t// http://localhost:8080/iris/web/framework?name=kataras&age=27\n\t// myURL: main.myURL{Name:\"kataras\", Age:27, Tail:[]string{\"iris\", \"web\", \"framework\"}}\n\tapp.Listen(\":8080\")\n}\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\n\tapp.Get(\"/{tail:path}\", func(ctx iris.Context) {\n\t\tvar u myURL\n\t\t// ReadURL is a shortcut of ReadParams + ReadQuery.\n\t\tif err := ctx.ReadURL(&u); err != nil {\n\t\t\tctx.StopWithError(iris.StatusInternalServerError, err)\n\t\t\treturn\n\t\t}\n\n\t\tctx.Writef(\"myURL: %#v\", u)\n\t})\n\n\treturn app\n}\n"
  },
  {
    "path": "_examples/request-body/read-url/main_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nfunc TestReadURL(t *testing.T) {\n\tapp := newApp()\n\n\te := httptest.New(t, app)\n\n\texpectedBody := `myURL: main.myURL{Name:\"kataras\", Age:27, Tail:[]string{\"iris\", \"web\", \"framework\"}}`\n\te.GET(\"/iris/web/framework\").WithQuery(\"name\", \"kataras\").WithQuery(\"age\", 27).Expect().Status(httptest.StatusOK).Body().IsEqual(expectedBody)\n}\n"
  },
  {
    "path": "_examples/request-body/read-xml/main.go",
    "content": "package main\n\nimport (\n\t\"encoding/xml\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := newApp()\n\n\t// use Postman or whatever to do a POST request\n\t// to the http://localhost:8080 with RAW BODY:\n\t/*\n\t\t<person name=\"Winston Churchill\" age=\"90\">\n\t\t\t<description>Description of this person, the body of this inner element.</description>\n\t\t</person>\n\t*/\n\t// and Content-Type to application/xml (optionally but good practise)\n\t//\n\t// The response should be:\n\t// Received: main.person{XMLName:xml.Name{Space:\"\", Local:\"person\"}, Name:\"Winston Churchill\", Age:90, Description:\"Description of this person, the body of this inner element.\"}\n\tapp.Listen(\":8080\", iris.WithOptimizations)\n}\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\tapp.Post(\"/\", handler)\n\n\treturn app\n}\n\n// simple xml stuff, read more at https://golang.org/pkg/encoding/xml\ntype person struct {\n\tXMLName     xml.Name `xml:\"person\"`      // element name\n\tName        string   `xml:\"name,attr\"`   // ,attr for attribute.\n\tAge         int      `xml:\"age,attr\"`    // ,attr attribute.\n\tDescription string   `xml:\"description\"` // inner element name, value is its body.\n}\n\nfunc handler(ctx iris.Context) {\n\tvar p person\n\tif err := ctx.ReadXML(&p); err != nil {\n\t\tctx.StopWithError(iris.StatusBadRequest, err)\n\t\treturn\n\t}\n\n\tctx.Writef(\"Received: %#+v\", p)\n}\n"
  },
  {
    "path": "_examples/request-body/read-xml/main_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nfunc TestReadXML(t *testing.T) {\n\tapp := newApp()\n\te := httptest.New(t, app)\n\n\texpectedResponse := `Received: main.person{XMLName:xml.Name{Space:\"\", Local:\"person\"}, Name:\"Winston Churchill\", Age:90, Description:\"Description of this person, the body of this inner element.\"}`\n\tsend := `<person name=\"Winston Churchill\" age=\"90\"><description>Description of this person, the body of this inner element.</description></person>`\n\n\te.POST(\"/\").WithText(send).Expect().\n\t\tStatus(httptest.StatusOK).Body().IsEqual(expectedResponse)\n}\n"
  },
  {
    "path": "_examples/request-body/read-yaml/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\tapp.Post(\"/\", handler)\n\n\treturn app\n}\n\n// simple yaml stuff, read more at https://yaml.org/start.html\ntype product struct {\n\tInvoice  int     `yaml:\"invoice\"`\n\tTax      float32 `yaml:\"tax\"`\n\tTotal    float32 `yaml:\"total\"`\n\tComments string  `yaml:\"comments\"`\n}\n\nfunc handler(ctx iris.Context) {\n\tvar p product\n\tif err := ctx.ReadYAML(&p); err != nil {\n\t\tctx.StopWithError(iris.StatusBadRequest, err)\n\t\treturn\n\t}\n\n\tctx.Writef(\"Received: %#+v\", p)\n}\n\nfunc main() {\n\tapp := newApp()\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/request-body/read-yaml/main_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nfunc TestReadYAML(t *testing.T) {\n\tapp := newApp()\n\te := httptest.New(t, app)\n\n\texpectedResponse := `Received: main.product{Invoice:34843, Tax:251.42, Total:4443.52, Comments:\"Late afternoon is best. Backup contact is Nancy Billsmer @ 338-4338.\"}`\n\tsend := `invoice: 34843\ntax  : 251.42\ntotal: 4443.52\ncomments: >\n    Late afternoon is best.\n    Backup contact is Nancy\n    Billsmer @ 338-4338.`\n\n\te.POST(\"/\").WithHeader(\"Content-Type\", \"application/x-yaml\").WithBytes([]byte(send)).Expect().\n\t\tStatus(httptest.StatusOK).Body().IsEqual(expectedResponse)\n}\n"
  },
  {
    "path": "_examples/request-ratelimit/rate-middleware/main.go",
    "content": "package main\n\nimport (\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/middleware/rate\"\n)\n\nfunc main() {\n\tapp := newApp()\n\tapp.Logger().SetLevel(\"debug\")\n\n\t// * http://localhost:8080/v1\n\t// * http://localhost:8080/v1/other\n\t// * http://localhost:8080/v2/list (with X-API-Key request header)\n\t//   Read more at: https://en.wikipedia.org/wiki/Token_bucket\n\t//\n\t// Alternatives:\n\t//   * https://github.com/iris-contrib/middleware/blob/master/throttler/_example/main.go\n\t//     Read more at: https://en.wikipedia.org/wiki/Generic_cell_rate_algorithm\n\t//   * https://github.com/iris-contrib/middleware/tree/master/tollboothic/_examples/limit-handler\n\tapp.Listen(\":8080\")\n}\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\n\tv1 := app.Party(\"/v1\")\n\t{\n\t\t// Register the rate limiter middleware at the \"/v1\" subrouter.\n\t\t//\n\t\t// Fist and second input parameters:\n\t\t//\t  Allow 1 request per second, with a maximum burst size of 5.\n\t\t//\n\t\t// Third optional variadic input parameter:\n\t\t//    Can be a cleanup function.\n\t\t//    Iris provides a cleanup function that will check for old entries and remove them.\n\t\t//    You can customize it, e.g. check every 1 minute\n\t\t//    if a client's last visit was 5 minutes ago (\"old\" entry)\n\t\t//    and remove it from the memory.\n\t\tlimitV1 := rate.Limit(1, 5, rate.PurgeEvery(time.Minute, 5*time.Minute))\n\t\t// rate.Every helper:\n\t\t//\t\t\t   rate.Limit(rate.Every(time.Minute), 5)\n\t\tv1.Use(limitV1)\n\n\t\tv1.Get(\"/\", index)\n\t\tv1.Get(\"/other\", other)\n\t}\n\n\tv2 := app.Party(\"/v2\")\n\t{\n\t\tv2.Use(useAPIKey)\n\t\t// Initialize a new rate limit middleware to limit requests\n\t\t// per API Key(see `useAPIKey` below) instead of client's Remote IP Address.\n\t\tlimitV2 := rate.Limit(rate.Every(time.Minute), 300, rate.PurgeEvery(5*time.Minute, 15*time.Minute))\n\t\tv2.Use(limitV2)\n\n\t\tv2.Get(\"/list\", list)\n\t}\n\n\treturn app\n}\n\nfunc useAPIKey(ctx iris.Context) {\n\tapiKey := ctx.GetHeader(\"X-API-Key\")\n\tif apiKey == \"\" { // [validate your API Key here...]\n\t\tctx.StopWithStatus(iris.StatusForbidden)\n\t\treturn\n\t}\n\n\t// Change the method that rate limit matches the requests with a specific user\n\t// and set our own api key as theirs identifier.\n\trate.SetIdentifier(ctx, apiKey)\n\tctx.Next()\n}\n\nfunc list(ctx iris.Context) {\n\tctx.JSON(iris.Map{\"key\": \"value\"})\n}\n\nfunc index(ctx iris.Context) {\n\tctx.HTML(\"<h1>Index Page</h1>\")\n}\n\nfunc other(ctx iris.Context) {\n\tctx.HTML(\"<h1>Other Page</h1>\")\n}\n\n// Note: Use `ctx.SendFileWithRate` to use a download rate limiter instead.\n"
  },
  {
    "path": "_examples/request-ratelimit/ulule-limiter/main.go",
    "content": "package main\n\nimport (\n\t\"log\"\n\t\"net/http\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n\n\t\"github.com/ulule/limiter/v3\"\n\t\"github.com/ulule/limiter/v3/drivers/store/memory\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.Get(\"/hello\", IPRateLimit(), helloWorldHandler) // 3. Use middleware\n\tapp.Run(iris.Addr(\":8080\"))\n}\n\nfunc helloWorldHandler(ctx iris.Context) {\n\terr := ctx.StopWithJSON(iris.StatusOK, iris.Map{\n\t\t\"message\": \"Hello World!\",\n\t})\n\tif err != nil {\n\t\treturn\n\t}\n}\n\nfunc IPRateLimit() iris.Handler {\n\t// 1. Configure\n\trate := limiter.Rate{\n\t\tPeriod: 2 * time.Second,\n\t\tLimit:  1,\n\t}\n\tstore := memory.NewStore()\n\tipRateLimiter := limiter.New(store, rate)\n\n\t// 2. Return middleware handler\n\treturn func(ctx iris.Context) {\n\t\tip := ctx.RemoteAddr()\n\t\tlimiterCtx, err := ipRateLimiter.Get(ctx.Request().Context(), ip)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"IPRateLimit - ipRateLimiter.Get - err: %v, %s on %s\", err, ip, ctx.Request().URL)\n\t\t\tctx.StatusCode(http.StatusInternalServerError)\n\t\t\tctx.JSON(iris.Map{\n\t\t\t\t\"success\": false,\n\t\t\t\t\"message\": err,\n\t\t\t})\n\t\t\treturn\n\t\t}\n\n\t\tctx.Header(\"X-RateLimit-Limit\", strconv.FormatInt(limiterCtx.Limit, 10))\n\t\tctx.Header(\"X-RateLimit-Remaining\", strconv.FormatInt(limiterCtx.Remaining, 10))\n\t\tctx.Header(\"X-RateLimit-Reset\", strconv.FormatInt(limiterCtx.Reset, 10))\n\n\t\tif limiterCtx.Reached {\n\t\t\tlog.Printf(\"Too Many Requests from %s on %s\", ip, ctx.Request().URL)\n\t\t\tctx.StatusCode(http.StatusTooManyRequests)\n\t\t\tctx.JSON(iris.Map{\n\t\t\t\t\"success\": false,\n\t\t\t\t\"message\": \"Too Many Requests on \" + ctx.Request().URL.String(),\n\t\t\t})\n\t\t\treturn\n\t\t}\n\t\tctx.Next()\n\t}\n}\n"
  },
  {
    "path": "_examples/request-referrer/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\t// GetReferrer extracts and returns the information from the \"Referer\" (or \"Referrer\") header\n\t\t// and url query parameter as specified in https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy.\n\t\tr := ctx.GetReferrer()\n\t\tswitch r.Type {\n\t\tcase iris.ReferrerSearch:\n\t\t\tctx.Writef(\"Search %s: %s\\n\", r.Label, r.Query)\n\t\t\tctx.Writef(\"Google: %s\\n\", r.GoogleType)\n\t\tcase iris.ReferrerSocial:\n\t\t\tctx.Writef(\"Social %s\\n\", r.Label)\n\t\tcase iris.ReferrerIndirect:\n\t\t\tctx.Writef(\"Indirect: %s\\n\", r.URL)\n\t\t}\n\t})\n\n\t// http://localhost:8080?referrer=https://twitter.com/Xinterio/status/1023566830974251008\n\t// http://localhost:8080?referrer=https://www.google.com/search?q=Top+6+golang+web+frameworks&oq=Top+6+golang+web+frameworks\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/response-writer/cache/client-side/main.go",
    "content": "// Package main shows how you can use the `WriteWithExpiration`\n// based on the \"modtime\", if it's newer than the request header then\n// it will refresh the contents, otherwise will let the client (99.9% the browser)\n// to handle the cache mechanism, it's faster than iris.Cache because server-side\n// has nothing to do and no need to store the responses in the memory.\npackage main\n\nimport (\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nconst refreshEvery = 10 * time.Second\n\nfunc main() {\n\tapp := iris.New()\n\tapp.Use(iris.Cache304(refreshEvery))\n\t// same as:\n\t// app.Use(func(ctx iris.Context) {\n\t// \tnow := time.Now()\n\t// \tif modified, err := ctx.CheckIfModifiedSince(now.Add(-refresh)); !modified && err == nil {\n\t// \t\tctx.WriteNotModified()\n\t// \t\treturn\n\t// \t}\n\n\t// \tctx.SetLastModified(now)\n\n\t// \tctx.Next()\n\t// })\n\n\tapp.Get(\"/\", greet)\n\tapp.Listen(\":8080\")\n}\n\nfunc greet(ctx iris.Context) {\n\tctx.Header(\"X-Custom\", \"my  custom header\")\n\tctx.Writef(\"Hello World! %s\", time.Now())\n}\n"
  },
  {
    "path": "_examples/response-writer/cache/simple/main.go",
    "content": "package main\n\nimport (\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/cache\"\n\t\"github.com/kataras/iris/v12/middleware/basicauth\"\n)\n\nvar markdownContents = []byte(`## Hello Markdown\n\nThis is a sample of Markdown contents\n\nFeatures\n--------\n\nAll features of Sundown are supported, including:\n\n*   **Compatibility**. The Markdown v1.0.3 test suite passes with\n    the --tidy option.  Without --tidy, the differences are\n    mostly in whitespace and entity escaping, where blackfriday is\n    more consistent and cleaner.\n`)\n\n// Cache should not be used on handlers that contain dynamic data.\n// Cache is a good and a must-feature on static content, i.e \"about page\" or for a whole blog site.\nfunc main() {\n\tapp := iris.New()\n\tapp.Logger().SetLevel(\"debug\")\n\tapp.Get(\"/\", cache.Handler(10*time.Second), writeMarkdown)\n\t// To customize the cache handler:\n\t// cache.Cache(nil).MaxAge(func(ctx iris.Context) time.Duration {\n\t// \treturn time.Duration(ctx.MaxAge()) * time.Second\n\t// }).AddRule(...).Store(...)\n\t// saves its content on the first request and serves it instead of re-calculating the content.\n\t// After 10 seconds it will be cleared and reset.\n\n\tpages := app.Party(\"/pages\")\n\tpages.Use(cache.Handler(10 * time.Second)) // Per Party.\n\tpages.Get(\"/\", pagesIndex)\n\tpages.Post(\"/\", pagesIndexPost)\n\n\t// Note: on authenticated requests\n\t// the cache middleare does not run at all (see iris/cache/ruleset).\n\tauth := basicauth.Default(map[string]string{\n\t\t\"admin\": \"admin\",\n\t})\n\tapp.Get(\"/protected\", auth, cache.Handler(5*time.Second), protected)\n\n\t// Set custom cache key/identifier,\n\t// for the sake of the example\n\t// we will SHARE the keys on both GET and POST routes\n\t// so the first one is executed that's the body\n\t// for both of the routes. Please don't do that\n\t// on production, this is just an example.\n\tcustom := app.Party(\"/custom\")\n\tcustom.Use(cache.WithKey(\"shared\"))\n\tcustom.Use(cache.Handler(10 * time.Second))\n\tcustom.Get(\"/\", customIndex)\n\tcustom.Post(\"/\", customIndexPost)\n\n\tapp.Listen(\":8080\")\n}\n\nfunc writeMarkdown(ctx iris.Context) {\n\t// tap multiple times the browser's refresh button and you will\n\t// see this println only once every 10 seconds.\n\tprintln(\"Handler executed. Content refreshed.\")\n\n\tctx.Markdown(markdownContents)\n}\n\nfunc pagesIndex(ctx iris.Context) {\n\tprintln(\"Handler executed. Content refreshed.\")\n\tctx.WriteString(\"GET: hello\")\n}\n\nfunc pagesIndexPost(ctx iris.Context) {\n\tprintln(\"Handler executed. Content refreshed.\")\n\tctx.WriteString(\"POST: hello\")\n}\n\nfunc protected(ctx iris.Context) {\n\tusername, _, _ := ctx.Request().BasicAuth()\n\tctx.Writef(\"Hello, %s!\", username)\n}\n\nfunc customIndex(ctx iris.Context) {\n\tctx.WriteString(\"Contents from GET custom index\")\n}\n\nfunc customIndexPost(ctx iris.Context) {\n\tctx.WriteString(\"Contents from POST custom index\")\n}\n\n/* Note that `HandleDir` does use the browser's disk caching by-default\ntherefore, register the cache handler AFTER any HandleDir calls,\nfor a faster solution that server doesn't need to keep track of the response\nnavigate to https://github.com/kataras/iris/blob/main/_examples/cache/client-side/main.go.\n\nThe `HandleDir` has its own cache mechanism, read the 'file-server' examples. */\n"
  },
  {
    "path": "_examples/response-writer/content-negotiation/main.go",
    "content": "// Package main contains three different ways to render content based on the client's accepted.\npackage main\n\nimport \"github.com/kataras/iris/v12\"\n\ntype testdata struct {\n\tName string `json:\"name\" xml:\"Name\"`\n\tAge  int    `json:\"age\" xml:\"Age\"`\n}\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\tapp.Logger().SetLevel(\"debug\")\n\n\t// app.Use(func(ctx iris.Context) {\n\t// \trequestedMime := ctx.URLParamDefault(\"type\", \"application/json\")\n\t//\n\t//  ctx.Negotiation().Accept.Override().MIME(requestedMime, nil)\n\t// \tctx.Next()\n\t// })\n\n\tapp.Get(\"/resource\", func(ctx iris.Context) {\n\t\tdata := testdata{\n\t\t\tName: \"test name\",\n\t\t\tAge:  26,\n\t\t}\n\n\t\t// Server allows response only JSON and XML. These values\n\t\t// are compared with the clients mime needs. Iris comes with default mime types responses\n\t\t// but you can add a custom one by the `Negotiation().Mime(mime, content)` method,\n\t\t// same for the \"accept\".\n\t\t// You can also pass a custom ContentSelector(mime string) or ContentNegotiator to the\n\t\t// `Context.Negotiate` method if you want to perform more advanced things.\n\t\t//\n\t\t//\n\t\t// By-default the client accept mime is retrieved by the \"Accept\" header\n\t\t// Indeed you can override or update it by `Negotiation().Accept.XXX` i.e\n\t\t// ctx.Negotiation().Accept.Override().XML()\n\t\t//\n\t\t// All these values can change inside middlewares, the `Negotiation().Override()` and `.Accept.Override()`\n\t\t// can override any previously set values.\n\t\t// Order matters, if the client accepts anything (*/*)\n\t\t// then the first prioritized mime's response data will be rendered.\n\t\tctx.Negotiation().JSON().XML()\n\t\t// Accept-Charset vs:\n\t\tctx.Negotiation().Charset(\"utf-8\", \"iso-8859-7\")\n\t\t// Alternatively you can define the content/data per mime type\n\t\t// anywhere in the handlers chain using the optional \"v\" variadic\n\t\t// input argument of the Context.Negotiation().JSON,XML,YAML,Binary,Text,HTML(...) and e.t.c\n\t\t// example (order matters):\n\t\t// ctx.Negotiation().JSON(data).XML(data).Any(\"content for */*\")\n\t\t// ctx.Negotiate(nil)\n\n\t\t// if not nil passed in the `Context.Negotiate` method\n\t\t// then it overrides any contents made by the negotitation builder above.\n\t\t_, err := ctx.Negotiate(data)\n\t\tif err != nil {\n\t\t\tctx.Writef(\"%v\", err)\n\t\t}\n\t})\n\n\tapp.Get(\"/resource2\", func(ctx iris.Context) {\n\t\tjsonAndXML := testdata{\n\t\t\tName: \"test name\",\n\t\t\tAge:  26,\n\t\t}\n\n\t\t// I prefer that one, as it gives me the freedom to modify\n\t\t// response data per accepted mime content type on middlewares as well.\n\t\tctx.Negotiation().\n\t\t\tJSON(jsonAndXML).\n\t\t\tXML(jsonAndXML).\n\t\t\tHTML(\"<h1>Test Name</h1><h2>Age 26</h2>\")\n\n\t\tctx.Negotiate(nil)\n\t})\n\n\tapp.Get(\"/resource3\", func(ctx iris.Context) {\n\t\t// If that line is missing and the requested\n\t\t// mime type of content is */* or application/xml or application/json\n\t\t// then 406 Not Acceptable http error code will be rendered instead.\n\t\t//\n\t\t// We also add the \"gzip\" algorithm as an option to encode\n\t\t// resources on send.\n\t\tctx.Negotiation().JSON().XML().HTML().EncodingGzip()\n\n\t\tjsonAndXML := testdata{\n\t\t\tName: \"test name\",\n\t\t\tAge:  26,\n\t\t}\n\n\t\t// Prefer that way instead of the '/resource2' above\n\t\t// if \"iris.N\" is a static one and can be declared\n\t\t// outside of a handler.\n\t\tctx.Negotiate(iris.N{\n\t\t\t// Text: for text/plain,\n\t\t\t// Markdown: for text/mardown,\n\t\t\t// Binary: for application/octet-stream,\n\t\t\t// YAML: for application/x-yaml,\n\t\t\t// JSONP: for text/javascript\n\t\t\t// Other: for anything else,\n\t\t\tJSON: jsonAndXML,                          // for application/json\n\t\t\tXML:  jsonAndXML,                          // for application/xml or text/xml\n\t\t\tHTML: \"<h1>Test Name</h1><h2>Age 26</h2>\", // for text/html\n\t\t})\n\t})\n\n\treturn app\n}\n\nfunc main() {\n\tapp := newApp()\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/response-writer/content-negotiation/main_test.go",
    "content": "package main\n\nimport (\n\t\"bytes\"\n\t\"compress/gzip\"\n\t\"encoding/xml\"\n\t\"io\"\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nfunc TestContentNegotiation(t *testing.T) {\n\tvar (\n\t\texpectedJSONResponse = testdata{\n\t\t\tName: \"test name\",\n\t\t\tAge:  26,\n\t\t}\n\t\texpectedXMLResponse, _ = xml.Marshal(expectedJSONResponse)\n\t\texpectedHTMLResponse   = \"<h1>Test Name</h1><h2>Age 26</h2>\"\n\t)\n\n\tapp := newApp()\n\tapp.Configure(iris.WithOptimizations)\n\te := httptest.New(t, app)\n\n\te.GET(\"/resource\").WithHeader(\"Accept\", \"application/json\").\n\t\tExpect().Status(httptest.StatusOK).\n\t\tContentType(\"application/json\", \"utf-8\").\n\t\tJSON().IsEqual(expectedJSONResponse)\n\te.GET(\"/resource\").WithHeader(\"Accept\", \"application/xml\").WithHeader(\"Accept-Charset\", \"iso-8859-7\").\n\t\tExpect().Status(httptest.StatusOK).\n\t\tContentType(\"application/xml\", \"iso-8859-7\").\n\t\tBody().IsEqual(string(expectedXMLResponse))\n\n\te.GET(\"/resource2\").WithHeader(\"Accept\", \"application/json\").\n\t\tExpect().Status(httptest.StatusOK).\n\t\tContentType(\"application/json\", \"utf-8\").\n\t\tJSON().IsEqual(expectedJSONResponse)\n\te.GET(\"/resource2\").WithHeader(\"Accept\", \"application/xml\").\n\t\tExpect().Status(httptest.StatusOK).\n\t\tContentType(\"application/xml\", \"utf-8\").\n\t\tBody().IsEqual(string(expectedXMLResponse))\n\te.GET(\"/resource2\").WithHeader(\"Accept\", \"text/html\").\n\t\tExpect().Status(httptest.StatusOK).\n\t\tContentType(\"text/html\", \"utf-8\").\n\t\tBody().IsEqual(expectedHTMLResponse)\n\n\te.GET(\"/resource3\").WithHeader(\"Accept\", \"application/json\").\n\t\tExpect().Status(httptest.StatusOK).\n\t\tContentType(\"application/json\", \"utf-8\").\n\t\tJSON().IsEqual(expectedJSONResponse)\n\te.GET(\"/resource3\").WithHeader(\"Accept\", \"application/xml\").\n\t\tExpect().Status(httptest.StatusOK).\n\t\tContentType(\"application/xml\", \"utf-8\").\n\t\tBody().IsEqual(string(expectedXMLResponse))\n\n\t// test html with \"gzip\" encoding algorithm.\n\trawGzipResponse := e.GET(\"/resource3\").WithHeader(\"Accept\", \"text/html\").\n\t\tWithHeader(\"Accept-Encoding\", \"gzip\").\n\t\tExpect().Status(httptest.StatusOK).\n\t\tContentType(\"text/html\", \"utf-8\").\n\t\tContentEncoding(\"gzip\").\n\t\tBody().Raw()\n\n\tzr, err := gzip.NewReader(bytes.NewReader([]byte(rawGzipResponse)))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\trawResponse, err := io.ReadAll(zr)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif expected, got := expectedHTMLResponse, string(rawResponse); expected != got {\n\t\tt.Fatalf(\"expected response to be:\\n%s but got:\\n%s\", expected, got)\n\t}\n}\n"
  },
  {
    "path": "_examples/response-writer/http2push/main.go",
    "content": "// Server push lets the server preemptively \"push\" website assets\n// to the client without the user having explicitly asked for them.\n// When used with care, we can send what we know the user is going\n// to need for the page they're requesting.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.Get(\"/\", pushHandler)\n\tapp.Get(\"/main.js\", simpleAssetHandler)\n\n\tapp.Run(iris.TLS(\"127.0.0.1:443\", \"mycert.crt\", \"mykey.key\"))\n\t// $ openssl req -new -newkey rsa:4096 -x509 -sha256 \\\n\t// -days 365 -nodes -out mycert.crt -keyout mykey.key\n}\n\nfunc pushHandler(ctx iris.Context) {\n\t// The target must either be an absolute path (like \"/path\") or an absolute\n\t// URL that contains a valid host and the same scheme as the parent request.\n\t// If the target is a path, it will inherit the scheme and host of the\n\t// parent request.\n\ttarget := \"/main.js\"\n\n\tif pusher, ok := ctx.ResponseWriter().Naive().(http.Pusher); ok {\n\t\terr := pusher.Push(target, nil)\n\t\tif err != nil {\n\t\t\tif err == iris.ErrPushNotSupported {\n\t\t\t\tctx.StopWithText(iris.StatusHTTPVersionNotSupported, \"HTTP/2 push not supported.\")\n\t\t\t} else {\n\t\t\t\tctx.StopWithError(iris.StatusInternalServerError, err)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t}\n\n\tctx.HTML(fmt.Sprintf(`<html><body><script src=\"%s\"></script></body></html>`, target))\n}\n\nfunc simpleAssetHandler(ctx iris.Context) {\n\tctx.ServeFile(\"./public/main.js\")\n}\n"
  },
  {
    "path": "_examples/response-writer/http2push/mycert.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIIFazCCA1OgAwIBAgIUfwMd9auWixp19UnXOmyxJ9Jkv7IwDQYJKoZIhvcNAQEL\nBQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM\nGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMDA2MjUwOTUxNDdaFw0yMTA2\nMjUwOTUxNDdaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw\nHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggIiMA0GCSqGSIb3DQEB\nAQUAA4ICDwAwggIKAoICAQDlVGyGAQ9uyfNbwZyrtYOSjLpxf5NpNToh2OzU7gy2\nOexBji5lmWBQ3oYDG+FjAkbHORPzOMNpeMwje+IjGZBw8x6E+8WoGdSzbrEZ6pUV\nwKJGKEuDlx6g6HEmtv3ZwgGe20gvPjjW+oCO888dwK/mbIHrHTq4nO3o0gAdAJwu\namn9BlHU5O4RW7BQ4tLF+j/fBCACWRG1NHXA0AT8eg544GyCdyteAH11oCDsHS8/\nDAPsM6t+tZrMCIt9+9dzPdVoOmQNaMMrcz8eJohddRTK6zHe9ixZTt/soayOF7OS\nQQeekbr3HPYhD450zRVplLMHx7wnph/+O+Po6bqDnUzdnkqAAwwymQapHMuHXZKN\nrhdfKau3rVo1GeXLIRgeWLUoxFSm4TYshrgt+0AidLRH+dCY7MS9Ngga/sAK3vID\ngSF75mFgOhY+q7nvY9Ecao6TnoNNRY29hUat4y0VwSyysUy887vHr6lMK5CrAT/l\nCh8fuu20HUCoiLwMJvA6+wpivZkuiIvWY7bVGYsEYrrW+bCNN9wCGYTZEyX++os9\nv/38wdOqGUT00ewXkjIUFCWbrnxxSr98kF3w3wPf9K4Y40MNxeR90nyX4zjXGF1/\n91msUh+iivsz9mcN9DK83fgTyOsoVLX5cm/L2UBwMacsfjBbN4djOc5IuYMar/VN\nGQIDAQABo1MwUTAdBgNVHQ4EFgQUtkf+yAvqgZC8f22iJny9hFEDolMwHwYDVR0j\nBBgwFoAUtkf+yAvqgZC8f22iJny9hFEDolMwDwYDVR0TAQH/BAUwAwEB/zANBgkq\nhkiG9w0BAQsFAAOCAgEAE2QasBVru618rxupyJgEHw6r4iv7sz1Afz3Q5qJ4oSA9\nxVsrVCjr3iHRFSw8Rf670E8Ffk/JjzS65mHw6zeZj/ANBKQWLjRlqzYXeetq5HzG\nSIgaG7p1RFvvzz3+leFGzjinZ6sKbfB4OB72o2YN+fO8DsDxgGKll0W4KAazizSe\nHY9Pgu437tWnwF16rFO3IL47n5HzYlRoGIPOpzFoNX5+fyn9GlnKEtONF2QBKTjY\nrdjvqFRByDiC74d8z/Yx8IiDRn1mTcG90JLR9+c6M7fruha9Y/rJfw+4AhVh5ZDz\nBl9rGPjwEs5zwutYvVAJzs7AVcighYP1lHKoJ7DxBDQeyBsYlUNk2l6bmZgLgGUZ\n+2OyWlqc/jD2GdDsIaZ4i7QqhTI/6aYZIf5zUkblKV1aMSaDulKxRv//OwW28Jax\n9EEoV7VaFb3sOkB/tZGhusXeQVtdrhahT3KkZLNwmNXoXWKJ5LjeUlFWJyV6JbDe\ny/PIWWCwWqyuFCSZS+Cg3RDgAzfSxkI8uVZ+IKKJS3UluDX45lxXtbRrvTQ+oDrA\n6ga5c1Vz9C4kn1K5yW4d7QIvg6vPiy7gvl+//sz9oxUM3yswInDBY0HKLgT0Uq9b\nYzLDh2RSaHsgHMPy2BKqR+q2N+lpg7inAWuJM1Huq6eHFqhiyQkzsfscBd1Dpm8=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "_examples/response-writer/http2push/mykey.key",
    "content": "-----BEGIN PRIVATE KEY-----\nMIIJRAIBADANBgkqhkiG9w0BAQEFAASCCS4wggkqAgEAAoICAQDlVGyGAQ9uyfNb\nwZyrtYOSjLpxf5NpNToh2OzU7gy2OexBji5lmWBQ3oYDG+FjAkbHORPzOMNpeMwj\ne+IjGZBw8x6E+8WoGdSzbrEZ6pUVwKJGKEuDlx6g6HEmtv3ZwgGe20gvPjjW+oCO\n888dwK/mbIHrHTq4nO3o0gAdAJwuamn9BlHU5O4RW7BQ4tLF+j/fBCACWRG1NHXA\n0AT8eg544GyCdyteAH11oCDsHS8/DAPsM6t+tZrMCIt9+9dzPdVoOmQNaMMrcz8e\nJohddRTK6zHe9ixZTt/soayOF7OSQQeekbr3HPYhD450zRVplLMHx7wnph/+O+Po\n6bqDnUzdnkqAAwwymQapHMuHXZKNrhdfKau3rVo1GeXLIRgeWLUoxFSm4TYshrgt\n+0AidLRH+dCY7MS9Ngga/sAK3vIDgSF75mFgOhY+q7nvY9Ecao6TnoNNRY29hUat\n4y0VwSyysUy887vHr6lMK5CrAT/lCh8fuu20HUCoiLwMJvA6+wpivZkuiIvWY7bV\nGYsEYrrW+bCNN9wCGYTZEyX++os9v/38wdOqGUT00ewXkjIUFCWbrnxxSr98kF3w\n3wPf9K4Y40MNxeR90nyX4zjXGF1/91msUh+iivsz9mcN9DK83fgTyOsoVLX5cm/L\n2UBwMacsfjBbN4djOc5IuYMar/VNGQIDAQABAoICAQCtWx1SSxjkcerxsLEDKApW\nzOTfiUXgoOjZz0ZwS6b2VWDfyWAPU1r4ps39KaU+F+lzDhWjpYQqhbMjG7G9QMTs\nbQvkEQLAaQ5duU5NPgQG1oCUsj8rMSBpGGz4jBnm834QHMk7VTjYYbKu3WTyo8cU\nU2/+UDEkfxRlC+IkCmMFv1FxgMZ5PbktC/eDnYMhP2Pq7Q5ZWAVHymk9IMK0LHwm\nKdg842K4A3zTXwGkGwetDCMm+YQpG5TxqX/w82BRcCuTR5h8fnYSsWLEIvKwWyIl\nppcjaUnrFPG2yhxLqWUIKPpehuEjjhQMt9rDNoh6MHsJZZY5Dp5eq91EIvLoLQ99\nhXBmD4P8LDop4r0jniPZJi/ACsaD0jBooA4525+Kouq7RP28Jp/pek7lVOOcBgRv\nD3zyESbKfqoaOfyfQ2ff4sILnTAr4V2nq3ekphGEYJrWN0ZoADcLdnr1cZ8L+VBI\no/4mi5/3HID/UEDliHSa97hxxGBEqTto0ZuXuNwfwx5ho33uVT6zNwRgiJ62Bgu3\nFhk/wVGuZxWvb1KHUNInG9cvsslhO4Vu9wJvYj91BnRq36rsyKKid5DrU+PNgmog\nlw3IXQpTojyRCYPuG9TKqEZ6b+so7GTKhBOjiwaupMOletVRGSAdbE81VN6HtxNW\naj39+FnxzMAlsieib+PBAQKCAQEA+t1fOYSaZBo7pZUmo2S0zulUEJjrYRGKJlWJ\n4psWSwFu/7/3UL4q0RBQaSRew9u/YSpaNlBYfcpnFVOjiLwHq5Hx46Eq0BuKsNlJ\n1/qxw9qjHqcrOre6K4/7NaWLPuM9fEmV+3MhFVXgv+WC5BHOowRTlOG30vIcC1J2\nL5xsBUsxDDY13cD1bLKRmFcyMFM8y7wMZmo7H/WfVmyoPKQaC43pTcmIXH0Jr2Ws\nWsfh18mhjtamaOPEFx5K0x4d0PI8tW5ouiUUkVIDaue27XfS969qEChv768/44eX\nWeqcekaG9jv2noMClt79rYd3Lne9HkgY6IT9FT+JqXfu+KYwuQKCAQEA6gYzUsGB\n9GQO8DE8AYn7JwNOtg1X4zKakXiGxH+nuZb7wJjAeGdYqTHySxPBXg0A2nDwoyz5\n4sAdLAr3FZoIvTzo7M5KIKFDzfyDmQDavhroH1mBAEiqKGNniP+RND3nWBBqDK1R\nqcqbhI3Kj5Ycany6a4nP+hZRBIyT9sfJ0S0YruSY8IGXgDwhlJrZ7bsWMZylrgD/\n1qnPL0KqVBY8YR8msRj88h72IlD5o0kwvisOIvyhA0YgwGBb6lg7A+DifiF03ZlS\n2yELbIkKDVr+p3jC7MBh4B+OJY68AMl6wVjAaDM1AZnpjKE5YmZg5+Ks5823zILo\nPrSB9hn0+DIPYQKCAQEAh9x+JuNmzhHa/dkiHNl8hpadHYQD7gUWwZ4P1/bQAv0a\nxU2MvmDPRXxFYDv/SqlnI1NRmhq3YiDM5SLv7SyQJt4al4IAcsaHvTFgqaSuw3hU\nYVR9uAYqwE7w6OPn3r4o3Xfoz05Ru4FP//1nfucZ9vVv4rC/4nGWuJcHRM+9PLy1\nKnztfVR0VlL7QPrwRnW99kS4nnqn3K4khiTAlF73cAyCLsuXmydoqGIzDtMzv68G\nXRpo82NvHmoccevcj/2w3T2XYECWvAEjsrEdQ8xiKBwLIAcWYEOUIUCcumiyKBKs\nIwzkioI/U8AeuO0lobfdZ1n6i2sCuZA4mNxIQseWmQKCAQEA5YkfXdQeuq5JWJ1x\n1bCYfjNoSHfd9CH2KSimRqVOxWGpm8Y3QeFbvNgYZjsCNlVauOZ9oA7FKfp0onY+\n0xk56SKM83eCjW6fKrK6AKAt7LhHZDhNpxGek+6r5luE+FCfUGkJG1YD+x2WW/UW\n8K6zQF8GGeQZ8Zlh7axUlIBxGpG43BGrUHpLNqPD7BXWGq6dnhufBYRFay8y34/r\nsH3+yuPa92ki7/geQppZwCZRgLSKMRbIdoWaKhZZEQlpGOzCOiRmk9OGyRcoNVRU\nX7UYgPqZdc1cMo/AxGWzULJNjMaYMZvIKcHkqOKZfkIcWlSictn7pMPhN1+k+NWM\nyMORAQKCAQAyXl02h/c2ihx6cjKlnNeDr2ZfzkoiAvFuKaoAR+KVvb9F9X7ZgKSi\nwudZyelTglIVCYXeRmG09uX3rNGCzFrweRwgn6x/8DnN5pMRJVZOXFdgR+V9uKep\nK6F7DYbPyggvLOAsezB+09i9lwxM+XdA2whVpL5NFR1rGfFglnE1EQHcEvNONkcv\n0h8x9cNSptJyRDLiTIKI9EhonuzwzkGpvjULQE8MLbT8PbjoLFINcE9ZWhwtyw0V\nXO32KE8iLKt3KzHz9CfTRCI3M7DwD752AC6zRr8ZS/HXzs+5WTkdVVEtRC7Abd3y\nW2TzuSMYNDu876twbTVQJED3mwOAQ3J7\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "_examples/response-writer/http2push/public/main.js",
    "content": "window.alert(\"javascript loaded\");"
  },
  {
    "path": "_examples/response-writer/json-third-party/go.mod",
    "content": "module github.com/kataras/iris/_examples/response-writer/json-third-party\n\ngo 1.25\n\nrequire (\n\tgithub.com/bytedance/sonic v1.14.2\n\tgithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd\n)\n\nrequire (\n\tgithub.com/BurntSushi/toml v1.6.0 // indirect\n\tgithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect\n\tgithub.com/CloudyKit/jet/v6 v6.3.1 // indirect\n\tgithub.com/Joker/jade v1.1.3 // indirect\n\tgithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 // indirect\n\tgithub.com/andybalholm/brotli v1.2.0 // indirect\n\tgithub.com/aymerick/douceur v0.2.0 // indirect\n\tgithub.com/bytedance/gopkg v0.1.3 // indirect\n\tgithub.com/bytedance/sonic/loader v0.4.0 // indirect\n\tgithub.com/cloudwego/base64x v0.1.6 // indirect\n\tgithub.com/fatih/structs v1.1.0 // indirect\n\tgithub.com/flosch/pongo2/v4 v4.0.2 // indirect\n\tgithub.com/golang/snappy v1.0.0 // indirect\n\tgithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a // indirect\n\tgithub.com/google/uuid v1.6.0 // indirect\n\tgithub.com/gorilla/css v1.0.1 // indirect\n\tgithub.com/iris-contrib/schema v0.0.6 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/kataras/blocks v0.0.8 // indirect\n\tgithub.com/kataras/golog v0.1.12 // indirect\n\tgithub.com/kataras/pio v0.0.13 // indirect\n\tgithub.com/kataras/sitemap v0.0.6 // indirect\n\tgithub.com/kataras/tunnel v0.0.4 // indirect\n\tgithub.com/klauspost/compress v1.18.2 // indirect\n\tgithub.com/klauspost/cpuid/v2 v2.2.9 // indirect\n\tgithub.com/kr/pretty v0.3.1 // indirect\n\tgithub.com/mailgun/raymond/v2 v2.0.48 // indirect\n\tgithub.com/mailru/easyjson v0.9.1 // indirect\n\tgithub.com/microcosm-cc/bluemonday v1.0.27 // indirect\n\tgithub.com/rogpeppe/go-internal v1.10.0 // indirect\n\tgithub.com/russross/blackfriday/v2 v2.1.0 // indirect\n\tgithub.com/schollz/closestmatch v2.1.0+incompatible // indirect\n\tgithub.com/sirupsen/logrus v1.8.1 // indirect\n\tgithub.com/stretchr/testify v1.11.1 // indirect\n\tgithub.com/tdewolff/minify/v2 v2.24.8 // indirect\n\tgithub.com/tdewolff/parse/v2 v2.8.5 // indirect\n\tgithub.com/twitchyliquid64/golang-asm v0.15.1 // indirect\n\tgithub.com/valyala/bytebufferpool v1.0.0 // indirect\n\tgithub.com/vmihailenco/msgpack/v5 v5.4.1 // indirect\n\tgithub.com/vmihailenco/tagparser/v2 v2.0.0 // indirect\n\tgithub.com/yosssi/ace v0.0.5 // indirect\n\tgolang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect\n\tgolang.org/x/crypto v0.47.0 // indirect\n\tgolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 // indirect\n\tgolang.org/x/net v0.49.0 // indirect\n\tgolang.org/x/sys v0.40.0 // indirect\n\tgolang.org/x/text v0.33.0 // indirect\n\tgolang.org/x/time v0.14.0 // indirect\n\tgoogle.golang.org/protobuf v1.36.11 // indirect\n\tgopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect\n\tgopkg.in/ini.v1 v1.67.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n"
  },
  {
    "path": "_examples/response-writer/json-third-party/go.sum",
    "content": "github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=\ngithub.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=\ngithub.com/CloudyKit/jet/v6 v6.3.1 h1:6IAo5Cx21xrHVaR8zzXN5gJatKV/wO7Nf6bfCnCSbUw=\ngithub.com/CloudyKit/jet/v6 v6.3.1/go.mod h1:lf8ksdNsxZt7/yH/3n4vJQWA9RUq4wpaHtArHhGVMOw=\ngithub.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc=\ngithub.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=\ngithub.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=\ngithub.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 h1:dG7/gLroJGht/jSQtHiLvT48Hxn+crbmvyItZC8cWOs=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05/go.mod h1:NYezi6wtnJtBm5btoprXc5SvAdqH0XTXWnUup0MptAI=\ngithub.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=\ngithub.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=\ngithub.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=\ngithub.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=\ngithub.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=\ngithub.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=\ngithub.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=\ngithub.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=\ngithub.com/bytedance/sonic v1.14.2 h1:k1twIoe97C1DtYUo+fZQy865IuHia4PR5RPiuGPPIIE=\ngithub.com/bytedance/sonic v1.14.2/go.mod h1:T80iDELeHiHKSc0C9tubFygiuXoGzrkjKzX2quAx980=\ngithub.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2NYzevs+o=\ngithub.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=\ngithub.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=\ngithub.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=\ngithub.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=\ngithub.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=\ngithub.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=\ngithub.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw=\ngithub.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8=\ngithub.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=\ngithub.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=\ngithub.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=\ngithub.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a h1:l7A0loSszR5zHd/qK53ZIHMO8b3bBSmENnQ6eKnUT0A=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=\ngithub.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=\ngithub.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=\ngithub.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=\ngithub.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=\ngithub.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2 h1:T9THsdP1woyAqKHwjkEsbCnMefsAFvk8iJJKokcJ3Go=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2/go.mod h1:JLDgIqnFy5loDSUv1OA2j0mb6p/rDhiCqigP22Uq9xE=\ngithub.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw=\ngithub.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/kataras/blocks v0.0.8 h1:MrpVhoFTCR2v1iOOfGng5VJSILKeZZI+7NGfxEh3SUM=\ngithub.com/kataras/blocks v0.0.8/go.mod h1:9Jm5zx6BB+06NwA+OhTbHW1xkMOYxahnqTN5DveZ2Yg=\ngithub.com/kataras/golog v0.1.12 h1:Bu7I/G4ilJlbfzjmU39O9N+2uO1pBcMK045fzZ4ytNg=\ngithub.com/kataras/golog v0.1.12/go.mod h1:wrGSbOiBqbQSQznleVNX4epWM8rl9SJ/rmEacl0yqy4=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd h1:oMsQgqKACbUdlaIWnN+EZzzAnHkw90hE/y/R0BSij2U=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd/go.mod h1:yucjtDl9Vn3OctWM1fi0MuM9OckemC7kEreFa9QtPF8=\ngithub.com/kataras/pio v0.0.13 h1:x0rXVX0fviDTXOOLOmr4MUxOabu1InVSTu5itF8CXCM=\ngithub.com/kataras/pio v0.0.13/go.mod h1:k3HNuSw+eJ8Pm2lA4lRhg3DiCjVgHlP8hmXApSej3oM=\ngithub.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY=\ngithub.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4=\ngithub.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA=\ngithub.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw=\ngithub.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=\ngithub.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=\ngithub.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=\ngithub.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=\ngithub.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=\ngithub.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8=\ngithub.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=\ngithub.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=\ngithub.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=\ngithub.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=\ngithub.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=\ngithub.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=\ngithub.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=\ngithub.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=\ngithub.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=\ngithub.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=\ngithub.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=\ngithub.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=\ngithub.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk=\ngithub.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=\ngithub.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=\ngithub.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=\ngithub.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=\ngithub.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=\ngithub.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=\ngithub.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=\ngithub.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=\ngithub.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/tdewolff/minify/v2 v2.24.8 h1:58/VjsbevI4d5FGV0ZSuBrHMSSkH4MCH0sIz/eKIauE=\ngithub.com/tdewolff/minify/v2 v2.24.8/go.mod h1:0Ukj0CRpo/sW/nd8uZ4ccXaV1rEVIWA3dj8U7+Shhfw=\ngithub.com/tdewolff/parse/v2 v2.8.5 h1:ZmBiA/8Do5Rpk7bDye0jbbDUpXXbCdc3iah4VeUvwYU=\ngithub.com/tdewolff/parse/v2 v2.8.5/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=\ngithub.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE=\ngithub.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=\ngithub.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=\ngithub.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=\ngithub.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=\ngithub.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=\ngithub.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=\ngithub.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=\ngithub.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=\ngithub.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=\ngithub.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA=\ngithub.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0=\ngithub.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=\ngithub.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=\ngithub.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngolang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU=\ngolang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=\ngolang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=\ngolang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=\ngolang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=\ngolang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=\ngolang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=\ngolang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=\ngolang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=\ngolang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=\ngoogle.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=\ngopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nmoul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs=\nmoul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE=\n"
  },
  {
    "path": "_examples/response-writer/json-third-party/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n\n\t// This is a 3rd-party library, which you can use to override the default behavior of ctx.JSON method.\n\t\"github.com/bytedance/sonic\"\n)\n\nfunc init() {\n\tapplyIrisGlobalPatches() // <- IMPORTANT.\n}\n\nfunc applyIrisGlobalPatches() {\n\tvar json = sonic.ConfigFastest\n\n\t// Apply global modifications to the context REST writers\n\t// without modifications to your web server's handlers code.\n\tiris.Patches().Context().Writers().JSON(func(ctx iris.Context, v any, options *iris.JSON) error {\n\t\tenc := json.NewEncoder(ctx.ResponseWriter())\n\t\tenc.SetEscapeHTML(!options.UnescapeHTML)\n\t\tenc.SetIndent(\"\", options.Indent)\n\t\treturn enc.Encode(v)\n\t})\n}\n\n// User example struct for json.\ntype User struct {\n\tFirstname string `json:\"firstname\"`\n\tLastname  string `json:\"lastname\"`\n\tCity      string `json:\"city\"`\n\tAge       int    `json:\"age\"`\n}\n\nfunc main() {\n\tapp := iris.New()\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tuser := User{\n\t\t\tFirstname: \"Gerasimos\",\n\t\t\tLastname:  \"Maropoulos\",\n\t\t\tCity:      \"Athens\",\n\t\t\tAge:       29,\n\t\t}\n\n\t\t// Use ctx.JSON as you used to.\n\t\tctx.JSON(user)\n\t})\n\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/response-writer/protobuf/README.md",
    "content": "# Protocol Buffers\n\nThe `Context.Protobuf(proto.Message)` is the method which sends protos to the client. It accepts a [proto.Message](https://godoc.org/google.golang.org/protobuf/proto#Message) value.\n\n> Note: Iris is using the newest version of the Go protocol buffers implementation. Read more about it at [The Go Blog: A new Go API for Protocol Buffers](https://blog.golang.org/protobuf-apiv2).\n\n\n1. Install the protoc-gen-go tool.\n\n```sh\n$ go get -u google.golang.org/protobuf/cmd/protoc-gen-go@latest\n```\n\n2. Generate proto\n\n```sh\n$ protoc -I protos/ protos/hello.proto --go_out=.\n```\n"
  },
  {
    "path": "_examples/response-writer/protobuf/go.mod",
    "content": "module app\n\ngo 1.25\n\nrequire (\n\tgithub.com/golang/protobuf v1.5.0\n\tgithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd\n\tgoogle.golang.org/protobuf v1.36.11\n)\n\nrequire (\n\tgithub.com/BurntSushi/toml v1.6.0 // indirect\n\tgithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect\n\tgithub.com/CloudyKit/jet/v6 v6.3.1 // indirect\n\tgithub.com/Joker/jade v1.1.3 // indirect\n\tgithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 // indirect\n\tgithub.com/andybalholm/brotli v1.2.0 // indirect\n\tgithub.com/aymerick/douceur v0.2.0 // indirect\n\tgithub.com/fatih/structs v1.1.0 // indirect\n\tgithub.com/flosch/pongo2/v4 v4.0.2 // indirect\n\tgithub.com/golang/snappy v1.0.0 // indirect\n\tgithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a // indirect\n\tgithub.com/google/uuid v1.6.0 // indirect\n\tgithub.com/gorilla/css v1.0.1 // indirect\n\tgithub.com/iris-contrib/schema v0.0.6 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/kataras/blocks v0.0.8 // indirect\n\tgithub.com/kataras/golog v0.1.12 // indirect\n\tgithub.com/kataras/pio v0.0.13 // indirect\n\tgithub.com/kataras/sitemap v0.0.6 // indirect\n\tgithub.com/kataras/tunnel v0.0.4 // indirect\n\tgithub.com/klauspost/compress v1.18.2 // indirect\n\tgithub.com/kr/pretty v0.3.1 // indirect\n\tgithub.com/mailgun/raymond/v2 v2.0.48 // indirect\n\tgithub.com/mailru/easyjson v0.9.1 // indirect\n\tgithub.com/microcosm-cc/bluemonday v1.0.27 // indirect\n\tgithub.com/rogpeppe/go-internal v1.10.0 // indirect\n\tgithub.com/russross/blackfriday/v2 v2.1.0 // indirect\n\tgithub.com/schollz/closestmatch v2.1.0+incompatible // indirect\n\tgithub.com/sirupsen/logrus v1.8.1 // indirect\n\tgithub.com/stretchr/testify v1.11.1 // indirect\n\tgithub.com/tdewolff/minify/v2 v2.24.8 // indirect\n\tgithub.com/tdewolff/parse/v2 v2.8.5 // indirect\n\tgithub.com/valyala/bytebufferpool v1.0.0 // indirect\n\tgithub.com/vmihailenco/msgpack/v5 v5.4.1 // indirect\n\tgithub.com/vmihailenco/tagparser/v2 v2.0.0 // indirect\n\tgithub.com/yosssi/ace v0.0.5 // indirect\n\tgolang.org/x/crypto v0.47.0 // indirect\n\tgolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 // indirect\n\tgolang.org/x/net v0.49.0 // indirect\n\tgolang.org/x/sys v0.40.0 // indirect\n\tgolang.org/x/text v0.33.0 // indirect\n\tgolang.org/x/time v0.14.0 // indirect\n\tgopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect\n\tgopkg.in/ini.v1 v1.67.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n"
  },
  {
    "path": "_examples/response-writer/protobuf/go.sum",
    "content": "github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=\ngithub.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=\ngithub.com/CloudyKit/jet/v6 v6.3.1 h1:6IAo5Cx21xrHVaR8zzXN5gJatKV/wO7Nf6bfCnCSbUw=\ngithub.com/CloudyKit/jet/v6 v6.3.1/go.mod h1:lf8ksdNsxZt7/yH/3n4vJQWA9RUq4wpaHtArHhGVMOw=\ngithub.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc=\ngithub.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=\ngithub.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=\ngithub.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 h1:dG7/gLroJGht/jSQtHiLvT48Hxn+crbmvyItZC8cWOs=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05/go.mod h1:NYezi6wtnJtBm5btoprXc5SvAdqH0XTXWnUup0MptAI=\ngithub.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=\ngithub.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=\ngithub.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=\ngithub.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=\ngithub.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=\ngithub.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=\ngithub.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=\ngithub.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=\ngithub.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=\ngithub.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw=\ngithub.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8=\ngithub.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=\ngithub.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=\ngithub.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4=\ngithub.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=\ngithub.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=\ngithub.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a h1:l7A0loSszR5zHd/qK53ZIHMO8b3bBSmENnQ6eKnUT0A=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=\ngithub.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=\ngithub.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=\ngithub.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=\ngithub.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=\ngithub.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=\ngithub.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2 h1:T9THsdP1woyAqKHwjkEsbCnMefsAFvk8iJJKokcJ3Go=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2/go.mod h1:JLDgIqnFy5loDSUv1OA2j0mb6p/rDhiCqigP22Uq9xE=\ngithub.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw=\ngithub.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/kataras/blocks v0.0.8 h1:MrpVhoFTCR2v1iOOfGng5VJSILKeZZI+7NGfxEh3SUM=\ngithub.com/kataras/blocks v0.0.8/go.mod h1:9Jm5zx6BB+06NwA+OhTbHW1xkMOYxahnqTN5DveZ2Yg=\ngithub.com/kataras/golog v0.1.12 h1:Bu7I/G4ilJlbfzjmU39O9N+2uO1pBcMK045fzZ4ytNg=\ngithub.com/kataras/golog v0.1.12/go.mod h1:wrGSbOiBqbQSQznleVNX4epWM8rl9SJ/rmEacl0yqy4=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd h1:oMsQgqKACbUdlaIWnN+EZzzAnHkw90hE/y/R0BSij2U=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd/go.mod h1:yucjtDl9Vn3OctWM1fi0MuM9OckemC7kEreFa9QtPF8=\ngithub.com/kataras/pio v0.0.13 h1:x0rXVX0fviDTXOOLOmr4MUxOabu1InVSTu5itF8CXCM=\ngithub.com/kataras/pio v0.0.13/go.mod h1:k3HNuSw+eJ8Pm2lA4lRhg3DiCjVgHlP8hmXApSej3oM=\ngithub.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY=\ngithub.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4=\ngithub.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA=\ngithub.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw=\ngithub.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=\ngithub.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=\ngithub.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=\ngithub.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8=\ngithub.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=\ngithub.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=\ngithub.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=\ngithub.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=\ngithub.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=\ngithub.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=\ngithub.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=\ngithub.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=\ngithub.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=\ngithub.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=\ngithub.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=\ngithub.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=\ngithub.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk=\ngithub.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=\ngithub.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=\ngithub.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=\ngithub.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=\ngithub.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/tdewolff/minify/v2 v2.24.8 h1:58/VjsbevI4d5FGV0ZSuBrHMSSkH4MCH0sIz/eKIauE=\ngithub.com/tdewolff/minify/v2 v2.24.8/go.mod h1:0Ukj0CRpo/sW/nd8uZ4ccXaV1rEVIWA3dj8U7+Shhfw=\ngithub.com/tdewolff/parse/v2 v2.8.5 h1:ZmBiA/8Do5Rpk7bDye0jbbDUpXXbCdc3iah4VeUvwYU=\ngithub.com/tdewolff/parse/v2 v2.8.5/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=\ngithub.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE=\ngithub.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=\ngithub.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=\ngithub.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=\ngithub.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=\ngithub.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=\ngithub.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=\ngithub.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=\ngithub.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA=\ngithub.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0=\ngithub.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=\ngithub.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=\ngithub.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=\ngolang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=\ngolang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=\ngolang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=\ngolang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=\ngolang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=\ngolang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=\ngolang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=\ngolang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=\ngoogle.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=\ngoogle.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=\ngopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nmoul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs=\nmoul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE=\n"
  },
  {
    "path": "_examples/response-writer/protobuf/main.go",
    "content": "package main\n\nimport (\n\t\"app/protos\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\tapp.Get(\"/\", send)\n\tapp.Get(\"/json\", sendAsJSON)\n\tapp.Post(\"/read\", read)\n\tapp.Post(\"/read_json\", readFromJSON)\n\n\tapp.Listen(\":8080\")\n}\n\nfunc send(ctx iris.Context) {\n\tresponse := &protos.HelloReply{Message: \"Hello, World!\"}\n\tctx.Protobuf(response)\n}\n\nfunc sendAsJSON(ctx iris.Context) {\n\tresponse := &protos.HelloReply{Message: \"Hello, World!\"}\n\toptions := iris.JSON{\n\t\tProto: iris.ProtoMarshalOptions{\n\t\t\tAllowPartial: true,\n\t\t\tMultiline:    true,\n\t\t\tIndent:       \"    \",\n\t\t},\n\t}\n\n\tctx.JSON(response, options)\n}\n\nfunc read(ctx iris.Context) {\n\tvar request protos.HelloRequest\n\n\terr := ctx.ReadProtobuf(&request)\n\tif err != nil {\n\t\tctx.StopWithError(iris.StatusBadRequest, err)\n\t\treturn\n\t}\n\n\tctx.Writef(\"HelloRequest.Name = %s\", request.Name)\n}\n\nfunc readFromJSON(ctx iris.Context) {\n\tvar request protos.HelloRequest\n\n\terr := ctx.ReadJSONProtobuf(&request)\n\tif err != nil {\n\t\tctx.StopWithError(iris.StatusBadRequest, err)\n\t\treturn\n\t}\n\n\tctx.Writef(\"HelloRequest.Name = %s\", request.Name)\n}\n"
  },
  {
    "path": "_examples/response-writer/protobuf/protos/hello.pb.go",
    "content": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.25.0\n// \tprotoc        v3.11.1\n// source: hello.proto\n\npackage protos\n\nimport (\n\tproto \"github.com/golang/protobuf/proto\"\n\tprotoreflect \"google.golang.org/protobuf/reflect/protoreflect\"\n\tprotoimpl \"google.golang.org/protobuf/runtime/protoimpl\"\n\treflect \"reflect\"\n\tsync \"sync\"\n)\n\nconst (\n\t// Verify that this generated code is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)\n\t// Verify that runtime/protoimpl is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)\n)\n\n// This is a compile-time assertion that a sufficiently up-to-date version\n// of the legacy proto package is being used.\nconst _ = proto.ProtoPackageIsVersion4\n\ntype HelloRequest struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tName string `protobuf:\"bytes,1,opt,name=name,proto3\" json:\"name,omitempty\"`\n}\n\nfunc (x *HelloRequest) Reset() {\n\t*x = HelloRequest{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_hello_proto_msgTypes[0]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *HelloRequest) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*HelloRequest) ProtoMessage() {}\n\nfunc (x *HelloRequest) ProtoReflect() protoreflect.Message {\n\tmi := &file_hello_proto_msgTypes[0]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use HelloRequest.ProtoReflect.Descriptor instead.\nfunc (*HelloRequest) Descriptor() ([]byte, []int) {\n\treturn file_hello_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *HelloRequest) GetName() string {\n\tif x != nil {\n\t\treturn x.Name\n\t}\n\treturn \"\"\n}\n\ntype HelloReply struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tMessage string `protobuf:\"bytes,1,opt,name=message,proto3\" json:\"message,omitempty\"`\n}\n\nfunc (x *HelloReply) Reset() {\n\t*x = HelloReply{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_hello_proto_msgTypes[1]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *HelloReply) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*HelloReply) ProtoMessage() {}\n\nfunc (x *HelloReply) ProtoReflect() protoreflect.Message {\n\tmi := &file_hello_proto_msgTypes[1]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use HelloReply.ProtoReflect.Descriptor instead.\nfunc (*HelloReply) Descriptor() ([]byte, []int) {\n\treturn file_hello_proto_rawDescGZIP(), []int{1}\n}\n\nfunc (x *HelloReply) GetMessage() string {\n\tif x != nil {\n\t\treturn x.Message\n\t}\n\treturn \"\"\n}\n\nvar File_hello_proto protoreflect.FileDescriptor\n\nvar file_hello_proto_rawDesc = []byte{\n\t0x0a, 0x0b, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x70,\n\t0x72, 0x6f, 0x74, 0x6f, 0x73, 0x22, 0x22, 0x0a, 0x0c, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65,\n\t0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20,\n\t0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x26, 0x0a, 0x0a, 0x48, 0x65, 0x6c,\n\t0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61,\n\t0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67,\n\t0x65, 0x42, 0x0a, 0x5a, 0x08, 0x2e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x62, 0x06, 0x70,\n\t0x72, 0x6f, 0x74, 0x6f, 0x33,\n}\n\nvar (\n\tfile_hello_proto_rawDescOnce sync.Once\n\tfile_hello_proto_rawDescData = file_hello_proto_rawDesc\n)\n\nfunc file_hello_proto_rawDescGZIP() []byte {\n\tfile_hello_proto_rawDescOnce.Do(func() {\n\t\tfile_hello_proto_rawDescData = protoimpl.X.CompressGZIP(file_hello_proto_rawDescData)\n\t})\n\treturn file_hello_proto_rawDescData\n}\n\nvar file_hello_proto_msgTypes = make([]protoimpl.MessageInfo, 2)\nvar file_hello_proto_goTypes = []any{\n\t(*HelloRequest)(nil), // 0: protos.HelloRequest\n\t(*HelloReply)(nil),   // 1: protos.HelloReply\n}\nvar file_hello_proto_depIdxs = []int32{\n\t0, // [0:0] is the sub-list for method output_type\n\t0, // [0:0] is the sub-list for method input_type\n\t0, // [0:0] is the sub-list for extension type_name\n\t0, // [0:0] is the sub-list for extension extendee\n\t0, // [0:0] is the sub-list for field type_name\n}\n\nfunc init() { file_hello_proto_init() }\nfunc file_hello_proto_init() {\n\tif File_hello_proto != nil {\n\t\treturn\n\t}\n\tif !protoimpl.UnsafeEnabled {\n\t\tfile_hello_proto_msgTypes[0].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*HelloRequest); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_hello_proto_msgTypes[1].Exporter = func(v any, i int) any {\n\t\t\tswitch v := v.(*HelloReply); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n\ttype x struct{}\n\tout := protoimpl.TypeBuilder{\n\t\tFile: protoimpl.DescBuilder{\n\t\t\tGoPackagePath: reflect.TypeOf(x{}).PkgPath(),\n\t\t\tRawDescriptor: file_hello_proto_rawDesc,\n\t\t\tNumEnums:      0,\n\t\t\tNumMessages:   2,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   0,\n\t\t},\n\t\tGoTypes:           file_hello_proto_goTypes,\n\t\tDependencyIndexes: file_hello_proto_depIdxs,\n\t\tMessageInfos:      file_hello_proto_msgTypes,\n\t}.Build()\n\tFile_hello_proto = out.File\n\tfile_hello_proto_rawDesc = nil\n\tfile_hello_proto_goTypes = nil\n\tfile_hello_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "_examples/response-writer/protobuf/protos/hello.proto",
    "content": "syntax = \"proto3\";\n\npackage protos;\n\noption go_package = \"./protos\";\n\nmessage HelloRequest {\n    string name = 1;\n}\n\nmessage HelloReply {\n    string message = 1;\n}"
  },
  {
    "path": "_examples/response-writer/sse/main.go",
    "content": "// Package main shows how to send continuous event messages to the clients through SSE via a broker.\n// Read details at: https://www.w3schools.com/htmL/html5_serversentevents.asp and\n// https://robots.thoughtbot.com/writing-a-server-sent-events-server-in-go\npackage main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/kataras/golog\"\n\t\"github.com/kataras/iris/v12\"\n)\n\n// A Broker holds open client connections,\n// listens for incoming events on its Notifier channel\n// and broadcast event data to all registered connections.\ntype Broker struct {\n\n\t// Events are pushed to this channel by the main events-gathering routine.\n\tNotifier chan []byte\n\n\t// New client connections.\n\tnewClients chan chan []byte\n\n\t// Closed client connections.\n\tclosingClients chan chan []byte\n\n\t// Client connections registry.\n\tclients map[chan []byte]bool\n}\n\n// NewBroker returns a new broker factory.\nfunc NewBroker() *Broker {\n\tb := &Broker{\n\t\tNotifier:       make(chan []byte, 1),\n\t\tnewClients:     make(chan chan []byte),\n\t\tclosingClients: make(chan chan []byte),\n\t\tclients:        make(map[chan []byte]bool),\n\t}\n\n\t// Set it running - listening and broadcasting events.\n\tgo b.listen()\n\n\treturn b\n}\n\n// Listen on different channels and act accordingly.\nfunc (b *Broker) listen() {\n\tfor {\n\t\tselect {\n\t\tcase s := <-b.newClients:\n\t\t\t// A new client has connected.\n\t\t\t// Register their message channel.\n\t\t\tb.clients[s] = true\n\t\t\tgolog.Infof(\"Client added. %d registered clients\", len(b.clients))\n\n\t\tcase s := <-b.closingClients:\n\t\t\t// A client has dettached and we want to\n\t\t\t// stop sending them messages.\n\t\t\tdelete(b.clients, s)\n\t\t\tgolog.Infof(\"Removed client. %d registered clients\", len(b.clients))\n\n\t\tcase event := <-b.Notifier:\n\t\t\t// We got a new event from the outside!\n\t\t\t// Send event to all connected clients.\n\t\t\tfor clientMessageChan := range b.clients {\n\t\t\t\tclientMessageChan <- event\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc (b *Broker) ServeHTTP(ctx iris.Context) {\n\t// Make sure that the writer supports flushing.\n\n\tflusher, ok := ctx.ResponseWriter().Flusher()\n\tif !ok {\n\t\tctx.StopWithText(iris.StatusHTTPVersionNotSupported, \"Streaming unsupported!\")\n\t\treturn\n\t}\n\n\t// Set the headers related to event streaming, you can omit the \"application/json\" if you send plain text.\n\t// If you develop a go client, you must have: \"Accept\" : \"application/json, text/event-stream\" header as well.\n\tctx.ContentType(\"application/json, text/event-stream\")\n\tctx.Header(\"Cache-Control\", \"no-cache\")\n\tctx.Header(\"Connection\", \"keep-alive\")\n\t// We also add a Cross-origin Resource Sharing header so browsers on different domains can still connect.\n\tctx.Header(\"Access-Control-Allow-Origin\", \"*\")\n\n\t// Each connection registers its own message channel with the Broker's connections registry.\n\tmessageChan := make(chan []byte)\n\n\t// Signal the broker that we have a new connection.\n\tb.newClients <- messageChan\n\n\t// Listen to connection close and when the entire request handler chain exits(this handler here) and un-register messageChan.\n\tctx.OnClose(func(iris.Context) {\n\t\t// Remove this client from the map of connected clients\n\t\t// when this handler exits.\n\t\tb.closingClients <- messageChan\n\t})\n\n\t// Block waiting for messages broadcast on this connection's messageChan.\n\tfor {\n\t\t// Write to the ResponseWriter.\n\t\t// Server Sent Events compatible.\n\t\tctx.Writef(\"data: %s\\n\\n\", <-messageChan)\n\t\t// or json: data:{obj}.\n\n\t\t// Flush the data immediately instead of buffering it for later.\n\t\tflusher.Flush()\n\t}\n}\n\ntype event struct {\n\tTimestamp int64  `json:\"timestamp\"`\n\tMessage   string `json:\"message\"`\n}\n\nconst script = `<script type=\"text/javascript\">\nif(typeof(EventSource) !== \"undefined\") {\n\tconsole.log(\"server-sent events supported\");\n\tvar client = new EventSource(\"http://localhost:8080/events\");\n\tvar index = 1;\n\tclient.onmessage = function (evt) {\n\t\tconsole.log(evt);\n\t\t// it's not required that you send and receive JSON, you can just output the \"evt.data\" as well.\n\t\tdataJSON = JSON.parse(evt.data)\n\t\tvar table = document.getElementById(\"messagesTable\");\n\t\tvar row = table.insertRow(index);\n\t\tvar cellTimestamp = row.insertCell(0);\n\t\tvar cellMessage = row.insertCell(1);\n\t\tcellTimestamp.innerHTML = dataJSON.timestamp;\n\t\tcellMessage.innerHTML = dataJSON.message;\n\t\tindex++;\n\n\t\twindow.scrollTo(0,document.body.scrollHeight);\n\t};\n} else {\n\tdocument.getElementById(\"header\").innerHTML = \"<h2>SSE not supported by this client-protocol</h2>\";\n}\n</script>`\n\nfunc main() {\n\tbroker := NewBroker()\n\n\tgo func() {\n\t\tfor {\n\t\t\ttime.Sleep(2 * time.Second)\n\n\t\t\tnow := time.Now()\n\t\t\tevt := event{\n\t\t\t\tTimestamp: now.Unix(),\n\t\t\t\tMessage:   fmt.Sprintf(\"Hello at %s\", now.Format(time.RFC1123)),\n\t\t\t}\n\n\t\t\tevtBytes, err := json.Marshal(evt)\n\t\t\tif err != nil {\n\t\t\t\tgolog.Error(err)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tbroker.Notifier <- evtBytes\n\t\t}\n\t}()\n\n\tapp := iris.New()\n\tapp.Logger().SetLevel(\"debug\")\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.HTML(\n\t\t\t`<html><head><title>SSE</title>` + script + `</head>\n\t\t\t\t<body>\n\t\t\t\t\t<h1 id=\"header\">Waiting for messages...</h1>\n\t\t\t\t\t<table id=\"messagesTable\" border=\"1\">\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<th>Timestamp (server)</th>\n\t\t\t\t\t\t\t<th>Message</th>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t</table>\n\t\t\t\t</body>\n\t\t\t </html>`)\n\t})\n\n\tapp.Get(\"/events\", broker.ServeHTTP)\n\n\t// http://localhost:8080\n\t// http://localhost:8080/events\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/response-writer/sse/optional.sse.mini.js.html",
    "content": "<!-- you can just put that to your favourite browser -->\n<html>\n\n<head>\n    <title>SSE (javascript side)</title>\n\n    <script type=\"text/javascript\">\n        var client = new EventSource(\"http://localhost:8080/events\")\n        client.onmessage = function (evt) {\n            console.log(evt)\n        }\n    </script>\n</head>\n<body>\n    <h1>Open the browser's console(F12) and watch for incoming event messages</h1>\n</body>\n</html>"
  },
  {
    "path": "_examples/response-writer/sse-third-party/main.go",
    "content": "package main\n\nimport (\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/r3labs/sse/v2\"\n)\n\n// First of all install the sse third-party package (you can use other if you don't like this approach or go ahead to the \"sse\" example)\n// $ go get github.com/r3labs/sse/v2@v2.7.4\nfunc main() {\n\tapp := iris.New()\n\ts := sse.New()\n\t/*\n\t\tThis creates a new stream inside of the scheduler.\n\t\tSeeing as there are no consumers, publishing a message\n\t\tto this channel will do nothing.\n\t\tClients can connect to this stream once the iris handler is started\n\t\tby specifying stream as a url parameter, like so:\n\t\thttp://localhost:8080/events?stream=messages\n\t*/\n\ts.CreateStream(\"messages\")\n\n\tapp.Any(\"/events\", iris.FromStd(s))\n\n\tgo func() {\n\t\t// You design when to send messages to the client,\n\t\t// here we just wait 5 seconds to send the first message\n\t\t// in order to give u time to open a browser window...\n\t\ttime.Sleep(5 * time.Second)\n\t\t// Publish a payload to the stream.\n\t\ts.Publish(\"messages\", &sse.Event{\n\t\t\tData: []byte(\"ping\"),\n\t\t})\n\n\t\ttime.Sleep(3 * time.Second)\n\t\ts.Publish(\"messages\", &sse.Event{\n\t\t\tData: []byte(\"second message\"),\n\t\t})\n\t\ttime.Sleep(2 * time.Second)\n\t\ts.Publish(\"messages\", &sse.Event{\n\t\t\tData: []byte(\"third message\"),\n\t\t})\n\t}() // ...\n\n\tapp.Listen(\":8080\")\n}\n\n/* For a golang SSE client you can look at: https://github.com/r3labs/sse#example-client */\n"
  },
  {
    "path": "_examples/response-writer/sse-third-party-2/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n    <title>SSE Examples</title>\n</head>\n<body>\n    <strong>Messages</strong>\n    <br>\n    <div id=\"messages\"></div>\n\n    <script type=\"text/javascript\">\n        e1 = new EventSource('/events/channel-1');\n        e1.onmessage = function(event) {\n            document.getElementById('messages').innerHTML += 'Message on channel-1: ' + event.data + '<br>';\n        };\n\n        e2 = new EventSource('/events/channel-2');\n        e2.onmessage = function(event) {\n            document.getElementById('messages').innerHTML += 'Message on channel-2: ' + event.data + '<br>';\n        };\n    </script>\n</body>\n</html>\n"
  },
  {
    "path": "_examples/response-writer/sse-third-party-2/main.go",
    "content": "package main\n\nimport (\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/alexandrevicenzi/go-sse\"\n\t\"github.com/kataras/iris/v12\"\n)\n\n// Install the sse third-party package.\n// $ go get -u github.com/alexandrevicenzi/go-sse\n//\n// Documentation: https://pkg.go.dev/github.com/alexandrevicenzi/go-sse\nfunc main() {\n\ts := sse.NewServer(&sse.Options{\n\t\t// Increase default retry interval to 10s.\n\t\tRetryInterval: 10 * 1000,\n\t\t// CORS headers\n\t\tHeaders: map[string]string{\n\t\t\t\"Access-Control-Allow-Origin\":  \"*\",\n\t\t\t\"Access-Control-Allow-Methods\": \"GET, OPTIONS\",\n\t\t\t\"Access-Control-Allow-Headers\": \"Keep-Alive,X-Requested-With,Cache-Control,Content-Type,Last-Event-ID\",\n\t\t},\n\t\t// Custom channel name generator\n\t\tChannelNameFunc: func(request *http.Request) string {\n\t\t\treturn request.URL.Path\n\t\t},\n\t\t// Print debug info\n\t\tLogger: log.New(os.Stdout, \"go-sse: \", log.Ldate|log.Ltime|log.Lshortfile),\n\t})\n\n\tdefer s.Shutdown()\n\n\tapp := iris.New()\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.ServeFile(\"./index.html\")\n\t})\n\tapp.Get(\"/events/{channel}\", iris.FromStd(s))\n\n\tgo func() {\n\t\tfor {\n\t\t\ts.SendMessage(\"/events/channel-1\", sse.SimpleMessage(time.Now().Format(\"2006/02/01/ 15:04:05\")))\n\t\t\ttime.Sleep(5 * time.Second)\n\t\t}\n\t}()\n\n\tgo func() {\n\t\ti := 0\n\t\tfor {\n\t\t\ti++\n\t\t\ts.SendMessage(\"/events/channel-2\", sse.SimpleMessage(strconv.Itoa(i)))\n\t\t\ttime.Sleep(5 * time.Second)\n\t\t}\n\t}()\n\n\tapp.Listen(\":3000\")\n}\n"
  },
  {
    "path": "_examples/response-writer/stream-writer/main.go",
    "content": "package main\n\nimport (\n\t\"errors\"\n\t\"io\"\n\t\"time\" // showcase the delay\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nvar errDone = errors.New(\"done\")\n\nfunc main() {\n\tapp := iris.New()\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.ContentType(\"text/html\")\n\t\tctx.Header(\"Transfer-Encoding\", \"chunked\")\n\t\ti := 0\n\t\tints := []int{1, 2, 3, 5, 7, 9, 11, 13, 15, 17, 23, 29}\n\t\t// Send the response in chunks and wait for half a second between each chunk,\n\t\t// until connection close.\n\t\terr := ctx.StreamWriter(func(w io.Writer) error {\n\t\t\tctx.Writef(\"Message number %d<br>\", ints[i])\n\t\t\ttime.Sleep(500 * time.Millisecond) // simulate delay.\n\t\t\tif i == len(ints)-1 {\n\t\t\t\treturn errDone // ends the loop.\n\t\t\t}\n\t\t\ti++\n\t\t\treturn nil // continue write\n\t\t})\n\n\t\tif err != errDone {\n\t\t\t// Test it by canceling the request before the stream ends:\n\t\t\t// [ERRO] $DATETIME stream: context canceled.\n\t\t\tctx.Application().Logger().Errorf(\"stream: %v\", err)\n\t\t}\n\t})\n\n\ttype messageNumber struct {\n\t\tNumber int `json:\"number\"`\n\t}\n\n\tapp.Get(\"/json\", func(ctx iris.Context) {\n\t\tctx.Header(\"Transfer-Encoding\", \"chunked\")\n\t\ti := 0\n\t\tints := []int{1, 2, 3, 5, 7, 9, 11, 13, 15, 17, 23, 29}\n\t\t// Send the response in chunks and wait for half a second between each chunk,\n\t\t// until connection close.\n\t\tnotifyClose := ctx.Request().Context().Done()\n\t\tfor {\n\t\t\tselect {\n\t\t\tcase <-notifyClose:\n\t\t\t\t// err := ctx.Request().Context().Err()\n\t\t\t\tctx.Application().Logger().Infof(\"Connection closed, loop end.\")\n\t\t\t\treturn\n\t\t\tdefault:\n\t\t\t\tctx.JSON(messageNumber{Number: ints[i]})\n\t\t\t\tctx.WriteString(\"\\n\")\n\t\t\t\ttime.Sleep(500 * time.Millisecond) // simulate delay.\n\t\t\t\tif i == len(ints)-1 {\n\t\t\t\t\tctx.Application().Logger().Infof(\"Loop end.\")\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\ti++\n\t\t\t\tctx.ResponseWriter().Flush()\n\t\t\t}\n\t\t}\n\t})\n\n\tapp.Listen(\":8080\")\n}\n\n/*\nLook the following methods too:\n- Context.OnClose(callback)\n- Context.OnConnectionClose(callback) and\n- Context.Request().Context().Done()/.Err() too\n*/\n"
  },
  {
    "path": "_examples/response-writer/write-rest/main.go",
    "content": "package main\n\nimport (\n\t\"encoding/xml\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/x/errors\"\n)\n\n// User example struct for json and msgpack.\ntype User struct {\n\tFirstname string `json:\"firstname\" msgpack:\"firstname\"`\n\tLastname  string `json:\"lastname\" msgpack:\"lastname\"`\n\tCity      string `json:\"city\" msgpack:\"city\"`\n\tAge       int    `json:\"age\" msgpack:\"age\"`\n}\n\n// ExampleXML just a test struct to view represents xml content-type\ntype ExampleXML struct {\n\tXMLName xml.Name `xml:\"example\"`\n\tOne     string   `xml:\"one,attr\"`\n\tTwo     string   `xml:\"two,attr\"`\n}\n\n// ExampleYAML just a test struct to write yaml to the client.\ntype ExampleYAML struct {\n\tName       string `yaml:\"name\"`\n\tServerAddr string `yaml:\"ServerAddr\"`\n}\n\nfunc main() {\n\tapp := iris.New()\n\t// Optionally, set a custom handler for JSON, JSONP, Protobuf, MsgPack, YAML, Markdown...\n\t// write errors.\n\tapp.SetContextErrorHandler(new(errorHandler))\n\t// Read\n\tapp.Post(\"/decode\", func(ctx iris.Context) {\n\t\t// Read https://github.com/kataras/iris/blob/main/_examples/request-body/read-json/main.go as well.\n\t\tvar user User\n\t\terr := ctx.ReadJSON(&user)\n\t\tif err != nil {\n\t\t\terrors.InvalidArgument.Details(ctx, \"unable to parse body\", err.Error())\n\t\t\treturn\n\t\t}\n\n\t\tctx.Writef(\"%s %s is %d years old and comes from %s!\", user.Firstname, user.Lastname, user.Age, user.City)\n\t})\n\n\t// Write\n\tapp.Get(\"/encode\", func(ctx iris.Context) {\n\t\tu := User{\n\t\t\tFirstname: \"John\",\n\t\t\tLastname:  \"Doe\",\n\t\t\tCity:      \"Neither FBI knows!!!\",\n\t\t\tAge:       25,\n\t\t}\n\n\t\t// Manually setting a content type: ctx.ContentType(\"text/javascript\")\n\t\tctx.JSON(u)\n\t})\n\n\t// Use Secure field to prevent json hijacking.\n\t// It prepends `\"while(1),\"` to the body when the data is array.\n\tapp.Get(\"/json_secure\", func(ctx iris.Context) {\n\t\tresponse := []string{\"val1\", \"val2\", \"val3\"}\n\t\toptions := iris.JSON{Indent: \"\", Secure: true}\n\t\tctx.JSON(response, options)\n\n\t\t// Will output: while(1);[\"val1\",\"val2\",\"val3\"]\n\t})\n\n\t// Use ASCII field to generate ASCII-only JSON\n\t// with escaped non-ASCII characters.\n\tapp.Get(\"/json_ascii\", func(ctx iris.Context) {\n\t\tresponse := iris.Map{\"lang\": \"GO-虹膜\", \"tag\": \"<br>\"}\n\t\toptions := iris.JSON{Indent: \"    \", ASCII: true}\n\t\tctx.JSON(response, options)\n\n\t\t/* Will output:\n\t\t   {\n\t\t       \"lang\": \"GO-\\u8679\\u819c\",\n\t\t       \"tag\": \"\\u003cbr\\u003e\"\n\t\t   }\n\t\t*/\n\t})\n\n\t// Do not replace special HTML characters with their unicode entities\n\t// using the UnescapeHTML field.\n\tapp.Get(\"/json_raw\", func(ctx iris.Context) {\n\t\toptions := iris.JSON{UnescapeHTML: true}\n\t\tctx.JSON(iris.Map{\n\t\t\t\"html\": \"<b>Hello, world!</b>\",\n\t\t}, options)\n\n\t\t// Will output: {\"html\":\"<b>Hello, world!</b>\"}\n\t})\n\n\t// Other content types,\n\n\tapp.Get(\"/binary\", func(ctx iris.Context) {\n\t\t// useful when you want force-download of contents of raw bytes form.\n\t\tctx.Binary([]byte(\"Some binary data here.\"))\n\t})\n\n\tapp.Get(\"/text\", func(ctx iris.Context) {\n\t\tctx.Text(\"Plain text here\")\n\t})\n\n\tapp.Get(\"/json\", func(ctx iris.Context) {\n\t\tctx.JSON(map[string]string{\"hello\": \"json\"}) // or myjsonStruct{hello:\"json}\n\t})\n\n\tapp.Get(\"/jsonp\", func(ctx iris.Context) {\n\t\tctx.JSONP(map[string]string{\"hello\": \"jsonp\"}, iris.JSONP{Callback: \"callbackName\"})\n\t})\n\n\tapp.Get(\"/xml\", func(ctx iris.Context) {\n\t\tctx.XML(ExampleXML{One: \"hello\", Two: \"xml\"})\n\t\t// OR:\n\t\t// ctx.XML(iris.XMLMap(\"keys\", iris.Map{\"key\": \"value\"}))\n\t})\n\n\tapp.Get(\"/markdown\", func(ctx iris.Context) {\n\t\tctx.Markdown([]byte(\"# Hello Dynamic Markdown -- iris\"))\n\t})\n\n\tapp.Get(\"/yaml\", func(ctx iris.Context) {\n\t\tctx.YAML(ExampleYAML{Name: \"Iris\", ServerAddr: \"localhost:8080\"})\n\t\t// OR:\n\t\t// ctx.YAML(iris.Map{\"name\": \"Iris\", \"serverAddr\": \"localhost:8080\"})\n\t})\n\n\t// app.Get(\"/protobuf\", func(ctx iris.Context) {\n\t// \tctx.Protobuf(proto.Message)\n\t// })\n\n\tapp.Get(\"/msgpack\", func(ctx iris.Context) {\n\t\tu := User{\n\t\t\tFirstname: \"John\",\n\t\t\tLastname:  \"Doe\",\n\t\t\tCity:      \"Neither FBI knows!!!\",\n\t\t\tAge:       25,\n\t\t}\n\n\t\tctx.MsgPack(u)\n\t})\n\n\t// http://localhost:8080/decode\n\t// http://localhost:8080/encode\n\t// http://localhost:8080/json_secure\n\t// http://localhost:8080/json_ascii\n\t//\n\t// http://localhost:8080/binary\n\t// http://localhost:8080/text\n\t// http://localhost:8080/json\n\t// http://localhost:8080/jsonp\n\t// http://localhost:8080/xml\n\t// http://localhost:8080/markdown\n\t// http://localhost:8080/msgpack\n\t//\n\t// `iris.WithOptimizations` is an optional configurator,\n\t// if passed to the `Run` then it will ensure that the application\n\t// response to the client as fast as possible.\n\t//\n\t//\n\t// `iris.WithoutServerError` is an optional configurator,\n\t// if passed to the `Run` then it will not print its passed error as an actual server error.\n\tapp.Listen(\":8080\", iris.WithOptimizations)\n}\n\ntype errorHandler struct{}\n\nfunc (h *errorHandler) HandleContextError(ctx iris.Context, err error) {\n\terrors.Internal.Err(ctx, err)\n}\n"
  },
  {
    "path": "_examples/routing/basic/.dockerignore",
    "content": ".git\nnode_modules\nbin"
  },
  {
    "path": "_examples/routing/basic/Dockerfile",
    "content": "# docker build -t myapp . \n# docker run --rm -it -p 8080:8080 myapp:latest\nFROM golang:latest AS builder\nRUN apt-get update\nENV GO111MODULE=on \\\n    CGO_ENABLED=0 \\\n    GOOS=linux \\\n    GOARCH=amd64\nWORKDIR /go/src/app\nCOPY go.mod .\nRUN go mod download\nCOPY . .\nRUN go install\n\nFROM scratch\nCOPY --from=builder /go/bin/app .\nENTRYPOINT [\"./app\"]"
  },
  {
    "path": "_examples/routing/basic/README.md",
    "content": "# Basic Example\n\nThe only requirement for this example is [Docker](https://docs.docker.com/install/).\n\n## Docker Compose\n\nThe Docker Compose is pre-installed with Docker for Windows. For linux please follow the steps described at: https://docs.docker.com/compose/install/.\n\nBuild and run the application for linux arch and expose it on http://localhost:8080.\n\n```sh\n$ docker-compose up\n```\n\nSee [docker-compose file](docker-compose.yml).\n\n## Without Docker Compose\n\n1. Build the image as \"myapp\" (docker build)\n2. Run the image and map exposed ports (-p 8080:8080)\n3. Attach the interactive mode so CTRL/CMD+C signals are respected to shutdown the Iris Server (-it)\n4. Cleanup the image on finish (--rm)\n\n```sh\n$ docker build -t myapp . \n$ docker run --rm -it -p 8080:8080 myapp:latest\n```\n"
  },
  {
    "path": "_examples/routing/basic/docker-compose.yml",
    "content": "# docker-compose up [--build]\nversion: '3'\n\nservices:\n  app:\n    build: .\n    ports:\n      - 8080:8080"
  },
  {
    "path": "_examples/routing/basic/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\tapp.Logger().SetLevel(\"debug\")\n\n\t// registers a custom handler for 404 not found http (error) status code,\n\t// fires when route not found or manually by ctx.StatusCode(iris.StatusNotFound).\n\tapp.OnErrorCode(iris.StatusNotFound, notFoundHandler)\n\n\t// GET -> HTTP Method\n\t// / -> Path\n\t// func(ctx iris.Context) -> The route's handler.\n\t//\n\t// Third receiver should contains the route's handler(s), they are executed by order.\n\tapp.Handle(\"GET\", \"/\", func(ctx iris.Context) {\n\t\tctx.HTML(\"Hello from \" + ctx.Path()) // Hello from /\n\t})\n\n\tapp.Get(\"/home\", func(ctx iris.Context) {\n\t\tctx.Writef(`Same as app.Handle(\"GET\", \"/\", [...])`)\n\t})\n\n\t// Different path parameters types in the same path.\n\t// Note that: fallback should registered first e.g. {path} {string},\n\t// because the handler on this case is executing from last to top.\n\tapp.Get(\"/u/{p:path}\", func(ctx iris.Context) {\n\t\tctx.Writef(\":string, :int, :uint, :alphabetical and :path in the same path pattern.\")\n\t})\n\n\tapp.Get(\"/u/{username:string}\", func(ctx iris.Context) {\n\t\tctx.Writef(\"before username (string), current route name: %s\\n\", ctx.RouteName())\n\t\tctx.Next()\n\t}, func(ctx iris.Context) {\n\t\tctx.Writef(\"username (string): %s\", ctx.Params().Get(\"username\"))\n\t})\n\n\tapp.Get(\"/u/{firstname:alphabetical}\", func(ctx iris.Context) {\n\t\tctx.Writef(\"before firstname (alphabetical), current route name: %s\\n\", ctx.RouteName())\n\t\tctx.Next()\n\t}, func(ctx iris.Context) {\n\t\tctx.Writef(\"firstname (alphabetical): %s\", ctx.Params().Get(\"firstname\"))\n\t})\n\n\tapp.Get(\"/u/{id:int}\", func(ctx iris.Context) {\n\t\tctx.Writef(\"before id (int), current route name: %s\\n\", ctx.RouteName())\n\t\tctx.Next()\n\t}, func(ctx iris.Context) {\n\t\tctx.Writef(\"id (int): %d\", ctx.Params().GetIntDefault(\"id\", 0))\n\t})\n\n\tapp.Get(\"/u/{uid:uint}\", func(ctx iris.Context) {\n\t\tctx.Writef(\"before uid (uint), current route name: %s\\n\", ctx.RouteName())\n\t\tctx.Next()\n\t}, func(ctx iris.Context) {\n\t\tctx.Writef(\"uid (uint): %d\", ctx.Params().GetUintDefault(\"uid\", 0))\n\t})\n\n\t/*\n\t\t/u/some/path/here maps to :path\n\t\t/u/abcd maps to :alphabetical (if :alphabetical registered otherwise :string)\n\t\t/u/42 maps to :uint (if :uint registered otherwise :int)\n\t\t/u/-1 maps to :int (if :int registered otherwise :string)\n\t\t/u/abcd123 maps to :string\n\t*/\n\n\t// Pssst, don't forget dynamic-path example for more \"magic\"!\n\tapp.Get(\"/api/users/{userid:uint64 min(1)}\", func(ctx iris.Context) {\n\t\tuserID, err := ctx.Params().GetUint64(\"userid\")\n\t\tif err != nil {\n\t\t\tctx.Writef(\"error while trying to parse userid parameter,\" +\n\t\t\t\t\"this will never happen if :uint64 is being used because if it's not a valid uint64 it will fire Not Found automatically.\")\n\t\t\tctx.StatusCode(iris.StatusBadRequest)\n\t\t\treturn\n\t\t}\n\n\t\tctx.JSON(map[string]any{\n\t\t\t// you can pass any custom structured go value of course.\n\t\t\t\"user_id\": userID,\n\t\t})\n\t})\n\t// app.Post(\"/\", func(ctx iris.Context){}) -> for POST http method.\n\t// app.Put(\"/\", func(ctx iris.Context){})-> for \"PUT\" http method.\n\t// app.Delete(\"/\", func(ctx iris.Context){})-> for \"DELETE\" http method.\n\t// app.Options(\"/\", func(ctx iris.Context){})-> for \"OPTIONS\" http method.\n\t// app.Trace(\"/\", func(ctx iris.Context){})-> for \"TRACE\" http method.\n\t// app.Head(\"/\", func(ctx iris.Context){})-> for \"HEAD\" http method.\n\t// app.Connect(\"/\", func(ctx iris.Context){})-> for \"CONNECT\" http method.\n\t// app.Patch(\"/\", func(ctx iris.Context){})-> for \"PATCH\" http method.\n\t// app.Any(\"/\", func(ctx iris.Context){}) for all http methods.\n\n\t// More than one route can contain the same path with a different http mapped method.\n\t// You can catch any route creation errors with:\n\t// route, err := app.Get(...)\n\t// set a name to a route: route.Name = \"myroute\"\n\n\t// You can also group routes by path prefix, sharing middleware(s) and done handlers.\n\n\tadminRoutes := app.Party(\"/admin\", adminMiddleware)\n\n\tadminRoutes.Done(func(ctx iris.Context) { // executes always last if ctx.Next()\n\t\tctx.Application().Logger().Infof(\"response sent to \" + ctx.Path())\n\t})\n\t// adminRoutes.Layout(\"/views/layouts/admin.html\") // set a view layout for these routes, see more at view examples.\n\n\t// GET: http://localhost:8080/admin\n\tadminRoutes.Get(\"/\", func(ctx iris.Context) {\n\t\t// [...]\n\t\tctx.StatusCode(iris.StatusOK) // default is 200 == iris.StatusOK\n\t\tctx.HTML(\"<h1>Hello from admin/</h1>\")\n\n\t\tctx.Next() // in order to execute the party's \"Done\" Handler(s)\n\t})\n\n\t// GET: http://localhost:8080/admin/login\n\tadminRoutes.Get(\"/login\", func(ctx iris.Context) {\n\t\t// [...]\n\t})\n\t// POST: http://localhost:8080/admin/login\n\tadminRoutes.Post(\"/login\", func(ctx iris.Context) {\n\t\t// [...]\n\t})\n\n\t// subdomains, easier than ever, should add localhost or 127.0.0.1 into your hosts file,\n\t// etc/hosts on unix or C:/windows/system32/drivers/etc/hosts on windows.\n\tv1 := app.Party(\"v1.\")\n\t{ // braces are optional, it's just type of style, to group the routes visually.\n\n\t\t// http://v1.localhost:8080\n\t\t// Note: for versioning-specific features checkout the _examples/routing/versioning instead.\n\t\tv1.Get(\"/\", func(ctx iris.Context) {\n\t\t\tctx.HTML(`Version 1 API. go to <a href=\"/api/users\">/api/users</a>`)\n\t\t})\n\n\t\tusersAPI := v1.Party(\"/api/users\")\n\t\t{\n\t\t\t// http://v1.localhost:8080/api/users\n\t\t\tusersAPI.Get(\"/\", func(ctx iris.Context) {\n\t\t\t\tctx.Writef(\"All users\")\n\t\t\t})\n\t\t\t// http://v1.localhost:8080/api/users/42\n\t\t\tusersAPI.Get(\"/{userid:int}\", func(ctx iris.Context) {\n\t\t\t\tctx.Writef(\"user with id: %d\", ctx.Params().GetIntDefault(\"userid\", 0))\n\t\t\t})\n\t\t}\n\t}\n\n\t// wildcard subdomains.\n\twildcardSubdomain := app.WildcardSubdomain()\n\t{\n\t\twildcardSubdomain.Get(\"/\", func(ctx iris.Context) {\n\t\t\tctx.Writef(\"Subdomain can be anything, now you're here from: %s\", ctx.Subdomain())\n\t\t})\n\t}\n\n\treturn app\n}\n\nfunc main() {\n\tapp := newApp()\n\n\t// http://localhost:8080\n\t// http://localhost:8080/home\n\t// http://localhost:8080/api/users/42\n\t// http://localhost:8080/admin\n\t// http://localhost:8080/admin/login\n\t//\n\t// http://localhost:8080/api/users/0\n\t// http://localhost:8080/api/users/blabla\n\t// http://localhost:8080/wontfound\n\t//\n\t// http://localhost:8080/u/abcd\n\t// http://localhost:8080/u/42\n\t// http://localhost:8080/u/-1\n\t// http://localhost:8080/u/abcd123\n\t// http://localhost:8080/u/some/path/here\n\t//\n\t// if hosts edited:\n\t//  http://v1.localhost:8080\n\t//  http://v1.localhost:8080/api/users\n\t//  http://v1.localhost:8080/api/users/42\n\t//  http://anything.localhost:8080\n\tapp.Listen(\":8080\")\n}\n\nfunc adminMiddleware(ctx iris.Context) {\n\t// [...]\n\tctx.Next() // to move to the next handler, or don't that if you have any auth logic.\n}\n\nfunc notFoundHandler(ctx iris.Context) {\n\tctx.HTML(\"Custom route for 404 not found http code, here you can render a view, html, json <b>any valid response</b>.\")\n}\n\n// Notes:\n// A path parameter name should contain only alphabetical letters, symbols, containing '_' and numbers are NOT allowed.\n// If route failed to be registered, the app will panic without any warnings\n// if you didn't catch the second return value(error) on .Handle/.Get....\n\n// See \"file-server/single-page-application\" to see how another feature, \"WrapRouter\", works.\n"
  },
  {
    "path": "_examples/routing/basic/main_test.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\n// Shows a very basic usage of the httptest.\n// The tests are written in a way to be easy to understand,\n// for a more comprehensive testing examples check out the:\n// _examples/routing/main_test.go,\n// _examples/routing/subdomains/www/main_test.go\n// _examples/file-server and e.t.c.\n// Almost every example which covers\n// a new feature from you to learn\n// contains a test file as well.\nfunc TestRoutingBasic(t *testing.T) {\n\texpectedUResponse := func(paramName, paramType, paramValue string) string {\n\t\ts := fmt.Sprintf(\"before %s (%s), current route name: GET/u/{%s:%s}\\n\", paramName, paramType, paramName, paramType)\n\t\ts += fmt.Sprintf(\"%s (%s): %s\", paramName, paramType, paramValue)\n\t\treturn s\n\t}\n\n\tvar (\n\t\texpectedNotFoundResponse = \"Custom route for 404 not found http code, here you can render a view, html, json <b>any valid response</b>.\"\n\n\t\texpectedIndexResponse = \"Hello from /\"\n\t\texpectedHomeResponse  = `Same as app.Handle(\"GET\", \"/\", [...])`\n\n\t\texpectedUpathResponse         = \":string, :int, :uint, :alphabetical and :path in the same path pattern.\"\n\t\texpectedUStringResponse       = expectedUResponse(\"username\", \"string\", \"abcd123\")\n\t\texpectedUIntResponse          = expectedUResponse(\"id\", \"int\", \"-1\")\n\t\texpectedUUintResponse         = expectedUResponse(\"uid\", \"uint\", \"42\")\n\t\texpectedUAlphabeticalResponse = expectedUResponse(\"firstname\", \"alphabetical\", \"abcd\")\n\n\t\texpectedAPIUsersIndexResponse = map[string]any{\"user_id\": 42}\n\n\t\texpectedAdminIndexResponse = \"<h1>Hello from admin/</h1>\"\n\n\t\texpectedSubdomainV1IndexResponse                  = `Version 1 API. go to <a href=\"/api/users\">/api/users</a>`\n\t\texpectedSubdomainV1APIUsersIndexResponse          = \"All users\"\n\t\texpectedSubdomainV1APIUsersIndexWithParamResponse = \"user with id: 42\"\n\n\t\texpectedSubdomainWildcardIndexResponse = \"Subdomain can be anything, now you're here from: any-subdomain-here\"\n\t)\n\n\tapp := newApp()\n\te := httptest.New(t, app)\n\n\te.GET(\"/anotfound\").Expect().Status(httptest.StatusNotFound).\n\t\tBody().IsEqual(expectedNotFoundResponse)\n\n\te.GET(\"/\").Expect().Status(httptest.StatusOK).\n\t\tBody().IsEqual(expectedIndexResponse)\n\te.GET(\"/home\").Expect().Status(httptest.StatusOK).\n\t\tBody().IsEqual(expectedHomeResponse)\n\n\te.GET(\"/u/some/path/here\").Expect().Status(httptest.StatusOK).\n\t\tBody().IsEqual(expectedUpathResponse)\n\te.GET(\"/u/abcd123\").Expect().Status(httptest.StatusOK).\n\t\tBody().IsEqual(expectedUStringResponse)\n\te.GET(\"/u/-1\").Expect().Status(httptest.StatusOK).\n\t\tBody().IsEqual(expectedUIntResponse)\n\te.GET(\"/u/42\").Expect().Status(httptest.StatusOK).\n\t\tBody().IsEqual(expectedUUintResponse)\n\te.GET(\"/u/abcd\").Expect().Status(httptest.StatusOK).\n\t\tBody().IsEqual(expectedUAlphabeticalResponse)\n\n\te.GET(\"/api/users/42\").Expect().Status(httptest.StatusOK).\n\t\tJSON().IsEqual(expectedAPIUsersIndexResponse)\n\n\te.GET(\"/admin\").Expect().Status(httptest.StatusOK).\n\t\tBody().IsEqual(expectedAdminIndexResponse)\n\n\te.Request(\"GET\", \"/\").WithURL(\"http://v1.example.com\").Expect().Status(httptest.StatusOK).\n\t\tBody().IsEqual(expectedSubdomainV1IndexResponse)\n\n\te.Request(\"GET\", \"/api/users\").WithURL(\"http://v1.example.com\").Expect().Status(httptest.StatusOK).\n\t\tBody().IsEqual(expectedSubdomainV1APIUsersIndexResponse)\n\n\te.Request(\"GET\", \"/api/users/42\").WithURL(\"http://v1.example.com\").Expect().Status(httptest.StatusOK).\n\t\tBody().IsEqual(expectedSubdomainV1APIUsersIndexWithParamResponse)\n\n\te.Request(\"GET\", \"/\").WithURL(\"http://any-subdomain-here.example.com\").Expect().Status(httptest.StatusOK).\n\t\tBody().IsEqual(expectedSubdomainWildcardIndexResponse)\n}\n"
  },
  {
    "path": "_examples/routing/conditional-chain/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\tv1 := app.Party(\"/api/v1\")\n\n\tmyFilter := func(ctx iris.Context) bool {\n\t\t// don't do that on production, use session or/and database calls and etc.\n\t\tok, _ := ctx.URLParamBool(\"admin\")\n\t\treturn ok\n\t}\n\n\tonlyWhenFilter1 := func(ctx iris.Context) {\n\t\tctx.Application().Logger().Infof(\"admin: %#+v\", ctx.URLParams())\n\t\tctx.Writef(\"<title>Admin</title>\\n\")\n\t\tctx.Next()\n\t}\n\n\tonlyWhenFilter2 := func(ctx iris.Context) {\n\t\t// You can always use the per-request storage\n\t\t// to perform actions like this ofc.\n\t\t//\n\t\t// this handler: ctx.Values().Set(\"is_admin\", true)\n\t\t// next handler: isAdmin := ctx.Values().GetBoolDefault(\"is_admin\", false)\n\t\t//\n\t\t// but, let's simplify it:\n\t\tctx.HTML(\"<h1>Hello Admin</h1><br>\")\n\t\tctx.Next()\n\t}\n\n\t// HERE:\n\t// It can be registered anywhere, as a middleware.\n\t// It will fire the `onlyWhenFilter1` and `onlyWhenFilter2` as middlewares (with ctx.Next())\n\t// if myFilter pass otherwise it will just continue the handler chain with ctx.Next() by ignoring\n\t// the `onlyWhenFilter1` and `onlyWhenFilter2`.\n\tmyMiddleware := iris.NewConditionalHandler(myFilter, onlyWhenFilter1, onlyWhenFilter2)\n\n\tv1UsersRouter := v1.Party(\"/users\", myMiddleware)\n\tv1UsersRouter.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.HTML(\"requested: <b>/api/v1/users</b>\")\n\t})\n\n\treturn app\n}\n\nfunc main() {\n\tapp := newApp()\n\n\t// http://localhost:8080/api/v1/users\n\t// http://localhost:8080/api/v1/users?admin=true\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/routing/conditional-chain/main_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nfunc TestNewConditionalHandler(t *testing.T) {\n\tapp := newApp()\n\te := httptest.New(t, app)\n\n\te.GET(\"/api/v1/users\").Expect().Status(httptest.StatusOK).\n\t\tBody().IsEqual(\"requested: <b>/api/v1/users</b>\")\n\te.GET(\"/api/v1/users\").WithQuery(\"admin\", \"true\").Expect().Status(httptest.StatusOK).\n\t\tBody().IsEqual(\"<title>Admin</title>\\n<h1>Hello Admin</h1><br>requested: <b>/api/v1/users</b>\")\n}\n"
  },
  {
    "path": "_examples/routing/custom-context/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"sync\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\t// 1. Create the iris app instance.\n\tapp := iris.New()\n\n\t// 2. Create the Context Wrapper which will be used to wrap the handlers\n\t// that expect a *myCustomContext instead of iris.Context.\n\tw := iris.NewContextWrapper(&myCustomContextPool{})\n\t// OR:\n\t// w := iris.NewContextWrapper(iris.NewContextPool[myCustomContext, *myCustomContext]())\n\t// The example custom context pool operates exactly the same as the result of iris.NewContextPool.\n\n\t// 3. Register the handler(s) which expects a *myCustomContext instead of iris.Context.\n\t// The `w.Handler` will wrap the handler and will call the `Acquire` and `Release`\n\t// methods of the `myCustomContextPool` to get and release the *myCustomContext.\n\tapp.Get(\"/\", w.Handler(index))\n\n\t// 4. Start the server.\n\tapp.Listen(\":8080\")\n}\n\nfunc index(ctx *myCustomContext) {\n\tctx.HTML(\"<h1>Hello, World!</h1>\")\n}\n\n/*\n\tCustom Context Pool\n*/\n// Create the context sync pool for our custom context,\n// the pool must implement Acquire() T and Release(T) methods to satisfy the iris.ContextPool interface.\ntype myCustomContextPool struct {\n\tpool sync.Pool\n}\n\n// Acquire returns a new custom context from the pool.\nfunc (p *myCustomContextPool) Acquire(ctx iris.Context) *myCustomContext {\n\tv := p.pool.Get()\n\tif v == nil {\n\t\tv = &myCustomContext{\n\t\t\tContext: ctx,\n\t\t\t// custom fields here...\n\t\t}\n\t}\n\n\treturn v.(*myCustomContext)\n}\n\n// Release puts a custom context back to the pool.\nfunc (p *myCustomContextPool) Release(t *myCustomContext) {\n\t// You can take advantage of this method to clear the context\n\t// and re-use it on the Acquire method, use the sync.Pool.\n\tp.pool.Put(t)\n}\n\n/*\n\tCustom Context\n*/\n// Create a custom context.\ntype myCustomContext struct {\n\t// It's just an embedded field which is set on AcquireFunc,\n\t// so you can use myCustomContext with the same methods as iris.Context,\n\t// override existing iris.Context's methods or add custom methods.\n\t// You can use the `Context` field to access the original context.\n\tiris.Context\n}\n\n// SetContext sets the original iris.Context,\n// should be implemented by custom context type(s) when\n// the ContextWrapper uses a context Pool through the iris.NewContextPool function.\n// Comment line 15, uncomment line 17 and the method below.\nfunc (c *myCustomContext) SetContext(ctx iris.Context) {\n\tc.Context = ctx\n}\n\nfunc (c *myCustomContext) HTML(format string, args ...any) (int, error) {\n\tc.Application().Logger().Info(\"HTML was called from custom Context\")\n\n\treturn c.Context.HTML(fmt.Sprintf(format, args...))\n}\n"
  },
  {
    "path": "_examples/routing/custom-router/main.go",
    "content": "package main\n\nimport (\n\t\"strings\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/core/router\"\n)\n\n/*\n\t A Router should contain all three of the following methods:\n\t   - HandleRequest should handle the request based on the Context.\n\t\t  HandleRequest(ctx iris.Context)\n\t   - Build should builds the handler, it's being called on router's BuildRouter.\n\t\t  Build(provider router.RoutesProvider) error\n\t   - RouteExists reports whether a particular route exists.\n\t\t  RouteExists(ctx iris.Context, method, path string) bool\n\t   - FireErrorCode(ctx iris.Context) should handle the given ctx.GetStatusCode().\n\nFor a more detailed, complete and useful example\nyou can take a look at the iris' router itself which is located at:\nhttps://github.com/kataras/iris/tree/main/core/router/handler.go\nwhich completes this exact interface, the `router#RequestHandler`.\n*/\ntype customRouter struct {\n\t// a copy of routes (safer because you will not be able to alter a route on serve-time without a `app.RefreshRouter` call):\n\t// []router.Route\n\t// or just expect the whole routes provider:\n\tprovider router.RoutesProvider\n}\n\n// HandleRequest a silly example which finds routes based only on the first part of the requested path\n// which must be a static one as well, the rest goes to fill the parameters.\nfunc (r *customRouter) HandleRequest(ctx iris.Context) {\n\tpath := ctx.Path()\n\tctx.Application().Logger().Infof(\"Requested resource path: %s\", path)\n\n\tparts := strings.Split(path, \"/\")[1:]\n\tstaticPath := \"/\" + parts[0]\n\tfor _, route := range r.provider.GetRoutes() {\n\t\tif strings.HasPrefix(route.Path, staticPath) && route.Method == ctx.Method() {\n\t\t\tparamParts := parts[1:]\n\t\t\tfor _, paramValue := range paramParts {\n\t\t\t\tfor _, p := range route.Tmpl().Params {\n\t\t\t\t\tctx.Params().Set(p.Name, paramValue)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tctx.SetCurrentRoute(route.ReadOnly)\n\t\t\tctx.Do(route.Handlers)\n\t\t\treturn\n\t\t}\n\t}\n\n\t// if nothing found...\n\tctx.StatusCode(iris.StatusNotFound)\n}\n\nfunc (r *customRouter) Build(provider router.RoutesProvider) error {\n\tfor _, route := range provider.GetRoutes() {\n\t\t// do any necessary validation or conversations based on your custom logic here\n\t\t// but always run the \"BuildHandlers\" for each registered route.\n\t\troute.BuildHandlers()\n\t\t// [...] r.routes = append(r.routes, *route)\n\t}\n\n\tr.provider = provider\n\treturn nil\n}\n\nfunc (r *customRouter) RouteExists(ctx iris.Context, method, path string) bool {\n\t// [...]\n\treturn false\n}\n\nfunc (r *customRouter) FireErrorCode(ctx iris.Context) {\n\t// responseStatusCode := ctx.GetStatusCode() // set by prior ctx.StatusCode calls\n\t// [...]\n}\n\nfunc main() {\n\tapp := iris.New()\n\n\t// In case you are wondering, the parameter types and macros like \"{param:string $func()}\" still work inside\n\t// your custom router if you fetch by the Route's Handler\n\t// because they are middlewares under the hood, so you don't have to implement the logic of handling them manually,\n\t// though you have to match what requested path is what route and fill the ctx.Params(), this is the work of your custom router.\n\tapp.Get(\"/hello/{name}\", func(ctx iris.Context) {\n\t\tname := ctx.Params().Get(\"name\")\n\t\tctx.Writef(\"Hello %s\\n\", name)\n\t})\n\n\tapp.Get(\"/cs/{num:uint64 min(10) else 400}\", func(ctx iris.Context) {\n\t\tnum := ctx.Params().GetUint64Default(\"num\", 0)\n\t\tctx.Writef(\"num is: %d\\n\", num)\n\t})\n\n\t// To replace the existing router with a customized one by using the iris/context.Context\n\t// you have to use the `app.BuildRouter` method before `app.Run` and after the routes registered.\n\t// You should pass your custom router's instance as the second input arg, which must completes the `router#RequestHandler`\n\t// interface as shown above.\n\t//\n\t// To see how you can build something even more low-level without direct iris' context support (you can do that manually as well)\n\t// navigate to the \"custom-wrapper\" example instead.\n\tmyCustomRouter := new(customRouter)\n\tapp.BuildRouter(app.ContextPool, myCustomRouter, app.APIBuilder, true)\n\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/routing/custom-wrapper/main.go",
    "content": "package main\n\nimport (\n\t\"net/http\"\n\t\"strings\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\n// In this example you'll just see one use case of .WrapRouter.\n// You can use the .WrapRouter to add custom logic when or when not the router should\n// be executed in order to execute the registered routes' handlers.\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\n\tapp.OnErrorCode(iris.StatusNotFound, func(ctx iris.Context) {\n\t\tctx.HTML(\"<b>Resource Not found</b>\")\n\t})\n\n\tapp.Get(\"/profile/{username}\", func(ctx iris.Context) {\n\t\tctx.Writef(\"Hello %s\", ctx.Params().Get(\"username\"))\n\t})\n\n\tapp.HandleDir(\"/\", iris.Dir(\"./public\"))\n\n\tmyOtherHandler := func(ctx iris.Context) {\n\t\tctx.Writef(\"inside a handler which is fired manually by our custom router wrapper\")\n\t}\n\n\t// wrap the router with a native net/http handler.\n\t// if url does not contain any \".\" (i.e: .css, .js...)\n\t// (depends on the app , you may need to add more file-server exceptions),\n\t// then the handler will execute the router that is responsible for the\n\t// registered routes (look \"/\" and \"/profile/{username}\")\n\t// if not then it will serve the files based on the root \"/\" path.\n\tapp.WrapRouter(func(w http.ResponseWriter, r *http.Request, router http.HandlerFunc) {\n\t\tpath := r.URL.Path\n\n\t\tif strings.HasPrefix(path, \"/other\") {\n\t\t\t// acquire and release a context in order to use it to execute\n\t\t\t// our custom handler\n\t\t\t// remember: we use net/http.Handler because here we are in the \"low-level\", before the router itself.\n\t\t\tctx := app.ContextPool.Acquire(w, r)\n\t\t\tmyOtherHandler(ctx)\n\t\t\tapp.ContextPool.Release(ctx)\n\t\t\treturn\n\t\t}\n\n\t\trouter.ServeHTTP(w, r) // else continue serving routes as usual.\n\t})\n\n\treturn app\n}\n\nfunc main() {\n\tapp := newApp()\n\n\t// http://localhost:8080\n\t// http://localhost:8080/index.html\n\t// http://localhost:8080/app.js\n\t// http://localhost:8080/css/main.css\n\t// http://localhost:8080/profile/anyusername\n\t// http://localhost:8080/other/random\n\tapp.Listen(\":8080\")\n\n\t// Note: In this example we just saw one use case,\n\t// you may want to .WrapRouter or .Downgrade in order to bypass the iris' default router, i.e:\n\t// you can use that method to setup custom proxies too.\n}\n"
  },
  {
    "path": "_examples/routing/custom-wrapper/main_test.go",
    "content": "package main\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\ntype resource string\n\nfunc (r resource) String() string {\n\treturn string(r)\n}\n\nfunc (r resource) strip(strip string) string {\n\ts := r.String()\n\treturn strings.TrimPrefix(s, strip)\n}\n\nfunc (r resource) loadFromBase(dir string) string {\n\tfilename := r.String()\n\n\tif filename == \"/\" {\n\t\tfilename = \"/index.html\"\n\t}\n\n\tfullpath := filepath.Join(dir, filename)\n\n\tb, err := os.ReadFile(fullpath)\n\tif err != nil {\n\t\tpanic(fullpath + \" failed with error: \" + err.Error())\n\t}\n\n\treturn string(b)\n}\n\nvar urls = []resource{\n\t\"/\",\n\t\"/index.html\",\n\t\"/app.js\",\n\t\"/css/main.css\",\n}\n\nfunc TestCustomWrapper(t *testing.T) {\n\tapp := newApp()\n\te := httptest.New(t, app)\n\n\tfor _, u := range urls {\n\t\turl := u.String()\n\t\tcontents := u.loadFromBase(\"./public\")\n\n\t\te.GET(url).Expect().\n\t\t\tStatus(httptest.StatusOK).\n\t\t\tBody().IsEqual(contents)\n\t}\n\n\te.GET(\"/other/something\").Expect().Status(httptest.StatusOK)\n}\n"
  },
  {
    "path": "_examples/routing/custom-wrapper/public/app.js",
    "content": "window.alert(\"app.js loaded from \\\"/\");"
  },
  {
    "path": "_examples/routing/custom-wrapper/public/css/main.css",
    "content": "body {\r\n    background-color: black;\r\n}\r\n"
  },
  {
    "path": "_examples/routing/custom-wrapper/public/index.html",
    "content": "<html>\r\n\r\n<head>\r\n    <title>Index Page</title>\r\n</head>\r\n\r\n<body>\r\n    <h1> Hello from index.html </h1>\r\n\r\n\r\n    <script src=\"/app.js\">  </script>\r\n</body>\r\n\r\n</html>"
  },
  {
    "path": "_examples/routing/dynamic-path/at-username/main.go",
    "content": "package main\n\nimport \"github.com/kataras/iris/v12\"\n\nfunc main() {\n\tapp := iris.New()\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.Writef(\"Hello %s\", \"world\")\n\t})\n\n\t// This is an Iris-only feature across all web frameworks\n\t// in every programming language for years.\n\t// Dynamic Route Path Parameters Functions.\n\t// Set min length characters to 2.\n\t// Prefix of the username is '@'\n\t// Otherwise 404.\n\t//\n\t// You can also use the regexp(...) function for more advanced expressions.\n\tapp.Get(\"/{username:string min(2) prefix(@)}\", func(ctx iris.Context) {\n\t\tusername := ctx.Params().Get(\"username\")[1:]\n\t\tctx.Writef(\"Username is %s\", username)\n\t})\n\n\t// http://localhost:8080          -> FOUND (Hello world)\n\t// http://localhost:8080/other    -> NOT FOUND\n\t// http://localhost:8080/@        -> NOT FOUND\n\t// http://localhost:8080/@kataras -> FOUND (username is kataras)\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/routing/dynamic-path/main.go",
    "content": "package main\n\nimport (\n\t\"regexp\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\t// At the previous example \"routing/basic\",\n\t// we've seen static routes, group of routes, subdomains, wildcard subdomains, a small example of parameterized path\n\t// with a single known paramete and custom http errors, now it's time to see wildcard parameters and macros.\n\n\t// Iris, like net/http std package registers route's handlers\n\t// by a Handler, the iris' type of handler is just a func(ctx iris.Context)\n\t// where context comes from github.com/kataras/iris/context.\n\t//\n\t// Iris has the easiest and the most powerful routing process you have ever meet.\n\t//\n\t// At the same time,\n\t// Iris has its own interpeter(yes like a programming language)\n\t// for route's path syntax and their dynamic path parameters parsing and evaluation,\n\t// We call them \"macros\" for shortcut.\n\t// How? It calculates its needs and if not any special regexp needed then it just\n\t// registers the route with the low-level underline  path syntax,\n\t// otherwise it pre-compiles the regexp and adds the necessary middleware(s).\n\t//\n\t// Standard macro types for parameters:\n\t// +------------------------+\n\t// | {param:string}         |\n\t// +------------------------+\n\t// string type\n\t// anything (single path segmnent)\n\t//\n\t// +-------------------------------+\n\t// | {param:int}                   |\n\t// +-------------------------------+\n\t// int type\n\t// -9223372036854775808 to 9223372036854775807 (x64) or -2147483648 to 2147483647 (x32), depends on the host arch\n\t//\n\t// +------------------------+\n\t// | {param:int8}           |\n\t// +------------------------+\n\t// int8 type\n\t// -128 to 127\n\t//\n\t// +------------------------+\n\t// | {param:int16}          |\n\t// +------------------------+\n\t// int16 type\n\t// -32768 to 32767\n\t//\n\t// +------------------------+\n\t// | {param:int32}          |\n\t// +------------------------+\n\t// int32 type\n\t// -2147483648 to 2147483647\n\t//\n\t// +------------------------+\n\t// | {param:int64}          |\n\t// +------------------------+\n\t// int64 type\n\t// -9223372036854775808 to 9223372036854775807\n\t//\n\t// +------------------------+\n\t// | {param:uint}           |\n\t// +------------------------+\n\t// uint type\n\t// 0 to 18446744073709551615 (x64) or 0 to 4294967295 (x32)\n\t//\n\t// +------------------------+\n\t// | {param:uint8}          |\n\t// +------------------------+\n\t// uint8 type\n\t// 0 to 255\n\t//\n\t// +------------------------+\n\t// | {param:uint16}         |\n\t// +------------------------+\n\t// uint16 type\n\t// 0 to 65535\n\t//\n\t// +------------------------+\n\t// | {param:uint32}          |\n\t// +------------------------+\n\t// uint32 type\n\t// 0 to 4294967295\n\t//\n\t// +------------------------+\n\t// | {param:uint64}         |\n\t// +------------------------+\n\t// uint64 type\n\t// 0 to 18446744073709551615\n\t//\n\t// +---------------------------------+\n\t// | {param:bool} or {param:boolean} |\n\t// +---------------------------------+\n\t// bool type\n\t// only \"1\" or \"t\" or \"T\" or \"TRUE\" or \"true\" or \"True\"\n\t// or \"0\" or \"f\" or \"F\" or \"FALSE\" or \"false\" or \"False\"\n\t//\n\t// +------------------------+\n\t// | {param:alphabetical}   |\n\t// +------------------------+\n\t// alphabetical/letter type\n\t// letters only (upper or lowercase)\n\t//\n\t// +------------------------+\n\t// | {param:file}           |\n\t// +------------------------+\n\t// file type\n\t// letters (upper or lowercase)\n\t// numbers (0-9)\n\t// underscore (_)\n\t// dash (-)\n\t// point (.)\n\t// no spaces ! or other character\n\t//\n\t// +------------------------+\n\t// | {param:path}           |\n\t// +------------------------+\n\t// path type\n\t// anything, should be the last part, can be more than one path segment,\n\t// i.e: \"/test/{param:path}\" and request: \"/test/path1/path2/path3\" , ctx.Params().Get(\"param\") == \"path1/path2/path3\"\n\t//\n\t// +------------------------+\n\t// | {param:uuid}           |\n\t// +------------------------+\n\t// UUIDv4 (and v1) path parameter validation.\n\t//\n\t// +------------------------+\n\t// | {param:mail}           |\n\t// +------------------------+\n\t// Email without domain validation.\n\t//\n\t// +------------------------+\n\t// | {param:email}           |\n\t// +------------------------+\n\t// Email with domain validation.\n\t//\n\t//\n\t// +------------------------+\n\t// | {param:date}           |\n\t// +------------------------+\n\t// yyyy/mm/dd format e.g. /blog/{param:date} matches /blog/2022/04/21.\n\t//\n\t// +------------------------+\n\t// | {param:weekday}        |\n\t// +------------------------+\n\t// positive integer 0 to 6 or\n\t// string of time.Weekday longname format (\"sunday\" to \"monday\" or \"Sunday\" to \"Monday\")\n\t// format e.g. /schedule/{param:weekday} matches /schedule/monday.\n\t//\n\t// If type is missing then parameter's type is defaulted to string, so\n\t// {param} is identical to {param:string}.\n\t//\n\t// If a function not found on that type then the `string` macro type's functions are being used.\n\t//\n\t//\n\t// Besides the fact that iris provides the basic types and some default \"macro funcs\"\n\t// you are able to register your own too!.\n\t//\n\t// Register a named path parameter function:\n\t// app.Macros().Number.RegisterFunc(\"min\", func(argument int) func(paramValue string) bool {\n\t//  [...]\n\t//  return true/false -> true means valid.\n\t// })\n\t//\n\t// at the func(argument ...) you can have any standard type, it will be validated before the server starts\n\t// so don't care about performance here, the only thing it runs at serve time is the returning func(paramValue string) bool.\n\t//\n\t// {param:string equal(iris)} , \"iris\" will be the argument here:\n\t// app.Macros().String.RegisterFunc(\"equal\", func(argument string) func(paramValue string) bool {\n\t// \treturn func(paramValue string) bool { return argument == paramValue }\n\t// })\n\n\t// Optionally, set custom handler on path parameter type error:\n\tapp.Macros().Get(\"uuid\").HandleError(func(ctx iris.Context, paramIndex int, err error) {\n\t\tctx.StatusCode(iris.StatusBadRequest)\n\n\t\tparam := ctx.Params().GetEntryAt(paramIndex)\n\t\tctx.JSON(iris.Map{\n\t\t\t\"error\":     err.Error(),\n\t\t\t\"message\":   \"invalid path parameter\",\n\t\t\t\"parameter\": param.Key,\n\t\t\t\"value\":     param.ValueRaw,\n\t\t})\n\t})\n\n\t// http://localhost:8080/user/bb4f33e4-dc08-40d8-9f2b-e8b2bb615c0e -> OK\n\t// http://localhost:8080/user/dsadsa-invalid-uuid                  -> NOT FOUND\n\tapp.Get(\"/user/{id:uuid}\", func(ctx iris.Context) {\n\t\tid := ctx.Params().Get(\"id\")\n\t\tctx.WriteString(id)\n\t})\n\n\t// +------------------------+\n\t// | {param:email}           |\n\t// +------------------------+\n\t// Email + mx look uppath parameter validation.\n\t// Note that, you can also use the simpler \":mail\" to accept any domain email.\n\n\t// http://localhost:8080/user/email/kataras2006@hotmail.com -> OK\n\t// http://localhost:8080/user/email/b-c@                    -> NOT FOUND\n\tapp.Get(\"/user/email/{user_email:email}\", func(ctx iris.Context) {\n\t\temail := ctx.Params().Get(\"user_email\")\n\t\tctx.WriteString(email)\n\t})\n\n\t// http://localhost:8080/blog/2022/04/21\n\tapp.Get(\"/blog/{date:date}\", func(ctx iris.Context) {\n\t\t// rawTimeValue := ctx.Params().GetEntry(\"d\").ValueRaw.(time.Time)\n\t\t// OR\n\t\trawTimeValue, _ := ctx.Params().GetTime(\"date\")\n\t\t// yearMonthDay := rawTimeValue.Format(\"2006/01/02\")\n\t\t// OR\n\t\tyearMonthDay := ctx.Params().SimpleDate(\"date\")\n\t\tctx.Writef(\"Raw time.Time.String value: %v\\nyyyy/mm/dd: %s\\n\", rawTimeValue, yearMonthDay)\n\t})\n\n\t// 0 to 7 or \"Sunday\" to \"Monday\" or \"sunday\" to \"monday\". Leading zeros don't matter.\n\t// http://localhost:8080/schedule/monday or http://localhost:8080/schedule/Monday or\n\t// http://localhost:8080/schedule/1 or http://localhost:8080/schedule/0001.\n\tapp.Get(\"/schedule/{day:weekday}\", func(ctx iris.Context) {\n\t\tday, _ := ctx.Params().GetWeekday(\"day\")\n\t\tctx.Writef(\"Weekday requested was: %v\\n\", day)\n\t})\n\n\t// you can use the \"string\" type which is valid for a single path parameter that can be anything.\n\tapp.Get(\"/username/{name}\", func(ctx iris.Context) {\n\t\tctx.Writef(\"Hello %s\", ctx.Params().Get(\"name\"))\n\t}) // type is missing = {name:string}\n\n\t// Let's register our first macro attached to uint64 macro type.\n\t// \"min\" = the function\n\t// \"minValue\" = the argument of the function\n\t// func(uint64) bool = our func's evaluator, this executes in serve time when\n\t// a user requests a path which contains the :uint64 macro parameter type with the min(...) macro parameter function.\n\tapp.Macros().Get(\"uint64\").RegisterFunc(\"min\", func(minValue uint64) func(uint64) bool {\n\t\t// type of \"paramValue\" should match the type of the internal macro's evaluator function, which in this case is \"uint64\".\n\t\treturn func(paramValue uint64) bool {\n\t\t\treturn paramValue >= minValue\n\t\t}\n\t})\n\n\t// http://localhost:8080/profile/id>=20\n\t// this will throw 404 even if it's found as route on : /profile/0, /profile/blabla, /profile/-1\n\t// macro parameter functions are optional of course.\n\tapp.Get(\"/profile/{id:uint64 min(20)}\", func(ctx iris.Context) {\n\t\t// second parameter is the error but it will always nil because we use macros,\n\t\t// the validaton already happened.\n\t\tid := ctx.Params().GetUint64Default(\"id\", 0)\n\t\tctx.Writef(\"Hello id: %d\", id)\n\t})\n\n\t// to change the error code per route's macro evaluator:\n\tapp.Get(\"/profile/{id:uint64 min(1)}/friends/{friendid:uint64 min(1) else 504}\", func(ctx iris.Context) {\n\t\tid := ctx.Params().GetUint64Default(\"id\", 0)\n\t\tfriendid := ctx.Params().GetUint64Default(\"friendid\", 0)\n\t\tctx.Writef(\"Hello id: %d looking for friend id: %d\", id, friendid)\n\t}) // this will throw e 504 error code instead of 404 if all route's macros not passed.\n\n\t// :uint8 0 to 255.\n\tapp.Get(\"/ages/{age:uint8 else 400}\", func(ctx iris.Context) {\n\t\tage, _ := ctx.Params().GetUint8(\"age\")\n\t\tctx.Writef(\"age selected: %d\", age)\n\t})\n\n\t// Another example using a custom regexp or any custom logic.\n\n\t// Register your custom argument-less macro function to the :string param type.\n\tlatLonExpr := \"^-?[0-9]{1,3}(?:\\\\.[0-9]{1,10})?$\"\n\tlatLonRegex, err := regexp.Compile(latLonExpr)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// MatchString is a type of func(string) bool, so we use it as it is.\n\tapp.Macros().Get(\"string\").RegisterFunc(\"coordinate\", latLonRegex.MatchString)\n\n\tapp.Get(\"/coordinates/{lat:string coordinate() else 502}/{lon:string coordinate() else 502}\", func(ctx iris.Context) {\n\t\tctx.Writef(\"Lat: %s | Lon: %s\", ctx.Params().Get(\"lat\"), ctx.Params().Get(\"lon\"))\n\t})\n\n\t//\n\n\t// Another one is by using a custom body.\n\tapp.Macros().Get(\"string\").RegisterFunc(\"range\", func(minLength, maxLength int) func(string) bool {\n\t\treturn func(paramValue string) bool {\n\t\t\treturn len(paramValue) >= minLength && len(paramValue) <= maxLength\n\t\t}\n\t})\n\n\tapp.Get(\"/limitchar/{name:string range(1,200)}\", func(ctx iris.Context) {\n\t\tname := ctx.Params().Get(\"name\")\n\t\tctx.Writef(`Hello %s | the name should be between 1 and 200 characters length\n\t\totherwise this handler will not be executed`, name)\n\t})\n\n\t//\n\n\t// Register your custom macro function which accepts a slice of strings `[...,...]`.\n\tapp.Macros().Get(\"string\").RegisterFunc(\"has\", func(validNames []string) func(string) bool {\n\t\treturn func(paramValue string) bool {\n\t\t\tfor _, validName := range validNames {\n\t\t\t\tif validName == paramValue {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn false\n\t\t}\n\t})\n\n\tapp.Get(\"/static_validation/{name:string has([kataras,gerasimos,maropoulos])}\", func(ctx iris.Context) {\n\t\tname := ctx.Params().Get(\"name\")\n\t\tctx.Writef(`Hello %s | the name should be \"kataras\" or \"gerasimos\" or \"maropoulos\"\n\t\totherwise this handler will not be executed`, name)\n\t})\n\n\t//\n\n\t// http://localhost:8080/game/a-zA-Z/level/42\n\t// remember, alphabetical is lowercase or uppercase letters only.\n\tapp.Get(\"/game/{name:alphabetical}/level/{level:int}\", func(ctx iris.Context) {\n\t\tctx.Writef(\"name: %s | level: %s\", ctx.Params().Get(\"name\"), ctx.Params().Get(\"level\"))\n\t})\n\n\tapp.Get(\"/lowercase/static\", func(ctx iris.Context) {\n\t\tctx.Writef(\"static and dynamic paths are not conflicted anymore!\")\n\t})\n\n\t// let's use a trivial custom regexp that validates a single path parameter\n\t// which its value is only lowercase letters.\n\n\t// http://localhost:8080/lowercase/anylowercase\n\tapp.Get(\"/lowercase/{name:string regexp(^[a-z]+)}\", func(ctx iris.Context) {\n\t\tctx.Writef(\"name should be only lowercase, otherwise this handler will never executed: %s\", ctx.Params().Get(\"name\"))\n\t})\n\n\t// http://localhost:8080/single_file/app.js\n\tapp.Get(\"/single_file/{myfile:file}\", func(ctx iris.Context) {\n\t\tctx.Writef(\"file type validates if the parameter value has a form of a file name, got: %s\", ctx.Params().Get(\"myfile\"))\n\t})\n\n\t// http://localhost:8080/myfiles/any/directory/here/\n\t// this is the only macro type that accepts any number of path segments.\n\tapp.Get(\"/myfiles/{directory:path}\", func(ctx iris.Context) {\n\t\tctx.Writef(\"path type accepts any number of path segments, path after /myfiles/ is: %s\", ctx.Params().Get(\"directory\"))\n\t}) // for wildcard path (any number of path segments) without validation you can use:\n\t// /myfiles/*\n\n\t// http://localhost:8080/trimmed/42.html\n\tapp.Get(\"/trimmed/{uid:string regexp(^[0-9]{1,20}.html$)}\", iris.TrimParamFilePart, func(ctx iris.Context) {\n\t\t//\n\t\t// The above line is useless now that we've registered the TrimParamFilePart middleware:\n\t\t// uid := ctx.Params().GetTrimFileUint64(\"uid\")\n\t\t// TrimParamFilePart can be registered to a Party (group of routes) too.\n\n\t\tuid := ctx.Params().GetUint64Default(\"uid\", 0)\n\t\tctx.Writef(\"Param value: %d\\n\", uid)\n\t})\n\n\t// \"{param}\"'s performance is exactly the same of \":param\"'s.\n\n\t// alternatives -> \":param\" for single path parameter and \"*\" for wildcard path parameter.\n\t// Note these:\n\t// if  \"/mypath/*\" then the parameter name is \"*\".\n\t// if  \"/mypath/{myparam:path}\" then the parameter has two names, one is the \"*\" and the other is the user-defined \"myparam\".\n\n\t// WARNING:\n\t// A path parameter name should contain only alphabetical letters or digits. Symbols like  '_' are NOT allowed.\n\t// Last, do not confuse `ctx.Params()` with `ctx.Values()`.\n\t// Path parameter's values can be retrieved from `ctx.Params()`,\n\t// context's local storage that can be used to communicate between handlers and middleware(s) can be stored to `ctx.Values()`.\n\t//\n\t// When registering different parameter types in the same exact path pattern, the path parameter's name\n\t// should differ e.g.\n\t// /path/{name:string}\n\t// /path/{id:uint}\n\t//\n\t// Note:\n\t// If * path part is declared at the end of the route path, then\n\t// it's considered a wildcard (same as {p:path}). In order to declare\n\t// literal * and over pass this limitation use the string's path parameter 'eq' function\n\t// as shown below:\n\t// app.Get(\"/*/*/{p:string eq(*)}\", handler) <- This will match only: /*/*/* and not /*/*/anything.\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/routing/dynamic-path/root-wildcard/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\t// this works as expected now,\n\t// will handle all GET requests\n\t// except:\n\t// /                     -> because of app.Get(\"/\", ...)\n\t// /other/anything/here  -> because of app.Get(\"/other/{paramother:path}\", ...)\n\t// /other2/anything/here -> because of app.Get(\"/other2/{paramothersecond:path}\", ...)\n\t// /other2/static2        -> because of app.Get(\"/other2/static\", ...)\n\t//\n\t// It isn't conflicts with the rest of the routes, without routing performance cost!\n\t//\n\t// i.e /something/here/that/cannot/be/found/by/other/registered/routes/order/not/matters\n\tapp.Get(\"/{p:path}\", h)\n\t// app.Get(\"/static/{p:path}\", staticWildcardH)\n\n\t// this will handle only GET /\n\tapp.Get(\"/\", staticPath)\n\n\t// this will handle all GET requests starting with \"/other/\"\n\t//\n\t// i.e /other/more/than/one/path/parts\n\tapp.Get(\"/other/{paramother:path}\", other)\n\n\t// this will handle all GET requests starting with \"/other2/\"\n\t// except /other2/static (because of the next static route)\n\t//\n\t// i.e /other2/more/than/one/path/parts\n\tapp.Get(\"/other2/{paramothersecond:path}\", other2)\n\n\t// this will handle only GET \"/other2/static\"\n\tapp.Get(\"/other2/static2\", staticPathOther2)\n\n\tapp.Listen(\":8080\")\n}\n\nfunc h(ctx iris.Context) {\n\tparam := ctx.Params().Get(\"p\")\n\tctx.WriteString(param)\n}\n\nfunc staticWildcardH(ctx iris.Context) {\n\tparam := ctx.Params().Get(\"p\")\n\tctx.WriteString(\"from staticWildcardH: param=\" + param)\n}\n\nfunc other(ctx iris.Context) {\n\tparam := ctx.Params().Get(\"paramother\")\n\tctx.Writef(\"from other: %s\", param)\n}\n\nfunc other2(ctx iris.Context) {\n\tparam := ctx.Params().Get(\"paramothersecond\")\n\tctx.Writef(\"from other2: %s\", param)\n}\n\nfunc staticPath(ctx iris.Context) {\n\tctx.Writef(\"from the static path(/): %s\", ctx.Path())\n}\n\nfunc staticPathOther2(ctx iris.Context) {\n\tctx.Writef(\"from the static path(/other2/static2): %s\", ctx.Path())\n}\n"
  },
  {
    "path": "_examples/routing/dynamic-path/same-pattern-different-func/main.go",
    "content": "package main\n\nimport \"github.com/kataras/iris/v12\"\n\nfunc main() {\n\tapp := newApp()\n\tapp.Logger().SetLevel(\"debug\")\n\tapp.Listen(\":8080\")\n}\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\n\tapp.HandleMany(iris.MethodGet, \"/ /api/{page:string suffix(.html)}\", handler1)\n\tapp.Get(\"/api/{name:string suffix(.zip)}\", handler2)\n\n\treturn app\n}\n\nfunc handler1(ctx iris.Context) {\n\treply(ctx)\n}\n\nfunc handler2(ctx iris.Context) {\n\treply(ctx)\n}\n\nfunc reply(ctx iris.Context) {\n\tctx.JSON(iris.Map{\n\t\t\"handler\": ctx.HandlerName(),\n\t\t\"params\":  ctx.Params().Store,\n\t})\n}\n"
  },
  {
    "path": "_examples/routing/dynamic-path/same-pattern-different-func/main_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/core/memstore\"\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nfunc TestSameParameterTypeDifferentMacroFunctions(t *testing.T) {\n\tapp := newApp()\n\te := httptest.New(t, app)\n\n\ttype resp struct {\n\t\tHandler string         `json:\"handler\"`\n\t\tParams  memstore.Store `json:\"params\"`\n\t}\n\n\tvar (\n\t\texpectedIndex = resp{\n\t\t\tHandler: \"iris/_examples/routing/dynamic-path/same-pattern-different-func.handler1\",\n\t\t\tParams:  nil,\n\t\t}\n\t\texpectedHTMLPage = resp{\n\t\t\tHandler: \"iris/_examples/routing/dynamic-path/same-pattern-different-func.handler1\",\n\t\t\tParams: memstore.Store{\n\t\t\t\t{Key: \"page\", ValueRaw: \"random.html\"},\n\t\t\t},\n\t\t}\n\t\texpectedZipName = resp{\n\t\t\tHandler: \"iris/_examples/routing/dynamic-path/same-pattern-different-func.handler2\",\n\t\t\tParams: memstore.Store{\n\t\t\t\t{Key: \"name\", ValueRaw: \"random.zip\"},\n\t\t\t},\n\t\t}\n\t)\n\n\te.GET(\"/\").Expect().Status(httptest.StatusOK).JSON().IsEqual(expectedIndex)\n\te.GET(\"/api/random.html\").Expect().Status(httptest.StatusOK).JSON().IsEqual(expectedHTMLPage)\n\te.GET(\"/api/random.zip\").Expect().Status(httptest.StatusOK).JSON().IsEqual(expectedZipName)\n}\n"
  },
  {
    "path": "_examples/routing/dynamic-path/same-pattern-different-func/use-global/main.go",
    "content": "package main // #1552\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := newApp()\n\tapp.Listen(\":8080\")\n}\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\n\tapp.UseGlobal(middleware(\"first\"))\n\tapp.UseGlobal(middleware(\"second\"))\n\tapp.DoneGlobal(onDone)\n\n\tapp.Get(\"/{name prefix(one)}\", handler(\"first route\"))\n\tapp.Get(\"/{name prefix(two)}\", handler(\"second route\"))\n\tapp.Get(\"/{name prefix(three)}\", handler(\"third route\"))\n\n\treturn app\n}\n\nfunc middleware(str string) iris.Handler {\n\treturn func(ctx iris.Context) {\n\t\tctx.Writef(\"Called %s middleware\\n\", str)\n\t\tctx.Next()\n\t}\n}\n\nfunc handler(str string) iris.Handler {\n\treturn func(ctx iris.Context) {\n\t\tctx.Writef(\"%s\\n\", str)\n\t\tctx.Next() // or ignroe that and use app.SetRegisterRules.\n\t}\n}\n\nfunc onDone(ctx iris.Context) {\n\tctx.Writef(\"Called done: %s\", ctx.Params().Get(\"name\"))\n}\n"
  },
  {
    "path": "_examples/routing/dynamic-path/same-pattern-different-func/use-global/main_test.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nfunc TestSamePatternDifferentFuncUseGlobal(t *testing.T) {\n\tapp := newApp()\n\te := httptest.New(t, app)\n\n\texpectedResultFmt := \"Called first middleware\\nCalled second middleware\\n%s\\nCalled done: %s\"\n\ttests := map[string]string{\n\t\t\"/one-num\":   \"first route\",\n\t\t\"/two-num\":   \"second route\",\n\t\t\"/three-num\": \"third route\",\n\t}\n\n\tfor path, mainBody := range tests {\n\t\tresult := fmt.Sprintf(expectedResultFmt, mainBody, path[1:])\n\t\te.GET(path).Expect().Status(httptest.StatusOK).Body().IsEqual(result)\n\t}\n}\n"
  },
  {
    "path": "_examples/routing/hello-world/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n\n\t\"github.com/kataras/iris/v12/middleware/logger\"\n\t\"github.com/kataras/iris/v12/middleware/recover\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.Logger().SetLevel(\"debug\")\n\t// Optionally, add two builtin handlers\n\t// that can recover from any http-relative panics\n\t// and log the requests to the terminal.\n\tapp.Use(recover.New())\n\tapp.Use(logger.New())\n\n\t// Method:   GET\n\t// Resource: http://localhost:8080\n\tapp.Handle(\"GET\", \"/\", func(ctx iris.Context) {\n\t\tctx.HTML(\"<h1>Welcome</h1>\")\n\t})\n\n\t// same as app.Handle(\"GET\", \"/ping\", [...])\n\t// Method:   GET\n\t// Resource: http://localhost:8080/ping\n\tapp.Get(\"/ping\", func(ctx iris.Context) {\n\t\tctx.WriteString(\"pong\")\n\t})\n\n\t// Method:   GET\n\t// Resource: http://localhost:8080/hello\n\tapp.Get(\"/hello\", func(ctx iris.Context) {\n\t\tctx.JSON(iris.Map{\"message\": \"Hello Iris!\"})\n\t})\n\n\t// http://localhost:8080\n\t// http://localhost:8080/ping\n\t// http://localhost:8080/hello\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/routing/http-errors/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n)\n\n// See _examples/routing/http-wire-errors as well.\nfunc main() {\n\tapp := iris.New()\n\n\t// Catch a specific error code.\n\tapp.OnErrorCode(iris.StatusInternalServerError, func(ctx iris.Context) {\n\t\tctx.HTML(\"Message: <b>\" + ctx.Values().GetString(\"message\") + \"</b>\")\n\t})\n\n\t// Catch all error codes [app.OnAnyErrorCode...]\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.HTML(`Click <a href=\"/my500\">here</a> to pretend an HTTP error`)\n\t})\n\n\tapp.Get(\"/my500\", func(ctx iris.Context) {\n\t\tctx.Values().Set(\"message\", \"this is the error message\")\n\t\tctx.StatusCode(500)\n\t})\n\n\tapp.Get(\"/u/{firstname:alphabetical}\", func(ctx iris.Context) {\n\t\tctx.Writef(\"Hello %s\", ctx.Params().Get(\"firstname\"))\n\t})\n\n\t// Read more at: https://github.com/kataras/iris/issues/1335\n\tapp.Get(\"/product-problem\", problemExample)\n\n\tapp.Get(\"/product-error\", func(ctx iris.Context) {\n\t\tctx.Writef(\"explain the error\")\n\t})\n\n\t// http://localhost:8080\n\t// http://localhost:8080/my500\n\t// http://localhost:8080/u/gerasimos\n\t// http://localhost:8080/product-problem\n\tapp.Listen(\":8080\")\n}\n\nfunc newProductProblem(productName, detail string) iris.Problem {\n\treturn iris.NewProblem().\n\t\t// The type URI, if relative it automatically convert to absolute.\n\t\tType(\"/product-error\").\n\t\t// The title, if empty then it gets it from the status code.\n\t\tTitle(\"Product validation problem\").\n\t\t// Any optional details.\n\t\tDetail(detail).\n\t\t// The status error code, required.\n\t\tStatus(iris.StatusBadRequest).\n\t\t// Any custom key-value pair.\n\t\tKey(\"productName\", productName)\n\t// Optional cause of the problem, chain of Problems.\n\t// Cause(iris.NewProblem().Type(\"/error\").Title(\"cause of the problem\").Status(400))\n}\n\nfunc problemExample(ctx iris.Context) {\n\t/*\n\t\tp := iris.NewProblem().\n\t\t\tType(\"/validation-error\").\n\t\t\tTitle(\"Your request parameters didn't validate\").\n\t\t\tDetail(\"Optional details about the error.\").\n\t\t\tStatus(iris.StatusBadRequest).\n\t\t \tKey(\"customField1\", customValue1)\n\t\t \tKey(\"customField2\", customValue2)\n\t\tctx.Problem(p)\n\n\t\t// OR\n\t\tctx.Problem(iris.Problem{\n\t\t\t\"type\":   \"/validation-error\",\n\t\t\t\"title\":  \"Your request parameters didn't validate\",\n\t\t\t\"detail\": \"Optional details about the error.\",\n\t\t\t\"status\": iris.StatusBadRequest,\n\t\t \t\"customField1\": customValue1,\n\t\t \t\"customField2\": customValue2,\n\t\t})\n\n\t\t// OR\n\t*/\n\n\t// Response like JSON but with indent of \"  \" and\n\t// content type of \"application/problem+json\"\n\tctx.Problem(newProductProblem(\"product name\", \"problem error details\"), iris.ProblemOptions{\n\t\t// Optional JSON renderer settings.\n\t\tJSON: iris.JSON{\n\t\t\tIndent: \"  \",\n\t\t},\n\t\t// OR\n\t\t// Render as XML:\n\t\t//\n\t\t// RenderXML: true,\n\t\t// XML:       iris.XML{Indent: \"  \"},\n\t\t// and ctx.StatusCode(200) to see the result on browser as a user.\n\t\t//\n\t\t// The below `RetryAfter` field sets the \"Retry-After\" response header.\n\t\t//\n\t\t// Can accept:\n\t\t// time.Time for HTTP-Date,\n\t\t// time.Duration, int64, float64, int for seconds\n\t\t// or string for date or duration.\n\t\t// Examples:\n\t\t// time.Now().Add(5 * time.Minute),\n\t\t// 300 * time.Second,\n\t\t// \"5m\",\n\t\t//\n\t\tRetryAfter: 300,\n\t\t// A function that, if specified, can dynamically set\n\t\t// retry-after based on the request. Useful for ProblemOptions reusability.\n\t\t// Overrides the RetryAfter field.\n\t\t//\n\t\t// RetryAfterFunc: func(iris.Context) any { [...] }\n\t})\n}\n"
  },
  {
    "path": "_examples/routing/http-errors/reset-body/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := newApp()\n\tapp.Listen(\":8080\")\n}\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\tapp.Use(iris.Compression)\n\n\tapp.OnAnyErrorCode(onErrorCode)\n\tapp.Get(\"/\", handler)\n\n\tapp.Configure(iris.WithResetOnFireErrorCode)\n\treturn app\n}\n\n// This is the default error handler Iris uses for any error codes.\nfunc onErrorCode(ctx iris.Context) {\n\tif err := ctx.GetErr(); err != nil {\n\t\tctx.WriteString(err.Error())\n\t} else {\n\t\tctx.WriteString(iris.StatusText(ctx.GetStatusCode()))\n\t}\n}\n\nfunc handler(ctx iris.Context) {\n\tctx.Record()\n\n\tctx.WriteString(\"This should NOT be written\")\n\n\t// [....something bad happened after we \"write\"]\n\terr := fmt.Errorf(\"custom error\")\n\tctx.StopWithError(iris.StatusBadRequest, err)\n}\n"
  },
  {
    "path": "_examples/routing/http-errors/reset-body/main_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nfunc TestResetCompressionAndFireError(t *testing.T) { // #1569\n\tapp := newApp()\n\n\te := httptest.New(t, app)\n\te.GET(\"/\").Expect().Status(httptest.StatusBadRequest).Body().IsEqual(\"custom error\")\n}\n"
  },
  {
    "path": "_examples/routing/http-wire-errors/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n\t// IMPORTANT, import this sub-package.\n\t// Note tht it does NOT break compatibility with the\n\t// standard \"errors\" package as the New,\n\t// Is, As, Unwrap functions are aliases to the standard package.\n\t\"github.com/kataras/iris/v12/x/errors\"\n)\n\n// Optionally, register custom error codes.\n//\n// The default list of error code names:\n// errors.Cancelled\n// errors.Unknown\n// errors.InvalidArgument\n// errors.DeadlineExceeded\n// errors.NotFound\n// errors.AlreadyExists\n// errors.PermissionDenied\n// errors.Unauthenticated\n// errors.ResourceExhausted\n// errors.FailedPrecondition\n// errors.Aborted\n// errors.OutOfRange\n// errors.Unimplemented\n// errors.Internal\n// errors.Unavailable\n// errors.DataLoss\nvar (\n\tCustom = errors.Register(\"CUSTOM_CANONICAL_ERROR_NAME\", iris.StatusBadRequest)\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\t// Custom error code name.\n\tapp.Get(\"/custom\", fireCustomErrorCodeName)\n\n\t// Send a simple 400 request with message and an error\n\t// or with more details and data.\n\tapp.Post(\"/invalid_argument\", fireInvalidArgument)\n\n\t// Compatibility with the iris.Problem type (and any other custom type).\n\tapp.Get(\"/problem\", fireErrorWithProblem)\n\n\tapp.Listen(\":8080\")\n}\n\nfunc fireCustomErrorCodeName(ctx iris.Context) {\n\tCustom.Details(ctx, \"message\", fmt.Sprintf(\"details with arguments: %s\", \"an argument\"))\n}\n\nfunc fireInvalidArgument(ctx iris.Context) {\n\tvar req = struct {\n\t\tUsername string `json:\"username\"`\n\t}{}\n\tif err := ctx.ReadJSON(&req); err != nil {\n\t\terrors.InvalidArgument.Err(ctx, err)\n\t\treturn\n\t}\n\n\tctx.WriteString(req.Username)\n\n\t// Other examples: errors.InvalidArgument/NotFound/Internal and e.t.c.\n\t// .Message(ctx, \"message %s\", \"optional argument\")\n\t// .Details(ctx, \"message\", \"details %s\", \"optional details argument\")\n\t// .Data(ctx, \"message\", anyTypeOfValue)\n\t// .DataWithDetails(ctx, \"unable to read the body\", \"malformed json\", iris.Map{\"custom\": \"data of any type\"})\n\t// .Log(ctx, \"message %s\", \"optional argument\")\n\t// .LogErr(ctx, err)\n}\n\nfunc fireErrorWithProblem(ctx iris.Context) {\n\tmyCondition := true\n\tif myCondition {\n\t\tproblem := iris.NewProblem().\n\t\t\t// The type URI, if relative it automatically convert to absolute.\n\t\t\tType(\"/product-error\").\n\t\t\t// The title, if empty then it gets it from the status code.\n\t\t\tTitle(\"Product validation problem\").\n\t\t\t// Any optional details.\n\t\t\tDetail(\"details about the product error\").\n\t\t\t// The status error code of the problem, can be optional here.\n\t\t\t// Status(iris.StatusBadRequest).\n\t\t\t// Any custom key-value pair.\n\t\t\tKey(\"product_name\", \"the product name\")\n\n\t\terrors.InvalidArgument.Data(ctx, \"unable to process the request\", problem)\n\t\treturn\n\n\t\t/* Prints to the client:\n\t\t{\n\t\t  \"http_error_code\": {\n\t\t    \"canonical_name\": \"INVALID_ARGUMENT\",\n\t\t    \"status\": 400\n\t\t  },\n\t\t  \"message\": \"unable to process the request\",\n\t\t  \"data\": {\n\t\t    \"detail\": \"details about the product error\",\n\t\t    \"product_name\": \"the product name\",\n\t\t    \"title\": \"Product validation problem\",\n\t\t    \"type\": \"/product-error\"\n\t\t  }\n\t\t}\n\t\t*/\n\t}\n\n}\n"
  },
  {
    "path": "_examples/routing/http-wire-errors/service/main.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/x/errors\"\n\t\"github.com/kataras/iris/v12/x/errors/validation\"\n\t\"github.com/kataras/iris/v12/x/pagination\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\t// Create a new service and pass it to the handlers.\n\tservice := new(myService)\n\n\tapp.Post(\"/\", errors.Intercept(afterServiceCallButBeforeDataSent), createHandler(service)) // OR: errors.CreateHandler(service.Create)\n\tapp.Get(\"/\", listAllHandler(service))                                                      // OR errors.Handler(service.ListAll, errors.Value(ListRequest{}))\n\tapp.Post(\"/page\", listHandler(service))                                                    // OR: errors.ListHandler(service.ListPaginated)\n\tapp.Delete(\"/{id:string}\", deleteHandler(service))                                         // OR: errors.NoContentOrNotModifiedHandler(service.DeleteWithFeedback, errors.PathParam[string](\"id\"))\n\n\tapp.Listen(\":8080\")\n}\n\nfunc createHandler(service *myService) iris.Handler {\n\treturn func(ctx iris.Context) {\n\t\t// What it does?\n\t\t// 1. Reads the request body and binds it to the CreateRequest struct.\n\t\t// 2. Calls the service.Create function with the given request body.\n\t\t// 3. If the service.Create returns an error, it sends an appropriate error response to the client.\n\t\t// 4. If the service.Create returns a response, it sets the status code to 201 (Created) and sends the response as a JSON payload to the client.\n\t\t//\n\t\t// Useful for create operations.\n\t\terrors.Create(ctx, service.Create)\n\t}\n}\n\nfunc listAllHandler(service *myService) iris.Handler {\n\treturn func(ctx iris.Context) {\n\t\t// What it does?\n\t\t// 1. If the 3rd variadic (optional) parameter is empty (not our case here), it reads the request body and binds it to the ListRequest struct,\n\t\t// otherwise (our case) it calls the service.ListAll function directly with the given input parameter (empty ListRequest struct value in our case).\n\t\t// 2. Calls the service.ListAll function with the ListRequest value.\n\t\t// 3. If the service.ListAll returns an error, it sends an appropriate error response to the client.\n\t\t// 4. If the service.ListAll returns a response, it sets the status code to 200 (OK) and sends the response as a JSON payload to the client.\n\t\t//\n\t\t// Useful for get single, fetch multiple and search operations.\n\t\terrors.OK(ctx, service.ListAll, ListRequest{})\n\t}\n}\n\nfunc listHandler(service *myService) iris.Handler {\n\treturn func(ctx iris.Context) {\n\t\terrors.List(ctx, service.ListPaginated)\n\t}\n}\n\nfunc deleteHandler(service *myService) iris.Handler {\n\treturn func(ctx iris.Context) {\n\t\tid := ctx.Params().Get(\"id\")\n\t\t// What it does?\n\t\t// 1. Calls the service.DeleteWithFeedback function with the given input parameter.\n\t\t// 2. If the service.DeleteWithFeedback returns an error, it sends an appropriate error response to the client.\n\t\t// 3.If the service.DeleteWithFeedback doesn't return an error then it sets the status code to 204 (No Content) and\n\t\t// sends the response as a JSON payload to the client.\n\t\t// errors.NoContent(ctx, service.Delete, id)\n\t\t// OR:\n\t\t// 1. Calls the service.DeleteWithFeedback function with the given input parameter.\n\t\t// 2. If the service.DeleteWithFeedback returns an error, it sends an appropriate error response to the client.\n\t\t// 3. If the service.DeleteWithFeedback returns true, it sets the status code to 204 (No Content).\n\t\t// 4. If the service.DeleteWithFeedback returns false, it sets the status code to 304 (Not Modified).\n\t\t//\n\t\t// Useful for update and delete operations.\n\t\terrors.NoContentOrNotModified(ctx, service.DeleteWithFeedback, id)\n\t}\n}\n\ntype (\n\tmyService struct{}\n\n\tCreateRequest struct {\n\t\tFullname string   `json:\"fullname\"`\n\t\tAge      int      `json:\"age\"`\n\t\tHobbies  []string `json:\"hobbies\"`\n\t}\n\n\tCreateResponse struct {\n\t\tID        string   `json:\"id\"`\n\t\tFirstname string   `json:\"firstname\"`\n\t\tLastname  string   `json:\"lastname\"`\n\t\tAge       int      `json:\"age\"`\n\t\tHobbies   []string `json:\"hobbies\"`\n\t}\n)\n\n// HandleRequest implements the errors.RequestHandler interface.\n// It validates the request body and returns an error if the request body is invalid.\n// You can also alter the \"r\" CreateRequest before calling the service method,\n// e.g. give a default value to a field if it's empty or set an ID based on a path parameter.\n// OR\n// Custom function per route:\n//\n//\tr.Post(\"/\", errors.Validation(validateCreateRequest), createHandler(service))\n//\t[more code here...]\n//\n//\tfunc validateCreateRequest(ctx iris.Context, r *CreateRequest) error {\n//\t\treturn validation.Join(\n//\t\t\tvalidation.String(\"fullname\", r.Fullname).NotEmpty().Fullname().Length(3, 50),\n//\t\t\tvalidation.Number(\"age\", r.Age).InRange(18, 130),\n//\t\t\tvalidation.Slice(\"hobbies\", r.Hobbies).Length(1, 10),\n//\t\t)\n//\t}\nfunc (r *CreateRequest) HandleRequest(ctx iris.Context) error {\n\t// To pass custom validation functions:\n\t// return validation.Join(\n\t// \tvalidation.String(\"fullname\", r.Fullname).Func(customStringFuncHere),\n\t//   OR\n\t// \tvalidation.Field(\"any_field\", r.AnyFieldValue).Func(customAnyFuncHere))\n\treturn validation.Join(\n\t\tvalidation.String(\"fullname\", r.Fullname).Fullname().Length(3, 50),\n\t\tvalidation.Number(\"age\", r.Age).InRange(18, 130),\n\t\tvalidation.Slice(\"hobbies\", r.Hobbies).Length(1, 10),\n\t)\n\n\t/* Example Output:\n\t{\n\t    \"http_error_code\": {\n\t        \"canonical_name\": \"INVALID_ARGUMENT\",\n\t        \"status\": 400\n\t    },\n\t    \"message\": \"validation failure\",\n\t    \"details\": \"fields were invalid\",\n\t    \"validation\": [\n\t        {\n\t            \"field\": \"fullname\",\n\t            \"value\": \"\",\n\t            \"reason\": \"must not be empty, must contain first and last name, must be between 3 and 50 characters\"\n\t        },\n\t        {\n\t            \"field\": \"age\",\n\t            \"value\": 0,\n\t            \"reason\": \"must be in range of [18, 130]\"\n\t        },\n\t        {\n\t            \"field\": \"hobbies\",\n\t            \"value\": null,\n\t            \"reason\": \"must be between 1 and 10 elements\"\n\t        }\n\t    ]\n\t}\n\t*/\n}\n\n/*\n// HandleResponse implements the errors.ResponseHandler interface.\nfunc (r *CreateRequest) HandleResponse(ctx iris.Context, resp *CreateResponse) error {\n\tfmt.Printf(\"request got: %+v\\nresponse sent: %#+v\\n\", r, resp)\n\n\treturn nil // fmt.Errorf(\"let's fire an internal server error just for the shake of the example\") // return nil to continue.\n}\n*/\n\nfunc afterServiceCallButBeforeDataSent(ctx iris.Context, req CreateRequest, resp *CreateResponse) error {\n\tfmt.Printf(\"intercept: request got: %+v\\nresponse sent: %#+v\\n\", req, resp)\n\treturn nil\n}\n\nfunc (s *myService) Create(ctx context.Context, in CreateRequest) (CreateResponse, error) {\n\tarr := strings.Split(in.Fullname, \" \")\n\tfirstname, lastname := arr[0], arr[1]\n\tid := \"test_id\"\n\n\tresp := CreateResponse{\n\t\tID:        id,\n\t\tFirstname: firstname,\n\t\tLastname:  lastname,\n\t\tAge:       in.Age,\n\t\tHobbies:   in.Hobbies,\n\t}\n\treturn resp, nil // , errors.New(\"create: test error\")\n}\n\ntype ListRequest struct {\n}\n\nfunc (s *myService) ListAll(ctx context.Context, in ListRequest) ([]CreateResponse, error) {\n\tresp := []CreateResponse{\n\t\t{\n\t\t\tID:        \"test-id-1\",\n\t\t\tFirstname: \"test first name 1\",\n\t\t\tLastname:  \"test last name 1\",\n\t\t},\n\t\t{\n\t\t\tID:        \"test-id-2\",\n\t\t\tFirstname: \"test first name 2\",\n\t\t\tLastname:  \"test last name 2\",\n\t\t},\n\t\t{\n\t\t\tID:        \"test-id-3\",\n\t\t\tFirstname: \"test first name 3\",\n\t\t\tLastname:  \"test last name 3\",\n\t\t},\n\t}\n\n\treturn resp, nil //, errors.New(\"list all: test error\")\n}\n\ntype ListFilter struct {\n\tFirstname string `json:\"firstname\"`\n}\n\nfunc (s *myService) ListPaginated(ctx context.Context, opts pagination.ListOptions, filter ListFilter) ([]CreateResponse, int /* any number type */, error) {\n\tall, err := s.ListAll(ctx, ListRequest{})\n\tif err != nil {\n\t\treturn nil, 0, err\n\t}\n\n\tfilteredResp := make([]CreateResponse, 0)\n\tfor _, v := range all {\n\t\tif strings.Contains(v.Firstname, filter.Firstname) {\n\t\t\tfilteredResp = append(filteredResp, v)\n\t\t}\n\n\t\tif len(filteredResp) == opts.GetLimit() {\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn filteredResp, len(all), nil // errors.New(\"list paginated: test error\")\n}\n\nfunc (s *myService) GetByID(ctx context.Context, id string) (CreateResponse, error) {\n\treturn CreateResponse{Firstname: \"Gerasimos\"}, nil // errors.New(\"get by id: test error\")\n}\n\nfunc (s *myService) Delete(ctx context.Context, id string) error {\n\treturn nil // errors.New(\"delete: test error\")\n}\n\nfunc (s *myService) Update(ctx context.Context, req CreateRequest) (bool, error) {\n\treturn true, nil // false, errors.New(\"update: test error\")\n}\n\nfunc (s *myService) DeleteWithFeedback(ctx context.Context, id string) (bool, error) {\n\treturn true, nil // false, errors.New(\"delete: test error\")\n}\n"
  },
  {
    "path": "_examples/routing/intelligence/main.go",
    "content": "package main\n\nimport \"github.com/kataras/iris/v12\"\n\nfunc main() {\n\tapp := iris.New()\n\tapp.Get(\"/home\", handler)\n\tapp.Get(\"/contact\", handler)\n\tapp.Get(\"/contract\", handler)\n\n\t// http://localhost:8080/home\n\t// http://localhost:8080/hom\n\t//\n\t// http://localhost:8080/contact\n\t// http://localhost:8080/cont\n\t//\n\t// http://localhost:8080/contract\n\t// http://localhost:8080/contr\n\tapp.Listen(\":8080\", iris.WithPathIntelligence)\n}\n\nfunc handler(ctx iris.Context) {\n\tctx.Writef(\"Path: %s\", ctx.Path())\n}\n"
  },
  {
    "path": "_examples/routing/intelligence/manual/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.OnErrorCode(iris.StatusNotFound, notFound)\n\n\t// [register some routes...]\n\tapp.Get(\"/home\", handler)\n\tapp.Get(\"/news\", handler)\n\tapp.Get(\"/news/politics\", handler)\n\tapp.Get(\"/user/profile\", handler)\n\tapp.Get(\"/user\", handler)\n\tapp.Get(\"/newspaper\", handler)\n\tapp.Get(\"/user/{id}\", handler)\n\n\tapp.Listen(\":8080\")\n}\n\nfunc notFound(ctx iris.Context) {\n\tsuggestPaths := ctx.FindClosest(3)\n\tif len(suggestPaths) == 0 {\n\t\tctx.WriteString(\"404 not found\")\n\t\treturn\n\t}\n\n\tctx.HTML(\"Did you mean?<ul>\")\n\tfor _, s := range suggestPaths {\n\t\tctx.HTML(fmt.Sprintf(`<li><a href=\"%s\">%s</a></li>`, s, s))\n\t}\n\tctx.HTML(\"</ul>\")\n}\n\nfunc handler(ctx iris.Context) {\n\tctx.Writef(\"Path: %s\", ctx.Path())\n}\n"
  },
  {
    "path": "_examples/routing/macros/main.go",
    "content": "// Package main shows how you can register a custom parameter type and macro functions that belongs to it.\n// See _examples/routing/dynamic-path/main.go first.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/hero\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.Logger().SetLevel(\"debug\")\n\n\tapp.Macros().Register(\"slice\", \"\", []string{}, false, true, func(paramValue string) (any, bool) {\n\t\treturn strings.Split(paramValue, \"/\"), true\n\t}).RegisterFunc(\"contains\", func(expectedItems []string) func(paramValue []string) bool {\n\t\tsort.Strings(expectedItems)\n\t\treturn func(paramValue []string) bool {\n\t\t\tif len(paramValue) != len(expectedItems) {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\tsort.Strings(paramValue)\n\t\t\tfor i := 0; i < len(paramValue); i++ {\n\t\t\t\tif paramValue[i] != expectedItems[i] {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn true\n\t\t}\n\t})\n\n\t// In order to use your new param type inside MVC controller's function input argument or a hero function input argument\n\t// you have to tell the Iris what type it is, the `ValueRaw` of the parameter is the same type\n\t// as you defined it above with the func(paramValue string) (any, bool).\n\t// The new value and its type(from string to your new custom type) it is stored only once now,\n\t// you don't have to do any conversions for simple cases like this.\n\tcontext.ParamResolvers[reflect.TypeOf([]string{})] = func(paramIndex int) any {\n\t\treturn func(ctx iris.Context) []string {\n\t\t\t// When you want to retrieve a parameter with a value type that it is not supported by-default, such as ctx.Params().GetInt\n\t\t\t// then you can use the `GetEntry` or `GetEntryAt` and cast its underline `ValueRaw` to the desired type.\n\t\t\t// The type should be the same as the macro's evaluator function (last argument on the Macros#Register) return value.\n\t\t\treturn ctx.Params().GetEntryAt(paramIndex).ValueRaw.([]string)\n\t\t}\n\t}\n\n\t/*\n\t\thttp://localhost:8080/test_slice_hero/myvaluei1/myavlue2 ->\n\t\tmyparam's value (a trailing path parameter type) is: []string{\"myvalue1\", \"myavlue2\"}\n\t*/\n\tapp.Get(\"/test_slice_hero/{myparam:slice}\", hero.Handler(func(myparam []string) string {\n\t\treturn fmt.Sprintf(\"myparam's value (a trailing path parameter type) is: %#v\\n\", myparam)\n\t}))\n\n\t/*\n\t\thttp://localhost:8080/test_slice_contains/notcontains1/value2 ->\n\t\t(404) Not Found\n\n\t\thttp://localhost:8080/test_slice_contains/value1/value2 ->\n\t\tmyparam's value (a trailing path parameter type) is: []string{\"value1\", \"value2\"}\n\t*/\n\tapp.Get(\"/test_slice_contains/{myparam:slice contains([value1,value2])}\", func(ctx iris.Context) {\n\t\t// When it is not a builtin function available to retrieve your value with the type you want, such as ctx.Params().GetInt\n\t\t// then you can use the `GetEntry.ValueRaw` to get the real value, which is set-ed by your macro above.\n\t\tmyparam := ctx.Params().GetEntry(\"myparam\").ValueRaw.([]string)\n\t\tctx.Writef(\"myparam's value (a trailing path parameter type) is: %#v\\n\", myparam)\n\t})\n\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/routing/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n)\n\n/*\nRead:\n\"overview\"\n\"basic\"\n\"dynamic-path\"\nand \"reverse\" examples if you want to release iris' real power.\n*/\n\nconst maxBodySize = 1 << 20\nconst notFoundHTML = \"<h1> custom http error page </h1>\"\n\nfunc registerErrors(app *iris.Application) {\n\t// set a custom 404 handler\n\tapp.OnErrorCode(iris.StatusNotFound, func(ctx iris.Context) {\n\t\tctx.HTML(notFoundHTML)\n\t})\n}\n\nfunc registerGamesRoutes(app *iris.Application) {\n\tgamesMiddleware := func(ctx iris.Context) {\n\t\tctx.Next()\n\t}\n\n\t// party is just a group of routes with the same prefix\n\t// and middleware, i.e: \"/games\" and gamesMiddleware.\n\tgames := app.Party(\"/games\", gamesMiddleware)\n\t{ // braces are optional of course, it's just a style of code\n\n\t\t// \"GET\" method\n\t\tgames.Get(\"/{gameID:uint64}/clans\", h)\n\t\tgames.Get(\"/{gameID:uint64}/clans/clan/{clanPublicID:uint64}\", h)\n\t\tgames.Get(\"/{gameID:uint64}/clans/search\", h)\n\n\t\t// \"PUT\" method\n\t\tgames.Put(\"/{gameID:uint64}/players/{clanPublicID:uint64}\", h)\n\t\tgames.Put(\"/{gameID:uint64}/clans/clan/{clanPublicID:uint64}\", h)\n\t\t// remember: \"clanPublicID\" should not be changed to other routes with the same prefix.\n\t\t// \"POST\" method\n\t\tgames.Post(\"/{gameID:uint64}/clans\", h)\n\t\tgames.Post(\"/{gameID:uint64}/players\", h)\n\t\tgames.Post(\"/{gameID:uint64}/clans/{clanPublicID:uint64}/leave\", h)\n\t\tgames.Post(\"/{gameID:uint64}/clans/{clanPublicID:uint64}/memberships/application\", h)\n\t\tgames.Post(\"/{gameID:uint64}/clans/{clanPublicID:uint64}/memberships/application/{action}\", h) // {action} == {action:string}\n\t\tgames.Post(\"/{gameID:uint64}/clans/{clanPublicID:uint64}/memberships/invitation\", h)\n\t\tgames.Post(\"/{gameID:uint64}/clans/{clanPublicID:uint64}/memberships/invitation/{action}\", h)\n\t\tgames.Post(\"/{gameID:uint64}/clans/{clanPublicID:uint64}/memberships/delete\", h)\n\t\tgames.Post(\"/{gameID:uint64}/clans/{clanPublicID:uint64}/memberships/promote\", h)\n\t\tgames.Post(\"/{gameID:uint64}/clans/{clanPublicID:uint64}/memberships/demote\", h)\n\n\t\tgamesCh := games.Party(\"/challenge\")\n\t\t{\n\t\t\t// games/challenge\n\t\t\tgamesCh.Get(\"/\", h)\n\n\t\t\tgamesChBeginner := gamesCh.Party(\"/beginner\")\n\t\t\t{\n\t\t\t\t// games/challenge/beginner/start\n\t\t\t\tgamesChBeginner.Get(\"/start\", h)\n\t\t\t\tlevelBeginner := gamesChBeginner.Party(\"/level\")\n\t\t\t\t{\n\t\t\t\t\t// games/challenge/beginner/level/first\n\t\t\t\t\tlevelBeginner.Get(\"/first\", h)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tgamesChIntermediate := gamesCh.Party(\"/intermediate\")\n\t\t\t{\n\t\t\t\t// games/challenge/intermediate\n\t\t\t\tgamesChIntermediate.Get(\"/\", h)\n\t\t\t\t// games/challenge/intermediate/start\n\t\t\t\tgamesChIntermediate.Get(\"/start\", h)\n\t\t\t}\n\t\t}\n\n\t}\n}\n\nfunc registerSubdomains(app *iris.Application) {\n\tmysubdomain := app.Subdomain(\"mysubdomain\")\n\t// http://mysubdomain.myhost.com\n\tmysubdomain.Get(\"/\", h)\n\n\twilldcardSubdomain := app.WildcardSubdomain()\n\twilldcardSubdomain.Get(\"/\", h)\n\twilldcardSubdomain.Party(\"/party\").Get(\"/\", h)\n}\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\n\tregisterErrors(app)\n\tregisterGamesRoutes(app)\n\tregisterSubdomains(app)\n\n\tapp.Handle(\"GET\", \"/healthcheck\", h)\n\n\t// \"POST\" method\n\t// this handler reads raw body from the client/request\n\t// and sends back the same body\n\t// remember, we have limit to that body in order\n\t// to protect ourselves from \"over heating\".\n\tapp.Post(\"/\", iris.LimitRequestBodySize(maxBodySize), func(ctx iris.Context) {\n\t\t// get request body\n\t\tb, err := ctx.GetBody()\n\t\t// if is larger then send a bad request status\n\t\tif err != nil {\n\t\t\tctx.StatusCode(iris.StatusBadRequest)\n\t\t\tctx.WriteString(err.Error())\n\t\t\treturn\n\t\t}\n\t\t// send back the post body\n\t\tctx.Write(b)\n\t})\n\n\tapp.HandleMany(\"POST PUT\", \"/postvalue\", func(ctx iris.Context) {\n\t\tname := ctx.PostValueDefault(\"name\", \"iris\")\n\t\theadervale := ctx.GetHeader(\"headername\")\n\t\tctx.Writef(\"Hello %s | %s\", name, headervale)\n\t})\n\n\treturn app\n}\n\nfunc h(ctx iris.Context) {\n\tmethod := ctx.Method()       // the http method requested a server's resource.\n\tsubdomain := ctx.Subdomain() // the subdomain, if any.\n\n\t// the request path (without scheme and host).\n\tpath := ctx.Path()\n\t// how to get all parameters, if we don't know\n\t// the names:\n\tparamsLen := ctx.Params().Len()\n\n\tctx.Params().Visit(func(name string, value string) {\n\t\tctx.Writef(\"%s = %s\\n\", name, value)\n\t})\n\tctx.Writef(\"Info\\n\\n\")\n\tctx.Writef(\"Method: %s\\nSubdomain: %s\\nPath: %s\\nParameters length: %d\", method, subdomain, path, paramsLen)\n}\n\nfunc main() {\n\tapp := newApp()\n\tapp.Logger().SetLevel(\"debug\")\n\n\t/*\n\t\t// GET\n\t\thttp://localhost:8080/healthcheck\n\t\thttp://localhost:8080/games/42/clans\n\t\thttp://localhost:8080/games/42/clans/clan/93\n\t\thttp://localhost:8080/games/42/clans/search\n\t\thttp://mysubdomain.localhost:8080/\n\n\t\t// PUT\n\t\thttp://localhost:8080/postvalue\n\t\thttp://localhost:8080/games/42/players/93\n\t\thttp://localhost:8080/games/42/clans/clan/93\n\n\t\t// POST\n\t\thttp://localhost:8080/\n\t\thttp://localhost:8080/postvalue\n\t\thttp://localhost:8080/games/42/clans\n\t\thttp://localhost:8080/games/42/players\n\t\thttp://localhost:8080/games/42/clans/93/leave\n\t\thttp://localhost:8080/games/42/clans/93/memberships/application\n\t\thttp://localhost:8080/games/42/clans/93/memberships/application/anystring\n\t\thttp://localhost:8080/games/42/clans/93/memberships/invitation\n\t\thttp://localhost:8080/games/42/clans/93/memberships/invitation/anystring\n\t\thttp://localhost:8080/games/42/clans/93/memberships/delete\n\t\thttp://localhost:8080/games/42/clans/93/memberships/promote\n\t\thttp://localhost:8080/games/42/clans/93/memberships/demote\n\n\t\t// FIRE NOT FOUND\n\t\thttp://localhost:8080/coudlntfound\n\t*/\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/routing/main_test.go",
    "content": "package main\n\nimport (\n\t\"strconv\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nfunc calculatePathAndResponse(method, subdomain, path string, paramKeyValue ...string) (string, string) {\n\tparamsLen := 0\n\n\tif l := len(paramKeyValue); l >= 2 {\n\t\tparamsLen = len(paramKeyValue) / 2\n\t}\n\n\tparamsInfo := \"\"\n\tif paramsLen > 0 {\n\t\tfor i := 0; i < len(paramKeyValue); i++ {\n\t\t\tparamKey := paramKeyValue[i]\n\t\t\ti++\n\t\t\tif i >= len(paramKeyValue) {\n\t\t\t\tpanic(\"paramKeyValue should be align with path parameters {} and must be placed in order\")\n\t\t\t}\n\n\t\t\tparamValue := paramKeyValue[i]\n\t\t\tparamsInfo += paramKey + \" = \" + paramValue + \"\\n\"\n\n\t\t\tbeginParam := strings.IndexByte(path, '{')\n\t\t\tendParam := strings.IndexByte(path, '}')\n\t\t\tif beginParam == -1 || endParam == -1 {\n\t\t\t\tpanic(\"something wrong with parameters, please define them in order\")\n\t\t\t}\n\n\t\t\tpath = path[:beginParam] + paramValue + path[endParam+1:]\n\t\t}\n\t}\n\n\treturn path, paramsInfo + `Info\n\nMethod: ` + method + `\nSubdomain: ` + subdomain + `\nPath: ` + path + `\nParameters length: ` + strconv.Itoa(paramsLen)\n}\n\ntype troute struct {\n\tmethod, subdomain, path string\n\tstatus                  int\n\texpectedBody            string\n\tcontentType             string\n}\n\nfunc newTroute(method, subdomain, path string, status int, paramKeyValue ...string) troute {\n\tfinalPath, expectedBody := calculatePathAndResponse(method, subdomain, path, paramKeyValue...)\n\tcontentType := \"text/plain; charset=UTF-8\"\n\n\tif status == httptest.StatusNotFound {\n\t\texpectedBody = notFoundHTML\n\t\tcontentType = \"text/html; charset=UTF-8\"\n\t}\n\n\treturn troute{\n\t\tcontentType:  contentType,\n\t\tmethod:       method,\n\t\tsubdomain:    subdomain,\n\t\tpath:         finalPath,\n\t\tstatus:       status,\n\t\texpectedBody: expectedBody,\n\t}\n}\n\nfunc TestRouting(t *testing.T) {\n\tapp := newApp()\n\te := httptest.New(t, app)\n\n\ttests := []troute{\n\t\t// GET\n\t\tnewTroute(\"GET\", \"\", \"/healthcheck\", httptest.StatusOK),\n\t\tnewTroute(\"GET\", \"\", \"/games/{gameID}/clans\", httptest.StatusOK, \"gameID\", \"42\"),\n\t\tnewTroute(\"GET\", \"\", \"/games/{gameID}/clans/clan/{clanPublicID}\", httptest.StatusOK, \"gameID\", \"42\", \"clanPublicID\", \"93\"),\n\t\tnewTroute(\"GET\", \"\", \"/games/{gameID}/clans/search\", httptest.StatusOK, \"gameID\", \"42\"),\n\t\tnewTroute(\"GET\", \"\", \"/games/challenge\", httptest.StatusOK),\n\t\tnewTroute(\"GET\", \"\", \"/games/challenge/beginner/start\", httptest.StatusOK),\n\t\tnewTroute(\"GET\", \"\", \"/games/challenge/beginner/level/first\", httptest.StatusOK),\n\t\tnewTroute(\"GET\", \"\", \"/games/challenge/intermediate\", httptest.StatusOK),\n\t\tnewTroute(\"GET\", \"\", \"/games/challenge/intermediate/start\", httptest.StatusOK),\n\t\tnewTroute(\"GET\", \"mysubdomain\", \"/\", httptest.StatusOK),\n\t\tnewTroute(\"GET\", \"mywildcardsubdomain\", \"/\", httptest.StatusOK),\n\t\tnewTroute(\"GET\", \"mywildcardsubdomain\", \"/party\", httptest.StatusOK),\n\t\t// PUT\n\t\tnewTroute(\"PUT\", \"\", \"/games/{gameID}/players/{clanPublicID}\", httptest.StatusOK, \"gameID\", \"42\", \"clanPublicID\", \"93\"),\n\t\tnewTroute(\"PUT\", \"\", \"/games/{gameID}/clans/clan/{clanPublicID}\", httptest.StatusOK, \"gameID\", \"42\", \"clanPublicID\", \"93\"),\n\t\t// POST\n\t\tnewTroute(\"POST\", \"\", \"/games/{gameID}/clans\", httptest.StatusOK, \"gameID\", \"42\"),\n\t\tnewTroute(\"POST\", \"\", \"/games/{gameID}/players\", httptest.StatusOK, \"gameID\", \"42\"),\n\t\tnewTroute(\"POST\", \"\", \"/games/{gameID}/clans/{clanPublicID}/leave\", httptest.StatusOK, \"gameID\", \"42\", \"clanPublicID\", \"93\"),\n\t\tnewTroute(\"POST\", \"\", \"/games/{gameID}/clans/{clanPublicID}/memberships/application\", httptest.StatusOK, \"gameID\", \"42\", \"clanPublicID\", \"93\"),\n\t\tnewTroute(\"POST\", \"\", \"/games/{gameID}/clans/{clanPublicID}/memberships/application/{action}\", httptest.StatusOK, \"gameID\", \"42\", \"clanPublicID\", \"93\", \"action\", \"somethinghere\"),\n\t\tnewTroute(\"POST\", \"\", \"/games/{gameID}/clans/{clanPublicID}/memberships/invitation\", httptest.StatusOK, \"gameID\", \"42\", \"clanPublicID\", \"93\"),\n\t\tnewTroute(\"POST\", \"\", \"/games/{gameID}/clans/{clanPublicID}/memberships/invitation/{action}\", httptest.StatusOK, \"gameID\", \"42\", \"clanPublicID\", \"93\", \"action\", \"somethinghere\"),\n\t\tnewTroute(\"POST\", \"\", \"/games/{gameID}/clans/{clanPublicID}/memberships/delete\", httptest.StatusOK, \"gameID\", \"42\", \"clanPublicID\", \"93\"),\n\t\tnewTroute(\"POST\", \"\", \"/games/{gameID}/clans/{clanPublicID}/memberships/promote\", httptest.StatusOK, \"gameID\", \"42\", \"clanPublicID\", \"93\"),\n\t\tnewTroute(\"POST\", \"\", \"/games/{gameID}/clans/{clanPublicID}/memberships/demote\", httptest.StatusOK, \"gameID\", \"42\", \"clanPublicID\", \"93\"),\n\t\t// POST: / will be tested alone\n\t\t// custom not found\n\t\tnewTroute(\"GET\", \"\", \"/notfound\", httptest.StatusNotFound),\n\t\tnewTroute(\"POST\", \"\", \"/notfound2\", httptest.StatusNotFound),\n\t\tnewTroute(\"PUT\", \"\", \"/notfound3\", httptest.StatusNotFound),\n\t\tnewTroute(\"GET\", \"mysubdomain\", \"/notfound42\", httptest.StatusNotFound),\n\t}\n\n\tfor _, tt := range tests {\n\t\tet := e.Request(tt.method, tt.path)\n\t\tif tt.subdomain != \"\" {\n\t\t\tet.WithURL(\"http://\" + tt.subdomain + \".localhost:8080\")\n\t\t}\n\t\tet.Expect().Status(tt.status).Body().IsEqual(tt.expectedBody)\n\t}\n\n\t// test POST \"/\" limit data and post data return\n\n\t// test with small body\n\te.POST(\"/\").WithBytes([]byte(\"ok\")).Expect().Status(httptest.StatusOK).Body().IsEqual(\"ok\")\n\t// test with equal to max body size limit\n\tbsent := make([]byte, maxBodySize, maxBodySize)\n\te.POST(\"/\").WithBytes(bsent).Expect().Status(httptest.StatusOK).Body().Length().Equal(len(bsent))\n\t// test with larger body sent and wait for the custom response\n\tlargerBSent := make([]byte, maxBodySize+1, maxBodySize+1)\n\te.POST(\"/\").WithBytes(largerBSent).Expect().Status(httptest.StatusBadRequest).Body().IsEqual(\"http: request body too large\")\n\n\t// test the post value (both post and put) and headers.\n\te.PUT(\"/postvalue\").WithFormField(\"name\", \"test_put\").\n\t\tWithHeader(\"headername\", \"headervalue_put\").Expect().\n\t\tStatus(httptest.StatusOK).Body().IsEqual(\"Hello test_put | headervalue_put\")\n\n\te.POST(\"/postvalue\").WithFormField(\"name\", \"test_post\").\n\t\tWithHeader(\"headername\", \"headervalue_post\").Expect().\n\t\tStatus(httptest.StatusOK).Body().IsEqual(\"Hello test_post | headervalue_post\")\n}\n"
  },
  {
    "path": "_examples/routing/overview/main.go",
    "content": "package main\n\nimport (\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/middleware/logger\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\t// Set Logger level to \"debug\",\n\t// see your terminal and the created file.\n\tapp.Logger().SetLevel(\"debug\")\n\n\t// Write logs to a file too.\n\tf := newLogFile()\n\tdefer f.Close()\n\tapp.Logger().AddOutput(f)\n\n\t// Register a request logger middleware to the application.\n\tapp.Use(logger.New())\n\n\t// GET: http://localhost:8080\n\tapp.Get(\"/\", info)\n\n\t// GET: http://localhost:8080/profile/anyusername\n\t//\n\t// Want to use a custom regex expression instead?\n\t// Easy: app.Get(\"/profile/{username:string regexp(^[a-zA-Z ]+$)}\")\n\tapp.Get(\"/profile/{username:string}\", info)\n\n\t// If parameter type is missing then it's string which accepts anything,\n\t// i.e: /{paramname} it's exactly the same as /{paramname:string}.\n\t// The below is exactly the same as\n\t// {username:string}\n\t//\n\t// GET: http://localhost:8080/profile/anyusername/backups/any/number/of/paths/here\n\tapp.Get(\"/profile/{username}/backups/{filepath:path}\", info)\n\n\t// Favicon\n\n\t// GET: http://localhost:8080/favicon.ico\n\tapp.Favicon(\"./public/images/favicon.ico\")\n\n\t// Static assets\n\n\t// GET: http://localhost:8080/assets/css/main.css\n\t//\t    maps to ./public/assets/css/main.css file at system location.\n\tapp.HandleDir(\"/assets\", iris.Dir(\"./public/assets\"))\n\n\t/* OR\n\n\t// GET: http://localhost:8080/css/main.css\n\t// \t\tmaps to ./public/assets/css/main.css file at system location.\n\tapp.HandleDir(\"/css\", iris.Dir(\"./public/assets/css\"))\n\n\t// GET: http://localhost:8080/css/bootstrap.min.css\n\t// \t\tmaps to ./public/assets/css/bootstrap.min.css file at system location.\n\tapp.HandleDir(\"/css\", iris.Dir(\"./public/assets/css\"))\n\n\t*/\n\n\t// Grouping\n\n\tusersRoutes := app.Party(\"/users\")\n\t// GET: http://localhost:8080/users/help\n\tusersRoutes.Get(\"/help\", func(ctx iris.Context) {\n\t\tctx.Writef(\"GET / -- fetch all users\\n\")\n\t\tctx.Writef(\"GET /$ID -- fetch a user by id\\n\")\n\t\tctx.Writef(\"POST / -- create new user\\n\")\n\t\tctx.Writef(\"PUT /$ID -- update an existing user\\n\")\n\t\tctx.Writef(\"DELETE /$ID -- delete an existing user\\n\")\n\t})\n\n\t// GET: http://localhost:8080/users\n\tusersRoutes.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.Writef(\"get all users\")\n\t})\n\n\t// GET: http://localhost:8080/users/42\n\t// **/users/42 and /users/help works after iris version 7.0.5**\n\tusersRoutes.Get(\"/{id:uint64}\", func(ctx iris.Context) {\n\t\tid, _ := ctx.Params().GetUint64(\"id\")\n\t\tctx.Writef(\"get user by id: %d\", id)\n\t})\n\n\t// POST: http://localhost:8080/users\n\tusersRoutes.Post(\"/\", func(ctx iris.Context) {\n\t\tusername, password := ctx.PostValue(\"username\"), ctx.PostValue(\"password\")\n\t\tctx.Writef(\"create user for username= %s and password= %s\", username, password)\n\t})\n\n\t// PUT: http://localhost:8080/users\n\tusersRoutes.Put(\"/{id:uint64}\", func(ctx iris.Context) {\n\t\tid, _ := ctx.Params().GetUint64(\"id\") // or .Get to get its string represatantion.\n\t\tusername := ctx.PostValue(\"username\")\n\t\tctx.Writef(\"update user for id= %d and new username= %s\", id, username)\n\t})\n\n\t// DELETE: http://localhost:8080/users/42\n\tusersRoutes.Delete(\"/{id:uint64}\", func(ctx iris.Context) {\n\t\tid, _ := ctx.Params().GetUint64(\"id\")\n\t\tctx.Writef(\"delete user by id: %d\", id)\n\t}).Describe(\"deletes a user\")\n\n\t// Subdomains, depends on the host, you have to edit the hosts or nginx/caddy's configuration if you use them.\n\t//\n\t// See more subdomains examples at _examples/routing/subdomains folder.\n\tadminRoutes := app.Party(\"admin.\")\n\n\t// GET: http://admin.localhost:8080\n\tadminRoutes.Get(\"/\", info)\n\t// GET: http://admin.localhost:8080/settings\n\tadminRoutes.Get(\"/settings\", info)\n\n\t// Wildcard/dynamic subdomain\n\tdynamicSubdomainRoutes := app.WildcardSubdomain()\n\n\t// GET: http://any_thing_here.localhost:8080\n\tdynamicSubdomainRoutes.Get(\"/\", info)\n\n\tapp.Delete(\"/something\", func(ctx iris.Context) {\n\t\tname := ctx.URLParam(\"name\")\n\t\tctx.WriteString(name)\n\t})\n\n\tapp.None(\"/secret\", privateHandler)\n\tapp.Get(\"/public\", execPrivateHandler)\n\n\t// GET: http://localhost:8080/\n\t// GET: http://localhost:8080/profile/anyusername\n\t// GET: http://localhost:8080/profile/anyusername/backups/any/number/of/paths/here\n\n\t// GET: http://localhost:8080/users/help\n\t// GET: http://localhost:8080/users\n\t// GET: http://localhost:8080/users/42\n\t// POST: http://localhost:8080/users\n\t// PUT: http://localhost:8080/users\n\t// DELETE: http://localhost:8080/users/42\n\t// DELETE: http://localhost:8080/something?name=iris\n\n\t// GET: http://admin.localhost:8080\n\t// GET: http://admin.localhost:8080/settings\n\t// GET: http://any_thing_here.localhost:8080\n\tapp.Listen(\":8080\")\n}\n\nfunc privateHandler(ctx iris.Context) {\n\tctx.WriteString(`This can only be executed programmatically through server's another route:\nctx.Exec(iris.MethodNone, \"/secret\")`)\n}\n\nfunc execPrivateHandler(ctx iris.Context) {\n\tctx.Exec(iris.MethodNone, \"/secret\")\n}\n\nfunc info(ctx iris.Context) {\n\tmethod := ctx.Method()       // the http method requested a server's resource.\n\tsubdomain := ctx.Subdomain() // the subdomain, if any.\n\n\t// the request path (without scheme and host).\n\tpath := ctx.Path()\n\t// how to get all parameters, if we don't know\n\t// the names:\n\tparamsLen := ctx.Params().Len()\n\n\tctx.Params().Visit(func(name string, value string) {\n\t\tctx.Writef(\"%s = %s\\n\", name, value)\n\t})\n\tctx.Writef(\"\\nInfo\\n\\n\")\n\tctx.Writef(\"Method: %s\\nSubdomain: %s\\nPath: %s\\nParameters length: %d\", method, subdomain, path, paramsLen)\n}\n\n// get a filename based on the date, file logs works that way the most times\n// but these are just a sugar.\nfunc todayFilename() string {\n\ttoday := time.Now().Format(\"Jan 02 2006\")\n\treturn today + \".txt\"\n}\n\nfunc newLogFile() *os.File {\n\tfilename := todayFilename()\n\t// open an output file, this will append to the today's file if server restarted.\n\tf, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treturn f\n}\n"
  },
  {
    "path": "_examples/routing/overview/public/assets/css/main.css",
    "content": "body {\n    background-color: black;\n}\n"
  },
  {
    "path": "_examples/routing/overview-2/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\n// User is just a bindable object structure.\ntype User struct {\n\tUsername  string `json:\"username\"`\n\tFirstname string `json:\"firstname\"`\n\tLastname  string `json:\"lastname\"`\n\tCity      string `json:\"city\"`\n\tAge       int    `json:\"age\"`\n}\n\nfunc main() {\n\tapp := iris.New()\n\tapp.Logger().SetLevel(\"debug\")\n\t// app.Logger().SetLevel(\"disable\") to disable the logger.\n\n\t// Define templates using the std html/template engine.\n\t// Parse and load all files inside \"./views\" folder with \".html\" file extension.\n\t// Reload the templates on each request (development mode).\n\tapp.RegisterView(iris.HTML(\"./views\", \".html\").Reload(true))\n\n\t// Register custom handler for specific http errors.\n\tapp.OnErrorCode(iris.StatusInternalServerError, func(ctx iris.Context) {\n\t\t// .Values are used to communicate between handlers, middleware.\n\t\terrMessage := ctx.Values().GetString(\"error\")\n\t\tif errMessage != \"\" {\n\t\t\tctx.Writef(\"Internal server error: %s\", errMessage)\n\t\t\treturn\n\t\t}\n\n\t\tctx.Writef(\"(Unexpected) internal server error\")\n\t})\n\n\tapp.Use(func(ctx iris.Context) {\n\t\tctx.Application().Logger().Infof(\"Begin request for path: %s\", ctx.Path())\n\t\tctx.Next()\n\t})\n\t// app.Done(func(ctx iris.Context) {]})\n\n\t// POST: scheme://mysubdomain.$domain.com/decode\n\tapp.Subdomain(\"mysubdomain.\").Post(\"/decode\", func(ctx iris.Context) {})\n\t// Method POST: http://localhost:8080/decode\n\tapp.Post(\"/decode\", func(ctx iris.Context) {\n\t\tvar user User\n\t\tctx.ReadJSON(&user)\n\t\tctx.Writef(\"%s %s is %d years old and comes from %s\", user.Firstname, user.Lastname, user.Age, user.City)\n\t})\n\n\t// Method GET: http://localhost:8080/encode\n\tapp.Get(\"/encode\", func(ctx iris.Context) {\n\t\tdoe := User{\n\t\t\tUsername:  \"Johndoe\",\n\t\t\tFirstname: \"John\",\n\t\t\tLastname:  \"Doe\",\n\t\t\tCity:      \"Neither FBI knows!!!\",\n\t\t\tAge:       25,\n\t\t}\n\n\t\tctx.JSON(doe)\n\t})\n\n\t// Method GET: http://localhost:8080/profile/anytypeofstring\n\tapp.Get(\"/profile/{username:string}\", profileByUsername)\n\n\tusersRoutes := app.Party(\"/users\", logThisMiddleware)\n\t{\n\t\t// Method GET: http://localhost:8080/users/42\n\t\tusersRoutes.Get(\"/{id:int min(1)}\", getUserByID)\n\t\t// Method POST: http://localhost:8080/users/create\n\t\tusersRoutes.Post(\"/create\", createUser)\n\t}\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.HTML(\"<html><head></head><body><ul>\")\n\t\tfor _, link := range []string{\"/encode\", \"/profile/username\", \"/users/42\"} {\n\t\t\tctx.HTML(fmt.Sprintf(`<li><a href=\"%s\">%s</a></li>`, link, link))\n\t\t}\n\t\tctx.HTML(\"</ul></body></html>\")\n\t})\n\n\t// Listen for incoming HTTP/1.x & HTTP/2 clients on localhost port 8080.\n\tapp.Listen(\":8080\", iris.WithCharset(\"utf-8\"))\n}\n\nfunc logThisMiddleware(ctx iris.Context) {\n\tctx.Application().Logger().Infof(\"Path: %s | IP: %s\", ctx.Path(), ctx.RemoteAddr())\n\n\t// .Next is required to move forward to the chain of handlers,\n\t// if missing then it stops the execution at this handler.\n\tctx.Next()\n}\n\nfunc profileByUsername(ctx iris.Context) {\n\t// .Params are used to get dynamic path parameters.\n\tusername := ctx.Params().Get(\"username\")\n\tctx.ViewData(\"Username\", username)\n\t// renders \"./views/user/profile.html\"\n\t// with {{ .Username }} equals to the username dynamic path parameter.\n\tif err := ctx.View(\"user/profile.html\"); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n}\n\nfunc getUserByID(ctx iris.Context) {\n\tuserID := ctx.Params().Get(\"id\") // Or convert directly using: .Values().GetInt/GetInt64 etc...\n\t// your own db fetch here instead of user :=...\n\tuser := User{Username: \"username\" + userID}\n\n\tctx.XML(user)\n}\n\nfunc createUser(ctx iris.Context) {\n\tvar user User\n\terr := ctx.ReadForm(&user)\n\tif err != nil {\n\t\tctx.Values().Set(\"error\", \"creating user, read and parse form failed. \"+err.Error())\n\t\tctx.StatusCode(iris.StatusInternalServerError)\n\t\treturn\n\t}\n\t// renders \"./views/user/create_verification.html\"\n\t// with {{ . }} equals to the User object, i.e {{ .Username }} , {{ .Firstname}} etc...\n\tctx.ViewData(\"\", user)\n\tif err := ctx.View(\"user/create_verification.html\"); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "_examples/routing/overview-2/views/user/create_verification.html",
    "content": "<html>\n    <head><title>Create verification</title></head>\n    <body>\n        <h1> Create Verification </h1>\n        <table style=\"width:550px\">\n        <tr>\n            <th>Username</th>\n            <th>Firstname</th>\n            <th>Lastname</th>\n            <th>City</th>\n            <th>Age</th>\n        </tr>\n        <tr>\n            <td>{{ .Username }}</td>\n            <td>{{ .Firstname }}</td>\n            <td>{{ .Lastname }}</td>\n            <td>{{ .City }}</td>\n            <td>{{ .Age }}</td>\n        </tr>\n        </table> \n    </body>\n</html>\n"
  },
  {
    "path": "_examples/routing/overview-2/views/user/profile.html",
    "content": "<html>\n    <head><title>Profile page</title></head>\n    <body>\n        <h1> Profile </h1>\n        <b> {{ .Username }} </b>\n    </body>\n</html>\n"
  },
  {
    "path": "_examples/routing/party-controller/go.mod",
    "content": "module github.com/kataras/iris/_examples/routing/party-controller\n\ngo 1.25\n\nrequire github.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd\n\nrequire (\n\tgithub.com/BurntSushi/toml v1.6.0 // indirect\n\tgithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect\n\tgithub.com/CloudyKit/jet/v6 v6.3.1 // indirect\n\tgithub.com/Joker/jade v1.1.3 // indirect\n\tgithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 // indirect\n\tgithub.com/andybalholm/brotli v1.2.0 // indirect\n\tgithub.com/aymerick/douceur v0.2.0 // indirect\n\tgithub.com/fatih/structs v1.1.0 // indirect\n\tgithub.com/flosch/pongo2/v4 v4.0.2 // indirect\n\tgithub.com/golang/snappy v1.0.0 // indirect\n\tgithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a // indirect\n\tgithub.com/google/uuid v1.6.0 // indirect\n\tgithub.com/gorilla/css v1.0.1 // indirect\n\tgithub.com/iris-contrib/schema v0.0.6 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/kataras/blocks v0.0.8 // indirect\n\tgithub.com/kataras/golog v0.1.12 // indirect\n\tgithub.com/kataras/pio v0.0.13 // indirect\n\tgithub.com/kataras/sitemap v0.0.6 // indirect\n\tgithub.com/kataras/tunnel v0.0.4 // indirect\n\tgithub.com/klauspost/compress v1.18.2 // indirect\n\tgithub.com/kr/pretty v0.3.1 // indirect\n\tgithub.com/mailgun/raymond/v2 v2.0.48 // indirect\n\tgithub.com/mailru/easyjson v0.9.1 // indirect\n\tgithub.com/microcosm-cc/bluemonday v1.0.27 // indirect\n\tgithub.com/rogpeppe/go-internal v1.10.0 // indirect\n\tgithub.com/russross/blackfriday/v2 v2.1.0 // indirect\n\tgithub.com/schollz/closestmatch v2.1.0+incompatible // indirect\n\tgithub.com/sirupsen/logrus v1.8.1 // indirect\n\tgithub.com/stretchr/testify v1.11.1 // indirect\n\tgithub.com/tdewolff/minify/v2 v2.24.8 // indirect\n\tgithub.com/tdewolff/parse/v2 v2.8.5 // indirect\n\tgithub.com/valyala/bytebufferpool v1.0.0 // indirect\n\tgithub.com/vmihailenco/msgpack/v5 v5.4.1 // indirect\n\tgithub.com/vmihailenco/tagparser/v2 v2.0.0 // indirect\n\tgithub.com/yosssi/ace v0.0.5 // indirect\n\tgolang.org/x/crypto v0.47.0 // indirect\n\tgolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 // indirect\n\tgolang.org/x/net v0.49.0 // indirect\n\tgolang.org/x/sys v0.40.0 // indirect\n\tgolang.org/x/text v0.33.0 // indirect\n\tgolang.org/x/time v0.14.0 // indirect\n\tgoogle.golang.org/protobuf v1.36.11 // indirect\n\tgopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect\n\tgopkg.in/ini.v1 v1.67.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n"
  },
  {
    "path": "_examples/routing/party-controller/go.sum",
    "content": "github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=\ngithub.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=\ngithub.com/CloudyKit/jet/v6 v6.3.1 h1:6IAo5Cx21xrHVaR8zzXN5gJatKV/wO7Nf6bfCnCSbUw=\ngithub.com/CloudyKit/jet/v6 v6.3.1/go.mod h1:lf8ksdNsxZt7/yH/3n4vJQWA9RUq4wpaHtArHhGVMOw=\ngithub.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc=\ngithub.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=\ngithub.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=\ngithub.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 h1:dG7/gLroJGht/jSQtHiLvT48Hxn+crbmvyItZC8cWOs=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05/go.mod h1:NYezi6wtnJtBm5btoprXc5SvAdqH0XTXWnUup0MptAI=\ngithub.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=\ngithub.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=\ngithub.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=\ngithub.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=\ngithub.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=\ngithub.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=\ngithub.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=\ngithub.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=\ngithub.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=\ngithub.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw=\ngithub.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8=\ngithub.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=\ngithub.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=\ngithub.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=\ngithub.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a h1:l7A0loSszR5zHd/qK53ZIHMO8b3bBSmENnQ6eKnUT0A=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=\ngithub.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=\ngithub.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=\ngithub.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=\ngithub.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=\ngithub.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2 h1:T9THsdP1woyAqKHwjkEsbCnMefsAFvk8iJJKokcJ3Go=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2/go.mod h1:JLDgIqnFy5loDSUv1OA2j0mb6p/rDhiCqigP22Uq9xE=\ngithub.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw=\ngithub.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/kataras/blocks v0.0.8 h1:MrpVhoFTCR2v1iOOfGng5VJSILKeZZI+7NGfxEh3SUM=\ngithub.com/kataras/blocks v0.0.8/go.mod h1:9Jm5zx6BB+06NwA+OhTbHW1xkMOYxahnqTN5DveZ2Yg=\ngithub.com/kataras/golog v0.1.12 h1:Bu7I/G4ilJlbfzjmU39O9N+2uO1pBcMK045fzZ4ytNg=\ngithub.com/kataras/golog v0.1.12/go.mod h1:wrGSbOiBqbQSQznleVNX4epWM8rl9SJ/rmEacl0yqy4=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd h1:oMsQgqKACbUdlaIWnN+EZzzAnHkw90hE/y/R0BSij2U=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd/go.mod h1:yucjtDl9Vn3OctWM1fi0MuM9OckemC7kEreFa9QtPF8=\ngithub.com/kataras/pio v0.0.13 h1:x0rXVX0fviDTXOOLOmr4MUxOabu1InVSTu5itF8CXCM=\ngithub.com/kataras/pio v0.0.13/go.mod h1:k3HNuSw+eJ8Pm2lA4lRhg3DiCjVgHlP8hmXApSej3oM=\ngithub.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY=\ngithub.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4=\ngithub.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA=\ngithub.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw=\ngithub.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=\ngithub.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=\ngithub.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=\ngithub.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8=\ngithub.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=\ngithub.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=\ngithub.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=\ngithub.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=\ngithub.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=\ngithub.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=\ngithub.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=\ngithub.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=\ngithub.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=\ngithub.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=\ngithub.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=\ngithub.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=\ngithub.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk=\ngithub.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=\ngithub.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=\ngithub.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=\ngithub.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=\ngithub.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/tdewolff/minify/v2 v2.24.8 h1:58/VjsbevI4d5FGV0ZSuBrHMSSkH4MCH0sIz/eKIauE=\ngithub.com/tdewolff/minify/v2 v2.24.8/go.mod h1:0Ukj0CRpo/sW/nd8uZ4ccXaV1rEVIWA3dj8U7+Shhfw=\ngithub.com/tdewolff/parse/v2 v2.8.5 h1:ZmBiA/8Do5Rpk7bDye0jbbDUpXXbCdc3iah4VeUvwYU=\ngithub.com/tdewolff/parse/v2 v2.8.5/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=\ngithub.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE=\ngithub.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=\ngithub.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=\ngithub.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=\ngithub.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=\ngithub.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=\ngithub.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=\ngithub.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=\ngithub.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA=\ngithub.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0=\ngithub.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=\ngithub.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=\ngithub.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=\ngolang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=\ngolang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=\ngolang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=\ngolang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=\ngolang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=\ngolang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=\ngolang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=\ngolang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=\ngoogle.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=\ngopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nmoul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs=\nmoul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE=\n"
  },
  {
    "path": "_examples/routing/party-controller/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n\n\t\"github.com/kataras/iris/_examples/routing/party-controller/pkg/weatherapi\"\n)\n\n// Example of usage of Party Controllers.\n// The method of zero-performance cost at serve-time, APIs run as fast as common Iris handlers.\nfunc main() {\n\tapp := iris.New()\n\n\t// Define a group under /api request path.\n\tapi := app.Party(\"/api\")\n\t// Register one or more dependencies.\n\tapi.RegisterDependency(weatherapi.NewClient(weatherapi.Options{\n\t\tAPIKey: \"{YOUR_API_KEY}\",\n\t}))\n\n\t// Register a party controller under the \"/weather\" sub request path.\n\tapi.PartyConfigure(\"/weather\", new(WeatherController))\n\n\t// Start the local server at 8080 port.\n\tapp.Listen(\":8080\")\n}\n\n// Just like the MVC controllers, route group(aka Party) controller's\n// fields are injected by the parent or current party's RegisterDependency method.\n//\n// This controller structure could be live to another sub-package of our application as well.\ntype WeatherController struct {\n\tClient *weatherapi.Client // This is automatically injected by .RegisterDependency.\n}\n\nfunc (api *WeatherController) Configure(r iris.Party) {\n\t// Register routes under /api/weather.\n\tr.Get(\"/current\", api.getCurrentData)\n}\n\n// Normal Iris Handler.\nfunc (api *WeatherController) getCurrentData(ctx iris.Context) {\n\tcity := ctx.URLParamDefault(\"city\", \"Athens\")\n\n\t// Call the controller's \"Client\"'s GetCurrentByCity method\n\t// to retrieve data from external provider and push them to our clients.\n\tdata, err := api.Client.GetCurrentByCity(ctx, city)\n\tif err != nil {\n\t\tctx.StatusCode(iris.StatusBadRequest)\n\t\tctx.JSON(iris.Map{\n\t\t\t\"error\": err.Error(),\n\t\t})\n\t\treturn\n\t}\n\n\tctx.JSON(data)\n}\n"
  },
  {
    "path": "_examples/routing/party-controller/pkg/weatherapi/client.go",
    "content": "package weatherapi\n\nimport (\n\t\"context\"\n\t\"net/url\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/x/client\"\n)\n\n// The BaseURL of our API client.\nconst BaseURL = \"https://api.weatherapi.com/v1\"\n\ntype (\n\tOptions struct {\n\t\tAPIKey string `json:\"api_key\" yaml:\"APIKey\" toml:\"APIKey\"`\n\t}\n\n\tClient struct {\n\t\t*client.Client\n\t}\n)\n\nfunc NewClient(opts Options) *Client {\n\tapiKeyParameterSetter := client.RequestParam(\"key\", opts.APIKey)\n\n\tc := client.New(client.BaseURL(BaseURL),\n\t\tclient.PersistentRequestOptions(apiKeyParameterSetter))\n\n\treturn &Client{c}\n}\n\nfunc (c *Client) GetCurrentByCity(ctx context.Context, city string) (resp Response, err error) {\n\turlpath := \"/current.json\"\n\t// ?q=Athens&aqi=no\n\tparams := client.RequestQuery(url.Values{\n\t\t\"q\":   []string{city},\n\t\t\"aqi\": []string{\"no\"},\n\t})\n\n\terr = c.Client.ReadJSON(ctx, &resp, iris.MethodGet, urlpath, nil, params)\n\treturn\n}\n"
  },
  {
    "path": "_examples/routing/party-controller/pkg/weatherapi/response.go",
    "content": "package weatherapi\n\ntype Response struct {\n\tLocation struct {\n\t\tName           string  `json:\"name\"`\n\t\tRegion         string  `json:\"region\"`\n\t\tCountry        string  `json:\"country\"`\n\t\tLat            float64 `json:\"lat\"`\n\t\tLon            float64 `json:\"lon\"`\n\t\tTzID           string  `json:\"tz_id\"`\n\t\tLocaltimeEpoch int     `json:\"localtime_epoch\"`\n\t\tLocaltime      string  `json:\"localtime\"`\n\t} `json:\"location\"`\n\tCurrent struct {\n\t\tLastUpdatedEpoch int     `json:\"last_updated_epoch\"`\n\t\tLastUpdated      string  `json:\"last_updated\"`\n\t\tTempC            float64 `json:\"temp_c\"`\n\t\tTempF            float64 `json:\"temp_f\"`\n\t\tIsDay            int     `json:\"is_day\"`\n\t\tCondition        struct {\n\t\t\tText string `json:\"text\"`\n\t\t\tIcon string `json:\"icon\"`\n\t\t\tCode int    `json:\"code\"`\n\t\t} `json:\"condition\"`\n\t\tWindMph    float64 `json:\"wind_mph\"`\n\t\tWindKph    float64 `json:\"wind_kph\"`\n\t\tWindDegree int     `json:\"wind_degree\"`\n\t\tWindDir    string  `json:\"wind_dir\"`\n\t\tPressureMb float64 `json:\"pressure_mb\"`\n\t\tPressureIn float64 `json:\"pressure_in\"`\n\t\tPrecipMm   float64 `json:\"precip_mm\"`\n\t\tPrecipIn   float64 `json:\"precip_in\"`\n\t\tHumidity   int     `json:\"humidity\"`\n\t\tCloud      int     `json:\"cloud\"`\n\t\tFeelslikeC float64 `json:\"feelslike_c\"`\n\t\tFeelslikeF float64 `json:\"feelslike_f\"`\n\t\tVisKm      float64 `json:\"vis_km\"`\n\t\tVisMiles   float64 `json:\"vis_miles\"`\n\t\tUv         float64 `json:\"uv\"`\n\t\tGustMph    float64 `json:\"gust_mph\"`\n\t\tGustKph    float64 `json:\"gust_kph\"`\n\t} `json:\"current\"`\n}\n"
  },
  {
    "path": "_examples/routing/remove-handler/main.go",
    "content": "package main\n\nimport \"github.com/kataras/iris/v12\"\n\nfunc main() {\n\tapp := newApp()\n\tapp.Listen(\":8080\")\n}\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\n\tapi := app.Party(\"/api\")\n\tapi.Use(myMiddleware)\n\tusers := api.Party(\"/users\")\n\tusers.Get(\"/\", usersIndex).RemoveHandler(myMiddleware)\n\t// OR for all routes under a Party (or Application):\n\t// users.RemoveHandler(...)\n\n\treturn app\n}\n\nfunc myMiddleware(ctx iris.Context) {\n\tctx.WriteString(\"Middleware\\n\")\n}\n\nfunc usersIndex(ctx iris.Context) {\n\tctx.WriteString(\"OK\")\n}\n"
  },
  {
    "path": "_examples/routing/remove-handler/main_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nfunc TestSimpleRouteRemoveHandler(t *testing.T) {\n\tapp := newApp()\n\te := httptest.New(t, app)\n\n\te.GET(\"/api/users\").Expect().Status(httptest.StatusOK).Body().IsEqual(\"OK\")\n}\n"
  },
  {
    "path": "_examples/routing/remove-route/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.Get(\"/\", index)\n\tapp.Get(\"/about\", about).SetName(\"about_page\")\n\tapp.RemoveRoute(\"about_page\")\n\n\t// http://localhost:8080\n\t// http://localhost:8080/about (Not Found)\n\tapp.Listen(\":8080\")\n}\n\nfunc index(ctx iris.Context) {\n\tctx.WriteString(\"Hello, Gophers!\")\n}\n\nfunc about(ctx iris.Context) {\n\tctx.HTML(\"<h1>About Page</h1>\")\n}\n"
  },
  {
    "path": "_examples/routing/reverse/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/core/router\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\t// need for manually reverse routing when needed outside of view engine.\n\t// you normally don't need it because of the {{ urlpath \"routename\" \"path\" \"values\" \"here\"}}\n\trv := router.NewRoutePathReverser(app)\n\n\tmyroute := app.Get(\"/anything/{anythingparameter:path}\", func(ctx iris.Context) {\n\t\tparamValue := ctx.Params().Get(\"anythingparameter\")\n\t\tctx.Writef(\"The path after /anything is: %s\", paramValue)\n\t})\n\n\tmyroute.Name = \"myroute\"\n\n\t// useful for links, although iris' view engine has the {{ urlpath \"routename\" \"path values\"}} already.\n\tapp.Get(\"/reverse_myroute\", func(ctx iris.Context) {\n\t\tmyrouteRequestPath := rv.Path(myroute.Name, \"any/path\")\n\t\tctx.HTML(\"Should be <b>/anything/any/path</b>: \" + myrouteRequestPath)\n\t})\n\n\t// execute a route, similar to redirect but without redirect :)\n\tapp.Get(\"/execute_myroute\", func(ctx iris.Context) {\n\t\tctx.Exec(\"GET\", \"/anything/any/path\") // like it was called by the client.\n\t})\n\n\t// http://localhost:8080/reverse_myroute\n\t// http://localhost:8080/execute_myroute\n\t// http://localhost:8080/anything/any/path/here\n\t//\n\t// See view/template_html_4 example for more reverse routing examples\n\t// using the reverse router component and the {{url}} and {{urlpath}} template functions.\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/routing/rewrite/hosts",
    "content": "127.0.0.1\tmydomain.com\n127.0.0.1\twww.mydomain.com\n127.0.0.1\ttest.mydomain.com\n127.0.0.1\tnewtest.mydomain.com\n"
  },
  {
    "path": "_examples/routing/rewrite/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/middleware/rewrite\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\tapp.Get(\"/\", index)\n\tapp.Get(\"/about\", about)\n\tapp.Get(\"/docs\", docs)\n\tapp.Get(\"/users\", listUsers)\n\n\tapp.Subdomain(\"test\").Get(\"/\", testIndex)\n\n\tnewtest := app.Subdomain(\"newtest\")\n\tnewtest.Get(\"/\", newTestIndex)\n\tnewtest.Get(\"/about\", newTestAbout)\n\n\tredirects := rewrite.Load(\"redirects.yml\")\n\tapp.WrapRouter(redirects)\n\n\t// http://mydomain.com:8080/seo/about       -> http://www.mydomain.com:8080/about\n\t// http://test.mydomain.com:8080            -> http://newtest.mydomain.com:8080\n\t// http://test.mydomain.com:8080/seo/about  -> http://newtest.mydomain.com:8080/about\n\t// http://mydomain.com:8080/seo             -> http://www.mydomain.com:8080\n\t// http://mydomain.com:8080/about\n\t// http://mydomain.com:8080/docs/v12/hello  -> http://www.mydomain.com:8080/docs\n\t// http://mydomain.com:8080/docs/v12some    -> http://www.mydomain.com:8080/docs\n\t// http://mydomain.com:8080/oldsome         -> http://www.mydomain.com:8080\n\t// http://mydomain.com:8080/oldindex/random -> http://www.mydomain.com:8080\n\t// http://mydomain.com:8080/users.json      -> http://www.mydomain.com:8080/users?format=json\n\tapp.Listen(\":8080\")\n}\n\nfunc index(ctx iris.Context) {\n\tctx.WriteString(\"Index\")\n}\n\nfunc about(ctx iris.Context) {\n\tctx.WriteString(\"About\")\n}\n\nfunc docs(ctx iris.Context) {\n\tctx.WriteString(\"Docs\")\n}\n\nfunc listUsers(ctx iris.Context) {\n\tformat := ctx.URLParamDefault(\"format\", \"text\")\n\t/*\n\t\tswitch format{\n\t\t\tcase \"json\":\n\t\t\t\tctx.JSON(response)\n\t\t\tcase \"xml\":\n\t\t\t\tctx.XML(response)\n\t\t\t// [...]\n\t\t}\n\t*/\n\tctx.Writef(\"Format: %s\", format)\n}\n\nfunc testIndex(ctx iris.Context) {\n\tctx.WriteString(`Test Subdomain Index\n\t\t\t\t\t(This should never be executed,\n\t\t\t\t\tredirects to newtest subdomain)`)\n}\n\nfunc newTestIndex(ctx iris.Context) {\n\tctx.WriteString(\"New Test Subdomain Index\")\n}\n\nfunc newTestAbout(ctx iris.Context) {\n\tctx.WriteString(\"New Test Subdomain About\")\n}\n\n/* More...\nrewriteOptions := rewrite.Options{\n\tRedirectMatch: []string{\n\t\t\"301 /seo/(.*) /$1\",\n\t\t\"301 /docs/v12(.*) /docs\",\n\t\t\"301 /old(.*) /\",\n\t\t\"301 ^(http|https)://test.(.*) $1://newtest.$2\",\n\t\t\"0 /(.*).(json|xml) /$1?format=$2\",\n\t},\n\tPrimarySubdomain: \"www\",\n}\nrewriteEngine, err := rewrite.New(rewriteOptions)\n\n// To use it per-party use its `Handler` method. Even if not route match:\napp.UseRouter(rewriteEngine.Handler)\n// To use it per-party when route match:\napp.Use(rewriteEngine.Handler)\n//\n// To use it on a single route just pass it to the Get/Post method.\n//\n// To make the entire application respect the redirect rules\n// you have to wrap the Iris Router and pass the `Rewrite` method instead\n// as we did at this example:\n// app.WrapRouter(rewriteEngine.Rewrite)\n*/\n"
  },
  {
    "path": "_examples/routing/rewrite/redirects.yml",
    "content": "RedirectMatch: # REDIRECT_CODE_DIGITS | PATTERN_REGEX | TARGET_REPL\n  # Redirects /seo/* to /*\n  - 301 /seo/(.*) /$1\n\n  # Redirects /docs/v12* to /docs\n  - 301 /docs/v12(.*) /docs\n\n  # Redirects /old(.*) to /\n  - 301 /old(.*) /\n\n  # Redirects http or https://test.* to http or https://newtest.*\n  - 301 ^(http|https)://test.(.*) $1://newtest.$2\n\n  # Handles /*.json or .xml as *?format=json or xml,\n  # WITHOUT redirect. See /users route.\n  # When Code is 0 then it does not redirect the request,\n  # instead it changes the request URL\n  # and leaves a route handle the request.\n  - 0 /(.*).(json|xml) /$1?format=$2\n\n# Redirects root domain to www.\n# Creation of a www subdomain inside the Application is unnecessary,\n# all requests are handled by the root Application itself.\nPrimarySubdomain: www\n"
  },
  {
    "path": "_examples/routing/route-handlers-execution-rules/main.go",
    "content": "/*\nPackage main is a simple example of the behavior change of the execution flow of the handlers,\nnormally we need the `ctx.Next()` to call the next handler in a route's handler chain,\nbut with the `ExecutionRules` we can change this default behavior.\nPlease read below before continue.\n\nThe `Party#SetExecutionRules` alters the execution flow of the route handlers.\n\nFor example, if for some reason the desired result is the (done or all) handlers\nto be executed no matter what, even if no `ctx.Next()` is called in the previous handlers:\n\n\tapp.SetExecutionRules(iris.ExecutionRules {\n\t  Begin: iris.ExecutionOptions{Force: true}, # begin handlers(.Use)\n\t  Main:  iris.ExecutionOptions{Force: true}, # main handler (.Handle/Get...)\n\t  Done:  iris.ExecutionOptions{Force: true}, # done handlers (.Done)\n\t})\n\nNote that if `true` then the only remained way to \"break\" the handler chain\nis by calling the `ctx.StopExecution()` (now that `ctx.Next()` doesn't even matter).\n\nThese rules are per-party, so if a `Party` creates a child one then\nthe same rules will be applied to that as well.\n\nReset of these rules to their defaults (before `Party#Handle`) can be done\nwith `Party#SetExecutionRules(iris.ExecutionRules{})`.\n*/\npackage main\n\nimport \"github.com/kataras/iris/v12\"\n\nfunc main() {\n\tapp := iris.New()\n\n\tapp.SetExecutionRules(iris.ExecutionRules{\n\t\t// * From `Use[all]` to `Handle[last]` future route handlers,\n\t\t// execute all (even if `ctx.Next()` is missing):\n\t\t// Begin: true,\n\t\t//\n\t\t// * All `Handle` future route handlers, execute all:\n\t\t// Main: true,\n\t\t//\n\t\t// * From `Handle[last]` to `Done[last]` future route handlers, execute all:\n\t\tDone: iris.ExecutionOptions{Force: true},\n\t})\n\tapp.Done(doneHandler)\n\n\tapp.Get(\"/\", mainHandler)\n\n\t// http://localhost:8080\n\tapp.Listen(\":8080\")\n}\n\nfunc mainHandler(ctx iris.Context) {\n\tctx.WriteString(\"From Main Handler\\n\")\n\t// ctx.Next() is not required now that we have declared\n\t// Done: iris.ExecutionOptions{Force: true}.\n}\n\nfunc doneHandler(ctx iris.Context) {\n\tctx.WriteString(\"From Done Handler\\n\")\n}\n"
  },
  {
    "path": "_examples/routing/route-register-rule/main.go",
    "content": "package main\n\nimport \"github.com/kataras/iris/v12\"\n\nfunc main() {\n\tapp := newApp()\n\t// Navigate through https://github.com/kataras/iris/issues/1448 for details.\n\t//\n\t// GET: http://localhost:8080\n\t// POST, PUT, DELETE, CONNECT, HEAD, PATCH, OPTIONS, TRACE : http://localhost:8080\n\tapp.Listen(\":8080\")\n}\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\t// Skip and do NOT override existing regitered route, continue normally.\n\t// Applies to a Party and its children, in this case the whole application's routes.\n\tapp.SetRegisterRule(iris.RouteSkip)\n\n\t/* Read also:\n\t// The default behavior, will override the getHandler to anyHandler on `app.Any` call.\n\tapp.SetRegistRule(iris.RouteOverride)\n\n\t// Stops the execution and fires an error before server boot.\n\tapp.SetRegisterRule(iris.RouteError)\n\n\t// If ctx.StopExecution or StopWithXXX then the next route will be executed\n\t// (see mvc/authenticated-controller example too).\n\tapp.SetRegisterRule(iris.RouteOverlap)\n\t*/\n\n\tapp.Get(\"/\", getHandler)\n\t// app.Any does NOT override the previous GET route because of `iris.RouteSkip` rule.\n\tapp.Any(\"/\", anyHandler)\n\n\treturn app\n}\n\nfunc getHandler(ctx iris.Context) {\n\tctx.Writef(\"From GET: %s\", ctx.GetCurrentRoute().MainHandlerName())\n}\n\nfunc anyHandler(ctx iris.Context) {\n\tctx.Writef(\"From %s: %s\", ctx.Method(), ctx.GetCurrentRoute().MainHandlerName())\n}\n"
  },
  {
    "path": "_examples/routing/route-register-rule/main_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/core/router\"\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nfunc TestRouteRegisterRuleExample(t *testing.T) {\n\tapp := newApp()\n\te := httptest.New(t, app)\n\n\tfor _, method := range router.AllMethods {\n\t\ttt := e.Request(method, \"/\").Expect().Status(httptest.StatusOK).Body()\n\t\tif method == \"GET\" {\n\t\t\ttt.Equal(\"From GET: iris/_examples/routing/route-register-rule.getHandler\")\n\t\t} else {\n\t\t\ttt.Equal(\"From \" + method + \": iris/_examples/routing/route-register-rule.anyHandler\")\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "_examples/routing/route-state/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\tnone := app.None(\"/invisible/{username}\", func(ctx iris.Context) {\n\t\tctx.Writef(\"Hello %s with method: %s\", ctx.Params().Get(\"username\"), ctx.Method())\n\n\t\tif from := ctx.Values().GetString(\"from\"); from != \"\" {\n\t\t\tctx.Writef(\"\\nI see that you're coming from %s\", from)\n\t\t}\n\t})\n\n\tapp.Get(\"/change\", func(ctx iris.Context) {\n\t\tif none.IsOnline() {\n\t\t\tnone.Method = iris.MethodNone\n\t\t} else {\n\t\t\tnone.Method = iris.MethodGet\n\t\t}\n\n\t\t// refresh re-builds the router at serve-time in order to be notified for its new routes.\n\t\tapp.RefreshRouter()\n\t})\n\n\tapp.Get(\"/execute\", func(ctx iris.Context) {\n\t\tif !none.IsOnline() {\n\t\t\tctx.Values().Set(\"from\", \"/execute with offline access\")\n\t\t\tctx.Exec(\"NONE\", \"/invisible/iris\")\n\t\t\treturn\n\t\t}\n\n\t\t// same as navigating to \"http://localhost:8080/invisible/iris\" when /change has being invoked and route state changed\n\t\t// from \"offline\" to \"online\"\n\t\tctx.Values().Set(\"from\", \"/execute\") // values and session can be shared when calling Exec from a \"foreign\" context.\n\t\t// \tctx.Exec(\"NONE\", \"/invisible/iris\")\n\t\t// or after \"/change\":\n\t\tctx.Exec(\"GET\", \"/invisible/iris\")\n\t})\n\n\tapp.Listen(\":8080\", iris.WithDynamicHandler)\n}\n"
  },
  {
    "path": "_examples/routing/sitemap/main.go",
    "content": "package main\n\nimport (\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nconst startURL = \"http://localhost:8080\"\n\nfunc main() {\n\tapp := newApp()\n\n\t// http://localhost:8080/sitemap.xml\n\t// Lists only online GET static routes.\n\t//\n\t// Reference: https://www.sitemaps.org/protocol.html\n\tapp.Listen(\":8080\", iris.WithSitemap(startURL))\n}\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\tapp.Logger().SetLevel(\"debug\")\n\n\tlastModified, _ := time.Parse(\"2006-01-02T15:04:05-07:00\", \"2019-12-13T21:50:33+02:00\")\n\tapp.Get(\"/home\", handler).SetLastMod(lastModified).SetChangeFreq(\"hourly\").SetPriority(1)\n\tapp.Get(\"/articles\", handler).SetChangeFreq(\"daily\")\n\tapp.Get(\"/path1\", handler)\n\tapp.Get(\"/path2\", handler)\n\n\tapp.Post(\"/this-should-not-be-listed\", handler)\n\tapp.Get(\"/this/{myparam}/should/not/be/listed\", handler)\n\tapp.Get(\"/this-should-not-be-listed-offline\", handler).SetStatusOffline()\n\n\t// These should be excluded as well\n\tapp.Get(\"/about\", handler).ExcludeSitemap()\n\tapp.Get(\"/offline\", handler).SetStatusOffline()\n\n\treturn app\n}\n\nfunc handler(ctx iris.Context) { ctx.WriteString(ctx.Path()) }\n"
  },
  {
    "path": "_examples/routing/sitemap/main_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nfunc TestSitemap(t *testing.T) {\n\tconst expectedFullSitemapXML = `<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?><urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\"><url><loc>http://localhost:8080/home</loc><lastmod>2019-12-13T21:50:33+02:00</lastmod><changefreq>hourly</changefreq><priority>1</priority></url><url><loc>http://localhost:8080/articles</loc><changefreq>daily</changefreq></url><url><loc>http://localhost:8080/path1</loc></url><url><loc>http://localhost:8080/path2</loc></url></urlset>`\n\n\tapp := newApp()\n\tapp.Configure(iris.WithSitemap(startURL))\n\n\te := httptest.New(t, app)\n\te.GET(\"/sitemap.xml\").Expect().Status(httptest.StatusOK).Body().IsEqual(expectedFullSitemapXML)\n}\n"
  },
  {
    "path": "_examples/routing/subdomains/http-errors-view/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tnewApp().Listen(\"mydomain.com:80\", iris.WithLogLevel(\"debug\"))\n}\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\n\t// Create the \"test.mydomain.com\" subdomain.\n\ttest := app.Subdomain(\"test\")\n\t// Register views for the test subdomain.\n\ttest.RegisterView(iris.HTML(\"./views\", \".html\").\n\t\tLayout(\"layouts/test.layout.html\"))\n\n\t// Optionally, to minify the HTML5 error response.\n\t// Note that minification might be slower, caching is advised.\n\t// test.UseError(iris.Minify)\n\t// or pass it to OnErrorCode:\n\t// Register error code 404 handler.\n\ttest.OnErrorCode(iris.StatusNotFound, iris.Minify, handleNotFoundTestSubdomain)\n\n\ttest.Get(\"/\", testIndex)\n\n\treturn app\n}\n\nfunc handleNotFoundTestSubdomain(ctx iris.Context) {\n\tif err := ctx.View(\"error.html\", iris.Map{\n\t\t\"ErrorCode\": ctx.GetStatusCode(),\n\t}); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n}\n\nfunc testIndex(ctx iris.Context) {\n\tctx.Writef(\"%s index page\\n\", ctx.Subdomain())\n}\n"
  },
  {
    "path": "_examples/routing/subdomains/http-errors-view/main_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nfunc TestSubdomainsHTTPErrorsView(t *testing.T) {\n\tapp := newApp()\n\t// hard coded.\n\texpectedHTMLResponse := `<html>\n    <head>\n    <title>Test Subdomain</title>\n    \n    </head>\n    <body>\n        \n        <div style=\"background-color: black; color: red\">\n        <h1>Oups, you've got an error!</h1>\n        \n            \n            <div style=\"background-color: white; color: red\">\n        <h1>Not Found</h1>\n    </div>\n    \n        \n    </div>\n    \n    </body>\n    </html>`\n\n\te := httptest.New(t, app)\n\tgot := e.GET(\"/not_found\").WithURL(\"http://test.mydomain.com\").Expect().Status(httptest.StatusNotFound).\n\t\tContentType(\"text/html\", \"utf-8\").Body().Raw()\n\n\tif expected, _ := app.Minifier().String(\"text/html\", expectedHTMLResponse); expected != got {\n\t\tt.Fatalf(\"expected:\\n'%s'\\nbut got:\\n'%s'\", expected, got)\n\t}\n}\n"
  },
  {
    "path": "_examples/routing/subdomains/http-errors-view/views/error.html",
    "content": "<div style=\"background-color: black; color: red\">\n    <h1>Oups, you've got an error!</h1>\n    {{ if .ErrorCode }}\n        {{ $tmplName := print \"partials/\" .ErrorCode \".html\"}}\n        {{ render $tmplName . }}\n    {{ else }}\n        {{ render \"partials/500.html\" . }}\n    {{ end }}\n</div>\n"
  },
  {
    "path": "_examples/routing/subdomains/http-errors-view/views/index.html",
    "content": "<div style=\"background-color: black; color: blue\">\n\tIndex Page\n</div>\n"
  },
  {
    "path": "_examples/routing/subdomains/http-errors-view/views/layouts/layout.html",
    "content": "<html>\n<head>\n<title>Website Layout</title>\n\n</head>\n<body>\n\t<h1>This is the global layout</h1>\n\t<br />\n\t<!-- Render the current template here -->\n\t{{ yield . }}\n</body>\n</html>\n"
  },
  {
    "path": "_examples/routing/subdomains/http-errors-view/views/layouts/test.layout.html",
    "content": "<html>\n<head>\n<title>Test Subdomain</title>\n\n</head>\n<body>\n\t<!-- Render the current template here -->\n\t{{ yield . }}\n</body>\n</html>\n"
  },
  {
    "path": "_examples/routing/subdomains/http-errors-view/views/partials/404.html",
    "content": "<div style=\"background-color: white; color: red\">\n\t<h1>Not Found</h1>\n</div>\n"
  },
  {
    "path": "_examples/routing/subdomains/http-errors-view/views/partials/500.html",
    "content": "<div style=\"background-color: white; color: red\">\n\t<h1>Internal Server Error</h1>\n</div>\n"
  },
  {
    "path": "_examples/routing/subdomains/multi/hosts",
    "content": "# Copyright (c) 1993-2009 Microsoft Corp.\n#\n# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.\n#\n# This file contains the mappings of IP addresses to host names. Each\n# entry should be kept on an individual line. The IP address should\n# be placed in the first column followed by the corresponding host name.\n# The IP address and the host name should be separated by at least one\n# space.\n#\n# Additionally, comments (such as these) may be inserted on individual\n# lines or following the machine name denoted by a '#' symbol.\n#\n# For example:\n#\n#      102.54.94.97     rhino.acme.com          # source server\n#       38.25.63.10     x.acme.com              # x client host\n\n# localhost name resolution is handled within DNS itself.\n127.0.0.1       localhost\n::1             localhost\n#-iris-For development machine, you have to configure your dns also for online, search google how to do it if you don't know\n\n127.0.0.1 domain.local\n127.0.0.1 system.domain.local\n127.0.0.1 dashboard.domain.local\n\n#-END iris-\n"
  },
  {
    "path": "_examples/routing/subdomains/multi/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\t/*\n\t * Setup static files\n\t */\n\n\tapp.HandleDir(\"/assets\", iris.Dir(\"./public/assets\"))\n\tapp.HandleDir(\"/upload_resources\", iris.Dir(\"./public/upload_resources\"))\n\n\tdashboard := app.Party(\"dashboard.\")\n\t{\n\t\tdashboard.Get(\"/\", func(ctx iris.Context) {\n\t\t\tctx.Writef(\"HEY FROM dashboard\")\n\t\t})\n\t}\n\tsystem := app.Party(\"system.\")\n\t{\n\t\tsystem.Get(\"/\", func(ctx iris.Context) {\n\t\t\tctx.Writef(\"HEY FROM system\")\n\t\t})\n\t}\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.Writef(\"HEY FROM frontend /\")\n\t})\n\t// http://domain.local:80\n\t// http://dashboard.local\n\t// http://system.local\n\t// Make sure you prepend the \"http\" in your browser\n\t// because .local is a virtual domain we think to show case you\n\t// that you can declare any syntactical correct name as a subdomain in iris.\n\tapp.Listen(\"domain.local:80\") // for beginners: look ../hosts file\n}\n"
  },
  {
    "path": "_examples/routing/subdomains/redirect/hosts",
    "content": "127.0.0.1\tmydomain.com\n127.0.0.1\twww.mydomain.com\n127.0.0.1\totherdomain.com\n\n# Windows: Drive:/Windows/system32/drivers/etc/hosts, on Linux: /etc/hosts"
  },
  {
    "path": "_examples/routing/subdomains/redirect/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/middleware/rewrite\"\n)\n\nfunc main() {\n\tapp := newApp()\n\n\t// http://mydomain.com             -> http://www.mydomain.com\n\t// http://mydomain.com/user        -> http://www.mydomain.com/user\n\t// http://mydomain.com/user/login  -> http://www.mydomain.com/user/login\n\tapp.Listen(\":80\")\n}\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\tapp.Logger().SetLevel(\"debug\")\n\n\tstatic := app.Subdomain(\"static\")\n\tstatic.Get(\"/\", staticIndex)\n\n\tapp.Get(\"/\", index)\n\tuserRouter := app.Party(\"/user\")\n\tuserRouter.Get(\"/\", userGet)\n\tuserRouter.Get(\"/login\", userGetLogin)\n\n\t// redirects := rewrite.Load(\"redirects.yml\")\n\t// ^ see _examples/routing/rewrite example for that.\n\t//\n\t// Now let's do that by code.\n\trewriteEngine, _ := rewrite.New(rewrite.Options{\n\t\tPrimarySubdomain: \"www\",\n\t})\n\t// Enable this line for debugging:\n\t// rewriteEngine.SetLogger(app.Logger())\n\tapp.WrapRouter(rewriteEngine.Rewrite)\n\n\treturn app\n}\n\nfunc staticIndex(ctx iris.Context) {\n\tctx.Writef(\"This is the static.mydomain.com index.\")\n}\n\nfunc index(ctx iris.Context) {\n\tctx.Writef(\"This is the www.mydomain.com index.\")\n}\n\nfunc userGet(ctx iris.Context) {\n\t// Also, ctx.Subdomain(), ctx.SubdomainFull(), ctx.Host() and ctx.Path()\n\t// can be helpful when working with subdomains.\n\tctx.Writef(\"This is the www.mydomain.com/user endpoint.\")\n}\n\nfunc userGetLogin(ctx iris.Context) {\n\tctx.Writef(\"This is the www.mydomain.com/user/login endpoint.\")\n}\n"
  },
  {
    "path": "_examples/routing/subdomains/redirect/multi-instances/go.mod",
    "content": "module github.com/kataras/iris/_examples/routing/subdomains/redirect/multi-instances\n\ngo 1.25\n\nrequire github.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd\n\nrequire (\n\tgithub.com/BurntSushi/toml v1.6.0 // indirect\n\tgithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect\n\tgithub.com/CloudyKit/jet/v6 v6.3.1 // indirect\n\tgithub.com/Joker/jade v1.1.3 // indirect\n\tgithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 // indirect\n\tgithub.com/andybalholm/brotli v1.2.0 // indirect\n\tgithub.com/aymerick/douceur v0.2.0 // indirect\n\tgithub.com/fatih/structs v1.1.0 // indirect\n\tgithub.com/flosch/pongo2/v4 v4.0.2 // indirect\n\tgithub.com/golang/snappy v1.0.0 // indirect\n\tgithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a // indirect\n\tgithub.com/google/uuid v1.6.0 // indirect\n\tgithub.com/gorilla/css v1.0.1 // indirect\n\tgithub.com/iris-contrib/schema v0.0.6 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/kataras/blocks v0.0.8 // indirect\n\tgithub.com/kataras/golog v0.1.12 // indirect\n\tgithub.com/kataras/pio v0.0.13 // indirect\n\tgithub.com/kataras/sitemap v0.0.6 // indirect\n\tgithub.com/kataras/tunnel v0.0.4 // indirect\n\tgithub.com/klauspost/compress v1.18.2 // indirect\n\tgithub.com/kr/pretty v0.3.1 // indirect\n\tgithub.com/mailgun/raymond/v2 v2.0.48 // indirect\n\tgithub.com/mailru/easyjson v0.9.1 // indirect\n\tgithub.com/microcosm-cc/bluemonday v1.0.27 // indirect\n\tgithub.com/rogpeppe/go-internal v1.10.0 // indirect\n\tgithub.com/russross/blackfriday/v2 v2.1.0 // indirect\n\tgithub.com/schollz/closestmatch v2.1.0+incompatible // indirect\n\tgithub.com/sirupsen/logrus v1.8.1 // indirect\n\tgithub.com/stretchr/testify v1.11.1 // indirect\n\tgithub.com/tdewolff/minify/v2 v2.24.8 // indirect\n\tgithub.com/tdewolff/parse/v2 v2.8.5 // indirect\n\tgithub.com/valyala/bytebufferpool v1.0.0 // indirect\n\tgithub.com/vmihailenco/msgpack/v5 v5.4.1 // indirect\n\tgithub.com/vmihailenco/tagparser/v2 v2.0.0 // indirect\n\tgithub.com/yosssi/ace v0.0.5 // indirect\n\tgolang.org/x/crypto v0.47.0 // indirect\n\tgolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 // indirect\n\tgolang.org/x/net v0.49.0 // indirect\n\tgolang.org/x/sys v0.40.0 // indirect\n\tgolang.org/x/text v0.33.0 // indirect\n\tgolang.org/x/time v0.14.0 // indirect\n\tgoogle.golang.org/protobuf v1.36.11 // indirect\n\tgopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect\n\tgopkg.in/ini.v1 v1.67.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n"
  },
  {
    "path": "_examples/routing/subdomains/redirect/multi-instances/go.sum",
    "content": "github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=\ngithub.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=\ngithub.com/CloudyKit/jet/v6 v6.3.1 h1:6IAo5Cx21xrHVaR8zzXN5gJatKV/wO7Nf6bfCnCSbUw=\ngithub.com/CloudyKit/jet/v6 v6.3.1/go.mod h1:lf8ksdNsxZt7/yH/3n4vJQWA9RUq4wpaHtArHhGVMOw=\ngithub.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc=\ngithub.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=\ngithub.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=\ngithub.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 h1:dG7/gLroJGht/jSQtHiLvT48Hxn+crbmvyItZC8cWOs=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05/go.mod h1:NYezi6wtnJtBm5btoprXc5SvAdqH0XTXWnUup0MptAI=\ngithub.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=\ngithub.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=\ngithub.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=\ngithub.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=\ngithub.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=\ngithub.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=\ngithub.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=\ngithub.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=\ngithub.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=\ngithub.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw=\ngithub.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8=\ngithub.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=\ngithub.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=\ngithub.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=\ngithub.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a h1:l7A0loSszR5zHd/qK53ZIHMO8b3bBSmENnQ6eKnUT0A=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=\ngithub.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=\ngithub.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=\ngithub.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=\ngithub.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=\ngithub.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2 h1:T9THsdP1woyAqKHwjkEsbCnMefsAFvk8iJJKokcJ3Go=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2/go.mod h1:JLDgIqnFy5loDSUv1OA2j0mb6p/rDhiCqigP22Uq9xE=\ngithub.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw=\ngithub.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/kataras/blocks v0.0.8 h1:MrpVhoFTCR2v1iOOfGng5VJSILKeZZI+7NGfxEh3SUM=\ngithub.com/kataras/blocks v0.0.8/go.mod h1:9Jm5zx6BB+06NwA+OhTbHW1xkMOYxahnqTN5DveZ2Yg=\ngithub.com/kataras/golog v0.1.12 h1:Bu7I/G4ilJlbfzjmU39O9N+2uO1pBcMK045fzZ4ytNg=\ngithub.com/kataras/golog v0.1.12/go.mod h1:wrGSbOiBqbQSQznleVNX4epWM8rl9SJ/rmEacl0yqy4=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd h1:oMsQgqKACbUdlaIWnN+EZzzAnHkw90hE/y/R0BSij2U=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd/go.mod h1:yucjtDl9Vn3OctWM1fi0MuM9OckemC7kEreFa9QtPF8=\ngithub.com/kataras/pio v0.0.13 h1:x0rXVX0fviDTXOOLOmr4MUxOabu1InVSTu5itF8CXCM=\ngithub.com/kataras/pio v0.0.13/go.mod h1:k3HNuSw+eJ8Pm2lA4lRhg3DiCjVgHlP8hmXApSej3oM=\ngithub.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY=\ngithub.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4=\ngithub.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA=\ngithub.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw=\ngithub.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=\ngithub.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=\ngithub.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=\ngithub.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8=\ngithub.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=\ngithub.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=\ngithub.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=\ngithub.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=\ngithub.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=\ngithub.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=\ngithub.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=\ngithub.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=\ngithub.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=\ngithub.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=\ngithub.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=\ngithub.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=\ngithub.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk=\ngithub.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=\ngithub.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=\ngithub.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=\ngithub.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=\ngithub.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/tdewolff/minify/v2 v2.24.8 h1:58/VjsbevI4d5FGV0ZSuBrHMSSkH4MCH0sIz/eKIauE=\ngithub.com/tdewolff/minify/v2 v2.24.8/go.mod h1:0Ukj0CRpo/sW/nd8uZ4ccXaV1rEVIWA3dj8U7+Shhfw=\ngithub.com/tdewolff/parse/v2 v2.8.5 h1:ZmBiA/8Do5Rpk7bDye0jbbDUpXXbCdc3iah4VeUvwYU=\ngithub.com/tdewolff/parse/v2 v2.8.5/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=\ngithub.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE=\ngithub.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=\ngithub.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=\ngithub.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=\ngithub.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=\ngithub.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=\ngithub.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=\ngithub.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=\ngithub.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA=\ngithub.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0=\ngithub.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=\ngithub.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=\ngithub.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=\ngolang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=\ngolang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=\ngolang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=\ngolang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=\ngolang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=\ngolang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=\ngolang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=\ngolang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=\ngoogle.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=\ngopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nmoul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs=\nmoul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE=\n"
  },
  {
    "path": "_examples/routing/subdomains/redirect/multi-instances/main.go",
    "content": "package main\n\nimport (\n\t_ \"github.com/kataras/iris/_examples/routing/subdomains/redirect/multi-instances/other\"\n\t_ \"github.com/kataras/iris/_examples/routing/subdomains/redirect/multi-instances/root\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/apps\"\n)\n\n// In this example, you wanna use three different applications exposed as one.\n// The first one is the \"other\" package, the second is the \"root\",\n// the third is the switcher application which will expose the above.\n// Unlike the previous example, on this one we will NOT redirect,\n// the Hosts switcher will just pass the request to the matched Application to handle.\n// This is NOT an alternative of your favorite load balancer.\n// Read the comments carefully, if you need more information\n// you can head over to the \"apps\" package's godocs and tests.\nfunc main() {\n\t// The `apps.Hosts` switch provider:\n\t// The pattern. A regexp for matching the host part of incoming requests.\n\t// The target. An iris.Application instance (created by iris.New())\n\t//      OR\n\t// You can use the Application's name (app.SetName(\"myapp\")).\n\t// Example:\n\t// package rootdomain\n\t// func init() {\n\t//  app := iris.New().SetName(\"root app\")\n\t//  ...\n\t// }\n\t// On the main package add an empty import statement: ` _ import \"rootdomain\"`\n\t// And set the \"root app\" as the key to reference that application (of the same program).\n\t// Thats the target we wanna use now ^ (see ../hosts file).\n\t//      OR\n\t// An external host or a local running in the same machine but different port or host behind proxy.\n\tswitcher := apps.Switch(apps.Hosts{\n\t\t{\"^(www.)?mydomain.com$\", \"root app\"},\n\t\t{\"^otherdomain.com$\", \"other app\"},\n\t})\n\t// The registration order matters, so we can register a fallback server (when host no match)\n\t// using \"*\". However, you have alternatives by using the Switch Iris Application value\n\t// (let's call it \"switcher\"):\n\t// 1. Handle the not founds, e.g. switcher.OnErrorCode(404, ...)\n\t// 2. Use the switcher.WrapRouter, e.g. to log the flow of a request of all hosts exposed.\n\t// 3. Just register routes to match, e.g. switcher.Get(\"/\", ...)\n\tswitcher.Get(\"/\", fallback)\n\t// OR\n\t// Change the response code to 502\n\t// instead of 404 and write a message:\n\t// switcher.OnErrorCode(iris.StatusNotFound, fallback)\n\n\t// The switcher is a common Iris Application, so you have access to the Iris features.\n\t// And it should be listening to a host:port in order to match and serve its apps.\n\t//\n\t// http://mydomain.com (OK)\n\t// http://www.mydomain.com (OK)\n\t// http://mydomain.com/dsa (404)\n\t// http://no.mydomain.com (502 Bad Host)\n\t//\n\t// http://otherdomain.com (OK)\n\t// http://www.otherdomain.com (502 Bad Host)\n\t// http://otherdomain.com/dsa (404 JSON)\n\t// ...\n\tswitcher.Listen(\":80\")\n}\n\nfunc fallback(ctx iris.Context) {\n\tctx.StatusCode(iris.StatusBadGateway)\n\tctx.Writef(\"Bad Host %s\", ctx.Host())\n}\n"
  },
  {
    "path": "_examples/routing/subdomains/redirect/multi-instances/other/server.go",
    "content": "package other\n\nimport (\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc init() {\n\tapp := iris.New()\n\tapp.SetName(\"other app\")\n\n\tapp.OnAnyErrorCode(handleErrors)\n\tapp.Get(\"/\", index)\n}\n\nfunc index(ctx iris.Context) {\n\tctx.HTML(\"Other Index (App Name: <b>%s</b> | Host: <b>%s</b>)\",\n\t\tctx.Application().String(), ctx.Host())\n}\n\nfunc handleErrors(ctx iris.Context) {\n\terrCode := ctx.GetStatusCode()\n\tctx.JSON(iris.Map{\n\t\t\"Server\":    ctx.Application().String(),\n\t\t\"Code\":      errCode,\n\t\t\"Message\":   iris.StatusText(errCode),\n\t\t\"Timestamp\": time.Now().Unix(),\n\t})\n}\n"
  },
  {
    "path": "_examples/routing/subdomains/redirect/multi-instances/root/server.go",
    "content": "package root\n\nimport \"github.com/kataras/iris/v12\"\n\nfunc init() {\n\tapp := iris.New()\n\tapp.SetName(\"root app\")\n\n\tapp.Get(\"/\", index)\n}\n\nfunc index(ctx iris.Context) {\n\tctx.HTML(\"Main Root Index (App Name: <b>%s</b> | Host: <b>%s</b>)\",\n\t\tctx.Application().String(), ctx.Host())\n}\n"
  },
  {
    "path": "_examples/routing/subdomains/single/hosts",
    "content": "# Copyright (c) 1993-2009 Microsoft Corp.\n#\n# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.\n#\n# This file contains the mappings of IP addresses to host names. Each\n# entry should be kept on an individual line. The IP address should\n# be placed in the first column followed by the corresponding host name.\n# The IP address and the host name should be separated by at least one\n# space.\n#\n# Additionally, comments (such as these) may be inserted on individual\n# lines or following the machine name denoted by a '#' symbol.\n#\n# For example:\n#\n#      102.54.94.97     rhino.acme.com          # source server\n#       38.25.63.10     x.acme.com              # x client host\n\n# localhost name resolution is handled within DNS itself.\n127.0.0.1       localhost\n::1             localhost\n#-iris-For development machine, you have to configure your dns also for online, search google how to do it if you don't know\n\n127.0.0.1\t\tmydomain.com\n127.0.0.1\t\tadmin.mydomain.com\n\n#-END iris-\n"
  },
  {
    "path": "_examples/routing/subdomains/single/main.go",
    "content": "// Package main register static subdomains, simple as parties, check ./hosts if you use windows\npackage main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\t// Subdomain method is just another Party.\n\tadmin := app.Subdomain(\"admin\")\n\t{\n\t\t// admin.mydomain.com\n\t\tadmin.Get(\"/\", func(c iris.Context) {\n\t\t\tc.Writef(\"INDEX FROM admin.mydomain.com\")\n\t\t})\n\t\t// admin.mydomain.com/hey\n\t\tadmin.Get(\"/hey\", func(c iris.Context) {\n\t\t\tc.Writef(\"HEY FROM admin.mydomain.com/hey\")\n\t\t})\n\t\t// admin.mydomain.com/hey2\n\t\tadmin.Get(\"/hey2\", func(c iris.Context) {\n\t\t\tc.Writef(\"HEY SECOND FROM admin.mydomain.com/hey\")\n\t\t})\n\t}\n\n\t// mydomain.com\n\tapp.Get(\"/\", func(c iris.Context) {\n\t\tc.Writef(\"INDEX FROM no-subdomain hey\")\n\t})\n\n\t// mydomain.com/hey\n\tapp.Get(\"/hey\", func(c iris.Context) {\n\t\tc.Writef(\"HEY FROM no-subdomain hey\")\n\t})\n\n\t// http://admin.mydomain.com\n\t// http://admin.mydomain.com/hey\n\t// http://admin.mydomain.com/hey2\n\t// http://mydomain.com\n\t// http://mydomain.com/hey\n\tapp.Listen(\"mydomain.com:80\") // for beginners: look ../hosts file\n}\n"
  },
  {
    "path": "_examples/routing/subdomains/wildcard/hosts",
    "content": "# Copyright (c) 1993-2009 Microsoft Corp.\n#\n# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.\n#\n# This file contains the mappings of IP addresses to host names. Each\n# entry should be kept on an individual line. The IP address should\n# be placed in the first column followed by the corresponding host name.\n# The IP address and the host name should be separated by at least one\n# space.\n#\n# Additionally, comments (such as these) may be inserted on individual\n# lines or following the machine name denoted by a '#' symbol.\n#\n# For example:\n#\n#      102.54.94.97     rhino.acme.com          # source server\n#       38.25.63.10     x.acme.com              # x client host\n\n# localhost name resolution is handled within DNS itself.\n127.0.0.1       localhost\n::1             localhost\n#-iris-For development machine, you have to configure your dns also for online, search google how to do it if you don't know\n127.0.0.1\t\tmydomain.com\n127.0.0.1\t\tusername1.mydomain.com\n127.0.0.1\t\tusername2.mydomain.com\n127.0.0.1\t\tusername3.mydomain.com\n127.0.0.1\t\tusername4.mydomain.com\n127.0.0.1\t\tusername5.mydomain.com\n127.0.0.1\t\ten-us.test.mydomain.com\n#-END iris-\n"
  },
  {
    "path": "_examples/routing/subdomains/wildcard/main.go",
    "content": "// Package main an example on how to catch dynamic subdomains - wildcard.\n// On the first example we learnt how to create routes for static subdomains, subdomains you know that you will have.\n// Here we will see an example how to catch unknown subdomains, dynamic subdomains, like username.mydomain.com:8080.\npackage main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n)\n\n// register a dynamic-wildcard subdomain to your server machine(dns/...) first, check ./hosts if you use windows.\n// run this file and try to redirect: http://username1.mydomain.com:8080/ , http://username2.mydomain.com:8080/ , http://username1.mydomain.com/something, http://username1.mydomain.com/something/sadsadsa\n\nfunc main() {\n\tapp := iris.New()\n\n\t/* Keep note that you can use both type of subdomains (named and wildcard(*.) )\n\t   admin.mydomain.com,  and for other the Party(*.) but this is not this example's purpose\n\n\tadmin := app.Party(\"admin.\")\n\t{\n\t\t// admin.mydomain.com\n\t\tadmin.Get(\"/\", func(ctx iris.Context) {\n\t\t\tctx.Writef(\"INDEX FROM admin.mydomain.com\")\n\t\t})\n\t\t// admin.mydomain.com/hey\n\t\tadmin.Get(\"/hey\", func(ctx iris.Context) {\n\t\t\tctx.Writef(\"HEY FROM admin.mydomain.com/hey\")\n\t\t})\n\t\t// admin.mydomain.com/hey2\n\t\tadmin.Get(\"/hey2\", func(ctx iris.Context) {\n\t\t\tctx.Writef(\"HEY SECOND FROM admin.mydomain.com/hey\")\n\t\t})\n\t}*/\n\n\t// no order, you can register subdomains at the end also.\n\tdynamicSubdomains := app.WildcardSubdomain()\n\t{\n\t\tdynamicSubdomains.Get(\"/\", dynamicSubdomainHandler)\n\n\t\tdynamicSubdomains.Get(\"/something\", dynamicSubdomainHandler)\n\n\t\tdynamicSubdomains.Get(\"/something/{paramfirst}\", dynamicSubdomainHandlerWithParam)\n\t}\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.Writef(\"Hello from mydomain.com path: %s\", ctx.Path())\n\t})\n\n\tapp.Get(\"/hello\", func(ctx iris.Context) {\n\t\tctx.Writef(\"Hello from mydomain.com path: %s\", ctx.Path())\n\t})\n\n\t// http://mydomain.com:8080\n\t// http://username1.mydomain.com:8080\n\t// http://username2.mydomain.com:8080/something\n\t// http://username3.mydomain.com:8080/something/yourname\n\t// http://en-us.test.mydomain.com:8080/something/42\n\tapp.Listen(\"mydomain.com:8080\") // for beginners: look ../hosts file\n}\n\nfunc dynamicSubdomainHandler(ctx iris.Context) {\n\tusername := ctx.Subdomain()\n\tctx.Writef(\"Hello from dynamic subdomain path: %s, here you can handle the route for dynamic subdomains, handle the user: %s\", ctx.Path(), username)\n\t// if  http://username4.mydomain.com:8080/ prints:\n\t// Hello from dynamic subdomain path: /, here you can handle the route for dynamic subdomains, handle the user: username4\n}\n\nfunc dynamicSubdomainHandlerWithParam(ctx iris.Context) {\n\tusername := ctx.Subdomain()\n\tctx.Writef(\"Hello from dynamic (full) subdomain: %s and path: %s, here you can handle the route for dynamic subdomains, handle the user: %s\", ctx.SubdomainFull(), ctx.Path(), username)\n\tctx.Writef(\"The paramfirst is: %s\", ctx.Params().Get(\"paramfirst\"))\n}\n"
  },
  {
    "path": "_examples/routing/subdomains/www/hosts",
    "content": "# Copyright (c) 1993-2009 Microsoft Corp.\n#\n# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.\n#\n# This file contains the mappings of IP addresses to host names. Each\n# entry should be kept on an individual line. The IP address should\n# be placed in the first column followed by the corresponding host name.\n# The IP address and the host name should be separated by at least one\n# space.\n#\n# Additionally, comments (such as these) may be inserted on individual\n# lines or following the machine name denoted by a '#' symbol.\n#\n# For example:\n#\n#      102.54.94.97     rhino.acme.com          # source server\n#       38.25.63.10     x.acme.com              # x client host\n\n# localhost name resolution is handled within DNS itself.\n127.0.0.1       localhost\n::1             localhost\n#-iris-For development machine, you have to configure your dns also for online, search google how to do it if you don't know\n127.0.0.1\t\tmydomain.com\n127.0.0.1\t\twww.mydomain.com\n#-END iris-\n"
  },
  {
    "path": "_examples/routing/subdomains/www/main.go",
    "content": "package main\n\nimport \"github.com/kataras/iris/v12\"\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\n\tapp.Get(\"/\", info)\n\tapp.Get(\"/about\", info)\n\tapp.Get(\"/contact\", info)\n\n\tapp.PartyFunc(\"/api/users\", func(r iris.Party) {\n\t\tr.Get(\"/\", info)\n\t\tr.Get(\"/{id:uint64}\", info)\n\n\t\tr.Post(\"/\", info)\n\n\t\tr.Put(\"/{id:uint64}\", info)\n\t}) /* <- same as:\n\t usersAPI := app.Party(\"/api/users\")\n\t {  // those brackets are just syntactic-sugar things.\n\t\t// This method is rarely used but you can make use of it when you want\n\t    // scoped variables to that code block only.\n\t\tusersAPI.Get/Post...\n\t }\n\t usersAPI.Get/Post...\n\t*/\n\n\twww := app.Party(\"www.\")\n\t{\n\t\t// Just to show how you can get all routes and copy them to another\n\t\t// party or subdomain:\n\t\t// Get all routes that are registered so far, including all \"Parties\" and subdomains:\n\t\tcurrentRoutes := app.GetRoutes()\n\t\t// Register them to the www subdomain/VHost as well:\n\t\tfor _, r := range currentRoutes {\n\t\t\twww.Handle(r.Method, r.Tmpl().Src, r.Handlers...)\n\t\t}\n\n\t\t// http://www.mydomain.com/hi\n\t\twww.Get(\"/hi\", func(ctx iris.Context) {\n\t\t\tctx.Writef(\"hi from www.mydomain.com\")\n\t\t})\n\t}\n\t// See \"subdomains/redirect\" to register redirect router wrappers between subdomains,\n\t// i.e mydomain.com to www.mydomain.com (like facebook does for SEO reasons(;)).\n\t// And ./www-method example.\n\treturn app\n}\n\nfunc main() {\n\tapp := newApp()\n\t// http://mydomain.com\n\t// http://mydomain.com/about\n\t// http://imydomain.com/contact\n\t// http://mydomain.com/api/users\n\t// http://mydomain.com/api/users/42\n\n\t// http://www.mydomain.com\n\t// http://www.mydomain.com/hi\n\t// http://www.mydomain.com/about\n\t// http://www.mydomain.com/contact\n\t// http://www.mydomain.com/api/users\n\t// http://www.mydomain.com/api/users/42\n\tapp.Listen(\"mydomain.com:80\")\n}\n\nfunc info(ctx iris.Context) {\n\tmethod := ctx.Method()\n\tsubdomain := ctx.Subdomain()\n\tpath := ctx.Path()\n\n\tctx.Writef(\"\\nInfo\\n\\n\")\n\tctx.Writef(\"Method: %s\\nSubdomain: %s\\nPath: %s\", method, subdomain, path)\n}\n"
  },
  {
    "path": "_examples/routing/subdomains/www/main_test.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\ntype testRoute struct {\n\tpath      string\n\tmethod    string\n\tsubdomain string\n}\n\nfunc (r testRoute) response() string {\n\tmsg := fmt.Sprintf(\"\\nInfo\\n\\nMethod: %s\\nSubdomain: %s\\nPath: %s\", r.method, r.subdomain, r.path)\n\treturn msg\n}\n\nfunc TestSubdomainWWW(t *testing.T) {\n\tapp := newApp()\n\n\ttests := []testRoute{\n\t\t// host\n\t\t{\"/\", \"GET\", \"\"},\n\t\t{\"/about\", \"GET\", \"\"},\n\t\t{\"/contact\", \"GET\", \"\"},\n\t\t{\"/api/users\", \"GET\", \"\"},\n\t\t{\"/api/users/42\", \"GET\", \"\"},\n\t\t{\"/api/users\", \"POST\", \"\"},\n\t\t{\"/api/users/42\", \"PUT\", \"\"},\n\t\t// www sub domain\n\t\t{\"/\", \"GET\", \"www\"},\n\t\t{\"/about\", \"GET\", \"www\"},\n\t\t{\"/contact\", \"GET\", \"www\"},\n\t\t{\"/api/users\", \"GET\", \"www\"},\n\t\t{\"/api/users/42\", \"GET\", \"www\"},\n\t\t{\"/api/users\", \"POST\", \"www\"},\n\t\t{\"/api/users/42\", \"PUT\", \"www\"},\n\t}\n\n\thost := \"localhost:1111\"\n\te := httptest.New(t, app, httptest.Debug(false))\n\n\tfor _, test := range tests {\n\n\t\treq := e.Request(test.method, test.path)\n\t\tif subdomain := test.subdomain; subdomain != \"\" {\n\t\t\treq.WithURL(\"http://\" + subdomain + \".\" + host)\n\t\t}\n\n\t\treq.Expect().\n\t\t\tStatus(httptest.StatusOK).\n\t\t\tBody().IsEqual(test.response())\n\t}\n}\n"
  },
  {
    "path": "_examples/routing/subdomains/www/www-method/main.go",
    "content": "package main\n\nimport \"github.com/kataras/iris/v12\"\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\t// This will create a new \"www\" subdomain\n\t// and redirect root-level domain requests\n\t// to that one:\n\twww := app.WWW()\n\twww.Get(\"/\", info)\n\twww.Get(\"/about\", info)\n\twww.Get(\"/contact\", info)\n\n\twww.PartyFunc(\"/api/users\", func(r iris.Party) {\n\t\tr.Get(\"/\", info)\n\t\tr.Get(\"/{id:uint64}\", info)\n\n\t\tr.Post(\"/\", info)\n\n\t\tr.Put(\"/{id:uint64}\", info)\n\t})\n\n\treturn app\n}\n\nfunc main() {\n\tapp := newApp()\n\t// http://mydomain.com\n\t// http://mydomain.com/about\n\t// http://imydomain.com/contact\n\t// http://mydomain.com/api/users\n\t// http://mydomain.com/api/users/42\n\n\t// http://www.mydomain.com\n\t// http://www.mydomain.com/hi\n\t// http://www.mydomain.com/about\n\t// http://www.mydomain.com/contact\n\t// http://www.mydomain.com/api/users\n\t// http://www.mydomain.com/api/users/42\n\tapp.Listen(\"mydomain.com:80\")\n}\n\nfunc info(ctx iris.Context) {\n\tmethod := ctx.Method()\n\tsubdomain := ctx.Subdomain()\n\tpath := ctx.Path()\n\n\tctx.Writef(\"\\nInfo\\n\\n\")\n\tctx.Writef(\"Method: %s\\nSubdomain: %s\\nPath: %s\", method, subdomain, path)\n}\n"
  },
  {
    "path": "_examples/routing/versioning/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/versioning\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\tapp.OnErrorCode(iris.StatusNotFound, func(ctx iris.Context) {\n\t\tctx.WriteString(`Root not found handler.\n        This will be applied everywhere except the /api/* requests.`)\n\t})\n\n\tapi := app.Party(\"/api\")\n\t// Optional, set version aliases (literal strings).\n\t// We use `UseRouter` instead of `Use`\n\t// to handle HTTP errors per version, but it's up to you.\n\tapi.UseRouter(versioning.Aliases(versioning.AliasMap{\n\t\t// If no version provided by the client, default it to the \"1.0.0\".\n\t\tversioning.Empty: \"1.0.0\",\n\t\t// If a \"latest\" version is provided by the client,\n\t\t// set the version to be compared to \"3.0.0\".\n\t\t\"latest\": \"3.0.0\",\n\t}))\n\n\t/*\n\t   A version is extracted through the versioning.GetVersion function,\n\t   request headers:\n\t   - Accept-Version: 1.0.0\n\t   - Accept: application/json; version=1.0.0\n\t   You can customize it by setting a version based on the request context:\n\t   api.Use(func(ctx *context.Context) {\n\t       if version := ctx.URLParam(\"version\"); version != \"\" {\n\t           versioning.SetVersion(ctx, version)\n\t       }\n\n\t       ctx.Next()\n\t   })\n\t   OR: api.Use(versioning.FromQuery(\"version\", \"\"))\n\t*/\n\n\t// |----------------|\n\t// | The fun begins |\n\t// |----------------|\n\n\t// Create a new Group, which is a compatible Party,\n\t// based on version constraints.\n\tv1 := versioning.NewGroup(api, \">=1.0.0 <2.0.0\")\n\t// To mark an API version as deprecated use the Deprecated method.\n\t// v1.Deprecated(versioning.DefaultDeprecationOptions)\n\n\t// Optionally, set custom view engine and path\n\t// for templates based on the version.\n\tv1.RegisterView(iris.HTML(\"./v1\", \".html\"))\n\n\t// Optionally, set custom error handler(s) based on the version.\n\t// Keep in mind that if you do this, you will\n\t// have to register error handlers\n\t// for the rest of the parties as well.\n\tv1.OnErrorCode(iris.StatusNotFound, testError(\"v1\"))\n\n\t// Register resources based on the version.\n\tv1.Get(\"/\", testHandler(\"v1\"))\n\tv1.Get(\"/render\", testView)\n\n\t// Do the same for version 2 and version 3,\n\t// for the sake of the example.\n\tv2 := versioning.NewGroup(api, \">=2.0.0 <3.0.0\")\n\tv2.RegisterView(iris.HTML(\"./v2\", \".html\"))\n\tv2.OnErrorCode(iris.StatusNotFound, testError(\"v2\"))\n\tv2.Get(\"/\", testHandler(\"v2\"))\n\tv2.Get(\"/render\", testView)\n\n\tv3 := versioning.NewGroup(api, \">=3.0.0 <4.0.0\")\n\tv3.RegisterView(iris.HTML(\"./v3\", \".html\"))\n\tv3.OnErrorCode(iris.StatusNotFound, testError(\"v3\"))\n\tv3.Get(\"/\", testHandler(\"v3\"))\n\tv3.Get(\"/render\", testView)\n\n\tapp.Listen(\":8080\")\n}\n\nfunc testHandler(v string) iris.Handler {\n\treturn func(ctx iris.Context) {\n\t\tctx.JSON(iris.Map{\n\t\t\t\"version\": v,\n\t\t\t\"message\": \"Hello, world!\",\n\t\t})\n\t}\n}\n\nfunc testError(v string) iris.Handler {\n\treturn func(ctx iris.Context) {\n\t\tctx.Writef(\"not found: %s\", v)\n\t}\n}\n\nfunc testView(ctx iris.Context) {\n\tif err := ctx.View(\"index.html\"); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "_examples/routing/versioning/v1/index.html",
    "content": "<h1>This is the directory for version 1 templates</h1>"
  },
  {
    "path": "_examples/routing/versioning/v2/index.html",
    "content": "<h1>This is the directory for version 2 templates</h1>"
  },
  {
    "path": "_examples/routing/versioning/v3/index.html",
    "content": "<h1>This is the directory for version 3 templates</h1>"
  },
  {
    "path": "_examples/routing/writing-a-middleware/globally/main.go",
    "content": "package main\n\nimport \"github.com/kataras/iris/v12\"\n\nfunc main() {\n\tapp := iris.New()\n\t// register the \"before\" handler as the first handler which will be executed\n\t// on all domain's routes.\n\t// Or use the `UseGlobal` to register a middleware which will fire across subdomains.\n\t// app.Use(before)\n\t// register the \"after\" handler as the last handler which will be executed\n\t// after all domain's routes' handler(s).\n\t//\n\t// Or use the `DoneGlobal` to append handlers that will be fired globally.\n\t// app.Done(after)\n\n\t// register our routes.\n\tapp.Get(\"/\", indexHandler)\n\tapp.Get(\"/contact\", contactHandler)\n\n\t// Order of those calls doesn't matter, `UseGlobal` and `DoneGlobal`\n\t// are applied to existing routes and future routes.\n\t//\n\t// Remember: the `Use` and `Done` are applied to the current party's and its children,\n\t// so if we used the `app.Use/Done` before the routes registration\n\t// it would work like UseGlobal/DoneGlobal in this case, because the `app` is the root party.\n\t//\n\t// See `app.Party/PartyFunc` for more.\n\tapp.UseGlobal(before)\n\tapp.DoneGlobal(after)\n\n\tapp.Listen(\":8080\")\n}\n\nfunc before(ctx iris.Context) {\n\tshareInformation := \"this is a sharable information between handlers\"\n\n\trequestPath := ctx.Path()\n\tprintln(\"Before the indexHandler or contactHandler: \" + requestPath)\n\n\tctx.Values().Set(\"info\", shareInformation)\n\tctx.Next()\n}\n\nfunc after(ctx iris.Context) {\n\tprintln(\"After the indexHandler or contactHandler\")\n}\n\nfunc indexHandler(ctx iris.Context) {\n\tprintln(\"Inside indexHandler\")\n\n\t// take the info from the \"before\" handler.\n\tinfo := ctx.Values().GetString(\"info\")\n\n\t// write something to the client as a response.\n\tctx.HTML(\"<h1>Response</h1>\")\n\tctx.HTML(\"<br/> Info: \" + info)\n\n\tctx.Next() // execute the \"after\" handler registered via `DoneGlobal`.\n}\n\nfunc contactHandler(ctx iris.Context) {\n\tprintln(\"Inside contactHandler\")\n\n\t// write something to the client as a response.\n\tctx.HTML(\"<h1>Contact</h1>\")\n\n\tctx.Next() // execute the \"after\" handler registered via `DoneGlobal`.\n}\n"
  },
  {
    "path": "_examples/routing/writing-a-middleware/per-route/main.go",
    "content": "package main\n\nimport \"github.com/kataras/iris/v12\"\n\nfunc main() {\n\tapp := iris.New()\n\t// or app.Use(before) and app.Done(after).\n\tapp.Get(\"/\", before, mainHandler, after)\n\n\t// Use registers a middleware(prepend handlers) to all party's, and its children that will be registered\n\t// after.\n\t//\n\t// (`app` is the root children so those use and done handlers will be registered everywhere)\n\tapp.Use(func(ctx iris.Context) {\n\t\tprintln(`before the party's routes and its children,\nbut this is not applied to the '/' route\nbecause it's registered before the middleware, order matters.`)\n\t\tctx.Next()\n\t})\n\n\tapp.Done(func(ctx iris.Context) {\n\t\tprintln(\"this is executed always last, if the previous handler calls the `ctx.Next()`, it's the reverse of `.Use`\")\n\t\tmessage := ctx.Values().GetString(\"message\")\n\t\tprintln(\"message: \" + message)\n\t})\n\n\tapp.Get(\"/home\", func(ctx iris.Context) {\n\t\tctx.HTML(\"<h1> Home </h1>\")\n\t\tctx.Values().Set(\"message\", \"this is the home message, ip: \"+ctx.RemoteAddr())\n\t\tctx.Next() // call the done handlers.\n\t})\n\n\tchild := app.Party(\"/child\")\n\tchild.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.Writef(`this is the localhost:8080/child route.\nAll Use and Done handlers that are registered to the parent party,\nare applied here as well.`)\n\t\tctx.Next() // call the done handlers.\n\t})\n\n\tapp.Listen(\":8080\")\n}\n\nfunc before(ctx iris.Context) {\n\tshareInformation := \"this is a sharable information between handlers\"\n\n\trequestPath := ctx.Path()\n\tprintln(\"Before the mainHandler: \" + requestPath)\n\n\tctx.Values().Set(\"info\", shareInformation)\n\tctx.Next() // execute the next handler, in this case the main one.\n}\n\nfunc after(ctx iris.Context) {\n\tprintln(\"After the mainHandler\")\n}\n\nfunc mainHandler(ctx iris.Context) {\n\tprintln(\"Inside mainHandler\")\n\n\t// take the info from the \"before\" handler.\n\tinfo := ctx.Values().GetString(\"info\")\n\n\t// write something to the client as a response.\n\tctx.HTML(\"<h1>Response</h1>\")\n\tctx.HTML(\"<br/> Info: \" + info)\n\n\tctx.Next() // execute the \"after\".\n}\n"
  },
  {
    "path": "_examples/routing/writing-a-middleware/share-funcs/main.go",
    "content": "// Package main shows how you can share a\n// function between handlers of the same chain.\n// Note that, this case is very rarely used and it exists,\n// mostly, for 3rd-party middleware creators.\n//\n// The middleware creator registers a dynamic function by Context.SetFunc and\n// the route handler just needs to call Context.CallFunc(funcName, arguments),\n// without knowning what is the specific middleware's implementation or who was the creator\n// of that function, it may be a basicauth middleware's logout or session's logout.\n//\n// See Context.SetLogoutFunc and Context.Logout methods too (these are not covered here).\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := newApp()\n\n\t// GET: http://localhost:8080\n\tapp.Listen(\":8080\")\n}\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\tapp.Use(middleware)\n\t// OR app.UseRouter(middleware)\n\t// to register it everywhere,\n\t// including the HTTP errors.\n\n\tapp.Get(\"/\", handler)\n\tapp.Get(\"/2\", middleware2, handler2)\n\tapp.Get(\"/3\", middleware3, handler3)\n\n\treturn app\n}\n\n// Assume: this is a middleware which does not export\n// the 'hello' function for several reasons\n// but we offer a 'greeting' optional feature to the route handler.\nfunc middleware(ctx iris.Context) {\n\tctx.SetFunc(\"greet\", hello)\n\tctx.Next()\n}\n\n// Assume: this is a handler which needs to \"greet\" the client but\n// the function for that job is not predictable,\n// it may change - dynamically (SetFunc) - depending on\n// the middlewares registered before this route handler.\n// E.g. it may be a \"Hello $name\" or \"Greetings $Name\".\nfunc handler(ctx iris.Context) {\n\toutputs, err := ctx.CallFunc(\"greet\", \"Gophers\")\n\tif err != nil {\n\t\tctx.StopWithError(iris.StatusInternalServerError, err)\n\t\treturn\n\t}\n\n\tresponse := outputs[0].Interface().(string)\n\tctx.WriteString(response)\n}\n\nfunc middleware2(ctx iris.Context) {\n\tctx.SetFunc(\"greet\", sayHello)\n\tctx.Next()\n}\n\nfunc handler2(ctx iris.Context) {\n\t_, err := ctx.CallFunc(\"greet\", \"Gophers [2]\")\n\tif err != nil {\n\t\tctx.StopWithError(iris.StatusInternalServerError, err)\n\t\treturn\n\t}\n}\n\nfunc middleware3(ctx iris.Context) {\n\tctx.SetFunc(\"job\", function3)\n\tctx.Next()\n}\n\nfunc handler3(ctx iris.Context) {\n\t_, err := ctx.CallFunc(\"job\")\n\tif err != nil {\n\t\tctx.StopWithError(iris.StatusInternalServerError, err)\n\t\treturn\n\t}\n\n\tctx.WriteString(\"OK, job was executed.\\nSee the command prompt.\")\n}\n\n/*\n| ------------------------ |\n| function implementations |\n| ------------------------ |\n*/\n\nfunc hello(name string) string {\n\treturn fmt.Sprintf(\"Hello, %s!\", name)\n}\n\nfunc sayHello(ctx iris.Context, name string) {\n\tctx.WriteString(hello(name))\n}\n\nfunc function3() {\n\tfmt.Printf(\"function3 called\\n\")\n}\n"
  },
  {
    "path": "_examples/routing/writing-a-middleware/share-funcs/main_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nfunc TestShareFuncs(t *testing.T) {\n\tapp := newApp()\n\te := httptest.New(t, app)\n\n\te.GET(\"/\").Expect().Status(httptest.StatusOK).Body().IsEqual(\"Hello, Gophers!\")\n\te.GET(\"/2\").Expect().Status(httptest.StatusOK).Body().IsEqual(\"Hello, Gophers [2]!\")\n\te.GET(\"/3\").Expect().Status(httptest.StatusOK).Body().IsEqual(\"OK, job was executed.\\nSee the command prompt.\")\n}\n"
  },
  {
    "path": "_examples/routing/writing-a-middleware/share-services/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := newApp()\n\n\t// GET: http://localhost:8080\n\tapp.Listen(\":8080\")\n}\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\tapp.Use(middleware)\n\t// OR app.UseRouter(middleware)\n\t// to register it everywhere,\n\t// including the HTTP errors.\n\n\tapp.Get(\"/\", handler)\n\n\treturn app\n}\n\nfunc middleware(ctx iris.Context) {\n\tservice := &helloService{\n\t\tGreeting: \"Hello\",\n\t}\n\tsetService(ctx, service)\n\n\tctx.Next()\n}\n\nfunc handler(ctx iris.Context) {\n\tservice, ok := getService(ctx)\n\tif !ok {\n\t\tctx.StopWithStatus(iris.StatusInternalServerError)\n\t\treturn\n\t}\n\n\tresponse := service.Greet(\"Gophers\")\n\tctx.WriteString(response)\n}\n\n/*\n| ---------------------- |\n| service implementation |\n| ---------------------- |\n*/\n\nconst serviceContextKey = \"app.service\"\n\nfunc setService(ctx iris.Context, service GreetService) {\n\tctx.Values().Set(serviceContextKey, service)\n}\n\nfunc getService(ctx iris.Context) (GreetService, bool) {\n\tv := ctx.Values().Get(serviceContextKey)\n\tif v == nil {\n\t\treturn nil, false\n\t}\n\n\tservice, ok := v.(GreetService)\n\tif !ok {\n\t\treturn nil, false\n\t}\n\n\treturn service, true\n}\n\n// A GreetService example.\ntype GreetService interface {\n\tGreet(name string) string\n}\n\ntype helloService struct {\n\tGreeting string\n}\n\nfunc (m *helloService) Greet(name string) string {\n\treturn fmt.Sprintf(\"%s, %s!\", m.Greeting, name)\n}\n"
  },
  {
    "path": "_examples/routing/writing-a-middleware/share-services/main_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nfunc TestShareServices(t *testing.T) {\n\tapp := newApp()\n\te := httptest.New(t, app)\n\n\te.GET(\"/\").Expect().Status(httptest.StatusOK).Body().IsEqual(\"Hello, Gophers!\")\n}\n"
  },
  {
    "path": "_examples/sessions/basic/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n\n\t\"github.com/kataras/iris/v12/sessions\"\n)\n\nconst cookieNameForSessionID = \"session_id_cookie\"\n\nfunc secret(ctx iris.Context) {\n\t// Check if user is authenticated\n\tif auth, _ := sessions.Get(ctx).GetBoolean(\"authenticated\"); !auth {\n\t\tctx.StatusCode(iris.StatusForbidden)\n\t\treturn\n\t}\n\n\t// Print secret message\n\tctx.WriteString(\"The cake is a lie!\")\n}\n\nfunc login(ctx iris.Context) {\n\tsession := sessions.Get(ctx)\n\n\t// Authentication goes here\n\t// ...\n\n\t// Set user as authenticated\n\tsession.Set(\"authenticated\", true)\n}\n\nfunc logout(ctx iris.Context) {\n\tsession := sessions.Get(ctx)\n\n\t// Revoke users authentication\n\tsession.Set(\"authenticated\", false)\n}\n\nfunc main() {\n\tapp := iris.New()\n\tsess := sessions.New(sessions.Config{\n\t\tCookie: cookieNameForSessionID,\n\t\t// CookieSecureTLS: true,\n\t\tAllowReclaim: true,\n\t})\n\tapp.Use(sess.Handler())\n\t// ^ or comment this line and use sess.Start(ctx) inside your handlers\n\t// instead of sessions.Get(ctx).\n\n\tapp.Get(\"/secret\", secret)\n\tapp.Get(\"/login\", login)\n\tapp.Get(\"/logout\", logout)\n\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/sessions/database/badger/main.go",
    "content": "package main\n\nimport (\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n\n\t\"github.com/kataras/iris/v12/sessions\"\n\t\"github.com/kataras/iris/v12/sessions/sessiondb/badger\"\n\n\t\"github.com/kataras/iris/v12/_examples/sessions/overview/example\"\n)\n\nfunc main() {\n\tdb, err := badger.New(\"./data\")\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// close and unlock the database when control+C/cmd+C pressed\n\tiris.RegisterOnInterrupt(func() {\n\t\tdb.Close()\n\t})\n\n\tdefer db.Close() // close and unlock the database if application errored.\n\n\t// The default transcoder is the JSON one,\n\t// based on the https://golang.org/pkg/encoding/json/#Unmarshal\n\t// you can only retrieve numbers as float64 types:\n\t// * bool, for booleans\n\t// * float64, for numbers\n\t// * string, for strings\n\t// * []any, for arrays\n\t// * map[string]any, for objects.\n\t// If you want to save the data per go-specific types\n\t// you should change the DefaultTranscoder to the GobTranscoder one:\n\t// sessions.DefaultTranscoder = sessions.GobTranscoder{}\n\n\tsess := sessions.New(sessions.Config{\n\t\tCookie:       \"sessionscookieid\",\n\t\tExpires:      1 * time.Minute, // <=0 means unlimited life. Defaults to 0.\n\t\tAllowReclaim: true,\n\t})\n\n\tsess.OnDestroy(func(sid string) {\n\t\tprintln(sid + \" expired and destroyed from memory and its values from database\")\n\t})\n\n\t//\n\t// IMPORTANT:\n\t//\n\tsess.UseDatabase(db)\n\n\tapp := example.NewApp(sess)\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/sessions/database/boltdb/main.go",
    "content": "package main\n\nimport (\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n\n\t\"github.com/kataras/iris/v12/sessions\"\n\t\"github.com/kataras/iris/v12/sessions/sessiondb/boltdb\"\n\n\t\"github.com/kataras/iris/v12/_examples/sessions/overview/example\"\n)\n\nfunc main() {\n\tdb, err := boltdb.New(\"./sessions.db\", os.FileMode(0750))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// close and unlobkc the database when control+C/cmd+C pressed\n\tiris.RegisterOnInterrupt(func() {\n\t\tdb.Close()\n\t})\n\n\tdefer db.Close() // close and unlock the database if application errored.\n\tsess := sessions.New(sessions.Config{\n\t\tCookie:       \"sessionscookieid\",\n\t\tExpires:      45 * time.Minute, // <=0 means unlimited life. Defaults to 0.\n\t\tAllowReclaim: true,\n\t})\n\n\t//\n\t// IMPORTANT:\n\t//\n\tsess.UseDatabase(db)\n\n\t// The default database's values encoder and decoder\n\t// calls the value's `Marshal/Unmarshal` methods (if any)\n\t// otherwise JSON is selected,\n\t// the JSON format can be stored to any database and\n\t// it supports both builtin language types(e.g. string, int) and custom struct values.\n\t// Also, and the most important, the values can be\n\t// retrieved/logged/monitored by a third-party program\n\t// written in any other language as well.\n\t//\n\t// You can change this behavior by registering a custom `Transcoder`.\n\t// Iris provides a `GobTranscoder` which is mostly suitable\n\t// if your session values are going to be custom Go structs.\n\t// Select this if you always retrieving values through Go.\n\t// Don't forget to initialize a call of gob.Register when necessary.\n\t// Read https://golang.org/pkg/encoding/gob/ for more.\n\t//\n\t// You can also implement your own `sessions.Transcoder` and use it,\n\t// i.e: a transcoder which will allow(on Marshal: return its byte representation and nil error)\n\t// or dissalow(on Marshal: return non nil error) certain types.\n\t//\n\t// gob.Register(example.BusinessModel{})\n\t// sessions.DefaultTranscoder = sessions.GobTranscoder{}\n\n\tapp := example.NewApp(sess)\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/sessions/database/redis/Dockerfile",
    "content": "FROM golang:latest AS builder\nRUN apt-get update\nWORKDIR /go/src/app\nENV GO111MODULE=on \\\n    CGO_ENABLED=0 \\\n    GOOS=linux \\\n    GOARCH=amd64\n# Caching go modules and build the binary.\nCOPY go.mod .\nRUN go mod download\nCOPY . .\nRUN go install\n\nFROM scratch\nCOPY --from=builder /go/bin/app .\nENTRYPOINT [\"./app\"]"
  },
  {
    "path": "_examples/sessions/database/redis/docker-compose.yml",
    "content": "# docker-compose up [--build]\nversion: '3'\n\nservices:\n  redis-server:\n    image: redis\n  app1:\n    build: .\n    depends_on:\n      - redis-server\n    ports:\n      - 8080:8080\n    environment: \n      - PORT=8080\n      - REDIS_ADDR=redis-server:6379\n  app2:\n    build: .\n    depends_on:\n      - redis-server\n    ports:\n      - 9090:9090\n    environment: \n      - PORT=9090\n      - REDIS_ADDR=redis-server:6379"
  },
  {
    "path": "_examples/sessions/database/redis/go.mod",
    "content": "module app\n\ngo 1.25\n\nrequire github.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd\n\nrequire (\n\tgithub.com/BurntSushi/toml v1.6.0 // indirect\n\tgithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect\n\tgithub.com/CloudyKit/jet/v6 v6.3.1 // indirect\n\tgithub.com/Joker/jade v1.1.3 // indirect\n\tgithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 // indirect\n\tgithub.com/andybalholm/brotli v1.2.0 // indirect\n\tgithub.com/aymerick/douceur v0.2.0 // indirect\n\tgithub.com/cespare/xxhash/v2 v2.3.0 // indirect\n\tgithub.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect\n\tgithub.com/fatih/structs v1.1.0 // indirect\n\tgithub.com/flosch/pongo2/v4 v4.0.2 // indirect\n\tgithub.com/golang/snappy v1.0.0 // indirect\n\tgithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a // indirect\n\tgithub.com/google/uuid v1.6.0 // indirect\n\tgithub.com/gorilla/css v1.0.1 // indirect\n\tgithub.com/iris-contrib/schema v0.0.6 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/kataras/blocks v0.0.8 // indirect\n\tgithub.com/kataras/golog v0.1.12 // indirect\n\tgithub.com/kataras/pio v0.0.13 // indirect\n\tgithub.com/kataras/sitemap v0.0.6 // indirect\n\tgithub.com/kataras/tunnel v0.0.4 // indirect\n\tgithub.com/klauspost/compress v1.18.2 // indirect\n\tgithub.com/kr/pretty v0.3.1 // indirect\n\tgithub.com/mailgun/raymond/v2 v2.0.48 // indirect\n\tgithub.com/mailru/easyjson v0.9.1 // indirect\n\tgithub.com/microcosm-cc/bluemonday v1.0.27 // indirect\n\tgithub.com/redis/go-redis/v9 v9.17.2 // indirect\n\tgithub.com/rogpeppe/go-internal v1.10.0 // indirect\n\tgithub.com/russross/blackfriday/v2 v2.1.0 // indirect\n\tgithub.com/schollz/closestmatch v2.1.0+incompatible // indirect\n\tgithub.com/sirupsen/logrus v1.8.1 // indirect\n\tgithub.com/stretchr/testify v1.11.1 // indirect\n\tgithub.com/tdewolff/minify/v2 v2.24.8 // indirect\n\tgithub.com/tdewolff/parse/v2 v2.8.5 // indirect\n\tgithub.com/valyala/bytebufferpool v1.0.0 // indirect\n\tgithub.com/vmihailenco/msgpack/v5 v5.4.1 // indirect\n\tgithub.com/vmihailenco/tagparser/v2 v2.0.0 // indirect\n\tgithub.com/yosssi/ace v0.0.5 // indirect\n\tgolang.org/x/crypto v0.47.0 // indirect\n\tgolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 // indirect\n\tgolang.org/x/net v0.49.0 // indirect\n\tgolang.org/x/sys v0.40.0 // indirect\n\tgolang.org/x/text v0.33.0 // indirect\n\tgolang.org/x/time v0.14.0 // indirect\n\tgoogle.golang.org/protobuf v1.36.11 // indirect\n\tgopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect\n\tgopkg.in/ini.v1 v1.67.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n"
  },
  {
    "path": "_examples/sessions/database/redis/go.sum",
    "content": "github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=\ngithub.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=\ngithub.com/CloudyKit/jet/v6 v6.3.1 h1:6IAo5Cx21xrHVaR8zzXN5gJatKV/wO7Nf6bfCnCSbUw=\ngithub.com/CloudyKit/jet/v6 v6.3.1/go.mod h1:lf8ksdNsxZt7/yH/3n4vJQWA9RUq4wpaHtArHhGVMOw=\ngithub.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc=\ngithub.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=\ngithub.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=\ngithub.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 h1:dG7/gLroJGht/jSQtHiLvT48Hxn+crbmvyItZC8cWOs=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05/go.mod h1:NYezi6wtnJtBm5btoprXc5SvAdqH0XTXWnUup0MptAI=\ngithub.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=\ngithub.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=\ngithub.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=\ngithub.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=\ngithub.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=\ngithub.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=\ngithub.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=\ngithub.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=\ngithub.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=\ngithub.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=\ngithub.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=\ngithub.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=\ngithub.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=\ngithub.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=\ngithub.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=\ngithub.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=\ngithub.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=\ngithub.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw=\ngithub.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8=\ngithub.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=\ngithub.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=\ngithub.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=\ngithub.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a h1:l7A0loSszR5zHd/qK53ZIHMO8b3bBSmENnQ6eKnUT0A=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=\ngithub.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=\ngithub.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=\ngithub.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=\ngithub.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=\ngithub.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2 h1:T9THsdP1woyAqKHwjkEsbCnMefsAFvk8iJJKokcJ3Go=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2/go.mod h1:JLDgIqnFy5loDSUv1OA2j0mb6p/rDhiCqigP22Uq9xE=\ngithub.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw=\ngithub.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/kataras/blocks v0.0.8 h1:MrpVhoFTCR2v1iOOfGng5VJSILKeZZI+7NGfxEh3SUM=\ngithub.com/kataras/blocks v0.0.8/go.mod h1:9Jm5zx6BB+06NwA+OhTbHW1xkMOYxahnqTN5DveZ2Yg=\ngithub.com/kataras/golog v0.1.12 h1:Bu7I/G4ilJlbfzjmU39O9N+2uO1pBcMK045fzZ4ytNg=\ngithub.com/kataras/golog v0.1.12/go.mod h1:wrGSbOiBqbQSQznleVNX4epWM8rl9SJ/rmEacl0yqy4=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd h1:oMsQgqKACbUdlaIWnN+EZzzAnHkw90hE/y/R0BSij2U=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd/go.mod h1:yucjtDl9Vn3OctWM1fi0MuM9OckemC7kEreFa9QtPF8=\ngithub.com/kataras/pio v0.0.13 h1:x0rXVX0fviDTXOOLOmr4MUxOabu1InVSTu5itF8CXCM=\ngithub.com/kataras/pio v0.0.13/go.mod h1:k3HNuSw+eJ8Pm2lA4lRhg3DiCjVgHlP8hmXApSej3oM=\ngithub.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY=\ngithub.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4=\ngithub.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA=\ngithub.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw=\ngithub.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=\ngithub.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=\ngithub.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=\ngithub.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8=\ngithub.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=\ngithub.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=\ngithub.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=\ngithub.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=\ngithub.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=\ngithub.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=\ngithub.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=\ngithub.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/redis/go-redis/v9 v9.17.2 h1:P2EGsA4qVIM3Pp+aPocCJ7DguDHhqrXNhVcEp4ViluI=\ngithub.com/redis/go-redis/v9 v9.17.2/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0//kSOd3370=\ngithub.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=\ngithub.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=\ngithub.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=\ngithub.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=\ngithub.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=\ngithub.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk=\ngithub.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=\ngithub.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=\ngithub.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=\ngithub.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=\ngithub.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/tdewolff/minify/v2 v2.24.8 h1:58/VjsbevI4d5FGV0ZSuBrHMSSkH4MCH0sIz/eKIauE=\ngithub.com/tdewolff/minify/v2 v2.24.8/go.mod h1:0Ukj0CRpo/sW/nd8uZ4ccXaV1rEVIWA3dj8U7+Shhfw=\ngithub.com/tdewolff/parse/v2 v2.8.5 h1:ZmBiA/8Do5Rpk7bDye0jbbDUpXXbCdc3iah4VeUvwYU=\ngithub.com/tdewolff/parse/v2 v2.8.5/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=\ngithub.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE=\ngithub.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=\ngithub.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=\ngithub.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=\ngithub.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=\ngithub.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=\ngithub.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=\ngithub.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=\ngithub.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA=\ngithub.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0=\ngithub.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=\ngithub.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=\ngithub.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=\ngolang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=\ngolang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=\ngolang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=\ngolang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=\ngolang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=\ngolang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=\ngolang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=\ngolang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=\ngoogle.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=\ngopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nmoul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs=\nmoul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE=\n"
  },
  {
    "path": "_examples/sessions/database/redis/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/sessions\"\n\t\"github.com/kataras/iris/v12/sessions/sessiondb/redis\"\n\n\t\"github.com/kataras/iris/v12/_examples/sessions/overview/example\"\n)\n\n// 1. Install Redis:\n// 1.1 Windows: https://github.com/ServiceStack/redis-windows\n// 1.2 Other: https://redis.io/download\n// 2. Run command: go run -mod=mod main.go\n//\n// Tested with redis version 3.0.503.\nfunc main() {\n\t// These are the default values,\n\t// you can replace them based on your running redis' server settings:\n\tdb := redis.New(redis.Config{\n\t\tNetwork:   \"tcp\",\n\t\tAddr:      getenv(\"REDIS_ADDR\", \"127.0.0.1:6379\"),\n\t\tTimeout:   time.Duration(30) * time.Second,\n\t\tMaxActive: 10,\n\t\tUsername:  \"\",\n\t\tPassword:  \"\",\n\t\tDatabase:  \"\",\n\t\tPrefix:    \"myapp-\",\n\t\tDriver:    redis.GoRedis(), // defaults to this driver.\n\t\t// To set a custom, existing go-redis client, use the \"SetClient\" method:\n\t\t// Driver: redis.GoRedis().SetClient(customGoRedisClient)\n\t})\n\n\t// Optionally configure the underline driver:\n\t// driver := redis.GoRedis()\n\t// // To set a custom client:\n\t// driver.SetClient(customGoRedisClient)\n\t// OR:\n\t// driver.ClientOptions = redis.Options{...}\n\t// driver.ClusterOptions = redis.ClusterOptions{...}\n\t// redis.New(redis.Config{Driver: driver, ...})\n\n\tdefer db.Close() // close the database connection if application errored.\n\n\tsess := sessions.New(sessions.Config{\n\t\tCookie:          \"_session_id\",\n\t\tExpires:         0, // defaults to 0: unlimited life. Another good value is: 45 * time.Minute,\n\t\tAllowReclaim:    true,\n\t\tCookieSecureTLS: true,\n\t})\n\n\t//\n\t// IMPORTANT:\n\t//\n\tsess.UseDatabase(db)\n\n\tapp := example.NewApp(sess)\n\n\t// TIP scaling-out Iris sessions using redis:\n\t// $ docker-compose up\n\t// http://localhost:8080/set/$key/$value\n\t// The value will be available on all Iris servers as well.\n\t// E.g. http://localhost:9090/get/$key and vice versa.\n\taddr := fmt.Sprintf(\":%s\", getenv(\"PORT\", \"8080\"))\n\tapp.Listen(addr)\n}\n\nfunc getenv(key string, def string) string {\n\tif v := os.Getenv(strings.ToUpper(key)); v != \"\" {\n\t\treturn v\n\t}\n\n\treturn def\n}\n"
  },
  {
    "path": "_examples/sessions/flash-messages/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n\n\t\"github.com/kataras/iris/v12/sessions\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\tsess := sessions.New(sessions.Config{Cookie: \"_session_id\", AllowReclaim: true})\n\tapp.Use(sess.Handler())\n\n\tapp.Get(\"/set\", func(ctx iris.Context) {\n\t\ts := sessions.Get(ctx)\n\t\ts.SetFlash(\"name\", \"iris\")\n\t\tctx.Writef(\"Message set, is available for the next request\")\n\t})\n\n\tapp.Get(\"/get\", func(ctx iris.Context) {\n\t\ts := sessions.Get(ctx)\n\t\tname := s.GetFlashString(\"name\")\n\t\tif name == \"\" {\n\t\t\tctx.Writef(\"Empty name!!\")\n\t\t\treturn\n\t\t}\n\t\tctx.Writef(\"Hello %s\", name)\n\t})\n\n\tapp.Get(\"/test\", func(ctx iris.Context) {\n\t\ts := sessions.Get(ctx)\n\t\tname := s.GetFlashString(\"name\")\n\t\tif name == \"\" {\n\t\t\tctx.Writef(\"Empty name!!\")\n\t\t\treturn\n\t\t}\n\n\t\tctx.Writef(\"Ok you are coming from /set ,the value of the name is %s\", name)\n\t\tctx.Writef(\", and again from the same context: %s\", name)\n\t})\n\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/sessions/overview/example/example.go",
    "content": "package example\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/sessions\"\n)\n\n// BusinessModel is just a Go struct value that we will use in our session example,\n// never save sensitive information, like passwords, here.\ntype BusinessModel struct {\n\tName string\n}\n\n// NewApp returns a new application for showcasing the sessions feature.\nfunc NewApp(sess *sessions.Sessions) *iris.Application {\n\tapp := iris.New()\n\tapp.Use(sess.Handler()) // register the session manager on a group of routes or the root app.\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tsession := sessions.Get(ctx) // same as sess.Start(ctx, cookieOptions...)\n\t\tif session.Len() == 0 {\n\t\t\tctx.HTML(`no session values stored yet. Navigate to: <a href=\"/set\">set page</a>`)\n\t\t\treturn\n\t\t}\n\n\t\tctx.HTML(\"<ul>\")\n\t\tsession.Visit(func(key string, value any) {\n\t\t\tctx.HTML(fmt.Sprintf(\"<li> %s = %v </li>\", key, value))\n\t\t})\n\n\t\tctx.HTML(\"</ul>\")\n\t})\n\n\t// set session values.\n\tapp.Get(\"/set\", func(ctx iris.Context) {\n\t\tsession := sessions.Get(ctx)\n\t\tisNew := session.IsNew()\n\n\t\tsession.Set(\"username\", \"iris\")\n\n\t\tctx.Writef(\"All ok session set to: %s [isNew=%t]\", session.GetString(\"username\"), isNew)\n\t})\n\n\tapp.Get(\"/get\", func(ctx iris.Context) {\n\t\tsession := sessions.Get(ctx)\n\n\t\t// get a specific value, as string,\n\t\t// if not found then it returns just an empty string.\n\t\tname := session.GetString(\"username\")\n\n\t\tctx.Writef(\"The username on the /set was: %s\", name)\n\t})\n\n\tapp.Get(\"/set-struct\", func(ctx iris.Context) {\n\t\tsession := sessions.Get(ctx)\n\t\tsession.Set(\"struct\", BusinessModel{Name: \"John Doe\"})\n\n\t\tctx.WriteString(\"All ok session value of the 'struct' was set.\")\n\t})\n\n\tapp.Get(\"/get-struct\", func(ctx iris.Context) {\n\t\tsession := sessions.Get(ctx)\n\t\tvar v BusinessModel\n\t\tif err := session.Decode(\"struct\", &v); err != nil {\n\t\t\tctx.StopWithError(iris.StatusInternalServerError, err)\n\t\t\treturn\n\t\t}\n\t\tctx.Writef(\"Session value of the 'struct' is: %#+v\", v)\n\t})\n\n\tapp.Get(\"/set/{key}/{value}\", func(ctx iris.Context) {\n\t\tsession := sessions.Get(ctx)\n\n\t\tkey := ctx.Params().Get(\"key\")\n\t\tvalue := ctx.Params().Get(\"value\")\n\t\tisNew := session.IsNew()\n\n\t\tsession.Set(key, value)\n\n\t\tctx.Writef(\"All ok session value of the '%s' is: %s [isNew=%t]\", key, session.GetString(key), isNew)\n\t})\n\n\tapp.Get(\"/get/{key}\", func(ctx iris.Context) {\n\t\tsession := sessions.Get(ctx)\n\t\t// get a specific key, as string, if no found returns just an empty string\n\t\tkey := ctx.Params().Get(\"key\")\n\t\tvalue := session.Get(key)\n\n\t\tctx.Writef(\"The [%s:%T] on the /set was: %v\", key, value, value)\n\t})\n\n\tapp.Get(\"/set/{type}/{key}/{value}\", func(ctx iris.Context) {\n\t\tsession := sessions.Get(ctx)\n\n\t\tkey := ctx.Params().Get(\"key\")\n\t\tvar value any\n\n\t\tswitch ctx.Params().Get(\"type\") {\n\t\tcase \"int\":\n\t\t\tvalue = ctx.Params().GetIntDefault(\"value\", 0)\n\t\tcase \"float64\":\n\t\t\tvalue = ctx.Params().GetFloat64Default(\"value\", 0.0)\n\t\tdefault:\n\t\t\tvalue = ctx.Params().Get(\"value\")\n\t\t}\n\t\tsession.Set(key, value)\n\n\t\tvalue = session.Get(key)\n\t\tctx.Writef(\"Key: %s, Type: %T, Value: %v\", key, value, value)\n\t})\n\n\tapp.Get(\"/delete\", func(ctx iris.Context) {\n\t\tsession := sessions.Get(ctx)\n\t\t// delete a specific key\n\t\tsession.Delete(\"username\")\n\t})\n\n\tapp.Get(\"/clear\", func(ctx iris.Context) {\n\t\tsession := sessions.Get(ctx)\n\t\t// removes all entries.\n\t\tsession.Clear()\n\t})\n\n\tapp.Get(\"/update\", func(ctx iris.Context) {\n\t\tsession := sessions.Get(ctx)\n\t\t// shifts the expiration based on the session's `Lifetime`.\n\t\tif err := session.Man.ShiftExpiration(ctx); err != nil {\n\t\t\tif errors.Is(err, sessions.ErrNotFound) {\n\t\t\t\tctx.StatusCode(iris.StatusNotFound)\n\t\t\t} else if errors.Is(err, sessions.ErrNotImplemented) {\n\t\t\t\tctx.StatusCode(iris.StatusNotImplemented)\n\t\t\t} else {\n\t\t\t\tctx.StatusCode(iris.StatusNotModified)\n\t\t\t}\n\n\t\t\tctx.Writef(\"%v\", err)\n\t\t\tctx.Application().Logger().Error(err)\n\t\t}\n\t})\n\n\tapp.Get(\"/destroy\", func(ctx iris.Context) {\n\t\tsession := sessions.Get(ctx)\n\t\t// Man(anager)'s Destroy, removes the entire session data and cookie\n\t\tsession.Man.Destroy(ctx)\n\t})\n\n\t// Note about Destroy:\n\t//\n\t// You can destroy a session outside of a handler too, using the:\n\t// sess.DestroyByID\n\t// sess.DestroyAll\n\n\t// remember: slices and maps are muttable by-design\n\t// The `SetImmutable` makes sure that they will be stored and received\n\t// as immutable, so you can't change them directly by mistake.\n\t//\n\t// Use `SetImmutable` consistently, it's slower than `Set`.\n\t// Read more about muttable and immutable go types: https://stackoverflow.com/a/8021081\n\tapp.Get(\"/set-immutable\", func(ctx iris.Context) {\n\t\tsession := sessions.Get(ctx)\n\n\t\tbusiness := []BusinessModel{{Name: \"Edward\"}, {Name: \"value 2\"}}\n\t\tsession.SetImmutable(\"businessEdit\", business)\n\t\tvar businessGet []BusinessModel\n\t\terr := session.Decode(\"businessEdit\", &businessGet)\n\t\tif err != nil {\n\t\t\tctx.StopWithError(iris.StatusInternalServerError, err)\n\t\t\treturn\n\t\t}\n\n\t\t// try to change it, if we used `Set` instead of `SetImmutable` this\n\t\t// change will affect the underline array of the session's value \"businessEdit\", but now it will not.\n\t\tbusinessGet[0].Name = \"Gabriel\"\n\t})\n\n\tapp.Get(\"/get-immutable\", func(ctx iris.Context) {\n\t\tvar models []BusinessModel\n\t\terr := sessions.Get(ctx).Decode(\"businessEdit\", &models)\n\t\tif err != nil {\n\t\t\tctx.StopWithError(iris.StatusInternalServerError, err)\n\t\t\treturn\n\t\t}\n\n\t\tif models == nil {\n\t\t\tctx.HTML(\"please navigate to the <a href='/set_immutable'>/set-immutable</a> first\")\n\t\t\treturn\n\t\t}\n\n\t\tfirstModel := models[0]\n\t\t// businessGet[0].Name is equal to Edward initially\n\t\tif firstModel.Name != \"Edward\" {\n\t\t\tpanic(\"Report this as a bug, immutable data cannot be changed from the caller without re-SetImmutable\")\n\t\t}\n\n\t\tctx.Writef(\"[]businessModel[0].Name remains: %s\", firstModel.Name)\n\n\t\t// the name should remains \"Edward\"\n\t})\n\n\treturn app\n}\n"
  },
  {
    "path": "_examples/sessions/overview/main.go",
    "content": "package main\n\nimport (\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/_examples/sessions/overview/example\"\n\t\"github.com/kataras/iris/v12/sessions\"\n)\n\nfunc main() {\n\tsess := sessions.New(sessions.Config{\n\t\t// Cookie string, the session's client cookie name, for example: \"_session_id\"\n\t\t//\n\t\t// Defaults to \"irissessionid\"\n\t\tCookie: \"_session_id\",\n\t\t// it's time.Duration, from the time cookie is created, how long it can be alive?\n\t\t// 0 means no expire, unlimited life.\n\t\t// -1 means expire when browser closes\n\t\t// or set a value, like 2 hours:\n\t\tExpires: time.Hour * 2,\n\t\t// if you want to invalid cookies on different subdomains\n\t\t// of the same host, then enable it.\n\t\t// Defaults to false.\n\t\tDisableSubdomainPersistence: false,\n\t\t// Allow getting the session value stored by the request from the same request.\n\t\tAllowReclaim: true,\n\t\t/*\n\t\t\tSessionIDGenerator: func(ctx iris.Context) string {\n\t\t\t\tid:= ctx.GetHeader(\"X-Session-Id\")\n\t\t\t\tif id == \"\" {\n\t\t\t\t\tid = // [generate ID here and set the header]\n\t\t\t\t\tctx.Header(\"X-Session-Id\", id)\n\t\t\t\t}\n\n\t\t\t\treturn id\n\t\t\t},\n\t\t*/\n\t})\n\n\tapp := example.NewApp(sess)\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/sessions/securecookie/main.go",
    "content": "package main\n\n// developers can use any library to add a custom cookie encoder/decoder.\n// At this example we use the gorilla's securecookie package:\n// $ go get github.com/gorilla/securecookie\n// $ go run main.go\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/sessions\"\n\n\t\"github.com/kataras/iris/v12/_examples/sessions/overview/example\"\n\n\t\"github.com/gorilla/securecookie\"\n)\n\nfunc newApp() *iris.Application {\n\tcookieName := \"_session_id\"\n\t// AES only supports key sizes of 16, 24 or 32 bytes.\n\t// You either need to provide exactly that amount or you derive the key from what you type in.\n\thashKey := securecookie.GenerateRandomKey(64)\n\tblockKey := securecookie.GenerateRandomKey(32)\n\ts := securecookie.New(hashKey, blockKey)\n\tmySessions := sessions.New(sessions.Config{\n\t\tCookie:       cookieName,\n\t\tEncoding:     s,\n\t\tAllowReclaim: true,\n\t})\n\n\t// mySessions.UseDatabase(...see sessions/database example folder)\n\n\treturn example.NewApp(mySessions)\n}\n\nfunc main() {\n\tapp := newApp()\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/sessions/securecookie/main_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nfunc TestSessionsEncodeDecode(t *testing.T) {\n\tapp := newApp()\n\te := httptest.New(t, app, httptest.URL(\"http://example.com\"))\n\n\tes := e.GET(\"/set\").Expect()\n\tes.Status(iris.StatusOK)\n\tes.Cookies().NotEmpty()\n\tes.Body().IsEqual(\"All ok session set to: iris [isNew=true]\")\n\n\te.GET(\"/get\").Expect().Status(iris.StatusOK).Body().IsEqual(\"The username on the /set was: iris\")\n\t// delete and re-get\n\te.GET(\"/delete\").Expect().Status(iris.StatusOK)\n\te.GET(\"/get\").Expect().Status(iris.StatusOK).Body().IsEqual(\"The username on the /set was: \")\n\t// set, clear and re-get\n\te.GET(\"/set\").Expect().Body().IsEqual(\"All ok session set to: iris [isNew=false]\")\n\te.GET(\"/clear\").Expect().Status(iris.StatusOK)\n\te.GET(\"/get\").Expect().Status(iris.StatusOK).Body().IsEqual(\"The username on the /set was: \")\n}\n"
  },
  {
    "path": "_examples/sessions/viewdata/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/sessions\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.RegisterView(iris.HTML(\"./views\", \".html\"))\n\n\tsess := sessions.New(sessions.Config{Cookie: \"session_cookie\", AllowReclaim: true})\n\tapp.Use(sess.Handler())\n\t// ^ use app.UseRouter instead to access sessions on HTTP errors too.\n\n\t// Register our custom middleware, after the sessions middleware.\n\tapp.Use(setSessionViewData)\n\n\tapp.Get(\"/\", index)\n\tapp.Listen(\":8080\")\n}\n\nfunc setSessionViewData(ctx iris.Context) {\n\tsession := sessions.Get(ctx)\n\tctx.ViewData(\"session\", session)\n\tctx.Next()\n}\n\nfunc index(ctx iris.Context) {\n\tsession := sessions.Get(ctx)\n\tsession.Set(\"username\", \"kataras\")\n\tif err := ctx.View(\"index\"); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n\t/* OR without middleware:\n\tif err := ctx.View(\"index\", iris.Map{\n\t\t\t\"session\": session,\n\t\t\t//   {{.session.Get \"username\"}}\n\t\t\t// OR to pass only the 'username':\n\t\t\t// \"username\": session.Get(\"username\"),\n\t\t\t//   {{.username}}\n\t\t})\n\t*/\n}\n"
  },
  {
    "path": "_examples/sessions/viewdata/views/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Sessions View Data</title>\n</head>\n<body>\n    Hello {{.session.Get \"username\"}}\n</body>\n</html>"
  },
  {
    "path": "_examples/testing/ginkgotest/ginkgotest_suite_test.go",
    "content": "package main_test\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n\t\"testing\"\n\n\t. \"github.com/onsi/ginkgo/v2\"\n\t. \"github.com/onsi/gomega\"\n)\n\nfunc TestGinkgotest(t *testing.T) {\n\tRegisterFailHandler(Fail)\n\tRunSpecs(t, \"Ginkgotest Suite\")\n}\n\nfunc newApp(authentication iris.Handler) *iris.Application {\n\tapp := iris.New()\n\n\tapp.Get(\"/\", func(ctx iris.Context) { ctx.Redirect(\"/admin\") })\n\n\t// to party\n\n\tneedAuth := app.Party(\"/admin\", authentication)\n\t{\n\t\t//http://localhost:8080/admin\n\t\tneedAuth.Get(\"/\", h)\n\t\t// http://localhost:8080/admin/profile\n\t\tneedAuth.Get(\"/profile\", h)\n\n\t\t// http://localhost:8080/admin/settings\n\t\tneedAuth.Get(\"/settings\", h)\n\t}\n\n\treturn app\n}\nfunc h(ctx iris.Context) {\n\tusername, password, _ := ctx.Request().BasicAuth()\n\t// third parameter it will be always true because the middleware\n\t// makes sure for that, otherwise this handler will not be executed.\n\t// OR:\n\t//\n\t// user := ctx.User().(*myUserType)\n\t// ctx.Writef(\"%s %s:%s\", ctx.Path(), user.Username, user.Password)\n\t// OR if you don't have registered custom User structs:\n\t//\n\t// ctx.User().GetUsername()\n\t// ctx.User().GetPassword()\n\tctx.Writef(\"%s %s:%s\", ctx.Path(), username, password)\n}\n"
  },
  {
    "path": "_examples/testing/ginkgotest/go.mod",
    "content": "module ginkgotest\n\ngo 1.25\n\nrequire (\n\tgithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd\n\tgithub.com/onsi/ginkgo/v2 v2.11.0\n\tgithub.com/onsi/gomega v1.27.10\n)\n\nrequire (\n\tgithub.com/BurntSushi/toml v1.6.0 // indirect\n\tgithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect\n\tgithub.com/CloudyKit/jet/v6 v6.3.1 // indirect\n\tgithub.com/Joker/jade v1.1.3 // indirect\n\tgithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 // indirect\n\tgithub.com/ajg/form v1.5.1 // indirect\n\tgithub.com/andybalholm/brotli v1.2.0 // indirect\n\tgithub.com/aymerick/douceur v0.2.0 // indirect\n\tgithub.com/davecgh/go-spew v1.1.1 // indirect\n\tgithub.com/fatih/color v1.15.0 // indirect\n\tgithub.com/fatih/structs v1.1.0 // indirect\n\tgithub.com/flosch/pongo2/v4 v4.0.2 // indirect\n\tgithub.com/go-logr/logr v1.4.3 // indirect\n\tgithub.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect\n\tgithub.com/gobwas/glob v0.2.3 // indirect\n\tgithub.com/golang/snappy v1.0.0 // indirect\n\tgithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a // indirect\n\tgithub.com/google/go-cmp v0.7.0 // indirect\n\tgithub.com/google/go-querystring v1.1.0 // indirect\n\tgithub.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect\n\tgithub.com/google/uuid v1.6.0 // indirect\n\tgithub.com/gorilla/css v1.0.1 // indirect\n\tgithub.com/gorilla/websocket v1.5.3 // indirect\n\tgithub.com/imkira/go-interpol v1.1.0 // indirect\n\tgithub.com/iris-contrib/httpexpect/v2 v2.15.2 // indirect\n\tgithub.com/iris-contrib/schema v0.0.6 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/kataras/blocks v0.0.8 // indirect\n\tgithub.com/kataras/golog v0.1.12 // indirect\n\tgithub.com/kataras/pio v0.0.13 // indirect\n\tgithub.com/kataras/sitemap v0.0.6 // indirect\n\tgithub.com/kataras/tunnel v0.0.4 // indirect\n\tgithub.com/klauspost/compress v1.18.2 // indirect\n\tgithub.com/kr/pretty v0.3.1 // indirect\n\tgithub.com/mailgun/raymond/v2 v2.0.48 // indirect\n\tgithub.com/mailru/easyjson v0.9.1 // indirect\n\tgithub.com/mattn/go-colorable v0.1.13 // indirect\n\tgithub.com/mattn/go-isatty v0.0.19 // indirect\n\tgithub.com/microcosm-cc/bluemonday v1.0.27 // indirect\n\tgithub.com/mitchellh/go-wordwrap v1.0.1 // indirect\n\tgithub.com/pmezard/go-difflib v1.0.0 // indirect\n\tgithub.com/rogpeppe/go-internal v1.10.0 // indirect\n\tgithub.com/russross/blackfriday/v2 v2.1.0 // indirect\n\tgithub.com/sanity-io/litter v1.5.5 // indirect\n\tgithub.com/schollz/closestmatch v2.1.0+incompatible // indirect\n\tgithub.com/sergi/go-diff v1.0.0 // indirect\n\tgithub.com/sirupsen/logrus v1.8.1 // indirect\n\tgithub.com/stretchr/testify v1.11.1 // indirect\n\tgithub.com/tdewolff/minify/v2 v2.24.8 // indirect\n\tgithub.com/tdewolff/parse/v2 v2.8.5 // indirect\n\tgithub.com/valyala/bytebufferpool v1.0.0 // indirect\n\tgithub.com/vmihailenco/msgpack/v5 v5.4.1 // indirect\n\tgithub.com/vmihailenco/tagparser/v2 v2.0.0 // indirect\n\tgithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect\n\tgithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect\n\tgithub.com/xeipuuv/gojsonschema v1.2.0 // indirect\n\tgithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 // indirect\n\tgithub.com/yosssi/ace v0.0.5 // indirect\n\tgithub.com/yudai/gojsondiff v1.0.0 // indirect\n\tgithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect\n\tgolang.org/x/crypto v0.47.0 // indirect\n\tgolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 // indirect\n\tgolang.org/x/net v0.49.0 // indirect\n\tgolang.org/x/sys v0.40.0 // indirect\n\tgolang.org/x/text v0.33.0 // indirect\n\tgolang.org/x/time v0.14.0 // indirect\n\tgolang.org/x/tools v0.40.0 // indirect\n\tgoogle.golang.org/protobuf v1.36.11 // indirect\n\tgopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect\n\tgopkg.in/ini.v1 v1.67.0 // indirect\n\tgopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n\tmoul.io/http2curl/v2 v2.3.0 // indirect\n)\n"
  },
  {
    "path": "_examples/testing/ginkgotest/go.sum",
    "content": "github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=\ngithub.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=\ngithub.com/CloudyKit/jet/v6 v6.3.1 h1:6IAo5Cx21xrHVaR8zzXN5gJatKV/wO7Nf6bfCnCSbUw=\ngithub.com/CloudyKit/jet/v6 v6.3.1/go.mod h1:lf8ksdNsxZt7/yH/3n4vJQWA9RUq4wpaHtArHhGVMOw=\ngithub.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc=\ngithub.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=\ngithub.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=\ngithub.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 h1:dG7/gLroJGht/jSQtHiLvT48Hxn+crbmvyItZC8cWOs=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05/go.mod h1:NYezi6wtnJtBm5btoprXc5SvAdqH0XTXWnUup0MptAI=\ngithub.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=\ngithub.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=\ngithub.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=\ngithub.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=\ngithub.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=\ngithub.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=\ngithub.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=\ngithub.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=\ngithub.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=\ngithub.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=\ngithub.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=\ngithub.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=\ngithub.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw=\ngithub.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8=\ngithub.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=\ngithub.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=\ngithub.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=\ngithub.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=\ngithub.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=\ngithub.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=\ngithub.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=\ngithub.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=\ngithub.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=\ngithub.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=\ngithub.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=\ngithub.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a h1:l7A0loSszR5zHd/qK53ZIHMO8b3bBSmENnQ6eKnUT0A=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=\ngithub.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=\ngithub.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=\ngithub.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=\ngithub.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=\ngithub.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=\ngithub.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=\ngithub.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=\ngithub.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=\ngithub.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2 h1:T9THsdP1woyAqKHwjkEsbCnMefsAFvk8iJJKokcJ3Go=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2/go.mod h1:JLDgIqnFy5loDSUv1OA2j0mb6p/rDhiCqigP22Uq9xE=\ngithub.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw=\ngithub.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/kataras/blocks v0.0.8 h1:MrpVhoFTCR2v1iOOfGng5VJSILKeZZI+7NGfxEh3SUM=\ngithub.com/kataras/blocks v0.0.8/go.mod h1:9Jm5zx6BB+06NwA+OhTbHW1xkMOYxahnqTN5DveZ2Yg=\ngithub.com/kataras/golog v0.1.12 h1:Bu7I/G4ilJlbfzjmU39O9N+2uO1pBcMK045fzZ4ytNg=\ngithub.com/kataras/golog v0.1.12/go.mod h1:wrGSbOiBqbQSQznleVNX4epWM8rl9SJ/rmEacl0yqy4=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd h1:oMsQgqKACbUdlaIWnN+EZzzAnHkw90hE/y/R0BSij2U=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd/go.mod h1:yucjtDl9Vn3OctWM1fi0MuM9OckemC7kEreFa9QtPF8=\ngithub.com/kataras/pio v0.0.13 h1:x0rXVX0fviDTXOOLOmr4MUxOabu1InVSTu5itF8CXCM=\ngithub.com/kataras/pio v0.0.13/go.mod h1:k3HNuSw+eJ8Pm2lA4lRhg3DiCjVgHlP8hmXApSej3oM=\ngithub.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY=\ngithub.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4=\ngithub.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA=\ngithub.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw=\ngithub.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=\ngithub.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=\ngithub.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=\ngithub.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8=\ngithub.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=\ngithub.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=\ngithub.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=\ngithub.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=\ngithub.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=\ngithub.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=\ngithub.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=\ngithub.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=\ngithub.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY=\ngithub.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc=\ngithub.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=\ngithub.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=\ngithub.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU=\ngithub.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM=\ngithub.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=\ngithub.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M=\ngithub.com/pkg/diff v0.0.0-20200914180035-5b29258ca4f7/go.mod h1:zO8QMzTeZd5cpnIkz/Gn6iK0jDfGicM1nynOkkPIl28=\ngithub.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=\ngithub.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=\ngithub.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=\ngithub.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=\ngithub.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=\ngithub.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=\ngithub.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk=\ngithub.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=\ngithub.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=\ngithub.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=\ngithub.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=\ngithub.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502/go.mod h1:p9lPsd+cx33L3H9nNoecRRxPssFKUwwI50I3pZ0yT+8=\ngithub.com/tdewolff/minify/v2 v2.24.8 h1:58/VjsbevI4d5FGV0ZSuBrHMSSkH4MCH0sIz/eKIauE=\ngithub.com/tdewolff/minify/v2 v2.24.8/go.mod h1:0Ukj0CRpo/sW/nd8uZ4ccXaV1rEVIWA3dj8U7+Shhfw=\ngithub.com/tdewolff/parse/v2 v2.8.5 h1:ZmBiA/8Do5Rpk7bDye0jbbDUpXXbCdc3iah4VeUvwYU=\ngithub.com/tdewolff/parse/v2 v2.8.5/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=\ngithub.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE=\ngithub.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=\ngithub.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=\ngithub.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=\ngithub.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=\ngithub.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=\ngithub.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=\ngithub.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=\ngithub.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA=\ngithub.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0=\ngithub.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=\ngithub.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=\ngithub.com/yudai/pp v2.0.1+incompatible h1:Q4//iY4pNF6yPLZIigmvcl7k/bPgrcTPIFIcmawg5bI=\ngithub.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=\ngithub.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=\ngolang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=\ngolang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=\ngolang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=\ngolang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=\ngolang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=\ngolang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=\ngolang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=\ngolang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20201211185031-d93e913c1a58/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=\ngolang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA=\ngolang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=\ngoogle.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=\ngopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nmoul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs=\nmoul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE=\n"
  },
  {
    "path": "_examples/testing/ginkgotest/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/middleware/basicauth\"\n)\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\n\topts := basicauth.Options{\n\t\tAllow: basicauth.AllowUsers(map[string]string{\"myusername\": \"mypassword\"}),\n\t}\n\n\tauthentication := basicauth.New(opts) // or just: basicauth.Default(map...)\n\n\tapp.Get(\"/\", func(ctx iris.Context) { ctx.Redirect(\"/admin\") })\n\n\t// to party\n\n\tneedAuth := app.Party(\"/admin\", authentication)\n\t{\n\t\t//http://localhost:8080/admin\n\t\tneedAuth.Get(\"/\", h)\n\t\t// http://localhost:8080/admin/profile\n\t\tneedAuth.Get(\"/profile\", h)\n\n\t\t// http://localhost:8080/admin/settings\n\t\tneedAuth.Get(\"/settings\", h)\n\t}\n\n\treturn app\n}\n\nfunc h(ctx iris.Context) {\n\tusername, password, _ := ctx.Request().BasicAuth()\n\t// third parameter it will be always true because the middleware\n\t// makes sure for that, otherwise this handler will not be executed.\n\t// OR:\n\t//\n\t// user := ctx.User().(*myUserType)\n\t// ctx.Writef(\"%s %s:%s\", ctx.Path(), user.Username, user.Password)\n\t// OR if you don't have registered custom User structs:\n\t//\n\t// ctx.User().GetUsername()\n\t// ctx.User().GetPassword()\n\tctx.Writef(\"%s %s:%s\", ctx.Path(), username, password)\n}\n\nfunc main() {\n\tapp := newApp()\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/testing/ginkgotest/main_test.go",
    "content": "package main_test\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/httptest\"\n\t\"github.com/kataras/iris/v12/middleware/basicauth\"\n\t. \"github.com/onsi/ginkgo/v2\"\n\t. \"github.com/onsi/gomega\"\n)\n\nvar _ = Describe(\"Ginkgotest\", func() {\n\tvar (\n\t\te              *httptest.Expect\n\t\tapp            *iris.Application\n\t\topts           basicauth.Options\n\t\tauthentication iris.Handler // or just: basicauth.Default(map...)\n\t)\n\n\tBeforeEach(func() {\n\t\topts = basicauth.Options{\n\t\t\tAllow: basicauth.AllowUsers(map[string]string{\"myusername\": \"mypassword\"}),\n\t\t}\n\t\tauthentication = basicauth.New(opts)\n\t\tapp = newApp(authentication)\n\t\te = httptest.New(GinkgoT(), app, httptest.Strict(true))\n\t})\n\n\tWhen(\"no basic auth\", Ordered, func() {\n\t\tIt(\"redirects to /admin without basic auth\", func() {\n\t\t\tresponse := e.GET(\"/\").Expect().Raw()\n\t\t\tExpect(httptest.StatusUnauthorized).To(Equal(response.StatusCode))\n\t\t})\n\t\tIt(\"without basic auth\", func() {\n\t\t\t// without basic auth\n\t\t\tresponse := e.GET(\"/\").Expect().Raw()\n\t\t\tExpect(httptest.StatusUnauthorized).To(Equal(response.StatusCode))\n\t\t})\n\n\t})\n\n\tWhen(\"valid basic auth\", func() {\n\t\tIt(\"with basic auth /admin\", func() {\n\t\t\texpect := e.GET(\"/admin\").WithBasicAuth(\"myusername\", \"mypassword\").Expect()\n\t\t\tExpect(httptest.StatusOK).To(Equal(expect.Raw().StatusCode))\n\t\t\tExpect(\"/admin myusername:mypassword\").To(Equal(expect.Body().Raw()))\n\t\t})\n\t\tIt(\"with basic auth /admin/profile\", func() {\n\t\t\texpect := e.GET(\"/admin/profile\").WithBasicAuth(\"myusername\", \"mypassword\").Expect()\n\t\t\tExpect(httptest.StatusOK).To(Equal(expect.Raw().StatusCode))\n\t\t\tExpect(\"/admin/profile myusername:mypassword\").To(Equal(expect.Body().Raw()))\n\t\t})\n\t\tIt(\"with basic auth /admin/profile\", func() {\n\t\t\texpect := e.GET(\"/admin/settings\").WithBasicAuth(\"myusername\", \"mypassword\").Expect()\n\t\t\tExpect(httptest.StatusOK).To(Equal(expect.Raw().StatusCode))\n\t\t\tExpect(\"/admin/settings myusername:mypassword\").To(Equal(expect.Body().Raw()))\n\t\t})\n\t})\n\n\tWhen(\"invalid basic auth\", func() {\n\t\tIt(\"invalid basic auth /admin/settings\", func() {\n\t\t\texpect := e.GET(\"/admin/settings\").WithBasicAuth(\"invalidusername\", \"invalidpassword\").Expect()\n\t\t\tExpect(httptest.StatusUnauthorized).To(Equal(expect.Raw().StatusCode))\n\t\t})\n\t})\n})\n"
  },
  {
    "path": "_examples/testing/httptest/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/middleware/basicauth\"\n)\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\n\topts := basicauth.Options{\n\t\tAllow: basicauth.AllowUsers(map[string]string{\"myusername\": \"mypassword\"}),\n\t}\n\n\tauthentication := basicauth.New(opts) // or just: basicauth.Default(map...)\n\n\tapp.Get(\"/\", func(ctx iris.Context) { ctx.Redirect(\"/admin\") })\n\n\t// to party\n\n\tneedAuth := app.Party(\"/admin\", authentication)\n\t{\n\t\t//http://localhost:8080/admin\n\t\tneedAuth.Get(\"/\", h)\n\t\t// http://localhost:8080/admin/profile\n\t\tneedAuth.Get(\"/profile\", h)\n\n\t\t// http://localhost:8080/admin/settings\n\t\tneedAuth.Get(\"/settings\", h)\n\t}\n\n\treturn app\n}\n\nfunc h(ctx iris.Context) {\n\tusername, password, _ := ctx.Request().BasicAuth()\n\t// third parameter it will be always true because the middleware\n\t// makes sure for that, otherwise this handler will not be executed.\n\t// OR:\n\t//\n\t// user := ctx.User().(*myUserType)\n\t// ctx.Writef(\"%s %s:%s\", ctx.Path(), user.Username, user.Password)\n\t// OR if you don't have registered custom User structs:\n\t//\n\t// ctx.User().GetUsername()\n\t// ctx.User().GetPassword()\n\tctx.Writef(\"%s %s:%s\", ctx.Path(), username, password)\n}\n\nfunc main() {\n\tapp := newApp()\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/testing/httptest/main_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\n// $ go test -v\nfunc TestNewApp(t *testing.T) {\n\tapp := newApp()\n\te := httptest.New(t, app, httptest.Strict(true))\n\n\t// redirects to /admin without basic auth\n\te.GET(\"/\").Expect().Status(httptest.StatusUnauthorized)\n\t// without basic auth\n\te.GET(\"/admin\").Expect().Status(httptest.StatusUnauthorized)\n\n\t// with valid basic auth\n\te.GET(\"/admin\").WithBasicAuth(\"myusername\", \"mypassword\").Expect().\n\t\tStatus(httptest.StatusOK).Body().IsEqual(\"/admin myusername:mypassword\")\n\te.GET(\"/admin/profile\").WithBasicAuth(\"myusername\", \"mypassword\").Expect().\n\t\tStatus(httptest.StatusOK).Body().IsEqual(\"/admin/profile myusername:mypassword\")\n\te.GET(\"/admin/settings\").WithBasicAuth(\"myusername\", \"mypassword\").Expect().\n\t\tStatus(httptest.StatusOK).Body().IsEqual(\"/admin/settings myusername:mypassword\")\n\n\t// with invalid basic auth\n\te.GET(\"/admin/settings\").WithBasicAuth(\"invalidusername\", \"invalidpassword\").\n\t\tExpect().Status(httptest.StatusUnauthorized)\n}\n\nfunc TestHandlerUsingNetHTTP(t *testing.T) {\n\thandler := func(ctx iris.Context) {\n\t\tctx.WriteString(\"Hello, World!\")\n\t}\n\n\t// A shortcut for net/http/httptest.NewRecorder/NewRequest.\n\tw := httptest.NewRecorder()\n\tr := httptest.NewRequest(\"GET\", \"/\", nil)\n\n\thttptest.Do(w, r, handler)\n\tif expected, got := \"Hello, World!\", w.Body.String(); expected != got {\n\t\tt.Fatalf(\"expected body: %s but got: %s\", expected, got)\n\t}\n}\n"
  },
  {
    "path": "_examples/url-shortener/README.md",
    "content": "## A URL Shortener Service using Go, Iris and Bolt\n\nHackernoon Article: https://medium.com/hackernoon/a-url-shortener-service-using-go-iris-and-bolt-4182f0b00ae7"
  },
  {
    "path": "_examples/url-shortener/factory.go",
    "content": "package main\n\nimport (\n\t\"net/url\"\n\n\t\"github.com/google/uuid\"\n)\n\n// Generator the type to generate keys(short urls)\ntype Generator func() string\n\n// DefaultGenerator is the defautl url generator\nvar DefaultGenerator = func() string {\n\tid, _ := uuid.NewRandom()\n\treturn id.String()\n}\n\n// Factory is responsible to generate keys(short urls)\ntype Factory struct {\n\tstore     Store\n\tgenerator Generator\n}\n\n// NewFactory receives a generator and a store and returns a new url Factory.\nfunc NewFactory(generator Generator, store Store) *Factory {\n\treturn &Factory{\n\t\tstore:     store,\n\t\tgenerator: generator,\n\t}\n}\n\n// Gen generates the key.\nfunc (f *Factory) Gen(uri string) (key string, err error) {\n\t// we don't return the parsed url because #hash are converted to uri-compatible\n\t// and we don't want to encode/decode all the time, there is no need for that,\n\t// we save the url as the user expects if the uri validation passed.\n\t_, err = url.ParseRequestURI(uri)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tkey = f.generator()\n\t// Make sure that the key is unique\n\tfor {\n\t\tif v := f.store.Get(key); v == \"\" {\n\t\t\tbreak\n\t\t}\n\t\tkey = f.generator()\n\t}\n\n\treturn key, nil\n}\n"
  },
  {
    "path": "_examples/url-shortener/main.go",
    "content": "// Package main shows how you can create a simple URL Shortener.\n//\n// Article: https://medium.com/@kataras/a-url-shortener-service-using-go-iris-and-bolt-4182f0b00ae7\n//\n// $ go get go.etcd.io/bbolt/...\n// $ go get github.com/google/uuid\n// $ cd $GOPATH/src/github.com/kataras/iris/_examples/url-shortener\n// $ go build -mod=mod\n// $ ./url-shortener\npackage main\n\nimport (\n\t\"fmt\"\n\t\"html/template\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\t// assign a variable to the DB so we can use its features later.\n\tdb := NewDB(\"shortener.db\")\n\t// Pass that db to our app, in order to be able to test the whole app with a different database later on.\n\tapp := newApp(db)\n\n\t// release the \"db\" connection when server goes off.\n\tiris.RegisterOnInterrupt(db.Close)\n\n\tapp.Listen(\":8080\")\n}\n\nfunc newApp(db *DB) *iris.Application {\n\tapp := iris.Default() // or app := iris.New()\n\n\t// create our factory, which is the manager for the object creation.\n\t// between our web app and the db.\n\tfactory := NewFactory(DefaultGenerator, db)\n\n\t// serve the \"./templates\" directory's \"*.html\" files with the HTML std view engine.\n\ttmpl := iris.HTML(\"./templates\", \".html\").Reload(true)\n\t// register any template func(s) here.\n\t//\n\t// Look ./templates/index.html#L16\n\ttmpl.AddFunc(\"IsPositive\", func(n int) bool {\n\t\treturn n > 0\n\t})\n\n\tapp.RegisterView(tmpl)\n\n\t// Serve static files (css)\n\tapp.HandleDir(\"/static\", iris.Dir(\"./resources\"))\n\n\tindexHandler := func(ctx iris.Context) {\n\t\tctx.ViewData(\"URL_COUNT\", db.Len())\n\t\tif err := ctx.View(\"index.html\"); err != nil {\n\t\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\t\treturn\n\t\t}\n\t}\n\tapp.Get(\"/\", indexHandler)\n\n\t// find and execute a short url by its key\n\t// used on http://localhost:8080/u/dsaoj41u321dsa\n\texecShortURL := func(ctx iris.Context, key string) {\n\t\tif key == \"\" {\n\t\t\tctx.StatusCode(iris.StatusBadRequest)\n\t\t\treturn\n\t\t}\n\n\t\tvalue := db.Get(key)\n\t\tif value == \"\" {\n\t\t\tctx.StatusCode(iris.StatusNotFound)\n\t\t\tctx.Writef(\"Short URL for key: '%s' not found\", key)\n\t\t\treturn\n\t\t}\n\n\t\tctx.Redirect(value, iris.StatusBadGateway)\n\t}\n\tapp.Get(\"/u/{shortkey}\", func(ctx iris.Context) {\n\t\texecShortURL(ctx, ctx.Params().Get(\"shortkey\"))\n\t})\n\n\tapp.Post(\"/shorten\", func(ctx iris.Context) {\n\t\tformValue := ctx.FormValue(\"url\")\n\t\tif formValue == \"\" {\n\t\t\tctx.ViewData(\"FORM_RESULT\", \"You need to a enter a URL\")\n\t\t\tctx.StatusCode(iris.StatusLengthRequired)\n\t\t} else {\n\t\t\tkey, err := factory.Gen(formValue)\n\t\t\tif err != nil {\n\t\t\t\tctx.ViewData(\"FORM_RESULT\", \"Invalid URL\")\n\t\t\t\tctx.StatusCode(iris.StatusBadRequest)\n\t\t\t} else {\n\t\t\t\tif err = db.Set(key, formValue); err != nil {\n\t\t\t\t\tctx.ViewData(\"FORM_RESULT\", \"Internal error while saving the URL\")\n\t\t\t\t\tapp.Logger().Infof(\"while saving URL: \" + err.Error())\n\t\t\t\t\tctx.StatusCode(iris.StatusInternalServerError)\n\t\t\t\t} else {\n\t\t\t\t\tctx.StatusCode(iris.StatusOK)\n\t\t\t\t\tshortenURL := \"http://\" + app.ConfigurationReadOnly().GetVHost() + \"/u/\" + key\n\t\t\t\t\tctx.ViewData(\"FORM_RESULT\",\n\t\t\t\t\t\ttemplate.HTML(\"<pre><a target='_new' href='\"+shortenURL+\"'>\"+shortenURL+\" </a></pre>\"))\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tindexHandler(ctx) // no redirect, we need the FORM_RESULT.\n\t})\n\n\tapp.Post(\"/clear_cache\", func(ctx iris.Context) {\n\t\tdb.Clear()\n\t\tctx.Redirect(\"/\")\n\t})\n\n\treturn app\n}\n"
  },
  {
    "path": "_examples/url-shortener/main_test.go",
    "content": "package main\n\nimport (\n\t\"os\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\n// TestURLShortener tests the simple tasks of our url shortener application.\n// Note that it's a pure test.\n// The rest possible checks is up to you, take it as as an exercise!\nfunc TestURLShortener(t *testing.T) {\n\t// temp db file\n\tf, err := os.CreateTemp(\"\", \"shortener\")\n\tif err != nil {\n\t\tt.Fatalf(\"creating temp file for database failed: %v\", err)\n\t}\n\n\tdb := NewDB(f.Name())\n\tapp := newApp(db)\n\n\te := httptest.New(t, app)\n\toriginalURL := \"https://google.com\"\n\n\t// save\n\te.POST(\"/shorten\").\n\t\tWithFormField(\"url\", originalURL).Expect().\n\t\tStatus(httptest.StatusOK).Body().Contains(\"<pre><a target='_new' href=\")\n\n\tkeys := db.GetByValue(originalURL)\n\tif got := len(keys); got != 1 {\n\t\tt.Fatalf(\"expected to have 1 key but saved %d short urls\", got)\n\t}\n\n\t// get\n\te.GET(\"/u/\" + keys[0]).Expect().\n\t\tStatus(httptest.StatusBadGateway).Header(\"Location\").Equal(originalURL)\n\n\t// save the same again, it should add a new key\n\te.POST(\"/shorten\").\n\t\tWithFormField(\"url\", originalURL).Expect().\n\t\tStatus(httptest.StatusOK).Body().Contains(\"<pre><a target='_new' href=\")\n\n\tkeys2 := db.GetByValue(originalURL)\n\tif got := len(keys2); got != 1 {\n\t\tt.Fatalf(\"expected to have 1 keys even if we save the same original url but saved %d short urls\", got)\n\t} // the key is the same, so only the first one matters.\n\n\tif keys[0] != keys2[0] {\n\t\tt.Fatalf(\"expected keys to be equal if the original url is the same, but got %s = %s \", keys[0], keys2[0])\n\t}\n\n\t// clear db\n\te.POST(\"/clear_cache\").Expect().Status(httptest.StatusOK)\n\tif got := db.Len(); got != 0 {\n\t\tt.Fatalf(\"expected database to have 0 registered objects after /clear_cache but has %d\", got)\n\t}\n\n\t// give it some time to release the db connection\n\tdb.Close()\n\ttime.Sleep(1 * time.Second)\n\t// close the file\n\tif err := f.Close(); err != nil {\n\t\tt.Fatalf(\"unable to close the file: %s\", f.Name())\n\t}\n\n\t// and remove the file\n\tif err := os.Remove(f.Name()); err != nil {\n\t\tt.Fatalf(\"unable to remove the file from %s\", f.Name())\n\t}\n\n\ttime.Sleep(1 * time.Second)\n}\n"
  },
  {
    "path": "_examples/url-shortener/resources/css/style.css",
    "content": "body{\n    background-color:silver;\n}"
  },
  {
    "path": "_examples/url-shortener/store.go",
    "content": "package main\n\nimport (\n\t\"bytes\"\n\n\tbolt \"go.etcd.io/bbolt\"\n)\n\n// Panic panics, change it if you don't want to panic on critical INITIALIZE-ONLY-ERRORS\nvar Panic = func(v any) {\n\tpanic(v)\n}\n\n// Store is the store interface for urls.\n// Note: no Del functionality.\ntype Store interface {\n\tSet(key string, value string) error // error if something went wrong\n\tGet(key string) string              // empty value if not found\n\tLen() int                           // should return the number of all the records/tables/buckets\n\tClose()                             // release the store or ignore\n}\n\nvar tableURLs = []byte(\"urls\")\n\n// DB representation of a Store.\n// Only one table/bucket which contains the urls, so it's not a fully Database,\n// it works only with single bucket because that all we need.\ntype DB struct {\n\tdb *bolt.DB\n}\n\nvar _ Store = &DB{}\n\n// openDatabase open a new database connection\n// and returns its instance.\nfunc openDatabase(stumb string) *bolt.DB {\n\t// Open the data(base) file in the current working directory.\n\t// It will be created if it doesn't exist.\n\tdb, err := bolt.Open(stumb, 0600, nil)\n\tif err != nil {\n\t\tPanic(err)\n\t}\n\n\t// create the buckets here\n\ttables := [...][]byte{\n\t\ttableURLs,\n\t}\n\n\tdb.Update(func(tx *bolt.Tx) (err error) {\n\t\tfor _, table := range tables {\n\t\t\t_, err = tx.CreateBucketIfNotExists(table)\n\t\t\tif err != nil {\n\t\t\t\tPanic(err)\n\t\t\t}\n\t\t}\n\n\t\treturn\n\t})\n\n\treturn db\n}\n\n// NewDB returns a new DB instance, its connection is opened.\n// DB implements the Store.\nfunc NewDB(stumb string) *DB {\n\treturn &DB{\n\t\tdb: openDatabase(stumb),\n\t}\n}\n\n// Set sets a shorten url and its key\n// Note: Caller is responsible to generate a key.\nfunc (d *DB) Set(key string, value string) error {\n\treturn d.db.Update(func(tx *bolt.Tx) error {\n\t\tb, err := tx.CreateBucketIfNotExists(tableURLs)\n\t\t// Generate ID for the url\n\t\t// Note: we could use that instead of a random string key\n\t\t// but we want to simulate a real-world url shortener\n\t\t// so we skip that.\n\t\t// id, _ := b.NextSequence()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tk := []byte(key)\n\t\tvalueB := []byte(value)\n\t\tc := b.Cursor()\n\n\t\tfound := false\n\t\tfor k, v := c.First(); k != nil; k, v = c.Next() {\n\t\t\tif bytes.Equal(valueB, v) {\n\t\t\t\tfound = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\t// if value already exists don't re-put it.\n\t\tif found {\n\t\t\treturn nil\n\t\t}\n\n\t\treturn b.Put(k, []byte(value))\n\t})\n}\n\n// Clear clears all the database entries for the table urls.\nfunc (d *DB) Clear() error {\n\treturn d.db.Update(func(tx *bolt.Tx) error {\n\t\treturn tx.DeleteBucket(tableURLs)\n\t})\n}\n\n// Get returns a url by its key.\n//\n// Returns an empty string if not found.\nfunc (d *DB) Get(key string) (value string) {\n\tkeyB := []byte(key)\n\td.db.Update(func(tx *bolt.Tx) error {\n\t\tb := tx.Bucket(tableURLs)\n\t\tif b == nil {\n\t\t\treturn nil\n\t\t}\n\t\tc := b.Cursor()\n\t\tfor k, v := c.First(); k != nil; k, v = c.Next() {\n\t\t\tif bytes.Equal(keyB, k) {\n\t\t\t\tvalue = string(v)\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\treturn nil\n\t})\n\n\treturn\n}\n\n// GetByValue returns all keys for a specific (original) url value.\nfunc (d *DB) GetByValue(value string) (keys []string) {\n\tvalueB := []byte(value)\n\td.db.Update(func(tx *bolt.Tx) error {\n\t\tb := tx.Bucket(tableURLs)\n\t\tif b == nil {\n\t\t\treturn nil\n\t\t}\n\t\tc := b.Cursor()\n\t\t// first for the bucket's table \"urls\"\n\t\tfor k, v := c.First(); k != nil; k, v = c.Next() {\n\t\t\tif bytes.Equal(valueB, v) {\n\t\t\t\tkeys = append(keys, string(k))\n\t\t\t}\n\t\t}\n\n\t\treturn nil\n\t})\n\n\treturn\n}\n\n// Len returns all the \"shorted\" urls length\nfunc (d *DB) Len() (num int) {\n\td.db.View(func(tx *bolt.Tx) error {\n\t\t// Assume bucket exists and has keys\n\t\tb := tx.Bucket(tableURLs)\n\t\tif b == nil {\n\t\t\treturn nil\n\t\t}\n\n\t\tb.ForEach(func([]byte, []byte) error {\n\t\t\tnum++\n\t\t\treturn nil\n\t\t})\n\t\treturn nil\n\t})\n\treturn\n}\n\n// Close shutdowns the data(base) connection.\nfunc (d *DB) Close() {\n\tif err := d.db.Close(); err != nil {\n\t\tPanic(err)\n\t}\n}\n"
  },
  {
    "path": "_examples/url-shortener/templates/index.html",
    "content": "<html>\n\n<head>\n    <meta charset=\"utf-8\">\n    <title>Golang URL Shortener</title>\n    <link rel=\"stylesheet\" href=\"/static/css/style.css\" />\n</head>\n\n<body>\n    <h2>Golang URL Shortener</h2>\n    <h3>{{ .FORM_RESULT}}</h3>\n    <form action=\"/shorten\" method=\"POST\">\n        <input type=\"text\" name=\"url\" style=\"width: 35em;\" />\n        <input type=\"submit\" value=\"Shorten!\" />\n    </form>\n    {{ if IsPositive .URL_COUNT }}\n        <p>{{ .URL_COUNT }} URLs shortened</p>\n    {{ end }}\n\n    <form action=\"/clear_cache\" method=\"POST\">\n        <input type=\"submit\" value=\"Clear DB\" />\n    </form>\n</body>\n\n</html>"
  },
  {
    "path": "_examples/view/context-view-data/main.go",
    "content": "package main\n\nimport (\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nconst (\n\tDefaultTitle  = \"My Awesome Site\"\n\tDefaultLayout = \"layouts/layout.html\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\t// output startup banner and error logs on os.Stdout\n\n\t// set the view engine target to ./templates folder\n\tapp.RegisterView(iris.HTML(\"./templates\", \".html\").Reload(true))\n\n\tapp.Use(func(ctx iris.Context) {\n\t\t// set the title, current time and a layout in order to be used if and when the next handler(s) calls the .Render function\n\t\tctx.ViewData(\"Title\", DefaultTitle)\n\t\tnow := time.Now().Format(ctx.Application().ConfigurationReadOnly().GetTimeFormat())\n\t\tctx.ViewData(\"CurrentTime\", now)\n\t\tctx.ViewLayout(DefaultLayout)\n\n\t\tctx.Next()\n\t})\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.ViewData(\"BodyMessage\", \"a sample text here... set by the route handler\")\n\t\tif err := ctx.View(\"index.html\"); err != nil {\n\t\t\tctx.Application().Logger().Infof(err.Error())\n\t\t}\n\t})\n\n\tapp.Get(\"/about\", func(ctx iris.Context) {\n\t\tctx.ViewData(\"Title\", \"My About Page\")\n\t\tctx.ViewData(\"BodyMessage\", \"about text here... set by the route handler\")\n\n\t\t// same file, just to keep things simple.\n\t\tif err := ctx.View(\"index.html\"); err != nil {\n\t\t\tctx.Application().Logger().Infof(err.Error())\n\t\t}\n\t})\n\n\t// http://localhost:8080\n\t// http://localhost:8080/about\n\tapp.Listen(\":8080\")\n}\n\n// Notes: ViewData(\"\", myCustomStruct{}) will set this myCustomStruct value as a root binding data,\n// so any View(\"other\", \"otherValue\") will probably fail.\n// To clear the binding data: ctx.Set(ctx.Application().ConfigurationReadOnly().GetViewDataContextKey(), nil)\n"
  },
  {
    "path": "_examples/view/context-view-data/templates/index.html",
    "content": "<h1>\r\n\tTitle: {{.Title}}\r\n</h1>\r\n<h3>{{.BodyMessage}} </h3>\r\n\r\n<hr/>\r\n\r\nCurrent time: {{.CurrentTime}}"
  },
  {
    "path": "_examples/view/context-view-data/templates/layouts/layout.html",
    "content": "<html>\r\n<head>\r\n<title>My WebsiteLayout</title>\r\n\r\n</head>\r\n<body>\r\n\t<!-- Render the current template here -->\r\n\t{{ yield . }}\r\n</body>\r\n</html>\r\n"
  },
  {
    "path": "_examples/view/context-view-engine/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\t// Register a root view engine, as usual,\n\t// will be used to render files through Context.View method\n\t// when no Party or Handler-specific view engine is available.\n\tapp.RegisterView(iris.Blocks(\"./views/public\", \".html\"))\n\n\t// http://localhost:8080\n\tapp.Get(\"/\", index)\n\n\t// Register a view engine per group of routes.\n\tadminGroup := app.Party(\"/admin\")\n\tadminGroup.RegisterView(iris.Blocks(\"./views/admin\", \".html\"))\n\n\t// http://localhost:8080/admin\n\tadminGroup.Get(\"/\", admin)\n\n\t// Register a view engine on-fly for the current chain of handlers.\n\tviews := iris.Blocks(\"./views/on-fly\", \".html\")\n\tif err := views.Load(); err != nil {\n\t\tapp.Logger().Fatal(err)\n\t}\n\n\t// http://localhost:8080/on-fly\n\tapp.Get(\"/on-fly\", setViews(views), onFly)\n\n\tapp.Listen(\":8080\")\n}\n\nfunc index(ctx iris.Context) {\n\tdata := iris.Map{\n\t\t\"Title\": \"Public Index Title\",\n\t}\n\n\tctx.ViewLayout(\"main\")\n\tif err := ctx.View(\"index\", data); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n}\n\nfunc admin(ctx iris.Context) {\n\tdata := iris.Map{\n\t\t\"Title\": \"Admin Panel\",\n\t}\n\n\tctx.ViewLayout(\"main\")\n\tif err := ctx.View(\"index\", data); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n}\n\nfunc setViews(views iris.ViewEngine) iris.Handler {\n\treturn func(ctx iris.Context) {\n\t\tctx.ViewEngine(views)\n\t\tctx.Next()\n\t}\n}\n\nfunc onFly(ctx iris.Context) {\n\tdata := iris.Map{\n\t\t\"Message\": \"View engine changed through 'setViews' custom middleware.\",\n\t}\n\n\tif err := ctx.View(\"index\", data); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "_examples/view/context-view-engine/views/admin/index.html",
    "content": "{{ define \"content\" }}\n<h1>Hello, Admin!</h1>\n{{ end }}"
  },
  {
    "path": "_examples/view/context-view-engine/views/admin/layouts/main.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>{{ .Title }}</title>\n</head>\n<body>\n    {{ template \"content\" .}}\n\n\n    <h4>Copyright &copy; 2022 Admin</h4>\n</body>\n</html>"
  },
  {
    "path": "_examples/view/context-view-engine/views/on-fly/index.html",
    "content": "<h1>On-fly</h1>\n<h3>{{.Message}}</h3>"
  },
  {
    "path": "_examples/view/context-view-engine/views/public/500.html",
    "content": "<!-- You can define more than one block.\nThe default one is \"content\" which should be the main template's body.\nSo, even if it's missing (see index.html), it's added automatically by the view engine.\nWhen you need to define more than one block, you have to be more specific:\n-->\n{{ define \"content\" }}\n<h1>Internal Server Error</h1>\n{{ end }}\n\n{{ define \"message\" }}\n<p style=\"color:red;\">{{.Message}}</p>\n{{ end }}"
  },
  {
    "path": "_examples/view/context-view-engine/views/public/index.html",
    "content": "<h1>Index Body</h1>"
  },
  {
    "path": "_examples/view/context-view-engine/views/public/layouts/error.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>{{.Code}}</title>\n</head>\n<body>\n    {{ template \"content\" .}}\n\n    {{block \"message\" .}}{{end}}\n</body>\n</html>"
  },
  {
    "path": "_examples/view/context-view-engine/views/public/layouts/main.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>{{ if .Title }}{{ .Title }}{{ else }}Default Main Title{{ end }}</title>\n</head>\n<body>\n    {{ template \"content\" . }}\n\n<footer>{{ partial \"partials/footer\" . }}</footer>\n</body>\n</html>"
  },
  {
    "path": "_examples/view/context-view-engine/views/public/partials/footer.html",
    "content": "<h3>Footer Partial</h3>"
  },
  {
    "path": "_examples/view/embedding-templates-into-app/embedded/templates/layouts/layout.html",
    "content": "<html>\n<head>\n<title>Layout</title>\n\n</head>\n<body>\n\t<h1>This is the global layout</h1>\n\t<br />\n\t<!-- Render the current template here -->\n\t{{ yield . }}\n</body>\n</html>\n"
  },
  {
    "path": "_examples/view/embedding-templates-into-app/embedded/templates/layouts/mylayout.html",
    "content": "<html>\n<head>\n<title>my Layout</title>\n\n</head>\n<body>\n\t<h1>This is the layout for the /my/ and /my/other routes only</h1>\n\t<br />\n\t<!-- Render the current template here -->\n\t{{ yield . }}\n</body>\n</html>\n"
  },
  {
    "path": "_examples/view/embedding-templates-into-app/embedded/templates/page1.html",
    "content": "<div style=\"background-color: black; color: blue\">\n\n\t<h1>Page 1 {{ greet \"iris developer\"}}</h1>\n\n\t{{ render \"partials/page1_partial1.html\" . }}\n\n</div>\n"
  },
  {
    "path": "_examples/view/embedding-templates-into-app/embedded/templates/partials/page1_partial1.html",
    "content": "<div style=\"background-color: white; color: red\">\n\t<h1>Page 1's Partial 1</h1>\n</div>\n"
  },
  {
    "path": "_examples/view/embedding-templates-into-app/main.go",
    "content": "package main\n\nimport (\n\t\"embed\"\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\n//go:embed embedded/*\nvar embeddedFS embed.FS\n\nfunc main() {\n\tapp := iris.New()\n\n\ttmpl := iris.HTML(embeddedFS, \".html\").RootDir(\"embedded/templates\")\n\n\ttmpl.Layout(\"layouts/layout.html\")\n\ttmpl.AddFunc(\"greet\", func(s string) string {\n\t\treturn \"Greetings \" + s + \"!\"\n\t})\n\n\tapp.RegisterView(tmpl)\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tif err := ctx.View(\"page1.html\"); err != nil {\n\t\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\t\treturn\n\t\t}\n\t})\n\n\t// remove the layout for a specific route\n\tapp.Get(\"/nolayout\", func(ctx iris.Context) {\n\t\tctx.ViewLayout(iris.NoLayout)\n\t\tif err := ctx.View(\"page1.html\"); err != nil {\n\t\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\t\treturn\n\t\t}\n\t})\n\n\t// set a layout for a party, .Layout should be BEFORE any Get or other Handle party's method\n\tmy := app.Party(\"/my\").Layout(\"layouts/mylayout.html\")\n\t{ // both of these will use the layouts/mylayout.html as their layout.\n\t\tmy.Get(\"/\", func(ctx iris.Context) {\n\t\t\tif err := ctx.View(\"page1.html\"); err != nil {\n\t\t\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\t\t\treturn\n\t\t\t}\n\t\t})\n\t\tmy.Get(\"/other\", func(ctx iris.Context) {\n\t\t\tif err := ctx.View(\"page1.html\"); err != nil {\n\t\t\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\t\t\treturn\n\t\t\t}\n\t\t})\n\t}\n\n\t// http://localhost:8080\n\t// http://localhost:8080/nolayout\n\t// http://localhost:8080/my\n\t// http://localhost:8080/my/other\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/view/embedding-templates-into-app-bindata/bindata.go",
    "content": "// Code generated by go-bindata. (@generated) DO NOT EDIT.\n\n// Package main generated by go-bindata.// sources:\n// templates/layouts/layout.html\n// templates/layouts/mylayout.html\n// templates/page1.html\n// templates/partials/page1_partial1.html\npackage main\n\nimport (\n\t\"bytes\"\n\t\"compress/gzip\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"time\"\n)\n\nfunc bindataRead(data []byte, name string) ([]byte, error) {\n\tgz, err := gzip.NewReader(bytes.NewBuffer(data))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"read %q: %v\", name, err)\n\t}\n\n\tvar buf bytes.Buffer\n\t_, err = io.Copy(&buf, gz)\n\tclErr := gz.Close()\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"read %q: %v\", name, err)\n\t}\n\tif clErr != nil {\n\t\treturn nil, err\n\t}\n\n\treturn buf.Bytes(), nil\n}\n\ntype asset struct {\n\tbytes []byte\n\tinfo  os.FileInfo\n}\n\ntype bindataFileInfo struct {\n\tname    string\n\tsize    int64\n\tmode    os.FileMode\n\tmodTime time.Time\n}\n\n// Name return file name\nfunc (fi bindataFileInfo) Name() string {\n\treturn fi.name\n}\n\n// Size return file size\nfunc (fi bindataFileInfo) Size() int64 {\n\treturn fi.size\n}\n\n// Mode return file mode\nfunc (fi bindataFileInfo) Mode() os.FileMode {\n\treturn fi.mode\n}\n\n// ModTime return file modify time\nfunc (fi bindataFileInfo) ModTime() time.Time {\n\treturn fi.modTime\n}\n\n// IsDir return file whether a directory\nfunc (fi bindataFileInfo) IsDir() bool {\n\treturn fi.mode&os.ModeDir != 0\n}\n\n// Sys return file is sys mode\nfunc (fi bindataFileInfo) Sys() any {\n\treturn nil\n}\n\ntype assetFile struct {\n\t*bytes.Reader\n\tname            string\n\tchildInfos      []os.FileInfo\n\tchildInfoOffset int\n}\n\ntype assetOperator struct{}\n\n// Open implement http.FileSystem interface\nfunc (f *assetOperator) Open(name string) (http.File, error) {\n\tvar err error\n\tif len(name) > 0 && name[0] == '/' {\n\t\tname = name[1:]\n\t}\n\tcontent, err := Asset(name)\n\tif err == nil {\n\t\treturn &assetFile{name: name, Reader: bytes.NewReader(content)}, nil\n\t}\n\tchildren, err := AssetDir(name)\n\tif err == nil {\n\t\tchildInfos := make([]os.FileInfo, 0, len(children))\n\t\tfor _, child := range children {\n\t\t\tchildPath := filepath.Join(name, child)\n\t\t\tinfo, errInfo := AssetInfo(filepath.Join(name, child))\n\t\t\tif errInfo == nil {\n\t\t\t\tchildInfos = append(childInfos, info)\n\t\t\t} else {\n\t\t\t\tchildInfos = append(childInfos, newDirFileInfo(childPath))\n\t\t\t}\n\t\t}\n\t\treturn &assetFile{name: name, childInfos: childInfos}, nil\n\t} else {\n\t\t// If the error is not found, return an error that will\n\t\t// result in a 404 error. Otherwise the server returns\n\t\t// a 500 error for files not found.\n\t\tif strings.Contains(err.Error(), \"not found\") {\n\t\t\treturn nil, os.ErrNotExist\n\t\t}\n\t\treturn nil, err\n\t}\n}\n\n// Close no need do anything\nfunc (f *assetFile) Close() error {\n\treturn nil\n}\n\n// Readdir read dir's children file info\nfunc (f *assetFile) Readdir(count int) ([]os.FileInfo, error) {\n\tif len(f.childInfos) == 0 {\n\t\treturn nil, os.ErrNotExist\n\t}\n\tif count <= 0 {\n\t\treturn f.childInfos, nil\n\t}\n\tif f.childInfoOffset+count > len(f.childInfos) {\n\t\tcount = len(f.childInfos) - f.childInfoOffset\n\t}\n\toffset := f.childInfoOffset\n\tf.childInfoOffset += count\n\treturn f.childInfos[offset : offset+count], nil\n}\n\n// Stat read file info from asset item\nfunc (f *assetFile) Stat() (os.FileInfo, error) {\n\tif len(f.childInfos) != 0 {\n\t\treturn newDirFileInfo(f.name), nil\n\t}\n\treturn AssetInfo(f.name)\n}\n\n// newDirFileInfo return default dir file info\nfunc newDirFileInfo(name string) os.FileInfo {\n\treturn &bindataFileInfo{\n\t\tname:    name,\n\t\tsize:    0,\n\t\tmode:    os.FileMode(2147484068), // equal os.FileMode(0644)|os.ModeDir\n\t\tmodTime: time.Time{}}\n}\n\n// AssetFile return a http.FileSystem instance that data backend by asset\nfunc AssetFile() http.FileSystem {\n\treturn &assetOperator{}\n}\n\nvar _layoutsLayoutHtml = []byte(\"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\x34\\xce\\xc1\\xa9\\xc3\\x30\\x0c\\x06\\xe0\\xf3\\x33\\x78\\x07\\xbd\\x01\\x8c\\xc9\\x5d\\x78\\x82\\x9e\\x4a\\x17\\x70\\x6a\\x51\\x19\\x94\\xa4\\x38\\xca\\xc1\\x84\\xec\\x5e\\xec\\xba\\x27\\x49\\xf0\\x89\\xff\\x47\\xd6\\x45\\x82\\x35\\xc8\\x14\\x53\\x9b\\x9a\\x55\\x28\\xdc\\x62\\xdd\\x0e\\x45\\xff\\xbd\\xac\\xb1\\x06\\xfd\\x4f\\xcc\\x5b\\xaa\\xc1\\x9a\\x3f\\xe4\\x29\\x3c\\x38\\xef\\x90\\x77\\x50\\x26\\x78\\xc9\\x36\\x47\\x01\\x19\\xaf\\x3c\\x75\\x34\\x17\\xf0\\x7d\\xf9\\x77\\x0e\\xee\\xb4\\x26\\x2a\\x5d\\x3f\\x8f\\x52\\x68\\x55\\x50\\x5a\\xde\\x12\\x95\\x80\\xa9\\x10\\x38\\xd7\\xec\\x79\\x42\\xcd\\x24\\x09\\xae\\xab\\x05\\x8f\\x40\\xf4\\xa3\\xeb\\x27\\x00\\x00\\xff\\xff\\x68\\xca\\x16\\xc2\\xb4\\x00\\x00\\x00\")\n\nfunc layoutsLayoutHtmlBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_layoutsLayoutHtml,\n\t\t\"layouts/layout.html\",\n\t)\n}\n\nfunc layoutsLayoutHtml() (*asset, error) {\n\tbytes, err := layoutsLayoutHtmlBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"layouts/layout.html\", size: 180, mode: os.FileMode(438), modTime: time.Unix(1599156854, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\nvar _layoutsMylayoutHtml = []byte(\"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\x34\\x8f\\x4d\\x6a\\xc5\\x30\\x0c\\x84\\xd7\\x35\\xf8\\x0e\\xd3\\x03\\x18\\x93\\xbd\\xf1\\x09\\xba\\x2a\\xbd\\x80\\x53\\xab\\xc8\\xe0\\x9f\\xe2\\x28\\x0b\\x13\\x72\\xf7\\x47\\x9c\\xbc\\x95\\x46\\x62\\x46\\x7c\\xe3\\x58\\x4a\\xf6\\x5a\\x39\\xa6\\x10\\xaf\\x29\\x49\\x32\\xf9\\x32\\xf0\\x15\\x46\\xdb\\xc5\\xd9\\xfb\\xa0\\x95\\x56\\xce\\xbe\\x4d\\x6b\\x8b\\xc3\\x6b\\xf5\\xe1\\x78\\xf1\\x3f\\x9c\\x36\\xa4\\x0d\\xc2\\x84\\x3c\\x33\\xf8\\x6b\\x7d\\xae\\xb6\\x0c\\x8b\\x50\\xe3\\x14\\x4d\\x98\\x3a\\x7a\\xdb\\x85\\x36\\xb4\\x9a\\x87\\xb3\\xbc\\xcc\\x27\\x6b\\x87\\x9d\\xe2\\xd3\\x18\\x7c\\x53\\x8d\\x74\\xc7\\x7f\\xf7\\xde\\xa9\\x0a\\x84\\xca\\x7f\\x0e\\x42\\x60\\xea\\x04\\x63\\x2e\\xef\\x71\\x60\\x24\\xca\\x11\\xe7\\x79\\x81\\x3d\\x40\\xce\\x3e\\x75\\x5e\\x01\\x00\\x00\\xff\\xff\\x64\\xea\\xc5\\x1d\\xd7\\x00\\x00\\x00\")\n\nfunc layoutsMylayoutHtmlBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_layoutsMylayoutHtml,\n\t\t\"layouts/mylayout.html\",\n\t)\n}\n\nfunc layoutsMylayoutHtml() (*asset, error) {\n\tbytes, err := layoutsMylayoutHtmlBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"layouts/mylayout.html\", size: 215, mode: os.FileMode(438), modTime: time.Unix(1599156854, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\nvar _page1Html = []byte(\"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\x3c\\xca\\x41\\xaa\\xc2\\x30\\x10\\x00\\xd0\\xf5\\x2f\\xf4\\x0e\\xc3\\xec\\xbf\\x25\\x5b\\x8d\\x3d\\x83\\x37\\x90\\x69\\x33\\xa4\\xa1\\x63\\x53\\x26\\x69\\x40\\x42\\xee\\x2e\\xa2\\xb8\\x7c\\xf0\\xac\\x0b\\x05\\x52\\x7e\\x0a\\x5f\\x71\\xa2\\x79\\xf5\\x1a\\x8f\\xcd\\xfd\\xcf\\x51\\xa2\\x9e\\x61\\x12\\x9a\\xd7\\x0b\\xfc\\x74\\x30\\x8e\\x7d\\xd7\\x77\\x7f\\x76\\x31\\xe3\\x8d\\x3c\\x83\\x81\\x5a\\xc1\\x2b\\x73\\x06\\x0c\\x1a\\x12\\x38\\x2e\\x2c\\x71\\x67\\xc5\\xd6\\xec\\xb0\\x98\\xcf\\xaf\\x15\\x94\\x37\\xc7\\x0a\\xb8\\x93\\xe6\\x40\\x92\\x86\\x9d\\x3c\\x9b\\xfb\\x97\\xe6\\xb4\\xe4\\x87\\x60\\x6b\\xef\\x6e\\x07\\x17\\xca\\xd8\\x77\\xaf\\x00\\x00\\x00\\xff\\xff\\x47\\x41\\x4a\\x5c\\x9d\\x00\\x00\\x00\")\n\nfunc page1HtmlBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_page1Html,\n\t\t\"page1.html\",\n\t)\n}\n\nfunc page1Html() (*asset, error) {\n\tbytes, err := page1HtmlBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"page1.html\", size: 157, mode: os.FileMode(438), modTime: time.Unix(1599156854, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\nvar _partialsPage1_partial1Html = []byte(\"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\xb2\\x49\\xc9\\x2c\\x53\\x28\\x2e\\xa9\\xcc\\x49\\xb5\\x55\\x4a\\x4a\\x4c\\xce\\x4e\\x2f\\xca\\x2f\\xcd\\x4b\\xd1\\x4d\\xce\\xcf\\xc9\\x2f\\xb2\\x52\\x28\\xcf\\xc8\\x2c\\x49\\xb5\\x56\\x80\\xf2\\x8a\\x52\\x53\\x94\\xec\\x78\\xb9\\x38\\x6d\\x32\\x0c\\xed\\x02\\x12\\xd3\\x53\\x15\\x0c\\xd5\\x8b\\x15\\x02\\x12\\x8b\\x4a\\x32\\x13\\x73\\x14\\x0c\\x6d\\xf4\\x33\\x0c\\xed\\x78\\xb9\\x6c\\xf4\\x53\\x32\\xcb\\xec\\x78\\xb9\\x00\\x01\\x00\\x00\\xff\\xff\\xa2\\xa6\\x60\\xb6\\x59\\x00\\x00\\x00\")\n\nfunc partialsPage1_partial1HtmlBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_partialsPage1_partial1Html,\n\t\t\"partials/page1_partial1.html\",\n\t)\n}\n\nfunc partialsPage1_partial1Html() (*asset, error) {\n\tbytes, err := partialsPage1_partial1HtmlBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"partials/page1_partial1.html\", size: 89, mode: os.FileMode(438), modTime: time.Unix(1599156854, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\n// Asset loads and returns the asset for the given name.\n// It returns an error if the asset could not be found or\n// could not be loaded.\nfunc Asset(name string) ([]byte, error) {\n\tcannonicalName := strings.Replace(name, \"\\\\\", \"/\", -1)\n\tif f, ok := _bindata[cannonicalName]; ok {\n\t\ta, err := f()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"Asset %s can't read by error: %v\", name, err)\n\t\t}\n\t\treturn a.bytes, nil\n\t}\n\treturn nil, fmt.Errorf(\"Asset %s not found\", name)\n}\n\n// MustAsset is like Asset but panics when Asset would return an error.\n// It simplifies safe initialization of global variables.\nfunc MustAsset(name string) []byte {\n\ta, err := Asset(name)\n\tif err != nil {\n\t\tpanic(\"asset: Asset(\" + name + \"): \" + err.Error())\n\t}\n\n\treturn a\n}\n\n// AssetInfo loads and returns the asset info for the given name.\n// It returns an error if the asset could not be found or\n// could not be loaded.\nfunc AssetInfo(name string) (os.FileInfo, error) {\n\tcannonicalName := strings.Replace(name, \"\\\\\", \"/\", -1)\n\tif f, ok := _bindata[cannonicalName]; ok {\n\t\ta, err := f()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"AssetInfo %s can't read by error: %v\", name, err)\n\t\t}\n\t\treturn a.info, nil\n\t}\n\treturn nil, fmt.Errorf(\"AssetInfo %s not found\", name)\n}\n\n// AssetNames returns the names of the assets.\nfunc AssetNames() []string {\n\tnames := make([]string, 0, len(_bindata))\n\tfor name := range _bindata {\n\t\tnames = append(names, name)\n\t}\n\treturn names\n}\n\n// _bindata is a table, holding each asset generator, mapped to its name.\nvar _bindata = map[string]func() (*asset, error){\n\t\"layouts/layout.html\":          layoutsLayoutHtml,\n\t\"layouts/mylayout.html\":        layoutsMylayoutHtml,\n\t\"page1.html\":                   page1Html,\n\t\"partials/page1_partial1.html\": partialsPage1_partial1Html,\n}\n\n// AssetDir returns the file names below a certain\n// directory embedded in the file by go-bindata.\n// For example if you run go-bindata on data/... and data contains the\n// following hierarchy:\n//\n//\tdata/\n//\t  foo.txt\n//\t  img/\n//\t    a.png\n//\t    b.png\n//\n// then AssetDir(\"data\") would return []string{\"foo.txt\", \"img\"}\n// AssetDir(\"data/img\") would return []string{\"a.png\", \"b.png\"}\n// AssetDir(\"foo.txt\") and AssetDir(\"notexist\") would return an error\n// AssetDir(\"\") will return []string{\"data\"}.\nfunc AssetDir(name string) ([]string, error) {\n\tnode := _bintree\n\tif len(name) != 0 {\n\t\tcannonicalName := strings.Replace(name, \"\\\\\", \"/\", -1)\n\t\tpathList := strings.Split(cannonicalName, \"/\")\n\t\tfor _, p := range pathList {\n\t\t\tnode = node.Children[p]\n\t\t\tif node == nil {\n\t\t\t\treturn nil, fmt.Errorf(\"Asset %s not found\", name)\n\t\t\t}\n\t\t}\n\t}\n\tif node.Func != nil {\n\t\treturn nil, fmt.Errorf(\"Asset %s not found\", name)\n\t}\n\trv := make([]string, 0, len(node.Children))\n\tfor childName := range node.Children {\n\t\trv = append(rv, childName)\n\t}\n\treturn rv, nil\n}\n\ntype bintree struct {\n\tFunc     func() (*asset, error)\n\tChildren map[string]*bintree\n}\n\nvar _bintree = &bintree{nil, map[string]*bintree{\n\t\"layouts\": {nil, map[string]*bintree{\n\t\t\"layout.html\":   {layoutsLayoutHtml, map[string]*bintree{}},\n\t\t\"mylayout.html\": {layoutsMylayoutHtml, map[string]*bintree{}},\n\t}},\n\t\"page1.html\": {page1Html, map[string]*bintree{}},\n\t\"partials\": {nil, map[string]*bintree{\n\t\t\"page1_partial1.html\": {partialsPage1_partial1Html, map[string]*bintree{}},\n\t}},\n}}\n\n// RestoreAsset restores an asset under the given directory\nfunc RestoreAsset(dir, name string) error {\n\tdata, err := Asset(name)\n\tif err != nil {\n\t\treturn err\n\t}\n\tinfo, err := AssetInfo(name)\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = os.WriteFile(_filePath(dir, name), data, info.Mode())\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// RestoreAssets restores an asset under the given directory recursively\nfunc RestoreAssets(dir, name string) error {\n\tchildren, err := AssetDir(name)\n\t// File\n\tif err != nil {\n\t\treturn RestoreAsset(dir, name)\n\t}\n\t// Dir\n\tfor _, child := range children {\n\t\terr = RestoreAssets(dir, filepath.Join(name, child))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc _filePath(dir, name string) string {\n\tcannonicalName := strings.Replace(name, \"\\\\\", \"/\", -1)\n\treturn filepath.Join(append([]string{dir}, strings.Split(cannonicalName, \"/\")...)...)\n}\n"
  },
  {
    "path": "_examples/view/embedding-templates-into-app-bindata/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\t// $ go install github.com/go-bindata/go-bindata/v3/go-bindata@latest\n\t// $ go-bindata -fs -prefix \"templates\" ./templates/...\n\t// $ go run .\n\t// html files are not used, you can delete the folder and run the example.\n\ttmpl := iris.HTML(AssetFile(), \".html\")\n\ttmpl.Layout(\"layouts/layout.html\")\n\ttmpl.AddFunc(\"greet\", func(s string) string {\n\t\treturn \"Greetings \" + s + \"!\"\n\t})\n\n\tapp.RegisterView(tmpl)\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tif err := ctx.View(\"page1.html\"); err != nil {\n\t\t\tctx.StatusCode(iris.StatusInternalServerError)\n\t\t\tctx.WriteString(err.Error())\n\t\t}\n\t})\n\n\t// remove the layout for a specific route\n\tapp.Get(\"/nolayout\", func(ctx iris.Context) {\n\t\tctx.ViewLayout(iris.NoLayout)\n\t\tif err := ctx.View(\"page1.html\"); err != nil {\n\t\t\tctx.StatusCode(iris.StatusInternalServerError)\n\t\t\tctx.WriteString(err.Error())\n\t\t}\n\t})\n\n\t// set a layout for a party, .Layout should be BEFORE any Get or other Handle party's method\n\tmy := app.Party(\"/my\").Layout(\"layouts/mylayout.html\")\n\t{ // both of these will use the layouts/mylayout.html as their layout.\n\t\tmy.Get(\"/\", func(ctx iris.Context) {\n\t\t\tif err := ctx.View(\"page1.html\"); err != nil {\n\t\t\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\t\t\treturn\n\t\t\t}\n\t\t})\n\t\tmy.Get(\"/other\", func(ctx iris.Context) {\n\t\t\tif err := ctx.View(\"page1.html\"); err != nil {\n\t\t\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\t\t\treturn\n\t\t\t}\n\t\t})\n\t}\n\n\t// http://localhost:8080\n\t// http://localhost:8080/nolayout\n\t// http://localhost:8080/my\n\t// http://localhost:8080/my/other\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/view/embedding-templates-into-app-bindata/templates/layouts/layout.html",
    "content": "<html>\n<head>\n<title>Layout</title>\n\n</head>\n<body>\n\t<h1>This is the global layout</h1>\n\t<br />\n\t<!-- Render the current template here -->\n\t{{ yield . }}\n</body>\n</html>\n"
  },
  {
    "path": "_examples/view/embedding-templates-into-app-bindata/templates/layouts/mylayout.html",
    "content": "<html>\n<head>\n<title>my Layout</title>\n\n</head>\n<body>\n\t<h1>This is the layout for the /my/ and /my/other routes only</h1>\n\t<br />\n\t<!-- Render the current template here -->\n\t{{ yield . }}\n</body>\n</html>\n"
  },
  {
    "path": "_examples/view/embedding-templates-into-app-bindata/templates/page1.html",
    "content": "<div style=\"background-color: black; color: blue\">\n\n\t<h1>Page 1 {{ greet \"iris developer\"}}</h1>\n\n\t{{ render \"partials/page1_partial1.html\" . }}\n\n</div>\n"
  },
  {
    "path": "_examples/view/embedding-templates-into-app-bindata/templates/partials/page1_partial1.html",
    "content": "<div style=\"background-color: white; color: red\">\n\t<h1>Page 1's Partial 1</h1>\n</div>\n"
  },
  {
    "path": "_examples/view/fallback/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.RegisterView(iris.HTML(\"./view\", \".html\"))\n\n\t// Use the FallbackView helper Register a fallback view\n\t// filename per-party when the provided was not found.\n\tapp.FallbackView(iris.FallbackView(\"fallback.html\"))\n\n\t// Use the FallbackViewLayout helper to register a fallback view layout.\n\tapp.FallbackView(iris.FallbackViewLayout(\"layout.html\"))\n\n\t// Register a custom fallback function per-party to handle everything.\n\t// You can register more than one. If fails (returns a not nil error of ErrViewNotExists)\n\t// then it proceeds to the next registered fallback.\n\tapp.FallbackView(iris.FallbackViewFunc(func(ctx iris.Context, err iris.ErrViewNotExist) error {\n\t\t// err.Name is the previous template name.\n\t\t// err.IsLayout reports whether the failure came from the layout template.\n\t\t// err.Data is the template data provided to the previous View call.\n\t\t// [...custom logic e.g. ctx.View(\"fallback.html\", err.Data)]\n\t\treturn err\n\t}))\n\n\tapp.Get(\"/\", index)\n\n\tapp.Listen(\":8080\")\n}\n\n// Register fallback view(s) in a middleware.\n// func fallbackInsideAMiddleware(ctx iris.Context) {\n// \tctx.FallbackView(...)\n//  To remove all previous registered fallbacks, pass nil.\n//  ctx.FallbackView(nil)\n// \tctx.Next()\n// }\n\nfunc index(ctx iris.Context) {\n\tif err := ctx.View(\"blabla.html\"); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "_examples/view/fallback/view/fallback.html",
    "content": "<h1>Fallback view</h1>"
  },
  {
    "path": "_examples/view/herotemplate/README.md",
    "content": "# Hero Template Example\n\nThis folder contains the iris version of the original hero's example: https://github.com/shiyanhui/hero/tree/master/examples/app.\n\nIris is 100% compatible with `net/http` so you don't have to change anything else\nexcept the handler input from the original example.\n\nThe only inline handler's changes were:\n\nFrom:\n\n```go\nif _, err := w.Write(buffer.Bytes()); err != nil {\n// and\ntemplate.UserListToWriter(userList, w)\n```\nTo: \n```go\nif _, err := ctx.Write(buffer.Bytes()); err != nil {\n// and\ntemplate.UserListToWriter(userList, ctx)\n```\n\nSo easy.\n\nRead more at: https://github.com/shiyanhui/hero"
  },
  {
    "path": "_examples/view/herotemplate/app.go",
    "content": "package main\n\nimport (\n\t\"bytes\"\n\n\t\"github.com/kataras/iris/v12/_examples/view/herotemplate/template\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\n// $ go get -u github.com/shiyanhui/hero/hero\n// $ go run app.go\n//\n// Read more at https://github.com/shiyanhui/hero/hero\n\nfunc main() {\n\tapp := iris.New()\n\n\tapp.Get(\"/users\", func(ctx iris.Context) {\n\t\tctx.CompressWriter(true)\n\t\tctx.ContentType(\"text/html\")\n\n\t\tuserList := []string{\n\t\t\t\"Alice\",\n\t\t\t\"Bob\",\n\t\t\t\"Tom\",\n\t\t}\n\n\t\t// Had better use buffer sync.Pool.\n\t\t// Hero(github.com/shiyanhui/hero/hero) exports GetBuffer and PutBuffer for this.\n\t\t//\n\t\t// buffer := hero.GetBuffer()\n\t\t// defer hero.PutBuffer(buffer)\n\t\t// buffer := new(bytes.Buffer)\n\t\t// template.UserList(userList, buffer)\n\t\t// ctx.Write(buffer.Bytes())\n\n\t\t// iris context implements the io.Writer:\n\t\t// _, err := template.UserListToWriter(userList, ctx)\n\t\t// OR:\n\t\tbuffer := new(bytes.Buffer)\n\t\ttemplate.UserList(userList, buffer)\n\n\t\t_, err := ctx.Write(buffer.Bytes())\n\t\tif err != nil {\n\t\t\tctx.StopWithError(iris.StatusInternalServerError, err)\n\t\t\treturn\n\t\t}\n\t})\n\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/view/herotemplate/template/index.html",
    "content": "<!DOCTYPE html>\n<html>\n    <head>\n        <meta charset=\"utf-8\">\n    </head>\n\n    <body>\n        <%@ body { %>\n        <% } %>\n    </body>\n</html>\n"
  },
  {
    "path": "_examples/view/herotemplate/template/index.html.go",
    "content": "// Code generated by hero.\n// DO NOT EDIT!\npackage template\n"
  },
  {
    "path": "_examples/view/herotemplate/template/user.html",
    "content": "<li>\n    <%= user %>\n</li>\n"
  },
  {
    "path": "_examples/view/herotemplate/template/user.html.go",
    "content": "// Code generated by hero.\n// DO NOT EDIT!\npackage template\n"
  },
  {
    "path": "_examples/view/herotemplate/template/userlist.html",
    "content": "<%: func UserList(userList []string, buffer *bytes.Buffer) %>\n\n<%~ \"index.html\" %>\n\n<%@ body { %>\n    <% for _, user := range userList { %>\n        <ul>\n            <%+ \"user.html\" %>\n        </ul>\n    <% } %>\n<% } %>\n"
  },
  {
    "path": "_examples/view/herotemplate/template/userlist.html.go",
    "content": "// Code generated by hero.\n// DO NOT EDIT!\npackage template\n\nimport (\n\t\"bytes\"\n\n\t\"github.com/shiyanhui/hero\"\n)\n\nfunc UserList(userList []string, buffer *bytes.Buffer) {\n\tbuffer.WriteString(`<!DOCTYPE html>\n<html>\n    <head>\n        <meta charset=\"utf-8\">\n    </head>\n\n    <body>\n        `)\n\tfor _, user := range userList {\n\t\tbuffer.WriteString(`\n        <ul>\n            `)\n\t\tbuffer.WriteString(`<li>\n    `)\n\t\thero.EscapeHTML(user, buffer)\n\t\tbuffer.WriteString(`\n</li>\n`)\n\n\t\tbuffer.WriteString(`\n        </ul>\n    `)\n\t}\n\n\tbuffer.WriteString(`\n    </body>\n</html>\n`)\n}\n"
  },
  {
    "path": "_examples/view/herotemplate/template/userlistwriter.html",
    "content": "<%: func UserListToWriter(userList []string, w io.Writer) (int, error)%>\n\n<%~ \"index.html\" %>\n\n<%@ body { %>\n    <% for _, user := range userList { %>\n        <ul>\n            <%+ \"user.html\" %>\n        </ul>\n    <% } %>\n<% } %>\n"
  },
  {
    "path": "_examples/view/herotemplate/template/userlistwriter.html.go",
    "content": "// Code generated by hero.\n// DO NOT EDIT!\npackage template\n\nimport (\n\t\"io\"\n\n\t\"github.com/shiyanhui/hero\"\n)\n\nfunc UserListToWriter(userList []string, w io.Writer) (int, error) {\n\t_buffer := hero.GetBuffer()\n\tdefer hero.PutBuffer(_buffer)\n\t_buffer.WriteString(`<!DOCTYPE html>\n<html>\n    <head>\n        <meta charset=\"utf-8\">\n    </head>\n\n    <body>\n        `)\n\tfor _, user := range userList {\n\t\t_buffer.WriteString(`\n        <ul>\n            `)\n\t\t_buffer.WriteString(`<li>\n    `)\n\t\thero.EscapeHTML(user, _buffer)\n\t\t_buffer.WriteString(`\n</li>\n`)\n\n\t\t_buffer.WriteString(`\n        </ul>\n    `)\n\t}\n\n\t_buffer.WriteString(`\n    </body>\n</html>\n`)\n\treturn w.Write(_buffer.Bytes())\n}\n"
  },
  {
    "path": "_examples/view/layout/ace/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\t// By default Ace minifies the template before render,\n\t// using the SetIndent method, we make it to match\n\t// the rest of the template results.\n\tapp.RegisterView(iris.Ace(\"./views\", \".ace\").SetIndent(\"  \"))\n\n\tapp.Get(\"/\", index)\n\n\tapp.Listen(\":8080\")\n}\n\nfunc index(ctx iris.Context) {\n\tdata := iris.Map{\n\t\t\"Title\":      \"Page Title\",\n\t\t\"FooterText\": \"Footer contents\",\n\t\t\"Message\":    \"Main contents\",\n\t}\n\n\tctx.ViewLayout(\"layouts/main\")\n\tif err := ctx.View(\"index\", data); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "_examples/view/layout/ace/views/index.ace",
    "content": "h1 Index Body\nh3 Message: {{.Message}}"
  },
  {
    "path": "_examples/view/layout/ace/views/layouts/main.ace",
    "content": "= doctype html\nhtml\n  head\n    title {{.Title}}\n  body\n    {{ yield . }}\n    footer\n      = include partials/footer.ace ."
  },
  {
    "path": "_examples/view/layout/ace/views/partials/footer.ace",
    "content": "h3 Footer Partial\nh4 {{.FooterText}}"
  },
  {
    "path": "_examples/view/layout/blocks/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.RegisterView(iris.Blocks(\"./views\", \".html\"))\n\t// Note, in Blocks engine, layouts\n\t// are used by their base names, the\n\t// blocks.LayoutDir(layoutDir) defaults to \"./layouts\".\n\t// .Blocks(...).Layout(\"main\") for default layout for all views, it can be modified through ctx.ViewLayout though.\n\n\tapp.Get(\"/\", index)\n\n\tapp.Listen(\":8080\")\n}\n\nfunc index(ctx iris.Context) {\n\tdata := iris.Map{\n\t\t\"Title\":      \"Page Title\",\n\t\t\"FooterText\": \"Footer contents\",\n\t\t\"Message\":    \"Main contents\",\n\t}\n\n\tctx.ViewLayout(\"main\")\n\tif err := ctx.View(\"index\", data); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "_examples/view/layout/blocks/views/index.html",
    "content": "<h1>Index Body</h1>\n<h3>Message: {{.Message}}</h3>"
  },
  {
    "path": "_examples/view/layout/blocks/views/layouts/main.html",
    "content": "<!DOCTYPE html>\n<head>\n    <title>{{.Title}}</title>\n</head>\n<body>\n{{ template \"content\" . }}\n<footer>\n{{ partial \"partials/footer\" . }}\n</footer>\n</body>\n</html>"
  },
  {
    "path": "_examples/view/layout/blocks/views/partials/footer.html",
    "content": "<h3>Footer Partial</h3>\n<h4>{{.FooterText}}</h4>"
  },
  {
    "path": "_examples/view/layout/django/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.RegisterView(iris.Django(\"./views\", \".html\"))\n\n\tapp.Get(\"/\", index)\n\n\tapp.Listen(\":8080\")\n}\n\nfunc index(ctx iris.Context) {\n\tdata := iris.Map{\n\t\t\"Title\":      \"Page Title\",\n\t\t\"FooterText\": \"Footer contents\",\n\t\t\"Message\":    \"Main contents\",\n\t}\n\n\t// On Django this is ignored:  ctx.ViewLayout(\"layouts/main\")\n\t// Layouts are only rendered from inside the index page itself\n\t// using the \"extends\" keyword.\n\tif err := ctx.View(\"index\", data); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "_examples/view/layout/django/views/index.html",
    "content": "{% extends \"layouts/main.html\" %}\n\n{% block content %}\n<h1>Index Body</h1>\n<h3>Message: {{Message}}</h3>\n{% endblock %}"
  },
  {
    "path": "_examples/view/layout/django/views/layouts/main.html",
    "content": "<!DOCTYPE html>\n<head>\n    <title>{{Title}}</title>\n</head>\n<body>\n    {% block content %} {% endblock %}\n\n<footer>{% include \"../partials/footer.html\" %}</footer>\n</body>\n</html>"
  },
  {
    "path": "_examples/view/layout/django/views/partials/footer.html",
    "content": "<h3>Footer Partial</h3>\n<h4>{{FooterText}}</h4>"
  },
  {
    "path": "_examples/view/layout/handlebars/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\tapp.RegisterView(iris.Handlebars(\"./views\", \".html\"))\n\n\tapp.Get(\"/\", index)\n\n\tapp.Listen(\":8080\")\n}\n\nfunc index(ctx iris.Context) {\n\tdata := iris.Map{\n\t\t\"Title\":      \"Page Title\",\n\t\t\"FooterText\": \"Footer contents\",\n\t\t\"Message\":    \"Main contents\",\n\t}\n\n\tctx.ViewLayout(\"layouts/main\")\n\tif err := ctx.View(\"index\", data); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "_examples/view/layout/handlebars/views/index.html",
    "content": "<h1>Index Body</h1>\n<h3>Message: {{Message}} </h3>"
  },
  {
    "path": "_examples/view/layout/handlebars/views/layouts/main.html",
    "content": "<!DOCTYPE html>\n<head>\n    <title>{{Title}}</title>\n</head>\n<body>\n    {{ yield . }}\n\n<footer>{{ render \"partials/footer.html\" .}}</footer>\n</body>\n</html>"
  },
  {
    "path": "_examples/view/layout/handlebars/views/partials/footer.html",
    "content": "<h3>Footer Partial</h3>\n<h4>{{FooterText}}</h4>"
  },
  {
    "path": "_examples/view/layout/html/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\tapp.RegisterView(iris.HTML(\"./views\", \".html\"))\n\n\tapp.Get(\"/\", index)\n\n\tapp.Listen(\":8080\")\n}\n\nfunc index(ctx iris.Context) {\n\tdata := iris.Map{\n\t\t\"Title\":      \"Page Title\",\n\t\t\"FooterText\": \"Footer contents\",\n\t\t\"Message\":    \"Main contents\",\n\t}\n\n\tctx.ViewLayout(\"layouts/main\")\n\tif err := ctx.View(\"index\", data); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "_examples/view/layout/html/views/index.html",
    "content": "<h1>Index Body</h1>\n<h3>Message: {{.Message}}</h3>"
  },
  {
    "path": "_examples/view/layout/html/views/layouts/main.html",
    "content": "<!DOCTYPE html>\n<head>\n    <title>{{.Title}}</title>\n</head>\n<body>\n{{ yield . }}\n<footer>\n{{ render \"partials/footer.html\" . }}\n</footer>\n</body>\n</html>"
  },
  {
    "path": "_examples/view/layout/html/views/partials/footer.html",
    "content": "<h3>Footer Partial</h3>\n<h4>{{.FooterText}}</h4>"
  },
  {
    "path": "_examples/view/layout/jet/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\tapp.RegisterView(iris.Jet(\"./views\", \".jet\"))\n\n\tapp.Get(\"/\", index)\n\n\tapp.Listen(\":8080\")\n}\n\nfunc index(ctx iris.Context) {\n\tdata := iris.Map{\n\t\t\"Title\":      \"Page Title\",\n\t\t\"FooterText\": \"Footer contents\",\n\t\t\"Message\":    \"Main contents\",\n\t}\n\n\t// On Jet this is ignored:  ctx.ViewLayout(\"layouts/main\")\n\t// Layouts are only rendered from inside the index page itself\n\t// using the \"extends\" keyword.\n\tif err := ctx.View(\"index\", data); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "_examples/view/layout/jet/views/index.jet",
    "content": "{{ extends \"../layouts/main.jet\" }}\n{{ block documentBody() }}\n<h1>Index Body</h1>\n<h3>Message: {{.Message}}</h3>\n{{ end }}"
  },
  {
    "path": "_examples/view/layout/jet/views/layouts/main.jet",
    "content": "<!DOCTYPE html>\n<head>\n    <title>{{.Title}}</title>\n</head>\n<body>\n    {{ yield . documentBody() }}\n<footer>{{ include \"../partials/footer.jet\" . }}</footer>\n</body>\n</html>"
  },
  {
    "path": "_examples/view/layout/jet/views/partials/footer.jet",
    "content": "<h3>Footer Partial</h3>\n<h4>{{.FooterText}}</h4>"
  },
  {
    "path": "_examples/view/layout/pug/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.RegisterView(iris.Pug(\"./views\", \".pug\"))\n\n\tapp.Get(\"/\", index)\n\n\tapp.Listen(\":8080\")\n}\n\nfunc index(ctx iris.Context) {\n\tdata := iris.Map{\n\t\t\"Title\":      \"Page Title\",\n\t\t\"FooterText\": \"Footer contents\",\n\t\t\"Message\":    \"Main contents\",\n\t}\n\n\t// On Pug this is ignored:  ctx.ViewLayout(\"layouts/main\")\n\t// Layouts are only rendered from inside the index page itself\n\t// using the \"extends\" keyword.\n\tif err := ctx.View(\"index\", data); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "_examples/view/layout/pug/views/index.pug",
    "content": "extends layouts/main.pug\n\nblock content\n  h1 Index Body\n  h3 Message: {{.Message}}"
  },
  {
    "path": "_examples/view/layout/pug/views/layouts/main.pug",
    "content": "doctype html\nhtml\n  head\n    title {{.Title}}\n  body\n    block content\n    footer\n      include ../partials/footer.pug"
  },
  {
    "path": "_examples/view/layout/pug/views/partials/footer.pug",
    "content": "h3 Footer Partial\nh4 {{.FooterText}}"
  },
  {
    "path": "_examples/view/overview/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"html/template\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\n// ViewFunctions presents some builtin functions\n// for html view engines. See `View.Funcs` or `view/html.Funcs` and etc.\nvar Functions = template.FuncMap{\n\t\"Now\": time.Now,\n}\n\nfunc main() {\n\tapp := iris.New()\n\n\t// with default template funcs:\n\t//\n\t// - {{ urlpath \"mynamedroute\" \"pathParameter_ifneeded\" }}\n\t// - {{ render \"header.html\" . }}\n\t// - {{ render_r \"header.html\" . }} // partial relative path to current page\n\t// - {{ yield . }}\n\t// - {{ current . }}\n\tapp.RegisterView(iris.HTML(\"./templates\", \".html\").\n\t\tFuncs(Functions). // Optionally register some more builtin functions.\n\t\tReload(false))    // Set Reload to true on development.\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\t// enable compression based on Accept-Encoding (e.g. \"gzip\"),\n\t\t// alternatively: app.Use(iris.Compression).\n\t\tctx.CompressWriter(true)\n\t\t// the .Name inside the ./templates/hi.html.\n\t\tctx.ViewData(\"Name\", \"iris\")\n\t\t// render the template with the file name relative to the './templates'.\n\t\t// file extension is OPTIONAL.\n\t\tif err := ctx.View(\"hi.html\"); err != nil {\n\t\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\t\treturn\n\t\t}\n\t})\n\n\tapp.Get(\"/example_map\", func(ctx iris.Context) {\n\t\texamplePage := iris.Map{\n\t\t\t\"Name\":   \"Example Name\",\n\t\t\t\"Age\":    42,\n\t\t\t\"Items\":  []string{\"Example slice entry 1\", \"entry 2\", \"entry 3\"},\n\t\t\t\"Map\":    iris.Map{\"map key\": \"map value\", \"other key\": \"other value\"},\n\t\t\t\"Nested\": iris.Map{\"Title\": \"Iris E-Book\", \"Pages\": 620},\n\t\t}\n\n\t\tif err := ctx.View(\"example.html\", examplePage); err != nil {\n\t\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\t\treturn\n\t\t}\n\t})\n\n\tapp.Get(\"/example_struct\", func(ctx iris.Context) {\n\t\ttype book struct {\n\t\t\tTitle string\n\t\t\tPages int\n\t\t}\n\n\t\tvar examplePage = struct {\n\t\t\tName   string\n\t\t\tAge    int\n\t\t\tItems  []string\n\t\t\tMap    map[string]any\n\t\t\tNested book\n\t\t}{\n\t\t\t\"Example Name\",\n\t\t\t42,\n\t\t\t[]string{\"Example slice entry 1\", \"entry 2\", \"entry 3\"},\n\t\t\tiris.Map{\"map key\": \"map value\", \"other key\": \"other value\"},\n\t\t\tbook{\n\t\t\t\t\"Iris E-Book\",\n\t\t\t\t620,\n\t\t\t},\n\t\t}\n\n\t\tif err := ctx.View(\"example.html\", examplePage); err != nil {\n\t\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\t\treturn\n\t\t}\n\t})\n\n\tapp.Get(\"/functions\", func(ctx iris.Context) {\n\t\tvar functionsPage = struct {\n\t\t\t// A function.\n\t\t\tNow func() time.Time\n\t\t\t// A struct field which contains methods.\n\t\t\tCtx iris.Context\n\t\t}{\n\t\t\tNow: time.Now,\n\t\t\tCtx: ctx,\n\t\t}\n\n\t\tif err := ctx.View(\"functions.html\", functionsPage); err != nil {\n\t\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\t\treturn\n\t\t}\n\t})\n\n\t// http://localhost:8080/\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/view/overview/templates/example.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>HTML Template Example</title>\n</head>\n<body>\n    <h2>Name: {{.Name}}</h2>\n    <h3>Age: {{.Age}}</h3>\n\n    <h4>Slice</h4>\n    {{range $idx, $item := .Items}}\n        {{ $idx }} - {{ $item }} <br/>\n    {{end}}\n\n    <h4>Map</h4>\n    {{range $key, $value := .Map}}\n        {{ $key }} - {{ $value }} <br/>\n    {{end}}\n\n    <h4>Nested</h4>\n\n    <strong>Title:</strong>{{ .Nested.Title}}\n\n    <hr/>\n\n    <strong>Read more at: <a href=\"https://pkg.go.dev/html/template\" target=\"_new\">\n        https://pkg.go.dev/html/template</a>\n    </strong>\n</body>\n</html>"
  },
  {
    "path": "_examples/view/overview/templates/functions.html",
    "content": "<h1>Function: {{ Now }}</h1>\n<h1>Field: {{ .Ctx.Request.URL.Path }} </h1>\n<h1>Field Struct's Function (Method): {{ .Ctx.FullRequestURI }} </h1>\n"
  },
  {
    "path": "_examples/view/overview/templates/hi.html",
    "content": "<html>\r\n\r\n<head>\r\n\t<title>Hi iris</title>\r\n</head>\r\n\r\n<body>\r\n\t<h1>Hi {{.Name}} </h1>\r\n</body>\r\n\r\n</html>"
  },
  {
    "path": "_examples/view/parse-template/django/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\te := iris.Django(nil, \".html\") // You can still use a file system though.\n\te.AddFunc(\"greet\", func(name string) string {\n\t\treturn \"Hello, \" + name + \"!\"\n\t})\n\terr := e.ParseTemplate(\"program.html\", []byte(`<h1>{{greet(Name)}}</h1>`))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\te.Reload(true)\n\n\tapp := iris.New()\n\tapp.RegisterView(e)\n\tapp.Get(\"/\", index)\n\n\tapp.Listen(\":8080\")\n}\n\nfunc index(ctx iris.Context) {\n\tif err := ctx.View(\"program.html\", iris.Map{\n\t\t\"Name\": \"Gerasimos\",\n\t}); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "_examples/view/parse-template/handlebars/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\te := iris.Handlebars(nil, \".html\") // You can still use a file system though.\n\te.ParseTemplate(\"program.html\", `<h1>{{greet Name}}</h1>`, iris.Map{\n\t\t\"greet\": func(name string) string {\n\t\t\treturn \"Hello, \" + name + \"!\"\n\t\t},\n\t})\n\te.Reload(true)\n\n\tapp := iris.New()\n\tapp.RegisterView(e)\n\tapp.Get(\"/\", index)\n\n\tapp.Listen(\":8080\")\n}\n\nfunc index(ctx iris.Context) {\n\tif err := ctx.View(\"program.html\", iris.Map{\n\t\t\"Name\": \"Gerasimos\",\n\t}); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "_examples/view/parse-template/jet/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/view\"\n)\n\nfunc main() {\n\te := iris.Jet(nil, \".jet\") // You can still use a file system though.\n\te.AddFunc(\"greet\", func(args view.JetArguments) reflect.Value {\n\t\tmsg := \"Hello, \" + args.Get(0).String() + \"!\"\n\t\treturn reflect.ValueOf(msg)\n\t})\n\terr := e.ParseTemplate(\"program.jet\", `<h1>{{greet(.Name)}}</h1>`)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\te.Reload(true)\n\n\tapp := iris.New()\n\tapp.RegisterView(e)\n\tapp.Get(\"/\", index)\n\n\tapp.Listen(\":8080\")\n}\n\nfunc index(ctx iris.Context) {\n\tif err := ctx.View(\"program.jet\", iris.Map{\n\t\t\"Name\": \"Gerasimos\",\n\t}); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "_examples/view/parse-template/main.go",
    "content": "// Package main shows how to parse a template through custom byte slice content.\n// The following works with HTML, Pug and Ace template parsers.\n// To learn how you can manually parse a template from a text for the rest\n// template parsers navigate through the example's subdirectories.\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\t// To not load any templates from files or embedded data,\n\t// pass nil or empty string on the first argument:\n\t// e := iris.HTML(nil, \".html\")\n\n\te := iris.HTML(\"./views\", \".html\")\n\t// e := iris.Pug(\"./views\",\".pug\")\n\t// e := iris.Ace(\"./views\",\".ace\")\n\te.ParseTemplate(\"program.html\", []byte(`<h1>{{greet .Name}}</h1>`), iris.Map{\n\t\t\"greet\": func(name string) string {\n\t\t\treturn \"Hello, \" + name + \"!\"\n\t\t},\n\t})\n\te.Reload(true)\n\n\tapp := iris.New()\n\tapp.RegisterView(e)\n\n\tapp.Get(\"/\", index)\n\tapp.Get(\"/layout\", layout)\n\n\tapp.Listen(\":8080\")\n}\n\nfunc index(ctx iris.Context) {\n\tif err := ctx.View(\"program.html\", iris.Map{\n\t\t\"Name\": \"Gerasimos\",\n\t}); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n}\n\nfunc layout(ctx iris.Context) {\n\tctx.ViewLayout(\"layouts/main.html\")\n\tindex(ctx)\n}\n"
  },
  {
    "path": "_examples/view/parse-template/views/layouts/main.html",
    "content": "<html>\n<head>\n<title>My Layout</title>\n\n</head>\n<body>\n\t<h1>[layout] Body content is below...</h1>\n\t<!-- Render the current template here -->\n\t{{ yield . }}\n</body>\n</html>\n"
  },
  {
    "path": "_examples/view/quicktemplate/README.md",
    "content": "First of all, install [quicktemplate](https://github.com/valyala/quicktemplate) package and [quicktemplate compiler](https://github.com/valyala/quicktemplate/tree/master/qtc)\n\n```sh\ngo get -u github.com/valyala/quicktemplate\ngo get -u github.com/valyala/quicktemplate/qtc\n```\n\nThe example has the Go code compiled already for you, therefore:\n```sh\ngo run main.go # http://localhost:8080\n```\n\nHowever there is an instruction below, full documentation can be found at https://github.com/valyala/quicktemplate.\n\nSave your template files into `templates` folder under the extension *.qtpl, open your terminal and run `qtc` inside this folder.\n\nIf all went ok, `*.qtpl.go` files must appear in the `templates` folder. These files contain the Go code for  all `*.qtpl` files.\n\n> Remember, each time you change a  a `/templates/*.qtpl` file you have to run the `qtc` command and re-build your application."
  },
  {
    "path": "_examples/view/quicktemplate/controllers/execute_template.go",
    "content": "package controllers\n\nimport (\n\t\"github.com/kataras/iris/v12/_examples/view/quicktemplate/templates\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\n// ExecuteTemplate renders a \"tmpl\" partial template to the `Context.ResponseWriter`.\nfunc ExecuteTemplate(ctx iris.Context, tmpl templates.Partial) {\n\tctx.CompressWriter(true)\n\tctx.ContentType(\"text/html\")\n\ttemplates.WriteTemplate(ctx, tmpl)\n}\n"
  },
  {
    "path": "_examples/view/quicktemplate/controllers/hello.go",
    "content": "package controllers\n\nimport (\n\t\"github.com/kataras/iris/v12/_examples/view/quicktemplate/templates\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\n// Hello renders our ../templates/hello.qtpl file using the compiled ../templates/hello.qtpl.go file.\nfunc Hello(ctx iris.Context) {\n\t// vars := make(map[string]any)\n\t// vars[\"message\"] = \"Hello World!\"\n\t// vars[\"name\"] = ctx.Params().Get(\"name\")\n\t// [...]\n\t// &templates.Hello{ Vars: vars }\n\t// [...]\n\n\t// However, as an alternative, we recommend that you should the `ctx.ViewData(key, value)`\n\t// in order to be able modify the `templates.Hello#Vars` from a middleware(other handlers) as well.\n\tctx.ViewData(\"message\", \"Hello World!\")\n\tctx.ViewData(\"name\", ctx.Params().Get(\"name\"))\n\n\t// set view data to the `Vars` template's field\n\ttmpl := &templates.Hello{\n\t\tVars: ctx.GetViewData(),\n\t}\n\n\t// render the template\n\tExecuteTemplate(ctx, tmpl)\n}\n"
  },
  {
    "path": "_examples/view/quicktemplate/controllers/index.go",
    "content": "package controllers\n\nimport (\n\t\"github.com/kataras/iris/v12/_examples/view/quicktemplate/templates\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\n// Index renders our ../templates/index.qtpl file using the compiled ../templates/index.qtpl.go file.\nfunc Index(ctx iris.Context) {\n\ttmpl := &templates.Index{}\n\n\t// render the template\n\tExecuteTemplate(ctx, tmpl)\n}\n"
  },
  {
    "path": "_examples/view/quicktemplate/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12/_examples/view/quicktemplate/controllers\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc newApp() *iris.Application {\n\tapp := iris.New()\n\tapp.Get(\"/\", controllers.Index)\n\tapp.Get(\"/{name}\", controllers.Hello)\n\n\treturn app\n}\n\nfunc main() {\n\tapp := newApp()\n\t// http://localhost:8080\n\t// http://localhost:8080/yourname\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/view/quicktemplate/main_test.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nfunc TestResponseWriterQuicktemplate(t *testing.T) {\n\tbaseRawBody := `\n<html>\n\t<head>\n\t\t<title>Quicktemplate integration with Iris</title>\n\t</head>\n\t<body>\n\t\t<div>\n\t\t\tHeader contents here...\n\t\t</div>\n\n\t\t<div style=\"margin:10px;\">\n\t\t\t\n\t<h1>%s</h1>\n\t<div>\n\t\t%s\n\t</div>\n\n\t\t</div>\n\n\t</body>\n\t<footer>\n\t\tFooter contents here...\n\t</footer>\n</html>\n`\n\n\texpectedIndexRawBody := fmt.Sprintf(baseRawBody, \"Index Page\", \"This is our index page's body.\")\n\tname := \"yourname\"\n\texpectedHelloRawBody := fmt.Sprintf(baseRawBody, \"Hello World!\", \"Hello <b>\"+name+\"!</b>\")\n\n\tapp := newApp()\n\n\te := httptest.New(t, app)\n\n\te.GET(\"/\").Expect().Status(httptest.StatusOK).Body().IsEqual(expectedIndexRawBody)\n\te.GET(\"/\" + name).Expect().Status(httptest.StatusOK).Body().IsEqual(expectedHelloRawBody)\n}\n"
  },
  {
    "path": "_examples/view/quicktemplate/models/.gitkeep",
    "content": ""
  },
  {
    "path": "_examples/view/quicktemplate/templates/base.qtpl",
    "content": "This is our templates' base implementation.\n\n{% interface\nPartial {\n\tBody()\n}\n%}\n\n\nTemplate writes a template implementing the Partial interface.\n{% func Template(p Partial) %}\n<html>\n\t<head>\n\t\t<title>Quicktemplate integration with Iris</title>\n\t</head>\n\t<body>\n\t\t<div>\n\t\t\tHeader contents here...\n\t\t</div>\n\n\t\t<div style=\"margin:10px;\">\n\t\t\t{%= p.Body() %}\n\t\t</div>\n\n\t</body>\n\t<footer>\n\t\tFooter contents here...\n\t</footer>\n</html>\n{% endfunc %}\n\n\nBase template implementation. Other pages may inherit from it if they need\noverriding only certain Partial methods.\n{% code type Base struct {} %}\n{% func (b *Base) Body() %}This is the base body{% endfunc %}\n"
  },
  {
    "path": "_examples/view/quicktemplate/templates/base.qtpl.go",
    "content": "// This file is automatically generated by qtc from \"base.qtpl\".\n// See https://github.com/valyala/quicktemplate for details.\n\n// This is our templates' base implementation.\n//\n\n//line base.qtpl:3\n\npackage templates\n\n//line base.qtpl:3\n\nimport (\n\tqtio422016 \"io\"\n\n\tqt422016 \"github.com/valyala/quicktemplate\"\n)\n\n//line base.qtpl:3\n\nvar (\n\t_ = qtio422016.Copy\n\t_ = qt422016.AcquireByteBuffer\n)\n\n//line base.qtpl:4\n\ntype Partial interface {\n\t//line base.qtpl:4\n\tBody() string\n\t//line base.qtpl:4\n\tStreamBody(qw422016 *qt422016.Writer)\n\t//line base.qtpl:4\n\tWriteBody(qq422016 qtio422016.Writer)\n//line base.qtpl:4\n\n}\n\n// Template writes a template implementing the Partial interface.\n\n//line base.qtpl:11\n\nfunc StreamTemplate(qw422016 *qt422016.Writer, p Partial) {\n\t//line base.qtpl:11\n\tqw422016.N().S(`\n<html>\n\t<head>\n\t\t<title>Quicktemplate integration with Iris</title>\n\t</head>\n\t<body>\n\t\t<div>\n\t\t\tHeader contents here...\n\t\t</div>\n\n\t\t<div style=\"margin:10px;\">\n\t\t\t`)\n\t//line base.qtpl:22\n\tp.StreamBody(qw422016)\n\t//line base.qtpl:22\n\tqw422016.N().S(`\n\t\t</div>\n\n\t</body>\n\t<footer>\n\t\tFooter contents here...\n\t</footer>\n</html>\n`)\n//line base.qtpl:30\n\n}\n\n//line base.qtpl:30\n\nfunc WriteTemplate(qq422016 qtio422016.Writer, p Partial) {\n\t//line base.qtpl:30\n\tqw422016 := qt422016.AcquireWriter(qq422016)\n\t//line base.qtpl:30\n\tStreamTemplate(qw422016, p)\n\t//line base.qtpl:30\n\tqt422016.ReleaseWriter(qw422016)\n//line base.qtpl:30\n\n}\n\n//line base.qtpl:30\n\nfunc Template(p Partial) string {\n\t//line base.qtpl:30\n\tqb422016 := qt422016.AcquireByteBuffer()\n\t//line base.qtpl:30\n\tWriteTemplate(qb422016, p)\n\t//line base.qtpl:30\n\tqs422016 := string(qb422016.B)\n\t//line base.qtpl:30\n\tqt422016.ReleaseByteBuffer(qb422016)\n\t//line base.qtpl:30\n\treturn qs422016\n//line base.qtpl:30\n\n}\n\n// Base template implementation. Other pages may inherit from it if they need\n// overriding only certain Partial methods.\n\n//line base.qtpl:35\n\ntype Base struct{}\n\n//line base.qtpl:36\n\nfunc (b *Base) StreamBody(qw422016 *qt422016.Writer) {\n//line base.qtpl:36\n\n\tqw422016.N().S(`This is the base body`)\n}\n\n//line base.qtpl:36\n//line base.qtpl:36\n\nfunc (b *Base) WriteBody(qq422016 qtio422016.Writer) {\n\t//line base.qtpl:36\n\tqw422016 := qt422016.AcquireWriter(qq422016)\n\t//line base.qtpl:36\n\tb.StreamBody(qw422016)\n\t//line base.qtpl:36\n\tqt422016.ReleaseWriter(qw422016)\n//line base.qtpl:36\n\n}\n\n//line base.qtpl:36\n\nfunc (b *Base) Body() string {\n\t//line base.qtpl:36\n\tqb422016 := qt422016.AcquireByteBuffer()\n\t//line base.qtpl:36\n\tb.WriteBody(qb422016)\n\t//line base.qtpl:36\n\tqs422016 := string(qb422016.B)\n\t//line base.qtpl:36\n\tqt422016.ReleaseByteBuffer(qb422016)\n\t//line base.qtpl:36\n\treturn qs422016\n//line base.qtpl:36\n\n}\n"
  },
  {
    "path": "_examples/view/quicktemplate/templates/hello.qtpl",
    "content": "Hello template, implements the Partial's methods.\n\n{% code\ntype Hello struct {\n  Vars map[string]any\n}\n%}\n\n{% func (h *Hello) Body() %}\n\t<h1>{%v h.Vars[\"message\"] %}</h1>\n\t<div>\n\t\tHello <b>{%v h.Vars[\"name\"] %}!</b>\n\t</div>\n{% endfunc %}\n"
  },
  {
    "path": "_examples/view/quicktemplate/templates/hello.qtpl.go",
    "content": "// This file is automatically generated by qtc from \"hello.qtpl\".\n// See https://github.com/valyala/quicktemplate for details.\n\n// Hello template, implements the Partial's methods.\n//\n\n//line hello.qtpl:3\n\npackage templates\n\n//line hello.qtpl:3\n\nimport (\n\tqtio422016 \"io\"\n\n\tqt422016 \"github.com/valyala/quicktemplate\"\n)\n\n//line hello.qtpl:3\n\nvar (\n\t_ = qtio422016.Copy\n\t_ = qt422016.AcquireByteBuffer\n)\n\n//line hello.qtpl:4\n\ntype Hello struct {\n\tVars map[string]any\n}\n\n//line hello.qtpl:9\n\nfunc (h *Hello) StreamBody(qw422016 *qt422016.Writer) {\n\t//line hello.qtpl:9\n\tqw422016.N().S(`\n\t<h1>`)\n\t//line hello.qtpl:10\n\tqw422016.E().V(h.Vars[\"message\"])\n\t//line hello.qtpl:10\n\tqw422016.N().S(`</h1>\n\t<div>\n\t\tHello <b>`)\n\t//line hello.qtpl:12\n\tqw422016.E().V(h.Vars[\"name\"])\n\t//line hello.qtpl:12\n\tqw422016.N().S(`!</b>\n\t</div>\n`)\n//line hello.qtpl:14\n\n}\n\n//line hello.qtpl:14\n\nfunc (h *Hello) WriteBody(qq422016 qtio422016.Writer) {\n\t//line hello.qtpl:14\n\tqw422016 := qt422016.AcquireWriter(qq422016)\n\t//line hello.qtpl:14\n\th.StreamBody(qw422016)\n\t//line hello.qtpl:14\n\tqt422016.ReleaseWriter(qw422016)\n//line hello.qtpl:14\n\n}\n\n//line hello.qtpl:14\n\nfunc (h *Hello) Body() string {\n\t//line hello.qtpl:14\n\tqb422016 := qt422016.AcquireByteBuffer()\n\t//line hello.qtpl:14\n\th.WriteBody(qb422016)\n\t//line hello.qtpl:14\n\tqs422016 := string(qb422016.B)\n\t//line hello.qtpl:14\n\tqt422016.ReleaseByteBuffer(qb422016)\n\t//line hello.qtpl:14\n\treturn qs422016\n//line hello.qtpl:14\n\n}\n"
  },
  {
    "path": "_examples/view/quicktemplate/templates/index.qtpl",
    "content": "Index template, implements the Partial's methods.\n\n{% code\ntype Index struct {}\n%}\n\n{% func (i *Index) Body() %}\n\t<h1>Index Page</h1>\n\t<div>\n\t\tThis is our index page's body.\n\t</div>\n{% endfunc %}\n"
  },
  {
    "path": "_examples/view/quicktemplate/templates/index.qtpl.go",
    "content": "// This file is automatically generated by qtc from \"index.qtpl\".\n// See https://github.com/valyala/quicktemplate for details.\n\n// Index template, implements the Partial's methods.\n//\n\n//line index.qtpl:3\n\npackage templates\n\n//line index.qtpl:3\n\nimport (\n\tqtio422016 \"io\"\n\n\tqt422016 \"github.com/valyala/quicktemplate\"\n)\n\n//line index.qtpl:3\n\nvar (\n\t_ = qtio422016.Copy\n\t_ = qt422016.AcquireByteBuffer\n)\n\n//line index.qtpl:4\n\ntype Index struct{}\n\n//line index.qtpl:7\n\nfunc (i *Index) StreamBody(qw422016 *qt422016.Writer) {\n\t//line index.qtpl:7\n\tqw422016.N().S(`\n\t<h1>Index Page</h1>\n\t<div>\n\t\tThis is our index page's body.\n\t</div>\n`)\n//line index.qtpl:12\n\n}\n\n//line index.qtpl:12\n\nfunc (i *Index) WriteBody(qq422016 qtio422016.Writer) {\n\t//line index.qtpl:12\n\tqw422016 := qt422016.AcquireWriter(qq422016)\n\t//line index.qtpl:12\n\ti.StreamBody(qw422016)\n\t//line index.qtpl:12\n\tqt422016.ReleaseWriter(qw422016)\n//line index.qtpl:12\n\n}\n\n//line index.qtpl:12\n\nfunc (i *Index) Body() string {\n\t//line index.qtpl:12\n\tqb422016 := qt422016.AcquireByteBuffer()\n\t//line index.qtpl:12\n\ti.WriteBody(qb422016)\n\t//line index.qtpl:12\n\tqs422016 := string(qb422016.B)\n\t//line index.qtpl:12\n\tqt422016.ReleaseByteBuffer(qb422016)\n\t//line index.qtpl:12\n\treturn qs422016\n//line index.qtpl:12\n\n}\n"
  },
  {
    "path": "_examples/view/templ/go.mod",
    "content": "module github.com/kataras/iris/v12/_examples/view/templ\n\ngo 1.25\n\nrequire (\n\tgithub.com/a-h/templ v0.3.977\n\tgithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd\n)\n\nrequire (\n\tgithub.com/BurntSushi/toml v1.6.0 // indirect\n\tgithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect\n\tgithub.com/CloudyKit/jet/v6 v6.3.1 // indirect\n\tgithub.com/Joker/jade v1.1.3 // indirect\n\tgithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 // indirect\n\tgithub.com/andybalholm/brotli v1.2.0 // indirect\n\tgithub.com/aymerick/douceur v0.2.0 // indirect\n\tgithub.com/fatih/structs v1.1.0 // indirect\n\tgithub.com/flosch/pongo2/v4 v4.0.2 // indirect\n\tgithub.com/golang/snappy v1.0.0 // indirect\n\tgithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a // indirect\n\tgithub.com/google/uuid v1.6.0 // indirect\n\tgithub.com/gorilla/css v1.0.1 // indirect\n\tgithub.com/iris-contrib/schema v0.0.6 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/kataras/blocks v0.0.8 // indirect\n\tgithub.com/kataras/golog v0.1.12 // indirect\n\tgithub.com/kataras/pio v0.0.13 // indirect\n\tgithub.com/kataras/sitemap v0.0.6 // indirect\n\tgithub.com/kataras/tunnel v0.0.4 // indirect\n\tgithub.com/klauspost/compress v1.18.2 // indirect\n\tgithub.com/kr/pretty v0.3.1 // indirect\n\tgithub.com/mailgun/raymond/v2 v2.0.48 // indirect\n\tgithub.com/mailru/easyjson v0.9.1 // indirect\n\tgithub.com/microcosm-cc/bluemonday v1.0.27 // indirect\n\tgithub.com/rogpeppe/go-internal v1.10.0 // indirect\n\tgithub.com/russross/blackfriday/v2 v2.1.0 // indirect\n\tgithub.com/schollz/closestmatch v2.1.0+incompatible // indirect\n\tgithub.com/sirupsen/logrus v1.8.1 // indirect\n\tgithub.com/stretchr/testify v1.11.1 // indirect\n\tgithub.com/tdewolff/minify/v2 v2.24.8 // indirect\n\tgithub.com/tdewolff/parse/v2 v2.8.5 // indirect\n\tgithub.com/valyala/bytebufferpool v1.0.0 // indirect\n\tgithub.com/vmihailenco/msgpack/v5 v5.4.1 // indirect\n\tgithub.com/vmihailenco/tagparser/v2 v2.0.0 // indirect\n\tgithub.com/yosssi/ace v0.0.5 // indirect\n\tgolang.org/x/crypto v0.47.0 // indirect\n\tgolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 // indirect\n\tgolang.org/x/net v0.49.0 // indirect\n\tgolang.org/x/sys v0.40.0 // indirect\n\tgolang.org/x/text v0.33.0 // indirect\n\tgolang.org/x/time v0.14.0 // indirect\n\tgoogle.golang.org/protobuf v1.36.11 // indirect\n\tgopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect\n\tgopkg.in/ini.v1 v1.67.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n"
  },
  {
    "path": "_examples/view/templ/go.sum",
    "content": "github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=\ngithub.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=\ngithub.com/CloudyKit/jet/v6 v6.3.1 h1:6IAo5Cx21xrHVaR8zzXN5gJatKV/wO7Nf6bfCnCSbUw=\ngithub.com/CloudyKit/jet/v6 v6.3.1/go.mod h1:lf8ksdNsxZt7/yH/3n4vJQWA9RUq4wpaHtArHhGVMOw=\ngithub.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc=\ngithub.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=\ngithub.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=\ngithub.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 h1:dG7/gLroJGht/jSQtHiLvT48Hxn+crbmvyItZC8cWOs=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05/go.mod h1:NYezi6wtnJtBm5btoprXc5SvAdqH0XTXWnUup0MptAI=\ngithub.com/a-h/templ v0.3.977 h1:kiKAPXTZE2Iaf8JbtM21r54A8bCNsncrfnokZZSrSDg=\ngithub.com/a-h/templ v0.3.977/go.mod h1:oCZcnKRf5jjsGpf2yELzQfodLphd2mwecwG4Crk5HBo=\ngithub.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=\ngithub.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=\ngithub.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=\ngithub.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=\ngithub.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=\ngithub.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=\ngithub.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=\ngithub.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=\ngithub.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=\ngithub.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw=\ngithub.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8=\ngithub.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=\ngithub.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=\ngithub.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=\ngithub.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a h1:l7A0loSszR5zHd/qK53ZIHMO8b3bBSmENnQ6eKnUT0A=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=\ngithub.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=\ngithub.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=\ngithub.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=\ngithub.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=\ngithub.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2 h1:T9THsdP1woyAqKHwjkEsbCnMefsAFvk8iJJKokcJ3Go=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2/go.mod h1:JLDgIqnFy5loDSUv1OA2j0mb6p/rDhiCqigP22Uq9xE=\ngithub.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw=\ngithub.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/kataras/blocks v0.0.8 h1:MrpVhoFTCR2v1iOOfGng5VJSILKeZZI+7NGfxEh3SUM=\ngithub.com/kataras/blocks v0.0.8/go.mod h1:9Jm5zx6BB+06NwA+OhTbHW1xkMOYxahnqTN5DveZ2Yg=\ngithub.com/kataras/golog v0.1.12 h1:Bu7I/G4ilJlbfzjmU39O9N+2uO1pBcMK045fzZ4ytNg=\ngithub.com/kataras/golog v0.1.12/go.mod h1:wrGSbOiBqbQSQznleVNX4epWM8rl9SJ/rmEacl0yqy4=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd h1:oMsQgqKACbUdlaIWnN+EZzzAnHkw90hE/y/R0BSij2U=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd/go.mod h1:yucjtDl9Vn3OctWM1fi0MuM9OckemC7kEreFa9QtPF8=\ngithub.com/kataras/pio v0.0.13 h1:x0rXVX0fviDTXOOLOmr4MUxOabu1InVSTu5itF8CXCM=\ngithub.com/kataras/pio v0.0.13/go.mod h1:k3HNuSw+eJ8Pm2lA4lRhg3DiCjVgHlP8hmXApSej3oM=\ngithub.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY=\ngithub.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4=\ngithub.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA=\ngithub.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw=\ngithub.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=\ngithub.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=\ngithub.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=\ngithub.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8=\ngithub.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=\ngithub.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=\ngithub.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=\ngithub.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=\ngithub.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=\ngithub.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=\ngithub.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=\ngithub.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=\ngithub.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=\ngithub.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=\ngithub.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=\ngithub.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=\ngithub.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk=\ngithub.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=\ngithub.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=\ngithub.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=\ngithub.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=\ngithub.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/tdewolff/minify/v2 v2.24.8 h1:58/VjsbevI4d5FGV0ZSuBrHMSSkH4MCH0sIz/eKIauE=\ngithub.com/tdewolff/minify/v2 v2.24.8/go.mod h1:0Ukj0CRpo/sW/nd8uZ4ccXaV1rEVIWA3dj8U7+Shhfw=\ngithub.com/tdewolff/parse/v2 v2.8.5 h1:ZmBiA/8Do5Rpk7bDye0jbbDUpXXbCdc3iah4VeUvwYU=\ngithub.com/tdewolff/parse/v2 v2.8.5/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=\ngithub.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE=\ngithub.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=\ngithub.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=\ngithub.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=\ngithub.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=\ngithub.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=\ngithub.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=\ngithub.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=\ngithub.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA=\ngithub.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0=\ngithub.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=\ngithub.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=\ngithub.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=\ngolang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=\ngolang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=\ngolang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=\ngolang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=\ngolang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=\ngolang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=\ngolang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=\ngolang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=\ngoogle.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=\ngopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nmoul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs=\nmoul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE=\n"
  },
  {
    "path": "_examples/view/templ/hello.templ",
    "content": "package main\n\ntempl hello(name string) {\n\t<div>Hello, <strong>{ name }</strong></div>\n}"
  },
  {
    "path": "_examples/view/templ/hello_templ.go",
    "content": "// Code generated by templ - DO NOT EDIT.\n\n// templ: version: 0.2.476\npackage main\n\n//lint:file-ignore SA4006 This context is only used if a nested component is present.\n\nimport \"github.com/a-h/templ\"\nimport \"context\"\nimport \"io\"\nimport \"bytes\"\n\nfunc hello(name string) templ.Component {\n\treturn templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {\n\t\ttempl_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)\n\t\tif !templ_7745c5c3_IsBuffer {\n\t\t\ttempl_7745c5c3_Buffer = templ.GetBuffer()\n\t\t\tdefer templ.ReleaseBuffer(templ_7745c5c3_Buffer)\n\t\t}\n\t\tctx = templ.InitializeContext(ctx)\n\t\ttempl_7745c5c3_Var1 := templ.GetChildren(ctx)\n\t\tif templ_7745c5c3_Var1 == nil {\n\t\t\ttempl_7745c5c3_Var1 = templ.NopComponent\n\t\t}\n\t\tctx = templ.ClearChildren(ctx)\n\t\t_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(\"<div>\")\n\t\tif templ_7745c5c3_Err != nil {\n\t\t\treturn templ_7745c5c3_Err\n\t\t}\n\t\ttempl_7745c5c3_Var2 := `Hello, `\n\t\t_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var2)\n\t\tif templ_7745c5c3_Err != nil {\n\t\t\treturn templ_7745c5c3_Err\n\t\t}\n\t\t_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(\"<strong>\")\n\t\tif templ_7745c5c3_Err != nil {\n\t\t\treturn templ_7745c5c3_Err\n\t\t}\n\t\tvar templ_7745c5c3_Var3 string = name\n\t\t_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))\n\t\tif templ_7745c5c3_Err != nil {\n\t\t\treturn templ_7745c5c3_Err\n\t\t}\n\t\t_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(\"</strong></div>\")\n\t\tif templ_7745c5c3_Err != nil {\n\t\t\treturn templ_7745c5c3_Err\n\t\t}\n\t\tif !templ_7745c5c3_IsBuffer {\n\t\t\t_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)\n\t\t}\n\t\treturn templ_7745c5c3_Err\n\t})\n}\n"
  },
  {
    "path": "_examples/view/templ/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n)\n\n// $ go install github.com/a-h/templ/cmd/templ@latest\n// $ templ generate\n// $ go run .\nfunc main() {\n\tcomponent := hello(\"Makis\")\n\n\tapp := iris.New()\n\tapp.Get(\"/\", iris.Component(component))\n\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/view/template_ace_0/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\t// Read about its markup syntax at: https://github.com/yosssi/ace\n\ttmpl := iris.Ace(\"./views\", \".ace\")\n\t// tmpl.Layout(\"layouts/main.ace\") -> global layout for all pages.\n\n\tapp.RegisterView(tmpl)\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tif err := ctx.View(\"index\", iris.Map{\n\t\t\t\"Title\": \"Title of The Page\",\n\t\t}); err != nil {\n\t\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\t\treturn\n\t\t}\n\t})\n\n\tapp.Get(\"/layout\", func(ctx iris.Context) {\n\t\tctx.ViewLayout(\"layouts/main\")        // layout for that response.\n\t\tif err := ctx.View(\"index\", iris.Map{ // file extension is optional.\n\t\t\t\"Title\": \"Title of the main Page\",\n\t\t}); err != nil {\n\t\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\t\treturn\n\t\t}\n\t})\n\n\t// otherGroup := app.Party(\"/other\").Layout(\"layouts/other.ace\") -> layout for that party.\n\t// otherGroup.Get(\"/\", func(ctx iris.Context) { ctx.View(\"index.ace\", [...]) })\n\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/view/template_ace_0/views/index.ace",
    "content": "= include partials/header.ace .\n\nh2 {{.Title}}\n\nh3 Body\n\n= include partials/footer.ace ."
  },
  {
    "path": "_examples/view/template_ace_0/views/layouts/main.ace",
    "content": "= doctype html\nhtml\n  head\n    title Main Page\n  body\n    h1 Layout\n    {{ yield . }}"
  },
  {
    "path": "_examples/view/template_ace_0/views/partials/footer.ace",
    "content": "h1 Partial Footer"
  },
  {
    "path": "_examples/view/template_ace_0/views/partials/header.ace",
    "content": "h1 Partial Header"
  },
  {
    "path": "_examples/view/template_blocks_0/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\t// Read about its syntax at: https://github.com/kataras/blocks\n\tapp.RegisterView(iris.Blocks(\"./views\", \".html\").Reload(true))\n\n\tapp.Get(\"/\", index)\n\tapp.Get(\"/500\", internalServerError)\n\n\tapp.Listen(\":8080\")\n}\n\nfunc index(ctx iris.Context) {\n\tdata := iris.Map{\n\t\t\"Title\": \"Page Title\",\n\t}\n\n\tctx.ViewLayout(\"main\")\n\tif err := ctx.View(\"index\", data); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n}\n\nfunc internalServerError(ctx iris.Context) {\n\tctx.StatusCode(iris.StatusInternalServerError)\n\n\tdata := iris.Map{\n\t\t\"Code\":    iris.StatusInternalServerError,\n\t\t\"Message\": \"Internal Server Error\",\n\t}\n\n\tctx.ViewLayout(\"error\")\n\tif err := ctx.View(\"500\", data); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "_examples/view/template_blocks_0/views/500.html",
    "content": "{{ define \"content\" }}\n<h1>Internal Server Error</h1>\n{{ end }}\n\n{{ define \"message\" }}\n<p style=\"color:red;\">{{.Message}}</p>\n{{ end }}"
  },
  {
    "path": "_examples/view/template_blocks_0/views/index.html",
    "content": "<h1>Index Body</h1>"
  },
  {
    "path": "_examples/view/template_blocks_0/views/layouts/error.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>{{.Code}}</title>\n</head>\n<body>\n    {{ template \"content\" . }}\n\n    {{ block \"message\" . }}Default Error Message{{ end }}\n</body>\n</html>"
  },
  {
    "path": "_examples/view/template_blocks_0/views/layouts/main.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>{{ if .Title }}{{ .Title }}{{ else }}Default Main Title{{ end }}</title>\n</head>\n<body>\n    {{ template \"content\" . }}\n\n<footer>{{ partial \"partials/footer\" . }}</footer>\n</body>\n</html>"
  },
  {
    "path": "_examples/view/template_blocks_0/views/partials/footer.html",
    "content": "<h3>Footer Partial</h3>"
  },
  {
    "path": "_examples/view/template_blocks_1_embedded/bindata.go",
    "content": "// Code generated by go-bindata. (@generated) DO NOT EDIT.\n\n// Package main generated by go-bindata.// sources:\n// ../template_blocks_0/views/500.html\n// ../template_blocks_0/views/index.html\n// ../template_blocks_0/views/layouts/error.html\n// ../template_blocks_0/views/layouts/main.html\n// ../template_blocks_0/views/partials/footer.html\npackage main\n\nimport (\n\t\"bytes\"\n\t\"compress/gzip\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"time\"\n)\n\nfunc bindataRead(data []byte, name string) ([]byte, error) {\n\tgz, err := gzip.NewReader(bytes.NewBuffer(data))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"read %q: %v\", name, err)\n\t}\n\n\tvar buf bytes.Buffer\n\t_, err = io.Copy(&buf, gz)\n\tclErr := gz.Close()\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"read %q: %v\", name, err)\n\t}\n\tif clErr != nil {\n\t\treturn nil, err\n\t}\n\n\treturn buf.Bytes(), nil\n}\n\ntype asset struct {\n\tbytes []byte\n\tinfo  os.FileInfo\n}\n\ntype bindataFileInfo struct {\n\tname    string\n\tsize    int64\n\tmode    os.FileMode\n\tmodTime time.Time\n}\n\n// Name return file name\nfunc (fi bindataFileInfo) Name() string {\n\treturn fi.name\n}\n\n// Size return file size\nfunc (fi bindataFileInfo) Size() int64 {\n\treturn fi.size\n}\n\n// Mode return file mode\nfunc (fi bindataFileInfo) Mode() os.FileMode {\n\treturn fi.mode\n}\n\n// ModTime return file modify time\nfunc (fi bindataFileInfo) ModTime() time.Time {\n\treturn fi.modTime\n}\n\n// IsDir return file whether a directory\nfunc (fi bindataFileInfo) IsDir() bool {\n\treturn fi.mode&os.ModeDir != 0\n}\n\n// Sys return file is sys mode\nfunc (fi bindataFileInfo) Sys() any {\n\treturn nil\n}\n\ntype assetFile struct {\n\t*bytes.Reader\n\tname            string\n\tchildInfos      []os.FileInfo\n\tchildInfoOffset int\n}\n\ntype assetOperator struct{}\n\n// Open implement http.FileSystem interface\nfunc (f *assetOperator) Open(name string) (http.File, error) {\n\tvar err error\n\tif len(name) > 0 && name[0] == '/' {\n\t\tname = name[1:]\n\t}\n\tcontent, err := Asset(name)\n\tif err == nil {\n\t\treturn &assetFile{name: name, Reader: bytes.NewReader(content)}, nil\n\t}\n\tchildren, err := AssetDir(name)\n\tif err == nil {\n\t\tchildInfos := make([]os.FileInfo, 0, len(children))\n\t\tfor _, child := range children {\n\t\t\tchildPath := filepath.Join(name, child)\n\t\t\tinfo, errInfo := AssetInfo(filepath.Join(name, child))\n\t\t\tif errInfo == nil {\n\t\t\t\tchildInfos = append(childInfos, info)\n\t\t\t} else {\n\t\t\t\tchildInfos = append(childInfos, newDirFileInfo(childPath))\n\t\t\t}\n\t\t}\n\t\treturn &assetFile{name: name, childInfos: childInfos}, nil\n\t} else {\n\t\t// If the error is not found, return an error that will\n\t\t// result in a 404 error. Otherwise the server returns\n\t\t// a 500 error for files not found.\n\t\tif strings.Contains(err.Error(), \"not found\") {\n\t\t\treturn nil, os.ErrNotExist\n\t\t}\n\t\treturn nil, err\n\t}\n}\n\n// Close no need do anything\nfunc (f *assetFile) Close() error {\n\treturn nil\n}\n\n// Readdir read dir's children file info\nfunc (f *assetFile) Readdir(count int) ([]os.FileInfo, error) {\n\tif len(f.childInfos) == 0 {\n\t\treturn nil, os.ErrNotExist\n\t}\n\tif count <= 0 {\n\t\treturn f.childInfos, nil\n\t}\n\tif f.childInfoOffset+count > len(f.childInfos) {\n\t\tcount = len(f.childInfos) - f.childInfoOffset\n\t}\n\toffset := f.childInfoOffset\n\tf.childInfoOffset += count\n\treturn f.childInfos[offset : offset+count], nil\n}\n\n// Stat read file info from asset item\nfunc (f *assetFile) Stat() (os.FileInfo, error) {\n\tif len(f.childInfos) != 0 {\n\t\treturn newDirFileInfo(f.name), nil\n\t}\n\treturn AssetInfo(f.name)\n}\n\n// newDirFileInfo return default dir file info\nfunc newDirFileInfo(name string) os.FileInfo {\n\treturn &bindataFileInfo{\n\t\tname:    name,\n\t\tsize:    0,\n\t\tmode:    os.FileMode(2147484068), // equal os.FileMode(0644)|os.ModeDir\n\t\tmodTime: time.Time{}}\n}\n\n// AssetFile return a http.FileSystem instance that data backend by asset\nfunc AssetFile() http.FileSystem {\n\treturn &assetOperator{}\n}\n\nvar __500Html = []byte(\"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\x7c\\x8f\\xb1\\x4e\\xf3\\x30\\x14\\x85\\xf7\\x48\\x79\\x87\\xfb\\x67\\xe9\\x8f\\xd4\\xa4\\xea\\x5a\\x42\\x37\\x06\\x06\\xa6\\x22\\x21\\x46\\xc7\\x3e\\xad\\x2d\\x9c\\x7b\\x23\\xdb\\x49\\x89\\xa2\\xbc\\x3b\\x6a\\x5a\\x04\\x2c\\xac\\xf7\\x7e\\xe7\\x3b\\x3a\\xf5\\xbf\\xb2\\xa4\\x37\\xe9\\x49\\x2b\\x26\\x83\\xa3\\x63\\x50\\x2b\\x01\\x94\\xac\\x62\\x12\\x06\\x35\\x5e\\xf4\\x7b\\x95\\x67\\x2f\\x16\\x17\\x40\\xf5\\x3e\\x2d\\x77\\x17\\xa9\\xd0\\xc2\\x09\\x9c\\x0a\\x3a\\x5b\\xa7\\x2d\\x45\\x2b\\xbd\\x37\\xd4\\x5c\\xd2\\xa0\\x56\\x39\\xa6\\x84\\xb6\\xf3\\x2a\\x61\\x15\\xa9\\x11\\x33\\x56\\x79\\x76\\x90\\x35\\x61\\x00\\x93\\x3b\\x92\\x4b\\xab\\x48\\xad\\x8b\\xd1\\xf1\\x89\\xfe\\x47\\x80\\x1c\\x1b\\x7c\\x54\\x36\\xb5\\xfe\\x6e\\x7d\\x7d\\x2b\\x63\\x60\\x48\\xf5\\x49\\x5a\\x95\\x9c\\x56\\xde\\x8f\\xd4\\x8c\\x4b\\xc3\\xe0\\x70\\x26\\xf0\\xc9\\x31\\xaa\\x3c\\x7b\\xb5\\x60\\x1a\\xa5\\x27\\x06\\x0c\\x25\\xf9\\x63\\xce\\x7a\\xe1\\xac\\x1a\\x70\\xe1\\x9a\\x1b\\x13\\x3b\\x68\\x77\\x74\\x7a\\x97\\x67\\x65\\xb9\\xcf\\xb3\\x69\\xfa\\x52\\x7c\\x0f\\x9d\\xe7\\x3c\\xab\\xed\\x76\\xff\\xc4\\x09\\x81\\x95\\xa7\\x03\\xc2\\x80\\x40\\x8f\\x21\\x48\\xa8\\x37\\x76\\x7b\\xcd\\x81\\xcd\\x82\\xfe\\x92\\xb4\\x88\\x51\\x9d\\x70\\x93\\x74\\x14\\xd3\\xe8\\xf1\\x50\\x68\\xf1\\x12\\x76\\x01\\xe6\\xbe\\xd8\\x4f\\x53\\xf5\\x7c\\xa5\\xe6\\xb9\\xde\\x74\\x3f\\x65\\x9f\\x01\\x00\\x00\\xff\\xff\\xb3\\xfa\\x91\\xbd\\xaa\\x01\\x00\\x00\")\n\nfunc _500HtmlBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t__500Html,\n\t\t\"500.html\",\n\t)\n}\n\nfunc _500Html() (*asset, error) {\n\tbytes, err := _500HtmlBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"500.html\", size: 426, mode: os.FileMode(438), modTime: time.Unix(1599156854, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\nvar _indexHtml = []byte(\"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\xb2\\xc9\\x30\\xb4\\xf3\\xcc\\x4b\\x49\\xad\\x50\\x70\\xca\\x4f\\xa9\\xb4\\xd1\\xcf\\x30\\xb4\\x03\\x04\\x00\\x00\\xff\\xff\\xcc\\x4b\\x98\\x69\\x13\\x00\\x00\\x00\")\n\nfunc indexHtmlBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_indexHtml,\n\t\t\"index.html\",\n\t)\n}\n\nfunc indexHtml() (*asset, error) {\n\tbytes, err := indexHtmlBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"index.html\", size: 19, mode: os.FileMode(438), modTime: time.Unix(1599156854, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\nvar _layoutsErrorHtml = []byte(\"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\x54\\x90\\xbf\\x4e\\xf3\\x40\\x10\\xc4\\x7b\\x4b\\x7e\\x87\\xfd\\xb6\\xfe\\x6c\\x43\\x47\\x71\\xe7\\x26\\x40\\x0b\\x45\\x28\\x28\\x37\\x77\\xa3\\xf8\\xc4\\xfd\\x89\\xe2\\x55\\x22\\x74\\xf2\\xbb\\xa3\\x18\\x83\\x44\\xb5\\xda\\x99\\x9f\\x66\\x57\\x63\\xfe\\x3d\\xbe\\xec\\xf6\\xef\\xaf\\x4f\\x34\\x69\\x8a\\x63\\xdb\\x98\\xdb\\xa4\\x28\\xf9\\x68\\x19\\x99\\x57\\x05\\xe2\\xc7\\xb6\\x21\\x22\\x32\\x09\\x2a\\xe4\\x26\\x39\\xcf\\x50\\xcb\\x6f\\xfb\\xe7\\xee\\x81\\xff\\x78\\x59\\x12\\x2c\\x5f\\x02\\xae\\xa7\\x72\\x56\\x26\\x57\\xb2\\x22\\xab\\xe5\\x6b\\xf0\\x3a\\x59\\x8f\\x4b\\x70\\xe8\\xd6\\xe5\\x3f\\x85\\x1c\\x34\\x48\\xec\\x66\\x27\\x11\\xf6\\xbe\\xbf\\xfb\\xcd\\xd2\\xa0\\x11\\x63\\xad\\xfd\\xae\\x78\\x2c\\x8b\\x19\\xbe\\x85\\xb6\\x31\\xc3\\xf6\\x8e\\x39\\x14\\xff\\xb9\\xe1\\xb5\\x92\\x22\\x9d\\xa2\\x28\\x88\\xb7\\x8b\\x4c\\xfd\\xb2\\xb4\\xcd\\x0f\\x70\\x88\\xc5\\x7d\\x10\\x27\\xcc\\xb3\\x1c\\xb1\\x9a\\xb5\\x22\\xfb\\x1b\\x63\\x86\\x2d\\xcb\\x0c\\x6b\\x0b\\x5f\\x01\\x00\\x00\\xff\\xff\\xbe\\xb7\\x11\\x67\\x15\\x01\\x00\\x00\")\n\nfunc layoutsErrorHtmlBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_layoutsErrorHtml,\n\t\t\"layouts/error.html\",\n\t)\n}\n\nfunc layoutsErrorHtml() (*asset, error) {\n\tbytes, err := layoutsErrorHtmlBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"layouts/error.html\", size: 277, mode: os.FileMode(438), modTime: time.Unix(1599156854, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\nvar _layoutsMainHtml = []byte(\"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\x54\\x90\\xb1\\x4e\\xf4\\x30\\x10\\x84\\xfb\\x48\\x79\\x87\\xf9\\x5d\\xff\\x49\\xa0\\xa3\\xb0\\xd3\\x70\\xd0\\x21\\x28\\x42\\x41\\xb9\\x24\\x1b\\x62\\xc9\\x71\\xa2\\x64\\xb9\\x13\\xb2\\xfc\\xee\\xc8\\x39\\x0b\\xe9\\x2a\\xcf\\xfa\\xb3\\x66\\xc6\\xab\\xff\\x9d\\x5e\\x1f\\xbb\\x8f\\xb7\\x27\\x4c\\x32\\xbb\\xb6\\x2c\\x74\\x3a\\xe1\\xc8\\x7f\\x19\\xc5\\x5e\\x1d\\x37\\x4c\\x43\\x5b\\x16\\x00\\xa0\\x67\\x16\\x42\\x3f\\xd1\\xb6\\xb3\\x18\\xf5\\xde\\x3d\\x57\\x0f\\xea\\x86\\x79\\x9a\\xd9\\xa8\\xb3\\xe5\\xcb\\xba\\x6c\\xa2\\xd0\\x2f\\x5e\\xd8\\x8b\\x51\\x17\\x3b\\xc8\\x64\\x06\\x3e\\xdb\\x9e\\xab\\x63\\xf8\\x0f\\xeb\\xad\\x58\\x72\\xd5\\xde\\x93\\x63\\x73\\x5f\\xdf\\xfd\\x79\\x89\\x15\\xc7\\x6d\\x08\\xb0\\x23\\xea\\x2e\\x0d\\x88\\x31\\x84\\x1b\\xcd\\x6e\\x4f\\xea\\xc4\\x23\\x7d\\x3b\\xc1\\x0b\\x59\\x8f\\x03\\x27\\xe6\\x07\\xc4\\xa8\\x9b\\xab\\x4f\\x59\\xe8\\x26\\xff\\x42\\x7f\\x2e\\xc3\\x4f\\x4e\\x09\\x01\\xc2\\xf3\\xea\\x48\\x18\\x2a\\x17\\x55\\xa8\\x11\\x63\\x59\\x94\\x85\\x1e\\x97\\x45\\x78\\x4b\\x25\\x56\\xda\\x52\\x4f\\xa8\\x2c\\xf6\\xe6\\xca\\x14\\xea\\x14\\x92\\x1f\\xa6\\x94\\xec\\xae\\x9b\\x63\\x9d\\xbf\\x01\\x00\\x00\\xff\\xff\\x44\\x95\\x63\\x98\\x5e\\x01\\x00\\x00\")\n\nfunc layoutsMainHtmlBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_layoutsMainHtml,\n\t\t\"layouts/main.html\",\n\t)\n}\n\nfunc layoutsMainHtml() (*asset, error) {\n\tbytes, err := layoutsMainHtmlBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"layouts/main.html\", size: 350, mode: os.FileMode(438), modTime: time.Unix(1599156854, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\nvar _partialsFooterHtml = []byte(\"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\xb2\\xc9\\x30\\xb6\\x73\\xcb\\xcf\\x2f\\x49\\x2d\\x52\\x08\\x48\\x2c\\x2a\\xc9\\x4c\\xcc\\xb1\\xd1\\xcf\\x30\\xb6\\x03\\x04\\x00\\x00\\xff\\xff\\x08\\xe6\\xe9\\xf8\\x17\\x00\\x00\\x00\")\n\nfunc partialsFooterHtmlBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_partialsFooterHtml,\n\t\t\"partials/footer.html\",\n\t)\n}\n\nfunc partialsFooterHtml() (*asset, error) {\n\tbytes, err := partialsFooterHtmlBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"partials/footer.html\", size: 23, mode: os.FileMode(438), modTime: time.Unix(1599156854, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\n// Asset loads and returns the asset for the given name.\n// It returns an error if the asset could not be found or\n// could not be loaded.\nfunc Asset(name string) ([]byte, error) {\n\tcanonicalName := strings.Replace(name, \"\\\\\", \"/\", -1)\n\tif f, ok := _bindata[canonicalName]; ok {\n\t\ta, err := f()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"Asset %s can't read by error: %v\", name, err)\n\t\t}\n\t\treturn a.bytes, nil\n\t}\n\treturn nil, fmt.Errorf(\"Asset %s not found\", name)\n}\n\n// MustAsset is like Asset but panics when Asset would return an error.\n// It simplifies safe initialization of global variables.\nfunc MustAsset(name string) []byte {\n\ta, err := Asset(name)\n\tif err != nil {\n\t\tpanic(\"asset: Asset(\" + name + \"): \" + err.Error())\n\t}\n\n\treturn a\n}\n\n// AssetInfo loads and returns the asset info for the given name.\n// It returns an error if the asset could not be found or\n// could not be loaded.\nfunc AssetInfo(name string) (os.FileInfo, error) {\n\tcanonicalName := strings.Replace(name, \"\\\\\", \"/\", -1)\n\tif f, ok := _bindata[canonicalName]; ok {\n\t\ta, err := f()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"AssetInfo %s can't read by error: %v\", name, err)\n\t\t}\n\t\treturn a.info, nil\n\t}\n\treturn nil, fmt.Errorf(\"AssetInfo %s not found\", name)\n}\n\n// AssetNames returns the names of the assets.\nfunc AssetNames() []string {\n\tnames := make([]string, 0, len(_bindata))\n\tfor name := range _bindata {\n\t\tnames = append(names, name)\n\t}\n\treturn names\n}\n\n// _bindata is a table, holding each asset generator, mapped to its name.\nvar _bindata = map[string]func() (*asset, error){\n\t\"500.html\":             _500Html,\n\t\"index.html\":           indexHtml,\n\t\"layouts/error.html\":   layoutsErrorHtml,\n\t\"layouts/main.html\":    layoutsMainHtml,\n\t\"partials/footer.html\": partialsFooterHtml,\n}\n\n// AssetDir returns the file names below a certain\n// directory embedded in the file by go-bindata.\n// For example if you run go-bindata on data/... and data contains the\n// following hierarchy:\n//\n//\tdata/\n//\t  foo.txt\n//\t  img/\n//\t    a.png\n//\t    b.png\n//\n// then AssetDir(\"data\") would return []string{\"foo.txt\", \"img\"}\n// AssetDir(\"data/img\") would return []string{\"a.png\", \"b.png\"}\n// AssetDir(\"foo.txt\") and AssetDir(\"nonexistent\") would return an error\n// AssetDir(\"\") will return []string{\"data\"}.\nfunc AssetDir(name string) ([]string, error) {\n\tnode := _bintree\n\tif len(name) != 0 {\n\t\tcanonicalName := strings.Replace(name, \"\\\\\", \"/\", -1)\n\t\tpathList := strings.Split(canonicalName, \"/\")\n\t\tfor _, p := range pathList {\n\t\t\tnode = node.Children[p]\n\t\t\tif node == nil {\n\t\t\t\treturn nil, fmt.Errorf(\"Asset %s not found\", name)\n\t\t\t}\n\t\t}\n\t}\n\tif node.Func != nil {\n\t\treturn nil, fmt.Errorf(\"Asset %s not found\", name)\n\t}\n\trv := make([]string, 0, len(node.Children))\n\tfor childName := range node.Children {\n\t\trv = append(rv, childName)\n\t}\n\treturn rv, nil\n}\n\ntype bintree struct {\n\tFunc     func() (*asset, error)\n\tChildren map[string]*bintree\n}\n\nvar _bintree = &bintree{nil, map[string]*bintree{\n\t\"500.html\":   {_500Html, map[string]*bintree{}},\n\t\"index.html\": {indexHtml, map[string]*bintree{}},\n\t\"layouts\": {nil, map[string]*bintree{\n\t\t\"error.html\": {layoutsErrorHtml, map[string]*bintree{}},\n\t\t\"main.html\":  {layoutsMainHtml, map[string]*bintree{}},\n\t}},\n\t\"partials\": {nil, map[string]*bintree{\n\t\t\"footer.html\": {partialsFooterHtml, map[string]*bintree{}},\n\t}},\n}}\n\n// RestoreAsset restores an asset under the given directory\nfunc RestoreAsset(dir, name string) error {\n\tdata, err := Asset(name)\n\tif err != nil {\n\t\treturn err\n\t}\n\tinfo, err := AssetInfo(name)\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = os.WriteFile(_filePath(dir, name), data, info.Mode())\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// RestoreAssets restores an asset under the given directory recursively\nfunc RestoreAssets(dir, name string) error {\n\tchildren, err := AssetDir(name)\n\t// File\n\tif err != nil {\n\t\treturn RestoreAsset(dir, name)\n\t}\n\t// Dir\n\tfor _, child := range children {\n\t\terr = RestoreAssets(dir, filepath.Join(name, child))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc _filePath(dir, name string) string {\n\tcanonicalName := strings.Replace(name, \"\\\\\", \"/\", -1)\n\treturn filepath.Join(append([]string{dir}, strings.Split(canonicalName, \"/\")...)...)\n}\n"
  },
  {
    "path": "_examples/view/template_blocks_1_embedded/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\n// $ go install github.com/go-bindata/go-bindata/v3/go-bindata@latest\n//\n// $ go-bindata -fs -prefix \"../template_blocks_0/views\" ../template_blocks_0/views/...\n// $ go run .\n//\n// # OR: go-bindata -fs -prefix \"views\" ./views/... if the views dir is rel to the executable.\n// # OR: go-bindata -fs -prefix \"../template_blocks_0\" ../template_blocks_0/views/...\n// # with iris.Blocks(AssetFile()).RootDir(\"/views\")\n//\n// System files are not used, you can optionally delete the folder and run the example now.\nfunc main() {\n\tapp := iris.New()\n\tapp.RegisterView(iris.Blocks(AssetFile(), \".html\"))\n\n\tapp.Get(\"/\", index)\n\tapp.Get(\"/500\", internalServerError)\n\n\tapp.Listen(\":8080\")\n}\n\nfunc index(ctx iris.Context) {\n\tdata := iris.Map{\n\t\t\"Title\": \"Page Title\",\n\t}\n\n\tctx.ViewLayout(\"main\")\n\tif err := ctx.View(\"index\", data); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n}\n\nfunc internalServerError(ctx iris.Context) {\n\tctx.StatusCode(iris.StatusInternalServerError)\n\n\tdata := iris.Map{\n\t\t\"Code\":    iris.StatusInternalServerError,\n\t\t\"Message\": \"Internal Server Error\",\n\t}\n\n\tctx.ViewLayout(\"error\")\n\tif err := ctx.View(\"500\", data); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "_examples/view/template_blocks_2/main.go",
    "content": "package main\n\nimport \"github.com/kataras/iris/v12\"\n\n// Based on https://github.com/kataras/iris/issues/2214.\nfunc main() {\n\tapp := initApp()\n\tapp.Listen(\":8080\")\n}\n\nfunc initApp() *iris.Application {\n\tapp := iris.New()\n\tapp.Logger().SetLevel(\"debug\")\n\n\ttmpl := iris.Blocks(\"./src/public/html\", \".html\")\n\ttmpl.Layout(\"main\")\n\tapp.RegisterView(tmpl)\n\n\tapp.Get(\"/list\", func(ctx iris.Context) {\n\t\tctx.View(\"files/list\")\n\t})\n\n\tapp.Get(\"/menu\", func(ctx iris.Context) {\n\t\tctx.View(\"menu/menu\")\n\t})\n\n\tapp.Get(\"/list2\", func(ctx iris.Context) {\n\t\tctx.ViewLayout(\"secondary\")\n\t\tctx.View(\"files/list\")\n\t})\n\n\tapp.Get(\"/menu2\", func(ctx iris.Context) {\n\t\tctx.ViewLayout(\"secondary\")\n\t\tctx.View(\"menu/menu\")\n\t})\n\n\treturn app\n}\n"
  },
  {
    "path": "_examples/view/template_blocks_2/src/public/html/files/list.html",
    "content": "{{ define \"title\"}}\n    <title>222</title>\n{{ end }}\n\n{{ define \"content\" }}\n<h1>List Content</h1>\n{{ end }}"
  },
  {
    "path": "_examples/view/template_blocks_2/src/public/html/layouts/main.html",
    "content": "<html>\n<head>\n    {{ block \"title\" .}}<title>Main Default Title</title>{{end}}\n</head>\n<body>\n    {{ block \"content\" .}}<h1>Main Default Content</h1>{{end}}\n</body>\n</html>"
  },
  {
    "path": "_examples/view/template_blocks_2/src/public/html/layouts/secondary.html",
    "content": "<html>\n<head>\n    {{ block \"title\" .}}<title>Secondary Default Title</title>{{end}}\n</head>\n<body>\n    <h1>Secondary Layout</h1>\n    {{ block \"content\" .}}<h1>Secondary Default Content</h1>{{end}}\n</body>\n</html>"
  },
  {
    "path": "_examples/view/template_blocks_2/src/public/html/menu/menu.html",
    "content": "{{ define \"title\" }} <title>111</title>{{ end }}\n\n{{ define \"content\" }}<h1>Menu Content</h1>{{ end }}"
  },
  {
    "path": "_examples/view/template_django_0/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n\t// optionally, register filters like `timesince`.\n\t_ \"github.com/iris-contrib/pongo2-addons/v4\"\n)\n\nvar startTime = time.Now()\n\nfunc main() {\n\tapp := iris.New()\n\n\ttmpl := iris.Django(\"./templates\", \".html\")\n\ttmpl.Reload(true)                             // reload templates on each request (development mode)\n\ttmpl.AddFunc(\"greet\", func(s string) string { // {{greet(name)}}\n\t\treturn \"Greetings \" + s + \"!\"\n\t})\n\n\t// tmpl.RegisterFilter(\"myFilter\", myFilter) // {{\"simple input for filter\"|myFilter}}\n\tapp.RegisterView(tmpl)\n\n\tapp.Get(\"/\", hi)\n\n\t// http://localhost:8080\n\tapp.Listen(\":8080\")\n}\n\nfunc hi(ctx iris.Context) {\n\t// ctx.ViewData(\"title\", \"Hi Page\")\n\t// ctx.ViewData(\"name\", \"iris\")\n\t// ctx.ViewData(\"serverStartTime\", startTime)\n\t// or if you set all view data in the same handler you can use the\n\t// iris.Map/pongo2.Context/map[string]any, look below:\n\n\tif err := ctx.View(\"hi.html\", iris.Map{\n\t\t\"title\":           \"Hi Page\",\n\t\t\"name\":            \"iris\",\n\t\t\"serverStartTime\": startTime,\n\t}); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "_examples/view/template_django_0/templates/hi.html",
    "content": "<html>\n<head>\n<title>{{title}}</title>\n</head>\n<body>\n\t<h1>Hi {{name|capfirst}} </h1>\n\t\n\t<h2>{{greet(name)}}</h2>\n\n\t<h3>Server started about {{serverStartTime|timesince}}. Refresh the page to see different result</h3>\n</body>\n</html>\n"
  },
  {
    "path": "_examples/view/template_django_1/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.RegisterView(iris.Django(\"./views\", \".html\").Reload(true))\n\n\tmypathRoute := app.Get(\"/mypath\", writePathHandler)\n\tmypathRoute.Name = \"my-page1\"\n\n\tmypath2Route := app.Get(\"/mypath2/{paramfirst}/{paramsecond}\", writePathHandler)\n\tmypath2Route.Name = \"my-page2\"\n\n\tmypath3Route := app.Get(\"/mypath3/{paramfirst}/statichere/{paramsecond}\", writePathHandler)\n\tmypath3Route.Name = \"my-page3\"\n\n\tmypath4Route := app.Get(\"/mypath4/{paramfirst}/statichere/{paramsecond}/{otherparam}/{something:path}\", writePathHandler)\n\t// same as: app.Get(\"/mypath4/:paramfirst/statichere/:paramsecond/:otherparam/*something\", writePathHandler)\n\tmypath4Route.Name = \"my-page4\"\n\n\t// same with Handle/Func\n\tmypath5Route := app.Handle(\"GET\", \"/mypath5/{paramfirst}/statichere/{paramsecond}/{otherparam}/anything/{something:path}\", writePathHandler)\n\tmypath5Route.Name = \"my-page5\"\n\n\tmypath6Route := app.Get(\"/mypath6/{paramfirst}/{paramsecond}/statichere/{paramThirdAfterStatic}\", writePathHandler)\n\tmypath6Route.Name = \"my-page6\"\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\t// for /mypath6...\n\t\tparamsAsArray := []string{\"theParam1\", \"theParam2\", \"paramThirdAfterStatic\"}\n\t\tctx.ViewData(\"ParamsAsArray\", paramsAsArray)\n\t\tif err := ctx.View(\"page.html\"); err != nil {\n\t\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\t\treturn\n\t\t}\n\t})\n\n\tapp.Get(\"/redirect/{namedRoute}\", func(ctx iris.Context) {\n\t\trouteName := ctx.Params().Get(\"namedRoute\")\n\t\tr := app.GetRoute(routeName)\n\t\tif r == nil {\n\t\t\tctx.StatusCode(404)\n\t\t\tctx.Writef(\"Route with name %s not found\", routeName)\n\t\t\treturn\n\t\t}\n\n\t\tprintln(\"The path of \" + routeName + \"is: \" + r.Path)\n\t\t// if routeName == \"my-page1\"\n\t\t// prints: The path of of my-page1 is: /mypath\n\t\t// if it's a path which takes named parameters\n\t\t// then use \"r.ResolvePath(paramValuesHere)\"\n\t\tctx.Redirect(r.Path)\n\t\t// http://localhost:8080/redirect/my-page1 will redirect to -> http://localhost:8080/mypath\n\t})\n\n\t// http://localhost:8080\n\t// http://localhost:8080/redirect/my-page1\n\tapp.Listen(\":8080\")\n}\n\nfunc writePathHandler(ctx iris.Context) {\n\tctx.Writef(\"Hello from %s.\", ctx.Path())\n}\n"
  },
  {
    "path": "_examples/view/template_django_1/views/page.html",
    "content": "<html>\n\n<head>\n  <title>urlpath function - django</title>\n  <style>\n    a {\n      color: #0f7afc;\n      border-bottom-color: rgba(15, 122, 252, 0.2);\n      text-decoration: none\n    }\n\n    a:hover {\n      color: #cf0000;\n      border-bottom-color: rgba(208, 64, 0, 0.2);\n      text-decoration: none\n    }\n\n    a:visited {\n      color: #800080;\n      border-bottom-color: rgba(128, 0, 128, 0.2);\n      text-decoration: none\n    }\n  </style>\n</head>\n\n<body>\n\n  <a href=\"{{urlpath('my-page1')}}\">/mypath</a>\n  <br />\n  <br />\n\n  <a href=\"{{urlpath('my-page2','theParam1','theParam2')}}\">/mypath2/{paramfirst}/{paramsecond}</a>\n  <br />\n  <br />\n\n  <a href=\"{{urlpath('my-page3','theParam1','theParam2AfterStatic')}}\">/mypath3/{paramfirst}/statichere/{paramsecond}</a>\n  <br />\n  <br />\n\n  <a href=\"{{urlpath('my-page4','theParam1','theparam2AfterStatic','otherParam','matchAnything')}}\">\n    /mypath4/{paramfirst}/statichere/{paramsecond}/{otherparam}/{something:path}</a>\n  <br />\n  <br />\n\n  <a href=\"{{urlpath('my-page5','theParam1','theParam2Afterstatichere','otherParam','matchAnythingAfterStatic')}}\">\n    /mypath5/{paramfirst}/statichere/{paramsecond}/{otherparam}/anything/{anything:path}</a>\n  <br />\n  <br />\n\n  <a href=\"{{urlpath('my-page6', ParamsAsArray)}}\">\n    /mypath6/{paramfirst}/{paramsecond}/statichere/{paramThirdAfterStatic}\n  </a>\n</body>\n\n</html>"
  },
  {
    "path": "_examples/view/template_handlebars_0/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/mvc\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.Logger().SetLevel(\"debug\")\n\n\t// Init the handlebars engine\n\te := iris.Handlebars(\"./templates\", \".html\").Reload(true)\n\t// Register a helper.\n\te.AddFunc(\"fullName\", func(person map[string]string) string {\n\t\treturn person[\"firstName\"] + \" \" + person[\"lastName\"]\n\t})\n\n\tapp.RegisterView(e)\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tviewData := iris.Map{\n\t\t\t\"author\": map[string]string{\"firstName\": \"Jean\", \"lastName\": \"Valjean\"},\n\t\t\t\"body\":   \"Life is difficult\",\n\t\t\t\"comments\": []iris.Map{{\n\t\t\t\t\"author\": map[string]string{\"firstName\": \"Marcel\", \"lastName\": \"Beliveau\"},\n\t\t\t\t\"body\":   \"LOL!\",\n\t\t\t}},\n\t\t}\n\n\t\tif err := ctx.View(\"example.html\", viewData); err != nil {\n\t\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\t\treturn\n\t\t}\n\t})\n\n\texampleRouter := app.Party(\"/example\")\n\t/* See context-view-data example: Set data through one or more middleware */\n\texampleRouter.Use(func(ctx iris.Context) {\n\t\tctx.ViewData(\"author\", map[string]string{\"firstName\": \"Jean\", \"lastName\": \"Valjean\"})\n\t\tctx.ViewData(\"body\", \"Life is difficult\")\n\t\tctx.ViewData(\"comments\", []iris.Map{{\n\t\t\t\"author\": map[string]string{\"firstName\": \"Marcel\", \"lastName\": \"Beliveau\"},\n\t\t\t\"body\":   \"LOL!\",\n\t\t}})\n\n\t\t// OR:\n\t\t// ctx.ViewData(\"\", iris.Map{\n\t\t// \t\"author\": map[string]string{\"firstName\": \"Jean\", \"lastName\": \"Valjean\"},\n\t\t// \t\"body\":   \"Life is difficult\",\n\t\t// \t\"comments\": []iris.Map{{\n\t\t// \t\t\"author\": map[string]string{\"firstName\": \"Marcel\", \"lastName\": \"Beliveau\"},\n\t\t// \t\t\"body\":   \"LOL!\",\n\t\t// \t}},\n\t\t// })\n\n\t\tctx.Next()\n\t})\n\n\tmvc.New(exampleRouter).Handle(new(controller))\n\n\t// Read more about its syntax at:\n\t// https://github.com/mailgun/raymond and\n\t// https://handlebarsjs.com/guide\n\n\t// http://localhost:8080\n\t// http://localhost:8080/example\n\tapp.Listen(\":8080\")\n}\n\ntype controller struct{}\n\nfunc (c *controller) Get() mvc.Result {\n\treturn mvc.View{\n\t\tName: \"example\",\n\t\tCode: 200,\n\t}\n}\n"
  },
  {
    "path": "_examples/view/template_handlebars_0/templates/example.html",
    "content": "<div class=\"post\">\n    <h1>By {{fullName author}}</h1>\n    <div class=\"body\">{{body}}</div>\n\n    <h1>Comments</h1>\n\n    {{#each comments}}\n    <h2>By {{fullName author}}</h2>\n    <div class=\"body\">{{body}}</div>\n    {{/each}}\n\n\n    <!-- <h1>Struct example</h1>\n    {{#with myStruct}}\n    {{ this }}\n    OR\n    {{ FieldMame }}\n    {{/with}} -->\n\n    <hr />\n\n    <strong>Read more at: <a href=\"https://github.com/mailgun/raymond\" target=\"_new\">\n            https://github.com/mailgun/raymond</a> and <a href=\"https://handlebarsjs.com/guide\" target=\"_new\">\n            https://handlebarsjs.com/guide\n        </a>\n    </strong>\n</div>"
  },
  {
    "path": "_examples/view/template_html_0/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New() // defaults to these\n\n\ttmpl := iris.HTML(\"./templates\", \".html\")\n\ttmpl.Reload(true) // reload templates on each request (development mode)\n\t// default template funcs are:\n\t//\n\t// - {{ urlpath \"mynamedroute\" \"pathParameter_ifneeded\" }}\n\t// - {{ render \"header.html\" . }}\n\t// - {{ render_r \"header.html\" . }} // partial relative path to current page\n\t// - {{ yield . }}\n\t// - {{ current . }}\n\ttmpl.AddFunc(\"greet\", func(s string) string {\n\t\treturn \"Greetings \" + s + \"!\"\n\t})\n\tapp.RegisterView(tmpl)\n\n\tapp.Get(\"/\", hi)\n\n\t// http://localhost:8080\n\tapp.Listen(\":8080\", iris.WithCharset(\"utf-8\")) // defaults to that but you can change it.\n}\n\nfunc hi(ctx iris.Context) {\n\tctx.ViewData(\"Title\", \"Hi Page\")\n\tctx.ViewData(\"Name\", \"iris\") // {{.Name}} will render: iris\n\t// ctx.ViewData(\"\", myCcustomStruct{})\n\tif err := ctx.View(\"hi.html\"); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "_examples/view/template_html_0/templates/hi.html",
    "content": "<html>\r\n<head>\r\n<title>{{.Title}}</title>\r\n</head>\r\n<body>\r\n\t<h1>Hi {{.Name}} </h1>\r\n</body>\r\n</html>\r\n"
  },
  {
    "path": "_examples/view/template_html_1/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\ntype mypage struct {\n\tTitle   string\n\tMessage string\n}\n\nfunc main() {\n\tapp := iris.New()\n\n\tapp.RegisterView(iris.HTML(\"./templates\", \".html\").Layout(\"layout.html\"))\n\t// TIP: append .Reload(true) to reload the templates on each request.\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.CompressWriter(true)\n\t\tctx.ViewData(\"\", mypage{\"My Page title\", \"Hello world!\"})\n\t\tif err := ctx.View(\"mypage.html\"); err != nil {\n\t\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\t\treturn\n\t\t}\n\t\t// Note that: you can pass \"layout\" : \"otherLayout.html\" to bypass the config's Layout property\n\t\t// or view.NoLayout to disable layout on this render action.\n\t\t// third is an optional parameter\n\t})\n\n\t// http://localhost:8080\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/view/template_html_1/templates/layout.html",
    "content": "<html>\r\n<head>\r\n<title>My Layout</title>\r\n\r\n</head>\r\n<body>\r\n\t<h1>[layout] Body content is below...</h1>\r\n\t<!-- Render the current template here -->\r\n\t{{ yield . }}\r\n</body>\r\n</html>\r\n"
  },
  {
    "path": "_examples/view/template_html_1/templates/mypage.html",
    "content": "<h1>\r\n\tTitle: {{.Title}}\r\n</h1>\r\n<h3>Message: {{.Message}} </h3>"
  },
  {
    "path": "_examples/view/template_html_2/README.md",
    "content": "## Info\r\n\r\nThis folder examines the {{render \"dir/templatefilename\" .}} functionality to manually render any template inside any template\r\n"
  },
  {
    "path": "_examples/view/template_html_2/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\ttmpl := iris.HTML(\"./templates\", \".html\")\n\ttmpl.Layout(\"layouts/layout.html\")\n\ttmpl.AddFunc(\"greet\", func(s string) string {\n\t\treturn \"Greetings \" + s + \"!\"\n\t})\n\n\tapp.RegisterView(tmpl)\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tif err := ctx.View(\"page1.html\"); err != nil {\n\t\t\tctx.StatusCode(iris.StatusInternalServerError)\n\t\t\tctx.WriteString(err.Error())\n\t\t}\n\t})\n\n\t// remove the layout for a specific route\n\tapp.Get(\"/nolayout\", func(ctx iris.Context) {\n\t\tctx.ViewLayout(iris.NoLayout)\n\t\tif err := ctx.View(\"page1.html\"); err != nil {\n\t\t\tctx.StatusCode(iris.StatusInternalServerError)\n\t\t\tctx.WriteString(err.Error())\n\t\t}\n\t})\n\n\t// set a layout for a party, .Layout should be BEFORE any Get or other Handle party's method\n\tmy := app.Party(\"/my\").Layout(\"layouts/mylayout.html\")\n\t{ // both of these will use the layouts/mylayout.html as their layout.\n\t\tmy.Get(\"/\", func(ctx iris.Context) {\n\t\t\tif err := ctx.View(\"page1.html\"); err != nil {\n\t\t\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\t\t\treturn\n\t\t\t}\n\t\t})\n\t\tmy.Get(\"/other\", func(ctx iris.Context) {\n\t\t\tif err := ctx.View(\"page1.html\"); err != nil {\n\t\t\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\t\t\treturn\n\t\t\t}\n\t\t})\n\t}\n\n\t// http://localhost:8080\n\t// http://localhost:8080/nolayout\n\t// http://localhost:8080/my\n\t// http://localhost:8080/my/other\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/view/template_html_2/templates/layouts/layout.html",
    "content": "<html>\r\n<head>\r\n<title>Layout</title>\r\n\r\n</head>\r\n<body>\r\n\t<h1>This is the global layout</h1>\r\n\t<br />\r\n\t<!-- Render the current template here -->\r\n\t{{ yield . }}\r\n</body>\r\n</html>\r\n"
  },
  {
    "path": "_examples/view/template_html_2/templates/layouts/mylayout.html",
    "content": "<html>\r\n<head>\r\n<title>my Layout</title>\r\n\r\n</head>\r\n<body>\r\n\t<h1>This is the layout for the /my/ and /my/other routes only</h1>\r\n\t<br />\r\n\t<!-- Render the current template here -->\r\n\t{{ yield . }}\r\n</body>\r\n</html>\r\n"
  },
  {
    "path": "_examples/view/template_html_2/templates/page1.html",
    "content": "<div style=\"background-color: black; color: blue\">\r\n\r\n\t<h1>Page 1 {{ greet \"iris developer\"}}</h1>\r\n\r\n\t{{ render \"partials/page1_partial1.html\" . }}\r\n\r\n</div>\r\n"
  },
  {
    "path": "_examples/view/template_html_2/templates/partials/page1_partial1.html",
    "content": "<div style=\"background-color: white; color: red\">\r\n\t<h1>Page 1's Partial 1</h1>\r\n</div>\r\n"
  },
  {
    "path": "_examples/view/template_html_3/main.go",
    "content": "// Package main an example on how to naming your routes & use the custom\n// 'url path' HTML Template Engine, same for other template engines.\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.RegisterView(iris.HTML(\"./templates\", \".html\").Reload(true))\n\n\tmypathRoute := app.Get(\"/mypath\", writePathHandler)\n\tmypathRoute.Name = \"my-page1\"\n\n\tmypath2Route := app.Get(\"/mypath2/{paramfirst}/{paramsecond}\", writePathHandler)\n\tmypath2Route.Name = \"my-page2\"\n\n\tmypath3Route := app.Get(\"/mypath3/{paramfirst}/statichere/{paramsecond}\", writePathHandler)\n\tmypath3Route.Name = \"my-page3\"\n\n\tmypath4Route := app.Get(\"/mypath4/{paramfirst}/statichere/{paramsecond}/{otherparam}/{something:path}\", writePathHandler)\n\t// same as: app.Get(\"/mypath4/:paramfirst/statichere/:paramsecond/:otherparam/*something\", writePathHandler)\n\tmypath4Route.Name = \"my-page4\"\n\n\t// same with Handle/Func\n\tmypath5Route := app.Handle(\"GET\", \"/mypath5/{paramfirst}/statichere/{paramsecond}/{otherparam}/anything/{something:path}\", writePathHandler)\n\tmypath5Route.Name = \"my-page5\"\n\n\tmypath6Route := app.Get(\"/mypath6/{paramfirst}/{paramsecond}/statichere/{paramThirdAfterStatic}\", writePathHandler)\n\tmypath6Route.Name = \"my-page6\"\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\t// for /mypath6...\n\t\tparamsAsArray := []string{\"theParam1\", \"theParam2\", \"paramThirdAfterStatic\"}\n\t\tctx.ViewData(\"ParamsAsArray\", paramsAsArray)\n\t\tif err := ctx.View(\"page.html\"); err != nil {\n\t\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\t\treturn\n\t\t}\n\t})\n\n\tapp.Get(\"/redirect/{namedRoute}\", func(ctx iris.Context) {\n\t\trouteName := ctx.Params().Get(\"namedRoute\")\n\t\tr := app.GetRoute(routeName)\n\t\tif r == nil {\n\t\t\tctx.StatusCode(404)\n\t\t\tctx.Writef(\"Route with name %s not found\", routeName)\n\t\t\treturn\n\t\t}\n\n\t\tprintln(\"The path of \" + routeName + \"is: \" + r.Path)\n\t\t// if routeName == \"my-page1\"\n\t\t// prints: The path of of my-page1 is: /mypath\n\t\t// if it's a path which takes named parameters\n\t\t// then use \"r.ResolvePath(paramValuesHere)\"\n\t\tctx.Redirect(r.Path)\n\t\t// http://localhost:8080/redirect/my-page1 will redirect to -> http://localhost:8080/mypath\n\t})\n\n\t// http://localhost:8080\n\t// http://localhost:8080/redirect/my-page1\n\tapp.Listen(\":8080\")\n}\n\nfunc writePathHandler(ctx iris.Context) {\n\tctx.Writef(\"Hello from %s.\", ctx.Path())\n}\n"
  },
  {
    "path": "_examples/view/template_html_3/templates/page.html",
    "content": "<html>\r\n\r\n<head>\r\n  <title>template_html_3</title>\r\n  <style>\r\n    a {\r\n      color: #0f7afc;\r\n      border-bottom-color: rgba(15, 122, 252, 0.2);\r\n      text-decoration: none\r\n    }\r\n\r\n    a:hover {\r\n      color: #cf0000;\r\n      border-bottom-color: rgba(208, 64, 0, 0.2);\r\n      text-decoration: none\r\n    }\r\n\r\n    a:visited {\r\n      color: #800080;\r\n      border-bottom-color: rgba(128, 0, 128, 0.2);\r\n      text-decoration: none\r\n    }\r\n  </style>\r\n</head>\r\n\r\n<body>\r\n\r\n  <a href=\"{{urlpath \"my-page1\"}}\">/mypath</a>\r\n  <br />\r\n  <br />\r\n\r\n  <a href=\"{{urlpath \"my-page2\" \"theParam1\" \"theParam2\"}}\">/mypath2/{paramfirst}/{paramsecond}</a>\r\n  <br />\r\n  <br />\r\n\r\n  <a href=\"{{urlpath \"my-page3\" \"theParam1\" \"theParam2AfterStatic\"}}\">/mypath3/{paramfirst}/statichere/{paramsecond}</a>\r\n  <br />\r\n  <br />\r\n\r\n  <a href=\"{{urlpath \"my-page4\" \"theParam1\" \"theparam2AfterStatic\"  \"otherParam\"  \"matchAnything\"}}\">\r\n    /mypath4/{paramfirst}/statichere/{paramsecond}/{otherparam}/{something:path}</a>\r\n  <br />\r\n  <br />\r\n\r\n  <a href=\"{{urlpath \"my-page5\" \"theParam1\" \"theParam2Afterstatichere\" \"otherParam\"  \"matchAnythingAfterStatic\"}}\">\r\n    /mypath5/{paramfirst}/statichere/{paramsecond}/{otherparam}/anything/{anything:path}</a>\r\n  <br />\r\n  <br />\r\n\r\n  <a href={{urlpath \"my-page6\" .ParamsAsArray }}>\r\n    /mypath6/{paramfirst}/{paramsecond}/statichere/{paramThirdAfterStatic}\r\n  </a>\r\n</body>\r\n\r\n</html>"
  },
  {
    "path": "_examples/view/template_html_4/hosts",
    "content": "# Copyright (c) 1993-2009 Microsoft Corp.\r\n#\r\n# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.\r\n#\r\n# This file contains the mappings of IP addresses to host names. Each\r\n# entry should be kept on an individual line. The IP address should\r\n# be placed in the first column followed by the corresponding host name.\r\n# The IP address and the host name should be separated by at least one\r\n# space.\r\n#\r\n# Additionally, comments (such as these) may be inserted on individual\r\n# lines or following the machine name denoted by a '#' symbol.\r\n#\r\n# For example:\r\n#\r\n#      102.54.94.97     rhino.acme.com          # source server\r\n#       38.25.63.10     x.acme.com              # x client host\r\n\r\n# localhost name resolution is handled within DNS itself.\r\n127.0.0.1       localhost\r\n::1             localhost\r\n#-iris-For development machine, you have to configure your dns also for online, search google how to do it if you don't know\r\n\r\n127.0.0.1\t\tusername1.127.0.0.1\r\n127.0.0.1\t\tusername2.127.0.0.1\r\n127.0.0.1\t\tusername3.127.0.0.1\r\n127.0.0.1\t\tusername4.127.0.0.1\r\n127.0.0.1\t\tusername5.127.0.0.1\r\n# note that you can always use custom subdomains\r\n#-END iris-\r\n\r\n# Windows: Drive:/Windows/system32/drivers/etc/hosts, on Linux: /etc/hosts"
  },
  {
    "path": "_examples/view/template_html_4/main.go",
    "content": "// Package main an example on how to naming your routes & use the custom 'url' HTML Template Engine, same for other template engines.\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/core/router\"\n)\n\nconst (\n\thost = \"127.0.0.1:8080\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\t// create a custom path reverser, iris let you define your own host and scheme\n\t// which is useful when you have nginx or caddy in front of iris.\n\trv := router.NewRoutePathReverser(app, router.WithHost(host), router.WithScheme(\"http\"))\n\t// locate and define our templates as usual.\n\ttemplates := iris.HTML(\"./templates\", \".html\")\n\t// add a custom func of \"url\" and pass the rv.URL as its template function body,\n\t// so {{url \"routename\" \"paramsOrSubdomainAsFirstArgument\"}} will work inside our templates.\n\ttemplates.AddFunc(\"url\", rv.URL)\n\n\tapp.RegisterView(templates)\n\n\t// wildcard subdomain, will catch username1.... username2.... username3... username4.... username5...\n\t// that our below links are providing via page.html's first argument which is the subdomain.\n\n\tsubdomain := app.WildcardSubdomain()\n\n\tmypathRoute := subdomain.Get(\"/mypath\", emptyHandler)\n\tmypathRoute.Name = \"my-page1\"\n\n\tmypath2Route := subdomain.Get(\"/mypath2/{paramfirst}/{paramsecond}\", emptyHandler)\n\tmypath2Route.Name = \"my-page2\"\n\n\tmypath3Route := subdomain.Get(\"/mypath3/{paramfirst}/statichere/{paramsecond}\", emptyHandler)\n\tmypath3Route.Name = \"my-page3\"\n\n\tmypath4Route := subdomain.Get(\"/mypath4/{paramfirst}/statichere/{paramsecond}/{otherparam}/{something:path}\", emptyHandler)\n\tmypath4Route.Name = \"my-page4\"\n\n\tmypath5Route := subdomain.Handle(\"GET\", \"/mypath5/{paramfirst}/statichere/{paramsecond}/{otherparam}/anything/{something:path}\", emptyHandler)\n\tmypath5Route.Name = \"my-page5\"\n\n\tmypath6Route := subdomain.Get(\"/mypath6/{paramfirst}/{paramsecond}/staticParam/{paramThirdAfterStatic}\", emptyHandler)\n\tmypath6Route.Name = \"my-page6\"\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\t// for username5./mypath6...\n\t\tparamsAsArray := []string{\"username5\", \"theParam1\", \"theParam2\", \"paramThirdAfterStatic\"}\n\t\tctx.ViewData(\"ParamsAsArray\", paramsAsArray)\n\t\tif err := ctx.View(\"page.html\"); err != nil {\n\t\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\t\treturn\n\t\t}\n\t})\n\n\t// simple path so you can test it without host mapping and subdomains,\n\t// at view it make uses of {{urlpath ...}}\n\t// in order to showcase you that you can use it\n\t// if you don't want the entire scheme and host to be part of the url.\n\tapp.Get(\"/mypath7/{paramfirst}/{paramsecond}/static/{paramthird}\", emptyHandler).Name = \"my-page7\"\n\n\t// http://127.0.0.1:8080\n\tapp.Listen(host)\n}\n\nfunc emptyHandler(ctx iris.Context) {\n\tctx.Writef(\"Hello from subdomain: %s , you're in path:  %s\", ctx.Subdomain(), ctx.Path())\n}\n\n// Note:\n// If you got an empty string on {{ url }} or {{ urlpath }} it means that\n// args length are not aligned with the route's parameters length\n// or the route didn't found by the passed name.\n"
  },
  {
    "path": "_examples/view/template_html_4/templates/page.html",
    "content": "<!-- the only difference between normal named routes and dynamic subdomains named routes is that the first argument of  url\r\nis the subdomain part instead of named parameter-->\r\n\r\n<a href=\"{{url \"my-page1\" \"username1\"}}\">username1.127.0.0.1:8080/mypath</a>\r\n<br />\r\n<br />\r\n\r\n<a href=\"{{url  \"my-page2\" \"username2\" \"theParam1\" \"theParam2\"}}\">\r\n    username2.127.0.0.1:8080/mypath2/{paramfirst}/{paramsecond}\r\n</a>\r\n<br />\r\n<br />\r\n\r\n<a href=\"{{url \"my-page3\" \"username3\" \"theParam1\" \"theParam2AfterStatic\"}}\">\r\n    username3.127.0.0.1:8080/mypath3/{paramfirst}/statichere/{paramsecond}\r\n</a>\r\n<br />\r\n<br />\r\n\r\n<a href=\"{{url \"my-page4\" \"username4\" \"theParam1\" \"theparam2AfterStatic\" \"otherParam\" \"matchAnything\"}}\">\r\n    username4.127.0.0.1:8080/mypath4/{paramfirst}/statichere/{paramsecond}/{otherParam}/{something:path}\r\n</a>\r\n<br />\r\n<br />\r\n\r\n<a href=\"{{url \"my-page5\" \"username5\" \"theParam1\" \"theparam2AfterStatic\" \"otherParam\" \"matchAnything\"}}\">\r\n    username5.127.0.0.1:8080/mypath5/{paramfirst}/statichere/{paramsecond}/{otherparam}/anything/{something:path}\r\n</a>\r\n<br/>\r\n<br/>\r\n\r\n<a href=\"{{url \"my-page6\" .ParamsAsArray }}\">\r\n    username5.127.0.0.1:8080/mypath6/{paramfirst}/{paramsecond}/staticParam/{paramThirdAfterStatic}\r\n</a>\r\n<br/>\r\n<br/>\r\n\r\n<a href=\"{{urlpath \"my-page7\" \"theParam1\" \"theParam2\" \"theParam3\" }}\">\r\n    mypath7/{paramfirst}/{paramsecond}/static/{paramthird}\r\n</a>\r\n<br/>\r\n<br/>\r\n\r\n"
  },
  {
    "path": "_examples/view/template_html_5/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\tapp.RegisterView(iris.HTML(\"./views\", \".html\").Layout(\"layout.html\"))\n\t// TIP: append .Reload(true) to reload the templates on each request.\n\n\tapp.Get(\"/home\", func(ctx iris.Context) {\n\t\tctx.ViewData(\"title\", \"Home page\")\n\t\tif err := ctx.View(\"home.html\"); err != nil {\n\t\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\t\treturn\n\t\t}\n\n\t\t// Note that: you can pass \"layout\" : \"otherLayout.html\" to bypass the config's Layout property\n\t\t// or view.NoLayout to disable layout on this render action.\n\t\t// third is an optional parameter\n\t})\n\n\tapp.Get(\"/about\", func(ctx iris.Context) {\n\t\tif err := ctx.View(\"about.html\"); err != nil {\n\t\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\t\treturn\n\t\t}\n\t})\n\n\tapp.Get(\"/user/index\", func(ctx iris.Context) {\n\t\tif err := ctx.View(\"user/index.html\"); err != nil {\n\t\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\t\treturn\n\t\t}\n\t})\n\n\t// http://localhost:8080\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/view/template_html_5/views/about.html",
    "content": "{{ define \"about-head\"}}\n    <title>about page</title>\n    <style type=\"text/css\">\n        body {\n            background: #666;\n        }\n    </style>\n{{ end }}\n\n{{ define \"about-body\"}}\n    extend body content in layout.\n{{ end }}\n<div>\n    Hello about page\n</div>"
  },
  {
    "path": "_examples/view/template_html_5/views/home.html",
    "content": "{{ define \"home-head\"}}\n\t<title>{{.title}}</title>\n\t<style type=\"text/css\">\n\t\tbody {\n\t\t\tbackground: #999;\n\t\t}\n\t</style>\n{{ end }}\n<div>\n\tHello home page\n</div>"
  },
  {
    "path": "_examples/view/template_html_5/views/layout.html",
    "content": "<html>\n<head>\n{{ part \"head\"}}\n</head>\n<body>\n\t<h1>[layout] Body content is below...</h1>\n\t{{ part \"body\" . }}\n\t<!-- Render the current template here -->\n\t{{ yield . }}\n</body>\n</html>\n"
  },
  {
    "path": "_examples/view/template_html_5/views/user/index.html",
    "content": "{{ define \"user/index-head\"}}\n<style type=\"text/css\">\n    body {\n        background: red;\n    }\n</style>\n{{ end }}\n<div>\n    Hello user index page\n</div>"
  },
  {
    "path": "_examples/view/template_jet_0/README.md",
    "content": "# Jet Engine Example\n\nThis example is a fork of <https://github.com/CloudyKit/jet/tree/master/examples/todos> to work with Iris, so you can notice the differences side by side.\n\nRead more at: https://github.com/CloudyKit/jet/blob/master/docs/syntax.md\n\n> The Iris Jet View Engine fixes some bugs that the underline [jet template parser](https://github.com/CloudyKit/jet) currently has.\n\n\nContinue by learning how you can [serve embedded templates](../template_jet_1_embedded)."
  },
  {
    "path": "_examples/view/template_jet_0/main.go",
    "content": "// Package main shows how to use jet template parser with ease using the Iris built-in Jet view engine.\n// This example is customized fork of https://github.com/CloudyKit/jet/tree/master/examples/todos, so you can\n// notice the differences side by side.\npackage main\n\nimport (\n\t\"bytes\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"os\"\n\t\"reflect\"\n\t\"strings\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/view\"\n)\n\ntype tTODO struct {\n\tText string\n\tDone bool\n}\n\ntype doneTODOs struct {\n\tlist map[string]*tTODO\n\tkeys []string\n\tlen  int\n\ti    int\n}\n\nfunc (dt *doneTODOs) New(todos map[string]*tTODO) *doneTODOs {\n\tdt.len = len(todos)\n\tfor k := range todos {\n\t\tdt.keys = append(dt.keys, k)\n\t}\n\tdt.list = todos\n\treturn dt\n}\n\n// Range satisfies the jet.Ranger interface and only returns TODOs that are done,\n// even when the list contains TODOs that are not done.\nfunc (dt *doneTODOs) Range() (reflect.Value, reflect.Value, bool) {\n\tfor dt.i < dt.len {\n\t\tkey := dt.keys[dt.i]\n\t\tdt.i++\n\t\tif dt.list[key].Done {\n\t\t\treturn reflect.ValueOf(key), reflect.ValueOf(dt.list[key]), false\n\t\t}\n\t}\n\treturn reflect.Value{}, reflect.Value{}, true\n}\n\n// Note: jet version 4 requires this.\nfunc (dt *doneTODOs) ProvidesIndex() bool { return true }\n\nfunc (dt *doneTODOs) Render(r *view.JetRuntime) {\n\tr.Write([]byte(\"custom renderer\"))\n}\n\n// Render implements jet.Renderer interface\nfunc (t *tTODO) Render(r *view.JetRuntime) {\n\tdone := \"yes\"\n\tif !t.Done {\n\t\tdone = \"no\"\n\t}\n\tr.Write([]byte(fmt.Sprintf(\"TODO: %s (done: %s)\", t.Text, done)))\n}\n\nfunc main() {\n\t//\n\t// Type aliases:\n\t// view.JetRuntimeVars = jet.VarMap\n\t// view.JetRuntime = jet.Runtime\n\t// view.JetArguments = jet.Arguments\n\t//\n\t// Iris also gives you the ability to put runtime variables\n\t// from middlewares as well, by:\n\t// view.AddJetRuntimeVars(ctx, vars)\n\t// or tmpl.AddRuntimeVars(ctx, vars)\n\tapp := iris.New()\n\ttmpl := iris.Jet(\"./views\", \".jet\") // <--\n\ttmpl.Reload(true)                   // remove in production.\n\ttmpl.AddFunc(\"base64\", func(a view.JetArguments) reflect.Value {\n\t\ta.RequireNumOfArguments(\"base64\", 1, 1)\n\n\t\tbuffer := bytes.NewBuffer(nil)\n\t\tfmt.Fprint(buffer, a.Get(0))\n\n\t\treturn reflect.ValueOf(base64.URLEncoding.EncodeToString(buffer.Bytes()))\n\t})\n\tapp.RegisterView(tmpl) // <--\n\n\ttodos := map[string]*tTODO{\n\t\t\"example-todo-1\": {Text: \"Add an show todo page to the example project\", Done: true},\n\t\t\"example-todo-2\": {Text: \"Add an add todo page to the example project\"},\n\t\t\"example-todo-3\": {Text: \"Add an update todo page to the example project\"},\n\t\t\"example-todo-4\": {Text: \"Add an delete todo page to the example project\", Done: true},\n\t}\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\terr := ctx.View(\"todos/index.jet\", todos) // <--\n\t\t// Note that the `ctx.View` already logs the error if logger level is allowing it and returns the error.\n\t\tif err != nil {\n\t\t\tctx.StopWithText(iris.StatusInternalServerError, \"Templates not rendered!\")\n\t\t}\n\t})\n\n\tapp.Get(\"/todo\", func(ctx iris.Context) {\n\t\tid := ctx.URLParam(\"id\")\n\t\ttodo, ok := todos[id]\n\t\tif !ok {\n\t\t\tctx.Redirect(\"/\")\n\t\t\treturn\n\t\t}\n\n\t\tctx.ViewData(\"title\", \"Show TODO\")\n\t\tif err := ctx.View(\"todos/show.jet\", todo); err != nil {\n\t\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\t\treturn\n\t\t}\n\t})\n\tapp.Get(\"/all-done\", func(ctx iris.Context) {\n\t\t// vars := make(view.JetRuntimeVars)\n\t\t// vars.Set(\"showingAllDone\", true)\n\t\t// vars.Set(\"title\", \"Todos - All Done\")\n\t\t// view.AddJetRuntimeVars(ctx, vars)\n\t\t// ctx.View(\"todos/index.jet\", (&doneTODOs{}).New(todos))\n\t\t//\n\t\t// OR\n\t\tctx.ViewData(\"showingAllDone\", true)\n\t\tctx.ViewData(\"title\", \"Todos - All Done\")\n\n\t\t// Use ctx.ViewData(\"_jet\", jetData)\n\t\t// if using as middleware and you want\n\t\t// to pre-set the value or even change it later on from another next middleware.\n\t\t// ctx.ViewData(\"_jet\", (&doneTODOs{}).New(todos))\n\t\t// and ctx.View(\"todos/index.jet\")\n\t\t// OR\n\t\tif err := ctx.View(\"todos/index.jet\", (&doneTODOs{}).New(todos)); err != nil {\n\t\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\t\treturn\n\t\t}\n\t})\n\n\tport := os.Getenv(\"PORT\")\n\tif len(port) == 0 {\n\t\tport = \":8080\"\n\t} else if !strings.HasPrefix(\":\", port) {\n\t\tport = \":\" + port\n\t}\n\n\tapp.Listen(port)\n}\n"
  },
  {
    "path": "_examples/view/template_jet_0/views/layouts/application.jet",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\">\n    <title>{{ isset(title) ? title : \"Default\" }}</title>\n  </head>\n  <body>\n    {{block documentBody()}}{{end}}\n  </body>\n</html>\n"
  },
  {
    "path": "_examples/view/template_jet_0/views/todos/index.jet",
    "content": "{{extends \"../layouts/application.jet\"}}\n\n{{block button(label, href=\"javascript:void(0)\")}}\n  <a href=\"{{ href }}\">{{ label }}</a>\n{{end}}\n\n{{block ul()}}\n  <ul>\n    {{yield content}}\n  </ul>\n{{end}}\n\n{{block documentBody()}}\n  <h1>List of TODOs</h1>\n\n  {{if isset(showingAllDone) && showingAllDone}}\n    <p>Showing only TODOs that are done</p>\n  {{else}}\n    <p><a href=\"/all-done\">Show only TODOs that are done</a></p>\n  {{end}}\n\n  {{yield ul() content}}\n    {{range id, value := .}}\n      <li {{if value.Done}}style=\"color:red;text-decoration: line-through;\"{{end}}>\n        <a href=\"/todo?id={{ id }}\">{{ value.Text }}</a>\n        {{yield button(label=\"UP\", href=\"/update/?id=\"+base64(id))}} - {{yield button(href=\"/delete/?id=\"+id, label=\"DL\")}}\n      </li>\n    {{end}}\n  {{end}}\n{{end}}\n"
  },
  {
    "path": "_examples/view/template_jet_0/views/todos/show.jet",
    "content": "{{extends \"../layouts/application.jet\"}}\n\n{{block documentBody()}}\n  <h1>Show TODO</h1>\n  <p>This uses a custom renderer by implementing the Renderer interface.\n  <p>\n    {{.}}\n  </p>\n{{end}}\n"
  },
  {
    "path": "_examples/view/template_jet_1_embedded/bindata.go",
    "content": "// Code generated by go-bindata. (@generated) DO NOT EDIT.\n\n// Package main generated by go-bindata.// sources:\n// views/includes/_partial.jet\n// views/includes/blocks.jet\n// views/index.jet\n// views/layouts/application.jet\npackage main\n\nimport (\n\t\"bytes\"\n\t\"compress/gzip\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"time\"\n)\n\nfunc bindataRead(data []byte, name string) ([]byte, error) {\n\tgz, err := gzip.NewReader(bytes.NewBuffer(data))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"read %q: %v\", name, err)\n\t}\n\n\tvar buf bytes.Buffer\n\t_, err = io.Copy(&buf, gz)\n\tclErr := gz.Close()\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"read %q: %v\", name, err)\n\t}\n\tif clErr != nil {\n\t\treturn nil, err\n\t}\n\n\treturn buf.Bytes(), nil\n}\n\ntype asset struct {\n\tbytes []byte\n\tinfo  os.FileInfo\n}\n\ntype bindataFileInfo struct {\n\tname    string\n\tsize    int64\n\tmode    os.FileMode\n\tmodTime time.Time\n}\n\n// Name return file name\nfunc (fi bindataFileInfo) Name() string {\n\treturn fi.name\n}\n\n// Size return file size\nfunc (fi bindataFileInfo) Size() int64 {\n\treturn fi.size\n}\n\n// Mode return file mode\nfunc (fi bindataFileInfo) Mode() os.FileMode {\n\treturn fi.mode\n}\n\n// ModTime return file modify time\nfunc (fi bindataFileInfo) ModTime() time.Time {\n\treturn fi.modTime\n}\n\n// IsDir return file whether a directory\nfunc (fi bindataFileInfo) IsDir() bool {\n\treturn fi.mode&os.ModeDir != 0\n}\n\n// Sys return file is sys mode\nfunc (fi bindataFileInfo) Sys() any {\n\treturn nil\n}\n\ntype assetFile struct {\n\t*bytes.Reader\n\tname            string\n\tchildInfos      []os.FileInfo\n\tchildInfoOffset int\n}\n\ntype assetOperator struct{}\n\n// Open implement http.FileSystem interface\nfunc (f *assetOperator) Open(name string) (http.File, error) {\n\tvar err error\n\tif len(name) > 0 && name[0] == '/' {\n\t\tname = name[1:]\n\t}\n\tcontent, err := Asset(name)\n\tif err == nil {\n\t\treturn &assetFile{name: name, Reader: bytes.NewReader(content)}, nil\n\t}\n\tchildren, err := AssetDir(name)\n\tif err == nil {\n\t\tchildInfos := make([]os.FileInfo, 0, len(children))\n\t\tfor _, child := range children {\n\t\t\tchildPath := filepath.Join(name, child)\n\t\t\tinfo, errInfo := AssetInfo(filepath.Join(name, child))\n\t\t\tif errInfo == nil {\n\t\t\t\tchildInfos = append(childInfos, info)\n\t\t\t} else {\n\t\t\t\tchildInfos = append(childInfos, newDirFileInfo(childPath))\n\t\t\t}\n\t\t}\n\t\treturn &assetFile{name: name, childInfos: childInfos}, nil\n\t} else {\n\t\t// If the error is not found, return an error that will\n\t\t// result in a 404 error. Otherwise the server returns\n\t\t// a 500 error for files not found.\n\t\tif strings.Contains(err.Error(), \"not found\") {\n\t\t\treturn nil, os.ErrNotExist\n\t\t}\n\t\treturn nil, err\n\t}\n}\n\n// Close no need do anything\nfunc (f *assetFile) Close() error {\n\treturn nil\n}\n\n// Readdir read dir's children file info\nfunc (f *assetFile) Readdir(count int) ([]os.FileInfo, error) {\n\tif len(f.childInfos) == 0 {\n\t\treturn nil, os.ErrNotExist\n\t}\n\tif count <= 0 {\n\t\treturn f.childInfos, nil\n\t}\n\tif f.childInfoOffset+count > len(f.childInfos) {\n\t\tcount = len(f.childInfos) - f.childInfoOffset\n\t}\n\toffset := f.childInfoOffset\n\tf.childInfoOffset += count\n\treturn f.childInfos[offset : offset+count], nil\n}\n\n// Stat read file info from asset item\nfunc (f *assetFile) Stat() (os.FileInfo, error) {\n\tif len(f.childInfos) != 0 {\n\t\treturn newDirFileInfo(f.name), nil\n\t}\n\treturn AssetInfo(f.name)\n}\n\n// newDirFileInfo return default dir file info\nfunc newDirFileInfo(name string) os.FileInfo {\n\treturn &bindataFileInfo{\n\t\tname:    name,\n\t\tsize:    0,\n\t\tmode:    os.FileMode(2147484068), // equal os.FileMode(0644)|os.ModeDir\n\t\tmodTime: time.Time{}}\n}\n\n// AssetFile return a http.FileSystem instance that data backend by asset\nfunc AssetFile() http.FileSystem {\n\treturn &assetOperator{}\n}\n\nvar _includes_partialJet = []byte(\"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\xb2\\x29\\xb0\\xf3\\xcc\\x4b\\xce\\x29\\x4d\\x49\\x4d\\x51\\x28\\x48\\x2c\\x2a\\xc9\\x4c\\xcc\\xb1\\xd1\\x2f\\xb0\\xe3\\xe5\\x02\\x04\\x00\\x00\\xff\\xff\\x90\\x62\\x4f\\xfb\\x19\\x00\\x00\\x00\")\n\nfunc includes_partialJetBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_includes_partialJet,\n\t\t\"includes/_partial.jet\",\n\t)\n}\n\nfunc includes_partialJet() (*asset, error) {\n\tbytes, err := includes_partialJetBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"includes/_partial.jet\", size: 25, mode: os.FileMode(438), modTime: time.Unix(1599156854, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\nvar _includesBlocksJet = []byte(\"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\xaa\\xae\\x4e\\xca\\xc9\\x4f\\xce\\x56\\xc8\\x4d\\xcd\\x2b\\xd5\\xd0\\xac\\xad\\xe5\\xe5\\x52\\x50\\xb0\\x29\\xb0\\x0b\\xc9\\x48\\x05\\x0b\\x29\\x40\\x64\\xcb\\x13\\x8b\\x15\\x32\\xf3\\xca\\xf2\\xb3\\x53\\x53\\xf4\\x6c\\xf4\\x0b\\xec\\x78\\xb9\\xaa\\xab\\x53\\xf3\\x52\\x40\\xca\\x01\\x01\\x00\\x00\\xff\\xff\\xa0\\xd9\\xd9\\x5d\\x41\\x00\\x00\\x00\")\n\nfunc includesBlocksJetBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_includesBlocksJet,\n\t\t\"includes/blocks.jet\",\n\t)\n}\n\nfunc includesBlocksJet() (*asset, error) {\n\tbytes, err := includesBlocksJetBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"includes/blocks.jet\", size: 65, mode: os.FileMode(438), modTime: time.Unix(1599156854, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\nvar _indexJet = []byte(\"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\x64\\x90\\xb1\\x6a\\xf3\\x30\\x14\\x85\\xf7\\x1f\\xfe\\x77\\x38\\xf5\\xd2\\x74\\xa9\\xc9\\x5a\\x8c\\x87\\x42\\x86\\x2e\\x5d\\xfa\\x00\\x45\\xb6\\x6e\\xb0\\x1a\\x59\\x57\\xf8\\x5e\\xd7\\x36\\x22\\xef\\x5e\\x2c\\x9b\\x10\\xda\\xed\\x48\\xe7\\x3b\\xe8\\x43\\x29\\xd1\\xac\\x14\\xac\\xa0\\xf0\\x66\\xe1\\x51\\xa5\\x34\\x31\\x7a\\xd7\\x1a\\x75\\x1c\\x9e\\xbf\\x48\\x8b\\xeb\\xf5\\xff\\xbf\\x94\\x5c\\x1f\\x79\\x50\\x14\\x2e\\xb4\\x7e\\xb4\\x24\\x65\\xe3\\xb9\\xbd\\xc8\\x8d\\x58\\x99\\x7c\\x05\\xcb\\xed\\xd8\\x53\\xd0\\x57\\xb6\\xcb\\xe1\\x69\\xed\\x80\\xaa\\x3b\\xd6\\xa7\\xbe\\x21\\x6b\\xc9\\x82\\x66\\xd3\\x47\\x4f\\x55\\xd9\\x1d\\xeb\\x75\\x08\\x54\\xc1\\x7c\\xd7\\x6b\\x00\\x52\\x5a\\x1c\\x79\\x8b\\x9e\\xc2\\x78\\x5b\\x97\\x5b\\xbf\\xe6\\x94\\x76\\x83\\x3b\\x95\\xcf\\x68\\x06\\x75\\xc6\\xdf\\xc9\\x64\\xf0\\x8c\\x87\\x9d\\x79\\x3b\\x9f\\x66\\x27\\x2a\\x87\\xc2\\x32\\xc9\\x3b\\x6b\\x3e\\x66\\x7e\\x7f\\x03\\xa8\\x62\\xfd\\xd1\\xf1\\x24\\xe8\\x78\\xfa\\x33\\xc4\\xc4\\xc3\\x45\\x5e\\xf0\\x7b\\x8e\\xc9\\x08\\x02\\x2b\\x76\\xde\\xa2\\xa1\\xd6\\x8c\\x42\\x70\\x9a\\xe1\\xf0\\xa8\\xa0\\x4c\\x57\\x65\\xac\\x37\\x31\\x0a\\x76\\xfb\\xd4\\x3d\\xfc\\x04\\x00\\x00\\xff\\xff\\x28\\x5a\\x9d\\x42\\x85\\x01\\x00\\x00\")\n\nfunc indexJetBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_indexJet,\n\t\t\"index.jet\",\n\t)\n}\n\nfunc indexJet() (*asset, error) {\n\tbytes, err := indexJetBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"index.jet\", size: 389, mode: os.FileMode(438), modTime: time.Unix(1599156854, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\nvar _layoutsApplicationJet = []byte(\"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\x3c\\x8e\\xbd\\xae\\xc2\\x30\\x0c\\x85\\xf7\\x4a\\x7d\\x07\\xdf\\x4c\\x97\\x01\\x75\\x65\\x70\\x3b\\x00\\x65\\x85\\xa1\\x0c\\x8c\\x69\\x6d\\x11\\x44\\x7e\\x10\\x18\\x89\\x2a\\xca\\xbb\\x23\\xda\\xc0\\x64\\xcb\\x3e\\xdf\\xa7\\x83\\x7f\\xdb\\xfd\\xa6\\x3b\\x1d\\x5a\\x30\\xe2\\x6c\\x53\\x16\\xf8\\x99\\x60\\xb5\\x3f\\xd7\\x8a\\xbd\\x6a\\xca\\x02\\x00\\x0d\\x6b\\x9a\\x36\\x00\\x74\\x2c\\x1a\\x06\\xa3\\xef\\x0f\\x96\\x5a\\x1d\\xbb\\xdd\\x72\\xa5\\xbe\\x3f\\xb9\\x88\\xe5\\xa6\\x75\\x3d\\x13\\x31\\x01\\xbf\\xb4\\xbb\\x59\\xc6\\x6a\\xbe\\x4f\\xaa\\xea\\xe7\\xc2\\x3e\\xd0\\x98\\xc9\\x18\\x7b\\x1b\\x86\\x2b\\x50\\x18\\x9e\\x8e\\xbd\\xac\\x03\\x8d\\xff\\x8b\\x94\\x62\\x64\\x4f\\x29\\xcd\\x64\\xce\\x63\\x95\\xab\\xbe\\x03\\x00\\x00\\xff\\xff\\xee\\xc2\\x94\\xa4\\xbc\\x00\\x00\\x00\")\n\nfunc layoutsApplicationJetBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_layoutsApplicationJet,\n\t\t\"layouts/application.jet\",\n\t)\n}\n\nfunc layoutsApplicationJet() (*asset, error) {\n\tbytes, err := layoutsApplicationJetBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"layouts/application.jet\", size: 188, mode: os.FileMode(438), modTime: time.Unix(1599156854, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\n// Asset loads and returns the asset for the given name.\n// It returns an error if the asset could not be found or\n// could not be loaded.\nfunc Asset(name string) ([]byte, error) {\n\tcanonicalName := strings.Replace(name, \"\\\\\", \"/\", -1)\n\tif f, ok := _bindata[canonicalName]; ok {\n\t\ta, err := f()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"Asset %s can't read by error: %v\", name, err)\n\t\t}\n\t\treturn a.bytes, nil\n\t}\n\treturn nil, fmt.Errorf(\"Asset %s not found\", name)\n}\n\n// MustAsset is like Asset but panics when Asset would return an error.\n// It simplifies safe initialization of global variables.\nfunc MustAsset(name string) []byte {\n\ta, err := Asset(name)\n\tif err != nil {\n\t\tpanic(\"asset: Asset(\" + name + \"): \" + err.Error())\n\t}\n\n\treturn a\n}\n\n// AssetInfo loads and returns the asset info for the given name.\n// It returns an error if the asset could not be found or\n// could not be loaded.\nfunc AssetInfo(name string) (os.FileInfo, error) {\n\tcanonicalName := strings.Replace(name, \"\\\\\", \"/\", -1)\n\tif f, ok := _bindata[canonicalName]; ok {\n\t\ta, err := f()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"AssetInfo %s can't read by error: %v\", name, err)\n\t\t}\n\t\treturn a.info, nil\n\t}\n\treturn nil, fmt.Errorf(\"AssetInfo %s not found\", name)\n}\n\n// AssetNames returns the names of the assets.\nfunc AssetNames() []string {\n\tnames := make([]string, 0, len(_bindata))\n\tfor name := range _bindata {\n\t\tnames = append(names, name)\n\t}\n\treturn names\n}\n\n// _bindata is a table, holding each asset generator, mapped to its name.\nvar _bindata = map[string]func() (*asset, error){\n\t\"includes/_partial.jet\":   includes_partialJet,\n\t\"includes/blocks.jet\":     includesBlocksJet,\n\t\"index.jet\":               indexJet,\n\t\"layouts/application.jet\": layoutsApplicationJet,\n}\n\n// AssetDir returns the file names below a certain\n// directory embedded in the file by go-bindata.\n// For example if you run go-bindata on data/... and data contains the\n// following hierarchy:\n//\n//\tdata/\n//\t  foo.txt\n//\t  img/\n//\t    a.png\n//\t    b.png\n//\n// then AssetDir(\"data\") would return []string{\"foo.txt\", \"img\"}\n// AssetDir(\"data/img\") would return []string{\"a.png\", \"b.png\"}\n// AssetDir(\"foo.txt\") and AssetDir(\"nonexistent\") would return an error\n// AssetDir(\"\") will return []string{\"data\"}.\nfunc AssetDir(name string) ([]string, error) {\n\tnode := _bintree\n\tif len(name) != 0 {\n\t\tcanonicalName := strings.Replace(name, \"\\\\\", \"/\", -1)\n\t\tpathList := strings.Split(canonicalName, \"/\")\n\t\tfor _, p := range pathList {\n\t\t\tnode = node.Children[p]\n\t\t\tif node == nil {\n\t\t\t\treturn nil, fmt.Errorf(\"Asset %s not found\", name)\n\t\t\t}\n\t\t}\n\t}\n\tif node.Func != nil {\n\t\treturn nil, fmt.Errorf(\"Asset %s not found\", name)\n\t}\n\trv := make([]string, 0, len(node.Children))\n\tfor childName := range node.Children {\n\t\trv = append(rv, childName)\n\t}\n\treturn rv, nil\n}\n\ntype bintree struct {\n\tFunc     func() (*asset, error)\n\tChildren map[string]*bintree\n}\n\nvar _bintree = &bintree{nil, map[string]*bintree{\n\t\"includes\": {nil, map[string]*bintree{\n\t\t\"_partial.jet\": {includes_partialJet, map[string]*bintree{}},\n\t\t\"blocks.jet\":   {includesBlocksJet, map[string]*bintree{}},\n\t}},\n\t\"index.jet\": {indexJet, map[string]*bintree{}},\n\t\"layouts\": {nil, map[string]*bintree{\n\t\t\"application.jet\": {layoutsApplicationJet, map[string]*bintree{}},\n\t}},\n}}\n\n// RestoreAsset restores an asset under the given directory\nfunc RestoreAsset(dir, name string) error {\n\tdata, err := Asset(name)\n\tif err != nil {\n\t\treturn err\n\t}\n\tinfo, err := AssetInfo(name)\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = os.WriteFile(_filePath(dir, name), data, info.Mode())\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// RestoreAssets restores an asset under the given directory recursively\nfunc RestoreAssets(dir, name string) error {\n\tchildren, err := AssetDir(name)\n\t// File\n\tif err != nil {\n\t\treturn RestoreAsset(dir, name)\n\t}\n\t// Dir\n\tfor _, child := range children {\n\t\terr = RestoreAssets(dir, filepath.Join(name, child))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc _filePath(dir, name string) string {\n\tcanonicalName := strings.Replace(name, \"\\\\\", \"/\", -1)\n\treturn filepath.Join(append([]string{dir}, strings.Split(canonicalName, \"/\")...)...)\n}\n"
  },
  {
    "path": "_examples/view/template_jet_1_embedded/main.go",
    "content": "// Package main shows how to use jet templates embedded in your application with ease using the Iris built-in Jet view engine.\n// This example is a customized fork of https://github.com/CloudyKit/jet/tree/master/examples/asset_packaging, so you can\n// notice the differences side by side. For example, you don't have to use any external package inside your application,\n// Iris manually builds the template loader for binary data when Asset and AssetNames are available via tools like the go-bindata.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\n// $ go install github.com/go-bindata/go-bindata/v3/go-bindata@latest\n//\n// $ go-bindata -fs -prefix \"views\" ./views/...\n// $ go run .\n//\n// System files are not used, you can optionally delete the folder and run the example now.\nfunc main() {\n\tapp := iris.New()\n\ttmpl := iris.Jet(AssetFile(), \".jet\")\n\tapp.RegisterView(tmpl)\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tif err := ctx.View(\"index.jet\"); err != nil {\n\t\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\t\treturn\n\t\t}\n\t})\n\n\taddr := \":8080\"\n\tif port := os.Getenv(\"PORT\"); port != \"\" {\n\t\taddr = \":\" + port\n\t}\n\n\tapp.Listen(addr)\n}\n"
  },
  {
    "path": "_examples/view/template_jet_1_embedded/views/includes/_partial.jet",
    "content": "<p>Included partial</p>\n"
  },
  {
    "path": "_examples/view/template_jet_1_embedded/views/includes/blocks.jet",
    "content": "{{block menu()}}\n  <p>The menu block was invoked.</p>\n{{end}}\n"
  },
  {
    "path": "_examples/view/template_jet_1_embedded/views/index.jet",
    "content": "{{extends \"layouts/application.jet\"}}\n{{import \"includes/blocks.jet\"}}\n\n{{block documentBody()}}\n  <h1>Embedded example</h1>\n\n  <nav>\n    {{yield menu()}}\n  </nav>\n\n  {{include \"includes/_partial.jet\"}}\n\n  {{if !includeIfExists(\"doesNotExist.jet\")}}\n    <p>Shows how !includeIfExists works: doesNotExist.jet was not included because it doesn't exist.</p>\n  {{end}}\n{{end}}\n"
  },
  {
    "path": "_examples/view/template_jet_1_embedded/views/layouts/application.jet",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\">\n    <title>Embedded example</title>\n  </head>\n  <body>\n    {{block documentBody()}}{{end}}\n  </body>\n</html>\n"
  },
  {
    "path": "_examples/view/template_jet_2/main.go",
    "content": "// Package main an example on how to naming your routes & use the custom 'url path' Jet Template Engine.\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tapp.RegisterView(iris.Jet(\"./views\", \".jet\").Reload(true))\n\n\tmypathRoute := app.Get(\"/mypath\", writePathHandler)\n\tmypathRoute.Name = \"my-page1\"\n\n\tmypath2Route := app.Get(\"/mypath2/{paramfirst}/{paramsecond}\", writePathHandler)\n\tmypath2Route.Name = \"my-page2\"\n\n\tmypath3Route := app.Get(\"/mypath3/{paramfirst}/statichere/{paramsecond}\", writePathHandler)\n\tmypath3Route.Name = \"my-page3\"\n\n\tmypath4Route := app.Get(\"/mypath4/{paramfirst}/statichere/{paramsecond}/{otherparam}/{something:path}\", writePathHandler)\n\t// same as: app.Get(\"/mypath4/:paramfirst/statichere/:paramsecond/:otherparam/*something\", writePathHandler)\n\tmypath4Route.Name = \"my-page4\"\n\n\t// same with Handle/Func\n\tmypath5Route := app.Handle(\"GET\", \"/mypath5/{paramfirst:int}/statichere/{paramsecond}/{otherparam}/anything/{something:path}\", writePathHandlerPage5)\n\tmypath5Route.Name = \"my-page5\"\n\n\tmypath6Route := app.Get(\"/mypath6/{paramfirst}/{paramsecond}/statichere/{paramThirdAfterStatic}\", writePathHandler)\n\tmypath6Route.Name = \"my-page6\"\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\t// for /mypath6...\n\t\tparamsAsArray := []string{\"theParam1\", \"theParam2\", \"paramThirdAfterStatic\"}\n\t\tctx.ViewData(\"ParamsAsArray\", paramsAsArray)\n\t\tif err := ctx.View(\"page.jet\"); err != nil {\n\t\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\t\treturn\n\t\t}\n\t})\n\n\tapp.Get(\"/redirect/{namedRoute}\", func(ctx iris.Context) {\n\t\trouteName := ctx.Params().Get(\"namedRoute\")\n\t\tr := app.GetRoute(routeName)\n\t\tif r == nil {\n\t\t\tctx.StatusCode(iris.StatusNotFound)\n\t\t\tctx.Writef(\"Route with name %s not found\", routeName)\n\t\t\treturn\n\t\t}\n\n\t\tprintln(\"The path of \" + routeName + \"is: \" + r.Path)\n\t\t// if routeName == \"my-page1\"\n\t\t// prints: The path of of my-page1 is: /mypath\n\t\t// if it's a path which takes named parameters\n\t\t// then use \"r.ResolvePath(paramValuesHere)\"\n\t\tctx.Redirect(r.Path)\n\t\t// http://localhost:8080/redirect/my-page1 will redirect to -> http://localhost:8080/mypath\n\t})\n\n\t// http://localhost:8080\n\t// http://localhost:8080/redirect/my-page1\n\tapp.Listen(\":8080\")\n}\n\nfunc writePathHandler(ctx iris.Context) {\n\tctx.Writef(\"Hello from %s.\", ctx.Path())\n}\n\nfunc writePathHandlerPage5(ctx iris.Context) {\n\tctx.Writef(\"Hello from %s.\\nparamfirst(int)=%d\", ctx.Path(), ctx.Params().GetIntDefault(\"paramfirst\", 0))\n}\n"
  },
  {
    "path": "_examples/view/template_jet_2/views/page.jet",
    "content": "<a href=\"{{urlpath(\"my-page1\")}}\">/mypath</a>\n<br />\n<br/>\n<a href=\"{{urlpath(\"my-page2\",\"theParam1\",\"theParam2\")}}\">/mypath2/{paramfirst}/{paramsecond}</a>\n<br />\n<br />\n\n<a href=\"{{urlpath(\"my-page3\", \"theParam1\", \"theParam2AfterStatic\")}}\">/mypath3/{paramfirst}/statichere/{paramsecond}</a>\n<br />\n<br />\n\n<a href=\"{{urlpath(\"my-page4\", \"theParam1\", \"theparam2AfterStatic\",  \"otherParam\", \"matchAnything\")}}\">\n  /mypath4/{paramfirst}/statichere/{paramsecond}/{otherparam}/{something:path}</a>\n<br />\n<br />\n\n<a href=\"{{urlpath(\"my-page5\", \"42\", \"theParam2Afterstatichere\", \"otherParam\", \"matchAnythingAfterStatic\")}}\">\n  /mypath5/{paramfirst}/statichere/{paramsecond}/{otherparam}/anything/{anything:path}</a>\n<br />\n<br />\n\n<a href={{urlpath(\"my-page6\", .ParamsAsArray)}}>\n  /mypath6/{paramfirst}/{paramsecond}/statichere/{paramThirdAfterStatic}\n</a>\n"
  },
  {
    "path": "_examples/view/template_jet_3/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/view\"\n)\n\nfunc main() {\n\ttmpl := iris.Jet(\"./views\", \".jet\")\n\ttmpl.Reload(true)\n\n\tval := reflect.ValueOf(ViewBuiler{})\n\tfns := val.Type()\n\tfor i := 0; i < fns.NumMethod(); i++ {\n\t\tmethod := fns.Method(i)\n\t\ttmpl.AddFunc(strings.ToLower(method.Name), val.Method(i).Interface())\n\t}\n\n\tapp := iris.New()\n\tapp.RegisterView(tmpl)\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tif err := ctx.View(\"index.jet\"); err != nil {\n\t\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\t\treturn\n\t\t}\n\t})\n\n\tapp.Listen(\":8080\")\n}\n\ntype ViewBuiler struct {\n}\n\nfunc (ViewBuiler) Asset(a view.JetArguments) reflect.Value {\n\tpath := a.Get(0).String()\n\t// fmt.Println(os.Getenv(\"APP_URL\"))\n\treturn reflect.ValueOf(path)\n}\n\nfunc (ViewBuiler) Style(a view.JetArguments) reflect.Value {\n\tpath := a.Get(0).String()\n\ts := fmt.Sprintf(`<link href=\"%v\" rel=\"stylesheet\">`, path)\n\treturn reflect.ValueOf(s)\n}\n\nfunc (ViewBuiler) Script(a view.JetArguments) reflect.Value {\n\tpath := a.Get(0).String()\n\ts := fmt.Sprintf(`<script src=\"%v\"></script>`, path)\n\treturn reflect.ValueOf(s)\n}\n"
  },
  {
    "path": "_examples/view/template_jet_3/views/index.jet",
    "content": "{{ asset(\"./myasset.mp3\")}}\n<br/>\n{{ style(\"my-stle.css\") | raw}}\n<br/>\n{{ script(\"my-script.js\") | raw}}\n\n<!-- view page source: can't add a link as chrome blocks loading local source, instead\ndo that: -->\n\n<code>\n    ./myasset.mp3\n    <br/>\n    &lt;link href=\"my-stle.css\" rel=\"stylesheet\"&gt;\n    <br/>\n    &lt;script src=\"my-script.js\">&lt;/script&gt;\n</code>"
  },
  {
    "path": "_examples/view/template_pug_0/main.go",
    "content": "// Package main shows an example of pug actions based on https://github.com/Joker/jade/tree/master/example/actions\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\ntype Person struct {\n\tName   string\n\tAge    int\n\tEmails []string\n\tJobs   []*Job\n}\n\ntype Job struct {\n\tEmployer string\n\tRole     string\n}\n\nfunc main() {\n\tapp := iris.New()\n\n\ttmpl := iris.Pug(\"./templates\", \".pug\")\n\tapp.RegisterView(tmpl)\n\n\tapp.Get(\"/\", index)\n\n\t// http://localhost:8080\n\tapp.Listen(\":8080\")\n}\n\nfunc index(ctx iris.Context) {\n\tjob1 := Job{Employer: \"Monash B\", Role: \"Honorary\"}\n\tjob2 := Job{Employer: \"Box Hill\", Role: \"Head of HE\"}\n\n\tperson := Person{\n\t\tName:   \"jan\",\n\t\tAge:    50,\n\t\tEmails: []string{\"jan@newmarch.name\", \"jan.newmarch@gmail.com\"},\n\t\tJobs:   []*Job{&job1, &job2},\n\t}\n\n\tif err := ctx.View(\"index.pug\", person); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "_examples/view/template_pug_0/templates/index.pug",
    "content": "doctype html\nhtml(lang=\"en\")\n\thead\n\t\tmeta(charset=\"utf-8\")\n\t\ttitle Title\n\tbody\n\t\tp ads\n\t\tul\n\t\t\tli The name is {{.Name}}.\n\t\t\tli The age is {{.Age}}.\n\n\t\teach _,_ in .Emails\n\t\t\tdiv An email is {{.}}\n\n\t\t| {{ with .Jobs }}\n\t\t\teach _,_ in .\n\t\t\t\tdiv\n\t\t\t\t An employer is {{.Employer}}\n\t\t\t\t and the role is {{.Role}}\n\t\t| {{ end }}"
  },
  {
    "path": "_examples/view/template_pug_1/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"html/template\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\n\ttmpl := iris.Pug(\"./templates\", \".pug\")\n\ttmpl.Reload(true)                                            // reload templates on each request (development mode)\n\ttmpl.AddFunc(\"bold\", func(s string) (template.HTML, error) { // add your template func here.\n\t\treturn template.HTML(\"<b>\" + s + \"</b>\"), nil\n\t})\n\n\tapp.RegisterView(tmpl)\n\n\tapp.Get(\"/\", index)\n\n\t// http://localhost:8080\n\tapp.Listen(\":8080\")\n}\n\nfunc index(ctx iris.Context) {\n\tif err := ctx.View(\"index.pug\"); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "_examples/view/template_pug_1/templates/footer.pug",
    "content": "#footer\n  p Copyright (c) foobar"
  },
  {
    "path": "_examples/view/template_pug_1/templates/header.pug",
    "content": "head\n  title My Site\n  <!-- script(src='/javascripts/jquery.js')\n  script(src='/javascripts/app.js') -->"
  },
  {
    "path": "_examples/view/template_pug_1/templates/index.pug",
    "content": "doctype html\nhtml\n  include header.pug\n  body\n    h1 My Site\n    p {{ bold \"Welcome to my super lame site.\"}}\n    include footer.pug"
  },
  {
    "path": "_examples/view/template_pug_2_embedded/bindata.go",
    "content": "// Code generated by go-bindata. (@generated) DO NOT EDIT.\n\n// Package main generated by go-bindata.// sources:\n// templates/index.pug\n// templates/layout.pug\npackage main\n\nimport (\n\t\"bytes\"\n\t\"compress/gzip\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"time\"\n)\n\nfunc bindataRead(data []byte, name string) ([]byte, error) {\n\tgz, err := gzip.NewReader(bytes.NewBuffer(data))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"read %q: %v\", name, err)\n\t}\n\n\tvar buf bytes.Buffer\n\t_, err = io.Copy(&buf, gz)\n\tclErr := gz.Close()\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"read %q: %v\", name, err)\n\t}\n\tif clErr != nil {\n\t\treturn nil, err\n\t}\n\n\treturn buf.Bytes(), nil\n}\n\ntype asset struct {\n\tbytes []byte\n\tinfo  os.FileInfo\n}\n\ntype bindataFileInfo struct {\n\tname    string\n\tsize    int64\n\tmode    os.FileMode\n\tmodTime time.Time\n}\n\n// Name return file name\nfunc (fi bindataFileInfo) Name() string {\n\treturn fi.name\n}\n\n// Size return file size\nfunc (fi bindataFileInfo) Size() int64 {\n\treturn fi.size\n}\n\n// Mode return file mode\nfunc (fi bindataFileInfo) Mode() os.FileMode {\n\treturn fi.mode\n}\n\n// ModTime return file modify time\nfunc (fi bindataFileInfo) ModTime() time.Time {\n\treturn fi.modTime\n}\n\n// IsDir return file whether a directory\nfunc (fi bindataFileInfo) IsDir() bool {\n\treturn fi.mode&os.ModeDir != 0\n}\n\n// Sys return file is sys mode\nfunc (fi bindataFileInfo) Sys() any {\n\treturn nil\n}\n\ntype assetFile struct {\n\t*bytes.Reader\n\tname            string\n\tchildInfos      []os.FileInfo\n\tchildInfoOffset int\n}\n\ntype assetOperator struct{}\n\n// Open implement http.FileSystem interface\nfunc (f *assetOperator) Open(name string) (http.File, error) {\n\tvar err error\n\tif len(name) > 0 && name[0] == '/' {\n\t\tname = name[1:]\n\t}\n\tcontent, err := Asset(name)\n\tif err == nil {\n\t\treturn &assetFile{name: name, Reader: bytes.NewReader(content)}, nil\n\t}\n\tchildren, err := AssetDir(name)\n\tif err == nil {\n\t\tchildInfos := make([]os.FileInfo, 0, len(children))\n\t\tfor _, child := range children {\n\t\t\tchildPath := filepath.Join(name, child)\n\t\t\tinfo, errInfo := AssetInfo(filepath.Join(name, child))\n\t\t\tif errInfo == nil {\n\t\t\t\tchildInfos = append(childInfos, info)\n\t\t\t} else {\n\t\t\t\tchildInfos = append(childInfos, newDirFileInfo(childPath))\n\t\t\t}\n\t\t}\n\t\treturn &assetFile{name: name, childInfos: childInfos}, nil\n\t} else {\n\t\t// If the error is not found, return an error that will\n\t\t// result in a 404 error. Otherwise the server returns\n\t\t// a 500 error for files not found.\n\t\tif strings.Contains(err.Error(), \"not found\") {\n\t\t\treturn nil, os.ErrNotExist\n\t\t}\n\t\treturn nil, err\n\t}\n}\n\n// Close no need do anything\nfunc (f *assetFile) Close() error {\n\treturn nil\n}\n\n// Readdir read dir's children file info\nfunc (f *assetFile) Readdir(count int) ([]os.FileInfo, error) {\n\tif len(f.childInfos) == 0 {\n\t\treturn nil, os.ErrNotExist\n\t}\n\tif count <= 0 {\n\t\treturn f.childInfos, nil\n\t}\n\tif f.childInfoOffset+count > len(f.childInfos) {\n\t\tcount = len(f.childInfos) - f.childInfoOffset\n\t}\n\toffset := f.childInfoOffset\n\tf.childInfoOffset += count\n\treturn f.childInfos[offset : offset+count], nil\n}\n\n// Stat read file info from asset item\nfunc (f *assetFile) Stat() (os.FileInfo, error) {\n\tif len(f.childInfos) != 0 {\n\t\treturn newDirFileInfo(f.name), nil\n\t}\n\treturn AssetInfo(f.name)\n}\n\n// newDirFileInfo return default dir file info\nfunc newDirFileInfo(name string) os.FileInfo {\n\treturn &bindataFileInfo{\n\t\tname:    name,\n\t\tsize:    0,\n\t\tmode:    os.FileMode(2147484068), // equal os.FileMode(0644)|os.ModeDir\n\t\tmodTime: time.Time{}}\n}\n\n// AssetFile return a http.FileSystem instance that data backend by asset\nfunc AssetFile() http.FileSystem {\n\treturn &assetOperator{}\n}\n\nvar _indexPug = []byte(\"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\x4a\\xad\\x28\\x49\\xcd\\x4b\\x29\\x56\\xc8\\x49\\xac\\xcc\\x2f\\x2d\\xd1\\x2b\\x28\\x4d\\xe7\\xe5\\xe2\\xe5\\x4a\\xca\\xc9\\x4f\\xce\\x56\\x28\\xc9\\x2c\\xc9\\x49\\xe5\\xe5\\x52\\x80\\x30\\x14\\x1c\\x8b\\x4a\\x32\\x93\\x73\\x52\\x15\\x42\\x20\\xc2\\x30\\x55\\xc9\\xf9\\x79\\x25\\xa9\\x79\\x25\\x20\\x75\\x19\\x86\\x0a\\xbe\\x95\\x30\\x75\\x80\\x00\\x00\\x00\\xff\\xff\\xa6\\xfd\\x18\\x8c\\x5a\\x00\\x00\\x00\")\n\nfunc indexPugBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_indexPug,\n\t\t\"index.pug\",\n\t)\n}\n\nfunc indexPug() (*asset, error) {\n\tbytes, err := indexPugBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"index.pug\", size: 90, mode: os.FileMode(438), modTime: time.Unix(1599156854, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\nvar _layoutPug = []byte(\"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\x4a\\xc9\\x4f\\x2e\\xa9\\x2c\\x48\\x55\\xc8\\x28\\xc9\\xcd\\xe1\\xe5\\x82\\x90\\x0a\\x0a\\x19\\xa9\\x89\\x29\\x20\\x5a\\x41\\x21\\x29\\x27\\x3f\\x39\\x5b\\xa1\\x24\\xb3\\x24\\x27\\x15\\x22\\xa0\\x00\\xe1\\x28\\xb8\\xa4\\xa6\\x25\\x96\\xe6\\x94\\x20\\xa4\\x92\\xf2\\x53\\x2a\\x91\\xf5\\x24\\xe7\\xe7\\x95\\xa4\\xe6\\x95\\x00\\x02\\x00\\x00\\xff\\xff\\x5f\\xa5\\x93\\xf9\\x61\\x00\\x00\\x00\")\n\nfunc layoutPugBytes() ([]byte, error) {\n\treturn bindataRead(\n\t\t_layoutPug,\n\t\t\"layout.pug\",\n\t)\n}\n\nfunc layoutPug() (*asset, error) {\n\tbytes, err := layoutPugBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := bindataFileInfo{name: \"layout.pug\", size: 97, mode: os.FileMode(438), modTime: time.Unix(1599156854, 0)}\n\ta := &asset{bytes: bytes, info: info}\n\treturn a, nil\n}\n\n// Asset loads and returns the asset for the given name.\n// It returns an error if the asset could not be found or\n// could not be loaded.\nfunc Asset(name string) ([]byte, error) {\n\tcanonicalName := strings.Replace(name, \"\\\\\", \"/\", -1)\n\tif f, ok := _bindata[canonicalName]; ok {\n\t\ta, err := f()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"Asset %s can't read by error: %v\", name, err)\n\t\t}\n\t\treturn a.bytes, nil\n\t}\n\treturn nil, fmt.Errorf(\"Asset %s not found\", name)\n}\n\n// MustAsset is like Asset but panics when Asset would return an error.\n// It simplifies safe initialization of global variables.\nfunc MustAsset(name string) []byte {\n\ta, err := Asset(name)\n\tif err != nil {\n\t\tpanic(\"asset: Asset(\" + name + \"): \" + err.Error())\n\t}\n\n\treturn a\n}\n\n// AssetInfo loads and returns the asset info for the given name.\n// It returns an error if the asset could not be found or\n// could not be loaded.\nfunc AssetInfo(name string) (os.FileInfo, error) {\n\tcanonicalName := strings.Replace(name, \"\\\\\", \"/\", -1)\n\tif f, ok := _bindata[canonicalName]; ok {\n\t\ta, err := f()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"AssetInfo %s can't read by error: %v\", name, err)\n\t\t}\n\t\treturn a.info, nil\n\t}\n\treturn nil, fmt.Errorf(\"AssetInfo %s not found\", name)\n}\n\n// AssetNames returns the names of the assets.\nfunc AssetNames() []string {\n\tnames := make([]string, 0, len(_bindata))\n\tfor name := range _bindata {\n\t\tnames = append(names, name)\n\t}\n\treturn names\n}\n\n// _bindata is a table, holding each asset generator, mapped to its name.\nvar _bindata = map[string]func() (*asset, error){\n\t\"index.pug\":  indexPug,\n\t\"layout.pug\": layoutPug,\n}\n\n// AssetDir returns the file names below a certain\n// directory embedded in the file by go-bindata.\n// For example if you run go-bindata on data/... and data contains the\n// following hierarchy:\n//\n//\tdata/\n//\t  foo.txt\n//\t  img/\n//\t    a.png\n//\t    b.png\n//\n// then AssetDir(\"data\") would return []string{\"foo.txt\", \"img\"}\n// AssetDir(\"data/img\") would return []string{\"a.png\", \"b.png\"}\n// AssetDir(\"foo.txt\") and AssetDir(\"nonexistent\") would return an error\n// AssetDir(\"\") will return []string{\"data\"}.\nfunc AssetDir(name string) ([]string, error) {\n\tnode := _bintree\n\tif len(name) != 0 {\n\t\tcanonicalName := strings.Replace(name, \"\\\\\", \"/\", -1)\n\t\tpathList := strings.Split(canonicalName, \"/\")\n\t\tfor _, p := range pathList {\n\t\t\tnode = node.Children[p]\n\t\t\tif node == nil {\n\t\t\t\treturn nil, fmt.Errorf(\"Asset %s not found\", name)\n\t\t\t}\n\t\t}\n\t}\n\tif node.Func != nil {\n\t\treturn nil, fmt.Errorf(\"Asset %s not found\", name)\n\t}\n\trv := make([]string, 0, len(node.Children))\n\tfor childName := range node.Children {\n\t\trv = append(rv, childName)\n\t}\n\treturn rv, nil\n}\n\ntype bintree struct {\n\tFunc     func() (*asset, error)\n\tChildren map[string]*bintree\n}\n\nvar _bintree = &bintree{nil, map[string]*bintree{\n\t\"index.pug\":  {indexPug, map[string]*bintree{}},\n\t\"layout.pug\": {layoutPug, map[string]*bintree{}},\n}}\n\n// RestoreAsset restores an asset under the given directory\nfunc RestoreAsset(dir, name string) error {\n\tdata, err := Asset(name)\n\tif err != nil {\n\t\treturn err\n\t}\n\tinfo, err := AssetInfo(name)\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = os.WriteFile(_filePath(dir, name), data, info.Mode())\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// RestoreAssets restores an asset under the given directory recursively\nfunc RestoreAssets(dir, name string) error {\n\tchildren, err := AssetDir(name)\n\t// File\n\tif err != nil {\n\t\treturn RestoreAsset(dir, name)\n\t}\n\t// Dir\n\tfor _, child := range children {\n\t\terr = RestoreAssets(dir, filepath.Join(name, child))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc _filePath(dir, name string) string {\n\tcanonicalName := strings.Replace(name, \"\\\\\", \"/\", -1)\n\treturn filepath.Join(append([]string{dir}, strings.Split(canonicalName, \"/\")...)...)\n}\n"
  },
  {
    "path": "_examples/view/template_pug_2_embedded/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\n// $ go install github.com/go-bindata/go-bindata/v3/go-bindata@latest\n//\n// $ go-bindata -fs -prefix \"templates\" ./templates/...\n// $ go run .\n//\n// System files are not used, you can optionally delete the folder and run the example now.\nfunc main() {\n\tapp := iris.New()\n\n\ttmpl := iris.Pug(AssetFile(), \".pug\")\n\n\tapp.RegisterView(tmpl)\n\n\tapp.Get(\"/\", index)\n\n\t// http://localhost:8080\n\tapp.Listen(\":8080\")\n}\n\nfunc index(ctx iris.Context) {\n\tif err := ctx.View(\"index.pug\"); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "_examples/view/template_pug_2_embedded/templates/index.pug",
    "content": "extends layout.pug\n\nblock title\n  title Article Title\n\nblock content\n  h1 My Article"
  },
  {
    "path": "_examples/view/template_pug_2_embedded/templates/layout.pug",
    "content": "doctype html\nhtml\n  head\n    block title\n      title Default title\n  body\n    block content"
  },
  {
    "path": "_examples/view/write-to/main.go",
    "content": "package main\n\nimport (\n\t\"os\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\ntype mailData struct {\n\tTitle    string\n\tBody     string\n\tRefTitle string\n\tRefLink  string\n}\n\nfunc main() {\n\tapp := iris.New()\n\tapp.Logger().SetLevel(\"debug\")\n\tapp.RegisterView(iris.HTML(\"./views\", \".html\"))\n\n\t// you need to call `app.Build` manually before using the `app.View` func,\n\t// so templates are built in that state.\n\tapp.Build()\n\n\t// Or a string-buffered writer to use its body to send an e-mail\n\t// for sending e-mails you can use the https://github.com/kataras/go-mailer\n\t// or any other third-party package you like.\n\t//\n\t// The template's parsed result will be written to that writer.\n\twriter := os.Stdout\n\terr := app.View(writer, \"email/simple.html\", \"shared/email.html\", mailData{\n\t\tTitle:    \"This is my e-mail title\",\n\t\tBody:     \"This is my e-mail body\",\n\t\tRefTitle: \"Iris web framework\",\n\t\tRefLink:  \"https://iris-go.com\",\n\t})\n\tif err != nil {\n\t\tapp.Logger().Errorf(\"error from app.View: %v\", err)\n\t}\n\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/view/write-to/views/email/simple.html",
    "content": "{{.Body}}"
  },
  {
    "path": "_examples/view/write-to/views/shared/email.html",
    "content": "<h1>{{.Title}}</h1>\n<p class=\"body\">\n    {{yield}}\n</p>\n\n<a href=\"{{.RefLink}}\" target=\"_new\">{{.RefTitle}}</a>\n"
  },
  {
    "path": "_examples/webassembly/client/go-wasm-runtime.js",
    "content": "// Copyright 2018 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n(() => {\n\t// Map web browser API and Node.js API to a single common API (preferring web standards over Node.js API).\n\tconst isNodeJS = typeof process !== \"undefined\";\n\tif (isNodeJS) {\n\t\tglobal.require = require;\n\t\tglobal.fs = require(\"fs\");\n\n\t\tconst nodeCrypto = require(\"crypto\");\n\t\tglobal.crypto = {\n\t\t\tgetRandomValues(b) {\n\t\t\t\tnodeCrypto.randomFillSync(b);\n\t\t\t},\n\t\t};\n\n\t\tglobal.performance = {\n\t\t\tnow() {\n\t\t\t\tconst [sec, nsec] = process.hrtime();\n\t\t\t\treturn sec * 1000 + nsec / 1000000;\n\t\t\t},\n\t\t};\n\n\t\tconst util = require(\"util\");\n\t\tglobal.TextEncoder = util.TextEncoder;\n\t\tglobal.TextDecoder = util.TextDecoder;\n\t} else {\n\t\twindow.global = window;\n\n\t\tlet outputBuf = \"\";\n\t\tglobal.fs = {\n\t\t\tconstants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1, O_NONBLOCK: -1, O_SYNC: -1 }, // unused\n\t\t\twriteSync(fd, buf) {\n\t\t\t\toutputBuf += decoder.decode(buf);\n\t\t\t\tconst nl = outputBuf.lastIndexOf(\"\\n\");\n\t\t\t\tif (nl != -1) {\n\t\t\t\t\tconsole.log(outputBuf.substr(0, nl));\n\t\t\t\t\toutputBuf = outputBuf.substr(nl + 1);\n\t\t\t\t}\n\t\t\t\treturn buf.length;\n\t\t\t},\n\t\t\topenSync(path, flags, mode) {\n\t\t\t\tconst err = new Error(\"not implemented\");\n\t\t\t\terr.code = \"ENOSYS\";\n\t\t\t\tthrow err;\n\t\t\t},\n\t\t};\n\t}\n\n\tconst encoder = new TextEncoder(\"utf-8\");\n\tconst decoder = new TextDecoder(\"utf-8\");\n\n\tglobal.Go = class {\n\t\tconstructor() {\n\t\t\tthis.argv = [\"js\"];\n\t\t\tthis.env = {};\n\t\t\tthis.exit = (code) => {\n\t\t\t\tif (code !== 0) {\n\t\t\t\t\tconsole.warn(\"exit code:\", code);\n\t\t\t\t}\n\t\t\t};\n\t\t\tthis._callbackTimeouts = new Map();\n\t\t\tthis._nextCallbackTimeoutID = 1;\n\n\t\t\tconst mem = () => {\n\t\t\t\t// The buffer may change when requesting more memory.\n\t\t\t\treturn new DataView(this._inst.exports.mem.buffer);\n\t\t\t}\n\n\t\t\tconst setInt64 = (addr, v) => {\n\t\t\t\tmem().setUint32(addr + 0, v, true);\n\t\t\t\tmem().setUint32(addr + 4, Math.floor(v / 4294967296), true);\n\t\t\t}\n\n\t\t\tconst getInt64 = (addr) => {\n\t\t\t\tconst low = mem().getUint32(addr + 0, true);\n\t\t\t\tconst high = mem().getInt32(addr + 4, true);\n\t\t\t\treturn low + high * 4294967296;\n\t\t\t}\n\n\t\t\tconst loadValue = (addr) => {\n\t\t\t\tconst f = mem().getFloat64(addr, true);\n\t\t\t\tif (!isNaN(f)) {\n\t\t\t\t\treturn f;\n\t\t\t\t}\n\n\t\t\t\tconst id = mem().getUint32(addr, true);\n\t\t\t\treturn this._values[id];\n\t\t\t}\n\n\t\t\tconst storeValue = (addr, v) => {\n\t\t\t\tif (typeof v === \"number\") {\n\t\t\t\t\tif (isNaN(v)) {\n\t\t\t\t\t\tmem().setUint32(addr + 4, 0x7FF80000, true); // NaN\n\t\t\t\t\t\tmem().setUint32(addr, 0, true);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tmem().setFloat64(addr, v, true);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tmem().setUint32(addr + 4, 0x7FF80000, true); // NaN\n\n\t\t\t\tswitch (v) {\n\t\t\t\t\tcase undefined:\n\t\t\t\t\t\tmem().setUint32(addr, 1, true);\n\t\t\t\t\t\treturn;\n\t\t\t\t\tcase null:\n\t\t\t\t\t\tmem().setUint32(addr, 2, true);\n\t\t\t\t\t\treturn;\n\t\t\t\t\tcase true:\n\t\t\t\t\t\tmem().setUint32(addr, 3, true);\n\t\t\t\t\t\treturn;\n\t\t\t\t\tcase false:\n\t\t\t\t\t\tmem().setUint32(addr, 4, true);\n\t\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (typeof v === \"string\") {\n\t\t\t\t\tlet ref = this._stringRefs.get(v);\n\t\t\t\t\tif (ref === undefined) {\n\t\t\t\t\t\tref = this._values.length;\n\t\t\t\t\t\tthis._values.push(v);\n\t\t\t\t\t\tthis._stringRefs.set(v, ref);\n\t\t\t\t\t}\n\t\t\t\t\tmem().setUint32(addr, ref, true);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (typeof v === \"symbol\") {\n\t\t\t\t\tlet ref = this._symbolRefs.get(v);\n\t\t\t\t\tif (ref === undefined) {\n\t\t\t\t\t\tref = this._values.length;\n\t\t\t\t\t\tthis._values.push(v);\n\t\t\t\t\t\tthis._symbolRefs.set(v, ref);\n\t\t\t\t\t}\n\t\t\t\t\tmem().setUint32(addr, ref, true);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tlet ref = v[this._refProp];\n\t\t\t\tif (ref === undefined) {\n\t\t\t\t\tref = this._values.length;\n\t\t\t\t\tthis._values.push(v);\n\t\t\t\t\tv[this._refProp] = ref;\n\t\t\t\t}\n\t\t\t\tmem().setUint32(addr, ref, true);\n\t\t\t}\n\n\t\t\tconst loadSlice = (addr) => {\n\t\t\t\tconst array = getInt64(addr + 0);\n\t\t\t\tconst len = getInt64(addr + 8);\n\t\t\t\treturn new Uint8Array(this._inst.exports.mem.buffer, array, len);\n\t\t\t}\n\n\t\t\tconst loadSliceOfValues = (addr) => {\n\t\t\t\tconst array = getInt64(addr + 0);\n\t\t\t\tconst len = getInt64(addr + 8);\n\t\t\t\tconst a = new Array(len);\n\t\t\t\tfor (let i = 0; i < len; i++) {\n\t\t\t\t\ta[i] = loadValue(array + i * 8);\n\t\t\t\t}\n\t\t\t\treturn a;\n\t\t\t}\n\n\t\t\tconst loadString = (addr) => {\n\t\t\t\tconst saddr = getInt64(addr + 0);\n\t\t\t\tconst len = getInt64(addr + 8);\n\t\t\t\treturn decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len));\n\t\t\t}\n\n\t\t\tconst timeOrigin = Date.now() - performance.now();\n\t\t\tthis.importObject = {\n\t\t\t\tgo: {\n\t\t\t\t\t// func wasmExit(code int32)\n\t\t\t\t\t\"runtime.wasmExit\": (sp) => {\n\t\t\t\t\t\tthis.exited = true;\n\t\t\t\t\t\tthis.exit(mem().getInt32(sp + 8, true));\n\t\t\t\t\t},\n\n\t\t\t\t\t// func wasmWrite(fd uintptr, p unsafe.Pointer, n int32)\n\t\t\t\t\t\"runtime.wasmWrite\": (sp) => {\n\t\t\t\t\t\tconst fd = getInt64(sp + 8);\n\t\t\t\t\t\tconst p = getInt64(sp + 16);\n\t\t\t\t\t\tconst n = mem().getInt32(sp + 24, true);\n\t\t\t\t\t\tfs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n));\n\t\t\t\t\t},\n\n\t\t\t\t\t// func nanotime() int64\n\t\t\t\t\t\"runtime.nanotime\": (sp) => {\n\t\t\t\t\t\tsetInt64(sp + 8, (timeOrigin + performance.now()) * 1000000);\n\t\t\t\t\t},\n\n\t\t\t\t\t// func walltime() (sec int64, nsec int32)\n\t\t\t\t\t\"runtime.walltime\": (sp) => {\n\t\t\t\t\t\tconst msec = (new Date).getTime();\n\t\t\t\t\t\tsetInt64(sp + 8, msec / 1000);\n\t\t\t\t\t\tmem().setInt32(sp + 16, (msec % 1000) * 1000000, true);\n\t\t\t\t\t},\n\n\t\t\t\t\t// func scheduleCallback(delay int64) int32\n\t\t\t\t\t\"runtime.scheduleCallback\": (sp) => {\n\t\t\t\t\t\tconst id = this._nextCallbackTimeoutID;\n\t\t\t\t\t\tthis._nextCallbackTimeoutID++;\n\t\t\t\t\t\tthis._callbackTimeouts.set(id, setTimeout(\n\t\t\t\t\t\t\t() => { this._resolveCallbackPromise(); },\n\t\t\t\t\t\t\tgetInt64(sp + 8) + 1, // setTimeout has been seen to fire up to 1 millisecond early\n\t\t\t\t\t\t));\n\t\t\t\t\t\tmem().setInt32(sp + 16, id, true);\n\t\t\t\t\t},\n\n\t\t\t\t\t// func clearScheduledCallback(id int32)\n\t\t\t\t\t\"runtime.clearScheduledCallback\": (sp) => {\n\t\t\t\t\t\tconst id = mem().getInt32(sp + 8, true);\n\t\t\t\t\t\tclearTimeout(this._callbackTimeouts.get(id));\n\t\t\t\t\t\tthis._callbackTimeouts.delete(id);\n\t\t\t\t\t},\n\n\t\t\t\t\t// func getRandomData(r []byte)\n\t\t\t\t\t\"runtime.getRandomData\": (sp) => {\n\t\t\t\t\t\tcrypto.getRandomValues(loadSlice(sp + 8));\n\t\t\t\t\t},\n\n\t\t\t\t\t// func stringVal(value string) ref\n\t\t\t\t\t\"syscall/js.stringVal\": (sp) => {\n\t\t\t\t\t\tstoreValue(sp + 24, loadString(sp + 8));\n\t\t\t\t\t},\n\n\t\t\t\t\t// func valueGet(v ref, p string) ref\n\t\t\t\t\t\"syscall/js.valueGet\": (sp) => {\n\t\t\t\t\t\tstoreValue(sp + 32, Reflect.get(loadValue(sp + 8), loadString(sp + 16)));\n\t\t\t\t\t},\n\n\t\t\t\t\t// func valueSet(v ref, p string, x ref)\n\t\t\t\t\t\"syscall/js.valueSet\": (sp) => {\n\t\t\t\t\t\tReflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32));\n\t\t\t\t\t},\n\n\t\t\t\t\t// func valueIndex(v ref, i int) ref\n\t\t\t\t\t\"syscall/js.valueIndex\": (sp) => {\n\t\t\t\t\t\tstoreValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16)));\n\t\t\t\t\t},\n\n\t\t\t\t\t// valueSetIndex(v ref, i int, x ref)\n\t\t\t\t\t\"syscall/js.valueSetIndex\": (sp) => {\n\t\t\t\t\t\tReflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24));\n\t\t\t\t\t},\n\n\t\t\t\t\t// func valueCall(v ref, m string, args []ref) (ref, bool)\n\t\t\t\t\t\"syscall/js.valueCall\": (sp) => {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst v = loadValue(sp + 8);\n\t\t\t\t\t\t\tconst m = Reflect.get(v, loadString(sp + 16));\n\t\t\t\t\t\t\tconst args = loadSliceOfValues(sp + 32);\n\t\t\t\t\t\t\tstoreValue(sp + 56, Reflect.apply(m, v, args));\n\t\t\t\t\t\t\tmem().setUint8(sp + 64, 1);\n\t\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\t\tstoreValue(sp + 56, err);\n\t\t\t\t\t\t\tmem().setUint8(sp + 64, 0);\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\n\t\t\t\t\t// func valueInvoke(v ref, args []ref) (ref, bool)\n\t\t\t\t\t\"syscall/js.valueInvoke\": (sp) => {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst v = loadValue(sp + 8);\n\t\t\t\t\t\t\tconst args = loadSliceOfValues(sp + 16);\n\t\t\t\t\t\t\tstoreValue(sp + 40, Reflect.apply(v, undefined, args));\n\t\t\t\t\t\t\tmem().setUint8(sp + 48, 1);\n\t\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\t\tstoreValue(sp + 40, err);\n\t\t\t\t\t\t\tmem().setUint8(sp + 48, 0);\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\n\t\t\t\t\t// func valueNew(v ref, args []ref) (ref, bool)\n\t\t\t\t\t\"syscall/js.valueNew\": (sp) => {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst v = loadValue(sp + 8);\n\t\t\t\t\t\t\tconst args = loadSliceOfValues(sp + 16);\n\t\t\t\t\t\t\tstoreValue(sp + 40, Reflect.construct(v, args));\n\t\t\t\t\t\t\tmem().setUint8(sp + 48, 1);\n\t\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\t\tstoreValue(sp + 40, err);\n\t\t\t\t\t\t\tmem().setUint8(sp + 48, 0);\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\n\t\t\t\t\t// func valueLength(v ref) int\n\t\t\t\t\t\"syscall/js.valueLength\": (sp) => {\n\t\t\t\t\t\tsetInt64(sp + 16, parseInt(loadValue(sp + 8).length));\n\t\t\t\t\t},\n\n\t\t\t\t\t// valuePrepareString(v ref) (ref, int)\n\t\t\t\t\t\"syscall/js.valuePrepareString\": (sp) => {\n\t\t\t\t\t\tconst str = encoder.encode(String(loadValue(sp + 8)));\n\t\t\t\t\t\tstoreValue(sp + 16, str);\n\t\t\t\t\t\tsetInt64(sp + 24, str.length);\n\t\t\t\t\t},\n\n\t\t\t\t\t// valueLoadString(v ref, b []byte)\n\t\t\t\t\t\"syscall/js.valueLoadString\": (sp) => {\n\t\t\t\t\t\tconst str = loadValue(sp + 8);\n\t\t\t\t\t\tloadSlice(sp + 16).set(str);\n\t\t\t\t\t},\n\n\t\t\t\t\t// func valueInstanceOf(v ref, t ref) bool\n\t\t\t\t\t\"syscall/js.valueInstanceOf\": (sp) => {\n\t\t\t\t\t\tmem().setUint8(sp + 24, loadValue(sp + 8) instanceof loadValue(sp + 16));\n\t\t\t\t\t},\n\n\t\t\t\t\t\"debug\": (value) => {\n\t\t\t\t\t\tconsole.log(value);\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\n\t\tasync run(instance) {\n\t\t\tthis._inst = instance;\n\t\t\tthis._values = [ // TODO: garbage collection\n\t\t\t\tNaN,\n\t\t\t\tundefined,\n\t\t\t\tnull,\n\t\t\t\ttrue,\n\t\t\t\tfalse,\n\t\t\t\tglobal,\n\t\t\t\tthis._inst.exports.mem,\n\t\t\t\t() => { // resolveCallbackPromise\n\t\t\t\t\tif (this.exited) {\n\t\t\t\t\t\tthrow new Error(\"bad callback: Go program has already exited\");\n\t\t\t\t\t}\n\t\t\t\t\tsetTimeout(this._resolveCallbackPromise, 0); // make sure it is asynchronous\n\t\t\t\t},\n\t\t\t];\n\t\t\tthis._stringRefs = new Map();\n\t\t\tthis._symbolRefs = new Map();\n\t\t\tthis._refProp = Symbol();\n\t\t\tthis.exited = false;\n\n\t\t\tconst mem = new DataView(this._inst.exports.mem.buffer)\n\n\t\t\t// Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory.\n\t\t\tlet offset = 4096;\n\n\t\t\tconst strPtr = (str) => {\n\t\t\t\tlet ptr = offset;\n\t\t\t\tnew Uint8Array(mem.buffer, offset, str.length + 1).set(encoder.encode(str + \"\\0\"));\n\t\t\t\toffset += str.length + (8 - (str.length % 8));\n\t\t\t\treturn ptr;\n\t\t\t};\n\n\t\t\tconst argc = this.argv.length;\n\n\t\t\tconst argvPtrs = [];\n\t\t\tthis.argv.forEach((arg) => {\n\t\t\t\targvPtrs.push(strPtr(arg));\n\t\t\t});\n\n\t\t\tconst keys = Object.keys(this.env).sort();\n\t\t\targvPtrs.push(keys.length);\n\t\t\tkeys.forEach((key) => {\n\t\t\t\targvPtrs.push(strPtr(`${key}=${this.env[key]}`));\n\t\t\t});\n\n\t\t\tconst argv = offset;\n\t\t\targvPtrs.forEach((ptr) => {\n\t\t\t\tmem.setUint32(offset, ptr, true);\n\t\t\t\tmem.setUint32(offset + 4, 0, true);\n\t\t\t\toffset += 8;\n\t\t\t});\n\n\t\t\twhile (true) {\n\t\t\t\tconst callbackPromise = new Promise((resolve) => {\n\t\t\t\t\tthis._resolveCallbackPromise = resolve;\n\t\t\t\t});\n\t\t\t\tthis._inst.exports.run(argc, argv);\n\t\t\t\tif (this.exited) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tawait callbackPromise;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (isNodeJS) {\n\t\tif (process.argv.length < 3) {\n\t\t\tprocess.stderr.write(\"usage: go_js_wasm_exec [wasm binary] [arguments]\\n\");\n\t\t\tprocess.exit(1);\n\t\t}\n\n\t\tconst go = new Go();\n\t\tgo.argv = process.argv.slice(2);\n\t\tgo.env = process.env;\n\t\tgo.exit = process.exit;\n\t\tWebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => {\n\t\t\tprocess.on(\"exit\", () => { // Node.js exits if no callback is pending\n\t\t\t\tif (!go.exited) {\n\t\t\t\t\tconsole.error(\"error: all goroutines asleep and no JavaScript callback pending - deadlock!\");\n\t\t\t\t\tprocess.exit(1);\n\t\t\t\t}\n\t\t\t});\n\t\t\treturn go.run(result.instance);\n\t\t}).catch((err) => {\n\t\t\tconsole.error(err);\n\t\t\tgo.exited = true;\n\t\t\tprocess.exit(1);\n\t\t});\n\t}\n})();\n"
  },
  {
    "path": "_examples/webassembly/client/hello.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>Hello Webassembly + Iris (Go)</title>\n</head>\n<body>\n  <div id=\"hello\"></div>\n  <script type=\"module\" src=\"main.js\"></script>\n</body>\n</html>"
  },
  {
    "path": "_examples/webassembly/client/hello_go116.go",
    "content": "//go:build js\n// +build js\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"syscall/js\"\n\t\"time\"\n)\n\nfunc main() {\n\t// GOARCH=wasm GOOS=js /home/$yourusername/go1.16/bin/go build -mod=mod -o hello.wasm hello_go116.go\n\tjs.Global().Get(\"console\").Call(\"log\", \"Hello Webassembly!\")\n\tmessage := fmt.Sprintf(\"Hello, the current time is: %s\", time.Now().String())\n\tjs.Global().Get(\"document\").Call(\"getElementById\", \"hello\").Set(\"innerText\", message)\n}\n"
  },
  {
    "path": "_examples/webassembly/client/main.js",
    "content": "import './go-wasm-runtime.js';\n\nif (!WebAssembly.instantiateStreaming) { // polyfill\n  WebAssembly.instantiateStreaming = async (resp, importObject) => {\n    const source = await (await resp).arrayBuffer();\n    return await WebAssembly.instantiate(source, importObject);\n  };\n}\n\nconst go = new Go();\nWebAssembly.instantiateStreaming(fetch(\"hello.wasm\"), go.importObject).then((result) => {\n    return WebAssembly.instantiate(result.module, go.importObject);\n}).then(instance => go.run(instance));"
  },
  {
    "path": "_examples/webassembly/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n)\n\n/*\nYou need to build the hello.wasm first, download the go1.16 and execute the below command:\n$ cd client && GOARCH=wasm GOOS=js /home/$yourname/go1.16/bin/go build -mod=mod -o hello.wasm hello_go116.go\n*/\n\nfunc main() {\n\tapp := iris.New()\n\n\t// we could serve your assets like this the sake of the example,\n\t// never include the .go files there in production.\n\tapp.HandleDir(\"/\", iris.Dir(\"./client\"))\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\t// ctx.CompressWriter(true)\n\t\tctx.ServeFile(\"./client/hello.html\")\n\t})\n\n\t// visit http://localhost:8080\n\t// you should get an html output like this:\n\t// Hello, the current time is: 2018-07-09 05:54:12.564 +0000 UTC m=+0.003900161\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/websocket/README.md",
    "content": "# Websocket\n\n[WebSocket](https://wikipedia.org/wiki/WebSocket) is a protocol that enables two-way persistent communication channels over TCP connections. It is used for applications such as chat, stock tickers, games, anywhere you want real-time functionality in a web application.\n\nIris websocket library is now merged with the [neffos real-time framework](https://github.com/kataras/neffos) and Iris-specific helpers and type aliases live on the [iris/websocket](https://github.com/kataras/iris/tree/main/websocket) subpackage. Learn neffos from its [wiki](https://github.com/kataras/neffos#learning-neffos).\n\nHelpers and type aliases improves your code speed when writing a websocket module.\nFor example, instead of importing both `kataras/iris/websocket` - in order to use its `websocket.Handler` - and `github.com/kataras/neffos` - to create a new websocket server `neffos.New` - you can use the `websocket.New` instead, another example is the `neffos.Conn` which can be declared as `websocket.Conn`.\n\nAll neffos and its subpackage's types and package-level functions exist as type aliases on the `kataras/iris/websocket` package too, there are too many of those and there is no need to write each one of those here, some common types: \n\n- `github.com/kataras/neffos/#Conn`  -> `github.com/kataras/iris/websocket/#Conn`\n- `github.com/kataras/neffos/gorilla/#DefaultUpgrader` ->  `github.com/kataras/iris/websocket/#DefaultGorillaUpgrader`\n- `github.com/kataras/neffos/stackexchange/redis/#NewStackExchange` ->  `github.com/kataras/iris/websocket/#NewRedisStackExchange`\n"
  },
  {
    "path": "_examples/websocket/basic/README.md",
    "content": "# Basic Example\n\nAt the end of this example you will be able to run a websocket server\nand clients for all platforms (Go, Browser and Nodejs).\n\n![](overview.png)\n\nOpen as many clients as you want to try out and start typing.\n\nThis example contains only the basics, however, the library supports rooms, native websocket messages, any data can be sent and received (i.e protobufs, json) and all kinds of broadcasting and connections collections.\n\n## How to run\n\n### Server\n\nOpen a terminal window instance and execute:\n\n```sh\n$ go mod tidy -compat=1.17\n$ go run server.go # start the websocket server.\n```\n\n### Client (Go)\n\nStart a new terminal instance and execute:\n\n```sh\n$ cd ./go-client\n$ go mod tidy -compat=1.17\n$ go run client.go # start the websocket client.\n# start typing...\n```\n\n### Client (Browser)\n\nNavigate to <http://localhost:8080> and start typing.\nThe `./browser/index.html` should be served, it contains the client-side code.\n\n### Client (Browserify)\n\nInstall [NPM](https://nodejs.org) first, then start a new terminal instance and execute:\n\n```sh\n$ cd ./browserify\n$ npm install\n# build the modern browser-side client:\n# embed the neffos.js node-module and app.js\n# into a single ./browserify/bundle.js file\n# which ./browserify/client.html imports.\n$ npm run-script build\n```\n\nNavigate to <http://localhost:8080/browserify/client.html> and start typing.\n\n### Client (Nodejs)\n\nInstall [NPM](https://nodejs.org) if you haven't already and then, start a new terminal instance and execute:\n\n```sh\n$ cd nodejs-client\n$ npm install\n$ node client.js # start the websocket client.\n# start typing.\n```\n"
  },
  {
    "path": "_examples/websocket/basic/browser/index.html",
    "content": "<!-- the message's input -->\n<input id=\"input\" type=\"text\" />\n\n<!-- when clicked then a websocket event will be sent to the server, at this example we registered the 'chat' -->\n<button id=\"sendBtn\" disabled>Send</button>\n\n<!-- the messages will be shown here -->\n<pre id=\"output\"></pre>\n<!-- import the iris client-side library for browser from a CDN or locally.\n     However, `neffos.(min.)js` is a NPM package too so alternatively,\n     you can use it as dependency on your package.json and all nodejs-npm tooling become available:\n     see the \"browserify\" example for more-->\n<script src=\"https://cdn.jsdelivr.net/npm/neffos.js@0.1.27/dist/neffos.min.js\"></script>\n<script>\n    // `neffos` global variable is available now.\n    var scheme = document.location.protocol == \"https:\" ? \"wss\" : \"ws\";\n    var port = document.location.port ? \":\" + document.location.port : \"\";\n    var wsURL = scheme + \"://\" + document.location.hostname + port + \"/echo\";\n\n    const enableJWT = true;\n    if (enableJWT) {\n        // This is just a signature and a payload of an example content, \n        // please replace this with your logic.\n        //\n        // Add a random letter in front of the token to make it\n        // invalid and see that this client is not allowed to dial the websocket server.\n        const token = \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjozMjEzMjF9.8waEX7-vPKACa-Soi1pQvW3Rl8QY-SUFcHKTLZI4mvU\";\n        wsURL += \"?token=\" + token;\n    }\n\n    var outputTxt = document.getElementById(\"output\");\n    function addMessage(msg) {\n        outputTxt.innerHTML += msg + \"\\n\";\n    }\n\n    function handleError(reason) {\n        console.log(reason);\n        window.alert(\"error: see the dev console\");\n    }\n\n    function handleNamespaceConnectedConn(nsConn) {\n        nsConn.emit(\"Hello from browser client side!\");\n\n        let inputTxt = document.getElementById(\"input\");\n        let sendBtn = document.getElementById(\"sendBtn\");\n\n        sendBtn.disabled = false;\n        sendBtn.onclick = function () {\n            const input = inputTxt.value;\n            inputTxt.value = \"\";\n            nsConn.emit(\"chat\", input);\n            addMessage(\"Me: \" + input);\n        };\n    }\n\n    const username = window.prompt(\"Your username?\");\n\n    async function runExample() {\n        // You can omit the \"default\" and simply define only Events, the namespace will be an empty string\"\",\n        // however if you decide to make any changes on this example make sure the changes are reflecting inside the ../server.go file as well.\n        try {\n            const conn = await neffos.dial(wsURL, {\n                default: { // \"default\" namespace.\n                    _OnNamespaceConnected: function (nsConn, msg) {\n                        addMessage(\"connected to namespace: \" + msg.Namespace);\n                        handleNamespaceConnectedConn(nsConn)\n                    },\n                    _OnNamespaceDisconnect: function (nsConn, msg) {\n                        addMessage(\"disconnected from namespace: \" + msg.Namespace);\n                    },\n                    chat: function (nsConn, msg) { // \"chat\" event.\n                        addMessage(msg.Body);\n                    }\n                }\n            },{\n                headers: {\n                    \"X-Username\": username,\n                }\n            });\n\n            // You can either wait to conenct or just conn.connect(\"connect\")\n            // and put the `handleNamespaceConnectedConn` inside `_OnNamespaceConnected` callback instead.\n            // const nsConn = await conn.connect(\"default\");\n            // nsConn.emit(...); handleNamespaceConnectedConn(nsConn);\n            conn.connect(\"default\");\n\n        } catch (err) {\n            handleError(err);\n        }\n    }\n\n    runExample();\n\n    // If \"await\" and \"async\" are available, use them instead^, all modern browsers support those,\n    // all of the javascript examples will be written using async/await method instead of promise then/catch callbacks.\n    // A usage example of promise then/catch follows:\n    // neffos.dial(wsURL, {\n    //     default: { // \"default\" namespace.\n    //         _OnNamespaceConnected: function (ns, msg) {\n    //             addMessage(\"connected to namespace: \" + msg.Namespace);\n    //         },\n    //         _OnNamespaceDisconnect: function (ns, msg) {\n    //             addMessage(\"disconnected from namespace: \" + msg.Namespace);\n    //         },\n    //         chat: function (ns, msg) { // \"chat\" event.\n    //             addMessage(msg.Body);\n    //         }\n    //     }\n    // }).then(function (conn) {\n    //     conn.connect(\"default\").then(handleNamespaceConnectedConn).catch(handleError);\n    // }).catch(handleError);\n</script>"
  },
  {
    "path": "_examples/websocket/basic/browserify/app.js",
    "content": "const neffos = require('neffos.js');\n\nvar scheme = document.location.protocol == \"https:\" ? \"wss\" : \"ws\";\nvar port = document.location.port ? \":\" + document.location.port : \"\";\n\nvar wsURL = scheme + \"://\" + document.location.hostname + port + \"/echo\";\n\nconst enableJWT = true;\nif (enableJWT) {\n  // This is just a signature and a payload of an example content, \n  // please replace this with your logic.\n  //\n  // Add a random letter in front of the token to make it\n  // invalid and see that this client is not allowed to dial the websocket server.\n  const token = \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjozMjEzMjF9.8waEX7-vPKACa-Soi1pQvW3Rl8QY-SUFcHKTLZI4mvU\";\n  wsURL += \"?token=\" + token;\n}\n\nvar outputTxt = document.getElementById(\"output\");\n\nfunction addMessage(msg) {\n  outputTxt.innerHTML += msg + \"\\n\";\n}\n\nfunction handleError(reason) {\n  console.log(reason);\n  window.alert(reason);\n}\n\nfunction handleNamespaceConnectedConn(nsConn) {\n  nsConn.emit(\"chat\", \"Hello from browser(ify) client-side!\");\n\n  const inputTxt = document.getElementById(\"input\");\n  const sendBtn = document.getElementById(\"sendBtn\");\n\n  sendBtn.disabled = false;\n  sendBtn.onclick = function () {\n    const input = inputTxt.value;\n    inputTxt.value = \"\";\n\n    nsConn.emit(\"chat\", input);\n    addMessage(\"Me: \" + input);\n  };\n}\n\nasync function runExample() {\n  try {\n    const conn = await neffos.dial(wsURL, {\n      default: { // \"default\" namespace.\n        _OnNamespaceConnected: function (nsConn, msg) {\n          addMessage(\"connected to namespace: \" + msg.Namespace);\n          handleNamespaceConnectedConn(nsConn);\n        },\n        _OnNamespaceDisconnect: function (nsConn, msg) {\n          addMessage(\"disconnected from namespace: \" + msg.Namespace);\n        },\n        chat: function (nsConn, msg) { // \"chat\" event.\n          addMessage(msg.Body);\n        }\n      }\n    });\n\n    // You can either wait to conenct or just conn.connect(\"connect\")\n    // and put the `handleNamespaceConnectedConn` inside `_OnNamespaceConnected` callback instead.\n    // const nsConn = await conn.connect(\"default\");\n    // handleNamespaceConnectedConn(nsConn);\n    // nsConn.emit(...); handleNamespaceConnectedConn(nsConn);\n    conn.connect(\"default\");\n\n  } catch (err) {\n    handleError(err);\n  }\n}\n\nrunExample();"
  },
  {
    "path": "_examples/websocket/basic/browserify/bundle.js",
    "content": "(function(){function d(s,e,n){function f(u,i){if(!e[u]){if(!s[u]){var t=\"function\"==typeof require&&require;if(!i&&t)return t(u,!0);if(o)return o(u,!0);var r=new Error(\"Cannot find module '\"+u+\"'\");throw r.code=\"MODULE_NOT_FOUND\",r}var a=e[u]={exports:{}};s[u][0].call(a.exports,function(e){var d=s[u][1][e];return f(d||e)},a,a.exports,d,s,e,n)}return e[u].exports}for(var o=\"function\"==typeof require&&require,u=0;u<n.length;u++)f(n[u]);return f}return d})()({1:[function(e){function n(e){t.innerHTML+=e+\"\\n\"}function d(e){console.log(e),window.alert(e)}function i(e){e.emit(\"chat\",\"Hello from browser(ify) client-side!\");const d=document.getElementById(\"input\"),i=document.getElementById(\"sendBtn\");i.disabled=!1,i.onclick=function(){const i=d.value;d.value=\"\",e.emit(\"chat\",i),n(\"Me: \"+i)}}async function f(){try{const e=await u.dial(a,{default:{_OnNamespaceConnected:function(e,d){n(\"connected to namespace: \"+d.Namespace),i(e)},_OnNamespaceDisconnect:function(e,d){n(\"disconnected from namespace: \"+d.Namespace)},chat:function(e,d){n(d.Body)}}});e.connect(\"default\")}catch(e){d(e)}}const u=e(\"neffos.js\");var o=\"https:\"==document.location.protocol?\"wss\":\"ws\",s=document.location.port?\":\"+document.location.port:\"\",a=o+\"://\"+document.location.hostname+s+\"/echo\";{a+=\"?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjozMjEzMjF9.8waEX7-vPKACa-Soi1pQvW3Rl8QY-SUFcHKTLZI4mvU\"}var t=document.getElementById(\"output\");f()},{\"neffos.js\":5}],2:[function(e,n){var d=e(\"./lib/encoding.js\");n.exports={TextEncoder:d.TextEncoder,TextDecoder:d.TextDecoder}},{\"./lib/encoding.js\":4}],3:[function(e,n){(function(e){'use strict';\"undefined\"!=typeof n&&n.exports&&(n.exports=e),e[\"encoding-indexes\"]={big5:[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,17392,19506,17923,17830,17784,160359,19831,17843,162993,19682,163013,15253,18230,18244,19527,19520,148159,144919,160594,159371,159954,19543,172881,18255,17882,19589,162924,19719,19108,18081,158499,29221,154196,137827,146950,147297,26189,22267,null,32149,22813,166841,15860,38708,162799,23515,138590,23204,13861,171696,23249,23479,23804,26478,34195,170309,29793,29853,14453,138579,145054,155681,16108,153822,15093,31484,40855,147809,166157,143850,133770,143966,17162,33924,40854,37935,18736,34323,22678,38730,37400,31184,31282,26208,27177,34973,29772,31685,26498,31276,21071,36934,13542,29636,155065,29894,40903,22451,18735,21580,16689,145038,22552,31346,162661,35727,18094,159368,16769,155033,31662,140476,40904,140481,140489,140492,40905,34052,144827,16564,40906,17633,175615,25281,28782,40907,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,12736,12737,12738,12739,12740,131340,12741,131281,131277,12742,12743,131275,139240,12744,131274,12745,12746,12747,12748,131342,12749,12750,256,193,461,192,274,201,282,200,332,211,465,210,null,7870,null,7872,202,257,225,462,224,593,275,233,283,232,299,237,464,236,333,243,466,242,363,250,468,249,470,472,474,476,252,null,7871,null,7873,234,609,9178,9179,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,172969,135493,null,25866,null,null,20029,28381,40270,37343,null,null,161589,25745,20250,20264,20392,20822,20852,20892,20964,21153,21160,21307,21326,21457,21464,22242,22768,22788,22791,22834,22836,23398,23454,23455,23706,24198,24635,25993,26622,26628,26725,27982,28860,30005,32420,32428,32442,32455,32463,32479,32518,32567,33402,33487,33647,35270,35774,35810,36710,36711,36718,29713,31996,32205,26950,31433,21031,null,null,null,null,37260,30904,37214,32956,null,36107,33014,133607,null,null,32927,40647,19661,40393,40460,19518,171510,159758,40458,172339,13761,null,28314,33342,29977,null,18705,39532,39567,40857,31111,164972,138698,132560,142054,20004,20097,20096,20103,20159,20203,20279,13388,20413,15944,20483,20616,13437,13459,13477,20870,22789,20955,20988,20997,20105,21113,21136,21287,13767,21417,13649,21424,13651,21442,21539,13677,13682,13953,21651,21667,21684,21689,21712,21743,21784,21795,21800,13720,21823,13733,13759,21975,13765,163204,21797,null,134210,134421,151851,21904,142534,14828,131905,36422,150968,169189,16467,164030,30586,142392,14900,18389,164189,158194,151018,25821,134524,135092,134357,135412,25741,36478,134806,134155,135012,142505,164438,148691,null,134470,170573,164073,18420,151207,142530,39602,14951,169460,16365,13574,152263,169940,161992,142660,40302,38933,null,17369,155813,25780,21731,142668,142282,135287,14843,135279,157402,157462,162208,25834,151634,134211,36456,139681,166732,132913,null,18443,131497,16378,22643,142733,null,148936,132348,155799,134988,134550,21881,16571,17338,null,19124,141926,135325,33194,39157,134556,25465,14846,141173,36288,22177,25724,15939,null,173569,134665,142031,142537,null,135368,145858,14738,14854,164507,13688,155209,139463,22098,134961,142514,169760,13500,27709,151099,null,null,161140,142987,139784,173659,167117,134778,134196,157724,32659,135375,141315,141625,13819,152035,134796,135053,134826,16275,134960,134471,135503,134732,null,134827,134057,134472,135360,135485,16377,140950,25650,135085,144372,161337,142286,134526,134527,142417,142421,14872,134808,135367,134958,173618,158544,167122,167321,167114,38314,21708,33476,21945,null,171715,39974,39606,161630,142830,28992,33133,33004,23580,157042,33076,14231,21343,164029,37302,134906,134671,134775,134907,13789,151019,13833,134358,22191,141237,135369,134672,134776,135288,135496,164359,136277,134777,151120,142756,23124,135197,135198,135413,135414,22428,134673,161428,164557,135093,134779,151934,14083,135094,135552,152280,172733,149978,137274,147831,164476,22681,21096,13850,153405,31666,23400,18432,19244,40743,18919,39967,39821,154484,143677,22011,13810,22153,20008,22786,138177,194680,38737,131206,20059,20155,13630,23587,24401,24516,14586,25164,25909,27514,27701,27706,28780,29227,20012,29357,149737,32594,31035,31993,32595,156266,13505,null,156491,32770,32896,157202,158033,21341,34916,35265,161970,35744,36125,38021,38264,38271,38376,167439,38886,39029,39118,39134,39267,17e4,40060,40479,40644,27503,63751,20023,131207,38429,25143,38050,null,20539,28158,171123,40870,15817,34959,147790,28791,23797,19232,152013,13657,154928,24866,166450,36775,37366,29073,26393,29626,144001,172295,15499,137600,19216,30948,29698,20910,165647,16393,27235,172730,16931,34319,133743,31274,170311,166634,38741,28749,21284,139390,37876,30425,166371,40871,30685,20131,20464,20668,20015,20247,40872,21556,32139,22674,22736,138678,24210,24217,24514,141074,25995,144377,26905,27203,146531,27903,null,29184,148741,29580,16091,150035,23317,29881,35715,154788,153237,31379,31724,31939,32364,33528,34199,40873,34960,40874,36537,40875,36815,34143,39392,37409,40876,167353,136255,16497,17058,23066,null,null,null,39016,26475,17014,22333,null,34262,149883,33471,160013,19585,159092,23931,158485,159678,40877,40878,23446,40879,26343,32347,28247,31178,15752,17603,143958,141206,17306,17718,null,23765,146202,35577,23672,15634,144721,23928,40882,29015,17752,147692,138787,19575,14712,13386,131492,158785,35532,20404,131641,22975,33132,38998,170234,24379,134047,null,139713,166253,16642,18107,168057,16135,40883,172469,16632,14294,18167,158790,16764,165554,160767,17773,14548,152730,17761,17691,19849,19579,19830,17898,16328,150287,13921,17630,17597,16877,23870,23880,23894,15868,14351,23972,23993,14368,14392,24130,24253,24357,24451,14600,14612,14655,14669,24791,24893,23781,14729,25015,25017,25039,14776,25132,25232,25317,25368,14840,22193,14851,25570,25595,25607,25690,14923,25792,23829,22049,40863,14999,25990,15037,26111,26195,15090,26258,15138,26390,15170,26532,26624,15192,26698,26756,15218,15217,15227,26889,26947,29276,26980,27039,27013,15292,27094,15325,27237,27252,27249,27266,15340,27289,15346,27307,27317,27348,27382,27521,27585,27626,27765,27818,15563,27906,27910,27942,28033,15599,28068,28081,28181,28184,28201,28294,166336,28347,28386,28378,40831,28392,28393,28452,28468,15686,147265,28545,28606,15722,15733,29111,23705,15754,28716,15761,28752,28756,28783,28799,28809,131877,17345,13809,134872,147159,22462,159443,28990,153568,13902,27042,166889,23412,31305,153825,169177,31333,31357,154028,31419,31408,31426,31427,29137,156813,16842,31450,31453,31466,16879,21682,154625,31499,31573,31529,152334,154878,31650,31599,33692,154548,158847,31696,33825,31634,31672,154912,15789,154725,33938,31738,31750,31797,154817,31812,31875,149634,31910,26237,148856,31945,31943,31974,31860,31987,31989,31950,32359,17693,159300,32093,159446,29837,32137,32171,28981,32179,32210,147543,155689,32228,15635,32245,137209,32229,164717,32285,155937,155994,32366,32402,17195,37996,32295,32576,32577,32583,31030,156368,39393,32663,156497,32675,136801,131176,17756,145254,17667,164666,32762,156809,32773,32776,32797,32808,32815,172167,158915,32827,32828,32865,141076,18825,157222,146915,157416,26405,32935,166472,33031,33050,22704,141046,27775,156824,151480,25831,136330,33304,137310,27219,150117,150165,17530,33321,133901,158290,146814,20473,136445,34018,33634,158474,149927,144688,137075,146936,33450,26907,194964,16859,34123,33488,33562,134678,137140,14017,143741,144730,33403,33506,33560,147083,159139,158469,158615,144846,15807,33565,21996,33669,17675,159141,33708,33729,33747,13438,159444,27223,34138,13462,159298,143087,33880,154596,33905,15827,17636,27303,33866,146613,31064,33960,158614,159351,159299,34014,33807,33681,17568,33939,34020,154769,16960,154816,17731,34100,23282,159385,17703,34163,17686,26559,34326,165413,165435,34241,159880,34306,136578,159949,194994,17770,34344,13896,137378,21495,160666,34430,34673,172280,34798,142375,34737,34778,34831,22113,34412,26710,17935,34885,34886,161248,146873,161252,34910,34972,18011,34996,34997,25537,35013,30583,161551,35207,35210,35238,35241,35239,35260,166437,35303,162084,162493,35484,30611,37374,35472,162393,31465,162618,147343,18195,162616,29052,35596,35615,152624,152933,35647,35660,35661,35497,150138,35728,35739,35503,136927,17941,34895,35995,163156,163215,195028,14117,163155,36054,163224,163261,36114,36099,137488,36059,28764,36113,150729,16080,36215,36265,163842,135188,149898,15228,164284,160012,31463,36525,36534,36547,37588,36633,36653,164709,164882,36773,37635,172703,133712,36787,18730,166366,165181,146875,24312,143970,36857,172052,165564,165121,140069,14720,159447,36919,165180,162494,36961,165228,165387,37032,165651,37060,165606,37038,37117,37223,15088,37289,37316,31916,166195,138889,37390,27807,37441,37474,153017,37561,166598,146587,166668,153051,134449,37676,37739,166625,166891,28815,23235,166626,166629,18789,37444,166892,166969,166911,37747,37979,36540,38277,38310,37926,38304,28662,17081,140922,165592,135804,146990,18911,27676,38523,38550,16748,38563,159445,25050,38582,30965,166624,38589,21452,18849,158904,131700,156688,168111,168165,150225,137493,144138,38705,34370,38710,18959,17725,17797,150249,28789,23361,38683,38748,168405,38743,23370,168427,38751,37925,20688,143543,143548,38793,38815,38833,38846,38848,38866,38880,152684,38894,29724,169011,38911,38901,168989,162170,19153,38964,38963,38987,39014,15118,160117,15697,132656,147804,153350,39114,39095,39112,39111,19199,159015,136915,21936,39137,39142,39148,37752,39225,150057,19314,170071,170245,39413,39436,39483,39440,39512,153381,14020,168113,170965,39648,39650,170757,39668,19470,39700,39725,165376,20532,39732,158120,14531,143485,39760,39744,171326,23109,137315,39822,148043,39938,39935,39948,171624,40404,171959,172434,172459,172257,172323,172511,40318,40323,172340,40462,26760,40388,139611,172435,172576,137531,172595,40249,172217,172724,40592,40597,40606,40610,19764,40618,40623,148324,40641,15200,14821,15645,20274,14270,166955,40706,40712,19350,37924,159138,40727,40726,40761,22175,22154,40773,39352,168075,38898,33919,40802,40809,31452,40846,29206,19390,149877,149947,29047,150008,148296,150097,29598,166874,137466,31135,166270,167478,37737,37875,166468,37612,37761,37835,166252,148665,29207,16107,30578,31299,28880,148595,148472,29054,137199,28835,137406,144793,16071,137349,152623,137208,14114,136955,137273,14049,137076,137425,155467,14115,136896,22363,150053,136190,135848,136134,136374,34051,145062,34051,33877,149908,160101,146993,152924,147195,159826,17652,145134,170397,159526,26617,14131,15381,15847,22636,137506,26640,16471,145215,147681,147595,147727,158753,21707,22174,157361,22162,135135,134056,134669,37830,166675,37788,20216,20779,14361,148534,20156,132197,131967,20299,20362,153169,23144,131499,132043,14745,131850,132116,13365,20265,131776,167603,131701,35546,131596,20120,20685,20749,20386,20227,150030,147082,20290,20526,20588,20609,20428,20453,20568,20732,20825,20827,20829,20830,28278,144789,147001,147135,28018,137348,147081,20904,20931,132576,17629,132259,132242,132241,36218,166556,132878,21081,21156,133235,21217,37742,18042,29068,148364,134176,149932,135396,27089,134685,29817,16094,29849,29716,29782,29592,19342,150204,147597,21456,13700,29199,147657,21940,131909,21709,134086,22301,37469,38644,37734,22493,22413,22399,13886,22731,23193,166470,136954,137071,136976,23084,22968,37519,23166,23247,23058,153926,137715,137313,148117,14069,27909,29763,23073,155267,23169,166871,132115,37856,29836,135939,28933,18802,37896,166395,37821,14240,23582,23710,24158,24136,137622,137596,146158,24269,23375,137475,137476,14081,137376,14045,136958,14035,33066,166471,138682,144498,166312,24332,24334,137511,137131,23147,137019,23364,34324,161277,34912,24702,141408,140843,24539,16056,140719,140734,168072,159603,25024,131134,131142,140827,24985,24984,24693,142491,142599,149204,168269,25713,149093,142186,14889,142114,144464,170218,142968,25399,173147,25782,25393,25553,149987,142695,25252,142497,25659,25963,26994,15348,143502,144045,149897,144043,21773,144096,137433,169023,26318,144009,143795,15072,16784,152964,166690,152975,136956,152923,152613,30958,143619,137258,143924,13412,143887,143746,148169,26254,159012,26219,19347,26160,161904,138731,26211,144082,144097,26142,153714,14545,145466,145340,15257,145314,144382,29904,15254,26511,149034,26806,26654,15300,27326,14435,145365,148615,27187,27218,27337,27397,137490,25873,26776,27212,15319,27258,27479,147392,146586,37792,37618,166890,166603,37513,163870,166364,37991,28069,28427,149996,28007,147327,15759,28164,147516,23101,28170,22599,27940,30786,28987,148250,148086,28913,29264,29319,29332,149391,149285,20857,150180,132587,29818,147192,144991,150090,149783,155617,16134,16049,150239,166947,147253,24743,16115,29900,29756,37767,29751,17567,159210,17745,30083,16227,150745,150790,16216,30037,30323,173510,15129,29800,166604,149931,149902,15099,15821,150094,16127,149957,149747,37370,22322,37698,166627,137316,20703,152097,152039,30584,143922,30478,30479,30587,149143,145281,14942,149744,29752,29851,16063,150202,150215,16584,150166,156078,37639,152961,30750,30861,30856,30930,29648,31065,161601,153315,16654,31131,33942,31141,27181,147194,31290,31220,16750,136934,16690,37429,31217,134476,149900,131737,146874,137070,13719,21867,13680,13994,131540,134157,31458,23129,141045,154287,154268,23053,131675,30960,23082,154566,31486,16889,31837,31853,16913,154547,155324,155302,31949,150009,137136,31886,31868,31918,27314,32220,32263,32211,32590,156257,155996,162632,32151,155266,17002,158581,133398,26582,131150,144847,22468,156690,156664,149858,32733,31527,133164,154345,154947,31500,155150,39398,34373,39523,27164,144447,14818,150007,157101,39455,157088,33920,160039,158929,17642,33079,17410,32966,33033,33090,157620,39107,158274,33378,33381,158289,33875,159143,34320,160283,23174,16767,137280,23339,137377,23268,137432,34464,195004,146831,34861,160802,23042,34926,20293,34951,35007,35046,35173,35149,153219,35156,161669,161668,166901,166873,166812,166393,16045,33955,18165,18127,14322,35389,35356,169032,24397,37419,148100,26068,28969,28868,137285,40301,35999,36073,163292,22938,30659,23024,17262,14036,36394,36519,150537,36656,36682,17140,27736,28603,140065,18587,28537,28299,137178,39913,14005,149807,37051,37015,21873,18694,37307,37892,166475,16482,166652,37927,166941,166971,34021,35371,38297,38311,38295,38294,167220,29765,16066,149759,150082,148458,16103,143909,38543,167655,167526,167525,16076,149997,150136,147438,29714,29803,16124,38721,168112,26695,18973,168083,153567,38749,37736,166281,166950,166703,156606,37562,23313,35689,18748,29689,147995,38811,38769,39224,134950,24001,166853,150194,38943,169178,37622,169431,37349,17600,166736,150119,166756,39132,166469,16128,37418,18725,33812,39227,39245,162566,15869,39323,19311,39338,39516,166757,153800,27279,39457,23294,39471,170225,19344,170312,39356,19389,19351,37757,22642,135938,22562,149944,136424,30788,141087,146872,26821,15741,37976,14631,24912,141185,141675,24839,40015,40019,40059,39989,39952,39807,39887,171565,39839,172533,172286,40225,19630,147716,40472,19632,40204,172468,172269,172275,170287,40357,33981,159250,159711,158594,34300,17715,159140,159364,159216,33824,34286,159232,145367,155748,31202,144796,144960,18733,149982,15714,37851,37566,37704,131775,30905,37495,37965,20452,13376,36964,152925,30781,30804,30902,30795,137047,143817,149825,13978,20338,28634,28633,28702,28702,21524,147893,22459,22771,22410,40214,22487,28980,13487,147884,29163,158784,151447,23336,137141,166473,24844,23246,23051,17084,148616,14124,19323,166396,37819,37816,137430,134941,33906,158912,136211,148218,142374,148417,22932,146871,157505,32168,155995,155812,149945,149899,166394,37605,29666,16105,29876,166755,137375,16097,150195,27352,29683,29691,16086,150078,150164,137177,150118,132007,136228,149989,29768,149782,28837,149878,37508,29670,37727,132350,37681,166606,166422,37766,166887,153045,18741,166530,29035,149827,134399,22180,132634,134123,134328,21762,31172,137210,32254,136898,150096,137298,17710,37889,14090,166592,149933,22960,137407,137347,160900,23201,14050,146779,14e3,37471,23161,166529,137314,37748,15565,133812,19094,14730,20724,15721,15692,136092,29045,17147,164376,28175,168164,17643,27991,163407,28775,27823,15574,147437,146989,28162,28428,15727,132085,30033,14012,13512,18048,16090,18545,22980,37486,18750,36673,166940,158656,22546,22472,14038,136274,28926,148322,150129,143331,135856,140221,26809,26983,136088,144613,162804,145119,166531,145366,144378,150687,27162,145069,158903,33854,17631,17614,159014,159057,158850,159710,28439,160009,33597,137018,33773,158848,159827,137179,22921,23170,137139,23137,23153,137477,147964,14125,23023,137020,14023,29070,37776,26266,148133,23150,23083,148115,27179,147193,161590,148571,148170,28957,148057,166369,20400,159016,23746,148686,163405,148413,27148,148054,135940,28838,28979,148457,15781,27871,194597,150095,32357,23019,23855,15859,24412,150109,137183,32164,33830,21637,146170,144128,131604,22398,133333,132633,16357,139166,172726,28675,168283,23920,29583,31955,166489,168992,20424,32743,29389,29456,162548,29496,29497,153334,29505,29512,16041,162584,36972,29173,149746,29665,33270,16074,30476,16081,27810,22269,29721,29726,29727,16098,16112,16116,16122,29907,16142,16211,30018,30061,30066,30093,16252,30152,30172,16320,30285,16343,30324,16348,30330,151388,29064,22051,35200,22633,16413,30531,16441,26465,16453,13787,30616,16490,16495,23646,30654,30667,22770,30744,28857,30748,16552,30777,30791,30801,30822,33864,152885,31027,26627,31026,16643,16649,31121,31129,36795,31238,36796,16743,31377,16818,31420,33401,16836,31439,31451,16847,20001,31586,31596,31611,31762,31771,16992,17018,31867,31900,17036,31928,17044,31981,36755,28864,134351,32207,32212,32208,32253,32686,32692,29343,17303,32800,32805,31545,32814,32817,32852,15820,22452,28832,32951,33001,17389,33036,29482,33038,33042,30048,33044,17409,15161,33110,33113,33114,17427,22586,33148,33156,17445,33171,17453,33189,22511,33217,33252,33364,17551,33446,33398,33482,33496,33535,17584,33623,38505,27018,33797,28917,33892,24803,33928,17668,33982,34017,34040,34064,34104,34130,17723,34159,34160,34272,17783,34418,34450,34482,34543,38469,34699,17926,17943,34990,35071,35108,35143,35217,162151,35369,35384,35476,35508,35921,36052,36082,36124,18328,22623,36291,18413,20206,36410,21976,22356,36465,22005,36528,18487,36558,36578,36580,36589,36594,36791,36801,36810,36812,36915,39364,18605,39136,37395,18718,37416,37464,37483,37553,37550,37567,37603,37611,37619,37620,37629,37699,37764,37805,18757,18769,40639,37911,21249,37917,37933,37950,18794,37972,38009,38189,38306,18855,38388,38451,18917,26528,18980,38720,18997,38834,38850,22100,19172,24808,39097,19225,39153,22596,39182,39193,20916,39196,39223,39234,39261,39266,19312,39365,19357,39484,39695,31363,39785,39809,39901,39921,39924,19565,39968,14191,138178,40265,39994,40702,22096,40339,40381,40384,40444,38134,36790,40571,40620,40625,40637,40646,38108,40674,40689,40696,31432,40772,131220,131767,132e3,26906,38083,22956,132311,22592,38081,14265,132565,132629,132726,136890,22359,29043,133826,133837,134079,21610,194619,134091,21662,134139,134203,134227,134245,134268,24807,134285,22138,134325,134365,134381,134511,134578,134600,26965,39983,34725,134660,134670,134871,135056,134957,134771,23584,135100,24075,135260,135247,135286,26398,135291,135304,135318,13895,135359,135379,135471,135483,21348,33965,135907,136053,135990,35713,136567,136729,137155,137159,20088,28859,137261,137578,137773,137797,138282,138352,138412,138952,25283,138965,139029,29080,26709,139333,27113,14024,139900,140247,140282,141098,141425,141647,33533,141671,141715,142037,35237,142056,36768,142094,38840,142143,38983,39613,142412,null,142472,142519,154600,142600,142610,142775,142741,142914,143220,143308,143411,143462,144159,144350,24497,26184,26303,162425,144743,144883,29185,149946,30679,144922,145174,32391,131910,22709,26382,26904,146087,161367,155618,146961,147129,161278,139418,18640,19128,147737,166554,148206,148237,147515,148276,148374,150085,132554,20946,132625,22943,138920,15294,146687,148484,148694,22408,149108,14747,149295,165352,170441,14178,139715,35678,166734,39382,149522,149755,150037,29193,150208,134264,22885,151205,151430,132985,36570,151596,21135,22335,29041,152217,152601,147274,150183,21948,152646,152686,158546,37332,13427,152895,161330,152926,18200,152930,152934,153543,149823,153693,20582,13563,144332,24798,153859,18300,166216,154286,154505,154630,138640,22433,29009,28598,155906,162834,36950,156082,151450,35682,156674,156746,23899,158711,36662,156804,137500,35562,150006,156808,147439,156946,19392,157119,157365,141083,37989,153569,24981,23079,194765,20411,22201,148769,157436,20074,149812,38486,28047,158909,13848,35191,157593,157806,156689,157790,29151,157895,31554,168128,133649,157990,37124,158009,31301,40432,158202,39462,158253,13919,156777,131105,31107,158260,158555,23852,144665,33743,158621,18128,158884,30011,34917,159150,22710,14108,140685,159819,160205,15444,160384,160389,37505,139642,160395,37680,160486,149968,27705,38047,160848,134904,34855,35061,141606,164979,137137,28344,150058,137248,14756,14009,23568,31203,17727,26294,171181,170148,35139,161740,161880,22230,16607,136714,14753,145199,164072,136133,29101,33638,162269,168360,23143,19639,159919,166315,162301,162314,162571,163174,147834,31555,31102,163849,28597,172767,27139,164632,21410,159239,37823,26678,38749,164207,163875,158133,136173,143919,163912,23941,166960,163971,22293,38947,166217,23979,149896,26046,27093,21458,150181,147329,15377,26422,163984,164084,164142,139169,164175,164233,164271,164378,164614,164655,164746,13770,164968,165546,18682,25574,166230,30728,37461,166328,17394,166375,17375,166376,166726,166868,23032,166921,36619,167877,168172,31569,168208,168252,15863,168286,150218,36816,29327,22155,169191,169449,169392,169400,169778,170193,170313,170346,170435,170536,170766,171354,171419,32415,171768,171811,19620,38215,172691,29090,172799,19857,36882,173515,19868,134300,36798,21953,36794,140464,36793,150163,17673,32383,28502,27313,20202,13540,166700,161949,14138,36480,137205,163876,166764,166809,162366,157359,15851,161365,146615,153141,153942,20122,155265,156248,22207,134765,36366,23405,147080,150686,25566,25296,137206,137339,25904,22061,154698,21530,152337,15814,171416,19581,22050,22046,32585,155352,22901,146752,34672,19996,135146,134473,145082,33047,40286,36120,30267,40005,30286,30649,37701,21554,33096,33527,22053,33074,33816,32957,21994,31074,22083,21526,134813,13774,22021,22001,26353,164578,13869,30004,22e3,21946,21655,21874,134209,134294,24272,151880,134774,142434,134818,40619,32090,21982,135285,25245,38765,21652,36045,29174,37238,25596,25529,25598,21865,142147,40050,143027,20890,13535,134567,20903,21581,21790,21779,30310,36397,157834,30129,32950,34820,34694,35015,33206,33820,135361,17644,29444,149254,23440,33547,157843,22139,141044,163119,147875,163187,159440,160438,37232,135641,37384,146684,173737,134828,134905,29286,138402,18254,151490,163833,135147,16634,40029,25887,142752,18675,149472,171388,135148,134666,24674,161187,135149,null,155720,135559,29091,32398,40272,19994,19972,13687,23309,27826,21351,13996,14812,21373,13989,149016,22682,150382,33325,21579,22442,154261,133497,null,14930,140389,29556,171692,19721,39917,146686,171824,19547,151465,169374,171998,33884,146870,160434,157619,145184,25390,32037,147191,146988,14890,36872,21196,15988,13946,17897,132238,30272,23280,134838,30842,163630,22695,16575,22140,39819,23924,30292,173108,40581,19681,30201,14331,24857,143578,148466,null,22109,135849,22439,149859,171526,21044,159918,13741,27722,40316,31830,39737,22494,137068,23635,25811,169168,156469,160100,34477,134440,159010,150242,134513,null,20990,139023,23950,38659,138705,40577,36940,31519,39682,23761,31651,25192,25397,39679,31695,39722,31870,39726,31810,31878,39957,31740,39689,40727,39963,149822,40794,21875,23491,20477,40600,20466,21088,15878,21201,22375,20566,22967,24082,38856,40363,36700,21609,38836,39232,38842,21292,24880,26924,21466,39946,40194,19515,38465,27008,20646,30022,137069,39386,21107,null,37209,38529,37212,null,37201,167575,25471,159011,27338,22033,37262,30074,25221,132092,29519,31856,154657,146685,null,149785,30422,39837,20010,134356,33726,34882,null,23626,27072,20717,22394,21023,24053,20174,27697,131570,20281,21660,21722,21146,36226,13822,24332,13811,null,27474,37244,40869,39831,38958,39092,39610,40616,40580,29050,31508,null,27642,34840,32632,null,22048,173642,36471,40787,null,36308,36431,40476,36353,25218,164733,36392,36469,31443,150135,31294,30936,27882,35431,30215,166490,40742,27854,34774,30147,172722,30803,194624,36108,29410,29553,35629,29442,29937,36075,150203,34351,24506,34976,17591,null,137275,159237,null,35454,140571,null,24829,30311,39639,40260,37742,39823,34805,null,34831,36087,29484,38689,39856,13782,29362,19463,31825,39242,155993,24921,19460,40598,24957,null,22367,24943,25254,25145,25294,14940,25058,21418,144373,25444,26626,13778,23895,166850,36826,167481,null,20697,138566,30982,21298,38456,134971,16485,null,30718,null,31938,155418,31962,31277,32870,32867,32077,29957,29938,35220,33306,26380,32866,160902,32859,29936,33027,30500,35209,157644,30035,159441,34729,34766,33224,34700,35401,36013,35651,30507,29944,34010,13877,27058,36262,null,35241,29800,28089,34753,147473,29927,15835,29046,24740,24988,15569,29026,24695,null,32625,166701,29264,24809,19326,21024,15384,146631,155351,161366,152881,137540,135934,170243,159196,159917,23745,156077,166415,145015,131310,157766,151310,17762,23327,156492,40784,40614,156267,12288,65292,12289,12290,65294,8231,65307,65306,65311,65281,65072,8230,8229,65104,65105,65106,183,65108,65109,65110,65111,65372,8211,65073,8212,65075,9588,65076,65103,65288,65289,65077,65078,65371,65373,65079,65080,12308,12309,65081,65082,12304,12305,65083,65084,12298,12299,65085,65086,12296,12297,65087,65088,12300,12301,65089,65090,12302,12303,65091,65092,65113,65114,65115,65116,65117,65118,8216,8217,8220,8221,12317,12318,8245,8242,65283,65286,65290,8251,167,12291,9675,9679,9651,9650,9678,9734,9733,9671,9670,9633,9632,9661,9660,12963,8453,175,65507,65343,717,65097,65098,65101,65102,65099,65100,65119,65120,65121,65291,65293,215,247,177,8730,65308,65310,65309,8806,8807,8800,8734,8786,8801,65122,65123,65124,65125,65126,65374,8745,8746,8869,8736,8735,8895,13266,13265,8747,8750,8757,8756,9792,9794,8853,8857,8593,8595,8592,8594,8598,8599,8601,8600,8741,8739,65295,65340,8725,65128,65284,65509,12306,65504,65505,65285,65312,8451,8457,65129,65130,65131,13269,13212,13213,13214,13262,13217,13198,13199,13252,176,20825,20827,20830,20829,20833,20835,21991,29929,31950,9601,9602,9603,9604,9605,9606,9607,9608,9615,9614,9613,9612,9611,9610,9609,9532,9524,9516,9508,9500,9620,9472,9474,9621,9484,9488,9492,9496,9581,9582,9584,9583,9552,9566,9578,9569,9698,9699,9701,9700,9585,9586,9587,65296,65297,65298,65299,65300,65301,65302,65303,65304,65305,8544,8545,8546,8547,8548,8549,8550,8551,8552,8553,12321,12322,12323,12324,12325,12326,12327,12328,12329,21313,21316,21317,65313,65314,65315,65316,65317,65318,65319,65320,65321,65322,65323,65324,65325,65326,65327,65328,65329,65330,65331,65332,65333,65334,65335,65336,65337,65338,65345,65346,65347,65348,65349,65350,65351,65352,65353,65354,65355,65356,65357,65358,65359,65360,65361,65362,65363,65364,65365,65366,65367,65368,65369,65370,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,931,932,933,934,935,936,937,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,963,964,965,966,967,968,969,12549,12550,12551,12552,12553,12554,12555,12556,12557,12558,12559,12560,12561,12562,12563,12564,12565,12566,12567,12568,12569,12570,12571,12572,12573,12574,12575,12576,12577,12578,12579,12580,12581,12582,12583,12584,12585,729,713,714,711,715,9216,9217,9218,9219,9220,9221,9222,9223,9224,9225,9226,9227,9228,9229,9230,9231,9232,9233,9234,9235,9236,9237,9238,9239,9240,9241,9242,9243,9244,9245,9246,9247,9249,8364,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,19968,20057,19969,19971,20035,20061,20102,20108,20154,20799,20837,20843,20960,20992,20993,21147,21269,21313,21340,21448,19977,19979,19976,19978,20011,20024,20961,20037,20040,20063,20062,20110,20129,20800,20995,21242,21315,21449,21475,22303,22763,22805,22823,22899,23376,23377,23379,23544,23567,23586,23608,23665,24029,24037,24049,24050,24051,24062,24178,24318,24331,24339,25165,19985,19984,19981,20013,20016,20025,20043,23609,20104,20113,20117,20114,20116,20130,20161,20160,20163,20166,20167,20173,20170,20171,20164,20803,20801,20839,20845,20846,20844,20887,20982,20998,20999,21e3,21243,21246,21247,21270,21305,21320,21319,21317,21342,21380,21451,21450,21453,22764,22825,22827,22826,22829,23380,23569,23588,23610,23663,24052,24187,24319,24340,24341,24515,25096,25142,25163,25166,25903,25991,26007,26020,26041,26085,26352,26376,26408,27424,27490,27513,27595,27604,27611,27663,27700,28779,29226,29238,29243,29255,29273,29275,29356,29579,19993,19990,19989,19988,19992,20027,20045,20047,20046,20197,20184,20180,20181,20182,20183,20195,20196,20185,20190,20805,20804,20873,20874,20908,20985,20986,20984,21002,21152,21151,21253,21254,21271,21277,20191,21322,21321,21345,21344,21359,21358,21435,21487,21476,21491,21484,21486,21481,21480,21500,21496,21493,21483,21478,21482,21490,21489,21488,21477,21485,21499,22235,22234,22806,22830,22833,22900,22902,23381,23427,23612,24040,24039,24038,24066,24067,24179,24188,24321,24344,24343,24517,25098,25171,25172,25170,25169,26021,26086,26414,26412,26410,26411,26413,27491,27597,27665,27664,27704,27713,27712,27710,29359,29572,29577,29916,29926,29976,29983,29992,29993,3e4,30001,30002,30003,30091,30333,30382,30399,30446,30683,30690,30707,31034,31166,31348,31435,19998,19999,20050,20051,20073,20121,20132,20134,20133,20223,20233,20249,20234,20245,20237,20240,20241,20239,20210,20214,20219,20208,20211,20221,20225,20235,20809,20807,20806,20808,20840,20849,20877,20912,21015,21009,21010,21006,21014,21155,21256,21281,21280,21360,21361,21513,21519,21516,21514,21520,21505,21515,21508,21521,21517,21512,21507,21518,21510,21522,22240,22238,22237,22323,22320,22312,22317,22316,22319,22313,22809,22810,22839,22840,22916,22904,22915,22909,22905,22914,22913,23383,23384,23431,23432,23429,23433,23546,23574,23673,24030,24070,24182,24180,24335,24347,24537,24534,25102,25100,25101,25104,25187,25179,25176,25910,26089,26088,26092,26093,26354,26355,26377,26429,26420,26417,26421,27425,27492,27515,27670,27741,27735,27737,27743,27744,27728,27733,27745,27739,27725,27726,28784,29279,29277,30334,31481,31859,31992,32566,32650,32701,32769,32771,32780,32786,32819,32895,32905,32907,32908,33251,33258,33267,33276,33292,33307,33311,33390,33394,33406,34411,34880,34892,34915,35199,38433,20018,20136,20301,20303,20295,20311,20318,20276,20315,20309,20272,20304,20305,20285,20282,20280,20291,20308,20284,20294,20323,20316,20320,20271,20302,20278,20313,20317,20296,20314,20812,20811,20813,20853,20918,20919,21029,21028,21033,21034,21032,21163,21161,21162,21164,21283,21363,21365,21533,21549,21534,21566,21542,21582,21543,21574,21571,21555,21576,21570,21531,21545,21578,21561,21563,21560,21550,21557,21558,21536,21564,21568,21553,21547,21535,21548,22250,22256,22244,22251,22346,22353,22336,22349,22343,22350,22334,22352,22351,22331,22767,22846,22941,22930,22952,22942,22947,22937,22934,22925,22948,22931,22922,22949,23389,23388,23386,23387,23436,23435,23439,23596,23616,23617,23615,23614,23696,23697,23700,23692,24043,24076,24207,24199,24202,24311,24324,24351,24420,24418,24439,24441,24536,24524,24535,24525,24561,24555,24568,24554,25106,25105,25220,25239,25238,25216,25206,25225,25197,25226,25212,25214,25209,25203,25234,25199,25240,25198,25237,25235,25233,25222,25913,25915,25912,26097,26356,26463,26446,26447,26448,26449,26460,26454,26462,26441,26438,26464,26451,26455,27493,27599,27714,27742,27801,27777,27784,27785,27781,27803,27754,27770,27792,27760,27788,27752,27798,27794,27773,27779,27762,27774,27764,27782,27766,27789,27796,27800,27778,28790,28796,28797,28792,29282,29281,29280,29380,29378,29590,29996,29995,30007,30008,30338,30447,30691,31169,31168,31167,31350,31995,32597,32918,32915,32925,32920,32923,32922,32946,33391,33426,33419,33421,35211,35282,35328,35895,35910,35925,35997,36196,36208,36275,36523,36554,36763,36784,36802,36806,36805,36804,24033,37009,37026,37034,37030,37027,37193,37318,37324,38450,38446,38449,38442,38444,20006,20054,20083,20107,20123,20126,20139,20140,20335,20381,20365,20339,20351,20332,20379,20363,20358,20355,20336,20341,20360,20329,20347,20374,20350,20367,20369,20346,20820,20818,20821,20841,20855,20854,20856,20925,20989,21051,21048,21047,21050,21040,21038,21046,21057,21182,21179,21330,21332,21331,21329,21350,21367,21368,21369,21462,21460,21463,21619,21621,21654,21624,21653,21632,21627,21623,21636,21650,21638,21628,21648,21617,21622,21644,21658,21602,21608,21643,21629,21646,22266,22403,22391,22378,22377,22369,22374,22372,22396,22812,22857,22855,22856,22852,22868,22974,22971,22996,22969,22958,22993,22982,22992,22989,22987,22995,22986,22959,22963,22994,22981,23391,23396,23395,23447,23450,23448,23452,23449,23451,23578,23624,23621,23622,23735,23713,23736,23721,23723,23729,23731,24088,24090,24086,24085,24091,24081,24184,24218,24215,24220,24213,24214,24310,24358,24359,24361,24448,24449,24447,24444,24541,24544,24573,24565,24575,24591,24596,24623,24629,24598,24618,24597,24609,24615,24617,24619,24603,25110,25109,25151,25150,25152,25215,25289,25292,25284,25279,25282,25273,25298,25307,25259,25299,25300,25291,25288,25256,25277,25276,25296,25305,25287,25293,25269,25306,25265,25304,25302,25303,25286,25260,25294,25918,26023,26044,26106,26132,26131,26124,26118,26114,26126,26112,26127,26133,26122,26119,26381,26379,26477,26507,26517,26481,26524,26483,26487,26503,26525,26519,26479,26480,26495,26505,26494,26512,26485,26522,26515,26492,26474,26482,27427,27494,27495,27519,27667,27675,27875,27880,27891,27825,27852,27877,27827,27837,27838,27836,27874,27819,27861,27859,27832,27844,27833,27841,27822,27863,27845,27889,27839,27835,27873,27867,27850,27820,27887,27868,27862,27872,28821,28814,28818,28810,28825,29228,29229,29240,29256,29287,29289,29376,29390,29401,29399,29392,29609,29608,29599,29611,29605,30013,30109,30105,30106,30340,30402,30450,30452,30693,30717,31038,31040,31041,31177,31176,31354,31353,31482,31998,32596,32652,32651,32773,32954,32933,32930,32945,32929,32939,32937,32948,32938,32943,33253,33278,33293,33459,33437,33433,33453,33469,33439,33465,33457,33452,33445,33455,33464,33443,33456,33470,33463,34382,34417,21021,34920,36555,36814,36820,36817,37045,37048,37041,37046,37319,37329,38263,38272,38428,38464,38463,38459,38468,38466,38585,38632,38738,38750,20127,20141,20142,20449,20405,20399,20415,20448,20433,20431,20445,20419,20406,20440,20447,20426,20439,20398,20432,20420,20418,20442,20430,20446,20407,20823,20882,20881,20896,21070,21059,21066,21069,21068,21067,21063,21191,21193,21187,21185,21261,21335,21371,21402,21467,21676,21696,21672,21710,21705,21688,21670,21683,21703,21698,21693,21674,21697,21700,21704,21679,21675,21681,21691,21673,21671,21695,22271,22402,22411,22432,22435,22434,22478,22446,22419,22869,22865,22863,22862,22864,23004,23e3,23039,23011,23016,23043,23013,23018,23002,23014,23041,23035,23401,23459,23462,23460,23458,23461,23553,23630,23631,23629,23627,23769,23762,24055,24093,24101,24095,24189,24224,24230,24314,24328,24365,24421,24456,24453,24458,24459,24455,24460,24457,24594,24605,24608,24613,24590,24616,24653,24688,24680,24674,24646,24643,24684,24683,24682,24676,25153,25308,25366,25353,25340,25325,25345,25326,25341,25351,25329,25335,25327,25324,25342,25332,25361,25346,25919,25925,26027,26045,26082,26149,26157,26144,26151,26159,26143,26152,26161,26148,26359,26623,26579,26609,26580,26576,26604,26550,26543,26613,26601,26607,26564,26577,26548,26586,26597,26552,26575,26590,26611,26544,26585,26594,26589,26578,27498,27523,27526,27573,27602,27607,27679,27849,27915,27954,27946,27969,27941,27916,27953,27934,27927,27963,27965,27966,27958,27931,27893,27961,27943,27960,27945,27950,27957,27918,27947,28843,28858,28851,28844,28847,28845,28856,28846,28836,29232,29298,29295,29300,29417,29408,29409,29623,29642,29627,29618,29645,29632,29619,29978,29997,30031,30028,30030,30027,30123,30116,30117,30114,30115,30328,30342,30343,30344,30408,30406,30403,30405,30465,30457,30456,30473,30475,30462,30460,30471,30684,30722,30740,30732,30733,31046,31049,31048,31047,31161,31162,31185,31186,31179,31359,31361,31487,31485,31869,32002,32005,32e3,32009,32007,32004,32006,32568,32654,32703,32772,32784,32781,32785,32822,32982,32997,32986,32963,32964,32972,32993,32987,32974,32990,32996,32989,33268,33314,33511,33539,33541,33507,33499,33510,33540,33509,33538,33545,33490,33495,33521,33537,33500,33492,33489,33502,33491,33503,33519,33542,34384,34425,34427,34426,34893,34923,35201,35284,35336,35330,35331,35998,36e3,36212,36211,36276,36557,36556,36848,36838,36834,36842,36837,36845,36843,36836,36840,37066,37070,37057,37059,37195,37194,37325,38274,38480,38475,38476,38477,38754,38761,38859,38893,38899,38913,39080,39131,39135,39318,39321,20056,20147,20492,20493,20515,20463,20518,20517,20472,20521,20502,20486,20540,20511,20506,20498,20497,20474,20480,20500,20520,20465,20513,20491,20505,20504,20467,20462,20525,20522,20478,20523,20489,20860,20900,20901,20898,20941,20940,20934,20939,21078,21084,21076,21083,21085,21290,21375,21407,21405,21471,21736,21776,21761,21815,21756,21733,21746,21766,21754,21780,21737,21741,21729,21769,21742,21738,21734,21799,21767,21757,21775,22275,22276,22466,22484,22475,22467,22537,22799,22871,22872,22874,23057,23064,23068,23071,23067,23059,23020,23072,23075,23081,23077,23052,23049,23403,23640,23472,23475,23478,23476,23470,23477,23481,23480,23556,23633,23637,23632,23789,23805,23803,23786,23784,23792,23798,23809,23796,24046,24109,24107,24235,24237,24231,24369,24466,24465,24464,24665,24675,24677,24656,24661,24685,24681,24687,24708,24735,24730,24717,24724,24716,24709,24726,25159,25331,25352,25343,25422,25406,25391,25429,25410,25414,25423,25417,25402,25424,25405,25386,25387,25384,25421,25420,25928,25929,26009,26049,26053,26178,26185,26191,26179,26194,26188,26181,26177,26360,26388,26389,26391,26657,26680,26696,26694,26707,26681,26690,26708,26665,26803,26647,26700,26705,26685,26612,26704,26688,26684,26691,26666,26693,26643,26648,26689,27530,27529,27575,27683,27687,27688,27686,27684,27888,28010,28053,28040,28039,28006,28024,28023,27993,28051,28012,28041,28014,27994,28020,28009,28044,28042,28025,28037,28005,28052,28874,28888,28900,28889,28872,28879,29241,29305,29436,29433,29437,29432,29431,29574,29677,29705,29678,29664,29674,29662,30036,30045,30044,30042,30041,30142,30149,30151,30130,30131,30141,30140,30137,30146,30136,30347,30384,30410,30413,30414,30505,30495,30496,30504,30697,30768,30759,30776,30749,30772,30775,30757,30765,30752,30751,30770,31061,31056,31072,31071,31062,31070,31069,31063,31066,31204,31203,31207,31199,31206,31209,31192,31364,31368,31449,31494,31505,31881,32033,32023,32011,32010,32032,32034,32020,32016,32021,32026,32028,32013,32025,32027,32570,32607,32660,32709,32705,32774,32792,32789,32793,32791,32829,32831,33009,33026,33008,33029,33005,33012,33030,33016,33011,33032,33021,33034,33020,33007,33261,33260,33280,33296,33322,33323,33320,33324,33467,33579,33618,33620,33610,33592,33616,33609,33589,33588,33615,33586,33593,33590,33559,33600,33585,33576,33603,34388,34442,34474,34451,34468,34473,34444,34467,34460,34928,34935,34945,34946,34941,34937,35352,35344,35342,35340,35349,35338,35351,35347,35350,35343,35345,35912,35962,35961,36001,36002,36215,36524,36562,36564,36559,36785,36865,36870,36855,36864,36858,36852,36867,36861,36869,36856,37013,37089,37085,37090,37202,37197,37196,37336,37341,37335,37340,37337,38275,38498,38499,38497,38491,38493,38500,38488,38494,38587,39138,39340,39592,39640,39717,39730,39740,20094,20602,20605,20572,20551,20547,20556,20570,20553,20581,20598,20558,20565,20597,20596,20599,20559,20495,20591,20589,20828,20885,20976,21098,21103,21202,21209,21208,21205,21264,21263,21273,21311,21312,21310,21443,26364,21830,21866,21862,21828,21854,21857,21827,21834,21809,21846,21839,21845,21807,21860,21816,21806,21852,21804,21859,21811,21825,21847,22280,22283,22281,22495,22533,22538,22534,22496,22500,22522,22530,22581,22519,22521,22816,22882,23094,23105,23113,23142,23146,23104,23100,23138,23130,23110,23114,23408,23495,23493,23492,23490,23487,23494,23561,23560,23559,23648,23644,23645,23815,23814,23822,23835,23830,23842,23825,23849,23828,23833,23844,23847,23831,24034,24120,24118,24115,24119,24247,24248,24246,24245,24254,24373,24375,24407,24428,24425,24427,24471,24473,24478,24472,24481,24480,24476,24703,24739,24713,24736,24744,24779,24756,24806,24765,24773,24763,24757,24796,24764,24792,24789,24774,24799,24760,24794,24775,25114,25115,25160,25504,25511,25458,25494,25506,25509,25463,25447,25496,25514,25457,25513,25481,25475,25499,25451,25512,25476,25480,25497,25505,25516,25490,25487,25472,25467,25449,25448,25466,25949,25942,25937,25945,25943,21855,25935,25944,25941,25940,26012,26011,26028,26063,26059,26060,26062,26205,26202,26212,26216,26214,26206,26361,21207,26395,26753,26799,26786,26771,26805,26751,26742,26801,26791,26775,26800,26755,26820,26797,26758,26757,26772,26781,26792,26783,26785,26754,27442,27578,27627,27628,27691,28046,28092,28147,28121,28082,28129,28108,28132,28155,28154,28165,28103,28107,28079,28113,28078,28126,28153,28088,28151,28149,28101,28114,28186,28085,28122,28139,28120,28138,28145,28142,28136,28102,28100,28074,28140,28095,28134,28921,28937,28938,28925,28911,29245,29309,29313,29468,29467,29462,29459,29465,29575,29701,29706,29699,29702,29694,29709,29920,29942,29943,29980,29986,30053,30054,30050,30064,30095,30164,30165,30133,30154,30157,30350,30420,30418,30427,30519,30526,30524,30518,30520,30522,30827,30787,30798,31077,31080,31085,31227,31378,31381,31520,31528,31515,31532,31526,31513,31518,31534,31890,31895,31893,32070,32067,32113,32046,32057,32060,32064,32048,32051,32068,32047,32066,32050,32049,32573,32670,32666,32716,32718,32722,32796,32842,32838,33071,33046,33059,33067,33065,33072,33060,33282,33333,33335,33334,33337,33678,33694,33688,33656,33698,33686,33725,33707,33682,33674,33683,33673,33696,33655,33659,33660,33670,33703,34389,24426,34503,34496,34486,34500,34485,34502,34507,34481,34479,34505,34899,34974,34952,34987,34962,34966,34957,34955,35219,35215,35370,35357,35363,35365,35377,35373,35359,35355,35362,35913,35930,36009,36012,36011,36008,36010,36007,36199,36198,36286,36282,36571,36575,36889,36877,36890,36887,36899,36895,36893,36880,36885,36894,36896,36879,36898,36886,36891,36884,37096,37101,37117,37207,37326,37365,37350,37347,37351,37357,37353,38281,38506,38517,38515,38520,38512,38516,38518,38519,38508,38592,38634,38633,31456,31455,38914,38915,39770,40165,40565,40575,40613,40635,20642,20621,20613,20633,20625,20608,20630,20632,20634,26368,20977,21106,21108,21109,21097,21214,21213,21211,21338,21413,21883,21888,21927,21884,21898,21917,21912,21890,21916,21930,21908,21895,21899,21891,21939,21934,21919,21822,21938,21914,21947,21932,21937,21886,21897,21931,21913,22285,22575,22570,22580,22564,22576,22577,22561,22557,22560,22777,22778,22880,23159,23194,23167,23186,23195,23207,23411,23409,23506,23500,23507,23504,23562,23563,23601,23884,23888,23860,23879,24061,24133,24125,24128,24131,24190,24266,24257,24258,24260,24380,24429,24489,24490,24488,24785,24801,24754,24758,24800,24860,24867,24826,24853,24816,24827,24820,24936,24817,24846,24822,24841,24832,24850,25119,25161,25507,25484,25551,25536,25577,25545,25542,25549,25554,25571,25552,25569,25558,25581,25582,25462,25588,25578,25563,25682,25562,25593,25950,25958,25954,25955,26001,26e3,26031,26222,26224,26228,26230,26223,26257,26234,26238,26231,26366,26367,26399,26397,26874,26837,26848,26840,26839,26885,26847,26869,26862,26855,26873,26834,26866,26851,26827,26829,26893,26898,26894,26825,26842,26990,26875,27454,27450,27453,27544,27542,27580,27631,27694,27695,27692,28207,28216,28244,28193,28210,28263,28234,28192,28197,28195,28187,28251,28248,28196,28246,28270,28205,28198,28271,28212,28237,28218,28204,28227,28189,28222,28363,28297,28185,28238,28259,28228,28274,28265,28255,28953,28954,28966,28976,28961,28982,29038,28956,29260,29316,29312,29494,29477,29492,29481,29754,29738,29747,29730,29733,29749,29750,29748,29743,29723,29734,29736,29989,29990,30059,30058,30178,30171,30179,30169,30168,30174,30176,30331,30332,30358,30355,30388,30428,30543,30701,30813,30828,30831,31245,31240,31243,31237,31232,31384,31383,31382,31461,31459,31561,31574,31558,31568,31570,31572,31565,31563,31567,31569,31903,31909,32094,32080,32104,32085,32043,32110,32114,32097,32102,32098,32112,32115,21892,32724,32725,32779,32850,32901,33109,33108,33099,33105,33102,33081,33094,33086,33100,33107,33140,33298,33308,33769,33795,33784,33805,33760,33733,33803,33729,33775,33777,33780,33879,33802,33776,33804,33740,33789,33778,33738,33848,33806,33796,33756,33799,33748,33759,34395,34527,34521,34541,34516,34523,34532,34512,34526,34903,35009,35010,34993,35203,35222,35387,35424,35413,35422,35388,35393,35412,35419,35408,35398,35380,35386,35382,35414,35937,35970,36015,36028,36019,36029,36033,36027,36032,36020,36023,36022,36031,36024,36234,36229,36225,36302,36317,36299,36314,36305,36300,36315,36294,36603,36600,36604,36764,36910,36917,36913,36920,36914,36918,37122,37109,37129,37118,37219,37221,37327,37396,37397,37411,37385,37406,37389,37392,37383,37393,38292,38287,38283,38289,38291,38290,38286,38538,38542,38539,38525,38533,38534,38541,38514,38532,38593,38597,38596,38598,38599,38639,38642,38860,38917,38918,38920,39143,39146,39151,39145,39154,39149,39342,39341,40643,40653,40657,20098,20653,20661,20658,20659,20677,20670,20652,20663,20667,20655,20679,21119,21111,21117,21215,21222,21220,21218,21219,21295,21983,21992,21971,21990,21966,21980,21959,21969,21987,21988,21999,21978,21985,21957,21958,21989,21961,22290,22291,22622,22609,22616,22615,22618,22612,22635,22604,22637,22602,22626,22610,22603,22887,23233,23241,23244,23230,23229,23228,23219,23234,23218,23913,23919,24140,24185,24265,24264,24338,24409,24492,24494,24858,24847,24904,24863,24819,24859,24825,24833,24840,24910,24908,24900,24909,24894,24884,24871,24845,24838,24887,25121,25122,25619,25662,25630,25642,25645,25661,25644,25615,25628,25620,25613,25654,25622,25623,25606,25964,26015,26032,26263,26249,26247,26248,26262,26244,26264,26253,26371,27028,26989,26970,26999,26976,26964,26997,26928,27010,26954,26984,26987,26974,26963,27001,27014,26973,26979,26971,27463,27506,27584,27583,27603,27645,28322,28335,28371,28342,28354,28304,28317,28359,28357,28325,28312,28348,28346,28331,28369,28310,28316,28356,28372,28330,28327,28340,29006,29017,29033,29028,29001,29031,29020,29036,29030,29004,29029,29022,28998,29032,29014,29242,29266,29495,29509,29503,29502,29807,29786,29781,29791,29790,29761,29759,29785,29787,29788,30070,30072,30208,30192,30209,30194,30193,30202,30207,30196,30195,30430,30431,30555,30571,30566,30558,30563,30585,30570,30572,30556,30565,30568,30562,30702,30862,30896,30871,30872,30860,30857,30844,30865,30867,30847,31098,31103,31105,33836,31165,31260,31258,31264,31252,31263,31262,31391,31392,31607,31680,31584,31598,31591,31921,31923,31925,32147,32121,32145,32129,32143,32091,32622,32617,32618,32626,32681,32680,32676,32854,32856,32902,32900,33137,33136,33144,33125,33134,33139,33131,33145,33146,33126,33285,33351,33922,33911,33853,33841,33909,33894,33899,33865,33900,33883,33852,33845,33889,33891,33897,33901,33862,34398,34396,34399,34553,34579,34568,34567,34560,34558,34555,34562,34563,34566,34570,34905,35039,35028,35033,35036,35032,35037,35041,35018,35029,35026,35228,35299,35435,35442,35443,35430,35433,35440,35463,35452,35427,35488,35441,35461,35437,35426,35438,35436,35449,35451,35390,35432,35938,35978,35977,36042,36039,36040,36036,36018,36035,36034,36037,36321,36319,36328,36335,36339,36346,36330,36324,36326,36530,36611,36617,36606,36618,36767,36786,36939,36938,36947,36930,36948,36924,36949,36944,36935,36943,36942,36941,36945,36926,36929,37138,37143,37228,37226,37225,37321,37431,37463,37432,37437,37440,37438,37467,37451,37476,37457,37428,37449,37453,37445,37433,37439,37466,38296,38552,38548,38549,38605,38603,38601,38602,38647,38651,38649,38646,38742,38772,38774,38928,38929,38931,38922,38930,38924,39164,39156,39165,39166,39347,39345,39348,39649,40169,40578,40718,40723,40736,20711,20718,20709,20694,20717,20698,20693,20687,20689,20721,20686,20713,20834,20979,21123,21122,21297,21421,22014,22016,22043,22039,22013,22036,22022,22025,22029,22030,22007,22038,22047,22024,22032,22006,22296,22294,22645,22654,22659,22675,22666,22649,22661,22653,22781,22821,22818,22820,22890,22889,23265,23270,23273,23255,23254,23256,23267,23413,23518,23527,23521,23525,23526,23528,23522,23524,23519,23565,23650,23940,23943,24155,24163,24149,24151,24148,24275,24278,24330,24390,24432,24505,24903,24895,24907,24951,24930,24931,24927,24922,24920,24949,25130,25735,25688,25684,25764,25720,25695,25722,25681,25703,25652,25709,25723,25970,26017,26071,26070,26274,26280,26269,27036,27048,27029,27073,27054,27091,27083,27035,27063,27067,27051,27060,27088,27085,27053,27084,27046,27075,27043,27465,27468,27699,28467,28436,28414,28435,28404,28457,28478,28448,28460,28431,28418,28450,28415,28399,28422,28465,28472,28466,28451,28437,28459,28463,28552,28458,28396,28417,28402,28364,28407,29076,29081,29053,29066,29060,29074,29246,29330,29334,29508,29520,29796,29795,29802,29808,29805,29956,30097,30247,30221,30219,30217,30227,30433,30435,30596,30589,30591,30561,30913,30879,30887,30899,30889,30883,31118,31119,31117,31278,31281,31402,31401,31469,31471,31649,31637,31627,31605,31639,31645,31636,31631,31672,31623,31620,31929,31933,31934,32187,32176,32156,32189,32190,32160,32202,32180,32178,32177,32186,32162,32191,32181,32184,32173,32210,32199,32172,32624,32736,32737,32735,32862,32858,32903,33104,33152,33167,33160,33162,33151,33154,33255,33274,33287,33300,33310,33355,33993,33983,33990,33988,33945,33950,33970,33948,33995,33976,33984,34003,33936,33980,34001,33994,34623,34588,34619,34594,34597,34612,34584,34645,34615,34601,35059,35074,35060,35065,35064,35069,35048,35098,35055,35494,35468,35486,35491,35469,35489,35475,35492,35498,35493,35496,35480,35473,35482,35495,35946,35981,35980,36051,36049,36050,36203,36249,36245,36348,36628,36626,36629,36627,36771,36960,36952,36956,36963,36953,36958,36962,36957,36955,37145,37144,37150,37237,37240,37239,37236,37496,37504,37509,37528,37526,37499,37523,37532,37544,37500,37521,38305,38312,38313,38307,38309,38308,38553,38556,38555,38604,38610,38656,38780,38789,38902,38935,38936,39087,39089,39171,39173,39180,39177,39361,39599,39600,39654,39745,39746,40180,40182,40179,40636,40763,40778,20740,20736,20731,20725,20729,20738,20744,20745,20741,20956,21127,21128,21129,21133,21130,21232,21426,22062,22075,22073,22066,22079,22068,22057,22099,22094,22103,22132,22070,22063,22064,22656,22687,22686,22707,22684,22702,22697,22694,22893,23305,23291,23307,23285,23308,23304,23534,23532,23529,23531,23652,23653,23965,23956,24162,24159,24161,24290,24282,24287,24285,24291,24288,24392,24433,24503,24501,24950,24935,24942,24925,24917,24962,24956,24944,24939,24958,24999,24976,25003,24974,25004,24986,24996,24980,25006,25134,25705,25711,25721,25758,25778,25736,25744,25776,25765,25747,25749,25769,25746,25774,25773,25771,25754,25772,25753,25762,25779,25973,25975,25976,26286,26283,26292,26289,27171,27167,27112,27137,27166,27161,27133,27169,27155,27146,27123,27138,27141,27117,27153,27472,27470,27556,27589,27590,28479,28540,28548,28497,28518,28500,28550,28525,28507,28536,28526,28558,28538,28528,28516,28567,28504,28373,28527,28512,28511,29087,29100,29105,29096,29270,29339,29518,29527,29801,29835,29827,29822,29824,30079,30240,30249,30239,30244,30246,30241,30242,30362,30394,30436,30606,30599,30604,30609,30603,30923,30917,30906,30922,30910,30933,30908,30928,31295,31292,31296,31293,31287,31291,31407,31406,31661,31665,31684,31668,31686,31687,31681,31648,31692,31946,32224,32244,32239,32251,32216,32236,32221,32232,32227,32218,32222,32233,32158,32217,32242,32249,32629,32631,32687,32745,32806,33179,33180,33181,33184,33178,33176,34071,34109,34074,34030,34092,34093,34067,34065,34083,34081,34068,34028,34085,34047,34054,34690,34676,34678,34656,34662,34680,34664,34649,34647,34636,34643,34907,34909,35088,35079,35090,35091,35093,35082,35516,35538,35527,35524,35477,35531,35576,35506,35529,35522,35519,35504,35542,35533,35510,35513,35547,35916,35918,35948,36064,36062,36070,36068,36076,36077,36066,36067,36060,36074,36065,36205,36255,36259,36395,36368,36381,36386,36367,36393,36383,36385,36382,36538,36637,36635,36639,36649,36646,36650,36636,36638,36645,36969,36974,36968,36973,36983,37168,37165,37159,37169,37255,37257,37259,37251,37573,37563,37559,37610,37548,37604,37569,37555,37564,37586,37575,37616,37554,38317,38321,38660,38662,38663,38665,38752,38797,38795,38799,38945,38955,38940,39091,39178,39187,39186,39192,39389,39376,39391,39387,39377,39381,39378,39385,39607,39662,39663,39719,39749,39748,39799,39791,40198,40201,40195,40617,40638,40654,22696,40786,20754,20760,20756,20752,20757,20864,20906,20957,21137,21139,21235,22105,22123,22137,22121,22116,22136,22122,22120,22117,22129,22127,22124,22114,22134,22721,22718,22727,22725,22894,23325,23348,23416,23536,23566,24394,25010,24977,25001,24970,25037,25014,25022,25034,25032,25136,25797,25793,25803,25787,25788,25818,25796,25799,25794,25805,25791,25810,25812,25790,25972,26310,26313,26297,26308,26311,26296,27197,27192,27194,27225,27243,27224,27193,27204,27234,27233,27211,27207,27189,27231,27208,27481,27511,27653,28610,28593,28577,28611,28580,28609,28583,28595,28608,28601,28598,28582,28576,28596,29118,29129,29136,29138,29128,29141,29113,29134,29145,29148,29123,29124,29544,29852,29859,29848,29855,29854,29922,29964,29965,30260,30264,30266,30439,30437,30624,30622,30623,30629,30952,30938,30956,30951,31142,31309,31310,31302,31308,31307,31418,31705,31761,31689,31716,31707,31713,31721,31718,31957,31958,32266,32273,32264,32283,32291,32286,32285,32265,32272,32633,32690,32752,32753,32750,32808,33203,33193,33192,33275,33288,33368,33369,34122,34137,34120,34152,34153,34115,34121,34157,34154,34142,34691,34719,34718,34722,34701,34913,35114,35122,35109,35115,35105,35242,35238,35558,35578,35563,35569,35584,35548,35559,35566,35582,35585,35586,35575,35565,35571,35574,35580,35947,35949,35987,36084,36420,36401,36404,36418,36409,36405,36667,36655,36664,36659,36776,36774,36981,36980,36984,36978,36988,36986,37172,37266,37664,37686,37624,37683,37679,37666,37628,37675,37636,37658,37648,37670,37665,37653,37678,37657,38331,38567,38568,38570,38613,38670,38673,38678,38669,38675,38671,38747,38748,38758,38808,38960,38968,38971,38967,38957,38969,38948,39184,39208,39198,39195,39201,39194,39405,39394,39409,39608,39612,39675,39661,39720,39825,40213,40227,40230,40232,40210,40219,40664,40660,40845,40860,20778,20767,20769,20786,21237,22158,22144,22160,22149,22151,22159,22741,22739,22737,22734,23344,23338,23332,23418,23607,23656,23996,23994,23997,23992,24171,24396,24509,25033,25026,25031,25062,25035,25138,25140,25806,25802,25816,25824,25840,25830,25836,25841,25826,25837,25986,25987,26329,26326,27264,27284,27268,27298,27292,27355,27299,27262,27287,27280,27296,27484,27566,27610,27656,28632,28657,28639,28640,28635,28644,28651,28655,28544,28652,28641,28649,28629,28654,28656,29159,29151,29166,29158,29157,29165,29164,29172,29152,29237,29254,29552,29554,29865,29872,29862,29864,30278,30274,30284,30442,30643,30634,30640,30636,30631,30637,30703,30967,30970,30964,30959,30977,31143,31146,31319,31423,31751,31757,31742,31735,31756,31712,31968,31964,31966,31970,31967,31961,31965,32302,32318,32326,32311,32306,32323,32299,32317,32305,32325,32321,32308,32313,32328,32309,32319,32303,32580,32755,32764,32881,32882,32880,32879,32883,33222,33219,33210,33218,33216,33215,33213,33225,33214,33256,33289,33393,34218,34180,34174,34204,34193,34196,34223,34203,34183,34216,34186,34407,34752,34769,34739,34770,34758,34731,34747,34746,34760,34763,35131,35126,35140,35128,35133,35244,35598,35607,35609,35611,35594,35616,35613,35588,35600,35905,35903,35955,36090,36093,36092,36088,36091,36264,36425,36427,36424,36426,36676,36670,36674,36677,36671,36991,36989,36996,36993,36994,36992,37177,37283,37278,37276,37709,37762,37672,37749,37706,37733,37707,37656,37758,37740,37723,37744,37722,37716,38346,38347,38348,38344,38342,38577,38584,38614,38684,38686,38816,38867,38982,39094,39221,39425,39423,39854,39851,39850,39853,40251,40255,40587,40655,40670,40668,40669,40667,40766,40779,21474,22165,22190,22745,22744,23352,24413,25059,25139,25844,25842,25854,25862,25850,25851,25847,26039,26332,26406,27315,27308,27331,27323,27320,27330,27310,27311,27487,27512,27567,28681,28683,28670,28678,28666,28689,28687,29179,29180,29182,29176,29559,29557,29863,29887,29973,30294,30296,30290,30653,30655,30651,30652,30990,31150,31329,31330,31328,31428,31429,31787,31783,31786,31774,31779,31777,31975,32340,32341,32350,32346,32353,32338,32345,32584,32761,32763,32887,32886,33229,33231,33290,34255,34217,34253,34256,34249,34224,34234,34233,34214,34799,34796,34802,34784,35206,35250,35316,35624,35641,35628,35627,35920,36101,36441,36451,36454,36452,36447,36437,36544,36681,36685,36999,36995,37e3,37291,37292,37328,37780,37770,37782,37794,37811,37806,37804,37808,37784,37786,37783,38356,38358,38352,38357,38626,38620,38617,38619,38622,38692,38819,38822,38829,38905,38989,38991,38988,38990,38995,39098,39230,39231,39229,39214,39333,39438,39617,39683,39686,39759,39758,39757,39882,39881,39933,39880,39872,40273,40285,40288,40672,40725,40748,20787,22181,22750,22751,22754,23541,40848,24300,25074,25079,25078,25077,25856,25871,26336,26333,27365,27357,27354,27347,28699,28703,28712,28698,28701,28693,28696,29190,29197,29272,29346,29560,29562,29885,29898,29923,30087,30086,30303,30305,30663,31001,31153,31339,31337,31806,31807,31800,31805,31799,31808,32363,32365,32377,32361,32362,32645,32371,32694,32697,32696,33240,34281,34269,34282,34261,34276,34277,34295,34811,34821,34829,34809,34814,35168,35167,35158,35166,35649,35676,35672,35657,35674,35662,35663,35654,35673,36104,36106,36476,36466,36487,36470,36460,36474,36468,36692,36686,36781,37002,37003,37297,37294,37857,37841,37855,37827,37832,37852,37853,37846,37858,37837,37848,37860,37847,37864,38364,38580,38627,38698,38695,38753,38876,38907,39006,39e3,39003,39100,39237,39241,39446,39449,39693,39912,39911,39894,39899,40329,40289,40306,40298,40300,40594,40599,40595,40628,21240,22184,22199,22198,22196,22204,22756,23360,23363,23421,23542,24009,25080,25082,25880,25876,25881,26342,26407,27372,28734,28720,28722,29200,29563,29903,30306,30309,31014,31018,31020,31019,31431,31478,31820,31811,31821,31983,31984,36782,32381,32380,32386,32588,32768,33242,33382,34299,34297,34321,34298,34310,34315,34311,34314,34836,34837,35172,35258,35320,35696,35692,35686,35695,35679,35691,36111,36109,36489,36481,36485,36482,37300,37323,37912,37891,37885,38369,38704,39108,39250,39249,39336,39467,39472,39479,39477,39955,39949,40569,40629,40680,40751,40799,40803,40801,20791,20792,22209,22208,22210,22804,23660,24013,25084,25086,25885,25884,26005,26345,27387,27396,27386,27570,28748,29211,29351,29910,29908,30313,30675,31824,32399,32396,32700,34327,34349,34330,34851,34850,34849,34847,35178,35180,35261,35700,35703,35709,36115,36490,36493,36491,36703,36783,37306,37934,37939,37941,37946,37944,37938,37931,38370,38712,38713,38706,38911,39015,39013,39255,39493,39491,39488,39486,39631,39764,39761,39981,39973,40367,40372,40386,40376,40605,40687,40729,40796,40806,40807,20796,20795,22216,22218,22217,23423,24020,24018,24398,25087,25892,27402,27489,28753,28760,29568,29924,30090,30318,30316,31155,31840,31839,32894,32893,33247,35186,35183,35324,35712,36118,36119,36497,36499,36705,37192,37956,37969,37970,38717,38718,38851,38849,39019,39253,39509,39501,39634,39706,40009,39985,39998,39995,40403,40407,40756,40812,40810,40852,22220,24022,25088,25891,25899,25898,26348,27408,29914,31434,31844,31843,31845,32403,32406,32404,33250,34360,34367,34865,35722,37008,37007,37987,37984,37988,38760,39023,39260,39514,39515,39511,39635,39636,39633,40020,40023,40022,40421,40607,40692,22225,22761,25900,28766,30321,30322,30679,32592,32648,34870,34873,34914,35731,35730,35734,33399,36123,37312,37994,38722,38728,38724,38854,39024,39519,39714,39768,40031,40441,40442,40572,40573,40711,40823,40818,24307,27414,28771,31852,31854,34875,35264,36513,37313,38002,38e3,39025,39262,39638,39715,40652,28772,30682,35738,38007,38857,39522,39525,32412,35740,36522,37317,38013,38014,38012,40055,40056,40695,35924,38015,40474,29224,39530,39729,40475,40478,31858,9312,9313,9314,9315,9316,9317,9318,9319,9320,9321,9332,9333,9334,9335,9336,9337,9338,9339,9340,9341,8560,8561,8562,8563,8564,8565,8566,8567,8568,8569,20022,20031,20101,20128,20866,20886,20907,21241,21304,21353,21430,22794,23424,24027,12083,24191,24308,24400,24417,25908,26080,30098,30326,36789,38582,168,710,12541,12542,12445,12446,12291,20189,12293,12294,12295,12540,65339,65341,10045,12353,12354,12355,12356,12357,12358,12359,12360,12361,12362,12363,12364,12365,12366,12367,12368,12369,12370,12371,12372,12373,12374,12375,12376,12377,12378,12379,12380,12381,12382,12383,12384,12385,12386,12387,12388,12389,12390,12391,12392,12393,12394,12395,12396,12397,12398,12399,12400,12401,12402,12403,12404,12405,12406,12407,12408,12409,12410,12411,12412,12413,12414,12415,12416,12417,12418,12419,12420,12421,12422,12423,12424,12425,12426,12427,12428,12429,12430,12431,12432,12433,12434,12435,12449,12450,12451,12452,12453,12454,12455,12456,12457,12458,12459,12460,12461,12462,12463,12464,12465,12466,12467,12468,12469,12470,12471,12472,12473,12474,12475,12476,12477,12478,12479,12480,12481,12482,12483,12484,12485,12486,12487,12488,12489,12490,12491,12492,12493,12494,12495,12496,12497,12498,12499,12500,12501,12502,12503,12504,12505,12506,12507,12508,12509,12510,12511,12512,12513,12514,12515,12516,12517,12518,12519,12520,12521,12522,12523,12524,12525,12526,12527,12528,12529,12530,12531,12532,12533,12534,1040,1041,1042,1043,1044,1045,1025,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1105,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,8679,8632,8633,12751,131276,20058,131210,20994,17553,40880,20872,40881,161287,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,65506,65508,65287,65282,12849,8470,8481,12443,12444,11904,11908,11910,11911,11912,11914,11916,11917,11925,11932,11933,11941,11943,11946,11948,11950,11958,11964,11966,11974,11978,11980,11981,11983,11990,11991,11998,12003,null,null,null,643,592,603,596,629,339,248,331,650,618,20034,20060,20981,21274,21378,19975,19980,20039,20109,22231,64012,23662,24435,19983,20871,19982,20014,20115,20162,20169,20168,20888,21244,21356,21433,22304,22787,22828,23568,24063,26081,27571,27596,27668,29247,20017,20028,20200,20188,20201,20193,20189,20186,21004,21276,21324,22306,22307,22807,22831,23425,23428,23570,23611,23668,23667,24068,24192,24194,24521,25097,25168,27669,27702,27715,27711,27707,29358,29360,29578,31160,32906,38430,20238,20248,20268,20213,20244,20209,20224,20215,20232,20253,20226,20229,20258,20243,20228,20212,20242,20913,21011,21001,21008,21158,21282,21279,21325,21386,21511,22241,22239,22318,22314,22324,22844,22912,22908,22917,22907,22910,22903,22911,23382,23573,23589,23676,23674,23675,23678,24031,24181,24196,24322,24346,24436,24533,24532,24527,25180,25182,25188,25185,25190,25186,25177,25184,25178,25189,26095,26094,26430,26425,26424,26427,26426,26431,26428,26419,27672,27718,27730,27740,27727,27722,27732,27723,27724,28785,29278,29364,29365,29582,29994,30335,31349,32593,33400,33404,33408,33405,33407,34381,35198,37017,37015,37016,37019,37012,38434,38436,38432,38435,20310,20283,20322,20297,20307,20324,20286,20327,20306,20319,20289,20312,20269,20275,20287,20321,20879,20921,21020,21022,21025,21165,21166,21257,21347,21362,21390,21391,21552,21559,21546,21588,21573,21529,21532,21541,21528,21565,21583,21569,21544,21540,21575,22254,22247,22245,22337,22341,22348,22345,22347,22354,22790,22848,22950,22936,22944,22935,22926,22946,22928,22927,22951,22945,23438,23442,23592,23594,23693,23695,23688,23691,23689,23698,23690,23686,23699,23701,24032,24074,24078,24203,24201,24204,24200,24205,24325,24349,24440,24438,24530,24529,24528,24557,24552,24558,24563,24545,24548,24547,24570,24559,24567,24571,24576,24564,25146,25219,25228,25230,25231,25236,25223,25201,25211,25210,25200,25217,25224,25207,25213,25202,25204,25911,26096,26100,26099,26098,26101,26437,26439,26457,26453,26444,26440,26461,26445,26458,26443,27600,27673,27674,27768,27751,27755,27780,27787,27791,27761,27759,27753,27802,27757,27783,27797,27804,27750,27763,27749,27771,27790,28788,28794,29283,29375,29373,29379,29382,29377,29370,29381,29589,29591,29587,29588,29586,30010,30009,30100,30101,30337,31037,32820,32917,32921,32912,32914,32924,33424,33423,33413,33422,33425,33427,33418,33411,33412,35960,36809,36799,37023,37025,37029,37022,37031,37024,38448,38440,38447,38445,20019,20376,20348,20357,20349,20352,20359,20342,20340,20361,20356,20343,20300,20375,20330,20378,20345,20353,20344,20368,20380,20372,20382,20370,20354,20373,20331,20334,20894,20924,20926,21045,21042,21043,21062,21041,21180,21258,21259,21308,21394,21396,21639,21631,21633,21649,21634,21640,21611,21626,21630,21605,21612,21620,21606,21645,21615,21601,21600,21656,21603,21607,21604,22263,22265,22383,22386,22381,22379,22385,22384,22390,22400,22389,22395,22387,22388,22370,22376,22397,22796,22853,22965,22970,22991,22990,22962,22988,22977,22966,22972,22979,22998,22961,22973,22976,22984,22964,22983,23394,23397,23443,23445,23620,23623,23726,23716,23712,23733,23727,23720,23724,23711,23715,23725,23714,23722,23719,23709,23717,23734,23728,23718,24087,24084,24089,24360,24354,24355,24356,24404,24450,24446,24445,24542,24549,24621,24614,24601,24626,24587,24628,24586,24599,24627,24602,24606,24620,24610,24589,24592,24622,24595,24593,24588,24585,24604,25108,25149,25261,25268,25297,25278,25258,25270,25290,25262,25267,25263,25275,25257,25264,25272,25917,26024,26043,26121,26108,26116,26130,26120,26107,26115,26123,26125,26117,26109,26129,26128,26358,26378,26501,26476,26510,26514,26486,26491,26520,26502,26500,26484,26509,26508,26490,26527,26513,26521,26499,26493,26497,26488,26489,26516,27429,27520,27518,27614,27677,27795,27884,27883,27886,27865,27830,27860,27821,27879,27831,27856,27842,27834,27843,27846,27885,27890,27858,27869,27828,27786,27805,27776,27870,27840,27952,27853,27847,27824,27897,27855,27881,27857,28820,28824,28805,28819,28806,28804,28817,28822,28802,28826,28803,29290,29398,29387,29400,29385,29404,29394,29396,29402,29388,29393,29604,29601,29613,29606,29602,29600,29612,29597,29917,29928,30015,30016,30014,30092,30104,30383,30451,30449,30448,30453,30712,30716,30713,30715,30714,30711,31042,31039,31173,31352,31355,31483,31861,31997,32821,32911,32942,32931,32952,32949,32941,33312,33440,33472,33451,33434,33432,33435,33461,33447,33454,33468,33438,33466,33460,33448,33441,33449,33474,33444,33475,33462,33442,34416,34415,34413,34414,35926,36818,36811,36819,36813,36822,36821,36823,37042,37044,37039,37043,37040,38457,38461,38460,38458,38467,20429,20421,20435,20402,20425,20427,20417,20436,20444,20441,20411,20403,20443,20423,20438,20410,20416,20409,20460,21060,21065,21184,21186,21309,21372,21399,21398,21401,21400,21690,21665,21677,21669,21711,21699,33549,21687,21678,21718,21686,21701,21702,21664,21616,21692,21666,21694,21618,21726,21680,22453,22430,22431,22436,22412,22423,22429,22427,22420,22424,22415,22425,22437,22426,22421,22772,22797,22867,23009,23006,23022,23040,23025,23005,23034,23037,23036,23030,23012,23026,23031,23003,23017,23027,23029,23008,23038,23028,23021,23464,23628,23760,23768,23756,23767,23755,23771,23774,23770,23753,23751,23754,23766,23763,23764,23759,23752,23750,23758,23775,23800,24057,24097,24098,24099,24096,24100,24240,24228,24226,24219,24227,24229,24327,24366,24406,24454,24631,24633,24660,24690,24670,24645,24659,24647,24649,24667,24652,24640,24642,24671,24612,24644,24664,24678,24686,25154,25155,25295,25357,25355,25333,25358,25347,25323,25337,25359,25356,25336,25334,25344,25363,25364,25338,25365,25339,25328,25921,25923,26026,26047,26166,26145,26162,26165,26140,26150,26146,26163,26155,26170,26141,26164,26169,26158,26383,26384,26561,26610,26568,26554,26588,26555,26616,26584,26560,26551,26565,26603,26596,26591,26549,26573,26547,26615,26614,26606,26595,26562,26553,26574,26599,26608,26546,26620,26566,26605,26572,26542,26598,26587,26618,26569,26570,26563,26602,26571,27432,27522,27524,27574,27606,27608,27616,27680,27681,27944,27956,27949,27935,27964,27967,27922,27914,27866,27955,27908,27929,27962,27930,27921,27904,27933,27970,27905,27928,27959,27907,27919,27968,27911,27936,27948,27912,27938,27913,27920,28855,28831,28862,28849,28848,28833,28852,28853,28841,29249,29257,29258,29292,29296,29299,29294,29386,29412,29416,29419,29407,29418,29414,29411,29573,29644,29634,29640,29637,29625,29622,29621,29620,29675,29631,29639,29630,29635,29638,29624,29643,29932,29934,29998,30023,30024,30119,30122,30329,30404,30472,30467,30468,30469,30474,30455,30459,30458,30695,30696,30726,30737,30738,30725,30736,30735,30734,30729,30723,30739,31050,31052,31051,31045,31044,31189,31181,31183,31190,31182,31360,31358,31441,31488,31489,31866,31864,31865,31871,31872,31873,32003,32008,32001,32600,32657,32653,32702,32775,32782,32783,32788,32823,32984,32967,32992,32977,32968,32962,32976,32965,32995,32985,32988,32970,32981,32969,32975,32983,32998,32973,33279,33313,33428,33497,33534,33529,33543,33512,33536,33493,33594,33515,33494,33524,33516,33505,33522,33525,33548,33531,33526,33520,33514,33508,33504,33530,33523,33517,34423,34420,34428,34419,34881,34894,34919,34922,34921,35283,35332,35335,36210,36835,36833,36846,36832,37105,37053,37055,37077,37061,37054,37063,37067,37064,37332,37331,38484,38479,38481,38483,38474,38478,20510,20485,20487,20499,20514,20528,20507,20469,20468,20531,20535,20524,20470,20471,20503,20508,20512,20519,20533,20527,20529,20494,20826,20884,20883,20938,20932,20933,20936,20942,21089,21082,21074,21086,21087,21077,21090,21197,21262,21406,21798,21730,21783,21778,21735,21747,21732,21786,21759,21764,21768,21739,21777,21765,21745,21770,21755,21751,21752,21728,21774,21763,21771,22273,22274,22476,22578,22485,22482,22458,22470,22461,22460,22456,22454,22463,22471,22480,22457,22465,22798,22858,23065,23062,23085,23086,23061,23055,23063,23050,23070,23091,23404,23463,23469,23468,23555,23638,23636,23788,23807,23790,23793,23799,23808,23801,24105,24104,24232,24238,24234,24236,24371,24368,24423,24669,24666,24679,24641,24738,24712,24704,24722,24705,24733,24707,24725,24731,24727,24711,24732,24718,25113,25158,25330,25360,25430,25388,25412,25413,25398,25411,25572,25401,25419,25418,25404,25385,25409,25396,25432,25428,25433,25389,25415,25395,25434,25425,25400,25431,25408,25416,25930,25926,26054,26051,26052,26050,26186,26207,26183,26193,26386,26387,26655,26650,26697,26674,26675,26683,26699,26703,26646,26673,26652,26677,26667,26669,26671,26702,26692,26676,26653,26642,26644,26662,26664,26670,26701,26682,26661,26656,27436,27439,27437,27441,27444,27501,32898,27528,27622,27620,27624,27619,27618,27623,27685,28026,28003,28004,28022,27917,28001,28050,27992,28002,28013,28015,28049,28045,28143,28031,28038,27998,28007,28e3,28055,28016,28028,27999,28034,28056,27951,28008,28043,28030,28032,28036,27926,28035,28027,28029,28021,28048,28892,28883,28881,28893,28875,32569,28898,28887,28882,28894,28896,28884,28877,28869,28870,28871,28890,28878,28897,29250,29304,29303,29302,29440,29434,29428,29438,29430,29427,29435,29441,29651,29657,29669,29654,29628,29671,29667,29673,29660,29650,29659,29652,29661,29658,29655,29656,29672,29918,29919,29940,29941,29985,30043,30047,30128,30145,30139,30148,30144,30143,30134,30138,30346,30409,30493,30491,30480,30483,30482,30499,30481,30485,30489,30490,30498,30503,30755,30764,30754,30773,30767,30760,30766,30763,30753,30761,30771,30762,30769,31060,31067,31055,31068,31059,31058,31057,31211,31212,31200,31214,31213,31210,31196,31198,31197,31366,31369,31365,31371,31372,31370,31367,31448,31504,31492,31507,31493,31503,31496,31498,31502,31497,31506,31876,31889,31882,31884,31880,31885,31877,32030,32029,32017,32014,32024,32022,32019,32031,32018,32015,32012,32604,32609,32606,32608,32605,32603,32662,32658,32707,32706,32704,32790,32830,32825,33018,33010,33017,33013,33025,33019,33024,33281,33327,33317,33587,33581,33604,33561,33617,33573,33622,33599,33601,33574,33564,33570,33602,33614,33563,33578,33544,33596,33613,33558,33572,33568,33591,33583,33577,33607,33605,33612,33619,33566,33580,33611,33575,33608,34387,34386,34466,34472,34454,34445,34449,34462,34439,34455,34438,34443,34458,34437,34469,34457,34465,34471,34453,34456,34446,34461,34448,34452,34883,34884,34925,34933,34934,34930,34944,34929,34943,34927,34947,34942,34932,34940,35346,35911,35927,35963,36004,36003,36214,36216,36277,36279,36278,36561,36563,36862,36853,36866,36863,36859,36868,36860,36854,37078,37088,37081,37082,37091,37087,37093,37080,37083,37079,37084,37092,37200,37198,37199,37333,37346,37338,38492,38495,38588,39139,39647,39727,20095,20592,20586,20577,20574,20576,20563,20555,20573,20594,20552,20557,20545,20571,20554,20578,20501,20549,20575,20585,20587,20579,20580,20550,20544,20590,20595,20567,20561,20944,21099,21101,21100,21102,21206,21203,21293,21404,21877,21878,21820,21837,21840,21812,21802,21841,21858,21814,21813,21808,21842,21829,21772,21810,21861,21838,21817,21832,21805,21819,21824,21835,22282,22279,22523,22548,22498,22518,22492,22516,22528,22509,22525,22536,22520,22539,22515,22479,22535,22510,22499,22514,22501,22508,22497,22542,22524,22544,22503,22529,22540,22513,22505,22512,22541,22532,22876,23136,23128,23125,23143,23134,23096,23093,23149,23120,23135,23141,23148,23123,23140,23127,23107,23133,23122,23108,23131,23112,23182,23102,23117,23097,23116,23152,23145,23111,23121,23126,23106,23132,23410,23406,23489,23488,23641,23838,23819,23837,23834,23840,23820,23848,23821,23846,23845,23823,23856,23826,23843,23839,23854,24126,24116,24241,24244,24249,24242,24243,24374,24376,24475,24470,24479,24714,24720,24710,24766,24752,24762,24787,24788,24783,24804,24793,24797,24776,24753,24795,24759,24778,24767,24771,24781,24768,25394,25445,25482,25474,25469,25533,25502,25517,25501,25495,25515,25486,25455,25479,25488,25454,25519,25461,25500,25453,25518,25468,25508,25403,25503,25464,25477,25473,25489,25485,25456,25939,26061,26213,26209,26203,26201,26204,26210,26392,26745,26759,26768,26780,26733,26734,26798,26795,26966,26735,26787,26796,26793,26741,26740,26802,26767,26743,26770,26748,26731,26738,26794,26752,26737,26750,26779,26774,26763,26784,26761,26788,26744,26747,26769,26764,26762,26749,27446,27443,27447,27448,27537,27535,27533,27534,27532,27690,28096,28075,28084,28083,28276,28076,28137,28130,28087,28150,28116,28160,28104,28128,28127,28118,28094,28133,28124,28125,28123,28148,28106,28093,28141,28144,28090,28117,28098,28111,28105,28112,28146,28115,28157,28119,28109,28131,28091,28922,28941,28919,28951,28916,28940,28912,28932,28915,28944,28924,28927,28934,28947,28928,28920,28918,28939,28930,28942,29310,29307,29308,29311,29469,29463,29447,29457,29464,29450,29448,29439,29455,29470,29576,29686,29688,29685,29700,29697,29693,29703,29696,29690,29692,29695,29708,29707,29684,29704,30052,30051,30158,30162,30159,30155,30156,30161,30160,30351,30345,30419,30521,30511,30509,30513,30514,30516,30515,30525,30501,30523,30517,30792,30802,30793,30797,30794,30796,30758,30789,30800,31076,31079,31081,31082,31075,31083,31073,31163,31226,31224,31222,31223,31375,31380,31376,31541,31559,31540,31525,31536,31522,31524,31539,31512,31530,31517,31537,31531,31533,31535,31538,31544,31514,31523,31892,31896,31894,31907,32053,32061,32056,32054,32058,32069,32044,32041,32065,32071,32062,32063,32074,32059,32040,32611,32661,32668,32669,32667,32714,32715,32717,32720,32721,32711,32719,32713,32799,32798,32795,32839,32835,32840,33048,33061,33049,33051,33069,33055,33068,33054,33057,33045,33063,33053,33058,33297,33336,33331,33338,33332,33330,33396,33680,33699,33704,33677,33658,33651,33700,33652,33679,33665,33685,33689,33653,33684,33705,33661,33667,33676,33693,33691,33706,33675,33662,33701,33711,33672,33687,33712,33663,33702,33671,33710,33654,33690,34393,34390,34495,34487,34498,34497,34501,34490,34480,34504,34489,34483,34488,34508,34484,34491,34492,34499,34493,34494,34898,34953,34965,34984,34978,34986,34970,34961,34977,34975,34968,34983,34969,34971,34967,34980,34988,34956,34963,34958,35202,35286,35289,35285,35376,35367,35372,35358,35897,35899,35932,35933,35965,36005,36221,36219,36217,36284,36290,36281,36287,36289,36568,36574,36573,36572,36567,36576,36577,36900,36875,36881,36892,36876,36897,37103,37098,37104,37108,37106,37107,37076,37099,37100,37097,37206,37208,37210,37203,37205,37356,37364,37361,37363,37368,37348,37369,37354,37355,37367,37352,37358,38266,38278,38280,38524,38509,38507,38513,38511,38591,38762,38916,39141,39319,20635,20629,20628,20638,20619,20643,20611,20620,20622,20637,20584,20636,20626,20610,20615,20831,20948,21266,21265,21412,21415,21905,21928,21925,21933,21879,22085,21922,21907,21896,21903,21941,21889,21923,21906,21924,21885,21900,21926,21887,21909,21921,21902,22284,22569,22583,22553,22558,22567,22563,22568,22517,22600,22565,22556,22555,22579,22591,22582,22574,22585,22584,22573,22572,22587,22881,23215,23188,23199,23162,23202,23198,23160,23206,23164,23205,23212,23189,23214,23095,23172,23178,23191,23171,23179,23209,23163,23165,23180,23196,23183,23187,23197,23530,23501,23499,23508,23505,23498,23502,23564,23600,23863,23875,23915,23873,23883,23871,23861,23889,23886,23893,23859,23866,23890,23869,23857,23897,23874,23865,23881,23864,23868,23858,23862,23872,23877,24132,24129,24408,24486,24485,24491,24777,24761,24780,24802,24782,24772,24852,24818,24842,24854,24837,24821,24851,24824,24828,24830,24769,24835,24856,24861,24848,24831,24836,24843,25162,25492,25521,25520,25550,25573,25576,25583,25539,25757,25587,25546,25568,25590,25557,25586,25589,25697,25567,25534,25565,25564,25540,25560,25555,25538,25543,25548,25547,25544,25584,25559,25561,25906,25959,25962,25956,25948,25960,25957,25996,26013,26014,26030,26064,26066,26236,26220,26235,26240,26225,26233,26218,26226,26369,26892,26835,26884,26844,26922,26860,26858,26865,26895,26838,26871,26859,26852,26870,26899,26896,26867,26849,26887,26828,26888,26992,26804,26897,26863,26822,26900,26872,26832,26877,26876,26856,26891,26890,26903,26830,26824,26845,26846,26854,26868,26833,26886,26836,26857,26901,26917,26823,27449,27451,27455,27452,27540,27543,27545,27541,27581,27632,27634,27635,27696,28156,28230,28231,28191,28233,28296,28220,28221,28229,28258,28203,28223,28225,28253,28275,28188,28211,28235,28224,28241,28219,28163,28206,28254,28264,28252,28257,28209,28200,28256,28273,28267,28217,28194,28208,28243,28261,28199,28280,28260,28279,28245,28281,28242,28262,28213,28214,28250,28960,28958,28975,28923,28974,28977,28963,28965,28962,28978,28959,28968,28986,28955,29259,29274,29320,29321,29318,29317,29323,29458,29451,29488,29474,29489,29491,29479,29490,29485,29478,29475,29493,29452,29742,29740,29744,29739,29718,29722,29729,29741,29745,29732,29731,29725,29737,29728,29746,29947,29999,30063,30060,30183,30170,30177,30182,30173,30175,30180,30167,30357,30354,30426,30534,30535,30532,30541,30533,30538,30542,30539,30540,30686,30700,30816,30820,30821,30812,30829,30833,30826,30830,30832,30825,30824,30814,30818,31092,31091,31090,31088,31234,31242,31235,31244,31236,31385,31462,31460,31562,31547,31556,31560,31564,31566,31552,31576,31557,31906,31902,31912,31905,32088,32111,32099,32083,32086,32103,32106,32079,32109,32092,32107,32082,32084,32105,32081,32095,32078,32574,32575,32613,32614,32674,32672,32673,32727,32849,32847,32848,33022,32980,33091,33098,33106,33103,33095,33085,33101,33082,33254,33262,33271,33272,33273,33284,33340,33341,33343,33397,33595,33743,33785,33827,33728,33768,33810,33767,33764,33788,33782,33808,33734,33736,33771,33763,33727,33793,33757,33765,33752,33791,33761,33739,33742,33750,33781,33737,33801,33807,33758,33809,33798,33730,33779,33749,33786,33735,33745,33770,33811,33731,33772,33774,33732,33787,33751,33762,33819,33755,33790,34520,34530,34534,34515,34531,34522,34538,34525,34539,34524,34540,34537,34519,34536,34513,34888,34902,34901,35002,35031,35001,35e3,35008,35006,34998,35004,34999,35005,34994,35073,35017,35221,35224,35223,35293,35290,35291,35406,35405,35385,35417,35392,35415,35416,35396,35397,35410,35400,35409,35402,35404,35407,35935,35969,35968,36026,36030,36016,36025,36021,36228,36224,36233,36312,36307,36301,36295,36310,36316,36303,36309,36313,36296,36311,36293,36591,36599,36602,36601,36582,36590,36581,36597,36583,36584,36598,36587,36593,36588,36596,36585,36909,36916,36911,37126,37164,37124,37119,37116,37128,37113,37115,37121,37120,37127,37125,37123,37217,37220,37215,37218,37216,37377,37386,37413,37379,37402,37414,37391,37388,37376,37394,37375,37373,37382,37380,37415,37378,37404,37412,37401,37399,37381,37398,38267,38285,38284,38288,38535,38526,38536,38537,38531,38528,38594,38600,38595,38641,38640,38764,38768,38766,38919,39081,39147,40166,40697,20099,20100,20150,20669,20671,20678,20654,20676,20682,20660,20680,20674,20656,20673,20666,20657,20683,20681,20662,20664,20951,21114,21112,21115,21116,21955,21979,21964,21968,21963,21962,21981,21952,21972,21956,21993,21951,21970,21901,21967,21973,21986,21974,21960,22002,21965,21977,21954,22292,22611,22632,22628,22607,22605,22601,22639,22613,22606,22621,22617,22629,22619,22589,22627,22641,22780,23239,23236,23243,23226,23224,23217,23221,23216,23231,23240,23227,23238,23223,23232,23242,23220,23222,23245,23225,23184,23510,23512,23513,23583,23603,23921,23907,23882,23909,23922,23916,23902,23912,23911,23906,24048,24143,24142,24138,24141,24139,24261,24268,24262,24267,24263,24384,24495,24493,24823,24905,24906,24875,24901,24886,24882,24878,24902,24879,24911,24873,24896,25120,37224,25123,25125,25124,25541,25585,25579,25616,25618,25609,25632,25636,25651,25667,25631,25621,25624,25657,25655,25634,25635,25612,25638,25648,25640,25665,25653,25647,25610,25626,25664,25637,25639,25611,25575,25627,25646,25633,25614,25967,26002,26067,26246,26252,26261,26256,26251,26250,26265,26260,26232,26400,26982,26975,26936,26958,26978,26993,26943,26949,26986,26937,26946,26967,26969,27002,26952,26953,26933,26988,26931,26941,26981,26864,27e3,26932,26985,26944,26991,26948,26998,26968,26945,26996,26956,26939,26955,26935,26972,26959,26961,26930,26962,26927,27003,26940,27462,27461,27459,27458,27464,27457,27547,64013,27643,27644,27641,27639,27640,28315,28374,28360,28303,28352,28319,28307,28308,28320,28337,28345,28358,28370,28349,28353,28318,28361,28343,28336,28365,28326,28367,28338,28350,28355,28380,28376,28313,28306,28302,28301,28324,28321,28351,28339,28368,28362,28311,28334,28323,28999,29012,29010,29027,29024,28993,29021,29026,29042,29048,29034,29025,28994,29016,28995,29003,29040,29023,29008,29011,28996,29005,29018,29263,29325,29324,29329,29328,29326,29500,29506,29499,29498,29504,29514,29513,29764,29770,29771,29778,29777,29783,29760,29775,29776,29774,29762,29766,29773,29780,29921,29951,29950,29949,29981,30073,30071,27011,30191,30223,30211,30199,30206,30204,30201,30200,30224,30203,30198,30189,30197,30205,30361,30389,30429,30549,30559,30560,30546,30550,30554,30569,30567,30548,30553,30573,30688,30855,30874,30868,30863,30852,30869,30853,30854,30881,30851,30841,30873,30848,30870,30843,31100,31106,31101,31097,31249,31256,31257,31250,31255,31253,31266,31251,31259,31248,31395,31394,31390,31467,31590,31588,31597,31604,31593,31602,31589,31603,31601,31600,31585,31608,31606,31587,31922,31924,31919,32136,32134,32128,32141,32127,32133,32122,32142,32123,32131,32124,32140,32148,32132,32125,32146,32621,32619,32615,32616,32620,32678,32677,32679,32731,32732,32801,33124,33120,33143,33116,33129,33115,33122,33138,26401,33118,33142,33127,33135,33092,33121,33309,33353,33348,33344,33346,33349,34033,33855,33878,33910,33913,33935,33933,33893,33873,33856,33926,33895,33840,33869,33917,33882,33881,33908,33907,33885,34055,33886,33847,33850,33844,33914,33859,33912,33842,33861,33833,33753,33867,33839,33858,33837,33887,33904,33849,33870,33868,33874,33903,33989,33934,33851,33863,33846,33843,33896,33918,33860,33835,33888,33876,33902,33872,34571,34564,34551,34572,34554,34518,34549,34637,34552,34574,34569,34561,34550,34573,34565,35030,35019,35021,35022,35038,35035,35034,35020,35024,35205,35227,35295,35301,35300,35297,35296,35298,35292,35302,35446,35462,35455,35425,35391,35447,35458,35460,35445,35459,35457,35444,35450,35900,35915,35914,35941,35940,35942,35974,35972,35973,36044,36200,36201,36241,36236,36238,36239,36237,36243,36244,36240,36242,36336,36320,36332,36337,36334,36304,36329,36323,36322,36327,36338,36331,36340,36614,36607,36609,36608,36613,36615,36616,36610,36619,36946,36927,36932,36937,36925,37136,37133,37135,37137,37142,37140,37131,37134,37230,37231,37448,37458,37424,37434,37478,37427,37477,37470,37507,37422,37450,37446,37485,37484,37455,37472,37479,37487,37430,37473,37488,37425,37460,37475,37456,37490,37454,37459,37452,37462,37426,38303,38300,38302,38299,38546,38547,38545,38551,38606,38650,38653,38648,38645,38771,38775,38776,38770,38927,38925,38926,39084,39158,39161,39343,39346,39344,39349,39597,39595,39771,40170,40173,40167,40576,40701,20710,20692,20695,20712,20723,20699,20714,20701,20708,20691,20716,20720,20719,20707,20704,20952,21120,21121,21225,21227,21296,21420,22055,22037,22028,22034,22012,22031,22044,22017,22035,22018,22010,22045,22020,22015,22009,22665,22652,22672,22680,22662,22657,22655,22644,22667,22650,22663,22673,22670,22646,22658,22664,22651,22676,22671,22782,22891,23260,23278,23269,23253,23274,23258,23277,23275,23283,23266,23264,23259,23276,23262,23261,23257,23272,23263,23415,23520,23523,23651,23938,23936,23933,23942,23930,23937,23927,23946,23945,23944,23934,23932,23949,23929,23935,24152,24153,24147,24280,24273,24279,24270,24284,24277,24281,24274,24276,24388,24387,24431,24502,24876,24872,24897,24926,24945,24947,24914,24915,24946,24940,24960,24948,24916,24954,24923,24933,24891,24938,24929,24918,25129,25127,25131,25643,25677,25691,25693,25716,25718,25714,25715,25725,25717,25702,25766,25678,25730,25694,25692,25675,25683,25696,25680,25727,25663,25708,25707,25689,25701,25719,25971,26016,26273,26272,26271,26373,26372,26402,27057,27062,27081,27040,27086,27030,27056,27052,27068,27025,27033,27022,27047,27021,27049,27070,27055,27071,27076,27069,27044,27092,27065,27082,27034,27087,27059,27027,27050,27041,27038,27097,27031,27024,27074,27061,27045,27078,27466,27469,27467,27550,27551,27552,27587,27588,27646,28366,28405,28401,28419,28453,28408,28471,28411,28462,28425,28494,28441,28442,28455,28440,28475,28434,28397,28426,28470,28531,28409,28398,28461,28480,28464,28476,28469,28395,28423,28430,28483,28421,28413,28406,28473,28444,28412,28474,28447,28429,28446,28424,28449,29063,29072,29065,29056,29061,29058,29071,29051,29062,29057,29079,29252,29267,29335,29333,29331,29507,29517,29521,29516,29794,29811,29809,29813,29810,29799,29806,29952,29954,29955,30077,30096,30230,30216,30220,30229,30225,30218,30228,30392,30593,30588,30597,30594,30574,30592,30575,30590,30595,30898,30890,30900,30893,30888,30846,30891,30878,30885,30880,30892,30882,30884,31128,31114,31115,31126,31125,31124,31123,31127,31112,31122,31120,31275,31306,31280,31279,31272,31270,31400,31403,31404,31470,31624,31644,31626,31633,31632,31638,31629,31628,31643,31630,31621,31640,21124,31641,31652,31618,31931,31935,31932,31930,32167,32183,32194,32163,32170,32193,32192,32197,32157,32206,32196,32198,32203,32204,32175,32185,32150,32188,32159,32166,32174,32169,32161,32201,32627,32738,32739,32741,32734,32804,32861,32860,33161,33158,33155,33159,33165,33164,33163,33301,33943,33956,33953,33951,33978,33998,33986,33964,33966,33963,33977,33972,33985,33997,33962,33946,33969,34e3,33949,33959,33979,33954,33940,33991,33996,33947,33961,33967,33960,34006,33944,33974,33999,33952,34007,34004,34002,34011,33968,33937,34401,34611,34595,34600,34667,34624,34606,34590,34593,34585,34587,34627,34604,34625,34622,34630,34592,34610,34602,34605,34620,34578,34618,34609,34613,34626,34598,34599,34616,34596,34586,34608,34577,35063,35047,35057,35058,35066,35070,35054,35068,35062,35067,35056,35052,35051,35229,35233,35231,35230,35305,35307,35304,35499,35481,35467,35474,35471,35478,35901,35944,35945,36053,36047,36055,36246,36361,36354,36351,36365,36349,36362,36355,36359,36358,36357,36350,36352,36356,36624,36625,36622,36621,37155,37148,37152,37154,37151,37149,37146,37156,37153,37147,37242,37234,37241,37235,37541,37540,37494,37531,37498,37536,37524,37546,37517,37542,37530,37547,37497,37527,37503,37539,37614,37518,37506,37525,37538,37501,37512,37537,37514,37510,37516,37529,37543,37502,37511,37545,37533,37515,37421,38558,38561,38655,38744,38781,38778,38782,38787,38784,38786,38779,38788,38785,38783,38862,38861,38934,39085,39086,39170,39168,39175,39325,39324,39363,39353,39355,39354,39362,39357,39367,39601,39651,39655,39742,39743,39776,39777,39775,40177,40178,40181,40615,20735,20739,20784,20728,20742,20743,20726,20734,20747,20748,20733,20746,21131,21132,21233,21231,22088,22082,22092,22069,22081,22090,22089,22086,22104,22106,22080,22067,22077,22060,22078,22072,22058,22074,22298,22699,22685,22705,22688,22691,22703,22700,22693,22689,22783,23295,23284,23293,23287,23286,23299,23288,23298,23289,23297,23303,23301,23311,23655,23961,23959,23967,23954,23970,23955,23957,23968,23964,23969,23962,23966,24169,24157,24160,24156,32243,24283,24286,24289,24393,24498,24971,24963,24953,25009,25008,24994,24969,24987,24979,25007,25005,24991,24978,25002,24993,24973,24934,25011,25133,25710,25712,25750,25760,25733,25751,25756,25743,25739,25738,25740,25763,25759,25704,25777,25752,25974,25978,25977,25979,26034,26035,26293,26288,26281,26290,26295,26282,26287,27136,27142,27159,27109,27128,27157,27121,27108,27168,27135,27116,27106,27163,27165,27134,27175,27122,27118,27156,27127,27111,27200,27144,27110,27131,27149,27132,27115,27145,27140,27160,27173,27151,27126,27174,27143,27124,27158,27473,27557,27555,27554,27558,27649,27648,27647,27650,28481,28454,28542,28551,28614,28562,28557,28553,28556,28514,28495,28549,28506,28566,28534,28524,28546,28501,28530,28498,28496,28503,28564,28563,28509,28416,28513,28523,28541,28519,28560,28499,28555,28521,28543,28565,28515,28535,28522,28539,29106,29103,29083,29104,29088,29082,29097,29109,29085,29093,29086,29092,29089,29098,29084,29095,29107,29336,29338,29528,29522,29534,29535,29536,29533,29531,29537,29530,29529,29538,29831,29833,29834,29830,29825,29821,29829,29832,29820,29817,29960,29959,30078,30245,30238,30233,30237,30236,30243,30234,30248,30235,30364,30365,30366,30363,30605,30607,30601,30600,30925,30907,30927,30924,30929,30926,30932,30920,30915,30916,30921,31130,31137,31136,31132,31138,31131,27510,31289,31410,31412,31411,31671,31691,31678,31660,31694,31663,31673,31690,31669,31941,31944,31948,31947,32247,32219,32234,32231,32215,32225,32259,32250,32230,32246,32241,32240,32238,32223,32630,32684,32688,32685,32749,32747,32746,32748,32742,32744,32868,32871,33187,33183,33182,33173,33186,33177,33175,33302,33359,33363,33362,33360,33358,33361,34084,34107,34063,34048,34089,34062,34057,34061,34079,34058,34087,34076,34043,34091,34042,34056,34060,34036,34090,34034,34069,34039,34027,34035,34044,34066,34026,34025,34070,34046,34088,34077,34094,34050,34045,34078,34038,34097,34086,34023,34024,34032,34031,34041,34072,34080,34096,34059,34073,34095,34402,34646,34659,34660,34679,34785,34675,34648,34644,34651,34642,34657,34650,34641,34654,34669,34666,34640,34638,34655,34653,34671,34668,34682,34670,34652,34661,34639,34683,34677,34658,34663,34665,34906,35077,35084,35092,35083,35095,35096,35097,35078,35094,35089,35086,35081,35234,35236,35235,35309,35312,35308,35535,35526,35512,35539,35537,35540,35541,35515,35543,35518,35520,35525,35544,35523,35514,35517,35545,35902,35917,35983,36069,36063,36057,36072,36058,36061,36071,36256,36252,36257,36251,36384,36387,36389,36388,36398,36373,36379,36374,36369,36377,36390,36391,36372,36370,36376,36371,36380,36375,36378,36652,36644,36632,36634,36640,36643,36630,36631,36979,36976,36975,36967,36971,37167,37163,37161,37162,37170,37158,37166,37253,37254,37258,37249,37250,37252,37248,37584,37571,37572,37568,37593,37558,37583,37617,37599,37592,37609,37591,37597,37580,37615,37570,37608,37578,37576,37582,37606,37581,37589,37577,37600,37598,37607,37585,37587,37557,37601,37574,37556,38268,38316,38315,38318,38320,38564,38562,38611,38661,38664,38658,38746,38794,38798,38792,38864,38863,38942,38941,38950,38953,38952,38944,38939,38951,39090,39176,39162,39185,39188,39190,39191,39189,39388,39373,39375,39379,39380,39374,39369,39382,39384,39371,39383,39372,39603,39660,39659,39667,39666,39665,39750,39747,39783,39796,39793,39782,39798,39797,39792,39784,39780,39788,40188,40186,40189,40191,40183,40199,40192,40185,40187,40200,40197,40196,40579,40659,40719,40720,20764,20755,20759,20762,20753,20958,21300,21473,22128,22112,22126,22131,22118,22115,22125,22130,22110,22135,22300,22299,22728,22717,22729,22719,22714,22722,22716,22726,23319,23321,23323,23329,23316,23315,23312,23318,23336,23322,23328,23326,23535,23980,23985,23977,23975,23989,23984,23982,23978,23976,23986,23981,23983,23988,24167,24168,24166,24175,24297,24295,24294,24296,24293,24395,24508,24989,25e3,24982,25029,25012,25030,25025,25036,25018,25023,25016,24972,25815,25814,25808,25807,25801,25789,25737,25795,25819,25843,25817,25907,25983,25980,26018,26312,26302,26304,26314,26315,26319,26301,26299,26298,26316,26403,27188,27238,27209,27239,27186,27240,27198,27229,27245,27254,27227,27217,27176,27226,27195,27199,27201,27242,27236,27216,27215,27220,27247,27241,27232,27196,27230,27222,27221,27213,27214,27206,27477,27476,27478,27559,27562,27563,27592,27591,27652,27651,27654,28589,28619,28579,28615,28604,28622,28616,28510,28612,28605,28574,28618,28584,28676,28581,28590,28602,28588,28586,28623,28607,28600,28578,28617,28587,28621,28591,28594,28592,29125,29122,29119,29112,29142,29120,29121,29131,29140,29130,29127,29135,29117,29144,29116,29126,29146,29147,29341,29342,29545,29542,29543,29548,29541,29547,29546,29823,29850,29856,29844,29842,29845,29857,29963,30080,30255,30253,30257,30269,30259,30268,30261,30258,30256,30395,30438,30618,30621,30625,30620,30619,30626,30627,30613,30617,30615,30941,30953,30949,30954,30942,30947,30939,30945,30946,30957,30943,30944,31140,31300,31304,31303,31414,31416,31413,31409,31415,31710,31715,31719,31709,31701,31717,31706,31720,31737,31700,31722,31714,31708,31723,31704,31711,31954,31956,31959,31952,31953,32274,32289,32279,32268,32287,32288,32275,32270,32284,32277,32282,32290,32267,32271,32278,32269,32276,32293,32292,32579,32635,32636,32634,32689,32751,32810,32809,32876,33201,33190,33198,33209,33205,33195,33200,33196,33204,33202,33207,33191,33266,33365,33366,33367,34134,34117,34155,34125,34131,34145,34136,34112,34118,34148,34113,34146,34116,34129,34119,34147,34110,34139,34161,34126,34158,34165,34133,34151,34144,34188,34150,34141,34132,34149,34156,34403,34405,34404,34715,34703,34711,34707,34706,34696,34689,34710,34712,34681,34695,34723,34693,34704,34705,34717,34692,34708,34716,34714,34697,35102,35110,35120,35117,35118,35111,35121,35106,35113,35107,35119,35116,35103,35313,35552,35554,35570,35572,35573,35549,35604,35556,35551,35568,35528,35550,35553,35560,35583,35567,35579,35985,35986,35984,36085,36078,36081,36080,36083,36204,36206,36261,36263,36403,36414,36408,36416,36421,36406,36412,36413,36417,36400,36415,36541,36662,36654,36661,36658,36665,36663,36660,36982,36985,36987,36998,37114,37171,37173,37174,37267,37264,37265,37261,37263,37671,37662,37640,37663,37638,37647,37754,37688,37692,37659,37667,37650,37633,37702,37677,37646,37645,37579,37661,37626,37669,37651,37625,37623,37684,37634,37668,37631,37673,37689,37685,37674,37652,37644,37643,37630,37641,37632,37627,37654,38332,38349,38334,38329,38330,38326,38335,38325,38333,38569,38612,38667,38674,38672,38809,38807,38804,38896,38904,38965,38959,38962,39204,39199,39207,39209,39326,39406,39404,39397,39396,39408,39395,39402,39401,39399,39609,39615,39604,39611,39670,39674,39673,39671,39731,39808,39813,39815,39804,39806,39803,39810,39827,39826,39824,39802,39829,39805,39816,40229,40215,40224,40222,40212,40233,40221,40216,40226,40208,40217,40223,40584,40582,40583,40622,40621,40661,40662,40698,40722,40765,20774,20773,20770,20772,20768,20777,21236,22163,22156,22157,22150,22148,22147,22142,22146,22143,22145,22742,22740,22735,22738,23341,23333,23346,23331,23340,23335,23334,23343,23342,23419,23537,23538,23991,24172,24170,24510,24507,25027,25013,25020,25063,25056,25061,25060,25064,25054,25839,25833,25827,25835,25828,25832,25985,25984,26038,26074,26322,27277,27286,27265,27301,27273,27295,27291,27297,27294,27271,27283,27278,27285,27267,27304,27300,27281,27263,27302,27290,27269,27276,27282,27483,27565,27657,28620,28585,28660,28628,28643,28636,28653,28647,28646,28638,28658,28637,28642,28648,29153,29169,29160,29170,29156,29168,29154,29555,29550,29551,29847,29874,29867,29840,29866,29869,29873,29861,29871,29968,29969,29970,29967,30084,30275,30280,30281,30279,30372,30441,30645,30635,30642,30647,30646,30644,30641,30632,30704,30963,30973,30978,30971,30972,30962,30981,30969,30974,30980,31147,31144,31324,31323,31318,31320,31316,31322,31422,31424,31425,31749,31759,31730,31744,31743,31739,31758,31732,31755,31731,31746,31753,31747,31745,31736,31741,31750,31728,31729,31760,31754,31976,32301,32316,32322,32307,38984,32312,32298,32329,32320,32327,32297,32332,32304,32315,32310,32324,32314,32581,32639,32638,32637,32756,32754,32812,33211,33220,33228,33226,33221,33223,33212,33257,33371,33370,33372,34179,34176,34191,34215,34197,34208,34187,34211,34171,34212,34202,34206,34167,34172,34185,34209,34170,34168,34135,34190,34198,34182,34189,34201,34205,34177,34210,34178,34184,34181,34169,34166,34200,34192,34207,34408,34750,34730,34733,34757,34736,34732,34745,34741,34748,34734,34761,34755,34754,34764,34743,34735,34756,34762,34740,34742,34751,34744,34749,34782,34738,35125,35123,35132,35134,35137,35154,35127,35138,35245,35247,35246,35314,35315,35614,35608,35606,35601,35589,35595,35618,35599,35602,35605,35591,35597,35592,35590,35612,35603,35610,35919,35952,35954,35953,35951,35989,35988,36089,36207,36430,36429,36435,36432,36428,36423,36675,36672,36997,36990,37176,37274,37282,37275,37273,37279,37281,37277,37280,37793,37763,37807,37732,37718,37703,37756,37720,37724,37750,37705,37712,37713,37728,37741,37775,37708,37738,37753,37719,37717,37714,37711,37745,37751,37755,37729,37726,37731,37735,37760,37710,37721,38343,38336,38345,38339,38341,38327,38574,38576,38572,38688,38687,38680,38685,38681,38810,38817,38812,38814,38813,38869,38868,38897,38977,38980,38986,38985,38981,38979,39205,39211,39212,39210,39219,39218,39215,39213,39217,39216,39320,39331,39329,39426,39418,39412,39415,39417,39416,39414,39419,39421,39422,39420,39427,39614,39678,39677,39681,39676,39752,39834,39848,39838,39835,39846,39841,39845,39844,39814,39842,39840,39855,40243,40257,40295,40246,40238,40239,40241,40248,40240,40261,40258,40259,40254,40247,40256,40253,32757,40237,40586,40585,40589,40624,40648,40666,40699,40703,40740,40739,40738,40788,40864,20785,20781,20782,22168,22172,22167,22170,22173,22169,22896,23356,23657,23658,24e3,24173,24174,25048,25055,25069,25070,25073,25066,25072,25067,25046,25065,25855,25860,25853,25848,25857,25859,25852,26004,26075,26330,26331,26328,27333,27321,27325,27361,27334,27322,27318,27319,27335,27316,27309,27486,27593,27659,28679,28684,28685,28673,28677,28692,28686,28671,28672,28667,28710,28668,28663,28682,29185,29183,29177,29187,29181,29558,29880,29888,29877,29889,29886,29878,29883,29890,29972,29971,30300,30308,30297,30288,30291,30295,30298,30374,30397,30444,30658,30650,30975,30988,30995,30996,30985,30992,30994,30993,31149,31148,31327,31772,31785,31769,31776,31775,31789,31773,31782,31784,31778,31781,31792,32348,32336,32342,32355,32344,32354,32351,32337,32352,32343,32339,32693,32691,32759,32760,32885,33233,33234,33232,33375,33374,34228,34246,34240,34243,34242,34227,34229,34237,34247,34244,34239,34251,34254,34248,34245,34225,34230,34258,34340,34232,34231,34238,34409,34791,34790,34786,34779,34795,34794,34789,34783,34803,34788,34772,34780,34771,34797,34776,34787,34724,34775,34777,34817,34804,34792,34781,35155,35147,35151,35148,35142,35152,35153,35145,35626,35623,35619,35635,35632,35637,35655,35631,35644,35646,35633,35621,35639,35622,35638,35630,35620,35643,35645,35642,35906,35957,35993,35992,35991,36094,36100,36098,36096,36444,36450,36448,36439,36438,36446,36453,36455,36443,36442,36449,36445,36457,36436,36678,36679,36680,36683,37160,37178,37179,37182,37288,37285,37287,37295,37290,37813,37772,37778,37815,37787,37789,37769,37799,37774,37802,37790,37798,37781,37768,37785,37791,37773,37809,37777,37810,37796,37800,37812,37795,37797,38354,38355,38353,38579,38615,38618,24002,38623,38616,38621,38691,38690,38693,38828,38830,38824,38827,38820,38826,38818,38821,38871,38873,38870,38872,38906,38992,38993,38994,39096,39233,39228,39226,39439,39435,39433,39437,39428,39441,39434,39429,39431,39430,39616,39644,39688,39684,39685,39721,39733,39754,39756,39755,39879,39878,39875,39871,39873,39861,39864,39891,39862,39876,39865,39869,40284,40275,40271,40266,40283,40267,40281,40278,40268,40279,40274,40276,40287,40280,40282,40590,40588,40671,40705,40704,40726,40741,40747,40746,40745,40744,40780,40789,20788,20789,21142,21239,21428,22187,22189,22182,22183,22186,22188,22746,22749,22747,22802,23357,23358,23359,24003,24176,24511,25083,25863,25872,25869,25865,25868,25870,25988,26078,26077,26334,27367,27360,27340,27345,27353,27339,27359,27356,27344,27371,27343,27341,27358,27488,27568,27660,28697,28711,28704,28694,28715,28705,28706,28707,28713,28695,28708,28700,28714,29196,29194,29191,29186,29189,29349,29350,29348,29347,29345,29899,29893,29879,29891,29974,30304,30665,30666,30660,30705,31005,31003,31009,31004,30999,31006,31152,31335,31336,31795,31804,31801,31788,31803,31980,31978,32374,32373,32376,32368,32375,32367,32378,32370,32372,32360,32587,32586,32643,32646,32695,32765,32766,32888,33239,33237,33380,33377,33379,34283,34289,34285,34265,34273,34280,34266,34263,34284,34290,34296,34264,34271,34275,34268,34257,34288,34278,34287,34270,34274,34816,34810,34819,34806,34807,34825,34828,34827,34822,34812,34824,34815,34826,34818,35170,35162,35163,35159,35169,35164,35160,35165,35161,35208,35255,35254,35318,35664,35656,35658,35648,35667,35670,35668,35659,35669,35665,35650,35666,35671,35907,35959,35958,35994,36102,36103,36105,36268,36266,36269,36267,36461,36472,36467,36458,36463,36475,36546,36690,36689,36687,36688,36691,36788,37184,37183,37296,37293,37854,37831,37839,37826,37850,37840,37881,37868,37836,37849,37801,37862,37834,37844,37870,37859,37845,37828,37838,37824,37842,37863,38269,38362,38363,38625,38697,38699,38700,38696,38694,38835,38839,38838,38877,38878,38879,39004,39001,39005,38999,39103,39101,39099,39102,39240,39239,39235,39334,39335,39450,39445,39461,39453,39460,39451,39458,39456,39463,39459,39454,39452,39444,39618,39691,39690,39694,39692,39735,39914,39915,39904,39902,39908,39910,39906,39920,39892,39895,39916,39900,39897,39909,39893,39905,39898,40311,40321,40330,40324,40328,40305,40320,40312,40326,40331,40332,40317,40299,40308,40309,40304,40297,40325,40307,40315,40322,40303,40313,40319,40327,40296,40596,40593,40640,40700,40749,40768,40769,40781,40790,40791,40792,21303,22194,22197,22195,22755,23365,24006,24007,24302,24303,24512,24513,25081,25879,25878,25877,25875,26079,26344,26339,26340,27379,27376,27370,27368,27385,27377,27374,27375,28732,28725,28719,28727,28724,28721,28738,28728,28735,28730,28729,28736,28731,28723,28737,29203,29204,29352,29565,29564,29882,30379,30378,30398,30445,30668,30670,30671,30669,30706,31013,31011,31015,31016,31012,31017,31154,31342,31340,31341,31479,31817,31816,31818,31815,31813,31982,32379,32382,32385,32384,32698,32767,32889,33243,33241,33291,33384,33385,34338,34303,34305,34302,34331,34304,34294,34308,34313,34309,34316,34301,34841,34832,34833,34839,34835,34838,35171,35174,35257,35319,35680,35690,35677,35688,35683,35685,35687,35693,36270,36486,36488,36484,36697,36694,36695,36693,36696,36698,37005,37187,37185,37303,37301,37298,37299,37899,37907,37883,37920,37903,37908,37886,37909,37904,37928,37913,37901,37877,37888,37879,37895,37902,37910,37906,37882,37897,37880,37898,37887,37884,37900,37878,37905,37894,38366,38368,38367,38702,38703,38841,38843,38909,38910,39008,39010,39011,39007,39105,39106,39248,39246,39257,39244,39243,39251,39474,39476,39473,39468,39466,39478,39465,39470,39480,39469,39623,39626,39622,39696,39698,39697,39947,39944,39927,39941,39954,39928,4e4,39943,39950,39942,39959,39956,39945,40351,40345,40356,40349,40338,40344,40336,40347,40352,40340,40348,40362,40343,40353,40346,40354,40360,40350,40355,40383,40361,40342,40358,40359,40601,40603,40602,40677,40676,40679,40678,40752,40750,40795,40800,40798,40797,40793,40849,20794,20793,21144,21143,22211,22205,22206,23368,23367,24011,24015,24305,25085,25883,27394,27388,27395,27384,27392,28739,28740,28746,28744,28745,28741,28742,29213,29210,29209,29566,29975,30314,30672,31021,31025,31023,31828,31827,31986,32394,32391,32392,32395,32390,32397,32589,32699,32816,33245,34328,34346,34342,34335,34339,34332,34329,34343,34350,34337,34336,34345,34334,34341,34857,34845,34843,34848,34852,34844,34859,34890,35181,35177,35182,35179,35322,35705,35704,35653,35706,35707,36112,36116,36271,36494,36492,36702,36699,36701,37190,37188,37189,37305,37951,37947,37942,37929,37949,37948,37936,37945,37930,37943,37932,37952,37937,38373,38372,38371,38709,38714,38847,38881,39012,39113,39110,39104,39256,39254,39481,39485,39494,39492,39490,39489,39482,39487,39629,39701,39703,39704,39702,39738,39762,39979,39965,39964,39980,39971,39976,39977,39972,39969,40375,40374,40380,40385,40391,40394,40399,40382,40389,40387,40379,40373,40398,40377,40378,40364,40392,40369,40365,40396,40371,40397,40370,40570,40604,40683,40686,40685,40731,40728,40730,40753,40782,40805,40804,40850,20153,22214,22213,22219,22897,23371,23372,24021,24017,24306,25889,25888,25894,25890,27403,27400,27401,27661,28757,28758,28759,28754,29214,29215,29353,29567,29912,29909,29913,29911,30317,30381,31029,31156,31344,31345,31831,31836,31833,31835,31834,31988,31985,32401,32591,32647,33246,33387,34356,34357,34355,34348,34354,34358,34860,34856,34854,34858,34853,35185,35263,35262,35323,35710,35716,35714,35718,35717,35711,36117,36501,36500,36506,36498,36496,36502,36503,36704,36706,37191,37964,37968,37962,37963,37967,37959,37957,37960,37961,37958,38719,38883,39018,39017,39115,39252,39259,39502,39507,39508,39500,39503,39496,39498,39497,39506,39504,39632,39705,39723,39739,39766,39765,40006,40008,39999,40004,39993,39987,40001,39996,39991,39988,39986,39997,39990,40411,40402,40414,40410,40395,40400,40412,40401,40415,40425,40409,40408,40406,40437,40405,40413,40630,40688,40757,40755,40754,40770,40811,40853,40866,20797,21145,22760,22759,22898,23373,24024,34863,24399,25089,25091,25092,25897,25893,26006,26347,27409,27410,27407,27594,28763,28762,29218,29570,29569,29571,30320,30676,31847,31846,32405,33388,34362,34368,34361,34364,34353,34363,34366,34864,34866,34862,34867,35190,35188,35187,35326,35724,35726,35723,35720,35909,36121,36504,36708,36707,37308,37986,37973,37981,37975,37982,38852,38853,38912,39510,39513,39710,39711,39712,40018,40024,40016,40010,40013,40011,40021,40025,40012,40014,40443,40439,40431,40419,40427,40440,40420,40438,40417,40430,40422,40434,40432,40418,40428,40436,40435,40424,40429,40642,40656,40690,40691,40710,40732,40760,40759,40758,40771,40783,40817,40816,40814,40815,22227,22221,23374,23661,25901,26349,26350,27411,28767,28769,28765,28768,29219,29915,29925,30677,31032,31159,31158,31850,32407,32649,33389,34371,34872,34871,34869,34891,35732,35733,36510,36511,36512,36509,37310,37309,37314,37995,37992,37993,38629,38726,38723,38727,38855,38885,39518,39637,39769,40035,40039,40038,40034,40030,40032,40450,40446,40455,40451,40454,40453,40448,40449,40457,40447,40445,40452,40608,40734,40774,40820,40821,40822,22228,25902,26040,27416,27417,27415,27418,28770,29222,29354,30680,30681,31033,31849,31851,31990,32410,32408,32411,32409,33248,33249,34374,34375,34376,35193,35194,35196,35195,35327,35736,35737,36517,36516,36515,37998,37997,37999,38001,38003,38729,39026,39263,40040,40046,40045,40459,40461,40464,40463,40466,40465,40609,40693,40713,40775,40824,40827,40826,40825,22302,28774,31855,34876,36274,36518,37315,38004,38008,38006,38005,39520,40052,40051,40049,40053,40468,40467,40694,40714,40868,28776,28773,31991,34410,34878,34877,34879,35742,35996,36521,36553,38731,39027,39028,39116,39265,39339,39524,39526,39527,39716,40469,40471,40776,25095,27422,29223,34380,36520,38018,38016,38017,39529,39528,39726,40473,29225,34379,35743,38019,40057,40631,30325,39531,40058,40477,28777,28778,40612,40830,40777,40856,30849,37561,35023,22715,24658,31911,23290,9556,9574,9559,9568,9580,9571,9562,9577,9565,9554,9572,9557,9566,9578,9569,9560,9575,9563,9555,9573,9558,9567,9579,9570,9561,9576,9564,9553,9552,9581,9582,9584,9583,65517,132423,37595,132575,147397,34124,17077,29679,20917,13897,149826,166372,37700,137691,33518,146632,30780,26436,25311,149811,166314,131744,158643,135941,20395,140525,20488,159017,162436,144896,150193,140563,20521,131966,24484,131968,131911,28379,132127,20605,20737,13434,20750,39020,14147,33814,149924,132231,20832,144308,20842,134143,139516,131813,140592,132494,143923,137603,23426,34685,132531,146585,20914,20920,40244,20937,20943,20945,15580,20947,150182,20915,20962,21314,20973,33741,26942,145197,24443,21003,21030,21052,21173,21079,21140,21177,21189,31765,34114,21216,34317,158483,21253,166622,21833,28377,147328,133460,147436,21299,21316,134114,27851,136998,26651,29653,24650,16042,14540,136936,29149,17570,21357,21364,165547,21374,21375,136598,136723,30694,21395,166555,21408,21419,21422,29607,153458,16217,29596,21441,21445,27721,20041,22526,21465,15019,134031,21472,147435,142755,21494,134263,21523,28793,21803,26199,27995,21613,158547,134516,21853,21647,21668,18342,136973,134877,15796,134477,166332,140952,21831,19693,21551,29719,21894,21929,22021,137431,147514,17746,148533,26291,135348,22071,26317,144010,26276,26285,22093,22095,30961,22257,38791,21502,22272,22255,22253,166758,13859,135759,22342,147877,27758,28811,22338,14001,158846,22502,136214,22531,136276,148323,22566,150517,22620,22698,13665,22752,22748,135740,22779,23551,22339,172368,148088,37843,13729,22815,26790,14019,28249,136766,23076,21843,136850,34053,22985,134478,158849,159018,137180,23001,137211,137138,159142,28017,137256,136917,23033,159301,23211,23139,14054,149929,23159,14088,23190,29797,23251,159649,140628,15749,137489,14130,136888,24195,21200,23414,25992,23420,162318,16388,18525,131588,23509,24928,137780,154060,132517,23539,23453,19728,23557,138052,23571,29646,23572,138405,158504,23625,18653,23685,23785,23791,23947,138745,138807,23824,23832,23878,138916,23738,24023,33532,14381,149761,139337,139635,33415,14390,15298,24110,27274,24181,24186,148668,134355,21414,20151,24272,21416,137073,24073,24308,164994,24313,24315,14496,24316,26686,37915,24333,131521,194708,15070,18606,135994,24378,157832,140240,24408,140401,24419,38845,159342,24434,37696,166454,24487,23990,15711,152144,139114,159992,140904,37334,131742,166441,24625,26245,137335,14691,15815,13881,22416,141236,31089,15936,24734,24740,24755,149890,149903,162387,29860,20705,23200,24932,33828,24898,194726,159442,24961,20980,132694,24967,23466,147383,141407,25043,166813,170333,25040,14642,141696,141505,24611,24924,25886,25483,131352,25285,137072,25301,142861,25452,149983,14871,25656,25592,136078,137212,25744,28554,142902,38932,147596,153373,25825,25829,38011,14950,25658,14935,25933,28438,150056,150051,25989,25965,25951,143486,26037,149824,19255,26065,16600,137257,26080,26083,24543,144384,26136,143863,143864,26180,143780,143781,26187,134773,26215,152038,26227,26228,138813,143921,165364,143816,152339,30661,141559,39332,26370,148380,150049,15147,27130,145346,26462,26471,26466,147917,168173,26583,17641,26658,28240,37436,26625,144358,159136,26717,144495,27105,27147,166623,26995,26819,144845,26881,26880,15666,14849,144956,15232,26540,26977,166474,17148,26934,27032,15265,132041,33635,20624,27129,144985,139562,27205,145155,27293,15347,26545,27336,168348,15373,27421,133411,24798,27445,27508,141261,28341,146139,132021,137560,14144,21537,146266,27617,147196,27612,27703,140427,149745,158545,27738,33318,27769,146876,17605,146877,147876,149772,149760,146633,14053,15595,134450,39811,143865,140433,32655,26679,159013,159137,159211,28054,27996,28284,28420,149887,147589,159346,34099,159604,20935,27804,28189,33838,166689,28207,146991,29779,147330,31180,28239,23185,143435,28664,14093,28573,146992,28410,136343,147517,17749,37872,28484,28508,15694,28532,168304,15675,28575,147780,28627,147601,147797,147513,147440,147380,147775,20959,147798,147799,147776,156125,28747,28798,28839,28801,28876,28885,28886,28895,16644,15848,29108,29078,148087,28971,28997,23176,29002,29038,23708,148325,29007,37730,148161,28972,148570,150055,150050,29114,166888,28861,29198,37954,29205,22801,37955,29220,37697,153093,29230,29248,149876,26813,29269,29271,15957,143428,26637,28477,29314,29482,29483,149539,165931,18669,165892,29480,29486,29647,29610,134202,158254,29641,29769,147938,136935,150052,26147,14021,149943,149901,150011,29687,29717,26883,150054,29753,132547,16087,29788,141485,29792,167602,29767,29668,29814,33721,29804,14128,29812,37873,27180,29826,18771,150156,147807,150137,166799,23366,166915,137374,29896,137608,29966,29929,29982,167641,137803,23511,167596,37765,30029,30026,30055,30062,151426,16132,150803,30094,29789,30110,30132,30210,30252,30289,30287,30319,30326,156661,30352,33263,14328,157969,157966,30369,30373,30391,30412,159647,33890,151709,151933,138780,30494,30502,30528,25775,152096,30552,144044,30639,166244,166248,136897,30708,30729,136054,150034,26826,30895,30919,30931,38565,31022,153056,30935,31028,30897,161292,36792,34948,166699,155779,140828,31110,35072,26882,31104,153687,31133,162617,31036,31145,28202,160038,16040,31174,168205,31188],\"euc-kr\":[44034,44035,44037,44038,44043,44044,44045,44046,44047,44056,44062,44063,44065,44066,44067,44069,44070,44071,44072,44073,44074,44075,44078,44082,44083,44084,null,null,null,null,null,null,44085,44086,44087,44090,44091,44093,44094,44095,44097,44098,44099,44100,44101,44102,44103,44104,44105,44106,44108,44110,44111,44112,44113,44114,44115,44117,null,null,null,null,null,null,44118,44119,44121,44122,44123,44125,44126,44127,44128,44129,44130,44131,44132,44133,44134,44135,44136,44137,44138,44139,44140,44141,44142,44143,44146,44147,44149,44150,44153,44155,44156,44157,44158,44159,44162,44167,44168,44173,44174,44175,44177,44178,44179,44181,44182,44183,44184,44185,44186,44187,44190,44194,44195,44196,44197,44198,44199,44203,44205,44206,44209,44210,44211,44212,44213,44214,44215,44218,44222,44223,44224,44226,44227,44229,44230,44231,44233,44234,44235,44237,44238,44239,44240,44241,44242,44243,44244,44246,44248,44249,44250,44251,44252,44253,44254,44255,44258,44259,44261,44262,44265,44267,44269,44270,44274,44276,44279,44280,44281,44282,44283,44286,44287,44289,44290,44291,44293,44295,44296,44297,44298,44299,44302,44304,44306,44307,44308,44309,44310,44311,44313,44314,44315,44317,44318,44319,44321,44322,44323,44324,44325,44326,44327,44328,44330,44331,44334,44335,44336,44337,44338,44339,null,null,null,null,null,null,44342,44343,44345,44346,44347,44349,44350,44351,44352,44353,44354,44355,44358,44360,44362,44363,44364,44365,44366,44367,44369,44370,44371,44373,44374,44375,null,null,null,null,null,null,44377,44378,44379,44380,44381,44382,44383,44384,44386,44388,44389,44390,44391,44392,44393,44394,44395,44398,44399,44401,44402,44407,44408,44409,44410,44414,44416,44419,44420,44421,44422,44423,44426,44427,44429,44430,44431,44433,44434,44435,44436,44437,44438,44439,44440,44441,44442,44443,44446,44447,44448,44449,44450,44451,44453,44454,44455,44456,44457,44458,44459,44460,44461,44462,44463,44464,44465,44466,44467,44468,44469,44470,44472,44473,44474,44475,44476,44477,44478,44479,44482,44483,44485,44486,44487,44489,44490,44491,44492,44493,44494,44495,44498,44500,44501,44502,44503,44504,44505,44506,44507,44509,44510,44511,44513,44514,44515,44517,44518,44519,44520,44521,44522,44523,44524,44525,44526,44527,44528,44529,44530,44531,44532,44533,44534,44535,44538,44539,44541,44542,44546,44547,44548,44549,44550,44551,44554,44556,44558,44559,44560,44561,44562,44563,44565,44566,44567,44568,44569,44570,44571,44572,null,null,null,null,null,null,44573,44574,44575,44576,44577,44578,44579,44580,44581,44582,44583,44584,44585,44586,44587,44588,44589,44590,44591,44594,44595,44597,44598,44601,44603,44604,null,null,null,null,null,null,44605,44606,44607,44610,44612,44615,44616,44617,44619,44623,44625,44626,44627,44629,44631,44632,44633,44634,44635,44638,44642,44643,44644,44646,44647,44650,44651,44653,44654,44655,44657,44658,44659,44660,44661,44662,44663,44666,44670,44671,44672,44673,44674,44675,44678,44679,44680,44681,44682,44683,44685,44686,44687,44688,44689,44690,44691,44692,44693,44694,44695,44696,44697,44698,44699,44700,44701,44702,44703,44704,44705,44706,44707,44708,44709,44710,44711,44712,44713,44714,44715,44716,44717,44718,44719,44720,44721,44722,44723,44724,44725,44726,44727,44728,44729,44730,44731,44735,44737,44738,44739,44741,44742,44743,44744,44745,44746,44747,44750,44754,44755,44756,44757,44758,44759,44762,44763,44765,44766,44767,44768,44769,44770,44771,44772,44773,44774,44775,44777,44778,44780,44782,44783,44784,44785,44786,44787,44789,44790,44791,44793,44794,44795,44797,44798,44799,44800,44801,44802,44803,44804,44805,null,null,null,null,null,null,44806,44809,44810,44811,44812,44814,44815,44817,44818,44819,44820,44821,44822,44823,44824,44825,44826,44827,44828,44829,44830,44831,44832,44833,44834,44835,null,null,null,null,null,null,44836,44837,44838,44839,44840,44841,44842,44843,44846,44847,44849,44851,44853,44854,44855,44856,44857,44858,44859,44862,44864,44868,44869,44870,44871,44874,44875,44876,44877,44878,44879,44881,44882,44883,44884,44885,44886,44887,44888,44889,44890,44891,44894,44895,44896,44897,44898,44899,44902,44903,44904,44905,44906,44907,44908,44909,44910,44911,44912,44913,44914,44915,44916,44917,44918,44919,44920,44922,44923,44924,44925,44926,44927,44929,44930,44931,44933,44934,44935,44937,44938,44939,44940,44941,44942,44943,44946,44947,44948,44950,44951,44952,44953,44954,44955,44957,44958,44959,44960,44961,44962,44963,44964,44965,44966,44967,44968,44969,44970,44971,44972,44973,44974,44975,44976,44977,44978,44979,44980,44981,44982,44983,44986,44987,44989,44990,44991,44993,44994,44995,44996,44997,44998,45002,45004,45007,45008,45009,45010,45011,45013,45014,45015,45016,45017,45018,45019,45021,45022,45023,45024,45025,null,null,null,null,null,null,45026,45027,45028,45029,45030,45031,45034,45035,45036,45037,45038,45039,45042,45043,45045,45046,45047,45049,45050,45051,45052,45053,45054,45055,45058,45059,null,null,null,null,null,null,45061,45062,45063,45064,45065,45066,45067,45069,45070,45071,45073,45074,45075,45077,45078,45079,45080,45081,45082,45083,45086,45087,45088,45089,45090,45091,45092,45093,45094,45095,45097,45098,45099,45100,45101,45102,45103,45104,45105,45106,45107,45108,45109,45110,45111,45112,45113,45114,45115,45116,45117,45118,45119,45120,45121,45122,45123,45126,45127,45129,45131,45133,45135,45136,45137,45138,45142,45144,45146,45147,45148,45150,45151,45152,45153,45154,45155,45156,45157,45158,45159,45160,45161,45162,45163,45164,45165,45166,45167,45168,45169,45170,45171,45172,45173,45174,45175,45176,45177,45178,45179,45182,45183,45185,45186,45187,45189,45190,45191,45192,45193,45194,45195,45198,45200,45202,45203,45204,45205,45206,45207,45211,45213,45214,45219,45220,45221,45222,45223,45226,45232,45234,45238,45239,45241,45242,45243,45245,45246,45247,45248,45249,45250,45251,45254,45258,45259,45260,45261,45262,45263,45266,null,null,null,null,null,null,45267,45269,45270,45271,45273,45274,45275,45276,45277,45278,45279,45281,45282,45283,45284,45286,45287,45288,45289,45290,45291,45292,45293,45294,45295,45296,null,null,null,null,null,null,45297,45298,45299,45300,45301,45302,45303,45304,45305,45306,45307,45308,45309,45310,45311,45312,45313,45314,45315,45316,45317,45318,45319,45322,45325,45326,45327,45329,45332,45333,45334,45335,45338,45342,45343,45344,45345,45346,45350,45351,45353,45354,45355,45357,45358,45359,45360,45361,45362,45363,45366,45370,45371,45372,45373,45374,45375,45378,45379,45381,45382,45383,45385,45386,45387,45388,45389,45390,45391,45394,45395,45398,45399,45401,45402,45403,45405,45406,45407,45409,45410,45411,45412,45413,45414,45415,45416,45417,45418,45419,45420,45421,45422,45423,45424,45425,45426,45427,45428,45429,45430,45431,45434,45435,45437,45438,45439,45441,45443,45444,45445,45446,45447,45450,45452,45454,45455,45456,45457,45461,45462,45463,45465,45466,45467,45469,45470,45471,45472,45473,45474,45475,45476,45477,45478,45479,45481,45482,45483,45484,45485,45486,45487,45488,45489,45490,45491,45492,45493,45494,45495,45496,null,null,null,null,null,null,45497,45498,45499,45500,45501,45502,45503,45504,45505,45506,45507,45508,45509,45510,45511,45512,45513,45514,45515,45517,45518,45519,45521,45522,45523,45525,null,null,null,null,null,null,45526,45527,45528,45529,45530,45531,45534,45536,45537,45538,45539,45540,45541,45542,45543,45546,45547,45549,45550,45551,45553,45554,45555,45556,45557,45558,45559,45560,45562,45564,45566,45567,45568,45569,45570,45571,45574,45575,45577,45578,45581,45582,45583,45584,45585,45586,45587,45590,45592,45594,45595,45596,45597,45598,45599,45601,45602,45603,45604,45605,45606,45607,45608,45609,45610,45611,45612,45613,45614,45615,45616,45617,45618,45619,45621,45622,45623,45624,45625,45626,45627,45629,45630,45631,45632,45633,45634,45635,45636,45637,45638,45639,45640,45641,45642,45643,45644,45645,45646,45647,45648,45649,45650,45651,45652,45653,45654,45655,45657,45658,45659,45661,45662,45663,45665,45666,45667,45668,45669,45670,45671,45674,45675,45676,45677,45678,45679,45680,45681,45682,45683,45686,45687,45688,45689,45690,45691,45693,45694,45695,45696,45697,45698,45699,45702,45703,45704,45706,45707,45708,45709,45710,null,null,null,null,null,null,45711,45714,45715,45717,45718,45719,45723,45724,45725,45726,45727,45730,45732,45735,45736,45737,45739,45741,45742,45743,45745,45746,45747,45749,45750,45751,null,null,null,null,null,null,45752,45753,45754,45755,45756,45757,45758,45759,45760,45761,45762,45763,45764,45765,45766,45767,45770,45771,45773,45774,45775,45777,45779,45780,45781,45782,45783,45786,45788,45790,45791,45792,45793,45795,45799,45801,45802,45808,45809,45810,45814,45820,45821,45822,45826,45827,45829,45830,45831,45833,45834,45835,45836,45837,45838,45839,45842,45846,45847,45848,45849,45850,45851,45853,45854,45855,45856,45857,45858,45859,45860,45861,45862,45863,45864,45865,45866,45867,45868,45869,45870,45871,45872,45873,45874,45875,45876,45877,45878,45879,45880,45881,45882,45883,45884,45885,45886,45887,45888,45889,45890,45891,45892,45893,45894,45895,45896,45897,45898,45899,45900,45901,45902,45903,45904,45905,45906,45907,45911,45913,45914,45917,45920,45921,45922,45923,45926,45928,45930,45932,45933,45935,45938,45939,45941,45942,45943,45945,45946,45947,45948,45949,45950,45951,45954,45958,45959,45960,45961,45962,45963,45965,null,null,null,null,null,null,45966,45967,45969,45970,45971,45973,45974,45975,45976,45977,45978,45979,45980,45981,45982,45983,45986,45987,45988,45989,45990,45991,45993,45994,45995,45997,null,null,null,null,null,null,45998,45999,46e3,46001,46002,46003,46004,46005,46006,46007,46008,46009,46010,46011,46012,46013,46014,46015,46016,46017,46018,46019,46022,46023,46025,46026,46029,46031,46033,46034,46035,46038,46040,46042,46044,46046,46047,46049,46050,46051,46053,46054,46055,46057,46058,46059,46060,46061,46062,46063,46064,46065,46066,46067,46068,46069,46070,46071,46072,46073,46074,46075,46077,46078,46079,46080,46081,46082,46083,46084,46085,46086,46087,46088,46089,46090,46091,46092,46093,46094,46095,46097,46098,46099,46100,46101,46102,46103,46105,46106,46107,46109,46110,46111,46113,46114,46115,46116,46117,46118,46119,46122,46124,46125,46126,46127,46128,46129,46130,46131,46133,46134,46135,46136,46137,46138,46139,46140,46141,46142,46143,46144,46145,46146,46147,46148,46149,46150,46151,46152,46153,46154,46155,46156,46157,46158,46159,46162,46163,46165,46166,46167,46169,46170,46171,46172,46173,46174,46175,46178,46180,46182,null,null,null,null,null,null,46183,46184,46185,46186,46187,46189,46190,46191,46192,46193,46194,46195,46196,46197,46198,46199,46200,46201,46202,46203,46204,46205,46206,46207,46209,46210,null,null,null,null,null,null,46211,46212,46213,46214,46215,46217,46218,46219,46220,46221,46222,46223,46224,46225,46226,46227,46228,46229,46230,46231,46232,46233,46234,46235,46236,46238,46239,46240,46241,46242,46243,46245,46246,46247,46249,46250,46251,46253,46254,46255,46256,46257,46258,46259,46260,46262,46264,46266,46267,46268,46269,46270,46271,46273,46274,46275,46277,46278,46279,46281,46282,46283,46284,46285,46286,46287,46289,46290,46291,46292,46294,46295,46296,46297,46298,46299,46302,46303,46305,46306,46309,46311,46312,46313,46314,46315,46318,46320,46322,46323,46324,46325,46326,46327,46329,46330,46331,46332,46333,46334,46335,46336,46337,46338,46339,46340,46341,46342,46343,46344,46345,46346,46347,46348,46349,46350,46351,46352,46353,46354,46355,46358,46359,46361,46362,46365,46366,46367,46368,46369,46370,46371,46374,46379,46380,46381,46382,46383,46386,46387,46389,46390,46391,46393,46394,46395,46396,46397,46398,46399,46402,46406,null,null,null,null,null,null,46407,46408,46409,46410,46414,46415,46417,46418,46419,46421,46422,46423,46424,46425,46426,46427,46430,46434,46435,46436,46437,46438,46439,46440,46441,46442,null,null,null,null,null,null,46443,46444,46445,46446,46447,46448,46449,46450,46451,46452,46453,46454,46455,46456,46457,46458,46459,46460,46461,46462,46463,46464,46465,46466,46467,46468,46469,46470,46471,46472,46473,46474,46475,46476,46477,46478,46479,46480,46481,46482,46483,46484,46485,46486,46487,46488,46489,46490,46491,46492,46493,46494,46495,46498,46499,46501,46502,46503,46505,46508,46509,46510,46511,46514,46518,46519,46520,46521,46522,46526,46527,46529,46530,46531,46533,46534,46535,46536,46537,46538,46539,46542,46546,46547,46548,46549,46550,46551,46553,46554,46555,46556,46557,46558,46559,46560,46561,46562,46563,46564,46565,46566,46567,46568,46569,46570,46571,46573,46574,46575,46576,46577,46578,46579,46580,46581,46582,46583,46584,46585,46586,46587,46588,46589,46590,46591,46592,46593,46594,46595,46596,46597,46598,46599,46600,46601,46602,46603,46604,46605,46606,46607,46610,46611,46613,46614,46615,46617,46618,46619,46620,46621,null,null,null,null,null,null,46622,46623,46624,46625,46626,46627,46628,46630,46631,46632,46633,46634,46635,46637,46638,46639,46640,46641,46642,46643,46645,46646,46647,46648,46649,46650,null,null,null,null,null,null,46651,46652,46653,46654,46655,46656,46657,46658,46659,46660,46661,46662,46663,46665,46666,46667,46668,46669,46670,46671,46672,46673,46674,46675,46676,46677,46678,46679,46680,46681,46682,46683,46684,46685,46686,46687,46688,46689,46690,46691,46693,46694,46695,46697,46698,46699,46700,46701,46702,46703,46704,46705,46706,46707,46708,46709,46710,46711,46712,46713,46714,46715,46716,46717,46718,46719,46720,46721,46722,46723,46724,46725,46726,46727,46728,46729,46730,46731,46732,46733,46734,46735,46736,46737,46738,46739,46740,46741,46742,46743,46744,46745,46746,46747,46750,46751,46753,46754,46755,46757,46758,46759,46760,46761,46762,46765,46766,46767,46768,46770,46771,46772,46773,46774,46775,46776,46777,46778,46779,46780,46781,46782,46783,46784,46785,46786,46787,46788,46789,46790,46791,46792,46793,46794,46795,46796,46797,46798,46799,46800,46801,46802,46803,46805,46806,46807,46808,46809,46810,46811,46812,46813,null,null,null,null,null,null,46814,46815,46816,46817,46818,46819,46820,46821,46822,46823,46824,46825,46826,46827,46828,46829,46830,46831,46833,46834,46835,46837,46838,46839,46841,46842,null,null,null,null,null,null,46843,46844,46845,46846,46847,46850,46851,46852,46854,46855,46856,46857,46858,46859,46860,46861,46862,46863,46864,46865,46866,46867,46868,46869,46870,46871,46872,46873,46874,46875,46876,46877,46878,46879,46880,46881,46882,46883,46884,46885,46886,46887,46890,46891,46893,46894,46897,46898,46899,46900,46901,46902,46903,46906,46908,46909,46910,46911,46912,46913,46914,46915,46917,46918,46919,46921,46922,46923,46925,46926,46927,46928,46929,46930,46931,46934,46935,46936,46937,46938,46939,46940,46941,46942,46943,46945,46946,46947,46949,46950,46951,46953,46954,46955,46956,46957,46958,46959,46962,46964,46966,46967,46968,46969,46970,46971,46974,46975,46977,46978,46979,46981,46982,46983,46984,46985,46986,46987,46990,46995,46996,46997,47002,47003,47005,47006,47007,47009,47010,47011,47012,47013,47014,47015,47018,47022,47023,47024,47025,47026,47027,47030,47031,47033,47034,47035,47036,47037,47038,47039,47040,47041,null,null,null,null,null,null,47042,47043,47044,47045,47046,47048,47050,47051,47052,47053,47054,47055,47056,47057,47058,47059,47060,47061,47062,47063,47064,47065,47066,47067,47068,47069,null,null,null,null,null,null,47070,47071,47072,47073,47074,47075,47076,47077,47078,47079,47080,47081,47082,47083,47086,47087,47089,47090,47091,47093,47094,47095,47096,47097,47098,47099,47102,47106,47107,47108,47109,47110,47114,47115,47117,47118,47119,47121,47122,47123,47124,47125,47126,47127,47130,47132,47134,47135,47136,47137,47138,47139,47142,47143,47145,47146,47147,47149,47150,47151,47152,47153,47154,47155,47158,47162,47163,47164,47165,47166,47167,47169,47170,47171,47173,47174,47175,47176,47177,47178,47179,47180,47181,47182,47183,47184,47186,47188,47189,47190,47191,47192,47193,47194,47195,47198,47199,47201,47202,47203,47205,47206,47207,47208,47209,47210,47211,47214,47216,47218,47219,47220,47221,47222,47223,47225,47226,47227,47229,47230,47231,47232,47233,47234,47235,47236,47237,47238,47239,47240,47241,47242,47243,47244,47246,47247,47248,47249,47250,47251,47252,47253,47254,47255,47256,47257,47258,47259,47260,47261,47262,47263,null,null,null,null,null,null,47264,47265,47266,47267,47268,47269,47270,47271,47273,47274,47275,47276,47277,47278,47279,47281,47282,47283,47285,47286,47287,47289,47290,47291,47292,47293,null,null,null,null,null,null,47294,47295,47298,47300,47302,47303,47304,47305,47306,47307,47309,47310,47311,47313,47314,47315,47317,47318,47319,47320,47321,47322,47323,47324,47326,47328,47330,47331,47332,47333,47334,47335,47338,47339,47341,47342,47343,47345,47346,47347,47348,47349,47350,47351,47354,47356,47358,47359,47360,47361,47362,47363,47365,47366,47367,47368,47369,47370,47371,47372,47373,47374,47375,47376,47377,47378,47379,47380,47381,47382,47383,47385,47386,47387,47388,47389,47390,47391,47393,47394,47395,47396,47397,47398,47399,47400,47401,47402,47403,47404,47405,47406,47407,47408,47409,47410,47411,47412,47413,47414,47415,47416,47417,47418,47419,47422,47423,47425,47426,47427,47429,47430,47431,47432,47433,47434,47435,47437,47438,47440,47442,47443,47444,47445,47446,47447,47450,47451,47453,47454,47455,47457,47458,47459,47460,47461,47462,47463,47466,47468,47470,47471,47472,47473,47474,47475,47478,47479,47481,47482,47483,47485,null,null,null,null,null,null,47486,47487,47488,47489,47490,47491,47494,47496,47499,47500,47503,47504,47505,47506,47507,47508,47509,47510,47511,47512,47513,47514,47515,47516,47517,47518,null,null,null,null,null,null,47519,47520,47521,47522,47523,47524,47525,47526,47527,47528,47529,47530,47531,47534,47535,47537,47538,47539,47541,47542,47543,47544,47545,47546,47547,47550,47552,47554,47555,47556,47557,47558,47559,47562,47563,47565,47571,47572,47573,47574,47575,47578,47580,47583,47584,47586,47590,47591,47593,47594,47595,47597,47598,47599,47600,47601,47602,47603,47606,47611,47612,47613,47614,47615,47618,47619,47620,47621,47622,47623,47625,47626,47627,47628,47629,47630,47631,47632,47633,47634,47635,47636,47638,47639,47640,47641,47642,47643,47644,47645,47646,47647,47648,47649,47650,47651,47652,47653,47654,47655,47656,47657,47658,47659,47660,47661,47662,47663,47664,47665,47666,47667,47668,47669,47670,47671,47674,47675,47677,47678,47679,47681,47683,47684,47685,47686,47687,47690,47692,47695,47696,47697,47698,47702,47703,47705,47706,47707,47709,47710,47711,47712,47713,47714,47715,47718,47722,47723,47724,47725,47726,47727,null,null,null,null,null,null,47730,47731,47733,47734,47735,47737,47738,47739,47740,47741,47742,47743,47744,47745,47746,47750,47752,47753,47754,47755,47757,47758,47759,47760,47761,47762,null,null,null,null,null,null,47763,47764,47765,47766,47767,47768,47769,47770,47771,47772,47773,47774,47775,47776,47777,47778,47779,47780,47781,47782,47783,47786,47789,47790,47791,47793,47795,47796,47797,47798,47799,47802,47804,47806,47807,47808,47809,47810,47811,47813,47814,47815,47817,47818,47819,47820,47821,47822,47823,47824,47825,47826,47827,47828,47829,47830,47831,47834,47835,47836,47837,47838,47839,47840,47841,47842,47843,47844,47845,47846,47847,47848,47849,47850,47851,47852,47853,47854,47855,47856,47857,47858,47859,47860,47861,47862,47863,47864,47865,47866,47867,47869,47870,47871,47873,47874,47875,47877,47878,47879,47880,47881,47882,47883,47884,47886,47888,47890,47891,47892,47893,47894,47895,47897,47898,47899,47901,47902,47903,47905,47906,47907,47908,47909,47910,47911,47912,47914,47916,47917,47918,47919,47920,47921,47922,47923,47927,47929,47930,47935,47936,47937,47938,47939,47942,47944,47946,47947,47948,47950,47953,47954,null,null,null,null,null,null,47955,47957,47958,47959,47961,47962,47963,47964,47965,47966,47967,47968,47970,47972,47973,47974,47975,47976,47977,47978,47979,47981,47982,47983,47984,47985,null,null,null,null,null,null,47986,47987,47988,47989,47990,47991,47992,47993,47994,47995,47996,47997,47998,47999,48e3,48001,48002,48003,48004,48005,48006,48007,48009,48010,48011,48013,48014,48015,48017,48018,48019,48020,48021,48022,48023,48024,48025,48026,48027,48028,48029,48030,48031,48032,48033,48034,48035,48037,48038,48039,48041,48042,48043,48045,48046,48047,48048,48049,48050,48051,48053,48054,48056,48057,48058,48059,48060,48061,48062,48063,48065,48066,48067,48069,48070,48071,48073,48074,48075,48076,48077,48078,48079,48081,48082,48084,48085,48086,48087,48088,48089,48090,48091,48092,48093,48094,48095,48096,48097,48098,48099,48100,48101,48102,48103,48104,48105,48106,48107,48108,48109,48110,48111,48112,48113,48114,48115,48116,48117,48118,48119,48122,48123,48125,48126,48129,48131,48132,48133,48134,48135,48138,48142,48144,48146,48147,48153,48154,48160,48161,48162,48163,48166,48168,48170,48171,48172,48174,48175,48178,48179,48181,null,null,null,null,null,null,48182,48183,48185,48186,48187,48188,48189,48190,48191,48194,48198,48199,48200,48202,48203,48206,48207,48209,48210,48211,48212,48213,48214,48215,48216,48217,null,null,null,null,null,null,48218,48219,48220,48222,48223,48224,48225,48226,48227,48228,48229,48230,48231,48232,48233,48234,48235,48236,48237,48238,48239,48240,48241,48242,48243,48244,48245,48246,48247,48248,48249,48250,48251,48252,48253,48254,48255,48256,48257,48258,48259,48262,48263,48265,48266,48269,48271,48272,48273,48274,48275,48278,48280,48283,48284,48285,48286,48287,48290,48291,48293,48294,48297,48298,48299,48300,48301,48302,48303,48306,48310,48311,48312,48313,48314,48315,48318,48319,48321,48322,48323,48325,48326,48327,48328,48329,48330,48331,48332,48334,48338,48339,48340,48342,48343,48345,48346,48347,48349,48350,48351,48352,48353,48354,48355,48356,48357,48358,48359,48360,48361,48362,48363,48364,48365,48366,48367,48368,48369,48370,48371,48375,48377,48378,48379,48381,48382,48383,48384,48385,48386,48387,48390,48392,48394,48395,48396,48397,48398,48399,48401,48402,48403,48405,48406,48407,48408,48409,48410,48411,48412,48413,null,null,null,null,null,null,48414,48415,48416,48417,48418,48419,48421,48422,48423,48424,48425,48426,48427,48429,48430,48431,48432,48433,48434,48435,48436,48437,48438,48439,48440,48441,null,null,null,null,null,null,48442,48443,48444,48445,48446,48447,48449,48450,48451,48452,48453,48454,48455,48458,48459,48461,48462,48463,48465,48466,48467,48468,48469,48470,48471,48474,48475,48476,48477,48478,48479,48480,48481,48482,48483,48485,48486,48487,48489,48490,48491,48492,48493,48494,48495,48496,48497,48498,48499,48500,48501,48502,48503,48504,48505,48506,48507,48508,48509,48510,48511,48514,48515,48517,48518,48523,48524,48525,48526,48527,48530,48532,48534,48535,48536,48539,48541,48542,48543,48544,48545,48546,48547,48549,48550,48551,48552,48553,48554,48555,48556,48557,48558,48559,48561,48562,48563,48564,48565,48566,48567,48569,48570,48571,48572,48573,48574,48575,48576,48577,48578,48579,48580,48581,48582,48583,48584,48585,48586,48587,48588,48589,48590,48591,48592,48593,48594,48595,48598,48599,48601,48602,48603,48605,48606,48607,48608,48609,48610,48611,48612,48613,48614,48615,48616,48618,48619,48620,48621,48622,48623,48625,null,null,null,null,null,null,48626,48627,48629,48630,48631,48633,48634,48635,48636,48637,48638,48639,48641,48642,48644,48646,48647,48648,48649,48650,48651,48654,48655,48657,48658,48659,null,null,null,null,null,null,48661,48662,48663,48664,48665,48666,48667,48670,48672,48673,48674,48675,48676,48677,48678,48679,48680,48681,48682,48683,48684,48685,48686,48687,48688,48689,48690,48691,48692,48693,48694,48695,48696,48697,48698,48699,48700,48701,48702,48703,48704,48705,48706,48707,48710,48711,48713,48714,48715,48717,48719,48720,48721,48722,48723,48726,48728,48732,48733,48734,48735,48738,48739,48741,48742,48743,48745,48747,48748,48749,48750,48751,48754,48758,48759,48760,48761,48762,48766,48767,48769,48770,48771,48773,48774,48775,48776,48777,48778,48779,48782,48786,48787,48788,48789,48790,48791,48794,48795,48796,48797,48798,48799,48800,48801,48802,48803,48804,48805,48806,48807,48809,48810,48811,48812,48813,48814,48815,48816,48817,48818,48819,48820,48821,48822,48823,48824,48825,48826,48827,48828,48829,48830,48831,48832,48833,48834,48835,48836,48837,48838,48839,48840,48841,48842,48843,48844,48845,48846,48847,48850,48851,null,null,null,null,null,null,48853,48854,48857,48858,48859,48860,48861,48862,48863,48865,48866,48870,48871,48872,48873,48874,48875,48877,48878,48879,48880,48881,48882,48883,48884,48885,null,null,null,null,null,null,48886,48887,48888,48889,48890,48891,48892,48893,48894,48895,48896,48898,48899,48900,48901,48902,48903,48906,48907,48908,48909,48910,48911,48912,48913,48914,48915,48916,48917,48918,48919,48922,48926,48927,48928,48929,48930,48931,48932,48933,48934,48935,48936,48937,48938,48939,48940,48941,48942,48943,48944,48945,48946,48947,48948,48949,48950,48951,48952,48953,48954,48955,48956,48957,48958,48959,48962,48963,48965,48966,48967,48969,48970,48971,48972,48973,48974,48975,48978,48979,48980,48982,48983,48984,48985,48986,48987,48988,48989,48990,48991,48992,48993,48994,48995,48996,48997,48998,48999,49e3,49001,49002,49003,49004,49005,49006,49007,49008,49009,49010,49011,49012,49013,49014,49015,49016,49017,49018,49019,49020,49021,49022,49023,49024,49025,49026,49027,49028,49029,49030,49031,49032,49033,49034,49035,49036,49037,49038,49039,49040,49041,49042,49043,49045,49046,49047,49048,49049,49050,49051,49052,49053,null,null,null,null,null,null,49054,49055,49056,49057,49058,49059,49060,49061,49062,49063,49064,49065,49066,49067,49068,49069,49070,49071,49073,49074,49075,49076,49077,49078,49079,49080,null,null,null,null,null,null,49081,49082,49083,49084,49085,49086,49087,49088,49089,49090,49091,49092,49094,49095,49096,49097,49098,49099,49102,49103,49105,49106,49107,49109,49110,49111,49112,49113,49114,49115,49117,49118,49120,49122,49123,49124,49125,49126,49127,49128,49129,49130,49131,49132,49133,49134,49135,49136,49137,49138,49139,49140,49141,49142,49143,49144,49145,49146,49147,49148,49149,49150,49151,49152,49153,49154,49155,49156,49157,49158,49159,49160,49161,49162,49163,49164,49165,49166,49167,49168,49169,49170,49171,49172,49173,49174,49175,49176,49177,49178,49179,49180,49181,49182,49183,49184,49185,49186,49187,49188,49189,49190,49191,49192,49193,49194,49195,49196,49197,49198,49199,49200,49201,49202,49203,49204,49205,49206,49207,49208,49209,49210,49211,49213,49214,49215,49216,49217,49218,49219,49220,49221,49222,49223,49224,49225,49226,49227,49228,49229,49230,49231,49232,49234,49235,49236,49237,49238,49239,49241,49242,49243,null,null,null,null,null,null,49245,49246,49247,49249,49250,49251,49252,49253,49254,49255,49258,49259,49260,49261,49262,49263,49264,49265,49266,49267,49268,49269,49270,49271,49272,49273,null,null,null,null,null,null,49274,49275,49276,49277,49278,49279,49280,49281,49282,49283,49284,49285,49286,49287,49288,49289,49290,49291,49292,49293,49294,49295,49298,49299,49301,49302,49303,49305,49306,49307,49308,49309,49310,49311,49314,49316,49318,49319,49320,49321,49322,49323,49326,49329,49330,49335,49336,49337,49338,49339,49342,49346,49347,49348,49350,49351,49354,49355,49357,49358,49359,49361,49362,49363,49364,49365,49366,49367,49370,49374,49375,49376,49377,49378,49379,49382,49383,49385,49386,49387,49389,49390,49391,49392,49393,49394,49395,49398,49400,49402,49403,49404,49405,49406,49407,49409,49410,49411,49413,49414,49415,49417,49418,49419,49420,49421,49422,49423,49425,49426,49427,49428,49430,49431,49432,49433,49434,49435,49441,49442,49445,49448,49449,49450,49451,49454,49458,49459,49460,49461,49463,49466,49467,49469,49470,49471,49473,49474,49475,49476,49477,49478,49479,49482,49486,49487,49488,49489,49490,49491,49494,49495,null,null,null,null,null,null,49497,49498,49499,49501,49502,49503,49504,49505,49506,49507,49510,49514,49515,49516,49517,49518,49519,49521,49522,49523,49525,49526,49527,49529,49530,49531,null,null,null,null,null,null,49532,49533,49534,49535,49536,49537,49538,49539,49540,49542,49543,49544,49545,49546,49547,49551,49553,49554,49555,49557,49559,49560,49561,49562,49563,49566,49568,49570,49571,49572,49574,49575,49578,49579,49581,49582,49583,49585,49586,49587,49588,49589,49590,49591,49592,49593,49594,49595,49596,49598,49599,49600,49601,49602,49603,49605,49606,49607,49609,49610,49611,49613,49614,49615,49616,49617,49618,49619,49621,49622,49625,49626,49627,49628,49629,49630,49631,49633,49634,49635,49637,49638,49639,49641,49642,49643,49644,49645,49646,49647,49650,49652,49653,49654,49655,49656,49657,49658,49659,49662,49663,49665,49666,49667,49669,49670,49671,49672,49673,49674,49675,49678,49680,49682,49683,49684,49685,49686,49687,49690,49691,49693,49694,49697,49698,49699,49700,49701,49702,49703,49706,49708,49710,49712,49715,49717,49718,49719,49720,49721,49722,49723,49724,49725,49726,49727,49728,49729,49730,49731,49732,49733,null,null,null,null,null,null,49734,49735,49737,49738,49739,49740,49741,49742,49743,49746,49747,49749,49750,49751,49753,49754,49755,49756,49757,49758,49759,49761,49762,49763,49764,49766,null,null,null,null,null,null,49767,49768,49769,49770,49771,49774,49775,49777,49778,49779,49781,49782,49783,49784,49785,49786,49787,49790,49792,49794,49795,49796,49797,49798,49799,49802,49803,49804,49805,49806,49807,49809,49810,49811,49812,49813,49814,49815,49817,49818,49820,49822,49823,49824,49825,49826,49827,49830,49831,49833,49834,49835,49838,49839,49840,49841,49842,49843,49846,49848,49850,49851,49852,49853,49854,49855,49856,49857,49858,49859,49860,49861,49862,49863,49864,49865,49866,49867,49868,49869,49870,49871,49872,49873,49874,49875,49876,49877,49878,49879,49880,49881,49882,49883,49886,49887,49889,49890,49893,49894,49895,49896,49897,49898,49902,49904,49906,49907,49908,49909,49911,49914,49917,49918,49919,49921,49922,49923,49924,49925,49926,49927,49930,49931,49934,49935,49936,49937,49938,49942,49943,49945,49946,49947,49949,49950,49951,49952,49953,49954,49955,49958,49959,49962,49963,49964,49965,49966,49967,49968,49969,49970,null,null,null,null,null,null,49971,49972,49973,49974,49975,49976,49977,49978,49979,49980,49981,49982,49983,49984,49985,49986,49987,49988,49990,49991,49992,49993,49994,49995,49996,49997,null,null,null,null,null,null,49998,49999,5e4,50001,50002,50003,50004,50005,50006,50007,50008,50009,50010,50011,50012,50013,50014,50015,50016,50017,50018,50019,50020,50021,50022,50023,50026,50027,50029,50030,50031,50033,50035,50036,50037,50038,50039,50042,50043,50046,50047,50048,50049,50050,50051,50053,50054,50055,50057,50058,50059,50061,50062,50063,50064,50065,50066,50067,50068,50069,50070,50071,50072,50073,50074,50075,50076,50077,50078,50079,50080,50081,50082,50083,50084,50085,50086,50087,50088,50089,50090,50091,50092,50093,50094,50095,50096,50097,50098,50099,50100,50101,50102,50103,50104,50105,50106,50107,50108,50109,50110,50111,50113,50114,50115,50116,50117,50118,50119,50120,50121,50122,50123,50124,50125,50126,50127,50128,50129,50130,50131,50132,50133,50134,50135,50138,50139,50141,50142,50145,50147,50148,50149,50150,50151,50154,50155,50156,50158,50159,50160,50161,50162,50163,50166,50167,50169,50170,50171,50172,50173,50174,null,null,null,null,null,null,50175,50176,50177,50178,50179,50180,50181,50182,50183,50185,50186,50187,50188,50189,50190,50191,50193,50194,50195,50196,50197,50198,50199,50200,50201,50202,null,null,null,null,null,null,50203,50204,50205,50206,50207,50208,50209,50210,50211,50213,50214,50215,50216,50217,50218,50219,50221,50222,50223,50225,50226,50227,50229,50230,50231,50232,50233,50234,50235,50238,50239,50240,50241,50242,50243,50244,50245,50246,50247,50249,50250,50251,50252,50253,50254,50255,50256,50257,50258,50259,50260,50261,50262,50263,50264,50265,50266,50267,50268,50269,50270,50271,50272,50273,50274,50275,50278,50279,50281,50282,50283,50285,50286,50287,50288,50289,50290,50291,50294,50295,50296,50298,50299,50300,50301,50302,50303,50305,50306,50307,50308,50309,50310,50311,50312,50313,50314,50315,50316,50317,50318,50319,50320,50321,50322,50323,50325,50326,50327,50328,50329,50330,50331,50333,50334,50335,50336,50337,50338,50339,50340,50341,50342,50343,50344,50345,50346,50347,50348,50349,50350,50351,50352,50353,50354,50355,50356,50357,50358,50359,50361,50362,50363,50365,50366,50367,50368,50369,50370,50371,50372,50373,null,null,null,null,null,null,50374,50375,50376,50377,50378,50379,50380,50381,50382,50383,50384,50385,50386,50387,50388,50389,50390,50391,50392,50393,50394,50395,50396,50397,50398,50399,null,null,null,null,null,null,50400,50401,50402,50403,50404,50405,50406,50407,50408,50410,50411,50412,50413,50414,50415,50418,50419,50421,50422,50423,50425,50427,50428,50429,50430,50434,50435,50436,50437,50438,50439,50440,50441,50442,50443,50445,50446,50447,50449,50450,50451,50453,50454,50455,50456,50457,50458,50459,50461,50462,50463,50464,50465,50466,50467,50468,50469,50470,50471,50474,50475,50477,50478,50479,50481,50482,50483,50484,50485,50486,50487,50490,50492,50494,50495,50496,50497,50498,50499,50502,50503,50507,50511,50512,50513,50514,50518,50522,50523,50524,50527,50530,50531,50533,50534,50535,50537,50538,50539,50540,50541,50542,50543,50546,50550,50551,50552,50553,50554,50555,50558,50559,50561,50562,50563,50565,50566,50568,50569,50570,50571,50574,50576,50578,50579,50580,50582,50585,50586,50587,50589,50590,50591,50593,50594,50595,50596,50597,50598,50599,50600,50602,50603,50604,50605,50606,50607,50608,50609,50610,50611,50614,null,null,null,null,null,null,50615,50618,50623,50624,50625,50626,50627,50635,50637,50639,50642,50643,50645,50646,50647,50649,50650,50651,50652,50653,50654,50655,50658,50660,50662,50663,null,null,null,null,null,null,50664,50665,50666,50667,50671,50673,50674,50675,50677,50680,50681,50682,50683,50690,50691,50692,50697,50698,50699,50701,50702,50703,50705,50706,50707,50708,50709,50710,50711,50714,50717,50718,50719,50720,50721,50722,50723,50726,50727,50729,50730,50731,50735,50737,50738,50742,50744,50746,50748,50749,50750,50751,50754,50755,50757,50758,50759,50761,50762,50763,50764,50765,50766,50767,50770,50774,50775,50776,50777,50778,50779,50782,50783,50785,50786,50787,50788,50789,50790,50791,50792,50793,50794,50795,50797,50798,50800,50802,50803,50804,50805,50806,50807,50810,50811,50813,50814,50815,50817,50818,50819,50820,50821,50822,50823,50826,50828,50830,50831,50832,50833,50834,50835,50838,50839,50841,50842,50843,50845,50846,50847,50848,50849,50850,50851,50854,50856,50858,50859,50860,50861,50862,50863,50866,50867,50869,50870,50871,50875,50876,50877,50878,50879,50882,50884,50886,50887,50888,50889,50890,50891,50894,null,null,null,null,null,null,50895,50897,50898,50899,50901,50902,50903,50904,50905,50906,50907,50910,50911,50914,50915,50916,50917,50918,50919,50922,50923,50925,50926,50927,50929,50930,null,null,null,null,null,null,50931,50932,50933,50934,50935,50938,50939,50940,50942,50943,50944,50945,50946,50947,50950,50951,50953,50954,50955,50957,50958,50959,50960,50961,50962,50963,50966,50968,50970,50971,50972,50973,50974,50975,50978,50979,50981,50982,50983,50985,50986,50987,50988,50989,50990,50991,50994,50996,50998,51e3,51001,51002,51003,51006,51007,51009,51010,51011,51013,51014,51015,51016,51017,51019,51022,51024,51033,51034,51035,51037,51038,51039,51041,51042,51043,51044,51045,51046,51047,51049,51050,51052,51053,51054,51055,51056,51057,51058,51059,51062,51063,51065,51066,51067,51071,51072,51073,51074,51078,51083,51084,51085,51087,51090,51091,51093,51097,51099,51100,51101,51102,51103,51106,51111,51112,51113,51114,51115,51118,51119,51121,51122,51123,51125,51126,51127,51128,51129,51130,51131,51134,51138,51139,51140,51141,51142,51143,51146,51147,51149,51151,51153,51154,51155,51156,51157,51158,51159,51161,51162,51163,51164,null,null,null,null,null,null,51166,51167,51168,51169,51170,51171,51173,51174,51175,51177,51178,51179,51181,51182,51183,51184,51185,51186,51187,51188,51189,51190,51191,51192,51193,51194,null,null,null,null,null,null,51195,51196,51197,51198,51199,51202,51203,51205,51206,51207,51209,51211,51212,51213,51214,51215,51218,51220,51223,51224,51225,51226,51227,51230,51231,51233,51234,51235,51237,51238,51239,51240,51241,51242,51243,51246,51248,51250,51251,51252,51253,51254,51255,51257,51258,51259,51261,51262,51263,51265,51266,51267,51268,51269,51270,51271,51274,51275,51278,51279,51280,51281,51282,51283,51285,51286,51287,51288,51289,51290,51291,51292,51293,51294,51295,51296,51297,51298,51299,51300,51301,51302,51303,51304,51305,51306,51307,51308,51309,51310,51311,51314,51315,51317,51318,51319,51321,51323,51324,51325,51326,51327,51330,51332,51336,51337,51338,51342,51343,51344,51345,51346,51347,51349,51350,51351,51352,51353,51354,51355,51356,51358,51360,51362,51363,51364,51365,51366,51367,51369,51370,51371,51372,51373,51374,51375,51376,51377,51378,51379,51380,51381,51382,51383,51384,51385,51386,51387,51390,51391,51392,51393,null,null,null,null,null,null,51394,51395,51397,51398,51399,51401,51402,51403,51405,51406,51407,51408,51409,51410,51411,51414,51416,51418,51419,51420,51421,51422,51423,51426,51427,51429,null,null,null,null,null,null,51430,51431,51432,51433,51434,51435,51436,51437,51438,51439,51440,51441,51442,51443,51444,51446,51447,51448,51449,51450,51451,51454,51455,51457,51458,51459,51463,51464,51465,51466,51467,51470,12288,12289,12290,183,8229,8230,168,12291,173,8213,8741,65340,8764,8216,8217,8220,8221,12308,12309,12296,12297,12298,12299,12300,12301,12302,12303,12304,12305,177,215,247,8800,8804,8805,8734,8756,176,8242,8243,8451,8491,65504,65505,65509,9794,9792,8736,8869,8978,8706,8711,8801,8786,167,8251,9734,9733,9675,9679,9678,9671,9670,9633,9632,9651,9650,9661,9660,8594,8592,8593,8595,8596,12307,8810,8811,8730,8765,8733,8757,8747,8748,8712,8715,8838,8839,8834,8835,8746,8745,8743,8744,65506,51472,51474,51475,51476,51477,51478,51479,51481,51482,51483,51484,51485,51486,51487,51488,51489,51490,51491,51492,51493,51494,51495,51496,51497,51498,51499,null,null,null,null,null,null,51501,51502,51503,51504,51505,51506,51507,51509,51510,51511,51512,51513,51514,51515,51516,51517,51518,51519,51520,51521,51522,51523,51524,51525,51526,51527,null,null,null,null,null,null,51528,51529,51530,51531,51532,51533,51534,51535,51538,51539,51541,51542,51543,51545,51546,51547,51548,51549,51550,51551,51554,51556,51557,51558,51559,51560,51561,51562,51563,51565,51566,51567,8658,8660,8704,8707,180,65374,711,728,733,730,729,184,731,161,191,720,8750,8721,8719,164,8457,8240,9665,9664,9655,9654,9828,9824,9825,9829,9831,9827,8857,9672,9635,9680,9681,9618,9636,9637,9640,9639,9638,9641,9832,9743,9742,9756,9758,182,8224,8225,8597,8599,8601,8598,8600,9837,9833,9834,9836,12927,12828,8470,13255,8482,13250,13272,8481,8364,174,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,51569,51570,51571,51573,51574,51575,51576,51577,51578,51579,51581,51582,51583,51584,51585,51586,51587,51588,51589,51590,51591,51594,51595,51597,51598,51599,null,null,null,null,null,null,51601,51602,51603,51604,51605,51606,51607,51610,51612,51614,51615,51616,51617,51618,51619,51620,51621,51622,51623,51624,51625,51626,51627,51628,51629,51630,null,null,null,null,null,null,51631,51632,51633,51634,51635,51636,51637,51638,51639,51640,51641,51642,51643,51644,51645,51646,51647,51650,51651,51653,51654,51657,51659,51660,51661,51662,51663,51666,51668,51671,51672,51675,65281,65282,65283,65284,65285,65286,65287,65288,65289,65290,65291,65292,65293,65294,65295,65296,65297,65298,65299,65300,65301,65302,65303,65304,65305,65306,65307,65308,65309,65310,65311,65312,65313,65314,65315,65316,65317,65318,65319,65320,65321,65322,65323,65324,65325,65326,65327,65328,65329,65330,65331,65332,65333,65334,65335,65336,65337,65338,65339,65510,65341,65342,65343,65344,65345,65346,65347,65348,65349,65350,65351,65352,65353,65354,65355,65356,65357,65358,65359,65360,65361,65362,65363,65364,65365,65366,65367,65368,65369,65370,65371,65372,65373,65507,51678,51679,51681,51683,51685,51686,51688,51689,51690,51691,51694,51698,51699,51700,51701,51702,51703,51706,51707,51709,51710,51711,51713,51714,51715,51716,null,null,null,null,null,null,51717,51718,51719,51722,51726,51727,51728,51729,51730,51731,51733,51734,51735,51737,51738,51739,51740,51741,51742,51743,51744,51745,51746,51747,51748,51749,null,null,null,null,null,null,51750,51751,51752,51754,51755,51756,51757,51758,51759,51760,51761,51762,51763,51764,51765,51766,51767,51768,51769,51770,51771,51772,51773,51774,51775,51776,51777,51778,51779,51780,51781,51782,12593,12594,12595,12596,12597,12598,12599,12600,12601,12602,12603,12604,12605,12606,12607,12608,12609,12610,12611,12612,12613,12614,12615,12616,12617,12618,12619,12620,12621,12622,12623,12624,12625,12626,12627,12628,12629,12630,12631,12632,12633,12634,12635,12636,12637,12638,12639,12640,12641,12642,12643,12644,12645,12646,12647,12648,12649,12650,12651,12652,12653,12654,12655,12656,12657,12658,12659,12660,12661,12662,12663,12664,12665,12666,12667,12668,12669,12670,12671,12672,12673,12674,12675,12676,12677,12678,12679,12680,12681,12682,12683,12684,12685,12686,51783,51784,51785,51786,51787,51790,51791,51793,51794,51795,51797,51798,51799,51800,51801,51802,51803,51806,51810,51811,51812,51813,51814,51815,51817,51818,null,null,null,null,null,null,51819,51820,51821,51822,51823,51824,51825,51826,51827,51828,51829,51830,51831,51832,51833,51834,51835,51836,51838,51839,51840,51841,51842,51843,51845,51846,null,null,null,null,null,null,51847,51848,51849,51850,51851,51852,51853,51854,51855,51856,51857,51858,51859,51860,51861,51862,51863,51865,51866,51867,51868,51869,51870,51871,51872,51873,51874,51875,51876,51877,51878,51879,8560,8561,8562,8563,8564,8565,8566,8567,8568,8569,null,null,null,null,null,8544,8545,8546,8547,8548,8549,8550,8551,8552,8553,null,null,null,null,null,null,null,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,931,932,933,934,935,936,937,null,null,null,null,null,null,null,null,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,963,964,965,966,967,968,969,null,null,null,null,null,null,51880,51881,51882,51883,51884,51885,51886,51887,51888,51889,51890,51891,51892,51893,51894,51895,51896,51897,51898,51899,51902,51903,51905,51906,51907,51909,null,null,null,null,null,null,51910,51911,51912,51913,51914,51915,51918,51920,51922,51924,51925,51926,51927,51930,51931,51932,51933,51934,51935,51937,51938,51939,51940,51941,51942,51943,null,null,null,null,null,null,51944,51945,51946,51947,51949,51950,51951,51952,51953,51954,51955,51957,51958,51959,51960,51961,51962,51963,51964,51965,51966,51967,51968,51969,51970,51971,51972,51973,51974,51975,51977,51978,9472,9474,9484,9488,9496,9492,9500,9516,9508,9524,9532,9473,9475,9487,9491,9499,9495,9507,9523,9515,9531,9547,9504,9519,9512,9527,9535,9501,9520,9509,9528,9538,9490,9489,9498,9497,9494,9493,9486,9485,9502,9503,9505,9506,9510,9511,9513,9514,9517,9518,9521,9522,9525,9526,9529,9530,9533,9534,9536,9537,9539,9540,9541,9542,9543,9544,9545,9546,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,51979,51980,51981,51982,51983,51985,51986,51987,51989,51990,51991,51993,51994,51995,51996,51997,51998,51999,52002,52003,52004,52005,52006,52007,52008,52009,null,null,null,null,null,null,52010,52011,52012,52013,52014,52015,52016,52017,52018,52019,52020,52021,52022,52023,52024,52025,52026,52027,52028,52029,52030,52031,52032,52034,52035,52036,null,null,null,null,null,null,52037,52038,52039,52042,52043,52045,52046,52047,52049,52050,52051,52052,52053,52054,52055,52058,52059,52060,52062,52063,52064,52065,52066,52067,52069,52070,52071,52072,52073,52074,52075,52076,13205,13206,13207,8467,13208,13252,13219,13220,13221,13222,13209,13210,13211,13212,13213,13214,13215,13216,13217,13218,13258,13197,13198,13199,13263,13192,13193,13256,13223,13224,13232,13233,13234,13235,13236,13237,13238,13239,13240,13241,13184,13185,13186,13187,13188,13242,13243,13244,13245,13246,13247,13200,13201,13202,13203,13204,8486,13248,13249,13194,13195,13196,13270,13253,13229,13230,13231,13275,13225,13226,13227,13228,13277,13264,13267,13251,13257,13276,13254,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,52077,52078,52079,52080,52081,52082,52083,52084,52085,52086,52087,52090,52091,52092,52093,52094,52095,52096,52097,52098,52099,52100,52101,52102,52103,52104,null,null,null,null,null,null,52105,52106,52107,52108,52109,52110,52111,52112,52113,52114,52115,52116,52117,52118,52119,52120,52121,52122,52123,52125,52126,52127,52128,52129,52130,52131,null,null,null,null,null,null,52132,52133,52134,52135,52136,52137,52138,52139,52140,52141,52142,52143,52144,52145,52146,52147,52148,52149,52150,52151,52153,52154,52155,52156,52157,52158,52159,52160,52161,52162,52163,52164,198,208,170,294,null,306,null,319,321,216,338,186,222,358,330,null,12896,12897,12898,12899,12900,12901,12902,12903,12904,12905,12906,12907,12908,12909,12910,12911,12912,12913,12914,12915,12916,12917,12918,12919,12920,12921,12922,12923,9424,9425,9426,9427,9428,9429,9430,9431,9432,9433,9434,9435,9436,9437,9438,9439,9440,9441,9442,9443,9444,9445,9446,9447,9448,9449,9312,9313,9314,9315,9316,9317,9318,9319,9320,9321,9322,9323,9324,9325,9326,189,8531,8532,188,190,8539,8540,8541,8542,52165,52166,52167,52168,52169,52170,52171,52172,52173,52174,52175,52176,52177,52178,52179,52181,52182,52183,52184,52185,52186,52187,52188,52189,52190,52191,null,null,null,null,null,null,52192,52193,52194,52195,52197,52198,52200,52202,52203,52204,52205,52206,52207,52208,52209,52210,52211,52212,52213,52214,52215,52216,52217,52218,52219,52220,null,null,null,null,null,null,52221,52222,52223,52224,52225,52226,52227,52228,52229,52230,52231,52232,52233,52234,52235,52238,52239,52241,52242,52243,52245,52246,52247,52248,52249,52250,52251,52254,52255,52256,52259,52260,230,273,240,295,305,307,312,320,322,248,339,223,254,359,331,329,12800,12801,12802,12803,12804,12805,12806,12807,12808,12809,12810,12811,12812,12813,12814,12815,12816,12817,12818,12819,12820,12821,12822,12823,12824,12825,12826,12827,9372,9373,9374,9375,9376,9377,9378,9379,9380,9381,9382,9383,9384,9385,9386,9387,9388,9389,9390,9391,9392,9393,9394,9395,9396,9397,9332,9333,9334,9335,9336,9337,9338,9339,9340,9341,9342,9343,9344,9345,9346,185,178,179,8308,8319,8321,8322,8323,8324,52261,52262,52266,52267,52269,52271,52273,52274,52275,52276,52277,52278,52279,52282,52287,52288,52289,52290,52291,52294,52295,52297,52298,52299,52301,52302,null,null,null,null,null,null,52303,52304,52305,52306,52307,52310,52314,52315,52316,52317,52318,52319,52321,52322,52323,52325,52327,52329,52330,52331,52332,52333,52334,52335,52337,52338,null,null,null,null,null,null,52339,52340,52342,52343,52344,52345,52346,52347,52348,52349,52350,52351,52352,52353,52354,52355,52356,52357,52358,52359,52360,52361,52362,52363,52364,52365,52366,52367,52368,52369,52370,52371,12353,12354,12355,12356,12357,12358,12359,12360,12361,12362,12363,12364,12365,12366,12367,12368,12369,12370,12371,12372,12373,12374,12375,12376,12377,12378,12379,12380,12381,12382,12383,12384,12385,12386,12387,12388,12389,12390,12391,12392,12393,12394,12395,12396,12397,12398,12399,12400,12401,12402,12403,12404,12405,12406,12407,12408,12409,12410,12411,12412,12413,12414,12415,12416,12417,12418,12419,12420,12421,12422,12423,12424,12425,12426,12427,12428,12429,12430,12431,12432,12433,12434,12435,null,null,null,null,null,null,null,null,null,null,null,52372,52373,52374,52375,52378,52379,52381,52382,52383,52385,52386,52387,52388,52389,52390,52391,52394,52398,52399,52400,52401,52402,52403,52406,52407,52409,null,null,null,null,null,null,52410,52411,52413,52414,52415,52416,52417,52418,52419,52422,52424,52426,52427,52428,52429,52430,52431,52433,52434,52435,52437,52438,52439,52440,52441,52442,null,null,null,null,null,null,52443,52444,52445,52446,52447,52448,52449,52450,52451,52453,52454,52455,52456,52457,52458,52459,52461,52462,52463,52465,52466,52467,52468,52469,52470,52471,52472,52473,52474,52475,52476,52477,12449,12450,12451,12452,12453,12454,12455,12456,12457,12458,12459,12460,12461,12462,12463,12464,12465,12466,12467,12468,12469,12470,12471,12472,12473,12474,12475,12476,12477,12478,12479,12480,12481,12482,12483,12484,12485,12486,12487,12488,12489,12490,12491,12492,12493,12494,12495,12496,12497,12498,12499,12500,12501,12502,12503,12504,12505,12506,12507,12508,12509,12510,12511,12512,12513,12514,12515,12516,12517,12518,12519,12520,12521,12522,12523,12524,12525,12526,12527,12528,12529,12530,12531,12532,12533,12534,null,null,null,null,null,null,null,null,52478,52479,52480,52482,52483,52484,52485,52486,52487,52490,52491,52493,52494,52495,52497,52498,52499,52500,52501,52502,52503,52506,52508,52510,52511,52512,null,null,null,null,null,null,52513,52514,52515,52517,52518,52519,52521,52522,52523,52525,52526,52527,52528,52529,52530,52531,52532,52533,52534,52535,52536,52538,52539,52540,52541,52542,null,null,null,null,null,null,52543,52544,52545,52546,52547,52548,52549,52550,52551,52552,52553,52554,52555,52556,52557,52558,52559,52560,52561,52562,52563,52564,52565,52566,52567,52568,52569,52570,52571,52573,52574,52575,1040,1041,1042,1043,1044,1045,1025,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,1072,1073,1074,1075,1076,1077,1105,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,null,null,null,null,null,null,null,null,null,null,null,null,null,52577,52578,52579,52581,52582,52583,52584,52585,52586,52587,52590,52592,52594,52595,52596,52597,52598,52599,52601,52602,52603,52604,52605,52606,52607,52608,null,null,null,null,null,null,52609,52610,52611,52612,52613,52614,52615,52617,52618,52619,52620,52621,52622,52623,52624,52625,52626,52627,52630,52631,52633,52634,52635,52637,52638,52639,null,null,null,null,null,null,52640,52641,52642,52643,52646,52648,52650,52651,52652,52653,52654,52655,52657,52658,52659,52660,52661,52662,52663,52664,52665,52666,52667,52668,52669,52670,52671,52672,52673,52674,52675,52677,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,52678,52679,52680,52681,52682,52683,52685,52686,52687,52689,52690,52691,52692,52693,52694,52695,52696,52697,52698,52699,52700,52701,52702,52703,52704,52705,null,null,null,null,null,null,52706,52707,52708,52709,52710,52711,52713,52714,52715,52717,52718,52719,52721,52722,52723,52724,52725,52726,52727,52730,52732,52734,52735,52736,52737,52738,null,null,null,null,null,null,52739,52741,52742,52743,52745,52746,52747,52749,52750,52751,52752,52753,52754,52755,52757,52758,52759,52760,52762,52763,52764,52765,52766,52767,52770,52771,52773,52774,52775,52777,52778,52779,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,52780,52781,52782,52783,52786,52788,52790,52791,52792,52793,52794,52795,52796,52797,52798,52799,52800,52801,52802,52803,52804,52805,52806,52807,52808,52809,null,null,null,null,null,null,52810,52811,52812,52813,52814,52815,52816,52817,52818,52819,52820,52821,52822,52823,52826,52827,52829,52830,52834,52835,52836,52837,52838,52839,52842,52844,null,null,null,null,null,null,52846,52847,52848,52849,52850,52851,52854,52855,52857,52858,52859,52861,52862,52863,52864,52865,52866,52867,52870,52872,52874,52875,52876,52877,52878,52879,52882,52883,52885,52886,52887,52889,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,52890,52891,52892,52893,52894,52895,52898,52902,52903,52904,52905,52906,52907,52910,52911,52912,52913,52914,52915,52916,52917,52918,52919,52920,52921,52922,null,null,null,null,null,null,52923,52924,52925,52926,52927,52928,52930,52931,52932,52933,52934,52935,52936,52937,52938,52939,52940,52941,52942,52943,52944,52945,52946,52947,52948,52949,null,null,null,null,null,null,52950,52951,52952,52953,52954,52955,52956,52957,52958,52959,52960,52961,52962,52963,52966,52967,52969,52970,52973,52974,52975,52976,52977,52978,52979,52982,52986,52987,52988,52989,52990,52991,44032,44033,44036,44039,44040,44041,44042,44048,44049,44050,44051,44052,44053,44054,44055,44057,44058,44059,44060,44061,44064,44068,44076,44077,44079,44080,44081,44088,44089,44092,44096,44107,44109,44116,44120,44124,44144,44145,44148,44151,44152,44154,44160,44161,44163,44164,44165,44166,44169,44170,44171,44172,44176,44180,44188,44189,44191,44192,44193,44200,44201,44202,44204,44207,44208,44216,44217,44219,44220,44221,44225,44228,44232,44236,44245,44247,44256,44257,44260,44263,44264,44266,44268,44271,44272,44273,44275,44277,44278,44284,44285,44288,44292,44294,52994,52995,52997,52998,52999,53001,53002,53003,53004,53005,53006,53007,53010,53012,53014,53015,53016,53017,53018,53019,53021,53022,53023,53025,53026,53027,null,null,null,null,null,null,53029,53030,53031,53032,53033,53034,53035,53038,53042,53043,53044,53045,53046,53047,53049,53050,53051,53052,53053,53054,53055,53056,53057,53058,53059,53060,null,null,null,null,null,null,53061,53062,53063,53064,53065,53066,53067,53068,53069,53070,53071,53072,53073,53074,53075,53078,53079,53081,53082,53083,53085,53086,53087,53088,53089,53090,53091,53094,53096,53098,53099,53100,44300,44301,44303,44305,44312,44316,44320,44329,44332,44333,44340,44341,44344,44348,44356,44357,44359,44361,44368,44372,44376,44385,44387,44396,44397,44400,44403,44404,44405,44406,44411,44412,44413,44415,44417,44418,44424,44425,44428,44432,44444,44445,44452,44471,44480,44481,44484,44488,44496,44497,44499,44508,44512,44516,44536,44537,44540,44543,44544,44545,44552,44553,44555,44557,44564,44592,44593,44596,44599,44600,44602,44608,44609,44611,44613,44614,44618,44620,44621,44622,44624,44628,44630,44636,44637,44639,44640,44641,44645,44648,44649,44652,44656,44664,53101,53102,53103,53106,53107,53109,53110,53111,53113,53114,53115,53116,53117,53118,53119,53121,53122,53123,53124,53126,53127,53128,53129,53130,53131,53133,null,null,null,null,null,null,53134,53135,53136,53137,53138,53139,53140,53141,53142,53143,53144,53145,53146,53147,53148,53149,53150,53151,53152,53154,53155,53156,53157,53158,53159,53161,null,null,null,null,null,null,53162,53163,53164,53165,53166,53167,53169,53170,53171,53172,53173,53174,53175,53176,53177,53178,53179,53180,53181,53182,53183,53184,53185,53186,53187,53189,53190,53191,53192,53193,53194,53195,44665,44667,44668,44669,44676,44677,44684,44732,44733,44734,44736,44740,44748,44749,44751,44752,44753,44760,44761,44764,44776,44779,44781,44788,44792,44796,44807,44808,44813,44816,44844,44845,44848,44850,44852,44860,44861,44863,44865,44866,44867,44872,44873,44880,44892,44893,44900,44901,44921,44928,44932,44936,44944,44945,44949,44956,44984,44985,44988,44992,44999,45e3,45001,45003,45005,45006,45012,45020,45032,45033,45040,45041,45044,45048,45056,45057,45060,45068,45072,45076,45084,45085,45096,45124,45125,45128,45130,45132,45134,45139,45140,45141,45143,45145,53196,53197,53198,53199,53200,53201,53202,53203,53204,53205,53206,53207,53208,53209,53210,53211,53212,53213,53214,53215,53218,53219,53221,53222,53223,53225,null,null,null,null,null,null,53226,53227,53228,53229,53230,53231,53234,53236,53238,53239,53240,53241,53242,53243,53245,53246,53247,53249,53250,53251,53253,53254,53255,53256,53257,53258,null,null,null,null,null,null,53259,53260,53261,53262,53263,53264,53266,53267,53268,53269,53270,53271,53273,53274,53275,53276,53277,53278,53279,53280,53281,53282,53283,53284,53285,53286,53287,53288,53289,53290,53291,53292,45149,45180,45181,45184,45188,45196,45197,45199,45201,45208,45209,45210,45212,45215,45216,45217,45218,45224,45225,45227,45228,45229,45230,45231,45233,45235,45236,45237,45240,45244,45252,45253,45255,45256,45257,45264,45265,45268,45272,45280,45285,45320,45321,45323,45324,45328,45330,45331,45336,45337,45339,45340,45341,45347,45348,45349,45352,45356,45364,45365,45367,45368,45369,45376,45377,45380,45384,45392,45393,45396,45397,45400,45404,45408,45432,45433,45436,45440,45442,45448,45449,45451,45453,45458,45459,45460,45464,45468,45480,45516,45520,45524,45532,45533,53294,53295,53296,53297,53298,53299,53302,53303,53305,53306,53307,53309,53310,53311,53312,53313,53314,53315,53318,53320,53322,53323,53324,53325,53326,53327,null,null,null,null,null,null,53329,53330,53331,53333,53334,53335,53337,53338,53339,53340,53341,53342,53343,53345,53346,53347,53348,53349,53350,53351,53352,53353,53354,53355,53358,53359,null,null,null,null,null,null,53361,53362,53363,53365,53366,53367,53368,53369,53370,53371,53374,53375,53376,53378,53379,53380,53381,53382,53383,53384,53385,53386,53387,53388,53389,53390,53391,53392,53393,53394,53395,53396,45535,45544,45545,45548,45552,45561,45563,45565,45572,45573,45576,45579,45580,45588,45589,45591,45593,45600,45620,45628,45656,45660,45664,45672,45673,45684,45685,45692,45700,45701,45705,45712,45713,45716,45720,45721,45722,45728,45729,45731,45733,45734,45738,45740,45744,45748,45768,45769,45772,45776,45778,45784,45785,45787,45789,45794,45796,45797,45798,45800,45803,45804,45805,45806,45807,45811,45812,45813,45815,45816,45817,45818,45819,45823,45824,45825,45828,45832,45840,45841,45843,45844,45845,45852,45908,45909,45910,45912,45915,45916,45918,45919,45924,45925,53397,53398,53399,53400,53401,53402,53403,53404,53405,53406,53407,53408,53409,53410,53411,53414,53415,53417,53418,53419,53421,53422,53423,53424,53425,53426,null,null,null,null,null,null,53427,53430,53432,53434,53435,53436,53437,53438,53439,53442,53443,53445,53446,53447,53450,53451,53452,53453,53454,53455,53458,53462,53463,53464,53465,53466,null,null,null,null,null,null,53467,53470,53471,53473,53474,53475,53477,53478,53479,53480,53481,53482,53483,53486,53490,53491,53492,53493,53494,53495,53497,53498,53499,53500,53501,53502,53503,53504,53505,53506,53507,53508,45927,45929,45931,45934,45936,45937,45940,45944,45952,45953,45955,45956,45957,45964,45968,45972,45984,45985,45992,45996,46020,46021,46024,46027,46028,46030,46032,46036,46037,46039,46041,46043,46045,46048,46052,46056,46076,46096,46104,46108,46112,46120,46121,46123,46132,46160,46161,46164,46168,46176,46177,46179,46181,46188,46208,46216,46237,46244,46248,46252,46261,46263,46265,46272,46276,46280,46288,46293,46300,46301,46304,46307,46308,46310,46316,46317,46319,46321,46328,46356,46357,46360,46363,46364,46372,46373,46375,46376,46377,46378,46384,46385,46388,46392,53509,53510,53511,53512,53513,53514,53515,53516,53518,53519,53520,53521,53522,53523,53524,53525,53526,53527,53528,53529,53530,53531,53532,53533,53534,53535,null,null,null,null,null,null,53536,53537,53538,53539,53540,53541,53542,53543,53544,53545,53546,53547,53548,53549,53550,53551,53554,53555,53557,53558,53559,53561,53563,53564,53565,53566,null,null,null,null,null,null,53567,53570,53574,53575,53576,53577,53578,53579,53582,53583,53585,53586,53587,53589,53590,53591,53592,53593,53594,53595,53598,53600,53602,53603,53604,53605,53606,53607,53609,53610,53611,53613,46400,46401,46403,46404,46405,46411,46412,46413,46416,46420,46428,46429,46431,46432,46433,46496,46497,46500,46504,46506,46507,46512,46513,46515,46516,46517,46523,46524,46525,46528,46532,46540,46541,46543,46544,46545,46552,46572,46608,46609,46612,46616,46629,46636,46644,46664,46692,46696,46748,46749,46752,46756,46763,46764,46769,46804,46832,46836,46840,46848,46849,46853,46888,46889,46892,46895,46896,46904,46905,46907,46916,46920,46924,46932,46933,46944,46948,46952,46960,46961,46963,46965,46972,46973,46976,46980,46988,46989,46991,46992,46993,46994,46998,46999,53614,53615,53616,53617,53618,53619,53620,53621,53622,53623,53624,53625,53626,53627,53629,53630,53631,53632,53633,53634,53635,53637,53638,53639,53641,53642,null,null,null,null,null,null,53643,53644,53645,53646,53647,53648,53649,53650,53651,53652,53653,53654,53655,53656,53657,53658,53659,53660,53661,53662,53663,53666,53667,53669,53670,53671,null,null,null,null,null,null,53673,53674,53675,53676,53677,53678,53679,53682,53684,53686,53687,53688,53689,53691,53693,53694,53695,53697,53698,53699,53700,53701,53702,53703,53704,53705,53706,53707,53708,53709,53710,53711,47e3,47001,47004,47008,47016,47017,47019,47020,47021,47028,47029,47032,47047,47049,47084,47085,47088,47092,47100,47101,47103,47104,47105,47111,47112,47113,47116,47120,47128,47129,47131,47133,47140,47141,47144,47148,47156,47157,47159,47160,47161,47168,47172,47185,47187,47196,47197,47200,47204,47212,47213,47215,47217,47224,47228,47245,47272,47280,47284,47288,47296,47297,47299,47301,47308,47312,47316,47325,47327,47329,47336,47337,47340,47344,47352,47353,47355,47357,47364,47384,47392,47420,47421,47424,47428,47436,47439,47441,47448,47449,47452,47456,47464,47465,53712,53713,53714,53715,53716,53717,53718,53719,53721,53722,53723,53724,53725,53726,53727,53728,53729,53730,53731,53732,53733,53734,53735,53736,53737,53738,null,null,null,null,null,null,53739,53740,53741,53742,53743,53744,53745,53746,53747,53749,53750,53751,53753,53754,53755,53756,53757,53758,53759,53760,53761,53762,53763,53764,53765,53766,null,null,null,null,null,null,53768,53770,53771,53772,53773,53774,53775,53777,53778,53779,53780,53781,53782,53783,53784,53785,53786,53787,53788,53789,53790,53791,53792,53793,53794,53795,53796,53797,53798,53799,53800,53801,47467,47469,47476,47477,47480,47484,47492,47493,47495,47497,47498,47501,47502,47532,47533,47536,47540,47548,47549,47551,47553,47560,47561,47564,47566,47567,47568,47569,47570,47576,47577,47579,47581,47582,47585,47587,47588,47589,47592,47596,47604,47605,47607,47608,47609,47610,47616,47617,47624,47637,47672,47673,47676,47680,47682,47688,47689,47691,47693,47694,47699,47700,47701,47704,47708,47716,47717,47719,47720,47721,47728,47729,47732,47736,47747,47748,47749,47751,47756,47784,47785,47787,47788,47792,47794,47800,47801,47803,47805,47812,47816,47832,47833,47868,53802,53803,53806,53807,53809,53810,53811,53813,53814,53815,53816,53817,53818,53819,53822,53824,53826,53827,53828,53829,53830,53831,53833,53834,53835,53836,null,null,null,null,null,null,53837,53838,53839,53840,53841,53842,53843,53844,53845,53846,53847,53848,53849,53850,53851,53853,53854,53855,53856,53857,53858,53859,53861,53862,53863,53864,null,null,null,null,null,null,53865,53866,53867,53868,53869,53870,53871,53872,53873,53874,53875,53876,53877,53878,53879,53880,53881,53882,53883,53884,53885,53886,53887,53890,53891,53893,53894,53895,53897,53898,53899,53900,47872,47876,47885,47887,47889,47896,47900,47904,47913,47915,47924,47925,47926,47928,47931,47932,47933,47934,47940,47941,47943,47945,47949,47951,47952,47956,47960,47969,47971,47980,48008,48012,48016,48036,48040,48044,48052,48055,48064,48068,48072,48080,48083,48120,48121,48124,48127,48128,48130,48136,48137,48139,48140,48141,48143,48145,48148,48149,48150,48151,48152,48155,48156,48157,48158,48159,48164,48165,48167,48169,48173,48176,48177,48180,48184,48192,48193,48195,48196,48197,48201,48204,48205,48208,48221,48260,48261,48264,48267,48268,48270,48276,48277,48279,53901,53902,53903,53906,53907,53908,53910,53911,53912,53913,53914,53915,53917,53918,53919,53921,53922,53923,53925,53926,53927,53928,53929,53930,53931,53933,null,null,null,null,null,null,53934,53935,53936,53938,53939,53940,53941,53942,53943,53946,53947,53949,53950,53953,53955,53956,53957,53958,53959,53962,53964,53965,53966,53967,53968,53969,null,null,null,null,null,null,53970,53971,53973,53974,53975,53977,53978,53979,53981,53982,53983,53984,53985,53986,53987,53990,53991,53992,53993,53994,53995,53996,53997,53998,53999,54002,54003,54005,54006,54007,54009,54010,48281,48282,48288,48289,48292,48295,48296,48304,48305,48307,48308,48309,48316,48317,48320,48324,48333,48335,48336,48337,48341,48344,48348,48372,48373,48374,48376,48380,48388,48389,48391,48393,48400,48404,48420,48428,48448,48456,48457,48460,48464,48472,48473,48484,48488,48512,48513,48516,48519,48520,48521,48522,48528,48529,48531,48533,48537,48538,48540,48548,48560,48568,48596,48597,48600,48604,48617,48624,48628,48632,48640,48643,48645,48652,48653,48656,48660,48668,48669,48671,48708,48709,48712,48716,48718,48724,48725,48727,48729,48730,48731,48736,48737,48740,54011,54012,54013,54014,54015,54018,54020,54022,54023,54024,54025,54026,54027,54031,54033,54034,54035,54037,54039,54040,54041,54042,54043,54046,54050,54051,null,null,null,null,null,null,54052,54054,54055,54058,54059,54061,54062,54063,54065,54066,54067,54068,54069,54070,54071,54074,54078,54079,54080,54081,54082,54083,54086,54087,54088,54089,null,null,null,null,null,null,54090,54091,54092,54093,54094,54095,54096,54097,54098,54099,54100,54101,54102,54103,54104,54105,54106,54107,54108,54109,54110,54111,54112,54113,54114,54115,54116,54117,54118,54119,54120,54121,48744,48746,48752,48753,48755,48756,48757,48763,48764,48765,48768,48772,48780,48781,48783,48784,48785,48792,48793,48808,48848,48849,48852,48855,48856,48864,48867,48868,48869,48876,48897,48904,48905,48920,48921,48923,48924,48925,48960,48961,48964,48968,48976,48977,48981,49044,49072,49093,49100,49101,49104,49108,49116,49119,49121,49212,49233,49240,49244,49248,49256,49257,49296,49297,49300,49304,49312,49313,49315,49317,49324,49325,49327,49328,49331,49332,49333,49334,49340,49341,49343,49344,49345,49349,49352,49353,49356,49360,49368,49369,49371,49372,49373,49380,54122,54123,54124,54125,54126,54127,54128,54129,54130,54131,54132,54133,54134,54135,54136,54137,54138,54139,54142,54143,54145,54146,54147,54149,54150,54151,null,null,null,null,null,null,54152,54153,54154,54155,54158,54162,54163,54164,54165,54166,54167,54170,54171,54173,54174,54175,54177,54178,54179,54180,54181,54182,54183,54186,54188,54190,null,null,null,null,null,null,54191,54192,54193,54194,54195,54197,54198,54199,54201,54202,54203,54205,54206,54207,54208,54209,54210,54211,54214,54215,54218,54219,54220,54221,54222,54223,54225,54226,54227,54228,54229,54230,49381,49384,49388,49396,49397,49399,49401,49408,49412,49416,49424,49429,49436,49437,49438,49439,49440,49443,49444,49446,49447,49452,49453,49455,49456,49457,49462,49464,49465,49468,49472,49480,49481,49483,49484,49485,49492,49493,49496,49500,49508,49509,49511,49512,49513,49520,49524,49528,49541,49548,49549,49550,49552,49556,49558,49564,49565,49567,49569,49573,49576,49577,49580,49584,49597,49604,49608,49612,49620,49623,49624,49632,49636,49640,49648,49649,49651,49660,49661,49664,49668,49676,49677,49679,49681,49688,49689,49692,49695,49696,49704,49705,49707,49709,54231,54233,54234,54235,54236,54237,54238,54239,54240,54242,54244,54245,54246,54247,54248,54249,54250,54251,54254,54255,54257,54258,54259,54261,54262,54263,null,null,null,null,null,null,54264,54265,54266,54267,54270,54272,54274,54275,54276,54277,54278,54279,54281,54282,54283,54284,54285,54286,54287,54288,54289,54290,54291,54292,54293,54294,null,null,null,null,null,null,54295,54296,54297,54298,54299,54300,54302,54303,54304,54305,54306,54307,54308,54309,54310,54311,54312,54313,54314,54315,54316,54317,54318,54319,54320,54321,54322,54323,54324,54325,54326,54327,49711,49713,49714,49716,49736,49744,49745,49748,49752,49760,49765,49772,49773,49776,49780,49788,49789,49791,49793,49800,49801,49808,49816,49819,49821,49828,49829,49832,49836,49837,49844,49845,49847,49849,49884,49885,49888,49891,49892,49899,49900,49901,49903,49905,49910,49912,49913,49915,49916,49920,49928,49929,49932,49933,49939,49940,49941,49944,49948,49956,49957,49960,49961,49989,50024,50025,50028,50032,50034,50040,50041,50044,50045,50052,50056,50060,50112,50136,50137,50140,50143,50144,50146,50152,50153,50157,50164,50165,50168,50184,50192,50212,50220,50224,54328,54329,54330,54331,54332,54333,54334,54335,54337,54338,54339,54341,54342,54343,54344,54345,54346,54347,54348,54349,54350,54351,54352,54353,54354,54355,null,null,null,null,null,null,54356,54357,54358,54359,54360,54361,54362,54363,54365,54366,54367,54369,54370,54371,54373,54374,54375,54376,54377,54378,54379,54380,54382,54384,54385,54386,null,null,null,null,null,null,54387,54388,54389,54390,54391,54394,54395,54397,54398,54401,54403,54404,54405,54406,54407,54410,54412,54414,54415,54416,54417,54418,54419,54421,54422,54423,54424,54425,54426,54427,54428,54429,50228,50236,50237,50248,50276,50277,50280,50284,50292,50293,50297,50304,50324,50332,50360,50364,50409,50416,50417,50420,50424,50426,50431,50432,50433,50444,50448,50452,50460,50472,50473,50476,50480,50488,50489,50491,50493,50500,50501,50504,50505,50506,50508,50509,50510,50515,50516,50517,50519,50520,50521,50525,50526,50528,50529,50532,50536,50544,50545,50547,50548,50549,50556,50557,50560,50564,50567,50572,50573,50575,50577,50581,50583,50584,50588,50592,50601,50612,50613,50616,50617,50619,50620,50621,50622,50628,50629,50630,50631,50632,50633,50634,50636,50638,54430,54431,54432,54433,54434,54435,54436,54437,54438,54439,54440,54442,54443,54444,54445,54446,54447,54448,54449,54450,54451,54452,54453,54454,54455,54456,null,null,null,null,null,null,54457,54458,54459,54460,54461,54462,54463,54464,54465,54466,54467,54468,54469,54470,54471,54472,54473,54474,54475,54477,54478,54479,54481,54482,54483,54485,null,null,null,null,null,null,54486,54487,54488,54489,54490,54491,54493,54494,54496,54497,54498,54499,54500,54501,54502,54503,54505,54506,54507,54509,54510,54511,54513,54514,54515,54516,54517,54518,54519,54521,54522,54524,50640,50641,50644,50648,50656,50657,50659,50661,50668,50669,50670,50672,50676,50678,50679,50684,50685,50686,50687,50688,50689,50693,50694,50695,50696,50700,50704,50712,50713,50715,50716,50724,50725,50728,50732,50733,50734,50736,50739,50740,50741,50743,50745,50747,50752,50753,50756,50760,50768,50769,50771,50772,50773,50780,50781,50784,50796,50799,50801,50808,50809,50812,50816,50824,50825,50827,50829,50836,50837,50840,50844,50852,50853,50855,50857,50864,50865,50868,50872,50873,50874,50880,50881,50883,50885,50892,50893,50896,50900,50908,50909,50912,50913,50920,54526,54527,54528,54529,54530,54531,54533,54534,54535,54537,54538,54539,54541,54542,54543,54544,54545,54546,54547,54550,54552,54553,54554,54555,54556,54557,null,null,null,null,null,null,54558,54559,54560,54561,54562,54563,54564,54565,54566,54567,54568,54569,54570,54571,54572,54573,54574,54575,54576,54577,54578,54579,54580,54581,54582,54583,null,null,null,null,null,null,54584,54585,54586,54587,54590,54591,54593,54594,54595,54597,54598,54599,54600,54601,54602,54603,54606,54608,54610,54611,54612,54613,54614,54615,54618,54619,54621,54622,54623,54625,54626,54627,50921,50924,50928,50936,50937,50941,50948,50949,50952,50956,50964,50965,50967,50969,50976,50977,50980,50984,50992,50993,50995,50997,50999,51004,51005,51008,51012,51018,51020,51021,51023,51025,51026,51027,51028,51029,51030,51031,51032,51036,51040,51048,51051,51060,51061,51064,51068,51069,51070,51075,51076,51077,51079,51080,51081,51082,51086,51088,51089,51092,51094,51095,51096,51098,51104,51105,51107,51108,51109,51110,51116,51117,51120,51124,51132,51133,51135,51136,51137,51144,51145,51148,51150,51152,51160,51165,51172,51176,51180,51200,51201,51204,51208,51210,54628,54630,54631,54634,54636,54638,54639,54640,54641,54642,54643,54646,54647,54649,54650,54651,54653,54654,54655,54656,54657,54658,54659,54662,54666,54667,null,null,null,null,null,null,54668,54669,54670,54671,54673,54674,54675,54676,54677,54678,54679,54680,54681,54682,54683,54684,54685,54686,54687,54688,54689,54690,54691,54692,54694,54695,null,null,null,null,null,null,54696,54697,54698,54699,54700,54701,54702,54703,54704,54705,54706,54707,54708,54709,54710,54711,54712,54713,54714,54715,54716,54717,54718,54719,54720,54721,54722,54723,54724,54725,54726,54727,51216,51217,51219,51221,51222,51228,51229,51232,51236,51244,51245,51247,51249,51256,51260,51264,51272,51273,51276,51277,51284,51312,51313,51316,51320,51322,51328,51329,51331,51333,51334,51335,51339,51340,51341,51348,51357,51359,51361,51368,51388,51389,51396,51400,51404,51412,51413,51415,51417,51424,51425,51428,51445,51452,51453,51456,51460,51461,51462,51468,51469,51471,51473,51480,51500,51508,51536,51537,51540,51544,51552,51553,51555,51564,51568,51572,51580,51592,51593,51596,51600,51608,51609,51611,51613,51648,51649,51652,51655,51656,51658,51664,51665,51667,54730,54731,54733,54734,54735,54737,54739,54740,54741,54742,54743,54746,54748,54750,54751,54752,54753,54754,54755,54758,54759,54761,54762,54763,54765,54766,null,null,null,null,null,null,54767,54768,54769,54770,54771,54774,54776,54778,54779,54780,54781,54782,54783,54786,54787,54789,54790,54791,54793,54794,54795,54796,54797,54798,54799,54802,null,null,null,null,null,null,54806,54807,54808,54809,54810,54811,54813,54814,54815,54817,54818,54819,54821,54822,54823,54824,54825,54826,54827,54828,54830,54831,54832,54833,54834,54835,54836,54837,54838,54839,54842,54843,51669,51670,51673,51674,51676,51677,51680,51682,51684,51687,51692,51693,51695,51696,51697,51704,51705,51708,51712,51720,51721,51723,51724,51725,51732,51736,51753,51788,51789,51792,51796,51804,51805,51807,51808,51809,51816,51837,51844,51864,51900,51901,51904,51908,51916,51917,51919,51921,51923,51928,51929,51936,51948,51956,51976,51984,51988,51992,52e3,52001,52033,52040,52041,52044,52048,52056,52057,52061,52068,52088,52089,52124,52152,52180,52196,52199,52201,52236,52237,52240,52244,52252,52253,52257,52258,52263,52264,52265,52268,52270,52272,52280,52281,52283,54845,54846,54847,54849,54850,54851,54852,54854,54855,54858,54860,54862,54863,54864,54866,54867,54870,54871,54873,54874,54875,54877,54878,54879,54880,54881,null,null,null,null,null,null,54882,54883,54884,54885,54886,54888,54890,54891,54892,54893,54894,54895,54898,54899,54901,54902,54903,54904,54905,54906,54907,54908,54909,54910,54911,54912,null,null,null,null,null,null,54913,54914,54916,54918,54919,54920,54921,54922,54923,54926,54927,54929,54930,54931,54933,54934,54935,54936,54937,54938,54939,54940,54942,54944,54946,54947,54948,54949,54950,54951,54953,54954,52284,52285,52286,52292,52293,52296,52300,52308,52309,52311,52312,52313,52320,52324,52326,52328,52336,52341,52376,52377,52380,52384,52392,52393,52395,52396,52397,52404,52405,52408,52412,52420,52421,52423,52425,52432,52436,52452,52460,52464,52481,52488,52489,52492,52496,52504,52505,52507,52509,52516,52520,52524,52537,52572,52576,52580,52588,52589,52591,52593,52600,52616,52628,52629,52632,52636,52644,52645,52647,52649,52656,52676,52684,52688,52712,52716,52720,52728,52729,52731,52733,52740,52744,52748,52756,52761,52768,52769,52772,52776,52784,52785,52787,52789,54955,54957,54958,54959,54961,54962,54963,54964,54965,54966,54967,54968,54970,54972,54973,54974,54975,54976,54977,54978,54979,54982,54983,54985,54986,54987,null,null,null,null,null,null,54989,54990,54991,54992,54994,54995,54997,54998,55e3,55002,55003,55004,55005,55006,55007,55009,55010,55011,55013,55014,55015,55017,55018,55019,55020,55021,null,null,null,null,null,null,55022,55023,55025,55026,55027,55028,55030,55031,55032,55033,55034,55035,55038,55039,55041,55042,55043,55045,55046,55047,55048,55049,55050,55051,55052,55053,55054,55055,55056,55058,55059,55060,52824,52825,52828,52831,52832,52833,52840,52841,52843,52845,52852,52853,52856,52860,52868,52869,52871,52873,52880,52881,52884,52888,52896,52897,52899,52900,52901,52908,52909,52929,52964,52965,52968,52971,52972,52980,52981,52983,52984,52985,52992,52993,52996,53e3,53008,53009,53011,53013,53020,53024,53028,53036,53037,53039,53040,53041,53048,53076,53077,53080,53084,53092,53093,53095,53097,53104,53105,53108,53112,53120,53125,53132,53153,53160,53168,53188,53216,53217,53220,53224,53232,53233,53235,53237,53244,53248,53252,53265,53272,53293,53300,53301,53304,53308,55061,55062,55063,55066,55067,55069,55070,55071,55073,55074,55075,55076,55077,55078,55079,55082,55084,55086,55087,55088,55089,55090,55091,55094,55095,55097,null,null,null,null,null,null,55098,55099,55101,55102,55103,55104,55105,55106,55107,55109,55110,55112,55114,55115,55116,55117,55118,55119,55122,55123,55125,55130,55131,55132,55133,55134,null,null,null,null,null,null,55135,55138,55140,55142,55143,55144,55146,55147,55149,55150,55151,55153,55154,55155,55157,55158,55159,55160,55161,55162,55163,55166,55167,55168,55170,55171,55172,55173,55174,55175,55178,55179,53316,53317,53319,53321,53328,53332,53336,53344,53356,53357,53360,53364,53372,53373,53377,53412,53413,53416,53420,53428,53429,53431,53433,53440,53441,53444,53448,53449,53456,53457,53459,53460,53461,53468,53469,53472,53476,53484,53485,53487,53488,53489,53496,53517,53552,53553,53556,53560,53562,53568,53569,53571,53572,53573,53580,53581,53584,53588,53596,53597,53599,53601,53608,53612,53628,53636,53640,53664,53665,53668,53672,53680,53681,53683,53685,53690,53692,53696,53720,53748,53752,53767,53769,53776,53804,53805,53808,53812,53820,53821,53823,53825,53832,53852,55181,55182,55183,55185,55186,55187,55188,55189,55190,55191,55194,55196,55198,55199,55200,55201,55202,55203,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,53860,53888,53889,53892,53896,53904,53905,53909,53916,53920,53924,53932,53937,53944,53945,53948,53951,53952,53954,53960,53961,53963,53972,53976,53980,53988,53989,54e3,54001,54004,54008,54016,54017,54019,54021,54028,54029,54030,54032,54036,54038,54044,54045,54047,54048,54049,54053,54056,54057,54060,54064,54072,54073,54075,54076,54077,54084,54085,54140,54141,54144,54148,54156,54157,54159,54160,54161,54168,54169,54172,54176,54184,54185,54187,54189,54196,54200,54204,54212,54213,54216,54217,54224,54232,54241,54243,54252,54253,54256,54260,54268,54269,54271,54273,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,54280,54301,54336,54340,54364,54368,54372,54381,54383,54392,54393,54396,54399,54400,54402,54408,54409,54411,54413,54420,54441,54476,54480,54484,54492,54495,54504,54508,54512,54520,54523,54525,54532,54536,54540,54548,54549,54551,54588,54589,54592,54596,54604,54605,54607,54609,54616,54617,54620,54624,54629,54632,54633,54635,54637,54644,54645,54648,54652,54660,54661,54663,54664,54665,54672,54693,54728,54729,54732,54736,54738,54744,54745,54747,54749,54756,54757,54760,54764,54772,54773,54775,54777,54784,54785,54788,54792,54800,54801,54803,54804,54805,54812,54816,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,54820,54829,54840,54841,54844,54848,54853,54856,54857,54859,54861,54865,54868,54869,54872,54876,54887,54889,54896,54897,54900,54915,54917,54924,54925,54928,54932,54941,54943,54945,54952,54956,54960,54969,54971,54980,54981,54984,54988,54993,54996,54999,55001,55008,55012,55016,55024,55029,55036,55037,55040,55044,55057,55064,55065,55068,55072,55080,55081,55083,55085,55092,55093,55096,55100,55108,55111,55113,55120,55121,55124,55126,55127,55128,55129,55136,55137,55139,55141,55145,55148,55152,55156,55164,55165,55169,55176,55177,55180,55184,55192,55193,55195,55197,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,20285,20339,20551,20729,21152,21487,21621,21733,22025,23233,23478,26247,26550,26551,26607,27468,29634,30146,31292,33499,33540,34903,34952,35382,36040,36303,36603,36838,39381,21051,21364,21508,24682,24932,27580,29647,33050,35258,35282,38307,20355,21002,22718,22904,23014,24178,24185,25031,25536,26438,26604,26751,28567,30286,30475,30965,31240,31487,31777,32925,33390,33393,35563,38291,20075,21917,26359,28212,30883,31469,33883,35088,34638,38824,21208,22350,22570,23884,24863,25022,25121,25954,26577,27204,28187,29976,30131,30435,30640,32058,37039,37969,37970,40853,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,21283,23724,30002,32987,37440,38296,21083,22536,23004,23713,23831,24247,24378,24394,24951,27743,30074,30086,31968,32115,32177,32652,33108,33313,34193,35137,35611,37628,38477,40007,20171,20215,20491,20977,22607,24887,24894,24936,25913,27114,28433,30117,30342,30422,31623,33445,33995,63744,37799,38283,21888,23458,22353,63745,31923,32697,37301,20520,21435,23621,24040,25298,25454,25818,25831,28192,28844,31067,36317,36382,63746,36989,37445,37624,20094,20214,20581,24062,24314,24838,26967,33137,34388,36423,37749,39467,20062,20625,26480,26688,20745,21133,21138,27298,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,30652,37392,40660,21163,24623,36850,20552,25001,25581,25802,26684,27268,28608,33160,35233,38548,22533,29309,29356,29956,32121,32365,32937,35211,35700,36963,40273,25225,27770,28500,32080,32570,35363,20860,24906,31645,35609,37463,37772,20140,20435,20510,20670,20742,21185,21197,21375,22384,22659,24218,24465,24950,25004,25806,25964,26223,26299,26356,26775,28039,28805,28913,29855,29861,29898,30169,30828,30956,31455,31478,32069,32147,32789,32831,33051,33686,35686,36629,36885,37857,38915,38968,39514,39912,20418,21843,22586,22865,23395,23622,24760,25106,26690,26800,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,26856,28330,30028,30328,30926,31293,31995,32363,32380,35336,35489,35903,38542,40388,21476,21481,21578,21617,22266,22993,23396,23611,24235,25335,25911,25925,25970,26272,26543,27073,27837,30204,30352,30590,31295,32660,32771,32929,33167,33510,33533,33776,34241,34865,34996,35493,63747,36764,37678,38599,39015,39640,40723,21741,26011,26354,26767,31296,35895,40288,22256,22372,23825,26118,26801,26829,28414,29736,34974,39908,27752,63748,39592,20379,20844,20849,21151,23380,24037,24656,24685,25329,25511,25915,29657,31354,34467,36002,38799,20018,23521,25096,26524,29916,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,31185,33747,35463,35506,36328,36942,37707,38982,24275,27112,34303,37101,63749,20896,23448,23532,24931,26874,27454,28748,29743,29912,31649,32592,33733,35264,36011,38364,39208,21038,24669,25324,36866,20362,20809,21281,22745,24291,26336,27960,28826,29378,29654,31568,33009,37979,21350,25499,32619,20054,20608,22602,22750,24618,24871,25296,27088,39745,23439,32024,32945,36703,20132,20689,21676,21932,23308,23968,24039,25898,25934,26657,27211,29409,30350,30703,32094,32761,33184,34126,34527,36611,36686,37066,39171,39509,39851,19992,20037,20061,20167,20465,20855,21246,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,21312,21475,21477,21646,22036,22389,22434,23495,23943,24272,25084,25304,25937,26552,26601,27083,27472,27590,27628,27714,28317,28792,29399,29590,29699,30655,30697,31350,32127,32777,33276,33285,33290,33503,34914,35635,36092,36544,36881,37041,37476,37558,39378,39493,40169,40407,40860,22283,23616,33738,38816,38827,40628,21531,31384,32676,35033,36557,37089,22528,23624,25496,31391,23470,24339,31353,31406,33422,36524,20518,21048,21240,21367,22280,25331,25458,27402,28099,30519,21413,29527,34152,36470,38357,26426,27331,28528,35437,36556,39243,63750,26231,27512,36020,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,39740,63751,21483,22317,22862,25542,27131,29674,30789,31418,31429,31998,33909,35215,36211,36917,38312,21243,22343,30023,31584,33740,37406,63752,27224,20811,21067,21127,25119,26840,26997,38553,20677,21156,21220,25027,26020,26681,27135,29822,31563,33465,33771,35250,35641,36817,39241,63753,20170,22935,25810,26129,27278,29748,31105,31165,33449,34942,34943,35167,63754,37670,20235,21450,24613,25201,27762,32026,32102,20120,20834,30684,32943,20225,20238,20854,20864,21980,22120,22331,22522,22524,22804,22855,22931,23492,23696,23822,24049,24190,24524,25216,26071,26083,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,26398,26399,26462,26827,26820,27231,27450,27683,27773,27778,28103,29592,29734,29738,29826,29859,30072,30079,30849,30959,31041,31047,31048,31098,31637,32e3,32186,32648,32774,32813,32908,35352,35663,35912,36215,37665,37668,39138,39249,39438,39439,39525,40594,32202,20342,21513,25326,26708,37329,21931,20794,63755,63756,23068,25062,63757,25295,25343,63758,63759,63760,63761,63762,63763,37027,63764,63765,63766,63767,63768,35582,63769,63770,63771,63772,26262,63773,29014,63774,63775,38627,63776,25423,25466,21335,63777,26511,26976,28275,63778,30007,63779,63780,63781,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,32013,63782,63783,34930,22218,23064,63784,63785,63786,63787,63788,20035,63789,20839,22856,26608,32784,63790,22899,24180,25754,31178,24565,24684,25288,25467,23527,23511,21162,63791,22900,24361,24594,63792,63793,63794,29785,63795,63796,63797,63798,63799,63800,39377,63801,63802,63803,63804,63805,63806,63807,63808,63809,63810,63811,28611,63812,63813,33215,36786,24817,63814,63815,33126,63816,63817,23615,63818,63819,63820,63821,63822,63823,63824,63825,23273,35365,26491,32016,63826,63827,63828,63829,63830,63831,33021,63832,63833,23612,27877,21311,28346,22810,33590,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,20025,20150,20294,21934,22296,22727,24406,26039,26086,27264,27573,28237,30701,31471,31774,32222,34507,34962,37170,37723,25787,28606,29562,30136,36948,21846,22349,25018,25812,26311,28129,28251,28525,28601,30192,32835,33213,34113,35203,35527,35674,37663,27795,30035,31572,36367,36957,21776,22530,22616,24162,25095,25758,26848,30070,31958,34739,40680,20195,22408,22382,22823,23565,23729,24118,24453,25140,25825,29619,33274,34955,36024,38538,40667,23429,24503,24755,20498,20992,21040,22294,22581,22615,23566,23648,23798,23947,24230,24466,24764,25361,25481,25623,26691,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,26873,27330,28120,28193,28372,28644,29182,30428,30585,31153,31291,33796,35241,36077,36339,36424,36867,36884,36947,37117,37709,38518,38876,27602,28678,29272,29346,29544,30563,31167,31716,32411,35712,22697,24775,25958,26109,26302,27788,28958,29129,35930,38931,20077,31361,20189,20908,20941,21205,21516,24999,26481,26704,26847,27934,28540,30140,30643,31461,33012,33891,37509,20828,26007,26460,26515,30168,31431,33651,63834,35910,36887,38957,23663,33216,33434,36929,36975,37389,24471,23965,27225,29128,30331,31561,34276,35588,37159,39472,21895,25078,63835,30313,32645,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,34367,34746,35064,37007,63836,27931,28889,29662,32097,33853,63837,37226,39409,63838,20098,21365,27396,27410,28734,29211,34349,40478,21068,36771,23888,25829,25900,27414,28651,31811,32412,34253,35172,35261,25289,33240,34847,24266,26391,28010,29436,29701,29807,34690,37086,20358,23821,24480,33802,20919,25504,30053,20142,20486,20841,20937,26753,27153,31918,31921,31975,33391,35538,36635,37327,20406,20791,21237,21570,24300,24942,25150,26053,27354,28670,31018,34268,34851,38317,39522,39530,40599,40654,21147,26310,27511,28701,31019,36706,38722,24976,25088,25891,28451,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,29001,29833,32244,32879,34030,36646,36899,37706,20925,21015,21155,27916,28872,35010,24265,25986,27566,28610,31806,29557,20196,20278,22265,63839,23738,23994,24604,29618,31533,32666,32718,32838,36894,37428,38646,38728,38936,40801,20363,28583,31150,37300,38583,21214,63840,25736,25796,27347,28510,28696,29200,30439,32769,34310,34396,36335,36613,38706,39791,40442,40565,30860,31103,32160,33737,37636,40575,40595,35542,22751,24324,26407,28711,29903,31840,32894,20769,28712,29282,30922,36034,36058,36084,38647,20102,20698,23534,24278,26009,29134,30274,30637,32842,34044,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,36988,39719,40845,22744,23105,23650,27155,28122,28431,30267,32047,32311,34078,35128,37860,38475,21129,26066,26611,27060,27969,28316,28687,29705,29792,30041,30244,30827,35628,39006,20845,25134,38520,20374,20523,23833,28138,32184,36650,24459,24900,26647,63841,38534,21202,32907,20956,20940,26974,31260,32190,33777,38517,20442,21033,21400,21519,21774,23653,24743,26446,26792,28012,29313,29432,29702,29827,63842,30178,31852,32633,32696,33673,35023,35041,37324,37328,38626,39881,21533,28542,29136,29848,34298,36522,38563,40023,40607,26519,28107,29747,33256,38678,30764,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,31435,31520,31890,25705,29802,30194,30908,30952,39340,39764,40635,23518,24149,28448,33180,33707,37e3,19975,21325,23081,24018,24398,24930,25405,26217,26364,28415,28459,28771,30622,33836,34067,34875,36627,39237,39995,21788,25273,26411,27819,33545,35178,38778,20129,22916,24536,24537,26395,32178,32596,33426,33579,33725,36638,37017,22475,22969,23186,23504,26151,26522,26757,27599,29028,32629,36023,36067,36993,39749,33032,35978,38476,39488,40613,23391,27667,29467,30450,30431,33804,20906,35219,20813,20885,21193,26825,27796,30468,30496,32191,32236,38754,40629,28357,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,34065,20901,21517,21629,26126,26269,26919,28319,30399,30609,33559,33986,34719,37225,37528,40180,34946,20398,20882,21215,22982,24125,24917,25720,25721,26286,26576,27169,27597,27611,29279,29281,29761,30520,30683,32791,33468,33541,35584,35624,35980,26408,27792,29287,30446,30566,31302,40361,27519,27794,22818,26406,33945,21359,22675,22937,24287,25551,26164,26483,28218,29483,31447,33495,37672,21209,24043,25006,25035,25098,25287,25771,26080,26969,27494,27595,28961,29687,30045,32326,33310,33538,34154,35491,36031,38695,40289,22696,40664,20497,21006,21563,21839,25991,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,27766,32010,32011,32862,34442,38272,38639,21247,27797,29289,21619,23194,23614,23883,24396,24494,26410,26806,26979,28220,28228,30473,31859,32654,34183,35598,36855,38753,40692,23735,24758,24845,25003,25935,26107,26108,27665,27887,29599,29641,32225,38292,23494,34588,35600,21085,21338,25293,25615,25778,26420,27192,27850,29632,29854,31636,31893,32283,33162,33334,34180,36843,38649,39361,20276,21322,21453,21467,25292,25644,25856,26001,27075,27886,28504,29677,30036,30242,30436,30460,30928,30971,31020,32070,33324,34784,36820,38930,39151,21187,25300,25765,28196,28497,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,30332,36299,37297,37474,39662,39747,20515,20621,22346,22952,23592,24135,24439,25151,25918,26041,26049,26121,26507,27036,28354,30917,32033,32938,33152,33323,33459,33953,34444,35370,35607,37030,38450,40848,20493,20467,63843,22521,24472,25308,25490,26479,28227,28953,30403,32972,32986,35060,35061,35097,36064,36649,37197,38506,20271,20336,24091,26575,26658,30333,30334,39748,24161,27146,29033,29140,30058,63844,32321,34115,34281,39132,20240,31567,32624,38309,20961,24070,26805,27710,27726,27867,29359,31684,33539,27861,29754,20731,21128,22721,25816,27287,29863,30294,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,30887,34327,38370,38713,63845,21342,24321,35722,36776,36783,37002,21029,30629,40009,40712,19993,20482,20853,23643,24183,26142,26170,26564,26821,28851,29953,30149,31177,31453,36647,39200,39432,20445,22561,22577,23542,26222,27493,27921,28282,28541,29668,29995,33769,35036,35091,35676,36628,20239,20693,21264,21340,23443,24489,26381,31119,33145,33583,34068,35079,35206,36665,36667,39333,39954,26412,20086,20472,22857,23553,23791,23792,25447,26834,28925,29090,29739,32299,34028,34562,36898,37586,40179,19981,20184,20463,20613,21078,21103,21542,21648,22496,22827,23142,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,23386,23413,23500,24220,63846,25206,25975,26023,28014,28325,29238,31526,31807,32566,33104,33105,33178,33344,33433,33705,35331,36e3,36070,36091,36212,36282,37096,37340,38428,38468,39385,40167,21271,20998,21545,22132,22707,22868,22894,24575,24996,25198,26128,27774,28954,30406,31881,31966,32027,33452,36033,38640,63847,20315,24343,24447,25282,23849,26379,26842,30844,32323,40300,19989,20633,21269,21290,21329,22915,23138,24199,24754,24970,25161,25209,26e3,26503,27047,27604,27606,27607,27608,27832,63848,29749,30202,30738,30865,31189,31192,31875,32203,32737,32933,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,33086,33218,33778,34586,35048,35513,35692,36027,37145,38750,39131,40763,22188,23338,24428,25996,27315,27567,27996,28657,28693,29277,29613,36007,36051,38971,24977,27703,32856,39425,20045,20107,20123,20181,20282,20284,20351,20447,20735,21490,21496,21766,21987,22235,22763,22882,23057,23531,23546,23556,24051,24107,24473,24605,25448,26012,26031,26614,26619,26797,27515,27801,27863,28195,28681,29509,30722,31038,31040,31072,31169,31721,32023,32114,32902,33293,33678,34001,34503,35039,35408,35422,35613,36060,36198,36781,37034,39164,39391,40605,21066,63849,26388,63850,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,20632,21034,23665,25955,27733,29642,29987,30109,31639,33948,37240,38704,20087,25746,27578,29022,34217,19977,63851,26441,26862,28183,33439,34072,34923,25591,28545,37394,39087,19978,20663,20687,20767,21830,21930,22039,23360,23577,23776,24120,24202,24224,24258,24819,26705,27233,28248,29245,29248,29376,30456,31077,31665,32724,35059,35316,35443,35937,36062,38684,22622,29885,36093,21959,63852,31329,32034,33394,29298,29983,29989,63853,31513,22661,22779,23996,24207,24246,24464,24661,25234,25471,25933,26257,26329,26360,26646,26866,29312,29790,31598,32110,32214,32626,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,32997,33298,34223,35199,35475,36893,37604,40653,40736,22805,22893,24109,24796,26132,26227,26512,27728,28101,28511,30707,30889,33990,37323,37675,20185,20682,20808,21892,23307,23459,25159,25982,26059,28210,29053,29697,29764,29831,29887,30316,31146,32218,32341,32680,33146,33203,33337,34330,34796,35445,36323,36984,37521,37925,39245,39854,21352,23633,26964,27844,27945,28203,33292,34203,35131,35373,35498,38634,40807,21089,26297,27570,32406,34814,36109,38275,38493,25885,28041,29166,63854,22478,22995,23468,24615,24826,25104,26143,26207,29481,29689,30427,30465,31596,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,32854,32882,33125,35488,37266,19990,21218,27506,27927,31237,31545,32048,63855,36016,21484,22063,22609,23477,23567,23569,24034,25152,25475,25620,26157,26803,27836,28040,28335,28703,28836,29138,29990,30095,30094,30233,31505,31712,31787,32032,32057,34092,34157,34311,35380,36877,36961,37045,37559,38902,39479,20439,23660,26463,28049,31903,32396,35606,36118,36895,23403,24061,25613,33984,36956,39137,29575,23435,24730,26494,28126,35359,35494,36865,38924,21047,63856,28753,30862,37782,34928,37335,20462,21463,22013,22234,22402,22781,23234,23432,23723,23744,24101,24833,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,25101,25163,25480,25628,25910,25976,27193,27530,27700,27929,28465,29159,29417,29560,29703,29874,30246,30561,31168,31319,31466,31929,32143,32172,32353,32670,33065,33585,33936,34010,34282,34966,35504,35728,36664,36930,36995,37228,37526,37561,38539,38567,38568,38614,38656,38920,39318,39635,39706,21460,22654,22809,23408,23487,28113,28506,29087,29729,29881,32901,33789,24033,24455,24490,24642,26092,26642,26991,27219,27529,27957,28147,29667,30462,30636,31565,32020,33059,33308,33600,34036,34147,35426,35524,37255,37662,38918,39348,25100,34899,36848,37477,23815,23847,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,23913,29791,33181,34664,28629,25342,32722,35126,35186,19998,20056,20711,21213,21319,25215,26119,32361,34821,38494,20365,21273,22070,22987,23204,23608,23630,23629,24066,24337,24643,26045,26159,26178,26558,26612,29468,30690,31034,32709,33940,33997,35222,35430,35433,35553,35925,35962,22516,23508,24335,24687,25325,26893,27542,28252,29060,31698,34645,35672,36606,39135,39166,20280,20353,20449,21627,23072,23480,24892,26032,26216,29180,30003,31070,32051,33102,33251,33688,34218,34254,34563,35338,36523,36763,63857,36805,22833,23460,23526,24713,23529,23563,24515,27777,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,63858,28145,28683,29978,33455,35574,20160,21313,63859,38617,27663,20126,20420,20818,21854,23077,23784,25105,29273,33469,33706,34558,34905,35357,38463,38597,39187,40201,40285,22538,23731,23997,24132,24801,24853,25569,27138,28197,37122,37716,38990,39952,40823,23433,23736,25353,26191,26696,30524,38593,38797,38996,39839,26017,35585,36555,38332,21813,23721,24022,24245,26263,30284,33780,38343,22739,25276,29390,40232,20208,22830,24591,26171,27523,31207,40230,21395,21696,22467,23830,24859,26326,28079,30861,33406,38552,38724,21380,25212,25494,28082,32266,33099,38989,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,27387,32588,40367,40474,20063,20539,20918,22812,24825,25590,26928,29242,32822,63860,37326,24369,63861,63862,32004,33509,33903,33979,34277,36493,63863,20335,63864,63865,22756,23363,24665,25562,25880,25965,26264,63866,26954,27171,27915,28673,29036,30162,30221,31155,31344,63867,32650,63868,35140,63869,35731,37312,38525,63870,39178,22276,24481,26044,28417,30208,31142,35486,39341,39770,40812,20740,25014,25233,27277,33222,20547,22576,24422,28937,35328,35578,23420,34326,20474,20796,22196,22852,25513,28153,23978,26989,20870,20104,20313,63871,63872,63873,22914,63874,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,63875,27487,27741,63876,29877,30998,63877,33287,33349,33593,36671,36701,63878,39192,63879,63880,63881,20134,63882,22495,24441,26131,63883,63884,30123,32377,35695,63885,36870,39515,22181,22567,23032,23071,23476,63886,24310,63887,63888,25424,25403,63889,26941,27783,27839,28046,28051,28149,28436,63890,28895,28982,29017,63891,29123,29141,63892,30799,30831,63893,31605,32227,63894,32303,63895,34893,36575,63896,63897,63898,37467,63899,40182,63900,63901,63902,24709,28037,63903,29105,63904,63905,38321,21421,63906,63907,63908,26579,63909,28814,28976,29744,33398,33490,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,63910,38331,39653,40573,26308,63911,29121,33865,63912,63913,22603,63914,63915,23992,24433,63916,26144,26254,27001,27054,27704,27891,28214,28481,28634,28699,28719,29008,29151,29552,63917,29787,63918,29908,30408,31310,32403,63919,63920,33521,35424,36814,63921,37704,63922,38681,63923,63924,20034,20522,63925,21e3,21473,26355,27757,28618,29450,30591,31330,33454,34269,34306,63926,35028,35427,35709,35947,63927,37555,63928,38675,38928,20116,20237,20425,20658,21320,21566,21555,21978,22626,22714,22887,23067,23524,24735,63929,25034,25942,26111,26212,26791,27738,28595,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,28879,29100,29522,31613,34568,35492,39986,40711,23627,27779,29508,29577,37434,28331,29797,30239,31337,32277,34314,20800,22725,25793,29934,29973,30320,32705,37013,38605,39252,28198,29926,31401,31402,33253,34521,34680,35355,23113,23436,23451,26785,26880,28003,29609,29715,29740,30871,32233,32747,33048,33109,33694,35916,38446,38929,26352,24448,26106,26505,27754,29579,20525,23043,27498,30702,22806,23916,24013,29477,30031,63930,63931,20709,20985,22575,22829,22934,23002,23525,63932,63933,23970,25303,25622,25747,25854,63934,26332,63935,27208,63936,29183,29796,63937,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,31368,31407,32327,32350,32768,33136,63938,34799,35201,35616,36953,63939,36992,39250,24958,27442,28020,32287,35109,36785,20433,20653,20887,21191,22471,22665,23481,24248,24898,27029,28044,28263,28342,29076,29794,29992,29996,32883,33592,33993,36362,37780,37854,63940,20110,20305,20598,20778,21448,21451,21491,23431,23507,23588,24858,24962,26100,29275,29591,29760,30402,31056,31121,31161,32006,32701,33419,34261,34398,36802,36935,37109,37354,38533,38632,38633,21206,24423,26093,26161,26671,29020,31286,37057,38922,20113,63941,27218,27550,28560,29065,32792,33464,34131,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,36939,38549,38642,38907,34074,39729,20112,29066,38596,20803,21407,21729,22291,22290,22435,23195,23236,23491,24616,24895,25588,27781,27961,28274,28304,29232,29503,29783,33489,34945,36677,36960,63942,38498,39e3,40219,26376,36234,37470,20301,20553,20702,21361,22285,22996,23041,23561,24944,26256,28205,29234,29771,32239,32963,33806,33894,34111,34655,34907,35096,35586,36949,38859,39759,20083,20369,20754,20842,63943,21807,21929,23418,23461,24188,24189,24254,24736,24799,24840,24841,25540,25912,26377,63944,26580,26586,63945,26977,26978,27833,27943,63946,28216,63947,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,28641,29494,29495,63948,29788,30001,63949,30290,63950,63951,32173,33278,33848,35029,35480,35547,35565,36400,36418,36938,36926,36986,37193,37321,37742,63952,63953,22537,63954,27603,32905,32946,63955,63956,20801,22891,23609,63957,63958,28516,29607,32996,36103,63959,37399,38287,63960,63961,63962,63963,32895,25102,28700,32104,34701,63964,22432,24681,24903,27575,35518,37504,38577,20057,21535,28139,34093,38512,38899,39150,25558,27875,37009,20957,25033,33210,40441,20381,20506,20736,23452,24847,25087,25836,26885,27589,30097,30691,32681,33380,34191,34811,34915,35516,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,35696,37291,20108,20197,20234,63965,63966,22839,23016,63967,24050,24347,24411,24609,63968,63969,63970,63971,29246,29669,63972,30064,30157,63973,31227,63974,32780,32819,32900,33505,33617,63975,63976,36029,36019,36999,63977,63978,39156,39180,63979,63980,28727,30410,32714,32716,32764,35610,20154,20161,20995,21360,63981,21693,22240,23035,23493,24341,24525,28270,63982,63983,32106,33589,63984,34451,35469,63985,38765,38775,63986,63987,19968,20314,20350,22777,26085,28322,36920,37808,39353,20219,22764,22922,23001,24641,63988,63989,31252,63990,33615,36035,20837,21316,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,63991,63992,63993,20173,21097,23381,33471,20180,21050,21672,22985,23039,23376,23383,23388,24675,24904,28363,28825,29038,29574,29943,30133,30913,32043,32773,33258,33576,34071,34249,35566,36039,38604,20316,21242,22204,26027,26152,28796,28856,29237,32189,33421,37196,38592,40306,23409,26855,27544,28538,30430,23697,26283,28507,31668,31786,34870,38620,19976,20183,21280,22580,22715,22767,22892,23559,24115,24196,24373,25484,26290,26454,27167,27299,27404,28479,29254,63994,29520,29835,31456,31911,33144,33247,33255,33674,33900,34083,34196,34255,35037,36115,37292,38263,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,38556,20877,21705,22312,23472,25165,26448,26685,26771,28221,28371,28797,32289,35009,36001,36617,40779,40782,29229,31631,35533,37658,20295,20302,20786,21632,22992,24213,25269,26485,26990,27159,27822,28186,29401,29482,30141,31672,32053,33511,33785,33879,34295,35419,36015,36487,36889,37048,38606,40799,21219,21514,23265,23490,25688,25973,28404,29380,63995,30340,31309,31515,31821,32318,32735,33659,35627,36042,36196,36321,36447,36842,36857,36969,37841,20291,20346,20659,20840,20856,21069,21098,22625,22652,22880,23560,23637,24283,24731,25136,26643,27583,27656,28593,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,29006,29728,3e4,30008,30033,30322,31564,31627,31661,31686,32399,35438,36670,36681,37439,37523,37666,37931,38651,39002,39019,39198,20999,25130,25240,27993,30308,31434,31680,32118,21344,23742,24215,28472,28857,31896,38673,39822,40670,25509,25722,34678,19969,20117,20141,20572,20597,21576,22979,23450,24128,24237,24311,24449,24773,25402,25919,25972,26060,26230,26232,26622,26984,27273,27491,27712,28096,28136,28191,28254,28702,28833,29582,29693,30010,30555,30855,31118,31243,31357,31934,32142,33351,35330,35562,35998,37165,37194,37336,37478,37580,37664,38662,38742,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,38748,38914,40718,21046,21137,21884,22564,24093,24351,24716,25552,26799,28639,31085,31532,33229,34234,35069,35576,36420,37261,38500,38555,38717,38988,40778,20430,20806,20939,21161,22066,24340,24427,25514,25805,26089,26177,26362,26361,26397,26781,26839,27133,28437,28526,29031,29157,29226,29866,30522,31062,31066,31199,31264,31381,31895,31967,32068,32368,32903,34299,34468,35412,35519,36249,36481,36896,36973,37347,38459,38613,40165,26063,31751,36275,37827,23384,23562,21330,25305,29469,20519,23447,24478,24752,24939,26837,28121,29742,31278,32066,32156,32305,33131,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,36394,36405,37758,37912,20304,22352,24038,24231,25387,32618,20027,20303,20367,20570,23005,32964,21610,21608,22014,22863,23449,24030,24282,26205,26417,26609,26666,27880,27954,28234,28557,28855,29664,30087,31820,32002,32044,32162,33311,34523,35387,35461,36208,36490,36659,36913,37198,37202,37956,39376,31481,31909,20426,20737,20934,22472,23535,23803,26201,27197,27994,28310,28652,28940,30063,31459,34850,36897,36981,38603,39423,33537,20013,20210,34886,37325,21373,27355,26987,27713,33914,22686,24974,26366,25327,28893,29969,30151,32338,33976,35657,36104,20043,21482,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,21675,22320,22336,24535,25345,25351,25711,25903,26088,26234,26525,26547,27490,27744,27802,28460,30693,30757,31049,31063,32025,32930,33026,33267,33437,33463,34584,35468,63996,36100,36286,36978,30452,31257,31287,32340,32887,21767,21972,22645,25391,25634,26185,26187,26733,27035,27524,27941,28337,29645,29800,29857,30043,30137,30433,30494,30603,31206,32265,32285,33275,34095,34967,35386,36049,36587,36784,36914,37805,38499,38515,38663,20356,21489,23018,23241,24089,26702,29894,30142,31209,31378,33187,34541,36074,36300,36845,26015,26389,63997,22519,28503,32221,36655,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,37878,38598,24501,25074,28548,19988,20376,20511,21449,21983,23919,24046,27425,27492,30923,31642,63998,36425,36554,36974,25417,25662,30528,31364,37679,38015,40810,25776,28591,29158,29864,29914,31428,31762,32386,31922,32408,35738,36106,38013,39184,39244,21049,23519,25830,26413,32046,20717,21443,22649,24920,24921,25082,26028,31449,35730,35734,20489,20513,21109,21809,23100,24288,24432,24884,25950,26124,26166,26274,27085,28356,28466,29462,30241,31379,33081,33369,33750,33980,20661,22512,23488,23528,24425,25505,30758,32181,33756,34081,37319,37365,20874,26613,31574,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,36012,20932,22971,24765,34389,20508,63999,21076,23610,24957,25114,25299,25842,26021,28364,30240,33034,36448,38495,38587,20191,21315,21912,22825,24029,25797,27849,28154,29588,31359,33307,34214,36068,36368,36983,37351,38369,38433,38854,20984,21746,21894,24505,25764,28552,32180,36639,36685,37941,20681,23574,27838,28155,29979,30651,31805,31844,35449,35522,22558,22974,24086,25463,29266,30090,30571,35548,36028,36626,24307,26228,28152,32893,33729,35531,38737,39894,64e3,21059,26367,28053,28399,32224,35558,36910,36958,39636,21021,21119,21736,24980,25220,25307,26786,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,26898,26970,27189,28818,28966,30813,30977,30990,31186,31245,32918,33400,33493,33609,34121,35970,36229,37218,37259,37294,20419,22225,29165,30679,34560,35320,23544,24534,26449,37032,21474,22618,23541,24740,24961,25696,32317,32880,34085,37507,25774,20652,23828,26368,22684,25277,25512,26894,27e3,27166,28267,30394,31179,33467,33833,35535,36264,36861,37138,37195,37276,37648,37656,37786,38619,39478,39949,19985,30044,31069,31482,31569,31689,32302,33988,36441,36468,36600,36880,26149,26943,29763,20986,26414,40668,20805,24544,27798,34802,34909,34935,24756,33205,33795,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,36101,21462,21561,22068,23094,23601,28810,32736,32858,33030,33261,36259,37257,39519,40434,20596,20164,21408,24827,28204,23652,20360,20516,21988,23769,24159,24677,26772,27835,28100,29118,30164,30196,30305,31258,31305,32199,32251,32622,33268,34473,36636,38601,39347,40786,21063,21189,39149,35242,19971,26578,28422,20405,23522,26517,27784,28024,29723,30759,37341,37756,34756,31204,31281,24555,20182,21668,21822,22702,22949,24816,25171,25302,26422,26965,33333,38464,39345,39389,20524,21331,21828,22396,64001,25176,64002,25826,26219,26589,28609,28655,29730,29752,35351,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,37944,21585,22022,22374,24392,24986,27470,28760,28845,32187,35477,22890,33067,25506,30472,32829,36010,22612,25645,27067,23445,24081,28271,64003,34153,20812,21488,22826,24608,24907,27526,27760,27888,31518,32974,33492,36294,37040,39089,64004,25799,28580,25745,25860,20814,21520,22303,35342,24927,26742,64005,30171,31570,32113,36890,22534,27084,33151,35114,36864,38969,20600,22871,22956,25237,36879,39722,24925,29305,38358,22369,23110,24052,25226,25773,25850,26487,27874,27966,29228,29750,30772,32631,33453,36315,38935,21028,22338,26495,29256,29923,36009,36774,37393,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,38442,20843,21485,25420,20329,21764,24726,25943,27803,28031,29260,29437,31255,35207,35997,24429,28558,28921,33192,24846,20415,20559,25153,29255,31687,32232,32745,36941,38829,39449,36022,22378,24179,26544,33805,35413,21536,23318,24163,24290,24330,25987,32954,34109,38281,38491,20296,21253,21261,21263,21638,21754,22275,24067,24598,25243,25265,25429,64006,27873,28006,30129,30770,32990,33071,33502,33889,33970,34957,35090,36875,37610,39165,39825,24133,26292,26333,28689,29190,64007,20469,21117,24426,24915,26451,27161,28418,29922,31080,34920,35961,39111,39108,39491,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,21697,31263,26963,35575,35914,39080,39342,24444,25259,30130,30382,34987,36991,38466,21305,24380,24517,27852,29644,30050,30091,31558,33534,39325,20047,36924,19979,20309,21414,22799,24264,26160,27827,29781,33655,34662,36032,36944,38686,39957,22737,23416,34384,35604,40372,23506,24680,24717,26097,27735,28450,28579,28698,32597,32752,38289,38290,38480,38867,21106,36676,20989,21547,21688,21859,21898,27323,28085,32216,33382,37532,38519,40569,21512,21704,30418,34532,38308,38356,38492,20130,20233,23022,23270,24055,24658,25239,26477,26689,27782,28207,32568,32923,33322,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,64008,64009,38917,20133,20565,21683,22419,22874,23401,23475,25032,26999,28023,28707,34809,35299,35442,35559,36994,39405,39608,21182,26680,20502,24184,26447,33607,34892,20139,21521,22190,29670,37141,38911,39177,39255,39321,22099,22687,34395,35377,25010,27382,29563,36562,27463,38570,39511,22869,29184,36203,38761,20436,23796,24358,25080,26203,27883,28843,29572,29625,29694,30505,30541,32067,32098,32291,33335,34898,64010,36066,37449,39023,23377,31348,34880,38913,23244,20448,21332,22846,23805,25406,28025,29433,33029,33031,33698,37583,38960,20136,20804,21009,22411,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,24418,27842,28366,28677,28752,28847,29074,29673,29801,33610,34722,34913,36872,37026,37795,39336,20846,24407,24800,24935,26291,34137,36426,37295,38795,20046,20114,21628,22741,22778,22909,23733,24359,25142,25160,26122,26215,27627,28009,28111,28246,28408,28564,28640,28649,28765,29392,29733,29786,29920,30355,31068,31946,32286,32993,33446,33899,33983,34382,34399,34676,35703,35946,37804,38912,39013,24785,25110,37239,23130,26127,28151,28222,29759,39746,24573,24794,31503,21700,24344,27742,27859,27946,28888,32005,34425,35340,40251,21270,21644,23301,27194,28779,30069,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,31117,31166,33457,33775,35441,35649,36008,38772,64011,25844,25899,30906,30907,31339,20024,21914,22864,23462,24187,24739,25563,27489,26213,26707,28185,29029,29872,32008,36996,39529,39973,27963,28369,29502,35905,38346,20976,24140,24488,24653,24822,24880,24908,26179,26180,27045,27841,28255,28361,28514,29004,29852,30343,31681,31783,33618,34647,36945,38541,40643,21295,22238,24315,24458,24674,24724,25079,26214,26371,27292,28142,28590,28784,29546,32362,33214,33588,34516,35496,36036,21123,29554,23446,27243,37892,21742,22150,23389,25928,25989,26313,26783,28045,28102,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,29243,32948,37237,39501,20399,20505,21402,21518,21564,21897,21957,24127,24460,26429,29030,29661,36869,21211,21235,22628,22734,28932,29071,29179,34224,35347,26248,34216,21927,26244,29002,33841,21321,21913,27585,24409,24509,25582,26249,28999,35569,36637,40638,20241,25658,28875,30054,34407,24676,35662,40440,20807,20982,21256,27958,33016,40657,26133,27427,28824,30165,21507,23673,32007,35350,27424,27453,27462,21560,24688,27965,32725,33288,20694,20958,21916,22123,22221,23020,23305,24076,24985,24984,25137,26206,26342,29081,29113,29114,29351,31143,31232,32690,35440,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],gb18030:[19970,19972,19973,19974,19983,19986,19991,19999,2e4,20001,20003,20006,20009,20014,20015,20017,20019,20021,20023,20028,20032,20033,20034,20036,20038,20042,20049,20053,20055,20058,20059,20066,20067,20068,20069,20071,20072,20074,20075,20076,20077,20078,20079,20082,20084,20085,20086,20087,20088,20089,20090,20091,20092,20093,20095,20096,20097,20098,20099,20100,20101,20103,20106,20112,20118,20119,20121,20124,20125,20126,20131,20138,20143,20144,20145,20148,20150,20151,20152,20153,20156,20157,20158,20168,20172,20175,20176,20178,20186,20187,20188,20192,20194,20198,20199,20201,20205,20206,20207,20209,20212,20216,20217,20218,20220,20222,20224,20226,20227,20228,20229,20230,20231,20232,20235,20236,20242,20243,20244,20245,20246,20252,20253,20257,20259,20264,20265,20268,20269,20270,20273,20275,20277,20279,20281,20283,20286,20287,20288,20289,20290,20292,20293,20295,20296,20297,20298,20299,20300,20306,20308,20310,20321,20322,20326,20328,20330,20331,20333,20334,20337,20338,20341,20343,20344,20345,20346,20349,20352,20353,20354,20357,20358,20359,20362,20364,20366,20368,20370,20371,20373,20374,20376,20377,20378,20380,20382,20383,20385,20386,20388,20395,20397,20400,20401,20402,20403,20404,20406,20407,20408,20409,20410,20411,20412,20413,20414,20416,20417,20418,20422,20423,20424,20425,20427,20428,20429,20434,20435,20436,20437,20438,20441,20443,20448,20450,20452,20453,20455,20459,20460,20464,20466,20468,20469,20470,20471,20473,20475,20476,20477,20479,20480,20481,20482,20483,20484,20485,20486,20487,20488,20489,20490,20491,20494,20496,20497,20499,20501,20502,20503,20507,20509,20510,20512,20514,20515,20516,20519,20523,20527,20528,20529,20530,20531,20532,20533,20534,20535,20536,20537,20539,20541,20543,20544,20545,20546,20548,20549,20550,20553,20554,20555,20557,20560,20561,20562,20563,20564,20566,20567,20568,20569,20571,20573,20574,20575,20576,20577,20578,20579,20580,20582,20583,20584,20585,20586,20587,20589,20590,20591,20592,20593,20594,20595,20596,20597,20600,20601,20602,20604,20605,20609,20610,20611,20612,20614,20615,20617,20618,20619,20620,20622,20623,20624,20625,20626,20627,20628,20629,20630,20631,20632,20633,20634,20635,20636,20637,20638,20639,20640,20641,20642,20644,20646,20650,20651,20653,20654,20655,20656,20657,20659,20660,20661,20662,20663,20664,20665,20668,20669,20670,20671,20672,20673,20674,20675,20676,20677,20678,20679,20680,20681,20682,20683,20684,20685,20686,20688,20689,20690,20691,20692,20693,20695,20696,20697,20699,20700,20701,20702,20703,20704,20705,20706,20707,20708,20709,20712,20713,20714,20715,20719,20720,20721,20722,20724,20726,20727,20728,20729,20730,20732,20733,20734,20735,20736,20737,20738,20739,20740,20741,20744,20745,20746,20748,20749,20750,20751,20752,20753,20755,20756,20757,20758,20759,20760,20761,20762,20763,20764,20765,20766,20767,20768,20770,20771,20772,20773,20774,20775,20776,20777,20778,20779,20780,20781,20782,20783,20784,20785,20786,20787,20788,20789,20790,20791,20792,20793,20794,20795,20796,20797,20798,20802,20807,20810,20812,20814,20815,20816,20818,20819,20823,20824,20825,20827,20829,20830,20831,20832,20833,20835,20836,20838,20839,20841,20842,20847,20850,20858,20862,20863,20867,20868,20870,20871,20874,20875,20878,20879,20880,20881,20883,20884,20888,20890,20893,20894,20895,20897,20899,20902,20903,20904,20905,20906,20909,20910,20916,20920,20921,20922,20926,20927,20929,20930,20931,20933,20936,20938,20941,20942,20944,20946,20947,20948,20949,20950,20951,20952,20953,20954,20956,20958,20959,20962,20963,20965,20966,20967,20968,20969,20970,20972,20974,20977,20978,20980,20983,20990,20996,20997,21001,21003,21004,21007,21008,21011,21012,21013,21020,21022,21023,21025,21026,21027,21029,21030,21031,21034,21036,21039,21041,21042,21044,21045,21052,21054,21060,21061,21062,21063,21064,21065,21067,21070,21071,21074,21075,21077,21079,21080,21081,21082,21083,21085,21087,21088,21090,21091,21092,21094,21096,21099,21100,21101,21102,21104,21105,21107,21108,21109,21110,21111,21112,21113,21114,21115,21116,21118,21120,21123,21124,21125,21126,21127,21129,21130,21131,21132,21133,21134,21135,21137,21138,21140,21141,21142,21143,21144,21145,21146,21148,21156,21157,21158,21159,21166,21167,21168,21172,21173,21174,21175,21176,21177,21178,21179,21180,21181,21184,21185,21186,21188,21189,21190,21192,21194,21196,21197,21198,21199,21201,21203,21204,21205,21207,21209,21210,21211,21212,21213,21214,21216,21217,21218,21219,21221,21222,21223,21224,21225,21226,21227,21228,21229,21230,21231,21233,21234,21235,21236,21237,21238,21239,21240,21243,21244,21245,21249,21250,21251,21252,21255,21257,21258,21259,21260,21262,21265,21266,21267,21268,21272,21275,21276,21278,21279,21282,21284,21285,21287,21288,21289,21291,21292,21293,21295,21296,21297,21298,21299,21300,21301,21302,21303,21304,21308,21309,21312,21314,21316,21318,21323,21324,21325,21328,21332,21336,21337,21339,21341,21349,21352,21354,21356,21357,21362,21366,21369,21371,21372,21373,21374,21376,21377,21379,21383,21384,21386,21390,21391,21392,21393,21394,21395,21396,21398,21399,21401,21403,21404,21406,21408,21409,21412,21415,21418,21419,21420,21421,21423,21424,21425,21426,21427,21428,21429,21431,21432,21433,21434,21436,21437,21438,21440,21443,21444,21445,21446,21447,21454,21455,21456,21458,21459,21461,21466,21468,21469,21470,21473,21474,21479,21492,21498,21502,21503,21504,21506,21509,21511,21515,21524,21528,21529,21530,21532,21538,21540,21541,21546,21552,21555,21558,21559,21562,21565,21567,21569,21570,21572,21573,21575,21577,21580,21581,21582,21583,21585,21594,21597,21598,21599,21600,21601,21603,21605,21607,21609,21610,21611,21612,21613,21614,21615,21616,21620,21625,21626,21630,21631,21633,21635,21637,21639,21640,21641,21642,21645,21649,21651,21655,21656,21660,21662,21663,21664,21665,21666,21669,21678,21680,21682,21685,21686,21687,21689,21690,21692,21694,21699,21701,21706,21707,21718,21720,21723,21728,21729,21730,21731,21732,21739,21740,21743,21744,21745,21748,21749,21750,21751,21752,21753,21755,21758,21760,21762,21763,21764,21765,21768,21770,21771,21772,21773,21774,21778,21779,21781,21782,21783,21784,21785,21786,21788,21789,21790,21791,21793,21797,21798,21800,21801,21803,21805,21810,21812,21813,21814,21816,21817,21818,21819,21821,21824,21826,21829,21831,21832,21835,21836,21837,21838,21839,21841,21842,21843,21844,21847,21848,21849,21850,21851,21853,21854,21855,21856,21858,21859,21864,21865,21867,21871,21872,21873,21874,21875,21876,21881,21882,21885,21887,21893,21894,21900,21901,21902,21904,21906,21907,21909,21910,21911,21914,21915,21918,21920,21921,21922,21923,21924,21925,21926,21928,21929,21930,21931,21932,21933,21934,21935,21936,21938,21940,21942,21944,21946,21948,21951,21952,21953,21954,21955,21958,21959,21960,21962,21963,21966,21967,21968,21973,21975,21976,21977,21978,21979,21982,21984,21986,21991,21993,21997,21998,22e3,22001,22004,22006,22008,22009,22010,22011,22012,22015,22018,22019,22020,22021,22022,22023,22026,22027,22029,22032,22033,22034,22035,22036,22037,22038,22039,22041,22042,22044,22045,22048,22049,22050,22053,22054,22056,22057,22058,22059,22062,22063,22064,22067,22069,22071,22072,22074,22076,22077,22078,22080,22081,22082,22083,22084,22085,22086,22087,22088,22089,22090,22091,22095,22096,22097,22098,22099,22101,22102,22106,22107,22109,22110,22111,22112,22113,22115,22117,22118,22119,22125,22126,22127,22128,22130,22131,22132,22133,22135,22136,22137,22138,22141,22142,22143,22144,22145,22146,22147,22148,22151,22152,22153,22154,22155,22156,22157,22160,22161,22162,22164,22165,22166,22167,22168,22169,22170,22171,22172,22173,22174,22175,22176,22177,22178,22180,22181,22182,22183,22184,22185,22186,22187,22188,22189,22190,22192,22193,22194,22195,22196,22197,22198,22200,22201,22202,22203,22205,22206,22207,22208,22209,22210,22211,22212,22213,22214,22215,22216,22217,22219,22220,22221,22222,22223,22224,22225,22226,22227,22229,22230,22232,22233,22236,22243,22245,22246,22247,22248,22249,22250,22252,22254,22255,22258,22259,22262,22263,22264,22267,22268,22272,22273,22274,22277,22279,22283,22284,22285,22286,22287,22288,22289,22290,22291,22292,22293,22294,22295,22296,22297,22298,22299,22301,22302,22304,22305,22306,22308,22309,22310,22311,22315,22321,22322,22324,22325,22326,22327,22328,22332,22333,22335,22337,22339,22340,22341,22342,22344,22345,22347,22354,22355,22356,22357,22358,22360,22361,22370,22371,22373,22375,22380,22382,22384,22385,22386,22388,22389,22392,22393,22394,22397,22398,22399,22400,22401,22407,22408,22409,22410,22413,22414,22415,22416,22417,22420,22421,22422,22423,22424,22425,22426,22428,22429,22430,22431,22437,22440,22442,22444,22447,22448,22449,22451,22453,22454,22455,22457,22458,22459,22460,22461,22462,22463,22464,22465,22468,22469,22470,22471,22472,22473,22474,22476,22477,22480,22481,22483,22486,22487,22491,22492,22494,22497,22498,22499,22501,22502,22503,22504,22505,22506,22507,22508,22510,22512,22513,22514,22515,22517,22518,22519,22523,22524,22526,22527,22529,22531,22532,22533,22536,22537,22538,22540,22542,22543,22544,22546,22547,22548,22550,22551,22552,22554,22555,22556,22557,22559,22562,22563,22565,22566,22567,22568,22569,22571,22572,22573,22574,22575,22577,22578,22579,22580,22582,22583,22584,22585,22586,22587,22588,22589,22590,22591,22592,22593,22594,22595,22597,22598,22599,22600,22601,22602,22603,22606,22607,22608,22610,22611,22613,22614,22615,22617,22618,22619,22620,22621,22623,22624,22625,22626,22627,22628,22630,22631,22632,22633,22634,22637,22638,22639,22640,22641,22642,22643,22644,22645,22646,22647,22648,22649,22650,22651,22652,22653,22655,22658,22660,22662,22663,22664,22666,22667,22668,22669,22670,22671,22672,22673,22676,22677,22678,22679,22680,22683,22684,22685,22688,22689,22690,22691,22692,22693,22694,22695,22698,22699,22700,22701,22702,22703,22704,22705,22706,22707,22708,22709,22710,22711,22712,22713,22714,22715,22717,22718,22719,22720,22722,22723,22724,22726,22727,22728,22729,22730,22731,22732,22733,22734,22735,22736,22738,22739,22740,22742,22743,22744,22745,22746,22747,22748,22749,22750,22751,22752,22753,22754,22755,22757,22758,22759,22760,22761,22762,22765,22767,22769,22770,22772,22773,22775,22776,22778,22779,22780,22781,22782,22783,22784,22785,22787,22789,22790,22792,22793,22794,22795,22796,22798,22800,22801,22802,22803,22807,22808,22811,22813,22814,22816,22817,22818,22819,22822,22824,22828,22832,22834,22835,22837,22838,22843,22845,22846,22847,22848,22851,22853,22854,22858,22860,22861,22864,22866,22867,22873,22875,22876,22877,22878,22879,22881,22883,22884,22886,22887,22888,22889,22890,22891,22892,22893,22894,22895,22896,22897,22898,22901,22903,22906,22907,22908,22910,22911,22912,22917,22921,22923,22924,22926,22927,22928,22929,22932,22933,22936,22938,22939,22940,22941,22943,22944,22945,22946,22950,22951,22956,22957,22960,22961,22963,22964,22965,22966,22967,22968,22970,22972,22973,22975,22976,22977,22978,22979,22980,22981,22983,22984,22985,22988,22989,22990,22991,22997,22998,23001,23003,23006,23007,23008,23009,23010,23012,23014,23015,23017,23018,23019,23021,23022,23023,23024,23025,23026,23027,23028,23029,23030,23031,23032,23034,23036,23037,23038,23040,23042,23050,23051,23053,23054,23055,23056,23058,23060,23061,23062,23063,23065,23066,23067,23069,23070,23073,23074,23076,23078,23079,23080,23082,23083,23084,23085,23086,23087,23088,23091,23093,23095,23096,23097,23098,23099,23101,23102,23103,23105,23106,23107,23108,23109,23111,23112,23115,23116,23117,23118,23119,23120,23121,23122,23123,23124,23126,23127,23128,23129,23131,23132,23133,23134,23135,23136,23137,23139,23140,23141,23142,23144,23145,23147,23148,23149,23150,23151,23152,23153,23154,23155,23160,23161,23163,23164,23165,23166,23168,23169,23170,23171,23172,23173,23174,23175,23176,23177,23178,23179,23180,23181,23182,23183,23184,23185,23187,23188,23189,23190,23191,23192,23193,23196,23197,23198,23199,23200,23201,23202,23203,23204,23205,23206,23207,23208,23209,23211,23212,23213,23214,23215,23216,23217,23220,23222,23223,23225,23226,23227,23228,23229,23231,23232,23235,23236,23237,23238,23239,23240,23242,23243,23245,23246,23247,23248,23249,23251,23253,23255,23257,23258,23259,23261,23262,23263,23266,23268,23269,23271,23272,23274,23276,23277,23278,23279,23280,23282,23283,23284,23285,23286,23287,23288,23289,23290,23291,23292,23293,23294,23295,23296,23297,23298,23299,23300,23301,23302,23303,23304,23306,23307,23308,23309,23310,23311,23312,23313,23314,23315,23316,23317,23320,23321,23322,23323,23324,23325,23326,23327,23328,23329,23330,23331,23332,23333,23334,23335,23336,23337,23338,23339,23340,23341,23342,23343,23344,23345,23347,23349,23350,23352,23353,23354,23355,23356,23357,23358,23359,23361,23362,23363,23364,23365,23366,23367,23368,23369,23370,23371,23372,23373,23374,23375,23378,23382,23390,23392,23393,23399,23400,23403,23405,23406,23407,23410,23412,23414,23415,23416,23417,23419,23420,23422,23423,23426,23430,23434,23437,23438,23440,23441,23442,23444,23446,23455,23463,23464,23465,23468,23469,23470,23471,23473,23474,23479,23482,23483,23484,23488,23489,23491,23496,23497,23498,23499,23501,23502,23503,23505,23508,23509,23510,23511,23512,23513,23514,23515,23516,23520,23522,23523,23526,23527,23529,23530,23531,23532,23533,23535,23537,23538,23539,23540,23541,23542,23543,23549,23550,23552,23554,23555,23557,23559,23560,23563,23564,23565,23566,23568,23570,23571,23575,23577,23579,23582,23583,23584,23585,23587,23590,23592,23593,23594,23595,23597,23598,23599,23600,23602,23603,23605,23606,23607,23619,23620,23622,23623,23628,23629,23634,23635,23636,23638,23639,23640,23642,23643,23644,23645,23647,23650,23652,23655,23656,23657,23658,23659,23660,23661,23664,23666,23667,23668,23669,23670,23671,23672,23675,23676,23677,23678,23680,23683,23684,23685,23686,23687,23689,23690,23691,23694,23695,23698,23699,23701,23709,23710,23711,23712,23713,23716,23717,23718,23719,23720,23722,23726,23727,23728,23730,23732,23734,23737,23738,23739,23740,23742,23744,23746,23747,23749,23750,23751,23752,23753,23754,23756,23757,23758,23759,23760,23761,23763,23764,23765,23766,23767,23768,23770,23771,23772,23773,23774,23775,23776,23778,23779,23783,23785,23787,23788,23790,23791,23793,23794,23795,23796,23797,23798,23799,23800,23801,23802,23804,23805,23806,23807,23808,23809,23812,23813,23816,23817,23818,23819,23820,23821,23823,23824,23825,23826,23827,23829,23831,23832,23833,23834,23836,23837,23839,23840,23841,23842,23843,23845,23848,23850,23851,23852,23855,23856,23857,23858,23859,23861,23862,23863,23864,23865,23866,23867,23868,23871,23872,23873,23874,23875,23876,23877,23878,23880,23881,23885,23886,23887,23888,23889,23890,23891,23892,23893,23894,23895,23897,23898,23900,23902,23903,23904,23905,23906,23907,23908,23909,23910,23911,23912,23914,23917,23918,23920,23921,23922,23923,23925,23926,23927,23928,23929,23930,23931,23932,23933,23934,23935,23936,23937,23939,23940,23941,23942,23943,23944,23945,23946,23947,23948,23949,23950,23951,23952,23953,23954,23955,23956,23957,23958,23959,23960,23962,23963,23964,23966,23967,23968,23969,23970,23971,23972,23973,23974,23975,23976,23977,23978,23979,23980,23981,23982,23983,23984,23985,23986,23987,23988,23989,23990,23992,23993,23994,23995,23996,23997,23998,23999,24e3,24001,24002,24003,24004,24006,24007,24008,24009,24010,24011,24012,24014,24015,24016,24017,24018,24019,24020,24021,24022,24023,24024,24025,24026,24028,24031,24032,24035,24036,24042,24044,24045,24048,24053,24054,24056,24057,24058,24059,24060,24063,24064,24068,24071,24073,24074,24075,24077,24078,24082,24083,24087,24094,24095,24096,24097,24098,24099,24100,24101,24104,24105,24106,24107,24108,24111,24112,24114,24115,24116,24117,24118,24121,24122,24126,24127,24128,24129,24131,24134,24135,24136,24137,24138,24139,24141,24142,24143,24144,24145,24146,24147,24150,24151,24152,24153,24154,24156,24157,24159,24160,24163,24164,24165,24166,24167,24168,24169,24170,24171,24172,24173,24174,24175,24176,24177,24181,24183,24185,24190,24193,24194,24195,24197,24200,24201,24204,24205,24206,24210,24216,24219,24221,24225,24226,24227,24228,24232,24233,24234,24235,24236,24238,24239,24240,24241,24242,24244,24250,24251,24252,24253,24255,24256,24257,24258,24259,24260,24261,24262,24263,24264,24267,24268,24269,24270,24271,24272,24276,24277,24279,24280,24281,24282,24284,24285,24286,24287,24288,24289,24290,24291,24292,24293,24294,24295,24297,24299,24300,24301,24302,24303,24304,24305,24306,24307,24309,24312,24313,24315,24316,24317,24325,24326,24327,24329,24332,24333,24334,24336,24338,24340,24342,24345,24346,24348,24349,24350,24353,24354,24355,24356,24360,24363,24364,24366,24368,24370,24371,24372,24373,24374,24375,24376,24379,24381,24382,24383,24385,24386,24387,24388,24389,24390,24391,24392,24393,24394,24395,24396,24397,24398,24399,24401,24404,24409,24410,24411,24412,24414,24415,24416,24419,24421,24423,24424,24427,24430,24431,24434,24436,24437,24438,24440,24442,24445,24446,24447,24451,24454,24461,24462,24463,24465,24467,24468,24470,24474,24475,24477,24478,24479,24480,24482,24483,24484,24485,24486,24487,24489,24491,24492,24495,24496,24497,24498,24499,24500,24502,24504,24505,24506,24507,24510,24511,24512,24513,24514,24519,24520,24522,24523,24526,24531,24532,24533,24538,24539,24540,24542,24543,24546,24547,24549,24550,24552,24553,24556,24559,24560,24562,24563,24564,24566,24567,24569,24570,24572,24583,24584,24585,24587,24588,24592,24593,24595,24599,24600,24602,24606,24607,24610,24611,24612,24620,24621,24622,24624,24625,24626,24627,24628,24630,24631,24632,24633,24634,24637,24638,24640,24644,24645,24646,24647,24648,24649,24650,24652,24654,24655,24657,24659,24660,24662,24663,24664,24667,24668,24670,24671,24672,24673,24677,24678,24686,24689,24690,24692,24693,24695,24702,24704,24705,24706,24709,24710,24711,24712,24714,24715,24718,24719,24720,24721,24723,24725,24727,24728,24729,24732,24734,24737,24738,24740,24741,24743,24745,24746,24750,24752,24755,24757,24758,24759,24761,24762,24765,24766,24767,24768,24769,24770,24771,24772,24775,24776,24777,24780,24781,24782,24783,24784,24786,24787,24788,24790,24791,24793,24795,24798,24801,24802,24803,24804,24805,24810,24817,24818,24821,24823,24824,24827,24828,24829,24830,24831,24834,24835,24836,24837,24839,24842,24843,24844,24848,24849,24850,24851,24852,24854,24855,24856,24857,24859,24860,24861,24862,24865,24866,24869,24872,24873,24874,24876,24877,24878,24879,24880,24881,24882,24883,24884,24885,24886,24887,24888,24889,24890,24891,24892,24893,24894,24896,24897,24898,24899,24900,24901,24902,24903,24905,24907,24909,24911,24912,24914,24915,24916,24918,24919,24920,24921,24922,24923,24924,24926,24927,24928,24929,24931,24932,24933,24934,24937,24938,24939,24940,24941,24942,24943,24945,24946,24947,24948,24950,24952,24953,24954,24955,24956,24957,24958,24959,24960,24961,24962,24963,24964,24965,24966,24967,24968,24969,24970,24972,24973,24975,24976,24977,24978,24979,24981,24982,24983,24984,24985,24986,24987,24988,24990,24991,24992,24993,24994,24995,24996,24997,24998,25002,25003,25005,25006,25007,25008,25009,25010,25011,25012,25013,25014,25016,25017,25018,25019,25020,25021,25023,25024,25025,25027,25028,25029,25030,25031,25033,25036,25037,25038,25039,25040,25043,25045,25046,25047,25048,25049,25050,25051,25052,25053,25054,25055,25056,25057,25058,25059,25060,25061,25063,25064,25065,25066,25067,25068,25069,25070,25071,25072,25073,25074,25075,25076,25078,25079,25080,25081,25082,25083,25084,25085,25086,25088,25089,25090,25091,25092,25093,25095,25097,25107,25108,25113,25116,25117,25118,25120,25123,25126,25127,25128,25129,25131,25133,25135,25136,25137,25138,25141,25142,25144,25145,25146,25147,25148,25154,25156,25157,25158,25162,25167,25168,25173,25174,25175,25177,25178,25180,25181,25182,25183,25184,25185,25186,25188,25189,25192,25201,25202,25204,25205,25207,25208,25210,25211,25213,25217,25218,25219,25221,25222,25223,25224,25227,25228,25229,25230,25231,25232,25236,25241,25244,25245,25246,25251,25254,25255,25257,25258,25261,25262,25263,25264,25266,25267,25268,25270,25271,25272,25274,25278,25280,25281,25283,25291,25295,25297,25301,25309,25310,25312,25313,25316,25322,25323,25328,25330,25333,25336,25337,25338,25339,25344,25347,25348,25349,25350,25354,25355,25356,25357,25359,25360,25362,25363,25364,25365,25367,25368,25369,25372,25382,25383,25385,25388,25389,25390,25392,25393,25395,25396,25397,25398,25399,25400,25403,25404,25406,25407,25408,25409,25412,25415,25416,25418,25425,25426,25427,25428,25430,25431,25432,25433,25434,25435,25436,25437,25440,25444,25445,25446,25448,25450,25451,25452,25455,25456,25458,25459,25460,25461,25464,25465,25468,25469,25470,25471,25473,25475,25476,25477,25478,25483,25485,25489,25491,25492,25493,25495,25497,25498,25499,25500,25501,25502,25503,25505,25508,25510,25515,25519,25521,25522,25525,25526,25529,25531,25533,25535,25536,25537,25538,25539,25541,25543,25544,25546,25547,25548,25553,25555,25556,25557,25559,25560,25561,25562,25563,25564,25565,25567,25570,25572,25573,25574,25575,25576,25579,25580,25582,25583,25584,25585,25587,25589,25591,25593,25594,25595,25596,25598,25603,25604,25606,25607,25608,25609,25610,25613,25614,25617,25618,25621,25622,25623,25624,25625,25626,25629,25631,25634,25635,25636,25637,25639,25640,25641,25643,25646,25647,25648,25649,25650,25651,25653,25654,25655,25656,25657,25659,25660,25662,25664,25666,25667,25673,25675,25676,25677,25678,25679,25680,25681,25683,25685,25686,25687,25689,25690,25691,25692,25693,25695,25696,25697,25698,25699,25700,25701,25702,25704,25706,25707,25708,25710,25711,25712,25713,25714,25715,25716,25717,25718,25719,25723,25724,25725,25726,25727,25728,25729,25731,25734,25736,25737,25738,25739,25740,25741,25742,25743,25744,25747,25748,25751,25752,25754,25755,25756,25757,25759,25760,25761,25762,25763,25765,25766,25767,25768,25770,25771,25775,25777,25778,25779,25780,25782,25785,25787,25789,25790,25791,25793,25795,25796,25798,25799,25800,25801,25802,25803,25804,25807,25809,25811,25812,25813,25814,25817,25818,25819,25820,25821,25823,25824,25825,25827,25829,25831,25832,25833,25834,25835,25836,25837,25838,25839,25840,25841,25842,25843,25844,25845,25846,25847,25848,25849,25850,25851,25852,25853,25854,25855,25857,25858,25859,25860,25861,25862,25863,25864,25866,25867,25868,25869,25870,25871,25872,25873,25875,25876,25877,25878,25879,25881,25882,25883,25884,25885,25886,25887,25888,25889,25890,25891,25892,25894,25895,25896,25897,25898,25900,25901,25904,25905,25906,25907,25911,25914,25916,25917,25920,25921,25922,25923,25924,25926,25927,25930,25931,25933,25934,25936,25938,25939,25940,25943,25944,25946,25948,25951,25952,25953,25956,25957,25959,25960,25961,25962,25965,25966,25967,25969,25971,25973,25974,25976,25977,25978,25979,25980,25981,25982,25983,25984,25985,25986,25987,25988,25989,25990,25992,25993,25994,25997,25998,25999,26002,26004,26005,26006,26008,26010,26013,26014,26016,26018,26019,26022,26024,26026,26028,26030,26033,26034,26035,26036,26037,26038,26039,26040,26042,26043,26046,26047,26048,26050,26055,26056,26057,26058,26061,26064,26065,26067,26068,26069,26072,26073,26074,26075,26076,26077,26078,26079,26081,26083,26084,26090,26091,26098,26099,26100,26101,26104,26105,26107,26108,26109,26110,26111,26113,26116,26117,26119,26120,26121,26123,26125,26128,26129,26130,26134,26135,26136,26138,26139,26140,26142,26145,26146,26147,26148,26150,26153,26154,26155,26156,26158,26160,26162,26163,26167,26168,26169,26170,26171,26173,26175,26176,26178,26180,26181,26182,26183,26184,26185,26186,26189,26190,26192,26193,26200,26201,26203,26204,26205,26206,26208,26210,26211,26213,26215,26217,26218,26219,26220,26221,26225,26226,26227,26229,26232,26233,26235,26236,26237,26239,26240,26241,26243,26245,26246,26248,26249,26250,26251,26253,26254,26255,26256,26258,26259,26260,26261,26264,26265,26266,26267,26268,26270,26271,26272,26273,26274,26275,26276,26277,26278,26281,26282,26283,26284,26285,26287,26288,26289,26290,26291,26293,26294,26295,26296,26298,26299,26300,26301,26303,26304,26305,26306,26307,26308,26309,26310,26311,26312,26313,26314,26315,26316,26317,26318,26319,26320,26321,26322,26323,26324,26325,26326,26327,26328,26330,26334,26335,26336,26337,26338,26339,26340,26341,26343,26344,26346,26347,26348,26349,26350,26351,26353,26357,26358,26360,26362,26363,26365,26369,26370,26371,26372,26373,26374,26375,26380,26382,26383,26385,26386,26387,26390,26392,26393,26394,26396,26398,26400,26401,26402,26403,26404,26405,26407,26409,26414,26416,26418,26419,26422,26423,26424,26425,26427,26428,26430,26431,26433,26436,26437,26439,26442,26443,26445,26450,26452,26453,26455,26456,26457,26458,26459,26461,26466,26467,26468,26470,26471,26475,26476,26478,26481,26484,26486,26488,26489,26490,26491,26493,26496,26498,26499,26501,26502,26504,26506,26508,26509,26510,26511,26513,26514,26515,26516,26518,26521,26523,26527,26528,26529,26532,26534,26537,26540,26542,26545,26546,26548,26553,26554,26555,26556,26557,26558,26559,26560,26562,26565,26566,26567,26568,26569,26570,26571,26572,26573,26574,26581,26582,26583,26587,26591,26593,26595,26596,26598,26599,26600,26602,26603,26605,26606,26610,26613,26614,26615,26616,26617,26618,26619,26620,26622,26625,26626,26627,26628,26630,26637,26640,26642,26644,26645,26648,26649,26650,26651,26652,26654,26655,26656,26658,26659,26660,26661,26662,26663,26664,26667,26668,26669,26670,26671,26672,26673,26676,26677,26678,26682,26683,26687,26695,26699,26701,26703,26706,26710,26711,26712,26713,26714,26715,26716,26717,26718,26719,26730,26732,26733,26734,26735,26736,26737,26738,26739,26741,26744,26745,26746,26747,26748,26749,26750,26751,26752,26754,26756,26759,26760,26761,26762,26763,26764,26765,26766,26768,26769,26770,26772,26773,26774,26776,26777,26778,26779,26780,26781,26782,26783,26784,26785,26787,26788,26789,26793,26794,26795,26796,26798,26801,26802,26804,26806,26807,26808,26809,26810,26811,26812,26813,26814,26815,26817,26819,26820,26821,26822,26823,26824,26826,26828,26830,26831,26832,26833,26835,26836,26838,26839,26841,26843,26844,26845,26846,26847,26849,26850,26852,26853,26854,26855,26856,26857,26858,26859,26860,26861,26863,26866,26867,26868,26870,26871,26872,26875,26877,26878,26879,26880,26882,26883,26884,26886,26887,26888,26889,26890,26892,26895,26897,26899,26900,26901,26902,26903,26904,26905,26906,26907,26908,26909,26910,26913,26914,26915,26917,26918,26919,26920,26921,26922,26923,26924,26926,26927,26929,26930,26931,26933,26934,26935,26936,26938,26939,26940,26942,26944,26945,26947,26948,26949,26950,26951,26952,26953,26954,26955,26956,26957,26958,26959,26960,26961,26962,26963,26965,26966,26968,26969,26971,26972,26975,26977,26978,26980,26981,26983,26984,26985,26986,26988,26989,26991,26992,26994,26995,26996,26997,26998,27002,27003,27005,27006,27007,27009,27011,27013,27018,27019,27020,27022,27023,27024,27025,27026,27027,27030,27031,27033,27034,27037,27038,27039,27040,27041,27042,27043,27044,27045,27046,27049,27050,27052,27054,27055,27056,27058,27059,27061,27062,27064,27065,27066,27068,27069,27070,27071,27072,27074,27075,27076,27077,27078,27079,27080,27081,27083,27085,27087,27089,27090,27091,27093,27094,27095,27096,27097,27098,27100,27101,27102,27105,27106,27107,27108,27109,27110,27111,27112,27113,27114,27115,27116,27118,27119,27120,27121,27123,27124,27125,27126,27127,27128,27129,27130,27131,27132,27134,27136,27137,27138,27139,27140,27141,27142,27143,27144,27145,27147,27148,27149,27150,27151,27152,27153,27154,27155,27156,27157,27158,27161,27162,27163,27164,27165,27166,27168,27170,27171,27172,27173,27174,27175,27177,27179,27180,27181,27182,27184,27186,27187,27188,27190,27191,27192,27193,27194,27195,27196,27199,27200,27201,27202,27203,27205,27206,27208,27209,27210,27211,27212,27213,27214,27215,27217,27218,27219,27220,27221,27222,27223,27226,27228,27229,27230,27231,27232,27234,27235,27236,27238,27239,27240,27241,27242,27243,27244,27245,27246,27247,27248,27250,27251,27252,27253,27254,27255,27256,27258,27259,27261,27262,27263,27265,27266,27267,27269,27270,27271,27272,27273,27274,27275,27276,27277,27279,27282,27283,27284,27285,27286,27288,27289,27290,27291,27292,27293,27294,27295,27297,27298,27299,27300,27301,27302,27303,27304,27306,27309,27310,27311,27312,27313,27314,27315,27316,27317,27318,27319,27320,27321,27322,27323,27324,27325,27326,27327,27328,27329,27330,27331,27332,27333,27334,27335,27336,27337,27338,27339,27340,27341,27342,27343,27344,27345,27346,27347,27348,27349,27350,27351,27352,27353,27354,27355,27356,27357,27358,27359,27360,27361,27362,27363,27364,27365,27366,27367,27368,27369,27370,27371,27372,27373,27374,27375,27376,27377,27378,27379,27380,27381,27382,27383,27384,27385,27386,27387,27388,27389,27390,27391,27392,27393,27394,27395,27396,27397,27398,27399,27400,27401,27402,27403,27404,27405,27406,27407,27408,27409,27410,27411,27412,27413,27414,27415,27416,27417,27418,27419,27420,27421,27422,27423,27429,27430,27432,27433,27434,27435,27436,27437,27438,27439,27440,27441,27443,27444,27445,27446,27448,27451,27452,27453,27455,27456,27457,27458,27460,27461,27464,27466,27467,27469,27470,27471,27472,27473,27474,27475,27476,27477,27478,27479,27480,27482,27483,27484,27485,27486,27487,27488,27489,27496,27497,27499,27500,27501,27502,27503,27504,27505,27506,27507,27508,27509,27510,27511,27512,27514,27517,27518,27519,27520,27525,27528,27532,27534,27535,27536,27537,27540,27541,27543,27544,27545,27548,27549,27550,27551,27552,27554,27555,27556,27557,27558,27559,27560,27561,27563,27564,27565,27566,27567,27568,27569,27570,27574,27576,27577,27578,27579,27580,27581,27582,27584,27587,27588,27590,27591,27592,27593,27594,27596,27598,27600,27601,27608,27610,27612,27613,27614,27615,27616,27618,27619,27620,27621,27622,27623,27624,27625,27628,27629,27630,27632,27633,27634,27636,27638,27639,27640,27642,27643,27644,27646,27647,27648,27649,27650,27651,27652,27656,27657,27658,27659,27660,27662,27666,27671,27676,27677,27678,27680,27683,27685,27691,27692,27693,27697,27699,27702,27703,27705,27706,27707,27708,27710,27711,27715,27716,27717,27720,27723,27724,27725,27726,27727,27729,27730,27731,27734,27736,27737,27738,27746,27747,27749,27750,27751,27755,27756,27757,27758,27759,27761,27763,27765,27767,27768,27770,27771,27772,27775,27776,27780,27783,27786,27787,27789,27790,27793,27794,27797,27798,27799,27800,27802,27804,27805,27806,27808,27810,27816,27820,27823,27824,27828,27829,27830,27831,27834,27840,27841,27842,27843,27846,27847,27848,27851,27853,27854,27855,27857,27858,27864,27865,27866,27868,27869,27871,27876,27878,27879,27881,27884,27885,27890,27892,27897,27903,27904,27906,27907,27909,27910,27912,27913,27914,27917,27919,27920,27921,27923,27924,27925,27926,27928,27932,27933,27935,27936,27937,27938,27939,27940,27942,27944,27945,27948,27949,27951,27952,27956,27958,27959,27960,27962,27967,27968,27970,27972,27977,27980,27984,27989,27990,27991,27992,27995,27997,27999,28001,28002,28004,28005,28007,28008,28011,28012,28013,28016,28017,28018,28019,28021,28022,28025,28026,28027,28029,28030,28031,28032,28033,28035,28036,28038,28039,28042,28043,28045,28047,28048,28050,28054,28055,28056,28057,28058,28060,28066,28069,28076,28077,28080,28081,28083,28084,28086,28087,28089,28090,28091,28092,28093,28094,28097,28098,28099,28104,28105,28106,28109,28110,28111,28112,28114,28115,28116,28117,28119,28122,28123,28124,28127,28130,28131,28133,28135,28136,28137,28138,28141,28143,28144,28146,28148,28149,28150,28152,28154,28157,28158,28159,28160,28161,28162,28163,28164,28166,28167,28168,28169,28171,28175,28178,28179,28181,28184,28185,28187,28188,28190,28191,28194,28198,28199,28200,28202,28204,28206,28208,28209,28211,28213,28214,28215,28217,28219,28220,28221,28222,28223,28224,28225,28226,28229,28230,28231,28232,28233,28234,28235,28236,28239,28240,28241,28242,28245,28247,28249,28250,28252,28253,28254,28256,28257,28258,28259,28260,28261,28262,28263,28264,28265,28266,28268,28269,28271,28272,28273,28274,28275,28276,28277,28278,28279,28280,28281,28282,28283,28284,28285,28288,28289,28290,28292,28295,28296,28298,28299,28300,28301,28302,28305,28306,28307,28308,28309,28310,28311,28313,28314,28315,28317,28318,28320,28321,28323,28324,28326,28328,28329,28331,28332,28333,28334,28336,28339,28341,28344,28345,28348,28350,28351,28352,28355,28356,28357,28358,28360,28361,28362,28364,28365,28366,28368,28370,28374,28376,28377,28379,28380,28381,28387,28391,28394,28395,28396,28397,28398,28399,28400,28401,28402,28403,28405,28406,28407,28408,28410,28411,28412,28413,28414,28415,28416,28417,28419,28420,28421,28423,28424,28426,28427,28428,28429,28430,28432,28433,28434,28438,28439,28440,28441,28442,28443,28444,28445,28446,28447,28449,28450,28451,28453,28454,28455,28456,28460,28462,28464,28466,28468,28469,28471,28472,28473,28474,28475,28476,28477,28479,28480,28481,28482,28483,28484,28485,28488,28489,28490,28492,28494,28495,28496,28497,28498,28499,28500,28501,28502,28503,28505,28506,28507,28509,28511,28512,28513,28515,28516,28517,28519,28520,28521,28522,28523,28524,28527,28528,28529,28531,28533,28534,28535,28537,28539,28541,28542,28543,28544,28545,28546,28547,28549,28550,28551,28554,28555,28559,28560,28561,28562,28563,28564,28565,28566,28567,28568,28569,28570,28571,28573,28574,28575,28576,28578,28579,28580,28581,28582,28584,28585,28586,28587,28588,28589,28590,28591,28592,28593,28594,28596,28597,28599,28600,28602,28603,28604,28605,28606,28607,28609,28611,28612,28613,28614,28615,28616,28618,28619,28620,28621,28622,28623,28624,28627,28628,28629,28630,28631,28632,28633,28634,28635,28636,28637,28639,28642,28643,28644,28645,28646,28647,28648,28649,28650,28651,28652,28653,28656,28657,28658,28659,28660,28661,28662,28663,28664,28665,28666,28667,28668,28669,28670,28671,28672,28673,28674,28675,28676,28677,28678,28679,28680,28681,28682,28683,28684,28685,28686,28687,28688,28690,28691,28692,28693,28694,28695,28696,28697,28700,28701,28702,28703,28704,28705,28706,28708,28709,28710,28711,28712,28713,28714,28715,28716,28717,28718,28719,28720,28721,28722,28723,28724,28726,28727,28728,28730,28731,28732,28733,28734,28735,28736,28737,28738,28739,28740,28741,28742,28743,28744,28745,28746,28747,28749,28750,28752,28753,28754,28755,28756,28757,28758,28759,28760,28761,28762,28763,28764,28765,28767,28768,28769,28770,28771,28772,28773,28774,28775,28776,28777,28778,28782,28785,28786,28787,28788,28791,28793,28794,28795,28797,28801,28802,28803,28804,28806,28807,28808,28811,28812,28813,28815,28816,28817,28819,28823,28824,28826,28827,28830,28831,28832,28833,28834,28835,28836,28837,28838,28839,28840,28841,28842,28848,28850,28852,28853,28854,28858,28862,28863,28868,28869,28870,28871,28873,28875,28876,28877,28878,28879,28880,28881,28882,28883,28884,28885,28886,28887,28890,28892,28893,28894,28896,28897,28898,28899,28901,28906,28910,28912,28913,28914,28915,28916,28917,28918,28920,28922,28923,28924,28926,28927,28928,28929,28930,28931,28932,28933,28934,28935,28936,28939,28940,28941,28942,28943,28945,28946,28948,28951,28955,28956,28957,28958,28959,28960,28961,28962,28963,28964,28965,28967,28968,28969,28970,28971,28972,28973,28974,28978,28979,28980,28981,28983,28984,28985,28986,28987,28988,28989,28990,28991,28992,28993,28994,28995,28996,28998,28999,29e3,29001,29003,29005,29007,29008,29009,29010,29011,29012,29013,29014,29015,29016,29017,29018,29019,29021,29023,29024,29025,29026,29027,29029,29033,29034,29035,29036,29037,29039,29040,29041,29044,29045,29046,29047,29049,29051,29052,29054,29055,29056,29057,29058,29059,29061,29062,29063,29064,29065,29067,29068,29069,29070,29072,29073,29074,29075,29077,29078,29079,29082,29083,29084,29085,29086,29089,29090,29091,29092,29093,29094,29095,29097,29098,29099,29101,29102,29103,29104,29105,29106,29108,29110,29111,29112,29114,29115,29116,29117,29118,29119,29120,29121,29122,29124,29125,29126,29127,29128,29129,29130,29131,29132,29133,29135,29136,29137,29138,29139,29142,29143,29144,29145,29146,29147,29148,29149,29150,29151,29153,29154,29155,29156,29158,29160,29161,29162,29163,29164,29165,29167,29168,29169,29170,29171,29172,29173,29174,29175,29176,29178,29179,29180,29181,29182,29183,29184,29185,29186,29187,29188,29189,29191,29192,29193,29194,29195,29196,29197,29198,29199,29200,29201,29202,29203,29204,29205,29206,29207,29208,29209,29210,29211,29212,29214,29215,29216,29217,29218,29219,29220,29221,29222,29223,29225,29227,29229,29230,29231,29234,29235,29236,29242,29244,29246,29248,29249,29250,29251,29252,29253,29254,29257,29258,29259,29262,29263,29264,29265,29267,29268,29269,29271,29272,29274,29276,29278,29280,29283,29284,29285,29288,29290,29291,29292,29293,29296,29297,29299,29300,29302,29303,29304,29307,29308,29309,29314,29315,29317,29318,29319,29320,29321,29324,29326,29328,29329,29331,29332,29333,29334,29335,29336,29337,29338,29339,29340,29341,29342,29344,29345,29346,29347,29348,29349,29350,29351,29352,29353,29354,29355,29358,29361,29362,29363,29365,29370,29371,29372,29373,29374,29375,29376,29381,29382,29383,29385,29386,29387,29388,29391,29393,29395,29396,29397,29398,29400,29402,29403,58566,58567,58568,58569,58570,58571,58572,58573,58574,58575,58576,58577,58578,58579,58580,58581,58582,58583,58584,58585,58586,58587,58588,58589,58590,58591,58592,58593,58594,58595,58596,58597,58598,58599,58600,58601,58602,58603,58604,58605,58606,58607,58608,58609,58610,58611,58612,58613,58614,58615,58616,58617,58618,58619,58620,58621,58622,58623,58624,58625,58626,58627,58628,58629,58630,58631,58632,58633,58634,58635,58636,58637,58638,58639,58640,58641,58642,58643,58644,58645,58646,58647,58648,58649,58650,58651,58652,58653,58654,58655,58656,58657,58658,58659,58660,58661,12288,12289,12290,183,713,711,168,12291,12293,8212,65374,8214,8230,8216,8217,8220,8221,12308,12309,12296,12297,12298,12299,12300,12301,12302,12303,12310,12311,12304,12305,177,215,247,8758,8743,8744,8721,8719,8746,8745,8712,8759,8730,8869,8741,8736,8978,8857,8747,8750,8801,8780,8776,8765,8733,8800,8814,8815,8804,8805,8734,8757,8756,9794,9792,176,8242,8243,8451,65284,164,65504,65505,8240,167,8470,9734,9733,9675,9679,9678,9671,9670,9633,9632,9651,9650,8251,8594,8592,8593,8595,12307,58662,58663,58664,58665,58666,58667,58668,58669,58670,58671,58672,58673,58674,58675,58676,58677,58678,58679,58680,58681,58682,58683,58684,58685,58686,58687,58688,58689,58690,58691,58692,58693,58694,58695,58696,58697,58698,58699,58700,58701,58702,58703,58704,58705,58706,58707,58708,58709,58710,58711,58712,58713,58714,58715,58716,58717,58718,58719,58720,58721,58722,58723,58724,58725,58726,58727,58728,58729,58730,58731,58732,58733,58734,58735,58736,58737,58738,58739,58740,58741,58742,58743,58744,58745,58746,58747,58748,58749,58750,58751,58752,58753,58754,58755,58756,58757,8560,8561,8562,8563,8564,8565,8566,8567,8568,8569,59238,59239,59240,59241,59242,59243,9352,9353,9354,9355,9356,9357,9358,9359,9360,9361,9362,9363,9364,9365,9366,9367,9368,9369,9370,9371,9332,9333,9334,9335,9336,9337,9338,9339,9340,9341,9342,9343,9344,9345,9346,9347,9348,9349,9350,9351,9312,9313,9314,9315,9316,9317,9318,9319,9320,9321,8364,59245,12832,12833,12834,12835,12836,12837,12838,12839,12840,12841,59246,59247,8544,8545,8546,8547,8548,8549,8550,8551,8552,8553,8554,8555,59248,59249,58758,58759,58760,58761,58762,58763,58764,58765,58766,58767,58768,58769,58770,58771,58772,58773,58774,58775,58776,58777,58778,58779,58780,58781,58782,58783,58784,58785,58786,58787,58788,58789,58790,58791,58792,58793,58794,58795,58796,58797,58798,58799,58800,58801,58802,58803,58804,58805,58806,58807,58808,58809,58810,58811,58812,58813,58814,58815,58816,58817,58818,58819,58820,58821,58822,58823,58824,58825,58826,58827,58828,58829,58830,58831,58832,58833,58834,58835,58836,58837,58838,58839,58840,58841,58842,58843,58844,58845,58846,58847,58848,58849,58850,58851,58852,12288,65281,65282,65283,65509,65285,65286,65287,65288,65289,65290,65291,65292,65293,65294,65295,65296,65297,65298,65299,65300,65301,65302,65303,65304,65305,65306,65307,65308,65309,65310,65311,65312,65313,65314,65315,65316,65317,65318,65319,65320,65321,65322,65323,65324,65325,65326,65327,65328,65329,65330,65331,65332,65333,65334,65335,65336,65337,65338,65339,65340,65341,65342,65343,65344,65345,65346,65347,65348,65349,65350,65351,65352,65353,65354,65355,65356,65357,65358,65359,65360,65361,65362,65363,65364,65365,65366,65367,65368,65369,65370,65371,65372,65373,65507,58854,58855,58856,58857,58858,58859,58860,58861,58862,58863,58864,58865,58866,58867,58868,58869,58870,58871,58872,58873,58874,58875,58876,58877,58878,58879,58880,58881,58882,58883,58884,58885,58886,58887,58888,58889,58890,58891,58892,58893,58894,58895,58896,58897,58898,58899,58900,58901,58902,58903,58904,58905,58906,58907,58908,58909,58910,58911,58912,58913,58914,58915,58916,58917,58918,58919,58920,58921,58922,58923,58924,58925,58926,58927,58928,58929,58930,58931,58932,58933,58934,58935,58936,58937,58938,58939,58940,58941,58942,58943,58944,58945,58946,58947,58948,58949,12353,12354,12355,12356,12357,12358,12359,12360,12361,12362,12363,12364,12365,12366,12367,12368,12369,12370,12371,12372,12373,12374,12375,12376,12377,12378,12379,12380,12381,12382,12383,12384,12385,12386,12387,12388,12389,12390,12391,12392,12393,12394,12395,12396,12397,12398,12399,12400,12401,12402,12403,12404,12405,12406,12407,12408,12409,12410,12411,12412,12413,12414,12415,12416,12417,12418,12419,12420,12421,12422,12423,12424,12425,12426,12427,12428,12429,12430,12431,12432,12433,12434,12435,59250,59251,59252,59253,59254,59255,59256,59257,59258,59259,59260,58950,58951,58952,58953,58954,58955,58956,58957,58958,58959,58960,58961,58962,58963,58964,58965,58966,58967,58968,58969,58970,58971,58972,58973,58974,58975,58976,58977,58978,58979,58980,58981,58982,58983,58984,58985,58986,58987,58988,58989,58990,58991,58992,58993,58994,58995,58996,58997,58998,58999,59e3,59001,59002,59003,59004,59005,59006,59007,59008,59009,59010,59011,59012,59013,59014,59015,59016,59017,59018,59019,59020,59021,59022,59023,59024,59025,59026,59027,59028,59029,59030,59031,59032,59033,59034,59035,59036,59037,59038,59039,59040,59041,59042,59043,59044,59045,12449,12450,12451,12452,12453,12454,12455,12456,12457,12458,12459,12460,12461,12462,12463,12464,12465,12466,12467,12468,12469,12470,12471,12472,12473,12474,12475,12476,12477,12478,12479,12480,12481,12482,12483,12484,12485,12486,12487,12488,12489,12490,12491,12492,12493,12494,12495,12496,12497,12498,12499,12500,12501,12502,12503,12504,12505,12506,12507,12508,12509,12510,12511,12512,12513,12514,12515,12516,12517,12518,12519,12520,12521,12522,12523,12524,12525,12526,12527,12528,12529,12530,12531,12532,12533,12534,59261,59262,59263,59264,59265,59266,59267,59268,59046,59047,59048,59049,59050,59051,59052,59053,59054,59055,59056,59057,59058,59059,59060,59061,59062,59063,59064,59065,59066,59067,59068,59069,59070,59071,59072,59073,59074,59075,59076,59077,59078,59079,59080,59081,59082,59083,59084,59085,59086,59087,59088,59089,59090,59091,59092,59093,59094,59095,59096,59097,59098,59099,59100,59101,59102,59103,59104,59105,59106,59107,59108,59109,59110,59111,59112,59113,59114,59115,59116,59117,59118,59119,59120,59121,59122,59123,59124,59125,59126,59127,59128,59129,59130,59131,59132,59133,59134,59135,59136,59137,59138,59139,59140,59141,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,931,932,933,934,935,936,937,59269,59270,59271,59272,59273,59274,59275,59276,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,963,964,965,966,967,968,969,59277,59278,59279,59280,59281,59282,59283,65077,65078,65081,65082,65087,65088,65085,65086,65089,65090,65091,65092,59284,59285,65083,65084,65079,65080,65073,59286,65075,65076,59287,59288,59289,59290,59291,59292,59293,59294,59295,59142,59143,59144,59145,59146,59147,59148,59149,59150,59151,59152,59153,59154,59155,59156,59157,59158,59159,59160,59161,59162,59163,59164,59165,59166,59167,59168,59169,59170,59171,59172,59173,59174,59175,59176,59177,59178,59179,59180,59181,59182,59183,59184,59185,59186,59187,59188,59189,59190,59191,59192,59193,59194,59195,59196,59197,59198,59199,59200,59201,59202,59203,59204,59205,59206,59207,59208,59209,59210,59211,59212,59213,59214,59215,59216,59217,59218,59219,59220,59221,59222,59223,59224,59225,59226,59227,59228,59229,59230,59231,59232,59233,59234,59235,59236,59237,1040,1041,1042,1043,1044,1045,1025,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,59296,59297,59298,59299,59300,59301,59302,59303,59304,59305,59306,59307,59308,59309,59310,1072,1073,1074,1075,1076,1077,1105,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,59311,59312,59313,59314,59315,59316,59317,59318,59319,59320,59321,59322,59323,714,715,729,8211,8213,8229,8245,8453,8457,8598,8599,8600,8601,8725,8735,8739,8786,8806,8807,8895,9552,9553,9554,9555,9556,9557,9558,9559,9560,9561,9562,9563,9564,9565,9566,9567,9568,9569,9570,9571,9572,9573,9574,9575,9576,9577,9578,9579,9580,9581,9582,9583,9584,9585,9586,9587,9601,9602,9603,9604,9605,9606,9607,9608,9609,9610,9611,9612,9613,9614,9615,9619,9620,9621,9660,9661,9698,9699,9700,9701,9737,8853,12306,12317,12318,59324,59325,59326,59327,59328,59329,59330,59331,59332,59333,59334,257,225,462,224,275,233,283,232,299,237,464,236,333,243,466,242,363,250,468,249,470,472,474,476,252,234,593,7743,324,328,505,609,59337,59338,59339,59340,12549,12550,12551,12552,12553,12554,12555,12556,12557,12558,12559,12560,12561,12562,12563,12564,12565,12566,12567,12568,12569,12570,12571,12572,12573,12574,12575,12576,12577,12578,12579,12580,12581,12582,12583,12584,12585,59341,59342,59343,59344,59345,59346,59347,59348,59349,59350,59351,59352,59353,59354,59355,59356,59357,59358,59359,59360,59361,12321,12322,12323,12324,12325,12326,12327,12328,12329,12963,13198,13199,13212,13213,13214,13217,13252,13262,13265,13266,13269,65072,65506,65508,59362,8481,12849,59363,8208,59364,59365,59366,12540,12443,12444,12541,12542,12294,12445,12446,65097,65098,65099,65100,65101,65102,65103,65104,65105,65106,65108,65109,65110,65111,65113,65114,65115,65116,65117,65118,65119,65120,65121,65122,65123,65124,65125,65126,65128,65129,65130,65131,12350,12272,12273,12274,12275,12276,12277,12278,12279,12280,12281,12282,12283,12295,59380,59381,59382,59383,59384,59385,59386,59387,59388,59389,59390,59391,59392,9472,9473,9474,9475,9476,9477,9478,9479,9480,9481,9482,9483,9484,9485,9486,9487,9488,9489,9490,9491,9492,9493,9494,9495,9496,9497,9498,9499,9500,9501,9502,9503,9504,9505,9506,9507,9508,9509,9510,9511,9512,9513,9514,9515,9516,9517,9518,9519,9520,9521,9522,9523,9524,9525,9526,9527,9528,9529,9530,9531,9532,9533,9534,9535,9536,9537,9538,9539,9540,9541,9542,9543,9544,9545,9546,9547,59393,59394,59395,59396,59397,59398,59399,59400,59401,59402,59403,59404,59405,59406,59407,29404,29405,29407,29410,29411,29412,29413,29414,29415,29418,29419,29429,29430,29433,29437,29438,29439,29440,29442,29444,29445,29446,29447,29448,29449,29451,29452,29453,29455,29456,29457,29458,29460,29464,29465,29466,29471,29472,29475,29476,29478,29479,29480,29485,29487,29488,29490,29491,29493,29494,29498,29499,29500,29501,29504,29505,29506,29507,29508,29509,29510,29511,29512,29513,29514,29515,29516,29518,29519,29521,29523,29524,29525,29526,29528,29529,29530,29531,29532,29533,29534,29535,29537,29538,29539,29540,29541,29542,29543,29544,29545,29546,29547,29550,29552,29553,57344,57345,57346,57347,57348,57349,57350,57351,57352,57353,57354,57355,57356,57357,57358,57359,57360,57361,57362,57363,57364,57365,57366,57367,57368,57369,57370,57371,57372,57373,57374,57375,57376,57377,57378,57379,57380,57381,57382,57383,57384,57385,57386,57387,57388,57389,57390,57391,57392,57393,57394,57395,57396,57397,57398,57399,57400,57401,57402,57403,57404,57405,57406,57407,57408,57409,57410,57411,57412,57413,57414,57415,57416,57417,57418,57419,57420,57421,57422,57423,57424,57425,57426,57427,57428,57429,57430,57431,57432,57433,57434,57435,57436,57437,29554,29555,29556,29557,29558,29559,29560,29561,29562,29563,29564,29565,29567,29568,29569,29570,29571,29573,29574,29576,29578,29580,29581,29583,29584,29586,29587,29588,29589,29591,29592,29593,29594,29596,29597,29598,29600,29601,29603,29604,29605,29606,29607,29608,29610,29612,29613,29617,29620,29621,29622,29624,29625,29628,29629,29630,29631,29633,29635,29636,29637,29638,29639,29643,29644,29646,29650,29651,29652,29653,29654,29655,29656,29658,29659,29660,29661,29663,29665,29666,29667,29668,29670,29672,29674,29675,29676,29678,29679,29680,29681,29683,29684,29685,29686,29687,57438,57439,57440,57441,57442,57443,57444,57445,57446,57447,57448,57449,57450,57451,57452,57453,57454,57455,57456,57457,57458,57459,57460,57461,57462,57463,57464,57465,57466,57467,57468,57469,57470,57471,57472,57473,57474,57475,57476,57477,57478,57479,57480,57481,57482,57483,57484,57485,57486,57487,57488,57489,57490,57491,57492,57493,57494,57495,57496,57497,57498,57499,57500,57501,57502,57503,57504,57505,57506,57507,57508,57509,57510,57511,57512,57513,57514,57515,57516,57517,57518,57519,57520,57521,57522,57523,57524,57525,57526,57527,57528,57529,57530,57531,29688,29689,29690,29691,29692,29693,29694,29695,29696,29697,29698,29700,29703,29704,29707,29708,29709,29710,29713,29714,29715,29716,29717,29718,29719,29720,29721,29724,29725,29726,29727,29728,29729,29731,29732,29735,29737,29739,29741,29743,29745,29746,29751,29752,29753,29754,29755,29757,29758,29759,29760,29762,29763,29764,29765,29766,29767,29768,29769,29770,29771,29772,29773,29774,29775,29776,29777,29778,29779,29780,29782,29784,29789,29792,29793,29794,29795,29796,29797,29798,29799,29800,29801,29802,29803,29804,29806,29807,29809,29810,29811,29812,29813,29816,29817,29818,57532,57533,57534,57535,57536,57537,57538,57539,57540,57541,57542,57543,57544,57545,57546,57547,57548,57549,57550,57551,57552,57553,57554,57555,57556,57557,57558,57559,57560,57561,57562,57563,57564,57565,57566,57567,57568,57569,57570,57571,57572,57573,57574,57575,57576,57577,57578,57579,57580,57581,57582,57583,57584,57585,57586,57587,57588,57589,57590,57591,57592,57593,57594,57595,57596,57597,57598,57599,57600,57601,57602,57603,57604,57605,57606,57607,57608,57609,57610,57611,57612,57613,57614,57615,57616,57617,57618,57619,57620,57621,57622,57623,57624,57625,29819,29820,29821,29823,29826,29828,29829,29830,29832,29833,29834,29836,29837,29839,29841,29842,29843,29844,29845,29846,29847,29848,29849,29850,29851,29853,29855,29856,29857,29858,29859,29860,29861,29862,29866,29867,29868,29869,29870,29871,29872,29873,29874,29875,29876,29877,29878,29879,29880,29881,29883,29884,29885,29886,29887,29888,29889,29890,29891,29892,29893,29894,29895,29896,29897,29898,29899,29900,29901,29902,29903,29904,29905,29907,29908,29909,29910,29911,29912,29913,29914,29915,29917,29919,29921,29925,29927,29928,29929,29930,29931,29932,29933,29936,29937,29938,57626,57627,57628,57629,57630,57631,57632,57633,57634,57635,57636,57637,57638,57639,57640,57641,57642,57643,57644,57645,57646,57647,57648,57649,57650,57651,57652,57653,57654,57655,57656,57657,57658,57659,57660,57661,57662,57663,57664,57665,57666,57667,57668,57669,57670,57671,57672,57673,57674,57675,57676,57677,57678,57679,57680,57681,57682,57683,57684,57685,57686,57687,57688,57689,57690,57691,57692,57693,57694,57695,57696,57697,57698,57699,57700,57701,57702,57703,57704,57705,57706,57707,57708,57709,57710,57711,57712,57713,57714,57715,57716,57717,57718,57719,29939,29941,29944,29945,29946,29947,29948,29949,29950,29952,29953,29954,29955,29957,29958,29959,29960,29961,29962,29963,29964,29966,29968,29970,29972,29973,29974,29975,29979,29981,29982,29984,29985,29986,29987,29988,29990,29991,29994,29998,30004,30006,30009,30012,30013,30015,30017,30018,30019,30020,30022,30023,30025,30026,30029,30032,30033,30034,30035,30037,30038,30039,30040,30045,30046,30047,30048,30049,30050,30051,30052,30055,30056,30057,30059,30060,30061,30062,30063,30064,30065,30067,30069,30070,30071,30074,30075,30076,30077,30078,30080,30081,30082,30084,30085,30087,57720,57721,57722,57723,57724,57725,57726,57727,57728,57729,57730,57731,57732,57733,57734,57735,57736,57737,57738,57739,57740,57741,57742,57743,57744,57745,57746,57747,57748,57749,57750,57751,57752,57753,57754,57755,57756,57757,57758,57759,57760,57761,57762,57763,57764,57765,57766,57767,57768,57769,57770,57771,57772,57773,57774,57775,57776,57777,57778,57779,57780,57781,57782,57783,57784,57785,57786,57787,57788,57789,57790,57791,57792,57793,57794,57795,57796,57797,57798,57799,57800,57801,57802,57803,57804,57805,57806,57807,57808,57809,57810,57811,57812,57813,30088,30089,30090,30092,30093,30094,30096,30099,30101,30104,30107,30108,30110,30114,30118,30119,30120,30121,30122,30125,30134,30135,30138,30139,30143,30144,30145,30150,30155,30156,30158,30159,30160,30161,30163,30167,30169,30170,30172,30173,30175,30176,30177,30181,30185,30188,30189,30190,30191,30194,30195,30197,30198,30199,30200,30202,30203,30205,30206,30210,30212,30214,30215,30216,30217,30219,30221,30222,30223,30225,30226,30227,30228,30230,30234,30236,30237,30238,30241,30243,30247,30248,30252,30254,30255,30257,30258,30262,30263,30265,30266,30267,30269,30273,30274,30276,57814,57815,57816,57817,57818,57819,57820,57821,57822,57823,57824,57825,57826,57827,57828,57829,57830,57831,57832,57833,57834,57835,57836,57837,57838,57839,57840,57841,57842,57843,57844,57845,57846,57847,57848,57849,57850,57851,57852,57853,57854,57855,57856,57857,57858,57859,57860,57861,57862,57863,57864,57865,57866,57867,57868,57869,57870,57871,57872,57873,57874,57875,57876,57877,57878,57879,57880,57881,57882,57883,57884,57885,57886,57887,57888,57889,57890,57891,57892,57893,57894,57895,57896,57897,57898,57899,57900,57901,57902,57903,57904,57905,57906,57907,30277,30278,30279,30280,30281,30282,30283,30286,30287,30288,30289,30290,30291,30293,30295,30296,30297,30298,30299,30301,30303,30304,30305,30306,30308,30309,30310,30311,30312,30313,30314,30316,30317,30318,30320,30321,30322,30323,30324,30325,30326,30327,30329,30330,30332,30335,30336,30337,30339,30341,30345,30346,30348,30349,30351,30352,30354,30356,30357,30359,30360,30362,30363,30364,30365,30366,30367,30368,30369,30370,30371,30373,30374,30375,30376,30377,30378,30379,30380,30381,30383,30384,30387,30389,30390,30391,30392,30393,30394,30395,30396,30397,30398,30400,30401,30403,21834,38463,22467,25384,21710,21769,21696,30353,30284,34108,30702,33406,30861,29233,38552,38797,27688,23433,20474,25353,26263,23736,33018,26696,32942,26114,30414,20985,25942,29100,32753,34948,20658,22885,25034,28595,33453,25420,25170,21485,21543,31494,20843,30116,24052,25300,36299,38774,25226,32793,22365,38712,32610,29240,30333,26575,30334,25670,20336,36133,25308,31255,26001,29677,25644,25203,33324,39041,26495,29256,25198,25292,20276,29923,21322,21150,32458,37030,24110,26758,27036,33152,32465,26834,30917,34444,38225,20621,35876,33502,32990,21253,35090,21093,30404,30407,30409,30411,30412,30419,30421,30425,30426,30428,30429,30430,30432,30433,30434,30435,30436,30438,30439,30440,30441,30442,30443,30444,30445,30448,30451,30453,30454,30455,30458,30459,30461,30463,30464,30466,30467,30469,30470,30474,30476,30478,30479,30480,30481,30482,30483,30484,30485,30486,30487,30488,30491,30492,30493,30494,30497,30499,30500,30501,30503,30506,30507,30508,30510,30512,30513,30514,30515,30516,30521,30523,30525,30526,30527,30530,30532,30533,30534,30536,30537,30538,30539,30540,30541,30542,30543,30546,30547,30548,30549,30550,30551,30552,30553,30556,34180,38649,20445,22561,39281,23453,25265,25253,26292,35961,40077,29190,26479,30865,24754,21329,21271,36744,32972,36125,38049,20493,29384,22791,24811,28953,34987,22868,33519,26412,31528,23849,32503,29997,27893,36454,36856,36924,40763,27604,37145,31508,24444,30887,34006,34109,27605,27609,27606,24065,24199,30201,38381,25949,24330,24517,36767,22721,33218,36991,38491,38829,36793,32534,36140,25153,20415,21464,21342,36776,36777,36779,36941,26631,24426,33176,34920,40150,24971,21035,30250,24428,25996,28626,28392,23486,25672,20853,20912,26564,19993,31177,39292,28851,30557,30558,30559,30560,30564,30567,30569,30570,30573,30574,30575,30576,30577,30578,30579,30580,30581,30582,30583,30584,30586,30587,30588,30593,30594,30595,30598,30599,30600,30601,30602,30603,30607,30608,30611,30612,30613,30614,30615,30616,30617,30618,30619,30620,30621,30622,30625,30627,30628,30630,30632,30635,30637,30638,30639,30641,30642,30644,30646,30647,30648,30649,30650,30652,30654,30656,30657,30658,30659,30660,30661,30662,30663,30664,30665,30666,30667,30668,30670,30671,30672,30673,30674,30675,30676,30677,30678,30680,30681,30682,30685,30686,30687,30688,30689,30692,30149,24182,29627,33760,25773,25320,38069,27874,21338,21187,25615,38082,31636,20271,24091,33334,33046,33162,28196,27850,39539,25429,21340,21754,34917,22496,19981,24067,27493,31807,37096,24598,25830,29468,35009,26448,25165,36130,30572,36393,37319,24425,33756,34081,39184,21442,34453,27531,24813,24808,28799,33485,33329,20179,27815,34255,25805,31961,27133,26361,33609,21397,31574,20391,20876,27979,23618,36461,25554,21449,33580,33590,26597,30900,25661,23519,23700,24046,35815,25286,26612,35962,25600,25530,34633,39307,35863,32544,38130,20135,38416,39076,26124,29462,30694,30696,30698,30703,30704,30705,30706,30708,30709,30711,30713,30714,30715,30716,30723,30724,30725,30726,30727,30728,30730,30731,30734,30735,30736,30739,30741,30745,30747,30750,30752,30753,30754,30756,30760,30762,30763,30766,30767,30769,30770,30771,30773,30774,30781,30783,30785,30786,30787,30788,30790,30792,30793,30794,30795,30797,30799,30801,30803,30804,30808,30809,30810,30811,30812,30814,30815,30816,30817,30818,30819,30820,30821,30822,30823,30824,30825,30831,30832,30833,30834,30835,30836,30837,30838,30840,30841,30842,30843,30845,30846,30847,30848,30849,30850,30851,22330,23581,24120,38271,20607,32928,21378,25950,30021,21809,20513,36229,25220,38046,26397,22066,28526,24034,21557,28818,36710,25199,25764,25507,24443,28552,37108,33251,36784,23576,26216,24561,27785,38472,36225,34924,25745,31216,22478,27225,25104,21576,20056,31243,24809,28548,35802,25215,36894,39563,31204,21507,30196,25345,21273,27744,36831,24347,39536,32827,40831,20360,23610,36196,32709,26021,28861,20805,20914,34411,23815,23456,25277,37228,30068,36364,31264,24833,31609,20167,32504,30597,19985,33261,21021,20986,27249,21416,36487,38148,38607,28353,38500,26970,30852,30853,30854,30856,30858,30859,30863,30864,30866,30868,30869,30870,30873,30877,30878,30880,30882,30884,30886,30888,30889,30890,30891,30892,30893,30894,30895,30901,30902,30903,30904,30906,30907,30908,30909,30911,30912,30914,30915,30916,30918,30919,30920,30924,30925,30926,30927,30929,30930,30931,30934,30935,30936,30938,30939,30940,30941,30942,30943,30944,30945,30946,30947,30948,30949,30950,30951,30953,30954,30955,30957,30958,30959,30960,30961,30963,30965,30966,30968,30969,30971,30972,30973,30974,30975,30976,30978,30979,30980,30982,30983,30984,30985,30986,30987,30988,30784,20648,30679,25616,35302,22788,25571,24029,31359,26941,20256,33337,21912,20018,30126,31383,24162,24202,38383,21019,21561,28810,25462,38180,22402,26149,26943,37255,21767,28147,32431,34850,25139,32496,30133,33576,30913,38604,36766,24904,29943,35789,27492,21050,36176,27425,32874,33905,22257,21254,20174,19995,20945,31895,37259,31751,20419,36479,31713,31388,25703,23828,20652,33030,30209,31929,28140,32736,26449,23384,23544,30923,25774,25619,25514,25387,38169,25645,36798,31572,30249,25171,22823,21574,27513,20643,25140,24102,27526,20195,36151,34955,24453,36910,30989,30990,30991,30992,30993,30994,30996,30997,30998,30999,31e3,31001,31002,31003,31004,31005,31007,31008,31009,31010,31011,31013,31014,31015,31016,31017,31018,31019,31020,31021,31022,31023,31024,31025,31026,31027,31029,31030,31031,31032,31033,31037,31039,31042,31043,31044,31045,31047,31050,31051,31052,31053,31054,31055,31056,31057,31058,31060,31061,31064,31065,31073,31075,31076,31078,31081,31082,31083,31084,31086,31088,31089,31090,31091,31092,31093,31094,31097,31099,31100,31101,31102,31103,31106,31107,31110,31111,31112,31113,31115,31116,31117,31118,31120,31121,31122,24608,32829,25285,20025,21333,37112,25528,32966,26086,27694,20294,24814,28129,35806,24377,34507,24403,25377,20826,33633,26723,20992,25443,36424,20498,23707,31095,23548,21040,31291,24764,36947,30423,24503,24471,30340,36460,28783,30331,31561,30634,20979,37011,22564,20302,28404,36842,25932,31515,29380,28068,32735,23265,25269,24213,22320,33922,31532,24093,24351,36882,32532,39072,25474,28359,30872,28857,20856,38747,22443,30005,20291,30008,24215,24806,22880,28096,27583,30857,21500,38613,20939,20993,25481,21514,38035,35843,36300,29241,30879,34678,36845,35853,21472,31123,31124,31125,31126,31127,31128,31129,31131,31132,31133,31134,31135,31136,31137,31138,31139,31140,31141,31142,31144,31145,31146,31147,31148,31149,31150,31151,31152,31153,31154,31156,31157,31158,31159,31160,31164,31167,31170,31172,31173,31175,31176,31178,31180,31182,31183,31184,31187,31188,31190,31191,31193,31194,31195,31196,31197,31198,31200,31201,31202,31205,31208,31210,31212,31214,31217,31218,31219,31220,31221,31222,31223,31225,31226,31228,31230,31231,31233,31236,31237,31239,31240,31241,31242,31244,31247,31248,31249,31250,31251,31253,31254,31256,31257,31259,31260,19969,30447,21486,38025,39030,40718,38189,23450,35746,20002,19996,20908,33891,25026,21160,26635,20375,24683,20923,27934,20828,25238,26007,38497,35910,36887,30168,37117,30563,27602,29322,29420,35835,22581,30585,36172,26460,38208,32922,24230,28193,22930,31471,30701,38203,27573,26029,32526,22534,20817,38431,23545,22697,21544,36466,25958,39039,22244,38045,30462,36929,25479,21702,22810,22842,22427,36530,26421,36346,33333,21057,24816,22549,34558,23784,40517,20420,39069,35769,23077,24694,21380,25212,36943,37122,39295,24681,32780,20799,32819,23572,39285,27953,20108,31261,31263,31265,31266,31268,31269,31270,31271,31272,31273,31274,31275,31276,31277,31278,31279,31280,31281,31282,31284,31285,31286,31288,31290,31294,31296,31297,31298,31299,31300,31301,31303,31304,31305,31306,31307,31308,31309,31310,31311,31312,31314,31315,31316,31317,31318,31320,31321,31322,31323,31324,31325,31326,31327,31328,31329,31330,31331,31332,31333,31334,31335,31336,31337,31338,31339,31340,31341,31342,31343,31345,31346,31347,31349,31355,31356,31357,31358,31362,31365,31367,31369,31370,31371,31372,31374,31375,31376,31379,31380,31385,31386,31387,31390,31393,31394,36144,21457,32602,31567,20240,20047,38400,27861,29648,34281,24070,30058,32763,27146,30718,38034,32321,20961,28902,21453,36820,33539,36137,29359,39277,27867,22346,33459,26041,32938,25151,38450,22952,20223,35775,32442,25918,33778,38750,21857,39134,32933,21290,35837,21536,32954,24223,27832,36153,33452,37210,21545,27675,20998,32439,22367,28954,27774,31881,22859,20221,24575,24868,31914,20016,23553,26539,34562,23792,38155,39118,30127,28925,36898,20911,32541,35773,22857,20964,20315,21542,22827,25975,32932,23413,25206,25282,36752,24133,27679,31526,20239,20440,26381,31395,31396,31399,31401,31402,31403,31406,31407,31408,31409,31410,31412,31413,31414,31415,31416,31417,31418,31419,31420,31421,31422,31424,31425,31426,31427,31428,31429,31430,31431,31432,31433,31434,31436,31437,31438,31439,31440,31441,31442,31443,31444,31445,31447,31448,31450,31451,31452,31453,31457,31458,31460,31463,31464,31465,31466,31467,31468,31470,31472,31473,31474,31475,31476,31477,31478,31479,31480,31483,31484,31486,31488,31489,31490,31493,31495,31497,31500,31501,31502,31504,31506,31507,31510,31511,31512,31514,31516,31517,31519,31521,31522,31523,31527,31529,31533,28014,28074,31119,34993,24343,29995,25242,36741,20463,37340,26023,33071,33105,24220,33104,36212,21103,35206,36171,22797,20613,20184,38428,29238,33145,36127,23500,35747,38468,22919,32538,21648,22134,22030,35813,25913,27010,38041,30422,28297,24178,29976,26438,26577,31487,32925,36214,24863,31174,25954,36195,20872,21018,38050,32568,32923,32434,23703,28207,26464,31705,30347,39640,33167,32660,31957,25630,38224,31295,21578,21733,27468,25601,25096,40509,33011,30105,21106,38761,33883,26684,34532,38401,38548,38124,20010,21508,32473,26681,36319,32789,26356,24218,32697,31535,31536,31538,31540,31541,31542,31543,31545,31547,31549,31551,31552,31553,31554,31555,31556,31558,31560,31562,31565,31566,31571,31573,31575,31577,31580,31582,31583,31585,31587,31588,31589,31590,31591,31592,31593,31594,31595,31596,31597,31599,31600,31603,31604,31606,31608,31610,31612,31613,31615,31617,31618,31619,31620,31622,31623,31624,31625,31626,31627,31628,31630,31631,31633,31634,31635,31638,31640,31641,31642,31643,31646,31647,31648,31651,31652,31653,31662,31663,31664,31666,31667,31669,31670,31671,31673,31674,31675,31676,31677,31678,31679,31680,31682,31683,31684,22466,32831,26775,24037,25915,21151,24685,40858,20379,36524,20844,23467,24339,24041,27742,25329,36129,20849,38057,21246,27807,33503,29399,22434,26500,36141,22815,36764,33735,21653,31629,20272,27837,23396,22993,40723,21476,34506,39592,35895,32929,25925,39038,22266,38599,21038,29916,21072,23521,25346,35074,20054,25296,24618,26874,20851,23448,20896,35266,31649,39302,32592,24815,28748,36143,20809,24191,36891,29808,35268,22317,30789,24402,40863,38394,36712,39740,35809,30328,26690,26588,36330,36149,21053,36746,28378,26829,38149,37101,22269,26524,35065,36807,21704,31685,31688,31689,31690,31691,31693,31694,31695,31696,31698,31700,31701,31702,31703,31704,31707,31708,31710,31711,31712,31714,31715,31716,31719,31720,31721,31723,31724,31725,31727,31728,31730,31731,31732,31733,31734,31736,31737,31738,31739,31741,31743,31744,31745,31746,31747,31748,31749,31750,31752,31753,31754,31757,31758,31760,31761,31762,31763,31764,31765,31767,31768,31769,31770,31771,31772,31773,31774,31776,31777,31778,31779,31780,31781,31784,31785,31787,31788,31789,31790,31791,31792,31793,31794,31795,31796,31797,31798,31799,31801,31802,31803,31804,31805,31806,31810,39608,23401,28023,27686,20133,23475,39559,37219,25e3,37039,38889,21547,28085,23506,20989,21898,32597,32752,25788,25421,26097,25022,24717,28938,27735,27721,22831,26477,33322,22741,22158,35946,27627,37085,22909,32791,21495,28009,21621,21917,33655,33743,26680,31166,21644,20309,21512,30418,35977,38402,27827,28088,36203,35088,40548,36154,22079,40657,30165,24456,29408,24680,21756,20136,27178,34913,24658,36720,21700,28888,34425,40511,27946,23439,24344,32418,21897,20399,29492,21564,21402,20505,21518,21628,20046,24573,29786,22774,33899,32993,34676,29392,31946,28246,31811,31812,31813,31814,31815,31816,31817,31818,31819,31820,31822,31823,31824,31825,31826,31827,31828,31829,31830,31831,31832,31833,31834,31835,31836,31837,31838,31839,31840,31841,31842,31843,31844,31845,31846,31847,31848,31849,31850,31851,31852,31853,31854,31855,31856,31857,31858,31861,31862,31863,31864,31865,31866,31870,31871,31872,31873,31874,31875,31876,31877,31878,31879,31880,31882,31883,31884,31885,31886,31887,31888,31891,31892,31894,31897,31898,31899,31904,31905,31907,31910,31911,31912,31913,31915,31916,31917,31919,31920,31924,31925,31926,31927,31928,31930,31931,24359,34382,21804,25252,20114,27818,25143,33457,21719,21326,29502,28369,30011,21010,21270,35805,27088,24458,24576,28142,22351,27426,29615,26707,36824,32531,25442,24739,21796,30186,35938,28949,28067,23462,24187,33618,24908,40644,30970,34647,31783,30343,20976,24822,29004,26179,24140,24653,35854,28784,25381,36745,24509,24674,34516,22238,27585,24724,24935,21321,24800,26214,36159,31229,20250,28905,27719,35763,35826,32472,33636,26127,23130,39746,27985,28151,35905,27963,20249,28779,33719,25110,24785,38669,36135,31096,20987,22334,22522,26426,30072,31293,31215,31637,31935,31936,31938,31939,31940,31942,31945,31947,31950,31951,31952,31953,31954,31955,31956,31960,31962,31963,31965,31966,31969,31970,31971,31972,31973,31974,31975,31977,31978,31979,31980,31981,31982,31984,31985,31986,31987,31988,31989,31990,31991,31993,31994,31996,31997,31998,31999,32e3,32001,32002,32003,32004,32005,32006,32007,32008,32009,32011,32012,32013,32014,32015,32016,32017,32018,32019,32020,32021,32022,32023,32024,32025,32026,32027,32028,32029,32030,32031,32033,32035,32036,32037,32038,32040,32041,32042,32044,32045,32046,32048,32049,32050,32051,32052,32053,32054,32908,39269,36857,28608,35749,40481,23020,32489,32521,21513,26497,26840,36753,31821,38598,21450,24613,30142,27762,21363,23241,32423,25380,20960,33034,24049,34015,25216,20864,23395,20238,31085,21058,24760,27982,23492,23490,35745,35760,26082,24524,38469,22931,32487,32426,22025,26551,22841,20339,23478,21152,33626,39050,36158,30002,38078,20551,31292,20215,26550,39550,23233,27516,30417,22362,23574,31546,38388,29006,20860,32937,33392,22904,32516,33575,26816,26604,30897,30839,25315,25441,31616,20461,21098,20943,33616,27099,37492,36341,36145,35265,38190,31661,20214,32055,32056,32057,32058,32059,32060,32061,32062,32063,32064,32065,32066,32067,32068,32069,32070,32071,32072,32073,32074,32075,32076,32077,32078,32079,32080,32081,32082,32083,32084,32085,32086,32087,32088,32089,32090,32091,32092,32093,32094,32095,32096,32097,32098,32099,32100,32101,32102,32103,32104,32105,32106,32107,32108,32109,32111,32112,32113,32114,32115,32116,32117,32118,32120,32121,32122,32123,32124,32125,32126,32127,32128,32129,32130,32131,32132,32133,32134,32135,32136,32137,32138,32139,32140,32141,32142,32143,32144,32145,32146,32147,32148,32149,32150,32151,32152,20581,33328,21073,39279,28176,28293,28071,24314,20725,23004,23558,27974,27743,30086,33931,26728,22870,35762,21280,37233,38477,34121,26898,30977,28966,33014,20132,37066,27975,39556,23047,22204,25605,38128,30699,20389,33050,29409,35282,39290,32564,32478,21119,25945,37237,36735,36739,21483,31382,25581,25509,30342,31224,34903,38454,25130,21163,33410,26708,26480,25463,30571,31469,27905,32467,35299,22992,25106,34249,33445,30028,20511,20171,30117,35819,23626,24062,31563,26020,37329,20170,27941,35167,32039,38182,20165,35880,36827,38771,26187,31105,36817,28908,28024,32153,32154,32155,32156,32157,32158,32159,32160,32161,32162,32163,32164,32165,32167,32168,32169,32170,32171,32172,32173,32175,32176,32177,32178,32179,32180,32181,32182,32183,32184,32185,32186,32187,32188,32189,32190,32191,32192,32193,32194,32195,32196,32197,32198,32199,32200,32201,32202,32203,32204,32205,32206,32207,32208,32209,32210,32211,32212,32213,32214,32215,32216,32217,32218,32219,32220,32221,32222,32223,32224,32225,32226,32227,32228,32229,32230,32231,32232,32233,32234,32235,32236,32237,32238,32239,32240,32241,32242,32243,32244,32245,32246,32247,32248,32249,32250,23613,21170,33606,20834,33550,30555,26230,40120,20140,24778,31934,31923,32463,20117,35686,26223,39048,38745,22659,25964,38236,24452,30153,38742,31455,31454,20928,28847,31384,25578,31350,32416,29590,38893,20037,28792,20061,37202,21417,25937,26087,33276,33285,21646,23601,30106,38816,25304,29401,30141,23621,39545,33738,23616,21632,30697,20030,27822,32858,25298,25454,24040,20855,36317,36382,38191,20465,21477,24807,28844,21095,25424,40515,23071,20518,30519,21367,32482,25733,25899,25225,25496,20500,29237,35273,20915,35776,32477,22343,33740,38055,20891,21531,23803,32251,32252,32253,32254,32255,32256,32257,32258,32259,32260,32261,32262,32263,32264,32265,32266,32267,32268,32269,32270,32271,32272,32273,32274,32275,32276,32277,32278,32279,32280,32281,32282,32283,32284,32285,32286,32287,32288,32289,32290,32291,32292,32293,32294,32295,32296,32297,32298,32299,32300,32301,32302,32303,32304,32305,32306,32307,32308,32309,32310,32311,32312,32313,32314,32316,32317,32318,32319,32320,32322,32323,32324,32325,32326,32328,32329,32330,32331,32332,32333,32334,32335,32336,32337,32338,32339,32340,32341,32342,32343,32344,32345,32346,32347,32348,32349,20426,31459,27994,37089,39567,21888,21654,21345,21679,24320,25577,26999,20975,24936,21002,22570,21208,22350,30733,30475,24247,24951,31968,25179,25239,20130,28821,32771,25335,28900,38752,22391,33499,26607,26869,30933,39063,31185,22771,21683,21487,28212,20811,21051,23458,35838,32943,21827,22438,24691,22353,21549,31354,24656,23380,25511,25248,21475,25187,23495,26543,21741,31391,33510,37239,24211,35044,22840,22446,25358,36328,33007,22359,31607,20393,24555,23485,27454,21281,31568,29378,26694,30719,30518,26103,20917,20111,30420,23743,31397,33909,22862,39745,20608,32350,32351,32352,32353,32354,32355,32356,32357,32358,32359,32360,32361,32362,32363,32364,32365,32366,32367,32368,32369,32370,32371,32372,32373,32374,32375,32376,32377,32378,32379,32380,32381,32382,32383,32384,32385,32387,32388,32389,32390,32391,32392,32393,32394,32395,32396,32397,32398,32399,32400,32401,32402,32403,32404,32405,32406,32407,32408,32409,32410,32412,32413,32414,32430,32436,32443,32444,32470,32484,32492,32505,32522,32528,32542,32567,32569,32571,32572,32573,32574,32575,32576,32577,32579,32582,32583,32584,32585,32586,32587,32588,32589,32590,32591,32594,32595,39304,24871,28291,22372,26118,25414,22256,25324,25193,24275,38420,22403,25289,21895,34593,33098,36771,21862,33713,26469,36182,34013,23146,26639,25318,31726,38417,20848,28572,35888,25597,35272,25042,32518,28866,28389,29701,27028,29436,24266,37070,26391,28010,25438,21171,29282,32769,20332,23013,37226,28889,28061,21202,20048,38647,38253,34174,30922,32047,20769,22418,25794,32907,31867,27882,26865,26974,20919,21400,26792,29313,40654,31729,29432,31163,28435,29702,26446,37324,40100,31036,33673,33620,21519,26647,20029,21385,21169,30782,21382,21033,20616,20363,20432,32598,32601,32603,32604,32605,32606,32608,32611,32612,32613,32614,32615,32619,32620,32621,32623,32624,32627,32629,32630,32631,32632,32634,32635,32636,32637,32639,32640,32642,32643,32644,32645,32646,32647,32648,32649,32651,32653,32655,32656,32657,32658,32659,32661,32662,32663,32664,32665,32667,32668,32672,32674,32675,32677,32678,32680,32681,32682,32683,32684,32685,32686,32689,32691,32692,32693,32694,32695,32698,32699,32702,32704,32706,32707,32708,32710,32711,32712,32713,32715,32717,32719,32720,32721,32722,32723,32726,32727,32729,32730,32731,32732,32733,32734,32738,32739,30178,31435,31890,27813,38582,21147,29827,21737,20457,32852,33714,36830,38256,24265,24604,28063,24088,25947,33080,38142,24651,28860,32451,31918,20937,26753,31921,33391,20004,36742,37327,26238,20142,35845,25769,32842,20698,30103,29134,23525,36797,28518,20102,25730,38243,24278,26009,21015,35010,28872,21155,29454,29747,26519,30967,38678,20020,37051,40158,28107,20955,36161,21533,25294,29618,33777,38646,40836,38083,20278,32666,20940,28789,38517,23725,39046,21478,20196,28316,29705,27060,30827,39311,30041,21016,30244,27969,26611,20845,40857,32843,21657,31548,31423,32740,32743,32744,32746,32747,32748,32749,32751,32754,32756,32757,32758,32759,32760,32761,32762,32765,32766,32767,32770,32775,32776,32777,32778,32782,32783,32785,32787,32794,32795,32797,32798,32799,32801,32803,32804,32811,32812,32813,32814,32815,32816,32818,32820,32825,32826,32828,32830,32832,32833,32836,32837,32839,32840,32841,32846,32847,32848,32849,32851,32853,32854,32855,32857,32859,32860,32861,32862,32863,32864,32865,32866,32867,32868,32869,32870,32871,32872,32875,32876,32877,32878,32879,32880,32882,32883,32884,32885,32886,32887,32888,32889,32890,32891,32892,32893,38534,22404,25314,38471,27004,23044,25602,31699,28431,38475,33446,21346,39045,24208,28809,25523,21348,34383,40065,40595,30860,38706,36335,36162,40575,28510,31108,24405,38470,25134,39540,21525,38109,20387,26053,23653,23649,32533,34385,27695,24459,29575,28388,32511,23782,25371,23402,28390,21365,20081,25504,30053,25249,36718,20262,20177,27814,32438,35770,33821,34746,32599,36923,38179,31657,39585,35064,33853,27931,39558,32476,22920,40635,29595,30721,34434,39532,39554,22043,21527,22475,20080,40614,21334,36808,33033,30610,39314,34542,28385,34067,26364,24930,28459,32894,32897,32898,32901,32904,32906,32909,32910,32911,32912,32913,32914,32916,32917,32919,32921,32926,32931,32934,32935,32936,32940,32944,32947,32949,32950,32952,32953,32955,32965,32967,32968,32969,32970,32971,32975,32976,32977,32978,32979,32980,32981,32984,32991,32992,32994,32995,32998,33006,33013,33015,33017,33019,33022,33023,33024,33025,33027,33028,33029,33031,33032,33035,33036,33045,33047,33049,33051,33052,33053,33055,33056,33057,33058,33059,33060,33061,33062,33063,33064,33065,33066,33067,33069,33070,33072,33075,33076,33077,33079,33081,33082,33083,33084,33085,33087,35881,33426,33579,30450,27667,24537,33725,29483,33541,38170,27611,30683,38086,21359,33538,20882,24125,35980,36152,20040,29611,26522,26757,37238,38665,29028,27809,30473,23186,38209,27599,32654,26151,23504,22969,23194,38376,38391,20204,33804,33945,27308,30431,38192,29467,26790,23391,30511,37274,38753,31964,36855,35868,24357,31859,31192,35269,27852,34588,23494,24130,26825,30496,32501,20885,20813,21193,23081,32517,38754,33495,25551,30596,34256,31186,28218,24217,22937,34065,28781,27665,25279,30399,25935,24751,38397,26126,34719,40483,38125,21517,21629,35884,25720,33088,33089,33090,33091,33092,33093,33095,33097,33101,33102,33103,33106,33110,33111,33112,33115,33116,33117,33118,33119,33121,33122,33123,33124,33126,33128,33130,33131,33132,33135,33138,33139,33141,33142,33143,33144,33153,33155,33156,33157,33158,33159,33161,33163,33164,33165,33166,33168,33170,33171,33172,33173,33174,33175,33177,33178,33182,33183,33184,33185,33186,33188,33189,33191,33193,33195,33196,33197,33198,33199,33200,33201,33202,33204,33205,33206,33207,33208,33209,33212,33213,33214,33215,33220,33221,33223,33224,33225,33227,33229,33230,33231,33232,33233,33234,33235,25721,34321,27169,33180,30952,25705,39764,25273,26411,33707,22696,40664,27819,28448,23518,38476,35851,29279,26576,25287,29281,20137,22982,27597,22675,26286,24149,21215,24917,26408,30446,30566,29287,31302,25343,21738,21584,38048,37027,23068,32435,27670,20035,22902,32784,22856,21335,30007,38590,22218,25376,33041,24700,38393,28118,21602,39297,20869,23273,33021,22958,38675,20522,27877,23612,25311,20320,21311,33147,36870,28346,34091,25288,24180,30910,25781,25467,24565,23064,37247,40479,23615,25423,32834,23421,21870,38218,38221,28037,24744,26592,29406,20957,23425,33236,33237,33238,33239,33240,33241,33242,33243,33244,33245,33246,33247,33248,33249,33250,33252,33253,33254,33256,33257,33259,33262,33263,33264,33265,33266,33269,33270,33271,33272,33273,33274,33277,33279,33283,33287,33288,33289,33290,33291,33294,33295,33297,33299,33301,33302,33303,33304,33305,33306,33309,33312,33316,33317,33318,33319,33321,33326,33330,33338,33340,33341,33343,33344,33345,33346,33347,33349,33350,33352,33354,33356,33357,33358,33360,33361,33362,33363,33364,33365,33366,33367,33369,33371,33372,33373,33374,33376,33377,33378,33379,33380,33381,33382,33383,33385,25319,27870,29275,25197,38062,32445,33043,27987,20892,24324,22900,21162,24594,22899,26262,34384,30111,25386,25062,31983,35834,21734,27431,40485,27572,34261,21589,20598,27812,21866,36276,29228,24085,24597,29750,25293,25490,29260,24472,28227,27966,25856,28504,30424,30928,30460,30036,21028,21467,20051,24222,26049,32810,32982,25243,21638,21032,28846,34957,36305,27873,21624,32986,22521,35060,36180,38506,37197,20329,27803,21943,30406,30768,25256,28921,28558,24429,34028,26842,30844,31735,33192,26379,40527,25447,30896,22383,30738,38713,25209,25259,21128,29749,27607,33386,33387,33388,33389,33393,33397,33398,33399,33400,33403,33404,33408,33409,33411,33413,33414,33415,33417,33420,33424,33427,33428,33429,33430,33434,33435,33438,33440,33442,33443,33447,33458,33461,33462,33466,33467,33468,33471,33472,33474,33475,33477,33478,33481,33488,33494,33497,33498,33501,33506,33511,33512,33513,33514,33516,33517,33518,33520,33522,33523,33525,33526,33528,33530,33532,33533,33534,33535,33536,33546,33547,33549,33552,33554,33555,33558,33560,33561,33565,33566,33567,33568,33569,33570,33571,33572,33573,33574,33577,33578,33582,33584,33586,33591,33595,33597,21860,33086,30130,30382,21305,30174,20731,23617,35692,31687,20559,29255,39575,39128,28418,29922,31080,25735,30629,25340,39057,36139,21697,32856,20050,22378,33529,33805,24179,20973,29942,35780,23631,22369,27900,39047,23110,30772,39748,36843,31893,21078,25169,38138,20166,33670,33889,33769,33970,22484,26420,22275,26222,28006,35889,26333,28689,26399,27450,26646,25114,22971,19971,20932,28422,26578,27791,20854,26827,22855,27495,30054,23822,33040,40784,26071,31048,31041,39569,36215,23682,20062,20225,21551,22865,30732,22120,27668,36804,24323,27773,27875,35755,25488,33598,33599,33601,33602,33604,33605,33608,33610,33611,33612,33613,33614,33619,33621,33622,33623,33624,33625,33629,33634,33648,33649,33650,33651,33652,33653,33654,33657,33658,33662,33663,33664,33665,33666,33667,33668,33671,33672,33674,33675,33676,33677,33679,33680,33681,33684,33685,33686,33687,33689,33690,33693,33695,33697,33698,33699,33700,33701,33702,33703,33708,33709,33710,33711,33717,33723,33726,33727,33730,33731,33732,33734,33736,33737,33739,33741,33742,33744,33745,33746,33747,33749,33751,33753,33754,33755,33758,33762,33763,33764,33766,33767,33768,33771,33772,33773,24688,27965,29301,25190,38030,38085,21315,36801,31614,20191,35878,20094,40660,38065,38067,21069,28508,36963,27973,35892,22545,23884,27424,27465,26538,21595,33108,32652,22681,34103,24378,25250,27207,38201,25970,24708,26725,30631,20052,20392,24039,38808,25772,32728,23789,20431,31373,20999,33540,19988,24623,31363,38054,20405,20146,31206,29748,21220,33465,25810,31165,23517,27777,38738,36731,27682,20542,21375,28165,25806,26228,27696,24773,39031,35831,24198,29756,31351,31179,19992,37041,29699,27714,22234,37195,27845,36235,21306,34502,26354,36527,23624,39537,28192,33774,33775,33779,33780,33781,33782,33783,33786,33787,33788,33790,33791,33792,33794,33797,33799,33800,33801,33802,33808,33810,33811,33812,33813,33814,33815,33817,33818,33819,33822,33823,33824,33825,33826,33827,33833,33834,33835,33836,33837,33838,33839,33840,33842,33843,33844,33845,33846,33847,33849,33850,33851,33854,33855,33856,33857,33858,33859,33860,33861,33863,33864,33865,33866,33867,33868,33869,33870,33871,33872,33874,33875,33876,33877,33878,33880,33885,33886,33887,33888,33890,33892,33893,33894,33895,33896,33898,33902,33903,33904,33906,33908,33911,33913,33915,33916,21462,23094,40843,36259,21435,22280,39079,26435,37275,27849,20840,30154,25331,29356,21048,21149,32570,28820,30264,21364,40522,27063,30830,38592,35033,32676,28982,29123,20873,26579,29924,22756,25880,22199,35753,39286,25200,32469,24825,28909,22764,20161,20154,24525,38887,20219,35748,20995,22922,32427,25172,20173,26085,25102,33592,33993,33635,34701,29076,28342,23481,32466,20887,25545,26580,32905,33593,34837,20754,23418,22914,36785,20083,27741,20837,35109,36719,38446,34122,29790,38160,38384,28070,33509,24369,25746,27922,33832,33134,40131,22622,36187,19977,21441,33917,33918,33919,33920,33921,33923,33924,33925,33926,33930,33933,33935,33936,33937,33938,33939,33940,33941,33942,33944,33946,33947,33949,33950,33951,33952,33954,33955,33956,33957,33958,33959,33960,33961,33962,33963,33964,33965,33966,33968,33969,33971,33973,33974,33975,33979,33980,33982,33984,33986,33987,33989,33990,33991,33992,33995,33996,33998,33999,34002,34004,34005,34007,34008,34009,34010,34011,34012,34014,34017,34018,34020,34023,34024,34025,34026,34027,34029,34030,34031,34033,34034,34035,34036,34037,34038,34039,34040,34041,34042,34043,34045,34046,34048,34049,34050,20254,25955,26705,21971,20007,25620,39578,25195,23234,29791,33394,28073,26862,20711,33678,30722,26432,21049,27801,32433,20667,21861,29022,31579,26194,29642,33515,26441,23665,21024,29053,34923,38378,38485,25797,36193,33203,21892,27733,25159,32558,22674,20260,21830,36175,26188,19978,23578,35059,26786,25422,31245,28903,33421,21242,38902,23569,21736,37045,32461,22882,36170,34503,33292,33293,36198,25668,23556,24913,28041,31038,35774,30775,30003,21627,20280,36523,28145,23072,32453,31070,27784,23457,23158,29978,32958,24910,28183,22768,29983,29989,29298,21319,32499,34051,34052,34053,34054,34055,34056,34057,34058,34059,34061,34062,34063,34064,34066,34068,34069,34070,34072,34073,34075,34076,34077,34078,34080,34082,34083,34084,34085,34086,34087,34088,34089,34090,34093,34094,34095,34096,34097,34098,34099,34100,34101,34102,34110,34111,34112,34113,34114,34116,34117,34118,34119,34123,34124,34125,34126,34127,34128,34129,34130,34131,34132,34133,34135,34136,34138,34139,34140,34141,34143,34144,34145,34146,34147,34149,34150,34151,34153,34154,34155,34156,34157,34158,34159,34160,34161,34163,34165,34166,34167,34168,34172,34173,34175,34176,34177,30465,30427,21097,32988,22307,24072,22833,29422,26045,28287,35799,23608,34417,21313,30707,25342,26102,20160,39135,34432,23454,35782,21490,30690,20351,23630,39542,22987,24335,31034,22763,19990,26623,20107,25325,35475,36893,21183,26159,21980,22124,36866,20181,20365,37322,39280,27663,24066,24643,23460,35270,35797,25910,25163,39318,23432,23551,25480,21806,21463,30246,20861,34092,26530,26803,27530,25234,36755,21460,33298,28113,30095,20070,36174,23408,29087,34223,26257,26329,32626,34560,40653,40736,23646,26415,36848,26641,26463,25101,31446,22661,24246,25968,28465,34178,34179,34182,34184,34185,34186,34187,34188,34189,34190,34192,34193,34194,34195,34196,34197,34198,34199,34200,34201,34202,34205,34206,34207,34208,34209,34210,34211,34213,34214,34215,34217,34219,34220,34221,34225,34226,34227,34228,34229,34230,34232,34234,34235,34236,34237,34238,34239,34240,34242,34243,34244,34245,34246,34247,34248,34250,34251,34252,34253,34254,34257,34258,34260,34262,34263,34264,34265,34266,34267,34269,34270,34271,34272,34273,34274,34275,34277,34278,34279,34280,34282,34283,34284,34285,34286,34287,34288,34289,34290,34291,34292,34293,34294,34295,34296,24661,21047,32781,25684,34928,29993,24069,26643,25332,38684,21452,29245,35841,27700,30561,31246,21550,30636,39034,33308,35828,30805,26388,28865,26031,25749,22070,24605,31169,21496,19997,27515,32902,23546,21987,22235,20282,20284,39282,24051,26494,32824,24578,39042,36865,23435,35772,35829,25628,33368,25822,22013,33487,37221,20439,32032,36895,31903,20723,22609,28335,23487,35785,32899,37240,33948,31639,34429,38539,38543,32485,39635,30862,23681,31319,36930,38567,31071,23385,25439,31499,34001,26797,21766,32553,29712,32034,38145,25152,22604,20182,23427,22905,22612,34297,34298,34300,34301,34302,34304,34305,34306,34307,34308,34310,34311,34312,34313,34314,34315,34316,34317,34318,34319,34320,34322,34323,34324,34325,34327,34328,34329,34330,34331,34332,34333,34334,34335,34336,34337,34338,34339,34340,34341,34342,34344,34346,34347,34348,34349,34350,34351,34352,34353,34354,34355,34356,34357,34358,34359,34361,34362,34363,34365,34366,34367,34368,34369,34370,34371,34372,34373,34374,34375,34376,34377,34378,34379,34380,34386,34387,34389,34390,34391,34392,34393,34395,34396,34397,34399,34400,34401,34403,34404,34405,34406,34407,34408,34409,34410,29549,25374,36427,36367,32974,33492,25260,21488,27888,37214,22826,24577,27760,22349,25674,36138,30251,28393,22363,27264,30192,28525,35885,35848,22374,27631,34962,30899,25506,21497,28845,27748,22616,25642,22530,26848,33179,21776,31958,20504,36538,28108,36255,28907,25487,28059,28372,32486,33796,26691,36867,28120,38518,35752,22871,29305,34276,33150,30140,35466,26799,21076,36386,38161,25552,39064,36420,21884,20307,26367,22159,24789,28053,21059,23625,22825,28155,22635,3e4,29980,24684,33300,33094,25361,26465,36834,30522,36339,36148,38081,24086,21381,21548,28867,34413,34415,34416,34418,34419,34420,34421,34422,34423,34424,34435,34436,34437,34438,34439,34440,34441,34446,34447,34448,34449,34450,34452,34454,34455,34456,34457,34458,34459,34462,34463,34464,34465,34466,34469,34470,34475,34477,34478,34482,34483,34487,34488,34489,34491,34492,34493,34494,34495,34497,34498,34499,34501,34504,34508,34509,34514,34515,34517,34518,34519,34522,34524,34525,34528,34529,34530,34531,34533,34534,34535,34536,34538,34539,34540,34543,34549,34550,34551,34554,34555,34556,34557,34559,34561,34564,34565,34566,34571,34572,34574,34575,34576,34577,34580,34582,27712,24311,20572,20141,24237,25402,33351,36890,26704,37230,30643,21516,38108,24420,31461,26742,25413,31570,32479,30171,20599,25237,22836,36879,20984,31171,31361,22270,24466,36884,28034,23648,22303,21520,20820,28237,22242,25512,39059,33151,34581,35114,36864,21534,23663,33216,25302,25176,33073,40501,38464,39534,39548,26925,22949,25299,21822,25366,21703,34521,27964,23043,29926,34972,27498,22806,35916,24367,28286,29609,39037,20024,28919,23436,30871,25405,26202,30358,24779,23451,23113,19975,33109,27754,29579,20129,26505,32593,24448,26106,26395,24536,22916,23041,34585,34587,34589,34591,34592,34596,34598,34599,34600,34602,34603,34604,34605,34607,34608,34610,34611,34613,34614,34616,34617,34618,34620,34621,34624,34625,34626,34627,34628,34629,34630,34634,34635,34637,34639,34640,34641,34642,34644,34645,34646,34648,34650,34651,34652,34653,34654,34655,34657,34658,34662,34663,34664,34665,34666,34667,34668,34669,34671,34673,34674,34675,34677,34679,34680,34681,34682,34687,34688,34689,34692,34694,34695,34697,34698,34700,34702,34703,34704,34705,34706,34708,34709,34710,34712,34713,34714,34715,34716,34717,34718,34720,34721,34722,34723,34724,24013,24494,21361,38886,36829,26693,22260,21807,24799,20026,28493,32500,33479,33806,22996,20255,20266,23614,32428,26410,34074,21619,30031,32963,21890,39759,20301,28205,35859,23561,24944,21355,30239,28201,34442,25991,38395,32441,21563,31283,32010,38382,21985,32705,29934,25373,34583,28065,31389,25105,26017,21351,25569,27779,24043,21596,38056,20044,27745,35820,23627,26080,33436,26791,21566,21556,27595,27494,20116,25410,21320,33310,20237,20398,22366,25098,38654,26212,29289,21247,21153,24735,35823,26132,29081,26512,35199,30802,30717,26224,22075,21560,38177,29306,34725,34726,34727,34729,34730,34734,34736,34737,34738,34740,34742,34743,34744,34745,34747,34748,34750,34751,34753,34754,34755,34756,34757,34759,34760,34761,34764,34765,34766,34767,34768,34772,34773,34774,34775,34776,34777,34778,34780,34781,34782,34783,34785,34786,34787,34788,34790,34791,34792,34793,34795,34796,34797,34799,34800,34801,34802,34803,34804,34805,34806,34807,34808,34810,34811,34812,34813,34815,34816,34817,34818,34820,34821,34822,34823,34824,34825,34827,34828,34829,34830,34831,34832,34833,34834,34836,34839,34840,34841,34842,34844,34845,34846,34847,34848,34851,31232,24687,24076,24713,33181,22805,24796,29060,28911,28330,27728,29312,27268,34989,24109,20064,23219,21916,38115,27927,31995,38553,25103,32454,30606,34430,21283,38686,36758,26247,23777,20384,29421,19979,21414,22799,21523,25472,38184,20808,20185,40092,32420,21688,36132,34900,33335,38386,28046,24358,23244,26174,38505,29616,29486,21439,33146,39301,32673,23466,38519,38480,32447,30456,21410,38262,39321,31665,35140,28248,20065,32724,31077,35814,24819,21709,20139,39033,24055,27233,20687,21521,35937,33831,30813,38660,21066,21742,22179,38144,28040,23477,28102,26195,34852,34853,34854,34855,34856,34857,34858,34859,34860,34861,34862,34863,34864,34865,34867,34868,34869,34870,34871,34872,34874,34875,34877,34878,34879,34881,34882,34883,34886,34887,34888,34889,34890,34891,34894,34895,34896,34897,34898,34899,34901,34902,34904,34906,34907,34908,34909,34910,34911,34912,34918,34919,34922,34925,34927,34929,34931,34932,34933,34934,34936,34937,34938,34939,34940,34944,34947,34950,34951,34953,34954,34956,34958,34959,34960,34961,34963,34964,34965,34967,34968,34969,34970,34971,34973,34974,34975,34976,34977,34979,34981,34982,34983,34984,34985,34986,23567,23389,26657,32918,21880,31505,25928,26964,20123,27463,34638,38795,21327,25375,25658,37034,26012,32961,35856,20889,26800,21368,34809,25032,27844,27899,35874,23633,34218,33455,38156,27427,36763,26032,24571,24515,20449,34885,26143,33125,29481,24826,20852,21009,22411,24418,37026,34892,37266,24184,26447,24615,22995,20804,20982,33016,21256,27769,38596,29066,20241,20462,32670,26429,21957,38152,31168,34966,32483,22687,25100,38656,34394,22040,39035,24464,35768,33988,37207,21465,26093,24207,30044,24676,32110,23167,32490,32493,36713,21927,23459,24748,26059,29572,34988,34990,34991,34992,34994,34995,34996,34997,34998,35e3,35001,35002,35003,35005,35006,35007,35008,35011,35012,35015,35016,35018,35019,35020,35021,35023,35024,35025,35027,35030,35031,35034,35035,35036,35037,35038,35040,35041,35046,35047,35049,35050,35051,35052,35053,35054,35055,35058,35061,35062,35063,35066,35067,35069,35071,35072,35073,35075,35076,35077,35078,35079,35080,35081,35083,35084,35085,35086,35087,35089,35092,35093,35094,35095,35096,35100,35101,35102,35103,35104,35106,35107,35108,35110,35111,35112,35113,35116,35117,35118,35119,35121,35122,35123,35125,35127,36873,30307,30505,32474,38772,34203,23398,31348,38634,34880,21195,29071,24490,26092,35810,23547,39535,24033,27529,27739,35757,35759,36874,36805,21387,25276,40486,40493,21568,20011,33469,29273,34460,23830,34905,28079,38597,21713,20122,35766,28937,21693,38409,28895,28153,30416,20005,30740,34578,23721,24310,35328,39068,38414,28814,27839,22852,25513,30524,34893,28436,33395,22576,29141,21388,30746,38593,21761,24422,28976,23476,35866,39564,27523,22830,40495,31207,26472,25196,20335,30113,32650,27915,38451,27687,20208,30162,20859,26679,28478,36992,33136,22934,29814,35128,35129,35130,35131,35132,35133,35134,35135,35136,35138,35139,35141,35142,35143,35144,35145,35146,35147,35148,35149,35150,35151,35152,35153,35154,35155,35156,35157,35158,35159,35160,35161,35162,35163,35164,35165,35168,35169,35170,35171,35172,35173,35175,35176,35177,35178,35179,35180,35181,35182,35183,35184,35185,35186,35187,35188,35189,35190,35191,35192,35193,35194,35196,35197,35198,35200,35202,35204,35205,35207,35208,35209,35210,35211,35212,35213,35214,35215,35216,35217,35218,35219,35220,35221,35222,35223,35224,35225,35226,35227,35228,35229,35230,35231,35232,35233,25671,23591,36965,31377,35875,23002,21676,33280,33647,35201,32768,26928,22094,32822,29239,37326,20918,20063,39029,25494,19994,21494,26355,33099,22812,28082,19968,22777,21307,25558,38129,20381,20234,34915,39056,22839,36951,31227,20202,33008,30097,27778,23452,23016,24413,26885,34433,20506,24050,20057,30691,20197,33402,25233,26131,37009,23673,20159,24441,33222,36920,32900,30123,20134,35028,24847,27589,24518,20041,30410,28322,35811,35758,35850,35793,24322,32764,32716,32462,33589,33643,22240,27575,38899,38452,23035,21535,38134,28139,23493,39278,23609,24341,38544,35234,35235,35236,35237,35238,35239,35240,35241,35242,35243,35244,35245,35246,35247,35248,35249,35250,35251,35252,35253,35254,35255,35256,35257,35258,35259,35260,35261,35262,35263,35264,35267,35277,35283,35284,35285,35287,35288,35289,35291,35293,35295,35296,35297,35298,35300,35303,35304,35305,35306,35308,35309,35310,35312,35313,35314,35316,35317,35318,35319,35320,35321,35322,35323,35324,35325,35326,35327,35329,35330,35331,35332,35333,35334,35336,35337,35338,35339,35340,35341,35342,35343,35344,35345,35346,35347,35348,35349,35350,35351,35352,35353,35354,35355,35356,35357,21360,33521,27185,23156,40560,24212,32552,33721,33828,33829,33639,34631,36814,36194,30408,24433,39062,30828,26144,21727,25317,20323,33219,30152,24248,38605,36362,34553,21647,27891,28044,27704,24703,21191,29992,24189,20248,24736,24551,23588,30001,37038,38080,29369,27833,28216,37193,26377,21451,21491,20305,37321,35825,21448,24188,36802,28132,20110,30402,27014,34398,24858,33286,20313,20446,36926,40060,24841,28189,28180,38533,20104,23089,38632,19982,23679,31161,23431,35821,32701,29577,22495,33419,37057,21505,36935,21947,23786,24481,24840,27442,29425,32946,35465,35358,35359,35360,35361,35362,35363,35364,35365,35366,35367,35368,35369,35370,35371,35372,35373,35374,35375,35376,35377,35378,35379,35380,35381,35382,35383,35384,35385,35386,35387,35388,35389,35391,35392,35393,35394,35395,35396,35397,35398,35399,35401,35402,35403,35404,35405,35406,35407,35408,35409,35410,35411,35412,35413,35414,35415,35416,35417,35418,35419,35420,35421,35422,35423,35424,35425,35426,35427,35428,35429,35430,35431,35432,35433,35434,35435,35436,35437,35438,35439,35440,35441,35442,35443,35444,35445,35446,35447,35448,35450,35451,35452,35453,35454,35455,35456,28020,23507,35029,39044,35947,39533,40499,28170,20900,20803,22435,34945,21407,25588,36757,22253,21592,22278,29503,28304,32536,36828,33489,24895,24616,38498,26352,32422,36234,36291,38053,23731,31908,26376,24742,38405,32792,20113,37095,21248,38504,20801,36816,34164,37213,26197,38901,23381,21277,30776,26434,26685,21705,28798,23472,36733,20877,22312,21681,25874,26242,36190,36163,33039,33900,36973,31967,20991,34299,26531,26089,28577,34468,36481,22122,36896,30338,28790,29157,36131,25321,21017,27901,36156,24590,22686,24974,26366,36192,25166,21939,28195,26413,36711,35457,35458,35459,35460,35461,35462,35463,35464,35467,35468,35469,35470,35471,35472,35473,35474,35476,35477,35478,35479,35480,35481,35482,35483,35484,35485,35486,35487,35488,35489,35490,35491,35492,35493,35494,35495,35496,35497,35498,35499,35500,35501,35502,35503,35504,35505,35506,35507,35508,35509,35510,35511,35512,35513,35514,35515,35516,35517,35518,35519,35520,35521,35522,35523,35524,35525,35526,35527,35528,35529,35530,35531,35532,35533,35534,35535,35536,35537,35538,35539,35540,35541,35542,35543,35544,35545,35546,35547,35548,35549,35550,35551,35552,35553,35554,35555,38113,38392,30504,26629,27048,21643,20045,28856,35784,25688,25995,23429,31364,20538,23528,30651,27617,35449,31896,27838,30415,26025,36759,23853,23637,34360,26632,21344,25112,31449,28251,32509,27167,31456,24432,28467,24352,25484,28072,26454,19976,24080,36134,20183,32960,30260,38556,25307,26157,25214,27836,36213,29031,32617,20806,32903,21484,36974,25240,21746,34544,36761,32773,38167,34071,36825,27993,29645,26015,30495,29956,30759,33275,36126,38024,20390,26517,30137,35786,38663,25391,38215,38453,33976,25379,30529,24449,29424,20105,24596,25972,25327,27491,25919,35556,35557,35558,35559,35560,35561,35562,35563,35564,35565,35566,35567,35568,35569,35570,35571,35572,35573,35574,35575,35576,35577,35578,35579,35580,35581,35582,35583,35584,35585,35586,35587,35588,35589,35590,35592,35593,35594,35595,35596,35597,35598,35599,35600,35601,35602,35603,35604,35605,35606,35607,35608,35609,35610,35611,35612,35613,35614,35615,35616,35617,35618,35619,35620,35621,35623,35624,35625,35626,35627,35628,35629,35630,35631,35632,35633,35634,35635,35636,35637,35638,35639,35640,35641,35642,35643,35644,35645,35646,35647,35648,35649,35650,35651,35652,35653,24103,30151,37073,35777,33437,26525,25903,21553,34584,30693,32930,33026,27713,20043,32455,32844,30452,26893,27542,25191,20540,20356,22336,25351,27490,36286,21482,26088,32440,24535,25370,25527,33267,33268,32622,24092,23769,21046,26234,31209,31258,36136,28825,30164,28382,27835,31378,20013,30405,24544,38047,34935,32456,31181,32959,37325,20210,20247,33311,21608,24030,27954,35788,31909,36724,32920,24090,21650,30385,23449,26172,39588,29664,26666,34523,26417,29482,35832,35803,36880,31481,28891,29038,25284,30633,22065,20027,33879,26609,21161,34496,36142,38136,31569,35654,35655,35656,35657,35658,35659,35660,35661,35662,35663,35664,35665,35666,35667,35668,35669,35670,35671,35672,35673,35674,35675,35676,35677,35678,35679,35680,35681,35682,35683,35684,35685,35687,35688,35689,35690,35691,35693,35694,35695,35696,35697,35698,35699,35700,35701,35702,35703,35704,35705,35706,35707,35708,35709,35710,35711,35712,35713,35714,35715,35716,35717,35718,35719,35720,35721,35722,35723,35724,35725,35726,35727,35728,35729,35730,35731,35732,35733,35734,35735,35736,35737,35738,35739,35740,35741,35742,35743,35756,35761,35771,35783,35792,35818,35849,35870,20303,27880,31069,39547,25235,29226,25341,19987,30742,36716,25776,36186,31686,26729,24196,35013,22918,25758,22766,29366,26894,38181,36861,36184,22368,32512,35846,20934,25417,25305,21331,26700,29730,33537,37196,21828,30528,28796,27978,20857,21672,36164,23039,28363,28100,23388,32043,20180,31869,28371,23376,33258,28173,23383,39683,26837,36394,23447,32508,24635,32437,37049,36208,22863,25549,31199,36275,21330,26063,31062,35781,38459,32452,38075,32386,22068,37257,26368,32618,23562,36981,26152,24038,20304,26590,20570,20316,22352,24231,59408,59409,59410,59411,59412,35896,35897,35898,35899,35900,35901,35902,35903,35904,35906,35907,35908,35909,35912,35914,35915,35917,35918,35919,35920,35921,35922,35923,35924,35926,35927,35928,35929,35931,35932,35933,35934,35935,35936,35939,35940,35941,35942,35943,35944,35945,35948,35949,35950,35951,35952,35953,35954,35956,35957,35958,35959,35963,35964,35965,35966,35967,35968,35969,35971,35972,35974,35975,35976,35979,35981,35982,35983,35984,35985,35986,35987,35989,35990,35991,35993,35994,35995,35996,35997,35998,35999,36e3,36001,36002,36003,36004,36005,36006,36007,36008,36009,36010,36011,36012,36013,20109,19980,20800,19984,24319,21317,19989,20120,19998,39730,23404,22121,20008,31162,20031,21269,20039,22829,29243,21358,27664,22239,32996,39319,27603,30590,40727,20022,20127,40720,20060,20073,20115,33416,23387,21868,22031,20164,21389,21405,21411,21413,21422,38757,36189,21274,21493,21286,21294,21310,36188,21350,21347,20994,21e3,21006,21037,21043,21055,21056,21068,21086,21089,21084,33967,21117,21122,21121,21136,21139,20866,32596,20155,20163,20169,20162,20200,20193,20203,20190,20251,20211,20258,20324,20213,20261,20263,20233,20267,20318,20327,25912,20314,20317,36014,36015,36016,36017,36018,36019,36020,36021,36022,36023,36024,36025,36026,36027,36028,36029,36030,36031,36032,36033,36034,36035,36036,36037,36038,36039,36040,36041,36042,36043,36044,36045,36046,36047,36048,36049,36050,36051,36052,36053,36054,36055,36056,36057,36058,36059,36060,36061,36062,36063,36064,36065,36066,36067,36068,36069,36070,36071,36072,36073,36074,36075,36076,36077,36078,36079,36080,36081,36082,36083,36084,36085,36086,36087,36088,36089,36090,36091,36092,36093,36094,36095,36096,36097,36098,36099,36100,36101,36102,36103,36104,36105,36106,36107,36108,36109,20319,20311,20274,20285,20342,20340,20369,20361,20355,20367,20350,20347,20394,20348,20396,20372,20454,20456,20458,20421,20442,20451,20444,20433,20447,20472,20521,20556,20467,20524,20495,20526,20525,20478,20508,20492,20517,20520,20606,20547,20565,20552,20558,20588,20603,20645,20647,20649,20666,20694,20742,20717,20716,20710,20718,20743,20747,20189,27709,20312,20325,20430,40864,27718,31860,20846,24061,40649,39320,20865,22804,21241,21261,35335,21264,20971,22809,20821,20128,20822,20147,34926,34980,20149,33044,35026,31104,23348,34819,32696,20907,20913,20925,20924,36110,36111,36112,36113,36114,36115,36116,36117,36118,36119,36120,36121,36122,36123,36124,36128,36177,36178,36183,36191,36197,36200,36201,36202,36204,36206,36207,36209,36210,36216,36217,36218,36219,36220,36221,36222,36223,36224,36226,36227,36230,36231,36232,36233,36236,36237,36238,36239,36240,36242,36243,36245,36246,36247,36248,36249,36250,36251,36252,36253,36254,36256,36257,36258,36260,36261,36262,36263,36264,36265,36266,36267,36268,36269,36270,36271,36272,36274,36278,36279,36281,36283,36285,36288,36289,36290,36293,36295,36296,36297,36298,36301,36304,36306,36307,36308,20935,20886,20898,20901,35744,35750,35751,35754,35764,35765,35767,35778,35779,35787,35791,35790,35794,35795,35796,35798,35800,35801,35804,35807,35808,35812,35816,35817,35822,35824,35827,35830,35833,35836,35839,35840,35842,35844,35847,35852,35855,35857,35858,35860,35861,35862,35865,35867,35864,35869,35871,35872,35873,35877,35879,35882,35883,35886,35887,35890,35891,35893,35894,21353,21370,38429,38434,38433,38449,38442,38461,38460,38466,38473,38484,38495,38503,38508,38514,38516,38536,38541,38551,38576,37015,37019,37021,37017,37036,37025,37044,37043,37046,37050,36309,36312,36313,36316,36320,36321,36322,36325,36326,36327,36329,36333,36334,36336,36337,36338,36340,36342,36348,36350,36351,36352,36353,36354,36355,36356,36358,36359,36360,36363,36365,36366,36368,36369,36370,36371,36373,36374,36375,36376,36377,36378,36379,36380,36384,36385,36388,36389,36390,36391,36392,36395,36397,36400,36402,36403,36404,36406,36407,36408,36411,36412,36414,36415,36419,36421,36422,36428,36429,36430,36431,36432,36435,36436,36437,36438,36439,36440,36442,36443,36444,36445,36446,36447,36448,36449,36450,36451,36452,36453,36455,36456,36458,36459,36462,36465,37048,37040,37071,37061,37054,37072,37060,37063,37075,37094,37090,37084,37079,37083,37099,37103,37118,37124,37154,37150,37155,37169,37167,37177,37187,37190,21005,22850,21154,21164,21165,21182,21759,21200,21206,21232,21471,29166,30669,24308,20981,20988,39727,21430,24321,30042,24047,22348,22441,22433,22654,22716,22725,22737,22313,22316,22314,22323,22329,22318,22319,22364,22331,22338,22377,22405,22379,22406,22396,22395,22376,22381,22390,22387,22445,22436,22412,22450,22479,22439,22452,22419,22432,22485,22488,22490,22489,22482,22456,22516,22511,22520,22500,22493,36467,36469,36471,36472,36473,36474,36475,36477,36478,36480,36482,36483,36484,36486,36488,36489,36490,36491,36492,36493,36494,36497,36498,36499,36501,36502,36503,36504,36505,36506,36507,36509,36511,36512,36513,36514,36515,36516,36517,36518,36519,36520,36521,36522,36525,36526,36528,36529,36531,36532,36533,36534,36535,36536,36537,36539,36540,36541,36542,36543,36544,36545,36546,36547,36548,36549,36550,36551,36552,36553,36554,36555,36556,36557,36559,36560,36561,36562,36563,36564,36565,36566,36567,36568,36569,36570,36571,36572,36573,36574,36575,36576,36577,36578,36579,36580,22539,22541,22525,22509,22528,22558,22553,22596,22560,22629,22636,22657,22665,22682,22656,39336,40729,25087,33401,33405,33407,33423,33418,33448,33412,33422,33425,33431,33433,33451,33464,33470,33456,33480,33482,33507,33432,33463,33454,33483,33484,33473,33449,33460,33441,33450,33439,33476,33486,33444,33505,33545,33527,33508,33551,33543,33500,33524,33490,33496,33548,33531,33491,33553,33562,33542,33556,33557,33504,33493,33564,33617,33627,33628,33544,33682,33596,33588,33585,33691,33630,33583,33615,33607,33603,33631,33600,33559,33632,33581,33594,33587,33638,33637,36581,36582,36583,36584,36585,36586,36587,36588,36589,36590,36591,36592,36593,36594,36595,36596,36597,36598,36599,36600,36601,36602,36603,36604,36605,36606,36607,36608,36609,36610,36611,36612,36613,36614,36615,36616,36617,36618,36619,36620,36621,36622,36623,36624,36625,36626,36627,36628,36629,36630,36631,36632,36633,36634,36635,36636,36637,36638,36639,36640,36641,36642,36643,36644,36645,36646,36647,36648,36649,36650,36651,36652,36653,36654,36655,36656,36657,36658,36659,36660,36661,36662,36663,36664,36665,36666,36667,36668,36669,36670,36671,36672,36673,36674,36675,36676,33640,33563,33641,33644,33642,33645,33646,33712,33656,33715,33716,33696,33706,33683,33692,33669,33660,33718,33705,33661,33720,33659,33688,33694,33704,33722,33724,33729,33793,33765,33752,22535,33816,33803,33757,33789,33750,33820,33848,33809,33798,33748,33759,33807,33795,33784,33785,33770,33733,33728,33830,33776,33761,33884,33873,33882,33881,33907,33927,33928,33914,33929,33912,33852,33862,33897,33910,33932,33934,33841,33901,33985,33997,34e3,34022,33981,34003,33994,33983,33978,34016,33953,33977,33972,33943,34021,34019,34060,29965,34104,34032,34105,34079,34106,36677,36678,36679,36680,36681,36682,36683,36684,36685,36686,36687,36688,36689,36690,36691,36692,36693,36694,36695,36696,36697,36698,36699,36700,36701,36702,36703,36704,36705,36706,36707,36708,36709,36714,36736,36748,36754,36765,36768,36769,36770,36772,36773,36774,36775,36778,36780,36781,36782,36783,36786,36787,36788,36789,36791,36792,36794,36795,36796,36799,36800,36803,36806,36809,36810,36811,36812,36813,36815,36818,36822,36823,36826,36832,36833,36835,36839,36844,36847,36849,36850,36852,36853,36854,36858,36859,36860,36862,36863,36871,36872,36876,36878,36883,36885,36888,34134,34107,34047,34044,34137,34120,34152,34148,34142,34170,30626,34115,34162,34171,34212,34216,34183,34191,34169,34222,34204,34181,34233,34231,34224,34259,34241,34268,34303,34343,34309,34345,34326,34364,24318,24328,22844,22849,32823,22869,22874,22872,21263,23586,23589,23596,23604,25164,25194,25247,25275,25290,25306,25303,25326,25378,25334,25401,25419,25411,25517,25590,25457,25466,25486,25524,25453,25516,25482,25449,25518,25532,25586,25592,25568,25599,25540,25566,25550,25682,25542,25534,25669,25665,25611,25627,25632,25612,25638,25633,25694,25732,25709,25750,36889,36892,36899,36900,36901,36903,36904,36905,36906,36907,36908,36912,36913,36914,36915,36916,36919,36921,36922,36925,36927,36928,36931,36933,36934,36936,36937,36938,36939,36940,36942,36948,36949,36950,36953,36954,36956,36957,36958,36959,36960,36961,36964,36966,36967,36969,36970,36971,36972,36975,36976,36977,36978,36979,36982,36983,36984,36985,36986,36987,36988,36990,36993,36996,36997,36998,36999,37001,37002,37004,37005,37006,37007,37008,37010,37012,37014,37016,37018,37020,37022,37023,37024,37028,37029,37031,37032,37033,37035,37037,37042,37047,37052,37053,37055,37056,25722,25783,25784,25753,25786,25792,25808,25815,25828,25826,25865,25893,25902,24331,24530,29977,24337,21343,21489,21501,21481,21480,21499,21522,21526,21510,21579,21586,21587,21588,21590,21571,21537,21591,21593,21539,21554,21634,21652,21623,21617,21604,21658,21659,21636,21622,21606,21661,21712,21677,21698,21684,21714,21671,21670,21715,21716,21618,21667,21717,21691,21695,21708,21721,21722,21724,21673,21674,21668,21725,21711,21726,21787,21735,21792,21757,21780,21747,21794,21795,21775,21777,21799,21802,21863,21903,21941,21833,21869,21825,21845,21823,21840,21820,37058,37059,37062,37064,37065,37067,37068,37069,37074,37076,37077,37078,37080,37081,37082,37086,37087,37088,37091,37092,37093,37097,37098,37100,37102,37104,37105,37106,37107,37109,37110,37111,37113,37114,37115,37116,37119,37120,37121,37123,37125,37126,37127,37128,37129,37130,37131,37132,37133,37134,37135,37136,37137,37138,37139,37140,37141,37142,37143,37144,37146,37147,37148,37149,37151,37152,37153,37156,37157,37158,37159,37160,37161,37162,37163,37164,37165,37166,37168,37170,37171,37172,37173,37174,37175,37176,37178,37179,37180,37181,37182,37183,37184,37185,37186,37188,21815,21846,21877,21878,21879,21811,21808,21852,21899,21970,21891,21937,21945,21896,21889,21919,21886,21974,21905,21883,21983,21949,21950,21908,21913,21994,22007,21961,22047,21969,21995,21996,21972,21990,21981,21956,21999,21989,22002,22003,21964,21965,21992,22005,21988,36756,22046,22024,22028,22017,22052,22051,22014,22016,22055,22061,22104,22073,22103,22060,22093,22114,22105,22108,22092,22100,22150,22116,22129,22123,22139,22140,22149,22163,22191,22228,22231,22237,22241,22261,22251,22265,22271,22276,22282,22281,22300,24079,24089,24084,24081,24113,24123,24124,37189,37191,37192,37201,37203,37204,37205,37206,37208,37209,37211,37212,37215,37216,37222,37223,37224,37227,37229,37235,37242,37243,37244,37248,37249,37250,37251,37252,37254,37256,37258,37262,37263,37267,37268,37269,37270,37271,37272,37273,37276,37277,37278,37279,37280,37281,37284,37285,37286,37287,37288,37289,37291,37292,37296,37297,37298,37299,37302,37303,37304,37305,37307,37308,37309,37310,37311,37312,37313,37314,37315,37316,37317,37318,37320,37323,37328,37330,37331,37332,37333,37334,37335,37336,37337,37338,37339,37341,37342,37343,37344,37345,37346,37347,37348,37349,24119,24132,24148,24155,24158,24161,23692,23674,23693,23696,23702,23688,23704,23705,23697,23706,23708,23733,23714,23741,23724,23723,23729,23715,23745,23735,23748,23762,23780,23755,23781,23810,23811,23847,23846,23854,23844,23838,23814,23835,23896,23870,23860,23869,23916,23899,23919,23901,23915,23883,23882,23913,23924,23938,23961,23965,35955,23991,24005,24435,24439,24450,24455,24457,24460,24469,24473,24476,24488,24493,24501,24508,34914,24417,29357,29360,29364,29367,29368,29379,29377,29390,29389,29394,29416,29423,29417,29426,29428,29431,29441,29427,29443,29434,37350,37351,37352,37353,37354,37355,37356,37357,37358,37359,37360,37361,37362,37363,37364,37365,37366,37367,37368,37369,37370,37371,37372,37373,37374,37375,37376,37377,37378,37379,37380,37381,37382,37383,37384,37385,37386,37387,37388,37389,37390,37391,37392,37393,37394,37395,37396,37397,37398,37399,37400,37401,37402,37403,37404,37405,37406,37407,37408,37409,37410,37411,37412,37413,37414,37415,37416,37417,37418,37419,37420,37421,37422,37423,37424,37425,37426,37427,37428,37429,37430,37431,37432,37433,37434,37435,37436,37437,37438,37439,37440,37441,37442,37443,37444,37445,29435,29463,29459,29473,29450,29470,29469,29461,29474,29497,29477,29484,29496,29489,29520,29517,29527,29536,29548,29551,29566,33307,22821,39143,22820,22786,39267,39271,39272,39273,39274,39275,39276,39284,39287,39293,39296,39300,39303,39306,39309,39312,39313,39315,39316,39317,24192,24209,24203,24214,24229,24224,24249,24245,24254,24243,36179,24274,24273,24283,24296,24298,33210,24516,24521,24534,24527,24579,24558,24580,24545,24548,24574,24581,24582,24554,24557,24568,24601,24629,24614,24603,24591,24589,24617,24619,24586,24639,24609,24696,24697,24699,24698,24642,37446,37447,37448,37449,37450,37451,37452,37453,37454,37455,37456,37457,37458,37459,37460,37461,37462,37463,37464,37465,37466,37467,37468,37469,37470,37471,37472,37473,37474,37475,37476,37477,37478,37479,37480,37481,37482,37483,37484,37485,37486,37487,37488,37489,37490,37491,37493,37494,37495,37496,37497,37498,37499,37500,37501,37502,37503,37504,37505,37506,37507,37508,37509,37510,37511,37512,37513,37514,37515,37516,37517,37519,37520,37521,37522,37523,37524,37525,37526,37527,37528,37529,37530,37531,37532,37533,37534,37535,37536,37537,37538,37539,37540,37541,37542,37543,24682,24701,24726,24730,24749,24733,24707,24722,24716,24731,24812,24763,24753,24797,24792,24774,24794,24756,24864,24870,24853,24867,24820,24832,24846,24875,24906,24949,25004,24980,24999,25015,25044,25077,24541,38579,38377,38379,38385,38387,38389,38390,38396,38398,38403,38404,38406,38408,38410,38411,38412,38413,38415,38418,38421,38422,38423,38425,38426,20012,29247,25109,27701,27732,27740,27722,27811,27781,27792,27796,27788,27752,27753,27764,27766,27782,27817,27856,27860,27821,27895,27896,27889,27863,27826,27872,27862,27898,27883,27886,27825,27859,27887,27902,37544,37545,37546,37547,37548,37549,37551,37552,37553,37554,37555,37556,37557,37558,37559,37560,37561,37562,37563,37564,37565,37566,37567,37568,37569,37570,37571,37572,37573,37574,37575,37577,37578,37579,37580,37581,37582,37583,37584,37585,37586,37587,37588,37589,37590,37591,37592,37593,37594,37595,37596,37597,37598,37599,37600,37601,37602,37603,37604,37605,37606,37607,37608,37609,37610,37611,37612,37613,37614,37615,37616,37617,37618,37619,37620,37621,37622,37623,37624,37625,37626,37627,37628,37629,37630,37631,37632,37633,37634,37635,37636,37637,37638,37639,37640,37641,27961,27943,27916,27971,27976,27911,27908,27929,27918,27947,27981,27950,27957,27930,27983,27986,27988,27955,28049,28015,28062,28064,27998,28051,28052,27996,28e3,28028,28003,28186,28103,28101,28126,28174,28095,28128,28177,28134,28125,28121,28182,28075,28172,28078,28203,28270,28238,28267,28338,28255,28294,28243,28244,28210,28197,28228,28383,28337,28312,28384,28461,28386,28325,28327,28349,28347,28343,28375,28340,28367,28303,28354,28319,28514,28486,28487,28452,28437,28409,28463,28470,28491,28532,28458,28425,28457,28553,28557,28556,28536,28530,28540,28538,28625,37642,37643,37644,37645,37646,37647,37648,37649,37650,37651,37652,37653,37654,37655,37656,37657,37658,37659,37660,37661,37662,37663,37664,37665,37666,37667,37668,37669,37670,37671,37672,37673,37674,37675,37676,37677,37678,37679,37680,37681,37682,37683,37684,37685,37686,37687,37688,37689,37690,37691,37692,37693,37695,37696,37697,37698,37699,37700,37701,37702,37703,37704,37705,37706,37707,37708,37709,37710,37711,37712,37713,37714,37715,37716,37717,37718,37719,37720,37721,37722,37723,37724,37725,37726,37727,37728,37729,37730,37731,37732,37733,37734,37735,37736,37737,37739,28617,28583,28601,28598,28610,28641,28654,28638,28640,28655,28698,28707,28699,28729,28725,28751,28766,23424,23428,23445,23443,23461,23480,29999,39582,25652,23524,23534,35120,23536,36423,35591,36790,36819,36821,36837,36846,36836,36841,36838,36851,36840,36869,36868,36875,36902,36881,36877,36886,36897,36917,36918,36909,36911,36932,36945,36946,36944,36968,36952,36962,36955,26297,36980,36989,36994,37e3,36995,37003,24400,24407,24406,24408,23611,21675,23632,23641,23409,23651,23654,32700,24362,24361,24365,33396,24380,39739,23662,22913,22915,22925,22953,22954,22947,37740,37741,37742,37743,37744,37745,37746,37747,37748,37749,37750,37751,37752,37753,37754,37755,37756,37757,37758,37759,37760,37761,37762,37763,37764,37765,37766,37767,37768,37769,37770,37771,37772,37773,37774,37776,37777,37778,37779,37780,37781,37782,37783,37784,37785,37786,37787,37788,37789,37790,37791,37792,37793,37794,37795,37796,37797,37798,37799,37800,37801,37802,37803,37804,37805,37806,37807,37808,37809,37810,37811,37812,37813,37814,37815,37816,37817,37818,37819,37820,37821,37822,37823,37824,37825,37826,37827,37828,37829,37830,37831,37832,37833,37835,37836,37837,22935,22986,22955,22942,22948,22994,22962,22959,22999,22974,23045,23046,23005,23048,23011,23e3,23033,23052,23049,23090,23092,23057,23075,23059,23104,23143,23114,23125,23100,23138,23157,33004,23210,23195,23159,23162,23230,23275,23218,23250,23252,23224,23264,23267,23281,23254,23270,23256,23260,23305,23319,23318,23346,23351,23360,23573,23580,23386,23397,23411,23377,23379,23394,39541,39543,39544,39546,39551,39549,39552,39553,39557,39560,39562,39568,39570,39571,39574,39576,39579,39580,39581,39583,39584,39586,39587,39589,39591,32415,32417,32419,32421,32424,32425,37838,37839,37840,37841,37842,37843,37844,37845,37847,37848,37849,37850,37851,37852,37853,37854,37855,37856,37857,37858,37859,37860,37861,37862,37863,37864,37865,37866,37867,37868,37869,37870,37871,37872,37873,37874,37875,37876,37877,37878,37879,37880,37881,37882,37883,37884,37885,37886,37887,37888,37889,37890,37891,37892,37893,37894,37895,37896,37897,37898,37899,37900,37901,37902,37903,37904,37905,37906,37907,37908,37909,37910,37911,37912,37913,37914,37915,37916,37917,37918,37919,37920,37921,37922,37923,37924,37925,37926,37927,37928,37929,37930,37931,37932,37933,37934,32429,32432,32446,32448,32449,32450,32457,32459,32460,32464,32468,32471,32475,32480,32481,32488,32491,32494,32495,32497,32498,32525,32502,32506,32507,32510,32513,32514,32515,32519,32520,32523,32524,32527,32529,32530,32535,32537,32540,32539,32543,32545,32546,32547,32548,32549,32550,32551,32554,32555,32556,32557,32559,32560,32561,32562,32563,32565,24186,30079,24027,30014,37013,29582,29585,29614,29602,29599,29647,29634,29649,29623,29619,29632,29641,29640,29669,29657,39036,29706,29673,29671,29662,29626,29682,29711,29738,29787,29734,29733,29736,29744,29742,29740,37935,37936,37937,37938,37939,37940,37941,37942,37943,37944,37945,37946,37947,37948,37949,37951,37952,37953,37954,37955,37956,37957,37958,37959,37960,37961,37962,37963,37964,37965,37966,37967,37968,37969,37970,37971,37972,37973,37974,37975,37976,37977,37978,37979,37980,37981,37982,37983,37984,37985,37986,37987,37988,37989,37990,37991,37992,37993,37994,37996,37997,37998,37999,38e3,38001,38002,38003,38004,38005,38006,38007,38008,38009,38010,38011,38012,38013,38014,38015,38016,38017,38018,38019,38020,38033,38038,38040,38087,38095,38099,38100,38106,38118,38139,38172,38176,29723,29722,29761,29788,29783,29781,29785,29815,29805,29822,29852,29838,29824,29825,29831,29835,29854,29864,29865,29840,29863,29906,29882,38890,38891,38892,26444,26451,26462,26440,26473,26533,26503,26474,26483,26520,26535,26485,26536,26526,26541,26507,26487,26492,26608,26633,26584,26634,26601,26544,26636,26585,26549,26586,26547,26589,26624,26563,26552,26594,26638,26561,26621,26674,26675,26720,26721,26702,26722,26692,26724,26755,26653,26709,26726,26689,26727,26688,26686,26698,26697,26665,26805,26767,26740,26743,26771,26731,26818,26990,26876,26911,26912,26873,38183,38195,38205,38211,38216,38219,38229,38234,38240,38254,38260,38261,38263,38264,38265,38266,38267,38268,38269,38270,38272,38273,38274,38275,38276,38277,38278,38279,38280,38281,38282,38283,38284,38285,38286,38287,38288,38289,38290,38291,38292,38293,38294,38295,38296,38297,38298,38299,38300,38301,38302,38303,38304,38305,38306,38307,38308,38309,38310,38311,38312,38313,38314,38315,38316,38317,38318,38319,38320,38321,38322,38323,38324,38325,38326,38327,38328,38329,38330,38331,38332,38333,38334,38335,38336,38337,38338,38339,38340,38341,38342,38343,38344,38345,38346,38347,26916,26864,26891,26881,26967,26851,26896,26993,26937,26976,26946,26973,27012,26987,27008,27032,27e3,26932,27084,27015,27016,27086,27017,26982,26979,27001,27035,27047,27067,27051,27053,27092,27057,27073,27082,27103,27029,27104,27021,27135,27183,27117,27159,27160,27237,27122,27204,27198,27296,27216,27227,27189,27278,27257,27197,27176,27224,27260,27281,27280,27305,27287,27307,29495,29522,27521,27522,27527,27524,27538,27539,27533,27546,27547,27553,27562,36715,36717,36721,36722,36723,36725,36726,36728,36727,36729,36730,36732,36734,36737,36738,36740,36743,36747,38348,38349,38350,38351,38352,38353,38354,38355,38356,38357,38358,38359,38360,38361,38362,38363,38364,38365,38366,38367,38368,38369,38370,38371,38372,38373,38374,38375,38380,38399,38407,38419,38424,38427,38430,38432,38435,38436,38437,38438,38439,38440,38441,38443,38444,38445,38447,38448,38455,38456,38457,38458,38462,38465,38467,38474,38478,38479,38481,38482,38483,38486,38487,38488,38489,38490,38492,38493,38494,38496,38499,38501,38502,38507,38509,38510,38511,38512,38513,38515,38520,38521,38522,38523,38524,38525,38526,38527,38528,38529,38530,38531,38532,38535,38537,38538,36749,36750,36751,36760,36762,36558,25099,25111,25115,25119,25122,25121,25125,25124,25132,33255,29935,29940,29951,29967,29969,29971,25908,26094,26095,26096,26122,26137,26482,26115,26133,26112,28805,26359,26141,26164,26161,26166,26165,32774,26207,26196,26177,26191,26198,26209,26199,26231,26244,26252,26279,26269,26302,26331,26332,26342,26345,36146,36147,36150,36155,36157,36160,36165,36166,36168,36169,36167,36173,36181,36185,35271,35274,35275,35276,35278,35279,35280,35281,29294,29343,29277,29286,29295,29310,29311,29316,29323,29325,29327,29330,25352,25394,25520,38540,38542,38545,38546,38547,38549,38550,38554,38555,38557,38558,38559,38560,38561,38562,38563,38564,38565,38566,38568,38569,38570,38571,38572,38573,38574,38575,38577,38578,38580,38581,38583,38584,38586,38587,38591,38594,38595,38600,38602,38603,38608,38609,38611,38612,38614,38615,38616,38617,38618,38619,38620,38621,38622,38623,38625,38626,38627,38628,38629,38630,38631,38635,38636,38637,38638,38640,38641,38642,38644,38645,38648,38650,38651,38652,38653,38655,38658,38659,38661,38666,38667,38668,38672,38673,38674,38676,38677,38679,38680,38681,38682,38683,38685,38687,38688,25663,25816,32772,27626,27635,27645,27637,27641,27653,27655,27654,27661,27669,27672,27673,27674,27681,27689,27684,27690,27698,25909,25941,25963,29261,29266,29270,29232,34402,21014,32927,32924,32915,32956,26378,32957,32945,32939,32941,32948,32951,32999,33e3,33001,33002,32987,32962,32964,32985,32973,32983,26384,32989,33003,33009,33012,33005,33037,33038,33010,33020,26389,33042,35930,33078,33054,33068,33048,33074,33096,33100,33107,33140,33113,33114,33137,33120,33129,33148,33149,33133,33127,22605,23221,33160,33154,33169,28373,33187,33194,33228,26406,33226,33211,38689,38690,38691,38692,38693,38694,38695,38696,38697,38699,38700,38702,38703,38705,38707,38708,38709,38710,38711,38714,38715,38716,38717,38719,38720,38721,38722,38723,38724,38725,38726,38727,38728,38729,38730,38731,38732,38733,38734,38735,38736,38737,38740,38741,38743,38744,38746,38748,38749,38751,38755,38756,38758,38759,38760,38762,38763,38764,38765,38766,38767,38768,38769,38770,38773,38775,38776,38777,38778,38779,38781,38782,38783,38784,38785,38786,38787,38788,38790,38791,38792,38793,38794,38796,38798,38799,38800,38803,38805,38806,38807,38809,38810,38811,38812,38813,33217,33190,27428,27447,27449,27459,27462,27481,39121,39122,39123,39125,39129,39130,27571,24384,27586,35315,26e3,40785,26003,26044,26054,26052,26051,26060,26062,26066,26070,28800,28828,28822,28829,28859,28864,28855,28843,28849,28904,28874,28944,28947,28950,28975,28977,29043,29020,29032,28997,29042,29002,29048,29050,29080,29107,29109,29096,29088,29152,29140,29159,29177,29213,29224,28780,28952,29030,29113,25150,25149,25155,25160,25161,31035,31040,31046,31049,31067,31068,31059,31066,31074,31063,31072,31087,31079,31098,31109,31114,31130,31143,31155,24529,24528,38814,38815,38817,38818,38820,38821,38822,38823,38824,38825,38826,38828,38830,38832,38833,38835,38837,38838,38839,38840,38841,38842,38843,38844,38845,38846,38847,38848,38849,38850,38851,38852,38853,38854,38855,38856,38857,38858,38859,38860,38861,38862,38863,38864,38865,38866,38867,38868,38869,38870,38871,38872,38873,38874,38875,38876,38877,38878,38879,38880,38881,38882,38883,38884,38885,38888,38894,38895,38896,38897,38898,38900,38903,38904,38905,38906,38907,38908,38909,38910,38911,38912,38913,38914,38915,38916,38917,38918,38919,38920,38921,38922,38923,38924,38925,38926,24636,24669,24666,24679,24641,24665,24675,24747,24838,24845,24925,25001,24989,25035,25041,25094,32896,32895,27795,27894,28156,30710,30712,30720,30729,30743,30744,30737,26027,30765,30748,30749,30777,30778,30779,30751,30780,30757,30764,30755,30761,30798,30829,30806,30807,30758,30800,30791,30796,30826,30875,30867,30874,30855,30876,30881,30883,30898,30905,30885,30932,30937,30921,30956,30962,30981,30964,30995,31012,31006,31028,40859,40697,40699,40700,30449,30468,30477,30457,30471,30472,30490,30498,30489,30509,30502,30517,30520,30544,30545,30535,30531,30554,30568,38927,38928,38929,38930,38931,38932,38933,38934,38935,38936,38937,38938,38939,38940,38941,38942,38943,38944,38945,38946,38947,38948,38949,38950,38951,38952,38953,38954,38955,38956,38957,38958,38959,38960,38961,38962,38963,38964,38965,38966,38967,38968,38969,38970,38971,38972,38973,38974,38975,38976,38977,38978,38979,38980,38981,38982,38983,38984,38985,38986,38987,38988,38989,38990,38991,38992,38993,38994,38995,38996,38997,38998,38999,39e3,39001,39002,39003,39004,39005,39006,39007,39008,39009,39010,39011,39012,39013,39014,39015,39016,39017,39018,39019,39020,39021,39022,30562,30565,30591,30605,30589,30592,30604,30609,30623,30624,30640,30645,30653,30010,30016,30030,30027,30024,30043,30066,30073,30083,32600,32609,32607,35400,32616,32628,32625,32633,32641,32638,30413,30437,34866,38021,38022,38023,38027,38026,38028,38029,38031,38032,38036,38039,38037,38042,38043,38044,38051,38052,38059,38058,38061,38060,38063,38064,38066,38068,38070,38071,38072,38073,38074,38076,38077,38079,38084,38088,38089,38090,38091,38092,38093,38094,38096,38097,38098,38101,38102,38103,38105,38104,38107,38110,38111,38112,38114,38116,38117,38119,38120,38122,39023,39024,39025,39026,39027,39028,39051,39054,39058,39061,39065,39075,39080,39081,39082,39083,39084,39085,39086,39087,39088,39089,39090,39091,39092,39093,39094,39095,39096,39097,39098,39099,39100,39101,39102,39103,39104,39105,39106,39107,39108,39109,39110,39111,39112,39113,39114,39115,39116,39117,39119,39120,39124,39126,39127,39131,39132,39133,39136,39137,39138,39139,39140,39141,39142,39145,39146,39147,39148,39149,39150,39151,39152,39153,39154,39155,39156,39157,39158,39159,39160,39161,39162,39163,39164,39165,39166,39167,39168,39169,39170,39171,39172,39173,39174,39175,38121,38123,38126,38127,38131,38132,38133,38135,38137,38140,38141,38143,38147,38146,38150,38151,38153,38154,38157,38158,38159,38162,38163,38164,38165,38166,38168,38171,38173,38174,38175,38178,38186,38187,38185,38188,38193,38194,38196,38198,38199,38200,38204,38206,38207,38210,38197,38212,38213,38214,38217,38220,38222,38223,38226,38227,38228,38230,38231,38232,38233,38235,38238,38239,38237,38241,38242,38244,38245,38246,38247,38248,38249,38250,38251,38252,38255,38257,38258,38259,38202,30695,30700,38601,31189,31213,31203,31211,31238,23879,31235,31234,31262,31252,39176,39177,39178,39179,39180,39182,39183,39185,39186,39187,39188,39189,39190,39191,39192,39193,39194,39195,39196,39197,39198,39199,39200,39201,39202,39203,39204,39205,39206,39207,39208,39209,39210,39211,39212,39213,39215,39216,39217,39218,39219,39220,39221,39222,39223,39224,39225,39226,39227,39228,39229,39230,39231,39232,39233,39234,39235,39236,39237,39238,39239,39240,39241,39242,39243,39244,39245,39246,39247,39248,39249,39250,39251,39254,39255,39256,39257,39258,39259,39260,39261,39262,39263,39264,39265,39266,39268,39270,39283,39288,39289,39291,39294,39298,39299,39305,31289,31287,31313,40655,39333,31344,30344,30350,30355,30361,30372,29918,29920,29996,40480,40482,40488,40489,40490,40491,40492,40498,40497,40502,40504,40503,40505,40506,40510,40513,40514,40516,40518,40519,40520,40521,40523,40524,40526,40529,40533,40535,40538,40539,40540,40542,40547,40550,40551,40552,40553,40554,40555,40556,40561,40557,40563,30098,30100,30102,30112,30109,30124,30115,30131,30132,30136,30148,30129,30128,30147,30146,30166,30157,30179,30184,30182,30180,30187,30183,30211,30193,30204,30207,30224,30208,30213,30220,30231,30218,30245,30232,30229,30233,39308,39310,39322,39323,39324,39325,39326,39327,39328,39329,39330,39331,39332,39334,39335,39337,39338,39339,39340,39341,39342,39343,39344,39345,39346,39347,39348,39349,39350,39351,39352,39353,39354,39355,39356,39357,39358,39359,39360,39361,39362,39363,39364,39365,39366,39367,39368,39369,39370,39371,39372,39373,39374,39375,39376,39377,39378,39379,39380,39381,39382,39383,39384,39385,39386,39387,39388,39389,39390,39391,39392,39393,39394,39395,39396,39397,39398,39399,39400,39401,39402,39403,39404,39405,39406,39407,39408,39409,39410,39411,39412,39413,39414,39415,39416,39417,30235,30268,30242,30240,30272,30253,30256,30271,30261,30275,30270,30259,30285,30302,30292,30300,30294,30315,30319,32714,31462,31352,31353,31360,31366,31368,31381,31398,31392,31404,31400,31405,31411,34916,34921,34930,34941,34943,34946,34978,35014,34999,35004,35017,35042,35022,35043,35045,35057,35098,35068,35048,35070,35056,35105,35097,35091,35099,35082,35124,35115,35126,35137,35174,35195,30091,32997,30386,30388,30684,32786,32788,32790,32796,32800,32802,32805,32806,32807,32809,32808,32817,32779,32821,32835,32838,32845,32850,32873,32881,35203,39032,39040,39043,39418,39419,39420,39421,39422,39423,39424,39425,39426,39427,39428,39429,39430,39431,39432,39433,39434,39435,39436,39437,39438,39439,39440,39441,39442,39443,39444,39445,39446,39447,39448,39449,39450,39451,39452,39453,39454,39455,39456,39457,39458,39459,39460,39461,39462,39463,39464,39465,39466,39467,39468,39469,39470,39471,39472,39473,39474,39475,39476,39477,39478,39479,39480,39481,39482,39483,39484,39485,39486,39487,39488,39489,39490,39491,39492,39493,39494,39495,39496,39497,39498,39499,39500,39501,39502,39503,39504,39505,39506,39507,39508,39509,39510,39511,39512,39513,39049,39052,39053,39055,39060,39066,39067,39070,39071,39073,39074,39077,39078,34381,34388,34412,34414,34431,34426,34428,34427,34472,34445,34443,34476,34461,34471,34467,34474,34451,34473,34486,34500,34485,34510,34480,34490,34481,34479,34505,34511,34484,34537,34545,34546,34541,34547,34512,34579,34526,34548,34527,34520,34513,34563,34567,34552,34568,34570,34573,34569,34595,34619,34590,34597,34606,34586,34622,34632,34612,34609,34601,34615,34623,34690,34594,34685,34686,34683,34656,34672,34636,34670,34699,34643,34659,34684,34660,34649,34661,34707,34735,34728,34770,39514,39515,39516,39517,39518,39519,39520,39521,39522,39523,39524,39525,39526,39527,39528,39529,39530,39531,39538,39555,39561,39565,39566,39572,39573,39577,39590,39593,39594,39595,39596,39597,39598,39599,39602,39603,39604,39605,39609,39611,39613,39614,39615,39619,39620,39622,39623,39624,39625,39626,39629,39630,39631,39632,39634,39636,39637,39638,39639,39641,39642,39643,39644,39645,39646,39648,39650,39651,39652,39653,39655,39656,39657,39658,39660,39662,39664,39665,39666,39667,39668,39669,39670,39671,39672,39674,39676,39677,39678,39679,39680,39681,39682,39684,39685,39686,34758,34696,34693,34733,34711,34691,34731,34789,34732,34741,34739,34763,34771,34749,34769,34752,34762,34779,34794,34784,34798,34838,34835,34814,34826,34843,34849,34873,34876,32566,32578,32580,32581,33296,31482,31485,31496,31491,31492,31509,31498,31531,31503,31559,31544,31530,31513,31534,31537,31520,31525,31524,31539,31550,31518,31576,31578,31557,31605,31564,31581,31584,31598,31611,31586,31602,31601,31632,31654,31655,31672,31660,31645,31656,31621,31658,31644,31650,31659,31668,31697,31681,31692,31709,31706,31717,31718,31722,31756,31742,31740,31759,31766,31755,39687,39689,39690,39691,39692,39693,39694,39696,39697,39698,39700,39701,39702,39703,39704,39705,39706,39707,39708,39709,39710,39712,39713,39714,39716,39717,39718,39719,39720,39721,39722,39723,39724,39725,39726,39728,39729,39731,39732,39733,39734,39735,39736,39737,39738,39741,39742,39743,39744,39750,39754,39755,39756,39758,39760,39762,39763,39765,39766,39767,39768,39769,39770,39771,39772,39773,39774,39775,39776,39777,39778,39779,39780,39781,39782,39783,39784,39785,39786,39787,39788,39789,39790,39791,39792,39793,39794,39795,39796,39797,39798,39799,39800,39801,39802,39803,31775,31786,31782,31800,31809,31808,33278,33281,33282,33284,33260,34884,33313,33314,33315,33325,33327,33320,33323,33336,33339,33331,33332,33342,33348,33353,33355,33359,33370,33375,33384,34942,34949,34952,35032,35039,35166,32669,32671,32679,32687,32688,32690,31868,25929,31889,31901,31900,31902,31906,31922,31932,31933,31937,31943,31948,31949,31944,31941,31959,31976,33390,26280,32703,32718,32725,32741,32737,32742,32745,32750,32755,31992,32119,32166,32174,32327,32411,40632,40628,36211,36228,36244,36241,36273,36199,36205,35911,35913,37194,37200,37198,37199,37220,39804,39805,39806,39807,39808,39809,39810,39811,39812,39813,39814,39815,39816,39817,39818,39819,39820,39821,39822,39823,39824,39825,39826,39827,39828,39829,39830,39831,39832,39833,39834,39835,39836,39837,39838,39839,39840,39841,39842,39843,39844,39845,39846,39847,39848,39849,39850,39851,39852,39853,39854,39855,39856,39857,39858,39859,39860,39861,39862,39863,39864,39865,39866,39867,39868,39869,39870,39871,39872,39873,39874,39875,39876,39877,39878,39879,39880,39881,39882,39883,39884,39885,39886,39887,39888,39889,39890,39891,39892,39893,39894,39895,39896,39897,39898,39899,37218,37217,37232,37225,37231,37245,37246,37234,37236,37241,37260,37253,37264,37261,37265,37282,37283,37290,37293,37294,37295,37301,37300,37306,35925,40574,36280,36331,36357,36441,36457,36277,36287,36284,36282,36292,36310,36311,36314,36318,36302,36303,36315,36294,36332,36343,36344,36323,36345,36347,36324,36361,36349,36372,36381,36383,36396,36398,36387,36399,36410,36416,36409,36405,36413,36401,36425,36417,36418,36433,36434,36426,36464,36470,36476,36463,36468,36485,36495,36500,36496,36508,36510,35960,35970,35978,35973,35992,35988,26011,35286,35294,35290,35292,39900,39901,39902,39903,39904,39905,39906,39907,39908,39909,39910,39911,39912,39913,39914,39915,39916,39917,39918,39919,39920,39921,39922,39923,39924,39925,39926,39927,39928,39929,39930,39931,39932,39933,39934,39935,39936,39937,39938,39939,39940,39941,39942,39943,39944,39945,39946,39947,39948,39949,39950,39951,39952,39953,39954,39955,39956,39957,39958,39959,39960,39961,39962,39963,39964,39965,39966,39967,39968,39969,39970,39971,39972,39973,39974,39975,39976,39977,39978,39979,39980,39981,39982,39983,39984,39985,39986,39987,39988,39989,39990,39991,39992,39993,39994,39995,35301,35307,35311,35390,35622,38739,38633,38643,38639,38662,38657,38664,38671,38670,38698,38701,38704,38718,40832,40835,40837,40838,40839,40840,40841,40842,40844,40702,40715,40717,38585,38588,38589,38606,38610,30655,38624,37518,37550,37576,37694,37738,37834,37775,37950,37995,40063,40066,40069,40070,40071,40072,31267,40075,40078,40080,40081,40082,40084,40085,40090,40091,40094,40095,40096,40097,40098,40099,40101,40102,40103,40104,40105,40107,40109,40110,40112,40113,40114,40115,40116,40117,40118,40119,40122,40123,40124,40125,40132,40133,40134,40135,40138,40139,39996,39997,39998,39999,4e4,40001,40002,40003,40004,40005,40006,40007,40008,40009,40010,40011,40012,40013,40014,40015,40016,40017,40018,40019,40020,40021,40022,40023,40024,40025,40026,40027,40028,40029,40030,40031,40032,40033,40034,40035,40036,40037,40038,40039,40040,40041,40042,40043,40044,40045,40046,40047,40048,40049,40050,40051,40052,40053,40054,40055,40056,40057,40058,40059,40061,40062,40064,40067,40068,40073,40074,40076,40079,40083,40086,40087,40088,40089,40093,40106,40108,40111,40121,40126,40127,40128,40129,40130,40136,40137,40145,40146,40154,40155,40160,40161,40140,40141,40142,40143,40144,40147,40148,40149,40151,40152,40153,40156,40157,40159,40162,38780,38789,38801,38802,38804,38831,38827,38819,38834,38836,39601,39600,39607,40536,39606,39610,39612,39617,39616,39621,39618,39627,39628,39633,39749,39747,39751,39753,39752,39757,39761,39144,39181,39214,39253,39252,39647,39649,39654,39663,39659,39675,39661,39673,39688,39695,39699,39711,39715,40637,40638,32315,40578,40583,40584,40587,40594,37846,40605,40607,40667,40668,40669,40672,40671,40674,40681,40679,40677,40682,40687,40738,40748,40751,40761,40759,40765,40766,40772,40163,40164,40165,40166,40167,40168,40169,40170,40171,40172,40173,40174,40175,40176,40177,40178,40179,40180,40181,40182,40183,40184,40185,40186,40187,40188,40189,40190,40191,40192,40193,40194,40195,40196,40197,40198,40199,40200,40201,40202,40203,40204,40205,40206,40207,40208,40209,40210,40211,40212,40213,40214,40215,40216,40217,40218,40219,40220,40221,40222,40223,40224,40225,40226,40227,40228,40229,40230,40231,40232,40233,40234,40235,40236,40237,40238,40239,40240,40241,40242,40243,40244,40245,40246,40247,40248,40249,40250,40251,40252,40253,40254,40255,40256,40257,40258,57908,57909,57910,57911,57912,57913,57914,57915,57916,57917,57918,57919,57920,57921,57922,57923,57924,57925,57926,57927,57928,57929,57930,57931,57932,57933,57934,57935,57936,57937,57938,57939,57940,57941,57942,57943,57944,57945,57946,57947,57948,57949,57950,57951,57952,57953,57954,57955,57956,57957,57958,57959,57960,57961,57962,57963,57964,57965,57966,57967,57968,57969,57970,57971,57972,57973,57974,57975,57976,57977,57978,57979,57980,57981,57982,57983,57984,57985,57986,57987,57988,57989,57990,57991,57992,57993,57994,57995,57996,57997,57998,57999,58e3,58001,40259,40260,40261,40262,40263,40264,40265,40266,40267,40268,40269,40270,40271,40272,40273,40274,40275,40276,40277,40278,40279,40280,40281,40282,40283,40284,40285,40286,40287,40288,40289,40290,40291,40292,40293,40294,40295,40296,40297,40298,40299,40300,40301,40302,40303,40304,40305,40306,40307,40308,40309,40310,40311,40312,40313,40314,40315,40316,40317,40318,40319,40320,40321,40322,40323,40324,40325,40326,40327,40328,40329,40330,40331,40332,40333,40334,40335,40336,40337,40338,40339,40340,40341,40342,40343,40344,40345,40346,40347,40348,40349,40350,40351,40352,40353,40354,58002,58003,58004,58005,58006,58007,58008,58009,58010,58011,58012,58013,58014,58015,58016,58017,58018,58019,58020,58021,58022,58023,58024,58025,58026,58027,58028,58029,58030,58031,58032,58033,58034,58035,58036,58037,58038,58039,58040,58041,58042,58043,58044,58045,58046,58047,58048,58049,58050,58051,58052,58053,58054,58055,58056,58057,58058,58059,58060,58061,58062,58063,58064,58065,58066,58067,58068,58069,58070,58071,58072,58073,58074,58075,58076,58077,58078,58079,58080,58081,58082,58083,58084,58085,58086,58087,58088,58089,58090,58091,58092,58093,58094,58095,40355,40356,40357,40358,40359,40360,40361,40362,40363,40364,40365,40366,40367,40368,40369,40370,40371,40372,40373,40374,40375,40376,40377,40378,40379,40380,40381,40382,40383,40384,40385,40386,40387,40388,40389,40390,40391,40392,40393,40394,40395,40396,40397,40398,40399,40400,40401,40402,40403,40404,40405,40406,40407,40408,40409,40410,40411,40412,40413,40414,40415,40416,40417,40418,40419,40420,40421,40422,40423,40424,40425,40426,40427,40428,40429,40430,40431,40432,40433,40434,40435,40436,40437,40438,40439,40440,40441,40442,40443,40444,40445,40446,40447,40448,40449,40450,58096,58097,58098,58099,58100,58101,58102,58103,58104,58105,58106,58107,58108,58109,58110,58111,58112,58113,58114,58115,58116,58117,58118,58119,58120,58121,58122,58123,58124,58125,58126,58127,58128,58129,58130,58131,58132,58133,58134,58135,58136,58137,58138,58139,58140,58141,58142,58143,58144,58145,58146,58147,58148,58149,58150,58151,58152,58153,58154,58155,58156,58157,58158,58159,58160,58161,58162,58163,58164,58165,58166,58167,58168,58169,58170,58171,58172,58173,58174,58175,58176,58177,58178,58179,58180,58181,58182,58183,58184,58185,58186,58187,58188,58189,40451,40452,40453,40454,40455,40456,40457,40458,40459,40460,40461,40462,40463,40464,40465,40466,40467,40468,40469,40470,40471,40472,40473,40474,40475,40476,40477,40478,40484,40487,40494,40496,40500,40507,40508,40512,40525,40528,40530,40531,40532,40534,40537,40541,40543,40544,40545,40546,40549,40558,40559,40562,40564,40565,40566,40567,40568,40569,40570,40571,40572,40573,40576,40577,40579,40580,40581,40582,40585,40586,40588,40589,40590,40591,40592,40593,40596,40597,40598,40599,40600,40601,40602,40603,40604,40606,40608,40609,40610,40611,40612,40613,40615,40616,40617,40618,58190,58191,58192,58193,58194,58195,58196,58197,58198,58199,58200,58201,58202,58203,58204,58205,58206,58207,58208,58209,58210,58211,58212,58213,58214,58215,58216,58217,58218,58219,58220,58221,58222,58223,58224,58225,58226,58227,58228,58229,58230,58231,58232,58233,58234,58235,58236,58237,58238,58239,58240,58241,58242,58243,58244,58245,58246,58247,58248,58249,58250,58251,58252,58253,58254,58255,58256,58257,58258,58259,58260,58261,58262,58263,58264,58265,58266,58267,58268,58269,58270,58271,58272,58273,58274,58275,58276,58277,58278,58279,58280,58281,58282,58283,40619,40620,40621,40622,40623,40624,40625,40626,40627,40629,40630,40631,40633,40634,40636,40639,40640,40641,40642,40643,40645,40646,40647,40648,40650,40651,40652,40656,40658,40659,40661,40662,40663,40665,40666,40670,40673,40675,40676,40678,40680,40683,40684,40685,40686,40688,40689,40690,40691,40692,40693,40694,40695,40696,40698,40701,40703,40704,40705,40706,40707,40708,40709,40710,40711,40712,40713,40714,40716,40719,40721,40722,40724,40725,40726,40728,40730,40731,40732,40733,40734,40735,40737,40739,40740,40741,40742,40743,40744,40745,40746,40747,40749,40750,40752,40753,58284,58285,58286,58287,58288,58289,58290,58291,58292,58293,58294,58295,58296,58297,58298,58299,58300,58301,58302,58303,58304,58305,58306,58307,58308,58309,58310,58311,58312,58313,58314,58315,58316,58317,58318,58319,58320,58321,58322,58323,58324,58325,58326,58327,58328,58329,58330,58331,58332,58333,58334,58335,58336,58337,58338,58339,58340,58341,58342,58343,58344,58345,58346,58347,58348,58349,58350,58351,58352,58353,58354,58355,58356,58357,58358,58359,58360,58361,58362,58363,58364,58365,58366,58367,58368,58369,58370,58371,58372,58373,58374,58375,58376,58377,40754,40755,40756,40757,40758,40760,40762,40764,40767,40768,40769,40770,40771,40773,40774,40775,40776,40777,40778,40779,40780,40781,40782,40783,40786,40787,40788,40789,40790,40791,40792,40793,40794,40795,40796,40797,40798,40799,40800,40801,40802,40803,40804,40805,40806,40807,40808,40809,40810,40811,40812,40813,40814,40815,40816,40817,40818,40819,40820,40821,40822,40823,40824,40825,40826,40827,40828,40829,40830,40833,40834,40845,40846,40847,40848,40849,40850,40851,40852,40853,40854,40855,40856,40860,40861,40862,40865,40866,40867,40868,40869,63788,63865,63893,63975,63985,58378,58379,58380,58381,58382,58383,58384,58385,58386,58387,58388,58389,58390,58391,58392,58393,58394,58395,58396,58397,58398,58399,58400,58401,58402,58403,58404,58405,58406,58407,58408,58409,58410,58411,58412,58413,58414,58415,58416,58417,58418,58419,58420,58421,58422,58423,58424,58425,58426,58427,58428,58429,58430,58431,58432,58433,58434,58435,58436,58437,58438,58439,58440,58441,58442,58443,58444,58445,58446,58447,58448,58449,58450,58451,58452,58453,58454,58455,58456,58457,58458,58459,58460,58461,58462,58463,58464,58465,58466,58467,58468,58469,58470,58471,64012,64013,64014,64015,64017,64019,64020,64024,64031,64032,64033,64035,64036,64039,64040,64041,11905,59414,59415,59416,11908,13427,13383,11912,11915,59422,13726,13850,13838,11916,11927,14702,14616,59430,14799,14815,14963,14800,59435,59436,15182,15470,15584,11943,59441,59442,11946,16470,16735,11950,17207,11955,11958,11959,59451,17329,17324,11963,17373,17622,18017,17996,59459,18211,18217,18300,18317,11978,18759,18810,18813,18818,18819,18821,18822,18847,18843,18871,18870,59476,59477,19619,19615,19616,19617,19575,19618,19731,19732,19733,19734,19735,19736,19737,19886,59492,58472,58473,58474,58475,58476,58477,58478,58479,58480,58481,58482,58483,58484,58485,58486,58487,58488,58489,58490,58491,58492,58493,58494,58495,58496,58497,58498,58499,58500,58501,58502,58503,58504,58505,58506,58507,58508,58509,58510,58511,58512,58513,58514,58515,58516,58517,58518,58519,58520,58521,58522,58523,58524,58525,58526,58527,58528,58529,58530,58531,58532,58533,58534,58535,58536,58537,58538,58539,58540,58541,58542,58543,58544,58545,58546,58547,58548,58549,58550,58551,58552,58553,58554,58555,58556,58557,58558,58559,58560,58561,58562,58563,58564,58565],\"gb18030-ranges\":[[0,128],[36,165],[38,169],[45,178],[50,184],[81,216],[89,226],[95,235],[96,238],[100,244],[103,248],[104,251],[105,253],[109,258],[126,276],[133,284],[148,300],[172,325],[175,329],[179,334],[208,364],[306,463],[307,465],[308,467],[309,469],[310,471],[311,473],[312,475],[313,477],[341,506],[428,594],[443,610],[544,712],[545,716],[558,730],[741,930],[742,938],[749,962],[750,970],[805,1026],[819,1104],[820,1106],[7922,8209],[7924,8215],[7925,8218],[7927,8222],[7934,8231],[7943,8241],[7944,8244],[7945,8246],[7950,8252],[8062,8365],[8148,8452],[8149,8454],[8152,8458],[8164,8471],[8174,8482],[8236,8556],[8240,8570],[8262,8596],[8264,8602],[8374,8713],[8380,8720],[8381,8722],[8384,8726],[8388,8731],[8390,8737],[8392,8740],[8393,8742],[8394,8748],[8396,8751],[8401,8760],[8406,8766],[8416,8777],[8419,8781],[8424,8787],[8437,8802],[8439,8808],[8445,8816],[8482,8854],[8485,8858],[8496,8870],[8521,8896],[8603,8979],[8936,9322],[8946,9372],[9046,9548],[9050,9588],[9063,9616],[9066,9622],[9076,9634],[9092,9652],[9100,9662],[9108,9672],[9111,9676],[9113,9680],[9131,9702],[9162,9735],[9164,9738],[9218,9793],[9219,9795],[11329,11906],[11331,11909],[11334,11913],[11336,11917],[11346,11928],[11361,11944],[11363,11947],[11366,11951],[11370,11956],[11372,11960],[11375,11964],[11389,11979],[11682,12284],[11686,12292],[11687,12312],[11692,12319],[11694,12330],[11714,12351],[11716,12436],[11723,12447],[11725,12535],[11730,12543],[11736,12586],[11982,12842],[11989,12850],[12102,12964],[12336,13200],[12348,13215],[12350,13218],[12384,13253],[12393,13263],[12395,13267],[12397,13270],[12510,13384],[12553,13428],[12851,13727],[12962,13839],[12973,13851],[13738,14617],[13823,14703],[13919,14801],[13933,14816],[14080,14964],[14298,15183],[14585,15471],[14698,15585],[15583,16471],[15847,16736],[16318,17208],[16434,17325],[16438,17330],[16481,17374],[16729,17623],[17102,17997],[17122,18018],[17315,18212],[17320,18218],[17402,18301],[17418,18318],[17859,18760],[17909,18811],[17911,18814],[17915,18820],[17916,18823],[17936,18844],[17939,18848],[17961,18872],[18664,19576],[18703,19620],[18814,19738],[18962,19887],[19043,40870],[33469,59244],[33470,59336],[33471,59367],[33484,59413],[33485,59417],[33490,59423],[33497,59431],[33501,59437],[33505,59443],[33513,59452],[33520,59460],[33536,59478],[33550,59493],[37845,63789],[37921,63866],[37948,63894],[38029,63976],[38038,63986],[38064,64016],[38065,64018],[38066,64021],[38069,64025],[38075,64034],[38076,64037],[38078,64042],[39108,65074],[39109,65093],[39113,65107],[39114,65112],[39115,65127],[39116,65132],[39265,65375],[39394,65510],[189e3,65536]],jis0208:[12288,12289,12290,65292,65294,12539,65306,65307,65311,65281,12443,12444,180,65344,168,65342,65507,65343,12541,12542,12445,12446,12291,20189,12293,12294,12295,12540,8213,8208,65295,65340,65374,8741,65372,8230,8229,8216,8217,8220,8221,65288,65289,12308,12309,65339,65341,65371,65373,12296,12297,12298,12299,12300,12301,12302,12303,12304,12305,65291,65293,177,215,247,65309,8800,65308,65310,8806,8807,8734,8756,9794,9792,176,8242,8243,8451,65509,65284,65504,65505,65285,65283,65286,65290,65312,167,9734,9733,9675,9679,9678,9671,9670,9633,9632,9651,9650,9661,9660,8251,12306,8594,8592,8593,8595,12307,null,null,null,null,null,null,null,null,null,null,null,8712,8715,8838,8839,8834,8835,8746,8745,null,null,null,null,null,null,null,null,8743,8744,65506,8658,8660,8704,8707,null,null,null,null,null,null,null,null,null,null,null,8736,8869,8978,8706,8711,8801,8786,8810,8811,8730,8765,8733,8757,8747,8748,null,null,null,null,null,null,null,8491,8240,9839,9837,9834,8224,8225,182,null,null,null,null,9711,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,65296,65297,65298,65299,65300,65301,65302,65303,65304,65305,null,null,null,null,null,null,null,65313,65314,65315,65316,65317,65318,65319,65320,65321,65322,65323,65324,65325,65326,65327,65328,65329,65330,65331,65332,65333,65334,65335,65336,65337,65338,null,null,null,null,null,null,65345,65346,65347,65348,65349,65350,65351,65352,65353,65354,65355,65356,65357,65358,65359,65360,65361,65362,65363,65364,65365,65366,65367,65368,65369,65370,null,null,null,null,12353,12354,12355,12356,12357,12358,12359,12360,12361,12362,12363,12364,12365,12366,12367,12368,12369,12370,12371,12372,12373,12374,12375,12376,12377,12378,12379,12380,12381,12382,12383,12384,12385,12386,12387,12388,12389,12390,12391,12392,12393,12394,12395,12396,12397,12398,12399,12400,12401,12402,12403,12404,12405,12406,12407,12408,12409,12410,12411,12412,12413,12414,12415,12416,12417,12418,12419,12420,12421,12422,12423,12424,12425,12426,12427,12428,12429,12430,12431,12432,12433,12434,12435,null,null,null,null,null,null,null,null,null,null,null,12449,12450,12451,12452,12453,12454,12455,12456,12457,12458,12459,12460,12461,12462,12463,12464,12465,12466,12467,12468,12469,12470,12471,12472,12473,12474,12475,12476,12477,12478,12479,12480,12481,12482,12483,12484,12485,12486,12487,12488,12489,12490,12491,12492,12493,12494,12495,12496,12497,12498,12499,12500,12501,12502,12503,12504,12505,12506,12507,12508,12509,12510,12511,12512,12513,12514,12515,12516,12517,12518,12519,12520,12521,12522,12523,12524,12525,12526,12527,12528,12529,12530,12531,12532,12533,12534,null,null,null,null,null,null,null,null,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,931,932,933,934,935,936,937,null,null,null,null,null,null,null,null,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,963,964,965,966,967,968,969,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,1040,1041,1042,1043,1044,1045,1025,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,1072,1073,1074,1075,1076,1077,1105,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,null,null,null,null,null,null,null,null,null,null,null,null,null,9472,9474,9484,9488,9496,9492,9500,9516,9508,9524,9532,9473,9475,9487,9491,9499,9495,9507,9523,9515,9531,9547,9504,9519,9512,9527,9535,9501,9520,9509,9528,9538,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,9312,9313,9314,9315,9316,9317,9318,9319,9320,9321,9322,9323,9324,9325,9326,9327,9328,9329,9330,9331,8544,8545,8546,8547,8548,8549,8550,8551,8552,8553,null,13129,13076,13090,13133,13080,13095,13059,13110,13137,13143,13069,13094,13091,13099,13130,13115,13212,13213,13214,13198,13199,13252,13217,null,null,null,null,null,null,null,null,13179,12317,12319,8470,13261,8481,12964,12965,12966,12967,12968,12849,12850,12857,13182,13181,13180,8786,8801,8747,8750,8721,8730,8869,8736,8735,8895,8757,8745,8746,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,20124,21782,23043,38463,21696,24859,25384,23030,36898,33909,33564,31312,24746,25569,28197,26093,33894,33446,39925,26771,22311,26017,25201,23451,22992,34427,39156,32098,32190,39822,25110,31903,34999,23433,24245,25353,26263,26696,38343,38797,26447,20197,20234,20301,20381,20553,22258,22839,22996,23041,23561,24799,24847,24944,26131,26885,28858,30031,30064,31227,32173,32239,32963,33806,34915,35586,36949,36986,21307,20117,20133,22495,32946,37057,30959,19968,22769,28322,36920,31282,33576,33419,39983,20801,21360,21693,21729,22240,23035,24341,39154,28139,32996,34093,38498,38512,38560,38907,21515,21491,23431,28879,32701,36802,38632,21359,40284,31418,19985,30867,33276,28198,22040,21764,27421,34074,39995,23013,21417,28006,29916,38287,22082,20113,36939,38642,33615,39180,21473,21942,23344,24433,26144,26355,26628,27704,27891,27945,29787,30408,31310,38964,33521,34907,35424,37613,28082,30123,30410,39365,24742,35585,36234,38322,27022,21421,20870,22290,22576,22852,23476,24310,24616,25513,25588,27839,28436,28814,28948,29017,29141,29503,32257,33398,33489,34199,36960,37467,40219,22633,26044,27738,29989,20985,22830,22885,24448,24540,25276,26106,27178,27431,27572,29579,32705,35158,40236,40206,40644,23713,27798,33659,20740,23627,25014,33222,26742,29281,20057,20474,21368,24681,28201,31311,38899,19979,21270,20206,20309,20285,20385,20339,21152,21487,22025,22799,23233,23478,23521,31185,26247,26524,26550,27468,27827,28779,29634,31117,31166,31292,31623,33457,33499,33540,33655,33775,33747,34662,35506,22057,36008,36838,36942,38686,34442,20420,23784,25105,29273,30011,33253,33469,34558,36032,38597,39187,39381,20171,20250,35299,22238,22602,22730,24315,24555,24618,24724,24674,25040,25106,25296,25913,39745,26214,26800,28023,28784,30028,30342,32117,33445,34809,38283,38542,35997,20977,21182,22806,21683,23475,23830,24936,27010,28079,30861,33995,34903,35442,37799,39608,28012,39336,34521,22435,26623,34510,37390,21123,22151,21508,24275,25313,25785,26684,26680,27579,29554,30906,31339,35226,35282,36203,36611,37101,38307,38548,38761,23398,23731,27005,38989,38990,25499,31520,27179,27263,26806,39949,28511,21106,21917,24688,25324,27963,28167,28369,33883,35088,36676,19988,39993,21494,26907,27194,38788,26666,20828,31427,33970,37340,37772,22107,40232,26658,33541,33841,31909,21e3,33477,29926,20094,20355,20896,23506,21002,21208,21223,24059,21914,22570,23014,23436,23448,23515,24178,24185,24739,24863,24931,25022,25563,25954,26577,26707,26874,27454,27475,27735,28450,28567,28485,29872,29976,30435,30475,31487,31649,31777,32233,32566,32752,32925,33382,33694,35251,35532,36011,36996,37969,38291,38289,38306,38501,38867,39208,33304,20024,21547,23736,24012,29609,30284,30524,23721,32747,36107,38593,38929,38996,39e3,20225,20238,21361,21916,22120,22522,22855,23305,23492,23696,24076,24190,24524,25582,26426,26071,26082,26399,26827,26820,27231,24112,27589,27671,27773,30079,31048,23395,31232,32e3,24509,35215,35352,36020,36215,36556,36637,39138,39438,39740,20096,20605,20736,22931,23452,25135,25216,25836,27450,29344,30097,31047,32681,34811,35516,35696,25516,33738,38816,21513,21507,21931,26708,27224,35440,30759,26485,40653,21364,23458,33050,34384,36870,19992,20037,20167,20241,21450,21560,23470,24339,24613,25937,26429,27714,27762,27875,28792,29699,31350,31406,31496,32026,31998,32102,26087,29275,21435,23621,24040,25298,25312,25369,28192,34394,35377,36317,37624,28417,31142,39770,20136,20139,20140,20379,20384,20689,20807,31478,20849,20982,21332,21281,21375,21483,21932,22659,23777,24375,24394,24623,24656,24685,25375,25945,27211,27841,29378,29421,30703,33016,33029,33288,34126,37111,37857,38911,39255,39514,20208,20957,23597,26241,26989,23616,26354,26997,29577,26704,31873,20677,21220,22343,24062,37670,26020,27427,27453,29748,31105,31165,31563,32202,33465,33740,34943,35167,35641,36817,37329,21535,37504,20061,20534,21477,21306,29399,29590,30697,33510,36527,39366,39368,39378,20855,24858,34398,21936,31354,20598,23507,36935,38533,20018,27355,37351,23633,23624,25496,31391,27795,38772,36705,31402,29066,38536,31874,26647,32368,26705,37740,21234,21531,34219,35347,32676,36557,37089,21350,34952,31041,20418,20670,21009,20804,21843,22317,29674,22411,22865,24418,24452,24693,24950,24935,25001,25522,25658,25964,26223,26690,28179,30054,31293,31995,32076,32153,32331,32619,33550,33610,34509,35336,35427,35686,36605,38938,40335,33464,36814,39912,21127,25119,25731,28608,38553,26689,20625,27424,27770,28500,31348,32080,34880,35363,26376,20214,20537,20518,20581,20860,21048,21091,21927,22287,22533,23244,24314,25010,25080,25331,25458,26908,27177,29309,29356,29486,30740,30831,32121,30476,32937,35211,35609,36066,36562,36963,37749,38522,38997,39443,40568,20803,21407,21427,24187,24358,28187,28304,29572,29694,32067,33335,35328,35578,38480,20046,20491,21476,21628,22266,22993,23396,24049,24235,24359,25144,25925,26543,28246,29392,31946,34996,32929,32993,33776,34382,35463,36328,37431,38599,39015,40723,20116,20114,20237,21320,21577,21566,23087,24460,24481,24735,26791,27278,29786,30849,35486,35492,35703,37264,20062,39881,20132,20348,20399,20505,20502,20809,20844,21151,21177,21246,21402,21475,21521,21518,21897,22353,22434,22909,23380,23389,23439,24037,24039,24055,24184,24195,24218,24247,24344,24658,24908,25239,25304,25511,25915,26114,26179,26356,26477,26657,26775,27083,27743,27946,28009,28207,28317,30002,30343,30828,31295,31968,32005,32024,32094,32177,32789,32771,32943,32945,33108,33167,33322,33618,34892,34913,35611,36002,36092,37066,37237,37489,30783,37628,38308,38477,38917,39321,39640,40251,21083,21163,21495,21512,22741,25335,28640,35946,36703,40633,20811,21051,21578,22269,31296,37239,40288,40658,29508,28425,33136,29969,24573,24794,39592,29403,36796,27492,38915,20170,22256,22372,22718,23130,24680,25031,26127,26118,26681,26801,28151,30165,32058,33390,39746,20123,20304,21449,21766,23919,24038,24046,26619,27801,29811,30722,35408,37782,35039,22352,24231,25387,20661,20652,20877,26368,21705,22622,22971,23472,24425,25165,25505,26685,27507,28168,28797,37319,29312,30741,30758,31085,25998,32048,33756,35009,36617,38555,21092,22312,26448,32618,36001,20916,22338,38442,22586,27018,32948,21682,23822,22524,30869,40442,20316,21066,21643,25662,26152,26388,26613,31364,31574,32034,37679,26716,39853,31545,21273,20874,21047,23519,25334,25774,25830,26413,27578,34217,38609,30352,39894,25420,37638,39851,30399,26194,19977,20632,21442,23665,24808,25746,25955,26719,29158,29642,29987,31639,32386,34453,35715,36059,37240,39184,26028,26283,27531,20181,20180,20282,20351,21050,21496,21490,21987,22235,22763,22987,22985,23039,23376,23629,24066,24107,24535,24605,25351,25903,23388,26031,26045,26088,26525,27490,27515,27663,29509,31049,31169,31992,32025,32043,32930,33026,33267,35222,35422,35433,35430,35468,35566,36039,36060,38604,39164,27503,20107,20284,20365,20816,23383,23546,24904,25345,26178,27425,28363,27835,29246,29885,30164,30913,31034,32780,32819,33258,33940,36766,27728,40575,24335,35672,40235,31482,36600,23437,38635,19971,21489,22519,22833,23241,23460,24713,28287,28422,30142,36074,23455,34048,31712,20594,26612,33437,23649,34122,32286,33294,20889,23556,25448,36198,26012,29038,31038,32023,32773,35613,36554,36974,34503,37034,20511,21242,23610,26451,28796,29237,37196,37320,37675,33509,23490,24369,24825,20027,21462,23432,25163,26417,27530,29417,29664,31278,33131,36259,37202,39318,20754,21463,21610,23551,25480,27193,32172,38656,22234,21454,21608,23447,23601,24030,20462,24833,25342,27954,31168,31179,32066,32333,32722,33261,33311,33936,34886,35186,35728,36468,36655,36913,37195,37228,38598,37276,20160,20303,20805,21313,24467,25102,26580,27713,28171,29539,32294,37325,37507,21460,22809,23487,28113,31069,32302,31899,22654,29087,20986,34899,36848,20426,23803,26149,30636,31459,33308,39423,20934,24490,26092,26991,27529,28147,28310,28516,30462,32020,24033,36981,37255,38918,20966,21021,25152,26257,26329,28186,24246,32210,32626,26360,34223,34295,35576,21161,21465,22899,24207,24464,24661,37604,38500,20663,20767,21213,21280,21319,21484,21736,21830,21809,22039,22888,22974,23100,23477,23558,23567,23569,23578,24196,24202,24288,24432,25215,25220,25307,25484,25463,26119,26124,26157,26230,26494,26786,27167,27189,27836,28040,28169,28248,28988,28966,29031,30151,30465,30813,30977,31077,31216,31456,31505,31911,32057,32918,33750,33931,34121,34909,35059,35359,35388,35412,35443,35937,36062,37284,37478,37758,37912,38556,38808,19978,19976,19998,20055,20887,21104,22478,22580,22732,23330,24120,24773,25854,26465,26454,27972,29366,30067,31331,33976,35698,37304,37664,22065,22516,39166,25325,26893,27542,29165,32340,32887,33394,35302,39135,34645,36785,23611,20280,20449,20405,21767,23072,23517,23529,24515,24910,25391,26032,26187,26862,27035,28024,28145,30003,30137,30495,31070,31206,32051,33251,33455,34218,35242,35386,36523,36763,36914,37341,38663,20154,20161,20995,22645,22764,23563,29978,23613,33102,35338,36805,38499,38765,31525,35535,38920,37218,22259,21416,36887,21561,22402,24101,25512,27700,28810,30561,31883,32736,34928,36930,37204,37648,37656,38543,29790,39620,23815,23913,25968,26530,36264,38619,25454,26441,26905,33733,38935,38592,35070,28548,25722,23544,19990,28716,30045,26159,20932,21046,21218,22995,24449,24615,25104,25919,25972,26143,26228,26866,26646,27491,28165,29298,29983,30427,31934,32854,22768,35069,35199,35488,35475,35531,36893,37266,38738,38745,25993,31246,33030,38587,24109,24796,25114,26021,26132,26512,30707,31309,31821,32318,33034,36012,36196,36321,36447,30889,20999,25305,25509,25666,25240,35373,31363,31680,35500,38634,32118,33292,34633,20185,20808,21315,21344,23459,23554,23574,24029,25126,25159,25776,26643,26676,27849,27973,27927,26579,28508,29006,29053,26059,31359,31661,32218,32330,32680,33146,33307,33337,34214,35438,36046,36341,36984,36983,37549,37521,38275,39854,21069,21892,28472,28982,20840,31109,32341,33203,31950,22092,22609,23720,25514,26366,26365,26970,29401,30095,30094,30990,31062,31199,31895,32032,32068,34311,35380,38459,36961,40736,20711,21109,21452,21474,20489,21930,22766,22863,29245,23435,23652,21277,24803,24819,25436,25475,25407,25531,25805,26089,26361,24035,27085,27133,28437,29157,20105,30185,30456,31379,31967,32207,32156,32865,33609,33624,33900,33980,34299,35013,36208,36865,36973,37783,38684,39442,20687,22679,24974,33235,34101,36104,36896,20419,20596,21063,21363,24687,25417,26463,28204,36275,36895,20439,23646,36042,26063,32154,21330,34966,20854,25539,23384,23403,23562,25613,26449,36956,20182,22810,22826,27760,35409,21822,22549,22949,24816,25171,26561,33333,26965,38464,39364,39464,20307,22534,23550,32784,23729,24111,24453,24608,24907,25140,26367,27888,28382,32974,33151,33492,34955,36024,36864,36910,38538,40667,39899,20195,21488,22823,31532,37261,38988,40441,28381,28711,21331,21828,23429,25176,25246,25299,27810,28655,29730,35351,37944,28609,35582,33592,20967,34552,21482,21481,20294,36948,36784,22890,33073,24061,31466,36799,26842,35895,29432,40008,27197,35504,20025,21336,22022,22374,25285,25506,26086,27470,28129,28251,28845,30701,31471,31658,32187,32829,32966,34507,35477,37723,22243,22727,24382,26029,26262,27264,27573,30007,35527,20516,30693,22320,24347,24677,26234,27744,30196,31258,32622,33268,34584,36933,39347,31689,30044,31481,31569,33988,36880,31209,31378,33590,23265,30528,20013,20210,23449,24544,25277,26172,26609,27880,34411,34935,35387,37198,37619,39376,27159,28710,29482,33511,33879,36015,19969,20806,20939,21899,23541,24086,24115,24193,24340,24373,24427,24500,25074,25361,26274,26397,28526,29266,30010,30522,32884,33081,33144,34678,35519,35548,36229,36339,37530,38263,38914,40165,21189,25431,30452,26389,27784,29645,36035,37806,38515,27941,22684,26894,27084,36861,37786,30171,36890,22618,26626,25524,27131,20291,28460,26584,36795,34086,32180,37716,26943,28528,22378,22775,23340,32044,29226,21514,37347,40372,20141,20302,20572,20597,21059,35998,21576,22564,23450,24093,24213,24237,24311,24351,24716,25269,25402,25552,26799,27712,30855,31118,31243,32224,33351,35330,35558,36420,36883,37048,37165,37336,40718,27877,25688,25826,25973,28404,30340,31515,36969,37841,28346,21746,24505,25764,36685,36845,37444,20856,22635,22825,23637,24215,28155,32399,29980,36028,36578,39003,28857,20253,27583,28593,3e4,38651,20814,21520,22581,22615,22956,23648,24466,26007,26460,28193,30331,33759,36077,36884,37117,37709,30757,30778,21162,24230,22303,22900,24594,20498,20826,20908,20941,20992,21776,22612,22616,22871,23445,23798,23947,24764,25237,25645,26481,26691,26812,26847,30423,28120,28271,28059,28783,29128,24403,30168,31095,31561,31572,31570,31958,32113,21040,33891,34153,34276,35342,35588,35910,36367,36867,36879,37913,38518,38957,39472,38360,20685,21205,21516,22530,23566,24999,25758,27934,30643,31461,33012,33796,36947,37509,23776,40199,21311,24471,24499,28060,29305,30563,31167,31716,27602,29420,35501,26627,27233,20984,31361,26932,23626,40182,33515,23493,37193,28702,22136,23663,24775,25958,27788,35930,36929,38931,21585,26311,37389,22856,37027,20869,20045,20970,34201,35598,28760,25466,37707,26978,39348,32260,30071,21335,26976,36575,38627,27741,20108,23612,24336,36841,21250,36049,32905,34425,24319,26085,20083,20837,22914,23615,38894,20219,22922,24525,35469,28641,31152,31074,23527,33905,29483,29105,24180,24565,25467,25754,29123,31896,20035,24316,20043,22492,22178,24745,28611,32013,33021,33075,33215,36786,35223,34468,24052,25226,25773,35207,26487,27874,27966,29750,30772,23110,32629,33453,39340,20467,24259,25309,25490,25943,26479,30403,29260,32972,32954,36649,37197,20493,22521,23186,26757,26995,29028,29437,36023,22770,36064,38506,36889,34687,31204,30695,33833,20271,21093,21338,25293,26575,27850,30333,31636,31893,33334,34180,36843,26333,28448,29190,32283,33707,39361,40614,20989,31665,30834,31672,32903,31560,27368,24161,32908,30033,30048,20843,37474,28300,30330,37271,39658,20240,32624,25244,31567,38309,40169,22138,22617,34532,38588,20276,21028,21322,21453,21467,24070,25644,26001,26495,27710,27726,29256,29359,29677,30036,32321,33324,34281,36009,31684,37318,29033,38930,39151,25405,26217,30058,30436,30928,34115,34542,21290,21329,21542,22915,24199,24444,24754,25161,25209,25259,26e3,27604,27852,30130,30382,30865,31192,32203,32631,32933,34987,35513,36027,36991,38750,39131,27147,31800,20633,23614,24494,26503,27608,29749,30473,32654,40763,26570,31255,21305,30091,39661,24422,33181,33777,32920,24380,24517,30050,31558,36924,26727,23019,23195,32016,30334,35628,20469,24426,27161,27703,28418,29922,31080,34920,35413,35961,24287,25551,30149,31186,33495,37672,37618,33948,34541,39981,21697,24428,25996,27996,28693,36007,36051,38971,25935,29942,19981,20184,22496,22827,23142,23500,20904,24067,24220,24598,25206,25975,26023,26222,28014,29238,31526,33104,33178,33433,35676,36e3,36070,36212,38428,38468,20398,25771,27494,33310,33889,34154,37096,23553,26963,39080,33914,34135,20239,21103,24489,24133,26381,31119,33145,35079,35206,28149,24343,25173,27832,20175,29289,39826,20998,21563,22132,22707,24996,25198,28954,22894,31881,31966,32027,38640,25991,32862,19993,20341,20853,22592,24163,24179,24330,26564,20006,34109,38281,38491,31859,38913,20731,22721,30294,30887,21029,30629,34065,31622,20559,22793,29255,31687,32232,36794,36820,36941,20415,21193,23081,24321,38829,20445,33303,37610,22275,25429,27497,29995,35036,36628,31298,21215,22675,24917,25098,26286,27597,31807,33769,20515,20472,21253,21574,22577,22857,23453,23792,23791,23849,24214,25265,25447,25918,26041,26379,27861,27873,28921,30770,32299,32990,33459,33804,34028,34562,35090,35370,35914,37030,37586,39165,40179,40300,20047,20129,20621,21078,22346,22952,24125,24536,24537,25151,26292,26395,26576,26834,20882,32033,32938,33192,35584,35980,36031,37502,38450,21536,38956,21271,20693,21340,22696,25778,26420,29287,30566,31302,37350,21187,27809,27526,22528,24140,22868,26412,32763,20961,30406,25705,30952,39764,40635,22475,22969,26151,26522,27598,21737,27097,24149,33180,26517,39850,26622,40018,26717,20134,20451,21448,25273,26411,27819,36804,20397,32365,40639,19975,24930,28288,28459,34067,21619,26410,39749,24051,31637,23724,23494,34588,28234,34001,31252,33032,22937,31885,27665,30496,21209,22818,28961,29279,30683,38695,40289,26891,23167,23064,20901,21517,21629,26126,30431,36855,37528,40180,23018,29277,28357,20813,26825,32191,32236,38754,40634,25720,27169,33538,22916,23391,27611,29467,30450,32178,32791,33945,20786,26408,40665,30446,26466,21247,39173,23588,25147,31870,36016,21839,24758,32011,38272,21249,20063,20918,22812,29242,32822,37326,24357,30690,21380,24441,32004,34220,35379,36493,38742,26611,34222,37971,24841,24840,27833,30290,35565,36664,21807,20305,20778,21191,21451,23461,24189,24736,24962,25558,26377,26586,28263,28044,29494,29495,30001,31056,35029,35480,36938,37009,37109,38596,34701,22805,20104,20313,19982,35465,36671,38928,20653,24188,22934,23481,24248,25562,25594,25793,26332,26954,27096,27915,28342,29076,29992,31407,32650,32768,33865,33993,35201,35617,36362,36965,38525,39178,24958,25233,27442,27779,28020,32716,32764,28096,32645,34746,35064,26469,33713,38972,38647,27931,32097,33853,37226,20081,21365,23888,27396,28651,34253,34349,35239,21033,21519,23653,26446,26792,29702,29827,30178,35023,35041,37324,38626,38520,24459,29575,31435,33870,25504,30053,21129,27969,28316,29705,30041,30827,31890,38534,31452,40845,20406,24942,26053,34396,20102,20142,20698,20001,20940,23534,26009,26753,28092,29471,30274,30637,31260,31975,33391,35538,36988,37327,38517,38936,21147,32209,20523,21400,26519,28107,29136,29747,33256,36650,38563,40023,40607,29792,22593,28057,32047,39006,20196,20278,20363,20919,21169,23994,24604,29618,31036,33491,37428,38583,38646,38666,40599,40802,26278,27508,21015,21155,28872,35010,24265,24651,24976,28451,29001,31806,32244,32879,34030,36899,37676,21570,39791,27347,28809,36034,36335,38706,21172,23105,24266,24324,26391,27004,27028,28010,28431,29282,29436,31725,32769,32894,34635,37070,20845,40595,31108,32907,37682,35542,20525,21644,35441,27498,36036,33031,24785,26528,40434,20121,20120,39952,35435,34241,34152,26880,28286,30871,33109,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,24332,19984,19989,20010,20017,20022,20028,20031,20034,20054,20056,20098,20101,35947,20106,33298,24333,20110,20126,20127,20128,20130,20144,20147,20150,20174,20173,20164,20166,20162,20183,20190,20205,20191,20215,20233,20314,20272,20315,20317,20311,20295,20342,20360,20367,20376,20347,20329,20336,20369,20335,20358,20374,20760,20436,20447,20430,20440,20443,20433,20442,20432,20452,20453,20506,20520,20500,20522,20517,20485,20252,20470,20513,20521,20524,20478,20463,20497,20486,20547,20551,26371,20565,20560,20552,20570,20566,20588,20600,20608,20634,20613,20660,20658,20681,20682,20659,20674,20694,20702,20709,20717,20707,20718,20729,20725,20745,20737,20738,20758,20757,20756,20762,20769,20794,20791,20796,20795,20799,20800,20818,20812,20820,20834,31480,20841,20842,20846,20864,20866,22232,20876,20873,20879,20881,20883,20885,20886,20900,20902,20898,20905,20906,20907,20915,20913,20914,20912,20917,20925,20933,20937,20955,20960,34389,20969,20973,20976,20981,20990,20996,21003,21012,21006,21031,21034,21038,21043,21049,21071,21060,21067,21068,21086,21076,21098,21108,21097,21107,21119,21117,21133,21140,21138,21105,21128,21137,36776,36775,21164,21165,21180,21173,21185,21197,21207,21214,21219,21222,39149,21216,21235,21237,21240,21241,21254,21256,30008,21261,21264,21263,21269,21274,21283,21295,21297,21299,21304,21312,21318,21317,19991,21321,21325,20950,21342,21353,21358,22808,21371,21367,21378,21398,21408,21414,21413,21422,21424,21430,21443,31762,38617,21471,26364,29166,21486,21480,21485,21498,21505,21565,21568,21548,21549,21564,21550,21558,21545,21533,21582,21647,21621,21646,21599,21617,21623,21616,21650,21627,21632,21622,21636,21648,21638,21703,21666,21688,21669,21676,21700,21704,21672,21675,21698,21668,21694,21692,21720,21733,21734,21775,21780,21757,21742,21741,21754,21730,21817,21824,21859,21836,21806,21852,21829,21846,21847,21816,21811,21853,21913,21888,21679,21898,21919,21883,21886,21912,21918,21934,21884,21891,21929,21895,21928,21978,21957,21983,21956,21980,21988,21972,22036,22007,22038,22014,22013,22043,22009,22094,22096,29151,22068,22070,22066,22072,22123,22116,22063,22124,22122,22150,22144,22154,22176,22164,22159,22181,22190,22198,22196,22210,22204,22209,22211,22208,22216,22222,22225,22227,22231,22254,22265,22272,22271,22276,22281,22280,22283,22285,22291,22296,22294,21959,22300,22310,22327,22328,22350,22331,22336,22351,22377,22464,22408,22369,22399,22409,22419,22432,22451,22436,22442,22448,22467,22470,22484,22482,22483,22538,22486,22499,22539,22553,22557,22642,22561,22626,22603,22640,27584,22610,22589,22649,22661,22713,22687,22699,22714,22750,22715,22712,22702,22725,22739,22737,22743,22745,22744,22757,22748,22756,22751,22767,22778,22777,22779,22780,22781,22786,22794,22800,22811,26790,22821,22828,22829,22834,22840,22846,31442,22869,22864,22862,22874,22872,22882,22880,22887,22892,22889,22904,22913,22941,20318,20395,22947,22962,22982,23016,23004,22925,23001,23002,23077,23071,23057,23068,23049,23066,23104,23148,23113,23093,23094,23138,23146,23194,23228,23230,23243,23234,23229,23267,23255,23270,23273,23254,23290,23291,23308,23307,23318,23346,23248,23338,23350,23358,23363,23365,23360,23377,23381,23386,23387,23397,23401,23408,23411,23413,23416,25992,23418,23424,23427,23462,23480,23491,23495,23497,23508,23504,23524,23526,23522,23518,23525,23531,23536,23542,23539,23557,23559,23560,23565,23571,23584,23586,23592,23608,23609,23617,23622,23630,23635,23632,23631,23409,23660,23662,20066,23670,23673,23692,23697,23700,22939,23723,23739,23734,23740,23735,23749,23742,23751,23769,23785,23805,23802,23789,23948,23786,23819,23829,23831,23900,23839,23835,23825,23828,23842,23834,23833,23832,23884,23890,23886,23883,23916,23923,23926,23943,23940,23938,23970,23965,23980,23982,23997,23952,23991,23996,24009,24013,24019,24018,24022,24027,24043,24050,24053,24075,24090,24089,24081,24091,24118,24119,24132,24131,24128,24142,24151,24148,24159,24162,24164,24135,24181,24182,24186,40636,24191,24224,24257,24258,24264,24272,24271,24278,24291,24285,24282,24283,24290,24289,24296,24297,24300,24305,24307,24304,24308,24312,24318,24323,24329,24413,24412,24331,24337,24342,24361,24365,24376,24385,24392,24396,24398,24367,24401,24406,24407,24409,24417,24429,24435,24439,24451,24450,24447,24458,24456,24465,24455,24478,24473,24472,24480,24488,24493,24508,24534,24571,24548,24568,24561,24541,24755,24575,24609,24672,24601,24592,24617,24590,24625,24603,24597,24619,24614,24591,24634,24666,24641,24682,24695,24671,24650,24646,24653,24675,24643,24676,24642,24684,24683,24665,24705,24717,24807,24707,24730,24708,24731,24726,24727,24722,24743,24715,24801,24760,24800,24787,24756,24560,24765,24774,24757,24792,24909,24853,24838,24822,24823,24832,24820,24826,24835,24865,24827,24817,24845,24846,24903,24894,24872,24871,24906,24895,24892,24876,24884,24893,24898,24900,24947,24951,24920,24921,24922,24939,24948,24943,24933,24945,24927,24925,24915,24949,24985,24982,24967,25004,24980,24986,24970,24977,25003,25006,25036,25034,25033,25079,25032,25027,25030,25018,25035,32633,25037,25062,25059,25078,25082,25076,25087,25085,25084,25086,25088,25096,25097,25101,25100,25108,25115,25118,25121,25130,25134,25136,25138,25139,25153,25166,25182,25187,25179,25184,25192,25212,25218,25225,25214,25234,25235,25238,25300,25219,25236,25303,25297,25275,25295,25343,25286,25812,25288,25308,25292,25290,25282,25287,25243,25289,25356,25326,25329,25383,25346,25352,25327,25333,25424,25406,25421,25628,25423,25494,25486,25472,25515,25462,25507,25487,25481,25503,25525,25451,25449,25534,25577,25536,25542,25571,25545,25554,25590,25540,25622,25652,25606,25619,25638,25654,25885,25623,25640,25615,25703,25711,25718,25678,25898,25749,25747,25765,25769,25736,25788,25818,25810,25797,25799,25787,25816,25794,25841,25831,33289,25824,25825,25260,25827,25839,25900,25846,25844,25842,25850,25856,25853,25880,25884,25861,25892,25891,25899,25908,25909,25911,25910,25912,30027,25928,25942,25941,25933,25944,25950,25949,25970,25976,25986,25987,35722,26011,26015,26027,26039,26051,26054,26049,26052,26060,26066,26075,26073,26080,26081,26097,26482,26122,26115,26107,26483,26165,26166,26164,26140,26191,26180,26185,26177,26206,26205,26212,26215,26216,26207,26210,26224,26243,26248,26254,26249,26244,26264,26269,26305,26297,26313,26302,26300,26308,26296,26326,26330,26336,26175,26342,26345,26352,26357,26359,26383,26390,26398,26406,26407,38712,26414,26431,26422,26433,26424,26423,26438,26462,26464,26457,26467,26468,26505,26480,26537,26492,26474,26508,26507,26534,26529,26501,26551,26607,26548,26604,26547,26601,26552,26596,26590,26589,26594,26606,26553,26574,26566,26599,27292,26654,26694,26665,26688,26701,26674,26702,26803,26667,26713,26723,26743,26751,26783,26767,26797,26772,26781,26779,26755,27310,26809,26740,26805,26784,26810,26895,26765,26750,26881,26826,26888,26840,26914,26918,26849,26892,26829,26836,26855,26837,26934,26898,26884,26839,26851,26917,26873,26848,26863,26920,26922,26906,26915,26913,26822,27001,26999,26972,27e3,26987,26964,27006,26990,26937,26996,26941,26969,26928,26977,26974,26973,27009,26986,27058,27054,27088,27071,27073,27091,27070,27086,23528,27082,27101,27067,27075,27047,27182,27025,27040,27036,27029,27060,27102,27112,27138,27163,27135,27402,27129,27122,27111,27141,27057,27166,27117,27156,27115,27146,27154,27329,27171,27155,27204,27148,27250,27190,27256,27207,27234,27225,27238,27208,27192,27170,27280,27277,27296,27268,27298,27299,27287,34327,27323,27331,27330,27320,27315,27308,27358,27345,27359,27306,27354,27370,27387,27397,34326,27386,27410,27414,39729,27423,27448,27447,30428,27449,39150,27463,27459,27465,27472,27481,27476,27483,27487,27489,27512,27513,27519,27520,27524,27523,27533,27544,27541,27550,27556,27562,27563,27567,27570,27569,27571,27575,27580,27590,27595,27603,27615,27628,27627,27635,27631,40638,27656,27667,27668,27675,27684,27683,27742,27733,27746,27754,27778,27789,27802,27777,27803,27774,27752,27763,27794,27792,27844,27889,27859,27837,27863,27845,27869,27822,27825,27838,27834,27867,27887,27865,27882,27935,34893,27958,27947,27965,27960,27929,27957,27955,27922,27916,28003,28051,28004,27994,28025,27993,28046,28053,28644,28037,28153,28181,28170,28085,28103,28134,28088,28102,28140,28126,28108,28136,28114,28101,28154,28121,28132,28117,28138,28142,28205,28270,28206,28185,28274,28255,28222,28195,28267,28203,28278,28237,28191,28227,28218,28238,28196,28415,28189,28216,28290,28330,28312,28361,28343,28371,28349,28335,28356,28338,28372,28373,28303,28325,28354,28319,28481,28433,28748,28396,28408,28414,28479,28402,28465,28399,28466,28364,28478,28435,28407,28550,28538,28536,28545,28544,28527,28507,28659,28525,28546,28540,28504,28558,28561,28610,28518,28595,28579,28577,28580,28601,28614,28586,28639,28629,28652,28628,28632,28657,28654,28635,28681,28683,28666,28689,28673,28687,28670,28699,28698,28532,28701,28696,28703,28720,28734,28722,28753,28771,28825,28818,28847,28913,28844,28856,28851,28846,28895,28875,28893,28889,28937,28925,28956,28953,29029,29013,29064,29030,29026,29004,29014,29036,29071,29179,29060,29077,29096,29100,29143,29113,29118,29138,29129,29140,29134,29152,29164,29159,29173,29180,29177,29183,29197,29200,29211,29224,29229,29228,29232,29234,29243,29244,29247,29248,29254,29259,29272,29300,29310,29314,29313,29319,29330,29334,29346,29351,29369,29362,29379,29382,29380,29390,29394,29410,29408,29409,29433,29431,20495,29463,29450,29468,29462,29469,29492,29487,29481,29477,29502,29518,29519,40664,29527,29546,29544,29552,29560,29557,29563,29562,29640,29619,29646,29627,29632,29669,29678,29662,29858,29701,29807,29733,29688,29746,29754,29781,29759,29791,29785,29761,29788,29801,29808,29795,29802,29814,29822,29835,29854,29863,29898,29903,29908,29681,29920,29923,29927,29929,29934,29938,29936,29937,29944,29943,29956,29955,29957,29964,29966,29965,29973,29971,29982,29990,29996,30012,30020,30029,30026,30025,30043,30022,30042,30057,30052,30055,30059,30061,30072,30070,30086,30087,30068,30090,30089,30082,30100,30106,30109,30117,30115,30146,30131,30147,30133,30141,30136,30140,30129,30157,30154,30162,30169,30179,30174,30206,30207,30204,30209,30192,30202,30194,30195,30219,30221,30217,30239,30247,30240,30241,30242,30244,30260,30256,30267,30279,30280,30278,30300,30296,30305,30306,30312,30313,30314,30311,30316,30320,30322,30326,30328,30332,30336,30339,30344,30347,30350,30358,30355,30361,30362,30384,30388,30392,30393,30394,30402,30413,30422,30418,30430,30433,30437,30439,30442,34351,30459,30472,30471,30468,30505,30500,30494,30501,30502,30491,30519,30520,30535,30554,30568,30571,30555,30565,30591,30590,30585,30606,30603,30609,30624,30622,30640,30646,30649,30655,30652,30653,30651,30663,30669,30679,30682,30684,30691,30702,30716,30732,30738,31014,30752,31018,30789,30862,30836,30854,30844,30874,30860,30883,30901,30890,30895,30929,30918,30923,30932,30910,30908,30917,30922,30956,30951,30938,30973,30964,30983,30994,30993,31001,31020,31019,31040,31072,31063,31071,31066,31061,31059,31098,31103,31114,31133,31143,40779,31146,31150,31155,31161,31162,31177,31189,31207,31212,31201,31203,31240,31245,31256,31257,31264,31263,31104,31281,31291,31294,31287,31299,31319,31305,31329,31330,31337,40861,31344,31353,31357,31368,31383,31381,31384,31382,31401,31432,31408,31414,31429,31428,31423,36995,31431,31434,31437,31439,31445,31443,31449,31450,31453,31457,31458,31462,31469,31472,31490,31503,31498,31494,31539,31512,31513,31518,31541,31528,31542,31568,31610,31492,31565,31499,31564,31557,31605,31589,31604,31591,31600,31601,31596,31598,31645,31640,31647,31629,31644,31642,31627,31634,31631,31581,31641,31691,31681,31692,31695,31668,31686,31709,31721,31761,31764,31718,31717,31840,31744,31751,31763,31731,31735,31767,31757,31734,31779,31783,31786,31775,31799,31787,31805,31820,31811,31828,31823,31808,31824,31832,31839,31844,31830,31845,31852,31861,31875,31888,31908,31917,31906,31915,31905,31912,31923,31922,31921,31918,31929,31933,31936,31941,31938,31960,31954,31964,31970,39739,31983,31986,31988,31990,31994,32006,32002,32028,32021,32010,32069,32075,32046,32050,32063,32053,32070,32115,32086,32078,32114,32104,32110,32079,32099,32147,32137,32091,32143,32125,32155,32186,32174,32163,32181,32199,32189,32171,32317,32162,32175,32220,32184,32159,32176,32216,32221,32228,32222,32251,32242,32225,32261,32266,32291,32289,32274,32305,32287,32265,32267,32290,32326,32358,32315,32309,32313,32323,32311,32306,32314,32359,32349,32342,32350,32345,32346,32377,32362,32361,32380,32379,32387,32213,32381,36782,32383,32392,32393,32396,32402,32400,32403,32404,32406,32398,32411,32412,32568,32570,32581,32588,32589,32590,32592,32593,32597,32596,32600,32607,32608,32616,32617,32615,32632,32642,32646,32643,32648,32647,32652,32660,32670,32669,32666,32675,32687,32690,32697,32686,32694,32696,35697,32709,32710,32714,32725,32724,32737,32742,32745,32755,32761,39132,32774,32772,32779,32786,32792,32793,32796,32801,32808,32831,32827,32842,32838,32850,32856,32858,32863,32866,32872,32883,32882,32880,32886,32889,32893,32895,32900,32902,32901,32923,32915,32922,32941,20880,32940,32987,32997,32985,32989,32964,32986,32982,33033,33007,33009,33051,33065,33059,33071,33099,38539,33094,33086,33107,33105,33020,33137,33134,33125,33126,33140,33155,33160,33162,33152,33154,33184,33173,33188,33187,33119,33171,33193,33200,33205,33214,33208,33213,33216,33218,33210,33225,33229,33233,33241,33240,33224,33242,33247,33248,33255,33274,33275,33278,33281,33282,33285,33287,33290,33293,33296,33302,33321,33323,33336,33331,33344,33369,33368,33373,33370,33375,33380,33378,33384,33386,33387,33326,33393,33399,33400,33406,33421,33426,33451,33439,33467,33452,33505,33507,33503,33490,33524,33523,33530,33683,33539,33531,33529,33502,33542,33500,33545,33497,33589,33588,33558,33586,33585,33600,33593,33616,33605,33583,33579,33559,33560,33669,33690,33706,33695,33698,33686,33571,33678,33671,33674,33660,33717,33651,33653,33696,33673,33704,33780,33811,33771,33742,33789,33795,33752,33803,33729,33783,33799,33760,33778,33805,33826,33824,33725,33848,34054,33787,33901,33834,33852,34138,33924,33911,33899,33965,33902,33922,33897,33862,33836,33903,33913,33845,33994,33890,33977,33983,33951,34009,33997,33979,34010,34e3,33985,33990,34006,33953,34081,34047,34036,34071,34072,34092,34079,34069,34068,34044,34112,34147,34136,34120,34113,34306,34123,34133,34176,34212,34184,34193,34186,34216,34157,34196,34203,34282,34183,34204,34167,34174,34192,34249,34234,34255,34233,34256,34261,34269,34277,34268,34297,34314,34323,34315,34302,34298,34310,34338,34330,34352,34367,34381,20053,34388,34399,34407,34417,34451,34467,34473,34474,34443,34444,34486,34479,34500,34502,34480,34505,34851,34475,34516,34526,34537,34540,34527,34523,34543,34578,34566,34568,34560,34563,34555,34577,34569,34573,34553,34570,34612,34623,34615,34619,34597,34601,34586,34656,34655,34680,34636,34638,34676,34647,34664,34670,34649,34643,34659,34666,34821,34722,34719,34690,34735,34763,34749,34752,34768,38614,34731,34756,34739,34759,34758,34747,34799,34802,34784,34831,34829,34814,34806,34807,34830,34770,34833,34838,34837,34850,34849,34865,34870,34873,34855,34875,34884,34882,34898,34905,34910,34914,34923,34945,34942,34974,34933,34941,34997,34930,34946,34967,34962,34990,34969,34978,34957,34980,34992,35007,34993,35011,35012,35028,35032,35033,35037,35065,35074,35068,35060,35048,35058,35076,35084,35082,35091,35139,35102,35109,35114,35115,35137,35140,35131,35126,35128,35148,35101,35168,35166,35174,35172,35181,35178,35183,35188,35191,35198,35203,35208,35210,35219,35224,35233,35241,35238,35244,35247,35250,35258,35261,35263,35264,35290,35292,35293,35303,35316,35320,35331,35350,35344,35340,35355,35357,35365,35382,35393,35419,35410,35398,35400,35452,35437,35436,35426,35461,35458,35460,35496,35489,35473,35493,35494,35482,35491,35524,35533,35522,35546,35563,35571,35559,35556,35569,35604,35552,35554,35575,35550,35547,35596,35591,35610,35553,35606,35600,35607,35616,35635,38827,35622,35627,35646,35624,35649,35660,35663,35662,35657,35670,35675,35674,35691,35679,35692,35695,35700,35709,35712,35724,35726,35730,35731,35734,35737,35738,35898,35905,35903,35912,35916,35918,35920,35925,35938,35948,35960,35962,35970,35977,35973,35978,35981,35982,35988,35964,35992,25117,36013,36010,36029,36018,36019,36014,36022,36040,36033,36068,36067,36058,36093,36090,36091,36100,36101,36106,36103,36111,36109,36112,40782,36115,36045,36116,36118,36199,36205,36209,36211,36225,36249,36290,36286,36282,36303,36314,36310,36300,36315,36299,36330,36331,36319,36323,36348,36360,36361,36351,36381,36382,36368,36383,36418,36405,36400,36404,36426,36423,36425,36428,36432,36424,36441,36452,36448,36394,36451,36437,36470,36466,36476,36481,36487,36485,36484,36491,36490,36499,36497,36500,36505,36522,36513,36524,36528,36550,36529,36542,36549,36552,36555,36571,36579,36604,36603,36587,36606,36618,36613,36629,36626,36633,36627,36636,36639,36635,36620,36646,36659,36667,36665,36677,36674,36670,36684,36681,36678,36686,36695,36700,36706,36707,36708,36764,36767,36771,36781,36783,36791,36826,36837,36834,36842,36847,36999,36852,36869,36857,36858,36881,36885,36897,36877,36894,36886,36875,36903,36918,36917,36921,36856,36943,36944,36945,36946,36878,36937,36926,36950,36952,36958,36968,36975,36982,38568,36978,36994,36989,36993,36992,37002,37001,37007,37032,37039,37041,37045,37090,37092,25160,37083,37122,37138,37145,37170,37168,37194,37206,37208,37219,37221,37225,37235,37234,37259,37257,37250,37282,37291,37295,37290,37301,37300,37306,37312,37313,37321,37323,37328,37334,37343,37345,37339,37372,37365,37366,37406,37375,37396,37420,37397,37393,37470,37463,37445,37449,37476,37448,37525,37439,37451,37456,37532,37526,37523,37531,37466,37583,37561,37559,37609,37647,37626,37700,37678,37657,37666,37658,37667,37690,37685,37691,37724,37728,37756,37742,37718,37808,37804,37805,37780,37817,37846,37847,37864,37861,37848,37827,37853,37840,37832,37860,37914,37908,37907,37891,37895,37904,37942,37931,37941,37921,37946,37953,37970,37956,37979,37984,37986,37982,37994,37417,38e3,38005,38007,38013,37978,38012,38014,38017,38015,38274,38279,38282,38292,38294,38296,38297,38304,38312,38311,38317,38332,38331,38329,38334,38346,28662,38339,38349,38348,38357,38356,38358,38364,38369,38373,38370,38433,38440,38446,38447,38466,38476,38479,38475,38519,38492,38494,38493,38495,38502,38514,38508,38541,38552,38549,38551,38570,38567,38577,38578,38576,38580,38582,38584,38585,38606,38603,38601,38605,35149,38620,38669,38613,38649,38660,38662,38664,38675,38670,38673,38671,38678,38681,38692,38698,38704,38713,38717,38718,38724,38726,38728,38722,38729,38748,38752,38756,38758,38760,21202,38763,38769,38777,38789,38780,38785,38778,38790,38795,38799,38800,38812,38824,38822,38819,38835,38836,38851,38854,38856,38859,38876,38893,40783,38898,31455,38902,38901,38927,38924,38968,38948,38945,38967,38973,38982,38991,38987,39019,39023,39024,39025,39028,39027,39082,39087,39089,39094,39108,39107,39110,39145,39147,39171,39177,39186,39188,39192,39201,39197,39198,39204,39200,39212,39214,39229,39230,39234,39241,39237,39248,39243,39249,39250,39244,39253,39319,39320,39333,39341,39342,39356,39391,39387,39389,39384,39377,39405,39406,39409,39410,39419,39416,39425,39439,39429,39394,39449,39467,39479,39493,39490,39488,39491,39486,39509,39501,39515,39511,39519,39522,39525,39524,39529,39531,39530,39597,39600,39612,39616,39631,39633,39635,39636,39646,39647,39650,39651,39654,39663,39659,39662,39668,39665,39671,39675,39686,39704,39706,39711,39714,39715,39717,39719,39720,39721,39722,39726,39727,39730,39748,39747,39759,39757,39758,39761,39768,39796,39827,39811,39825,39830,39831,39839,39840,39848,39860,39872,39882,39865,39878,39887,39889,39890,39907,39906,39908,39892,39905,39994,39922,39921,39920,39957,39956,39945,39955,39948,39942,39944,39954,39946,39940,39982,39963,39973,39972,39969,39984,40007,39986,40006,39998,40026,40032,40039,40054,40056,40167,40172,40176,40201,40200,40171,40195,40198,40234,40230,40367,40227,40223,40260,40213,40210,40257,40255,40254,40262,40264,40285,40286,40292,40273,40272,40281,40306,40329,40327,40363,40303,40314,40346,40356,40361,40370,40388,40385,40379,40376,40378,40390,40399,40386,40409,40403,40440,40422,40429,40431,40445,40474,40475,40478,40565,40569,40573,40577,40584,40587,40588,40594,40597,40593,40605,40613,40617,40632,40618,40621,38753,40652,40654,40655,40656,40660,40668,40670,40669,40672,40677,40680,40687,40692,40694,40695,40697,40699,40700,40701,40711,40712,30391,40725,40737,40748,40766,40778,40786,40788,40803,40799,40800,40801,40806,40807,40812,40810,40823,40818,40822,40853,40860,40864,22575,27079,36953,29796,20956,29081,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,32394,35100,37704,37512,34012,20425,28859,26161,26824,37625,26363,24389,20008,20193,20220,20224,20227,20281,20310,20370,20362,20378,20372,20429,20544,20514,20479,20510,20550,20592,20546,20628,20724,20696,20810,20836,20893,20926,20972,21013,21148,21158,21184,21211,21248,21255,21284,21362,21395,21426,21469,64014,21660,21642,21673,21759,21894,22361,22373,22444,22472,22471,64015,64016,22686,22706,22795,22867,22875,22877,22883,22948,22970,23382,23488,29999,23512,23532,23582,23718,23738,23797,23847,23891,64017,23874,23917,23992,23993,24016,24353,24372,24423,24503,24542,24669,24709,24714,24798,24789,24864,24818,24849,24887,24880,24984,25107,25254,25589,25696,25757,25806,25934,26112,26133,26171,26121,26158,26142,26148,26213,26199,26201,64018,26227,26265,26272,26290,26303,26362,26382,63785,26470,26555,26706,26560,26625,26692,26831,64019,26984,64020,27032,27106,27184,27243,27206,27251,27262,27362,27364,27606,27711,27740,27782,27759,27866,27908,28039,28015,28054,28076,28111,28152,28146,28156,28217,28252,28199,28220,28351,28552,28597,28661,28677,28679,28712,28805,28843,28943,28932,29020,28998,28999,64021,29121,29182,29361,29374,29476,64022,29559,29629,29641,29654,29667,29650,29703,29685,29734,29738,29737,29742,29794,29833,29855,29953,30063,30338,30364,30366,30363,30374,64023,30534,21167,30753,30798,30820,30842,31024,64024,64025,64026,31124,64027,31131,31441,31463,64028,31467,31646,64029,32072,32092,32183,32160,32214,32338,32583,32673,64030,33537,33634,33663,33735,33782,33864,33972,34131,34137,34155,64031,34224,64032,64033,34823,35061,35346,35383,35449,35495,35518,35551,64034,35574,35667,35711,36080,36084,36114,36214,64035,36559,64036,64037,36967,37086,64038,37141,37159,37338,37335,37342,37357,37358,37348,37349,37382,37392,37386,37434,37440,37436,37454,37465,37457,37433,37479,37543,37495,37496,37607,37591,37593,37584,64039,37589,37600,37587,37669,37665,37627,64040,37662,37631,37661,37634,37744,37719,37796,37830,37854,37880,37937,37957,37960,38290,63964,64041,38557,38575,38707,38715,38723,38733,38735,38737,38741,38999,39013,64042,64043,39207,64044,39326,39502,39641,39644,39797,39794,39823,39857,39867,39936,40304,40299,64045,40473,40657,null,null,8560,8561,8562,8563,8564,8565,8566,8567,8568,8569,65506,65508,65287,65282,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,8560,8561,8562,8563,8564,8565,8566,8567,8568,8569,8544,8545,8546,8547,8548,8549,8550,8551,8552,8553,65506,65508,65287,65282,12849,8470,8481,8757,32394,35100,37704,37512,34012,20425,28859,26161,26824,37625,26363,24389,20008,20193,20220,20224,20227,20281,20310,20370,20362,20378,20372,20429,20544,20514,20479,20510,20550,20592,20546,20628,20724,20696,20810,20836,20893,20926,20972,21013,21148,21158,21184,21211,21248,21255,21284,21362,21395,21426,21469,64014,21660,21642,21673,21759,21894,22361,22373,22444,22472,22471,64015,64016,22686,22706,22795,22867,22875,22877,22883,22948,22970,23382,23488,29999,23512,23532,23582,23718,23738,23797,23847,23891,64017,23874,23917,23992,23993,24016,24353,24372,24423,24503,24542,24669,24709,24714,24798,24789,24864,24818,24849,24887,24880,24984,25107,25254,25589,25696,25757,25806,25934,26112,26133,26171,26121,26158,26142,26148,26213,26199,26201,64018,26227,26265,26272,26290,26303,26362,26382,63785,26470,26555,26706,26560,26625,26692,26831,64019,26984,64020,27032,27106,27184,27243,27206,27251,27262,27362,27364,27606,27711,27740,27782,27759,27866,27908,28039,28015,28054,28076,28111,28152,28146,28156,28217,28252,28199,28220,28351,28552,28597,28661,28677,28679,28712,28805,28843,28943,28932,29020,28998,28999,64021,29121,29182,29361,29374,29476,64022,29559,29629,29641,29654,29667,29650,29703,29685,29734,29738,29737,29742,29794,29833,29855,29953,30063,30338,30364,30366,30363,30374,64023,30534,21167,30753,30798,30820,30842,31024,64024,64025,64026,31124,64027,31131,31441,31463,64028,31467,31646,64029,32072,32092,32183,32160,32214,32338,32583,32673,64030,33537,33634,33663,33735,33782,33864,33972,34131,34137,34155,64031,34224,64032,64033,34823,35061,35346,35383,35449,35495,35518,35551,64034,35574,35667,35711,36080,36084,36114,36214,64035,36559,64036,64037,36967,37086,64038,37141,37159,37338,37335,37342,37357,37358,37348,37349,37382,37392,37386,37434,37440,37436,37454,37465,37457,37433,37479,37543,37495,37496,37607,37591,37593,37584,64039,37589,37600,37587,37669,37665,37627,64040,37662,37631,37661,37634,37744,37719,37796,37830,37854,37880,37937,37957,37960,38290,63964,64041,38557,38575,38707,38715,38723,38733,38735,38737,38741,38999,39013,64042,64043,39207,64044,39326,39502,39641,39644,39797,39794,39823,39857,39867,39936,40304,40299,64045,40473,40657,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],jis0212:[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,728,711,184,729,733,175,731,730,65374,900,901,null,null,null,null,null,null,null,null,161,166,191,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,186,170,169,174,8482,164,8470,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,902,904,905,906,938,null,908,null,910,939,null,911,null,null,null,null,940,941,942,943,970,912,972,962,973,971,944,974,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,1026,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,1038,1039,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,1106,1107,1108,1109,1110,1111,1112,1113,1114,1115,1116,1118,1119,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,198,272,null,294,null,306,null,321,319,null,330,216,338,null,358,222,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,230,273,240,295,305,307,312,322,320,329,331,248,339,223,359,254,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,193,192,196,194,258,461,256,260,197,195,262,264,268,199,266,270,201,200,203,202,282,278,274,280,null,284,286,290,288,292,205,204,207,206,463,304,298,302,296,308,310,313,317,315,323,327,325,209,211,210,214,212,465,336,332,213,340,344,342,346,348,352,350,356,354,218,217,220,219,364,467,368,362,370,366,360,471,475,473,469,372,221,376,374,377,381,379,null,null,null,null,null,null,null,225,224,228,226,259,462,257,261,229,227,263,265,269,231,267,271,233,232,235,234,283,279,275,281,501,285,287,null,289,293,237,236,239,238,464,null,299,303,297,309,311,314,318,316,324,328,326,241,243,242,246,244,466,337,333,245,341,345,343,347,349,353,351,357,355,250,249,252,251,365,468,369,363,371,367,361,472,476,474,470,373,253,255,375,378,382,380,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,19970,19972,19973,19980,19986,19999,20003,20004,20008,20011,20014,20015,20016,20021,20032,20033,20036,20039,20049,20058,20060,20067,20072,20073,20084,20085,20089,20095,20109,20118,20119,20125,20143,20153,20163,20176,20186,20187,20192,20193,20194,20200,20207,20209,20211,20213,20221,20222,20223,20224,20226,20227,20232,20235,20236,20242,20245,20246,20247,20249,20270,20273,20320,20275,20277,20279,20281,20283,20286,20288,20290,20296,20297,20299,20300,20306,20308,20310,20312,20319,20323,20330,20332,20334,20337,20343,20344,20345,20346,20349,20350,20353,20354,20356,20357,20361,20362,20364,20366,20368,20370,20371,20372,20375,20377,20378,20382,20383,20402,20407,20409,20411,20412,20413,20414,20416,20417,20421,20422,20424,20425,20427,20428,20429,20431,20434,20444,20448,20450,20464,20466,20476,20477,20479,20480,20481,20484,20487,20490,20492,20494,20496,20499,20503,20504,20507,20508,20509,20510,20514,20519,20526,20528,20530,20531,20533,20544,20545,20546,20549,20550,20554,20556,20558,20561,20562,20563,20567,20569,20575,20576,20578,20579,20582,20583,20586,20589,20592,20593,20539,20609,20611,20612,20614,20618,20622,20623,20624,20626,20627,20628,20630,20635,20636,20638,20639,20640,20641,20642,20650,20655,20656,20665,20666,20669,20672,20675,20676,20679,20684,20686,20688,20691,20692,20696,20700,20701,20703,20706,20708,20710,20712,20713,20719,20721,20726,20730,20734,20739,20742,20743,20744,20747,20748,20749,20750,20722,20752,20759,20761,20763,20764,20765,20766,20771,20775,20776,20780,20781,20783,20785,20787,20788,20789,20792,20793,20802,20810,20815,20819,20821,20823,20824,20831,20836,20838,20862,20867,20868,20875,20878,20888,20893,20897,20899,20909,20920,20922,20924,20926,20927,20930,20936,20943,20945,20946,20947,20949,20952,20958,20962,20965,20974,20978,20979,20980,20983,20993,20994,20997,21010,21011,21013,21014,21016,21026,21032,21041,21042,21045,21052,21061,21065,21077,21079,21080,21082,21084,21087,21088,21089,21094,21102,21111,21112,21113,21120,21122,21125,21130,21132,21139,21141,21142,21143,21144,21146,21148,21156,21157,21158,21159,21167,21168,21174,21175,21176,21178,21179,21181,21184,21188,21190,21192,21196,21199,21201,21204,21206,21211,21212,21217,21221,21224,21225,21226,21228,21232,21233,21236,21238,21239,21248,21251,21258,21259,21260,21265,21267,21272,21275,21276,21278,21279,21285,21287,21288,21289,21291,21292,21293,21296,21298,21301,21308,21309,21310,21314,21324,21323,21337,21339,21345,21347,21349,21356,21357,21362,21369,21374,21379,21383,21384,21390,21395,21396,21401,21405,21409,21412,21418,21419,21423,21426,21428,21429,21431,21432,21434,21437,21440,21445,21455,21458,21459,21461,21466,21469,21470,21472,21478,21479,21493,21506,21523,21530,21537,21543,21544,21546,21551,21553,21556,21557,21571,21572,21575,21581,21583,21598,21602,21604,21606,21607,21609,21611,21613,21614,21620,21631,21633,21635,21637,21640,21641,21645,21649,21653,21654,21660,21663,21665,21670,21671,21673,21674,21677,21678,21681,21687,21689,21690,21691,21695,21702,21706,21709,21710,21728,21738,21740,21743,21750,21756,21758,21759,21760,21761,21765,21768,21769,21772,21773,21774,21781,21802,21803,21810,21813,21814,21819,21820,21821,21825,21831,21833,21834,21837,21840,21841,21848,21850,21851,21854,21856,21857,21860,21862,21887,21889,21890,21894,21896,21902,21903,21905,21906,21907,21908,21911,21923,21924,21933,21938,21951,21953,21955,21958,21961,21963,21964,21966,21969,21970,21971,21975,21976,21979,21982,21986,21993,22006,22015,22021,22024,22026,22029,22030,22031,22032,22033,22034,22041,22060,22064,22067,22069,22071,22073,22075,22076,22077,22079,22080,22081,22083,22084,22086,22089,22091,22093,22095,22100,22110,22112,22113,22114,22115,22118,22121,22125,22127,22129,22130,22133,22148,22149,22152,22155,22156,22165,22169,22170,22173,22174,22175,22182,22183,22184,22185,22187,22188,22189,22193,22195,22199,22206,22213,22217,22218,22219,22223,22224,22220,22221,22233,22236,22237,22239,22241,22244,22245,22246,22247,22248,22257,22251,22253,22262,22263,22273,22274,22279,22282,22284,22289,22293,22298,22299,22301,22304,22306,22307,22308,22309,22313,22314,22316,22318,22319,22323,22324,22333,22334,22335,22341,22342,22348,22349,22354,22370,22373,22375,22376,22379,22381,22382,22383,22384,22385,22387,22388,22389,22391,22393,22394,22395,22396,22398,22401,22403,22412,22420,22423,22425,22426,22428,22429,22430,22431,22433,22421,22439,22440,22441,22444,22456,22461,22471,22472,22476,22479,22485,22493,22494,22500,22502,22503,22505,22509,22512,22517,22518,22520,22525,22526,22527,22531,22532,22536,22537,22497,22540,22541,22555,22558,22559,22560,22566,22567,22573,22578,22585,22591,22601,22604,22605,22607,22608,22613,22623,22625,22628,22631,22632,22648,22652,22655,22656,22657,22663,22664,22665,22666,22668,22669,22671,22672,22676,22678,22685,22688,22689,22690,22694,22697,22705,22706,22724,22716,22722,22728,22733,22734,22736,22738,22740,22742,22746,22749,22753,22754,22761,22771,22789,22790,22795,22796,22802,22803,22804,34369,22813,22817,22819,22820,22824,22831,22832,22835,22837,22838,22847,22851,22854,22866,22867,22873,22875,22877,22878,22879,22881,22883,22891,22893,22895,22898,22901,22902,22905,22907,22908,22923,22924,22926,22930,22933,22935,22943,22948,22951,22957,22958,22959,22960,22963,22967,22970,22972,22977,22979,22980,22984,22986,22989,22994,23005,23006,23007,23011,23012,23015,23022,23023,23025,23026,23028,23031,23040,23044,23052,23053,23054,23058,23059,23070,23075,23076,23079,23080,23082,23085,23088,23108,23109,23111,23112,23116,23120,23125,23134,23139,23141,23143,23149,23159,23162,23163,23166,23179,23184,23187,23190,23193,23196,23198,23199,23200,23202,23207,23212,23217,23218,23219,23221,23224,23226,23227,23231,23236,23238,23240,23247,23258,23260,23264,23269,23274,23278,23285,23286,23293,23296,23297,23304,23319,23348,23321,23323,23325,23329,23333,23341,23352,23361,23371,23372,23378,23382,23390,23400,23406,23407,23420,23421,23422,23423,23425,23428,23430,23434,23438,23440,23441,23443,23444,23446,23464,23465,23468,23469,23471,23473,23474,23479,23482,23484,23488,23489,23501,23503,23510,23511,23512,23513,23514,23520,23535,23537,23540,23549,23564,23575,23582,23583,23587,23590,23593,23595,23596,23598,23600,23602,23605,23606,23641,23642,23644,23650,23651,23655,23656,23657,23661,23664,23668,23669,23674,23675,23676,23677,23687,23688,23690,23695,23698,23709,23711,23712,23714,23715,23718,23722,23730,23732,23733,23738,23753,23755,23762,23773,23767,23790,23793,23794,23796,23809,23814,23821,23826,23851,23843,23844,23846,23847,23857,23860,23865,23869,23871,23874,23875,23878,23880,23893,23889,23897,23882,23903,23904,23905,23906,23908,23914,23917,23920,23929,23930,23934,23935,23937,23939,23944,23946,23954,23955,23956,23957,23961,23963,23967,23968,23975,23979,23984,23988,23992,23993,24003,24007,24011,24016,24014,24024,24025,24032,24036,24041,24056,24057,24064,24071,24077,24082,24084,24085,24088,24095,24096,24110,24104,24114,24117,24126,24139,24144,24137,24145,24150,24152,24155,24156,24158,24168,24170,24171,24172,24173,24174,24176,24192,24203,24206,24226,24228,24229,24232,24234,24236,24241,24243,24253,24254,24255,24262,24268,24267,24270,24273,24274,24276,24277,24284,24286,24293,24299,24322,24326,24327,24328,24334,24345,24348,24349,24353,24354,24355,24356,24360,24363,24364,24366,24368,24372,24374,24379,24381,24383,24384,24388,24389,24391,24397,24400,24404,24408,24411,24416,24419,24420,24423,24431,24434,24436,24437,24440,24442,24445,24446,24457,24461,24463,24470,24476,24477,24482,24487,24491,24484,24492,24495,24496,24497,24504,24516,24519,24520,24521,24523,24528,24529,24530,24531,24532,24542,24545,24546,24552,24553,24554,24556,24557,24558,24559,24562,24563,24566,24570,24572,24583,24586,24589,24595,24596,24599,24600,24602,24607,24612,24621,24627,24629,24640,24647,24648,24649,24652,24657,24660,24662,24663,24669,24673,24679,24689,24702,24703,24706,24710,24712,24714,24718,24721,24723,24725,24728,24733,24734,24738,24740,24741,24744,24752,24753,24759,24763,24766,24770,24772,24776,24777,24778,24779,24782,24783,24788,24789,24793,24795,24797,24798,24802,24805,24818,24821,24824,24828,24829,24834,24839,24842,24844,24848,24849,24850,24851,24852,24854,24855,24857,24860,24862,24866,24874,24875,24880,24881,24885,24886,24887,24889,24897,24901,24902,24905,24926,24928,24940,24946,24952,24955,24956,24959,24960,24961,24963,24964,24971,24973,24978,24979,24983,24984,24988,24989,24991,24992,24997,25e3,25002,25005,25016,25017,25020,25024,25025,25026,25038,25039,25045,25052,25053,25054,25055,25057,25058,25063,25065,25061,25068,25069,25071,25089,25091,25092,25095,25107,25109,25116,25120,25122,25123,25127,25129,25131,25145,25149,25154,25155,25156,25158,25164,25168,25169,25170,25172,25174,25178,25180,25188,25197,25199,25203,25210,25213,25229,25230,25231,25232,25254,25256,25267,25270,25271,25274,25278,25279,25284,25294,25301,25302,25306,25322,25330,25332,25340,25341,25347,25348,25354,25355,25357,25360,25363,25366,25368,25385,25386,25389,25397,25398,25401,25404,25409,25410,25411,25412,25414,25418,25419,25422,25426,25427,25428,25432,25435,25445,25446,25452,25453,25457,25460,25461,25464,25468,25469,25471,25474,25476,25479,25482,25488,25492,25493,25497,25498,25502,25508,25510,25517,25518,25519,25533,25537,25541,25544,25550,25553,25555,25556,25557,25564,25568,25573,25578,25580,25586,25587,25589,25592,25593,25609,25610,25616,25618,25620,25624,25630,25632,25634,25636,25637,25641,25642,25647,25648,25653,25661,25663,25675,25679,25681,25682,25683,25684,25690,25691,25692,25693,25695,25696,25697,25699,25709,25715,25716,25723,25725,25733,25735,25743,25744,25745,25752,25753,25755,25757,25759,25761,25763,25766,25768,25772,25779,25789,25790,25791,25796,25801,25802,25803,25804,25806,25808,25809,25813,25815,25828,25829,25833,25834,25837,25840,25845,25847,25851,25855,25857,25860,25864,25865,25866,25871,25875,25876,25878,25881,25883,25886,25887,25890,25894,25897,25902,25905,25914,25916,25917,25923,25927,25929,25936,25938,25940,25951,25952,25959,25963,25978,25981,25985,25989,25994,26002,26005,26008,26013,26016,26019,26022,26030,26034,26035,26036,26047,26050,26056,26057,26062,26064,26068,26070,26072,26079,26096,26098,26100,26101,26105,26110,26111,26112,26116,26120,26121,26125,26129,26130,26133,26134,26141,26142,26145,26146,26147,26148,26150,26153,26154,26155,26156,26158,26160,26161,26163,26169,26167,26176,26181,26182,26186,26188,26193,26190,26199,26200,26201,26203,26204,26208,26209,26363,26218,26219,26220,26238,26227,26229,26239,26231,26232,26233,26235,26240,26236,26251,26252,26253,26256,26258,26265,26266,26267,26268,26271,26272,26276,26285,26289,26290,26293,26299,26303,26304,26306,26307,26312,26316,26318,26319,26324,26331,26335,26344,26347,26348,26350,26362,26373,26375,26382,26387,26393,26396,26400,26402,26419,26430,26437,26439,26440,26444,26452,26453,26461,26470,26476,26478,26484,26486,26491,26497,26500,26510,26511,26513,26515,26518,26520,26521,26523,26544,26545,26546,26549,26555,26556,26557,26617,26560,26562,26563,26565,26568,26569,26578,26583,26585,26588,26593,26598,26608,26610,26614,26615,26706,26644,26649,26653,26655,26664,26663,26668,26669,26671,26672,26673,26675,26683,26687,26692,26693,26698,26700,26709,26711,26712,26715,26731,26734,26735,26736,26737,26738,26741,26745,26746,26747,26748,26754,26756,26758,26760,26774,26776,26778,26780,26785,26787,26789,26793,26794,26798,26802,26811,26821,26824,26828,26831,26832,26833,26835,26838,26841,26844,26845,26853,26856,26858,26859,26860,26861,26864,26865,26869,26870,26875,26876,26877,26886,26889,26890,26896,26897,26899,26902,26903,26929,26931,26933,26936,26939,26946,26949,26953,26958,26967,26971,26979,26980,26981,26982,26984,26985,26988,26992,26993,26994,27002,27003,27007,27008,27021,27026,27030,27032,27041,27045,27046,27048,27051,27053,27055,27063,27064,27066,27068,27077,27080,27089,27094,27095,27106,27109,27118,27119,27121,27123,27125,27134,27136,27137,27139,27151,27153,27157,27162,27165,27168,27172,27176,27184,27186,27188,27191,27195,27198,27199,27205,27206,27209,27210,27214,27216,27217,27218,27221,27222,27227,27236,27239,27242,27249,27251,27262,27265,27267,27270,27271,27273,27275,27281,27291,27293,27294,27295,27301,27307,27311,27312,27313,27316,27325,27326,27327,27334,27337,27336,27340,27344,27348,27349,27350,27356,27357,27364,27367,27372,27376,27377,27378,27388,27389,27394,27395,27398,27399,27401,27407,27408,27409,27415,27419,27422,27428,27432,27435,27436,27439,27445,27446,27451,27455,27462,27466,27469,27474,27478,27480,27485,27488,27495,27499,27502,27504,27509,27517,27518,27522,27525,27543,27547,27551,27552,27554,27555,27560,27561,27564,27565,27566,27568,27576,27577,27581,27582,27587,27588,27593,27596,27606,27610,27617,27619,27622,27623,27630,27633,27639,27641,27647,27650,27652,27653,27657,27661,27662,27664,27666,27673,27679,27686,27687,27688,27692,27694,27699,27701,27702,27706,27707,27711,27722,27723,27725,27727,27730,27732,27737,27739,27740,27755,27757,27759,27764,27766,27768,27769,27771,27781,27782,27783,27785,27796,27797,27799,27800,27804,27807,27824,27826,27828,27842,27846,27853,27855,27856,27857,27858,27860,27862,27866,27868,27872,27879,27881,27883,27884,27886,27890,27892,27908,27911,27914,27918,27919,27921,27923,27930,27942,27943,27944,27751,27950,27951,27953,27961,27964,27967,27991,27998,27999,28001,28005,28007,28015,28016,28028,28034,28039,28049,28050,28052,28054,28055,28056,28074,28076,28084,28087,28089,28093,28095,28100,28104,28106,28110,28111,28118,28123,28125,28127,28128,28130,28133,28137,28143,28144,28148,28150,28156,28160,28164,28190,28194,28199,28210,28214,28217,28219,28220,28228,28229,28232,28233,28235,28239,28241,28242,28243,28244,28247,28252,28253,28254,28258,28259,28264,28275,28283,28285,28301,28307,28313,28320,28327,28333,28334,28337,28339,28347,28351,28352,28353,28355,28359,28360,28362,28365,28366,28367,28395,28397,28398,28409,28411,28413,28420,28424,28426,28428,28429,28438,28440,28442,28443,28454,28457,28458,28463,28464,28467,28470,28475,28476,28461,28495,28497,28498,28499,28503,28505,28506,28509,28510,28513,28514,28520,28524,28541,28542,28547,28551,28552,28555,28556,28557,28560,28562,28563,28564,28566,28570,28575,28576,28581,28582,28583,28584,28590,28591,28592,28597,28598,28604,28613,28615,28616,28618,28634,28638,28648,28649,28656,28661,28665,28668,28669,28672,28677,28678,28679,28685,28695,28704,28707,28719,28724,28727,28729,28732,28739,28740,28744,28745,28746,28747,28756,28757,28765,28766,28750,28772,28773,28780,28782,28789,28790,28798,28801,28805,28806,28820,28821,28822,28823,28824,28827,28836,28843,28848,28849,28852,28855,28874,28881,28883,28884,28885,28886,28888,28892,28900,28922,28931,28932,28933,28934,28935,28939,28940,28943,28958,28960,28971,28973,28975,28976,28977,28984,28993,28997,28998,28999,29002,29003,29008,29010,29015,29018,29020,29022,29024,29032,29049,29056,29061,29063,29068,29074,29082,29083,29088,29090,29103,29104,29106,29107,29114,29119,29120,29121,29124,29131,29132,29139,29142,29145,29146,29148,29176,29182,29184,29191,29192,29193,29203,29207,29210,29213,29215,29220,29227,29231,29236,29240,29241,29249,29250,29251,29253,29262,29263,29264,29267,29269,29270,29274,29276,29278,29280,29283,29288,29291,29294,29295,29297,29303,29304,29307,29308,29311,29316,29321,29325,29326,29331,29339,29352,29357,29358,29361,29364,29374,29377,29383,29385,29388,29397,29398,29400,29407,29413,29427,29428,29434,29435,29438,29442,29444,29445,29447,29451,29453,29458,29459,29464,29465,29470,29474,29476,29479,29480,29484,29489,29490,29493,29498,29499,29501,29507,29517,29520,29522,29526,29528,29533,29534,29535,29536,29542,29543,29545,29547,29548,29550,29551,29553,29559,29561,29564,29568,29569,29571,29573,29574,29582,29584,29587,29589,29591,29592,29596,29598,29599,29600,29602,29605,29606,29610,29611,29613,29621,29623,29625,29628,29629,29631,29637,29638,29641,29643,29644,29647,29650,29651,29654,29657,29661,29665,29667,29670,29671,29673,29684,29685,29687,29689,29690,29691,29693,29695,29696,29697,29700,29703,29706,29713,29722,29723,29732,29734,29736,29737,29738,29739,29740,29741,29742,29743,29744,29745,29753,29760,29763,29764,29766,29767,29771,29773,29777,29778,29783,29789,29794,29798,29799,29800,29803,29805,29806,29809,29810,29824,29825,29829,29830,29831,29833,29839,29840,29841,29842,29848,29849,29850,29852,29855,29856,29857,29859,29862,29864,29865,29866,29867,29870,29871,29873,29874,29877,29881,29883,29887,29896,29897,29900,29904,29907,29912,29914,29915,29918,29919,29924,29928,29930,29931,29935,29940,29946,29947,29948,29951,29958,29970,29974,29975,29984,29985,29988,29991,29993,29994,29999,30006,30009,30013,30014,30015,30016,30019,30023,30024,30030,30032,30034,30039,30046,30047,30049,30063,30065,30073,30074,30075,30076,30077,30078,30081,30085,30096,30098,30099,30101,30105,30108,30114,30116,30132,30138,30143,30144,30145,30148,30150,30156,30158,30159,30167,30172,30175,30176,30177,30180,30183,30188,30190,30191,30193,30201,30208,30210,30211,30212,30215,30216,30218,30220,30223,30226,30227,30229,30230,30233,30235,30236,30237,30238,30243,30245,30246,30249,30253,30258,30259,30261,30264,30265,30266,30268,30282,30272,30273,30275,30276,30277,30281,30283,30293,30297,30303,30308,30309,30317,30318,30319,30321,30324,30337,30341,30348,30349,30357,30363,30364,30365,30367,30368,30370,30371,30372,30373,30374,30375,30376,30378,30381,30397,30401,30405,30409,30411,30412,30414,30420,30425,30432,30438,30440,30444,30448,30449,30454,30457,30460,30464,30470,30474,30478,30482,30484,30485,30487,30489,30490,30492,30498,30504,30509,30510,30511,30516,30517,30518,30521,30525,30526,30530,30533,30534,30538,30541,30542,30543,30546,30550,30551,30556,30558,30559,30560,30562,30564,30567,30570,30572,30576,30578,30579,30580,30586,30589,30592,30596,30604,30605,30612,30613,30614,30618,30623,30626,30631,30634,30638,30639,30641,30645,30654,30659,30665,30673,30674,30677,30681,30686,30687,30688,30692,30694,30698,30700,30704,30705,30708,30712,30715,30725,30726,30729,30733,30734,30737,30749,30753,30754,30755,30765,30766,30768,30773,30775,30787,30788,30791,30792,30796,30798,30802,30812,30814,30816,30817,30819,30820,30824,30826,30830,30842,30846,30858,30863,30868,30872,30881,30877,30878,30879,30884,30888,30892,30893,30896,30897,30898,30899,30907,30909,30911,30919,30920,30921,30924,30926,30930,30931,30933,30934,30948,30939,30943,30944,30945,30950,30954,30962,30963,30976,30966,30967,30970,30971,30975,30982,30988,30992,31002,31004,31006,31007,31008,31013,31015,31017,31021,31025,31028,31029,31035,31037,31039,31044,31045,31046,31050,31051,31055,31057,31060,31064,31067,31068,31079,31081,31083,31090,31097,31099,31100,31102,31115,31116,31121,31123,31124,31125,31126,31128,31131,31132,31137,31144,31145,31147,31151,31153,31156,31160,31163,31170,31172,31175,31176,31178,31183,31188,31190,31194,31197,31198,31200,31202,31205,31210,31211,31213,31217,31224,31228,31234,31235,31239,31241,31242,31244,31249,31253,31259,31262,31265,31271,31275,31277,31279,31280,31284,31285,31288,31289,31290,31300,31301,31303,31304,31308,31317,31318,31321,31324,31325,31327,31328,31333,31335,31338,31341,31349,31352,31358,31360,31362,31365,31366,31370,31371,31376,31377,31380,31390,31392,31395,31404,31411,31413,31417,31419,31420,31430,31433,31436,31438,31441,31451,31464,31465,31467,31468,31473,31476,31483,31485,31486,31495,31508,31519,31523,31527,31529,31530,31531,31533,31534,31535,31536,31537,31540,31549,31551,31552,31553,31559,31566,31573,31584,31588,31590,31593,31594,31597,31599,31602,31603,31607,31620,31625,31630,31632,31633,31638,31643,31646,31648,31653,31660,31663,31664,31666,31669,31670,31674,31675,31676,31677,31682,31685,31688,31690,31700,31702,31703,31705,31706,31707,31720,31722,31730,31732,31733,31736,31737,31738,31740,31742,31745,31746,31747,31748,31750,31753,31755,31756,31758,31759,31769,31771,31776,31781,31782,31784,31788,31793,31795,31796,31798,31801,31802,31814,31818,31829,31825,31826,31827,31833,31834,31835,31836,31837,31838,31841,31843,31847,31849,31853,31854,31856,31858,31865,31868,31869,31878,31879,31887,31892,31902,31904,31910,31920,31926,31927,31930,31931,31932,31935,31940,31943,31944,31945,31949,31951,31955,31956,31957,31959,31961,31962,31965,31974,31977,31979,31989,32003,32007,32008,32009,32015,32017,32018,32019,32022,32029,32030,32035,32038,32042,32045,32049,32060,32061,32062,32064,32065,32071,32072,32077,32081,32083,32087,32089,32090,32092,32093,32101,32103,32106,32112,32120,32122,32123,32127,32129,32130,32131,32133,32134,32136,32139,32140,32141,32145,32150,32151,32157,32158,32166,32167,32170,32179,32182,32183,32185,32194,32195,32196,32197,32198,32204,32205,32206,32215,32217,32256,32226,32229,32230,32234,32235,32237,32241,32245,32246,32249,32250,32264,32272,32273,32277,32279,32284,32285,32288,32295,32296,32300,32301,32303,32307,32310,32319,32324,32325,32327,32334,32336,32338,32344,32351,32353,32354,32357,32363,32366,32367,32371,32376,32382,32385,32390,32391,32394,32397,32401,32405,32408,32410,32413,32414,32572,32571,32573,32574,32575,32579,32580,32583,32591,32594,32595,32603,32604,32605,32609,32611,32612,32613,32614,32621,32625,32637,32638,32639,32640,32651,32653,32655,32656,32657,32662,32663,32668,32673,32674,32678,32682,32685,32692,32700,32703,32704,32707,32712,32718,32719,32731,32735,32739,32741,32744,32748,32750,32751,32754,32762,32765,32766,32767,32775,32776,32778,32781,32782,32783,32785,32787,32788,32790,32797,32798,32799,32800,32804,32806,32812,32814,32816,32820,32821,32823,32825,32826,32828,32830,32832,32836,32864,32868,32870,32877,32881,32885,32897,32904,32910,32924,32926,32934,32935,32939,32952,32953,32968,32973,32975,32978,32980,32981,32983,32984,32992,33005,33006,33008,33010,33011,33014,33017,33018,33022,33027,33035,33046,33047,33048,33052,33054,33056,33060,33063,33068,33072,33077,33082,33084,33093,33095,33098,33100,33106,33111,33120,33121,33127,33128,33129,33133,33135,33143,33153,33168,33156,33157,33158,33163,33166,33174,33176,33179,33182,33186,33198,33202,33204,33211,33227,33219,33221,33226,33230,33231,33237,33239,33243,33245,33246,33249,33252,33259,33260,33264,33265,33266,33269,33270,33272,33273,33277,33279,33280,33283,33295,33299,33300,33305,33306,33309,33313,33314,33320,33330,33332,33338,33347,33348,33349,33350,33355,33358,33359,33361,33366,33372,33376,33379,33383,33389,33396,33403,33405,33407,33408,33409,33411,33412,33415,33417,33418,33422,33425,33428,33430,33432,33434,33435,33440,33441,33443,33444,33447,33448,33449,33450,33454,33456,33458,33460,33463,33466,33468,33470,33471,33478,33488,33493,33498,33504,33506,33508,33512,33514,33517,33519,33526,33527,33533,33534,33536,33537,33543,33544,33546,33547,33620,33563,33565,33566,33567,33569,33570,33580,33581,33582,33584,33587,33591,33594,33596,33597,33602,33603,33604,33607,33613,33614,33617,33621,33622,33623,33648,33656,33661,33663,33664,33666,33668,33670,33677,33682,33684,33685,33688,33689,33691,33692,33693,33702,33703,33705,33708,33726,33727,33728,33735,33737,33743,33744,33745,33748,33757,33619,33768,33770,33782,33784,33785,33788,33793,33798,33802,33807,33809,33813,33817,33709,33839,33849,33861,33863,33864,33866,33869,33871,33873,33874,33878,33880,33881,33882,33884,33888,33892,33893,33895,33898,33904,33907,33908,33910,33912,33916,33917,33921,33925,33938,33939,33941,33950,33958,33960,33961,33962,33967,33969,33972,33978,33981,33982,33984,33986,33991,33992,33996,33999,34003,34012,34023,34026,34031,34032,34033,34034,34039,34098,34042,34043,34045,34050,34051,34055,34060,34062,34064,34076,34078,34082,34083,34084,34085,34087,34090,34091,34095,34099,34100,34102,34111,34118,34127,34128,34129,34130,34131,34134,34137,34140,34141,34142,34143,34144,34145,34146,34148,34155,34159,34169,34170,34171,34173,34175,34177,34181,34182,34185,34187,34188,34191,34195,34200,34205,34207,34208,34210,34213,34215,34228,34230,34231,34232,34236,34237,34238,34239,34242,34247,34250,34251,34254,34221,34264,34266,34271,34272,34278,34280,34285,34291,34294,34300,34303,34304,34308,34309,34317,34318,34320,34321,34322,34328,34329,34331,34334,34337,34343,34345,34358,34360,34362,34364,34365,34368,34370,34374,34386,34387,34390,34391,34392,34393,34397,34400,34401,34402,34403,34404,34409,34412,34415,34421,34422,34423,34426,34445,34449,34454,34456,34458,34460,34465,34470,34471,34472,34477,34481,34483,34484,34485,34487,34488,34489,34495,34496,34497,34499,34501,34513,34514,34517,34519,34522,34524,34528,34531,34533,34535,34440,34554,34556,34557,34564,34565,34567,34571,34574,34575,34576,34579,34580,34585,34590,34591,34593,34595,34600,34606,34607,34609,34610,34617,34618,34620,34621,34622,34624,34627,34629,34637,34648,34653,34657,34660,34661,34671,34673,34674,34683,34691,34692,34693,34694,34695,34696,34697,34699,34700,34704,34707,34709,34711,34712,34713,34718,34720,34723,34727,34732,34733,34734,34737,34741,34750,34751,34753,34760,34761,34762,34766,34773,34774,34777,34778,34780,34783,34786,34787,34788,34794,34795,34797,34801,34803,34808,34810,34815,34817,34819,34822,34825,34826,34827,34832,34841,34834,34835,34836,34840,34842,34843,34844,34846,34847,34856,34861,34862,34864,34866,34869,34874,34876,34881,34883,34885,34888,34889,34890,34891,34894,34897,34901,34902,34904,34906,34908,34911,34912,34916,34921,34929,34937,34939,34944,34968,34970,34971,34972,34975,34976,34984,34986,35002,35005,35006,35008,35018,35019,35020,35021,35022,35025,35026,35027,35035,35038,35047,35055,35056,35057,35061,35063,35073,35078,35085,35086,35087,35093,35094,35096,35097,35098,35100,35104,35110,35111,35112,35120,35121,35122,35125,35129,35130,35134,35136,35138,35141,35142,35145,35151,35154,35159,35162,35163,35164,35169,35170,35171,35179,35182,35184,35187,35189,35194,35195,35196,35197,35209,35213,35216,35220,35221,35227,35228,35231,35232,35237,35248,35252,35253,35254,35255,35260,35284,35285,35286,35287,35288,35301,35305,35307,35309,35313,35315,35318,35321,35325,35327,35332,35333,35335,35343,35345,35346,35348,35349,35358,35360,35362,35364,35366,35371,35372,35375,35381,35383,35389,35390,35392,35395,35397,35399,35401,35405,35406,35411,35414,35415,35416,35420,35421,35425,35429,35431,35445,35446,35447,35449,35450,35451,35454,35455,35456,35459,35462,35467,35471,35472,35474,35478,35479,35481,35487,35495,35497,35502,35503,35507,35510,35511,35515,35518,35523,35526,35528,35529,35530,35537,35539,35540,35541,35543,35549,35551,35564,35568,35572,35573,35574,35580,35583,35589,35590,35595,35601,35612,35614,35615,35594,35629,35632,35639,35644,35650,35651,35652,35653,35654,35656,35666,35667,35668,35673,35661,35678,35683,35693,35702,35704,35705,35708,35710,35713,35716,35717,35723,35725,35727,35732,35733,35740,35742,35743,35896,35897,35901,35902,35909,35911,35913,35915,35919,35921,35923,35924,35927,35928,35931,35933,35929,35939,35940,35942,35944,35945,35949,35955,35957,35958,35963,35966,35974,35975,35979,35984,35986,35987,35993,35995,35996,36004,36025,36026,36037,36038,36041,36043,36047,36054,36053,36057,36061,36065,36072,36076,36079,36080,36082,36085,36087,36088,36094,36095,36097,36099,36105,36114,36119,36123,36197,36201,36204,36206,36223,36226,36228,36232,36237,36240,36241,36245,36254,36255,36256,36262,36267,36268,36271,36274,36277,36279,36281,36283,36288,36293,36294,36295,36296,36298,36302,36305,36308,36309,36311,36313,36324,36325,36327,36332,36336,36284,36337,36338,36340,36349,36353,36356,36357,36358,36363,36369,36372,36374,36384,36385,36386,36387,36390,36391,36401,36403,36406,36407,36408,36409,36413,36416,36417,36427,36429,36430,36431,36436,36443,36444,36445,36446,36449,36450,36457,36460,36461,36463,36464,36465,36473,36474,36475,36482,36483,36489,36496,36498,36501,36506,36507,36509,36510,36514,36519,36521,36525,36526,36531,36533,36538,36539,36544,36545,36547,36548,36551,36559,36561,36564,36572,36584,36590,36592,36593,36599,36601,36602,36589,36608,36610,36615,36616,36623,36624,36630,36631,36632,36638,36640,36641,36643,36645,36647,36648,36652,36653,36654,36660,36661,36662,36663,36666,36672,36673,36675,36679,36687,36689,36690,36691,36692,36693,36696,36701,36702,36709,36765,36768,36769,36772,36773,36774,36789,36790,36792,36798,36800,36801,36806,36810,36811,36813,36816,36818,36819,36821,36832,36835,36836,36840,36846,36849,36853,36854,36859,36862,36866,36868,36872,36876,36888,36891,36904,36905,36911,36906,36908,36909,36915,36916,36919,36927,36931,36932,36940,36955,36957,36962,36966,36967,36972,36976,36980,36985,36997,37e3,37003,37004,37006,37008,37013,37015,37016,37017,37019,37024,37025,37026,37029,37040,37042,37043,37044,37046,37053,37068,37054,37059,37060,37061,37063,37064,37077,37079,37080,37081,37084,37085,37087,37093,37074,37110,37099,37103,37104,37108,37118,37119,37120,37124,37125,37126,37128,37133,37136,37140,37142,37143,37144,37146,37148,37150,37152,37157,37154,37155,37159,37161,37166,37167,37169,37172,37174,37175,37177,37178,37180,37181,37187,37191,37192,37199,37203,37207,37209,37210,37211,37217,37220,37223,37229,37236,37241,37242,37243,37249,37251,37253,37254,37258,37262,37265,37267,37268,37269,37272,37278,37281,37286,37288,37292,37293,37294,37296,37297,37298,37299,37302,37307,37308,37309,37311,37314,37315,37317,37331,37332,37335,37337,37338,37342,37348,37349,37353,37354,37356,37357,37358,37359,37360,37361,37367,37369,37371,37373,37376,37377,37380,37381,37382,37383,37385,37386,37388,37392,37394,37395,37398,37400,37404,37405,37411,37412,37413,37414,37416,37422,37423,37424,37427,37429,37430,37432,37433,37434,37436,37438,37440,37442,37443,37446,37447,37450,37453,37454,37455,37457,37464,37465,37468,37469,37472,37473,37477,37479,37480,37481,37486,37487,37488,37493,37494,37495,37496,37497,37499,37500,37501,37503,37512,37513,37514,37517,37518,37522,37527,37529,37535,37536,37540,37541,37543,37544,37547,37551,37554,37558,37560,37562,37563,37564,37565,37567,37568,37569,37570,37571,37573,37574,37575,37576,37579,37580,37581,37582,37584,37587,37589,37591,37592,37593,37596,37597,37599,37600,37601,37603,37605,37607,37608,37612,37614,37616,37625,37627,37631,37632,37634,37640,37645,37649,37652,37653,37660,37661,37662,37663,37665,37668,37669,37671,37673,37674,37683,37684,37686,37687,37703,37704,37705,37712,37713,37714,37717,37719,37720,37722,37726,37732,37733,37735,37737,37738,37741,37743,37744,37745,37747,37748,37750,37754,37757,37759,37760,37761,37762,37768,37770,37771,37773,37775,37778,37781,37784,37787,37790,37793,37795,37796,37798,37800,37803,37812,37813,37814,37818,37801,37825,37828,37829,37830,37831,37833,37834,37835,37836,37837,37843,37849,37852,37854,37855,37858,37862,37863,37881,37879,37880,37882,37883,37885,37889,37890,37892,37896,37897,37901,37902,37903,37909,37910,37911,37919,37934,37935,37937,37938,37939,37940,37947,37951,37949,37955,37957,37960,37962,37964,37973,37977,37980,37983,37985,37987,37992,37995,37997,37998,37999,38001,38002,38020,38019,38264,38265,38270,38276,38280,38284,38285,38286,38301,38302,38303,38305,38310,38313,38315,38316,38324,38326,38330,38333,38335,38342,38344,38345,38347,38352,38353,38354,38355,38361,38362,38365,38366,38367,38368,38372,38374,38429,38430,38434,38436,38437,38438,38444,38449,38451,38455,38456,38457,38458,38460,38461,38465,38482,38484,38486,38487,38488,38497,38510,38516,38523,38524,38526,38527,38529,38530,38531,38532,38537,38545,38550,38554,38557,38559,38564,38565,38566,38569,38574,38575,38579,38586,38602,38610,23986,38616,38618,38621,38622,38623,38633,38639,38641,38650,38658,38659,38661,38665,38682,38683,38685,38689,38690,38691,38696,38705,38707,38721,38723,38730,38734,38735,38741,38743,38744,38746,38747,38755,38759,38762,38766,38771,38774,38775,38776,38779,38781,38783,38784,38793,38805,38806,38807,38809,38810,38814,38815,38818,38828,38830,38833,38834,38837,38838,38840,38841,38842,38844,38846,38847,38849,38852,38853,38855,38857,38858,38860,38861,38862,38864,38865,38868,38871,38872,38873,38877,38878,38880,38875,38881,38884,38895,38897,38900,38903,38904,38906,38919,38922,38937,38925,38926,38932,38934,38940,38942,38944,38947,38950,38955,38958,38959,38960,38962,38963,38965,38949,38974,38980,38983,38986,38993,38994,38995,38998,38999,39001,39002,39010,39011,39013,39014,39018,39020,39083,39085,39086,39088,39092,39095,39096,39098,39099,39103,39106,39109,39112,39116,39137,39139,39141,39142,39143,39146,39155,39158,39170,39175,39176,39185,39189,39190,39191,39194,39195,39196,39199,39202,39206,39207,39211,39217,39218,39219,39220,39221,39225,39226,39227,39228,39232,39233,39238,39239,39240,39245,39246,39252,39256,39257,39259,39260,39262,39263,39264,39323,39325,39327,39334,39344,39345,39346,39349,39353,39354,39357,39359,39363,39369,39379,39380,39385,39386,39388,39390,39399,39402,39403,39404,39408,39412,39413,39417,39421,39422,39426,39427,39428,39435,39436,39440,39441,39446,39454,39456,39458,39459,39460,39463,39469,39470,39475,39477,39478,39480,39495,39489,39492,39498,39499,39500,39502,39505,39508,39510,39517,39594,39596,39598,39599,39602,39604,39605,39606,39609,39611,39614,39615,39617,39619,39622,39624,39630,39632,39634,39637,39638,39639,39643,39644,39648,39652,39653,39655,39657,39660,39666,39667,39669,39673,39674,39677,39679,39680,39681,39682,39683,39684,39685,39688,39689,39691,39692,39693,39694,39696,39698,39702,39705,39707,39708,39712,39718,39723,39725,39731,39732,39733,39735,39737,39738,39741,39752,39755,39756,39765,39766,39767,39771,39774,39777,39779,39781,39782,39784,39786,39787,39788,39789,39790,39795,39797,39799,39800,39801,39807,39808,39812,39813,39814,39815,39817,39818,39819,39821,39823,39824,39828,39834,39837,39838,39846,39847,39849,39852,39856,39857,39858,39863,39864,39867,39868,39870,39871,39873,39879,39880,39886,39888,39895,39896,39901,39903,39909,39911,39914,39915,39919,39923,39927,39928,39929,39930,39933,39935,39936,39938,39947,39951,39953,39958,39960,39961,39962,39964,39966,39970,39971,39974,39975,39976,39977,39978,39985,39989,39990,39991,39997,40001,40003,40004,40005,40009,40010,40014,40015,40016,40019,40020,40022,40024,40027,40029,40030,40031,40035,40041,40042,40028,40043,40040,40046,40048,40050,40053,40055,40059,40166,40178,40183,40185,40203,40194,40209,40215,40216,40220,40221,40222,40239,40240,40242,40243,40244,40250,40252,40261,40253,40258,40259,40263,40266,40275,40276,40287,40291,40290,40293,40297,40298,40299,40304,40310,40311,40315,40316,40318,40323,40324,40326,40330,40333,40334,40338,40339,40341,40342,40343,40344,40353,40362,40364,40366,40369,40373,40377,40380,40383,40387,40391,40393,40394,40404,40405,40406,40407,40410,40414,40415,40416,40421,40423,40425,40427,40430,40432,40435,40436,40446,40458,40450,40455,40462,40464,40465,40466,40469,40470,40473,40476,40477,40570,40571,40572,40576,40578,40579,40580,40581,40583,40590,40591,40598,40600,40603,40606,40612,40616,40620,40622,40623,40624,40627,40628,40629,40646,40648,40651,40661,40671,40676,40679,40684,40685,40686,40688,40689,40690,40693,40696,40703,40706,40707,40713,40719,40720,40721,40722,40724,40726,40727,40729,40730,40731,40735,40738,40742,40746,40747,40751,40753,40754,40756,40759,40761,40762,40764,40765,40767,40769,40771,40772,40773,40774,40775,40787,40789,40790,40791,40792,40794,40797,40798,40808,40809,40813,40814,40815,40816,40817,40819,40821,40826,40829,40847,40848,40849,40850,40852,40854,40855,40862,40865,40866,40867,40869,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],ibm866:[1040,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,9617,9618,9619,9474,9508,9569,9570,9558,9557,9571,9553,9559,9565,9564,9563,9488,9492,9524,9516,9500,9472,9532,9566,9567,9562,9556,9577,9574,9568,9552,9580,9575,9576,9572,9573,9561,9560,9554,9555,9579,9578,9496,9484,9608,9604,9612,9616,9600,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,1025,1105,1028,1108,1031,1111,1038,1118,176,8729,183,8730,8470,164,9632,160],\"iso-8859-2\":[128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,260,728,321,164,317,346,167,168,352,350,356,377,173,381,379,176,261,731,322,180,318,347,711,184,353,351,357,378,733,382,380,340,193,194,258,196,313,262,199,268,201,280,203,282,205,206,270,272,323,327,211,212,336,214,215,344,366,218,368,220,221,354,223,341,225,226,259,228,314,263,231,269,233,281,235,283,237,238,271,273,324,328,243,244,337,246,247,345,367,250,369,252,253,355,729],\"iso-8859-3\":[128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,294,728,163,164,null,292,167,168,304,350,286,308,173,null,379,176,295,178,179,180,181,293,183,184,305,351,287,309,189,null,380,192,193,194,null,196,266,264,199,200,201,202,203,204,205,206,207,null,209,210,211,212,288,214,215,284,217,218,219,220,364,348,223,224,225,226,null,228,267,265,231,232,233,234,235,236,237,238,239,null,241,242,243,244,289,246,247,285,249,250,251,252,365,349,729],\"iso-8859-4\":[128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,260,312,342,164,296,315,167,168,352,274,290,358,173,381,175,176,261,731,343,180,297,316,711,184,353,275,291,359,330,382,331,256,193,194,195,196,197,198,302,268,201,280,203,278,205,206,298,272,325,332,310,212,213,214,215,216,370,218,219,220,360,362,223,257,225,226,227,228,229,230,303,269,233,281,235,279,237,238,299,273,326,333,311,244,245,246,247,248,371,250,251,252,361,363,729],\"iso-8859-5\":[128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,1025,1026,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,173,1038,1039,1040,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,8470,1105,1106,1107,1108,1109,1110,1111,1112,1113,1114,1115,1116,167,1118,1119],\"iso-8859-6\":[128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,null,null,null,164,null,null,null,null,null,null,null,1548,173,null,null,null,null,null,null,null,null,null,null,null,null,null,1563,null,null,null,1567,null,1569,1570,1571,1572,1573,1574,1575,1576,1577,1578,1579,1580,1581,1582,1583,1584,1585,1586,1587,1588,1589,1590,1591,1592,1593,1594,null,null,null,null,null,1600,1601,1602,1603,1604,1605,1606,1607,1608,1609,1610,1611,1612,1613,1614,1615,1616,1617,1618,null,null,null,null,null,null,null,null,null,null,null,null,null],\"iso-8859-7\":[128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,8216,8217,163,8364,8367,166,167,168,169,890,171,172,173,null,8213,176,177,178,179,900,901,902,183,904,905,906,187,908,189,910,911,912,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,null,931,932,933,934,935,936,937,938,939,940,941,942,943,944,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,962,963,964,965,966,967,968,969,970,971,972,973,974,null],\"iso-8859-8\":[128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,null,162,163,164,165,166,167,168,169,215,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,247,187,188,189,190,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,8215,1488,1489,1490,1491,1492,1493,1494,1495,1496,1497,1498,1499,1500,1501,1502,1503,1504,1505,1506,1507,1508,1509,1510,1511,1512,1513,1514,null,null,8206,8207,null],\"iso-8859-10\":[128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,260,274,290,298,296,310,167,315,272,352,358,381,173,362,330,176,261,275,291,299,297,311,183,316,273,353,359,382,8213,363,331,256,193,194,195,196,197,198,302,268,201,280,203,278,205,206,207,208,325,332,211,212,213,214,360,216,370,218,219,220,221,222,223,257,225,226,227,228,229,230,303,269,233,281,235,279,237,238,239,240,326,333,243,244,245,246,361,248,371,250,251,252,253,254,312],\"iso-8859-13\":[128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,8221,162,163,164,8222,166,167,216,169,342,171,172,173,174,198,176,177,178,179,8220,181,182,183,248,185,343,187,188,189,190,230,260,302,256,262,196,197,280,274,268,201,377,278,290,310,298,315,352,323,325,211,332,213,214,215,370,321,346,362,220,379,381,223,261,303,257,263,228,229,281,275,269,233,378,279,291,311,299,316,353,324,326,243,333,245,246,247,371,322,347,363,252,380,382,8217],\"iso-8859-14\":[128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,7682,7683,163,266,267,7690,167,7808,169,7810,7691,7922,173,174,376,7710,7711,288,289,7744,7745,182,7766,7809,7767,7811,7776,7923,7812,7813,7777,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,372,209,210,211,212,213,214,7786,216,217,218,219,220,221,374,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,373,241,242,243,244,245,246,7787,248,249,250,251,252,253,375,255],\"iso-8859-15\":[128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,8364,165,352,167,353,169,170,171,172,173,174,175,176,177,178,179,381,181,182,183,382,185,186,187,338,339,376,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255],\"iso-8859-16\":[128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,260,261,321,8364,8222,352,167,353,169,536,171,377,173,378,379,176,177,268,322,381,8221,182,183,382,269,537,187,338,339,376,380,192,193,194,258,196,262,198,199,200,201,202,203,204,205,206,207,272,323,210,211,212,336,214,346,368,217,218,219,220,280,538,223,224,225,226,259,228,263,230,231,232,233,234,235,236,237,238,239,273,324,242,243,244,337,246,347,369,249,250,251,252,281,539,255],\"koi8-r\":[9472,9474,9484,9488,9492,9496,9500,9508,9516,9524,9532,9600,9604,9608,9612,9616,9617,9618,9619,8992,9632,8729,8730,8776,8804,8805,160,8993,176,178,183,247,9552,9553,9554,1105,9555,9556,9557,9558,9559,9560,9561,9562,9563,9564,9565,9566,9567,9568,9569,1025,9570,9571,9572,9573,9574,9575,9576,9577,9578,9579,9580,169,1102,1072,1073,1094,1076,1077,1092,1075,1093,1080,1081,1082,1083,1084,1085,1086,1087,1103,1088,1089,1090,1091,1078,1074,1100,1099,1079,1096,1101,1097,1095,1098,1070,1040,1041,1062,1044,1045,1060,1043,1061,1048,1049,1050,1051,1052,1053,1054,1055,1071,1056,1057,1058,1059,1046,1042,1068,1067,1047,1064,1069,1065,1063,1066],\"koi8-u\":[9472,9474,9484,9488,9492,9496,9500,9508,9516,9524,9532,9600,9604,9608,9612,9616,9617,9618,9619,8992,9632,8729,8730,8776,8804,8805,160,8993,176,178,183,247,9552,9553,9554,1105,1108,9556,1110,1111,9559,9560,9561,9562,9563,1169,1118,9566,9567,9568,9569,1025,1028,9571,1030,1031,9574,9575,9576,9577,9578,1168,1038,169,1102,1072,1073,1094,1076,1077,1092,1075,1093,1080,1081,1082,1083,1084,1085,1086,1087,1103,1088,1089,1090,1091,1078,1074,1100,1099,1079,1096,1101,1097,1095,1098,1070,1040,1041,1062,1044,1045,1060,1043,1061,1048,1049,1050,1051,1052,1053,1054,1055,1071,1056,1057,1058,1059,1046,1042,1068,1067,1047,1064,1069,1065,1063,1066],macintosh:[196,197,199,201,209,214,220,225,224,226,228,227,229,231,233,232,234,235,237,236,238,239,241,243,242,244,246,245,250,249,251,252,8224,176,162,163,167,8226,182,223,174,169,8482,180,168,8800,198,216,8734,177,8804,8805,165,181,8706,8721,8719,960,8747,170,186,937,230,248,191,161,172,8730,402,8776,8710,171,187,8230,160,192,195,213,338,339,8211,8212,8220,8221,8216,8217,247,9674,255,376,8260,8364,8249,8250,64257,64258,8225,183,8218,8222,8240,194,202,193,203,200,205,206,207,204,211,212,63743,210,218,219,217,305,710,732,175,728,729,730,184,733,731,711],\"windows-874\":[8364,129,130,131,132,8230,134,135,136,137,138,139,140,141,142,143,144,8216,8217,8220,8221,8226,8211,8212,152,153,154,155,156,157,158,159,160,3585,3586,3587,3588,3589,3590,3591,3592,3593,3594,3595,3596,3597,3598,3599,3600,3601,3602,3603,3604,3605,3606,3607,3608,3609,3610,3611,3612,3613,3614,3615,3616,3617,3618,3619,3620,3621,3622,3623,3624,3625,3626,3627,3628,3629,3630,3631,3632,3633,3634,3635,3636,3637,3638,3639,3640,3641,3642,null,null,null,null,3647,3648,3649,3650,3651,3652,3653,3654,3655,3656,3657,3658,3659,3660,3661,3662,3663,3664,3665,3666,3667,3668,3669,3670,3671,3672,3673,3674,3675,null,null,null,null],\"windows-1250\":[8364,129,8218,131,8222,8230,8224,8225,136,8240,352,8249,346,356,381,377,144,8216,8217,8220,8221,8226,8211,8212,152,8482,353,8250,347,357,382,378,160,711,728,321,164,260,166,167,168,169,350,171,172,173,174,379,176,177,731,322,180,181,182,183,184,261,351,187,317,733,318,380,340,193,194,258,196,313,262,199,268,201,280,203,282,205,206,270,272,323,327,211,212,336,214,215,344,366,218,368,220,221,354,223,341,225,226,259,228,314,263,231,269,233,281,235,283,237,238,271,273,324,328,243,244,337,246,247,345,367,250,369,252,253,355,729],\"windows-1251\":[1026,1027,8218,1107,8222,8230,8224,8225,8364,8240,1033,8249,1034,1036,1035,1039,1106,8216,8217,8220,8221,8226,8211,8212,152,8482,1113,8250,1114,1116,1115,1119,160,1038,1118,1032,164,1168,166,167,1025,169,1028,171,172,173,174,1031,176,177,1030,1110,1169,181,182,183,1105,8470,1108,187,1112,1029,1109,1111,1040,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103],\"windows-1252\":[8364,129,8218,402,8222,8230,8224,8225,710,8240,352,8249,338,141,381,143,144,8216,8217,8220,8221,8226,8211,8212,732,8482,353,8250,339,157,382,376,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255],\"windows-1253\":[8364,129,8218,402,8222,8230,8224,8225,136,8240,138,8249,140,141,142,143,144,8216,8217,8220,8221,8226,8211,8212,152,8482,154,8250,156,157,158,159,160,901,902,163,164,165,166,167,168,169,null,171,172,173,174,8213,176,177,178,179,900,181,182,183,904,905,906,187,908,189,910,911,912,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,null,931,932,933,934,935,936,937,938,939,940,941,942,943,944,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,962,963,964,965,966,967,968,969,970,971,972,973,974,null],\"windows-1254\":[8364,129,8218,402,8222,8230,8224,8225,710,8240,352,8249,338,141,142,143,144,8216,8217,8220,8221,8226,8211,8212,732,8482,353,8250,339,157,158,376,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,286,209,210,211,212,213,214,215,216,217,218,219,220,304,350,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,287,241,242,243,244,245,246,247,248,249,250,251,252,305,351,255],\"windows-1255\":[8364,129,8218,402,8222,8230,8224,8225,710,8240,138,8249,140,141,142,143,144,8216,8217,8220,8221,8226,8211,8212,732,8482,154,8250,156,157,158,159,160,161,162,163,8362,165,166,167,168,169,215,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,247,187,188,189,190,191,1456,1457,1458,1459,1460,1461,1462,1463,1464,1465,1466,1467,1468,1469,1470,1471,1472,1473,1474,1475,1520,1521,1522,1523,1524,null,null,null,null,null,null,null,1488,1489,1490,1491,1492,1493,1494,1495,1496,1497,1498,1499,1500,1501,1502,1503,1504,1505,1506,1507,1508,1509,1510,1511,1512,1513,1514,null,null,8206,8207,null],\"windows-1256\":[8364,1662,8218,402,8222,8230,8224,8225,710,8240,1657,8249,338,1670,1688,1672,1711,8216,8217,8220,8221,8226,8211,8212,1705,8482,1681,8250,339,8204,8205,1722,160,1548,162,163,164,165,166,167,168,169,1726,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,1563,187,188,189,190,1567,1729,1569,1570,1571,1572,1573,1574,1575,1576,1577,1578,1579,1580,1581,1582,1583,1584,1585,1586,1587,1588,1589,1590,215,1591,1592,1593,1594,1600,1601,1602,1603,224,1604,226,1605,1606,1607,1608,231,232,233,234,235,1609,1610,238,239,1611,1612,1613,1614,244,1615,1616,247,1617,249,1618,251,252,8206,8207,1746],\"windows-1257\":[8364,129,8218,131,8222,8230,8224,8225,136,8240,138,8249,140,168,711,184,144,8216,8217,8220,8221,8226,8211,8212,152,8482,154,8250,156,175,731,159,160,null,162,163,164,null,166,167,216,169,342,171,172,173,174,198,176,177,178,179,180,181,182,183,248,185,343,187,188,189,190,230,260,302,256,262,196,197,280,274,268,201,377,278,290,310,298,315,352,323,325,211,332,213,214,215,370,321,346,362,220,379,381,223,261,303,257,263,228,229,281,275,269,233,378,279,291,311,299,316,353,324,326,243,333,245,246,247,371,322,347,363,252,380,382,729],\"windows-1258\":[8364,129,8218,402,8222,8230,8224,8225,710,8240,138,8249,338,141,142,143,144,8216,8217,8220,8221,8226,8211,8212,732,8482,154,8250,339,157,158,376,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,258,196,197,198,199,200,201,202,203,768,205,206,207,272,209,777,211,212,416,214,215,216,217,218,219,220,431,771,223,224,225,226,259,228,229,230,231,232,233,234,235,769,237,238,239,273,241,803,243,244,417,246,247,248,249,250,251,252,432,8363,255],\"x-mac-cyrillic\":[1040,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,8224,176,1168,163,167,8226,182,1030,174,169,8482,1026,1106,8800,1027,1107,8734,177,8804,8805,1110,181,1169,1032,1028,1108,1031,1111,1033,1113,1034,1114,1112,1029,172,8730,402,8776,8710,171,187,8230,160,1035,1115,1036,1116,1109,8211,8212,8220,8221,8216,8217,247,8222,1038,1118,1039,1119,8470,1025,1105,1103,1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,8364]}})(this||{})},{}],4:[function(e,n){(function(d){'use strict';function i(e,n,d){return n<=e&&e<=d}function f(e,n){return-1!==e.indexOf(n)}function u(e){if(e===void 0)return{};if(e===Object(e))return e;throw TypeError(\"Could not convert argument to dictionary\")}function o(e){for(var f=e+\"\",o=f.length,n=0,s=[];n<o;){var u=f.charCodeAt(n);if(55296>u||57343<u)s.push(u);else if(56320<=u&&57343>=u)s.push(65533);else if(55296<=u&&56319>=u)if(n===o-1)s.push(65533);else{var a=f.charCodeAt(n+1);if(56320<=a&&57343>=a){s.push(65536+((1023&u)<<10)+(1023&a)),n+=1}else s.push(65533)}n+=1}return s}function s(e){for(var n=String.fromCharCode,d=\"\",f=0,u;f<e.length;++f)u=e[f],65535>=u?d+=n(u):(u-=65536,d+=n((u>>10)+55296,(1023&u)+56320));return d}function a(e){return 0<=e&&127>=e}function t(e){this.tokens=[].slice.call(e),this.tokens.reverse()}function r(e,n){if(e)throw TypeError(\"Decoder error\");return n||65533}function c(e){throw TypeError(\"The code point \"+e+\" could not be encoded.\")}function l(e){return e=(e+\"\").trim().toLowerCase(),Object.prototype.hasOwnProperty.call(G,e)?G[e]:null}function p(e,n){return n?n[e]||null:null}function m(e,n){var d=n.indexOf(e);return-1===d?null:d}function g(e){if(!(\"encoding-indexes\"in d))throw Error(\"Indexes missing. Did you forget to include encoding-indexes.js first?\");return d[\"encoding-indexes\"][e]}function h(e){if(39419<e&&189e3>e||1237575<e)return null;if(7457===e)return 59335;var n=0,d=0,f=g(\"gb18030-ranges\"),u;for(u=0;u<f.length;++u){var o=f[u];if(o[0]<=e)n=o[0],d=o[1];else break}return d+e-n}function y(e){if(59335===e)return 7457;var n=0,d=0,f=g(\"gb18030-ranges\"),u;for(u=0;u<f.length;++u){var o=f[u];if(o[1]<=e)n=o[1],d=o[0];else break}return d+e-n}function w(e){$=$||g(\"jis0208\").map(function(e,n){return i(n,8272,8835)?null:e});var n=$;return n.indexOf(e)}function _(e){Y=Y||g(\"big5\").map(function(e,n){return n<5024?null:e});var n=Y;return 9552===e||9566===e||9569===e||9578===e||21313===e||21317===e?n.lastIndexOf(e):m(e,n)}function b(e,n){if(!(this instanceof b))throw TypeError(\"Called as a function. Did you forget 'new'?\");e=void 0===e?\"utf-8\":e+\"\",n=u(n),this._encoding=null,this._decoder=null,this._ignoreBOM=!1,this._BOMseen=!1,this._error_mode=\"replacement\",this._do_not_flush=!1;var d=l(e);if(null===d||\"replacement\"===d.name)throw RangeError(\"Unknown encoding: \"+e);if(!Q[d.name])throw Error(\"Decoder not present. Did you forget to include encoding-indexes.js first?\");var i=this;return i._encoding=d,!!n.fatal&&(i._error_mode=\"fatal\"),!!n.ignoreBOM&&(i._ignoreBOM=!0),Object.defineProperty||(this.encoding=i._encoding.name.toLowerCase(),this.fatal=\"fatal\"===i._error_mode,this.ignoreBOM=i._ignoreBOM),i}function E(e,n){if(!(this instanceof E))throw TypeError(\"Called as a function. Did you forget 'new'?\");n=u(n),this._encoding=null,this._encoder=null,this._do_not_flush=!1,this._fatal=!n.fatal?\"replacement\":\"fatal\";var i=this;if(!!n.NONSTANDARD_allowLegacyEncoding){e=void 0===e?\"utf-8\":e+\"\";var f=l(e);if(null===f||\"replacement\"===f.name)throw RangeError(\"Unknown encoding: \"+e);if(!X[f.name])throw Error(\"Encoder not present. Did you forget to include encoding-indexes.js first?\");i._encoding=f}else i._encoding=l(\"utf-8\"),void 0!==e&&\"console\"in d&&console.warn(\"TextEncoder constructor called with encoding label, which is ignored.\");return Object.defineProperty||(this.encoding=i._encoding.name.toLowerCase()),i}function v(e){var n=e.fatal,d=0,f=0,u=0,o=128,s=191;this.handler=function(e,a){if(-1===a&&0!==u)return u=0,r(n);if(-1===a)return-1;if(0===u){if(i(a,0,127))return a;if(i(a,194,223))u=1,d=31&a;else if(i(a,224,239))224===a&&(o=160),237===a&&(s=159),u=2,d=15&a;else if(i(a,240,244))240===a&&(o=144),244===a&&(s=143),u=3,d=7&a;else return r(n);return null}if(!i(a,o,s))return d=u=f=0,o=128,s=191,e.prepend(a),r(n);if(o=128,s=191,d=d<<6|63&a,f+=1,f!==u)return null;var t=d;return d=u=f=0,t}}function O(e){e.fatal;this.handler=function(e,n){if(n===-1)return-1;if(z(n))return n;var d,f;i(n,128,2047)?(d=1,f=192):i(n,2048,65535)?(d=2,f=224):i(n,65536,1114111)&&(d=3,f=240);for(var u=[(n>>6*d)+f];0<d;){var o=n>>6*(d-1);u.push(128|63&o),d-=1}return u}}function I(e,n){var d=n.fatal;this.handler=function(n,i){if(i===-1)return-1;if(a(i))return i;var f=e[i-128];return null===f?r(d):f}}function k(e,n){n.fatal;this.handler=function(n,d){if(-1===d)return-1;if(z(d))return d;var i=m(d,e);return null===i&&c(d),i+128}}function x(e){var n=e.fatal,d=0,f=0,u=0;this.handler=function(e,o){if(o===-1&&0===d&&0===f&&0===u)return-1;o===-1&&(0!==d||0!==f||0!==u)&&(d=0,f=0,u=0,r(n));var s;if(0!==u){s=null,i(o,48,57)&&(s=h(10*(126*(10*(d-129)+f-48)+u-129)+o-48));var t=[f,u,o];return d=0,f=0,u=0,null===s?(e.prepend(t),r(n)):s}if(0!==f)return i(o,129,254)?(u=o,null):(e.prepend([f,o]),d=0,f=0,r(n));if(0!==d){if(i(o,48,57))return f=o,null;var c=d,l=null;d=0;var m=127>o?64:65;return(i(o,64,126)||i(o,128,254))&&(l=190*(c-129)+(o-m)),s=null===l?null:p(l,g(\"gb18030\")),null===s&&a(o)&&e.prepend(o),null===s?r(n):s}return a(o)?o:128===o?8364:i(o,129,254)?(d=o,null):r(n)}}function N(e,n){e.fatal;this.handler=function(e,d){if(d===-1)return-1;if(z(d))return d;if(58853===d)return c(d);if(n&&8364===d)return 128;var i=m(d,g(\"gb18030\"));if(null!==i){var f=W(i/190)+129,u=i%190,o=63>u?64:65;return[f,u+o]}if(n)return c(d);i=y(d);var s=W(i/10/126/10);i-=10*(126*(10*s));var a=W(i/10/126);i-=126*(10*a);var t=W(i/10),r=i-10*t;return[s+129,a+48,t+129,r+48]}}function C(e){var n=e.fatal,d=0;this.handler=function(e,f){if(-1===f&&0!==d)return d=0,r(n);if(-1===f&&0===d)return-1;if(0!==d){var u=d,o=null;d=0;var s=127>f?64:98;switch((i(f,64,126)||i(f,161,254))&&(o=157*(u-129)+(f-s)),o){case 1133:return[202,772];case 1135:return[202,780];case 1164:return[234,772];case 1166:return[234,780];}var t=null===o?null:p(o,g(\"big5\"));return null===t&&a(f)&&e.prepend(f),null===t?r(n):t}return a(f)?f:i(f,129,254)?(d=f,null):r(n)}}function R(e){e.fatal;this.handler=function(e,n){if(n===-1)return-1;if(z(n))return n;var d=_(n);if(null===d)return c(n);var i=W(d/157)+129;if(161>i)return c(n);var f=d%157,u=63>f?64:98;return[i,f+u]}}function S(e){var n=e.fatal,d=!1,f=0;this.handler=function(e,u){if(-1===u&&0!==f)return f=0,r(n);if(-1===u&&0===f)return-1;if(142===f&&i(u,161,223))return f=0,65216+u;if(143===f&&i(u,161,254))return d=!0,f=u,null;if(0!==f){var o=f;f=0;var s=null;return i(o,161,254)&&i(u,161,254)&&(s=p(94*(o-161)+(u-161),g(d?\"jis0212\":\"jis0208\"))),d=!1,i(u,161,254)||e.prepend(u),null===s?r(n):s}return a(u)?u:142===u||143===u||i(u,161,254)?(f=u,null):r(n)}}function L(e){e.fatal;this.handler=function(e,n){if(n===-1)return-1;if(z(n))return n;if(165===n)return 92;if(8254===n)return 126;if(i(n,65377,65439))return[142,n-65377+161];8722===n&&(n=65293);var d=m(n,g(\"jis0208\"));if(null===d)return c(n);var f=W(d/94)+161;return[f,d%94+161]}}function B(e){var n=e.fatal,d={ASCII:0,Roman:1,Katakana:2,LeadByte:3,TrailByte:4,EscapeStart:5,Escape:6},f=d.ASCII,u=d.ASCII,o=0,s=!1;this.handler=function(e,a){switch(f){default:case d.ASCII:return 27===a?(f=d.EscapeStart,null):i(a,0,127)&&14!==a&&15!==a&&27!==a?(s=!1,a):-1===a?-1:(s=!1,r(n));case d.Roman:return 27===a?(f=d.EscapeStart,null):92===a?(s=!1,165):126===a?(s=!1,8254):i(a,0,127)&&14!==a&&15!==a&&27!==a&&92!==a&&126!==a?(s=!1,a):-1===a?-1:(s=!1,r(n));case d.Katakana:return 27===a?(f=d.EscapeStart,null):i(a,33,95)?(s=!1,65344+a):-1===a?-1:(s=!1,r(n));case d.LeadByte:return 27===a?(f=d.EscapeStart,null):i(a,33,126)?(s=!1,o=a,f=d.TrailByte,null):-1===a?-1:(s=!1,r(n));case d.TrailByte:if(27===a)return f=d.EscapeStart,r(n);if(i(a,33,126)){f=d.LeadByte;var t=94*(o-33)+a-33,c=p(t,g(\"jis0208\"));return null===c?r(n):c}return-1===a?(f=d.LeadByte,e.prepend(a),r(n)):(f=d.LeadByte,r(n));case d.EscapeStart:return 36===a||40===a?(o=a,f=d.Escape,null):(e.prepend(a),s=!1,f=u,r(n));case d.Escape:var l=o;o=0;var m=null;if(40===l&&66===a&&(m=d.ASCII),40===l&&74===a&&(m=d.Roman),40===l&&73===a&&(m=d.Katakana),36===l&&(64===a||66===a)&&(m=d.LeadByte),null!==m){f=f=m;var h=s;return s=!0,h?r(n):null}return e.prepend([l,a]),s=!1,f=u,r(n);}}}function j(e){var n=e.fatal,d={ASCII:0,Roman:1,jis0208:2},i=d.ASCII;this.handler=function(e,n){if(-1===n&&i!==d.ASCII)return e.prepend(n),i=d.ASCII,[27,40,66];if(-1===n&&i===d.ASCII)return-1;if((i===d.ASCII||i===d.Roman)&&(14===n||15===n||27===n))return c(65533);if(i===d.ASCII&&z(n))return n;if(i===d.Roman&&(z(n)&&92!==n&&126!==n||165==n||8254==n)){if(z(n))return n;if(165===n)return 92;if(8254===n)return 126}if(z(n)&&i!==d.ASCII)return e.prepend(n),i=d.ASCII,[27,40,66];if((165===n||8254===n)&&i!==d.Roman)return e.prepend(n),i=d.Roman,[27,40,74];8722===n&&(n=65293);var f=m(n,g(\"jis0208\"));if(null===f)return c(n);if(i!==d.jis0208)return e.prepend(n),i=d.jis0208,[27,36,66];var u=W(f/94)+33;return[u,f%94+33]}}function A(e){var n=e.fatal,d=0;this.handler=function(e,f){if(-1===f&&0!==d)return d=0,r(n);if(-1===f&&0===d)return-1;if(0!==d){var u=d,o=null;d=0;var s=127>f?64:65,t=160>u?129:193;if((i(f,64,126)||i(f,128,252))&&(o=188*(u-t)+f-s),i(o,8836,10715))return 48508+o;var c=null===o?null:p(o,g(\"jis0208\"));return null===c&&a(f)&&e.prepend(f),null===c?r(n):c}return a(f)||128===f?f:i(f,161,223)?65216+f:i(f,129,159)||i(f,224,252)?(d=f,null):r(n)}}function T(e){e.fatal;this.handler=function(e,n){if(n===-1)return-1;if(z(n)||128===n)return n;if(165===n)return 92;if(8254===n)return 126;if(i(n,65377,65439))return n-65377+161;8722===n&&(n=65293);var d=w(n);if(null===d)return c(n);var f=W(d/188),u=31>f?129:193,o=d%188,s=63>o?64:65;return[f+u,o+s]}}function M(e){var n=e.fatal,d=0;this.handler=function(e,f){if(-1===f&&0!==d)return d=0,r(n);if(-1===f&&0===d)return-1;if(0!==d){var u=d,o=null;d=0,i(f,65,254)&&(o=190*(u-129)+(f-65));var s=null===o?null:p(o,g(\"euc-kr\"));return null===o&&a(f)&&e.prepend(f),null===s?r(n):s}return a(f)?f:i(f,129,254)?(d=f,null):r(n)}}function D(e){e.fatal;this.handler=function(e,n){if(n===-1)return-1;if(z(n))return n;var d=m(n,g(\"euc-kr\"));if(null===d)return c(n);var i=W(d/190)+129;return[i,d%190+65]}}function J(e,n){var d=e>>8,i=255&e;return n?[d,i]:[i,d]}function U(e,n){var d=n.fatal,f=null,u=null;this.handler=function(n,o){if(-1===o&&(null!==f||null!==u))return r(d);if(-1===o&&null===f&&null===u)return-1;if(null===f)return f=o,null;var s;if(s=e?(f<<8)+o:(o<<8)+f,f=null,null!==u){var a=u;return(u=null,i(s,56320,57343))?65536+1024*(a-55296)+(s-56320):(n.prepend(J(s,e)),r(d))}return i(s,55296,56319)?(u=s,null):i(s,56320,57343)?r(d):s}}function P(e,n){n.fatal;this.handler=function(n,d){if(d===-1)return-1;if(i(d,0,65535))return J(d,e);var f=J((d-65536>>10)+55296,e),u=J((1023&d-65536)+56320,e);return f.concat(u)}}function F(e){e.fatal;this.handler=function(e,n){return-1===n?-1:a(n)?n:63360+n-128}}function K(e){e.fatal;this.handler=function(e,n){return-1===n?-1:z(n)?n:i(n,63360,63487)?n-63360+128:c(n)}}\"undefined\"!=typeof n&&n.exports&&!d[\"encoding-indexes\"]&&(d[\"encoding-indexes\"]=e(\"./encoding-indexes.js\")[\"encoding-indexes\"]);var W=Math.floor,z=a,H=-1;t.prototype={endOfStream:function(){return!this.tokens.length},read:function(){return this.tokens.length?this.tokens.pop():H},prepend:function(e){if(Array.isArray(e))for(var n=e;n.length;)this.tokens.push(n.pop());else this.tokens.push(e)},push:function(e){if(Array.isArray(e))for(var n=e;n.length;)this.tokens.unshift(n.shift());else this.tokens.unshift(e)}};(function(){}).prototype={handler:function(){}},function(){}.prototype={handler:function(){}};var q=[{encodings:[{labels:[\"unicode-1-1-utf-8\",\"utf-8\",\"utf8\"],name:\"UTF-8\"}],heading:\"The Encoding\"},{encodings:[{labels:[\"866\",\"cp866\",\"csibm866\",\"ibm866\"],name:\"IBM866\"},{labels:[\"csisolatin2\",\"iso-8859-2\",\"iso-ir-101\",\"iso8859-2\",\"iso88592\",\"iso_8859-2\",\"iso_8859-2:1987\",\"l2\",\"latin2\"],name:\"ISO-8859-2\"},{labels:[\"csisolatin3\",\"iso-8859-3\",\"iso-ir-109\",\"iso8859-3\",\"iso88593\",\"iso_8859-3\",\"iso_8859-3:1988\",\"l3\",\"latin3\"],name:\"ISO-8859-3\"},{labels:[\"csisolatin4\",\"iso-8859-4\",\"iso-ir-110\",\"iso8859-4\",\"iso88594\",\"iso_8859-4\",\"iso_8859-4:1988\",\"l4\",\"latin4\"],name:\"ISO-8859-4\"},{labels:[\"csisolatincyrillic\",\"cyrillic\",\"iso-8859-5\",\"iso-ir-144\",\"iso8859-5\",\"iso88595\",\"iso_8859-5\",\"iso_8859-5:1988\"],name:\"ISO-8859-5\"},{labels:[\"arabic\",\"asmo-708\",\"csiso88596e\",\"csiso88596i\",\"csisolatinarabic\",\"ecma-114\",\"iso-8859-6\",\"iso-8859-6-e\",\"iso-8859-6-i\",\"iso-ir-127\",\"iso8859-6\",\"iso88596\",\"iso_8859-6\",\"iso_8859-6:1987\"],name:\"ISO-8859-6\"},{labels:[\"csisolatingreek\",\"ecma-118\",\"elot_928\",\"greek\",\"greek8\",\"iso-8859-7\",\"iso-ir-126\",\"iso8859-7\",\"iso88597\",\"iso_8859-7\",\"iso_8859-7:1987\",\"sun_eu_greek\"],name:\"ISO-8859-7\"},{labels:[\"csiso88598e\",\"csisolatinhebrew\",\"hebrew\",\"iso-8859-8\",\"iso-8859-8-e\",\"iso-ir-138\",\"iso8859-8\",\"iso88598\",\"iso_8859-8\",\"iso_8859-8:1988\",\"visual\"],name:\"ISO-8859-8\"},{labels:[\"csiso88598i\",\"iso-8859-8-i\",\"logical\"],name:\"ISO-8859-8-I\"},{labels:[\"csisolatin6\",\"iso-8859-10\",\"iso-ir-157\",\"iso8859-10\",\"iso885910\",\"l6\",\"latin6\"],name:\"ISO-8859-10\"},{labels:[\"iso-8859-13\",\"iso8859-13\",\"iso885913\"],name:\"ISO-8859-13\"},{labels:[\"iso-8859-14\",\"iso8859-14\",\"iso885914\"],name:\"ISO-8859-14\"},{labels:[\"csisolatin9\",\"iso-8859-15\",\"iso8859-15\",\"iso885915\",\"iso_8859-15\",\"l9\"],name:\"ISO-8859-15\"},{labels:[\"iso-8859-16\"],name:\"ISO-8859-16\"},{labels:[\"cskoi8r\",\"koi\",\"koi8\",\"koi8-r\",\"koi8_r\"],name:\"KOI8-R\"},{labels:[\"koi8-ru\",\"koi8-u\"],name:\"KOI8-U\"},{labels:[\"csmacintosh\",\"mac\",\"macintosh\",\"x-mac-roman\"],name:\"macintosh\"},{labels:[\"dos-874\",\"iso-8859-11\",\"iso8859-11\",\"iso885911\",\"tis-620\",\"windows-874\"],name:\"windows-874\"},{labels:[\"cp1250\",\"windows-1250\",\"x-cp1250\"],name:\"windows-1250\"},{labels:[\"cp1251\",\"windows-1251\",\"x-cp1251\"],name:\"windows-1251\"},{labels:[\"ansi_x3.4-1968\",\"ascii\",\"cp1252\",\"cp819\",\"csisolatin1\",\"ibm819\",\"iso-8859-1\",\"iso-ir-100\",\"iso8859-1\",\"iso88591\",\"iso_8859-1\",\"iso_8859-1:1987\",\"l1\",\"latin1\",\"us-ascii\",\"windows-1252\",\"x-cp1252\"],name:\"windows-1252\"},{labels:[\"cp1253\",\"windows-1253\",\"x-cp1253\"],name:\"windows-1253\"},{labels:[\"cp1254\",\"csisolatin5\",\"iso-8859-9\",\"iso-ir-148\",\"iso8859-9\",\"iso88599\",\"iso_8859-9\",\"iso_8859-9:1989\",\"l5\",\"latin5\",\"windows-1254\",\"x-cp1254\"],name:\"windows-1254\"},{labels:[\"cp1255\",\"windows-1255\",\"x-cp1255\"],name:\"windows-1255\"},{labels:[\"cp1256\",\"windows-1256\",\"x-cp1256\"],name:\"windows-1256\"},{labels:[\"cp1257\",\"windows-1257\",\"x-cp1257\"],name:\"windows-1257\"},{labels:[\"cp1258\",\"windows-1258\",\"x-cp1258\"],name:\"windows-1258\"},{labels:[\"x-mac-cyrillic\",\"x-mac-ukrainian\"],name:\"x-mac-cyrillic\"}],heading:\"Legacy single-byte encodings\"},{encodings:[{labels:[\"chinese\",\"csgb2312\",\"csiso58gb231280\",\"gb2312\",\"gb_2312\",\"gb_2312-80\",\"gbk\",\"iso-ir-58\",\"x-gbk\"],name:\"GBK\"},{labels:[\"gb18030\"],name:\"gb18030\"}],heading:\"Legacy multi-byte Chinese (simplified) encodings\"},{encodings:[{labels:[\"big5\",\"big5-hkscs\",\"cn-big5\",\"csbig5\",\"x-x-big5\"],name:\"Big5\"}],heading:\"Legacy multi-byte Chinese (traditional) encodings\"},{encodings:[{labels:[\"cseucpkdfmtjapanese\",\"euc-jp\",\"x-euc-jp\"],name:\"EUC-JP\"},{labels:[\"csiso2022jp\",\"iso-2022-jp\"],name:\"ISO-2022-JP\"},{labels:[\"csshiftjis\",\"ms932\",\"ms_kanji\",\"shift-jis\",\"shift_jis\",\"sjis\",\"windows-31j\",\"x-sjis\"],name:\"Shift_JIS\"}],heading:\"Legacy multi-byte Japanese encodings\"},{encodings:[{labels:[\"cseuckr\",\"csksc56011987\",\"euc-kr\",\"iso-ir-149\",\"korean\",\"ks_c_5601-1987\",\"ks_c_5601-1989\",\"ksc5601\",\"ksc_5601\",\"windows-949\"],name:\"EUC-KR\"}],heading:\"Legacy multi-byte Korean encodings\"},{encodings:[{labels:[\"csiso2022kr\",\"hz-gb-2312\",\"iso-2022-cn\",\"iso-2022-cn-ext\",\"iso-2022-kr\"],name:\"replacement\"},{labels:[\"utf-16be\"],name:\"UTF-16BE\"},{labels:[\"utf-16\",\"utf-16le\"],name:\"UTF-16LE\"},{labels:[\"x-user-defined\"],name:\"x-user-defined\"}],heading:\"Legacy miscellaneous encodings\"}],G={};q.forEach(function(e){e.encodings.forEach(function(e){e.labels.forEach(function(n){G[n]=e})})});var X={},Q={},$,Y;Object.defineProperty&&(Object.defineProperty(b.prototype,\"encoding\",{get:function(){return this._encoding.name.toLowerCase()}}),Object.defineProperty(b.prototype,\"fatal\",{get:function(){return\"fatal\"===this._error_mode}}),Object.defineProperty(b.prototype,\"ignoreBOM\",{get:function(){return this._ignoreBOM}})),b.prototype.decode=function(e,n){var d;d=\"object\"==typeof e&&e instanceof ArrayBuffer?new Uint8Array(e):\"object\"==typeof e&&\"buffer\"in e&&e.buffer instanceof ArrayBuffer?new Uint8Array(e.buffer,e.byteOffset,e.byteLength):new Uint8Array(0),n=u(n),this._do_not_flush||(this._decoder=Q[this._encoding.name]({fatal:\"fatal\"===this._error_mode}),this._BOMseen=!1),this._do_not_flush=!!n.stream;for(var i=new t(d),o=[],a,r;(r=i.read(),r!==H)&&!(a=this._decoder.handler(i,r),-1===a);)null!==a&&(Array.isArray(a)?o.push.apply(o,a):o.push(a));if(!this._do_not_flush){do{if(a=this._decoder.handler(i,i.read()),-1===a)break;if(null===a)continue;Array.isArray(a)?o.push.apply(o,a):o.push(a)}while(!i.endOfStream());this._decoder=null}return function(e){return!f([\"UTF-8\",\"UTF-16LE\",\"UTF-16BE\"],this._encoding.name)||this._ignoreBOM||this._BOMseen||(0<e.length&&65279===e[0]?(this._BOMseen=!0,e.shift()):0<e.length&&(this._BOMseen=!0)),s(e)}.call(this,o)},Object.defineProperty&&Object.defineProperty(E.prototype,\"encoding\",{get:function(){return this._encoding.name.toLowerCase()}}),E.prototype.encode=function(e,n){e=e===void 0?\"\":e+\"\",n=u(n),this._do_not_flush||(this._encoder=X[this._encoding.name]({fatal:\"fatal\"===this._fatal})),this._do_not_flush=!!n.stream;for(var d=new t(o(e)),i=[],f,s;(s=d.read(),s!==H)&&!(f=this._encoder.handler(d,s),-1===f);)Array.isArray(f)?i.push.apply(i,f):i.push(f);if(!this._do_not_flush){for(;f=this._encoder.handler(d,d.read()),-1!==f;)Array.isArray(f)?i.push.apply(i,f):i.push(f);this._encoder=null}return new Uint8Array(i)},X[\"UTF-8\"]=function(e){return new O(e)},Q[\"UTF-8\"]=function(e){return new v(e)},function(){\"encoding-indexes\"in d&&q.forEach(function(e){\"Legacy single-byte encodings\"!==e.heading||e.encodings.forEach(function(e){var n=e.name,d=g(n.toLowerCase());Q[n]=function(e){return new I(d,e)},X[n]=function(e){return new k(d,e)}})})}(),Q.GBK=function(e){return new x(e)},X.GBK=function(e){return new N(e,!0)},X.gb18030=function(e){return new N(e)},Q.gb18030=function(e){return new x(e)},X.Big5=function(e){return new R(e)},Q.Big5=function(e){return new C(e)},X[\"EUC-JP\"]=function(e){return new L(e)},Q[\"EUC-JP\"]=function(e){return new S(e)},X[\"ISO-2022-JP\"]=function(e){return new j(e)},Q[\"ISO-2022-JP\"]=function(e){return new B(e)},X.Shift_JIS=function(e){return new T(e)},Q.Shift_JIS=function(e){return new A(e)},X[\"EUC-KR\"]=function(e){return new D(e)},Q[\"EUC-KR\"]=function(e){return new M(e)},X[\"UTF-16BE\"]=function(e){return new P(!0,e)},Q[\"UTF-16BE\"]=function(e){return new U(!0,e)},X[\"UTF-16LE\"]=function(e){return new P(!1,e)},Q[\"UTF-16LE\"]=function(e){return new U(!1,e)},X[\"x-user-defined\"]=function(e){return new K(e)},Q[\"x-user-defined\"]=function(e){return new F(e)},d.TextEncoder||(d.TextEncoder=E),d.TextDecoder||(d.TextDecoder=b),\"undefined\"!=typeof n&&n.exports&&(n.exports={TextEncoder:d.TextEncoder,TextDecoder:d.TextDecoder,EncodingIndexes:d[\"encoding-indexes\"]})})(this||{})},{\"./encoding-indexes.js\":3}],5:[function(e,n,d){(function(i,f){function u(e){return!(\"_OnNamespaceConnect\"!==e&&\"_OnNamespaceConnected\"!==e&&\"_OnNamespaceDisconnect\"!==e&&\"_OnRoomJoin\"!==e&&\"_OnRoomJoined\"!==e&&\"_OnRoomLeave\"!==e&&\"_OnRoomLeft\"!==e)}function o(e){return!(void 0!==e)||!(null!==e)||(\"\"==e||\"string\"==typeof e||e instanceof String?0===e.length||\"\"===e:!!(e instanceof Error)&&o(e.message))}function s(e){return JSON.stringify(e)}function a(e){return o(e)?\"\":e.replace(F,\"@%!semicolon@%!\")}function t(e){return o(e)?\"\":e.replace(K,\";\")}function r(e){return new W(e)}function c(e){return e instanceof W}function l(e){if(e.IsNative&&o(e.wait))return e.Body;var n=\"0\",d=\"0\",i=e.Body||\"\";o(e.Err)||(i=e.Err.message,!c(e.Err)&&(n=\"1\")),e.isNoOp&&(d=\"1\");var f=[e.wait||\"\",a(e.Namespace),a(e.Room),a(e.Event),n,d,\"\"].join(\";\");if(e.SetBinary){var u=z.encode(f);f=new Uint8Array(u.length+i.length),f.set(u,0),f.set(i,u.length)}else i instanceof Uint8Array&&(i=H.decode(i,{stream:!1})),f+=i;return f}function p(e,n,d){if(0==d)return[e];var i=e.split(n,d);if(i.length==d){var f=i.join(n)+n;return i.push(e.substr(f.length)),i}return[e]}function m(e,n){var d=new U;if(0==e.length)return d.isInvalid=!0,d;var f=e instanceof ArrayBuffer,u;if(f){for(var s=new Uint8Array(e),a=1,r=0,c=0;c<s.length&&!(s[c]==q&&(a++,r=c,7==a));c++);if(7!=a)return d.isInvalid=!0,d;u=p(H.decode(s.slice(0,r),{stream:!1}),\";\",5),u.push(e.slice(r+1,e.length)),d.SetBinary=!0}else u=p(e,\";\",6);if(7!=u.length)return n?(d.Event=\"_OnNativeMessage\",d.Body=e):d.isInvalid=!0,d;d.wait=u[0],d.Namespace=t(u[1]),d.Room=t(u[2]),d.Event=t(u[3]),d.isError=\"1\"==u[4]||!1,d.isNoOp=\"1\"==u[5]||!1;var l=u[6];return o(l)?d.Body=\"\":d.isError?d.Err=new Error(l):d.Body=l,d.isInvalid=!1,d.IsForced=!1,d.IsLocal=!1,d.IsNative=n&&\"_OnNativeMessage\"==d.Event||!1,d}function g(){if(!S){var e=i.hrtime();return\"$\"+1e9*e[0]+e[1]}var n=window.performance.now();return\"$\"+n.toString()}function h(e){return e+\";\".repeat(6)}function y(e,n){return e.events.has(n.Event)?e.events.get(n.Event)(e,n):e.events.has(\"_OnAnyEvent\")?e.events.get(\"_OnAnyEvent\")(e,n):null}function w(e){return null===e||e===void 0||\"undefined\"==typeof e}function _(e,n){if(w(e))return w(n)||n(\"connHandler is empty.\"),null;var d=new Map,i=new Map,f=0;if(Object.keys(e).forEach(function(n){f++;var u=e[n];if(u instanceof Function)i.set(n,u);else if(u instanceof Map)d.set(n,u);else{var o=new Map;Object.keys(u).forEach(function(e){o.set(e,u[e])}),d.set(n,o)}}),0<i.size){if(f!=i.size)return w(n)||n(\"all keys of connHandler should be events, mix of namespaces and event callbacks is not supported \"+i.size+\" vs total \"+f),null;d.set(\"\",i)}return d}function b(e,n){return e.has(n)?e.get(n):null}function E(e,n){if(w(e))return n;for(var d in e)if(e.hasOwnProperty(d)){var i=e[d];d=encodeURIComponent(\"X-Websocket-Header-\"+d),i=encodeURIComponent(i);var f=d+\"=\"+i;n=-1==n.indexOf(\"?\")?-1==n.indexOf(\"#\")?n+\"?\"+f:n.split(\"#\")[0]+\"?\"+f+\"#\"+n.split(\"#\")[1]:n.split(\"?\")[0]+\"?\"+f+\"&\"+n.split(\"?\")[1]}return n}function v(e,n,d){return O(e,n,0,d)}function O(e,n,d,i){if(S&&0==e.indexOf(\"/\")){var f=\"https:\"==document.location.protocol?\"wss\":\"ws\",u=document.location.port?\":\"+document.location.port:\"\";e=f+\"://\"+document.location.hostname+u+e}return-1==e.indexOf(\"ws\")&&(e=\"ws://\"+e),new Promise(function(f,u){WebSocket||u(\"WebSocket is not accessible through this browser.\");var s=_(n,u);if(!w(s)){w(i)&&(i={}),w(i.headers)&&(i.headers={});var a=i.reconnect?i.reconnect:0;0<d&&0<a?i.headers[\"X-Websocket-Reconnect\"]=d.toString():!w(i.headers[\"X-Websocket-Reconnect\"])&&delete i.headers[\"X-Websocket-Reconnect\"];var t=I(e,i),r=new ee(t,s);r.reconnectTries=d,t.binaryType=\"arraybuffer\",t.onmessage=function(e){var n=r.handle(e);return o(n)?void(r.isAcknowledged()&&f(r)):void u(n)},t.onopen=function(){t.send(\"M\")},t.onerror=function(e){r.close(),u(e)},t.onclose=function(){if(r.isClosed());else{if(t.onmessage=void 0,t.onopen=void 0,t.onerror=void 0,t.onclose=void 0,0>=a)return r.close(),null;var d=new Map;r.connectedNamespaces.forEach(function(e,n){var i=[];!w(e.rooms)&&0<e.rooms.size&&e.rooms.forEach(function(e,n){i.push(n)}),d.set(n,i)}),r.close(),k(e,a,function(o){O(e,n,o,i).then(function(e){return w(f)||\"function () { [native code] }\"==f.toString()?void d.forEach(function(n,d){e.connect(d).then(function(e){return function(n){e.forEach(function(e){n.joinRoom(e)})}}(n))}):void f(e)}).catch(u)})}return null}}})}function I(e,n){return S&&!w(n)?(n.headers&&(e=E(n.headers,e)),n.protocols?new WebSocket(e,n.protocols):new WebSocket(e)):new WebSocket(e,n)}function k(e,n,d){var i=e.replace(/(ws)(s)?\\:\\/\\//,\"http$2://\"),f=1,u={method:\"HEAD\",mode:\"no-cors\"},o=function(){L(i,u).then(function(){d(f)}).catch(function(){f++,setTimeout(function(){o()},n)})};setTimeout(o,n)}function x(e){return!(!e||o(e.message))&&0<=e.message.indexOf(\"[-1] write closed\")}var N=this&&this.__extends||function(){var e=function(n,d){return e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,n){e.__proto__=n}||function(e,n){for(var d in n)n.hasOwnProperty(d)&&(e[d]=n[d])},e(n,d)};return function(n,d){function i(){this.constructor=n}e(n,d),n.prototype=null===d?Object.create(d):(i.prototype=d.prototype,new i)}}(),C=this&&this.__awaiter||function(e,n,d,i){function f(e){return e instanceof d?e:new d(function(n){n(e)})}return new(d||(d=Promise))(function(d,u){function o(e){try{a(i.next(e))}catch(n){u(n)}}function s(e){try{a(i[\"throw\"](e))}catch(n){u(n)}}function a(e){e.done?d(e.value):f(e.value).then(o,s)}a((i=i.apply(e,n||[])).next())})},R=this&&this.__generator||function(e,n){function d(e){return function(n){return i([e,n])}}function i(d){if(o)throw new TypeError(\"Generator is already executing.\");for(;u;)try{if(o=1,s&&(a=2&d[0]?s[\"return\"]:d[0]?s[\"throw\"]||((a=s[\"return\"])&&a.call(s),0):s.next)&&!(a=a.call(s,d[1])).done)return a;switch((s=0,a)&&(d=[2&d[0],a.value]),d[0]){case 0:case 1:a=d;break;case 4:return u.label++,{value:d[1],done:!1};case 5:u.label++,s=d[1],d=[0];continue;case 7:d=u.ops.pop(),u.trys.pop();continue;default:if((a=u.trys,!(a=0<a.length&&a[a.length-1]))&&(6===d[0]||2===d[0])){u=0;continue}if(3===d[0]&&(!a||d[1]>a[0]&&d[1]<a[3])){u.label=d[1];break}if(6===d[0]&&u.label<a[1]){u.label=a[1],a=d;break}if(a&&u.label<a[2]){u.label=a[2],u.ops.push(d);break}a[2]&&u.ops.pop(),u.trys.pop();continue;}d=n.call(e,u)}catch(n){d=[6,n],s=0}finally{o=a=0}if(5&d[0])throw d[1];return{value:d[0]?d[1]:void 0,done:!0}}var u={label:0,sent:function(){if(1&a[0])throw a[1];return a[1]},trys:[],ops:[]},o,s,a,r;return r={next:d(0),throw:d(1),return:d(2)},\"function\"==typeof Symbol&&(r[Symbol.iterator]=function(){return this}),r},S=\"undefined\"!=typeof window,L=\"undefined\"==typeof fetch?void 0:fetch;S?WebSocket=window.WebSocket:(WebSocket=e(\"ws\"),L=e(\"node-fetch\"),TextDecoder=e(\"@sinonjs/text-encoding\").TextDecoder,TextEncoder=e(\"@sinonjs/text-encoding\").TextEncoder);var B=\"_OnNamespaceConnected\",j=\"_OnNamespaceDisconnect\",A=\"_OnRoomJoin\",T=\"_OnRoomJoined\",M=\"_OnRoomLeave\",D=\"_OnRoomLeft\",J=\"_OnNativeMessage\",U=function(){function e(){}return e.prototype.isConnect=function(){return\"_OnNamespaceConnect\"==this.Event||!1},e.prototype.isDisconnect=function(){return this.Event==j||!1},e.prototype.isRoomJoin=function(){return this.Event==A||!1},e.prototype.isRoomLeft=function(){return this.Event==D||!1},e.prototype.isWait=function(){return!o(this.wait)&&(this.wait[0]==\"#\"||this.wait[0]==\"$\"||!1)},e.prototype.unmarshal=function(){return JSON.parse(this.Body)},e}(),P=\";\",F=new RegExp(P,\"g\"),K=new RegExp(\"@%!semicolon@%!\",\"g\"),W=function(e){function n(d){var i=e.call(this,d)||this;return i.name=\"replyError\",Error.captureStackTrace(i,n),Object.setPrototypeOf(i,n.prototype),i}return N(n,e),n}(Error),z=new TextEncoder,H=new TextDecoder(\"utf-8\"),q=P.charCodeAt(0),G=function(){function e(e,n){this.nsConn=e,this.name=n}return e.prototype.emit=function(e,n){var d=new U;return d.Namespace=this.nsConn.namespace,d.Room=this.name,d.Event=e,d.Body=n,this.nsConn.conn.write(d)},e.prototype.leave=function(){var e=new U;return e.Namespace=this.nsConn.namespace,e.Room=this.name,e.Event=M,this.nsConn.askRoomLeave(e)},e}(),X=function(){function e(e,n,d){this.conn=e,this.namespace=n,this.events=d,this.rooms=new Map}return e.prototype.emit=function(e,n){var d=new U;return d.Namespace=this.namespace,d.Event=e,d.Body=n,this.conn.write(d)},e.prototype.emitBinary=function(e,n){var d=new U;return d.Namespace=this.namespace,d.Event=e,d.Body=n,d.SetBinary=!0,this.conn.write(d)},e.prototype.ask=function(e,n){var d=new U;return d.Namespace=this.namespace,d.Event=e,d.Body=n,this.conn.ask(d)},e.prototype.joinRoom=function(e){return C(this,void 0,void 0,function(){return R(this,function(n){switch(n.label){case 0:return[4,this.askRoomJoin(e)];case 1:return[2,n.sent()];}})})},e.prototype.room=function(e){return this.rooms.get(e)},e.prototype.leaveAll=function(){return C(this,void 0,void 0,function(){var e=this,n;return R(this,function(){return n=new U,n.Namespace=this.namespace,n.Event=D,n.IsLocal=!0,this.rooms.forEach(function(d,i){return C(e,void 0,void 0,function(){var e;return R(this,function(d){switch(d.label){case 0:n.Room=i,d.label=1;case 1:return d.trys.push([1,3,,4]),[4,this.askRoomLeave(n)];case 2:return d.sent(),[3,4];case 3:return e=d.sent(),[2,e];case 4:return[2];}})})}),[2,null]})})},e.prototype.forceLeaveAll=function(e){var n=this,d=new U;d.Namespace=this.namespace,d.Event=M,d.IsForced=!0,d.IsLocal=e,this.rooms.forEach(function(e,i){d.Room=i,y(n,d),n.rooms.delete(i),d.Event=D,y(n,d),d.Event=M})},e.prototype.disconnect=function(){var e=new U;return e.Namespace=this.namespace,e.Event=j,this.conn.askDisconnect(e)},e.prototype.askRoomJoin=function(e){var n=this;return new Promise(function(d,i){return C(n,void 0,void 0,function(){var n,f,u,s;return R(this,function(a){switch(a.label){case 0:if(n=this.rooms.get(e),void 0!==n)return d(n),[2];f=new U,f.Namespace=this.namespace,f.Room=e,f.Event=A,f.IsLocal=!0,a.label=1;case 1:return a.trys.push([1,3,,4]),[4,this.conn.ask(f)];case 2:return a.sent(),[3,4];case 3:return u=a.sent(),i(u),[2];case 4:return(s=y(this,f),!o(s))?(i(s),[2]):(n=new G(this,e),this.rooms.set(e,n),f.Event=T,y(this,f),d(n),[2]);}})})})},e.prototype.askRoomLeave=function(e){return C(this,void 0,void 0,function(){var n,d;return R(this,function(i){switch(i.label){case 0:if(!this.rooms.has(e.Room))return[2,Y];i.label=1;case 1:return i.trys.push([1,3,,4]),[4,this.conn.ask(e)];case 2:return i.sent(),[3,4];case 3:return n=i.sent(),[2,n];case 4:return(d=y(this,e),!o(d))?[2,d]:(this.rooms.delete(e.Room),e.Event=D,y(this,e),[2,null]);}})})},e.prototype.replyRoomJoin=function(e){if(!(o(e.wait)||e.isNoOp)){if(!this.rooms.has(e.Room)){var n=y(this,e);if(!o(n))return e.Err=n,void this.conn.write(e);this.rooms.set(e.Room,new G(this,e.Room)),e.Event=T,y(this,e)}this.conn.writeEmptyReply(e.wait)}},e.prototype.replyRoomLeave=function(e){return o(e.wait)||e.isNoOp?void 0:this.rooms.has(e.Room)?void(y(this,e),this.rooms.delete(e.Room),this.conn.writeEmptyReply(e.wait),e.Event=D,y(this,e)):void this.conn.writeEmptyReply(e.wait)},e}(),Q=new Error(\"invalid payload\"),$=new Error(\"bad namespace\"),Y=new Error(\"bad room\"),Z=new Error(\"use of closed connection\"),V=new Error(\"write closed\"),ee=function(){function e(e,n){this.conn=e,this.reconnectTries=0,this._isAcknowledged=!1,this.namespaces=n;var d=n.has(\"\");this.allowNativeMessages=d&&n.get(\"\").has(J),this.queue=[],this.waitingMessages=new Map,this.connectedNamespaces=new Map,this.closed=!1}return e.prototype.wasReconnected=function(){return 0<this.reconnectTries},e.prototype.isAcknowledged=function(){return this._isAcknowledged},e.prototype.handle=function(e){if(!this._isAcknowledged){var n=this.handleAck(e.data);return null==n?(this._isAcknowledged=!0,this.handleQueue()):this.conn.close(),n}return this.handleMessage(e.data)},e.prototype.handleAck=function(e){var n=e[0];switch(n){case\"A\":var d=e.slice(1);this.ID=d;break;case\"H\":var i=e.slice(1);return new Error(i);default:return this.queue.push(e),null;}},e.prototype.handleQueue=function(){var e=this;null==this.queue||0==this.queue.length||this.queue.forEach(function(n,d){e.queue.splice(d,1),e.handleMessage(n)})},e.prototype.handleMessage=function(e){var n=m(e,this.allowNativeMessages);if(n.isInvalid)return Q;if(n.IsNative&&this.allowNativeMessages){var d=this.namespace(\"\");return y(d,n)}if(n.isWait()){var i=this.waitingMessages.get(n.wait);if(null!=i)return void i(n)}var f=this.namespace(n.Namespace);switch(n.Event){case\"_OnNamespaceConnect\":this.replyConnect(n);break;case j:this.replyDisconnect(n);break;case A:if(void 0!==f){f.replyRoomJoin(n);break}case M:if(void 0!==f){f.replyRoomLeave(n);break}default:if(void 0===f)return $;n.IsLocal=!1;var u=y(f,n);if(!o(u))return n.Err=u,this.write(n),u;}return null},e.prototype.connect=function(e){return this.askConnect(e)},e.prototype.waitServerConnect=function(e){var n=this;return w(this.waitServerConnectNotifiers)&&(this.waitServerConnectNotifiers=new Map),new Promise(function(d){return C(n,void 0,void 0,function(){var n=this;return R(this,function(){return this.waitServerConnectNotifiers.set(e,function(){n.waitServerConnectNotifiers.delete(e),d(n.namespace(e))}),[2]})})})},e.prototype.namespace=function(e){return this.connectedNamespaces.get(e)},e.prototype.replyConnect=function(e){if(!(o(e.wait)||e.isNoOp)){var n=this.namespace(e.Namespace);if(void 0!==n)return void this.writeEmptyReply(e.wait);var d=b(this.namespaces,e.Namespace);return w(d)?(e.Err=$,void this.write(e)):void(n=new X(this,e.Namespace,d),this.connectedNamespaces.set(e.Namespace,n),this.writeEmptyReply(e.wait),e.Event=B,y(n,e),!w(this.waitServerConnectNotifiers)&&0<this.waitServerConnectNotifiers.size&&this.waitServerConnectNotifiers.has(e.Namespace)&&this.waitServerConnectNotifiers.get(e.Namespace)())}},e.prototype.replyDisconnect=function(e){if(!(o(e.wait)||e.isNoOp)){var n=this.namespace(e.Namespace);return void 0===n?void this.writeEmptyReply(e.wait):void(n.forceLeaveAll(!0),this.connectedNamespaces.delete(e.Namespace),this.writeEmptyReply(e.wait),y(n,e))}},e.prototype.ask=function(e){var n=this;return new Promise(function(d,i){return n.isClosed()?void i(Z):(e.wait=g(),n.waitingMessages.set(e.wait,function(e){return e.isError?void i(e.Err):void d(e)}),!n.write(e))?void i(V):void 0})},e.prototype.askConnect=function(e){var n=this;return new Promise(function(d,i){return C(n,void 0,void 0,function(){var n,f,u,s,a;return R(this,function(t){switch(t.label){case 0:if(n=this.namespace(e),void 0!==n)return d(n),[2];if(f=b(this.namespaces,e),w(f))return i($),[2];if(u=new U,u.Namespace=e,u.Event=\"_OnNamespaceConnect\",u.IsLocal=!0,n=new X(this,e,f),s=y(n,u),!o(s))return i(s),[2];t.label=1;case 1:return t.trys.push([1,3,,4]),[4,this.ask(u)];case 2:return t.sent(),[3,4];case 3:return a=t.sent(),i(a),[2];case 4:return this.connectedNamespaces.set(e,n),u.Event=B,y(n,u),d(n),[2];}})})})},e.prototype.askDisconnect=function(e){return C(this,void 0,void 0,function(){var n,d;return R(this,function(i){switch(i.label){case 0:if(n=this.namespace(e.Namespace),void 0===n)return[2,$];i.label=1;case 1:return i.trys.push([1,3,,4]),[4,this.ask(e)];case 2:return i.sent(),[3,4];case 3:return d=i.sent(),[2,d];case 4:return n.forceLeaveAll(!0),this.connectedNamespaces.delete(e.Namespace),e.IsLocal=!0,[2,y(n,e)];}})})},e.prototype.isClosed=function(){return this.closed},e.prototype.write=function(e){if(this.isClosed())return!1;if(!e.isConnect()&&!e.isDisconnect()){var n=this.namespace(e.Namespace);if(void 0===n)return!1;if(!o(e.Room)&&!e.isRoomJoin()&&!e.isRoomLeft()&&!n.rooms.has(e.Room))return!1}return this.conn.send(l(e)),!0},e.prototype.writeEmptyReply=function(e){this.conn.send(h(e))},e.prototype.close=function(){var e=this;if(!this.closed){var n=new U;n.Event=j,n.IsForced=!0,n.IsLocal=!0,this.connectedNamespaces.forEach(function(d){d.forceLeaveAll(!0),n.Namespace=d.namespace,y(d,n),e.connectedNamespaces.delete(d.namespace)}),this.waitingMessages.clear(),this.closed=!0,this.conn.readyState===this.conn.OPEN&&this.conn.close()}},e}();(function(){var e={dial:v,isSystemEvent:u,OnNamespaceConnect:\"_OnNamespaceConnect\",OnNamespaceConnected:B,OnNamespaceDisconnect:j,OnRoomJoin:A,OnRoomJoined:T,OnRoomLeave:M,OnRoomLeft:D,OnAnyEvent:\"_OnAnyEvent\",OnNativeMessage:J,Message:U,Room:G,NSConn:X,Conn:ee,ErrInvalidPayload:Q,ErrBadNamespace:$,ErrBadRoom:Y,ErrClosed:Z,ErrWrite:V,isCloseError:x,reply:r,marshal:s};if(\"undefined\"!=typeof d)d=e,n.exports=e;else{var i=\"object\"==typeof self&&self.self===self&&self||\"object\"==typeof f&&f.global===f&&f;i.neffos=e}})()}).call(this,e(\"_process\"),\"undefined\"==typeof global?\"undefined\"==typeof self?\"undefined\"==typeof window?{}:window:self:global)},{\"@sinonjs/text-encoding\":2,_process:7,\"node-fetch\":6,ws:8}],6:[function(e,n,d){(function(e){\"use strict\";var i=function(){if(\"undefined\"!=typeof self)return self;if(\"undefined\"!=typeof window)return window;if(\"undefined\"!=typeof e)return e;throw new Error(\"unable to locate global object\")},e=i();n.exports=d=e.fetch,d.default=e.fetch.bind(e),d.Headers=e.Headers,d.Request=e.Request,d.Response=e.Response}).call(this,\"undefined\"==typeof global?\"undefined\"==typeof self?\"undefined\"==typeof window?{}:window:self:global)},{}],7:[function(e,n){function d(){throw new Error(\"setTimeout has not been defined\")}function i(){throw new Error(\"clearTimeout has not been defined\")}function f(n){if(c===setTimeout)return setTimeout(n,0);if((c===d||!c)&&setTimeout)return c=setTimeout,setTimeout(n,0);try{return c(n,0)}catch(d){try{return c.call(null,n,0)}catch(d){return c.call(this,n,0)}}}function u(n){if(l===clearTimeout)return clearTimeout(n);if((l===i||!l)&&clearTimeout)return l=clearTimeout,clearTimeout(n);try{return l(n)}catch(d){try{return l.call(null,n)}catch(d){return l.call(this,n)}}}function o(){m&&h&&(m=!1,h.length?p=h.concat(p):g=-1,p.length&&s())}function s(){if(!m){var e=f(o);m=!0;for(var n=p.length;n;){for(h=p,p=[];++g<n;)h&&h[g].run();g=-1,n=p.length}h=null,m=!1,u(e)}}function a(e,n){this.fun=e,this.array=n}function t(){}var r=n.exports={},c,l;(function(){try{c=\"function\"==typeof setTimeout?setTimeout:d}catch(n){c=d}try{l=\"function\"==typeof clearTimeout?clearTimeout:i}catch(n){l=i}})();var p=[],m=!1,g=-1,h;r.nextTick=function(e){var n=Array(arguments.length-1);if(1<arguments.length)for(var d=1;d<arguments.length;d++)n[d-1]=arguments[d];p.push(new a(e,n)),1!==p.length||m||f(s)},a.prototype.run=function(){this.fun.apply(null,this.array)},r.title=\"browser\",r.browser=!0,r.env={},r.argv=[],r.version=\"\",r.versions={},r.on=t,r.addListener=t,r.once=t,r.off=t,r.removeListener=t,r.removeAllListeners=t,r.emit=t,r.prependListener=t,r.prependOnceListener=t,r.listeners=function(){return[]},r.binding=function(){throw new Error(\"process.binding is not supported\")},r.cwd=function(){return\"/\"},r.chdir=function(){throw new Error(\"process.chdir is not supported\")},r.umask=function(){return 0}},{}],8:[function(e,n){'use strict';n.exports=function(){throw new Error(\"ws does not work in the browser. Browser clients must use the native WebSocket object\")}},{}]},{},[1]);"
  },
  {
    "path": "_examples/websocket/basic/browserify/client.html",
    "content": "<!-- the message's input -->\n<input id=\"input\" type=\"text\" />\n\n<!-- when clicked then a websocket event will be sent to the server, at this example we registered the 'chat' -->\n<button id=\"sendBtn\" disabled>Send</button>\n\n<!-- the messages will be shown here -->\n<pre id=\"output\"></pre>\n\n<script src=\"./bundle.js\"></script>\n"
  },
  {
    "path": "_examples/websocket/basic/browserify/package.json",
    "content": "{\n    \"name\": \"neffos.js.example.browserify\",\n    \"version\": \"0.0.1\",\n    \"scripts\": {\n        \"browserify\": \"browserify ./app.js -o ./bundle.js\",\n        \"minifyES6\": \"minify ./bundle.js --outFile ./bundle.js\",\n        \"build\": \"npm run-script browserify && npm run-script minifyES6\"\n    },\n    \"dependencies\": {\n        \"neffos.js\": \"latest\"\n    },\n    \"devDependencies\": {\n        \"browserify\": \"^16.2.3\",\n        \"babel-minify\": \"^0.5.0\"\n    }\n}\n"
  },
  {
    "path": "_examples/websocket/basic/go-client/client.go",
    "content": "package main\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/websocket\"\n)\n\nconst (\n\tendpoint              = \"ws://localhost:8080/echo\"\n\tnamespace             = \"default\"\n\tdialAndConnectTimeout = 5 * time.Second\n)\n\n// this can be shared with the server.go's.\n// `NSConn.Conn` has the `IsClient() bool` method which can be used to\n// check if that's is a client or a server-side callback.\nvar clientEvents = websocket.Namespaces{\n\tnamespace: websocket.Events{\n\t\twebsocket.OnNamespaceConnected: func(c *websocket.NSConn, msg websocket.Message) error {\n\t\t\tlog.Printf(\"connected to namespace: %s\", msg.Namespace)\n\t\t\treturn nil\n\t\t},\n\t\twebsocket.OnNamespaceDisconnect: func(c *websocket.NSConn, msg websocket.Message) error {\n\t\t\tlog.Printf(\"disconnected from namespace: %s\", msg.Namespace)\n\t\t\treturn nil\n\t\t},\n\t\t\"chat\": func(c *websocket.NSConn, msg websocket.Message) error {\n\t\t\tlog.Printf(\"%s\", string(msg.Body))\n\t\t\treturn nil\n\t\t},\n\t},\n}\n\nfunc main() {\n\tctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(dialAndConnectTimeout))\n\tdefer cancel()\n\n\t// username := \"my_username\"\n\t// dialer := websocket.GobwasDialer(websocket.GobwasDialerOptions{Header: websocket.GobwasHeader{\"X-Username\": []string{username}}})\n\tdialer := websocket.DefaultGobwasDialer\n\tclient, err := websocket.Dial(ctx, dialer, endpoint, clientEvents)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer client.Close()\n\n\tc, err := client.Connect(ctx, namespace)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tc.Emit(\"chat\", []byte(\"Hello from Go client side!\"))\n\n\tfmt.Fprint(os.Stdout, \">> \")\n\tscanner := bufio.NewScanner(os.Stdin)\n\tfor {\n\t\tif !scanner.Scan() {\n\t\t\tlog.Printf(\"ERROR: %v\", scanner.Err())\n\t\t\treturn\n\t\t}\n\n\t\ttext := scanner.Bytes()\n\n\t\tif bytes.Equal(text, []byte(\"exit\")) {\n\t\t\tif err := c.Disconnect(nil); err != nil {\n\t\t\t\tlog.Printf(\"reply from server: %v\", err)\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\n\t\tok := c.Emit(\"chat\", text)\n\t\tif !ok {\n\t\t\tbreak\n\t\t}\n\n\t\tfmt.Fprint(os.Stdout, \">> \")\n\t}\n} // try running this program twice or/and run the server's http://localhost:8080 to check the browser client as well.\n"
  },
  {
    "path": "_examples/websocket/basic/go.mod",
    "content": "module github.com/kataras/iris/_examples/websocket/basic\n\ngo 1.25\n\nrequire (\n\tgithub.com/iris-contrib/middleware/jwt v0.0.0-20251225090426-92c6f28facda\n\tgithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd\n)\n\nrequire (\n\tgithub.com/BurntSushi/toml v1.6.0 // indirect\n\tgithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect\n\tgithub.com/CloudyKit/jet/v6 v6.3.1 // indirect\n\tgithub.com/Joker/jade v1.1.3 // indirect\n\tgithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 // indirect\n\tgithub.com/andybalholm/brotli v1.2.0 // indirect\n\tgithub.com/aymerick/douceur v0.2.0 // indirect\n\tgithub.com/fatih/structs v1.1.0 // indirect\n\tgithub.com/flosch/pongo2/v4 v4.0.2 // indirect\n\tgithub.com/gobwas/httphead v0.1.0 // indirect\n\tgithub.com/gobwas/pool v0.2.1 // indirect\n\tgithub.com/gobwas/ws v1.4.0 // indirect\n\tgithub.com/golang-jwt/jwt/v4 v4.5.2 // indirect\n\tgithub.com/golang/snappy v1.0.0 // indirect\n\tgithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a // indirect\n\tgithub.com/google/uuid v1.6.0 // indirect\n\tgithub.com/gorilla/css v1.0.1 // indirect\n\tgithub.com/gorilla/websocket v1.5.3 // indirect\n\tgithub.com/iris-contrib/schema v0.0.6 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/kataras/blocks v0.0.8 // indirect\n\tgithub.com/kataras/golog v0.1.12 // indirect\n\tgithub.com/kataras/neffos v0.0.24 // indirect\n\tgithub.com/kataras/pio v0.0.13 // indirect\n\tgithub.com/kataras/sitemap v0.0.6 // indirect\n\tgithub.com/kataras/tunnel v0.0.4 // indirect\n\tgithub.com/klauspost/compress v1.18.2 // indirect\n\tgithub.com/kr/text v0.2.0 // indirect\n\tgithub.com/mailgun/raymond/v2 v2.0.48 // indirect\n\tgithub.com/mailru/easyjson v0.9.1 // indirect\n\tgithub.com/mediocregopher/radix/v3 v3.8.1 // indirect\n\tgithub.com/microcosm-cc/bluemonday v1.0.27 // indirect\n\tgithub.com/nats-io/nats.go v1.40.1 // indirect\n\tgithub.com/nats-io/nkeys v0.4.9 // indirect\n\tgithub.com/nats-io/nuid v1.0.1 // indirect\n\tgithub.com/russross/blackfriday/v2 v2.1.0 // indirect\n\tgithub.com/schollz/closestmatch v2.1.0+incompatible // indirect\n\tgithub.com/sirupsen/logrus v1.8.1 // indirect\n\tgithub.com/tdewolff/minify/v2 v2.24.8 // indirect\n\tgithub.com/tdewolff/parse/v2 v2.8.5 // indirect\n\tgithub.com/valyala/bytebufferpool v1.0.0 // indirect\n\tgithub.com/vmihailenco/msgpack/v5 v5.4.1 // indirect\n\tgithub.com/vmihailenco/tagparser/v2 v2.0.0 // indirect\n\tgithub.com/yosssi/ace v0.0.5 // indirect\n\tgolang.org/x/crypto v0.47.0 // indirect\n\tgolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 // indirect\n\tgolang.org/x/net v0.49.0 // indirect\n\tgolang.org/x/sys v0.40.0 // indirect\n\tgolang.org/x/text v0.33.0 // indirect\n\tgolang.org/x/time v0.14.0 // indirect\n\tgolang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect\n\tgoogle.golang.org/protobuf v1.36.11 // indirect\n\tgopkg.in/ini.v1 v1.67.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n"
  },
  {
    "path": "_examples/websocket/basic/go.sum",
    "content": "github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=\ngithub.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=\ngithub.com/CloudyKit/jet/v6 v6.3.1 h1:6IAo5Cx21xrHVaR8zzXN5gJatKV/wO7Nf6bfCnCSbUw=\ngithub.com/CloudyKit/jet/v6 v6.3.1/go.mod h1:lf8ksdNsxZt7/yH/3n4vJQWA9RUq4wpaHtArHhGVMOw=\ngithub.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc=\ngithub.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=\ngithub.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=\ngithub.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 h1:dG7/gLroJGht/jSQtHiLvT48Hxn+crbmvyItZC8cWOs=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05/go.mod h1:NYezi6wtnJtBm5btoprXc5SvAdqH0XTXWnUup0MptAI=\ngithub.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=\ngithub.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=\ngithub.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=\ngithub.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=\ngithub.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=\ngithub.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=\ngithub.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=\ngithub.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=\ngithub.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=\ngithub.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw=\ngithub.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8=\ngithub.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=\ngithub.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=\ngithub.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=\ngithub.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=\ngithub.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=\ngithub.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=\ngithub.com/gobwas/ws v1.4.0 h1:CTaoG1tojrh4ucGPcoJFiAQUAsEWekEWvLy7GsVNqGs=\ngithub.com/gobwas/ws v1.4.0/go.mod h1:G3gNqMNtPppf5XUz7O4shetPpcZ1VJ7zt18dlUeakrc=\ngithub.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=\ngithub.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=\ngithub.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=\ngithub.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a h1:l7A0loSszR5zHd/qK53ZIHMO8b3bBSmENnQ6eKnUT0A=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=\ngithub.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=\ngithub.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=\ngithub.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=\ngithub.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=\ngithub.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2 h1:T9THsdP1woyAqKHwjkEsbCnMefsAFvk8iJJKokcJ3Go=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2/go.mod h1:JLDgIqnFy5loDSUv1OA2j0mb6p/rDhiCqigP22Uq9xE=\ngithub.com/iris-contrib/middleware/jwt v0.0.0-20251225090426-92c6f28facda h1:1VquMwe3hyozXkURE0uJLqMCiTmJpTYwLFCEZSkXzs0=\ngithub.com/iris-contrib/middleware/jwt v0.0.0-20251225090426-92c6f28facda/go.mod h1:gvZR1e7IFVaT5ph6WFdleMXYSsocncDG+0BvKyJerTc=\ngithub.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw=\ngithub.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/kataras/blocks v0.0.8 h1:MrpVhoFTCR2v1iOOfGng5VJSILKeZZI+7NGfxEh3SUM=\ngithub.com/kataras/blocks v0.0.8/go.mod h1:9Jm5zx6BB+06NwA+OhTbHW1xkMOYxahnqTN5DveZ2Yg=\ngithub.com/kataras/golog v0.1.12 h1:Bu7I/G4ilJlbfzjmU39O9N+2uO1pBcMK045fzZ4ytNg=\ngithub.com/kataras/golog v0.1.12/go.mod h1:wrGSbOiBqbQSQznleVNX4epWM8rl9SJ/rmEacl0yqy4=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd h1:oMsQgqKACbUdlaIWnN+EZzzAnHkw90hE/y/R0BSij2U=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd/go.mod h1:yucjtDl9Vn3OctWM1fi0MuM9OckemC7kEreFa9QtPF8=\ngithub.com/kataras/neffos v0.0.24 h1:S3lHqJopCfXN285VdlbGeOj+Id83u4xdQKToa+w1vW0=\ngithub.com/kataras/neffos v0.0.24/go.mod h1:/3K9zQ0yEC5/xUiSQx46ToWa3xneGfUo/nMit/F5g+U=\ngithub.com/kataras/pio v0.0.13 h1:x0rXVX0fviDTXOOLOmr4MUxOabu1InVSTu5itF8CXCM=\ngithub.com/kataras/pio v0.0.13/go.mod h1:k3HNuSw+eJ8Pm2lA4lRhg3DiCjVgHlP8hmXApSej3oM=\ngithub.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY=\ngithub.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4=\ngithub.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA=\ngithub.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw=\ngithub.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=\ngithub.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=\ngithub.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=\ngithub.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8=\ngithub.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=\ngithub.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=\ngithub.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=\ngithub.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=\ngithub.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/mediocregopher/radix/v3 v3.8.1 h1:rOkHflVuulFKlwsLY01/M2cM2tWCjDoETcMqKbAWu1M=\ngithub.com/mediocregopher/radix/v3 v3.8.1/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8=\ngithub.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=\ngithub.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=\ngithub.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=\ngithub.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=\ngithub.com/nats-io/nats.go v1.40.1 h1:MLjDkdsbGUeCMKFyCFoLnNn/HDTqcgVa3EQm+pMNDPk=\ngithub.com/nats-io/nats.go v1.40.1/go.mod h1:wV73x0FSI/orHPSYoyMeJB+KajMDoWyXmFaRrrYaaTo=\ngithub.com/nats-io/nkeys v0.4.9 h1:qe9Faq2Gxwi6RZnZMXfmGMZkg3afLLOtrU+gDZJ35b0=\ngithub.com/nats-io/nkeys v0.4.9/go.mod h1:jcMqs+FLG+W5YO36OX6wFIFcmpdAns+w1Wm6D3I/evE=\ngithub.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=\ngithub.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=\ngithub.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=\ngithub.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=\ngithub.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=\ngithub.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk=\ngithub.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=\ngithub.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=\ngithub.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=\ngithub.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=\ngithub.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/tdewolff/minify/v2 v2.24.8 h1:58/VjsbevI4d5FGV0ZSuBrHMSSkH4MCH0sIz/eKIauE=\ngithub.com/tdewolff/minify/v2 v2.24.8/go.mod h1:0Ukj0CRpo/sW/nd8uZ4ccXaV1rEVIWA3dj8U7+Shhfw=\ngithub.com/tdewolff/parse/v2 v2.8.5 h1:ZmBiA/8Do5Rpk7bDye0jbbDUpXXbCdc3iah4VeUvwYU=\ngithub.com/tdewolff/parse/v2 v2.8.5/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=\ngithub.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE=\ngithub.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=\ngithub.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=\ngithub.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=\ngithub.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=\ngithub.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=\ngithub.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=\ngithub.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=\ngithub.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA=\ngithub.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0=\ngithub.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=\ngithub.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=\ngithub.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=\ngolang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=\ngolang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=\ngolang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=\ngolang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=\ngolang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=\ngolang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=\ngolang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=\ngolang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=\ngolang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY=\ngolang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=\ngoogle.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=\ngoogle.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=\ngopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nmoul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs=\nmoul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE=\n"
  },
  {
    "path": "_examples/websocket/basic/nodejs-client/client.js",
    "content": "const neffos = require('neffos.js');\nconst stdin = process.openStdin();\n\nconst wsURL = \"ws://localhost:8080/echo\";\n\nasync function runExample() {\n  try {\n    const conn = await neffos.dial(wsURL, {\n      default: { // \"default\" namespace.\n        _OnNamespaceConnected: function (nsConn, msg) {\n          console.log(\"connected to namespace: \" + msg.Namespace);\n        },\n        _OnNamespaceDisconnect: function (nsConn, msg) {\n          console.log(\"disconnected from namespace: \" + msg.Namespace);\n        },\n        chat: function (nsConn, msg) { // \"chat\" event.\n          console.log(msg.Body);\n        }\n      }\n    });\n\n    const nsConn = await conn.connect(\"default\");\n    nsConn.emit(\"chat\", \"Hello from Nodejs client side!\");\n\n    stdin.addListener(\"data\", function (data) {\n      const text = data.toString().trim();\n      nsConn.emit(\"chat\", text);\n    });\n\n  } catch (err) {\n    console.error(err);\n  }\n}\n\nrunExample();\n"
  },
  {
    "path": "_examples/websocket/basic/nodejs-client/package.json",
    "content": "{\n    \"name\": \"neffos.js.example.nodejsclient\",\n    \"version\": \"0.0.1\",\n    \"main\": \"client.js\",\n    \"dependencies\": {\n        \"neffos.js\": \"latest\"\n    }\n}\n"
  },
  {
    "path": "_examples/websocket/basic/server.go",
    "content": "package main\n\nimport (\n\t\"log\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/websocket\"\n\n\t// Used when \"enableJWT\" constant is true:\n\t\"github.com/iris-contrib/middleware/jwt\"\n)\n\n// values should match with the client sides as well.\nconst enableJWT = true\nconst namespace = \"default\"\n\n// if namespace is empty then simply websocket.Events{...} can be used instead.\nvar serverEvents = websocket.Namespaces{\n\tnamespace: websocket.Events{\n\t\twebsocket.OnNamespaceConnected: func(nsConn *websocket.NSConn, msg websocket.Message) error {\n\t\t\t// with `websocket.GetContext` you can retrieve the Iris' `Context`.\n\t\t\tctx := websocket.GetContext(nsConn.Conn)\n\n\t\t\tlog.Printf(\"[%s] connected to namespace [%s] with IP [%s]\",\n\t\t\t\tnsConn, msg.Namespace,\n\t\t\t\tctx.RemoteAddr())\n\t\t\treturn nil\n\t\t},\n\t\twebsocket.OnNamespaceDisconnect: func(nsConn *websocket.NSConn, msg websocket.Message) error {\n\t\t\tlog.Printf(\"[%s] disconnected from namespace [%s]\", nsConn, msg.Namespace)\n\t\t\treturn nil\n\t\t},\n\t\t\"chat\": func(nsConn *websocket.NSConn, msg websocket.Message) error {\n\t\t\t// room.String() returns -> NSConn.String() returns -> Conn.String() returns -> Conn.ID()\n\t\t\tlog.Printf(\"[%s] sent: %s\", nsConn, string(msg.Body))\n\n\t\t\t// Write message back to the client message owner with:\n\t\t\t// nsConn.Emit(\"chat\", msg)\n\t\t\t// Write message to all except this client with:\n\t\t\tnsConn.Conn.Server().Broadcast(nsConn, msg)\n\t\t\treturn nil\n\t\t},\n\t},\n}\n\nfunc main() {\n\tapp := iris.New()\n\twebsocketServer := websocket.New(\n\t\twebsocket.DefaultGorillaUpgrader, /* DefaultGobwasUpgrader can be used too. */\n\t\tserverEvents)\n\n\tj := jwt.New(jwt.Config{\n\t\t// Extract by the \"token\" url,\n\t\t// so the client should dial with ws://localhost:8080/echo?token=$token\n\t\tExtractor: jwt.FromParameter(\"token\"),\n\n\t\tValidationKeyGetter: func(token *jwt.Token) (any, error) {\n\t\t\treturn []byte(\"My Secret\"), nil\n\t\t},\n\n\t\t// When set, the middleware verifies that tokens are signed\n\t\t// with the specific signing algorithm\n\t\t// If the signing method is not constant the\n\t\t// `Config.ValidationKeyGetter` callback field can be used\n\t\t// to implement additional checks\n\t\t// Important to avoid security issues described here:\n\t\t// https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/\n\t\tSigningMethod: jwt.SigningMethodHS256,\n\t})\n\n\tidGen := func(ctx iris.Context) string {\n\t\tif username := ctx.GetHeader(\"X-Username\"); username != \"\" {\n\t\t\treturn username\n\t\t}\n\n\t\treturn websocket.DefaultIDGenerator(ctx)\n\t}\n\n\t// serves the endpoint of ws://localhost:8080/echo\n\t// with optional custom ID generator.\n\twebsocketRoute := app.Get(\"/echo\", websocket.Handler(websocketServer, idGen))\n\n\tif enableJWT {\n\t\t// Register the jwt middleware (on handshake):\n\t\twebsocketRoute.Use(j.Serve)\n\t\t// OR\n\t\t//\n\t\t// Check for token through the jwt middleware\n\t\t// on websocket connection or on any event:\n\t\t/* websocketServer.OnConnect = func(c *websocket.Conn) error {\n\t\tctx := websocket.GetContext(c)\n\t\tif err := j.CheckJWT(ctx); err != nil {\n\t\t\t// will send the above error on the client\n\t\t\t// and will not allow it to connect to the websocket server at all.\n\t\t\treturn err\n\t\t}\n\n\t\tuser := ctx.Values().Get(\"jwt\").(*jwt.Token)\n\t\t// or just: user := j.Get(ctx)\n\n\t\tlog.Printf(\"This is an authenticated request\\n\")\n\t\tlog.Printf(\"Claim content:\")\n\t\tlog.Printf(\"%#+v\\n\", user.Claims)\n\n\t\tlog.Printf(\"[%s] connected to the server\", c.ID())\n\n\t\treturn nil\n\t\t} */\n\t}\n\n\t// serves the browser-based websocket client.\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.ServeFile(\"./browser/index.html\")\n\t})\n\n\tapp.Get(\"/other\", func(ctx iris.Context) {\n\t\tctx.WriteString(\"Other route\")\n\t})\n\n\t// serves the npm browser websocket client usage example.\n\tapp.HandleDir(\"/browserify\", iris.Dir(\"./browserify\"))\n\n\t// http://localhost:8080\n\t// http://localhost:8080/browserify/client.html\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/websocket/gorilla-filewatch/go.mod",
    "content": "module gorilla-filewatch-example\n\ngo 1.25\n\nrequire (\n\tgithub.com/gorilla/websocket v1.5.3\n\tgithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd\n)\n\nrequire (\n\tgithub.com/BurntSushi/toml v1.6.0 // indirect\n\tgithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect\n\tgithub.com/CloudyKit/jet/v6 v6.3.1 // indirect\n\tgithub.com/Joker/jade v1.1.3 // indirect\n\tgithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 // indirect\n\tgithub.com/andybalholm/brotli v1.2.0 // indirect\n\tgithub.com/aymerick/douceur v0.2.0 // indirect\n\tgithub.com/fatih/structs v1.1.0 // indirect\n\tgithub.com/flosch/pongo2/v4 v4.0.2 // indirect\n\tgithub.com/golang/snappy v1.0.0 // indirect\n\tgithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a // indirect\n\tgithub.com/google/uuid v1.6.0 // indirect\n\tgithub.com/gorilla/css v1.0.1 // indirect\n\tgithub.com/iris-contrib/schema v0.0.6 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/kataras/blocks v0.0.8 // indirect\n\tgithub.com/kataras/golog v0.1.12 // indirect\n\tgithub.com/kataras/pio v0.0.13 // indirect\n\tgithub.com/kataras/sitemap v0.0.6 // indirect\n\tgithub.com/kataras/tunnel v0.0.4 // indirect\n\tgithub.com/klauspost/compress v1.18.2 // indirect\n\tgithub.com/kr/pretty v0.3.1 // indirect\n\tgithub.com/mailgun/raymond/v2 v2.0.48 // indirect\n\tgithub.com/mailru/easyjson v0.9.1 // indirect\n\tgithub.com/microcosm-cc/bluemonday v1.0.27 // indirect\n\tgithub.com/rogpeppe/go-internal v1.10.0 // indirect\n\tgithub.com/russross/blackfriday/v2 v2.1.0 // indirect\n\tgithub.com/schollz/closestmatch v2.1.0+incompatible // indirect\n\tgithub.com/sirupsen/logrus v1.8.1 // indirect\n\tgithub.com/stretchr/testify v1.11.1 // indirect\n\tgithub.com/tdewolff/minify/v2 v2.24.8 // indirect\n\tgithub.com/tdewolff/parse/v2 v2.8.5 // indirect\n\tgithub.com/valyala/bytebufferpool v1.0.0 // indirect\n\tgithub.com/vmihailenco/msgpack/v5 v5.4.1 // indirect\n\tgithub.com/vmihailenco/tagparser/v2 v2.0.0 // indirect\n\tgithub.com/yosssi/ace v0.0.5 // indirect\n\tgolang.org/x/crypto v0.47.0 // indirect\n\tgolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 // indirect\n\tgolang.org/x/net v0.49.0 // indirect\n\tgolang.org/x/sys v0.40.0 // indirect\n\tgolang.org/x/text v0.33.0 // indirect\n\tgolang.org/x/time v0.14.0 // indirect\n\tgoogle.golang.org/protobuf v1.36.11 // indirect\n\tgopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect\n\tgopkg.in/ini.v1 v1.67.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n"
  },
  {
    "path": "_examples/websocket/gorilla-filewatch/go.sum",
    "content": "github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=\ngithub.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=\ngithub.com/CloudyKit/jet/v6 v6.3.1 h1:6IAo5Cx21xrHVaR8zzXN5gJatKV/wO7Nf6bfCnCSbUw=\ngithub.com/CloudyKit/jet/v6 v6.3.1/go.mod h1:lf8ksdNsxZt7/yH/3n4vJQWA9RUq4wpaHtArHhGVMOw=\ngithub.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc=\ngithub.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=\ngithub.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=\ngithub.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 h1:dG7/gLroJGht/jSQtHiLvT48Hxn+crbmvyItZC8cWOs=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05/go.mod h1:NYezi6wtnJtBm5btoprXc5SvAdqH0XTXWnUup0MptAI=\ngithub.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=\ngithub.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=\ngithub.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=\ngithub.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=\ngithub.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=\ngithub.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=\ngithub.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=\ngithub.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=\ngithub.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=\ngithub.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw=\ngithub.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8=\ngithub.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=\ngithub.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=\ngithub.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=\ngithub.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a h1:l7A0loSszR5zHd/qK53ZIHMO8b3bBSmENnQ6eKnUT0A=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=\ngithub.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=\ngithub.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=\ngithub.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=\ngithub.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=\ngithub.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2 h1:T9THsdP1woyAqKHwjkEsbCnMefsAFvk8iJJKokcJ3Go=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2/go.mod h1:JLDgIqnFy5loDSUv1OA2j0mb6p/rDhiCqigP22Uq9xE=\ngithub.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw=\ngithub.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/kataras/blocks v0.0.8 h1:MrpVhoFTCR2v1iOOfGng5VJSILKeZZI+7NGfxEh3SUM=\ngithub.com/kataras/blocks v0.0.8/go.mod h1:9Jm5zx6BB+06NwA+OhTbHW1xkMOYxahnqTN5DveZ2Yg=\ngithub.com/kataras/golog v0.1.12 h1:Bu7I/G4ilJlbfzjmU39O9N+2uO1pBcMK045fzZ4ytNg=\ngithub.com/kataras/golog v0.1.12/go.mod h1:wrGSbOiBqbQSQznleVNX4epWM8rl9SJ/rmEacl0yqy4=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd h1:oMsQgqKACbUdlaIWnN+EZzzAnHkw90hE/y/R0BSij2U=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd/go.mod h1:yucjtDl9Vn3OctWM1fi0MuM9OckemC7kEreFa9QtPF8=\ngithub.com/kataras/pio v0.0.13 h1:x0rXVX0fviDTXOOLOmr4MUxOabu1InVSTu5itF8CXCM=\ngithub.com/kataras/pio v0.0.13/go.mod h1:k3HNuSw+eJ8Pm2lA4lRhg3DiCjVgHlP8hmXApSej3oM=\ngithub.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY=\ngithub.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4=\ngithub.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA=\ngithub.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw=\ngithub.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=\ngithub.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=\ngithub.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=\ngithub.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8=\ngithub.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=\ngithub.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=\ngithub.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=\ngithub.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=\ngithub.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=\ngithub.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=\ngithub.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=\ngithub.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=\ngithub.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=\ngithub.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=\ngithub.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=\ngithub.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=\ngithub.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk=\ngithub.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=\ngithub.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=\ngithub.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=\ngithub.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=\ngithub.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/tdewolff/minify/v2 v2.24.8 h1:58/VjsbevI4d5FGV0ZSuBrHMSSkH4MCH0sIz/eKIauE=\ngithub.com/tdewolff/minify/v2 v2.24.8/go.mod h1:0Ukj0CRpo/sW/nd8uZ4ccXaV1rEVIWA3dj8U7+Shhfw=\ngithub.com/tdewolff/parse/v2 v2.8.5 h1:ZmBiA/8Do5Rpk7bDye0jbbDUpXXbCdc3iah4VeUvwYU=\ngithub.com/tdewolff/parse/v2 v2.8.5/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=\ngithub.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE=\ngithub.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=\ngithub.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=\ngithub.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=\ngithub.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=\ngithub.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=\ngithub.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=\ngithub.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=\ngithub.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA=\ngithub.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0=\ngithub.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=\ngithub.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=\ngithub.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=\ngolang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=\ngolang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=\ngolang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=\ngolang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=\ngolang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=\ngolang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=\ngolang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=\ngolang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=\ngoogle.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=\ngopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nmoul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs=\nmoul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE=\n"
  },
  {
    "path": "_examples/websocket/gorilla-filewatch/main.go",
    "content": "// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n//\n// This example shows a use case with gorilla webscokets package\n// sends a file to the browser client for display whenever the file is modified.\n//\n// $ go run main.go <name of file to watch>\n// # Open http://localhost:8080/ .\n// # Modify the file to see it update in the browser.\npackage main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/gorilla/websocket\"\n\t\"github.com/kataras/iris/v12\"\n)\n\nconst (\n\t// Time allowed to write the file to the client.\n\twriteWait = 10 * time.Second\n\n\t// Time allowed to read the next pong message from the client.\n\tpongWait = 60 * time.Second\n\n\t// Send pings to client with this period. Must be less than pongWait.\n\tpingPeriod = (pongWait * 9) / 10\n\n\t// Poll file for changes with this period.\n\tfilePeriod = 10 * time.Second\n)\n\nvar (\n\taddr     = flag.String(\"addr\", \":8080\", \"http service address\")\n\tfilename string\n\tupgrader = websocket.Upgrader{\n\t\tReadBufferSize:  1024,\n\t\tWriteBufferSize: 1024,\n\t}\n)\n\nfunc readFileIfModified(lastMod time.Time) ([]byte, time.Time, error) {\n\tfi, err := os.Stat(filename)\n\tif err != nil {\n\t\treturn nil, lastMod, err\n\t}\n\tif !fi.ModTime().After(lastMod) {\n\t\treturn nil, lastMod, nil\n\t}\n\tp, err := os.ReadFile(filename)\n\tif err != nil {\n\t\treturn nil, fi.ModTime(), err\n\t}\n\treturn p, fi.ModTime(), nil\n}\n\nfunc reader(ws *websocket.Conn) {\n\tdefer ws.Close()\n\tws.SetReadLimit(512)\n\tws.SetReadDeadline(time.Now().Add(pongWait))\n\tws.SetPongHandler(func(string) error { ws.SetReadDeadline(time.Now().Add(pongWait)); return nil })\n\tfor {\n\t\t_, _, err := ws.ReadMessage()\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t}\n}\n\nfunc writer(ws *websocket.Conn, lastMod time.Time) {\n\tlastError := \"\"\n\tpingTicker := time.NewTicker(pingPeriod)\n\tfileTicker := time.NewTicker(filePeriod)\n\tdefer func() {\n\t\tpingTicker.Stop()\n\t\tfileTicker.Stop()\n\t\tws.Close()\n\t}()\n\tfor {\n\t\tselect {\n\t\tcase <-fileTicker.C:\n\t\t\tvar p []byte\n\t\t\tvar err error\n\n\t\t\tp, lastMod, err = readFileIfModified(lastMod)\n\n\t\t\tif err != nil {\n\t\t\t\tif s := err.Error(); s != lastError {\n\t\t\t\t\tlastError = s\n\t\t\t\t\tp = []byte(lastError)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tlastError = \"\"\n\t\t\t}\n\n\t\t\tif p != nil {\n\t\t\t\tws.SetWriteDeadline(time.Now().Add(writeWait))\n\t\t\t\tif err := ws.WriteMessage(websocket.TextMessage, p); err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\tcase <-pingTicker.C:\n\t\t\tws.SetWriteDeadline(time.Now().Add(writeWait))\n\t\t\tif err := ws.WriteMessage(websocket.PingMessage, []byte{}); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc serveWs(ctx iris.Context) {\n\tws, err := upgrader.Upgrade(ctx.ResponseWriter(), ctx.Request(), nil)\n\tif err != nil {\n\t\tif _, ok := err.(websocket.HandshakeError); !ok {\n\t\t\tlog.Println(err)\n\t\t}\n\t\treturn\n\t}\n\n\tvar lastMod time.Time\n\tif n, err := strconv.ParseInt(ctx.FormValue(\"lastMod\"), 16, 64); err == nil {\n\t\tlastMod = time.Unix(0, n)\n\t}\n\n\tgo writer(ws, lastMod)\n\treader(ws)\n}\n\nfunc serveHome(ctx iris.Context) {\n\tp, lastMod, err := readFileIfModified(time.Time{})\n\tif err != nil {\n\t\tp = []byte(err.Error())\n\t\tlastMod = time.Unix(0, 0)\n\t}\n\tvar v = struct {\n\t\tHost    string\n\t\tData    string\n\t\tLastMod string\n\t}{\n\t\tctx.Host(),\n\t\tstring(p),\n\t\tstrconv.FormatInt(lastMod.UnixNano(), 16),\n\t}\n\tif err := ctx.View(\"home.html\", v); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n}\n\n// $ go get github.com/gorilla/websocket\n// $ go run main.go testfile.txt\nfunc main() {\n\tflag.Parse()\n\tif flag.NArg() != 1 {\n\t\tlog.Fatal(\"filename not specified\")\n\t}\n\tfilename = flag.Args()[0]\n\n\tapp := iris.New()\n\tapp.RegisterView(iris.HTML(\"./views\", \".html\"))\n\tapp.Get(\"/\", serveHome)\n\tapp.Any(\"/ws\", serveWs)\n\n\tapp.Listen(*addr)\n}\n"
  },
  {
    "path": "_examples/websocket/gorilla-filewatch/testfile.txt",
    "content": "Some Contents\n# you can edit this file locally\n# and http://localhost:8080\n# will render the new contents through the live websocket connection."
  },
  {
    "path": "_examples/websocket/gorilla-filewatch/views/home.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n    <head>\n        <title>WebSocket Example</title>\n    </head>\n    <body>\n        <pre id=\"fileData\">{{.Data}}</pre>\n        <script type=\"text/javascript\">\n            (function() {\n                var data = document.getElementById(\"fileData\");\n                var conn = new WebSocket(\"ws://{{.Host}}/ws?lastMod={{.LastMod}}\");\n                conn.onclose = function(evt) {\n                    data.textContent = 'Connection closed';\n                }\n                conn.onmessage = function(evt) {\n                    console.log('file updated');\n                    data.textContent = evt.data;\n                }\n            })();\n        </script>\n    </body>\n</html>"
  },
  {
    "path": "_examples/websocket/native-messages/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/websocket\"\n)\n\ntype clientPage struct {\n\tTitle string\n\tHost  string\n}\n\nfunc main() {\n\tapp := iris.New()\n\n\tapp.RegisterView(iris.HTML(\"./templates\", \".html\")) // select the html engine to serve templates\n\n\t// Almost all features of neffos are disabled because no custom message can pass\n\t// when app expects to accept and send only raw websocket native messages.\n\t// When only allow native messages is a fact?\n\t// When the registered namespace is just one and it's empty\n\t// and contains only one registered event which is the `OnNativeMessage`.\n\t// When `Events{...}` is used instead of `Namespaces{ \"namespaceName\": Events{...}}`\n\t// then the namespace is empty \"\".\n\tws := websocket.New(websocket.DefaultGorillaUpgrader, websocket.Events{\n\t\twebsocket.OnNativeMessage: func(nsConn *websocket.NSConn, msg websocket.Message) error {\n\t\t\tlog.Printf(\"Server got: %s from [%s]\", msg.Body, nsConn.Conn.ID())\n\n\t\t\tnsConn.Conn.Server().Broadcast(nsConn, msg)\n\t\t\treturn nil\n\t\t},\n\t})\n\n\tws.OnConnect = func(c *websocket.Conn) error {\n\t\tlog.Printf(\"[%s] Connected to server!\", c.ID())\n\t\treturn nil\n\t}\n\n\tws.OnDisconnect = func(c *websocket.Conn) {\n\t\tlog.Printf(\"[%s] Disconnected from server\", c.ID())\n\t}\n\n\tapp.HandleDir(\"/js\", iris.Dir(\"./static/js\")) // serve our custom javascript code.\n\n\t// register the server on an endpoint.\n\t// see the inline javascript code i the websockets.html, this endpoint is used to connect to the server.\n\tapp.Get(\"/my_endpoint\", websocket.Handler(ws))\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tif err := ctx.View(\"client.html\", clientPage{\"Client Page\", \"localhost:8080\"}); err != nil {\n\t\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\t\treturn\n\t\t}\n\t})\n\n\t// Target some browser windows/tabs to http://localhost:8080 and send some messages,\n\t// see the static/js/chat.js,\n\t// note that the client is using only the browser's native WebSocket API instead of the neffos one.\n\tapp.Listen(\":8080\")\n}\n"
  },
  {
    "path": "_examples/websocket/native-messages/static/js/chat.js",
    "content": "var messageTxt = document.getElementById(\"messageTxt\");\r\nvar messages = document.getElementById(\"messages\");\r\nvar sendBtn = document.getElementById(\"sendBtn\")\r\n\r\nw = new WebSocket(\"ws://\" + HOST + \"/my_endpoint\");\r\nw.onopen = function () {\r\n\tconsole.log(\"Websocket connection enstablished\");\r\n};\r\n\r\nw.onclose = function () {\r\n\tappendMessage(\"<div><center><h3>Disconnected</h3></center></div>\");\r\n};\r\nw.onmessage = function (message) {\r\n\tappendMessage(\"<div>\" + message.data + \"</div>\");\r\n};\r\n\r\nsendBtn.onclick = function () {\r\n\tmyText = messageTxt.value;\r\n\tmessageTxt.value = \"\";\r\n\r\n\tappendMessage(\"<div style='color: red'> me: \" + myText + \"</div>\");\r\n\tw.send(myText);\r\n};\r\n\r\nmessageTxt.addEventListener(\"keyup\", function (e) {\r\n\tif (e.keyCode === 13) {\r\n\t\te.preventDefault();\r\n\r\n\t\tsendBtn.click();\r\n\t}\r\n});\r\n\r\nfunction appendMessage(messageDivHTML) {\r\n\tmessages.insertAdjacentHTML('afterbegin', messageDivHTML);\r\n}\r\n"
  },
  {
    "path": "_examples/websocket/native-messages/templates/client.html",
    "content": "<html>\r\n\r\n<head>\r\n\t<title>{{ .Title}}</title>\r\n</head>\r\n\r\n<body style=\"padding:10px;\">\r\n\t<input type=\"text\" id=\"messageTxt\" />\r\n\t<button type=\"button\" id=\"sendBtn\">Send</button>\r\n\t<div id=\"messages\" style=\"width: 375px;margin:10 0 0 0px;border-top: 1px solid black;\">\r\n\t</div>\r\n\r\n\t<script type=\"text/javascript\">\r\n\t\tvar HOST = {{.Host }}\r\n\t</script>\r\n\t<script src=\"js/chat.js\" type=\"text/javascript\"></script>\r\n</body>\r\n\r\n</html>"
  },
  {
    "path": "_examples/websocket/online-visitors/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"sync/atomic\"\n\n\t\"github.com/kataras/iris/v12\"\n\n\t\"github.com/kataras/iris/v12/websocket\"\n)\n\nvar events = websocket.Namespaces{\n\t\"default\": websocket.Events{\n\t\twebsocket.OnRoomJoined: onRoomJoined,\n\t\twebsocket.OnRoomLeft:   onRoomLeft,\n\t},\n}\n\nfunc main() {\n\t// init the web application instance\n\t// app := iris.New()\n\tapp := iris.Default()\n\n\t// load templates\n\tapp.RegisterView(iris.HTML(\"./templates\", \".html\").Reload(true))\n\t// setup the websocket server\n\tws := websocket.New(websocket.DefaultGorillaUpgrader, events)\n\n\tapp.Get(\"/my_endpoint\", websocket.Handler(ws))\n\n\t// register static assets request path and system directory\n\tapp.HandleDir(\"/js\", iris.Dir(\"./static/assets/js\"))\n\n\th := func(ctx iris.Context) {\n\t\tctx.ViewData(\"\", page{PageID: \"index page\"})\n\t\tif err := ctx.View(\"index.html\"); err != nil {\n\t\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\t\treturn\n\t\t}\n\t}\n\n\th2 := func(ctx iris.Context) {\n\t\tctx.ViewData(\"\", page{PageID: \"other page\"})\n\t\tif err := ctx.View(\"other.html\"); err != nil {\n\t\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\t\treturn\n\t\t}\n\t}\n\n\t// Open some browser tabs/or windows\n\t// and navigate to\n\t// http://localhost:8080/ and http://localhost:8080/other multiple times.\n\t// Each page has its own online-visitors counter.\n\tapp.Get(\"/\", h)\n\tapp.Get(\"/other\", h2)\n\tapp.Listen(\":8080\")\n}\n\ntype page struct {\n\tPageID string\n}\n\ntype pageView struct {\n\tsource string\n\tcount  uint64\n}\n\nfunc (v *pageView) increment() {\n\tatomic.AddUint64(&v.count, 1)\n}\n\nfunc (v *pageView) decrement() {\n\tatomic.AddUint64(&v.count, ^uint64(0))\n}\n\nfunc (v *pageView) getCount() uint64 {\n\treturn atomic.LoadUint64(&v.count)\n}\n\ntype (\n\tpageViews []pageView\n)\n\nfunc (v *pageViews) Add(source string) {\n\targs := *v\n\tn := len(args)\n\tfor i := 0; i < n; i++ {\n\t\tkv := &args[i]\n\t\tif kv.source == source {\n\t\t\tkv.increment()\n\t\t\treturn\n\t\t}\n\t}\n\n\tc := cap(args)\n\tif c > n {\n\t\targs = args[:n+1]\n\t\tkv := &args[n]\n\t\tkv.source = source\n\t\tkv.count = 1\n\t\t*v = args\n\t\treturn\n\t}\n\n\tkv := pageView{}\n\tkv.source = source\n\tkv.count = 1\n\t*v = append(args, kv)\n}\n\nfunc (v *pageViews) Get(source string) *pageView {\n\targs := *v\n\tn := len(args)\n\tfor i := 0; i < n; i++ {\n\t\tkv := &args[i]\n\t\tif kv.source == source {\n\t\t\treturn kv\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (v *pageViews) Reset() {\n\t*v = (*v)[:0]\n}\n\nvar v pageViews\n\nfunc viewsCountBytes(viewsCount uint64) []byte {\n\t// * there are other methods to convert uint64 to []byte\n\treturn []byte(fmt.Sprintf(\"%d\", viewsCount))\n}\n\nfunc onRoomJoined(ns *websocket.NSConn, msg websocket.Message) error {\n\t// the roomName here is the source.\n\tpageSource := string(msg.Room)\n\n\tv.Add(pageSource)\n\n\tviewsCount := v.Get(pageSource).getCount()\n\tif viewsCount == 0 {\n\t\tviewsCount++ // count should be always > 0 here\n\t}\n\n\t// fire the \"onNewVisit\" client event\n\t// on each connection joined to this room (source page)\n\t// and notify of the new visit,\n\t// including this connection (see nil on first input arg).\n\tns.Conn.Server().Broadcast(nil, websocket.Message{\n\t\tNamespace: msg.Namespace,\n\t\tRoom:      pageSource,\n\t\tEvent:     \"onNewVisit\", // fire the \"onNewVisit\" client event.\n\t\tBody:      viewsCountBytes(viewsCount),\n\t})\n\n\treturn nil\n}\n\nfunc onRoomLeft(ns *websocket.NSConn, msg websocket.Message) error {\n\t// the roomName here is the source.\n\tpageV := v.Get(msg.Room)\n\tif pageV == nil {\n\t\treturn nil // for any case that this room is not a pageView source\n\t}\n\t// decrement -1 the specific counter for this page source.\n\tpageV.decrement()\n\n\t// fire the \"onNewVisit\" client event\n\t// on each connection joined to this room (source page)\n\t// and notify of the new, decremented by one, visits count.\n\tns.Conn.Server().Broadcast(nil, websocket.Message{\n\t\tNamespace: msg.Namespace,\n\t\tRoom:      msg.Room,\n\t\tEvent:     \"onNewVisit\",\n\t\tBody:      viewsCountBytes(pageV.getCount()),\n\t})\n\n\treturn nil\n}\n"
  },
  {
    "path": "_examples/websocket/online-visitors/static/assets/js/visitors.js",
    "content": "(function () {\n  var events = {\n    default: {\n      _OnNamespaceConnected: function (ns, msg) {\n        ns.joinRoom(PAGE_SOURCE);\n      },\n      _OnNamespaceDisconnect: function (ns, msg) {\n        document.getElementById(\"online_views\").innerHTML = \"you've been disconnected\";\n      },\n      onNewVisit: function (ns, msg) {\n        var text = \"1 online view\";\n        var onlineViews = Number(msg.Body);\n        if (onlineViews > 1) {\n          text = onlineViews + \" online views\";\n        }\n        document.getElementById(\"online_views\").innerHTML = text;\n      }\n    }\n  };\n\n  neffos.dial(\"ws://localhost:8080/my_endpoint\", events).then(function (client) {\n    client.connect(\"default\");\n  });\n})();\n"
  },
  {
    "path": "_examples/websocket/online-visitors/templates/index.html",
    "content": "<html>\n\n<head>\n    <title>Online visitors example</title>\n    <style>\n        body {\n            margin: 0;\n            font-family: -apple-system, \"San Francisco\", \"Helvetica Neue\", \"Noto\", \"Roboto\", \"Calibri Light\", sans-serif;\n            color: #212121;\n            font-size: 1.0em;\n            line-height: 1.6;\n        }\n\n        .container {\n            max-width: 750px;\n            margin: auto;\n            padding: 15px;\n        }\n\n        #online_views {\n            font-weight: bold;\n            font-size: 18px;\n        }\n    </style>\n</head>\n\n<body>\n    <div class=\"container\">\n        <span id=\"online_views\">1 online view</span>\n    </div>\n\n    <script type=\"text/javascript\">\n        /* take the page source from our passed struct  on .Render */\n        var PAGE_SOURCE = {{ .PageID }}\n    </script>\n\n    <script src=\"https://cdn.jsdelivr.net/npm/neffos.js@0.1.27/dist/neffos.min.js\"></script>\n\n    <script src=\"/js/visitors.js\"></script>\n\n</body>\n\n</html>"
  },
  {
    "path": "_examples/websocket/online-visitors/templates/other.html",
    "content": "<html>\n\n<head>\n    <title>Different page, different results</title>\n    <style>\n        #online_views {\n            font-weight: bold;\n            font-size: 18px;\n        }\n    </style>\n</head>\n\n<body>\n\n    <span id=\"online_views\">1 online view</span>\n\n\n    <script type=\"text/javascript\">\n        /* take the page source from our passed struct  on .Render */\n        var PAGE_SOURCE = {{ .PageID }}\n    </script>\n\n    <script src=\"https://cdn.jsdelivr.net/npm/neffos.js@0.1.27/dist/neffos.min.js\"></script>\n\n    <script src=\"/js/visitors.js\"></script>\n\n</body>\n\n</html>"
  },
  {
    "path": "_examples/websocket/secure/README.md",
    "content": "# Secure Websockets\n\n1. Run your server through `https://` (`iris.Run(iris.TLS)` or `iris.Run(iris.AutoTLS)` or a custom `iris.Listener(...)`)\n2. Nothing changes inside the whole app, including the websocket side\n3. The clients must dial the websocket server endpoint (i.e `/echo`) via `wss://` prefix (instead of the non-secure `ws://`), for example `wss://example.com/echo`\n4. Ready to GO.\n"
  },
  {
    "path": "_examples/websocket/socketio/asset/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n    <title>Socket.IO chat</title>\n    <style>\n        * {\n            margin: 0;\n            padding: 0;\n            box-sizing: border-box;\n        }\n\n        body {\n            font: 13px Helvetica, Arial;\n        }\n\n        form {\n            background: #000;\n            padding: 3px;\n            position: fixed;\n            bottom: 0;\n            width: 100%;\n        }\n\n        form input {\n            border: 0;\n            padding: 10px;\n            width: 90%;\n            margin-right: .5%;\n        }\n\n        form button {\n            width: 9%;\n            background: rgb(130, 224, 255);\n            border: none;\n            padding: 10px;\n        }\n\n        #messages {\n            list-style-type: none;\n            margin: 0;\n            padding: 0;\n        }\n\n        #messages li {\n            padding: 5px 10px;\n        }\n\n        #messages li:nth-child(odd) {\n            background: #eee;\n        }\n    </style>\n</head>\n\n<body>\n    <ul id=\"messages\"></ul>\n    <form action=\"\">\n        <input id=\"m\" autocomplete=\"off\" /><button>Send</button>\n    </form>\n    <script src=\"https://cdn.socket.io/socket.io-1.2.0.js\"></script>\n    <script src=\"https://code.jquery.com/jquery-1.11.1.js\"></script>\n    <script>\n        var socket = io();\n        // socket.emit('msg', 'hello');\n        var s2 = io(\"/chat\");\n        socket.on('reply', function (msg) {\n            $('#messages').append($('<li>').text(msg));\n        });\n        $('form').submit(function () {\n            s2.emit('msg', $('#m').val(), function (data) {\n                $('#messages').append($('<li>').text('ACK CALLBACK: ' + data));\n            });\n            socket.emit('notice', $('#m').val());\n            $('#m').val('');\n            return false;\n        });\n    </script>\n</body>\n\n</html>"
  },
  {
    "path": "_examples/websocket/socketio/go.mod",
    "content": "module github.com/kataras/iris/_examples/websocket/socketio\n\ngo 1.25\n\nrequire (\n\tgithub.com/googollee/go-socket.io v1.7.0\n\tgithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd\n)\n\nrequire (\n\tgithub.com/BurntSushi/toml v1.6.0 // indirect\n\tgithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect\n\tgithub.com/CloudyKit/jet/v6 v6.3.1 // indirect\n\tgithub.com/Joker/jade v1.1.3 // indirect\n\tgithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 // indirect\n\tgithub.com/andybalholm/brotli v1.2.0 // indirect\n\tgithub.com/aymerick/douceur v0.2.0 // indirect\n\tgithub.com/fatih/structs v1.1.0 // indirect\n\tgithub.com/flosch/pongo2/v4 v4.0.2 // indirect\n\tgithub.com/gofrs/uuid v4.0.0+incompatible // indirect\n\tgithub.com/golang/snappy v1.0.0 // indirect\n\tgithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a // indirect\n\tgithub.com/gomodule/redigo v1.8.4 // indirect\n\tgithub.com/google/uuid v1.6.0 // indirect\n\tgithub.com/gorilla/css v1.0.1 // indirect\n\tgithub.com/gorilla/websocket v1.5.3 // indirect\n\tgithub.com/iris-contrib/schema v0.0.6 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/kataras/blocks v0.0.8 // indirect\n\tgithub.com/kataras/golog v0.1.12 // indirect\n\tgithub.com/kataras/pio v0.0.13 // indirect\n\tgithub.com/kataras/sitemap v0.0.6 // indirect\n\tgithub.com/kataras/tunnel v0.0.4 // indirect\n\tgithub.com/klauspost/compress v1.18.2 // indirect\n\tgithub.com/kr/pretty v0.3.1 // indirect\n\tgithub.com/mailgun/raymond/v2 v2.0.48 // indirect\n\tgithub.com/mailru/easyjson v0.9.1 // indirect\n\tgithub.com/microcosm-cc/bluemonday v1.0.27 // indirect\n\tgithub.com/rogpeppe/go-internal v1.10.0 // indirect\n\tgithub.com/russross/blackfriday/v2 v2.1.0 // indirect\n\tgithub.com/schollz/closestmatch v2.1.0+incompatible // indirect\n\tgithub.com/sirupsen/logrus v1.8.1 // indirect\n\tgithub.com/stretchr/testify v1.11.1 // indirect\n\tgithub.com/tdewolff/minify/v2 v2.24.8 // indirect\n\tgithub.com/tdewolff/parse/v2 v2.8.5 // indirect\n\tgithub.com/valyala/bytebufferpool v1.0.0 // indirect\n\tgithub.com/vmihailenco/msgpack/v5 v5.4.1 // indirect\n\tgithub.com/vmihailenco/tagparser/v2 v2.0.0 // indirect\n\tgithub.com/yosssi/ace v0.0.5 // indirect\n\tgolang.org/x/crypto v0.47.0 // indirect\n\tgolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 // indirect\n\tgolang.org/x/net v0.49.0 // indirect\n\tgolang.org/x/sys v0.40.0 // indirect\n\tgolang.org/x/text v0.33.0 // indirect\n\tgolang.org/x/time v0.14.0 // indirect\n\tgoogle.golang.org/protobuf v1.36.11 // indirect\n\tgopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect\n\tgopkg.in/ini.v1 v1.67.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n"
  },
  {
    "path": "_examples/websocket/socketio/go.sum",
    "content": "github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=\ngithub.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=\ngithub.com/CloudyKit/jet/v6 v6.3.1 h1:6IAo5Cx21xrHVaR8zzXN5gJatKV/wO7Nf6bfCnCSbUw=\ngithub.com/CloudyKit/jet/v6 v6.3.1/go.mod h1:lf8ksdNsxZt7/yH/3n4vJQWA9RUq4wpaHtArHhGVMOw=\ngithub.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc=\ngithub.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=\ngithub.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=\ngithub.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 h1:dG7/gLroJGht/jSQtHiLvT48Hxn+crbmvyItZC8cWOs=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05/go.mod h1:NYezi6wtnJtBm5btoprXc5SvAdqH0XTXWnUup0MptAI=\ngithub.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=\ngithub.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=\ngithub.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=\ngithub.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=\ngithub.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=\ngithub.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=\ngithub.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=\ngithub.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=\ngithub.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=\ngithub.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw=\ngithub.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8=\ngithub.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=\ngithub.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=\ngithub.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=\ngithub.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=\ngithub.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=\ngithub.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a h1:l7A0loSszR5zHd/qK53ZIHMO8b3bBSmENnQ6eKnUT0A=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=\ngithub.com/gomodule/redigo v1.8.4 h1:Z5JUg94HMTR1XpwBaSH4vq3+PNSIykBLxMdglbw10gg=\ngithub.com/gomodule/redigo v1.8.4/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=\ngithub.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/googollee/go-socket.io v1.7.0 h1:ODcQSAvVIPvKozXtUGuJDV3pLwdpBLDs1Uoq/QHIlY8=\ngithub.com/googollee/go-socket.io v1.7.0/go.mod h1:0vGP8/dXR9SZUMMD4+xxaGo/lohOw3YWMh2WRiWeKxg=\ngithub.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=\ngithub.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=\ngithub.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=\ngithub.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=\ngithub.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2 h1:T9THsdP1woyAqKHwjkEsbCnMefsAFvk8iJJKokcJ3Go=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2/go.mod h1:JLDgIqnFy5loDSUv1OA2j0mb6p/rDhiCqigP22Uq9xE=\ngithub.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw=\ngithub.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/kataras/blocks v0.0.8 h1:MrpVhoFTCR2v1iOOfGng5VJSILKeZZI+7NGfxEh3SUM=\ngithub.com/kataras/blocks v0.0.8/go.mod h1:9Jm5zx6BB+06NwA+OhTbHW1xkMOYxahnqTN5DveZ2Yg=\ngithub.com/kataras/golog v0.1.12 h1:Bu7I/G4ilJlbfzjmU39O9N+2uO1pBcMK045fzZ4ytNg=\ngithub.com/kataras/golog v0.1.12/go.mod h1:wrGSbOiBqbQSQznleVNX4epWM8rl9SJ/rmEacl0yqy4=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd h1:oMsQgqKACbUdlaIWnN+EZzzAnHkw90hE/y/R0BSij2U=\ngithub.com/kataras/iris/v12 v12.2.11-0.20251225090712-30c1c6b10edd/go.mod h1:yucjtDl9Vn3OctWM1fi0MuM9OckemC7kEreFa9QtPF8=\ngithub.com/kataras/pio v0.0.13 h1:x0rXVX0fviDTXOOLOmr4MUxOabu1InVSTu5itF8CXCM=\ngithub.com/kataras/pio v0.0.13/go.mod h1:k3HNuSw+eJ8Pm2lA4lRhg3DiCjVgHlP8hmXApSej3oM=\ngithub.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY=\ngithub.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4=\ngithub.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA=\ngithub.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw=\ngithub.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=\ngithub.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=\ngithub.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=\ngithub.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8=\ngithub.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=\ngithub.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=\ngithub.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=\ngithub.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=\ngithub.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=\ngithub.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=\ngithub.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=\ngithub.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=\ngithub.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=\ngithub.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=\ngithub.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=\ngithub.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=\ngithub.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk=\ngithub.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=\ngithub.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=\ngithub.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=\ngithub.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=\ngithub.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/tdewolff/minify/v2 v2.24.8 h1:58/VjsbevI4d5FGV0ZSuBrHMSSkH4MCH0sIz/eKIauE=\ngithub.com/tdewolff/minify/v2 v2.24.8/go.mod h1:0Ukj0CRpo/sW/nd8uZ4ccXaV1rEVIWA3dj8U7+Shhfw=\ngithub.com/tdewolff/parse/v2 v2.8.5 h1:ZmBiA/8Do5Rpk7bDye0jbbDUpXXbCdc3iah4VeUvwYU=\ngithub.com/tdewolff/parse/v2 v2.8.5/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=\ngithub.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE=\ngithub.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=\ngithub.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=\ngithub.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=\ngithub.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=\ngithub.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=\ngithub.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=\ngithub.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=\ngithub.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA=\ngithub.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0=\ngithub.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=\ngithub.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=\ngithub.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=\ngolang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0=\ngolang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=\ngolang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=\ngolang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=\ngolang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=\ngolang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=\ngolang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=\ngolang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=\ngolang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=\ngoogle.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=\ngopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nmoul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs=\nmoul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE=\n"
  },
  {
    "path": "_examples/websocket/socketio/main.go",
    "content": "// Package main runs a go-socket.io based websocket server.\n// An Iris compatible clone of: https://github.com/googollee/go-socket.io#example,\n// use of `iris.FromStd` to convert its handler.\npackage main\n\nimport (\n\t\"fmt\"\n\n\tsocketio \"github.com/googollee/go-socket.io\"\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc main() {\n\tapp := iris.New()\n\tserver := socketio.NewServer(nil)\n\tserver.OnConnect(\"/\", func(s socketio.Conn) error {\n\t\ts.SetContext(\"\")\n\t\tfmt.Println(\"connected:\", s.ID())\n\t\treturn nil\n\t})\n\tserver.OnEvent(\"/\", \"notice\", func(s socketio.Conn, msg string) {\n\t\tfmt.Println(\"notice:\", msg)\n\t\ts.Emit(\"reply\", \"have \"+msg)\n\t})\n\tserver.OnEvent(\"/chat\", \"msg\", func(s socketio.Conn, msg string) string {\n\t\ts.SetContext(msg)\n\t\treturn \"recv \" + msg\n\t})\n\tserver.OnEvent(\"/\", \"bye\", func(s socketio.Conn) string {\n\t\tlast := s.Context().(string)\n\t\ts.Emit(\"bye\", last)\n\t\ts.Close()\n\t\treturn last\n\t})\n\tserver.OnError(\"/\", func(s socketio.Conn, e error) {\n\t\tfmt.Println(\"meet error:\", e)\n\t})\n\tserver.OnDisconnect(\"/\", func(s socketio.Conn, reason string) {\n\t\tfmt.Println(\"closed\", reason)\n\t})\n\tgo server.Serve()\n\tdefer server.Close()\n\n\tapp.HandleMany(\"GET POST\", \"/socket.io/{any:path}\", iris.FromStd(server))\n\tapp.HandleDir(\"/\", iris.Dir(\"./asset\"))\n\n\tapp.Listen(\":8000\", iris.WithoutPathCorrection)\n}\n\n/*\nIf you want to enable CORS in your websocket handler,\nplease follow this post: https://github.com/googollee/go-socket.io/issues/242\n*/\n"
  },
  {
    "path": "_proposals/route_builder.md",
    "content": "```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/kataras/iris/v12/macro\"\n)\n\nfunc main() {\n\tpath := NewRouteBuilder().\n\t\tPath(\"/user\").\n\t\tString(\"name\", \"prefix(ma)\", \"suffix(kis)\").\n\t\tInt(\"age\").\n\t\tPath(\"/friends\").\n\t\tWildcard(\"rest\").\n\t\tBuild()\n\n\tfmt.Println(path)\n}\n\ntype RouteBuilder struct {\n\tpath string\n}\n\nfunc NewRouteBuilder() *RouteBuilder {\n\treturn &RouteBuilder{\n\t\tpath: \"/\",\n\t}\n}\n\nfunc (r *RouteBuilder) Path(path string) *RouteBuilder {\n\tif path[0] != '/' {\n\t\tpath = \"/\" + path\n\t}\n\n\tr.path = strings.TrimSuffix(r.path, \"/\") + path\n\treturn r\n}\n\ntype StaticPathBuilder interface {\n\tPath(path string) *RouteBuilder\n}\n\nfunc (r *RouteBuilder) Param(param ParamBuilder) *RouteBuilder { // StaticPathBuilder {\n\tpath := \"\" // keep it here, a single call to r.Path must be done.\n\tif len(r.path) == 0 || r.path[len(r.path)-1] != '/' {\n\t\tpath += \"/\" // if for some reason no prior Path(\"/\") was called for delimeter between path parameter.\n\t}\n\n\tpath += fmt.Sprintf(\"{%s:%s\", param.GetName(), param.GetParamType().Indent())\n\tif funcs := param.GetFuncs(); len(funcs) > 0 {\n\t\tpath += fmt.Sprintf(\" %s\", strings.Join(funcs, \" \"))\n\t}\n\tpath += \"}\"\n\n\treturn r.Path(path)\n}\n\nfunc (r *RouteBuilder) String(name string, funcs ...string) *RouteBuilder {\n\treturn r.Param(Param(macro.String, name, funcs...))\n}\n\nfunc (r *RouteBuilder) Int(name string, funcs ...string) *RouteBuilder {\n\treturn r.Param(Param(macro.Int, name, funcs...))\n}\n\nfunc (r *RouteBuilder) Int8(name string, funcs ...string) *RouteBuilder {\n\treturn r.Param(Param(macro.Int8, name, funcs...))\n}\n\nfunc (r *RouteBuilder) Int16(name string, funcs ...string) *RouteBuilder {\n\treturn r.Param(Param(macro.Int16, name, funcs...))\n}\n\nfunc (r *RouteBuilder) Int32(name string, funcs ...string) *RouteBuilder {\n\treturn r.Param(Param(macro.Int32, name, funcs...))\n}\n\nfunc (r *RouteBuilder) Int64(name string, funcs ...string) *RouteBuilder {\n\treturn r.Param(Param(macro.Int64, name, funcs...))\n}\n\nfunc (r *RouteBuilder) Uint(name string, funcs ...string) *RouteBuilder {\n\treturn r.Param(Param(macro.Uint, name, funcs...))\n}\n\nfunc (r *RouteBuilder) Uint8(name string, funcs ...string) *RouteBuilder {\n\treturn r.Param(Param(macro.Uint8, name, funcs...))\n}\n\nfunc (r *RouteBuilder) Uint16(name string, funcs ...string) *RouteBuilder {\n\treturn r.Param(Param(macro.Uint16, name, funcs...))\n}\n\nfunc (r *RouteBuilder) Uint32(name string, funcs ...string) *RouteBuilder {\n\treturn r.Param(Param(macro.Uint32, name, funcs...))\n}\n\nfunc (r *RouteBuilder) Uint64(name string, funcs ...string) *RouteBuilder {\n\treturn r.Param(Param(macro.Uint64, name, funcs...))\n}\n\nfunc (r *RouteBuilder) Bool(name string, funcs ...string) *RouteBuilder {\n\treturn r.Param(Param(macro.Bool, name, funcs...))\n}\n\nfunc (r *RouteBuilder) Alphabetical(name string, funcs ...string) *RouteBuilder {\n\treturn r.Param(Param(macro.Alphabetical, name, funcs...))\n}\n\nfunc (r *RouteBuilder) File(name string, funcs ...string) *RouteBuilder {\n\treturn r.Param(Param(macro.File, name, funcs...))\n}\n\nfunc (r *RouteBuilder) Wildcard(name string, funcs ...string) *RouteBuilder {\n\treturn r.Param(Param(macro.Path, name, funcs...))\n}\n\nfunc (r *RouteBuilder) UUID(name string, funcs ...string) *RouteBuilder {\n\treturn r.Param(Param(macro.UUID, name, funcs...))\n}\n\nfunc (r *RouteBuilder) Mail(name string, funcs ...string) *RouteBuilder {\n\treturn r.Param(Param(macro.Mail, name, funcs...))\n}\n\nfunc (r *RouteBuilder) Email(name string, funcs ...string) *RouteBuilder {\n\treturn r.Param(Param(macro.Email, name, funcs...))\n}\n\nfunc (r *RouteBuilder) Date(name string, funcs ...string) *RouteBuilder {\n\treturn r.Param(Param(macro.Date, name, funcs...))\n}\n\nfunc (r *RouteBuilder) Weekday(name string, funcs ...string) *RouteBuilder {\n\treturn r.Param(Param(macro.Weekday, name, funcs...))\n}\n\nfunc (r *RouteBuilder) Build() string {\n\treturn r.path\n}\n\ntype ParamBuilder interface {\n\tGetName() string\n\tGetFuncs() []string\n\tGetParamType() *macro.Macro\n}\n\ntype pathParam struct {\n\tName      string\n\tFuncs     []string\n\tParamType *macro.Macro\n}\n\nvar _ ParamBuilder = (*pathParam)(nil)\n\nfunc Param(paramType *macro.Macro, name string, funcs ...string) ParamBuilder {\n\treturn &pathParam{\n\t\tName:      name,\n\t\tParamType: paramType,\n\t\tFuncs:     funcs,\n\t}\n}\n\nfunc (p *pathParam) GetName() string {\n\treturn p.Name\n}\n\nfunc (p *pathParam) GetParamType() *macro.Macro {\n\treturn p.ParamType\n}\n\nfunc (p *pathParam) GetFuncs() []string {\n\treturn p.Funcs\n}\n```\n"
  },
  {
    "path": "_proposals/xerrors_party.md",
    "content": "```go\napp.PartyConfigure(\"/api\", errors.NewParty[CreateRequest, CreateResponse, ListFilter]().\n\tCreate(service.Create).\n\tUpdate(service.Update).\n\tDelete(service.DeleteWithFeedback).\n\tList(service.ListPaginated).\n\tGet(service.GetByID).Validation(validateCreateRequest))\n```\n\n```go\ntype Party[T, R, F any] struct {\n\tvalidators         []ContextRequestFunc[T]\n\tfilterValidators   []ContextRequestFunc[F]\n\tfilterIntercepters []ContextResponseFunc[F, R]\n\tintercepters       []ContextResponseFunc[T, R]\n\n\tserviceCreateFunc func(stdContext.Context, T) (R, error)\n\tserviceUpdateFunc func(stdContext.Context, T) (bool, error)\n\tserviceDeleteFunc func(stdContext.Context, string) (bool, error)\n\tserviceListFunc   func(stdContext.Context, pagination.ListOptions, F /* filter options */) ([]R, int, error)\n\tserviceGetFunc    func(stdContext.Context, string) (R, error)\n}\n\nfunc (p *Party[T, R, F]) Configure(r router.Party) {\n\tif p.serviceCreateFunc != nil {\n\t\tr.Post(\"/\", Validation(p.validators...), Intercept(p.intercepters...), CreateHandler(p.serviceCreateFunc))\n\t}\n\n\tif p.serviceUpdateFunc != nil {\n\t\tr.Put(\"/{id:string}\", Validation(p.validators...), Intercept(p.intercepters...), NoContentOrNotModifiedHandler(p.serviceUpdateFunc))\n\t}\n\n\tif p.serviceListFunc != nil {\n\t\tr.Post(\"/list\", Validation(p.filterValidators...), Intercept(p.filterIntercepters...), ListHandler(p.serviceListFunc))\n\t}\n\n\tif p.serviceDeleteFunc != nil {\n\t\tr.Delete(\"/{id:string}\", NoContentOrNotModifiedHandler(p.serviceDeleteFunc, PathParam[string](\"id\")))\n\t}\n\n\tif p.serviceGetFunc != nil {\n\t\tr.Get(\"/{id:string}\", Handler(p.serviceGetFunc, PathParam[string](\"id\")))\n\t}\n}\n\nfunc NewParty[T, R, F any]() *Party[T, R, F] {\n\treturn &Party[T, R, F]{}\n}\n\nfunc (p *Party[T, R, F]) Validation(validators ...ContextRequestFunc[T]) *Party[T, R, F] {\n\tp.validators = append(p.validators, validators...)\n\treturn p\n}\n\nfunc (p *Party[T, R, F]) FilterValidation(filterValidators ...ContextRequestFunc[F]) *Party[T, R, F] {\n\tp.filterValidators = append(p.filterValidators, filterValidators...)\n\treturn p\n}\n\nfunc (p *Party[T, R, F]) Intercept(intercepters ...ContextResponseFunc[T, R]) *Party[T, R, F] {\n\tp.intercepters = append(p.intercepters, intercepters...)\n\treturn p\n}\n\nfunc (p *Party[T, R, F]) FilterIntercept(filterIntercepters ...ContextResponseFunc[F, R]) *Party[T, R, F] {\n\tp.filterIntercepters = append(p.filterIntercepters, filterIntercepters...)\n\treturn p\n}\n\nfunc (p *Party[T, R, F]) Create(fn func(stdContext.Context, T) (R, error)) *Party[T, R, F] {\n\tp.serviceCreateFunc = fn\n\treturn p\n}\n\nfunc (p *Party[T, R, F]) Update(fn func(stdContext.Context, T) (bool, error)) *Party[T, R, F] {\n\tp.serviceUpdateFunc = fn\n\treturn p\n}\n\nfunc (p *Party[T, R, F]) Delete(fn func(stdContext.Context, string) (bool, error)) *Party[T, R, F] {\n\tp.serviceDeleteFunc = fn\n\treturn p\n}\n\nfunc (p *Party[T, R, F]) List(fn func(stdContext.Context, pagination.ListOptions, F /* filter options */) ([]R, int, error)) *Party[T, R, F] {\n\tp.serviceListFunc = fn\n\treturn p\n}\n\nfunc (p *Party[T, R, F]) Get(fn func(stdContext.Context, string) (R, error)) *Party[T, R, F] {\n\tp.serviceGetFunc = fn\n\treturn p\n}\n\n```\n"
  },
  {
    "path": "aliases.go",
    "content": "package iris\n\nimport (\n\t\"io/fs\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"path\"\n\t\"regexp\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/cache\"\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/core/handlerconv\"\n\t\"github.com/kataras/iris/v12/core/host\"\n\t\"github.com/kataras/iris/v12/core/router\"\n\t\"github.com/kataras/iris/v12/hero\"\n\t\"github.com/kataras/iris/v12/view\"\n)\n\nvar (\n\t// BuildRevision holds the vcs commit id information of the program's build.\n\t// To display the Iris' version please use the iris.Version constant instead.\n\t// Available at go version 1.18+\n\tBuildRevision = context.BuildRevision\n\t// BuildTime holds the vcs commit time information of the program's build.\n\t// Available at go version 1.18+\n\tBuildTime = context.BuildTime\n)\n\n// SameSite attributes.\nconst (\n\tSameSiteDefaultMode = http.SameSiteDefaultMode\n\tSameSiteLaxMode     = http.SameSiteLaxMode\n\tSameSiteStrictMode  = http.SameSiteStrictMode\n\tSameSiteNoneMode    = http.SameSiteNoneMode\n)\n\ntype (\n\t// Context is the middle-man server's \"object\" for the clients.\n\t//\n\t// A New context is being acquired from a sync.Pool on each connection.\n\t// The Context is the most important thing on the iris's http flow.\n\t//\n\t// Developers send responses to the client's request through a Context.\n\t// Developers get request information from the client's request by a Context.\n\tContext = *context.Context\n\t// ViewEngine is an alias of `context.ViewEngine`.\n\t// See HTML, Blocks, Django, Jet, Pug, Ace, Handlebars and e.t.c.\n\tViewEngine = context.ViewEngine\n\t// UnmarshalerFunc a shortcut, an alias for the `context#UnmarshalerFunc` type\n\t// which implements the `context#Unmarshaler` interface for reading request's body\n\t// via custom decoders, most of them already implement the `context#UnmarshalerFunc`\n\t// like the json.Unmarshal, xml.Unmarshal, yaml.Unmarshal and every library which\n\t// follows the best practises and is aligned with the Go standards.\n\t//\n\t// See 'context#UnmarshalBody` for more.\n\t//\n\t// Example: https://github.com/kataras/iris/blob/main/_examples/request-body/read-custom-via-unmarshaler/main.go\n\tUnmarshalerFunc = context.UnmarshalerFunc\n\t// DecodeFunc is a generic type of decoder function.\n\t// When the returned error is not nil the decode operation\n\t// is terminated and the error is received by the ReadJSONStream method,\n\t// otherwise it continues to read the next available object.\n\t// Look the `Context.ReadJSONStream` method.\n\t//\n\t// Example: https://github.com/kataras/iris/blob/main/_examples/request-body/read-json-stream.\n\tDecodeFunc = context.DecodeFunc\n\t// A Handler responds to an HTTP request.\n\t// It writes reply headers and data to the Context.ResponseWriter() and then return.\n\t// Returning signals that the request is finished;\n\t// it is not valid to use the Context after or concurrently with the completion of the Handler call.\n\t//\n\t// Depending on the HTTP client software, HTTP protocol version,\n\t// and any intermediaries between the client and the iris server,\n\t// it may not be possible to read from the Context.Request().Body after writing to the context.ResponseWriter().\n\t// Cautious handlers should read the Context.Request().Body first, and then reply.\n\t//\n\t// Except for reading the body, handlers should not modify the provided Context.\n\t//\n\t// If Handler panics, the server (the caller of Handler) assumes that the effect of the panic was isolated to the active request.\n\t// It recovers the panic, logs a stack trace to the server error log, and hangs up the connection.\n\tHandler = context.Handler\n\t// Filter is just a type of func(Context) bool which reports whether an action must be performed\n\t// based on the incoming request.\n\t//\n\t// See `NewConditionalHandler` for more.\n\t// An alias for the `context/Filter`.\n\tFilter = context.Filter\n\t// A Map is an alias of map[string]any.\n\tMap = context.Map\n\t// User is a generic view of an authorized client.\n\t// See `Context.User` and `SetUser` methods for more.\n\t// An alias for the `context/User` type.\n\tUser = context.User\n\t// SimpleUser is a simple implementation of the User interface.\n\tSimpleUser = context.SimpleUser\n\t// Problem Details for HTTP APIs.\n\t// Pass a Problem value to `context.Problem` to\n\t// write an \"application/problem+json\" response.\n\t//\n\t// Read more at: https://github.com/kataras/iris/blob/main/_examples/routing/http-errors.\n\t//\n\t// It is an alias of the `context#Problem` type.\n\tProblem = context.Problem\n\t// ProblemOptions the optional settings when server replies with a Problem.\n\t// See `Context.Problem` method and `Problem` type for more details.\n\t//\n\t// It is an alias of the `context#ProblemOptions` type.\n\tProblemOptions = context.ProblemOptions\n\t// JSON the optional settings for JSON renderer.\n\t//\n\t// It is an alias of the `context#JSON` type.\n\tJSON = context.JSON\n\t// JSONReader holds the JSON decode options of the `Context.ReadJSON, ReadBody` methods.\n\t//\n\t// It is an alias of the `context#JSONReader` type.\n\tJSONReader = context.JSONReader\n\t// JSONP the optional settings for JSONP renderer.\n\t//\n\t// It is an alias of the `context#JSONP` type.\n\tJSONP = context.JSONP\n\t// ProtoMarshalOptions is a type alias for protojson.MarshalOptions.\n\tProtoMarshalOptions = context.ProtoMarshalOptions\n\t// ProtoUnmarshalOptions is a type alias for protojson.UnmarshalOptions.\n\tProtoUnmarshalOptions = context.ProtoUnmarshalOptions\n\t// XML the optional settings for XML renderer.\n\t//\n\t// It is an alias of the `context#XML` type.\n\tXML = context.XML\n\t// Markdown the optional settings for Markdown renderer.\n\t// See `Context.Markdown` for more.\n\t//\n\t// It is an alias of the `context#Markdown` type.\n\tMarkdown = context.Markdown\n\t// Supervisor is a shortcut of the `host#Supervisor`.\n\t// Used to add supervisor configurators on common Runners\n\t// without the need of importing the `core/host` package.\n\tSupervisor = host.Supervisor\n\n\t// Party is just a group joiner of routes which have the same prefix and share same middleware(s) also.\n\t// Party could also be named as 'Join' or 'Node' or 'Group' , Party chosen because it is fun.\n\t//\n\t// Look the `core/router#APIBuilder` for its implementation.\n\t//\n\t// A shortcut for the `core/router#Party`, useful when `PartyFunc` is being used.\n\tParty = router.Party\n\t// APIContainer is a wrapper of a common `Party` featured by Dependency Injection.\n\t// See `Party.ConfigureContainer` for more.\n\t//\n\t// A shortcut for the `core/router#APIContainer`.\n\tAPIContainer = router.APIContainer\n\t// ResultHandler describes the function type which should serve the \"v\" struct value.\n\t// See `APIContainer.UseResultHandler`.\n\tResultHandler = hero.ResultHandler\n\n\t// DirOptions contains the optional settings that\n\t// `FileServer` and `Party#HandleDir` can use to serve files and assets.\n\t// A shortcut for the `router.DirOptions`, useful when `FileServer` or `HandleDir` is being used.\n\tDirOptions = router.DirOptions\n\t// DirCacheOptions holds the options for the cached file system.\n\t// See `DirOptions`.\n\tDirCacheOptions = router.DirCacheOptions\n\t// DirListRichOptions the options for the `DirListRich` helper function.\n\t// A shortcut for the `router.DirListRichOptions`.\n\t// Useful when `DirListRich` function is passed to `DirOptions.DirList` field.\n\tDirListRichOptions = router.DirListRichOptions\n\t// Attachments options for files to be downloaded and saved locally by the client.\n\t// See `DirOptions`.\n\tAttachments = router.Attachments\n\t// Dir implements FileSystem using the native file system restricted to a\n\t// specific directory tree, can be passed to the `FileServer` function\n\t// and `HandleDir` method. It's an alias of `http.Dir`.\n\tDir = http.Dir\n\n\t// ExecutionRules gives control to the execution of the route handlers outside of the handlers themselves.\n\t// Usage:\n\t// Party#SetExecutionRules(ExecutionRules {\n\t//   Done: ExecutionOptions{Force: true},\n\t// })\n\t//\n\t// See `core/router/Party#SetExecutionRules` for more.\n\t// Example: https://github.com/kataras/iris/tree/main/_examples/mvc/middleware/without-ctx-next\n\tExecutionRules = router.ExecutionRules\n\t// ExecutionOptions is a set of default behaviors that can be changed in order to customize the execution flow of the routes' handlers with ease.\n\t//\n\t// See `ExecutionRules` and `core/router/Party#SetExecutionRules` for more.\n\tExecutionOptions = router.ExecutionOptions\n\n\t// CookieOption is the type of function that is accepted on\n\t// context's methods like `SetCookieKV`, `RemoveCookie` and `SetCookie`\n\t// as their (last) variadic input argument to amend the end cookie's form.\n\t//\n\t// Any custom or builtin `CookieOption` is valid,\n\t// see `CookiePath`, `CookieCleanPath`, `CookieExpires` and `CookieHTTPOnly` for more.\n\t//\n\t// An alias for the `context.CookieOption`.\n\tCookieOption = context.CookieOption\n\t// Cookie is a type alias for the standard net/http Cookie struct type.\n\t// See `Context.SetCookie`.\n\tCookie = http.Cookie\n\t// N is a struct which can be passed on the `Context.Negotiate` method.\n\t// It contains fields which should be filled based on the `Context.Negotiation()`\n\t// server side values. If no matched mime then its \"Other\" field will be sent,\n\t// which should be a string or []byte.\n\t// It completes the `context/context.ContentSelector` interface.\n\t//\n\t// An alias for the `context.N`.\n\tN = context.N\n\t// Locale describes the i18n locale.\n\t// An alias for the `context.Locale`.\n\tLocale = context.Locale\n\t// ErrPrivate if provided then the error saved in context\n\t// should NOT be visible to the client no matter what.\n\t// An alias for the `context.ErrPrivate`.\n\tErrPrivate = context.ErrPrivate\n)\n\n// Constants for input argument at `router.RouteRegisterRule`.\n// See `Party#SetRegisterRule`.\nconst (\n\t// RouteOverride replaces an existing route with the new one, the default rule.\n\tRouteOverride = router.RouteOverride\n\t// RouteSkip keeps the original route and skips the new one.\n\tRouteSkip = router.RouteSkip\n\t// RouteError log when a route already exists, shown after the `Build` state,\n\t// server never starts.\n\tRouteError = router.RouteError\n\t// RouteOverlap will overlap the new route to the previous one.\n\t// If the route stopped and its response can be reset then the new route will be execute.\n\tRouteOverlap = router.RouteOverlap\n)\n\n// Contains the enum values of the `Context.GetReferrer()` method,\n// shortcuts of the context subpackage.\nconst (\n\tReferrerInvalid  = context.ReferrerInvalid\n\tReferrerIndirect = context.ReferrerIndirect\n\tReferrerDirect   = context.ReferrerDirect\n\tReferrerEmail    = context.ReferrerEmail\n\tReferrerSearch   = context.ReferrerSearch\n\tReferrerSocial   = context.ReferrerSocial\n\n\tReferrerNotGoogleSearch     = context.ReferrerNotGoogleSearch\n\tReferrerGoogleOrganicSearch = context.ReferrerGoogleOrganicSearch\n\tReferrerGoogleAdwords       = context.ReferrerGoogleAdwords\n)\n\n// NoLayout to disable layout for a particular template file\n// A shortcut for the `view#NoLayout`.\nconst NoLayout = view.NoLayout\n\nvar (\n\t// HTML view engine.\n\t// Shortcut of the view.HTML.\n\tHTML = view.HTML\n\t// Blocks view engine.\n\t// Can be used as a faster alternative of the HTML engine.\n\t// Shortcut of the view.Blocks.\n\tBlocks = view.Blocks\n\t// Django view engine.\n\t// Shortcut of the view.Django.\n\tDjango = view.Django\n\t// Handlebars view engine.\n\t// Shortcut of the view.Handlebars.\n\tHandlebars = view.Handlebars\n\t// Pug view engine.\n\t// Shortcut of the view.Pug.\n\tPug = view.Pug\n\t// Jet view engine.\n\t// Shortcut of the view.Jet.\n\tJet = view.Jet\n\t// Ace view engine.\n\t// Shortcut of the view.Ace.\n\tAce = view.Ace\n)\n\ntype (\n\t// ErrViewNotExist reports whether a template was not found in the parsed templates tree.\n\tErrViewNotExist = context.ErrViewNotExist\n\t// FallbackViewFunc is a function that can be registered\n\t// to handle view fallbacks. It accepts the Context and\n\t// a special error which contains information about the previous template error.\n\t// It implements the FallbackViewProvider interface.\n\t//\n\t// See `Context.View` method.\n\tFallbackViewFunc = context.FallbackViewFunc\n\t// FallbackView is a helper to register a single template filename as a fallback\n\t// when the provided tempate filename was not found.\n\tFallbackView = context.FallbackView\n\t// FallbackViewLayout is a helper to register a single template filename as a fallback\n\t// layout when the provided layout filename was not found.\n\tFallbackViewLayout = context.FallbackViewLayout\n)\n\n// Component returns a new Handler which can be registered as a main handler for a route.\n// It's a shortcut handler that renders the given component as HTML through Context.RenderComponent.\nfunc Component(component context.Component) Handler {\n\treturn func(ctx Context) {\n\t\tctx.RenderComponent(component)\n\t}\n}\n\n// PrefixDir returns a new FileSystem that opens files\n// by adding the given \"prefix\" to the directory tree of \"fs\".\n//\n// Useful when having templates and static files in the same\n// bindata AssetFile method. This way you can select\n// which one to serve as static files and what for templates.\n// All view engines have a `RootDir` method for that reason too\n// but alternatively, you can wrap the given file system with this `PrefixDir`.\n//\n// Example: https://github.com/kataras/iris/blob/main/_examples/file-server/single-page-application/embedded-single-page-application/main.go\nfunc PrefixDir(prefix string, fs http.FileSystem) http.FileSystem {\n\treturn &prefixedDir{prefix, fs}\n}\n\n// PrefixFS same as \"PrefixDir\" but for `fs.FS` type.\nfunc PrefixFS(fileSystem fs.FS, dir string) (fs.FS, error) {\n\treturn fs.Sub(fileSystem, dir)\n}\n\ntype prefixedDir struct {\n\tprefix string\n\tfs     http.FileSystem\n}\n\nfunc (p *prefixedDir) Open(name string) (http.File, error) {\n\t// Don't do this: as this is responsibility of the underline fs.\n\t// _, filename, ok, err := context.SafeFilename(\"\", name)\n\n\tdestPath := path.Join(p.prefix, name)\n\treturn p.fs.Open(destPath)\n}\n\ntype partyConfiguratorMiddleware struct {\n\thandlers []Handler\n}\n\nfunc (p *partyConfiguratorMiddleware) Configure(r Party) {\n\tr.Use(p.handlers...)\n}\n\n// ConfigureMiddleware is a PartyConfigurator which can be used\n// as a shortcut to add middlewares on Party.PartyConfigure(\"/path\", WithMiddleware(handler), new(example.API)).\nfunc ConfigureMiddleware(handlers ...Handler) router.PartyConfigurator {\n\treturn &partyConfiguratorMiddleware{handlers: handlers}\n}\n\n// Compression is a middleware which enables\n// writing and reading using the best offered compression.\n// Usage:\n// app.Use (for matched routes)\n// app.UseRouter (for both matched and 404s or other HTTP errors).\nfunc Compression(ctx Context) {\n\tctx.CompressWriter(true)\n\tctx.CompressReader(true)\n\tctx.Next()\n}\n\nvar (\n\t// AllowQuerySemicolons returns a middleware that serves requests by converting any\n\t// unescaped semicolons(;) in the URL query to ampersands(&).\n\t//\n\t// This restores the pre-Go 1.17 behavior of splitting query parameters on both\n\t// semicolons and ampersands.\n\t// (See golang.org/issue/25192 and https://github.com/kataras/iris/issues/1875).\n\t// Note that this behavior doesn't match that of many proxies,\n\t// and the mismatch can lead to security issues.\n\t//\n\t// AllowQuerySemicolons should be invoked before any Context read query or\n\t// form methods are called.\n\t//\n\t// To skip HTTP Server logging for this type of warning:\n\t// app.Listen/Run(..., iris.WithoutServerError(iris.ErrURLQuerySemicolon)).\n\tAllowQuerySemicolons = func(ctx Context) {\n\t\t// clopy of net/http.AllowQuerySemicolons.\n\t\tr := ctx.Request()\n\t\tif s := r.URL.RawQuery; strings.Contains(s, \";\") {\n\t\t\tr2 := new(http.Request)\n\t\t\t*r2 = *r\n\t\t\tr2.URL = new(url.URL)\n\t\t\t*r2.URL = *r.URL\n\t\t\tr2.URL.RawQuery = strings.ReplaceAll(s, \";\", \"&\")\n\t\t\tctx.ResetRequest(r2)\n\t\t}\n\n\t\tctx.Next()\n\t}\n\n\t// MatchImagesAssets is a simple regex expression\n\t// that can be passed to the DirOptions.Cache.CompressIgnore field\n\t// in order to skip compression on already-compressed file types\n\t// such as images and pdf.\n\tMatchImagesAssets = regexp.MustCompile(\"((.*).pdf|(.*).jpg|(.*).jpeg|(.*).gif|(.*).tif|(.*).tiff)$\")\n\t// MatchCommonAssets is a simple regex expression which\n\t// can be used on `DirOptions.PushTargetsRegexp`.\n\t// It will match and Push\n\t// all available js, css, font and media files.\n\t// Ideal for Single Page Applications.\n\tMatchCommonAssets = regexp.MustCompile(\"((.*).js|(.*).css|(.*).ico|(.*).png|(.*).ttf|(.*).svg|(.*).webp|(.*).gif)$\")\n)\n\nvar (\n\t// RegisterOnInterrupt registers a global function to call when CTRL+C/CMD+C pressed or a unix kill command received.\n\t//\n\t// A shortcut for the `host#RegisterOnInterrupt`.\n\tRegisterOnInterrupt = host.RegisterOnInterrupt\n\n\t// LimitRequestBodySize is a middleware which sets a request body size limit\n\t// for all next handlers in the chain.\n\t//\n\t// A shortcut for the `context#LimitRequestBodySize`.\n\tLimitRequestBodySize = context.LimitRequestBodySize\n\t// NewConditionalHandler returns a single Handler which can be registered\n\t// as a middleware.\n\t// Filter is just a type of Handler which returns a boolean.\n\t// Handlers here should act like middleware, they should contain `ctx.Next` to proceed\n\t// to the next handler of the chain. Those \"handlers\" are registered to the per-request context.\n\t//\n\t//\n\t// It checks the \"filter\" and if passed then\n\t// it, correctly, executes the \"handlers\".\n\t//\n\t// If passed, this function makes sure that the Context's information\n\t// about its per-request handler chain based on the new \"handlers\" is always updated.\n\t//\n\t// If not passed, then simply the Next handler(if any) is executed and \"handlers\" are ignored.\n\t// Example can be found at: _examples/routing/conditional-chain.\n\t//\n\t// A shortcut for the `context#NewConditionalHandler`.\n\tNewConditionalHandler = context.NewConditionalHandler\n\t// FileServer returns a Handler which serves files from a specific system, phyisical, directory\n\t// or an embedded one.\n\t// The first parameter is the directory, relative to the executable program.\n\t// The second optional parameter is any optional settings that the caller can use.\n\t//\n\t// See `Party#HandleDir` too.\n\t// Examples can be found at: https://github.com/kataras/iris/tree/main/_examples/file-server\n\t// A shortcut for the `router.FileServer`.\n\tFileServer = router.FileServer\n\t// DirList is the default `DirOptions.DirList` field.\n\t// Read more at: `core/router.DirList`.\n\tDirList = router.DirList\n\t// DirListRich can be passed to `DirOptions.DirList` field\n\t// to override the default file listing appearance.\n\t// Read more at: `core/router.DirListRich`.\n\tDirListRich = router.DirListRich\n\t// StripPrefix returns a handler that serves HTTP requests\n\t// by removing the given prefix from the request URL's Path\n\t// and invoking the handler h. StripPrefix handles a\n\t// request for a path that doesn't begin with prefix by\n\t// replying with an HTTP 404 not found error.\n\t//\n\t// Usage:\n\t// fileserver := iris.FileServer(\"./static_files\", DirOptions {...})\n\t// h := iris.StripPrefix(\"/static\", fileserver)\n\t// app.Get(\"/static/{file:path}\", h)\n\t// app.Head(\"/static/{file:path}\", h)\n\tStripPrefix = router.StripPrefix\n\t// FromStd converts native http.Handler, http.HandlerFunc & func(w, r, next) to context.Handler.\n\t//\n\t// Supported form types:\n\t// \t\t .FromStd(h http.Handler)\n\t// \t\t .FromStd(func(w http.ResponseWriter, r *http.Request))\n\t// \t\t .FromStd(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc))\n\t//\n\t// A shortcut for the `handlerconv#FromStd`.\n\tFromStd = handlerconv.FromStd\n\t// Cache is a middleware providing server-side cache functionalities\n\t// to the next handlers, can be used as: `app.Get(\"/\", iris.Cache, aboutHandler)`.\n\t// It should be used after Static methods.\n\t// See `iris#Cache304` for an alternative, faster way.\n\t//\n\t// Examples can be found at: https://github.com/kataras/iris/tree/main/_examples/#caching\n\tCache = cache.Handler\n\t// NoCache is a middleware which overrides the Cache-Control, Pragma and Expires headers\n\t// in order to disable the cache during the browser's back and forward feature.\n\t//\n\t// A good use of this middleware is on HTML routes; to refresh the page even on \"back\" and \"forward\" browser's arrow buttons.\n\t//\n\t// See `iris#StaticCache` for the opposite behavior.\n\t//\n\t// A shortcut of the `cache#NoCache`\n\tNoCache = cache.NoCache\n\t// StaticCache middleware for caching static files by sending the \"Cache-Control\" and \"Expires\" headers to the client.\n\t// It accepts a single input parameter, the \"cacheDur\", a time.Duration that it's used to calculate the expiration.\n\t//\n\t// If \"cacheDur\" <=0 then it returns the `NoCache` middleware instaed to disable the caching between browser's \"back\" and \"forward\" actions.\n\t//\n\t// Usage: `app.Use(iris.StaticCache(24 * time.Hour))` or `app.Use(iris.StaticCache(-1))`.\n\t// A middleware, which is a simple Handler can be called inside another handler as well, example:\n\t// cacheMiddleware := iris.StaticCache(...)\n\t// func(ctx iris.Context){\n\t//  cacheMiddleware(ctx)\n\t//  [...]\n\t// }\n\t//\n\t// A shortcut of the `cache#StaticCache`\n\tStaticCache = cache.StaticCache\n\t// Cache304 sends a `StatusNotModified` (304) whenever\n\t// the \"If-Modified-Since\" request header (time) is before the\n\t// time.Now() + expiresEvery (always compared to their UTC values).\n\t// Use this, which is a shortcut of the, `chache#Cache304` instead of the \"github.com/kataras/iris/v12/cache\" or iris.Cache\n\t// for better performance.\n\t// Clients that are compatible with the http RCF (all browsers are and tools like postman)\n\t// will handle the caching.\n\t// The only disadvantage of using that instead of server-side caching\n\t// is that this method will send a 304 status code instead of 200,\n\t// So, if you use it side by side with other micro services\n\t// you have to check for that status code as well for a valid response.\n\t//\n\t// Developers are free to extend this method's behavior\n\t// by watching system directories changes manually and use of the `ctx.WriteWithExpiration`\n\t// with a \"modtime\" based on the file modified date,\n\t// similar to the `HandleDir`(which sends status OK(200) and browser disk caching instead of 304).\n\t//\n\t// A shortcut of the `cache#Cache304`.\n\tCache304 = cache.Cache304\n\n\t// CookieOverride is a CookieOption which overrides the cookie explicitly to the given \"cookie\".\n\t//\n\t// A shortcut for the `context#CookieOverride`.\n\tCookieOverride = context.CookieOverride\n\t// CookieDomain is a CookieOption which sets the cookie's Domain field.\n\t// If empty then the current domain is used.\n\t//\n\t// A shortcut for the `context#CookieDomain`.\n\tCookieDomain = context.CookieDomain\n\t// CookieAllowReclaim accepts the Context itself.\n\t// If set it will add the cookie to (on `CookieSet`, `CookieSetKV`, `CookieUpsert`)\n\t// or remove the cookie from (on `CookieRemove`) the Request object too.\n\t//\n\t// A shortcut for the `context#CookieAllowReclaim`.\n\tCookieAllowReclaim = context.CookieAllowReclaim\n\t// CookieAllowSubdomains set to the Cookie Options\n\t// in order to allow subdomains to have access to the cookies.\n\t// It sets the cookie's Domain field (if was empty) and\n\t// it also sets the cookie's SameSite to lax mode too.\n\t//\n\t// A shortcut for the `context#CookieAllowSubdomains`.\n\tCookieAllowSubdomains = context.CookieAllowSubdomains\n\t// CookieSameSite sets a same-site rule for cookies to set.\n\t// SameSite allows a server to define a cookie attribute making it impossible for\n\t// the browser to send this cookie along with cross-site requests. The main\n\t// goal is to mitigate the risk of cross-origin information leakage, and provide\n\t// some protection against cross-site request forgery attacks.\n\t//\n\t// See https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00 for details.\n\t//\n\t// A shortcut for the `context#CookieSameSite`.\n\tCookieSameSite = context.CookieSameSite\n\t// CookieSecure sets the cookie's Secure option if the current request's\n\t// connection is using TLS. See `CookieHTTPOnly` too.\n\t//\n\t// A shortcut for the `context#CookieSecure`.\n\tCookieSecure = context.CookieSecure\n\t// CookieHTTPOnly is a `CookieOption`.\n\t// Use it to set the cookie's HttpOnly field to false or true.\n\t// HttpOnly field defaults to true for `RemoveCookie` and `SetCookieKV`.\n\t//\n\t// A shortcut for the `context#CookieHTTPOnly`.\n\tCookieHTTPOnly = context.CookieHTTPOnly\n\t// CookiePath is a `CookieOption`.\n\t// Use it to change the cookie's Path field.\n\t//\n\t// A shortcut for the `context#CookiePath`.\n\tCookiePath = context.CookiePath\n\t// CookieCleanPath is a `CookieOption`.\n\t// Use it to clear the cookie's Path field, exactly the same as `CookiePath(\"\")`.\n\t//\n\t// A shortcut for the `context#CookieCleanPath`.\n\tCookieCleanPath = context.CookieCleanPath\n\t// CookieExpires is a `CookieOption`.\n\t// Use it to change the cookie's Expires and MaxAge fields by passing the lifetime of the cookie.\n\t//\n\t// A shortcut for the `context#CookieExpires`.\n\tCookieExpires = context.CookieExpires\n\t// CookieEncoding accepts a value which implements `Encode` and `Decode` methods.\n\t// It calls its `Encode` on `Context.SetCookie, UpsertCookie, and SetCookieKV` methods.\n\t// And on `Context.GetCookie` method it calls its `Decode`.\n\t//\n\t// A shortcut for the `context#CookieEncoding`.\n\tCookieEncoding = context.CookieEncoding\n\n\t// IsErrEmptyJSON reports whether the given \"err\" is caused by a\n\t// Context.ReadJSON call when the request body\n\t// didn't start with { or it was totally empty.\n\tIsErrEmptyJSON = context.IsErrEmptyJSON\n\t// IsErrPath can be used at `context#ReadForm` and `context#ReadQuery`.\n\t// It reports whether the incoming error is type of `schema.ErrPath`,\n\t// which can be ignored when server allows unknown post values to be sent by the client.\n\t//\n\t// A shortcut for the `context#IsErrPath`.\n\tIsErrPath = context.IsErrPath\n\t// IsErrCanceled reports whether the \"err\" is caused by a cancellation or timeout.\n\t//\n\t// A shortcut for the `context#IsErrCanceled`.\n\tIsErrCanceled = context.IsErrCanceled\n\t// ErrEmptyForm is the type error which API users can make use of\n\t// to check if a form was empty on `Context.ReadForm`.\n\t//\n\t// A shortcut for the `context#ErrEmptyForm`.\n\tErrEmptyForm = context.ErrEmptyForm\n\t// ErrEmptyFormField reports whether if form value is empty.\n\t// An alias of `context.ErrEmptyFormField`.\n\tErrEmptyFormField = context.ErrEmptyFormField\n\t// ErrNotFound reports whether a key was not found, useful\n\t// on post data, versioning feature and others.\n\t// An alias of `context.ErrNotFound`.\n\tErrNotFound = context.ErrNotFound\n\t// NewProblem returns a new Problem.\n\t// Head over to the `Problem` type godoc for more.\n\t//\n\t// A shortcut for the `context#NewProblem`.\n\tNewProblem = context.NewProblem\n\t// XMLMap wraps a map[string]any to compatible xml marshaler,\n\t// in order to be able to render maps as XML on the `Context.XML` method.\n\t//\n\t// Example: `Context.XML(XMLMap(\"Root\", map[string]any{...})`.\n\t//\n\t// A shortcut for the `context#XMLMap`.\n\tXMLMap = context.XMLMap\n\t// ErrStopExecution if returned from a hero middleware or a request-scope dependency\n\t// stops the handler's execution, see _examples/dependency-injection/basic/middleware.\n\tErrStopExecution = hero.ErrStopExecution\n\t// ErrHijackNotSupported is returned by the Hijack method to\n\t// indicate that Hijack feature is not available.\n\t//\n\t// A shortcut for the `context#ErrHijackNotSupported`.\n\tErrHijackNotSupported = context.ErrHijackNotSupported\n\t// ErrPushNotSupported is returned by the Push method to\n\t// indicate that HTTP/2 Push support is not available.\n\t//\n\t// A shortcut for the `context#ErrPushNotSupported`.\n\tErrPushNotSupported = context.ErrPushNotSupported\n\t// PrivateError accepts an error and returns a wrapped private one.\n\t// A shortcut for the `context#PrivateError` function.\n\tPrivateError = context.PrivateError\n\n\t// TrimParamFilePart is a middleware which trims any last part after a dot (.) character\n\t// of the current route's dynamic path parameters.\n\t// A shortcut for the `context#TrimParamFilePart` function.\n\tTrimParamFilePart Handler = context.TrimParamFilePart\n)\n\n// HTTP Methods copied from `net/http`.\nconst (\n\tMethodGet     = http.MethodGet\n\tMethodPost    = http.MethodPost\n\tMethodPut     = http.MethodPut\n\tMethodDelete  = http.MethodDelete\n\tMethodConnect = http.MethodConnect\n\tMethodHead    = http.MethodHead\n\tMethodPatch   = http.MethodPatch\n\tMethodOptions = http.MethodOptions\n\tMethodTrace   = http.MethodTrace\n\t// MethodNone is an iris-specific \"virtual\" method\n\t// to store the \"offline\" routes.\n\tMethodNone = router.MethodNone\n)\n\n// HTTP status codes as registered with IANA.\n// See: http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml.\n// Raw Copy from the future(tip) net/http std package in order to recude the import path of \"net/http\" for the users.\nconst (\n\tStatusContinue           = http.StatusContinue           // RFC 7231, 6.2.1\n\tStatusSwitchingProtocols = http.StatusSwitchingProtocols // RFC 7231, 6.2.2\n\tStatusProcessing         = http.StatusProcessing         // RFC 2518, 10.1\n\tStatusEarlyHints         = http.StatusEarlyHints         // RFC 8297\n\n\tStatusOK                   = http.StatusOK                   // RFC 7231, 6.3.1\n\tStatusCreated              = http.StatusCreated              // RFC 7231, 6.3.2\n\tStatusAccepted             = http.StatusAccepted             // RFC 7231, 6.3.3\n\tStatusNonAuthoritativeInfo = http.StatusNonAuthoritativeInfo // RFC 7231, 6.3.4\n\tStatusNoContent            = http.StatusNoContent            // RFC 7231, 6.3.5\n\tStatusResetContent         = http.StatusResetContent         // RFC 7231, 6.3.6\n\tStatusPartialContent       = http.StatusPartialContent       // RFC 7233, 4.1\n\tStatusMultiStatus          = http.StatusMultiStatus          // RFC 4918, 11.1\n\tStatusAlreadyReported      = http.StatusAlreadyReported      // RFC 5842, 7.1\n\tStatusIMUsed               = http.StatusIMUsed               // RFC 3229, 10.4.1\n\n\tStatusMultipleChoices   = http.StatusMultipleChoices   // RFC 7231, 6.4.1\n\tStatusMovedPermanently  = http.StatusMovedPermanently  // RFC 7231, 6.4.2\n\tStatusFound             = http.StatusFound             // RFC 7231, 6.4.3\n\tStatusSeeOther          = http.StatusSeeOther          // RFC 7231, 6.4.4\n\tStatusNotModified       = http.StatusNotModified       // RFC 7232, 4.1\n\tStatusUseProxy          = http.StatusUseProxy          // RFC 7231, 6.4.5\n\t_                       = 306                          // RFC 7231, 6.4.6 (Unused)\n\tStatusTemporaryRedirect = http.StatusTemporaryRedirect // RFC 7231, 6.4.7\n\tStatusPermanentRedirect = http.StatusPermanentRedirect // RFC 7538, 3\n\n\tStatusBadRequest                   = http.StatusBadRequest                   // RFC 7231, 6.5.1\n\tStatusUnauthorized                 = http.StatusUnauthorized                 // RFC 7235, 3.1\n\tStatusPaymentRequired              = http.StatusPaymentRequired              // RFC 7231, 6.5.2\n\tStatusForbidden                    = http.StatusForbidden                    // RFC 7231, 6.5.3\n\tStatusNotFound                     = http.StatusNotFound                     // RFC 7231, 6.5.4\n\tStatusMethodNotAllowed             = http.StatusMethodNotAllowed             // RFC 7231, 6.5.5\n\tStatusNotAcceptable                = http.StatusNotAcceptable                // RFC 7231, 6.5.6\n\tStatusProxyAuthRequired            = http.StatusProxyAuthRequired            // RFC 7235, 3.2\n\tStatusRequestTimeout               = http.StatusRequestTimeout               // RFC 7231, 6.5.7\n\tStatusConflict                     = http.StatusConflict                     // RFC 7231, 6.5.8\n\tStatusGone                         = http.StatusGone                         // RFC 7231, 6.5.9\n\tStatusLengthRequired               = http.StatusLengthRequired               // RFC 7231, 6.5.10\n\tStatusPreconditionFailed           = http.StatusPreconditionFailed           // RFC 7232, 4.2\n\tStatusRequestEntityTooLarge        = http.StatusRequestEntityTooLarge        // RFC 7231, 6.5.11\n\tStatusRequestURITooLong            = http.StatusRequestURITooLong            // RFC 7231, 6.5.12\n\tStatusUnsupportedMediaType         = http.StatusUnsupportedMediaType         // RFC 7231, 6.5.13\n\tStatusRequestedRangeNotSatisfiable = http.StatusRequestedRangeNotSatisfiable // RFC 7233, 4.4\n\tStatusExpectationFailed            = http.StatusExpectationFailed            // RFC 7231, 6.5.14\n\tStatusTeapot                       = http.StatusTeapot                       // RFC 7168, 2.3.3\n\tStatusMisdirectedRequest           = http.StatusMisdirectedRequest           // RFC 7540, 9.1.2\n\tStatusUnprocessableEntity          = http.StatusUnprocessableEntity          // RFC 4918, 11.2\n\tStatusLocked                       = http.StatusLocked                       // RFC 4918, 11.3\n\tStatusFailedDependency             = http.StatusFailedDependency             // RFC 4918, 11.4\n\tStatusTooEarly                     = http.StatusTooEarly                     // RFC 8470, 5.2.\n\tStatusUpgradeRequired              = http.StatusUpgradeRequired              // RFC 7231, 6.5.15\n\tStatusPreconditionRequired         = http.StatusPreconditionRequired         // RFC 6585, 3\n\tStatusTooManyRequests              = http.StatusTooManyRequests              // RFC 6585, 4\n\tStatusRequestHeaderFieldsTooLarge  = http.StatusRequestHeaderFieldsTooLarge  // RFC 6585, 5\n\tStatusUnavailableForLegalReasons   = http.StatusUnavailableForLegalReasons   // RFC 7725, 3\n\t// Unofficial Client Errors.\n\tStatusPageExpired                      = context.StatusPageExpired\n\tStatusBlockedByWindowsParentalControls = context.StatusBlockedByWindowsParentalControls\n\tStatusInvalidToken                     = context.StatusInvalidToken\n\tStatusTokenRequired                    = context.StatusTokenRequired\n\t//\n\tStatusInternalServerError           = http.StatusInternalServerError           // RFC 7231, 6.6.1\n\tStatusNotImplemented                = http.StatusNotImplemented                // RFC 7231, 6.6.2\n\tStatusBadGateway                    = http.StatusBadGateway                    // RFC 7231, 6.6.3\n\tStatusServiceUnavailable            = http.StatusServiceUnavailable            // RFC 7231, 6.6.4\n\tStatusGatewayTimeout                = http.StatusGatewayTimeout                // RFC 7231, 6.6.5\n\tStatusHTTPVersionNotSupported       = http.StatusHTTPVersionNotSupported       // RFC 7231, 6.6.6\n\tStatusVariantAlsoNegotiates         = http.StatusVariantAlsoNegotiates         // RFC 2295, 8.1\n\tStatusInsufficientStorage           = http.StatusInsufficientStorage           // RFC 4918, 11.5\n\tStatusLoopDetected                  = http.StatusLoopDetected                  // RFC 5842, 7.2\n\tStatusNotExtended                   = http.StatusNotExtended                   // RFC 2774, 7\n\tStatusNetworkAuthenticationRequired = http.StatusNetworkAuthenticationRequired // RFC 6585, 6\n\t// Unofficial Server Errors.\n\tStatusBandwidthLimitExceeded = context.StatusBandwidthLimitExceeded\n\tStatusInvalidSSLCertificate  = context.StatusInvalidSSLCertificate\n\tStatusSiteOverloaded         = context.StatusSiteOverloaded\n\tStatusSiteFrozen             = context.StatusSiteFrozen\n\tStatusNetworkReadTimeout     = context.StatusNetworkReadTimeout\n)\n\nvar (\n\t// StatusText returns a text for the HTTP status code. It returns the empty\n\t// string if the code is unknown.\n\t//\n\t// Shortcut for core/router#StatusText.\n\tStatusText = context.StatusText\n\t// RegisterMethods adds custom http methods to the \"AllMethods\" list.\n\t// Use it on initialization of your program.\n\t//\n\t// Shortcut for core/router#RegisterMethods.\n\tRegisterMethods = router.RegisterMethods\n\n\t// WebDAVMethods contains a list of WebDAV HTTP Verbs.\n\t// Register using RegiterMethods package-level function or\n\t// through HandleMany party-level method.\n\tWebDAVMethods = []string{\n\t\tMethodGet,\n\t\tMethodHead,\n\t\tMethodPatch,\n\t\tMethodPut,\n\t\tMethodPost,\n\t\tMethodDelete,\n\t\tMethodOptions,\n\t\tMethodConnect,\n\t\tMethodTrace,\n\t\t\"MKCOL\",\n\t\t\"COPY\",\n\t\t\"MOVE\",\n\t\t\"LOCK\",\n\t\t\"UNLOCK\",\n\t\t\"PROPFIND\",\n\t\t\"PROPPATCH\",\n\t\t\"LINK\",\n\t\t\"UNLINK\",\n\t\t\"PURGE\",\n\t\t\"VIEW\",\n\t}\n)\n\nvar globalPatches = &GlobalPatches{\n\tcontextPatches: &ContextPatches{\n\t\twriters: &ContextWriterPatches{},\n\t},\n}\n\n// GlobalPatches is a singleton features a uniform way to apply global/package-level modifications.\n//\n// See the `Patches` package-level function.\ntype GlobalPatches struct {\n\tcontextPatches *ContextPatches\n}\n\n// Patches returns the singleton of GlobalPatches, an easy way to modify\n// global(package-level) configuration for Iris applications.\n//\n// See its `Context` method.\n//\n// Example: https://github.com/kataras/iris/blob/main/_examples/response-writer/json-third-party/main.go\nfunc Patches() *GlobalPatches { // singleton.\n\treturn globalPatches\n}\n\n// Context returns the available context patches.\nfunc (p *GlobalPatches) Context() *ContextPatches {\n\treturn p.contextPatches\n}\n\n// ContextPatches contains the available global Iris context modifications.\ntype ContextPatches struct {\n\twriters *ContextWriterPatches\n}\n\n// Writers returns the available global Iris context modifications for REST writers.\nfunc (cp *ContextPatches) Writers() *ContextWriterPatches {\n\treturn cp.writers\n}\n\n// GetDomain modifies the way a domain is fetched from `Context#Domain` method,\n// which is used on subdomain redirect feature, i18n's language cookie for subdomain sharing\n// and the rewrite middleware.\nfunc (cp *ContextPatches) GetDomain(patchFunc func(hostport string) string) {\n\tcontext.GetDomain = patchFunc\n}\n\n// SetCookieKVExpiration modifies the default cookie expiration time on `Context#SetCookieKV` method.\nfunc (cp *ContextPatches) SetCookieKVExpiration(patch time.Duration) {\n\tcontext.SetCookieKVExpiration = patch\n}\n\n// ResolveHTTPFS modifies the default way to resolve a filesystem by any type of value.\n// It affects the Application's API Builder's `HandleDir` method.\nfunc (cp *ContextPatches) ResolveHTTPFS(patchFunc func(fsOrDir any) http.FileSystem) {\n\tcontext.ResolveHTTPFS = patchFunc\n}\n\n// ResolveHTTPFS modifies the default way to resolve a filesystem by any type of value.\n// It affects the view engine's filesystem resolver.\nfunc (cp *ContextPatches) ResolveFS(patchFunc func(fsOrDir any) fs.FS) {\n\tcontext.ResolveFS = patchFunc\n}\n\n// ContextWriterPatches features the context's writers patches.\ntype ContextWriterPatches struct{}\n\n// JSON sets a custom function which runs and overrides the default behavior of the `Context#JSON` method.\nfunc (cwp *ContextWriterPatches) JSON(patchFunc func(ctx Context, v any, options *JSON) error) {\n\tcontext.WriteJSON = patchFunc\n}\n\n// JSONP sets a custom function which runs and overrides the default behavior of the `Context#JSONP` method.\nfunc (cwp *ContextWriterPatches) JSONP(patchFunc func(ctx Context, v any, options *JSONP) error) {\n\tcontext.WriteJSONP = patchFunc\n}\n\n// XML sets a custom function which runs and overrides the default behavior of the `Context#XML` method.\nfunc (cwp *ContextWriterPatches) XML(patchFunc func(ctx Context, v any, options *XML) error) {\n\tcontext.WriteXML = patchFunc\n}\n\n// Markdown sets a custom function which runs and overrides the default behavior of the `Context#Markdown` method.\nfunc (cwp *ContextWriterPatches) Markdown(patchFunc func(ctx Context, v []byte, options *Markdown) error) {\n\tcontext.WriteMarkdown = patchFunc\n}\n\n// YAML sets a custom function which runs and overrides the default behavior of the `Context#YAML` method.\nfunc (cwp *ContextWriterPatches) YAML(patchFunc func(ctx Context, v any, indentSpace int) error) {\n\tcontext.WriteYAML = patchFunc\n}\n\n// Singleton is a structure which can be used as an embedded field on\n// struct/controllers that should be marked as singletons on `PartyConfigure` or `MVC` Applications.\ntype Singleton struct{}\n\n// Singleton returns true as this controller is a singleton.\nfunc (c Singleton) Singleton() bool { return true }\n"
  },
  {
    "path": "apps/README.md",
    "content": "# The `apps` package\n\nPackage `github.com/kataras/iris/v12/apps` provides a globally scoped control over all registered Iris Applications of the same Program.\n\n[Example Application](https://github.com/kataras/iris/tree/main/_examples/routing/subdomains/redirect/multi-instances)\n\nBelow you will find a use case for each feature.\n\n## The `Get` function\n\nThe `Get` function returns an Iris Application based on its \"appName\".\nIt returns nil when no application was found with the given exact name.\n\nIf \"appName\" parameter is missing then it returns the last registered one.\nWhen no application is registered yet then it creates a new on-fly\nwith a \"Default\" name and returns that instead.\nThe \"Default\" one can be used across multiple Go packages\nof the same Program too.\n\nApplications of the same program are registered automatically.\n\nTo check if at least one application is registered or not,\nuse the `GetAll() []*iris.Application` function instead.\n\n```go\nfunc Get(appName ...string) *iris.Application\n```\n\n### Features\n\n- Access an Iris Application globally from all of your Go packages of the same Program without a global parameter and import cycle.\n\n```go\n/* myserver/api/user.go */\n\npackage userapi\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/apps\"\n)\n\nfunc init() {\n\tapp := apps.Get()\n\tapp.Get(\"/\", list)\n}\n\nfunc list(ctx iris.Context) {\n\t// [...]\n\tctx.WriteString(\"list users\")\n}\n```\n\n```go\n/* myserver/main.go */\n\npackage main\n\nimport (\n\t_ \"myserver/api/user\"\n\n\t\"github.com/kataras/iris/v12/apps\"\n)\n\nfunc main() {\n\tapp := apps.Get()\n\tapp.Listen(\":80\")\n}\n```\n\nThe empty `_` import statement will call the `userapi.init` function (before `main`) which\ninitializes an Iris Application, if not already exists, and registers it to the internal global store for further use across packages.\n\n- Reference one or more Iris Applications based on their names.\n\nThe `Application.SetName` method sets a unique name to this Iris Application.\nIt sets a child prefix for the current Application's Logger.\nIts `Application.String` method returns the given name. It returns itself.\n\n```go\nfunc (app *Application) SetName(appName string) *Application\n```\n\n```go\n/* myserver/main.go */\n\npackage main\n\nimport \"github.com/kataras/iris/v12\"\n\nfunc main() {\n    app := iris.New().SetName(\"app.company.com\")\n}\n```\n\n```go\n/* myserver/pkg/something.go */\n\npackage pkg\n\nimport \"github.com/kataras/iris/v12/apps\"\n\nfunc DoSomething() {\n    app := apps.Get(\"app.company.com\")\n}\n```\n\nThe `main` function creates and registers an Iris Application on `\"app.company.com\"` name, after that declaration every package of the same Program can retrieve that specific Application instance through its name.\n\n## The `Switch` function\n\nThe `Switch` function returns a new Application with the sole purpose of routing the matched Applications through the \"provided cases\". Read below about the available SwitchProviders and how you can create and use your own one.\n\nThe cases are filtered in order of their registration.\n\n```go\nfunc Switch(provider SwitchProvider, options ...SwitchOption) *iris.Application\n```\n\nExample Code:\n\n```go\nswitcher := Switch(Hosts{\n\t{Pattern: \"mydomain.com\", Target: app},\n\t{Pattern: \"test.mydomain.com\", Target: testSubdomainApp},\n\t{Pattern: \"otherdomain.com\", Target: \"appName\"},\n})\nswitcher.Listen(\":80\")\n```\n\nNote that this is NOT an alternative for a load balancer.\nThe filters are executed by registration order and a matched Application\nhandles the request, that's all it does.\n\nThe returned Switch Iris Application can register routes that will run\nwhen neither of the registered Applications is responsible to handle the incoming request against the provided filters.\n\nThe returned Switch Iris Application can also register custom error code handlers,\ne.g. to inject the 404 on not responsible Application was found.\nIt can also be wrapped with its `WrapRouter` method,\nwhich is really useful for logging and statistics.\n\n### The `SwitchProvider` interface\n\nThis is the first required input argument for the `Switch` function.\n\nA `SwitchProvider` should return one or more `SwitchCase` values.\n\n```go\ntype SwitchProvider interface {\n\tGetSwitchCases() []SwitchCase\n}\n```\n\nThe `SwitchCase` structure contains the Filter and the target Iris Application.\n\n```go\ntype SwitchCase struct {\n\tFilter func(ctx iris.Context) bool\n\tApp    *iris.Application\n}\n```\n\n### The `SwitchOptions` structure\n\nThis is the last variadic (optional) input argument for the `Switch` function.\n\n```go\ntype SwitchOptions struct {\n\t// RequestModifiers holds functions to run\n\t// if and only if at least one Filter passed.\n\t// They are used to modify the request object\n\t// of the matched Application, e.g. modify the host.\n\t//\n\t// See `SetHost` option too.\n\tRequestModifiers []func(*http.Request)\n}\n```\n\nThe `SetHost` function is a SwitchOption.\nIt force sets a Host field for the matched Application's request object.\nExtremely useful when used with Hosts SwitchProvider.\nUsecase: www. to root domain without redirection (SEO reasons)\nand keep the same internal request Host for both of them so\nthe root app's handlers will always work with a single host no matter\nwhat the real request Host was.\n\n```go\nfunc SetHost(hostField string) SwitchOptionFunc\n```\n\nExample Code:\n\n```go\ncases := Hosts{\n\t{\"^(www.)?mydomain.com$\", rootApp},\n}\nswitcher := Switch(cases, SetHost(\"mydomain.com\"))\n```\n\n### Join different type of SwitchProviders\n\nWrap with the `Join` slice to pass more than one provider at the same time.\n\nExample Code:\n\n```go\nSwitch(Join{\n\tSwitchCase{\n\t\tFilter: customFilter,\n\t\tApp:    myapp,\n\t},\n\tHosts{\n\t\t{Pattern: \"^test.*$\", Target: myapp},\n\t},\n})\n```\n"
  },
  {
    "path": "apps/apps.go",
    "content": "// Package apps is responsible to control many Iris Applications.\n// This package directly imports the iris root package and cannot be used\n// inside Iris' codebase itself. Only external packages/programs can make use of it.\npackage apps\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/context\"\n)\n\n// Get returns an Iris Application based on its \"appName\".\n// It returns nil when no application was found with the given exact name.\n//\n// If \"appName\" parameter is missing then it returns the last registered one.\n// When no application is registered yet then it creates a new on-fly\n// with a \"Default\" name and returns that instead.\n// The \"Default\" one can be used across multiple Go packages\n// of the same Program too.\n//\n// Applications of the same program are registered automatically.\n//\n// To check if at least one application is registered or not\n// use the `GetAll` function instead.\nfunc Get(appName ...string) *iris.Application {\n\tif len(appName) == 0 || appName[0] == \"\" {\n\t\tif app := context.LastApplication(); app != nil {\n\t\t\treturn app.(*iris.Application)\n\t\t}\n\n\t\treturn iris.New().SetName(\"Default\")\n\t}\n\n\tif app, ok := context.GetApplication(appName[0]); ok {\n\t\treturn app.(*iris.Application)\n\t}\n\n\treturn nil\n}\n\n// GetAll returns a slice of all registered Iris Applications.\nfunc GetAll() []*iris.Application {\n\tappsReadOnly := context.GetApplications()\n\tapps := make([]*iris.Application, 0, len(appsReadOnly))\n\n\tfor _, app := range appsReadOnly {\n\t\tapps = append(apps, app.(*iris.Application))\n\t}\n\n\treturn apps\n}\n\n// OnApplicationRegistered adds a function which fires when a new application\n// is registered.\nfunc OnApplicationRegistered(listeners ...func(app *iris.Application)) {\n\tappListeners := make([]func(context.Application), 0, len(listeners))\n\tfor i := range listeners {\n\t\tappListeners = append(appListeners, func(ctxApp context.Application) {\n\t\t\tlisteners[i](ctxApp.(*iris.Application))\n\t\t})\n\t}\n\tcontext.OnApplicationRegistered(appListeners...)\n}\n"
  },
  {
    "path": "apps/switch.go",
    "content": "package apps\n\nimport (\n\t\"strings\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\n// Switch returns a new Application\n// with the sole purpose of routing the\n// matched Applications through the \"provided cases\".\n//\n// The cases are filtered in order of their registration.\n//\n// Example Code:\n//\n//\tswitcher := Switch(Hosts{\n//\t\t{ Pattern: \"mydomain.com\", Target: app },\n//\t\t{ Pattern: \"test.mydomain.com\", Target: testSubdomainApp },\n//\t\t{ Pattern: \"otherdomain.com\", \"Target: appName\" },\n//\t})\n//\tswitcher.Listen(\":80\")\n//\n// Note that this is NOT an alternative for a load balancer.\n// The filters are executed by registration order and a matched Application\n// handles the request, that's all it does.\n//\n// The returned Switch Iris Application can register routes that will run\n// when neither of the registered Applications is responsible\n// to handle the incoming request against the provided filters.\n// The returned Switch Iris Application can also register custom error code handlers,\n// e.g. to inject the 404 on not responsible Application was found.\n// It can also be wrapped with its `WrapRouter` method,\n// which is really useful for logging and statistics.\n//\n// Wrap with the `Join` slice to pass\n// more than one provider at the same time.\n//\n// An alternative way for manually embedding an Iris Application to another one is:\n//\n//\tapp := iris.New() // root app.\n//\tmyOtherApp := api.NewServer(otherServerConfiguration) // embedded app.\n//\t// myOtherApp.Logger().SetLevel(\"debug\")\n//\n//\tif err := myOtherApp.Build(); err != nil {\n//\t\tpanic(err)\n//\t}\n//\n//\tapp.Any(\"/api/identity/{p:path}\", func(ctx iris.Context) {\n//\t\tapiPath := \"/\" + ctx.Params().Get(\"p\")\n//\t\tr := ctx.Request()\n//\t\tr.URL.Path = apiPath\n//\t\tr.URL.RawPath = apiPath\n//\t\tctx.Params().Remove(\"p\")\n//\n//\t\tmyOtherApp.ServeHTTPC(ctx)\n//\t})\n//\n// app.Listen(\":80\")\nfunc Switch(provider SwitchProvider, options ...SwitchOption) *iris.Application {\n\tcases := provider.GetSwitchCases()\n\tif len(cases) == 0 {\n\t\tpanic(\"iris: switch: empty cases\")\n\t}\n\n\tvar friendlyAddrs []string\n\tif fp, ok := provider.(FriendlyNameProvider); ok {\n\t\tif friendlyName := fp.GetFriendlyName(); friendlyName != \"\" {\n\t\t\tfriendlyAddrs = append(friendlyAddrs, friendlyName)\n\t\t}\n\t}\n\n\topts := DefaultSwitchOptions()\n\tfor _, opt := range options {\n\t\tif opt == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\topt.Apply(&opts)\n\t}\n\n\tapp := iris.New()\n\t// Try to build the cases apps on app.Build/Listen/Run so\n\t// end-developers don't worry about it.\n\tapp.OnBuild = func() error {\n\t\tfor _, c := range cases {\n\t\t\tif err := c.App.Build(); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\t// If we have a request to support\n\t// middlewares in that switcher app then\n\t// we can use app.Get(\"{p:path}\"...) instead.\n\tapp.UseRouter(func(ctx iris.Context) {\n\t\tfor _, c := range cases {\n\t\t\tif c.Filter(ctx) {\n\t\t\t\tw := ctx.ResponseWriter()\n\t\t\t\tr := ctx.Request()\n\n\t\t\t\tfor _, reqMod := range opts.RequestModifiers {\n\t\t\t\t\treqMod(r)\n\t\t\t\t}\n\n\t\t\t\tc.App.ServeHTTP(w, r)\n\n\t\t\t\t// if c.App.Downgraded() {\n\t\t\t\t// \tc.App.ServeHTTP(w, r)\n\t\t\t\t// } else {\n\t\t\t\t// Note(@kataras): don't ever try something like that;\n\t\t\t\t// the context pool is the switcher's one.\n\t\t\t\t// \tctx.SetApplication(c.App)\n\t\t\t\t// \tc.App.ServeHTTPC(ctx)\n\t\t\t\t// \tctx.SetApplication(app)\n\t\t\t\t// }\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\t// let the \"switch app\" handle it or fire a custom 404 error page,\n\t\t// next is the switch app's router.\n\t\tctx.Next()\n\t})\n\n\t// Configure the switcher's supervisor.\n\tapp.ConfigureHost(func(su *iris.Supervisor) {\n\t\tif len(friendlyAddrs) > 0 {\n\t\t\tsu.FriendlyAddr = strings.Join(friendlyAddrs, \", \")\n\t\t}\n\t})\n\treturn app\n}\n\ntype (\n\t// SwitchCase contains the filter\n\t// and the matched Application instance.\n\tSwitchCase struct {\n\t\tFilter iris.Filter       // Filter runs against the Switcher.\n\t\tApp    *iris.Application // App is the main target application responsible to handle the request.\n\t}\n\n\t// A SwitchProvider should return the switch cases.\n\t// It's an interface instead of a direct slice because\n\t// we want to make available different type of structures\n\t// without wrapping.\n\tSwitchProvider interface {\n\t\tGetSwitchCases() []SwitchCase\n\t}\n\n\t// FriendlyNameProvider can be optionally implemented by providers\n\t// to customize the Switcher's Supervisor.FriendlyAddr field (Startup log).\n\tFriendlyNameProvider interface {\n\t\tGetFriendlyName() string\n\t}\n\n\t// Join returns a new slice which joins different type of switch cases.\n\tJoin []SwitchProvider\n)\n\nvar _ SwitchProvider = SwitchCase{}\n\n// GetSwitchCases completes the SwitchProvider, it returns itself.\nfunc (sc SwitchCase) GetSwitchCases() []SwitchCase {\n\treturn []SwitchCase{sc}\n}\n\nvar _ SwitchProvider = Join{}\n\n// GetSwitchCases completes the switch provider.\nfunc (j Join) GetSwitchCases() (cases []SwitchCase) {\n\tfor _, p := range j {\n\t\tif p == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tcases = append(cases, p.GetSwitchCases()...)\n\t}\n\n\treturn\n}\n"
  },
  {
    "path": "apps/switch_hosts.go",
    "content": "package apps\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"regexp\"\n\t\"strings\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/context\"\n)\n\ntype (\n\t// Host holds the pattern for the SwitchCase filter\n\t// and the Target host or application.\n\tHost struct {\n\t\t// Pattern is the incoming host matcher regexp or a literal.\n\t\tPattern string\n\t\t// Target is the target Host that incoming requests will be redirected on pattern match\n\t\t// or an Application's Name that will handle the incoming request matched the Pattern.\n\t\tTarget any // It was a string in my initial design but let's do that any, we may support more types here in the future, until generics are in, keep it any.\n\t}\n\t// Hosts is a switch provider.\n\t// It can be used as input argument to the `Switch` function\n\t// to map host to existing Iris Application instances, e.g.\n\t// { \"www.mydomain.com\": \"mydomainApp\" } .\n\t// It can accept regexp as a host too, e.g.\n\t// { \"^my.*$\": \"mydomainApp\" } .\n\tHosts []Host\n\n\t// Good by we need order and map can't provide it for us\n\t//  (e.g. \"fallback\" regexp }\n\t// Hosts map[string]*iris.Application\n)\n\nvar _ SwitchProvider = Hosts{}\n\n// AnyDomain is a regexp that matches any domain.\n// It can be used as the Pattern field of a Host.\n//\n// Example:\n//\n//\tapps.Switch(apps.Hosts{\n//\t\t{\n//\t\t\tPattern: \"^id.*$\", Target: identityApp,\n//\t\t},\n//\t\t{\n//\t\t\tPattern: apps.AnyDomain, Target: app,\n//\t\t},\n//\t}).Listen(\":80\")\nconst AnyDomain = `^(([a-zA-Z]{1})|([a-zA-Z]{1}[a-zA-Z]{1})|([a-zA-Z]{1}[0-9]{1})|([0-9]{1}[a-zA-Z]{1})|([a-zA-Z0-9][a-zA-Z0-9-_]{1,61}[a-zA-Z0-9]))\\.([a-zA-Z]{2,6}|[a-zA-Z0-9-]{2,30}\\.[a-zA-Z\n\t]{2,3})$`\n\n// GetSwitchCases completes the SwitchProvider.\n// It returns a slice of SwitchCase which\n// if passed on `Switch` function, they act\n// as a router between matched domains and subdomains\n// between existing Iris Applications.\nfunc (hosts Hosts) GetSwitchCases() []SwitchCase {\n\tcases := make([]SwitchCase, 0, len(hosts))\n\n\tfor _, host := range hosts {\n\t\tcases = append(cases, SwitchCase{\n\t\t\tFilter: hostFilter(host.Pattern),\n\t\t\tApp:    hostApp(host),\n\t\t})\n\t}\n\n\treturn cases\n}\n\n// GetFriendlyName implements the FriendlyNameProvider.\nfunc (hosts Hosts) GetFriendlyName() string {\n\tvar patterns []string\n\tfor _, host := range hosts {\n\t\tif strings.TrimSpace(host.Pattern) != \"\" {\n\t\t\tpatterns = append(patterns, host.Pattern)\n\t\t}\n\t}\n\n\treturn strings.Join(patterns, \", \")\n}\n\nfunc hostApp(host Host) *iris.Application {\n\tif host.Target == nil {\n\t\treturn nil\n\t}\n\n\tswitch target := host.Target.(type) {\n\tcase context.Application:\n\t\treturn target.(*iris.Application)\n\tcase string:\n\t\t// Check if the given target is an application name, if so\n\t\t// we must not redirect (loop) we must serve the request\n\t\t// using that app.\n\t\tif targetApp, ok := context.GetApplication(target); ok {\n\t\t\t// It's always iris.Application so we are totally safe here.\n\t\t\treturn targetApp.(*iris.Application)\n\t\t}\n\t\t// If it's a real host, warn the user of invalid input.\n\t\tu, err := url.Parse(target)\n\t\tif err == nil && u.IsAbs() {\n\t\t\t// remember, we redirect hosts, not full URLs here.\n\t\t\tpanic(fmt.Sprintf(`iris: switch: hosts: invalid target host: \"%s\"`, target))\n\t\t}\n\n\t\tif regex := regexp.MustCompile(host.Pattern); regex.MatchString(target) {\n\t\t\tpanic(fmt.Sprintf(`iris: switch: hosts: loop detected between expression: \"%s\" and target host: \"%s\"`, host.Pattern, host.Target))\n\t\t}\n\n\t\treturn newHostRedirectApp(target, HostsRedirectCode)\n\tdefault:\n\t\tpanic(fmt.Sprintf(\"iris: switch: hosts: invalid target type: %T\", target))\n\t}\n}\n\nfunc hostFilter(expr string) iris.Filter {\n\tregex := regexp.MustCompile(expr)\n\treturn func(ctx iris.Context) bool {\n\t\treturn regex.MatchString(ctx.Host())\n\t}\n}\n\n// HostsRedirectCode is the default status code is used\n// to redirect a matching host to a url.\nvar HostsRedirectCode = iris.StatusMovedPermanently\n\nfunc newHostRedirectApp(targetHost string, code int) *iris.Application {\n\tapp := iris.New()\n\tapp.Downgrade(func(w http.ResponseWriter, r *http.Request) {\n\t\tif targetHost == context.GetHost(r) {\n\t\t\t// Note(@kataras):\n\t\t\t// this should never happen as the HostsRedirect\n\t\t\t// carefully checks if the expression already matched the \"redirectTo\"\n\t\t\t// to avoid the redirect loops at all.\n\t\t\t// iris: switch: hosts redirect: loop detected between expression: \"^my.*$\" and target host: \"mydomain.com\"\n\t\t\thttp.Error(w, iris.StatusText(iris.StatusTooManyRequests), iris.StatusTooManyRequests)\n\t\t\treturn\n\t\t}\n\n\t\tr.Host = targetHost\n\t\tr.URL.Host = targetHost\n\t\t// r.URL.User = nil\n\t\thttp.Redirect(w, r, r.URL.String(), code)\n\t})\n\treturn app\n}\n"
  },
  {
    "path": "apps/switch_hosts_test.go",
    "content": "package apps\n\nimport (\n\t\"fmt\"\n\t\"net/url\"\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\ntype testRequests map[string]map[string]int // url -> path -> status code\n\nfunc TestSwitchHosts(t *testing.T) {\n\tvar (\n\t\texpected = func(app context.Application, host string) string {\n\t\t\treturn fmt.Sprintf(\"App Name: %s\\nHost: %s\", app, host)\n\t\t}\n\t\tindex = func(ctx iris.Context) {\n\t\t\tctx.WriteString(expected(ctx.Application(), ctx.Host()))\n\t\t}\n\t)\n\n\ttestdomain1 := iris.New().SetName(\"test 1 domain\")\n\ttestdomain1.Get(\"/\", index) // should match host matching with \"testdomain1.com\".\n\n\ttestdomain2 := iris.New().SetName(\"test 2 domain\")\n\ttestdomain2.Get(\"/\", index) // should match host matching with \"testdomain2.com\".\n\n\tmydomain := iris.New().SetName(\"my domain\")\n\tmydomain.OnErrorCode(iris.StatusNotFound, func(ctx iris.Context) {\n\t\tctx.WriteString(ctx.Host() + \" custom not found\")\n\t})\n\tmydomain.Get(\"/\", index) // should match ALL hosts starting with \"my\".\n\n\ttests := []struct {\n\t\tPattern  string\n\t\tTarget   *iris.Application\n\t\tRequests testRequests\n\t}{\n\t\t{\n\t\t\t\"testdomain1.com\",\n\t\t\ttestdomain1,\n\t\t\ttestRequests{\n\t\t\t\t\"http://testdomain1.com\": {\n\t\t\t\t\t\"/\": iris.StatusOK,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"testdomain2.com\",\n\t\t\ttestdomain2,\n\t\t\ttestRequests{\n\t\t\t\t\"http://testdomain2.com\": {\n\t\t\t\t\t\"/\": iris.StatusOK,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"^my.*$\",\n\t\t\tmydomain,\n\t\t\ttestRequests{\n\t\t\t\t\"http://mydomain.com\": {\n\t\t\t\t\t\"/\":   iris.StatusOK,\n\t\t\t\t\t\"/nf\": iris.StatusNotFound,\n\t\t\t\t},\n\t\t\t\t\"http://myotherdomain.com\": {\n\t\t\t\t\t\"/\": iris.StatusOK,\n\t\t\t\t},\n\t\t\t\t\"http://mymy.com\": {\n\t\t\t\t\t\"/\": iris.StatusOK,\n\t\t\t\t},\n\t\t\t\t\"http://nmy.com\": {\n\t\t\t\t\t\"/\": iris.StatusBadGateway, /* 404 hijacked by switch.OnErrorCode */\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tvar hosts Hosts\n\tfor _, tt := range tests {\n\t\thosts = append(hosts, Host{tt.Pattern, tt.Target})\n\t}\n\tswitcher := Switch(hosts)\n\tswitcher.OnErrorCode(iris.StatusNotFound, func(ctx iris.Context) {\n\t\t// inject the 404 to 502.\n\t\t// tests the ctx.Next inside the Hosts switch provider.\n\t\tctx.StatusCode(iris.StatusBadGateway)\n\t\tctx.WriteString(\"Switcher: Bad Gateway\")\n\t})\n\n\te := httptest.New(t, switcher)\n\tfor i, tt := range tests {\n\t\tfor URL, paths := range tt.Requests {\n\t\t\tu, err := url.Parse(URL)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"[%d] %v\", i, err)\n\t\t\t}\n\t\t\ttargetHost := u.Host\n\t\t\tfor requestPath, statusCode := range paths {\n\t\t\t\t// url := fmt.Sprintf(\"http://%s\", requestHost)\n\t\t\t\tbody := expected(tt.Target, targetHost)\n\t\t\t\tswitch statusCode {\n\t\t\t\tcase 404:\n\t\t\t\t\tbody = targetHost + \" custom not found\"\n\t\t\t\tcase 502:\n\t\t\t\t\tbody = \"Switcher: Bad Gateway\"\n\t\t\t\t}\n\n\t\t\t\te.GET(requestPath).WithURL(URL).Expect().Status(statusCode).Body().IsEqual(body)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestSwitchHostsRedirect(t *testing.T) {\n\tvar (\n\t\texpected = func(appName, host, path string) string {\n\t\t\treturn fmt.Sprintf(\"App Name: %s\\nHost: %s\\nPath: %s\", appName, host, path)\n\t\t}\n\t\tindex = func(ctx iris.Context) {\n\t\t\tctx.WriteString(expected(ctx.Application().String(), ctx.Host(), ctx.Path()))\n\t\t}\n\t)\n\n\tmydomain := iris.New().SetName(\"mydomain\")\n\tmydomain.OnAnyErrorCode(func(ctx iris.Context) {\n\t\tctx.WriteString(\"custom: \" + iris.StatusText(ctx.GetStatusCode()))\n\t})\n\tmydomain.Get(\"/\", index)\n\tmydomain.Get(\"/f\", index)\n\n\ttests := []struct {\n\t\tPattern  string\n\t\tTarget   string\n\t\tRequests testRequests\n\t}{\n\t\t{\n\t\t\t\"www.mydomain.com\",\n\t\t\t\"mydomain\",\n\t\t\ttestRequests{\n\t\t\t\t\"http://www.mydomain.com\": {\n\t\t\t\t\t\"/\":   iris.StatusOK,\n\t\t\t\t\t\"/f\":  iris.StatusOK,\n\t\t\t\t\t\"/nf\": iris.StatusNotFound,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"^test.*$\",\n\t\t\t\"mydomain\",\n\t\t\ttestRequests{\n\t\t\t\t\"http://testdomain.com\": {\n\t\t\t\t\t\"/\":   iris.StatusOK,\n\t\t\t\t\t\"/f\":  iris.StatusOK,\n\t\t\t\t\t\"/nf\": iris.StatusNotFound,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t// Something like this will panic to protect users:\n\t\t// {\n\t\t// \t...,\n\t\t// \t\"^my.*$\",\n\t\t// \t\"mydomain.com\",\n\t\t// ...\n\t\t//\n\t\t{\n\t\t\t\"^www.*$\",\n\t\t\t\"google.com\",\n\t\t\ttestRequests{\n\t\t\t\t\"http://www.mydomain.com\": {\n\t\t\t\t\t\"/\": iris.StatusOK,\n\t\t\t\t},\n\t\t\t\t\"http://www.golang.org\": {\n\t\t\t\t\t\"/\": iris.StatusNotFound, // should give not found because this is not a switcher's web app.\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tvar hostsRedirect Hosts\n\tfor _, tt := range tests {\n\t\thostsRedirect = append(hostsRedirect, Host{tt.Pattern, tt.Target})\n\t}\n\n\tswitcher := Switch(hostsRedirect)\n\te := httptest.New(t, switcher)\n\n\tfor i, tt := range tests {\n\t\tfor requestURL, paths := range tt.Requests {\n\t\t\tu, err := url.Parse(requestURL)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"[%d] %v\", i, err)\n\t\t\t}\n\t\t\ttargetHost := u.Host\n\t\t\tfor requestPath, statusCode := range paths {\n\t\t\t\tbody := expected(mydomain.String(), targetHost, requestPath)\n\t\t\t\tif statusCode != 200 {\n\t\t\t\t\tif tt.Target != mydomain.String() { // it's external.\n\t\t\t\t\t\tbody = \"Not Found\"\n\t\t\t\t\t} else {\n\t\t\t\t\t\tbody = \"custom: \" + iris.StatusText(statusCode)\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\te.GET(requestPath).WithURL(requestURL).Expect().Status(statusCode).Body().IsEqual(body)\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "apps/switch_options.go",
    "content": "package apps\n\nimport \"net/http\"\n\ntype (\n\t// SwitchOptions holds configuration\n\t// for the switcher application.\n\tSwitchOptions struct {\n\t\t// RequestModifiers holds functions to run\n\t\t// if and only if at least one Filter passed.\n\t\t// They are used to modify the request object\n\t\t// of the matched Application, e.g. modify the host.\n\t\t//\n\t\t// See `SetHost` option too.\n\t\tRequestModifiers []func(*http.Request)\n\t\t// Note(@kataras): I though a lot of API designs for that one and the current is the safest to use.\n\t\t// I skipped the idea of returning a wrapped Application to have functions like app.UseFilter\n\t\t// or the idea of accepting a chain of Iris Handlers here because the Context belongs\n\t\t// to the switcher application and a new one is acquired on the matched Application level,\n\t\t// so communication between them is not possible although\n\t\t// we can make it possible but lets not complicate the code here, unless otherwise requested.\n\t}\n\n\t// SwitchOption should be implemented by all options\n\t// passed to the `Switch` package-level last variadic input argument.\n\tSwitchOption interface {\n\t\tApply(*SwitchOptions)\n\t}\n\n\t// SwitchOptionFunc provides a functional way to pass options\n\t// to the `Switch` package-level function's last variadic input argument.\n\tSwitchOptionFunc func(*SwitchOptions)\n)\n\n// Apply completes the `SwitchOption` interface.\nfunc (f SwitchOptionFunc) Apply(opts *SwitchOptions) {\n\tf(opts)\n}\n\n// DefaultSwitchOptions returns a fresh SwitchOptions\n// struct value with its fields set to their defaults.\nfunc DefaultSwitchOptions() SwitchOptions {\n\treturn SwitchOptions{\n\t\tRequestModifiers: nil,\n\t}\n}\n\n// Apply completes the `SwitchOption` interface.\n// It does copies values from \"o\" to \"opts\" when necessary.\nfunc (o SwitchOptions) Apply(opts *SwitchOptions) {\n\tif v := o.RequestModifiers; len(v) > 0 {\n\t\topts.RequestModifiers = v // override, not append.\n\t}\n}\n\n// SetHost is a SwitchOption.\n// It force sets a Host field for the matched Application's request object.\n// Extremely useful when used with Hosts SwitchProvider.\n// Usecase: www. to root domain without redirection (SEO reasons)\n// and keep the same internal request Host for both of them so\n// the root app's handlers will always work with a single host no matter\n// what the real request Host was.\nfunc SetHost(hostField string) SwitchOptionFunc {\n\tif hostField == \"\" {\n\t\treturn nil\n\t}\n\n\tsetHost := func(r *http.Request) {\n\t\tr.Host = hostField\n\t\tr.URL.Host = hostField // note: the URL.String builds the uri based on that.\n\t}\n\n\treturn func(opts *SwitchOptions) {\n\t\topts.RequestModifiers = append(opts.RequestModifiers, setHost)\n\t}\n}\n"
  },
  {
    "path": "apps/switch_options_test.go",
    "content": "package apps\n\nimport (\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nfunc TestSetHost(t *testing.T) {\n\tvar (\n\t\tindex = func(ctx iris.Context) {\n\t\t\tctx.Header(\"Server\", ctx.Application().String())\n\t\t\tctx.WriteString(ctx.Host())\n\t\t}\n\n\t\tforceHost = \"www.mydomain.com\"\n\t)\n\n\trootApp := iris.New().SetName(\"My Server\")\n\trootApp.Get(\"/\", index)\n\n\tswitcher := Switch(Hosts{\n\t\t{\"^(www.)?mydomain.com$\", rootApp},\n\t}, SetHost(forceHost))\n\n\te := httptest.New(t, switcher)\n\ttests := []*httptest.Request{\n\t\te.GET(\"/\").WithURL(\"http://mydomain.com\"),\n\t\te.GET(\"/\").WithURL(\"http://www.mydomain.com\"),\n\t}\n\n\tfor _, tt := range tests {\n\t\tex := tt.Expect().Status(iris.StatusOK)\n\t\tex.Header(\"Server\").Equal(rootApp.String())\n\t\tex.Body().IsEqual(forceHost)\n\t}\n}\n"
  },
  {
    "path": "apps/switch_scheme.go",
    "content": "package apps\n"
  },
  {
    "path": "apps/switch_test.go",
    "content": "package apps\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\nfunc TestSwitchJoin(t *testing.T) {\n\tmyapp := iris.New()\n\tcustomFilter := func(ctx iris.Context) bool {\n\t\tpass, _ := ctx.URLParamBool(\"filter\")\n\t\treturn pass\n\t}\n\n\tjoinedCases := Join{\n\t\tSwitchCase{\n\t\t\tFilter: customFilter,\n\t\t\tApp:    myapp,\n\t\t},\n\t\tHosts{{Pattern: \"^test.*$\", Target: myapp}},\n\t}\n\n\tcases := []SwitchCase{\n\t\t{\n\t\t\tFilter: customFilter,\n\t\t\tApp:    myapp,\n\t\t},\n\t\t{Filter: hostFilter(\"^test.*$\"), App: myapp},\n\t}\n\n\tif expected, got := fmt.Sprintf(\"%#+v\", cases), fmt.Sprintf(\"%#+v\", joinedCases.GetSwitchCases()); expected != got {\n\t\tt.Fatalf(\"join does not match with the expected slice of cases, expected:\\n%s\\nbut got:\\n%s\", expected, got)\n\t}\n\n}\n"
  },
  {
    "path": "auth/auth.go",
    "content": "//go:build go1.18\n// +build go1.18\n\npackage auth\n\nimport (\n\tstdContext \"context\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\n\t\"github.com/google/uuid\"\n\t\"github.com/gorilla/securecookie\"\n\t\"github.com/kataras/jwt\"\n)\n\ntype (\n\t// Auth holds the necessary functionality to authorize and optionally authenticating\n\t// users to access and perform actions against the resource server (Iris API).\n\t// It completes a secure and fast JSON Web Token signer and verifier which,\n\t// based on the custom application needs, can be further customized.\n\t// Each Auth of T instance can sign and verify a single custom <T> instance,\n\t// more Auth instances can share the same configuration to support multiple custom user types.\n\t// Initialize a new Auth of T instance using the New or MustLoad package-level functions.\n\t// Most important methods of the instance are:\n\t// - AddProvider\n\t// - SigninHandler\n\t// - VerifyHandler\n\t// - SignoutHandler\n\t// - SignoutAllHandler\n\t//\n\t// Example can be found at: https://github.com/kataras/iris/tree/main/_examples/auth/auth/main.go.\n\tAuth[T User] struct {\n\t\t// Holds the configuration passed through the New and MustLoad\n\t\t// package-level functions. One or more Auth instance can share the\n\t\t// same configuration's values.\n\t\tconfig Configuration\n\t\t// Holds the result of the config.KeysConfiguration.\n\t\tkeys jwt.Keys\n\t\t// This is an Iris cookie option used to encrypt and decrypt a cookie when\n\t\t// the config.Cookie.Hash & Block are not empty.\n\t\tsecurecookie context.SecureCookie\n\t\t// Defaults to an empty list, which cannot sign any tokens.\n\t\t// One or more custom providers should be registered through\n\t\t// the AddProvider or WithProviderAndErrorHandler methods.\n\t\tproviders []Provider[T] // at least one.\n\t\t// Always not nil, set to custom error handler on SetErrorHandler.\n\t\terrorHandler ErrorHandler\n\t\t// Not nil if a transformer is registered.\n\t\ttransformer Transformer[T]\n\t\t// Not nil if a custom claims provider is registered.\n\t\tclaimsProvider ClaimsProvider\n\t\t// True if KIDRefresh on config.Keys.\n\t\trefreshEnabled bool\n\t}\n\n\t// VerifyUserFunc is passed on Verify and VerifyHandler method\n\t// to, optionally, further validate a T user value.\n\tVerifyUserFunc[T User] func(t T) error\n\n\t// SigninRequest is the request body the server expects\n\t// on SignHandler. The Password and Username or Email should be filled.\n\tSigninRequest struct {\n\t\tUsername string `json:\"username\" form:\"username,omitempty\"` // username OR email, username has priority over email.\n\t\tEmail    string `json:\"email\" form:\"email,omitempty\"`       // email OR username.\n\t\tPassword string `json:\"password\" form:\"password\"`\n\t}\n\n\t// SigninResponse is the response body the server sends\n\t// to the client on the SignHandler. It contains a pair of the access token\n\t// and the refresh token if the refresh jwt token id exists in the configuration.\n\tSigninResponse struct {\n\t\tAccessToken  string `json:\"access_token\"`\n\t\tRefreshToken string `json:\"refresh_token,omitempty\"`\n\t}\n\n\t// RefreshRequest is the request body the server expects\n\t// on VerifyHandler to renew an access and refresh token pair.\n\tRefreshRequest struct {\n\t\tRefreshToken string `json:\"refresh_token\"`\n\t}\n)\n\n// MustLoad binds a filename (fullpath) configuration yaml or json\n// and constructs a new Auth instance. It panics on error.\nfunc MustLoad[T User](filename string) *Auth[T] {\n\tvar config Configuration\n\tif err := config.BindFile(filename); err != nil {\n\t\tpanic(err)\n\t}\n\n\treturn Must(New[T](config))\n}\n\n// Must is a helper that wraps a call to a function returning (*Auth[T], error)\n// and panics if the error is non-nil. It is intended for use in variable\n// initializations such as\n//\n//\tvar s = auth.Must(auth.New[MyUser](config))\nfunc Must[T User](s *Auth[T], err error) *Auth[T] {\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treturn s\n}\n\n// New initializes a new Auth instance typeof T and returns it.\n// The T generic can be any custom struct.\n// It accepts a Configuration value which can be constructed\n// manually or through a configuration file using the\n// MustGenerateConfiguration or MustLoadConfiguration\n// or LoadConfiguration or MustLoad package-level functions.\n//\n// Example can be found at: https://github.com/kataras/iris/tree/main/_examples/auth/auth/main.go.\nfunc New[T User](config Configuration) (*Auth[T], error) {\n\tkeys, err := config.validate()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\t_, refreshEnabled := keys[KIDRefresh]\n\n\ts := &Auth[T]{\n\t\tconfig:         config,\n\t\tkeys:           keys,\n\t\tsecurecookie:   securecookie.New([]byte(config.Cookie.Hash), []byte(config.Cookie.Block)),\n\t\trefreshEnabled: refreshEnabled,\n\t\t// providers:    []Provider[T]{newProvider[T]()},\n\t\terrorHandler: new(DefaultErrorHandler),\n\t}\n\n\treturn s, nil\n}\n\n// WithProviderAndErrorHandler registers a provider (if not nil) and\n// an error handler (if not nil) and returns this \"s\" Auth instance.\n// It's the same as calling AddProvider and SetErrorHandler at once.\n// It's really useful when registering an Auth instance using the iris.Party.PartyConfigure\n// method when a Provider of T and ErrorHandler is available through the registered Party's dependencies.\n//\n// Usage Example:\n//\n//\tapi := app.Party(\"/api\")\n//\tapi.EnsureStaticBindings().RegisterDependency(\n//\t  NewAuthProviderErrorHandler(),\n//\t  NewAuthCustomerProvider,\n//\t  auth.Must(auth.New[Customer](authConfig)).WithProviderAndErrorHandler,\n//\t)\nfunc (s *Auth[T]) WithProviderAndErrorHandler(provider Provider[T], errHandler ErrorHandler) *Auth[T] {\n\tif provider != nil {\n\t\tfor i := range s.providers {\n\t\t\ts.providers[i] = nil\n\t\t}\n\t\ts.providers = nil\n\n\t\ts.providers = make([]Provider[T], 0, 1)\n\t\ts.AddProvider(provider)\n\t}\n\n\tif errHandler != nil {\n\t\ts.SetErrorHandler(errHandler)\n\t}\n\n\treturn s\n}\n\n// AddProvider registers one or more providers to this Auth of T instance and returns itself.\n// Look the Provider godoc for more.\nfunc (s *Auth[T]) AddProvider(providers ...Provider[T]) *Auth[T] {\n\t// A provider can also implement both transformer and\n\t// error handler if that's the design option of the end-developer.\n\tfor _, p := range providers {\n\t\tif s.transformer == nil {\n\t\t\tif transformer, ok := p.(Transformer[T]); ok {\n\t\t\t\ts.SetTransformer(transformer)\n\t\t\t}\n\t\t}\n\n\t\tif errHandler, ok := p.(ErrorHandler); ok {\n\t\t\ts.SetErrorHandler(errHandler)\n\t\t}\n\n\t\tif s.claimsProvider == nil {\n\t\t\tif claimsProvider, ok := p.(ClaimsProvider); ok {\n\t\t\t\ts.claimsProvider = claimsProvider\n\t\t\t}\n\t\t}\n\t}\n\n\ts.providers = append(s.providers, providers...)\n\treturn s\n}\n\n// SetErrorHandler sets a custom error handler to this Auth of T instance and returns itself.\n// Look the Provider and ErrorHandler godoc for more.\nfunc (s *Auth[T]) SetErrorHandler(errHandler ErrorHandler) *Auth[T] {\n\ts.errorHandler = errHandler\n\treturn s\n}\n\n// SetTransformer sets a custom transformer to this Auth of T instance and returns itself.\n// Look the Provider and Transformer godoc for more.\nfunc (s *Auth[T]) SetTransformer(transformer Transformer[T]) *Auth[T] {\n\ts.transformer = transformer\n\treturn s\n}\n\n// SetTransformerFunc like SetTransformer method but accepts a function instead.\nfunc (s *Auth[T]) SetTransformerFunc(transfermerFunc func(ctx stdContext.Context, tok *VerifiedToken) (T, error)) *Auth[T] {\n\ts.transformer = TransformerFunc[T](transfermerFunc)\n\treturn s\n}\n\n// Signin signs a token based on the provided username and password\n// and returns a pair of access and refresh tokens.\n//\n// Signin calls the Provider.Signin method to check if a user\n// is authenticated by the given username and password combination.\nfunc (s *Auth[T]) Signin(ctx stdContext.Context, username, password string) ([]byte, []byte, error) {\n\tvar t T\n\n\t// get \"t\" from a valid provider.\n\tif n := len(s.providers); n > 0 {\n\t\tfor i := 0; i < n; i++ {\n\t\t\tp := s.providers[i]\n\n\t\t\tv, err := p.Signin(ctx, username, password)\n\t\t\tif err != nil {\n\t\t\t\tif i == n-1 { // last provider errored.\n\t\t\t\t\treturn nil, nil, fmt.Errorf(\"auth: signin: %w\", err)\n\t\t\t\t}\n\t\t\t\t// keep searching.\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// found.\n\t\t\tt = v\n\t\t\tbreak\n\t\t}\n\t} else {\n\t\treturn nil, nil, fmt.Errorf(\"auth: signin: no provider\")\n\t}\n\n\t// sign the tokens.\n\taccessToken, refreshToken, err := s.sign(t)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"auth: signin: %w\", err)\n\t}\n\n\treturn accessToken, refreshToken, nil\n}\n\nfunc (s *Auth[T]) sign(t T) ([]byte, []byte, error) {\n\t// sign the tokens.\n\tvar (\n\t\taccessStdClaims  StandardClaims\n\t\trefreshStdClaims StandardClaims\n\t)\n\n\tif s.claimsProvider != nil {\n\t\taccessStdClaims = s.claimsProvider.GetAccessTokenClaims()\n\t\trefreshStdClaims = s.claimsProvider.GetRefreshTokenClaims(accessStdClaims)\n\t}\n\n\tiat := jwt.Clock().Unix()\n\n\tif accessStdClaims.IssuedAt == 0 {\n\t\taccessStdClaims.IssuedAt = iat\n\t}\n\n\tif accessStdClaims.ID == \"\" {\n\t\taccessStdClaims.ID = uuid.NewString()\n\t}\n\n\tif refreshStdClaims.IssuedAt == 0 {\n\t\trefreshStdClaims.IssuedAt = iat\n\t}\n\n\tif refreshStdClaims.ID == \"\" {\n\t\trefreshStdClaims.ID = uuid.NewString()\n\t}\n\n\tif refreshStdClaims.OriginID == \"\" {\n\t\t// keep a reference of the access token the refresh token is created,\n\t\t// if that access token is invalidated then\n\t\t// its refresh token should be too so the user can force-login.\n\t\trefreshStdClaims.OriginID = accessStdClaims.ID\n\t}\n\n\taccessToken, err := s.keys.SignToken(KIDAccess, t, accessStdClaims)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"access: %w\", err)\n\t}\n\n\tvar refreshToken []byte\n\tif s.refreshEnabled {\n\t\trefreshToken, err = s.keys.SignToken(KIDRefresh, t, refreshStdClaims)\n\t\tif err != nil {\n\t\t\treturn nil, nil, fmt.Errorf(\"refresh: %w\", err)\n\t\t}\n\t}\n\n\treturn accessToken, refreshToken, nil\n}\n\n// SignHandler generates and sends a pair of access and refresh token to the client\n// as JSON body of `SigninResponse` and cookie (if cookie setting was provided).\n// See `Signin` method for more.\nfunc (s *Auth[T]) SigninHandler(ctx *context.Context) {\n\t// No, let the developer decide it based on a middleware, e.g. iris.LimitRequestBodySize.\n\t// ctx.SetMaxRequestBodySize(s.maxRequestBodySize)\n\n\tvar (\n\t\treq SigninRequest\n\t\terr error\n\t)\n\n\tswitch ctx.GetContentTypeRequested() {\n\tcase context.ContentFormHeaderValue, context.ContentFormMultipartHeaderValue:\n\t\terr = ctx.ReadForm(&req)\n\tdefault:\n\t\terr = ctx.ReadJSON(&req)\n\t}\n\n\tif err != nil {\n\t\ts.errorHandler.InvalidArgument(ctx, err)\n\t\treturn\n\t}\n\n\tif req.Username == \"\" {\n\t\treq.Username = req.Email\n\t}\n\n\taccessTokenBytes, refreshTokenBytes, err := s.Signin(ctx, req.Username, req.Password)\n\tif err != nil {\n\t\ts.tryRemoveCookie(ctx) // remove cookie on invalidated.\n\n\t\ts.errorHandler.Unauthenticated(ctx, err)\n\t\treturn\n\t}\n\taccessToken := jwt.BytesToString(accessTokenBytes)\n\trefreshToken := jwt.BytesToString(refreshTokenBytes)\n\n\ts.trySetCookie(ctx, accessToken)\n\n\tresp := SigninResponse{\n\t\tAccessToken:  accessToken,\n\t\tRefreshToken: refreshToken,\n\t}\n\tctx.JSON(resp)\n}\n\n// Verify accepts a token and verifies it.\n// It returns the token's custom and standard JWT claims.\nfunc (s *Auth[T]) Verify(ctx stdContext.Context, token []byte, verifyFuncs ...VerifyUserFunc[T]) (T, StandardClaims, error) {\n\tt, claims, err := s.verify(ctx, token)\n\tif err != nil {\n\t\treturn t, StandardClaims{}, fmt.Errorf(\"auth: verify: %w\", err)\n\t}\n\n\tfor _, verify := range verifyFuncs {\n\t\tif verify == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tif err = verify(t); err != nil {\n\t\t\treturn t, StandardClaims{}, fmt.Errorf(\"auth: verify: %w\", err)\n\t\t}\n\t}\n\n\treturn t, claims, nil\n}\n\nfunc (s *Auth[T]) verify(ctx stdContext.Context, token []byte) (T, StandardClaims, error) {\n\tvar t T\n\n\tif len(token) == 0 { // should never happen at this state.\n\t\treturn t, StandardClaims{}, jwt.ErrMissing\n\t}\n\n\tverifiedToken, err := jwt.VerifyWithHeaderValidator(nil, nil, token, s.keys.ValidateHeader, jwt.Future(time.Minute), jwt.Leeway(time.Minute))\n\tif err != nil {\n\t\treturn t, StandardClaims{}, err\n\t}\n\n\tif s.transformer != nil {\n\t\tif t, err = s.transformer.Transform(ctx, verifiedToken); err != nil {\n\t\t\treturn t, StandardClaims{}, err\n\t\t}\n\t} else {\n\t\tif err = verifiedToken.Claims(&t); err != nil {\n\t\t\treturn t, StandardClaims{}, err\n\t\t}\n\t}\n\n\tstandardClaims := verifiedToken.StandardClaims\n\n\tif n := len(s.providers); n > 0 {\n\t\tfor i := 0; i < n; i++ {\n\t\t\tp := s.providers[i]\n\n\t\t\terr := p.ValidateToken(ctx, standardClaims, t)\n\t\t\tif err != nil {\n\t\t\t\tif i == n-1 { // last provider errored.\n\t\t\t\t\treturn t, StandardClaims{}, err\n\t\t\t\t}\n\t\t\t\t// keep searching.\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// token is allowed.\n\t\t\tbreak\n\t\t}\n\t} else {\n\t\t// return t, StandardClaims{}, fmt.Errorf(\"no provider\")\n\t}\n\n\treturn t, standardClaims, nil\n}\n\n// VerifyHandler verifies and sets the necessary information about the user(claims) and\n// the verified token to the Iris Context and calls the Context's Next method.\n// This information is available through auth.GetAccessToken, auth.GetStandardClaims and\n// auth.GetUser[T] package-level functions.\n//\n// See `Verify` method for more.\nfunc (s *Auth[T]) VerifyHandler(verifyFuncs ...VerifyUserFunc[T]) context.Handler {\n\treturn func(ctx *context.Context) {\n\t\taccessToken := s.ExtractAccessToken(ctx)\n\n\t\tif accessToken == \"\" { // if empty, fire 401.\n\t\t\ts.errorHandler.Unauthenticated(ctx, jwt.ErrMissing)\n\t\t\treturn\n\t\t}\n\n\t\tt, claims, err := s.Verify(ctx, []byte(accessToken), verifyFuncs...)\n\t\tif err != nil {\n\t\t\ts.errorHandler.Unauthenticated(ctx, err)\n\t\t\treturn\n\t\t}\n\n\t\tctx.SetUser(t)\n\n\t\t// store the user to the request.\n\t\tctx.Values().Set(accessTokenContextKey, accessToken)\n\t\tctx.Values().Set(standardClaimsContextKey, claims)\n\t\tctx.Values().Set(userContextKey, t)\n\n\t\tctx.Next()\n\t}\n}\n\n// ExtractAccessToken extracts the access token from the request's header or cookie.\nfunc (s *Auth[T]) ExtractAccessToken(ctx *context.Context) string {\n\t// first try from authorization: bearer header.\n\taccessToken := s.extractTokenFromHeader(ctx)\n\n\t// then if no header, try try extract from cookie.\n\tif accessToken == \"\" {\n\t\tif cookieName := s.config.Cookie.Name; cookieName != \"\" {\n\t\t\taccessToken = ctx.GetCookie(cookieName, context.CookieEncoding(s.securecookie))\n\t\t}\n\t}\n\n\treturn accessToken\n}\n\n// Refresh accepts a previously generated refresh token (from SigninHandler) and\n// returns a new access and refresh token pair.\nfunc (s *Auth[T]) Refresh(ctx stdContext.Context, refreshToken []byte) ([]byte, []byte, error) {\n\tif !s.refreshEnabled {\n\t\treturn nil, nil, fmt.Errorf(\"auth: refresh: disabled\")\n\t}\n\n\tt, _, err := s.verify(ctx, refreshToken)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"auth: refresh: %w\", err)\n\t}\n\n\t// refresh the tokens, both refresh & access tokens will be renew to prevent\n\t// malicious 😈 users that may hold a refresh token.\n\taccessTok, refreshTok, err := s.sign(t)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"auth: refresh: %w\", err)\n\t}\n\n\treturn accessTok, refreshTok, nil\n}\n\n// RefreshHandler reads the request body which should include data for `RefreshRequest` structure\n// and sends a new access and refresh token pair,\n// also sets the cookie to the new encrypted access token value.\n// See `Refresh` method for more.\nfunc (s *Auth[T]) RefreshHandler(ctx *context.Context) {\n\tvar req RefreshRequest\n\terr := ctx.ReadJSON(&req)\n\tif err != nil {\n\t\ts.errorHandler.InvalidArgument(ctx, err)\n\t\treturn\n\t}\n\n\taccessTokenBytes, refreshTokenBytes, err := s.Refresh(ctx, []byte(req.RefreshToken))\n\tif err != nil {\n\t\t// s.tryRemoveCookie(ctx)\n\t\ts.errorHandler.Unauthenticated(ctx, err)\n\t\treturn\n\t}\n\n\taccessToken := jwt.BytesToString(accessTokenBytes)\n\trefreshToken := jwt.BytesToString(refreshTokenBytes)\n\n\ts.trySetCookie(ctx, accessToken)\n\n\tresp := SigninResponse{\n\t\tAccessToken:  accessToken,\n\t\tRefreshToken: refreshToken,\n\t}\n\tctx.JSON(resp)\n}\n\n// Signout accepts the access token and a boolean which reports whether\n// the signout should be applied to all tokens generated for a specific user (logout from all devices)\n// or just the provided token's one.\n// It calls the Provider's InvalidateToken(all=false) or InvalidateTokens (all=true).\nfunc (s *Auth[T]) Signout(ctx stdContext.Context, token []byte, all bool) error {\n\tt, standardClaims, err := s.verify(ctx, token)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"auth: signout: verify: %w\", err)\n\t}\n\n\tfor i, n := 0, len(s.providers)-1; i <= n; i++ {\n\t\tp := s.providers[i]\n\n\t\tif all {\n\t\t\terr = p.InvalidateTokens(ctx, t)\n\t\t} else {\n\t\t\terr = p.InvalidateToken(ctx, standardClaims, t)\n\t\t}\n\n\t\tif err != nil {\n\t\t\tif i == n { // last provider errored.\n\t\t\t\treturn err\n\t\t\t}\n\t\t\t// keep trying.\n\t\t\tcontinue\n\t\t}\n\n\t\t// token is marked as invalidated by a provider.\n\t\tbreak\n\t}\n\n\treturn nil\n}\n\n// SignoutHandler verifies the request's access token and invalidates it, calling\n// the Provider's InvalidateToken method.\n// See `Signout` method too.\nfunc (s *Auth[T]) SignoutHandler(ctx *context.Context) {\n\ts.signoutHandler(ctx, false)\n}\n\n// SignoutAllHandler verifies the request's access token and\n// should invalidate all the tokens generated previously calling\n// the Provider's InvalidateTokens method.\n// See `Signout` method too.\nfunc (s *Auth[T]) SignoutAllHandler(ctx *context.Context) {\n\ts.signoutHandler(ctx, true)\n}\n\nfunc (s *Auth[T]) signoutHandler(ctx *context.Context, all bool) {\n\taccessToken := s.ExtractAccessToken(ctx)\n\tif accessToken == \"\" {\n\t\ts.errorHandler.Unauthenticated(ctx, jwt.ErrMissing)\n\t\treturn\n\t}\n\n\terr := s.Signout(ctx, []byte(accessToken), all)\n\tif err != nil {\n\t\ts.errorHandler.Unauthenticated(ctx, err)\n\t\treturn\n\t}\n\n\ts.tryRemoveCookie(ctx)\n\n\tctx.SetUser(nil)\n\n\tctx.Values().Remove(accessTokenContextKey)\n\tctx.Values().Remove(standardClaimsContextKey)\n\tctx.Values().Remove(userContextKey)\n}\n\nfunc (s *Auth[T]) extractTokenFromHeader(ctx *context.Context) string {\n\tfor _, headerKey := range s.config.Headers {\n\t\theaderValue := ctx.GetHeader(headerKey)\n\t\tif headerValue == \"\" {\n\t\t\tcontinue\n\t\t}\n\n\t\t// pure check: authorization header format must be Bearer {token}\n\t\tauthHeaderParts := strings.Split(headerValue, \" \")\n\t\tif len(authHeaderParts) != 2 || strings.ToLower(authHeaderParts[0]) != \"bearer\" {\n\t\t\tcontinue\n\t\t}\n\n\t\treturn authHeaderParts[1]\n\t}\n\n\treturn \"\"\n}\n\nfunc (s *Auth[T]) trySetCookie(ctx *context.Context, accessToken string) {\n\tif cookieName := s.config.Cookie.Name; cookieName != \"\" {\n\t\tmaxAge := s.keys[KIDAccess].MaxAge\n\t\tif maxAge == 0 {\n\t\t\tmaxAge = context.SetCookieKVExpiration\n\t\t}\n\n\t\tcookie := &http.Cookie{\n\t\t\tName:     cookieName,\n\t\t\tValue:    url.QueryEscape(accessToken),\n\t\t\tPath:     \"/\",\n\t\t\tHttpOnly: true,\n\t\t\tSecure:   s.config.Cookie.Secure || ctx.IsSSL(),\n\t\t\tDomain:   ctx.Domain(),\n\t\t\tSameSite: http.SameSiteLaxMode,\n\t\t\tExpires:  time.Now().Add(maxAge),\n\t\t\tMaxAge:   int(maxAge.Seconds()),\n\t\t}\n\n\t\tctx.SetCookie(cookie, context.CookieEncoding(s.securecookie), context.CookieAllowReclaim())\n\t}\n}\n\nfunc (s *Auth[T]) tryRemoveCookie(ctx *context.Context) {\n\tif cookieName := s.config.Cookie.Name; cookieName != \"\" {\n\t\tctx.RemoveCookie(cookieName)\n\t}\n}\n"
  },
  {
    "path": "auth/configuration.go",
    "content": "//go:build go1.18\n// +build go1.18\n\npackage auth\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"time\"\n\n\t\"github.com/gorilla/securecookie\"\n\t\"github.com/kataras/jwt\"\n\t\"gopkg.in/yaml.v3\"\n)\n\nconst (\n\t// The JWT Key ID for access tokens.\n\tKIDAccess = \"IRIS_AUTH_ACCESS\"\n\t// The JWT Key ID for refresh tokens.\n\tKIDRefresh = \"IRIS_AUTH_REFRESH\"\n)\n\ntype (\n\t// Configuration holds the necessary information for Iris Auth & Single-Sign-On feature.\n\t//\n\t// See the `New` package-level function.\n\tConfiguration struct {\n\t\t// The authorization header keys that server should read the access token from.\n\t\t//\n\t\t// Defaults to:\n\t\t// - Authorization\n\t\t// - X-Authorization\n\t\tHeaders []string `json:\"headers\" yaml:\"Headers\" toml:\"Headers\" ini:\"Headers\"`\n\t\t// Cookie optional configuration.\n\t\t// A Cookie.Name holds the access token value fully encrypted.\n\t\tCookie CookieConfiguration `json:\"cookie\" yaml:\"Cookie\" toml:\"Cookie\" ini:\"cookie\"`\n\t\t// Keys MUST define the jwt keys configuration for access,\n\t\t// and optionally, for refresh tokens signing and verification.\n\t\tKeys jwt.KeysConfiguration `json:\"keys\" yaml:\"Keys\" toml:\"Keys\" ini:\"keys\"`\n\t}\n\n\t// CookieConfiguration holds the necessary information for cookie client storage.\n\tCookieConfiguration struct {\n\t\t// Name defines the cookie's name.\n\t\tName string `json:\"cookie\" yaml:\"Name\" toml:\"Name\" ini:\"name\"`\n\t\t// Secure if true then \"; Secure\" is appended to the Set-Cookie header.\n\t\t// By setting the secure to true, the web browser will prevent the\n\t\t// transmission of a cookie over an unencrypted channel.\n\t\t//\n\t\t// Defaults to false but it's true when the request is under iris.Context.IsSSL().\n\t\tSecure bool `json:\"secure\" yaml:\"Secure\" toml:\"Secure\" ini:\"secure\"`\n\t\t// Hash is optional, it is used to authenticate cookie value using HMAC.\n\t\t// It is recommended to use a key with 32 or 64 bytes.\n\t\tHash string `json:\"hash\" yaml:\"Hash\" toml:\"Hash\" ini:\"hash\"`\n\t\t// Block is optional, used to encrypt cookie value.\n\t\t// The key length must correspond to the block size\n\t\t// of the encryption algorithm. For AES, used by default, valid lengths are\n\t\t// 16, 24, or 32 bytes to select AES-128, AES-192, or AES-256.\n\t\tBlock string `json:\"block\" yaml:\"Block\" toml:\"Block\" ini:\"block\"`\n\t}\n)\n\nfunc (c *Configuration) validate() (jwt.Keys, error) {\n\tif len(c.Headers) == 0 {\n\t\treturn nil, fmt.Errorf(\"auth: configuration: headers slice is empty\")\n\t}\n\n\tif c.Cookie.Name != \"\" {\n\t\tif c.Cookie.Hash == \"\" || c.Cookie.Block == \"\" {\n\t\t\treturn nil, fmt.Errorf(\"auth: configuration: cookie block and cookie hash are required for security reasons when cookie is used\")\n\t\t}\n\t}\n\n\tkeys, err := c.Keys.Load()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"auth: configuration: %w\", err)\n\t}\n\n\tif _, ok := keys[KIDAccess]; !ok {\n\t\treturn nil, fmt.Errorf(\"auth: configuration: %s access token is missing from the configuration\", KIDAccess)\n\t}\n\n\t// Let's keep refresh optional.\n\t// if _, ok := keys[KIDRefresh]; !ok {\n\t// \treturn nil, fmt.Errorf(\"auth: configuration: %s refresh token is missing from the configuration\", KIDRefresh)\n\t// }\n\treturn keys, nil\n}\n\n// BindRandom binds the \"c\" configuration to random values for keys and cookie security.\n// Keys will not be persisted between restarts,\n// a more persistent storage should be considered for production applications,\n// see BindFile method and LoadConfiguration/MustLoadConfiguration package-level functions.\nfunc (c *Configuration) BindRandom() error {\n\taccessPublic, accessPrivate, err := jwt.GenerateEdDSA()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\trefreshPublic, refreshPrivate, err := jwt.GenerateEdDSA()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*c = Configuration{\n\t\tHeaders: []string{\n\t\t\t\"Authorization\",\n\t\t\t\"X-Authorization\",\n\t\t},\n\t\tCookie: CookieConfiguration{\n\t\t\tName:   \"iris_auth_cookie\",\n\t\t\tSecure: false,\n\t\t\tHash:   string(securecookie.GenerateRandomKey(64)),\n\t\t\tBlock:  string(securecookie.GenerateRandomKey(32)),\n\t\t},\n\t\tKeys: jwt.KeysConfiguration{\n\t\t\t{\n\t\t\t\tID:      KIDAccess,\n\t\t\t\tAlg:     jwt.EdDSA.Name(),\n\t\t\t\tMaxAge:  2 * time.Hour,\n\t\t\t\tPublic:  string(accessPublic),\n\t\t\t\tPrivate: string(accessPrivate),\n\t\t\t},\n\t\t\t{\n\t\t\t\tID:            KIDRefresh,\n\t\t\t\tAlg:           jwt.EdDSA.Name(),\n\t\t\t\tMaxAge:        720 * time.Hour,\n\t\t\t\tPublic:        string(refreshPublic),\n\t\t\t\tPrivate:       string(refreshPrivate),\n\t\t\t\tEncryptionKey: string(jwt.MustGenerateRandom(32)),\n\t\t\t},\n\t\t},\n\t}\n\n\treturn nil\n}\n\n// BindFile binds a filename (fullpath) to \"c\" Configuration.\n// The file format is either JSON or YAML and it should be suffixed\n// with .json or .yml/.yaml.\nfunc (c *Configuration) BindFile(filename string) error {\n\tswitch filepath.Ext(filename) {\n\tcase \".json\":\n\t\tcontents, err := os.ReadFile(filename)\n\t\tif err != nil {\n\t\t\tif errors.Is(err, os.ErrNotExist) {\n\t\t\t\tgeneratedConfig := MustGenerateConfiguration()\n\t\t\t\tif generatedYAML, gErr := generatedConfig.ToJSON(); gErr == nil {\n\t\t\t\t\terr = fmt.Errorf(\"%w: example:\\n\\n%s\", err, generatedYAML)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\n\t\treturn json.Unmarshal(contents, c)\n\tdefault:\n\t\tcontents, err := os.ReadFile(filename)\n\t\tif err != nil {\n\t\t\tif errors.Is(err, os.ErrNotExist) {\n\t\t\t\tgeneratedConfig := MustGenerateConfiguration()\n\t\t\t\tif generatedYAML, gErr := generatedConfig.ToYAML(); gErr == nil {\n\t\t\t\t\terr = fmt.Errorf(\"%w: example:\\n\\n%s\", err, generatedYAML)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\n\t\treturn yaml.Unmarshal(contents, c)\n\t}\n}\n\n// ToYAML returns the \"c\" Configuration's contents as raw yaml byte slice.\nfunc (c *Configuration) ToYAML() ([]byte, error) {\n\treturn yaml.Marshal(c)\n}\n\n// ToJSON returns the \"c\" Configuration's contents as raw json byte slice.\nfunc (c *Configuration) ToJSON() ([]byte, error) {\n\treturn json.Marshal(c)\n}\n\n// MustGenerateConfiguration calls the Configuration's BindRandom\n// method and returns the result. It panics on errors.\nfunc MustGenerateConfiguration() (c Configuration) {\n\tif err := c.BindRandom(); err != nil {\n\t\tpanic(err)\n\t}\n\n\treturn\n}\n\n// MustLoadConfiguration same as LoadConfiguration package-level function\n// but it panics on error.\nfunc MustLoadConfiguration(filename string) Configuration {\n\tc, err := LoadConfiguration(filename)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treturn c\n}\n\n// LoadConfiguration reads a filename (fullpath)\n// and returns a Configuration binded to the contents of the given filename.\n// See Configuration.BindFile method too.\nfunc LoadConfiguration(filename string) (c Configuration, err error) {\n\terr = c.BindFile(filename)\n\treturn\n}\n"
  },
  {
    "path": "auth/provider.go",
    "content": "//go:build go1.18\n// +build go1.18\n\npackage auth\n\nimport (\n\tstdContext \"context\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/x/errors\"\n\n\t\"github.com/kataras/jwt\"\n)\n\n// VerifiedToken holds the information about a verified token.\ntype VerifiedToken = jwt.VerifiedToken\n\n// Provider is an interface of T which MUST be completed\n// by a custom value type to provide user information to the Auth's\n// JWT Token Signer and Verifier.\n//\n// A provider can optionally complete the Transformer, ClaimsProvider and\n// ErrorHandler all in once when necessary.\n// Set a provider using the AddProvider method of Auth type.\n//\n// Example can be found at: https://github.com/kataras/iris/tree/main/_examples/auth/auth/user_provider.go.\ntype Provider[T User] interface {\n\t// Signin accepts a username (or email) and a password and should\n\t// return a valid T value or an error describing\n\t// the user authentication or verification reason of failure.\n\t//\n\t// The first input argument standard context can be\n\t// casted to iris.Context if executed through Auth.SigninHandler.\n\t//\n\t// It's called on Auth.SigninHandler.\n\tSignin(ctx stdContext.Context, username, password string) (T, error)\n\n\t// ValidateToken accepts the standard JWT claims and the T value obtained\n\t// by the Signin method and should return a nil error on validation success\n\t// or a non-nil error for validation failure.\n\t// It is mostly used to perform checks of the T value's struct fields or\n\t// the standard claim's (e.g. origin jwt token id).\n\t// It can be an empty method too which returns a nil error.\n\t//\n\t// The first input argument standard context can be\n\t// casted to iris.Context if executed through Auth.VerifyHandler.\n\t//\n\t// It's caleld on Auth.VerifyHandler.\n\tValidateToken(ctx stdContext.Context, standardClaims StandardClaims, t T) error\n\n\t// InvalidateToken is optional and can be used to allow tokens to be invalidated\n\t// from server-side. Commonly, implement when a token and user pair is saved\n\t// on a persistence storage and server can decide which token is valid or invalid.\n\t// It can be an empty method too which returns a nil error.\n\t//\n\t// The first input argument standard context can be\n\t// casted to iris.Context if executed through Auth.SignoutHandler.\n\t//\n\t// It's called on Auth.SignoutHandler.\n\tInvalidateToken(ctx stdContext.Context, standardClaims StandardClaims, t T) error\n\t// InvalidateTokens is like InvalidateToken but it should invalidate\n\t// all tokens generated for a specific T value.\n\t// It can be an empty method too which returns a nil error.\n\t//\n\t// The first input argument standard context can be\n\t// casted to iris.Context if executed through Auth.SignoutAllHandler.\n\t//\n\t// It's called on Auth.SignoutAllHandler.\n\tInvalidateTokens(ctx stdContext.Context, t T) error\n}\n\n// ClaimsProvider is an optional interface, which may not be used at all.\n// If implemented by a Provider, it signs the jwt token\n// using these claims to each of the following token types.\ntype ClaimsProvider interface {\n\tGetAccessTokenClaims() StandardClaims\n\tGetRefreshTokenClaims(accessClaims StandardClaims) StandardClaims\n}\n\n// Transformer is an optional interface which can be implemented by a Provider as well.\n// Set a Transformer through Auth.SetTransformer or Auth.SetTransformerFunc or by implementing\n// the Transform method inside a Provider which can be registered through the Auth.AddProvider\n// method.\n//\n// A transformer is called on Auth.VerifyHandler before Provider.ValidateToken and it can\n// be used to modify the T value based on the token's contents. It is mostly used\n// to convert the json claims to T value manually, when they differ.\n//\n// The first input argument standard context can be\n// casted to iris.Context if executed through Auth.VerifyHandler.\ntype Transformer[T User] interface {\n\tTransform(ctx stdContext.Context, tok *VerifiedToken) (T, error)\n}\n\n// TransformerFunc completes the Transformer interface.\ntype TransformerFunc[T User] func(ctx stdContext.Context, tok *VerifiedToken) (T, error)\n\n// Transform calls itself.\nfunc (fn TransformerFunc[T]) Transform(ctx stdContext.Context, tok *VerifiedToken) (T, error) {\n\treturn fn(ctx, tok)\n}\n\n// ErrorHandler is an optional interface which can be implemented by a Provider as well.\n//\n// ErrorHandler is the interface which controls the HTTP errors on\n// Auth.SigninHandler, Auth.VerifyHandler, Auth.SignoutHandler and\n// Auth.SignoutAllHandler handelrs.\ntype ErrorHandler interface {\n\t// InvalidArgument should handle any 400 (bad request) errors,\n\t// e.g. invalid request body.\n\tInvalidArgument(ctx *context.Context, err error)\n\t// Unauthenticated should handle any 401 (unauthenticated) errors,\n\t// e.g. user not found or invalid credentials.\n\tUnauthenticated(ctx *context.Context, err error)\n}\n\n// DefaultErrorHandler is the default registered ErrorHandler which can be\n// replaced through the Auth.SetErrorHandler method.\ntype DefaultErrorHandler struct{}\n\n// InvalidArgument sends 400 (bad request) with \"unable to parse body\" as its message\n// and the \"err\" value as its details.\nfunc (e *DefaultErrorHandler) InvalidArgument(ctx *context.Context, err error) {\n\terrors.InvalidArgument.Details(ctx, \"unable to parse body\", err.Error())\n}\n\n// Unauthenticated sends 401 (unauthenticated) with the \"err\" value as its message.\nfunc (e *DefaultErrorHandler) Unauthenticated(ctx *context.Context, err error) {\n\terrors.Unauthenticated.Err(ctx, err)\n}\n"
  },
  {
    "path": "auth/user.go",
    "content": "//go:build go1.18\n// +build go1.18\n\npackage auth\n\nimport (\n\t\"github.com/kataras/iris/v12/context\"\n\n\t\"github.com/kataras/jwt\"\n)\n\ntype (\n\t// StandardClaims is an alias of jwt.Claims, it holds the standard JWT claims.\n\tStandardClaims = jwt.Claims\n\t// User is an alias of an empty interface, it's here to declare the typeof T,\n\t// which can be any custom struct type.\n\t//\n\t// Example can be found at: https://github.com/kataras/iris/tree/main/_examples/auth/auth/user.go.\n\tUser = any\n)\n\nconst accessTokenContextKey = \"iris.auth.context.access_token\"\n\n// GetAccessToken accepts the iris Context and returns the raw access token value.\n// It's only available after Auth.VerifyHandler is executed.\nfunc GetAccessToken(ctx *context.Context) string {\n\treturn ctx.Values().GetString(accessTokenContextKey)\n}\n\nconst standardClaimsContextKey = \"iris.auth.context.standard_claims\"\n\n// GetStandardClaims accepts the iris Context and returns the standard token's claims.\n// It's only available after Auth.VerifyHandler is executed.\nfunc GetStandardClaims(ctx *context.Context) StandardClaims {\n\tif v := ctx.Values().Get(standardClaimsContextKey); v != nil {\n\t\tif c, ok := v.(StandardClaims); ok {\n\t\t\treturn c\n\t\t}\n\t}\n\n\treturn StandardClaims{}\n}\n\nconst userContextKey = \"iris.auth.context.user\"\n\n// GetUser is the package-level function of the Auth.GetUser method.\n// It returns the T user value after Auth.VerifyHandler is executed.\nfunc GetUser[T User](ctx *context.Context) T {\n\tif v := ctx.Values().Get(userContextKey); v != nil {\n\t\tif t, ok := v.(T); ok {\n\t\t\treturn t\n\t\t}\n\t}\n\n\tvar empty T\n\treturn empty\n}\n\n// GetUser accepts the iris Context and returns the T custom user/claims struct value.\n// It's only available after Auth.VerifyHandler is executed.\nfunc (s *Auth[T]) GetUser(ctx *context.Context) T {\n\treturn GetUser[T](ctx)\n}\n"
  },
  {
    "path": "cache/browser.go",
    "content": "package cache\n\nimport (\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/cache/client\"\n\t\"github.com/kataras/iris/v12/context\"\n)\n\n// CacheControlHeaderValue is the header value of the\n// \"Cache-Control\": \"private, no-cache, max-age=0, must-revalidate, no-store, proxy-revalidate, s-maxage=0\".\n//\n// It can be overridden.\nvar CacheControlHeaderValue = \"private, no-cache, max-age=0, must-revalidate, no-store, proxy-revalidate, s-maxage=0\"\n\nconst (\n\t// PragmaHeaderKey is the header key of \"Pragma\".\n\tPragmaHeaderKey = \"Pragma\"\n\t// PragmaNoCacheHeaderValue is the header value of \"Pragma\": \"no-cache\".\n\tPragmaNoCacheHeaderValue = \"no-cache\"\n\t// ExpiresHeaderKey is the header key of \"Expires\".\n\tExpiresHeaderKey = \"Expires\"\n\t// ExpiresNeverHeaderValue is the header value of \"ExpiresHeaderKey\": \"0\".\n\tExpiresNeverHeaderValue = \"0\"\n)\n\n// NoCache is a middleware which overrides the Cache-Control, Pragma and Expires headers\n// in order to disable the cache during the browser's back and forward feature.\n//\n// A good use of this middleware is on HTML routes; to refresh the page even on \"back\" and \"forward\" browser's arrow buttons.\n//\n// See `cache#StaticCache` for the opposite behavior.\nvar NoCache = func(ctx *context.Context) {\n\tctx.Header(context.CacheControlHeaderKey, CacheControlHeaderValue)\n\tctx.Header(PragmaHeaderKey, PragmaNoCacheHeaderValue)\n\tctx.Header(ExpiresHeaderKey, ExpiresNeverHeaderValue)\n\t// Add the X-No-Cache header as well, for any customized case, i.e `cache#Handler` or `cache#Cache`.\n\tclient.NoCache(ctx)\n\n\tctx.Next()\n}\n\n// StaticCache middleware for caching static files by sending the \"Cache-Control\" and \"Expires\" headers to the client.\n// It accepts a single input parameter, the \"cacheDur\", a time.Duration that it's used to calculate the expiration.\n//\n// If \"cacheDur\" <=0 then it returns the `NoCache` middleware instaed to disable the caching between browser's \"back\" and \"forward\" actions.\n//\n// Usage: `app.Use(cache.StaticCache(24 * time.Hour))` or `app.Use(cache.Staticcache(-1))`.\n// A middleware, which is a simple Handler can be called inside another handler as well, example:\n// cacheMiddleware := cache.StaticCache(...)\n//\n//\tfunc(ctx iris.Context){\n//\t cacheMiddleware(ctx)\n//\t [...]\n//\t}\nvar StaticCache = func(cacheDur time.Duration) context.Handler {\n\tif int64(cacheDur) <= 0 {\n\t\treturn NoCache\n\t}\n\n\tcacheControlHeaderValue := \"public, max-age=\" + strconv.Itoa(int(cacheDur.Seconds()))\n\treturn func(ctx *context.Context) {\n\t\tcacheUntil := time.Now().Add(cacheDur).Format(ctx.Application().ConfigurationReadOnly().GetTimeFormat())\n\t\tctx.Header(ExpiresHeaderKey, cacheUntil)\n\t\tctx.Header(context.CacheControlHeaderKey, cacheControlHeaderValue)\n\n\t\tctx.Next()\n\t}\n}\n\nconst ifNoneMatchHeaderKey = \"If-None-Match\"\n\n// ETag is another browser & server cache request-response feature.\n// It can be used side by side with the `StaticCache`, usually `StaticCache` middleware should go first.\n// This should be used on routes that serves static files only.\n// The key of the `ETag` is the `ctx.Request().URL.Path`, invalidation of the not modified cache method\n// can be made by other request handler as well.\n//\n// In typical usage, when a URL is retrieved, the web server will return the resource's current\n// representation along with its corresponding ETag value,\n// which is placed in an HTTP response header \"ETag\" field:\n//\n// ETag: \"/mypath\"\n//\n// The client may then decide to cache the representation, along with its ETag.\n// Later, if the client wants to retrieve the same URL resource again,\n// it will first determine whether the local cached version of the URL has expired\n// (through the Cache-Control (`StaticCache` method) and the Expire headers).\n// If the URL has not expired, it will retrieve the local cached resource.\n// If it determined that the URL has expired (is stale), then the client will contact the server\n// and send its previously saved copy of the ETag along with the request in a \"If-None-Match\" field.\n//\n// Usage with combination of `StaticCache`:\n// assets := app.Party(\"/assets\", cache.StaticCache(24 * time.Hour), ETag)\n// assets.HandleDir(\"/\", iris.Dir(\"./assets\"))\n//\n// Similar to `Cache304` but it doesn't depends on any \"modified date\", it uses just the ETag and If-None-Match headers.\n//\n// Read more at: https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching and\n// https://en.wikipedia.org/wiki/HTTP_ETag\nvar ETag = func(ctx *context.Context) {\n\tkey := ctx.Request().URL.Path\n\tctx.Header(context.ETagHeaderKey, key)\n\tif match := ctx.GetHeader(ifNoneMatchHeaderKey); match == key {\n\t\tctx.WriteNotModified()\n\t\treturn\n\t}\n\tctx.Next()\n}\n\n// Cache304 sends a `StatusNotModified` (304) whenever\n// the \"If-Modified-Since\" request header (time) is before the\n// time.Now() + expiresEvery (always compared to their UTC values).\n// Use this `cache#Cache304` instead of the \"github.com/kataras/iris/v12/cache\" or iris.Cache\n// for better performance.\n// Clients that are compatible with the http RCF (all browsers are and tools like postman)\n// will handle the caching.\n// The only disadvantage of using that instead of server-side caching\n// is that this method will send a 304 status code instead of 200,\n// So, if you use it side by side with other micro services\n// you have to check for that status code as well for a valid response.\n//\n// Developers are free to extend this method's behavior\n// by watching system directories changes manually and use of the `ctx.WriteWithExpiration`\n// with a \"modtime\" based on the file modified date,\n// can be used on Party's that contains a static handler,\n// i.e `HandleDir`.\nvar Cache304 = func(expiresEvery time.Duration) context.Handler {\n\treturn func(ctx *context.Context) {\n\t\tnow := time.Now()\n\t\tif modified, err := ctx.CheckIfModifiedSince(now.Add(-expiresEvery)); !modified && err == nil {\n\t\t\tctx.WriteNotModified()\n\t\t\treturn\n\t\t}\n\n\t\tctx.SetLastModified(now)\n\t\tctx.Next()\n\t}\n}\n"
  },
  {
    "path": "cache/browser_test.go",
    "content": "package cache_test\n\nimport (\n\t\"strconv\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/cache\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nfunc TestNoCache(t *testing.T) {\n\tapp := iris.New()\n\tapp.Get(\"/\", cache.NoCache, func(ctx iris.Context) {\n\t\tctx.WriteString(\"no_cache\")\n\t})\n\n\t// tests\n\te := httptest.New(t, app)\n\n\tr := e.GET(\"/\").Expect().Status(httptest.StatusOK)\n\tr.Body().IsEqual(\"no_cache\")\n\tr.Header(context.CacheControlHeaderKey).Equal(cache.CacheControlHeaderValue)\n\tr.Header(cache.PragmaHeaderKey).Equal(cache.PragmaNoCacheHeaderValue)\n\tr.Header(cache.ExpiresHeaderKey).Equal(cache.ExpiresNeverHeaderValue)\n}\n\nfunc TestStaticCache(t *testing.T) {\n\t// test change the time format, which is not recommended but can be done.\n\tapp := iris.New().Configure(iris.WithTimeFormat(\"02 Jan 2006 15:04:05 GMT\"))\n\n\tcacheDur := 30 * (24 * time.Hour)\n\tvar expectedTime time.Time\n\tapp.Get(\"/\", cache.StaticCache(cacheDur), func(ctx iris.Context) {\n\t\texpectedTime = time.Now()\n\t\tctx.WriteString(\"static_cache\")\n\t})\n\n\t// tests\n\te := httptest.New(t, app)\n\tr := e.GET(\"/\").Expect().Status(httptest.StatusOK)\n\tr.Body().IsEqual(\"static_cache\")\n\n\tr.Header(cache.ExpiresHeaderKey).Equal(expectedTime.Add(cacheDur).Format(app.ConfigurationReadOnly().GetTimeFormat()))\n\tcacheControlHeaderValue := \"public, max-age=\" + strconv.Itoa(int(cacheDur.Seconds()))\n\tr.Header(context.CacheControlHeaderKey).Equal(cacheControlHeaderValue)\n}\n\nfunc TestCache304(t *testing.T) {\n\t// t.Parallel()\n\tapp := iris.New()\n\n\texpiresEvery := 4 * time.Second\n\tapp.Get(\"/\", cache.Cache304(expiresEvery), func(ctx iris.Context) {\n\t\tctx.WriteString(\"send\")\n\t})\n\t// handlers\n\te := httptest.New(t, app)\n\n\t// when 304, content type, content length and if ETagg is there are removed from the headers.\n\tinsideCacheTimef := time.Now().Add(-expiresEvery).UTC().Format(app.ConfigurationReadOnly().GetTimeFormat())\n\tr := e.GET(\"/\").WithHeader(context.IfModifiedSinceHeaderKey, insideCacheTimef).Expect().Status(httptest.StatusNotModified)\n\tr.Headers().NotContainsKey(context.ContentTypeHeaderKey).NotContainsKey(context.ContentLengthHeaderKey).NotContainsKey(\"ETag\")\n\tr.Body().IsEqual(\"\")\n\n\t// continue to the handler itself.\n\tcacheInvalidatedTimef := time.Now().Add(expiresEvery).UTC().Format(app.ConfigurationReadOnly().GetTimeFormat()) // after ~5seconds.\n\tr = e.GET(\"/\").WithHeader(context.LastModifiedHeaderKey, cacheInvalidatedTimef).Expect().Status(httptest.StatusOK)\n\tr.Body().IsEqual(\"send\")\n\t// now without header, it should continue to the handler itself as well.\n\tr = e.GET(\"/\").Expect().Status(httptest.StatusOK)\n\tr.Body().IsEqual(\"send\")\n}\n\nfunc TestETag(t *testing.T) {\n\t// t.Parallel()\n\n\tapp := iris.New()\n\tn := \"_\"\n\tapp.Get(\"/\", cache.ETag, func(ctx iris.Context) {\n\t\tctx.WriteString(n)\n\t\tn += \"_\"\n\t})\n\n\t// the first and last test writes the content with status OK without cache,\n\t// the rest tests the cache headers and status 304 and return, so body should be \"\".\n\te := httptest.New(t, app)\n\n\tr := e.GET(\"/\").Expect().Status(httptest.StatusOK)\n\tr.Header(\"ETag\").Equal(\"/\") // test if header set.\n\tr.Body().IsEqual(\"_\")\n\n\te.GET(\"/\").WithHeader(\"ETag\", \"/\").WithHeader(\"If-None-Match\", \"/\").Expect().\n\t\tStatus(httptest.StatusNotModified).Body().IsEqual(\"\") // browser is responsible, no the test engine.\n\n\tr = e.GET(\"/\").Expect().Status(httptest.StatusOK)\n\tr.Header(\"ETag\").Equal(\"/\") // test if header set.\n\tr.Body().IsEqual(\"__\")\n}\n"
  },
  {
    "path": "cache/cache.go",
    "content": "/* Package cache provides server-side caching capabilities with rich support of options and rules.\n\nUse it for server-side caching, see the `iris#Cache304` for an alternative approach that\nmay fit your needs most.\n\nExample code:\n\n\n\t\t import (\n\t\t \t\"time\"\n\n\t\t \t\"github.com/kataras/iris/v12\"\n\t\t \t\"github.com/kataras/iris/v12/cache\"\n\t\t )\n\n\t\t func main(){\n\t\t \tapp := iris.Default()\n\t\t \tmiddleware := cache.Handler(2 *time.Minute)\n\t\t \tapp.Get(\"/hello\", middleware, h)\n\t\t \tapp.Listen(\":8080\")\n\t\t }\n\n\t\t func h(ctx iris.Context) {\n\t\t \tctx.HTML(\"<h1> Hello, this should be cached. Every 2 minutes it will be refreshed, check your browser's inspector</h1>\")\n\t\t }\n*/\n\npackage cache\n\nimport (\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/cache/client\"\n\t\"github.com/kataras/iris/v12/context\"\n)\n\n// WithKey sets a custom entry key for cached pages.\n// Should be prepended to the cache handler.\n//\n// Usage:\n// app.Get(\"/\", cache.WithKey(\"custom-key\"), cache.Handler(time.Minute), mainHandler)\nfunc WithKey(key string) context.Handler {\n\treturn func(ctx *context.Context) {\n\t\tclient.SetKey(ctx, key)\n\t\tctx.Next()\n\t}\n}\n\n// DefaultMaxAge is a function which returns the\n// `context#MaxAge` as time.Duration.\n// It's the default expiration function for the cache handler.\nvar DefaultMaxAge = func(ctx *context.Context) time.Duration {\n\treturn time.Duration(ctx.MaxAge()) * time.Second\n}\n\n// MaxAge is a shortcut to set a simple duration as a MaxAgeFunc.\n//\n// Usage:\n// app.Get(\"/\", cache.Cache(cache.MaxAge(1*time.Minute), mainHandler)\nfunc MaxAge(dur time.Duration) client.MaxAgeFunc {\n\treturn func(*context.Context) time.Duration {\n\t\treturn dur\n\t}\n}\n\n// Cache accepts the cache expiration duration.\n// If the \"maxAgeFunc\" input argument is nil,\n// then expiration is taken by the \"cache-control's maxage\" header.\n// Returns a Handler structure which you can use to customize cache further.\n//\n// All types of response can be cached, templates, json, text, anything.\n//\n// Use it for server-side caching, see the `iris#Cache304` for an alternative approach that\n// may be more suited to your needs.\n//\n// You can add validators with this function.\nfunc Cache(maxAgeFunc client.MaxAgeFunc) *client.Handler {\n\tif maxAgeFunc == nil {\n\t\tmaxAgeFunc = DefaultMaxAge\n\t}\n\n\treturn client.NewHandler(maxAgeFunc)\n}\n\n// Handler like `Cache` but returns an Iris Handler to be used as a middleware.\n// For more options use the `Cache`.\n//\n// Examples can be found at: https://github.com/kataras/iris/tree/main/_examples/response-writer/cache\nfunc Handler(expiration time.Duration) context.Handler {\n\tmaxAgeFunc := func(*context.Context) time.Duration {\n\t\treturn expiration\n\t}\n\n\th := Cache(maxAgeFunc).ServeHTTP\n\treturn h\n}\n"
  },
  {
    "path": "cache/cache_test.go",
    "content": "package cache_test\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/cache\"\n\t\"github.com/kataras/iris/v12/cache/client\"\n\t\"github.com/kataras/iris/v12/cache/client/rule\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/context\"\n\n\t\"github.com/iris-contrib/httpexpect/v2\"\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nvar (\n\tcacheDuration   = 2 * time.Second\n\texpectedBodyStr = \"Imagine it as a big message to achieve x20 response performance!\"\n)\n\ntype testError struct {\n\texpected int\n\tgot      uint32\n}\n\nfunc (h *testError) Error() string {\n\treturn fmt.Sprintf(\"expected the main handler to be executed %d times instead of %d\", h.expected, h.got)\n}\n\nfunc runTest(e *httpexpect.Expect, path string, counterPtr *uint32, expectedBodyStr string, nocache string) error {\n\te.GET(path).Expect().Status(http.StatusOK).Body().IsEqual(expectedBodyStr)\n\ttime.Sleep(cacheDuration / 5) // lets wait for a while, cache should be saved and ready\n\te.GET(path).Expect().Status(http.StatusOK).Body().IsEqual(expectedBodyStr)\n\tcounter := atomic.LoadUint32(counterPtr)\n\tif counter > 1 {\n\t\t// n should be 1 because it doesn't changed after the first call\n\t\treturn &testError{1, counter}\n\t}\n\ttime.Sleep(cacheDuration)\n\n\t// cache should be cleared now\n\te.GET(path).Expect().Status(http.StatusOK).Body().IsEqual(expectedBodyStr)\n\ttime.Sleep(cacheDuration / 5)\n\t// let's call again , the cache should be saved\n\te.GET(path).Expect().Status(http.StatusOK).Body().IsEqual(expectedBodyStr)\n\tcounter = atomic.LoadUint32(counterPtr)\n\tif counter != 2 {\n\t\treturn &testError{2, counter}\n\t}\n\n\t// we have cache response saved for the path, we have some time more here, but here\n\t// we will make the requestS with some of the deniers options\n\te.GET(path).WithHeader(\"max-age\", \"0\").Expect().Status(http.StatusOK).Body().IsEqual(expectedBodyStr)\n\te.GET(path).WithHeader(\"Authorization\", \"basic or anything\").Expect().Status(http.StatusOK).Body().IsEqual(expectedBodyStr)\n\tcounter = atomic.LoadUint32(counterPtr)\n\tif counter != 4 {\n\t\treturn &testError{4, counter}\n\t}\n\n\tif nocache != \"\" {\n\t\t// test the NoCache, first sleep to pass the cache expiration,\n\t\t// second add to the cache with a valid request and response\n\t\t// third, do it with the \"/nocache\" path (static for now, pure test design) given by the consumer\n\t\ttime.Sleep(cacheDuration)\n\n\t\t// cache should be cleared now, this should work because we are not in the \"nocache\" path\n\t\te.GET(\"/\").Expect().Status(http.StatusOK).Body().IsEqual(expectedBodyStr) // counter = 5\n\t\ttime.Sleep(cacheDuration / 5)\n\n\t\t// let's call the \"nocache\", the expiration is not passed so but the \"nocache\"\n\t\t// route's path has the cache.NoCache so it should be not cached and the counter should be ++\n\t\te.GET(nocache).Expect().Status(http.StatusOK).Body().IsEqual(expectedBodyStr) // counter should be 6\n\t\tcounter = atomic.LoadUint32(counterPtr)\n\t\tif counter != 6 { // 4 before, 5 with the first call to store the cache, and six with the no cache, again original handler executation\n\t\t\treturn &testError{6, counter}\n\t\t}\n\n\t\t// let's call again the path the expiration is not passed so  it should be cached\n\t\te.GET(path).Expect().Status(http.StatusOK).Body().IsEqual(expectedBodyStr)\n\t\tcounter = atomic.LoadUint32(counterPtr)\n\t\tif counter != 6 {\n\t\t\treturn &testError{6, counter}\n\t\t}\n\n\t\t// but now check for the No\n\t}\n\n\treturn nil\n}\n\nfunc TestClientNoCache(t *testing.T) {\n\tapp := iris.New()\n\tvar n uint32\n\n\tapp.Get(\"/\", cache.Handler(cacheDuration), func(ctx *context.Context) {\n\t\tatomic.AddUint32(&n, 1)\n\t\tctx.Write([]byte(expectedBodyStr))\n\t})\n\n\tapp.Get(\"/nocache\", cache.Handler(cacheDuration), func(ctx *context.Context) {\n\t\tclient.NoCache(ctx) // <----\n\t\tatomic.AddUint32(&n, 1)\n\t\tctx.Write([]byte(expectedBodyStr))\n\t})\n\n\te := httptest.New(t, app)\n\tif err := runTest(e, \"/\", &n, expectedBodyStr, \"/nocache\"); err != nil {\n\t\tt.Fatalf(t.Name()+\": %v\", err)\n\t}\n}\n\nfunc TestCache(t *testing.T) {\n\tapp := iris.New()\n\tvar n uint32\n\n\tapp.Use(cache.Handler(cacheDuration))\n\n\tapp.Get(\"/\", func(ctx *context.Context) {\n\t\tatomic.AddUint32(&n, 1)\n\t\tctx.Write([]byte(expectedBodyStr))\n\t})\n\n\tvar (\n\t\tn2               uint32\n\t\texpectedBodyStr2 = \"This is the other\"\n\t)\n\n\tapp.Get(\"/other\", func(ctx *context.Context) {\n\t\tatomic.AddUint32(&n2, 1)\n\t\tctx.Write([]byte(expectedBodyStr2))\n\t})\n\n\te := httptest.New(t, app)\n\tif err := runTest(e, \"/\", &n, expectedBodyStr, \"\"); err != nil {\n\t\tt.Fatalf(t.Name()+\": %v\", err)\n\t}\n\n\tif err := runTest(e, \"/other\", &n2, expectedBodyStr2, \"\"); err != nil {\n\t\tt.Fatalf(t.Name()+\" other: %v\", err)\n\t}\n}\n\n// This works but we have issue on golog.SetLevel and get golog.Level on httptest.New\n// when tests are running in parallel and the loggers are used.\n// // TODO: Fix it on golog repository or here, we'll see.\n// func TestCacheHandlerParallel(t *testing.T) {\n// \tt.Parallel()\n// \tTestCache(t)\n// }\n\nfunc TestCacheValidator(t *testing.T) {\n\tapp := iris.New()\n\tvar n uint32\n\n\th := func(ctx *context.Context) {\n\t\tatomic.AddUint32(&n, 1)\n\t\tctx.Write([]byte(expectedBodyStr))\n\t}\n\n\tvalidCache := cache.Handler(cacheDuration)\n\tapp.Get(\"/\", validCache, h)\n\n\tmanagedCache := cache.Cache(cache.MaxAge(cacheDuration))\n\tmanagedCache.AddRule(rule.Validator([]rule.PreValidator{\n\t\tfunc(ctx *context.Context) bool {\n\t\t\t// should always invalid for cache, don't bother to go to try to get or set cache\n\t\t\treturn ctx.Request().URL.Path != \"/invalid\"\n\t\t},\n\t}, nil))\n\n\tmanagedCache2 := cache.Cache(cache.MaxAge(cacheDuration))\n\tmanagedCache2.AddRule(rule.Validator(nil,\n\t\t[]rule.PostValidator{\n\t\t\tfunc(ctx *context.Context) bool {\n\t\t\t\t// it's passed the Claim and now Valid checks if the response contains a header of \"DONT\"\n\t\t\t\treturn ctx.ResponseWriter().Header().Get(\"DONT\") == \"\"\n\t\t\t},\n\t\t},\n\t))\n\n\tapp.Get(\"/valid\", validCache, h)\n\n\tapp.Get(\"/invalid\", managedCache.ServeHTTP, h)\n\tapp.Get(\"/invalid2\", managedCache2.ServeHTTP, func(ctx *context.Context) {\n\t\tatomic.AddUint32(&n, 1)\n\t\tctx.Header(\"DONT\", \"DO not cache that response even if it was claimed\")\n\t\tctx.Write([]byte(expectedBodyStr))\n\t})\n\n\te := httptest.New(t, app)\n\n\t// execute from cache the next time\n\te.GET(\"/valid\").Expect().Status(http.StatusOK).Body().IsEqual(expectedBodyStr)\n\ttime.Sleep(cacheDuration / 5) // lets wait for a while, cache should be saved and ready\n\te.GET(\"/valid\").Expect().Status(http.StatusOK).Body().IsEqual(expectedBodyStr)\n\tcounter := atomic.LoadUint32(&n)\n\tif counter > 1 {\n\t\t// n should be 1 because it doesn't changed after the first call\n\t\tt.Fatalf(\"%s: %v\", t.Name(), &testError{1, counter})\n\t}\n\t// don't execute from cache, execute the original, counter should ++ here\n\te.GET(\"/invalid\").Expect().Status(http.StatusOK).Body().IsEqual(expectedBodyStr)  // counter = 2\n\te.GET(\"/invalid2\").Expect().Status(http.StatusOK).Body().IsEqual(expectedBodyStr) // counter = 3\n\n\tcounter = atomic.LoadUint32(&n)\n\tif counter != 3 {\n\t\t// n should be 1 because it doesn't changed after the first call\n\t\tt.Fatalf(\"%s: %v\", t.Name(), &testError{3, counter})\n\t}\n}\n"
  },
  {
    "path": "cache/cfg/cfg.go",
    "content": "package cfg\n\nimport \"time\"\n\n// The constants be used by both client and server\nvar (\n\tFailStatus            = 400\n\tSuccessStatus         = 200\n\tContentHTML           = \"text/html; charset=utf-8\"\n\tContentTypeHeader     = \"Content-Type\"\n\tStatusCodeHeader      = \"Status\"\n\tQueryCacheKey         = \"cache_key\"\n\tQueryCacheDuration    = \"cache_duration\"\n\tQueryCacheStatusCode  = \"cache_status_code\"\n\tQueryCacheContentType = \"cache_content_type\"\n\tRequestCacheTimeout   = 5 * time.Second\n)\n\n// NoCacheHeader is the static header key which is set to the response when NoCache is called,\n// used inside nethttp and fhttp Skippers.\nvar NoCacheHeader = \"X-No-Cache\"\n\n// MinimumCacheDuration is the minimum duration from time.Now\n// which is allowed between cache save and cache clear\nvar MinimumCacheDuration = 2 * time.Second\n"
  },
  {
    "path": "cache/client/client.go",
    "content": "package client\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/cache/cfg\"\n\t\"github.com/kataras/iris/v12/cache/client/rule\"\n\t\"github.com/kataras/iris/v12/cache/uri\"\n\t\"github.com/kataras/iris/v12/context\"\n)\n\n// ClientHandler is the client-side handler\n// for each of the cached route paths's response\n// register one client handler per route.\n//\n// it's just calls a remote cache service server/handler, which may lives on other, external machine.\ntype ClientHandler struct {\n\t// bodyHandler the original route's handler\n\tbodyHandler context.Handler\n\n\t// Rule optional validators for pre cache and post cache actions\n\t//\n\t// See more at ruleset.go\n\trule rule.Rule\n\n\tlife time.Duration\n\n\tremoteHandlerURL string\n}\n\n// NewClientHandler returns a new remote client handler\n// which asks the remote handler the cached entry's response\n// with a GET request, or add a response with POST request\n// these all are done automatically, users can use this\n// handler as they use the local.go/NewHandler\n//\n// the ClientHandler is useful when user\n// wants to apply horizontal scaling to the app and\n// has a central http server which handles\nfunc NewClientHandler(bodyHandler context.Handler, life time.Duration, remote string) *ClientHandler {\n\treturn &ClientHandler{\n\t\tbodyHandler:      bodyHandler,\n\t\trule:             DefaultRuleSet,\n\t\tlife:             life,\n\t\tremoteHandlerURL: remote,\n\t}\n}\n\n// Rule sets the ruleset for this handler,\n// see internal/net/http/ruleset.go for more information.\n//\n// returns itself.\nfunc (h *ClientHandler) Rule(r rule.Rule) *ClientHandler {\n\tif r == nil {\n\t\t// if nothing passed then use the allow-everything rule\n\t\tr = rule.Satisfied()\n\t}\n\th.rule = r\n\n\treturn h\n}\n\n// AddRule adds a rule in the chain, the default rules are executed first.\n//\n// returns itself.\nfunc (h *ClientHandler) AddRule(r rule.Rule) *ClientHandler {\n\tif r == nil {\n\t\treturn h\n\t}\n\n\th.rule = rule.Chained(h.rule, r)\n\treturn h\n}\n\n// Client is used inside the global Request function\n// this client is an exported to give you a freedom of change its Transport, Timeout and so on(in case of ssl)\nvar Client = &http.Client{Timeout: cfg.RequestCacheTimeout}\n\n// ServeHTTP , or remote cache client whatever you like, it's the client-side function of the ServeHTTP\n// sends a request to the server-side remote cache Service and sends the cached response to the frontend client\n// it is used only when you achieved something like horizontal scaling (separate machines)\n// look ../remote/remote.ServeHTTP for more\n//\n// if cache din't find then it sends a POST request and save the bodyHandler's body to the remote cache.\n//\n// It takes 3 parameters\n// the first is the remote address (it's the address you started your http server which handled by the Service.ServeHTTP)\n// the second is the handler (or the mux) you want to cache\n// and the  third is the, optionally, cache expiration,\n// which is used to set cache duration of this specific cache entry to the remote cache service\n// if <=minimumAllowedCacheDuration then the server will try to parse from \"cache-control\" header\n//\n// client-side function\nfunc (h *ClientHandler) ServeHTTP(ctx *context.Context) {\n\t// check for deniers, if at least one of them return true\n\t// for this specific request, then skip the whole cache\n\tif !h.rule.Claim(ctx) {\n\t\th.bodyHandler(ctx)\n\t\treturn\n\t}\n\n\turi := &uri.URIBuilder{}\n\turi.ServerAddr(h.remoteHandlerURL).ClientURI(ctx.Request().URL.RequestURI()).ClientMethod(ctx.Request().Method)\n\n\t// set the full url here because below we have other issues, probably net/http bugs\n\trequest, err := http.NewRequest(http.MethodGet, uri.String(), nil)\n\tif err != nil {\n\t\t//// println(\"error when requesting to the remote service: \" + err.Error())\n\t\t// somehing very bad happens, just execute the user's handler and return\n\t\th.bodyHandler(ctx)\n\t\treturn\n\t}\n\n\t// println(\"GET Do to the remote cache service with the url: \" + request.URL.String())\n\tresponse, err := Client.Do(request)\n\n\tif err != nil || response.StatusCode == cfg.FailStatus {\n\n\t\t// if not found on cache, then execute the handler and save the cache to the remote server\n\t\trecorder := ctx.Recorder()\n\t\th.bodyHandler(ctx)\n\n\t\t// check if it's a valid response, if it's not then just return.\n\t\tif !h.rule.Valid(ctx) {\n\t\t\treturn\n\t\t}\n\t\t// save to the remote cache\n\t\t// we re-create the request for any case\n\t\tbody := recorder.Body()[0:]\n\t\tif len(body) == 0 {\n\t\t\t//// println(\"Request: len body is zero, do nothing\")\n\t\t\treturn\n\t\t}\n\t\turi.StatusCode(recorder.StatusCode())\n\t\turi.Lifetime(h.life)\n\t\turi.ContentType(recorder.Header().Get(cfg.ContentTypeHeader))\n\n\t\trequest, err = http.NewRequest(http.MethodPost, uri.String(), bytes.NewBuffer(body)) // yes new buffer every time\n\n\t\t// println(\"POST Do to the remote cache service with the url: \" + request.URL.String())\n\t\tif err != nil {\n\t\t\t//// println(\"Request: error on method Post of request to the remote: \" + err.Error())\n\t\t\treturn\n\t\t}\n\t\t// go Client.Do(request)\n\t\tresp, err := Client.Do(request)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tresp.Body.Close()\n\t} else {\n\t\t// get the status code , content type and the write the response body\n\t\tctx.ContentType(response.Header.Get(cfg.ContentTypeHeader))\n\t\tctx.StatusCode(response.StatusCode)\n\t\tresponseBody, err := io.ReadAll(response.Body)\n\t\tresponse.Body.Close()\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\t_, _ = ctx.Write(responseBody)\n\t}\n}\n"
  },
  {
    "path": "cache/client/handler.go",
    "content": "package client\n\nimport (\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/cache/client/rule\"\n\t\"github.com/kataras/iris/v12/cache/entry\"\n\t\"github.com/kataras/iris/v12/context\"\n)\n\nfunc init() {\n\tcontext.SetHandlerName(\"iris/cache/client.(*Handler).ServeHTTP-fm\", \"iris.cache\")\n}\n\n// Handler the local cache service handler contains\n// the original response, the memory cache entry and\n// the validator for each of the incoming requests and post responses\ntype Handler struct {\n\t// Rule optional validators for pre cache and post cache actions\n\t//\n\t// See more at ruleset.go\n\trule rule.Rule\n\t// when expires.\n\tmaxAgeFunc MaxAgeFunc\n\t// entries the memory cache stored responses.\n\tentryPool  *entry.Pool\n\tentryStore entry.Store\n}\n\ntype MaxAgeFunc func(*context.Context) time.Duration\n\n// NewHandler returns a new Server-side cached handler for the \"bodyHandler\"\n// which expires every \"expiration\".\nfunc NewHandler(maxAgeFunc MaxAgeFunc) *Handler {\n\treturn &Handler{\n\t\trule:       DefaultRuleSet,\n\t\tmaxAgeFunc: maxAgeFunc,\n\n\t\tentryPool:  entry.NewPool(),\n\t\tentryStore: entry.NewMemStore(),\n\t}\n}\n\n// Rule sets the ruleset for this handler.\n//\n// returns itself.\nfunc (h *Handler) Rule(r rule.Rule) *Handler {\n\tif r == nil {\n\t\t// if nothing passed then use the allow-everything rule\n\t\tr = rule.Satisfied()\n\t}\n\th.rule = r\n\n\treturn h\n}\n\n// AddRule adds a rule in the chain, the default rules are executed first.\n//\n// returns itself.\nfunc (h *Handler) AddRule(r rule.Rule) *Handler {\n\tif r == nil {\n\t\treturn h\n\t}\n\n\th.rule = rule.Chained(h.rule, r)\n\treturn h\n}\n\n// Store sets a custom store for this handler.\nfunc (h *Handler) Store(store entry.Store) *Handler {\n\th.entryStore = store\n\treturn h\n}\n\n// MaxAge customizes the expiration duration for this handler.\nfunc (h *Handler) MaxAge(fn MaxAgeFunc) *Handler {\n\th.maxAgeFunc = fn\n\treturn h\n}\n\nvar emptyHandler = func(ctx *context.Context) {\n\tctx.StopWithText(500, \"cache: empty body handler\")\n}\n\nconst entryKeyContextKey = \"iris.cache.server.entry.key\"\n\n// SetKey sets a custom entry key for cached pages.\n// See root package-level `WithKey` instead.\nfunc SetKey(ctx *context.Context, key string) {\n\tctx.Values().Set(entryKeyContextKey, key)\n}\n\n// GetKey returns the entry key for the current page.\nfunc GetKey(ctx *context.Context) string {\n\treturn ctx.Values().GetString(entryKeyContextKey)\n}\n\nfunc getOrSetKey(ctx *context.Context) string {\n\tif key := GetKey(ctx); key != \"\" {\n\t\treturn key\n\t}\n\n\t// Note: by-default the rules(ruleset pkg)\n\t// explicitly ignores the cache handler\n\t// execution on authenticated requests\n\t// and immediately runs the next handler:\n\t// if !h.rule.Claim(ctx) ...see `Handler` method.\n\t// So the below two lines are useless,\n\t// however we add it for cases\n\t// that the end-developer messedup with the rules\n\t// and by accident allow authenticated cached results.\n\tusername, password, _ := ctx.Request().BasicAuth()\n\tauthPart := username + strings.Repeat(\"*\", len(password))\n\n\tkey := ctx.Method() + authPart\n\n\tu := ctx.Request().URL\n\tif !u.IsAbs() {\n\t\tkey += ctx.Scheme() + ctx.Host()\n\t}\n\tkey += u.String()\n\n\tSetKey(ctx, key)\n\treturn key\n}\n\nfunc (h *Handler) ServeHTTP(ctx *context.Context) {\n\t// check for pre-cache validators, if at least one of them return false\n\t// for this specific request, then skip the whole cache\n\tbodyHandler := ctx.NextHandler()\n\tif bodyHandler == nil {\n\t\temptyHandler(ctx)\n\t\treturn\n\t}\n\t// skip prepares the context to move to the next handler if the \"nextHandler\" has a ctx.Next() inside it,\n\t// even if it's not executed because it's cached.\n\tctx.Skip()\n\n\tif !h.rule.Claim(ctx) {\n\t\tbodyHandler(ctx)\n\t\treturn\n\t}\n\n\tkey := getOrSetKey(ctx) // unique per subdomains and paths with different url query.\n\n\te := h.entryStore.Get(key)\n\tif e == nil {\n\t\t// if it's expired, then execute the original handler\n\t\t// with our custom response recorder response writer\n\t\t// because the net/http doesn't give us\n\t\t// a builtin way to get the status code & body\n\t\trecorder := ctx.Recorder()\n\t\tbodyHandler(ctx)\n\n\t\t// now that we have recordered the response,\n\t\t// we are ready to check if that specific response is valid to be stored.\n\n\t\t// check if it's a valid response, if it's not then just return.\n\t\tif !h.rule.Valid(ctx) {\n\t\t\treturn\n\t\t}\n\n\t\t// no need to copy the body, its already done inside\n\t\tbody := recorder.Body()\n\t\tif len(body) == 0 {\n\t\t\t// if no body then just exit.\n\t\t\treturn\n\t\t}\n\n\t\t// fmt.Printf(\"reset cache entry\\n\")\n\t\t// fmt.Printf(\"key: %s\\n\", key)\n\t\t// fmt.Printf(\"content type: %s\\n\", recorder.Header().Get(cfg.ContentTypeHeader))\n\t\t// fmt.Printf(\"body len: %d\\n\", len(body))\n\n\t\tr := entry.NewResponse(recorder.StatusCode(), recorder.Header(), body)\n\t\te = h.entryPool.Acquire(h.maxAgeFunc(ctx), r, func() {\n\t\t\th.entryStore.Delete(key)\n\t\t})\n\n\t\th.entryStore.Set(key, e)\n\t\treturn\n\t}\n\n\t// if it's valid then just write the cached results\n\tr := e.Response()\n\t// if !ok {\n\t// \t// it shouldn't be happen because if it's not valid (= expired)\n\t// \t// then it shouldn't be found on the store, we return as it is, the body was written.\n\t// \treturn\n\t// }\n\n\tcopyHeaders(ctx.ResponseWriter().Header(), r.Headers())\n\tctx.SetLastModified(e.LastModified)\n\tctx.StatusCode(r.StatusCode())\n\tctx.Write(r.Body())\n\n\t// fmt.Printf(\"key: %s\\n\", key)\n\t// fmt.Printf(\"write content type: %s\\n\", response.Headers()[\"ContentType\"])\n\t// fmt.Printf(\"write body len: %d\\n\", len(response.Body()))\n}\n\nfunc copyHeaders(dst, src http.Header) {\n\t// Clone returns a copy of h or nil if h is nil.\n\tif src == nil {\n\t\treturn\n\t}\n\n\t// Find total number of values.\n\tnv := 0\n\tfor _, vv := range src {\n\t\tnv += len(vv)\n\t}\n\n\tsv := make([]string, nv) // shared backing array for headers' values\n\tfor k, vv := range src {\n\t\tif vv == nil {\n\t\t\t// Preserve nil values. ReverseProxy distinguishes\n\t\t\t// between nil and zero-length header values.\n\t\t\tdst[k] = nil\n\t\t\tcontinue\n\t\t}\n\n\t\tn := copy(sv, vv)\n\t\tdst[k] = sv[:n:n]\n\t\tsv = sv[n:]\n\t}\n}\n"
  },
  {
    "path": "cache/client/rule/chained.go",
    "content": "package rule\n\nimport (\n\t\"github.com/kataras/iris/v12/context\"\n)\n\n// chainedRule is a Rule with next Rule\ntype chainedRule struct {\n\tRule\n\tnext Rule\n}\n\nvar _ Rule = &chainedRule{}\n\n// chainedSingle returns a new rule witch has a next rule too\nfunc chainedSingle(rule Rule, next Rule) Rule {\n\tif next == nil {\n\t\tnext = Satisfied()\n\t}\n\n\treturn &chainedRule{\n\t\tRule: rule,\n\t\tnext: next,\n\t}\n}\n\n// Chained returns a new rule which has more than one coming next ruleset\nfunc Chained(rule Rule, next ...Rule) Rule {\n\tif len(next) == 0 {\n\t\treturn chainedSingle(rule, nil)\n\t}\n\tc := chainedSingle(rule, next[0])\n\n\tfor i := 1; i < len(next); i++ {\n\t\tc = chainedSingle(c, next[i])\n\t}\n\n\treturn c\n}\n\n// Claim validator\nfunc (c *chainedRule) Claim(ctx *context.Context) bool {\n\tif !c.Rule.Claim(ctx) {\n\t\treturn false\n\t}\n\treturn c.next.Claim(ctx)\n}\n\n// Valid validator\nfunc (c *chainedRule) Valid(ctx *context.Context) bool {\n\tif !c.Rule.Valid(ctx) {\n\t\treturn false\n\t}\n\treturn c.next.Valid(ctx)\n}\n"
  },
  {
    "path": "cache/client/rule/conditional.go",
    "content": "package rule\n\nimport (\n\t\"github.com/kataras/iris/v12/context\"\n)\n\n// Conditional is a Rule witch adds a predicate in order to its methods to execute\ntype conditionalRule struct {\n\tclaimPredicate func() bool\n\tvalidPredicate func() bool\n}\n\nvar emptyConditionalPredicate = func() bool {\n\treturn true\n}\n\nvar _ Rule = &conditionalRule{}\n\n// Conditional returns a new rule witch has conditionals\nfunc Conditional(claimPredicate func() bool, validPredicate func() bool) Rule {\n\tif claimPredicate == nil {\n\t\tclaimPredicate = emptyConditionalPredicate\n\t}\n\n\tif validPredicate == nil {\n\t\tvalidPredicate = emptyConditionalPredicate\n\t}\n\n\treturn &conditionalRule{\n\t\tclaimPredicate: claimPredicate,\n\t\tvalidPredicate: validPredicate,\n\t}\n}\n\n// Claim validator\nfunc (c *conditionalRule) Claim(ctx *context.Context) bool {\n\treturn c.claimPredicate()\n}\n\n// Valid validator\nfunc (c *conditionalRule) Valid(ctx *context.Context) bool {\n\treturn c.validPredicate()\n}\n"
  },
  {
    "path": "cache/client/rule/header.go",
    "content": "package rule\n\nimport (\n\t\"github.com/kataras/iris/v12/context\"\n\n\t\"github.com/kataras/iris/v12/cache/ruleset\"\n)\n\n// The HeaderPredicate should be alived on each of $package/rule BUT GOLANG DOESN'T SUPPORT type alias and I don't want to have so many copies around\n// read more at ../../ruleset.go\n\n// headerRule is a Rule witch receives and checks for a header predicates\n// request headers on Claim and response headers on Valid.\ntype headerRule struct {\n\tclaim ruleset.HeaderPredicate\n\tvalid ruleset.HeaderPredicate\n}\n\nvar _ Rule = &headerRule{}\n\n// Header returns a new rule witch claims and execute the post validations trough headers\nfunc Header(claim ruleset.HeaderPredicate, valid ruleset.HeaderPredicate) Rule {\n\tif claim == nil {\n\t\tclaim = ruleset.EmptyHeaderPredicate\n\t}\n\n\tif valid == nil {\n\t\tvalid = ruleset.EmptyHeaderPredicate\n\t}\n\n\treturn &headerRule{\n\t\tclaim: claim,\n\t\tvalid: valid,\n\t}\n}\n\n// HeaderClaim returns a header rule which cares only about claiming (pre-validation)\nfunc HeaderClaim(claim ruleset.HeaderPredicate) Rule {\n\treturn Header(claim, nil)\n}\n\n// HeaderValid returns a header rule which cares only about valid (post-validation)\nfunc HeaderValid(valid ruleset.HeaderPredicate) Rule {\n\treturn Header(nil, valid)\n}\n\n// Claim validator\nfunc (h *headerRule) Claim(ctx *context.Context) bool {\n\treturn h.claim(ctx.Request().Header.Get)\n}\n\n// Valid validator\nfunc (h *headerRule) Valid(ctx *context.Context) bool {\n\treturn h.valid(ctx.ResponseWriter().Header().Get)\n}\n"
  },
  {
    "path": "cache/client/rule/not_satisfied.go",
    "content": "package rule\n\nimport (\n\t\"github.com/kataras/iris/v12/context\"\n)\n\ntype notSatisfiedRule struct{}\n\nvar _ Rule = &notSatisfiedRule{}\n\n// NotSatisfied returns a rule which allows nothing\nfunc NotSatisfied() Rule {\n\treturn &notSatisfiedRule{}\n}\n\nfunc (n *notSatisfiedRule) Claim(*context.Context) bool {\n\treturn false\n}\n\nfunc (n *notSatisfiedRule) Valid(*context.Context) bool {\n\treturn false\n}\n"
  },
  {
    "path": "cache/client/rule/rule.go",
    "content": "package rule\n\nimport \"github.com/kataras/iris/v12/context\"\n\n// Rule a superset of validators\ntype Rule interface {\n\tClaim(ctx *context.Context) bool\n\tValid(ctx *context.Context) bool\n}\n"
  },
  {
    "path": "cache/client/rule/satisfied.go",
    "content": "package rule\n\nimport (\n\t\"github.com/kataras/iris/v12/context\"\n)\n\ntype satisfiedRule struct{}\n\nvar _ Rule = &satisfiedRule{}\n\n// Satisfied returns a rule which allows anything,\n// it's usually the last rule on chained rules if no next rule is given,\n// but it can be used outside of a chain too as a default allow-all rule.\nfunc Satisfied() Rule {\n\treturn &satisfiedRule{}\n}\n\nfunc (n *satisfiedRule) Claim(*context.Context) bool {\n\treturn true\n}\n\nfunc (n *satisfiedRule) Valid(*context.Context) bool {\n\treturn true\n}\n"
  },
  {
    "path": "cache/client/rule/validator.go",
    "content": "package rule\n\nimport \"github.com/kataras/iris/v12/context\"\n\n// Validators are introduced to implement the RFC about cache (https://tools.ietf.org/html/rfc7234#section-1.1).\n\n// PreValidator like middleware, executes before the cache action begins, if a callback returns false\n// then this specific cache action, with specific request, is ignored and the real (original)\n// handler is executed instead.\n//\n// I'll not add all specifications here I'll give the opportunity (public API in the httpcache package-level)\n// to the end-user to specify her/his ignore rules too (ignore-only for now).\n//\n// Each package, nethttp and fhttp should implement their own encapsulations because of different request object.\n//\n// One function, accepts the request and returns false if should be denied/ignore, otherwise true.\n// if at least one return false then the original handler will execute as it's\n// and the whole cache action(set & get) should be ignored, it will be never go to the step of post-cache validations.\ntype PreValidator func(*context.Context) bool\n\n// PostValidator type is is introduced to implement the second part of the RFC about cache.\n//\n// Q: What's the difference between this and a PreValidator?\n// A: PreValidator runs BEFORE trying to get the cache, it cares only for the request\n//\n//\tand if at least one PreValidator returns false then it just runs the original handler and stop there, at the other hand\n//\ta PostValidator runs if all PreValidators returns true and original handler is executed but with a response recorder,\n//\talso the PostValidator should return true to store the cached response.\n//\tLast, a PostValidator accepts a context\n//\tin order to be able to catch the original handler's response,\n//\tthe PreValidator checks only for request.\n//\n// If a function of type of PostValidator returns true then the (shared-always) cache is allowed to be stored.\ntype PostValidator func(*context.Context) bool\n\n// validatorRule is a rule witch receives PreValidators and PostValidators\n// it's a 'complete set of rules', you can call it as a Responsible Validator,\n// it's used when you the user wants to check for special things inside a request and a response.\ntype validatorRule struct {\n\t// preValidators a list of PreValidator functions, execute before real cache begins\n\t// if at least one of them returns false then the original handler will execute as it's\n\t// and the whole cache action(set & get) will be skipped for this specific client's request.\n\t//\n\t// Read-only 'runtime'\n\tpreValidators []PreValidator\n\n\t// postValidators a list of PostValidator functions, execute after the original handler is executed with the response recorder\n\t// and exactly before this cached response is saved,\n\t// if at least one of them returns false then the response will be not saved for this specific client's request.\n\t//\n\t// Read-only 'runtime'\n\tpostValidators []PostValidator\n}\n\nvar _ Rule = &validatorRule{}\n\n// DefaultValidator returns a new validator which contains the default pre and post cache validators\nfunc DefaultValidator() Rule { return Validator(nil, nil) }\n\n// Validator receives the preValidators and postValidators and returns a new Validator rule\nfunc Validator(preValidators []PreValidator, postValidators []PostValidator) Rule {\n\treturn &validatorRule{\n\t\tpreValidators:  preValidators,\n\t\tpostValidators: postValidators,\n\t}\n}\n\n// Claim returns true if incoming request can claim for a cached handler\n// the original handler should run as it is and exit\nfunc (v *validatorRule) Claim(ctx *context.Context) bool {\n\t// check for pre-cache validators, if at least one of them return false\n\t// for this specific request, then skip the whole cache\n\tfor _, shouldProcess := range v.preValidators {\n\t\tif !shouldProcess(ctx) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\n// Valid returns true if incoming request and post-response from the original handler\n// is valid to be store to the cache, if not(false) then the consumer should just exit\n// otherwise(true) the consumer should store the cached response\nfunc (v *validatorRule) Valid(ctx *context.Context) bool {\n\t// check if it's a valid response, if it's not then just return.\n\tfor _, valid := range v.postValidators {\n\t\tif !valid(ctx) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n"
  },
  {
    "path": "cache/client/ruleset.go",
    "content": "package client\n\nimport (\n\t\"github.com/kataras/iris/v12/cache/cfg\"\n\t\"github.com/kataras/iris/v12/cache/client/rule\"\n\t\"github.com/kataras/iris/v12/cache/ruleset\"\n\t\"github.com/kataras/iris/v12/context\"\n)\n\n// DefaultRuleSet is a list of the default pre-cache validators\n// which exists in ALL handlers, local and remote.\nvar DefaultRuleSet = rule.Chained(\n\t// #1 A shared cache MUST NOT use a cached response to a request with an\n\t// Authorization header field\n\trule.HeaderClaim(ruleset.AuthorizationRule),\n\t// #2 \"must-revalidate\" and/or\n\t// \"s-maxage\" response directives are not allowed to be served stale\n\t// (Section 4.2.4) by shared caches.  In particular, a response with\n\t// either \"max-age=0, must-revalidate\" or \"s-maxage=0\" cannot be used to\n\t// satisfy a subsequent request without revalidating it on the origin\n\t// server.\n\trule.HeaderClaim(ruleset.MustRevalidateRule),\n\trule.HeaderClaim(ruleset.ZeroMaxAgeRule),\n\t// #3 custom No-Cache header used inside this library\n\t// for BOTH request and response (after get-cache action)\n\trule.Header(ruleset.NoCacheRule, ruleset.NoCacheRule),\n)\n\n// NoCache disables the cache for a particular request,\n// can be used as a middleware or called manually from the handler.\nfunc NoCache(ctx *context.Context) {\n\tctx.Header(cfg.NoCacheHeader, \"true\")\n}\n"
  },
  {
    "path": "cache/entry/entry.go",
    "content": "package entry\n\nimport (\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/core/memstore\"\n)\n\n// Entry is the cache entry\n// contains the expiration datetime and the response\ntype Entry struct {\n\t// ExpiresAt is the time which this cache will not be available\n\tlifeTime *memstore.LifeTime\n\n\t// when `Reset` this value is reseting to time.Now(),\n\t// it's used to send the \"Last-Modified\" header,\n\t// some clients may need it.\n\tLastModified time.Time\n\n\t// Response the response should be served to the client\n\tresponse *Response\n\t// but we need the key to invalidate manually...xmm\n\t// let's see for that later, maybe we make a slice instead\n\t// of store map\n}\n\n// reset called each time a new entry is acquired from the pool.\nfunc (e *Entry) reset(lt *memstore.LifeTime, r *Response) {\n\te.response = r\n\te.LastModified = lt.Begun\n}\n\n// Response returns the cached response as it's.\nfunc (e *Entry) Response() *Response {\n\treturn e.response\n}\n\n// // Response gets the cache response contents\n// // if it's valid returns them with a true value\n// // otherwise returns nil, false\n// func (e *Entry) Response() (*Response, bool) {\n// \tif !e.isValid() {\n// \t\t// it has been expired\n// \t\treturn nil, false\n// \t}\n// \treturn e.response, true\n// }\n\n// // isValid reports whether this entry's response is still valid or expired.\n// // If the entry exists in the store then it should be valid anyways.\n// func (e *Entry) isValid() bool {\n// \treturn !e.lifeTime.HasExpired()\n// }\n"
  },
  {
    "path": "cache/entry/pool.go",
    "content": "package entry\n\nimport (\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/cache/cfg\"\n\t\"github.com/kataras/iris/v12/core/memstore\"\n)\n\n// Pool is the context pool, it's used inside router and the framework by itself.\ntype Pool struct {\n\tpool *sync.Pool\n}\n\n// NewPool creates and returns a new context pool.\nfunc NewPool() *Pool {\n\treturn &Pool{pool: &sync.Pool{New: func() any { return &Entry{} }}}\n}\n\n// func NewPool(newFunc func() any) *Pool {\n// \treturn &Pool{pool: &sync.Pool{New: newFunc}}\n// }\n\n// Acquire returns an Entry from pool.\n// See Release.\nfunc (c *Pool) Acquire(lifeDuration time.Duration, r *Response, onExpire func()) *Entry {\n\t// If the given duration is not <=0 (which means finds from the headers)\n\t// then we should check for the MinimumCacheDuration here\n\tif lifeDuration >= 0 && lifeDuration < cfg.MinimumCacheDuration {\n\t\tlifeDuration = cfg.MinimumCacheDuration\n\t}\n\n\te := c.pool.Get().(*Entry)\n\n\tlt := memstore.NewLifeTime()\n\tlt.Begin(lifeDuration, func() {\n\t\tonExpire()\n\t\tc.release(e)\n\t})\n\n\te.reset(lt, r)\n\treturn e\n}\n\n// Release puts an Entry back to its pull, this function releases its resources.\n// See Acquire.\nfunc (c *Pool) release(e *Entry) {\n\te.response.body = nil\n\te.response.headers = nil\n\te.response.statusCode = 0\n\te.response = nil\n\n\t// do not call it, it contains a lock too, release is controlled only inside the Acquire itself when the entry is expired.\n\t// if e.lifeTime != nil {\n\t// \te.lifeTime.ExpireNow() // stop any opening timers if force released.\n\t// }\n\n\tc.pool.Put(e)\n}\n\n// Release can be called by custom stores to release an entry.\nfunc (c *Pool) Release(e *Entry) {\n\tif e.lifeTime != nil {\n\t\te.lifeTime.ExpireNow() // stop any opening timers if force released.\n\t}\n\n\tc.release(e)\n}\n"
  },
  {
    "path": "cache/entry/response.go",
    "content": "package entry\n\nimport (\n\t\"io\"\n\t\"net/http\"\n)\n\n// Response is the cached response will be send to the clients\n// its fields set at runtime on each of the non-cached executions\n// non-cached executions = first execution, and each time after\n// cache expiration datetime passed.\ntype Response struct {\n\t// statusCode for the response cache handler.\n\tstatusCode int\n\t// body is the contents will be served by the cache handler.\n\tbody []byte\n\t// the total headers of the response, including content type.\n\theaders http.Header\n}\n\n// NewResponse returns a new cached Response.\nfunc NewResponse(statusCode int, headers http.Header, body []byte) *Response {\n\tr := new(Response)\n\n\tr.SetStatusCode(statusCode)\n\tr.SetHeaders(headers)\n\tr.SetBody(body)\n\n\treturn r\n}\n\n// SetStatusCode sets a valid status code.\nfunc (r *Response) SetStatusCode(statusCode int) {\n\tif statusCode <= 0 {\n\t\tstatusCode = http.StatusOK\n\t}\n\n\tr.statusCode = statusCode\n}\n\n// StatusCode returns a valid status code.\nfunc (r *Response) StatusCode() int {\n\treturn r.statusCode\n}\n\n// ContentType returns a valid content type\n// func (r *Response) ContentType() string {\n// \tif r.headers == \"\" {\n// \t\tr.contentType = \"text/html; charset=utf-8\"\n// \t}\n// \treturn r.contentType\n// }\n\n// SetHeaders sets a clone of headers of the cached response.\nfunc (r *Response) SetHeaders(h http.Header) {\n\tr.headers = h.Clone()\n}\n\n// Headers returns the total headers of the cached response.\nfunc (r *Response) Headers() http.Header {\n\treturn r.headers\n}\n\n// SetBody consumes \"b\" and sets the body of the cached response.\nfunc (r *Response) SetBody(body []byte) {\n\tr.body = make([]byte, len(body))\n\tcopy(r.body, body)\n}\n\n// Body returns contents will be served by the cache handler.\nfunc (r *Response) Body() []byte {\n\treturn r.body\n}\n\n// Read implements the io.Reader interface.\nfunc (r *Response) Read(b []byte) (int, error) {\n\tif len(r.body) == 0 {\n\t\treturn 0, io.EOF\n\t}\n\n\tn := copy(b, r.body)\n\tr.body = r.body[n:]\n\treturn n, nil\n}\n\n// Bytes returns a copy of the cached response body.\nfunc (r *Response) Bytes() []byte {\n\treturn append([]byte(nil), r.body...)\n}\n"
  },
  {
    "path": "cache/entry/store.go",
    "content": "package entry\n\nimport (\n\t\"sync\"\n)\n\n// Store is the interface which is responsible to store the cache entries.\ntype Store interface {\n\t// Get returns an entry based on its key.\n\tGet(key string) *Entry\n\t// Set sets an entry based on its key.\n\tSet(key string, e *Entry)\n\t// Delete deletes an entry based on its key.\n\tDelete(key string)\n}\n\n// memStore is the default in-memory store for the cache entries.\ntype memStore struct {\n\tentries map[string]*Entry\n\tmu      sync.RWMutex\n}\n\nvar _ Store = (*memStore)(nil)\n\n// NewMemStore returns a new in-memory store for the cache entries.\nfunc NewMemStore() Store {\n\treturn &memStore{\n\t\tentries: make(map[string]*Entry),\n\t}\n}\n\n// Get returns an entry based on its key.\nfunc (s *memStore) Get(key string) *Entry {\n\ts.mu.RLock()\n\te := s.entries[key]\n\ts.mu.RUnlock()\n\treturn e\n}\n\n// Set sets an entry based on its key.\nfunc (s *memStore) Set(key string, e *Entry) {\n\ts.mu.Lock()\n\ts.entries[key] = e\n\ts.mu.Unlock()\n}\n\n// Delete deletes an entry based on its key.\nfunc (s *memStore) Delete(key string) {\n\ts.mu.Lock()\n\tdelete(s.entries, key)\n\ts.mu.Unlock()\n}\n"
  },
  {
    "path": "cache/ruleset/ruleset.go",
    "content": "// Package ruleset provides the basics rules which are being extended by rules.\npackage ruleset\n\n// The shared header-mostly rules for both nethttp and fasthttp\nvar (\n\tAuthorizationRule = func(header GetHeader) bool {\n\t\treturn header(\"Authorization\") == \"\" &&\n\t\t\theader(\"Proxy-Authenticate\") == \"\"\n\t}\n\n\tMustRevalidateRule = func(header GetHeader) bool {\n\t\treturn header(\"Must-Revalidate\") == \"\"\n\t}\n\n\tZeroMaxAgeRule = func(header GetHeader) bool {\n\t\treturn header(\"S-Maxage\") != \"0\" &&\n\t\t\theader(\"Max-Age\") != \"0\"\n\t}\n\n\tNoCacheRule = func(header GetHeader) bool {\n\t\treturn header(\"No-Cache\") != \"true\"\n\t}\n)\n\n// THESE ARE HERE BECAUSE THE GOLANG DOESN'T SUPPORTS THE F....  INTERFACE ALIAS, THIS SHOULD EXISTS ONLY ON /$package/rule\n// or somehow move interface generic rules (such as conditional, header) here, because the code sharing is exactly THE SAME\n// except the -end interface, this on other language can be designing very very nice but here no OOP so we stuck on this,\n// it's better than before but not as I wanted to be.\n// They will make me to forget my software design skills,\n// they (the language's designers) rollback the feature of type alias, BLOG POSTING that is an UNUSEFUL feature.....\n\n// GetHeader is a type of func which receives a func of string which returns a string\n// used to get headers values, both request's and response's.\ntype GetHeader func(string) string\n\n// HeaderPredicate is a type of func which receives a func of string which returns a string and a boolean,\n// used to get headers values, both request's and response's.\ntype HeaderPredicate func(GetHeader) bool\n\n// EmptyHeaderPredicate returns always true\nvar EmptyHeaderPredicate = func(GetHeader) bool {\n\treturn true\n}\n"
  },
  {
    "path": "cache/uri/uribuilder.go",
    "content": "package uri\n\nimport (\n\t\"net/url\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/cache/cfg\"\n)\n\n// URIBuilder is the requested url builder\n// which keeps all the information the server\n// should know to save or retrieve a cached entry's response\n// used on client-side only\n// both from net/http and valyala/fasthttp\ntype URIBuilder struct {\n\tserverAddr,\n\tclientMethod,\n\tclientURI string\n\n\tcacheLifetime    time.Duration\n\tcacheStatuscode  int\n\tcacheContentType string\n}\n\n// ServerAddr sets the server address for the final request url\nfunc (r *URIBuilder) ServerAddr(s string) *URIBuilder {\n\tr.serverAddr = s\n\treturn r\n}\n\n// ClientMethod sets the method which the client should call the remote server's handler\n// used to build the final request url too\nfunc (r *URIBuilder) ClientMethod(s string) *URIBuilder {\n\tr.clientMethod = s\n\treturn r\n}\n\n// ClientURI sets the client path for the final request url\nfunc (r *URIBuilder) ClientURI(s string) *URIBuilder {\n\tr.clientURI = s\n\treturn r\n}\n\n// Lifetime sets the cache lifetime for the final request url\nfunc (r *URIBuilder) Lifetime(d time.Duration) *URIBuilder {\n\tr.cacheLifetime = d\n\treturn r\n}\n\n// StatusCode sets the cache status code for the final request url\nfunc (r *URIBuilder) StatusCode(code int) *URIBuilder {\n\tr.cacheStatuscode = code\n\treturn r\n}\n\n// ContentType sets the cache content type for the final request url\nfunc (r *URIBuilder) ContentType(s string) *URIBuilder {\n\tr.cacheContentType = s\n\treturn r\n}\n\n// String returns the full url which should be passed to get a cache entry response back\n// (it could be set by server too but we need some client-freedom on the requested key)\n// in order to be sure that the registered cache entries are unique among different clients with the same key\n// note1: we do it manually*,\n// note2: on fasthttp that is not required because the query args added as expected but we will use it there too to be align with net/http\nfunc (r URIBuilder) String() string {\n\treturn r.build()\n}\n\nfunc (r URIBuilder) build() string {\n\tremoteURL := r.serverAddr\n\n\t// fasthttp appends the \"/\" in the last uri (with query args also, that's probably a fasthttp bug which I'll fix later)\n\t// for now lets make that check:\n\n\tif !strings.HasSuffix(remoteURL, \"/\") {\n\t\tremoteURL += \"/\"\n\t}\n\tscheme := \"http://\"\n\t// validate the remoteURL, should contains a scheme, if not then check if the client has given a scheme and if so\n\t// use that for the server too\n\tif !strings.Contains(remoteURL, \"://\") {\n\t\tif strings.Contains(remoteURL, \":443\") || strings.Contains(remoteURL, \":https\") {\n\t\t\tremoteURL = \"https://\" + remoteURL\n\t\t} else {\n\t\t\tremoteURL = scheme + \"://\" + remoteURL\n\t\t}\n\t}\n\tvar cacheDurationStr, statusCodeStr string\n\n\tif r.cacheLifetime.Seconds() > 0 {\n\t\tcacheDurationStr = strconv.Itoa(int(r.cacheLifetime.Seconds()))\n\t}\n\n\tif r.cacheStatuscode > 0 {\n\t\tstatusCodeStr = strconv.Itoa(r.cacheStatuscode)\n\t}\n\n\ts := remoteURL + \"?\" + cfg.QueryCacheKey + \"=\" + url.QueryEscape(r.clientMethod+scheme+r.clientURI)\n\tif cacheDurationStr != \"\" {\n\t\ts += \"&\" + cfg.QueryCacheDuration + \"=\" + url.QueryEscape(cacheDurationStr)\n\t}\n\tif statusCodeStr != \"\" {\n\t\ts += \"&\" + cfg.QueryCacheStatusCode + \"=\" + url.QueryEscape(statusCodeStr)\n\t}\n\tif r.cacheContentType != \"\" {\n\t\ts += \"&\" + cfg.QueryCacheContentType + \"=\" + url.QueryEscape(r.cacheContentType)\n\t}\n\treturn s\n}\n"
  },
  {
    "path": "cli.go",
    "content": "package iris\n\n//  +------------------------------------------------------------+\n//  | Bridge code between iris-cli and iris web application      |\n//  | https://github.com/kataras/iris-cli                        |\n//  +------------------------------------------------------------+\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\n\t\"gopkg.in/yaml.v3\"\n)\n\n// injectLiveReload tries to check if this application\n// runs under https://github.com/kataras/iris-cli and if so\n// then it checks if the livereload is enabled and then injects\n// the watch listener (js script) on every HTML response.\n// It has a slight performance cost but\n// this (iris-cli with watch and livereload enabled)\n// is meant to be used only in development mode.\n// It does a full reload at the moment and if the port changed\n// at runtime it will fire 404 instead of redirecting to the correct port (that's a TODO).\n//\n// tryInjectLiveReload runs right before Build -> BuildRouter.\nfunc injectLiveReload(r Party) (bool, error) {\n\tconf := struct {\n\t\tRunning    bool `yaml:\"Running,omitempty\"`\n\t\tLiveReload struct {\n\t\t\tDisable bool `yaml:\"Disable\"`\n\t\t\tPort    int  `yaml:\"Port\"`\n\t\t} `yaml:\"LiveReload\"`\n\t}{}\n\t// defaults to disabled here.\n\tconf.LiveReload.Disable = true\n\n\twd, err := os.Getwd()\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\tfor _, path := range []string{\".iris.yml\" /*, \"../.iris.yml\", \"../../.iris.yml\" */} {\n\t\tpath = filepath.Join(wd, path)\n\n\t\tif _, err := os.Stat(path); err == nil {\n\t\t\tinFile, err := os.OpenFile(path, os.O_RDONLY, 0600)\n\t\t\tif err != nil {\n\t\t\t\treturn false, err\n\t\t\t}\n\n\t\t\tdec := yaml.NewDecoder(inFile)\n\t\t\terr = dec.Decode(&conf)\n\t\t\tinFile.Close()\n\t\t\tif err != nil {\n\t\t\t\treturn false, err\n\t\t\t}\n\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif !conf.Running || conf.LiveReload.Disable {\n\t\treturn false, nil\n\t}\n\n\tscriptReloadJS := []byte(fmt.Sprintf(`<script>(function () {\n    const scheme = document.location.protocol == \"https:\" ? \"wss\" : \"ws\";\n    const endpoint = scheme + \"://\" + document.location.hostname + \":%d/livereload\";\n\n    w = new WebSocket(endpoint);\n    w.onopen = function () {\n        console.info(\"LiveReload: initialization\");\n    };\n    w.onclose = function () {\n        console.info(\"LiveReload: terminated\");\n    };\n    w.onmessage = function (message) {\n        // NOTE: full-reload, at least for the moment. Also if backend changed its port then we will get 404 here. \n        window.location.reload();\n    };\n}());</script>`, conf.LiveReload.Port))\n\n\tbodyCloseTag := []byte(\"</body>\")\n\n\tr.UseRouter(func(ctx Context) {\n\t\trec := ctx.Recorder() // Record everything and write all in once at the Context release.\n\t\tctx.Next()            // call the next, so this is a 'done' handler.\n\t\tif strings.HasPrefix(ctx.GetContentType(), \"text/html\") {\n\t\t\t// delete(rec.Header(), context.ContentLengthHeaderKey)\n\n\t\t\tbody := rec.Body()\n\n\t\t\tif idx := bytes.LastIndex(body, bodyCloseTag); idx > 0 {\n\t\t\t\t// add the script right before last </body>.\n\t\t\t\tbody = append(body[:idx], bytes.Replace(body[idx:], bodyCloseTag, append(scriptReloadJS, bodyCloseTag...), 1)...)\n\t\t\t\trec.SetBody(body)\n\t\t\t} else {\n\t\t\t\t// Just append it.\n\t\t\t\trec.Write(scriptReloadJS) // nolint:errcheck\n\t\t\t}\n\n\t\t\tif _, has := rec.Header()[context.ContentLengthHeaderKey]; has {\n\t\t\t\trec.Header().Set(context.ContentLengthHeaderKey, fmt.Sprintf(\"%d\", len(rec.Body())))\n\t\t\t}\n\t\t}\n\t})\n\treturn true, nil\n}\n"
  },
  {
    "path": "configuration.go",
    "content": "package iris\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"os/user\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/core/netutil\"\n\n\t\"github.com/BurntSushi/toml\"\n\t\"github.com/kataras/golog\"\n\t\"github.com/kataras/sitemap\"\n\t\"github.com/kataras/tunnel\"\n\t\"gopkg.in/yaml.v3\"\n)\n\nconst globalConfigurationKeyword = \"~\"\n\n// homeConfigurationFilename returns the physical location of the global configuration(yaml or toml) file.\n// This is useful when we run multiple iris servers that share the same\n// configuration, even with custom values at its \"Other\" field.\n// It will return a file location\n// which targets to $HOME or %HOMEDRIVE%+%HOMEPATH% + \"iris\" + the given \"ext\".\nfunc homeConfigurationFilename(ext string) string {\n\treturn filepath.Join(homeDir(), \"iris\"+ext)\n}\n\nfunc homeDir() (home string) {\n\tu, err := user.Current()\n\tif u != nil && err == nil {\n\t\thome = u.HomeDir\n\t}\n\n\tif home == \"\" {\n\t\thome = os.Getenv(\"HOME\")\n\t}\n\n\tif home == \"\" {\n\t\tswitch runtime.GOOS {\n\t\tcase \"plan9\":\n\t\t\thome = os.Getenv(\"home\")\n\t\tcase \"windows\":\n\t\t\thome = os.Getenv(\"HOMEDRIVE\") + os.Getenv(\"HOMEPATH\")\n\t\t\tif home == \"\" {\n\t\t\t\thome = os.Getenv(\"USERPROFILE\")\n\t\t\t}\n\t\t}\n\t}\n\n\treturn\n}\n\nfunc parseYAML(filename string) (Configuration, error) {\n\tc := DefaultConfiguration()\n\t// get the abs\n\t// which will try to find the 'filename' from current workind dir too.\n\tyamlAbsPath, err := filepath.Abs(filename)\n\tif err != nil {\n\t\treturn c, fmt.Errorf(\"parse yaml: %w\", err)\n\t}\n\n\t// read the raw contents of the file\n\tdata, err := os.ReadFile(yamlAbsPath)\n\tif err != nil {\n\t\treturn c, fmt.Errorf(\"parse yaml: %w\", err)\n\t}\n\n\t// put the file's contents as yaml to the default configuration(c)\n\tif err := yaml.Unmarshal(data, &c); err != nil {\n\t\treturn c, fmt.Errorf(\"parse yaml: %w\", err)\n\t}\n\treturn c, nil\n}\n\n// YAML reads Configuration from a configuration.yml file.\n//\n// Accepts the absolute path of the cfg.yml.\n// An error will be shown to the user via panic with the error message.\n// Error may occur when the cfg.yml does not exist or is not formatted correctly.\n//\n// Note: if the char '~' passed as \"filename\" then it tries to load and return\n// the configuration from the $home_directory + iris.yml,\n// see `WithGlobalConfiguration` for more information.\n//\n// Usage:\n// app.Configure(iris.WithConfiguration(iris.YAML(\"myconfig.yml\"))) or\n// app.Run([iris.Runner], iris.WithConfiguration(iris.YAML(\"myconfig.yml\"))).\nfunc YAML(filename string) Configuration {\n\t// check for globe configuration file and use that, otherwise\n\t// return the default configuration if file doesn't exist.\n\tif filename == globalConfigurationKeyword {\n\t\tfilename = homeConfigurationFilename(\".yml\")\n\t\tif _, err := os.Stat(filename); os.IsNotExist(err) {\n\t\t\tpanic(\"default configuration file '\" + filename + \"' does not exist\")\n\t\t}\n\t}\n\n\tc, err := parseYAML(filename)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treturn c\n}\n\n// TOML reads Configuration from a toml-compatible document file.\n// Read more about toml's implementation at:\n// https://github.com/toml-lang/toml\n//\n// Accepts the absolute path of the configuration file.\n// An error will be shown to the user via panic with the error message.\n// Error may occur when the file does not exist or is not formatted correctly.\n//\n// Note: if the char '~' passed as \"filename\" then it tries to load and return\n// the configuration from the $home_directory + iris.tml,\n// see `WithGlobalConfiguration` for more information.\n//\n// Usage:\n// app.Configure(iris.WithConfiguration(iris.TOML(\"myconfig.tml\"))) or\n// app.Run([iris.Runner], iris.WithConfiguration(iris.TOML(\"myconfig.tml\"))).\nfunc TOML(filename string) Configuration {\n\tc := DefaultConfiguration()\n\n\t// check for globe configuration file and use that, otherwise\n\t// return the default configuration if file doesn't exist.\n\tif filename == globalConfigurationKeyword {\n\t\tfilename = homeConfigurationFilename(\".tml\")\n\t\tif _, err := os.Stat(filename); os.IsNotExist(err) {\n\t\t\tpanic(\"default configuration file '\" + filename + \"' does not exist\")\n\t\t}\n\t}\n\n\t// get the abs\n\t// which will try to find the 'filename' from current workind dir too.\n\ttomlAbsPath, err := filepath.Abs(filename)\n\tif err != nil {\n\t\tpanic(fmt.Errorf(\"toml: %w\", err))\n\t}\n\n\t// read the raw contents of the file\n\tdata, err := os.ReadFile(tomlAbsPath)\n\tif err != nil {\n\t\tpanic(fmt.Errorf(\"toml :%w\", err))\n\t}\n\n\t// put the file's contents as toml to the default configuration(c)\n\tif _, err := toml.Decode(string(data), &c); err != nil {\n\t\tpanic(fmt.Errorf(\"toml :%w\", err))\n\t}\n\t// Author's notes:\n\t// The toml's 'usual thing' for key naming is: the_config_key instead of TheConfigKey\n\t// but I am always prefer to use the specific programming language's syntax\n\t// and the original configuration name fields for external configuration files\n\t// so we do 'toml: \"TheConfigKeySameAsTheConfigField\" instead.\n\treturn c\n}\n\n// Configurator is just an interface which accepts the framework instance.\n//\n// It can be used to register a custom configuration with `Configure` in order\n// to modify the framework instance.\n//\n// Currently Configurator is being used to describe the configuration's fields values.\ntype Configurator func(*Application)\n\n// WithGlobalConfiguration will load the global yaml configuration file\n// from the home directory and it will set/override the whole app's configuration\n// to that file's contents. The global configuration file can be modified by user\n// and be used by multiple iris instances.\n//\n// This is useful when we run multiple iris servers that share the same\n// configuration, even with custom values at its \"Other\" field.\n//\n// Usage: `app.Configure(iris.WithGlobalConfiguration)` or `app.Run([iris.Runner], iris.WithGlobalConfiguration)`.\nvar WithGlobalConfiguration = func(app *Application) {\n\tapp.Configure(WithConfiguration(YAML(globalConfigurationKeyword)))\n}\n\n// WithLogLevel sets the `Configuration.LogLevel` field.\nfunc WithLogLevel(level string) Configurator {\n\treturn func(app *Application) {\n\t\tif app.logger == nil {\n\t\t\tapp.logger = golog.Default\n\t\t}\n\t\tapp.logger.SetLevel(level) // can be fired through app.Configure.\n\n\t\tapp.config.LogLevel = level\n\t}\n}\n\n// WithSocketSharding sets the `Configuration.SocketSharding` field to true.\nfunc WithSocketSharding(app *Application) {\n\t// Note(@kataras): It could be a host Configurator but it's an application setting in order\n\t// to configure it through yaml/toml files as well.\n\tapp.config.SocketSharding = true\n}\n\n// WithKeepAlive sets the `Configuration.KeepAlive` field to the given duration.\nfunc WithKeepAlive(keepAliveDur time.Duration) Configurator {\n\treturn func(app *Application) {\n\t\tapp.config.KeepAlive = keepAliveDur\n\t}\n}\n\n// WithTimeout sets the `Configuration.Timeout` field to the given duration.\nfunc WithTimeout(timeoutDur time.Duration, htmlBody ...string) Configurator {\n\treturn func(app *Application) {\n\t\tapp.config.Timeout = timeoutDur\n\t\tif len(htmlBody) > 0 {\n\t\t\tapp.config.TimeoutMessage = htmlBody[0]\n\t\t}\n\t}\n}\n\n// NonBlocking sets the `Configuration.NonBlocking` field to true.\nfunc NonBlocking() Configurator {\n\treturn func(app *Application) {\n\t\tapp.config.NonBlocking = true\n\t}\n}\n\n// WithoutServerError will cause to ignore the matched \"errors\"\n// from the main application's `Run/Listen` function.\n//\n// Usage:\n// err := app.Listen(\":8080\", iris.WithoutServerError(iris.ErrServerClosed))\n// will return `nil` if the server's error was `http/iris#ErrServerClosed`.\n//\n// See `Configuration#IgnoreServerErrors []string` too.\n//\n// Example: https://github.com/kataras/iris/tree/main/_examples/http-server/listen-addr/omit-server-errors\nfunc WithoutServerError(errors ...error) Configurator {\n\treturn func(app *Application) {\n\t\tif len(errors) == 0 {\n\t\t\treturn\n\t\t}\n\n\t\terrorsAsString := make([]string, len(errors))\n\t\tfor i, e := range errors {\n\t\t\terrorsAsString[i] = e.Error()\n\t\t}\n\n\t\tapp.config.IgnoreServerErrors = append(app.config.IgnoreServerErrors, errorsAsString...)\n\t}\n}\n\n// WithoutStartupLog turns off the information send, once, to the terminal when the main server is open.\nvar WithoutStartupLog = func(app *Application) {\n\tapp.config.DisableStartupLog = true\n}\n\n// WithoutBanner is a conversion for the `WithoutStartupLog` option.\n//\n// Turns off the information send, once, to the terminal when the main server is open.\nvar WithoutBanner = WithoutStartupLog\n\n// WithoutInterruptHandler disables the automatic graceful server shutdown\n// when control/cmd+C pressed.\nvar WithoutInterruptHandler = func(app *Application) {\n\tapp.config.DisableInterruptHandler = true\n}\n\n// WithoutPathCorrection disables the PathCorrection setting.\n//\n// See `Configuration`.\nvar WithoutPathCorrection = func(app *Application) {\n\tapp.config.DisablePathCorrection = true\n}\n\n// WithPathIntelligence enables the EnablePathIntelligence setting.\n//\n// See `Configuration`.\nvar WithPathIntelligence = func(app *Application) {\n\tapp.config.EnablePathIntelligence = true\n}\n\n// WithoutPathCorrectionRedirection disables the PathCorrectionRedirection setting.\n//\n// See `Configuration`.\nvar WithoutPathCorrectionRedirection = func(app *Application) {\n\tapp.config.DisablePathCorrection = false\n\tapp.config.DisablePathCorrectionRedirection = true\n}\n\n// WithoutBodyConsumptionOnUnmarshal disables BodyConsumptionOnUnmarshal setting.\n//\n// See `Configuration`.\nvar WithoutBodyConsumptionOnUnmarshal = func(app *Application) {\n\tapp.config.DisableBodyConsumptionOnUnmarshal = true\n}\n\n// WithEmptyFormError enables the setting `FireEmptyFormError`.\n//\n// See `Configuration`.\nvar WithEmptyFormError = func(app *Application) {\n\tapp.config.FireEmptyFormError = true\n}\n\n// WithPathEscape sets the EnablePathEscape setting to true.\n//\n// See `Configuration`.\nvar WithPathEscape = func(app *Application) {\n\tapp.config.EnablePathEscape = true\n}\n\n// WithLowercaseRouting enables for lowercase routing by\n// setting the `ForceLowercaseRoutes` to true.\n//\n// See `Configuration`.\nvar WithLowercaseRouting = func(app *Application) {\n\tapp.config.ForceLowercaseRouting = true\n}\n\n// WithDynamicHandler enables for dynamic routing by\n// setting the `EnableDynamicHandler` to true.\n//\n// See `Configuration`.\nvar WithDynamicHandler = func(app *Application) {\n\tapp.config.EnableDynamicHandler = true\n}\n\n// WithOptimizations can force the application to optimize for the best performance where is possible.\n//\n// See `Configuration`.\nvar WithOptimizations = func(app *Application) {\n\tapp.config.EnableOptimizations = true\n}\n\n// WithProtoJSON enables the proto marshaler on Context.JSON method.\n//\n// See `Configuration` for more.\nvar WithProtoJSON = func(app *Application) {\n\tapp.config.EnableProtoJSON = true\n}\n\n// WithEasyJSON enables the fast easy json marshaler on Context.JSON method.\n//\n// See `Configuration` for more.\nvar WithEasyJSON = func(app *Application) {\n\tapp.config.EnableEasyJSON = true\n}\n\n// WithFireMethodNotAllowed enables the FireMethodNotAllowed setting.\n//\n// See `Configuration`.\nvar WithFireMethodNotAllowed = func(app *Application) {\n\tapp.config.FireMethodNotAllowed = true\n}\n\n// WithoutAutoFireStatusCode sets the DisableAutoFireStatusCode setting to true.\n//\n// See `Configuration`.\nvar WithoutAutoFireStatusCode = func(app *Application) {\n\tapp.config.DisableAutoFireStatusCode = true\n}\n\n// WithResetOnFireErrorCode sets the ResetOnFireErrorCode setting to true.\n//\n// See `Configuration`.\nvar WithResetOnFireErrorCode = func(app *Application) {\n\tapp.config.ResetOnFireErrorCode = true\n}\n\n// WithURLParamSeparator sets the URLParamSeparator setting to \"sep\".\n//\n// See `Configuration`.\nvar WithURLParamSeparator = func(sep string) Configurator {\n\treturn func(app *Application) {\n\t\tapp.config.URLParamSeparator = &sep\n\t}\n}\n\n// WithTimeFormat sets the TimeFormat setting.\n//\n// See `Configuration`.\nfunc WithTimeFormat(timeformat string) Configurator {\n\treturn func(app *Application) {\n\t\tapp.config.TimeFormat = timeformat\n\t}\n}\n\n// WithCharset sets the Charset setting.\n//\n// See `Configuration`.\nfunc WithCharset(charset string) Configurator {\n\treturn func(app *Application) {\n\t\tapp.config.Charset = charset\n\t}\n}\n\n// WithPostMaxMemory sets the maximum post data size\n// that a client can send to the server, this differs\n// from the overall request body size which can be modified\n// by the `context#SetMaxRequestBodySize` or `iris#LimitRequestBodySize`.\n//\n// Defaults to 32MB or 32 << 20 or 32*iris.MB if you prefer.\nfunc WithPostMaxMemory(limit int64) Configurator {\n\treturn func(app *Application) {\n\t\tapp.config.PostMaxMemory = limit\n\t}\n}\n\n// WithRemoteAddrHeader adds a new request header name\n// that can be used to validate the client's real IP.\nfunc WithRemoteAddrHeader(header ...string) Configurator {\n\treturn func(app *Application) {\n\t\tfor _, h := range header {\n\t\t\texists := false\n\t\t\tfor _, v := range app.config.RemoteAddrHeaders {\n\t\t\t\tif v == h {\n\t\t\t\t\texists = true\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif !exists {\n\t\t\t\tapp.config.RemoteAddrHeaders = append(app.config.RemoteAddrHeaders, h)\n\t\t\t}\n\t\t}\n\t}\n}\n\n// WithoutRemoteAddrHeader removes an existing request header name\n// that can be used to validate and parse the client's real IP.\n//\n// Look `context.RemoteAddr()` for more.\nfunc WithoutRemoteAddrHeader(headerName string) Configurator {\n\treturn func(app *Application) {\n\t\ttmp := app.config.RemoteAddrHeaders[:0]\n\t\tfor _, v := range app.config.RemoteAddrHeaders {\n\t\t\tif v != headerName {\n\t\t\t\ttmp = append(tmp, v)\n\t\t\t}\n\t\t}\n\t\tapp.config.RemoteAddrHeaders = tmp\n\t}\n}\n\n// WithRemoteAddrPrivateSubnet adds a new private sub-net to be excluded from `context.RemoteAddr`.\n// See `WithRemoteAddrHeader` too.\nfunc WithRemoteAddrPrivateSubnet(startIP, endIP string) Configurator {\n\treturn func(app *Application) {\n\t\tapp.config.RemoteAddrPrivateSubnets = append(app.config.RemoteAddrPrivateSubnets, netutil.IPRange{\n\t\t\tStart: startIP,\n\t\t\tEnd:   endIP,\n\t\t})\n\t}\n}\n\n// WithSSLProxyHeader sets a SSLProxyHeaders key value pair.\n// Example: WithSSLProxyHeader(\"X-Forwarded-Proto\", \"https\").\n// See `Context.IsSSL` for more.\nfunc WithSSLProxyHeader(headerKey, headerValue string) Configurator {\n\treturn func(app *Application) {\n\t\tif app.config.SSLProxyHeaders == nil {\n\t\t\tapp.config.SSLProxyHeaders = make(map[string]string)\n\t\t}\n\n\t\tapp.config.SSLProxyHeaders[headerKey] = headerValue\n\t}\n}\n\n// WithHostProxyHeader sets a HostProxyHeaders key value pair.\n// Example: WithHostProxyHeader(\"X-Host\").\n// See `Context.Host` for more.\nfunc WithHostProxyHeader(headers ...string) Configurator {\n\treturn func(app *Application) {\n\t\tif app.config.HostProxyHeaders == nil {\n\t\t\tapp.config.HostProxyHeaders = make(map[string]bool)\n\t\t}\n\t\tfor _, k := range headers {\n\t\t\tapp.config.HostProxyHeaders[k] = true\n\t\t}\n\t}\n}\n\n// WithOtherValue adds a value based on a key to the Other setting.\n//\n// See `Configuration.Other`.\nfunc WithOtherValue(key string, val any) Configurator {\n\treturn func(app *Application) {\n\t\tif app.config.Other == nil {\n\t\t\tapp.config.Other = make(map[string]any)\n\t\t}\n\t\tapp.config.Other[key] = val\n\t}\n}\n\n// WithSitemap enables the sitemap generator.\n// Use the Route's `SetLastMod`, `SetChangeFreq` and `SetPriority` to modify\n// the sitemap's URL child element properties.\n// Excluded routes:\n// - dynamic\n// - subdomain\n// - offline\n// - ExcludeSitemap method called\n//\n// It accepts a \"startURL\" input argument which\n// is the prefix for the registered routes that will be included in the sitemap.\n//\n// If more than 50,000 static routes are registered then sitemaps will be splitted and a sitemap index will be served in\n// /sitemap.xml.\n//\n// If `Application.I18n.Load/LoadAssets` is called then the sitemap will contain translated links for each static route.\n//\n// If the result does not complete your needs you can take control\n// and use the github.com/kataras/sitemap package to generate a customized one instead.\n//\n// Example: https://github.com/kataras/iris/tree/main/_examples/sitemap.\nfunc WithSitemap(startURL string) Configurator {\n\tsitemaps := sitemap.New(startURL)\n\treturn func(app *Application) {\n\t\tvar defaultLang string\n\t\tif tags := app.I18n.Tags(); len(tags) > 0 {\n\t\t\tdefaultLang = tags[0].String()\n\t\t\tsitemaps.DefaultLang(defaultLang)\n\t\t}\n\n\t\tfor _, r := range app.GetRoutes() {\n\t\t\tif !r.IsStatic() || r.Subdomain != \"\" || !r.IsOnline() || r.NoSitemap {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tloc := r.StaticPath()\n\t\t\tvar translatedLinks []sitemap.Link\n\n\t\t\tfor _, tag := range app.I18n.Tags() {\n\t\t\t\tlang := tag.String()\n\t\t\t\tlangPath := lang\n\t\t\t\thref := \"\"\n\t\t\t\tif lang == defaultLang {\n\t\t\t\t\t// http://domain.com/en-US/path to just http://domain.com/path if en-US is the default language.\n\t\t\t\t\tlangPath = \"\"\n\t\t\t\t}\n\n\t\t\t\tif app.I18n.PathRedirect {\n\t\t\t\t\t// then use the path prefix.\n\t\t\t\t\t// e.g. http://domain.com/el-GR/path\n\t\t\t\t\tif langPath == \"\" { // fix double slashes http://domain.com// when self-included default language.\n\t\t\t\t\t\thref = loc\n\t\t\t\t\t} else {\n\t\t\t\t\t\thref = \"/\" + langPath + loc\n\t\t\t\t\t}\n\t\t\t\t} else if app.I18n.Subdomain {\n\t\t\t\t\t// then use the subdomain.\n\t\t\t\t\t// e.g. http://el.domain.com/path\n\t\t\t\t\tscheme := netutil.ResolveSchemeFromVHost(startURL)\n\t\t\t\t\thost := strings.TrimLeft(startURL, scheme)\n\t\t\t\t\tif langPath != \"\" {\n\t\t\t\t\t\thref = scheme + strings.Split(langPath, \"-\")[0] + \".\" + host + loc\n\t\t\t\t\t} else {\n\t\t\t\t\t\thref = loc\n\t\t\t\t\t}\n\n\t\t\t\t} else if p := app.I18n.URLParameter; p != \"\" {\n\t\t\t\t\t// then use the URL parameter.\n\t\t\t\t\t// e.g. http://domain.com/path?lang=el-GR\n\t\t\t\t\thref = loc + \"?\" + p + \"=\" + lang\n\t\t\t\t} else {\n\t\t\t\t\t// then skip it, we can't generate the link at this state.\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\ttranslatedLinks = append(translatedLinks, sitemap.Link{\n\t\t\t\t\tRel:      \"alternate\",\n\t\t\t\t\tHreflang: lang,\n\t\t\t\t\tHref:     href,\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tsitemaps.URL(sitemap.URL{\n\t\t\t\tLoc:        loc,\n\t\t\t\tLastMod:    r.LastMod,\n\t\t\t\tChangeFreq: r.ChangeFreq,\n\t\t\t\tPriority:   r.Priority,\n\t\t\t\tLinks:      translatedLinks,\n\t\t\t})\n\t\t}\n\n\t\tfor _, s := range sitemaps.Build() {\n\t\t\tcontentCopy := make([]byte, len(s.Content))\n\t\t\tcopy(contentCopy, s.Content)\n\n\t\t\thandler := func(ctx Context) {\n\t\t\t\tctx.ContentType(context.ContentXMLHeaderValue)\n\t\t\t\tctx.Write(contentCopy) // nolint:errcheck\n\t\t\t}\n\t\t\tif app.builded {\n\t\t\t\troutes := app.CreateRoutes([]string{MethodGet, MethodHead, MethodOptions}, s.Path, handler)\n\n\t\t\t\tfor _, r := range routes {\n\t\t\t\t\tif err := app.Router.AddRouteUnsafe(r); err != nil {\n\t\t\t\t\t\tapp.Logger().Errorf(\"sitemap route: %v\", err)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tapp.HandleMany(\"GET HEAD OPTIONS\", s.Path, handler)\n\t\t\t}\n\n\t\t}\n\t}\n}\n\n// WithTunneling is the `iris.Configurator` for the `iris.Configuration.Tunneling` field.\n// It's used to enable http tunneling for an Iris Application, per registered host\n//\n// Alternatively use the `iris.WithConfiguration(iris.Configuration{Tunneling: iris.TunnelingConfiguration{ ...}}}`.\nvar WithTunneling = func(app *Application) {\n\tconf := TunnelingConfiguration{\n\t\tTunnels: []Tunnel{{}}, // create empty tunnel, its addr and name are set right before host serve.\n\t}\n\n\tapp.config.Tunneling = conf\n}\n\ntype (\n\t// TunnelingConfiguration contains configuration\n\t// for the optional tunneling through ngrok feature.\n\t// Note that the ngrok should be already installed at the host machine.\n\tTunnelingConfiguration = tunnel.Configuration\n\t// Tunnel is the Tunnels field of the TunnelingConfiguration structure.\n\tTunnel = tunnel.Tunnel\n)\n\n// Configuration holds the necessary settings for an Iris Application instance.\n// All fields are optionally, the default values will work for a common web application.\n//\n// A Configuration value can be passed through `WithConfiguration` Configurator.\n// Usage:\n// conf := iris.Configuration{ ... }\n// app := iris.New()\n// app.Configure(iris.WithConfiguration(conf)) OR\n// app.Run/Listen(..., iris.WithConfiguration(conf)).\ntype Configuration struct {\n\t// VHost lets you customize the trusted domain this server should run on.\n\t// Its value will be used as the return value of Context.Domain() too.\n\t// It can be retrieved by the context if needed (i.e router for subdomains)\n\tVHost string `ini:\"v_host\" json:\"vHost\" yaml:\"VHost\" toml:\"VHost\" env:\"V_HOST\"`\n\n\t// LogLevel is the log level the application should use to output messages.\n\t// Logger, by default, is mostly used on Build state but it is also possible\n\t// that debug error messages could be thrown when the app is running, e.g.\n\t// when malformed data structures try to be sent on Client (i.e Context.JSON/JSONP/XML...).\n\t//\n\t// Defaults to \"info\". Possible values are:\n\t// * \"disable\"\n\t// * \"fatal\"\n\t// * \"error\"\n\t// * \"warn\"\n\t// * \"info\"\n\t// * \"debug\"\n\tLogLevel string `ini:\"log_level\" json:\"logLevel\" yaml:\"LogLevel\" toml:\"LogLevel\" env:\"LOG_LEVEL\"`\n\n\t// SocketSharding enables SO_REUSEPORT (or SO_REUSEADDR for windows)\n\t// on all registered Hosts.\n\t// This option allows linear scaling server performance on multi-CPU servers.\n\t//\n\t// Please read the following:\n\t// 1. https://stackoverflow.com/a/14388707\n\t// 2. https://stackoverflow.com/a/59692868\n\t// 3. https://www.nginx.com/blog/socket-sharding-nginx-release-1-9-1/\n\t// 4. (BOOK) Learning HTTP/2: A Practical Guide for Beginners:\n\t//\t  Page 37, To Shard or Not to Shard?\n\t//\n\t// Defaults to false.\n\tSocketSharding bool `ini:\"socket_sharding\" json:\"socketSharding\" yaml:\"SocketSharding\" toml:\"SocketSharding\" env:\"SOCKET_SHARDING\"`\n\t// KeepAlive sets the TCP connection's keep-alive duration.\n\t// If set to greater than zero then a tcp listener featured keep alive\n\t// will be used instead of the simple tcp one.\n\t//\n\t// Defaults to 0.\n\tKeepAlive time.Duration `ini:\"keepalive\" json:\"keepAlive\" yaml:\"KeepAlive\" toml:\"KeepAlive\" env:\"KEEP_ALIVE\"`\n\t// Timeout wraps the application's router with an http timeout handler\n\t// if the value is greater than zero.\n\t//\n\t// The underline response writer supports the Pusher interface but does not support\n\t// the Hijacker or Flusher interfaces when Timeout handler is registered.\n\t//\n\t// Read more at: https://pkg.go.dev/net/http#TimeoutHandler.\n\tTimeout time.Duration `ini:\"timeout\" json:\"timeout\" yaml:\"Timeout\" toml:\"Timeout\"`\n\t// TimeoutMessage specifies the HTML body when a handler hits its life time based\n\t// on the Timeout configuration field.\n\tTimeoutMessage string `ini:\"timeout_message\" json:\"timeoutMessage\" yaml:\"TimeoutMessage\" toml:\"TimeoutMessage\"`\n\t// NonBlocking, if set to true then the server will start listening for incoming connections\n\t// without blocking the main goroutine. Use the Application.Wait method to block and wait for the server to be up and running.\n\tNonBlocking bool `ini:\"non_blocking\" json:\"nonBlocking\" yaml:\"NonBlocking\" toml:\"NonBlocking\"`\n\n\t// Tunneling can be optionally set to enable ngrok http(s) tunneling for this Iris app instance.\n\t// See the `WithTunneling` Configurator too.\n\tTunneling TunnelingConfiguration `ini:\"tunneling\" json:\"tunneling,omitempty\" yaml:\"Tunneling\" toml:\"Tunneling\"`\n\t// IgnoreServerErrors will cause to ignore the matched \"errors\"\n\t// from the main application's `Run` function.\n\t// This is a slice of string, not a slice of error\n\t// users can register these errors using yaml or toml configuration file\n\t// like the rest of the configuration fields.\n\t//\n\t// See `WithoutServerError(...)` function too.\n\t//\n\t// Example: https://github.com/kataras/iris/tree/main/_examples/http-server/listen-addr/omit-server-errors\n\t//\n\t// Defaults to an empty slice.\n\tIgnoreServerErrors []string `ini:\"ignore_server_errors\" json:\"ignoreServerErrors,omitempty\" yaml:\"IgnoreServerErrors\" toml:\"IgnoreServerErrors\"`\n\n\t// DisableStartupLog if set to true then it turns off the write banner on server startup.\n\t//\n\t// Defaults to false.\n\tDisableStartupLog bool `ini:\"disable_startup_log\" json:\"disableStartupLog,omitempty\" yaml:\"DisableStartupLog\" toml:\"DisableStartupLog\"`\n\t// DisableInterruptHandler if set to true then it disables the automatic graceful server shutdown\n\t// when control/cmd+C pressed.\n\t// Turn this to true if you're planning to handle this by your own via a custom host.Task.\n\t//\n\t// Defaults to false.\n\tDisableInterruptHandler bool `ini:\"disable_interrupt_handler\" json:\"disableInterruptHandler,omitempty\" yaml:\"DisableInterruptHandler\" toml:\"DisableInterruptHandler\"`\n\n\t// DisablePathCorrection disables the correcting\n\t// and redirecting or executing directly the handler of\n\t// the requested path to the registered path\n\t// for example, if /home/ path is requested but no handler for this Route found,\n\t// then the Router checks if /home handler exists, if yes,\n\t// (permanent)redirects the client to the correct path /home.\n\t//\n\t// See `DisablePathCorrectionRedirection` to enable direct handler execution instead of redirection.\n\t//\n\t// Defaults to false.\n\tDisablePathCorrection bool `ini:\"disable_path_correction\" json:\"disablePathCorrection,omitempty\" yaml:\"DisablePathCorrection\" toml:\"DisablePathCorrection\"`\n\t// DisablePathCorrectionRedirection works whenever configuration.DisablePathCorrection is set to false\n\t// and if DisablePathCorrectionRedirection set to true then it will fire the handler of the matching route without\n\t// the trailing slash (\"/\") instead of send a redirection status.\n\t//\n\t// Defaults to false.\n\tDisablePathCorrectionRedirection bool `ini:\"disable_path_correction_redirection\" json:\"disablePathCorrectionRedirection,omitempty\" yaml:\"DisablePathCorrectionRedirection\" toml:\"DisablePathCorrectionRedirection\"`\n\t// EnablePathIntelligence if set to true,\n\t// the router will redirect HTTP \"GET\" not found pages to the most closest one path(if any). For example\n\t// you register a route at \"/contact\" path -\n\t// a client tries to reach it by \"/cont\", the path will be automatic fixed\n\t// and the client will be redirected to the \"/contact\" path\n\t// instead of getting a 404 not found response back.\n\t//\n\t// Defaults to false.\n\tEnablePathIntelligence bool `ini:\"enable_path_intelligence\" json:\"enablePathIntelligence,omitempty\" yaml:\"EnablePathIntelligence\" toml:\"EnablePathIntelligence\"`\n\t// EnablePathEscape when is true then its escapes the path and the named parameters (if any).\n\t// When do you need to Disable(false) it:\n\t// accepts parameters with slash '/'\n\t// Request: http://localhost:8080/details/Project%2FDelta\n\t// ctx.Param(\"project\") returns the raw named parameter: Project%2FDelta\n\t// which you can escape it manually with net/url:\n\t// projectName, _ := url.QueryUnescape(c.Param(\"project\").\n\t//\n\t// Defaults to false.\n\tEnablePathEscape bool `ini:\"enable_path_escape\" json:\"enablePathEscape,omitempty\" yaml:\"EnablePathEscape\" toml:\"EnablePathEscape\"`\n\t// ForceLowercaseRouting if enabled, converts all registered routes paths to lowercase\n\t// and it does lowercase the request path too for matching.\n\t//\n\t// Defaults to false.\n\tForceLowercaseRouting bool `ini:\"force_lowercase_routing\" json:\"forceLowercaseRouting,omitempty\" yaml:\"ForceLowercaseRouting\" toml:\"ForceLowercaseRouting\"`\n\t// EnableOptimizations enables dynamic request handler.\n\t// It gives the router the feature to add routes while in serve-time,\n\t// when `RefreshRouter` is called.\n\t// If this setting is set to true, the request handler will use a mutex for data(trie routing) protection,\n\t// hence the performance cost.\n\t//\n\t// Defaults to false.\n\tEnableDynamicHandler bool `ini:\"enable_dynamic_handler\" json:\"enableDynamicHandler,omitempty\" yaml:\"EnableDynamicHandler\" toml:\"EnableDynamicHandler\"`\n\t// FireMethodNotAllowed if it's true router checks for StatusMethodNotAllowed(405) and\n\t//  fires the 405 error instead of 404\n\t// Defaults to false.\n\tFireMethodNotAllowed bool `ini:\"fire_method_not_allowed\" json:\"fireMethodNotAllowed,omitempty\" yaml:\"FireMethodNotAllowed\" toml:\"FireMethodNotAllowed\"`\n\t// DisableAutoFireStatusCode if true then it turns off the http error status code\n\t// handler automatic execution on error code from a `Context.StatusCode` call.\n\t// By-default a custom http error handler will be fired when \"Context.StatusCode(errorCode)\" called.\n\t//\n\t// Defaults to false.\n\tDisableAutoFireStatusCode bool `ini:\"disable_auto_fire_status_code\" json:\"disableAutoFireStatusCode,omitempty\" yaml:\"DisableAutoFireStatusCode\" toml:\"DisableAutoFireStatusCode\"`\n\t// ResetOnFireErrorCode if true then any previously response body or headers through\n\t// response recorder will be ignored and the router\n\t// will fire the registered (or default) HTTP error handler instead.\n\t// See `core/router/handler#FireErrorCode` and `Context.EndRequest` for more details.\n\t//\n\t// Read more at: https://github.com/kataras/iris/issues/1531\n\t//\n\t// Defaults to false.\n\tResetOnFireErrorCode bool `ini:\"reset_on_fire_error_code\" json:\"resetOnFireErrorCode,omitempty\" yaml:\"ResetOnFireErrorCode\" toml:\"ResetOnFireErrorCode\"`\n\n\t// URLParamSeparator defines the character(s) separator for Context.URLParamSlice.\n\t// If empty or null then request url parameters with comma separated values will be retrieved as one.\n\t//\n\t// Defaults to comma \",\".\n\tURLParamSeparator *string `ini:\"url_param_separator\" json:\"urlParamSeparator,omitempty\" yaml:\"URLParamSeparator\" toml:\"URLParamSeparator\"`\n\t// EnableOptimization when this field is true\n\t// then the application tries to optimize for the best performance where is possible.\n\t//\n\t// Defaults to false.\n\t// Deprecated. As of version 12.2.x this field does nothing.\n\tEnableOptimizations bool `ini:\"enable_optimizations\" json:\"enableOptimizations,omitempty\" yaml:\"EnableOptimizations\" toml:\"EnableOptimizations\"`\n\t// EnableProtoJSON when this field is true\n\t// enables the proto marshaler on given proto messages when calling the Context.JSON method.\n\t//\n\t// Defaults to false.\n\tEnableProtoJSON bool `ini:\"enable_proto_json\" json:\"enableProtoJSON,omitempty\" yaml:\"EnableProtoJSON\" toml:\"EnableProtoJSON\"`\n\t// EnableEasyJSON when this field is true\n\t// enables the fast easy json marshaler on compatible struct values when calling the Context.JSON method.\n\t//\n\t// Defaults to false.\n\tEnableEasyJSON bool `ini:\"enable_easy_json\" json:\"enableEasyJSON,omitempty\" yaml:\"EnableEasyJSON\" toml:\"EnableEasyJSON\"`\n\n\t// DisableBodyConsumptionOnUnmarshal manages the reading behavior of the context's body readers/binders.\n\t// If set to true then it\n\t// disables the body consumption by the `context.UnmarshalBody/ReadJSON/ReadXML`.\n\t//\n\t// By-default io.ReadAll` is used to read the body from the `context.Request.Body which is an `io.ReadCloser`,\n\t// if this field set to true then a new buffer will be created to read from and the request body.\n\t// The body will not be changed and existing data before the\n\t// context.UnmarshalBody/ReadJSON/ReadXML will be not consumed.\n\t//\n\t// See `Context.RecordRequestBody` method for the same feature, per-request.\n\tDisableBodyConsumptionOnUnmarshal bool `ini:\"disable_body_consumption\" json:\"disableBodyConsumptionOnUnmarshal,omitempty\" yaml:\"DisableBodyConsumptionOnUnmarshal\" toml:\"DisableBodyConsumptionOnUnmarshal\"`\n\t// FireEmptyFormError returns if set to tue true then the `context.ReadForm/ReadQuery/ReadBody`\n\t// will return an `iris.ErrEmptyForm` on empty request form data.\n\tFireEmptyFormError bool `ini:\"fire_empty_form_error\" json:\"fireEmptyFormError,omitempty\" yaml:\"FireEmptyFormError\" toml:\"FireEmptyFormError\"`\n\n\t// TimeFormat time format for any kind of datetime parsing\n\t// Defaults to  \"Mon, 02 Jan 2006 15:04:05 GMT\".\n\tTimeFormat string `ini:\"time_format\" json:\"timeFormat,omitempty\" yaml:\"TimeFormat\" toml:\"TimeFormat\"`\n\n\t// Charset character encoding for various rendering\n\t// used for templates and the rest of the responses\n\t// Defaults to \"utf-8\".\n\tCharset string `ini:\"charset\" json:\"charset,omitempty\" yaml:\"Charset\" toml:\"Charset\"`\n\n\t// PostMaxMemory sets the maximum post data size\n\t// that a client can send to the server, this differs\n\t// from the overall request body size which can be modified\n\t// by the `context#SetMaxRequestBodySize` or `iris#LimitRequestBodySize`.\n\t//\n\t// Defaults to 32MB or 32 << 20 if you prefer.\n\tPostMaxMemory int64 `ini:\"post_max_memory\" json:\"postMaxMemory\" yaml:\"PostMaxMemory\" toml:\"PostMaxMemory\"`\n\t//  +----------------------------------------------------+\n\t//  | Context's keys for values used on various featuers |\n\t//  +----------------------------------------------------+\n\n\t// Context values' keys for various features.\n\t//\n\t// LocaleContextKey is used by i18n to get the current request's locale, which contains a translate function too.\n\t//\n\t// Defaults to \"iris.locale\".\n\tLocaleContextKey string `ini:\"locale_context_key\" json:\"localeContextKey,omitempty\" yaml:\"LocaleContextKey\" toml:\"LocaleContextKey\"`\n\t// LanguageContextKey is the context key which a language can be modified by a middleware.\n\t// It has the highest priority over the rest and if it is empty then it is ignored,\n\t// if it set to a static string of \"default\" or to the default language's code\n\t// then the rest of the language extractors will not be called at all and\n\t// the default language will be set instead.\n\t//\n\t// Use with `Context.SetLanguage(\"el-GR\")`.\n\t//\n\t// See `i18n.ExtractFunc` for a more organised way of the same feature.\n\t// Defaults to \"iris.locale.language\".\n\tLanguageContextKey string `ini:\"language_context_key\" json:\"languageContextKey,omitempty\" yaml:\"LanguageContextKey\" toml:\"LanguageContextKey\"`\n\t// LanguageInputContextKey is the context key of a language that is given by the end-user.\n\t// It's the real user input of the language string, matched or not.\n\t//\n\t// Defaults to \"iris.locale.language.input\".\n\tLanguageInputContextKey string `ini:\"language_input_context_key\" json:\"languageInputContextKey,omitempty\" yaml:\"LanguageInputContextKey\" toml:\"LanguageInputContextKey\"`\n\t// VersionContextKey is the context key which an API Version can be modified\n\t// via a middleware through `SetVersion` method, e.g. `versioning.SetVersion(ctx, \">=1.0.0 <2.0.0\")`.\n\t// Defaults to \"iris.api.version\".\n\tVersionContextKey string `ini:\"version_context_key\" json:\"versionContextKey\" yaml:\"VersionContextKey\" toml:\"VersionContextKey\"`\n\t// VersionAliasesContextKey is the context key which the versioning feature\n\t// can look up for alternative values of a version and fallback to that.\n\t// Head over to the versioning package for more.\n\t// Defaults to \"iris.api.version.aliases\"\n\tVersionAliasesContextKey string `ini:\"version_aliases_context_key\" json:\"versionAliasesContextKey\" yaml:\"VersionAliasesContextKey\" toml:\"VersionAliasesContextKey\"`\n\t// ViewEngineContextKey is the context's values key\n\t// responsible to store and retrieve(view.Engine) the current view engine.\n\t// A middleware or a Party can modify its associated value to change\n\t// a view engine that `ctx.View` will render through.\n\t// If not an engine is registered by the end-developer\n\t// then its associated value is always nil,\n\t// meaning that the default value is nil.\n\t// See `Party.RegisterView` and `Context.ViewEngine` methods as well.\n\t//\n\t// Defaults to \"iris.view.engine\".\n\tViewEngineContextKey string `ini:\"view_engine_context_key\" json:\"viewEngineContextKey,omitempty\" yaml:\"ViewEngineContextKey\" toml:\"ViewEngineContextKey\"`\n\t// ViewLayoutContextKey is the context's values key\n\t// responsible to store and retrieve(string) the current view layout.\n\t// A middleware can modify its associated value to change\n\t// the layout that `ctx.View` will use to render a template.\n\t//\n\t// Defaults to \"iris.view.layout\".\n\tViewLayoutContextKey string `ini:\"view_layout_context_key\" json:\"viewLayoutContextKey,omitempty\" yaml:\"ViewLayoutContextKey\" toml:\"ViewLayoutContextKey\"`\n\t// ViewDataContextKey is the context's values key\n\t// responsible to store and retrieve(any) the current view binding data.\n\t// A middleware can modify its associated value to change\n\t// the template's data on-fly.\n\t//\n\t// Defaults to \"iris.view.data\".\n\tViewDataContextKey string `ini:\"view_data_context_key\" json:\"viewDataContextKey,omitempty\" yaml:\"ViewDataContextKey\" toml:\"ViewDataContextKey\"`\n\t// FallbackViewContextKey is the context's values key\n\t// responsible to store the view fallback information.\n\t//\n\t// Defaults to \"iris.view.fallback\".\n\tFallbackViewContextKey string `ini:\"fallback_view_context_key\" json:\"fallbackViewContextKey,omitempty\" yaml:\"FallbackViewContextKey\" toml:\"FallbackViewContextKey\"`\n\t// RemoteAddrHeaders are the allowed request headers names\n\t// that can be valid to parse the client's IP based on.\n\t// By-default no \"X-\" header is consired safe to be used for retrieving the\n\t// client's IP address, because those headers can manually change by\n\t// the client. But sometimes are useful e.g. when behind a proxy\n\t// you want to enable the \"X-Forwarded-For\" or when cloudflare\n\t// you want to enable the \"CF-Connecting-IP\", indeed you\n\t// can allow the `ctx.RemoteAddr()` to use any header\n\t// that the client may sent.\n\t//\n\t// Defaults to an empty slice but an example usage is:\n\t// RemoteAddrHeaders {\n\t//    \"X-Real-Ip\",\n\t//    \"X-Forwarded-For\",\n\t//    \"CF-Connecting-IP\",\n\t//    \"True-Client-Ip\",\n\t//    \"X-Appengine-Remote-Addr\",\n\t//\t}\n\t//\n\t// Look `context.RemoteAddr()` for more.\n\tRemoteAddrHeaders []string `ini:\"remote_addr_headers\" json:\"remoteAddrHeaders,omitempty\" yaml:\"RemoteAddrHeaders\" toml:\"RemoteAddrHeaders\"`\n\t// RemoteAddrHeadersForce forces the `Context.RemoteAddr()` method\n\t// to return the first entry of a request header as a fallback,\n\t// even if that IP is a part of the `RemoteAddrPrivateSubnets` list.\n\t// The default behavior, if a remote address is part of the `RemoteAddrPrivateSubnets`,\n\t// is to retrieve the IP from the `Request.RemoteAddr` field instead.\n\tRemoteAddrHeadersForce bool `ini:\"remote_addr_headers_force\" json:\"remoteAddrHeadersForce,omitempty\" yaml:\"RemoteAddrHeadersForce\" toml:\"RemoteAddrHeadersForce\"`\n\t// RemoteAddrPrivateSubnets defines the private sub-networks.\n\t// They are used to be compared against\n\t// IP Addresses fetched through `RemoteAddrHeaders` or `Context.Request.RemoteAddr`.\n\t// For details please navigate through: https://github.com/kataras/iris/issues/1453\n\t// Defaults to:\n\t// {\n\t// \tStart: \"10.0.0.0\",\n\t// \tEnd:   \"10.255.255.255\",\n\t// },\n\t// {\n\t// \tStart: \"100.64.0.0\",\n\t// \tEnd:   \"100.127.255.255\",\n\t// },\n\t// {\n\t// \tStart: \"172.16.0.0\",\n\t// \tEnd:   \"172.31.255.255\",\n\t// },\n\t// {\n\t// \tStart: \"192.0.0.0\",\n\t// \tEnd:   \"192.0.0.255\",\n\t// },\n\t// {\n\t// \tStart: \"192.168.0.0\",\n\t// \tEnd:   \"192.168.255.255\",\n\t// },\n\t// {\n\t// \tStart: \"198.18.0.0\",\n\t// \tEnd:   \"198.19.255.255\",\n\t// }\n\t//\n\t// Look `Context.RemoteAddr()` for more.\n\tRemoteAddrPrivateSubnets []netutil.IPRange `ini:\"remote_addr_private_subnets\" json:\"remoteAddrPrivateSubnets\" yaml:\"RemoteAddrPrivateSubnets\" toml:\"RemoteAddrPrivateSubnets\"`\n\t// SSLProxyHeaders defines the set of header key values\n\t// that would indicate a valid https Request (look `Context.IsSSL()`).\n\t// Example: `map[string]string{\"X-Forwarded-Proto\": \"https\"}`.\n\t//\n\t// Defaults to empty map.\n\tSSLProxyHeaders map[string]string `ini:\"ssl_proxy_headers\" json:\"sslProxyHeaders\" yaml:\"SSLProxyHeaders\" toml:\"SSLProxyHeaders\"`\n\t// HostProxyHeaders defines the set of headers that may hold a proxied hostname value for the clients.\n\t// Look `Context.Host()` for more.\n\t// Defaults to empty map.\n\tHostProxyHeaders map[string]bool `ini:\"host_proxy_headers\" json:\"hostProxyHeaders\" yaml:\"HostProxyHeaders\" toml:\"HostProxyHeaders\"`\n\t// Other are the custom, dynamic options, can be empty.\n\t// This field used only by you to set any app's options you want.\n\t//\n\t// Defaults to empty map.\n\tOther map[string]any `ini:\"other\" json:\"other,omitempty\" yaml:\"Other\" toml:\"Other\"`\n}\n\nvar _ context.ConfigurationReadOnly = (*Configuration)(nil)\n\n// GetVHost returns the VHost config field.\nfunc (c *Configuration) GetVHost() string {\n\tvhost := c.VHost\n\treturn vhost\n}\n\n// SetVHost sets the VHost config field.\nfunc (c *Configuration) SetVHost(s string) {\n\tc.VHost = s\n}\n\n// GetLogLevel returns the LogLevel field.\nfunc (c *Configuration) GetLogLevel() string {\n\treturn c.LogLevel\n}\n\n// GetSocketSharding returns the SocketSharding field.\nfunc (c *Configuration) GetSocketSharding() bool {\n\treturn c.SocketSharding\n}\n\n// GetKeepAlive returns the KeepAlive field.\nfunc (c *Configuration) GetKeepAlive() time.Duration {\n\treturn c.KeepAlive\n}\n\n// GetTimeout returns the Timeout field.\nfunc (c *Configuration) GetTimeout() time.Duration {\n\treturn c.Timeout\n}\n\n// GetNonBlocking returns the NonBlocking field.\nfunc (c *Configuration) GetNonBlocking() bool {\n\treturn c.NonBlocking\n}\n\n// GetTimeoutMessage returns the TimeoutMessage field.\nfunc (c *Configuration) GetTimeoutMessage() string {\n\treturn c.TimeoutMessage\n}\n\n// GetDisablePathCorrection returns the DisablePathCorrection field.\nfunc (c *Configuration) GetDisablePathCorrection() bool {\n\treturn c.DisablePathCorrection\n}\n\n// GetDisablePathCorrectionRedirection returns the DisablePathCorrectionRedirection field.\nfunc (c *Configuration) GetDisablePathCorrectionRedirection() bool {\n\treturn c.DisablePathCorrectionRedirection\n}\n\n// GetEnablePathIntelligence returns the EnablePathIntelligence field.\nfunc (c *Configuration) GetEnablePathIntelligence() bool {\n\treturn c.EnablePathIntelligence\n}\n\n// GetEnablePathEscape returns the EnablePathEscape field.\nfunc (c *Configuration) GetEnablePathEscape() bool {\n\treturn c.EnablePathEscape\n}\n\n// GetForceLowercaseRouting returns the ForceLowercaseRouting field.\nfunc (c *Configuration) GetForceLowercaseRouting() bool {\n\treturn c.ForceLowercaseRouting\n}\n\n// GetEnableDynamicHandler returns the EnableDynamicHandler field.\nfunc (c *Configuration) GetEnableDynamicHandler() bool {\n\treturn c.EnableDynamicHandler\n}\n\n// GetFireMethodNotAllowed returns the FireMethodNotAllowed field.\nfunc (c *Configuration) GetFireMethodNotAllowed() bool {\n\treturn c.FireMethodNotAllowed\n}\n\n// GetEnableOptimizations returns the EnableOptimizations.\nfunc (c *Configuration) GetEnableOptimizations() bool {\n\treturn c.EnableOptimizations\n}\n\n// GetEnableProtoJSON returns the EnableProtoJSON field.\nfunc (c *Configuration) GetEnableProtoJSON() bool {\n\treturn c.EnableProtoJSON\n}\n\n// GetEnableEasyJSON returns the EnableEasyJSON field.\nfunc (c *Configuration) GetEnableEasyJSON() bool {\n\treturn c.EnableEasyJSON\n}\n\n// GetDisableBodyConsumptionOnUnmarshal returns the DisableBodyConsumptionOnUnmarshal field.\nfunc (c *Configuration) GetDisableBodyConsumptionOnUnmarshal() bool {\n\treturn c.DisableBodyConsumptionOnUnmarshal\n}\n\n// GetFireEmptyFormError returns the DisableBodyConsumptionOnUnmarshal field.\nfunc (c *Configuration) GetFireEmptyFormError() bool {\n\treturn c.FireEmptyFormError\n}\n\n// GetDisableAutoFireStatusCode returns the DisableAutoFireStatusCode field.\nfunc (c *Configuration) GetDisableAutoFireStatusCode() bool {\n\treturn c.DisableAutoFireStatusCode\n}\n\n// GetResetOnFireErrorCode returns ResetOnFireErrorCode field.\nfunc (c *Configuration) GetResetOnFireErrorCode() bool {\n\treturn c.ResetOnFireErrorCode\n}\n\n// GetURLParamSeparator returns URLParamSeparator field.\nfunc (c *Configuration) GetURLParamSeparator() *string {\n\treturn c.URLParamSeparator\n}\n\n// GetTimeFormat returns the TimeFormat field.\nfunc (c *Configuration) GetTimeFormat() string {\n\treturn c.TimeFormat\n}\n\n// GetCharset returns the Charset field.\nfunc (c *Configuration) GetCharset() string {\n\treturn c.Charset\n}\n\n// GetPostMaxMemory returns the PostMaxMemory field.\nfunc (c *Configuration) GetPostMaxMemory() int64 {\n\treturn c.PostMaxMemory\n}\n\n// GetLocaleContextKey returns the LocaleContextKey field.\nfunc (c *Configuration) GetLocaleContextKey() string {\n\treturn c.LocaleContextKey\n}\n\n// GetLanguageContextKey returns the LanguageContextKey field.\nfunc (c *Configuration) GetLanguageContextKey() string {\n\treturn c.LanguageContextKey\n}\n\n// GetLanguageInputContextKey returns the LanguageInputContextKey field.\nfunc (c *Configuration) GetLanguageInputContextKey() string {\n\treturn c.LanguageInputContextKey\n}\n\n// GetVersionContextKey returns the VersionContextKey field.\nfunc (c *Configuration) GetVersionContextKey() string {\n\treturn c.VersionContextKey\n}\n\n// GetVersionAliasesContextKey returns the VersionAliasesContextKey field.\nfunc (c *Configuration) GetVersionAliasesContextKey() string {\n\treturn c.VersionAliasesContextKey\n}\n\n// GetViewEngineContextKey returns the ViewEngineContextKey field.\nfunc (c *Configuration) GetViewEngineContextKey() string {\n\treturn c.ViewEngineContextKey\n}\n\n// GetViewLayoutContextKey returns the ViewLayoutContextKey field.\nfunc (c *Configuration) GetViewLayoutContextKey() string {\n\treturn c.ViewLayoutContextKey\n}\n\n// GetViewDataContextKey returns the ViewDataContextKey field.\nfunc (c *Configuration) GetViewDataContextKey() string {\n\treturn c.ViewDataContextKey\n}\n\n// GetFallbackViewContextKey returns the FallbackViewContextKey field.\nfunc (c *Configuration) GetFallbackViewContextKey() string {\n\treturn c.FallbackViewContextKey\n}\n\n// GetRemoteAddrHeaders returns the RemoteAddrHeaders field.\nfunc (c *Configuration) GetRemoteAddrHeaders() []string {\n\treturn c.RemoteAddrHeaders\n}\n\n// GetRemoteAddrHeadersForce returns RemoteAddrHeadersForce field.\nfunc (c *Configuration) GetRemoteAddrHeadersForce() bool {\n\treturn c.RemoteAddrHeadersForce\n}\n\n// GetSSLProxyHeaders returns the SSLProxyHeaders field.\nfunc (c *Configuration) GetSSLProxyHeaders() map[string]string {\n\treturn c.SSLProxyHeaders\n}\n\n// GetRemoteAddrPrivateSubnets returns the RemoteAddrPrivateSubnets field.\nfunc (c *Configuration) GetRemoteAddrPrivateSubnets() []netutil.IPRange {\n\treturn c.RemoteAddrPrivateSubnets\n}\n\n// GetHostProxyHeaders returns the HostProxyHeaders field.\nfunc (c *Configuration) GetHostProxyHeaders() map[string]bool {\n\treturn c.HostProxyHeaders\n}\n\n// GetOther returns the Other field.\nfunc (c *Configuration) GetOther() map[string]any {\n\treturn c.Other\n}\n\n// WithConfiguration sets the \"c\" values to the framework's configurations.\n//\n// Usage:\n// app.Listen(\":8080\", iris.WithConfiguration(iris.Configuration{/* fields here */ }))\n// or\n// iris.WithConfiguration(iris.YAML(\"./cfg/iris.yml\"))\n// or\n// iris.WithConfiguration(iris.TOML(\"./cfg/iris.tml\"))\nfunc WithConfiguration(c Configuration) Configurator {\n\treturn func(app *Application) {\n\t\tmain := app.config\n\n\t\tif main == nil {\n\t\t\tapp.config = &c\n\t\t\treturn\n\t\t}\n\n\t\tif v := c.LogLevel; v != \"\" {\n\t\t\tmain.LogLevel = v\n\t\t}\n\n\t\tif v := c.SocketSharding; v {\n\t\t\tmain.SocketSharding = v\n\t\t}\n\n\t\tif v := c.KeepAlive; v > 0 {\n\t\t\tmain.KeepAlive = v\n\t\t}\n\n\t\tif v := c.Timeout; v > 0 {\n\t\t\tmain.Timeout = v\n\t\t}\n\n\t\tif v := c.TimeoutMessage; v != \"\" {\n\t\t\tmain.TimeoutMessage = v\n\t\t}\n\n\t\tif v := c.NonBlocking; v {\n\t\t\tmain.NonBlocking = v\n\t\t}\n\n\t\tif len(c.Tunneling.Tunnels) > 0 {\n\t\t\tmain.Tunneling = c.Tunneling\n\t\t}\n\n\t\tif v := c.IgnoreServerErrors; len(v) > 0 {\n\t\t\tmain.IgnoreServerErrors = append(main.IgnoreServerErrors, v...)\n\t\t}\n\n\t\tif v := c.DisableStartupLog; v {\n\t\t\tmain.DisableStartupLog = v\n\t\t}\n\n\t\tif v := c.DisableInterruptHandler; v {\n\t\t\tmain.DisableInterruptHandler = v\n\t\t}\n\n\t\tif v := c.DisablePathCorrection; v {\n\t\t\tmain.DisablePathCorrection = v\n\t\t}\n\n\t\tif v := c.DisablePathCorrectionRedirection; v {\n\t\t\tmain.DisablePathCorrectionRedirection = v\n\t\t}\n\n\t\tif v := c.EnablePathIntelligence; v {\n\t\t\tmain.EnablePathIntelligence = v\n\t\t}\n\n\t\tif v := c.EnablePathEscape; v {\n\t\t\tmain.EnablePathEscape = v\n\t\t}\n\n\t\tif v := c.ForceLowercaseRouting; v {\n\t\t\tmain.ForceLowercaseRouting = v\n\t\t}\n\n\t\tif v := c.EnableOptimizations; v {\n\t\t\tmain.EnableOptimizations = v\n\t\t}\n\n\t\tif v := c.EnableProtoJSON; v {\n\t\t\tmain.EnableProtoJSON = v\n\t\t}\n\n\t\tif v := c.EnableEasyJSON; v {\n\t\t\tmain.EnableEasyJSON = v\n\t\t}\n\n\t\tif v := c.FireMethodNotAllowed; v {\n\t\t\tmain.FireMethodNotAllowed = v\n\t\t}\n\n\t\tif v := c.DisableAutoFireStatusCode; v {\n\t\t\tmain.DisableAutoFireStatusCode = v\n\t\t}\n\n\t\tif v := c.ResetOnFireErrorCode; v {\n\t\t\tmain.ResetOnFireErrorCode = v\n\t\t}\n\n\t\tif v := c.URLParamSeparator; v != nil {\n\t\t\tmain.URLParamSeparator = v\n\t\t}\n\n\t\tif v := c.DisableBodyConsumptionOnUnmarshal; v {\n\t\t\tmain.DisableBodyConsumptionOnUnmarshal = v\n\t\t}\n\n\t\tif v := c.FireEmptyFormError; v {\n\t\t\tmain.FireEmptyFormError = v\n\t\t}\n\n\t\tif v := c.TimeFormat; v != \"\" {\n\t\t\tmain.TimeFormat = v\n\t\t}\n\n\t\tif v := c.Charset; v != \"\" {\n\t\t\tmain.Charset = v\n\t\t}\n\n\t\tif v := c.PostMaxMemory; v > 0 {\n\t\t\tmain.PostMaxMemory = v\n\t\t}\n\n\t\tif v := c.LocaleContextKey; v != \"\" {\n\t\t\tmain.LocaleContextKey = v\n\t\t}\n\n\t\tif v := c.LanguageContextKey; v != \"\" {\n\t\t\tmain.LanguageContextKey = v\n\t\t}\n\n\t\tif v := c.LanguageInputContextKey; v != \"\" {\n\t\t\tmain.LanguageInputContextKey = v\n\t\t}\n\n\t\tif v := c.VersionContextKey; v != \"\" {\n\t\t\tmain.VersionContextKey = v\n\t\t}\n\n\t\tif v := c.VersionAliasesContextKey; v != \"\" {\n\t\t\tmain.VersionAliasesContextKey = v\n\t\t}\n\n\t\tif v := c.ViewEngineContextKey; v != \"\" {\n\t\t\tmain.ViewEngineContextKey = v\n\t\t}\n\t\tif v := c.ViewLayoutContextKey; v != \"\" {\n\t\t\tmain.ViewLayoutContextKey = v\n\t\t}\n\t\tif v := c.ViewDataContextKey; v != \"\" {\n\t\t\tmain.ViewDataContextKey = v\n\t\t}\n\t\tif v := c.FallbackViewContextKey; v != \"\" {\n\t\t\tmain.FallbackViewContextKey = v\n\t\t}\n\n\t\tif v := c.RemoteAddrHeaders; len(v) > 0 {\n\t\t\tmain.RemoteAddrHeaders = v\n\t\t}\n\n\t\tif v := c.RemoteAddrHeadersForce; v {\n\t\t\tmain.RemoteAddrHeadersForce = v\n\t\t}\n\n\t\tif v := c.RemoteAddrPrivateSubnets; len(v) > 0 {\n\t\t\tmain.RemoteAddrPrivateSubnets = v\n\t\t}\n\n\t\tif v := c.SSLProxyHeaders; len(v) > 0 {\n\t\t\tif main.SSLProxyHeaders == nil {\n\t\t\t\tmain.SSLProxyHeaders = make(map[string]string, len(v))\n\t\t\t}\n\t\t\tfor key, value := range v {\n\t\t\t\tmain.SSLProxyHeaders[key] = value\n\t\t\t}\n\t\t}\n\n\t\tif v := c.HostProxyHeaders; len(v) > 0 {\n\t\t\tif main.HostProxyHeaders == nil {\n\t\t\t\tmain.HostProxyHeaders = make(map[string]bool, len(v))\n\t\t\t}\n\t\t\tfor key, value := range v {\n\t\t\t\tmain.HostProxyHeaders[key] = value\n\t\t\t}\n\t\t}\n\n\t\tif v := c.Other; len(v) > 0 {\n\t\t\tif main.Other == nil {\n\t\t\t\tmain.Other = make(map[string]any, len(v))\n\t\t\t}\n\t\t\tfor key, value := range v {\n\t\t\t\tmain.Other[key] = value\n\t\t\t}\n\t\t}\n\t}\n}\n\n// DefaultTimeoutMessage is the default timeout message which is rendered\n// on expired handlers when timeout handler is registered (see Timeout configuration field).\nvar DefaultTimeoutMessage = `<html><head><title>Timeout</title></head><body><h1>Timeout</h1>Looks like the server is taking too long to respond, this can be caused by either poor connectivity or an error with our servers. Please try again in a while.</body></html>`\n\nfunc toStringPtr(s string) *string {\n\treturn &s\n}\n\n// DefaultConfiguration returns the default configuration for an iris station, fills the main Configuration\nfunc DefaultConfiguration() Configuration {\n\treturn Configuration{\n\t\tLogLevel:                          \"info\",\n\t\tSocketSharding:                    false,\n\t\tKeepAlive:                         0,\n\t\tTimeout:                           0,\n\t\tTimeoutMessage:                    DefaultTimeoutMessage,\n\t\tNonBlocking:                       false,\n\t\tDisableStartupLog:                 false,\n\t\tDisableInterruptHandler:           false,\n\t\tDisablePathCorrection:             false,\n\t\tEnablePathEscape:                  false,\n\t\tForceLowercaseRouting:             false,\n\t\tFireMethodNotAllowed:              false,\n\t\tDisableBodyConsumptionOnUnmarshal: false,\n\t\tFireEmptyFormError:                false,\n\t\tDisableAutoFireStatusCode:         false,\n\t\tResetOnFireErrorCode:              false,\n\t\tURLParamSeparator:                 toStringPtr(\",\"),\n\t\tTimeFormat:                        \"Mon, 02 Jan 2006 15:04:05 GMT\",\n\t\tCharset:                           \"utf-8\",\n\n\t\t// PostMaxMemory is for post body max memory.\n\t\t//\n\t\t// The request body the size limit\n\t\t// can be set by the middleware `LimitRequestBodySize`\n\t\t// or `context#SetMaxRequestBodySize`.\n\t\tPostMaxMemory:            32 << 20, // 32MB\n\t\tLocaleContextKey:         \"iris.locale\",\n\t\tLanguageContextKey:       \"iris.locale.language\",\n\t\tLanguageInputContextKey:  \"iris.locale.language.input\",\n\t\tVersionContextKey:        \"iris.api.version\",\n\t\tVersionAliasesContextKey: \"iris.api.version.aliases\",\n\t\tViewEngineContextKey:     \"iris.view.engine\",\n\t\tViewLayoutContextKey:     \"iris.view.layout\",\n\t\tViewDataContextKey:       \"iris.view.data\",\n\t\tFallbackViewContextKey:   \"iris.view.fallback\",\n\t\tRemoteAddrHeaders:        nil,\n\t\tRemoteAddrHeadersForce:   false,\n\t\tRemoteAddrPrivateSubnets: []netutil.IPRange{\n\t\t\t{\n\t\t\t\tStart: \"10.0.0.0\",\n\t\t\t\tEnd:   \"10.255.255.255\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tStart: \"100.64.0.0\",\n\t\t\t\tEnd:   \"100.127.255.255\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tStart: \"172.16.0.0\",\n\t\t\t\tEnd:   \"172.31.255.255\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tStart: \"192.0.0.0\",\n\t\t\t\tEnd:   \"192.0.0.255\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tStart: \"192.168.0.0\",\n\t\t\t\tEnd:   \"192.168.255.255\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tStart: \"198.18.0.0\",\n\t\t\t\tEnd:   \"198.19.255.255\",\n\t\t\t},\n\t\t},\n\t\tSSLProxyHeaders:     make(map[string]string),\n\t\tHostProxyHeaders:    make(map[string]bool),\n\t\tEnableOptimizations: false,\n\t\tEnableProtoJSON:     false,\n\t\tEnableEasyJSON:      false,\n\t\tOther:               make(map[string]any),\n\t}\n}\n"
  },
  {
    "path": "configuration_test.go",
    "content": "package iris\n\nimport (\n\t\"os\"\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n\n\t\"gopkg.in/yaml.v3\"\n)\n\n// $ go test -v -run TestConfiguration*\n\nfunc TestConfigurationStatic(t *testing.T) {\n\tdef := DefaultConfiguration()\n\n\tapp := New()\n\tafterNew := *app.config\n\n\tif !reflect.DeepEqual(def, afterNew) {\n\t\tt.Fatalf(\"Default configuration is not the same after New expected:\\n %#v \\ngot:\\n %#v\", def, afterNew)\n\t}\n\n\tafterNew.Charset = \"changed\"\n\n\tif reflect.DeepEqual(def, afterNew) {\n\t\tt.Fatalf(\"Configuration should be not equal, got: %#v\", afterNew)\n\t}\n\n\tapp = New().Configure(WithConfiguration(Configuration{DisableBodyConsumptionOnUnmarshal: true}))\n\n\tafterNew = *app.config\n\n\tif !app.config.DisableBodyConsumptionOnUnmarshal {\n\t\tt.Fatalf(\"Passing a Configuration field as Option fails, expected DisableBodyConsumptionOnUnmarshal to be true but was false\")\n\t}\n\n\tapp = New() // empty , means defaults so\n\tif !reflect.DeepEqual(def, *app.config) {\n\t\tt.Fatalf(\"Default configuration is not the same after New expected:\\n %#v \\ngot:\\n %#v\", def, *app.config)\n\t}\n}\n\nfunc TestConfigurationOptions(t *testing.T) {\n\tcharset := \"MYCHARSET\"\n\tdisableBodyConsumptionOnUnmarshal := true\n\tdisableBanner := true\n\n\tapp := New().Configure(WithCharset(charset), WithoutBodyConsumptionOnUnmarshal, WithoutBanner)\n\n\tif got := app.config.Charset; got != charset {\n\t\tt.Fatalf(\"Expected configuration Charset to be: %s but got: %s\", charset, got)\n\t}\n\n\tif got := app.config.DisableBodyConsumptionOnUnmarshal; got != disableBodyConsumptionOnUnmarshal {\n\t\tt.Fatalf(\"Expected configuration DisableBodyConsumptionOnUnmarshal to be: %#v but got: %#v\", disableBodyConsumptionOnUnmarshal, got)\n\t}\n\n\tif got := app.config.DisableStartupLog; got != disableBanner {\n\t\tt.Fatalf(\"Expected configuration DisableStartupLog to be: %#v but got: %#v\", disableBanner, got)\n\t}\n\n\t// now check if other default values are set (should be set automatically)\n\n\texpected := DefaultConfiguration()\n\texpected.Charset = charset\n\texpected.DisableBodyConsumptionOnUnmarshal = disableBodyConsumptionOnUnmarshal\n\texpected.DisableStartupLog = disableBanner\n\n\thas := *app.config\n\tif !reflect.DeepEqual(has, expected) {\n\t\tt.Fatalf(\"Default configuration is not the same after New expected:\\n %#v \\ngot:\\n %#v\", expected, has)\n\t}\n}\n\nfunc TestConfigurationOptionsDeep(t *testing.T) {\n\tcharset := \"MYCHARSET\"\n\n\tapp := New().Configure(WithCharset(charset), WithoutBodyConsumptionOnUnmarshal, WithoutBanner)\n\n\texpected := DefaultConfiguration()\n\texpected.Charset = charset\n\texpected.DisableBodyConsumptionOnUnmarshal = true\n\texpected.DisableStartupLog = true\n\n\thas := *app.config\n\n\tif !reflect.DeepEqual(has, expected) {\n\t\tt.Fatalf(\"DEEP configuration is not the same after New expected:\\n %#v \\ngot:\\n %#v\", expected, has)\n\t}\n}\n\nfunc createGlobalConfiguration(t *testing.T) {\n\tfilename := homeConfigurationFilename(\".yml\")\n\tc, err := parseYAML(filename)\n\tif err != nil {\n\t\t// this error will be occurred the first time that the configuration\n\t\t// file doesn't exist.\n\t\t// Create the YAML-ONLY global configuration file now using the default configuration 'c'.\n\t\t// This is useful when we run multiple iris servers that share the same\n\t\t// configuration, even with custom values at its \"Other\" field.\n\t\tout, err := yaml.Marshal(&c)\n\n\t\tif err == nil {\n\t\t\terr = os.WriteFile(filename, out, os.FileMode(0666))\n\t\t}\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"error while writing the global configuration field at: %s. Trace: %v\\n\", filename, err)\n\t\t}\n\t}\n}\n\nfunc TestConfigurationGlobal(t *testing.T) {\n\tt.Cleanup(func() {\n\t\tos.Remove(homeConfigurationFilename(\".yml\"))\n\t})\n\n\tcreateGlobalConfiguration(t)\n\n\ttestConfigurationGlobal(t, WithGlobalConfiguration)\n\ttestConfigurationGlobal(t, WithConfiguration(YAML(globalConfigurationKeyword)))\n}\n\nfunc testConfigurationGlobal(t *testing.T, c Configurator) {\n\tapp := New().Configure(c)\n\n\tif has, expected := *app.config, DefaultConfiguration(); !reflect.DeepEqual(has, expected) {\n\t\tt.Fatalf(\"global configuration (which should be defaulted) is not the same with the default one:\\n %#v \\ngot:\\n %#v\", has, expected)\n\t}\n}\n\nfunc TestConfigurationYAML(t *testing.T) {\n\tyamlFile, ferr := os.CreateTemp(\"\", \"configuration.yml\")\n\n\tif ferr != nil {\n\t\tt.Fatal(ferr)\n\t}\n\n\tdefer func() {\n\t\tyamlFile.Close()\n\t\ttime.Sleep(50 * time.Millisecond)\n\t\tos.Remove(yamlFile.Name())\n\t}()\n\n\tyamlConfigurationContents := `\nDisablePathCorrection: false\nDisablePathCorrectionRedirection: true\nEnablePathIntelligence: true\nEnablePathEscape: false\nFireMethodNotAllowed: true\nEnableOptimizations: true\nDisableBodyConsumptionOnUnmarshal: true\nTimeFormat: \"Mon, 02 Jan 2006 15:04:05 GMT\"\nCharset: \"utf-8\"\nRemoteAddrHeaders:\n  - X-Real-Ip\n  - X-Forwarded-For\n  - CF-Connecting-IP\nHostProxyHeaders:\n  X-Host: true\nSSLProxyHeaders:\n  X-Forwarded-Proto: https\nOther:\n  MyServerName: \"Iris: https://github.com/kataras/iris\"\n`\n\tyamlFile.WriteString(yamlConfigurationContents)\n\tfilename := yamlFile.Name()\n\tapp := New().Configure(WithConfiguration(YAML(filename)))\n\n\tc := app.config\n\n\tif expected := false; c.DisablePathCorrection != expected {\n\t\tt.Fatalf(\"error on TestConfigurationYAML: Expected DisablePathCorrection %v but got %v\", expected, c.DisablePathCorrection)\n\t}\n\n\tif expected := true; c.DisablePathCorrectionRedirection != expected {\n\t\tt.Fatalf(\"error on TestConfigurationYAML: Expected DisablePathCorrectionRedirection %v but got %v\", expected, c.DisablePathCorrectionRedirection)\n\t}\n\n\tif expected := true; c.EnablePathIntelligence != expected {\n\t\tt.Fatalf(\"error on TestConfigurationYAML: Expected EnablePathIntelligence %v but got %v\", expected, c.EnablePathIntelligence)\n\t}\n\n\tif expected := false; c.EnablePathEscape != expected {\n\t\tt.Fatalf(\"error on TestConfigurationYAML: Expected EnablePathEscape %v but got %v\", expected, c.EnablePathEscape)\n\t}\n\n\tif expected := true; c.EnableOptimizations != expected {\n\t\tt.Fatalf(\"error on TestConfigurationYAML: Expected EnableOptimizations %v but got %v\", expected, c.EnablePathEscape)\n\t}\n\n\tif expected := true; c.FireMethodNotAllowed != expected {\n\t\tt.Fatalf(\"error on TestConfigurationYAML: Expected FireMethodNotAllowed %v but got %v\", expected, c.FireMethodNotAllowed)\n\t}\n\n\tif expected := true; c.DisableBodyConsumptionOnUnmarshal != expected {\n\t\tt.Fatalf(\"error on TestConfigurationYAML: Expected DisableBodyConsumptionOnUnmarshal %v but got %v\", expected, c.DisableBodyConsumptionOnUnmarshal)\n\t}\n\n\tif expected := \"Mon, 02 Jan 2006 15:04:05 GMT\"; c.TimeFormat != expected {\n\t\tt.Fatalf(\"error on TestConfigurationYAML: Expected TimeFormat %s but got %s\", expected, c.TimeFormat)\n\t}\n\n\tif expected := \"utf-8\"; c.Charset != expected {\n\t\tt.Fatalf(\"error on TestConfigurationYAML: Expected Charset %s but got %s\", expected, c.Charset)\n\t}\n\n\tif len(c.RemoteAddrHeaders) == 0 {\n\t\tt.Fatalf(\"error on TestConfigurationYAML: Expected RemoteAddrHeaders to be filled\")\n\t}\n\n\texpectedRemoteAddrHeaders := []string{\n\t\t\"X-Real-Ip\",\n\t\t\"X-Forwarded-For\",\n\t\t\"CF-Connecting-IP\",\n\t}\n\n\tif expected, got := len(c.RemoteAddrHeaders), len(expectedRemoteAddrHeaders); expected != got {\n\t\tt.Fatalf(\"error on TestConfigurationYAML: Expected RemoteAddrHeaders' len(%d) and got(%d), len is not the same\", expected, got)\n\t}\n\n\tfor i, v := range c.RemoteAddrHeaders {\n\t\tif expected, got := expectedRemoteAddrHeaders[i], v; expected != got {\n\t\t\tt.Fatalf(\"error on TestConfigurationYAML: Expected RemoteAddrHeaders[%d] = %s but got %s\", i, expected, got)\n\t\t}\n\t}\n\n\texpectedHostProxyHeaders := map[string]bool{\n\t\t\"X-Host\": true,\n\t}\n\n\tif expected, got := len(c.HostProxyHeaders), len(expectedHostProxyHeaders); expected != got {\n\t\tt.Fatalf(\"error on TestConfigurationYAML: Expected HostProxyHeaders' len(%d) and got(%d), len is not the same\", expected, got)\n\t}\n\n\tfor k, v := range c.HostProxyHeaders {\n\t\tif expected, got := expectedHostProxyHeaders[k], v; expected != got {\n\t\t\tt.Fatalf(\"error on TestConfigurationYAML: Expected HostProxyHeaders[%s] = %t but got %t\", k, expected, got)\n\t\t}\n\t}\n\n\texpectedSSLProxyHeaders := map[string]string{\n\t\t\"X-Forwarded-Proto\": \"https\",\n\t}\n\n\tif expected, got := len(c.SSLProxyHeaders), len(c.SSLProxyHeaders); expected != got {\n\t\tt.Fatalf(\"error on TestConfigurationYAML: Expected SSLProxyHeaders' len(%d) and got(%d), len is not the same\", expected, got)\n\t}\n\n\tfor k, v := range c.SSLProxyHeaders {\n\t\tif expected, got := expectedSSLProxyHeaders[k], v; expected != got {\n\t\t\tt.Fatalf(\"error on TestConfigurationYAML: Expected SSLProxyHeaders[%s] = %s but got %s\", k, expected, got)\n\t\t}\n\t}\n\n\tif len(c.Other) == 0 {\n\t\tt.Fatalf(\"error on TestConfigurationYAML: Expected Other to be filled\")\n\t}\n\n\tif expected, got := \"Iris: https://github.com/kataras/iris\", c.Other[\"MyServerName\"]; expected != got {\n\t\tt.Fatalf(\"error on TestConfigurationYAML: Expected Other['MyServerName'] %s but got %s\", expected, got)\n\t}\n}\n\nfunc TestConfigurationTOML(t *testing.T) {\n\ttomlFile, ferr := os.CreateTemp(\"\", \"configuration.toml\")\n\n\tif ferr != nil {\n\t\tt.Fatal(ferr)\n\t}\n\n\tdefer func() {\n\t\ttomlFile.Close()\n\t\ttime.Sleep(50 * time.Millisecond)\n\t\tos.Remove(tomlFile.Name())\n\t}()\n\n\ttomlConfigurationContents := `\nDisablePathCorrectionRedirection = true\nEnablePathEscape = false\nFireMethodNotAllowed = true\nEnableOptimizations = true\nDisableBodyConsumptionOnUnmarshal = true\nTimeFormat = \"Mon, 02 Jan 2006 15:04:05 GMT\"\nCharset = \"utf-8\"\n\nRemoteAddrHeaders = [\"X-Real-Ip\", \"X-Forwarded-For\", \"CF-Connecting-IP\"]\n\n[Other]\n\t# Indentation (tabs and/or spaces) is allowed but not required\n\tMyServerName = \"Iris: https://github.com/kataras/iris\"\n\n`\n\ttomlFile.WriteString(tomlConfigurationContents)\n\tfilename := tomlFile.Name()\n\tapp := New().Configure(WithConfiguration(TOML(filename)))\n\n\tc := app.config\n\n\tif expected := false; c.DisablePathCorrection != expected {\n\t\tt.Fatalf(\"error on TestConfigurationTOML: Expected DisablePathCorrection %v but got %v\", expected, c.DisablePathCorrection)\n\t}\n\n\tif expected := true; c.DisablePathCorrectionRedirection != expected {\n\t\tt.Fatalf(\"error on TestConfigurationTOML: Expected DisablePathCorrectionRedirection %v but got %v\", expected, c.DisablePathCorrectionRedirection)\n\t}\n\n\tif expected := false; c.EnablePathEscape != expected {\n\t\tt.Fatalf(\"error on TestConfigurationTOML: Expected EnablePathEscape %v but got %v\", expected, c.EnablePathEscape)\n\t}\n\n\tif expected := true; c.EnableOptimizations != expected {\n\t\tt.Fatalf(\"error on TestConfigurationTOML: Expected EnableOptimizations %v but got %v\", expected, c.EnablePathEscape)\n\t}\n\n\tif expected := true; c.FireMethodNotAllowed != expected {\n\t\tt.Fatalf(\"error on TestConfigurationTOML: Expected FireMethodNotAllowed %v but got %v\", expected, c.FireMethodNotAllowed)\n\t}\n\n\tif expected := true; c.DisableBodyConsumptionOnUnmarshal != expected {\n\t\tt.Fatalf(\"error on TestConfigurationTOML: Expected DisableBodyConsumptionOnUnmarshal %v but got %v\", expected, c.DisableBodyConsumptionOnUnmarshal)\n\t}\n\n\tif expected := \"Mon, 02 Jan 2006 15:04:05 GMT\"; c.TimeFormat != expected {\n\t\tt.Fatalf(\"error on TestConfigurationTOML: Expected TimeFormat %s but got %s\", expected, c.TimeFormat)\n\t}\n\n\tif expected := \"utf-8\"; c.Charset != expected {\n\t\tt.Fatalf(\"error on TestConfigurationTOML: Expected Charset %s but got %s\", expected, c.Charset)\n\t}\n\n\tif len(c.RemoteAddrHeaders) == 0 {\n\t\tt.Fatalf(\"error on TestConfigurationTOML: Expected RemoteAddrHeaders to be filled\")\n\t}\n\n\texpectedRemoteAddrHeaders := []string{\n\t\t\"X-Real-Ip\",\n\t\t\"X-Forwarded-For\",\n\t\t\"CF-Connecting-IP\",\n\t}\n\n\tif expected, got := len(c.RemoteAddrHeaders), len(expectedRemoteAddrHeaders); expected != got {\n\t\tt.Fatalf(\"error on TestConfigurationTOML: Expected RemoteAddrHeaders' len(%d) and got(%d), len is not the same\", expected, got)\n\t}\n\n\tfor i, got := range c.RemoteAddrHeaders {\n\t\tif expected := expectedRemoteAddrHeaders[i]; expected != got {\n\t\t\tt.Fatalf(\"error on TestConfigurationTOML: Expected RemoteAddrHeaders[%d] = %s but got %s\", i, expected, got)\n\t\t}\n\t}\n\n\tif len(c.Other) == 0 {\n\t\tt.Fatalf(\"error on TestConfigurationTOML: Expected Other to be filled\")\n\t}\n\n\tif expected, got := \"Iris: https://github.com/kataras/iris\", c.Other[\"MyServerName\"]; expected != got {\n\t\tt.Fatalf(\"error on TestConfigurationTOML: Expected Other['MyServerName'] %s but got %s\", expected, got)\n\t}\n}\n"
  },
  {
    "path": "context/accept_header.go",
    "content": "package context\n\nimport \"strings\"\n\nfunc negotiationMatch(in []string, priorities []string) string {\n\t// e.g.\n\t// match json:\n\t// \tin: text/html, application/json\n\t// \tpriorities: application/json\n\t// not match:\n\t// \tin: text/html, application/json\n\t// \tpriorities: text/xml\n\t// match html:\n\t// \tin: text/html, application/json\n\t// \tpriorities: */*\n\t// not match:\n\t// \tin: application/json\n\t// \tpriorities: text/xml\n\t// match json:\n\t// \tin: text/html, application/*\n\t// \tpriorities: application/json\n\n\tif len(priorities) == 0 {\n\t\treturn \"\"\n\t}\n\n\tif len(in) == 0 {\n\t\treturn priorities[0]\n\t}\n\n\tfor _, accepted := range in {\n\t\tfor _, p := range priorities {\n\t\t\t// wildcard is */* or text/* and etc.\n\t\t\t// so loop through each char.\n\t\t\tfor i, n := 0, len(accepted); i < n; i++ {\n\t\t\t\tif accepted[i] != p[i] {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\n\t\t\t\tif accepted[i] == '*' || p[i] == '*' {\n\t\t\t\t\treturn p\n\t\t\t\t}\n\n\t\t\t\tif i == n-1 {\n\t\t\t\t\treturn p\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn \"\"\n}\n\nfunc negotiateAcceptHeader(in []string, offers []string, bestOffer string) string {\n\tif bestOffer == \"\" {\n\t\tbestOffer = IDENTITY\n\t}\n\n\tbestQ := -1.0\n\tspecs := parseAccept(in)\n\tfor _, offer := range offers {\n\t\tfor _, spec := range specs {\n\t\t\tif spec.Q > bestQ &&\n\t\t\t\t(spec.Value == \"*\" || spec.Value == offer) {\n\t\t\t\tbestQ = spec.Q\n\t\t\t\tbestOffer = offer\n\t\t\t}\n\t\t}\n\t}\n\tif bestQ == 0 {\n\t\tbestOffer = \"\"\n\t}\n\treturn bestOffer\n}\n\n// acceptSpec describes an Accept* header.\ntype acceptSpec struct {\n\tValue string\n\tQ     float64\n}\n\n// parseAccept parses Accept* headers.\nfunc parseAccept(in []string) (specs []acceptSpec) {\nloop:\n\tfor _, s := range in {\n\t\tfor {\n\t\t\tvar spec acceptSpec\n\t\t\tspec.Value, s = expectTokenSlash(s)\n\t\t\tif spec.Value == \"\" {\n\t\t\t\tcontinue loop\n\t\t\t}\n\t\t\tspec.Q = 1.0\n\t\t\ts = skipSpace(s)\n\t\t\tif strings.HasPrefix(s, \";\") {\n\t\t\t\ts = skipSpace(s[1:])\n\t\t\t\tif !strings.HasPrefix(s, \"q=\") {\n\t\t\t\t\tcontinue loop\n\t\t\t\t}\n\t\t\t\tspec.Q, s = expectQuality(s[2:])\n\t\t\t\tif spec.Q < 0.0 {\n\t\t\t\t\tcontinue loop\n\t\t\t\t}\n\t\t\t}\n\t\t\tspecs = append(specs, spec)\n\t\t\ts = skipSpace(s)\n\t\t\tif !strings.HasPrefix(s, \",\") {\n\t\t\t\tcontinue loop\n\t\t\t}\n\t\t\ts = skipSpace(s[1:])\n\t\t}\n\t}\n\treturn\n}\n\nfunc skipSpace(s string) (rest string) {\n\ti := 0\n\tfor ; i < len(s); i++ {\n\t\tif octetTypes[s[i]]&isSpace == 0 {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn s[i:]\n}\n\nfunc expectTokenSlash(s string) (token, rest string) {\n\ti := 0\n\tfor ; i < len(s); i++ {\n\t\tb := s[i]\n\t\tif (octetTypes[b]&isToken == 0) && b != '/' {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn s[:i], s[i:]\n}\n\nfunc expectQuality(s string) (q float64, rest string) {\n\tswitch {\n\tcase s == \"\":\n\t\treturn -1, \"\"\n\tcase s[0] == '0':\n\t\tq = 0\n\tcase s[0] == '1':\n\t\tq = 1\n\tdefault:\n\t\treturn -1, \"\"\n\t}\n\ts = s[1:]\n\tif !strings.HasPrefix(s, \".\") {\n\t\treturn q, s\n\t}\n\ts = s[1:]\n\ti := 0\n\tn := 0\n\td := 1\n\tfor ; i < len(s); i++ {\n\t\tb := s[i]\n\t\tif b < '0' || b > '9' {\n\t\t\tbreak\n\t\t}\n\t\tn = n*10 + int(b) - '0'\n\t\td *= 10\n\t}\n\treturn q + float64(n)/float64(d), s[i:]\n}\n\n// Octet types from RFC 2616.\nvar octetTypes [256]octetType\n\ntype octetType byte\n\nconst (\n\tisToken octetType = 1 << iota\n\tisSpace\n)\n\nfunc init() {\n\t// OCTET      = <any 8-bit sequence of data>\n\t// CHAR       = <any US-ASCII character (octets 0 - 127)>\n\t// CTL        = <any US-ASCII control character (octets 0 - 31) and DEL (127)>\n\t// CR         = <US-ASCII CR, carriage return (13)>\n\t// LF         = <US-ASCII LF, linefeed (10)>\n\t// SP         = <US-ASCII SP, space (32)>\n\t// HT         = <US-ASCII HT, horizontal-tab (9)>\n\t// <\">        = <US-ASCII double-quote mark (34)>\n\t// CRLF       = CR LF\n\t// LWS        = [CRLF] 1*( SP | HT )\n\t// TEXT       = <any OCTET except CTLs, but including LWS>\n\t// separators = \"(\" | \")\" | \"<\" | \">\" | \"@\" | \",\" | \";\" | \":\" | \"\\\" | <\">\n\t//              | \"/\" | \"[\" | \"]\" | \"?\" | \"=\" | \"{\" | \"}\" | SP | HT\n\t// token      = 1*<any CHAR except CTLs or separators>\n\t// qdtext     = <any TEXT except <\">>\n\n\tfor c := 0; c < 256; c++ {\n\t\tvar t octetType\n\t\tisCtl := c <= 31 || c == 127\n\t\tisChar := 0 <= c && c <= 127\n\t\tisSeparator := strings.ContainsRune(\" \\t\\\"(),/:;<=>?@[]\\\\{}\", rune(c))\n\t\tif strings.ContainsRune(\" \\t\\r\\n\", rune(c)) {\n\t\t\tt |= isSpace\n\t\t}\n\t\tif isChar && !isCtl && !isSeparator {\n\t\t\tt |= isToken\n\t\t}\n\t\toctetTypes[c] = t\n\t}\n}\n"
  },
  {
    "path": "context/application.go",
    "content": "package context\n\nimport (\n\tstdContext \"context\"\n\t\"io\"\n\t\"net/http\"\n\t\"sync\"\n\n\t\"github.com/kataras/golog\"\n\t\"github.com/tdewolff/minify/v2\"\n)\n\n// Application is the context's owner.\n// This interface contains the functions that can be used with safety inside a Handler\n// by `context.Application()`.\ntype Application interface {\n\t// ConfigurationReadOnly returns all the available configuration values can be used on a request.\n\tConfigurationReadOnly() ConfigurationReadOnly\n\n\t// Logger returns the golog logger instance(pointer) that is being used inside the \"app\".\n\tLogger() *golog.Logger\n\t// IsDebug reports whether the application is running\n\t// under debug/development mode.\n\t// It's just a shortcut of Logger().Level >= golog.DebugLevel.\n\t// The same method existss as Context.IsDebug() too.\n\tIsDebug() bool\n\n\t// I18nReadOnly returns the i18n's read-only features.\n\tI18nReadOnly() I18nReadOnly\n\n\t// Validate validates a value and returns nil if passed or\n\t// the failure reason if not.\n\tValidate(any) error\n\n\t// Minifier returns the minifier instance.\n\t// By default it can minifies:\n\t// - text/html\n\t// - text/css\n\t// - image/svg+xml\n\t// - application/text(javascript, ecmascript, json, xml).\n\t// Use that instance to add custom Minifiers before server ran.\n\tMinifier() *minify.M\n\t// View executes and write the result of a template file to the writer.\n\t//\n\t// Use context.View to render templates to the client instead.\n\t// Returns an error on failure, otherwise nil.\n\tView(writer io.Writer, filename string, layout string, bindingData any) error\n\n\t// GetContextPool returns the Iris sync.Pool which holds the contexts values.\n\t// Iris automatically releases the request context, so you don't have to use it.\n\t// It's only useful to manually release the context on cases that connection\n\t// is hijacked by a third-party middleware and the http handler return too fast.\n\tGetContextPool() *Pool\n\n\t// GetContextErrorHandler returns the handler which handles errors\n\t// on JSON write failures.\n\tGetContextErrorHandler() ErrorHandler\n\n\t// ServeHTTPC is the internal router, it's visible because it can be used for advanced use cases,\n\t// i.e: routing within a foreign context.\n\t//\n\t// It is ready to use after Build state.\n\tServeHTTPC(ctx *Context)\n\n\t// ServeHTTP is the main router handler which calls the .Serve and acquires a new context from the pool.\n\t//\n\t// It is ready to use after Build state.\n\tServeHTTP(w http.ResponseWriter, r *http.Request)\n\n\t// Shutdown gracefully terminates all the application's server hosts and any tunnels.\n\t// Returns an error on the first failure, otherwise nil.\n\tShutdown(ctx stdContext.Context) error\n\n\t// GetRouteReadOnly returns the registered \"read-only\" route based on its name, otherwise nil.\n\t// One note: \"routeName\" should be case-sensitive. Used by the context to get the current route.\n\t// It returns an interface instead to reduce wrong usage and to keep the decoupled design between\n\t// the context and the routes.\n\t//\n\t// Look core/router/APIBuilder#GetRoute for more.\n\tGetRouteReadOnly(routeName string) RouteReadOnly\n\n\t// GetRoutesReadOnly returns the registered \"read-only\" routes.\n\t//\n\t// Look core/router/APIBuilder#GetRoutes for more.\n\tGetRoutesReadOnly() []RouteReadOnly\n\n\t// FireErrorCode handles the response's error response.\n\t// If `Configuration.ResetOnFireErrorCode()` is true\n\t// and the response writer was a recorder or a gzip writer one\n\t// then it will try to reset the headers and the body before calling the\n\t// registered (or default) error handler for that error code set by\n\t// `ctx.StatusCode` method.\n\tFireErrorCode(ctx *Context)\n\n\t// RouteExists reports whether a particular route exists\n\t// It will search from the current subdomain of context's host, if not inside the root domain.\n\tRouteExists(ctx *Context, method, path string) bool\n\t// FindClosestPaths returns a list of \"n\" paths close to \"path\" under the given \"subdomain\".\n\t//\n\t// Order may change.\n\tFindClosestPaths(subdomain, searchPath string, n int) []string\n\n\t// String returns the Application's Name.\n\tString() string\n}\n\n// Notes(@kataras):\n// Alternative places...\n// 1. in apps/store, but it would require an empty `import _ \"....apps/store\"\n//\t  from end-developers, to avoid the import cycle and *iris.Application access.\n// 2. in root package level, that could be the best option, it has access to the *iris.Application\n// instead of the context.Application interface, but we try to keep the root package\n// as minimum as possible, however: if in the future, those Application instances\n// can be registered through network instead of same-process then we must think of that choice.\n// 3. this is the best possible place, as the root package and all subpackages\n// have access to this context package without import cycles and they already using it,\n// the only downside is that we don't have access to the *iris.Application instance\n// but this context.Application is designed that way that can execute all important methods\n// as the whole Iris code base is so well written.\n\nvar (\n\t// registerApps holds all the created iris Applications by this process.\n\t// It's slice instead of map because if IRIS_APP_NAME env var exists,\n\t// by-default all applications running on the same machine\n\t// will have the same name unless `Application.SetName` is called.\n\tregisteredApps                   []Application\n\tonApplicationRegisteredListeners []func(Application)\n\tmu                               sync.RWMutex\n)\n\n// RegisterApplication registers an application to the global shared storage.\nfunc RegisterApplication(app Application) {\n\tif app == nil {\n\t\treturn\n\t}\n\n\tmu.Lock()\n\tregisteredApps = append(registeredApps, app)\n\tmu.Unlock()\n\n\tmu.RLock()\n\tfor _, listener := range onApplicationRegisteredListeners {\n\t\tlistener(app)\n\t}\n\tmu.RUnlock()\n}\n\n// OnApplicationRegistered adds a function which fires when a new application\n// is registered.\nfunc OnApplicationRegistered(listeners ...func(app Application)) {\n\tmu.Lock()\n\tonApplicationRegisteredListeners = append(onApplicationRegisteredListeners, listeners...)\n\tmu.Unlock()\n}\n\n// GetApplications returns a slice of all the registered Applications.\nfunc GetApplications() []Application {\n\tmu.RLock()\n\t// a copy slice but the instances are pointers so be careful what modifications are done\n\t// the return value is read-only but it can be casted to *iris.Application.\n\tapps := make([]Application, 0, len(registeredApps))\n\tcopy(apps, registeredApps)\n\tmu.RUnlock()\n\n\treturn apps\n}\n\n// LastApplication returns the last registered Application.\n// Handlers has access to the current Application,\n// use `Context.Application()` instead.\nfunc LastApplication() Application {\n\tmu.RLock()\n\tfor i := len(registeredApps) - 1; i >= 0; i-- {\n\t\tif app := registeredApps[i]; app != nil {\n\t\t\tmu.RUnlock()\n\t\t\treturn app\n\t\t}\n\t}\n\tmu.RUnlock()\n\treturn nil\n}\n\n// GetApplication returns a registered Application\n// based on its name. If the \"appName\" is not unique\n// across Applications, then it will return the newest one.\nfunc GetApplication(appName string) (Application, bool) {\n\tmu.RLock()\n\n\tfor i := len(registeredApps) - 1; i >= 0; i-- {\n\t\tif app := registeredApps[i]; app != nil && app.String() == appName {\n\t\t\tmu.RUnlock()\n\t\t\treturn app, true\n\t\t}\n\t}\n\n\tmu.RUnlock()\n\treturn nil, false\n}\n\n// MustGetApplication same as `GetApplication` but it\n// panics if \"appName\" is not a registered Application's name.\nfunc MustGetApplication(appName string) Application {\n\tapp, ok := GetApplication(appName)\n\tif !ok || app == nil {\n\t\tpanic(appName + \" is not a registered Application\")\n\t}\n\n\treturn app\n}\n\n// DefaultLogger returns a Logger instance for an Iris module.\n// If the program contains at least one registered Iris Application\n// before this call then it will return a child of that Application's Logger\n// otherwise a fresh child of the `golog.Default` will be returned instead.\n//\n// It should be used when a module has no access to the Application or its Logger.\nfunc DefaultLogger(prefix string) (logger *golog.Logger) {\n\tif app := LastApplication(); app != nil {\n\t\tlogger = app.Logger()\n\t} else {\n\t\tlogger = golog.Default\n\t}\n\n\tlogger = logger.Child(prefix)\n\treturn\n}\n"
  },
  {
    "path": "context/compress.go",
    "content": "package context\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"sync\"\n\n\t\"github.com/andybalholm/brotli\"\n\t\"github.com/golang/snappy\"\n\t\"github.com/klauspost/compress/flate\"\n\t\"github.com/klauspost/compress/gzip\"\n\t\"github.com/klauspost/compress/s2\" // snappy output but likely faster decompression.\n)\n\n// The available builtin compression algorithms.\nconst (\n\tGZIP    = \"gzip\"\n\tDEFLATE = \"deflate\"\n\tBROTLI  = \"br\"\n\tSNAPPY  = \"snappy\"\n\tS2      = \"s2\"\n)\n\n// IDENTITY no transformation whatsoever.\nconst IDENTITY = \"identity\"\n\nvar (\n\t// ErrResponseNotCompressed returned from AcquireCompressResponseWriter\n\t// when response's Content-Type header is missing due to golang/go/issues/31753 or\n\t// when accept-encoding is empty. The caller should fallback to the original response writer.\n\tErrResponseNotCompressed = errors.New(\"compress: response will not be compressed\")\n\t// ErrRequestNotCompressed returned from NewCompressReader\n\t// when request is not compressed.\n\tErrRequestNotCompressed = errors.New(\"compress: request is not compressed\")\n\t// ErrNotSupportedCompression returned from\n\t// AcquireCompressResponseWriter, NewCompressWriter and NewCompressReader\n\t// when the request's Accept-Encoding was not found in the server's supported\n\t// compression algorithms. Check that error with `errors.Is`.\n\tErrNotSupportedCompression = errors.New(\"compress: unsupported compression\")\n)\n\n// AllEncodings is a slice of default content encodings.\n// See `AcquireCompressResponseWriter`.\nvar AllEncodings = []string{GZIP, DEFLATE, BROTLI, SNAPPY}\n\n// GetEncoding extracts the best available encoding from the request.\nfunc GetEncoding(r *http.Request, offers []string) (string, error) {\n\tacceptEncoding := r.Header[AcceptEncodingHeaderKey]\n\n\tif len(acceptEncoding) == 0 {\n\t\treturn \"\", ErrResponseNotCompressed\n\t}\n\n\tencoding := negotiateAcceptHeader(acceptEncoding, offers, IDENTITY)\n\tif encoding == \"\" {\n\t\treturn \"\", fmt.Errorf(\"%w: %s\", ErrNotSupportedCompression, encoding)\n\t}\n\n\treturn encoding, nil\n}\n\ntype (\n\tnoOpWriter struct{}\n\n\tnoOpReadCloser struct {\n\t\tio.Reader\n\t}\n)\n\nvar (\n\t_ io.ReadCloser = (*noOpReadCloser)(nil)\n\t_ io.Writer     = (*noOpWriter)(nil)\n)\n\nfunc (w *noOpWriter) Write(p []byte) (int, error) { return 0, nil }\n\nfunc (r *noOpReadCloser) Close() error {\n\treturn nil\n}\n\n// CompressWriter is an interface which all compress writers should implement.\ntype CompressWriter interface {\n\tio.WriteCloser\n\t// All known implementations contain `Flush` and `Reset`  methods,\n\t// so we wanna declare them upfront.\n\tFlush() error\n\tReset(io.Writer)\n}\n\n// NewCompressWriter returns a CompressWriter of \"w\" based on the given \"encoding\".\nfunc NewCompressWriter(w io.Writer, encoding string, level int) (cw CompressWriter, err error) {\n\tswitch encoding {\n\tcase GZIP:\n\t\tcw, err = gzip.NewWriterLevel(w, level)\n\tcase DEFLATE: // -1 default level, same for gzip.\n\t\tcw, err = flate.NewWriter(w, level)\n\tcase BROTLI: // 6 default level.\n\t\tif level == -1 {\n\t\t\tlevel = 6\n\t\t}\n\t\tcw = brotli.NewWriterLevel(w, level)\n\tcase SNAPPY:\n\t\tcw = snappy.NewBufferedWriter(w)\n\tcase S2:\n\t\tcw = s2.NewWriter(w)\n\tdefault:\n\t\t// Throw if \"identity\" is given. As this is not acceptable on \"Content-Encoding\" header.\n\t\t// Only Accept-Encoding (client) can use that; it means, no transformation whatsoever.\n\t\terr = ErrNotSupportedCompression\n\t}\n\n\treturn\n}\n\n// CompressReader is a structure which wraps a compressed reader.\n// It is used for determination across common request body and a compressed one.\ntype CompressReader struct {\n\tio.ReadCloser\n\n\t// We need this to reset the body to its original state, if requested.\n\tSrc io.ReadCloser\n\t// Encoding is the compression alogirthm is used to decompress and read the data.\n\tEncoding string\n}\n\n// NewCompressReader returns a new \"compressReader\" wrapper of \"src\".\n// It returns `ErrRequestNotCompressed` if client's request data are not compressed\n// or `ErrNotSupportedCompression` if server missing the decompression algorithm.\n// Note: on server-side the request body (src) will be closed automaticaly.\nfunc NewCompressReader(src io.Reader, encoding string) (*CompressReader, error) {\n\tif encoding == \"\" || src == nil {\n\t\treturn nil, ErrRequestNotCompressed\n\t}\n\n\tvar (\n\t\trc  io.ReadCloser\n\t\terr error\n\t)\n\n\tswitch encoding {\n\tcase GZIP:\n\t\trc, err = gzip.NewReader(src)\n\tcase DEFLATE:\n\t\trc = flate.NewReader(src)\n\tcase BROTLI:\n\t\trc = &noOpReadCloser{brotli.NewReader(src)}\n\tcase SNAPPY:\n\t\trc = &noOpReadCloser{snappy.NewReader(src)}\n\tcase S2:\n\t\trc = &noOpReadCloser{s2.NewReader(src)}\n\tdefault:\n\t\terr = ErrNotSupportedCompression\n\t}\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tsrcReadCloser, ok := src.(io.ReadCloser)\n\tif !ok {\n\t\tsrcReadCloser = &noOpReadCloser{src}\n\t}\n\n\treturn &CompressReader{\n\t\tReadCloser: rc,\n\t\tSrc:        srcReadCloser,\n\t\tEncoding:   encoding,\n\t}, nil\n}\n\nvar compressWritersPool = sync.Pool{New: func() any { return &CompressResponseWriter{} }}\n\n// AddCompressHeaders just adds the headers \"Vary\" to \"Accept-Encoding\"\n// and \"Content-Encoding\" to the given encoding.\nfunc AddCompressHeaders(h http.Header, encoding string) {\n\th.Set(VaryHeaderKey, AcceptEncodingHeaderKey)\n\th.Set(ContentEncodingHeaderKey, encoding)\n}\n\n// CompressResponseWriter is a compressed data http.ResponseWriter.\ntype CompressResponseWriter struct {\n\tCompressWriter\n\tResponseWriter\n\n\thttp.Hijacker\n\n\tDisabled bool\n\tEncoding string\n\tLevel    int\n}\n\nvar _ ResponseWriter = (*CompressResponseWriter)(nil)\n\n// AcquireCompressResponseWriter returns a CompressResponseWriter from the pool.\n// It accepts an Iris response writer, a net/http request value and\n// the level of compression (use -1 for default compression level).\n//\n// It returns the best candidate among \"gzip\", \"defate\", \"br\", \"snappy\" and \"s2\"\n// based on the request's \"Accept-Encoding\" header value.\nfunc AcquireCompressResponseWriter(w ResponseWriter, r *http.Request, level int) (*CompressResponseWriter, error) {\n\tencoding, err := GetEncoding(r, AllEncodings)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tv := compressWritersPool.Get().(*CompressResponseWriter)\n\n\tif h, ok := w.(http.Hijacker); ok {\n\t\tv.Hijacker = h\n\t} else {\n\t\tv.Hijacker = nil\n\t}\n\n\t// The Naive() should be used to check for Pusher,\n\t// as examples explicitly says, so don't do it:\n\t// if p, ok := w.Naive().(http.Pusher); ok {\n\t// \tv.Pusher = p\n\t// } else {\n\t// \tv.Pusher = nil\n\t// }\n\n\tv.ResponseWriter = w\n\tv.Disabled = false\n\tif level == -1 && encoding == BROTLI {\n\t\tlevel = 6\n\t}\n\n\t/*\n\t\t// Writer exists, encoding matching and it's valid because it has a non nil encWriter;\n\t\t// just reset to reduce allocations.\n\t\tif v.Encoding == encoding && v.Level == level && v.CompressWriter != nil {\n\t\t\tv.CompressWriter.Reset(w)\n\t\t\treturn v, nil\n\t\t}\n\t*/\n\n\tv.Encoding = encoding\n\n\tv.Level = level\n\tencWriter, err := NewCompressWriter(w, encoding, level)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tv.CompressWriter = encWriter\n\n\tAddCompressHeaders(w.Header(), encoding)\n\treturn v, nil\n}\n\nfunc releaseCompressResponseWriter(w *CompressResponseWriter) {\n\tcompressWritersPool.Put(w)\n}\n\n// FlushResponse flushes any data, closes the underline compress writer\n// and writes the status code.\n// Called automatically before `EndResponse`.\nfunc (w *CompressResponseWriter) FlushResponse() {\n\tw.FlushHeaders()\n\n\t/* this should NEVER happen, see `context.CompressWriter` method.\n\tif rec, ok := w.ResponseWriter.(*ResponseRecorder); ok {\n\t\t// Usecase: record, then compression.\n\t\tw.CompressWriter.Close() // flushes and closes.\n\t\trec.FlushResponse()\n\t\treturn\n\t}\n\t*/\n\n\t// write the status, after header set and before any flushed content sent.\n\tw.ResponseWriter.FlushResponse()\n\n\tif w.IsHijacked() {\n\t\t// net/http docs:\n\t\t// It becomes the caller's responsibility to manage\n\t\t// and close the connection.\n\t\treturn\n\t}\n\n\tw.CompressWriter.Close() // flushes and closes.\n}\n\n// FlushHeaders deletes the encoding headers if\n// the compressed writer was disabled otherwise\n// removes the content-length so next callers can re-calculate the correct length.\nfunc (w *CompressResponseWriter) FlushHeaders() {\n\tif w.Disabled {\n\t\tw.Header().Del(VaryHeaderKey)\n\t\tw.Header().Del(ContentEncodingHeaderKey)\n\t\tw.CompressWriter.Reset(&noOpWriter{})\n\t} else {\n\t\tw.ResponseWriter.Header().Del(ContentLengthHeaderKey)\n\t}\n}\n\n// EndResponse reeases the writers.\nfunc (w *CompressResponseWriter) EndResponse() {\n\tw.ResponseWriter.EndResponse()\n\treleaseCompressResponseWriter(w)\n}\n\nfunc (w *CompressResponseWriter) Write(p []byte) (int, error) {\n\tif w.Disabled {\n\t\t// If disabled or the content-type is empty the response will not be compressed (golang/go/issues/31753).\n\t\treturn w.ResponseWriter.Write(p)\n\t}\n\n\tif w.Header().Get(ContentTypeHeaderKey) == \"\" {\n\t\tw.Header().Set(ContentTypeHeaderKey, http.DetectContentType(p))\n\t}\n\n\treturn w.CompressWriter.Write(p)\n}\n\n// Flush sends any buffered data to the client.\n// Can be called manually.\nfunc (w *CompressResponseWriter) Flush() {\n\t// if w.Disabled {\n\t// \tw.Header().Del(VaryHeaderKey)\n\t// \tw.Header().Del(ContentEncodingHeaderKey)\n\t// } else {\n\t// \tw.encWriter.Flush()\n\t// }\n\n\tif !w.Disabled {\n\t\tw.CompressWriter.Flush()\n\t}\n\n\tw.ResponseWriter.Flush()\n}\n\n// WriteTo writes the \"p\" to \"dest\" Writer using the compression that this compress writer was made of.\nfunc (w *CompressResponseWriter) WriteTo(dest io.Writer, p []byte) (int, error) {\n\tif w.Disabled {\n\t\treturn dest.Write(p)\n\t}\n\n\tcw, err := NewCompressWriter(dest, w.Encoding, w.Level)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tn, err := cw.Write(p)\n\tcw.Close()\n\treturn n, err\n}\n\n// Reset implements the ResponseWriterReseter interface.\nfunc (w *CompressResponseWriter) Reset() bool {\n\tif w.Disabled {\n\t\t// If it's disabled then the underline one is responsible.\n\t\trs, ok := w.ResponseWriter.(ResponseWriterReseter)\n\t\treturn ok && rs.Reset()\n\t}\n\n\tw.CompressWriter.Reset(w.ResponseWriter)\n\treturn true\n}\n"
  },
  {
    "path": "context/configuration.go",
    "content": "package context\n\nimport (\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/core/netutil\"\n)\n\n// ConfigurationReadOnly can be implemented\n// by Configuration, it's being used inside the Context.\n// All methods that it contains should be \"safe\" to be called by the context\n// at \"serve time\". A configuration field may be missing when it's not\n// safe or its useless to be called from a request handler.\ntype ConfigurationReadOnly interface {\n\t// GetVHost returns the non-exported vhost config field.\n\tGetVHost() string\n\t// GetLogLevel returns the LogLevel field.\n\tGetLogLevel() string\n\t// GetSocketSharding returns the SocketSharding field.\n\tGetSocketSharding() bool\n\t// GetKeepAlive returns the KeepAlive field.\n\tGetKeepAlive() time.Duration\n\t// GetTimeout returns the Timeout field.\n\tGetTimeout() time.Duration\n\t// GetTimeoutMessage returns the TimeoutMessage field.\n\tGetTimeoutMessage() string\n\t// GetNonBlocking returns the NonBlocking field.\n\tGetNonBlocking() bool\n\t// GetDisablePathCorrection returns the DisablePathCorrection field\n\tGetDisablePathCorrection() bool\n\t// GetDisablePathCorrectionRedirection returns the DisablePathCorrectionRedirection field.\n\tGetDisablePathCorrectionRedirection() bool\n\t// GetEnablePathIntelligence returns the EnablePathIntelligence field.\n\tGetEnablePathIntelligence() bool\n\t// GetEnablePathEscape returns the EnablePathEscape field.\n\tGetEnablePathEscape() bool\n\t// GetForceLowercaseRouting returns the ForceLowercaseRouting field.\n\tGetForceLowercaseRouting() bool\n\t// GetEnableOptimizations returns the EnableDynamicHandler field.\n\tGetEnableDynamicHandler() bool\n\t// GetFireMethodNotAllowed returns the FireMethodNotAllowed field.\n\tGetFireMethodNotAllowed() bool\n\t// GetDisableAutoFireStatusCode returns the DisableAutoFireStatusCode field.\n\tGetDisableAutoFireStatusCode() bool\n\t// GetResetOnFireErrorCode returns the ResetOnFireErrorCode field.\n\tGetResetOnFireErrorCode() bool\n\t// GetURLParamSeparator returns URLParamSeparator field.\n\tGetURLParamSeparator() *string\n\t// GetEnableOptimizations returns the EnableOptimizations field.\n\tGetEnableOptimizations() bool\n\t// GetEnableProtoJSON returns the EnableProtoJSON field.\n\tGetEnableProtoJSON() bool\n\t// GetEnableEasyJSON returns the EnableEasyJSON field.\n\tGetEnableEasyJSON() bool\n\n\t// GetDisableBodyConsumptionOnUnmarshal returns the DisableBodyConsumptionOnUnmarshal field.\n\tGetDisableBodyConsumptionOnUnmarshal() bool\n\t// GetFireEmptyFormError returns the FireEmptyFormError field.\n\tGetFireEmptyFormError() bool\n\n\t// GetTimeFormat returns the TimeFormat field.\n\tGetTimeFormat() string\n\t// GetCharset returns the Charset field.\n\tGetCharset() string\n\t// GetPostMaxMemory returns the PostMaxMemory field.\n\tGetPostMaxMemory() int64\n\n\t// GetLocaleContextKey returns the LocaleContextKey field.\n\tGetLocaleContextKey() string\n\t// GetLanguageContextKey returns the LanguageContextKey field.\n\tGetLanguageContextKey() string\n\t// GetLanguageInputContextKey returns the LanguageInputContextKey field.\n\tGetLanguageInputContextKey() string\n\t// GetVersionContextKey returns the VersionContextKey field.\n\tGetVersionContextKey() string\n\t// GetVersionAliasesContextKey returns the VersionAliasesContextKey field.\n\tGetVersionAliasesContextKey() string\n\n\t// GetViewEngineContextKey returns the ViewEngineContextKey field.\n\tGetViewEngineContextKey() string\n\t// GetViewLayoutContextKey returns the ViewLayoutContextKey field.\n\tGetViewLayoutContextKey() string\n\t// GetViewDataContextKey returns the ViewDataContextKey field.\n\tGetViewDataContextKey() string\n\t// GetFallbackViewContextKey returns the FallbackViewContextKey field.\n\tGetFallbackViewContextKey() string\n\n\t// GetRemoteAddrHeaders returns RemoteAddrHeaders field.\n\tGetRemoteAddrHeaders() []string\n\t// GetRemoteAddrHeadersForce returns RemoteAddrHeadersForce field.\n\tGetRemoteAddrHeadersForce() bool\n\t// GetRemoteAddrPrivateSubnets returns the RemoteAddrPrivateSubnets field.\n\tGetRemoteAddrPrivateSubnets() []netutil.IPRange\n\t// GetSSLProxyHeaders returns the SSLProxyHeaders field.\n\tGetSSLProxyHeaders() map[string]string\n\t// GetHostProxyHeaders returns the HostProxyHeaders field.\n\tGetHostProxyHeaders() map[string]bool\n\t// GetOther returns the Other field.\n\tGetOther() map[string]any\n}\n"
  },
  {
    "path": "context/context.go",
    "content": "package context\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"encoding/xml\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"mime\"\n\t\"mime/multipart\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"regexp\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync/atomic\"\n\t\"time\"\n\t\"unsafe\"\n\n\t\"github.com/kataras/iris/v12/core/memstore\"\n\t\"github.com/kataras/iris/v12/core/netutil\"\n\n\t\"github.com/Shopify/goreferrer\"\n\t\"github.com/fatih/structs\"\n\t\"github.com/gomarkdown/markdown\"\n\t\"github.com/gomarkdown/markdown/html\"\n\t\"github.com/iris-contrib/schema\"\n\t\"github.com/mailru/easyjson\"\n\t\"github.com/mailru/easyjson/jwriter\"\n\t\"github.com/microcosm-cc/bluemonday\"\n\t\"github.com/vmihailenco/msgpack/v5\"\n\t\"golang.org/x/net/publicsuffix\"\n\t\"golang.org/x/time/rate\"\n\t\"google.golang.org/protobuf/encoding/protojson\"\n\t\"google.golang.org/protobuf/proto\"\n\t\"gopkg.in/yaml.v3\"\n)\n\nvar (\n\t// BuildRevision holds the vcs commit id information of the program's build.\n\t// Available at go version 1.18+\n\tBuildRevision string\n\t// BuildTime holds the vcs commit time information of the program's build.\n\t// Available at go version 1.18+\n\tBuildTime string\n)\n\ntype (\n\t// BodyDecoder is an interface which any struct can implement in order to customize the decode action\n\t// from ReadJSON and ReadXML\n\t//\n\t// Trivial example of this could be:\n\t// type User struct { Username string }\n\t//\n\t// func (u *User) Decode(data []byte) error {\n\t//\t  return json.Unmarshal(data, u)\n\t// }\n\t//\n\t// the 'Context.ReadJSON/ReadXML(&User{})' will call the User's\n\t// Decode option to decode the request body\n\t//\n\t// Note: This is totally optionally, the default decoders\n\t// for ReadJSON is the encoding/json and for ReadXML is the encoding/xml.\n\t//\n\t// Example: https://github.com/kataras/iris/blob/main/_examples/request-body/read-custom-per-type/main.go\n\tBodyDecoder interface {\n\t\tDecode(data []byte) error\n\t}\n\n\t// BodyDecoderWithContext same as BodyDecoder but it can accept a standard context,\n\t// which is binded to the HTTP request's context.\n\tBodyDecoderWithContext interface {\n\t\tDecodeContext(ctx context.Context, data []byte) error\n\t}\n\n\t// Unmarshaler is the interface implemented by types that can unmarshal any raw data.\n\t// TIP INFO: Any pointer to a value which implements the BodyDecoder can be override the unmarshaler.\n\tUnmarshaler interface {\n\t\tUnmarshal(data []byte, outPtr any) error\n\t}\n\n\t// UnmarshalerFunc a shortcut for the Unmarshaler interface\n\t//\n\t// See 'Unmarshaler' and 'BodyDecoder' for more.\n\t//\n\t// Example: https://github.com/kataras/iris/blob/main/_examples/request-body/read-custom-via-unmarshaler/main.go\n\tUnmarshalerFunc func(data []byte, outPtr any) error\n\n\t// DecodeFunc is a generic type of decoder function.\n\t// When the returned error is not nil the decode operation\n\t// is terminated and the error is received by the ReadJSONStream method,\n\t// otherwise it continues to read the next available object.\n\t// Look the `Context.ReadJSONStream` method.\n\tDecodeFunc func(outPtr any) error\n)\n\n// Unmarshal parses the X-encoded data and stores the result in the value pointed to by v.\n// Unmarshal uses the inverse of the encodings that Marshal uses, allocating maps,\n// slices, and pointers as necessary.\nfunc (u UnmarshalerFunc) Unmarshal(data []byte, v any) error {\n\treturn u(data, v)\n}\n\n// LimitRequestBodySize is a middleware which sets a request body size limit\n// for all next handlers in the chain.\nvar LimitRequestBodySize = func(maxRequestBodySizeBytes int64) Handler {\n\treturn func(ctx *Context) {\n\t\tctx.SetMaxRequestBodySize(maxRequestBodySizeBytes)\n\t\tctx.Next()\n\t}\n}\n\n// Map is just a type alias of the map[string]any type.\ntype Map = map[string]any\n\n// Context is the midle-man server's \"object\" dealing with incoming requests.\n//\n// A New context is being acquired from a sync.Pool on each connection.\n// The Context is the most important thing on the iris's http flow.\n//\n// Developers send responses to the client's request through a Context.\n// Developers get request information from the client's request a Context.\ntype Context struct {\n\t// the http.ResponseWriter wrapped by custom writer.\n\twriter ResponseWriter\n\t// the original http.Request\n\trequest *http.Request\n\t// the current route registered to this request path.\n\tcurrentRoute RouteReadOnly\n\n\t// the local key-value storage\n\tparams RequestParams  // url named parameters.\n\tvalues memstore.Store // generic storage, middleware communication.\n\tquery  url.Values     // GET url query temp cache, useful on many URLParamXXX calls.\n\t// the underline application app.\n\tapp Application\n\t// the route's handlers\n\thandlers Handlers\n\t// the current position of the handler's chain\n\tcurrentHandlerIndex int\n\t// proceeded reports whether `Proceed` method\n\t// called before a `Next`. It is a flash field and it is set\n\t// to true on `Next` call when its called on the last handler in the chain.\n\t// Reports whether a `Next` is called,\n\t// even if the handler index remains the same (last handler).\n\t//\n\t// Also it's responsible to keep the old value of the last known handler index\n\t// before StopExecution. See ResumeExecution.\n\tproceeded int\n\n\t// if true, caller is responsible to release the context (put the context to the pool).\n\tmanualRelease bool\n}\n\n// NewContext returns a new Context instance.\nfunc NewContext(app Application) *Context {\n\treturn &Context{app: app}\n}\n\n/* Not required, unless requested.\n// SetApplication sets an Iris Application on-fly.\n// Do NOT use it after ServeHTTPC is fired.\nfunc (ctx *Context) SetApplication(app Application) {\n\tctx.app = app\n}\n*/\n\n// Clone returns a copy of the context that\n// can be safely used outside the request's scope.\n// Note that if the request-response lifecycle terminated\n// or request canceled by the client (can be checked by `ctx.IsCanceled()`)\n// then the response writer is totally useless.\n// The http.Request pointer value is shared.\nfunc (ctx *Context) Clone() *Context {\n\tvaluesCopy := make(memstore.Store, len(ctx.values))\n\tcopy(valuesCopy, ctx.values)\n\n\tparamsCopy := make(memstore.Store, len(ctx.params.Store))\n\tcopy(paramsCopy, ctx.params.Store)\n\n\tqueryCopy := make(url.Values, len(ctx.query))\n\tfor k, v := range ctx.query {\n\t\tqueryCopy[k] = v\n\t}\n\n\treq := ctx.request.Clone(ctx.request.Context())\n\treturn &Context{\n\t\tapp:                 ctx.app,\n\t\tvalues:              valuesCopy,\n\t\tparams:              RequestParams{Store: paramsCopy},\n\t\tquery:               queryCopy,\n\t\twriter:              ctx.writer.Clone(),\n\t\trequest:             req,\n\t\tcurrentHandlerIndex: stopExecutionIndex,\n\t\tproceeded:           ctx.proceeded,\n\t\tmanualRelease:       ctx.manualRelease,\n\t\tcurrentRoute:        ctx.currentRoute,\n\t}\n}\n\n// BeginRequest is executing once for each request\n// it should prepare the (new or acquired from pool) context's fields for the new request.\n// Do NOT call it manually. Framework calls it automatically.\n//\n// Resets\n// 1. handlers to nil.\n// 2. values to empty.\n// 3. the defer function.\n// 4. response writer to the http.ResponseWriter.\n// 5. request to the *http.Request.\nfunc (ctx *Context) BeginRequest(w http.ResponseWriter, r *http.Request) {\n\tctx.currentRoute = nil\n\tctx.handlers = nil           // will be filled by router.Serve/HTTP\n\tctx.values = ctx.values[0:0] // >>      >>     by context.Values().Set\n\tctx.params.Store = ctx.params.Store[0:0]\n\tctx.query = nil\n\tctx.request = r\n\tctx.currentHandlerIndex = 0\n\tctx.proceeded = 0\n\tctx.manualRelease = false\n\tctx.writer = AcquireResponseWriter()\n\tctx.writer.BeginResponse(w)\n}\n\n// EndRequest is executing once after a response to the request was sent and this context is useless or released.\n// Do NOT call it manually. Framework calls it automatically.\n//\n// 1. executes the OnClose function (if any).\n// 2. flushes the response writer's result or fire any error handler.\n// 3. releases the response writer.\nfunc (ctx *Context) EndRequest() {\n\tif !ctx.app.ConfigurationReadOnly().GetDisableAutoFireStatusCode() &&\n\t\tStatusCodeNotSuccessful(ctx.GetStatusCode()) {\n\t\tctx.app.FireErrorCode(ctx)\n\t}\n\n\tctx.writer.FlushResponse()\n\tctx.writer.EndResponse()\n}\n\n// DisablePoolRelease disables the auto context pool Put call.\n// Do NOT use it, unless you know what you are doing.\nfunc (ctx *Context) DisablePoolRelease() {\n\tctx.manualRelease = true\n}\n\n// IsCanceled reports whether the client canceled the request\n// or the underlying connection has gone.\n// Note that it will always return true\n// when called from a goroutine after the request-response lifecycle.\nfunc (ctx *Context) IsCanceled() bool {\n\tvar err error\n\tif reqCtx := ctx.request.Context(); reqCtx != nil {\n\t\terr = reqCtx.Err()\n\t} else {\n\t\terr = ctx.GetErr()\n\t}\n\n\treturn IsErrCanceled(err)\n}\n\n// IsErrCanceled reports whether the \"err\" is caused by a cancellation or timeout.\nfunc IsErrCanceled(err error) bool {\n\tif err == nil {\n\t\treturn false\n\t}\n\n\tvar netErr net.Error\n\treturn (errors.As(err, &netErr) && netErr.Timeout()) ||\n\t\terrors.Is(err, context.Canceled) ||\n\t\terrors.Is(err, context.DeadlineExceeded) ||\n\t\terrors.Is(err, http.ErrHandlerTimeout) ||\n\t\terr.Error() == \"closed pool\"\n}\n\n// OnConnectionClose registers the \"cb\" Handler\n// which will be fired on its on goroutine on a cloned Context\n// when the underlying connection has gone away.\n//\n// The code inside the given callback is running on its own routine,\n// as explained above, therefore the callback should NOT\n// try to access to handler's Context response writer.\n//\n// This mechanism can be used to cancel long operations on the server\n// if the client has disconnected before the response is ready.\n//\n// It depends on the Request's Context.Done() channel.\n//\n// Finally, it reports whether the protocol supports pipelines (HTTP/1.1 with pipelines disabled is not supported).\n// The \"cb\" will not fire for sure if the output value is false.\n//\n// Note that you can register only one callback per route.\n//\n// See `OnClose` too.\nfunc (ctx *Context) OnConnectionClose(cb Handler) bool {\n\tif cb == nil {\n\t\treturn false\n\t}\n\n\treqCtx := ctx.Request().Context()\n\tif reqCtx == nil {\n\t\treturn false\n\t}\n\n\tnotifyClose := reqCtx.Done()\n\tif notifyClose == nil {\n\t\treturn false\n\t}\n\n\tgo func() {\n\t\t<-notifyClose\n\t\t// Note(@kataras): No need to clone if not canceled,\n\t\t// EndRequest will be called on the end of the handler chain,\n\t\t// no matter the cancelation.\n\t\t// therefore the context will still be there.\n\t\tcb(ctx.Clone())\n\t}()\n\n\treturn true\n}\n\n// OnConnectionCloseErr same as `OnConnectionClose` but instead it\n// receives a function which returns an error.\n// If error is not nil, it will be logged as a debug message.\nfunc (ctx *Context) OnConnectionCloseErr(cb func() error) bool {\n\tif cb == nil {\n\t\treturn false\n\t}\n\n\treqCtx := ctx.Request().Context()\n\tif reqCtx == nil {\n\t\treturn false\n\t}\n\n\tnotifyClose := reqCtx.Done()\n\tif notifyClose == nil {\n\t\treturn false\n\t}\n\n\tgo func() {\n\t\t<-notifyClose\n\t\tif err := cb(); err != nil {\n\t\t\t// Can be ignored.\n\t\t\tctx.app.Logger().Debugf(\"OnConnectionCloseErr: received error: %v\", err)\n\t\t}\n\t}()\n\n\treturn true\n}\n\n// OnClose registers a callback which\n// will be fired when the underlying connection has gone away(request canceled)\n// on its own goroutine or in the end of the request-response lifecylce\n// on the handler's routine itself (Context access).\n//\n// See `OnConnectionClose` too.\nfunc (ctx *Context) OnClose(cb Handler) {\n\tif cb == nil {\n\t\treturn\n\t}\n\n\t// Note(@kataras):\n\t// - on normal request-response lifecycle\n\t// the `SetBeforeFlush` will be called first\n\t// and then `OnConnectionClose`,\n\t// - when request was canceled before handler finish its job\n\t// then the `OnConnectionClose` will be called first instead,\n\t// and when the handler function completed then `SetBeforeFlush` is fired.\n\t// These are synchronized, they cannot be executed the same exact time,\n\t// below we just make sure the \"cb\" is executed once\n\t// by simple boolean check or an atomic one.\n\tvar executed uint32\n\n\tcallback := func(ctx *Context) {\n\t\tif atomic.CompareAndSwapUint32(&executed, 0, 1) {\n\t\t\tcb(ctx)\n\t\t}\n\t}\n\n\tctx.OnConnectionClose(callback)\n\n\tonFlush := func() {\n\t\tcallback(ctx)\n\t}\n\n\tctx.writer.SetBeforeFlush(onFlush)\n}\n\n// OnCloseErr same as `OnClose` but instead it\n// receives a function which returns an error.\n// If error is not nil, it will be logged as a debug message.\nfunc (ctx *Context) OnCloseErr(cb func() error) {\n\tif cb == nil {\n\t\treturn\n\t}\n\n\tvar executed uint32\n\n\tcallback := func() error {\n\t\tif atomic.CompareAndSwapUint32(&executed, 0, 1) {\n\t\t\treturn cb()\n\t\t}\n\n\t\treturn nil\n\t}\n\n\tctx.OnConnectionCloseErr(callback)\n\n\tonFlush := func() {\n\t\tif err := callback(); err != nil {\n\t\t\t// Can be ignored.\n\t\t\tctx.app.Logger().Debugf(\"OnClose: SetBeforeFlush: received error: %v\", err)\n\t\t}\n\t}\n\n\tctx.writer.SetBeforeFlush(onFlush)\n}\n\n/* Note(@kataras): just leave end-developer decide.\nconst goroutinesContextKey = \"iris.goroutines\"\n\ntype goroutines struct {\n\twg     *sync.WaitGroup\n\tlength int\n\tmu     sync.RWMutex\n}\n\nvar acquireGoroutines = func() any {\n\treturn &goroutines{wg: new(sync.WaitGroup)}\n}\n\nfunc (ctx *Context) Go(fn func(cancelCtx context.Context)) (running int) {\n\tg := ctx.values.GetOrSet(goroutinesContextKey, acquireGoroutines).(*goroutines)\n\tif fn != nil {\n\t\tg.wg.Add(1)\n\n\t\tg.mu.Lock()\n\t\tg.length++\n\t\tg.mu.Unlock()\n\n\t\tctx.waitFunc = g.wg.Wait\n\n\t\tgo func(reqCtx context.Context) {\n\t\t\tfn(reqCtx)\n\t\t\tg.wg.Done()\n\n\t\t\tg.mu.Lock()\n\t\t\tg.length--\n\t\t\tg.mu.Unlock()\n\t\t}(ctx.request.Context())\n\t}\n\n\tg.mu.RLock()\n\trunning = g.length\n\tg.mu.RUnlock()\n\treturn\n}\n*/\n\n// ResponseWriter returns an http.ResponseWriter compatible response writer, as expected.\nfunc (ctx *Context) ResponseWriter() ResponseWriter {\n\treturn ctx.writer\n}\n\n// ResetResponseWriter sets a new ResponseWriter implementation\n// to this Context to use as its writer.\n// Note, to change the underline http.ResponseWriter use\n// ctx.ResponseWriter().SetWriter(http.ResponseWriter) instead.\nfunc (ctx *Context) ResetResponseWriter(newResponseWriter ResponseWriter) {\n\tif rec, ok := ctx.IsRecording(); ok {\n\t\treleaseResponseRecorder(rec)\n\t}\n\n\tctx.writer = newResponseWriter\n}\n\n// Request returns the original *http.Request, as expected.\nfunc (ctx *Context) Request() *http.Request {\n\treturn ctx.request\n}\n\n// ResetRequest sets the Context's Request,\n// It is useful to store the new request created by a std *http.Request#WithContext() into Iris' Context.\n// Use `ResetRequest` when for some reason you want to make a full\n// override of the *http.Request.\n// Note that: when you just want to change one of each fields you can use the Request() which returns a pointer to Request,\n// so the changes will have affect without a full override.\n// Usage: you use a native http handler which uses the standard \"context\" package\n// to get values instead of the Iris' Context#Values():\n// r := ctx.Request()\n// stdCtx := context.WithValue(r.Context(), key, val)\n// ctx.ResetRequest(r.WithContext(stdCtx)).\nfunc (ctx *Context) ResetRequest(r *http.Request) {\n\tctx.request = r\n}\n\n// SetCurrentRoute sets the route internally,\n// See `GetCurrentRoute()` method too.\n// It's being initialized by the Router.\n// See `Exec` or `SetHandlers/AddHandler` methods to simulate a request.\nfunc (ctx *Context) SetCurrentRoute(route RouteReadOnly) {\n\tctx.currentRoute = route\n}\n\n// GetCurrentRoute returns the current \"read-only\" route that\n// was registered to this request's path.\nfunc (ctx *Context) GetCurrentRoute() RouteReadOnly {\n\treturn ctx.currentRoute\n}\n\n// Do sets the \"handlers\" as the chain\n// and executes the first handler,\n// handlers should not be empty.\n//\n// It's used by the router, developers may use that\n// to replace and execute handlers immediately.\nfunc (ctx *Context) Do(handlers Handlers) {\n\tif len(handlers) == 0 {\n\t\treturn\n\t}\n\n\tctx.handlers = handlers\n\thandlers[0](ctx)\n}\n\n// AddHandler can add handler(s)\n// to the current request in serve-time,\n// these handlers are not persistenced to the router.\n//\n// Router is calling this function to add the route's handler.\n// If AddHandler called then the handlers will be inserted\n// to the end of the already-defined route's handler.\nfunc (ctx *Context) AddHandler(handlers ...Handler) {\n\tctx.handlers = append(ctx.handlers, handlers...)\n}\n\n// SetHandlers replaces all handlers with the new.\nfunc (ctx *Context) SetHandlers(handlers Handlers) {\n\tctx.handlers = handlers\n}\n\n// Handlers keeps tracking of the current handlers.\nfunc (ctx *Context) Handlers() Handlers {\n\treturn ctx.handlers\n}\n\n// HandlerIndex sets the current index of the\n// current context's handlers chain.\n// If n < 0 or the current handlers length is 0 then it just returns the\n// current handler index without change the current index.\n//\n// Look Handlers(), Next() and StopExecution() too.\nfunc (ctx *Context) HandlerIndex(n int) (currentIndex int) {\n\tif n < 0 || n > len(ctx.handlers)-1 {\n\t\treturn ctx.currentHandlerIndex\n\t}\n\n\tctx.currentHandlerIndex = n\n\treturn n\n}\n\n// Proceed is an alternative way to check if a particular handler\n// has been executed.\n// The given \"h\" Handler can report a failure with `StopXXX` methods\n// or ignore calling a `Next` (see `iris.ExecutionRules` too).\n//\n// This is useful only when you run a handler inside\n// another handler. It justs checks for before index and the after index.\n//\n// A usecase example is when you want to execute a middleware\n// inside controller's `BeginRequest` that calls the `ctx.Next` inside it.\n// The Controller looks the whole flow (BeginRequest, method handler, EndRequest)\n// as one handler, so `ctx.Next` will not be reflected to the method handler\n// if called from the `BeginRequest`.\n//\n// Although `BeginRequest` should NOT be used to call other handlers,\n// the `BeginRequest` has been introduced to be able to set\n// common data to all method handlers before their execution.\n// Controllers can accept middleware(s) from the MVC's Application's Router as normally.\n//\n// That said let's see an example of `ctx.Proceed`:\n//\n//\tvar authMiddleware = basicauth.New(basicauth.Config{\n//\t\tUsers: map[string]string{\n//\t\t\t\"admin\": \"password\",\n//\t\t},\n//\t})\n//\n//\tfunc (c *UsersController) BeginRequest(ctx iris.Context) {\n//\t\tif !ctx.Proceed(authMiddleware) {\n//\t\t\tctx.StopExecution()\n//\t\t}\n//\t}\n//\n// This Get() will be executed in the same handler as `BeginRequest`,\n// internally controller checks for `ctx.StopExecution`.\n// So it will not be fired if BeginRequest called the `StopExecution`.\n//\n//\tfunc(c *UsersController) Get() []models.User {\n//\t\t  return c.Service.GetAll()\n//\t}\n//\n// Alternative way is `!ctx.IsStopped()` if middleware make use of the `ctx.StopExecution()` on failure.\nfunc (ctx *Context) Proceed(h Handler) bool {\n\t_, ok := ctx.ProceedAndReportIfStopped(h)\n\treturn ok\n}\n\n// ProceedAndReportIfStopped same as \"Proceed\" method\n// but the first output parameter reports whether the \"h\"\n// called \"StopExecution\" manually.\nfunc (ctx *Context) ProceedAndReportIfStopped(h Handler) (bool, bool) {\n\tctx.proceeded = internalPauseExecutionIndex\n\n\t// Store the current index.\n\tbeforeIdx := ctx.currentHandlerIndex\n\th(ctx)\n\t// Retrieve the next one, if Next is called this is beforeIdx + 1 and so on.\n\tafterIdx := ctx.currentHandlerIndex\n\t// Restore prev index, no matter what.\n\tctx.currentHandlerIndex = beforeIdx\n\n\tproceededByNext := ctx.proceeded == internalProceededHandlerIndex\n\tctx.proceeded = beforeIdx\n\n\t// Stop called, return false but keep the handlers index.\n\tif afterIdx == stopExecutionIndex {\n\t\treturn true, false\n\t}\n\n\tif proceededByNext {\n\t\treturn false, true\n\t}\n\n\t// Next called or not.\n\treturn false, afterIdx > beforeIdx\n}\n\n// HandlerName returns the current handler's name, helpful for debugging.\nfunc (ctx *Context) HandlerName() string {\n\treturn HandlerName(ctx.handlers[ctx.currentHandlerIndex])\n}\n\n// HandlerFileLine returns the current running handler's function source file and line information.\n// Useful mostly when debugging.\nfunc (ctx *Context) HandlerFileLine() (file string, line int) {\n\treturn HandlerFileLine(ctx.handlers[ctx.currentHandlerIndex])\n}\n\n// RouteName returns the route name that this handler is running on.\n// Note that it may return empty on not found handlers.\nfunc (ctx *Context) RouteName() string {\n\tif ctx.currentRoute == nil {\n\t\treturn \"\"\n\t}\n\n\treturn ctx.currentRoute.Name()\n}\n\n// Next calls the next handler from the handlers chain,\n// it should be used inside a middleware.\nfunc (ctx *Context) Next() {\n\tif ctx.IsStopped() {\n\t\treturn\n\t}\n\n\tif ctx.proceeded <= internalPauseExecutionIndex /* pause and proceeded */ {\n\t\tctx.proceeded = internalProceededHandlerIndex\n\t\treturn\n\t}\n\n\tnextIndex, n := ctx.currentHandlerIndex+1, len(ctx.handlers)\n\tif nextIndex < n {\n\t\tctx.currentHandlerIndex = nextIndex\n\t\tctx.handlers[nextIndex](ctx)\n\t}\n}\n\n// NextOr checks if chain has a next handler, if so then it executes it\n// otherwise it sets a new chain assigned to this Context based on the given handler(s)\n// and executes its first handler.\n//\n// Returns true if next handler exists and executed, otherwise false.\n//\n// Note that if no next handler found and handlers are missing then\n// it sends a Status Not Found (404) to the client and it stops the execution.\nfunc (ctx *Context) NextOr(handlers ...Handler) bool {\n\tif next := ctx.NextHandler(); next != nil {\n\t\tctx.Skip() // skip this handler from the chain.\n\t\tnext(ctx)\n\t\treturn true\n\t}\n\n\tif len(handlers) == 0 {\n\t\tctx.NotFound()\n\t\tctx.StopExecution()\n\t\treturn false\n\t}\n\n\tctx.Do(handlers)\n\n\treturn false\n}\n\n// NextOrNotFound checks if chain has a next handler, if so then it executes it\n// otherwise it sends a Status Not Found (404) to the client and stops the execution.\n//\n// Returns true if next handler exists and executed, otherwise false.\nfunc (ctx *Context) NextOrNotFound() bool { return ctx.NextOr() }\n\n// NextHandler returns (it doesn't execute) the next handler from the handlers chain.\n//\n// Use .Skip() to skip this handler if needed to execute the next of this returning handler.\nfunc (ctx *Context) NextHandler() Handler {\n\tif ctx.IsStopped() {\n\t\treturn nil\n\t}\n\tnextIndex := ctx.currentHandlerIndex + 1\n\t// check if it has a next middleware\n\tif nextIndex < len(ctx.handlers) {\n\t\treturn ctx.handlers[nextIndex]\n\t}\n\treturn nil\n}\n\n// Skip skips/ignores the next handler from the handlers chain,\n// it should be used inside a middleware.\nfunc (ctx *Context) Skip() {\n\tctx.HandlerIndex(ctx.currentHandlerIndex + 1)\n}\n\nconst (\n\tstopExecutionIndex            = -1\n\tinternalPauseExecutionIndex   = -2\n\tinternalProceededHandlerIndex = -3\n)\n\n// StopExecution stops the handlers chain of this request.\n// Meaning that any following `Next` calls are ignored,\n// as a result the next handlers in the chain will not be fire.\n//\n// See ResumeExecution too.\nfunc (ctx *Context) StopExecution() {\n\tif curIdx := ctx.currentHandlerIndex; curIdx != stopExecutionIndex {\n\t\t// Protect against multiple calls of StopExecution.\n\t\t// Resume should set the last proceeded handler index.\n\t\t// Store the current index.\n\t\tctx.proceeded = curIdx\n\t\t// And stop.\n\t\tctx.currentHandlerIndex = stopExecutionIndex\n\t}\n}\n\n// IsStopped reports whether the current position of the context's handlers is -1,\n// means that the StopExecution() was called at least once.\nfunc (ctx *Context) IsStopped() bool {\n\treturn ctx.currentHandlerIndex == stopExecutionIndex\n}\n\n// ResumeExecution sets the current handler index to the last\n// index of the executed handler before StopExecution method was fired.\n//\n// Reports whether it's restored after a StopExecution call.\nfunc (ctx *Context) ResumeExecution() bool {\n\tif ctx.IsStopped() {\n\t\tctx.currentHandlerIndex = ctx.proceeded\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// StopWithStatus stops the handlers chain and writes the \"statusCode\".\n//\n// If the status code is a failure one then\n// it will also fire the specified error code handler.\nfunc (ctx *Context) StopWithStatus(statusCode int) {\n\tctx.StopExecution()\n\tctx.StatusCode(statusCode)\n}\n\n// StopWithText stops the handlers chain and writes the \"statusCode\"\n// among with a fmt-style text of \"format\" and optional arguments.\n//\n// If the status code is a failure one then\n// it will also fire the specified error code handler.\nfunc (ctx *Context) StopWithText(statusCode int, text string) {\n\tctx.StopWithStatus(statusCode)\n\tctx.WriteString(text)\n}\n\n// StopWithError stops the handlers chain and writes the \"statusCode\"\n// among with the error \"err\".\n// It Calls the `SetErr` method so error handlers can access the given error.\n//\n// If the status code is a failure one then\n// it will also fire the specified error code handler.\n//\n// If the given \"err\" is private then the\n// status code's text is rendered instead (unless a registered error handler overrides it).\nfunc (ctx *Context) StopWithError(statusCode int, err error) {\n\tif err == nil {\n\t\treturn\n\t}\n\n\tctx.SetErr(err)\n\tif _, ok := err.(ErrPrivate); ok {\n\t\t// error is private, we SHOULD not render it,\n\t\t// leave the error handler alone to\n\t\t// render the code's text instead.\n\t\tctx.StopWithStatus(statusCode)\n\t\treturn\n\t}\n\n\tctx.StopWithText(statusCode, err.Error())\n}\n\n// StopWithPlainError like `StopWithError` but it does NOT\n// write anything to the response writer, it stores the error\n// so any error handler matching the given \"statusCode\" can handle it by its own.\nfunc (ctx *Context) StopWithPlainError(statusCode int, err error) {\n\tif err == nil {\n\t\treturn\n\t}\n\n\tctx.SetErr(err)\n\tctx.StopWithStatus(statusCode)\n}\n\n// StopWithJSON stops the handlers chain, writes the status code\n// and sends a JSON response.\n//\n// If the status code is a failure one then\n// it will also fire the specified error code handler.\nfunc (ctx *Context) StopWithJSON(statusCode int, jsonObject any) error {\n\tctx.StopWithStatus(statusCode)\n\treturn ctx.writeJSON(jsonObject, &DefaultJSONOptions) // do not modify - see errors.DefaultContextErrorHandler.\n}\n\n// StopWithProblem stops the handlers chain, writes the status code\n// and sends an application/problem+json response.\n// See `iris.NewProblem` to build a \"problem\" value correctly.\n//\n// If the status code is a failure one then\n// it will also fire the specified error code handler.\nfunc (ctx *Context) StopWithProblem(statusCode int, problem Problem) error {\n\tctx.StopWithStatus(statusCode)\n\tproblem.Status(statusCode)\n\treturn ctx.Problem(problem)\n}\n\n//  +------------------------------------------------------------+\n//  | Current \"user/request\" storage                             |\n//  | and share information between the handlers - Values().     |\n//  | Save and get named path parameters - Params()              |\n//  +------------------------------------------------------------+\n\n// Params returns the current url's named parameters key-value storage.\n// Named path parameters are being saved here.\n// This storage, as the whole context, is per-request lifetime.\nfunc (ctx *Context) Params() *RequestParams {\n\treturn &ctx.params\n}\n\n// Values returns the current \"user\" storage.\n// Named path parameters and any optional data can be saved here.\n// This storage, as the whole context, is per-request lifetime.\n//\n// You can use this function to Set and Get local values\n// that can be used to share information between handlers and middleware.\nfunc (ctx *Context) Values() *memstore.Store {\n\treturn &ctx.values\n}\n\n//  +------------------------------------------------------------+\n//  | Path, Host, Subdomain, IP, Headers etc...                  |\n//  +------------------------------------------------------------+\n\n// Method returns the request.Method, the client's http method to the server.\nfunc (ctx *Context) Method() string {\n\treturn ctx.request.Method\n}\n\n// Path returns the full request path,\n// escaped if EnablePathEscape config field is true.\nfunc (ctx *Context) Path() string {\n\treturn ctx.RequestPath(ctx.app.ConfigurationReadOnly().GetEnablePathEscape())\n}\n\n// DecodeQuery returns the uri parameter as url (string)\n// useful when you want to pass something to a database and be valid to retrieve it via context.Param\n// use it only for special cases, when the default behavior doesn't suits you.\n//\n// http://www.blooberry.com/indexdot/html/topics/urlencoding.htm\n// it uses just the url.QueryUnescape\nfunc DecodeQuery(path string) string {\n\tif path == \"\" {\n\t\treturn \"\"\n\t}\n\tencodedPath, err := url.QueryUnescape(path)\n\tif err != nil {\n\t\treturn path\n\t}\n\treturn encodedPath\n}\n\n// DecodeURL returns the decoded uri\n// useful when you want to pass something to a database and be valid to retrieve it via context.Param\n// use it only for special cases, when the default behavior doesn't suits you.\n//\n// http://www.blooberry.com/indexdot/html/topics/urlencoding.htm\n// it uses just the url.Parse\nfunc DecodeURL(uri string) string {\n\tu, err := url.Parse(uri)\n\tif err != nil {\n\t\treturn uri\n\t}\n\treturn u.String()\n}\n\n// RequestPath returns the full request path,\n// based on the 'escape'.\nfunc (ctx *Context) RequestPath(escape bool) string {\n\tif escape {\n\t\treturn ctx.request.URL.EscapedPath() // DecodeQuery(ctx.request.URL.EscapedPath())\n\t}\n\n\treturn ctx.request.URL.Path // RawPath returns empty, requesturi can be used instead also.\n}\n\nconst sufscheme = \"://\"\n\n// GetScheme returns the full scheme of the request URL (https://, http:// or ws:// and e.t.c.).\nfunc GetScheme(r *http.Request) string {\n\tscheme := r.URL.Scheme\n\n\tif scheme == \"\" {\n\t\tif r.TLS != nil {\n\t\t\tscheme = netutil.SchemeHTTPS\n\t\t} else {\n\t\t\tscheme = netutil.SchemeHTTP\n\t\t}\n\t}\n\n\treturn scheme + sufscheme\n}\n\n// Scheme returns the full scheme of the request (including :// suffix).\nfunc (ctx *Context) Scheme() string {\n\treturn GetScheme(ctx.Request())\n}\n\n// PathPrefixMap accepts a map of string and a handler.\n// The key of \"m\" is the key, which is the prefix, regular expressions are not valid.\n// The value of \"m\" is the handler that will be executed if HasPrefix(context.Path).\n// func (ctx *Context) PathPrefixMap(m map[string]context.Handler) bool {\n// \tpath := ctx.Path()\n// \tfor k, v := range m {\n// \t\tif strings.HasPrefix(path, k) {\n// \t\t\tv(ctx)\n// \t\t\treturn true\n// \t\t}\n// \t}\n// \treturn false\n// } no, it will not work because map is a random peek data structure.\n\n// GetHost returns the host part of the current URI.\nfunc GetHost(r *http.Request) string {\n\t// contains subdomain.\n\tif host := r.URL.Host; host != \"\" {\n\t\treturn host\n\t}\n\treturn r.Host\n}\n\n// Host returns the host:port part of the request URI, calls the `Request().Host`.\n// To get the subdomain part as well use the `Request().URL.Host` method instead.\n// To get the subdomain only use the `Subdomain` method instead.\n// This method makes use of the `Configuration.HostProxyHeaders` field too.\nfunc (ctx *Context) Host() string {\n\tfor header, ok := range ctx.app.ConfigurationReadOnly().GetHostProxyHeaders() {\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\n\t\tif host := ctx.GetHeader(header); host != \"\" {\n\t\t\treturn host\n\t\t}\n\t}\n\n\treturn GetHost(ctx.request)\n}\n\n// GetDomain resolves and returns the server's domain.\n// To customize its behavior, developers can modify this package-level function at initialization.\nvar GetDomain = func(hostport string) string {\n\thost := hostport\n\tif tmp, _, err := net.SplitHostPort(hostport); err == nil {\n\t\thost = tmp\n\t}\n\n\tswitch host {\n\t// We could use the netutil.LoopbackRegex but leave it as it's for now, it's faster.\n\tcase \"localhost\", \"127.0.0.1\", \"0.0.0.0\", \"::1\", \"[::1]\", \"0:0:0:0:0:0:0:0\", \"0:0:0:0:0:0:0:1\":\n\t\t// loopback.\n\t\treturn \"localhost\"\n\tdefault:\n\t\tif net.ParseIP(host) != nil { // if it's an IP, see #1945.\n\t\t\treturn host\n\t\t}\n\n\t\tif domain, err := publicsuffix.EffectiveTLDPlusOne(host); err == nil {\n\t\t\thost = domain\n\t\t}\n\n\t\treturn host\n\t}\n}\n\n// Domain returns the root level domain.\nfunc (ctx *Context) Domain() string {\n\treturn GetDomain(ctx.Host())\n}\n\n// GetSubdomainFull returns the full subdomain level, e.g.\n// [test.user.]mydomain.com.\nfunc GetSubdomainFull(r *http.Request) string {\n\thost := GetHost(r)            // host:port\n\trootDomain := GetDomain(host) // mydomain.com\n\trootDomainIdx := strings.Index(host, rootDomain)\n\tif rootDomainIdx == -1 {\n\t\treturn \"\"\n\t}\n\n\treturn host[0:rootDomainIdx]\n}\n\n// SubdomainFull returns the full subdomain level, e.g.\n// [test.user.]mydomain.com.\n// Note that HostProxyHeaders are being respected here.\nfunc (ctx *Context) SubdomainFull() string {\n\thost := ctx.Host()            // host:port\n\trootDomain := GetDomain(host) // mydomain.com\n\trootDomainIdx := strings.Index(host, rootDomain)\n\tif rootDomainIdx == -1 {\n\t\treturn \"\"\n\t}\n\n\treturn host[0:rootDomainIdx]\n}\n\n// Subdomain returns the first subdomain of this request,\n// e.g. [user.]mydomain.com.\n// See `SubdomainFull` too.\nfunc (ctx *Context) Subdomain() (subdomain string) {\n\thost := ctx.Host()\n\tif index := strings.IndexByte(host, '.'); index > 0 {\n\t\tsubdomain = host[0:index]\n\t}\n\n\t// listening on mydomain.com:80\n\t// subdomain = mydomain, but it's wrong, it should return \"\"\n\tvhost := ctx.app.ConfigurationReadOnly().GetVHost()\n\tif strings.Contains(vhost, subdomain) { // then it's not subdomain\n\t\treturn \"\"\n\t}\n\n\treturn\n}\n\n// FindClosest returns a list of \"n\" paths close to\n// this request based on subdomain and request path.\n//\n// Order may change.\n// Example: https://github.com/kataras/iris/tree/main/_examples/routing/intelligence/manual\nfunc (ctx *Context) FindClosest(n int) []string {\n\treturn ctx.app.FindClosestPaths(ctx.Subdomain(), ctx.Path(), n)\n}\n\n// IsWWW returns true if the current subdomain (if any) is www.\nfunc (ctx *Context) IsWWW() bool {\n\thost := ctx.Host()\n\tif index := strings.IndexByte(host, '.'); index > 0 {\n\t\t// if it has a subdomain and it's www then return true.\n\t\tif subdomain := host[0:index]; !strings.Contains(ctx.app.ConfigurationReadOnly().GetVHost(), subdomain) {\n\t\t\treturn subdomain == \"www\"\n\t\t}\n\t}\n\treturn false\n}\n\n// FullRequestURI returns the full URI,\n// including the scheme, the host and the relative requested path/resource.\nfunc (ctx *Context) FullRequestURI() string {\n\treturn ctx.AbsoluteURI(ctx.Path())\n}\n\n// RemoteAddr tries to parse and return the real client's request IP.\n//\n// Based on allowed headers names that can be modified from Configuration.RemoteAddrHeaders.\n//\n// If parse based on these headers fail then it will return the Request's `RemoteAddr` field\n// which is filled by the server before the HTTP handler,\n// unless the Configuration.RemoteAddrHeadersForce was set to true\n// which will force this method to return the first IP from RemoteAddrHeaders\n// even if it's part of a private network.\n//\n// Look `Configuration.RemoteAddrHeaders`,\n//\n//\tConfiguration.RemoteAddrHeadersForce,\n//\tConfiguration.WithRemoteAddrHeader(...),\n//\tConfiguration.WithoutRemoteAddrHeader(...) and\n//\tConfiguration.RemoteAddrPrivateSubnetsW for more.\nfunc (ctx *Context) RemoteAddr() string {\n\tif remoteHeaders := ctx.app.ConfigurationReadOnly().GetRemoteAddrHeaders(); len(remoteHeaders) > 0 {\n\t\tprivateSubnets := ctx.app.ConfigurationReadOnly().GetRemoteAddrPrivateSubnets()\n\n\t\tfor _, headerName := range remoteHeaders {\n\t\t\tipAddresses := strings.Split(ctx.GetHeader(headerName), \",\")\n\t\t\tif ip, ok := netutil.GetIPAddress(ipAddresses, privateSubnets); ok {\n\t\t\t\treturn ip\n\t\t\t}\n\t\t}\n\n\t\tif ctx.app.ConfigurationReadOnly().GetRemoteAddrHeadersForce() {\n\t\t\tfor _, headerName := range remoteHeaders {\n\t\t\t\t// return the first valid IP,\n\t\t\t\t//  even if it's a part of a private network.\n\t\t\t\tipAddresses := strings.Split(ctx.GetHeader(headerName), \",\")\n\t\t\t\tfor _, addr := range ipAddresses {\n\t\t\t\t\tif ip, _, err := net.SplitHostPort(addr); err == nil {\n\t\t\t\t\t\treturn ip\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\taddr := strings.TrimSpace(ctx.request.RemoteAddr)\n\tif addr != \"\" {\n\t\t// if addr has port use the net.SplitHostPort otherwise(error occurs) take as it is\n\t\tif ip, _, err := net.SplitHostPort(addr); err == nil {\n\t\t\treturn ip\n\t\t}\n\t}\n\n\treturn addr\n}\n\n// TrimHeaderValue returns the \"v[0:first space or semicolon]\".\nfunc TrimHeaderValue(v string) string {\n\tfor i, char := range v {\n\t\tif char == ' ' || char == ';' {\n\t\t\treturn v[:i]\n\t\t}\n\t}\n\treturn v\n}\n\n// GetHeader returns the request header's value based on its name.\nfunc (ctx *Context) GetHeader(name string) string {\n\treturn ctx.request.Header.Get(name)\n}\n\n// IsAjax returns true if this request is an 'ajax request'( XMLHttpRequest)\n//\n// There is no a 100% way of knowing that a request was made via Ajax.\n// You should never trust data coming from the client, they can be easily overcome by spoofing.\n//\n// Note that \"X-Requested-With\" Header can be modified by any client(because of \"X-\"),\n// so don't rely on IsAjax for really serious stuff,\n// try to find another way of detecting the type(i.e, content type),\n// there are many blogs that describe these problems and provide different kind of solutions,\n// it's always depending on the application you're building,\n// this is the reason why this `IsAjax` is simple enough for general purpose use.\n//\n// Read more at: https://developer.mozilla.org/en-US/docs/AJAX\n// and https://xhr.spec.whatwg.org/\nfunc (ctx *Context) IsAjax() bool {\n\treturn ctx.GetHeader(\"X-Requested-With\") == \"XMLHttpRequest\"\n}\n\nvar isMobileRegex = regexp.MustCompile(\"(?:hpw|i|web)os|alamofire|alcatel|amoi|android|avantgo|blackberry|blazer|cell|cfnetwork|darwin|dolfin|dolphin|fennec|htc|ip(?:hone|od|ad)|ipaq|j2me|kindle|midp|minimo|mobi|motorola|nec-|netfront|nokia|opera m(ob|in)i|palm|phone|pocket|portable|psp|silk-accelerated|skyfire|sony|ucbrowser|up.browser|up.link|windows ce|xda|zte|zune\")\n\n// IsMobile checks if client is using a mobile device(phone or tablet) to communicate with this server.\n// If the return value is true that means that the http client using a mobile\n// device to communicate with the server, otherwise false.\n//\n// Keep note that this checks the \"User-Agent\" request header.\nfunc (ctx *Context) IsMobile() bool {\n\ts := strings.ToLower(ctx.GetHeader(\"User-Agent\"))\n\treturn isMobileRegex.MatchString(s)\n}\n\nvar isScriptRegex = regexp.MustCompile(\"curl|wget|collectd|python|urllib|java|jakarta|httpclient|phpcrawl|libwww|perl|go-http|okhttp|lua-resty|winhttp|awesomium\")\n\n// IsScript reports whether a client is a script.\nfunc (ctx *Context) IsScript() bool {\n\ts := strings.ToLower(ctx.GetHeader(\"User-Agent\"))\n\treturn isScriptRegex.MatchString(s)\n}\n\n// IsSSL reports whether the client is running under HTTPS SSL.\n//\n// See `IsHTTP2` too.\nfunc (ctx *Context) IsSSL() bool {\n\tssl := strings.EqualFold(ctx.request.URL.Scheme, \"https\") || ctx.request.TLS != nil\n\tif !ssl {\n\t\tfor k, v := range ctx.app.ConfigurationReadOnly().GetSSLProxyHeaders() {\n\t\t\tif ctx.GetHeader(k) == v {\n\t\t\t\tssl = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\treturn ssl\n}\n\n// IsHTTP2 reports whether the protocol version for incoming request was HTTP/2.\n// The client code always uses either HTTP/1.1 or HTTP/2.\n//\n// See `IsSSL` too.\nfunc (ctx *Context) IsHTTP2() bool {\n\treturn ctx.request.ProtoMajor == 2\n}\n\n// IsGRPC reports whether the request came from a gRPC client.\nfunc (ctx *Context) IsGRPC() bool {\n\treturn ctx.IsHTTP2() && strings.Contains(ctx.GetContentTypeRequested(), ContentGRPCHeaderValue)\n}\n\ntype (\n\t// Referrer contains the extracted information from the `GetReferrer`\n\t//\n\t// The structure contains struct tags for JSON, form, XML, YAML and TOML.\n\t// Look the `GetReferrer() Referrer` and `goreferrer` external package.\n\tReferrer struct {\n\t\t// The raw refer(r)er URL.\n\t\tRaw        string                   `json:\"raw\" form:\"raw\" xml:\"Raw\" yaml:\"Raw\" toml:\"Raw\"`\n\t\tType       ReferrerType             `json:\"type\" form:\"referrer_type\" xml:\"Type\" yaml:\"Type\" toml:\"Type\"`\n\t\tLabel      string                   `json:\"label\" form:\"referrer_form\" xml:\"Label\" yaml:\"Label\" toml:\"Label\"`\n\t\tURL        string                   `json:\"url\" form:\"referrer_url\" xml:\"URL\" yaml:\"URL\" toml:\"URL\"`\n\t\tSubdomain  string                   `json:\"subdomain\" form:\"referrer_subdomain\" xml:\"Subdomain\" yaml:\"Subdomain\" toml:\"Subdomain\"`\n\t\tDomain     string                   `json:\"domain\" form:\"referrer_domain\" xml:\"Domain\" yaml:\"Domain\" toml:\"Domain\"`\n\t\tTld        string                   `json:\"tld\" form:\"referrer_tld\" xml:\"Tld\" yaml:\"Tld\" toml:\"Tld\"`\n\t\tPath       string                   `json:\"path\" form:\"referrer_path\" xml:\"Path\" yaml:\"Path\" toml:\"Path\"`\n\t\tQuery      string                   `json:\"query\" form:\"referrer_query\" xml:\"Query\" yaml:\"Query\" toml:\"GoogleType\"`\n\t\tGoogleType ReferrerGoogleSearchType `json:\"googleType\" form:\"referrer_google_type\" xml:\"GoogleType\" yaml:\"GoogleType\" toml:\"GoogleType\"`\n\t}\n\n\t// ReferrerType is the goreferrer enum for a referrer type (indirect, direct, email, search, social).\n\tReferrerType = goreferrer.ReferrerType\n\n\t// ReferrerGoogleSearchType is the goreferrer enum for a google search type (organic, adwords).\n\tReferrerGoogleSearchType = goreferrer.GoogleSearchType\n)\n\n// String returns the raw ref url.\nfunc (ref Referrer) String() string {\n\treturn ref.Raw\n}\n\n// Contains the available values of the goreferrer enums.\nconst (\n\tReferrerInvalid ReferrerType = iota\n\tReferrerIndirect\n\tReferrerDirect\n\tReferrerEmail\n\tReferrerSearch\n\tReferrerSocial\n\n\tReferrerNotGoogleSearch ReferrerGoogleSearchType = iota\n\tReferrerGoogleOrganicSearch\n\tReferrerGoogleAdwords\n)\n\n// unnecessary but good to know the default values upfront.\nvar emptyReferrer = Referrer{Type: ReferrerInvalid, GoogleType: ReferrerNotGoogleSearch}\n\n// GetReferrer extracts and returns the information from the \"Referer\" (or \"Referrer\") header\n// and url query parameter as specified in https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy.\nfunc (ctx *Context) GetReferrer() Referrer {\n\t// the underline net/http follows the https://tools.ietf.org/html/rfc7231#section-5.5.2,\n\t// so there is nothing special left to do.\n\t// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy\n\trefURL := ctx.GetHeader(\"Referer\")\n\tif refURL == \"\" {\n\t\trefURL = ctx.GetHeader(\"Referrer\")\n\t\tif refURL == \"\" {\n\t\t\trefURL = ctx.URLParam(\"referer\")\n\t\t\tif refURL == \"\" {\n\t\t\t\trefURL = ctx.URLParam(\"referrer\")\n\t\t\t}\n\t\t}\n\t}\n\n\tif refURL == \"\" {\n\t\treturn emptyReferrer\n\t}\n\n\tif ref := goreferrer.DefaultRules.Parse(refURL); ref.Type > goreferrer.Invalid {\n\t\treturn Referrer{\n\t\t\tRaw:        refURL,\n\t\t\tType:       ReferrerType(ref.Type),\n\t\t\tLabel:      ref.Label,\n\t\t\tURL:        ref.URL,\n\t\t\tSubdomain:  ref.Subdomain,\n\t\t\tDomain:     ref.Domain,\n\t\t\tTld:        ref.Tld,\n\t\t\tPath:       ref.Path,\n\t\t\tQuery:      ref.Query,\n\t\t\tGoogleType: ReferrerGoogleSearchType(ref.GoogleType),\n\t\t}\n\t}\n\n\treturn emptyReferrer\n}\n\n// SetLanguage force-sets the language for i18n, can be used inside a middleare.\n// It has the highest priority over the rest and if it is empty then it is ignored,\n// if it set to a static string of \"default\" or to the default language's code\n// then the rest of the language extractors will not be called at all and\n// the default language will be set instead.\n//\n// See `i18n.ExtractFunc` for a more organised way of the same feature.\nfunc (ctx *Context) SetLanguage(langCode string) {\n\tctx.values.Set(ctx.app.ConfigurationReadOnly().GetLanguageContextKey(), langCode)\n}\n\n// GetLocale returns the current request's `Locale` found by i18n middleware.\n// It always fallbacks to the default one.\n// See `Tr` too.\nfunc (ctx *Context) GetLocale() Locale {\n\t// Cache the Locale itself for multiple calls of `Tr` method.\n\tcontextKey := ctx.app.ConfigurationReadOnly().GetLocaleContextKey()\n\tif v := ctx.values.Get(contextKey); v != nil {\n\t\tif locale, ok := v.(Locale); ok {\n\t\t\treturn locale\n\t\t}\n\t}\n\n\tif locale := ctx.app.I18nReadOnly().GetLocale(ctx); locale != nil {\n\t\tctx.values.Set(contextKey, locale)\n\t\treturn locale\n\t}\n\n\treturn nil\n}\n\n// Tr returns a i18n localized message based on format with optional arguments.\n// See `GetLocale` too.\n//\n// Example: https://github.com/kataras/iris/tree/main/_examples/i18n\nfunc (ctx *Context) Tr(key string, args ...any) string {\n\treturn ctx.app.I18nReadOnly().TrContext(ctx, key, args...)\n}\n\n//  +------------------------------------------------------------+\n//  | Response Headers helpers                                   |\n//  +------------------------------------------------------------+\n\n// Header adds a header to the response, if value is empty\n// it removes the header by its name.\nfunc (ctx *Context) Header(name string, value string) {\n\tif value == \"\" {\n\t\tctx.writer.Header().Del(name)\n\t\treturn\n\t}\n\tctx.writer.Header().Add(name, value)\n}\n\nconst contentTypeContextKey = \"iris.content_type\"\n\nfunc shouldAppendCharset(cType string) bool {\n\tif idx := strings.IndexRune(cType, '/'); idx > 1 && len(cType) > idx+1 {\n\t\ttyp := cType[0:idx]\n\t\tif typ == \"application\" {\n\t\t\tswitch cType[idx+1:] {\n\t\t\tcase \"json\", \"xml\", \"yaml\", \"problem+json\", \"problem+xml\":\n\t\t\t\treturn true\n\t\t\tdefault:\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\n\t}\n\n\treturn true\n}\n\nfunc (ctx *Context) contentTypeOnce(cType string, charset string) {\n\tif charset == \"\" {\n\t\tcharset = ctx.app.ConfigurationReadOnly().GetCharset()\n\t}\n\n\tif shouldAppendCharset(cType) {\n\t\tcType += \"; charset=\" + charset\n\t}\n\n\tctx.values.Set(contentTypeContextKey, cType)\n\tctx.writer.Header().Set(ContentTypeHeaderKey, cType)\n}\n\n// ContentType sets the response writer's\n// header \"Content-Type\" to the 'cType'.\nfunc (ctx *Context) ContentType(cType string) {\n\tif cType == \"\" {\n\t\treturn\n\t}\n\n\tif _, wroteOnce := ctx.values.GetEntry(contentTypeContextKey); wroteOnce {\n\t\treturn\n\t}\n\n\t// 1. if it's path or a filename or an extension,\n\t// then take the content type from that,\n\t// ^ No, it's not always a file,e .g. vnd.$type\n\t// if strings.Contains(cType, \".\") {\n\t// \text := filepath.Ext(cType)\n\t// \tcType = mime.TypeByExtension(ext)\n\t// }\n\t// if doesn't contain a charset already then append it\n\tif shouldAppendCharset(cType) {\n\t\tif !strings.Contains(cType, \"charset\") {\n\t\t\tcType += \"; charset=\" + ctx.app.ConfigurationReadOnly().GetCharset()\n\t\t}\n\t}\n\n\tctx.writer.Header().Set(ContentTypeHeaderKey, cType)\n}\n\n// GetContentType returns the response writer's\n// header value of \"Content-Type\".\nfunc (ctx *Context) GetContentType() string {\n\treturn ctx.writer.Header().Get(ContentTypeHeaderKey)\n}\n\n// GetContentTypeRequested returns the request's\n// trim-ed(without the charset and priority values)\n// header value of \"Content-Type\".\nfunc (ctx *Context) GetContentTypeRequested() string {\n\t// could use mime.ParseMediaType too.\n\treturn TrimHeaderValue(ctx.GetHeader(ContentTypeHeaderKey))\n}\n\n// GetContentLength returns the request's\n// header value of \"Content-Length\".\nfunc (ctx *Context) GetContentLength() int64 {\n\tif v := ctx.GetHeader(ContentLengthHeaderKey); v != \"\" {\n\t\tn, _ := strconv.ParseInt(v, 10, 64)\n\t\treturn n\n\t}\n\treturn 0\n}\n\n// StatusCode sets the status code header to the response.\n// Look .GetStatusCode & .FireStatusCode too.\n//\n// Note that you must set status code before write response body (except when recorder is used).\nfunc (ctx *Context) StatusCode(statusCode int) {\n\tctx.writer.WriteHeader(statusCode)\n}\n\n// NotFound emits an error 404 to the client, using the specific custom error error handler.\n// Note that you may need to call ctx.StopExecution() if you don't want the next handlers\n// to be executed. Next handlers are being executed on iris because you can alt the\n// error code and change it to a more specific one, i.e\n// users := app.Party(\"/users\")\n// users.Done(func(ctx iris.Context){ if ctx.GetStatusCode() == 400 { /*  custom error code for /users */ }})\nfunc (ctx *Context) NotFound() {\n\tctx.StatusCode(http.StatusNotFound)\n}\n\n// GetStatusCode returns the current status code of the response.\n// Look StatusCode too.\nfunc (ctx *Context) GetStatusCode() int {\n\treturn ctx.writer.StatusCode()\n}\n\n//  +------------------------------------------------------------+\n//  | Various Request and Post Data                              |\n//  +------------------------------------------------------------+\n\nfunc (ctx *Context) getQuery() url.Values {\n\tif ctx.query == nil {\n\t\tctx.query = ctx.request.URL.Query()\n\t}\n\n\treturn ctx.query\n}\n\n// URLParamExists returns true if the url parameter exists, otherwise false.\nfunc (ctx *Context) URLParamExists(name string) bool {\n\t_, exists := ctx.getQuery()[name]\n\treturn exists\n}\n\n// URLParamDefault returns the get parameter from a request, if not found then \"def\" is returned.\nfunc (ctx *Context) URLParamDefault(name string, def string) string {\n\tif v := ctx.getQuery().Get(name); v != \"\" {\n\t\treturn v\n\t}\n\n\treturn def\n}\n\n// URLParam returns the get parameter from a request, if any.\nfunc (ctx *Context) URLParam(name string) string {\n\treturn ctx.URLParamDefault(name, \"\")\n}\n\n// URLParamSlice a shortcut of ctx.Request().URL.Query()[name].\n// Like `URLParam` but it returns all values instead of a single string separated by commas.\n// Returns the values of a url query of the given \"name\" as string slice, e.g.\n// ?names=john&names=doe&names=kataras and ?names=john,doe,kataras will return [ john doe kataras].\n//\n// Note that, this method skips any empty entries.\n//\n// See `URLParamsSorted` for sorted values.\nfunc (ctx *Context) URLParamSlice(name string) []string {\n\tvalues := ctx.getQuery()[name]\n\tn := len(values)\n\tif n == 0 {\n\t\treturn values\n\t}\n\n\tvar sep string\n\tif sepPtr := ctx.app.ConfigurationReadOnly().GetURLParamSeparator(); sepPtr != nil {\n\t\tsep = *sepPtr\n\t}\n\n\tnormalizedValues := make([]string, 0, n)\n\tfor _, v := range values {\n\t\tif v == \"\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tif sep != \"\" {\n\t\t\tvalues := strings.Split(v, sep)\n\t\t\tnormalizedValues = append(normalizedValues, values...)\n\t\t\tcontinue\n\t\t}\n\n\t\tnormalizedValues = append(normalizedValues, v)\n\t}\n\n\treturn normalizedValues\n}\n\n// URLParamTrim returns the url query parameter with trailing white spaces removed from a request.\nfunc (ctx *Context) URLParamTrim(name string) string {\n\treturn strings.TrimSpace(ctx.URLParam(name))\n}\n\n// URLParamEscape returns the escaped url query parameter from a request.\nfunc (ctx *Context) URLParamEscape(name string) string {\n\treturn DecodeQuery(ctx.URLParam(name))\n}\n\n// ErrNotFound is the type error which API users can make use of\n// to check if a `Context` action of a `Handler` is type of Not Found,\n// e.g. URL Query Parameters.\n// Example:\n//\n// n, err := context.URLParamInt(\"url_query_param_name\")\n//\n//\tif errors.Is(err, context.ErrNotFound) {\n//\t\t// [handle error...]\n//\t}\n//\n// Another usage would be `err == context.ErrNotFound`\n// HOWEVER prefer use the new `errors.Is` as API details may change in the future.\nvar ErrNotFound = errors.New(\"not found\")\n\n// URLParamInt returns the url query parameter as int value from a request,\n// returns -1 and an error if parse failed or not found.\nfunc (ctx *Context) URLParamInt(name string) (int, error) {\n\tif v := ctx.URLParam(name); v != \"\" {\n\t\tn, err := strconv.Atoi(v)\n\t\tif err != nil {\n\t\t\treturn -1, err\n\t\t}\n\t\treturn n, nil\n\t}\n\n\treturn -1, ErrNotFound\n}\n\n// URLParamIntDefault returns the url query parameter as int value from a request,\n// if not found or parse failed then \"def\" is returned.\nfunc (ctx *Context) URLParamIntDefault(name string, def int) int {\n\tv, err := ctx.URLParamInt(name)\n\tif err != nil {\n\t\treturn def\n\t}\n\n\treturn v\n}\n\n// URLParamInt32Default returns the url query parameter as int32 value from a request,\n// if not found or parse failed then \"def\" is returned.\nfunc (ctx *Context) URLParamInt32Default(name string, def int32) int32 {\n\tif v := ctx.URLParam(name); v != \"\" {\n\t\tn, err := strconv.ParseInt(v, 10, 32)\n\t\tif err != nil {\n\t\t\treturn def\n\t\t}\n\n\t\treturn int32(n)\n\t}\n\n\treturn def\n}\n\n// URLParamInt64 returns the url query parameter as int64 value from a request,\n// returns -1 and an error if parse failed or not found.\nfunc (ctx *Context) URLParamInt64(name string) (int64, error) {\n\tif v := ctx.URLParam(name); v != \"\" {\n\t\tn, err := strconv.ParseInt(v, 10, 64)\n\t\tif err != nil {\n\t\t\treturn -1, err\n\t\t}\n\t\treturn n, nil\n\t}\n\n\treturn -1, ErrNotFound\n}\n\n// URLParamInt64Default returns the url query parameter as int64 value from a request,\n// if not found or parse failed then \"def\" is returned.\nfunc (ctx *Context) URLParamInt64Default(name string, def int64) int64 {\n\tv, err := ctx.URLParamInt64(name)\n\tif err != nil {\n\t\treturn def\n\t}\n\n\treturn v\n}\n\n// URLParamUint64 returns the url query parameter as uint64 value from a request.\n// Returns 0 on parse errors or when the URL parameter does not exist in the Query.\nfunc (ctx *Context) URLParamUint64(name string) uint64 {\n\tif v := ctx.URLParam(name); v != \"\" {\n\t\tn, err := strconv.ParseUint(v, 10, 64)\n\t\tif err != nil {\n\t\t\treturn 0\n\t\t}\n\t\treturn n\n\t}\n\n\treturn 0\n}\n\n// URLParamFloat64 returns the url query parameter as float64 value from a request,\n// returns an error and -1 if parse failed.\nfunc (ctx *Context) URLParamFloat64(name string) (float64, error) {\n\tif v := ctx.URLParam(name); v != \"\" {\n\t\tn, err := strconv.ParseFloat(v, 64)\n\t\tif err != nil {\n\t\t\treturn -1, err\n\t\t}\n\t\treturn n, nil\n\t}\n\n\treturn -1, ErrNotFound\n}\n\n// URLParamFloat64Default returns the url query parameter as float64 value from a request,\n// if not found or parse failed then \"def\" is returned.\nfunc (ctx *Context) URLParamFloat64Default(name string, def float64) float64 {\n\tv, err := ctx.URLParamFloat64(name)\n\tif err != nil {\n\t\treturn def\n\t}\n\n\treturn v\n}\n\n// URLParamBool returns the url query parameter as boolean value from a request,\n// returns an error if parse failed.\nfunc (ctx *Context) URLParamBool(name string) (bool, error) {\n\treturn strconv.ParseBool(ctx.URLParam(name))\n}\n\n// URLParamBoolDefault returns the url query parameter as boolean value from a request,\n// if not found or parse failed then \"def\" is returned.\nfunc (ctx *Context) URLParamBoolDefault(name string, def bool) bool {\n\tv, err := ctx.URLParamBool(name)\n\tif err != nil {\n\t\treturn def\n\t}\n\n\treturn v\n}\n\n// URLParams returns a map of URL Query parameters.\n// If the value of a URL parameter is a slice,\n// then it is joined as one separated by comma.\n// It returns an empty map on empty URL query.\n//\n// See URLParamsSorted too.\nfunc (ctx *Context) URLParams() map[string]string {\n\tq := ctx.getQuery()\n\tvalues := make(map[string]string, len(q))\n\n\tfor k, v := range q {\n\t\tvalues[k] = strings.Join(v, \",\")\n\t}\n\n\treturn values\n}\n\n// URLParamsSorted returns a sorted (by key) slice\n// of key-value entries of the URL Query parameters.\nfunc (ctx *Context) URLParamsSorted() []memstore.StringEntry {\n\tq := ctx.getQuery()\n\tn := len(q)\n\tif n == 0 {\n\t\treturn nil\n\t}\n\n\tkeys := make([]string, 0, n)\n\tfor key := range q {\n\t\tkeys = append(keys, key)\n\t}\n\n\tsort.Strings(keys)\n\n\tentries := make([]memstore.StringEntry, 0, n)\n\tfor _, key := range keys {\n\t\tvalue := q[key]\n\t\tentries = append(entries, memstore.StringEntry{\n\t\t\tKey:   key,\n\t\t\tValue: strings.Join(value, \",\"),\n\t\t})\n\t}\n\n\treturn entries\n}\n\n// ResetQuery clears the GET URL Query request, temporary, cache.\n// Any new URLParamXXX calls will receive the new parsed values.\nfunc (ctx *Context) ResetQuery() {\n\tctx.query = nil\n}\n\n// No need anymore, net/http checks for the Form already.\n// func (ctx *Context) askParseForm() error {\n// \tif ctx.request.Form == nil {\n// \t\tif err := ctx.request.ParseForm(); err != nil {\n// \t\t\treturn err\n// \t\t}\n// \t}\n// \treturn nil\n// }\n\n// FormValueDefault returns a single parsed form value by its \"name\",\n// including both the URL field's query parameters and the POST or PUT form data.\n//\n// Returns the \"def\" if not found.\nfunc (ctx *Context) FormValueDefault(name string, def string) string {\n\tif form, has := ctx.form(); has {\n\t\tif v := form[name]; len(v) > 0 {\n\t\t\treturn v[0]\n\t\t}\n\t}\n\treturn def\n}\n\n// FormValueDefault retruns a single parsed form value.\nfunc FormValueDefault(r *http.Request, name string, def string, postMaxMemory int64, resetBody bool) string {\n\tif form, has := GetForm(r, postMaxMemory, resetBody); has {\n\t\tif v := form[name]; len(v) > 0 {\n\t\t\treturn v[0]\n\t\t}\n\t}\n\treturn def\n}\n\n// FormValue returns a single parsed form value by its \"name\",\n// including both the URL field's query parameters and the POST or PUT form data.\nfunc (ctx *Context) FormValue(name string) string {\n\treturn ctx.FormValueDefault(name, \"\")\n}\n\n// FormValues returns the parsed form data, including both the URL\n// field's query parameters and the POST or PUT form data.\n//\n// The default form's memory maximum size is 32MB, it can be changed by the\n// `iris#WithPostMaxMemory` configurator at main configuration passed on `app.Run`'s second argument.\n// NOTE: A check for nil is necessary.\nfunc (ctx *Context) FormValues() map[string][]string {\n\tform, _ := ctx.form()\n\treturn form\n}\n\n// Form contains the parsed form data, including both the URL\n// field's query parameters and the POST or PUT form data.\nfunc (ctx *Context) form() (form map[string][]string, found bool) {\n\treturn GetForm(ctx.request, ctx.app.ConfigurationReadOnly().GetPostMaxMemory(), ctx.app.ConfigurationReadOnly().GetDisableBodyConsumptionOnUnmarshal())\n}\n\n// GetForm returns the request form (url queries, post or multipart) values.\nfunc GetForm(r *http.Request, postMaxMemory int64, resetBody bool) (form map[string][]string, found bool) {\n\t/*\n\t\tnet/http/request.go#1219\n\t\tfor k, v := range f.Value {\n\t\t\tr.Form[k] = append(r.Form[k], v...)\n\t\t\t// r.PostForm should also be populated. See Issue 9305.\n\t\t\tr.PostForm[k] = append(r.PostForm[k], v...)\n\t\t}\n\t*/\n\n\tif form := r.Form; len(form) > 0 {\n\t\treturn form, true\n\t}\n\n\tif form := r.PostForm; len(form) > 0 {\n\t\treturn form, true\n\t}\n\n\tif m := r.MultipartForm; m != nil {\n\t\tif len(m.Value) > 0 {\n\t\t\treturn m.Value, true\n\t\t}\n\t}\n\n\tif resetBody {\n\t\t// on POST, PUT and PATCH it will read the form values from request body otherwise from URL queries.\n\t\tif m := r.Method; m == \"POST\" || m == \"PUT\" || m == \"PATCH\" {\n\t\t\tbody, restoreBody, err := GetBody(r, resetBody)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, false\n\t\t\t}\n\t\t\tsetBody(r, body)    // so the ctx.request.Body works\n\t\t\tdefer restoreBody() // so the next GetForm calls work.\n\n\t\t\t// r.Body = io.NopCloser(io.TeeReader(r.Body, buf))\n\t\t} else {\n\t\t\tresetBody = false\n\t\t}\n\t}\n\n\t// ParseMultipartForm calls `request.ParseForm` automatically\n\t// therefore we don't need to call it here, although it doesn't hurt.\n\t// After one call to ParseMultipartForm or ParseForm,\n\t// subsequent calls have no effect, are idempotent.\n\terr := r.ParseMultipartForm(postMaxMemory)\n\t// if resetBody {\n\t// \tr.Body = io.NopCloser(bytes.NewBuffer(bodyCopy))\n\t// }\n\tif err != nil && err != http.ErrNotMultipart {\n\t\treturn nil, false\n\t}\n\n\tif form := r.Form; len(form) > 0 {\n\t\treturn form, true\n\t}\n\n\tif form := r.PostForm; len(form) > 0 {\n\t\treturn form, true\n\t}\n\n\tif m := r.MultipartForm; m != nil {\n\t\tif len(m.Value) > 0 {\n\t\t\treturn m.Value, true\n\t\t}\n\t}\n\n\treturn nil, false\n}\n\n// PostValues returns all the parsed form data from POST, PATCH,\n// or PUT body parameters based on a \"name\" as a string slice.\n//\n// The default form's memory maximum size is 32MB, it can be changed by the\n// `iris#WithPostMaxMemory` configurator at main configuration passed on `app.Run`'s second argument.\n//\n// In addition, it reports whether the form was empty\n// or when the \"name\" does not exist\n// or whether the available values are empty.\n// It strips any empty key-values from the slice before return.\n//\n// Look ErrEmptyForm, ErrNotFound and ErrEmptyFormField respectfully.\n// See `PostValueMany` method too.\nfunc (ctx *Context) PostValues(name string) ([]string, error) {\n\t_, ok := ctx.form()\n\tif !ok {\n\t\tif !ctx.app.ConfigurationReadOnly().GetFireEmptyFormError() {\n\t\t\treturn nil, nil\n\t\t}\n\n\t\treturn nil, ErrEmptyForm // empty form.\n\t}\n\n\tvalues, ok := ctx.request.PostForm[name]\n\tif !ok {\n\t\treturn nil, ErrNotFound // field does not exist\n\t}\n\n\tif len(values) == 0 ||\n\t\t// Fast check for its first empty value (see below).\n\t\tstrings.TrimSpace(values[0]) == \"\" {\n\t\treturn nil, fmt.Errorf(\"%w: %s\", ErrEmptyFormField, name)\n\t}\n\n\tfor _, value := range values {\n\t\tif value == \"\" { // if at least one empty value, then perform the strip from the beginning.\n\t\t\tresult := make([]string, 0, len(values))\n\t\t\tfor _, value := range values {\n\t\t\t\tif strings.TrimSpace(value) != \"\" {\n\t\t\t\t\tresult = append(result, value) // we store the value as it is, not space-trimmed.\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif len(result) == 0 {\n\t\t\t\treturn nil, fmt.Errorf(\"%w: %s\", ErrEmptyFormField, name)\n\t\t\t}\n\n\t\t\treturn result, nil\n\t\t}\n\t}\n\n\treturn values, nil\n}\n\n// PostValueMany is like `PostValues` method, it returns the post data of a given key.\n// In addition to `PostValues` though, the returned value is a single string\n// separated by commas on multiple values.\n//\n// See ErrEmptyForm, ErrNotFound and ErrEmptyFormField respectfully.\nfunc (ctx *Context) PostValueMany(name string) (string, error) {\n\tvalues, err := ctx.PostValues(name)\n\tif err != nil || len(values) == 0 {\n\t\treturn \"\", err\n\t}\n\n\treturn strings.Join(values, \",\"), nil\n}\n\n// PostValueDefault returns the last parsed form data from POST, PATCH,\n// or PUT body parameters based on a \"name\".\n//\n// If not found then \"def\" is returned instead.\nfunc (ctx *Context) PostValueDefault(name string, def string) string {\n\tvalues, err := ctx.PostValues(name)\n\tif err != nil || len(values) == 0 {\n\t\treturn def // it returns \"def\" even if it's empty here.\n\t}\n\n\treturn values[len(values)-1]\n}\n\n// PostValueString same as `PostValue` method but it reports\n// an error if the value with key equals to \"name\" does not exist.\nfunc (ctx *Context) PostValueString(name string) (string, error) {\n\tvalues, err := ctx.PostValues(name)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tif len(values) == 0 { // just in case.\n\t\treturn \"\", ErrEmptyForm\n\t}\n\n\treturn values[len(values)-1], nil\n}\n\n// PostValue returns the last parsed form data from POST, PATCH,\n// or PUT body parameters based on a \"name\".\n//\n// See `PostValueMany` too.\nfunc (ctx *Context) PostValue(name string) string {\n\treturn ctx.PostValueDefault(name, \"\")\n}\n\n// PostValueTrim returns the last parsed form data from POST, PATCH,\n// or PUT body parameters based on a \"name\",  without trailing spaces.\nfunc (ctx *Context) PostValueTrim(name string) string {\n\treturn strings.TrimSpace(ctx.PostValue(name))\n}\n\n// PostValueUint returns the last parsed form data matches the given \"name\" key\n// from POST, PATCH, or PUT body request parameters as unassigned number.\n//\n// See ErrEmptyForm, ErrNotFound and ErrEmptyFormField respectfully.\nfunc (ctx *Context) PostValueUint(name string) (uint, error) {\n\tvalue, err := ctx.PostValueString(name)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn strParseUint(value)\n}\n\n// PostValueUint returns the last parsed form data matches the given \"name\" key\n// from POST, PATCH, or PUT body request parameters as unassigned number.\n//\n// See ErrEmptyForm, ErrNotFound and ErrEmptyFormField respectfully.\nfunc (ctx *Context) PostValueUint8(name string) (uint8, error) {\n\tvalue, err := ctx.PostValueString(name)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn strParseUint8(value)\n}\n\n// PostValueUint returns the last parsed form data matches the given \"name\" key\n// from POST, PATCH, or PUT body request parameters as unassigned number.\n//\n// See ErrEmptyForm, ErrNotFound and ErrEmptyFormField respectfully.\nfunc (ctx *Context) PostValueUint16(name string) (uint16, error) {\n\tvalue, err := ctx.PostValueString(name)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn strParseUint16(value)\n}\n\n// PostValueUint returns the last parsed form data matches the given \"name\" key\n// from POST, PATCH, or PUT body request parameters as unassigned number.\n//\n// See ErrEmptyForm, ErrNotFound and ErrEmptyFormField respectfully.\nfunc (ctx *Context) PostValueUint32(name string) (uint32, error) {\n\tvalue, err := ctx.PostValueString(name)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn strParseUint32(value)\n}\n\n// PostValueUint returns the last parsed form data matches the given \"name\" key\n// from POST, PATCH, or PUT body request parameters as unassigned number.\n//\n// See ErrEmptyForm, ErrNotFound and ErrEmptyFormField respectfully.\nfunc (ctx *Context) PostValueUint64(name string) (uint64, error) {\n\tvalue, err := ctx.PostValueString(name)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn strParseUint64(value)\n}\n\n// PostValueInt returns the last parsed form data matches the given \"name\" key\n// from POST, PATCH, or PUT body request parameters as signed number.\n//\n// See ErrEmptyForm, ErrNotFound and ErrEmptyFormField respectfully.\nfunc (ctx *Context) PostValueInt(name string) (int, error) {\n\tvalue, err := ctx.PostValueString(name)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn strParseInt(value)\n}\n\n// PostValueIntDefault same as PostValueInt but if errored it returns\n// the given \"def\" default value.\nfunc (ctx *Context) PostValueIntDefault(name string, def int) int {\n\tvalue, err := ctx.PostValueInt(name)\n\tif err != nil {\n\t\treturn def\n\t}\n\n\treturn value\n}\n\n// PostValueInt8 returns the last parsed form data matches the given \"name\" key\n// from POST, PATCH, or PUT body request parameters as int8.\n//\n// See ErrEmptyForm, ErrNotFound and ErrEmptyFormField respectfully.\nfunc (ctx *Context) PostValueInt8(name string) (int8, error) {\n\tvalue, err := ctx.PostValueString(name)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn strParseInt8(value)\n}\n\n// PostValueInt8Default same as PostValueInt8 but if errored it returns\n// the given \"def\" default value.\nfunc (ctx *Context) PostValueInt8Default(name string, def int8) int8 {\n\tvalue, err := ctx.PostValueInt8(name)\n\tif err != nil {\n\t\treturn def\n\t}\n\n\treturn value\n}\n\n// PostValueInt16 returns the last parsed form data matches the given \"name\" key\n// from POST, PATCH, or PUT body request parameters as int16.\n//\n// See ErrEmptyForm, ErrNotFound and ErrEmptyFormField respectfully.\nfunc (ctx *Context) PostValueInt16(name string) (int16, error) {\n\tvalue, err := ctx.PostValueString(name)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn strParseInt16(value)\n}\n\n// PostValueInt16Default same as PostValueInt16 but if errored it returns\n// the given \"def\" default value.\nfunc (ctx *Context) PostValueInt16Default(name string, def int16) int16 {\n\tvalue, err := ctx.PostValueInt16(name)\n\tif err != nil {\n\t\treturn def\n\t}\n\n\treturn value\n}\n\n// PostValueInt32 returns the last parsed form data matches the given \"name\" key\n// from POST, PATCH, or PUT body request parameters as int32.\n//\n// See ErrEmptyForm, ErrNotFound and ErrEmptyFormField respectfully.\nfunc (ctx *Context) PostValueInt32(name string) (int32, error) {\n\tvalue, err := ctx.PostValueString(name)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn strParseInt32(value)\n}\n\n// PostValueInt32Default same as PostValueInt32 but if errored it returns\n// the given \"def\" default value.\nfunc (ctx *Context) PostValueInt32Default(name string, def int32) int32 {\n\tvalue, err := ctx.PostValueInt32(name)\n\tif err != nil {\n\t\treturn def\n\t}\n\n\treturn value\n}\n\n// PostValueInt64 returns the last parsed form data matches the given \"name\" key\n// from POST, PATCH, or PUT body request parameters as int64.\n//\n// See ErrEmptyForm, ErrNotFound and ErrEmptyFormField respectfully.\nfunc (ctx *Context) PostValueInt64(name string) (int64, error) {\n\tvalue, err := ctx.PostValueString(name)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn strParseInt64(value)\n}\n\n// PostValueInt64Default same as PostValueInt64 but if errored it returns\n// the given \"def\" default value.\nfunc (ctx *Context) PostValueInt64Default(name string, def int64) int64 {\n\tvalue, err := ctx.PostValueInt64(name)\n\tif err != nil {\n\t\treturn def\n\t}\n\n\treturn value\n}\n\n// PostValueFloat32 returns the last parsed form data matches the given \"name\" key\n// from POST, PATCH, or PUT body request parameters as float32.\n//\n// See ErrEmptyForm, ErrNotFound and ErrEmptyFormField respectfully.\nfunc (ctx *Context) PostValueFloat32(name string) (float32, error) {\n\tvalue, err := ctx.PostValueString(name)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn strParseFloat32(value)\n}\n\n// PostValueFloat32Default same as PostValueFloat32 but if errored it returns\n// the given \"def\" default value.\nfunc (ctx *Context) PostValueFloat32Default(name string, def float32) float32 {\n\tvalue, err := ctx.PostValueFloat32(name)\n\tif err != nil {\n\t\treturn def\n\t}\n\n\treturn value\n}\n\n// PostValueFloat64 returns the last parsed form data matches the given \"name\" key\n// from POST, PATCH, or PUT body request parameters as float64.\n//\n// See ErrEmptyForm, ErrNotFound and ErrEmptyFormField respectfully.\nfunc (ctx *Context) PostValueFloat64(name string) (float64, error) {\n\tvalue, err := ctx.PostValueString(name)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn strParseFloat64(value)\n}\n\n// PostValueFloat64Default same as PostValueFloat64 but if errored it returns\n// the given \"def\" default value.\nfunc (ctx *Context) PostValueFloat64Default(name string, def float64) float64 {\n\tvalue, err := ctx.PostValueFloat64(name)\n\tif err != nil {\n\t\treturn def\n\t}\n\n\treturn value\n}\n\n// PostValueComplex64 returns the last parsed form data matches the given \"name\" key\n// from POST, PATCH, or PUT body request parameters as complex64.\n//\n// See ErrEmptyForm, ErrNotFound and ErrEmptyFormField respectfully.\nfunc (ctx *Context) PostValueComplex64(name string) (complex64, error) {\n\tvalue, err := ctx.PostValueString(name)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn strParseComplex64(value)\n}\n\n// PostValueComplex64Default same as PostValueComplex64 but if errored it returns\n// the given \"def\" default value.\nfunc (ctx *Context) PostValueComplex64Default(name string, def complex64) complex64 {\n\tvalue, err := ctx.PostValueComplex64(name)\n\tif err != nil {\n\t\treturn def\n\t}\n\n\treturn value\n}\n\n// PostValueComplex128 returns the last parsed form data matches the given \"name\" key\n// from POST, PATCH, or PUT body request parameters as complex128.\n//\n// See ErrEmptyForm, ErrNotFound and ErrEmptyFormField respectfully.\nfunc (ctx *Context) PostValueComplex128(name string) (complex128, error) {\n\tvalue, err := ctx.PostValueString(name)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn strParseComplex128(value)\n}\n\n// PostValueComplex128Default same as PostValueComplex128 but if errored it returns\n// the given \"def\" default value.\nfunc (ctx *Context) PostValueComplex128Default(name string, def complex128) complex128 {\n\tvalue, err := ctx.PostValueComplex128(name)\n\tif err != nil {\n\t\treturn def\n\t}\n\n\treturn value\n}\n\n// PostValueBool returns the last parsed form data matches the given \"name\" key\n// from POST, PATCH, or PUT body request parameters as bool.\n//\n// See ErrEmptyForm, ErrNotFound and ErrEmptyFormField respectfully.\nfunc (ctx *Context) PostValueBool(name string) (bool, error) {\n\tvalue, err := ctx.PostValueString(name)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\treturn strParseBool(value)\n}\n\n// PostValueWeekday returns the last parsed form data matches the given \"name\" key\n// from POST, PATCH, or PUT body request parameters as time.Weekday.\n//\n// See ErrEmptyForm, ErrNotFound and ErrEmptyFormField respectfully.\nfunc (ctx *Context) PostValueWeekday(name string) (time.Weekday, error) {\n\tvalue, err := ctx.PostValueString(name)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn strParseWeekday(value)\n}\n\n// PostValueTime returns the last parsed form data matches the given \"name\" key\n// from POST, PATCH, or PUT body request parameters as time.Time with the given \"layout\".\n//\n// See ErrEmptyForm, ErrNotFound and ErrEmptyFormField respectfully.\nfunc (ctx *Context) PostValueTime(layout, name string) (time.Time, error) {\n\tvalue, err := ctx.PostValueString(name)\n\tif err != nil {\n\t\treturn time.Time{}, err\n\t}\n\n\treturn strParseTime(layout, value)\n}\n\n// PostValueSimpleDate returns the last parsed form data matches the given \"name\" key\n// from POST, PATCH, or PUT body request parameters as time.Time with \"2006/01/02\"\n// or \"2006-01-02\" time layout.\n//\n// See ErrEmptyForm, ErrNotFound and ErrEmptyFormField respectfully.\nfunc (ctx *Context) PostValueSimpleDate(name string) (time.Time, error) {\n\tvalue, err := ctx.PostValueString(name)\n\tif err != nil {\n\t\treturn time.Time{}, err\n\t}\n\n\treturn strParseSimpleDate(value)\n}\n\n// FormFile returns the first uploaded file that received from the client.\n//\n// The default form's memory maximum size is 32MB, it can be changed by the\n// `iris#WithPostMaxMemory` configurator at main configuration passed on `app.Run`'s second argument.\n//\n// Example: https://github.com/kataras/iris/tree/main/_examples/file-server/upload-file\nfunc (ctx *Context) FormFile(key string) (multipart.File, *multipart.FileHeader, error) {\n\t// we don't have access to see if the request is body stream\n\t// and then the ParseMultipartForm can be useless\n\t// here but do it in order to apply the post limit,\n\t// the internal request.FormFile will not do it if that's filled\n\t// and it's not a stream body.\n\tif err := ctx.request.ParseMultipartForm(ctx.app.ConfigurationReadOnly().GetPostMaxMemory()); err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\treturn ctx.request.FormFile(key)\n}\n\n// FormFiles same as FormFile but may return multiple file inputs based on a key, e.g. \"files[]\".\nfunc (ctx *Context) FormFiles(key string, before ...func(*Context, *multipart.FileHeader) bool) (files []multipart.File, headers []*multipart.FileHeader, err error) {\n\terr = ctx.request.ParseMultipartForm(ctx.app.ConfigurationReadOnly().GetPostMaxMemory())\n\tif err != nil {\n\t\treturn\n\t}\n\n\tif ctx.request.MultipartForm != nil {\n\t\tfhs := ctx.request.MultipartForm.File\n\t\tif n := len(fhs); n > 0 {\n\t\t\tfiles = make([]multipart.File, 0, n)\n\t\t\theaders = make([]*multipart.FileHeader, 0, n)\n\n\t\tinnerLoop:\n\t\t\tfor _, header := range fhs[key] {\n\t\t\t\theader.Filename = filepath.Base(header.Filename)\n\n\t\t\t\tfor _, b := range before {\n\t\t\t\t\tif !b(ctx, header) {\n\t\t\t\t\t\tcontinue innerLoop\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfile, fErr := header.Open()\n\t\t\t\tif fErr != nil { // exit on first error but return the succeed.\n\t\t\t\t\treturn files, headers, fErr\n\t\t\t\t}\n\n\t\t\t\tfiles = append(files, file)\n\t\t\t\theaders = append(headers, header)\n\t\t\t}\n\t\t}\n\n\t\treturn\n\t}\n\n\treturn nil, nil, http.ErrMissingFile\n}\n\nvar (\n\t// ValidFileNameRegexp is used to validate the user input by using a regular expression.\n\t// See `Context.UploadFormFiles` method.\n\tValidFilenameRegexp = regexp.MustCompile(`^[a-zA-Z0-9_\\-\\.]+$`)\n\t// ValidExtensionRegexp acts as an allowlist of valid extensions. It's optional. Defaults to nil (all file extensions are allowed to be uploaded).\n\t// See `Context.UploadFormFiles` method.\n\tValidExtensionRegexp *regexp.Regexp\n)\n\n// SafeFilename returns a safe filename based on the given name.\n//   - Using filepath.Base and filepath.ToSlash: This ensures that only the base file name is used, without any directory components,\n//     and converts all separators to slashes. This is a good practice to prevent directory traversal.\n//   - Regular Expression for Filenames: The ValidFilenameRegexp ensures that filenames are restricted to a safe character set.\n//     This helps prevent the use of special characters that could lead to path traversal or other types of injection attacks.\n//   - Extension Validation: If you have a ValidExtensionRegexp, it would further ensure that the file has an expected and safe extension, which is another good practice.\n//   - Canonical Path Check: By evaluating symlinks and ensuring that the destination path starts with the canonical destination directory, you’re adding.\n//\n// It returns the safe prefix directory (destination directory), the safe filename, a boolean indicating whether the filename is safe, and an error if any.\nfunc SafeFilename(prefixDir string, name string) (string, string, bool, error) {\n\t// Security fix for go < 1.17.5:\n\t// Reported by Kirill Efimov (snyk.io) through security reports.\n\tfilename := filepath.Base(filepath.ToSlash(name))\n\n\t// CWE-99.\n\n\t// Sanitize the user input by using a regular expression\n\t// and an allowlist of valid extensions\n\tisValidFilename := ValidFilenameRegexp.MatchString(filename)\n\tif !isValidFilename {\n\t\t// Reject the input as it is invalid or unsafe.\n\t\treturn prefixDir, name, false, nil\n\t}\n\n\tif ValidExtensionRegexp != nil && !ValidExtensionRegexp.MatchString(filename) {\n\t\t// Reject the input as it is invalid or unsafe.\n\t\treturn prefixDir, name, false, nil\n\t}\n\n\tvar destPath string\n\tif prefixDir != \"\" {\n\t\t// Join the sanitized input with the destination directory.\n\t\tdestPath = filepath.Join(prefixDir, filename)\n\n\t\t// Get the canonical path of the destination directory.\n\t\tcanonicalDestDir, err := filepath.EvalSymlinks(prefixDir) // the prefix dir should exists.\n\t\tif err != nil {\n\t\t\treturn prefixDir, name, false, fmt.Errorf(\"dest directory: %s: eval symlinks: %w\", prefixDir, err)\n\t\t}\n\n\t\t// Check if the destination path is within the destination directory.\n\t\tif !strings.HasPrefix(destPath, canonicalDestDir) {\n\t\t\t// Reject the input as it is a path traversal attempt.\n\t\t\treturn prefixDir, name, false, nil\n\t\t}\n\t}\n\n\treturn destPath, filename, true, nil\n}\n\n// UploadFormFiles uploads any received file(s) from the client\n// to the system physical location \"destDirectory\".\n//\n// The second optional argument \"before\" gives caller the chance to\n// modify or cancel the *miltipart.FileHeader before saving to the disk,\n// it can be used to change a file's name based on the current request,\n// all FileHeader's options can be changed. You can ignore it if\n// you don't need to use this capability before saving a file to the disk.\n//\n// Note that it doesn't check if request body streamed.\n//\n// Returns the copied length as int64 and\n// a not nil error if at least one new file\n// can't be created due to the operating system's permissions or\n// http.ErrMissingFile if no file received.\n//\n// If you want to receive & accept files and manage them manually you can use the `context#FormFile`\n// instead and create a copy function that suits your needs or use the `SaveFormFile` method,\n// the below is for generic usage.\n//\n// The default form's memory maximum size is 32MB, it can be changed by\n// the `WithPostMaxMemory` configurator or by `SetMaxRequestBodySize` or\n// by the `LimitRequestBodySize` middleware (depends the use case).\n//\n// See `FormFile` and `FormFiles` to a more controlled way to receive a file.\n//\n// Example: https://github.com/kataras/iris/tree/main/_examples/file-server/upload-files\nfunc (ctx *Context) UploadFormFiles(destDirectory string, before ...func(*Context, *multipart.FileHeader) bool) (uploaded []*multipart.FileHeader, n int64, err error) {\n\terr = ctx.request.ParseMultipartForm(ctx.app.ConfigurationReadOnly().GetPostMaxMemory())\n\tif err != nil {\n\t\treturn nil, 0, err\n\t}\n\n\tif ctx.request.MultipartForm != nil {\n\t\tif fhs := ctx.request.MultipartForm.File; fhs != nil {\n\t\t\tfor _, files := range fhs {\n\t\t\tinnerLoop:\n\t\t\t\tfor _, file := range files {\n\t\t\t\t\tfor _, b := range before {\n\t\t\t\t\t\tif !b(ctx, file) {\n\t\t\t\t\t\t\tcontinue innerLoop\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tdestPath, filename, ok, err := SafeFilename(destDirectory, file.Filename)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn nil, 0, err\n\t\t\t\t\t}\n\t\t\t\t\tif !ok {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\n\t\t\t\t\tfile.Filename = filename\n\n\t\t\t\t\tn0, err0 := ctx.SaveFormFile(file, destPath)\n\t\t\t\t\tif err0 != nil {\n\t\t\t\t\t\treturn nil, 0, err0\n\t\t\t\t\t}\n\t\t\t\t\tn += n0\n\n\t\t\t\t\tuploaded = append(uploaded, file)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn uploaded, n, nil\n\t\t}\n\t}\n\n\treturn nil, 0, http.ErrMissingFile\n}\n\n// SaveFormFile saves a result of `FormFile` to the \"dest\" disk full path (directory + filename).\n// See `FormFile` and `UploadFormFiles` too.\nfunc (ctx *Context) SaveFormFile(fh *multipart.FileHeader, dest string) (int64, error) {\n\tsrc, err := fh.Open()\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tdefer src.Close()\n\n\tout, err := os.Create(dest)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tdefer out.Close()\n\n\treturn io.Copy(out, src)\n}\n\n// AbsoluteURI parses the \"s\" and returns its absolute URI form.\nfunc (ctx *Context) AbsoluteURI(s string) string {\n\tif s == \"\" {\n\t\treturn \"\"\n\t}\n\n\tuserInfo := \"\"\n\tif s[0] == '@' {\n\t\tendUserInfoIdx := strings.IndexByte(s, '/')\n\t\tif endUserInfoIdx > 0 && len(s) > endUserInfoIdx {\n\t\t\tuserInfo = s[1:endUserInfoIdx] + \"@\"\n\t\t\ts = s[endUserInfoIdx:]\n\t\t}\n\t}\n\n\tif s[0] == '/' {\n\t\tscheme := ctx.request.URL.Scheme\n\t\tif scheme == \"\" {\n\t\t\tif ctx.request.TLS != nil {\n\t\t\t\tscheme = \"https:\"\n\t\t\t} else {\n\t\t\t\tscheme = \"http:\"\n\t\t\t}\n\t\t}\n\n\t\thost := ctx.Host()\n\n\t\treturn scheme + \"//\" + userInfo + host + path.Clean(s)\n\t}\n\n\tif u, err := url.Parse(s); err == nil {\n\t\tr := ctx.request\n\n\t\tif u.Scheme == \"\" && u.Host == \"\" {\n\t\t\toldpath := r.URL.Path\n\t\t\tif oldpath == \"\" {\n\t\t\t\toldpath = \"/\"\n\t\t\t}\n\n\t\t\tif s == \"\" || s[0] != '/' {\n\t\t\t\tolddir, _ := path.Split(oldpath)\n\t\t\t\ts = olddir + s\n\t\t\t}\n\n\t\t\tvar query string\n\t\t\tif i := strings.Index(s, \"?\"); i != -1 {\n\t\t\t\ts, query = s[:i], s[i:]\n\t\t\t}\n\n\t\t\t// clean up but preserve trailing slash\n\t\t\ttrailing := strings.HasSuffix(s, \"/\")\n\t\t\ts = path.Clean(s)\n\t\t\tif trailing && !strings.HasSuffix(s, \"/\") {\n\t\t\t\ts += \"/\"\n\t\t\t}\n\t\t\ts += query\n\t\t}\n\t}\n\n\treturn s\n}\n\n// Redirect sends a redirect response to the client\n// of an absolute or relative target URL.\n// It accepts 2 input arguments, a string and an optional integer.\n// The first parameter is the target url to redirect.\n// The second one is the HTTP status code should be sent\n// among redirection response,\n// If the second parameter is missing, then it defaults to 302 (StatusFound).\n// It can be set to 301 (Permant redirect), StatusTemporaryRedirect(307)\n// or 303 (StatusSeeOther) if POST method.\nfunc (ctx *Context) Redirect(urlToRedirect string, statusHeader ...int) {\n\tctx.StopExecution()\n\t// get the previous status code given by the end-developer.\n\tstatus := ctx.GetStatusCode()\n\tif status < 300 { // the previous is not a RCF-valid redirect status.\n\t\tstatus = 0\n\t}\n\n\tif len(statusHeader) > 0 {\n\t\t// check if status code is passed via receivers.\n\t\tif s := statusHeader[0]; s > 0 {\n\t\t\tstatus = s\n\t\t}\n\t}\n\tif status == 0 {\n\t\t// if status remains zero then default it.\n\t\t// a 'temporary-redirect-like' which works better than for our purpose\n\t\tstatus = http.StatusFound\n\t}\n\n\thttp.Redirect(ctx.writer, ctx.request, urlToRedirect, status)\n}\n\n//  +------------------------------------------------------------+\n//  | Body Readers                                               |\n//  +------------------------------------------------------------+\n\n// SetMaxRequestBodySize sets a limit to the request body size\n// should be called before reading the request body from the client.\nfunc (ctx *Context) SetMaxRequestBodySize(limitOverBytes int64) {\n\tctx.request.Body = http.MaxBytesReader(ctx.writer, ctx.request.Body, limitOverBytes)\n}\n\nvar emptyFunc = func() {}\n\n// GetBody reads and returns the request body.\nfunc GetBody(r *http.Request, resetBody bool) ([]byte, func(), error) {\n\tdata, err := io.ReadAll(r.Body)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tif resetBody {\n\t\t// * remember, Request.Body has no Bytes(), we have to consume them first\n\t\t// and after re-set them to the body, this is the only solution.\n\t\treturn data, func() {\n\t\t\tsetBody(r, data)\n\t\t}, nil\n\t}\n\n\treturn data, emptyFunc, nil\n}\n\nfunc setBody(r *http.Request, data []byte) {\n\tr.Body = io.NopCloser(bytes.NewBuffer(data))\n}\n\nconst disableRequestBodyConsumptionContextKey = \"iris.request.body.record\"\n\n// RecordRequestBody same as the Application's DisableBodyConsumptionOnUnmarshal\n// configuration field but acts only for the current request.\n// It makes the request body readable more than once.\nfunc (ctx *Context) RecordRequestBody(b bool) {\n\tctx.values.Set(disableRequestBodyConsumptionContextKey, b)\n}\n\n// IsRecordingBody reports whether the request body can be readen multiple times.\nfunc (ctx *Context) IsRecordingBody() bool {\n\tif ctx.app.ConfigurationReadOnly().GetDisableBodyConsumptionOnUnmarshal() {\n\t\treturn true\n\t}\n\n\tvalue, _ := ctx.values.GetBool(disableRequestBodyConsumptionContextKey)\n\treturn value\n}\n\n// GetBody reads and returns the request body.\n// The default behavior for the http request reader is to consume the data readen\n// but you can change that behavior by passing the `WithoutBodyConsumptionOnUnmarshal` Iris option\n// or by calling the `RecordRequestBody` method.\n//\n// However, whenever you can use the `ctx.Request().Body` instead.\nfunc (ctx *Context) GetBody() ([]byte, error) {\n\tbody, release, err := GetBody(ctx.request, ctx.IsRecordingBody())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\trelease()\n\treturn body, nil\n}\n\n// Validator is the validator for request body on Context methods such as\n// ReadJSON, ReadMsgPack, ReadXML, ReadYAML, ReadForm, ReadQuery, ReadBody and e.t.c.\ntype Validator interface {\n\tStruct(any) error\n\t// If community asks for more than a struct validation on JSON, XML, MsgPack, Form, Query and e.t.c\n\t// then we should add more methods here, alternative approach would be to have a\n\t// `Validator:Validate(any) error` and a map[reflect.Kind]Validator instead.\n}\n\n// UnmarshalBody reads the request's body and binds it to a value or pointer of any type\n// Examples of usage: context.ReadJSON, context.ReadXML.\n//\n// Example: https://github.com/kataras/iris/blob/main/_examples/request-body/read-custom-via-unmarshaler/main.go\nfunc (ctx *Context) UnmarshalBody(outPtr any, unmarshaler Unmarshaler) error {\n\tif ctx.request.Body == nil {\n\t\treturn fmt.Errorf(\"unmarshal: empty body: %w\", ErrNotFound)\n\t}\n\n\trawData, err := ctx.GetBody()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif decoderWithCtx, ok := outPtr.(BodyDecoderWithContext); ok {\n\t\treturn decoderWithCtx.DecodeContext(ctx.request.Context(), rawData)\n\t}\n\n\t// check if the v contains its own decode\n\t// in this case the v should be a pointer also,\n\t// but this is up to the user's custom Decode implementation*\n\t//\n\t// See 'BodyDecoder' for more.\n\tif decoder, isDecoder := outPtr.(BodyDecoder); isDecoder {\n\t\treturn decoder.Decode(rawData)\n\t}\n\n\t// // check if v is already a pointer, if yes then pass as it's\n\t// if reflect.TypeOf(v).Kind() == reflect.Ptr {\n\t// \treturn unmarshaler.Unmarshal(rawData, v)\n\t// } <- no need for that, ReadJSON is documented enough to receive a pointer,\n\t// we don't need to reduce the performance here by using the reflect.TypeOf method.\n\n\t// f the v doesn't contains a self-body decoder use the custom unmarshaler to bind the body.\n\terr = unmarshaler.Unmarshal(rawData, outPtr)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn ctx.app.Validate(outPtr)\n}\n\n// JSONReader holds the JSON decode options of the `Context.ReadJSON, ReadBody` methods.\ntype JSONReader struct { // Note(@kataras): struct instead of optional funcs to keep consistently with the encoder options.\n\t// DisallowUnknownFields causes the json decoder to return an error when the destination\n\t// is a struct and the input contains object keys which do not match any\n\t// non-ignored, exported fields in the destination.\n\tDisallowUnknownFields bool\n\t// If set to true then a bit faster json decoder is used instead,\n\t// note that if this is true then it overrides\n\t// the Application's EnableOptimizations configuration field.\n\tOptimize bool\n\t// This field only applies to the ReadJSONStream.\n\t// The Optimize field has no effect when this is true.\n\t// If set to true the request body stream MUST start with a `[`\n\t// and end with `]` literals, example:\n\t//  [\n\t//   {\"username\":\"john\"},\n\t//   {\"username\": \"makis\"},\n\t//   {\"username\": \"george\"}\n\t//  ]\n\t// Defaults to false: decodes a json object one by one, example:\n\t//  {\"username\":\"john\"}\n\t//  {\"username\": \"makis\"}\n\t//  {\"username\": \"george\"}\n\tArrayStream bool\n}\n\nvar ReadJSON = func(ctx *Context, outPtr any, opts ...JSONReader) error {\n\tvar body io.Reader\n\n\tif ctx.IsRecordingBody() {\n\t\tdata, err := io.ReadAll(ctx.request.Body)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tsetBody(ctx.request, data)\n\t\tbody = bytes.NewReader(data)\n\t} else {\n\t\tbody = ctx.request.Body\n\t}\n\n\tdecoder := json.NewDecoder(body)\n\t// decoder := gojson.NewDecoder(ctx.Request().Body)\n\tif len(opts) > 0 {\n\t\toptions := opts[0]\n\n\t\tif options.DisallowUnknownFields {\n\t\t\tdecoder.DisallowUnknownFields()\n\t\t}\n\t}\n\n\tif err := decoder.Decode(&outPtr); err != nil {\n\t\treturn err\n\t}\n\n\treturn ctx.app.Validate(outPtr)\n}\n\n// ReadJSON reads JSON from request's body and binds it to a value of any json-valid type.\n//\n// Example: https://github.com/kataras/iris/blob/main/_examples/request-body/read-json/main.go\nfunc (ctx *Context) ReadJSON(outPtr any, opts ...JSONReader) error {\n\treturn ReadJSON(ctx, outPtr, opts...)\n}\n\n// ReadJSONStream is an alternative of ReadJSON which can reduce the memory load\n// by reading only one json object every time.\n// It buffers just the content required for a single json object instead of the entire string,\n// and discards that once it reaches an end of value that can be decoded into the provided struct\n// inside the onDecode's DecodeFunc.\n//\n// It accepts a function which accepts the json Decode function and returns an error.\n// The second variadic argument is optional and can be used to customize the decoder even further.\n//\n// Example: https://github.com/kataras/iris/blob/main/_examples/request-body/read-json-stream/main.go\nfunc (ctx *Context) ReadJSONStream(onDecode func(DecodeFunc) error, opts ...JSONReader) error {\n\tdecoder := json.NewDecoder(ctx.request.Body)\n\n\tif len(opts) > 0 && opts[0].ArrayStream {\n\t\t_, err := decoder.Token() // read open bracket.\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tfor decoder.More() { // hile the array contains values.\n\t\t\tif err = onDecode(decoder.Decode); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\t_, err = decoder.Token() // read closing bracket.\n\t\treturn err\n\t}\n\n\t// while the array contains values\n\tfor decoder.More() {\n\t\tif err := onDecode(decoder.Decode); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// ReadXML reads XML from request's body and binds it to a value of any xml-valid type.\n//\n// Example: https://github.com/kataras/iris/blob/main/_examples/request-body/read-xml/main.go\nfunc (ctx *Context) ReadXML(outPtr any) error {\n\treturn ctx.UnmarshalBody(outPtr, UnmarshalerFunc(xml.Unmarshal))\n}\n\n// ReadYAML reads YAML from request's body and binds it to the \"outPtr\" value.\n//\n// Example: https://github.com/kataras/iris/blob/main/_examples/request-body/read-yaml/main.go\nfunc (ctx *Context) ReadYAML(outPtr any) error {\n\treturn ctx.UnmarshalBody(outPtr, UnmarshalerFunc(yaml.Unmarshal))\n}\n\nvar (\n\t// IsErrEmptyJSON reports whether the given \"err\" is caused by a\n\t// Client.ReadJSON call when the request body was empty or\n\t// didn't start with { or [.\n\tIsErrEmptyJSON = func(err error) bool {\n\t\tif err == nil {\n\t\t\treturn false\n\t\t}\n\n\t\tif errors.Is(err, io.EOF) {\n\t\t\treturn true\n\t\t}\n\n\t\tif v, ok := err.(*json.SyntaxError); ok {\n\t\t\t// standard go json encoder error.\n\t\t\treturn v.Offset == 0 && v.Error() == \"unexpected end of JSON input\"\n\t\t}\n\n\t\terrMsg := err.Error()\n\t\t// 3rd party pacakges:\n\t\treturn strings.Contains(errMsg, \"readObjectStart: expect {\") || strings.Contains(errMsg, \"readArrayStart: expect [\")\n\t}\n\n\t// IsErrPath can be used at `context#ReadForm` and `context#ReadQuery`.\n\t// It reports whether the incoming error\n\t// can be ignored when server allows unknown post values to be sent by the client.\n\t//\n\t// A shortcut for the `schema#IsErrPath`.\n\tIsErrPath = schema.IsErrPath\n\n\t// IsErrPathCRSFToken reports whether the given \"err\" is caused\n\t// by unknown key error on \"csrf.token\". See `context#ReadForm` for more.\n\tIsErrPathCRSFToken = func(err error) bool {\n\t\tif err == nil || CSRFTokenFormKey == \"\" {\n\t\t\treturn false\n\t\t}\n\n\t\tif m, ok := err.(schema.MultiError); ok {\n\t\t\tif csrfErr, hasCSRFToken := m[CSRFTokenFormKey]; hasCSRFToken {\n\t\t\t\t_, is := csrfErr.(schema.UnknownKeyError)\n\t\t\t\treturn is\n\n\t\t\t}\n\t\t}\n\n\t\treturn false\n\t}\n\n\t// ErrEmptyForm is returned by\n\t// - `context#ReadForm`\n\t// - `context#ReadQuery`\n\t// - `context#ReadBody`\n\t// when the request data (form, query and body respectfully) is empty.\n\tErrEmptyForm = errors.New(\"empty form\")\n\n\t// ErrEmptyFormField reports whether a specific field exists but it's empty.\n\t// Usage: errors.Is(err, ErrEmptyFormField)\n\t// See postValue method. It's only returned on parsed post value methods.\n\tErrEmptyFormField = errors.New(\"empty form field\")\n\n\t// ConnectionCloseErrorSubstr if at least one of the given\n\t// substrings are found in a net.OpError:os.SyscallError error type\n\t// on `IsErrConnectionReset` then the function will report true.\n\tConnectionCloseErrorSubstr = []string{\n\t\t\"broken pipe\",\n\t\t\"connection reset by peer\",\n\t}\n\n\t// IsErrConnectionClosed reports whether the given \"err\"\n\t// is caused because of a broken connection.\n\tIsErrConnectionClosed = func(err error) bool {\n\t\tif err == nil {\n\t\t\treturn false\n\t\t}\n\n\t\tif opErr, ok := err.(*net.OpError); ok {\n\t\t\tif syscallErr, ok := opErr.Err.(*os.SyscallError); ok {\n\t\t\t\terrStr := strings.ToLower(syscallErr.Error())\n\t\t\t\tfor _, s := range ConnectionCloseErrorSubstr {\n\t\t\t\t\tif strings.Contains(errStr, s) {\n\t\t\t\t\t\treturn true\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn false\n\t}\n)\n\n// CSRFTokenFormKey the CSRF token key of the form data.\n//\n// See ReadForm method for more.\nconst CSRFTokenFormKey = \"csrf.token\"\n\n// ReadForm binds the request body of a form to the \"formObject\".\n// It supports any kind of type, including custom structs.\n// It will return nothing if request data are empty.\n// The struct field tag is \"form\".\n// If tag is missing it tries to bind using the field's name.\n// To ignore a specific field from binding use tag value \"-\".\n// Note that it will return nil error on empty form data if `Configuration.FireEmptyFormError`\n// is false (as defaulted) in this case the caller should check the pointer to\n// see if something was actually binded.\n//\n// If a client sent an unknown field, this method will return an error,\n// in order to ignore that error use the `err != nil && !iris.IsErrPath(err)`.\n//\n// As of 15 Aug 2022, ReadForm does not return an error over unknown CSRF token form key,\n// to change this behavior globally, set the `context.CSRFTokenFormKey` to an empty value.\n//\n// Example: https://github.com/kataras/iris/blob/main/_examples/request-body/read-form/main.go\nfunc (ctx *Context) ReadForm(formObject any) error {\n\tvalues := ctx.FormValues()\n\tif len(values) == 0 {\n\t\tif ctx.app.ConfigurationReadOnly().GetFireEmptyFormError() {\n\t\t\treturn ErrEmptyForm\n\t\t}\n\t\treturn nil\n\t}\n\n\terr := schema.DecodeForm(values, formObject)\n\tif err != nil && !IsErrPathCRSFToken(err) {\n\t\treturn err\n\t}\n\n\treturn ctx.app.Validate(formObject)\n}\n\ntype (\n\t// MultipartRelated is the result of the context.ReadMultipartRelated method.\n\tMultipartRelated struct {\n\t\t// ContentIDs keeps an ordered list of all the\n\t\t// content-ids of the multipart related request.\n\t\tContentIDs []string\n\t\t// Contents keeps each part's information by Content-ID.\n\t\t// Contents holds each of the multipart/related part's data.\n\t\tContents map[string]MultipartRelatedContent\n\t}\n\n\t// MultipartRelatedContent holds a multipart/related part's id, header and body.\n\tMultipartRelatedContent struct {\n\t\t// ID holds the Content-ID.\n\t\tID string\n\t\t// Headers holds the part's request headers.\n\t\tHeaders map[string][]string\n\t\t// Body holds the part's body.\n\t\tBody []byte\n\t}\n)\n\n// ReadMultipartRelated returns a structure which contain\n// information about each part (id, headers, body).\n//\n// Read more at: https://www.ietf.org/rfc/rfc2387.txt.\n//\n// Example request (2387/5.2 Text/X-Okie):\n// Content-Type: Multipart/Related; boundary=example-2;\n// start=\"<950118.AEBH@XIson.com>\"\n// type=\"Text/x-Okie\"\n//\n// --example-2\n// Content-Type: Text/x-Okie; charset=iso-8859-1;\n// declaration=\"<950118.AEB0@XIson.com>\"\n// Content-ID: <950118.AEBH@XIson.com>\n// Content-Description: Document\n//\n// {doc}\n// This picture was taken by an automatic camera mounted ...\n// {image file=cid:950118.AECB@XIson.com}\n// {para}\n// Now this is an enlargement of the area ...\n// {image file=cid:950118:AFDH@XIson.com}\n// {/doc}\n// --example-2\n// Content-Type: image/jpeg\n// Content-ID: <950118.AFDH@XIson.com>\n// Content-Transfer-Encoding: BASE64\n// Content-Description: Picture A\n//\n// [encoded jpeg image]\n// --example-2\n// Content-Type: image/jpeg\n// Content-ID: <950118.AECB@XIson.com>\n// Content-Transfer-Encoding: BASE64\n// Content-Description: Picture B\n//\n// [encoded jpeg image]\n// --example-2--\nfunc (ctx *Context) ReadMultipartRelated() (MultipartRelated, error) {\n\tcontentType, params, err := mime.ParseMediaType(ctx.GetHeader(ContentTypeHeaderKey))\n\tif err != nil {\n\t\treturn MultipartRelated{}, err\n\t}\n\n\tif !strings.HasPrefix(contentType, ContentMultipartRelatedHeaderValue) {\n\t\treturn MultipartRelated{}, ErrEmptyForm\n\t}\n\n\tvar (\n\t\tcontentIDs []string\n\t\tcontents   = make(map[string]MultipartRelatedContent)\n\t)\n\n\tif ctx.IsRecordingBody() {\n\t\t// * remember, Request.Body has no Bytes(), we have to consume them first\n\t\t// and after re-set them to the body, this is the only solution.\n\t\tbody, restoreBody, err := GetBody(ctx.request, true)\n\t\tif err != nil {\n\t\t\treturn MultipartRelated{}, fmt.Errorf(\"multipart related: body copy because of iris.Configuration.DisableBodyConsumptionOnUnmarshal: %w\", err)\n\t\t}\n\t\tsetBody(ctx.request, body) // so the ctx.request.Body works\n\t\tdefer restoreBody()        // so the next ctx.GetBody calls work.\n\t}\n\n\tmultipartReader := multipart.NewReader(ctx.request.Body, params[\"boundary\"])\n\tfor {\n\t\tpart, err := multipartReader.NextPart()\n\t\tif err != nil {\n\t\t\tif err == io.EOF {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\treturn MultipartRelated{}, fmt.Errorf(\"multipart related: next part: %w\", err)\n\t\t}\n\t\tdefer part.Close()\n\n\t\tb, err := io.ReadAll(part)\n\t\tif err != nil {\n\t\t\treturn MultipartRelated{}, fmt.Errorf(\"multipart related: next part: read: %w\", err)\n\t\t}\n\n\t\tcontentID := part.Header.Get(\"Content-ID\")\n\t\tcontentIDs = append(contentIDs, contentID)\n\t\tcontents[contentID] = MultipartRelatedContent{ // replace if same Content-ID appears, which it shouldn't.\n\t\t\tID:      contentID,\n\t\t\tHeaders: http.Header(part.Header),\n\t\t\tBody:    b,\n\t\t}\n\t}\n\n\tif len(contents) != len(contentIDs) {\n\t\tcontentIDs = distinctStrings(contentIDs)\n\t}\n\n\tresult := MultipartRelated{\n\t\tContentIDs: contentIDs,\n\t\tContents:   contents,\n\t}\n\treturn result, nil\n}\n\nfunc distinctStrings(values []string) []string {\n\tseen := make(map[string]struct{}, len(values))\n\tresult := make([]string, 0, len(values))\n\n\tfor _, val := range values {\n\t\tif _, ok := seen[val]; !ok {\n\t\t\tseen[val] = struct{}{}\n\t\t\tresult = append(result, val)\n\t\t}\n\t}\n\n\treturn result\n}\n\n// ReadQuery binds URL Query to \"ptr\". The struct field tag is \"url\".\n// If tag is missing it tries to bind using the field's name.\n// To ignore a specific field from binding use tag value \"-\".\n//\n// Example: https://github.com/kataras/iris/blob/main/_examples/request-body/read-query/main.go\nfunc (ctx *Context) ReadQuery(ptr any) error {\n\tvalues := ctx.getQuery()\n\tif len(values) == 0 {\n\t\tif ctx.app.ConfigurationReadOnly().GetFireEmptyFormError() {\n\t\t\treturn ErrEmptyForm\n\t\t}\n\t\treturn nil\n\t}\n\n\terr := schema.DecodeQuery(values, ptr)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn ctx.app.Validate(ptr)\n}\n\n// ReadHeaders binds request headers to \"ptr\". The struct field tag is \"header\".\n//\n// Example: https://github.com/kataras/iris/blob/main/_examples/request-body/read-headers/main.go\nfunc (ctx *Context) ReadHeaders(ptr any) error {\n\terr := schema.DecodeHeaders(ctx.request.Header, ptr)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn ctx.app.Validate(ptr)\n}\n\n// ReadParams binds URI Dynamic Path Parameters to \"ptr\". The struct field tag is \"param\".\n//\n// Example: https://github.com/kataras/iris/blob/main/_examples/request-body/read-params/main.go\nfunc (ctx *Context) ReadParams(ptr any) error {\n\tn := ctx.params.Len()\n\tif n == 0 {\n\t\treturn nil\n\t}\n\n\tvalues := make(map[string][]string, n)\n\tctx.params.Visit(func(key string, value string) {\n\t\t// []string on path parameter, e.g.\n\t\t// /.../{tail:path}\n\t\t// Tail []string `param:\"tail\"`\n\t\tvalues[key] = strings.Split(value, \"/\")\n\t})\n\n\terr := schema.DecodeParams(values, ptr)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn ctx.app.Validate(ptr)\n}\n\n// ReadURL is a shortcut of ReadParams and ReadQuery.\n// It binds dynamic path parameters and URL query parameters\n// to the \"ptr\" pointer struct value.\n// The struct fields may contain \"url\" or \"param\" binding tags.\n// If a validator exists then it validates the result too.\nfunc (ctx *Context) ReadURL(ptr any) error {\n\tvalues := make(map[string][]string, ctx.params.Len())\n\tctx.params.Visit(func(key string, value string) {\n\t\tvalues[key] = strings.Split(value, \"/\")\n\t})\n\n\tfor k, v := range ctx.getQuery() {\n\t\tvalues[k] = append(values[k], v...)\n\t}\n\n\t// Decode using all available binding tags (url, header, param).\n\terr := schema.Decode(values, ptr)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn ctx.app.Validate(ptr)\n}\n\n// ReadProtobuf binds the body to the \"ptr\" of a proto Message and returns any error.\n// Look `ReadJSONProtobuf` too.\nfunc (ctx *Context) ReadProtobuf(ptr proto.Message) error {\n\trawData, err := ctx.GetBody()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn proto.Unmarshal(rawData, ptr)\n}\n\n// ProtoUnmarshalOptions is a type alias for protojson.UnmarshalOptions.\ntype ProtoUnmarshalOptions = protojson.UnmarshalOptions\n\nvar defaultProtobufUnmarshalOptions ProtoUnmarshalOptions\n\n// ReadJSONProtobuf reads a JSON body request into the given \"ptr\" proto.Message.\n// Look `ReadProtobuf` too.\nfunc (ctx *Context) ReadJSONProtobuf(ptr proto.Message, opts ...ProtoUnmarshalOptions) error {\n\trawData, err := ctx.GetBody()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\topt := defaultProtobufUnmarshalOptions\n\tif len(opts) > 0 {\n\t\topt = opts[0]\n\t}\n\n\treturn opt.Unmarshal(rawData, ptr)\n}\n\n// ReadMsgPack binds the request body of msgpack format to the \"ptr\" and returns any error.\nfunc (ctx *Context) ReadMsgPack(ptr any) error {\n\trawData, err := ctx.GetBody()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\terr = msgpack.Unmarshal(rawData, ptr)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn ctx.app.Validate(ptr)\n}\n\n// ReadBody binds the request body to the \"ptr\" depending on the HTTP Method and the Request's Content-Type.\n// If a GET method request then it reads from a form (or URL Query), otherwise\n// it tries to match (depending on the request content-type) the data format e.g.\n// JSON, Protobuf, MsgPack, XML, YAML, MultipartForm and binds the result to the \"ptr\".\n// As a special case if the \"ptr\" was a pointer to string or []byte\n// then it will bind it to the request body as it is.\nfunc (ctx *Context) ReadBody(ptr any) error {\n\t// If the ptr is string or byte, read the body as it's.\n\tswitch v := ptr.(type) {\n\tcase *string:\n\t\tb, err := ctx.GetBody()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t*v = string(b)\n\tcase *[]byte:\n\t\tb, err := ctx.GetBody()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tcopy(*v, b)\n\t}\n\n\tif ctx.Method() == http.MethodGet {\n\t\tif ctx.Request().URL.RawQuery != \"\" {\n\t\t\t// try read from query.\n\t\t\treturn ctx.ReadQuery(ptr)\n\t\t}\n\n\t\t// otherwise use the ReadForm,\n\t\t// it's actually the same except\n\t\t// ReadQuery will not fire errors on:\n\t\t// 1. unknown or empty url query parameters\n\t\t// 2. empty query or form (if FireEmptyFormError is enabled).\n\t\treturn ctx.ReadForm(ptr)\n\t}\n\n\tswitch ctx.GetContentTypeRequested() {\n\tcase ContentXMLHeaderValue, ContentXMLUnreadableHeaderValue:\n\t\treturn ctx.ReadXML(ptr)\n\t\t// \"%v reflect.Indirect(reflect.ValueOf(ptr)).Interface())\n\tcase ContentYAMLHeaderValue, ContentYAMLTextHeaderValue:\n\t\treturn ctx.ReadYAML(ptr)\n\tcase ContentFormHeaderValue, ContentFormMultipartHeaderValue:\n\t\treturn ctx.ReadForm(ptr)\n\tcase ContentMultipartRelatedHeaderValue:\n\t\treturn fmt.Errorf(\"context: read body: cannot bind multipart/related: use ReadMultipartRelated instead\")\n\tcase ContentJSONHeaderValue:\n\t\treturn ctx.ReadJSON(ptr)\n\tcase ContentProtobufHeaderValue:\n\t\tmsg, ok := ptr.(proto.Message)\n\t\tif !ok {\n\t\t\treturn ErrContentNotSupported\n\t\t}\n\n\t\treturn ctx.ReadProtobuf(msg)\n\tcase ContentMsgPackHeaderValue, ContentMsgPack2HeaderValue:\n\t\treturn ctx.ReadMsgPack(ptr)\n\tdefault:\n\t\tif ctx.Request().URL.RawQuery != \"\" {\n\t\t\t// try read from query.\n\t\t\treturn ctx.ReadQuery(ptr)\n\t\t}\n\n\t\t// otherwise default to JSON.\n\t\treturn ctx.ReadJSON(ptr)\n\t}\n}\n\n//  +------------------------------------------------------------+\n//  | Body (raw) Writers                                         |\n//  +------------------------------------------------------------+\n\n// Write writes the data to the connection as part of an HTTP reply.\n//\n// If WriteHeader has not yet been called, Write calls\n// WriteHeader(http.StatusOK) before writing the data. If the Header\n// does not contain a Content-Type line, Write adds a Content-Type set\n// to the result of passing the initial 512 bytes of written data to\n// DetectContentType.\n//\n// Depending on the HTTP protocol version and the client, calling\n// Write or WriteHeader may prevent future reads on the\n// Request.Body. For HTTP/1.x requests, handlers should read any\n// needed request body data before writing the response. Once the\n// headers have been flushed (due to either an explicit Flusher.Flush\n// call or writing enough data to trigger a flush), the request body\n// may be unavailable. For HTTP/2 requests, the Go HTTP server permits\n// handlers to continue to read the request body while concurrently\n// writing the response. However, such behavior may not be supported\n// by all HTTP/2 clients. Handlers should read before writing if\n// possible to maximize compatibility.\n//\n// It reports any write errors back to the caller, Application.SetContentErrorHandler does NOT apply here\n// as this is a lower-level method which must be remain as it is.\nfunc (ctx *Context) Write(rawBody []byte) (int, error) {\n\treturn ctx.writer.Write(rawBody)\n}\n\n// Writef formats according to a format specifier and writes to the response.\n//\n// Returns the number of bytes written and any write error encountered.\nfunc (ctx *Context) Writef(format string, a ...any) (n int, err error) {\n\t/* if len(a) == 0 {\n\t \treturn ctx.WriteString(format)\n\t} ^ No, let it complain about arguments, because go test will do even if the app is running.\n\tUsers should use WriteString instead of (format, args)\n\twhen format may contain go-sprintf reserved chars (e.g. %).*/\n\n\treturn fmt.Fprintf(ctx.writer, format, a...)\n}\n\n// WriteString writes a simple string to the response.\n//\n// Returns the number of bytes written and any write error encountered.\nfunc (ctx *Context) WriteString(body string) (n int, err error) {\n\treturn io.WriteString(ctx.writer, body)\n}\n\nconst (\n\t// ContentTypeHeaderKey is the header key of \"Content-Type\".\n\tContentTypeHeaderKey = \"Content-Type\"\n\n\t// LastModifiedHeaderKey is the header key of \"Last-Modified\".\n\tLastModifiedHeaderKey = \"Last-Modified\"\n\t// IfModifiedSinceHeaderKey is the header key of \"If-Modified-Since\".\n\tIfModifiedSinceHeaderKey = \"If-Modified-Since\"\n\t// CacheControlHeaderKey is the header key of \"Cache-Control\".\n\tCacheControlHeaderKey = \"Cache-Control\"\n\t// ETagHeaderKey is the header key of \"ETag\".\n\tETagHeaderKey = \"ETag\"\n\n\t// ContentDispositionHeaderKey is the header key of \"Content-Disposition\".\n\tContentDispositionHeaderKey = \"Content-Disposition\"\n\t// ContentLengthHeaderKey is the header key of \"Content-Length\"\n\tContentLengthHeaderKey = \"Content-Length\"\n\t// ContentEncodingHeaderKey is the header key of \"Content-Encoding\".\n\tContentEncodingHeaderKey = \"Content-Encoding\"\n\t// GzipHeaderValue is the header value of \"gzip\".\n\tGzipHeaderValue = \"gzip\"\n\t// AcceptEncodingHeaderKey is the header key of \"Accept-Encoding\".\n\tAcceptEncodingHeaderKey = \"Accept-Encoding\"\n\t// VaryHeaderKey is the header key of \"Vary\".\n\tVaryHeaderKey = \"Vary\"\n)\n\nvar unixEpochTime = time.Unix(0, 0)\n\n// IsZeroTime reports whether t is obviously unspecified (either zero or Unix()=0).\nfunc IsZeroTime(t time.Time) bool {\n\treturn t.IsZero() || t.Equal(unixEpochTime)\n}\n\n// ParseTime parses a time header (such as the Date: header),\n// trying each forth formats (or three if Application's configuration's TimeFormat is defaulted)\n// that are allowed by HTTP/1.1:\n// Application's configuration's TimeFormat or/and http.TimeFormat,\n// time.RFC850, and time.ANSIC.\n//\n// Look `context#FormatTime` for the opossite operation (Time to string).\nvar ParseTime = func(ctx *Context, text string) (t time.Time, err error) {\n\tt, err = time.Parse(ctx.Application().ConfigurationReadOnly().GetTimeFormat(), text)\n\tif err != nil {\n\t\treturn http.ParseTime(text)\n\t}\n\n\treturn\n}\n\n// FormatTime returns a textual representation of the time value formatted\n// according to the Application's configuration's TimeFormat field\n// which defines the format.\n//\n// Look `context#ParseTime` for the opossite operation (string to Time).\nvar FormatTime = func(ctx *Context, t time.Time) string {\n\treturn t.Format(ctx.Application().ConfigurationReadOnly().GetTimeFormat())\n}\n\n// SetLastModified sets the \"Last-Modified\" based on the \"modtime\" input.\n// If \"modtime\" is zero then it does nothing.\n//\n// It's mostly internally on core/router and context packages.\nfunc (ctx *Context) SetLastModified(modtime time.Time) {\n\tif !IsZeroTime(modtime) {\n\t\tctx.Header(LastModifiedHeaderKey, FormatTime(ctx, modtime.UTC())) // or modtime.UTC()?\n\t}\n}\n\n// ErrPreconditionFailed may be returned from `Context` methods\n// that has to perform one or more client side preconditions before the actual check, e.g. `CheckIfModifiedSince`.\n// Usage:\n// ok, err := context.CheckIfModifiedSince(modTime)\n//\n//\tif err != nil {\n//\t   if errors.Is(err, context.ErrPreconditionFailed) {\n//\t        [handle missing client conditions,such as not valid request method...]\n//\t    }else {\n//\t        [the error is probably a time parse error...]\n//\t   }\n//\t}\nvar ErrPreconditionFailed = errors.New(\"precondition failed\")\n\n// CheckIfModifiedSince checks if the response is modified since the \"modtime\".\n// Note that it has nothing to do with server-side caching.\n// It does those checks by checking if the \"If-Modified-Since\" request header\n// sent by client or a previous server response header\n// (e.g with WriteWithExpiration or HandleDir or Favicon etc.)\n// is a valid one and it's before the \"modtime\".\n//\n// A check for !modtime && err == nil is necessary to make sure that\n// it's not modified since, because it may return false but without even\n// had the chance to check the client-side (request) header due to some errors,\n// like the HTTP Method is not \"GET\" or \"HEAD\" or if the \"modtime\" is zero\n// or if parsing time from the header failed. See `ErrPreconditionFailed` too.\n//\n// It's mostly used internally, e.g. `context#WriteWithExpiration`.\nfunc (ctx *Context) CheckIfModifiedSince(modtime time.Time) (bool, error) {\n\tif method := ctx.Method(); method != http.MethodGet && method != http.MethodHead {\n\t\treturn false, fmt.Errorf(\"method: %w\", ErrPreconditionFailed)\n\t}\n\tims := ctx.GetHeader(IfModifiedSinceHeaderKey)\n\tif ims == \"\" || IsZeroTime(modtime) {\n\t\treturn false, fmt.Errorf(\"zero time: %w\", ErrPreconditionFailed)\n\t}\n\tt, err := ParseTime(ctx, ims)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\t// sub-second precision, so\n\t// use mtime < t+1s instead of mtime <= t to check for unmodified.\n\tif modtime.UTC().Before(t.Add(1 * time.Second)) {\n\t\treturn false, nil\n\t}\n\treturn true, nil\n}\n\n// WriteNotModified sends a 304 \"Not Modified\" status code to the client,\n// it makes sure that the content type, the content length headers\n// and any \"ETag\" are removed before the response sent.\n//\n// It's mostly used internally on core/router/fs.go and context methods.\nfunc (ctx *Context) WriteNotModified() {\n\t// RFC 7232 section 4.1:\n\t// a sender SHOULD NOT generate representation metadata other than the\n\t// above listed fields unless said metadata exists for the purpose of\n\t// guiding cache updates (e.g.,\" Last-Modified\" might be useful if the\n\t// response does not have an ETag field).\n\th := ctx.ResponseWriter().Header()\n\tdelete(h, ContentTypeHeaderKey)\n\tdelete(h, ContentLengthHeaderKey)\n\tif h.Get(ETagHeaderKey) != \"\" {\n\t\tdelete(h, LastModifiedHeaderKey)\n\t}\n\tctx.StatusCode(http.StatusNotModified)\n}\n\n// WriteWithExpiration works like `Write` but it will check if a resource is modified,\n// based on the \"modtime\" input argument,\n// otherwise sends a 304 status code in order to let the client-side render the cached content.\nfunc (ctx *Context) WriteWithExpiration(body []byte, modtime time.Time) (int, error) {\n\tif modified, err := ctx.CheckIfModifiedSince(modtime); !modified && err == nil {\n\t\tctx.WriteNotModified()\n\t\treturn 0, nil\n\t}\n\n\tctx.SetLastModified(modtime)\n\treturn ctx.writer.Write(body)\n}\n\n// StreamWriter registers the given stream writer for populating\n// response body.\n//\n// Access to context's and/or its' members is forbidden from writer.\n//\n// This function may be used in the following cases:\n//\n//   - if response body is too big (more than iris.LimitRequestBodySize(if set)).\n//   - if response body is streamed from slow external sources.\n//   - if response body must be streamed to the client in chunks.\n//     (aka `http server push`).\nfunc (ctx *Context) StreamWriter(writer func(w io.Writer) error) error {\n\tcancelCtx := ctx.Request().Context()\n\tnotifyClosed := cancelCtx.Done()\n\n\tfor {\n\t\tselect {\n\t\t// response writer forced to close, exit.\n\t\tcase <-notifyClosed:\n\t\t\treturn cancelCtx.Err()\n\t\tdefault:\n\t\t\tif err := writer(ctx.writer); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tctx.writer.Flush()\n\t\t}\n\t}\n}\n\n//  +------------------------------------------------------------+\n//  | Body Writers with compression                              |\n//  +------------------------------------------------------------+\n\n// ClientSupportsEncoding reports whether the\n// client expects one of the given \"encodings\" compression.\n//\n// Note, this method just reports back the first valid encoding it sees,\n// meaning that request accept-encoding offers don't matter here.\n// See `CompressWriter` too.\nfunc (ctx *Context) ClientSupportsEncoding(encodings ...string) bool {\n\tif len(encodings) == 0 {\n\t\treturn false\n\t}\n\n\tif h := ctx.GetHeader(AcceptEncodingHeaderKey); h != \"\" {\n\t\tfor _, v := range strings.Split(h, \",\") {\n\t\t\tfor _, encoding := range encodings {\n\t\t\t\tif strings.Contains(v, encoding) {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn false\n}\n\n// CompressWriter enables or disables the compress response writer.\n// if the client expects a valid compression algorithm then this\n// will change the response writer to a compress writer instead.\n// All future write and rich write methods will respect this option.\n// Usage:\n//\n//\tapp.Use(func(ctx iris.Context){\n//\t\terr := ctx.CompressWriter(true)\n//\t\tctx.Next()\n//\t})\n//\n// The recommendation is to compress data as much as possible and therefore to use this field,\n// but some types of resources, such as jpeg images, are already compressed.\n// Sometimes, using additional compression doesn't reduce payload size and\n// can even make the payload longer.\nfunc (ctx *Context) CompressWriter(enable bool) error {\n\tswitch w := ctx.writer.(type) {\n\tcase *CompressResponseWriter:\n\t\tif enable {\n\t\t\treturn nil\n\t\t}\n\n\t\tw.Disabled = true\n\tcase *ResponseRecorder:\n\t\tif enable {\n\t\t\t// If it's a recorder which already wraps the compress, exit.\n\t\t\tif _, ok := w.ResponseWriter.(*CompressResponseWriter); ok {\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\t// Keep the Recorder as ctx.writer.\n\t\t\t// Wrap the existing net/http response writer\n\t\t\t// with the compressed writer and\n\t\t\t// replace the recorder's response writer\n\t\t\t// reference with that compressed one.\n\t\t\t// Fixes an issue when Record is called before CompressWriter.\n\t\t\tcw, err := AcquireCompressResponseWriter(w.ResponseWriter, ctx.request, -1)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tw.ResponseWriter = cw\n\t\t} else {\n\t\t\tcw, ok := w.ResponseWriter.(*CompressResponseWriter)\n\t\t\tif ok {\n\t\t\t\tcw.Disabled = true\n\t\t\t}\n\t\t}\n\tdefault:\n\t\tif !enable {\n\t\t\treturn nil\n\t\t}\n\n\t\tcw, err := AcquireCompressResponseWriter(w, ctx.request, -1)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tctx.writer = cw\n\t}\n\n\treturn nil\n}\n\n// CompressReader accepts a boolean, which, if set to true\n// it wraps the request body reader with a reader which decompresses request data before read.\n// If the \"enable\" input argument is false then the request body will reset to the default one.\n//\n// Useful when incoming request data are compressed.\n// All future calls of `ctx.GetBody/ReadXXX/UnmarshalBody` methods will respect this option.\n//\n// Usage:\n//\n//\tapp.Use(func(ctx iris.Context){\n//\t\terr := ctx.CompressReader(true)\n//\t\tctx.Next()\n//\t})\n//\n// More:\n//\n//\tif cr, ok := ctx.Request().Body.(*CompressReader); ok {\n//\t\tcr.Src // the original request body\n//\t cr.Encoding // the compression algorithm.\n//\t}\n//\n// It returns `ErrRequestNotCompressed` if client's request data are not compressed\n// (or empty)\n// or `ErrNotSupportedCompression` if server missing the decompression algorithm.\nfunc (ctx *Context) CompressReader(enable bool) error {\n\tcr, ok := ctx.request.Body.(*CompressReader)\n\tif enable {\n\t\tif ok {\n\t\t\t// already called.\n\t\t\treturn nil\n\t\t}\n\n\t\tencoding := ctx.GetHeader(ContentEncodingHeaderKey)\n\t\tif encoding == IDENTITY {\n\t\t\t// no transformation whatsoever, return nil error and\n\t\t\t// don't wrap the body reader.\n\t\t\treturn nil\n\t\t}\n\n\t\tr, err := NewCompressReader(ctx.request.Body, encoding)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tctx.request.Body = r\n\t} else if ok {\n\t\tctx.request.Body = cr.Src\n\t}\n\n\treturn nil\n}\n\n//  +------------------------------------------------------------+\n//  | Rich Body Content Writers/Renderers                        |\n//  +------------------------------------------------------------+\n\n// ViewEngine registers a view engine for the current chain of handlers.\n// It overrides any previously registered engines, including the application's root ones.\n// Note that, because performance is everything,\n// the \"engine\" MUST be already ready-to-use,\n// meaning that its `Load` method should be called once before this method call.\n//\n// To register a view engine per-group of groups too see `Party.RegisterView` instead.\nfunc (ctx *Context) ViewEngine(engine ViewEngine) {\n\tctx.values.Set(ctx.app.ConfigurationReadOnly().GetViewEngineContextKey(), engine)\n}\n\n// ViewLayout sets the \"layout\" option if and when .View\n// is being called afterwards, in the same request.\n// Useful when need to set or/and change a layout based on the previous handlers in the chain.\n//\n// Note that the 'layoutTmplFile' argument can be set to iris.NoLayout\n// to disable the layout for a specific view render action,\n// it disables the engine's configuration's layout property.\n//\n// Look .ViewData and .View too.\n//\n// Example: https://github.com/kataras/iris/tree/main/_examples/view/context-view-data/\nfunc (ctx *Context) ViewLayout(layoutTmplFile string) {\n\tctx.values.Set(ctx.app.ConfigurationReadOnly().GetViewLayoutContextKey(), layoutTmplFile)\n}\n\n// ViewData saves one or more key-value pair in order to be passed if and when .View\n// is being called afterwards, in the same request.\n// Useful when need to set or/and change template data from previous hanadlers in the chain.\n//\n// If .View's \"binding\" argument is not nil and it's not a type of map\n// then these data are being ignored, binding has the priority, so the main route's handler can still decide.\n// If binding is a map or iris.Map then these data are being added to the view data\n// and passed to the template.\n//\n// After .View, the data are not destroyed, in order to be re-used if needed (again, in the same request as everything else),\n// to clear the view data, developers can call:\n// ctx.Set(ctx.Application().ConfigurationReadOnly().GetViewDataContextKey(), nil)\n//\n// If 'key' is empty then the value is added as it's (struct or map) and developer is unable to add other value.\n//\n// Look .ViewLayout and .View too.\n//\n// Example: https://github.com/kataras/iris/tree/main/_examples/view/context-view-data/\nfunc (ctx *Context) ViewData(key string, value any) {\n\tviewDataContextKey := ctx.app.ConfigurationReadOnly().GetViewDataContextKey()\n\tif key == \"\" {\n\t\tctx.values.Set(viewDataContextKey, value)\n\t\treturn\n\t}\n\n\tv := ctx.values.Get(viewDataContextKey)\n\tif v == nil {\n\t\tctx.values.Set(viewDataContextKey, Map{key: value})\n\t\treturn\n\t}\n\n\tif data, ok := v.(Map); ok {\n\t\tdata[key] = value\n\t}\n}\n\n// GetViewData returns the values registered by `context#ViewData`.\n// The return value is `map[string]any`, this means that\n// if a custom struct registered to ViewData then this function\n// will try to parse it to map, if failed then the return value is nil\n// A check for nil is always a good practise if different\n// kind of values or no data are registered via `ViewData`.\n//\n// Similarly to `viewData := ctx.Values().Get(\"iris.view.data\")` or\n// `viewData := ctx.Values().Get(ctx.Application().ConfigurationReadOnly().GetViewDataContextKey())`.\nfunc (ctx *Context) GetViewData() map[string]any {\n\tif v := ctx.values.Get(ctx.app.ConfigurationReadOnly().GetViewDataContextKey()); v != nil {\n\t\t// if pure map[string]any\n\t\tif viewData, ok := v.(Map); ok {\n\t\t\treturn viewData\n\t\t}\n\n\t\t// if struct, convert it to map[string]any\n\t\tif structs.IsStruct(v) {\n\t\t\treturn structs.Map(v)\n\t\t}\n\t}\n\n\t// if no values found, then return nil\n\treturn nil\n}\n\n// FallbackViewProvider is an interface which can be registered to the `Party.FallbackView`\n// or `Context.FallbackView` methods to handle fallback views.\n// See FallbackView, FallbackViewLayout and FallbackViewFunc.\ntype FallbackViewProvider interface {\n\tFallbackView(ctx *Context, err ErrViewNotExist) error\n} /* Notes(@kataras): If ever requested, this fallback logic (of ctx, error) can go to all necessary methods.\n   I've designed with a bit more complexity here instead of a simple filename fallback in order to give\n   the freedom to the developer to do whatever he/she wants with that template/layout not exists error,\n   e.g. have a list of fallbacks views to loop through until succeed or fire a different error than the default.\n   We also provide some helpers for common fallback actions (FallbackView, FallbackViewLayout).\n   This naming was chosen in order to be easy to follow up with the previous view-relative context features.\n   Also note that here we catch a specific error, we want the developer\n   to be aware of the rest template errors (e.g. when a template having parsing issues).\n*/\n\n// FallbackViewFunc is a function that can be registered\n// to handle view fallbacks. It accepts the Context and\n// a special error which contains information about the previous template error.\n// It implements the FallbackViewProvider interface.\n//\n// See `Context.View` method.\ntype FallbackViewFunc func(ctx *Context, err ErrViewNotExist) error\n\n// FallbackView completes the FallbackViewProvider interface.\nfunc (fn FallbackViewFunc) FallbackView(ctx *Context, err ErrViewNotExist) error {\n\treturn fn(ctx, err)\n}\n\nvar (\n\t_ FallbackViewProvider = FallbackView(\"\")\n\t_ FallbackViewProvider = FallbackViewLayout(\"\")\n)\n\n// FallbackView is a helper to register a single template filename as a fallback\n// when the provided tempate filename was not found.\ntype FallbackView string\n\n// FallbackView completes the FallbackViewProvider interface.\nfunc (f FallbackView) FallbackView(ctx *Context, err ErrViewNotExist) error {\n\tif err.IsLayout { // Not responsible to render layouts.\n\t\treturn err\n\t}\n\n\t// ctx.StatusCode(200) // Let's keep the previous status code here, developer can change it anyways.\n\treturn ctx.View(string(f), err.Data)\n}\n\n// FallbackViewLayout is a helper to register a single template filename as a fallback\n// layout when the provided layout filename was not found.\ntype FallbackViewLayout string\n\n// FallbackView completes the FallbackViewProvider interface.\nfunc (f FallbackViewLayout) FallbackView(ctx *Context, err ErrViewNotExist) error {\n\tif !err.IsLayout {\n\t\t// Responsible to render layouts only.\n\t\treturn err\n\t}\n\n\tctx.ViewLayout(string(f))\n\treturn ctx.View(err.Name, err.Data)\n}\n\nconst fallbackViewOnce = \"iris.fallback.view.once\"\n\nfunc (ctx *Context) fireFallbackViewOnce(err ErrViewNotExist) error {\n\t// Note(@kataras): this is our way to keep the same View method for\n\t// both fallback and normal views, remember, we export the whole\n\t// Context functionality to the end-developer through the fallback view provider.\n\tif ctx.values.Get(fallbackViewOnce) != nil {\n\t\treturn err\n\t}\n\n\tv := ctx.values.Get(ctx.app.ConfigurationReadOnly().GetFallbackViewContextKey())\n\tif v == nil {\n\t\treturn err\n\t}\n\n\tproviders, ok := v.([]FallbackViewProvider)\n\tif !ok {\n\t\treturn err\n\t}\n\n\tctx.values.Set(fallbackViewOnce, struct{}{})\n\n\tvar pErr error\n\tfor _, provider := range providers {\n\t\tpErr = provider.FallbackView(ctx, err)\n\t\tif pErr != nil {\n\t\t\tif vErr, ok := pErr.(ErrViewNotExist); ok {\n\t\t\t\t// This fallback view does not exist or it's not responsible to handle,\n\t\t\t\t// try the next.\n\t\t\t\tpErr = vErr\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\t// If OK then we found the correct fallback.\n\t\t// If the error was a parse error and not a template not found\n\t\t// then exit and report the pErr error.\n\t\tbreak\n\t}\n\n\treturn pErr\n}\n\n// FallbackView registers one or more fallback views for a template or a template layout.\n// When View cannot find the given filename to execute then this \"provider\"\n// is responsible to handle the error or render a different view.\n//\n// Usage:\n//\n//\tFallbackView(iris.FallbackView(\"fallback.html\"))\n//\tFallbackView(iris.FallbackViewLayout(\"layouts/fallback.html\"))\n//\tOR\n//\tFallbackView(iris.FallbackViewFunc(ctx iris.Context, err iris.ErrViewNotExist) error {\n//\t  err.Name is the previous template name.\n//\t  err.IsLayout reports whether the failure came from the layout template.\n//\t  err.Data is the template data provided to the previous View call.\n//\t  [...custom logic e.g. ctx.View(\"fallback\", err.Data)]\n//\t})\nfunc (ctx *Context) FallbackView(providers ...FallbackViewProvider) {\n\tkey := ctx.app.ConfigurationReadOnly().GetFallbackViewContextKey()\n\tif key == \"\" {\n\t\treturn\n\t}\n\n\tv := ctx.values.Get(key)\n\tif v == nil {\n\t\tctx.values.Set(key, providers)\n\t\treturn\n\t}\n\n\t// Can register more than one.\n\tstoredProviders, ok := v.([]FallbackViewProvider)\n\tif !ok {\n\t\treturn\n\t}\n\n\tstoredProviders = append(storedProviders, providers...)\n\tctx.values.Set(key, storedProviders)\n}\n\n// View renders a template based on the registered view engine(s).\n// First argument accepts the filename, relative to the view engine's Directory and Extension,\n// i.e: if directory is \"./templates\" and want to render the \"./templates/users/index.html\"\n// then you pass the \"users/index.html\" as the filename argument.\n//\n// The second optional argument can receive a single \"view model\".\n// If \"optionalViewModel\" exists, even if it's nil, overrides any previous `ViewData` calls.\n// If second argument is missing then binds the data through previous `ViewData` calls (e.g. middleware).\n//\n// Look .ViewData and .ViewLayout too.\n//\n// Examples: https://github.com/kataras/iris/tree/main/_examples/view\nfunc (ctx *Context) View(filename string, optionalViewModel ...any) error {\n\tctx.ContentType(ContentHTMLHeaderValue)\n\n\terr := ctx.renderView(filename, optionalViewModel...)\n\tif err != nil {\n\t\tif errNotExists, ok := err.(ErrViewNotExist); ok {\n\t\t\terr = ctx.fireFallbackViewOnce(errNotExists)\n\t\t}\n\t}\n\n\tif err != nil {\n\t\tif ctx.IsDebug() {\n\t\t\t// send the error back to the client, when debug mode.\n\t\t\tctx.StopWithError(http.StatusInternalServerError, err)\n\t\t} else {\n\t\t\tctx.SetErrPrivate(err)\n\t\t\tctx.StopWithStatus(http.StatusInternalServerError)\n\t\t}\n\t}\n\n\treturn err\n}\n\nfunc (ctx *Context) renderView(filename string, optionalViewModel ...any) error {\n\tcfg := ctx.app.ConfigurationReadOnly()\n\tlayout := ctx.values.GetString(cfg.GetViewLayoutContextKey())\n\n\tvar bindingData any\n\tif len(optionalViewModel) > 0 /* Don't do it: can break a lot of servers: && optionalViewModel[0] != nil */ {\n\t\t// a nil can override the existing data or model sent by `ViewData`.\n\t\tbindingData = optionalViewModel[0]\n\t} else {\n\t\tbindingData = ctx.values.Get(cfg.GetViewDataContextKey())\n\t}\n\n\tif key := cfg.GetViewEngineContextKey(); key != \"\" {\n\t\tif engineV := ctx.values.Get(key); engineV != nil {\n\t\t\tif engine, ok := engineV.(ViewEngine); ok {\n\t\t\t\treturn engine.ExecuteWriter(ctx, filename, layout, bindingData)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn ctx.app.View(ctx, filename, layout, bindingData)\n}\n\nconst (\n\t// ContentBinaryHeaderValue header value for binary data.\n\tContentBinaryHeaderValue = \"application/octet-stream\"\n\t// ContentWebassemblyHeaderValue header value for web assembly files.\n\tContentWebassemblyHeaderValue = \"application/wasm\"\n\t// ContentHTMLHeaderValue is the  string of text/html response header's content type value.\n\tContentHTMLHeaderValue = \"text/html\"\n\t// ContentJSONHeaderValue header value for JSON data.\n\tContentJSONHeaderValue = \"application/json\"\n\t// ContentJSONProblemHeaderValue header value for JSON API problem error.\n\t// Read more at: https://tools.ietf.org/html/rfc7807\n\tContentJSONProblemHeaderValue = \"application/problem+json\"\n\t// ContentXMLProblemHeaderValue header value for XML API problem error.\n\t// Read more at: https://tools.ietf.org/html/rfc7807\n\tContentXMLProblemHeaderValue = \"application/problem+xml\"\n\t// ContentJavascriptHeaderValue header value for JSONP & Javascript data.\n\tContentJavascriptHeaderValue = \"text/javascript\"\n\t// ContentTextHeaderValue header value for Text data.\n\tContentTextHeaderValue = \"text/plain\"\n\t// ContentXMLHeaderValue header value for XML data.\n\tContentXMLHeaderValue = \"text/xml\"\n\t// ContentXMLUnreadableHeaderValue obselete header value for XML.\n\tContentXMLUnreadableHeaderValue = \"application/xml\"\n\t// ContentMarkdownHeaderValue custom key/content type, the real is the text/html.\n\tContentMarkdownHeaderValue = \"text/markdown\"\n\t// ContentYAMLHeaderValue header value for YAML data.\n\tContentYAMLHeaderValue = \"application/x-yaml\"\n\t// ContentYAMLTextHeaderValue header value for YAML plain text.\n\tContentYAMLTextHeaderValue = \"text/yaml\"\n\t// ContentProtobufHeaderValue header value for Protobuf messages data.\n\tContentProtobufHeaderValue = \"application/x-protobuf\"\n\t// ContentMsgPackHeaderValue header value for MsgPack data.\n\tContentMsgPackHeaderValue = \"application/msgpack\"\n\t// ContentMsgPack2HeaderValue alternative header value for MsgPack data.\n\tContentMsgPack2HeaderValue = \"application/x-msgpack\"\n\t// ContentFormHeaderValue header value for post form data.\n\tContentFormHeaderValue = \"application/x-www-form-urlencoded\"\n\t// ContentFormMultipartHeaderValue header value for post multipart form data.\n\tContentFormMultipartHeaderValue = \"multipart/form-data\"\n\t// ContentMultipartRelatedHeaderValue header value for multipart related data.\n\tContentMultipartRelatedHeaderValue = \"multipart/related\"\n\t// ContentGRPCHeaderValue Content-Type header value for gRPC.\n\tContentGRPCHeaderValue = \"application/grpc\"\n)\n\n// Binary writes out the raw bytes as binary data.\nfunc (ctx *Context) Binary(data []byte) (int, error) {\n\tctx.ContentType(ContentBinaryHeaderValue)\n\treturn ctx.Write(data)\n}\n\n// Text writes out a string as plain text.\nfunc (ctx *Context) Text(text string) (int, error) {\n\tctx.ContentType(ContentTextHeaderValue)\n\treturn ctx.WriteString(text)\n}\n\n// HTML writes out a string as text/html.\nfunc (ctx *Context) HTML(text string) (int, error) {\n\tctx.ContentType(ContentHTMLHeaderValue)\n\treturn ctx.WriteString(text)\n}\n\n// ProtoMarshalOptions is a type alias for protojson.MarshalOptions.\ntype ProtoMarshalOptions = protojson.MarshalOptions\n\n// JSON contains the options for the JSON (Context's) Renderer.\ntype JSON struct {\n\t// content-specific\n\tUnescapeHTML bool   `yaml:\"UnescapeHTML\"`\n\tIndent       string `yaml:\"Indent\"`\n\tPrefix       string `yaml:\"Prefix\"`\n\tASCII        bool   `yaml:\"ASCII\"`  // if true writes with unicode to ASCII content.\n\tSecure       bool   `yaml:\"Secure\"` // if true then it prepends a \"while(1);\" when Go slice (to JSON Array) value.\n\t// proto.Message specific marshal options.\n\tProto ProtoMarshalOptions `yaml:\"ProtoMarshalOptions\"`\n\t// If true and json writing failed then the error handler is skipped\n\t// and it just returns to the caller.\n\t//\n\t// See StopWithJSON and x/errors package.\n\tOmitErrorHandler bool `yaml:\"OmitErrorHandler\"`\n}\n\n// DefaultJSONOptions is the optional settings that are being used\n// inside `Context.JSON`.\nvar DefaultJSONOptions = JSON{}\n\n// IsDefault reports whether this JSON options structure holds the default values.\nfunc (j *JSON) IsDefault() bool {\n\treturn j.UnescapeHTML == DefaultJSONOptions.UnescapeHTML &&\n\t\tj.Indent == DefaultJSONOptions.Indent &&\n\t\tj.Prefix == DefaultJSONOptions.Prefix &&\n\t\tj.ASCII == DefaultJSONOptions.ASCII &&\n\t\tj.Secure == DefaultJSONOptions.Secure &&\n\t\tj.Proto == DefaultJSONOptions.Proto\n}\n\n// JSONP contains the options for the JSONP (Context's) Renderer.\ntype JSONP struct {\n\t// content-specific\n\tIndent           string\n\tCallback         string\n\tOmitErrorHandler bool // See JSON.OmitErrorHandler.\n}\n\n// XML contains the options for the XML (Context's) Renderer.\ntype XML struct {\n\t// content-specific\n\tIndent           string\n\tPrefix           string\n\tOmitErrorHandler bool // See JSON.OmitErrorHandler.\n}\n\n// Markdown contains the options for the Markdown (Context's) Renderer.\ntype Markdown struct {\n\t// content-specific\n\tSanitize         bool\n\tOmitErrorHandler bool // See JSON.OmitErrorHandler.\n\t//\n\t// Library-specific.\n\t// E.g. Flags: html.CommonFlags | html.HrefTargetBlank\n\tRenderOptions html.RendererOptions\n}\n\nvar (\n\tnewLineB = []byte(\"\\n\")\n\t// the html codes for unescaping.\n\tltHex = []byte(\"\\\\u003c\")\n\tlt    = []byte(\"<\")\n\n\tgtHex = []byte(\"\\\\u003e\")\n\tgt    = []byte(\">\")\n\n\tandHex = []byte(\"\\\\u0026\")\n\tand    = []byte(\"&\")\n\n\t// secure JSON.\n\tjsonArrayPrefix  = []byte(\"[\")\n\tjsonArraySuffix  = []byte(\"]\")\n\tsecureJSONPrefix = []byte(\"while(1);\")\n)\n\nfunc (ctx *Context) handleSpecialJSONResponseValue(v any, options *JSON) (bool, int, error) {\n\tif ctx.app.ConfigurationReadOnly().GetEnableProtoJSON() {\n\t\tif m, ok := v.(proto.Message); ok {\n\t\t\tprotoJSON := ProtoMarshalOptions{}\n\t\t\tif options != nil {\n\t\t\t\tprotoJSON = options.Proto\n\t\t\t}\n\n\t\t\tresult, err := protoJSON.Marshal(m)\n\t\t\tif err != nil {\n\t\t\t\treturn true, 0, err\n\t\t\t}\n\n\t\t\tn, err := ctx.writer.Write(result)\n\t\t\treturn true, n, err\n\t\t}\n\t}\n\n\tif ctx.app.ConfigurationReadOnly().GetEnableEasyJSON() {\n\t\tif easyObject, ok := v.(easyjson.Marshaler); ok {\n\t\t\tnoEscapeHTML := false\n\t\t\tif options != nil {\n\t\t\t\tnoEscapeHTML = !options.UnescapeHTML\n\t\t\t}\n\t\t\tjw := jwriter.Writer{NoEscapeHTML: noEscapeHTML}\n\t\t\teasyObject.MarshalEasyJSON(&jw)\n\n\t\t\tn, err := jw.DumpTo(ctx.writer)\n\t\t\treturn true, n, err\n\t\t}\n\t}\n\n\treturn false, 0, nil\n}\n\n// WriteJSON marshals the given interface object and writes the JSON response to the 'writer'.\nvar WriteJSON = func(ctx *Context, v any, options *JSON) error {\n\tif !options.Secure && !options.ASCII && options.Prefix == \"\" {\n\t\t// jsoniterConfig := jsoniter.Config{\n\t\t// \tEscapeHTML:    !options.UnescapeHTML,\n\t\t// \tIndentionStep: 4,\n\t\t// }.Froze()\n\t\t// enc := jsoniterConfig.NewEncoder(ctx.writer)\n\t\t// err = enc.Encode(v)\n\t\t//\n\t\t// enc := gojson.NewEncoder(ctx.writer)\n\t\t// enc.SetEscapeHTML(!options.UnescapeHTML)\n\t\t// enc.SetIndent(options.Prefix, options.Indent)\n\t\t// err = enc.EncodeContext(ctx, v)\n\t\tenc := json.NewEncoder(ctx.writer)\n\t\tenc.SetEscapeHTML(!options.UnescapeHTML)\n\t\tenc.SetIndent(options.Prefix, options.Indent)\n\n\t\treturn enc.Encode(v)\n\t}\n\n\tvar (\n\t\tresult []byte\n\t\terr    error\n\t)\n\n\tif indent := options.Indent; indent != \"\" {\n\t\tresult, err = json.MarshalIndent(v, \"\", indent)\n\t\tresult = append(result, newLineB...)\n\t} else {\n\t\tresult, err = json.Marshal(v)\n\t}\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tprependSecure := false\n\tif options.Secure {\n\t\tif bytes.HasPrefix(result, jsonArrayPrefix) {\n\t\t\tif options.Indent == \"\" {\n\t\t\t\tprependSecure = bytes.HasSuffix(result, jsonArraySuffix)\n\t\t\t} else {\n\t\t\t\tprependSecure = bytes.HasSuffix(bytes.TrimRightFunc(result, func(r rune) bool {\n\t\t\t\t\treturn r == '\\n' || r == '\\r'\n\t\t\t\t}), jsonArraySuffix)\n\t\t\t}\n\t\t}\n\t}\n\n\tif options.UnescapeHTML {\n\t\tresult = bytes.ReplaceAll(result, ltHex, lt)\n\t\tresult = bytes.ReplaceAll(result, gtHex, gt)\n\t\tresult = bytes.ReplaceAll(result, andHex, and)\n\t}\n\n\tif prependSecure {\n\t\tresult = append(secureJSONPrefix, result...)\n\t}\n\n\tif options.ASCII {\n\t\tif len(result) > 0 {\n\t\t\tbuf := new(bytes.Buffer)\n\t\t\tfor _, s := range string(result) {\n\t\t\t\tchar := string(s)\n\t\t\t\tif s >= 128 {\n\t\t\t\t\tchar = fmt.Sprintf(\"\\\\u%04x\", int64(s))\n\t\t\t\t}\n\t\t\t\tbuf.WriteString(char)\n\t\t\t}\n\n\t\t\tresult = buf.Bytes()\n\t\t}\n\t}\n\n\tif prefix := options.Prefix; prefix != \"\" {\n\t\tresult = append(stringToBytes(prefix), result...)\n\t}\n\n\t_, err = ctx.Write(result)\n\treturn err\n}\n\n// See https://golang.org/src/strings/builder.go#L45\n// func bytesToString(b []byte) string {\n// \treturn unsafe.String(unsafe.SliceData(b), len(b))\n// }\n\nfunc stringToBytes(s string) []byte {\n\treturn unsafe.Slice(unsafe.StringData(s), len(s))\n}\n\ntype (\n\t// ErrorHandler describes a context error handler which applies on\n\t// JSON, JSONP, Protobuf, MsgPack, XML, YAML and Markdown write errors.\n\t//\n\t// It does NOT modify or handle the result of Context.GetErr at all,\n\t// use a custom middleware instead if you want to handle the handler-provided custom errors (Context.SetErr)\n\t//\n\t// An ErrorHandler can be registered once via Application.SetErrorHandler method to override the default behavior.\n\t// The default behavior is to simply send status internal code error\n\t// without a body back to the client.\n\t//\n\t// See Application.SetContextErrorHandler for more.\n\tErrorHandler interface {\n\t\tHandleContextError(ctx *Context, err error)\n\t}\n\t// ErrorHandlerFunc a function shortcut for ErrorHandler interface.\n\tErrorHandlerFunc func(ctx *Context, err error)\n)\n\n// HandleContextError completes the ErrorHandler interface.\nfunc (h ErrorHandlerFunc) HandleContextError(ctx *Context, err error) {\n\th(ctx, err)\n}\n\nfunc (ctx *Context) handleContextError(err error) {\n\tif err == nil {\n\t\treturn\n\t}\n\n\tif errHandler := ctx.app.GetContextErrorHandler(); errHandler != nil {\n\t\terrHandler.HandleContextError(ctx, err)\n\t} else {\n\t\tif ctx.IsDebug() {\n\t\t\tctx.app.Logger().Error(err)\n\t\t}\n\t\tctx.StatusCode(http.StatusInternalServerError)\n\t}\n\n\t// keep the error non nil so the caller has control over further actions.\n}\n\n// Render writes the response headers and calls the given renderer \"r\" to render data.\n// This method can be used while migrating from other frameworks.\nfunc (ctx *Context) Render(statusCode int, r interface {\n\t// Render should push data with custom content type to the client.\n\tRender(http.ResponseWriter) error\n\t// WriteContentType writes custom content type to the response.\n\tWriteContentType(w http.ResponseWriter)\n}) {\n\tctx.StatusCode(statusCode)\n\n\tif statusCode >= 100 && statusCode <= 199 || statusCode == http.StatusNoContent || statusCode == http.StatusNotModified {\n\t\tr.WriteContentType(ctx.writer)\n\t\treturn\n\t}\n\n\tif err := r.Render(ctx.writer); err != nil {\n\t\tctx.StopWithError(http.StatusInternalServerError, err)\n\t}\n}\n\n// Component is the interface which all components must implement.\n// A component is a struct which can be rendered to a writer.\n// It's being used by the `Context.RenderComponent` method.\n// An example of compatible Component is a templ.Component.\ntype Component interface {\n\tRender(context.Context, io.Writer) error\n}\n\n// RenderComponent renders a component to the client.\n// It sets the \"Content-Type\" header to \"text/html; charset=utf-8\".\n// It reports any component render errors back to the caller.\n// Look the Application.SetContextErrorHandler to override the\n// default status code 500 with a custom error response.\nfunc (ctx *Context) RenderComponent(component Component) error {\n\tctx.ContentType(\"text/html; charset=utf-8\")\n\terr := component.Render(ctx.Request().Context(), ctx.ResponseWriter())\n\tif err != nil {\n\t\tctx.handleContextError(err)\n\t}\n\treturn err\n}\n\n// JSON marshals the given \"v\" value to JSON and writes the response to the client.\n// Look the Configuration.EnableProtoJSON and EnableEasyJSON too.\n//\n// It reports any JSON parser or write errors back to the caller.\n// Look the Application.SetContextErrorHandler to override the\n// default status code 500 with a custom error response.\n//\n// Customize the behavior of every `Context.JSON“ can be achieved\n// by modifying the package-level `WriteJSON` function on program initilization.\nfunc (ctx *Context) JSON(v any, opts ...JSON) (err error) {\n\tvar options *JSON\n\tif len(opts) > 0 {\n\t\toptions = &opts[0]\n\t} else {\n\t\t// If no options are given safely read the already-initialized value.\n\t\toptions = &DefaultJSONOptions\n\t}\n\n\tif err = ctx.writeJSON(v, options); err != nil {\n\t\t// if no options are given or OmitErrorHandler is false\n\t\t// then call the error handler (which may lead to a cycle if the error handler fails to write JSON too).\n\t\tif !options.OmitErrorHandler {\n\t\t\tctx.handleContextError(err)\n\t\t}\n\t}\n\n\treturn\n}\n\nfunc (ctx *Context) writeJSON(v any, options *JSON) error {\n\tctx.ContentType(ContentJSONHeaderValue)\n\n\t// After content type given and before everything else, try handle proto or easyjson, no matter the performance mode.\n\tif handled, _, err := ctx.handleSpecialJSONResponseValue(v, options); handled {\n\t\treturn err\n\t}\n\n\treturn WriteJSON(ctx, v, options)\n}\n\nvar finishCallbackB = []byte(\");\")\n\n// WriteJSONP marshals the given interface object and writes the JSONP response to the writer.\nvar WriteJSONP = func(ctx *Context, v any, options *JSONP) (err error) {\n\tif callback := options.Callback; callback != \"\" {\n\t\t_, err = ctx.Write(stringToBytes(callback + \"(\"))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdefer func() {\n\t\t\tif err == nil {\n\t\t\t\tctx.Write(finishCallbackB)\n\t\t\t}\n\t\t}()\n\t}\n\n\terr = WriteJSON(ctx, v, &JSON{\n\t\tIndent:           options.Indent,\n\t\tOmitErrorHandler: options.OmitErrorHandler,\n\t})\n\treturn err\n}\n\n// DefaultJSONPOptions is the optional settings that are being used\n// inside `ctx.JSONP`.\nvar DefaultJSONPOptions = JSONP{}\n\n// JSONP marshals the given \"v\" value to JSON and sends the response to the client.\n//\n// It reports any JSON parser or write errors back to the caller.\n// Look the Application.SetContextErrorHandler to override the\n// default status code 500 with a custom error response.\nfunc (ctx *Context) JSONP(v any, opts ...JSONP) (err error) {\n\tvar options *JSONP\n\tif len(opts) > 0 {\n\t\toptions = &opts[0]\n\t} else {\n\t\toptions = &DefaultJSONPOptions\n\t}\n\n\tctx.ContentType(ContentJavascriptHeaderValue)\n\tif err = WriteJSONP(ctx, v, options); err != nil {\n\t\tif !options.OmitErrorHandler {\n\t\t\tctx.handleContextError(err)\n\t\t}\n\t}\n\n\treturn\n}\n\ntype xmlMapEntry struct {\n\tXMLName xml.Name\n\tValue   any `xml:\",chardata\"`\n}\n\n// XMLMap wraps a map[string]any to compatible xml marshaler,\n// in order to be able to render maps as XML on the `Context.XML` method.\n//\n// Example: `Context.XML(XMLMap(\"Root\", map[string]any{...})`.\nfunc XMLMap(elementName string, v Map) xml.Marshaler {\n\treturn xmlMap{\n\t\tentries:     v,\n\t\telementName: elementName,\n\t}\n}\n\ntype xmlMap struct {\n\tentries     Map\n\telementName string\n}\n\n// MarshalXML marshals a map to XML.\nfunc (m xmlMap) MarshalXML(e *xml.Encoder, start xml.StartElement) error {\n\tif len(m.entries) == 0 {\n\t\treturn nil\n\t}\n\n\tstart.Name = xml.Name{Local: m.elementName}\n\terr := e.EncodeToken(start)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor k, v := range m.entries {\n\t\terr = e.Encode(xmlMapEntry{XMLName: xml.Name{Local: k}, Value: v})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn e.EncodeToken(start.End())\n}\n\n// WriteXML marshals the given interface object and writes the XML response to the writer.\nvar WriteXML = func(ctx *Context, v any, options *XML) error {\n\tif prefix := options.Prefix; prefix != \"\" {\n\t\t_, err := ctx.Write(stringToBytes(prefix))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tencoder := xml.NewEncoder(ctx.writer)\n\tencoder.Indent(\"\", options.Indent)\n\tif err := encoder.Encode(v); err != nil {\n\t\treturn err\n\t}\n\n\treturn encoder.Flush()\n}\n\n// DefaultXMLOptions is the optional settings that are being used\n// from `ctx.XML`.\nvar DefaultXMLOptions = XML{}\n\n// XML marshals the given interface object and writes the XML response to the client.\n// To render maps as XML see the `XMLMap` package-level function.\n//\n// It reports any XML parser or write errors back to the caller.\n// Look the Application.SetContextErrorHandler to override the\n// default status code 500 with a custom error response.\nfunc (ctx *Context) XML(v any, opts ...XML) (err error) {\n\tvar options *XML\n\tif len(opts) > 0 {\n\t\toptions = &opts[0]\n\t} else {\n\t\toptions = &DefaultXMLOptions\n\t}\n\n\tctx.ContentType(ContentXMLHeaderValue)\n\tif err = WriteXML(ctx, v, options); err != nil {\n\t\tif !options.OmitErrorHandler {\n\t\t\tctx.handleContextError(err)\n\t\t}\n\t}\n\n\treturn\n}\n\n// Problem writes a JSON or XML problem response.\n// Order of Problem fields are not always rendered the same.\n//\n// Behaves exactly like the `Context.JSON` method\n// but with default ProblemOptions.JSON indent of \" \" and\n// a response content type of \"application/problem+json\" instead.\n//\n// Use the options.RenderXML and XML fields to change this behavior and\n// send a response of content type \"application/problem+xml\" instead.\n//\n// Read more at: https://github.com/kataras/iris/blob/main/_examples/routing/http-errors.\nfunc (ctx *Context) Problem(v any, opts ...ProblemOptions) error {\n\toptions := DefaultProblemOptions\n\tif len(opts) > 0 {\n\t\toptions = opts[0]\n\t\t// Currently apply only if custom options passsed, otherwise,\n\t\t// with the current settings, it's not required.\n\t\t// This may change in the future though.\n\t\toptions.Apply(ctx)\n\t}\n\n\tif p, ok := v.(Problem); ok {\n\t\t// if !p.Validate() {\n\t\t// \tctx.StatusCode(http.StatusInternalServerError)\n\t\t// \treturn ErrNotValidProblem\n\t\t// }\n\t\tp.updateURIsToAbs(ctx)\n\t\tcode, _ := p.getStatus()\n\t\tif code == 0 { // get the current status code and set it to the problem.\n\t\t\tcode = ctx.GetStatusCode()\n\t\t\tctx.StatusCode(code)\n\t\t} else {\n\t\t\t// send the problem's status code\n\t\t\tctx.StatusCode(code)\n\t\t}\n\n\t\tif options.RenderXML {\n\t\t\tctx.contentTypeOnce(ContentXMLProblemHeaderValue, \"\")\n\t\t\t// Problem is an xml Marshaler already, don't use `XMLMap`.\n\t\t\treturn ctx.XML(v, options.XML)\n\t\t}\n\t}\n\n\tctx.contentTypeOnce(ContentJSONProblemHeaderValue, \"\")\n\treturn ctx.writeJSON(v, &options.JSON)\n}\n\nvar sanitizer = bluemonday.UGCPolicy()\n\n// WriteMarkdown parses the markdown to html and writes these contents to the writer.\nvar WriteMarkdown = func(ctx *Context, markdownB []byte, options *Markdown) error {\n\tout := markdown.NormalizeNewlines(markdownB)\n\n\trenderer := html.NewRenderer(options.RenderOptions)\n\tdoc := markdown.Parse(out, nil)\n\tout = markdown.Render(doc, renderer)\n\tif options.Sanitize {\n\t\tout = sanitizer.SanitizeBytes(out)\n\t}\n\n\t_, err := ctx.Write(out)\n\treturn err\n}\n\n// DefaultMarkdownOptions is the optional settings that are being used\n// from `WriteMarkdown` and `ctx.Markdown`.\nvar DefaultMarkdownOptions = Markdown{}\n\n// Markdown parses the markdown to html and renders its result to the client.\n//\n// It reports any Markdown parser or write errors back to the caller.\n// Look the Application.SetContextErrorHandler to override the\n// default status code 500 with a custom error response.\nfunc (ctx *Context) Markdown(markdownB []byte, opts ...Markdown) (err error) {\n\tvar options *Markdown\n\tif len(opts) > 0 {\n\t\toptions = &opts[0]\n\t} else {\n\t\toptions = &DefaultMarkdownOptions\n\t}\n\n\tctx.ContentType(ContentHTMLHeaderValue)\n\tif err = WriteMarkdown(ctx, markdownB, options); err != nil {\n\t\tif !options.OmitErrorHandler {\n\t\t\tctx.handleContextError(err)\n\t\t}\n\t}\n\n\treturn\n}\n\n// WriteYAML sends YAML response to the client.\nvar WriteYAML = func(ctx *Context, v any, indentSpace int) error {\n\tencoder := yaml.NewEncoder(ctx.writer)\n\tencoder.SetIndent(indentSpace)\n\n\tif err := encoder.Encode(v); err != nil {\n\t\treturn err\n\t}\n\n\treturn encoder.Close()\n}\n\n// YAML marshals the given \"v\" value using the yaml marshaler and writes the result to the client.\n//\n// It reports any YAML parser or write errors back to the caller.\n// Look the Application.SetContextErrorHandler to override the\n// default status code 500 with a custom error response.\nfunc (ctx *Context) YAML(v any) error {\n\tctx.ContentType(ContentYAMLHeaderValue)\n\n\terr := WriteYAML(ctx, v, 0)\n\tif err != nil {\n\t\tctx.handleContextError(err)\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\n// TextYAML calls the Context.YAML method but with the text/yaml content type instead.\nfunc (ctx *Context) TextYAML(v any) error {\n\tctx.ContentType(ContentYAMLTextHeaderValue)\n\n\terr := WriteYAML(ctx, v, 4)\n\tif err != nil {\n\t\tctx.handleContextError(err)\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\n// Protobuf marshals the given \"v\" value of proto Message and writes its result to the client.\n//\n// It reports any protobuf parser or write errors back to the caller.\n// Look the Application.SetContextErrorHandler to override the\n// default status code 500 with a custom error response.\nfunc (ctx *Context) Protobuf(v proto.Message) (int, error) {\n\tout, err := proto.Marshal(v)\n\tif err != nil {\n\t\tctx.handleContextError(err)\n\t\treturn 0, err\n\t}\n\n\tctx.ContentType(ContentProtobufHeaderValue)\n\tn, err := ctx.Write(out)\n\tif err != nil {\n\t\tctx.handleContextError(err)\n\t}\n\n\treturn n, err\n}\n\n// MsgPack marshals the given \"v\" value of msgpack format and writes its result to the client.\n//\n// It reports any message pack or write errors back to the caller.\n// Look the Application.SetContextErrorHandler to override the\n// default status code 500 with a custom error response.\nfunc (ctx *Context) MsgPack(v any) (int, error) {\n\tout, err := msgpack.Marshal(v)\n\tif err != nil {\n\t\tctx.handleContextError(err)\n\t}\n\n\tctx.ContentType(ContentMsgPackHeaderValue)\n\tn, err := ctx.Write(out)\n\tif err != nil {\n\t\tctx.handleContextError(err)\n\t}\n\n\treturn n, err\n}\n\n//  +-----------------------------------------------------------------------+\n//  | Content Νegotiation                                                   |\n//  | https://developer.mozilla.org/en-US/docs/Web/HTTP/Content_negotiation |                                       |\n//  +-----------------------------------------------------------------------+\n\n// ErrContentNotSupported returns from the `Negotiate` method\n// when server responds with 406.\nvar ErrContentNotSupported = errors.New(\"unsupported content\")\n\n// ContentSelector is the interface which structs can implement\n// to manually choose a content based on the negotiated mime (content type).\n// It can be passed to the `Context.Negotiate` method.\n//\n// See the `N` struct too.\ntype ContentSelector interface {\n\tSelectContent(mime string) any\n}\n\n// ContentNegotiator is the interface which structs can implement\n// to override the `Context.Negotiate` default implementation and\n// manually respond to the client based on a manuall call of `Context.Negotiation().Build()`\n// to get the final negotiated mime and charset.\n// It can be passed to the `Context.Negotiate` method.\ntype ContentNegotiator interface {\n\t// mime and charset can be retrieved by:\n\t// mime, charset := Context.Negotiation().Build()\n\t// Pass this method to `Context.Negotiate` method\n\t// to write custom content.\n\t// Overriding the existing behavior of Context.Negotiate for selecting values based on\n\t// content types, although it can accept any custom mime type with []byte.\n\t// Content type is already set.\n\t// Use it with caution, 99.9% you don't need this but it's here for extreme cases.\n\tNegotiate(ctx *Context) (int, error)\n}\n\n// N is a struct which can be passed on the `Context.Negotiate` method.\n// It contains fields which should be filled based on the `Context.Negotiation()`\n// server side values. If no matched mime then its \"Other\" field will be sent,\n// which should be a string or []byte.\n// It completes the `ContentSelector` interface.\ntype N struct {\n\tText, HTML string\n\tMarkdown   []byte\n\tBinary     []byte\n\n\tJSON     any\n\tProblem  Problem\n\tJSONP    any\n\tXML      any\n\tYAML     any\n\tProtobuf any\n\tMsgPack  any\n\n\tOther []byte // custom content types.\n}\n\nvar _ ContentSelector = N{}\n\n// SelectContent returns a content based on the matched negotiated \"mime\".\nfunc (n N) SelectContent(mime string) any {\n\tswitch mime {\n\tcase ContentTextHeaderValue:\n\t\treturn n.Text\n\tcase ContentHTMLHeaderValue:\n\t\treturn n.HTML\n\tcase ContentMarkdownHeaderValue:\n\t\treturn n.Markdown\n\tcase ContentBinaryHeaderValue:\n\t\treturn n.Binary\n\tcase ContentJSONHeaderValue:\n\t\treturn n.JSON\n\tcase ContentJSONProblemHeaderValue:\n\t\treturn n.Problem\n\tcase ContentJavascriptHeaderValue:\n\t\treturn n.JSONP\n\tcase ContentXMLHeaderValue, ContentXMLUnreadableHeaderValue:\n\t\treturn n.XML\n\tcase ContentYAMLHeaderValue:\n\t\treturn n.YAML\n\tcase ContentProtobufHeaderValue:\n\t\treturn n.Protobuf\n\tcase ContentMsgPackHeaderValue, ContentMsgPack2HeaderValue:\n\t\treturn n.MsgPack\n\tdefault:\n\t\treturn n.Other\n\t}\n}\n\nconst negotiationContextKey = \"iris.negotiation_builder\"\n\n// Negotiation creates once and returns the negotiation builder\n// to build server-side available prioritized content\n// for specific content type(s), charset(s) and encoding algorithm(s).\n//\n// See `Negotiate` method too.\nfunc (ctx *Context) Negotiation() *NegotiationBuilder {\n\tif n := ctx.values.Get(negotiationContextKey); n != nil {\n\t\treturn n.(*NegotiationBuilder)\n\t}\n\n\tacceptBuilder := NegotiationAcceptBuilder{}\n\tacceptBuilder.accept = parseHeader(ctx.GetHeader(\"Accept\"))\n\tacceptBuilder.charset = parseHeader(ctx.GetHeader(\"Accept-Charset\"))\n\n\tn := &NegotiationBuilder{Accept: acceptBuilder}\n\n\tctx.values.Set(negotiationContextKey, n)\n\n\treturn n\n}\n\nfunc parseHeader(headerValue string) []string {\n\tin := strings.Split(headerValue, \",\")\n\tout := make([]string, 0, len(in))\n\n\tfor _, value := range in {\n\t\t// remove any spaces and quality values such as ;q=0.8.\n\t\tv := strings.TrimSpace(strings.Split(value, \";\")[0])\n\t\tif v != \"\" {\n\t\t\tout = append(out, v)\n\t\t}\n\t}\n\n\treturn out\n}\n\n// Negotiate used for serving different representations of a resource at the same URI.\n//\n// The \"v\" can be a single `N` struct value.\n// The \"v\" can be any value completes the `ContentSelector` interface.\n// The \"v\" can be any value completes the `ContentNegotiator` interface.\n// The \"v\" can be any value of struct(JSON, JSONP, XML, YAML, Protobuf, MsgPack) or\n// string(TEXT, HTML) or []byte(Markdown, Binary) or []byte with any matched mime type.\n//\n// If the \"v\" is nil, the `Context.Negotitation()` builder's\n// content will be used instead, otherwise \"v\" overrides builder's content\n// (server mime types are still retrieved by its registered, supported, mime list)\n//\n// Set mime type priorities by `Negotiation().JSON().XML().HTML()...`.\n// Set charset priorities by `Negotiation().Charset(...)`.\n// Set encoding algorithm priorities by `Negotiation().Encoding(...)`.\n// Modify the accepted by\n// `Negotiation().Accept./Override()/.XML().JSON().Charset(...).Encoding(...)...`.\n//\n// It returns `ErrContentNotSupported` when not matched mime type(s).\n//\n// Resources:\n// https://developer.mozilla.org/en-US/docs/Web/HTTP/Content_negotiation\n// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept\n// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Charset\n// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding\n//\n// Supports the above without quality values.\n//\n// Read more at: https://github.com/kataras/iris/tree/main/_examples/response-writer/content-negotiation\nfunc (ctx *Context) Negotiate(v any) (int, error) {\n\tcontentType, charset, encoding, content := ctx.Negotiation().Build()\n\tif v == nil {\n\t\tv = content\n\t}\n\n\tif contentType == \"\" {\n\t\t// If the server cannot serve any matching set,\n\t\t// it SHOULD send back a 406 (Not Acceptable) error code.\n\t\tctx.StatusCode(http.StatusNotAcceptable)\n\t\treturn -1, ErrContentNotSupported\n\t}\n\n\tif charset == \"\" {\n\t\tcharset = ctx.app.ConfigurationReadOnly().GetCharset()\n\t}\n\n\tif encoding != \"\" {\n\t\tctx.CompressWriter(true)\n\t}\n\n\tctx.contentTypeOnce(contentType, charset)\n\n\tif n, ok := v.(ContentNegotiator); ok {\n\t\treturn n.Negotiate(ctx)\n\t}\n\n\tif s, ok := v.(ContentSelector); ok {\n\t\tv = s.SelectContent(contentType)\n\t}\n\n\t// switch v := value.(type) {\n\t// case []byte:\n\t// \tif contentType == ContentMarkdownHeaderValue {\n\t// \t\treturn ctx.Markdown(v)\n\t// \t}\n\n\t// \treturn ctx.Write(v)\n\t// case string:\n\t// \treturn ctx.WriteString(v)\n\t// default:\n\t// make it switch by content-type only, but we lose custom mime types capability that way:\n\t//                                                 ^ solved with []byte on default case and\n\t//                                                 ^ N.Other and\n\t//                                                 ^ ContentSelector and ContentNegotiator interfaces.\n\n\tswitch contentType {\n\tcase ContentTextHeaderValue, ContentHTMLHeaderValue:\n\t\treturn ctx.WriteString(v.(string))\n\tcase ContentMarkdownHeaderValue:\n\t\terr := ctx.Markdown(v.([]byte))\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\n\t\treturn ctx.writer.Written(), nil\n\tcase ContentJSONHeaderValue:\n\t\terr := ctx.JSON(v)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\n\t\treturn ctx.writer.Written(), nil\n\tcase ContentJSONProblemHeaderValue, ContentXMLProblemHeaderValue:\n\t\terr := ctx.Problem(v)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\n\t\treturn ctx.writer.Written(), nil\n\tcase ContentJavascriptHeaderValue:\n\t\terr := ctx.JSONP(v)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\n\t\treturn ctx.writer.Written(), nil\n\tcase ContentXMLHeaderValue, ContentXMLUnreadableHeaderValue:\n\t\terr := ctx.XML(v)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\n\t\treturn ctx.writer.Written(), nil\n\tcase ContentYAMLHeaderValue:\n\t\terr := ctx.YAML(v)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\n\t\treturn ctx.writer.Written(), nil\n\tcase ContentYAMLTextHeaderValue:\n\t\terr := ctx.TextYAML(v)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\n\t\treturn ctx.writer.Written(), nil\n\tcase ContentProtobufHeaderValue:\n\t\tmsg, ok := v.(proto.Message)\n\t\tif !ok {\n\t\t\treturn -1, ErrContentNotSupported\n\t\t}\n\n\t\treturn ctx.Protobuf(msg)\n\tcase ContentMsgPackHeaderValue, ContentMsgPack2HeaderValue:\n\t\treturn ctx.MsgPack(v)\n\tdefault:\n\t\t// maybe \"Other\" or v is []byte or string but not a built-in framework mime,\n\t\t// for custom content types,\n\t\t// panic if not correct usage.\n\t\tswitch vv := v.(type) {\n\t\tcase []byte:\n\t\t\treturn ctx.Write(vv)\n\t\tcase string:\n\t\t\treturn ctx.WriteString(vv)\n\t\tdefault:\n\t\t\tctx.StatusCode(http.StatusNotAcceptable)\n\t\t\treturn -1, ErrContentNotSupported\n\t\t}\n\t}\n}\n\n// NegotiationBuilder returns from the `Context.Negotitation`\n// and can be used inside chain of handlers to build server-side\n// mime type(s), charset(s) and encoding algorithm(s)\n// that should match with the client's\n// Accept, Accept-Charset and Accept-Encoding headers (by-default).\n// To modify the client's accept use its \"Accept\" field\n// which it's the `NegotitationAcceptBuilder`.\n//\n// See the `Negotiate` method too.\ntype NegotiationBuilder struct {\n\tAccept NegotiationAcceptBuilder\n\n\tmime     []string       // we need order.\n\tcontents map[string]any // map to the \"mime\" and content should be rendered if that mime requested.\n\tcharset  []string\n\tencoding []string\n}\n\n// MIME registers a mime type and optionally the value that should be rendered\n// through `Context.Negotiate` when this mime type is accepted by client.\n//\n// Returns itself for recursive calls.\nfunc (n *NegotiationBuilder) MIME(mime string, content any) *NegotiationBuilder {\n\tmimes := parseHeader(mime) // if contains more than one sep by commas \",\".\n\tif content == nil {\n\t\tn.mime = append(n.mime, mimes...)\n\t\treturn n\n\t}\n\n\tif n.contents == nil {\n\t\tn.contents = make(map[string]any)\n\t}\n\n\tfor _, m := range mimes {\n\t\tn.mime = append(n.mime, m)\n\t\tn.contents[m] = content\n\t}\n\n\treturn n\n}\n\n// Text registers the \"text/plain\" content type and, optionally,\n// a value that `Context.Negotiate` will render\n// when a client accepts the \"text/plain\" content type.\n//\n// Returns itself for recursive calls.\nfunc (n *NegotiationBuilder) Text(v ...string) *NegotiationBuilder {\n\tvar content any\n\tif len(v) > 0 {\n\t\tcontent = v[0]\n\t}\n\treturn n.MIME(ContentTextHeaderValue, content)\n}\n\n// HTML registers the \"text/html\" content type and, optionally,\n// a value that `Context.Negotiate` will render\n// when a client accepts the \"text/html\" content type.\n//\n// Returns itself for recursive calls.\nfunc (n *NegotiationBuilder) HTML(v ...string) *NegotiationBuilder {\n\tvar content any\n\tif len(v) > 0 {\n\t\tcontent = v[0]\n\t}\n\treturn n.MIME(ContentHTMLHeaderValue, content)\n}\n\n// Markdown registers the \"text/markdown\" content type and, optionally,\n// a value that `Context.Negotiate` will render\n// when a client accepts the \"text/markdown\" content type.\n//\n// Returns itself for recursive calls.\nfunc (n *NegotiationBuilder) Markdown(v ...[]byte) *NegotiationBuilder {\n\tvar content any\n\tif len(v) > 0 {\n\t\tcontent = v\n\t}\n\treturn n.MIME(ContentMarkdownHeaderValue, content)\n}\n\n// Binary registers the \"application/octet-stream\" content type and, optionally,\n// a value that `Context.Negotiate` will render\n// when a client accepts the \"application/octet-stream\" content type.\n//\n// Returns itself for recursive calls.\nfunc (n *NegotiationBuilder) Binary(v ...[]byte) *NegotiationBuilder {\n\tvar content any\n\tif len(v) > 0 {\n\t\tcontent = v[0]\n\t}\n\treturn n.MIME(ContentBinaryHeaderValue, content)\n}\n\n// JSON registers the \"application/json\" content type and, optionally,\n// a value that `Context.Negotiate` will render\n// when a client accepts the \"application/json\" content type.\n//\n// Returns itself for recursive calls.\nfunc (n *NegotiationBuilder) JSON(v ...any) *NegotiationBuilder {\n\tvar content any\n\tif len(v) > 0 {\n\t\tcontent = v[0]\n\t}\n\treturn n.MIME(ContentJSONHeaderValue, content)\n}\n\n// Problem registers the \"application/problem+json\" or \"application/problem+xml\" content type and, optionally,\n// a value that `Context.Negotiate` will render\n// when a client accepts the \"application/problem+json\" or the \"application/problem+xml\" content type.\n//\n// Returns itself for recursive calls.\nfunc (n *NegotiationBuilder) Problem(v ...any) *NegotiationBuilder {\n\tvar content any\n\tif len(v) > 0 {\n\t\tcontent = v[0]\n\t}\n\treturn n.MIME(ContentJSONProblemHeaderValue+\",\"+ContentXMLProblemHeaderValue, content)\n}\n\n// JSONP registers the \"text/javascript\" content type and, optionally,\n// a value that `Context.Negotiate` will render\n// when a client accepts the \"javascript/javascript\" content type.\n//\n// Returns itself for recursive calls.\nfunc (n *NegotiationBuilder) JSONP(v ...any) *NegotiationBuilder {\n\tvar content any\n\tif len(v) > 0 {\n\t\tcontent = v[0]\n\t}\n\treturn n.MIME(ContentJavascriptHeaderValue, content)\n}\n\n// XML registers the \"text/xml\" and \"application/xml\" content types and, optionally,\n// a value that `Context.Negotiate` will render\n// when a client accepts one of the \"text/xml\" or \"application/xml\" content types.\n//\n// Returns itself for recursive calls.\nfunc (n *NegotiationBuilder) XML(v ...any) *NegotiationBuilder {\n\tvar content any\n\tif len(v) > 0 {\n\t\tcontent = v[0]\n\t}\n\treturn n.MIME(ContentXMLHeaderValue+\",\"+ContentXMLUnreadableHeaderValue, content)\n}\n\n// YAML registers the \"application/x-yaml\" content type and, optionally,\n// a value that `Context.Negotiate` will render\n// when a client accepts the \"application/x-yaml\" content type.\n//\n// Returns itself for recursive calls.\nfunc (n *NegotiationBuilder) YAML(v ...any) *NegotiationBuilder {\n\tvar content any\n\tif len(v) > 0 {\n\t\tcontent = v[0]\n\t}\n\treturn n.MIME(ContentYAMLHeaderValue, content)\n}\n\n// TextYAML registers the \"text/yaml\" content type and, optionally,\n// a value that `Context.Negotiate` will render\n// when a client accepts the \"application/x-yaml\" content type.\n//\n// Returns itself for recursive calls.\nfunc (n *NegotiationBuilder) TextYAML(v ...any) *NegotiationBuilder {\n\tvar content any\n\tif len(v) > 0 {\n\t\tcontent = v[0]\n\t}\n\treturn n.MIME(ContentYAMLTextHeaderValue, content)\n}\n\n// Protobuf registers the \"application/x-protobuf\" content type and, optionally,\n// a value that `Context.Negotiate` will render\n// when a client accepts the \"application/x-protobuf\" content type.\n//\n// Returns itself for recursive calls.\nfunc (n *NegotiationBuilder) Protobuf(v ...any) *NegotiationBuilder {\n\tvar content any\n\tif len(v) > 0 {\n\t\tcontent = v[0]\n\t}\n\treturn n.MIME(ContentProtobufHeaderValue, content)\n}\n\n// MsgPack registers the \"application/x-msgpack\" and \"application/msgpack\" content types and, optionally,\n// a value that `Context.Negotiate` will render\n// when a client accepts one of the \"application/x-msgpack\" or \"application/msgpack\" content types.\n//\n// Returns itself for recursive calls.\nfunc (n *NegotiationBuilder) MsgPack(v ...any) *NegotiationBuilder {\n\tvar content any\n\tif len(v) > 0 {\n\t\tcontent = v[0]\n\t}\n\treturn n.MIME(ContentMsgPackHeaderValue+\",\"+ContentMsgPack2HeaderValue, content)\n}\n\n// Any registers a wildcard that can match any client's accept content type.\n//\n// Returns itself for recursive calls.\nfunc (n *NegotiationBuilder) Any(v ...any) *NegotiationBuilder {\n\tvar content any\n\tif len(v) > 0 {\n\t\tcontent = v[0]\n\t}\n\treturn n.MIME(\"*\", content)\n}\n\n// Charset overrides the application's config's charset (which defaults to \"utf-8\")\n// that a client should match for\n// (through Accept-Charset header or custom through `NegotitationBuilder.Accept.Override().Charset(...)` call).\n// Do not set it if you don't know what you're doing.\n//\n// Returns itself for recursive calls.\nfunc (n *NegotiationBuilder) Charset(charset ...string) *NegotiationBuilder {\n\tn.charset = append(n.charset, charset...)\n\treturn n\n}\n\n// Encoding registers one or more encoding algorithms by name, i.e gzip, deflate, br, snappy, s2.\n// that a client should match for (through Accept-Encoding header).\n//\n// Returns itself for recursive calls.\nfunc (n *NegotiationBuilder) Encoding(encoding ...string) *NegotiationBuilder {\n\tn.encoding = append(n.encoding, encoding...)\n\treturn n\n}\n\n// EncodingGzip registers the \"gzip\" encoding algorithm\n// that a client should match for (through Accept-Encoding header or call of Accept.Encoding(enc)).\n//\n// It will make resources to served by \"gzip\" if Accept-Encoding contains the \"gzip\" as well.\n//\n// Returns itself for recursive calls.\nfunc (n *NegotiationBuilder) EncodingGzip() *NegotiationBuilder {\n\treturn n.Encoding(GzipHeaderValue)\n}\n\n// Build calculates the client's and server's mime type(s), charset(s) and encoding\n// and returns the final content type, charset and encoding that server should render\n// to the client. It does not clear the fields, use the `Clear` method if neeeded.\n//\n// The returned \"content\" can be nil if the matched \"contentType\" does not provide any value,\n// in that case the `Context.Negotiate(v)` must be called with a non-nil value.\nfunc (n *NegotiationBuilder) Build() (contentType, charset, encoding string, content any) {\n\tcontentType = negotiationMatch(n.Accept.accept, n.mime)\n\tcharset = negotiationMatch(n.Accept.charset, n.charset)\n\tencoding = negotiationMatch(n.Accept.encoding, n.encoding)\n\n\tif n.contents != nil {\n\t\tif data, ok := n.contents[contentType]; ok {\n\t\t\tcontent = data\n\t\t}\n\t}\n\n\treturn\n}\n\n// Clear clears the prioritized mime type(s), charset(s) and any contents\n// relative to those mime type(s).\n// The \"Accept\" field is stay as it is, use its `Override` method\n// to clear out the client's accepted mime type(s) and charset(s).\nfunc (n *NegotiationBuilder) Clear() *NegotiationBuilder {\n\tn.mime = n.mime[0:0]\n\tn.contents = nil\n\tn.charset = n.charset[0:0]\n\treturn n\n}\n\n// NegotiationAcceptBuilder builds the accepted mime types and charset\n//\n// and \"Accept-Charset\" headers respectfully.\n// The default values are set by the client side, server can append or override those.\n// The end result will be challenged with runtime preffered set of content types and charsets.\n//\n// See the `Negotiate` method too.\ntype NegotiationAcceptBuilder struct {\n\t// initialized with \"Accept\" request header values.\n\taccept []string\n\t// initialized with \"Accept-Charset\" request header. and if was empty then the\n\t// application's default (which defaults to utf-8).\n\tcharset []string\n\t// initialized with \"Accept-Encoding\" request header values.\n\tencoding []string\n\n\t// To support override in request life cycle.\n\t// We need slice when data is the same format\n\t// for one or more mime types,\n\t// i.e text/xml and obselete application/xml.\n\tlastAccept   []string\n\tlastCharset  []string\n\tlastEncoding []string\n}\n\n// Override clears the default values for accept and accept charset.\n// Returns itself.\nfunc (n *NegotiationAcceptBuilder) Override() *NegotiationAcceptBuilder {\n\t// when called first.\n\tn.accept = n.accept[0:0]\n\tn.charset = n.charset[0:0]\n\tn.encoding = n.encoding[0:0]\n\n\t// when called after.\n\tif len(n.lastAccept) > 0 {\n\t\tn.accept = append(n.accept, n.lastAccept...)\n\t\tn.lastAccept = n.lastAccept[0:0]\n\t}\n\n\tif len(n.lastCharset) > 0 {\n\t\tn.charset = append(n.charset, n.lastCharset...)\n\t\tn.lastCharset = n.lastCharset[0:0]\n\t}\n\n\tif len(n.lastEncoding) > 0 {\n\t\tn.encoding = append(n.encoding, n.lastEncoding...)\n\t\tn.lastEncoding = n.lastEncoding[0:0]\n\t}\n\n\treturn n\n}\n\n// MIME adds accepted client's mime type(s).\n// Returns itself.\nfunc (n *NegotiationAcceptBuilder) MIME(mimeType ...string) *NegotiationAcceptBuilder {\n\tn.lastAccept = mimeType\n\tn.accept = append(n.accept, mimeType...)\n\treturn n\n}\n\n// Text adds the \"text/plain\" as accepted client content type.\n// Returns itself.\nfunc (n *NegotiationAcceptBuilder) Text() *NegotiationAcceptBuilder {\n\treturn n.MIME(ContentTextHeaderValue)\n}\n\n// HTML adds the \"text/html\" as accepted client content type.\n// Returns itself.\nfunc (n *NegotiationAcceptBuilder) HTML() *NegotiationAcceptBuilder {\n\treturn n.MIME(ContentHTMLHeaderValue)\n}\n\n// Markdown adds the \"text/markdown\" as accepted client content type.\n// Returns itself.\nfunc (n *NegotiationAcceptBuilder) Markdown() *NegotiationAcceptBuilder {\n\treturn n.MIME(ContentMarkdownHeaderValue)\n}\n\n// Binary adds the \"application/octet-stream\" as accepted client content type.\n// Returns itself.\nfunc (n *NegotiationAcceptBuilder) Binary() *NegotiationAcceptBuilder {\n\treturn n.MIME(ContentBinaryHeaderValue)\n}\n\n// JSON adds the \"application/json\" as accepted client content type.\n// Returns itself.\nfunc (n *NegotiationAcceptBuilder) JSON() *NegotiationAcceptBuilder {\n\treturn n.MIME(ContentJSONHeaderValue)\n}\n\n// Problem adds the \"application/problem+json\" and \"application/problem-xml\"\n// as accepted client content types.\n// Returns itself.\nfunc (n *NegotiationAcceptBuilder) Problem() *NegotiationAcceptBuilder {\n\treturn n.MIME(ContentJSONProblemHeaderValue, ContentXMLProblemHeaderValue)\n}\n\n// JSONP adds the \"text/javascript\" as accepted client content type.\n// Returns itself.\nfunc (n *NegotiationAcceptBuilder) JSONP() *NegotiationAcceptBuilder {\n\treturn n.MIME(ContentJavascriptHeaderValue)\n}\n\n// XML adds the \"text/xml\" and \"application/xml\" as accepted client content types.\n// Returns itself.\nfunc (n *NegotiationAcceptBuilder) XML() *NegotiationAcceptBuilder {\n\treturn n.MIME(ContentXMLHeaderValue, ContentXMLUnreadableHeaderValue)\n}\n\n// YAML adds the \"application/x-yaml\" as accepted client content type.\n// Returns itself.\nfunc (n *NegotiationAcceptBuilder) YAML() *NegotiationAcceptBuilder {\n\treturn n.MIME(ContentYAMLHeaderValue)\n}\n\n// TextYAML adds the \"text/yaml\" as accepted client content type.\n// Returns itself.\nfunc (n *NegotiationAcceptBuilder) TextYAML() *NegotiationAcceptBuilder {\n\treturn n.MIME(ContentYAMLTextHeaderValue)\n}\n\n// Protobuf adds the \"application/x-protobuf\" as accepted client content type.\n// Returns itself.\nfunc (n *NegotiationAcceptBuilder) Protobuf() *NegotiationAcceptBuilder {\n\treturn n.MIME(ContentYAMLHeaderValue)\n}\n\n// MsgPack adds the \"application/msgpack\" and \"application/x-msgpack\" as accepted client content types.\n// Returns itself.\nfunc (n *NegotiationAcceptBuilder) MsgPack() *NegotiationAcceptBuilder {\n\treturn n.MIME(ContentYAMLHeaderValue)\n}\n\n// Charset adds one or more client accepted charsets.\n// Returns itself.\nfunc (n *NegotiationAcceptBuilder) Charset(charset ...string) *NegotiationAcceptBuilder {\n\tn.lastCharset = charset\n\tn.charset = append(n.charset, charset...)\n\n\treturn n\n}\n\n// Encoding adds one or more client accepted encoding algorithms.\n// Returns itself.\nfunc (n *NegotiationAcceptBuilder) Encoding(encoding ...string) *NegotiationAcceptBuilder {\n\tn.lastEncoding = encoding\n\tn.encoding = append(n.encoding, encoding...)\n\n\treturn n\n}\n\n// EncodingGzip adds the \"gzip\" as accepted encoding.\n// Returns itself.\nfunc (n *NegotiationAcceptBuilder) EncodingGzip() *NegotiationAcceptBuilder {\n\treturn n.Encoding(GzipHeaderValue)\n}\n\n//  +------------------------------------------------------------+\n//  | Serve files                                                |\n//  +------------------------------------------------------------+\n\n// ServeContent replies to the request using the content in the\n// provided ReadSeeker. The main benefit of ServeContent over io.Copy\n// is that it handles Range requests properly, sets the MIME type, and\n// handles If-Match, If-Unmodified-Since, If-None-Match, If-Modified-Since,\n// and If-Range requests.\n//\n// If the response's Content-Type header is not set, ServeContent\n// first tries to deduce the type from name's file extension.\n//\n// The name is otherwise unused; in particular it can be empty and is\n// never sent in the response.\n//\n// If modtime is not the zero time or Unix epoch, ServeContent\n// includes it in a Last-Modified header in the response. If the\n// request includes an If-Modified-Since header, ServeContent uses\n// modtime to decide whether the content needs to be sent at all.\n//\n// The content's Seek method must work: ServeContent uses\n// a seek to the end of the content to determine its size.\n//\n// If the caller has set w's ETag header formatted per RFC 7232, section 2.3,\n// ServeContent uses it to handle requests using If-Match, If-None-Match, or If-Range.\n//\n// Note that *os.File implements the io.ReadSeeker interface.\n// Note that compression can be registered\n// through `ctx.CompressWriter(true)` or `app.Use(iris.Compression)`.\nfunc (ctx *Context) ServeContent(content io.ReadSeeker, filename string, modtime time.Time) {\n\tctx.ServeContentWithRate(content, filename, modtime, 0, 0)\n}\n\n// rateReadSeeker is a io.ReadSeeker that is rate limited by\n// the given token bucket. Each token in the bucket\n// represents one byte. See \"golang.org/x/time/rate\" package.\ntype rateReadSeeker struct {\n\tio.ReadSeeker\n\tctx     context.Context\n\tlimiter *rate.Limiter\n}\n\nfunc (rs *rateReadSeeker) Read(buf []byte) (int, error) {\n\tn, err := rs.ReadSeeker.Read(buf)\n\tif n <= 0 {\n\t\treturn n, err\n\t}\n\terr = rs.limiter.WaitN(rs.ctx, n)\n\treturn n, err\n}\n\n// ServeContentWithRate same as `ServeContent` but it can throttle the speed of reading\n// and though writing the \"content\" to the client.\nfunc (ctx *Context) ServeContentWithRate(content io.ReadSeeker, filename string, modtime time.Time, limit float64, burst int) {\n\tif limit > 0 {\n\t\tcontent = &rateReadSeeker{\n\t\t\tReadSeeker: content,\n\t\t\tctx:        ctx.request.Context(),\n\t\t\tlimiter:    rate.NewLimiter(rate.Limit(limit), burst),\n\t\t}\n\t}\n\n\thttp.ServeContent(ctx.writer, ctx.request, filename, modtime, content)\n}\n\n// ServeFile replies to the request with the contents of the named\n// file or directory.\n//\n// If the provided file or directory name is a relative path, it is\n// interpreted relative to the current directory and may ascend to\n// parent directories. If the provided name is constructed from user\n// input, it should be sanitized before calling `ServeFile`.\n//\n// Use it when you want to serve assets like css and javascript files.\n// If client should confirm and save the file use the `SendFile` instead.\n// Note that compression can be registered\n// through `ctx.CompressWriter(true)` or `app.Use(iris.Compression)`.\nfunc (ctx *Context) ServeFile(filename string) error {\n\treturn ctx.ServeFileWithRate(filename, 0, 0)\n}\n\n// ServeFileWithRate same as `ServeFile` but it can throttle the speed of reading\n// and though writing the file to the client.\nfunc (ctx *Context) ServeFileWithRate(filename string, limit float64, burst int) error {\n\tf, err := os.Open(filename)\n\tif err != nil {\n\t\tctx.StatusCode(http.StatusNotFound)\n\t\treturn err\n\t}\n\tdefer f.Close()\n\n\tst, err := f.Stat()\n\tif err != nil {\n\t\tcode := http.StatusInternalServerError\n\t\tif os.IsNotExist(err) {\n\t\t\tcode = http.StatusNotFound\n\t\t}\n\n\t\tif os.IsPermission(err) {\n\t\t\tcode = http.StatusForbidden\n\t\t}\n\n\t\tctx.StatusCode(code)\n\t\treturn err\n\t}\n\n\tif st.IsDir() {\n\t\treturn ctx.ServeFile(path.Join(filename, \"index.html\"))\n\t}\n\n\tctx.ServeContentWithRate(f, st.Name(), st.ModTime(), limit, burst)\n\treturn nil\n}\n\n// SendFile sends a file as an attachment, that is downloaded and saved locally from client.\n// Note that compression can be registered\n// through `ctx.CompressWriter(true)` or `app.Use(iris.Compression)`.\n// Use `ServeFile` if a file should be served as a page asset instead.\nfunc (ctx *Context) SendFile(src string, destName string) error {\n\treturn ctx.SendFileWithRate(src, destName, 0, 0)\n}\n\n// SendFileWithRate same as `SendFile` but it can throttle the speed of reading\n// and though writing the file to the client.\nfunc (ctx *Context) SendFileWithRate(src, destName string, limit float64, burst int) error {\n\tif destName == \"\" {\n\t\tdestName = filepath.Base(src)\n\t}\n\n\tctx.writer.Header().Set(ContentDispositionHeaderKey, MakeDisposition(destName))\n\treturn ctx.ServeFileWithRate(src, limit, burst)\n}\n\n// MakeDisposition generates an HTTP Content-Disposition field-value.\n// Similar solution followed by: Spring(Java), Symfony(PHP) and Ruby on Rails frameworks too.\n//\n// Fixes CVE-2020-5398. Reported by motoyasu-saburi.\nfunc MakeDisposition(filename string) string {\n\treturn `attachment; filename*=UTF-8''` + url.QueryEscape(filename)\n} /*\n// Found at: https://stackoverflow.com/questions/53069040/checking-a-string-contains-only-ascii-characters\n// A faster (better, more idiomatic) version, which avoids unnecessary rune conversions.\nfunc isASCII(s string) bool {\n\tfor i := 0; i < len(s); i++ {\n\t\tif s[i] > unicode.MaxASCII {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\nfunc isRFC5987AttrChar(c rune) bool {\n\treturn (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||\n\t\tc == '!' || c == '#' || c == '$' || c == '&' || c == '+' || c == '-' ||\n\t\tc == '.' || c == '^' || c == '_' || c == '`' || c == '|' || c == '~'\n}\n*/\n\n//  +------------------------------------------------------------+\n//  | Cookies                                                    |\n//  +------------------------------------------------------------+\n\n// Set of Cookie actions for `CookieOption`.\nconst (\n\tOpCookieGet uint8 = iota\n\tOpCookieSet\n\tOpCookieDel\n)\n\n// CookieOption is the type of function that is accepted on\n// context's methods like `SetCookieKV`, `RemoveCookie` and `SetCookie`\n// as their (last) variadic input argument to amend the to-be-sent cookie.\n//\n// The \"op\" is the operation code, 0 is GET, 1 is SET and 2 is REMOVE.\ntype CookieOption func(ctx *Context, c *http.Cookie, op uint8)\n\n// CookieIncluded reports whether the \"cookie.Name\" is in the list of \"cookieNames\".\n// Notes:\n// If \"cookieNames\" slice is empty then it returns true,\n// If \"cookie.Name\" is empty then it returns false.\nfunc CookieIncluded(cookie *http.Cookie, cookieNames []string) bool {\n\tif cookie.Name == \"\" {\n\t\treturn false\n\t}\n\n\tif len(cookieNames) > 0 {\n\t\tfor _, name := range cookieNames {\n\t\t\tif cookie.Name == name {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\n\t\treturn false\n\t}\n\n\treturn true\n}\n\n// var cookieNameSanitizer = strings.NewReplacer(\"\\n\", \"-\", \"\\r\", \"-\")\n//\n// func sanitizeCookieName(n string) string {\n// \treturn cookieNameSanitizer.Replace(n)\n// }\n\n// CookieOverride is a CookieOption which overrides the cookie explicitly to the given \"cookie\".\n//\n// Usage:\n// ctx.RemoveCookie(\"the_cookie_name\", iris.CookieOverride(&http.Cookie{Domain: \"example.com\"}))\nfunc CookieOverride(cookie *http.Cookie) CookieOption { // The \"Cookie\" word method name is reserved as it's used as an alias.\n\treturn func(_ *Context, c *http.Cookie, op uint8) {\n\t\tif op == OpCookieGet {\n\t\t\treturn\n\t\t}\n\n\t\t*cookie = *c\n\t}\n}\n\n// CookieDomain is a CookieOption which sets the cookie's Domain field.\n// If empty then the current domain is used.\n//\n// Usage:\n// ctx.RemoveCookie(\"the_cookie_name\", iris.CookieDomain(\"example.com\"))\nfunc CookieDomain(domain string) CookieOption {\n\treturn func(_ *Context, c *http.Cookie, op uint8) {\n\t\tif op == OpCookieGet {\n\t\t\treturn\n\t\t}\n\n\t\tc.Domain = domain\n\t}\n}\n\n// CookieAllowReclaim accepts the Context itself.\n// If set it will add the cookie to (on `CookieSet`, `CookieSetKV`, `CookieUpsert`)\n// or remove the cookie from (on `CookieRemove`) the Request object too.\nfunc CookieAllowReclaim(cookieNames ...string) CookieOption {\n\treturn func(ctx *Context, c *http.Cookie, op uint8) {\n\t\tif op == OpCookieGet {\n\t\t\treturn\n\t\t}\n\n\t\tif !CookieIncluded(c, cookieNames) {\n\t\t\treturn\n\t\t}\n\n\t\tswitch op {\n\t\tcase OpCookieSet:\n\t\t\t// perform upsert on request cookies or is it too much and not worth the cost?\n\t\t\tctx.Request().AddCookie(c)\n\t\tcase OpCookieDel:\n\t\t\tcookies := ctx.Request().Cookies()\n\t\t\tctx.Request().Header.Del(\"Cookie\")\n\t\t\tfor i, v := range cookies {\n\t\t\t\tif v.Name != c.Name {\n\t\t\t\t\tctx.Request().AddCookie(cookies[i])\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n// CookieAllowSubdomains set to the Cookie Options\n// in order to allow subdomains to have access to the cookies.\n// It sets the cookie's Domain field (if was empty) and\n// it also sets the cookie's SameSite to lax mode too.\nfunc CookieAllowSubdomains(cookieNames ...string) CookieOption {\n\treturn func(ctx *Context, c *http.Cookie, _ uint8) {\n\t\tif c.Domain != \"\" {\n\t\t\treturn // already set.\n\t\t}\n\n\t\tif !CookieIncluded(c, cookieNames) {\n\t\t\treturn\n\t\t}\n\n\t\tc.Domain = ctx.Domain()\n\t\tc.SameSite = http.SameSiteLaxMode // allow subdomain sharing.\n\t}\n}\n\n// CookieSameSite sets a same-site rule for cookies to set.\n// SameSite allows a server to define a cookie attribute making it impossible for\n// the browser to send this cookie along with cross-site requests. The main\n// goal is to mitigate the risk of cross-origin information leakage, and provide\n// some protection against cross-site request forgery attacks.\n//\n// See https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00 for details.\nfunc CookieSameSite(sameSite http.SameSite) CookieOption {\n\treturn func(_ *Context, c *http.Cookie, op uint8) {\n\t\tif op == OpCookieSet {\n\t\t\tc.SameSite = sameSite\n\t\t}\n\t}\n}\n\n// CookieSecure sets the cookie's Secure option if the current request's\n// connection is using TLS. See `CookieHTTPOnly` too.\nfunc CookieSecure(ctx *Context, c *http.Cookie, op uint8) {\n\tif op == OpCookieSet {\n\t\tif ctx.Request().TLS != nil {\n\t\t\tc.Secure = true\n\t\t}\n\t}\n}\n\n// CookieHTTPOnly is a `CookieOption`.\n// Use it to set the cookie's HttpOnly field to false or true.\n// HttpOnly field defaults to true for `RemoveCookie` and `SetCookieKV`.\n// See `CookieSecure` too.\nfunc CookieHTTPOnly(httpOnly bool) CookieOption {\n\treturn func(_ *Context, c *http.Cookie, op uint8) {\n\t\tif op == OpCookieSet {\n\t\t\tc.HttpOnly = httpOnly\n\t\t}\n\t}\n}\n\n// CookiePath is a `CookieOption`.\n// Use it to change the cookie's Path field.\nfunc CookiePath(path string) CookieOption {\n\treturn func(_ *Context, c *http.Cookie, op uint8) {\n\t\tif op > OpCookieGet { // on set and remove.\n\t\t\tc.Path = path\n\t\t}\n\t}\n}\n\n// CookieCleanPath is a `CookieOption`.\n// Use it to clear the cookie's Path field, exactly the same as `CookiePath(\"\")`.\nfunc CookieCleanPath(_ *Context, c *http.Cookie, op uint8) {\n\tif op > OpCookieGet {\n\t\tc.Path = \"\"\n\t}\n}\n\n// CookieExpires is a `CookieOption`.\n// Use it to change the cookie's Expires and MaxAge fields by passing the lifetime of the cookie.\nfunc CookieExpires(durFromNow time.Duration) CookieOption {\n\treturn func(_ *Context, c *http.Cookie, op uint8) {\n\t\tif op == OpCookieSet {\n\t\t\tc.Expires = time.Now().Add(durFromNow)\n\t\t\tc.MaxAge = int(durFromNow.Seconds())\n\t\t}\n\t}\n}\n\n// SecureCookie should encodes and decodes\n// authenticated and optionally encrypted cookie values.\n// See `CookieEncoding` package-level function.\ntype SecureCookie interface {\n\t// Encode should encode the cookie value.\n\t// Should accept the cookie's name as its first argument\n\t// and as second argument the cookie value ptr.\n\t// Should return an encoded value or an empty one if encode operation failed.\n\t// Should return an error if encode operation failed.\n\t//\n\t// Note: Errors are not printed, so you have to know what you're doing,\n\t// and remember: if you use AES it only supports key sizes of 16, 24 or 32 bytes.\n\t// You either need to provide exactly that amount or you derive the key from what you type in.\n\t//\n\t// See `Decode` too.\n\tEncode(cookieName string, cookieValue any) (string, error)\n\t// Decode should decode the cookie value.\n\t// Should accept the cookie's name as its first argument,\n\t// as second argument the encoded cookie value and as third argument the decoded value ptr.\n\t// Should return a decoded value or an empty one if decode operation failed.\n\t// Should return an error if decode operation failed.\n\t//\n\t// Note: Errors are not printed, so you have to know what you're doing,\n\t// and remember: if you use AES it only supports key sizes of 16, 24 or 32 bytes.\n\t// You either need to provide exactly that amount or you derive the key from what you type in.\n\t//\n\t// See `Encode` too.\n\tDecode(cookieName string, cookieValue string, cookieValuePtr any) error\n}\n\n// CookieEncoding accepts a value which implements `Encode` and `Decode` methods.\n// It calls its `Encode` on `Context.SetCookie, UpsertCookie, and SetCookieKV` methods.\n// And on `Context.GetCookie` method it calls its `Decode`.\n// If \"cookieNames\" slice is not empty then only cookies\n// with that `Name` will be encoded on set and decoded on get, that way you can encrypt\n// specific cookie names (like the session id) and let the rest of the cookies \"insecure\".\n//\n// Example: https://github.com/kataras/iris/tree/main/_examples/cookies/securecookie\nfunc CookieEncoding(encoding SecureCookie, cookieNames ...string) CookieOption {\n\tif encoding == nil {\n\t\treturn func(_ *Context, _ *http.Cookie, _ uint8) {}\n\t}\n\n\treturn func(ctx *Context, c *http.Cookie, op uint8) {\n\t\tif op == OpCookieDel {\n\t\t\treturn\n\t\t}\n\n\t\tif !CookieIncluded(c, cookieNames) {\n\t\t\treturn\n\t\t}\n\n\t\tswitch op {\n\t\tcase OpCookieSet:\n\t\t\t// Should encode, it's a write to the client operation.\n\t\t\tnewVal, err := encoding.Encode(c.Name, c.Value)\n\t\t\tif err != nil {\n\t\t\t\tctx.Application().Logger().Error(err)\n\t\t\t\tc.Value = \"\"\n\t\t\t} else {\n\t\t\t\tc.Value = newVal\n\t\t\t}\n\n\t\t\treturn\n\t\tcase OpCookieGet:\n\t\t\t// Should decode, it's a read from the client operation.\n\t\t\tif err := encoding.Decode(c.Name, c.Value, &c.Value); err != nil {\n\t\t\t\tc.Value = \"\"\n\t\t\t}\n\t\t}\n\t}\n}\n\nconst cookieOptionsContextKey = \"iris.cookie.options\"\n\n// AddCookieOptions adds cookie options for `SetCookie`,\n// `SetCookieKV, UpsertCookie` and `RemoveCookie` methods\n// for the current request. It can be called from a middleware before\n// cookies sent or received from the next Handler in the chain.\n//\n// Available builtin Cookie options are:\n//   - CookieOverride\n//   - CookieDomain\n//   - CookieAllowReclaim\n//   - CookieAllowSubdomains\n//   - CookieSecure\n//   - CookieHTTPOnly\n//   - CookieSameSite\n//   - CookiePath\n//   - CookieCleanPath\n//   - CookieExpires\n//   - CookieEncoding\n//\n// Example at: https://github.com/kataras/iris/tree/main/_examples/cookies/securecookie\nfunc (ctx *Context) AddCookieOptions(options ...CookieOption) {\n\tif len(options) == 0 {\n\t\treturn\n\t}\n\n\tif v := ctx.values.Get(cookieOptionsContextKey); v != nil {\n\t\tif opts, ok := v.([]CookieOption); ok {\n\t\t\toptions = append(opts, options...)\n\t\t}\n\t}\n\n\tctx.values.Set(cookieOptionsContextKey, options)\n}\n\nfunc (ctx *Context) applyCookieOptions(c *http.Cookie, op uint8, override []CookieOption) {\n\tif v := ctx.values.Get(cookieOptionsContextKey); v != nil {\n\t\tif options, ok := v.([]CookieOption); ok {\n\t\t\tfor _, opt := range options {\n\t\t\t\topt(ctx, c, op)\n\t\t\t}\n\t\t}\n\t}\n\n\t// The function's ones should be called last, so they can override\n\t// the stored ones (i.e by a prior middleware).\n\tfor _, opt := range override {\n\t\topt(ctx, c, op)\n\t}\n}\n\n// ClearCookieOptions clears any previously registered cookie options.\n// See `AddCookieOptions` too.\nfunc (ctx *Context) ClearCookieOptions() {\n\tctx.values.Remove(cookieOptionsContextKey)\n}\n\n// SetCookie adds a cookie.\n// Use of the \"options\" is not required, they can be used to amend the \"cookie\".\n//\n// Example: https://github.com/kataras/iris/tree/main/_examples/cookies/basic\nfunc (ctx *Context) SetCookie(cookie *http.Cookie, options ...CookieOption) {\n\tctx.applyCookieOptions(cookie, OpCookieSet, options)\n\thttp.SetCookie(ctx.writer, cookie)\n}\n\nconst setCookieHeaderKey = \"Set-Cookie\"\n\n// UpsertCookie adds a cookie to the response like `SetCookie` does\n// but it will also perform a replacement of the cookie\n// if already set by a previous `SetCookie` call.\n// It reports whether the cookie is new (true) or an existing one was updated (false).\nfunc (ctx *Context) UpsertCookie(cookie *http.Cookie, options ...CookieOption) bool {\n\tctx.applyCookieOptions(cookie, OpCookieSet, options)\n\n\theader := ctx.ResponseWriter().Header()\n\tif cookies := header[setCookieHeaderKey]; len(cookies) > 0 {\n\t\ts := cookie.Name + \"=\" // name=?value\n\n\t\texistingUpdated := false\n\n\t\tfor i, c := range cookies {\n\t\t\tif strings.HasPrefix(c, s) {\n\t\t\t\tif existingUpdated { // fixes #1877\n\t\t\t\t\t// remove any duplicated.\n\t\t\t\t\tcookies[i] = \"\"\n\t\t\t\t\theader[setCookieHeaderKey] = cookies\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\t// We need to update the Set-Cookie (to update the expiration or any other cookie's properties).\n\t\t\t\t// Probably the cookie is set and then updated in the first session creation\n\t\t\t\t// (e.g. UpdateExpiration, see https://github.com/kataras/iris/issues/1485).\n\t\t\t\tcookies[i] = cookie.String()\n\t\t\t\theader[setCookieHeaderKey] = cookies\n\t\t\t\texistingUpdated = true\n\t\t\t}\n\t\t}\n\n\t\tif existingUpdated {\n\t\t\treturn false // existing one updated.\n\t\t}\n\t}\n\n\theader.Add(setCookieHeaderKey, cookie.String())\n\treturn true\n}\n\n// SetCookieKVExpiration is 365 days by-default\n// you can change it or simple, use the SetCookie for more control.\n//\n// See `CookieExpires` and `AddCookieOptions` for more.\nvar SetCookieKVExpiration = 8760 * time.Hour\n\n// SetCookieKV adds a cookie, requires the name(string) and the value(string).\n//\n// By default it expires after 365 days and it is added to the root URL path,\n// use the `CookieExpires` and `CookiePath` to modify them.\n// Alternatively: ctx.SetCookie(&http.Cookie{...}) or ctx.AddCookieOptions(...)\n//\n// If you want to set custom the path:\n// ctx.SetCookieKV(name, value, iris.CookiePath(\"/custom/path/cookie/will/be/stored\"))\n//\n// If you want to be visible only to current request path:\n// (note that client should be responsible for that if server sent an empty cookie's path, all browsers are compatible)\n// ctx.SetCookieKV(name, value, iris.CookieCleanPath/iris.CookiePath(\"\"))\n// More:\n//\n//\tiris.CookieExpires(time.Duration)\n//\tiris.CookieHTTPOnly(false)\n//\n// Examples: https://github.com/kataras/iris/tree/main/_examples/cookies/basic\nfunc (ctx *Context) SetCookieKV(name, value string, options ...CookieOption) {\n\tc := &http.Cookie{}\n\tc.Path = \"/\"\n\tc.Name = name\n\tc.Value = url.QueryEscape(value)\n\tc.HttpOnly = true\n\n\t// MaxAge=0 means no 'Max-Age' attribute specified.\n\t// MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0'\n\t// MaxAge>0 means Max-Age attribute present and given in seconds\n\tc.Expires = time.Now().Add(SetCookieKVExpiration)\n\tc.MaxAge = int(time.Until(c.Expires).Seconds())\n\n\tctx.SetCookie(c, options...)\n}\n\n// GetCookie returns cookie's value by its name\n// returns empty string if nothing was found.\n//\n// If you want more than the value then:\n// cookie, err := ctx.GetRequestCookie(\"name\")\n//\n// Example: https://github.com/kataras/iris/tree/main/_examples/cookies/basic\nfunc (ctx *Context) GetCookie(name string, options ...CookieOption) string {\n\tc, err := ctx.GetRequestCookie(name, options...)\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\n\tvalue, _ := url.QueryUnescape(c.Value)\n\treturn value\n}\n\n// GetRequestCookie returns the request cookie including any context's cookie options (stored or given by this method).\nfunc (ctx *Context) GetRequestCookie(name string, options ...CookieOption) (*http.Cookie, error) {\n\tc, err := ctx.request.Cookie(name)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tctx.applyCookieOptions(c, OpCookieGet, options)\n\treturn c, nil\n}\n\nvar (\n\t// CookieExpireDelete may be set on Cookie.Expire for expiring the given cookie.\n\tCookieExpireDelete = memstore.ExpireDelete\n\n\t// CookieExpireUnlimited indicates that does expires after 24 years.\n\tCookieExpireUnlimited = time.Now().AddDate(24, 10, 10)\n)\n\n// RemoveCookie deletes a cookie by its name and path = \"/\".\n// Tip: change the cookie's path to the current one by: RemoveCookie(\"the_cookie_name\", iris.CookieCleanPath)\n//\n// If you intend to remove a cookie with a specific domain and value, please ensure to pass these values explicitly:\n//\n//\tctx.RemoveCookie(\"the_cookie_name\", iris.CookieDomain(\"example.com\"), iris.CookiePath(\"/\"))\n//\n// OR use a Cookie value instead:\n//\n//\tctx.RemoveCookie(\"the_cookie_name\", iris.CookieOverride(&http.Cookie{Domain: \"example.com\", Path: \"/\"}))\n//\n// Example: https://github.com/kataras/iris/tree/main/_examples/cookies/basic\nfunc (ctx *Context) RemoveCookie(name string, options ...CookieOption) {\n\tc := &http.Cookie{Path: \"/\"}\n\t// Send the cookie back to the client\n\tctx.applyCookieOptions(c, OpCookieDel, options)\n\tc.Name = name\n\tc.Value = \"\"\n\tc.HttpOnly = true\n\t// Set the cookie expiration date to a past time\n\tc.Expires = CookieExpireDelete\n\tc.MaxAge = -1 // RFC says 1 second, but let's do it -1  to make sure is working.\n\n\thttp.SetCookie(ctx.writer, c)\n}\n\n// VisitAllCookies takes a visitor function which is called\n// on each (request's) cookies' name and value.\nfunc (ctx *Context) VisitAllCookies(visitor func(name string, value string)) {\n\tfor _, cookie := range ctx.request.Cookies() {\n\t\tvisitor(cookie.Name, cookie.Value)\n\t}\n}\n\nvar maxAgeExp = regexp.MustCompile(`maxage=(\\d+)`)\n\n// MaxAge returns the \"cache-control\" request header's value\n// seconds as int64\n// if header not found or parse failed then it returns -1.\nfunc (ctx *Context) MaxAge() int64 {\n\theader := ctx.GetHeader(CacheControlHeaderKey)\n\tif header == \"\" {\n\t\treturn -1\n\t}\n\tm := maxAgeExp.FindStringSubmatch(header)\n\tif len(m) == 2 {\n\t\tif v, err := strconv.Atoi(m[1]); err == nil {\n\t\t\treturn int64(v)\n\t\t}\n\t}\n\treturn -1\n}\n\n//  +------------------------------------------------------------+\n//  | Advanced: Response Recorder                                |\n//  +------------------------------------------------------------+\n\n// Record transforms the context's basic and direct responseWriter to a *ResponseRecorder\n// which can be used to reset the body, reset headers, get the body,\n// get & set the status code at any time and more.\nfunc (ctx *Context) Record() {\n\tswitch w := ctx.writer.(type) {\n\tcase *ResponseRecorder:\n\tdefault:\n\t\trecorder := AcquireResponseRecorder()\n\t\trecorder.BeginRecord(w)\n\t\tctx.ResetResponseWriter(recorder)\n\t}\n}\n\n// Recorder returns the context's ResponseRecorder\n// if not recording then it starts recording and returns the new context's ResponseRecorder\nfunc (ctx *Context) Recorder() *ResponseRecorder {\n\tctx.Record()\n\treturn ctx.writer.(*ResponseRecorder)\n}\n\n// IsRecording returns the response recorder and a true value\n// when the response writer is recording the status code, body, headers and so on,\n// else returns nil and false.\nfunc (ctx *Context) IsRecording() (*ResponseRecorder, bool) {\n\t// NOTE:\n\t// two return values in order to minimize the if statement:\n\t// if (Recording) then writer = Recorder()\n\t// instead we do: recorder,ok = Recording()\n\trr, ok := ctx.writer.(*ResponseRecorder)\n\treturn rr, ok\n}\n\n// Exec calls the framewrok's ServeHTTPC\n// based on this context but with a changed method and path\n// like it was requested by the user, but it is not.\n//\n// Offline means that the route is registered to the iris and have all features that a normal route has\n// BUT it isn't available by browsing, its handlers executed only when other handler's context call them\n// it can validate paths, has sessions, path parameters and all.\n//\n// You can find the Route by app.GetRoute(\"theRouteName\")\n// you can set a route name as: myRoute := app.Get(\"/mypath\", handler)(\"theRouteName\")\n// that will set a name to the route and returns its RouteInfo instance for further usage.\n//\n// It doesn't changes the global state, if a route was \"offline\" it remains offline.\n//\n// app.None(...) and app.GetRoutes().Offline(route)/.Online(route, method)\n//\n// Example: https://github.com/kataras/iris/tree/main/_examples/routing/route-state\n//\n// User can get the response by simple using rec := ctx.Recorder(); rec.Body()/rec.StatusCode()/rec.Header().\n//\n// context's Values and the Session are kept in order to be able to communicate via the result route.\n//\n// It's for extreme use cases, 99% of the times will never be useful for you.\nfunc (ctx *Context) Exec(method string, path string) {\n\tif path == \"\" {\n\t\treturn\n\t}\n\n\tif method == \"\" {\n\t\tmethod = \"GET\"\n\t}\n\n\t// backup the handlers\n\tbackupHandlers := ctx.handlers[0:]\n\tbackupPos := ctx.currentHandlerIndex\n\n\treq := ctx.request\n\t// backup the request path information\n\tbackupPath := req.URL.Path\n\tbackupMethod := req.Method\n\t// don't backupValues := ctx.values.ReadOnly()\n\t// set the request to be align with the 'againstRequestPath'\n\treq.RequestURI = path\n\treq.URL.Path = path\n\treq.Method = method\n\n\t// [values stays]\n\t// reset handlers\n\tctx.handlers = ctx.handlers[0:0]\n\tctx.currentHandlerIndex = 0\n\n\t// execute the route from the (internal) context router\n\t// this way we keep the sessions and the values\n\tctx.app.ServeHTTPC(ctx)\n\n\t// set the request back to its previous state\n\treq.RequestURI = backupPath\n\treq.URL.Path = backupPath\n\treq.Method = backupMethod\n\n\t// set back the old handlers and the last known index\n\tctx.handlers = backupHandlers\n\tctx.currentHandlerIndex = backupPos\n}\n\n// RouteExists reports whether a particular route exists\n// It will search from the current subdomain of context's host, if not inside the root domain.\nfunc (ctx *Context) RouteExists(method, path string) bool {\n\treturn ctx.app.RouteExists(ctx, method, path)\n}\n\nconst (\n\treflectValueContextKey = \"iris.context.reflect_value\"\n\t// ControllerContextKey returns the context key from which\n\t// the `Context.Controller` method returns the store's value.\n\tControllerContextKey = \"iris.controller.reflect_value\"\n)\n\n// ReflectValue caches and returns a []reflect.Value{reflect.ValueOf(ctx)}.\n// It's just a helper to maintain variable inside the context itself.\nfunc (ctx *Context) ReflectValue() []reflect.Value {\n\tif v := ctx.values.Get(reflectValueContextKey); v != nil {\n\t\treturn v.([]reflect.Value)\n\t}\n\n\tv := []reflect.Value{reflect.ValueOf(ctx)}\n\tctx.values.Set(reflectValueContextKey, v)\n\treturn v\n}\n\nvar emptyValue reflect.Value\n\n// Controller returns a reflect Value of the custom Controller from which this handler executed.\n// It will return a Kind() == reflect.Invalid if the handler was not executed from within a controller.\nfunc (ctx *Context) Controller() reflect.Value {\n\tif v := ctx.values.Get(ControllerContextKey); v != nil {\n\t\treturn v.(reflect.Value)\n\t}\n\n\treturn emptyValue\n}\n\n// DependenciesContextKey is the context key for the context's value\n// to keep the serve-time static dependencies raw values.\nconst DependenciesContextKey = \"iris.dependencies\"\n\n// DependenciesMap is the type which context serve-time\n// struct dependencies are stored with.\ntype DependenciesMap map[reflect.Type]reflect.Value\n\n// RegisterDependency registers a struct or slice\n// or pointer to struct dependency at request-time\n// for the next handler in the chain. One value per type.\n// Note that it's highly recommended to register\n// your dependencies before server ran\n// through Party.ConfigureContainer or mvc.Application.Register\n// in sake of minimum performance cost.\n//\n// See `UnregisterDependency` too.\nfunc (ctx *Context) RegisterDependency(v any) {\n\tif v == nil {\n\t\treturn\n\t}\n\n\tval, ok := v.(reflect.Value)\n\tif !ok {\n\t\tval = reflect.ValueOf(v)\n\t}\n\n\tcv := ctx.values.Get(DependenciesContextKey)\n\tif cv != nil {\n\t\tm, ok := cv.(DependenciesMap)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\n\t\tm[val.Type()] = val\n\t\treturn\n\t}\n\n\tctx.values.Set(DependenciesContextKey, DependenciesMap{\n\t\tval.Type(): val,\n\t})\n}\n\n// UnregisterDependency removes a dependency based on its type.\n// Reports whether a dependency with that type was found and removed successfully.\nfunc (ctx *Context) UnregisterDependency(typ reflect.Type) bool {\n\tcv := ctx.values.Get(DependenciesContextKey)\n\tif cv != nil {\n\t\tm, ok := cv.(DependenciesMap)\n\t\tif ok {\n\t\t\tdelete(m, typ)\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\n// Application returns the iris app instance which belongs to this context.\n// Worth to notice that this function returns an interface\n// of the Application, which contains methods that are safe\n// to be executed at serve-time. The full app's fields\n// and methods are not available here for the developer's safety.\nfunc (ctx *Context) Application() Application {\n\treturn ctx.app\n}\n\n// IsDebug reports whether the application runs with debug log level.\n// It is a shortcut of Application.IsDebug().\nfunc (ctx *Context) IsDebug() bool {\n\treturn ctx.app.IsDebug()\n}\n\n// SetErr is just a helper that sets an error value\n// as a context value, it does nothing more.\n// Also, by-default this error's value is written to the client\n// on failures when no registered error handler is available (see `Party.On(Any)ErrorCode`).\n// See `GetErr` to retrieve it back.\n//\n// To remove an error simply pass nil.\n//\n// Note that, if you want to stop the chain\n// with an error see the `StopWithError/StopWithPlainError` instead.\nfunc (ctx *Context) SetErr(err error) {\n\tif err == nil {\n\t\tctx.values.Remove(errorContextKey)\n\t\treturn\n\t}\n\n\tctx.values.Set(errorContextKey, err)\n}\n\n// GetErr is a helper which retrieves\n// the error value stored by `SetErr`.\n//\n// Note that, if an error was stored by `SetErrPrivate`\n// then it returns the underline/original error instead\n// of the internal error wrapper.\nfunc (ctx *Context) GetErr() error {\n\t_, err := ctx.GetErrPublic()\n\treturn err\n}\n\n// ErrPrivate if provided then the error saved in context\n// should NOT be visible to the client no matter what.\ntype ErrPrivate interface {\n\terror\n\tIrisPrivateError()\n}\n\n// An internal wrapper for the `SetErrPrivate` method.\ntype privateError struct{ error }\n\nfunc (e privateError) IrisPrivateError() {}\n\n// PrivateError accepts an error and returns a wrapped private one.\nfunc PrivateError(err error) ErrPrivate {\n\tif err == nil {\n\t\treturn nil\n\t}\n\n\terrPrivate, ok := err.(ErrPrivate)\n\tif !ok {\n\t\terrPrivate = privateError{err}\n\t}\n\n\treturn errPrivate\n}\n\nconst errorContextKey = \"iris.context.error\"\n\n// SetErrPrivate sets an error that it's only accessible through `GetErr`\n// and it should never be sent to the client.\n//\n// Same as ctx.SetErr with an error that completes the `ErrPrivate` interface.\n// See `GetErrPublic` too.\nfunc (ctx *Context) SetErrPrivate(err error) {\n\tctx.SetErr(PrivateError(err))\n}\n\n// GetErrPublic reports whether the stored error\n// can be displayed to the client without risking\n// to expose security server implementation to the client.\n//\n// If the error is not nil, it is always the original one.\nfunc (ctx *Context) GetErrPublic() (bool, error) {\n\tif v := ctx.values.Get(errorContextKey); v != nil {\n\t\tswitch err := v.(type) {\n\t\tcase privateError:\n\t\t\t// If it's an error set by SetErrPrivate then unwrap it.\n\t\t\treturn false, err.error\n\t\tcase ErrPrivate:\n\t\t\treturn false, err\n\t\tcase error:\n\t\t\treturn true, err\n\t\t}\n\t}\n\n\treturn false, nil\n}\n\n// ErrPanicRecovery may be returned from `Context` actions of a `Handler`\n// which recovers from a manual panic.\ntype ErrPanicRecovery struct {\n\tErrPrivate\n\tCause                  any\n\tCallers                []string // file:line callers.\n\tStack                  []byte   // the full debug stack.\n\tRegisteredHandlers     []string // file:line of all registered handlers.\n\tCurrentHandlerFileLine string   // the handler panic came from.\n\tCurrentHandlerName     string   // the handler name panic came from.\n\tRequest                string   // the http dumped request.\n}\n\n// Error implements the Go standard error type.\nfunc (e *ErrPanicRecovery) Error() string {\n\tif e.Cause != nil {\n\t\tif err, ok := e.Cause.(error); ok {\n\t\t\treturn err.Error()\n\t\t}\n\t}\n\n\treturn fmt.Sprintf(\"%v\\n%s\\nRequest:\\n%s\", e.Cause, strings.Join(e.Callers, \"\\n\"), e.Request)\n}\n\n// Is completes the internal errors.Is interface.\nfunc (e *ErrPanicRecovery) Is(err error) bool {\n\t_, ok := IsErrPanicRecovery(err)\n\treturn ok\n}\n\nfunc (e *ErrPanicRecovery) LogMessage() string {\n\tlogMessage := fmt.Sprintf(\"Recovered from a route's Handler('%s')\\n\", e.CurrentHandlerName)\n\tlogMessage += fmt.Sprint(e.Request)\n\tlogMessage += fmt.Sprintf(\"%s\\n\", e.Cause)\n\tlogMessage += fmt.Sprintf(\"%s\\n\", strings.Join(e.Callers, \"\\n\"))\n\n\treturn logMessage\n}\n\n// IsErrPanicRecovery reports whether the given \"err\" is a type of ErrPanicRecovery.\nfunc IsErrPanicRecovery(err error) (*ErrPanicRecovery, bool) {\n\tif err == nil {\n\t\treturn nil, false\n\t}\n\tv, ok := err.(*ErrPanicRecovery)\n\treturn v, ok\n}\n\n// IsRecovered reports whether this handler has been recovered\n// by the Iris recover middleware.\nfunc (ctx *Context) IsRecovered() (*ErrPanicRecovery, bool) {\n\tif ctx.GetStatusCode() == http.StatusInternalServerError {\n\t\t// Panic error from recovery middleware is private.\n\t\treturn IsErrPanicRecovery(ctx.GetErr())\n\t}\n\n\treturn nil, false\n}\n\nconst (\n\tfuncsContextPrefixKey = \"iris.funcs.\"\n\tfuncLogoutContextKey  = \"auth.logout_func\"\n)\n\n// SetFunc registers a custom function to this Request.\n// It's a helper to pass dynamic functions across handlers of the same chain.\n// For a more complete solution please use Dependency Injection instead.\n// This is just an easy to way to pass a function to the\n// next handler like ctx.Values().Set/Get does.\n// Sometimes is faster and easier to pass the object as a request value\n// and cast it when you want to use one of its methods instead of using\n// these `SetFunc` and `CallFunc` methods.\n// This implementation is suitable for functions that may change inside the\n// handler chain and the object holding the method is not predictable.\n//\n// The \"name\" argument is the custom name of the function,\n// you will use its name to call it later on, e.g. \"auth.myfunc\".\n//\n// The second, \"fn\" argument is the raw function/method you want\n// to pass through the next handler(s) of the chain.\n//\n// The last variadic input argument is optionally, if set\n// then its arguments are passing into the function's input arguments,\n// they should be always be the first ones to be accepted by the \"fn\" inputs,\n// e.g. an object, a receiver or a static service.\n//\n// See its `CallFunc` to call the \"fn\" on the next handler.\n//\n// Example at:\n// https://github.com/kataras/iris/tree/main/_examples/routing/writing-a-middleware/share-funcs\nfunc (ctx *Context) SetFunc(name string, fn any, persistenceArgs ...any) {\n\tf := newFunc(name, fn, persistenceArgs...)\n\tctx.values.Set(funcsContextPrefixKey+name, f)\n}\n\n// GetFunc returns the context function declaration which holds\n// some information about the function registered under the given \"name\" by\n// the `SetFunc` method.\nfunc (ctx *Context) GetFunc(name string) (*Func, bool) {\n\tfn := ctx.values.Get(funcsContextPrefixKey + name)\n\tif fn == nil {\n\t\treturn nil, false\n\t}\n\n\treturn fn.(*Func), true\n}\n\n// CallFunc calls the function registered by the `SetFunc`.\n// The input arguments MUST match the expected ones.\n//\n// If the registered function was just a handler\n// or a handler which returns an error\n// or a simple function\n// or a simple function which returns an error\n// then this operation will perform without any serious cost,\n// otherwise reflection will be used instead, which may slow down the overall performance.\n//\n// Retruns ErrNotFound if the function was not registered.\n//\n// For a more complete solution without limiations navigate through\n// the Iris Dependency Injection feature instead.\nfunc (ctx *Context) CallFunc(name string, args ...any) ([]reflect.Value, error) {\n\tfn, ok := ctx.GetFunc(name)\n\tif !ok || fn == nil {\n\t\treturn nil, ErrNotFound\n\t}\n\n\treturn fn.call(ctx, args...)\n}\n\n// SetLogoutFunc registers a custom logout function that will be\n// available to use inside the next handler(s). The function\n// may be registered multiple times but the last one is the valid.\n// So a logout function may start with basic authentication\n// and other middleware in the chain may change it to a custom sessions logout one.\n// This method uses the `SetFunc` method under the hoods.\n//\n// See `Logout` method too.\nfunc (ctx *Context) SetLogoutFunc(fn any, persistenceArgs ...any) {\n\tctx.SetFunc(funcLogoutContextKey, fn, persistenceArgs...)\n}\n\n// Logout calls the registered logout function.\n// Returns ErrNotFound if a logout function was not specified\n// by a prior call of `SetLogoutFunc`.\nfunc (ctx *Context) Logout(args ...any) error {\n\t_, err := ctx.CallFunc(funcLogoutContextKey, args...)\n\treturn err\n}\n\nconst userContextKey = \"iris.user\"\n\n// SetUser sets a value as a User for this request.\n// It's used by auth middlewares as a common\n// method to provide user information to the\n// next handlers in the chain.\n//\n// The \"i\" input argument can be:\n//   - A value which completes the User interface\n//   - A map[string]any.\n//   - A value which does not complete the whole User interface\n//   - A value which does not complete the User interface at all\n//     (only its `User().GetRaw` method is available).\n//\n// Look the `User` method to retrieve it.\nfunc (ctx *Context) SetUser(i any) error {\n\tif i == nil {\n\t\tctx.values.Remove(userContextKey)\n\t\treturn nil\n\t}\n\n\tu, ok := i.(User)\n\tif !ok {\n\t\tif m, ok := i.(Map); ok { // it's a map, convert it to a User.\n\t\t\tu = UserMap(m)\n\t\t} else {\n\t\t\t// It's a structure, wrap it and let\n\t\t\t// runtime decide the features.\n\t\t\tp := newUserPartial(i)\n\t\t\tif p == nil {\n\t\t\t\treturn ErrNotSupported\n\t\t\t}\n\t\t\tu = p\n\t\t}\n\t}\n\n\tctx.values.Set(userContextKey, u)\n\treturn nil\n}\n\n// User returns the registered User of this request.\n// To get the original value (even if a value set by SetUser does not implement the User interface)\n// use its GetRaw method.\n// /\n// See `SetUser` too.\nfunc (ctx *Context) User() User {\n\tif v := ctx.values.Get(userContextKey); v != nil {\n\t\tif u, ok := v.(User); ok {\n\t\t\treturn u\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// Ensure Iris Context implements the standard Context package, build-time.\nvar _ context.Context = (*Context)(nil)\n\n// Deadline returns the time when work done on behalf of this context\n// should be canceled. Deadline returns ok==false when no deadline is\n// set. Successive calls to Deadline return the same results.\n//\n// Shortcut of Request().Context().Deadline().\nfunc (ctx *Context) Deadline() (deadline time.Time, ok bool) {\n\treturn ctx.request.Context().Deadline()\n}\n\n// Done returns a channel that's closed when work done on behalf of this\n// context should be canceled. Done may return nil if this context can\n// never be canceled. Successive calls to Done return the same value.\n// The close of the Done channel may happen asynchronously,\n// after the cancel function returns.\n//\n// WithCancel arranges for Done to be closed when cancel is called;\n// WithDeadline arranges for Done to be closed when the deadline\n// expires; WithTimeout arranges for Done to be closed when the timeout\n// elapses.\n//\n// Done is provided for use in select statements:\n//\n//\t// Stream generates values with DoSomething and sends them to out\n//\t// until DoSomething returns an error or ctx.Done is closed.\n//\tfunc Stream(ctx context.Context, out chan<- Value) error {\n//\t\tfor {\n//\t\t\tv, err := DoSomething(ctx)\n//\t\t\tif err != nil {\n//\t\t\t\treturn err\n//\t\t\t}\n//\t\t\tselect {\n//\t\t\tcase <-ctx.Done():\n//\t\t\t\treturn ctx.Err()\n//\t\t\tcase out <- v:\n//\t\t\t}\n//\t\t}\n//\t}\n//\n// See https://blog.golang.org/pipelines for more examples of how to use\n// a Done channel for cancellation.\n//\n// Shortcut of Request().Context().Done().\nfunc (ctx *Context) Done() <-chan struct{} {\n\treturn ctx.request.Context().Done()\n}\n\n// If Done is not yet closed, Err returns nil.\n// If Done is closed, Err returns a non-nil error explaining why:\n// Canceled if the context was canceled\n// or DeadlineExceeded if the context's deadline passed.\n// After Err returns a non-nil error, successive calls to Err return the same error.\n//\n// Shortcut of Request().Context().Err().\nfunc (ctx *Context) Err() error {\n\treturn ctx.request.Context().Err()\n}\n\n// Value returns the value associated with this context for key, or nil\n// if no value is associated with key. Successive calls to Value with\n// the same key returns the same result.\n//\n// Shortcut of Request().Context().Value(key any) any.\nfunc (ctx *Context) Value(key any) any {\n\tif keyStr, ok := key.(string); ok { // check if the key is a type of string, which can be retrieved by the mem store.\n\t\tif entry, exists := ctx.values.GetEntry(keyStr); exists {\n\t\t\treturn entry.ValueRaw\n\t\t}\n\t}\n\t// otherwise return the chained value.\n\treturn ctx.request.Context().Value(key)\n}\n\nconst idContextKey = \"iris.context.id\"\n\n// SetID sets an ID, any value, to the Request Context.\n// If possible the \"id\" should implement a `String() string` method\n// so it can be rendered on `Context.String` method.\n//\n// See `GetID` and `middleware/requestid` too.\nfunc (ctx *Context) SetID(id any) {\n\tctx.values.Set(idContextKey, id)\n}\n\n// GetID returns the Request Context's ID.\n// It returns nil if not given by a prior `SetID` call.\n// See `middleware/requestid` too.\nfunc (ctx *Context) GetID() any {\n\treturn ctx.values.Get(idContextKey)\n}\n\n// String returns the string representation of this request.\n//\n// It returns the Context's ID given by a `SetID`call,\n// followed by the client's IP and the method:uri.\nfunc (ctx *Context) String() string {\n\tid := ctx.GetID()\n\tif id != nil {\n\t\tif stringer, ok := id.(fmt.Stringer); ok {\n\t\t\tid = stringer.String()\n\t\t}\n\t}\n\n\treturn fmt.Sprintf(\"[%v] %s ▶ %s:%s\", id, ctx.RemoteAddr(), ctx.Method(), ctx.Request().RequestURI)\n}\n"
  },
  {
    "path": "context/context_func.go",
    "content": "package context\n\nimport (\n\t\"errors\"\n\t\"reflect\"\n\t\"sync\"\n)\n\n// ErrInvalidArgs fires when the `Context.CallFunc`\n// is called with invalid number of arguments.\nvar ErrInvalidArgs = errors.New(\"invalid arguments\")\n\n// Func represents a function registered by the Context.\n// See its `buildMeta` and `call` internal methods.\ntype Func struct {\n\tRegisterName    string // the name of which this function is registered, for information only.\n\tRaw             any    // the Raw function, can be used for custom casting.\n\tPersistenceArgs []any  // the persistence input arguments given on registration.\n\n\tonce sync.Once // guards build once, on first call.\n\t// Available after the first call.\n\tMeta *FuncMeta\n}\n\nfunc newFunc(name string, fn any, persistenceArgs ...any) *Func {\n\treturn &Func{\n\t\tRegisterName:    name,\n\t\tRaw:             fn,\n\t\tPersistenceArgs: persistenceArgs,\n\t}\n}\n\n// FuncMeta holds the necessary information about a registered\n// context function. Built once by the Func.\ntype FuncMeta struct {\n\tHandler            Handler              // when it's just a handler.\n\tHandlerWithErr     func(*Context) error // when it's just a handler which returns an error.\n\tRawFunc            func()               // when it's just a func.\n\tRawFuncWithErr     func() error         // when it's just a func which returns an error.\n\tRawFuncArgs        func(...any)\n\tRawFuncArgsWithErr func(...any) error\n\n\tValue                   reflect.Value\n\tType                    reflect.Type\n\tExpectedArgumentsLength int\n\tPersistenceInputs       []reflect.Value\n\tAcceptsContext          bool // the Context, if exists should be always first argument.\n\tReturnsError            bool // when the function's last output argument is error.\n}\n\nfunc (f *Func) buildMeta() {\n\tswitch fn := f.Raw.(type) {\n\tcase Handler:\n\t\tf.Meta = &FuncMeta{Handler: fn}\n\t\treturn\n\t// case func(*Context):\n\t// \tf.Meta = &FuncMeta{Handler: fn}\n\t// \treturn\n\tcase func(*Context) error:\n\t\tf.Meta = &FuncMeta{HandlerWithErr: fn}\n\t\treturn\n\tcase func():\n\t\tf.Meta = &FuncMeta{RawFunc: fn}\n\t\treturn\n\tcase func() error:\n\t\tf.Meta = &FuncMeta{RawFuncWithErr: fn}\n\t\treturn\n\tcase func(...any):\n\t\tf.Meta = &FuncMeta{RawFuncArgs: fn}\n\t\treturn\n\tcase func(...any) error:\n\t\tf.Meta = &FuncMeta{RawFuncArgsWithErr: fn}\n\t\treturn\n\t}\n\n\tfn := f.Raw\n\n\tmeta := FuncMeta{}\n\tif val, ok := fn.(reflect.Value); ok {\n\t\tmeta.Value = val\n\t} else {\n\t\tmeta.Value = reflect.ValueOf(fn)\n\t}\n\n\tmeta.Type = meta.Value.Type()\n\n\tif meta.Type.Kind() != reflect.Func {\n\t\treturn\n\t}\n\n\tmeta.ExpectedArgumentsLength = meta.Type.NumIn()\n\n\tskipInputs := len(meta.PersistenceInputs)\n\tif meta.ExpectedArgumentsLength > skipInputs {\n\t\tmeta.AcceptsContext = isContext(meta.Type.In(skipInputs))\n\t}\n\n\tif numOut := meta.Type.NumOut(); numOut > 0 {\n\t\t// error should be the last output.\n\t\tif isError(meta.Type.Out(numOut - 1)) {\n\t\t\tmeta.ReturnsError = true\n\t\t}\n\t}\n\n\tpersistenceArgs := f.PersistenceArgs\n\tif len(persistenceArgs) > 0 {\n\t\tinputs := make([]reflect.Value, 0, len(persistenceArgs))\n\t\tfor _, arg := range persistenceArgs {\n\t\t\tif in, ok := arg.(reflect.Value); ok {\n\t\t\t\tinputs = append(inputs, in)\n\t\t\t} else {\n\t\t\t\tinputs = append(inputs, reflect.ValueOf(in))\n\t\t\t}\n\t\t}\n\n\t\tmeta.PersistenceInputs = inputs\n\t}\n\n\tf.Meta = &meta\n}\n\nfunc (f *Func) call(ctx *Context, args ...any) ([]reflect.Value, error) {\n\tf.once.Do(f.buildMeta)\n\tmeta := f.Meta\n\n\tif meta.Handler != nil {\n\t\tmeta.Handler(ctx)\n\t\treturn nil, nil\n\t}\n\n\tif meta.HandlerWithErr != nil {\n\t\treturn nil, meta.HandlerWithErr(ctx)\n\t}\n\n\tif meta.RawFunc != nil {\n\t\tmeta.RawFunc()\n\t\treturn nil, nil\n\t}\n\n\tif meta.RawFuncWithErr != nil {\n\t\treturn nil, meta.RawFuncWithErr()\n\t}\n\n\tif meta.RawFuncArgs != nil {\n\t\tmeta.RawFuncArgs(args...)\n\t\treturn nil, nil\n\t}\n\n\tif meta.RawFuncArgsWithErr != nil {\n\t\treturn nil, meta.RawFuncArgsWithErr(args...)\n\t}\n\n\tinputs := make([]reflect.Value, 0, f.Meta.ExpectedArgumentsLength)\n\tinputs = append(inputs, f.Meta.PersistenceInputs...)\n\tif f.Meta.AcceptsContext {\n\t\tinputs = append(inputs, reflect.ValueOf(ctx))\n\t}\n\n\tfor _, arg := range args {\n\t\tif in, ok := arg.(reflect.Value); ok {\n\t\t\tinputs = append(inputs, in)\n\t\t} else {\n\t\t\tinputs = append(inputs, reflect.ValueOf(arg))\n\t\t}\n\t}\n\n\t// keep it here, the inptus may contain the context.\n\tif f.Meta.ExpectedArgumentsLength != len(inputs) {\n\t\treturn nil, ErrInvalidArgs\n\t}\n\n\toutputs := f.Meta.Value.Call(inputs)\n\treturn outputs, getError(outputs)\n}\n\nvar contextType = reflect.TypeOf((*Context)(nil))\n\n// isContext returns true if the \"typ\" is a type of Context.\nfunc isContext(typ reflect.Type) bool {\n\treturn typ == contextType\n}\n\nvar errTyp = reflect.TypeOf((*error)(nil)).Elem()\n\n// isError returns true if \"typ\" is type of `error`.\nfunc isError(typ reflect.Type) bool {\n\treturn typ.Implements(errTyp)\n}\n\nfunc getError(outputs []reflect.Value) error {\n\tif n := len(outputs); n > 0 {\n\t\tlastOut := outputs[n-1]\n\t\tif isError(lastOut.Type()) {\n\t\t\tif lastOut.IsNil() {\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\treturn lastOut.Interface().(error)\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "context/context_go118.go",
    "content": "//go:build go1.18\n// +build go1.18\n\npackage context\n\nimport \"runtime/debug\"\n\nfunc init() {\n\tif info, ok := debug.ReadBuildInfo(); ok {\n\t\tfor _, setting := range info.Settings {\n\t\t\tif BuildRevision != \"\" && BuildTime != \"\" {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tif setting.Key == \"vcs.revision\" {\n\t\t\t\tBuildRevision = setting.Value\n\t\t\t}\n\n\t\t\tif setting.Key == \"vcs.time\" {\n\t\t\t\tBuildTime = setting.Value\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "context/context_user.go",
    "content": "package context\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"strings\"\n\t\"time\"\n\t\"unicode\"\n)\n\n// ErrNotSupported is fired when a specific method is not implemented\n// or not supported entirely.\n// Can be used by User implementations when\n// an authentication system does not implement a specific, but required,\n// method of the User interface.\nvar ErrNotSupported = errors.New(\"not supported\")\n\n// User is a generic view of an authorized client.\n// See `Context.User` and `SetUser` methods for more.\n//\n// The informational methods starts with a \"Get\" prefix\n// in order to allow the implementation to contain exported\n// fields such as `Username` so they can be JSON encoded when necessary.\n//\n// The caller is free to cast this with the implementation directly\n// when special features are offered by the authorization system.\n//\n// To make optional some of the fields you can just embed the User interface\n// and implement whatever methods you want to support.\n//\n// There are three builtin implementations of the User interface:\n// - SimpleUser\n// - UserMap (a wrapper by SetUser)\n// - UserPartial (a wrapper by SetUser)\ntype User interface {\n\t// GetRaw should return the raw instance of the user, if supported.\n\tGetRaw() (any, error)\n\t// GetAuthorization should return the authorization method,\n\t// e.g. Basic Authentication.\n\tGetAuthorization() (string, error)\n\t// GetAuthorizedAt should return the exact time the\n\t// client has been authorized for the \"first\" time.\n\tGetAuthorizedAt() (time.Time, error)\n\t// GetID should return the ID of the User.\n\tGetID() (string, error)\n\t// GetUsername should return the name of the User.\n\tGetUsername() (string, error)\n\t// GetPassword should return the encoded or raw password\n\t// (depends on the implementation) of the User.\n\tGetPassword() (string, error)\n\t// GetEmail should return the e-mail of the User.\n\tGetEmail() (string, error)\n\t// GetRoles should optionally return the specific user's roles.\n\t// Returns `ErrNotSupported` if this method is not\n\t// implemented by the User implementation.\n\tGetRoles() ([]string, error)\n\t// GetToken should optionally return a token used\n\t// to authorize this User.\n\tGetToken() ([]byte, error)\n\t// GetField should optionally return a dynamic field\n\t// based on its key. Useful for custom user fields.\n\t// Keep in mind that these fields are encoded as a separate JSON key.\n\tGetField(key string) (any, error)\n} /* Notes:\nWe could use a structure of User wrapper and separate interfaces for each of the methods\nso they return ErrNotSupported if the implementation is missing it, so the `Features`\nfield and HasUserFeature can be omitted and\nadd a Raw() any to return the underline User implementation too.\nThe advandages of the above idea is that we don't have to add new methods\nfor each of the builtin features and we can keep the (assumed) struct small.\nBut we dont as it has many disadvantages, unless is requested.\n^ UPDATE: this is done through UserPartial.\n\nThe disadvantage of the current implementation is that the developer MUST\ncomplete the whole interface in order to be a valid User and if we add\nnew methods in the future their implementation will break\n(unless they have a static interface implementation check as we have on SimpleUser).\nWe kind of by-pass this disadvantage by providing a SimpleUser which can be embedded (as pointer)\nto the end-developer's custom implementations.\n*/\n\n// SimpleUser is a simple implementation of the User interface.\ntype SimpleUser struct {\n\tAuthorization string          `json:\"authorization,omitempty\" db:\"authorization\"`\n\tAuthorizedAt  time.Time       `json:\"authorized_at,omitempty\" db:\"authorized_at\"`\n\tID            string          `json:\"id,omitempty\" db:\"id\"`\n\tUsername      string          `json:\"username,omitempty\" db:\"username\"`\n\tPassword      string          `json:\"password,omitempty\" db:\"password\"`\n\tEmail         string          `json:\"email,omitempty\" db:\"email\"`\n\tRoles         []string        `json:\"roles,omitempty\" db:\"roles\"`\n\tToken         json.RawMessage `json:\"token,omitempty\" db:\"token\"`\n\tFields        Map             `json:\"fields,omitempty\" db:\"fields\"`\n}\n\nvar _ User = (*SimpleUser)(nil)\n\n// GetRaw returns itself.\nfunc (u *SimpleUser) GetRaw() (any, error) {\n\treturn u, nil\n}\n\n// GetAuthorization returns the authorization method,\n// e.g. Basic Authentication.\nfunc (u *SimpleUser) GetAuthorization() (string, error) {\n\treturn u.Authorization, nil\n}\n\n// GetAuthorizedAt returns the exact time the\n// client has been authorized for the \"first\" time.\nfunc (u *SimpleUser) GetAuthorizedAt() (time.Time, error) {\n\treturn u.AuthorizedAt, nil\n}\n\n// GetID returns the ID of the User.\nfunc (u *SimpleUser) GetID() (string, error) {\n\treturn u.ID, nil\n}\n\n// GetUsername returns the name of the User.\nfunc (u *SimpleUser) GetUsername() (string, error) {\n\treturn u.Username, nil\n}\n\n// GetPassword returns the raw password of the User.\nfunc (u *SimpleUser) GetPassword() (string, error) {\n\treturn u.Password, nil\n}\n\n// GetEmail returns the e-mail of (string,error) User.\nfunc (u *SimpleUser) GetEmail() (string, error) {\n\treturn u.Email, nil\n}\n\n// GetRoles returns the specific user's roles.\n// Returns with `ErrNotSupported` if the Roles field is not initialized.\nfunc (u *SimpleUser) GetRoles() ([]string, error) {\n\tif u.Roles == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\n\treturn u.Roles, nil\n}\n\n// GetToken returns the token associated with this User.\n// It may return empty if the User is not featured with a Token.\n//\n// The implementation can change that behavior.\n// Returns with `ErrNotSupported` if the Token field is empty.\nfunc (u *SimpleUser) GetToken() ([]byte, error) {\n\tif len(u.Token) == 0 {\n\t\treturn nil, ErrNotSupported\n\t}\n\n\treturn u.Token, nil\n}\n\n// GetField optionally returns a dynamic field from the `Fields` field\n// based on its key.\nfunc (u *SimpleUser) GetField(key string) (any, error) {\n\tif u.Fields == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\n\treturn u.Fields[key], nil\n}\n\n// UserMap can be used to convert a common map[string]any to a User.\n// Usage:\n//\n//\tuser := map[string]any{\n//\t  \"username\": \"kataras\",\n//\t  \"age\"     : 27,\n//\t}\n//\n// ctx.SetUser(user)\n// OR\n// user := UserStruct{....}\n// ctx.SetUser(user)\n// [...]\n// username, err := ctx.User().GetUsername()\n// field,err := ctx.User().GetField(\"age\")\n// age := field.(int)\n// OR cast it:\n// user := ctx.User().(UserMap)\n// username := user[\"username\"].(string)\n// age := user[\"age\"].(int)\ntype UserMap Map\n\nvar _ User = UserMap{}\n\n// GetRaw returns the underline map.\nfunc (u UserMap) GetRaw() (any, error) {\n\treturn Map(u), nil\n}\n\n// GetAuthorization returns the authorization or Authorization value of the map.\nfunc (u UserMap) GetAuthorization() (string, error) {\n\treturn u.str(\"authorization\")\n}\n\n// GetAuthorizedAt returns the authorized_at or Authorized_At value of the map.\nfunc (u UserMap) GetAuthorizedAt() (time.Time, error) {\n\treturn u.time(\"authorized_at\")\n}\n\n// GetID returns the id or Id or ID value of the map.\nfunc (u UserMap) GetID() (string, error) {\n\treturn u.str(\"id\")\n}\n\n// GetUsername returns the username or Username value of the map.\nfunc (u UserMap) GetUsername() (string, error) {\n\treturn u.str(\"username\")\n}\n\n// GetPassword returns the password or Password value of the map.\nfunc (u UserMap) GetPassword() (string, error) {\n\treturn u.str(\"password\")\n}\n\n// GetEmail returns the email or Email value of the map.\nfunc (u UserMap) GetEmail() (string, error) {\n\treturn u.str(\"email\")\n}\n\n// GetRoles returns the roles or Roles value of the map.\nfunc (u UserMap) GetRoles() ([]string, error) {\n\treturn u.strSlice(\"roles\")\n}\n\n// GetToken returns the roles or Roles value of the map.\nfunc (u UserMap) GetToken() ([]byte, error) {\n\treturn u.bytes(\"token\")\n}\n\n// GetField returns the raw map's value based on its \"key\".\n// It's not kind of useful here as you can just use the map.\nfunc (u UserMap) GetField(key string) (any, error) {\n\treturn u[key], nil\n}\n\nfunc (u UserMap) val(key string) any {\n\tisTitle := unicode.IsTitle(rune(key[0])) // if starts with uppercase.\n\tif isTitle {\n\t\tkey = strings.ToLower(key)\n\t}\n\n\treturn u[key]\n}\n\nfunc (u UserMap) bytes(key string) ([]byte, error) {\n\tif v := u.val(key); v != nil {\n\t\tswitch s := v.(type) {\n\t\tcase []byte:\n\t\t\treturn s, nil\n\t\tcase string:\n\t\t\treturn []byte(s), nil\n\t\t}\n\t}\n\n\treturn nil, ErrNotSupported\n}\n\nfunc (u UserMap) str(key string) (string, error) {\n\tif v := u.val(key); v != nil {\n\t\tif s, ok := v.(string); ok {\n\t\t\treturn s, nil\n\t\t}\n\n\t\t// exists or not we don't care, if it's invalid type we don't fill it.\n\t}\n\n\treturn \"\", ErrNotSupported\n}\n\nfunc (u UserMap) strSlice(key string) ([]string, error) {\n\tif v := u.val(key); v != nil {\n\t\tif s, ok := v.([]string); ok {\n\t\t\treturn s, nil\n\t\t}\n\t}\n\n\treturn nil, ErrNotSupported\n}\n\nfunc (u UserMap) time(key string) (time.Time, error) {\n\tif v := u.val(key); v != nil {\n\t\tif t, ok := v.(time.Time); ok {\n\t\t\treturn t, nil\n\t\t}\n\t}\n\n\treturn time.Time{}, ErrNotSupported\n}\n\ntype (\n\tuserGetAuthorization interface {\n\t\tGetAuthorization() string\n\t}\n\n\tuserGetAuthorizedAt interface {\n\t\tGetAuthorizedAt() time.Time\n\t}\n\n\tuserGetID interface {\n\t\tGetID() string\n\t}\n\n\t// UserGetUsername interface which\n\t// requires a single method to complete\n\t// a User on Context.SetUser.\n\tUserGetUsername interface {\n\t\tGetUsername() string\n\t}\n\n\t// UserGetPassword interface which\n\t// requires a single method to complete\n\t// a User on Context.SetUser.\n\tUserGetPassword interface {\n\t\tGetPassword() string\n\t}\n\n\tuserGetEmail interface {\n\t\tGetEmail() string\n\t}\n\n\tuserGetRoles interface {\n\t\tGetRoles() []string\n\t}\n\n\tuserGetToken interface {\n\t\tGetToken() []byte\n\t}\n\n\tuserGetField interface {\n\t\tGetField(string) any\n\t}\n\n\t// UserPartial is a User.\n\t// It's a helper which wraps a struct value that\n\t// may or may not complete the whole User interface.\n\t// See Context.SetUser.\n\tUserPartial struct {\n\t\tRaw                  any `json:\"raw\"`\n\t\tuserGetAuthorization `json:\",omitempty\"`\n\t\tuserGetAuthorizedAt  `json:\",omitempty\"`\n\t\tuserGetID            `json:\",omitempty\"`\n\t\tUserGetUsername      `json:\",omitempty\"`\n\t\tUserGetPassword      `json:\",omitempty\"`\n\t\tuserGetEmail         `json:\",omitempty\"`\n\t\tuserGetRoles         `json:\",omitempty\"`\n\t\tuserGetToken         `json:\",omitempty\"`\n\t\tuserGetField         `json:\",omitempty\"`\n\t}\n)\n\nvar _ User = (*UserPartial)(nil)\n\nfunc newUserPartial(i any) *UserPartial {\n\tif i == nil {\n\t\treturn nil\n\t}\n\n\tp := &UserPartial{Raw: i}\n\n\tif u, ok := i.(userGetAuthorization); ok {\n\t\tp.userGetAuthorization = u\n\t}\n\n\tif u, ok := i.(userGetAuthorizedAt); ok {\n\t\tp.userGetAuthorizedAt = u\n\t}\n\n\tif u, ok := i.(userGetID); ok {\n\t\tp.userGetID = u\n\t}\n\n\tif u, ok := i.(UserGetUsername); ok {\n\t\tp.UserGetUsername = u\n\t}\n\n\tif u, ok := i.(UserGetPassword); ok {\n\t\tp.UserGetPassword = u\n\t}\n\n\tif u, ok := i.(userGetEmail); ok {\n\t\tp.userGetEmail = u\n\t}\n\n\tif u, ok := i.(userGetRoles); ok {\n\t\tp.userGetRoles = u\n\t}\n\n\tif u, ok := i.(userGetToken); ok {\n\t\tp.userGetToken = u\n\t}\n\n\tif u, ok := i.(userGetField); ok {\n\t\tp.userGetField = u\n\t}\n\n\t// if !containsAtLeastOneMethod {\n\t// \treturn nil\n\t// }\n\n\treturn p\n}\n\n// GetRaw returns the original raw instance of the user.\nfunc (u *UserPartial) GetRaw() (any, error) {\n\tif u == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\n\treturn u.Raw, nil\n}\n\n// GetAuthorization should return the authorization method,\n// e.g. Basic Authentication.\nfunc (u *UserPartial) GetAuthorization() (string, error) {\n\tif v := u.userGetAuthorization; v != nil {\n\t\treturn v.GetAuthorization(), nil\n\t}\n\n\treturn \"\", ErrNotSupported\n}\n\n// GetAuthorizedAt should return the exact time the\n// client has been authorized for the \"first\" time.\nfunc (u *UserPartial) GetAuthorizedAt() (time.Time, error) {\n\tif v := u.userGetAuthorizedAt; v != nil {\n\t\treturn v.GetAuthorizedAt(), nil\n\t}\n\n\treturn time.Time{}, ErrNotSupported\n}\n\n// GetID should return the ID of the User.\nfunc (u *UserPartial) GetID() (string, error) {\n\tif v := u.userGetID; v != nil {\n\t\treturn v.GetID(), nil\n\t}\n\n\treturn \"\", ErrNotSupported\n}\n\n// GetUsername should return the name of the User.\nfunc (u *UserPartial) GetUsername() (string, error) {\n\tif v := u.UserGetUsername; v != nil {\n\t\treturn v.GetUsername(), nil\n\t}\n\n\treturn \"\", ErrNotSupported\n}\n\n// GetPassword should return the encoded or raw password\n// (depends on the implementation) of the User.\nfunc (u *UserPartial) GetPassword() (string, error) {\n\tif v := u.UserGetPassword; v != nil {\n\t\treturn v.GetPassword(), nil\n\t}\n\n\treturn \"\", ErrNotSupported\n}\n\n// GetEmail should return the e-mail of the User.\nfunc (u *UserPartial) GetEmail() (string, error) {\n\tif v := u.userGetEmail; v != nil {\n\t\treturn v.GetEmail(), nil\n\t}\n\n\treturn \"\", ErrNotSupported\n}\n\n// GetRoles should optionally return the specific user's roles.\n// Returns `ErrNotSupported` if this method is not\n// implemented by the User implementation.\nfunc (u *UserPartial) GetRoles() ([]string, error) {\n\tif v := u.userGetRoles; v != nil {\n\t\treturn v.GetRoles(), nil\n\t}\n\n\treturn nil, ErrNotSupported\n}\n\n// GetToken should optionally return a token used\n// to authorize this User.\nfunc (u *UserPartial) GetToken() ([]byte, error) {\n\tif v := u.userGetToken; v != nil {\n\t\treturn v.GetToken(), nil\n\t}\n\n\treturn nil, ErrNotSupported\n}\n\n// GetField should optionally return a dynamic field\n// based on its key. Useful for custom user fields.\n// Keep in mind that these fields are encoded as a separate JSON key.\nfunc (u *UserPartial) GetField(key string) (any, error) {\n\tif v := u.userGetField; v != nil {\n\t\treturn v.GetField(key), nil\n\t}\n\n\treturn nil, ErrNotSupported\n}\n"
  },
  {
    "path": "context/counter.go",
    "content": "package context\n\nimport (\n\t\"math\"\n\t\"sync/atomic\"\n)\n\n// Counter is the shared counter instances between Iris applications of the same process.\nvar Counter = NewGlobalCounter() // it's not used anywhere, currently but it's here.\n\n// NewGlobalCounter returns a fresh instance of a global counter.\n// End developers can use it as a helper for their applications.\nfunc NewGlobalCounter() *GlobalCounter {\n\treturn &GlobalCounter{Max: math.MaxUint64}\n}\n\n// GlobalCounter is a counter which\n// atomically increments until Max.\ntype GlobalCounter struct {\n\tvalue uint64\n\tMax   uint64\n}\n\n// Increment increments the Value.\n// The value cannot exceed the Max one.\n// It uses Compare and Swap with the atomic package.\n//\n// Returns the new number value.\nfunc (c *GlobalCounter) Increment() (newValue uint64) {\n\tfor {\n\t\tprev := atomic.LoadUint64(&c.value)\n\t\tnewValue = prev + 1\n\n\t\tif newValue >= c.Max {\n\t\t\tnewValue = 0\n\t\t}\n\n\t\tif atomic.CompareAndSwapUint64(&c.value, prev, newValue) {\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn\n}\n\n// Get returns the current counter without incrementing.\nfunc (c *GlobalCounter) Get() uint64 {\n\treturn atomic.LoadUint64(&c.value)\n}\n"
  },
  {
    "path": "context/fs.go",
    "content": "package context\n\nimport (\n\t\"embed\"\n\t\"fmt\"\n\t\"io/fs\"\n\t\"net/http\"\n\t\"os\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"strings\"\n)\n\n// ResolveFS accepts a single input argument of any type\n// and tries to cast it to fs.FS.\n//\n// It affects the view engine's filesystem resolver.\n//\n// This package-level variable can be modified on initialization.\nvar ResolveFS = func(fsOrDir any) fs.FS {\n\tif fsOrDir == nil {\n\t\treturn noOpFS{}\n\t}\n\n\tswitch v := fsOrDir.(type) {\n\tcase string:\n\t\tif v == \"\" {\n\t\t\treturn noOpFS{}\n\t\t}\n\t\treturn os.DirFS(v)\n\tcase fs.FS:\n\t\treturn v\n\tcase http.FileSystem: // handles go-bindata.\n\t\treturn &httpFS{v}\n\tdefault:\n\t\tpanic(fmt.Errorf(`unexpected \"fsOrDir\" argument type of %T (string or fs.FS or embed.FS or http.FileSystem)`, v))\n\t}\n}\n\ntype noOpFS struct{}\n\nfunc (fileSystem noOpFS) Open(name string) (fs.File, error) { return nil, nil }\n\n// IsNoOpFS reports whether the given \"fileSystem\" is a no operation fs.\nfunc IsNoOpFS(fileSystem fs.FS) bool {\n\t_, ok := fileSystem.(noOpFS)\n\treturn ok\n}\n\ntype httpFS struct {\n\tfs http.FileSystem\n}\n\nfunc (f *httpFS) Open(name string) (fs.File, error) {\n\tif name == \".\" {\n\t\tname = \"/\"\n\t}\n\n\treturn f.fs.Open(filepath.ToSlash(name))\n}\n\nfunc (f *httpFS) ReadDir(name string) ([]fs.DirEntry, error) {\n\tname = filepath.ToSlash(name)\n\tif name == \".\" {\n\t\tname = \"/\"\n\t}\n\n\tfile, err := f.fs.Open(name)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer file.Close()\n\n\tinfos, err := file.Readdir(-1)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tentries := make([]fs.DirEntry, 0, len(infos))\n\tfor _, info := range infos {\n\t\tif info.IsDir() { // http file's does not return the whole tree, so read it.\n\t\t\tsub, err := f.ReadDir(info.Name())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\t\tentries = append(entries, sub...)\n\t\t\tcontinue\n\t\t}\n\n\t\tentry := fs.FileInfoToDirEntry(info)\n\t\tentries = append(entries, entry)\n\t}\n\n\treturn entries, nil\n}\n\n// ResolveHTTPFS accepts a single input argument of any type\n// and tries to cast it to http.FileSystem.\n//\n// It affects the Application's API Builder's `HandleDir` method.\n//\n// This package-level variable can be modified on initialization.\nvar ResolveHTTPFS = func(fsOrDir any) http.FileSystem {\n\tvar fileSystem http.FileSystem\n\tswitch v := fsOrDir.(type) {\n\tcase string:\n\t\tfileSystem = http.Dir(v)\n\tcase http.FileSystem:\n\t\tfileSystem = v\n\tcase embed.FS:\n\t\tdireEtries, err := v.ReadDir(\".\")\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\n\t\tif len(direEtries) == 0 {\n\t\t\tpanic(\"HandleDir: no directories found under the embedded file system\")\n\t\t}\n\n\t\tsubfs, err := fs.Sub(v, direEtries[0].Name())\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tfileSystem = http.FS(subfs)\n\tcase fs.FS:\n\t\tfileSystem = http.FS(v)\n\tdefault:\n\t\tpanic(fmt.Sprintf(`unexpected \"fsOrDir\" argument type of %T (string or http.FileSystem or embed.FS or fs.FS)`, v))\n\t}\n\n\treturn fileSystem\n}\n\n// FindNames accepts a \"http.FileSystem\" and a root name and returns\n// the list containing its file names.\nfunc FindNames(fileSystem http.FileSystem, name string) ([]string, error) {\n\tif strings.Contains(name, \"..\") {\n\t\treturn nil, fmt.Errorf(\"invalid root name\")\n\t}\n\n\tf, err := fileSystem.Open(name) // it's the root dir.\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer f.Close()\n\n\tfi, err := f.Stat()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !fi.IsDir() {\n\t\treturn []string{name}, nil\n\t}\n\n\tfileinfos, err := f.Readdir(-1)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tfiles := make([]string, 0)\n\n\tfor _, info := range fileinfos {\n\t\t// Note:\n\t\t// go-bindata has absolute names with os.Separator,\n\t\t// http.Dir the basename.\n\t\tbaseFilename := toBaseName(info.Name())\n\t\tfullname := path.Join(name, baseFilename)\n\t\tif fullname == name { // prevent looping through itself.\n\t\t\tcontinue\n\t\t}\n\t\trfiles, err := FindNames(fileSystem, fullname)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tfiles = append(files, rfiles...)\n\t}\n\n\treturn files, nil\n}\n\n// Instead of path.Base(filepath.ToSlash(s))\n// let's do something like that, it is faster\n// (used to list directories on serve-time too):\nfunc toBaseName(s string) string {\n\tn := len(s) - 1\n\tfor i := n; i >= 0; i-- {\n\t\tif c := s[i]; c == '/' || c == '\\\\' {\n\t\t\tif i == n {\n\t\t\t\t// \"s\" ends with a slash, remove it and retry.\n\t\t\t\treturn toBaseName(s[:n])\n\t\t\t}\n\n\t\t\treturn s[i+1:] // return the rest, trimming the slash.\n\t\t}\n\t}\n\n\treturn s\n}\n"
  },
  {
    "path": "context/handler.go",
    "content": "package context\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"regexp\"\n\t\"runtime\"\n\t\"strings\"\n\t\"sync\"\n)\n\nvar (\n\t// PackageName is the Iris Go module package name.\n\tPackageName = strings.TrimSuffix(reflect.TypeOf(Context{}).PkgPath(), \"/context\")\n\n\t// WorkingDir is the (initial) current directory.\n\tWorkingDir, _ = os.Getwd()\n)\n\nvar (\n\thandlerNames   = make(map[*NameExpr]string)\n\thandlerNamesMu sync.RWMutex\n\n\tignoreMainHandlerNames = [...]string{\n\t\t\"iris.cache\",\n\t\t\"iris.basicauth\",\n\t\t\"iris.hCaptcha\",\n\t\t\"iris.reCAPTCHA\",\n\t\t\"iris.profiling\",\n\t\t\"iris.recover\",\n\t\t\"iris.accesslog\",\n\t\t\"iris.grpc\",\n\t\t\"iris.requestid\",\n\t\t\"iris.rewrite\",\n\t\t\"iris.cors\",\n\t\t\"iris.jwt\",\n\t\t\"iris.logger\",\n\t\t\"iris.rate\",\n\t\t\"iris.methodoverride\",\n\t\t\"iris.errors.recover\",\n\t}\n)\n\n// SetHandlerName sets a handler name that could be\n// fetched through `HandlerName`. The \"original\" should be\n// the Go's original regexp-featured (can be retrieved through a `HandlerName` call) function name.\n// The \"replacement\" should be the custom, human-text of that function name.\n//\n// If the name starts with \"iris\" then it replaces that string with the\n// full Iris module package name,\n// e.g. iris/middleware/logger.(*requestLoggerMiddleware).ServeHTTP-fm to\n// github.com/kataras/iris/v12/middleware/logger.(*requestLoggerMiddleware).ServeHTTP-fm\n// for convenient between Iris versions.\nfunc SetHandlerName(original string, replacement string) {\n\tif strings.HasPrefix(original, \"iris\") {\n\t\toriginal = PackageName + strings.TrimPrefix(original, \"iris\")\n\t}\n\n\thandlerNamesMu.Lock()\n\t// If regexp syntax is wrong\n\t// then its `MatchString` will compare through literal. Fixes an issue\n\t// when a handler name is declared as it's and cause regex parsing expression error,\n\t// e.g. `iris/cache/client.(*Handler).ServeHTTP-fm`\n\tregex, _ := regexp.Compile(original)\n\thandlerNames[&NameExpr{\n\t\tliteral:           original,\n\t\tnormalizedLiteral: normalizeExpression(original),\n\t\tregex:             regex,\n\t}] = replacement\n\n\thandlerNamesMu.Unlock()\n}\n\n// NameExpr regex or literal comparison through `MatchString`.\ntype NameExpr struct {\n\tregex             *regexp.Regexp\n\tliteral           string\n\tnormalizedLiteral string\n}\n\n// MatchString reports whether \"s\" is literal of \"literal\"\n// or it matches the regex expression at \"regex\".\nfunc (expr *NameExpr) MatchString(s string) bool {\n\tif expr.literal == s { // if matches as string, as it's.\n\t\treturn true\n\t}\n\n\tif expr.regex != nil {\n\t\treturn expr.regex.MatchString(s)\n\t}\n\n\treturn false\n}\n\n// MatchFilename reports whether \"filename\" contains the \"literal\".\nfunc (expr *NameExpr) MatchFilename(filename string) bool {\n\tif filename == \"\" {\n\t\treturn false\n\t}\n\n\treturn strings.Contains(filename, expr.normalizedLiteral)\n}\n\n// The regular expression to match the versioning and the domain part\nvar trimFileModuleNameRegex = regexp.MustCompile(`^[\\w.]+/(kataras|iris-contrib)/|/v\\d+|\\.\\*`)\n\nfunc normalizeExpression(str string) string {\n\t// Replace all occurrences of the regular expression with the replacement string.\n\treturn strings.ToLower(trimFileModuleNameRegex.ReplaceAllString(str, \"\"))\n}\n\n// A Handler responds to an HTTP request.\n// It writes reply headers and data to the Context.ResponseWriter() and then return.\n// Returning signals that the request is finished;\n// it is not valid to use the Context after or concurrently with the completion of the Handler call.\n//\n// Depending on the HTTP client software, HTTP protocol version,\n// and any intermediaries between the client and the iris server,\n// it may not be possible to read from the Context.Request().Body after writing to the Context.ResponseWriter().\n// Cautious handlers should read the Context.Request().Body first, and then reply.\n//\n// Except for reading the body, handlers should not modify the provided Context.\n//\n// If Handler panics, the server (the caller of Handler) assumes that the effect of the panic was isolated to the active request.\n// It recovers the panic, logs a stack trace to the server error log, and hangs up the connection.\ntype Handler = func(*Context)\n\n// Handlers is just a type of slice of []Handler.\n//\n// See `Handler` for more.\ntype Handlers = []Handler\n\nfunc valueOf(v any) reflect.Value {\n\tif val, ok := v.(reflect.Value); ok {\n\t\treturn val\n\t}\n\n\treturn reflect.ValueOf(v)\n}\n\n// HandlerName returns the handler's function name.\n// See `Context.HandlerName` method to get function name of the current running handler in the chain.\n// See `SetHandlerName` too.\nfunc HandlerName(h any) string {\n\tpc := valueOf(h).Pointer()\n\tfn := runtime.FuncForPC(pc)\n\tname := fn.Name()\n\tfilename, _ := fn.FileLine(fn.Entry())\n\tfilenameLower := strings.ToLower(filename)\n\n\thandlerNamesMu.RLock()\n\tfor expr, newName := range handlerNames {\n\t\tif expr.MatchString(name) || expr.MatchFilename(filenameLower) {\n\t\t\tname = newName\n\t\t\tbreak\n\t\t}\n\t}\n\thandlerNamesMu.RUnlock()\n\n\treturn trimHandlerName(name)\n}\n\n// HandlersNames returns a slice of \"handlers\" names\n// separated by commas. Can be used for debugging\n// or to determinate if end-developer\n// called the same exactly Use/UseRouter/Done... API methods\n// so framework can give a warning.\nfunc HandlersNames(handlers ...any) string {\n\tif len(handlers) == 1 {\n\t\tif hs, ok := handlers[0].(Handlers); ok {\n\t\t\tasInterfaces := make([]any, 0, len(hs))\n\t\t\tfor _, h := range hs {\n\t\t\t\tasInterfaces = append(asInterfaces, h)\n\t\t\t}\n\n\t\t\treturn HandlersNames(asInterfaces...)\n\t\t}\n\t}\n\n\tnames := make([]string, 0, len(handlers))\n\tfor _, h := range handlers {\n\t\tnames = append(names, HandlerName(h))\n\t}\n\n\treturn strings.Join(names, \",\")\n}\n\n// HandlerFileLine returns the handler's file and line information.\n// See `context.HandlerFileLine` to get the file, line of the current running handler in the chain.\nfunc HandlerFileLine(h any) (file string, line int) {\n\tpc := valueOf(h).Pointer()\n\treturn runtime.FuncForPC(pc).FileLine(pc)\n}\n\n// HandlerFileLineRel same as `HandlerFileLine` but it returns the path\n// corresponding to its relative based on the package-level \"WorkingDir\" variable.\nfunc HandlerFileLineRel(h any) (file string, line int) {\n\tfile, line = HandlerFileLine(h)\n\tif relFile, err := filepath.Rel(WorkingDir, file); err == nil {\n\t\tif !strings.HasPrefix(relFile, \"..\") {\n\t\t\t// Only if it's relative to this path, not parent.\n\t\t\tfile = \"./\" + relFile\n\t\t}\n\t}\n\n\treturn\n}\n\n// MainHandlerName tries to find the main handler that end-developer\n// registered on the provided chain of handlers and returns its function name.\nfunc MainHandlerName(handlers ...any) (name string, index int) {\n\tif len(handlers) == 0 {\n\t\treturn\n\t}\n\n\tif hs, ok := handlers[0].(Handlers); ok {\n\t\ttmp := make([]any, 0, len(hs))\n\t\tfor _, h := range hs {\n\t\t\ttmp = append(tmp, h)\n\t\t}\n\n\t\treturn MainHandlerName(tmp...)\n\t}\n\n\tfor i := 0; i < len(handlers); i++ {\n\t\tname = HandlerName(handlers[i])\n\t\tif name == \"\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tindex = i\n\t\tif !ingoreMainHandlerName(name) {\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn\n}\n\nfunc trimHandlerName(name string) string {\n\t// trim the path for Iris' internal middlewares, e.g.\n\t// irs/mvc.GRPC.Apply.func1\n\tif internalName := PackageName; strings.HasPrefix(name, internalName) {\n\t\tname = strings.Replace(name, internalName, \"iris\", 1)\n\t}\n\n\tif internalName := \"github.com/iris-contrib\"; strings.HasPrefix(name, internalName) {\n\t\tname = strings.Replace(name, internalName, \"iris-contrib\", 1)\n\t}\n\n\tname = strings.TrimSuffix(name, \"GRPC.Apply.func1\")\n\treturn name\n}\n\nvar ignoreHandlerNames = [...]string{\n\t\"iris/macro/handler.MakeHandler\",\n\t\"iris/hero.makeHandler.func2\",\n\t\"iris/core/router.ExecutionOptions.buildHandler\",\n\t\"iris/core/router.(*APIBuilder).Favicon\",\n\t\"iris/core/router.StripPrefix\",\n\t\"iris/core/router.PrefixDir\",\n\t\"iris/core/router.PrefixFS\",\n\t\"iris/context.glob..func2.1\",\n}\n\n// IgnoreHandlerName compares a static slice of Iris builtin\n// internal methods that should be ignored from trace.\n// Some internal methods are kept out of this list for actual debugging.\nfunc IgnoreHandlerName(name string) bool {\n\tfor _, ignore := range ignoreHandlerNames {\n\t\tif name == ignore {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\n// ingoreMainHandlerName reports whether a main handler of \"name\" should\n// be ignored and continue to match the next.\n// The ignored main handler names are literals and respects the `ignoreNameHandlers` too.\nfunc ingoreMainHandlerName(name string) bool {\n\tif IgnoreHandlerName(name) {\n\t\t// If ignored at all, it can't be the main.\n\t\treturn true\n\t}\n\n\tfor _, ignore := range ignoreMainHandlerNames {\n\t\tif name == ignore {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\n// Filter is just a type of func(Context) bool which reports whether an action must be performed\n// based on the incoming request.\n//\n// See `NewConditionalHandler` for more.\ntype Filter func(*Context) bool\n\n// NewConditionalHandler returns a single Handler which can be registered\n// as a middleware.\n// Filter is just a type of Handler which returns a boolean.\n// Handlers here should act like middleware, they should contain `ctx.Next` to proceed\n// to the next handler of the chain. Those \"handlers\" are registered to the per-request context.\n//\n// It checks the \"filter\" and if passed then\n// it, correctly, executes the \"handlers\".\n//\n// If passed, this function makes sure that the Context's information\n// about its per-request handler chain based on the new \"handlers\" is always updated.\n//\n// If not passed, then simply the Next handler(if any) is executed and \"handlers\" are ignored.\n//\n// Example can be found at: _examples/routing/conditional-chain.\nfunc NewConditionalHandler(filter Filter, handlers ...Handler) Handler {\n\treturn func(ctx *Context) {\n\t\tif filter(ctx) {\n\t\t\t// Note that we don't want just to fire the incoming handlers, we must make sure\n\t\t\t// that it won't break any further handler chain\n\t\t\t// information that may be required for the next handlers.\n\t\t\t//\n\t\t\t// The below code makes sure that this conditional handler does not break\n\t\t\t// the ability that iris provides to its end-devs\n\t\t\t// to check and modify the per-request handlers chain at runtime.\n\t\t\tcurrIdx := ctx.HandlerIndex(-1)\n\t\t\tcurrHandlers := ctx.Handlers()\n\n\t\t\tif currIdx == len(currHandlers)-1 {\n\t\t\t\t// if this is the last handler of the chain\n\t\t\t\t// just add to the last the new handlers and call Next to fire those.\n\t\t\t\tctx.AddHandler(handlers...)\n\t\t\t\tctx.Next()\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// otherwise insert the new handlers in the middle of the current executed chain and the next chain.\n\t\t\tnewHandlers := append(currHandlers[:currIdx+1], append(handlers, currHandlers[currIdx+1:]...)...)\n\t\t\tctx.SetHandlers(newHandlers)\n\t\t\tctx.Next()\n\t\t\treturn\n\t\t}\n\t\t// if not pass, then just execute the next.\n\t\tctx.Next()\n\t}\n}\n\n// JoinHandlers returns a copy of \"h1\" and \"h2\" Handlers slice joined as one slice of Handlers.\nfunc JoinHandlers(h1 Handlers, h2 Handlers) Handlers {\n\tif len(h1) == 0 {\n\t\treturn h2\n\t}\n\n\tif len(h2) == 0 {\n\t\treturn h1\n\t}\n\n\tnowLen := len(h1)\n\ttotalLen := nowLen + len(h2)\n\t// create a new slice of Handlers in order to merge the \"h1\" and \"h2\"\n\tnewHandlers := make(Handlers, totalLen)\n\t// copy the already Handlers to the just created\n\tcopy(newHandlers, h1)\n\t// start from there we finish, and store the new Handlers too\n\tcopy(newHandlers[nowLen:], h2)\n\treturn newHandlers\n}\n\n// UpsertHandlers like `JoinHandlers` but it does\n// NOT copies the handlers entries and it does remove duplicates.\nfunc UpsertHandlers(h1 Handlers, h2 Handlers) Handlers {\nreg:\n\tfor _, handler := range h2 {\n\t\tname := HandlerName(handler)\n\t\tfor i, registeredHandler := range h1 {\n\t\t\tregisteredName := HandlerName(registeredHandler)\n\t\t\tif name == registeredName {\n\t\t\t\th1[i] = handler // replace this handler with the new one.\n\t\t\t\tcontinue reg    // break and continue to the next handler.\n\t\t\t}\n\t\t}\n\n\t\th1 = append(h1, handler) // or just insert it.\n\t}\n\n\treturn h1\n}\n\n// CopyHandlers returns a copy of \"handlers\" Handlers slice.\nfunc CopyHandlers(handlers Handlers) Handlers {\n\thandlersCp := make(Handlers, 0, len(handlers))\n\tfor _, handler := range handlers {\n\t\tif handler == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\thandlersCp = append(handlersCp, handler)\n\t}\n\n\treturn handlersCp\n}\n\n// HandlerExists reports whether a handler exists in the \"handlers\" slice.\nfunc HandlerExists(handlers Handlers, handlerNameOrFunc any) bool {\n\tif handlerNameOrFunc == nil {\n\t\treturn false\n\t}\n\n\tvar matchHandler func(any) bool\n\n\tswitch v := handlerNameOrFunc.(type) {\n\tcase string:\n\t\tmatchHandler = func(handler any) bool {\n\t\t\treturn HandlerName(handler) == v\n\t\t}\n\tcase Handler:\n\t\thandlerName := HandlerName(v)\n\t\tmatchHandler = func(handler any) bool {\n\t\t\treturn HandlerName(handler) == handlerName\n\t\t}\n\tdefault:\n\t\tmatchHandler = func(handler any) bool {\n\t\t\treturn reflect.TypeOf(handler) == reflect.TypeOf(v)\n\t\t}\n\t}\n\n\tfor _, handler := range handlers {\n\t\tif matchHandler(handler) {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n"
  },
  {
    "path": "context/i18n.go",
    "content": "package context\n\nimport \"golang.org/x/text/language\"\n\n// I18nReadOnly is the interface which contains the read-only i18n features.\n// Read the \"i18n\" package fo details.\ntype I18nReadOnly interface {\n\tTags() []language.Tag\n\tGetLocale(ctx *Context) Locale\n\tTr(lang string, key string, args ...any) string\n\tTrContext(ctx *Context, key string, args ...any) string\n}\n\n// Locale is the interface which returns from a `Localizer.GetLocale` method.\n// It serves the translations based on \"key\" or format. See `GetMessage`.\ntype Locale interface {\n\t// Index returns the current locale index from the languages list.\n\tIndex() int\n\t// Tag returns the full language Tag attached to this Locale,\n\t// it should be unique across different Locales.\n\tTag() *language.Tag\n\t// Language should return the exact languagecode of this `Locale`\n\t//that the user provided on `New` function.\n\t//\n\t// Same as `Tag().String()` but it's static.\n\tLanguage() string\n\t// GetMessage should return translated text based on the given \"key\".\n\tGetMessage(key string, args ...any) string\n}\n"
  },
  {
    "path": "context/pool.go",
    "content": "package context\n\nimport (\n\t\"net/http\"\n\t\"sync\"\n)\n\n// Pool is the context pool, it's used inside router and the framework by itself.\ntype Pool struct {\n\tpool *sync.Pool\n}\n\n// New creates and returns a new context pool.\nfunc New(newFunc func() any) *Pool {\n\treturn &Pool{pool: &sync.Pool{New: newFunc}}\n}\n\n// Acquire returns a Context from pool.\n// See Release.\nfunc (c *Pool) Acquire(w http.ResponseWriter, r *http.Request) *Context {\n\tctx := c.pool.Get().(*Context)\n\tctx.BeginRequest(w, r)\n\treturn ctx\n}\n\n// Release puts a Context back to its pull, this function releases its resources.\n// See Acquire.\nfunc (c *Pool) Release(ctx *Context) {\n\tif !ctx.manualRelease {\n\t\tctx.EndRequest()\n\t\tc.pool.Put(ctx)\n\t}\n}\n\n// ReleaseLight will just release the object back to the pool, but the\n// clean method is caller's responsibility now, currently this is only used\n// on `SPABuilder` and `websocket.Handler`.\n//\n// ReleaseLight does a force-put, it does NOT respect the context.DisablePoolRelease.\nfunc (c *Pool) ReleaseLight(ctx *Context) {\n\tc.pool.Put(ctx)\n}\n"
  },
  {
    "path": "context/problem.go",
    "content": "package context\n\nimport (\n\t\"encoding/xml\"\n\t\"fmt\"\n\t\"math\"\n\t\"net/http\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n)\n\n// Problem Details for HTTP APIs.\n// Pass a Problem value to `context.Problem` to\n// write an \"application/problem+json\" response.\n//\n// Read more at: https://github.com/kataras/iris/blob/main/_examples/routing/http-errors.\ntype Problem map[string]any\n\n// NewProblem retruns a new Problem.\n// Head over to the `Problem` type godoc for more.\nfunc NewProblem() Problem {\n\tp := make(Problem)\n\treturn p\n}\n\nfunc (p Problem) keyExists(key string) bool {\n\tif p == nil {\n\t\treturn false\n\t}\n\n\t_, found := p[key]\n\treturn found\n}\n\n// DefaultProblemStatusCode is being sent to the client\n// when Problem's status is not a valid one.\nvar DefaultProblemStatusCode = http.StatusBadRequest\n\nfunc (p Problem) getStatus() (int, bool) {\n\tstatusField, found := p[\"status\"]\n\tif !found {\n\t\treturn DefaultProblemStatusCode, false\n\t}\n\n\tstatus, ok := statusField.(int)\n\tif !ok {\n\t\treturn DefaultProblemStatusCode, false\n\t}\n\n\tif !StatusCodeNotSuccessful(status) {\n\t\treturn DefaultProblemStatusCode, false\n\t}\n\n\treturn status, true\n}\n\nfunc isEmptyTypeURI(uri string) bool {\n\treturn uri == \"\" || uri == \"about:blank\"\n}\n\nfunc (p Problem) getURI(key string) string {\n\tf, found := p[key]\n\tif found {\n\t\tif typ, ok := f.(string); ok {\n\t\t\tif !isEmptyTypeURI(typ) {\n\t\t\t\treturn typ\n\t\t\t}\n\t\t}\n\t}\n\n\treturn \"\"\n}\n\n// Updates \"type\" field to absolute URI, recursively.\nfunc (p Problem) updateURIsToAbs(ctx *Context) {\n\tif p == nil {\n\t\treturn\n\t}\n\n\tif uriRef := p.getURI(\"type\"); uriRef != \"\" && !strings.HasPrefix(uriRef, \"http\") {\n\t\tp.Type(ctx.AbsoluteURI(uriRef))\n\t}\n\n\tif uriRef := p.getURI(\"instance\"); uriRef != \"\" {\n\t\tp.Instance(ctx.AbsoluteURI(uriRef))\n\t}\n\n\tif cause, ok := p[\"cause\"]; ok {\n\t\tif causeP, ok := cause.(Problem); ok {\n\t\t\tcauseP.updateURIsToAbs(ctx)\n\t\t}\n\t}\n}\n\nconst (\n\tproblemTempKeyPrefix = \"@temp_\"\n)\n\n// TempKey sets a temporary key-value pair, which is being removed\n// on the its first get.\nfunc (p Problem) TempKey(key string, value any) Problem {\n\treturn p.Key(problemTempKeyPrefix+key, value)\n}\n\n// GetTempKey returns the temp value based on \"key\" and removes it.\nfunc (p Problem) GetTempKey(key string) any {\n\tkey = problemTempKeyPrefix + key\n\tv, ok := p[key]\n\tif ok {\n\t\tdelete(p, key)\n\t\treturn v\n\t}\n\n\treturn nil\n}\n\n// Key sets a custom key-value pair.\nfunc (p Problem) Key(key string, value any) Problem {\n\tp[key] = value\n\treturn p\n}\n\n// Type URI SHOULD resolve to HTML [W3C.REC-html5-20141028]\n// documentation that explains how to resolve the problem.\n// Example: \"https://example.net/validation-error\"\n//\n// Empty URI or \"about:blank\", when used as a problem type,\n// indicates that the problem has no additional semantics beyond that of the HTTP status code.\n// When \"about:blank\" is used and \"title\" was not set-ed,\n// the title is being automatically set the same as the recommended HTTP status phrase for that code\n// (e.g., \"Not Found\" for 404, and so on) on `Status` call.\n//\n// Relative paths are also valid when writing this Problem to an Iris Context.\nfunc (p Problem) Type(uri string) Problem {\n\treturn p.Key(\"type\", uri)\n}\n\n// Title sets the problem's title field.\n// Example: \"Your request parameters didn't validate.\"\n// It is set to status Code text if missing,\n// (e.g., \"Not Found\" for 404, and so on).\nfunc (p Problem) Title(title string) Problem {\n\treturn p.Key(\"title\", title)\n}\n\n// Status sets HTTP error code for problem's status field.\n// Example: 404\n//\n// It is required.\nfunc (p Problem) Status(statusCode int) Problem {\n\tshouldOverrideTitle := !p.keyExists(\"title\")\n\n\t// if !shouldOverrideTitle {\n\t// \ttyp, found := p[\"type\"]\n\t// \tshouldOverrideTitle = !found || isEmptyTypeURI(typ.(string))\n\t// }\n\n\tif shouldOverrideTitle {\n\t\t// Set title by code.\n\t\tp.Title(http.StatusText(statusCode))\n\t}\n\n\treturn p.Key(\"status\", statusCode)\n}\n\n// Detail sets the problem's detail field.\n// Example: \"Optional details about the error...\".\nfunc (p Problem) Detail(detail string) Problem {\n\treturn p.Key(\"detail\", detail)\n}\n\n// DetailErr calls `Detail(err.Error())`.\nfunc (p Problem) DetailErr(err error) Problem {\n\tif err == nil {\n\t\treturn p\n\t}\n\n\treturn p.Key(\"detail\", err.Error())\n}\n\n// Instance sets the problem's instance field.\n// A URI reference that identifies the specific\n// occurrence of the problem.  It may or may not yield further\n// information if dereferenced.\nfunc (p Problem) Instance(instanceURI string) Problem {\n\treturn p.Key(\"instance\", instanceURI)\n}\n\n// Cause sets the problem's cause field.\n// Any chain of problems.\nfunc (p Problem) Cause(cause Problem) Problem {\n\tif !cause.Validate() {\n\t\treturn p\n\t}\n\n\treturn p.Key(\"cause\", cause)\n}\n\n// Validate reports whether this Problem value is a valid problem one.\nfunc (p Problem) Validate() bool {\n\t// A nil problem is not a valid one.\n\tif p == nil {\n\t\treturn false\n\t}\n\n\treturn p.keyExists(\"type\") &&\n\t\tp.keyExists(\"title\") &&\n\t\tp.keyExists(\"status\")\n}\n\n// Error method completes the go error.\n// Returns the \"[Status] Title\" string form of this Problem.\n// If Problem is not a valid one, it returns \"invalid problem\".\nfunc (p Problem) Error() string {\n\tif !p.Validate() {\n\t\treturn \"invalid problem\"\n\t}\n\n\treturn fmt.Sprintf(\"[%d] %s\", p[\"status\"], p[\"title\"])\n}\n\n// MarshalXML makes this Problem XML-compatible content to render.\nfunc (p Problem) MarshalXML(e *xml.Encoder, start xml.StartElement) error {\n\tif len(p) == 0 {\n\t\treturn nil\n\t}\n\n\terr := e.EncodeToken(start)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// toTitle := cases.Title(language.English)\n\t// toTitle.String(k)\n\n\tfor k, v := range p {\n\t\t// convert keys like \"type\" to \"Type\", \"productName\" to \"ProductName\" and e.t.c. when xml.\n\t\terr = e.Encode(xmlMapEntry{XMLName: xml.Name{Local: strings.Title(k)}, Value: v})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn e.EncodeToken(start.End())\n}\n\n// DefaultProblemOptions the default options for `Context.Problem` method.\nvar DefaultProblemOptions = ProblemOptions{\n\tJSON: JSON{Indent: \"  \"},\n\tXML:  XML{Indent: \"  \"},\n}\n\n// ProblemOptions the optional settings when server replies with a Problem.\n// See `Context.Problem` method and `Problem` type for more details.\ntype ProblemOptions struct {\n\t// JSON are the optional JSON renderer options.\n\tJSON JSON\n\n\t// RenderXML set to true if want to render as XML doc.\n\t// See `XML` option field too.\n\tRenderXML bool\n\t// XML are the optional XML renderer options.\n\t// Affect only when `RenderXML` field is set to true.\n\tXML XML\n\n\t// RetryAfter sets the Retry-After response header.\n\t// https://tools.ietf.org/html/rfc7231#section-7.1.3\n\t// The value can be one of those:\n\t// time.Time\n\t// time.Duration for seconds\n\t// int64, int, float64 for seconds\n\t// string for duration string or for datetime string.\n\t//\n\t// Examples:\n\t// time.Now().Add(5 * time.Minute),\n\t// 300 * time.Second,\n\t// \"5m\",\n\t// 300\n\tRetryAfter any\n\t// A function that, if specified, can dynamically set\n\t// retry-after based on the request. Useful for ProblemOptions reusability.\n\t// Should return time.Time, time.Duration, int64, int, float64 or string.\n\t//\n\t// Overrides the RetryAfter field.\n\tRetryAfterFunc func(*Context) any\n}\n\nfunc parseDurationToSeconds(dur time.Duration) int64 {\n\treturn int64(math.Round(dur.Seconds()))\n}\n\nfunc (o *ProblemOptions) parseRetryAfter(value any, timeLayout string) string {\n\t// https://tools.ietf.org/html/rfc7231#section-7.1.3\n\t// Retry-After = HTTP-date / delay-seconds\n\tswitch v := value.(type) {\n\tcase int64:\n\t\treturn strconv.FormatInt(v, 10)\n\tcase int:\n\t\treturn o.parseRetryAfter(int64(v), timeLayout)\n\tcase float64:\n\t\treturn o.parseRetryAfter(int64(math.Round(v)), timeLayout)\n\tcase time.Time:\n\t\treturn v.Format(timeLayout)\n\tcase time.Duration:\n\t\treturn o.parseRetryAfter(parseDurationToSeconds(v), timeLayout)\n\tcase string:\n\t\tdur, err := time.ParseDuration(v)\n\t\tif err != nil {\n\t\t\tt, err := time.Parse(timeLayout, v)\n\t\t\tif err != nil {\n\t\t\t\treturn \"\"\n\t\t\t}\n\n\t\t\treturn o.parseRetryAfter(t, timeLayout)\n\t\t}\n\n\t\treturn o.parseRetryAfter(parseDurationToSeconds(dur), timeLayout)\n\t}\n\n\treturn \"\"\n}\n\n// Apply accepts a Context and applies specific response-time options.\nfunc (o *ProblemOptions) Apply(ctx *Context) {\n\tretryAfterHeaderValue := \"\"\n\ttimeLayout := ctx.Application().ConfigurationReadOnly().GetTimeFormat()\n\n\tif o.RetryAfterFunc != nil {\n\t\tretryAfterHeaderValue = o.parseRetryAfter(o.RetryAfterFunc(ctx), timeLayout)\n\t} else if o.RetryAfter != nil {\n\t\tretryAfterHeaderValue = o.parseRetryAfter(o.RetryAfter, timeLayout)\n\t}\n\n\tif retryAfterHeaderValue != \"\" {\n\t\tctx.Header(\"Retry-After\", retryAfterHeaderValue)\n\t}\n}\n"
  },
  {
    "path": "context/request_params.go",
    "content": "package context\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/core/memstore\"\n)\n\n// RequestParams is a key string - value string storage which\n// context's request dynamic path params are being kept.\n// Empty if the route is static.\ntype RequestParams struct {\n\tmemstore.Store\n}\n\n// Set inserts a parameter value.\n// See `Get` too.\nfunc (r *RequestParams) Set(key, value string) {\n\tif ln := len(r.Store); cap(r.Store) > ln {\n\t\tr.Store = r.Store[:ln+1]\n\t\tp := &r.Store[ln]\n\t\tp.Key = key\n\t\tp.ValueRaw = value\n\t\treturn\n\t}\n\n\tr.Store = append(r.Store, memstore.Entry{\n\t\tKey:      key,\n\t\tValueRaw: value,\n\t})\n}\n\n// Get returns a path parameter's value based on its route's dynamic path key.\nfunc (r *RequestParams) Get(key string) string {\n\tfor i := range r.Store {\n\t\tif kv := r.Store[i]; kv.Key == key {\n\t\t\tif v, ok := kv.ValueRaw.(string); ok {\n\t\t\t\treturn v // it should always be string here on :string parameter.\n\t\t\t}\n\n\t\t\tif v, ok := kv.ValueRaw.(fmt.Stringer); ok {\n\t\t\t\treturn v.String()\n\t\t\t}\n\n\t\t\treturn fmt.Sprintf(\"%v\", kv.ValueRaw)\n\t\t}\n\t}\n\n\treturn \"\"\n}\n\n// GetEntryAt will return the parameter's internal store's `Entry` based on the index.\n// If not found it will return an emptry `Entry`.\nfunc (r *RequestParams) GetEntryAt(index int) memstore.Entry {\n\tentry, _ := r.Store.GetEntryAt(index)\n\treturn entry\n}\n\n// GetEntry will return the parameter's internal store's `Entry` based on its name/key.\n// If not found it will return an emptry `Entry`.\nfunc (r *RequestParams) GetEntry(key string) memstore.Entry {\n\tentry, _ := r.Store.GetEntry(key)\n\treturn entry\n}\n\n// Visit accepts a visitor which will be filled\n// by the key-value params.\nfunc (r *RequestParams) Visit(visitor func(key string, value string)) {\n\tr.Store.Visit(func(k string, v any) {\n\t\tvisitor(k, fmt.Sprintf(\"%v\", v)) // always string here.\n\t})\n}\n\n// GetTrim returns a path parameter's value without trailing spaces based on its route's dynamic path key.\nfunc (r *RequestParams) GetTrim(key string) string {\n\treturn strings.TrimSpace(r.Get(key))\n}\n\n// GetEscape returns a path parameter's double-url-query-escaped value based on its route's dynamic path key.\nfunc (r *RequestParams) GetEscape(key string) string {\n\treturn DecodeQuery(DecodeQuery(r.Get(key)))\n}\n\n// GetDecoded returns a path parameter's double-url-query-escaped value based on its route's dynamic path key.\n// same as `GetEscape`.\nfunc (r *RequestParams) GetDecoded(key string) string {\n\treturn r.GetEscape(key)\n}\n\n// TrimParamFilePart is a middleware which replaces all route dynamic path parameters\n// with values that do not contain any part after the last dot (.) character.\n//\n// Example Code:\n//\n//\tpackage main\n//\n//\timport (\n//\t\t\"github.com/kataras/iris/v12\"\n//\t)\n//\n//\tfunc main() {\n//\t\tapp := iris.New()\n//\t\tapp.Get(\"/{uid:string regexp(^[0-9]{1,20}.html$)}\", iris.TrimParamFilePart, handler)\n//\t\t// TrimParamFilePart can be registered as a middleware to a Party (group of routes) as well.\n//\t\tapp.Listen(\":8080\")\n//\t}\n//\n//\tfunc handler(ctx iris.Context) {\n//\t\t//\n//\t\t// The above line is useless now that we've registered the TrimParamFilePart middleware:\n//\t\t// uid := ctx.Params().GetTrimFileUint64(\"uid\")\n//\t\t//\n//\n//\t\tuid := ctx.Params().GetUint64Default(\"uid\", 0)\n//\t\tctx.Writef(\"Param value: %d\\n\", uid)\n//\t}\nfunc TrimParamFilePart(ctx *Context) { // See #2024.\n\tparams := ctx.Params()\n\n\tfor i, param := range params.Store {\n\t\tif value, ok := param.ValueRaw.(string); ok {\n\t\t\tif idx := strings.LastIndexByte(value, '.'); idx > 1 /* at least .h */ {\n\t\t\t\tvalue = value[0:idx]\n\t\t\t\tparam.ValueRaw = value\n\t\t\t}\n\t\t}\n\n\t\tparams.Store[i] = param\n\t}\n\n\tctx.Next()\n}\n\n// GetTrimFile returns a parameter value but without the last \".ANYTHING_HERE\" part.\nfunc (r *RequestParams) GetTrimFile(key string) string {\n\tvalue := r.Get(key)\n\n\tif idx := strings.LastIndexByte(value, '.'); idx > 1 /* at least .h */ {\n\t\treturn value[0:idx]\n\t}\n\n\treturn value\n}\n\n// GetTrimFileInt same as GetTrimFile but it returns the value as int.\nfunc (r *RequestParams) GetTrimFileInt(key string) int {\n\tvalue := r.Get(key)\n\n\tif idx := strings.LastIndexByte(value, '.'); idx > 1 /* at least .h */ {\n\t\tvalue = value[0:idx]\n\t}\n\n\tv, _ := strconv.Atoi(value)\n\treturn v\n}\n\n// GetTrimFileUint64 same as GetTrimFile but it returns the value as uint64.\nfunc (r *RequestParams) GetTrimFileUint64(key string) uint64 {\n\tvalue := r.Get(key)\n\n\tif idx := strings.LastIndexByte(value, '.'); idx > 1 /* at least .h */ {\n\t\tvalue = value[0:idx]\n\t}\n\n\tv, err := strconv.ParseUint(value, 10, strconv.IntSize)\n\tif err != nil {\n\t\treturn 0\n\t}\n\n\treturn v\n}\n\n// GetTrimFileUint64 same as GetTrimFile but it returns the value as uint.\nfunc (r *RequestParams) GetTrimFileUint(key string) uint {\n\treturn uint(r.GetTrimFileUint64(key))\n}\n\nfunc (r *RequestParams) getRightTrimmed(key string, cutset string) string {\n\treturn strings.TrimRight(strings.ToLower(r.Get(key)), cutset)\n}\n\n// GetTrimHTML returns a parameter value but without the last \".html\" part.\nfunc (r *RequestParams) GetTrimHTML(key string) string {\n\treturn r.getRightTrimmed(key, \".html\")\n}\n\n// GetTrimJSON returns a parameter value but without the last \".json\" part.\nfunc (r *RequestParams) GetTrimJSON(key string) string {\n\treturn r.getRightTrimmed(key, \".json\")\n}\n\n// GetTrimXML returns a parameter value but without the last \".xml\" part.\nfunc (r *RequestParams) GetTrimXML(key string) string {\n\treturn r.getRightTrimmed(key, \".xml\")\n}\n\n// GetIntUnslashed same as Get but it removes the first slash if found.\n// Usage: Get an id from a wildcard path.\n//\n// Returns -1 and false if not path parameter with that \"key\" found.\nfunc (r *RequestParams) GetIntUnslashed(key string) (int, bool) {\n\tv := r.Get(key)\n\tif v != \"\" {\n\t\tif len(v) > 1 {\n\t\t\tif v[0] == '/' {\n\t\t\t\tv = v[1:]\n\t\t\t}\n\t\t}\n\n\t\tvInt, err := strconv.Atoi(v)\n\t\tif err != nil {\n\t\t\treturn -1, false\n\t\t}\n\t\treturn vInt, true\n\t}\n\n\treturn -1, false\n}\n\n// ParamResolvers is the global param resolution for a parameter type for a specific go std or custom type.\n//\n// Key is the specific type, which should be unique.\n// The value is a function which accepts the parameter index\n// and it should return the value as the parameter type evaluator expects it.\n//\n//\ti.e [reflect.TypeOf(\"string\")] = func(paramIndex int) any {\n//\t    return func(ctx *Context) <T> {\n//\t        return ctx.Params().GetEntryAt(paramIndex).ValueRaw.(<T>)\n//\t    }\n//\t}\n//\n// Read https://github.com/kataras/iris/tree/main/_examples/routing/macros for more details.\n// Checks for total available request parameters length\n// and parameter index based on the hero/mvc function added\n// in order to support the MVC.HandleMany(\"GET\", \"/path/{ps}/{pssecond} /path/{ps}\")\n// when on the second requested path, the 'pssecond' should be empty.\nvar ParamResolvers = map[reflect.Type]func(paramIndex int) any{\n\treflect.TypeOf(\"\"): func(paramIndex int) any {\n\t\treturn func(ctx *Context) string {\n\t\t\tif ctx.Params().Len() <= paramIndex {\n\t\t\t\treturn \"\"\n\t\t\t}\n\t\t\treturn ctx.Params().GetEntryAt(paramIndex).ValueRaw.(string)\n\t\t}\n\t},\n\treflect.TypeOf(int(1)): func(paramIndex int) any {\n\t\treturn func(ctx *Context) int {\n\t\t\tif ctx.Params().Len() <= paramIndex {\n\t\t\t\treturn 0\n\t\t\t}\n\t\t\t// v, _ := ctx.Params().GetEntryAt(paramIndex).IntDefault(0)\n\t\t\t// return v\n\t\t\treturn ctx.Params().GetEntryAt(paramIndex).ValueRaw.(int)\n\t\t}\n\t},\n\treflect.TypeOf(int8(1)): func(paramIndex int) any {\n\t\treturn func(ctx *Context) int8 {\n\t\t\tif ctx.Params().Len() <= paramIndex {\n\t\t\t\treturn 0\n\t\t\t}\n\t\t\treturn ctx.Params().GetEntryAt(paramIndex).ValueRaw.(int8)\n\t\t}\n\t},\n\treflect.TypeOf(int16(1)): func(paramIndex int) any {\n\t\treturn func(ctx *Context) int16 {\n\t\t\tif ctx.Params().Len() <= paramIndex {\n\t\t\t\treturn 0\n\t\t\t}\n\t\t\treturn ctx.Params().GetEntryAt(paramIndex).ValueRaw.(int16)\n\t\t}\n\t},\n\treflect.TypeOf(int32(1)): func(paramIndex int) any {\n\t\treturn func(ctx *Context) int32 {\n\t\t\tif ctx.Params().Len() <= paramIndex {\n\t\t\t\treturn 0\n\t\t\t}\n\t\t\treturn ctx.Params().GetEntryAt(paramIndex).ValueRaw.(int32)\n\t\t}\n\t},\n\treflect.TypeOf(int64(1)): func(paramIndex int) any {\n\t\treturn func(ctx *Context) int64 {\n\t\t\tif ctx.Params().Len() <= paramIndex {\n\t\t\t\treturn 0\n\t\t\t}\n\t\t\treturn ctx.Params().GetEntryAt(paramIndex).ValueRaw.(int64)\n\t\t}\n\t},\n\treflect.TypeOf(uint(1)): func(paramIndex int) any {\n\t\treturn func(ctx *Context) uint {\n\t\t\tif ctx.Params().Len() <= paramIndex {\n\t\t\t\treturn 0\n\t\t\t}\n\t\t\treturn ctx.Params().GetEntryAt(paramIndex).ValueRaw.(uint)\n\t\t}\n\t},\n\treflect.TypeOf(uint8(1)): func(paramIndex int) any {\n\t\treturn func(ctx *Context) uint8 {\n\t\t\tif ctx.Params().Len() <= paramIndex {\n\t\t\t\treturn 0\n\t\t\t}\n\t\t\treturn ctx.Params().GetEntryAt(paramIndex).ValueRaw.(uint8)\n\t\t}\n\t},\n\treflect.TypeOf(uint16(1)): func(paramIndex int) any {\n\t\treturn func(ctx *Context) uint16 {\n\t\t\tif ctx.Params().Len() <= paramIndex {\n\t\t\t\treturn 0\n\t\t\t}\n\t\t\treturn ctx.Params().GetEntryAt(paramIndex).ValueRaw.(uint16)\n\t\t}\n\t},\n\treflect.TypeOf(uint32(1)): func(paramIndex int) any {\n\t\treturn func(ctx *Context) uint32 {\n\t\t\tif ctx.Params().Len() <= paramIndex {\n\t\t\t\treturn 0\n\t\t\t}\n\t\t\treturn ctx.Params().GetEntryAt(paramIndex).ValueRaw.(uint32)\n\t\t}\n\t},\n\treflect.TypeOf(uint64(1)): func(paramIndex int) any {\n\t\treturn func(ctx *Context) uint64 {\n\t\t\tif ctx.Params().Len() <= paramIndex {\n\t\t\t\treturn 0\n\t\t\t}\n\t\t\treturn ctx.Params().GetEntryAt(paramIndex).ValueRaw.(uint64)\n\t\t}\n\t},\n\treflect.TypeOf(true): func(paramIndex int) any {\n\t\treturn func(ctx *Context) bool {\n\t\t\tif ctx.Params().Len() <= paramIndex {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn ctx.Params().GetEntryAt(paramIndex).ValueRaw.(bool)\n\t\t}\n\t},\n\treflect.TypeOf(time.Time{}): func(paramIndex int) any {\n\t\treturn func(ctx *Context) time.Time {\n\t\t\tif ctx.Params().Len() <= paramIndex {\n\t\t\t\treturn unixEpochTime\n\t\t\t}\n\n\t\t\tv, ok := ctx.Params().GetEntryAt(paramIndex).ValueRaw.(time.Time)\n\t\t\tif !ok {\n\t\t\t\treturn unixEpochTime\n\t\t\t}\n\n\t\t\treturn v\n\t\t}\n\t},\n\treflect.TypeOf(time.Weekday(0)): func(paramIndex int) any {\n\t\treturn func(ctx *Context) time.Weekday {\n\t\t\tif ctx.Params().Len() <= paramIndex {\n\t\t\t\treturn time.Sunday\n\t\t\t}\n\n\t\t\tv, ok := ctx.Params().GetEntryAt(paramIndex).ValueRaw.(time.Weekday)\n\t\t\tif !ok {\n\t\t\t\treturn time.Sunday\n\t\t\t}\n\n\t\t\treturn v\n\t\t}\n\t},\n}\n\n// ParamResolverByTypeAndIndex will return a function that can be used to bind path parameter's exact value by its Go std type\n// and the parameter's index based on the registered path.\n// Usage: nameResolver := ParamResolverByKindAndKey(reflect.TypeOf(\"\"), 0)\n// Inside a Handler:      nameResolver.Call(ctx)[0]\n//\n//\tit will return the reflect.Value Of the exact type of the parameter(based on the path parameters and macros).\n//\n// It is only useful for dynamic binding of the parameter, it is used on \"hero\" package and it should be modified\n// only when Macros are modified in such way that the default selections for the available go std types are not enough.\n//\n// Returns empty value and false if \"k\" does not match any valid parameter resolver.\nfunc ParamResolverByTypeAndIndex(typ reflect.Type, paramIndex int) (reflect.Value, bool) {\n\t/* NO:\n\t// This could work but its result is not exact type, so direct binding is not possible.\n\tresolver := m.ParamResolver\n\tfn := func(ctx *context.Context) any {\n\t\tentry, _ := ctx.Params().GetEntry(paramName)\n\t\treturn resolver(entry)\n\t}\n\t//\n\n\t// This works but it is slower on serve-time.\n\tparamNameValue := []reflect.Value{reflect.ValueOf(paramName)}\n\tvar fnSignature func(*context.Context) string\n\treturn reflect.MakeFunc(reflect.ValueOf(&fnSignature).Elem().Type(), func(in []reflect.Value) []reflect.Value {\n\t\treturn in[0].MethodByName(\"Params\").Call(emptyIn)[0].MethodByName(\"Get\").Call(paramNameValue)\n\t\t// return []reflect.Value{reflect.ValueOf(in[0].Interface().(*context.Context).Params().Get(paramName))}\n\t})\n\t//\n\t*/\n\n\tr, ok := ParamResolvers[typ]\n\tif !ok || r == nil {\n\t\treturn reflect.Value{}, false\n\t}\n\n\treturn reflect.ValueOf(r(paramIndex)), true\n}\n"
  },
  {
    "path": "context/response_recorder.go",
    "content": "package context\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/textproto\"\n\t\"strconv\"\n\t\"sync\"\n)\n\n// Recorder the middleware to enable response writer recording ( ResponseWriter -> ResponseRecorder)\nvar Recorder = func(ctx *Context) {\n\tctx.Record()\n\tctx.Next()\n}\n\nvar rrpool = sync.Pool{New: func() any { return &ResponseRecorder{} }}\n\n// AcquireResponseRecorder returns a new *AcquireResponseRecorder from the pool.\n// Releasing is done automatically when request and response is done.\nfunc AcquireResponseRecorder() *ResponseRecorder {\n\treturn rrpool.Get().(*ResponseRecorder)\n}\n\nfunc releaseResponseRecorder(w *ResponseRecorder) {\n\trrpool.Put(w)\n}\n\n// A ResponseRecorder is used mostly for testing\n// in order to record and modify, if necessary, the body and status code and headers.\n//\n// See `context.Recorder“ method too.\ntype ResponseRecorder struct {\n\tResponseWriter\n\n\t// keep track of the body written.\n\tchunks []byte\n\t// the saved headers\n\theaders http.Header\n\n\tresult *http.Response\n}\n\nvar _ ResponseWriter = (*ResponseRecorder)(nil)\n\n// Naive returns the simple, underline and original http.ResponseWriter\n// that backends this response writer.\nfunc (w *ResponseRecorder) Naive() http.ResponseWriter {\n\treturn w.ResponseWriter.Naive()\n}\n\n// BeginRecord accepts its parent ResponseWriter and\n// prepares itself, the response recorder, to record and send response to the client.\nfunc (w *ResponseRecorder) BeginRecord(underline ResponseWriter) {\n\tw.ResponseWriter = underline\n\tw.headers = underline.Header().Clone()\n\tw.result = nil\n\tw.ResetBody()\n}\n\n// EndResponse is auto-called when the whole client's request is done,\n// releases the response recorder and its underline ResponseWriter.\nfunc (w *ResponseRecorder) EndResponse() {\n\tw.ResponseWriter.EndResponse()\n\treleaseResponseRecorder(w)\n}\n\n// Write Adds the contents to the body reply, it writes the contents temporarily\n// to a value in order to be flushed at the end of the request,\n// this method give us the opportunity to reset the body if needed.\n//\n// If WriteHeader has not yet been called, Write calls\n// WriteHeader(http.StatusOK) before writing the data. If the Header\n// does not contain a Content-Type line, Write adds a Content-Type set\n// to the result of passing the initial 512 bytes of written data to\n// DetectContentType.\n//\n// Depending on the HTTP protocol version and the client, calling\n// Write or WriteHeader may prevent future reads on the\n// Request.Body. For HTTP/1.x requests, handlers should read any\n// needed request body data before writing the response. Once the\n// headers have been flushed (due to either an explicit Flusher.Flush\n// call or writing enough data to trigger a flush), the request body\n// may be unavailable. For HTTP/2 requests, the Go HTTP server permits\n// handlers to continue to read the request body while concurrently\n// writing the response. However, such behavior may not be supported\n// by all HTTP/2 clients. Handlers should read before writing if\n// possible to maximize compatibility.\nfunc (w *ResponseRecorder) Write(contents []byte) (int, error) {\n\tw.chunks = append(w.chunks, contents...)\n\t// Remember that we should not return all the written length within `Write`:\n\t// see https://github.com/kataras/iris/pull/931\n\treturn len(contents), nil\n}\n\n// Header returns the temporary header map that, on flush response,\n// will be sent by the underline's ResponseWriter's WriteHeader method.\nfunc (w *ResponseRecorder) Header() http.Header {\n\treturn w.headers\n}\n\n// SetBody overrides the body and sets it to a slice of bytes value.\nfunc (w *ResponseRecorder) SetBody(b []byte) {\n\tw.chunks = b\n}\n\n// SetBodyString overrides the body and sets it to a string value.\nfunc (w *ResponseRecorder) SetBodyString(s string) {\n\tw.SetBody([]byte(s))\n}\n\n// Body returns the body tracked from the writer so far,\n// do not use this for edit.\nfunc (w *ResponseRecorder) Body() []byte {\n\treturn w.chunks\n}\n\n// ResetBody resets the response body.\nfunc (w *ResponseRecorder) ResetBody() {\n\tw.chunks = w.chunks[0:0]\n}\n\n// ResetHeaders sets the headers to the underline's response writer's headers, may empty.\nfunc (w *ResponseRecorder) ResetHeaders() {\n\tw.headers = w.ResponseWriter.Header().Clone()\n}\n\n// ClearHeaders clears all headers, both temp and underline's response writer.\nfunc (w *ResponseRecorder) ClearHeaders() {\n\tw.headers = http.Header{}\n\th := w.ResponseWriter.Header()\n\tfor k := range h {\n\t\tdelete(h, k)\n\t}\n}\n\n// Reset clears headers, sets the status code to 200\n// and clears the cached body.\n//\n// - Use ResetBody() and ResetHeaders() instead to keep compression after reseting.\n//\n// - Use Reset() & ResponseRecorder.ResponseWriter.(*context.CompressResponseWriter).Disabled = true\n// to set a new body without compression when the previous handler was iris.Compression.\n//\n// Implements the `ResponseWriterReseter`.\nfunc (w *ResponseRecorder) Reset() bool {\n\tw.ClearHeaders()\n\tw.WriteHeader(defaultStatusCode)\n\tw.ResetBody()\n\treturn true\n}\n\n// FlushResponse the full body, headers and status code to the underline response writer\n// called automatically at the end of each request.\nfunc (w *ResponseRecorder) FlushResponse() {\n\t// copy the headers to the underline response writer\n\tif w.headers != nil {\n\t\th := w.ResponseWriter.Header()\n\t\t// note: we don't reset the current underline's headers.\n\t\tfor k, v := range w.headers {\n\t\t\th[k] = v\n\t\t}\n\t}\n\n\tcw, mustWriteToClose := w.ResponseWriter.(*CompressResponseWriter)\n\tif mustWriteToClose { // see #1569#issuecomment-664003098\n\t\tcw.FlushHeaders()\n\t} else {\n\t\t// NOTE: before the ResponseWriter.Write in order to:\n\t\t// set the given status code even if the body is empty.\n\t\tw.ResponseWriter.FlushResponse()\n\t}\n\n\tif len(w.chunks) > 0 {\n\t\t// ignore error\n\t\tw.ResponseWriter.Write(w.chunks)\n\t}\n\n\tif mustWriteToClose {\n\t\tcw.ResponseWriter.FlushResponse()\n\t\tcw.CompressWriter.Close()\n\t}\n}\n\n// Clone returns a clone of this response writer\n// it copies the header, status code, headers and the beforeFlush finally  returns a new ResponseRecorder\nfunc (w *ResponseRecorder) Clone() ResponseWriter {\n\twc := &ResponseRecorder{}\n\n\t// copy headers.\n\twc.headers = w.headers.Clone()\n\n\t// copy body.\n\tchunksCopy := make([]byte, len(w.chunks))\n\tcopy(chunksCopy, w.chunks)\n\twc.chunks = chunksCopy\n\n\tif resW, ok := w.ResponseWriter.(*responseWriter); ok {\n\t\twc.ResponseWriter = &responseWriter{\n\t\t\tResponseWriter: resW.ResponseWriter,\n\t\t\tstatusCode:     resW.statusCode,\n\t\t\twritten:        resW.written,\n\t\t\tbeforeFlush:    resW.beforeFlush,\n\t\t} // clone it\n\t} else { // else just copy, may pointer, developer can change its behavior\n\t\twc.ResponseWriter = w.ResponseWriter\n\t}\n\n\treturn wc\n}\n\n// CopyTo writes a response writer (temp: status code, headers and body) to another response writer\nfunc (w *ResponseRecorder) CopyTo(res ResponseWriter) {\n\tif to, ok := res.(*ResponseRecorder); ok {\n\n\t\t// set the status code, to is first ( probably an error? (context.StatusCodeNotSuccessful, defaults to >=400).\n\t\tif statusCode := w.ResponseWriter.StatusCode(); statusCode == defaultStatusCode {\n\t\t\tto.WriteHeader(statusCode)\n\t\t}\n\n\t\tif beforeFlush := w.ResponseWriter.GetBeforeFlush(); beforeFlush != nil {\n\t\t\t// if to had a before flush, lets combine them\n\t\t\tif to.GetBeforeFlush() != nil {\n\t\t\t\tnextBeforeFlush := beforeFlush\n\t\t\t\tprevBeforeFlush := to.GetBeforeFlush()\n\t\t\t\tto.SetBeforeFlush(func() {\n\t\t\t\t\tprevBeforeFlush()\n\t\t\t\t\tnextBeforeFlush()\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tto.SetBeforeFlush(w.ResponseWriter.GetBeforeFlush())\n\t\t\t}\n\t\t}\n\n\t\t// if \"to\" is *responseWriter and it never written before (if -1),\n\t\t// set the \"w\"'s written length.\n\t\tif resW, ok := to.ResponseWriter.(*responseWriter); ok {\n\t\t\tif resW.Written() != StatusCodeWritten {\n\t\t\t\tresW.written = w.ResponseWriter.Written()\n\t\t\t}\n\t\t}\n\n\t\t// append the headers\n\t\tfor k, values := range w.headers {\n\t\t\tfor _, v := range values {\n\t\t\t\tif to.headers.Get(v) == \"\" {\n\t\t\t\t\tto.headers.Add(k, v)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// append the body\n\t\tif len(w.chunks) > 0 {\n\t\t\t// ignore error\n\t\t\tto.Write(w.chunks)\n\t\t}\n\t}\n}\n\n// Flush sends any buffered data to the client.\nfunc (w *ResponseRecorder) Flush() {\n\t// This fixes response recorder when chunked + Flush is used.\n\tif w.headers.Get(\"Transfer-Encoding\") == \"chunked\" {\n\t\tif w.Written() == NoWritten {\n\t\t\tif len(w.headers) > 0 {\n\t\t\t\th := w.ResponseWriter.Header()\n\t\t\t\t// note: we don't reset the current underline's headers.\n\t\t\t\tfor k, v := range w.headers {\n\t\t\t\t\th[k] = v\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif len(w.chunks) > 0 {\n\t\t\tw.ResponseWriter.Write(w.chunks)\n\t\t}\n\t}\n\n\tw.ResponseWriter.Flush()\n\tw.ResetBody()\n}\n\n// ErrPushNotSupported is returned by the Push method to\n// indicate that HTTP/2 Push support is not available.\nvar ErrPushNotSupported = errors.New(\"push feature is not supported by this ResponseWriter\")\n\n// Push initiates an HTTP/2 server push. This constructs a synthetic\n// request using the given target and options, serializes that request\n// into a PUSH_PROMISE frame, then dispatches that request using the\n// server's request handler. If opts is nil, default options are used.\n//\n// The target must either be an absolute path (like \"/path\") or an absolute\n// URL that contains a valid host and the same scheme as the parent request.\n// If the target is a path, it will inherit the scheme and host of the\n// parent request.\n//\n// The HTTP/2 spec disallows recursive pushes and cross-authority pushes.\n// Push may or may not detect these invalid pushes; however, invalid\n// pushes will be detected and canceled by conforming clients.\n//\n// Handlers that wish to push URL X should call Push before sending any\n// data that may trigger a request for URL X. This avoids a race where the\n// client issues requests for X before receiving the PUSH_PROMISE for X.\n//\n// Push returns ErrPushNotSupported if the client has disabled push or if push\n// is not supported on the underlying connection.\nfunc (w *ResponseRecorder) Push(target string, opts *http.PushOptions) (err error) {\n\tw.FlushResponse()\n\n\tif pusher, ok := w.ResponseWriter.Naive().(http.Pusher); ok {\n\t\terr = pusher.Push(target, opts)\n\t\tif err != nil && err.Error() == http.ErrNotSupported.ErrorString {\n\t\t\treturn ErrPushNotSupported\n\t\t}\n\t}\n\n\t// NOTE: we have to reset them even if the push failed.\n\tw.ResetBody()\n\tw.ResetHeaders()\n\n\treturn ErrPushNotSupported\n}\n\n// Result returns the response generated by the handler.\n// It does set all provided headers.\n//\n// Result must only be called after the handler has finished running.\nfunc (w *ResponseRecorder) Result() *http.Response { // a modified copy of net/http/httptest\n\tif w.result != nil {\n\t\treturn w.result\n\t}\n\n\theaders := w.headers.Clone()\n\n\t// for k, v := range w.ResponseWriter.Header() {\n\t// \theaders[k] = v\n\t// }\n\t/*\n\t\tdateFound := false\n\t\tfor k := range headers {\n\t\t\tif strings.ToLower(k) == \"date\" {\n\t\t\t\tdateFound = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tif !dateFound {\n\t\t\theaders[\"Date\"] = []string{time.Now().Format(http.TimeFormat)}\n\t\t}\n\t*/\n\n\tres := &http.Response{\n\t\tProto:      \"HTTP/1.1\",\n\t\tProtoMajor: 1,\n\t\tProtoMinor: 1,\n\t\tStatusCode: w.StatusCode(),\n\t\tHeader:     headers,\n\t}\n\tif res.StatusCode == 0 {\n\t\tres.StatusCode = 200\n\t}\n\tres.Status = fmt.Sprintf(\"%03d %s\", res.StatusCode, http.StatusText(res.StatusCode))\n\tif w.chunks != nil {\n\t\tres.Body = io.NopCloser(bytes.NewReader(w.chunks))\n\t} else {\n\t\tres.Body = http.NoBody\n\t}\n\tres.ContentLength = parseContentLength(res.Header.Get(\"Content-Length\"))\n\n\tw.result = res\n\treturn res\n}\n\n// copy of net/http/httptest\nfunc parseContentLength(cl string) int64 {\n\tcl = textproto.TrimString(cl)\n\tif cl == \"\" {\n\t\treturn -1\n\t}\n\tn, err := strconv.ParseUint(cl, 10, 63)\n\tif err != nil {\n\t\treturn -1\n\t}\n\treturn int64(n)\n}\n"
  },
  {
    "path": "context/response_writer.go",
    "content": "package context\n\nimport (\n\t\"bufio\"\n\t\"errors\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"sync\"\n)\n\n// ResponseWriter interface is used by the context to serve an HTTP handler to\n// construct an HTTP response.\n//\n// Note: Only this ResponseWriter is an interface in order to be able\n// for developers to change the response writer of the Context via `context.ResetResponseWriter`.\n// The rest of the response writers implementations (ResponseRecorder & CompressResponseWriter)\n// are coupled to the internal ResponseWriter implementation(*responseWriter).\n//\n// A ResponseWriter may not be used after the Handler\n// has returned.\ntype ResponseWriter interface {\n\thttp.ResponseWriter\n\n\t// Naive returns the simple, underline and original http.ResponseWriter\n\t// that backends this response writer.\n\tNaive() http.ResponseWriter\n\t// SetWriter sets the underline http.ResponseWriter\n\t// that this responseWriter should write on.\n\tSetWriter(underline http.ResponseWriter)\n\t// BeginResponse receives an http.ResponseWriter\n\t// and initialize or reset the response writer's field's values.\n\tBeginResponse(http.ResponseWriter)\n\t// EndResponse is the last function which is called right before the server sent the final response.\n\t//\n\t// Here is the place which we can make the last checks or do a cleanup.\n\tEndResponse()\n\n\t// IsHijacked reports whether this response writer's connection is hijacked.\n\tIsHijacked() bool\n\n\t// StatusCode returns the status code header value.\n\tStatusCode() int\n\n\t// Written should returns the total length of bytes that were being written to the client.\n\t// In addition iris provides some variables to help low-level actions:\n\t// NoWritten, means that nothing were written yet and the response writer is still live.\n\t// StatusCodeWritten, means that status code was written but no other bytes are written to the client, response writer may closed.\n\t// > 0 means that the reply was written and it's the total number of bytes were written.\n\tWritten() int\n\n\t// SetWritten sets manually a value for written, it can be\n\t// NoWritten(-1) or StatusCodeWritten(0), > 0 means body length which is useless here.\n\tSetWritten(int)\n\n\t// SetBeforeFlush registers the unique callback which called exactly before the response is flushed to the client.\n\tSetBeforeFlush(cb func())\n\t// GetBeforeFlush returns (not execute) the before flush callback, or nil if not set by SetBeforeFlush.\n\tGetBeforeFlush() func()\n\t// FlushResponse should be called only once before EndResponse.\n\t// it tries to send the status code if not sent already\n\t// and calls the  before flush callback, if any.\n\t//\n\t// FlushResponse can be called before EndResponse, but it should\n\t// be the last call of this response writer.\n\tFlushResponse()\n\n\t// clone returns a clone of this response writer\n\t// it copies the header, status code, headers and the beforeFlush finally  returns a new ResponseRecorder.\n\tClone() ResponseWriter\n\n\t// CopyTo writes a response writer (temp: status code, headers and body) to another response writer\n\tCopyTo(ResponseWriter)\n\n\t// Flusher indicates if `Flush` is supported by the client.\n\t//\n\t// The default HTTP/1.x and HTTP/2 ResponseWriter implementations\n\t// support Flusher, but ResponseWriter wrappers may not. Handlers\n\t// should always test for this ability at runtime.\n\t//\n\t// Note that even for ResponseWriters that support Flush,\n\t// if the client is connected through an HTTP proxy,\n\t// the buffered data may not reach the client until the response\n\t// completes.\n\tFlusher() (http.Flusher, bool)\n\t// Flush sends any buffered data to the client.\n\tFlush() // required by compress writer.\n}\n\n// ResponseWriterBodyReseter can be implemented by\n// response writers that supports response body overriding\n// (e.g. recorder and compressed).\ntype ResponseWriterBodyReseter interface {\n\t// ResetBody should reset the body and reports back if it could reset successfully.\n\tResetBody()\n}\n\n// ResponseWriterDisabler can be implemented\n// by response writers that can be disabled and restored to their previous state\n// (e.g. compressed).\ntype ResponseWriterDisabler interface {\n\t// Disable should disable this type of response writer and fallback to the default one.\n\tDisable()\n}\n\n// ResponseWriterReseter can be implemented\n// by response writers that can clear the whole response\n// so a new handler can write into this from the beginning.\n// E.g. recorder, compressed (full) and common writer (status code and headers).\ntype ResponseWriterReseter interface {\n\t// Reset should reset the whole response and reports\n\t// whether it could reset successfully.\n\tReset() bool\n}\n\n// ResponseWriterWriteTo can be implemented\n// by response writers that needs a special\n// encoding before writing to their buffers.\n// E.g. a custom recorder that wraps a custom compressed one.\n//\n// Not used by the framework itself.\ntype ResponseWriterWriteTo interface {\n\tWriteTo(dest io.Writer, p []byte)\n}\n\n//  +------------------------------------------------------------+\n//  | Response Writer Implementation                             |\n//  +------------------------------------------------------------+\n\nvar rpool = sync.Pool{New: func() any { return &responseWriter{} }}\n\n// AcquireResponseWriter returns a new *ResponseWriter from the pool.\n// Releasing is done automatically when request and response is done.\nfunc AcquireResponseWriter() ResponseWriter {\n\treturn rpool.Get().(*responseWriter)\n}\n\nfunc releaseResponseWriter(w ResponseWriter) {\n\trpool.Put(w)\n}\n\n// ResponseWriter is the basic response writer,\n// it writes directly to the underline http.ResponseWriter\ntype responseWriter struct {\n\thttp.ResponseWriter\n\n\tstatusCode int // the saved status code which will be used from the cache service\n\t// statusCodeSent bool // reply header has been (logically) written | no needed any more as we have a variable to catch total len of written bytes\n\twritten int // the total size of bytes were written\n\t// yes only one callback, we need simplicity here because on FireStatusCode the beforeFlush events should NOT be cleared\n\t// but the response is cleared.\n\t// Sometimes is useful to keep the event,\n\t// so we keep one func only and let the user decide when he/she wants to override it with an empty func before the FireStatusCode (context's behavior)\n\tbeforeFlush func()\n}\n\nvar _ ResponseWriter = (*responseWriter)(nil)\n\nconst (\n\tdefaultStatusCode = http.StatusOK\n\t// NoWritten !=-1 => when nothing written before\n\tNoWritten = -1\n\t// StatusCodeWritten != 0 =>  when only status code written\n\tStatusCodeWritten = 0\n)\n\n// Naive returns the simple, underline and original http.ResponseWriter\n// that backends this response writer.\nfunc (w *responseWriter) Naive() http.ResponseWriter {\n\treturn w.ResponseWriter\n}\n\n// BeginResponse receives an http.ResponseWriter\n// and initialize or reset the response writer's field's values.\nfunc (w *responseWriter) BeginResponse(underline http.ResponseWriter) {\n\tw.beforeFlush = nil\n\tw.written = NoWritten\n\tw.statusCode = defaultStatusCode\n\tw.SetWriter(underline)\n}\n\n// SetWriter sets the underline http.ResponseWriter\n// that this responseWriter should write on.\nfunc (w *responseWriter) SetWriter(underline http.ResponseWriter) {\n\tw.ResponseWriter = underline\n}\n\n// EndResponse is the last function which is called right before the server sent the final response.\n//\n// Here is the place which we can make the last checks or do a cleanup.\nfunc (w *responseWriter) EndResponse() {\n\treleaseResponseWriter(w)\n}\n\n// Reset clears headers, sets the status code to 200\n// and clears the cached body.\n//\n// Implements the `ResponseWriterReseter`.\nfunc (w *responseWriter) Reset() bool {\n\tif w.written > 0 {\n\t\treturn false // if already written we can't reset this type of response writer.\n\t}\n\n\th := w.Header()\n\tfor k := range h {\n\t\th[k] = nil\n\t}\n\n\tw.written = NoWritten\n\tw.statusCode = defaultStatusCode\n\treturn true\n}\n\n// SetWritten sets manually a value for written, it can be\n// NoWritten(-1) or StatusCodeWritten(0), > 0 means body length which is useless here.\nfunc (w *responseWriter) SetWritten(n int) {\n\tif n >= NoWritten && n <= StatusCodeWritten {\n\t\tw.written = n\n\t}\n}\n\n// Written should returns the total length of bytes that were being written to the client.\n// In addition iris provides some variables to help low-level actions:\n// NoWritten, means that nothing were written yet and the response writer is still live.\n// StatusCodeWritten, means that status code were written but no other bytes are written to the client, response writer may closed.\n// > 0 means that the reply was written and it's the total number of bytes were written.\nfunc (w *responseWriter) Written() int {\n\treturn w.written\n}\n\n// WriteHeader sends an HTTP response header with status code.\n// If WriteHeader is not called explicitly, the first call to Write\n// will trigger an implicit WriteHeader(http.StatusOK).\n// Thus explicit calls to WriteHeader are mainly used to\n// send error codes.\nfunc (w *responseWriter) WriteHeader(statusCode int) {\n\tw.statusCode = statusCode\n}\n\nfunc (w *responseWriter) tryWriteHeader() {\n\tif w.written == NoWritten { // before write, once.\n\t\tw.written = StatusCodeWritten\n\t\tw.ResponseWriter.WriteHeader(w.statusCode)\n\t}\n}\n\n// IsHijacked reports whether this response writer's connection is hijacked.\nfunc (w *responseWriter) IsHijacked() bool {\n\t// Note:\n\t// A zero-byte `ResponseWriter.Write` on a hijacked connection will\n\t// return `http.ErrHijacked` without any other side effects.\n\t_, err := w.ResponseWriter.Write(nil)\n\treturn err == http.ErrHijacked\n}\n\n// Write writes to the client\n// If WriteHeader has not yet been called, Write calls\n// WriteHeader(http.StatusOK) before writing the data. If the Header\n// does not contain a Content-Type line, Write adds a Content-Type set\n// to the result of passing the initial 512 bytes of written data to\n// DetectContentType.\n//\n// Depending on the HTTP protocol version and the client, calling\n// Write or WriteHeader may prevent future reads on the\n// Request.Body. For HTTP/1.x requests, handlers should read any\n// needed request body data before writing the response. Once the\n// headers have been flushed (due to either an explicit Flusher.Flush\n// call or writing enough data to trigger a flush), the request body\n// may be unavailable. For HTTP/2 requests, the Go HTTP server permits\n// handlers to continue to read the request body while concurrently\n// writing the response. However, such behavior may not be supported\n// by all HTTP/2 clients. Handlers should read before writing if\n// possible to maximize compatibility.\nfunc (w *responseWriter) Write(contents []byte) (int, error) {\n\tw.tryWriteHeader()\n\tn, err := w.ResponseWriter.Write(contents)\n\tw.written += n\n\treturn n, err\n}\n\n// StatusCode returns the status code header value\nfunc (w *responseWriter) StatusCode() int {\n\treturn w.statusCode\n}\n\nfunc (w *responseWriter) GetBeforeFlush() func() {\n\treturn w.beforeFlush\n}\n\n// SetBeforeFlush registers the unique callback which called exactly before the response is flushed to the client\nfunc (w *responseWriter) SetBeforeFlush(cb func()) {\n\tw.beforeFlush = cb\n}\n\nfunc (w *responseWriter) FlushResponse() {\n\tif w.beforeFlush != nil {\n\t\tw.beforeFlush()\n\t}\n\n\tw.tryWriteHeader()\n}\n\n// Clone returns a clone of this response writer\n// it copies the header, status code, headers and the beforeFlush finally  returns a new ResponseRecorder.\nfunc (w *responseWriter) Clone() ResponseWriter {\n\twc := &responseWriter{}\n\twc.ResponseWriter = w.ResponseWriter\n\twc.statusCode = w.statusCode\n\twc.beforeFlush = w.beforeFlush\n\twc.written = w.written\n\treturn wc\n}\n\n// CopyTo writes a response writer (temp: status code, headers and body) to another response writer.\nfunc (w *responseWriter) CopyTo(to ResponseWriter) {\n\t// set the status code, failure status code are first class\n\tif w.statusCode >= 400 {\n\t\tto.WriteHeader(w.statusCode)\n\t}\n\n\t// append the headers\n\tfor k, values := range w.Header() {\n\t\tfor _, v := range values {\n\t\t\tif to.Header().Get(v) == \"\" {\n\t\t\t\tto.Header().Add(k, v)\n\t\t\t}\n\t\t}\n\t}\n\t// the body is not copied, this writer doesn't support recording\n}\n\n// ErrHijackNotSupported is returned by the Hijack method to\n// indicate that Hijack feature is not available.\nvar ErrHijackNotSupported = errors.New(\"hijack is not supported by this ResponseWriter\")\n\n// Hijack lets the caller take over the connection.\n// After a call to Hijack(), the HTTP server library\n// will not do anything else with the connection.\n//\n// It becomes the caller's responsibility to manage\n// and close the connection.\n//\n// The returned net.Conn may have read or write deadlines\n// already set, depending on the configuration of the\n// Server. It is the caller's responsibility to set\n// or clear those deadlines as needed.\nfunc (w *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {\n\tif h, isHijacker := w.ResponseWriter.(http.Hijacker); isHijacker {\n\t\tw.written = StatusCodeWritten\n\t\treturn h.Hijack()\n\t}\n\n\treturn nil, nil, ErrHijackNotSupported\n}\n\n// Flusher indicates if `Flush` is supported by the client.\n//\n// The default HTTP/1.x and HTTP/2 ResponseWriter implementations\n// support Flusher, but ResponseWriter wrappers may not. Handlers\n// should always test for this ability at runtime.\n//\n// Note that even for ResponseWriters that support Flush,\n// if the client is connected through an HTTP proxy,\n// the buffered data may not reach the client until the response\n// completes.\nfunc (w *responseWriter) Flusher() (http.Flusher, bool) {\n\tflusher, canFlush := w.ResponseWriter.(http.Flusher)\n\treturn flusher, canFlush\n}\n\n// Flush sends any buffered data to the client.\nfunc (w *responseWriter) Flush() {\n\tif flusher, ok := w.Flusher(); ok {\n\t\t// Flow: WriteHeader -> Flush -> Write -> Write -> Write....\n\t\tw.tryWriteHeader()\n\n\t\tflusher.Flush()\n\t}\n}\n"
  },
  {
    "path": "context/route.go",
    "content": "package context\n\nimport (\n\t\"io\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/macro\"\n)\n\n// RouteReadOnly allows decoupled access to the current route\n// inside the context.\ntype RouteReadOnly interface {\n\t// Name returns the route's name.\n\tName() string\n\n\t// StatusErrorCode returns 0 for common resource routes\n\t// or the error code that an http error handler registered on.\n\tStatusErrorCode() int\n\n\t// Method returns the route's method.\n\tMethod() string\n\n\t// Subdomains returns the route's subdomain.\n\tSubdomain() string\n\n\t// Path returns the route's original registered path.\n\tPath() string\n\n\t// String returns the form of METHOD, SUBDOMAIN, TMPL PATH.\n\tString() string\n\n\t// IsOnline returns true if the route is marked as \"online\" (state).\n\tIsOnline() bool\n\n\t// IsStatic reports whether this route is a static route.\n\t// Does not contain dynamic path parameters,\n\t// is online and registered on GET HTTP Method.\n\tIsStatic() bool\n\t// StaticPath returns the static part of the original, registered route path.\n\t// if /user/{id} it will return /user\n\t// if /user/{id}/friend/{friendid:uint64} it will return /user too\n\t// if /assets/{filepath:path} it will return /assets.\n\tStaticPath() string\n\n\t// ResolvePath returns the formatted path's %v replaced with the args.\n\tResolvePath(args ...string) string\n\t// Trace should writes debug route info to the \"w\".\n\t// Should be called after Build.\n\tTrace(w io.Writer, stoppedIndex int)\n\n\t// Tmpl returns the path template,\n\t// it contains the parsed template\n\t// for the route's path.\n\t// May contain zero named parameters.\n\t//\n\t// Available after the build state, i.e a request handler or Iris Configurator.\n\tTmpl() macro.Template\n\n\t// MainHandlerName returns the first registered handler for the route.\n\tMainHandlerName() string\n\n\t// MainHandlerIndex returns the first registered handler's index for the route.\n\tMainHandlerIndex() int\n\n\t// Property returns a specific property based on its \"key\"\n\t// of this route's Party owner.\n\tProperty(key string) (any, bool)\n\n\t// Sitemap properties: https://www.sitemaps.org/protocol.html\n\n\t// GetLastMod returns the date of last modification of the file served by this route.\n\tGetLastMod() time.Time\n\t// GetChangeFreq returns the the page frequently is likely to change.\n\tGetChangeFreq() string\n\t// GetPriority returns the priority of this route's URL relative to other URLs on your site.\n\tGetPriority() float32\n}\n"
  },
  {
    "path": "context/status.go",
    "content": "package context\n\nimport \"net/http\"\n\n// ClientErrorCodes holds the 4xx Client errors.\nvar (\n\tClientErrorCodes = []int{\n\t\thttp.StatusBadRequest,\n\t\thttp.StatusUnauthorized,\n\t\thttp.StatusPaymentRequired,\n\t\thttp.StatusForbidden,\n\t\thttp.StatusNotFound,\n\t\thttp.StatusMethodNotAllowed,\n\t\thttp.StatusNotAcceptable,\n\t\thttp.StatusProxyAuthRequired,\n\t\thttp.StatusRequestTimeout,\n\t\thttp.StatusConflict,\n\t\thttp.StatusGone,\n\t\thttp.StatusLengthRequired,\n\t\thttp.StatusPreconditionFailed,\n\t\thttp.StatusRequestEntityTooLarge,\n\t\thttp.StatusRequestURITooLong,\n\t\thttp.StatusUnsupportedMediaType,\n\t\thttp.StatusRequestedRangeNotSatisfiable,\n\t\thttp.StatusExpectationFailed,\n\t\thttp.StatusTeapot,\n\t\thttp.StatusMisdirectedRequest,\n\t\thttp.StatusUnprocessableEntity,\n\t\thttp.StatusLocked,\n\t\thttp.StatusFailedDependency,\n\t\thttp.StatusTooEarly,\n\t\thttp.StatusUpgradeRequired,\n\t\thttp.StatusPreconditionRequired,\n\t\thttp.StatusTooManyRequests,\n\t\thttp.StatusRequestHeaderFieldsTooLarge,\n\t\thttp.StatusUnavailableForLegalReasons,\n\t\t// Unofficial.\n\t\tStatusPageExpired,\n\t\tStatusBlockedByWindowsParentalControls,\n\t\tStatusInvalidToken,\n\t\tStatusTokenRequired,\n\t}\n\t// ServerErrorCodes holds the 5xx Server errors.\n\tServerErrorCodes = []int{\n\t\thttp.StatusInternalServerError,\n\t\thttp.StatusNotImplemented,\n\t\thttp.StatusBadGateway,\n\t\thttp.StatusServiceUnavailable,\n\t\thttp.StatusGatewayTimeout,\n\t\thttp.StatusHTTPVersionNotSupported,\n\t\thttp.StatusVariantAlsoNegotiates,\n\t\thttp.StatusInsufficientStorage,\n\t\thttp.StatusLoopDetected,\n\t\thttp.StatusNotExtended,\n\t\thttp.StatusNetworkAuthenticationRequired,\n\t\t// Unofficial.\n\t\tStatusBandwidthLimitExceeded,\n\t\tStatusInvalidSSLCertificate,\n\t\tStatusSiteOverloaded,\n\t\tStatusSiteFrozen,\n\t\tStatusNetworkReadTimeout,\n\t}\n\n\t// ClientAndServerErrorCodes is the static list of all client and server error codes.\n\tClientAndServerErrorCodes = append(ClientErrorCodes, ServerErrorCodes...)\n)\n\n// Unofficial status error codes.\nconst (\n\t// 4xx\n\tStatusPageExpired                      = 419\n\tStatusBlockedByWindowsParentalControls = 450\n\tStatusInvalidToken                     = 498\n\tStatusTokenRequired                    = 499\n\t// 5xx\n\tStatusBandwidthLimitExceeded = 509\n\tStatusInvalidSSLCertificate  = 526\n\tStatusSiteOverloaded         = 529\n\tStatusSiteFrozen             = 530\n\tStatusNetworkReadTimeout     = 598\n)\n\nvar unofficialStatusText = map[int]string{\n\tStatusPageExpired:                      \"Page Expired\",\n\tStatusBlockedByWindowsParentalControls: \"Blocked by Windows Parental Controls\",\n\tStatusInvalidToken:                     \"Invalid Token\",\n\tStatusTokenRequired:                    \"Token Required\",\n\tStatusBandwidthLimitExceeded:           \"Bandwidth Limit Exceeded\",\n\tStatusInvalidSSLCertificate:            \"Invalid SSL Certificate\",\n\tStatusSiteOverloaded:                   \"Site is overloaded\",\n\tStatusSiteFrozen:                       \"Site is frozen\",\n\tStatusNetworkReadTimeout:               \"Network read timeout error\",\n}\n\n// StatusText returns a text for the HTTP status code. It returns the empty\n// string if the code is unknown.\nfunc StatusText(code int) string {\n\ttext := http.StatusText(code)\n\tif text == \"\" {\n\t\ttext = unofficialStatusText[code]\n\t}\n\n\treturn text\n}\n\n// StatusCodeNotSuccessful defines if a specific \"statusCode\" is not\n// a valid status code for a successful response.\n// By default if the status code is lower than 400 then it is not a failure one,\n// otherwise it is considered as an error code.\n//\n// Read more at `iris/Configuration#DisableAutoFireStatusCode` and\n// `iris/core/router/Party#OnAnyErrorCode` for relative information.\n//\n// Modify this variable when your Iris server or/and client\n// not follows the RFC: https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html\nvar StatusCodeNotSuccessful = func(statusCode int) bool { return statusCode >= 400 }\n"
  },
  {
    "path": "context/strconv.go",
    "content": "package context\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n\t\"time\"\n)\n\nfunc strParseUint(value string) (uint, error) {\n\tresult, err := strconv.ParseUint(value, 10, strconv.IntSize)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn uint(result), nil\n}\n\nfunc strParseUint8(value string) (uint8, error) {\n\tresult, err := strconv.ParseUint(value, 10, 8)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn uint8(result), nil\n}\n\nfunc strParseUint16(value string) (uint16, error) {\n\tresult, err := strconv.ParseUint(value, 10, 16)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn uint16(result), nil\n}\n\nfunc strParseUint32(value string) (uint32, error) {\n\tresult, err := strconv.ParseUint(value, 10, 32)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn uint32(result), nil\n}\n\nfunc strParseUint64(value string) (uint64, error) {\n\tresult, err := strconv.ParseUint(value, 10, 64)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn result, nil\n}\n\nfunc strParseInt(value string) (int, error) {\n\tresult, err := strconv.Atoi(value)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn result, nil\n}\n\nfunc strParseInt8(value string) (int8, error) {\n\tresult, err := strconv.ParseInt(value, 10, 8)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn int8(result), nil\n}\n\nfunc strParseInt16(value string) (int16, error) {\n\tresult, err := strconv.ParseInt(value, 10, 16)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn int16(result), nil\n}\n\nfunc strParseInt32(value string) (int32, error) {\n\tresult, err := strconv.ParseInt(value, 10, 32)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn int32(result), nil\n}\n\nfunc strParseInt64(value string) (int64, error) {\n\tresult, err := strconv.ParseInt(value, 10, 64)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn result, nil\n}\n\nfunc strParseFloat32(value string) (float32, error) {\n\tresult, err := strconv.ParseFloat(value, 32)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn float32(result), nil\n}\n\nfunc strParseFloat64(value string) (float64, error) {\n\tresult, err := strconv.ParseFloat(value, 64)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn result, nil\n}\n\nfunc strParseComplex64(value string) (complex64, error) {\n\tresult, err := strconv.ParseComplex(value, 64)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn complex64(result), nil\n}\n\nfunc strParseComplex128(value string) (complex128, error) {\n\tresult, err := strconv.ParseComplex(value, 128)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn result, nil\n}\n\nfunc strParseBool(value string) (bool, error) {\n\tresult, err := strconv.ParseBool(value)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\treturn result, nil\n}\n\nvar dayNames = map[string]time.Weekday{\n\t// longDayNames.\n\t\"Sunday\":    time.Sunday,\n\t\"Monday\":    time.Monday,\n\t\"Tuesday\":   time.Tuesday,\n\t\"Wednesday\": time.Wednesday,\n\t\"Thursday\":  time.Thursday,\n\t\"Friday\":    time.Friday,\n\t\"Saturday\":  time.Saturday,\n\t// longDayNames: lowercase.\n\t\"sunday\":    time.Sunday,\n\t\"monday\":    time.Monday,\n\t\"tuesday\":   time.Tuesday,\n\t\"wednesday\": time.Wednesday,\n\t\"thursday\":  time.Thursday,\n\t\"friday\":    time.Friday,\n\t\"saturday\":  time.Saturday,\n\n\t// shortDayNames\n\t\"Sun\": time.Sunday,\n\t\"Mon\": time.Monday,\n\t\"Tue\": time.Tuesday,\n\t\"Wed\": time.Wednesday,\n\t\"Thu\": time.Thursday,\n\t\"Fri\": time.Friday,\n\t\"Sat\": time.Saturday,\n\t// shortDayNames: lowercase.\n\t\"sun\": time.Sunday,\n\t\"mon\": time.Monday,\n\t\"tue\": time.Tuesday,\n\t\"wed\": time.Wednesday,\n\t\"thu\": time.Thursday,\n\t\"fri\": time.Friday,\n\t\"sat\": time.Saturday,\n}\n\nfunc strParseWeekday(value string) (time.Weekday, error) {\n\tresult, ok := dayNames[value]\n\tif !ok {\n\t\treturn 0, ErrNotFound\n\t}\n\n\treturn result, nil\n}\n\nfunc strParseTime(layout, value string) (time.Time, error) {\n\treturn time.Parse(layout, value)\n}\n\nconst (\n\tsimpleDateLayout1 = \"2006/01/02\"\n\tsimpleDateLayout2 = \"2006-01-02\"\n)\n\nfunc strParseSimpleDate(value string) (time.Time, error) {\n\tt1, err := strParseTime(simpleDateLayout1, value)\n\tif err != nil {\n\t\tt2, err2 := strParseTime(simpleDateLayout2, value)\n\t\tif err2 != nil {\n\t\t\treturn time.Time{}, fmt.Errorf(\"%s, %w\", err.Error(), err2)\n\t\t}\n\n\t\treturn t2, nil\n\t}\n\n\treturn t1, nil\n}\n"
  },
  {
    "path": "context/view.go",
    "content": "package context\n\nimport (\n\t\"fmt\"\n\t\"io\"\n)\n\n// ErrViewNotExist it's an error.\n// It reports whether a template was not found in the parsed templates tree.\ntype ErrViewNotExist struct {\n\tName     string\n\tIsLayout bool\n\tData     any\n}\n\n// Error completes the `error` interface.\nfunc (e ErrViewNotExist) Error() string {\n\ttitle := \"template\"\n\tif e.IsLayout {\n\t\ttitle = \"layout\"\n\t}\n\treturn fmt.Sprintf(\"%s '%s' does not exist\", title, e.Name)\n}\n\n// ViewEngine is the interface which all view engines should be implemented in order to be registered inside iris.\ntype ViewEngine interface {\n\t// Name returns the name of the engine.\n\tName() string\n\t// Load should load the templates from the given FileSystem.\n\tLoad() error\n\t// ExecuteWriter should execute a template by its filename with an optional layout and bindingData.\n\tExecuteWriter(w io.Writer, filename string, layout string, bindingData any) error\n\t// Ext should return the final file extension (including the dot)\n\t// which this view engine is responsible to render.\n\t// If the filename extension on ExecuteWriter is empty then this is appended.\n\tExt() string\n}\n\n// ViewEngineFuncer is an addition of a view engine,\n// if a view engine implements that interface\n// then iris can add some closed-relative iris functions\n// like {{ url }}, {{ urlpath }} and {{ tr }}.\ntype ViewEngineFuncer interface {\n\t// AddFunc should adds a function to the template's function map.\n\tAddFunc(funcName string, funcBody any)\n}\n"
  },
  {
    "path": "context_wrapper.go",
    "content": "package iris\n\nimport (\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/context\"\n)\n\ntype (\n\t// ContextSetter is an interface which can be implemented by a struct\n\t// to set the iris.Context to the struct.\n\t// The receiver must be a pointer of the struct.\n\tContextSetter interface {\n\t\t// SetContext sets the iris.Context to the struct.\n\t\tSetContext(Context)\n\t}\n\n\t// ContextSetterPtr is a pointer of T which implements the `ContextSetter` interface.\n\t// The T must be a struct.\n\tContextSetterPtr[T any] interface {\n\t\t*T\n\t\tContextSetter\n\t}\n\n\t// emptyContextSetter is an empty struct which implements the `ContextSetter` interface.\n\temptyContextSetter struct{}\n)\n\n// SetContext method implements `ContextSetter` interface.\nfunc (*emptyContextSetter) SetContext(Context) {}\n\n// ContextPool is a pool of T. It's used to acquire and release custom context.\n// Use of custom implementation or `NewContextPool`.\n//\n// See `NewContextWrapper` and `NewContextPool` for more.\ntype (\n\tContextPool[T any] interface {\n\t\t// Acquire must return a new T from a pool.\n\t\tAcquire(ctx Context) T\n\t\t// Release must put the T back to the pool.\n\t\tRelease(T)\n\t}\n\n\t// syncContextPool is a sync pool implementation of T.\n\t// It's used to acquire and release T.\n\t// The contextPtr is acquired from the sync pool and released back to the sync pool after the handler's execution.\n\t// The contextPtr is passed to the handler as an argument.\n\t// ThecontextPtr is not shared between requests.\n\t// The contextPtr must implement the `ContextSetter` interface.\n\t// The T must be a struct.\n\t// The contextPtr must be a pointer of T.\n\tsyncContextPool[T any, contextPtr ContextSetterPtr[T]] struct {\n\t\tpool *sync.Pool\n\t}\n)\n\n// Ensure that syncContextPool implements ContextPool.\nvar _ ContextPool[*emptyContextSetter] = (*syncContextPool[emptyContextSetter, *emptyContextSetter])(nil)\n\n// NewContextPool returns a new ContextPool default implementation which\n// uses sync.Pool to implement its Acquire and Release methods.\n// The contextPtr is acquired from the sync pool and released back to the sync pool after the handler's execution.\n// The contextPtr is passed to the handler as an argument.\n// ThecontextPtr is not shared between requests.\n// The contextPtr must implement the `ContextSetter` interface.\n// The T must be a struct.\n// The contextPtr must be a pointer of T.\n//\n// Example:\n// w := iris.NewContextWrapper(iris.NewContextPool[myCustomContext, *myCustomContext]())\nfunc NewContextPool[T any, contextPtr ContextSetterPtr[T]]() ContextPool[contextPtr] {\n\treturn &syncContextPool[T, contextPtr]{\n\t\tpool: &sync.Pool{\n\t\t\tNew: func() any {\n\t\t\t\tvar t contextPtr = new(T)\n\t\t\t\treturn t\n\t\t\t},\n\t\t},\n\t}\n}\n\n// Acquire returns a new T from the sync pool.\nfunc (p *syncContextPool[T, contextPtr]) Acquire(ctx Context) contextPtr {\n\t// var t contextPtr\n\t// if v := p.pool.Get(); v == nil {\n\t// \tt = new(T)\n\t// } else {\n\t// \tt = v.(contextPtr)\n\t// }\n\n\tt := p.pool.Get().(contextPtr)\n\tt.SetContext(ctx)\n\treturn t\n}\n\n// Release puts the T back to the sync pool.\nfunc (p *syncContextPool[T, contextPtr]) Release(t contextPtr) {\n\tp.pool.Put(t)\n}\n\n// ContextWrapper is a wrapper for handlers which expect a T instead of iris.Context.\n//\n// See the `NewContextWrapper` function for more.\ntype ContextWrapper[T any] struct {\n\tpool ContextPool[T]\n}\n\n// NewContextWrapper returns a new ContextWrapper.\n// If pool is nil, a default pool is used.\n// The default pool's AcquireFunc returns a zero value of T.\n// The default pool's ReleaseFunc does nothing.\n// The default pool is used when the pool is nil.\n// Use the `iris.NewContextPool[T, *T]()` to pass a simple context pool.\n// Then, use the `Handler` method to wrap custom handlers to iris ones.\n//\n// Example: https://github.com/kataras/iris/tree/main/_examples/routing/custom-context\nfunc NewContextWrapper[T any](pool ContextPool[T]) *ContextWrapper[T] {\n\tif pool == nil {\n\t\tpanic(\"pool cannot be nil\")\n\t}\n\n\treturn &ContextWrapper[T]{\n\t\tpool: pool,\n\t}\n}\n\n// Pool returns the pool, useful when manually Acquire and Release of custom context is required.\nfunc (w *ContextWrapper[T]) Pool() ContextPool[T] {\n\treturn w.pool\n}\n\n// Handler wraps the handler with the pool's Acquire and Release methods.\n// It returns a new handler which expects a T instead of iris.Context.\n// The T is the type of the pool.\n// The T is acquired from the pool and released back to the pool after the handler's execution.\n// The T is passed to the handler as an argument.\n// The T is not shared between requests.\nfunc (w *ContextWrapper[T]) Handler(handler func(T)) Handler {\n\tif handler == nil {\n\t\treturn nil\n\t}\n\n\treturn func(ctx Context) {\n\t\tnewT := w.pool.Acquire(ctx)\n\t\thandler(newT)\n\t\tw.pool.Release(newT)\n\t}\n}\n\n// Handlers wraps the handlers with the pool's Acquire and Release methods.\nfunc (w *ContextWrapper[T]) Handlers(handlers ...func(T)) context.Handlers {\n\tnewHandlers := make(context.Handlers, len(handlers))\n\tfor i, handler := range handlers {\n\t\tnewHandlers[i] = w.Handler(handler)\n\t}\n\n\treturn newHandlers\n}\n\n// HandlerReturnError same as `Handler` but it converts a handler which returns an error.\nfunc (w *ContextWrapper[T]) HandlerReturnError(handler func(T) error) func(Context) error {\n\tif handler == nil {\n\t\treturn nil\n\t}\n\n\treturn func(ctx Context) error {\n\t\tnewT := w.pool.Acquire(ctx)\n\t\terr := handler(newT)\n\t\tw.pool.Release(newT)\n\t\treturn err\n\t}\n}\n\n// HandlerReturnDuration same as `Handler` but it converts a handler which returns a time.Duration.\nfunc (w *ContextWrapper[T]) HandlerReturnDuration(handler func(T) time.Duration) func(Context) time.Duration {\n\tif handler == nil {\n\t\treturn nil\n\t}\n\n\treturn func(ctx Context) time.Duration {\n\t\tnewT := w.pool.Acquire(ctx)\n\t\tduration := handler(newT)\n\t\tw.pool.Release(newT)\n\t\treturn duration\n\t}\n}\n\n// Filter same as `Handler` but it converts a handler to Filter.\nfunc (w *ContextWrapper[T]) Filter(handler func(T) bool) Filter {\n\tif handler == nil {\n\t\treturn nil\n\t}\n\n\treturn func(ctx Context) bool {\n\t\tnewT := w.pool.Acquire(ctx)\n\t\tshouldContinue := handler(newT)\n\t\tw.pool.Release(newT)\n\t\treturn shouldContinue\n\t}\n}\n\n// FallbackViewFunc same as `Handler` but it converts a handler to FallbackViewFunc.\nfunc (w *ContextWrapper[T]) FallbackViewFunc(handler func(ctx T, err ErrViewNotExist) error) FallbackViewFunc {\n\tif handler == nil {\n\t\treturn nil\n\t}\n\n\treturn func(ctx Context, err ErrViewNotExist) error {\n\t\tnewT := w.pool.Acquire(ctx)\n\t\treturningErr := handler(newT, err)\n\t\tw.pool.Release(newT)\n\t\treturn returningErr\n\t}\n}\n"
  },
  {
    "path": "core/errgroup/errgroup.go",
    "content": "package errgroup\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"sort\"\n\t\"strings\"\n)\n\n// Check reports whether the \"err\" is not nil.\n// If it is a group then it returns true if that or its children contains any error.\nfunc Check(err error) error {\n\tif isNotNil(err) {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\n// Walk loops through each of the errors of \"err\".\n// If \"err\" is *Group then it fires the \"visitor\" for each of its errors, including children.\n// if \"err\" is *Error then it fires the \"visitor\" with its type and wrapped error.\n// Otherwise it fires the \"visitor\" once with typ of nil and err as \"err\".\nfunc Walk(err error, visitor func(typ any, err error)) error {\n\tif err == nil {\n\t\treturn nil\n\t}\n\n\tif group, ok := err.(*Group); ok {\n\t\tlist := group.getAllErrors()\n\t\tfor _, entry := range list {\n\t\t\tif e, ok := entry.(*Error); ok {\n\t\t\t\tvisitor(e.Type, e.Err) // e.Unwrap() <-no.\n\t\t\t} else {\n\t\t\t\tvisitor(nil, err)\n\t\t\t}\n\t\t}\n\t} else if e, ok := err.(*Error); ok {\n\t\tvisitor(e.Type, e.Err)\n\t} else {\n\t\tvisitor(nil, err)\n\t}\n\n\treturn err\n}\n\n/*\nfunc Errors(err error, conv bool) []error {\n\tif err == nil {\n\t\treturn nil\n\t}\n\n\tif group, ok := err.(*Group); ok {\n\t\tlist := group.getAllErrors()\n\t\tif conv {\n\t\t\tfor i, entry := range list {\n\t\t\t\tif _, ok := entry.(*Error); !ok {\n\t\t\t\t\tlist[i] = &Error{Err: entry, Type: group.Type}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn list\n\t}\n\n\treturn []error{err}\n}\n\nfunc Type(err error) any {\n\tif err == nil {\n\t\treturn nil\n\t}\n\n\tif e, ok := err.(*Error); ok && e.Err != nil {\n\t\treturn e.Type\n\t}\n\n\treturn nil\n}\n\nfunc Fill(parent *Group, errors []*Error) {\n\tfor _, err := range errors {\n\t\tif err.Type == parent.Type {\n\t\t\tparent.Add(err)\n\t\t\tcontinue\n\t\t}\n\n\t\tparent.Group(err.Type).Err(err)\n\t}\n\treturn\n}\n*/\n\n// Error implements the error interface.\n// It is a special error type which keep the \"Type\" of the\n// Group that it's created through Group's `Err` and `Errf` methods.\ntype Error struct {\n\tErr  error `json:\"error\" xml:\"Error\" yaml:\"Error\" toml:\"Error\" sql:\"error\"`\n\tType any   `json:\"type\" xml:\"Type\" yaml:\"Type\" toml:\"Type\" sql:\"type\"`\n}\n\n// Error returns the error message of the \"Err\".\nfunc (e *Error) Error() string {\n\treturn e.Err.Error()\n}\n\n// Unwrap calls and returns the result of the \"Err\" Unwrap method or nil.\nfunc (e *Error) Unwrap() error {\n\treturn errors.Unwrap(e.Err)\n}\n\n// Is reports whether the \"err\" is an *Error.\nfunc (e *Error) Is(err error) bool {\n\tif err == nil {\n\t\treturn false\n\t}\n\n\tok := errors.Is(e.Err, err)\n\tif !ok {\n\t\tte, ok := err.(*Error)\n\t\tif !ok {\n\t\t\treturn false\n\t\t}\n\n\t\treturn errors.Is(e.Err, te.Err)\n\t}\n\n\treturn ok\n}\n\n// As reports whether the \"target\" can be used as &Error{target.Type: ?}.\nfunc (e *Error) As(target any) bool {\n\tif target == nil {\n\t\treturn target == e\n\t}\n\n\tok := errors.As(e.Err, target)\n\tif !ok {\n\t\tte, ok := target.(*Error)\n\t\tif !ok {\n\t\t\treturn false\n\t\t}\n\n\t\tif te.Type != nil {\n\t\t\tif te.Type != e.Type {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\n\t\treturn errors.As(te.Err, &e)\n\t}\n\n\treturn ok\n}\n\n// Group is an error container of a specific Type and can have child containers per type too.\ntype Group struct {\n\tparent *Group\n\t// a list of children groups, used to get or create new group through Group method.\n\tchildren map[any]*Group\n\tdepth    int\n\n\tType   any\n\tErrors []error // []*Error\n\n\t// if true then this Group's Error method will return the messages of the errors made by this Group's Group method.\n\t// Defaults to true.\n\tIncludeChildren bool // it clones.\n\t// IncludeTypeText bool\n\tindex int // group index.\n}\n\n// New returns a new empty Group.\nfunc New(typ any) *Group {\n\treturn &Group{\n\t\tType:            typ,\n\t\tIncludeChildren: true,\n\t}\n}\n\nconst delim = \"\\n\"\n\nfunc (g *Group) Error() (s string) {\n\tif len(g.Errors) > 0 {\n\t\tmsgs := make([]string, len(g.Errors))\n\t\tfor i, err := range g.Errors {\n\t\t\tmsgs[i] = err.Error()\n\t\t}\n\n\t\ts = strings.Join(msgs, delim)\n\t}\n\n\tif g.IncludeChildren && len(g.children) > 0 {\n\t\t// return with order of definition.\n\t\tgroups := g.getAllChildren()\n\t\tsortGroups(groups)\n\n\t\tfor _, ge := range groups {\n\t\t\tfor _, childErr := range ge.Errors {\n\t\t\t\ts += childErr.Error() + delim\n\t\t\t}\n\t\t}\n\n\t\tif s != \"\" {\n\t\t\treturn s[:len(s)-1]\n\t\t}\n\t}\n\n\treturn\n}\n\nfunc (g *Group) getAllErrors() []error {\n\tlist := g.Errors\n\n\tif len(g.children) > 0 {\n\t\t// return with order of definition.\n\t\tgroups := g.getAllChildren()\n\t\tsortGroups(groups)\n\n\t\tfor _, ge := range groups {\n\t\t\tlist = append(list, ge.Errors...)\n\t\t}\n\t}\n\n\treturn list\n}\n\nfunc (g *Group) getAllChildren() []*Group {\n\tif len(g.children) == 0 {\n\t\treturn nil\n\t}\n\n\tvar groups []*Group\n\tfor _, child := range g.children {\n\t\tgroups = append(groups, append([]*Group{child}, child.getAllChildren()...)...)\n\t}\n\n\treturn groups\n}\n\n// Unwrap implements the dynamic std errors interface and it returns the parent Group.\nfunc (g *Group) Unwrap() error {\n\tif g == nil {\n\t\treturn nil\n\t}\n\n\treturn g.parent\n}\n\n// Group creates a new group of \"typ\" type, if does not exist, and returns it.\nfunc (g *Group) Group(typ any) *Group {\n\tif g.children == nil {\n\t\tg.children = make(map[any]*Group)\n\t} else {\n\t\tfor _, child := range g.children {\n\t\t\tif child.Type == typ {\n\t\t\t\treturn child\n\t\t\t}\n\t\t}\n\t}\n\n\tchild := &Group{\n\t\tType:            typ,\n\t\tparent:          g,\n\t\tdepth:           g.depth + 1,\n\t\tIncludeChildren: g.IncludeChildren,\n\t\tindex:           g.index + 1 + len(g.children),\n\t}\n\n\tg.children[typ] = child\n\n\treturn child\n}\n\n// Add adds an error to the group.\nfunc (g *Group) Add(err error) {\n\tif err == nil {\n\t\treturn\n\t}\n\n\tg.Errors = append(g.Errors, err)\n}\n\n// Addf adds an error to the group like `fmt.Errorf` and returns it.\nfunc (g *Group) Addf(format string, args ...any) error {\n\terr := fmt.Errorf(format, args...)\n\tg.Add(err)\n\treturn err\n}\n\n// Err adds an error to the group, it transforms it to an Error type if necessary and returns it.\nfunc (g *Group) Err(err error) error {\n\tif err == nil {\n\t\treturn nil\n\t}\n\n\te, ok := err.(*Error)\n\tif !ok {\n\t\tif ge, ok := err.(*Group); ok {\n\t\t\tif g.children == nil {\n\t\t\t\tg.children = make(map[any]*Group)\n\t\t\t}\n\n\t\t\tg.children[ge.Type] = ge\n\t\t\treturn ge\n\t\t}\n\n\t\te = &Error{err, 0}\n\t}\n\te.Type = g.Type\n\n\tg.Add(e)\n\treturn e\n}\n\n// Errf adds an error like `fmt.Errorf` and returns it.\nfunc (g *Group) Errf(format string, args ...any) error {\n\treturn g.Err(fmt.Errorf(format, args...))\n}\n\nfunc sortGroups(groups []*Group) {\n\tsort.Slice(groups, func(i, j int) bool {\n\t\treturn groups[i].index < groups[j].index\n\t})\n}\n\nfunc isNotNil(err error) bool {\n\tif g, ok := err.(*Group); ok {\n\t\tif len(g.Errors) > 0 {\n\t\t\treturn true\n\t\t}\n\n\t\tfor _, child := range g.children {\n\t\t\tif isNotNil(child) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\n\t\treturn false\n\t}\n\n\treturn err != nil\n}\n"
  },
  {
    "path": "core/errgroup/errgroup_test.go",
    "content": "package errgroup\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestErrorError(t *testing.T) {\n\ttestErr := errors.New(\"error\")\n\terr := &Error{Err: testErr}\n\tif expected, got := testErr.Error(), err.Error(); expected != got {\n\t\tt.Fatalf(\"expected %s but got %s\", expected, got)\n\t}\n}\n\nfunc TestErrorUnwrap(t *testing.T) {\n\twrapped := errors.New(\"unwrap\")\n\n\terr := &Error{Err: fmt.Errorf(\"this wraps:%w\", wrapped)}\n\tif expected, got := wrapped, errors.Unwrap(err); expected != got {\n\t\tt.Fatalf(\"expected %#+v but got %#+v\", expected, got)\n\t}\n\n}\nfunc TestErrorIs(t *testing.T) {\n\ttestErr := errors.New(\"is\")\n\terr := &Error{Err: fmt.Errorf(\"this is: %w\", testErr)}\n\tif expected, got := true, errors.Is(err, testErr); expected != got {\n\t\tt.Fatalf(\"expected %v but got %v\", expected, got)\n\t}\n}\n\n// errorString is a trivial implementation of error.\ntype errorString struct {\n\ts string\n}\n\nfunc (e *errorString) Error() string {\n\treturn e.s\n}\n\nfunc TestErrorAs(t *testing.T) {\n\ttestErr := &errorString{\"as\"}\n\terr := &Error{Err: testErr}\n\tif expected, got := true, errors.As(err, &testErr); expected != got {\n\t\tt.Fatalf(\"[testErr as err] expected %v but got %v\", expected, got)\n\t}\n\tif expected, got := false, errors.As(testErr, &err); expected != got /* errorString does not implemeny As, so the std/default functionality will be applied */ {\n\t\tt.Fatalf(\"[err as testErr] expected %v but got %v\", expected, got)\n\t}\n}\n\nfunc TestGroupError(t *testing.T) {\n\tg := New(0)\n\ttests := []string{\"error 1\", \"error 2\", \"error 3\"}\n\tfor _, tt := range tests {\n\t\tg.Add(errors.New(tt))\n\t}\n\n\tif expected, got := strings.Join(tests, \"\\n\"), g.Error(); expected != got {\n\t\tt.Fatalf(\"expected '%s' but got '%s'\", expected, got)\n\t}\n}\n\nfunc TestGroup(t *testing.T) {\n\tconst (\n\t\tapiErrorsType = iota + 1\n\t\tchildAPIErrorsType\n\t\tchildAPIErrors2Type  = \"string type 1\"\n\t\tchildAPIErrors2Type1 = \"string type 2\"\n\n\t\tapiErrorsText        = \"apiErrors error 1\"\n\t\tchildAPIErrorsText   = \"apiErrors:child error 1\"\n\t\tchildAPIErrors2Text  = \"apiErrors:child2 error 1\"\n\t\tchildAPIErrors2Text1 = \"apiErrors:child2_1 error 1\"\n\t)\n\n\tg := New(nil)\n\tapiErrorsGroup := g.Group(apiErrorsType)\n\tapiErrorsGroup.Errf(apiErrorsText)\n\n\tchildAPIErrorsGroup := apiErrorsGroup.Group(childAPIErrorsType)\n\tchildAPIErrorsGroup.Addf(childAPIErrorsText)\n\tchildAPIErrorsGroup2 := apiErrorsGroup.Group(childAPIErrors2Type)\n\tchildAPIErrorsGroup2.Addf(childAPIErrors2Text)\n\tchildAPIErrorsGroup2Group1 := childAPIErrorsGroup2.Group(childAPIErrors2Type1)\n\tchildAPIErrorsGroup2Group1.Addf(childAPIErrors2Text1)\n\n\tif apiErrorsGroup.Type != apiErrorsType {\n\t\tt.Fatal(\"invalid type\")\n\t}\n\n\tif childAPIErrorsGroup.Type != childAPIErrorsType {\n\t\tt.Fatal(\"invalid type\")\n\t}\n\n\tif childAPIErrorsGroup2.Type != childAPIErrors2Type {\n\t\tt.Fatal(\"invalid type\")\n\t}\n\n\tif childAPIErrorsGroup2Group1.Type != childAPIErrors2Type1 {\n\t\tt.Fatal(\"invalid type\")\n\t}\n\n\tif expected, got := 2, len(apiErrorsGroup.children); expected != got {\n\t\tt.Fatalf(\"expected %d but got %d\", expected, got)\n\t}\n\n\tif expected, got := 0, len(childAPIErrorsGroup.children); expected != got {\n\t\tt.Fatalf(\"expected %d but got %d\", expected, got)\n\t}\n\n\tif expected, got := 1, len(childAPIErrorsGroup2.children); expected != got {\n\t\tt.Fatalf(\"expected %d but got %d\", expected, got)\n\t}\n\n\tif expected, got := 0, len(childAPIErrorsGroup2Group1.children); expected != got {\n\t\tt.Fatalf(\"expected %d but got %d\", expected, got)\n\t}\n\n\tif expected, got := 1, apiErrorsGroup.index; expected != got {\n\t\tt.Fatalf(\"expected index %d but got %d\", expected, got)\n\t}\n\n\tif expected, got := 2, childAPIErrorsGroup.index; expected != got {\n\t\tt.Fatalf(\"expected index %d but got %d\", expected, got)\n\t}\n\n\tif expected, got := 3, childAPIErrorsGroup2.index; expected != got {\n\t\tt.Fatalf(\"expected index %d but got %d\", expected, got)\n\t}\n\n\tif expected, got := 4, childAPIErrorsGroup2Group1.index; expected != got {\n\t\tt.Fatalf(\"expected index %d but got %d\", expected, got)\n\t}\n\n\tt.Run(\"Error\", func(t *testing.T) {\n\t\tif expected, got :=\n\t\t\tstrings.Join([]string{apiErrorsText, childAPIErrorsText, childAPIErrors2Text, childAPIErrors2Text1}, delim), g.Error(); expected != got {\n\t\t\tt.Fatalf(\"expected '%s' but got '%s'\", expected, got)\n\t\t}\n\t})\n\n\tt.Run(\"Walk\", func(t *testing.T) {\n\t\texpectedEntries := 4\n\t\t_ = Walk(g, func(typ any, err error) {\n\t\t\tg.IncludeChildren = false\n\t\t\tchildAPIErrorsGroup.IncludeChildren = false\n\t\t\tchildAPIErrorsGroup2.IncludeChildren = false\n\t\t\tchildAPIErrorsGroup2Group1.IncludeChildren = false\n\n\t\t\texpectedEntries--\n\t\t\tvar expected string\n\n\t\t\tswitch typ {\n\t\t\tcase apiErrorsType:\n\t\t\t\texpected = apiErrorsText\n\t\t\tcase childAPIErrorsType:\n\t\t\t\texpected = childAPIErrorsText\n\t\t\tcase childAPIErrors2Type:\n\t\t\t\texpected = childAPIErrors2Text\n\t\t\tcase childAPIErrors2Type1:\n\t\t\t\texpected = childAPIErrors2Text1\n\t\t\t}\n\n\t\t\tif got := err.Error(); expected != got {\n\t\t\t\tt.Fatalf(\"[%v] expected '%s' but got '%s'\", typ, expected, got)\n\t\t\t}\n\t\t})\n\n\t\tif expectedEntries != 0 {\n\t\t\tt.Fatalf(\"not valid number of errors [...%d]\", expectedEntries)\n\t\t}\n\n\t\tg.IncludeChildren = true\n\t\tchildAPIErrorsGroup.IncludeChildren = true\n\t\tchildAPIErrorsGroup2.IncludeChildren = true\n\t\tchildAPIErrorsGroup2Group1.IncludeChildren = true\n\t})\n}\n"
  },
  {
    "path": "core/handlerconv/from_std.go",
    "content": "package handlerconv\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\n\t\"github.com/kataras/iris/v12/context\"\n)\n\n// FromStd converts native http.Handler & http.HandlerFunc to context.Handler.\n//\n// Supported form types:\n//\n//\t.FromStd(h http.Handler)\n//\t.FromStd(func(w http.ResponseWriter, r *http.Request))\n//\t.FromStd(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc))\nfunc FromStd(handler any) context.Handler {\n\tswitch h := handler.(type) {\n\tcase context.Handler:\n\t\treturn h\n\t// case func(*context.Context):\n\t// \treturn h\n\tcase http.Handler:\n\t\t// handlerFunc.ServeHTTP(w,r)\n\t\treturn func(ctx *context.Context) {\n\t\t\th.ServeHTTP(ctx.ResponseWriter(), ctx.Request())\n\t\t}\n\tcase func(http.ResponseWriter, *http.Request):\n\t\t// handlerFunc(w,r)\n\t\treturn FromStd(http.HandlerFunc(h))\n\tcase func(http.ResponseWriter, *http.Request, http.HandlerFunc):\n\t\t// handlerFunc(w,r, http.HandlerFunc)\n\t\t//\n\t\treturn FromStdWithNext(h)\n\tcase func(http.Handler) http.Handler:\n\t\tpanic(fmt.Errorf(`\n\t\t\tPassed handler cannot be converted directly:\n\t\t\t- http.Handler(http.Handler)\n\t\t\t---------------------------------------------------------------------\n\t\t\tPlease use the Application.WrapRouter method instead, example code:\n\t\t\tapp := iris.New()\n\t\t\t// ...\n\t\t\tapp.WrapRouter(func(w http.ResponseWriter, r *http.Request, router http.HandlerFunc) {\n\t\t\t    httpThirdPartyHandler(router).ServeHTTP(w, r)\n\t\t\t})`))\n\tdefault:\n\t\t// No valid handler passed\n\t\tpanic(fmt.Errorf(`\n\t\t\tPassed argument is not a func(iris.Context) neither one of these types:\n\t\t\t- http.Handler\n\t\t\t- func(w http.ResponseWriter, r *http.Request)\n\t\t\t- func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc)\n\t\t\t---------------------------------------------------------------------\n\t\t\tIt seems to be a %T points to: %v`, handler, handler))\n\t}\n}\n\n// FromStdWithNext receives a standar handler - middleware form - and returns a\n// compatible context.Handler wrapper.\nfunc FromStdWithNext(h func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc)) context.Handler {\n\treturn func(ctx *context.Context) {\n\t\tnext := func(w http.ResponseWriter, r *http.Request) {\n\t\t\tctx.ResetRequest(r)\n\t\t\tctx.Next()\n\t\t}\n\n\t\th(ctx.ResponseWriter(), ctx.Request(), next)\n\t}\n}\n"
  },
  {
    "path": "core/handlerconv/from_std_test.go",
    "content": "// black-box testing\npackage handlerconv_test\n\nimport (\n\tstdContext \"context\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/core/handlerconv\"\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nfunc TestFromStd(t *testing.T) {\n\texpected := \"ok\"\n\tstd := func(w http.ResponseWriter, r *http.Request) {\n\t\t_, err := w.Write([]byte(expected))\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t}\n\n\th := handlerconv.FromStd(http.HandlerFunc(std))\n\n\thFunc := handlerconv.FromStd(std)\n\n\tapp := iris.New()\n\tapp.Get(\"/handler\", h)\n\tapp.Get(\"/func\", hFunc)\n\n\te := httptest.New(t, app)\n\n\te.GET(\"/handler\").\n\t\tExpect().Status(iris.StatusOK).Body().IsEqual(expected)\n\n\te.GET(\"/func\").\n\t\tExpect().Status(iris.StatusOK).Body().IsEqual(expected)\n}\n\nfunc TestFromStdWithNext(t *testing.T) {\n\tbasicauth := \"secret\"\n\tpassed := \"ok\"\n\n\ttype contextKey string\n\tstdWNext := func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {\n\t\tif username, password, ok := r.BasicAuth(); ok &&\n\t\t\tusername == basicauth && password == basicauth {\n\t\t\tctx := stdContext.WithValue(r.Context(), contextKey(\"key\"), \"ok\")\n\t\t\tnext.ServeHTTP(w, r.WithContext(ctx))\n\t\t\treturn\n\t\t}\n\t\tw.WriteHeader(iris.StatusForbidden)\n\t}\n\n\th := handlerconv.FromStdWithNext(stdWNext)\n\tnext := func(ctx *context.Context) {\n\t\tctx.WriteString(ctx.Request().Context().Value(contextKey(\"key\")).(string))\n\t}\n\n\tapp := iris.New()\n\tapp.Get(\"/handlerwithnext\", h, next)\n\n\te := httptest.New(t, app)\n\n\te.GET(\"/handlerwithnext\").\n\t\tExpect().Status(iris.StatusForbidden)\n\n\te.GET(\"/handlerwithnext\").WithBasicAuth(basicauth, basicauth).\n\t\tExpect().Status(iris.StatusOK).Body().IsEqual(passed)\n}\n"
  },
  {
    "path": "core/host/interrupt.go",
    "content": "package host\n\nimport (\n\t\"os\"\n\t\"os/signal\"\n\t\"sync\"\n\t\"syscall\"\n)\n\n// RegisterOnInterrupt registers a global function to call when CTRL+C pressed or a unix kill command received.\nfunc RegisterOnInterrupt(cb func()) {\n\t// var cb func()\n\t// switch v := callbackFuncOrFuncReturnsError.(type) {\n\t// case func():\n\t// \tcb = v\n\t// case func() error:\n\t// \tcb = func() { v() }\n\t// default:\n\t// \tpanic(fmt.Errorf(\"unknown type of RegisterOnInterrupt callback: expected func() or func() error but got: %T\", v))\n\t// }\n\n\tInterrupt.Register(cb)\n}\n\n// Interrupt watches the os.Signals for interruption signals\n// and fires the callbacks when those happens.\n// A call of its `FireNow` manually will fire and reset the registered interrupt handlers.\nvar Interrupt TnterruptListener = new(interruptListener)\n\ntype TnterruptListener interface {\n\tRegister(cb func())\n\tFireNow()\n}\n\ntype interruptListener struct {\n\tmu   sync.Mutex\n\tonce sync.Once\n\t// onInterrupt contains a list of the functions that should be called when CTRL+C/CMD+C or\n\t// a unix kill command received.\n\tonInterrupt []func()\n}\n\n// Register registers a global function to call when CTRL+C/CMD+C pressed or a unix kill command received.\nfunc (i *interruptListener) Register(cb func()) {\n\tif cb == nil {\n\t\treturn\n\t}\n\n\ti.listenOnce()\n\ti.mu.Lock()\n\ti.onInterrupt = append(i.onInterrupt, cb)\n\ti.mu.Unlock()\n}\n\n// FireNow can be called more than one times from a Consumer in order to\n// execute all interrupt handlers manually.\nfunc (i *interruptListener) FireNow() {\n\ti.mu.Lock()\n\tfor _, f := range i.onInterrupt {\n\t\tf()\n\t}\n\ti.onInterrupt = i.onInterrupt[0:0]\n\ti.mu.Unlock()\n}\n\n// listenOnce fires a goroutine which calls the interrupt handlers when CTRL+C/CMD+C and e.t.c.\n// If `FireNow` called before then it does nothing when interrupt signal received,\n// so it's safe to be used side by side with `FireNow`.\n//\n// Btw this `listenOnce` is called automatically on first register, it's useless for outsiders.\nfunc (i *interruptListener) listenOnce() {\n\ti.once.Do(func() { go i.notifyAndFire() })\n}\n\nfunc (i *interruptListener) notifyAndFire() {\n\tch := make(chan os.Signal, 1)\n\tsignal.Notify(ch,\n\t\t// kill -SIGINT XXXX or Ctrl+c\n\t\tos.Interrupt,\n\t\tsyscall.SIGINT, // register that too, it should be ok\n\t\t// os.Kill  is equivalent with the syscall.SIGKILL\n\t\t// os.Kill,\n\t\t// syscall.SIGKILL, // register that too, it should be ok\n\t\t// kill -SIGTERM XXXX\n\t\tsyscall.SIGTERM,\n\t)\n\t<-ch\n\ti.FireNow()\n}\n"
  },
  {
    "path": "core/host/proxy.go",
    "content": "package host\n\nimport (\n\t\"crypto/tls\"\n\t\"net/http\"\n\t\"net/http/httputil\"\n\t\"net/url\"\n\t\"path\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/core/netutil\"\n)\n\n// ProxyHandler returns a new ReverseProxy that rewrites\n// URLs to the scheme, host, and base path provided in target. If the\n// target's path is \"/base\" and the incoming request was for \"/dir\",\n// the target request will be for /base/dir.\n//\n// Relative to httputil.NewSingleHostReverseProxy with some additions.\n//\n// Look `ProxyHandlerRemote` too.\nfunc ProxyHandler(target *url.URL, config *tls.Config) *httputil.ReverseProxy {\n\tif config == nil {\n\t\tconfig = &tls.Config{MinVersion: tls.VersionTLS13}\n\t}\n\n\tdirector := func(req *http.Request) {\n\t\tmodifyProxiedRequest(req, target)\n\t\treq.Host = target.Host\n\t\treq.URL.Path = path.Join(target.Path, req.URL.Path)\n\t}\n\n\t// TODO: when go 1.20 released:\n\t/*\n\t\trewrite := func(r *httputil.ProxyRequest) {\n\t\t\tr.SetURL(target)  // Forward request to outboundURL.\n\t\t\tr.SetXForwarded() // Set X-Forwarded-* headers.\n\t\t\t// r.Out.Header.Set(\"X-Additional-Header\", \"header set by the proxy\")\n\t\t\t// To preserve the inbound request's Host header (the default behavior of NewSingleHostReverseProxy):\n\t\t\t// r.Out.Host = r.In.Host\n\t\t}\n\t*/\n\n\tp := &httputil.ReverseProxy{Director: director /*, Rewrite: rewrite */}\n\n\tif netutil.IsLoopbackHost(target.Host) {\n\t\ttransport := &http.Transport{\n\t\t\tTLSClientConfig: config, // lint:ignore\n\t\t}\n\t\tp.Transport = transport\n\t}\n\n\treturn p\n}\n\n// mergeQuery return a query string that combines targetQuery and reqQuery\n// and remove the duplicated query parameters of them.\nfunc mergeQuery(targetQuery, reqQuery string) string {\n\tvar paramSlice []string\n\tif targetQuery != \"\" {\n\t\tparamSlice = strings.Split(targetQuery, \"&\")\n\t}\n\n\tif reqQuery != \"\" {\n\t\tparamSlice = append(paramSlice, strings.Split(reqQuery, \"&\")...)\n\t}\n\n\tvar mergedSlice []string\n\tqueryMap := make(map[string]bool)\n\tfor _, param := range paramSlice {\n\t\tsize := len(queryMap)\n\t\tqueryMap[param] = true\n\t\tif size != len(queryMap) {\n\t\t\tmergedSlice = append(mergedSlice, param)\n\t\t}\n\t}\n\treturn strings.Join(mergedSlice, \"&\")\n}\n\nfunc modifyProxiedRequest(req *http.Request, target *url.URL) {\n\treq.URL.Scheme = target.Scheme\n\treq.URL.Host = target.Host\n\treq.URL.RawQuery = mergeQuery(target.RawQuery, req.URL.RawQuery)\n\n\tif _, ok := req.Header[\"User-Agent\"]; !ok {\n\t\t// explicitly disable User-Agent so it's not set to default value\n\t\treq.Header.Set(\"User-Agent\", \"\")\n\t}\n}\n\n// ProxyHandlerRemote returns a new ReverseProxy that rewrites\n// URLs to the scheme, host, and path provided in target.\n// Case 1: req.Host == target.Host\n// behavior same as ProxyHandler\n// Case 2: req.Host != target.Host\n// the target request will be forwarded to the target's url\n// insecureSkipVerify indicates enable ssl certificate verification or not.\n//\n// Look `ProxyHandler` too.\nfunc ProxyHandlerRemote(target *url.URL, config *tls.Config) *httputil.ReverseProxy {\n\tif config == nil {\n\t\tconfig = &tls.Config{MinVersion: tls.VersionTLS13}\n\t}\n\n\tdirector := func(req *http.Request) {\n\t\tmodifyProxiedRequest(req, target)\n\n\t\tif req.Host != target.Host {\n\t\t\treq.URL.Path = target.Path\n\t\t} else {\n\t\t\treq.URL.Path = path.Join(target.Path, req.URL.Path)\n\t\t}\n\n\t\treq.Host = target.Host\n\t}\n\tp := &httputil.ReverseProxy{Director: director}\n\n\tif netutil.IsLoopbackHost(target.Host) {\n\t\tconfig.InsecureSkipVerify = true\n\t}\n\n\ttransport := &http.Transport{\n\t\tTLSClientConfig: config, // lint:ignore\n\t}\n\tp.Transport = transport\n\treturn p\n}\n\n// NewProxy returns a new host (server supervisor) which\n// proxies all requests to the target.\n// It uses the httputil.NewSingleHostReverseProxy.\n//\n// Usage:\n// target, _ := url.Parse(\"https://mydomain.com\")\n// proxy := NewProxy(\"mydomain.com:80\", target)\n// proxy.ListenAndServe() // use of `proxy.Shutdown` to close the proxy server.\nfunc NewProxy(hostAddr string, target *url.URL, config *tls.Config) *Supervisor {\n\tproxyHandler := ProxyHandler(target, config)\n\tproxy := New(&http.Server{\n\t\tAddr:    hostAddr,\n\t\tHandler: proxyHandler,\n\t})\n\n\treturn proxy\n}\n\n// NewProxyRemote returns a new host (server supervisor) which\n// proxies all requests to the target.\n// It uses the httputil.NewSingleHostReverseProxy.\n//\n// Usage:\n// target, _ := url.Parse(\"https://anotherdomain.com/abc\")\n// proxy := NewProxyRemote(\"mydomain.com\", target, false)\n// proxy.ListenAndServe() // use of `proxy.Shutdown` to close the proxy server.\nfunc NewProxyRemote(hostAddr string, target *url.URL, config *tls.Config) *Supervisor {\n\tproxyHandler := ProxyHandlerRemote(target, config)\n\tproxy := New(&http.Server{\n\t\tAddr:    hostAddr,\n\t\tHandler: proxyHandler,\n\t})\n\n\treturn proxy\n}\n\n// NewRedirection returns a new host (server supervisor) which\n// redirects all requests to the target.\n// Usage:\n// target, _ := url.Parse(\"https://mydomain.com\")\n// r := NewRedirection(\":80\", target, 307)\n// r.ListenAndServe() // use of `r.Shutdown` to close this server.\nfunc NewRedirection(hostAddr string, target *url.URL, redirectStatus int) *Supervisor {\n\tredirectSrv := &http.Server{\n\t\tReadTimeout:  30 * time.Second,\n\t\tWriteTimeout: 60 * time.Second,\n\t\tAddr:         hostAddr,\n\t\tHandler:      RedirectHandler(target, redirectStatus),\n\t}\n\n\treturn New(redirectSrv)\n}\n\n// RedirectHandler returns a simple redirect handler.\n// See `NewProxy` or `ProxyHandler` for more features.\nfunc RedirectHandler(target *url.URL, redirectStatus int) http.Handler {\n\ttargetURI := target.String()\n\tif redirectStatus <= 300 {\n\t\t// here we should use StatusPermanentRedirect but\n\t\t// that may result on unexpected behavior\n\t\t// for end-developers who might change their minds\n\t\t// after a while, so keep status temporary.\n\t\t// Note thatwe could also use StatusFound\n\t\t// as we do on the `Context#Redirect`.\n\t\t// It will also help us to prevent any post data issues.\n\t\tredirectStatus = http.StatusTemporaryRedirect\n\t}\n\n\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tredirectTo := path.Join(targetURI, r.URL.Path)\n\t\tif len(r.URL.RawQuery) > 0 {\n\t\t\tredirectTo += \"?\" + r.URL.RawQuery\n\t\t}\n\t\thttp.Redirect(w, r, redirectTo, redirectStatus)\n\t})\n}\n"
  },
  {
    "path": "core/host/proxy_test.go",
    "content": "// black-box testing\npackage host_test\n\nimport (\n\tstdContext \"context\"\n\t\"crypto/tls\"\n\t\"net\"\n\t\"net/url\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/core/host\"\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nfunc TestProxy(t *testing.T) {\n\texpectedIndex := \"ok /\"\n\texpectedAbout := \"ok /about\"\n\tunexpectedRoute := \"unexpected\"\n\n\t// proxySrv := iris.New()\n\tu, err := url.Parse(\"https://localhost:4444\")\n\tif err != nil {\n\t\tt.Fatalf(\"%v while parsing url\", err)\n\t}\n\n\tconfig := &tls.Config{\n\t\tInsecureSkipVerify: true,\n\t\tMinVersion:         tls.VersionTLS13,\n\t}\n\tproxy := host.NewProxy(\"\", u, config)\n\tproxy.Configure(host.NonBlocking())\n\n\taddr := &net.TCPAddr{\n\t\tIP:   net.IPv4(127, 0, 0, 1),\n\t\tPort: 0,\n\t}\n\n\tlistener, err := net.ListenTCP(\"tcp\", addr)\n\tif err != nil {\n\t\tt.Fatalf(\"%v while creating listener\", err)\n\t}\n\n\t// non-blocking (see above).\n\tproxy.Serve(listener)\n\n\tctx, cancelFunc := stdContext.WithTimeout(stdContext.Background(), 15*time.Second)\n\tdefer cancelFunc()\n\n\t// Wait for up to 15 seconds or until the proxy is ready to serve or a serve failure.\n\tproxy.Wait(ctx)\n\n\tt.Log(listener.Addr().String())\n\n\tapp := iris.New()\n\tapp.Get(\"/\", func(ctx *context.Context) {\n\t\tctx.WriteString(expectedIndex)\n\t})\n\n\tapp.Get(\"/about\", func(ctx *context.Context) {\n\t\tctx.WriteString(expectedAbout)\n\t})\n\n\tapp.OnErrorCode(iris.StatusNotFound, func(ctx *context.Context) {\n\t\tctx.WriteString(unexpectedRoute)\n\t})\n\n\tl, err := net.Listen(\"tcp\", \"localhost:4444\")\n\tif err != nil {\n\t\tt.Fatalf(\"%v while creating tcp4 listener for new tls local test listener\", err)\n\t}\n\t// main server\n\tgo app.Run(iris.Listener(httptest.NewLocalTLSListener(l)), iris.WithoutStartupLog) // nolint:errcheck\n\n\te := httptest.NewInsecure(t, httptest.URL(\"http://\"+listener.Addr().String()))\n\te.GET(\"/\").Expect().Status(iris.StatusOK).Body().IsEqual(expectedIndex)\n\te.GET(\"/about\").Expect().Status(iris.StatusOK).Body().IsEqual(expectedAbout)\n\te.GET(\"/notfound\").Expect().Status(iris.StatusNotFound).Body().IsEqual(unexpectedRoute)\n}\n"
  },
  {
    "path": "core/host/supervisor.go",
    "content": "package host\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\"\n\t\"strings\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/core/netutil\"\n\n\t\"golang.org/x/crypto/acme/autocert\"\n)\n\n// Configurator provides an easy way to modify\n// the Supervisor.\n//\n// Look the `Configure` func for more.\ntype Configurator func(su *Supervisor)\n\n// NonBlocking sets the server to non-blocking mode. Use its `Wait` method to wait for server to be up and running.\nfunc NonBlocking() Configurator {\n\treturn func(su *Supervisor) {\n\t\tsu.nonBlocking = true\n\t}\n}\n\n// Supervisor is the wrapper and the manager for a compatible server\n// and it's relative actions, called Tasks.\n//\n// Interfaces are separated to return relative functionality to them.\ntype Supervisor struct {\n\tServer *http.Server\n\t// FriendlyAddr can be set to customize the \"Now Listening on: {FriendlyAddr}\".\n\tFriendlyAddr                   string // e.g mydomain.com instead of :443 when AutoTLS is used, see `WriteStartupLogOnServe` task.\n\tdisableHTTP1ToHTTP2Redirection bool\n\n\tclosedByInterruptHandler uint32 // non-zero means that the end-developer interrupted it by-purpose.\n\tmanuallyTLS              bool   // we need that in order to determinate what to output on the console before the server begin.\n\tautoTLS                  bool\n\n\tmu sync.RWMutex\n\n\tonServe []func(TaskHost)\n\t// IgnoreErrors should contains the errors that should be ignored\n\t// on both serve functions return statements and error handlers.\n\t//\n\t// Note that this will match the string value instead of the equality of the type's variables.\n\t//\n\t// Defaults to empty.\n\tIgnoredErrors []string\n\tonErr         []func(error)\n\n\t// Fallback should return a http.Server, which may already running\n\t// to handle the HTTP/1.1 clients when TLS/AutoTLS.\n\t// On manual TLS the accepted \"challengeHandler\" just returns the passed handler,\n\t// otherwise it binds to the acme challenge wrapper.\n\t// Example:\n\t//      Fallback = func(h func(fallback http.Handler) http.Handler) *http.Server {\n\t//          s := &http.Server{\n\t//             Handler: h(myServerHandler),\n\t//             ...otherOptions\n\t//          }\n\t//          go s.ListenAndServe()\n\t//          return s\n\t//      }\n\tFallback func(challegeHandler func(fallback http.Handler) http.Handler) *http.Server\n\n\t// See `iris.Configuration.SocketSharding`.\n\tSocketSharding bool\n\t// If more than zero then tcp keep alive listener is attached instead of the simple TCP listener.\n\t// See `iris.Configuration.KeepAlive`\n\tKeepAlive time.Duration\n\n\taddress     string\n\tnonBlocking bool\n\twaiter      *Waiter\n}\n\n// New returns a new host supervisor\n// based on a native net/http \"srv\".\n//\n// It contains all native net/http's Server methods.\n// Plus you can add tasks on specific events.\n// It has its own flow, which means that you can prevent\n// to return and exit and restore the flow too.\nfunc New(srv *http.Server) *Supervisor {\n\tsu := &Supervisor{\n\t\tServer: srv,\n\t}\n\n\tsu.waiter = NewWaiter(7, su.getAddress)\n\treturn su\n}\n\n// Configure accepts one or more `Configurator`.\n// With this function you can use simple functions\n// that are spread across your app to modify\n// the supervisor, these Configurators can be\n// used on any Supervisor instance.\n//\n// Look `Configurator` too.\n//\n// Returns itself.\nfunc (su *Supervisor) Configure(configurators ...Configurator) *Supervisor {\n\tfor _, conf := range configurators {\n\t\tconf(su)\n\t}\n\treturn su\n}\n\n// NoRedirect should be called before `ListenAndServeTLS` when\n// secondary http1 to http2 server is not required. This method will disable\n// the automatic registration of secondary http.Server\n// which would redirect \"http://\" requests to their \"https://\" equivalent.\nfunc (su *Supervisor) NoRedirect() {\n\tsu.disableHTTP1ToHTTP2Redirection = true\n}\n\nfunc (su *Supervisor) newListener() (net.Listener, error) {\n\tvar (\n\t\tl   net.Listener\n\t\terr error\n\t)\n\n\tif su.KeepAlive > 0 {\n\t\tl, err = netutil.TCPKeepAlive(su.Server.Addr, su.SocketSharding, su.KeepAlive)\n\t} else {\n\t\tl, err = netutil.TCP(su.Server.Addr, su.SocketSharding)\n\t}\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// here we can check for sure, without the need of the supervisor's `manuallyTLS` field.\n\tif netutil.IsTLS(su.Server) {\n\t\t// means tls\n\t\ttlsl := tls.NewListener(l, su.Server.TLSConfig)\n\t\treturn tlsl, nil\n\t}\n\n\treturn l, nil\n}\n\n// RegisterOnError registers a function to call when errors occurred by the underline http server.\nfunc (su *Supervisor) RegisterOnError(cb func(error)) {\n\tsu.mu.Lock()\n\tsu.onErr = append(su.onErr, cb)\n\tsu.mu.Unlock()\n}\n\nfunc (su *Supervisor) validateErr(err error) error {\n\tif err == nil {\n\t\treturn nil\n\t}\n\n\tif errors.Is(err, http.ErrServerClosed) && atomic.LoadUint32(&su.closedByInterruptHandler) > 0 {\n\t\treturn nil\n\t}\n\n\tsu.mu.Lock()\n\tdefer su.mu.Unlock()\n\n\tfor _, e := range su.IgnoredErrors {\n\t\tif err.Error() == e {\n\t\t\treturn nil\n\t\t}\n\t}\n\n\treturn err\n}\n\nfunc (su *Supervisor) notifyErr(err error) {\n\tif err == nil {\n\t\treturn\n\t}\n\n\tsu.mu.Lock()\n\tfor _, f := range su.onErr {\n\t\tgo f(err)\n\t}\n\tsu.mu.Unlock()\n}\n\nfunc (su *Supervisor) getAddress() string {\n\tsu.mu.RLock()\n\taddr := su.address\n\tsu.mu.RUnlock()\n\treturn addr\n}\n\nfunc (su *Supervisor) setAddress(addr string) {\n\tsu.mu.Lock()\n\tsu.address = addr\n\tsu.mu.Unlock()\n}\n\n// RegisterOnServe registers a function to call on\n// Serve/ListenAndServe/ListenAndServeTLS/ListenAndServeAutoTLS.\nfunc (su *Supervisor) RegisterOnServe(cb func(TaskHost)) {\n\tsu.mu.Lock()\n\tsu.onServe = append(su.onServe, cb)\n\tsu.mu.Unlock()\n}\n\nfunc (su *Supervisor) notifyServe(host TaskHost) {\n\tsu.mu.Lock()\n\tfor _, f := range su.onServe {\n\t\tgo f(host)\n\t}\n\tsu.mu.Unlock()\n}\n\nfunc (su *Supervisor) supervise(blockFunc func() error) error {\n\thost := createTaskHost(su)\n\n\tsu.notifyServe(host)\n\tatomic.StoreUint32(&su.closedByInterruptHandler, 0)\n\n\tif su.nonBlocking {\n\t\tgo func() {\n\t\t\terr := blockFunc()\n\t\t\tif err != nil {\n\t\t\t\tsu.waiter.Fail(err)\n\t\t\t}\n\n\t\t\terr = su.validateErr(err)\n\t\t\tsu.notifyErr(err)\n\t\t}()\n\n\t\treturn nil\n\t}\n\n\terr := blockFunc()\n\terr = su.validateErr(err)\n\tsu.notifyErr(err)\n\n\treturn err\n}\n\n// Wait blocks until server is up and running or a serve failure.\nfunc (su *Supervisor) Wait(ctx context.Context) error {\n\treturn su.waiter.Wait(ctx)\n}\n\n// Serve accepts incoming connections on the Listener l, creating a\n// new service goroutine for each. The service goroutines read requests and\n// then call su.server.Handler to reply to them.\n//\n// For HTTP/2 support, server.TLSConfig should be initialized to the\n// provided listener's TLS Config before calling Serve. If\n// server.TLSConfig is non-nil and doesn't include the string \"h2\" in\n// Config.NextProtos, HTTP/2 support is not enabled.\n//\n// Serve always returns a non-nil error. After Shutdown or Close, the\n// returned error is http.ErrServerClosed.\nfunc (su *Supervisor) Serve(l net.Listener) error {\n\tsu.setAddress(l.Addr().String())\n\n\treturn su.supervise(func() error {\n\t\treturn su.Server.Serve(l)\n\t})\n}\n\n// ListenAndServe listens on the TCP network address addr\n// and then calls Serve with handler to handle requests\n// on incoming connections.\n// Accepted connections are configured to enable TCP keep-alives.\nfunc (su *Supervisor) ListenAndServe() error {\n\tl, err := su.newListener()\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn su.Serve(l)\n}\n\nfunc loadCertificate(c, k string) (*tls.Certificate, error) {\n\tvar (\n\t\tcert tls.Certificate\n\t\terr  error\n\t)\n\n\tif fileExists(c) && fileExists(k) {\n\t\t// act them as files in the system.\n\t\tcert, err = tls.LoadX509KeyPair(c, k)\n\t} else {\n\t\t// act them as raw contents.\n\t\tcert, err = tls.X509KeyPair([]byte(c), []byte(k))\n\t}\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &cert, nil\n}\n\n// ListenAndServeTLS acts identically to ListenAndServe, except that it\n// expects HTTPS connections. Additionally, files containing a certificate and\n// matching private key for the server must be provided. If the certificate\n// is signed by a certificate authority, the certFile should be the concatenation\n// of the server's certificate, any intermediates, and the CA's certificate.\nfunc (su *Supervisor) ListenAndServeTLS(certFileOrContents string, keyFileOrContents string) error {\n\tvar getCertificate func(*tls.ClientHelloInfo) (*tls.Certificate, error)\n\n\t// If tls.Config configured manually through a host configurator then skip that\n\t// and let the redirection service registered alone.\n\t// e.g. https://github.com/kataras/iris/issues/1481#issuecomment-605621255\n\tif su.Server.TLSConfig == nil {\n\t\tif certFileOrContents == \"\" && keyFileOrContents == \"\" {\n\t\t\treturn errors.New(\"empty certFileOrContents or keyFileOrContents and Server.TLSConfig\")\n\t\t}\n\n\t\tcert, err := loadCertificate(certFileOrContents, keyFileOrContents)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tgetCertificate = func(*tls.ClientHelloInfo) (*tls.Certificate, error) {\n\t\t\treturn cert, nil\n\t\t}\n\t}\n\n\tsu.manuallyTLS = true\n\treturn su.runTLS(getCertificate, nil)\n}\n\n// ListenAndServeAutoTLS acts identically to ListenAndServe, except that it\n// expects HTTPS connections. Server's certificates are auto generated from LETSENCRYPT using\n// the golang/x/net/autocert package.\n//\n// The whitelisted domains are separated by whitespace in \"domain\" argument, i.e \"iris-go.com\".\n// If empty, all hosts are currently allowed. This is not recommended,\n// as it opens a potential attack where clients connect to a server\n// by IP address and pretend to be asking for an incorrect host name.\n// Manager will attempt to obtain a certificate for that host, incorrectly,\n// eventually reaching the CA's rate limit for certificate requests\n// and making it impossible to obtain actual certificates.\n//\n// For an \"e-mail\" use a non-public one, letsencrypt needs that for your own security.\n//\n// The \"cacheDir\" is being, optionally, used to provide cache\n// stores and retrieves previously-obtained certificates.\n// If empty, certs will only be cached for the lifetime of the auto tls manager.\n//\n// Note: The domain should be like \"iris-go.com www.iris-go.com\",\n// the e-mail like \"kataras2006@hotmail.com\" and the cacheDir like \"letscache\"\n// The `ListenAndServeAutoTLS` will start a new server for you,\n// which will redirect all http versions to their https, including subdomains as well.\nfunc (su *Supervisor) ListenAndServeAutoTLS(domain string, email string, cacheDir string) error {\n\tvar (\n\t\tcache      autocert.Cache\n\t\thostPolicy autocert.HostPolicy\n\t)\n\n\tif cacheDir != \"\" {\n\t\tcache = autocert.DirCache(cacheDir)\n\t}\n\n\tif strings.TrimSpace(domain) != \"\" {\n\t\tdomains := strings.Split(domain, \" \")\n\t\tsu.FriendlyAddr = strings.Join(domains, \", \")\n\t\thostPolicy = autocert.HostWhitelist(domains...)\n\t}\n\n\tsu.autoTLS = true\n\n\tautoTLSManager := &autocert.Manager{\n\t\tPrompt:     autocert.AcceptTOS,\n\t\tHostPolicy: hostPolicy,\n\t\tEmail:      email,\n\t\tCache:      cache,\n\t}\n\n\treturn su.runTLS(autoTLSManager.GetCertificate, autoTLSManager.HTTPHandler)\n}\n\nfunc (su *Supervisor) runTLS(getCertificate func(*tls.ClientHelloInfo) (*tls.Certificate, error), challengeHandler func(fallback http.Handler) http.Handler) error {\n\tif su.manuallyTLS && !su.disableHTTP1ToHTTP2Redirection {\n\t\t// If manual TLS and auto-redirection is enabled,\n\t\t// then create an empty challenge handler so the :80 server starts.\n\t\tchallengeHandler = func(h http.Handler) http.Handler { // it is always nil on manual TLS.\n\t\t\ttarget, _ := url.Parse(\"https://\" + netutil.ResolveVHost(su.Server.Addr)) // e.g. https://localhost:443\n\t\t\thttp1Handler := RedirectHandler(target, http.StatusMovedPermanently)\n\t\t\treturn http1Handler\n\t\t}\n\t}\n\n\tif challengeHandler != nil {\n\t\thttp1Server := &http.Server{\n\t\t\tAddr:              \":http\",\n\t\t\tHandler:           challengeHandler(nil), // nil for redirection.\n\t\t\tReadTimeout:       su.Server.ReadTimeout,\n\t\t\tReadHeaderTimeout: su.Server.ReadHeaderTimeout,\n\t\t\tWriteTimeout:      su.Server.WriteTimeout,\n\t\t\tIdleTimeout:       su.Server.IdleTimeout,\n\t\t\tMaxHeaderBytes:    su.Server.MaxHeaderBytes,\n\t\t}\n\n\t\tif su.Fallback == nil {\n\t\t\tif !su.manuallyTLS && su.disableHTTP1ToHTTP2Redirection {\n\t\t\t\t// automatic redirection was disabled but Fallback was not registered.\n\t\t\t\treturn fmt.Errorf(\"autotls: use iris.AutoTLSNoRedirect instead\")\n\t\t\t}\n\t\t\tgo http1Server.ListenAndServe()\n\t\t} else {\n\t\t\t// if it's manual TLS still can have its own Fallback server here,\n\t\t\t// the handler will be the redirect one, the difference is that it can run on any port.\n\t\t\tsrv := su.Fallback(challengeHandler)\n\t\t\tif srv == nil {\n\t\t\t\tif !su.manuallyTLS {\n\t\t\t\t\treturn fmt.Errorf(\"autotls: relies on an HTTP/1.1 server\")\n\t\t\t\t}\n\t\t\t\t// for any case the end-developer decided to return nil here,\n\t\t\t\t// we proceed with the automatic redirection.\n\t\t\t\tsrv = http1Server\n\t\t\t\tgo srv.ListenAndServe()\n\t\t\t} else {\n\t\t\t\tif srv.Addr == \"\" {\n\t\t\t\t\tsrv.Addr = \":http\"\n\t\t\t\t}\n\t\t\t\t// } else if !su.manuallyTLS && srv.Addr != \":80\" && srv.Addr != \":http\" {\n\t\t\t\t// \thostname, _, _ := net.SplitHostPort(su.Server.Addr)\n\t\t\t\t// \treturn fmt.Errorf(\"autotls: The HTTP-01 challenge relies on http://%s:80/.well-known/acme-challenge/\", hostname)\n\t\t\t\t// }\n\n\t\t\t\tif srv.Handler == nil {\n\t\t\t\t\t// handler was nil, caller wanted to change the server's options like read/write timeout.\n\t\t\t\t\tsrv.Handler = http1Server.Handler\n\t\t\t\t\tgo srv.ListenAndServe() // automatically start it, we assume the above ^\n\t\t\t\t}\n\t\t\t\thttp1Server = srv // to register the shutdown event.\n\t\t\t}\n\t\t}\n\n\t\tsu.RegisterOnShutdown(func() {\n\t\t\ttimeout := 10 * time.Second\n\t\t\tctx, cancel := context.WithTimeout(context.Background(), timeout)\n\t\t\tdefer cancel()\n\t\t\thttp1Server.Shutdown(ctx)\n\t\t})\n\t}\n\n\tif su.Server.TLSConfig == nil {\n\t\t// If tls.Config is NOT configured manually through a host configurator,\n\t\t// then create it.\n\t\tsu.Server.TLSConfig = &tls.Config{\n\t\t\tMinVersion:               tls.VersionTLS12,\n\t\t\tGetCertificate:           getCertificate,\n\t\t\tPreferServerCipherSuites: true,\n\t\t\tNextProtos:               []string{\"h2\", \"http/1.1\"},\n\t\t\tCurvePreferences: []tls.CurveID{\n\t\t\t\ttls.CurveP521,\n\t\t\t\ttls.CurveP384,\n\t\t\t\ttls.CurveP256,\n\t\t\t},\n\t\t\tCipherSuites: []uint16{\n\t\t\t\ttls.TLS_AES_128_GCM_SHA256,\n\t\t\t\ttls.TLS_CHACHA20_POLY1305_SHA256,\n\t\t\t\ttls.TLS_AES_256_GCM_SHA384,\n\t\t\t\ttls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,\n\t\t\t\ttls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,\n\t\t\t\ttls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,\n\t\t\t\ttls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,\n\t\t\t\ttls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,\n\t\t\t\ttls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,\n\t\t\t\ttls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,\n\t\t\t\t// tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, G402: TLS Bad Cipher Suite\n\t\t\t\t0xC028, /* TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 */\n\t\t\t},\n\t\t}\n\t}\n\n\tln, err := netutil.TCP(su.Server.Addr, su.SocketSharding)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn su.supervise(func() error { return su.Server.ServeTLS(ln, \"\", \"\") })\n}\n\n// RegisterOnShutdown registers a function to call on Shutdown.\n// This can be used to gracefully shutdown connections that have\n// undergone NPN/ALPN protocol upgrade or that have been hijacked.\n// This function should start protocol-specific graceful shutdown,\n// but should not wait for shutdown to complete.\n//\n// Callbacks will run as separate go routines.\nfunc (su *Supervisor) RegisterOnShutdown(cb func()) {\n\tsu.Server.RegisterOnShutdown(cb)\n}\n\n// Shutdown gracefully shuts down the server without interrupting any\n// active connections. Shutdown works by first closing all open\n// listeners, then closing all idle connections, and then waiting\n// indefinitely for connections to return to idle and then shut down.\n// If the provided context expires before the shutdown is complete,\n// then the context's error is returned.\n//\n// Shutdown does not attempt to close nor wait for hijacked\n// connections such as WebSockets. The caller of Shutdown should\n// separately notify such long-lived connections of shutdown and wait\n// for them to close, if desired.\nfunc (su *Supervisor) Shutdown(ctx context.Context) error {\n\tif ctx == nil {\n\t\tctx = context.Background()\n\t}\n\treturn su.Server.Shutdown(ctx)\n}\n\nfunc (su *Supervisor) shutdownOnInterrupt(ctx context.Context) {\n\tatomic.StoreUint32(&su.closedByInterruptHandler, 1)\n\tsu.Shutdown(ctx)\n}\n\n// fileExists tries to report whether a local physical file of \"filename\" exists.\nfunc fileExists(filename string) bool {\n\tinfo, err := os.Stat(filename)\n\tif err != nil {\n\t\treturn false\n\t}\n\n\treturn !info.IsDir()\n}\n"
  },
  {
    "path": "core/host/supervisor_task_example_test.go",
    "content": "// white-box testing\npackage host\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"time\"\n)\n\nfunc ExampleSupervisor_RegisterOnError() {\n\tsu := New(&http.Server{Addr: \":8273\", Handler: http.DefaultServeMux})\n\n\tsu.RegisterOnError(func(err error) {\n\t\tfmt.Println(err.Error())\n\t})\n\n\tsu.RegisterOnError(func(err error) {\n\t\tfmt.Println(err.Error())\n\t})\n\n\tsu.RegisterOnError(func(err error) {\n\t\tfmt.Println(err.Error())\n\t})\n\n\tgo su.ListenAndServe()\n\ttime.Sleep(1 * time.Second)\n\tif err := su.Shutdown(context.TODO()); err != nil {\n\t\tpanic(err)\n\t}\n\ttime.Sleep(1 * time.Second)\n\n\t// Output:\n\t// http: Server closed\n\t// http: Server closed\n\t// http: Server closed\n}\n"
  },
  {
    "path": "core/host/supervisor_test.go",
    "content": "// white-box testing\n\npackage host\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/tls\"\n\t\"log\"\n\t\"net\"\n\t\"net/http\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/iris-contrib/httpexpect/v2\"\n)\n\nconst (\n\tdebugMode = false\n)\n\nfunc newTester(t *testing.T, baseURL string, handler http.Handler) *httpexpect.Expect {\n\tvar transporter http.RoundTripper\n\n\tif strings.HasPrefix(baseURL, \"http\") { // means we are testing real serve time\n\t\ttransporter = &http.Transport{\n\t\t\tTLSClientConfig: &tls.Config{InsecureSkipVerify: true, MinVersion: tls.VersionTLS13},\n\t\t}\n\t} else { // means we are testing the handler itself\n\t\ttransporter = httpexpect.NewBinder(handler)\n\t}\n\n\ttestConfiguration := httpexpect.Config{\n\t\tBaseURL: baseURL,\n\t\tClient: &http.Client{\n\t\t\tTransport: transporter,\n\t\t\tJar:       httpexpect.NewCookieJar(),\n\t\t},\n\t\tReporter: httpexpect.NewAssertReporter(t),\n\t}\n\n\tif debugMode {\n\t\ttestConfiguration.Printers = []httpexpect.Printer{\n\t\t\thttpexpect.NewDebugPrinter(t, true),\n\t\t}\n\t}\n\n\treturn httpexpect.WithConfig(testConfiguration)\n}\n\nfunc testSupervisor(t *testing.T, creator func(*http.Server, []func(TaskHost)) *Supervisor) {\n\tloggerOutput := &bytes.Buffer{}\n\tlogger := log.New(loggerOutput, \"\", 0)\n\tmu := new(sync.RWMutex)\n\tconst (\n\t\texpectedHelloMessage = \"Hello\\n\"\n\t)\n\n\t// http routing\n\n\texpectedBody := \"this is the response body\\n\"\n\n\tmux := http.NewServeMux()\n\tmux.HandleFunc(\"/\", func(w http.ResponseWriter, r *http.Request) {\n\t\t_, err := w.Write([]byte(expectedBody))\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t})\n\n\t// host (server wrapper and adapter) construction\n\n\tsrv := &http.Server{Handler: mux, ErrorLog: logger}\n\taddr := \"localhost:5525\"\n\t// serving\n\tln, err := net.Listen(\"tcp4\", addr)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\thelloMe := func(_ TaskHost) {\n\t\tmu.Lock()\n\t\tlogger.Print(expectedHelloMessage)\n\t\tmu.Unlock()\n\t}\n\n\thost := creator(srv, []func(TaskHost){helloMe})\n\tdefer host.Shutdown(context.TODO())\n\n\tgo host.Serve(ln)\n\n\t// http testsing and various calls\n\t// no need for time sleep because the following will take some time by theirselves\n\ttester := newTester(t, \"http://\"+addr, mux)\n\ttester.Request(\"GET\", \"/\").Expect().Status(http.StatusOK).Body().IsEqual(expectedBody)\n\n\t// WARNING: Data Race here because we try to read the logs\n\t// but it's \"safe\" here.\n\ttime.Sleep(500 * time.Millisecond) // wait a bit\n\n\t// testing Task (recorded) message:\n\tmu.RLock()\n\tgot := loggerOutput.String()\n\tmu.RUnlock()\n\tif expectedHelloMessage != got {\n\t\tt.Fatalf(\"expected hello Task's message to be '%s' but got '%s'\", expectedHelloMessage, got)\n\t}\n}\n\nfunc TestSupervisor(t *testing.T) {\n\ttestSupervisor(t, func(srv *http.Server, tasks []func(TaskHost)) *Supervisor {\n\t\tsu := New(srv)\n\t\tfor _, t := range tasks {\n\t\t\tsu.RegisterOnServe(t)\n\t\t}\n\n\t\treturn su\n\t})\n}\n"
  },
  {
    "path": "core/host/task.go",
    "content": "package host\n\n// the 24hour name was \"Supervisor\" but it's not cover its usage\n// 100%, best name is Task or Thead, I'll chouse Task.\n// and re-name the host to \"Supervisor\" because that is the really\n// supervisor.\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/core/netutil\"\n)\n\n// WriteStartupLogOnServe is a task which accepts a logger(io.Writer)\n// and logs the listening address\n// by a generated message based on the host supervisor's server and writes it to the \"w\".\n// This function should be registered on Serve.\nfunc WriteStartupLogOnServe(w io.Writer) func(TaskHost) {\n\treturn func(h TaskHost) {\n\t\tguessScheme := netutil.ResolveScheme(h.Supervisor.autoTLS || h.Supervisor.manuallyTLS || h.Supervisor.Fallback != nil)\n\t\taddr := h.Supervisor.FriendlyAddr\n\t\tif addr == \"\" {\n\t\t\taddr = h.Supervisor.Server.Addr\n\t\t}\n\n\t\tvar listeningURIs = make([]string, 0, 1)\n\n\t\tif host, port, err := net.SplitHostPort(addr); err == nil { // Improve for the issue #2175.\n\t\t\tif host == \"\" || host == \"0.0.0.0\" {\n\t\t\t\tif ifaces, err := net.Interfaces(); err == nil {\n\t\t\t\t\tvar ips []string\n\t\t\t\t\tfor _, i := range ifaces {\n\t\t\t\t\t\taddrs, err := i.Addrs()\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfor _, localAddr := range addrs {\n\t\t\t\t\t\t\tvar ip net.IP\n\t\t\t\t\t\t\tswitch v := localAddr.(type) {\n\t\t\t\t\t\t\tcase *net.IPNet:\n\t\t\t\t\t\t\t\tip = v.IP\n\t\t\t\t\t\t\tcase *net.IPAddr:\n\t\t\t\t\t\t\t\tip = v.IP\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif ip != nil && ip.To4() != nil {\n\t\t\t\t\t\t\t\tif !ip.IsPrivate() {\n\t\t\t\t\t\t\t\t\t// let's don't print ips that are not accessible through browser.\n\t\t\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tips = append(ips, ip.String())\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tfor _, ip := range ips {\n\t\t\t\t\t\tlisteningURI := netutil.ResolveURL(guessScheme, fmt.Sprintf(\"%s:%s\", ip, port))\n\n\t\t\t\t\t\tlisteningURI = \"> Network:  \" + listeningURI\n\t\t\t\t\t\tlisteningURIs = append(listeningURIs, listeningURI)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t//\tif len(listeningURIs) == 0 {\n\t\t// ^ check no need, we want to print the virtual addr too.\n\t\tlisteningURI := netutil.ResolveURL(guessScheme, addr)\n\t\tif len(listeningURIs) > 0 {\n\t\t\tlisteningURIs[0] = \"\\n\" + listeningURIs[0]\n\t\t\tlisteningURI = \"> Local:    \" + listeningURI\n\t\t}\n\t\tlisteningURIs = append(listeningURIs, listeningURI)\n\t\t/*\n\t\t\tNow listening on:\n\t\t\t\t> Network:  http://192.168.1.109:8080\n\t\t\t\t> Network:  http://172.25.224.1:8080\n\t\t\t\t> Local:    http://localhost:8080\n\n\t\t\tOtherwise:\n\t\t\t\tNow listening on: http://192.168.1.109:8080\n\t\t*/\n\t\t_, _ = fmt.Fprintf(w, \"Now listening on: %s\\nApplication started. Press CTRL+C to shut down.\\n\",\n\t\t\tstrings.Join(listeningURIs, \"\\n\"))\n\t}\n}\n\n// ShutdownOnInterrupt terminates the supervisor and its underline server when CMD+C/CTRL+C pressed.\n// This function should be registered on Interrupt.\nfunc ShutdownOnInterrupt(su *Supervisor, shutdownTimeout time.Duration) func() {\n\treturn func() {\n\t\tctx, cancel := context.WithTimeout(context.Background(), shutdownTimeout)\n\t\tdefer cancel()\n\n\t\tsu.shutdownOnInterrupt(ctx)\n\t}\n}\n\n// TaskHost contains all the necessary information\n// about the host supervisor, its server\n// and the exports the whole flow controller of it.\ntype TaskHost struct {\n\tSupervisor *Supervisor\n}\n\n// Serve can (re)run the server with the latest known configuration.\nfunc (h TaskHost) Serve() error {\n\t// the underline server's serve, using the \"latest known\" listener from the supervisor.\n\tl, err := h.Supervisor.newListener()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// if http.serverclosed ignore the error, it will have this error\n\t// from the previous close\n\tif err := h.Supervisor.Server.Serve(l); !errors.Is(err, http.ErrServerClosed) {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// HostURL returns the listening full url (scheme+host)\n// based on the supervisor's server's address.\nfunc (h TaskHost) HostURL() string {\n\treturn netutil.ResolveURLFromServer(h.Supervisor.Server)\n}\n\n// Hostname returns the underline server's hostname.\nfunc (h TaskHost) Hostname() string {\n\treturn netutil.ResolveHostname(h.Supervisor.Server.Addr)\n}\n\n// Shutdown gracefully shuts down the server without interrupting any\n// active connections. Shutdown works by first closing all open\n// listeners, then closing all idle connections, and then waiting\n// indefinitely for connections to return to idle and then shut down.\n// If the provided context expires before the shutdown is complete,\n// then the context's error is returned.\n//\n// Shutdown does not attempt to close nor wait for hijacked\n// connections such as WebSockets. The caller of Shutdown should\n// separately notify such long-lived connections of shutdown and wait\n// for them to close, if desired.\n//\n// This Shutdown calls the underline's Server's Shutdown, in order to be able to re-start the server\n// from a task.\nfunc (h TaskHost) Shutdown(ctx context.Context) error {\n\t// the underline server's Shutdown (otherwise we will cancel all tasks and do cycles)\n\treturn h.Supervisor.Server.Shutdown(ctx)\n}\n\nfunc createTaskHost(su *Supervisor) TaskHost {\n\treturn TaskHost{\n\t\tSupervisor: su,\n\t}\n}\n"
  },
  {
    "path": "core/host/waiter.go",
    "content": "package host\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"math\"\n\t\"net\"\n\t\"sync\"\n\t\"time\"\n)\n\n// Waiter is a helper for waiting for a server to be up and running.\ntype Waiter struct {\n\tdefaultMaxRetries int\n\taddressFunc       func() string\n\n\tfailure error // or runError for app.Run.\n\tmu      sync.RWMutex\n}\n\n// NewWaiter returns a new Waiter.\nfunc NewWaiter(defaultMaxRetries int, addressFunc func() string) *Waiter {\n\tif defaultMaxRetries <= 0 {\n\t\tdefaultMaxRetries = 7 // 256 seconds max.\n\t}\n\n\treturn &Waiter{\n\t\tdefaultMaxRetries: defaultMaxRetries,\n\t\taddressFunc:       addressFunc,\n\t}\n}\n\n// Wait blocks the main goroutine until the application is up and running.\nfunc (w *Waiter) Wait(ctx context.Context) error {\n\t// First check if there is an error already from Done.\n\tif err := w.getFailure(); err != nil {\n\t\treturn err\n\t}\n\n\t// Set the base for exponential backoff.\n\tbase := 2.0\n\n\t// Get the maximum number of retries by context or force to default max retries (e.g. 7).\n\tvar maxRetries int\n\t// Get the deadline of the context.\n\tif deadline, ok := ctx.Deadline(); ok {\n\t\tnow := time.Now()\n\t\ttimeout := deadline.Sub(now)\n\n\t\tmaxRetries = getMaxRetries(timeout, base)\n\t} else {\n\t\tmaxRetries = w.defaultMaxRetries\n\t}\n\n\t// Set the initial retry interval.\n\tretryInterval := time.Second\n\n\treturn w.tryConnect(ctx, w.addressFunc, maxRetries, retryInterval, base)\n}\n\n// getMaxRetries calculates the maximum number of retries from the retry interval and the base.\nfunc getMaxRetries(retryInterval time.Duration, base float64) int {\n\t// Convert the retry interval to seconds.\n\tseconds := retryInterval.Seconds()\n\t// Apply the inverse formula.\n\tretries := math.Log(seconds)/math.Log(base) - 1\n\treturn int(math.Round(retries))\n}\n\n// tryConnect tries to connect to the server with the given context and retry parameters.\nfunc (w *Waiter) tryConnect(ctx context.Context, addressFunc func() string, maxRetries int, retryInterval time.Duration, base float64) error {\n\t// Try to connect to the server in a loop.\n\tfor i := 0; i < maxRetries; i++ {\n\t\t// Check the context before each attempt.\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\t// Context is canceled, return the context error.\n\t\t\treturn ctx.Err()\n\t\tdefault:\n\t\t\taddress := addressFunc() // Get this server's listening address.\n\t\t\tif address == \"\" {\n\t\t\t\ti-- // Note that this may be modified at another go routine of the serve method. So it may be empty at first chance. So retry fetching the VHost every 1 second.\n\t\t\t\ttime.Sleep(time.Second)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// Context is not canceled, proceed with the attempt.\n\t\t\tconn, err := net.Dial(\"tcp\", address)\n\t\t\tif err == nil {\n\t\t\t\t// Connection successful, close the connection and return nil.\n\t\t\t\tconn.Close()\n\t\t\t\treturn nil // exit.\n\t\t\t} // ignore error.\n\n\t\t\t// Connection failed, wait for the retry interval and try again.\n\t\t\ttime.Sleep(retryInterval)\n\t\t\t// After each failed attempt, check the server Run's error again.\n\t\t\tif err := w.getFailure(); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\t// Increase the retry interval by the base raised to the power of the number of attempts.\n\t\t\t/*\n\t\t\t\t0\t2 seconds\n\t\t\t\t1\t4 seconds\n\t\t\t\t2\t8 seconds\n\t\t\t\t3\t~16 seconds\n\t\t\t\t4\t~32 seconds\n\t\t\t\t5\t~64 seconds\n\t\t\t\t6\t~128 seconds\n\t\t\t\t7\t~256 seconds\n\t\t\t\t8\t~512 seconds\n\t\t\t\t...\n\t\t\t*/\n\t\t\tretryInterval = time.Duration(math.Pow(base, float64(i+1))) * time.Second\n\t\t}\n\t}\n\t// All attempts failed, return an error.\n\treturn fmt.Errorf(\"failed to connect to the server after %d retries\", maxRetries)\n}\n\n// Fail is called by the server's Run method when the server failed to start.\nfunc (w *Waiter) Fail(err error) {\n\tw.mu.Lock()\n\tw.failure = err\n\tw.mu.Unlock()\n}\n\nfunc (w *Waiter) getFailure() error {\n\tw.mu.RLock()\n\terr := w.failure\n\tw.mu.RUnlock()\n\treturn err\n}\n"
  },
  {
    "path": "core/memstore/gob.go",
    "content": "package memstore\n\nimport (\n\t\"bytes\"\n\t\"encoding/gob\"\n\t\"io\"\n\t\"time\"\n)\n\n// why?\n// on the future we may change how these encoders/decoders\n// and we may need different method for store and other for entry.\n\nfunc init() {\n\tgob.Register(Store{})\n\tgob.Register(Entry{})\n\tgob.Register(time.Time{})\n}\n\n// GobEncode accepts a store and writes\n// as series of bytes to the \"w\" writer.\nfunc GobEncode(store Store, w io.Writer) error {\n\tenc := gob.NewEncoder(w)\n\terr := enc.Encode(store)\n\treturn err\n}\n\n// GobSerialize same as GobEncode but it returns\n// the bytes using a temp buffer.\nfunc GobSerialize(store Store) ([]byte, error) {\n\tw := new(bytes.Buffer)\n\terr := GobEncode(store, w)\n\treturn w.Bytes(), err\n}\n\n// GobEncodeEntry accepts an entry and writes\n// as series of bytes to the \"w\" writer.\nfunc GobEncodeEntry(entry Entry, w io.Writer) error {\n\tenc := gob.NewEncoder(w)\n\terr := enc.Encode(entry)\n\treturn err\n}\n\n// GobSerializeEntry same as GobEncodeEntry but it returns\n// the bytes using a temp buffer.\nfunc GobSerializeEntry(entry Entry) ([]byte, error) {\n\tw := new(bytes.Buffer)\n\terr := GobEncodeEntry(entry, w)\n\treturn w.Bytes(), err\n}\n\n// GobDecode accepts a series of bytes and returns\n// the store.\nfunc GobDecode(b []byte) (store Store, err error) {\n\tdec := gob.NewDecoder(bytes.NewBuffer(b))\n\t// no reference because of:\n\t// gob: decoding into local type *memstore.Store, received remote type Entry\n\terr = dec.Decode(&store)\n\treturn\n}\n\n// GobDecodeEntry accepts a series of bytes and returns\n// the entry.\nfunc GobDecodeEntry(b []byte) (entry Entry, err error) {\n\tdec := gob.NewDecoder(bytes.NewBuffer(b))\n\terr = dec.Decode(&entry)\n\treturn\n}\n"
  },
  {
    "path": "core/memstore/lifetime.go",
    "content": "package memstore\n\nimport (\n\t\"sync\"\n\t\"time\"\n)\n\nvar (\n\t// Clock is the default clock to get the current time,\n\t// it can be used for testing purposes too.\n\t//\n\t// Defaults to time.Now.\n\tClock func() time.Time = time.Now\n\n\t// ExpireDelete may be set on Cookie.Expire for expiring the given cookie.\n\tExpireDelete = time.Unix(0, 0) // time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)\n)\n\n// LifeTime controls the session expiration datetime.\ntype LifeTime struct {\n\t// Remember, tip for the future:\n\t// No need of gob.Register, because we embed the time.Time.\n\t// And serious bug which has a result of me spending my whole evening:\n\t// Because of gob encoding it doesn't encodes/decodes the other fields if time.Time is embedded\n\t// (this should be a bug(go1.9-rc1) or not. We don't care atm)\n\ttime.Time\n\ttimer *time.Timer\n\n\t// StartTime holds the Now of the Begin.\n\tBegun time.Time\n\n\tmu sync.RWMutex\n}\n\n// NewLifeTime returns a pointer to an empty LifeTime instance.\nfunc NewLifeTime() *LifeTime {\n\treturn &LifeTime{}\n}\n\n// Begin will begin the life based on the Clock (time.Now()).Add(d).\n// Use `Continue` to continue from a stored time(database-based session does that).\nfunc (lt *LifeTime) Begin(d time.Duration, onExpire func()) {\n\tif d <= 0 {\n\t\treturn\n\t}\n\n\tnow := Clock()\n\n\tlt.mu.Lock()\n\tlt.Begun = now\n\tlt.Time = now.Add(d)\n\tlt.timer = time.AfterFunc(d, onExpire)\n\tlt.mu.Unlock()\n}\n\n// Revive will continue the life based on the stored Time.\n// Other words that could be used for this func are: Continue, Restore, Resc.\nfunc (lt *LifeTime) Revive(onExpire func()) {\n\tlt.mu.RLock()\n\tt := lt.Time\n\tlt.mu.RUnlock()\n\n\tif t.IsZero() {\n\t\treturn\n\t}\n\n\tnow := Clock()\n\tif t.After(now) {\n\t\td := t.Sub(now)\n\t\tlt.mu.Lock()\n\t\tif lt.timer != nil {\n\t\t\tlt.timer.Stop() // Stop the existing timer, if any.\n\t\t}\n\t\tlt.Begun = now\n\t\tlt.timer = time.AfterFunc(d, onExpire) // and execute on-time the new onExpire function.\n\t\tlt.mu.Unlock()\n\t}\n}\n\n// Shift resets the lifetime based on \"d\".\nfunc (lt *LifeTime) Shift(d time.Duration) {\n\tlt.mu.Lock()\n\tif d > 0 && lt.timer != nil {\n\t\tnow := Clock()\n\t\tlt.Begun = now\n\t\tlt.Time = now.Add(d)\n\t\tlt.timer.Reset(d)\n\t}\n\tlt.mu.Unlock()\n}\n\n// ExpireNow reduce the lifetime completely.\nfunc (lt *LifeTime) ExpireNow() {\n\tlt.mu.Lock()\n\tlt.Time = ExpireDelete\n\tif lt.timer != nil {\n\t\tlt.timer.Stop()\n\t}\n\tlt.mu.Unlock()\n}\n\n// HasExpired reports whether \"lt\" represents is expired.\nfunc (lt *LifeTime) HasExpired() bool {\n\tlt.mu.RLock()\n\tt := lt.Time\n\tlt.mu.RUnlock()\n\n\tif t.IsZero() {\n\t\treturn false\n\t}\n\n\treturn t.Before(Clock())\n}\n\n// DurationUntilExpiration returns the duration until expires, it can return negative number if expired,\n// a call to `HasExpired` may be useful before calling this `Dur` function.\nfunc (lt *LifeTime) DurationUntilExpiration() time.Duration {\n\tlt.mu.RLock()\n\tt := lt.Time\n\tlt.mu.RUnlock()\n\n\treturn t.Sub(Clock())\n}\n"
  },
  {
    "path": "core/memstore/memstore.go",
    "content": "// Package memstore contains a store which is just\n// a collection of key-value entries with immutability capabilities.\n//\n// Developers can use that storage to their own apps if they like its behavior.\n// It's fast and in the same time you get read-only access (safety) when you need it.\npackage memstore\n\nimport (\n\t\"fmt\"\n\t\"math\"\n\t\"reflect\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n)\n\ntype (\n\t// ValueSetter is the interface which can be accepted as a generic solution of RequestParams or memstore when Set is the only requirement,\n\t// i.e internally on macro/template/TemplateParam#Eval:paramChanger.\n\tValueSetter interface {\n\t\tSet(key string, newValue any) (Entry, bool)\n\t}\n\t// Entry is the entry of the context storage Store - .Values()\n\tEntry struct {\n\t\tKey       string `json:\"key\" msgpack:\"key\" yaml:\"Key\" toml:\"Value\"`\n\t\tValueRaw  any    `json:\"value\" msgpack:\"value\" yaml:\"Value\" toml:\"Value\"`\n\t\timmutable bool   // if true then it can't change by its caller.\n\t}\n\n\t// StringEntry is just a key-value wrapped by a struct.\n\t// See Context.URLParamsSorted method.\n\tStringEntry struct {\n\t\tKey   string `json:\"key\" msgpack:\"key\" yaml:\"Key\" toml:\"Value\"`\n\t\tValue string `json:\"value\" msgpack:\"value\" yaml:\"Value\" toml:\"Value\"`\n\t}\n\n\t// Store is a collection of key-value entries with immutability capabilities.\n\tStore []Entry\n)\n\nvar _ ValueSetter = (*Store)(nil)\n\n// GetByKindOrNil will try to get this entry's value of \"k\" kind,\n// if value is not that kind it will NOT try to convert it the \"k\", instead\n// it will return nil, except if boolean; then it will return false\n// even if the value was not bool.\n//\n// If the \"k\" kind is not a string or int or int64 or bool\n// then it will return the raw value of the entry as it's.\nfunc (e Entry) GetByKindOrNil(k reflect.Kind) any {\n\tswitch k {\n\tcase reflect.String:\n\t\tv := e.StringDefault(\"__$nf\")\n\t\tif v == \"__$nf\" {\n\t\t\treturn nil\n\t\t}\n\t\treturn v\n\tcase reflect.Int:\n\t\tv, err := e.IntDefault(-1)\n\t\tif err != nil || v == -1 {\n\t\t\treturn nil\n\t\t}\n\t\treturn v\n\tcase reflect.Int64:\n\t\tv, err := e.Int64Default(-1)\n\t\tif err != nil || v == -1 {\n\t\t\treturn nil\n\t\t}\n\t\treturn v\n\tcase reflect.Bool:\n\t\tv, err := e.BoolDefault(false)\n\t\tif err != nil {\n\t\t\treturn nil\n\t\t}\n\t\treturn v\n\tdefault:\n\t\treturn e.ValueRaw\n\t}\n}\n\n// StringDefault returns the entry's value as string.\n// If not found returns \"def\".\nfunc (e Entry) StringDefault(def string) string {\n\tv := e.ValueRaw\n\tif v == nil {\n\t\treturn def\n\t}\n\n\tif vString, ok := v.(string); ok {\n\t\treturn vString\n\t}\n\n\tval := fmt.Sprintf(\"%v\", v)\n\tif val != \"\" {\n\t\treturn val\n\t}\n\n\treturn def\n}\n\n// String returns the entry's value as string.\nfunc (e Entry) String() string {\n\treturn e.StringDefault(\"\")\n}\n\n// StringTrim returns the entry's string value without trailing spaces.\nfunc (e Entry) StringTrim() string {\n\treturn strings.TrimSpace(e.String())\n}\n\n// ErrEntryNotFound may be returned from memstore methods if\n// a key (with a certain kind) was not found.\n// Usage:\n//\n// var e *ErrEntryNotFound\n// errors.As(err, &e)\n// To check for specific key error:\n// errors.As(err, &ErrEntryNotFound{Key: \"key\"})\n// To check for specific key-kind error:\n// errors.As(err, &ErrEntryNotFound{Key: \"key\", Kind: reflect.Int})\ntype ErrEntryNotFound struct {\n\tKey  string       // the entry's key.\n\tKind reflect.Kind // i.e bool, int, string...\n\tType reflect.Type // i.e time.Time{} or custom struct.\n}\n\nfunc (e *ErrEntryNotFound) Error() string {\n\treturn fmt.Sprintf(\"not found: %s as %s (%s)\", e.Key, e.Kind.String(), e.Type.String())\n}\n\n// As can be used to manually check if the error came from the memstore\n// is a not found entry, the key must much in order to return true.\n// Usage:\n// errors.As(err, &ErrEntryNotFound{Key: \"key\", Kind: reflect.Int})\n//\n// Do NOT use this method directly, prefer` errors.As` method as explained above.\n//\n// Implements: go/src/errors/wrap.go#84\nfunc (e *ErrEntryNotFound) As(target any) bool {\n\tv, ok := target.(*ErrEntryNotFound)\n\tif !ok {\n\t\treturn false\n\t}\n\n\tif v.Key != \"\" && v.Key != e.Key {\n\t\treturn false\n\t}\n\n\tif v.Kind != reflect.Invalid && v.Kind != e.Kind {\n\t\treturn false\n\t}\n\n\treturn true\n}\n\nfunc (e Entry) notFound(typ reflect.Type) *ErrEntryNotFound {\n\treturn &ErrEntryNotFound{Key: e.Key, Kind: typ.Kind(), Type: typ}\n}\n\nvar intType = reflect.TypeOf(int(0))\n\n// IntDefault returns the entry's value as int.\n// If not found returns \"def\" and a non-nil error.\nfunc (e Entry) IntDefault(def int) (int, error) {\n\tv := e.ValueRaw\n\tif v == nil {\n\t\treturn def, e.notFound(intType)\n\t}\n\n\tswitch vv := v.(type) {\n\tcase string:\n\t\tval, err := strconv.Atoi(vv)\n\t\tif err != nil {\n\t\t\treturn def, err\n\t\t}\n\t\treturn val, nil\n\tcase int:\n\t\treturn vv, nil\n\tcase int8:\n\t\treturn int(vv), nil\n\tcase int16:\n\t\treturn int(vv), nil\n\tcase int32:\n\t\treturn int(vv), nil\n\tcase int64:\n\t\treturn int(vv), nil\n\n\tcase uint:\n\t\treturn int(vv), nil\n\tcase uint8:\n\t\treturn int(vv), nil\n\tcase uint16:\n\t\treturn int(vv), nil\n\tcase uint32:\n\t\treturn int(vv), nil\n\tcase uint64:\n\t\treturn int(vv), nil\n\t}\n\n\treturn def, e.notFound(intType)\n}\n\nvar int8Type = reflect.TypeOf(int8(0))\n\n// Int8Default returns the entry's value as int8.\n// If not found returns \"def\" and a non-nil error.\nfunc (e Entry) Int8Default(def int8) (int8, error) {\n\tv := e.ValueRaw\n\tif v == nil {\n\t\treturn def, e.notFound(int8Type)\n\t}\n\n\tswitch vv := v.(type) {\n\tcase string:\n\t\tval, err := strconv.ParseInt(vv, 10, 8)\n\t\tif err != nil {\n\t\t\treturn def, err\n\t\t}\n\t\treturn int8(val), nil\n\tcase int:\n\t\treturn int8(vv), nil\n\tcase int8:\n\t\treturn vv, nil\n\tcase int16:\n\t\treturn int8(vv), nil\n\tcase int32:\n\t\treturn int8(vv), nil\n\tcase int64:\n\t\treturn int8(vv), nil\n\n\tcase uint:\n\t\treturn int8(vv), nil\n\tcase uint8:\n\t\treturn int8(vv), nil\n\tcase uint16:\n\t\treturn int8(vv), nil\n\tcase uint32:\n\t\treturn int8(vv), nil\n\tcase uint64:\n\t\treturn int8(vv), nil\n\t}\n\n\treturn def, e.notFound(int8Type)\n}\n\nvar int16Type = reflect.TypeOf(int16(0))\n\n// Int16Default returns the entry's value as int16.\n// If not found returns \"def\" and a non-nil error.\nfunc (e Entry) Int16Default(def int16) (int16, error) {\n\tv := e.ValueRaw\n\tif v == nil {\n\t\treturn def, e.notFound(int16Type)\n\t}\n\n\tswitch vv := v.(type) {\n\tcase string:\n\t\tval, err := strconv.ParseInt(vv, 10, 16)\n\t\tif err != nil {\n\t\t\treturn def, err\n\t\t}\n\t\treturn int16(val), nil\n\tcase int:\n\t\treturn int16(vv), nil\n\tcase int8:\n\t\treturn int16(vv), nil\n\tcase int16:\n\t\treturn vv, nil\n\tcase int32:\n\t\treturn int16(vv), nil\n\tcase int64:\n\t\treturn int16(vv), nil\n\n\tcase uint:\n\t\treturn int16(vv), nil\n\tcase uint8:\n\t\treturn int16(vv), nil\n\tcase uint16:\n\t\treturn int16(vv), nil\n\tcase uint32:\n\t\treturn int16(vv), nil\n\tcase uint64:\n\t\treturn int16(vv), nil\n\t}\n\n\treturn def, e.notFound(int16Type)\n}\n\nvar int32Type = reflect.TypeOf(int32(0))\n\n// Int32Default returns the entry's value as int32.\n// If not found returns \"def\" and a non-nil error.\nfunc (e Entry) Int32Default(def int32) (int32, error) {\n\tv := e.ValueRaw\n\tif v == nil {\n\t\treturn def, e.notFound(int32Type)\n\t}\n\n\tswitch vv := v.(type) {\n\tcase string:\n\t\tval, err := strconv.ParseInt(vv, 10, 32)\n\t\tif err != nil {\n\t\t\treturn def, err\n\t\t}\n\t\treturn int32(val), nil\n\tcase int:\n\t\treturn int32(vv), nil\n\tcase int8:\n\t\treturn int32(vv), nil\n\tcase int16:\n\t\treturn int32(vv), nil\n\tcase int32:\n\t\treturn vv, nil\n\tcase int64:\n\t\treturn int32(vv), nil\n\t}\n\n\treturn def, e.notFound(int32Type)\n}\n\nvar int64Type = reflect.TypeOf(int64(0))\n\n// Int64Default returns the entry's value as int64.\n// If not found returns \"def\" and a non-nil error.\nfunc (e Entry) Int64Default(def int64) (int64, error) {\n\tv := e.ValueRaw\n\tif v == nil {\n\t\treturn def, e.notFound(int64Type)\n\t}\n\n\tswitch vv := v.(type) {\n\tcase string:\n\t\treturn strconv.ParseInt(vv, 10, 64)\n\tcase int64:\n\t\treturn vv, nil\n\tcase int32:\n\t\treturn int64(vv), nil\n\tcase int8:\n\t\treturn int64(vv), nil\n\tcase int:\n\t\treturn int64(vv), nil\n\n\tcase uint:\n\t\treturn int64(vv), nil\n\tcase uint8:\n\t\treturn int64(vv), nil\n\tcase uint16:\n\t\treturn int64(vv), nil\n\tcase uint32:\n\t\treturn int64(vv), nil\n\tcase uint64:\n\t\treturn int64(vv), nil\n\t}\n\n\treturn def, e.notFound(int64Type)\n}\n\nvar uintType = reflect.TypeOf(uint(0))\n\n// UintDefault returns the entry's value as uint.\n// If not found returns \"def\" and a non-nil error.\nfunc (e Entry) UintDefault(def uint) (uint, error) {\n\tv := e.ValueRaw\n\tif v == nil {\n\t\treturn def, e.notFound(uintType)\n\t}\n\n\tx64 := strconv.IntSize == 64\n\tvar maxValue uint64 = math.MaxUint32\n\tif x64 {\n\t\tmaxValue = math.MaxUint64\n\t}\n\n\tswitch vv := v.(type) {\n\tcase string:\n\t\tval, err := strconv.ParseUint(vv, 10, strconv.IntSize)\n\t\tif err != nil {\n\t\t\treturn def, err\n\t\t}\n\t\treturn uint(val), nil\n\tcase uint:\n\t\treturn vv, nil\n\tcase uint8:\n\t\treturn uint(vv), nil\n\tcase uint16:\n\t\treturn uint(vv), nil\n\tcase uint32:\n\t\treturn uint(vv), nil\n\tcase uint64:\n\t\tif vv > uint64(maxValue) {\n\t\t\treturn def, e.notFound(uintType)\n\t\t}\n\t\treturn uint(vv), nil\n\tcase int:\n\t\tif vv < 0 || vv > int(maxValue) {\n\t\t\treturn def, e.notFound(uintType)\n\t\t}\n\t\treturn uint(vv), nil\n\tcase int8:\n\t\treturn uint(vv), nil\n\tcase int16:\n\t\treturn uint(vv), nil\n\tcase int32:\n\t\treturn uint(vv), nil\n\tcase int64:\n\t\treturn uint(vv), nil\n\t}\n\n\treturn def, e.notFound(uintType)\n}\n\nvar uint8Type = reflect.TypeOf(uint8(0))\n\n// Uint8Default returns the entry's value as uint8.\n// If not found returns \"def\" and a non-nil error.\nfunc (e Entry) Uint8Default(def uint8) (uint8, error) {\n\tv := e.ValueRaw\n\tif v == nil {\n\t\treturn def, e.notFound(uint8Type)\n\t}\n\n\tswitch vv := v.(type) {\n\tcase string:\n\t\tval, err := strconv.ParseUint(vv, 10, 8)\n\t\tif err != nil {\n\t\t\treturn def, err\n\t\t}\n\t\treturn uint8(val), nil\n\tcase uint:\n\t\tif vv > math.MaxUint8 {\n\t\t\treturn def, e.notFound(uint8Type)\n\t\t}\n\t\treturn uint8(vv), nil\n\tcase uint8:\n\t\treturn vv, nil\n\tcase uint16:\n\t\tif vv > math.MaxUint8 {\n\t\t\treturn def, e.notFound(uint8Type)\n\t\t}\n\t\treturn uint8(vv), nil\n\tcase uint32:\n\t\tif vv > math.MaxUint8 {\n\t\t\treturn def, e.notFound(uint8Type)\n\t\t}\n\t\treturn uint8(vv), nil\n\tcase uint64:\n\t\tif vv > math.MaxUint8 {\n\t\t\treturn def, e.notFound(uint8Type)\n\t\t}\n\t\treturn uint8(vv), nil\n\tcase int:\n\t\tif vv < 0 || vv > math.MaxUint8 {\n\t\t\treturn def, e.notFound(uint8Type)\n\t\t}\n\t\treturn uint8(vv), nil\n\t}\n\n\treturn def, e.notFound(uint8Type)\n}\n\nvar uint16Type = reflect.TypeOf(uint16(0))\n\n// Uint16Default returns the entry's value as uint16.\n// If not found returns \"def\" and a non-nil error.\nfunc (e Entry) Uint16Default(def uint16) (uint16, error) {\n\tv := e.ValueRaw\n\tif v == nil {\n\t\treturn def, e.notFound(uint16Type)\n\t}\n\n\tswitch vv := v.(type) {\n\tcase string:\n\t\tval, err := strconv.ParseUint(vv, 10, 16)\n\t\tif err != nil {\n\t\t\treturn def, err\n\t\t}\n\t\treturn uint16(val), nil\n\tcase uint:\n\t\tif vv > math.MaxUint16 {\n\t\t\treturn def, e.notFound(uint16Type)\n\t\t}\n\t\treturn uint16(vv), nil\n\tcase uint8:\n\t\treturn uint16(vv), nil\n\tcase uint16:\n\t\treturn vv, nil\n\tcase uint32:\n\t\tif vv > math.MaxUint16 {\n\t\t\treturn def, e.notFound(uint16Type)\n\t\t}\n\t\treturn uint16(vv), nil\n\tcase uint64:\n\t\tif vv > math.MaxUint16 {\n\t\t\treturn def, e.notFound(uint16Type)\n\t\t}\n\t\treturn uint16(vv), nil\n\tcase int:\n\t\tif vv < 0 || vv > math.MaxUint16 {\n\t\t\treturn def, e.notFound(uint16Type)\n\t\t}\n\t\treturn uint16(vv), nil\n\t}\n\n\treturn def, e.notFound(uint16Type)\n}\n\nvar uint32Type = reflect.TypeOf(uint32(0))\n\n// Uint32Default returns the entry's value as uint32.\n// If not found returns \"def\" and a non-nil error.\nfunc (e Entry) Uint32Default(def uint32) (uint32, error) {\n\tv := e.ValueRaw\n\tif v == nil {\n\t\treturn def, e.notFound(uint32Type)\n\t}\n\n\tswitch vv := v.(type) {\n\tcase string:\n\t\tval, err := strconv.ParseUint(vv, 10, 32)\n\t\tif err != nil {\n\t\t\treturn def, err\n\t\t}\n\t\treturn uint32(val), nil\n\tcase uint:\n\t\tif vv > math.MaxUint32 {\n\t\t\treturn def, e.notFound(uint32Type)\n\t\t}\n\t\treturn uint32(vv), nil\n\tcase uint8:\n\t\treturn uint32(vv), nil\n\tcase uint16:\n\t\treturn uint32(vv), nil\n\tcase uint32:\n\t\treturn vv, nil\n\tcase uint64:\n\t\tif vv > math.MaxUint32 {\n\t\t\treturn def, e.notFound(uint32Type)\n\t\t}\n\t\treturn uint32(vv), nil\n\tcase int32:\n\t\treturn uint32(vv), nil\n\tcase int64:\n\t\tif vv < 0 || vv > math.MaxUint32 {\n\t\t\treturn def, e.notFound(uint32Type)\n\t\t}\n\t\treturn uint32(vv), nil\n\t}\n\n\treturn def, e.notFound(uint32Type)\n}\n\nvar uint64Type = reflect.TypeOf(uint64(0))\n\n// Uint64Default returns the entry's value as uint64.\n// If not found returns \"def\" and a non-nil error.\nfunc (e Entry) Uint64Default(def uint64) (uint64, error) {\n\tv := e.ValueRaw\n\tif v == nil {\n\t\treturn def, e.notFound(uint64Type)\n\t}\n\n\tswitch vv := v.(type) {\n\tcase string:\n\t\tval, err := strconv.ParseUint(vv, 10, 64)\n\t\tif err != nil {\n\t\t\treturn def, err\n\t\t}\n\t\treturn uint64(val), nil\n\tcase uint8:\n\t\treturn uint64(vv), nil\n\tcase uint16:\n\t\treturn uint64(vv), nil\n\tcase uint32:\n\t\treturn uint64(vv), nil\n\tcase uint64:\n\t\treturn vv, nil\n\tcase int64:\n\t\treturn uint64(vv), nil\n\tcase int:\n\t\treturn uint64(vv), nil\n\t}\n\n\treturn def, e.notFound(uint64Type)\n}\n\nvar float32Type = reflect.TypeOf(float32(0))\n\n// Float32Default returns the entry's value as float32.\n// If not found returns \"def\" and a non-nil error.\nfunc (e Entry) Float32Default(key string, def float32) (float32, error) {\n\tv := e.ValueRaw\n\tif v == nil {\n\t\treturn def, e.notFound(float32Type)\n\t}\n\n\tswitch vv := v.(type) {\n\tcase string:\n\t\tval, err := strconv.ParseFloat(vv, 32)\n\t\tif err != nil {\n\t\t\treturn def, err\n\t\t}\n\t\treturn float32(val), nil\n\tcase float32:\n\t\treturn vv, nil\n\tcase float64:\n\t\tif vv > math.MaxFloat32 {\n\t\t\treturn def, e.notFound(float32Type)\n\t\t}\n\t\treturn float32(vv), nil\n\tcase int:\n\t\treturn float32(vv), nil\n\t}\n\n\treturn def, e.notFound(float32Type)\n}\n\nvar float64Type = reflect.TypeOf(float64(0))\n\n// Float64Default returns the entry's value as float64.\n// If not found returns \"def\" and a non-nil error.\nfunc (e Entry) Float64Default(def float64) (float64, error) {\n\tv := e.ValueRaw\n\tif v == nil {\n\t\treturn def, e.notFound(float64Type)\n\t}\n\n\tswitch vv := v.(type) {\n\tcase string:\n\t\tval, err := strconv.ParseFloat(vv, 64)\n\t\tif err != nil {\n\t\t\treturn def, err\n\t\t}\n\t\treturn val, nil\n\tcase float32:\n\t\treturn float64(vv), nil\n\tcase float64:\n\t\treturn vv, nil\n\tcase int:\n\t\treturn float64(vv), nil\n\tcase int64:\n\t\treturn float64(vv), nil\n\tcase uint:\n\t\treturn float64(vv), nil\n\tcase uint64:\n\t\treturn float64(vv), nil\n\t}\n\n\treturn def, e.notFound(float64Type)\n}\n\nvar boolType = reflect.TypeOf(false)\n\n// BoolDefault returns the user's value as bool.\n// a string which is \"1\" or \"t\" or \"T\" or \"TRUE\" or \"true\" or \"True\"\n// or \"0\" or \"f\" or \"F\" or \"FALSE\" or \"false\" or \"False\".\n// Any other value returns an error.\n//\n// If not found returns \"def\" and a non-nil error.\nfunc (e Entry) BoolDefault(def bool) (bool, error) {\n\tv := e.ValueRaw\n\tif v == nil {\n\t\treturn def, e.notFound(boolType)\n\t}\n\n\tswitch vv := v.(type) {\n\tcase string:\n\t\tval, err := strconv.ParseBool(vv)\n\t\tif err != nil {\n\t\t\treturn def, err\n\t\t}\n\t\treturn val, nil\n\tcase bool:\n\t\treturn vv, nil\n\tcase int:\n\t\tif vv == 1 {\n\t\t\treturn true, nil\n\t\t}\n\t\treturn false, nil\n\t}\n\n\treturn def, e.notFound(boolType)\n}\n\nvar timeType = reflect.TypeOf(time.Time{})\n\n// TimeDefault returns the stored time.Time value based on its \"key\".\n// If does not exist or the stored key's value is not a time\n// it returns the \"def\" time value and a not found error.\nfunc (e Entry) TimeDefault(def time.Time) (time.Time, error) {\n\tv := e.ValueRaw\n\tif v == nil {\n\t\treturn def, e.notFound(timeType)\n\t}\n\n\tvv, ok := v.(time.Time)\n\tif !ok {\n\t\treturn def, nil\n\t}\n\n\treturn vv, nil\n}\n\nvar weekdayType = reflect.TypeOf(time.Weekday(0))\n\n// WeekdayDefault returns the stored time.Weekday value based on its \"key\".\n// If does not exist or the stored key's value is not a weekday\n// it returns the \"def\" weekday value and a not found error.\nfunc (e Entry) WeekdayDefault(def time.Weekday) (time.Weekday, error) {\n\tv := e.ValueRaw\n\tif v == nil {\n\t\treturn def, e.notFound(weekdayType)\n\t}\n\n\tvv, ok := v.(time.Weekday)\n\tif !ok {\n\t\treturn def, nil\n\t}\n\n\treturn vv, nil\n}\n\n// Value returns the value of the entry,\n// respects the immutable.\nfunc (e Entry) Value() any {\n\tif e.immutable {\n\t\t// take its value, no pointer even if set with a reference.\n\t\tvv := reflect.Indirect(reflect.ValueOf(e.ValueRaw))\n\n\t\t// return copy of that slice\n\t\tif vv.Type().Kind() == reflect.Slice {\n\t\t\tnewSlice := reflect.MakeSlice(vv.Type(), vv.Len(), vv.Cap())\n\t\t\treflect.Copy(newSlice, vv)\n\t\t\treturn newSlice.Interface()\n\t\t}\n\t\t// return a copy of that map\n\t\tif vv.Type().Kind() == reflect.Map {\n\t\t\tnewMap := reflect.MakeMap(vv.Type())\n\t\t\tfor _, k := range vv.MapKeys() {\n\t\t\t\tnewMap.SetMapIndex(k, vv.MapIndex(k))\n\t\t\t}\n\t\t\treturn newMap.Interface()\n\t\t}\n\t\t// if was *value it will return value{}.\n\t\treturn vv.Interface()\n\t}\n\treturn e.ValueRaw\n}\n\n// Save same as `Set`\n// However, if \"immutable\" is true then saves it as immutable (same as `SetImmutable`).\n//\n// Returns the entry and true if it was just inserted, meaning that\n// it will return the entry and a false boolean if the entry exists and it has been updated.\nfunc (r *Store) Save(key string, value any, immutable bool) (Entry, bool) {\n\targs := *r\n\tn := len(args)\n\n\t// replace if we can, else just return\n\tfor i := 0; i < n; i++ {\n\t\tkv := &args[i]\n\t\tif kv.Key == key {\n\t\t\tif immutable && kv.immutable {\n\t\t\t\t// if called by `SetImmutable`\n\t\t\t\t// then allow the update, maybe it's a slice that user wants to update by SetImmutable method,\n\t\t\t\t// we should allow this\n\t\t\t\tkv.ValueRaw = value\n\t\t\t\tkv.immutable = immutable\n\t\t\t} else if !kv.immutable {\n\t\t\t\t// if it was not immutable then user can alt it via `Set` and `SetImmutable`\n\t\t\t\tkv.ValueRaw = value\n\t\t\t\tkv.immutable = immutable\n\t\t\t}\n\t\t\t// else it was immutable and called by `Set` then disallow the update\n\t\t\treturn *kv, false\n\t\t}\n\t}\n\n\t// expand slice to add it\n\tc := cap(args)\n\tif c > n {\n\t\targs = args[:n+1]\n\t\tkv := &args[n]\n\t\tkv.Key = key\n\t\tkv.ValueRaw = value\n\t\tkv.immutable = immutable\n\t\t*r = args\n\t\treturn *kv, true\n\t}\n\n\t// add\n\tkv := Entry{\n\t\tKey:       key,\n\t\tValueRaw:  value,\n\t\timmutable: immutable,\n\t}\n\t*r = append(args, kv)\n\treturn kv, true\n}\n\n// Set saves a value to the key-value storage.\n// Returns the entry and true if it was just inserted, meaning that\n// it will return the entry and a false boolean if the entry exists and it has been updated.\n//\n// See `SetImmutable` and `Get`.\nfunc (r *Store) Set(key string, value any) (Entry, bool) {\n\treturn r.Save(key, value, false)\n}\n\n// SetImmutable saves a value to the key-value storage.\n// Unlike `Set`, the output value cannot be changed by the caller later on (when .Get OR .Set)\n//\n// An Immutable entry should be only changed with a `SetImmutable`, simple `Set` will not work\n// if the entry was immutable, for your own safety.\n//\n// Returns the entry and true if it was just inserted, meaning that\n// it will return the entry and a false boolean if the entry exists and it has been updated.\n//\n// Use it consistently, it's far slower than `Set`.\n// Read more about muttable and immutable go types: https://stackoverflow.com/a/8021081\nfunc (r *Store) SetImmutable(key string, value any) (Entry, bool) {\n\treturn r.Save(key, value, true)\n}\n\nvar emptyEntry Entry\n\n// GetEntry returns a pointer to the \"Entry\" found with the given \"key\"\n// if nothing found then it returns an empty Entry and false.\nfunc (r *Store) GetEntry(key string) (Entry, bool) {\n\targs := *r\n\tn := len(args)\n\tfor i := 0; i < n; i++ {\n\t\tif kv := args[i]; kv.Key == key {\n\t\t\treturn kv, true\n\t\t}\n\t}\n\n\treturn emptyEntry, false\n}\n\n// GetEntryAt returns the internal Entry of the memstore based on its index,\n// the stored index by the router.\n// If not found then it returns a zero Entry and false.\nfunc (r *Store) GetEntryAt(index int) (Entry, bool) {\n\targs := *r\n\tif len(args) > index {\n\t\treturn args[index], true\n\t}\n\treturn emptyEntry, false\n}\n\n// GetDefault returns the entry's value based on its key.\n// If not found returns \"def\".\n// This function checks for immutability as well, the rest don't.\nfunc (r *Store) GetDefault(key string, def any) any {\n\tv, ok := r.GetEntry(key)\n\tif !ok || v.ValueRaw == nil {\n\t\treturn def\n\t}\n\tvv := v.Value()\n\tif vv == nil {\n\t\treturn def\n\t}\n\treturn vv\n}\n\n// Exists is a small helper which reports whether a key exists.\n// It's not recommended to be used outside of templates.\n// Use Get or GetEntry instead which will give you back the entry value too,\n// so you don't have to loop again the key-value storage to get its value.\nfunc (r *Store) Exists(key string) bool {\n\t_, ok := r.GetEntry(key)\n\treturn ok\n}\n\n// Get returns the entry's value based on its key.\n// If not found returns nil.\nfunc (r *Store) Get(key string) any {\n\treturn r.GetDefault(key, nil)\n}\n\n// GetOrSet is like `GetDefault` but it accepts a function which is\n// fired and its result is used to `Set` if\n// the \"key\" was not found or its value is nil.\nfunc (r *Store) GetOrSet(key string, setFunc func() any) any {\n\tif v, ok := r.GetEntry(key); ok && v.ValueRaw != nil {\n\t\treturn v.Value()\n\t}\n\n\tvalue := setFunc()\n\tr.Set(key, value)\n\treturn value\n}\n\n// Visit accepts a visitor which will be filled\n// by the key-value objects.\nfunc (r *Store) Visit(visitor func(key string, value any)) {\n\targs := *r\n\tfor i, n := 0, len(args); i < n; i++ {\n\t\tkv := args[i]\n\t\tvisitor(kv.Key, kv.Value())\n\t}\n}\n\n// GetStringDefault returns the entry's value as string, based on its key.\n// If not found returns \"def\".\nfunc (r *Store) GetStringDefault(key string, def string) string {\n\tv, ok := r.GetEntry(key)\n\tif !ok {\n\t\treturn def\n\t}\n\n\treturn v.StringDefault(def)\n}\n\n// GetString returns the entry's value as string, based on its key.\nfunc (r *Store) GetString(key string) string {\n\treturn r.GetStringDefault(key, \"\")\n}\n\n// GetStringTrim returns the entry's string value without trailing spaces.\nfunc (r *Store) GetStringTrim(name string) string {\n\treturn strings.TrimSpace(r.GetString(name))\n}\n\n// GetInt returns the entry's value as int, based on its key.\n// If not found returns -1 and a non-nil error.\nfunc (r *Store) GetInt(key string) (int, error) {\n\tv, ok := r.GetEntry(key)\n\tif !ok {\n\t\treturn 0, v.notFound(intType)\n\t}\n\treturn v.IntDefault(-1)\n}\n\n// GetIntDefault returns the entry's value as int, based on its key.\n// If not found returns \"def\".\nfunc (r *Store) GetIntDefault(key string, def int) int {\n\tif v, err := r.GetInt(key); err == nil {\n\t\treturn v\n\t}\n\n\treturn def\n}\n\n// GetInt8 returns the entry's value as int8, based on its key.\n// If not found returns -1 and a non-nil error.\nfunc (r *Store) GetInt8(key string) (int8, error) {\n\tv, ok := r.GetEntry(key)\n\tif !ok {\n\t\treturn -1, v.notFound(int8Type)\n\t}\n\treturn v.Int8Default(-1)\n}\n\n// GetInt8Default returns the entry's value as int8, based on its key.\n// If not found returns \"def\".\nfunc (r *Store) GetInt8Default(key string, def int8) int8 {\n\tif v, err := r.GetInt8(key); err == nil {\n\t\treturn v\n\t}\n\n\treturn def\n}\n\n// GetInt16 returns the entry's value as int16, based on its key.\n// If not found returns -1 and a non-nil error.\nfunc (r *Store) GetInt16(key string) (int16, error) {\n\tv, ok := r.GetEntry(key)\n\tif !ok {\n\t\treturn -1, v.notFound(int16Type)\n\t}\n\treturn v.Int16Default(-1)\n}\n\n// GetInt16Default returns the entry's value as int16, based on its key.\n// If not found returns \"def\".\nfunc (r *Store) GetInt16Default(key string, def int16) int16 {\n\tif v, err := r.GetInt16(key); err == nil {\n\t\treturn v\n\t}\n\n\treturn def\n}\n\n// GetInt32 returns the entry's value as int32, based on its key.\n// If not found returns -1 and a non-nil error.\nfunc (r *Store) GetInt32(key string) (int32, error) {\n\tv, ok := r.GetEntry(key)\n\tif !ok {\n\t\treturn -1, v.notFound(int32Type)\n\t}\n\treturn v.Int32Default(-1)\n}\n\n// GetInt32Default returns the entry's value as int32, based on its key.\n// If not found returns \"def\".\nfunc (r *Store) GetInt32Default(key string, def int32) int32 {\n\tif v, err := r.GetInt32(key); err == nil {\n\t\treturn v\n\t}\n\n\treturn def\n}\n\n// GetInt64 returns the entry's value as int64, based on its key.\n// If not found returns -1 and a non-nil error.\nfunc (r *Store) GetInt64(key string) (int64, error) {\n\tv, ok := r.GetEntry(key)\n\tif !ok {\n\t\treturn -1, v.notFound(int64Type)\n\t}\n\treturn v.Int64Default(-1)\n}\n\n// GetInt64Default returns the entry's value as int64, based on its key.\n// If not found returns \"def\".\nfunc (r *Store) GetInt64Default(key string, def int64) int64 {\n\tif v, err := r.GetInt64(key); err == nil {\n\t\treturn v\n\t}\n\n\treturn def\n}\n\n// GetUint returns the entry's value as uint, based on its key.\n// If not found returns 0 and a non-nil error.\nfunc (r *Store) GetUint(key string) (uint, error) {\n\tv, ok := r.GetEntry(key)\n\tif !ok {\n\t\treturn 0, v.notFound(uintType)\n\t}\n\treturn v.UintDefault(0)\n}\n\n// GetUintDefault returns the entry's value as uint, based on its key.\n// If not found returns \"def\".\nfunc (r *Store) GetUintDefault(key string, def uint) uint {\n\tif v, err := r.GetUint(key); err == nil {\n\t\treturn v\n\t}\n\n\treturn def\n}\n\n// GetUint8 returns the entry's value as uint8, based on its key.\n// If not found returns 0 and a non-nil error.\nfunc (r *Store) GetUint8(key string) (uint8, error) {\n\tv, ok := r.GetEntry(key)\n\tif !ok {\n\t\treturn 0, v.notFound(uint8Type)\n\t}\n\treturn v.Uint8Default(0)\n}\n\n// GetUint8Default returns the entry's value as uint8, based on its key.\n// If not found returns \"def\".\nfunc (r *Store) GetUint8Default(key string, def uint8) uint8 {\n\tif v, err := r.GetUint8(key); err == nil {\n\t\treturn v\n\t}\n\n\treturn def\n}\n\n// GetUint16 returns the entry's value as uint16, based on its key.\n// If not found returns 0 and a non-nil error.\nfunc (r *Store) GetUint16(key string) (uint16, error) {\n\tv, ok := r.GetEntry(key)\n\tif !ok {\n\t\treturn 0, v.notFound(uint16Type)\n\t}\n\treturn v.Uint16Default(0)\n}\n\n// GetUint16Default returns the entry's value as uint16, based on its key.\n// If not found returns \"def\".\nfunc (r *Store) GetUint16Default(key string, def uint16) uint16 {\n\tif v, err := r.GetUint16(key); err == nil {\n\t\treturn v\n\t}\n\n\treturn def\n}\n\n// GetUint32 returns the entry's value as uint32, based on its key.\n// If not found returns 0 and a non-nil error.\nfunc (r *Store) GetUint32(key string) (uint32, error) {\n\tv, ok := r.GetEntry(key)\n\tif !ok {\n\t\treturn 0, v.notFound(uint32Type)\n\t}\n\treturn v.Uint32Default(0)\n}\n\n// GetUint32Default returns the entry's value as uint32, based on its key.\n// If not found returns \"def\".\nfunc (r *Store) GetUint32Default(key string, def uint32) uint32 {\n\tif v, err := r.GetUint32(key); err == nil {\n\t\treturn v\n\t}\n\n\treturn def\n}\n\n// GetUint64 returns the entry's value as uint64, based on its key.\n// If not found returns 0 and a non-nil error.\nfunc (r *Store) GetUint64(key string) (uint64, error) {\n\tv, ok := r.GetEntry(key)\n\tif !ok {\n\t\treturn 0, v.notFound(uint64Type)\n\t}\n\treturn v.Uint64Default(0)\n}\n\n// GetUint64Default returns the entry's value as uint64, based on its key.\n// If not found returns \"def\".\nfunc (r *Store) GetUint64Default(key string, def uint64) uint64 {\n\tif v, err := r.GetUint64(key); err == nil {\n\t\treturn v\n\t}\n\n\treturn def\n}\n\n// GetFloat64 returns the entry's value as float64, based on its key.\n// If not found returns -1 and a non nil error.\nfunc (r *Store) GetFloat64(key string) (float64, error) {\n\tv, ok := r.GetEntry(key)\n\tif !ok {\n\t\treturn -1, v.notFound(float64Type)\n\t}\n\treturn v.Float64Default(-1)\n}\n\n// GetFloat64Default returns the entry's value as float64, based on its key.\n// If not found returns \"def\".\nfunc (r *Store) GetFloat64Default(key string, def float64) float64 {\n\tif v, err := r.GetFloat64(key); err == nil {\n\t\treturn v\n\t}\n\n\treturn def\n}\n\n// GetBool returns the user's value as bool, based on its key.\n// a string which is \"1\" or \"t\" or \"T\" or \"TRUE\" or \"true\" or \"True\"\n// or \"0\" or \"f\" or \"F\" or \"FALSE\" or \"false\" or \"False\".\n// Any other value returns an error.\n//\n// If not found returns false and a non-nil error.\nfunc (r *Store) GetBool(key string) (bool, error) {\n\tv, ok := r.GetEntry(key)\n\tif !ok {\n\t\treturn false, v.notFound(boolType)\n\t}\n\n\treturn v.BoolDefault(false)\n}\n\n// GetBoolDefault returns the user's value as bool, based on its key.\n// a string which is \"1\" or \"t\" or \"T\" or \"TRUE\" or \"true\" or \"True\"\n// or \"0\" or \"f\" or \"F\" or \"FALSE\" or \"false\" or \"False\".\n//\n// If not found returns \"def\".\nfunc (r *Store) GetBoolDefault(key string, def bool) bool {\n\tif v, err := r.GetBool(key); err == nil {\n\t\treturn v\n\t}\n\n\treturn def\n}\n\nvar zeroTime = time.Time{}\n\n// GetTime returns the stored time.Time value based on its \"key\".\n// If does not exist or the stored key's value is not a time\n// it returns a zero time value and a not found error.\nfunc (r *Store) GetTime(key string) (time.Time, error) {\n\tv, ok := r.GetEntry(key)\n\tif !ok {\n\t\treturn zeroTime, v.notFound(timeType)\n\t}\n\n\treturn v.TimeDefault(zeroTime)\n}\n\nconst simpleDateLayout = \"2006/01/02\"\n\n// SimpleDate calls GetTime and formats the time as \"yyyyy/mm/dd\".\n// It returns an empty string if the key does not exist or the\n// stored value on \"key\" is not a time.Time type.\nfunc (r *Store) SimpleDate(key string) string {\n\tv, ok := r.GetEntry(key)\n\tif !ok {\n\t\treturn \"\"\n\t}\n\n\ttt, err := v.TimeDefault(zeroTime)\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\n\treturn tt.Format(simpleDateLayout)\n}\n\nconst zeroWeekday = time.Sunday\n\n// GetWeekday returns the stored time.Weekday value based on its \"key\".\n// If does not exist or the stored key's value is not a weekday\n// it returns the time.Sunday value and a not found error.\nfunc (r *Store) GetWeekday(key string) (time.Weekday, error) {\n\tv, ok := r.GetEntry(key)\n\tif !ok {\n\t\treturn zeroWeekday, v.notFound(timeType)\n\t}\n\n\treturn v.WeekdayDefault(zeroWeekday)\n}\n\n// Remove deletes an entry linked to that \"key\",\n// returns true if an entry is actually removed.\nfunc (r *Store) Remove(key string) bool {\n\targs := *r\n\tn := len(args)\n\tfor i := 0; i < n; i++ {\n\t\tkv := &args[i]\n\t\tif kv.Key == key {\n\t\t\t// we found the index,\n\t\t\t// let's remove the item by appending to the temp and\n\t\t\t// after set the pointer of the slice to this temp args\n\t\t\targs = append(args[:i], args[i+1:]...)\n\t\t\t*r = args\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// Reset clears all the request entries.\nfunc (r *Store) Reset() {\n\t*r = (*r)[0:0]\n}\n\n// Len returns the full length of the entries.\nfunc (r *Store) Len() int {\n\targs := *r\n\treturn len(args)\n}\n\n// Serialize returns the byte representation of the current Store.\nfunc (r Store) Serialize() []byte { // note: no pointer here, ignore linters if shows up.\n\tb, _ := GobSerialize(r)\n\treturn b\n}\n"
  },
  {
    "path": "core/memstore/memstore_test.go",
    "content": "// white-box testing\npackage memstore\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"testing\"\n)\n\ntype myTestObject struct {\n\tName string `json:\"name\"`\n}\n\nfunc TestMuttable(t *testing.T) {\n\tvar p Store\n\n\t// slice\n\tp.Set(\"slice\", []myTestObject{{\"value 1\"}, {\"value 2\"}})\n\tv := p.Get(\"slice\").([]myTestObject)\n\tv[0].Name = \"modified\"\n\n\tvv := p.Get(\"slice\").([]myTestObject)\n\tif vv[0].Name != \"modified\" {\n\t\tt.Fatalf(\"expected slice to be muttable but caller was not able to change its value\")\n\t}\n\n\t// map\n\n\tp.Set(\"map\", map[string]myTestObject{\"key 1\": {\"value 1\"}, \"key 2\": {\"value 2\"}})\n\tvMap := p.Get(\"map\").(map[string]myTestObject)\n\tvMap[\"key 1\"] = myTestObject{\"modified\"}\n\n\tvvMap := p.Get(\"map\").(map[string]myTestObject)\n\tif vvMap[\"key 1\"].Name != \"modified\" {\n\t\tt.Fatalf(\"expected map to be muttable but caller was not able to change its value\")\n\t}\n\n\t// object pointer of a value, it can change like maps or slices and arrays.\n\tp.Set(\"objp\", &myTestObject{\"value\"})\n\t// we expect pointer here, as we set it.\n\tvObjP := p.Get(\"objp\").(*myTestObject)\n\n\tvObjP.Name = \"modified\"\n\n\tvvObjP := p.Get(\"objp\").(*myTestObject)\n\tif vvObjP.Name != \"modified\" {\n\t\tt.Fatalf(\"expected objp to be muttable but caller was able to change its value\")\n\t}\n}\n\nfunc TestImmutable(t *testing.T) {\n\tvar p Store\n\n\t// slice\n\tp.SetImmutable(\"slice\", []myTestObject{{\"value 1\"}, {\"value 2\"}})\n\tv := p.Get(\"slice\").([]myTestObject)\n\tv[0].Name = \"modified\"\n\n\tvv := p.Get(\"slice\").([]myTestObject)\n\tif vv[0].Name == \"modified\" {\n\t\tt.Fatalf(\"expected slice to be immutable but caller was able to change its value\")\n\t}\n\n\t// map\n\tp.SetImmutable(\"map\", map[string]myTestObject{\"key 1\": {\"value 1\"}, \"key 2\": {\"value 2\"}})\n\tvMap := p.Get(\"map\").(map[string]myTestObject)\n\tvMap[\"key 1\"] = myTestObject{\"modified\"}\n\n\tvvMap := p.Get(\"map\").(map[string]myTestObject)\n\tif vvMap[\"key 1\"].Name == \"modified\" {\n\t\tt.Fatalf(\"expected map to be immutable but caller was able to change its value\")\n\t}\n\n\t// object value, it's immutable at all cases.\n\tp.SetImmutable(\"obj\", myTestObject{\"value\"})\n\tvObj := p.Get(\"obj\").(myTestObject)\n\tvObj.Name = \"modified\"\n\n\tvvObj := p.Get(\"obj\").(myTestObject)\n\tif vvObj.Name == \"modified\" {\n\t\tt.Fatalf(\"expected obj to be immutable but caller was able to change its value\")\n\t}\n\n\t// object pointer of a value, it's immutable at all cases.\n\tp.SetImmutable(\"objp\", &myTestObject{\"value\"})\n\t// we expect no pointer here if SetImmutable.\n\t// so it can't be changed by-design\n\tvObjP := p.Get(\"objp\").(myTestObject)\n\n\tvObjP.Name = \"modified\"\n\n\tvvObjP := p.Get(\"objp\").(myTestObject)\n\tif vvObjP.Name == \"modified\" {\n\t\tt.Fatalf(\"expected objp to be immutable but caller was able to change its value\")\n\t}\n}\n\nfunc TestImmutableSetOnlyWithSetImmutable(t *testing.T) {\n\tvar p Store\n\n\tp.SetImmutable(\"objp\", &myTestObject{\"value\"})\n\n\tp.Set(\"objp\", &myTestObject{\"modified\"})\n\tvObjP := p.Get(\"objp\").(myTestObject)\n\tif vObjP.Name == \"modified\" {\n\t\tt.Fatalf(\"caller should not be able to change the immutable entry with a simple `Set`\")\n\t}\n\n\tp.SetImmutable(\"objp\", &myTestObject{\"value with SetImmutable\"})\n\tvvObjP := p.Get(\"objp\").(myTestObject)\n\tif vvObjP.Name != \"value with SetImmutable\" {\n\t\tt.Fatalf(\"caller should be able to change the immutable entry with a `SetImmutable`\")\n\t}\n}\n\nfunc TestGetInt64Default(t *testing.T) {\n\tvar p Store\n\n\tp.Set(\"a uint16\", uint16(2))\n\tif v := p.GetInt64Default(\"a uint16\", 0); v != 2 {\n\t\tt.Fatalf(\"unexpected value of %d\", v)\n\t}\n}\n\nfunc TestJSON(t *testing.T) {\n\tvar p Store\n\n\tp.Set(\"key1\", \"value1\")\n\tp.Set(\"key2\", 2)\n\tp.Set(\"key3\", myTestObject{Name: \"makis\"})\n\n\tb, err := json.Marshal(p)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\texpectedJSON := []byte(`[{\"key\":\"key1\",\"value\":\"value1\"},{\"key\":\"key2\",\"value\":2},{\"key\":\"key3\",\"value\":{\"name\":\"makis\"}}]`)\n\n\tif !bytes.Equal(b, expectedJSON) {\n\t\tt.Fatalf(\"expected: %s but got: %s\", string(expectedJSON), string(b))\n\t}\n\n\tvar newStore Store\n\tif err = json.Unmarshal(b, &newStore); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tfor i, v := range newStore {\n\t\texpected, got := p.Get(v.Key), v.ValueRaw\n\n\t\tif ex, g := fmt.Sprintf(\"%v\", expected), fmt.Sprintf(\"%v\", got); ex != g {\n\t\t\tif _, isMap := got.(map[string]any); isMap {\n\t\t\t\t// was struct but converted into map (as expected).\n\t\t\t\tb1, _ := json.Marshal(expected)\n\t\t\t\tb2, _ := json.Marshal(got)\n\n\t\t\t\tif !bytes.Equal(b1, b2) {\n\t\t\t\t\tt.Fatalf(\"[%d] JSON expected: %s but got: %s\", i, string(b1), string(b2))\n\t\t\t\t}\n\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tt.Fatalf(\"[%d] expected: %s but got: %s\", i, ex, g)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "core/netutil/addr.go",
    "content": "package netutil\n\nimport (\n\t\"os\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n)\n\nvar (\n\t// LoopbackRegex the regex if matched a host:port is a loopback.\n\tLoopbackRegex    = regexp.MustCompile(`^localhost$|^127(?:\\.[0-9]+){0,2}\\.[0-9]+$|^(?:0*\\:)*?:?0*1$`)\n\tloopbackSubRegex = regexp.MustCompile(`^127(?:\\.[0-9]+){0,2}\\.[0-9]+$|^(?:0*\\:)*?:?0*1$`)\n\tmachineHostname  string\n)\n\nfunc init() {\n\tmachineHostname, _ = os.Hostname()\n}\n\n// IsLoopbackSubdomain checks if a string is a subdomain or a hostname.\nvar IsLoopbackSubdomain = func(s string) bool {\n\tif strings.HasPrefix(s, \"127.0.0.1:\") || s == \"127.0.0.1\" ||\n\t\tstrings.HasPrefix(s, \"0.0.0.0:\") || s == \"0.0.0.0\" /* let's resolve that without regex (see below)*/ {\n\t\treturn true\n\t}\n\n\tvalid := loopbackSubRegex.MatchString(s)\n\tif !valid { // if regex failed to match it, then try with the pc's name.\n\t\tif !strings.Contains(machineHostname, \".\") { // if machine name's is not a loopback by itself\n\t\t\tvalid = s == machineHostname\n\t\t}\n\t}\n\treturn valid\n}\n\n// GetLoopbackSubdomain returns the part of the loopback subdomain.\nfunc GetLoopbackSubdomain(s string) string {\n\tif strings.HasPrefix(s, \"127.0.0.1:\") || s == \"127.0.0.1\" ||\n\t\tstrings.HasPrefix(s, \"0.0.0.0:\") || s == \"0.0.0.0\" {\n\t\treturn s\n\t}\n\n\treturn loopbackSubRegex.FindString(s)\n}\n\n// IsLoopbackHost tries to catch the local addresses when a developer\n// navigates to a subdomain that its hostname differs from Application.Configuration.VHost.\n// Developer may want to override this function to return always false\n// in order to not allow different hostname from Application.Configuration.VHost in local environment (remote is not reached).\nvar IsLoopbackHost = func(requestHost string) bool {\n\t// this func will be called if we have a subdomain actually, not otherwise, so we are\n\t// safe to do some hacks.\n\n\t// if subdomain.127.0.0.1:8080/path, we need to compare the 127.0.0.1\n\t// if subdomain.localhost:8080/mypath, we need to compare the localhost\n\t// if subdomain.127.0.0.1/mypath, we need to compare the 127.0.0.1\n\t// if subdomain.127.0.0.1, we need to compare the 127.0.0.1\n\n\t// find the first index of [:]8080 or [/]mypath or nothing(root with loopback address like 127.0.0.1)\n\t// remember: we are not looking for .com or these things, if is up and running then the developer\n\t// would probably not want to reach the server with different Application.Configuration.VHost than\n\t// he/she declared.\n\tportOrPathIdx := strings.LastIndexByte(requestHost, ':')\n\n\tif portOrPathIdx == 0 { //  0.0.0.0:[...]/localhost:[...]/127.0.0.1:[...]/ipv6 local...\n\t\treturn true\n\t}\n\t// this will not catch ipv6 loopbacks like subdomain.0000:0:0000::01.1:8080\n\t// but, again, is for developers only, is hard to try to navigate with something like this,\n\t// and if that happened, I provide a way to override the whole \"algorithm\" to a custom one via \"IsLoopbackHost\".\n\tif portOrPathIdx == -1 {\n\t\tportOrPathIdx = strings.LastIndexByte(requestHost, '/')\n\t\tif portOrPathIdx == -1 {\n\t\t\tportOrPathIdx = len(requestHost) // if not port or / then it should be something like subodmain.127.0.0.1\n\t\t}\n\t}\n\n\t// remove the left part of subdomain[.]<- and the right part of ->[:]8080/[/]mypath\n\t// so result should be 127.0.0.1/localhost/0.0.0.0 or any ip\n\tsubdomainFinishIdx := strings.IndexByte(requestHost, '.') + 1\n\tif l := len(requestHost); l <= subdomainFinishIdx || l < portOrPathIdx {\n\t\treturn false // for any case to not panic here.\n\t}\n\n\thostname := requestHost[subdomainFinishIdx:portOrPathIdx]\n\tif hostname == \"\" {\n\t\treturn false\n\t}\n\t// we use regex here to catch all posibilities, we compiled the regex at init func\n\t// so it shouldn't hurt so much, but we don't care a lot because it's a special case here\n\t// because this function will be called only if developer him/herself can reach the server\n\t// with a loopback/local address, so we are totally safe.\n\tvalid := LoopbackRegex.MatchString(hostname)\n\tif !valid { // if regex failed to match it, then try with the pc's name.\n\t\tvalid = hostname == machineHostname\n\t}\n\treturn valid\n}\n\n/*\nfunc isLoopbackHostGoVersion(host string) bool {\n\tip := net.ParseIP(host)\n\tif ip != nil {\n\t\treturn ip.IsLoopback()\n\t}\n\n\t// Host is not an ip, perform lookup.\n\taddrs, err := net.LookupHost(host)\n\tif err != nil {\n\t\treturn false\n\t}\n\n\tfor _, addr := range addrs {\n\t\tif !net.ParseIP(addr).IsLoopback() {\n\t\t\treturn false\n\t\t}\n\t}\n\n\treturn true\n}\n*/\n\nconst (\n\t// defaultServerHostname returns the default hostname which is \"localhost\"\n\tdefaultServerHostname = \"localhost\"\n)\n\n// ResolveAddr tries to convert a given string to an address which is compatible with net.Listener and server\nfunc ResolveAddr(addr string) string {\n\t// check if addr has :port, if not do it +:80 ,we need the hostname for many cases\n\ta := addr\n\tif a == \"\" {\n\t\t// check for os environments\n\t\tif oshost := os.Getenv(\"ADDR\"); oshost != \"\" {\n\t\t\ta = oshost\n\t\t} else if oshost := os.Getenv(\"HOST\"); oshost != \"\" {\n\t\t\ta = oshost\n\t\t} else if oshost := os.Getenv(\"HOSTNAME\"); oshost != \"\" {\n\t\t\ta = oshost\n\t\t\t// check for port also here\n\t\t\tif osport := os.Getenv(\"PORT\"); osport != \"\" {\n\t\t\t\ta += \":\" + osport\n\t\t\t}\n\t\t} else if osport := os.Getenv(\"PORT\"); osport != \"\" {\n\t\t\ta = \":\" + osport\n\t\t} else {\n\t\t\ta = \":http\"\n\t\t}\n\t}\n\tif portIdx := strings.IndexByte(a, ':'); portIdx == 0 {\n\t\tif a[portIdx:] == \":https\" {\n\t\t\ta = defaultServerHostname + \":443\"\n\t\t} else {\n\t\t\t// if contains only :port\t,then the : is the first letter, so we dont have set a hostname, lets set it\n\t\t\ta = defaultServerHostname + a\n\t\t}\n\t}\n\n\treturn a\n}\n\n// ResolveHostname receives an addr of form host[:port] and returns the hostname part of it\n// ex: localhost:8080 will return the `localhost`, mydomain.com:8080 will return the 'mydomain'\nfunc ResolveHostname(addr string) string {\n\tif idx := strings.IndexByte(addr, ':'); idx == 0 {\n\t\t// only port, then return the localhost hostname\n\t\treturn \"localhost\"\n\t} else if idx > 0 {\n\t\treturn addr[0:idx]\n\t}\n\t// it's already hostname\n\treturn addr\n}\n\n// ResolveVHost tries to get the hostname if port is no needed for Addr's usage.\n// Addr is being used inside router->subdomains\n// and inside {{ url }} template funcs.\n// It should be the same as \"browser's\"\n// usually they removing :80 or :443.\nfunc ResolveVHost(addr string) string {\n\tif addr == \":https\" || addr == \":http\" {\n\t\treturn \"localhost\"\n\t}\n\n\tif idx := strings.IndexByte(addr, ':'); idx == 0 {\n\t\t// only port, then return the 0.0.0.0:PORT\n\t\treturn /* \"0.0.0.0\" */ \"localhost\" + addr[idx:]\n\t} else if idx > 0 { // if 0.0.0.0:80 let's just convert it to localhost.\n\t\tif addr[0:idx] == \"0.0.0.0\" {\n\t\t\tif addr[idx:] == \":80\" {\n\t\t\t\treturn \"localhost\"\n\t\t\t}\n\t\t\treturn \"localhost\" + addr[idx:]\n\t\t}\n\t}\n\n\t// with ':' in order to not replace the ipv6 loopback addresses\n\t// addr = strings.Replace(addr, \"0.0.0.0:\", \"localhost:\", 1)\n\t// some users are confusing from the log output ^.\n\n\tport := ResolvePort(addr)\n\tif port == 80 || port == 443 {\n\t\treturn ResolveHostname(addr)\n\t}\n\n\treturn addr\n}\n\nconst (\n\t// SchemeHTTPS the \"https\" url scheme.\n\tSchemeHTTPS = \"https\"\n\t// SchemeHTTP the \"http\" url scheme.\n\tSchemeHTTP = \"http\"\n)\n\n// ResolvePort receives an addr of form host[:port] and returns the port part of it\n// ex: localhost:8080 will return the `8080`, mydomain.com will return the '80'\nfunc ResolvePort(addr string) int {\n\tif portIdx := strings.IndexByte(addr, ':'); portIdx != -1 {\n\t\tafP := addr[portIdx+1:]\n\t\tp, err := strconv.Atoi(afP)\n\t\tif err == nil {\n\t\t\treturn p\n\t\t} else if afP == SchemeHTTPS { // it's not number, check if it's :https\n\t\t\treturn 443\n\t\t}\n\t}\n\treturn 80\n}\n\n// ResolveScheme returns \"https\" if \"isTLS\" receiver is true,\n// otherwise \"http\".\nfunc ResolveScheme(isTLS bool) string {\n\tif isTLS {\n\t\treturn SchemeHTTPS\n\t}\n\n\treturn SchemeHTTP\n}\n\n// ResolveSchemeFromVHost returns the scheme based on the \"vhost\".\nfunc ResolveSchemeFromVHost(vhost string) string {\n\t// pure check\n\tisTLS := strings.HasPrefix(vhost, SchemeHTTPS) || ResolvePort(vhost) == 443\n\treturn ResolveScheme(isTLS)\n}\n\n// ResolveURL takes the scheme and an address\n// and returns its URL, pure implementation but it does the job.\nfunc ResolveURL(scheme string, addr string) string {\n\thost := ResolveVHost(addr)\n\treturn scheme + \"://\" + host\n}\n"
  },
  {
    "path": "core/netutil/addr_test.go",
    "content": "package netutil\n\nimport (\n\t\"testing\"\n)\n\nfunc TestIsLoopbackHost(t *testing.T) {\n\ttests := []struct {\n\t\thost  string\n\t\tvalid bool\n\t}{\n\t\t{\"subdomain.127.0.0.1:8080\", true},\n\t\t{\"subdomain.127.0.0.1\", true},\n\t\t{\"subdomain.localhost:8080\", true},\n\t\t{\"subdomain.localhost\", true},\n\t\t{\"subdomain.127.0000.0000.1:8080\", true},\n\t\t{\"subdomain.127.0000.0000.1\", true},\n\t\t{\"subdomain.127.255.255.254:8080\", true},\n\t\t{\"subdomain.127.255.255.254\", true},\n\n\t\t{\"subdomain.0000:0:0000::01.1:8080\", false},\n\t\t{\"subdomain.0000:0:0000::01\", false},\n\t\t{\"subdomain.0000:0:0000::01.1:8080\", false},\n\t\t{\"subdomain.0000:0:0000::01\", false},\n\t\t{\"subdomain.0000:0000:0000:0000:0000:0000:0000:0001:8080\", true},\n\t\t{\"subdomain.0000:0000:0000:0000:0000:0000:0000:0001\", false},\n\n\t\t{\"subdomain.example:8080\", false},\n\t\t{\"subdomain.example\", false},\n\t\t{\"subdomain.example.com:8080\", false},\n\t\t{\"subdomain.example.com\", false},\n\t\t{\"subdomain.com\", false},\n\t\t{\"subdomain\", false},\n\t\t{\".subdomain\", false},\n\t\t{\"127.0.0.1.com\", false},\n\t}\n\n\tfor i, tt := range tests {\n\t\tif expected, got := tt.valid, IsLoopbackHost(tt.host); expected != got {\n\t\t\tt.Fatalf(\"[%d] expected %t but got %t for %s\", i, expected, got, tt.host)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "core/netutil/client.go",
    "content": "package netutil\n\nimport (\n\t\"net\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/kataras/golog\"\n)\n\n// Client returns a new http.Client using\n// the \"timeout\" for open connection.\nfunc Client(timeout time.Duration) *http.Client {\n\ttransport := http.Transport{\n\t\tDial: func(network string, addr string) (net.Conn, error) {\n\t\t\tconn, err := net.DialTimeout(network, addr, timeout)\n\t\t\tif err != nil {\n\t\t\t\tgolog.Debugf(\"%v\", err)\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\treturn conn, err\n\t\t},\n\t}\n\n\tclient := &http.Client{\n\t\tTransport: &transport,\n\t}\n\n\treturn client\n}\n"
  },
  {
    "path": "core/netutil/ip.go",
    "content": "package netutil\n\nimport (\n\t\"bytes\"\n\t\"net\"\n\t\"strings\"\n)\n\n/* Based on:\nhttps://husobee.github.io/golang/ip-address/2015/12/17/remote-ip-go.html requested at:\nhttps://github.com/kataras/iris/issues/1453\n*/\n\n// IPRange is a structure that holds the start and end of a range of IP Addresses.\ntype IPRange struct {\n\tStart string `ini:\"start\" json:\"start\" yaml:\"Start\" toml:\"Start\"`\n\tEnd   string `ini:\"end\" json:\"end\" yaml:\"End\" toml:\"End\"`\n}\n\n// IPInRange reports whether a given IP Address is within a range given.\nfunc IPInRange(r IPRange, ipAddress net.IP) bool {\n\treturn bytes.Compare(ipAddress, net.ParseIP(r.Start)) >= 0 && bytes.Compare(ipAddress, net.ParseIP(r.End)) <= 0\n}\n\n// IPIsPrivateSubnet reports whether this \"ipAddress\" is in a private subnet.\nfunc IPIsPrivateSubnet(ipAddress net.IP, privateRanges []IPRange) bool {\n\t// IPv4 for now.\n\tif ipCheck := ipAddress.To4(); ipCheck != nil {\n\t\t// iterate over all our ranges.\n\t\tfor _, r := range privateRanges {\n\t\t\t// check if this ip is in a private range.\n\t\t\tif IPInRange(r, ipAddress) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t}\n\treturn false\n}\n\n// GetIPAddress returns a valid public IP Address from a collection of IP Addresses\n// and a range of private subnets.\n//\n// Reports whether a valid IP was found.\nfunc GetIPAddress(ipAddresses []string, privateRanges []IPRange) (string, bool) {\n\t// march from right to left until we get a public address\n\t// that will be the address right before our proxy.\n\tfor i := len(ipAddresses) - 1; i >= 0; i-- {\n\t\tip := strings.TrimSpace(ipAddresses[i])\n\t\trealIP := net.ParseIP(ip)\n\t\tif !realIP.IsGlobalUnicast() || IPIsPrivateSubnet(realIP, privateRanges) {\n\t\t\t// bad address, go to next\n\t\t\tcontinue\n\t\t}\n\t\treturn ip, true\n\n\t}\n\n\treturn \"\", false\n}\n"
  },
  {
    "path": "core/netutil/ip_test.go",
    "content": "package netutil\n\nimport (\n\t\"testing\"\n)\n\nfunc TestIP(t *testing.T) {\n\tprivateRanges := []IPRange{\n\t\t{\n\t\t\tStart: \"10.0.0.0\",\n\t\t\tEnd:   \"10.255.255.255\",\n\t\t},\n\t\t{\n\t\t\tStart: \"100.64.0.0\",\n\t\t\tEnd:   \"100.127.255.255\",\n\t\t},\n\t\t{\n\t\t\tStart: \"172.16.0.0\",\n\t\t\tEnd:   \"172.31.255.255\",\n\t\t},\n\t\t{\n\t\t\tStart: \"192.0.0.0\",\n\t\t\tEnd:   \"192.0.0.255\",\n\t\t},\n\t\t{\n\t\t\tStart: \"192.168.0.0\",\n\t\t\tEnd:   \"192.168.255.255\",\n\t\t},\n\t\t{\n\t\t\tStart: \"198.18.0.0\",\n\t\t\tEnd:   \"198.19.255.255\",\n\t\t},\n\t}\n\n\taddresses := []string{\n\t\t\"201.37.138.59\",\n\t\t\"159.117.3.153\",\n\t\t\"166.192.97.84\",\n\t\t\"225.181.213.210\",\n\t\t\"124.50.84.134\",\n\t\t\"87.53.250.102\",\n\t\t\"106.79.33.62\",\n\t\t\"242.120.17.144\",\n\t\t\"131.179.101.254\",\n\t\t\"103.11.11.174\",\n\t\t\"115.97.0.114\",\n\t\t\"219.202.120.251\",\n\t\t\"37.72.123.120\",\n\t\t\"154.94.78.101\",\n\t\t\"126.105.144.250\",\n\t}\n\n\tgot, ok := GetIPAddress(addresses, privateRanges)\n\tif !ok {\n\t\tt.Logf(\"expected addr to be matched\")\n\t}\n\n\tif expected := \"126.105.144.250\"; expected != got {\n\t\tt.Logf(\"expected addr to be found: %s but got: %s\", expected, got)\n\t}\n\n\taddresses = []string{\n\t\t\"10.10.233.1\",\n\t\t\"126.105.144.250\",\n\t\t\"192.168.99.33\",\n\t\t\"172.18.22.23\",\n\t\t\"10.0.0.0\",\n\t\t\"10.255.255.255\",\n\t}\n\n\tgot, ok = GetIPAddress(addresses, privateRanges)\n\tif !ok {\n\t\tt.Logf(\"expected addr to be matched\")\n\t}\n\n\tif expected := \"126.105.144.250\"; expected != got {\n\t\tt.Logf(\"expected addr to be found: %s but got: %s\", expected, got)\n\t}\n\n\taddresses = []string{\n\t\t\"10.0.0.0\",\n\t\t\"10.10.233.1\",\n\t\t\"192.168.99.33\",\n\t\t\"172.18.22.23\",\n\t}\n\n\tgot, ok = GetIPAddress(addresses, privateRanges)\n\tif ok {\n\t\tt.Logf(\"expected addr to not be matched\")\n\t}\n}\n"
  },
  {
    "path": "core/netutil/server.go",
    "content": "package netutil\n\nimport (\n\t\"net/http\"\n)\n\n// used on host/supervisor/task and router/path\n\n// IsTLS returns true if the \"srv\" contains any certificates\n// or a get certificate function, meaning that is secure.\nfunc IsTLS(srv *http.Server) bool {\n\tif cfg := srv.TLSConfig; cfg != nil &&\n\t\t(len(cfg.Certificates) > 0 || cfg.GetCertificate != nil) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// ResolveSchemeFromServer tries to resolve a url scheme\n// based on the server's configuration.\n// Returns \"https\" on secure server,\n// otherwise \"http\".\nfunc ResolveSchemeFromServer(srv *http.Server) string {\n\treturn ResolveScheme(IsTLS(srv))\n}\n\n// ResolveURLFromServer returns the scheme+host from a server.\nfunc ResolveURLFromServer(srv *http.Server) string {\n\tscheme := ResolveSchemeFromServer(srv)\n\thost := ResolveVHost(srv.Addr)\n\treturn scheme + \"://\" + host\n}\n"
  },
  {
    "path": "core/netutil/tcp.go",
    "content": "package netutil\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"os\"\n\t\"strings\"\n\t\"time\"\n\n\t\"golang.org/x/crypto/acme/autocert\"\n)\n\n// tcpKeepAliveListener sets TCP keep-alive timeouts on accepted\n// connections. It's used by Run, ListenAndServe and ListenAndServeTLS so\n// dead TCP connections (e.g. closing laptop mid-download) eventually\n// go away.\n//\n// A raw copy of standar library.\ntype tcpKeepAliveListener struct {\n\t*net.TCPListener\n\tkeepAliveDur time.Duration\n}\n\n// Accept accepts tcp connections aka clients.\nfunc (l tcpKeepAliveListener) Accept() (net.Conn, error) {\n\ttc, err := l.AcceptTCP()\n\tif err != nil {\n\t\treturn tc, err\n\t}\n\tif err = tc.SetKeepAlive(true); err != nil {\n\t\treturn tc, err\n\t}\n\tif err = tc.SetKeepAlivePeriod(l.keepAliveDur); err != nil {\n\t\treturn tc, err\n\t}\n\treturn tc, nil\n}\n\n// TCP returns a new tcp(ipv6 if supported by network) and an error on failure.\nfunc TCP(addr string, reuse bool) (net.Listener, error) {\n\tvar cfg net.ListenConfig\n\tif reuse {\n\t\tcfg.Control = control\n\t}\n\n\treturn cfg.Listen(context.Background(), \"tcp\", addr)\n}\n\n// TCPKeepAlive returns a new tcp keep alive Listener and an error on failure.\nfunc TCPKeepAlive(addr string, reuse bool, keepAliveDur time.Duration) (ln net.Listener, err error) {\n\t// if strings.HasPrefix(addr, \"127.0.0.1\") {\n\t// \t// it's ipv4, use ipv4 tcp listener instead of the default ipv6. Don't.\n\t// \tln, err = net.Listen(\"tcp4\", addr)\n\t// } else {\n\t// \tln, err = TCP(addr)\n\t// }\n\n\tln, err = TCP(addr, reuse)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn tcpKeepAliveListener{ln.(*net.TCPListener), keepAliveDur}, nil\n}\n\n// UNIX returns a new unix(file) Listener.\nfunc UNIX(socketFile string, mode os.FileMode) (net.Listener, error) {\n\tif errOs := os.Remove(socketFile); errOs != nil && !os.IsNotExist(errOs) {\n\t\treturn nil, fmt.Errorf(\"%s: %w\", socketFile, errOs)\n\t}\n\n\tl, err := net.Listen(\"unix\", socketFile)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"port already in use: %w\", err)\n\t}\n\n\tif err = os.Chmod(socketFile, mode); err != nil {\n\t\treturn nil, fmt.Errorf(\"cannot chmod %#o for %q: %w\", mode, socketFile, err)\n\t}\n\n\treturn l, nil\n}\n\n// TLS returns a new TLS Listener and an error on failure.\nfunc TLS(addr, certFile, keyFile string) (net.Listener, error) {\n\tif certFile == \"\" || keyFile == \"\" {\n\t\treturn nil, errors.New(\"empty certFile or KeyFile\")\n\t}\n\n\tcert, err := tls.LoadX509KeyPair(certFile, keyFile)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn CERT(addr, cert)\n}\n\n// CERT returns a listener which contans tls.Config with the provided certificate, use for ssl.\nfunc CERT(addr string, cert tls.Certificate) (net.Listener, error) {\n\tl, err := net.Listen(\"tcp\", addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\ttlsConfig := &tls.Config{\n\t\tCertificates:             []tls.Certificate{cert},\n\t\tPreferServerCipherSuites: true,\n\t\tMinVersion:               tls.VersionTLS13,\n\t}\n\treturn tls.NewListener(l, tlsConfig), nil\n}\n\n// LETSENCRYPT returns a new Automatic TLS Listener using letsencrypt.org service\n// receives three parameters,\n// the first is the host of the server,\n// second one should declare if the underline tcp listener can be binded more than once,\n// third can be the server name(domain) or empty if skip verification is the expected behavior (not recommended),\n// and the forth is optionally, the cache directory, if you skip it then the cache directory is \"./certcache\"\n// if you want to disable cache directory then simple give it a value of empty string \"\"\n//\n// does NOT supports localhost domains for testing.\n//\n// this is the recommended function to use when you're ready for production state.\nfunc LETSENCRYPT(addr string, reuse bool, serverName string, cacheDirOptional ...string) (net.Listener, error) {\n\tif portIdx := strings.IndexByte(addr, ':'); portIdx == -1 {\n\t\taddr += \":443\"\n\t}\n\n\tl, err := TCP(addr, reuse)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tcacheDir := \"./certcache\"\n\tif len(cacheDirOptional) > 0 {\n\t\tcacheDir = cacheDirOptional[0]\n\t}\n\n\tm := autocert.Manager{\n\t\tPrompt: autocert.AcceptTOS,\n\t} // HostPolicy is missing, if user wants it, then she/he should manually\n\n\tif cacheDir == \"\" {\n\t\t// then the user passed empty by own will, then I guess she/he doesnt' want any cache directory\n\t} else {\n\t\tm.Cache = autocert.DirCache(cacheDir)\n\t}\n\ttlsConfig := &tls.Config{GetCertificate: m.GetCertificate, MinVersion: tls.VersionTLS13}\n\n\t// use InsecureSkipVerify or ServerName to a value\n\tif serverName == \"\" {\n\t\t// if server name is invalid then bypass it\n\t\ttlsConfig.InsecureSkipVerify = true\n\t} else {\n\t\ttlsConfig.ServerName = serverName\n\t}\n\n\treturn tls.NewListener(l, tlsConfig), nil\n}\n"
  },
  {
    "path": "core/netutil/tcp_soreuse_control_unix.go",
    "content": "//go:build !windows && !wasm\n// +build !windows,!wasm\n\npackage netutil\n\nimport (\n\t\"syscall\"\n\n\t\"golang.org/x/sys/unix\"\n)\n\nfunc control(network, address string, c syscall.RawConn) (err error) {\n\tc.Control(func(fd uintptr) {\n\t\terr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEADDR, 1)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\n\t\terr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t})\n\n\treturn\n}\n"
  },
  {
    "path": "core/netutil/tcp_soreuse_control_wasm.go",
    "content": "//go:build wasm\n// +build wasm\n\npackage netutil\n\nimport \"syscall\"\n\nfunc control(network, address string, c syscall.RawConn) error {\n\treturn nil\n}\n"
  },
  {
    "path": "core/netutil/tcp_soreuse_control_windows.go",
    "content": "package netutil\n\nimport (\n\t\"syscall\"\n\n\t\"golang.org/x/sys/windows\"\n)\n\nfunc control(network, address string, c syscall.RawConn) (err error) {\n\treturn c.Control(func(fd uintptr) {\n\t\terr = windows.SetsockoptInt(windows.Handle(fd), windows.SOL_SOCKET, windows.SO_REUSEADDR, 1)\n\t})\n}\n"
  },
  {
    "path": "core/router/TestUseRouterParentDisallow_fuzz_test.go",
    "content": "package router_test\n\nimport (\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/httptest\"\n\t\"testing\"\n)\n\nfunc FuzzTestUseRouterParentDisallow(f *testing.F) {\n\tf.Add(\"no_userouter_allowed\", \"always\", \"_2\", \"_3\", \"/index\", \"/\", \"/user\")\n\tf.Fuzz(func(t *testing.T, data1 string, data2 string, data3 string, data4 string, data5 string,\n\t\tdata6 string, data7 string) {\n\t\tapp := iris.New()\n\t\tapp.UseRouter(func(ctx iris.Context) {\n\t\t\tctx.WriteString(data2)\n\t\t\tctx.Next()\n\t\t})\n\t\tapp.Get(data5, func(ctx iris.Context) {\n\t\t\tctx.WriteString(data1)\n\t\t})\n\n\t\tapp.SetPartyMatcher(func(ctx iris.Context, p iris.Party) bool {\n\t\t\t// modifies the PartyMatcher to not match any UseRouter,\n\t\t\t// tests should receive the handlers response alone.\n\t\t\treturn false\n\t\t})\n\n\t\tapp.PartyFunc(data6, func(p iris.Party) { // it's the same instance of app.\n\t\t\tp.UseRouter(func(ctx iris.Context) {\n\t\t\t\tctx.WriteString(data3)\n\t\t\t\tctx.Next()\n\t\t\t})\n\t\t\tp.Get(data6, func(ctx iris.Context) {\n\t\t\t\tctx.WriteString(data1)\n\t\t\t})\n\t\t})\n\n\t\tapp.PartyFunc(data7, func(p iris.Party) {\n\t\t\tp.UseRouter(func(ctx iris.Context) {\n\t\t\t\tctx.WriteString(data4)\n\t\t\t\tctx.Next()\n\t\t\t})\n\n\t\t\tp.Get(data6, func(ctx iris.Context) {\n\t\t\t\tctx.WriteString(data1)\n\t\t\t})\n\t\t})\n\n\t\te := httptest.New(t, app)\n\t\te.GET(data5)\n\t\te.GET(data6)\n\t\te.GET(data7)\n\n\t})\n}\n"
  },
  {
    "path": "core/router/api_builder.go",
    "content": "package router\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"os\"\n\t\"path\"\n\t\"reflect\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/hero\"\n\t\"github.com/kataras/iris/v12/macro\"\n\tmacroHandler \"github.com/kataras/iris/v12/macro/handler\"\n\n\t\"github.com/kataras/golog\"\n)\n\n// MethodNone is a Virtual method\n// to store the \"offline\" routes.\nconst MethodNone = \"NONE\"\n\n// AllMethods contains the valid HTTP Methods:\n// \"GET\", \"POST\", \"PUT\", \"DELETE\", \"CONNECT\", \"HEAD\",\n// \"PATCH\", \"OPTIONS\", \"TRACE\".\nvar AllMethods = []string{\n\thttp.MethodGet,\n\thttp.MethodHead,\n\thttp.MethodPatch,\n\thttp.MethodPut,\n\thttp.MethodPost,\n\thttp.MethodDelete,\n\thttp.MethodOptions,\n\thttp.MethodConnect,\n\thttp.MethodTrace,\n}\n\n// RegisterMethods adds custom http methods to the \"AllMethods\" list.\n// Use it on initialization of your program.\nfunc RegisterMethods(newCustomHTTPVerbs ...string) {\n\tnewMethods := append(AllMethods, newCustomHTTPVerbs...)\n\tAllMethods = removeDuplicates(newMethods)\n}\n\n// repository passed to all parties(subrouters), it's the object witch keeps\n// all the routes.\ntype repository struct {\n\troutes []*Route\n\tpaths  map[string]*Route // only the fullname path part, required at CreateRoutes for registering index page.\n}\n\nfunc (repo *repository) get(routeName string) *Route {\n\tfor _, r := range repo.routes {\n\t\tif r.Name == routeName {\n\t\t\treturn r\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (repo *repository) getRelative(r *Route) *Route {\n\tif r.tmpl.IsTrailing() || !macroHandler.CanMakeHandler(r.tmpl) {\n\t\treturn nil\n\t}\n\n\tfor _, route := range repo.routes {\n\t\tif r.tmpl.Src == route.tmpl.Src { // No topLink on the same route syntax.\n\t\t\t// Fixes #2008, because of APIBuilder.handle, repo.getRelative and repo.register replacement but with a toplink of the old route.\n\t\t\tcontinue\n\t\t}\n\n\t\tif r.Subdomain == route.Subdomain && r.StatusCode == route.StatusCode && r.Method == route.Method &&\n\t\t\tr.FormattedPath == route.FormattedPath && !route.tmpl.IsTrailing() {\n\t\t\treturn route\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (repo *repository) getByPath(tmplPath string) *Route {\n\tif r, ok := repo.paths[tmplPath]; ok {\n\t\treturn r\n\t}\n\n\treturn nil\n}\n\nfunc (repo *repository) getAll() []*Route {\n\treturn repo.routes\n}\n\nfunc (repo *repository) remove(routeName string) bool {\n\tfor i, r := range repo.routes {\n\t\tif r.Name == routeName {\n\t\t\tlastIdx := len(repo.routes) - 1\n\t\t\tif lastIdx == i {\n\t\t\t\trepo.routes = repo.routes[0:lastIdx]\n\t\t\t} else {\n\t\t\t\tcp := make([]*Route, 0, lastIdx)\n\t\t\t\tcp = append(cp, repo.routes[:i]...)\n\t\t\t\trepo.routes = append(cp, repo.routes[i+1:]...)\n\t\t\t}\n\n\t\t\tdelete(repo.paths, r.tmpl.Src)\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (repo *repository) register(route *Route, rule RouteRegisterRule) (*Route, error) {\n\tfor i, r := range repo.routes {\n\t\t// 14 August 2019 allow register same path pattern with different macro functions,\n\t\t// see #1058\n\t\tif route.DeepEqual(r) {\n\t\t\tif rule == RouteSkip {\n\t\t\t\treturn r, nil\n\t\t\t} else if rule == RouteError {\n\t\t\t\treturn nil, fmt.Errorf(\"new route: %s conflicts with an already registered one: %s route\", route.String(), r.String())\n\t\t\t} else if rule == RouteOverlap {\n\t\t\t\toverlapRoute(r, route)\n\t\t\t\treturn route, nil\n\t\t\t} else {\n\t\t\t\t// replace existing with the latest one, the default behavior.\n\t\t\t\trepo.routes = append(repo.routes[:i], repo.routes[i+1:]...)\n\t\t\t}\n\n\t\t\tbreak // continue\n\t\t}\n\t}\n\n\trepo.routes = append(repo.routes, route)\n\n\tif route.StatusCode == 0 { // a common resource route, not a status code error handler.\n\t\tif repo.paths == nil {\n\t\t\trepo.paths = make(map[string]*Route)\n\t\t}\n\t\trepo.paths[route.tmpl.Src] = route\n\t}\n\n\treturn route, nil\n}\n\nvar defaultOverlapFilter = func(ctx *context.Context) bool {\n\tif ctx.IsStopped() {\n\t\t// It's stopped and the response can be overridden by a new handler.\n\t\t// An exception of compress writer, which does not implement Reseter (and it shouldn't):\n\t\trs, ok := ctx.ResponseWriter().(context.ResponseWriterReseter)\n\t\treturn ok && rs.Reset()\n\t}\n\n\t// It's not stopped, all OK no need to execute the alternative route.\n\treturn false\n}\n\nfunc overlapRoute(r *Route, next *Route) {\n\tnext.BuildHandlers()\n\tnextHandlers := next.Handlers[0:]\n\n\tisErrorRoutes := r.StatusCode > 0 && next.StatusCode > 0\n\n\tdecisionHandler := func(ctx *context.Context) {\n\t\tctx.Next()\n\n\t\tif isErrorRoutes { // fixes issue #1602.\n\t\t\t// If it's an error we don't need to reset (see defaultOverlapFilter)\n\t\t\t// its status code(!) and its body, we just check if it was proceed or not.\n\t\t\tif !ctx.IsStopped() {\n\t\t\t\treturn\n\t\t\t}\n\t\t} else {\n\t\t\tprevStatusCode := ctx.GetStatusCode()\n\n\t\t\tif !defaultOverlapFilter(ctx) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// set the status code that it was stopped with.\n\t\t\t// useful for dependencies with StopWithStatus(XXX)\n\t\t\t// instead of raw ctx.StopExecution().\n\t\t\t// The func_result now also catch the last registered status code\n\t\t\t// of the chain, unless the controller returns an integer.\n\t\t\t// See _examples/authenticated-controller.\n\t\t\tif prevStatusCode > 0 {\n\t\t\t\t// An exception when stored error\n\t\t\t\t// exists and it's type of ErrNotFound.\n\t\t\t\t// Example:\n\t\t\t\t// Version was not found:\n\t\t\t\t//\t we need to be able to send the status on the last not found version\n\t\t\t\t//   but reset the status code if a next available matched version was found.\n\t\t\t\t//\t see the versioning package.\n\t\t\t\tif !errors.Is(ctx.GetErr(), context.ErrNotFound) {\n\t\t\t\t\tctx.StatusCode(prevStatusCode)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tctx.SetErr(nil) // clear any stored error.\n\t\t// Set the route to the next one and execute it.\n\t\tctx.SetCurrentRoute(next.ReadOnly)\n\t\tctx.HandlerIndex(0)\n\t\tctx.Do(nextHandlers)\n\t}\n\n\tr.builtinBeginHandlers = append(context.Handlers{decisionHandler}, r.builtinBeginHandlers...)\n\tr.overlappedLink = next\n}\n\n// APIBuilder the visible API for constructing the router\n// and child routers.\ntype APIBuilder struct {\n\t// the application logger.\n\tlogger *golog.Logger\n\t// parent is the creator of this Party.\n\t// It is nil on Root.\n\tparent *APIBuilder // currently it's not used anywhere.\n\n\t// the per-party APIBuilder with DI.\n\tapiBuilderDI *APIContainer\n\n\t// the api builder global macros registry\n\tmacros *macro.Macros\n\t// the per-party (and its children) values map\n\t// that may help on building the API\n\t// when source code is splitted between projects.\n\t// Initialized on Properties method.\n\tproperties context.Map\n\t// the api builder global routes repository\n\troutes *repository\n\t// disables the debug logging of routes under a per-party and its children.\n\troutesNoLog bool\n\n\t// the per-party handlers, order\n\t// of handlers registration matters,\n\t// inherited by children unless Reset is called.\n\tmiddleware          context.Handlers\n\tmiddlewareErrorCode context.Handlers\n\t// the global middleware handlers, order of call doesn't matters, order\n\t// of handlers registration matters. We need a secondary field for this\n\t// because `UseGlobal` registers handlers that should be executed\n\t// even before the `middleware` handlers, and in the same time keep the order\n\t// of handlers registration, so the same type of handlers are being called in order.\n\tbeginGlobalHandlers context.Handlers\n\n\t// the per-party done handlers, order matters.\n\tdoneHandlers context.Handlers\n\t// global done handlers, order doesn't matter.\n\tdoneGlobalHandlers context.Handlers\n\n\t// the per-party relative path.\n\trelativePath string\n\t// allowMethods are filled with the `AllowMethods` method.\n\t// They are used to create new routes\n\t// per any party's (and its children) routes registered\n\t// if the method \"x\" wasn't registered already via  the `Handle` (and its extensions like `Get`, `Post`...).\n\tallowMethods []string\n\n\t// the per-party (and its children) execution rules for begin, main and done handlers.\n\thandlerExecutionRules ExecutionRules\n\t// the per-party (and its children) route registration rule, see `SetRegisterRule`.\n\trouteRegisterRule RouteRegisterRule\n\n\t// routerFilterHandlers holds a reference\n\t// of the handlers used by the current and its parent Party's registered\n\t// router filters. Inherited by children unless `Reset` (see `UseRouter`),\n\trouterFilterHandlers context.Handlers\n\t// routerFilters field is shared across Parties. Each Party registers\n\t// one or more middlewares to run before the router itself using the `UseRouter` method.\n\t// Each Party calls the shared filter (`partyMatcher`) that decides if its `UseRouter` handlers\n\t// can be executed. By default it's based on party's static path and/or subdomain,\n\t// it can be modified through an `Application.SetPartyMatcher` call\n\t// once before or after routerFilters filled.\n\t//\n\t// The Key is the Party (instance of APIBuilder),\n\t// value wraps the partyFilter + the handlers registered through `UseRouter`.\n\t// See `GetRouterFilters` too.\n\trouterFilters map[Party]*Filter\n\t// partyMatcher field is shared across all Parties,\n\t// can be modified through the Application level only.\n\t//\n\t// It defaults to the internal, simple, \"defaultPartyMatcher\".\n\t// It applies when \"routerFilters\" are used.\n\tpartyMatcher PartyMatcherFunc\n}\n\nvar (\n\t_ Party          = (*APIBuilder)(nil)\n\t_ PartyMatcher   = (*APIBuilder)(nil)\n\t_ RoutesProvider = (*APIBuilder)(nil) // passed to the default request handler (routerHandler)\n)\n\n// NewAPIBuilder creates & returns a new builder\n// which is responsible to build the API and the router handler.\nfunc NewAPIBuilder(logger *golog.Logger) *APIBuilder {\n\treturn &APIBuilder{\n\t\tlogger:        logger,\n\t\tparent:        nil,\n\t\tmacros:        macro.Defaults,\n\t\trelativePath:  \"/\",\n\t\troutes:        new(repository),\n\t\tapiBuilderDI:  &APIContainer{Container: hero.New().WithLogger(logger)},\n\t\trouterFilters: make(map[Party]*Filter),\n\t\tpartyMatcher:  defaultPartyMatcher,\n\t}\n}\n\n// Logger returns the Application Logger.\nfunc (api *APIBuilder) Logger() *golog.Logger {\n\treturn api.logger\n}\n\n// IsRoot reports whether this Party is the root Application's one.\n// It will return false on all children Parties, no exception.\nfunc (api *APIBuilder) IsRoot() bool {\n\treturn api.parent == nil\n}\n\n/* If requested:\n// GetRoot returns the very first Party (the Application).\nfunc (api *APIBuilder) GetRoot() *APIBuilder {\n\troot := api.parent\n\tfor root != nil {\n\t\troot = api.parent\n\t}\n\n\treturn root\n}*/\n\n// ConfigureContainer accepts one or more functions that can be used\n// to configure dependency injection features of this Party\n// such as register dependency and register handlers that will automatically inject any valid dependency.\n// However, if the \"builder\" parameter is nil or not provided then it just returns the *APIContainer,\n// which automatically initialized on Party allocation.\n//\n// It returns the same `APIBuilder` featured with Dependency Injection.\nfunc (api *APIBuilder) ConfigureContainer(builder ...func(*APIContainer)) *APIContainer {\n\tif api.apiBuilderDI.Self == nil {\n\t\tapi.apiBuilderDI.Self = api\n\t}\n\n\tfor _, b := range builder {\n\t\tif b != nil {\n\t\t\tb(api.apiBuilderDI)\n\t\t}\n\t}\n\n\treturn api.apiBuilderDI\n}\n\n// EnsureStaticBindings panics on struct handler (controller)\n// if at least one input binding depends on the request and not in a static structure.\n// Should be called before `RegisterDependency`.\nfunc (api *APIBuilder) EnsureStaticBindings() Party {\n\tdiContainer := api.ConfigureContainer()\n\tdiContainer.Container.DisableStructDynamicBindings = true\n\treturn api\n}\n\n// RegisterDependency calls the `ConfigureContainer.RegisterDependency` method\n// with the provided value(s). See `HandleFunc` and `PartyConfigure` methods too.\nfunc (api *APIBuilder) RegisterDependency(dependencies ...any) {\n\tdiContainer := api.ConfigureContainer()\n\tfor i, dependency := range dependencies {\n\t\tif dependency == nil {\n\t\t\tapi.logger.Warnf(\"Party: %s: nil dependency on position: %d\", api.relativePath, i)\n\t\t\tcontinue\n\t\t}\n\n\t\tdiContainer.RegisterDependency(dependency)\n\t}\n}\n\n// HandleFunc registers a route on HTTP verb \"method\" and relative, to this Party, path.\n// It is like the `Handle` method but it accepts one or more \"handlersFn\" functions\n// that each one of them can accept any input arguments as the HTTP request and\n// output a result as the HTTP response. Specifically,\n// the input of the \"handlersFn\" can be any registered dependency\n// (see ConfigureContainer().RegisterDependency)\n// or leave the framework to parse the request and fill the values accordingly.\n// The output of the \"handlersFn\" can be any output result:\n//\n//\tcustom structs <T>, string, []byte, int, error,\n//\ta combination of the above, hero.Result(hero.View | hero.Response) and more.\n//\n// If more than one handler function is registered\n// then the execution happens without the nessecity of the `Context.Next` method,\n// simply, to stop the execution and not continue to the next \"handlersFn\" in chain\n// you should return an `iris.ErrStopExecution`.\n//\n// Example Code:\n//\n// The client's request body and server's response body Go types.\n// Could be any data structure.\n//\n//\ttype (\n//\t\trequest struct {\n//\t\t\tFirstname string `json:\"firstname\"`\n//\t\t\tLastname string `json:\"lastname\"`\n//\t\t}\n//\n//\t\tresponse struct {\n//\t\t\tID uint64 `json:\"id\"`\n//\t\t\tMessage string `json:\"message\"`\n//\t\t}\n//\t)\n//\n// Register the route hander.\n//\n//\t            HTTP VERB    ROUTE PATH       ROUTE HANDLER\n//\tapp.HandleFunc(\"PUT\", \"/users/{id:uint64}\", updateUser)\n//\n// Code the route handler function.\n// Path parameters and request body are binded\n// automatically.\n// The \"id\" uint64 binds to \"{id:uint64}\" route path parameter and\n// the \"input\" binds to client request data such as JSON.\n//\n//\tfunc updateUser(id uint64, input request) response {\n//\t\t// [custom logic...]\n//\n//\t\treturn response{\n//\t\t\tID:id,\n//\t\t\tMessage: \"User updated successfully\",\n//\t\t}\n//\t}\n//\n// Simulate a client request which sends data\n// to the server and prints out the response.\n//\n//\tcurl --request PUT -d '{\"firstname\":\"John\",\"lastname\":\"Doe\"}' \\\n//\t-H \"Content-Type: application/json\" \\\n//\thttp://localhost:8080/users/42\n//\n//\t{\n//\t\t\"id\": 42,\n//\t\t\"message\": \"User updated successfully\"\n//\t}\n//\n// See the `ConfigureContainer` for more features regrading\n// the dependency injection, mvc and function handlers.\n//\n// This method is just a shortcut of the `ConfigureContainer().Handle`.\nfunc (api *APIBuilder) HandleFunc(method, relativePath string, handlersFn ...any) *Route {\n\treturn api.ConfigureContainer().Handle(method, relativePath, handlersFn...)\n}\n\n// UseFunc registers a function which can accept one or more\n// dependencies (see RegisterDependency) and returns an iris.Handler\n// or a result of <T> and/or an error.\n//\n// This method is just a shortcut of the `ConfigureContainer().Use`.\nfunc (api *APIBuilder) UseFunc(handlersFn ...any) {\n\tapi.ConfigureContainer().Use(handlersFn...)\n}\n\n// GetRelPath returns the current party's relative path.\n// i.e:\n// if r := app.Party(\"/users\"), then the `r.GetRelPath()` is the \"/users\".\n// if r := app.Party(\"www.\") or app.Subdomain(\"www\") then the `r.GetRelPath()` is the \"www.\".\nfunc (api *APIBuilder) GetRelPath() string {\n\treturn api.relativePath\n}\n\n// AllowMethods will re-register the future routes that will be registered\n// via `Handle`, `Get`, `Post`, ... to the given \"methods\" on that Party and its children \"Parties\",\n// duplicates are not registered.\n//\n// Call of `AllowMethod` will override any previous allow methods.\nfunc (api *APIBuilder) AllowMethods(methods ...string) Party {\n\tapi.allowMethods = methods\n\treturn api\n}\n\n// SetExecutionRules alters the execution flow of the route handlers outside of the handlers themselves.\n//\n// For example, if for some reason the desired result is the (done or all) handlers to be executed no matter what\n// even if no `ctx.Next()` is called in the previous handlers, including the begin(`Use`),\n// the main(`Handle`) and the done(`Done`) handlers themselves, then:\n//\n//\tParty#SetExecutionRules(iris.ExecutionRules {\n//\t  Begin: iris.ExecutionOptions{Force: true},\n//\t  Main:  iris.ExecutionOptions{Force: true},\n//\t  Done:  iris.ExecutionOptions{Force: true},\n//\t})\n//\n// Note that if : true then the only remained way to \"break\" the handler chain is by `ctx.StopExecution()` now that `ctx.Next()` does not matter.\n//\n// These rules are per-party, so if a `Party` creates a child one then the same rules will be applied to that as well.\n// Reset of these rules (before `Party#Handle`) can be done with `Party#SetExecutionRules(iris.ExecutionRules{})`.\n//\n// The most common scenario for its use can be found inside Iris MVC Applications;\n// when we want the `Done` handlers of that specific mvc app's `Party`\n// to be executed but we don't want to add `ctx.Next()` on the `OurController#EndRequest`.\n//\n// Returns this Party.\n//\n// Example: https://github.com/kataras/iris/tree/main/_examples/mvc/middleware/without-ctx-next\nfunc (api *APIBuilder) SetExecutionRules(executionRules ExecutionRules) Party {\n\tapi.handlerExecutionRules = executionRules\n\treturn api\n}\n\n// RouteRegisterRule is a type of uint8.\n// Defines the register rule for new routes that already exists.\n// Available values are: RouteOverride, RouteSkip and RouteError.\n//\n// See `Party#SetRegisterRule`.\ntype RouteRegisterRule uint8\n\nconst (\n\t// RouteOverride replaces an existing route with the new one, the default rule.\n\tRouteOverride RouteRegisterRule = iota\n\t// RouteSkip keeps the original route and skips the new one.\n\tRouteSkip\n\t// RouteError log when a route already exists, shown after the `Build` state,\n\t// server never starts.\n\tRouteError\n\t// RouteOverlap will overlap the new route to the previous one.\n\t// If the route stopped and its response can be reset then the new route will be execute.\n\tRouteOverlap\n)\n\n// SetRegisterRule sets a `RouteRegisterRule` for this Party and its children.\n// Available values are:\n// * RouteOverride (the default one)\n// * RouteSkip\n// * RouteError\n// * RouteOverlap.\nfunc (api *APIBuilder) SetRegisterRule(rule RouteRegisterRule) Party {\n\tapi.routeRegisterRule = rule\n\treturn api\n}\n\n// Handle registers a route to this Party.\n// if empty method is passed then handler(s) are being registered to all methods, same as .Any.\n//\n// Returns a *Route, app will throw any errors later on.\nfunc (api *APIBuilder) Handle(method string, relativePath string, handlers ...context.Handler) *Route {\n\treturn api.handle(0, method, relativePath, handlers...)\n}\n\n// handle registers a full route to this Party.\n// Use Handle or Get, Post, Put, Delete and et.c. instead.\nfunc (api *APIBuilder) handle(errorCode int, method string, relativePath string, handlers ...context.Handler) *Route {\n\tif relativePath == \"\" {\n\t\trelativePath = \"/\"\n\t}\n\n\troutes := api.createRoutes(errorCode, []string{method}, relativePath, handlers...)\n\n\tvar route *Route // the last one is returned.\n\tvar err error\n\tfor _, route = range routes {\n\t\tif route == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\t// global\n\n\t\troute.topLink = api.routes.getRelative(route)\n\t\tif route, err = api.routes.register(route, api.routeRegisterRule); err != nil {\n\t\t\tapi.logger.Error(err)\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn route\n}\n\n// HandleMany works like `Handle` but can receive more than one\n// paths separated by spaces and returns always a slice of *Route instead of a single instance of Route.\n//\n// It's useful only if the same handler can handle more than one request paths,\n// otherwise use `Party` which can handle many paths with different handlers and middlewares.\n//\n// Usage:\n//\n//\tapp.HandleMany(\"GET\", \"/user /user/{id:uint64} /user/me\", genericUserHandler)\n//\n// At the other side, with `Handle` we've had to write:\n//\n//\tapp.Handle(\"GET\", \"/user\", userHandler)\n//\tapp.Handle(\"GET\", \"/user/{id:uint64}\", userByIDHandler)\n//\tapp.Handle(\"GET\", \"/user/me\", userMeHandler)\n//\n// app.HandleMany(\"GET POST\", \"/path\", handler)\nfunc (api *APIBuilder) HandleMany(methodOrMulti string, relativePathorMulti string, handlers ...context.Handler) (routes []*Route) {\n\t// at least slash\n\t// a space\n\t// at least one other slash for the next path\n\tpaths := splitPath(relativePathorMulti)\n\tmethods := splitMethod(methodOrMulti)\n\tfor _, p := range paths {\n\t\tif p != \"\" {\n\t\t\tfor _, method := range methods {\n\t\t\t\tif method == \"\" {\n\t\t\t\t\tmethod = \"ANY\"\n\t\t\t\t}\n\t\t\t\tif method == \"ANY\" || method == \"ALL\" {\n\t\t\t\t\troutes = append(routes, api.Any(p, handlers...)...)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\troutes = append(routes, api.Handle(method, p, handlers...))\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\n// HandleDir registers a handler that serves HTTP requests\n// with the contents of a file system (physical or embedded).\n//\n// first parameter  : the route path\n// second parameter : the file system needs to be served\n// third parameter  : not required, the serve directory options.\n//\n// Alternatively, to get just the handler for that look the FileServer function instead.\n//\n//\tapi.HandleDir(\"/static\", iris.Dir(\"./assets\"), iris.DirOptions{IndexName: \"/index.html\", Compress: true})\n//\n// Returns all the registered routes, including GET index and path patterm and HEAD.\n//\n// Usage:\n// HandleDir(\"/public\", \"./assets\", DirOptions{...}) or\n// HandleDir(\"/public\", iris.Dir(\"./assets\"), DirOptions{...})\n// OR\n// //go:embed assets/*\n// var filesystem embed.FS\n// HandleDir(\"/public\",filesystem, DirOptions{...})\n// OR to pick a specific folder of the embedded filesystem:\n// import \"io/fs\"\n// subFilesystem, err := fs.Sub(filesystem, \"assets\")\n// HandleDir(\"/public\",subFilesystem, DirOptions{...})\n//\n// Examples:\n// https://github.com/kataras/iris/tree/main/_examples/file-server\nfunc (api *APIBuilder) HandleDir(requestPath string, fsOrDir any, opts ...DirOptions) (routes []*Route) {\n\toptions := DefaultDirOptions\n\tif len(opts) > 0 {\n\t\toptions = opts[0]\n\t}\n\n\tfs := context.ResolveHTTPFS(fsOrDir)\n\th := FileServer(fs, options)\n\tdescription := \"file server\"\n\tif d, ok := fs.(http.Dir); ok {\n\t\tdescription = string(d)\n\t}\n\n\tfileName, lineNumber := context.HandlerFileLine(h) // take those before StripPrefix.\n\n\t// if subdomain, we get the full path of the path only,\n\t// because a subdomain can have parties as well\n\t// and we need that path to call the `StripPrefix`.\n\t_, fullpath := splitSubdomainAndPath(joinPath(api.relativePath, requestPath))\n\tif fullpath != \"/\" {\n\t\th = StripPrefix(fullpath, h)\n\t}\n\n\tif api.GetRouteByPath(fullpath) == nil {\n\t\t// register index if not registered by the end-developer.\n\t\troutes = api.CreateRoutes([]string{http.MethodGet, http.MethodHead}, requestPath, h)\n\t}\n\n\trequestPath = joinPath(requestPath, WildcardFileParam())\n\n\troutes = append(routes, api.CreateRoutes([]string{http.MethodGet, http.MethodHead}, requestPath, h)...)\n\n\tfor _, route := range routes {\n\t\tif route.Method == http.MethodHead {\n\t\t} else {\n\t\t\troute.Describe(description)\n\t\t\troute.SetSourceLine(fileName, lineNumber)\n\t\t}\n\n\t\tif _, err := api.routes.register(route, api.routeRegisterRule); err != nil {\n\t\t\tapi.logger.Error(err)\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn routes\n}\n\n// CreateRoutes returns a list of Party-based Routes.\n// It does NOT registers the route. Use `Handle, Get...` methods instead.\n// This method can be used for third-parties Iris helpers packages and tools\n// that want a more detailed view of Party-based Routes before take the decision to register them.\nfunc (api *APIBuilder) CreateRoutes(methods []string, relativePath string, handlers ...context.Handler) []*Route {\n\treturn api.createRoutes(0, methods, relativePath, handlers...)\n}\n\n// RemoveRoute deletes a registered route by its name before `Application.Listen`.\n// The default naming for newly created routes is: method + subdomain + path.\n// Reports whether a route with that name was found and removed successfully.\n//\n// Note that this method applies to all Parties (sub routers)\n// even if each of the Parties have access to this method,\n// as the route name is unique per Iris Application.\nfunc (api *APIBuilder) RemoveRoute(routeName string) bool {\n\treturn api.routes.remove(routeName)\n}\n\nfunc (api *APIBuilder) createRoutes(errorCode int, methods []string, relativePath string, handlers ...context.Handler) []*Route {\n\tif statusCodeSuccessful(errorCode) {\n\t\terrorCode = 0\n\t}\n\n\tmainHandlers := context.CopyHandlers(handlers)\n\n\tif errorCode == 0 {\n\t\tif len(methods) == 0 || methods[0] == \"ALL\" || methods[0] == \"ANY\" { // then use like it was .Any\n\t\t\treturn api.Any(relativePath, mainHandlers...)\n\t\t}\n\t}\n\n\t// no clean path yet because of subdomain indicator/separator which contains a dot.\n\t// but remove the first slash if the relative has already ending with a slash\n\t// it's not needed because later on we do normalize/clean the path, but better do it here too\n\t// for any future updates.\n\tif api.relativePath[len(api.relativePath)-1] == '/' {\n\t\tif relativePath[0] == '/' {\n\t\t\trelativePath = relativePath[1:]\n\t\t}\n\t}\n\n\tfilename, line := hero.GetCaller()\n\n\tfullpath := api.relativePath + relativePath // for now, keep the last \"/\" if any,  \"/xyz/\"\n\tif len(mainHandlers) == 0 {\n\t\tapi.logger.Errorf(\"missing handlers for route[%s:%d] %s: %s\", filename, line, strings.Join(methods, \", \"), fullpath)\n\t\treturn nil\n\t}\n\n\t// note: this can not change the caller's handlers as they're but the entry values(handlers)\n\t// of `middleware`, `doneHandlers` and `handlers` can.\n\t// So if we just put `api.middleware` or `api.doneHandlers`\n\t// then the next `Party` will have those updated handlers\n\t// but dev may change the rules for that child Party, so we have to make clones of them here.\n\n\tvar (\n\t\t// global middleware to error handlers as well.\n\t\tbeginHandlers = api.beginGlobalHandlers\n\t\tdoneHandlers  = api.doneGlobalHandlers\n\t)\n\n\tif errorCode == 0 {\n\t\tbeginHandlers = context.JoinHandlers(beginHandlers, api.middleware)\n\t\tdoneHandlers = context.JoinHandlers(doneHandlers, api.doneHandlers)\n\t} else {\n\t\tbeginHandlers = context.JoinHandlers(beginHandlers, api.middlewareErrorCode)\n\t}\n\n\t// before join the middleware + handlers + done handlers and apply the execution rules.\n\n\tmainHandlerName, mainHandlerIndex := context.MainHandlerName(mainHandlers)\n\n\tmainHandlerFileName, mainHandlerFileNumber := context.HandlerFileLineRel(mainHandlers[mainHandlerIndex])\n\n\t// TODO: think of it.\n\tif mainHandlerFileName == \"<autogenerated>\" {\n\t\t// At PartyConfigure, 2nd+ level of routes it will get <autogenerated> but in reallity will be the same as the caller.\n\t\tmainHandlerFileName = filename\n\t\tmainHandlerFileNumber = line\n\t}\n\n\t// re-calculate mainHandlerIndex in favor of the middlewares.\n\tmainHandlerIndex = len(beginHandlers) + mainHandlerIndex\n\n\t// TODO: for UseGlobal/DoneGlobal that doesn't work.\n\tapplyExecutionRules(api.handlerExecutionRules, &beginHandlers, &doneHandlers, &mainHandlers)\n\n\t// global begin handlers -> middleware that are registered before route registration\n\t// -> handlers that are passed to this Handle function.\n\trouteHandlers := context.JoinHandlers(beginHandlers, mainHandlers)\n\t// -> done handlers\n\trouteHandlers = context.JoinHandlers(routeHandlers, doneHandlers)\n\n\t// here we separate the subdomain and relative path\n\tsubdomain, path := splitSubdomainAndPath(fullpath)\n\n\t// if allowMethods are empty, then simply register with the passed, main, method.\n\tmethods = removeDuplicates(append(api.allowMethods, methods...))\n\n\troutes := make([]*Route, len(methods))\n\n\tfor i, m := range methods { // single, empty method for error handlers.\n\t\troute, err := NewRoute(api, errorCode, m, subdomain, path, routeHandlers, *api.macros)\n\t\tif err != nil { // template path parser errors:\n\t\t\tapi.logger.Errorf(\"[%s:%d] %v -> %s:%s:%s\", filename, line, err, m, subdomain, path)\n\t\t\tcontinue\n\t\t}\n\n\t\t// The caller tiself, if anonymous, it's the first line of `app.X(\"/path\", here)`\n\t\troute.RegisterFileName = mainHandlerFileName     // filename\n\t\troute.RegisterLineNumber = mainHandlerFileNumber // line\n\n\t\troute.MainHandlerName = mainHandlerName\n\t\troute.MainHandlerIndex = mainHandlerIndex\n\n\t\t// The main handler source, could be the same as the register's if anonymous.\n\t\troute.SourceFileName = mainHandlerFileName\n\t\troute.SourceLineNumber = mainHandlerFileNumber\n\n\t\t// Add UseGlobal & DoneGlobal Handlers\n\t\t// route.Use(api.beginGlobalHandlers...)\n\t\t// route.Done(api.doneGlobalHandlers...)\n\n\t\troute.NoLog = api.routesNoLog\n\t\troutes[i] = route\n\t}\n\n\treturn routes\n}\n\nfunc removeDuplicates(elements []string) (result []string) {\n\tseen := make(map[string]struct{})\n\n\tfor v := range elements {\n\t\tval := elements[v]\n\t\tif _, ok := seen[val]; !ok {\n\t\t\tseen[val] = struct{}{}\n\t\t\tresult = append(result, val)\n\t\t}\n\t}\n\n\treturn result\n}\n\n// Party returns a new child Party which inherites its\n// parent's options and middlewares.\n// A Party groups routes which may have the same prefix or subdomain and share same middlewares.\n//\n// To create a group of routes for subdomains\n// use the `Subdomain` or `WildcardSubdomain` methods\n// or pass a \"relativePath\" of \"admin.\" or \"*.\" respectfully.\nfunc (api *APIBuilder) Party(relativePath string, handlers ...context.Handler) Party {\n\tif relativePath == \"\" {\n\t\trelativePath = \"/\"\n\t}\n\n\t// if app.Party(\"/\"), root party or app.Party(\"/user\") == app.Party(\"/user\")\n\t// then just add the middlewares and return itself.\n\t// if relativePath == \"\" || api.relativePath == relativePath {\n\t// \tapi.Use(handlers...)\n\t// \treturn api\n\t// }\n\t// ^ No, this is wrong, let the developer do its job, if she/he wants a copy let have it,\n\t// it's a pure check as well, a path can be the same even if it's the same as its parent, i.e.\n\t// app.Party(\"/user\").Party(\"/user\") should result in a /user/user, not a /user.\n\n\tparentPath := api.relativePath\n\tdot := string(SubdomainPrefix[0])\n\tif len(parentPath) > 0 && parentPath[0] == '/' && strings.HasSuffix(relativePath, dot) {\n\t\t// if ends with . , i.e admin., it's subdomain->\n\t\tparentPath = parentPath[1:] // remove first slash\n\t}\n\n\t// this is checked later on but for easier debug is better to do it here:\n\tif api.relativePath[len(api.relativePath)-1] == '/' && relativePath[0] == '/' {\n\t\trelativePath = relativePath[1:] // remove first slash if parent ended with / and new one started with /.\n\t}\n\n\t// if it's subdomain then it has priority, i.e:\n\t// api.relativePath == \"admin.\"\n\t// relativePath == \"panel.\"\n\t// then it should be panel.admin.\n\t// instead of admin.panel.\n\tif hasSubdomain(parentPath) && hasSubdomain(relativePath) {\n\t\trelativePath = relativePath + parentPath\n\t\tparentPath = \"\"\n\t}\n\n\tfullpath := parentPath + relativePath\n\t// append the parent's + child's handlers\n\tmiddleware := context.JoinHandlers(api.middleware, handlers)\n\n\t// the allow methods per party and its children.\n\tallowMethods := make([]string, len(api.allowMethods))\n\tcopy(allowMethods, api.allowMethods)\n\n\t// make a copy of the parent properties.\n\tproperties := make(context.Map, len(api.properties))\n\tfor k, v := range api.properties {\n\t\tproperties[k] = v\n\t}\n\n\tchildAPI := &APIBuilder{\n\t\t// global/api builder\n\t\tlogger:              api.logger,\n\t\tmacros:              api.macros,\n\t\tproperties:          properties,\n\t\troutes:              api.routes,\n\t\troutesNoLog:         api.routesNoLog,\n\t\tbeginGlobalHandlers: api.beginGlobalHandlers,\n\t\tdoneGlobalHandlers:  api.doneGlobalHandlers,\n\n\t\t// per-party/children\n\t\tparent:                api,\n\t\tmiddleware:            middleware,\n\t\tmiddlewareErrorCode:   context.JoinHandlers(api.middlewareErrorCode, context.Handlers{}),\n\t\tdoneHandlers:          api.doneHandlers[0:],\n\t\trouterFilters:         api.routerFilters,\n\t\trouterFilterHandlers:  api.routerFilterHandlers,\n\t\tpartyMatcher:          api.partyMatcher,\n\t\trelativePath:          fullpath,\n\t\tallowMethods:          allowMethods,\n\t\thandlerExecutionRules: api.handlerExecutionRules,\n\t\trouteRegisterRule:     api.routeRegisterRule,\n\t\tapiBuilderDI: &APIContainer{\n\t\t\t// attach a new Container with correct dynamic path parameter start index for input arguments\n\t\t\t// based on the fullpath.\n\t\t\tContainer: api.apiBuilderDI.Container.Clone(),\n\t\t},\n\t}\n\n\treturn childAPI\n}\n\n// PartyFunc same as `Party`, groups routes that share a base path or/and same handlers.\n// However this function accepts a function that receives this created Party instead.\n// Returns the Party in order the caller to be able to use this created Party to continue the\n// top-bottom routes \"tree\".\n//\n// Note: `iris#Party` and `core/router#Party` describes the exactly same interface.\n//\n// Usage:\n//\n//\tapp.PartyFunc(\"/users\", func(u iris.Party){\n//\t\tu.Use(authMiddleware, logMiddleware)\n//\t\tu.Get(\"/\", getAllUsers)\n//\t\tu.Post(\"/\", createOrUpdateUser)\n//\t\tu.Delete(\"/\", deleteUser)\n//\t})\n//\n// Look `Party` for more.\nfunc (api *APIBuilder) PartyFunc(relativePath string, partyBuilderFunc func(p Party)) Party {\n\tp := api.Party(relativePath)\n\tpartyBuilderFunc(p)\n\treturn p\n}\n\ntype (\n\t// PartyConfigurator is an interface which all child parties that are registered\n\t// through `PartyConfigure` should implement.\n\tPartyConfigurator interface {\n\t\tConfigure(parent Party)\n\t}\n\n\t// StrictlyPartyConfigurator is an optional interface which a `PartyConfigurator`\n\t// can implement to make sure that all exported fields having a not-nin, non-zero\n\t// value before server starts.\n\t// StrictlyPartyConfigurator interface {\n\t// \tStrict() bool\n\t// }\n\t// Good idea but a `mvc or bind:\"required\"` is a better one I think.\n)\n\n// PartyConfigure like `Party` and `PartyFunc` registers a new children Party\n// but instead it accepts a struct value which should implement the PartyConfigurator interface.\n//\n// PartyConfigure accepts the relative path of the child party\n// (As an exception, if it's empty then all configurators are applied to the current Party)\n// and one or more Party configurators and\n// executes the PartyConfigurator's Configure method.\n//\n// If the end-developer registered one or more dependencies upfront through\n// RegisterDependencies or ConfigureContainer.RegisterDependency methods\n// and \"p\" is a pointer to a struct then try to bind the unset/zero exported fields\n// to the registered dependencies, just like we do with Controllers.\n// Useful when the api's dependencies amount are too much to pass on a function.\n//\n// Usage:\n//\n//\tapp.PartyConfigure(\"/users\", &api.UsersAPI{UserRepository: ..., ...})\n//\n// Where UsersAPI looks like:\n//\n//\ttype UsersAPI struct { [...] }\n//\tfunc(api *UsersAPI) Configure(router iris.Party) {\n//\t router.Get(\"/{id:uuid}\", api.getUser)\n//\t [...]\n//\t}\n//\n// Usage with (static) dependencies:\n//\n//\tapp.RegisterDependency(userRepo, ...)\n//\tapp.PartyConfigure(\"/users\", new(api.UsersAPI))\nfunc (api *APIBuilder) PartyConfigure(relativePath string, partyReg ...PartyConfigurator) Party {\n\tvar child Party\n\n\tif relativePath == \"\" {\n\t\tchild = api\n\t} else {\n\t\tchild = api.Party(relativePath)\n\t}\n\n\tfor _, p := range partyReg {\n\t\tif p == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tif len(api.apiBuilderDI.Container.Dependencies) > 0 {\n\t\t\tif typ := reflect.TypeOf(p); typ.Kind() == reflect.Ptr && typ.Elem().Kind() == reflect.Struct {\n\t\t\t\tapi.apiBuilderDI.Container.Struct(p, -1)\n\t\t\t}\n\t\t}\n\n\t\tp.Configure(child)\n\t}\n\n\treturn child\n}\n\n// Subdomain returns a new party which is responsible to register routes to\n// this specific \"subdomain\".\n//\n// If called from a child party then the subdomain will be prepended to the path instead of appended.\n// So if app.Subdomain(\"admin\").Subdomain(\"panel\") then the result is: \"panel.admin.\".\nfunc (api *APIBuilder) Subdomain(subdomain string, middleware ...context.Handler) Party {\n\tif api.relativePath == SubdomainWildcardIndicator {\n\t\t// cannot concat wildcard subdomain with something else\n\t\tapi.logger.Errorf(\"cannot concat parent wildcard subdomain with anything else ->  %s , %s\",\n\t\t\tapi.relativePath, subdomain)\n\t\treturn api\n\t}\n\tif l := len(subdomain); l < 1 {\n\t\treturn api\n\t} else if subdomain[l-1] != '.' {\n\t\tsubdomain += \".\"\n\t}\n\n\treturn api.Party(subdomain, middleware...)\n}\n\n// WildcardSubdomain returns a new party which is responsible to register routes to\n// a dynamic, wildcard(ed) subdomain. A dynamic subdomain is a subdomain which\n// can reply to any subdomain requests. Server will accept any subdomain\n// (if not static subdomain found) and it will search and execute the handlers of this party.\nfunc (api *APIBuilder) WildcardSubdomain(middleware ...context.Handler) Party {\n\tif hasSubdomain(api.relativePath) {\n\t\t// cannot concat static subdomain with a dynamic one, wildcard should be at the root level\n\t\tapi.logger.Errorf(\"cannot concat static subdomain with a dynamic one. Dynamic subdomains should be at the root level -> %s\",\n\t\t\tapi.relativePath)\n\t\treturn api\n\t}\n\treturn api.Subdomain(SubdomainWildcardIndicator, middleware...)\n}\n\n// Macros returns the macro collection that is responsible\n// to register custom macros with their own parameter types and their macro functions for all routes.\n//\n// Learn more at:  https://github.com/kataras/iris/tree/main/_examples/routing/dynamic-path\nfunc (api *APIBuilder) Macros() *macro.Macros {\n\treturn api.macros\n}\n\n// Properties returns the original Party's properties map,\n// it can be modified before server startup but not afterwards.\nfunc (api *APIBuilder) Properties() context.Map {\n\tif api.properties == nil {\n\t\tapi.properties = make(context.Map)\n\t}\n\n\treturn api.properties\n}\n\n// GetRoutes returns the routes information,\n// some of them can be changed at runtime some others not.\n//\n// Needs refresh of the router to Method or Path or Handlers changes to take place.\nfunc (api *APIBuilder) GetRoutes() []*Route {\n\treturn api.routes.getAll()\n}\n\n// CountHandlers returns the total number of all unique\n// registered route handlers.\nfunc (api *APIBuilder) CountHandlers() int {\n\tuniqueNames := make(map[string]struct{})\n\n\tfor _, r := range api.GetRoutes() {\n\t\tfor _, h := range r.Handlers {\n\t\t\thandlerName := context.HandlerName(h)\n\t\t\tif _, exists := uniqueNames[handlerName]; !exists {\n\t\t\t\tuniqueNames[handlerName] = struct{}{}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn len(uniqueNames)\n}\n\n// GetRoute returns the registered route based on its name, otherwise nil.\n// One note: \"routeName\" should be case-sensitive.\nfunc (api *APIBuilder) GetRoute(routeName string) *Route {\n\treturn api.routes.get(routeName)\n}\n\n// GetRouteByPath returns the registered route based on the template path (`Route.Tmpl().Src`).\nfunc (api *APIBuilder) GetRouteByPath(tmplPath string) *Route {\n\treturn api.routes.getByPath(tmplPath)\n}\n\n// GetRoutesReadOnly returns the registered routes with \"read-only\" access,\n// you cannot and you should not change any of these routes' properties on request state,\n// you can use the `GetRoutes()` for that instead.\n//\n// It returns interface-based slice instead of the real ones in order to apply\n// safe fetch between context(request-state) and the builded application.\n//\n// Look `GetRouteReadOnly` too.\nfunc (api *APIBuilder) GetRoutesReadOnly() []context.RouteReadOnly {\n\troutes := api.GetRoutes()\n\treadOnlyRoutes := make([]context.RouteReadOnly, len(routes))\n\tfor i, r := range routes {\n\t\treadOnlyRoutes[i] = r.ReadOnly\n\t}\n\n\treturn readOnlyRoutes\n}\n\n// GetRouteReadOnly returns the registered \"read-only\" route based on its name, otherwise nil.\n// One note: \"routeName\" should be case-sensitive. Used by the context to get the current route.\n// It returns an interface instead to reduce wrong usage and to keep the decoupled design between\n// the context and the routes.\n// Look `GetRoutesReadOnly` to fetch a list of all registered routes.\n//\n// Look `GetRoute` for more.\nfunc (api *APIBuilder) GetRouteReadOnly(routeName string) context.RouteReadOnly {\n\tr := api.GetRoute(routeName)\n\tif r == nil {\n\t\treturn nil\n\t}\n\treturn r.ReadOnly\n}\n\n// GetRouteReadOnlyByPath returns the registered read-only route based on the template path (`Route.Tmpl().Src`).\nfunc (api *APIBuilder) GetRouteReadOnlyByPath(tmplPath string) context.RouteReadOnly {\n\tr := api.GetRouteByPath(tmplPath)\n\tif r == nil {\n\t\treturn nil\n\t}\n\n\treturn r.ReadOnly\n}\n\n// SetRoutesNoLog disables (true) the verbose logging for the next registered\n// routes under this Party and its children.\n//\n// To disable logging for controllers under MVC Application,\n// see `mvc/Application.SetControllersNoLog` instead.\n//\n// Defaults to false when log level is \"debug\".\nfunc (api *APIBuilder) SetRoutesNoLog(disable bool) Party {\n\tapi.routesNoLog = disable\n\treturn api\n}\n\ntype (\n\t// PartyMatcherFunc used to build a filter which decides\n\t// if the given Party is responsible to fire its `UseRouter` handlers or not.\n\t// Can be customized through `SetPartyMatcher` method. See `Match` method too.\n\tPartyMatcherFunc func(*context.Context, Party) bool\n\t// PartyMatcher decides if `UseRouter` handlers should be executed or not.\n\t// A different interface becauwe we want to separate\n\t// the Party's public API from `UseRouter` internals.\n\tPartyMatcher interface {\n\t\tMatch(ctx *context.Context) bool\n\t}\n\t// Filter is a wraper for a Router Filter contains information\n\t// for its Party's fullpath, subdomain the Party's\n\t// matcher and the associated handlers to be executed before main router's request handler.\n\tFilter struct {\n\t\tMatcher   PartyMatcher             // it's a Party, for freedom that can be changed through a custom matcher which accepts the same filter.\n\t\tSkippers  map[*APIBuilder]struct{} // skip execution on these builders ( see `Reset`)\n\t\tSubdomain string\n\t\tPath      string\n\t\tHandlers  context.Handlers\n\t}\n)\n\n// SetPartyMatcher accepts a function which runs against\n// a Party and should report whether its `UseRouter` handlers should be executed.\n// PartyMatchers are run through parent to children.\n// It modifies the default Party filter that decides\n// which `UseRouter` middlewares to run before the Router,\n// each one of those middlewares can skip `Context.Next` or call `Context.StopXXX`\n// to stop the main router from searching for a route match.\n// Can be called before or after `UseRouter`, it doesn't matter.\nfunc (api *APIBuilder) SetPartyMatcher(matcherFunc PartyMatcherFunc) {\n\tif matcherFunc == nil {\n\t\tmatcherFunc = defaultPartyMatcher\n\t}\n\tapi.partyMatcher = matcherFunc\n}\n\n// Match reports whether the `UseRouter` handlers should be executed.\n// Calls its parent's Match if possible.\n// Implements the `PartyMatcher` interface.\nfunc (api *APIBuilder) Match(ctx *context.Context) bool {\n\treturn api.partyMatcher(ctx, api)\n}\n\nfunc defaultPartyMatcher(ctx *context.Context, p Party) bool {\n\tsubdomain, path := splitSubdomainAndPath(p.GetRelPath())\n\tstaticPath := staticPath(path)\n\thosts := subdomain != \"\"\n\n\tif p.IsRoot() {\n\t\t// ALWAYS executed first when registered\n\t\t// through an `Application.UseRouter` call.\n\t\treturn true\n\t}\n\n\tif hosts {\n\t\t// Note(@kataras): do NOT try to implement something like party matcher for each party\n\t\t// separately. We will introduce a new problem with subdomain inside a subdomain:\n\t\t// they are not by prefix, so parenting calls will not help\n\t\t// e.g. admin. and control.admin, control.admin is a sub of the admin.\n\t\tif !canHandleSubdomain(ctx, subdomain) {\n\t\t\treturn false\n\t\t}\n\t}\n\n\t// this is the longest static path.\n\treturn strings.HasPrefix(ctx.Path(), staticPath)\n}\n\n// GetRouterFilters returns the global router filters.\n// Read `UseRouter` for more.\n// The map can be altered before router built.\n// The router internally prioritized them by the subdomains and\n// longest static path.\n// Implements the `RoutesProvider` interface.\nfunc (api *APIBuilder) GetRouterFilters() map[Party]*Filter {\n\treturn api.routerFilters\n}\n\n// UseRouter upserts one or more handlers that will be fired\n// right before the main router's request handler.\n//\n// Use this method to register handlers, that can ran\n// independently of the incoming request's values,\n// that they will be executed ALWAYS against ALL children incoming requests.\n// Example of use-case: CORS.\n//\n// Note that because these are executed before the router itself\n// the Context should not have access to the `GetCurrentRoute`\n// as it is not decided yet which route is responsible to handle the incoming request.\n// It's one level higher than the `WrapRouter`.\n// The context SHOULD call its `Next` method in order to proceed to\n// the next handler in the chain or the main request handler one.\nfunc (api *APIBuilder) UseRouter(handlers ...context.Handler) {\n\tif len(handlers) == 0 || handlers[0] == nil {\n\t\treturn\n\t}\n\n\tbeginHandlers := context.Handlers(handlers)\n\t// respect any execution rules (begin).\n\tapi.handlerExecutionRules.Begin.apply(&beginHandlers)\n\tbeginHandlers = context.JoinHandlers(api.routerFilterHandlers, beginHandlers)\n\n\tif f := api.routerFilters[api]; f != nil && len(f.Handlers) > 0 { // exists.\n\t\tbeginHandlers = context.UpsertHandlers(f.Handlers, beginHandlers) // remove dupls.\n\t}\n\n\t// we are not using the parent field here,\n\t// we need to have control over those values in order to be able to `Reset`.\n\tapi.routerFilterHandlers = beginHandlers\n\n\tsubdomain, path := splitSubdomainAndPath(api.relativePath)\n\tapi.routerFilters[api] = &Filter{\n\t\tMatcher:   api,\n\t\tSubdomain: subdomain,\n\t\tPath:      path,\n\t\tHandlers:  beginHandlers,\n\t}\n}\n\n// GetDefaultErrorMiddleware returns the application's error pre handlers\n// registered through `UseError` for the default error handlers.\n// This is used when no matching error handlers registered\n// for a specific status code but `UseError` is called to register a middleware,\n// so the default error handler should make use of those middleware now.\nfunc (api *APIBuilder) GetDefaultErrorMiddleware() context.Handlers {\n\treturn api.middlewareErrorCode\n}\n\n// UseError upserts one or more handlers that will be fired,\n// as middleware, before any error handler registered through `On(Any)ErrorCode`.\n// See `OnErrorCode` too.\nfunc (api *APIBuilder) UseError(handlers ...context.Handler) {\n\tapi.middlewareErrorCode = context.UpsertHandlers(api.middlewareErrorCode, handlers)\n}\n\n// Use appends Handler(s) to the current Party's routes and child routes.\n// If the current Party is the root, then it registers the middleware to all child Parties' routes too.\n// The given \"handlers\" will be executed only on matched routes.\n//\n// Call order matters, it should be called right before the routes that they care about these handlers.\n//\n// If it's called after the routes then these handlers will never be executed.\n// Use `UseGlobal` if you want to register begin handlers(middleware)\n// that should be always run before all application's routes.\n// To register a middleware for error handlers, look `UseError` method instead.\nfunc (api *APIBuilder) Use(handlers ...context.Handler) {\n\tapi.middleware = append(api.middleware, handlers...)\n}\n\n// UseOnce either inserts a middleware,\n// or on the basis of the middleware already existing,\n// replace that existing middleware instead.\n// To register a middleware for error handlers, look `UseError` method instead.\nfunc (api *APIBuilder) UseOnce(handlers ...context.Handler) {\n\tapi.middleware = context.UpsertHandlers(api.middleware, handlers)\n}\n\n// UseGlobal registers handlers that should run at the very beginning.\n// It prepends those handler(s) to all routes,\n// including all parties, subdomains and errors.\n// It doesn't care about call order, it will prepend the handlers to all\n// existing routes and the future routes that may being registered.\n//\n// The given \"handlers\" will be executed only on matched routes and registered errors.\n// See `UseRouter` if you want to register middleware that will always run, even on 404 not founds.\n//\n// The difference from `.DoneGlobal` is that this/or these Handler(s) are being always running first.\n// Use of `ctx.Next()` of those handler(s) is necessary to call the main handler or the next middleware.\n// It's always a good practise to call it right before the `Application#Run` function.\nfunc (api *APIBuilder) UseGlobal(handlers ...context.Handler) {\n\tfor _, r := range api.routes.routes {\n\t\t// r.beginHandlers = append(handlers, r.beginHandlers...)\n\t\t// ^ this is correct but we act global begin handlers as one chain, so\n\t\t// if called last more than one time, after all routes registered, we must somehow\n\t\t// register them by order, so:\n\t\tr.Use(handlers...)\n\t}\n\t// set as begin handlers for the next routes as well.\n\tapi.beginGlobalHandlers = append(api.beginGlobalHandlers, handlers...)\n}\n\n// Done appends to the very end, Handler(s) to the current Party's routes and child routes.\n// The given \"handlers\" will be executed only on matched routes.\n//\n// Call order matters, it should be called right before the routes that they care about these handlers.\n//\n// The difference from .Use is that this/or these Handler(s) are being always running last.\nfunc (api *APIBuilder) Done(handlers ...context.Handler) {\n\tapi.doneHandlers = append(api.doneHandlers, handlers...)\n}\n\n// DoneGlobal registers handlers that should run at the very end.\n// It appends those handler(s) to all routes,\n// including all parties, subdomains.\n// It doesn't care about call order, it will append the handlers to all\n// existing routes and the future routes that may being registered.\n//\n// The given \"handlers\" will be executed only on matched and registered error routes.\n//\n// The difference from `.UseGlobal` is that this/or these Handler(s) are being always running last.\n// Use of `ctx.Next()` at the previous handler is necessary.\n// It's always a good practise to call it right before the `Application#Run` function.\nfunc (api *APIBuilder) DoneGlobal(handlers ...context.Handler) {\n\tfor _, r := range api.routes.routes {\n\t\tr.Done(handlers...) // append the handlers to the existing routes\n\t}\n\t// set as done handlers for the next routes as well.\n\tapi.doneGlobalHandlers = append(api.doneGlobalHandlers, handlers...)\n}\n\n// MiddlewareExists reports whether the given handler exists in the middleware chain.\nfunc (api *APIBuilder) MiddlewareExists(handlerNameOrFunc any) bool {\n\tif handlerNameOrFunc == nil {\n\t\treturn false\n\t}\n\n\tvar handlers context.Handlers\n\n\tif filter, ok := api.routerFilters[api]; ok {\n\t\thandlers = append(handlers, filter.Handlers...)\n\t}\n\n\thandlers = append(handlers, api.middleware...)\n\thandlers = append(handlers, api.doneHandlers...)\n\thandlers = append(handlers, api.beginGlobalHandlers...)\n\thandlers = append(handlers, api.doneGlobalHandlers...)\n\n\treturn context.HandlerExists(handlers, handlerNameOrFunc)\n}\n\n// RemoveHandler deletes a handler from begin and done handlers\n// based on its name or the handler pc function.\n// Note that UseGlobal and DoneGlobal handlers cannot be removed\n// through this method as they were registered to the routes already.\n//\n// As an exception, if one of the arguments is a pointer to an int,\n// then this is used to set the total amount of removed handlers.\n//\n// Returns the Party itself for chain calls.\n//\n// Should be called before children routes regitration.\nfunc (api *APIBuilder) RemoveHandler(namesOrHandlers ...any) Party {\n\tvar counter *int\n\n\tfor _, nameOrHandler := range namesOrHandlers {\n\t\thandlerName := \"\"\n\t\tswitch h := nameOrHandler.(type) {\n\t\tcase string:\n\t\t\thandlerName = h\n\t\tcase context.Handler: //, func(*context.Context):\n\t\t\thandlerName = context.HandlerName(h)\n\t\tcase *int:\n\t\t\tcounter = h\n\t\tdefault:\n\t\t\tpanic(fmt.Sprintf(\"remove handler: unexpected type of %T\", h))\n\t\t}\n\n\t\tapi.middleware = removeHandler(handlerName, api.middleware, counter)\n\t\tapi.doneHandlers = removeHandler(handlerName, api.doneHandlers, counter)\n\t}\n\n\treturn api\n}\n\n// Reset removes all the begin and done handlers that may derived from the parent party via `Use` & `Done`,\n// and the execution rules.\n// Note that the `Reset` will not reset the handlers that are registered via `UseGlobal` & `DoneGlobal`.\n//\n// Returns this Party.\nfunc (api *APIBuilder) Reset() Party {\n\tapi.middleware = api.middleware[0:0]\n\tapi.middlewareErrorCode = api.middlewareErrorCode[0:0]\n\tapi.ResetRouterFilters()\n\n\tapi.doneHandlers = api.doneHandlers[0:0]\n\tapi.handlerExecutionRules = ExecutionRules{}\n\tapi.routeRegisterRule = RouteOverride\n\n\t// keep container as it's.\n\treturn api\n}\n\n// ResetRouterFilters deactivates any previous registered\n// router filters and the parents ones for this Party.\n//\n// Returns this Party.\nfunc (api *APIBuilder) ResetRouterFilters() Party {\n\tapi.routerFilterHandlers = api.routerFilterHandlers[0:0]\n\tdelete(api.routerFilters, api)\n\n\tif api.parent == nil {\n\t\t// it's the root, stop, nothing else to do here.\n\t\treturn api\n\t}\n\n\t// Set a filter with empty handlers, the router will find it, execute nothing\n\t// and continue with the request handling. This works on Reset() and no UseRouter\n\t// and with Reset().UseRouter.\n\tsubdomain, path := splitSubdomainAndPath(api.relativePath)\n\tapi.routerFilters[api] = &Filter{\n\t\tMatcher:   api,\n\t\tHandlers:  nil,\n\t\tSubdomain: subdomain,\n\t\tPath:      path,\n\t}\n\n\treturn api\n}\n\n// None registers an \"offline\" route\n// see context.ExecRoute(routeName) and\n// party.Routes().Online(handleResultRouteInfo, \"GET\") and\n// Offline(handleResultRouteInfo)\n//\n// Returns a *Route and an error which will be filled if route wasn't registered successfully.\nfunc (api *APIBuilder) None(relativePath string, handlers ...context.Handler) *Route {\n\treturn api.Handle(MethodNone, relativePath, handlers...)\n}\n\n// Get registers a route for the Get HTTP Method.\n//\n// Returns a *Route and an error which will be filled if route wasn't registered successfully.\nfunc (api *APIBuilder) Get(relativePath string, handlers ...context.Handler) *Route {\n\treturn api.Handle(http.MethodGet, relativePath, handlers...)\n}\n\n// Post registers a route for the Post HTTP Method.\n//\n// Returns a *Route and an error which will be filled if route wasn't registered successfully.\nfunc (api *APIBuilder) Post(relativePath string, handlers ...context.Handler) *Route {\n\treturn api.Handle(http.MethodPost, relativePath, handlers...)\n}\n\n// Put registers a route for the Put HTTP Method.\n//\n// Returns a *Route and an error which will be filled if route wasn't registered successfully.\nfunc (api *APIBuilder) Put(relativePath string, handlers ...context.Handler) *Route {\n\treturn api.Handle(http.MethodPut, relativePath, handlers...)\n}\n\n// Delete registers a route for the Delete HTTP Method.\n//\n// Returns a *Route and an error which will be filled if route wasn't registered successfully.\nfunc (api *APIBuilder) Delete(relativePath string, handlers ...context.Handler) *Route {\n\treturn api.Handle(http.MethodDelete, relativePath, handlers...)\n}\n\n// Connect registers a route for the Connect HTTP Method.\n//\n// Returns a *Route and an error which will be filled if route wasn't registered successfully.\nfunc (api *APIBuilder) Connect(relativePath string, handlers ...context.Handler) *Route {\n\treturn api.Handle(http.MethodConnect, relativePath, handlers...)\n}\n\n// Head registers a route for the Head HTTP Method.\n//\n// Returns a *Route and an error which will be filled if route wasn't registered successfully.\nfunc (api *APIBuilder) Head(relativePath string, handlers ...context.Handler) *Route {\n\treturn api.Handle(http.MethodHead, relativePath, handlers...)\n}\n\n// Options registers a route for the Options HTTP Method.\n//\n// Returns a *Route and an error which will be filled if route wasn't registered successfully.\nfunc (api *APIBuilder) Options(relativePath string, handlers ...context.Handler) *Route {\n\treturn api.Handle(http.MethodOptions, relativePath, handlers...)\n}\n\n// Patch registers a route for the Patch HTTP Method.\n//\n// Returns a *Route and an error which will be filled if route wasn't registered successfully.\nfunc (api *APIBuilder) Patch(relativePath string, handlers ...context.Handler) *Route {\n\treturn api.Handle(http.MethodPatch, relativePath, handlers...)\n}\n\n// Trace registers a route for the Trace HTTP Method.\n//\n// Returns a *Route and an error which will be filled if route wasn't registered successfully.\nfunc (api *APIBuilder) Trace(relativePath string, handlers ...context.Handler) *Route {\n\treturn api.Handle(http.MethodTrace, relativePath, handlers...)\n}\n\n// Any registers a route for ALL of the HTTP methods:\n// Get\n// Post\n// Put\n// Delete\n// Head\n// Patch\n// Options\n// Connect\n// Trace\nfunc (api *APIBuilder) Any(relativePath string, handlers ...context.Handler) (routes []*Route) {\n\tfor _, m := range AllMethods {\n\t\tr := api.HandleMany(m, relativePath, handlers...)\n\t\troutes = append(routes, r...)\n\t}\n\n\treturn\n}\n\ntype (\n\t// ServerHandler is the interface which all server handlers should implement.\n\t// The Iris Application implements it.\n\t// See `Party.HandleServer` method for more.\n\tServerHandler interface {\n\t\tServeHTTPC(*context.Context)\n\t}\n\n\tserverBuilder interface {\n\t\tBuild() error\n\t}\n)\n\n// HandleServer registers a route for all HTTP methods which forwards the requests to the given server.\n//\n// Usage:\n//\n//\tapp.HandleServer(\"/api/identity/{first:string}/orgs/{second:string}/{p:path}\", otherApp)\n//\n// OR\n//\n//\tapp.HandleServer(\"/api/identity\", otherApp)\nfunc (api *APIBuilder) HandleServer(path string, server ServerHandler) {\n\tif server == nil {\n\t\treturn\n\t}\n\n\tif app, ok := server.(serverBuilder); ok {\n\t\t// Do an extra check for Build() error at any case\n\t\t// the end-developer didn't call Build before.\n\t\tif err := app.Build(); err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t}\n\n\tpathParameterName := \"\"\n\n\t// Check and get the last parameter name if it's a wildcard one by the end-developer.\n\tparsedPath, err := macro.Parse(path, *api.macros)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tif n := len(parsedPath.Params); n > 0 {\n\t\tlastParam := parsedPath.Params[n-1]\n\t\tif lastParam.IsMacro(macro.Path) {\n\t\t\tpathParameterName = lastParam.Name\n\t\t\t// path remains as it was defined by the end-developer.\n\t\t}\n\t}\n\t//\n\n\tif pathParameterName == \"\" {\n\t\tpathParameterName = fmt.Sprintf(\"iris_wildcard_path_parameter%d\", len(api.routes.routes))\n\t\tpath = fmt.Sprintf(\"%s/{%s:path}\", path, pathParameterName)\n\t}\n\n\thandler := makeServerHandler(pathParameterName, server.ServeHTTPC)\n\tapi.Any(path, handler)\n}\n\nfunc makeServerHandler(givenPathParameter string, handler context.Handler) context.Handler {\n\treturn func(ctx *context.Context) {\n\t\tpathValue := \"\"\n\t\tif givenPathParameter == \"\" {\n\t\t\tpathValue = ctx.Params().GetEntryAt(ctx.Params().Len() - 1).ValueRaw.(string)\n\t\t} else {\n\t\t\tpathValue = ctx.Params().Get(givenPathParameter)\n\t\t}\n\n\t\tapiPath := \"/\" + pathValue\n\n\t\tr := ctx.Request()\n\t\tr.URL.Path = apiPath\n\t\tr.URL.RawPath = apiPath\n\t\tctx.Params().Reset()\n\n\t\thandler(ctx)\n\t}\n}\n\nfunc (api *APIBuilder) registerResourceRoute(reqPath string, h context.Handler) *Route {\n\tapi.Head(reqPath, h)\n\treturn api.Get(reqPath, h)\n}\n\n// StaticContent registers a GET and HEAD method routes to the requestPath\n// that are ready to serve raw static bytes, memory cached.\n//\n// Returns the GET *Route.\nfunc (api *APIBuilder) StaticContent(reqPath string, cType string, content []byte) *Route {\n\tmodtime := time.Now()\n\th := func(ctx *context.Context) {\n\t\tctx.ContentType(cType)\n\t\tif _, err := ctx.WriteWithExpiration(content, modtime); err != nil {\n\t\t\tctx.StatusCode(http.StatusInternalServerError)\n\t\t\t// ctx.Application().Logger().Infof(\"error while serving []byte via StaticContent: %s\", err.Error())\n\t\t}\n\t}\n\n\treturn api.registerResourceRoute(reqPath, h)\n}\n\n// Favicon serves static favicon\n// accepts 2 parameters, second is optional\n// favPath (string), declare the system directory path of the __.ico\n// requestPath (string), it's the route's path, by default this is the \"/favicon.ico\" because some browsers tries to get this by default first,\n// you can declare your own path if you have more than one favicon (desktop, mobile and so on)\n//\n// this func will add a route for you which will static serve the /yuorpath/yourfile.ico to the /yourfile.ico\n// (nothing special that you can't handle by yourself).\n// Note that you have to call it on every favicon you have to serve automatically (desktop, mobile and so on).\n//\n// Returns the GET *Route.\nfunc (api *APIBuilder) Favicon(favPath string, requestPath ...string) *Route {\n\tdescription := favPath\n\tfavPath = Abs(favPath)\n\tf, err := os.Open(favPath)\n\tif err != nil {\n\t\tapi.logger.Errorf(\"favicon: file or directory %s not found: %w\", favPath, err)\n\t\treturn nil\n\t}\n\n\tdefer f.Close()\n\tfi, _ := f.Stat()\n\tif fi.IsDir() { // if it's dir the try to get the favicon.ico\n\t\treturn api.Favicon(path.Join(favPath, \"favicon.ico\"))\n\t}\n\n\t// copy the bytes here in order to cache and not read the ico on each request.\n\tcacheFav := make([]byte, fi.Size())\n\tif _, err = f.Read(cacheFav); err != nil {\n\t\t// Here we are before actually run the server.\n\t\t// So we could panic but we don't,\n\t\t// we just interrupt with a message\n\t\t// to the (user-defined) logger.\n\t\tapi.logger.Errorf(\"favicon: couldn't read the data bytes for %s: %w\", favPath, err)\n\t\treturn nil\n\t}\n\n\tmodtime := time.Now()\n\tcType := TypeByFilename(favPath)\n\th := func(ctx *context.Context) {\n\t\tctx.ContentType(cType)\n\t\tif _, err := ctx.WriteWithExpiration(cacheFav, modtime); err != nil {\n\t\t\tctx.StatusCode(http.StatusInternalServerError)\n\t\t\tctx.Application().Logger().Debugf(\"while trying to serve the favicon: %s\", err.Error())\n\t\t}\n\t}\n\n\treqPath := \"/favicon\" + path.Ext(fi.Name()) // we could use the filename, but because standards is /favicon.ico\n\tif len(requestPath) > 0 && requestPath[0] != \"\" {\n\t\treqPath = requestPath[0]\n\t}\n\n\treturn api.registerResourceRoute(reqPath, h).Describe(description)\n}\n\n// OnErrorCode registers a handlers chain for this `Party` for a specific HTTP status code.\n// Read more at: http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml\n// Look `UseError` and `OnAnyErrorCode` too.\nfunc (api *APIBuilder) OnErrorCode(statusCode int, handlers ...context.Handler) (routes []*Route) {\n\troutes = append(routes, api.handle(statusCode, \"\", \"/\", handlers...))\n\n\tif api.relativePath != \"/\" {\n\t\troutes = append(routes, api.handle(statusCode, \"\", \"/{tail:path}\", handlers...))\n\t}\n\n\treturn\n}\n\n// OnAnyErrorCode registers a handlers chain for all error codes\n// (4xxx and 5xxx, change the `context.ClientErrorCodes` and `context.ServerErrorCodes` variables to modify those)\n// Look `UseError` and `OnErrorCode` too.\nfunc (api *APIBuilder) OnAnyErrorCode(handlers ...context.Handler) (routes []*Route) {\n\tfor _, statusCode := range context.ClientAndServerErrorCodes {\n\t\troutes = append(routes, api.OnErrorCode(statusCode, handlers...)...)\n\t}\n\n\tif n := len(routes); n > 1 {\n\t\tfor _, r := range routes[1:n] {\n\t\t\tr.NoLog = true\n\t\t}\n\n\t\troutes[0].Title = \"ERR\"\n\t}\n\n\treturn\n}\n\n// RegisterView registers and loads a view engine middleware for this group of routes.\n// It overrides any of the application's root registered view engines.\n// To register a view engine per handler chain see the `Context.ViewEngine` instead.\n// Read `Configuration.ViewEngineContextKey` documentation for more.\nfunc (api *APIBuilder) RegisterView(viewEngine context.ViewEngine) {\n\tif err := viewEngine.Load(); err != nil {\n\t\tapi.logger.Error(err)\n\t\treturn\n\t}\n\n\thandler := func(ctx *context.Context) {\n\t\tctx.ViewEngine(viewEngine)\n\t\tctx.Next()\n\t}\n\tapi.Use(handler)\n\tapi.UseError(handler)\n\t// Note (@kataras): It does not return the Party in order\n\t// to keep the iris.Application a compatible Party.\n}\n\n// FallbackView registers one or more fallback views for a template or a template layout.\n// Usage:\n//\n//\tFallbackView(iris.FallbackView(\"fallback.html\"))\n//\tFallbackView(iris.FallbackViewLayout(\"layouts/fallback.html\"))\n//\tOR\n//\tFallbackView(iris.FallbackViewFunc(ctx iris.Context, err iris.ErrViewNotExist) error {\n//\t  err.Name is the previous template name.\n//\t  err.IsLayout reports whether the failure came from the layout template.\n//\t  err.Data is the template data provided to the previous View call.\n//\t  [...custom logic e.g. ctx.View(\"fallback\", err.Data)]\n//\t})\nfunc (api *APIBuilder) FallbackView(provider context.FallbackViewProvider) {\n\thandler := func(ctx *context.Context) {\n\t\tctx.FallbackView(provider)\n\t\tctx.Next()\n\t}\n\tapi.Use(handler)\n\tapi.UseError(handler)\n}\n\n// Layout overrides the parent template layout with a more specific layout for this Party.\n// It returns the current Party.\n//\n// The \"tmplLayoutFile\" should be a relative path to the templates dir.\n// Usage:\n//\n// app := iris.New()\n// app.RegisterView(iris.$VIEW_ENGINE(\"./views\", \".$extension\"))\n// my := app.Party(\"/my\").Layout(\"layouts/mylayout.html\")\n//\n//\t\tmy.Get(\"/\", func(ctx iris.Context) {\n//\t\tif err := ctx.View(\"page1.html\"); err != nil {\n//\t\t  ctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n//\t\t  return\n//\t }\n//\t\t})\n//\n// Examples: https://github.com/kataras/iris/tree/main/_examples/view\nfunc (api *APIBuilder) Layout(tmplLayoutFile string) Party {\n\thandler := func(ctx *context.Context) {\n\t\tctx.ViewLayout(tmplLayoutFile)\n\t\tctx.Next()\n\t}\n\n\tapi.Use(handler)\n\tapi.UseError(handler)\n\n\treturn api\n}\n"
  },
  {
    "path": "core/router/api_builder_benchmark_test.go",
    "content": "package router\n\nimport (\n\t\"bytes\"\n\t\"math/rand\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\n\t\"github.com/kataras/golog\"\n)\n\n// randStringBytesMaskImprSrc helps us to generate random paths for the test,\n// the below piece of code is external, as an answer to a stackoverflow question.\n//\n// START.\nconst letterBytes = \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\"\nconst (\n\tletterIdxBits = 6                    // 6 bits to represent a letter index\n\tletterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits\n\tletterIdxMax  = 63 / letterIdxBits   // # of letter indices fitting in 63 bits\n)\n\nvar src = rand.NewSource(time.Now().UnixNano())\n\nfunc randStringBytesMaskImprSrc(n int) string {\n\tb := make([]byte, n)\n\t// A src.Int63() generates 63 random bits, enough for letterIdxMax characters!\n\tfor i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {\n\t\tif remain == 0 {\n\t\t\tcache, remain = src.Int63(), letterIdxMax\n\t\t}\n\t\tif idx := int(cache & letterIdxMask); idx < len(letterBytes) {\n\t\t\tb[i] = letterBytes[idx]\n\t\t\ti--\n\t\t}\n\t\tcache >>= letterIdxBits\n\t\tremain--\n\t}\n\n\treturn strings.ToLower(string(b))\n}\n\n// END.\n\nfunc genPaths(routesLength, minCharLength, maxCharLength int) []string {\n\t// b := new(strings.Builder)\n\tb := new(bytes.Buffer)\n\tpaths := make([]string, routesLength)\n\tpathStart := '/'\n\tfor i := 0; i < routesLength; i++ {\n\t\tpathSegmentCharsLength := rand.Intn(maxCharLength-minCharLength) + minCharLength\n\n\t\tb.WriteRune(pathStart)\n\t\tb.WriteString(randStringBytesMaskImprSrc(pathSegmentCharsLength))\n\t\tb.WriteString(\"/{name:string}/\") // sugar.\n\t\tb.WriteString(randStringBytesMaskImprSrc(pathSegmentCharsLength))\n\t\tb.WriteString(\"/{age:int}/end\")\n\t\tpaths[i] = b.String()\n\n\t\tb.Reset()\n\t}\n\n\treturn paths\n}\n\n// Build 1296(=144*9(the available http methods)) routes\n// with up to 2*range(15-42)+ 2 named paths lowercase letters\n// and 12 request handlers each.\n//\n// GOCACHE=off && go test -run=XXX -bench=BenchmarkAPIBuilder$ -benchtime=10s\nfunc BenchmarkAPIBuilder(b *testing.B) {\n\trand.New(rand.NewSource(time.Now().Unix()))\n\n\tnoOpHandler := func(ctx *context.Context) {}\n\thandlersPerRoute := make(context.Handlers, 12)\n\tfor i := 0; i < cap(handlersPerRoute); i++ {\n\t\thandlersPerRoute[i] = noOpHandler\n\t}\n\n\troutesLength := 144\n\t// i.e /gzhyweumidvelqewrvoyqmzopvuxli/{name:string}/bibrkratnrrhvsjwsxygfwmqwhcstc/{age:int}/end\n\tpaths := genPaths(routesLength, 15, 42)\n\n\tapi := NewAPIBuilder(golog.Default)\n\trequestHandler := NewDefaultHandler(nil, nil)\n\n\tb.ReportAllocs()\n\tb.ResetTimer()\n\n\tfor i := 0; i < routesLength; i++ {\n\t\tapi.Any(paths[i], handlersPerRoute...)\n\t}\n\n\tif err := requestHandler.Build(api); err != nil {\n\t\tb.Fatal(err)\n\t}\n\n\tb.StopTimer()\n\n\tb.Logf(\"%d routes have just builded\\n\", len(api.GetRoutes()))\n}\n"
  },
  {
    "path": "core/router/api_container.go",
    "content": "package router\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/hero\"\n\t\"github.com/kataras/iris/v12/macro\"\n)\n\n// APIContainer is a wrapper of a common `Party` featured by Dependency Injection.\n// See `Party.ConfigureContainer` for more.\ntype APIContainer struct {\n\t// Self returns the original `Party` without DI features.\n\tSelf Party\n\n\t// Container is the per-party (and its children gets a clone) DI container..\n\tContainer *hero.Container\n}\n\n// Party returns a child of this `APIContainer` featured with Dependency Injection.\n// Like the `Self.Party` method does for the common Router Groups.\nfunc (api *APIContainer) Party(relativePath string, handlersFn ...any) *APIContainer {\n\thandlers := api.convertHandlerFuncs(relativePath, handlersFn...)\n\tp := api.Self.Party(relativePath, handlers...)\n\treturn p.ConfigureContainer()\n}\n\n// PartyFunc same as `Party` but it accepts a party builder function instead.\n// Returns the new Party's APIContainer\nfunc (api *APIContainer) PartyFunc(relativePath string, fn func(*APIContainer)) *APIContainer {\n\tchildContainer := api.Party(relativePath)\n\tfn(childContainer)\n\treturn childContainer\n}\n\n// OnError adds an error handler for this Party's DI Hero Container and its handlers (or controllers).\n// The \"errorHandler\" handles any error may occurred and returned\n// during dependencies injection of the Party's hero handlers or from the handlers themselves.\n//\n// Same as:\n// Container.GetErrorHandler = func(ctx iris.Context) hero.ErrorHandler { return errorHandler }\n//\n// See `RegisterDependency`, `Use`, `Done` and `Handle` too.\nfunc (api *APIContainer) OnError(errorHandler func(*context.Context, error)) {\n\terrHandler := hero.ErrorHandlerFunc(errorHandler)\n\tapi.Container.GetErrorHandler = func(ctx *context.Context) hero.ErrorHandler {\n\t\treturn errHandler\n\t}\n}\n\n// RegisterDependency adds a dependency.\n// The value can be a single struct value or a function.\n// Follow the rules:\n// * <T>{structValue}\n// * func(accepts <T>)                                 returns <D> or (<D>, error)\n// * func(accepts iris.Context)                        returns <D> or (<D>, error)\n//\n// A Dependency can accept a previous registered dependency and return a new one or the same updated.\n// * func(accepts1 <D>, accepts2 <T>)                  returns <E> or (<E>, error) or error\n// * func(acceptsPathParameter1 string, id uint64)     returns <T> or (<T>, error)\n//\n// Usage:\n//\n// - RegisterDependency(loggerService{prefix: \"dev\"})\n// - RegisterDependency(func(ctx iris.Context) User {...})\n// - RegisterDependency(func(User) OtherResponse {...})\n//\n// See `OnError`, `Use`, `Done` and `Handle` too.\nfunc (api *APIContainer) RegisterDependency(dependency any) *hero.Dependency {\n\treturn api.Container.Register(dependency)\n}\n\n// UseResultHandler adds a result handler to the Container.\n// A result handler can be used to inject the returned struct value\n// from a request handler or to replace the default renderer.\nfunc (api *APIContainer) UseResultHandler(handler func(next hero.ResultHandler) hero.ResultHandler) *APIContainer {\n\tapi.Container.UseResultHandler(handler)\n\treturn api\n}\n\n// EnableStrictMode sets the container's DisablePayloadAutoBinding and MarkExportedFieldsAsRequired to true.\n// Meaning that all struct's  fields (or function's parameters) should be binded manually (except the path parameters).\n//\n// Note that children will clone the same properties.\n// Call the same method with `false` for children\n// to enable automatic binding on missing dependencies.\n//\n// Strict mode is disabled by default;\n// structs or path parameters that don't match to registered dependencies\n// are automatically binded from the request context (body and url path parameters respectfully).\nfunc (api *APIContainer) EnableStrictMode(strictMode bool) *APIContainer {\n\tapi.Container.DisablePayloadAutoBinding = strictMode\n\tapi.Container.MarkExportedFieldsAsRequired = strictMode\n\treturn api\n}\n\n// EnableStructDependents sets the container's EnableStructDependents to true.\n// It's used to automatically fill the dependencies of a struct's fields\n// based on the previous registered dependencies, just like function inputs.\nfunc (api *APIContainer) EnableStructDependents() *APIContainer {\n\tapi.Container.EnableStructDependents = true\n\treturn api\n}\n\n// SetDependencyMatcher replaces the function that compares equality between\n// a dependency and an input (struct field or function parameter).\n//\n// Defaults to hero.DefaultMatchDependencyFunc.\nfunc (api *APIContainer) SetDependencyMatcher(fn hero.DependencyMatcher) *APIContainer {\n\tif fn == nil {\n\t\tpanic(\"api container: set dependency matcher: fn cannot be nil\")\n\t}\n\n\tapi.Container.DependencyMatcher = fn\n\treturn api\n}\n\n// convertHandlerFuncs accepts Iris hero handlers and returns a slice of native Iris handlers.\nfunc (api *APIContainer) convertHandlerFuncs(relativePath string, handlersFn ...any) context.Handlers {\n\tfullpath := api.Self.GetRelPath() + relativePath\n\tparamsCount := macro.CountParams(fullpath, *api.Self.Macros())\n\n\thandlers := make(context.Handlers, 0, len(handlersFn))\n\tfor _, h := range handlersFn {\n\t\thandlers = append(handlers, api.Container.HandlerWithParams(h, paramsCount))\n\t}\n\n\t// Note: let end-developer to decide that through Party.SetExecutionRules.\n\t// On that type of handlers the end-developer does not have to include the Context in the handler,\n\t// so the ctx.Next is automatically called unless an `ErrStopExecution` returned (implementation inside hero pkg).\n\t//\n\t// o := ExecutionOptions{Force: true}\n\t// o.apply(&handlers)\n\n\treturn handlers\n}\n\nfunc fixRouteInfo(route *Route, handlersFn []any) {\n\t// Fix main handler name and source modified by execution rules wrapper.\n\troute.MainHandlerName, route.MainHandlerIndex = context.MainHandlerName(handlersFn...)\n\tif len(handlersFn) > route.MainHandlerIndex {\n\t\troute.SourceFileName, route.SourceLineNumber = context.HandlerFileLineRel(handlersFn[route.MainHandlerIndex])\n\t}\n}\n\n// Handler receives a function which can receive dependencies and output result\n// and returns a common Iris Handler, useful for Versioning API integration otherwise\n// the `Handle/Get/Post...` methods are preferable.\nfunc (api *APIContainer) Handler(handlerFn any, handlerParamsCount int) context.Handler {\n\tparamsCount := macro.CountParams(api.Self.GetRelPath(), *api.Self.Macros()) + handlerParamsCount\n\treturn api.Container.HandlerWithParams(handlerFn, paramsCount)\n}\n\n// Use same as `Self.Use` but it accepts dynamic functions as its \"handlersFn\" input.\n//\n// See `OnError`, `RegisterDependency`, `Done` and `Handle` for more.\nfunc (api *APIContainer) Use(handlersFn ...any) {\n\thandlers := api.convertHandlerFuncs(\"/\", handlersFn...)\n\tapi.Self.Use(handlers...)\n}\n\n// Done same as `Self.Done` but it accepts dynamic functions as its \"handlersFn\" input.\n// See `OnError`, `RegisterDependency`, `Use` and `Handle` for more.\nfunc (api *APIContainer) Done(handlersFn ...any) {\n\thandlers := api.convertHandlerFuncs(\"/\", handlersFn...)\n\tapi.Self.Done(handlers...)\n}\n\n// Handle same as `Self.Handle` but it accepts one or more \"handlersFn\" functions which each one of them\n// can accept any input arguments that match with the Party's registered Container's `Dependencies` and\n// any output result; like custom structs <T>, string, []byte, int, error,\n// a combination of the above, hero.Result(hero.View | hero.Response) and more.\n//\n// It's common from a hero handler to not even need to accept a `Context`, for that reason,\n// the \"handlersFn\" will call `ctx.Next()` automatically when not called manually.\n// To stop the execution and not continue to the next \"handlersFn\"\n// the end-developer should output an error and return `iris.ErrStopExecution`.\n//\n// See `OnError`, `RegisterDependency`, `Use`, `Done`, `Get`, `Post`, `Put`, `Patch` and `Delete` too.\nfunc (api *APIContainer) Handle(method, relativePath string, handlersFn ...any) *Route {\n\thandlers := api.convertHandlerFuncs(relativePath, handlersFn...)\n\troute := api.Self.Handle(method, relativePath, handlers...)\n\tfixRouteInfo(route, handlersFn)\n\treturn route\n}\n\n// Get registers a route for the Get HTTP Method.\n//\n// Returns a *Route and an error which will be filled if route wasn't registered successfully.\nfunc (api *APIContainer) Get(relativePath string, handlersFn ...any) *Route {\n\treturn api.Handle(http.MethodGet, relativePath, handlersFn...)\n}\n\n// Post registers a route for the Post HTTP Method.\n//\n// Returns a *Route and an error which will be filled if route wasn't registered successfully.\nfunc (api *APIContainer) Post(relativePath string, handlersFn ...any) *Route {\n\treturn api.Handle(http.MethodPost, relativePath, handlersFn...)\n}\n\n// Put registers a route for the Put HTTP Method.\n//\n// Returns a *Route and an error which will be filled if route wasn't registered successfully.\nfunc (api *APIContainer) Put(relativePath string, handlersFn ...any) *Route {\n\treturn api.Handle(http.MethodPut, relativePath, handlersFn...)\n}\n\n// Delete registers a route for the Delete HTTP Method.\n//\n// Returns a *Route and an error which will be filled if route wasn't registered successfully.\nfunc (api *APIContainer) Delete(relativePath string, handlersFn ...any) *Route {\n\treturn api.Handle(http.MethodDelete, relativePath, handlersFn...)\n}\n\n// Connect registers a route for the Connect HTTP Method.\n//\n// Returns a *Route and an error which will be filled if route wasn't registered successfully.\nfunc (api *APIContainer) Connect(relativePath string, handlersFn ...any) *Route {\n\treturn api.Handle(http.MethodConnect, relativePath, handlersFn...)\n}\n\n// Head registers a route for the Head HTTP Method.\n//\n// Returns a *Route and an error which will be filled if route wasn't registered successfully.\nfunc (api *APIContainer) Head(relativePath string, handlersFn ...any) *Route {\n\treturn api.Handle(http.MethodHead, relativePath, handlersFn...)\n}\n\n// Options registers a route for the Options HTTP Method.\n//\n// Returns a *Route and an error which will be filled if route wasn't registered successfully.\nfunc (api *APIContainer) Options(relativePath string, handlersFn ...any) *Route {\n\treturn api.Handle(http.MethodOptions, relativePath, handlersFn...)\n}\n\n// Patch registers a route for the Patch HTTP Method.\n//\n// Returns a *Route and an error which will be filled if route wasn't registered successfully.\nfunc (api *APIContainer) Patch(relativePath string, handlersFn ...any) *Route {\n\treturn api.Handle(http.MethodPatch, relativePath, handlersFn...)\n}\n\n// Trace registers a route for the Trace HTTP Method.\n//\n// Returns a *Route and an error which will be filled if route wasn't registered successfully.\nfunc (api *APIContainer) Trace(relativePath string, handlersFn ...any) *Route {\n\treturn api.Handle(http.MethodTrace, relativePath, handlersFn...)\n}\n\n// Any registers a route for ALL of the HTTP methods:\n// Get\n// Post\n// Put\n// Delete\n// Head\n// Patch\n// Options\n// Connect\n// Trace\nfunc (api *APIContainer) Any(relativePath string, handlersFn ...any) (routes []*Route) {\n\thandlers := api.convertHandlerFuncs(relativePath, handlersFn...)\n\n\tfor _, m := range AllMethods {\n\t\tr := api.Self.HandleMany(m, relativePath, handlers...)\n\t\troutes = append(routes, r...)\n\t}\n\n\treturn\n}\n\n/* TODO: fix those\n\n// OnErrorCode registers a handlers chain for this `Party` for a specific HTTP status code.\n// Read more at: http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml\n// Look `OnAnyErrorCode` too.\nfunc (api *APIContainer) OnErrorCode(statusCode int, handlersFn ...any) []*Route {\n\thandlers := api.convertHandlerFuncs(\"/{tail:path}\", handlersFn...)\n\treturn api.Self.OnErrorCode(statusCode, handlers...)\n}\n\n// OnAnyErrorCode registers a handlers chain for all error codes\n// (4xxx and 5xxx, change the `ClientErrorCodes` and `ServerErrorCodes` variables to modify those)\n// Look `OnErrorCode` too.\nfunc (api *APIContainer) OnAnyErrorCode(handlersFn ...any) []*Route {\n\thandlers := api.convertHandlerFuncs(\"/{tail:path}\", handlersFn...)\n\treturn api.Self.OnAnyErrorCode(handlers...)\n}\n*/\n"
  },
  {
    "path": "core/router/fs.go",
    "content": "package router\n\nimport (\n\t\"bytes\"\n\tstdContext \"context\"\n\t\"fmt\"\n\t\"html\"\n\t\"html/template\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"regexp\"\n\t\"runtime\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/context\"\n)\n\nconst indexName = \"/index.html\"\n\n// DirListFunc is the function signature for customizing directory and file listing.\n// See `DirList` and `DirListRich` functions for its implementations.\ntype DirListFunc func(ctx *context.Context, dirOptions DirOptions, dirName string, dir http.File) error\n\n// Attachments options for files to be downloaded and saved locally by the client.\n// See `DirOptions`.\ntype Attachments struct {\n\t// Set to true to enable the files to be downloaded and\n\t// saved locally by the client, instead of serving the file.\n\tEnable bool\n\t// Options to send files with a limit of bytes sent per second.\n\tLimit float64\n\tBurst int\n\t// Use this function to change the sent filename.\n\tNameFunc func(systemName string) (attachmentName string)\n}\n\n// DirCacheOptions holds the options for the cached file system.\n// See `DirOptions`structure for more.\ntype DirCacheOptions struct {\n\t// Enable or disable cache.\n\tEnable bool\n\t// Minimum content size for compression in bytes.\n\tCompressMinSize int64\n\t// Ignore compress files that match this pattern.\n\tCompressIgnore *regexp.Regexp\n\t// The available sever's encodings to be negotiated with the client's needs,\n\t// common values: gzip, br.\n\tEncodings []string\n\n\t// If greater than zero then prints information about cached files to the stdout.\n\t// If it's 1 then it prints only the total cached and after-compression reduced file sizes\n\t// If it's 2 then it prints it per file too.\n\tVerbose uint8\n}\n\n// DirOptions contains the settings that `FileServer` can use to serve files.\n// See `DefaultDirOptions`.\ntype DirOptions struct {\n\t// Defaults to \"/index.html\", if request path is ending with **/*/$IndexName\n\t// then it redirects to **/*(/).\n\t// That index handler is registered automatically\n\t// by the framework unless but it can be overridden.\n\tIndexName string\n\t// PushTargets filenames (map's value) to\n\t// be served without additional client's requests (HTTP/2 Push)\n\t// when a specific request path (map's key WITHOUT prefix)\n\t// is requested and it's not a directory (it's an `IndexFile`).\n\t//\n\t// Example:\n\t// \t\"/\": {\n\t// \t\t\"favicon.ico\",\n\t// \t\t\"js/main.js\",\n\t// \t\t\"css/main.css\",\n\t// \t}\n\tPushTargets map[string][]string\n\t// PushTargetsRegexp like `PushTargets` but accepts regexp which\n\t// is compared against all files under a directory (recursively).\n\t// The `IndexName` should be set.\n\t//\n\t// Example:\n\t// \"/\": regexp.MustCompile(\"((.*).js|(.*).css|(.*).ico)$\")\n\t// See `iris.MatchCommonAssets` too.\n\tPushTargetsRegexp map[string]*regexp.Regexp\n\n\t// Cache to enable in-memory cache and pre-compress files.\n\tCache DirCacheOptions\n\t// When files should served under compression.\n\tCompress bool\n\n\t// List the files inside the current requested\n\t// directory if `IndexName` not found.\n\tShowList bool\n\t// If `ShowList` is true then this function will be used instead\n\t// of the default one to show the list of files\n\t// of a current requested directory(dir).\n\t// See `DirListRich` package-level function too.\n\tDirList DirListFunc\n\n\t// Show hidden files or directories or not when `ShowList` is true.\n\tShowHidden bool\n\n\t// Files downloaded and saved locally.\n\tAttachments Attachments\n\n\t// Optional validator that loops through each requested resource.\n\tAssetValidator func(ctx *context.Context, name string) bool\n\t// If enabled then the router will render the index file on any not-found file\n\t// instead of firing the 404 error code handler.\n\t// Make sure the `IndexName` field is set.\n\t//\n\t// Usage:\n\t//  app.HandleDir(\"/\", iris.Dir(\"./public\"), iris.DirOptions{\n\t// \t IndexName: \"index.html\",\n\t// \t SPA:       true,\n\t//  })\n\tSPA bool\n}\n\n// DefaultDirOptions holds the default settings for `FileServer`.\nvar DefaultDirOptions = DirOptions{\n\tIndexName:         indexName,\n\tPushTargets:       make(map[string][]string),\n\tPushTargetsRegexp: make(map[string]*regexp.Regexp),\n\tCache: DirCacheOptions{\n\t\t// Disable by-default.\n\t\tEnable: false,\n\t\t// Don't compress files smaller than 300 bytes.\n\t\tCompressMinSize: 300,\n\t\t// Gzip, deflate, br(brotli), snappy.\n\t\tEncodings: context.AllEncodings,\n\t\t// Log to the stdout (no iris logger) the total reduced file size.\n\t\tVerbose: 1,\n\t},\n\tCompress: true,\n\tShowList: false,\n\tDirList: DirListRich(DirListRichOptions{\n\t\tTmpl:     DirListRichTemplate,\n\t\tTmplName: \"dirlist\",\n\t}),\n\tAttachments: Attachments{\n\t\tEnable: false,\n\t\tLimit:  0,\n\t\tBurst:  0,\n\t},\n\tAssetValidator: nil,\n\tSPA:            false,\n}\n\n// FileServer returns a Handler which serves files from a specific file system.\n// The first parameter is the file system,\n// if it's a `http.Dir` the files should be located near the executable program.\n// The second parameter is the settings that the caller can use to customize the behavior.\n//\n// See `Party#HandleDir` too.\n// Examples can be found at: https://github.com/kataras/iris/tree/main/_examples/file-server\nfunc FileServer(fs http.FileSystem, options DirOptions) context.Handler {\n\tif fs == nil {\n\t\tpanic(\"FileServer: fs is nil. The fs parameter should point to a file system of physical system directory or to an embedded one\")\n\t}\n\n\t// Make sure index name starts with a slash.\n\tif options.IndexName != \"\" {\n\t\toptions.IndexName = prefix(options.IndexName, \"/\")\n\t}\n\n\t// Make sure PushTarget's paths are in the proper form.\n\tfor path, filenames := range options.PushTargets {\n\t\tfor idx, filename := range filenames {\n\t\t\tfilenames[idx] = filepath.ToSlash(filename)\n\t\t}\n\t\toptions.PushTargets[path] = filenames\n\t}\n\n\tif !options.Attachments.Enable {\n\t\t// make sure rate limiting is not used when attachments are not.\n\t\toptions.Attachments.Limit = 0\n\t\toptions.Attachments.Burst = 0\n\t}\n\n\tplainStatusCode := func(ctx *context.Context, statusCode int) {\n\t\tif writer, ok := ctx.ResponseWriter().(*context.CompressResponseWriter); ok {\n\t\t\twriter.Disabled = true\n\t\t}\n\t\tctx.StatusCode(statusCode)\n\t}\n\n\tdirList := options.DirList\n\tif dirList == nil {\n\t\tdirList = DirList\n\t}\n\n\topen := fsOpener(fs, options.Cache) // We only need its opener, the \"fs\" is NOT used below.\n\n\th := func(ctx *context.Context) {\n\t\tr := ctx.Request()\n\t\tname := prefix(r.URL.Path, \"/\")\n\t\tr.URL.Path = name\n\n\t\tvar (\n\t\t\tindexFound bool\n\t\t\tnoRedirect bool\n\t\t)\n\n\t\tf, err := open(name, r)\n\t\tif err != nil {\n\t\t\tif options.SPA && name != options.IndexName {\n\t\t\t\toldname := name\n\t\t\t\tname = prefix(options.IndexName, \"/\") // to match push targets.\n\t\t\t\tr.URL.Path = name\n\t\t\t\tf, err = open(name, r) // try find the main index.\n\t\t\t\tif err != nil {\n\t\t\t\t\tr.URL.Path = oldname\n\t\t\t\t\tplainStatusCode(ctx, http.StatusNotFound)\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tindexFound = true // to support push targets.\n\t\t\t\tnoRedirect = true // to disable redirecting back to /.\n\t\t\t} else {\n\t\t\t\tplainStatusCode(ctx, http.StatusNotFound)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tdefer f.Close()\n\n\t\tinfo, err := f.Stat()\n\t\tif err != nil {\n\t\t\tplainStatusCode(ctx, http.StatusNotFound)\n\t\t\treturn\n\t\t}\n\n\t\t// use contents of index.html for directory, if present\n\t\tif info.IsDir() && options.IndexName != \"\" {\n\t\t\t// Note that, in contrast of the default net/http mechanism;\n\t\t\t// here different handlers may serve the indexes\n\t\t\t// if manually then this will block will never fire,\n\t\t\t// if index handler are automatically registered by the framework\n\t\t\t// then this block will be fired on indexes because the static site routes are registered using the static route's handler.\n\t\t\t//\n\t\t\t// End-developers must have the chance to register different logic and middlewares\n\t\t\t// to an index file, useful on Single Page Applications.\n\n\t\t\tindex := strings.TrimSuffix(name, \"/\") + options.IndexName\n\t\t\tfIndex, err := open(index, r)\n\t\t\tif err == nil {\n\t\t\t\tdefer fIndex.Close()\n\t\t\t\tinfoIndex, err := fIndex.Stat()\n\t\t\t\tif err == nil {\n\t\t\t\t\tindexFound = true\n\t\t\t\t\tf = fIndex\n\t\t\t\t\tinfo = infoIndex\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Still a directory? (we didn't find an index.html file)\n\t\tif info.IsDir() {\n\t\t\tif !options.ShowList {\n\t\t\t\tplainStatusCode(ctx, http.StatusNotFound)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif modified, err := ctx.CheckIfModifiedSince(info.ModTime()); !modified && err == nil {\n\t\t\t\tctx.WriteNotModified()\n\t\t\t\tctx.StatusCode(http.StatusNotModified)\n\t\t\t\tctx.Next()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tctx.SetLastModified(info.ModTime())\n\t\t\terr = dirList(ctx, options, info.Name(), f)\n\t\t\tif err != nil {\n\t\t\t\tctx.Application().Logger().Errorf(\"FileServer: dirList: %v\", err)\n\t\t\t\tplainStatusCode(ctx, http.StatusInternalServerError)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tctx.Next()\n\t\t\treturn\n\t\t}\n\n\t\t// index requested, send a moved permanently status\n\t\t// and navigate back to the route without the index suffix.\n\t\tif !noRedirect && options.IndexName != \"\" && strings.HasSuffix(name, options.IndexName) {\n\t\t\tlocalRedirect(ctx, \"./\")\n\t\t\treturn\n\t\t}\n\n\t\tif options.AssetValidator != nil {\n\t\t\tif !options.AssetValidator(ctx, name) {\n\t\t\t\terrCode := ctx.GetStatusCode()\n\t\t\t\tif ctx.ResponseWriter().Written() <= context.StatusCodeWritten {\n\t\t\t\t\t// if nothing written as body from the AssetValidator but 200 status code(which is the default),\n\t\t\t\t\t// then we assume that the end-developer just returned false expecting this to be not found.\n\t\t\t\t\tif errCode == http.StatusOK {\n\t\t\t\t\t\terrCode = http.StatusNotFound\n\t\t\t\t\t}\n\t\t\t\t\tplainStatusCode(ctx, errCode)\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\t// try to find and send the correct content type based on the filename\n\t\t// and the binary data inside \"f\".\n\t\tdetectOrWriteContentType(ctx, info.Name(), f)\n\n\t\t// if not index file and attachments should be force-sent:\n\t\tif !indexFound && options.Attachments.Enable {\n\t\t\tdestName := info.Name()\n\t\t\t// diposition := \"attachment\"\n\t\t\tif nameFunc := options.Attachments.NameFunc; nameFunc != nil {\n\t\t\t\tdestName = nameFunc(destName)\n\t\t\t}\n\n\t\t\tctx.ResponseWriter().Header().Set(context.ContentDispositionHeaderKey, context.MakeDisposition(destName))\n\t\t}\n\n\t\t// the encoding saved from the negotiation.\n\t\tencoding, isCached := getFileEncoding(f)\n\t\tif isCached {\n\t\t\t// if it's cached and its settings didnt allow this file to be compressed\n\t\t\t// then don't try to compress it on the fly, even if the options.Compress was set to true.\n\t\t\tif encoding != \"\" {\n\t\t\t\tif ctx.ResponseWriter().Header().Get(context.ContentEncodingHeaderKey) != \"\" {\n\t\t\t\t\t// disable any compression writer if that header exist,\n\t\t\t\t\t// note that, we don't directly check for CompressResponseWriter type\n\t\t\t\t\t// because it may be a ResponseRecorder.\n\t\t\t\t\tctx.CompressWriter(false)\n\t\t\t\t}\n\t\t\t\t// Set the response header we need, the data are already compressed.\n\t\t\t\tcontext.AddCompressHeaders(ctx.ResponseWriter().Header(), encoding)\n\t\t\t}\n\t\t} else if options.Compress {\n\t\t\tctx.CompressWriter(true)\n\t\t}\n\n\t\tif indexFound && !options.Attachments.Enable {\n\t\t\tif indexAssets, ok := options.PushTargets[name]; ok {\n\t\t\t\tif pusher, ok := ctx.ResponseWriter().Naive().(http.Pusher); ok {\n\t\t\t\t\tvar pushOpts *http.PushOptions\n\t\t\t\t\tif encoding != \"\" {\n\t\t\t\t\t\tpushOpts = &http.PushOptions{Header: r.Header}\n\t\t\t\t\t}\n\n\t\t\t\t\tfor _, indexAsset := range indexAssets {\n\t\t\t\t\t\tif indexAsset[0] != '/' {\n\t\t\t\t\t\t\t// it's relative path.\n\t\t\t\t\t\t\tindexAsset = path.Join(r.RequestURI, indexAsset)\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif err = pusher.Push(indexAsset, pushOpts); err != nil {\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif regex, ok := options.PushTargetsRegexp[r.URL.Path]; ok {\n\t\t\t\tif pusher, ok := ctx.ResponseWriter().Naive().(http.Pusher); ok {\n\t\t\t\t\tvar pushOpts *http.PushOptions\n\t\t\t\t\tif encoding != \"\" {\n\t\t\t\t\t\tpushOpts = &http.PushOptions{Header: r.Header}\n\t\t\t\t\t}\n\n\t\t\t\t\tprefixURL := strings.TrimSuffix(r.RequestURI, name)\n\t\t\t\t\tnames, err := context.FindNames(fs, name)\n\t\t\t\t\tif err == nil {\n\t\t\t\t\t\tfor _, indexAsset := range names {\n\t\t\t\t\t\t\t// it's an index file, do not pushed that.\n\t\t\t\t\t\t\tif strings.HasSuffix(prefix(indexAsset, \"/\"), options.IndexName) {\n\t\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// match using relative path (without the first '/' slash)\n\t\t\t\t\t\t\t// to keep consistency between the `PushTargets` behavior\n\t\t\t\t\t\t\tif regex.MatchString(indexAsset) {\n\t\t\t\t\t\t\t\t// println(\"Regex Matched: \" + indexAsset)\n\t\t\t\t\t\t\t\tif err = pusher.Push(path.Join(prefixURL, indexAsset), pushOpts); err != nil {\n\t\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// If limit is 0 then same as ServeContent.\n\t\tctx.ServeContentWithRate(f, info.Name(), info.ModTime(), options.Attachments.Limit, options.Attachments.Burst)\n\t\tif serveCode := ctx.GetStatusCode(); context.StatusCodeNotSuccessful(serveCode) {\n\t\t\tplainStatusCode(ctx, serveCode)\n\t\t\treturn\n\t\t}\n\n\t\tctx.Next() // fire any middleware, if any.\n\t}\n\n\treturn h\n}\n\n// StripPrefix returns a handler that serves HTTP requests\n// by removing the given prefix from the request URL's Path\n// and invoking the handler h. StripPrefix handles a\n// request for a path that doesn't begin with prefix by\n// replying with an HTTP 404 not found error.\n//\n// Usage:\n// fileserver := FileServer(\"./static_files\", DirOptions {...})\n// h := StripPrefix(\"/static\", fileserver)\n// app.Get(\"/static/{file:path}\", h)\n// app.Head(\"/static/{file:path}\", h)\nfunc StripPrefix(prefix string, h context.Handler) context.Handler {\n\tif prefix == \"\" {\n\t\treturn h\n\t}\n\t// here we separate the path from the subdomain (if any), we care only for the path\n\t// fixes a bug when serving static files via a subdomain\n\tcanonicalPrefix := prefix\n\tif dotWSlashIdx := strings.Index(canonicalPrefix, SubdomainPrefix); dotWSlashIdx > 0 {\n\t\tcanonicalPrefix = canonicalPrefix[dotWSlashIdx+1:]\n\t}\n\tcanonicalPrefix = toWebPath(canonicalPrefix)\n\n\treturn func(ctx *context.Context) {\n\t\tu := ctx.Request().URL\n\t\tif p := strings.TrimPrefix(u.Path, canonicalPrefix); len(p) < len(u.Path) {\n\t\t\tif p == \"\" {\n\t\t\t\tp = \"/\"\n\t\t\t}\n\t\t\tu.Path = p\n\t\t\th(ctx)\n\t\t} else {\n\t\t\tctx.NotFound()\n\t\t}\n\t}\n}\n\nfunc toWebPath(systemPath string) string {\n\t// winos slash to slash\n\twebpath := strings.ReplaceAll(systemPath, \"\\\\\", \"/\")\n\t// double slashes to single\n\twebpath = strings.ReplaceAll(webpath, \"//\", \"/\")\n\treturn webpath\n}\n\n// Abs calls filepath.Abs but ignores the error and\n// returns the original value if any error occurred.\nfunc Abs(path string) string {\n\tabsPath, err := filepath.Abs(path)\n\tif err != nil {\n\t\treturn path\n\t}\n\treturn absPath\n}\n\n// The algorithm uses at most sniffLen bytes to make its decision.\nconst sniffLen = 512\n\nfunc detectOrWriteContentType(ctx *context.Context, name string, content io.ReadSeeker) (string, error) {\n\t// If Content-Type isn't set, use the file's extension to find it, but\n\t// if the Content-Type is unset explicitly, do not sniff the type.\n\tctypes, haveType := ctx.ResponseWriter().Header()[\"Content-Type\"]\n\tvar ctype string\n\n\tif !haveType {\n\t\tctype = TypeByExtension(filepath.Ext(name))\n\t\tif ctype == \"\" {\n\t\t\t// read a chunk to decide between utf-8 text and binary\n\t\t\tvar buf [sniffLen]byte\n\t\t\tn, _ := io.ReadFull(content, buf[:])\n\t\t\tctype = http.DetectContentType(buf[:n])\n\t\t\t_, err := content.Seek(0, io.SeekStart) // rewind to output whole file\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t}\n\n\t\tctx.ContentType(ctype)\n\t} else if len(ctypes) > 0 {\n\t\tctype = ctypes[0]\n\t}\n\n\treturn ctype, nil\n}\n\n// localRedirect gives a Moved Permanently response.\n// It does not convert relative paths to absolute paths like Redirect does.\nfunc localRedirect(ctx *context.Context, newPath string) {\n\tif q := ctx.Request().URL.RawQuery; q != \"\" {\n\t\tnewPath += \"?\" + q\n\t}\n\n\tctx.Header(\"Location\", newPath)\n\tctx.StatusCode(http.StatusMovedPermanently)\n}\n\n// DirectoryExists returns true if a directory(or file) exists, otherwise false\nfunc DirectoryExists(dir string) bool {\n\tif _, err := os.Stat(dir); os.IsNotExist(err) {\n\t\treturn false\n\t}\n\treturn true\n}\n\n// Instead of path.Base(filepath.ToSlash(s))\n// let's do something like that, it is faster\n// (used to list directories on serve-time too):\nfunc toBaseName(s string) string {\n\tn := len(s) - 1\n\tfor i := n; i >= 0; i-- {\n\t\tif c := s[i]; c == '/' || c == '\\\\' {\n\t\t\tif i == n {\n\t\t\t\t// \"s\" ends with a slash, remove it and retry.\n\t\t\t\treturn toBaseName(s[:n])\n\t\t\t}\n\n\t\t\treturn s[i+1:] // return the rest, trimming the slash.\n\t\t}\n\t}\n\n\treturn s\n}\n\n// IsHidden checks a file is hidden or not\nfunc IsHidden(file os.FileInfo) bool {\n\tisHidden := false\n\tif runtime.GOOS == \"windows\" {\n\t\tfa := reflect.ValueOf(file.Sys()).Elem().FieldByName(\"FileAttributes\").Uint()\n\t\tbytefa := []byte(strconv.FormatUint(fa, 2))\n\t\tif bytefa[len(bytefa)-2] == '1' {\n\t\t\tisHidden = true\n\t\t}\n\t} else {\n\t\tisHidden = file.Name()[0] == '.'\n\t}\n\n\treturn isHidden\n}\n\n// DirList is a `DirListFunc` which renders directories and files in html, but plain, mode.\n// See `DirListRich` for more.\nfunc DirList(ctx *context.Context, dirOptions DirOptions, dirName string, dir http.File) error {\n\tdirs, err := dir.Readdir(-1)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tsort.Slice(dirs, func(i, j int) bool { return dirs[i].Name() < dirs[j].Name() })\n\n\tctx.ContentType(context.ContentHTMLHeaderValue)\n\t_, err = ctx.WriteString(\"<div>\\n\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// show current directory\n\t_, err = ctx.Writef(\"<h2>Current Directory: %s</h2>\", ctx.Request().RequestURI)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t_, err = ctx.WriteString(\"<ul style=\\\"list-style: none; padding-left: 20px\\\">\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// link to parent directory\n\t_, err = ctx.WriteString(\"<li><span style=\\\"width: 150px; float: left; display: inline-block;\\\">drwxrwxrwx</span><a href=\\\"./\\\">../</a><li>\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, d := range dirs {\n\t\tif !dirOptions.ShowHidden && IsHidden(d) {\n\t\t\tcontinue\n\t\t}\n\n\t\tname := toBaseName(d.Name())\n\n\t\tu, err := url.Parse(ctx.Request().RequestURI) // clone url and remove query (#1882).\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"name: %s: error: %w\", name, err)\n\t\t}\n\t\tu.RawQuery = \"\"\n\n\t\tupath := url.URL{Path: path.Join(u.String(), name)}\n\n\t\tdownloadAttr := \"\"\n\t\tif dirOptions.Attachments.Enable && !d.IsDir() {\n\t\t\tdownloadAttr = \" download\" // fixes chrome Resource interpreted, other browsers will just ignore this <a> attribute.\n\t\t}\n\n\t\tviewName := name\n\t\tif d.IsDir() {\n\t\t\tviewName += \"/\"\n\t\t}\n\n\t\t// name may contain '?' or '#', which must be escaped to remain\n\t\t// part of the URL path, and not indicate the start of a query\n\t\t// string or fragment.\n\t\t_, err = ctx.Writef(\"<li>\"+\n\t\t\t\"<span style=\\\"width: 150px; float: left; display: inline-block;\\\">%s</span>\"+\n\t\t\t\"<a href=\\\"%s\\\"%s>%s</a>\"+\n\t\t\t\"</li>\",\n\t\t\td.Mode().String(), upath.String(), downloadAttr, html.EscapeString(viewName))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\t_, err = ctx.WriteString(\"</ul></div>\\n\")\n\treturn err\n}\n\n// DirListRichOptions the options for the `DirListRich` helper function.\ntype DirListRichOptions struct {\n\t// If not nil then this template's \"dirlist\" is used to render the listing page.\n\tTmpl *template.Template\n\t// If not empty then this view file is used to render the listing page.\n\t// The view should be registered with `Application.RegisterView`.\n\t// E.g. \"dirlist.html\"\n\tTmplName string\n}\n\n// DirListRich is a `DirListFunc` which can be passed to `DirOptions.DirList` field\n// to override the default file listing appearance.\n// See `DirListRichTemplate` to modify the template, if necessary.\nfunc DirListRich(opts ...DirListRichOptions) DirListFunc {\n\tvar options DirListRichOptions\n\tif len(opts) > 0 {\n\t\toptions = opts[0]\n\t}\n\tif options.TmplName == \"\" && options.Tmpl == nil {\n\t\toptions.Tmpl = DirListRichTemplate\n\t}\n\n\treturn func(ctx *context.Context, dirOptions DirOptions, dirName string, dir http.File) error {\n\t\tdirs, err := dir.Readdir(-1)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tsortBy := ctx.URLParam(\"sort\")\n\t\tswitch sortBy {\n\t\tcase \"name\":\n\t\t\tsort.Slice(dirs, func(i, j int) bool { return dirs[i].Name() < dirs[j].Name() })\n\t\tcase \"size\":\n\t\t\tsort.Slice(dirs, func(i, j int) bool { return dirs[i].Size() < dirs[j].Size() })\n\t\tdefault:\n\t\t\tsort.Slice(dirs, func(i, j int) bool { return dirs[i].ModTime().After(dirs[j].ModTime()) })\n\t\t}\n\n\t\tpageData := listPageData{\n\t\t\tTitle: fmt.Sprintf(\"List of %d files\", len(dirs)),\n\t\t\tFiles: make([]fileInfoData, 0, len(dirs)),\n\t\t}\n\n\t\tfor _, d := range dirs {\n\t\t\tif !dirOptions.ShowHidden && IsHidden(d) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tname := toBaseName(d.Name())\n\n\t\t\tu, err := url.Parse(ctx.Request().RequestURI) // clone url and remove query (#1882).\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"name: %s: error: %w\", name, err)\n\t\t\t}\n\t\t\tu.RawQuery = \"\"\n\n\t\t\tupath := url.URL{Path: path.Join(u.String(), name)}\n\n\t\t\tviewName := name\n\t\t\tif d.IsDir() {\n\t\t\t\tviewName += \"/\"\n\t\t\t}\n\n\t\t\tshouldDownload := dirOptions.Attachments.Enable && !d.IsDir()\n\t\t\tpageData.Files = append(pageData.Files, fileInfoData{\n\t\t\t\tInfo:     d,\n\t\t\t\tModTime:  d.ModTime().UTC().Format(http.TimeFormat),\n\t\t\t\tPath:     upath.String(),\n\t\t\t\tRelPath:  path.Join(ctx.Path(), name),\n\t\t\t\tName:     html.EscapeString(viewName),\n\t\t\t\tDownload: shouldDownload,\n\t\t\t})\n\t\t}\n\n\t\tif options.TmplName != \"\" {\n\t\t\treturn ctx.View(options.TmplName, pageData)\n\t\t}\n\n\t\treturn options.Tmpl.ExecuteTemplate(ctx, \"dirlist\", pageData)\n\t}\n}\n\ntype (\n\tlistPageData struct {\n\t\tTitle string // the document's title.\n\t\tFiles []fileInfoData\n\t}\n\n\tfileInfoData struct {\n\t\tInfo     os.FileInfo\n\t\tModTime  string // format-ed time.\n\t\tPath     string // the request path.\n\t\tRelPath  string // file path without the system directory itself (we are not exposing it to the user).\n\t\tName     string // the html-escaped name.\n\t\tDownload bool   // the file should be downloaded (attachment instead of inline view).\n\t}\n)\n\n// FormatBytes returns a string representation of the \"b\" bytes.\nfunc FormatBytes(b int64) string {\n\tconst unit = 1000\n\tif b < unit {\n\t\treturn fmt.Sprintf(\"%d B\", b)\n\t}\n\tdiv, exp := int64(unit), 0\n\tfor n := b / unit; n >= unit; n /= unit {\n\t\tdiv *= unit\n\t\texp++\n\t}\n\treturn fmt.Sprintf(\"%.1f %cB\",\n\t\tfloat64(b)/float64(div), \"kMGTPE\"[exp])\n}\n\n// DirListRichTemplate is the html template the `DirListRich` function is using to render\n// the directories and files.\nvar DirListRichTemplate = template.Must(template.New(\"dirlist\").\n\tFuncs(template.FuncMap{\n\t\t\"formatBytes\": FormatBytes,\n\t}).Parse(`\n<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n\t<title>{{.Title}}</title>\n    <style>\n        a {\n            padding: 8px 8px;\n            text-decoration:none;\n            cursor:pointer;\n            color: #10a2ff;\n        }\n        table {\n            position: absolute;\n            top: 0;\n            bottom: 0;\n            left: 0;\n            right: 0;\n            height: 100%;\n            width: 100%;\n            border-collapse: collapse;\n            border-spacing: 0;\n            empty-cells: show;\n            border: 1px solid #cbcbcb;\n        }\n        \n        table caption {\n            color: #000;\n            font: italic 85%/1 arial, sans-serif;\n            padding: 1em 0;\n            text-align: center;\n        }\n        \n        table td,\n        table th {\n            border-left: 1px solid #cbcbcb;\n            border-width: 0 0 0 1px;\n            font-size: inherit;\n            margin: 0;\n            overflow: visible;\n            padding: 0.5em 1em;\n        }\n        \n        table thead {\n            background-color: #10a2ff;\n            color: #fff;\n            text-align: left;\n            vertical-align: bottom;\n        }\n        \n        table td {\n            background-color: transparent;\n        }\n\n        .table-odd td {\n            background-color: #f2f2f2;\n        }\n\n        .table-bordered td {\n            border-bottom: 1px solid #cbcbcb;\n        }\n        .table-bordered tbody > tr:last-child > td {\n            border-bottom-width: 0;\n        }\n\t</style>\n</head>\n<body>\n    <table class=\"table-bordered table-odd\">\n        <thead>\n            <tr>\n                <th>#</th>\n                <th>Name</th>\n\t\t\t\t<th>Size</th>\n            </tr>\n        </thead>\n        <tbody>\n            {{ range $idx, $file := .Files }}\n            <tr>\n                <td>{{ $idx }}</td>\n                {{ if $file.Download }}\n                <td><a href=\"{{ $file.Path }}\" title=\"{{ $file.ModTime }}\" download>{{ $file.Name }}</a></td> \n                {{ else }}\n                <td><a href=\"{{ $file.Path }}\" title=\"{{ $file.ModTime }}\">{{ $file.Name }}</a></td>\n                {{ end }}\n\t\t\t\t{{ if $file.Info.IsDir }}\n\t\t\t\t<td>Dir</td>\n\t\t\t\t{{ else }}\n\t\t\t\t<td>{{ formatBytes $file.Info.Size }}</td>\n\t\t\t\t{{ end }}\n            </tr>\n            {{ end }}\n        </tbody>\n\t</table>\n</body></html>\n`))\n\n// fsOpener returns the file system opener, cached one or the original based on the options Enable field.\nfunc fsOpener(fs http.FileSystem, options DirCacheOptions) func(name string, r *http.Request) (http.File, error) {\n\tif !options.Enable {\n\t\t// if it's not enabled return the opener original one.\n\t\treturn func(name string, _ *http.Request) (http.File, error) {\n\t\t\treturn fs.Open(name)\n\t\t}\n\t}\n\n\tc, err := cache(fs, options)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn c.Ropen\n}\n\n// cache returns a http.FileSystem which serves in-memory cached (compressed) files.\n// Look `Verbose` function to print out information while in development status.\nfunc cache(fs http.FileSystem, options DirCacheOptions) (*cacheFS, error) {\n\tstart := time.Now()\n\n\tnames, err := context.FindNames(fs, \"/\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tsort.Slice(names, func(i, j int) bool {\n\t\treturn strings.Count(names[j], \"/\") > strings.Count(names[i], \"/\")\n\t})\n\n\tdirs, err := findDirs(fs, names)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tfiles, err := cacheFiles(stdContext.Background(), fs, names,\n\t\toptions.Encodings, options.CompressMinSize, options.CompressIgnore)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tttc := time.Since(start)\n\n\tc := &cacheFS{dirs: dirs, files: files, algs: options.Encodings}\n\tgo logCacheFS(c, ttc, len(names), options.Verbose)\n\n\treturn c, nil\n}\n\nfunc logCacheFS(fs *cacheFS, ttc time.Duration, n int, level uint8) {\n\tif level == 0 {\n\t\treturn\n\t}\n\n\tvar (\n\t\ttotalLength             int64\n\t\ttotalCompressedLength   = make(map[string]int64)\n\t\ttotalCompressedContents int64\n\t)\n\n\tfor name, f := range fs.files {\n\t\tuncompressed := f.algs[\"\"]\n\t\ttotalLength += int64(len(uncompressed))\n\n\t\tif level == 2 {\n\t\t\tfmt.Printf(\"%s (%s)\\n\", name, FormatBytes(int64(len(uncompressed))))\n\t\t}\n\n\t\tfor alg, contents := range f.algs {\n\t\t\tif alg == \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\ttotalCompressedContents++\n\n\t\t\tif len(alg) < 7 {\n\t\t\t\talg += strings.Repeat(\" \", 7-len(alg))\n\t\t\t}\n\t\t\ttotalCompressedLength[alg] += int64(len(contents))\n\n\t\t\tif level == 2 {\n\t\t\t\tfmt.Printf(\"%s (%s)\\n\", alg, FormatBytes(int64(len(contents))))\n\t\t\t}\n\t\t}\n\t}\n\n\tfmt.Printf(\"Time to complete the compression and caching of [%d/%d] files: %s\\n\", totalCompressedContents/int64(len(fs.algs)), n, ttc)\n\tfmt.Printf(\"Total size reduced from %s to:\\n\", FormatBytes(totalLength))\n\tfor alg, length := range totalCompressedLength {\n\t\t// https://en.wikipedia.org/wiki/Data_compression_ratio\n\t\treducedRatio := 1 - float64(length)/float64(totalLength)\n\t\tfmt.Printf(\"%s (%s) [%.2f%%]\\n\", alg, FormatBytes(length), reducedRatio*100)\n\t}\n}\n\ntype cacheFS struct {\n\tdirs  map[string]*dir\n\tfiles fileMap\n\talgs  []string\n}\n\nvar _ http.FileSystem = (*cacheFS)(nil)\n\n// Open returns the http.File based on \"name\".\n// If file, it always returns a cached file of uncompressed data.\n// See `Ropen` too.\nfunc (c *cacheFS) Open(name string) (http.File, error) {\n\t// we always fetch with the sep,\n\t// as http requests will do,\n\t// and the filename's info.Name() is always base\n\t// and without separator prefix\n\t// (keep note, we need that fileInfo\n\t// wrapper because go-bindata's Name originally\n\t// returns the fullname while the http.Dir returns the basename).\n\tif name == \"\" || name[0] != '/' {\n\t\tname = \"/\" + name\n\t}\n\n\tif d, ok := c.dirs[name]; ok {\n\t\treturn d, nil\n\t}\n\n\tif f, ok := c.files[name]; ok {\n\t\treturn f.Get(\"\")\n\t}\n\n\treturn nil, os.ErrNotExist\n}\n\n// Ropen returns the http.File based on \"name\".\n// If file, it negotiates the content encoding,\n// based on the given algorithms, and\n// returns the cached file with compressed data,\n// if the encoding was empty then it\n// returns the cached file with its original, uncompressed data.\n//\n// A check of `GetEncoding(file)` is required to set\n// response headers.\n//\n// Note: We don't require a response writer to set the headers\n// because the caller of this method may stop the operation\n// before file's contents are written to the client.\nfunc (c *cacheFS) Ropen(name string, r *http.Request) (http.File, error) {\n\tif name == \"\" || name[0] != '/' {\n\t\tname = \"/\" + name\n\t}\n\n\tif d, ok := c.dirs[name]; ok {\n\t\treturn d, nil\n\t}\n\n\tif f, ok := c.files[name]; ok {\n\t\tencoding, _ := context.GetEncoding(r, c.algs)\n\t\treturn f.Get(encoding)\n\t}\n\n\treturn nil, os.ErrNotExist\n}\n\n// getFileEncoding returns the encoding of an http.File.\n// If the \"f\" file was created by a `Cache` call then\n// it returns the content encoding that this file was cached with.\n// It returns empty string for files that\n// were too small or ignored to be compressed.\n//\n// It also reports whether the \"f\" is a cached file or not.\nfunc getFileEncoding(f http.File) (string, bool) {\n\tif f == nil {\n\t\treturn \"\", false\n\t}\n\n\tff, ok := f.(*file)\n\tif !ok {\n\t\treturn \"\", false\n\t}\n\n\treturn ff.alg, true\n}\n\n// type fileMap map[string] /* path */ map[string] /*compression alg or empty for original */ []byte /*contents */\ntype fileMap map[string]*file\n\nfunc cacheFiles(ctx stdContext.Context, fs http.FileSystem, names []string, compressAlgs []string, compressMinSize int64, compressIgnore *regexp.Regexp) (fileMap, error) {\n\tctx, cancel := stdContext.WithCancel(ctx)\n\tdefer cancel()\n\n\tlist := make(fileMap, len(names))\n\tmutex := new(sync.Mutex)\n\n\tcache := func(name string) error {\n\t\tf, err := fs.Open(name)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tinf, err := f.Stat()\n\t\tif err != nil {\n\t\t\tf.Close()\n\t\t\treturn err\n\t\t}\n\n\t\tfi := newFileInfo(path.Base(name), inf.Mode(), inf.ModTime())\n\n\t\tcontents, err := io.ReadAll(f)\n\t\tf.Close()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\talgs := make(map[string][]byte, len(compressAlgs)+1)\n\t\talgs[\"\"] = contents // original contents.\n\n\t\tmutex.Lock()\n\t\tlist[name] = newFile(name, fi, algs)\n\t\tmutex.Unlock()\n\t\tif compressMinSize > 0 && compressMinSize > int64(len(contents)) {\n\t\t\treturn nil\n\t\t}\n\n\t\tif compressIgnore != nil && compressIgnore.MatchString(name) {\n\t\t\treturn nil\n\t\t}\n\n\t\t// Note:\n\t\t// We can fire a new goroutine for each compression of the same file\n\t\t// but this will have an impact on CPU cost if\n\t\t// thousands of files running 4 compressions at the same time,\n\t\t// so, unless requested keep it as it's.\n\t\tbuf := new(bytes.Buffer)\n\t\tfor _, alg := range compressAlgs {\n\t\t\tselect {\n\t\t\tcase <-ctx.Done():\n\t\t\t\treturn ctx.Err() // stop all compressions if at least one file failed to.\n\t\t\tdefault:\n\t\t\t}\n\n\t\t\tif alg == \"brotli\" {\n\t\t\t\talg = \"br\"\n\t\t\t}\n\n\t\t\tw, err := context.NewCompressWriter(buf, strings.ToLower(alg), -1)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\t_, err = w.Write(contents)\n\t\t\tw.Close()\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tbs := buf.Bytes()\n\t\t\tdest := make([]byte, len(bs))\n\t\t\tcopy(dest, bs)\n\t\t\talgs[alg] = dest\n\n\t\t\tbuf.Reset()\n\t\t}\n\n\t\treturn nil\n\t}\n\n\tvar (\n\t\terr     error\n\t\twg      sync.WaitGroup\n\t\terrOnce sync.Once\n\t)\n\n\tfor _, name := range names {\n\t\twg.Add(1)\n\n\t\tgo func(name string) {\n\t\t\tdefer wg.Done()\n\n\t\t\tif fnErr := cache(name); fnErr != nil {\n\t\t\t\terrOnce.Do(func() {\n\t\t\t\t\terr = fnErr\n\t\t\t\t\tcancel()\n\t\t\t\t})\n\t\t\t}\n\t\t}(name)\n\t}\n\n\twg.Wait()\n\treturn list, err\n}\n\ntype cacheStoreFile interface {\n\tGet(compressionAlgorithm string) (http.File, error)\n}\n\ntype file struct {\n\tio.ReadSeeker                   // nil on cache store and filled on file Get.\n\talgs          map[string][]byte // non empty for store and nil for files.\n\talg           string            // empty for cache store, filled with the compression algorithm of this file (useful to decompress).\n\tname          string\n\tbaseName      string\n\tinfo          os.FileInfo\n}\n\nvar (\n\t_ http.File      = (*file)(nil)\n\t_ cacheStoreFile = (*file)(nil)\n)\n\nfunc newFile(name string, fi os.FileInfo, algs map[string][]byte) *file {\n\treturn &file{\n\t\tname:     name,\n\t\tbaseName: path.Base(name),\n\t\tinfo:     fi,\n\t\talgs:     algs,\n\t}\n}\n\nfunc (f *file) Close() error                             { return nil }\nfunc (f *file) Readdir(count int) ([]os.FileInfo, error) { return nil, os.ErrNotExist }\nfunc (f *file) Stat() (os.FileInfo, error)               { return f.info, nil }\n\n// Get returns a new http.File to be served.\n// Caller should check if a specific http.File has this method as well.\nfunc (f *file) Get(alg string) (http.File, error) {\n\t// The \"alg\" can be empty for non-compressed file contents.\n\t// We don't need a new structure.\n\n\tif contents, ok := f.algs[alg]; ok {\n\t\treturn &file{\n\t\t\tname:       f.name,\n\t\t\tbaseName:   f.baseName,\n\t\t\tinfo:       f.info,\n\t\t\talg:        alg,\n\t\t\tReadSeeker: bytes.NewReader(contents),\n\t\t}, nil\n\t}\n\n\t// When client accept compression but cached contents are not compressed,\n\t// e.g. file too small or ignored one.\n\treturn f.Get(\"\")\n}\n\ntype fileInfo struct {\n\tbaseName string\n\tmodTime  time.Time\n\tisDir    bool\n\tmode     os.FileMode\n}\n\nvar _ os.FileInfo = (*fileInfo)(nil)\n\nfunc newFileInfo(baseName string, mode os.FileMode, modTime time.Time) *fileInfo {\n\treturn &fileInfo{\n\t\tbaseName: baseName,\n\t\tmodTime:  modTime,\n\t\tmode:     mode,\n\t\tisDir:    mode == os.ModeDir,\n\t}\n}\n\nfunc (fi *fileInfo) Close() error       { return nil }\nfunc (fi *fileInfo) Name() string       { return fi.baseName }\nfunc (fi *fileInfo) Mode() os.FileMode  { return fi.mode }\nfunc (fi *fileInfo) ModTime() time.Time { return fi.modTime }\nfunc (fi *fileInfo) IsDir() bool        { return fi.isDir }\nfunc (fi *fileInfo) Size() int64        { return 0 }\nfunc (fi *fileInfo) Sys() any           { return fi }\n\ntype dir struct {\n\tos.FileInfo   // *fileInfo\n\tio.ReadSeeker // nil\n\n\tname     string // fullname, for any case.\n\tbaseName string\n\tchildren []os.FileInfo // a slice of *fileInfo\n}\n\nvar (\n\t_ os.FileInfo = (*dir)(nil)\n\t_ http.File   = (*dir)(nil)\n)\n\nfunc (d *dir) Close() error               { return nil }\nfunc (d *dir) Name() string               { return d.baseName }\nfunc (d *dir) Stat() (os.FileInfo, error) { return d.FileInfo, nil }\n\nfunc (d *dir) Readdir(count int) ([]os.FileInfo, error) {\n\treturn d.children, nil\n}\n\nfunc newDir(fi os.FileInfo, fullname string) *dir {\n\tbaseName := path.Base(fullname)\n\treturn &dir{\n\t\tFileInfo: newFileInfo(baseName, os.ModeDir, fi.ModTime()),\n\t\tname:     fullname,\n\t\tbaseName: baseName,\n\t}\n}\n\nvar _ http.File = (*dir)(nil)\n\n// returns unorderded map of directories both reclusive and flat.\nfunc findDirs(fs http.FileSystem, names []string) (map[string]*dir, error) {\n\tdirs := make(map[string]*dir)\n\n\tfor _, name := range names {\n\t\tf, err := fs.Open(name)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tinf, err := f.Stat()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tdirName := path.Dir(name)\n\t\td, ok := dirs[dirName]\n\t\tif !ok {\n\t\t\tfi := newFileInfo(path.Base(dirName), os.ModeDir, inf.ModTime())\n\t\t\td = newDir(fi, dirName)\n\t\t\tdirs[dirName] = d\n\t\t}\n\n\t\tfi := newFileInfo(path.Base(name), inf.Mode(), inf.ModTime())\n\n\t\t// Add the directory file info (=this dir) to the parent one,\n\t\t// so `ShowList` can render sub-directories of this dir.\n\t\tparentName := path.Dir(dirName)\n\t\tif parent, hasParent := dirs[parentName]; hasParent {\n\t\t\tparent.children = append(parent.children, d)\n\t\t}\n\n\t\td.children = append(d.children, fi)\n\t}\n\n\treturn dirs, nil\n}\n"
  },
  {
    "path": "core/router/handler.go",
    "content": "package router\n\nimport (\n\t\"errors\"\n\t\"net/http\"\n\t\"sort\"\n\t\"strings\"\n\t\"sync\"\n\t\"sync/atomic\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/core/errgroup\"\n\t\"github.com/kataras/iris/v12/core/netutil\"\n\tmacroHandler \"github.com/kataras/iris/v12/macro/handler\"\n\n\t\"github.com/kataras/golog\"\n)\n\ntype (\n\t// RequestHandler the middle man between acquiring a context and releasing it.\n\t// By-default is the router algorithm.\n\tRequestHandler interface {\n\t\t// Note: A different interface in order  to hide the rest of the implementation.\n\t\t// We only need the `FireErrorCode` to be accessible through the Iris application (see `iris.go#Build`)\n\t\tHTTPErrorHandler\n\n\t\t// HandleRequest should handle the request based on the Context.\n\t\tHandleRequest(ctx *context.Context)\n\t\t// Build should builds the handler, it's being called on router's BuildRouter.\n\t\tBuild(provider RoutesProvider) error\n\t\t// RouteExists reports whether a particular route exists.\n\t\tRouteExists(ctx *context.Context, method, path string) bool\n\t}\n\n\t// HTTPErrorHandler should contain a method `FireErrorCode` which\n\t// handles http unsuccessful status codes.\n\tHTTPErrorHandler interface {\n\t\t// FireErrorCode should send an error response to the client based\n\t\t// on the given context's response status code.\n\t\tFireErrorCode(ctx *context.Context)\n\t}\n\n\t// RouteAdder is an optional interface that can be implemented by a `RequestHandler`.\n\tRouteAdder interface {\n\t\t// AddRoute should add a route to the request handler directly.\n\t\tAddRoute(*Route) error\n\t}\n)\n\n// ErrNotRouteAdder throws on `AddRouteUnsafe` when a registered `RequestHandler`\n// does not implements the optional `AddRoute(*Route) error` method.\nvar ErrNotRouteAdder = errors.New(\"request handler does not implement AddRoute method\")\n\ntype routerHandler struct {\n\t// Config.\n\tdisablePathCorrection            bool\n\tdisablePathCorrectionRedirection bool\n\tfireMethodNotAllowed             bool\n\tenablePathIntelligence           bool\n\tforceLowercaseRouting            bool\n\t//\n\tlogger *golog.Logger\n\n\ttrees      []*trie\n\terrorTrees []*trie\n\n\thosts                bool             // true if at least one route contains a Subdomain.\n\terrorHosts           bool             // true if error handlers are registered to at least one Subdomain.\n\terrorDefaultHandlers context.Handlers // the main handler(s) for default error code handlers, when not registered directly by the end-developer.\n}\n\nvar (\n\t_ RequestHandler   = (*routerHandler)(nil)\n\t_ HTTPErrorHandler = (*routerHandler)(nil)\n)\n\ntype routerHandlerDynamic struct {\n\tRequestHandler\n\trw sync.RWMutex\n\n\tlocked uint32\n}\n\n// RouteExists reports whether a particular route exists.\nfunc (h *routerHandlerDynamic) RouteExists(ctx *context.Context, method, path string) (exists bool) {\n\th.lock(false, func() error {\n\t\texists = h.RequestHandler.RouteExists(ctx, method, path)\n\t\treturn nil\n\t})\n\n\treturn\n}\n\nfunc (h *routerHandlerDynamic) AddRoute(r *Route) error {\n\tif v, ok := h.RequestHandler.(RouteAdder); ok {\n\t\treturn h.lock(true, func() error {\n\t\t\treturn v.AddRoute(r)\n\t\t})\n\t}\n\n\treturn ErrNotRouteAdder\n}\n\nfunc (h *routerHandlerDynamic) lock(writeAccess bool, fn func() error) error {\n\tif atomic.CompareAndSwapUint32(&h.locked, 0, 1) {\n\t\tif writeAccess {\n\t\t\th.rw.Lock()\n\t\t} else {\n\t\t\th.rw.RLock()\n\t\t}\n\n\t\terr := fn()\n\n\t\t// check agan because fn may called the unlock method.\n\t\tif atomic.CompareAndSwapUint32(&h.locked, 1, 0) {\n\t\t\tif writeAccess {\n\t\t\t\th.rw.Unlock()\n\t\t\t} else {\n\t\t\t\th.rw.RUnlock()\n\t\t\t}\n\t\t}\n\n\t\treturn err\n\t}\n\n\treturn fn()\n}\n\nfunc (h *routerHandlerDynamic) Build(provider RoutesProvider) error {\n\t// Build can be called inside HandleRequest if the route handler\n\t// calls the RefreshRouter method, and it will stuck on the rw.Lock() call,\n\t// so use a custom version of it.\n\t// h.rw.Lock()\n\t// defer h.rw.Unlock()\n\n\treturn h.lock(true, func() error {\n\t\treturn h.RequestHandler.Build(provider)\n\t})\n}\n\nfunc (h *routerHandlerDynamic) HandleRequest(ctx *context.Context) {\n\th.lock(false, func() error {\n\t\th.RequestHandler.HandleRequest(ctx)\n\t\treturn nil\n\t})\n}\n\nfunc (h *routerHandlerDynamic) FireErrorCode(ctx *context.Context) {\n\th.lock(false, func() error {\n\t\th.RequestHandler.FireErrorCode(ctx)\n\t\treturn nil\n\t})\n}\n\n// NewDynamicHandler returns a new router handler which is responsible handle each request\n// with routes that can be added in serve-time.\n// It's a wrapper of the `NewDefaultHandler`.\n// It's being used when the `ConfigurationReadOnly.GetEnableDynamicHandler` is true.\nfunc NewDynamicHandler(config context.ConfigurationReadOnly, logger *golog.Logger) RequestHandler /* #2167 */ {\n\thandler := NewDefaultHandler(config, logger)\n\treturn wrapDynamicHandler(handler)\n}\n\nfunc wrapDynamicHandler(handler RequestHandler) RequestHandler {\n\treturn &routerHandlerDynamic{\n\t\tRequestHandler: handler,\n\t}\n}\n\n// NewDefaultHandler returns the handler which is responsible\n// to map the request with a route (aka mux implementation).\nfunc NewDefaultHandler(config context.ConfigurationReadOnly, logger *golog.Logger) RequestHandler {\n\tvar (\n\t\tdisablePathCorrection            bool\n\t\tdisablePathCorrectionRedirection bool\n\t\tfireMethodNotAllowed             bool\n\t\tenablePathIntelligence           bool\n\t\tforceLowercaseRouting            bool\n\t\tdynamicHandlerEnabled            bool\n\t)\n\n\tif config != nil { // #2147\n\t\tdisablePathCorrection = config.GetDisablePathCorrection()\n\t\tdisablePathCorrectionRedirection = config.GetDisablePathCorrectionRedirection()\n\t\tfireMethodNotAllowed = config.GetFireMethodNotAllowed()\n\t\tenablePathIntelligence = config.GetEnablePathIntelligence()\n\t\tforceLowercaseRouting = config.GetForceLowercaseRouting()\n\t\tdynamicHandlerEnabled = config.GetEnableDynamicHandler()\n\t}\n\n\thandler := &routerHandler{\n\t\tdisablePathCorrection:            disablePathCorrection,\n\t\tdisablePathCorrectionRedirection: disablePathCorrectionRedirection,\n\t\tfireMethodNotAllowed:             fireMethodNotAllowed,\n\t\tenablePathIntelligence:           enablePathIntelligence,\n\t\tforceLowercaseRouting:            forceLowercaseRouting,\n\t\tlogger:                           logger,\n\t}\n\n\tif dynamicHandlerEnabled {\n\t\treturn wrapDynamicHandler(handler)\n\t}\n\n\treturn handler\n}\n\nfunc (h *routerHandler) getTree(statusCode int, method, subdomain string) *trie {\n\tif statusCode > 0 {\n\t\tfor i := range h.errorTrees {\n\t\t\tt := h.errorTrees[i]\n\t\t\tif t.statusCode == statusCode && t.subdomain == subdomain {\n\t\t\t\treturn t\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\n\tfor i := range h.trees {\n\t\tt := h.trees[i]\n\t\tif t.method == method && t.subdomain == subdomain {\n\t\t\treturn t\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// AddRoute registers a route. See `Router.AddRouteUnsafe`.\nfunc (h *routerHandler) AddRoute(r *Route) error {\n\tvar (\n\t\tmethod     = r.Method\n\t\tstatusCode = r.StatusCode\n\t\tsubdomain  = r.Subdomain\n\t\tpath       = r.Path\n\t\thandlers   = r.Handlers\n\t)\n\n\tt := h.getTree(statusCode, method, subdomain)\n\n\tif t == nil {\n\t\tn := newTrieNode()\n\t\t// first time we register a route to this method with this subdomain\n\t\tt = &trie{statusCode: statusCode, method: method, subdomain: subdomain, root: n}\n\t\tif statusCode > 0 {\n\t\t\th.errorTrees = append(h.errorTrees, t)\n\t\t} else {\n\t\t\th.trees = append(h.trees, t)\n\t\t}\n\t}\n\n\tt.insert(path, r.ReadOnly, handlers)\n\n\treturn nil\n}\n\n// RoutesProvider should be implemented by\n// iteral which contains the registered routes.\ntype RoutesProvider interface { // api builder\n\tGetRoutes() []*Route\n\tGetRoute(routeName string) *Route\n\t// GetRouterFilters returns the app's router filters.\n\t// Read `UseRouter` for more.\n\t// The map can be altered before router built.\n\tGetRouterFilters() map[Party]*Filter\n\t// GetDefaultErrorMiddleware should return\n\t// the default error handler middleares.\n\tGetDefaultErrorMiddleware() context.Handlers\n}\n\nfunc defaultErrorHandler(ctx *context.Context) {\n\tif ok, err := ctx.GetErrPublic(); ok {\n\t\t// If an error is stored and it's not a private one\n\t\t// write it to the response body.\n\t\tctx.WriteString(err.Error())\n\t\treturn\n\t}\n\t// Otherwise, write the code's text instead.\n\tctx.WriteString(context.StatusText(ctx.GetStatusCode()))\n}\n\nfunc (h *routerHandler) Build(provider RoutesProvider) error {\n\th.trees = h.trees[0:0] // reset, inneed when rebuilding.\n\th.errorTrees = h.errorTrees[0:0]\n\n\t// set the default error code handler, will be fired on error codes\n\t// that are not handled by a specific handler (On(Any)ErrorCode).\n\th.errorDefaultHandlers = append(provider.GetDefaultErrorMiddleware(), defaultErrorHandler)\n\n\trp := errgroup.New(\"Routes Builder\")\n\tregisteredRoutes := provider.GetRoutes()\n\n\t// before sort.\n\tfor _, r := range registeredRoutes {\n\t\tif r.topLink != nil {\n\t\t\tbindMultiParamTypesHandler(r)\n\t\t}\n\t}\n\n\t// sort, subdomains go first.\n\tsort.Slice(registeredRoutes, func(i, j int) bool {\n\t\tfirst, second := registeredRoutes[i], registeredRoutes[j]\n\t\tlsub1 := len(first.Subdomain)\n\t\tlsub2 := len(second.Subdomain)\n\n\t\tfirstSlashLen := strings.Count(first.Path, \"/\")\n\t\tsecondSlashLen := strings.Count(second.Path, \"/\")\n\n\t\tif lsub1 == lsub2 && first.Method == second.Method {\n\t\t\tif secondSlashLen < firstSlashLen {\n\t\t\t\t// fixes order when wildcard root is registered before other wildcard paths\n\t\t\t\treturn true\n\t\t\t}\n\n\t\t\tif secondSlashLen == firstSlashLen {\n\t\t\t\t// fixes order when static path with the same prefix with a wildcard path\n\t\t\t\t// is registered after the wildcard path, although this is managed\n\t\t\t\t// by the low-level node but it couldn't work if we registered a root level wildcard, this fixes it.\n\t\t\t\tif len(first.tmpl.Params) == 0 {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t\tif len(second.tmpl.Params) == 0 {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\n\t\t\t\t// No don't fix the order by framework's suggestion,\n\t\t\t\t// let it as it is today; {string} and {path} should be registered before {id} {uint} and e.t.c.\n\t\t\t\t// see `bindMultiParamTypesHandler` for the reason. Order of registration matters.\n\t\t\t}\n\t\t}\n\n\t\t// the rest are handled inside the node\n\t\treturn lsub1 > lsub2\n\t})\n\n\tnoLogCount := 0\n\n\tfor _, r := range registeredRoutes {\n\t\tif r.NoLog {\n\t\t\tnoLogCount++\n\t\t}\n\n\t\tif h.forceLowercaseRouting {\n\t\t\t// only in that state, keep everything else as end-developer registered.\n\t\t\tr.Path = strings.ToLower(r.Path)\n\t\t}\n\n\t\tif r.Subdomain != \"\" {\n\t\t\tif r.StatusCode > 0 {\n\t\t\t\th.errorHosts = true\n\t\t\t} else {\n\t\t\t\th.hosts = true\n\t\t\t}\n\t\t}\n\n\t\tif r.topLink == nil {\n\t\t\t// build the r.Handlers based on begin and done handlers, if any.\n\t\t\tr.BuildHandlers()\n\n\t\t\t// the only \"bad\" with this is if the user made an error\n\t\t\t// on route, it will be stacked shown in this build state\n\t\t\t// and no in the lines of the user's action, they should read\n\t\t\t// the docs better. Or TODO: add a link here in order to help new users.\n\t\t\tif err := h.AddRoute(r); err != nil {\n\t\t\t\t// node errors:\n\t\t\t\trp.Addf(\"%s: %w\", r.String(), err)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t}\n\n\tprintRoutesInfo(h.logger, registeredRoutes, noLogCount)\n\n\treturn errgroup.Check(rp)\n}\n\nfunc bindMultiParamTypesHandler(r *Route) { // like overlap feature but specifically for path parameters.\n\tr.BuildHandlers()\n\n\th := r.Handlers[1:] // remove the macro evaluator handler as we manually check below.\n\tf := macroHandler.MakeFilter(r.tmpl)\n\tif f == nil {\n\t\treturn // should never happen, previous checks made to set the top link.\n\t}\n\n\tcurrentStatusCode := r.StatusCode\n\tif currentStatusCode == 0 {\n\t\tcurrentStatusCode = http.StatusOK\n\t}\n\n\tdecisionHandler := func(ctx *context.Context) {\n\t\t// println(\"core/router/handler.go: decision handler; \" + ctx.Path() + \" route.Name: \" + r.Name + \" vs context's \" + ctx.GetCurrentRoute().Name())\n\t\tcurrentRoute := ctx.GetCurrentRoute()\n\n\t\t// Different path parameters types in the same path, fallback should registered first e.g. {path} {string},\n\t\t// because the handler on this case is executing from last to top.\n\t\tif f(ctx) {\n\t\t\t// println(\"core/router/handler.go: filter for : \" + r.Name + \" passed\")\n\t\t\tctx.SetCurrentRoute(r.ReadOnly)\n\t\t\t// Note: error handlers will be the same, routes came from the same party,\n\t\t\t// no need to update them.\n\t\t\tctx.HandlerIndex(0)\n\t\t\tctx.Do(h)\n\t\t\treturn\n\t\t}\n\n\t\tctx.SetCurrentRoute(currentRoute)\n\t\tctx.StatusCode(currentStatusCode)\n\t\tctx.Next()\n\t}\n\n\tr.topLink.builtinBeginHandlers = append(context.Handlers{decisionHandler}, r.topLink.builtinBeginHandlers...)\n}\n\nfunc canHandleSubdomain(ctx *context.Context, subdomain string) bool {\n\tif subdomain == \"\" {\n\t\treturn true\n\t}\n\n\trequestHost := ctx.Host()\n\tif netutil.IsLoopbackSubdomain(requestHost) {\n\t\t// this fixes a bug when listening on\n\t\t// 127.0.0.1:8080 for example\n\t\t// and have a wildcard subdomain and a route registered to root domain.\n\t\treturn false // it's not a subdomain, it's something like 127.0.0.1 probably\n\t}\n\t// it's a dynamic wildcard subdomain, we have just to check if ctx.subdomain is not empty\n\tif subdomain == SubdomainWildcardIndicator {\n\t\t// mydomain.com -> invalid\n\t\t// localhost -> invalid\n\t\t// sub.mydomain.com -> valid\n\t\t// sub.localhost -> valid\n\t\tserverHost := ctx.Application().ConfigurationReadOnly().GetVHost()\n\t\tif serverHost == requestHost {\n\t\t\treturn false // it's not a subdomain, it's a full domain (with .com...)\n\t\t}\n\n\t\tdotIdx := strings.IndexByte(requestHost, '.')\n\t\tslashIdx := strings.IndexByte(requestHost, '/')\n\t\tif dotIdx > 0 && (slashIdx == -1 || slashIdx > dotIdx) {\n\t\t\t// if \".\" was found anywhere but not at the first path segment (host).\n\t\t} else {\n\t\t\treturn false\n\t\t}\n\t\t// continue to that, any subdomain is valid.\n\t} else if !strings.HasPrefix(requestHost, subdomain) { // subdomain contains the dot, e.g. \"admin.\"\n\t\treturn false\n\t}\n\n\treturn true\n}\n\nfunc (h *routerHandler) HandleRequest(ctx *context.Context) {\n\tmethod := ctx.Method()\n\tpath := ctx.Path()\n\n\tif !h.disablePathCorrection {\n\t\tif len(path) > 1 && strings.HasSuffix(path, \"/\") {\n\t\t\t// Remove trailing slash and client-permanent rule for redirection,\n\t\t\t// if confgiuration allows that and path has an extra slash.\n\n\t\t\t// update the new path and redirect.\n\t\t\tu := ctx.Request().URL\n\t\t\t// use Trim to ensure there is no open redirect due to two leading slashes\n\t\t\tpath = \"/\" + strings.Trim(path, \"/\")\n\t\t\tu.Path = path\n\t\t\tif !h.disablePathCorrectionRedirection {\n\t\t\t\t// do redirect, else continue with the modified path without the last \"/\".\n\t\t\t\turl := u.String()\n\n\t\t\t\t// Fixes https://github.com/kataras/iris/issues/921\n\t\t\t\t// This is caused for security reasons, imagine a payment shop,\n\t\t\t\t// you can't just permantly redirect a POST request, so just 307 (RFC 7231, 6.4.7).\n\t\t\t\tif method == http.MethodPost || method == http.MethodPut {\n\t\t\t\t\tctx.Redirect(url, http.StatusTemporaryRedirect)\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tctx.Redirect(url, http.StatusMovedPermanently)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t}\n\t}\n\n\tfor i := range h.trees {\n\t\tt := h.trees[i]\n\t\tif method != t.method {\n\t\t\tcontinue\n\t\t}\n\n\t\tif h.hosts && !canHandleSubdomain(ctx, t.subdomain) {\n\t\t\tcontinue\n\t\t}\n\n\t\tn := t.search(path, ctx.Params())\n\t\tif n != nil {\n\t\t\tctx.SetCurrentRoute(n.Route)\n\t\t\tctx.Do(n.Handlers)\n\t\t\t// found\n\t\t\treturn\n\t\t}\n\t\t// not found or method not allowed.\n\t\tbreak\n\t}\n\n\tif h.fireMethodNotAllowed {\n\t\tfor i := range h.trees {\n\t\t\tt := h.trees[i]\n\t\t\t// if `Configuration#FireMethodNotAllowed` is kept as defaulted(false) then this function will not\n\t\t\t// run, therefore performance kept as before.\n\t\t\tif h.subdomainAndPathAndMethodExists(ctx, t, \"\", path) {\n\t\t\t\t// RCF rfc2616 https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html\n\t\t\t\t// The response MUST include an Allow header containing a list of valid methods for the requested resource.\n\t\t\t\tctx.Header(\"Allow\", t.method)\n\t\t\t\tctx.StatusCode(http.StatusMethodNotAllowed)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\n\tif h.enablePathIntelligence && method == http.MethodGet {\n\t\tclosestPaths := ctx.FindClosest(1)\n\t\tif len(closestPaths) > 0 {\n\t\t\tu := ctx.Request().URL\n\t\t\tu.Path = closestPaths[0]\n\t\t\tctx.Redirect(u.String(), http.StatusMovedPermanently)\n\t\t\treturn\n\t\t}\n\t}\n\n\tctx.StatusCode(http.StatusNotFound)\n}\n\nfunc statusCodeSuccessful(statusCode int) bool {\n\treturn !context.StatusCodeNotSuccessful(statusCode)\n}\n\n// FireErrorCode handles the response's error response.\n// If `Configuration.ResetOnFireErrorCode()` is true\n// and the response writer was a recorder one\n// then it will try to reset the headers and the body before calling the\n// registered (or default) error handler for that error code set by\n// `ctx.StatusCode` method.\nfunc (h *routerHandler) FireErrorCode(ctx *context.Context) {\n\t// On common response writer, always check\n\t// if we can't reset the body and the body has been filled\n\t// which means that the status code already sent,\n\t// then do not fire this custom error code,\n\t// rel: context/context.go#EndRequest.\n\t//\n\t// Note that, this is set to 0 on recorder because it holds the response before sent,\n\t// so we check their len(Body()) instead, look below.\n\tif ctx.ResponseWriter().Written() > 0 {\n\t\treturn\n\t}\n\n\tstatusCode := ctx.GetStatusCode() // the response's cached one.\n\n\tif ctx.Application().ConfigurationReadOnly().GetResetOnFireErrorCode() /* could be an argument too but we must not break the method */ {\n\t\t// if we can reset the body, probably manual call of `Application.FireErrorCode`.\n\t\tif w, ok := ctx.IsRecording(); ok {\n\t\t\tif statusCodeSuccessful(w.StatusCode()) { // if not an error status code\n\t\t\t\tw.WriteHeader(statusCode) // then set it manually here, otherwise it should be set via ctx.StatusCode(...)\n\t\t\t}\n\t\t\t// reset if previous content and it's recorder, keep the status code.\n\t\t\tw.ClearHeaders()\n\t\t\tw.ResetBody()\n\n\t\t\tif cw, ok := w.ResponseWriter.(*context.CompressResponseWriter); ok {\n\t\t\t\t// recorder wraps a compress writer.\n\t\t\t\tcw.Disabled = true\n\t\t\t}\n\t\t} else if w, ok := ctx.ResponseWriter().(*context.CompressResponseWriter); ok {\n\t\t\t// reset and disable the gzip in order to be an expected form of http error result\n\t\t\tw.Disabled = true\n\t\t}\n\t} else {\n\t\t// check if a body already set (the error response is handled by the handler itself,\n\t\t// see `Context.EndRequest`)\n\t\tif w, ok := ctx.IsRecording(); ok {\n\t\t\tif len(w.Body()) > 0 {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\n\tfor i := range h.errorTrees {\n\t\tt := h.errorTrees[i]\n\n\t\tif statusCode != t.statusCode {\n\t\t\tcontinue\n\t\t}\n\n\t\tif h.errorHosts && !canHandleSubdomain(ctx, t.subdomain) {\n\t\t\tcontinue\n\t\t}\n\n\t\tn := t.search(ctx.Path(), ctx.Params())\n\t\tif n == nil {\n\t\t\t// try to take the root's one.\n\t\t\tn = t.root.getChild(pathSep)\n\t\t}\n\n\t\tif n != nil {\n\t\t\t// Note: handlers can contain macro filters here,\n\t\t\t// they are registered as error handlers, see macro/handler.go#42.\n\n\t\t\t// fmt.Println(\"Error Handlers\")\n\t\t\t// for _, h := range n.Handlers {\n\n\t\t\t// \tf, l := context.HandlerFileLine(h)\n\t\t\t// \tfmt.Printf(\"%s: %s:%d\\n\", ctx.Path(), f, l)\n\t\t\t// }\n\n\t\t\t// fire this http status code's error handlers chain.\n\n\t\t\t// ctx.StopExecution() // not uncomment this, is here to remember why to.\n\t\t\t// note for me: I don't stopping the execution of the other handlers\n\t\t\t// because may the user want to add a fallback error code\n\t\t\t// i.e\n\t\t\t// users := app.Party(\"/users\")\n\t\t\t// users.Done(func(ctx *context.Context){ if ctx.StatusCode() == 400 { /*  custom error code for /users */ }})\n\n\t\t\t// use .HandlerIndex\n\t\t\t// that sets the current handler index to zero\n\t\t\t// in order to:\n\t\t\t// ignore previous runs that may changed the handler index,\n\t\t\t// via ctx.Next or ctx.StopExecution, if any.\n\t\t\t//\n\t\t\t// use .Do\n\t\t\t// that overrides the existing handlers and sets and runs these error handlers.\n\t\t\t// in order to:\n\t\t\t// ignore the route's after-handlers, if any.\n\t\t\tctx.SetCurrentRoute(n.Route)\n\t\t\t// Should work with:\n\t\t\t// Manual call of ctx.Application().FireErrorCode(ctx) (handlers length > 0)\n\t\t\t// And on `ctx.SetStatusCode`: Context -> EndRequest -> FireErrorCode (handlers length > 0)\n\t\t\t// And on router: HandleRequest -> SetStatusCode -> Context ->\n\t\t\t//                EndRequest -> FireErrorCode (handlers' length is always 0)\n\t\t\tctx.HandlerIndex(0)\n\t\t\tctx.Do(n.Handlers)\n\t\t\treturn\n\t\t}\n\n\t\tbreak\n\t}\n\n\t// not error handler found,\n\t// see if failed with a stored error, and if so\n\t// then render it, otherwise write a default message.\n\tctx.Do(h.errorDefaultHandlers)\n}\n\nfunc (h *routerHandler) subdomainAndPathAndMethodExists(ctx *context.Context, t *trie, method, path string) bool {\n\tif method != \"\" && method != t.method {\n\t\treturn false\n\t}\n\n\tif h.hosts && t.subdomain != \"\" {\n\t\trequestHost := ctx.Host()\n\t\tif netutil.IsLoopbackSubdomain(requestHost) {\n\t\t\t// this fixes a bug when listening on\n\t\t\t// 127.0.0.1:8080 for example\n\t\t\t// and have a wildcard subdomain and a route registered to root domain.\n\t\t\treturn false // it's not a subdomain, it's something like 127.0.0.1 probably\n\t\t}\n\t\t// it's a dynamic wildcard subdomain, we have just to check if ctx.subdomain is not empty\n\t\tif t.subdomain == SubdomainWildcardIndicator {\n\t\t\t// mydomain.com -> invalid\n\t\t\t// localhost -> invalid\n\t\t\t// sub.mydomain.com -> valid\n\t\t\t// sub.localhost -> valid\n\t\t\tserverHost := ctx.Application().ConfigurationReadOnly().GetVHost()\n\t\t\tif serverHost == requestHost {\n\t\t\t\treturn false // it's not a subdomain, it's a full domain (with .com...)\n\t\t\t}\n\n\t\t\tdotIdx := strings.IndexByte(requestHost, '.')\n\t\t\tslashIdx := strings.IndexByte(requestHost, '/')\n\t\t\tif dotIdx > 0 && (slashIdx == -1 || slashIdx > dotIdx) {\n\t\t\t\t// if \".\" was found anywhere but not at the first path segment (host).\n\t\t\t} else {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\t// continue to that, any subdomain is valid.\n\t\t} else if !strings.HasPrefix(requestHost, t.subdomain) { // t.subdomain contains the dot.\n\t\t\treturn false\n\t\t}\n\t}\n\n\tn := t.search(path, ctx.Params())\n\treturn n != nil\n}\n\n// RouteExists reports whether a particular route exists\n// It will search from the current subdomain of context's host, if not inside the root domain.\nfunc (h *routerHandler) RouteExists(ctx *context.Context, method, path string) bool {\n\tfor i := range h.trees {\n\t\tt := h.trees[i]\n\t\tif h.subdomainAndPathAndMethodExists(ctx, t, method, path) {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n"
  },
  {
    "path": "core/router/handler_debug.go",
    "content": "package router\n\nimport (\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/kataras/golog\"\n\t\"github.com/kataras/pio\"\n)\n\nfunc printRoutesInfo(logger *golog.Logger, registeredRoutes []*Route, noLogCount int) {\n\tif !(logger != nil && logger.Level == golog.DebugLevel && noLogCount < len(registeredRoutes)) {\n\t\treturn\n\t}\n\n\t// group routes by method and print them without the [DBUG] and time info,\n\t// the route logs are colorful.\n\t// Note: don't use map, we need to keep registered order, use\n\t// different slices for each method.\n\n\tcollect := func(method string) (methodRoutes []*Route) {\n\t\tfor _, r := range registeredRoutes {\n\t\t\tif r.NoLog {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif r.Method == method {\n\t\t\t\tmethodRoutes = append(methodRoutes, r)\n\t\t\t}\n\t\t}\n\n\t\treturn\n\t}\n\n\ttype MethodRoutes struct {\n\t\tmethod string\n\t\troutes []*Route\n\t}\n\n\tallMethods := append(AllMethods, []string{MethodNone, \"\"}...)\n\tmethodRoutes := make([]MethodRoutes, 0, len(allMethods))\n\n\tfor _, method := range allMethods {\n\t\troutes := collect(method)\n\t\tif len(routes) > 0 {\n\t\t\tmethodRoutes = append(methodRoutes, MethodRoutes{method, routes})\n\t\t}\n\t}\n\n\tif n := len(methodRoutes); n > 0 {\n\t\ttr := \"routes\"\n\t\tif len(registeredRoutes) == 1 {\n\t\t\ttr = tr[0 : len(tr)-1]\n\t\t}\n\n\t\tbckpNewLine := logger.NewLine\n\t\tlogger.NewLine = false\n\t\tdebugLevel := golog.Levels[golog.DebugLevel]\n\t\t// Replace that in order to not transfer it to the log handler (e.g. json)\n\t\t// logger.Debugf(\"API: %d registered %s (\", len(registeredRoutes), tr)\n\t\t// with:\n\t\tpio.WriteRich(logger.Printer, debugLevel.Title, debugLevel.ColorCode, debugLevel.Style...)\n\t\tfmt.Fprintf(logger.Printer, \" %s %sAPI: %d registered %s (\", time.Now().Format(logger.TimeFormat), logger.Prefix, len(registeredRoutes)-noLogCount, tr)\n\t\t//\n\t\tlogger.NewLine = bckpNewLine\n\n\t\tfor i, m := range methodRoutes {\n\t\t\t// @method: @count\n\t\t\tif i > 0 {\n\t\t\t\tif i == n-1 {\n\t\t\t\t\tfmt.Fprint(logger.Printer, \" and \")\n\t\t\t\t} else {\n\t\t\t\t\tfmt.Fprint(logger.Printer, \", \")\n\t\t\t\t}\n\t\t\t}\n\t\t\tif m.method == \"\" {\n\t\t\t\tm.method = \"ERROR\"\n\t\t\t}\n\t\t\tfmt.Fprintf(logger.Printer, \"%d \", len(m.routes))\n\t\t\tpio.WriteRich(logger.Printer, m.method, TraceTitleColorCode(m.method))\n\t\t}\n\n\t\tfmt.Fprint(logger.Printer, \")\\n\")\n\t}\n\n\tfor i, m := range methodRoutes {\n\t\tfor _, r := range m.routes {\n\t\t\tr.Trace(logger.Printer, -1)\n\t\t}\n\n\t\tif i != len(allMethods)-1 {\n\t\t\tlogger.Printer.Write(pio.NewLine)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "core/router/handler_execution_rules.go",
    "content": "package router\n\nimport (\n\t\"github.com/kataras/iris/v12/context\"\n)\n\n// ExecutionRules gives control to the execution of the route handlers outside of the handlers themselves.\n// Usage:\n//\n//\tParty#SetExecutionRules(ExecutionRules {\n//\t  Done: ExecutionOptions{Force: true},\n//\t})\n//\n// See `Party#SetExecutionRules` for more.\ntype ExecutionRules struct {\n\t// Begin applies from `Party#Use`/`APIBUilder#UseGlobal` to the first...!last `Party#Handle`'s IF main handlers > 1.\n\tBegin ExecutionOptions\n\t// Done applies to the latest `Party#Handle`'s (even if one) and all done handlers.\n\tDone ExecutionOptions\n\t// Main applies to the `Party#Handle`'s all handlers, plays nice with the `Done` rule\n\t// when more than one handler was registered in `Party#Handle` without `ctx.Next()` (for Force: true).\n\tMain ExecutionOptions\n}\n\nfunc applyExecutionRules(rules ExecutionRules, begin, done, main *context.Handlers) {\n\tif !rules.Begin.Force && !rules.Done.Force && !rules.Main.Force {\n\t\treturn // do not proceed and spend build-time here if nothing changed.\n\t}\n\n\tbeginOK := rules.Begin.apply(begin)\n\tmainOK := rules.Main.apply(main)\n\tdoneOK := rules.Done.apply(done)\n\n\tif !mainOK {\n\t\tmainCp := (*main)[0:]\n\n\t\tlastIdx := len(mainCp) - 1\n\n\t\tif beginOK {\n\t\t\tif len(mainCp) > 1 {\n\t\t\t\tmainCpFirstButNotLast := make(context.Handlers, lastIdx)\n\t\t\t\tcopy(mainCpFirstButNotLast, mainCp[:lastIdx])\n\n\t\t\t\tfor i, h := range mainCpFirstButNotLast {\n\t\t\t\t\t(*main)[i] = rules.Begin.buildHandler(h)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif doneOK {\n\t\t\tlatestMainHandler := mainCp[lastIdx]\n\t\t\t(*main)[lastIdx] = rules.Done.buildHandler(latestMainHandler)\n\t\t}\n\t}\n}\n\n// ExecutionOptions is a set of default behaviors that can be changed in order to customize the execution flow of the routes' handlers with ease.\n//\n// See `ExecutionRules` and `Party#SetExecutionRules` for more.\ntype ExecutionOptions struct {\n\t// Force if true then the handler9s) will execute even if the previous (or/and current, depends on the type of the rule)\n\t// handler does not calling the `ctx.Next()`,\n\t// note that the only way remained to stop a next handler is with the `ctx.StopExecution()` if this option is true.\n\t//\n\t// If true and `ctx.Next()` exists in the handlers that it shouldn't be, the framework will understand it but use it wisely.\n\t//\n\t// Defaults to false.\n\tForce bool\n}\n\nfunc (e ExecutionOptions) buildHandler(h context.Handler) context.Handler {\n\tif !e.Force {\n\t\treturn h\n\t}\n\n\treturn func(ctx *context.Context) {\n\t\t// Proceed will fire the handler and return false here if it doesn't contain a `ctx.Next()`,\n\t\t// so we add the `ctx.Next()` wherever is necessary in order to eliminate any dev's misuse.\n\t\t//\n\t\t// 26 Feb 2022: check if manually stopped, and if it's then don't call ctx.Next.\n\t\tif hasStopped, hasNext := ctx.ProceedAndReportIfStopped(h); !hasStopped && !hasNext {\n\t\t\t// `ctx.Next()` always checks for `ctx.IsStopped()` and handler(s) positions by-design.\n\t\t\tctx.Next()\n\t\t}\n\t}\n}\n\nfunc (e ExecutionOptions) apply(handlers *context.Handlers) bool {\n\tif !e.Force {\n\t\treturn false\n\t}\n\n\ttmp := *handlers\n\n\tfor i, h := range tmp {\n\t\tif h == nil {\n\t\t\tif len(tmp) == 1 {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\t(*handlers)[i] = e.buildHandler(h)\n\t}\n\n\treturn true\n}\n"
  },
  {
    "path": "core/router/handler_execution_rules_test.go",
    "content": "package router_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/core/router\"\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nvar (\n\tfinalExecutionRulesResponse = \"1234\"\n\n\ttestExecutionResponse = func(t *testing.T, app *iris.Application, path string) {\n\t\te := httptest.New(t, app)\n\t\te.GET(path).Expect().Status(httptest.StatusOK).Body().IsEqual(finalExecutionRulesResponse)\n\t}\n)\n\nfunc writeStringHandler(text string, withNext bool) context.Handler {\n\treturn func(ctx *context.Context) {\n\t\tctx.WriteString(text)\n\t\tif withNext {\n\t\t\tctx.Next()\n\t\t}\n\t}\n}\n\nfunc TestRouterExecutionRulesForceMain(t *testing.T) {\n\tapp := iris.New()\n\tbegin := app.Party(\"/\")\n\tbegin.SetExecutionRules(router.ExecutionRules{Main: router.ExecutionOptions{Force: true}})\n\n\t// no need of `ctx.Next()` all main handlers should be executed with the Main.Force:True rule.\n\tbegin.Get(\"/\", writeStringHandler(\"12\", false), writeStringHandler(\"3\", false), writeStringHandler(\"4\", false))\n\n\ttestExecutionResponse(t, app, \"/\")\n}\n\nfunc TestRouterExecutionRulesForceBegin(t *testing.T) {\n\tapp := iris.New()\n\tbegin := app.Party(\"/begin_force\")\n\tbegin.SetExecutionRules(router.ExecutionRules{Begin: router.ExecutionOptions{Force: true}})\n\n\t// should execute, begin rule is to force execute them without `ctx.Next()`.\n\tbegin.Use(writeStringHandler(\"1\", false))\n\tbegin.Use(writeStringHandler(\"2\", false))\n\t// begin starts with begin and ends to the main handlers but not last, so this done should not be executed.\n\tbegin.Done(writeStringHandler(\"5\", false))\n\tbegin.Get(\"/\", writeStringHandler(\"3\", false), writeStringHandler(\"4\", false))\n\n\ttestExecutionResponse(t, app, \"/begin_force\")\n}\n\nfunc TestRouterExecutionRulesForceDone(t *testing.T) {\n\tapp := iris.New()\n\tdone := app.Party(\"/done_force\")\n\tdone.SetExecutionRules(router.ExecutionRules{Done: router.ExecutionOptions{Force: true}})\n\n\t// these done should be executed without `ctx.Next()`\n\tdone.Done(writeStringHandler(\"3\", false), writeStringHandler(\"4\", false))\n\t// first with `ctx.Next()`, because Done.Force:True rule will alter the latest of the main handler(s) only.\n\tdone.Get(\"/\", writeStringHandler(\"1\", true), writeStringHandler(\"2\", false))\n\n\t// rules should be kept in children.\n\tdoneChild := done.Party(\"/child\")\n\t// even if only one, it's the latest, Done.Force:True rule should modify it.\n\tdoneChild.Get(\"/\", writeStringHandler(\"12\", false))\n\n\ttestExecutionResponse(t, app, \"/done_force\")\n\ttestExecutionResponse(t, app, \"/done_force/child\")\n}\n\nfunc TestRouterExecutionRulesShouldNotModifyTheCallersHandlerAndChildrenCanResetExecutionRules(t *testing.T) {\n\tapp := iris.New()\n\tapp.SetExecutionRules(router.ExecutionRules{Done: router.ExecutionOptions{Force: true}})\n\th := writeStringHandler(\"4\", false)\n\n\tapp.Done(h)\n\tapp.Get(\"/\", writeStringHandler(\"123\", false))\n\n\t// remember: the handler stored in var didn't had a `ctx.Next()`, modified its clone above with adding a `ctx.Next()`\n\t// note the \"clone\" word, the original handler shouldn't be changed.\n\tapp.Party(\"/c\").SetExecutionRules(router.ExecutionRules{}).Get(\"/\", h, writeStringHandler(\"err caller modified!\", false))\n\n\ttestExecutionResponse(t, app, \"/\")\n\n\te := httptest.New(t, app)\n\te.GET(\"/c\").Expect().Status(httptest.StatusOK).Body().IsEqual(\"4\") // the \"should not\" should not be written.\n}\n"
  },
  {
    "path": "core/router/mime.go",
    "content": "package router\n\nimport (\n\t\"mime\"\n\t\"path/filepath\"\n\n\t\"github.com/kataras/iris/v12/context\"\n)\n\nvar types = map[string]string{\n\t\".3dm\":       \"x-world/x-3dmf\",\n\t\".3dmf\":      \"x-world/x-3dmf\",\n\t\".7z\":        \"application/x-7z-compressed\",\n\t\".a\":         \"application/octet-stream\",\n\t\".aab\":       \"application/x-authorware-bin\",\n\t\".aam\":       \"application/x-authorware-map\",\n\t\".aas\":       \"application/x-authorware-seg\",\n\t\".abc\":       \"text/vndabc\",\n\t\".ace\":       \"application/x-ace-compressed\",\n\t\".acgi\":      \"text/html\",\n\t\".afl\":       \"video/animaflex\",\n\t\".ai\":        \"application/postscript\",\n\t\".aif\":       \"audio/aiff\",\n\t\".aifc\":      \"audio/aiff\",\n\t\".aiff\":      \"audio/aiff\",\n\t\".aim\":       \"application/x-aim\",\n\t\".aip\":       \"text/x-audiosoft-intra\",\n\t\".alz\":       \"application/x-alz-compressed\",\n\t\".ani\":       \"application/x-navi-animation\",\n\t\".aos\":       \"application/x-nokia-9000-communicator-add-on-software\",\n\t\".aps\":       \"application/mime\",\n\t\".apk\":       \"application/vnd.android.package-archive\",\n\t\".arc\":       \"application/x-arc-compressed\",\n\t\".arj\":       \"application/arj\",\n\t\".art\":       \"image/x-jg\",\n\t\".asf\":       \"video/x-ms-asf\",\n\t\".asm\":       \"text/x-asm\",\n\t\".asp\":       \"text/asp\",\n\t\".asx\":       \"application/x-mplayer2\",\n\t\".au\":        \"audio/basic\",\n\t\".avi\":       \"video/x-msvideo\",\n\t\".avs\":       \"video/avs-video\",\n\t\".bcpio\":     \"application/x-bcpio\",\n\t\".bin\":       \"application/mac-binary\",\n\t\".bmp\":       \"image/bmp\",\n\t\".boo\":       \"application/book\",\n\t\".book\":      \"application/book\",\n\t\".boz\":       \"application/x-bzip2\",\n\t\".bsh\":       \"application/x-bsh\",\n\t\".bz2\":       \"application/x-bzip2\",\n\t\".bz\":        \"application/x-bzip\",\n\t\".c++\":       context.ContentTextHeaderValue,\n\t\".c\":         \"text/x-c\",\n\t\".cab\":       \"application/vnd.ms-cab-compressed\",\n\t\".cat\":       \"application/vndms-pkiseccat\",\n\t\".cc\":        \"text/x-c\",\n\t\".ccad\":      \"application/clariscad\",\n\t\".cco\":       \"application/x-cocoa\",\n\t\".cdf\":       \"application/cdf\",\n\t\".cer\":       \"application/pkix-cert\",\n\t\".cha\":       \"application/x-chat\",\n\t\".chat\":      \"application/x-chat\",\n\t\".chrt\":      \"application/vnd.kde.kchart\",\n\t\".class\":     \"application/java\",\n\t\".com\":       context.ContentTextHeaderValue,\n\t\".conf\":      context.ContentTextHeaderValue,\n\t\".cpio\":      \"application/x-cpio\",\n\t\".cpp\":       \"text/x-c\",\n\t\".cpt\":       \"application/mac-compactpro\",\n\t\".crl\":       \"application/pkcs-crl\",\n\t\".crt\":       \"application/pkix-cert\",\n\t\".crx\":       \"application/x-chrome-extension\",\n\t\".csh\":       \"text/x-scriptcsh\",\n\t\".css\":       \"text/css\",\n\t\".csv\":       \"text/csv\",\n\t\".cxx\":       context.ContentTextHeaderValue,\n\t\".dar\":       \"application/x-dar\",\n\t\".dcr\":       \"application/x-director\",\n\t\".deb\":       \"application/x-debian-package\",\n\t\".deepv\":     \"application/x-deepv\",\n\t\".def\":       context.ContentTextHeaderValue,\n\t\".der\":       \"application/x-x509-ca-cert\",\n\t\".dif\":       \"video/x-dv\",\n\t\".dir\":       \"application/x-director\",\n\t\".divx\":      \"video/divx\",\n\t\".dl\":        \"video/dl\",\n\t\".dmg\":       \"application/x-apple-diskimage\",\n\t\".doc\":       \"application/msword\",\n\t\".dot\":       \"application/msword\",\n\t\".dp\":        \"application/commonground\",\n\t\".drw\":       \"application/drafting\",\n\t\".dump\":      \"application/octet-stream\",\n\t\".dv\":        \"video/x-dv\",\n\t\".dvi\":       \"application/x-dvi\",\n\t\".dwf\":       \"drawing/x-dwf=(old)\",\n\t\".dwg\":       \"application/acad\",\n\t\".dxf\":       \"application/dxf\",\n\t\".dxr\":       \"application/x-director\",\n\t\".el\":        \"text/x-scriptelisp\",\n\t\".elc\":       \"application/x-bytecodeelisp=(compiled=elisp)\",\n\t\".eml\":       \"message/rfc822\",\n\t\".env\":       \"application/x-envoy\",\n\t\".eps\":       \"application/postscript\",\n\t\".es\":        \"application/x-esrehber\",\n\t\".etx\":       \"text/x-setext\",\n\t\".evy\":       \"application/envoy\",\n\t\".exe\":       \"application/octet-stream\",\n\t\".f77\":       \"text/x-fortran\",\n\t\".f90\":       \"text/x-fortran\",\n\t\".f\":         \"text/x-fortran\",\n\t\".fdf\":       \"application/vndfdf\",\n\t\".fif\":       \"application/fractals\",\n\t\".fli\":       \"video/fli\",\n\t\".flo\":       \"image/florian\",\n\t\".flv\":       \"video/x-flv\",\n\t\".flx\":       \"text/vndfmiflexstor\",\n\t\".fmf\":       \"video/x-atomic3d-feature\",\n\t\".for\":       \"text/x-fortran\",\n\t\".fpx\":       \"image/vndfpx\",\n\t\".frl\":       \"application/freeloader\",\n\t\".funk\":      \"audio/make\",\n\t\".g3\":        \"image/g3fax\",\n\t\".g\":         context.ContentTextHeaderValue,\n\t\".gif\":       \"image/gif\",\n\t\".gl\":        \"video/gl\",\n\t\".gsd\":       \"audio/x-gsm\",\n\t\".gsm\":       \"audio/x-gsm\",\n\t\".gsp\":       \"application/x-gsp\",\n\t\".gss\":       \"application/x-gss\",\n\t\".gtar\":      \"application/x-gtar\",\n\t\".gz\":        \"application/x-compressed\",\n\t\".gzip\":      \"application/x-gzip\",\n\t\".h\":         \"text/x-h\",\n\t\".hdf\":       \"application/x-hdf\",\n\t\".help\":      \"application/x-helpfile\",\n\t\".hgl\":       \"application/vndhp-hpgl\",\n\t\".hh\":        \"text/x-h\",\n\t\".hlb\":       \"text/x-script\",\n\t\".hlp\":       \"application/hlp\",\n\t\".hpg\":       \"application/vndhp-hpgl\",\n\t\".hpgl\":      \"application/vndhp-hpgl\",\n\t\".hqx\":       \"application/binhex\",\n\t\".hta\":       \"application/hta\",\n\t\".htc\":       \"text/x-component\",\n\t\".htm\":       \"text/html\",\n\t\".html\":      \"text/html\",\n\t\".htmls\":     \"text/html\",\n\t\".htt\":       \"text/webviewhtml\",\n\t\".htx\":       \"text/html\",\n\t\".ice\":       \"x-conference/x-cooltalk\",\n\t\".ico\":       \"image/x-icon\",\n\t\".ics\":       \"text/calendar\",\n\t\".icz\":       \"text/calendar\",\n\t\".idc\":       context.ContentTextHeaderValue,\n\t\".ief\":       \"image/ief\",\n\t\".iefs\":      \"image/ief\",\n\t\".iges\":      \"application/iges\",\n\t\".igs\":       \"application/iges\",\n\t\".ima\":       \"application/x-ima\",\n\t\".imap\":      \"application/x-httpd-imap\",\n\t\".inf\":       \"application/inf\",\n\t\".ins\":       \"application/x-internett-signup\",\n\t\".ip\":        \"application/x-ip2\",\n\t\".isu\":       \"video/x-isvideo\",\n\t\".it\":        \"audio/it\",\n\t\".iv\":        \"application/x-inventor\",\n\t\".ivr\":       \"i-world/i-vrml\",\n\t\".ivy\":       \"application/x-livescreen\",\n\t\".jam\":       \"audio/x-jam\",\n\t\".jav\":       \"text/x-java-source\",\n\t\".java\":      \"text/x-java-source\",\n\t\".jcm\":       \"application/x-java-commerce\",\n\t\".jfif-tbnl\": \"image/jpeg\",\n\t\".jfif\":      \"image/jpeg\",\n\t\".jnlp\":      \"application/x-java-jnlp-file\",\n\t\".jpe\":       \"image/jpeg\",\n\t\".jpeg\":      \"image/jpeg\",\n\t\".jpg\":       \"image/jpeg\",\n\t\".jps\":       \"image/x-jps\",\n\t\".js\":        context.ContentJavascriptHeaderValue,\n\t\".mjs\":       context.ContentJavascriptHeaderValue,\n\t\".json\":      context.ContentJSONHeaderValue,\n\t\".vue\":       context.ContentJavascriptHeaderValue,\n\t\".jut\":       \"image/jutvision\",\n\t\".kar\":       \"audio/midi\",\n\t\".karbon\":    \"application/vnd.kde.karbon\",\n\t\".kfo\":       \"application/vnd.kde.kformula\",\n\t\".flw\":       \"application/vnd.kde.kivio\",\n\t\".kml\":       \"application/vnd.google-earth.kml+xml\",\n\t\".kmz\":       \"application/vnd.google-earth.kmz\",\n\t\".kon\":       \"application/vnd.kde.kontour\",\n\t\".kpr\":       \"application/vnd.kde.kpresenter\",\n\t\".kpt\":       \"application/vnd.kde.kpresenter\",\n\t\".ksp\":       \"application/vnd.kde.kspread\",\n\t\".kwd\":       \"application/vnd.kde.kword\",\n\t\".kwt\":       \"application/vnd.kde.kword\",\n\t\".ksh\":       \"text/x-scriptksh\",\n\t\".la\":        \"audio/nspaudio\",\n\t\".lam\":       \"audio/x-liveaudio\",\n\t\".latex\":     \"application/x-latex\",\n\t\".lha\":       \"application/lha\",\n\t\".lhx\":       \"application/octet-stream\",\n\t\".list\":      context.ContentTextHeaderValue,\n\t\".lma\":       \"audio/nspaudio\",\n\t\".log\":       context.ContentTextHeaderValue,\n\t\".lsp\":       \"text/x-scriptlisp\",\n\t\".lst\":       context.ContentTextHeaderValue,\n\t\".lsx\":       \"text/x-la-asf\",\n\t\".ltx\":       \"application/x-latex\",\n\t\".lzh\":       \"application/octet-stream\",\n\t\".lzx\":       \"application/lzx\",\n\t\".m1v\":       \"video/mpeg\",\n\t\".m2a\":       \"audio/mpeg\",\n\t\".m2v\":       \"video/mpeg\",\n\t\".m3u\":       \"audio/x-mpegurl\",\n\t\".m\":         \"text/x-m\",\n\t\".man\":       \"application/x-troff-man\",\n\t\".manifest\":  \"text/cache-manifest\",\n\t\".map\":       \"application/x-navimap\",\n\t\".mar\":       context.ContentTextHeaderValue,\n\t\".mbd\":       \"application/mbedlet\",\n\t\".mc$\":       \"application/x-magic-cap-package-10\",\n\t\".mcd\":       \"application/mcad\",\n\t\".mcf\":       \"text/mcf\",\n\t\".mcp\":       \"application/netmc\",\n\t\".me\":        \"application/x-troff-me\",\n\t\".mht\":       \"message/rfc822\",\n\t\".mhtml\":     \"message/rfc822\",\n\t\".mid\":       \"application/x-midi\",\n\t\".midi\":      \"application/x-midi\",\n\t\".mif\":       \"application/x-frame\",\n\t\".mime\":      \"message/rfc822\",\n\t\".mjf\":       \"audio/x-vndaudioexplosionmjuicemediafile\",\n\t\".mjpg\":      \"video/x-motion-jpeg\",\n\t\".mm\":        \"application/base64\",\n\t\".mme\":       \"application/base64\",\n\t\".mod\":       \"audio/mod\",\n\t\".moov\":      \"video/quicktime\",\n\t\".mov\":       \"video/quicktime\",\n\t\".movie\":     \"video/x-sgi-movie\",\n\t\".mp2\":       \"audio/mpeg\",\n\t\".mp3\":       \"audio/mpeg\",\n\t\".mp4\":       \"video/mp4\",\n\t\".mpa\":       \"audio/mpeg\",\n\t\".mpc\":       \"application/x-project\",\n\t\".mpe\":       \"video/mpeg\",\n\t\".mpeg\":      \"video/mpeg\",\n\t\".mpg\":       \"video/mpeg\",\n\t\".mpga\":      \"audio/mpeg\",\n\t\".mpp\":       \"application/vndms-project\",\n\t\".mpt\":       \"application/x-project\",\n\t\".mpv\":       \"application/x-project\",\n\t\".mpx\":       \"application/x-project\",\n\t\".mrc\":       \"application/marc\",\n\t\".ms\":        \"application/x-troff-ms\",\n\t\".mv\":        \"video/x-sgi-movie\",\n\t\".my\":        \"audio/make\",\n\t\".mzz\":       \"application/x-vndaudioexplosionmzz\",\n\t\".nap\":       \"image/naplps\",\n\t\".naplps\":    \"image/naplps\",\n\t\".nc\":        \"application/x-netcdf\",\n\t\".ncm\":       \"application/vndnokiaconfiguration-message\",\n\t\".nif\":       \"image/x-niff\",\n\t\".niff\":      \"image/x-niff\",\n\t\".nix\":       \"application/x-mix-transfer\",\n\t\".nsc\":       \"application/x-conference\",\n\t\".nvd\":       \"application/x-navidoc\",\n\t\".o\":         \"application/octet-stream\",\n\t\".oda\":       \"application/oda\",\n\t\".odb\":       \"application/vnd.oasis.opendocument.database\",\n\t\".odc\":       \"application/vnd.oasis.opendocument.chart\",\n\t\".odf\":       \"application/vnd.oasis.opendocument.formula\",\n\t\".odg\":       \"application/vnd.oasis.opendocument.graphics\",\n\t\".odi\":       \"application/vnd.oasis.opendocument.image\",\n\t\".odm\":       \"application/vnd.oasis.opendocument.text-master\",\n\t\".odp\":       \"application/vnd.oasis.opendocument.presentation\",\n\t\".ods\":       \"application/vnd.oasis.opendocument.spreadsheet\",\n\t\".odt\":       \"application/vnd.oasis.opendocument.text\",\n\t\".oga\":       \"audio/ogg\",\n\t\".ogg\":       \"audio/ogg\",\n\t\".ogv\":       \"video/ogg\",\n\t\".omc\":       \"application/x-omc\",\n\t\".omcd\":      \"application/x-omcdatamaker\",\n\t\".omcr\":      \"application/x-omcregerator\",\n\t\".otc\":       \"application/vnd.oasis.opendocument.chart-template\",\n\t\".otf\":       \"application/vnd.oasis.opendocument.formula-template\",\n\t\".otg\":       \"application/vnd.oasis.opendocument.graphics-template\",\n\t\".oth\":       \"application/vnd.oasis.opendocument.text-web\",\n\t\".oti\":       \"application/vnd.oasis.opendocument.image-template\",\n\t\".otm\":       \"application/vnd.oasis.opendocument.text-master\",\n\t\".otp\":       \"application/vnd.oasis.opendocument.presentation-template\",\n\t\".ots\":       \"application/vnd.oasis.opendocument.spreadsheet-template\",\n\t\".ott\":       \"application/vnd.oasis.opendocument.text-template\",\n\t\".p10\":       \"application/pkcs10\",\n\t\".p12\":       \"application/pkcs-12\",\n\t\".p7a\":       \"application/x-pkcs7-signature\",\n\t\".p7c\":       \"application/pkcs7-mime\",\n\t\".p7m\":       \"application/pkcs7-mime\",\n\t\".p7r\":       \"application/x-pkcs7-certreqresp\",\n\t\".p7s\":       \"application/pkcs7-signature\",\n\t\".p\":         \"text/x-pascal\",\n\t\".part\":      \"application/pro_eng\",\n\t\".pas\":       \"text/pascal\",\n\t\".pbm\":       \"image/x-portable-bitmap\",\n\t\".pcl\":       \"application/vndhp-pcl\",\n\t\".pct\":       \"image/x-pict\",\n\t\".pcx\":       \"image/x-pcx\",\n\t\".pdb\":       \"chemical/x-pdb\",\n\t\".pdf\":       \"application/pdf\",\n\t\".pfunk\":     \"audio/make\",\n\t\".pgm\":       \"image/x-portable-graymap\",\n\t\".pic\":       \"image/pict\",\n\t\".pict\":      \"image/pict\",\n\t\".pkg\":       \"application/x-newton-compatible-pkg\",\n\t\".pko\":       \"application/vndms-pkipko\",\n\t\".pl\":        \"text/x-scriptperl\",\n\t\".plx\":       \"application/x-pixclscript\",\n\t\".pm4\":       \"application/x-pagemaker\",\n\t\".pm5\":       \"application/x-pagemaker\",\n\t\".pm\":        \"text/x-scriptperl-module\",\n\t\".png\":       \"image/png\",\n\t\".pnm\":       \"application/x-portable-anymap\",\n\t\".pot\":       \"application/mspowerpoint\",\n\t\".pov\":       \"model/x-pov\",\n\t\".ppa\":       \"application/vndms-powerpoint\",\n\t\".ppm\":       \"image/x-portable-pixmap\",\n\t\".pps\":       \"application/mspowerpoint\",\n\t\".ppt\":       \"application/mspowerpoint\",\n\t\".ppz\":       \"application/mspowerpoint\",\n\t\".pre\":       \"application/x-freelance\",\n\t\".prt\":       \"application/pro_eng\",\n\t\".ps\":        \"application/postscript\",\n\t\".psd\":       \"application/octet-stream\",\n\t\".pvu\":       \"paleovu/x-pv\",\n\t\".pwz\":       \"application/vndms-powerpoint\",\n\t\".py\":        \"text/x-scriptphyton\",\n\t\".pyc\":       \"application/x-bytecodepython\",\n\t\".qcp\":       \"audio/vndqcelp\",\n\t\".qd3\":       \"x-world/x-3dmf\",\n\t\".qd3d\":      \"x-world/x-3dmf\",\n\t\".qif\":       \"image/x-quicktime\",\n\t\".qt\":        \"video/quicktime\",\n\t\".qtc\":       \"video/x-qtc\",\n\t\".qti\":       \"image/x-quicktime\",\n\t\".qtif\":      \"image/x-quicktime\",\n\t\".ra\":        \"audio/x-pn-realaudio\",\n\t\".ram\":       \"audio/x-pn-realaudio\",\n\t\".rar\":       \"application/x-rar-compressed\",\n\t\".ras\":       \"application/x-cmu-raster\",\n\t\".rast\":      \"image/cmu-raster\",\n\t\".rexx\":      \"text/x-scriptrexx\",\n\t\".rf\":        \"image/vndrn-realflash\",\n\t\".rgb\":       \"image/x-rgb\",\n\t\".rm\":        \"application/vndrn-realmedia\",\n\t\".rmi\":       \"audio/mid\",\n\t\".rmm\":       \"audio/x-pn-realaudio\",\n\t\".rmp\":       \"audio/x-pn-realaudio\",\n\t\".rng\":       \"application/ringing-tones\",\n\t\".rnx\":       \"application/vndrn-realplayer\",\n\t\".roff\":      \"application/x-troff\",\n\t\".rp\":        \"image/vndrn-realpix\",\n\t\".rpm\":       \"audio/x-pn-realaudio-plugin\",\n\t\".rt\":        \"text/vndrn-realtext\",\n\t\".rtf\":       \"text/richtext\",\n\t\".rtx\":       \"text/richtext\",\n\t\".rv\":        \"video/vndrn-realvideo\",\n\t\".s\":         \"text/x-asm\",\n\t\".s3m\":       \"audio/s3m\",\n\t\".s7z\":       \"application/x-7z-compressed\",\n\t\".saveme\":    \"application/octet-stream\",\n\t\".sbk\":       \"application/x-tbook\",\n\t\".scm\":       \"text/x-scriptscheme\",\n\t\".sdml\":      context.ContentTextHeaderValue,\n\t\".sdp\":       \"application/sdp\",\n\t\".sdr\":       \"application/sounder\",\n\t\".sea\":       \"application/sea\",\n\t\".set\":       \"application/set\",\n\t\".sgm\":       \"text/x-sgml\",\n\t\".sgml\":      \"text/x-sgml\",\n\t\".sh\":        \"text/x-scriptsh\",\n\t\".shar\":      \"application/x-bsh\",\n\t\".shtml\":     \"text/x-server-parsed-html\",\n\t\".sid\":       \"audio/x-psid\",\n\t\".skd\":       \"application/x-koan\",\n\t\".skm\":       \"application/x-koan\",\n\t\".skp\":       \"application/x-koan\",\n\t\".skt\":       \"application/x-koan\",\n\t\".sit\":       \"application/x-stuffit\",\n\t\".sitx\":      \"application/x-stuffitx\",\n\t\".sl\":        \"application/x-seelogo\",\n\t\".smi\":       \"application/smil\",\n\t\".smil\":      \"application/smil\",\n\t\".snd\":       \"audio/basic\",\n\t\".sol\":       \"application/solids\",\n\t\".spc\":       \"text/x-speech\",\n\t\".spl\":       \"application/futuresplash\",\n\t\".spr\":       \"application/x-sprite\",\n\t\".sprite\":    \"application/x-sprite\",\n\t\".spx\":       \"audio/ogg\",\n\t\".src\":       \"application/x-wais-source\",\n\t\".ssi\":       \"text/x-server-parsed-html\",\n\t\".ssm\":       \"application/streamingmedia\",\n\t\".sst\":       \"application/vndms-pkicertstore\",\n\t\".step\":      \"application/step\",\n\t\".stl\":       \"application/sla\",\n\t\".stp\":       \"application/step\",\n\t\".sv4cpio\":   \"application/x-sv4cpio\",\n\t\".sv4crc\":    \"application/x-sv4crc\",\n\t\".svf\":       \"image/vnddwg\",\n\t\".svg\":       \"image/svg+xml\",\n\t\".svr\":       \"application/x-world\",\n\t\".swf\":       \"application/x-shockwave-flash\",\n\t\".t\":         \"application/x-troff\",\n\t\".talk\":      \"text/x-speech\",\n\t\".tar\":       \"application/x-tar\",\n\t\".tbk\":       \"application/toolbook\",\n\t\".tcl\":       \"text/x-scripttcl\",\n\t\".tcsh\":      \"text/x-scripttcsh\",\n\t\".tex\":       \"application/x-tex\",\n\t\".texi\":      \"application/x-texinfo\",\n\t\".texinfo\":   \"application/x-texinfo\",\n\t\".text\":      context.ContentTextHeaderValue,\n\t\".tgz\":       \"application/gnutar\",\n\t\".tif\":       \"image/tiff\",\n\t\".tiff\":      \"image/tiff\",\n\t\".tr\":        \"application/x-troff\",\n\t\".tsi\":       \"audio/tsp-audio\",\n\t\".tsp\":       \"application/dsptype\",\n\t\".tsv\":       \"text/tab-separated-values\",\n\t\".turbot\":    \"image/florian\",\n\t\".txt\":       context.ContentTextHeaderValue,\n\t\".uil\":       \"text/x-uil\",\n\t\".uni\":       \"text/uri-list\",\n\t\".unis\":      \"text/uri-list\",\n\t\".unv\":       \"application/i-deas\",\n\t\".uri\":       \"text/uri-list\",\n\t\".uris\":      \"text/uri-list\",\n\t\".ustar\":     \"application/x-ustar\",\n\t\".uu\":        \"text/x-uuencode\",\n\t\".uue\":       \"text/x-uuencode\",\n\t\".vcd\":       \"application/x-cdlink\",\n\t\".vcf\":       \"text/x-vcard\",\n\t\".vcard\":     \"text/x-vcard\",\n\t\".vcs\":       \"text/x-vcalendar\",\n\t\".vda\":       \"application/vda\",\n\t\".vdo\":       \"video/vdo\",\n\t\".vew\":       \"application/groupwise\",\n\t\".viv\":       \"video/vivo\",\n\t\".vivo\":      \"video/vivo\",\n\t\".vmd\":       \"application/vocaltec-media-desc\",\n\t\".vmf\":       \"application/vocaltec-media-file\",\n\t\".voc\":       \"audio/voc\",\n\t\".vos\":       \"video/vosaic\",\n\t\".vox\":       \"audio/voxware\",\n\t\".vqe\":       \"audio/x-twinvq-plugin\",\n\t\".vqf\":       \"audio/x-twinvq\",\n\t\".vql\":       \"audio/x-twinvq-plugin\",\n\t\".vrml\":      \"application/x-vrml\",\n\t\".vrt\":       \"x-world/x-vrt\",\n\t\".vsd\":       \"application/x-visio\",\n\t\".vst\":       \"application/x-visio\",\n\t\".vsw\":       \"application/x-visio\",\n\t\".w60\":       \"application/wordperfect60\",\n\t\".w61\":       \"application/wordperfect61\",\n\t\".w6w\":       \"application/msword\",\n\t\".wav\":       \"audio/wav\",\n\t\".wb1\":       \"application/x-qpro\",\n\t\".wbmp\":      \"image/vnd.wap.wbmp\",\n\t\".web\":       \"application/vndxara\",\n\t\".wiz\":       \"application/msword\",\n\t\".wk1\":       \"application/x-123\",\n\t\".wmf\":       \"windows/metafile\",\n\t\".wml\":       \"text/vnd.wap.wml\",\n\t\".wmlc\":      \"application/vnd.wap.wmlc\",\n\t\".wmls\":      \"text/vnd.wap.wmlscript\",\n\t\".wmlsc\":     \"application/vnd.wap.wmlscriptc\",\n\t\".word\":      \"application/msword\",\n\t\".wp5\":       \"application/wordperfect\",\n\t\".wp6\":       \"application/wordperfect\",\n\t\".wp\":        \"application/wordperfect\",\n\t\".wpd\":       \"application/wordperfect\",\n\t\".wq1\":       \"application/x-lotus\",\n\t\".wri\":       \"application/mswrite\",\n\t\".wrl\":       \"application/x-world\",\n\t\".wrz\":       \"model/vrml\",\n\t\".wsc\":       \"text/scriplet\",\n\t\".wsrc\":      \"application/x-wais-source\",\n\t\".wtk\":       \"application/x-wintalk\",\n\t\".x-png\":     \"image/png\",\n\t\".xbm\":       \"image/x-xbitmap\",\n\t\".xdr\":       \"video/x-amt-demorun\",\n\t\".xgz\":       \"xgl/drawing\",\n\t\".xif\":       \"image/vndxiff\",\n\t\".xl\":        \"application/excel\",\n\t\".xla\":       \"application/excel\",\n\t\".xlb\":       \"application/excel\",\n\t\".xlc\":       \"application/excel\",\n\t\".xld\":       \"application/excel\",\n\t\".xlk\":       \"application/excel\",\n\t\".xll\":       \"application/excel\",\n\t\".xlm\":       \"application/excel\",\n\t\".xls\":       \"application/excel\",\n\t\".xlt\":       \"application/excel\",\n\t\".xlv\":       \"application/excel\",\n\t\".xlw\":       \"application/excel\",\n\t\".xm\":        \"audio/xm\",\n\t\".xml\":       context.ContentXMLHeaderValue,\n\t\".xmz\":       \"xgl/movie\",\n\t\".xpix\":      \"application/x-vndls-xpix\",\n\t\".xpm\":       \"image/x-xpixmap\",\n\t\".xsr\":       \"video/x-amt-showrun\",\n\t\".xwd\":       \"image/x-xwd\",\n\t\".xyz\":       \"chemical/x-pdb\",\n\t\".z\":         \"application/x-compress\",\n\t\".zip\":       \"application/zip\",\n\t\".zoo\":       \"application/octet-stream\",\n\t\".zsh\":       \"text/x-scriptzsh\",\n\t\".docx\":      \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n\t\".docm\":      \"application/vnd.ms-word.document.macroEnabled.12\",\n\t\".dotx\":      \"application/vnd.openxmlformats-officedocument.wordprocessingml.template\",\n\t\".dotm\":      \"application/vnd.ms-word.template.macroEnabled.12\",\n\t\".xlsx\":      \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n\t\".xlsm\":      \"application/vnd.ms-excel.sheet.macroEnabled.12\",\n\t\".xltx\":      \"application/vnd.openxmlformats-officedocument.spreadsheetml.template\",\n\t\".xltm\":      \"application/vnd.ms-excel.template.macroEnabled.12\",\n\t\".xlsb\":      \"application/vnd.ms-excel.sheet.binary.macroEnabled.12\",\n\t\".xlam\":      \"application/vnd.ms-excel.addin.macroEnabled.12\",\n\t\".pptx\":      \"application/vnd.openxmlformats-officedocument.presentationml.presentation\",\n\t\".pptm\":      \"application/vnd.ms-powerpoint.presentation.macroEnabled.12\",\n\t\".ppsx\":      \"application/vnd.openxmlformats-officedocument.presentationml.slideshow\",\n\t\".ppsm\":      \"application/vnd.ms-powerpoint.slideshow.macroEnabled.12\",\n\t\".potx\":      \"application/vnd.openxmlformats-officedocument.presentationml.template\",\n\t\".potm\":      \"application/vnd.ms-powerpoint.template.macroEnabled.12\",\n\t\".ppam\":      \"application/vnd.ms-powerpoint.addin.macroEnabled.12\",\n\t\".sldx\":      \"application/vnd.openxmlformats-officedocument.presentationml.slide\",\n\t\".sldm\":      \"application/vnd.ms-powerpoint.slide.macroEnabled.12\",\n\t\".thmx\":      \"application/vnd.ms-officetheme\",\n\t\".onetoc\":    \"application/onenote\",\n\t\".onetoc2\":   \"application/onenote\",\n\t\".onetmp\":    \"application/onenote\",\n\t\".onepkg\":    \"application/onenote\",\n\t\".xpi\":       \"application/x-xpinstall\",\n\t\".wasm\":      \"application/wasm\",\n}\n\nfunc init() {\n\tfor ext, typ := range types {\n\t\t// skip errors\n\t\t_ = mime.AddExtensionType(ext, typ)\n\t}\n}\n\n// TypeByExtension returns the MIME type associated with the file extension ext.\n// The extension ext should begin with a leading dot, as in \".html\".\n// When ext has no associated type, typeByExtension returns \"\".\n//\n// Extensions are looked up first case-sensitively, then case-insensitively.\n//\n// The built-in table is small but on unix it is augmented by the local\n// system's mime.types file(s) if available under one or more of these\n// names:\n//\n//\t/etc/mime.types\n//\t/etc/apache2/mime.types\n//\t/etc/apache/mime.types\n//\n// On Windows, MIME types are extracted from the registry.\n//\n// Text types have the charset parameter set to \"utf-8\" by default.\nfunc TypeByExtension(ext string) (typ string) {\n\tif len(ext) < 2 {\n\t\treturn\n\t}\n\n\tif ext[0] != '.' { // try to take it by filename\n\t\ttyp = context.TrimHeaderValue(TypeByFilename(ext))\n\t\tif typ == \"\" {\n\t\t\text = \".\" + ext // if error or something wrong then prepend the dot\n\t\t}\n\t}\n\n\tif typ == \"\" {\n\t\ttyp = context.TrimHeaderValue(mime.TypeByExtension(ext))\n\t}\n\n\t// mime.TypeByExtension returns as text/plain; | charset=utf-8 the static .js (not always)\n\tif ext == \".js\" || ext == \".mjs\" && (typ == context.ContentJavascriptHeaderValue || typ == context.ContentTextHeaderValue) {\n\t\ttyp = context.ContentJavascriptHeaderValue\n\t}\n\n\treturn typ\n}\n\n// TypeByFilename same as TypeByExtension\n// but receives a filename path instead.\nfunc TypeByFilename(fullFilename string) string {\n\text := filepath.Ext(fullFilename)\n\treturn TypeByExtension(ext)\n}\n"
  },
  {
    "path": "core/router/party.go",
    "content": "package router\n\nimport (\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/macro\"\n\n\t\"github.com/kataras/golog\"\n)\n\n// Party is just a group joiner of routes which have the same prefix and share same middleware(s) also.\n// Party could also be named as 'Join' or 'Node' or 'Group' , Party chosen because it is fun.\n//\n// Look the `APIBuilder` structure for its implementation.\ntype Party interface {\n\t// Logger returns the Application Logger.\n\tLogger() *golog.Logger\n\n\t// IsRoot reports whether this Party is the root Application's one.\n\t// It will return false on all children Parties, no exception.\n\tIsRoot() bool\n\n\t// ConfigureContainer accepts one or more functions that can be used\n\t// to configure dependency injection features of this Party\n\t// such as register dependency and register handlers that will automatically inject any valid dependency.\n\t// However, if the \"builder\" parameter is nil or not provided then it just returns the *APIContainer,\n\t// which automatically initialized on Party allocation.\n\t//\n\t// It returns the same `APIBuilder` featured with Dependency Injection.\n\tConfigureContainer(builder ...func(*APIContainer)) *APIContainer\n\t// EnsureStaticBindings panics on struct handler (controller)\n\t// if at least one input binding depends on the request and not in a static structure.\n\t// Should be called before `RegisterDependency`.\n\tEnsureStaticBindings() Party\n\t// RegisterDependency calls the `ConfigureContainer.RegisterDependency` method\n\t// with the provided value(s). See `HandleFunc` and `PartyConfigure` methods too.\n\tRegisterDependency(dependencies ...any)\n\t// HandleFunc registers a route on HTTP verb \"method\" and relative, to this Party, path.\n\t// It is like the `Handle` method but it accepts one or more \"handlersFn\" functions\n\t// that each one of them can accept any input arguments as the HTTP request and\n\t// output a result as the HTTP response. Specifically,\n\t// the input of the \"handlersFn\" can be any registered dependency\n\t// (see ConfigureContainer().RegisterDependency)\n\t// or leave the framework to parse the request and fill the values accordingly.\n\t// The output of the \"handlersFn\" can be any output result:\n\t//  custom structs <T>, string, []byte, int, error,\n\t//  a combination of the above, hero.Result(hero.View | hero.Response) and more.\n\t//\n\t// If more than one handler function is registered\n\t// then the execution happens without the nessecity of the `Context.Next` method,\n\t// simply, to stop the execution and not continue to the next \"handlersFn\" in chain\n\t// you should return an `iris.ErrStopExecution`.\n\t//\n\t// Example Code:\n\t//\n\t// The client's request body and server's response body Go types.\n\t// Could be any data structure.\n\t//\n\t// \ttype (\n\t// \t\trequest struct {\n\t// \t\t\tFirstname string `json:\"firstname\"`\n\t// \t\t\tLastname string `json:\"lastname\"`\n\t// \t\t}\n\t//\n\t// \t\tresponse struct {\n\t// \t\t\tID uint64 `json:\"id\"`\n\t// \t\t\tMessage string `json:\"message\"`\n\t// \t\t}\n\t// \t)\n\t//\n\t// Register the route hander.\n\t//\n\t//              HTTP VERB    ROUTE PATH       ROUTE HANDLER\n\t//  app.HandleFunc(\"PUT\", \"/users/{id:uint64}\", updateUser)\n\t//\n\t// Code the route handler function.\n\t// Path parameters and request body are binded\n\t// automatically.\n\t// The \"id\" uint64 binds to \"{id:uint64}\" route path parameter and\n\t// the \"input\" binds to client request data such as JSON.\n\t//\n\t// \tfunc updateUser(id uint64, input request) response {\n\t// \t\t// [custom logic...]\n\t//\n\t// \t\treturn response{\n\t// \t\t\tID:id,\n\t// \t\t\tMessage: \"User updated successfully\",\n\t// \t\t}\n\t// \t}\n\t//\n\t// Simulate a client request which sends data\n\t// to the server and prints out the response.\n\t//\n\t// \tcurl --request PUT -d '{\"firstname\":\"John\",\"lastname\":\"Doe\"}' \\\n\t// \t-H \"Content-Type: application/json\" \\\n\t// \thttp://localhost:8080/users/42\n\t//\n\t// \t{\n\t// \t\t\"id\": 42,\n\t// \t\t\"message\": \"User updated successfully\"\n\t// \t}\n\t//\n\t// See the `ConfigureContainer` for more features regrading\n\t// the dependency injection, mvc and function handlers.\n\t//\n\t// This method is just a shortcut for the `ConfigureContainer().Handle` one.\n\tHandleFunc(method, relativePath string, handlersFn ...any) *Route\n\t// UseFunc registers a function which can accept one or more\n\t// dependencies (see RegisterDependency) and returns an iris.Handler\n\t// or a result of <T> and/or an error.\n\t//\n\t// This method is just a shortcut of the `ConfigureContainer().Use`.\n\tUseFunc(handlersFn ...any)\n\n\t// GetRelPath returns the current party's relative path.\n\t// i.e:\n\t// if r := app.Party(\"/users\"), then the `r.GetRelPath()` is the \"/users\".\n\t// if r := app.Party(\"www.\") or app.Subdomain(\"www\") then the `r.GetRelPath()` is the \"www.\".\n\tGetRelPath() string\n\t// Macros returns the macro collection that is responsible\n\t// to register custom macros with their own parameter types and their macro functions for all routes.\n\t//\n\t// Learn more at:  https://github.com/kataras/iris/tree/main/_examples/routing/dynamic-path\n\tMacros() *macro.Macros\n\n\t// Properties returns the original Party's properties map,\n\t// it can be modified before server startup but not afterwards.\n\tProperties() context.Map\n\n\t// SetRoutesNoLog disables (true) the verbose logging for the next registered\n\t// routes under this Party and its children.\n\t//\n\t// To disable logging for controllers under MVC Application,\n\t// see `mvc/Application.SetControllersNoLog` instead.\n\t//\n\t// Defaults to false when log level is \"debug\".\n\tSetRoutesNoLog(disable bool) Party\n\n\t// OnErrorCode registers a handlers chain for this `Party` for a specific HTTP status code.\n\t// Read more at: http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml\n\t// Look `UseError` and `OnAnyErrorCode` too.\n\tOnErrorCode(statusCode int, handlers ...context.Handler) []*Route\n\t// OnAnyErrorCode registers a handlers chain for all error codes\n\t// (4xxx and 5xxx, change the `ClientErrorCodes` and `ServerErrorCodes` variables to modify those)\n\t// Look `UseError` and `OnErrorCode` too.\n\tOnAnyErrorCode(handlers ...context.Handler) []*Route\n\n\t// Party returns a new child Party which inherites its\n\t// parent's options and middlewares.\n\t// If \"relativePath\" matches the parent's one then it returns the current Party.\n\t// A Party groups routes which may have the same prefix or subdomain and share same middlewares.\n\t//\n\t// To create a group of routes for subdomains\n\t// use the `Subdomain` or `WildcardSubdomain` methods\n\t// or pass a \"relativePath\" as \"admin.\" or \"*.\" respectfully.\n\tParty(relativePath string, middleware ...context.Handler) Party\n\t// PartyFunc same as `Party`, groups routes that share a base path or/and same handlers.\n\t// However this function accepts a function that receives this created Party instead.\n\t// Returns the Party in order the caller to be able to use this created Party to continue the\n\t// top-bottom routes \"tree\".\n\t//\n\t// Note: `iris#Party` and `core/router#Party` describes the exactly same interface.\n\t//\n\t// Usage:\n\t// app.PartyFunc(\"/users\", func(u iris.Party){\n\t//\tu.Use(authMiddleware, logMiddleware)\n\t//\tu.Get(\"/\", getAllUsers)\n\t//\tu.Post(\"/\", createOrUpdateUser)\n\t//\tu.Delete(\"/\", deleteUser)\n\t// })\n\t//\n\t// Look `Party` for more.\n\tPartyFunc(relativePath string, partyBuilderFunc func(p Party)) Party\n\t// PartyConfigure like `Party` and `PartyFunc` registers a new children Party\n\t// but instead it accepts a struct value which should implement the PartyConfigurator interface.\n\t//\n\t// PartyConfigure accepts the relative path of the child party\n\t// (As an exception, if it's empty then all configurators are applied to the current Party)\n\t// and one or more Party configurators and\n\t// executes the PartyConfigurator's Configure method.\n\t//\n\t// If the end-developer registered one or more dependencies upfront through\n\t// RegisterDependencies or ConfigureContainer.RegisterDependency methods\n\t// and \"p\" is a pointer to a struct then try to bind the unset/zero exported fields\n\t// to the registered dependencies, just like we do with Controllers.\n\t// Useful when the api's dependencies amount are too much to pass on a function.\n\t//\n\t// Usage:\n\t//  app.PartyConfigure(\"/users\", &api.UsersAPI{UserRepository: ..., ...})\n\t// Where UsersAPI looks like:\n\t//  type UsersAPI struct { [...] }\n\t//  func(api *UsersAPI) Configure(router iris.Party) {\n\t//   router.Get(\"/{id:uuid}\", api.getUser)\n\t//   [...]\n\t//  }\n\t// Usage with (static) dependencies:\n\t//  app.RegisterDependency(userRepo, ...)\n\t//  app.PartyConfigure(\"/users\", &api.UsersAPI{})\n\tPartyConfigure(relativePath string, partyReg ...PartyConfigurator) Party\n\t// Subdomain returns a new party which is responsible to register routes to\n\t// this specific \"subdomain\".\n\t//\n\t// If called from a child party then the subdomain will be prepended to the path instead of appended.\n\t// So if app.Subdomain(\"admin\").Subdomain(\"panel\") then the result is: \"panel.admin.\".\n\tSubdomain(subdomain string, middleware ...context.Handler) Party\n\n\t// UseRouter upserts one or more handlers that will be fired\n\t// right before the main router's request handler.\n\t//\n\t// Use this method to register handlers, that can ran\n\t// independently of the incoming request's values,\n\t// that they will be executed ALWAYS against ALL children incoming requests.\n\t// Example of use-case: CORS.\n\t//\n\t// Note that because these are executed before the router itself\n\t// the Context should not have access to the `GetCurrentRoute`\n\t// as it is not decided yet which route is responsible to handle the incoming request.\n\t// It's one level higher than the `WrapRouter`.\n\t// The context SHOULD call its `Next` method in order to proceed to\n\t// the next handler in the chain or the main request handler one.\n\tUseRouter(handlers ...context.Handler)\n\t// UseError upserts one or more handlers that will be fired,\n\t// as middleware, before any error handler registered through `On(Any)ErrorCode`.\n\t// See `OnErrorCode` too.\n\tUseError(handlers ...context.Handler)\n\t// Use appends Handler(s) to the current Party's routes and child routes.\n\t// If the current Party is the root, then it registers the middleware to all child Parties' routes too.\n\t// To register a middleware for error handlers, look `UseError` method instead.\n\tUse(middleware ...context.Handler)\n\t// UseOnce either inserts a middleware,\n\t// or on the basis of the middleware already existing,\n\t// replace that existing middleware instead.\n\t// To register a middleware for error handlers, look `UseError` method instead.\n\tUseOnce(handlers ...context.Handler)\n\t// Done appends to the very end, Handler(s) to the current Party's routes and child routes.\n\t// The difference from .Use is that this/or these Handler(s) are being always running last.\n\tDone(handlers ...context.Handler)\n\t// MiddlewareExists reports whether the given handler exists in the middleware chain.\n\tMiddlewareExists(handlerNameOrFunc any) bool\n\t// RemoveHandler deletes a handler from begin and done handlers\n\t// based on its name or the handler pc function.\n\t//\n\t// As an exception, if one of the arguments is a pointer to an int,\n\t// then this is used to set the total amount of removed handlers.\n\t//\n\t// Returns the Party itself for chain calls.\n\t//\n\t// Should be called before children routes regitration.\n\tRemoveHandler(namesOrHandlers ...any) Party\n\t// Reset removes all the begin and done handlers that may derived from the parent party via `Use` & `Done`,\n\t// and the execution rules.\n\t// Note that the `Reset` will not reset the handlers that are registered via `UseGlobal` & `DoneGlobal`.\n\t//\n\t// Returns this Party.\n\tReset() Party\n\t// ResetRouterFilters deactivates any previous registered\n\t// router filters and the parents ones for this Party.\n\t//\n\t// Returns this Party.\n\tResetRouterFilters() Party\n\n\t// AllowMethods will re-register the future routes that will be registered\n\t// via `Handle`, `Get`, `Post`, ... to the given \"methods\" on that Party and its children \"Parties\",\n\t// duplicates are not registered.\n\t//\n\t// Call of `AllowMethod` will override any previous allow methods.\n\tAllowMethods(methods ...string) Party\n\n\t// SetExecutionRules alters the execution flow of the route handlers outside of the handlers themselves.\n\t//\n\t// For example, if for some reason the desired result is the (done or all) handlers to be executed no matter what\n\t// even if no `ctx.Next()` is called in the previous handlers, including the begin(`Use`),\n\t// the main(`Handle`) and the done(`Done`) handlers themselves, then:\n\t// Party#SetExecutionRules(iris.ExecutionRules {\n\t//   Begin: iris.ExecutionOptions{Force: true},\n\t//   Main:  iris.ExecutionOptions{Force: true},\n\t//   Done:  iris.ExecutionOptions{Force: true},\n\t// })\n\t//\n\t// Note that if : true then the only remained way to \"break\" the handler chain is by `ctx.StopExecution()` now that `ctx.Next()` does not matter.\n\t//\n\t// These rules are per-party, so if a `Party` creates a child one then the same rules will be applied to that as well.\n\t// Reset of these rules (before `Party#Handle`) can be done with `Party#SetExecutionRules(iris.ExecutionRules{})`.\n\t//\n\t// The most common scenario for its use can be found inside Iris MVC Applications;\n\t// when we want the `Done` handlers of that specific mvc app's `Party`\n\t// to be executed but we don't want to add `ctx.Next()` on the `OurController#EndRequest`.\n\t//\n\t// Returns this Party.\n\t//\n\t// Example: https://github.com/kataras/iris/tree/main/_examples/mvc/middleware/without-ctx-next\n\tSetExecutionRules(executionRules ExecutionRules) Party\n\t// SetRegisterRule sets a `RouteRegisterRule` for this Party and its children.\n\t// Available values are:\n\t// * RouteOverride (the default one)\n\t// * RouteSkip\n\t// * RouteError\n\t// * RouteOverlap.\n\tSetRegisterRule(rule RouteRegisterRule) Party\n\n\t// Handle registers a route to the server's router.\n\t// if empty method is passed then handler(s) are being registered to all methods, same as .Any.\n\t//\n\t// Returns the read-only route information.\n\tHandle(method string, registeredPath string, handlers ...context.Handler) *Route\n\t// HandleMany works like `Handle` but can receive more than one\n\t// paths separated by spaces and returns always a slice of *Route instead of a single instance of Route.\n\t//\n\t// It's useful only if the same handler can handle more than one request paths,\n\t// otherwise use `Party` which can handle many paths with different handlers and middlewares.\n\t//\n\t// Usage:\n\t// \tapp.HandleMany(iris.MethodGet, \"/user /user/{id:uint64} /user/me\", userHandler)\n\t// At the other side, with `Handle` we've had to write:\n\t// \tapp.Handle(iris.MethodGet, \"/user\", userHandler)\n\t// \tapp.Handle(iris.MethodGet, \"/user/{id:uint64}\", userHandler)\n\t// \tapp.Handle(iris.MethodGet, \"/user/me\", userHandler)\n\t//\n\t// This method is used behind the scenes at the `Controller` function\n\t// in order to handle more than one paths for the same controller instance.\n\tHandleMany(method string, relativePath string, handlers ...context.Handler) []*Route\n\n\t// HandleDir registers a handler that serves HTTP requests\n\t// with the contents of a file system (physical or embedded).\n\t//\n\t// first parameter  : the route path\n\t// second parameter : the file system needs to be served\n\t// third parameter  : not required, the serve directory options.\n\t//\n\t// Alternatively, to get just the handler for that look the FileServer function instead.\n\t//\n\t//     api.HandleDir(\"/static\", iris.Dir(\"./assets\"), iris.DirOptions{IndexName: \"/index.html\", Compress: true})\n\t//\n\t// Returns all the registered routes, including GET index and path patterm and HEAD.\n\t//\n\t// Usage:\n\t// HandleDir(\"/public\", \"./assets\", DirOptions{...}) or\n\t// HandleDir(\"/public\", iris.Dir(\"./assets\"), DirOptions{...})\n\t//\n\t// Examples:\n\t// https://github.com/kataras/iris/tree/main/_examples/file-server\n\tHandleDir(requestPath string, fileSystem any, opts ...DirOptions) []*Route\n\n\t// None registers an \"offline\" route\n\t// see context.ExecRoute(routeName) and\n\t// party.Routes().Online(handleResultregistry.*Route, \"GET\") and\n\t// Offline(handleResultregistry.*Route)\n\t//\n\t// Returns the read-only route information.\n\tNone(path string, handlers ...context.Handler) *Route\n\n\t// Get registers a route for the Get HTTP Method.\n\t//\n\t// Returns the read-only route information.\n\tGet(path string, handlers ...context.Handler) *Route\n\t// Post registers a route for the Post HTTP Method.\n\t//\n\t// Returns the read-only route information.\n\tPost(path string, handlers ...context.Handler) *Route\n\t// Put registers a route for the Put HTTP Method.\n\t//\n\t// Returns the read-only route information.\n\tPut(path string, handlers ...context.Handler) *Route\n\t// Delete registers a route for the Delete HTTP Method.\n\t//\n\t// Returns the read-only route information.\n\tDelete(path string, handlers ...context.Handler) *Route\n\t// Connect registers a route for the Connect HTTP Method.\n\t//\n\t// Returns the read-only route information.\n\tConnect(path string, handlers ...context.Handler) *Route\n\t// Head registers a route for the Head HTTP Method.\n\t//\n\t// Returns the read-only route information.\n\tHead(path string, handlers ...context.Handler) *Route\n\t// Options registers a route for the Options HTTP Method.\n\t//\n\t// Returns the read-only route information.\n\tOptions(path string, handlers ...context.Handler) *Route\n\t// Patch registers a route for the Patch HTTP Method.\n\t//\n\t// Returns the read-only route information.\n\tPatch(path string, handlers ...context.Handler) *Route\n\t// Trace registers a route for the Trace HTTP Method.\n\t//\n\t// Returns the read-only route information.\n\tTrace(path string, handlers ...context.Handler) *Route\n\t// Any registers a route for ALL of the HTTP methods:\n\t// Get\n\t// Post\n\t// Put\n\t// Delete\n\t// Head\n\t// Patch\n\t// Options\n\t// Connect\n\t// Trace\n\tAny(registeredPath string, handlers ...context.Handler) []*Route\n\t// HandleServer registers a route for all HTTP methods which forwards the requests to the given server.\n\t//\n\t// Usage:\n\t//\n\t//\tapp.HandleServer(\"/api/identity/{first:string}/orgs/{second:string}/{p:path}\", otherApp)\n\t//\n\t// OR\n\t//\n\t//\tapp.HandleServer(\"/api/identity\", otherApp)\n\tHandleServer(path string, server ServerHandler)\n\n\t// CreateRoutes returns a list of Party-based Routes.\n\t// It does NOT registers the route. Use `Handle, Get...` methods instead.\n\t// This method can be used for third-parties Iris helpers packages and tools\n\t// that want a more detailed view of Party-based Routes before take the decision to register them.\n\tCreateRoutes(methods []string, relativePath string, handlers ...context.Handler) []*Route\n\t// RemoveRoute deletes a registered route by its name before `Application.Listen`.\n\t// The default naming for newly created routes is: method + subdomain + path.\n\t// Reports whether a route with that name was found and removed successfully.\n\t//\n\t// Note that this method applies to all Parties (sub routers)\n\t// even if each of the Parties have access to this method,\n\t// as the route name is unique per Iris Application.\n\tRemoveRoute(routeName string) bool\n\n\t// StaticContent registers a GET and HEAD method routes to the requestPath\n\t// that are ready to serve raw static bytes, memory cached.\n\t//\n\t// Returns the GET *Route.\n\tStaticContent(requestPath string, cType string, content []byte) *Route\n\t// Favicon serves static favicon\n\t// accepts 2 parameters, second is optional\n\t// favPath (string), declare the system directory path of the __.ico\n\t// requestPath (string), it's the route's path, by default this is the \"/favicon.ico\" because some browsers tries to get this by default first,\n\t// you can declare your own path if you have more than one favicon (desktop, mobile and so on)\n\t//\n\t// this func will add a route for you which will static serve the /yuorpath/yourfile.ico to the /yourfile.ico\n\t// (nothing special that you can't handle by yourself).\n\t// Note that you have to call it on every favicon you have to serve automatically (desktop, mobile and so on).\n\t//\n\t// Returns the GET *Route.\n\tFavicon(favPath string, requestPath ...string) *Route\n\n\t// RegisterView registers and loads a view engine middleware for this group of routes.\n\t// It overrides any of the application's root registered view engine.\n\t// To register a view engine per handler chain see the `Context.ViewEngine` instead.\n\t// Read `Configuration.ViewEngineContextKey` documentation for more.\n\tRegisterView(viewEngine context.ViewEngine)\n\t// FallbackView registers one or more fallback views for a template or a template layout.\n\t// Usage:\n\t//  FallbackView(iris.FallbackView(\"fallback.html\"))\n\t//  FallbackView(iris.FallbackViewLayout(\"layouts/fallback.html\"))\n\t//  OR\n\t//  FallbackView(iris.FallbackViewFunc(ctx iris.Context, err iris.ErrViewNotExist) error {\n\t//    err.Name is the previous template name.\n\t//    err.IsLayout reports whether the failure came from the layout template.\n\t//    err.Data is the template data provided to the previous View call.\n\t//    [...custom logic e.g. ctx.View(\"fallback\", err.Data)]\n\t//  })\n\tFallbackView(provider context.FallbackViewProvider)\n\t// Layout overrides the parent template layout with a more specific layout for this Party.\n\t// It returns the current Party.\n\t//\n\t// The \"tmplLayoutFile\" should be a relative path to the templates dir.\n\t// Usage:\n\t//\n\t// app := iris.New()\n\t// app.RegisterView(iris.$VIEW_ENGINE(\"./views\", \".$extension\"))\n\t// my := app.Party(\"/my\").Layout(\"layouts/mylayout.html\")\n\t// \tmy.Get(\"/\", func(ctx iris.Context) {\n\t// \tif err := ctx.View(\"page1.html\"); err != nil {\n\t//\t  ctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t//\t  return\n\t//  }\n\t// \t})\n\t//\n\t// Examples: https://github.com/kataras/iris/tree/main/_examples/view\n\tLayout(tmplLayoutFile string) Party\n}\n"
  },
  {
    "path": "core/router/path.go",
    "content": "package router\n\nimport (\n\t\"net/http\"\n\t\"path\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/kataras/iris/v12/core/netutil\"\n\t\"github.com/kataras/iris/v12/macro\"\n\t\"github.com/kataras/iris/v12/macro/interpreter/ast\"\n\t\"github.com/kataras/iris/v12/macro/interpreter/lexer\"\n)\n\n// Param receives a parameter name prefixed with the ParamStart symbol.\nfunc Param(name string) string {\n\treturn prefix(name, ParamStart)\n}\n\n// WildcardParam receives a parameter name prefixed with the WildcardParamStart symbol.\nfunc WildcardParam(name string) string {\n\tif name == \"\" {\n\t\treturn \"\"\n\t}\n\treturn prefix(name, WildcardParamStart)\n}\n\n// WildcardFileParam wraps a named parameter \"file\" with the trailing \"path\" macro parameter type.\n// At build state this \"file\" parameter is prefixed with the request handler's `WildcardParamStart`.\n// Created mostly for routes that serve static files to be visibly collected by\n// the `Application#GetRouteReadOnly` via the `Route.Tmpl().Src` instead of\n// the underline request handler's representation (`Route.Path()`).\nfunc WildcardFileParam() string {\n\treturn \"{file:path}\"\n}\n\nfunc convertMacroTmplToNodePath(tmpl macro.Template) string {\n\troutePath := tmpl.Src\n\tif len(routePath) > 1 && routePath[len(routePath)-1] == '/' {\n\t\troutePath = routePath[0 : len(routePath)-1] // remove any last \"/\"\n\t}\n\n\t// if it has started with {} and it's valid\n\t// then the tmpl.Params will be filled,\n\t// so no any further check needed.\n\tfor i := range tmpl.Params {\n\t\tp := tmpl.Params[i]\n\t\tif ast.IsTrailing(p.Type) {\n\t\t\troutePath = strings.Replace(routePath, p.Src, WildcardParam(p.Name), 1)\n\t\t} else {\n\t\t\troutePath = strings.Replace(routePath, p.Src, Param(p.Name), 1)\n\t\t}\n\t}\n\n\treturn routePath\n}\n\nfunc prefix(s string, prefix string) string {\n\tif !strings.HasPrefix(s, prefix) {\n\t\treturn prefix + s\n\t}\n\n\treturn s\n}\n\nfunc splitMethod(methodMany string) []string {\n\tmethodMany = strings.Trim(methodMany, \" \")\n\treturn strings.Split(methodMany, \" \")\n}\n\nfunc splitPath(pathMany string) (paths []string) {\n\tpathMany = strings.Trim(pathMany, \" \")\n\tpathsWithoutSlashFromFirstAndSoOn := strings.Split(pathMany, \" /\")\n\tfor _, path := range pathsWithoutSlashFromFirstAndSoOn {\n\t\tif path == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tif path[0] != '/' {\n\t\t\tpath = \"/\" + path\n\t\t}\n\t\tpaths = append(paths, path)\n\t}\n\treturn\n}\n\nfunc joinPath(path1 string, path2 string) string {\n\treturn path.Join(path1, path2)\n}\n\n// cleanPath applies the following rules\n// iteratively until no further processing can be done:\n//\n//  1. Replace multiple slashes with a single slash.\n//  2. Replace '\\' with '/'\n//  3. Replace \"\\\\\" with '/'\n//  4. Ignore anything inside '{' and '}'\n//  5. Makes sure that prefixed with '/'\n//  6. Remove trailing '/'.\n//\n// The returned path ends in a slash only if it is the root \"/\".\n// The function does not modify the dynamic path parts.\nfunc cleanPath(path string) string {\n\t// note that we don't care about the performance here, it's before the server ran.\n\tif path == \"\" || path == \".\" {\n\t\treturn \"/\"\n\t}\n\n\t// remove suffix \"/\", if it's root \"/\" then it will add it as a prefix below.\n\tif lidx := len(path) - 1; path[lidx] == '/' {\n\t\tpath = path[:lidx]\n\t}\n\n\t// prefix with \"/\".\n\tpath = prefix(path, \"/\")\n\n\ts := []rune(path)\n\n\t// If you're learning go through Iris I will ask you to ignore the\n\t// following part, it's not the recommending way to do that,\n\t// but it's understable to me.\n\tvar (\n\t\tinsideMacro = false\n\t\ti           = -1\n\t)\n\n\tfor {\n\t\ti++\n\t\tif len(s) <= i {\n\t\t\tbreak\n\t\t}\n\n\t\tif s[i] == lexer.Begin {\n\t\t\tinsideMacro = true\n\t\t\tcontinue\n\t\t}\n\n\t\tif s[i] == lexer.End {\n\t\t\tinsideMacro = false\n\t\t\tcontinue\n\t\t}\n\n\t\t// when inside {} then don't try to clean it.\n\t\tif !insideMacro {\n\t\t\tif s[i] == '\\\\' {\n\t\t\t\ts[i] = '/'\n\n\t\t\t\tif len(s)-1 > i+1 && s[i+1] == '\\\\' {\n\t\t\t\t\ts = deleteCharacter(s, i+1)\n\t\t\t\t} else {\n\t\t\t\t\ti-- // set to minus in order for the next check to be applied for prev tokens too.\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif s[i] == '/' && len(s)-1 > i+1 && s[i+1] == '/' {\n\t\t\t\ts[i] = '/'\n\t\t\t\ts = deleteCharacter(s, i+1)\n\t\t\t\ti--\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t}\n\n\tif len(s) > 1 && s[len(s)-1] == '/' { // remove any last //.\n\t\ts = s[:len(s)-1]\n\t}\n\n\treturn string(s)\n}\n\nfunc deleteCharacter(s []rune, index int) []rune {\n\treturn append(s[0:index], s[index+1:]...)\n}\n\nconst (\n\t// SubdomainWildcardIndicator where a registered path starts with '*.'.\n\t// if subdomain == \"*.\" then its wildcard.\n\t//\n\t// used internally by router and api builder.\n\tSubdomainWildcardIndicator = \"*.\"\n\n\t// SubdomainWildcardPrefix where a registered path starts with \"*./\",\n\t// then this route should accept any subdomain.\n\tSubdomainWildcardPrefix = SubdomainWildcardIndicator + \"/\"\n\t// SubdomainPrefix where './' exists in a registered path then it contains subdomain\n\t//\n\t// used on api builder.\n\tSubdomainPrefix = \"./\" // i.e subdomain./ -> Subdomain: subdomain. Path: /\n)\n\nfunc hasSubdomain(s string) bool {\n\tif s == \"\" {\n\t\treturn false\n\t}\n\n\t// subdomain./path\n\t// .*/path\n\t//\n\t// remember: path always starts with \"/\"\n\t// if not start with \"/\" then it should be something else,\n\t// we don't assume anything else but subdomain.\n\tslashIdx := strings.IndexByte(s, '/')\n\treturn slashIdx > 0 || // for route paths\n\t\ts[0] == SubdomainPrefix[0] || // for route paths\n\t\t(len(s) >= 2 && s[0:2] == SubdomainWildcardIndicator) || // for party rel path or route paths\n\t\t(len(s) >= 2 && slashIdx != 0 && s[len(s)-1] == '.') // for party rel, i.e www., or subsub.www.\n}\n\n// splitSubdomainAndPath checks if the path has subdomain and if it's\n// it splits the subdomain and path and returns them, otherwise it returns\n// an empty subdomain and the clean path.\n//\n// First return value is the subdomain, second is the path.\nfunc splitSubdomainAndPath(fullUnparsedPath string) (subdomain string, path string) {\n\ts := fullUnparsedPath\n\tif s == \"\" || s == \"/\" {\n\t\treturn \"\", \"/\"\n\t}\n\n\tsplitPath := strings.Split(s, \".\")\n\tif len(splitPath) == 2 && splitPath[1] == \"\" {\n\t\treturn splitPath[0] + \".\", \"/\"\n\t}\n\n\tslashIdx := strings.IndexByte(s, '/')\n\tif slashIdx > 0 {\n\t\t// has subdomain\n\t\tsubdomain = s[0:slashIdx]\n\t}\n\n\tif slashIdx == -1 {\n\t\t// this will only happen when this function\n\t\t// is called to Party's relative path (e.g. control.admin.),\n\t\t// and not a route's one (the route's one always contains a slash).\n\t\t// return all as subdomain and \"/\" as path.\n\t\treturn s, \"/\"\n\t}\n\n\tpath = s[slashIdx:]\n\tif !strings.Contains(path, \"{\") {\n\t\tpath = strings.ReplaceAll(path, \"//\", \"/\")\n\t\tpath = strings.ReplaceAll(path, \"\\\\\", \"/\")\n\t}\n\n\t// remove any left trailing slashes, i.e \"//api/users\".\n\tfor i := 1; i < len(path); i++ {\n\t\tif path[i] == '/' {\n\t\t\tpath = path[0:i] + path[i+1:]\n\t\t} else {\n\t\t\tbreak\n\t\t}\n\t}\n\n\t// remove last /.\n\tpath = strings.TrimRight(path, \"/\")\n\n\t// no cleanPath(path) in order\n\t// to be able to parse macro function regexp(\\\\).\n\treturn // return subdomain without slash, path with slash\n}\n\nfunc staticPath(src string) string {\n\tbidx := strings.IndexByte(src, '{')\n\tif bidx == -1 || len(src) <= bidx {\n\t\treturn src // no dynamic part found\n\t}\n\tif bidx <= 1 { // found at first{...} or second index (/{...}),\n\t\t// although first index should never happen because of the prepended slash.\n\t\treturn \"/\"\n\t}\n\n\treturn src[:bidx-1] // (/static/{...} -> /static)\n}\n\n// RoutePathReverserOption option signature for the RoutePathReverser.\ntype RoutePathReverserOption func(*RoutePathReverser)\n\n// WithScheme is an option for the RoutepathReverser,\n// it sets the optional field \"vscheme\",\n// v for virtual.\n// if vscheme is empty then it will try to resolve it from\n// the RoutePathReverser's vhost field.\n//\n// See WithHost or WithServer to enable the URL feature.\nfunc WithScheme(scheme string) RoutePathReverserOption {\n\treturn func(ps *RoutePathReverser) {\n\t\tps.vscheme = scheme\n\t}\n}\n\n// WithHost enables the RoutePathReverser's URL feature.\n// Both \"WithHost\" and \"WithScheme\" can be different from\n// the real server's listening address, i.e nginx in front.\nfunc WithHost(host string) RoutePathReverserOption {\n\treturn func(ps *RoutePathReverser) {\n\t\tps.vhost = host\n\t\tif ps.vscheme == \"\" {\n\t\t\tps.vscheme = netutil.ResolveSchemeFromVHost(host)\n\t\t}\n\t}\n}\n\n// WithServer enables the RoutePathReverser's URL feature.\n// It receives an *http.Server and tries to resolve\n// a scheme and a host to be used in the URL function.\nfunc WithServer(srv *http.Server) RoutePathReverserOption {\n\treturn func(ps *RoutePathReverser) {\n\t\tps.vhost = netutil.ResolveVHost(srv.Addr)\n\t\tps.vscheme = netutil.ResolveSchemeFromServer(srv)\n\t}\n}\n\n// RoutePathReverser contains methods that helps to reverse a\n// (dynamic) path from a specific route,\n// route name is required because a route may being registered\n// on more than one http method.\ntype RoutePathReverser struct {\n\tprovider RoutesProvider\n\t// both vhost and vscheme are being used, optionally, for the URL feature.\n\tvhost   string\n\tvscheme string\n}\n\n// NewRoutePathReverser returns a new path reverser based on\n// a routes provider, needed to get a route based on its name.\n// Options is required for the URL function.\n// See WithScheme and WithHost or WithServer.\nfunc NewRoutePathReverser(apiRoutesProvider RoutesProvider, options ...RoutePathReverserOption) *RoutePathReverser {\n\tps := &RoutePathReverser{\n\t\tprovider: apiRoutesProvider,\n\t}\n\tfor _, o := range options {\n\t\to(ps)\n\t}\n\treturn ps\n}\n\n// Path  returns a route path based on a route name and any dynamic named parameter's values-only.\nfunc (ps *RoutePathReverser) Path(routeName string, paramValues ...any) string {\n\tr := ps.provider.GetRoute(routeName)\n\tif r == nil {\n\t\treturn \"\"\n\t}\n\n\tif len(paramValues) == 0 {\n\t\treturn r.Path\n\t}\n\n\treturn r.ResolvePath(toStringSlice(paramValues)...)\n}\n\nfunc toStringSlice(args []any) (argsString []string) {\n\targsSize := len(args)\n\tif argsSize <= 0 {\n\t\treturn\n\t}\n\n\targsString = make([]string, argsSize)\n\tfor i, v := range args {\n\t\tif s, ok := v.(string); ok {\n\t\t\targsString[i] = s\n\t\t} else if num, ok := v.(int); ok {\n\t\t\targsString[i] = strconv.Itoa(num)\n\t\t} else if b, ok := v.(bool); ok {\n\t\t\targsString[i] = strconv.FormatBool(b)\n\t\t} else if arr, ok := v.([]string); ok {\n\t\t\tif len(arr) > 0 {\n\t\t\t\targsString[i] = arr[0]\n\t\t\t\targsString = append(argsString, arr[1:]...)\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\n// Remove the URL for now, it complicates things for the whole framework without a specific benefits,\n// developers can just concat the subdomain, (host can be auto-retrieve by browser using the Path).\n\n// URL same as Path but returns the full uri, i.e https://mysubdomain.mydomain.com/hello/iris\nfunc (ps *RoutePathReverser) URL(routeName string, paramValues ...any) (url string) {\n\tif ps.vhost == \"\" || ps.vscheme == \"\" {\n\t\treturn \"not supported\"\n\t}\n\n\tr := ps.provider.GetRoute(routeName)\n\tif r == nil {\n\t\treturn\n\t}\n\n\thost := ps.vhost\n\tscheme := ps.vscheme\n\targs := toStringSlice(paramValues)\n\n\t// if it's dynamic subdomain then the first argument is the subdomain part\n\t// for this part we are responsible not the custom routers\n\tif len(args) > 0 && r.Subdomain == SubdomainWildcardIndicator {\n\t\tsubdomain := args[0]\n\t\thost = subdomain + \".\" + host\n\t\targs = args[1:] // remove the subdomain part for the arguments,\n\t}\n\n\tif parsedPath := r.ResolvePath(args...); parsedPath != \"\" {\n\t\turl = scheme + \"://\" + host + parsedPath\n\t}\n\n\treturn\n}\n"
  },
  {
    "path": "core/router/path_test.go",
    "content": "package router\n\nimport (\n\t\"testing\"\n)\n\nfunc TestCleanPath(t *testing.T) {\n\ttests := []struct {\n\t\tpath     string\n\t\texpected string\n\t}{\n\t\t{\n\t\t\t\"/\",\n\t\t\t\"/\",\n\t\t},\n\t\t{\n\t\t\t\"noslashPrefix\",\n\t\t\t\"/noslashPrefix\",\n\t\t},\n\t\t{\n\t\t\t\"slashSuffix/\",\n\t\t\t\"/slashSuffix\",\n\t\t},\n\t\t{\n\t\t\t\"noSlashPrefixAndslashSuffix/\",\n\t\t\t\"/noSlashPrefixAndslashSuffix\",\n\t\t},\n\t\t// don't do any clean up inside {},\n\t\t// fixes #927.\n\t\t{\n\t\t\t\"/total/{year:string regexp(\\\\d{4})}\",\n\t\t\t\"/total/{year:string regexp(\\\\d{4})}\",\n\t\t},\n\t\t{\n\t\t\t\"/total/{year:string regexp(\\\\d{4})}/more\",\n\t\t\t\"/total/{year:string regexp(\\\\d{4})}/more\",\n\t\t},\n\t\t{\n\t\t\t\"/total/{year:string regexp(\\\\d{4})}/more/{s:string regexp(\\\\d{7})}\",\n\t\t\t\"/total/{year:string regexp(\\\\d{4})}/more/{s:string regexp(\\\\d{7})}\",\n\t\t},\n\t\t{\n\t\t\t\"/single_no_params\",\n\t\t\t\"/single_no_params\",\n\t\t},\n\t\t{\n\t\t\t\"/single/{id:uint64}\",\n\t\t\t\"/single/{id:uint64}\",\n\t\t},\n\t\t{\n\t\t\t\"0\\\\\\\\\\\\0\",\n\t\t\t\"/0/0\",\n\t\t},\n\t\t{\n\t\t\t\"*\\\\*\\\\*\",\n\t\t\t\"/*/*/*\",\n\t\t},\n\t\t{\n\t\t\t\"\\\\\",\n\t\t\t\"/\",\n\t\t},\n\t}\n\n\tfor i, tt := range tests {\n\t\tif expected, got := tt.expected, cleanPath(tt.path); expected != got {\n\t\t\tt.Fatalf(\"[%d] - expected path '%s' but got '%s'\", i, expected, got)\n\t\t}\n\t}\n}\n\nfunc TestSplitPath(t *testing.T) {\n\ttests := []struct {\n\t\tpath     string\n\t\texpected []string\n\t}{\n\t\t{\n\t\t\t\"/v2/stores/{id:string format(uuid)} /v3\",\n\t\t\t[]string{\"/v2/stores/{id:string format(uuid)}\", \"/v3\"},\n\t\t},\n\t\t{\n\t\t\t\"/user/{id:uint64} /admin/{id:uint64}\",\n\t\t\t[]string{\"/user/{id:uint64}\", \"/admin/{id:uint64}\"},\n\t\t},\n\t\t{\n\t\t\t\"/users/{id:int} /admins/{id:int64}\",\n\t\t\t[]string{\"/users/{id:int}\", \"/admins/{id:int64}\"},\n\t\t},\n\t\t{\n\t\t\t\"/user /admin\",\n\t\t\t[]string{\"/user\", \"/admin\"},\n\t\t},\n\t\t{\n\t\t\t\"/single_no_params\",\n\t\t\t[]string{\"/single_no_params\"},\n\t\t},\n\t\t{\n\t\t\t\"/single/{id:int}\",\n\t\t\t[]string{\"/single/{id:int}\"},\n\t\t},\n\t}\n\n\tequalSlice := func(s1 []string, s2 []string) bool {\n\t\tif len(s1) != len(s2) {\n\t\t\treturn false\n\t\t}\n\n\t\tfor i := range s1 {\n\t\t\tif s2[i] != s1[i] {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\n\t\treturn true\n\t}\n\n\tfor i, tt := range tests {\n\t\tpaths := splitPath(tt.path)\n\t\tif expected, got := tt.expected, paths; !equalSlice(expected, got) {\n\t\t\tt.Fatalf(\"[%d] - expected paths '%#v' but got '%#v'\", i, expected, got)\n\t\t}\n\t}\n}\n\nfunc TestSplitSubdomainAndPath(t *testing.T) {\n\ttests := []struct {\n\t\toriginal  string\n\t\tsubdomain string\n\t\tpath      string\n\t}{\n\t\t{\"admin./users/42\", \"admin.\", \"/users/42\"},\n\t\t{\"static.\", \"static.\", \"/\"},\n\t\t{\"static./\" + WildcardFileParam(), \"static.\", \"/\" + WildcardFileParam()},\n\t\t{\"//api/users\\\\42\", \"\", \"/api/users/42\"},\n\t\t{\"admin./users//42\", \"admin.\", \"/users/42\"},\n\t\t{\"*./users/42/\", \"*.\", \"/users/42\"},\n\t}\n\n\tfor i, tt := range tests {\n\t\tsubdomain, path := splitSubdomainAndPath(tt.original)\n\n\t\tif expected, got := tt.subdomain, subdomain; expected != got {\n\t\t\tt.Fatalf(\"[%d] - expected subdomain '%s' but got '%s'\", i, expected, got)\n\t\t}\n\t\tif expected, got := tt.path, path; expected != got {\n\t\t\tt.Fatalf(\"[%d] - expected path '%s' but got '%s'\", i, expected, got)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "core/router/route.go",
    "content": "package router\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/macro\"\n\t\"github.com/kataras/iris/v12/macro/handler\"\n\n\t\"github.com/kataras/pio\"\n)\n\n// Route contains the information about a registered Route.\n// If any of the following fields are changed then the\n// caller should Refresh the router.\ntype Route struct {\n\t// The Party which this Route was created and registered on.\n\tParty       Party\n\tTitle       string         `json:\"title\"`       // custom name to replace the method on debug logging.\n\tName        string         `json:\"name\"`        // \"userRoute\"\n\tDescription string         `json:\"description\"` // \"lists a user\"\n\tMethod      string         `json:\"method\"`      // \"GET\"\n\tStatusCode  int            `json:\"statusCode\"`  // 404 (only for HTTP error handlers).\n\tmethodBckp  string         // if Method changed to something else (which is possible at runtime as well, via RefreshRouter) then this field will be filled with the old one.\n\tSubdomain   string         `json:\"subdomain\"` // \"admin.\"\n\ttmpl        macro.Template // Tmpl().Src: \"/api/user/{id:uint64}\"\n\t// temp storage, they're appended to the Handlers on build.\n\t// Execution happens before Handlers, can be empty.\n\t// They run right after any builtinBeginHandlers.\n\tbeginHandlers context.Handlers\n\t// temp storage, these are always registered first as Handlers on Build.\n\t// There are the handlers may be added by the framework and\n\t// can NOT be modified by the end-developer (i.e overlapRoute & bindMultiParamTypesHandler),\n\t// even if a function like UseGlobal is used.\n\tbuiltinBeginHandlers context.Handlers\n\n\t// Handlers are the main route's handlers, executed by order.\n\t// Cannot be empty.\n\tHandlers         context.Handlers `json:\"-\"`\n\tMainHandlerName  string           `json:\"mainHandlerName\"`\n\tMainHandlerIndex int              `json:\"mainHandlerIndex\"`\n\t// temp storage, they're appended to the Handlers on build.\n\t// Execution happens after Begin and main Handler(s), can be empty.\n\tdoneHandlers context.Handlers\n\n\tPath string `json:\"path\"` // the underline router's representation, i.e \"/api/user/:id\"\n\t// FormattedPath all dynamic named parameters (if any) replaced with %v,\n\t// used by Application to validate param values of a Route based on its name.\n\tFormattedPath string `json:\"formattedPath\"`\n\n\t// the source code's filename:filenumber that this route was created from.\n\tSourceFileName   string `json:\"sourceFileName\"`\n\tSourceLineNumber int    `json:\"sourceLineNumber\"`\n\n\t// where the route registered.\n\tRegisterFileName   string `json:\"registerFileName\"`\n\tRegisterLineNumber int    `json:\"registerLineNumber\"`\n\n\t// see APIBuilder.handle, routerHandler.bindMultiParamTypesHandler and routerHandler.Build,\n\t// it's the parent route of the last registered of the same path parameter. Specifically for path parameters.\n\ttopLink *Route\n\t// overlappedLink specifically for overlapRoute feature.\n\t// keeps the second route of the same path pattern registered.\n\t// It's used ONLY for logging.\n\toverlappedLink *Route\n\n\t// Sitemap properties: https://www.sitemaps.org/protocol.html\n\tNoSitemap  bool      // when this route should be hidden from sitemap.\n\tLastMod    time.Time `json:\"lastMod,omitempty\"`\n\tChangeFreq string    `json:\"changeFreq,omitempty\"`\n\tPriority   float32   `json:\"priority,omitempty\"`\n\n\t// ReadOnly is the read-only structure of the Route.\n\tReadOnly context.RouteReadOnly\n\n\t// OnBuild runs right before BuildHandlers.\n\tOnBuild func(r *Route)\n\tNoLog   bool // disables debug logging.\n}\n\n// NewRoute returns a new route based on its method,\n// subdomain, the path (unparsed or original),\n// handlers and the macro container which all routes should share.\n// It parses the path based on the \"macros\",\n// handlers are being changed to validate the macros at serve time, if needed.\nfunc NewRoute(p Party, statusErrorCode int, method, subdomain, unparsedPath string,\n\thandlers context.Handlers, macros macro.Macros) (*Route, error) {\n\tpath := cleanPath(unparsedPath) // required. Before macro template parse as the cleanPath does not modify the dynamic path route parts.\n\n\ttmpl, err := macro.Parse(path, macros)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tpath = convertMacroTmplToNodePath(tmpl)\n\t// prepend the macro handler to the route, now,\n\t// right before the register to the tree, so APIBuilder#UseGlobal will work as expected.\n\tif handler.CanMakeHandler(tmpl) {\n\t\tmacroEvaluatorHandler := handler.MakeHandler(tmpl)\n\t\thandlers = append(context.Handlers{macroEvaluatorHandler}, handlers...)\n\t}\n\n\tdefaultName := method + subdomain + tmpl.Src\n\tif statusErrorCode > 0 {\n\t\tdefaultName = fmt.Sprintf(\"%d_%s\", statusErrorCode, defaultName)\n\t}\n\n\tformattedPath := formatPath(path)\n\n\troute := &Route{\n\t\tParty:         p,\n\t\tStatusCode:    statusErrorCode,\n\t\tName:          defaultName,\n\t\tMethod:        method,\n\t\tmethodBckp:    method,\n\t\tSubdomain:     subdomain,\n\t\ttmpl:          tmpl,\n\t\tPath:          path,\n\t\tHandlers:      handlers,\n\t\tFormattedPath: formattedPath,\n\t}\n\n\troute.ReadOnly = routeReadOnlyWrapper{route}\n\treturn route, nil\n}\n\n// Use adds explicit begin handlers to this route.\n// Alternatively the end-dev can prepend to the `Handlers` field.\n// Should be used before the `BuildHandlers` which is\n// called by the framework itself on `Application#Run` (build state).\n//\n// Used internally at  `APIBuilder#UseGlobal` -> `beginGlobalHandlers` -> `APIBuilder#Handle`.\nfunc (r *Route) Use(handlers ...context.Handler) {\n\tif len(handlers) == 0 {\n\t\treturn\n\t}\n\tr.beginHandlers = append(r.beginHandlers, handlers...)\n}\n\n// UseOnce like Use but it replaces any duplicate handlers with\n// the new ones.\n// Should be called before Application Build.\nfunc (r *Route) UseOnce(handlers ...context.Handler) {\n\tr.beginHandlers = context.UpsertHandlers(r.beginHandlers, handlers)\n}\n\n// RemoveHandler deletes a handler from begin, main and done handlers\n// based on its name or the handler pc function.\n// Returns the total amount of handlers removed.\n//\n// Should be called before Application Build.\nfunc (r *Route) RemoveHandler(namesOrHandlers ...any) (count int) {\n\tfor _, nameOrHandler := range namesOrHandlers {\n\t\thandlerName := \"\"\n\t\tswitch h := nameOrHandler.(type) {\n\t\tcase string:\n\t\t\thandlerName = h\n\t\tcase context.Handler: //, func(*context.Context):\n\t\t\thandlerName = context.HandlerName(h)\n\t\tdefault:\n\t\t\tpanic(fmt.Sprintf(\"remove handler: unexpected type of %T\", h))\n\t\t}\n\n\t\tr.beginHandlers = removeHandler(handlerName, r.beginHandlers, &count)\n\t\tr.Handlers = removeHandler(handlerName, r.Handlers, &count)\n\t\tr.doneHandlers = removeHandler(handlerName, r.doneHandlers, &count)\n\t}\n\n\treturn\n}\n\nfunc removeHandler(handlerName string, handlers context.Handlers, counter *int) (newHandlers context.Handlers) {\n\tfor _, h := range handlers {\n\t\tif h == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tif context.HandlerName(h) == handlerName {\n\t\t\tif counter != nil {\n\t\t\t\t*counter++\n\t\t\t}\n\n\t\t\tcontinue\n\t\t}\n\n\t\tnewHandlers = append(newHandlers, h)\n\t}\n\n\treturn\n}\n\n// Done adds explicit finish handlers to this route.\n// Alternatively the end-dev can append to the `Handlers` field.\n// Should be used before the `BuildHandlers` which is\n// called by the framework itself on `Application#Run` (build state).\n//\n// Used internally at  `APIBuilder#DoneGlobal` -> `doneGlobalHandlers` -> `APIBuilder#Handle`.\nfunc (r *Route) Done(handlers ...context.Handler) {\n\tif len(handlers) == 0 {\n\t\treturn\n\t}\n\tr.doneHandlers = append(r.doneHandlers, handlers...)\n}\n\n// ChangeMethod will try to change the HTTP Method of this route instance.\n// A call of `RefreshRouter` is required after this type of change in order to change to be really applied.\nfunc (r *Route) ChangeMethod(newMethod string) bool {\n\tif newMethod != r.Method {\n\t\tr.methodBckp = r.Method\n\t\tr.Method = newMethod\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// SetStatusOffline will try make this route unavailable.\n// A call of `RefreshRouter` is required after this type of change in order to change to be really applied.\nfunc (r *Route) SetStatusOffline() bool {\n\treturn r.ChangeMethod(MethodNone)\n}\n\n// Describe sets the route's description\n// that will be logged alongside with the route information\n// in DEBUG log level.\n// Returns the `Route` itself.\nfunc (r *Route) Describe(description string) *Route {\n\tr.Description = description\n\treturn r\n}\n\n// SetSourceLine sets the route's source caller, useful for debugging.\n// Returns the `Route` itself.\nfunc (r *Route) SetSourceLine(fileName string, lineNumber int) *Route {\n\tr.SourceFileName = fileName\n\tr.SourceLineNumber = lineNumber\n\treturn r\n}\n\n// RestoreStatus will try to restore the status of this route instance, i.e if `SetStatusOffline` called on a \"GET\" route,\n// then this function will make this route available with \"GET\" HTTP Method.\n// Note if that you want to set status online for an offline registered route then you should call the `ChangeMethod` instead.\n// It will return true if the status restored, otherwise false.\n// A call of `RefreshRouter` is required after this type of change in order to change to be really applied.\nfunc (r *Route) RestoreStatus() bool {\n\treturn r.ChangeMethod(r.methodBckp)\n}\n\n// BuildHandlers is executed automatically by the router handler\n// at the `Application#Build` state. Do not call it manually, unless\n// you were defined your own request mux handler.\nfunc (r *Route) BuildHandlers() {\n\tif r.OnBuild != nil {\n\t\tr.OnBuild(r)\n\t}\n\n\t// prepend begin handlers.\n\tr.Handlers = append(r.builtinBeginHandlers, append(r.beginHandlers, r.Handlers...)...)\n\t// append done handlers.\n\tr.Handlers = append(r.Handlers, r.doneHandlers...)\n\t// reset the temp storage, so a second call of\n\t// BuildHandlers will not re-add them (i.e RefreshRouter).\n\tr.builtinBeginHandlers = r.builtinBeginHandlers[0:0]\n\tr.beginHandlers = r.beginHandlers[0:0]\n\tr.doneHandlers = r.doneHandlers[0:0]\n}\n\n// String returns the form of METHOD, SUBDOMAIN, TMPL PATH.\nfunc (r *Route) String() string {\n\tstart := r.GetTitle()\n\t// if r.StatusCode > 0 {\n\t// \tstart = fmt.Sprintf(\"%d (%s)\", r.StatusCode, http.StatusText(r.StatusCode))\n\t// }\n\n\treturn fmt.Sprintf(\"%s %s%s\",\n\t\tstart, r.Subdomain, r.Tmpl().Src)\n}\n\n// Equal compares the method, subdomain and the\n// underline representation of the route's path,\n// instead of the `String` function which returns the front representation.\nfunc (r *Route) Equal(other *Route) bool {\n\treturn r.StatusCode == other.StatusCode && r.Method == other.Method && r.Subdomain == other.Subdomain && r.Path == other.Path\n}\n\n// DeepEqual compares the method, subdomain, the\n// underline representation of the route's path,\n// and the template source.\nfunc (r *Route) DeepEqual(other *Route) bool {\n\treturn r.Equal(other) && r.tmpl.Src == other.tmpl.Src\n}\n\n// SetName overrides the default route name which defaults to\n// method + subdomain + path and\n// statusErrorCode_method+subdomain+path for error routes.\n//\n// Note that the route name MUST BE unique per Iris Application.\nfunc (r *Route) SetName(newRouteName string) *Route {\n\tr.Name = newRouteName\n\treturn r\n}\n\n// ExcludeSitemap excludes this route page from sitemap generator.\n// It sets the NoSitemap field to true.\n//\n// See `SetLastMod`, `SetChangeFreq`, `SetPriority` methods\n// and `iris.WithSitemap`.\nfunc (r *Route) ExcludeSitemap() *Route {\n\tr.NoSitemap = true\n\treturn r\n}\n\n// SetLastMod sets the date of last modification of the file served by this static GET route.\nfunc (r *Route) SetLastMod(t time.Time) *Route {\n\tr.LastMod = t\n\treturn r\n}\n\n// SetChangeFreq sets how frequently this static GET route's page is likely to change,\n// possible values:\n// - \"always\"\n// - \"hourly\"\n// - \"daily\"\n// - \"weekly\"\n// - \"monthly\"\n// - \"yearly\"\n// - \"never\"\nfunc (r *Route) SetChangeFreq(freq string) *Route {\n\tr.ChangeFreq = freq\n\treturn r\n}\n\n// SetPriority sets the priority of this static GET route's URL relative to other URLs on your site.\nfunc (r *Route) SetPriority(prio float32) *Route {\n\tr.Priority = prio\n\treturn r\n}\n\n// Tmpl returns the path template,\n// it contains the parsed template\n// for the route's path.\n// May contain zero named parameters.\n//\n// Developer can get his registered path\n// via Tmpl().Src, Route.Path is the path\n// converted to match the underline router's specs.\nfunc (r *Route) Tmpl() macro.Template {\n\treturn r.tmpl\n}\n\n// RegisteredHandlersLen returns the end-developer's registered handlers, all except the macro evaluator handler\n// if was required by the build process.\nfunc (r *Route) RegisteredHandlersLen() int {\n\tn := len(r.Handlers)\n\tif handler.CanMakeHandler(r.tmpl) {\n\t\tn--\n\t}\n\n\treturn n\n}\n\n// IsOnline returns true if the route is marked as \"online\" (state).\nfunc (r *Route) IsOnline() bool {\n\treturn r.Method != MethodNone\n}\n\n// formats the parsed to the underline path syntax.\n// path = \"/api/users/:id\"\n// return \"/api/users/%v\"\n//\n// path = \"/files/*file\"\n// return /files/%v\n//\n// path = \"/:username/messages/:messageid\"\n// return \"/%v/messages/%v\"\n// we don't care about performance here, it's prelisten.\nfunc formatPath(path string) string {\n\tif strings.Contains(path, ParamStart) || strings.Contains(path, WildcardParamStart) {\n\t\tvar (\n\t\t\tstartRune         = ParamStart[0]\n\t\t\twildcardStartRune = WildcardParamStart[0]\n\t\t)\n\n\t\tvar formattedParts []string\n\t\tparts := strings.Split(path, \"/\")\n\t\tfor _, part := range parts {\n\t\t\tif part == \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif part[0] == startRune || part[0] == wildcardStartRune {\n\t\t\t\t// is param or wildcard param\n\t\t\t\tpart = \"%v\"\n\t\t\t}\n\t\t\tformattedParts = append(formattedParts, part)\n\t\t}\n\n\t\treturn \"/\" + strings.Join(formattedParts, \"/\")\n\t}\n\t// the whole path is static just return it\n\treturn path\n}\n\n// IsStatic reports whether this route is a static route.\n// Does not contain dynamic path parameters,\n// is online and registered on GET HTTP Method.\nfunc (r *Route) IsStatic() bool {\n\treturn r.IsOnline() && len(r.Tmpl().Params) == 0 && r.Method == \"GET\"\n}\n\n// StaticPath returns the static part of the original, registered route path.\n// if /user/{id} it will return /user\n// if /user/{id}/friend/{friendid:uint64} it will return /user too\n// if /assets/{filepath:path} it will return /assets.\nfunc (r *Route) StaticPath() string {\n\tsrc := r.tmpl.Src\n\treturn staticPath(src)\n}\n\n// ResolvePath returns the formatted path's %v replaced with the args.\nfunc (r *Route) ResolvePath(args ...string) string {\n\trpath, formattedPath := r.Path, r.FormattedPath\n\tif rpath == formattedPath {\n\t\t// static, no need to pass args\n\t\treturn rpath\n\t}\n\t// check if we have /*, if yes then join all arguments to one as path and pass that as parameter\n\tif rpath[len(rpath)-1] == WildcardParamStart[0] {\n\t\tparameter := strings.Join(args, \"/\")\n\t\treturn fmt.Sprintf(formattedPath, parameter)\n\t}\n\t// else return the formattedPath with its args,\n\t// the order matters.\n\tfor _, s := range args {\n\t\tformattedPath = strings.Replace(formattedPath, \"%v\", s, 1)\n\t}\n\treturn formattedPath\n}\n\nfunc traceHandlerFile(title, name, line string, number int) string {\n\tfile := fmt.Sprintf(\"(%s:%d)\", filepath.ToSlash(line), number)\n\n\tif context.IgnoreHandlerName(name) {\n\t\treturn \"\"\n\t}\n\n\tspace := strings.Repeat(\" \", len(title)+1)\n\treturn fmt.Sprintf(\"\\n%s • %s %s\", space, name, file)\n}\n\nvar methodColors = map[string]int{\n\thttp.MethodGet:     pio.Green,\n\thttp.MethodPost:    pio.Magenta,\n\thttp.MethodPut:     pio.Blue,\n\thttp.MethodDelete:  pio.Red,\n\thttp.MethodConnect: pio.Green,\n\thttp.MethodHead:    23,\n\thttp.MethodPatch:   pio.Blue,\n\thttp.MethodOptions: pio.Gray,\n\thttp.MethodTrace:   pio.Yellow,\n\tMethodNone:         203, // orange-red.\n}\n\n// TraceTitleColorCode returns the color code depending on the method or the status.\nfunc TraceTitleColorCode(method string) int {\n\tif color, ok := methodColors[method]; ok {\n\t\treturn color\n\t}\n\n\treturn 131 // for error handlers, of \"ERROR [%STATUSCODE]\"\n}\n\n// GetTitle returns the custom Title or the method or the error code.\nfunc (r *Route) GetTitle() string {\n\ttitle := r.Title\n\tif title == \"\" {\n\t\tif r.StatusCode > 0 {\n\t\t\ttitle = fmt.Sprintf(\"%d\", r.StatusCode) // if error code then title is the status code, e.g. 400.\n\t\t} else {\n\t\t\ttitle = r.Method // else is its method, e.g. GET\n\t\t}\n\t}\n\n\treturn title\n}\n\n// Trace prints some debug info about the Route to the \"w\".\n// Should be called after `Build` state.\n//\n// It prints the @method: @path (@description) (@route_rel_location)\n//   - @handler_name (@handler_rel_location)\n//   - @second_handler ...\n//\n// If route and handler line:number locations are equal then the second is ignored.\nfunc (r *Route) Trace(w io.Writer, stoppedIndex int) {\n\ttitle := r.GetTitle()\n\n\t// Color the method.\n\tcolor := TraceTitleColorCode(title)\n\n\t// @method: @path\n\t// space := strings.Repeat(\" \", len(http.MethodConnect)-len(method))\n\t// s := fmt.Sprintf(\"%s: %s\", pio.Rich(title, color), path)\n\tpio.WriteRich(w, title, color)\n\n\tpath := r.tmpl.Src\n\tif path == \"\" {\n\t\tpath = \"/\"\n\t}\n\n\tfmt.Fprintf(w, \": %s\", path)\n\n\t// (@description)\n\tdescription := r.Description\n\tif description == \"\" {\n\t\tif title == MethodNone {\n\t\t\tdescription = \"offline\"\n\t\t}\n\n\t\tif subdomain := r.Subdomain; subdomain != \"\" {\n\t\t\tif subdomain == \"*.\" { // wildcard.\n\t\t\t\tsubdomain = \"subdomain\"\n\t\t\t}\n\n\t\t\tif description == \"offline\" {\n\t\t\t\tdescription += \", \"\n\t\t\t}\n\n\t\t\tdescription += subdomain\n\t\t}\n\t}\n\n\tif description != \"\" {\n\t\t// s += fmt.Sprintf(\" %s\", pio.Rich(description, pio.Cyan, pio.Underline))\n\t\tfmt.Fprint(w, \" \")\n\t\tpio.WriteRich(w, description, pio.Cyan, pio.Underline)\n\t}\n\n\t// (@route_rel_location)\n\t// s += fmt.Sprintf(\" (%s:%d)\", r.RegisterFileName, r.RegisterLineNumber)\n\tfmt.Fprintf(w, \" (%s:%d)\", r.RegisterFileName, r.RegisterLineNumber)\n\n\tfor i, h := range r.Handlers {\n\t\tvar (\n\t\t\tname string\n\t\t\tfile string\n\t\t\tline int\n\t\t)\n\n\t\tif i == r.MainHandlerIndex && r.MainHandlerName != \"\" {\n\t\t\t// Main handler info can be programmatically\n\t\t\t// changed to be more specific, respect these changes.\n\t\t\tname = r.MainHandlerName\n\t\t\tfile = r.SourceFileName\n\t\t\tline = r.SourceLineNumber\n\t\t} else {\n\t\t\tname = context.HandlerName(h)\n\t\t\tfile, line = context.HandlerFileLineRel(h)\n\t\t\t// If a middleware, e.g (macro) which changes the main handler index,\n\t\t\t// skip it.\n\n\t\t\t// TODO: think of it.\n\t\t\tif file == \"<autogenerated>\" {\n\t\t\t\t// At PartyConfigure, 2nd+ level of routes it will get <autogenerated> but in reallity will be the same as the caller.\n\t\t\t\tfile = r.RegisterFileName\n\t\t\t\tline = r.RegisterLineNumber\n\t\t\t}\n\n\t\t\tif file == r.SourceFileName && line == r.SourceLineNumber {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\t// If a handler is an anonymous function then it was already\n\t\t// printed in the first line, skip it.\n\t\tif file == r.RegisterFileName && line == r.RegisterLineNumber {\n\t\t\tcontinue\n\t\t}\n\n\t\t// * @handler_name (@handler_rel_location)\n\t\tfmt.Fprint(w, traceHandlerFile(title, name, file, line))\n\t\tif stoppedIndex != -1 && stoppedIndex <= len(r.Handlers) {\n\t\t\tif i <= stoppedIndex {\n\t\t\t\tpio.WriteRich(w, \" ✓\", pio.Green)\n\t\t\t\t// } else {\n\t\t\t\t// pio.WriteRich(w, \" ✕\", pio.Red, pio.Underline)\n\t\t\t}\n\t\t}\n\t}\n\n\tfmt.Fprintln(w)\n\n\tif r.overlappedLink != nil {\n\t\tbckpDesc := r.overlappedLink.Description\n\t\tr.overlappedLink.Description += \" (overlapped)\"\n\t\tr.overlappedLink.Trace(w, -1)\n\t\tr.overlappedLink.Description = bckpDesc\n\t}\n}\n\ntype routeReadOnlyWrapper struct {\n\t*Route\n}\n\nvar _ context.RouteReadOnly = routeReadOnlyWrapper{}\n\nfunc (rd routeReadOnlyWrapper) StatusErrorCode() int {\n\treturn rd.Route.StatusCode\n}\n\nfunc (rd routeReadOnlyWrapper) Method() string {\n\treturn rd.Route.Method\n}\n\nfunc (rd routeReadOnlyWrapper) Name() string {\n\treturn rd.Route.Name\n}\n\nfunc (rd routeReadOnlyWrapper) Subdomain() string {\n\treturn rd.Route.Subdomain\n}\n\nfunc (rd routeReadOnlyWrapper) Path() string {\n\treturn rd.Route.tmpl.Src\n}\n\nfunc (rd routeReadOnlyWrapper) Trace(w io.Writer, stoppedIndex int) {\n\trd.Route.Trace(w, stoppedIndex)\n}\n\nfunc (rd routeReadOnlyWrapper) Tmpl() macro.Template {\n\treturn rd.Route.Tmpl()\n}\n\nfunc (rd routeReadOnlyWrapper) MainHandlerName() string {\n\treturn rd.Route.MainHandlerName\n}\n\nfunc (rd routeReadOnlyWrapper) MainHandlerIndex() int {\n\treturn rd.Route.MainHandlerIndex\n}\n\nfunc (rd routeReadOnlyWrapper) Property(key string) (any, bool) {\n\tproperties := rd.Route.Party.Properties()\n\tif properties != nil {\n\t\tif property, ok := properties[key]; ok {\n\t\t\treturn property, true\n\t\t}\n\t}\n\n\treturn nil, false\n}\n\nfunc (rd routeReadOnlyWrapper) GetLastMod() time.Time {\n\treturn rd.Route.LastMod\n}\n\nfunc (rd routeReadOnlyWrapper) GetChangeFreq() string {\n\treturn rd.Route.ChangeFreq\n}\n\nfunc (rd routeReadOnlyWrapper) GetPriority() float32 {\n\treturn rd.Route.Priority\n}\n"
  },
  {
    "path": "core/router/route_register_rule_test.go",
    "content": "package router_test\n\nimport (\n\t\"bytes\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/core/router\"\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nfunc TestRegisterRule(t *testing.T) {\n\tapp := iris.New()\n\tapp.Configure(iris.WithDynamicHandler)\n\n\t// collect the error on RouteError rule.\n\tbuf := new(bytes.Buffer)\n\tapp.Logger().SetTimeFormat(\"\").DisableNewLine().SetOutput(buf)\n\n\tv1 := app.Party(\"/v1\")\n\tv1.SetRegisterRule(iris.RouteSkip)\n\n\tgetHandler := func(ctx iris.Context) {\n\t\tctx.Writef(\"[get] %s\", ctx.Method())\n\t}\n\n\tanyHandler := func(ctx iris.Context) {\n\t\tctx.Writef(\"[any] %s\", ctx.Method())\n\t}\n\n\tgetRoute := v1.Get(\"/\", getHandler)\n\tv1.Any(\"/\", anyHandler)\n\tif route := v1.Get(\"/\", getHandler); !reflect.DeepEqual(route, getRoute) {\n\t\tt.Fatalf(\"expected route to be equal with the original get route\")\n\t}\n\n\t// test RouteSkip.\n\te := httptest.New(t, app, httptest.LogLevel(\"error\"))\n\ttestRegisterRule(e, \"[get] GET\")\n\n\t// test RouteOverride (default behavior).\n\tv1.SetRegisterRule(iris.RouteOverride)\n\tv1.Any(\"/\", anyHandler)\n\tapp.RefreshRouter()\n\ttestRegisterRule(e, \"[any] GET\")\n\n\t// test RouteError.\n\tv1.SetRegisterRule(iris.RouteError)\n\tif route := v1.Get(\"/\", getHandler); route != nil {\n\t\tt.Fatalf(\"expected duplicated route, with RouteError rule, to be nil but got: %#+v\", route)\n\t}\n\tif expected, got := \"[ERRO] new route: GET /v1 conflicts with an already registered one: GET /v1 route\", buf.String(); expected != got {\n\t\tt.Fatalf(\"expected api builder's error to be:\\n'%s'\\nbut got:\\n'%s'\", expected, got)\n\t}\n}\n\nfunc testRegisterRule(e *httptest.Expect, expectedGetBody string) {\n\tfor _, method := range router.AllMethods {\n\t\ttt := e.Request(method, \"/v1\").Expect().Status(httptest.StatusOK).Body()\n\t\tif method == iris.MethodGet {\n\t\t\ttt.IsEqual(expectedGetBody)\n\t\t} else {\n\t\t\ttt.IsEqual(\"[any] \" + method)\n\t\t}\n\t}\n}\n\nfunc TestRegisterRuleOverlap(t *testing.T) {\n\tapp := iris.New()\n\t// TODO(@kataras) the overlapping does not work per-party yet,\n\t// it just checks compares from the total app's routes (which is the best possible action to do\n\t// because MVC applications can be separated into different parties too?).\n\tusersRouter := app.Party(\"/users\")\n\tusersRouter.SetRegisterRule(iris.RouteOverlap)\n\n\t// second handler will be executed, status will be reset-ed as well,\n\t// stop without data written.\n\tusersRouter.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.StopWithStatus(iris.StatusUnauthorized)\n\t})\n\tusersRouter.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.StatusCode(iris.StatusOK)\n\t\tctx.WriteString(\"data\")\n\t})\n\n\t// first handler will be executed, no stop called.\n\tusersRouter.Get(\"/p1\", func(ctx iris.Context) {\n\t\tctx.StatusCode(iris.StatusUnauthorized)\n\t})\n\tusersRouter.Get(\"/p1\", func(ctx iris.Context) {\n\t\tctx.WriteString(\"not written\")\n\t})\n\n\t// first handler will be executed, stop but with data sent on default writer\n\t// (body sent cannot be reset-ed here).\n\tusersRouter.Get(\"/p2\", func(ctx iris.Context) {\n\t\tctx.StopWithText(iris.StatusUnauthorized, \"no access\")\n\t})\n\tusersRouter.Get(\"/p2\", func(ctx iris.Context) {\n\t\tctx.WriteString(\"not written\")\n\t})\n\n\t// second will be executed, response can be reset-ed on recording.\n\tusersRouter.Get(\"/p3\", func(ctx iris.Context) {\n\t\tctx.Record()\n\t\tctx.StopWithText(iris.StatusUnauthorized, \"no access\")\n\t})\n\tusersRouter.Get(\"/p3\", func(ctx iris.Context) {\n\t\tctx.StatusCode(iris.StatusOK)\n\t\tctx.WriteString(\"p3 data\")\n\t})\n\n\te := httptest.New(t, app)\n\n\te.GET(\"/users\").Expect().Status(httptest.StatusOK).Body().IsEqual(\"data\")\n\te.GET(\"/users/p1\").Expect().Status(httptest.StatusUnauthorized).Body().IsEqual(\"Unauthorized\")\n\te.GET(\"/users/p2\").Expect().Status(httptest.StatusUnauthorized).Body().IsEqual(\"no access\")\n\te.GET(\"/users/p3\").Expect().Status(httptest.StatusOK).Body().IsEqual(\"p3 data\")\n}\n"
  },
  {
    "path": "core/router/route_test.go",
    "content": "// white-box testing\n\npackage router\n\nimport (\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/macro\"\n)\n\nfunc TestRouteStaticPath(t *testing.T) {\n\ttests := []struct {\n\t\ttmpl   string\n\t\tstatic string\n\t}{\n\t\t{\n\t\t\ttmpl:   \"/files/{file:path}\",\n\t\t\tstatic: \"/files\",\n\t\t},\n\t\t{\n\t\t\ttmpl:   \"/path\",\n\t\t\tstatic: \"/path\",\n\t\t},\n\t\t{\n\t\t\ttmpl:   \"/path/segment\",\n\t\t\tstatic: \"/path/segment\",\n\t\t},\n\t\t{\n\t\t\ttmpl:   \"/path/segment/{n:int}\",\n\t\t\tstatic: \"/path/segment\",\n\t\t},\n\t\t{\n\t\t\ttmpl:   \"/path/{n:uint64}/{n:int}\",\n\t\t\tstatic: \"/path\",\n\t\t},\n\t\t{\n\t\t\ttmpl:   \"/path/{n:uint64}/static\",\n\t\t\tstatic: \"/path\",\n\t\t},\n\t\t{\n\t\t\ttmpl:   \"/{name}\",\n\t\t\tstatic: \"/\",\n\t\t},\n\t\t{\n\t\t\ttmpl:   \"/\",\n\t\t\tstatic: \"/\",\n\t\t},\n\t}\n\n\tfor i, tt := range tests {\n\t\troute := Route{tmpl: macro.Template{Src: tt.tmpl}}\n\t\tif expected, got := tt.static, route.StaticPath(); expected != got {\n\t\t\tt.Fatalf(\"[%d:%s] expected static path to be: '%s' but got: '%s'\", i, tt.tmpl, expected, got)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "core/router/router.go",
    "content": "package router\n\nimport (\n\t\"errors\"\n\t\"net/http\"\n\t\"sort\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\n\t\"github.com/schollz/closestmatch\"\n)\n\n// Router is the \"director\".\n// Caller should provide a request handler (router implementation or root handler).\n// Router is responsible to build the received request handler and run it\n// to serve requests, based on the received context.Pool.\n//\n// User can refresh the router with `RefreshRouter` whenever a route's field is changed by him.\ntype Router struct {\n\tmu sync.Mutex // for Downgrade, WrapRouter & BuildRouter,\n\n\trequestHandler RequestHandler   // build-accessible, can be changed to define a custom router or proxy, used on RefreshRouter too.\n\tmainHandler    http.HandlerFunc // init-accessible\n\twrapperFunc    WrapperFunc\n\t// wrappers to be built on BuildRouter state,\n\t// first is executed first at this case.\n\t// Case:\n\t// - SubdomainRedirect on user call, registers a wrapper, on design state\n\t// - i18n,if loaded and Subdomain or PathRedirect is true, registers a wrapper too, on build state\n\t// the SubdomainRedirect should be the first(subdomainWrap(i18nWrap)) wrapper\n\t// to be executed instead of last(i18nWrap(subdomainWrap)).\n\twrapperFuncs []WrapperFunc\n\n\tcPool          *context.Pool // used on RefreshRouter\n\troutesProvider RoutesProvider\n\n\t// key = subdomain\n\t// value = closest of static routes, filled on `BuildRouter/RefreshRouter`.\n\tclosestPaths map[string]*closestmatch.ClosestMatch\n}\n\n// NewRouter returns a new empty Router.\nfunc NewRouter() *Router {\n\treturn &Router{}\n}\n\n// RefreshRouter re-builds the router. Should be called when a route's state\n// changed (i.e Method changed at serve-time).\n//\n// Note that in order to use RefreshRouter while in serve-time,\n// you have to set the `EnableDynamicHandler` Iris Application setting to true,\n// e.g. `app.Listen(\":8080\", iris.WithEnableDynamicHandler)`\nfunc (router *Router) RefreshRouter() error {\n\treturn router.BuildRouter(router.cPool, router.requestHandler, router.routesProvider, true)\n}\n\n// AddRouteUnsafe adds a route directly to the router's request handler.\n// Works before or after Build state.\n// Mainly used for internal cases like `iris.WithSitemap`.\n// Do NOT use it on serve-time.\nfunc (router *Router) AddRouteUnsafe(routes ...*Route) error {\n\tif h := router.requestHandler; h != nil {\n\t\tif v, ok := h.(RouteAdder); ok {\n\t\t\tfor _, r := range routes {\n\t\t\t\treturn v.AddRoute(r)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn ErrNotRouteAdder\n}\n\n// FindClosestPaths returns a list of \"n\" paths close to \"path\" under the given \"subdomain\".\n//\n// Order may change.\nfunc (router *Router) FindClosestPaths(subdomain, searchPath string, n int) []string {\n\tif router.closestPaths == nil {\n\t\treturn nil\n\t}\n\n\tcm, ok := router.closestPaths[subdomain]\n\tif !ok {\n\t\treturn nil\n\t}\n\n\tlist := cm.ClosestN(searchPath, n)\n\tif len(list) == 1 && list[0] == \"\" {\n\t\t// yes, it may return empty string as its first slice element when not found.\n\t\treturn nil\n\t}\n\n\treturn list\n}\n\nfunc (router *Router) buildMainHandler(cPool *context.Pool, requestHandler RequestHandler) {\n\trouter.mainHandler = func(w http.ResponseWriter, r *http.Request) {\n\t\tctx := cPool.Acquire(w, r)\n\t\trouter.requestHandler.HandleRequest(ctx)\n\t\tcPool.Release(ctx)\n\t}\n}\n\nfunc (router *Router) buildMainHandlerWithFilters(routerFilters map[Party]*Filter, cPool *context.Pool, requestHandler RequestHandler) {\n\tsortedFilters := make([]*Filter, 0, len(routerFilters))\n\t// key was just there to enforce uniqueness on API level.\n\tfor _, f := range routerFilters {\n\t\tsortedFilters = append(sortedFilters, f)\n\t\t// append it as one handlers so execution rules are being respected in that step too.\n\t\tf.Handlers = append(f.Handlers, func(ctx *context.Context) {\n\t\t\t// set the handler index back to 0 so the route's handlers can be executed as expected.\n\t\t\tctx.HandlerIndex(0)\n\t\t\t// execute the main request handler, this will fire the found route's handlers\n\t\t\t// or if error the error code's associated handler.\n\t\t\trouter.requestHandler.HandleRequest(ctx)\n\t\t})\n\t}\n\n\tsort.SliceStable(sortedFilters, func(i, j int) bool {\n\t\tleft, right := sortedFilters[i], sortedFilters[j]\n\t\tvar (\n\t\t\tleftSubLen  = len(left.Subdomain)\n\t\t\trightSubLen = len(right.Subdomain)\n\n\t\t\tleftSlashLen  = strings.Count(left.Path, \"/\")\n\t\t\trightSlashLen = strings.Count(right.Path, \"/\")\n\t\t)\n\n\t\tif leftSubLen == rightSubLen {\n\t\t\tif leftSlashLen > rightSlashLen {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\n\t\tif leftSubLen > rightSubLen {\n\t\t\treturn true\n\t\t}\n\n\t\tif leftSlashLen > rightSlashLen {\n\t\t\treturn true\n\t\t}\n\n\t\tif leftSlashLen == rightSlashLen {\n\t\t\treturn len(left.Path) > len(right.Path)\n\t\t}\n\n\t\treturn len(left.Path) > len(right.Path)\n\t})\n\n\trouter.mainHandler = func(w http.ResponseWriter, r *http.Request) {\n\t\tctx := cPool.Acquire(w, r)\n\n\t\tfilterExecuted := false\n\t\tfor _, f := range sortedFilters { // from subdomain, largest path to shortest.\n\t\t\t// fmt.Printf(\"Sorted filter execution: [%s] [%s]\\n\", f.Subdomain, f.Path)\n\t\t\tif f.Matcher.Match(ctx) {\n\t\t\t\t// fmt.Printf(\"Matched [%s] and execute [%d] handlers [%s]\\n\\n\", ctx.Path(), len(f.Handlers), context.HandlersNames(f.Handlers))\n\t\t\t\tfilterExecuted = true\n\t\t\t\t// execute the final handlers chain.\n\t\t\t\tctx.Do(f.Handlers)\n\t\t\t\tbreak // and break on first found.\n\t\t\t}\n\t\t}\n\n\t\tif !filterExecuted {\n\t\t\t// If not at least one match filter found and executed,\n\t\t\t// then just run the router.\n\t\t\trouter.requestHandler.HandleRequest(ctx)\n\t\t}\n\n\t\tcPool.Release(ctx)\n\t}\n}\n\n// BuildRouter builds the router based on\n// the context factory (explicit pool in this case),\n// the request handler which manages how the main handler will multiplexes the routes\n// provided by the third parameter, routerProvider (it's the api builder in this case) and\n// its wrapper.\n//\n// Use of RefreshRouter to re-build the router if needed.\nfunc (router *Router) BuildRouter(cPool *context.Pool, requestHandler RequestHandler, routesProvider RoutesProvider, force bool) error {\n\tif requestHandler == nil {\n\t\treturn errors.New(\"router: request handler is nil\")\n\t}\n\n\tif cPool == nil {\n\t\treturn errors.New(\"router: context pool is nil\")\n\t}\n\n\t// build the handler using the routesProvider\n\tif err := requestHandler.Build(routesProvider); err != nil {\n\t\treturn err\n\t}\n\n\trouter.mu.Lock()\n\tdefer router.mu.Unlock()\n\n\t// store these for RefreshRouter's needs.\n\tif force {\n\t\trouter.cPool = cPool\n\t\trouter.requestHandler = requestHandler\n\t\trouter.routesProvider = routesProvider\n\t} else {\n\t\tif router.cPool == nil {\n\t\t\trouter.cPool = cPool\n\t\t}\n\n\t\tif router.requestHandler == nil {\n\t\t\trouter.requestHandler = requestHandler\n\t\t}\n\n\t\tif router.routesProvider == nil && routesProvider != nil {\n\t\t\trouter.routesProvider = routesProvider\n\t\t}\n\t}\n\n\t// the important stuff.\n\tif routerFilters := routesProvider.GetRouterFilters(); len(routerFilters) > 0 {\n\t\trouter.buildMainHandlerWithFilters(routerFilters, cPool, requestHandler)\n\t} else {\n\t\trouter.buildMainHandler(cPool, requestHandler)\n\t}\n\n\tfor i := len(router.wrapperFuncs) - 1; i >= 0; i-- {\n\t\tw := router.wrapperFuncs[i]\n\t\tif w == nil {\n\t\t\tcontinue\n\t\t}\n\t\trouter.WrapRouter(w)\n\t}\n\n\tif router.wrapperFunc != nil { // if wrapper used then attach that as the router service\n\t\trouter.mainHandler = newWrapper(router.wrapperFunc, router.mainHandler).ServeHTTP\n\t}\n\n\t// build closest.\n\tsubdomainPaths := make(map[string][]string)\n\tfor _, r := range router.routesProvider.GetRoutes() {\n\t\tif !r.IsStatic() {\n\t\t\tcontinue\n\t\t}\n\n\t\tsubdomainPaths[r.Subdomain] = append(subdomainPaths[r.Subdomain], r.Path)\n\t}\n\n\trouter.closestPaths = make(map[string]*closestmatch.ClosestMatch)\n\tfor subdomain, paths := range subdomainPaths {\n\t\trouter.closestPaths[subdomain] = closestmatch.New(paths, []int{3, 4, 6})\n\t}\n\n\treturn nil\n}\n\n// Downgrade \"downgrades\", alters the router supervisor service(Router.mainHandler)\n// algorithm to a custom one,\n// be aware to change the global variables of 'ParamStart' and 'ParamWildcardStart'.\n// can be used to implement a custom proxy or\n// a custom router which should work with raw ResponseWriter, *Request\n// instead of the Context(which again, can be retrieved by the Framework's context pool).\n//\n// Note: Downgrade will by-pass the Wrapper, the caller is responsible for everything.\n// Downgrade is thread-safe.\nfunc (router *Router) Downgrade(newMainHandler http.HandlerFunc) {\n\trouter.mu.Lock()\n\trouter.mainHandler = newMainHandler\n\trouter.mu.Unlock()\n}\n\n// Downgraded returns true if this router is downgraded.\nfunc (router *Router) Downgraded() bool {\n\treturn router.mainHandler != nil && router.requestHandler == nil\n}\n\n// SetTimeoutHandler overrides the main handler with a timeout handler.\n//\n// TimeoutHandler supports the Pusher interface but does not support\n// the Hijacker or Flusher interfaces.\n//\n// All previous registered wrappers and middlewares are still executed as expected.\nfunc (router *Router) SetTimeoutHandler(timeout time.Duration, msg string) {\n\tif timeout <= 0 {\n\t\treturn\n\t}\n\n\tmainHandler := router.mainHandler\n\th := func(w http.ResponseWriter, r *http.Request) {\n\t\tmainHandler(w, r)\n\t}\n\n\trouter.mainHandler = http.TimeoutHandler(http.HandlerFunc(h), timeout, msg).ServeHTTP\n}\n\n// WrapRouter adds a wrapper on the top of the main router.\n// Usually it's useful for third-party middleware\n// when need to wrap the entire application with a middleware like CORS.\n//\n// Developers can add more than one wrappers,\n// those wrappers' execution comes from last to first.\n// That means that the second wrapper will wrap the first, and so on.\n//\n// Before build.\nfunc (router *Router) WrapRouter(wrapperFunc WrapperFunc) {\n\t// logger := context.DefaultLogger(\"router wrapper\")\n\t// file, line := context.HandlerFileLineRel(wrapperFunc)\n\t// if router.wrapperFunc != nil {\n\t// \twrappedFile, wrappedLine := context.HandlerFileLineRel(router.wrapperFunc)\n\t// \tlogger.Infof(\"%s:%d wraps %s:%d\", file, line, wrappedFile, wrappedLine)\n\t// } else {\n\t// \tlogger.Infof(\"%s:%d wraps the main router\", file, line)\n\t// }\n\trouter.wrapperFunc = makeWrapperFunc(router.wrapperFunc, wrapperFunc)\n}\n\n// AddRouterWrapper adds a router wrapper.\n// Unlike `WrapRouter` the first registered will be executed first\n// so a wrapper wraps its next not the previous one.\n// it defers the wrapping until the `BuildRouter`.\n// Redirection wrappers should be added using this method\n// e.g. SubdomainRedirect.\nfunc (router *Router) AddRouterWrapper(wrapperFunc WrapperFunc) {\n\trouter.wrapperFuncs = append(router.wrapperFuncs, wrapperFunc)\n}\n\n// PrependRouterWrapper like `AddRouterWrapper` but this wrapperFunc\n// will always be executed before the previous `AddRouterWrapper`.\n// Path form (no modification) wrappers should be added using this method\n// e.g. ForceLowercaseRouting.\nfunc (router *Router) PrependRouterWrapper(wrapperFunc WrapperFunc) {\n\trouter.wrapperFuncs = append([]WrapperFunc{wrapperFunc}, router.wrapperFuncs...)\n}\n\n// ServeHTTPC serves the raw context, useful if we have already a context, it by-pass the wrapper.\nfunc (router *Router) ServeHTTPC(ctx *context.Context) {\n\trouter.requestHandler.HandleRequest(ctx)\n}\n\nfunc (router *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\trouter.mainHandler(w, r)\n}\n\n// RouteExists reports whether a particular route exists\n// It will search from the current subdomain of context's host, if not inside the root domain.\nfunc (router *Router) RouteExists(ctx *context.Context, method, path string) bool {\n\treturn router.requestHandler.RouteExists(ctx, method, path)\n}\n"
  },
  {
    "path": "core/router/router_handlers_order_test.go",
    "content": "// black-box testing\n//\n// see _examples/routing/main_test.go for the most common router tests that you may want to see,\n// this is a test which makes sure that the APIBuilder's `UseGlobal`, `Use` and `Done` functions are\n// working as expected.\n\npackage router_test\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\n// test registering of below handlers\n// with a different order but the route's final\n// response should be the same at all cases.\nvar (\n\twriteHandler = func(s string) iris.Handler {\n\t\treturn func(ctx iris.Context) {\n\t\t\tctx.WriteString(s)\n\t\t\tctx.Next()\n\t\t}\n\t}\n\n\tmainResponse = \"main\"\n\tmainHandler  = writeHandler(mainResponse)\n\n\tfirstUseResponse = \"use1\"\n\tfirstUseHandler  = writeHandler(firstUseResponse)\n\n\tsecondUseResponse = \"use2\"\n\tsecondUseHandler  = writeHandler(secondUseResponse)\n\n\tfirstUseRouterResponse = \"userouter1\"\n\t// Use inline handler, no the `writeHandler`,\n\t// because it will be overridden by `secondUseRouterHandler` otherwise,\n\t// look `UseRouter:context.UpsertHandlers` for more.\n\tfirstUseRouterHandler = func(ctx iris.Context) {\n\t\tctx.WriteString(firstUseRouterResponse)\n\t\tctx.Next()\n\t}\n\n\tsecondUseRouterResponse = \"userouter2\"\n\tsecondUseRouterHandler  = writeHandler(secondUseRouterResponse)\n\n\tfirstUseGlobalResponse = \"useglobal1\"\n\tfirstUseGlobalHandler  = writeHandler(firstUseGlobalResponse)\n\n\tsecondUseGlobalResponse = \"useglobal2\"\n\tsecondUseGlobalHandler  = writeHandler(secondUseGlobalResponse)\n\n\tfirstDoneResponse = \"done1\"\n\tfirstDoneHandler  = writeHandler(firstDoneResponse)\n\n\tsecondDoneResponse = \"done2\"\n\tsecondDoneHandler  = func(ctx iris.Context) {\n\t\tctx.WriteString(secondDoneResponse)\n\t}\n\n\tfinalResponse = firstUseRouterResponse + secondUseRouterResponse + firstUseGlobalResponse + secondUseGlobalResponse +\n\t\tfirstUseResponse + secondUseResponse + mainResponse + firstDoneResponse + secondDoneResponse\n\n\ttestResponse = func(t *testing.T, app *iris.Application, path string) {\n\t\tt.Helper()\n\n\t\te := httptest.New(t, app)\n\t\te.GET(path).Expect().Status(httptest.StatusOK).Body().IsEqual(finalResponse)\n\t}\n)\n\nfunc TestMiddlewareByRouteDef(t *testing.T) {\n\tapp := iris.New()\n\tapp.UseRouter(firstUseRouterHandler)\n\tapp.UseRouter(secondUseRouterHandler)\n\n\tapp.Get(\"/mypath\", firstUseGlobalHandler, secondUseGlobalHandler, firstUseHandler, secondUseHandler,\n\t\tmainHandler, firstDoneHandler, secondDoneHandler)\n\n\ttestResponse(t, app, \"/mypath\")\n}\n\nfunc TestMiddlewareByUseAndDoneDef(t *testing.T) {\n\tapp := iris.New()\n\tapp.UseRouter(firstUseRouterHandler, secondUseRouterHandler)\n\tapp.Use(firstUseGlobalHandler, secondUseGlobalHandler, firstUseHandler, secondUseHandler)\n\tapp.Done(firstDoneHandler, secondDoneHandler)\n\n\tapp.Get(\"/mypath\", mainHandler)\n\n\ttestResponse(t, app, \"/mypath\")\n}\n\nfunc TestMiddlewareByUseUseGlobalAndDoneDef(t *testing.T) {\n\tapp := iris.New()\n\n\tapp.Use(firstUseHandler, secondUseHandler)\n\t// if failed then UseGlobal didnt' registered these handlers even before the\n\t// existing middleware.\n\tapp.UseGlobal(firstUseGlobalHandler, secondUseGlobalHandler)\n\tapp.Done(firstDoneHandler, secondDoneHandler)\n\n\tapp.UseRouter(firstUseRouterHandler, secondUseRouterHandler)\n\tapp.Get(\"/mypath\", mainHandler)\n\n\ttestResponse(t, app, \"/mypath\")\n}\n\nfunc TestMiddlewareByUseDoneAndUseGlobalDef(t *testing.T) {\n\tapp := iris.New()\n\tapp.UseRouter(firstUseRouterHandler, secondUseRouterHandler)\n\n\tapp.Use(firstUseHandler, secondUseHandler)\n\tapp.Done(firstDoneHandler, secondDoneHandler)\n\n\tapp.Get(\"/mypath\", mainHandler)\n\n\t// if failed then UseGlobal was unable to\n\t// prepend these handlers to the route was registered before\n\t// OR\n\t// when order failed because these should be executed in order, first the firstUseGlobalHandler,\n\t// because they are the same type (global begin handlers)\n\tapp.UseGlobal(firstUseGlobalHandler)\n\tapp.UseGlobal(secondUseGlobalHandler)\n\n\ttestResponse(t, app, \"/mypath\")\n}\n\nfunc TestMiddlewareByUseGlobalUseAndDoneGlobalDef(t *testing.T) {\n\tapp := iris.New()\n\tapp.UseRouter(firstUseRouterHandler)\n\tapp.UseRouter(secondUseRouterHandler)\n\n\tapp.UseGlobal(firstUseGlobalHandler)\n\tapp.UseGlobal(secondUseGlobalHandler)\n\tapp.Use(firstUseHandler, secondUseHandler)\n\n\tapp.Get(\"/mypath\", mainHandler)\n\n\tapp.DoneGlobal(firstDoneHandler, secondDoneHandler)\n\n\ttestResponse(t, app, \"/mypath\")\n}\n\nfunc TestMiddlewareByDoneUseAndUseGlobalDef(t *testing.T) {\n\tapp := iris.New()\n\tapp.UseRouter(firstUseRouterHandler, secondUseRouterHandler)\n\tapp.Done(firstDoneHandler, secondDoneHandler)\n\n\tapp.Use(firstUseHandler, secondUseHandler)\n\n\tapp.Get(\"/mypath\", mainHandler)\n\n\tapp.UseGlobal(firstUseGlobalHandler)\n\tapp.UseGlobal(secondUseGlobalHandler)\n\n\ttestResponse(t, app, \"/mypath\")\n}\n\nfunc TestUseRouterStopExecution(t *testing.T) {\n\tapp := iris.New()\n\tapp.UseRouter(func(ctx iris.Context) {\n\t\tctx.WriteString(\"stop\")\n\t\t// no ctx.Next, so the router has not even the chance to work.\n\t})\n\tapp.Get(\"/\", writeHandler(\"index\"))\n\n\te := httptest.New(t, app)\n\te.GET(\"/\").Expect().Status(iris.StatusOK).Body().IsEqual(\"stop\")\n\n\tapp = iris.New()\n\tapp.OnErrorCode(iris.StatusForbidden, func(ctx iris.Context) {\n\t\tctx.Writef(\"err: %v\", ctx.GetErr())\n\t})\n\tapp.UseRouter(func(ctx iris.Context) {\n\t\tctx.StopWithPlainError(iris.StatusForbidden, fmt.Errorf(\"custom error\"))\n\t\t// stopped but not data written yet, the error code handler\n\t\t// should be responsible of it (use StopWithError to write and close).\n\t})\n\tapp.Get(\"/\", writeHandler(\"index\"))\n\n\te = httptest.New(t, app)\n\te.GET(\"/\").Expect().Status(iris.StatusForbidden).Body().IsEqual(\"err: custom error\")\n}\n\nfunc TestUseRouterParentDisallow(t *testing.T) {\n\tconst expectedResponse = \"no_userouter_allowed\"\n\n\tapp := iris.New()\n\tapp.UseRouter(func(ctx iris.Context) {\n\t\tctx.WriteString(\"always\")\n\t\tctx.Next()\n\t})\n\tapp.Get(\"/index\", func(ctx iris.Context) {\n\t\tctx.WriteString(expectedResponse)\n\t})\n\n\tapp.SetPartyMatcher(func(ctx iris.Context, p iris.Party) bool {\n\t\t// modifies the PartyMatcher to not match any UseRouter,\n\t\t// tests should receive the handlers response alone.\n\t\treturn false\n\t})\n\n\tapp.PartyFunc(\"/\", func(p iris.Party) { // it's the same instance of app.\n\t\tp.UseRouter(func(ctx iris.Context) {\n\t\t\tctx.WriteString(\"_2\")\n\t\t\tctx.Next()\n\t\t})\n\t\tp.Get(\"/\", func(ctx iris.Context) {\n\t\t\tctx.WriteString(expectedResponse)\n\t\t})\n\t})\n\n\tapp.PartyFunc(\"/user\", func(p iris.Party) {\n\t\tp.UseRouter(func(ctx iris.Context) {\n\t\t\tctx.WriteString(\"_3\")\n\t\t\tctx.Next()\n\t\t})\n\n\t\tp.Get(\"/\", func(ctx iris.Context) {\n\t\t\tctx.WriteString(expectedResponse)\n\t\t})\n\t})\n\n\te := httptest.New(t, app)\n\te.GET(\"/index\").Expect().Status(iris.StatusOK).Body().IsEqual(expectedResponse)\n\te.GET(\"/\").Expect().Status(iris.StatusOK).Body().IsEqual(expectedResponse)\n\te.GET(\"/user\").Expect().Status(iris.StatusOK).Body().IsEqual(expectedResponse)\n}\n\nfunc TestUseRouterSubdomains(t *testing.T) {\n\tapp := iris.New()\n\tapp.UseRouter(func(ctx iris.Context) {\n\t\tif ctx.Subdomain() == \"old\" {\n\t\t\tctx.Next() // call the router, do not write.\n\t\t\treturn\n\t\t}\n\n\t\t// if we write here, it will always give 200 OK,\n\t\t// even on not registered routes, that's the point at the end,\n\t\t// full control here when we need it.\n\t\tctx.WriteString(\"always_\")\n\t\tctx.Next()\n\t})\n\n\tadminAPI := app.Subdomain(\"admin\")\n\tadminAPI.UseRouter(func(ctx iris.Context) {\n\t\tctx.WriteString(\"admin always_\")\n\t\tctx.Next()\n\t})\n\tadminAPI.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.WriteString(\"admin\")\n\t})\n\n\tadminControlAPI := adminAPI.Subdomain(\"control\")\n\tadminControlAPI.UseRouter(func(ctx iris.Context) {\n\t\tctx.WriteString(\"control admin always_\")\n\t\tctx.Next()\n\t})\n\tadminControlAPI.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.WriteString(\"control admin\")\n\t})\n\n\toldAPI := app.Subdomain(\"old\")\n\toldAPI.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.WriteString(\"chat\")\n\t})\n\n\te := httptest.New(t, app, httptest.URL(\"http://example.com\"))\n\te.GET(\"/notfound\").Expect().Status(iris.StatusOK).Body().IsEqual(\"always_\")\n\n\te.GET(\"/\").WithURL(\"http://admin.example.com\").Expect().Status(iris.StatusOK).Body().\n\t\tIsEqual(\"always_admin always_admin\")\n\n\te.GET(\"/\").WithURL(\"http://control.admin.example.com\").Expect().Status(iris.StatusOK).Body().\n\t\tIsEqual(\"always_admin always_control admin always_control admin\")\n\n\t// It has a route, and use router just proceeds to the router.\n\te.GET(\"/\").WithURL(\"http://old.example.com\").Expect().Status(iris.StatusOK).Body().\n\t\tIsEqual(\"chat\")\n\t// this is not a registered path, should fire 404, the UseRouter does not write\n\t// anything to the response writer, so the router has control over it.\n\te.GET(\"/notfound\").WithURL(\"http://old.example.com\").Expect().Status(iris.StatusNotFound).Body().\n\t\tIsEqual(\"Not Found\")\n}\n\nfunc TestUseWrapOrder(t *testing.T) {\n\tvar (\n\t\texpectedBody         = \"#1 .WrapRouter\\n#2 .UseRouter\\n#3 .UseGlobal\\n#4 .Use\\n#5 Main Handler\\n\"\n\t\texpectedNotFoundBody = \"#3 .UseGlobal\\n#1 .UseError\\n#2 Main Error Handler\\n\"\n\t\tmakeMiddleware       = func(body string) iris.Handler {\n\t\t\treturn func(ctx iris.Context) {\n\t\t\t\tctx.WriteString(body)\n\t\t\t\tctx.Next()\n\t\t\t}\n\t\t}\n\n\t\thandler = func(ctx iris.Context) {\n\t\t\tctx.WriteString(\"#5 Main Handler\\n\")\n\t\t}\n\n\t\terrorHandler = func(ctx iris.Context) {\n\t\t\tctx.WriteString(\"#2 Main Error Handler\\n\")\n\t\t}\n\n\t\tuseHandler = makeMiddleware(\"#4 .Use\\n\")\n\t\tuseGlobal  = makeMiddleware(\"#3 .UseGlobal\\n\")\n\t\tuseError   = func(ctx iris.Context) {\n\t\t\t// UseError has captured the status code, because it runs\n\t\t\t// after the router itself but only one error handlers.\n\t\t\tctx.WriteString(\"#1 .UseError\\n\")\n\t\t\tctx.Next()\n\t\t}\n\t\tuseRouter = func(ctx iris.Context) {\n\t\t\tif ctx.Path() == \"/\" {\n\t\t\t\tctx.WriteString(\"#2 .UseRouter\\n\")\n\t\t\t}\n\n\t\t\tctx.Next()\n\t\t}\n\t\twrapRouter = func(w http.ResponseWriter, r *http.Request, router http.HandlerFunc) {\n\t\t\tif r.URL.Path == \"/\" {\n\t\t\t\tw.Write([]byte(\"#1 .WrapRouter\\n\"))\n\t\t\t\t/* Note for new Gophers:\n\n\t\t\t\tIf we write something here, on a not found resource,\n\t\t\t\tin the raw `net/http` wrapper like this one, then the\n\t\t\t\tresponse writer will send `200` status OK (on first write).\n\t\t\t\tAny error handler will not be fired as expected.\n\t\t\t\tAlso, when `w.WriteHeader` is called you can NOT\n\t\t\t\tchange the status code later on.\n\n\t\t\t\tIn Iris Handlers, if you write before status code set,\n\t\t\t\tthen it sends 200 status OK and it cannot change as well.\n\t\t\t\tHowever if we just called `ctx.StatusCode` inside an\n\t\t\t\tIris Handler without any content written then we\n\t\t\t\twould able to change the status code later on.\n\t\t\t\tWhen you need to change that behavior you should\n\t\t\t\tstart the handler with a ctx.Record() call.\n\t\t\t\t*/\n\t\t\t}\n\n\t\t\t// Continue by executing the Iris Router and leave it do its job.\n\t\t\trouter(w, r)\n\t\t}\n\t)\n\n\tapp := iris.New()\n\tapp.Use(useHandler)\n\tapp.UseGlobal(useGlobal)\n\tapp.UseError(useError)\n\tapp.UseRouter(useRouter)\n\tapp.WrapRouter(wrapRouter)\n\n\tapp.OnErrorCode(iris.StatusNotFound, errorHandler)\n\tapp.Get(\"/\", handler)\n\n\te := httptest.New(t, app)\n\te.GET(\"/NotFound\").Expect().Status(iris.StatusNotFound).Body().IsEqual(expectedNotFoundBody)\n\te.GET(\"/\").Expect().Status(iris.StatusOK).Body().IsEqual(expectedBody)\n}\n\nfunc TestResumeExecution(t *testing.T) {\n\tbefore := func(ctx iris.Context) {\n\t\tctx.WriteString(\"1\")\n\n\t\tcurIdx := ctx.HandlerIndex(-1)\n\n\t\tctx.StopExecution()\n\t\tctx.Next()\n\t\tctx.StopExecution()\n\t\tctx.Next()\n\t\tctx.ResumeExecution()\n\n\t\tif ctx.HandlerIndex(-1) != curIdx {\n\t\t\tctx.WriteString(\"| 1. NOT OK\")\n\t\t}\n\n\t\tctx.StopExecution()\n\t\tctx.ResumeExecution()\n\n\t\tif ctx.HandlerIndex(-1) != curIdx {\n\t\t\tctx.WriteString(\"| 2. NOT OK\")\n\t\t}\n\n\t\tctx.Next()\n\n\t\tif ctx.HandlerIndex(-1) != curIdx+2 /* 2 and 3 */ {\n\t\t\tctx.WriteString(\"| 3. NOT OK\")\n\t\t}\n\t}\n\n\thandler := func(ctx iris.Context) {\n\t\tctx.WriteString(\"2\")\n\t\tctx.Next()\n\t}\n\n\tafter := func(ctx iris.Context) {\n\t\tctx.WriteString(\"3\")\n\n\t\tif !ctx.Proceed(func(ctx iris.Context) {\n\t\t\tctx.Next()\n\t\t}) {\n\t\t\tctx.WriteString(\" | 4. NOT OK\")\n\t\t}\n\t}\n\n\texpectedBody := \"123\"\n\n\tapp := iris.New()\n\tapp.Get(\"/\", before, handler, after)\n\n\te := httptest.New(t, app)\n\te.GET(\"/\").Expect().Status(iris.StatusOK).Body().IsEqual(expectedBody)\n}\n"
  },
  {
    "path": "core/router/router_subdomain_redirect.go",
    "content": "package router\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"strconv\"\n\t\"strings\"\n\t\"text/template\"\n\t\"unicode/utf8\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/core/netutil\"\n)\n\ntype subdomainRedirectWrapper struct {\n\t// the func which will give us the root domain,\n\t// it's declared as a func because in that state the application is not configurated neither ran yet.\n\troot func() string\n\t// the from and to locations, if subdomains must end with dot('.').\n\tfrom, to string\n\t// true if from wildcard subdomain is given by 'from' (\"*.\" or '*').\n\tisFromAny bool\n\t// true for the location that is the root domain ('/', '.' or \"\").\n\tisFromRoot, isToRoot bool\n}\n\nfunc pathIsRootDomain(partyRelPath string) bool {\n\treturn partyRelPath == \"/\" || partyRelPath == \"\" || partyRelPath == \".\"\n}\n\nfunc pathIsWildcard(partyRelPath string) bool {\n\treturn partyRelPath == SubdomainWildcardIndicator || partyRelPath == \"*\"\n}\n\n// NewSubdomainRedirectWrapper returns a router wrapper which\n// if it's registered to the router via `router#WrapRouter` it\n// redirects(StatusMovedPermanently) a subdomain or the root domain to another subdomain or to the root domain.\n//\n// It receives three arguments,\n// the first one is a function which returns the root domain, (in the application it's the app.ConfigurationReadOnly().GetVHost()).\n// The second and third are the from and to locations, 'from' can be a wildcard subdomain as well (*. or *)\n// 'to' is not allowed to be a wildcard for obvious reasons,\n// 'from' can be the root domain when the 'to' is not the root domain and visa-versa.\n// To declare a root domain as 'from' or 'to' you MUST pass an empty string or a slash('/') or a dot('.').\n// Important note: the 'from' and 'to' should end with \".\" like we use the `APIBuilder#Party`, if they are subdomains.\n//\n// Usage(package-level):\n// sd := NewSubdomainRedirectWrapper(func() string { return \"mydomain.com\" }, \".\", \"www.\")\n// router.AddRouterWrapper(sd)\n//\n// Usage(high-level using `iris#Application.SubdomainRedirect`)\n// www := app.Subdomain(\"www\")\n// app.SubdomainRedirect(app, www)\n// Because app's rel path is \"/\" it translates it to the root domain\n// and www's party's rel path is the \"www.\", so it's the target subdomain.\n//\n// All the above code snippets will register a router wrapper which will\n// redirect all http(s)://mydomain.com/%anypath% to http(s)://www.mydomain.com/%anypath%.\n//\n// One or more subdomain redirect wrappers can be used to the same router instance.\n//\n// NewSubdomainRedirectWrapper may return nil if not allowed input arguments values were received\n// but in that case, the `AddRouterWrapper` will, simply, ignore that wrapper.\n//\n// Example: https://github.com/kataras/iris/tree/main/_examples/routing/subdomains/redirect\nfunc NewSubdomainRedirectWrapper(rootDomainGetter func() string, from, to string) WrapperFunc {\n\t// we can return nil,\n\t// because if wrapper is nil then it's not be used on the `router#AddRouterWrapper`.\n\tif from == to {\n\t\t// cannot redirect to the same location, cycle.\n\t\treturn nil\n\t}\n\n\tif pathIsWildcard(to) {\n\t\t// cannot redirect to \"any location\".\n\t\treturn nil\n\t}\n\n\tisFromRoot, isToRoot := pathIsRootDomain(from), pathIsRootDomain(to)\n\tif isFromRoot && isToRoot {\n\t\t// cannot redirect to the root domain from the root domain.\n\t\treturn nil\n\t}\n\n\tsd := &subdomainRedirectWrapper{\n\t\troot:       rootDomainGetter,\n\t\tfrom:       from,\n\t\tto:         to,\n\t\tisFromAny:  pathIsWildcard(from),\n\t\tisFromRoot: isFromRoot,\n\t\tisToRoot:   isToRoot,\n\t}\n\n\treturn sd.Wrapper\n}\n\n// Wrapper is the function that is being used to wrap the router with a redirect\n// service that is able to redirect between (sub)domains as fast as possible.\n// Please take a look at the `NewSubdomainRedirectWrapper` function for more.\nfunc (s *subdomainRedirectWrapper) Wrapper(w http.ResponseWriter, r *http.Request, router http.HandlerFunc) {\n\t// Author's note:\n\t// I use the StatusMovedPermanently(301) instead of the the StatusPermanentRedirect(308)\n\t// because older browsers may not be able to recognise that status code (the RFC 7538, is not so old)\n\t// although note that move is not the same thing as redirect: move reminds a specific address or location moved while\n\t// redirect is a new location.\n\thost := context.GetHost(r)\n\troot := s.root()\n\tif loopback := netutil.GetLoopbackSubdomain(root); loopback != \"\" {\n\t\troot = strings.Replace(root, loopback, context.GetDomain(host), 1)\n\t}\n\n\thasSubdomain := host != root\n\tif !hasSubdomain && !s.isFromRoot {\n\t\t// if the current endpoint is not a subdomain\n\t\t// and the redirect is not configured to be used from root domain to a subdomain.\n\t\t// This check comes first because it's the most common scenario.\n\t\trouter(w, r)\n\t\treturn\n\t}\n\n\tif hasSubdomain {\n\t\t// the current endpoint is a subdomain and\n\t\t// redirect is used for a subdomain to another subdomain or to its root domain.\n\t\tsubdomain := strings.TrimSuffix(host, root) // with dot '.'.\n\t\tif s.to == subdomain {\n\t\t\t// we are in the subdomain we wanted to be redirected,\n\t\t\t// remember: a redirect response will fire a new request.\n\t\t\t// This check is needed to not allow cycles (too many redirects).\n\t\t\trouter(w, r)\n\t\t\treturn\n\t\t}\n\n\t\tif subdomain == s.from || s.isFromAny {\n\t\t\tresturi := r.URL.RequestURI()\n\t\t\tif s.isToRoot {\n\t\t\t\t// from a specific subdomain or any subdomain to the root domain.\n\t\t\t\tRedirectAbsolute(w, r, context.GetScheme(r)+root+resturi, http.StatusMovedPermanently)\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// from a specific subdomain or any subdomain to a specific subdomain.\n\t\t\tRedirectAbsolute(w, r, context.GetScheme(r)+s.to+root+resturi, http.StatusMovedPermanently)\n\t\t\treturn\n\t\t}\n\n\t\t/* Think of another way. As it's a breaking change.\n\t\tif s.isFromRoot && !s.isFromAny {\n\t\t\t// Then we must not continue,\n\t\t\t// the subdomain didn't match the \"to\" but the from\n\t\t\t// was the application root itself, which is not a wildcard\n\t\t\t// so it shouldn't accept any subdomain, we must fire 404 here.\n\t\t\t// Something like:\n\t\t\t// http://registered_host_but_not_in_app.your.mydomain.com\n\t\t\thttp.NotFound(w, r)\n\t\t\treturn\n\t\t}\n\t\t*/\n\n\t\t// the from subdomain is not matched and it's not from root.\n\t\trouter(w, r)\n\t\treturn\n\t}\n\n\tif s.isFromRoot {\n\t\tresturi := r.URL.RequestURI()\n\t\t// we are not inside a subdomain, so we are in the root domain\n\t\t// and the redirect is configured to be used from root domain to a subdomain.\n\t\tRedirectAbsolute(w, r, context.GetScheme(r)+s.to+root+resturi, http.StatusMovedPermanently)\n\t\treturn\n\t}\n\n\trouter(w, r)\n}\n\n// NewSubdomainPartyRedirectHandler returns a handler which can be registered\n// through `UseRouter` or `Use` to redirect from the current request's\n// subdomain to the one which the given `to` Party can handle.\nfunc NewSubdomainPartyRedirectHandler(to Party) context.Handler {\n\treturn NewSubdomainRedirectHandler(to.GetRelPath())\n}\n\n// NewSubdomainRedirectHandler returns a handler which can be registered\n// through `UseRouter` or `Use` to redirect from the current request's\n// subdomain to the given \"toSubdomain\".\nfunc NewSubdomainRedirectHandler(toSubdomain string) context.Handler {\n\ttoSubdomain, _ = splitSubdomainAndPath(toSubdomain) // let it here so users can just pass the GetRelPath of a Party.\n\tif pathIsWildcard(toSubdomain) {\n\t\treturn nil\n\t}\n\n\treturn func(ctx *context.Context) {\n\t\t// en-us.test.mydomain.com\n\t\thost := ctx.Host()\n\t\tfullSubdomain := ctx.SubdomainFull()\n\t\ttargetHost := strings.Replace(host, fullSubdomain, toSubdomain, 1)\n\t\t// resturi := ctx.Request().URL.RequestURI()\n\t\t// urlToRedirect := ctx.Scheme() + newHost + resturi\n\t\tr := ctx.Request()\n\t\tr.Host = targetHost\n\t\tr.URL.Host = targetHost\n\t\turlToRedirect := r.URL.String()\n\t\tRedirectAbsolute(ctx.ResponseWriter(), r, urlToRedirect, http.StatusMovedPermanently)\n\t}\n}\n\n// RedirectAbsolute replies to the request with a redirect to an absolute URL.\n//\n// The provided code should be in the 3xx range and is usually\n// StatusMovedPermanently, StatusFound or StatusSeeOther.\n//\n// If the Content-Type header has not been set, Redirect sets it\n// to \"text/html; charset=utf-8\" and writes a small HTML body.\n// Setting the Content-Type header to any value, including nil,\n// disables that behavior.\nfunc RedirectAbsolute(w http.ResponseWriter, r *http.Request, url string, code int) {\n\th := w.Header()\n\n\t// RFC 7231 notes that a short HTML body is usually included in\n\t// the response because older user agents may not understand 301/307.\n\t// Do it only if the request didn't already have a Content-Type header.\n\t_, hadCT := h[context.ContentTypeHeaderKey]\n\n\th.Set(\"Location\", hexEscapeNonASCII(url))\n\tif !hadCT && (r.Method == http.MethodGet || r.Method == http.MethodHead) {\n\t\th.Set(context.ContentTypeHeaderKey, \"text/html; charset=utf-8\")\n\t}\n\tw.WriteHeader(code)\n\n\t// Shouldn't send the body for POST or HEAD; that leaves GET.\n\tif !hadCT && r.Method == \"GET\" {\n\t\tbody := \"<a href=\\\"\" + template.HTMLEscapeString(url) + \"\\\">\" + http.StatusText(code) + \"</a>.\\n\"\n\t\tfmt.Fprintln(w, body)\n\t}\n}\n\nfunc hexEscapeNonASCII(s string) string { // part of the standard library.\n\tnewLen := 0\n\tfor i := 0; i < len(s); i++ {\n\t\tif s[i] >= utf8.RuneSelf {\n\t\t\tnewLen += 3\n\t\t} else {\n\t\t\tnewLen++\n\t\t}\n\t}\n\tif newLen == len(s) {\n\t\treturn s\n\t}\n\tb := make([]byte, 0, newLen)\n\tfor i := 0; i < len(s); i++ {\n\t\tif s[i] >= utf8.RuneSelf {\n\t\t\tb = append(b, '%')\n\t\t\tb = strconv.AppendInt(b, int64(s[i]), 16)\n\t\t} else {\n\t\t\tb = append(b, s[i])\n\t\t}\n\t}\n\treturn string(b)\n}\n"
  },
  {
    "path": "core/router/router_test.go",
    "content": "package router_test\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/core/router\"\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nfunc TestRouteExists(t *testing.T) {\n\t// build the api\n\tapp := iris.New()\n\temptyHandler := func(*context.Context) {}\n\n\t// setup the tested routes\n\tapp.Handle(\"GET\", \"/route-exists\", emptyHandler)\n\tapp.Handle(\"POST\", \"/route-with-param/{param}\", emptyHandler)\n\n\t// check RouteExists\n\tapp.Handle(\"GET\", \"/route-test\", func(ctx *context.Context) {\n\t\tif ctx.RouteExists(\"GET\", \"/route-not-exists\") {\n\t\t\tt.Error(\"Route with path should not exists\")\n\t\t}\n\n\t\tif ctx.RouteExists(\"POST\", \"/route-exists\") {\n\t\t\tt.Error(\"Route with method should not exists\")\n\t\t}\n\n\t\tif !ctx.RouteExists(\"GET\", \"/route-exists\") {\n\t\t\tt.Error(\"Route 1 should exists\")\n\t\t}\n\n\t\tif !ctx.RouteExists(\"POST\", \"/route-with-param/a-param\") {\n\t\t\tt.Error(\"Route 2 should exists\")\n\t\t}\n\t})\n\n\t// run the tests\n\thttptest.New(t, app, httptest.Debug(false)).Request(\"GET\", \"/route-test\").Expect().Status(iris.StatusOK)\n}\n\nfunc TestLowercaseRouting(t *testing.T) {\n\tapp := iris.New()\n\tapp.WrapRouter(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {\n\t\t// test bottom to begin wrapper, the last ones should execute first.\n\t\t// The ones that are registered at `Build` state, after this `WrapRouter` call.\n\t\t// So path should be already lowecased.\n\t\tif expected, got := strings.ToLower(r.URL.Path), r.URL.Path; expected != got {\n\t\t\tt.Fatalf(\"expected path: %s but got: %s\", expected, got)\n\t\t}\n\t\tnext(w, r)\n\t})\n\n\th := func(ctx iris.Context) { ctx.WriteString(ctx.Path()) }\n\n\t// Register routes.\n\ttests := []string{\"/\", \"/lowercase\", \"/UPPERCASE\", \"/Title\", \"/m1xEd2\"}\n\tfor _, tt := range tests {\n\t\tapp.Get(tt, h)\n\t}\n\n\tapp.Configure(iris.WithLowercaseRouting)\n\t// Test routes.\n\te := httptest.New(t, app)\n\tfor _, tt := range tests {\n\t\ts := strings.ToLower(tt)\n\t\te.GET(tt).Expect().Status(httptest.StatusOK).Body().IsEqual(s)\n\t\te.GET(s).Expect().Status(httptest.StatusOK).Body().IsEqual(s)\n\t\te.GET(strings.ToUpper(tt)).Expect().Status(httptest.StatusOK).Body().IsEqual(s)\n\t}\n}\n\nfunc TestRouterWrapperOrder(t *testing.T) {\n\t// last is wrapping the previous.\n\n\t// first is executed last.\n\tuserWrappers := []router.WrapperFunc{\n\t\tfunc(w http.ResponseWriter, r *http.Request, main http.HandlerFunc) {\n\t\t\tio.WriteString(w, \"6\")\n\t\t\tmain(w, r)\n\t\t},\n\t\tfunc(w http.ResponseWriter, r *http.Request, main http.HandlerFunc) {\n\t\t\tio.WriteString(w, \"5\")\n\t\t\tmain(w, r)\n\t\t},\n\t}\n\t// should be executed before userWrappers.\n\tredirectionWrappers := []router.WrapperFunc{\n\t\tfunc(w http.ResponseWriter, r *http.Request, main http.HandlerFunc) {\n\t\t\tio.WriteString(w, \"3\")\n\t\t\tmain(w, r)\n\t\t},\n\t\tfunc(w http.ResponseWriter, r *http.Request, main http.HandlerFunc) {\n\t\t\tio.WriteString(w, \"4\")\n\t\t\tmain(w, r)\n\t\t},\n\t}\n\t// should be executed before redirectionWrappers.\n\tafterRedirectionWrappers := []router.WrapperFunc{\n\t\tfunc(w http.ResponseWriter, r *http.Request, main http.HandlerFunc) {\n\t\t\tio.WriteString(w, \"2\")\n\t\t\tmain(w, r)\n\t\t},\n\t\tfunc(w http.ResponseWriter, r *http.Request, main http.HandlerFunc) {\n\t\t\tio.WriteString(w, \"1\")\n\t\t\tmain(w, r)\n\t\t},\n\t}\n\n\ttestOrder1 := iris.New()\n\tfor _, w := range userWrappers {\n\t\ttestOrder1.WrapRouter(w)\n\t\t// this always wraps the previous one, but it's not accessible after Build state,\n\t\t// the below are simulating the SubdomainRedirect and ForceLowercaseRouting.\n\t}\n\tfor _, w := range redirectionWrappers {\n\t\ttestOrder1.AddRouterWrapper(w)\n\t}\n\tfor _, w := range afterRedirectionWrappers {\n\t\ttestOrder1.PrependRouterWrapper(w)\n\t}\n\n\ttestOrder2 := iris.New()\n\tfor _, w := range redirectionWrappers {\n\t\ttestOrder2.AddRouterWrapper(w)\n\t}\n\tfor _, w := range userWrappers {\n\t\ttestOrder2.WrapRouter(w)\n\t}\n\tfor _, w := range afterRedirectionWrappers {\n\t\ttestOrder2.PrependRouterWrapper(w)\n\t}\n\n\ttestOrder3 := iris.New()\n\tfor _, w := range redirectionWrappers {\n\t\ttestOrder3.AddRouterWrapper(w)\n\t}\n\tfor _, w := range afterRedirectionWrappers {\n\t\ttestOrder3.PrependRouterWrapper(w)\n\t}\n\tfor _, w := range userWrappers {\n\t\ttestOrder3.WrapRouter(w)\n\t}\n\n\tappTests := []*iris.Application{\n\t\ttestOrder1, testOrder2, testOrder3,\n\t}\n\n\texpectedOrderStr := \"123456\"\n\tfor _, app := range appTests {\n\t\tapp.Get(\"/\", func(ctx iris.Context) {}) // to not append the not found one.\n\n\t\te := httptest.New(t, app)\n\t\te.GET(\"/\").Expect().Status(iris.StatusOK).Body().IsEqual(expectedOrderStr)\n\t}\n}\n\nfunc TestNewSubdomainPartyRedirectHandler(t *testing.T) {\n\tapp := iris.New()\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.WriteString(\"root index\")\n\t})\n\n\ttest := app.Subdomain(\"test\")\n\ttest.OnErrorCode(iris.StatusNotFound, func(ctx iris.Context) {\n\t\tctx.WriteString(\"test 404\")\n\t})\n\ttest.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.WriteString(\"test index\")\n\t})\n\n\ttestold := app.Subdomain(\"testold\")\n\t// redirects testold.mydomain.com to test.mydomain.com .\n\ttestold.UseRouter(router.NewSubdomainPartyRedirectHandler(test))\n\ttestold.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.WriteString(\"test old index (should never be fired)\")\n\t})\n\ttestoldLeveled := testold.Subdomain(\"leveled\")\n\ttestoldLeveled.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.WriteString(\"leveled.testold this can be fired\")\n\t})\n\n\tif redirectHandler := router.NewSubdomainPartyRedirectHandler(app.WildcardSubdomain()); redirectHandler != nil {\n\t\tt.Fatal(\"redirect handler should be nil, we cannot redirect to a wildcard\")\n\t}\n\n\te := httptest.New(t, app)\n\te.GET(\"/\").WithURL(\"http://mydomain.com\").Expect().Status(iris.StatusOK).Body().IsEqual(\"root index\")\n\te.GET(\"/\").WithURL(\"http://test.mydomain.com\").Expect().Status(iris.StatusOK).Body().IsEqual(\"test index\")\n\te.GET(\"/\").WithURL(\"http://testold.mydomain.com\").Expect().Status(iris.StatusOK).Body().IsEqual(\"test index\")\n\te.GET(\"/\").WithURL(\"http://testold.mydomain.com/notfound\").Expect().Status(iris.StatusNotFound).Body().IsEqual(\"test 404\")\n\te.GET(\"/\").WithURL(\"http://leveled.testold.mydomain.com\").Expect().Status(iris.StatusOK).Body().IsEqual(\"leveled.testold this can be fired\")\n}\n\nfunc TestHandleServer(t *testing.T) {\n\totherApp := iris.New()\n\totherApp.Get(\"/test/me/{first:string}\", func(ctx iris.Context) {\n\t\tctx.HTML(fmt.Sprintf(\"<h1>Other App: %s</h1>\", ctx.Params().Get(\"first\")))\n\t})\n\totherApp.Build()\n\n\tapp := iris.New()\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tctx.HTML(\"<h1>Main App</h1>\")\n\t})\n\n\tapp.HandleServer(\"/api/identity/{first:string}/orgs/{second:string}/{p:path}\", otherApp)\n\n\te := httptest.New(t, app)\n\te.GET(\"/\").Expect().Status(iris.StatusOK).Body().IsEqual(\"<h1>Main App</h1>\")\n\te.GET(\"/api/identity/first/orgs/second/test/me/kataras\").Expect().Status(iris.StatusOK).Body().IsEqual(\"<h1>Other App: kataras</h1>\")\n\te.GET(\"/api/identity/first/orgs/second/test/me\").Expect().Status(iris.StatusNotFound)\n}\n"
  },
  {
    "path": "core/router/router_wildcard_root_test.go",
    "content": "// black-box testing\n//\n// see _examples/routing/main_test.go for the most common router tests that you may want to see,\n// this is a test for the new feature that I just coded: wildcard \"/{p:path}\" on root without conflicts\n\npackage router_test\n\nimport (\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nconst (\n\tsame_as_request_path                         = \"same\"\n\tfrom_status_code                             = \"from\"\n\tstaticPathPrefixBody                         = \"from the static path: \"\n\tprefix_static_path_following_by_request_path = \"prefix_same\"\n)\n\ntype testRouteRequest struct {\n\tmethod             string\n\tsubdomain          string\n\tpath               string\n\texpectedStatusCode int\n\texpectedBody       string\n}\n\ntype testRoute struct {\n\tmethod   string\n\tpath     string\n\thandler  context.Handler\n\trequests []testRouteRequest\n}\n\nvar h = func(ctx *context.Context) {\n\tctx.WriteString(ctx.Path())\n}\n\nvar h2 = func(ctx *context.Context) {\n\tctx.StatusCode(iris.StatusForbidden) // ! 200 but send the body as expected,\n\t// we need that kind of behavior to determinate which handler is executed for routes that\n\t// both having wildcard path but first one is registered on root level.\n\tctx.WriteString(ctx.Path())\n}\n\nfunc h3(ctx *context.Context) {\n\tctx.WriteString(staticPathPrefixBody + ctx.Path())\n}\n\nfunc TestRouterWildcardDifferentPrefixPath(t *testing.T) {\n\ttt := []testRoute{\n\t\t{\"GET\", \"/s/{p:path}\", h, []testRouteRequest{\n\t\t\t{\"GET\", \"\", \"/s/that/is/wildcard\", iris.StatusOK, same_as_request_path},\n\t\t\t{\"GET\", \"\", \"/s/ok\", iris.StatusOK, same_as_request_path},\n\t\t}},\n\t\t{\"GET\", \"/som/{p:path}\", h, []testRouteRequest{\n\t\t\t{\"GET\", \"\", \"/som/that/is/wildcard\", iris.StatusOK, same_as_request_path},\n\t\t\t{\"GET\", \"\", \"/som/ok\", iris.StatusOK, same_as_request_path},\n\t\t}},\n\t\t{\"GET\", \"/some/{p:path}\", h, []testRouteRequest{\n\t\t\t{\"GET\", \"\", \"/some/that/is/wildcard\", iris.StatusOK, same_as_request_path},\n\t\t\t{\"GET\", \"\", \"/some1/that/is/wildcard\", iris.StatusNotFound, from_status_code},\n\t\t}},\n\t}\n\n\ttestTheRoutes(t, tt, true)\n}\n\nfunc TestRouterWildcardAndStatic(t *testing.T) {\n\ttt := []testRoute{\n\t\t{\"GET\", \"/some/{p:path}\", h2, []testRouteRequest{\n\t\t\t{\"GET\", \"\", \"/some/that/is/wildcard\", iris.StatusForbidden, same_as_request_path},\n\t\t\t{\"GET\", \"\", \"/some/did\", iris.StatusForbidden, same_as_request_path},\n\t\t\t{\"GET\", \"\", \"/some1/that/is/wildcard\", iris.StatusNotFound, from_status_code},\n\t\t}},\n\t\t{\"GET\", \"/some/static\", h, []testRouteRequest{\n\t\t\t{\"GET\", \"\", \"/some/static\", iris.StatusOK, same_as_request_path},\n\t\t}},\n\n\t\t{\"GET\", \"/s/{p:path}\", h2, []testRouteRequest{\n\t\t\t{\"GET\", \"\", \"/s/that/is/wildcard\", iris.StatusForbidden, same_as_request_path},\n\t\t\t{\"GET\", \"\", \"/s/did\", iris.StatusForbidden, same_as_request_path},\n\t\t\t{\"GET\", \"\", \"/s1/that/is/wildcard\", iris.StatusNotFound, from_status_code},\n\t\t}},\n\t\t{\"GET\", \"/s/static\", h, []testRouteRequest{\n\t\t\t{\"GET\", \"\", \"/s/static\", iris.StatusOK, same_as_request_path},\n\t\t}},\n\t}\n\n\ttestTheRoutes(t, tt, false)\n}\n\nfunc TestRouterWildcardRootMany(t *testing.T) {\n\ttt := []testRoute{\n\t\t// all routes will be handlded by \"h\" because we added wildcard to root,\n\t\t// this feature is very important and can remove noumerous of previous hacks on our apps.\n\t\t{\"GET\", \"/{p:path}\", h, []testRouteRequest{\n\t\t\t{\"GET\", \"\", \"/this/is/wildcard/on/root\", iris.StatusOK, same_as_request_path},\n\t\t}}, // mormally, order matters, root should be registered at last\n\t\t// but we change the front level order algorithm to put last these automatically\n\t\t// see handler.go\n\t\t{\"GET\", \"/some/{p:path}\", h2, []testRouteRequest{\n\t\t\t{\"GET\", \"\", \"/some/that/is/wildcard\", iris.StatusForbidden, same_as_request_path},\n\t\t\t{\"GET\", \"\", \"/some/did\", iris.StatusForbidden, same_as_request_path},\n\t\t}},\n\t\t{\"GET\", \"/some/static\", h, []testRouteRequest{\n\t\t\t{\"GET\", \"\", \"/some/static\", iris.StatusOK, same_as_request_path},\n\t\t}},\n\t\t{\"GET\", \"/some1\", h, []testRouteRequest{\n\t\t\t{\"GET\", \"\", \"/some1\", iris.StatusOK, same_as_request_path},\n\t\t\t// this will show up because of the first wildcard, as we wanted to do.\n\t\t\t{\"GET\", \"\", \"/some1/that/is/wildcard\", iris.StatusOK, same_as_request_path},\n\t\t}},\n\t}\n\n\ttestTheRoutes(t, tt, true)\n}\n\nfunc TestRouterWildcardRootManyAndRootStatic(t *testing.T) {\n\ttt := []testRoute{\n\t\t// routes that may return 404 will be handled by the below route (\"h\" handler) because we added wildcard to root,\n\t\t// this feature is very important and can remove noumerous of previous hacks on our apps.\n\t\t//\n\t\t// Static paths and parameters have priority over wildcard, all three types can be registered in the same path prefix.\n\t\t//\n\t\t// Remember, all of those routes are registered don't be tricked by the visual appearance of the below test blocks.\n\t\t{\"GET\", \"/{p:path}\", h, []testRouteRequest{\n\t\t\t{\"GET\", \"\", \"/other2almost/some\", iris.StatusOK, same_as_request_path},\n\t\t}},\n\t\t{\"GET\", \"/static/{p:path}\", h, []testRouteRequest{\n\t\t\t{\"GET\", \"\", \"/static\", iris.StatusOK, same_as_request_path}, // HERE<- IF NOT FOUND THEN BACKWARDS TO WILDCARD IF THERE IS ONE, HMM.\n\t\t\t{\"GET\", \"\", \"/static/something/here\", iris.StatusOK, same_as_request_path},\n\t\t}},\n\t\t{\"GET\", \"/\", h, []testRouteRequest{\n\t\t\t{\"GET\", \"\", \"/\", iris.StatusOK, same_as_request_path},\n\t\t}},\n\t\t{\"GET\", \"/other/{paramother:path}\", h2, []testRouteRequest{\n\t\t\t// OK and not h2 because of the root wildcard.\n\t\t\t{\"GET\", \"\", \"/other\", iris.StatusOK, same_as_request_path},\n\t\t\t{\"GET\", \"\", \"/other/wildcard\", iris.StatusForbidden, same_as_request_path},\n\t\t\t{\"GET\", \"\", \"/other/wildcard/here\", iris.StatusForbidden, same_as_request_path},\n\t\t}},\n\t\t{\"GET\", \"/other2/{paramothersecond:path}\", h2, []testRouteRequest{\n\t\t\t{\"GET\", \"\", \"/other2/wildcard\", iris.StatusForbidden, same_as_request_path},\n\t\t\t{\"GET\", \"\", \"/other2/more/than/one/path/parts\", iris.StatusForbidden, same_as_request_path},\n\t\t}},\n\t\t{\"GET\", \"/other2/static\", h3, []testRouteRequest{\n\t\t\t{\"GET\", \"\", \"/other2/static\", iris.StatusOK, prefix_static_path_following_by_request_path},\n\t\t\t// h2(Forbiddenn) instead of h3 OK because it will be handled by the /other2/{paramothersecond:path}'s handler which gives 403.\n\t\t\t{\"GET\", \"\", \"/other2/staticed\", iris.StatusForbidden, same_as_request_path},\n\t\t}},\n\t}\n\n\ttestTheRoutes(t, tt, false)\n}\n\nfunc testTheRoutes(t *testing.T, tests []testRoute, debug bool) {\n\t// build the api\n\tapp := iris.New()\n\tfor _, tt := range tests {\n\t\tapp.Handle(tt.method, tt.path, tt.handler)\n\t}\n\n\t// setup the test suite\n\te := httptest.New(t, app, httptest.Debug(debug))\n\n\t// run the tests\n\tfor _, tt := range tests {\n\t\tfor _, req := range tt.requests {\n\t\t\t// t.Logf(\"req: %s:%s\\n\", tt.method, tt.path)\n\t\t\tmethod := req.method\n\t\t\tif method == \"\" {\n\t\t\t\tmethod = tt.method\n\t\t\t}\n\t\t\tex := e.Request(method, req.path)\n\t\t\tif req.subdomain != \"\" {\n\t\t\t\tex.WithURL(\"http://\" + req.subdomain + \".localhost:8080\")\n\t\t\t}\n\n\t\t\texpectedBody := req.expectedBody\n\t\t\tif req.expectedBody == same_as_request_path {\n\t\t\t\texpectedBody = req.path\n\t\t\t}\n\t\t\tif req.expectedBody == from_status_code {\n\t\t\t\texpectedBody = http.StatusText(req.expectedStatusCode)\n\t\t\t}\n\t\t\tif req.expectedBody == prefix_static_path_following_by_request_path {\n\t\t\t\texpectedBody = staticPathPrefixBody + req.path\n\t\t\t}\n\n\t\t\tex.Expect().Status(req.expectedStatusCode).Body().IsEqual(expectedBody)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "core/router/router_wrapper.go",
    "content": "package router\n\nimport \"net/http\"\n\n// WrapperFunc is used as an expected input parameter signature\n// for the WrapRouter. It's a \"low-level\" signature which is compatible\n// with the net/http.\n// It's being used to run or no run the router based on a custom logic.\ntype WrapperFunc func(w http.ResponseWriter, r *http.Request, router http.HandlerFunc)\n\nfunc makeWrapperFunc(original WrapperFunc, wrapperFunc WrapperFunc) WrapperFunc {\n\tif wrapperFunc == nil {\n\t\treturn original\n\t}\n\n\tif original != nil {\n\t\t// wrap into one function, from bottom to top, end to begin.\n\t\tnextWrapper := wrapperFunc\n\t\tprevWrapper := original\n\t\twrapperFunc = func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {\n\t\t\tif next != nil {\n\t\t\t\tnexthttpFunc := http.HandlerFunc(func(_w http.ResponseWriter, _r *http.Request) {\n\t\t\t\t\tprevWrapper(_w, _r, next)\n\t\t\t\t})\n\t\t\t\tnextWrapper(w, r, nexthttpFunc)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn wrapperFunc\n}\n\ntype wrapper struct {\n\trouter      http.HandlerFunc // http.HandlerFunc to catch the CURRENT state of its .ServeHTTP on case of future change.\n\twrapperFunc WrapperFunc\n}\n\n// newWrapper returns a new http.Handler wrapped by the 'wrapperFunc'\n// the \"next\" is the final \"wrapped\" input parameter.\n//\n// Application is responsible to make it to work on more than one wrappers\n// via composition or func clojure.\nfunc newWrapper(wrapperFunc WrapperFunc, wrapped http.HandlerFunc) http.Handler {\n\treturn &wrapper{\n\t\twrapperFunc: wrapperFunc,\n\t\trouter:      wrapped,\n\t}\n}\n\nfunc (wr *wrapper) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\twr.wrapperFunc(w, r, wr.router)\n}\n"
  },
  {
    "path": "core/router/router_wrapper_test.go",
    "content": "package router\n\nimport (\n\t\"bytes\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\nfunc TestMakeWrapperFunc(t *testing.T) {\n\tvar (\n\t\tfirstBody    = []byte(\"1\")\n\t\tsecondBody   = []byte(\"2\")\n\t\tmainBody     = []byte(\"3\")\n\t\texpectedBody = append(firstBody, append(secondBody, mainBody...)...)\n\t)\n\n\tpre := func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {\n\t\tw.Header().Set(\"X-Custom\", \"data\")\n\t\tnext(w, r)\n\t}\n\n\tfirst := func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {\n\t\tw.Write(firstBody)\n\t\tnext(w, r)\n\t}\n\n\tsecond := func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {\n\t\tw.Write(secondBody)\n\t\tnext(w, r)\n\t}\n\n\tmainHandler := func(w http.ResponseWriter, r *http.Request) {\n\t\tw.Write(mainBody)\n\t}\n\n\twrapper := makeWrapperFunc(second, first)\n\twrapper = makeWrapperFunc(wrapper, pre)\n\n\tw := httptest.NewRecorder()\n\tr := httptest.NewRequest(\"GET\", \"https://iris-go.com\", nil)\n\twrapper(w, r, mainHandler)\n\n\tif got := w.Body.Bytes(); !bytes.Equal(expectedBody, got) {\n\t\tt.Fatalf(\"expected boy: %s but got: %s\", string(expectedBody), string(got))\n\t}\n\n\tif expected, got := \"data\", w.Header().Get(\"X-Custom\"); expected != got {\n\t\tt.Fatalf(\"expected x-custom header: %s but got: %s\", expected, got)\n\t}\n}\n"
  },
  {
    "path": "core/router/status_test.go",
    "content": "// black-box testing\npackage router_test\n\nimport (\n\t\"bytes\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/context\"\n\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nvar defaultErrHandler = func(ctx *context.Context) {\n\ttext := http.StatusText(ctx.GetStatusCode())\n\tctx.WriteString(text)\n}\n\nfunc TestOnAnyErrorCode(t *testing.T) {\n\tapp := iris.New()\n\tapp.Configure(iris.WithFireMethodNotAllowed)\n\n\tbuff := &bytes.Buffer{}\n\texpectedPrintBeforeExecuteErr := \"printed before error\"\n\n\t// with a middleware\n\tapp.OnAnyErrorCode(func(ctx *context.Context) {\n\t\tbuff.WriteString(expectedPrintBeforeExecuteErr)\n\t\tctx.Next()\n\t}, defaultErrHandler)\n\n\texpectedFoundResponse := \"found\"\n\tapp.Get(\"/found\", func(ctx *context.Context) {\n\t\tctx.WriteString(expectedFoundResponse)\n\t})\n\n\texpected407 := \"this should be sent, we manage the response response by ourselves\"\n\tapp.Get(\"/407\", func(ctx *context.Context) {\n\t\tctx.Record()\n\t\tctx.WriteString(expected407)\n\t\tctx.StatusCode(iris.StatusProxyAuthRequired)\n\t})\n\n\te := httptest.New(t, app)\n\n\te.GET(\"/found\").Expect().Status(iris.StatusOK).\n\t\tBody().IsEqual(expectedFoundResponse)\n\n\te.GET(\"/notfound\").Expect().Status(iris.StatusNotFound).\n\t\tBody().IsEqual(http.StatusText(iris.StatusNotFound))\n\n\tcheckAndClearBuf(t, buff, expectedPrintBeforeExecuteErr)\n\n\te.POST(\"/found\").Expect().Status(iris.StatusMethodNotAllowed).\n\t\tBody().IsEqual(http.StatusText(iris.StatusMethodNotAllowed))\n\n\tcheckAndClearBuf(t, buff, expectedPrintBeforeExecuteErr)\n\n\te.GET(\"/407\").Expect().Status(iris.StatusProxyAuthRequired).\n\t\tBody().IsEqual(expected407)\n\n\t// Test Configuration.ResetOnFireErrorCode.\n\tapp2 := iris.New()\n\tapp2.Configure(iris.WithResetOnFireErrorCode)\n\n\tapp2.OnAnyErrorCode(func(ctx *context.Context) {\n\t\tbuff.WriteString(expectedPrintBeforeExecuteErr)\n\t\tctx.Next()\n\t}, defaultErrHandler)\n\n\tapp2.Get(\"/406\", func(ctx *context.Context) {\n\t\tctx.Record()\n\t\tctx.WriteString(\"this should not be sent, only status text will be sent\")\n\t\tctx.WriteString(\"the handler can handle 'rollback' of the text when error code fired because of the recorder\")\n\t\tctx.StatusCode(iris.StatusNotAcceptable)\n\t})\n\n\thttptest.New(t, app2).GET(\"/406\").Expect().Status(iris.StatusNotAcceptable).\n\t\tBody().IsEqual(http.StatusText(iris.StatusNotAcceptable))\n\n\tcheckAndClearBuf(t, buff, expectedPrintBeforeExecuteErr)\n}\n\nfunc checkAndClearBuf(t *testing.T, buff *bytes.Buffer, expected string) {\n\tt.Helper()\n\n\tif got := buff.String(); got != expected {\n\t\tt.Fatalf(\"expected middleware to run before the error handler, expected: '%s' but got: '%s'\", expected, got)\n\t}\n\n\tbuff.Reset()\n}\n\nfunc TestPartyOnErrorCode(t *testing.T) {\n\tapp := iris.New()\n\tapp.Configure(iris.WithFireMethodNotAllowed)\n\n\tglobalNotFoundResponse := \"custom not found\"\n\tapp.OnErrorCode(iris.StatusNotFound, func(ctx iris.Context) {\n\t\tctx.WriteString(globalNotFoundResponse)\n\t})\n\n\tglobalMethodNotAllowedResponse := \"global: method not allowed\"\n\tapp.OnErrorCode(iris.StatusMethodNotAllowed, func(ctx iris.Context) {\n\t\tctx.WriteString(globalMethodNotAllowedResponse)\n\t})\n\n\tapp.Get(\"/path\", h)\n\n\tusersResponse := \"users: method allowed\"\n\tusers := app.Party(\"/users\")\n\tusers.OnErrorCode(iris.StatusMethodNotAllowed, func(ctx iris.Context) {\n\t\tctx.WriteString(usersResponse)\n\t})\n\tusers.Get(\"/\", h)\n\twrite400 := func(ctx iris.Context) { ctx.StatusCode(iris.StatusBadRequest) }\n\t// test setting the error code from a handler.\n\tusers.Get(\"/badrequest\", write400)\n\n\tusersuserResponse := \"users:user: method allowed\"\n\tuser := users.Party(\"/{id:int}\")\n\tuser.OnErrorCode(iris.StatusMethodNotAllowed, func(ctx iris.Context) {\n\t\tctx.WriteString(usersuserResponse)\n\t})\n\tusersuserNotFoundResponse := \"users:user: not found\"\n\tuser.OnErrorCode(iris.StatusNotFound, func(ctx iris.Context) {\n\t\tctx.WriteString(usersuserNotFoundResponse)\n\t})\n\tuser.Get(\"/\", h)\n\tuser.Get(\"/ab/badrequest\", write400)\n\n\tfriends := users.Party(\"/friends\")\n\tfriends.Get(\"/{id:int}\", h)\n\n\te := httptest.New(t, app)\n\n\te.GET(\"/notfound\").Expect().Status(iris.StatusNotFound).Body().IsEqual(globalNotFoundResponse)\n\te.POST(\"/path\").Expect().Status(iris.StatusMethodNotAllowed).Body().IsEqual(globalMethodNotAllowedResponse)\n\te.GET(\"/path\").Expect().Status(iris.StatusOK).Body().IsEqual(\"/path\")\n\n\te.POST(\"/users\").Expect().Status(iris.StatusMethodNotAllowed).\n\t\tBody().IsEqual(usersResponse)\n\n\te.POST(\"/users/42\").Expect().Status(iris.StatusMethodNotAllowed).\n\t\tBody().IsEqual(usersuserResponse)\n\n\te.GET(\"/users/42\").Expect().Status(iris.StatusOK).\n\t\tBody().IsEqual(\"/users/42\")\n\te.GET(\"/users/ab\").Expect().Status(iris.StatusNotFound).Body().IsEqual(usersuserNotFoundResponse)\n\t// inherit the parent.\n\te.GET(\"/users/42/friends/dsa\").Expect().Status(iris.StatusNotFound).Body().IsEqual(usersuserNotFoundResponse)\n\n\t// if not registered to the party, then the root is taking action.\n\te.GET(\"/users/42/ab/badrequest\").Expect().Status(iris.StatusBadRequest).Body().IsEqual(http.StatusText(iris.StatusBadRequest))\n\n\t// if not registered to the party, and not in root, then just write the status text (fallback behavior)\n\te.GET(\"/users/badrequest\").Expect().Status(iris.StatusBadRequest).\n\t\tBody().IsEqual(http.StatusText(iris.StatusBadRequest))\n}\n"
  },
  {
    "path": "core/router/trie.go",
    "content": "package router\n\nimport (\n\t\"strings\"\n\n\t\"github.com/kataras/iris/v12/context\"\n)\n\nconst (\n\t// ParamStart the character in string representation where the underline router starts its dynamic named parameter.\n\tParamStart = \":\"\n\t// paramStartCharacter is the character as rune of ParamStart.\n\tparamStartCharacter = ':'\n\t// WildcardParamStart the character in string representation where the underline router starts its dynamic wildcard\n\t// path parameter.\n\tWildcardParamStart = \"*\"\n\t// wildcardParamStartCharacter is the character as rune of WildcardParamStart.\n\twildcardParamStartCharacter = '*'\n)\n\n// An iris-specific identical version of the https://github.com/kataras/muxie version 1.0.0 released at 15 Oct 2018\ntype trieNode struct {\n\tparent *trieNode\n\n\tchildren               map[string]*trieNode\n\thasDynamicChild        bool     // does one of the children contains a parameter or wildcard?\n\tchildNamedParameter    bool     // is the child a named parameter (single segmnet)\n\tchildWildcardParameter bool     // or it is a wildcard (can be more than one path segments) ?\n\tparamKeys              []string // the param keys without : or *.\n\tend                    bool     // it is a complete node, here we stop and we can say that the node is valid.\n\tkey                    string   // if end == true then key is filled with the original value of the insertion's key.\n\t// if key != \"\" && its parent has childWildcardParameter == true,\n\t// we need it to track the static part for the closest-wildcard's parameter storage.\n\tstaticKey string\n\n\t// insert data.\n\tRoute    context.RouteReadOnly\n\tHandlers context.Handlers\n}\n\nfunc newTrieNode() *trieNode {\n\tn := new(trieNode)\n\treturn n\n}\n\nfunc (tn *trieNode) hasChild(s string) bool {\n\treturn tn.getChild(s) != nil\n}\n\nfunc (tn *trieNode) getChild(s string) *trieNode {\n\tif tn.children == nil {\n\t\treturn nil\n\t}\n\n\treturn tn.children[s]\n}\n\nfunc (tn *trieNode) addChild(s string, n *trieNode) {\n\tif tn.children == nil {\n\t\ttn.children = make(map[string]*trieNode)\n\t}\n\n\tif _, exists := tn.children[s]; exists {\n\t\treturn\n\t}\n\n\tn.parent = tn\n\ttn.children[s] = n\n}\n\nfunc (tn *trieNode) findClosestParentWildcardNode() *trieNode {\n\ttn = tn.parent\n\tfor tn != nil {\n\t\tif tn.childWildcardParameter {\n\t\t\treturn tn.getChild(WildcardParamStart)\n\t\t}\n\n\t\ttn = tn.parent\n\t}\n\n\treturn nil\n}\n\nfunc (tn *trieNode) String() string {\n\treturn tn.key\n}\n\ntype trie struct {\n\troot *trieNode\n\n\t// if true then it will handle any path if not other parent wildcard exists,\n\t// so even 404 (on http services) is up to it, see trie#insert.\n\thasRootWildcard bool\n\thasRootSlash    bool\n\n\tstatusCode int // for error codes only, method is ignored.\n\tmethod     string\n\n\t// subdomain is empty for default-hostname routes,\n\t// ex: mysubdomain.\n\tsubdomain string\n}\n\nconst (\n\tpathSep  = \"/\"\n\tpathSepB = '/'\n)\n\nfunc slowPathSplit(path string) []string {\n\tif path == \"/\" {\n\t\treturn []string{\"/\"}\n\t}\n\n\treturn strings.Split(path, pathSep)[1:]\n}\n\nfunc (tr *trie) insert(path string, route context.RouteReadOnly, handlers context.Handlers) {\n\tinput := slowPathSplit(path)\n\tif len(input) == 0 {\n\t\treturn\n\t}\n\n\tn := tr.root\n\tif path == pathSep {\n\t\ttr.hasRootSlash = true\n\t}\n\n\tvar paramKeys []string\n\n\tfor _, s := range input {\n\t\tif len(s) == 0 {\n\t\t\tcontinue\n\t\t}\n\n\t\tc := s[0]\n\n\t\tif len(s) > 1 { // has more than one character.\n\t\t\t// get the next character, should be the name of the parameter.\n\t\t\t// E.g:\n\t\t\t// If /test/:param (or /test/*param) then it's dynamic.\n\t\t\t// If /test/: (or /test/*) then it's static.\n\t\t\tif isParam, isWildcard := c == paramStartCharacter, c == wildcardParamStartCharacter; isParam || isWildcard {\n\t\t\t\tn.hasDynamicChild = true\n\t\t\t\tparamKeys = append(paramKeys, s[1:]) // without : or *.\n\t\t\t\tif isParam {\n\t\t\t\t\tn.childNamedParameter = true\n\t\t\t\t\ts = ParamStart\n\t\t\t\t}\n\n\t\t\t\tif isWildcard {\n\t\t\t\t\tn.childWildcardParameter = true\n\t\t\t\t\ts = WildcardParamStart\n\t\t\t\t\tif tr.root == n {\n\t\t\t\t\t\ttr.hasRootWildcard = true\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif !n.hasChild(s) {\n\t\t\tchild := newTrieNode()\n\t\t\tn.addChild(s, child)\n\t\t}\n\n\t\tn = n.getChild(s)\n\t}\n\n\tn.Route = route\n\tn.Handlers = handlers\n\n\tn.paramKeys = paramKeys\n\tn.key = path\n\tn.end = true\n\n\ti := strings.Index(path, ParamStart)\n\tif i == -1 {\n\t\ti = strings.Index(path, WildcardParamStart)\n\t}\n\tif i == -1 {\n\t\ti = len(n.key)\n\t}\n\n\tn.staticKey = path[:i]\n\t// fmt.Printf(\"trie.insert: (whole path=%v) Path: %s, Route name: %s, Handlers len: %d\\n\", n.end, n.key, route.Name(), len(handlers))\n}\n\nfunc (tr *trie) search(q string, params *context.RequestParams) *trieNode {\n\tend := len(q)\n\n\tif end == 0 || (end == 1 && q[0] == pathSepB) {\n\t\t// fixes only root wildcard but no / registered at.\n\t\tif tr.hasRootSlash {\n\t\t\treturn tr.root.getChild(pathSep)\n\t\t} else if tr.hasRootWildcard {\n\t\t\t// no need to going through setting parameters, this one has not but it is wildcard.\n\t\t\treturn tr.root.getChild(WildcardParamStart)\n\t\t}\n\n\t\treturn nil\n\t}\n\n\tn := tr.root\n\tstart := 1\n\ti := 1\n\tvar paramValues []string\n\n\tfor {\n\t\tif i == end || q[i] == pathSepB {\n\t\t\tsegment := q[start:i]\n\t\t\tif child := n.getChild(segment); child != nil {\n\t\t\t\tn = child\n\t\t\t} else if n.childNamedParameter {\n\t\t\t\tn = n.getChild(ParamStart)\n\t\t\t\tif ln := len(paramValues); cap(paramValues) > ln {\n\t\t\t\t\tparamValues = paramValues[:ln+1]\n\t\t\t\t\tparamValues[ln] = segment\n\t\t\t\t} else {\n\t\t\t\t\tparamValues = append(paramValues, segment)\n\t\t\t\t}\n\t\t\t} else if n.childWildcardParameter {\n\t\t\t\tn = n.getChild(WildcardParamStart)\n\t\t\t\tif ln := len(paramValues); cap(paramValues) > ln {\n\t\t\t\t\tparamValues = paramValues[:ln+1]\n\t\t\t\t\tparamValues[ln] = q[start:]\n\t\t\t\t} else {\n\t\t\t\t\tparamValues = append(paramValues, q[start:])\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t} else {\n\t\t\t\tn = n.findClosestParentWildcardNode()\n\t\t\t\tif n != nil && len(n.paramKeys) > 0 {\n\t\t\t\t\t// means that it has :param/static and *wildcard, we go trhough the :param\n\t\t\t\t\t// but the next path segment is not the /static, so go back to *wildcard\n\t\t\t\t\t// instead of not found.\n\t\t\t\t\t//\n\t\t\t\t\t// Fixes:\n\t\t\t\t\t// /hello/*p\n\t\t\t\t\t// /hello/:p1/static/:p2\n\t\t\t\t\t// req: http://localhost:8080/hello/dsadsa/static/dsadsa => found\n\t\t\t\t\t// req: http://localhost:8080/hello/dsadsa => but not found!\n\t\t\t\t\t// and\n\t\t\t\t\t// /second/wild/*p\n\t\t\t\t\t// /second/wild/static/otherstatic/\n\t\t\t\t\t// req: /second/wild/static/otherstatic/random => but not found!\n\t\t\t\t\tparams.Set(n.paramKeys[0], q[len(n.staticKey):])\n\t\t\t\t\treturn n\n\t\t\t\t}\n\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\tif i == end {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\ti++\n\t\t\tstart = i\n\t\t\tcontinue\n\t\t}\n\n\t\ti++\n\t}\n\n\tif n == nil || !n.end {\n\t\tif n != nil { // we need it on both places, on last segment (below) or on the first unnknown (above).\n\t\t\tif n = n.findClosestParentWildcardNode(); n != nil && len(n.paramKeys) > 0 {\n\t\t\t\tparams.Set(n.paramKeys[0], q[len(n.staticKey):])\n\t\t\t\treturn n\n\t\t\t}\n\t\t}\n\n\t\tif tr.hasRootWildcard {\n\t\t\t// that's the case for root wildcard, tests are passing\n\t\t\t// even without it but stick with it for reference.\n\t\t\t// Note ote that something like:\n\t\t\t// Routes: /other2/*myparam and /other2/static\n\t\t\t// Reqs: /other2/staticed will be handled\n\t\t\t// the /other2/*myparam and not the root wildcard, which is what we want.\n\t\t\t//\n\t\t\tn = tr.root.getChild(WildcardParamStart)\n\t\t\tif len(n.paramKeys) == 0 { // fix crashes on /*/*/*.\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\tparams.Set(n.paramKeys[0], q[1:])\n\t\t\treturn n\n\t\t}\n\n\t\treturn nil\n\t}\n\n\tfor i, paramValue := range paramValues {\n\t\tif len(n.paramKeys) > i {\n\t\t\tparams.Set(n.paramKeys[i], paramValue)\n\t\t}\n\t}\n\n\treturn n\n}\n"
  },
  {
    "path": "doc.go",
    "content": "/*\nPackage iris implements the highest realistic performance, easy to learn Go web framework.\nIris provides a beautifully expressive and easy to use foundation for your next website, API, or distributed app.\nLow-level handlers compatible with `net/http` and high-level fastest MVC implementation and handlers dependency injection.\nEasy to learn for new gophers and advanced features for experienced, it goes as far as you dive into it!\n\nSource code and other details for the project are available at GitHub:\n\n\thttps://github.com/kataras/iris\n\n# Current Version\n\n12.2.11\n\n# Installation\n\nThe only requirement is the Go Programming Language, at least version 1.23.\n\n\t$ go get github.com/kataras/iris/v12@latest\n\nWiki:\n\n\thttps://www.iris-go.com/#ebookDonateForm\n\nExamples:\n\n\thttps://github.com/kataras/iris/tree/main/_examples\n\nMiddleware:\n\n\thttps://github.com/kataras/iris/tree/main/middleware\n\thttps://github.com/iris-contrib/middleware\n\nHome Page:\n\n\thttps://iris-go.com\n*/\npackage iris\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/kataras/iris/v12\n\ngo 1.25\n\nretract [v12.0.0, v12.1.8] // Retract older versions as only latest is to be depended upon. Please update to @latest\n\nrequire (\n\tgithub.com/BurntSushi/toml v1.6.0\n\tgithub.com/CloudyKit/jet/v6 v6.3.1\n\tgithub.com/Joker/jade v1.1.3\n\tgithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05\n\tgithub.com/andybalholm/brotli v1.2.0\n\tgithub.com/blang/semver/v4 v4.0.0\n\tgithub.com/dgraph-io/badger/v4 v4.9.0\n\tgithub.com/fatih/structs v1.1.0\n\tgithub.com/flosch/pongo2/v4 v4.0.2\n\tgithub.com/golang/snappy v1.0.0\n\tgithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a\n\tgithub.com/google/uuid v1.6.0\n\tgithub.com/gorilla/securecookie v1.1.2\n\tgithub.com/iris-contrib/httpexpect/v2 v2.15.2\n\tgithub.com/iris-contrib/schema v0.0.6\n\tgithub.com/json-iterator/go v1.1.12\n\tgithub.com/kataras/blocks v0.0.8\n\tgithub.com/kataras/golog v0.1.12\n\tgithub.com/kataras/jwt v0.1.17\n\tgithub.com/kataras/neffos v0.0.24\n\tgithub.com/kataras/pio v0.0.13\n\tgithub.com/kataras/sitemap v0.0.6\n\tgithub.com/kataras/tunnel v0.0.4\n\tgithub.com/klauspost/compress v1.18.2\n\tgithub.com/mailgun/raymond/v2 v2.0.48\n\tgithub.com/mailru/easyjson v0.9.1\n\tgithub.com/microcosm-cc/bluemonday v1.0.27\n\tgithub.com/redis/go-redis/v9 v9.17.2\n\tgithub.com/schollz/closestmatch v2.1.0+incompatible\n\tgithub.com/shirou/gopsutil/v3 v3.24.5\n\tgithub.com/tdewolff/minify/v2 v2.24.8\n\tgithub.com/vmihailenco/msgpack/v5 v5.4.1\n\tgithub.com/yosssi/ace v0.0.5\n\tgo.etcd.io/bbolt v1.4.3\n\tgolang.org/x/crypto v0.47.0\n\tgolang.org/x/exp v0.0.0-20260112195511-716be5621a96\n\tgolang.org/x/net v0.49.0\n\tgolang.org/x/sys v0.40.0\n\tgolang.org/x/text v0.33.0\n\tgolang.org/x/time v0.14.0\n\tgoogle.golang.org/protobuf v1.36.11\n\tgopkg.in/ini.v1 v1.67.1\n\tgopkg.in/yaml.v3 v3.0.1\n)\n\nrequire (\n\tgithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect\n\tgithub.com/ajg/form v1.5.1 // indirect\n\tgithub.com/aymerick/douceur v0.2.0 // indirect\n\tgithub.com/cespare/xxhash/v2 v2.3.0 // indirect\n\tgithub.com/davecgh/go-spew v1.1.1 // indirect\n\tgithub.com/dgraph-io/ristretto/v2 v2.2.0 // indirect\n\tgithub.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect\n\tgithub.com/dustin/go-humanize v1.0.1 // indirect\n\tgithub.com/fatih/color v1.15.0 // indirect\n\tgithub.com/go-logr/logr v1.4.3 // indirect\n\tgithub.com/go-logr/stdr v1.2.2 // indirect\n\tgithub.com/go-ole/go-ole v1.2.6 // indirect\n\tgithub.com/gobwas/glob v0.2.3 // indirect\n\tgithub.com/gobwas/httphead v0.1.0 // indirect\n\tgithub.com/gobwas/pool v0.2.1 // indirect\n\tgithub.com/gobwas/ws v1.4.0 // indirect\n\tgithub.com/google/flatbuffers v25.2.10+incompatible // indirect\n\tgithub.com/google/go-querystring v1.1.0 // indirect\n\tgithub.com/gorilla/css v1.0.1 // indirect\n\tgithub.com/gorilla/websocket v1.5.3 // indirect\n\tgithub.com/imkira/go-interpol v1.1.0 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect\n\tgithub.com/mattn/go-colorable v0.1.13 // indirect\n\tgithub.com/mattn/go-isatty v0.0.19 // indirect\n\tgithub.com/mediocregopher/radix/v3 v3.8.1 // indirect\n\tgithub.com/mitchellh/go-wordwrap v1.0.1 // indirect\n\tgithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect\n\tgithub.com/modern-go/reflect2 v1.0.2 // indirect\n\tgithub.com/nats-io/nats.go v1.40.1 // indirect\n\tgithub.com/nats-io/nkeys v0.4.9 // indirect\n\tgithub.com/nats-io/nuid v1.0.1 // indirect\n\tgithub.com/nxadm/tail v1.4.11 // indirect\n\tgithub.com/pmezard/go-difflib v1.0.0 // indirect\n\tgithub.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect\n\tgithub.com/russross/blackfriday/v2 v2.1.0 // indirect\n\tgithub.com/sanity-io/litter v1.5.5 // indirect\n\tgithub.com/sergi/go-diff v1.0.0 // indirect\n\tgithub.com/shoenig/go-m1cpu v0.1.6 // indirect\n\tgithub.com/sirupsen/logrus v1.8.1 // indirect\n\tgithub.com/stretchr/testify v1.11.1 // indirect\n\tgithub.com/tdewolff/parse/v2 v2.8.5 // indirect\n\tgithub.com/tklauser/go-sysconf v0.3.12 // indirect\n\tgithub.com/tklauser/numcpus v0.6.1 // indirect\n\tgithub.com/valyala/bytebufferpool v1.0.0 // indirect\n\tgithub.com/vmihailenco/tagparser/v2 v2.0.0 // indirect\n\tgithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect\n\tgithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect\n\tgithub.com/xeipuuv/gojsonschema v1.2.0 // indirect\n\tgithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 // indirect\n\tgithub.com/yudai/gojsondiff v1.0.0 // indirect\n\tgithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect\n\tgithub.com/yusufpapurcu/wmi v1.2.4 // indirect\n\tgo.opentelemetry.io/auto/sdk v1.1.0 // indirect\n\tgo.opentelemetry.io/otel v1.37.0 // indirect\n\tgo.opentelemetry.io/otel/metric v1.37.0 // indirect\n\tgo.opentelemetry.io/otel/trace v1.37.0 // indirect\n\tgolang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect\n\tmoul.io/http2curl/v2 v2.3.0 // indirect\n)\n"
  },
  {
    "path": "go.sum",
    "content": "github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=\ngithub.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=\ngithub.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=\ngithub.com/CloudyKit/jet/v6 v6.3.1 h1:6IAo5Cx21xrHVaR8zzXN5gJatKV/wO7Nf6bfCnCSbUw=\ngithub.com/CloudyKit/jet/v6 v6.3.1/go.mod h1:lf8ksdNsxZt7/yH/3n4vJQWA9RUq4wpaHtArHhGVMOw=\ngithub.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc=\ngithub.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=\ngithub.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=\ngithub.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05 h1:dG7/gLroJGht/jSQtHiLvT48Hxn+crbmvyItZC8cWOs=\ngithub.com/Shopify/goreferrer v0.0.0-20250617153402-88c1d9a79b05/go.mod h1:NYezi6wtnJtBm5btoprXc5SvAdqH0XTXWnUup0MptAI=\ngithub.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=\ngithub.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=\ngithub.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=\ngithub.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=\ngithub.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=\ngithub.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=\ngithub.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=\ngithub.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=\ngithub.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=\ngithub.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=\ngithub.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=\ngithub.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=\ngithub.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=\ngithub.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/dgraph-io/badger/v4 v4.9.0 h1:tpqWb0NewSrCYqTvywbcXOhQdWcqephkVkbBmaaqHzc=\ngithub.com/dgraph-io/badger/v4 v4.9.0/go.mod h1:5/MEx97uzdPUHR4KtkNt8asfI2T4JiEiQlV7kWUo8c0=\ngithub.com/dgraph-io/ristretto/v2 v2.2.0 h1:bkY3XzJcXoMuELV8F+vS8kzNgicwQFAaGINAEJdWGOM=\ngithub.com/dgraph-io/ristretto/v2 v2.2.0/go.mod h1:RZrm63UmcBAaYWC1DotLYBmTvgkrs0+XhBd7Npn7/zI=\ngithub.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da h1:aIftn67I1fkbMa512G+w+Pxci9hJPB8oMnkcP3iZF38=\ngithub.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=\ngithub.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=\ngithub.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=\ngithub.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=\ngithub.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=\ngithub.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=\ngithub.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=\ngithub.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=\ngithub.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=\ngithub.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw=\ngithub.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8=\ngithub.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=\ngithub.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=\ngithub.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=\ngithub.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=\ngithub.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=\ngithub.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=\ngithub.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=\ngithub.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=\ngithub.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=\ngithub.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=\ngithub.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=\ngithub.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=\ngithub.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=\ngithub.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=\ngithub.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=\ngithub.com/gobwas/ws v1.4.0 h1:CTaoG1tojrh4ucGPcoJFiAQUAsEWekEWvLy7GsVNqGs=\ngithub.com/gobwas/ws v1.4.0/go.mod h1:G3gNqMNtPppf5XUz7O4shetPpcZ1VJ7zt18dlUeakrc=\ngithub.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=\ngithub.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a h1:l7A0loSszR5zHd/qK53ZIHMO8b3bBSmENnQ6eKnUT0A=\ngithub.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=\ngithub.com/google/flatbuffers v25.2.10+incompatible h1:F3vclr7C3HpB1k9mxCGRMXq6FdUalZ6H/pNX4FP1v0Q=\ngithub.com/google/flatbuffers v25.2.10+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=\ngithub.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=\ngithub.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=\ngithub.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=\ngithub.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=\ngithub.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=\ngithub.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=\ngithub.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=\ngithub.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=\ngithub.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2 h1:T9THsdP1woyAqKHwjkEsbCnMefsAFvk8iJJKokcJ3Go=\ngithub.com/iris-contrib/httpexpect/v2 v2.15.2/go.mod h1:JLDgIqnFy5loDSUv1OA2j0mb6p/rDhiCqigP22Uq9xE=\ngithub.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw=\ngithub.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=\ngithub.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=\ngithub.com/kataras/blocks v0.0.8 h1:MrpVhoFTCR2v1iOOfGng5VJSILKeZZI+7NGfxEh3SUM=\ngithub.com/kataras/blocks v0.0.8/go.mod h1:9Jm5zx6BB+06NwA+OhTbHW1xkMOYxahnqTN5DveZ2Yg=\ngithub.com/kataras/golog v0.1.12 h1:Bu7I/G4ilJlbfzjmU39O9N+2uO1pBcMK045fzZ4ytNg=\ngithub.com/kataras/golog v0.1.12/go.mod h1:wrGSbOiBqbQSQznleVNX4epWM8rl9SJ/rmEacl0yqy4=\ngithub.com/kataras/jwt v0.1.17 h1:dYjemzcdYqA4ylwq9/56MslCr/pNOyVUZ2bl3hYNHgc=\ngithub.com/kataras/jwt v0.1.17/go.mod h1:HUnU5HDBCDanVF8zrPVSE2VK8HicospKefZDD4DzOKU=\ngithub.com/kataras/neffos v0.0.24 h1:S3lHqJopCfXN285VdlbGeOj+Id83u4xdQKToa+w1vW0=\ngithub.com/kataras/neffos v0.0.24/go.mod h1:/3K9zQ0yEC5/xUiSQx46ToWa3xneGfUo/nMit/F5g+U=\ngithub.com/kataras/pio v0.0.13 h1:x0rXVX0fviDTXOOLOmr4MUxOabu1InVSTu5itF8CXCM=\ngithub.com/kataras/pio v0.0.13/go.mod h1:k3HNuSw+eJ8Pm2lA4lRhg3DiCjVgHlP8hmXApSej3oM=\ngithub.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY=\ngithub.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4=\ngithub.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA=\ngithub.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw=\ngithub.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=\ngithub.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=\ngithub.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=\ngithub.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=\ngithub.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=\ngithub.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8=\ngithub.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=\ngithub.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=\ngithub.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=\ngithub.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=\ngithub.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=\ngithub.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/mediocregopher/radix/v3 v3.8.1 h1:rOkHflVuulFKlwsLY01/M2cM2tWCjDoETcMqKbAWu1M=\ngithub.com/mediocregopher/radix/v3 v3.8.1/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8=\ngithub.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=\ngithub.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=\ngithub.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=\ngithub.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=\ngithub.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=\ngithub.com/nats-io/nats.go v1.40.1 h1:MLjDkdsbGUeCMKFyCFoLnNn/HDTqcgVa3EQm+pMNDPk=\ngithub.com/nats-io/nats.go v1.40.1/go.mod h1:wV73x0FSI/orHPSYoyMeJB+KajMDoWyXmFaRrrYaaTo=\ngithub.com/nats-io/nkeys v0.4.9 h1:qe9Faq2Gxwi6RZnZMXfmGMZkg3afLLOtrU+gDZJ35b0=\ngithub.com/nats-io/nkeys v0.4.9/go.mod h1:jcMqs+FLG+W5YO36OX6wFIFcmpdAns+w1Wm6D3I/evE=\ngithub.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=\ngithub.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY=\ngithub.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc=\ngithub.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=\ngithub.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=\ngithub.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=\ngithub.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M=\ngithub.com/pkg/diff v0.0.0-20200914180035-5b29258ca4f7/go.mod h1:zO8QMzTeZd5cpnIkz/Gn6iK0jDfGicM1nynOkkPIl28=\ngithub.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=\ngithub.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=\ngithub.com/redis/go-redis/v9 v9.17.2 h1:P2EGsA4qVIM3Pp+aPocCJ7DguDHhqrXNhVcEp4ViluI=\ngithub.com/redis/go-redis/v9 v9.17.2/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0//kSOd3370=\ngithub.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=\ngithub.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=\ngithub.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=\ngithub.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=\ngithub.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk=\ngithub.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=\ngithub.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=\ngithub.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=\ngithub.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=\ngithub.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk=\ngithub.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=\ngithub.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=\ngithub.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=\ngithub.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=\ngithub.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=\ngithub.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=\ngithub.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=\ngithub.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=\ngithub.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=\ngithub.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502/go.mod h1:p9lPsd+cx33L3H9nNoecRRxPssFKUwwI50I3pZ0yT+8=\ngithub.com/tdewolff/minify/v2 v2.24.8 h1:58/VjsbevI4d5FGV0ZSuBrHMSSkH4MCH0sIz/eKIauE=\ngithub.com/tdewolff/minify/v2 v2.24.8/go.mod h1:0Ukj0CRpo/sW/nd8uZ4ccXaV1rEVIWA3dj8U7+Shhfw=\ngithub.com/tdewolff/parse/v2 v2.8.5 h1:ZmBiA/8Do5Rpk7bDye0jbbDUpXXbCdc3iah4VeUvwYU=\ngithub.com/tdewolff/parse/v2 v2.8.5/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=\ngithub.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE=\ngithub.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=\ngithub.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=\ngithub.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=\ngithub.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=\ngithub.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=\ngithub.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=\ngithub.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=\ngithub.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=\ngithub.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=\ngithub.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=\ngithub.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=\ngithub.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=\ngithub.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA=\ngithub.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0=\ngithub.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=\ngithub.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=\ngithub.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=\ngithub.com/yudai/pp v2.0.1+incompatible h1:Q4//iY4pNF6yPLZIigmvcl7k/bPgrcTPIFIcmawg5bI=\ngithub.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=\ngithub.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngithub.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=\ngithub.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=\ngo.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo=\ngo.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E=\ngo.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=\ngo.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=\ngo.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=\ngo.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=\ngo.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=\ngo.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=\ngo.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=\ngo.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=\ngolang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=\ngolang.org/x/exp v0.0.0-20260112195511-716be5621a96 h1:Z/6YuSHTLOHfNFdb8zVZomZr7cqNgTJvA8+Qz75D8gU=\ngolang.org/x/exp v0.0.0-20260112195511-716be5621a96/go.mod h1:nzimsREAkjBCIEFtHiYkrJyT+2uy9YZJB7H1k68CXZU=\ngolang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=\ngolang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=\ngolang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=\ngolang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=\ngolang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=\ngolang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=\ngolang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=\ngolang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20201211185031-d93e913c1a58/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY=\ngolang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=\ngoogle.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=\ngoogle.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/ini.v1 v1.67.1 h1:tVBILHy0R6e4wkYOn3XmiITt/hEVH4TFMYvAX2Ytz6k=\ngopkg.in/ini.v1 v1.67.1/go.mod h1:x/cyOwCgZqOkJoDIJ3c1KNHMo10+nLGAhh+kn3Zizss=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nmoul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs=\nmoul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE=\n"
  },
  {
    "path": "hero/binding.go",
    "content": "package hero\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"sort\"\n\t\"strconv\"\n\n\t\"github.com/kataras/iris/v12/context\"\n)\n\n// binding contains the Dependency and the Input, it's the result of a function or struct + dependencies.\ntype binding struct {\n\tDependency *Dependency\n\tInput      *Input\n}\n\n// Input contains the input reference of which a dependency is binded to.\ntype Input struct {\n\tIndex            int    // for func inputs\n\tStructFieldIndex []int  // for struct fields in order to support embedded ones.\n\tStructFieldName  string // the struct field's name.\n\tType             reflect.Type\n\n\tselfValue reflect.Value // reflect.ValueOf(*Input) cache.\n}\n\nfunc newInput(typ reflect.Type, index int, structFieldIndex []int) *Input {\n\tin := &Input{\n\t\tIndex:            index,\n\t\tStructFieldIndex: structFieldIndex,\n\t\tType:             typ,\n\t}\n\n\tin.selfValue = reflect.ValueOf(in)\n\treturn in\n}\n\nfunc newStructFieldInput(f reflect.StructField) *Input {\n\tinput := newInput(f.Type, f.Index[0], f.Index)\n\tinput.StructFieldName = f.Name\n\treturn input\n}\n\n// String returns the string representation of a binding.\nfunc (b *binding) String() string {\n\tvar index string\n\tif len(b.Input.StructFieldIndex) > 0 {\n\t\tindex = strconv.Itoa(b.Input.StructFieldIndex[0])\n\t\tfor _, i := range b.Input.StructFieldIndex[1:] {\n\t\t\tindex += fmt.Sprintf(\".%d\", i)\n\t\t}\n\t} else {\n\t\tindex = strconv.Itoa(b.Input.Index)\n\t}\n\n\treturn fmt.Sprintf(\"[%s:%s] maps to [%s]\", index, b.Input.Type.String(), b.Dependency)\n}\n\n// Equal compares \"b\" and \"other\" bindings and reports whether they are referring to the same values.\nfunc (b *binding) Equal(other *binding) bool {\n\tif b == nil {\n\t\treturn other == nil\n\t}\n\n\tif other == nil {\n\t\treturn false\n\t}\n\n\t// if b.String() != other.String() {\n\t// \treturn false\n\t// }\n\n\tif expected, got := b.Dependency != nil, other.Dependency != nil; expected != got {\n\t\treturn false\n\t}\n\n\tif expected, got := fmt.Sprintf(\"%v\", b.Dependency.OriginalValue), fmt.Sprintf(\"%v\", other.Dependency.OriginalValue); expected != got {\n\t\treturn false\n\t}\n\n\tif expected, got := b.Dependency.DestType != nil, other.Dependency.DestType != nil; expected != got {\n\t\treturn false\n\t}\n\n\tif b.Dependency.DestType != nil {\n\t\tif expected, got := b.Dependency.DestType.String(), other.Dependency.DestType.String(); expected != got {\n\t\t\treturn false\n\t\t}\n\t}\n\n\tif expected, got := b.Input != nil, other.Input != nil; expected != got {\n\t\treturn false\n\t}\n\n\tif b.Input != nil {\n\t\tif expected, got := b.Input.Index, other.Input.Index; expected != got {\n\t\t\treturn false\n\t\t}\n\n\t\tif expected, got := b.Input.Type.String(), other.Input.Type.String(); expected != got {\n\t\t\treturn false\n\t\t}\n\n\t\tif expected, got := b.Input.StructFieldIndex, other.Input.StructFieldIndex; !reflect.DeepEqual(expected, got) {\n\t\t\treturn false\n\t\t}\n\t}\n\n\treturn true\n}\n\n// DependencyMatcher type alias describes a dependency match function.\ntype DependencyMatcher = func(*Dependency, reflect.Type) bool\n\n// DefaultDependencyMatcher is the default dependency match function for all DI containers.\n// It is used to collect dependencies from struct's fields and function's parameters.\nvar DefaultDependencyMatcher = func(dep *Dependency, in reflect.Type) bool {\n\tif dep.Explicit {\n\t\treturn dep.DestType == in\n\t}\n\n\treturn dep.DestType == nil || equalTypes(dep.DestType, in)\n}\n\n// ToDependencyMatchFunc converts a DependencyMatcher (generic for all dependencies)\n// to a dependency-specific input matcher.\nfunc ToDependencyMatchFunc(d *Dependency, match DependencyMatcher) DependencyMatchFunc {\n\treturn func(in reflect.Type) bool {\n\t\treturn match(d, in)\n\t}\n}\n\nfunc getBindingsFor(inputs []reflect.Type, deps []*Dependency, disablePayloadAutoBinding bool, paramsCount int) (bindings []*binding) {\n\t// Path parameter start index is the result of [total path parameters] - [total func path parameters inputs],\n\t// moving from last to first path parameters and first to last (probably) available input args.\n\t//\n\t// That way the above will work as expected:\n\t// 1. mvc.New(app.Party(\"/path/{firstparam}\")).Handle(....Controller.GetBy(secondparam string))\n\t// 2. mvc.New(app.Party(\"/path/{firstparam}/{secondparam}\")).Handle(...Controller.GetBy(firstparam, secondparam string))\n\t// 3. usersRouter := app.Party(\"/users/{id:uint64}\"); usersRouter.ConfigureContainer().Handle(method, \"/\", handler(id uint64))\n\t// 4. usersRouter.Party(\"/friends\").ConfigureContainer().Handle(method, \"/{friendID:uint64}\", handler(friendID uint64))\n\t//\n\t// Therefore, count the inputs that can be path parameters first.\n\tshouldBindParams := make(map[int]struct{})\n\ttotalParamsExpected := 0\n\tif paramsCount != -1 {\n\t\tfor i, in := range inputs {\n\t\t\tif _, canBePathParameter := context.ParamResolvers[in]; !canBePathParameter {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tshouldBindParams[i] = struct{}{}\n\n\t\t\ttotalParamsExpected++\n\t\t}\n\t}\n\n\tstartParamIndex := paramsCount - totalParamsExpected\n\tif startParamIndex < 0 {\n\t\tstartParamIndex = 0\n\t}\n\n\tlastParamIndex := startParamIndex\n\n\tgetParamIndex := func() int {\n\t\tparamIndex := lastParamIndex\n\t\tlastParamIndex++\n\t\treturn paramIndex\n\t}\n\n\tbindedInput := make(map[int]struct{})\n\n\tfor i, in := range inputs { //order matters.\n\t\t_, canBePathParameter := shouldBindParams[i]\n\n\t\tprevN := len(bindings) // to check if a new binding is attached; a dependency was matched (see below).\n\n\t\tfor j := len(deps) - 1; j >= 0; j-- {\n\t\t\td := deps[j]\n\t\t\t// Note: we could use the same slice to return.\n\t\t\t//\n\t\t\t// Add all dynamic dependencies (caller-selecting) and the exact typed dependencies.\n\t\t\t//\n\t\t\t// A dependency can only be matched to 1 value, and 1 value has a single dependency\n\t\t\t// (e.g. to avoid conflicting path parameters of the same type).\n\t\t\tif _, alreadyBinded := bindedInput[j]; alreadyBinded {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tmatch := d.Match(in)\n\t\t\tif !match {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif canBePathParameter {\n\t\t\t\t// wrap the existing dependency handler.\n\t\t\t\tparamHandler := paramDependencyHandler(getParamIndex())\n\t\t\t\tprevHandler := d.Handle\n\t\t\t\td.Handle = func(ctx *context.Context, input *Input) (reflect.Value, error) {\n\t\t\t\t\tv, err := paramHandler(ctx, input)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tv, err = prevHandler(ctx, input)\n\t\t\t\t\t}\n\n\t\t\t\t\treturn v, err\n\t\t\t\t}\n\t\t\t\td.Static = false\n\t\t\t\td.OriginalValue = nil\n\t\t\t}\n\n\t\t\tbindings = append(bindings, &binding{\n\t\t\t\tDependency: d,\n\t\t\t\tInput:      newInput(in, i, nil),\n\t\t\t})\n\n\t\t\tif !d.Explicit { // if explicit then it can be binded to more than one input\n\t\t\t\tbindedInput[j] = struct{}{}\n\t\t\t}\n\n\t\t\tbreak\n\t\t}\n\n\t\tif prevN == len(bindings) {\n\t\t\tif canBePathParameter { // Let's keep that option just for \"payload\": disablePayloadAutoBinding\n\t\t\t\t// no new dependency added for this input,\n\t\t\t\t// let's check for path parameters.\n\t\t\t\tbindings = append(bindings, paramBinding(i, getParamIndex(), in))\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// else, if payload binding is not disabled,\n\t\t\t// add builtin request bindings that\n\t\t\t// could be registered by end-dev but they didn't\n\t\t\tif !disablePayloadAutoBinding && isPayloadType(in) {\n\t\t\t\tbindings = append(bindings, payloadBinding(i, in))\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t}\n\n\treturn\n}\n\nfunc isPayloadType(in reflect.Type) bool {\n\tswitch indirectType(in).Kind() {\n\tcase reflect.Struct, reflect.Slice, reflect.Ptr:\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\nfunc getBindingsForFunc(fn reflect.Value, dependencies []*Dependency, disablePayloadAutoBinding bool, paramsCount int) []*binding {\n\tfnTyp := fn.Type()\n\tif !isFunc(fnTyp) {\n\t\tpanic(fmt.Sprintf(\"bindings: unresolved: no a func type: %#+v\", fn))\n\t}\n\n\tn := fnTyp.NumIn()\n\tinputs := make([]reflect.Type, n)\n\tfor i := 0; i < n; i++ {\n\t\tinputs[i] = fnTyp.In(i)\n\t}\n\n\tbindings := getBindingsFor(inputs, dependencies, disablePayloadAutoBinding, paramsCount)\n\tif expected, got := n, len(bindings); expected != got {\n\t\texpectedInputs := \"\"\n\t\tmissingInputs := \"\"\n\t\tfor i, in := range inputs {\n\t\t\tpos := i + 1\n\t\t\ttypName := in.String()\n\t\t\texpectedInputs += fmt.Sprintf(\"\\n  - [%d] %s\", pos, typName)\n\t\t\tfound := false\n\t\t\tfor _, b := range bindings {\n\t\t\t\tif b.Input.Index == i {\n\t\t\t\t\tfound = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif !found {\n\t\t\t\tmissingInputs += fmt.Sprintf(\"\\n  - [%d] %s\", pos, typName)\n\t\t\t}\n\t\t}\n\n\t\tfnName := context.HandlerName(fn)\n\t\tpanic(fmt.Sprintf(\"expected [%d] bindings (input parameters) but got [%d]\\nFunction:\\n  - %s\\nExpected:%s\\nMissing:%s\",\n\t\t\texpected, got, fnName, expectedInputs, missingInputs))\n\t}\n\n\treturn bindings\n}\n\nfunc getBindingsForStruct(v reflect.Value, dependencies []*Dependency, markExportedFieldsAsRequired bool, disablePayloadAutoBinding, enableStructDependents bool, matchDependency DependencyMatcher, paramsCount int, sorter Sorter) (bindings []*binding) {\n\ttyp := indirectType(v.Type())\n\tif typ.Kind() != reflect.Struct {\n\t\tpanic(fmt.Sprintf(\"bindings: unresolved: not a struct type: %#+v\", v))\n\t}\n\n\t// get bindings from any struct's non zero values first, including unexported.\n\telem := reflect.Indirect(v)\n\tnonZero := lookupNonZeroFieldValues(elem)\n\tfor _, f := range nonZero {\n\t\t// fmt.Printf(\"Controller [%s] | NonZero | Field Index: %v | Field Type: %s\\n\", typ, f.Index, f.Type)\n\t\tbindings = append(bindings, &binding{\n\t\t\tDependency: newDependency(elem.FieldByIndex(f.Index).Interface(), disablePayloadAutoBinding, enableStructDependents, nil),\n\t\t\tInput:      newStructFieldInput(f),\n\t\t})\n\t}\n\n\tfields, stateless := lookupFields(elem, true, true, nil)\n\tn := len(fields)\n\n\tif n > 1 && sorter != nil {\n\t\tsort.Slice(fields, func(i, j int) bool {\n\t\t\treturn sorter(fields[i].Type, fields[j].Type)\n\t\t})\n\t}\n\n\tinputs := make([]reflect.Type, n)\n\tfor i := 0; i < n; i++ {\n\t\t// fmt.Printf(\"Controller [%s] | Field Index: %v | Field Type: %s\\n\", typ, fields[i].Index, fields[i].Type)\n\t\tinputs[i] = fields[i].Type\n\t}\n\n\texportedBindings := getBindingsFor(inputs, dependencies, disablePayloadAutoBinding, paramsCount)\n\n\t// fmt.Printf(\"Controller [%s] | Inputs length: %d vs Bindings length: %d | NonZero: %d | Stateless : %d\\n\",\n\t// \ttyp, n, len(exportedBindings), len(nonZero), stateless)\n\t// for i, b := range exportedBindings {\n\t// \tfmt.Printf(\"[%d] [Static=%v] %#+v\\n\", i, b.Dependency.Static, b.Dependency.OriginalValue)\n\t// }\n\n\tif markExportedFieldsAsRequired && len(exportedBindings) != n {\n\t\tpanic(fmt.Sprintf(\"MarkExportedFieldsAsRequired is true and at least one of struct's (%s) field was not binded to a dependency.\\nFields length: %d, matched exported bindings length: %d.\\nUse the Reporter for further details\", typ.String(), n, len(exportedBindings)))\n\t}\n\n\tif stateless == 0 && len(nonZero) >= len(exportedBindings) {\n\t\t// if we have not a single stateless and fields are defined then just return.\n\t\t// Note(@kataras): this can accept further improvements.\n\t\treturn\n\t}\n\n\t// get declared bindings from deps.\n\tbindings = append(bindings, exportedBindings...)\n\tfor _, binding := range bindings {\n\t\t// fmt.Printf(\"\"Controller [%s] | Binding: %s\\n\", typ, binding.String())\n\n\t\tif len(binding.Input.StructFieldIndex) == 0 {\n\t\t\t// set correctly the input's field index and name.\n\t\t\tf := fields[binding.Input.Index]\n\t\t\tbinding.Input.StructFieldIndex = f.Index\n\t\t\tbinding.Input.StructFieldName = f.Name\n\t\t}\n\n\t\t// fmt.Printf(\"Controller [%s] | binding Index: %v | binding Type: %s\\n\", typ, binding.Input.StructFieldIndex, binding.Input.Type)\n\t\t// fmt.Printf(\"Controller [%s] Set [%s] to struct field index: %v\\n\", typ.String(), binding.Input.Type.String(), binding.Input.StructFieldIndex)\n\t}\n\n\treturn\n}\n\nfunc getStaticInputs(bindings []*binding, numIn int) []reflect.Value {\n\tinputs := make([]reflect.Value, numIn)\n\tfor _, b := range bindings {\n\t\tif d := b.Dependency; d != nil && d.Static {\n\t\t\tinputs[b.Input.Index], _ = d.Handle(nil, nil)\n\t\t}\n\t}\n\n\treturn inputs\n}\n\n/*\n\tBuiltin dynamic bindings.\n*/\n\nfunc paramBinding(index, paramIndex int, typ reflect.Type) *binding {\n\treturn &binding{\n\t\tDependency: &Dependency{Handle: paramDependencyHandler(paramIndex), DestType: typ, Source: getSource()},\n\t\tInput:      newInput(typ, index, nil),\n\t}\n}\n\nfunc paramDependencyHandler(paramIndex int) DependencyHandler {\n\treturn func(ctx *context.Context, input *Input) (reflect.Value, error) {\n\t\tif ctx.Params().Len() <= paramIndex {\n\t\t\treturn emptyValue, ErrSeeOther\n\t\t}\n\n\t\treturn reflect.ValueOf(ctx.Params().Store[paramIndex].ValueRaw), nil\n\t}\n}\n\n// registered if input parameters are more than matched dependencies.\n// It binds an input to a request body based on the request content-type header\n// (JSON, Protobuf, Msgpack, XML, YAML, Query, Form).\nfunc payloadBinding(index int, typ reflect.Type) *binding {\n\t// fmt.Printf(\"Register payload binding for index: %d and type: %s\\n\", index, typ.String())\n\n\treturn &binding{\n\t\tDependency: &Dependency{\n\t\t\tHandle: func(ctx *context.Context, input *Input) (newValue reflect.Value, err error) {\n\t\t\t\twasPtr := input.Type.Kind() == reflect.Ptr\n\n\t\t\t\tif serveDepsV := ctx.Values().Get(context.DependenciesContextKey); serveDepsV != nil {\n\t\t\t\t\tif serveDeps, ok := serveDepsV.(context.DependenciesMap); ok {\n\t\t\t\t\t\tif newValue, ok = serveDeps[typ]; ok {\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif input.Type.Kind() == reflect.Slice {\n\t\t\t\t\tnewValue = reflect.New(reflect.SliceOf(indirectType(input.Type)))\n\t\t\t\t} else {\n\t\t\t\t\tnewValue = reflect.New(indirectType(input.Type))\n\t\t\t\t}\n\n\t\t\t\tptr := newValue.Interface()\n\t\t\t\terr = ctx.ReadBody(ptr)\n\n\t\t\t\tif !wasPtr {\n\t\t\t\t\tnewValue = newValue.Elem()\n\t\t\t\t}\n\n\t\t\t\treturn\n\t\t\t},\n\t\t\tSource: getSource(),\n\t\t},\n\t\tInput: newInput(typ, index, nil),\n\t}\n\n}\n"
  },
  {
    "path": "hero/binding_test.go",
    "content": "package hero\n\nimport (\n\tstdContext \"context\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/kataras/golog\"\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/sessions\"\n)\n\nvar (\n\tstdContextTyp = reflect.TypeOf((*stdContext.Context)(nil)).Elem()\n\tsessionTyp    = reflect.TypeOf((*sessions.Session)(nil))\n\ttimeTyp       = reflect.TypeOf((*time.Time)(nil)).Elem()\n\tmapStringsTyp = reflect.TypeOf(map[string][]string{})\n)\n\nfunc contextBinding(index int) *binding {\n\treturn &binding{\n\t\tDependency: BuiltinDependencies[0],\n\t\tInput:      &Input{Type: BuiltinDependencies[0].DestType, Index: index},\n\t}\n}\n\nfunc TestGetBindingsForFunc(t *testing.T) {\n\ttype (\n\t\ttestResponse struct {\n\t\t\tName string `json:\"name\"`\n\t\t}\n\n\t\ttestRequest struct {\n\t\t\tEmail string `json:\"email\"`\n\t\t}\n\n\t\ttestRequest2 struct {\n\t\t\t// normally a body can't have two requests but let's test it.\n\t\t\tAge int `json:\"age\"`\n\t\t}\n\t)\n\n\tvar testRequestTyp = reflect.TypeOf(testRequest{})\n\n\tvar deps = []*Dependency{\n\t\tNewDependency(func(ctx *context.Context) testRequest { return testRequest{Email: \"should be ignored\"} }),\n\t\tNewDependency(42),\n\t\tNewDependency(func(ctx *context.Context) (v testRequest, err error) {\n\t\t\terr = ctx.ReadJSON(&v)\n\t\t\treturn\n\t\t}),\n\t\tNewDependency(\"if two strings requested this should be the last one\"),\n\t\tNewDependency(\"should not be ignored when requested\"),\n\n\t\t// Dependencies like these should always be registered last.\n\t\tNewDependency(func(ctx *context.Context, input *Input) (newValue reflect.Value, err error) {\n\t\t\twasPtr := input.Type.Kind() == reflect.Ptr\n\n\t\t\tnewValue = reflect.New(indirectType(input.Type))\n\t\t\tptr := newValue.Interface()\n\t\t\terr = ctx.ReadJSON(ptr)\n\n\t\t\tif !wasPtr {\n\t\t\t\tnewValue = newValue.Elem()\n\t\t\t}\n\n\t\t\treturn newValue, err\n\t\t}),\n\t}\n\n\tvar tests = []struct {\n\t\tFunc     any\n\t\tExpected []*binding\n\t}{\n\t\t{ // 0\n\t\t\tFunc: func(ctx *context.Context) {\n\t\t\t\tctx.WriteString(\"t1\")\n\t\t\t},\n\t\t\tExpected: []*binding{contextBinding(0)},\n\t\t},\n\t\t{ // 1\n\t\t\tFunc: func(ctx *context.Context) error {\n\t\t\t\treturn fmt.Errorf(\"err1\")\n\t\t\t},\n\t\t\tExpected: []*binding{contextBinding(0)},\n\t\t},\n\t\t{ // 2\n\t\t\tFunc: func(ctx *context.Context) testResponse {\n\t\t\t\treturn testResponse{Name: \"name\"}\n\t\t\t},\n\t\t\tExpected: []*binding{contextBinding(0)},\n\t\t},\n\t\t{ // 3\n\t\t\tFunc: func(in testRequest) (testResponse, error) {\n\t\t\t\treturn testResponse{Name: \"email of \" + in.Email}, nil\n\t\t\t},\n\t\t\tExpected: []*binding{{Dependency: deps[2], Input: &Input{Index: 0, Type: testRequestTyp}}},\n\t\t},\n\t\t{ // 4\n\t\t\tFunc: func(in testRequest) (testResponse, error) {\n\t\t\t\treturn testResponse{Name: \"not valid \"}, fmt.Errorf(\"invalid\")\n\t\t\t},\n\t\t\tExpected: []*binding{{Dependency: deps[2], Input: &Input{Index: 0, Type: testRequestTyp}}},\n\t\t},\n\t\t{ // 5\n\t\t\tFunc: func(ctx *context.Context, in testRequest) testResponse {\n\t\t\t\treturn testResponse{Name: \"(with ctx) email of \" + in.Email}\n\t\t\t},\n\t\t\tExpected: []*binding{contextBinding(0), {Dependency: deps[2], Input: &Input{Index: 1, Type: testRequestTyp}}},\n\t\t},\n\t\t{ // 6\n\t\t\tFunc: func(in testRequest, ctx *context.Context) testResponse { // reversed.\n\t\t\t\treturn testResponse{Name: \"(with ctx) email of \" + in.Email}\n\t\t\t},\n\t\t\tExpected: []*binding{{Dependency: deps[2], Input: &Input{Index: 0, Type: testRequestTyp}}, contextBinding(1)},\n\t\t},\n\t\t{ // 7\n\t\t\tFunc: func(in testRequest, ctx *context.Context, in2 string) testResponse { // reversed.\n\t\t\t\treturn testResponse{Name: \"(with ctx) email of \" + in.Email + \"and in2: \" + in2}\n\t\t\t},\n\t\t\tExpected: []*binding{\n\t\t\t\t{\n\t\t\t\t\tDependency: deps[2],\n\t\t\t\t\tInput:      &Input{Index: 0, Type: testRequestTyp},\n\t\t\t\t},\n\t\t\t\tcontextBinding(1),\n\t\t\t\t{\n\t\t\t\t\tDependency: deps[4],\n\t\t\t\t\tInput:      &Input{Index: 2, Type: reflect.TypeOf(\"\")},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{ // 8\n\t\t\tFunc: func(in testRequest, ctx *context.Context, in2, in3 string) testResponse { // reversed.\n\t\t\t\treturn testResponse{Name: \"(with ctx) email of \" + in.Email + \" | in2: \" + in2 + \" in3: \" + in3}\n\t\t\t},\n\t\t\tExpected: []*binding{\n\t\t\t\t{\n\t\t\t\t\tDependency: deps[2],\n\t\t\t\t\tInput:      &Input{Index: 0, Type: testRequestTyp},\n\t\t\t\t},\n\t\t\t\tcontextBinding(1),\n\t\t\t\t{\n\t\t\t\t\tDependency: deps[len(deps)-3],\n\t\t\t\t\tInput:      &Input{Index: 2, Type: reflect.TypeOf(\"\")},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tDependency: deps[len(deps)-2],\n\t\t\t\t\tInput:      &Input{Index: 3, Type: reflect.TypeOf(\"\")},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{ // 9\n\t\t\tFunc: func(ctx *context.Context, in testRequest, in2 testRequest2) testResponse {\n\t\t\t\treturn testResponse{Name: fmt.Sprintf(\"(with ctx) email of %s and in2.Age %d\", in.Email, in2.Age)}\n\t\t\t},\n\t\t\tExpected: []*binding{\n\t\t\t\tcontextBinding(0),\n\t\t\t\t{\n\t\t\t\t\tDependency: deps[2],\n\t\t\t\t\tInput:      &Input{Index: 1, Type: testRequestTyp},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tDependency: deps[len(deps)-1],\n\t\t\t\t\tInput:      &Input{Index: 2, Type: reflect.TypeOf(testRequest2{})},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{ // 10\n\t\t\tFunc: func() testResponse {\n\t\t\t\treturn testResponse{Name: \"empty in, one out\"}\n\t\t\t},\n\t\t\tExpected: nil,\n\t\t},\n\t\t{ // 1\n\t\t\tFunc: func(userID string, age int) testResponse {\n\t\t\t\treturn testResponse{Name: \"in from path parameters\"}\n\t\t\t},\n\t\t\tExpected: []*binding{\n\t\t\t\tparamBinding(0, 0, reflect.TypeOf(\"\")),\n\t\t\t\tparamBinding(1, 1, reflect.TypeOf(0)),\n\t\t\t},\n\t\t},\n\t\t// test std context, session, time, request, response writer and headers  bindings.\n\t\t{ // 12\n\t\t\tFunc: func(stdContext.Context, *sessions.Session, *golog.Logger, time.Time, *http.Request, http.ResponseWriter, http.Header) testResponse {\n\t\t\t\treturn testResponse{\"builtin deps\"}\n\t\t\t},\n\t\t\tExpected: []*binding{\n\t\t\t\t{\n\t\t\t\t\tDependency: NewDependency(BuiltinDependencies[1]),\n\t\t\t\t\tInput:      &Input{Index: 0, Type: stdContextTyp},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tDependency: NewDependency(BuiltinDependencies[2]),\n\t\t\t\t\tInput:      &Input{Index: 1, Type: sessionTyp},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tDependency: NewDependency(BuiltinDependencies[3]),\n\t\t\t\t\tInput:      &Input{Index: 2, Type: BuiltinDependencies[3].DestType},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tDependency: NewDependency(BuiltinDependencies[4]),\n\t\t\t\t\tInput:      &Input{Index: 3, Type: timeTyp},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tDependency: NewDependency(BuiltinDependencies[5]),\n\t\t\t\t\tInput:      &Input{Index: 4, Type: BuiltinDependencies[5].DestType},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tDependency: NewDependency(BuiltinDependencies[6]),\n\t\t\t\t\tInput:      &Input{Index: 5, Type: BuiltinDependencies[6].DestType},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tDependency: NewDependency(BuiltinDependencies[7]),\n\t\t\t\t\tInput:      &Input{Index: 6, Type: BuiltinDependencies[7].DestType},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t// test explicitly of http.Header and its underline type map[string][]string which\n\t\t// but shouldn't be binded to request headers because of the (.Explicitly()), instead\n\t\t// the map should be binded to our last of \"deps\" which is is a dynamic functions reads from request body's JSON\n\t\t// (it's a builtin dependency as well but we declared it to test user dynamic dependencies too).\n\t\t{ // 13\n\t\t\tFunc: func(http.Header) testResponse {\n\t\t\t\treturn testResponse{\"builtin http.Header dep\"}\n\t\t\t},\n\t\t\tExpected: []*binding{\n\t\t\t\t{\n\t\t\t\t\tDependency: NewDependency(BuiltinDependencies[7]),\n\t\t\t\t\tInput:      &Input{Index: 0, Type: BuiltinDependencies[7].DestType},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{ // 14\n\t\t\tFunc: func(map[string][]string) testResponse {\n\t\t\t\treturn testResponse{\"not dep registered except the dynamic one\"}\n\t\t\t},\n\t\t\tExpected: []*binding{\n\t\t\t\t{\n\t\t\t\t\tDependency: deps[len(deps)-1],\n\t\t\t\t\tInput:      &Input{Index: 0, Type: mapStringsTyp},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{ // 15\n\t\t\tFunc: func(http.Header, map[string][]string) testResponse {\n\t\t\t\treturn testResponse{}\n\t\t\t},\n\t\t\tExpected: []*binding{ // only http.Header should be binded, we don't have map[string][]string registered.\n\t\t\t\t{\n\t\t\t\t\tDependency: NewDependency(BuiltinDependencies[7]),\n\t\t\t\t\tInput:      &Input{Index: 0, Type: BuiltinDependencies[7].DestType},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tDependency: deps[len(deps)-1],\n\t\t\t\t\tInput:      &Input{Index: 1, Type: mapStringsTyp},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tc := New()\n\tfor _, dependency := range deps {\n\t\tc.Register(dependency)\n\t}\n\n\tfor i, tt := range tests {\n\t\tbindings := getBindingsForFunc(reflect.ValueOf(tt.Func), c.Dependencies, c.DisablePayloadAutoBinding, 0)\n\n\t\tif expected, got := len(tt.Expected), len(bindings); expected != got {\n\t\t\tt.Fatalf(\"[%d] expected bindings length to be: %d but got: %d of: %s\", i, expected, got, bindings)\n\t\t}\n\n\t\tfor j, b := range bindings {\n\t\t\tif b == nil {\n\t\t\t\tt.Fatalf(\"[%d:%d] binding is nil!\", i, j)\n\t\t\t}\n\n\t\t\tif tt.Expected[j] == nil {\n\t\t\t\tt.Fatalf(\"[%d:%d] expected dependency was not found!\", i, j)\n\t\t\t}\n\n\t\t\t// if expected := tt.Expected[j]; !expected.Equal(b) {\n\t\t\t// \tt.Fatalf(\"[%d:%d] got unexpected binding:\\n%s\", i, j, spew.Sdump(expected, b))\n\t\t\t// }\n\n\t\t\tif expected := tt.Expected[j]; !expected.Equal(b) {\n\t\t\t\tt.Fatalf(\"[%d:%d] expected binding:\\n%s\\nbut got:\\n%s\", i, j, expected, b)\n\t\t\t}\n\t\t}\n\t}\n}\n\ntype (\n\tservice interface {\n\t\tString() string\n\t}\n\tserviceImpl struct{}\n)\n\nvar serviceTyp = reflect.TypeOf((*service)(nil)).Elem()\n\nfunc (s *serviceImpl) String() string {\n\treturn \"service\"\n}\n\nfunc TestBindingsForStruct(t *testing.T) {\n\ttype (\n\t\tcontroller struct {\n\t\t\tName    string\n\t\t\tService service\n\t\t}\n\n\t\tembedded1 struct {\n\t\t\tAge int\n\t\t}\n\n\t\tembedded2 struct {\n\t\t\tNow time.Time\n\t\t}\n\n\t\tEmbedded3 struct {\n\t\t\tAge int\n\t\t}\n\n\t\tEmbedded4 struct {\n\t\t\tNow time.Time\n\t\t}\n\n\t\tcontrollerEmbeddingExported struct {\n\t\t\tEmbedded3\n\t\t\tEmbedded4\n\t\t}\n\n\t\tcontrollerEmbeddingUnexported struct {\n\t\t\tembedded1\n\t\t\tembedded2\n\t\t}\n\n\t\tcontroller2 struct {\n\t\t\tEmb1 embedded1\n\t\t\tEmb2 embedded2\n\t\t}\n\n\t\tcontroller3 struct {\n\t\t\tEmb1 embedded1\n\t\t\temb2 embedded2 // unused\n\t\t}\n\t)\n\n\tvar deps = []*Dependency{\n\t\tNewDependency(\"name\"),\n\t\tNewDependency(new(serviceImpl)),\n\t}\n\n\tvar depsForAnonymousEmbedded = []*Dependency{\n\t\tNewDependency(42),\n\t\tNewDependency(time.Now()),\n\t}\n\n\tvar depsForFieldsOfStruct = []*Dependency{\n\t\tNewDependency(embedded1{Age: 42}),\n\t\tNewDependency(embedded2{time.Now()}),\n\t}\n\n\tvar depsInterfaces = []*Dependency{\n\t\tNewDependency(func(ctx *context.Context) any {\n\t\t\treturn \"name\"\n\t\t}),\n\t}\n\n\tvar autoBindings = []*binding{\n\t\tpayloadBinding(0, reflect.TypeOf(embedded1{})),\n\t\tpayloadBinding(1, reflect.TypeOf(embedded2{})),\n\t}\n\n\tfor _, b := range autoBindings {\n\t\tb.Input.StructFieldIndex = []int{b.Input.Index}\n\t}\n\n\tvar tests = []struct {\n\t\tValue      any\n\t\tRegistered []*Dependency\n\t\tExpected   []*binding\n\t}{\n\t\t{ // 0.\n\t\t\tValue:      &controller{},\n\t\t\tRegistered: deps,\n\t\t\tExpected: []*binding{\n\t\t\t\t{\n\t\t\t\t\tDependency: deps[0],\n\t\t\t\t\tInput:      &Input{Index: 0, StructFieldIndex: []int{0}, Type: reflect.TypeOf(\"\")},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tDependency: deps[1],\n\t\t\t\t\tInput:      &Input{Index: 1, StructFieldIndex: []int{1}, Type: serviceTyp},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t// 1. test controller with pre-defined variables.\n\t\t{\n\t\t\tValue:    &controller{Name: \"name_struct\", Service: new(serviceImpl)},\n\t\t\tExpected: nil,\n\t\t},\n\t\t// 2. test controller with pre-defined variables and other deps with the exact order and value\n\t\t// (deps from non zero values should be not registerded, if not the Dependency:name_struct will fail for sure).\n\t\t{\n\t\t\tValue:      &controller{Name: \"name_struct\", Service: new(serviceImpl)},\n\t\t\tRegistered: deps,\n\t\t\tExpected:   nil,\n\t\t},\n\t\t// 3. test embedded structs with anonymous and exported.\n\t\t{\n\t\t\tValue:      &controllerEmbeddingExported{},\n\t\t\tRegistered: depsForAnonymousEmbedded,\n\t\t\tExpected: []*binding{\n\t\t\t\t{\n\t\t\t\t\tDependency: depsForAnonymousEmbedded[0],\n\t\t\t\t\tInput:      &Input{Index: 0, StructFieldIndex: []int{0, 0}, Type: reflect.TypeOf(0)},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tDependency: depsForAnonymousEmbedded[1],\n\t\t\t\t\tInput:      &Input{Index: 1, StructFieldIndex: []int{1, 0}, Type: reflect.TypeOf(time.Time{})},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t// 4. test for anonymous but not exported (should still be 2, unexported structs are binded).\n\t\t{\n\t\t\tValue:      &controllerEmbeddingUnexported{},\n\t\t\tRegistered: depsForAnonymousEmbedded,\n\t\t\tExpected: []*binding{\n\t\t\t\t{\n\t\t\t\t\tDependency: depsForAnonymousEmbedded[0],\n\t\t\t\t\tInput:      &Input{Index: 0, StructFieldIndex: []int{0, 0}, Type: reflect.TypeOf(0)},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tDependency: depsForAnonymousEmbedded[1],\n\t\t\t\t\tInput:      &Input{Index: 1, StructFieldIndex: []int{1, 0}, Type: reflect.TypeOf(time.Time{})},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t// 5. test for auto-bindings with zero registered.\n\t\t{\n\t\t\tValue:      &controller2{},\n\t\t\tRegistered: nil,\n\t\t\tExpected:   autoBindings,\n\t\t},\n\t\t// 6. test for embedded with named fields which should NOT contain any registered deps\n\t\t// except the two auto-bindings for structs,\n\t\t{\n\t\t\tValue:      &controller2{},\n\t\t\tRegistered: depsForAnonymousEmbedded,\n\t\t\tExpected:   autoBindings,\n\t\t}, // 7. and only embedded struct's fields are readen, otherwise we expect the struct to be a dependency.\n\t\t{\n\t\t\tValue:      &controller2{},\n\t\t\tRegistered: depsForFieldsOfStruct,\n\t\t\tExpected: []*binding{\n\t\t\t\t{\n\t\t\t\t\tDependency: depsForFieldsOfStruct[0],\n\t\t\t\t\tInput:      &Input{Index: 0, StructFieldIndex: []int{0}, Type: reflect.TypeOf(embedded1{})},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tDependency: depsForFieldsOfStruct[1],\n\t\t\t\t\tInput:      &Input{Index: 1, StructFieldIndex: []int{1}, Type: reflect.TypeOf(embedded2{})},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t// 8. test one exported and other not exported.\n\t\t{\n\t\t\tValue:      &controller3{},\n\t\t\tRegistered: []*Dependency{depsForFieldsOfStruct[0]},\n\t\t\tExpected: []*binding{\n\t\t\t\t{\n\t\t\t\t\tDependency: depsForFieldsOfStruct[0],\n\t\t\t\t\tInput:      &Input{Index: 0, StructFieldIndex: []int{0}, Type: reflect.TypeOf(embedded1{})},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t// 9. test same as the above but by registering all dependencies.\n\t\t{\n\t\t\tValue:      &controller3{},\n\t\t\tRegistered: depsForFieldsOfStruct,\n\t\t\tExpected: []*binding{\n\t\t\t\t{\n\t\t\t\t\tDependency: depsForFieldsOfStruct[0],\n\t\t\t\t\tInput:      &Input{Index: 0, StructFieldIndex: []int{0}, Type: reflect.TypeOf(embedded1{})},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t// 10. test bind an any.\n\t\t{\n\t\t\tValue:      &controller{},\n\t\t\tRegistered: depsInterfaces,\n\t\t\tExpected: []*binding{\n\t\t\t\t{\n\t\t\t\t\tDependency: depsInterfaces[0],\n\t\t\t\t\tInput:      &Input{Index: 0, StructFieldIndex: []int{0}, Type: reflect.TypeOf(\"\")},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor i, tt := range tests {\n\t\tbindings := getBindingsForStruct(reflect.ValueOf(tt.Value), tt.Registered, false, false, false, DefaultDependencyMatcher, 0, nil)\n\n\t\tif expected, got := len(tt.Expected), len(bindings); expected != got {\n\t\t\tt.Logf(\"[%d] expected bindings length to be: %d but got: %d:\\n\", i, expected, got)\n\t\t\tfor _, b := range bindings {\n\t\t\t\tt.Logf(\"\\t%s\\n\", b)\n\t\t\t}\n\t\t\tt.FailNow()\n\t\t}\n\n\t\tfor j, b := range bindings {\n\t\t\tif tt.Expected[j] == nil {\n\t\t\t\tt.Fatalf(\"[%d:%d] expected dependency was not found!\", i, j)\n\t\t\t}\n\n\t\t\tif expected := tt.Expected[j]; !expected.Equal(b) {\n\t\t\t\tt.Fatalf(\"[%d:%d] expected binding:\\n%s\\nbut got:\\n%s\", i, j, expected, b)\n\t\t\t}\n\t\t}\n\t}\n\n}\n\nfunc TestBindingsForStructMarkExportedFieldsAsRequred(t *testing.T) {\n\ttype (\n\t\tEmbedded struct {\n\t\t\tVal string\n\t\t}\n\n\t\tcontroller struct {\n\t\t\tMyService service\n\t\t\tEmbedded  *Embedded\n\t\t}\n\t)\n\n\tdependencies := []*Dependency{\n\t\tNewDependency(&Embedded{\"test\"}),\n\t\tNewDependency(&serviceImpl{}),\n\t}\n\n\t// should panic if fail.\n\t_ = getBindingsForStruct(reflect.ValueOf(new(controller)), dependencies, true, true, false, DefaultDependencyMatcher, 0, nil)\n}\n"
  },
  {
    "path": "hero/container.go",
    "content": "package hero\n\nimport (\n\tstdContext \"context\"\n\t\"errors\"\n\t\"net\"\n\t\"net/http\"\n\t\"reflect\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/sessions\"\n\n\t\"github.com/kataras/golog\"\n)\n\n// Default is the default container value which can be used for dependencies share.\nvar Default = New().WithLogger(golog.Default)\n\n// Container contains and delivers the Dependencies that will be binded\n// to the controller(s) or handler(s) that can be created\n// using the Container's `Handler` and `Struct` methods.\n//\n// This is not exported for being used by everyone, use it only when you want\n// to share containers between multi mvc.go#Application\n// or make custom hero handlers that can be used on the standard\n// iris' APIBuilder.\n//\n// For a more high-level structure please take a look at the \"mvc.go#Application\".\ntype Container struct {\n\t// Optional Logger to report dependencies and matched bindings\n\t// per struct, function and method.\n\t// By default it is set by the Party creator of this Container.\n\tLogger *golog.Logger\n\t// Sorter specifies how the inputs should be sorted before binded.\n\t// Defaults to sort by \"thinnest\" target empty interface.\n\tSorter Sorter\n\t// The dependencies entries.\n\tDependencies []*Dependency\n\n\t// MarkExportedFieldsAsRequired reports whether all struct's fields\n\t// MUST be binded to a dependency from the `Dependencies` list field.\n\t// In-short, if it is set to true and if at least one exported field\n\t// of a struct is not binded to a dependency then\n\t// the entire application will exit with a panic message before server startup.\n\tMarkExportedFieldsAsRequired bool\n\t// DisablePayloadAutoBinding reports whether\n\t// a function's parameter or struct's field of struct type\n\t// should not be binded automatically to the request body (e.g. JSON)\n\t// if a dependency for that type is missing.\n\t// By default the binder will bind structs to request body,\n\t// set to true to disable that kind of behavior.\n\tDisablePayloadAutoBinding bool\n\n\t// DisableStructDynamicBindings if true panics on struct handler (controller)\n\t// if at least one input binding depends on the request and not in a static structure.\n\tDisableStructDynamicBindings bool\n\n\t// StructDependents if true then the Container will try to resolve\n\t// the fields of a struct value, if any, when it's a dependent struct value\n\t// based on the previous registered dependencies.\n\t//\n\t// Defaults to false.\n\tEnableStructDependents bool // this can be renamed to IndirectDependencies?.\n\n\t// DependencyMatcher holds the function that compares equality between\n\t// a dependency with an input. Defaults to DefaultMatchDependencyFunc.\n\tDependencyMatcher DependencyMatcher\n\t// GetErrorHandler should return a valid `ErrorHandler` to handle bindings AND handler dispatch errors.\n\t// Defaults to a functon which returns the `DefaultErrorHandler`.\n\tGetErrorHandler func(*context.Context) ErrorHandler // cannot be nil.\n\t// Reports contains an ordered list of information about bindings for further analysys and testing.\n\tReports []*Report\n\n\t// resultHandlers is a list of functions that serve the return struct value of a function handler.\n\t// Defaults to \"defaultResultHandler\" but it can be overridden.\n\tresultHandlers []func(next ResultHandler) ResultHandler\n}\n\n// A Report holds meta information about dependency sources and target values per package,\n// struct, struct's fields, struct's method, package-level function or closure.\n// E.g. main -> (*UserController) -> HandleHTTPError.\ntype Report struct {\n\t// The name is the last part of the name of a struct or its methods or a function.\n\t// Each name is splited by its package.struct.field or package.funcName or package.func.inlineFunc.\n\tName string\n\t// If it's a struct or package or function\n\t// then it contains children reports of each one of its methods or input parameters\n\t// respectfully.\n\tReports []*Report\n\n\tParent  *Report\n\tEntries []ReportEntry\n}\n\n// A ReportEntry holds the information about a binding.\ntype ReportEntry struct {\n\tInputPosition   int          // struct field position or parameter position.\n\tInputFieldName  string       // if it's a struct field, then this is its type name (we can't get param names).\n\tInputFieldType  reflect.Type // the input's type.\n\tDependencyValue any          // the dependency value binded to that InputPosition of Name.\n\tDependencyFile  string       // the file\n\tDependencyLine  int          // and line number of the dependency's value.\n\tStatic          bool\n}\n\nfunc (r *Report) fill(bindings []*binding) {\n\tfor _, b := range bindings {\n\t\tinputFieldName := b.Input.StructFieldName\n\t\tif inputFieldName == \"\" {\n\t\t\t// it's not a struct field, then type.\n\t\t\tinputFieldName = b.Input.Type.String()\n\t\t}\n\t\t// remove only the main one prefix.\n\t\tinputFieldName = strings.TrimPrefix(inputFieldName, \"main.\")\n\n\t\tfieldName := inputFieldName\n\t\tswitch fieldName {\n\t\tcase \"*context.Context\":\n\t\t\tinputFieldName = strings.Replace(inputFieldName, \"*context\", \"iris\", 1)\n\t\tcase \"hero.Code\", \"hero.Result\", \"hero.View\", \"hero.Response\":\n\t\t\tinputFieldName = strings.Replace(inputFieldName, \"hero\", \"mvc\", 1)\n\t\t}\n\n\t\tentry := ReportEntry{\n\t\t\tInputPosition:  b.Input.Index,\n\t\t\tInputFieldName: inputFieldName,\n\t\t\tInputFieldType: b.Input.Type,\n\n\t\t\tDependencyValue: b.Dependency.OriginalValue,\n\t\t\tDependencyFile:  b.Dependency.Source.File,\n\t\t\tDependencyLine:  b.Dependency.Source.Line,\n\t\t\tStatic:          b.Dependency.Static,\n\t\t}\n\n\t\tr.Entries = append(r.Entries, entry)\n\t}\n}\n\n// fillReport adds a report to the Reports field.\nfunc (c *Container) fillReport(fullName string, bindings []*binding) {\n\t// r := c.getReport(fullName)\n\n\tr := &Report{\n\t\tName: fullName,\n\t}\n\tr.fill(bindings)\n\tc.Reports = append(c.Reports, r)\n}\n\n// BuiltinDependencies is a list of builtin dependencies that are added on Container's initilization.\n// Contains the iris context, standard context, iris sessions and time dependencies.\nvar BuiltinDependencies = []*Dependency{\n\t// iris context dependency.\n\tnewDependency(func(ctx *context.Context) *context.Context { return ctx }, true, false, nil).Explicitly(),\n\t// standard context dependency.\n\tnewDependency(func(ctx *context.Context) stdContext.Context {\n\t\treturn ctx.Request().Context()\n\t}, true, false, nil).Explicitly(),\n\t// iris session dependency.\n\tnewDependency(func(ctx *context.Context) *sessions.Session {\n\t\tsession := sessions.Get(ctx)\n\t\tif session == nil {\n\t\t\tctx.Application().Logger().Debugf(\"binding: session is nil\\nMaybe inside HandleHTTPError? Register it with app.UseRouter(sess.Handler()) to fix it\")\n\t\t\t// let's don't panic here and let the application continue, now we support\n\t\t\t// not matched routes inside the controller through HandleHTTPError,\n\t\t\t// so each dependency can check if session was not nil or just use `UseRouter` instead of `Use`\n\t\t\t// to register the sessions middleware.\n\t\t}\n\n\t\treturn session\n\t}, true, false, nil).Explicitly(),\n\t// application's logger.\n\tnewDependency(func(ctx *context.Context) *golog.Logger {\n\t\treturn ctx.Application().Logger()\n\t}, true, false, nil).Explicitly(),\n\t// time.Time to time.Now dependency.\n\tnewDependency(func(ctx *context.Context) time.Time {\n\t\treturn time.Now()\n\t}, true, false, nil).Explicitly(),\n\t// standard http Request dependency.\n\tnewDependency(func(ctx *context.Context) *http.Request {\n\t\treturn ctx.Request()\n\t}, true, false, nil).Explicitly(),\n\t// standard http ResponseWriter dependency.\n\tnewDependency(func(ctx *context.Context) http.ResponseWriter {\n\t\treturn ctx.ResponseWriter()\n\t}, true, false, nil).Explicitly(),\n\t// http headers dependency.\n\tnewDependency(func(ctx *context.Context) http.Header {\n\t\treturn ctx.Request().Header\n\t}, true, false, nil).Explicitly(),\n\t// Client IP.\n\tnewDependency(func(ctx *context.Context) net.IP {\n\t\treturn net.ParseIP(ctx.RemoteAddr())\n\t}, true, false, nil).Explicitly(),\n\t// Status Code (special type for MVC HTTP Error handler to not conflict with path parameters)\n\tnewDependency(func(ctx *context.Context) Code {\n\t\treturn Code(ctx.GetStatusCode())\n\t}, true, false, nil).Explicitly(),\n\t// Context Error. May be nil\n\tnewDependency(func(ctx *context.Context) Err {\n\t\terr := ctx.GetErr()\n\t\tif err == nil {\n\t\t\treturn nil\n\t\t}\n\t\treturn err\n\t}, true, false, nil).Explicitly(),\n\t// Context User, e.g. from basic authentication.\n\tnewDependency(func(ctx *context.Context) context.User {\n\t\tu := ctx.User()\n\t\tif u == nil {\n\t\t\treturn nil\n\t\t}\n\n\t\treturn u\n\t}, true, false, nil),\n\t// payload and param bindings are dynamically allocated and declared at the end of the `binding` source file.\n}\n\n// New returns a new Container, a container for dependencies and a factory\n// for handlers and controllers, this is used internally by the `mvc#Application` structure.\n// Please take a look at the structure's documentation for more information.\nfunc New(dependencies ...any) *Container {\n\tdeps := make([]*Dependency, len(BuiltinDependencies))\n\tcopy(deps, BuiltinDependencies)\n\n\tc := &Container{\n\t\tSorter:       sortByNumMethods,\n\t\tDependencies: deps,\n\t\tGetErrorHandler: func(*context.Context) ErrorHandler {\n\t\t\treturn DefaultErrorHandler\n\t\t},\n\t\tDependencyMatcher: DefaultDependencyMatcher,\n\t}\n\n\tfor _, dependency := range dependencies {\n\t\tc.Register(dependency)\n\t}\n\n\treturn c\n}\n\n// WithLogger injects a logger to use to debug dependencies and bindings.\nfunc (c *Container) WithLogger(logger *golog.Logger) *Container {\n\tc.Logger = logger\n\treturn c\n}\n\n// Clone returns a new cloned container.\n// It copies the ErrorHandler, Dependencies and all Options from \"c\" receiver.\nfunc (c *Container) Clone() *Container {\n\tcloned := New()\n\tcloned.Logger = c.Logger\n\tcloned.GetErrorHandler = c.GetErrorHandler\n\tcloned.Sorter = c.Sorter\n\tcloned.DependencyMatcher = c.DependencyMatcher\n\tclonedDeps := make([]*Dependency, len(c.Dependencies))\n\tcopy(clonedDeps, c.Dependencies)\n\tcloned.Dependencies = clonedDeps\n\tcloned.DisablePayloadAutoBinding = c.DisablePayloadAutoBinding\n\tcloned.DisableStructDynamicBindings = c.DisableStructDynamicBindings\n\tcloned.EnableStructDependents = c.EnableStructDependents\n\tcloned.MarkExportedFieldsAsRequired = c.MarkExportedFieldsAsRequired\n\tcloned.resultHandlers = c.resultHandlers\n\t// Reports are not cloned.\n\treturn cloned\n}\n\n// Register adds a dependency.\n// The value can be a single struct value-instance or a function\n// which has one input and one output, that output type\n// will be binded to the handler's input argument, if matching.\n//\n// Usage:\n// - Register(loggerService{prefix: \"dev\"})\n// - Register(func(ctx iris.Context) User {...})\n// - Register(func(User) OtherResponse {...})\nfunc Register(dependency any) *Dependency {\n\treturn Default.Register(dependency)\n}\n\n// Register adds a dependency.\n// The value can be a single struct value or a function.\n// Follow the rules:\n// * <T>{structValue}\n// * func(accepts <T>)                                 returns <D> or (<D>, error)\n// * func(accepts iris.Context)                        returns <D> or (<D>, error)\n// * func(accepts1 iris.Context, accepts2 *hero.Input) returns <D> or (<D>, error)\n//\n// A Dependency can accept a previous registered dependency and return a new one or the same updated.\n// * func(accepts1 <D>, accepts2 <T>)                  returns <E> or (<E>, error) or error\n// * func(acceptsPathParameter1 string, id uint64)     returns <T> or (<T>, error)\n//\n// Usage:\n//\n// - Register(loggerService{prefix: \"dev\"})\n// - Register(func(ctx iris.Context) User {...})\n// - Register(func(User) OtherResponse {...})\nfunc (c *Container) Register(dependency any) *Dependency {\n\td := newDependency(dependency, c.DisablePayloadAutoBinding, c.EnableStructDependents, c.DependencyMatcher, c.Dependencies...)\n\tif d.DestType == nil {\n\t\t// prepend the dynamic dependency so it will be tried at the end\n\t\t// (we don't care about performance here, design-time)\n\t\tc.Dependencies = append([]*Dependency{d}, c.Dependencies...)\n\t} else {\n\t\tc.Dependencies = append(c.Dependencies, d)\n\t}\n\n\treturn d\n}\n\n// UseResultHandler adds a result handler to the Container.\n// A result handler can be used to inject the returned struct value\n// from a request handler or to replace the default renderer.\nfunc (c *Container) UseResultHandler(handler func(next ResultHandler) ResultHandler) *Container {\n\tc.resultHandlers = append(c.resultHandlers, handler)\n\treturn c\n}\n\n// Handler accepts a \"handler\" function which can accept any input arguments that match\n// with the Container's `Dependencies` and any output result; like string, int (string,int),\n// custom structs, Result(View | Response) and anything you can imagine.\n// It returns a standard `iris/context.Handler` which can be used anywhere in an Iris Application,\n// as middleware or as simple route handler or subdomain's handler.\nfunc Handler(fn any) context.Handler {\n\treturn Default.Handler(fn)\n}\n\n// Handler accepts a handler \"fn\" function which can accept any input arguments that match\n// with the Container's `Dependencies` and any output result; like string, int (string,int),\n// custom structs, Result(View | Response) and more.\n// It returns a standard `iris/context.Handler` which can be used anywhere in an Iris Application,\n// as middleware or as simple route handler or subdomain's handler.\n//\n//\tfunc(...<T>) iris.Handler\n//\n// - if <T> are all static dependencies then\n// there is no reflection involved at serve-time.\n//\n//\tfunc(pathParameter string, ...<T>)\n//\n// - one or more path parameters (e.g. :uid, :string, :int, :path, :uint64)\n// are automatically binded to the first input Go standard types (string, int, uint64 and e.t.c.)\n//\n//\tfunc(<T>) error\n//\n// - if a function returns an error then this error's text is sent to the client automatically.\n//\n//\tfunc(<T>) <R>\n//\n// - The result of the function is a dependency too.\n// If <R> is a request-scope dependency (dynamic) then\n// this function will be called at every request.\n//\n//\tfunc(<T>) <R>\n//\n// - If <R> is static dependency (e.g. a database or a service) then its result\n// can be used as a static dependency to the next dependencies or to the controller/function itself.\nfunc (c *Container) Handler(fn any) context.Handler {\n\treturn c.HandlerWithParams(fn, 0)\n}\n\n// HandlerWithParams same as `Handler` but it can receive a total path parameters counts\n// to resolve coblex path parameters input dependencies.\nfunc (c *Container) HandlerWithParams(fn any, paramsCount int) context.Handler {\n\treturn makeHandler(fn, c, paramsCount)\n}\n\n// Struct accepts a pointer to a struct value and returns a structure which\n// contains bindings for the struct's fields and a method to\n// extract a Handler from this struct's method.\nfunc (c *Container) Struct(ptrValue any, partyParamsCount int) *Struct {\n\treturn makeStruct(ptrValue, c, partyParamsCount)\n}\n\n// ErrMissingDependency may returned only from the `Container.Inject` method\n// when not a matching dependency found for \"toPtr\".\nvar ErrMissingDependency = errors.New(\"missing dependency\")\n\n// Inject SHOULD only be used outside of HTTP handlers (performance is not priority for this method)\n// as it does not pre-calculate the available list of bindings for the \"toPtr\" and the registered dependencies.\n//\n// It sets a static-only matching dependency to the value of \"toPtr\".\n// The parameter \"toPtr\" SHOULD be a pointer to a value corresponding to a dependency,\n// like input parameter of a handler or field of a struct.\n//\n// If no matching dependency found, the `Inject` method returns an `ErrMissingDependency` and\n// \"toPtr\" keeps its original state (e.g. nil).\n//\n// Example Code:\n// c.Register(&LocalDatabase{...})\n// [...]\n// var db Database\n// err := c.Inject(&db)\nfunc (c *Container) Inject(toPtr any) error {\n\tval := reflect.Indirect(valueOf(toPtr))\n\ttyp := val.Type()\n\n\tfor _, d := range c.Dependencies {\n\t\tif d.Static && c.DependencyMatcher(d, typ) {\n\t\t\tv, err := d.Handle(nil, &Input{Type: typ})\n\t\t\tif err != nil {\n\t\t\t\tif err == ErrSeeOther {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tval.Set(v)\n\t\t\treturn nil\n\t\t}\n\t}\n\n\treturn ErrMissingDependency\n}\n"
  },
  {
    "path": "hero/container_test.go",
    "content": "package hero_test\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12\"\n\t. \"github.com/kataras/iris/v12/hero\"\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\nvar errTyp = reflect.TypeOf((*error)(nil)).Elem()\n\n// isError returns true if \"typ\" is type of `error`.\nfunc isError(typ reflect.Type) bool {\n\treturn typ.Implements(errTyp)\n}\n\ntype (\n\ttestInput struct {\n\t\tName string `json:\"name\"`\n\t}\n\n\ttestOutput struct {\n\t\tID   int    `json:\"id\"`\n\t\tName string `json:\"name\"`\n\t}\n)\n\nvar (\n\tfn = func(id int, in testInput) testOutput {\n\t\treturn testOutput{\n\t\t\tID:   id,\n\t\t\tName: in.Name,\n\t\t}\n\t}\n\n\texpectedOutput = testOutput{\n\t\tID:   42,\n\t\tName: \"makis\",\n\t}\n\n\tinput = testInput{\n\t\tName: \"makis\",\n\t}\n)\n\nfunc TestContainerHandler(t *testing.T) {\n\tapp := iris.New()\n\n\tc := New()\n\tpostHandler := c.Handler(fn)\n\tapp.Post(\"/{id:int}\", postHandler)\n\n\te := httptest.New(t, app)\n\tpath := fmt.Sprintf(\"/%d\", expectedOutput.ID)\n\te.POST(path).WithJSON(input).Expect().Status(httptest.StatusOK).JSON().IsEqual(expectedOutput)\n}\n\nfunc TestContainerInject(t *testing.T) {\n\tc := New()\n\n\texpected := testInput{Name: \"test\"}\n\tc.Register(expected)\n\tc.Register(&expected)\n\n\t// struct value.\n\tvar got1 testInput\n\tif err := c.Inject(&got1); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif !reflect.DeepEqual(expected, got1) {\n\t\tt.Fatalf(\"[struct value] expected: %#+v but got: %#+v\", expected, got1)\n\t}\n\n\t// ptr.\n\tvar got2 *testInput\n\tif err := c.Inject(&got2); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif !reflect.DeepEqual(&expected, got2) {\n\t\tt.Fatalf(\"[ptr] expected: %#+v but got: %#+v\", &expected, got2)\n\t}\n\n\t// register implementation, expect interface.\n\texpected3 := &testServiceImpl{prefix: \"prefix: \"}\n\tc.Register(expected3)\n\n\tvar got3 testService\n\tif err := c.Inject(&got3); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif !reflect.DeepEqual(expected3, got3) {\n\t\tt.Fatalf(\"[service] expected: %#+v but got: %#+v\", expected3, got3)\n\t}\n}\n\nfunc TestContainerUseResultHandler(t *testing.T) {\n\tc := New()\n\tresultLogger := func(next ResultHandler) ResultHandler {\n\t\treturn func(ctx iris.Context, v any) error {\n\t\t\tt.Logf(\"%#+v\", v)\n\t\t\treturn next(ctx, v)\n\t\t}\n\t}\n\n\tc.UseResultHandler(resultLogger)\n\texpectedResponse := map[string]any{\"injected\": true}\n\tc.UseResultHandler(func(next ResultHandler) ResultHandler {\n\t\treturn func(ctx iris.Context, v any) error {\n\t\t\treturn next(ctx, expectedResponse)\n\t\t}\n\t})\n\tc.UseResultHandler(resultLogger)\n\n\thandler := c.Handler(func(id int) testOutput {\n\t\treturn testOutput{\n\t\t\tID:   id,\n\t\t\tName: \"kataras\",\n\t\t}\n\t})\n\n\tapp := iris.New()\n\tapp.Get(\"/{id:int}\", handler)\n\n\te := httptest.New(t, app)\n\te.GET(\"/42\").Expect().Status(httptest.StatusOK).JSON().IsEqual(expectedResponse)\n}\n"
  },
  {
    "path": "hero/dependency.go",
    "content": "package hero\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\n\t\"reflect\"\n\n\t\"github.com/kataras/iris/v12/context\"\n)\n\ntype (\n\t// DependencyHandler is the native function declaration which implementors should return a value match to an input.\n\tDependencyHandler = func(ctx *context.Context, input *Input) (reflect.Value, error)\n\n\t// DependencyMatchFunc type alias describes dependency\n\t// match function with an input (field or parameter).\n\t//\n\t// See \"DependencyMatcher\" too, which can be used on a Container to\n\t// change the way dependencies are matched to inputs for all dependencies.\n\tDependencyMatchFunc = func(in reflect.Type) bool\n\n\t// Dependency describes the design-time dependency to be injected at serve time.\n\t// Contains its source location, the dependency handler (provider) itself and information\n\t// such as static for static struct values or explicit to bind a value to its exact DestType and not if just assignable to it (interfaces).\n\tDependency struct {\n\t\tOriginalValue any // Used for debugging and for logging only.\n\t\tSource        Source\n\t\tHandle        DependencyHandler\n\t\t// It's the exact type of return to bind, if declared to return <T>, otherwise nil.\n\t\tDestType reflect.Type\n\t\tStatic   bool\n\t\t// If true then input and dependency DestType should be indedical,\n\t\t// not just assiginable to each other.\n\t\t// Example of use case: depenendency like time.Time that we want to be bindable\n\t\t// only to time.Time inputs and not to a service with a `String() string` method that time.Time struct implements too.\n\t\tExplicit bool\n\n\t\t// Match holds the matcher. Defaults to the Container's one.\n\t\tMatch DependencyMatchFunc\n\n\t\t// StructDependents if true then the Container will try to resolve\n\t\t// the fields of a struct value, if any, when it's a dependent struct value\n\t\t// based on the previous registered dependencies.\n\t\t//\n\t\t// Defaults to false.\n\t\tStructDependents bool\n\t}\n)\n\n// Explicitly sets Explicit option to true.\n// See `Dependency.Explicit` field godoc for more.\n//\n// Returns itself.\nfunc (d *Dependency) Explicitly() *Dependency {\n\td.Explicit = true\n\treturn d\n}\n\n// EnableStructDependents sets StructDependents to true.\nfunc (d *Dependency) EnableStructDependents() *Dependency {\n\td.StructDependents = true\n\treturn d\n}\n\nfunc (d *Dependency) String() string {\n\tsourceLine := d.Source.String()\n\tval := d.OriginalValue\n\tif val == nil {\n\t\tval = d.Handle\n\t}\n\treturn fmt.Sprintf(\"%s (%#+v)\", sourceLine, val)\n}\n\n// NewDependency converts a function or a function which accepts other dependencies or static struct value to a *Dependency.\n//\n// See `Container.Handler` for more.\nfunc NewDependency(dependency any, funcDependencies ...*Dependency) *Dependency { // used only on tests.\n\treturn newDependency(dependency, false, false, nil, funcDependencies...)\n}\n\nfunc newDependency(\n\tdependency any,\n\tdisablePayloadAutoBinding bool,\n\tenableStructDependents bool,\n\tmatchDependency DependencyMatcher,\n\tfuncDependencies ...*Dependency,\n) *Dependency {\n\tif dependency == nil {\n\t\tpanic(fmt.Sprintf(\"bad value: nil: %T\", dependency))\n\t}\n\n\tif d, ok := dependency.(*Dependency); ok {\n\t\t// already a *Dependency, do not continue (and most importatly do not call resolveDependency) .\n\t\treturn d\n\t}\n\n\tv := valueOf(dependency)\n\tif !goodVal(v) {\n\t\tpanic(fmt.Sprintf(\"bad value: %#+v\", dependency))\n\t}\n\n\tif matchDependency == nil {\n\t\tmatchDependency = DefaultDependencyMatcher\n\t}\n\n\tdest := &Dependency{\n\t\tSource:           newSource(v),\n\t\tOriginalValue:    dependency,\n\t\tStructDependents: enableStructDependents,\n\t}\n\tdest.Match = ToDependencyMatchFunc(dest, matchDependency)\n\n\tif !resolveDependency(v, disablePayloadAutoBinding, dest, funcDependencies...) {\n\t\tpanic(fmt.Sprintf(\"bad value: could not resolve a dependency from: %#+v\", dependency))\n\t}\n\n\treturn dest\n}\n\n// DependencyResolver func(v reflect.Value, dest *Dependency) bool\n// Resolver     DependencyResolver\n\nfunc resolveDependency(v reflect.Value, disablePayloadAutoBinding bool, dest *Dependency, prevDependencies ...*Dependency) bool {\n\treturn fromDependencyHandler(v, dest) ||\n\t\tfromBuiltinValue(v, dest) ||\n\t\tfromStructValueOrDependentStructValue(v, disablePayloadAutoBinding, dest, prevDependencies) ||\n\t\tfromFunc(v, dest) ||\n\t\tlen(prevDependencies) > 0 && fromDependentFunc(v, disablePayloadAutoBinding, dest, prevDependencies)\n}\n\nfunc fromDependencyHandler(_ reflect.Value, dest *Dependency) bool {\n\t// It's already on the desired form, just return it.\n\tdependency := dest.OriginalValue\n\thandler, ok := dependency.(DependencyHandler)\n\tif !ok {\n\t\thandler, ok = dependency.(func(*context.Context, *Input) (reflect.Value, error))\n\t\tif !ok {\n\t\t\t// It's almost a handler, only the second `Input` argument is missing.\n\t\t\tif h, is := dependency.(func(*context.Context) (reflect.Value, error)); is {\n\t\t\t\thandler = func(ctx *context.Context, _ *Input) (reflect.Value, error) {\n\t\t\t\t\treturn h(ctx)\n\t\t\t\t}\n\t\t\t\tok = is\n\t\t\t}\n\t\t}\n\t}\n\tif !ok {\n\t\treturn false\n\t}\n\n\tdest.Handle = handler\n\treturn true\n}\n\nfunc fromBuiltinValue(v reflect.Value, dest *Dependency) bool {\n\tif !isBuiltinValue(v) {\n\t\treturn false\n\t}\n\n\t// It's just a static builtin value.\n\thandler := func(*context.Context, *Input) (reflect.Value, error) {\n\t\treturn v, nil\n\t}\n\n\tdest.DestType = v.Type()\n\tdest.Static = true\n\tdest.Handle = handler\n\treturn true\n}\n\nfunc fromStructValue(v reflect.Value, dest *Dependency) bool {\n\tif !isStructValue(v) {\n\t\treturn false\n\t}\n\n\t// It's just a static struct value.\n\thandler := func(*context.Context, *Input) (reflect.Value, error) {\n\t\treturn v, nil\n\t}\n\n\tdest.DestType = v.Type()\n\tdest.Static = true\n\tdest.Handle = handler\n\treturn true\n}\n\nfunc fromStructValueOrDependentStructValue(v reflect.Value, disablePayloadAutoBinding bool, dest *Dependency, prevDependencies []*Dependency) bool {\n\tif !isStructValue(v) {\n\t\t// It's not just a static struct value.\n\t\treturn false\n\t}\n\n\tif len(prevDependencies) == 0 || !dest.StructDependents { // As a non depedent struct.\n\t\t// We must make this check so we can avoid the auto-filling of\n\t\t// the dependencies from Iris builtin dependencies.\n\t\treturn fromStructValue(v, dest)\n\t}\n\n\t// Check if it's a builtin dependency (e.g an MVC Application (see mvc.go#newApp)),\n\t// if it's and registered without a Dependency wrapper, like the rest builtin dependencies,\n\t// then do NOT try to resolve its fields.\n\t//\n\t// Although EnableStructDependents is false by default, we must check if it's a builtin dependency for any case.\n\tif strings.HasPrefix(indirectType(v.Type()).PkgPath(), \"github.com/kataras/iris/v12\") {\n\t\treturn fromStructValue(v, dest)\n\t}\n\n\tbindings := getBindingsForStruct(v, prevDependencies, false, disablePayloadAutoBinding, dest.StructDependents, DefaultDependencyMatcher, -1, nil)\n\tif len(bindings) == 0 {\n\t\treturn fromStructValue(v, dest) // same as above.\n\t}\n\n\t// As a depedent struct, however we may need to resolve its dependencies first\n\t// so we can decide if it's really a depedent struct or not.\n\tvar (\n\t\thandler = func(*context.Context, *Input) (reflect.Value, error) {\n\t\t\treturn v, nil\n\t\t}\n\t\tisStatic = true\n\t)\n\n\tfor _, binding := range bindings {\n\t\tif !binding.Dependency.Static {\n\t\t\tisStatic = false\n\t\t\tbreak\n\t\t}\n\t}\n\n\thandler = func(ctx *context.Context, _ *Input) (reflect.Value, error) { // Called once per dependency on build-time if the dependency is static.\n\t\telem := v\n\t\tif elem.Kind() == reflect.Ptr {\n\t\t\telem = elem.Elem()\n\t\t}\n\n\t\tfor _, binding := range bindings {\n\t\t\tfield := elem.FieldByIndex(binding.Input.StructFieldIndex)\n\t\t\tif !field.CanSet() || !field.IsZero() {\n\t\t\t\tcontinue // already set.\n\t\t\t}\n\t\t\t// if !binding.Dependency.Match(field.Type()) { A check already happen in getBindingsForStruct.\n\t\t\t// \tcontinue\n\t\t\t// }\n\n\t\t\tinput, err := binding.Dependency.Handle(ctx, binding.Input)\n\t\t\tif err != nil {\n\t\t\t\tif err == ErrSeeOther {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\treturn emptyValue, err\n\t\t\t}\n\n\t\t\t// fmt.Printf(\"binding %s to %#+v\\n\", field.String(), input)\n\n\t\t\tfield.Set(input)\n\t\t}\n\n\t\treturn v, nil\n\t}\n\n\tdest.DestType = v.Type()\n\tdest.Static = isStatic\n\tdest.Handle = handler\n\treturn true\n}\n\nfunc fromFunc(v reflect.Value, dest *Dependency) bool {\n\tif !isFunc(v) {\n\t\treturn false\n\t}\n\n\ttyp := v.Type()\n\tnumIn := typ.NumIn()\n\tnumOut := typ.NumOut()\n\n\tif numIn == 0 {\n\t\t// it's an empty function, that must return a structure.\n\t\tif numOut != 1 {\n\t\t\tfirstOutType := indirectType(typ.Out(0))\n\t\t\tif firstOutType.Kind() != reflect.Struct && firstOutType.Kind() != reflect.Interface {\n\t\t\t\tpanic(fmt.Sprintf(\"bad value: function has zero inputs: empty input function must output a single value but got: length=%v, type[0]=%s\", numOut, firstOutType.String()))\n\t\t\t}\n\t\t}\n\n\t\t// fallback to structure.\n\t\treturn fromStructValue(v.Call(nil)[0], dest)\n\t}\n\n\tif numOut == 0 {\n\t\tpanic(\"bad value: function has zero outputs\")\n\t}\n\n\tif numOut == 2 && !isError(typ.Out(1)) {\n\t\tpanic(\"bad value: second output should be an error\")\n\t}\n\n\tif numOut > 2 {\n\t\t// - at least one output value\n\t\t// - maximum of two output values\n\t\t// - second output value should be a type of error.\n\t\tpanic(fmt.Sprintf(\"bad value: function has invalid number of output arguments: %v\", numOut))\n\t}\n\n\tvar handler DependencyHandler\n\n\tfirstIsContext := isContext(typ.In(0))\n\tsecondIsInput := numIn == 2 && typ.In(1) == inputTyp\n\tonlyContext := (numIn == 1 && firstIsContext) || (numIn == 2 && firstIsContext && typ.IsVariadic())\n\n\tif onlyContext || (firstIsContext && secondIsInput) {\n\t\thandler = handlerFromFunc(v, typ)\n\t}\n\n\tif handler == nil {\n\t\treturn false\n\t}\n\n\tdest.DestType = typ.Out(0)\n\tdest.Handle = handler\n\treturn true\n}\n\nfunc handlerFromFunc(v reflect.Value, typ reflect.Type) DependencyHandler {\n\t// * func(Context, *Input) <T>, func(Context) <T>\n\t// * func(Context) <T>, func(Context) <T>\n\t// * func(Context, *Input) <T>, func(Context) (<T>, error)\n\t// * func(Context) <T>, func(Context) (<T>, error)\n\n\thasErrorOut := typ.NumOut() == 2 // if two, always an error type here.\n\thasInputIn := typ.NumIn() == 2 && typ.In(1) == inputTyp\n\n\treturn func(ctx *context.Context, input *Input) (reflect.Value, error) {\n\t\tinputs := ctx.ReflectValue()\n\t\tif hasInputIn {\n\t\t\tinputs = append(inputs, input.selfValue)\n\t\t}\n\t\tresults := v.Call(inputs)\n\t\tif hasErrorOut {\n\t\t\treturn results[0], toError(results[1])\n\t\t}\n\n\t\treturn results[0], nil\n\t}\n}\n\nfunc fromDependentFunc(v reflect.Value, disablePayloadAutoBinding bool, dest *Dependency, funcDependencies []*Dependency) bool {\n\t// * func(<D>...) returns <T>\n\t// * func(<D>...) returns error\n\t// * func(<D>...) returns <T>, error\n\n\ttyp := v.Type()\n\tif !isFunc(v) {\n\t\treturn false\n\t}\n\n\tbindings := getBindingsForFunc(v, funcDependencies, disablePayloadAutoBinding, -1 /* parameter bindings are disabled for depent dependencies */)\n\n\tnumIn := typ.NumIn()\n\tnumOut := typ.NumOut()\n\n\t// d1 = Logger\n\t// d2 = func(Logger) S1\n\t// d2 should be static: it accepts dependencies that are static\n\t// (note: we don't check the output argument(s) of this dependency).\n\tif numIn == len(bindings) {\n\t\tstatic := true\n\t\tfor _, b := range bindings {\n\t\t\tif !b.Dependency.Static && b.Dependency.Match(typ.In(b.Input.Index)) {\n\t\t\t\tstatic = false\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tif static {\n\t\t\tdest.Static = static\n\t\t}\n\t}\n\n\tfirstOutIsError := numOut == 1 && isError(typ.Out(0))\n\tsecondOutIsError := numOut == 2 && isError(typ.Out(1))\n\n\thandler := func(ctx *context.Context, _ *Input) (reflect.Value, error) {\n\t\tinputs := make([]reflect.Value, numIn)\n\n\t\tfor _, binding := range bindings {\n\t\t\tinput, err := binding.Dependency.Handle(ctx, binding.Input)\n\t\t\tif err != nil {\n\t\t\t\tif err == ErrSeeOther {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\treturn emptyValue, err\n\t\t\t}\n\n\t\t\tinputs[binding.Input.Index] = input\n\t\t}\n\n\t\toutputs := v.Call(inputs)\n\t\tif firstOutIsError {\n\t\t\treturn emptyValue, toError(outputs[0])\n\t\t} else if secondOutIsError {\n\t\t\treturn outputs[0], toError(outputs[1])\n\t\t}\n\t\treturn outputs[0], nil\n\t}\n\n\tdest.DestType = typ.Out(0)\n\tdest.Handle = handler\n\n\treturn true\n}\n"
  },
  {
    "path": "hero/dependency_source.go",
    "content": "package hero\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"runtime\"\n\t\"strings\"\n)\n\n// Source describes where a dependency is located at the source code itself.\ntype Source struct {\n\tFile   string\n\tLine   int\n\tCaller string\n}\n\nfunc newSource(fn reflect.Value) Source {\n\tvar (\n\t\tcallerFileName   string\n\t\tcallerLineNumber int\n\t\tcallerName       string\n\t)\n\n\tswitch fn.Kind() {\n\tcase reflect.Func, reflect.Chan, reflect.Map, reflect.Ptr, reflect.UnsafePointer, reflect.Slice:\n\t\tpc := fn.Pointer()\n\t\tfpc := runtime.FuncForPC(pc)\n\t\tif fpc != nil {\n\t\t\tcallerFileName, callerLineNumber = fpc.FileLine(pc)\n\t\t\tcallerName = fpc.Name()\n\t\t}\n\n\t\tfallthrough\n\tdefault:\n\t\tif callerFileName == \"\" {\n\t\t\tcallerFileName, callerLineNumber = GetCaller()\n\t\t}\n\t}\n\n\twd, _ := os.Getwd()\n\tif relFile, err := filepath.Rel(wd, callerFileName); err == nil {\n\t\tif !strings.HasPrefix(relFile, \"..\") {\n\t\t\t// Only if it's relative to this path, not parent.\n\t\t\tcallerFileName = \"./\" + relFile\n\t\t}\n\t}\n\n\treturn Source{\n\t\tFile:   filepath.ToSlash(callerFileName),\n\t\tLine:   callerLineNumber,\n\t\tCaller: callerName,\n\t}\n}\n\nfunc getSource() Source {\n\tfilename, line := GetCaller()\n\treturn Source{\n\t\tFile: filename,\n\t\tLine: line,\n\t}\n}\n\nfunc (s Source) String() string {\n\treturn fmt.Sprintf(\"%s:%d\", s.File, s.Line)\n}\n\n// https://golang.org/doc/go1.9#callersframes\nfunc GetCaller() (string, int) {\n\tvar pcs [32]uintptr\n\tn := runtime.Callers(4, pcs[:])\n\tframes := runtime.CallersFrames(pcs[:n])\n\n\tfor {\n\t\tframe, more := frames.Next()\n\t\tfile := frame.File\n\n\t\tif strings.Contains(file, \"go/src/runtime/\") {\n\t\t\tcontinue\n\t\t}\n\n\t\t// funcName is something like \"github.com/kataras/iris.SomeFunc\"\n\t\tfuncName := frame.Function\n\t\tif !strings.HasPrefix(funcName, \"github.com/kataras/iris/v12\") {\n\t\t\treturn file, frame.Line\n\t\t}\n\n\t\tif !more {\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn \"???\", 0\n}\n"
  },
  {
    "path": "hero/dependency_test.go",
    "content": "package hero_test\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\t. \"github.com/kataras/iris/v12/hero\"\n)\n\ntype testDependencyTest struct {\n\tDependency any\n\tExpected   any\n}\n\nfunc TestDependency(t *testing.T) {\n\tvar tests = []testDependencyTest{\n\t\t{\n\t\t\tDependency: \"myValue\",\n\t\t\tExpected:   \"myValue\",\n\t\t},\n\t\t{\n\t\t\tDependency: struct{ Name string }{\"name\"},\n\t\t\tExpected:   struct{ Name string }{\"name\"},\n\t\t},\n\t\t{\n\t\t\tDependency: func(*context.Context, *Input) (reflect.Value, error) {\n\t\t\t\treturn reflect.ValueOf(42), nil\n\t\t\t},\n\t\t\tExpected: 42,\n\t\t},\n\t\t{\n\t\t\tDependency: DependencyHandler(func(*context.Context, *Input) (reflect.Value, error) {\n\t\t\t\treturn reflect.ValueOf(255), nil\n\t\t\t}),\n\t\t\tExpected: 255,\n\t\t},\n\t\t{\n\t\t\tDependency: func(*context.Context) (reflect.Value, error) {\n\t\t\t\treturn reflect.ValueOf(\"OK without Input\"), nil\n\t\t\t},\n\t\t\tExpected: \"OK without Input\",\n\t\t},\n\t\t{\n\n\t\t\tDependency: func(*context.Context, ...string) (reflect.Value, error) {\n\t\t\t\treturn reflect.ValueOf(\"OK variadic ignored\"), nil\n\t\t\t},\n\t\t\tExpected: \"OK variadic ignored\",\n\t\t},\n\t\t{\n\n\t\t\tDependency: func(*context.Context) reflect.Value {\n\t\t\t\treturn reflect.ValueOf(\"OK without Input and error\")\n\t\t\t},\n\t\t\tExpected: \"OK without Input and error\",\n\t\t},\n\t\t{\n\n\t\t\tDependency: func(*context.Context, ...int) reflect.Value {\n\t\t\t\treturn reflect.ValueOf(\"OK without error and variadic ignored\")\n\t\t\t},\n\t\t\tExpected: \"OK without error and variadic ignored\",\n\t\t},\n\t\t{\n\n\t\t\tDependency: func(*context.Context) any {\n\t\t\t\treturn \"1\"\n\t\t\t},\n\t\t\tExpected: \"1\",\n\t\t},\n\t\t{\n\n\t\t\tDependency: func(*context.Context) any {\n\t\t\t\treturn false\n\t\t\t},\n\t\t\tExpected: false,\n\t\t},\n\t\t{\n\n\t\t\tDependency: map[string]string{\"test\": \"value\"},\n\t\t\tExpected:   map[string]string{\"test\": \"value\"},\n\t\t},\n\t}\n\n\ttestDependencies(t, tests)\n}\n\n// Test dependencies that depend on previous one(s).\nfunc TestDependentDependency(t *testing.T) {\n\tmsgBody := \"prefix: it is a deep dependency\"\n\tnewMsgBody := msgBody + \" new\"\n\tvar tests = []testDependencyTest{\n\t\t// test three level depth and error.\n\t\t{ // 0\n\t\t\tDependency: &testServiceImpl{prefix: \"prefix:\"},\n\t\t\tExpected:   &testServiceImpl{prefix: \"prefix:\"},\n\t\t},\n\t\t{ // 1\n\t\t\tDependency: func(service testService) testMessage {\n\t\t\t\treturn testMessage{Body: service.Say(\"it is a deep\") + \" dependency\"}\n\t\t\t},\n\t\t\tExpected: testMessage{Body: msgBody},\n\t\t},\n\t\t{ // 2\n\t\t\tDependency: func(msg testMessage) string {\n\t\t\t\treturn msg.Body\n\t\t\t},\n\t\t\tExpected: msgBody,\n\t\t},\n\t\t{ // 3\n\t\t\tDependency: func(msg testMessage) error {\n\t\t\t\treturn errors.New(msg.Body)\n\t\t\t},\n\t\t\tExpected: errors.New(msgBody),\n\t\t},\n\t\t// Test depend on more than one previous registered dependencies and require a before-previous one.\n\t\t{ // 4\n\t\t\tDependency: func(body string, msg testMessage) string {\n\t\t\t\tif body != msg.Body {\n\t\t\t\t\tt.Fatalf(\"body[%s] != msg.Body[%s]\", body, msg.Body)\n\t\t\t\t}\n\n\t\t\t\treturn body + \" new\"\n\t\t\t},\n\t\t\tExpected: newMsgBody,\n\t\t},\n\t\t// Test dependency order by expecting the first <string> returning value and not the later-on registered dependency(#4).\n\t\t// 5\n\t\t{\n\t\t\tDependency: func(body string) string {\n\t\t\t\treturn body\n\t\t\t},\n\t\t\tExpected: newMsgBody,\n\t\t},\n\t}\n\n\ttestDependencies(t, tests)\n}\n\nfunc testDependencies(t *testing.T, tests []testDependencyTest) {\n\tt.Helper()\n\n\tc := New()\n\tfor i, tt := range tests {\n\t\td := c.Register(tt.Dependency)\n\n\t\tif d == nil {\n\t\t\tt.Fatalf(\"[%d] expected %#+v to be converted to a valid dependency\", i, tt)\n\t\t}\n\n\t\tval, err := d.Handle(context.NewContext(nil), &Input{})\n\n\t\tif expectError := isError(reflect.TypeOf(tt.Expected)); expectError {\n\t\t\tval = reflect.ValueOf(err)\n\t\t\terr = nil\n\t\t}\n\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"[%d] expected a nil error but got: %v\", i, err)\n\t\t}\n\n\t\tif !val.CanInterface() {\n\t\t\tt.Fatalf(\"[%d] expected output value to be accessible: %T\", i, val)\n\t\t}\n\n\t\tif expected, got := fmt.Sprintf(\"%#+v\", tt.Expected), fmt.Sprintf(\"%#+v\", val.Interface()); expected != got {\n\t\t\tt.Fatalf(\"[%d] expected return value to be:\\n%s\\nbut got:\\n%s\", i, expected, got)\n\t\t}\n\n\t\t// t.Logf(\"[%d] %s\", i, d)\n\t\t// t.Logf(\"[%d] output: %#+v\", i, val.Interface())\n\t}\n}\n\nfunc TestDependentDependencyInheritanceStatic(t *testing.T) {\n\t// Tests the following case #1564:\n\t// Logger\n\t// func(Logger) S1\n\t// ^ Should be static because Logger\n\t// is a structure, a static dependency.\n\t//\n\t// func(Logger) S2\n\t// func(S1, S2) S3\n\t// ^ Should be marked as static dependency\n\t// because everything that depends on are static too.\n\n\ttype S1 struct {\n\t\tmsg string\n\t}\n\n\ttype S2 struct {\n\t\tmsg2 string\n\t}\n\n\tserviceDep := NewDependency(&testServiceImpl{prefix: \"1\"})\n\td1 := NewDependency(func(t testService) S1 {\n\t\treturn S1{t.Say(\"2\")}\n\t}, serviceDep)\n\tif !d1.Static {\n\t\tt.Fatalf(\"d1 dependency should be static: %#+v\", d1)\n\t}\n\n\td2 := NewDependency(func(t testService, s S1) S2 {\n\t\treturn S2{\"3\"}\n\t}, serviceDep, d1)\n\tif !d2.Static {\n\t\tt.Fatalf(\"d2 dependency should be static: %#+v\", d2)\n\t}\n}\n"
  },
  {
    "path": "hero/func_result.go",
    "content": "package hero\n\nimport (\n\t\"reflect\"\n\t\"strings\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\n\t\"github.com/fatih/structs\"\n\t\"google.golang.org/protobuf/proto\"\n)\n\n// ResultHandler describes the function type which should serve the \"v\" struct value.\ntype ResultHandler func(ctx *context.Context, v any) error\n\nfunc defaultResultHandler(ctx *context.Context, v any) error {\n\tif p, ok := v.(PreflightResult); ok {\n\t\tif err := p.Preflight(ctx); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif d, ok := v.(Result); ok {\n\t\td.Dispatch(ctx)\n\t\treturn nil\n\t}\n\n\tswitch context.TrimHeaderValue(ctx.GetContentType()) {\n\tcase context.ContentXMLHeaderValue, context.ContentXMLUnreadableHeaderValue:\n\t\treturn ctx.XML(v)\n\tcase context.ContentYAMLHeaderValue:\n\t\treturn ctx.YAML(v)\n\tcase context.ContentProtobufHeaderValue:\n\t\tmsg, ok := v.(proto.Message)\n\t\tif !ok {\n\t\t\treturn context.ErrContentNotSupported\n\t\t}\n\n\t\t_, err := ctx.Protobuf(msg)\n\t\treturn err\n\tcase context.ContentMsgPackHeaderValue, context.ContentMsgPack2HeaderValue:\n\t\t_, err := ctx.MsgPack(v)\n\t\treturn err\n\tdefault:\n\t\t// otherwise default to JSON.\n\t\treturn ctx.JSON(v)\n\t}\n}\n\n// Result is a response dispatcher.\n// All types that complete this interface\n// can be returned as values from the method functions.\n//\n// Example at: https://github.com/kataras/iris/tree/main/_examples/dependency-injection/overview.\ntype Result interface {\n\t// Dispatch should send a response to the client.\n\tDispatch(*context.Context)\n}\n\n// PreflightResult is an interface which implementers\n// should be responsible to perform preflight checks of a <T> resource (or Result) before sent to the client.\n//\n// If a non-nil error returned from the `Preflight` method then the JSON result\n// will be not sent to the client and an ErrorHandler will be responsible to render the error.\n//\n// Usage: a custom struct value will be a JSON body response (by-default) but it contains\n// \"Code int\" and `ID string` fields, the \"Code\" should be the status code of the response\n// and the \"ID\" should be sent as a Header of \"X-Request-ID: $ID\".\n//\n// The caller can manage it at the handler itself. However,\n// to reduce thoese type of duplications it's preferable to use such a standard interface instead.\n//\n// The Preflight method can return `iris.ErrStopExecution` to render\n// and override any interface that the structure value may implement, e.g. mvc.Result.\ntype PreflightResult interface {\n\tPreflight(*context.Context) error\n}\n\nvar defaultFailureResponse = Response{Code: DefaultErrStatusCode}\n\n// Try will check if \"fn\" ran without any panics,\n// using recovery,\n// and return its result as the final response\n// otherwise it returns the \"failure\" response if any,\n// if not then a 400 bad request is being sent.\n//\n// Example usage at: https://github.com/kataras/iris/blob/main/hero/func_result_test.go.\nfunc Try(fn func() Result, failure ...Result) Result {\n\tvar failed bool\n\tvar actionResponse Result\n\n\tfunc() {\n\t\tdefer func() {\n\t\t\tif rec := recover(); rec != nil {\n\t\t\t\tfailed = true\n\t\t\t}\n\t\t}()\n\t\tactionResponse = fn()\n\t}()\n\n\tif failed {\n\t\tif len(failure) > 0 {\n\t\t\treturn failure[0]\n\t\t}\n\t\treturn defaultFailureResponse\n\t}\n\n\treturn actionResponse\n}\n\nconst slashB byte = '/'\n\ntype compatibleErr interface {\n\tError() string\n}\n\n// dispatchErr sets the error status code\n// and the error value to the context.\n// The APIBuilder's On(Any)ErrorCode is responsible to render this error code.\nfunc dispatchErr(ctx *context.Context, status int, err error) bool {\n\tif err == nil {\n\t\treturn false\n\t}\n\n\tif err != ErrStopExecution {\n\t\tif status == 0 || !context.StatusCodeNotSuccessful(status) {\n\t\t\tstatus = DefaultErrStatusCode\n\t\t}\n\n\t\tctx.StatusCode(status)\n\t}\n\n\tctx.SetErr(err)\n\treturn true\n}\n\n// DispatchFuncResult is being used internally to resolve\n// and send the method function's output values to the\n// context's response writer using a smart way which\n// respects status code, content type, content, custom struct\n// and an error type.\n// Supports for:\n// func(c *ExampleController) Get() string |\n// (string, string) |\n// (string, int) |\n// ...\n// int |\n// (int, string |\n// (string, error) |\n// ...\n// error |\n// (int, error) |\n// (customStruct, error) |\n// ...\n// bool |\n// (int, bool) |\n// (string, bool) |\n// (customStruct, bool) |\n// ...\n// customStruct |\n// (customStruct, int) |\n// (customStruct, string) |\n// Result or (Result, error) and so on...\n//\n// where Get is an HTTP METHOD.\nfunc dispatchFuncResult(ctx *context.Context, values []reflect.Value, handler ResultHandler) error {\n\tif len(values) == 0 {\n\t\treturn nil\n\t}\n\n\tvar (\n\t\t// if statusCode > 0 then send this status code.\n\t\t// Except when err != nil then check if status code is < 400 and\n\t\t// if it's set it as DefaultErrStatusCode.\n\t\t// Except when found == false, then the status code is 404.\n\t\tstatusCode = ctx.GetStatusCode() // Get the current status code given by any previous middleware.\n\t\t// if not empty then use that as content type,\n\t\t// if empty and custom != nil then set it to application/json.\n\t\tcontentType string\n\t\t// if len > 0 then write that to the response writer as raw bytes,\n\t\t// except when found == false or err != nil or custom != nil.\n\t\tcontent []byte\n\t\t// if not nil then check\n\t\t// for content type (or json default) and send the custom data object\n\t\t// except when found == false or err != nil.\n\t\tcustom any\n\t\t// if false then skip everything and fire 404.\n\t\tfound = true // defaults to true of course, otherwise will break :)\n\t)\n\n\tfor _, v := range values {\n\t\t// order of these checks matters\n\t\t// for example, first  we need to check for status code,\n\t\t// secondly the string (for content type and content)...\n\t\t// if !v.IsValid() || !v.CanInterface() {\n\t\t// \tcontinue\n\t\t// }\n\t\tif !v.IsValid() {\n\t\t\tcontinue\n\t\t}\n\n\t\tf := v.Interface()\n\t\t/*\n\t\t\t\tif b, ok := f.(bool); ok {\n\t\t\t\t\tfound = b\n\t\t\t\t\tif !found {\n\t\t\t\t\t\t// skip everything, we don't care about other return values,\n\t\t\t\t\t\t// this boolean is the higher in order.\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tif i, ok := f.(int); ok {\n\t\t\t\t\tstatusCode = i\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tif s, ok := f.(string); ok {\n\t\t\t\t\t// a string is content type when it contains a slash and\n\t\t\t\t\t// content or custom struct is being calculated already;\n\t\t\t\t\t// (string -> content, string-> content type)\n\t\t\t\t\t// (customStruct, string -> content type)\n\t\t\t\t\tif (len(content) > 0 || custom != nil) && strings.IndexByte(s, slashB) > 0 {\n\t\t\t\t\t\tcontentType = s\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// otherwise is content\n\t\t\t\t\t\tcontent = []byte(s)\n\t\t\t\t\t}\n\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tif b, ok := f.([]byte); ok {\n\t\t\t\t\t// it's raw content, get the latest\n\t\t\t\t\tcontent = b\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tif e, ok := f.(compatibleErr); ok {\n\t\t\t\t\tif e != nil { // it's always not nil but keep it here.\n\t\t\t\t\t\terr = e\n\t\t\t\t\t\tif statusCode < 400 {\n\t\t\t\t\t\t\tstatusCode = DefaultErrStatusCode\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak // break on first error, error should be in the end but we\n\t\t\t\t\t\t// need to know break the dispatcher if any error.\n\t\t\t\t\t\t// at the end; we don't want to write anything to the response if error is not nil.\n\t\t\t\t\t}\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\t// else it's a custom struct or a dispatcher, we'll decide later\n\t\t\t\t// because content type and status code matters\n\t\t\t\t// do that check in order to be able to correctly dispatch:\n\t\t\t\t// (customStruct, error) -> customStruct filled and error is nil\n\t\t\t\tif custom == nil && f != nil {\n\t\t\t\t\tcustom = f\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t*/\n\t\tswitch value := f.(type) {\n\t\tcase bool:\n\t\t\tfound = value\n\t\t\tif !found {\n\t\t\t\t// skip everything, skip other values, we don't care about other return values,\n\t\t\t\t// this boolean is the higher in order.\n\t\t\t\tbreak\n\t\t\t}\n\t\tcase int:\n\t\t\tstatusCode = value\n\t\tcase string:\n\t\t\t// a string is content type when it contains a slash and\n\t\t\t// content or custom struct is being calculated already;\n\t\t\t// (string -> content, string-> content type)\n\t\t\t// (customStruct, string -> content type)\n\t\t\tif (len(content) > 0 || custom != nil) && strings.IndexByte(value, slashB) > 0 {\n\t\t\t\tcontentType = value\n\t\t\t} else {\n\t\t\t\t// otherwise is content\n\t\t\t\tcontentType = context.ContentTextHeaderValue\n\t\t\t\tcontent = []byte(value)\n\t\t\t}\n\n\t\tcase []byte:\n\t\t\t// it's raw content, get the latest\n\t\t\tcontent = value\n\t\tcase compatibleErr:\n\t\t\tif value == nil || isNil(v) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif statusCode < 400 && value != ErrStopExecution {\n\t\t\t\tstatusCode = DefaultErrStatusCode\n\t\t\t}\n\n\t\t\tctx.StatusCode(statusCode)\n\t\t\treturn value\n\t\tdefault:\n\t\t\t// else it's a custom struct or a dispatcher, we'll decide later\n\t\t\t// because content type and status code matters\n\t\t\t// do that check in order to be able to correctly dispatch:\n\t\t\t// (customStruct, error) -> customStruct filled and error is nil\n\t\t\tif custom == nil {\n\t\t\t\t// if it's a pointer to struct/map.\n\n\t\t\t\tif isNil(v) {\n\t\t\t\t\t// if just a ptr to struct with no content type given\n\t\t\t\t\t// then try to get the previous response writer's content type,\n\t\t\t\t\t// and if that is empty too then force-it to application/json\n\t\t\t\t\t// as the default content type we use for structs/maps.\n\t\t\t\t\tif contentType == \"\" {\n\t\t\t\t\t\tcontentType = ctx.GetContentType()\n\t\t\t\t\t\tif contentType == \"\" {\n\t\t\t\t\t\t\tcontentType = context.ContentJSONHeaderValue\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tif value != nil {\n\t\t\t\t\tcustom = value // content type will be take care later on.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn dispatchCommon(ctx, statusCode, contentType, content, custom, handler, found)\n}\n\n// dispatchCommon is being used internally to send\n// commonly used data to the response writer with a smart way.\nfunc dispatchCommon(ctx *context.Context,\n\tstatusCode int, contentType string, content []byte, v any, handler ResultHandler, found bool) error {\n\t// if we have a false boolean as a return value\n\t// then skip everything and fire a not found,\n\t// we even don't care about the given status code or the object or the content.\n\tif !found {\n\t\tctx.NotFound()\n\t\treturn nil\n\t}\n\n\tstatus := statusCode\n\tif status == 0 {\n\t\tstatus = 200\n\t}\n\n\t// write the status code, the rest will need that before any write ofc.\n\tctx.StatusCode(status)\n\tif contentType == \"\" {\n\t\t// to respect any ctx.ContentType(...) call\n\t\t// especially if v is not nil.\n\t\tif contentType = ctx.GetContentType(); contentType == \"\" {\n\t\t\t// if it's still empty set to JSON. (useful for dynamic middlewares that returns an int status code and the next handler dispatches the JSON,\n\t\t\t// see dependency-injection/basic/middleware example)\n\t\t\tcontentType = context.ContentJSONHeaderValue\n\t\t}\n\t}\n\n\t// write the content type now (internal check for empty value)\n\tctx.ContentType(contentType)\n\n\tif v != nil {\n\t\treturn handler(ctx, v)\n\t}\n\n\t// .Write even len(content) == 0 , this should be called in order to call the internal tryWriteHeader,\n\t// it will not cost anything.\n\t_, err := ctx.Write(content)\n\treturn err\n}\n\n// Response completes the `methodfunc.Result` interface.\n// It's being used as an alternative return value which\n// wraps the status code, the content type, a content as bytes or as string\n// and an error, it's smart enough to complete the request and send the correct response to the client.\ntype Response struct {\n\tCode        int\n\tContentType string\n\tContent     []byte\n\n\t// If not empty then content type is the \"text/plain\"\n\t// and content is the text as []byte. If not empty and\n\t// the \"Lang\" field is not empty then this \"Text\" field\n\t// becomes the current locale file's key.\n\tText string\n\t// If not empty then \"Text\" field becomes the locale file's key that should point\n\t// to a translation file's unique key. See `Object` for locale template data.\n\t// The \"Lang\" field is the language code\n\t// that should render the text inside the locale file's key.\n\tLang string\n\t// If not nil then it will fire that as \"application/json\" or any\n\t// previously set \"ContentType\". If \"Lang\" and \"Text\" are not empty\n\t// then this \"Object\" field becomes the template data that the\n\t// locale text should use to be rendered.\n\tObject any\n\n\t// If Path is not empty then it will redirect\n\t// the client to this Path, if Code is >= 300 and < 400\n\t// then it will use that Code to do the redirection, otherwise\n\t// StatusFound(302) or StatusSeeOther(303) for post methods will be used.\n\t// Except when err != nil.\n\tPath string\n\n\t// if not empty then fire a 400 bad request error\n\t// unless the Status is > 200, then fire that error code\n\t// with the Err.Error() string as its content.\n\t//\n\t// if Err.Error() is empty then it fires the custom error handler\n\t// if any otherwise the framework sends the default http error text based on the status.\n\tErr error\n\tTry func() int\n\n\t// if true then it skips everything else and it throws a 404 not found error.\n\t// Can be named as Failure but NotFound is more precise name in order\n\t// to be visible that it's different than the `Err`\n\t// because it throws a 404 not found instead of a 400 bad request.\n\t// NotFound bool\n\t// let's don't add this yet, it has its dangerous of missuse.\n}\n\nvar _ Result = Response{}\n\n// Dispatch writes the response result to the context's response writer.\nfunc (r Response) Dispatch(ctx *context.Context) {\n\tif dispatchErr(ctx, r.Code, r.Err) {\n\t\treturn\n\t}\n\n\tif r.Path != \"\" {\n\t\t// it's not a redirect valid status\n\t\tif r.Code < 300 || r.Code >= 400 {\n\t\t\tif ctx.Method() == \"POST\" {\n\t\t\t\tr.Code = 303 // StatusSeeOther\n\t\t\t}\n\t\t\tr.Code = 302 // StatusFound\n\t\t}\n\t\tctx.Redirect(r.Path, r.Code)\n\t\treturn\n\t}\n\n\tif r.Text != \"\" {\n\t\tif r.Lang != \"\" {\n\t\t\tif r.Code > 0 {\n\t\t\t\tctx.StatusCode(r.Code)\n\t\t\t}\n\t\t\tctx.ContentType(r.ContentType)\n\n\t\t\tctx.SetLanguage(r.Lang)\n\t\t\tr.Content = []byte(ctx.Tr(r.Text, r.Object))\n\t\t} else {\n\t\t\tr.Content = []byte(r.Text)\n\t\t}\n\t}\n\n\terr := dispatchCommon(ctx, r.Code, r.ContentType, r.Content, r.Object, defaultResultHandler, true)\n\tdispatchErr(ctx, r.Code, err)\n}\n\n// View completes the `hero.Result` interface.\n// It's being used as an alternative return value which\n// wraps the template file name, layout, (any) view data, status code and error.\n// It's smart enough to complete the request and send the correct response to the client.\n//\n// Example at: https://github.com/kataras/iris/blob/main/_examples/dependency-injection/overview/web/routes/hello.go.\ntype View struct {\n\tName   string\n\tLayout string\n\tData   any // map or a custom struct.\n\tCode   int\n\tErr    error\n}\n\nvar _ Result = View{}\n\n// Dispatch writes the template filename, template layout and (any) data to the  client.\n// Completes the `Result` interface.\nfunc (r View) Dispatch(ctx *context.Context) { // r as Response view.\n\tif dispatchErr(ctx, r.Code, r.Err) {\n\t\treturn\n\t}\n\n\tif r.Code > 0 {\n\t\tctx.StatusCode(r.Code)\n\t}\n\n\tif r.Name != \"\" {\n\t\tif r.Layout != \"\" {\n\t\t\tctx.ViewLayout(r.Layout)\n\t\t}\n\n\t\tif r.Data != nil {\n\t\t\t// In order to respect any c.Ctx.ViewData that may called manually before;\n\t\t\tdataKey := ctx.Application().ConfigurationReadOnly().GetViewDataContextKey()\n\t\t\tif ctx.Values().Get(dataKey) == nil {\n\t\t\t\t// if no c.Ctx.ViewData set-ed before (the most common scenario) then do a\n\t\t\t\t// simple set, it's faster.\n\t\t\t\tctx.Values().Set(dataKey, r.Data)\n\t\t\t} else {\n\t\t\t\t// else check if r.Data is map or struct, if struct convert it to map,\n\t\t\t\t// do a range loop and modify the data one by one.\n\t\t\t\t// context.Map is actually a map[string]any but we have to make that check:\n\t\t\t\tif m, ok := r.Data.(context.Map); ok {\n\t\t\t\t\tsetViewData(ctx, m)\n\t\t\t\t} else if reflect.Indirect(reflect.ValueOf(r.Data)).Kind() == reflect.Struct {\n\t\t\t\t\tsetViewData(ctx, structs.Map(r))\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t_ = ctx.View(r.Name)\n\t}\n}\n\nfunc setViewData(ctx *context.Context, data map[string]any) {\n\tfor k, v := range data {\n\t\tctx.ViewData(k, v)\n\t}\n}\n"
  },
  {
    "path": "hero/func_result_test.go",
    "content": "package hero_test\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/httptest\"\n\n\t. \"github.com/kataras/iris/v12/hero\"\n)\n\nfunc GetText() string {\n\treturn \"text\"\n}\n\nfunc GetStatus() int {\n\treturn iris.StatusBadGateway\n}\n\nfunc GetTextWithStatusOk() (string, int) {\n\treturn \"OK\", iris.StatusOK\n}\n\n// tests should have output arguments mixed\nfunc GetStatusWithTextNotOkBy(first string, second string) (int, string) {\n\treturn iris.StatusForbidden, \"NOT_OK_\" + first + second\n}\n\nfunc GetTextAndContentType() (string, string) {\n\treturn \"<b>text</b>\", \"text/html\"\n}\n\ntype testCustomResult struct {\n\tHTML string\n}\n\n// The only one required function to make that a custom Response dispatcher.\nfunc (r testCustomResult) Dispatch(ctx iris.Context) {\n\t_, _ = ctx.HTML(r.HTML)\n}\n\nfunc GetCustomResponse() testCustomResult {\n\treturn testCustomResult{\"<b>text</b>\"}\n}\n\nfunc GetCustomResponseWithStatusOk() (testCustomResult, int) {\n\treturn testCustomResult{\"<b>OK</b>\"}, iris.StatusOK\n}\n\nfunc GetCustomResponseWithStatusNotOk() (testCustomResult, int) {\n\treturn testCustomResult{\"<b>internal server error</b>\"}, iris.StatusInternalServerError\n}\n\ntype testCustomStruct struct {\n\tName string `json:\"name\" xml:\"name\"`\n\tAge  int    `json:\"age\" xml:\"age\"`\n}\n\nfunc GetCustomStruct() testCustomStruct {\n\treturn testCustomStruct{\"Iris\", 2}\n}\n\nfunc GetCustomStructWithStatusNotOk() (testCustomStruct, int) {\n\treturn testCustomStruct{\"Iris\", 2}, iris.StatusInternalServerError\n}\n\nfunc GetCustomStructWithContentType() (testCustomStruct, string) {\n\treturn testCustomStruct{\"Iris\", 2}, \"text/xml\"\n}\n\nfunc GetCustomStructWithError(ctx iris.Context) (s testCustomStruct, err error) {\n\ts = testCustomStruct{\"Iris\", 2}\n\tif ctx.URLParamExists(\"err\") {\n\t\terr = errors.New(\"omit return of testCustomStruct and fire error\")\n\t}\n\n\t// it should send the testCustomStruct as JSON if error is nil\n\t// otherwise it should fire the default error(BadRequest) with the error's text.\n\treturn\n}\n\ntype err struct {\n\tStatus  int    `json:\"status_code\"`\n\tMessage string `json:\"message\"`\n}\n\nfunc (e err) Dispatch(ctx iris.Context) {\n\t// write the status code based on the err's StatusCode.\n\tctx.StatusCode(e.Status)\n\t// send to the client the whole object as json\n\t_ = ctx.JSON(e)\n}\n\nfunc GetCustomErrorAsDispatcher() err {\n\treturn err{iris.StatusBadRequest, \"this is my error as json\"}\n}\n\nfunc GetCustomTypedNilEmptyResponse() iris.Map {\n\treturn nil\n}\n\nfunc GetCustomTypedPtrNilEmptyResponse() *iris.Map {\n\treturn nil\n}\n\nfunc GetCustomMapNilEmptyResponse() map[string]any {\n\treturn nil\n}\n\nfunc GetCustomPtrStructNilEmptyResponse() *testCustomStruct {\n\treturn nil\n}\n\nfunc TestFuncResult(t *testing.T) {\n\tapp := iris.New()\n\th := New()\n\t// for any 'By', by is not required but we use this suffix here, like controllers\n\t// to make it easier for the future to resolve if any bug.\n\t// add the binding for path parameters.\n\n\tapp.Get(\"/text\", h.Handler(GetText))\n\tapp.Get(\"/status\", h.Handler(GetStatus))\n\tapp.Get(\"/text/with/status/ok\", h.Handler(GetTextWithStatusOk))\n\tapp.Get(\"/status/with/text/not/ok/{first}/{second}\", h.Handler(GetStatusWithTextNotOkBy))\n\tapp.Get(\"/text/and/content/type\", h.Handler(GetTextAndContentType))\n\t//\n\tapp.Get(\"/custom/response\", h.Handler(GetCustomResponse))\n\tapp.Get(\"/custom/response/with/status/ok\", h.Handler(GetCustomResponseWithStatusOk))\n\tapp.Get(\"/custom/response/with/status/not/ok\", h.Handler(GetCustomResponseWithStatusNotOk))\n\t//\n\tapp.Get(\"/custom/struct\", h.Handler(GetCustomStruct))\n\tapp.Get(\"/custom/struct/with/status/not/ok\", h.Handler(GetCustomStructWithStatusNotOk))\n\tapp.Get(\"/custom/struct/with/content/type\", h.Handler(GetCustomStructWithContentType))\n\tapp.Get(\"/custom/struct/with/error\", h.Handler(GetCustomStructWithError))\n\tapp.Get(\"/custom/error/as/dispatcher\", h.Handler(GetCustomErrorAsDispatcher))\n\n\tapp.Get(\"/custom/nil/typed\", h.Handler(GetCustomTypedNilEmptyResponse))\n\tapp.Get(\"/custom/nil/typed/ptr\", h.Handler(GetCustomTypedPtrNilEmptyResponse))\n\tapp.Get(\"/custom/nil/map\", h.Handler(GetCustomMapNilEmptyResponse))\n\tapp.Get(\"/custom/nil/struct\", h.Handler(GetCustomPtrStructNilEmptyResponse))\n\n\te := httptest.New(t, app)\n\n\te.GET(\"/text\").Expect().Status(iris.StatusOK).\n\t\tBody().IsEqual(\"text\")\n\n\te.GET(\"/status\").Expect().Status(iris.StatusBadGateway)\n\n\te.GET(\"/text/with/status/ok\").Expect().Status(iris.StatusOK).\n\t\tBody().IsEqual(\"OK\")\n\n\te.GET(\"/status/with/text/not/ok/first/second\").Expect().Status(iris.StatusForbidden).\n\t\tBody().IsEqual(\"NOT_OK_firstsecond\")\n\t// Author's note: <-- if that fails means that the last binder called for both input args,\n\t// see path_param_binder.go\n\n\te.GET(\"/text/and/content/type\").Expect().Status(iris.StatusOK).\n\t\tContentType(\"text/html\", \"utf-8\").\n\t\tBody().IsEqual(\"<b>text</b>\")\n\n\te.GET(\"/custom/response\").Expect().Status(iris.StatusOK).\n\t\tContentType(\"text/html\", \"utf-8\").\n\t\tBody().IsEqual(\"<b>text</b>\")\n\te.GET(\"/custom/response/with/status/ok\").Expect().Status(iris.StatusOK).\n\t\tContentType(\"text/html\", \"utf-8\").\n\t\tBody().IsEqual(\"<b>OK</b>\")\n\te.GET(\"/custom/response/with/status/not/ok\").Expect().Status(iris.StatusInternalServerError).\n\t\tContentType(\"text/html\", \"utf-8\").\n\t\tBody().IsEqual(\"<b>internal server error</b>\")\n\n\texpectedResultFromCustomStruct := map[string]any{\n\t\t\"name\": \"Iris\",\n\t\t\"age\":  2,\n\t}\n\te.GET(\"/custom/struct\").Expect().Status(iris.StatusOK).\n\t\tJSON().IsEqual(expectedResultFromCustomStruct)\n\te.GET(\"/custom/struct/with/status/not/ok\").Expect().Status(iris.StatusInternalServerError).\n\t\tJSON().IsEqual(expectedResultFromCustomStruct)\n\te.GET(\"/custom/struct/with/content/type\").Expect().Status(iris.StatusOK).\n\t\tContentType(\"text/xml\", \"utf-8\")\n\te.GET(\"/custom/struct/with/error\").Expect().Status(iris.StatusOK).\n\t\tJSON().IsEqual(expectedResultFromCustomStruct)\n\te.GET(\"/custom/struct/with/error\").WithQuery(\"err\", true).Expect().\n\t\tStatus(iris.StatusBadRequest). // the default status code if error is not nil\n\t\t// the content should be not JSON it should be the status code's text\n\t\t// it will fire the error's text\n\t\tBody().IsEqual(\"omit return of testCustomStruct and fire error\")\n\n\te.GET(\"/custom/error/as/dispatcher\").Expect().\n\t\tStatus(iris.StatusBadRequest). // the default status code if error is not nil\n\t\t// the content should be not JSON it should be the status code's text\n\t\t// it will fire the error's text\n\t\tJSON().IsEqual(err{iris.StatusBadRequest, \"this is my error as json\"})\n\n\t// its result is nil should give an empty response but content-type is set correctly.\n\te.GET(\"/custom/nil/typed\").Expect().\n\t\tStatus(iris.StatusOK).ContentType(context.ContentJSONHeaderValue).Body().IsEmpty()\n\te.GET(\"/custom/nil/typed/ptr\").Expect().\n\t\tStatus(iris.StatusOK).ContentType(context.ContentJSONHeaderValue).Body().IsEmpty()\n\te.GET(\"/custom/nil/map\").Expect().\n\t\tStatus(iris.StatusOK).ContentType(context.ContentJSONHeaderValue).Body().IsEmpty()\n\te.GET(\"/custom/nil/struct\").Expect().\n\t\tStatus(iris.StatusOK).ContentType(context.ContentJSONHeaderValue).Body().IsEmpty()\n}\n\ntype (\n\ttestPreflightRequest struct {\n\t\tFailCode int `json:\"fail_code\"` // for the sake of the test.\n\t}\n\n\ttestPreflightResponse struct {\n\t\tCode    int\n\t\tMessage string\n\t}\n)\n\nfunc (r testPreflightResponse) Preflight(ctx iris.Context) error {\n\tif r.Code == httptest.StatusInternalServerError {\n\t\treturn fmt.Errorf(\"custom error\")\n\t}\n\n\tif r.Code > 0 {\n\t\tctx.StatusCode(r.Code)\n\t}\n\n\treturn nil\n}\n\nfunc TestPreflightResult(t *testing.T) {\n\tapp := iris.New()\n\tc := New()\n\thandler := c.Handler(func(in testPreflightRequest) testPreflightResponse {\n\t\treturn testPreflightResponse{Code: in.FailCode, Message: http.StatusText(in.FailCode)}\n\t})\n\tapp.Post(\"/\", handler)\n\thandler2 := c.Handler(func(in testInput) (int, testOutput) {\n\t\treturn httptest.StatusAccepted, testOutput{Name: in.Name}\n\t})\n\tapp.Post(\"/alternative\", handler2)\n\n\te := httptest.New(t, app)\n\n\texpected1 := testPreflightResponse{Code: httptest.StatusOK, Message: \"OK\"}\n\te.POST(\"/\").WithJSON(testPreflightRequest{FailCode: expected1.Code}).\n\t\tExpect().Status(httptest.StatusOK).JSON().IsEqual(expected1)\n\n\texpected2 := testPreflightResponse{Code: httptest.StatusBadRequest, Message: \"Bad Request\"}\n\te.POST(\"/\").WithJSON(testPreflightRequest{FailCode: expected2.Code}).\n\t\tExpect().Status(httptest.StatusBadRequest).JSON().IsEqual(expected2)\n\n\t// Test error returned from Preflight.\n\te.POST(\"/\").WithJSON(testPreflightRequest{FailCode: httptest.StatusInternalServerError}).\n\t\tExpect().Status(httptest.StatusBadRequest).Body().IsEqual(\"custom error\")\n\n\t// Can be done without Preflight as the second output argument can be a status code.\n\texpected4 := testOutput{Name: \"my_name\"}\n\te.POST(\"/alternative\").WithJSON(testInput{expected4.Name}).\n\t\tExpect().Status(httptest.StatusAccepted).JSON().IsEqual(expected4)\n}\n\nfunc TestResponseErr(t *testing.T) {\n\tapp := iris.New()\n\tvar expectedErr = errors.New(\"response error\")\n\n\tapp.OnAnyErrorCode(func(ctx iris.Context) {\n\t\terr := ctx.GetErr()\n\t\tif err != expectedErr {\n\t\t\tt.Fatalf(\"expected error value does not match\")\n\t\t}\n\n\t\tctx.WriteString(err.Error())\n\t})\n\n\tapp.ConfigureContainer().Get(\"/\", func() Response {\n\t\treturn Response{Code: iris.StatusBadGateway, Err: expectedErr}\n\t})\n\n\te := httptest.New(t, app)\n\te.GET(\"/\").Expect().Status(iris.StatusBadGateway).Body().IsEqual(\"response error\")\n}\n"
  },
  {
    "path": "hero/handler.go",
    "content": "package hero\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\n\t\"github.com/kataras/iris/v12/context\"\n)\n\ntype (\n\t// ErrorHandler describes an interface to handle errors per hero handler and its dependencies.\n\t//\n\t// Handles non-nil errors return from a hero handler or a controller's method (see `getBindingsFor` and `Handler`)\n\t// the error may return from a request-scoped dependency too (see `Handler`).\n\tErrorHandler interface {\n\t\tHandleError(*context.Context, error)\n\t}\n\t// ErrorHandlerFunc implements the `ErrorHandler`.\n\t// It describes the type defnition for an error function handler.\n\tErrorHandlerFunc func(*context.Context, error)\n\n\t// Code is a special type for status code.\n\t// It's used for a builtin dependency to map the status code given by a previous\n\t// method or middleware.\n\t// Use a type like that in order to not conflict with any developer-registered\n\t// dependencies.\n\t// Alternatively: ctx.GetStatusCode().\n\tCode int\n\n\t// Err is a special type for error stored in mvc responses or context.\n\t// It's used for a builtin dependency to map the error given by a previous\n\t// method or middleware.\n\t// Use a type like that in order to not conflict with any developer-registered\n\t// dependencies.\n\t// Alternatively: ctx.GetErr().\n\tErr error\n)\n\n// HandleError fires when a non-nil error returns from a request-scoped dependency at serve-time or the handler itself.\nfunc (fn ErrorHandlerFunc) HandleError(ctx *context.Context, err error) {\n\tfn(ctx, err)\n}\n\n// String implements the fmt.Stringer interface.\n// Returns the text corresponding to this status code, e.g. \"Not Found\".\n// Same as iris.StatusText(int(code)).\nfunc (code Code) String() string {\n\treturn context.StatusText(int(code))\n}\n\n// Value returns the underline int value.\n// Same as int(code).\nfunc (code Code) Value() int {\n\treturn int(code)\n}\n\nvar (\n\t// ErrSeeOther may be returned from a dependency handler to skip a specific dependency\n\t// based on custom logic.\n\tErrSeeOther = fmt.Errorf(\"see other\")\n\t// ErrStopExecution may be returned from a dependency handler to stop\n\t// and return the execution of the function without error (it calls ctx.StopExecution() too).\n\t// It may be occurred from request-scoped dependencies as well.\n\tErrStopExecution = fmt.Errorf(\"stop execution\")\n)\n\nvar (\n\t// DefaultErrStatusCode is the default error status code (400)\n\t// when the response contains a non-nil error or a request-scoped binding error occur.\n\tDefaultErrStatusCode = 400\n\n\t// DefaultErrorHandler is the default error handler which is fired\n\t// when a function returns a non-nil error or a request-scoped dependency failed to binded.\n\tDefaultErrorHandler = ErrorHandlerFunc(func(ctx *context.Context, err error) {\n\t\tif err != ErrStopExecution {\n\t\t\tif status := ctx.GetStatusCode(); status == 0 || !context.StatusCodeNotSuccessful(status) {\n\t\t\t\tctx.StatusCode(DefaultErrStatusCode)\n\t\t\t}\n\n\t\t\t_, _ = ctx.WriteString(err.Error())\n\t\t}\n\n\t\tctx.StopExecution()\n\t})\n)\n\nvar (\n\tirisHandlerType     = reflect.TypeOf((*context.Handler)(nil)).Elem()\n\tirisHandlerFuncType = reflect.TypeOf(func(*context.Context) {})\n)\n\nfunc isIrisHandlerType(typ reflect.Type) bool {\n\treturn typ == irisHandlerType || typ == irisHandlerFuncType\n}\n\nfunc makeHandler(fn any, c *Container, paramsCount int) context.Handler {\n\tif fn == nil {\n\t\tpanic(\"makeHandler: function is nil\")\n\t}\n\n\t// 0. A normal handler.\n\tif handler, ok := isHandler(fn); ok {\n\t\treturn handler\n\t}\n\n\t// 1. A handler which returns just an error, handle it faster.\n\tif handlerWithErr, ok := isHandlerWithError(fn); ok {\n\t\treturn func(ctx *context.Context) {\n\t\t\tif err := handlerWithErr(ctx); err != nil {\n\t\t\t\tc.GetErrorHandler(ctx).HandleError(ctx, err)\n\t\t\t}\n\t\t}\n\t}\n\n\tv := valueOf(fn)\n\ttyp := v.Type()\n\tnumIn := typ.NumIn()\n\n\tbindings := getBindingsForFunc(v, c.Dependencies, c.DisablePayloadAutoBinding, paramsCount)\n\tc.fillReport(context.HandlerName(fn), bindings)\n\n\t// Check if it's a function that accept zero or more dependencies\n\t// and returns an Iris Handler.\n\tif paramsCount <= 0 {\n\t\t// println(irisHandlerType.String())\n\t\tif typ.NumOut() == 1 && isIrisHandlerType(typ.Out(0)) {\n\t\t\tinputs := getStaticInputs(bindings, numIn)\n\t\t\tif len(inputs) != numIn {\n\t\t\t\tpanic(fmt.Sprintf(\"makeHandler: func(...<T>) iris.Handler: expected %d function input parameters but fewer static dependencies matched (%d)\", numIn, len(inputs)))\n\t\t\t}\n\t\t\thandler := v.Call(inputs)[0].Interface().(context.Handler)\n\t\t\treturn handler\n\t\t}\n\t}\n\n\tresultHandler := defaultResultHandler\n\tfor i, lidx := 0, len(c.resultHandlers)-1; i <= lidx; i++ {\n\t\tresultHandler = c.resultHandlers[lidx-i](resultHandler)\n\t}\n\n\treturn func(ctx *context.Context) {\n\t\tinputs := make([]reflect.Value, numIn)\n\n\t\tfor _, binding := range bindings {\n\t\t\tinput, err := binding.Dependency.Handle(ctx, binding.Input)\n\t\t\tif err != nil {\n\t\t\t\tif err == ErrSeeOther {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\t// handled inside ErrorHandler.\n\t\t\t\t// else if err == ErrStopExecution {\n\t\t\t\t// \tctx.StopExecution()\n\t\t\t\t// \treturn // return without error.\n\t\t\t\t// }\n\n\t\t\t\tc.GetErrorHandler(ctx).HandleError(ctx, err)\n\t\t\t\t// return [13 Sep 2020, commented that in order to be able to\n\t\t\t\t// give end-developer the option not only to handle the error\n\t\t\t\t// but to skip it if necessary, example:\n\t\t\t\t// read form, unknown field, continue without StopWith,\n\t\t\t\t// the binder should bind the method's input argument and continue\n\t\t\t\t// without errors. See `mvc.TestErrorHandlerContinue` test.]\n\t\t\t}\n\n\t\t\t// If ~an error status code is set or~ execution has stopped\n\t\t\t// from within the dependency (something went wrong while validating the request),\n\t\t\t// then stop everything and let handler fire that status code.\n\t\t\tif ctx.IsStopped() /* || context.StatusCodeNotSuccessful(ctx.GetStatusCode())*/ {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tinputs[binding.Input.Index] = input\n\t\t}\n\n\t\t// fmt.Printf(\"For func: %s | valid input deps length(%d)\\n\", typ.String(), len(inputs))\n\t\t// for idx, in := range inputs {\n\t\t// \tfmt.Printf(\"[%d] (%s) %#+v\\n\", idx, in.Type().String(), in.Interface())\n\t\t// }\n\n\t\toutputs := v.Call(inputs)\n\t\tif err := dispatchFuncResult(ctx, outputs, resultHandler); err != nil {\n\t\t\tc.GetErrorHandler(ctx).HandleError(ctx, err)\n\t\t}\n\t}\n}\n\nfunc isHandler(fn any) (context.Handler, bool) {\n\tif handler, ok := fn.(context.Handler); ok {\n\t\treturn handler, ok\n\t}\n\n\tif handler, ok := fn.(func(*context.Context)); ok {\n\t\treturn handler, ok\n\t}\n\n\treturn nil, false\n}\n\nfunc isHandlerWithError(fn any) (func(*context.Context) error, bool) {\n\tif handlerWithErr, ok := fn.(func(*context.Context) error); ok {\n\t\treturn handlerWithErr, true\n\t}\n\n\treturn nil, false\n}\n"
  },
  {
    "path": "hero/handler_test.go",
    "content": "package hero_test\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12\"\n\t. \"github.com/kataras/iris/v12/hero\"\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\n// dynamic func\ntype testUserStruct struct {\n\tID       int64  `json:\"id\" form:\"id\" url:\"id\"`\n\tUsername string `json:\"username\" form:\"username\" url:\"username\"`\n}\n\nfunc testBinderFunc(ctx iris.Context) testUserStruct {\n\tid, _ := ctx.Params().GetInt64(\"id\")\n\tusername := ctx.Params().Get(\"username\")\n\treturn testUserStruct{\n\t\tID:       id,\n\t\tUsername: username,\n\t}\n}\n\n// service\ntype (\n\t// these testService and testServiceImpl could be in lowercase, unexported\n\t// but the `Say` method should be exported however we have those exported\n\t// because of the controller handler test.\n\ttestService interface {\n\t\tSay(string) string\n\t}\n\ttestServiceImpl struct {\n\t\tprefix string\n\t}\n)\n\nfunc (s *testServiceImpl) Say(message string) string {\n\treturn s.prefix + \" \" + message\n}\n\nvar (\n\t// binders, as user-defined\n\ttestBinderFuncUserStruct = testBinderFunc\n\ttestBinderService        = &testServiceImpl{prefix: \"say\"}\n\ttestBinderFuncParam      = func(ctx iris.Context) string {\n\t\treturn ctx.Params().Get(\"param\")\n\t}\n\n\t// consumers\n\t// a context as first input arg, which is not needed to be binded manually,\n\t// and a user struct which is binded to the input arg by the #1 func(ctx) any binder.\n\ttestConsumeUserHandler = func(ctx iris.Context, user testUserStruct) {\n\t\tctx.JSON(user)\n\t}\n\n\t// just one input arg, the service which is binded by the #2 service binder.\n\ttestConsumeServiceHandler = func(service testService) string {\n\t\treturn service.Say(\"something\")\n\t}\n\t// just one input arg, a standar string which is binded by the #3 func(ctx) any binder.\n\ttestConsumeParamHandler = func(myParam string) string {\n\t\treturn \"param is: \" + myParam\n\t}\n)\n\nfunc TestHandler(t *testing.T) {\n\tRegister(testBinderFuncUserStruct)\n\tRegister(testBinderService)\n\tRegister(testBinderFuncParam)\n\tvar (\n\t\th1 = Handler(testConsumeUserHandler)\n\t\th2 = Handler(testConsumeServiceHandler)\n\t\th3 = Handler(testConsumeParamHandler)\n\t)\n\n\ttestAppWithHeroHandlers(t, h1, h2, h3)\n}\n\nfunc testAppWithHeroHandlers(t *testing.T, h1, h2, h3 iris.Handler) {\n\tapp := iris.New()\n\tapp.Get(\"/{id:int64}/{username:string}\", h1)\n\tapp.Get(\"/service\", h2)\n\tapp.Get(\"/param/{param:string}\", h3)\n\n\texpectedUser := testUserStruct{\n\t\tID:       42,\n\t\tUsername: \"kataras\",\n\t}\n\n\te := httptest.New(t, app)\n\t// 1\n\te.GET(fmt.Sprintf(\"/%d/%s\", expectedUser.ID, expectedUser.Username)).Expect().Status(httptest.StatusOK).\n\t\tJSON().IsEqual(expectedUser)\n\t// 2\n\te.GET(\"/service\").Expect().Status(httptest.StatusOK).\n\t\tBody().IsEqual(\"say something\")\n\t// 3\n\te.GET(\"/param/the_param_value\").Expect().Status(httptest.StatusOK).\n\t\tBody().IsEqual(\"param is: the_param_value\")\n}\n\n// TestBindFunctionAsFunctionInputArgument tests to bind\n// a whole dynamic function based on the current context\n// as an input argument in the hero handler's function.\nfunc TestBindFunctionAsFunctionInputArgument(t *testing.T) {\n\tapp := iris.New()\n\tpostsBinder := func(ctx iris.Context) func(string) string {\n\t\treturn ctx.PostValue // or FormValue, the same here.\n\t}\n\n\th := New(postsBinder).Handler(func(get func(string) string) string {\n\t\t// send the `ctx.PostValue/FormValue(\"username\")` value\n\t\t// to the client.\n\t\treturn get(\"username\")\n\t})\n\n\tapp.Post(\"/\", h)\n\n\te := httptest.New(t, app)\n\n\texpectedUsername := \"kataras\"\n\te.POST(\"/\").WithFormField(\"username\", expectedUsername).\n\t\tExpect().Status(iris.StatusOK).Body().IsEqual(expectedUsername)\n}\n\nfunc TestPayloadBinding(t *testing.T) {\n\th := New()\n\n\tptrHandler := h.Handler(func(input *testUserStruct /* ptr */) string {\n\t\treturn input.Username\n\t})\n\n\tvalHandler := h.Handler(func(input testUserStruct) string {\n\t\treturn input.Username\n\t})\n\n\th.GetErrorHandler = func(iris.Context) ErrorHandler {\n\t\treturn ErrorHandlerFunc(func(ctx iris.Context, err error) {\n\t\t\tif iris.IsErrPath(err) {\n\t\t\t\treturn // continue.\n\t\t\t}\n\n\t\t\tctx.StopWithError(iris.StatusBadRequest, err)\n\t\t})\n\t}\n\n\tapp := iris.New()\n\tapp.Get(\"/\", ptrHandler)\n\tapp.Post(\"/\", ptrHandler)\n\tapp.Post(\"/2\", valHandler)\n\n\te := httptest.New(t, app)\n\n\t// JSON\n\te.POST(\"/\").WithJSON(iris.Map{\"username\": \"makis\"}).Expect().Status(httptest.StatusOK).Body().IsEqual(\"makis\")\n\te.POST(\"/2\").WithJSON(iris.Map{\"username\": \"kataras\"}).Expect().Status(httptest.StatusOK).Body().IsEqual(\"kataras\")\n\n\t// FORM (url-encoded)\n\te.POST(\"/\").WithFormField(\"username\", \"makis\").Expect().Status(httptest.StatusOK).Body().IsEqual(\"makis\")\n\t// FORM (multipart)\n\te.POST(\"/\").WithMultipart().WithFormField(\"username\", \"makis\").Expect().Status(httptest.StatusOK).Body().IsEqual(\"makis\")\n\t// FORM: test ErrorHandler skip the ErrPath.\n\te.POST(\"/\").WithMultipart().WithFormField(\"username\", \"makis\").WithFormField(\"unknown\", \"continue\").\n\t\tExpect().Status(httptest.StatusOK).Body().IsEqual(\"makis\")\n\n\t// POST URL query.\n\te.POST(\"/\").WithQuery(\"username\", \"makis\").Expect().Status(httptest.StatusOK).Body().IsEqual(\"makis\")\n\t// GET URL query.\n\te.GET(\"/\").WithQuery(\"username\", \"makis\").Expect().Status(httptest.StatusOK).Body().IsEqual(\"makis\")\n}\n\n/* Author's notes:\nIf aksed or required by my company, make the following test to pass but think downsides of code complexity and performance-cost\nbefore begin the implementation of it.\n- Dependencies without depending on other values can be named \"root-level dependencies\"\n- Dependencies could be linked (a new .DependsOn?) to a \"root-level dependency\"(or by theirs same-level deps too?) with much\n  more control if \"root-level dependencies\" are named, e.g.:\n\tb.Register(\"db\", &myDBImpl{})\n\tb.Register(\"user_dep\", func(db myDB) User{...}).DependsOn(\"db\")\n\tb.Handler(func(user User) error{...})\n\tb.Handler(func(ctx iris.Context, reuseDB myDB) {...})\nWhy linked over automatically? Because more than one dependency can implement the same input and\nend-user does not care about ordering the registered ones.\nLink with `DependsOn` SHOULD be optional, if exists then limit the available dependencies,\n`DependsOn` SHOULD accept comma-separated values, e.g. \"db, otherdep\" and SHOULD also work\nby calling it multiple times i.e `Depends(\"db\").DependsOn(\"otherdep\")`.\nHandlers should also be able to explicitly limit the list of\ntheir available dependencies per-handler, a `.DependsOn` feature SHOULD exist there too.\n\nAlso, note that with the new implementation a `*hero.Input` value can be accepted on dynamic dependencies,\nthat value contains an `Options.Dependencies` field which lists all the registered dependencies,\nso, in theory, end-developers could achieve same results by hand-code(inside the dependency's function body).\n\n26 Feb 2020. Gerasimos Maropoulos\n______________________________________________\n\n29 Feb 2020. It's done.\n*/\n\ntype testMessage struct {\n\tBody string\n}\n\ntype myMap map[string]*testMessage\n\nfunc TestDependentDependencies(t *testing.T) {\n\tb := New()\n\tb.Register(&testServiceImpl{prefix: \"prefix:\"})\n\tb.Register(func(service testService) testMessage {\n\t\treturn testMessage{Body: service.Say(\"it is a deep\") + \" dependency\"}\n\t})\n\tb.Register(myMap{\"test\": &testMessage{Body: \"value\"}})\n\tvar (\n\t\th1 = b.Handler(func(msg testMessage) string {\n\t\t\treturn msg.Body\n\t\t})\n\t\th2 = b.Handler(func(reuse testService) string {\n\t\t\treturn reuse.Say(\"message\")\n\t\t})\n\t\th3 = b.Handler(func(m myMap) string {\n\t\t\treturn m[\"test\"].Body\n\t\t})\n\t)\n\n\tapp := iris.New()\n\tapp.Get(\"/h1\", h1)\n\tapp.Get(\"/h2\", h2)\n\tapp.Get(\"/h3\", h3)\n\n\te := httptest.New(t, app)\n\te.GET(\"/h1\").Expect().Status(httptest.StatusOK).Body().IsEqual(\"prefix: it is a deep dependency\")\n\te.GET(\"/h2\").Expect().Status(httptest.StatusOK).Body().IsEqual(\"prefix: message\")\n\te.GET(\"/h3\").Expect().Status(httptest.StatusOK).Body().IsEqual(\"value\")\n}\n\nfunc TestHandlerPathParams(t *testing.T) {\n\t// See white box `TestPathParams` test too.\n\t// All cases should pass.\n\tapp := iris.New()\n\thandler := func(id uint64) string {\n\t\treturn fmt.Sprintf(\"%d\", id)\n\t}\n\n\tapp.Party(\"/users\").ConfigureContainer(func(api *iris.APIContainer) {\n\t\tapi.Get(\"/{id:uint64}\", handler)\n\t})\n\n\tapp.Party(\"/editors/{id:uint64}\").ConfigureContainer(func(api *iris.APIContainer) {\n\t\tapi.Get(\"/\", handler)\n\t})\n\n\t// should receive the last one, as we expected only one useful for MVC (there is a similar test there too).\n\tapp.ConfigureContainer().Get(\"/{ownerID:uint64}/book/{booKID:uint64}\", handler)\n\n\te := httptest.New(t, app)\n\n\tfor _, testReq := range []*httptest.Request{\n\t\te.GET(\"/users/42\"),\n\t\te.GET(\"/editors/42\"),\n\t\te.GET(\"/1/book/42\"),\n\t} {\n\t\ttestReq.Expect().Status(httptest.StatusOK).Body().IsEqual(\"42\")\n\t}\n}\n\nfunc TestRegisterDependenciesFromContext(t *testing.T) {\n\t// Tests serve-time struct dependencies through a common Iris middleware.\n\tapp := iris.New()\n\tapp.Use(func(ctx iris.Context) {\n\t\tctx.RegisterDependency(testUserStruct{Username: \"kataras\"})\n\t\tctx.Next()\n\t})\n\tapp.Use(func(ctx iris.Context) {\n\t\tctx.RegisterDependency(&testServiceImpl{prefix: \"say\"})\n\t\tctx.Next()\n\t})\n\n\tapp.ConfigureContainer(func(api *iris.APIContainer) {\n\t\tapi.Get(\"/\", func(u testUserStruct) string {\n\t\t\treturn u.Username\n\t\t})\n\n\t\tapi.Get(\"/service\", func(s *testServiceImpl) string {\n\t\t\treturn s.Say(\"hello\")\n\t\t})\n\n\t\t// Note: we are not allowed to pass the service as an interface here\n\t\t// because the container will, correctly, panic because it will expect\n\t\t// a dependency to be registered before server ran.\n\t\tapi.Get(\"/both\", func(s *testServiceImpl, u testUserStruct) string {\n\t\t\treturn s.Say(u.Username)\n\t\t})\n\n\t\tapi.Get(\"/non\", func() string {\n\t\t\treturn \"nothing\"\n\t\t})\n\t})\n\n\te := httptest.New(t, app)\n\n\te.GET(\"/\").Expect().Status(httptest.StatusOK).Body().IsEqual(\"kataras\")\n\te.GET(\"/service\").Expect().Status(httptest.StatusOK).Body().IsEqual(\"say hello\")\n\te.GET(\"/both\").Expect().Status(httptest.StatusOK).Body().IsEqual(\"say kataras\")\n\te.GET(\"/non\").Expect().Status(httptest.StatusOK).Body().IsEqual(\"nothing\")\n}\n"
  },
  {
    "path": "hero/param_test.go",
    "content": "package hero\n\nimport (\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/context\"\n)\n\nfunc TestPathParams(t *testing.T) {\n\tgot := \"\"\n\th := New()\n\thandler := h.Handler(func(firstname string, lastname string) {\n\t\tgot = firstname + lastname\n\t})\n\n\th.Register(func(ctx *context.Context) func() string { return func() string { return \"\" } })\n\thandlerWithOther := h.Handler(func(f func() string, firstname string, lastname string) {\n\t\tgot = f() + firstname + lastname\n\t})\n\n\thandlerWithOtherBetweenThem := h.Handler(func(firstname string, f func() string, lastname string) {\n\t\tgot = firstname + lastname\n\t})\n\n\tctx := context.NewContext(nil)\n\tctx.Params().Set(\"firstname\", \"Gerasimos\")\n\tctx.Params().Set(\"lastname\", \"Maropoulos\")\n\thandler(ctx)\n\texpected := \"GerasimosMaropoulos\"\n\tif got != expected {\n\t\tt.Fatalf(\"[0] expected the params 'firstname' + 'lastname' to be '%s' but got '%s'\", expected, got)\n\t}\n\n\tgot = \"\"\n\thandlerWithOther(ctx)\n\texpected = \"GerasimosMaropoulos\"\n\tif got != expected {\n\t\tt.Fatalf(\"[1] expected the params 'firstname' + 'lastname' to be '%s' but got '%s'\", expected, got)\n\t}\n\n\tgot = \"\"\n\thandlerWithOtherBetweenThem(ctx)\n\texpected = \"GerasimosMaropoulos\"\n\tif got != expected {\n\t\tt.Fatalf(\"[2] expected the params 'firstname' + 'lastname' to be '%s' but got '%s'\", expected, got)\n\t}\n}\n"
  },
  {
    "path": "hero/reflect.go",
    "content": "package hero\n\nimport (\n\t\"net\"\n\t\"reflect\"\n\n\t\"github.com/kataras/iris/v12/context\"\n)\n\nfunc valueOf(v any) reflect.Value {\n\tif val, ok := v.(reflect.Value); ok {\n\t\t// check if it's already a reflect.Value.\n\t\treturn val\n\t}\n\n\treturn reflect.ValueOf(v)\n}\n\n// indirectType returns the value of a pointer-type \"typ\".\n// If \"typ\" is a pointer, array, chan, map or slice it returns its Elem,\n// otherwise returns the \"typ\" as it is.\nfunc indirectType(typ reflect.Type) reflect.Type {\n\tswitch typ.Kind() {\n\tcase reflect.Ptr, reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:\n\t\treturn typ.Elem()\n\t}\n\treturn typ\n}\n\nfunc goodVal(v reflect.Value) bool {\n\tswitch v.Kind() {\n\tcase reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Slice:\n\t\tif v.IsNil() {\n\t\t\treturn false\n\t\t}\n\t}\n\n\treturn v.IsValid()\n}\n\nfunc isFunc(kindable interface{ Kind() reflect.Kind }) bool {\n\treturn kindable.Kind() == reflect.Func\n}\n\nfunc isStructValue(v reflect.Value) bool {\n\treturn indirectType(v.Type()).Kind() == reflect.Struct\n}\n\n// isBuiltin reports whether a reflect.Value is a builtin type\nfunc isBuiltinValue(v reflect.Value) bool {\n\tswitch v.Type().Kind() {\n\tcase reflect.Bool,\n\t\treflect.Int,\n\t\treflect.Int8,\n\t\treflect.Int16,\n\t\treflect.Int32,\n\t\treflect.Int64,\n\t\treflect.Uint,\n\t\treflect.Uint8,\n\t\treflect.Uint16,\n\t\treflect.Uint32,\n\t\treflect.Uint64,\n\t\treflect.Float32,\n\t\treflect.Float64,\n\t\treflect.Complex64,\n\t\treflect.Complex128,\n\t\treflect.Array,\n\t\treflect.Chan,\n\t\treflect.Map,\n\t\treflect.Slice,\n\t\treflect.String:\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\nvar (\n\tinputTyp = reflect.TypeOf((*Input)(nil))\n\terrTyp   = reflect.TypeOf((*error)(nil)).Elem()\n\n\tipTyp = reflect.TypeOf(net.IP{})\n)\n\n// isError returns true if \"typ\" is type of `error`.\nfunc isError(typ reflect.Type) bool {\n\treturn typ.Implements(errTyp)\n}\n\nfunc toError(v reflect.Value) error {\n\tif v.IsNil() {\n\t\treturn nil\n\t}\n\n\treturn v.Interface().(error)\n}\n\nvar contextType = reflect.TypeOf((*context.Context)(nil))\n\n// isContext returns true if the \"typ\" is a type of Context.\nfunc isContext(typ reflect.Type) bool {\n\treturn typ == contextType\n}\n\nvar errorHandlerTyp = reflect.TypeOf((*ErrorHandler)(nil)).Elem()\n\nfunc isErrorHandler(typ reflect.Type) bool {\n\treturn typ.Implements(errorHandlerTyp)\n}\n\nvar emptyValue reflect.Value\n\nfunc equalTypes(binding reflect.Type, input reflect.Type) bool {\n\tif binding == input {\n\t\treturn true\n\t}\n\n\t// fmt.Printf(\"got: %s expected: %s\\n\", got.String(), expected.String())\n\t// if accepts an interface, check if the given \"got\" type does\n\t// implement this \"expected\" user handler's input argument.\n\tif input.Kind() == reflect.Interface {\n\t\t// fmt.Printf(\"expected interface = %s and got to set on the arg is: %s\\n\", binding.String(), input.String())\n\t\t// return input.Implements(binding)\n\t\treturn binding.AssignableTo(input)\n\t}\n\n\t// dependency: func(...) any { return \"string\" }\n\t// expected input: string.\n\tif binding.Kind() == reflect.Interface {\n\t\treturn input.AssignableTo(binding)\n\t}\n\n\treturn false\n}\n\nfunc structFieldIgnored(f reflect.StructField) bool {\n\tif !f.Anonymous {\n\t\treturn true // if not anonymous(embedded), ignore it.\n\t}\n\n\tif s := f.Tag.Get(\"ignore\"); s == \"true\" {\n\t\treturn true\n\t}\n\n\tif s := f.Tag.Get(\"stateless\"); s == \"true\" {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// all except non-zero.\nfunc lookupFields(elem reflect.Value, skipUnexported bool, onlyZeros bool, parentIndex []int) (fields []reflect.StructField, stateless int) {\n\t// Note: embedded pointers are not supported.\n\t// elem = reflect.Indirect(elem)\n\telemTyp := elem.Type()\n\tif elemTyp.Kind() == reflect.Pointer {\n\t\treturn\n\t}\n\n\tfor i, n := 0, elem.NumField(); i < n; i++ {\n\t\tfield := elemTyp.Field(i)\n\t\tfieldValue := elem.Field(i)\n\n\t\t// embed any fields from other structs.\n\t\tif indirectType(field.Type).Kind() == reflect.Struct {\n\t\t\tif structFieldIgnored(field) {\n\t\t\t\tstateless++ // don't skip the loop yet, e.g. iris.Context.\n\t\t\t} else {\n\t\t\t\tstructFields, statelessN := lookupFields(fieldValue, skipUnexported, onlyZeros, append(parentIndex, i))\n\t\t\t\tstateless += statelessN\n\t\t\t\tfields = append(fields, structFields...)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\tif onlyZeros && !isZero(fieldValue) {\n\t\t\tcontinue\n\t\t}\n\n\t\t// skip unexported fields here.\n\t\tif isExported := field.PkgPath == \"\"; skipUnexported && !isExported {\n\t\t\tcontinue\n\t\t}\n\n\t\tindex := []int{i}\n\t\tif len(parentIndex) > 0 {\n\t\t\tindex = append(parentIndex, i)\n\t\t}\n\n\t\ttmp := make([]int, len(index))\n\t\tcopy(tmp, index)\n\t\tfield.Index = tmp\n\n\t\tfields = append(fields, field)\n\t}\n\n\treturn\n}\n\nfunc lookupNonZeroFieldValues(elem reflect.Value) (nonZeroFields []reflect.StructField) {\n\tfields, _ := lookupFields(elem, true, false, nil)\n\tfor _, f := range fields {\n\t\tif structFieldIgnored(f) {\n\t\t\tcontinue // re-check here for ignored struct fields so we don't include them on dependencies. Non-zeroes fields can be static, even if they are functions.\n\t\t}\n\t\tif fieldVal := elem.FieldByIndex(f.Index); goodVal(fieldVal) && !isZero(fieldVal) {\n\t\t\t/* && f.Type.Kind() == reflect.Ptr &&*/\n\t\t\tnonZeroFields = append(nonZeroFields, f)\n\t\t}\n\t}\n\n\treturn\n}\n\n// isZero returns true if a value is nil.\n// Remember; fields to be checked should be exported otherwise it returns false.\n// Notes for users:\n// Boolean's zero value is false, even if not set-ed.\n// UintXX are not zero on 0 because they are pointers to.\nfunc isZero(v reflect.Value) bool {\n\t// switch v.Kind() {\n\t// case reflect.Struct:\n\t// \tzero := true\n\t// \tfor i := 0; i < v.NumField(); i++ {\n\t// \t\tf := v.Field(i)\n\t// \t\tif f.Type().PkgPath() != \"\" {\n\t// \t\t\tcontinue // unexported.\n\t// \t\t}\n\t// \t\tzero = zero && isZero(f)\n\t// \t}\n\n\t// \tif typ := v.Type(); typ != nil && v.IsValid() {\n\t// \t\tf, ok := typ.MethodByName(\"IsZero\")\n\t// \t\t// if not found\n\t// \t\t// if has input arguments (1 is for the value receiver, so > 1 for the actual input args)\n\t// \t\t// if output argument is not boolean\n\t// \t\t// then skip this IsZero user-defined function.\n\t// \t\tif !ok || f.Type.NumIn() > 1 || f.Type.NumOut() != 1 && f.Type.Out(0).Kind() != reflect.Bool {\n\t// \t\t\treturn zero\n\t// \t\t}\n\n\t// \t\tmethod := v.Method(f.Index)\n\t// \t\t// no needed check but:\n\t// \t\tif method.IsValid() && !method.IsNil() {\n\t// \t\t\t// it shouldn't panic here.\n\t// \t\t\tzero = method.Call([]reflect.Value{})[0].Interface().(bool)\n\t// \t\t}\n\t// \t}\n\n\t// \treturn zero\n\t// case reflect.Func, reflect.Map, reflect.Slice:\n\t// \treturn v.IsNil()\n\t// case reflect.Array:\n\t// \tzero := true\n\t// \tfor i := 0; i < v.Len(); i++ {\n\t// \t\tzero = zero && isZero(v.Index(i))\n\t// \t}\n\t// \treturn zero\n\t// }\n\t// if not any special type then use the reflect's .Zero\n\t// usually for fields, but remember if it's boolean and it's false\n\t// then it's zero, even if set-ed.\n\n\tif !v.CanInterface() {\n\t\t// if can't interface, i.e return value from unexported field or method then return false\n\t\treturn false\n\t}\n\n\tif v.Type() == ipTyp {\n\t\treturn len(v.Interface().(net.IP)) == 0\n\t}\n\n\t// zero := reflect.Zero(v.Type())\n\t// return v.Interface() == zero.Interface()\n\n\treturn v.IsZero()\n}\n\n// IsNil same as `reflect.IsNil` but a bit safer to use, returns false if not a correct type.\nfunc isNil(v reflect.Value) bool {\n\tk := v.Kind()\n\tswitch k {\n\tcase reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.UnsafePointer, reflect.Interface, reflect.Slice:\n\t\treturn v.IsNil()\n\tdefault:\n\t\treturn false\n\t}\n}\n"
  },
  {
    "path": "hero/reflect_test.go",
    "content": "package hero\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\ntype testInterface interface {\n\tGet() string\n}\n\nvar testInterfaceTyp = reflect.TypeOf((*testInterface)(nil)).Elem()\n\ntype testImplPtr struct{}\n\nfunc (*testImplPtr) Get() string { return \"get_ptr\" }\n\ntype testImpl struct{}\n\nfunc (testImpl) Get() string { return \"get\" }\n\nfunc TestEqualTypes(t *testing.T) {\n\tof := reflect.TypeOf\n\n\tvar tests = map[reflect.Type]reflect.Type{\n\t\tof(\"string\"):         of(\"input\"),\n\t\tof(42):               of(10),\n\t\ttestInterfaceTyp:     testInterfaceTyp,\n\t\tof(new(testImplPtr)): testInterfaceTyp,\n\t\tof(new(testImpl)):    testInterfaceTyp,\n\t\tof(testImpl{}):       testInterfaceTyp,\n\t}\n\n\tfor binding, input := range tests {\n\t\tif !equalTypes(binding, input) {\n\t\t\tt.Fatalf(\"expected type of: %s to be equal to the binded one of: %s\", input, binding)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "hero/struct.go",
    "content": "package hero\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\n\t\"github.com/kataras/iris/v12/context\"\n)\n\n// Sorter is the type for sort customization of a struct's fields\n// and its available bindable values.\n//\n// Sorting applies only when a field can accept more than one registered value.\ntype Sorter func(t1 reflect.Type, t2 reflect.Type) bool\n\n// sortByNumMethods is a builtin sorter to sort fields and values\n// based on their type and its number of methods, highest number of methods goes first.\n//\n// It is the default sorter on struct injector of `hero.Struct` method.\nvar sortByNumMethods Sorter = func(t1 reflect.Type, t2 reflect.Type) bool {\n\tif t1.Kind() != t2.Kind() {\n\t\treturn true\n\t}\n\n\tif k := t1.Kind(); k == reflect.Interface || k == reflect.Struct {\n\t\treturn t1.NumMethod() > t2.NumMethod()\n\t} else if k != reflect.Struct {\n\t\treturn false // non-structs goes last.\n\t}\n\n\treturn true\n}\n\n// Struct keeps a record of a particular struct value injection.\n// See `Container.Struct` and `mvc#Application.Handle` methods.\ntype Struct struct {\n\tptrType     reflect.Type\n\tptrValue    reflect.Value // the original ptr struct value.\n\telementType reflect.Type  // the original struct type.\n\tbindings    []*binding    // struct field bindings.\n\n\tContainer *Container\n\tSingleton bool\n}\n\ntype singletonStruct interface {\n\tSingleton() bool\n}\n\nfunc isMarkedAsSingleton(structPtr any) bool {\n\tif sing, ok := structPtr.(singletonStruct); ok && sing.Singleton() {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\nfunc makeStruct(structPtr any, c *Container, partyParamsCount int) *Struct {\n\tv := valueOf(structPtr)\n\ttyp := v.Type()\n\tif typ.Kind() != reflect.Ptr || indirectType(typ).Kind() != reflect.Struct {\n\t\tpanic(\"binder: struct: should be a pointer to a struct value\")\n\t}\n\n\tisSingleton := isMarkedAsSingleton(structPtr)\n\n\tdisablePayloadAutoBinding := c.DisablePayloadAutoBinding\n\tenableStructDependents := c.EnableStructDependents\n\tdisableStructDynamicBindings := c.DisableStructDynamicBindings\n\tif isSingleton {\n\t\tdisablePayloadAutoBinding = true\n\t\tenableStructDependents = false\n\t\tdisableStructDynamicBindings = true\n\t}\n\n\t// get struct's fields bindings.\n\tbindings := getBindingsForStruct(v, c.Dependencies, c.MarkExportedFieldsAsRequired, disablePayloadAutoBinding, enableStructDependents, c.DependencyMatcher, partyParamsCount, c.Sorter)\n\n\t// length bindings of 0, means that it has no fields or all mapped deps are static.\n\t// If static then Struct.Acquire will return the same \"value\" instance, otherwise it will create a new one.\n\tsingleton := true\n\telem := v.Elem()\n\n\t// fmt.Printf(\"Service: %s, Bindings(%d):\\n\", typ, len(bindings))\n\tfor _, b := range bindings {\n\t\t// fmt.Printf(\"* \" + b.String() + \"\\n\")\n\t\tif b.Dependency.Static {\n\t\t\t// Fill now.\n\t\t\tinput, err := b.Dependency.Handle(nil, b.Input)\n\t\t\tif err != nil {\n\t\t\t\tif err == ErrSeeOther {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tpanic(err)\n\t\t\t}\n\n\t\t\telem.FieldByIndex(b.Input.StructFieldIndex).Set(input)\n\t\t} else if !b.Dependency.Static {\n\t\t\tif disableStructDynamicBindings {\n\t\t\t\tpanic(fmt.Sprintf(\"binder: DisableStructDynamicBindings setting is set to true: dynamic binding found: %s\", b.String()))\n\t\t\t}\n\n\t\t\tsingleton = false\n\t\t}\n\t}\n\n\tif isSingleton && !singleton {\n\t\tpanic(fmt.Sprintf(\"binder: Singleton setting is set to true but struct has dynamic bindings: %s\", typ))\n\t}\n\n\ts := &Struct{\n\t\tptrValue:    v,\n\t\tptrType:     typ,\n\t\telementType: elem.Type(),\n\t\tbindings:    bindings,\n\t\tSingleton:   singleton,\n\t}\n\n\tisErrHandler := isErrorHandler(typ)\n\tnewContainer := c.Clone()\n\tnewContainer.fillReport(typ.String(), bindings)\n\t// Add the controller dependency itself as func dependency but with a known type which should be explicit binding\n\t// in order to keep its maximum priority.\n\tnewContainer.Register(s.Acquire).Explicitly().DestType = typ\n\n\tnewContainer.GetErrorHandler = func(ctx *context.Context) ErrorHandler {\n\t\tif isErrHandler {\n\t\t\treturn ctx.Controller().Interface().(ErrorHandler)\n\t\t}\n\n\t\treturn c.GetErrorHandler(ctx)\n\t}\n\n\ts.Container = newContainer\n\treturn s\n}\n\n// Acquire returns a struct value based on the request.\n// If the dependencies are all static then these are already set-ed at the initialization of this Struct\n// and the same struct value instance will be returned, ignoring the Context. Otherwise\n// a new struct value with filled fields by its pre-calculated bindings will be returned instead.\nfunc (s *Struct) Acquire(ctx *context.Context) (reflect.Value, error) {\n\tif s.Singleton {\n\t\tctx.Values().Set(context.ControllerContextKey, s.ptrValue)\n\t\treturn s.ptrValue, nil\n\t}\n\n\tctrl := ctx.Controller()\n\tif ctrl.Kind() == reflect.Invalid ||\n\t\tctrl.Type() != s.ptrType /* in case of changing controller in the same request (see RouteOverlap feature) */ {\n\t\tctrl = reflect.New(s.elementType)\n\t\tctx.Values().Set(context.ControllerContextKey, ctrl)\n\t\telem := ctrl.Elem()\n\t\tfor _, b := range s.bindings {\n\t\t\tinput, err := b.Dependency.Handle(ctx, b.Input)\n\t\t\tif err != nil {\n\t\t\t\tif err == ErrSeeOther {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\ts.Container.GetErrorHandler(ctx).HandleError(ctx, err)\n\n\t\t\t\tif ctx.IsStopped() {\n\t\t\t\t\t// return emptyValue, err\n\t\t\t\t\treturn ctrl, err\n\t\t\t\t} // #1629\n\t\t\t}\n\n\t\t\telem.FieldByIndex(b.Input.StructFieldIndex).Set(input)\n\t\t}\n\t}\n\n\treturn ctrl, nil\n}\n\n// MethodHandler accepts a \"methodName\" that should be a valid an exported\n// method of the struct and returns its converted Handler.\n//\n// Second input is optional,\n// even zero is a valid value and can resolve path parameters correctly if from root party.\nfunc (s *Struct) MethodHandler(methodName string, paramsCount int) context.Handler {\n\tm, ok := s.ptrValue.Type().MethodByName(methodName)\n\tif !ok {\n\t\tpanic(fmt.Sprintf(\"struct: method: %s does not exist\", methodName))\n\t}\n\n\treturn makeHandler(m.Func, s.Container, paramsCount)\n}\n"
  },
  {
    "path": "hero/struct_test.go",
    "content": "package hero_test\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12\"\n\t. \"github.com/kataras/iris/v12/hero\"\n\t\"github.com/kataras/iris/v12/httptest\"\n)\n\ntype testStruct struct {\n\tCtx iris.Context\n}\n\nfunc (c *testStruct) MyHandler(name string) testOutput {\n\treturn fn(42, testInput{Name: name})\n}\n\nfunc (c *testStruct) MyHandler2(id int, in testInput) testOutput {\n\treturn fn(id, in)\n}\n\nfunc (c *testStruct) MyHandler3(in testInput) testOutput {\n\treturn fn(42, in)\n}\n\nfunc (c *testStruct) MyHandler4() {\n\tc.Ctx.WriteString(\"MyHandler4\")\n}\n\nfunc TestStruct(t *testing.T) {\n\tapp := iris.New()\n\n\tb := New()\n\ts := b.Struct(&testStruct{}, 0)\n\n\tpostHandler := s.MethodHandler(\"MyHandler\", 0) // fallbacks such as {path} and {string} should registered first when same path.\n\tapp.Post(\"/{name:string}\", postHandler)\n\tpostHandler2 := s.MethodHandler(\"MyHandler2\", 0)\n\tapp.Post(\"/{id:int}\", postHandler2)\n\tpostHandler3 := s.MethodHandler(\"MyHandler3\", 0)\n\tapp.Post(\"/myHandler3\", postHandler3)\n\tgetHandler := s.MethodHandler(\"MyHandler4\", 0)\n\tapp.Get(\"/myHandler4\", getHandler)\n\n\te := httptest.New(t, app)\n\te.POST(\"/\" + input.Name).Expect().Status(httptest.StatusOK).JSON().IsEqual(expectedOutput)\n\tpath := fmt.Sprintf(\"/%d\", expectedOutput.ID)\n\te.POST(path).WithJSON(input).Expect().Status(httptest.StatusOK).JSON().IsEqual(expectedOutput)\n\te.POST(\"/myHandler3\").WithJSON(input).Expect().Status(httptest.StatusOK).JSON().IsEqual(expectedOutput)\n\te.GET(\"/myHandler4\").Expect().Status(httptest.StatusOK).Body().IsEqual(\"MyHandler4\")\n}\n\ntype testStructErrorHandler struct{}\n\nfunc (s *testStructErrorHandler) HandleError(ctx iris.Context, err error) {\n\tctx.StopWithError(httptest.StatusConflict, err)\n}\n\nfunc (s *testStructErrorHandler) Handle(errText string) error {\n\treturn errors.New(errText)\n}\n\nfunc TestStructErrorHandler(t *testing.T) {\n\tb := New()\n\ts := b.Struct(&testStructErrorHandler{}, 0)\n\n\tapp := iris.New()\n\tapp.Get(\"/{errText:string}\", s.MethodHandler(\"Handle\", 0))\n\n\texpectedErrText := \"an error\"\n\te := httptest.New(t, app)\n\te.GET(\"/\" + expectedErrText).Expect().Status(httptest.StatusConflict).Body().IsEqual(expectedErrText)\n}\n\ntype (\n\ttestServiceInterface1 interface {\n\t\tParse() string\n\t}\n\n\ttestServiceImpl1 struct {\n\t\tinner string\n\t}\n\n\ttestServiceInterface2 interface {\n\t}\n\n\ttestServiceImpl2 struct {\n\t\ttf int\n\t}\n\n\ttestControllerDependenciesSorter struct {\n\t\tService2 testServiceInterface2\n\t\tService1 testServiceInterface1\n\t}\n)\n\nfunc (s *testServiceImpl1) Parse() string {\n\treturn s.inner\n}\n\nfunc (c *testControllerDependenciesSorter) Index() string {\n\treturn fmt.Sprintf(\"%#+v | %#+v\", c.Service1, c.Service2)\n}\n\nfunc TestStructFieldsSorter(t *testing.T) { // see https://github.com/kataras/iris/issues/1343\n\tb := New()\n\tb.Register(&testServiceImpl1{\"parser\"})\n\tb.Register(&testServiceImpl2{24})\n\ts := b.Struct(&testControllerDependenciesSorter{}, 0)\n\n\tapp := iris.New()\n\tapp.Get(\"/\", s.MethodHandler(\"Index\", 0))\n\n\te := httptest.New(t, app)\n\n\texpectedBody := `&hero_test.testServiceImpl1{inner:\"parser\"} | &hero_test.testServiceImpl2{tf:24}`\n\te.GET(\"/\").Expect().Status(httptest.StatusOK).Body().IsEqual(expectedBody)\n}\n"
  },
  {
    "path": "httptest/aliases.go",
    "content": "package httptest\n\nimport \"github.com/iris-contrib/httpexpect/v2\"\n\ntype (\n\t// Request type alias.\n\tRequest = httpexpect.Request\n\t// Expect type alias.\n\tExpect = httpexpect.Expect\n)\n"
  },
  {
    "path": "httptest/httptest.go",
    "content": "package httptest\n\nimport (\n\t\"crypto/tls\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/core/router\"\n\t\"github.com/kataras/iris/v12/i18n\"\n\n\t\"github.com/iris-contrib/httpexpect/v2\"\n)\n\ntype (\n\t// OptionSetter sets a configuration field to the configuration\n\tOptionSetter interface {\n\t\t// Set receives a pointer to the Configuration type and does the job of filling it\n\t\tSet(c *Configuration)\n\t}\n\t// OptionSet implements the OptionSetter\n\tOptionSet func(c *Configuration)\n)\n\n// Set is the func which makes the OptionSet an OptionSetter, this is used mostly\nfunc (o OptionSet) Set(c *Configuration) {\n\to(c)\n}\n\n// Configuration httptest configuration\ntype Configuration struct {\n\t// URL the base url.\n\t// Defaults to empty string \"\".\n\tURL string\n\t// Debug if true then debug messages from the httpexpect will be shown when a test runs\n\t// Defaults to false.\n\tDebug bool\n\t// LogLevel sets the application's log level.\n\t// Defaults to \"disable\" when testing.\n\tLogLevel string\n\n\t// If true then the underline httpexpect report will be acquired by the NewRequireReporter\n\t// call instead of the default NewAssertReporter.\n\t// Defaults to false.\n\tStrict bool // Note: if more reports are available in the future then add a Reporter interface as a field.\n}\n\n// Set implements the OptionSetter for the Configuration itself\nfunc (c Configuration) Set(main *Configuration) {\n\tmain.URL = c.URL\n\tmain.Debug = c.Debug\n\tif c.LogLevel != \"\" {\n\t\tmain.LogLevel = c.LogLevel\n\t}\n\tmain.Strict = c.Strict\n}\n\nvar (\n\t// URL if set then it sets the httptest's BaseURL.\n\t// Defaults to empty string \"\".\n\tURL = func(schemeAndHost string) OptionSet {\n\t\treturn func(c *Configuration) {\n\t\t\tc.URL = schemeAndHost\n\t\t}\n\t}\n\t// Debug if true then debug messages from the httpexpect will be shown when a test runs\n\t// Defaults to false.\n\tDebug = func(val bool) OptionSet {\n\t\treturn func(c *Configuration) {\n\t\t\tc.Debug = val\n\t\t}\n\t}\n\n\t// LogLevel sets the application's log level.\n\t// Defaults to disabled when testing.\n\tLogLevel = func(level string) OptionSet {\n\t\treturn func(c *Configuration) {\n\t\t\tc.LogLevel = level\n\t\t}\n\t}\n\n\t// Strict sets the Strict configuration field to \"val\".\n\t// Applies the NewRequireReporter instead of the default one.\n\t// Use this if you want the test to fail on first error, before all checks have been done.\n\tStrict = func(val bool) OptionSet {\n\t\treturn func(c *Configuration) {\n\t\t\tc.Strict = val\n\t\t}\n\t}\n)\n\n// DefaultConfiguration returns the default configuration for the httptest.\nfunc DefaultConfiguration() *Configuration {\n\treturn &Configuration{URL: \"\", Debug: false, LogLevel: \"disable\"}\n}\n\n// New Prepares and returns a new test framework based on the \"app\".\n// Usage:\n//\n//\thttptest.New(t, app)\n//\n// With options:\n//\n//\thttptest.New(t, app, httptest.URL(...), httptest.Debug(true), httptest.LogLevel(\"debug\"), httptest.Strict(true))\n//\n// Examples at: https://github.com/kataras/iris/tree/main/_examples/testing/httptest and\n// https://github.com/kataras/iris/tree/main/_examples/testing/ginkgotest.\nfunc New(t IrisTesty, app *iris.Application, setters ...OptionSetter) *httpexpect.Expect {\n\tconf := DefaultConfiguration()\n\tfor _, setter := range setters {\n\t\tsetter.Set(conf)\n\t}\n\n\t// set the logger or disable it (default).\n\tapp.Logger().SetLevel(conf.LogLevel)\n\n\tif err := app.Build(); err != nil {\n\t\tif conf.LogLevel != \"disable\" {\n\t\t\tapp.Logger().Println(err.Error())\n\t\t\treturn nil\n\t\t}\n\t}\n\n\tvar reporter httpexpect.Reporter\n\n\tif conf.Strict {\n\t\treporter = httpexpect.NewRequireReporter(t)\n\t} else {\n\t\treporter = httpexpect.NewAssertReporter(t)\n\t}\n\n\ttestConfiguration := httpexpect.Config{\n\t\tBaseURL: conf.URL,\n\t\tClient: &http.Client{\n\t\t\tTransport: httpexpect.NewBinder(app),\n\t\t\tJar:       httpexpect.NewCookieJar(),\n\t\t},\n\t\tReporter: reporter,\n\t}\n\n\tif conf.Debug {\n\t\ttestConfiguration.Printers = []httpexpect.Printer{\n\t\t\thttpexpect.NewDebugPrinter(t, true),\n\t\t}\n\t}\n\n\treturn httpexpect.WithConfig(testConfiguration)\n}\n\n// NewInsecure same as New but receives a single host instead of the whole framework.\n// Useful for testing running TLS servers.\nfunc NewInsecure(t IrisTesty, setters ...OptionSetter) *httpexpect.Expect {\n\tconf := DefaultConfiguration()\n\tfor _, setter := range setters {\n\t\tsetter.Set(conf)\n\t}\n\ttransport := &http.Transport{\n\t\tTLSClientConfig: &tls.Config{InsecureSkipVerify: true, MinVersion: tls.VersionTLS13}, // lint:ignore\n\t}\n\n\ttestConfiguration := httpexpect.Config{\n\t\tBaseURL: conf.URL,\n\t\tClient: &http.Client{\n\t\t\tTransport: transport,\n\t\t\tJar:       httpexpect.NewCookieJar(),\n\t\t},\n\t\tReporter: httpexpect.NewAssertReporter(t),\n\t}\n\n\tif conf.Debug {\n\t\ttestConfiguration.Printers = []httpexpect.Printer{\n\t\t\thttpexpect.NewDebugPrinter(t, true),\n\t\t}\n\t}\n\n\treturn httpexpect.WithConfig(testConfiguration)\n}\n\n// Aliases for \"net/http/httptest\" package. See `Do` package-level function.\nvar (\n\tNewRecorder = httptest.NewRecorder\n\tNewRequest  = httptest.NewRequest\n)\n\n// Do is a simple helper which can be used to test handlers individually\n// with the \"net/http/httptest\" package.\n// This package contains aliases for `NewRequest` and `NewRecorder` too.\n//\n// For a more efficient testing please use the `New` function instead.\nfunc Do(w http.ResponseWriter, r *http.Request, handler iris.Handler, irisConfigurators ...iris.Configurator) {\n\tapp := new(iris.Application)\n\tapp.I18n = i18n.New()\n\tapp.Configure(iris.WithConfiguration(iris.DefaultConfiguration()), iris.WithLogLevel(\"disable\"))\n\tapp.Configure(irisConfigurators...)\n\n\tapp.HTTPErrorHandler = router.NewDefaultHandler(app.ConfigurationReadOnly(), app.Logger())\n\tapp.ContextPool = context.New(func() any {\n\t\treturn context.NewContext(app)\n\t})\n\n\tctx := app.ContextPool.Acquire(w, r)\n\thandler(ctx)\n\tapp.ContextPool.Release(ctx)\n}\n\n// IrisTesty is an interface which all testing package should implement.\n// The `httptest` standard package and `ginkgo` third-party module do implement this interface indeed.\n//\n// See the `New` package-level function for more.\ntype IrisTesty interface {\n\tCleanup(func())\n\tError(args ...any)\n\tErrorf(format string, args ...any)\n\tFail()\n\tFailNow()\n\tFailed() bool\n\tFatal(args ...any)\n\tFatalf(format string, args ...any)\n\tHelper()\n\tLog(args ...any)\n\tLogf(format string, args ...any)\n\tName() string\n\tSetenv(key, value string)\n\tSkip(args ...any)\n\tSkipNow()\n\tSkipf(format string, args ...any)\n\tSkipped() bool\n\tTempDir() string\n}\n"
  },
  {
    "path": "httptest/netutils.go",
    "content": "package httptest\n\nimport (\n\t\"crypto/tls\"\n\t\"net\"\n)\n\n// copied from net/http/httptest/internal\n\n// LocalhostCert is a PEM-encoded TLS cert with SAN IPs\n// \"127.0.0.1\" and \"[::1]\", expiring at Jan 29 16:00:00 2084 GMT.\n// generated from src/crypto/tls:\n// go run generate_cert.go  --rsa-bits 1024 --host 127.0.0.1,::1,example.com --ca --start-date \"Jan 1 00:00:00 1970\" --duration=1000000h\n// note: these are not the net/http/httptest/internal contents but doesn't matter.\nvar LocalhostCert = []byte(`-----BEGIN CERTIFICATE-----\nMIIDAzCCAeugAwIBAgIJAP0pWSuIYyQCMA0GCSqGSIb3DQEBBQUAMBgxFjAUBgNV\nBAMMDWxvY2FsaG9zdDozMzEwHhcNMTYxMjI1MDk1OTI3WhcNMjYxMjIzMDk1OTI3\nWjAYMRYwFAYDVQQDDA1sb2NhbGhvc3Q6MzMxMIIBIjANBgkqhkiG9w0BAQEFAAOC\nAQ8AMIIBCgKCAQEA5vETjLa+8W856rWXO1xMF/CLss9vn5xZhPXKhgz+D7ogSAXm\nmWP53eeBUGC2r26J++CYfVqwOmfJEu9kkGUVi8cGMY9dHeIFPfxD31MYX175jJQe\ntu0WeUII7ciNsSUDyBMqsl7yi1IgN7iLONM++1+QfbbmNiEbghRV6icEH6M+bWlz\n3YSAMEdpK3mg2gsugfLKMwJkaBKEehUNMySRlIhyLITqt1exYGaggRd1zjqUpqpD\nsL2sRVHJ3qHGkSh8nVy8MvG8BXiFdYQJP3mCQDZzruCyMWj5/19KAyu7Cto3Bcvu\nPgujnwRtU+itt8WhZUVtU1n7Ivf6lMJTBcc4OQIDAQABo1AwTjAdBgNVHQ4EFgQU\nMXrBvbILQmiwjUj19aecF2N+6IkwHwYDVR0jBBgwFoAUMXrBvbILQmiwjUj19aec\nF2N+6IkwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEA4zbFml1t9KXJ\nOijAV8gALePR8v04DQwJP+jsRxXw5zzhc8Wqzdd2hjUd07mfRWAvmyywrmhCV6zq\nOHznR+aqIqHtm0vV8OpKxLoIQXavfBd6axEXt3859RDM4xJNwIlxs3+LWGPgINud\nwjJqjyzSlhJpQpx4YZ5Da+VMiqAp8N1UeaZ5lBvmSDvoGh6HLODSqtPlWMrldRW9\nAfsXVxenq81MIMeKW2fSOoPnWZ4Vjf1+dSlbJE/DD4zzcfbyfgY6Ep/RrUltJ3ag\nFQbuNTQlgKabe21dSL9zJ2PengVKXl4Trl+4t/Kina9N9Jw535IRCSwinD6a/2Ca\nm7DnVXFiVA==\n-----END CERTIFICATE-----\n`)\n\n// LocalhostKey is the private key for localhostCert.\nvar LocalhostKey = []byte(`-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA5vETjLa+8W856rWXO1xMF/CLss9vn5xZhPXKhgz+D7ogSAXm\nmWP53eeBUGC2r26J++CYfVqwOmfJEu9kkGUVi8cGMY9dHeIFPfxD31MYX175jJQe\ntu0WeUII7ciNsSUDyBMqsl7yi1IgN7iLONM++1+QfbbmNiEbghRV6icEH6M+bWlz\n3YSAMEdpK3mg2gsugfLKMwJkaBKEehUNMySRlIhyLITqt1exYGaggRd1zjqUpqpD\nsL2sRVHJ3qHGkSh8nVy8MvG8BXiFdYQJP3mCQDZzruCyMWj5/19KAyu7Cto3Bcvu\nPgujnwRtU+itt8WhZUVtU1n7Ivf6lMJTBcc4OQIDAQABAoIBAQCTLE0eHpPevtg0\n+FaRUMd5diVA5asoF3aBIjZXaU47bY0G+SO02x6wSMmDFK83a4Vpy/7B3Bp0jhF5\nDLCUyKaLdmE/EjLwSUq37ty+JHFizd7QtNBCGSN6URfpmSabHpCjX3uVQqblHIhF\nmki3BQCdJ5CoXPemxUCHjDgYSZb6JVNIPJExjekc0+4A2MYWMXV6Wr86C7AY3659\nKmveZpC3gOkLA/g/IqDQL/QgTq7/3eloHaO+uPBihdF56do4eaOO0jgFYpl8V7ek\nPZhHfhuPZV3oq15+8Vt77ngtjUWVI6qX0E3ilh+V5cof+03q0FzHPVe3zBUNXcm0\nOGz19u/FAoGBAPSm4Aa4xs/ybyjQakMNix9rak66ehzGkmlfeK5yuQ/fHmTg8Ac+\nahGs6A3lFWQiyU6hqm6Qp0iKuxuDh35DJGCWAw5OUS/7WLJtu8fNFch6iIG29rFs\ns+Uz2YLxJPebpBsKymZUp7NyDRgEElkiqsREmbYjLrc8uNKkDy+k14YnAoGBAPGn\nZlN0Mo5iNgQStulYEP5pI7WOOax9KOYVnBNguqgY9c7fXVXBxChoxt5ebQJWG45y\nKPG0hB0bkA4YPu4bTRf5acIMpjFwcxNlmwdc4oCkT4xqAFs9B/AKYZgkf4IfKHqW\nP9PD7TbUpkaxv25bPYwUSEB7lPa+hBtRyN9Wo6qfAoGAPBkeISiU1hJE0i7YW55h\nFZfKZoqSYq043B+ywo+1/Dsf+UH0VKM1ZSAnZPpoVc/hyaoW9tAb98r0iZ620wJl\nVkCjgYklknbY5APmw/8SIcxP6iVq1kzQqDYjcXIRVa3rEyWEcLzM8VzL8KFXbIQC\nlPIRHFfqKuMEt+HLRTXmJ7MCgYAHGvv4QjdmVl7uObqlG9DMGj1RjlAF0VxNf58q\nNrLmVG2N2qV86wigg4wtZ6te4TdINfUcPkmQLYpLz8yx5Z2bsdq5OPP+CidoD5nC\nWqnSTIKGR2uhQycjmLqL5a7WHaJsEFTqHh2wego1k+5kCUzC/KmvM7MKmkl6ICp+\n3qZLUwKBgQCDOhKDwYo1hdiXoOOQqg/LZmpWOqjO3b4p99B9iJqhmXN0GKXIPSBh\n5nqqmGsG8asSQhchs7EPMh8B80KbrDTeidWskZuUoQV27Al1UEmL6Zcl83qXD6sf\nk9X9TwWyZtp5IL1CAEd/Il9ZTXFzr3lNaN8LCFnU+EIsz1YgUW8LTg==\n-----END RSA PRIVATE KEY-----\n`)\n\n// NewLocalListener returns a new ipv4 \"127.0.0.1:0\"\n// or tcp6 \"[::1]:0\" tcp listener.\nfunc NewLocalListener() net.Listener {\n\tl, err := net.Listen(\"tcp\", \"127.0.0.1:0\")\n\tif err != nil {\n\t\tif l, err = net.Listen(\"tcp6\", \"[::1]:0\"); err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t}\n\treturn l\n}\n\n// NewLocalTLSListener returns a new tls listener\n// based on the \"tcpListener\", if \"tcpListener\" is nil\n// it make use of the `NewLocalListener`.\n// Cert and Key are `LocalhostCert` and `LocalhostKey` respectfully.\nfunc NewLocalTLSListener(tcpListener net.Listener) net.Listener {\n\tif tcpListener == nil {\n\t\ttcpListener = NewLocalListener()\n\t}\n\n\tcert, err := tls.X509KeyPair(LocalhostCert, LocalhostKey)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tcfg := new(tls.Config)\n\tcfg.NextProtos = []string{\"http/1.1\"}\n\tcfg.Certificates = []tls.Certificate{cert}\n\tcfg.InsecureSkipVerify = true\n\treturn tls.NewListener(tcpListener, cfg)\n}\n"
  },
  {
    "path": "httptest/server.go",
    "content": "package httptest\n\nimport (\n\t\"net/http/httptest\"\n\n\t\"github.com/kataras/iris/v12\"\n)\n\n// NewServer is just a helper to create a new standard\n// httptest.Server instance.\nfunc NewServer(t IrisTesty, app *iris.Application) *httptest.Server {\n\tif err := app.Build(); err != nil {\n\t\tt.Fatal(err)\n\t\treturn nil\n\t}\n\n\treturn httptest.NewServer(app)\n}\n"
  },
  {
    "path": "httptest/status.go",
    "content": "package httptest\n\n// HTTP status codes as registered with IANA.\n// See: http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml\n// Raw Copy from the future(tip) net/http std package in order to recude the import path of \"net/http\" for the users.\nconst (\n\tStatusContinue             = 100 // RFC 7231, 6.2.1\n\tStatusSwitchingProtocols   = 101 // RFC 7231, 6.2.2\n\tStatusProcessing           = 102 // RFC 2518, 10.1\n\tStatusEarlyHints           = 103 // RFC 8297\n\tStatusOK                   = 200 // RFC 7231, 6.3.1\n\tStatusCreated              = 201 // RFC 7231, 6.3.2\n\tStatusAccepted             = 202 // RFC 7231, 6.3.3\n\tStatusNonAuthoritativeInfo = 203 // RFC 7231, 6.3.4\n\tStatusNoContent            = 204 // RFC 7231, 6.3.5\n\tStatusResetContent         = 205 // RFC 7231, 6.3.6\n\tStatusPartialContent       = 206 // RFC 7233, 4.1\n\tStatusMultiStatus          = 207 // RFC 4918, 11.1\n\tStatusAlreadyReported      = 208 // RFC 5842, 7.1\n\tStatusIMUsed               = 226 // RFC 3229, 10.4.1\n\n\tStatusMultipleChoices   = 300 // RFC 7231, 6.4.1\n\tStatusMovedPermanently  = 301 // RFC 7231, 6.4.2\n\tStatusFound             = 302 // RFC 7231, 6.4.3\n\tStatusSeeOther          = 303 // RFC 7231, 6.4.4\n\tStatusNotModified       = 304 // RFC 7232, 4.1\n\tStatusUseProxy          = 305 // RFC 7231, 6.4.5\n\t_                       = 306 // RFC 7231, 6.4.6 (Unused)\n\tStatusTemporaryRedirect = 307 // RFC 7231, 6.4.7\n\tStatusPermanentRedirect = 308 // RFC 7538, 3\n\n\tStatusBadRequest                   = 400 // RFC 7231, 6.5.1\n\tStatusUnauthorized                 = 401 // RFC 7235, 3.1\n\tStatusPaymentRequired              = 402 // RFC 7231, 6.5.2\n\tStatusForbidden                    = 403 // RFC 7231, 6.5.3\n\tStatusNotFound                     = 404 // RFC 7231, 6.5.4\n\tStatusMethodNotAllowed             = 405 // RFC 7231, 6.5.5\n\tStatusNotAcceptable                = 406 // RFC 7231, 6.5.6\n\tStatusProxyAuthRequired            = 407 // RFC 7235, 3.2\n\tStatusRequestTimeout               = 408 // RFC 7231, 6.5.7\n\tStatusConflict                     = 409 // RFC 7231, 6.5.8\n\tStatusGone                         = 410 // RFC 7231, 6.5.9\n\tStatusLengthRequired               = 411 // RFC 7231, 6.5.10\n\tStatusPreconditionFailed           = 412 // RFC 7232, 4.2\n\tStatusRequestEntityTooLarge        = 413 // RFC 7231, 6.5.11\n\tStatusRequestURITooLong            = 414 // RFC 7231, 6.5.12\n\tStatusUnsupportedMediaType         = 415 // RFC 7231, 6.5.13\n\tStatusRequestedRangeNotSatisfiable = 416 // RFC 7233, 4.4\n\tStatusExpectationFailed            = 417 // RFC 7231, 6.5.14\n\tStatusTeapot                       = 418 // RFC 7168, 2.3.3\n\tStatusUnprocessableEntity          = 422 // RFC 4918, 11.2\n\tStatusLocked                       = 423 // RFC 4918, 11.3\n\tStatusFailedDependency             = 424 // RFC 4918, 11.4\n\tStatusUpgradeRequired              = 426 // RFC 7231, 6.5.15\n\tStatusPreconditionRequired         = 428 // RFC 6585, 3\n\tStatusTooManyRequests              = 429 // RFC 6585, 4\n\tStatusRequestHeaderFieldsTooLarge  = 431 // RFC 6585, 5\n\tStatusUnavailableForLegalReasons   = 451 // RFC 7725, 3\n\n\tStatusInternalServerError           = 500 // RFC 7231, 6.6.1\n\tStatusNotImplemented                = 501 // RFC 7231, 6.6.2\n\tStatusBadGateway                    = 502 // RFC 7231, 6.6.3\n\tStatusServiceUnavailable            = 503 // RFC 7231, 6.6.4\n\tStatusGatewayTimeout                = 504 // RFC 7231, 6.6.5\n\tStatusHTTPVersionNotSupported       = 505 // RFC 7231, 6.6.6\n\tStatusVariantAlsoNegotiates         = 506 // RFC 2295, 8.1\n\tStatusInsufficientStorage           = 507 // RFC 4918, 11.5\n\tStatusLoopDetected                  = 508 // RFC 5842, 7.2\n\tStatusNotExtended                   = 510 // RFC 2774, 7\n\tStatusNetworkAuthenticationRequired = 511 // RFC 6585, 6\n)\n"
  },
  {
    "path": "i18n/i18n.go",
    "content": "// Package i18n provides internalization and localization features for Iris.\n// To use with net/http see https://github.com/kataras/i18n instead.\npackage i18n\n\nimport (\n\t\"fmt\"\n\t\"io/fs\"\n\t\"net/http\"\n\t\"os\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/core/router\"\n\t\"github.com/kataras/iris/v12/i18n/internal\"\n\n\t\"golang.org/x/text/language\"\n)\n\ntype (\n\t// MessageFunc is the function type to modify the behavior when a key or language was not found.\n\t// All language inputs fallback to the default locale if not matched.\n\t// This is why this signature accepts both input and matched languages, so caller\n\t// can provide better messages.\n\t//\n\t// The first parameter is set to the client real input of the language,\n\t// the second one is set to the matched language (default one if input wasn't matched)\n\t// and the third and forth are the translation format/key and its optional arguments.\n\t//\n\t// Note: we don't accept the Context here because Tr method and template func {{ tr }}\n\t// have no direct access to it.\n\tMessageFunc = internal.MessageFunc\n\n\t// Loader accepts a `Matcher` and should return a `Localizer`.\n\t// Functions that implement this type should load locale files.\n\tLoader func(m *Matcher) (Localizer, error)\n\n\t// Localizer is the interface which returned from a `Loader`.\n\t// Types that implement this interface should be able to retrieve a `Locale`\n\t// based on the language index.\n\tLocalizer interface {\n\t\t// GetLocale should return a valid `Locale` based on the language index.\n\t\t// It will always match the Loader.Matcher.Languages[index].\n\t\t// It may return the default language if nothing else matches based on custom localizer's criteria.\n\t\tGetLocale(index int) context.Locale\n\t}\n)\n\n// I18n is the structure which keeps the i18n configuration and implements localization and internationalization features.\ntype I18n struct {\n\tlocalizer Localizer\n\tmatcher   *Matcher\n\n\tLoader LoaderConfig\n\tloader Loader\n\tmu     sync.Mutex\n\n\t// ExtractFunc is the type signature for declaring custom logic\n\t// to extract the language tag name.\n\t// Defaults to nil.\n\tExtractFunc func(ctx *context.Context) string\n\t// DefaultMessageFunc is the field which can be used\n\t// to modify the behavior when a key or language was not found.\n\t// All language inputs fallback to the default locale if not matched.\n\t// This is why this one accepts both input and matched languages,\n\t// so the caller can be more expressful knowing those.\n\t//\n\t// Defaults to nil.\n\tDefaultMessageFunc MessageFunc\n\n\t// If not empty, it is language identifier by url query.\n\t//\n\t// Defaults to \"lang\".\n\tURLParameter string\n\t// If not empty, it is language identifier by cookie of this name.\n\t//\n\t// Defaults to empty.\n\tCookie string\n\t// If true then a subdomain can be a language identifier.\n\t//\n\t// Defaults to true.\n\tSubdomain bool\n\t// If a DefaultMessageFunc is NOT set:\n\t// If true then it will return empty string when translation for a\n\t// specific language's key was not found.\n\t// Defaults to false, fallback defaultLang:key will be used.\n\t// Otherwise, DefaultMessageFunc is called in either case.\n\tStrict bool\n\n\t// If true then Iris will wrap its router with the i18n router wrapper on its Build state.\n\t// It will (local) redirect requests like:\n\t// 1. /$lang_prefix/$path to /$path with the language set to $lang_prefix part.\n\t// 2. $lang_subdomain.$domain/$path to $domain/$path with the language set to $lang_subdomain part.\n\t//\n\t// Defaults to true.\n\tPathRedirect bool\n}\n\nvar _ context.I18nReadOnly = (*I18n)(nil)\n\n// makeTags converts language codes to language Tags.\nfunc makeTags(languages ...string) (tags []language.Tag) {\n\tlanguages = removeDuplicates(languages)\n\n\tfor _, lang := range languages {\n\t\ttag, err := language.Parse(lang)\n\t\tif err == nil && tag != language.Und {\n\t\t\ttags = append(tags, tag)\n\t\t}\n\t}\n\n\treturn\n}\n\n// New returns a new `I18n` instance. Use its `Load` or `LoadAssets` to load languages.\n// Examples at: https://github.com/kataras/iris/tree/main/_examples/i18n.\nfunc New() *I18n {\n\ti := &I18n{\n\t\tLoader:       DefaultLoaderConfig,\n\t\tURLParameter: \"lang\",\n\t\tSubdomain:    true,\n\t\tPathRedirect: true,\n\t}\n\n\treturn i\n}\n\n// Load is a method shortcut to load files using a filepath.Glob pattern.\n// It returns a non-nil error on failure.\n//\n// See `New` and `Glob` package-level functions for more.\nfunc (i *I18n) Load(globPattern string, languages ...string) error {\n\treturn i.Reset(Glob(globPattern, i.Loader), languages...)\n}\n\n// LoadAssets is a method shortcut to load files using go-bindata.\n// It returns a non-nil error on failure.\n//\n// See `New` and `Asset` package-level functions for more.\nfunc (i *I18n) LoadAssets(assetNames func() []string, asset func(string) ([]byte, error), languages ...string) error {\n\treturn i.Reset(Assets(assetNames, asset, i.Loader), languages...)\n}\n\n// LoadFS is a method shortcut to load files using\n// an `embed.FS` or `fs.FS` or `http.FileSystem` value.\n// The \"pattern\" is a classic glob pattern.\n//\n// See `New` and `FS` package-level functions for more.\n// Example: https://github.com/kataras/iris/blob/main/_examples/i18n/template-embedded/main.go.\nfunc (i *I18n) LoadFS(fileSystem fs.FS, pattern string, languages ...string) error {\n\tloader, err := FS(fileSystem, pattern, i.Loader)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn i.Reset(loader, languages...)\n}\n\n// LoadKV is a method shortcut to load locales from a map of specified languages.\n// See `KV` package-level function for more.\nfunc (i *I18n) LoadKV(langMap LangMap, languages ...string) error {\n\tloader := KV(langMap, i.Loader)\n\treturn i.Reset(loader, languages...)\n}\n\n// Reset sets the locales loader and languages.\n// It is not meant to be used by users unless\n// a custom `Loader` must be used instead of the default one.\nfunc (i *I18n) Reset(loader Loader, languages ...string) error {\n\ttags := makeTags(languages...)\n\n\ti.loader = loader\n\ti.matcher = &Matcher{\n\t\tstrict:             len(tags) > 0,\n\t\tLanguages:          tags,\n\t\tmatcher:            language.NewMatcher(tags),\n\t\tdefaultMessageFunc: i.DefaultMessageFunc,\n\t}\n\n\treturn i.reload()\n}\n\n// reload loads the language files from the provided Loader,\n// the `New` package-level function preloads those files already.\nfunc (i *I18n) reload() error { // May be an exported function, if requested.\n\ti.mu.Lock()\n\tdefer i.mu.Unlock()\n\n\tif i.loader == nil {\n\t\treturn fmt.Errorf(\"nil loader\")\n\t}\n\n\tlocalizer, err := i.loader(i.matcher)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\ti.localizer = localizer\n\treturn nil\n}\n\n// Loaded reports whether `New` or `Load/LoadAssets` called.\nfunc (i *I18n) Loaded() bool {\n\treturn i != nil && i.loader != nil && i.localizer != nil && i.matcher != nil\n}\n\n// Tags returns the registered languages or dynamically resolved by files.\n// Use `Load` or `LoadAssets` first.\nfunc (i *I18n) Tags() []language.Tag {\n\tif !i.Loaded() {\n\t\treturn nil\n\t}\n\n\treturn i.matcher.Languages\n}\n\n// SetDefault changes the default language.\n// Please avoid using this method; the default behavior will accept\n// the first language of the registered tags as the default one.\nfunc (i *I18n) SetDefault(langCode string) bool {\n\tt, err := language.Parse(langCode)\n\tif err != nil {\n\t\treturn false\n\t}\n\n\tif tag, index, conf := i.matcher.Match(t); conf > language.Low {\n\t\tif l, ok := i.localizer.(interface {\n\t\t\tSetDefault(int) bool\n\t\t}); ok {\n\t\t\tif l.SetDefault(index) {\n\t\t\t\ttags := i.matcher.Languages\n\t\t\t\t// set the order\n\t\t\t\ttags[index] = tags[0]\n\t\t\t\ttags[0] = tag\n\n\t\t\t\ti.matcher.Languages = tags\n\t\t\t\ti.matcher.matcher = language.NewMatcher(tags)\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t}\n\n\treturn false\n}\n\n// Matcher implements the languae.Matcher.\n// It contains the original language Matcher and keeps an ordered\n// list of the registered languages for further use (see `Loader` implementation).\ntype Matcher struct {\n\tstrict    bool\n\tLanguages []language.Tag\n\tmatcher   language.Matcher\n\t// defaultMessageFunc passed by the i18n structure.\n\tdefaultMessageFunc MessageFunc\n}\n\nvar _ language.Matcher = (*Matcher)(nil)\n\n// Match returns the best match for any of the given tags, along with\n// a unique index associated with the returned tag and a confidence\n// score.\nfunc (m *Matcher) Match(t ...language.Tag) (language.Tag, int, language.Confidence) {\n\treturn m.matcher.Match(t...)\n}\n\n// MatchOrAdd acts like Match but it checks and adds a language tag, if not found,\n// when the `Matcher.strict` field is true (when no tags are provided by the caller)\n// and they should be dynamically added to the list.\nfunc (m *Matcher) MatchOrAdd(t language.Tag) (tag language.Tag, index int, conf language.Confidence) {\n\ttag, index, conf = m.Match(t)\n\tif conf <= language.Low && !m.strict {\n\t\t// not found, add it now.\n\t\tm.Languages = append(m.Languages, t)\n\t\ttag = t\n\t\tindex = len(m.Languages) - 1\n\t\tconf = language.Exact\n\t\tm.matcher = language.NewMatcher(m.Languages) // reset matcher to include the new language.\n\t}\n\n\treturn\n}\n\n// ParseLanguageFiles returns a map of language indexes and\n// their associated files based on the \"fileNames\".\nfunc (m *Matcher) ParseLanguageFiles(fileNames []string) (map[int][]string, error) {\n\tlanguageFiles := make(map[int][]string)\n\n\tfor _, fileName := range fileNames {\n\t\tindex := parsePath(m, fileName)\n\t\tif index == -1 {\n\t\t\tcontinue\n\t\t}\n\n\t\tlanguageFiles[index] = append(languageFiles[index], fileName)\n\t}\n\n\treturn languageFiles, nil\n}\n\nfunc parsePath(m *Matcher, path string) int {\n\tif t, ok := parseLanguage(path); ok {\n\t\tif _, index, conf := m.MatchOrAdd(t); conf > language.Low {\n\t\t\treturn index\n\t\t}\n\t}\n\n\treturn -1\n}\n\nfunc parseLanguageName(m *Matcher, name string) int {\n\tif t, err := language.Parse(name); err == nil {\n\t\tif _, index, conf := m.MatchOrAdd(t); conf > language.Low {\n\t\t\treturn index\n\t\t}\n\t}\n\n\treturn -1\n}\n\nfunc reverseStrings(s []string) []string {\n\tfor i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {\n\t\ts[i], s[j] = s[j], s[i]\n\t}\n\treturn s\n}\n\nfunc parseLanguage(path string) (language.Tag, bool) {\n\tif idx := strings.LastIndexByte(path, '.'); idx > 0 {\n\t\tpath = path[0:idx]\n\t}\n\n\t// path = strings.ReplaceAll(path, \"..\", \"\")\n\n\tnames := strings.FieldsFunc(path, func(r rune) bool {\n\t\treturn r == '_' || r == os.PathSeparator || r == '/' || r == '.'\n\t})\n\n\tnames = reverseStrings(names) // see https://github.com/kataras/i18n/issues/1\n\n\tfor _, s := range names {\n\t\tt, err := language.Parse(s)\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\n\t\treturn t, true\n\t}\n\n\treturn language.Und, false\n}\n\n// TryMatchString will try to match the \"s\" with a registered language tag.\n// It returns -1 as the language index and false if not found.\nfunc (i *I18n) TryMatchString(s string) (language.Tag, int, bool) {\n\tif tag, err := language.Parse(s); err == nil {\n\t\tif tag, index, conf := i.matcher.Match(tag); conf > language.Low {\n\t\t\treturn tag, index, true\n\t\t}\n\t}\n\n\treturn language.Und, -1, false\n}\n\n// Tr returns a translated message based on the \"lang\" language code\n// and its key with any optional arguments attached to it.\n//\n// It returns an empty string if \"lang\" not matched, unless DefaultMessageFunc.\n// It returns the default language's translation if \"key\" not matched, unless DefaultMessageFunc.\nfunc (i *I18n) Tr(lang, key string, args ...any) string {\n\t_, index, ok := i.TryMatchString(lang)\n\tif !ok {\n\t\tindex = 0\n\t}\n\tloc := i.localizer.GetLocale(index)\n\treturn i.getLocaleMessage(loc, lang, key, args...)\n}\n\n// TrContext returns the localized text message for this Context.\n// It returns an empty string if context's locale not matched, unless DefaultMessageFunc.\n// It returns the default language's translation if \"key\" not matched, unless DefaultMessageFunc.\nfunc (i *I18n) TrContext(ctx *context.Context, key string, args ...any) string {\n\tloc := ctx.GetLocale()\n\tlangInput := ctx.Values().GetString(ctx.Application().ConfigurationReadOnly().GetLanguageInputContextKey())\n\treturn i.getLocaleMessage(loc, langInput, key, args...)\n}\n\nfunc (i *I18n) getLocaleMessage(loc context.Locale, langInput string, key string, args ...any) (msg string) {\n\tlangMatched := \"\"\n\n\tif loc != nil {\n\t\tlangMatched = loc.Language()\n\n\t\tmsg = loc.GetMessage(key, args...)\n\t\tif msg == \"\" && i.DefaultMessageFunc == nil && !i.Strict && loc.Index() > 0 {\n\t\t\t// it's not the default/fallback language and not message found for that lang:key.\n\t\t\tmsg = i.localizer.GetLocale(0).GetMessage(key, args...)\n\t\t}\n\t}\n\n\tif msg == \"\" && i.DefaultMessageFunc != nil {\n\t\tmsg = i.DefaultMessageFunc(langInput, langMatched, key, args...)\n\t}\n\n\treturn\n}\n\nconst acceptLanguageHeaderKey = \"Accept-Language\"\n\n// GetLocale returns the found locale of a request.\n// It will return the first registered language if nothing else matched.\nfunc (i *I18n) GetLocale(ctx *context.Context) context.Locale {\n\tvar (\n\t\tindex         int\n\t\tok            bool\n\t\textractedLang string\n\t)\n\n\tlanguageInputKey := ctx.Application().ConfigurationReadOnly().GetLanguageInputContextKey()\n\n\tif contextKey := ctx.Application().ConfigurationReadOnly().GetLanguageContextKey(); contextKey != \"\" {\n\t\tif v := ctx.Values().GetString(contextKey); v != \"\" {\n\t\t\tif languageInputKey != \"\" {\n\t\t\t\tctx.Values().Set(languageInputKey, v)\n\t\t\t}\n\n\t\t\tif v == \"default\" {\n\t\t\t\tindex = 0 // no need to call `TryMatchString` and spend time.\n\t\t\t} else {\n\t\t\t\t_, index, _ = i.TryMatchString(v)\n\t\t\t}\n\n\t\t\tlocale := i.localizer.GetLocale(index)\n\t\t\tif locale == nil {\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\treturn locale\n\t\t}\n\t}\n\n\tif !ok && i.ExtractFunc != nil {\n\t\tif v := i.ExtractFunc(ctx); v != \"\" {\n\t\t\textractedLang = v\n\t\t\t_, index, ok = i.TryMatchString(v)\n\t\t}\n\t}\n\n\tif !ok && i.URLParameter != \"\" {\n\t\tif v := ctx.URLParam(i.URLParameter); v != \"\" {\n\t\t\textractedLang = v\n\t\t\t_, index, ok = i.TryMatchString(v)\n\t\t}\n\t}\n\n\tif !ok && i.Cookie != \"\" {\n\t\tif v := ctx.GetCookie(i.Cookie); v != \"\" {\n\t\t\textractedLang = v\n\t\t\t_, index, ok = i.TryMatchString(v) // url.QueryUnescape(cookie.Value)\n\t\t}\n\t}\n\n\tif !ok && i.Subdomain {\n\t\tif v := ctx.Subdomain(); v != \"\" {\n\t\t\textractedLang = v\n\t\t\t_, index, ok = i.TryMatchString(v)\n\t\t}\n\t}\n\n\tif !ok {\n\t\tif v := ctx.GetHeader(acceptLanguageHeaderKey); v != \"\" {\n\t\t\textractedLang = v // note.\n\t\t\tdesired, _, err := language.ParseAcceptLanguage(v)\n\t\t\tif err == nil {\n\t\t\t\tif _, idx, conf := i.matcher.Match(desired...); conf > language.Low {\n\t\t\t\t\tindex = idx\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// locale := i.localizer.GetLocale(index)\n\t// ctx.Values().Set(ctx.Application().ConfigurationReadOnly().GetLocaleContextKey(), locale)\n\n\tif languageInputKey != \"\" {\n\t\t// Set the user input we wanna use it on DefaultMessageFunc.\n\t\t// Even if matched because it may be en-gb or en but if there is a language registered\n\t\t// as en-us it will be successfully matched ( see TrymatchString and Low conf).\n\t\tctx.Values().Set(languageInputKey, extractedLang)\n\t}\n\n\t// if index == 0 then it defaults to the first language.\n\tlocale := i.localizer.GetLocale(index)\n\tif locale == nil {\n\t\treturn nil\n\t}\n\n\treturn locale\n}\n\nfunc (i *I18n) setLangWithoutContext(w http.ResponseWriter, r *http.Request, lang string) {\n\tif i.Cookie != \"\" {\n\t\thttp.SetCookie(w, &http.Cookie{\n\t\t\tName:  i.Cookie,\n\t\t\tValue: lang,\n\t\t\t// allow subdomain sharing.\n\t\t\tDomain:   context.GetDomain(context.GetHost(r)),\n\t\t\tSameSite: http.SameSiteLaxMode,\n\t\t})\n\t} else if i.URLParameter != \"\" {\n\t\tq := r.URL.Query()\n\t\tq.Set(i.URLParameter, lang)\n\t\tr.URL.RawQuery = q.Encode()\n\t}\n\n\tr.Header.Set(acceptLanguageHeaderKey, lang)\n}\n\n// Wrapper returns a new router wrapper.\n// The result function can be passed on `Application.WrapRouter/AddRouterWrapper`.\n// It compares the path prefix for translated language and\n// local redirects the requested path with the selected (from the path) language to the router.\n//\n// You do NOT have to call it manually, just set the `I18n.PathRedirect` field to true.\nfunc (i *I18n) Wrapper() router.WrapperFunc {\n\tif !i.PathRedirect {\n\t\treturn nil\n\t}\n\treturn func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {\n\t\tfound := false\n\t\tpath := r.URL.Path\n\t\tif len(path) > 0 && path[0] == '/' {\n\t\t\tpath = path[1:]\n\t\t}\n\n\t\tif idx := strings.IndexByte(path, '/'); idx > 0 {\n\t\t\tpath = path[:idx]\n\t\t}\n\n\t\tif path != \"\" {\n\t\t\tif tag, _, ok := i.TryMatchString(path); ok {\n\t\t\t\tlang := tag.String()\n\n\t\t\t\tpath = r.URL.Path[len(path)+1:]\n\t\t\t\tif path == \"\" {\n\t\t\t\t\tpath = \"/\"\n\t\t\t\t}\n\n\t\t\t\tr.RequestURI = path\n\t\t\t\tr.URL.Path = path\n\t\t\t\ti.setLangWithoutContext(w, r, lang)\n\t\t\t\tfound = true\n\t\t\t}\n\t\t}\n\n\t\tif !found && i.Subdomain {\n\t\t\thost := context.GetHost(r)\n\t\t\tif dotIdx := strings.IndexByte(host, '.'); dotIdx > 0 {\n\t\t\t\tif subdomain := host[0:dotIdx]; subdomain != \"\" {\n\t\t\t\t\tif tag, _, ok := i.TryMatchString(subdomain); ok {\n\t\t\t\t\t\thost = host[dotIdx+1:]\n\t\t\t\t\t\tr.URL.Host = host\n\t\t\t\t\t\tr.Host = host\n\t\t\t\t\t\ti.setLangWithoutContext(w, r, tag.String())\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tnext(w, r)\n\t}\n}\n\nfunc removeDuplicates(elements []string) (result []string) {\n\tseen := make(map[string]struct{})\n\n\tfor v := range elements {\n\t\tval := elements[v]\n\t\tif _, ok := seen[val]; !ok {\n\t\t\tseen[val] = struct{}{}\n\t\t\tresult = append(result, val)\n\t\t}\n\t}\n\n\treturn result\n}\n"
  },
  {
    "path": "i18n/internal/aliases.go",
    "content": "package internal\n\n// Map is just an alias of the map[string]any type.\n// Just like the iris.Map one.\ntype Map = map[string]any\n"
  },
  {
    "path": "i18n/internal/catalog.go",
    "content": "package internal\n\nimport (\n\t\"fmt\"\n\t\"text/template\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\n\t\"golang.org/x/text/language\"\n\t\"golang.org/x/text/message\"\n\t\"golang.org/x/text/message/catalog\"\n)\n\n// MessageFunc is the function type to modify the behavior when a key or language was not found.\n// All language inputs fallback to the default locale if not matched.\n// This is why this signature accepts both input and matched languages, so caller\n// can provide better messages.\n//\n// The first parameter is set to the client real input of the language,\n// the second one is set to the matched language (default one if input wasn't matched)\n// and the third and forth are the translation format/key and its optional arguments.\n//\n// Note: we don't accept the Context here because Tr method and template func {{ tr }}\n// have no direct access to it.\ntype MessageFunc func(langInput, langMatched, key string, args ...any) string\n\n// Catalog holds the locales and the variables message storage.\ntype Catalog struct {\n\tbuilder *catalog.Builder\n\tLocales []*Locale\n}\n\n// The Options of the Catalog and its Locales.\ntype Options struct {\n\t// Left delimiter for template messages.\n\tLeft string\n\t// Right delimeter for template messages.\n\tRight string\n\t// Enable strict mode.\n\tStrict bool\n\t// Optional functions for template messages per locale.\n\tFuncs func(context.Locale) template.FuncMap\n\t// Optional function to be called when no message was found.\n\tDefaultMessageFunc MessageFunc\n\t// Customize the overall behavior of the plurazation feature.\n\tPluralFormDecoder PluralFormDecoder\n}\n\n// NewCatalog returns a new Catalog based on the registered languages and the loader options.\nfunc NewCatalog(languages []language.Tag, opts Options) (*Catalog, error) { // ordered languages, the first should be the default one.\n\tif len(languages) == 0 {\n\t\treturn nil, fmt.Errorf(\"catalog: empty languages\")\n\t}\n\n\tif opts.Left == \"\" {\n\t\topts.Left = \"{{\"\n\t}\n\n\tif opts.Right == \"\" {\n\t\topts.Right = \"}}\"\n\t}\n\n\tif opts.PluralFormDecoder == nil {\n\t\topts.PluralFormDecoder = DefaultPluralFormDecoder\n\t}\n\n\tbuilder := catalog.NewBuilder(catalog.Fallback(languages[0]))\n\n\tlocales := make([]*Locale, 0, len(languages))\n\tfor idx, tag := range languages {\n\t\tlocale := &Locale{\n\t\t\ttag:      tag,\n\t\t\tindex:    idx,\n\t\t\tID:       tag.String(),\n\t\t\tOptions:  opts,\n\t\t\tPrinter:  message.NewPrinter(tag, message.Catalog(builder)),\n\t\t\tMessages: make(map[string]Renderer),\n\t\t}\n\t\tlocale.FuncMap = getFuncs(locale)\n\n\t\tlocales = append(locales, locale)\n\t}\n\n\tc := &Catalog{\n\t\tbuilder: builder,\n\t\tLocales: locales,\n\t}\n\n\treturn c, nil\n}\n\n// Set sets a simple translation message.\nfunc (c *Catalog) Set(tag language.Tag, key string, msgs ...catalog.Message) error {\n\t// fmt.Printf(\"Catalog.Set[%s] %s:\\n\", tag.String(), key)\n\t// for _, msg := range msgs {\n\t// \tfmt.Printf(\"%#+v\\n\", msg)\n\t// }\n\treturn c.builder.Set(tag, key, msgs...)\n}\n\n// Store stores the a map of values to the locale derives from the given \"langIndex\".\nfunc (c *Catalog) Store(langIndex int, kv Map) error {\n\tloc := c.getLocale(langIndex)\n\tif loc == nil {\n\t\treturn fmt.Errorf(\"expected language index to be lower or equal than %d but got %d\", len(c.Locales), langIndex)\n\t}\n\treturn loc.Load(c, kv)\n}\n\n/* Localizer interface. */\n\n// SetDefault changes the default language based on the \"index\".\n// See `I18n#SetDefault` method for more.\nfunc (c *Catalog) SetDefault(index int) bool {\n\tif index < 0 {\n\t\tindex = 0\n\t}\n\n\tif maxIdx := len(c.Locales) - 1; index > maxIdx {\n\t\treturn false\n\t}\n\n\t// callers should protect with mutex if called at serve-time.\n\tloc := c.Locales[index]\n\tloc.index = 0\n\tf := c.Locales[0]\n\tc.Locales[0] = loc\n\tf.index = index\n\tc.Locales[index] = f\n\treturn true\n}\n\n// GetLocale returns a valid `Locale` based on the \"index\".\nfunc (c *Catalog) GetLocale(index int) context.Locale {\n\treturn c.getLocale(index)\n}\n\nfunc (c *Catalog) getLocale(index int) *Locale {\n\tif index < 0 {\n\t\tindex = 0\n\t}\n\n\tif maxIdx := len(c.Locales) - 1; index > maxIdx {\n\t\t// panic(\"expected language index to be lower or equal than %d but got %d\", maxIdx, langIndex)\n\t\treturn nil\n\t}\n\n\tloc := c.Locales[index]\n\treturn loc\n}\n"
  },
  {
    "path": "i18n/internal/locale.go",
    "content": "package internal\n\nimport (\n\t\"fmt\"\n\t\"text/template\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\n\t\"golang.org/x/text/language\"\n\t\"golang.org/x/text/message\"\n\t\"golang.org/x/text/message/catalog\"\n)\n\n// Locale is the default Locale.\n// Created by Catalog.\n// One Locale maps to one registered and loaded language.\n// Stores the translation variables and most importantly, the Messages (keys and their renderers).\ntype Locale struct {\n\t// The index of the language registered by the user, starting from zero.\n\tindex int\n\ttag   language.Tag\n\t// ID is the tag.String().\n\tID string\n\t// Options given by the Catalog\n\tOptions Options\n\n\t// Fields set by Catalog.\n\tFuncMap template.FuncMap\n\tPrinter *message.Printer\n\t//\n\n\t// Fields set by this Load method.\n\tMessages map[string]Renderer\n\tVars     []Var // shared per-locale variables.\n}\n\n// Ensures that the Locale completes the context.Locale interface.\nvar _ context.Locale = (*Locale)(nil)\n\n// Load sets the translation messages based on the Catalog's key values.\nfunc (loc *Locale) Load(c *Catalog, keyValues Map) error {\n\treturn loc.setMap(c, \"\", keyValues)\n}\n\nfunc (loc *Locale) setMap(c *Catalog, key string, keyValues Map) error {\n\t// unique locals or the shared ones.\n\tisRoot := key == \"\"\n\n\tvars := getVars(loc, VarsKey, keyValues)\n\tif isRoot {\n\t\tloc.Vars = vars\n\t} else {\n\t\tvars = removeVarsDuplicates(append(vars, loc.Vars...))\n\t}\n\n\tfor k, v := range keyValues {\n\t\tform, isPlural := loc.Options.PluralFormDecoder(loc, k)\n\t\tif isPlural {\n\t\t\tk = key\n\t\t} else if !isRoot {\n\t\t\tk = key + \".\" + k\n\t\t}\n\n\t\tswitch value := v.(type) {\n\t\tcase string:\n\t\t\tif err := loc.setString(c, k, value, vars, form); err != nil {\n\t\t\t\treturn fmt.Errorf(\"%s:%s parse string: %w\", loc.ID, key, err)\n\t\t\t}\n\t\tcase Map:\n\t\t\t// fmt.Printf(\"%s is map\\n\", fullKey)\n\t\t\tif err := loc.setMap(c, k, value); err != nil {\n\t\t\t\treturn fmt.Errorf(\"%s:%s parse map: %w\", loc.ID, key, err)\n\t\t\t}\n\n\t\tdefault:\n\t\t\treturn fmt.Errorf(\"%s:%s unexpected type of %T as value\", loc.ID, key, value)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (loc *Locale) setString(c *Catalog, key string, value string, vars []Var, form PluralForm) (err error) {\n\tisPlural := form != nil\n\n\t// fmt.Printf(\"setStringVars: %s=%s\\n\", key, value)\n\tmsgs, vars := makeSelectfVars(value, vars, isPlural)\n\tmsgs = append(msgs, catalog.String(value))\n\n\tm := &Message{\n\t\tLocale: loc,\n\t\tKey:    key,\n\t\tValue:  value,\n\t\tVars:   vars,\n\t\tPlural: isPlural,\n\t}\n\n\tvar (\n\t\trenderer, pluralRenderer Renderer = m, m\n\t)\n\n\tif stringIsTemplateValue(value, loc.Options.Left, loc.Options.Right) {\n\t\tt, err := NewTemplate(c, m)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tpluralRenderer = t\n\t\tif !isPlural {\n\t\t\trenderer = t\n\t\t}\n\t} else {\n\t\tif isPlural {\n\t\t\tpluralRenderer, err = newIndependentPluralRenderer(c, loc, key, msgs...)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"<%s = %s>: %w\", key, value, err)\n\t\t\t}\n\t\t} else if err = c.Set(loc.tag, key, msgs...); err != nil {\n\t\t\t// let's make normal keys direct fire:\n\t\t\t// renderer = &simpleRenderer{key, loc.Printer}\n\t\t\treturn fmt.Errorf(\"<%s = %s>: %w\", key, value, err)\n\t\t}\n\n\t}\n\n\tif isPlural {\n\t\tif existingMsg, ok := loc.Messages[key]; ok {\n\t\t\tif msg, ok := existingMsg.(*Message); ok {\n\t\t\t\tmsg.AddPlural(form, pluralRenderer)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tm.AddPlural(form, pluralRenderer)\n\t}\n\n\tloc.Messages[key] = renderer\n\treturn\n}\n\n/* context.Locale interface */\n\n// Index returns the current locale index from the languages list.\nfunc (loc *Locale) Index() int {\n\treturn loc.index\n}\n\n// Tag returns the full language Tag attached to this Locale,\n// it should be unique across different Locales.\nfunc (loc *Locale) Tag() *language.Tag {\n\treturn &loc.tag\n}\n\n// Language should return the exact languagecode of this `Locale`\n// that the user provided on `New` function.\n//\n// Same as `Tag().String()` but it's static.\nfunc (loc *Locale) Language() string {\n\treturn loc.ID\n}\n\n// GetMessage should return translated text based on the given \"key\".\nfunc (loc *Locale) GetMessage(key string, args ...any) string {\n\tif msg, ok := loc.Messages[key]; ok {\n\t\tresult, err := msg.Render(args...)\n\t\tif err != nil {\n\t\t\tresult = err.Error()\n\t\t}\n\n\t\treturn result\n\t}\n\n\treturn \"\"\n}\n"
  },
  {
    "path": "i18n/internal/message.go",
    "content": "package internal\n\nimport (\n\t\"fmt\"\n\t\"sort\"\n)\n\n// Renderer is responsible to render a translation based\n// on the given \"args\".\ntype Renderer interface {\n\tRender(args ...any) (string, error)\n}\n\n// Message is the default Renderer for translation messages.\n// Holds the variables and the plurals of this key.\n// Each Locale has its own list of messages.\ntype Message struct {\n\tLocale *Locale\n\n\tKey   string\n\tValue string\n\n\tPlural  bool\n\tPlurals []*PluralMessage // plural forms by order.\n\n\tVars []Var\n}\n\n// AddPlural adds a plural message to the Plurals list.\nfunc (m *Message) AddPlural(form PluralForm, r Renderer) {\n\tmsg := &PluralMessage{\n\t\tForm:     form,\n\t\tRenderer: r,\n\t}\n\n\tif len(m.Plurals) == 0 {\n\t\tm.Plural = true\n\t\tm.Plurals = append(m.Plurals, msg)\n\t\treturn\n\t}\n\n\tfor i, p := range m.Plurals {\n\t\tif p.Form.String() == form.String() {\n\t\t\t// replace\n\t\t\tm.Plurals[i] = msg\n\t\t\treturn\n\t\t}\n\t}\n\n\tm.Plurals = append(m.Plurals, msg)\n\tsort.SliceStable(m.Plurals, func(i, j int) bool {\n\t\treturn m.Plurals[i].Form.Less(m.Plurals[j].Form)\n\t})\n}\n\n// Render completes the Renderer interface.\n// It accepts arguments, which can resolve the pluralization type of the message\n// and its variables. If the Message is wrapped by a Template then the\n// first argument should be a map. The map key resolves to the pluralization\n// of the message is the \"PluralCount\". And for variables the user\n// should set a message key which looks like: %VAR_NAME%Count, e.g. \"DogsCount\"\n// to set plural count for the \"Dogs\" variable, case-sensitive.\nfunc (m *Message) Render(args ...any) (string, error) {\n\tif m.Plural {\n\t\tif len(args) > 0 {\n\t\t\tif pluralCount, ok := findPluralCount(args[0]); ok {\n\t\t\t\tfor _, plural := range m.Plurals {\n\t\t\t\t\tif plural.Form.MatchPlural(pluralCount) {\n\t\t\t\t\t\treturn plural.Renderer.Render(args...)\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn \"\", fmt.Errorf(\"key: %q: no registered plurals for <%d>\", m.Key, pluralCount)\n\t\t\t}\n\t\t}\n\n\t\treturn \"\", fmt.Errorf(\"key: %q: missing plural count argument\", m.Key)\n\t}\n\n\treturn m.Locale.Printer.Sprintf(m.Key, args...), nil\n}\n"
  },
  {
    "path": "i18n/internal/plural.go",
    "content": "package internal\n\nimport (\n\t\"strconv\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\n\t\"golang.org/x/text/feature/plural\"\n\t\"golang.org/x/text/message\"\n\t\"golang.org/x/text/message/catalog\"\n)\n\n// PluralCounter if completes by an input argument of a message to render,\n// then the plural renderer will resolve the plural count\n// and any variables' counts. This is useful when the data is not a type of Map or integers.\ntype PluralCounter interface {\n\t// PluralCount returns the plural count of the message.\n\t// If returns -1 then this is not a valid plural message.\n\tPluralCount() int\n\t// VarCount should return the variable count, based on the variable name.\n\tVarCount(name string) int\n}\n\n// PluralMessage holds the registered Form and the corresponding Renderer.\n// It is used on the `Message.AddPlural` method.\ntype PluralMessage struct {\n\tForm     PluralForm\n\tRenderer Renderer\n}\n\ntype independentPluralRenderer struct {\n\tkey     string\n\tprinter *message.Printer\n}\n\nfunc newIndependentPluralRenderer(c *Catalog, loc *Locale, key string, msgs ...catalog.Message) (Renderer, error) {\n\tbuilder := catalog.NewBuilder(catalog.Fallback(c.Locales[0].tag))\n\tif err := builder.Set(loc.tag, key, msgs...); err != nil {\n\t\treturn nil, err\n\t}\n\tprinter := message.NewPrinter(loc.tag, message.Catalog(builder))\n\treturn &independentPluralRenderer{key, printer}, nil\n}\n\nfunc (m *independentPluralRenderer) Render(args ...any) (string, error) {\n\treturn m.printer.Sprintf(m.key, args...), nil\n}\n\n// A PluralFormDecoder should report and return whether\n// a specific \"key\" is a plural one. This function\n// can be implemented and set on the `Options` to customize\n// the plural forms and their behavior in general.\n//\n// See the `DefaultPluralFormDecoder` package-level\n// variable for the default implementation one.\ntype PluralFormDecoder func(loc context.Locale, key string) (PluralForm, bool)\n\n// DefaultPluralFormDecoder is the default `PluralFormDecoder`.\n// Supprots \"zero\", \"one\", \"two\", \"other\", \"=x\", \"<x\", \">x\".\nvar DefaultPluralFormDecoder = func(_ context.Locale, key string) (PluralForm, bool) {\n\tif isDefaultPluralForm(key) {\n\t\treturn pluralForm(key), true\n\t}\n\n\treturn nil, false\n}\n\nfunc isDefaultPluralForm(s string) bool {\n\tswitch s {\n\tcase \"zero\", \"one\", \"two\", \"other\":\n\t\treturn true\n\tdefault:\n\t\tif len(s) > 1 {\n\t\t\tch := s[0]\n\t\t\tif ch == '=' || ch == '<' || ch == '>' {\n\t\t\t\tif isDigit(s[1]) {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn false\n\t}\n}\n\n// A PluralForm is responsible to decode\n// locale keys to plural forms and match plural forms\n// based on the given pluralCount.\n//\n// See `pluralForm` package-level type for a default implementation.\ntype PluralForm interface {\n\tString() string\n\t// the string is a verified plural case's raw string value.\n\t// Field for priority on which order to register the plural cases.\n\tLess(next PluralForm) bool\n\tMatchPlural(pluralCount int) bool\n}\n\ntype pluralForm string\n\nfunc (f pluralForm) String() string {\n\treturn string(f)\n}\n\nfunc (f pluralForm) Less(next PluralForm) bool {\n\tform1 := f.String()\n\tform2 := next.String()\n\n\t// Order by\n\t// - equals,\n\t// - less than\n\t// - greater than\n\t// - \"zero\", \"one\", \"two\"\n\t// - rest is last \"other\".\n\tdig1, typ1, hasDig1 := formAtoi(form1)\n\tif typ1 == eq {\n\t\treturn true\n\t}\n\n\tdig2, typ2, hasDig2 := formAtoi(form2)\n\tif typ2 == eq {\n\t\treturn false\n\t}\n\n\t// digits smaller, number.\n\tif hasDig1 {\n\t\treturn !hasDig2 || dig1 < dig2\n\t}\n\n\tif hasDig2 {\n\t\treturn false\n\t}\n\n\tif form1 == \"other\" {\n\t\treturn false // other go to last.\n\t}\n\n\tif form2 == \"other\" {\n\t\treturn true\n\t}\n\n\tif form1 == \"zero\" {\n\t\treturn true\n\t}\n\n\tif form2 == \"zero\" {\n\t\treturn false\n\t}\n\n\tif form1 == \"one\" {\n\t\treturn true\n\t}\n\n\tif form2 == \"one\" {\n\t\treturn false\n\t}\n\n\tif form1 == \"two\" {\n\t\treturn true\n\t}\n\n\tif form2 == \"two\" {\n\t\treturn false\n\t}\n\n\treturn false\n}\n\nfunc (f pluralForm) MatchPlural(pluralCount int) bool {\n\tswitch f {\n\tcase \"other\":\n\t\treturn true\n\tcase \"=0\", \"zero\":\n\t\treturn pluralCount == 0\n\tcase \"=1\", \"one\":\n\t\treturn pluralCount == 1\n\tcase \"=2\", \"two\":\n\t\treturn pluralCount == 2\n\tdefault:\n\t\t// <5 or =5\n\n\t\tn, typ, ok := formAtoi(string(f))\n\t\tif !ok {\n\t\t\treturn false\n\t\t}\n\n\t\tswitch typ {\n\t\tcase eq:\n\t\t\treturn n == pluralCount\n\t\tcase lt:\n\t\t\treturn pluralCount < n\n\t\tcase gt:\n\t\t\treturn pluralCount > n\n\t\tdefault:\n\t\t\treturn false\n\t\t}\n\t}\n}\n\nfunc makeSelectfVars(text string, vars []Var, insidePlural bool) ([]catalog.Message, []Var) {\n\tnewVars := sortVars(text, vars)\n\tnewVars = removeVarsDuplicates(newVars)\n\tmsgs := selectfVars(newVars, insidePlural)\n\treturn msgs, newVars\n}\n\nfunc selectfVars(vars []Var, insidePlural bool) []catalog.Message {\n\tmsgs := make([]catalog.Message, 0, len(vars))\n\tfor _, variable := range vars {\n\t\targth := variable.Argth\n\t\tif insidePlural {\n\t\t\targth++\n\t\t}\n\n\t\tmsg := catalog.Var(variable.Name, plural.Selectf(argth, variable.Format, variable.Cases...))\n\t\t// fmt.Printf(\"%s:%d | cases | %#+v\\n\", variable.Name, variable.Argth, variable.Cases)\n\t\tmsgs = append(msgs, msg)\n\t}\n\n\treturn msgs\n}\n\nconst (\n\teq uint8 = iota + 1\n\tlt\n\tgt\n)\n\nfunc formType(ch byte) uint8 {\n\tswitch ch {\n\tcase '=':\n\t\treturn eq\n\tcase '<':\n\t\treturn lt\n\tcase '>':\n\t\treturn gt\n\t}\n\n\treturn 0\n}\n\nfunc formAtoi(form string) (int, uint8, bool) {\n\tif len(form) < 2 {\n\t\treturn -1, 0, false\n\t}\n\n\ttyp := formType(form[0])\n\tif typ == 0 {\n\t\treturn -1, 0, false\n\t}\n\n\tdig, err := strconv.Atoi(form[1:])\n\tif err != nil {\n\t\treturn -1, 0, false\n\t}\n\treturn dig, typ, true\n}\n\nfunc isDigit(ch byte) bool {\n\treturn '0' <= ch && ch <= '9'\n}\n"
  },
  {
    "path": "i18n/internal/template.go",
    "content": "package internal\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"text/template\"\n\n\t\"golang.org/x/text/message/catalog\"\n)\n\nconst (\n\t// VarsKey is the key for the message's variables, per locale(global) or per key (local).\n\tVarsKey = \"Vars\"\n\t// PluralCountKey is the key for the template's message pluralization.\n\tPluralCountKey = \"PluralCount\"\n\t// VarCountKeySuffix is the key suffix for the template's variable's pluralization,\n\t// e.g. HousesCount for ${Houses}.\n\tVarCountKeySuffix = \"Count\"\n\t// VarsKeySuffix is the key which the template message's variables\n\t// are stored with,\n\t// e.g. welcome.human.other_vars\n\tVarsKeySuffix = \"_vars\"\n)\n\n// Template is a Renderer which renders template messages.\ntype Template struct {\n\t*Message\n\ttmpl    *template.Template\n\tbufPool *sync.Pool\n}\n\n// NewTemplate returns a new Template message based on the\n// catalog and the base translation Message. See `Locale.Load` method.\nfunc NewTemplate(c *Catalog, m *Message) (*Template, error) {\n\ttmpl, err := template.New(m.Key).\n\t\tDelims(m.Locale.Options.Left, m.Locale.Options.Right).\n\t\tFuncs(m.Locale.FuncMap).\n\t\tParse(m.Value)\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err := registerTemplateVars(c, m); err != nil {\n\t\treturn nil, fmt.Errorf(\"template vars: <%s = %s>: %w\", m.Key, m.Value, err)\n\t}\n\n\tbufPool := &sync.Pool{\n\t\tNew: func() any {\n\t\t\treturn new(bytes.Buffer)\n\t\t},\n\t}\n\n\tt := &Template{\n\t\tMessage: m,\n\t\ttmpl:    tmpl,\n\t\tbufPool: bufPool,\n\t}\n\n\treturn t, nil\n}\n\nfunc registerTemplateVars(c *Catalog, m *Message) error {\n\tif len(m.Vars) == 0 {\n\t\treturn nil\n\t}\n\n\tmsgs := selectfVars(m.Vars, false)\n\n\tvariableText := \"\"\n\n\tfor _, variable := range m.Vars {\n\t\tvariableText += variable.Literal + \" \"\n\t}\n\n\tvariableText = variableText[0 : len(variableText)-1]\n\n\tfullKey := m.Key + \".\" + VarsKeySuffix\n\n\treturn c.Set(m.Locale.tag, fullKey, append(msgs, catalog.String(variableText))...)\n}\n\n// Render completes the Renderer interface.\n// It renders a template message.\n// Each key has its own Template, plurals too.\nfunc (t *Template) Render(args ...any) (string, error) {\n\tvar (\n\t\tdata   any\n\t\tresult string\n\t)\n\n\targsLength := len(args)\n\n\tif argsLength > 0 {\n\t\tdata = args[0]\n\t}\n\n\tbuf := t.bufPool.Get().(*bytes.Buffer)\n\tbuf.Reset()\n\n\tif err := t.tmpl.Execute(buf, data); err != nil {\n\t\tt.bufPool.Put(buf)\n\t\treturn \"\", err\n\t}\n\n\tresult = buf.String()\n\tt.bufPool.Put(buf)\n\n\tif len(t.Vars) > 0 {\n\t\t// get the variables plurals.\n\t\tif argsLength > 1 {\n\t\t\t// if has more than the map/struct\n\t\t\t// then let's assume the user passes variable counts by raw integer arguments.\n\t\t\targs = args[1:]\n\t\t} else if data != nil {\n\t\t\t// otherwise try to resolve them by the map(%var_name%Count)/struct(PlrualCounter).\n\t\t\targs = findVarsCount(data, t.Vars)\n\t\t}\n\t\tresult = t.replaceTmplVars(result, args...)\n\t}\n\n\treturn result, nil\n}\n\nfunc findVarsCount(data any, vars []Var) (args []any) {\n\tif data == nil {\n\t\treturn nil\n\t}\n\n\tswitch dataValue := data.(type) {\n\tcase PluralCounter:\n\t\tfor _, v := range vars {\n\t\t\tif count := dataValue.VarCount(v.Name); count >= 0 {\n\t\t\t\targs = append(args, count)\n\t\t\t}\n\t\t}\n\tcase Map:\n\t\tfor _, v := range vars {\n\t\t\tvarCountKey := v.Name + VarCountKeySuffix\n\t\t\tif value, ok := dataValue[varCountKey]; ok {\n\t\t\t\targs = append(args, value)\n\t\t\t}\n\t\t}\n\tcase map[string]string:\n\t\tfor _, v := range vars {\n\t\t\tvarCountKey := v.Name + VarCountKeySuffix\n\t\t\tif value, ok := dataValue[varCountKey]; ok {\n\t\t\t\tif count, err := strconv.Atoi(value); err == nil {\n\t\t\t\t\targs = append(args, count)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\tcase map[string]int:\n\t\tfor _, v := range vars {\n\t\t\tvarCountKey := v.Name + VarCountKeySuffix\n\t\t\tif value, ok := dataValue[varCountKey]; ok {\n\t\t\t\targs = append(args, value)\n\t\t\t}\n\t\t}\n\tdefault:\n\t\treturn nil\n\t}\n\n\treturn\n}\n\nfunc findPluralCount(data any) (int, bool) {\n\tif data == nil {\n\t\treturn -1, false\n\t}\n\n\tswitch dataValue := data.(type) {\n\tcase PluralCounter:\n\t\tif count := dataValue.PluralCount(); count >= 0 {\n\t\t\treturn count, true\n\t\t}\n\tcase Map:\n\t\tif v, ok := dataValue[PluralCountKey]; ok {\n\t\t\tif count, ok := v.(int); ok {\n\t\t\t\treturn count, true\n\t\t\t}\n\t\t}\n\tcase map[string]string:\n\t\tif v, ok := dataValue[PluralCountKey]; ok {\n\t\t\tcount, err := strconv.Atoi(v)\n\t\t\tif err != nil {\n\t\t\t\treturn -1, false\n\t\t\t}\n\n\t\t\treturn count, true\n\t\t}\n\n\tcase map[string]int:\n\t\tif count, ok := dataValue[PluralCountKey]; ok {\n\t\t\treturn count, true\n\t\t}\n\tcase int:\n\t\treturn dataValue, true // when this is not a template data, the caller's argument should be args[1:] now.\n\tcase int64:\n\t\tcount := int(dataValue)\n\t\treturn count, true\n\t}\n\n\treturn -1, false\n}\n\nfunc (t *Template) replaceTmplVars(result string, args ...any) string {\n\tvarsKey := t.Key + \".\" + VarsKeySuffix\n\ttranslationVarsText := t.Locale.Printer.Sprintf(varsKey, args...)\n\tif translationVarsText != \"\" {\n\t\ttranslatioVars := strings.Split(translationVarsText, \" \")\n\t\tfor i, variable := range t.Vars {\n\t\t\tresult = strings.Replace(result, variable.Literal, translatioVars[i], 1)\n\t\t}\n\t}\n\n\treturn result\n}\n\nfunc stringIsTemplateValue(value, left, right string) bool {\n\tleftIdx, rightIdx := strings.Index(value, left), strings.Index(value, right)\n\treturn leftIdx != -1 && rightIdx > leftIdx\n}\n\nfunc getFuncs(loc *Locale) template.FuncMap {\n\t// set the template funcs for this locale.\n\tfuncs := template.FuncMap{\n\t\t\"tr\": loc.GetMessage,\n\t}\n\n\tif getFuncs := loc.Options.Funcs; getFuncs != nil {\n\t\t// set current locale's template's funcs.\n\t\tfor k, v := range getFuncs(loc) {\n\t\t\tfuncs[k] = v\n\t\t}\n\t}\n\n\treturn funcs\n}\n"
  },
  {
    "path": "i18n/internal/var.go",
    "content": "package internal\n\nimport (\n\t\"regexp\"\n\t\"sort\"\n)\n\n// Var represents a message variable.\n// The variables, like the sub messages are sorted.\n// First: plurals (which again, are sorted)\n// and then any custom keys.\n// In variables, the sorting depends on the exact\n// order the associated message uses the variables.\n// This is extremely handy.\n// This package requires the golang.org/x/text/message capabilities\n// only for the variables feature, the message itself's pluralization is managed by the package.\ntype Var struct {\n\tName    string // Variable name, e.g. Name\n\tLiteral string // Its literal is ${Name}\n\tCases   []any  // one:...,few:...,...\n\tFormat  string // defaults to \"%d\".\n\tArgth   int    // 1, 2, 3...\n}\n\nfunc getVars(loc *Locale, key string, src map[string]any) []Var {\n\tif len(src) == 0 {\n\t\treturn nil\n\t}\n\n\tvarsKey, ok := src[key]\n\tif !ok {\n\t\treturn nil\n\t}\n\n\tvarValue, ok := varsKey.([]any)\n\tif !ok {\n\t\treturn nil\n\t}\n\n\tvars := make([]Var, 0, len(varValue))\n\n\tfor _, v := range varValue {\n\t\tm, ok := v.(map[string]any)\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\n\t\tfor k, inner := range m {\n\t\t\tvarFormat := \"%d\"\n\n\t\t\tinnerMap, ok := inner.(map[string]any)\n\t\t\tif !ok {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tfor kk, vv := range innerMap {\n\t\t\t\tif kk == \"format\" {\n\t\t\t\t\tif format, ok := vv.(string); ok {\n\t\t\t\t\t\tvarFormat = format\n\t\t\t\t\t}\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tcases := getCases(loc, innerMap)\n\n\t\t\tif len(cases) > 0 {\n\t\t\t\t// cases = sortCases(cases)\n\t\t\t\tvars = append(vars, Var{\n\t\t\t\t\tName:    k,\n\t\t\t\t\tLiteral: \"${\" + k + \"}\",\n\t\t\t\t\tCases:   cases,\n\t\t\t\t\tFormat:  varFormat,\n\t\t\t\t\tArgth:   1,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t}\n\n\tdelete(src, key) // delete the key after.\n\treturn vars\n}\n\nvar unescapeVariableRegex = regexp.MustCompile(`\\$\\{(.*?)}`)\n\nfunc sortVars(text string, vars []Var) (newVars []Var) {\n\targth := 1\n\tfor _, submatches := range unescapeVariableRegex.FindAllStringSubmatch(text, -1) {\n\t\tname := submatches[1]\n\t\tfor _, variable := range vars {\n\t\t\tif variable.Name == name {\n\t\t\t\tvariable.Argth = argth\n\t\t\t\tnewVars = append(newVars, variable)\n\t\t\t\targth++\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\n\tsort.SliceStable(newVars, func(i, j int) bool {\n\t\treturn newVars[i].Argth < newVars[j].Argth\n\t})\n\treturn\n}\n\n// it will panic if the incoming \"elements\" are not catmsg.Var (internal text package).\nfunc removeVarsDuplicates(elements []Var) (result []Var) {\n\tseen := make(map[string]struct{})\n\n\tfor v := range elements {\n\t\tvariable := elements[v]\n\t\tname := variable.Name\n\t\tif _, ok := seen[name]; !ok {\n\t\t\tseen[name] = struct{}{}\n\t\t\tresult = append(result, variable)\n\t\t}\n\t}\n\n\treturn result\n}\n\n/*\nfunc removeMsgVarsDuplicates(elements []catalog.Message) (result []catalog.Message) {\n\tseen := make(map[string]struct{})\n\n\tfor _, elem := range elements {\n\t\tval := reflect.Indirect(reflect.ValueOf(elem))\n\t\tif val.Type().String() != \"catmsg.Var\" {\n\t\t\t// keep.\n\t\t\tresult = append(result, elem)\n\t\t\tcontinue // it's not a var.\n\t\t}\n\t\tname := val.FieldByName(\"Name\").Interface().(string)\n\t\tif _, ok := seen[name]; !ok {\n\t\t\tseen[name] = struct{}{}\n\t\t\tresult = append(result, elem)\n\t\t}\n\t}\n\n\treturn\n}\n*/\n\nfunc getCases(loc *Locale, src map[string]any) []any {\n\ttype PluralCase struct {\n\t\tForm  PluralForm\n\t\tValue any\n\t}\n\n\tpluralCases := make([]PluralCase, 0, len(src))\n\n\tfor key, value := range src {\n\t\tform, ok := loc.Options.PluralFormDecoder(loc, key)\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\n\t\tpluralCases = append(pluralCases, PluralCase{\n\t\t\tForm:  form,\n\t\t\tValue: value,\n\t\t})\n\t}\n\n\tif len(pluralCases) == 0 {\n\t\treturn nil\n\t}\n\n\tsort.SliceStable(pluralCases, func(i, j int) bool {\n\t\tleft, right := pluralCases[i].Form, pluralCases[j].Form\n\t\treturn left.Less(right)\n\t})\n\n\tcases := make([]any, 0, len(pluralCases)*2)\n\tfor _, pluralCase := range pluralCases {\n\t\t// fmt.Printf(\"%s=%v\\n\", pluralCase.Form, pluralCase.Value)\n\t\tcases = append(cases, pluralCase.Form.String(), pluralCase.Value)\n\t}\n\n\treturn cases\n}\n"
  },
  {
    "path": "i18n/loader.go",
    "content": "package i18n\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/kataras/iris/v12/i18n/internal\"\n\n\t\"github.com/BurntSushi/toml\"\n\t\"gopkg.in/ini.v1\"\n\t\"gopkg.in/yaml.v3\"\n)\n\n// LoaderConfig the configuration structure which contains\n// some options about how the template loader should act.\n//\n// See `Glob` and `Assets` package-level functions.\ntype LoaderConfig = internal.Options\n\n// Glob accepts a glob pattern (see: https://golang.org/pkg/path/filepath/#Glob)\n// and loads the locale files based on any \"options\".\n//\n// The \"globPattern\" input parameter is a glob pattern which the default loader should\n// search and load for locale files.\n//\n// See `New` and `LoaderConfig` too.\nfunc Glob(globPattern string, options LoaderConfig) Loader {\n\tassetNames, err := filepath.Glob(globPattern)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treturn load(assetNames, os.ReadFile, options)\n}\n\n// Assets accepts a function that returns a list of filenames (physical or virtual),\n// another a function that should return the contents of a specific file\n// and any Loader options. Go-bindata usage.\n// It returns a valid `Loader` which loads and maps the locale files.\n//\n// See `Glob`, `FS`, `New` and `LoaderConfig` too.\nfunc Assets(assetNames func() []string, asset func(string) ([]byte, error), options LoaderConfig) Loader {\n\treturn load(assetNames(), asset, options)\n}\n\n// LoadFS loads the files using embed.FS or fs.FS or\n// http.FileSystem or string (local directory).\n// The \"pattern\" is a classic glob pattern.\n//\n// See `Glob`, `Assets`, `New` and `LoaderConfig` too.\nfunc FS(fileSystem fs.FS, pattern string, options LoaderConfig) (Loader, error) {\n\tpattern = strings.TrimPrefix(pattern, \"./\")\n\n\tassetNames, err := fs.Glob(fileSystem, pattern)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tassetFunc := func(name string) ([]byte, error) {\n\t\tf, err := fileSystem.Open(name)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\treturn io.ReadAll(f)\n\t}\n\n\treturn load(assetNames, assetFunc, options), nil\n}\n\n// LangMap key as language (e.g. \"el-GR\") and value as a map of key-value pairs (e.g. \"hello\": \"Γειά\").\ntype LangMap = map[string]map[string]any\n\n// KV is a loader which accepts a map of language(key) and the available key-value pairs.\n// Example Code:\n//\n//\tm := i18n.LangMap{\n//\t\t\"en-US\": map[string]any{\n//\t\t\t\"hello\": \"Hello\",\n//\t\t},\n//\t\t\"el-GR\": map[string]any{\n//\t\t\t\"hello\": \"Γειά\",\n//\t\t},\n//\t}\n//\n// app := iris.New()\n// [...]\n// app.I18N.LoadKV(m)\n// app.I18N.SetDefault(\"en-US\")\nfunc KV(langMap LangMap, opts ...LoaderConfig) Loader {\n\treturn func(m *Matcher) (Localizer, error) {\n\t\toptions := DefaultLoaderConfig\n\t\tif len(opts) > 0 {\n\t\t\toptions = opts[0]\n\t\t}\n\n\t\tlanguageIndexes := make([]int, 0, len(langMap))\n\t\tkeyValuesMulti := make([]map[string]any, 0, len(langMap))\n\n\t\tfor languageName, pairs := range langMap {\n\t\t\tlangIndex := parseLanguageName(m, languageName) // matches and adds the language tag to m.Languages.\n\t\t\tlanguageIndexes = append(languageIndexes, langIndex)\n\t\t\tkeyValuesMulti = append(keyValuesMulti, pairs)\n\t\t}\n\n\t\tcat, err := internal.NewCatalog(m.Languages, options)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tfor _, langIndex := range languageIndexes {\n\t\t\tif langIndex == -1 {\n\t\t\t\t// If loader has more languages than defined for use in New function,\n\t\t\t\t// e.g. when New(KV(m), \"en-US\") contains el-GR and en-US but only \"en-US\" passed.\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tkv := keyValuesMulti[langIndex]\n\t\t\terr := cat.Store(langIndex, kv)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\n\t\tif n := len(cat.Locales); n == 0 {\n\t\t\treturn nil, fmt.Errorf(\"locales not found in map\")\n\t\t} else if options.Strict && n < len(m.Languages) {\n\t\t\treturn nil, fmt.Errorf(\"locales expected to be %d but %d parsed\", len(m.Languages), n)\n\t\t}\n\n\t\treturn cat, nil\n\t}\n}\n\n// DefaultLoaderConfig represents the default loader configuration.\nvar DefaultLoaderConfig = LoaderConfig{\n\tLeft:               \"{{\",\n\tRight:              \"}}\",\n\tStrict:             false,\n\tDefaultMessageFunc: nil,\n\tPluralFormDecoder:  internal.DefaultPluralFormDecoder,\n\tFuncs:              nil,\n}\n\n// load accepts a list of filenames (physical or virtual),\n// a function that should return the contents of a specific file\n// and any Loader options.\n// It returns a valid `Loader` which loads and maps the locale files.\n//\n// See `FS`, `Glob`, `Assets` and `LoaderConfig` too.\nfunc load(assetNames []string, asset func(string) ([]byte, error), options LoaderConfig) Loader {\n\treturn func(m *Matcher) (Localizer, error) {\n\t\tlanguageFiles, err := m.ParseLanguageFiles(assetNames)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tif options.DefaultMessageFunc == nil {\n\t\t\toptions.DefaultMessageFunc = m.defaultMessageFunc\n\t\t}\n\n\t\tcat, err := internal.NewCatalog(m.Languages, options)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tfor langIndex, langFiles := range languageFiles {\n\t\t\tkeyValues := make(map[string]any)\n\n\t\t\tfor _, fileName := range langFiles {\n\t\t\t\tunmarshal := yaml.Unmarshal\n\t\t\t\tif idx := strings.LastIndexByte(fileName, '.'); idx > 1 {\n\t\t\t\t\tswitch fileName[idx:] {\n\t\t\t\t\tcase \".toml\", \".tml\":\n\t\t\t\t\t\tunmarshal = toml.Unmarshal\n\t\t\t\t\tcase \".json\":\n\t\t\t\t\t\tunmarshal = json.Unmarshal\n\t\t\t\t\tcase \".ini\":\n\t\t\t\t\t\tunmarshal = unmarshalINI\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tb, err := asset(fileName)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\n\t\t\t\tif err = unmarshal(b, &keyValues); err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t}\n\n\t\t\terr = cat.Store(langIndex, keyValues)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\n\t\tif n := len(cat.Locales); n == 0 {\n\t\t\treturn nil, fmt.Errorf(\"locales not found in %s\", strings.Join(assetNames, \", \"))\n\t\t} else if options.Strict && n < len(m.Languages) {\n\t\t\treturn nil, fmt.Errorf(\"locales expected to be %d but %d parsed\", len(m.Languages), n)\n\t\t}\n\n\t\treturn cat, nil\n\t}\n}\n\nfunc unmarshalINI(data []byte, v any) error {\n\tf, err := ini.Load(data)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tm := *v.(*map[string]any)\n\n\t// Includes the ini.DefaultSection which has the root keys too.\n\t// We don't have to iterate to each section to find the subsection,\n\t// the Sections() returns all sections, sub-sections are separated by dot '.'\n\t// and we match the dot with a section on the translate function, so we just save the values as they are,\n\t// so we don't have to do section lookup on every translate call.\n\tfor _, section := range f.Sections() {\n\t\tkeyPrefix := \"\"\n\t\tif name := section.Name(); name != ini.DefaultSection {\n\t\t\tkeyPrefix = name + \".\"\n\t\t}\n\n\t\tfor _, key := range section.Keys() {\n\t\t\tm[keyPrefix+key.Name()] = key.Value()\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "iris.go",
    "content": "package iris\n\nimport (\n\t\"bytes\"\n\tstdContext \"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"math\"\n\t\"net\"\n\t\"net/http\"\n\t\"os\"\n\t\"regexp\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/core/host\"\n\t\"github.com/kataras/iris/v12/core/netutil\"\n\t\"github.com/kataras/iris/v12/core/router\"\n\t\"github.com/kataras/iris/v12/i18n\"\n\t\"github.com/kataras/iris/v12/middleware/cors\"\n\t\"github.com/kataras/iris/v12/middleware/recover\"\n\t\"github.com/kataras/iris/v12/middleware/requestid\"\n\t\"github.com/kataras/iris/v12/view\"\n\n\t\"github.com/kataras/golog\"\n\t\"github.com/kataras/tunnel\"\n\n\t\"github.com/tdewolff/minify/v2\"\n\t\"github.com/tdewolff/minify/v2/css\"\n\t\"github.com/tdewolff/minify/v2/html\"\n\t\"github.com/tdewolff/minify/v2/js\"\n\t\"github.com/tdewolff/minify/v2/json\"\n\t\"github.com/tdewolff/minify/v2/svg\"\n\t\"github.com/tdewolff/minify/v2/xml\"\n)\n\n// Version is the current version of the Iris Web Framework.\nconst Version = \"12.2.11\"\n\n// Byte unit helpers.\nconst (\n\tB = 1 << (10 * iota)\n\tKB\n\tMB\n\tGB\n\tTB\n\tPB\n\tEB\n)\n\n// Application is responsible to manage the state of the application.\n// It contains and handles all the necessary parts to create a fast web server.\ntype Application struct {\n\t// routing embedded | exposing APIBuilder's and Router's public API.\n\t*router.APIBuilder\n\t*router.Router\n\trouter.HTTPErrorHandler // if Router is Downgraded this is nil.\n\tContextPool             *context.Pool\n\t// See SetContextErrorHandler, defaults to nil.\n\tcontextErrorHandler context.ErrorHandler\n\n\t// config contains the configuration fields\n\t// all fields defaults to something that is working, developers don't have to set it.\n\tconfig *Configuration\n\n\t// the golog logger instance, defaults to \"Info\" level messages (all except \"Debug\")\n\tlogger *golog.Logger\n\n\t// I18n contains localization and internationalization support.\n\t// Use the `Load` or `LoadAssets` to locale language files.\n\t//\n\t// See `Context#Tr` method for request-based translations.\n\tI18n *i18n.I18n\n\n\t// Validator is the request body validator, defaults to nil.\n\tValidator context.Validator\n\t// Minifier to minify responses.\n\tminifier *minify.M\n\n\t// view engine\n\tview *view.View\n\t// used for build\n\tbuilded     bool\n\tdefaultMode bool\n\t// OnBuild is a single function which\n\t// is fired on the first `Build` method call.\n\t// If reports an error then the execution\n\t// is stopped and the error is logged.\n\t// It's nil by default except when `Switch` instead of `New` or `Default`\n\t// is used to initialize the Application.\n\t// Users can wrap it to accept more events.\n\tOnBuild func() error\n\n\tmu sync.RWMutex\n\t// name is the application name and the log prefix for\n\t// that Application instance's Logger. See `SetName` and `String`.\n\t// Defaults to IRIS_APP_NAME envrinoment variable otherwise empty.\n\tname string\n\t// Hosts contains a list of all servers (Host Supervisors) that this app is running on.\n\t//\n\t// Hosts may be empty only if application ran(`app.Run`) with `iris.Raw` option runner,\n\t// otherwise it contains a single host (`app.Hosts[0]`).\n\t//\n\t// Additional Host Supervisors can be added to that list by calling the `app.NewHost` manually.\n\t//\n\t// Hosts field is available after `Run` or `NewHost`.\n\tHosts             []*host.Supervisor\n\thostConfigurators []host.Configurator\n\trunError          error\n\trunErrorMu        sync.RWMutex\n}\n\n// New creates and returns a fresh empty iris *Application instance.\nfunc New() *Application {\n\tconfig := DefaultConfiguration()\n\tapp := &Application{\n\t\tconfig:   &config,\n\t\tRouter:   router.NewRouter(),\n\t\tI18n:     i18n.New(),\n\t\tminifier: newMinifier(),\n\t\tview:     new(view.View),\n\t}\n\n\tlogger := newLogger(app)\n\tapp.logger = logger\n\tapp.APIBuilder = router.NewAPIBuilder(logger)\n\tapp.ContextPool = context.New(func() any {\n\t\treturn context.NewContext(app)\n\t})\n\n\tcontext.RegisterApplication(app)\n\treturn app\n}\n\n// Default returns a new Application.\n// Default with \"debug\" Logger Level.\n// Localization enabled on \"./locales\" directory\n// and HTML templates on \"./views\" or \"./templates\" directory.\n// CORS (allow all), Recovery and\n// Request ID middleware already registered.\nfunc Default() *Application {\n\tapp := New()\n\t// Set default log level.\n\tapp.logger.SetLevel(\"debug\")\n\tapp.logger.Debugf(`Log level set to \"debug\"`)\n\n\t/* #2046.\n\t// Register the accesslog middleware.\n\tlogFile, err := os.OpenFile(\"./access.log\", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o600)\n\tif err == nil {\n\t\t// Close the file on shutdown.\n\t\tapp.ConfigureHost(func(su *Supervisor) {\n\t\t\tsu.RegisterOnShutdown(func() {\n\t\t\t\tlogFile.Close()\n\t\t\t})\n\t\t})\n\n\t\tac := accesslog.New(logFile)\n\t\tac.AddOutput(app.logger.Printer)\n\t\tapp.UseRouter(ac.Handler)\n\t\tapp.logger.Debugf(\"Using <%s> to log requests\", logFile.Name())\n\t}\n\t*/\n\n\t// Register the requestid middleware\n\t// before recover so current Context.GetID() contains the info on panic logs.\n\tapp.UseRouter(requestid.New())\n\tapp.logger.Debugf(\"Using <UUID4> to identify requests\")\n\n\t// Register the recovery, after accesslog and recover,\n\t// before end-developer's middleware.\n\tapp.UseRouter(recover.New())\n\n\t// Register CORS (allow any origin to pass through) middleware.\n\tapp.UseRouter(cors.New().\n\t\tExtractOriginFunc(cors.DefaultOriginExtractor).\n\t\tReferrerPolicy(cors.NoReferrerWhenDowngrade).\n\t\tAllowOriginFunc(cors.AllowAnyOrigin).\n\t\tHandler())\n\n\tapp.defaultMode = true\n\n\treturn app\n}\n\nfunc newLogger(app *Application) *golog.Logger {\n\tlogger := golog.Default.Child(app)\n\tif name := os.Getenv(\"IRIS_APP_NAME\"); name != \"\" {\n\t\tapp.name = name\n\t\tlogger.SetChildPrefix(name)\n\t}\n\n\treturn logger\n}\n\n// SetName sets a unique name to this Iris Application.\n// It sets a child prefix for the current Application's Logger.\n// Look `String` method too.\n//\n// It returns this Application.\nfunc (app *Application) SetName(appName string) *Application {\n\tapp.mu.Lock()\n\tdefer app.mu.Unlock()\n\n\tif app.name == \"\" {\n\t\tapp.logger.SetChildPrefix(appName)\n\t}\n\tapp.name = appName\n\n\treturn app\n}\n\n// String completes the fmt.Stringer interface and it returns\n// the application's name.\n// If name was not set by `SetName` or `IRIS_APP_NAME` environment variable\n// then this will return an empty string.\nfunc (app *Application) String() string {\n\treturn app.name\n}\n\n// WWW creates and returns a \"www.\" subdomain.\n// The difference from `app.Subdomain(\"www\")` or `app.Party(\"www.\")` is that the `app.WWW()` method\n// wraps the router so all http(s)://mydomain.com will be redirect to http(s)://www.mydomain.com.\n// Other subdomains can be registered using the app: `sub := app.Subdomain(\"mysubdomain\")`,\n// child subdomains can be registered using the www := app.WWW(); www.Subdomain(\"wwwchildSubdomain\").\nfunc (app *Application) WWW() router.Party {\n\treturn app.SubdomainRedirect(app, app.Subdomain(\"www\"))\n}\n\n// SubdomainRedirect registers a router wrapper which\n// redirects(StatusMovedPermanently) a (sub)domain to another subdomain or to the root domain as fast as possible,\n// before the router's try to execute route's handler(s).\n//\n// It receives two arguments, they are the from and to/target locations,\n// 'from' can be a wildcard subdomain as well (app.WildcardSubdomain())\n// 'to' is not allowed to be a wildcard for obvious reasons,\n// 'from' can be the root domain(app) when the 'to' is not the root domain and visa-versa.\n//\n// Usage:\n// www := app.Subdomain(\"www\") <- same as app.Party(\"www.\")\n// app.SubdomainRedirect(app, www)\n// This will redirect all http(s)://mydomain.com/%anypath% to http(s)://www.mydomain.com/%anypath%.\n//\n// One or more subdomain redirects can be used to the same app instance.\n//\n// If you need more information about this implementation then you have to navigate through\n// the `core/router#NewSubdomainRedirectWrapper` function instead.\n//\n// Example: https://github.com/kataras/iris/tree/main/_examples/routing/subdomains/redirect\nfunc (app *Application) SubdomainRedirect(from, to router.Party) router.Party {\n\tsd := router.NewSubdomainRedirectWrapper(app.ConfigurationReadOnly().GetVHost, from.GetRelPath(), to.GetRelPath())\n\tapp.Router.AddRouterWrapper(sd)\n\treturn to\n}\n\n// Configure can called when modifications to the framework instance needed.\n// It accepts the framework instance\n// and returns an error which if it's not nil it's printed to the logger.\n// See configuration.go for more.\n//\n// Returns itself in order to be used like `app:= New().Configure(...)`\nfunc (app *Application) Configure(configurators ...Configurator) *Application {\n\tfor _, cfg := range configurators {\n\t\tif cfg != nil {\n\t\t\tcfg(app)\n\t\t}\n\t}\n\n\treturn app\n}\n\n// ConfigurationReadOnly returns an object which doesn't allow field writing.\nfunc (app *Application) ConfigurationReadOnly() context.ConfigurationReadOnly {\n\treturn app.config\n}\n\n// Logger returns the golog logger instance(pointer) that is being used inside the \"app\".\n//\n// Available levels:\n// - \"disable\"\n// - \"fatal\"\n// - \"error\"\n// - \"warn\"\n// - \"info\"\n// - \"debug\"\n// Usage: app.Logger().SetLevel(\"error\")\n// Or set the level through Configurartion's LogLevel or WithLogLevel functional option.\n// Defaults to \"info\" level.\n//\n// Callers can use the application's logger which is\n// the same `golog.Default.LastChild()` logger,\n// to print custom logs too.\n// Usage:\n// app.Logger().Error/Errorf(\"...\")\n// app.Logger().Warn/Warnf(\"...\")\n// app.Logger().Info/Infof(\"...\")\n// app.Logger().Debug/Debugf(\"...\")\n//\n// Setting one or more outputs: app.Logger().SetOutput(io.Writer...)\n// Adding one or more outputs : app.Logger().AddOutput(io.Writer...)\n//\n// Adding custom levels requires import of the `github.com/kataras/golog` package:\n//\n//\tFirst we create our level to a golog.Level\n//\tin order to be used in the Log functions.\n//\tvar SuccessLevel golog.Level = 6\n//\tRegister our level, just three fields.\n//\tgolog.Levels[SuccessLevel] = &golog.LevelMetadata{\n//\t\tName:    \"success\",\n//\t\tRawText: \"[SUCC]\",\n//\t\t// ColorfulText (Green Color[SUCC])\n//\t\tColorfulText: \"\\x1b[32m[SUCC]\\x1b[0m\",\n//\t}\n//\n// Usage:\n// app.Logger().SetLevel(\"success\")\n// app.Logger().Logf(SuccessLevel, \"a custom leveled log message\")\nfunc (app *Application) Logger() *golog.Logger {\n\treturn app.logger\n}\n\n// IsDebug reports whether the application is running\n// under debug/development mode.\n// It's just a shortcut of Logger().Level >= golog.DebugLevel.\n// The same method existss as Context.IsDebug() too.\nfunc (app *Application) IsDebug() bool {\n\treturn app.logger.Level >= golog.DebugLevel\n}\n\n// I18nReadOnly returns the i18n's read-only features.\n// See `I18n` method for more.\nfunc (app *Application) I18nReadOnly() context.I18nReadOnly {\n\treturn app.I18n\n}\n\n// Validate validates a value and returns nil if passed or\n// the failure reason if does not.\nfunc (app *Application) Validate(v any) error {\n\tif app.Validator == nil {\n\t\treturn nil\n\t}\n\n\t// val := reflect.ValueOf(v)\n\t// if val.Kind() == reflect.Ptr && !val.IsNil() {\n\t// \tval = val.Elem()\n\t// }\n\n\t// if val.Kind() == reflect.Struct && val.Type() != timeType {\n\t// \treturn app.Validator.Struct(v)\n\t// }\n\n\t// no need to check the kind, underline lib does it but in the future this may change (look above).\n\terr := app.Validator.Struct(v)\n\tif err != nil {\n\t\tif !strings.HasPrefix(err.Error(), \"validator: \") {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc newMinifier() *minify.M {\n\tm := minify.New()\n\tm.AddFunc(\"text/css\", css.Minify)\n\tm.AddFunc(\"text/html\", html.Minify)\n\tm.AddFunc(\"image/svg+xml\", svg.Minify)\n\tm.AddFuncRegexp(regexp.MustCompile(\"^(application|text)/(x-)?(java|ecma)script$\"), js.Minify)\n\tm.AddFuncRegexp(regexp.MustCompile(\"[/+]json$\"), json.Minify)\n\tm.AddFuncRegexp(regexp.MustCompile(\"[/+]xml$\"), xml.Minify)\n\treturn m\n}\n\n// Minify is a middleware which minifies the responses\n// based on the response content type.\n// Note that minification might be slower, caching is advised.\n// Customize the minifier through `Application.Minifier()`.\n// Usage:\n// app.Use(iris.Minify)\nfunc Minify(ctx Context) {\n\tw := ctx.Application().Minifier().ResponseWriter(ctx.ResponseWriter().Naive(), ctx.Request())\n\t// Note(@kataras):\n\t// We don't use defer w.Close()\n\t// because this response writer holds a sync.WaitGroup under the hoods\n\t// and we MUST be sure that its wg.Wait is called on request cancelation\n\t// and not in the end of handlers chain execution\n\t// (which if running a time-consuming task it will delay its resource release).\n\tctx.OnCloseErr(w.Close)\n\tctx.ResponseWriter().SetWriter(w)\n\tctx.Next()\n}\n\n// Minifier returns the minifier instance.\n// By default it can minifies:\n// - text/html\n// - text/css\n// - image/svg+xml\n// - application/text(javascript, ecmascript, json, xml).\n// Use that instance to add custom Minifiers before server ran.\nfunc (app *Application) Minifier() *minify.M {\n\treturn app.minifier\n}\n\n// RegisterView registers a view engine for the application.\n// Children can register their own too. If no Party view Engine is registered\n// then this one will be used to render the templates instead.\nfunc (app *Application) RegisterView(viewEngine view.Engine) {\n\tapp.view.Register(viewEngine)\n}\n\n// View executes and writes the result of a template file to the writer.\n//\n// First parameter is the writer to write the parsed template.\n// Second parameter is the relative, to templates directory, template filename, including extension.\n// Third parameter is the layout, can be empty string.\n// Forth parameter is the bindable data to the template, can be nil.\n//\n// Use context.View to render templates to the client instead.\n// Returns an error on failure, otherwise nil.\nfunc (app *Application) View(writer io.Writer, filename string, layout string, bindingData any) error {\n\tif !app.view.Registered() {\n\t\terr := errors.New(\"view engine is missing, use `RegisterView`\")\n\t\tapp.logger.Error(err)\n\t\treturn err\n\t}\n\n\treturn app.view.ExecuteWriter(writer, filename, layout, bindingData)\n}\n\n// GetContextPool returns the Iris sync.Pool which holds the contexts values.\n// Iris automatically releases the request context, so you don't have to use it.\n// It's only useful to manually release the context on cases that connection\n// is hijacked by a third-party middleware and the http handler return too fast.\nfunc (app *Application) GetContextPool() *context.Pool {\n\treturn app.ContextPool\n}\n\n// SetContextErrorHandler can optionally register a handler to handle\n// and fire a customized error body to the client on JSON write failures.\n//\n// ExampleCode:\n//\n//\t type contextErrorHandler struct{}\n//\t func (e *contextErrorHandler) HandleContextError(ctx iris.Context, err error) {\n//\t\t errors.HandleError(ctx, err)\n//\t }\n//\t ...\n//\t app.SetContextErrorHandler(new(contextErrorHandler))\nfunc (app *Application) SetContextErrorHandler(errHandler context.ErrorHandler) *Application {\n\tapp.contextErrorHandler = errHandler\n\treturn app\n}\n\n// GetContextErrorHandler returns the handler which handles errors\n// on JSON write failures.\nfunc (app *Application) GetContextErrorHandler() context.ErrorHandler {\n\treturn app.contextErrorHandler\n}\n\n// ConfigureHost accepts one or more `host#Configuration`, these configurators functions\n// can access the host created by `app.Run` or `app.Listen`,\n// they're being executed when application is ready to being served to the public.\n//\n// It's an alternative way to interact with a host that is automatically created by\n// `app.Run`.\n//\n// These \"configurators\" can work side-by-side with the `iris#Addr, iris#Server, iris#TLS, iris#AutoTLS, iris#Listener`\n// final arguments(\"hostConfigs\") too.\n//\n// Note that these application's host \"configurators\" will be shared with the rest of\n// the hosts that this app will may create (using `app.NewHost`), meaning that\n// `app.NewHost` will execute these \"configurators\" everytime that is being called as well.\n//\n// These \"configurators\" should be registered before the `app.Run` or `host.Serve/Listen` functions.\nfunc (app *Application) ConfigureHost(configurators ...host.Configurator) *Application {\n\tapp.mu.Lock()\n\tapp.hostConfigurators = append(app.hostConfigurators, configurators...)\n\tapp.mu.Unlock()\n\treturn app\n}\n\nconst serverLoggerPrefix = \"[HTTP Server] \"\n\ntype customHostServerLogger struct { // see #1875\n\tparent     io.Writer\n\tignoreLogs [][]byte\n}\n\nvar newLineBytes = []byte(\"\\n\")\n\nfunc newCustomHostServerLogger(w io.Writer, ignoreLogs []string) *customHostServerLogger {\n\tprefixAsByteSlice := []byte(serverLoggerPrefix)\n\n\t// build the ignore lines.\n\tignoreLogsAsByteSlice := make([][]byte, 0, len(ignoreLogs))\n\tfor _, s := range ignoreLogs {\n\t\tignoreLogsAsByteSlice = append(ignoreLogsAsByteSlice, append(prefixAsByteSlice, []byte(s)...)) // append([]byte(s), newLineBytes...)\n\t}\n\n\treturn &customHostServerLogger{\n\t\tparent:     w,\n\t\tignoreLogs: ignoreLogsAsByteSlice,\n\t}\n}\n\nfunc (l *customHostServerLogger) Write(p []byte) (int, error) {\n\tfor _, ignoredLogBytes := range l.ignoreLogs {\n\t\tif bytes.Equal(bytes.TrimSuffix(p, newLineBytes), ignoredLogBytes) {\n\t\t\treturn 0, nil\n\t\t}\n\t}\n\n\treturn l.parent.Write(p)\n}\n\n// this may change during parallel jobs (see Application.NonBlocking & Wait).\nfunc (app *Application) getVHost() string {\n\tapp.mu.RLock()\n\tvhost := app.config.VHost\n\tapp.mu.RUnlock()\n\treturn vhost\n}\n\nfunc (app *Application) setVHost(vhost string) {\n\tapp.mu.Lock()\n\tapp.config.VHost = vhost\n\tapp.mu.Unlock()\n}\n\n// NewHost accepts a standard *http.Server object,\n// completes the necessary missing parts of that \"srv\"\n// and returns a new, ready-to-use, host (supervisor).\nfunc (app *Application) NewHost(srv *http.Server) *host.Supervisor {\n\tif app.getVHost() == \"\" { // vhost now is useful for router subdomain on wildcard subdomains,\n\t\t// in order to correct decide what to do on:\n\t\t// mydomain.com -> invalid\n\t\t// localhost -> invalid\n\t\t// sub.mydomain.com -> valid\n\t\t// sub.localhost -> valid\n\t\t// we need the host (without port if 80 or 443) in order to validate these, so:\n\t\tapp.setVHost(netutil.ResolveVHost(srv.Addr))\n\t} else {\n\t\tcontext.GetDomain = func(_ string) string { // #1886\n\t\t\treturn app.config.VHost // GetVHost: here we don't need mutex protection as it's request-time and all modifications are already made.\n\t\t}\n\t} // before lock.\n\n\tapp.mu.Lock()\n\tdefer app.mu.Unlock()\n\n\t// set the server's handler to the framework's router\n\tif srv.Handler == nil {\n\t\tsrv.Handler = app.Router\n\t}\n\n\t// check if different ErrorLog provided, if not bind it with the framework's logger.\n\tif srv.ErrorLog == nil {\n\t\tserverLogger := newCustomHostServerLogger(app.logger.Printer.Output, app.config.IgnoreServerErrors)\n\t\tsrv.ErrorLog = log.New(serverLogger, serverLoggerPrefix, 0)\n\t}\n\n\tif addr := srv.Addr; addr == \"\" {\n\t\taddr = \":8080\"\n\t\tif len(app.Hosts) > 0 {\n\t\t\tif v := app.Hosts[0].Server.Addr; v != \"\" {\n\t\t\t\taddr = v\n\t\t\t}\n\t\t}\n\n\t\tsrv.Addr = addr\n\t}\n\n\t// app.logger.Debugf(\"Host: addr is %s\", srv.Addr)\n\n\t// create the new host supervisor\n\t// bind the constructed server and return it\n\tsu := host.New(srv)\n\t// app.logger.Debugf(\"Host: virtual host is %s\", app.config.VHost)\n\n\t// the below schedules some tasks that will run among the server\n\n\tif !app.config.DisableStartupLog {\n\t\tprinter := app.logger.Printer.Output\n\t\thostPrinter := host.WriteStartupLogOnServe(printer)\n\t\tif len(app.Hosts) == 0 { // print the version info on the first running host.\n\t\t\tsu.RegisterOnServe(func(h host.TaskHost) {\n\t\t\t\thasBuildInfo := BuildTime != \"\" && BuildRevision != \"\"\n\t\t\t\ttab := \" \"\n\t\t\t\tif hasBuildInfo {\n\t\t\t\t\ttab = \"   \"\n\t\t\t\t}\n\t\t\t\tfmt.Fprintf(printer, \"Iris Version:%s%s\\n\", tab, Version)\n\n\t\t\t\tif hasBuildInfo {\n\t\t\t\t\tfmt.Fprintf(printer, \"Build Time:     %s\\nBuild Revision: %s\\n\", BuildTime, BuildRevision)\n\t\t\t\t}\n\t\t\t\tfmt.Fprintln(printer)\n\n\t\t\t\thostPrinter(h)\n\t\t\t})\n\t\t} else {\n\t\t\tsu.RegisterOnServe(hostPrinter)\n\t\t}\n\n\t\t// app.logger.Debugf(\"Host: register startup notifier\")\n\t}\n\n\tif !app.config.DisableInterruptHandler {\n\t\t// when CTRL/CMD+C pressed.\n\t\tshutdownTimeout := 10 * time.Second\n\t\tRegisterOnInterrupt(host.ShutdownOnInterrupt(su, shutdownTimeout))\n\t\t// app.logger.Debugf(\"Host: register server shutdown on interrupt(CTRL+C/CMD+C)\")\n\t}\n\n\tsu.IgnoredErrors = append(su.IgnoredErrors, app.config.IgnoreServerErrors...)\n\tif len(su.IgnoredErrors) > 0 {\n\t\tapp.logger.Debugf(\"Host: server will ignore the following errors: %s\", su.IgnoredErrors)\n\t}\n\n\tsu.Configure(app.hostConfigurators...)\n\n\tapp.Hosts = append(app.Hosts, su)\n\n\treturn su\n}\n\n// func (app *Application) OnShutdown(closers ...func()) {\n// \tfor _,cb := range closers {\n// \t\tif cb == nil {\n// \t\t\tcontinue\n// \t\t}\n// \t\tRegisterOnInterrupt(cb)\n// \t}\n// }\n\n// Shutdown gracefully terminates all the application's server hosts and any tunnels.\n// Returns an error on the first failure, otherwise nil.\nfunc (app *Application) Shutdown(ctx stdContext.Context) error {\n\tapp.mu.Lock()\n\tdefer app.mu.Unlock()\n\tdefer app.setRunError(ErrServerClosed) // make sure to set the error so any .Wait calls return.\n\n\tfor i, su := range app.Hosts {\n\t\tapp.logger.Debugf(\"Host[%d]: Shutdown now\", i)\n\t\tif err := su.Shutdown(ctx); err != nil {\n\t\t\tapp.logger.Debugf(\"Host[%d]: Error while trying to shutdown\", i)\n\t\t\treturn err\n\t\t}\n\t}\n\n\tfor _, t := range app.config.Tunneling.Tunnels {\n\t\tif t.Name == \"\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tif err := app.config.Tunneling.StopTunnel(t); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// Build sets up, once, the framework.\n// It builds the default router with its default macros\n// and the template functions that are very-closed to iris.\n//\n// If error occurred while building the Application, the returns type of error will be an *errgroup.Group\n// which let the callers to inspect the errors and cause, usage:\n//\n// import \"github.com/kataras/iris/v12/core/errgroup\"\n//\n//\terrgroup.Walk(app.Build(), func(typ any, err error) {\n//\t\tapp.Logger().Errorf(\"%s: %s\", typ, err)\n//\t})\nfunc (app *Application) Build() error {\n\tif app.builded {\n\t\treturn nil\n\t}\n\n\tif cb := app.OnBuild; cb != nil {\n\t\tif err := cb(); err != nil {\n\t\t\treturn fmt.Errorf(\"build: %w\", err)\n\t\t}\n\t}\n\n\t// start := time.Now()\n\tapp.builded = true // even if fails.\n\n\t// check if a prior app.Logger().SetLevel called and if not\n\t// then set the defined configuration's log level.\n\tif app.logger.Level == golog.InfoLevel /* the default level */ {\n\t\tapp.logger.SetLevel(app.config.LogLevel)\n\t}\n\n\tif app.defaultMode { // the app.I18n and app.View will be not available until Build.\n\t\tif !app.I18n.Loaded() {\n\t\t\tfor _, s := range []string{\"./locales/*/*\", \"./locales/*\", \"./translations\"} {\n\t\t\t\tif _, err := os.Stat(s); err != nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tif err := app.I18n.Load(s); err != nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tapp.I18n.SetDefault(\"en-US\")\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tif !app.view.Registered() {\n\t\t\tfor _, s := range []string{\"./views\", \"./templates\", \"./web/views\"} {\n\t\t\t\tif _, err := os.Stat(s); err != nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tapp.RegisterView(HTML(s, \".html\"))\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\n\tif app.I18n.Loaded() {\n\t\t// {{ tr \"lang\" \"key\" arg1 arg2 }}\n\t\tapp.view.AddFunc(\"tr\", app.I18n.Tr)\n\t\tapp.Router.PrependRouterWrapper(app.I18n.Wrapper())\n\t}\n\n\tif app.view.Registered() {\n\t\tapp.logger.Debugf(\"Application: view engine %q is registered\", app.view.Name())\n\t\t// view engine\n\t\t// here is where we declare the closed-relative framework functions.\n\t\t// Each engine has their defaults, i.e yield,render,render_r,partial, params...\n\t\trv := router.NewRoutePathReverser(app.APIBuilder)\n\t\tapp.view.AddFunc(\"urlpath\", rv.Path)\n\t\t// app.view.AddFunc(\"url\", rv.URL)\n\t\tif err := app.view.Load(); err != nil {\n\t\t\treturn fmt.Errorf(\"build: view engine: %v\", err)\n\t\t}\n\t}\n\n\tif !app.Router.Downgraded() {\n\t\t// router\n\t\tif _, err := injectLiveReload(app); err != nil {\n\t\t\treturn fmt.Errorf(\"build: inject live reload: failed: %v\", err)\n\t\t}\n\n\t\tif app.config.ForceLowercaseRouting {\n\t\t\t// This should always be executed first.\n\t\t\tapp.Router.PrependRouterWrapper(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {\n\t\t\t\tr.Host = strings.ToLower(r.Host)\n\t\t\t\tr.URL.Host = strings.ToLower(r.URL.Host)\n\t\t\t\tr.URL.Path = strings.ToLower(r.URL.Path)\n\t\t\t\tnext(w, r)\n\t\t\t})\n\t\t}\n\n\t\t// create the request handler, the default routing handler\n\t\trouterHandler := router.NewDefaultHandler(app.config, app.logger)\n\t\terr := app.Router.BuildRouter(app.ContextPool, routerHandler, app.APIBuilder, false)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"build: router: %w\", err)\n\t\t}\n\t\tapp.HTTPErrorHandler = routerHandler\n\n\t\tif app.config.Timeout > 0 {\n\t\t\tapp.Router.SetTimeoutHandler(app.config.Timeout, app.config.TimeoutMessage)\n\n\t\t\tapp.ConfigureHost(func(su *Supervisor) {\n\t\t\t\tif su.Server.ReadHeaderTimeout == 0 {\n\t\t\t\t\tsu.Server.ReadHeaderTimeout = app.config.Timeout + 5*time.Second\n\t\t\t\t}\n\n\t\t\t\tif su.Server.ReadTimeout == 0 {\n\t\t\t\t\tsu.Server.ReadTimeout = app.config.Timeout + 10*time.Second\n\t\t\t\t}\n\n\t\t\t\tif su.Server.WriteTimeout == 0 {\n\t\t\t\t\tsu.Server.WriteTimeout = app.config.Timeout + 15*time.Second\n\t\t\t\t}\n\n\t\t\t\tif su.Server.IdleTimeout == 0 {\n\t\t\t\t\tsu.Server.IdleTimeout = app.config.Timeout + 25*time.Second\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\n\t\t// re-build of the router from outside can be done with\n\t\t// app.RefreshRouter()\n\t}\n\n\t// if end := time.Since(start); end.Seconds() > 5 {\n\t// app.logger.Debugf(\"Application: build took %s\", time.Since(start))\n\n\treturn nil\n}\n\n// Runner is just an interface which accepts the framework instance\n// and returns an error.\n//\n// It can be used to register a custom runner with `Run` in order\n// to set the framework's server listen action.\n//\n// Currently `Runner` is being used to declare the builtin server listeners.\n//\n// See `Run` for more.\ntype Runner func(*Application) error\n\n// Listener can be used as an argument for the `Run` method.\n// It can start a server with a custom net.Listener via server's `Serve`.\n//\n// Second argument is optional, it accepts one or more\n// `func(*host.Configurator)` that are being executed\n// on that specific host that this function will create to start the server.\n// Via host configurators you can configure the back-end host supervisor,\n// i.e to add events for shutdown, serve or error.\n// An example of this use case can be found at:\n// https://github.com/kataras/iris/blob/main/_examples/http-server/notify-on-shutdown/main.go\n// Look at the `ConfigureHost` too.\n//\n// See `Run` for more.\nfunc Listener(l net.Listener, hostConfigs ...host.Configurator) Runner {\n\treturn func(app *Application) error {\n\t\tapp.config.SetVHost(netutil.ResolveVHost(l.Addr().String()))\n\t\treturn app.NewHost(&http.Server{Addr: l.Addr().String()}).\n\t\t\tConfigure(hostConfigs...).\n\t\t\tServe(l)\n\t}\n}\n\n// Server can be used as an argument for the `Run` method.\n// It can start a server with a *http.Server.\n//\n// Second argument is optional, it accepts one or more\n// `func(*host.Configurator)` that are being executed\n// on that specific host that this function will create to start the server.\n// Via host configurators you can configure the back-end host supervisor,\n// i.e to add events for shutdown, serve or error.\n// An example of this use case can be found at:\n// https://github.com/kataras/iris/blob/main/_examples/http-server/notify-on-shutdown/main.go\n// Look at the `ConfigureHost` too.\n//\n// See `Run` for more.\nfunc Server(srv *http.Server, hostConfigs ...host.Configurator) Runner {\n\treturn func(app *Application) error {\n\t\treturn app.NewHost(srv).\n\t\t\tConfigure(hostConfigs...).\n\t\t\tListenAndServe()\n\t}\n}\n\n// Addr can be used as an argument for the `Run` method.\n// It accepts a host address which is used to build a server\n// and a listener which listens on that host and port.\n//\n// Addr should have the form of [host]:port, i.e localhost:8080 or :8080.\n//\n// Second argument is optional, it accepts one or more\n// `func(*host.Configurator)` that are being executed\n// on that specific host that this function will create to start the server.\n// Via host configurators you can configure the back-end host supervisor,\n// i.e to add events for shutdown, serve or error.\n// An example of this use case can be found at:\n// https://github.com/kataras/iris/blob/main/_examples/http-server/notify-on-shutdown/main.go\n// Look at the `ConfigureHost` too.\n//\n// See `Run` for more.\nfunc Addr(addr string, hostConfigs ...host.Configurator) Runner {\n\treturn func(app *Application) error {\n\t\treturn app.NewHost(&http.Server{Addr: addr}).\n\t\t\tConfigure(hostConfigs...).\n\t\t\tListenAndServe()\n\t}\n}\n\nvar (\n\t// TLSNoRedirect is a `host.Configurator` which can be passed as last argument\n\t// to the `TLS` runner function. It disables the automatic\n\t// registration of redirection from \"http://\" to \"https://\" requests.\n\t// Applies only to the `TLS` runner.\n\t// See `AutoTLSNoRedirect` to register a custom fallback server for `AutoTLS` runner.\n\tTLSNoRedirect = func(su *host.Supervisor) { su.NoRedirect() }\n\t// AutoTLSNoRedirect is a `host.Configurator`.\n\t// It registers a fallback HTTP/1.1 server for the `AutoTLS` one.\n\t// The function accepts the letsencrypt wrapper and it\n\t// should return a valid instance of http.Server which its handler should be the result\n\t// of the \"acmeHandler\" wrapper.\n\t// Usage:\n\t//\t getServer := func(acme func(http.Handler) http.Handler) *http.Server {\n\t//\t     srv := &http.Server{Handler: acme(yourCustomHandler), ...otherOptions}\n\t//\t     go srv.ListenAndServe()\n\t//\t     return srv\n\t//   }\n\t//   app.Run(iris.AutoTLS(\":443\", \"example.com example2.com\", \"mail@example.com\", getServer))\n\t//\n\t// Note that if Server.Handler is nil then the server is automatically ran\n\t// by the framework and the handler set to automatic redirection, it's still\n\t// a valid option when the caller wants just to customize the server's fields (except Addr).\n\t// With this host configurator the caller can customize the server\n\t// that letsencrypt relies to perform the challenge.\n\t// LetsEncrypt Certification Manager relies on http://example.com/.well-known/acme-challenge/<TOKEN>.\n\tAutoTLSNoRedirect = func(getFallbackServer func(acmeHandler func(fallback http.Handler) http.Handler) *http.Server) host.Configurator {\n\t\treturn func(su *host.Supervisor) {\n\t\t\tsu.NoRedirect()\n\t\t\tsu.Fallback = getFallbackServer\n\t\t}\n\t}\n)\n\n// TLS can be used as an argument for the `Run` method.\n// It will start the Application's secure server.\n//\n// Use it like you used to use the http.ListenAndServeTLS function.\n//\n// Addr should have the form of [host]:port, i.e localhost:443 or :443.\n// \"certFileOrContents\" & \"keyFileOrContents\" should be filenames with their extensions\n// or raw contents of the certificate and the private key.\n//\n// Last argument is optional, it accepts one or more\n// `func(*host.Configurator)` that are being executed\n// on that specific host that this function will create to start the server.\n// Via host configurators you can configure the back-end host supervisor,\n// i.e to add events for shutdown, serve or error.\n// An example of this use case can be found at:\n// https://github.com/kataras/iris/blob/main/_examples/http-server/notify-on-shutdown/main.go\n// Look at the `ConfigureHost` too.\n//\n// See `Run` for more.\nfunc TLS(addr string, certFileOrContents, keyFileOrContents string, hostConfigs ...host.Configurator) Runner {\n\treturn func(app *Application) error {\n\t\treturn app.NewHost(&http.Server{Addr: addr}).\n\t\t\tConfigure(hostConfigs...).\n\t\t\tListenAndServeTLS(certFileOrContents, keyFileOrContents)\n\t}\n}\n\n// AutoTLS can be used as an argument for the `Run` method.\n// It will start the Application's secure server using\n// certifications created on the fly by the \"autocert\" golang/x package,\n// so localhost may not be working, use it at \"production\" machine.\n//\n// Addr should have the form of [host]:port, i.e mydomain.com:443.\n//\n// The whitelisted domains are separated by whitespace in \"domain\" argument,\n// i.e \"iris-go.com\", can be different than \"addr\".\n// If empty, all hosts are currently allowed. This is not recommended,\n// as it opens a potential attack where clients connect to a server\n// by IP address and pretend to be asking for an incorrect host name.\n// Manager will attempt to obtain a certificate for that host, incorrectly,\n// eventually reaching the CA's rate limit for certificate requests\n// and making it impossible to obtain actual certificates.\n//\n// For an \"e-mail\" use a non-public one, letsencrypt needs that for your own security.\n//\n// Note: `AutoTLS` will start a new server for you\n// which will redirect all http versions to their https, including subdomains as well.\n//\n// Last argument is optional, it accepts one or more\n// `func(*host.Configurator)` that are being executed\n// on that specific host that this function will create to start the server.\n// Via host configurators you can configure the back-end host supervisor,\n// i.e to add events for shutdown, serve or error.\n// An example of this use case can be found at:\n// https://github.com/kataras/iris/blob/main/_examples/http-server/notify-on-shutdown/main.go\n// Look at the `ConfigureHost` too.\n//\n// Usage:\n// app.Run(iris.AutoTLS(\"iris-go.com:443\", \"iris-go.com www.iris-go.com\", \"mail@example.com\"))\n//\n// See `Run` and `core/host/Supervisor#ListenAndServeAutoTLS` for more.\nfunc AutoTLS(\n\taddr string,\n\tdomain string, email string,\n\thostConfigs ...host.Configurator,\n) Runner {\n\treturn func(app *Application) error {\n\t\treturn app.NewHost(&http.Server{Addr: addr}).\n\t\t\tConfigure(hostConfigs...).\n\t\t\tListenAndServeAutoTLS(domain, email, \"letscache\")\n\t}\n}\n\n// Raw can be used as an argument for the `Run` method.\n// It accepts any (listen) function that returns an error,\n// this function should be block and return an error\n// only when the server exited or a fatal error caused.\n//\n// With this option you're not limited to the servers\n// that iris can run by-default.\n//\n// See `Run` for more.\nfunc Raw(f func() error) Runner {\n\treturn func(app *Application) error {\n\t\tapp.logger.Debugf(\"HTTP Server will start from unknown, external function\")\n\t\treturn f()\n\t}\n}\n\nvar (\n\t// ErrServerClosed is logged by the standard net/http server when the server is terminated.\n\t// Ignore it by passing this error to the `iris.WithoutServerError` configurator\n\t// on `Application.Run/Listen` method.\n\t//\n\t// An alias of the `http#ErrServerClosed`.\n\tErrServerClosed = http.ErrServerClosed\n\n\t// ErrURLQuerySemicolon is logged by the standard net/http server when\n\t// the request contains a semicolon (;) wihch, after go1.17 it's not used as a key-value separator character.\n\t//\n\t// Ignore it by passing this error to the `iris.WithoutServerError` configurator\n\t// on `Application.Run/Listen` method.\n\t//\n\t// An alias of the `http#ErrServerClosed`.\n\tErrURLQuerySemicolon = errors.New(\"http: URL query contains semicolon, which is no longer a supported separator; parts of the query may be stripped when parsed; see golang.org/issue/25192\")\n)\n\n// Listen builds the application and starts the server\n// on the TCP network address \"host:port\" which\n// handles requests on incoming connections.\n//\n// Listen always returns a non-nil error except\n// when NonBlocking option is being passed, so the error goes to the Wait method.\n// Ignore specific errors by using an `iris.WithoutServerError(iris.ErrServerClosed)`\n// as a second input argument.\n//\n// Listen is a shortcut of `app.Run(iris.Addr(hostPort, withOrWithout...))`.\n// See `Run` for details.\nfunc (app *Application) Listen(hostPort string, withOrWithout ...Configurator) error {\n\treturn app.Run(Addr(hostPort), withOrWithout...)\n}\n\n// Run builds the framework and starts the desired `Runner` with or without configuration edits.\n//\n// Run should be called only once per Application instance, it blocks like http.Server.\n//\n// If more than one server needed to run on the same iris instance\n// then create a new host and run it manually by `go NewHost(*http.Server).Serve/ListenAndServe` etc...\n// or use an already created host:\n// h := NewHost(*http.Server)\n// Run(Raw(h.ListenAndServe), WithCharset(\"utf-8\"), WithRemoteAddrHeader(\"CF-Connecting-IP\"))\n//\n// The Application can go online with any type of server or iris's host with the help of\n// the following runners:\n// `Listener`, `Server`, `Addr`, `TLS`, `AutoTLS` and `Raw`.\nfunc (app *Application) Run(serve Runner, withOrWithout ...Configurator) error {\n\tapp.Configure(withOrWithout...)\n\n\tif err := app.Build(); err != nil {\n\t\tapp.logger.Error(err)\n\t\treturn err\n\t}\n\n\tapp.ConfigureHost(func(host *Supervisor) {\n\t\thost.SocketSharding = app.config.SocketSharding\n\t\thost.KeepAlive = app.config.KeepAlive\n\t})\n\n\tapp.tryStartTunneling()\n\n\tif len(app.Hosts) > 0 {\n\t\tapp.logger.Debugf(\"Application: running using %d host(s)\", len(app.Hosts)+1 /* +1 the current */)\n\t}\n\n\tif app.config.NonBlocking {\n\t\tgo func() {\n\t\t\terr := app.serve(serve)\n\t\t\tif err != nil {\n\t\t\t\tapp.setRunError(err)\n\t\t\t}\n\t\t}()\n\n\t\treturn nil\n\t}\n\n\t// this will block until an error(unless supervisor's DeferFlow called from a Task)\n\t// or NonBlocking was passed (see above).\n\treturn app.serve(serve)\n}\n\nfunc (app *Application) serve(serve Runner) error {\n\terr := serve(app)\n\tif err != nil {\n\t\tapp.logger.Error(err)\n\t}\n\treturn err\n}\n\nfunc (app *Application) setRunError(err error) {\n\tapp.runErrorMu.Lock()\n\tapp.runError = err\n\tapp.runErrorMu.Unlock()\n}\n\nfunc (app *Application) getRunError() error {\n\tapp.runErrorMu.RLock()\n\terr := app.runError\n\tapp.runErrorMu.RUnlock()\n\treturn err\n}\n\n// Wait blocks the main goroutine until the server application is up and running.\n// Useful only when `Run` is called with `iris.NonBlocking()` option.\nfunc (app *Application) Wait(ctx stdContext.Context) error {\n\tif !app.config.NonBlocking {\n\t\treturn nil\n\t}\n\n\t// First check if there is an error already from the app.Run.\n\tif err := app.getRunError(); err != nil {\n\t\treturn err\n\t}\n\n\t// Set the base for exponential backoff.\n\tbase := 2.0\n\n\t// Get the maximum number of retries by context or force to 7 retries.\n\tvar maxRetries int\n\t// Get the deadline of the context.\n\tif deadline, ok := ctx.Deadline(); ok {\n\t\tnow := time.Now()\n\t\ttimeout := deadline.Sub(now)\n\n\t\tmaxRetries = getMaxRetries(timeout, base)\n\t} else {\n\t\tmaxRetries = 7 // 256 seconds max.\n\t}\n\n\t// Set the initial retry interval.\n\tretryInterval := time.Second\n\n\treturn app.tryConnect(ctx, maxRetries, retryInterval, base)\n}\n\n// getMaxRetries calculates the maximum number of retries from the retry interval and the base.\nfunc getMaxRetries(retryInterval time.Duration, base float64) int {\n\t// Convert the retry interval to seconds.\n\tseconds := retryInterval.Seconds()\n\t// Apply the inverse formula.\n\tretries := math.Log(seconds)/math.Log(base) - 1\n\treturn int(math.Round(retries))\n}\n\n// tryConnect tries to connect to the server with the given context and retry parameters.\nfunc (app *Application) tryConnect(ctx stdContext.Context, maxRetries int, retryInterval time.Duration, base float64) error {\n\t// Try to connect to the server in a loop.\n\tfor i := 0; i < maxRetries; i++ {\n\t\t// Check the context before each attempt.\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\t// Context is canceled, return the context error.\n\t\t\treturn ctx.Err()\n\t\tdefault:\n\t\t\taddress := app.getVHost() // Get this server's listening address.\n\t\t\tif address == \"\" {\n\t\t\t\ti-- // Note that this may be modified at another go routine of the serve method. So it may be empty at first chance. So retry fetching the VHost every 1 second.\n\t\t\t\ttime.Sleep(time.Second)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// Context is not canceled, proceed with the attempt.\n\t\t\tconn, err := net.Dial(\"tcp\", address)\n\t\t\tif err == nil {\n\t\t\t\t// Connection successful, close the connection and return nil.\n\t\t\t\tconn.Close()\n\t\t\t\treturn nil // exit.\n\t\t\t} // ignore error.\n\n\t\t\t// Connection failed, wait for the retry interval and try again.\n\t\t\ttime.Sleep(retryInterval)\n\t\t\t// After each failed attempt, check the server Run's error again.\n\t\t\tif err := app.getRunError(); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\t// Increase the retry interval by the base raised to the power of the number of attempts.\n\t\t\t/*\n\t\t\t\t0\t2 seconds\n\t\t\t\t1\t4 seconds\n\t\t\t\t2\t8 seconds\n\t\t\t\t3\t~16 seconds\n\t\t\t\t4\t~32 seconds\n\t\t\t\t5\t~64 seconds\n\t\t\t\t6\t~128 seconds\n\t\t\t\t7\t~256 seconds\n\t\t\t\t8\t~512 seconds\n\t\t\t\t...\n\t\t\t*/\n\t\t\tretryInterval = time.Duration(math.Pow(base, float64(i+1))) * time.Second\n\t\t}\n\t}\n\t// All attempts failed, return an error.\n\treturn fmt.Errorf(\"failed to connect to the server after %d retries\", maxRetries)\n}\n\n// https://ngrok.com/docs\nfunc (app *Application) tryStartTunneling() {\n\tif len(app.config.Tunneling.Tunnels) == 0 {\n\t\treturn\n\t}\n\n\tapp.ConfigureHost(func(su *host.Supervisor) {\n\t\tsu.RegisterOnServe(func(h host.TaskHost) {\n\t\t\tpublicAddrs, err := tunnel.Start(app.config.Tunneling)\n\t\t\tif err != nil {\n\t\t\t\tapp.logger.Errorf(\"Host: tunneling error: %v\", err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tpublicAddr := publicAddrs[0]\n\t\t\t// to make subdomains resolution still based on this new remote, public addresses.\n\t\t\tapp.setVHost(publicAddr[strings.Index(publicAddr, \"://\")+3:])\n\n\t\t\tdirectLog := []byte(fmt.Sprintf(\"• Public Address: %s\\n\", publicAddr))\n\t\t\tapp.logger.Printer.Write(directLog) // nolint:errcheck\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "iris_guide.go",
    "content": "package iris\n\nimport (\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/core/router\"\n\n\t\"github.com/kataras/iris/v12/middleware/cors\"\n\t\"github.com/kataras/iris/v12/middleware/modrevision\"\n\t\"github.com/kataras/iris/v12/middleware/recover\"\n\n\t\"github.com/kataras/iris/v12/x/errors\"\n)\n\n// NewGuide returns a simple Iris API builder.\n//\n// Example Code:\n/*\n   package main\n\n   import (\n       \"context\"\n       \"database/sql\"\n       \"time\"\n\n       \"github.com/kataras/iris/v12\"\n       \"github.com/kataras/iris/v12/x/errors\"\n   )\n\n   func main() {\n       iris.NewGuide().\n           AllowOrigin(\"*\").\n           Compression(true).\n           Health(true, \"development\", \"kataras\").\n           Timeout(0, 20*time.Second, 20*time.Second).\n           Middlewares().\n           Services(\n               // openDatabase(),\n               // NewSQLRepoRegistry,\n               NewMemRepoRegistry,\n               NewTestService,\n           ).\n           API(\"/tests\", new(TestAPI)).\n           Listen(\":80\")\n   }\n\n   // Recommendation: move it to /api/tests/api.go file.\n   type TestAPI struct {\n       TestService *TestService\n   }\n\n   func (api *TestAPI) Configure(r iris.Party) {\n       r.Get(\"/\", api.listTests)\n   }\n\n   func (api *TestAPI) listTests(ctx iris.Context) {\n       tests, err := api.TestService.ListTests(ctx)\n       if err != nil {\n           errors.Internal.LogErr(ctx, err)\n           return\n       }\n\n       ctx.JSON(tests)\n   }\n\n   // Recommendation: move it to /pkg/storage/sql/db.go file.\n   type DB struct {\n       *sql.DB\n   }\n\n   func openDatabase( your database configuration... ) *DB {\n       conn, err := sql.Open(...)\n       // handle error.\n       return &DB{DB: conn}\n   }\n\n   func (db *DB) Close() error {\n       return nil\n   }\n\n   // Recommendation: move it to /pkg/repository/registry.go file.\n   type RepoRegistry interface {\n       Tests() TestRepository\n\n       InTransaction(ctx context.Context, fn func(RepoRegistry) error) error\n   }\n\n   // Recommendation: move it to /pkg/repository/registry/memory.go file.\n   type repoRegistryMem struct {\n       tests TestRepository\n   }\n\n   func NewMemRepoRegistry() RepoRegistry {\n       return &repoRegistryMem{\n           tests: NewMemTestRepository(),\n       }\n   }\n\n   func (r *repoRegistryMem) Tests() TestRepository {\n       return r.tests\n   }\n\n   func (r *repoRegistryMem) InTransaction(ctx context.Context, fn func(RepoRegistry) error) error {\n       return nil\n   }\n\n   // Recommendation: move it to /pkg/repository/registry/sql.go file.\n   type repoRegistrySQL struct {\n       db *DB\n\n       tests TestRepository\n   }\n\n   func NewSQLRepoRegistry(db *DB) RepoRegistry {\n       return &repoRegistrySQL{\n           db:    db,\n           tests: NewSQLTestRepository(db),\n       }\n   }\n\n   func (r *repoRegistrySQL) Tests() TestRepository {\n       return r.tests\n   }\n\n   func (r *repoRegistrySQL) InTransaction(ctx context.Context, fn func(RepoRegistry) error) error {\n       return nil\n\n       // your own database transaction code, may look something like that:\n       // tx, err := r.db.BeginTx(ctx, nil)\n       // if err != nil {\n       //     return err\n       // }\n       // defer tx.Rollback()\n       // newRegistry := NewSQLRepoRegistry(tx)\n       // if err := fn(newRegistry);err!=nil{\n       // \treturn err\n       // }\n       // return tx.Commit()\n   }\n\n   // Recommendation: move it to /pkg/test/test.go\n   type Test struct {\n       Name string `db:\"name\"`\n   }\n\n   // Recommendation: move it to /pkg/test/repository.go\n   type TestRepository interface {\n       ListTests(ctx context.Context) ([]Test, error)\n   }\n\n   type testRepositoryMem struct {\n       tests []Test\n   }\n\n   func NewMemTestRepository() TestRepository {\n       list := []Test{\n           {Name: \"test1\"},\n           {Name: \"test2\"},\n           {Name: \"test3\"},\n       }\n\n       return &testRepositoryMem{\n           tests: list,\n       }\n   }\n\n   func (r *testRepositoryMem) ListTests(ctx context.Context) ([]Test, error) {\n       return r.tests, nil\n   }\n\n   type testRepositorySQL struct {\n       db *DB\n   }\n\n   func NewSQLTestRepository(db *DB) TestRepository {\n       return &testRepositorySQL{db: db}\n   }\n\n   func (r *testRepositorySQL) ListTests(ctx context.Context) ([]Test, error) {\n       query := `SELECT * FROM tests ORDER BY created_at;`\n\n       rows, err := r.db.QueryContext(ctx, query)\n       if err != nil {\n           return nil, err\n       }\n       defer rows.Close()\n\n       tests := make([]Test, 0)\n       for rows.Next() {\n           var t Test\n           if err := rows.Scan(&t.Name); err != nil {\n               return nil, err\n           }\n           tests = append(tests, t)\n       }\n\n       if err := rows.Err(); err != nil {\n           return nil, err\n       }\n\n       return tests, nil\n   }\n\n   // Recommendation: move it to /pkg/service/test_service.go file.\n   type TestService struct {\n       repos RepoRegistry\n   }\n\n   func NewTestService(registry RepoRegistry) *TestService {\n       return &TestService{\n           repos: registry,\n       }\n   }\n\n   func (s *TestService) ListTests(ctx context.Context) ([]Test, error) {\n       return s.repos.Tests().ListTests(ctx)\n   }\n*/\nfunc NewGuide() Guide {\n\treturn &step1{}\n}\n\ntype (\n\t// Guide is the simplify API builder.\n\t// It's a step-by-step builder which can be used to build an Iris Application\n\t// with the most common features.\n\tGuide interface {\n\t\t// AllowOrigin defines the CORS allowed domains.\n\t\t// Many can be splitted by comma.\n\t\t// If \"*\" is provided then all origins are accepted (use it for public APIs).\n\t\tAllowOrigin(originLine string) CompressionGuide\n\t}\n\n\t// CompressionGuide is the 2nd step of the Guide.\n\t// Compression (gzip or any other client requested) can be enabled or disabled.\n\tCompressionGuide interface {\n\t\t// Compression enables or disables the gzip (or any other client-preferred) compression algorithm\n\t\t// for response writes.\n\t\tCompression(b bool) HealthGuide\n\t}\n\n\t// HealthGuide is the 3rd step of the Guide.\n\t// Health enables the /health route.\n\tHealthGuide interface {\n\t\t// Health enables the /health route.\n\t\t// If \"env\" and \"developer\" are given, these fields will be populated to the client\n\t\t// through headers and environment on health route.\n\t\tHealth(b bool, env, developer string) TimeoutGuide\n\t}\n\n\t// TimeoutGuide is the 4th step of the Guide.\n\t// Timeout defines the http timeout, server read & write timeouts.\n\tTimeoutGuide interface {\n\t\t// Timeout defines the http timeout, server read & write timeouts.\n\t\tTimeout(requestResponseLife, read time.Duration, write time.Duration) MiddlewareGuide\n\t}\n\n\t// MiddlewareGuide is the 5th step of the Guide.\n\t// It registers one or more handlers to run before everything else (RouterMiddlewares) or\n\t// before registered routes (Middlewares).\n\tMiddlewareGuide interface {\n\t\t// RouterMiddlewares registers one or more handlers to run before everything else.\n\t\tRouterMiddlewares(handlers ...Handler) MiddlewareGuide\n\t\t// Middlewares registers one or more handlers to run before the requested route's handler.\n\t\tMiddlewares(handlers ...Handler) ServiceGuide\n\t}\n\n\t// ServiceGuide is the 6th step of the Guide.\n\t// It is used to register deferrable functions and, most importantly, dependencies that APIs can use.\n\tServiceGuide interface {\n\t\t// Deferrables registers one or more functions to be ran when the server is terminated.\n\t\tDeferrables(closers ...func()) ServiceGuide\n\t\t// Prefix sets the API Party prefix path.\n\t\t// Usage: WithPrefix(\"/api\").\n\t\tWithPrefix(prefixPath string) ServiceGuide\n\t\t// WithoutPrefix disables the API Party prefix path.\n\t\t// Usage: WithoutPrefix(), same as WithPrefix(\"\").\n\t\tWithoutPrefix() ServiceGuide\n\t\t// Services registers one or more dependencies that APIs can use.\n\t\tServices(deps ...any) ApplicationBuilder\n\t}\n\t// ApplicationBuilder is the final step of the Guide.\n\t// It is used to register APIs controllers (PartyConfigurators) and\n\t// its Build, Listen and Run methods configure and build the actual Iris application\n\t// based on the previous steps.\n\tApplicationBuilder interface {\n\t\t// Handle registers a simple route on specific method and (dynamic) path.\n\t\t// It simply calls the Iris Application's Handle method.\n\t\t// Use the \"API\" method instead to keep the app organized.\n\t\tHandle(method, path string, handlers ...Handler) ApplicationBuilder\n\t\t// API registers a router which is responsible to serve the /api group.\n\t\tAPI(pathPrefix string, c ...router.PartyConfigurator) ApplicationBuilder\n\t\t// Build builds the application with the prior configuration and returns the\n\t\t// Iris Application instance for further customizations.\n\t\t//\n\t\t// Use \"Build\" before \"Listen\" or \"Run\" to apply further modifications\n\t\t// to the framework before starting the server. Calling \"Build\" is optional.\n\t\tBuild() *Application // optional call.\n\t\t// Listen calls the Application's Listen method which is a shortcut of Run(iris.Addr(\"hostPort\")).\n\t\t// Use \"Run\" instead if you need to customize the HTTP/2 server itself.\n\t\tListen(hostPort string, configurators ...Configurator) error // Listen OR Run.\n\t\t// Run calls the Application's Run method.\n\t\t// The 1st argument is a Runner (iris.Listener, iris.Server, iris.Addr, iris.TLS, iris.AutoTLS and iris.Raw).\n\t\t// The 2nd argument can be used to add custom configuration right before the server is up and running.\n\t\tRun(runner Runner, configurators ...Configurator) error\n\t}\n)\n\ntype step1 struct {\n\toriginLine string\n}\n\nfunc (s *step1) AllowOrigin(originLine string) CompressionGuide {\n\ts.originLine = originLine\n\treturn &step2{\n\t\tstep1: s,\n\t}\n}\n\ntype step2 struct {\n\tstep1 *step1\n\n\tenableCompression bool\n}\n\nfunc (s *step2) Compression(b bool) HealthGuide {\n\ts.enableCompression = b\n\treturn &step3{\n\t\tstep2: s,\n\t}\n}\n\ntype step3 struct {\n\tstep2 *step2\n\n\tenableHealth   bool\n\tenv, developer string\n}\n\nfunc (s *step3) Health(b bool, env, developer string) TimeoutGuide {\n\ts.enableHealth = b\n\ts.env, s.developer = env, developer\n\treturn &step4{\n\t\tstep3: s,\n\t}\n}\n\ntype step4 struct {\n\tstep3 *step3\n\n\thandlerTimeout time.Duration\n\n\tserverTimeoutRead  time.Duration\n\tserverTimeoutWrite time.Duration\n}\n\nfunc (s *step4) Timeout(requestResponseLife, read, write time.Duration) MiddlewareGuide {\n\ts.handlerTimeout = requestResponseLife\n\n\ts.serverTimeoutRead = read\n\ts.serverTimeoutWrite = write\n\treturn &step5{\n\t\tstep4: s,\n\t}\n}\n\ntype step5 struct {\n\tstep4 *step4\n\n\trouterMiddlewares []Handler // top-level router middlewares, fire even on 404s.\n\tmiddlewares       []Handler\n}\n\nfunc (s *step5) RouterMiddlewares(handlers ...Handler) MiddlewareGuide {\n\ts.routerMiddlewares = append(s.routerMiddlewares, handlers...)\n\treturn s\n}\n\nfunc (s *step5) Middlewares(handlers ...Handler) ServiceGuide {\n\ts.middlewares = handlers\n\n\treturn &step6{\n\t\tstep5:  s,\n\t\tprefix: getDefaultAPIPrefix(),\n\t}\n}\n\ntype step6 struct {\n\tstep5 *step5\n\n\tdeps []any\n\t// derives from \"deps\".\n\tclosers []func()\n\t// derives from \"deps\".\n\tconfiguratorsAsDeps []Configurator\n\n\t// API Party optional prefix path.\n\t// If this is nil then it defaults to \"/api\" in order to keep backwards compatibility,\n\t// otherwise can be set to empty or a custom one.\n\tprefix *string\n}\n\nfunc (s *step6) Deferrables(closers ...func()) ServiceGuide {\n\ts.closers = append(s.closers, closers...)\n\treturn s\n}\n\nvar defaultAPIPrefix = \"/api\"\n\nfunc getDefaultAPIPrefix() *string {\n\treturn &defaultAPIPrefix\n}\n\n// WithPrefix sets the API Party prefix path.\n// Usage: WithPrefix(\"/api\").\nfunc (s *step6) WithPrefix(prefixPath string) ServiceGuide {\n\tif prefixPath == \"\" {\n\t\treturn s.WithoutPrefix()\n\t}\n\n\t*s.prefix = prefixPath\n\treturn s\n}\n\n// WithoutPrefix disables the API Party prefix path, same as WithPrefix(\"\").\n// Usage: WithoutPrefix()\nfunc (s *step6) WithoutPrefix() ServiceGuide {\n\ts.prefix = nil\n\treturn s\n}\n\nfunc (s *step6) getPrefix() string {\n\tif s.prefix == nil { // if WithoutPrefix called then API has no prefix.\n\t\treturn \"\"\n\t}\n\n\tapiPrefix := *s.prefix\n\tif apiPrefix == \"\" { // if not nil but empty (this shouldn't happen) then it defaults to \"/api\".\n\t\tapiPrefix = defaultAPIPrefix\n\t}\n\n\treturn apiPrefix\n}\n\nfunc (s *step6) Services(deps ...any) ApplicationBuilder {\n\ts.deps = deps\n\tfor _, d := range deps {\n\t\tif d == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tswitch cb := d.(type) {\n\t\tcase func():\n\t\t\ts.closers = append(s.closers, cb)\n\t\tcase func() error:\n\t\t\ts.closers = append(s.closers, func() { cb() })\n\t\tcase interface{ Close() }:\n\t\t\ts.closers = append(s.closers, cb.Close)\n\t\tcase interface{ Close() error }:\n\t\t\ts.closers = append(s.closers, func() {\n\t\t\t\tcb.Close()\n\t\t\t})\n\t\tcase Configurator:\n\t\t\ts.configuratorsAsDeps = append(s.configuratorsAsDeps, cb)\n\t\t}\n\t}\n\n\treturn &step7{\n\t\tstep6: s,\n\t}\n}\n\ntype step7 struct {\n\tstep6 *step6\n\n\tapp *Application\n\n\tm        map[string][]router.PartyConfigurator\n\thandlers []step7SimpleRoute\n}\n\ntype step7SimpleRoute struct {\n\tmethod, path string\n\thandlers     []Handler\n}\n\nfunc (s *step7) Handle(method, path string, handlers ...Handler) ApplicationBuilder {\n\ts.handlers = append(s.handlers, step7SimpleRoute{method: method, path: path, handlers: handlers})\n\treturn s\n}\n\nfunc (s *step7) API(prefix string, c ...router.PartyConfigurator) ApplicationBuilder {\n\tif s.m == nil {\n\t\ts.m = make(map[string][]router.PartyConfigurator)\n\t}\n\n\ts.m[prefix] = append(s.m[prefix], c...)\n\treturn s\n}\n\nfunc (s *step7) Build() *Application {\n\tif s.app != nil {\n\t\treturn s.app\n\t}\n\n\tapp := New()\n\tapp.SetContextErrorHandler(errors.DefaultContextErrorHandler)\n\tapp.Macros().SetErrorHandler(errors.DefaultPathParameterTypeErrorHandler)\n\n\trouteFilters := s.step6.step5.routerMiddlewares\n\tif !context.HandlerExists(routeFilters, errors.RecoveryHandler) {\n\t\t// If not errors.RecoveryHandler registered, then use the default one.\n\t\tapp.UseRouter(recover.New())\n\t}\n\n\tapp.UseRouter(routeFilters...)\n\tapp.UseRouter(func(ctx Context) {\n\t\tctx.Header(\"Server\", \"Iris\")\n\t\tif dev := s.step6.step5.step4.step3.developer; dev != \"\" {\n\t\t\tctx.Header(\"X-Developer\", dev)\n\t\t}\n\n\t\tctx.Next()\n\t})\n\n\tif allowOrigin := s.step6.step5.step4.step3.step2.step1.originLine; strings.TrimSpace(allowOrigin) != \"\" && allowOrigin != \"none\" {\n\t\tcorsMiddleware := cors.New().HandleErrorFunc(errors.FailedPrecondition.Err).AllowOrigin(allowOrigin).Handler()\n\t\tapp.UseRouter(corsMiddleware)\n\t}\n\n\tif s.step6.step5.step4.step3.step2.enableCompression {\n\t\tapp.Use(Compression)\n\t}\n\n\tfor _, middleware := range s.step6.step5.middlewares {\n\t\tif middleware == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tapp.Use(middleware)\n\t}\n\n\tif configAsDeps := s.step6.configuratorsAsDeps; len(configAsDeps) > 0 {\n\t\tapp.Configure(configAsDeps...)\n\t}\n\n\tif s.step6.step5.step4.step3.enableHealth {\n\t\tapp.Get(\"/health\", modrevision.New(modrevision.Options{\n\t\t\tServerName: \"Iris Server\",\n\t\t\tEnv:        s.step6.step5.step4.step3.env,\n\t\t\tDeveloper:  s.step6.step5.step4.step3.developer,\n\t\t}))\n\t}\n\n\tif deps := s.step6.deps; len(deps) > 0 {\n\t\tapp.EnsureStaticBindings().RegisterDependency(deps...)\n\t}\n\n\tapiPrefix := s.step6.getPrefix()\n\tfor prefix, c := range s.m {\n\t\tapp.PartyConfigure(apiPrefix+prefix, c...)\n\t}\n\n\tfor _, route := range s.handlers {\n\t\tapp.Handle(route.method, route.path, route.handlers...)\n\t}\n\n\tif readTimeout := s.step6.step5.step4.serverTimeoutRead; readTimeout > 0 {\n\t\tapp.ConfigureHost(func(su *Supervisor) {\n\t\t\tsu.Server.ReadTimeout = readTimeout\n\t\t\tsu.Server.IdleTimeout = readTimeout\n\t\t\tif v, recommended := readTimeout/4, 5*time.Second; v > recommended {\n\t\t\t\tsu.Server.ReadHeaderTimeout = v\n\t\t\t} else {\n\t\t\t\tsu.Server.ReadHeaderTimeout = recommended\n\t\t\t}\n\t\t})\n\t}\n\n\tif writeTimeout := s.step6.step5.step4.serverTimeoutWrite; writeTimeout > 0 {\n\t\tapp.ConfigureHost(func(su *Supervisor) {\n\t\t\tsu.Server.WriteTimeout = writeTimeout\n\t\t})\n\t}\n\n\tvar defaultConfigurators = []Configurator{\n\t\tWithoutServerError(ErrServerClosed, ErrURLQuerySemicolon),\n\t\tWithOptimizations,\n\t\tWithRemoteAddrHeader(\n\t\t\t\"X-Real-Ip\",\n\t\t\t\"X-Forwarded-For\",\n\t\t\t\"CF-Connecting-IP\",\n\t\t\t\"True-Client-Ip\",\n\t\t\t\"X-Appengine-Remote-Addr\",\n\t\t),\n\t\tWithTimeout(s.step6.step5.step4.handlerTimeout),\n\t}\n\tapp.Configure(defaultConfigurators...)\n\n\ts.app = app\n\treturn app\n}\n\nfunc (s *step7) Listen(hostPort string, configurators ...Configurator) error {\n\treturn s.Run(Addr(hostPort), configurators...)\n}\n\nfunc (s *step7) Run(runner Runner, configurators ...Configurator) error {\n\tapp := s.Build()\n\n\tdefer func() {\n\t\t// they will be called on interrupt signals too,\n\t\t// because Iris has a builtin mechanism to call server's shutdown on interrupt.\n\t\tfor _, cb := range s.step6.closers {\n\t\t\tif cb == nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tcb()\n\t\t}\n\t}()\n\n\treturn app.Run(runner, configurators...)\n}\n"
  },
  {
    "path": "macro/handler/handler.go",
    "content": "// Package handler is the highest level module of the macro package which makes use the rest of the macro package,\n// it is mainly used, internally, by the router package.\npackage handler\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/core/memstore\"\n\t\"github.com/kataras/iris/v12/macro\"\n)\n\n// ParamErrorHandler is a special type of Iris handler which receives\n// any error produced by a path type parameter evaluator and let developers\n// customize the output instead of the\n// provided error code 404 or anyother status code given on the `else` literal.\n//\n// Note that the builtin macros return error too, but they're handled\n// by the `else` literal (error code). To change this behavior\n// and send a custom error response you have to register it:\n//\n//\tapp.Macros().Get(\"uuid\").HandleError(func(ctx iris.Context, paramIndex int, err error)).\n//\n// You can also set custom macros by `app.Macros().Register`.\n//\n// See macro.HandleError to set it.\ntype ParamErrorHandler = func(*context.Context, int, error) // alias.\n\n// CanMakeHandler reports whether a macro template needs a special macro's evaluator handler to be validated\n// before procceed to the next handler(s).\n// If the template does not contain any dynamic attributes and a special handler is NOT required\n// then it returns false.\nfunc CanMakeHandler(tmpl macro.Template) (needsMacroHandler bool) {\n\tif len(tmpl.Params) == 0 {\n\t\treturn\n\t}\n\n\t// check if we have params like: {name:string} or {name} or {anything:path} without else keyword or any functions used inside these params.\n\t// 1. if we don't have, then we don't need to add a handler before the main route's handler (as I said, no performance if macro is not really used)\n\t// 2. if we don't have any named params then we don't need a handler too.\n\tfor i := range tmpl.Params {\n\t\tp := tmpl.Params[i]\n\t\tif p.CanEval() {\n\t\t\t// if at least one needs it, then create the handler.\n\t\t\tneedsMacroHandler = true\n\n\t\t\tif p.HandleError != nil {\n\t\t\t\t// Check for its type.\n\t\t\t\tif _, ok := p.HandleError.(ParamErrorHandler); !ok {\n\t\t\t\t\tpanic(fmt.Sprintf(\"HandleError input argument must be a type of func(iris.Context, int, error) but got: %T\", p.HandleError))\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn\n}\n\n// MakeHandler creates and returns a handler from a macro template, the handler evaluates each of the parameters if necessary at all.\n// If the template does not contain any dynamic attributes and a special handler is NOT required\n// then it returns a nil handler.\nfunc MakeHandler(tmpl macro.Template) context.Handler {\n\tfilter := MakeFilter(tmpl)\n\n\treturn func(ctx *context.Context) {\n\t\tif !filter(ctx) {\n\t\t\tif ctx.GetCurrentRoute().StatusErrorCode() == ctx.GetStatusCode() {\n\t\t\t\tctx.Next()\n\t\t\t} else {\n\t\t\t\tctx.StopExecution()\n\t\t\t}\n\n\t\t\treturn\n\t\t}\n\n\t\t// if all passed or the next is the registered error handler to handle this status code,\n\t\t// just continue.\n\t\tctx.Next()\n\t}\n}\n\n// MakeFilter returns a Filter which reports whether a specific macro template\n// and its parameters pass the serve-time validation.\nfunc MakeFilter(tmpl macro.Template) context.Filter {\n\tif !CanMakeHandler(tmpl) {\n\t\treturn nil\n\t}\n\n\treturn func(ctx *context.Context) bool {\n\t\tfor i := range tmpl.Params {\n\t\t\tp := tmpl.Params[i]\n\t\t\tif !p.CanEval() {\n\t\t\t\tcontinue // allow.\n\t\t\t}\n\n\t\t\t// 07-29-2019\n\t\t\t// changed to retrieve by param index in order to support\n\t\t\t// different parameter names for routes with\n\t\t\t// different param types (and probably different param names i.e {name:string}, {id:uint64})\n\t\t\t// in the exact same path pattern.\n\t\t\t//\n\t\t\t// Same parameter names are not allowed, different param types in the same path\n\t\t\t// should have different name e.g. {name} {id:uint64};\n\t\t\t// something like {name} and {name:uint64}\n\t\t\t// is bad API design and we do NOT allow it by-design.\n\t\t\tentry, found := ctx.Params().Store.GetEntryAt(p.Index)\n\t\t\tif !found {\n\t\t\t\t// should never happen.\n\t\t\t\tctx.StatusCode(p.ErrCode) // status code can change from an error handler, set it here.\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\tvalue, passed := p.Eval(entry.String())\n\t\t\tif !passed {\n\t\t\t\tctx.StatusCode(p.ErrCode) // status code can change from an error handler, set it here.\n\t\t\t\tif value != nil && p.HandleError != nil {\n\t\t\t\t\t// The \"value\" is an error here, always (see template.Eval).\n\t\t\t\t\t// This is always a type of ParamErrorHandler at this state (see CanMakeHandler).\n\t\t\t\t\tp.HandleError.(ParamErrorHandler)(ctx, p.Index, value.(error))\n\t\t\t\t}\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\t// Fixes binding different path parameters names,\n\t\t\t//\n\t\t\t// app.Get(\"/{fullname:string}\", strHandler)\n\t\t\t// app.Get(\"/{id:int}\", idHandler)\n\t\t\t//\n\t\t\t// before that user didn't see anything\n\t\t\t// but under the hoods the set-ed value was a type of string instead of type of int,\n\t\t\t// because store contained both \"fullname\" (which set-ed by the router itself on its string representation)\n\t\t\t// and \"id\" by the param evaluator (see core/router/handler.go and bindMultiParamTypesHandler->MakeFilter)\n\t\t\t// and the MVC get by index (e.g. 0) therefore\n\t\t\t// it got the \"fullname\" of type string instead of \"id\" int if /{int} requested.\n\t\t\t// which is critical for faster type assertion in the upcoming, new iris dependency injection (20 Feb 2020).\n\t\t\tctx.Params().Store[p.Index] = memstore.Entry{\n\t\t\t\tKey:      p.Name,\n\t\t\t\tValueRaw: value,\n\t\t\t}\n\n\t\t\t// for i, v := range ctx.Params().Store {\n\t\t\t// \tfmt.Printf(\"[%d:%s] macro/handler/handler.go: param passed: %s(%v of type: %T)\\n\", i, v.Key,\n\t\t\t// \t\tp.Src, v.ValueRaw, v.ValueRaw)\n\t\t\t// }\n\t\t}\n\n\t\treturn true\n\t}\n}\n"
  },
  {
    "path": "macro/handler/handler_test.go",
    "content": "package handler\n\nimport (\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/macro\"\n)\n\nfunc TestCanMakeHandler(t *testing.T) {\n\ttests := []struct {\n\t\tsrc          string\n\t\tneedsHandler bool\n\t}{\n\t\t{\"/static/static\", false},\n\t\t{\"/{myparam}\", false},\n\t\t{\"/{myparam min(1)}\", true},\n\t\t{\"/{myparam else 500}\", true},\n\t\t{\"/{myparam else 404}\", false},\n\t\t{\"/{myparam:string}/static\", false},\n\t\t{\"/{myparam:int}\", true},\n\t\t{\"/static/{myparam:int}/static\", true},\n\t\t{\"/{myparam:path}\", false},\n\t\t{\"/{myparam:path min(1) else 404}\", true},\n\t}\n\n\tavailableMacros := *macro.Defaults\n\tfor i, tt := range tests {\n\t\ttmpl, err := macro.Parse(tt.src, availableMacros)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"[%d] '%s' failed to be parsed: %v\", i, tt.src, err)\n\t\t}\n\n\t\tif got := CanMakeHandler(tmpl); got != tt.needsHandler {\n\t\t\tif tt.needsHandler {\n\t\t\t\tt.Fatalf(\"[%d] '%s' expected to be able to generate an evaluator handler instead of a nil one\", i, tt.src)\n\t\t\t} else {\n\t\t\t\tt.Fatalf(\"[%d] '%s' should not need an evaluator handler\", i, tt.src)\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "macro/interpreter/ast/ast.go",
    "content": "package ast\n\ntype (\n\t// ParamType holds the necessary information about a parameter type for the parser to lookup for.\n\tParamType interface {\n\t\t// The name of the parameter type.\n\t\t// Indent should contain the characters for the parser.\n\t\tIndent() string\n\t}\n\n\t// MasterParamType if implemented and its `Master()` returns true then empty type param will be translated to this param type.\n\t// Also its functions will be available to the rest of the macro param type's funcs.\n\t//\n\t// Only one Master is allowed.\n\tMasterParamType interface {\n\t\tParamType\n\t\tMaster() bool\n\t}\n\n\t// TrailingParamType if implemented and its `Trailing()` returns true\n\t// then it should be declared at the end of a route path and can accept any trailing path segment as one parameter.\n\tTrailingParamType interface {\n\t\tParamType\n\t\tTrailing() bool\n\t}\n\n\t// AliasParamType if implemeneted nad its `Alias()` returns a non-empty string\n\t// then the param type can be written with that string literal too.\n\tAliasParamType interface {\n\t\tParamType\n\t\tAlias() string\n\t}\n)\n\n// IsMaster returns true if the \"pt\" param type is a master one.\nfunc IsMaster(pt ParamType) bool {\n\tp, ok := pt.(MasterParamType)\n\treturn ok && p.Master()\n}\n\n// IsTrailing returns true if the \"pt\" param type is a marked as trailing,\n// which should accept more than one path segment when in the end.\nfunc IsTrailing(pt ParamType) bool {\n\tp, ok := pt.(TrailingParamType)\n\treturn ok && p.Trailing()\n}\n\n// HasAlias returns any alias of the \"pt\" param type.\n// If alias is empty or not found then it returns false as its second output argument.\nfunc HasAlias(pt ParamType) (string, bool) {\n\tif p, ok := pt.(AliasParamType); ok {\n\t\talias := p.Alias()\n\t\treturn alias, len(alias) > 0\n\t}\n\n\treturn \"\", false\n}\n\n// GetMasterParamType accepts a list of ParamType and returns its master.\n// If no `Master` specified:\n// and len(paramTypes) > 0 then it will return the first one,\n// otherwise it returns nil.\nfunc GetMasterParamType(paramTypes ...ParamType) ParamType {\n\tfor _, pt := range paramTypes {\n\t\tif IsMaster(pt) {\n\t\t\treturn pt\n\t\t}\n\t}\n\n\tif len(paramTypes) > 0 {\n\t\treturn paramTypes[0]\n\t}\n\n\treturn nil\n}\n\n// LookupParamType accepts the string\n// representation of a parameter type.\n// Example:\n// \"string\"\n// \"number\" or \"int\"\n// \"long\" or \"int64\"\n// \"uint8\"\n// \"uint64\"\n// \"boolean\" or \"bool\"\n// \"alphabetical\"\n// \"file\"\n// \"path\"\nfunc LookupParamType(indentOrAlias string, paramTypes ...ParamType) (ParamType, bool) {\n\tfor _, pt := range paramTypes {\n\t\tif pt.Indent() == indentOrAlias {\n\t\t\treturn pt, true\n\t\t}\n\n\t\tif alias, has := HasAlias(pt); has {\n\t\t\tif alias == indentOrAlias {\n\t\t\t\treturn pt, true\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil, false\n}\n\n// ParamStatement is a struct\n// which holds all the necessary information about a macro parameter.\n// It holds its type (string, int, alphabetical, file, path),\n// its source ({param:type}),\n// its name (\"param\"),\n// its attached functions by the user (min, max...)\n// and the http error code if that parameter\n// failed to be evaluated.\ntype ParamStatement struct {\n\tSrc       string      // the original unparsed source, i.e: {id:int range(1,5) else 404}\n\tName      string      // id\n\tType      ParamType   // int\n\tFuncs     []ParamFunc // range\n\tErrorCode int         // 404\n}\n\n// ParamFunc holds the name of a parameter's function\n// and its arguments (values)\n// A param func is declared with:\n// {param:int range(1,5)},\n// the range is the\n// param function name\n// the 1 and 5 are the two param function arguments\n// range(1,5)\ntype ParamFunc struct {\n\tName string   // range\n\tArgs []string // [\"1\",\"5\"]\n}\n"
  },
  {
    "path": "macro/interpreter/lexer/lexer.go",
    "content": "package lexer\n\nimport (\n\t\"github.com/kataras/iris/v12/macro/interpreter/token\"\n)\n\n// Lexer helps us to read/scan characters of a source and resolve their token types.\ntype Lexer struct {\n\tinput   string\n\tpos     int  // current pos in input, current char\n\treadPos int  // current reading pos in input, after current char\n\tch      byte // current char under examination\n}\n\n// New takes a source, series of chars, and returns\n// a new, ready to read from the first letter, lexer.\nfunc New(src string) *Lexer {\n\tl := &Lexer{\n\t\tinput: src,\n\t}\n\t// step to the first character in order to be ready\n\tl.readChar()\n\treturn l\n}\n\nfunc (l *Lexer) readChar() {\n\tif l.readPos >= len(l.input) {\n\t\tl.ch = 0\n\t} else {\n\t\tl.ch = l.input[l.readPos]\n\t}\n\tl.pos = l.readPos\n\tl.readPos++\n}\n\nconst (\n\t// Begin is the symbol which lexer should scan forward to.\n\tBegin = '{' // token.LBRACE\n\t// End is the symbol which lexer should stop scanning.\n\tEnd = '}' // token.RBRACE\n)\n\nfunc resolveTokenType(ch byte) token.Type {\n\tswitch ch {\n\tcase Begin:\n\t\treturn token.LBRACE\n\tcase End:\n\t\treturn token.RBRACE\n\t// Let's keep it simple, no evaluation for logical operators, we are not making a new programming language, keep it simple makis.\n\t// ||\n\t// case '|':\n\t// \tif l.peekChar() == '|' {\n\t// \t\tch := l.ch\n\t// \t\tl.readChar()\n\t// \t\tt = token.Token{Type: token.OR, Literal: string(ch) + string(l.ch)}\n\t// \t}\n\t// ==\n\tcase ':':\n\t\treturn token.COLON\n\tcase '(':\n\t\treturn token.LPAREN\n\tcase ')':\n\t\treturn token.RPAREN\n\tcase ',':\n\t\treturn token.COMMA\n\t\t// literals\n\tcase 0:\n\t\treturn token.EOF\n\tdefault:\n\t\treturn token.IDENT //\n\t}\n}\n\n// NextToken returns the next token in the series of characters.\n// It can be a single symbol, a token type or a literal.\n// It's able to return an EOF token too.\n//\n// It moves the cursor forward.\nfunc (l *Lexer) NextToken() (t token.Token) {\n\tl.skipWhitespace()\n\ttyp := resolveTokenType(l.ch)\n\tt.Type = typ\n\tswitch typ {\n\tcase token.EOF:\n\t\tt.Literal = \"\"\n\tcase token.IDENT:\n\t\tif isLetter(l.ch) {\n\t\t\t// letters\n\t\t\tlit := l.readIdentifier()\n\t\t\ttyp = token.LookupIdent(lit)\n\t\t\tt = l.newToken(typ, lit)\n\t\t\treturn\n\t\t}\n\t\tif isDigit(l.ch) {\n\t\t\t// numbers\n\t\t\tlit := l.readNumber()\n\t\t\tt = l.newToken(token.INT, lit)\n\t\t\treturn\n\t\t}\n\n\t\tt = l.newTokenRune(token.ILLEGAL, l.ch)\n\tdefault:\n\t\tt = l.newTokenRune(typ, l.ch)\n\t}\n\tl.readChar() // set the pos to the next\n\treturn\n}\n\n// NextDynamicToken doesn't cares about the grammar.\n// It reads numbers or any unknown symbol,\n// it's being used by parser to skip all characters\n// between parameter function's arguments inside parenthesis,\n// in order to allow custom regexp on the end-language too.\n//\n// It moves the cursor forward.\nfunc (l *Lexer) NextDynamicToken() (t token.Token) {\n\t// calculate anything, even spaces.\n\n\t// numbers\n\tlit := l.readNumber()\n\tif lit != \"\" {\n\t\treturn l.newToken(token.INT, lit)\n\t}\n\n\tlit = l.readIdentifierFuncArgument()\n\treturn l.newToken(token.IDENT, lit)\n}\n\n// used to skip any illegal token if inside parenthesis, used to be able to set custom regexp inside a func.\nfunc (l *Lexer) readIdentifierFuncArgument() string {\n\tpos := l.pos\n\tfor resolveTokenType(l.ch) != token.RPAREN && l.ch != 0 {\n\t\tl.readChar()\n\t}\n\n\treturn l.input[pos:l.pos]\n}\n\n// PeekNextTokenType returns only the token type\n// of the next character and it does not move forward the cursor.\n// It's being used by parser to recognise empty functions, i.e `even()`\n// as valid functions with zero input arguments.\nfunc (l *Lexer) PeekNextTokenType() token.Type {\n\tif len(l.input)-1 > l.pos {\n\t\tch := l.input[l.pos]\n\t\treturn resolveTokenType(ch)\n\t}\n\treturn resolveTokenType(0) // EOF\n}\n\nfunc (l *Lexer) newToken(tokenType token.Type, lit string) token.Token {\n\tt := token.Token{\n\t\tType:    tokenType,\n\t\tLiteral: lit,\n\t\tStart:   l.pos,\n\t\tEnd:     l.pos,\n\t}\n\t// remember, l.pos is the last char\n\t// and we want to include both start and end\n\t// in order to be easy to the user to see by just marking the expression\n\tif l.pos > 1 && len(lit) > 1 {\n\t\tt.End = l.pos - 1\n\t\tt.Start = t.End - len(lit) + 1\n\t}\n\n\treturn t\n}\n\nfunc (l *Lexer) newTokenRune(tokenType token.Type, ch byte) token.Token {\n\treturn l.newToken(tokenType, string(ch))\n}\n\nfunc (l *Lexer) skipWhitespace() {\n\tfor l.ch == ' ' || l.ch == '\\t' || l.ch == '\\n' || l.ch == '\\r' {\n\t\tl.readChar()\n\t}\n}\n\nfunc (l *Lexer) readIdentifier() string {\n\tpos := l.pos\n\tfor isLetter(l.ch) || isDigit(l.ch) {\n\t\tl.readChar()\n\t}\n\treturn l.input[pos:l.pos]\n}\n\nfunc isLetter(ch byte) bool {\n\treturn 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_'\n}\n\nfunc (l *Lexer) readNumber() string {\n\tpos := l.pos\n\tfor isDigit(l.ch) {\n\t\tl.readChar()\n\t}\n\treturn l.input[pos:l.pos]\n}\n\nfunc isDigit(ch byte) bool {\n\treturn '0' <= ch && ch <= '9'\n}\n"
  },
  {
    "path": "macro/interpreter/lexer/lexer_test.go",
    "content": "package lexer\n\nimport (\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/macro/interpreter/token\"\n)\n\nfunc TestNextToken(t *testing.T) {\n\tinput := `{id:int min(1) max(5) else 404}`\n\n\ttests := []struct {\n\t\texpectedType    token.Type\n\t\texpectedLiteral string\n\t}{\n\t\t{token.LBRACE, \"{\"},  // 0\n\t\t{token.IDENT, \"id\"},  // 1\n\t\t{token.COLON, \":\"},   // 2\n\t\t{token.IDENT, \"int\"}, // 3\n\t\t{token.IDENT, \"min\"}, // 4\n\t\t{token.LPAREN, \"(\"},  // 5\n\t\t{token.INT, \"1\"},     // 6\n\t\t{token.RPAREN, \")\"},  // 7\n\t\t{token.IDENT, \"max\"}, // 8\n\t\t{token.LPAREN, \"(\"},  // 9\n\t\t{token.INT, \"5\"},     // 10\n\t\t{token.RPAREN, \")\"},  // 11\n\t\t{token.ELSE, \"else\"}, // 12\n\t\t{token.INT, \"404\"},   // 13\n\t\t{token.RBRACE, \"}\"},  // 14\n\t}\n\n\tl := New(input)\n\n\tfor i, tt := range tests {\n\t\ttok := l.NextToken()\n\n\t\tif tok.Type != tt.expectedType {\n\t\t\tt.Fatalf(\"tests[%d] - tokentype wrong. expected=%q, got=%q\",\n\t\t\t\ti, tt.expectedType, tok.Type)\n\t\t}\n\n\t\tif tok.Literal != tt.expectedLiteral {\n\t\t\tt.Fatalf(\"tests[%d] - literal wrong. expected=%q, got=%q\",\n\t\t\t\ti, tt.expectedLiteral, tok.Literal)\n\t\t}\n\n\t}\n}\n\n// EMEINA STO:\n// 30/232 selida apto making a interpeter in Go.\n// den ekana to skipWhitespaces giati skeftomai\n// an borei na to xreiastw 9a dw aurio.\n"
  },
  {
    "path": "macro/interpreter/parser/parser.go",
    "content": "package parser\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/kataras/iris/v12/macro/interpreter/ast\"\n\t\"github.com/kataras/iris/v12/macro/interpreter/lexer\"\n\t\"github.com/kataras/iris/v12/macro/interpreter/token\"\n)\n\n// Parse takes a route \"fullpath\"\n// and returns its param statements\n// or an error if failed.\nfunc Parse(fullpath string, paramTypes []ast.ParamType) ([]*ast.ParamStatement, error) {\n\tif len(paramTypes) == 0 {\n\t\treturn nil, fmt.Errorf(\"empty parameter types\")\n\t}\n\n\tpathParts := strings.Split(fullpath, \"/\")\n\tp := new(ParamParser)\n\tstatements := make([]*ast.ParamStatement, 0)\n\tfor i, s := range pathParts {\n\t\tif s == \"\" { // if starts with /\n\t\t\tcontinue\n\t\t}\n\n\t\t// if it's not a named path parameter of the new syntax then continue to the next\n\t\t// if s[0] != lexer.Begin || s[len(s)-1] != lexer.End {\n\t\t// \tcontinue\n\t\t// }\n\n\t\t// Modified to show an error on a certain invalid action.\n\t\tif s[0] != lexer.Begin {\n\t\t\tcontinue\n\t\t}\n\n\t\tif s[len(s)-1] != lexer.End {\n\t\t\tif idx := strings.LastIndexByte(s, lexer.End); idx > 2 && idx < len(s)-1 /* at least {x}*/ {\n\t\t\t\t// Do NOT allow something more than a dynamic path parameter in the same path segment,\n\t\t\t\t// e.g. /{param}-other-static-part/. See #2024.\n\t\t\t\t// this allows it but NO (see trie insert): s = s[0 : idx+1]\n\t\t\t\treturn nil, fmt.Errorf(\"%s: invalid path part: dynamic path parameter and other parameters or static parts are not allowed in the same exact request path part, use the {regexp} function alone instead\", s)\n\t\t\t} else {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\tp.Reset(s)\n\t\tstmt, err := p.Parse(paramTypes)\n\t\tif err != nil {\n\t\t\t// exit on first error\n\t\t\treturn nil, err\n\t\t}\n\t\t// if we have param type path but it's not the last path part\n\t\tif ast.IsTrailing(stmt.Type) && i < len(pathParts)-1 {\n\t\t\treturn nil, fmt.Errorf(\"%s: parameter type \\\"%s\\\" should be registered to the very end of a path once\", s, stmt.Type.Indent())\n\t\t}\n\n\t\tstatements = append(statements, stmt)\n\t}\n\n\treturn statements, nil\n}\n\n// ParamParser is the parser\n// which is being used by the Parse function\n// to parse path segments one by one\n// and return their parsed parameter statements (param name, param type its functions and the inline route's functions).\ntype ParamParser struct {\n\tsrc    string\n\terrors []string\n}\n\n// NewParamParser receives a \"src\" of a single parameter\n// and returns a new ParamParser, ready to Parse.\nfunc NewParamParser(src string) *ParamParser {\n\tp := new(ParamParser)\n\tp.Reset(src)\n\treturn p\n}\n\n// Reset resets this ParamParser,\n// reset the errors and set the source to the input \"src\".\nfunc (p *ParamParser) Reset(src string) {\n\tp.src = src\n\tp.errors = []string{}\n}\n\nfunc (p *ParamParser) appendErr(format string, a ...any) {\n\tp.errors = append(p.errors, fmt.Sprintf(format, a...))\n}\n\nconst (\n\t// DefaultParamErrorCode is the default http error code, 404 not found,\n\t// per-parameter. An error code can be set via\n\t// the \"else\" keyword inside a route's path.\n\tDefaultParamErrorCode = 404\n)\n\nfunc (p ParamParser) Error() error {\n\tif len(p.errors) > 0 {\n\t\treturn errors.New(strings.Join(p.errors, \"\\n\"))\n\t}\n\treturn nil\n}\n\n// Parse parses the p.src based on the given param types and returns its param statement\n// and an error on failure.\nfunc (p *ParamParser) Parse(paramTypes []ast.ParamType) (*ast.ParamStatement, error) {\n\tl := lexer.New(p.src)\n\n\tstmt := &ast.ParamStatement{\n\t\tErrorCode: DefaultParamErrorCode,\n\t\tType:      ast.GetMasterParamType(paramTypes...),\n\t\tSrc:       p.src,\n\t}\n\n\tlastParamFunc := ast.ParamFunc{}\n\n\tfor {\n\t\tt := l.NextToken()\n\t\tif t.Type == token.EOF {\n\t\t\tif stmt.Name == \"\" {\n\t\t\t\tp.appendErr(\"[1:] parameter name is missing\")\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\n\t\tswitch t.Type {\n\t\tcase token.LBRACE:\n\t\t\t// can accept only letter or number only.\n\t\t\tnextTok := l.NextToken()\n\t\t\tstmt.Name = nextTok.Literal\n\t\tcase token.COLON:\n\t\t\t// type can accept both letters and numbers but not symbols ofc.\n\t\t\tnextTok := l.NextToken()\n\t\t\tparamType, found := ast.LookupParamType(nextTok.Literal, paramTypes...)\n\n\t\t\tif !found {\n\t\t\t\tp.appendErr(\"[%d:%d] unexpected parameter type: %s\", t.Start, t.End, nextTok.Literal)\n\t\t\t}\n\t\t\tstmt.Type = paramType\n\t\t\t// param func\n\t\tcase token.IDENT:\n\t\t\tlastParamFunc.Name = t.Literal\n\t\tcase token.LPAREN:\n\t\t\t// param function without arguments ()\n\t\t\tif l.PeekNextTokenType() == token.RPAREN {\n\t\t\t\t// do nothing, just continue to the RPAREN\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\targValTok := l.NextDynamicToken() // catch anything from \"(\" and forward, until \")\", because we need to\n\t\t\t// be able to use regex expression as a macro type's func argument too.\n\n\t\t\t// fmt.Printf(\"argValTok: %#v\\n\", argValTok)\n\t\t\t// fmt.Printf(\"argVal: %#v\\n\", argVal)\n\t\t\tlastParamFunc.Args = append(lastParamFunc.Args, argValTok.Literal)\n\n\t\tcase token.COMMA:\n\t\t\targValTok := l.NextToken()\n\t\t\tlastParamFunc.Args = append(lastParamFunc.Args, argValTok.Literal)\n\t\tcase token.RPAREN:\n\t\t\tstmt.Funcs = append(stmt.Funcs, lastParamFunc)\n\t\t\tlastParamFunc = ast.ParamFunc{} // reset\n\t\tcase token.ELSE:\n\t\t\terrCodeTok := l.NextToken()\n\t\t\tif errCodeTok.Type != token.INT {\n\t\t\t\tp.appendErr(\"[%d:%d] expected error code to be an integer but got %s\", t.Start, t.End, errCodeTok.Literal)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\terrCode, err := strconv.Atoi(errCodeTok.Literal)\n\t\t\tif err != nil {\n\t\t\t\t// this is a bug on lexer if throws because we already check for token.INT\n\t\t\t\tp.appendErr(\"[%d:%d] unexpected lexer error while trying to convert error code to an integer, %s\", t.Start, t.End, err.Error())\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tstmt.ErrorCode = errCode\n\t\tcase token.RBRACE:\n\t\t\t// check if } but not {\n\t\t\tif stmt.Name == \"\" {\n\t\t\t\tp.appendErr(\"[%d:%d] illegal token: }, forgot '{' ?\", t.Start, t.End)\n\t\t\t}\n\t\tcase token.ILLEGAL:\n\t\t\tp.appendErr(\"[%d:%d] illegal token: %s\", t.Start, t.End, t.Literal)\n\t\tdefault:\n\t\t\tp.appendErr(\"[%d:%d] unexpected token type: %q with value %s\", t.Start, t.End, t.Type, t.Literal)\n\t\t}\n\t}\n\n\treturn stmt, p.Error()\n}\n"
  },
  {
    "path": "macro/interpreter/parser/parser_test.go",
    "content": "package parser\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/macro/interpreter/ast\"\n)\n\ntype simpleParamType string\n\nfunc (pt simpleParamType) Indent() string { return string(pt) }\n\ntype masterParamType simpleParamType\n\nfunc (pt masterParamType) Indent() string { return string(pt) }\nfunc (pt masterParamType) Master() bool   { return true }\n\ntype wildcardParamType string\n\nfunc (pt wildcardParamType) Indent() string { return string(pt) }\nfunc (pt wildcardParamType) Trailing() bool { return true }\n\ntype aliasedParamType []string\n\nfunc (pt aliasedParamType) Indent() string { return string(pt[0]) }\nfunc (pt aliasedParamType) Alias() string  { return pt[1] }\n\nvar (\n\tparamTypeString       = masterParamType(\"string\")\n\tparamTypeNumber       = aliasedParamType{\"number\", \"int\"}\n\tparamTypeInt64        = aliasedParamType{\"int64\", \"long\"}\n\tparamTypeUint8        = simpleParamType(\"uint8\")\n\tparamTypeUint64       = simpleParamType(\"uint64\")\n\tparamTypeBool         = aliasedParamType{\"bool\", \"boolean\"}\n\tparamTypeAlphabetical = simpleParamType(\"alphabetical\")\n\tparamTypeFile         = simpleParamType(\"file\")\n\tparamTypePath         = wildcardParamType(\"path\")\n)\n\nvar testParamTypes = []ast.ParamType{\n\tparamTypeString,\n\tparamTypeNumber, paramTypeInt64, paramTypeUint8, paramTypeUint64,\n\tparamTypeBool,\n\tparamTypeAlphabetical, paramTypeFile, paramTypePath,\n}\n\nfunc TestParseParamError(t *testing.T) {\n\t// fail\n\tillegalChar := '$'\n\n\tinput := \"{id\" + string(illegalChar) + \"int range(1,5) else 404}\"\n\tp := NewParamParser(input)\n\n\t_, err := p.Parse(testParamTypes)\n\n\tif err == nil {\n\t\tt.Fatalf(\"expecting not empty error on input '%s'\", input)\n\t}\n\n\tillIdx := strings.IndexRune(input, illegalChar)\n\texpectedErr := fmt.Sprintf(\"[%d:%d] illegal token: %s\", illIdx, illIdx, \"$\")\n\tif got := err.Error(); got != expectedErr {\n\t\tt.Fatalf(\"expecting error to be '%s' but got: %s\", expectedErr, got)\n\t}\n\t//\n\n\t// success\n\tinput2 := \"{id:uint64 range(1,5) else 404}\"\n\tp.Reset(input2)\n\t_, err = p.Parse(testParamTypes)\n\n\tif err != nil {\n\t\tt.Fatalf(\"expecting empty error on input '%s', but got: %s\", input2, err.Error())\n\t}\n\t//\n}\n\n// mustLookupParamType same as `ast.LookupParamType` but it panics if \"indent\" does not match with a valid Param Type.\nfunc mustLookupParamType(indent string) ast.ParamType {\n\tpt, found := ast.LookupParamType(indent, testParamTypes...)\n\tif !found {\n\t\tpanic(\"param type '\" + indent + \"' is not part of the provided param types\")\n\t}\n\n\treturn pt\n}\n\nfunc TestParseParam(t *testing.T) {\n\ttests := []struct {\n\t\tvalid             bool\n\t\texpectedStatement ast.ParamStatement\n\t}{\n\t\t{\n\t\t\ttrue,\n\t\t\tast.ParamStatement{\n\t\t\t\tSrc:  \"{id:int min(1) max(5) else 404}\",\n\t\t\t\tName: \"id\",\n\t\t\t\tType: mustLookupParamType(\"number\"),\n\t\t\t\tFuncs: []ast.ParamFunc{\n\t\t\t\t\t{\n\t\t\t\t\t\tName: \"min\",\n\t\t\t\t\t\tArgs: []string{\"1\"},\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tName: \"max\",\n\t\t\t\t\t\tArgs: []string{\"5\"},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tErrorCode: 404,\n\t\t\t},\n\t\t}, // 0\n\n\t\t{\n\t\t\ttrue,\n\t\t\tast.ParamStatement{\n\t\t\t\t// test alias of int.\n\t\t\t\tSrc:  \"{id:number range(1,5)}\",\n\t\t\t\tName: \"id\",\n\t\t\t\tType: mustLookupParamType(\"number\"),\n\t\t\t\tFuncs: []ast.ParamFunc{\n\t\t\t\t\t{\n\t\t\t\t\t\tName: \"range\",\n\t\t\t\t\t\tArgs: []string{\"1\", \"5\"},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tErrorCode: 404,\n\t\t\t},\n\t\t}, // 1\n\t\t{\n\t\t\ttrue,\n\t\t\tast.ParamStatement{\n\t\t\t\tSrc:  \"{file:path contains(.)}\",\n\t\t\t\tName: \"file\",\n\t\t\t\tType: mustLookupParamType(\"path\"),\n\t\t\t\tFuncs: []ast.ParamFunc{\n\t\t\t\t\t{\n\t\t\t\t\t\tName: \"contains\",\n\t\t\t\t\t\tArgs: []string{\".\"},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tErrorCode: 404,\n\t\t\t},\n\t\t}, // 2\n\t\t{\n\t\t\ttrue,\n\t\t\tast.ParamStatement{\n\t\t\t\tSrc:       \"{username:alphabetical}\",\n\t\t\t\tName:      \"username\",\n\t\t\t\tType:      mustLookupParamType(\"alphabetical\"),\n\t\t\t\tErrorCode: 404,\n\t\t\t},\n\t\t}, // 3\n\t\t{\n\t\t\ttrue,\n\t\t\tast.ParamStatement{\n\t\t\t\tSrc:       \"{myparam}\",\n\t\t\t\tName:      \"myparam\",\n\t\t\t\tType:      mustLookupParamType(\"string\"),\n\t\t\t\tErrorCode: 404,\n\t\t\t},\n\t\t}, // 4\n\t\t{\n\t\t\tfalse,\n\t\t\tast.ParamStatement{\n\t\t\t\tSrc:       \"{myparam_:thisianunexpected}\",\n\t\t\t\tName:      \"myparam_\",\n\t\t\t\tType:      nil,\n\t\t\t\tErrorCode: 404,\n\t\t\t},\n\t\t}, // 5\n\t\t{\n\t\t\ttrue,\n\t\t\tast.ParamStatement{\n\t\t\t\tSrc:       \"{myparam2}\",\n\t\t\t\tName:      \"myparam2\", // we now allow integers to the parameter names.\n\t\t\t\tType:      ast.GetMasterParamType(testParamTypes...),\n\t\t\t\tErrorCode: 404,\n\t\t\t},\n\t\t}, // 6\n\t\t{\n\t\t\ttrue,\n\t\t\tast.ParamStatement{\n\t\t\t\tSrc:  \"{id:int even()}\", // test param funcs without any arguments (LPAREN peek for RPAREN)\n\t\t\t\tName: \"id\",\n\t\t\t\tType: mustLookupParamType(\"number\"),\n\t\t\t\tFuncs: []ast.ParamFunc{\n\t\t\t\t\t{\n\t\t\t\t\t\tName: \"even\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tErrorCode: 404,\n\t\t\t},\n\t\t}, // 7\n\t\t{\n\t\t\ttrue,\n\t\t\tast.ParamStatement{\n\t\t\t\tSrc:       \"{id:int64 else 404}\",\n\t\t\t\tName:      \"id\",\n\t\t\t\tType:      mustLookupParamType(\"int64\"),\n\t\t\t\tErrorCode: 404,\n\t\t\t},\n\t\t}, // 8\n\t\t{\n\t\t\ttrue,\n\t\t\tast.ParamStatement{\n\t\t\t\tSrc:       \"{id:long else 404}\", // backwards-compatible test.\n\t\t\t\tName:      \"id\",\n\t\t\t\tType:      mustLookupParamType(\"int64\"),\n\t\t\t\tErrorCode: 404,\n\t\t\t},\n\t\t}, // 9\n\t\t{\n\t\t\ttrue,\n\t\t\tast.ParamStatement{\n\t\t\t\tSrc:       \"{id:long else 404}\",\n\t\t\t\tName:      \"id\",\n\t\t\t\tType:      mustLookupParamType(\"int64\"), // backwards-compatible test of LookupParamType.\n\t\t\t\tErrorCode: 404,\n\t\t\t},\n\t\t}, // 10\n\t\t{\n\t\t\ttrue,\n\t\t\tast.ParamStatement{\n\t\t\t\tSrc:       \"{has:bool else 404}\",\n\t\t\t\tName:      \"has\",\n\t\t\t\tType:      mustLookupParamType(\"bool\"),\n\t\t\t\tErrorCode: 404,\n\t\t\t},\n\t\t}, // 11\n\t\t{\n\t\t\ttrue,\n\t\t\tast.ParamStatement{\n\t\t\t\tSrc:       \"{has:boolean else 404}\", // backwards-compatible test.\n\t\t\t\tName:      \"has\",\n\t\t\t\tType:      mustLookupParamType(\"bool\"),\n\t\t\t\tErrorCode: 404,\n\t\t\t},\n\t\t}, // 12\n\n\t}\n\n\tp := new(ParamParser)\n\tfor i, tt := range tests {\n\t\tp.Reset(tt.expectedStatement.Src)\n\t\tresultStmt, err := p.Parse(testParamTypes)\n\n\t\tif tt.valid && err != nil {\n\t\t\tt.Fatalf(\"tests[%d] - error %s\", i, err.Error())\n\t\t} else if !tt.valid && err == nil {\n\t\t\tt.Fatalf(\"tests[%d] - expected to be a failure\", i)\n\t\t}\n\n\t\tif resultStmt != nil { // is valid here\n\t\t\tif !reflect.DeepEqual(tt.expectedStatement, *resultStmt) {\n\t\t\t\tt.Fatalf(\"tests[%d] - wrong statement, expected and result differs. Details:\\n%#v\\n%#v\", i, tt.expectedStatement, *resultStmt)\n\t\t\t}\n\t\t}\n\n\t}\n}\n\nfunc TestParse(t *testing.T) {\n\ttests := []struct {\n\t\tpath               string\n\t\tvalid              bool\n\t\texpectedStatements []ast.ParamStatement\n\t}{\n\t\t{\n\t\t\t\"/api/users/{id:int min(1) max(5) else 404}\", true,\n\t\t\t[]ast.ParamStatement{\n\t\t\t\t{\n\t\t\t\t\tSrc:  \"{id:int min(1) max(5) else 404}\",\n\t\t\t\t\tName: \"id\",\n\t\t\t\t\tType: paramTypeNumber,\n\t\t\t\t\tFuncs: []ast.ParamFunc{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tName: \"min\",\n\t\t\t\t\t\t\tArgs: []string{\"1\"},\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tName: \"max\",\n\t\t\t\t\t\t\tArgs: []string{\"5\"},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tErrorCode: 404,\n\t\t\t\t},\n\t\t\t},\n\t\t}, // 0\n\t\t{\n\t\t\t\"/admin/{id:uint64 range(1,5)}\", true,\n\t\t\t[]ast.ParamStatement{\n\t\t\t\t{\n\t\t\t\t\tSrc:  \"{id:uint64 range(1,5)}\",\n\t\t\t\t\tName: \"id\",\n\t\t\t\t\tType: paramTypeUint64,\n\t\t\t\t\tFuncs: []ast.ParamFunc{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tName: \"range\",\n\t\t\t\t\t\t\tArgs: []string{\"1\", \"5\"},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tErrorCode: 404,\n\t\t\t\t},\n\t\t\t},\n\t\t}, // 1\n\t\t{\n\t\t\t\"/files/{file:path contains(.)}\", true,\n\t\t\t[]ast.ParamStatement{\n\t\t\t\t{\n\t\t\t\t\tSrc:  \"{file:path contains(.)}\",\n\t\t\t\t\tName: \"file\",\n\t\t\t\t\tType: paramTypePath,\n\t\t\t\t\tFuncs: []ast.ParamFunc{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tName: \"contains\",\n\t\t\t\t\t\t\tArgs: []string{\".\"},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tErrorCode: 404,\n\t\t\t\t},\n\t\t\t},\n\t\t}, // 2\n\t\t{\n\t\t\t\"/profile/{username:alphabetical}\", true,\n\t\t\t[]ast.ParamStatement{\n\t\t\t\t{\n\t\t\t\t\tSrc:       \"{username:alphabetical}\",\n\t\t\t\t\tName:      \"username\",\n\t\t\t\t\tType:      paramTypeAlphabetical,\n\t\t\t\t\tErrorCode: 404,\n\t\t\t\t},\n\t\t\t},\n\t\t}, // 3\n\t\t{\n\t\t\t\"/something/here/{myparam}\", true,\n\t\t\t[]ast.ParamStatement{\n\t\t\t\t{\n\t\t\t\t\tSrc:       \"{myparam}\",\n\t\t\t\t\tName:      \"myparam\",\n\t\t\t\t\tType:      paramTypeString,\n\t\t\t\t\tErrorCode: 404,\n\t\t\t\t},\n\t\t\t},\n\t\t}, // 4\n\t\t{\n\t\t\t\"/unexpected/{myparam_:thisianunexpected}\", false,\n\t\t\t[]ast.ParamStatement{\n\t\t\t\t{\n\t\t\t\t\tSrc:       \"{myparam_:thisianunexpected}\",\n\t\t\t\t\tName:      \"myparam_\",\n\t\t\t\t\tType:      nil,\n\t\t\t\t\tErrorCode: 404,\n\t\t\t\t},\n\t\t\t},\n\t\t}, // 5\n\t\t{\n\t\t\t\"/p2/{myparam2}\", true,\n\t\t\t[]ast.ParamStatement{\n\t\t\t\t{\n\t\t\t\t\tSrc:       \"{myparam2}\",\n\t\t\t\t\tName:      \"myparam2\", // we now allow integers to the parameter names.\n\t\t\t\t\tType:      paramTypeString,\n\t\t\t\t\tErrorCode: 404,\n\t\t\t\t},\n\t\t\t},\n\t\t}, // 6\n\t\t{\n\t\t\t\"/assets/{file:path}/invalid\", false, // path should be in the end segment\n\t\t\t[]ast.ParamStatement{\n\t\t\t\t{\n\t\t\t\t\tSrc:       \"{file:path}\",\n\t\t\t\t\tName:      \"file\",\n\t\t\t\t\tType:      paramTypePath,\n\t\t\t\t\tErrorCode: 404,\n\t\t\t\t},\n\t\t\t},\n\t\t}, // 7\n\t}\n\tfor i, tt := range tests {\n\t\tstatements, err := Parse(tt.path, testParamTypes)\n\n\t\tif tt.valid && err != nil {\n\t\t\tt.Fatalf(\"tests[%d] - error %s\", i, err.Error())\n\t\t} else if !tt.valid && err == nil {\n\t\t\tt.Fatalf(\"tests[%d] - expected to be a failure\", i)\n\t\t}\n\t\tfor j := range statements {\n\t\t\tfor l := range tt.expectedStatements {\n\t\t\t\tif !reflect.DeepEqual(tt.expectedStatements[l], *statements[j]) {\n\t\t\t\t\tt.Fatalf(\"tests[%d] - wrong statements, expected and result differs. Details:\\n%#v\\n%#v\", i, tt.expectedStatements[l], *statements[j])\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "macro/interpreter/token/token.go",
    "content": "package token\n\n// Type is a specific type of int which describes the symbols.\ntype Type int\n\n// Token describes the letter(s) or symbol, is a result of the lexer.\ntype Token struct {\n\tType    Type\n\tLiteral string\n\tStart   int // including the first char\n\tEnd     int // including the last char\n}\n\n// /about/{fullname:alphabetical}\n// /profile/{anySpecialName:string}\n// {id:uint64 range(1,5) else 404}\n// /admin/{id:int eq(1) else 402}\n// /file/{filepath:file else 405}\nconst (\n\tEOF = iota // 0\n\tILLEGAL\n\n\t// Identifiers + literals\n\tLBRACE // {\n\tRBRACE // }\n\t//\tPARAM_IDENTIFIER // id\n\tCOLON  // :\n\tLPAREN // (\n\tRPAREN // )\n\t//\tPARAM_FUNC_ARG   // 1\n\tCOMMA\n\tIDENT // string or keyword\n\t// Keywords\n\t// keywords_start\n\tELSE // else\n\t// keywords_end\n\tINT // 42\n)\n\nvar keywords = map[string]Type{\n\t\"else\": ELSE,\n}\n\n// LookupIdent receives a series of chars\n// and tries to resolves the token type.\nfunc LookupIdent(ident string) Type {\n\tif tok, ok := keywords[ident]; ok {\n\t\treturn tok\n\t}\n\treturn IDENT\n}\n"
  },
  {
    "path": "macro/macro.go",
    "content": "package macro\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n\t\"unicode\"\n)\n\ntype (\n\t// ParamEvaluator is the signature for param type evaluator.\n\t// It accepts the param's value as string and returns\n\t// the <T> value (which its type is used for the input argument of the parameter functions, if any)\n\t// and a true value for passed, otherwise nil and false should be returned.\n\tParamEvaluator func(paramValue string) (any, bool)\n)\n\nvar goodEvaluatorFuncs = []reflect.Type{\n\treflect.TypeOf(func(string) (any, bool) { return nil, false }),\n\treflect.TypeOf(ParamEvaluator(func(string) (any, bool) { return nil, false })),\n}\n\nfunc goodParamFunc(typ reflect.Type) bool {\n\tif typ.Kind() == reflect.Func { // it should be a func which returns a func (see below check).\n\t\tif typ.NumOut() == 1 {\n\t\t\ttypOut := typ.Out(0)\n\t\t\tif typOut.Kind() != reflect.Func {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\tif typOut.NumOut() == 2 { // if it's a type of EvaluatorFunc, used for param evaluator.\n\t\t\t\tfor _, fType := range goodEvaluatorFuncs {\n\t\t\t\t\tif typOut == fType {\n\t\t\t\t\t\treturn true\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\tif typOut.NumIn() == 1 && typOut.NumOut() == 1 { // if it's a type of func(paramValue [int,string...]) bool, used for param funcs.\n\t\t\t\treturn typOut.Out(0).Kind() == reflect.Bool\n\t\t\t}\n\t\t}\n\t}\n\n\treturn false\n}\n\n// Regexp accepts a regexp \"expr\" expression\n// and returns its MatchString.\n// The regexp is compiled before return.\n//\n// Returns a not-nil error on regexp compile failure.\nfunc Regexp(expr string) (func(string) bool, error) {\n\tif expr == \"\" {\n\t\treturn nil, fmt.Errorf(\"empty regex expression\")\n\t}\n\n\t// add the last $ if missing (and not wildcard(?))\n\tif i := expr[len(expr)-1]; i != '$' && i != '*' {\n\t\texpr += \"$\"\n\t}\n\n\tr, err := regexp.Compile(expr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn r.MatchString, nil\n}\n\n// MustRegexp same as Regexp\n// but it panics on the \"expr\" parse failure.\nfunc MustRegexp(expr string) func(string) bool {\n\tr, err := Regexp(expr)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn r\n}\n\n// goodParamFuncName reports whether the function name is a valid identifier.\nfunc goodParamFuncName(name string) bool {\n\tif name == \"\" {\n\t\treturn false\n\t}\n\t// valid names are only letters and _\n\tfor _, r := range name {\n\t\tswitch {\n\t\tcase r == '_':\n\t\tcase !unicode.IsLetter(r):\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\n// the convertBuilderFunc return value is generating at boot time.\n// convertFunc converts an interface to a valid full param function.\nfunc convertBuilderFunc(fn any) ParamFuncBuilder {\n\ttypFn := reflect.TypeOf(fn)\n\tif !goodParamFunc(typFn) {\n\t\t// it's not a function which returns a function,\n\t\t// it's not a a func(compileArgs) func(requestDynamicParamValue) bool\n\t\t// but it's a func(requestDynamicParamValue) bool, such as regexp.Compile.MatchString\n\t\tif typFn.NumIn() == 1 && typFn.In(0).Kind() == reflect.String && typFn.NumOut() == 1 && typFn.Out(0).Kind() == reflect.Bool {\n\t\t\tfnV := reflect.ValueOf(fn)\n\t\t\t// let's convert it to a ParamFuncBuilder which its combile route arguments are empty and not used at all.\n\t\t\t// the below return function runs on each route that this param type function is used in order to validate the function,\n\t\t\t// if that param type function is used wrongly it will be panic like the rest,\n\t\t\t// indeed the only check is the len of arguments not > 0, no types of values or conversions,\n\t\t\t// so we return it as soon as possible.\n\t\t\treturn func(args []string) reflect.Value {\n\t\t\t\tif n := len(args); n > 0 {\n\t\t\t\t\tpanic(fmt.Sprintf(\"%T does not allow any input arguments from route but got [len=%d,values=%s]\", fn, n, strings.Join(args, \", \")))\n\t\t\t\t}\n\t\t\t\treturn fnV\n\t\t\t}\n\t\t}\n\n\t\treturn nil\n\t}\n\n\tnumFields := typFn.NumIn()\n\n\tpanicIfErr := func(i int, err error) {\n\t\tif err != nil {\n\t\t\tpanic(fmt.Sprintf(\"on field index: %d: %v\", i, err))\n\t\t}\n\t}\n\n\treturn func(args []string) reflect.Value {\n\t\tif len(args) != numFields {\n\t\t\t// no variadics support, for now.\n\t\t\tpanic(fmt.Sprintf(\"args(len=%d) should be the same len as numFields(%d) for: %s\", len(args), numFields, typFn))\n\t\t}\n\t\tvar argValues []reflect.Value\n\t\tfor i := 0; i < numFields; i++ {\n\t\t\tfield := typFn.In(i)\n\t\t\targ := args[i]\n\n\t\t\t// try to convert the string literal as we get it from the parser.\n\t\t\tvar (\n\t\t\t\tval any\n\t\t\t)\n\n\t\t\t// try to get the value based on the expected type.\n\t\t\tswitch field.Kind() {\n\t\t\tcase reflect.Int:\n\t\t\t\tv, err := strconv.Atoi(arg)\n\t\t\t\tpanicIfErr(i, err)\n\t\t\t\tval = v\n\t\t\tcase reflect.Int8:\n\t\t\t\tv, err := strconv.ParseInt(arg, 10, 8)\n\t\t\t\tpanicIfErr(i, err)\n\t\t\t\tval = int8(v)\n\t\t\tcase reflect.Int16:\n\t\t\t\tv, err := strconv.ParseInt(arg, 10, 16)\n\t\t\t\tpanicIfErr(i, err)\n\t\t\t\tval = int16(v)\n\t\t\tcase reflect.Int32:\n\t\t\t\tv, err := strconv.ParseInt(arg, 10, 32)\n\t\t\t\tpanicIfErr(i, err)\n\t\t\t\tval = int32(v)\n\t\t\tcase reflect.Int64:\n\t\t\t\tv, err := strconv.ParseInt(arg, 10, 64)\n\t\t\t\tpanicIfErr(i, err)\n\t\t\t\tval = v\n\t\t\tcase reflect.Uint:\n\t\t\t\tv, err := strconv.ParseUint(arg, 10, strconv.IntSize)\n\t\t\t\tpanicIfErr(i, err)\n\t\t\t\tval = uint(v)\n\t\t\tcase reflect.Uint8:\n\t\t\t\tv, err := strconv.ParseUint(arg, 10, 8)\n\t\t\t\tpanicIfErr(i, err)\n\t\t\t\tval = uint8(v)\n\t\t\tcase reflect.Uint16:\n\t\t\t\tv, err := strconv.ParseUint(arg, 10, 16)\n\t\t\t\tpanicIfErr(i, err)\n\t\t\t\tval = uint16(v)\n\t\t\tcase reflect.Uint32:\n\t\t\t\tv, err := strconv.ParseUint(arg, 10, 32)\n\t\t\t\tpanicIfErr(i, err)\n\t\t\t\tval = uint32(v)\n\t\t\tcase reflect.Uint64:\n\t\t\t\tv, err := strconv.ParseUint(arg, 10, 64)\n\t\t\t\tpanicIfErr(i, err)\n\t\t\t\tval = v\n\t\t\tcase reflect.Float32:\n\t\t\t\tv, err := strconv.ParseFloat(arg, 32)\n\t\t\t\tpanicIfErr(i, err)\n\t\t\t\tval = float32(v)\n\t\t\tcase reflect.Float64:\n\t\t\t\tv, err := strconv.ParseFloat(arg, 64)\n\t\t\t\tpanicIfErr(i, err)\n\t\t\t\tval = v\n\t\t\tcase reflect.Bool:\n\t\t\t\tv, err := strconv.ParseBool(arg)\n\t\t\t\tpanicIfErr(i, err)\n\t\t\t\tval = v\n\t\t\tcase reflect.Slice:\n\t\t\t\tif len(arg) > 1 {\n\t\t\t\t\tif arg[0] == '[' && arg[len(arg)-1] == ']' {\n\t\t\t\t\t\t// it is a single argument but as slice.\n\t\t\t\t\t\tval = strings.Split(arg[1:len(arg)-1], \",\") // only string slices.\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\tval = arg\n\t\t\t}\n\n\t\t\targValue := reflect.ValueOf(val)\n\t\t\tif expected, got := field.Kind(), argValue.Kind(); expected != got {\n\t\t\t\tpanic(fmt.Sprintf(\"func's input arguments should have the same type: [%d] expected %s but got %s\", i, expected, got))\n\t\t\t}\n\n\t\t\targValues = append(argValues, argValue)\n\t\t}\n\n\t\tevalFn := reflect.ValueOf(fn).Call(argValues)[0]\n\n\t\t// var evaluator EvaluatorFunc\n\t\t// // check for typed and not typed\n\t\t// if _v, ok := evalFn.(EvaluatorFunc); ok {\n\t\t// \tevaluator = _v\n\t\t// } else if _v, ok = evalFn.(func(string) bool); ok {\n\t\t// \tevaluator = _v\n\t\t// }\n\t\t// return func(paramValue any) bool {\n\t\t// \treturn evaluator(paramValue)\n\t\t// }\n\t\treturn evalFn\n\t}\n}\n\ntype (\n\t// Macro represents the parsed macro,\n\t// which holds\n\t// the evaluator (param type's evaluator + param functions evaluators)\n\t// and its param functions.\n\t//\n\t// Any type contains its own macro\n\t// instance, so an String type\n\t// contains its type evaluator\n\t// which is the \"Evaluator\" field\n\t// and it can register param functions\n\t// to that macro which maps to a parameter type.\n\tMacro struct {\n\t\tindent   string\n\t\talias    string\n\t\tmaster   bool\n\t\ttrailing bool\n\n\t\tEvaluator   ParamEvaluator\n\t\thandleError any\n\t\tfuncs       []ParamFunc\n\n\t\tgoType reflect.Type\n\t}\n\n\t// ParamFuncBuilder is a func\n\t// which accepts a param function's arguments (values)\n\t// and returns a function as value, its job\n\t// is to make the macros to be registered\n\t// by user at the most generic possible way.\n\tParamFuncBuilder func([]string) reflect.Value // the func(<T>) bool\n\n\t// ParamFunc represents the parsed\n\t// parameter function, it holds\n\t// the parameter's name\n\t// and the function which will build\n\t// the evaluator func.\n\tParamFunc struct {\n\t\tName string\n\t\tFunc ParamFuncBuilder\n\t}\n)\n\n// NewMacro creates and returns a Macro that can be used as a registry for\n// a new customized parameter type and its functions.\nfunc NewMacro(indent, alias string, valueType any, master, trailing bool, evaluator ParamEvaluator) *Macro {\n\treturn &Macro{\n\t\tindent:   indent,\n\t\talias:    alias,\n\t\tmaster:   master,\n\t\ttrailing: trailing,\n\t\tgoType:   reflect.TypeOf(valueType),\n\n\t\tEvaluator: evaluator,\n\t}\n}\n\n// Indent returns the name of the parameter type.\nfunc (m *Macro) Indent() string {\n\treturn m.indent\n}\n\n// Alias returns the alias of the parameter type, if any.\nfunc (m *Macro) Alias() string {\n\treturn m.alias\n}\n\n// Master returns true if that macro's parameter type is the\n// default one if not :type is followed by a parameter type inside the route path.\nfunc (m *Macro) Master() bool {\n\treturn m.master\n}\n\n// Trailing returns true if that macro's parameter type\n// is wildcard and can accept one or more path segments as one parameter value.\n// A wildcard should be registered in the last path segment only.\nfunc (m *Macro) Trailing() bool {\n\treturn m.trailing\n}\n\n// GoType returns the type of the parameter type's evaluator.\n// string if it's a string evaluator, int if it's an int evaluator etc.\nfunc (m *Macro) GoType() reflect.Type {\n\treturn m.goType\n}\n\n// HandleError registers a handler which will be executed\n// when a parameter evaluator returns false and a non nil value which is a type of `error`.\n// The \"fnHandler\" value MUST BE a type of `func(iris.Context, paramIndex int, err error)`,\n// otherwise the program will receive a panic before server startup.\n// The status code of the ErrCode (`else` literal) is set\n// before the error handler but it can be modified inside the handler itself.\nfunc (m *Macro) HandleError(fnHandler any) *Macro { // See handler.MakeFilter.\n\tm.handleError = fnHandler\n\treturn m\n}\n\n// func (m *Macro) SetParamResolver(fn func(memstore.Entry) any) *Macro {\n// \tm.ParamResolver = fn\n// \treturn m\n// }\n\n// RegisterFunc registers a parameter function\n// to that macro.\n// Accepts the func name (\"range\")\n// and the function body, which should return an EvaluatorFunc\n// a bool (it will be converted to EvaluatorFunc later on),\n// i.e RegisterFunc(\"min\", func(minValue int) func(paramValue string) bool){})\nfunc (m *Macro) RegisterFunc(funcName string, fn any) *Macro {\n\tfullFn := convertBuilderFunc(fn)\n\n\t// if it's not valid then not register it at all.\n\tif fullFn != nil {\n\t\tm.registerFunc(funcName, fullFn)\n\t}\n\n\treturn m\n}\n\nfunc (m *Macro) registerFunc(funcName string, fullFn ParamFuncBuilder) {\n\tif !goodParamFuncName(funcName) {\n\t\treturn\n\t}\n\n\tfor _, fn := range m.funcs {\n\t\tif fn.Name == funcName {\n\t\t\tfn.Func = fullFn\n\t\t\treturn\n\t\t}\n\t}\n\n\tm.funcs = append(m.funcs, ParamFunc{\n\t\tName: funcName,\n\t\tFunc: fullFn,\n\t})\n}\n\nfunc (m *Macro) getFunc(funcName string) ParamFuncBuilder {\n\tfor _, fn := range m.funcs {\n\t\tif fn.Name == funcName {\n\t\t\tif fn.Func == nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn fn.Func\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "macro/macro_test.go",
    "content": "package macro\n\nimport (\n\t\"reflect\"\n\t\"strconv\"\n\t\"testing\"\n\t\"time\"\n)\n\n// Most important tests to look:\n// ../parser/parser_test.go\n// ../lexer/lexer_test.go\n\nfunc TestGoodParamFunc(t *testing.T) {\n\tgood1 := func(min int, max int) func(string) bool {\n\t\treturn func(paramValue string) bool {\n\t\t\treturn true\n\t\t}\n\t}\n\n\tgood2 := func(min uint64, max uint64) func(string) bool {\n\t\treturn func(paramValue string) bool {\n\t\t\treturn true\n\t\t}\n\t}\n\n\tnotgood1 := func(min int, max int) bool {\n\t\treturn false\n\t}\n\n\tif !goodParamFunc(reflect.TypeOf(good1)) {\n\t\tt.Fatalf(\"expected good1 func to be good but it's not\")\n\t}\n\n\tif !goodParamFunc(reflect.TypeOf(good2)) {\n\t\tt.Fatalf(\"expected good2 func to be good but it's not\")\n\t}\n\n\tif goodParamFunc(reflect.TypeOf(notgood1)) {\n\t\tt.Fatalf(\"expected notgood1 func to be the worst\")\n\t}\n}\n\nfunc TestGoodParamFuncName(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tgood bool\n\t}{\n\t\t{\"range\", true},\n\t\t{\"_range\", true},\n\t\t{\"range_\", true},\n\t\t{\"r_ange\", true},\n\t\t// numbers or other symbols are invalid.\n\t\t{\"range1\", false},\n\t\t{\"2range\", false},\n\t\t{\"r@nge\", false},\n\t\t{\"rang3\", false},\n\t}\n\tfor i, tt := range tests {\n\t\tisGood := goodParamFuncName(tt.name)\n\t\tif tt.good && !isGood {\n\t\t\tt.Fatalf(\"tests[%d] - expecting valid name but got invalid for name %s\", i, tt.name)\n\t\t} else if !tt.good && isGood {\n\t\t\tt.Fatalf(\"tests[%d] - expecting invalid name but got valid for name %s\", i, tt.name)\n\t\t}\n\t}\n}\n\nfunc testEvaluatorRaw(t *testing.T, macroEvaluator *Macro, input string, expectedType reflect.Kind, pass bool, i int) {\n\tt.Helper()\n\n\tif macroEvaluator.Evaluator == nil && pass {\n\t\treturn // if not evaluator defined then it should allow everything.\n\t}\n\tvalue, passed := macroEvaluator.Evaluator(input)\n\tif pass != passed {\n\t\tt.Fatalf(\"%s - tests[%d] - expecting[pass] %v but got %v\", t.Name(), i, pass, passed)\n\t}\n\n\tif !passed {\n\t\treturn\n\t}\n\n\tif value == nil && expectedType != reflect.Invalid {\n\t\tt.Fatalf(\"%s - tests[%d] - expecting[value] to not be nil\", t.Name(), i)\n\t}\n\n\tif v := reflect.ValueOf(value); v.Kind() != expectedType {\n\t\tt.Fatalf(\"%s - tests[%d] - expecting[value.Kind] %v but got %v\", t.Name(), i, expectedType, v.Kind())\n\t}\n}\n\nfunc TestStringEvaluatorRaw(t *testing.T) {\n\ttests := []struct {\n\t\tpass  bool\n\t\tinput string\n\t}{\n\t\t{true, \"astring\"},                         // 0\n\t\t{true, \"astringwith_numb3rS_and_symbol$\"}, // 1\n\t\t{true, \"32321\"},                           // 2\n\t\t{true, \"main.css\"},                        // 3\n\t\t{true, \"/assets/main.css\"},                // 4\n\t\t// false never\n\t} // 0\n\n\tfor i, tt := range tests {\n\t\ttestEvaluatorRaw(t, String, tt.input, reflect.String, tt.pass, i)\n\t}\n}\n\nfunc TestIntEvaluatorRaw(t *testing.T) {\n\tx64 := strconv.IntSize == 64\n\n\ttests := []struct {\n\t\tpass  bool\n\t\tinput string\n\t}{\n\t\t{false, \"astring\"},                                 // 0\n\t\t{false, \"astringwith_numb3rS_and_symbol$\"},         // 1\n\t\t{true, \"32321\"},                                    // 2\n\t\t{x64, \"9223372036854775807\" /* max int64 */},       // 3\n\t\t{x64, \"-9223372036854775808\" /* min int64 */},      // 4\n\t\t{false, \"-18446744073709553213213213213213121615\"}, // 5\n\t\t{false, \"42 18446744073709551615\"},                 // 6\n\t\t{false, \"--42\"},                                    // 7\n\t\t{true, \"+42\"},                                      // 8\n\t\t{false, \"main.css\"},                                // 9\n\t\t{false, \"/assets/main.css\"},                        // 10\n\t}\n\n\tfor i, tt := range tests {\n\t\ttestEvaluatorRaw(t, Int, tt.input, reflect.Int, tt.pass, i)\n\t}\n}\n\nfunc BenchmarkIntEvaluatorRaw(b *testing.B) {\n\tb.ReportAllocs()\n\n\tfor i := 0; i < b.N; i++ {\n\t\tInt.Evaluator(\"1234568320\")\n\t\tInt.Evaluator(\"-12345678999321\")\n\t}\n}\n\nfunc TestInt8EvaluatorRaw(t *testing.T) {\n\ttests := []struct {\n\t\tpass  bool\n\t\tinput string\n\t}{\n\t\t{false, \"astring\"},                         // 0\n\t\t{false, \"astringwith_numb3rS_and_symbol$\"}, // 1\n\t\t{false, \"32321\"},                           // 2\n\t\t{true, \"127\" /* max int8 */},               // 3\n\t\t{true, \"-128\" /* min int8 */},              // 4\n\t\t{false, \"128\"},                             // 5\n\t\t{false, \"-129\"},                            // 6\n\t\t{false, \"-18446744073709553213213213213213121615\"}, // 7\n\t\t{false, \"42 18446744073709551615\"},                 // 8\n\t\t{false, \"--42\"},                                    // 9\n\t\t{true, \"+42\"},                                      // 10\n\t\t{false, \"main.css\"},                                // 11\n\t\t{false, \"/assets/main.css\"},                        // 12\n\t}\n\n\tfor i, tt := range tests {\n\t\ttestEvaluatorRaw(t, Int8, tt.input, reflect.Int8, tt.pass, i)\n\t}\n}\n\nfunc TestInt16EvaluatorRaw(t *testing.T) {\n\ttests := []struct {\n\t\tpass  bool\n\t\tinput string\n\t}{\n\t\t{false, \"astring\"},                         // 0\n\t\t{false, \"astringwith_numb3rS_and_symbol$\"}, // 1\n\t\t{true, \"32321\"},                            // 2\n\t\t{true, \"32767\" /* max int16 */},            // 3\n\t\t{true, \"-32768\" /* min int16 */},           // 4\n\t\t{false, \"-32769\"},                          // 5\n\t\t{false, \"32768\"},                           // 6\n\t\t{false, \"-18446744073709553213213213213213121615\"}, // 7\n\t\t{false, \"42 18446744073709551615\"},                 // 8\n\t\t{false, \"--42\"},                                    // 9\n\t\t{true, \"+42\"},                                      // 10\n\t\t{false, \"main.css\"},                                // 11\n\t\t{false, \"/assets/main.css\"},                        // 12\n\t}\n\n\tfor i, tt := range tests {\n\t\ttestEvaluatorRaw(t, Int16, tt.input, reflect.Int16, tt.pass, i)\n\t}\n}\n\nfunc TestInt32EvaluatorRaw(t *testing.T) {\n\ttests := []struct {\n\t\tpass  bool\n\t\tinput string\n\t}{\n\t\t{false, \"astring\"},                         // 0\n\t\t{false, \"astringwith_numb3rS_and_symbol$\"}, // 1\n\t\t{true, \"32321\"},                            // 2\n\t\t{true, \"1\"},                                // 3\n\t\t{true, \"42\"},                               // 4\n\t\t{true, \"2147483647\" /* max int32 */},       // 5\n\t\t{true, \"-2147483648\" /* min int32 */},      // 6\n\t\t{false, \"-2147483649\"},                     // 7\n\t\t{false, \"2147483648\"},                      // 8\n\t\t{false, \"-18446744073709553213213213213213121615\"}, // 9\n\t\t{false, \"42 18446744073709551615\"},                 // 10\n\t\t{false, \"--42\"},                                    // 11\n\t\t{true, \"+42\"},                                      // 12\n\t\t{false, \"main.css\"},                                // 13\n\t\t{false, \"/assets/main.css\"},                        // 14\n\t}\n\n\tfor i, tt := range tests {\n\t\ttestEvaluatorRaw(t, Int32, tt.input, reflect.Int32, tt.pass, i)\n\t}\n}\n\nfunc TestInt64EvaluatorRaw(t *testing.T) {\n\ttests := []struct {\n\t\tpass  bool\n\t\tinput string\n\t}{\n\t\t{false, \"astring\"},                                 // 0\n\t\t{false, \"astringwith_numb3rS_and_symbol$\"},         // 1\n\t\t{false, \"18446744073709551615\"},                    // 2\n\t\t{false, \"92233720368547758079223372036854775807\"},  // 3\n\t\t{false, \"9223372036854775808 9223372036854775808\"}, // 4\n\t\t{false, \"main.css\"},                                // 5\n\t\t{false, \"/assets/main.css\"},                        // 6\n\t\t{true, \"9223372036854775807\"},                      // 7\n\t\t{true, \"-9223372036854775808\"},                     // 8\n\t\t{true, \"-0\"},                                       // 9\n\t\t{true, \"1\"},                                        // 10\n\t\t{true, \"-042\"},                                     // 11\n\t\t{true, \"142\"},                                      // 12\n\t}\n\n\tfor i, tt := range tests {\n\t\ttestEvaluatorRaw(t, Int64, tt.input, reflect.Int64, tt.pass, i)\n\t}\n}\n\nfunc TestUintEvaluatorRaw(t *testing.T) {\n\tx64 := strconv.IntSize == 64\n\n\ttests := []struct {\n\t\tpass  bool\n\t\tinput string\n\t}{\n\t\t{false, \"astring\"},                             // 0\n\t\t{false, \"astringwith_numb3rS_and_symbol$\"},     // 1\n\t\t{true, \"32321\"},                                // 2\n\t\t{true, \"1\"},                                    // 3\n\t\t{true, \"42\"},                                   // 4\n\t\t{x64, \"18446744073709551615\" /* max uint64 */}, // 5\n\t\t{true, \"4294967295\" /* max uint32 */},          // 6\n\t\t{false, \"-2147483649\"},                         // 7\n\t\t{true, \"2147483648\"},                           // 8\n\t\t{false, \"-18446744073709553213213213213213121615\"}, // 9\n\t\t{false, \"42 18446744073709551615\"},                 // 10\n\t\t{false, \"--42\"},                                    // 11\n\t\t{false, \"+42\"},                                     // 12\n\t\t{false, \"main.css\"},                                // 13\n\t\t{false, \"/assets/main.css\"},                        // 14\n\t}\n\n\tfor i, tt := range tests {\n\t\ttestEvaluatorRaw(t, Uint, tt.input, reflect.Uint, tt.pass, i)\n\t}\n}\n\nfunc TestUint8EvaluatorRaw(t *testing.T) {\n\ttests := []struct {\n\t\tpass  bool\n\t\tinput string\n\t}{\n\t\t{false, \"astring\"},                                 // 0\n\t\t{false, \"astringwith_numb3rS_and_symbol$\"},         // 1\n\t\t{false, \"-9223372036854775808\"},                    // 2\n\t\t{false, \"main.css\"},                                // 3\n\t\t{false, \"/assets/main.css\"},                        // 4\n\t\t{false, \"92233720368547758079223372036854775807\"},  // 5\n\t\t{false, \"9223372036854775808 9223372036854775808\"}, // 6\n\t\t{false, \"-1\"},                                      // 7\n\t\t{false, \"-0\"},                                      // 8\n\t\t{false, \"+1\"},                                      // 9\n\t\t{false, \"18446744073709551615\"},                    // 10\n\t\t{false, \"9223372036854775807\"},                     // 11\n\t\t{true, \"021\"},                                      // 12 - leading zeroes are allowed.\n\t\t{false, \"300\"},                                     // 13\n\t\t{true, \"0\"},                                        // 14\n\t\t{true, \"255\"},                                      // 15\n\t\t{true, \"21\"},                                       // 16\n\t}\n\n\tfor i, tt := range tests {\n\t\ttestEvaluatorRaw(t, Uint8, tt.input, reflect.Uint8, tt.pass, i)\n\t}\n}\n\nfunc TestUint16EvaluatorRaw(t *testing.T) {\n\ttests := []struct {\n\t\tpass  bool\n\t\tinput string\n\t}{\n\t\t{false, \"astring\"},                         // 0\n\t\t{false, \"astringwith_numb3rS_and_symbol$\"}, // 1\n\t\t{true, \"32321\"},                            // 2\n\t\t{true, \"65535\" /* max uint16 */},           // 3\n\t\t{true, \"0\" /* min uint16 */},               // 4\n\t\t{false, \"-32769\"},                          // 5\n\t\t{true, \"32768\"},                            // 6\n\t\t{false, \"-18446744073709553213213213213213121615\"}, // 7\n\t\t{false, \"42 18446744073709551615\"},                 // 8\n\t\t{false, \"--42\"},                                    // 9\n\t\t{false, \"+42\"},                                     // 10\n\t\t{false, \"main.css\"},                                // 11\n\t\t{false, \"/assets/main.css\"},                        // 12\n\t}\n\n\tfor i, tt := range tests {\n\t\ttestEvaluatorRaw(t, Uint16, tt.input, reflect.Uint16, tt.pass, i)\n\t}\n}\n\nfunc TestUint32EvaluatorRaw(t *testing.T) {\n\ttests := []struct {\n\t\tpass  bool\n\t\tinput string\n\t}{\n\t\t{false, \"astring\"},                         // 0\n\t\t{false, \"astringwith_numb3rS_and_symbol$\"}, // 1\n\t\t{true, \"32321\"},                            // 2\n\t\t{true, \"1\"},                                // 3\n\t\t{true, \"42\"},                               // 4\n\t\t{true, \"4294967295\" /* max uint32*/},       // 5\n\t\t{true, \"0\" /* min uint32 */},               // 6\n\t\t{false, \"-2147483649\"},                     // 7\n\t\t{true, \"2147483648\"},                       // 8\n\t\t{false, \"-18446744073709553213213213213213121615\"}, // 9\n\t\t{false, \"42 18446744073709551615\"},                 // 10\n\t\t{false, \"--42\"},                                    // 11\n\t\t{false, \"+42\"},                                     // 12\n\t\t{false, \"main.css\"},                                // 13\n\t\t{false, \"/assets/main.css\"},                        // 14\n\t}\n\n\tfor i, tt := range tests {\n\t\ttestEvaluatorRaw(t, Uint32, tt.input, reflect.Uint32, tt.pass, i)\n\t}\n}\n\nfunc TestUint64EvaluatorRaw(t *testing.T) {\n\ttests := []struct {\n\t\tpass  bool\n\t\tinput string\n\t}{\n\t\t{false, \"astring\"},                                 // 0\n\t\t{false, \"astringwith_numb3rS_and_symbol$\"},         // 1\n\t\t{false, \"-9223372036854775808\"},                    // 2\n\t\t{false, \"main.css\"},                                // 3\n\t\t{false, \"/assets/main.css\"},                        // 4\n\t\t{false, \"92233720368547758079223372036854775807\"},  // 5\n\t\t{false, \"9223372036854775808 9223372036854775808\"}, // 6\n\t\t{false, \"-1\"},                                      // 7\n\t\t{false, \"-0\"},                                      // 8\n\t\t{false, \"+1\"},                                      // 9\n\t\t{true, \"18446744073709551615\"},                     // 10\n\t\t{true, \"9223372036854775807\"},                      // 11\n\t\t{true, \"0\"},                                        // 12\n\t}\n\n\tfor i, tt := range tests {\n\t\ttestEvaluatorRaw(t, Uint64, tt.input, reflect.Uint64, tt.pass, i)\n\t}\n}\n\nfunc TestAlphabeticalEvaluatorRaw(t *testing.T) {\n\ttests := []struct {\n\t\tpass  bool\n\t\tinput string\n\t}{\n\t\t{true, \"astring\"},                          // 0\n\t\t{false, \"astringwith_numb3rS_and_symbol$\"}, // 1\n\t\t{false, \"32321\"},                           // 2\n\t\t{false, \"main.css\"},                        // 3\n\t\t{false, \"/assets/main.css\"},                // 4\n\t}\n\n\tfor i, tt := range tests {\n\t\ttestEvaluatorRaw(t, Alphabetical, tt.input, reflect.String, tt.pass, i)\n\t}\n}\n\nfunc TestFileEvaluatorRaw(t *testing.T) {\n\ttests := []struct {\n\t\tpass  bool\n\t\tinput string\n\t}{\n\t\t{true, \"astring\"},                          // 0\n\t\t{false, \"astringwith_numb3rS_and_symbol$\"}, // 1\n\t\t{true, \"32321\"},                            // 2\n\t\t{true, \"main.css\"},                         // 3\n\t\t{false, \"/assets/main.css\"},                // 4\n\t}\n\n\tfor i, tt := range tests {\n\t\ttestEvaluatorRaw(t, File, tt.input, reflect.String, tt.pass, i)\n\t}\n}\n\nfunc TestPathEvaluatorRaw(t *testing.T) {\n\tpathTests := []struct {\n\t\tpass  bool\n\t\tinput string\n\t}{\n\t\t{true, \"astring\"},                         // 0\n\t\t{true, \"astringwith_numb3rS_and_symbol$\"}, // 1\n\t\t{true, \"32321\"},                           // 2\n\t\t{true, \"main.css\"},                        // 3\n\t\t{true, \"/assets/main.css\"},                // 4\n\t\t{true, \"disk/assets/main.css\"},            // 5\n\t}\n\n\tfor i, tt := range pathTests {\n\t\ttestEvaluatorRaw(t, Path, tt.input, reflect.String, tt.pass, i)\n\t}\n}\n\nfunc TestUUIDEvaluatorRaw(t *testing.T) {\n\ttests := []struct {\n\t\tpass  bool\n\t\tinput string\n\t}{\n\t\t{true, \"978ad967-5fad-4c82-af99-580097ace662\"}, // v4\n\t\t{true, \"c7067f9c-6d43-11eb-9439-0242ac130002\"}, // v1\n\t\t{false, \"astring\"},                         // 2\n\t\t{false, \"astringwith_numb3rS_and_symbol$\"}, // 3\n\t\t{false, \"32321\"},                           // 4\n\t\t{false, \"main.css\"},                        // 5\n\t\t{false, \"/assets/main.css\"},                // 6\n\t}\n\tfor i, tt := range tests {\n\t\ttestEvaluatorRaw(t, UUID, tt.input, reflect.String, tt.pass, i)\n\t}\n}\n\nfunc TestMailEvaluatorRaw(t *testing.T) {\n\ttests := []struct {\n\t\tpass  bool\n\t\tinput string\n\t}{\n\t\t{true, \"kataras2006@hotmail.com\"}, // 0\n\t\t{true, \"iris-go@outlook.com\"},     // 1\n\t\t{true, \"iris-go@mail\"},            // 2\n\t\t{true, \"kataras@k.c\"},             // 3\n\t\t{false, \"www.kataras@\"},           // 4\n\t\t{false, \"name\"},                   // 5\n\t\t{false, \"b-c@\"},                   // 6\n\t}\n\tfor i, tt := range tests {\n\t\ttestEvaluatorRaw(t, Mail, tt.input, reflect.String, tt.pass, i)\n\t}\n}\n\nfunc TestEmailEvaluatorRaw(t *testing.T) {\n\ttests := []struct {\n\t\tpass  bool\n\t\tinput string\n\t}{\n\t\t{true, \"kataras2006@hotmail.com\"}, // 0\n\t\t{true, \"iris-go@outlook.com\"},     // 1\n\t\t{false, \"iris-go@mail\"},           // 2\n\t\t{false, \"kataras@k.c\"},            // 3\n\t\t{false, \"www.kataras@\"},           // 4\n\t\t{false, \"name\"},                   // 5\n\t\t{false, \"b-c@\"},                   // 6\n\t}\n\tfor i, tt := range tests {\n\t\ttestEvaluatorRaw(t, Email, tt.input, reflect.String, tt.pass, i)\n\t}\n}\n\nfunc TestDateEvaluatorRaw(t *testing.T) {\n\ttests := []struct {\n\t\tpass            bool\n\t\tinput           string\n\t\ttimeStringValue string\n\t}{\n\t\t{true, \"2022/04/21\", \"2022-04-21 00:00:00 +0000 UTC\"}, // 0\n\t\t{true, \"2022/12/05\", \"2022-12-05 00:00:00 +0000 UTC\"}, // 1\n\t\t{false, \"2022/4\", \"\"},      // 2\n\t\t{false, \"1/4/1\", \"\"},       // 3\n\t\t{false, \"2022/4/\", \"\"},     // 4\n\t\t{false, \"2022/4/21/0\", \"\"}, // 5\n\t\t{false, \"1993\", \"\"},        // 6\n\t}\n\tfor i, tt := range tests {\n\t\ttestEvaluatorRaw(t, Date, tt.input, reflect.TypeOf(time.Time{}).Kind(), tt.pass, i)\n\n\t\tif v, ok := Date.Evaluator(tt.input); ok {\n\t\t\tif value, ok := v.(time.Time); ok {\n\t\t\t\tif expected, got := tt.timeStringValue, value.String(); expected != got {\n\t\t\t\t\tt.Fatalf(\"[%d] expected: %s but got: %s\", i, expected, got)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tt.Fatalf(\"[%d] expected to be able to cast as time.Time directly\", i)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestWeekdayEvaluatorRaw(t *testing.T) {\n\ttests := []struct {\n\t\tpass     bool\n\t\tinput    string\n\t\texpected time.Weekday\n\t}{\n\t\t{true, \"Monday\", time.Monday},        // 0\n\t\t{true, \"monday\", time.Monday},        // 1\n\t\t{false, \"Sundays\", time.Weekday(-1)}, // 2\n\t\t{false, \"sundays\", time.Weekday(-1)}, // 3\n\t\t{false, \"-1\", time.Weekday(-1)},      // 4\n\t\t{true, \"0000002\", time.Tuesday},      // 5\n\t\t{true, \"3\", time.Wednesday},          // 6\n\t\t{true, \"6\", time.Saturday},           // 7\n\t}\n\tfor i, tt := range tests {\n\t\ttestEvaluatorRaw(t, Weekday, tt.input, reflect.TypeOf(time.Weekday(0)).Kind(), tt.pass, i)\n\n\t\tif v, ok := Weekday.Evaluator(tt.input); ok {\n\t\t\tif value, ok := v.(time.Weekday); ok {\n\t\t\t\tif expected, got := tt.expected, value; expected != got {\n\t\t\t\t\tt.Fatalf(\"[%d] expected: %s but got: %s\", i, expected, got)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tt.Fatalf(\"[%d] expected to be able to cast as time.Weekday directly\", i)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestConvertBuilderFunc(t *testing.T) {\n\tfn := func(min uint64, slice []string) func(string) bool {\n\t\treturn func(paramValue string) bool {\n\t\t\tif expected, got := \"ok\", paramValue; expected != got {\n\t\t\t\tt.Fatalf(\"paramValue is not the expected one: %s vs %s\", expected, got)\n\t\t\t}\n\n\t\t\tif expected, got := uint64(1), min; expected != got {\n\t\t\t\tt.Fatalf(\"min argument is not the expected one: %d vs %d\", expected, got)\n\t\t\t}\n\n\t\t\tif expected, got := []string{\"name1\", \"name2\"}, slice; len(expected) == len(got) {\n\t\t\t\tif expected, got := \"name1\", slice[0]; expected != got {\n\t\t\t\t\tt.Fatalf(\"slice argument[%d] does not contain the expected value: %s vs %s\", 0, expected, got)\n\t\t\t\t}\n\n\t\t\t\tif expected, got := \"name2\", slice[1]; expected != got {\n\t\t\t\t\tt.Fatalf(\"slice argument[%d] does not contain the expected value: %s vs %s\", 1, expected, got)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tt.Fatalf(\"slice argument is not the expected one, the length is difference: %d vs %d\", len(expected), len(got))\n\t\t\t}\n\n\t\t\treturn true\n\t\t}\n\t}\n\n\tevalFunc := convertBuilderFunc(fn)\n\tif !evalFunc([]string{\"1\", \"[name1,name2]\"}).Call([]reflect.Value{reflect.ValueOf(\"ok\")})[0].Interface().(bool) {\n\t\tt.Fatalf(\"failed, it should fail already\")\n\t}\n\n\tfnSimplify := func(requestParamValue string) bool {\n\t\treturn requestParamValue == \"kataras\"\n\t}\n\n\tevalFunc = convertBuilderFunc(fnSimplify)\n\tif !evalFunc([]string{}).Call([]reflect.Value{reflect.ValueOf(\"kataras\")})[0].Interface().(bool) {\n\t\tt.Fatalf(\"it should pass, the combile arguments are empty and the given request value is the expected one\")\n\t}\n\n\tdefer func() {\n\t\tif r := recover(); r == nil {\n\t\t\tt.Fatalf(\"it should panic, the combile arguments are more than one\")\n\t\t}\n\t}()\n\n\t// should panic.\n\tevalFunc([]string{\"1\"}).Call([]reflect.Value{reflect.ValueOf(\"kataras\")})\n}\n"
  },
  {
    "path": "macro/macros.go",
    "content": "package macro\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/mail\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/macro/interpreter/ast\"\n\n\t\"github.com/google/uuid\"\n)\n\nvar (\n\t// String type\n\t// Allows anything (single path segment, as everything except the `Path`).\n\t// Its functions can be used by the rest of the macros and param types whenever not available function by name is used.\n\t// Because of its \"master\" boolean value to true (third parameter).\n\tString = NewMacro(\"string\", \"\", \"\", true, false, nil).\n\t\tRegisterFunc(\"regexp\", MustRegexp).\n\t\t// checks if param value starts with the 'prefix' arg\n\t\tRegisterFunc(\"prefix\", func(prefix string) func(string) bool {\n\t\t\treturn func(paramValue string) bool {\n\t\t\t\treturn strings.HasPrefix(paramValue, prefix)\n\t\t\t}\n\t\t}).\n\t\t// checks if param value ends with the 'suffix' arg\n\t\tRegisterFunc(\"suffix\", func(suffix string) func(string) bool {\n\t\t\treturn func(paramValue string) bool {\n\t\t\t\treturn strings.HasSuffix(paramValue, suffix)\n\t\t\t}\n\t\t}).\n\t\t// checks if param value contains the 's' arg\n\t\tRegisterFunc(\"contains\", func(s string) func(string) bool {\n\t\t\treturn func(paramValue string) bool {\n\t\t\t\treturn strings.Contains(paramValue, s)\n\t\t\t}\n\t\t}).\n\t\t// checks if param value's length is at least 'min'\n\t\tRegisterFunc(\"min\", func(min int) func(string) bool {\n\t\t\treturn func(paramValue string) bool {\n\t\t\t\treturn len(paramValue) >= min\n\t\t\t}\n\t\t}).\n\t\t// checks if param value's length is not bigger than 'max'\n\t\tRegisterFunc(\"max\", func(max int) func(string) bool {\n\t\t\treturn func(paramValue string) bool {\n\t\t\t\treturn max >= len(paramValue)\n\t\t\t}\n\t\t}).\n\t\t// checks if param value's matches the given input\n\t\tRegisterFunc(\"eq\", func(s string) func(string) bool {\n\t\t\treturn func(paramValue string) bool {\n\t\t\t\treturn paramValue == s\n\t\t\t}\n\t\t}).\n\t\t// checks if param value's matches at least one of the inputs\n\t\tRegisterFunc(\"eqor\", func(texts []string) func(string) bool {\n\t\t\tif len(texts) == 1 {\n\t\t\t\ttext := texts[0]\n\t\t\t\treturn func(paramValue string) bool {\n\t\t\t\t\treturn paramValue == text\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn func(paramValue string) bool {\n\t\t\t\tfor _, s := range texts {\n\t\t\t\t\tif paramValue == s {\n\t\t\t\t\t\treturn true\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn false\n\t\t\t}\n\t\t})\n\n\t// Int or number type\n\t// both positive and negative numbers, actual value can be min-max int64 or min-max int32 depends on the arch.\n\t// If x64: -9223372036854775808 to 9223372036854775807.\n\t// If x32: -2147483648 to 2147483647 and etc..\n\tInt = NewMacro(\"int\", \"number\", 0, false, false, func(paramValue string) (any, bool) {\n\t\tv, err := strconv.Atoi(paramValue)\n\t\tif err != nil {\n\t\t\treturn err, false\n\t\t}\n\n\t\treturn v, true\n\t}).\n\t\t// checks if the param value's int representation is\n\t\t// bigger or equal than 'min'\n\t\tRegisterFunc(\"min\", func(min int) func(int) bool {\n\t\t\treturn func(paramValue int) bool {\n\t\t\t\treturn paramValue >= min\n\t\t\t}\n\t\t}).\n\t\t// checks if the param value's int representation is\n\t\t// smaller or equal than 'max'.\n\t\tRegisterFunc(\"max\", func(max int) func(int) bool {\n\t\t\treturn func(paramValue int) bool {\n\t\t\t\treturn paramValue <= max\n\t\t\t}\n\t\t}).\n\t\t// checks if the param value's int representation is\n\t\t// between min and max, including 'min' and 'max'.\n\t\tRegisterFunc(\"range\", func(min, max int) func(int) bool {\n\t\t\treturn func(paramValue int) bool {\n\t\t\t\treturn !(paramValue < min || paramValue > max)\n\t\t\t}\n\t\t})\n\n\t// Int8 type\n\t// -128 to 127.\n\tInt8 = NewMacro(\"int8\", \"\", int8(0), false, false, func(paramValue string) (any, bool) {\n\t\tv, err := strconv.ParseInt(paramValue, 10, 8)\n\t\tif err != nil {\n\t\t\treturn err, false\n\t\t}\n\t\treturn int8(v), true\n\t}).\n\t\tRegisterFunc(\"min\", func(min int8) func(int8) bool {\n\t\t\treturn func(paramValue int8) bool {\n\t\t\t\treturn paramValue >= min\n\t\t\t}\n\t\t}).\n\t\tRegisterFunc(\"max\", func(max int8) func(int8) bool {\n\t\t\treturn func(paramValue int8) bool {\n\t\t\t\treturn paramValue <= max\n\t\t\t}\n\t\t}).\n\t\tRegisterFunc(\"range\", func(min, max int8) func(int8) bool {\n\t\t\treturn func(paramValue int8) bool {\n\t\t\t\treturn !(paramValue < min || paramValue > max)\n\t\t\t}\n\t\t})\n\n\t// Int16 type\n\t// -32768 to 32767.\n\tInt16 = NewMacro(\"int16\", \"\", int16(0), false, false, func(paramValue string) (any, bool) {\n\t\tv, err := strconv.ParseInt(paramValue, 10, 16)\n\t\tif err != nil {\n\t\t\treturn err, false\n\t\t}\n\t\treturn int16(v), true\n\t}).\n\t\tRegisterFunc(\"min\", func(min int16) func(int16) bool {\n\t\t\treturn func(paramValue int16) bool {\n\t\t\t\treturn paramValue >= min\n\t\t\t}\n\t\t}).\n\t\tRegisterFunc(\"max\", func(max int16) func(int16) bool {\n\t\t\treturn func(paramValue int16) bool {\n\t\t\t\treturn paramValue <= max\n\t\t\t}\n\t\t}).\n\t\tRegisterFunc(\"range\", func(min, max int16) func(int16) bool {\n\t\t\treturn func(paramValue int16) bool {\n\t\t\t\treturn !(paramValue < min || paramValue > max)\n\t\t\t}\n\t\t})\n\n\t// Int32 type\n\t// -2147483648 to 2147483647.\n\tInt32 = NewMacro(\"int32\", \"\", int32(0), false, false, func(paramValue string) (any, bool) {\n\t\tv, err := strconv.ParseInt(paramValue, 10, 32)\n\t\tif err != nil {\n\t\t\treturn err, false\n\t\t}\n\t\treturn int32(v), true\n\t}).\n\t\tRegisterFunc(\"min\", func(min int32) func(int32) bool {\n\t\t\treturn func(paramValue int32) bool {\n\t\t\t\treturn paramValue >= min\n\t\t\t}\n\t\t}).\n\t\tRegisterFunc(\"max\", func(max int32) func(int32) bool {\n\t\t\treturn func(paramValue int32) bool {\n\t\t\t\treturn paramValue <= max\n\t\t\t}\n\t\t}).\n\t\tRegisterFunc(\"range\", func(min, max int32) func(int32) bool {\n\t\t\treturn func(paramValue int32) bool {\n\t\t\t\treturn !(paramValue < min || paramValue > max)\n\t\t\t}\n\t\t})\n\n\t// Int64 as int64 type\n\t// -9223372036854775808 to 9223372036854775807.\n\tInt64 = NewMacro(\"int64\", \"long\", int64(0), false, false, func(paramValue string) (any, bool) {\n\t\tv, err := strconv.ParseInt(paramValue, 10, 64)\n\t\tif err != nil { // if err == strconv.ErrRange...\n\t\t\treturn err, false\n\t\t}\n\t\treturn v, true\n\t}).\n\t\t// checks if the param value's int64 representation is\n\t\t// bigger or equal than 'min'.\n\t\tRegisterFunc(\"min\", func(min int64) func(int64) bool {\n\t\t\treturn func(paramValue int64) bool {\n\t\t\t\treturn paramValue >= min\n\t\t\t}\n\t\t}).\n\t\t// checks if the param value's int64 representation is\n\t\t// smaller or equal than 'max'.\n\t\tRegisterFunc(\"max\", func(max int64) func(int64) bool {\n\t\t\treturn func(paramValue int64) bool {\n\t\t\t\treturn paramValue <= max\n\t\t\t}\n\t\t}).\n\t\t// checks if the param value's int64 representation is\n\t\t// between min and max, including 'min' and 'max'.\n\t\tRegisterFunc(\"range\", func(min, max int64) func(int64) bool {\n\t\t\treturn func(paramValue int64) bool {\n\t\t\t\treturn !(paramValue < min || paramValue > max)\n\t\t\t}\n\t\t})\n\n\t// Uint as uint type\n\t// actual value can be min-max uint64 or min-max uint32 depends on the arch.\n\t// If x64: 0 to 18446744073709551615.\n\t// If x32: 0 to 4294967295 and etc.\n\tUint = NewMacro(\"uint\", \"\", uint(0), false, false, func(paramValue string) (any, bool) {\n\t\tv, err := strconv.ParseUint(paramValue, 10, strconv.IntSize) // 32,64...\n\t\tif err != nil {\n\t\t\treturn err, false\n\t\t}\n\t\treturn uint(v), true\n\t}).\n\t\t// checks if the param value's int representation is\n\t\t// bigger or equal than 'min'\n\t\tRegisterFunc(\"min\", func(min uint) func(uint) bool {\n\t\t\treturn func(paramValue uint) bool {\n\t\t\t\treturn paramValue >= min\n\t\t\t}\n\t\t}).\n\t\t// checks if the param value's int representation is\n\t\t// smaller or equal than 'max'.\n\t\tRegisterFunc(\"max\", func(max uint) func(uint) bool {\n\t\t\treturn func(paramValue uint) bool {\n\t\t\t\treturn paramValue <= max\n\t\t\t}\n\t\t}).\n\t\t// checks if the param value's int representation is\n\t\t// between min and max, including 'min' and 'max'.\n\t\tRegisterFunc(\"range\", func(min, max uint) func(uint) bool {\n\t\t\treturn func(paramValue uint) bool {\n\t\t\t\treturn !(paramValue < min || paramValue > max)\n\t\t\t}\n\t\t})\n\n\t// Uint8 as uint8 type\n\t// 0 to 255.\n\tUint8 = NewMacro(\"uint8\", \"\", uint8(0), false, false, func(paramValue string) (any, bool) {\n\t\tv, err := strconv.ParseUint(paramValue, 10, 8)\n\t\tif err != nil {\n\t\t\treturn err, false\n\t\t}\n\t\treturn uint8(v), true\n\t}).\n\t\t// checks if the param value's uint8 representation is\n\t\t// bigger or equal than 'min'.\n\t\tRegisterFunc(\"min\", func(min uint8) func(uint8) bool {\n\t\t\treturn func(paramValue uint8) bool {\n\t\t\t\treturn paramValue >= min\n\t\t\t}\n\t\t}).\n\t\t// checks if the param value's uint8 representation is\n\t\t// smaller or equal than 'max'.\n\t\tRegisterFunc(\"max\", func(max uint8) func(uint8) bool {\n\t\t\treturn func(paramValue uint8) bool {\n\t\t\t\treturn paramValue <= max\n\t\t\t}\n\t\t}).\n\t\t// checks if the param value's uint8 representation is\n\t\t// between min and max, including 'min' and 'max'.\n\t\tRegisterFunc(\"range\", func(min, max uint8) func(uint8) bool {\n\t\t\treturn func(paramValue uint8) bool {\n\t\t\t\treturn !(paramValue < min || paramValue > max)\n\t\t\t}\n\t\t})\n\n\t// Uint16 as uint16 type\n\t// 0 to 65535.\n\tUint16 = NewMacro(\"uint16\", \"\", uint16(0), false, false, func(paramValue string) (any, bool) {\n\t\tv, err := strconv.ParseUint(paramValue, 10, 16)\n\t\tif err != nil {\n\t\t\treturn err, false\n\t\t}\n\t\treturn uint16(v), true\n\t}).\n\t\tRegisterFunc(\"min\", func(min uint16) func(uint16) bool {\n\t\t\treturn func(paramValue uint16) bool {\n\t\t\t\treturn paramValue >= min\n\t\t\t}\n\t\t}).\n\t\tRegisterFunc(\"max\", func(max uint16) func(uint16) bool {\n\t\t\treturn func(paramValue uint16) bool {\n\t\t\t\treturn paramValue <= max\n\t\t\t}\n\t\t}).\n\t\tRegisterFunc(\"range\", func(min, max uint16) func(uint16) bool {\n\t\t\treturn func(paramValue uint16) bool {\n\t\t\t\treturn !(paramValue < min || paramValue > max)\n\t\t\t}\n\t\t})\n\n\t// Uint32 as uint32 type\n\t// 0 to 4294967295.\n\tUint32 = NewMacro(\"uint32\", \"\", uint32(0), false, false, func(paramValue string) (any, bool) {\n\t\tv, err := strconv.ParseUint(paramValue, 10, 32)\n\t\tif err != nil {\n\t\t\treturn err, false\n\t\t}\n\t\treturn uint32(v), true\n\t}).\n\t\tRegisterFunc(\"min\", func(min uint32) func(uint32) bool {\n\t\t\treturn func(paramValue uint32) bool {\n\t\t\t\treturn paramValue >= min\n\t\t\t}\n\t\t}).\n\t\tRegisterFunc(\"max\", func(max uint32) func(uint32) bool {\n\t\t\treturn func(paramValue uint32) bool {\n\t\t\t\treturn paramValue <= max\n\t\t\t}\n\t\t}).\n\t\tRegisterFunc(\"range\", func(min, max uint32) func(uint32) bool {\n\t\t\treturn func(paramValue uint32) bool {\n\t\t\t\treturn !(paramValue < min || paramValue > max)\n\t\t\t}\n\t\t})\n\n\t// Uint64 as uint64 type\n\t// 0 to 18446744073709551615.\n\tUint64 = NewMacro(\"uint64\", \"\", uint64(0), false, false, func(paramValue string) (any, bool) {\n\t\tv, err := strconv.ParseUint(paramValue, 10, 64)\n\t\tif err != nil {\n\t\t\treturn err, false\n\t\t}\n\t\treturn v, true\n\t}).\n\t\t// checks if the param value's uint64 representation is\n\t\t// bigger or equal than 'min'.\n\t\tRegisterFunc(\"min\", func(min uint64) func(uint64) bool {\n\t\t\treturn func(paramValue uint64) bool {\n\t\t\t\treturn paramValue >= min\n\t\t\t}\n\t\t}).\n\t\t// checks if the param value's uint64 representation is\n\t\t// smaller or equal than 'max'.\n\t\tRegisterFunc(\"max\", func(max uint64) func(uint64) bool {\n\t\t\treturn func(paramValue uint64) bool {\n\t\t\t\treturn paramValue <= max\n\t\t\t}\n\t\t}).\n\t\t// checks if the param value's uint64 representation is\n\t\t// between min and max, including 'min' and 'max'.\n\t\tRegisterFunc(\"range\", func(min, max uint64) func(uint64) bool {\n\t\t\treturn func(paramValue uint64) bool {\n\t\t\t\treturn !(paramValue < min || paramValue > max)\n\t\t\t}\n\t\t})\n\n\t// Bool or boolean as bool type\n\t// a string which is \"1\" or \"t\" or \"T\" or \"TRUE\" or \"true\" or \"True\"\n\t// or \"0\" or \"f\" or \"F\" or \"FALSE\" or \"false\" or \"False\".\n\tBool = NewMacro(\"bool\", \"boolean\", false, false, false, func(paramValue string) (any, bool) {\n\t\t// a simple if statement is faster than regex ^(true|false|True|False|t|0|f|FALSE|TRUE)$\n\t\t// in this case.\n\t\tv, err := strconv.ParseBool(paramValue)\n\t\tif err != nil {\n\t\t\treturn err, false\n\t\t}\n\t\treturn v, true\n\t})\n\n\t// ErrParamNotAlphabetical is fired when the parameter value is not an alphabetical text.\n\tErrParamNotAlphabetical = errors.New(\"parameter is not alphabetical\")\n\talphabeticalEval        = MustRegexp(\"^[a-zA-Z ]+$\")\n\t// Alphabetical letter type\n\t// letters only (upper or lowercase)\n\tAlphabetical = NewMacro(\"alphabetical\", \"\", \"\", false, false, func(paramValue string) (any, bool) {\n\t\tif !alphabeticalEval(paramValue) {\n\t\t\treturn fmt.Errorf(\"%s: %w\", paramValue, ErrParamNotAlphabetical), false\n\t\t}\n\t\treturn paramValue, true\n\t})\n\n\t// ErrParamNotFile is fired when the parameter value is not a form of a file.\n\tErrParamNotFile = errors.New(\"parameter is not a file\")\n\tfileEval        = MustRegexp(\"^[a-zA-Z0-9_.-]*$\")\n\t// File type\n\t// letters (upper or lowercase)\n\t// numbers (0-9)\n\t// underscore (_)\n\t// dash (-)\n\t// point (.)\n\t// no spaces! or other character\n\tFile = NewMacro(\"file\", \"\", \"\", false, false, func(paramValue string) (any, bool) {\n\t\tif !fileEval(paramValue) {\n\t\t\treturn fmt.Errorf(\"%s: %w\", paramValue, ErrParamNotFile), false\n\t\t}\n\t\treturn paramValue, true\n\t})\n\t// Path type\n\t// anything, should be the last part\n\t//\n\t// It allows everything, we have String and Path as different\n\t// types because I want to give the opportunity to the user\n\t// to organise the macro functions based on wildcard or single dynamic named path parameter.\n\t// Should be living in the latest path segment of a route path.\n\tPath = NewMacro(\"path\", \"\", \"\", false, true, nil)\n\n\t// UUID string type for validating a uuidv4 (and v1) path parameter.\n\t// Read more at: https://tools.ietf.org/html/rfc4122.\n\tUUID = NewMacro(\"uuid\", \"uuidv4\", \"\", false, false, func(paramValue string) (any, bool) {\n\t\t_, err := uuid.Parse(paramValue) // this is x10+ times faster than regexp.\n\t\tif err != nil {\n\t\t\treturn err, false\n\t\t}\n\n\t\treturn paramValue, true\n\t})\n\n\t// Email string type for validating an e-mail path parameter. It returns the address as string, instead of an *mail.Address.\n\t// Read more at go std mail.ParseAddress method. See the ':email' path parameter for a more strictly version of validation.\n\tMail = NewMacro(\"mail\", \"\", \"\", false, false, func(paramValue string) (any, bool) {\n\t\t_, err := mail.ParseAddress(paramValue)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"%s: %w\", paramValue, err), false\n\t\t}\n\n\t\treturn paramValue, true\n\t})\n\n\t// Email string type for validating an e-mail path parameter. It returns the address as string, instead of an *mail.Address.\n\t// It is a combined validation using mail.ParseAddress and net.LookupMX so only valid domains can be passed.\n\t// It's a more strictly version of the ':mail' path parameter.\n\tEmail = NewMacro(\"email\", \"\", \"\", false, false, func(paramValue string) (any, bool) {\n\t\t_, err := mail.ParseAddress(paramValue)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"%s: %w\", paramValue, err), false\n\t\t}\n\n\t\tdomainPart := strings.Split(paramValue, \"@\")[1]\n\n\t\tmx, err := net.LookupMX(domainPart)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"%s: %w\", paramValue, err), false\n\t\t}\n\n\t\tif len(mx) == 0 {\n\t\t\treturn fmt.Errorf(\"%s: mx is empty\", paramValue), false\n\t\t}\n\n\t\treturn paramValue, true\n\t})\n\n\tsimpleDateLayout = \"2006/01/02\"\n\n\t// Date type.\n\tDate = NewMacro(\"date\", \"\", time.Time{}, false, true, func(paramValue string) (any, bool) {\n\t\ttt, err := time.Parse(simpleDateLayout, paramValue)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"%s: %w\", paramValue, err), false\n\t\t}\n\n\t\treturn tt, true\n\t})\n\n\t// ErrParamNotWeekday is fired when the parameter value is not a form of a time.Weekday.\n\tErrParamNotWeekday = errors.New(\"parameter is not a valid weekday\")\n\tlongDayNames       = map[string]time.Weekday{\n\t\t\"Sunday\":    time.Sunday,\n\t\t\"Monday\":    time.Monday,\n\t\t\"Tuesday\":   time.Tuesday,\n\t\t\"Wednesday\": time.Wednesday,\n\t\t\"Thursday\":  time.Thursday,\n\t\t\"Friday\":    time.Friday,\n\t\t\"Saturday\":  time.Saturday,\n\t\t// lowercase.\n\t\t\"sunday\":    time.Sunday,\n\t\t\"monday\":    time.Monday,\n\t\t\"tuesday\":   time.Tuesday,\n\t\t\"wednesday\": time.Wednesday,\n\t\t\"thursday\":  time.Thursday,\n\t\t\"friday\":    time.Friday,\n\t\t\"saturday\":  time.Saturday,\n\t}\n\n\t// Weekday type, returns a type of time.Weekday.\n\t// Valid values:\n\t// 0 to 7 (leading zeros don't matter)  or \"Sunday\" to \"Monday\" or \"sunday\" to \"monday\".\n\tWeekday = NewMacro(\"weekday\", \"\", time.Weekday(0), false, false, func(paramValue string) (any, bool) {\n\t\td, ok := longDayNames[paramValue]\n\t\tif !ok {\n\t\t\t// try parse from integer.\n\t\t\tn, err := strconv.Atoi(paramValue)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"%s: %w\", paramValue, err), false\n\t\t\t}\n\n\t\t\tif n < 0 || n > 6 {\n\t\t\t\treturn fmt.Errorf(\"%s: %w\", paramValue, ErrParamNotWeekday), false\n\t\t\t}\n\n\t\t\treturn time.Weekday(n), true\n\t\t}\n\n\t\treturn d, true\n\t})\n\n\t// Defaults contains the defaults macro and parameters types for the router.\n\t//\n\t// Read https://github.com/kataras/iris/tree/main/_examples/routing/macros for more details.\n\tDefaults = &Macros{\n\t\tString,\n\t\tInt,\n\t\tInt8,\n\t\tInt16,\n\t\tInt32,\n\t\tInt64,\n\t\tUint,\n\t\tUint8,\n\t\tUint16,\n\t\tUint32,\n\t\tUint64,\n\t\tBool,\n\t\tAlphabetical,\n\t\tFile,\n\t\tPath,\n\t\tUUID,\n\t\tMail,\n\t\tEmail,\n\t\tDate,\n\t\tWeekday,\n\t}\n)\n\n// Macros is just a type of a slice of *Macro\n// which is responsible to register and search for macros based on the indent(parameter type).\ntype Macros []*Macro\n\n// Register registers a custom Macro.\n// The \"indent\" should not be empty and should be unique, it is the parameter type's name, i.e \"string\".\n// The \"alias\" is optionally and it should be unique, it is the alias of the parameter type.\n// The \"valueType\" should be the zero value of the parameter type, i.e \"\" for string, 0 for int and etc.\n// \"isMaster\" and \"isTrailing\" is for default parameter type and wildcard respectfully.\n// The \"evaluator\" is the function that is converted to an Iris handler which is executed every time\n// before the main chain of a route's handlers that contains this macro of the specific parameter type.\n//\n// Read https://github.com/kataras/iris/tree/main/_examples/routing/macros for more details.\nfunc (ms *Macros) Register(indent, alias string, valueType any, isMaster, isTrailing bool, evaluator ParamEvaluator) *Macro {\n\tmacro := NewMacro(indent, alias, valueType, isMaster, isTrailing, evaluator)\n\tif ms.register(macro) {\n\t\treturn macro\n\t}\n\treturn nil\n}\n\nfunc (ms *Macros) register(macro *Macro) bool {\n\tif macro.Indent() == \"\" {\n\t\treturn false\n\t}\n\n\tcp := *ms\n\n\tfor _, m := range cp {\n\t\t// can't add more than one with the same ast characteristics.\n\t\tif macro.Indent() == m.Indent() {\n\t\t\treturn false\n\t\t}\n\n\t\tif alias := macro.Alias(); alias != \"\" {\n\t\t\tif alias == m.Alias() || alias == m.Indent() {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\n\t\tif macro.Master() && m.Master() {\n\t\t\treturn false\n\t\t}\n\t}\n\n\tcp = append(cp, macro)\n\n\t*ms = cp\n\treturn true\n}\n\n// Unregister removes a macro and its parameter type from the list.\nfunc (ms *Macros) Unregister(indent string) bool {\n\tcp := *ms\n\n\tfor i, m := range cp {\n\t\tif m.Indent() == indent {\n\t\t\tcopy(cp[i:], cp[i+1:])\n\t\t\tcp[len(cp)-1] = nil\n\t\t\tcp = cp[:len(cp)-1]\n\n\t\t\t*ms = cp\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\n// Lookup returns the responsible macro for a parameter type, it can return nil.\nfunc (ms *Macros) Lookup(pt ast.ParamType) *Macro {\n\tif m := ms.Get(pt.Indent()); m != nil {\n\t\treturn m\n\t}\n\n\tif alias, has := ast.HasAlias(pt); has {\n\t\tif m := ms.Get(alias); m != nil {\n\t\t\treturn m\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// Get returns the responsible macro for a parameter type, it can return nil.\nfunc (ms *Macros) Get(indentOrAlias string) *Macro {\n\tif indentOrAlias == \"\" {\n\t\treturn nil\n\t}\n\n\tfor _, m := range *ms {\n\t\tif m.Indent() == indentOrAlias {\n\t\t\treturn m\n\t\t}\n\n\t\tif m.Alias() == indentOrAlias {\n\t\t\treturn m\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// GetMaster returns the default macro and its parameter type,\n// by default it will return the `String` macro which is responsible for the \"string\" parameter type.\nfunc (ms *Macros) GetMaster() *Macro {\n\tfor _, m := range *ms {\n\t\tif m.Master() {\n\t\t\treturn m\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// GetTrailings returns the macros that have support for wildcards parameter types.\n// By default it will return the `Path` macro which is responsible for the \"path\" parameter type.\nfunc (ms *Macros) GetTrailings() (macros []*Macro) {\n\tfor _, m := range *ms {\n\t\tif m.Trailing() {\n\t\t\tmacros = append(macros, m)\n\t\t}\n\t}\n\n\treturn\n}\n\n// SetErrorHandler registers a common type path parameter error handler.\n// The \"fnHandler\" MUST be a type of handler.ParamErrorHandler:\n// func(ctx iris.Context, paramIndex int, err error). It calls\n// the Macro.HandleError method for each of the \"ms\" entries.\nfunc (ms *Macros) SetErrorHandler(fnHandler any) {\n\tfor _, m := range *ms {\n\t\tif m == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tm.HandleError(fnHandler)\n\t}\n}\n"
  },
  {
    "path": "macro/template.go",
    "content": "package macro\n\nimport (\n\t\"reflect\"\n\n\t\"github.com/kataras/iris/v12/macro/interpreter/ast\"\n\t\"github.com/kataras/iris/v12/macro/interpreter/parser\"\n)\n\n// Template contains a route's path full parsed template.\n//\n// Fields:\n// Src is the raw source of the path, i.e /users/{id:int min(1)}\n// Params is the list of the Params that are being used to the\n// path,  i.e the min as param name and  1 as the param argument.\ntype Template struct {\n\t// Src is the original template given by the client\n\tSrc    string          `json:\"src\"`\n\tParams []TemplateParam `json:\"params\"`\n}\n\n// IsTrailing reports whether this Template is a traling one.\nfunc (t *Template) IsTrailing() bool {\n\treturn len(t.Params) > 0 && ast.IsTrailing(t.Params[len(t.Params)-1].Type)\n}\n\n// TemplateParam is the parsed macro parameter's template\n// they are being used to describe the param's syntax result.\ntype TemplateParam struct {\n\tmacro *Macro // keep for reference.\n\n\tSrc string `json:\"src\"` // the unparsed param'false source\n\t// Type is not useful anywhere here but maybe\n\t// it's useful on host to decide how to convert the path template to specific router's syntax\n\tType    ast.ParamType `json:\"type\"`\n\tName    string        `json:\"name\"`\n\tIndex   int           `json:\"index\"`\n\tErrCode int           `json:\"errCode\"`\n\t// Note that, the value MUST BE a type of `handler.ParamErrorHandler`.\n\tHandleError any `json:\"-\"` /* It's not an typed value because of import-cycle,\n\t// neither a special struct required, see `handler.MakeFilter`. */\n\tTypeEvaluator ParamEvaluator  `json:\"-\"`\n\tFuncs         []reflect.Value `json:\"-\"`\n\n\tstringInFuncs []func(string) bool\n\tcanEval       bool\n}\n\nfunc (p TemplateParam) preComputed() TemplateParam {\n\tfor _, pfn := range p.Funcs {\n\t\tif fn, ok := pfn.Interface().(func(string) bool); ok {\n\t\t\tp.stringInFuncs = append(p.stringInFuncs, fn)\n\t\t}\n\t}\n\n\t// if true then it should be execute the type parameter or its functions\n\t// else it can be ignored,\n\t// i.e {myparam} or {myparam:string} or {myparam:path} ->\n\t// their type evaluator is nil because they don't do any checks and they don't change\n\t// the default parameter value's type (string) so no need for any work).\n\tp.canEval = p.TypeEvaluator != nil || len(p.Funcs) > 0 || p.ErrCode != parser.DefaultParamErrorCode || p.HandleError != nil\n\n\treturn p\n}\n\n// CanEval returns true if this \"p\" TemplateParam should be evaluated in serve time.\n// It is computed before server ran and it is used to determinate if a route needs to build a macro handler (middleware).\nfunc (p *TemplateParam) CanEval() bool {\n\treturn p.canEval\n}\n\ntype errorInterface interface {\n\tError() string\n}\n\n// Eval is the most critical part of the TemplateParam.\n// It is responsible to return the type-based value if passed otherwise nil.\n// If the \"paramValue\" is the correct type of the registered parameter type\n// and all functions, if any, are passed.\n//\n// It is called from the converted macro handler (middleware)\n// from the higher-level component of \"kataras/iris/macro/handler#MakeHandler\".\nfunc (p *TemplateParam) Eval(paramValue string) (any, bool) {\n\tif p.TypeEvaluator == nil {\n\t\tfor _, fn := range p.stringInFuncs {\n\t\t\tif !fn(paramValue) {\n\t\t\t\treturn nil, false\n\t\t\t}\n\t\t}\n\t\treturn paramValue, true\n\t}\n\n\t// fmt.Printf(\"macro/template.go#L88: Eval for param value: %s and p.Src: %s\\n\", paramValue, p.Src)\n\n\tnewValue, passed := p.TypeEvaluator(paramValue)\n\tif !passed {\n\t\tif newValue != nil && p.HandleError != nil { // return this error only when a HandleError was registered.\n\t\t\tif _, ok := newValue.(errorInterface); ok {\n\t\t\t\treturn newValue, false // this is an error, see `HandleError` and `MakeFilter`.\n\t\t\t}\n\t\t}\n\n\t\treturn nil, false\n\t}\n\n\tif len(p.Funcs) > 0 {\n\t\tparamIn := []reflect.Value{reflect.ValueOf(newValue)}\n\t\tfor _, evalFunc := range p.Funcs {\n\t\t\t// or make it as func(any) bool and pass directly the \"newValue\"\n\t\t\t// but that would not be as easy for end-developer, so keep that \"slower\":\n\t\t\tif !evalFunc.Call(paramIn)[0].Interface().(bool) { // i.e func(paramValue int) bool\n\t\t\t\treturn nil, false\n\t\t\t}\n\t\t}\n\t}\n\n\t// fmt.Printf(\"macro/template.go: passed with value: %v and type: %T\\n\", newValue, newValue)\n\n\treturn newValue, true\n}\n\n// IsMacro reports whether this TemplateParam's underline macro matches the given one.\nfunc (p *TemplateParam) IsMacro(macro *Macro) bool {\n\treturn p.macro == macro\n}\n\n// Parse takes a full route path and a macro map (macro map contains the macro types with their registered param functions)\n// and returns a new Template.\n// It builds all the parameter functions for that template\n// and their evaluators, it's the api call that makes use the interpeter's parser -> lexer.\nfunc Parse(src string, macros Macros) (Template, error) {\n\ttypes := make([]ast.ParamType, len(macros))\n\tfor i, m := range macros {\n\t\ttypes[i] = m\n\t}\n\n\ttmpl := Template{Src: src}\n\tparams, err := parser.Parse(src, types)\n\tif err != nil {\n\t\treturn tmpl, err\n\t}\n\n\tfor idx, p := range params {\n\t\tm := macros.Lookup(p.Type)\n\t\ttypEval := m.Evaluator\n\n\t\ttmplParam := TemplateParam{\n\t\t\tmacro: m,\n\n\t\t\tSrc:           p.Src,\n\t\t\tType:          p.Type,\n\t\t\tName:          p.Name,\n\t\t\tIndex:         idx,\n\t\t\tErrCode:       p.ErrorCode,\n\t\t\tHandleError:   m.handleError,\n\t\t\tTypeEvaluator: typEval,\n\t\t}\n\n\t\tfor _, paramfn := range p.Funcs {\n\t\t\ttmplFn := m.getFunc(paramfn.Name)\n\t\t\tif tmplFn == nil { // if not find on this type, check for Master's which is for global funcs too.\n\t\t\t\tif m := macros.GetMaster(); m != nil {\n\t\t\t\t\ttmplFn = m.getFunc(paramfn.Name)\n\t\t\t\t}\n\n\t\t\t\tif tmplFn == nil { // if not found then just skip this param.\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tevalFn := tmplFn(paramfn.Args)\n\t\t\tif evalFn.IsNil() || !evalFn.IsValid() || evalFn.Kind() != reflect.Func {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\ttmplParam.Funcs = append(tmplParam.Funcs, evalFn)\n\t\t}\n\n\t\ttmpl.Params = append(tmpl.Params, tmplParam.preComputed())\n\t}\n\n\treturn tmpl, nil\n}\n\n// CountParams returns the length of the dynamic path's input parameters.\nfunc CountParams(fullpath string, macros Macros) int {\n\ttmpl, _ := Parse(fullpath, macros)\n\treturn len(tmpl.Params)\n}\n"
  },
  {
    "path": "middleware/README.md",
    "content": "Builtin Handlers\r\n------------\r\n\r\n| Middleware | Example |\r\n| -----------|-------------|\r\n| [rewrite](rewrite) | [iris/_examples/routing/rewrite](https://github.com/kataras/iris/tree/main/_examples/routing/rewrite) |\r\n| [basic authentication](basicauth) | [iris/_examples/auth/basicauth](https://github.com/kataras/iris/tree/main/_examples/auth/basicauth) |\r\n| [request logger](logger) | [iris/_examples/logging/request-logger](https://github.com/kataras/iris/tree/main/_examples/logging/request-logger) |\r\n| [HTTP method override](methodoverride) | [iris/middleware/methodoverride/methodoverride_test.go](https://github.com/kataras/iris/blob/main/middleware/methodoverride/methodoverride_test.go) |\r\n| [profiling (pprof)](pprof) | [iris/_examples/pprof](https://github.com/kataras/iris/tree/main/_examples/pprof) |\r\n| [Google reCAPTCHA](recaptcha) | [iris/_examples/auth/recaptcha](https://github.com/kataras/iris/tree/main/_examples/auth/recaptcha) |\r\n| [hCaptcha](hcaptcha) | [iris/_examples/auth/recaptcha](https://github.com/kataras/iris/tree/main/_examples/auth/hcaptcha) |\r\n| [recovery](recover) | [iris/_examples/recover](https://github.com/kataras/iris/tree/main/_examples/recover) |\r\n| [rate](rate) | [iris/_examples/request-ratelimit](https://github.com/kataras/iris/tree/main/_examples/request-ratelimit) |\r\n| [jwt](jwt) | [iris/_examples/auth/jwt](https://github.com/kataras/iris/tree/main/_examples/auth/jwt) |\r\n| [requestid](requestid) | [iris/middleware/requestid/requestid_test.go](https://github.com/kataras/iris/blob/main/_examples/middleware/requestid/requestid_test.go) |\r\n\r\nCommunity made\r\n------------\r\n\r\nMost of the experimental handlers are ported to work with _iris_'s handler form, from third-party sources.\r\n\r\n| Middleware | Description | Example |\r\n| -----------|--------|-------------|\r\n| [pg](https://github.com/iris-contrib/middleware/tree/master/pg) | Middleware that provides easy and type-safe access to PostgreSQL database | [iris-contrib/middleware/pg/_examples](https://github.com/iris-contrib/middleware/tree/master/pg/_examples) |\r\n| [jwt](https://github.com/iris-contrib/middleware/tree/master/jwt) | Middleware checks for a JWT on the `Authorization` header on incoming requests and decodes it | [iris-contrib/middleware/jwt/_example](https://github.com/iris-contrib/middleware/tree/master/jwt/_example) |\r\n| [cors](https://github.com/iris-contrib/middleware/tree/master/cors) | HTTP Access Control | [iris-contrib/middleware/cors/_example](https://github.com/iris-contrib/middleware/tree/master/cors/_example) |\r\n| [secure](https://github.com/iris-contrib/middleware/tree/master/secure) | Middleware that implements a few quick security wins | [iris-contrib/middleware/secure/_example](https://github.com/iris-contrib/middleware/tree/master/secure/_example/main.go) |\r\n| [tollbooth](https://github.com/iris-contrib/middleware/tree/master/tollboothic) | Generic middleware to rate-limit HTTP requests | [iris-contrib/middleware/tollboothic/_examples/limit-handler](https://github.com/iris-contrib/middleware/tree/master/tollboothic/_examples/limit-handler) |\r\n| [cloudwatch](https://github.com/iris-contrib/middleware/tree/master/cloudwatch) |  AWS cloudwatch metrics middleware |[iris-contrib/middleware/cloudwatch/_example](https://github.com/iris-contrib/middleware/tree/master/cloudwatch/_example) |\r\n| [new relic](https://github.com/iris-contrib/middleware/tree/master/newrelic) | Official [New Relic Go Agent](https://github.com/newrelic/go-agent) | [iris-contrib/middleware/newrelic/_example](https://github.com/iris-contrib/middleware/tree/master/newrelic/_example) |\r\n| [prometheus](https://github.com/iris-contrib/middleware/tree/master/prometheus)| Easily create metrics endpoint for the [prometheus](http://prometheus.io) instrumentation tool | [iris-contrib/middleware/prometheus/_example](https://github.com/iris-contrib/middleware/tree/master/prometheus/_example) |\r\n| [casbin](https://github.com/iris-contrib/middleware/tree/master/casbin)| An authorization library that supports access control models like ACL, RBAC, ABAC | [iris-contrib/middleware/casbin/_examples](https://github.com/iris-contrib/middleware/tree/master/casbin/_examples) |\r\n| [sentry-go (ex. raven)](https://github.com/getsentry/sentry-go/tree/master/iris)| Sentry client in Go | [sentry-go/example/iris](https://github.com/getsentry/sentry-go/blob/master/example/iris/main.go) | <!-- raven was deprecated by its company, the successor is sentry-go, they contain an Iris middleware. -->\r\n| [csrf](https://github.com/iris-contrib/middleware/tree/master/csrf)| Cross-Site Request Forgery Protection | [iris-contrib/middleware/csrf/_example](https://github.com/iris-contrib/middleware/blob/master/csrf/_example/main.go) |\r\n| [throttler](https://github.com/iris-contrib/middleware/tree/master/throttler)| Rate limiting access to HTTP endpoints | [iris-contrib/middleware/throttler/_example](https://github.com/iris-contrib/middleware/blob/master/throttler/_example/main.go) |\r\n\r\nThird-Party Handlers\r\n------------\r\n\r\nIris has its own middleware form of `func(ctx iris.Context)` but it's also compatible with all `net/http` middleware forms. See [here](https://github.com/kataras/iris/tree/main/_examples/convert-handlers).\r\n\r\nHere's a small list of useful third-party handlers:\r\n\r\n| Middleware | Description |\r\n| -----------|-------------|\r\n| [goth](https://github.com/markbates/goth) | OAuth, OAuth2 authentication. [Example](https://github.com/kataras/iris/tree/main/_examples/auth/goth) |\r\n| [permissions2](https://github.com/xyproto/permissions2) | Cookies, users and permissions. [Example](https://github.com/kataras/iris/tree/main/_examples/auth/permissions) |\r\n| [csp](https://github.com/awakenetworks/csp) | [Content Security Policy](https://www.w3.org/TR/CSP2/) (CSP) support |\r\n| [delay](https://github.com/jeffbmartinez/delay) | Add delays/latency to endpoints. Useful when testing effects of high latency |\r\n| [onthefly](https://github.com/xyproto/onthefly) | Generate TinySVG, HTML and CSS on the fly |\r\n| [RestGate](https://github.com/pjebs/restgate) | Secure authentication for REST API endpoints |\r\n| [stats](https://github.com/thoas/stats) | Store information about your web application (response time, etc.) |\r\n| [VanGoH](https://github.com/auroratechnologies/vangoh) | Configurable [AWS-Style](http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html) HMAC authentication middleware |\r\n| [digits](https://github.com/bamarni/digits) | Middleware that handles [Twitter Digits](https://get.digits.com/) authentication |\r\n\r\n> Feel free to put up your own middleware in this list!"
  },
  {
    "path": "middleware/accesslog/accesslog.go",
    "content": "package accesslog\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\tstdContext \"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httputil\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/core/host\"\n\t\"github.com/kataras/iris/v12/core/memstore\"\n)\n\nfunc init() {\n\tcontext.SetHandlerName(\"iris/middleware/accesslog.*\", \"iris.accesslog\")\n}\n\nconst (\n\tfieldsContextKey  = \"iris.accesslog.request.fields\"\n\tskipLogContextKey = \"iris.accesslog.request.skip\"\n)\n\n// GetFields returns the accesslog fields for this request.\n// Returns a store which the caller can use to\n// set/get/remove custom log fields. Use its `Set` method.\n//\n// To use with MVC: Register(accesslog.GetFields).\n// DI Handlers: ConfigureContainer().RegisterDependency(accesslog.GetFields).\nfunc GetFields(ctx *context.Context) (fields *Fields) {\n\tif v := ctx.Values().Get(fieldsContextKey); v != nil {\n\t\tfields = v.(*Fields)\n\t} else {\n\t\tfields = new(Fields)\n\t\tctx.Values().Set(fieldsContextKey, fields)\n\t}\n\n\treturn\n}\n\n// Skip called when a specific route should be skipped from the logging process.\n// It's an easy to use alternative for iris.NewConditionalHandler.\nfunc Skip(ctx *context.Context) {\n\tctx.Values().Set(skipLogContextKey, struct{}{})\n}\n\n// SkipHandler same as `Skip` but it can be used\n// as a middleware, it executes ctx.Next().\nfunc SkipHandler(ctx *context.Context) {\n\tSkip(ctx)\n\tctx.Next()\n}\n\nfunc shouldSkip(ctx *context.Context) bool {\n\treturn ctx.Values().Get(skipLogContextKey) != nil\n}\n\ntype (\n\t// Fields is a type alias for memstore.Store, used to set\n\t// more than one field at serve-time. Same as FieldExtractor.\n\tFields = memstore.Store\n\t// FieldSetter sets one or more fields at once.\n\tFieldSetter func(*context.Context, *Fields)\n)\n\ntype (\n\t// Clock is an interface which contains a single `Now` method.\n\t// It can be used to set a static timer on end to end testing.\n\t// See `AccessLog.Clock` field.\n\tClock     interface{ Now() time.Time }\n\tclockFunc func() time.Time\n)\n\n// Now completes the `Clock` interface.\nfunc (c clockFunc) Now() time.Time {\n\treturn c()\n}\n\nvar (\n\t// UTC returns time with UTC based location.\n\tUTC = clockFunc(func() time.Time { return time.Now().UTC() })\n\t// TClock accepts a static time.Time to use as\n\t// accesslog's Now method on current log fired timestamp.\n\t// Useful for testing.\n\tTClock = func(t time.Time) clockFunc { return func() time.Time { return t } }\n)\n\n// AccessLog is a middleware which prints information\n// incoming HTTP requests.\n//\n// Default log format:\n// Time|Latency|Code|Method|Path|IP|Path Params Query Fields|Bytes Received|Bytes Sent|Request|Response|\n//\n// Look `New`, `File` package-level functions\n// and its `Handler` method to learn more.\n// If the given writer is a buffered one,\n// its contents are flushed automatically on Close call.\n//\n// A new AccessLog middleware MUST\n// be created after a `New` function call.\ntype AccessLog struct {\n\tmu sync.RWMutex // ensures atomic writes.\n\t// The destination writer.\n\t// If multiple output required, then define an `io.MultiWriter`.\n\t// See `SetOutput` and `AddOutput` methods too.\n\tWriter io.Writer\n\n\t// If not empty then each one of them is called on `Close` method.\n\t// File type destinations are automatically added.\n\tFlushers []Flusher\n\tClosers  []io.Closer\n\t// Outputs that support the Truncate method.\n\tBufferTruncaters []BufferTruncater\n\tFileTruncaters   []FileTruncater\n\n\t// If not empty then overrides the time.Now to this custom clocker's `Now` method,\n\t// useful for testing (see `TClock`) and\n\t// on platforms that its internal clock is not compatible by default (advanced case) and\n\t// to change the time location (e.g. `UTC`).\n\t//\n\t// This field is used to set the time the log fired.\n\t// By default the middleware is using the local time, however\n\t// can be changed to `UTC` too.\n\t//\n\t// Do NOT touch this field if you don't know what you're doing.\n\tClock Clock\n\n\t// If true then the middleware will fire the logs in a separate\n\t// go routine, making the request to finish first.\n\t// The log will be printed based on a copy of the Request's Context instead.\n\t//\n\t// Defaults to false.\n\tAsync bool\n\t// The delimiter between fields when logging with the default format.\n\t// See `SetFormatter` to customize the log even further.\n\t//\n\t// Defaults to '|'.\n\tDelim byte\n\t// The time format for current time on log print.\n\t// Set it to empty to inherit the Iris Application's TimeFormat.\n\t//\n\t// Defaults to \"2006-01-02 15:04:05\"\n\tTimeFormat string\n\t// A text that will appear in a blank value.\n\t// Applies to the default formatter on\n\t// IP, RequestBody and ResponseBody fields, if enabled, so far.\n\t//\n\t// Defaults to nil.\n\tBlank []byte\n\t// Round the latency based on the given duration, e.g. time.Second.\n\t//\n\t// Defaults to 0.\n\tLatencyRound time.Duration\n\n\t// IP displays the remote address.\n\t//\n\t// Defaults to true.\n\tIP bool\n\t// The number of bytes for the request body only.\n\t// Applied when BytesReceived is false.\n\t//\n\t// Defaults to true.\n\tBytesReceivedBody bool\n\t// The number of bytes for the response body only.\n\t// Applied when BytesSent is false.\n\t//\n\t// Defaults to true.\n\tBytesSentBody bool\n\t// The actual number of bytes received and sent on the network (headers + body).\n\t// It is kind of \"slow\" operation as it uses the httputil to dumb request\n\t// and response to get the total amount of bytes (headers + body).\n\t//\n\t// They override the BytesReceivedBody and BytesSentBody fields.\n\t// These two fields provide a more a acquirate measurement\n\t// than BytesReceivedBody and BytesSentBody however,\n\t// they are expensive operations, expect a slower execution.\n\t//\n\t// They both default to false.\n\tBytesReceived bool\n\tBytesSent     bool\n\t// Enable request body logging.\n\t// Note that, if this is true then it modifies the underline request's body type.\n\t//\n\t// Defaults to true.\n\tRequestBody bool\n\t// Enable response body logging.\n\t// Note that, if this is true then it uses a response recorder.\n\t//\n\t// Defaults to false.\n\tResponseBody bool\n\t// Force minify request and response contents.\n\t//\n\t// Defaults to true.\n\tBodyMinify bool\n\n\t// KeepMultiLineError displays the Context's error as it's.\n\t// If set to false then it replaces all line characters with spaces.\n\t//\n\t// See `PanicLog` to customize recovered-from-panic errors even further.\n\t//\n\t// Defaults to true.\n\tKeepMultiLineError bool\n\t// What the logger should write to the output destination\n\t// when recovered from a panic.\n\t// Available options:\n\t// * LogHandler (default, logs the handler's file:line only)\n\t// * LogCallers (logs callers separated by line breaker)\n\t// * LogStack   (logs the debug stack)\n\tPanicLog PanicLog\n\n\t// Map log fields with custom request values.\n\t// See `AddFields` method.\n\tFieldSetters []FieldSetter\n\t// Note: We could use a map but that way we lose the\n\t// order of registration so use a slice and\n\t// take the field key from the extractor itself.\n\tformatter Formatter\n\tbroker    *Broker\n\n\t// the log instance for custom formatters.\n\tlogsPool *sync.Pool\n\t// the builder for the default format.\n\tbufPool *sync.Pool\n\t// remaining logs when Close is called, we wait for timeout (see CloseContext).\n\tremaining uint32\n\t// reports whether the logger is already closed, see `Close` & `CloseContext` methods.\n\tisClosed uint32\n}\n\n// PanicLog holds the type for the available panic log levels.\ntype PanicLog uint8\n\nconst (\n\t// LogHandler logs the handler's file:line that recovered from.\n\tLogHandler PanicLog = iota\n\t// LogCallers logs all callers separated by new lines.\n\tLogCallers\n\t// LogStack logs the whole debug stack.\n\tLogStack\n)\n\nconst (\n\tdefaultDelim      = '|'\n\tdefaultTimeFormat = \"2006-01-02 15:04:05\"\n\tnewLine           = '\\n'\n)\n\n// New returns a new AccessLog value with the default values.\n// Writes to the \"w\". Output can be further modified through its `Set/AddOutput` methods.\n//\n// Register by its `Handler` method.\n// See `File` package-level function too.\n//\n// Examples:\n// https://github.com/kataras/iris/tree/main/_examples/logging/request-logger/accesslog\n// https://github.com/kataras/iris/tree/main/_examples/logging/request-logger/accesslog-template\n// https://github.com/kataras/iris/tree/main/_examples/logging/request-logger/accesslog-broker\nfunc New(w io.Writer) *AccessLog {\n\tac := &AccessLog{\n\t\tClock:              clockFunc(time.Now),\n\t\tDelim:              defaultDelim,\n\t\tTimeFormat:         defaultTimeFormat,\n\t\tBlank:              nil,\n\t\tLatencyRound:       0,\n\t\tAsync:              false,\n\t\tIP:                 true,\n\t\tBytesReceived:      false,\n\t\tBytesSent:          false,\n\t\tBytesReceivedBody:  true,\n\t\tBytesSentBody:      true,\n\t\tRequestBody:        true,\n\t\tResponseBody:       false,\n\t\tBodyMinify:         true,\n\t\tKeepMultiLineError: true,\n\t\tPanicLog:           LogHandler,\n\t\tlogsPool: &sync.Pool{New: func() any {\n\t\t\treturn new(Log)\n\t\t}},\n\t\tbufPool: &sync.Pool{New: func() any {\n\t\t\treturn new(bytes.Buffer)\n\t\t}},\n\t}\n\n\tif w == nil {\n\t\tw = os.Stdout\n\t}\n\tac.SetOutput(w)\n\n\t// workers := 20\n\t// listener := ac.Broker().NewListener()\n\t// for i := 0; i < workers; i++ {\n\t// \tgo func() {\n\t// \t\tfor log := range listener {\n\t// \t\t\tatomic.AddUint32(&ac.remaining, 1)\n\t// \t\t\tac.handleLog(log)\n\t// \t\t\tatomic.AddUint32(&ac.remaining, ^uint32(0))\n\t// \t\t}\n\t// \t}()\n\t// }\n\n\thost.RegisterOnInterrupt(func() {\n\t\tac.Close()\n\t})\n\n\treturn ac\n}\n\n// File returns a new AccessLog value with the given \"path\"\n// as the log's output file destination.\n// The Writer is now a buffered file writer & reader.\n// Register by its `Handler` method.\n//\n// A call of its `Close` method to unlock the underline\n// file is required on program termination.\n//\n// It panics on error.\nfunc File(path string) *AccessLog {\n\tf := mustOpenFile(path)\n\treturn New(bufio.NewReadWriter(bufio.NewReader(f), bufio.NewWriter(f)))\n}\n\n// FileUnbuffered same as File but it does not buffer the data,\n// it flushes the loggers contents as soon as possible.\nfunc FileUnbuffered(path string) *AccessLog {\n\tf := mustOpenFile(path)\n\treturn New(f)\n}\n\nfunc mustOpenFile(path string) *os.File {\n\t// Note: we add os.RDWR in order to be able to read from it,\n\t// some formatters (e.g. CSV) needs that.\n\tf, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treturn f\n}\n\n// Broker creates or returns the broker.\n// Use its `NewListener` and `CloseListener`\n// to listen and unlisten for incoming logs.\n//\n// Should be called before serve-time.\nfunc (ac *AccessLog) Broker() *Broker {\n\tac.mu.Lock()\n\tif ac.broker == nil {\n\t\tac.broker = newBroker()\n\t}\n\tac.mu.Unlock()\n\n\treturn ac.broker\n}\n\n// SetOutput sets the log's output destination. Accepts one or more io.Writer values.\n// Also, if a writer is a Closer, then it is automatically appended to the Closers.\n// It's safe to used concurrently (experimental).\nfunc (ac *AccessLog) SetOutput(writers ...io.Writer) *AccessLog {\n\tac.setOutput(true, writers...)\n\treturn ac\n}\n\n// AddOutput appends an io.Writer value to the existing writer.\n// Call it before `SetFormatter` and `Handler` methods.\nfunc (ac *AccessLog) AddOutput(writers ...io.Writer) *AccessLog {\n\tac.setOutput(false, writers...)\n\treturn ac\n}\n\nfunc (ac *AccessLog) setOutput(reset bool, writers ...io.Writer) {\n\n\t/*\n\t   Initial idea was to wait for remaining logs to be written\n\t   in the existing writer before resetting to the new one.\n\t   But, a faster approach would be to just write the logs\n\t   to the new writers instead. This can be done by:\n\t   1. copy all existing closers and flushers,\n\t   2. change the writer immediately\n\t   3. fire a goroutine which flushes and closes the old writers,\n\t       no locks required there because they are not used for concurrent writing\n\t       anymore. Errors there are ignored (we could collect them with sync errgroup\n\t       and wait for them before exit this Reset method, but we don't).\n\t*/\n\n\tif !reset {\n\t\t// prepend if one exists.\n\t\tac.mu.Lock()\n\t\tif ac.Writer != nil {\n\t\t\twriters = append([]io.Writer{ac.Writer}, writers...)\n\t\t}\n\t\tac.mu.Unlock()\n\t}\n\n\tswitch len(writers) {\n\tcase 0:\n\t\treturn\n\tcase 1:\n\t\tac.mu.Lock()\n\t\tac.Writer = writers[0]\n\t\tac.mu.Unlock()\n\tdefault:\n\t\tmulti := io.MultiWriter(writers...)\n\t\tac.mu.Lock()\n\t\tac.Writer = multi\n\t\tac.mu.Unlock()\n\t}\n\n\t// NO need to check for a \"hadWriter\",\n\t// because it will always have a previous writer\n\t// on serve-time (the spot we care about performance),\n\t// so if it set by New, on build-time, we don't rly care about some read locks slowdown.\n\tac.mu.RLock()\n\tn := len(ac.Flushers)\n\tac.mu.RUnlock()\n\n\tflushers := make([]Flusher, n)\n\tif n > 0 {\n\t\tac.mu.Lock()\n\t\tcopy(flushers, ac.Flushers)\n\t\tac.mu.Unlock()\n\t}\n\n\tac.mu.RLock()\n\tn = len(ac.Closers)\n\tac.mu.RUnlock()\n\n\tclosers := make([]io.Closer, n)\n\tif n > 0 {\n\t\tac.mu.Lock()\n\t\tcopy(closers, ac.Closers)\n\t\tac.mu.Unlock()\n\t}\n\n\tif reset {\n\t\t// Reset previous flushers and closers,\n\t\t// so any middle request can't flush to the old ones.\n\t\t// Note that, because we don't lock the whole operation,\n\t\t// there is a chance of Flush while we are doing this,\n\t\t// not by the middleware (unless panic, but again, the data are written\n\t\t// to the new writer, they are not lost, just not flushed immediately),\n\t\t// an outsider may call it, and if it does\n\t\t// then it is its responsibility to lock between manual Flush calls and\n\t\t// SetOutput ones. This is done to be able\n\t\t// to serve requests fast even on Async == false\n\t\t// while SetOutput is called at serve-time, if we didn't care about it\n\t\t// we could lock the whole operation which would make the\n\t\t// log writers to wait and be done with this.\n\t\tac.mu.Lock()\n\t\tac.Flushers = ac.Flushers[0:0]\n\t\tac.Closers = ac.Closers[0:0]\n\t\tac.BufferTruncaters = ac.BufferTruncaters[0:0]\n\t\tac.FileTruncaters = ac.FileTruncaters[0:0]\n\t\tac.mu.Unlock()\n\t}\n\n\t// Store the new flushers, closers and truncaters...\n\tfor _, w := range writers {\n\t\tif flusher, ok := w.(Flusher); ok {\n\t\t\tac.mu.Lock()\n\t\t\tac.Flushers = append(ac.Flushers, flusher)\n\t\t\tac.mu.Unlock()\n\t\t}\n\n\t\tif closer, ok := w.(io.Closer); ok {\n\t\t\tac.mu.Lock()\n\t\t\tac.Closers = append(ac.Closers, closer)\n\t\t\tac.mu.Unlock()\n\t\t}\n\n\t\tif truncater, ok := w.(BufferTruncater); ok {\n\t\t\tac.mu.Lock()\n\t\t\tac.BufferTruncaters = append(ac.BufferTruncaters, truncater)\n\t\t\tac.mu.Unlock()\n\t\t}\n\n\t\tif truncater, ok := w.(FileTruncater); ok {\n\t\t\tac.mu.Lock()\n\t\t\tac.FileTruncaters = append(ac.FileTruncaters, truncater)\n\t\t\tac.mu.Unlock()\n\t\t}\n\t}\n\n\tif reset {\n\t\t// And finally, wait before exit this method\n\t\t// until previous writer's closers and flush finish.\n\t\tfor _, flusher := range flushers {\n\t\t\tif flusher != nil {\n\t\t\t\tflusher.Flush()\n\t\t\t}\n\t\t}\n\t\tfor _, closer := range closers {\n\t\t\tif closer != nil {\n\t\t\t\t// cannot close os.Stdout/os.Stderr\n\t\t\t\tif closer == os.Stdout || closer == os.Stderr {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tcloser.Close()\n\t\t\t}\n\t\t}\n\t}\n}\n\n// Close terminates any broker listeners,\n// waits for any remaining logs up to 10 seconds\n// (see `CloseContext` to set custom deadline),\n// flushes any formatter and any buffered data to the underline writer\n// and finally closes any registered closers (files are automatically added as Closer).\n//\n// After Close is called the AccessLog is not accessible.\nfunc (ac *AccessLog) Close() (err error) {\n\tctx, cancelFunc := stdContext.WithTimeout(stdContext.Background(), 10*time.Second)\n\tdefer cancelFunc()\n\n\treturn ac.CloseContext(ctx)\n}\n\n// CloseContext same as `Close` but waits until given \"ctx\" is done.\nfunc (ac *AccessLog) CloseContext(ctx stdContext.Context) (err error) {\n\tif !atomic.CompareAndSwapUint32(&ac.isClosed, 0, 1) {\n\t\treturn\n\t}\n\n\tif ac.broker != nil {\n\t\tac.broker.close <- struct{}{}\n\t}\n\n\tif ac.Async {\n\t\tac.waitRemaining(ctx)\n\t}\n\n\tif fErr := ac.Flush(); fErr != nil {\n\t\tif err == nil {\n\t\t\terr = fErr\n\t\t} else {\n\t\t\terr = fmt.Errorf(\"%v, %v\", err, fErr)\n\t\t}\n\t}\n\n\tfor _, closer := range ac.Closers {\n\t\tcErr := closer.Close()\n\t\tif cErr != nil {\n\t\t\tif err == nil {\n\t\t\t\terr = cErr\n\t\t\t} else {\n\t\t\t\terr = fmt.Errorf(\"%v, %v\", err, cErr)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn\n}\n\nfunc (ac *AccessLog) waitRemaining(ctx stdContext.Context) {\n\tif n := atomic.LoadUint32(&ac.remaining); n == 0 {\n\t\treturn\n\t}\n\n\tt := time.NewTicker(2 * time.Second)\n\tdefer t.Stop()\n\tfor {\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\treturn\n\t\tcase <-t.C:\n\t\t\tif atomic.LoadUint32(&ac.remaining) == 0 {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n}\n\n// func (ac *AccessLog) isBrokerActive() bool { // see `Print` method.\n// \treturn atomic.LoadUint32(&ac.brokerActive) > 0\n// }\n// ^ No need, we declare that the Broker should be called\n// before serve-time. Let's respect our comment\n// and don't try to make it safe for write and read concurrent access.\n\n// Write writes to the log destination.\n// It completes the io.Writer interface.\n// Safe for concurrent use.\nfunc (ac *AccessLog) Write(p []byte) (n int, err error) {\n\tif ac.Async {\n\t\tif atomic.LoadUint32(&ac.isClosed) > 0 {\n\t\t\treturn 0, io.ErrClosedPipe\n\t\t}\n\t}\n\n\tac.mu.Lock()\n\tn, err = ac.Writer.Write(p)\n\tac.mu.Unlock()\n\treturn\n}\n\n// Flush writes any buffered data to the underlying Fluser Writer.\n// Flush is called automatically on Close.\nfunc (ac *AccessLog) Flush() (err error) {\n\tac.mu.Lock()\n\tfor _, f := range ac.Flushers {\n\t\tfErr := f.Flush()\n\t\tif fErr != nil {\n\t\t\tif err == nil {\n\t\t\t\terr = fErr\n\t\t\t} else {\n\t\t\t\terr = fmt.Errorf(\"%v, %v\", err, fErr)\n\t\t\t}\n\t\t}\n\t}\n\tac.mu.Unlock()\n\treturn\n}\n\n// Truncate if the output is a buffer, then\n// it discards all but the first n unread bytes.\n// See `TruncateFile` for a file size.\n//\n// It panics if n is negative or greater than the length of the buffer.\nfunc (ac *AccessLog) Truncate(n int) {\n\tac.mu.Lock() // Lock as we do with all write operations.\n\tfor _, truncater := range ac.BufferTruncaters {\n\t\ttruncater.Truncate(n)\n\t}\n\tac.mu.Unlock()\n}\n\n// TruncateFile flushes any buffered contents\n// and changes the size of the internal file destination, directly.\n// It does not change the I/O offset.\n//\n// Note that `TruncateFile` calls the `Truncate(int(size))` automatically\n// in order to clear any buffered contents (if the file was wrapped by a buffer)\n// before truncating the file itself.\n//\n// Usage, clear a file:\n// err := TruncateFile(0)\nfunc (ac *AccessLog) TruncateFile(size int64) (err error) {\n\tac.Truncate(int(size))\n\n\tac.mu.Lock()\n\tfor _, truncater := range ac.FileTruncaters {\n\t\ttErr := truncater.Truncate(size)\n\t\tif tErr != nil {\n\t\t\tif err == nil {\n\t\t\t\terr = tErr\n\t\t\t} else {\n\t\t\t\terr = fmt.Errorf(\"%v, %v\", err, tErr)\n\t\t\t}\n\t\t}\n\t}\n\tac.mu.Unlock()\n\n\treturn err\n}\n\n// SetFormatter sets a custom formatter to print the logs.\n// Any custom output writers should be\n// already registered before calling this method.\n// Returns this AccessLog instance.\n//\n// Usage:\n// ac.SetFormatter(&accesslog.JSON{Indent: \"    \"})\nfunc (ac *AccessLog) SetFormatter(f Formatter) *AccessLog {\n\tif ac.Writer == nil {\n\t\tpanic(\"accesslog: SetFormatter called with nil Writer\")\n\t}\n\n\tif f == nil {\n\t\treturn ac\n\t}\n\n\tif flusher, ok := ac.formatter.(Flusher); ok {\n\t\t// PREPEND formatter flushes, they should run before destination's ones.\n\t\tac.Flushers = append([]Flusher{flusher}, ac.Flushers...)\n\t}\n\n\t// Inject the writer (AccessLog) here, the writer\n\t// is protected with mutex.\n\tf.SetOutput(ac)\n\n\tac.formatter = f\n\treturn ac\n}\n\n// AddFields maps one or more log entries with values extracted by the Request Context.\n// You can also add fields per request handler, look the `GetFields` package-level function.\n// Note that this method can override a key stored by a handler's fields.\nfunc (ac *AccessLog) AddFields(setters ...FieldSetter) *AccessLog {\n\tac.FieldSetters = append(ac.FieldSetters, setters...)\n\treturn ac\n}\n\nfunc (ac *AccessLog) shouldReadRequestBody() bool {\n\treturn ac.RequestBody || ac.BytesReceived || ac.BytesReceivedBody\n\n}\n\nfunc (ac *AccessLog) shouldReadResponseBody() bool {\n\treturn ac.ResponseBody || ac.BytesSent /* || ac.BytesSentBody this can be measured by the default writer's Written() */\n}\n\n// Handler prints request information to the output destination.\n// It is the main method of the AccessLog middleware.\n//\n// Usage:\n// ac := New(io.Writer) or File(\"access.log\")\n// defer ac.Close()\n// app.UseRouter(ac.Handler)\nfunc (ac *AccessLog) Handler(ctx *context.Context) {\n\tif shouldSkip(ctx) { // usage: another middleware before that one disables logging.\n\t\tctx.Next()\n\t\treturn\n\t}\n\n\tvar (\n\t\tstartTime = time.Now()\n\t\t// Store some values, as future handler chain\n\t\t// can modify those (note: we could clone the request or context object too).\n\t\tmethod = ctx.Method()\n\t\tpath   = ctx.Path()\n\t)\n\n\t// Enable response recording.\n\tif ac.shouldReadResponseBody() {\n\t\tctx.Record()\n\t}\n\t// Enable reading the request body\n\t// multiple times (route handler and this middleware).\n\tif ac.shouldReadRequestBody() {\n\t\tctx.RecordRequestBody(true)\n\t}\n\n\t// Set the fields context value so they can be modified\n\t// on the following handlers chain. Same as `AddFields` but per-request.\n\t// ctx.Values().Set(fieldsContextKey, new(Fields))\n\t// No need ^ The GetFields will set it if it's missing.\n\t// So we initialize them whenever, and if, asked.\n\n\t// Proceed to the handlers chain.\n\tcurrentIndex := ctx.HandlerIndex(-1)\n\tctx.Next()\n\tif context.StatusCodeNotSuccessful(ctx.GetStatusCode()) {\n\t\t_, wasRecovered := ctx.IsRecovered()\n\t\t// The ctx.HandlerName is still accesslog because\n\t\t// on end of router filters the router resets\n\t\t// the handler index, same for errors.\n\t\t// So, as a special case, if it's a failure status code\n\t\t// call FireErorrCode manually instead of wait\n\t\t// to be called on EndRequest (which is, correctly, called on end of everything\n\t\t// so we don't have chance to record its body by default).\n\t\t//\n\t\t// Note: this however will call the error handler twice\n\t\t// if the end-developer registered that using `UseError` instead of `UseRouter`,\n\t\t// there is a way to fix that too: by checking the handler index diff:\n\t\tif currentIndex == ctx.HandlerIndex(-1) || wasRecovered {\n\t\t\t// if handler index before and after ctx.Next\n\t\t\t// is the same, then it means we are in `UseRouter`\n\t\t\t// and on error handler.\n\t\t\tctx.Application().FireErrorCode(ctx)\n\t\t}\n\t}\n\n\tif shouldSkip(ctx) { // normal flow, we can get the context by executing the handler first.\n\t\treturn\n\t}\n\n\tlatency := time.Since(startTime).Round(ac.LatencyRound)\n\n\tif ac.Async {\n\t\tctxCopy := ctx.Clone()\n\t\tgo ac.after(ctxCopy, latency, method, path)\n\t} else {\n\t\t// wait to finish before proceed with response end.\n\t\tac.after(ctx, latency, method, path)\n\t}\n}\n\nfunc (ac *AccessLog) after(ctx *context.Context, lat time.Duration, method, path string) {\n\tvar (\n\t\t// request and response data or error reading them.\n\t\trequestBody   string\n\t\tresponseBody  string\n\t\tbytesReceived int // total or body, depends on the configuration.\n\t\tbytesSent     int\n\t)\n\n\tif ac.shouldReadRequestBody() {\n\t\t//\tany error handler stored ( ctx.SetErr or StopWith(Plain)Error )\n\t\tif ctxErr := ctx.GetErr(); ctxErr != nil {\n\t\t\t// If there is an error here\n\t\t\t// we may need to NOT read the body for security reasons, e.g.\n\t\t\t// unauthorized user tries to send a malicious body.\n\t\t\trequestBody = ac.getErrorText(ctxErr)\n\t\t} else {\n\t\t\trequestData, err := ctx.GetBody()\n\t\t\trequestBodyLength := len(requestData)\n\t\t\tif ac.BytesReceivedBody {\n\t\t\t\tbytesReceived = requestBodyLength // store it, if the total is enabled then this will be overridden.\n\t\t\t}\n\t\t\tif err != nil && ac.RequestBody {\n\t\t\t\tif err != http.ErrBodyReadAfterClose { // if body was already closed, don't send it as error.\n\t\t\t\t\trequestBody = ac.getErrorText(err)\n\t\t\t\t}\n\t\t\t} else if requestBodyLength > 0 {\n\t\t\t\tif ac.RequestBody {\n\t\t\t\t\tif ac.BodyMinify {\n\t\t\t\t\t\tif minified, err := ctx.Application().Minifier().Bytes(ctx.GetContentTypeRequested(), requestData); err == nil {\n\t\t\t\t\t\t\trequestBody = string(minified)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t/* Some content types, like the text/plain,\n\t\t\t\t\t   no need minifier. Should be printed with spaces and \\n. */\n\t\t\t\t\tif requestBody == \"\" {\n\t\t\t\t\t\trequestBody = string(requestData)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ac.BytesReceived {\n\t\t\t\t// Unfortunally the DumpRequest cannot read the body\n\t\t\t\t// length as expected (see postman's i/o values)\n\t\t\t\t// so we had to read the data length manually even if RequestBody/ResponseBody\n\t\t\t\t// are false, extra operation if they are enabled is to minify their log entry representation.\n\n\t\t\t\tb, _ := httputil.DumpRequest(ctx.Request(), false)\n\t\t\t\tbytesReceived = len(b) + requestBodyLength\n\t\t\t}\n\t\t}\n\t}\n\n\tif ac.shouldReadResponseBody() {\n\t\tactualResponseData := ctx.Recorder().Body()\n\t\tresponseBodyLength := len(actualResponseData)\n\n\t\tif ac.BytesSentBody {\n\t\t\tbytesSent = responseBodyLength\n\t\t}\n\t\tif ac.ResponseBody && responseBodyLength > 0 {\n\t\t\tif ac.BodyMinify {\n\t\t\t\t// Copy response data as minifier now can change the back slice,\n\t\t\t\t// fixes: https://github.com/kataras/iris-premium/issues/17.\n\t\t\t\tresponseData := make([]byte, len(actualResponseData))\n\t\t\t\tcopy(responseData, actualResponseData)\n\n\t\t\t\tif minified, err := ctx.Application().Minifier().Bytes(ctx.GetContentType(), responseData); err == nil {\n\t\t\t\t\tresponseBody = string(minified)\n\t\t\t\t\tresponseBodyLength = len(responseBody)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif responseBody == \"\" {\n\t\t\t\tresponseBody = string(actualResponseData)\n\t\t\t}\n\t\t}\n\n\t\tif ac.BytesSent {\n\t\t\tresp := ctx.Recorder().Result()\n\t\t\tb, _ := httputil.DumpResponse(resp, false)\n\t\t\tdateLengthProx := 38 /* it's actually ~37 */\n\t\t\tif resp.Header.Get(\"Date\") != \"\" {\n\t\t\t\tdateLengthProx = 0 // dump response calculated it.\n\t\t\t}\n\t\t\tbytesSent = len(b) + responseBodyLength + dateLengthProx\n\t\t}\n\t} else if ac.BytesSentBody {\n\t\tbytesSent = ctx.ResponseWriter().Written()\n\t}\n\n\t// Grab any custom fields.\n\tfields := GetFields(ctx)\n\n\tfor _, setter := range ac.FieldSetters {\n\t\tsetter(ctx, fields)\n\t}\n\n\ttimeFormat := ac.TimeFormat\n\tif timeFormat == \"\" {\n\t\ttimeFormat = ctx.Application().ConfigurationReadOnly().GetTimeFormat()\n\t}\n\n\tip := \"\"\n\tif ac.IP {\n\t\tip = ctx.RemoteAddr()\n\t}\n\n\tif err := ac.Print(ctx,\n\t\t// latency between begin and finish of the handlers chain.\n\t\tlat,\n\t\ttimeFormat,\n\t\t// response code.\n\t\tctx.GetStatusCode(),\n\t\t// original request's method and path.\n\t\tmethod, path,\n\t\t// remote ip.\n\t\tip,\n\t\trequestBody, responseBody,\n\t\tbytesReceived, bytesSent,\n\t\tctx.Params().Store, ctx.URLParamsSorted(), *fields,\n\t); err != nil {\n\t\tctx.Application().Logger().Errorf(\"accesslog: %v\", err)\n\t}\n}\n\n// Print writes a log manually.\n// The `Handler` method calls it.\nfunc (ac *AccessLog) Print(ctx *context.Context,\n\tlatency time.Duration,\n\ttimeFormat string,\n\tcode int,\n\tmethod, path, ip, reqBody, respBody string,\n\tbytesReceived, bytesSent int,\n\tparams memstore.Store, query []memstore.StringEntry, fields []memstore.Entry) (err error) {\n\n\tif ac.Async {\n\t\t// atomic.AddUint32(&ac.remaining, 1)\n\t\t// This could work ^\n\t\t// but to make sure we have the correct number of increments.\n\t\t// CAS loop:\n\t\tfor {\n\t\t\tcur := atomic.LoadUint32(&ac.remaining)\n\t\t\tif atomic.CompareAndSwapUint32(&ac.remaining, cur, cur+1) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tdefer atomic.AddUint32(&ac.remaining, ^uint32(0))\n\t}\n\n\tnow := ac.Clock.Now()\n\n\tif hasFormatter, hasBroker := ac.formatter != nil, ac.broker != nil; hasFormatter || hasBroker {\n\t\tlog := ac.logsPool.Get().(*Log)\n\t\tlog.Logger = ac\n\t\tlog.Now = now\n\t\tlog.TimeFormat = timeFormat\n\t\tlog.Timestamp = now.UnixNano() / 1000000\n\t\tlog.Latency = latency\n\t\tlog.Code = code\n\t\tlog.Method = method\n\t\tlog.Path = path\n\t\tlog.IP = ip\n\t\tlog.Query = query\n\t\tlog.PathParams = params\n\t\tlog.Fields = fields\n\t\tlog.Request = reqBody\n\t\tlog.Response = respBody\n\t\tlog.BytesReceived = bytesReceived\n\t\tlog.BytesSent = bytesSent\n\t\tlog.Ctx = ctx\n\n\t\tvar handled bool\n\t\tif hasFormatter {\n\t\t\thandled, err = ac.formatter.Format(log) // formatter can alter this, we wait until it's finished.\n\t\t\tif err != nil {\n\t\t\t\tac.logsPool.Put(log)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tif hasBroker { // after Format, it may want to customize the log's fields.\n\t\t\tac.broker.notify(log.Clone()) // a listener cannot edit the log as we use object pooling.\n\t\t}\n\n\t\tac.logsPool.Put(log) // we don't need it anymore.\n\t\tif handled {\n\t\t\treturn // OK, it's handled, exit now.\n\t\t}\n\t}\n\n\t// the number of separators is the same, in order to be easier\n\t// for 3rd-party programs to read the result log file.\n\tbuilder := ac.bufPool.Get().(*bytes.Buffer)\n\n\tbuilder.WriteString(now.Format(timeFormat))\n\tbuilder.WriteByte(ac.Delim)\n\n\tbuilder.WriteString(latency.String())\n\tbuilder.WriteByte(ac.Delim)\n\n\tbuilder.WriteString(strconv.Itoa(code))\n\tbuilder.WriteByte(ac.Delim)\n\n\tbuilder.WriteString(method)\n\tbuilder.WriteByte(ac.Delim)\n\n\tbuilder.WriteString(path)\n\tbuilder.WriteByte(ac.Delim)\n\n\tif ac.IP {\n\t\tac.writeText(builder, ip)\n\t\tbuilder.WriteByte(ac.Delim)\n\t}\n\n\t// url parameters, path parameters and custom fields separated by space,\n\t// key=value key2=value2.\n\tif n, all := parseRequestValues(builder, code, params, query, fields); n > 0 {\n\t\tbuilder.Truncate(all - 1) // remove the last space.\n\t\tbuilder.WriteByte(ac.Delim)\n\t}\n\n\tif ac.BytesReceived || ac.BytesReceivedBody {\n\t\tbuilder.WriteString(formatBytes(bytesReceived))\n\t\tbuilder.WriteByte(ac.Delim)\n\t}\n\n\tif ac.BytesSent || ac.BytesSentBody {\n\t\tbuilder.WriteString(formatBytes(bytesSent))\n\t\tbuilder.WriteByte(ac.Delim)\n\t}\n\n\tif ac.RequestBody {\n\t\tac.writeText(builder, reqBody)\n\t\tbuilder.WriteByte(ac.Delim)\n\t}\n\n\tif ac.ResponseBody {\n\t\tac.writeText(builder, respBody)\n\t\tbuilder.WriteByte(ac.Delim)\n\t}\n\n\tbuilder.WriteByte(newLine)\n\n\t_, err = ac.Write(builder.Bytes())\n\tbuilder.Reset()\n\tac.bufPool.Put(builder)\n\n\treturn\n}\n\n// We could have a map of blanks per field,\n// but let's don't coplicate things so much\n// as the end-developer can use a custom template.\nfunc (ac *AccessLog) writeText(buf *bytes.Buffer, s string) {\n\tif s == \"\" {\n\t\tif len(ac.Blank) == 0 {\n\t\t\treturn\n\t\t}\n\t\tbuf.Write(ac.Blank)\n\t} else {\n\t\tbuf.WriteString(s)\n\t}\n}\n\nvar lineBreaksReplacer = strings.NewReplacer(\"\\n\\r\", \" \", \"\\n\", \" \")\n\nfunc (ac *AccessLog) getErrorText(err error) (text string) { // caller checks for nil.\n\tif errPanic, ok := context.IsErrPanicRecovery(err); ok {\n\t\tac.Flush() // flush any buffered contents to be written to the output.\n\n\t\tswitch ac.PanicLog {\n\t\tcase LogHandler:\n\t\t\ttext = errPanic.CurrentHandlerFileLine\n\t\tcase LogCallers:\n\t\t\ttext = strings.Join(errPanic.Callers, \"\\n\")\n\t\tcase LogStack:\n\t\t\ttext = string(errPanic.Stack)\n\t\t}\n\n\t\ttext = fmt.Sprintf(\"error(%v %s)\", errPanic.Cause, text)\n\t} else {\n\t\ttext = fmt.Sprintf(\"error(%s)\", err.Error())\n\t}\n\n\tif !ac.KeepMultiLineError {\n\t\treturn lineBreaksReplacer.Replace(text)\n\t}\n\n\treturn text\n}\n"
  },
  {
    "path": "middleware/accesslog/accesslog_test.go",
    "content": "package accesslog\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"strings\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/core/memstore\"\n)\n\nfunc TestAccessLogPrint_Simple(t *testing.T) {\n\tconst goroutinesN = 42\n\n\tw := new(bytes.Buffer)\n\tac := New(w)\n\tac.Async = true\n\tac.ResponseBody = true\n\tac.Clock = TClock(time.Time{})\n\n\tvar (\n\t\texpected      string\n\t\texpectedLines int\n\t\tmu            sync.Mutex\n\t)\n\n\tnow := time.Now()\n\tfor i := 0; i < goroutinesN; i++ {\n\t\tgo func() {\n\t\t\terr := ac.Print(\n\t\t\t\tnil,\n\t\t\t\t1*time.Second,\n\t\t\t\tac.TimeFormat,\n\t\t\t\t200,\n\t\t\t\t\"GET\",\n\t\t\t\t\"/path_value?url_query=url_query_value\",\n\t\t\t\t\"::1\",\n\t\t\t\t\"Incoming\",\n\t\t\t\t\"Outcoming\",\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t\tmemstore.Store{\n\t\t\t\t\t{Key: \"path_param\", ValueRaw: \"path_param_value\"},\n\t\t\t\t},\n\t\t\t\t[]memstore.StringEntry{\n\t\t\t\t\t{Key: \"url_query\", Value: \"url_query_value\"},\n\t\t\t\t},\n\t\t\t\t[]memstore.Entry{\n\t\t\t\t\t{Key: \"custom\", ValueRaw: \"custom_value\"},\n\t\t\t\t})\n\n\t\t\tif err == nil {\n\t\t\t\tmu.Lock()\n\t\t\t\texpected += \"0001-01-01 00:00:00|1s|200|GET|/path_value?url_query=url_query_value|::1|path_param=path_param_value url_query=url_query_value custom=custom_value|0 B|0 B|Incoming|Outcoming|\\n\"\n\t\t\t\texpectedLines++\n\t\t\t\tmu.Unlock()\n\t\t\t}\n\t\t}()\n\n\t}\n\n\t// give some time to write at least some messages or all\n\t// (depends on the machine the test is running).\n\ttime.Sleep(42 * time.Millisecond)\n\tac.Close()\n\tend := time.Since(now)\n\n\tif got := atomic.LoadUint32(&ac.remaining); got > 0 { // test wait.\n\t\tt.Fatalf(\"expected remaining: %d but got: %d\", 0, got)\n\t}\n\n\tmu.Lock()\n\texpectedSoFoar := expected\n\texpectedLinesSoFar := expectedLines\n\tmu.Unlock()\n\n\tif got := w.String(); expectedSoFoar != got {\n\t\tgotLines := strings.Count(got, \"\\n\")\n\t\tt.Logf(\"expected printed result to be[%d]:\\n'%s'\\n\\nbut got[%d]:\\n'%s'\", expectedLinesSoFar, expectedSoFoar, gotLines, got)\n\t\tt.Fatalf(\"expected: %d | got: %d lines\", expectedLinesSoFar, gotLines)\n\t} else {\n\t\tt.Logf(\"We've got [%d/%d] lines of logs in %s\", expectedLinesSoFar, goroutinesN, end.String())\n\t}\n}\n\nfunc TestAccessLogBroker(t *testing.T) {\n\tw := new(bytes.Buffer)\n\tac := New(w)\n\n\tac.Clock = TClock(time.Time{})\n\tbroker := ac.Broker()\n\n\twg := new(sync.WaitGroup)\n\tn := 4\n\twg.Add(n)\n\tgo func() {\n\t\ti := 0\n\t\tln := broker.NewListener()\n\n\t\tfor log := range ln {\n\t\t\tlat := log.Latency\n\t\t\tt.Log(lat.String())\n\t\t\twg.Done()\n\t\t\tif expected := time.Duration(i) * time.Second; expected != lat {\n\t\t\t\tpanic(fmt.Sprintf(\"expected latency: %s but got: %s\", expected, lat))\n\t\t\t}\n\t\t\ttime.Sleep(1350 * time.Millisecond)\n\t\t\tif log.Latency != lat {\n\t\t\t\tpanic(\"expected logger to wait for notifier before release the log\")\n\t\t\t}\n\t\t\ti++\n\t\t}\n\n\t\tif i != n {\n\t\t\tfor i < n {\n\t\t\t\twg.Done()\n\t\t\t\ti++\n\t\t\t}\n\t\t}\n\n\t\tt.Log(\"Log Listener Closed: interrupted\")\n\t}()\n\n\ttime.Sleep(time.Second)\n\n\tprintLog := func(lat time.Duration) {\n\t\terr := ac.Print(\n\t\t\tnil,\n\t\t\tlat,\n\t\t\t\"\",\n\t\t\t0,\n\t\t\t\"\",\n\t\t\t\"\",\n\t\t\t\"\",\n\t\t\t\"\",\n\t\t\t\"\",\n\t\t\t0,\n\t\t\t0,\n\t\t\tnil,\n\t\t\tnil,\n\t\t\tnil,\n\t\t)\n\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t}\n\n\tfor i := 0; i < n; i++ {\n\t\tprintLog(time.Duration(i) * time.Second)\n\t}\n\n\t// wait for all listeners to finish.\n\twg.Wait()\n\n\tac.Close()\n}\n\nfunc TestAccessLogBlank(t *testing.T) {\n\tw := new(bytes.Buffer)\n\tac := New(w)\n\tclockTime, _ := time.Parse(defaultTimeFormat, \"1993-01-01 05:00:00\")\n\tac.Clock = TClock(clockTime)\n\tac.Blank = []byte(\"<no value>\")\n\n\tac.Print(\n\t\tnil,\n\t\ttime.Second,\n\t\tdefaultTimeFormat,\n\t\t200,\n\t\t\"GET\",\n\t\t\"/\",\n\t\t\"127.0.0.1\",\n\t\t\"\",\n\t\t\"\",\n\t\t0,\n\t\t0,\n\t\tnil,\n\t\tnil,\n\t\tnil,\n\t)\n\n\tac.Close()\n\t// the request and bodies length are enabled by-default, zero bytes length\n\t// are written with 0 B and this cannot changed, so the request field\n\t// should be written as \"<no value>\".\n\texpected := \"1993-01-01 05:00:00|1s|200|GET|/|127.0.0.1|0 B|0 B|<no value>|\\n\"\n\tif got := w.String(); expected != got {\n\t\tt.Fatalf(\"expected:\\n'%s'\\n\\nbut got:\\n'%s'\", expected, got)\n\t}\n}\n\ntype slowClose struct{ *bytes.Buffer }\n\nfunc (c *slowClose) Close() error {\n\ttime.Sleep(1 * time.Second)\n\treturn nil\n}\n\nfunc TestAccessLogSetOutput(t *testing.T) {\n\tvar (\n\t\tw1 = &bytes.Buffer{}\n\t\tw2 = &bytes.Buffer{}\n\t\tw3 = &slowClose{&bytes.Buffer{}}\n\t\tw4 = &bytes.Buffer{}\n\t)\n\n\tac := New(w1)\n\tac.Clock = TClock(time.Time{})\n\n\tn := 40\n\texpected := strings.Repeat(\"0001-01-01 00:00:00|1s|200|GET|/|127.0.0.1|0 B|0 B||\\n\", n)\n\n\tprintLog := func() {\n\t\terr := ac.Print(\n\t\t\tnil,\n\t\t\ttime.Second,\n\t\t\tdefaultTimeFormat,\n\t\t\t200,\n\t\t\t\"GET\",\n\t\t\t\"/\",\n\t\t\t\"127.0.0.1\",\n\t\t\t\"\",\n\t\t\t\"\",\n\t\t\t0,\n\t\t\t0,\n\t\t\tnil,\n\t\t\tnil,\n\t\t\tnil,\n\t\t)\n\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t}\n\n\ttestSetOutput := func(name string, w io.Writer, withSlowClose bool) {\n\t\twg := new(sync.WaitGroup)\n\t\twg.Add(n / 4)\n\t\tfor i := 0; i < n/4; i++ {\n\t\t\tgo func(i int) {\n\t\t\t\tdefer wg.Done()\n\n\t\t\t\tif i%2 == 0 {\n\t\t\t\t\ttime.Sleep(10 * time.Millisecond)\n\t\t\t\t}\n\n\t\t\t\tif i == 5 {\n\t\t\t\t\tif w != nil {\n\t\t\t\t\t\tnow := time.Now()\n\t\t\t\t\t\tac.SetOutput(w)\n\t\t\t\t\t\tif withSlowClose {\n\t\t\t\t\t\t\tend := time.Since(now)\n\t\t\t\t\t\t\tif end < time.Second {\n\t\t\t\t\t\t\t\tpanic(fmt.Sprintf(\"[%s] [%d]: SetOutput should wait for previous Close. Expected to return a bit after %s but %s\", name, i, time.Second, end))\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tprintLog()\n\t\t\t}(i)\n\t\t}\n\n\t\t// wait to finish.\n\t\twg.Wait()\n\t}\n\n\tgo testSetOutput(\"w1\", nil, false) // write at least one line and then\n\ttime.Sleep(100 * time.Millisecond) // concurrently\n\ttestSetOutput(\"w2\", w2, false)     // change the writer\n\ttestSetOutput(\"w3\", w3, false)\n\ttestSetOutput(\"w4\", w4, true)\n\n\tgotAll := w1.String() + w2.String() + w3.String() + w4.String()\n\n\t// test if all content written and we have no loses.\n\tif expected != gotAll {\n\t\tt.Fatalf(\"expected total written result to be:\\n'%s'\\n\\nbut got:\\n'%s'\", expected, gotAll)\n\t}\n\n\t// now, check if all have contents, they should because we wait between them,\n\t// contents spread.\n\tcheckLines := func(name, s string, minimumLines int) {\n\t\tif got := strings.Count(s, \"\\n\"); got < minimumLines {\n\t\t\tt.Logf(\"[%s] expected minimum lines of: %d but got %d\", name, minimumLines, got)\n\t\t}\n\t}\n\n\tcheckLines(\"w1\", w1.String(), 1)\n\tcheckLines(\"w2\", w2.String(), 5)\n\tcheckLines(\"w3\", w3.String(), 5)\n\tcheckLines(\"w4\", w4.String(), 5)\n\n\tif err := ac.Close(); err != nil {\n\t\tt.Fatalf(\"On close: %v\", err)\n\t}\n}\n\ntype noOpFormatter struct{}\n\nfunc (*noOpFormatter) SetOutput(io.Writer) {}\n\n// Format prints the logs in text/template format.\nfunc (*noOpFormatter) Format(*Log) (bool, error) {\n\treturn true, nil\n}\n\n// go test -run=^$ -bench=BenchmarkAccessLogAfter -benchmem\nfunc BenchmarkAccessLogAfter(b *testing.B) {\n\tbenchmarkAccessLogAfter(b, true, false)\n}\n\nfunc BenchmarkAccessLogAfterPrint(b *testing.B) {\n\tbenchmarkAccessLogAfter(b, false, false)\n}\n\nfunc benchmarkAccessLogAfter(b *testing.B, withLogStruct, async bool) {\n\tac := New(io.Discard)\n\tac.Clock = TClock(time.Time{})\n\tac.BytesReceived = false\n\tac.BytesReceivedBody = false\n\tac.BytesSent = false\n\tac.BytesSentBody = false\n\tac.BodyMinify = false\n\tac.RequestBody = false\n\tac.ResponseBody = false\n\tac.Async = false\n\tac.IP = false\n\tif withLogStruct {\n\t\tac.SetFormatter(new(noOpFormatter)) // just to create the log structure, here we test the log creation time only.\n\t}\n\n\tctx := new(context.Context)\n\treq, err := http.NewRequest(\"GET\", \"/\", nil)\n\tif err != nil {\n\t\tb.Fatal(err)\n\t}\n\tctx.ResetRequest(req)\n\trecorder := httptest.NewRecorder()\n\tw := context.AcquireResponseWriter()\n\tw.BeginResponse(recorder)\n\tctx.ResetResponseWriter(w)\n\n\twg := new(sync.WaitGroup)\n\tif async {\n\t\twg.Add(b.N)\n\t}\n\n\tb.ResetTimer()\n\tfor n := 0; n < b.N; n++ {\n\t\tif async {\n\t\t\tgo func() {\n\t\t\t\tac.after(ctx, time.Millisecond, \"GET\", \"/\")\n\t\t\t\twg.Done()\n\t\t\t}()\n\t\t} else {\n\t\t\tac.after(ctx, time.Millisecond, \"GET\", \"/\")\n\t\t}\n\t}\n\tb.StopTimer()\n\tif async {\n\t\twg.Wait()\n\t}\n\tw.EndResponse()\n}\n"
  },
  {
    "path": "middleware/accesslog/broker.go",
    "content": "package accesslog\n\n// LogChan describes the log channel.\n// See `Broker` for details.\ntype LogChan chan Log\n\n// A Broker holds the active listeners,\n// incoming logs on its Notifier channel\n// and broadcast event data to all registered listeners.\n//\n// Exports the `NewListener` and `CloseListener` methods.\ntype Broker struct {\n\t// Logs are pushed to this channel\n\t// by the main events-gathering `run` routine.\n\tNotifier LogChan\n\n\t// NewListener action.\n\tnewListeners chan LogChan\n\n\t// CloseListener action.\n\tclosingListeners chan LogChan\n\n\t// listeners store.\n\tlisteners map[LogChan]bool\n\n\t// force-terminate all listeners.\n\tclose chan struct{}\n}\n\n// newBroker returns a new broker factory.\nfunc newBroker() *Broker {\n\tb := &Broker{\n\t\tNotifier:         make(LogChan, 1),\n\t\tnewListeners:     make(chan LogChan),\n\t\tclosingListeners: make(chan LogChan),\n\t\tlisteners:        make(map[LogChan]bool),\n\t\tclose:            make(chan struct{}),\n\t}\n\n\t// Listens and Broadcasts events.\n\tgo b.run()\n\n\treturn b\n}\n\n// run listens on different channels and act accordingly.\nfunc (b *Broker) run() {\n\tfor {\n\t\tselect {\n\t\tcase s := <-b.newListeners:\n\t\t\t// A new channel has started to listen.\n\t\t\tb.listeners[s] = true\n\n\t\tcase s := <-b.closingListeners:\n\t\t\t// A listener has dettached.\n\t\t\t// Stop sending them the logs.\n\t\t\tdelete(b.listeners, s)\n\n\t\tcase log := <-b.Notifier:\n\t\t\t// A new log sent by the logger.\n\t\t\t// Send it to all active listeners.\n\t\t\tfor clientMessageChan := range b.listeners {\n\t\t\t\tclientMessageChan <- log\n\t\t\t}\n\n\t\tcase <-b.close:\n\t\t\tfor clientMessageChan := range b.listeners {\n\t\t\t\tdelete(b.listeners, clientMessageChan)\n\t\t\t\tclose(clientMessageChan)\n\t\t\t}\n\t\t}\n\t}\n}\n\n// notify sends the \"log\" to all active listeners.\nfunc (b *Broker) notify(log Log) {\n\tb.Notifier <- log\n}\n\n// NewListener returns a new log channel listener.\n// The caller SHALL NOT use this to write logs.\nfunc (b *Broker) NewListener() LogChan {\n\t// Each listener registers its own message channel with the Broker's connections registry.\n\tlogs := make(LogChan)\n\t// Signal the broker that we have a new listener.\n\tb.newListeners <- logs\n\treturn logs\n}\n\n// CloseListener removes the \"ln\" listener from the active listeners.\nfunc (b *Broker) CloseListener(ln LogChan) {\n\tb.closingListeners <- ln\n}\n\n// As we cant export a read-only and pass it as closing client\n// we will return a read-write channel on NewListener and add a note that the user\n// should NOT send data back to the channel, its use is read-only.\n// func (b *Broker) CloseListener(ln <-chan *Log) {\n// \tb.closingListeners <- ln\n// }\n"
  },
  {
    "path": "middleware/accesslog/csv.go",
    "content": "package accesslog\n\nimport (\n\t\"encoding/csv\"\n\t\"io\"\n\t\"strconv\"\n\t\"sync\"\n)\n\n// CSV is a Formatter type for csv encoded logs.\ntype CSV struct {\n\twriterPool *sync.Pool\n\tac         *AccessLog\n\n\t// Add header fields to the first line if it's not exist.\n\t// Note that the destination should be a compatible io.Reader\n\t// with access to write.\n\tHeader bool\n\t// Google Spreadsheet's Script to wrap the Timestamp field\n\t// in order to convert it into a readable date.\n\t// Example: \"FROM_UNIX\" when\n\t// function FROM_UNIX(epoch_in_millis) {\n\t// \treturn new Date(epoch_in_millis);\n\t// }\n\tDateScript string\n\n\t// TODO: Fields []string // field name, position?\n}\n\n// SetOutput initializes the csv writer.\n// It uses the \"dest\" as AccessLog to\n// write the first csv record which\n// contains the names of the future log values.\nfunc (f *CSV) SetOutput(dest io.Writer) {\n\tf.ac, _ = dest.(*AccessLog)\n\tf.writerPool = &sync.Pool{\n\t\tNew: func() any {\n\t\t\treturn csv.NewWriter(dest)\n\t\t},\n\t}\n\n\tif !f.Header {\n\t\treturn\n\t}\n\n\t{\n\t\t// If the destination is not a reader\n\t\t// we can't detect if the header already inserted\n\t\t// so we exit, we dont want to malform the contents.\n\t\tdestReader, ok := f.ac.Writer.(io.Reader)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\n\t\tr := csv.NewReader(destReader)\n\t\tif header, err := r.Read(); err == nil && len(header) > 0 && header[0] == \"Timestamp\" {\n\t\t\t// we assume header already exists, exit.\n\t\t\treturn\n\t\t}\n\t}\n\n\t// Write the header.\n\tw := csv.NewWriter(dest)\n\n\tkeys := []string{\"Timestamp\", \"Latency\", \"Code\", \"Method\", \"Path\"}\n\n\tif f.ac.IP {\n\t\tkeys = append(keys, \"IP\")\n\t}\n\n\t// keys = append(keys, []string{\"Params\", \"Query\"}...)\n\tkeys = append(keys, \"Req Values\")\n\n\tif f.ac.BytesReceived || f.ac.BytesReceivedBody {\n\t\tkeys = append(keys, \"In\")\n\t}\n\n\tif f.ac.BytesSent || f.ac.BytesSentBody {\n\t\tkeys = append(keys, \"Out\")\n\t}\n\n\tif f.ac.RequestBody {\n\t\tkeys = append(keys, \"Request\")\n\t}\n\n\tif f.ac.ResponseBody {\n\t\tkeys = append(keys, \"Response\")\n\t}\n\n\tw.Write(keys)\n\tw.Flush()\n}\n\n// Format writes an incoming log using CSV encoding.\nfunc (f *CSV) Format(log *Log) (bool, error) {\n\t// Timestamp, Latency, Code, Method, Path, IP, Path Params Query Fields\n\t//|Bytes Received|Bytes Sent|Request|Response|\n\n\ttimestamp := strconv.FormatInt(log.Timestamp, 10)\n\n\tif f.DateScript != \"\" {\n\t\ttimestamp = \"=\" + f.DateScript + \"(\" + timestamp + \")\"\n\t}\n\n\tvalues := []string{\n\t\ttimestamp,\n\t\tlog.Latency.String(),\n\t\tstrconv.Itoa(log.Code),\n\t\tlog.Method,\n\t\tlog.Path,\n\t}\n\n\tif f.ac.IP {\n\t\tvalues = append(values, log.IP)\n\t}\n\n\tif s := log.RequestValuesLine(); s != \"\" || f.Header {\n\t\t// even if it's empty, if Header was set, then add it.\n\t\tvalues = append(values, s)\n\t}\n\n\tif f.ac.BytesReceived || f.ac.BytesReceivedBody {\n\t\tvalues = append(values, strconv.Itoa(log.BytesReceived))\n\t}\n\n\tif f.ac.BytesSent || f.ac.BytesSentBody {\n\t\tvalues = append(values, strconv.Itoa(log.BytesSent))\n\t}\n\n\tif f.ac.RequestBody && (log.Request != \"\" || f.Header) {\n\t\tvalues = append(values, log.Request)\n\t}\n\n\tif f.ac.ResponseBody && (log.Response != \"\" || f.Header) {\n\t\tvalues = append(values, log.Response)\n\t}\n\n\tw := f.writerPool.Get().(*csv.Writer)\n\terr := w.Write(values)\n\tw.Flush() // it works as \"reset\" too.\n\tf.writerPool.Put(w)\n\treturn true, err\n}\n"
  },
  {
    "path": "middleware/accesslog/csv_test.go",
    "content": "package accesslog\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/core/memstore\"\n)\n\nfunc TestCSV(t *testing.T) {\n\tbuf := new(bytes.Buffer)\n\tac := New(buf)\n\tac.RequestBody = false\n\tstaticNow, _ := time.Parse(defaultTimeFormat, \"1993-01-01 05:00:00\")\n\tac.Clock = TClock(staticNow)\n\tac.SetFormatter(&CSV{\n\t\tHeader: true,\n\t})\n\n\tlat, _ := time.ParseDuration(\"1s\")\n\n\tprintFunc := func() {\n\t\tac.Print(\n\t\t\tnil,\n\t\t\tlat,\n\t\t\t\"\",\n\t\t\t200,\n\t\t\t\"GET\",\n\t\t\t\"/\",\n\t\t\t\"::1\",\n\t\t\t\"\",\n\t\t\t\"Index\",\n\t\t\t573,\n\t\t\t81,\n\t\t\tnil,\n\t\t\t[]memstore.StringEntry{{Key: \"sleep\", Value: \"1s\"}},\n\t\t\tnil)\n\t}\n\n\t// print twice, the header should only be written once.\n\tprintFunc()\n\tprintFunc()\n\n\texpected := `Timestamp,Latency,Code,Method,Path,IP,Req Values,In,Out\n725864400000,1s,200,GET,/,::1,sleep=1s,573,81\n725864400000,1s,200,GET,/,::1,sleep=1s,573,81\n`\n\n\tac.Close()\n\tif got := buf.String(); expected != got {\n\t\tt.Fatalf(\"expected:\\n%s\\n\\nbut got:\\n%s\", expected, got)\n\t}\n}\n"
  },
  {
    "path": "middleware/accesslog/json.go",
    "content": "package accesslog\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"strconv\"\n\t\"strings\"\n\n\tjsoniter \"github.com/json-iterator/go\"\n)\n\n// JSON is a Formatter type for JSON logs.\ntype JSON struct {\n\t// Indent in spaces.\n\t// Note that, if set to > 0 then jsoniter is used instead of easyjson.\n\tIndent     string\n\tEscapeHTML bool\n\tHumanTime  bool\n\n\tjsoniter jsoniter.API\n\tac       *AccessLog\n}\n\n// SetOutput creates the json encoder writes to the \"dest\".\n// It's called automatically by the middleware when this Formatter is used.\nfunc (f *JSON) SetOutput(dest io.Writer) {\n\tf.ac, _ = dest.(*AccessLog)\n\tif indentStep := strings.Count(f.Indent, \" \"); indentStep > 0 {\n\t\t// Note that: indent setting should always be spaces\n\t\t// as the jsoniter does not support other chars.\n\t\tf.jsoniter = jsoniter.Config{\n\t\t\tTagKey:        \"json\",\n\t\t\tIndentionStep: indentStep,\n\t\t\tEscapeHTML:    f.EscapeHTML,\n\t\t\tSortMapKeys:   true,\n\t\t}.Froze()\n\t}\n}\n\nvar (\n\ttimestampKeyB        = []byte(`\"timestamp\":`)\n\ttimestampKeyIndentB  = append(timestampKeyB, ' ')\n\ttimestampKeyVB       = append(timestampKeyB, '0')\n\ttimestampIndentKeyVB = append(timestampKeyIndentB, '0')\n)\n\n// Format prints the logs in JSON format.\n// Writes to the destination directly,\n// locks on each Format call.\nfunc (f *JSON) Format(log *Log) (bool, error) {\n\tif f.jsoniter != nil {\n\t\tif f.HumanTime {\n\t\t\t// 1. Don't write the unix timestamp,\n\t\t\t// key will be visible though as we don't omit the field.\n\t\t\tlog.Timestamp = 0\n\t\t}\n\n\t\tb, err := f.jsoniter.Marshal(log)\n\t\tif err != nil {\n\t\t\treturn true, err\n\t\t}\n\n\t\tif f.HumanTime {\n\t\t\t// 2. Get the time text based on the configuration.\n\t\t\tt := log.Now.Format(log.TimeFormat)\n\n\t\t\t// 3. Find the \"timestamp:$indent\"\n\t\t\t// and set it to the text one.\n\t\t\tvar (\n\t\t\t\toldT  []byte\n\t\t\t\ttsKey []byte\n\t\t\t)\n\n\t\t\tif f.Indent != \"\" {\n\t\t\t\toldT = timestampIndentKeyVB\n\t\t\t\ttsKey = timestampKeyIndentB\n\t\t\t} else {\n\t\t\t\toldT = timestampKeyVB\n\t\t\t\ttsKey = timestampKeyB\n\t\t\t}\n\n\t\t\tnewT := append(tsKey, strconv.Quote(t)...)\n\t\t\tb = bytes.Replace(b, oldT, newT, 1)\n\t\t}\n\n\t\tf.ac.Write(append(b, newLine))\n\t\treturn true, nil\n\t}\n\n\terr := f.writeEasyJSON(log)\n\treturn true, err\n}\n"
  },
  {
    "path": "middleware/accesslog/json_easy.go",
    "content": "// Code generated by easyjson but it's highly edited. Please manually add fields as Log is grow,\n// in-short: all decode methods removed, rename of the methods, add a new line breaker,\n// remove the easyjson import requirement.\n\npackage accesslog\n\nimport (\n\t\"encoding/json\"\n\n\t\"github.com/kataras/iris/v12/core/memstore\"\n\t\"github.com/mailru/easyjson/jwriter\"\n)\n\nfunc (f *JSON) writeEasyJSON(in *Log) error {\n\tout := &jwriter.Writer{NoEscapeHTML: !f.EscapeHTML}\n\n\tout.RawByte('{')\n\tfirst := true\n\t_ = first\n\t{\n\t\tconst prefix string = \",\\\"timestamp\\\":\"\n\t\tif first {\n\t\t\tfirst = false\n\t\t\tout.RawString(prefix[1:])\n\t\t} else {\n\t\t\tout.RawString(prefix)\n\t\t}\n\n\t\tif f.HumanTime {\n\t\t\tt := in.Now.Format(in.TimeFormat)\n\t\t\tout.String(t)\n\t\t} else {\n\t\t\tout.Int64(in.Timestamp)\n\t\t}\n\t}\n\t{\n\t\tconst prefix string = \",\\\"latency\\\":\"\n\t\tout.RawString(prefix)\n\t\tout.Int64(int64(in.Latency))\n\t}\n\t{\n\t\tconst prefix string = \",\\\"code\\\":\"\n\t\tout.RawString(prefix)\n\t\tout.Int(int(in.Code))\n\t}\n\t{\n\t\tconst prefix string = \",\\\"method\\\":\"\n\t\tout.RawString(prefix)\n\t\tout.String(in.Method)\n\t}\n\t{\n\t\tconst prefix string = \",\\\"path\\\":\"\n\t\tout.RawString(prefix)\n\t\tout.String(in.Path)\n\t}\n\tif in.IP != \"\" {\n\t\tconst prefix string = \",\\\"ip\\\":\"\n\t\tout.RawString(prefix)\n\t\tout.String(in.IP)\n\t}\n\tif len(in.Query) != 0 {\n\t\tconst prefix string = \",\\\"query\\\":\"\n\t\tout.RawString(prefix)\n\t\t{\n\t\t\tout.RawByte('[')\n\t\t\tfor v4, v5 := range in.Query {\n\t\t\t\tif v4 > 0 {\n\t\t\t\t\tout.RawByte(',')\n\t\t\t\t}\n\t\t\t\teasyJSONStringEntry(out, v5)\n\t\t\t}\n\t\t\tout.RawByte(']')\n\t\t}\n\t}\n\tif len(in.PathParams) != 0 {\n\t\tconst prefix string = \",\\\"params\\\":\"\n\t\tout.RawString(prefix)\n\t\t{\n\t\t\tout.RawByte('[')\n\t\t\tfor v6, v7 := range in.PathParams {\n\t\t\t\tif v6 > 0 {\n\t\t\t\t\tout.RawByte(',')\n\t\t\t\t}\n\t\t\t\teasyJSONEntry(out, v7)\n\t\t\t}\n\t\t\tout.RawByte(']')\n\t\t}\n\t}\n\tif len(in.Fields) != 0 {\n\t\tconst prefix string = \",\\\"fields\\\":\"\n\t\tout.RawString(prefix)\n\t\t{\n\t\t\tout.RawByte('[')\n\t\t\tfor v8, v9 := range in.Fields {\n\t\t\t\tif v8 > 0 {\n\t\t\t\t\tout.RawByte(',')\n\t\t\t\t}\n\t\t\t\teasyJSONEntry(out, v9)\n\t\t\t}\n\t\t\tout.RawByte(']')\n\t\t}\n\t}\n\tif in.Logger.RequestBody {\n\t\tconst prefix string = \",\\\"request\\\":\"\n\t\tout.RawString(prefix)\n\t\tout.String(string(in.Request))\n\t}\n\tif in.Logger.ResponseBody {\n\n\t\tconst prefix string = \",\\\"response\\\":\"\n\t\tout.RawString(prefix)\n\t\tout.String(string(in.Response))\n\n\t}\n\tif in.BytesReceived != 0 {\n\t\tconst prefix string = \",\\\"bytes_received\\\":\"\n\t\tout.RawString(prefix)\n\t\tout.Int(int(in.BytesReceived))\n\t}\n\tif in.BytesSent != 0 {\n\t\tconst prefix string = \",\\\"bytes_sent\\\":\"\n\t\tout.RawString(prefix)\n\t\tout.Int(int(in.BytesSent))\n\t}\n\tout.RawByte('}')\n\tout.RawByte(newLine)\n\n\tif out.Error != nil {\n\t\treturn out.Error\n\t}\n\tf.ac.Write(out.Buffer.BuildBytes())\n\treturn nil\n}\n\nfunc easyJSONEntry(out *jwriter.Writer, in memstore.Entry) {\n\tout.RawByte('{')\n\tfirst := true\n\t_ = first\n\t{\n\t\tconst prefix string = \",\\\"key\\\":\"\n\t\tout.RawString(prefix[1:])\n\t\tout.String(string(in.Key))\n\t}\n\t{\n\t\tconst prefix string = \",\\\"value\\\":\"\n\t\tout.RawString(prefix)\n\t\tif m, ok := in.ValueRaw.(json.Marshaler); ok {\n\t\t\tout.Raw(m.MarshalJSON())\n\t\t} else {\n\t\t\tout.Raw(json.Marshal(in.ValueRaw))\n\t\t}\n\t}\n\tout.RawByte('}')\n}\n\nfunc easyJSONStringEntry(out *jwriter.Writer, in memstore.StringEntry) {\n\tout.RawByte('{')\n\tfirst := true\n\t_ = first\n\t{\n\t\tconst prefix string = \",\\\"key\\\":\"\n\t\tout.RawString(prefix[1:])\n\t\tout.String(string(in.Key))\n\t}\n\t{\n\t\tconst prefix string = \",\\\"value\\\":\"\n\t\tout.RawString(prefix)\n\t\tout.String(string(in.Value))\n\t}\n\tout.RawByte('}')\n}\n"
  },
  {
    "path": "middleware/accesslog/log.go",
    "content": "package accesslog\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/core/memstore\"\n)\n\n// Log represents the log data specifically for the accesslog middleware.\n//\n//easyjson:json\ntype Log struct {\n\t// The AccessLog instance this Log was created of.\n\tLogger *AccessLog `json:\"-\" yaml:\"-\" toml:\"-\"`\n\n\t// The time the log is created.\n\tNow time.Time `json:\"-\" yaml:\"-\" toml:\"-\"`\n\t// TimeFormat selected to print the Time as string,\n\t// useful on Template Formatter.\n\tTimeFormat string `json:\"-\" yaml:\"-\" toml:\"-\"`\n\t// Timestamp the Now's unix timestamp (milliseconds).\n\tTimestamp int64 `json:\"timestamp\" csv:\"timestamp\"`\n\n\t// Request-Response latency.\n\tLatency time.Duration `json:\"latency\" csv:\"latency\"`\n\t// The response status code.\n\tCode int `json:\"code\" csv:\"code\"`\n\t// Init request's Method and Path.\n\tMethod string `json:\"method\" csv:\"method\"`\n\tPath   string `json:\"path\" csv:\"path\"`\n\t// The Remote Address.\n\tIP string `json:\"ip,omitempty\" csv:\"ip,omitempty\"`\n\t// Sorted URL Query arguments.\n\tQuery []memstore.StringEntry `json:\"query,omitempty\" csv:\"query,omitempty\"`\n\t// Dynamic path parameters.\n\tPathParams memstore.Store `json:\"params,omitempty\" csv:\"params,omitempty\"`\n\t// Fields any data information useful to represent this Log.\n\tFields memstore.Store `json:\"fields,omitempty\" csv:\"fields,omitempty\"`\n\t// The Request and Response raw bodies.\n\t// If they are escaped (e.g. JSON),\n\t// A third-party software can read it through:\n\t// data, _ := strconv.Unquote(log.Request)\n\t// err := json.Unmarshal([]byte(data), &customStruct)\n\tRequest  string `json:\"request,omitempty\" csv:\"request,omitempty\"`\n\tResponse string `json:\"response,omitempty\" csv:\"response,omitempty\"`\n\t//  The actual number of bytes received and sent on the network (headers + body or body only).\n\tBytesReceived int `json:\"bytes_received,omitempty\" csv:\"bytes_received,omitempty\"`\n\tBytesSent     int `json:\"bytes_sent,omitempty\" csv:\"bytes_sent,omitempty\"`\n\n\t// A copy of the Request's Context when Async is true (safe to use concurrently),\n\t// otherwise it's the current Context (not safe for concurrent access).\n\tCtx *context.Context `json:\"-\" yaml:\"-\" toml:\"-\"`\n}\n\n// Clone returns a raw copy value of this Log.\nfunc (l *Log) Clone() Log {\n\treturn *l\n}\n\n// RequestValuesLine returns a string line which\n// combines the path parameters, query and custom fields.\nfunc (l *Log) RequestValuesLine() string {\n\tbuf := new(strings.Builder)\n\t_, n := parseRequestValues(buf, l.Code, l.PathParams, l.Query, l.Fields)\n\tif n == 0 {\n\t\treturn \"\"\n\t}\n\n\trequestValues := buf.String()\n\tif n > 1 {\n\t\treturn requestValues[0 : n-1] // remove last space.\n\t}\n\n\treturn requestValues\n}\n\n// BytesReceivedLine returns the formatted bytes received length.\nfunc (l *Log) BytesReceivedLine() string {\n\tif !l.Logger.BytesReceived && !l.Logger.BytesReceivedBody {\n\t\treturn \"\"\n\t}\n\treturn formatBytes(l.BytesReceived)\n}\n\n// BytesSentLine returns the formatted bytes sent length.\nfunc (l *Log) BytesSentLine() string {\n\tif !l.Logger.BytesSent && !l.Logger.BytesSentBody {\n\t\treturn \"\"\n\t}\n\treturn formatBytes(l.BytesSent)\n}\n\nfunc formatBytes(b int) string {\n\tif b <= 0 {\n\t\treturn \"0 B\"\n\t}\n\n\tconst unit = 1024\n\tif b < unit {\n\t\treturn fmt.Sprintf(\"%d B\", b)\n\t}\n\tdiv, exp := int64(unit), 0\n\tfor n := b / unit; n >= unit; n /= unit {\n\t\tdiv *= unit\n\t\texp++\n\t}\n\treturn fmt.Sprintf(\"%.1f %cB\",\n\t\tfloat64(b)/float64(div), \"KMGTPE\"[exp])\n}\n\nconst (\n\teq    = '='\n\tspace = ' '\n)\n\n// parses the request values (params, query and fields).\n// returns the length of written bytes for parsing request values\n// and the total.\nfunc parseRequestValues(buf interface {\n\tio.StringWriter\n\tio.ByteWriter\n\tLen() int\n}, code int, pathParams memstore.Store, query []memstore.StringEntry, fields memstore.Store) (int, int) {\n\tn := buf.Len()\n\tif !context.StatusCodeNotSuccessful(code) {\n\t\t// collect path parameters on a successful request-response only.\n\t\tfor _, entry := range pathParams {\n\t\t\tbuf.WriteString(entry.Key)\n\t\t\tbuf.WriteByte(eq)\n\t\t\tbuf.WriteString(fmt.Sprintf(\"%v\", entry.ValueRaw))\n\t\t\tbuf.WriteByte(space)\n\t\t}\n\t}\n\n\tfor _, entry := range query {\n\t\tbuf.WriteString(entry.Key)\n\t\tbuf.WriteByte(eq)\n\t\tbuf.WriteString(entry.Value)\n\t\tbuf.WriteByte(space)\n\t}\n\n\tfor _, entry := range fields {\n\t\tbuf.WriteString(entry.Key)\n\t\tbuf.WriteByte(eq)\n\t\tbuf.WriteString(fmt.Sprintf(\"%v\", entry.ValueRaw))\n\t\tbuf.WriteByte(space)\n\t}\n\n\ttotal := buf.Len()\n\treturn total - n, total\n}\n\ntype (\n\t// Formatter is responsible to print a Log to the accesslog's writer.\n\tFormatter interface {\n\t\t// SetOutput should inject the accesslog's direct output,\n\t\t// if this \"dest\" is used then the Formatter\n\t\t// should manually control its concurrent use.\n\t\tSetOutput(dest io.Writer)\n\t\t// Format should print the Log.\n\t\t// Returns nil error on handle successfully,\n\t\t// otherwise the log will be printed using the default formatter\n\t\t// and the error will be printed to the Iris Application's error log level.\n\t\t// Should return true if this handled the logging, otherwise false to\n\t\t// continue with the default print format.\n\t\tFormat(log *Log) (bool, error)\n\t}\n\n\t// Flusher can be implemented by a Writer or Formatter\n\t// to call its Flush method on AccessLog.Close\n\t// and on panic errors.\n\tFlusher interface{ Flush() error }\n\t// BufferTruncater can be implemented by writers\n\t// that support buffering.\n\tBufferTruncater interface{ Truncate(n int) }\n\t// FileTruncater can be implemented by files\n\t// that can support runtime size change.\n\tFileTruncater interface{ Truncate(size int64) error }\n)\n\nvar (\n\t_ Formatter = (*JSON)(nil)\n\t_ Formatter = (*Template)(nil)\n\t_ Formatter = (*CSV)(nil)\n)\n"
  },
  {
    "path": "middleware/accesslog/template.go",
    "content": "package accesslog\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"text/template\"\n)\n\n// Template is a Formatter.\n// It's used to print the Log in a text/template way.\n// The caller has full control over the printable result;\n// certain fields can be ignored, change the display order and e.t.c.\n//\n// For faster execution you can create a custom Formatter\n// and compile your own quicktemplate: https://github.com/valyala/quicktemplate\n//\n// This one uses the standard text/template syntax.\ntype Template struct {\n\t// Custom template source.\n\t// Use this or `Tmpl/TmplName` fields.\n\tText string\n\t// Custom template funcs to used when `Text` is not empty.\n\tFuncs template.FuncMap\n\n\t// Custom template to use, overrides the `Text` and `Funcs` fields.\n\tTmpl *template.Template\n\t// If not empty then this named template/block renders the log line.\n\tTmplName string\n\n\tac *AccessLog\n}\n\n// SetOutput creates the default template if missing\n// when this formatter is registered.\nfunc (f *Template) SetOutput(dest io.Writer) {\n\tf.ac, _ = dest.(*AccessLog)\n\n\tif f.Tmpl == nil {\n\t\ttmpl := template.New(\"\")\n\n\t\ttext := f.Text\n\t\tif text != \"\" {\n\t\t\ttmpl.Funcs(f.Funcs)\n\t\t} else {\n\t\t\ttext = defaultTmplText\n\t\t}\n\n\t\tf.Tmpl = template.Must(tmpl.Parse(text))\n\t}\n}\n\nconst defaultTmplText = \"{{.Now.Format .TimeFormat}}|{{.Latency}}|{{.Code}}|{{.Method}}|{{.Path}}|{{.IP}}|{{.RequestValuesLine}}|{{.BytesReceivedLine}}|{{.BytesSentLine}}|{{.Request}}|{{.Response}}|\\n\"\n\nfunc (f *Template) LogText(log *Log) (string, error) {\n\tvar err error\n\n\t// A template may be executed safely in parallel, although if parallel\n\t// executions share a Writer the output may be interleaved.\n\t// We solve that using a buffer pool, no locks when template is executing (x2 performance boost).\n\ttemp := f.ac.bufPool.Get().(*bytes.Buffer)\n\n\tif f.TmplName != \"\" {\n\t\terr = f.Tmpl.ExecuteTemplate(temp, f.TmplName, log)\n\t} else {\n\t\terr = f.Tmpl.Execute(temp, log)\n\t}\n\n\tif err != nil {\n\t\tf.ac.bufPool.Put(temp)\n\t\treturn \"\", err\n\t}\n\n\ttext := temp.String()\n\ttemp.Reset()\n\tf.ac.bufPool.Put(temp)\n\n\treturn text, nil\n}\n\n// Format prints the logs in text/template format.\nfunc (f *Template) Format(log *Log) (bool, error) {\n\tvar err error\n\n\t// A template may be executed safely in parallel, although if parallel\n\t// executions share a Writer the output may be interleaved.\n\t// We solve that using a buffer pool, no locks when template is executing (x2 performance boost).\n\ttemp := f.ac.bufPool.Get().(*bytes.Buffer)\n\n\tif f.TmplName != \"\" {\n\t\terr = f.Tmpl.ExecuteTemplate(temp, f.TmplName, log)\n\t} else {\n\t\terr = f.Tmpl.Execute(temp, log)\n\t}\n\n\tif err != nil {\n\t\tf.ac.bufPool.Put(temp)\n\t\treturn true, err\n\t}\n\n\tf.ac.Write(temp.Bytes())\n\ttemp.Reset()\n\tf.ac.bufPool.Put(temp)\n\treturn true, nil\n}\n"
  },
  {
    "path": "middleware/basicauth/basicauth.go",
    "content": "package basicauth\n\nimport (\n\tstdContext \"context\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/sessions\"\n)\n\nfunc init() {\n\tcontext.SetHandlerName(\"iris/middleware/basicauth.*\", \"iris.basicauth\")\n}\n\nconst (\n\t// DefaultRealm is the default realm directive value on Default and Load functions.\n\tDefaultRealm = \"Authorization Required\"\n\t// DefaultMaxTriesCookie is the default cookie name to store the\n\t// current amount of login failures when MaxTries > 0.\n\tDefaultMaxTriesCookie = \"basicmaxtries\"\n\t// DefaultCookieMaxAge is the default cookie max age on MaxTries,\n\t// when the Options.MaxAge is zero.\n\tDefaultCookieMaxAge = time.Hour\n)\n\nconst (\n\tauthorizationType           = \"Basic Authentication\"\n\tauthenticateHeaderKey       = \"WWW-Authenticate\"\n\tproxyAuthenticateHeaderKey  = \"Proxy-Authenticate\"\n\tauthorizationHeaderKey      = \"Authorization\"\n\tproxyAuthorizationHeaderKey = \"Proxy-Authorization\"\n)\n\n// AuthFunc accepts the current request and the username and password user inputs\n// and it should optionally return a user value and report whether the login succeed or not.\n// Look the Options.Allow field.\n//\n// Default implementations are:\n// AllowUsers and AllowUsersFile functions.\ntype AuthFunc func(ctx *context.Context, username, password string) (any, bool)\n\n// ErrorHandler should handle the given request credentials failure.\n// See Options.ErrorHandler and DefaultErrorHandler for details.\ntype ErrorHandler func(ctx *context.Context, err error)\n\n// Options holds the necessary information that the BasicAuth instance needs to perform.\n// The only required value is the Allow field.\n//\n// Usage:\n//\n//\topts := Options { ... }\n//\tauth := New(opts)\ntype Options struct {\n\t// Realm directive, read http://tools.ietf.org/html/rfc2617#section-1.2 for details.\n\t// E.g. \"Authorization Required\".\n\tRealm string\n\t// In the case of proxies, the challenging status code is 407 (Proxy Authentication Required),\n\t// the Proxy-Authenticate response header contains at least one challenge applicable to the proxy,\n\t// and the Proxy-Authorization request header is used for providing the credentials to the proxy server.\n\t//\n\t// Proxy should be used to gain access to a resource behind a proxy server.\n\t// It authenticates the request to the proxy server, allowing it to transmit the request further.\n\tProxy bool\n\t// If set to true then any non-https request will immediately\n\t// dropped with a 505 status code (StatusHTTPVersionNotSupported) response.\n\t//\n\t// Defaults to false.\n\tHTTPSOnly bool\n\t// Allow is the only one required field for the Options type.\n\t// Can be customized to validate a username and password combination\n\t// and return a user object, e.g. fetch from database.\n\t//\n\t// There are two available builtin values, the AllowUsers and AllowUsersFile,\n\t// both of them decode a static list of users and compares with the user input (see BCRYPT function too).\n\t// Usage:\n\t//  - Allow: AllowUsers(iris.Map{\"username\": \"...\", \"password\": \"...\", \"other_field\": ...}, [BCRYPT])\n\t//  - Allow: AllowUsersFile(\"users.yml\", [BCRYPT])\n\t// Look the user.go source file for details.\n\tAllow AuthFunc\n\t// MaxAge sets expiration duration for the in-memory credentials map.\n\t// By default an old map entry will be removed when the user visits a page.\n\t// In order to remove old entries automatically please take a look at the `GC` option too.\n\t//\n\t// Usage:\n\t//  MaxAge: 30 * time.Minute\n\tMaxAge time.Duration\n\t// If greater than zero then the server will send 403 forbidden status code afer\n\t// MaxTries amount of sign in failures (see MaxTriesCookie).\n\t// Note that the client can modify the cookie and its value,\n\t// do NOT depend for any type of custom domain logic based on this field.\n\t// By default the server will re-ask for credentials on invalid credentials, each time.\n\tMaxTries int\n\t// MaxTriesCookie is the cookie name the middleware uses to\n\t// store the failures amount on the client side.\n\t// The lifetime of the cookie is the same as the configured MaxAge or one hour,\n\t// therefore a forbidden client can request for authentication again after expiration.\n\t//\n\t// You can always set custom logic on the Allow field as you have access to the current request instance.\n\t//\n\t// Defaults to \"basicmaxtries\".\n\t// The MaxTries should be set to greater than zero.\n\tMaxTriesCookie string\n\t// If not empty then this session key will be used to store\n\t// the current tries of login failures. If not a session manager\n\t// was registered then the application will log an error.\n\t// Note that this field has a priority over the MaxTriesCookie.\n\tMaxTriesSession string\n\t// ErrorHandler handles the given request credentials failure.\n\t// E.g  when the client tried to access a protected resource\n\t// with empty or invalid or expired credentials or\n\t// when Allow returned false and MaxTries consumed.\n\t//\n\t// Defaults to the DefaultErrorHandler, do not modify if you don't need to.\n\tErrorHandler ErrorHandler\n\t// GC automatically clears old entries every x duration.\n\t// Note that, by old entries we mean expired credentials therefore\n\t// the `MaxAge` option should be already set,\n\t// if it's not then all entries will be removed on \"every\" duration.\n\t// The standard context can be used for the internal ticker cancelation, it can be nil.\n\t//\n\t// Usage:\n\t//  GC: basicauth.GC{Every: 2 * time.Hour}\n\tGC GC\n}\n\n// GC holds the context and the tick duration to clear expired stored credentials.\n// See the Options.GC field.\ntype GC struct {\n\tContext stdContext.Context\n\tEvery   time.Duration\n}\n\n// BasicAuth implements the basic access authentication.\n// It is a method for an HTTP client (e.g. a web browser)\n// to provide a user name and password when making a request.\n// Basic authentication implementation is the simplest technique\n// for enforcing access controls to web resources because it does not require\n// cookies, session identifiers, or login pages; rather,\n// HTTP Basic authentication uses standard fields in the HTTP header.\n//\n// As the username and password are passed over the network as clear text\n// the basic authentication scheme is not secure on plain HTTP communication.\n// It is base64 encoded, but base64 is a reversible encoding.\n// HTTPS/TLS should be used with basic authentication.\n// Without these additional security enhancements,\n// basic authentication should NOT be used to protect sensitive or valuable information.\n//\n// Read https://tools.ietf.org/html/rfc2617 and\n// https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication for details.\ntype BasicAuth struct {\n\topts Options\n\t// built based on proxy field\n\taskCode             int\n\tauthorizationHeader string\n\tauthenticateHeader  string\n\t// built based on realm field.\n\tauthenticateHeaderValue string\n\n\t// credentials stores the user expiration,\n\t// key = username:password, value = expiration time (if MaxAge > 0).\n\tcredentials map[string]*time.Time // TODO: think of just a uint64 here (unix seconds).\n\t// protects the credentials concurrent access.\n\tmu sync.RWMutex\n}\n\n// New returns a new basic authentication middleware.\n// The result should be used to wrap an existing handler or the HTTP application's root router.\n//\n// Example Code:\n//\n//\topts := basicauth.Options{\n//\t\tRealm: basicauth.DefaultRealm,\n//\t    ErrorHandler: basicauth.DefaultErrorHandler,\n//\t\tMaxAge: 2 * time.Hour,\n//\t\tGC: basicauth.GC{\n//\t\t\tEvery: 3 * time.Hour,\n//\t\t},\n//\t\tAllow: basicauth.AllowUsers(users),\n//\t}\n//\tauth := basicauth.New(opts)\n//\tapp.Use(auth)\n//\n// Access the user in the route handler with: ctx.User().GetRaw().(*myCustomType).\n//\n// Look the BasicAuth type docs for more information.\nfunc New(opts Options) context.Handler {\n\tvar (\n\t\taskCode                 = http.StatusUnauthorized\n\t\tauthorizationHeader     = authorizationHeaderKey\n\t\tauthenticateHeader      = authenticateHeaderKey\n\t\tauthenticateHeaderValue = \"Basic\"\n\t)\n\n\tif opts.Allow == nil {\n\t\tpanic(\"BasicAuth: Allow field is required\")\n\t}\n\n\tif opts.Realm != \"\" {\n\t\tauthenticateHeaderValue += \" realm=\" + strconv.Quote(opts.Realm)\n\t}\n\n\tif opts.Proxy {\n\t\taskCode = http.StatusProxyAuthRequired\n\t\tauthenticateHeader = proxyAuthenticateHeaderKey\n\t\tauthorizationHeader = proxyAuthorizationHeaderKey\n\t}\n\n\tif opts.MaxTries > 0 && opts.MaxTriesCookie == \"\" {\n\t\topts.MaxTriesCookie = DefaultMaxTriesCookie\n\t}\n\n\tif opts.ErrorHandler == nil {\n\t\topts.ErrorHandler = DefaultErrorHandler\n\t}\n\n\tb := &BasicAuth{\n\t\topts:                    opts,\n\t\taskCode:                 askCode,\n\t\tauthorizationHeader:     authorizationHeader,\n\t\tauthenticateHeader:      authenticateHeader,\n\t\tauthenticateHeaderValue: authenticateHeaderValue,\n\t\tcredentials:             make(map[string]*time.Time),\n\t}\n\n\tif opts.GC.Every > 0 {\n\t\tgo b.runGC(opts.GC.Context, opts.GC.Every)\n\t}\n\n\treturn b.serveHTTP\n}\n\n// Default returns a new basic authentication middleware\n// based on pre-defined user list.\n// A user can hold any custom fields but the username and password\n// are required as they are compared against the user input\n// when access to protected resource is requested.\n// A user list can defined with one of the following values:\n//\n//\tmap[string]string form of: {username:password, ...}\n//\tmap[string]any form of: {\"username\": {\"password\": \"...\", \"other_field\": ...}, ...}\n//\t[]T which T completes the User interface, where T is a struct value\n//\t[]T which T contains at least Username and Password fields.\n//\n// Usage:\n//\n//\tauth := Default(map[string]string{\n//\t  \"admin\": \"admin\",\n//\t  \"john\": \"p@ss\",\n//\t})\nfunc Default(users any, userOpts ...UserAuthOption) context.Handler {\n\topts := Options{\n\t\tRealm: DefaultRealm,\n\t\tAllow: AllowUsers(users, userOpts...),\n\t}\n\treturn New(opts)\n}\n\n// Load same as Default but instead of a hard-coded user list it accepts\n// a filename to load the users from.\n//\n// Usage:\n//\n//\tauth := Load(\"users.yml\")\nfunc Load(jsonOrYamlFilename string, userOpts ...UserAuthOption) context.Handler {\n\topts := Options{\n\t\tRealm: DefaultRealm,\n\t\tAllow: AllowUsersFile(jsonOrYamlFilename, userOpts...),\n\t}\n\treturn New(opts)\n}\n\nfunc (b *BasicAuth) getCurrentTries(ctx *context.Context) (tries int) {\n\tif key := b.opts.MaxTriesSession; key != \"\" {\n\t\tif sess := sessions.Get(ctx); sess != nil {\n\t\t\ttries = sess.GetIntDefault(key, 0)\n\t\t} else {\n\t\t\tctx.Application().Logger().Error(\"basicauth: getCurrentTries: session key: %s but no session manager is registered\", key)\n\t\t\treturn\n\t\t}\n\t} else {\n\t\tcookie := ctx.GetCookie(b.opts.MaxTriesCookie)\n\t\tif cookie != \"\" {\n\t\t\ttries, _ = strconv.Atoi(cookie)\n\t\t}\n\t}\n\n\treturn\n}\n\nfunc (b *BasicAuth) setCurrentTries(ctx *context.Context, tries int) {\n\tif key := b.opts.MaxTriesSession; key != \"\" {\n\t\tif sess := sessions.Get(ctx); sess != nil {\n\t\t\tsess.Set(key, tries)\n\t\t} else {\n\t\t\tctx.Application().Logger().Error(\"basicauth: setCurrentTries: session key: %s but no session manager is registered\", key)\n\t\t\treturn\n\t\t}\n\t} else {\n\t\tmaxAge := b.opts.MaxAge\n\t\tif maxAge == 0 {\n\t\t\tmaxAge = DefaultCookieMaxAge // 1 hour.\n\t\t}\n\n\t\tc := &http.Cookie{\n\t\t\tName:     b.opts.MaxTriesCookie,\n\t\t\tPath:     \"/\",\n\t\t\tValue:    url.QueryEscape(strconv.Itoa(tries)),\n\t\t\tHttpOnly: true,\n\t\t\tExpires:  time.Now().Add(maxAge),\n\t\t\tMaxAge:   int(maxAge.Seconds()),\n\t\t}\n\n\t\tctx.SetCookie(c)\n\t}\n}\n\nfunc (b *BasicAuth) resetCurrentTries(ctx *context.Context) {\n\tif key := b.opts.MaxTriesSession; key != \"\" {\n\t\tif sess := sessions.Get(ctx); sess != nil {\n\t\t\tsess.Delete(key)\n\t\t} else {\n\t\t\tctx.Application().Logger().Error(\"basicauth: resetCurrentTries: session key: %s but no session manager is registered\", key)\n\t\t\treturn\n\t\t}\n\t} else {\n\t\tctx.RemoveCookie(b.opts.MaxTriesCookie)\n\t}\n}\n\nfunc isHTTPS(r *http.Request) bool {\n\treturn (strings.EqualFold(r.URL.Scheme, \"https\") || r.TLS != nil) && r.ProtoMajor == 2\n}\n\nfunc (b *BasicAuth) handleError(ctx *context.Context, err error) {\n\tctx.Application().Logger().Debug(err)\n\n\t// should not be nil as it's defaulted on New.\n\tb.opts.ErrorHandler(ctx, err)\n}\n\n// serveHTTP is the main method of this middleware,\n// checks and verifies the auhorization header for basic authentication,\n// next handlers will only be executed when the client is allowed to continue.\nfunc (b *BasicAuth) serveHTTP(ctx *context.Context) {\n\tif b.opts.HTTPSOnly && !isHTTPS(ctx.Request()) {\n\t\tb.handleError(ctx, ErrHTTPVersion{})\n\t\treturn\n\t}\n\n\theader := ctx.GetHeader(b.authorizationHeader)\n\tfullUser, username, password, ok := decodeHeader(header)\n\tif !ok { // Header is malformed or missing (e.g. browser cancel button on user prompt).\n\t\tb.handleError(ctx, ErrCredentialsMissing{\n\t\t\tHeader:                  header,\n\t\t\tAuthenticateHeader:      b.authenticateHeader,\n\t\t\tAuthenticateHeaderValue: b.authenticateHeaderValue,\n\t\t\tCode:                    b.askCode,\n\t\t})\n\t\treturn\n\t}\n\n\tvar (\n\t\tmaxTries = b.opts.MaxTries\n\t\ttries    int\n\t)\n\n\tif maxTries > 0 {\n\t\ttries = b.getCurrentTries(ctx)\n\t}\n\n\tuser, ok := b.opts.Allow(ctx, username, password)\n\tif !ok { // This username:password combination was not allowed.\n\t\tif maxTries > 0 {\n\t\t\ttries++\n\t\t\tb.setCurrentTries(ctx, tries)\n\t\t\tif tries >= maxTries { // e.g. if MaxTries == 1 then it should be allowed only once, so we must send forbidden now.\n\t\t\t\tb.handleError(ctx, ErrCredentialsForbidden{\n\t\t\t\t\tUsername: username,\n\t\t\t\t\tPassword: password,\n\t\t\t\t\tTries:    tries,\n\t\t\t\t\tAge:      b.opts.MaxAge,\n\t\t\t\t})\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tb.handleError(ctx, ErrCredentialsInvalid{\n\t\t\tUsername:                username,\n\t\t\tPassword:                password,\n\t\t\tCurrentTries:            tries,\n\t\t\tAuthenticateHeader:      b.authenticateHeader,\n\t\t\tAuthenticateHeaderValue: b.authenticateHeaderValue,\n\t\t\tCode:                    b.askCode,\n\t\t})\n\t\treturn\n\t}\n\n\tif tries > 0 {\n\t\t// had failures but it's ok, reset the tries on success.\n\t\tb.resetCurrentTries(ctx)\n\t}\n\n\tb.mu.RLock()\n\texpiresAt, ok := b.credentials[fullUser]\n\tb.mu.RUnlock()\n\tvar authorizedAt time.Time\n\tif ok {\n\t\tif expiresAt != nil { // Has expiration.\n\t\t\tif expiresAt.Before(time.Now()) { // Has been expired.\n\t\t\t\tb.mu.Lock() // Delete the entry.\n\t\t\t\tdelete(b.credentials, fullUser)\n\t\t\t\tb.mu.Unlock()\n\n\t\t\t\t// Re-ask for new credentials.\n\t\t\t\tb.handleError(ctx, ErrCredentialsExpired{\n\t\t\t\t\tUsername:                username,\n\t\t\t\t\tPassword:                password,\n\t\t\t\t\tAuthenticateHeader:      b.authenticateHeader,\n\t\t\t\t\tAuthenticateHeaderValue: b.authenticateHeaderValue,\n\t\t\t\t\tCode:                    b.askCode,\n\t\t\t\t})\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// It's ok, find the time authorized to fill the user below, if necessary.\n\t\t\tauthorizedAt = expiresAt.Add(-b.opts.MaxAge)\n\t\t}\n\t} else {\n\t\t// Saved credential not found, first login.\n\t\tif b.opts.MaxAge > 0 { // Expiration is enabled, set the value.\n\t\t\tauthorizedAt = time.Now()\n\t\t\tt := authorizedAt.Add(b.opts.MaxAge)\n\t\t\texpiresAt = &t\n\t\t}\n\t\tb.mu.Lock()\n\t\tb.credentials[fullUser] = expiresAt\n\t\tb.mu.Unlock()\n\t}\n\n\tif user == nil {\n\t\t// No custom uset was set by the auth func,\n\t\t// it is passed though, set a simple user here:\n\t\tuser = &context.SimpleUser{\n\t\t\tAuthorization: authorizationType,\n\t\t\tAuthorizedAt:  authorizedAt,\n\t\t\tID:            username,\n\t\t\tUsername:      username,\n\t\t\tPassword:      password,\n\t\t}\n\t}\n\n\t// Store user instance and logout function.\n\t// Note that the end-developer has always have access\n\t// to the Request.BasicAuth, however, we support any user struct,\n\t// so we must store it on this request instance so it can be retrieved later on.\n\tctx.SetUser(user)\n\tctx.SetLogoutFunc(b.logout)\n\n\tctx.Next()\n}\n\n// logout clears the current user's credentials.\nfunc (b *BasicAuth) logout(ctx *context.Context) {\n\tvar (\n\t\tfullUser, username, password string\n\t\tok                           bool\n\t)\n\n\tif u := ctx.User(); u != nil { // Get the saved ones, if any.\n\t\tusername, _ = u.GetUsername()\n\t\tpassword, _ = u.GetPassword()\n\t\tfullUser = username + colonLiteral + password\n\t\tok = username != \"\" && password != \"\"\n\t}\n\n\tif !ok {\n\t\t// If the custom user does\n\t\t// not implement the User interface, then extract from the request header (most common scenario):\n\t\theader := ctx.GetHeader(b.authorizationHeader)\n\t\tfullUser, _, _, ok = decodeHeader(header)\n\t}\n\n\tif ok { // If it's authorized then try to lock and delete.\n\t\tctx.SetUser(nil)\n\t\tctx.SetLogoutFunc(nil)\n\n\t\tif b.opts.Proxy {\n\t\t\tctx.Request().Header.Del(proxyAuthorizationHeaderKey)\n\t\t}\n\t\t// delete the request header so future Request().BasicAuth are empty.\n\t\tctx.Request().Header.Del(authorizationHeaderKey)\n\n\t\tb.mu.Lock()\n\t\tdelete(b.credentials, fullUser)\n\t\tb.mu.Unlock()\n\n\t\tif b.opts.MaxTries > 0 {\n\t\t\tb.setCurrentTries(ctx, 0)\n\t\t}\n\n\t\tctx.StatusCode(http.StatusUnauthorized)\n\t}\n}\n\n// runGC runs a function in a separate go routine\n// every x duration to clear in-memory expired credential entries.\nfunc (b *BasicAuth) runGC(ctx stdContext.Context, every time.Duration) {\n\tif ctx == nil {\n\t\tctx = stdContext.Background()\n\t}\n\n\tt := time.NewTicker(every)\n\tdefer t.Stop()\n\n\tfor {\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\treturn\n\t\tcase <-t.C:\n\t\t\tb.gc()\n\t\t}\n\t}\n}\n\n// gc removes all entries expired based on the max age or all entries (if max age is missing),\n// note that this does not mean that the server will send 401/407 to the next request,\n// when the request header credentials are still valid (Allow passed).\nfunc (b *BasicAuth) gc() int {\n\tnow := time.Now()\n\tvar markedForDeletion []string\n\n\tb.mu.RLock()\n\tfor fullUser, expiresAt := range b.credentials {\n\t\tif expiresAt == nil || expiresAt.Before(now) {\n\t\t\tmarkedForDeletion = append(markedForDeletion, fullUser)\n\t\t}\n\t}\n\tb.mu.RUnlock()\n\n\tn := len(markedForDeletion)\n\tif n > 0 {\n\t\tfor _, fullUser := range markedForDeletion {\n\t\t\tb.mu.Lock()\n\t\t\tdelete(b.credentials, fullUser)\n\t\t\tb.mu.Unlock()\n\t\t}\n\t}\n\n\treturn n\n}\n"
  },
  {
    "path": "middleware/basicauth/basicauth_test.go",
    "content": "// Package basicauth_tests performs black-box testing of the basicauth middleware.\n// Note that, a secondary test is also available at: _examples/auth/basicauth/main_test.go\npackage basicauth_test\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/httptest\"\n\t\"github.com/kataras/iris/v12/middleware/basicauth\"\n)\n\nfunc TestBasicAuthUseRouter(t *testing.T) {\n\tapp := iris.New()\n\tusers := map[string]string{\n\t\t\"usr\":   \"pss\",\n\t\t\"admin\": \"admin\",\n\t}\n\n\tauth := basicauth.New(basicauth.Options{\n\t\tAllow:    basicauth.AllowUsers(users),\n\t\tRealm:    basicauth.DefaultRealm,\n\t\tMaxTries: 1,\n\t})\n\n\tapp.UseRouter(auth)\n\n\tapp.Get(\"/user_json\", func(ctx iris.Context) {\n\t\tctx.JSON(ctx.User())\n\t})\n\n\tapp.Get(\"/user_string\", func(ctx iris.Context) {\n\t\tuser := ctx.User()\n\n\t\tauthorization, _ := user.GetAuthorization()\n\t\tusername, _ := user.GetUsername()\n\t\tpassword, _ := user.GetPassword()\n\t\tctx.Writef(\"%s\\n%s\\n%s\", authorization, username, password)\n\t})\n\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tusername, _, _ := ctx.Request().BasicAuth()\n\t\tctx.Writef(\"Hello, %s!\", username)\n\t})\n\n\tapp.Subdomain(\"static\").Get(\"/\", func(ctx iris.Context) {\n\t\tusername, _, _ := ctx.Request().BasicAuth()\n\t\tctx.Writef(\"Static, %s\", username)\n\t})\n\n\tresetWithUseRouter := app.Subdomain(\"reset_with_use_router\").ResetRouterFilters()\n\tresetWithUseRouter.UseRouter(func(ctx iris.Context) {\n\t\tctx.Record()\n\t\tctx.Writef(\"with use router\\n\")\n\t\tctx.Next()\n\t})\n\tresetWithUseRouter.Get(\"/\", func(ctx iris.Context) {\n\t\tusername, _, _ := ctx.Request().BasicAuth()\n\t\tctx.Writef(\"%s\", username) // username should be empty.\n\t})\n\t// ^ order of these should not matter.\n\tapp.Subdomain(\"reset\").ResetRouterFilters().Get(\"/\", func(ctx iris.Context) {\n\t\tusername, _, _ := ctx.Request().BasicAuth()\n\t\tctx.Writef(\"%s\", username) // username should be empty.\n\t})\n\n\te := httptest.New(t, app.Configure(\n\t\tiris.WithFireMethodNotAllowed,\n\t\tiris.WithResetOnFireErrorCode,\n\t))\n\n\tfor username, password := range users {\n\t\t// Test pass authentication and route found.\n\t\te.GET(\"/\").WithBasicAuth(username, password).Expect().\n\t\t\tStatus(httptest.StatusOK).Body().IsEqual(fmt.Sprintf(\"Hello, %s!\", username))\n\t\te.GET(\"/user_json\").WithBasicAuth(username, password).Expect().\n\t\t\tStatus(httptest.StatusOK).JSON().Object().ContainsMap(iris.Map{\n\t\t\t\"username\": username,\n\t\t})\n\t\te.GET(\"/user_string\").WithBasicAuth(username, password).Expect().\n\t\t\tStatus(httptest.StatusOK).Body().\n\t\t\tEqual(fmt.Sprintf(\"%s\\n%s\\n%s\", \"Basic Authentication\", username, password))\n\n\t\t// Test empty auth.\n\t\te.GET(\"/\").Expect().Status(httptest.StatusUnauthorized).Body().IsEqual(\"Unauthorized\")\n\t\t// Test invalid auth.\n\t\te.GET(\"/\").WithBasicAuth(username, \"invalid_password\").Expect().\n\t\t\tStatus(httptest.StatusForbidden)\n\t\te.GET(\"/\").WithBasicAuth(\"invaid_username\", password).Expect().\n\t\t\tStatus(httptest.StatusForbidden)\n\n\t\t// Test different method, it should pass the authentication (no stop on 401)\n\t\t// but it doesn't fire the GET route, instead it gives 405.\n\t\te.POST(\"/\").WithBasicAuth(username, password).Expect().\n\t\t\tStatus(httptest.StatusMethodNotAllowed).Body().IsEqual(\"Method Not Allowed\")\n\n\t\t// Test pass the authentication but route not found.\n\t\te.GET(\"/notfound\").WithBasicAuth(username, password).Expect().\n\t\t\tStatus(httptest.StatusNotFound).Body().IsEqual(\"Not Found\")\n\n\t\t// Test empty auth.\n\t\te.GET(\"/notfound\").Expect().Status(httptest.StatusUnauthorized).Body().IsEqual(\"Unauthorized\")\n\t\t// Test invalid auth.\n\t\te.GET(\"/notfound\").WithBasicAuth(username, \"invalid_password\").Expect().\n\t\t\tStatus(httptest.StatusForbidden)\n\t\te.GET(\"/notfound\").WithBasicAuth(\"invaid_username\", password).Expect().\n\t\t\tStatus(httptest.StatusForbidden)\n\n\t\t// Test subdomain inherited.\n\t\tsub := e.Builder(func(req *httptest.Request) {\n\t\t\treq.WithURL(\"http://static.mydomain.com\")\n\t\t})\n\n\t\t// Test pass and route found.\n\t\tsub.GET(\"/\").WithBasicAuth(username, password).Expect().\n\t\t\tStatus(httptest.StatusOK).Body().IsEqual(fmt.Sprintf(\"Static, %s\", username))\n\n\t\t// Test empty auth.\n\t\tsub.GET(\"/\").Expect().Status(httptest.StatusUnauthorized)\n\t\t// Test invalid auth.\n\t\tsub.GET(\"/\").WithBasicAuth(username, \"invalid_password\").Expect().\n\t\t\tStatus(httptest.StatusForbidden)\n\t\tsub.GET(\"/\").WithBasicAuth(\"invaid_username\", password).Expect().\n\t\t\tStatus(httptest.StatusForbidden)\n\n\t\t// Test pass the authentication but route not found.\n\t\tsub.GET(\"/notfound\").WithBasicAuth(username, password).Expect().\n\t\t\tStatus(httptest.StatusNotFound).Body().IsEqual(\"Not Found\")\n\n\t\t// Test empty auth.\n\t\tsub.GET(\"/notfound\").Expect().Status(httptest.StatusUnauthorized).Body().IsEqual(\"Unauthorized\")\n\t\t// Test invalid auth.\n\t\tsub.GET(\"/notfound\").WithBasicAuth(username, \"invalid_password\").Expect().\n\t\t\tStatus(httptest.StatusForbidden)\n\t\tsub.GET(\"/notfound\").WithBasicAuth(\"invaid_username\", password).Expect().\n\t\t\tStatus(httptest.StatusForbidden)\n\n\t\t// Test a reset-ed Party with a single one UseRouter\n\t\t// which writes on matched routes and reset and send the error on errors.\n\t\t// (all should pass without auth).\n\t\tsub = e.Builder(func(req *httptest.Request) {\n\t\t\treq.WithURL(\"http://reset_with_use_router.mydomain.com\")\n\t\t})\n\t\tsub.GET(\"/\").Expect().Status(httptest.StatusOK).Body().IsEqual(\"with use router\\n\")\n\t\tsub.POST(\"/\").Expect().Status(httptest.StatusMethodNotAllowed).Body().IsEqual(\"Method Not Allowed\")\n\t\tsub.GET(\"/notfound\").Expect().Status(httptest.StatusNotFound).Body().IsEqual(\"Not Found\")\n\n\t\t// Test a reset-ed Party (all should pass without auth).\n\t\tsub = e.Builder(func(req *httptest.Request) {\n\t\t\treq.WithURL(\"http://reset.mydomain.com\")\n\t\t})\n\t\tsub.GET(\"/\").Expect().Status(httptest.StatusOK).Body().IsEmpty()\n\t\tsub.POST(\"/\").Expect().Status(httptest.StatusMethodNotAllowed).Body().IsEqual(\"Method Not Allowed\")\n\t\tsub.GET(\"/notfound\").Expect().Status(httptest.StatusNotFound).Body().IsEqual(\"Not Found\")\n\t}\n}\n"
  },
  {
    "path": "middleware/basicauth/error.go",
    "content": "package basicauth\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/context\"\n)\n\ntype (\n\t// ErrHTTPVersion is fired when Options.HTTPSOnly was enabled\n\t// and the current request is a plain http one.\n\tErrHTTPVersion struct{}\n\n\t// ErrCredentialsForbidden is fired when Options.MaxTries have been consumed\n\t// by the user and the client is forbidden to retry at least for \"Age\" time.\n\tErrCredentialsForbidden struct {\n\t\tUsername string\n\t\tPassword string\n\t\tTries    int\n\t\tAge      time.Duration\n\t}\n\n\t// ErrCredentialsMissing is fired when the authorization header is empty or malformed.\n\tErrCredentialsMissing struct {\n\t\tHeader string\n\n\t\tAuthenticateHeader      string\n\t\tAuthenticateHeaderValue string\n\t\tCode                    int\n\t}\n\n\t// ErrCredentialsInvalid is fired when the user input does not match with an existing user.\n\tErrCredentialsInvalid struct {\n\t\tUsername     string\n\t\tPassword     string\n\t\tCurrentTries int\n\n\t\tAuthenticateHeader      string\n\t\tAuthenticateHeaderValue string\n\t\tCode                    int\n\t}\n\n\t// ErrCredentialsExpired is fired when the username:password combination is valid\n\t// but the memory stored user has been expired.\n\tErrCredentialsExpired struct {\n\t\tUsername string\n\t\tPassword string\n\n\t\tAuthenticateHeader      string\n\t\tAuthenticateHeaderValue string\n\t\tCode                    int\n\t}\n)\n\nfunc (e ErrHTTPVersion) Error() string {\n\treturn \"http version not supported\"\n}\n\nfunc (e ErrCredentialsForbidden) Error() string {\n\treturn fmt.Sprintf(\"credentials: forbidden <%s:%s> for <%s> after <%d> attempts\", e.Username, e.Password, e.Age, e.Tries)\n}\n\nfunc (e ErrCredentialsMissing) Error() string {\n\tif e.Header != \"\" {\n\t\treturn fmt.Sprintf(\"credentials: malformed <%s>\", e.Header)\n\t}\n\n\treturn \"empty credentials\"\n}\n\nfunc (e ErrCredentialsInvalid) Error() string {\n\treturn fmt.Sprintf(\"credentials: invalid <%s:%s> current tries <%d>\", e.Username, e.Password, e.CurrentTries)\n}\n\nfunc (e ErrCredentialsExpired) Error() string {\n\treturn fmt.Sprintf(\"credentials: expired <%s:%s>\", e.Username, e.Password)\n}\n\n// DefaultErrorHandler is the default error handler for the Options.ErrorHandler field.\nfunc DefaultErrorHandler(ctx *context.Context, err error) {\n\tswitch e := err.(type) {\n\tcase ErrHTTPVersion:\n\t\tctx.StopWithStatus(http.StatusHTTPVersionNotSupported)\n\tcase ErrCredentialsForbidden:\n\t\t// If a (proxy) server receives valid credentials that are inadequate to access a given resource,\n\t\t// the server should respond with the 403 Forbidden status code.\n\t\t// Unlike 401 Unauthorized or 407 Proxy Authentication Required, authentication is impossible for this user.\n\t\tctx.StopWithStatus(http.StatusForbidden)\n\tcase ErrCredentialsMissing:\n\t\tunauthorize(ctx, e.AuthenticateHeader, e.AuthenticateHeaderValue, e.Code)\n\tcase ErrCredentialsInvalid:\n\t\tunauthorize(ctx, e.AuthenticateHeader, e.AuthenticateHeaderValue, e.Code)\n\tcase ErrCredentialsExpired:\n\t\tunauthorize(ctx, e.AuthenticateHeader, e.AuthenticateHeaderValue, e.Code)\n\tdefault:\n\t\t// This will never happen.\n\t\tctx.StopWithText(http.StatusInternalServerError, fmt.Sprintf(\"unknown error: %v\", err))\n\t}\n}\n\n// unauthorize sends a 401 status code (or 407 if Proxy was set to true)\n// which client should catch and prompt for username:password credentials.\nfunc unauthorize(ctx *context.Context, authHeader, authHeaderValue string, code int) {\n\tctx.Header(authHeader, authHeaderValue)\n\tctx.StopWithStatus(code)\n}\n"
  },
  {
    "path": "middleware/basicauth/header.go",
    "content": "package basicauth\n\nimport (\n\t\"encoding/base64\"\n\t\"strings\"\n)\n\nconst (\n\tspaceChar            = ' '\n\tcolonChar            = ':'\n\tcolonLiteral         = string(colonChar)\n\tbasicLiteral         = \"Basic\"\n\tbasicSpaceLiteral    = \"Basic \"\n\tbasicSpaceLiteralLen = len(basicSpaceLiteral)\n)\n\n// The username and password are combined with a single colon (:).\n// This means that the username itself cannot contain a colon.\n// URL encoding (e.g. https://Aladdin:OpenSesame@www.example.com/index.html)\n// has been deprecated by rfc3986.\nfunc encodeHeader(username, password string) (string, bool) {\n\tif strings.Contains(username, colonLiteral) || strings.Contains(password, colonLiteral) {\n\t\treturn \"\", false\n\t}\n\tfullUser := []byte(username + colonLiteral + password)\n\theader := basicSpaceLiteral + base64.StdEncoding.EncodeToString(fullUser)\n\n\treturn header, true\n}\n\n// Like net/http.parseBasicAuth\nfunc decodeHeader(header string) (fullUser, username, password string, ok bool) {\n\tif len(header) < basicSpaceLiteralLen || !strings.EqualFold(header[:basicSpaceLiteralLen], basicSpaceLiteral) {\n\t\treturn\n\t}\n\n\tc, err := base64.StdEncoding.DecodeString(header[basicSpaceLiteralLen:])\n\tif err != nil {\n\t\treturn\n\t}\n\n\tcs := string(c)\n\ts := strings.IndexByte(cs, colonChar)\n\tif s < 0 {\n\t\treturn\n\t}\n\treturn cs, cs[:s], cs[s+1:], true\n\n\t/*\n\t\tfor i := 0; i < n; i++ {\n\t\t\tif header[i] == spaceChar {\n\t\t\t\tprefix := header[:i]\n\t\t\t\tif prefix != basicLiteral {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tif n <= i+1 {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tdecodedFullUser, err := base64.RawStdEncoding.DecodeString(header[i+1:])\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tfullUser = string(decodedFullUser)\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tn = len(fullUser)\n\t\tfor i := n - 1; i > -1; i-- {\n\t\t\tif fullUser[i] == colonChar {\n\t\t\t\tusername = fullUser[:i]\n\t\t\t\tpassword = fullUser[i+1:]\n\n\t\t\t\tif strings.TrimSpace(username) == \"\" || strings.TrimSpace(password) == \"\" {\n\t\t\t\t\tok = false\n\t\t\t\t} else {\n\t\t\t\t\tok = true\n\t\t\t\t}\n\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\treturn*/\n}\n"
  },
  {
    "path": "middleware/basicauth/header_test.go",
    "content": "package basicauth\n\nimport \"testing\"\n\nfunc TestHeaderEncode(t *testing.T) {\n\tvar tests = []struct {\n\t\tusername string\n\t\tpassword string\n\t\theader   string\n\t\tok       bool\n\t}{\n\t\t{\n\t\t\tusername: \"user\",\n\t\t\tpassword: \"pass\",\n\t\t\theader:   \"Basic dXNlcjpwYXNz\",\n\t\t\tok:       true,\n\t\t},\n\t\t{\n\t\t\tusername: \"user\",\n\t\t\tpassword: \"p:(notallowed)ass\",\n\t\t\theader:   \"\",\n\t\t\tok:       false,\n\t\t},\n\t\t{\n\t\t\tusername: \"123u%ser\",\n\t\t\tpassword: \"pass132$\",\n\t\t\theader:   \"Basic MTIzdSVzZXI6cGFzczEzMiQ=\",\n\t\t\tok:       true,\n\t\t},\n\t}\n\n\tfor i, tt := range tests {\n\t\tgot, ok := encodeHeader(tt.username, tt.password)\n\t\tif tt.ok != ok {\n\t\t\tt.Fatalf(\"[%d] expected: %v but got: %v (username=%s,password=%s)\", i, tt.ok, ok, tt.username, tt.password)\n\t\t}\n\t\tif tt.header != got {\n\t\t\tt.Fatalf(\"[%d] expected result header: %q but got: %q\", i, tt.header, got)\n\t\t}\n\t}\n}\n\nfunc TestHeaderDecode(t *testing.T) {\n\tvar tests = []struct {\n\t\theader   string\n\t\tok       bool\n\t\tusername string\n\t\tpassword string\n\t}{\n\t\t{\n\t\t\theader:   \"Basic dXNlcjpwYXNz\",\n\t\t\tok:       true,\n\t\t\tusername: \"user\",\n\t\t\tpassword: \"pass\",\n\t\t},\n\t\t{\n\t\t\theader: \"dXNlcjpwYXNz\",\n\t\t\tok:     false,\n\t\t},\n\t\t{\n\t\t\theader: \"Basic \",\n\t\t\tok:     false,\n\t\t},\n\t\t{\n\t\t\theader: \"Basic dXNlcjp\",\n\t\t\tok:     false,\n\t\t},\n\t\t{\n\t\t\theader: \"dXNlcjpwYXNz Basic\",\n\t\t\tok:     false,\n\t\t},\n\t\t{\n\t\t\theader: \"dXNlcjpwYXNzBasic\",\n\t\t\tok:     false,\n\t\t},\n\t}\n\n\tfor i, tt := range tests {\n\t\tfullUser, username, password, ok := decodeHeader(tt.header)\n\t\tif expected, got := tt.ok, ok; expected != got {\n\t\t\tt.Fatalf(\"[%d] expected: %v but got: %v (header=%s)\", i, expected, got, tt.header)\n\t\t}\n\n\t\tif expected, got := tt.username, username; expected != got {\n\t\t\tt.Fatalf(\"[%d] expected username: %q but got: %q\", i, expected, got)\n\t\t}\n\n\t\tif expected, got := tt.password, password; expected != got {\n\t\t\tt.Fatalf(\"[%d] expected password: %q but got: %q\", i, expected, got)\n\t\t}\n\n\t\tif tt.username != \"\" || tt.password != \"\" {\n\t\t\tif expected, got := tt.username+colonLiteral+tt.password, fullUser; expected != got {\n\t\t\t\tt.Fatalf(\"[%d] expected username:password to be: %q but got: %q\", i, expected, got)\n\t\t\t}\n\t\t} else {\n\t\t\tif fullUser != \"\" {\n\t\t\t\tt.Fatalf(\"[%d] expected username:password to be empty but got: %q\", i, fullUser)\n\t\t\t}\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "middleware/basicauth/user.go",
    "content": "package basicauth\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"os\"\n\t\"reflect\"\n\t\"strings\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\n\t\"golang.org/x/crypto/bcrypt\"\n\t\"gopkg.in/yaml.v3\"\n)\n\n// ReadFile can be used to customize the way the\n// AllowUsersFile function is loading the filename from.\n// Example of usage: embedded users.yml file.\n// Defaults to the `os.ReadFile` which reads the file from the physical disk.\nvar ReadFile = os.ReadFile\n\n// User is a partial part of the iris.User interface.\n// It's used to declare a static slice of registered User for authentication.\ntype User interface {\n\tcontext.UserGetUsername\n\tcontext.UserGetPassword\n}\n\n// UserAuthOptions holds optional user authentication options\n// that can be given to the builtin Default and Load (and AllowUsers, AllowUsersFile) functions.\ntype UserAuthOptions struct {\n\t// Defaults to plain check, can be modified for encrypted passwords,\n\t// see the BCRYPT optional function.\n\tComparePassword func(stored, userPassword string) bool\n}\n\n// UserAuthOption is the option function type\n// for the Default and Load (and AllowUsers, AllowUsersFile) functions.\n//\n// See BCRYPT for an implementation.\ntype UserAuthOption func(*UserAuthOptions)\n\n// BCRYPT it is a UserAuthOption, it compares a bcrypt hashed password with its user input.\n// Reports true on success and false on failure.\n//\n// Useful when the users passwords are encrypted\n// using the Provos and Mazières's bcrypt adaptive hashing algorithm.\n// See https://www.usenix.org/legacy/event/usenix99/provos/provos.pdf.\n//\n// Usage:\n//\n//\tDefault(..., BCRYPT) OR\n//\tLoad(..., BCRYPT) OR\n//\tOptions.Allow = AllowUsers(..., BCRYPT) OR\n//\tOPtions.Allow = AllowUsersFile(..., BCRYPT)\nfunc BCRYPT(opts *UserAuthOptions) {\n\topts.ComparePassword = func(stored, userPassword string) bool {\n\t\terr := bcrypt.CompareHashAndPassword([]byte(stored), []byte(userPassword))\n\t\treturn err == nil\n\t}\n}\n\nfunc toUserAuthOptions(opts []UserAuthOption) (options UserAuthOptions) {\n\tfor _, opt := range opts {\n\t\topt(&options)\n\t}\n\n\tif options.ComparePassword == nil {\n\t\toptions.ComparePassword = func(stored, userPassword string) bool {\n\t\t\treturn stored == userPassword\n\t\t}\n\t}\n\n\treturn options\n}\n\n// AllowUsers is an AuthFunc which authenticates user input based on a (static) user list.\n// The \"users\" input parameter can be one of the following forms:\n//\n//\tmap[string]string e.g. {username: password, username: password...}.\n//\t[]map[string]any e.g. []{\"username\": \"...\", \"password\": \"...\", \"other_field\": ...}, ...}.\n//\t[]T which T completes the User interface.\n//\t[]T which T contains at least Username and Password fields.\n//\n// Usage:\n// New(Options{Allow: AllowUsers(..., [BCRYPT])})\nfunc AllowUsers(users any, opts ...UserAuthOption) AuthFunc {\n\t// create a local user structure to be used in the map copy,\n\t// takes longer to initialize but faster to serve.\n\ttype user struct {\n\t\tpassword string\n\t\tref      any\n\t}\n\tcp := make(map[string]*user)\n\n\tv := reflect.Indirect(reflect.ValueOf(users))\n\tswitch v.Kind() {\n\tcase reflect.Slice:\n\t\tfor i := 0; i < v.Len(); i++ {\n\t\t\telem := v.Index(i).Interface()\n\t\t\t// MUST contain a username and password.\n\t\t\tusername, password, ok := extractUsernameAndPassword(elem)\n\t\t\tif !ok {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tcp[username] = &user{\n\t\t\t\tpassword: password,\n\t\t\t\tref:      elem,\n\t\t\t}\n\t\t}\n\tcase reflect.Map:\n\t\telem := v.Interface()\n\t\tswitch m := elem.(type) {\n\t\tcase map[string]string:\n\t\t\treturn userMap(m, opts...)\n\t\tcase map[string]any:\n\t\t\tusername, password, ok := mapUsernameAndPassword(m)\n\t\t\tif !ok {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tcp[username] = &user{\n\t\t\t\tpassword: password,\n\t\t\t\tref:      m,\n\t\t\t}\n\t\tdefault:\n\t\t\tpanic(fmt.Sprintf(\"unsupported type of map: %T\", users))\n\t\t}\n\tdefault:\n\t\tpanic(fmt.Sprintf(\"unsupported type: %T\", users))\n\t}\n\n\toptions := toUserAuthOptions(opts)\n\n\treturn func(_ *context.Context, username, password string) (any, bool) {\n\t\tif u, ok := cp[username]; ok { // fast map access,\n\t\t\tif options.ComparePassword(u.password, password) {\n\t\t\t\treturn u.ref, true\n\t\t\t}\n\t\t}\n\n\t\treturn nil, false\n\t}\n}\n\nfunc userMap(usernamePassword map[string]string, opts ...UserAuthOption) AuthFunc {\n\toptions := toUserAuthOptions(opts)\n\n\treturn func(_ *context.Context, username, password string) (any, bool) {\n\t\tpass, ok := usernamePassword[username]\n\t\treturn nil, ok && options.ComparePassword(pass, password)\n\t}\n}\n\n// AllowUsersFile is an AuthFunc which authenticates user input based on a (static) user list\n// loaded from a file on initialization.\n//\n// Example Code:\n//\n//\tNew(Options{Allow: AllowUsersFile(\"users.yml\", BCRYPT)})\n//\n// The users.yml file looks like the following:\n//   - username: kataras\n//     password: kataras_pass\n//     age: 27\n//     role: admin\n//   - username: makis\n//     password: makis_password\n//     ...\nfunc AllowUsersFile(jsonOrYamlFilename string, opts ...UserAuthOption) AuthFunc {\n\tvar (\n\t\tusernamePassword map[string]string\n\t\t// no need to support too much forms, this would be for:\n\t\t// \"$username\": { \"password\": \"$pass\", \"other_field\": ...}\n\t\tuserList []map[string]any\n\t)\n\n\tif err := decodeFile(jsonOrYamlFilename, &usernamePassword, &userList); err != nil {\n\t\tpanic(err)\n\t}\n\n\tif len(usernamePassword) > 0 {\n\t\t// JSON Form: { \"$username\":\"$pass\", \"$username\": \"$pass\" }\n\t\t// YAML Form: $username: $pass\n\t\t// \t\t\t  $username: $pass\n\t\treturn userMap(usernamePassword, opts...)\n\t}\n\n\tif len(userList) > 0 {\n\t\t// JSON Form: [{\"username\": \"$username\", \"password\": \"$pass\", \"other_field\": ...}, {\"username\": ...}, ... ]\n\t\t// YAML Form:\n\t\t// - username: $username\n\t\t//   password: $password\n\t\t//   other_field: ...\n\t\treturn AllowUsers(userList, opts...)\n\t}\n\n\tpanic(\"malformed document file: \" + jsonOrYamlFilename)\n}\n\nfunc decodeFile(src string, dest ...any) error {\n\tdata, err := ReadFile(src)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// We use unmarshal instead of file decoder\n\t// as we may need to read it more than once (dests, see below).\n\tvar (\n\t\tunmarshal func(data []byte, v any) error\n\t\text       string\n\t)\n\n\tif idx := strings.LastIndexByte(src, '.'); idx > 0 {\n\t\text = src[idx:]\n\t}\n\n\tswitch ext {\n\tcase \"\", \".json\":\n\t\tunmarshal = json.Unmarshal\n\tcase \".yml\", \".yaml\":\n\t\tunmarshal = yaml.Unmarshal\n\tdefault:\n\t\treturn fmt.Errorf(\"unexpected file extension: %s\", ext)\n\t}\n\n\tvar (\n\t\tok      bool\n\t\tlastErr error\n\t)\n\n\tfor _, d := range dest {\n\t\tif err = unmarshal(data, d); err == nil {\n\t\t\tok = true\n\t\t} else {\n\t\t\tlastErr = err\n\t\t}\n\t}\n\n\tif !ok {\n\t\treturn lastErr\n\t}\n\n\treturn nil // if at least one is succeed we are ok.\n}\n\nfunc extractUsernameAndPassword(s any) (username, password string, ok bool) {\n\tif s == nil {\n\t\treturn\n\t}\n\n\tswitch u := s.(type) {\n\tcase User:\n\t\tusername = u.GetUsername()\n\t\tpassword = u.GetPassword()\n\t\tok = username != \"\" && password != \"\"\n\t\treturn\n\tcase map[string]any:\n\t\treturn mapUsernameAndPassword(u)\n\tdefault:\n\t\tb, err := json.Marshal(u)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\n\t\tvar m map[string]any\n\t\tif err = json.Unmarshal(b, &m); err != nil {\n\t\t\treturn\n\t\t}\n\n\t\treturn mapUsernameAndPassword(m)\n\t}\n}\n\nfunc mapUsernameAndPassword(m map[string]any) (username, password string, ok bool) {\n\t// type of username: password.\n\tif len(m) == 1 {\n\t\tfor username, v := range m {\n\t\t\tif password, ok := v.(string); ok {\n\t\t\t\tok := username != \"\" && password != \"\"\n\t\t\t\treturn username, password, ok\n\t\t\t}\n\t\t}\n\t}\n\n\tvar usernameFound, passwordFound bool\n\n\tfor k, v := range m {\n\t\tswitch k {\n\t\tcase \"username\", \"Username\":\n\t\t\tusername, usernameFound = v.(string)\n\t\tcase \"password\", \"Password\":\n\t\t\tpassword, passwordFound = v.(string)\n\t\t}\n\n\t\tif usernameFound && passwordFound {\n\t\t\tok = true\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn\n}\n"
  },
  {
    "path": "middleware/basicauth/user_test.go",
    "content": "package basicauth\n\nimport (\n\t\"errors\"\n\t\"os\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\n\t\"golang.org/x/crypto/bcrypt\"\n\t\"gopkg.in/yaml.v3\"\n)\n\ntype IUserRepository interface {\n\tGetByUsernameAndPassword(dest any, username, password string) error\n}\n\n// Test a custom implementation of AuthFunc with a user repository.\n// This is a usage example of custom AuthFunc implementation.\nfunc UserRepository(repo IUserRepository, newUserPtr func() any) AuthFunc {\n\treturn func(_ *context.Context, username, password string) (any, bool) {\n\t\tdest := newUserPtr()\n\t\terr := repo.GetByUsernameAndPassword(dest, username, password)\n\t\tif err == nil {\n\t\t\treturn dest, true\n\t\t}\n\n\t\treturn nil, false\n\t}\n}\n\ntype testUser struct {\n\tusername string\n\tpassword string\n\temail    string // custom field.\n}\n\n// GetUsername & Getpassword complete the User interface.\nfunc (u *testUser) GetUsername() string {\n\treturn u.username\n}\n\nfunc (u *testUser) GetPassword() string {\n\treturn u.password\n}\n\ntype testRepo struct {\n\tentries []testUser\n}\n\n// Implements IUserRepository interface.\nfunc (r *testRepo) GetByUsernameAndPassword(dest any, username, password string) error {\n\tfor _, e := range r.entries {\n\t\tif e.username == username && e.password == password {\n\t\t\t*dest.(*testUser) = e\n\t\t\treturn nil\n\t\t}\n\t}\n\n\treturn errors.New(\"invalid credentials\")\n}\n\nfunc TestAllowUserRepository(t *testing.T) {\n\trepo := &testRepo{\n\t\tentries: []testUser{\n\t\t\t{username: \"kataras\", password: \"kataras_pass\", email: \"kataras2006@hotmail.com\"},\n\t\t},\n\t}\n\n\tallow := UserRepository(repo, func() any {\n\t\treturn new(testUser)\n\t})\n\n\tvar tests = []struct {\n\t\tusername string\n\t\tpassword string\n\t\tok       bool\n\t\tuser     *testUser\n\t}{\n\t\t{\n\t\t\tusername: \"kataras\",\n\t\t\tpassword: \"kataras_pass\",\n\t\t\tok:       true,\n\t\t\tuser:     &testUser{username: \"kataras\", password: \"kataras_pass\", email: \"kataras2006@hotmail.com\"},\n\t\t},\n\t\t{\n\t\t\tusername: \"makis\",\n\t\t\tpassword: \"makis_password\",\n\t\t\tok:       false,\n\t\t},\n\t}\n\n\tfor i, tt := range tests {\n\t\tv, ok := allow(nil, tt.username, tt.password)\n\n\t\tif tt.ok != ok {\n\t\t\tt.Fatalf(\"[%d] expected: %v but got: %v (username=%s,password=%s)\", i, tt.ok, ok, tt.username, tt.password)\n\t\t}\n\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\n\t\tu, ok := v.(*testUser)\n\t\tif !ok {\n\t\t\tt.Fatalf(\"[%d] a user should be type of *testUser but got: %#+v (%T)\", i, v, v)\n\t\t}\n\n\t\tif !reflect.DeepEqual(tt.user, u) {\n\t\t\tt.Fatalf(\"[%d] expected user:\\n%#+v\\nbut got:\\n%#+v\", i, tt.user, u)\n\t\t}\n\t}\n}\n\nfunc TestAllowUsers(t *testing.T) {\n\tusers := []User{\n\t\t&testUser{username: \"kataras\", password: \"kataras_pass\", email: \"kataras2006@hotmail.com\"},\n\t}\n\n\tallow := AllowUsers(users)\n\n\tvar tests = []struct {\n\t\tusername string\n\t\tpassword string\n\t\tok       bool\n\t\tuser     *testUser\n\t}{\n\t\t{\n\t\t\tusername: \"kataras\",\n\t\t\tpassword: \"kataras_pass\",\n\t\t\tok:       true,\n\t\t\tuser:     &testUser{username: \"kataras\", password: \"kataras_pass\", email: \"kataras2006@hotmail.com\"},\n\t\t},\n\t\t{\n\t\t\tusername: \"makis\",\n\t\t\tpassword: \"makis_password\",\n\t\t\tok:       false,\n\t\t},\n\t}\n\n\tfor i, tt := range tests {\n\t\tv, ok := allow(nil, tt.username, tt.password)\n\n\t\tif tt.ok != ok {\n\t\t\tt.Fatalf(\"[%d] expected: %v but got: %v (username=%s,password=%s)\", i, tt.ok, ok, tt.username, tt.password)\n\t\t}\n\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\n\t\tu, ok := v.(*testUser)\n\t\tif !ok {\n\t\t\tt.Fatalf(\"[%d] a user should be type of *testUser but got: %#+v (%T)\", i, v, v)\n\t\t}\n\n\t\tif !reflect.DeepEqual(tt.user, u) {\n\t\t\tt.Fatalf(\"[%d] expected user:\\n%#+v\\nbut got:\\n%#+v\", i, tt.user, u)\n\t\t}\n\t}\n}\n\n// Test YAML user loading with b-encrypted passwords.\nfunc TestAllowUsersFile(t *testing.T) {\n\tf, err := os.CreateTemp(\"\", \"*users.yml\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer func() {\n\t\tf.Close()\n\t\tos.Remove(f.Name())\n\t}()\n\n\t// \tf.WriteString(`\n\t// - username: kataras\n\t//   password: kataras_pass\n\t//   age: 27\n\t//   role: admin\n\t// - username: makis\n\t//   password: makis_password\n\t// `)\n\t// This form is supported too, although its features are limited (no custom fields):\n\t// \tf.WriteString(`\n\t// kataras: kataras_pass\n\t// makis: makis_password\n\t// `)\n\n\tvar tests = []struct {\n\t\tusername      string\n\t\tpassword      string // hashed, auto-filled later on.\n\t\tinputPassword string\n\t\tok            bool\n\t\tuser          context.Map\n\t}{\n\t\t{\n\t\t\tusername:      \"kataras\",\n\t\t\tinputPassword: \"kataras_pass\",\n\t\t\tok:            true,\n\t\t\tuser:          context.Map{\"age\": 27, \"role\": \"admin\"}, // username and password are auto-filled in our tests below.\n\t\t},\n\t\t{\n\t\t\tusername:      \"makis\",\n\t\t\tinputPassword: \"makis_password\",\n\t\t\tok:            true,\n\t\t\tuser:          context.Map{},\n\t\t},\n\t\t{\n\t\t\tusername: \"invalid\",\n\t\t\tpassword: \"invalid_pass\",\n\t\t\tok:       false,\n\t\t},\n\t\t{\n\t\t\tusername: \"notvalid\",\n\t\t\tpassword: \"\",\n\t\t\tok:       false,\n\t\t},\n\t}\n\n\t// Write the tests to the users YAML file.\n\tvar usersToWrite []context.Map\n\tfor _, tt := range tests {\n\t\tif tt.ok {\n\t\t\t// store the hashed password.\n\t\t\ttt.password = mustGeneratePassword(t, tt.inputPassword)\n\n\t\t\t// store and write the username and hashed password.\n\t\t\ttt.user[\"username\"] = tt.username\n\t\t\ttt.user[\"password\"] = tt.password\n\n\t\t\t// cannot write it as a stream, write it as a slice.\n\t\t\t// enc.Encode(tt.user)\n\t\t\tusersToWrite = append(usersToWrite, tt.user)\n\t\t}\n\t\t// \tbcrypt.GenerateFromPassword([]byte(\"kataras_pass\"), bcrypt.DefaultCost)\n\t}\n\n\tfileContents, err := yaml.Marshal(usersToWrite)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tf.Write(fileContents)\n\n\t// Build the authentication func.\n\tallow := AllowUsersFile(f.Name(), BCRYPT)\n\tfor i, tt := range tests {\n\t\tv, ok := allow(nil, tt.username, tt.inputPassword)\n\n\t\tif tt.ok != ok {\n\t\t\tt.Fatalf(\"[%d] expected: %v but got: %v (username=%s,password=%s,user=%#+v)\", i, tt.ok, ok, tt.username, tt.inputPassword, v)\n\t\t}\n\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\n\t\tif len(tt.user) == 0 { // when username: password form.\n\t\t\tcontinue\n\t\t}\n\n\t\tu, ok := v.(context.Map)\n\t\tif !ok {\n\t\t\tt.Fatalf(\"[%d] a user loaded from external source or file should be alway type of map[string]any but got: %#+v (%T)\", i, v, v)\n\t\t}\n\n\t\tif expected, got := len(tt.user), len(u); expected != got {\n\t\t\tt.Fatalf(\"[%d] expected user map length to be equal, expected: %d but got: %d\\n%#+v\\n%#+v\", i, expected, got, tt.user, u)\n\t\t}\n\n\t\tfor k, v := range tt.user {\n\t\t\tif u[k] != v {\n\t\t\t\tt.Fatalf(\"[%d] expected user map %q to be %q but got: %q\", i, k, v, u[k])\n\t\t\t}\n\t\t}\n\t}\n\n}\n\nfunc mustGeneratePassword(t *testing.T, userPassword string) string {\n\tt.Helper()\n\thashed, err := bcrypt.GenerateFromPassword([]byte(userPassword), bcrypt.DefaultCost)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\treturn string(hashed)\n}\n"
  },
  {
    "path": "middleware/cors/cors.go",
    "content": "package cors\n\nimport (\n\t\"errors\"\n\t\"net/http\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/context\"\n)\n\nfunc init() {\n\tcontext.SetHandlerName(\"iris/middleware/cors.*\", \"iris.cors\")\n}\n\nvar (\n\t// ErrOriginNotAllowed is given to the error handler\n\t// when the error is caused because an origin was not allowed to pass through.\n\tErrOriginNotAllowed = errors.New(\"origin not allowed\")\n\n\t// AllowAnyOrigin allows all origins to pass.\n\tAllowAnyOrigin = func(_ *context.Context, _ string) bool {\n\t\treturn true\n\t}\n\n\t// DefaultErrorHandler is the default error handler which\n\t// fires forbidden status (403) on disallowed origins.\n\tDefaultErrorHandler = func(ctx *context.Context, _ error) {\n\t\tctx.StopWithStatus(http.StatusForbidden)\n\t}\n\n\t// DefaultOriginExtractor is the default method which\n\t// an origin is extracted. It returns the value of the request's \"Origin\" header\n\t// and always true, means that it allows empty origin headers as well.\n\tDefaultOriginExtractor = func(ctx *context.Context) (string, bool) {\n\t\theader := ctx.GetHeader(originRequestHeader)\n\t\treturn header, true\n\t}\n\n\t// StrictOriginExtractor is an ExtractOriginFunc type\n\t// which is a bit more strictly than the DefaultOriginExtractor.\n\t// It allows only non-empty \"Origin\" header values to be passed.\n\t// If the header is missing, the middleware will not allow the execution\n\t// of the next handler(s).\n\tStrictOriginExtractor = func(ctx *context.Context) (string, bool) {\n\t\theader := ctx.GetHeader(originRequestHeader)\n\t\treturn header, header != \"\"\n\t}\n)\n\ntype (\n\t// ExtractOriginFunc describes the function which should return the request's origin or false.\n\tExtractOriginFunc = func(ctx *context.Context) (string, bool)\n\n\t// AllowOriginFunc describes the function which is called when the\n\t// middleware decides if the request's origin should be allowed or not.\n\tAllowOriginFunc = func(ctx *context.Context, origin string) bool\n\n\t// HandleErrorFunc describes the function which is fired\n\t// when a request by a specific (or empty) origin was not allowed to pass through.\n\tHandleErrorFunc = func(ctx *context.Context, err error)\n\n\t// CORS holds the customizations developers can\n\t// do on the cors middleware.\n\t//\n\t// Read more at: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS.\n\tCORS struct {\n\t\textractOriginFunc ExtractOriginFunc\n\t\tallowOriginFunc   AllowOriginFunc\n\t\terrorHandler      HandleErrorFunc\n\n\t\tallowCredentialsValue string\n\t\texposeHeadersValue    string\n\t\tallowHeadersValue     string\n\t\tallowMethodsValue     string\n\t\tmaxAgeSecondsValue    string\n\t\treferrerPolicyValue   string\n\t}\n)\n\n// New returns the default CORS middleware.\n// For a more advanced type of protection middleware with more options\n// please refer to: https://github.com/iris-contrib/middleware repository instead.\n//\n// Example Code:\n//\n//\t\timport \"github.com/kataras/iris/v12/middleware/cors\"\n//\t import \"github.com/kataras/iris/v12/x/errors\"\n//\n//\t app.UseRouter(cors.New().\n//\t     HandleErrorFunc(func(ctx iris.Context, err error) {\n//\t         errors.FailedPrecondition.Err(ctx, err)\n//\t     }).\n//\t     ExtractOriginFunc(cors.StrictOriginExtractor).\n//\t     ReferrerPolicy(cors.NoReferrerWhenDowngrade).\n//\t     AllowOrigin(\"domain1.com,domain2.com,domain3.com\").\n//\t     Handler())\nfunc New() *CORS {\n\treturn &CORS{\n\t\textractOriginFunc: DefaultOriginExtractor,\n\t\tallowOriginFunc:   AllowAnyOrigin,\n\t\terrorHandler:      DefaultErrorHandler,\n\n\t\tallowCredentialsValue: \"true\",\n\t\texposeHeadersValue:    \"*, Authorization, X-Authorization\",\n\t\tallowHeadersValue:     \"*\",\n\t\t// This field cannot be modified by the end-developer,\n\t\t// as we have another type of controlling the HTTP verbs per handler.\n\t\tallowMethodsValue:   \"*\",\n\t\tmaxAgeSecondsValue:  \"86400\",\n\t\treferrerPolicyValue: NoReferrerWhenDowngrade.String(),\n\t}\n}\n\n// ExtractOriginFunc sets the function which should return the request's origin.\nfunc (c *CORS) ExtractOriginFunc(fn ExtractOriginFunc) *CORS {\n\tc.extractOriginFunc = fn\n\treturn c\n}\n\n// AllowOriginFunc sets the function which decides if an origin(domain) is allowed\n// to continue or not.\n//\n// Read more at: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#access-control-allow-origin.\nfunc (c *CORS) AllowOriginFunc(fn AllowOriginFunc) *CORS {\n\tc.allowOriginFunc = fn\n\treturn c\n}\n\n// AllowOrigin calls the \"AllowOriginFunc\" method\n// and registers a function which accepts any incoming\n// request with origin of the given \"originLine\".\n// The originLine can contain one or more domains separated by comma.\n// See \"AllowOrigins\" to set a list of strings instead.\nfunc (c *CORS) AllowOrigin(originLine string) *CORS {\n\treturn c.AllowOrigins(strings.Split(originLine, \",\")...)\n}\n\n// AllowOriginMatcherFunc sets the allow origin func without iris.Context\n// as its first parameter, i.e. a regular expression.\nfunc (c *CORS) AllowOriginMatcherFunc(fn func(origin string) bool) *CORS {\n\treturn c.AllowOriginFunc(func(ctx *context.Context, origin string) bool {\n\t\treturn fn(origin)\n\t})\n}\n\n// AllowOriginRegex calls the \"AllowOriginFunc\" method\n// and registers a function which accepts any incoming\n// request with origin that matches at least one of the given \"regexpLines\".\nfunc (c *CORS) AllowOriginRegex(regexpLines ...string) *CORS {\n\tmatchers := make([]func(string) bool, 0, len(regexpLines))\n\tfor _, line := range regexpLines {\n\t\tmatcher := regexp.MustCompile(line).MatchString\n\t\tmatchers = append(matchers, matcher)\n\t}\n\n\treturn c.AllowOriginFunc(func(ctx *context.Context, origin string) bool {\n\t\tfor _, m := range matchers {\n\t\t\tif m(origin) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\n\t\treturn false\n\t})\n}\n\n// AllowOrigins calls the \"AllowOriginFunc\" method\n// and registers a function which accepts any incoming\n// request with origin of one of the given \"origins\".\nfunc (c *CORS) AllowOrigins(origins ...string) *CORS {\n\tallowOrigins := make(map[string]struct{}, len(origins)) // read-only at serve time.\n\tfor _, origin := range origins {\n\t\tif origin == \"*\" {\n\t\t\t// If AllowOrigins called with asterix, it is a missuse of this\n\t\t\t// middleware (set AllowAnyOrigin instead).\n\t\t\tallowOrigins = nil\n\t\t\treturn c.AllowOriginFunc(AllowAnyOrigin)\n\t\t\t// panic(\"wildcard is not allowed, use AllowOriginFunc(AllowAnyOrigin) instead\")\n\t\t\t// No ^ let's register a function which allows all and continue.\n\t\t}\n\n\t\torigin = strings.TrimSpace(origin)\n\t\tallowOrigins[origin] = struct{}{}\n\t}\n\n\treturn c.AllowOriginFunc(func(ctx *context.Context, origin string) bool {\n\t\t_, allow := allowOrigins[origin]\n\t\treturn allow\n\t})\n}\n\n// HandleErrorFunc sets the function which is called\n// when an error of origin not allowed is fired.\nfunc (c *CORS) HandleErrorFunc(fn HandleErrorFunc) *CORS {\n\tc.errorHandler = fn\n\treturn c\n}\n\n// DisallowCredentials sets the \"Access-Control-Allow-Credentials\" header to false.\n//\n// Read more at: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#access-control-allow-credentials.\nfunc (c *CORS) DisallowCredentials() *CORS {\n\tc.allowCredentialsValue = \"false\"\n\treturn c\n}\n\n// ExposeHeaders sets the \"Access-Control-Expose-Headers\" header value.\n//\n// Read more at: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#access-control-expose-headers.\nfunc (c *CORS) ExposeHeaders(headers ...string) *CORS {\n\tc.exposeHeadersValue = strings.Join(headers, \", \")\n\treturn c\n}\n\n// AllowHeaders sets the \"Access-Control-Allow-Headers\" header value.\n//\n// Read more at: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#access-control-allow-headers.\nfunc (c *CORS) AllowHeaders(headers ...string) *CORS {\n\tc.allowHeadersValue = strings.Join(headers, \", \")\n\treturn c\n}\n\n// ReferrerPolicy type for referrer-policy header value.\ntype ReferrerPolicy string\n\n// All available referrer policies.\n// Read more at: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy.\nconst (\n\tNoReferrer                  ReferrerPolicy = \"no-referrer\"\n\tNoReferrerWhenDowngrade     ReferrerPolicy = \"no-referrer-when-downgrade\"\n\tOrigin                      ReferrerPolicy = \"origin\"\n\tOriginWhenCrossOrigin       ReferrerPolicy = \"origin-when-cross-origin\"\n\tSameOrigin                  ReferrerPolicy = \"same-origin\"\n\tStrictOrigin                ReferrerPolicy = \"strict-origin\"\n\tStrictOriginWhenCrossOrigin ReferrerPolicy = \"strict-origin-when-cross-origin\"\n\tUnsafeURL                   ReferrerPolicy = \"unsafe-url\"\n)\n\n// String returns the text representation of the \"r\" ReferrerPolicy.\nfunc (r ReferrerPolicy) String() string {\n\treturn string(r)\n}\n\n// ReferrerPolicy sets the \"Referrer-Policy\" header value.\n// Defaults to \"no-referrer-when-downgrade\".\n//\n// Read more at: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy\n// and https://developer.mozilla.org/en-US/docs/Web/Security/Referer_header:_privacy_and_security_concerns.\nfunc (c *CORS) ReferrerPolicy(referrerPolicy ReferrerPolicy) *CORS {\n\tc.referrerPolicyValue = referrerPolicy.String()\n\treturn c\n}\n\n// MaxAge sets the \"Access-Control-Max-Age\" header value.\n//\n// Read more at: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#access-control-max-age.\nfunc (c *CORS) MaxAge(d time.Duration) *CORS {\n\tc.maxAgeSecondsValue = strconv.FormatFloat(d.Seconds(), 'E', -1, 64)\n\treturn c\n}\n\nconst (\n\toriginRequestHeader    = \"Origin\"\n\tallowOriginHeader      = \"Access-Control-Allow-Origin\"\n\tallowCredentialsHeader = \"Access-Control-Allow-Credentials\"\n\treferrerPolicyHeader   = \"Referrer-Policy\"\n\texposeHeadersHeader    = \"Access-Control-Expose-Headers\"\n\trequestMethodHeader    = \"Access-Control-Request-Method\"\n\trequestHeadersHeader   = \"Access-Control-Request-Headers\"\n\tallowMethodsHeader     = \"Access-Control-Allow-Methods\"\n\tallowAllMethodsValue   = \"*\"\n\tallowHeadersHeader     = \"Access-Control-Allow-Headers\"\n\tmaxAgeHeader           = \"Access-Control-Max-Age\"\n\tvaryHeader             = \"Vary\"\n)\n\nfunc (c *CORS) addVaryHeaders(ctx *context.Context) {\n\tctx.Header(varyHeader, originRequestHeader)\n\n\tif ctx.Method() == http.MethodOptions {\n\t\tctx.Header(varyHeader, requestMethodHeader)\n\t\tctx.Header(varyHeader, requestHeadersHeader)\n\t}\n}\n\n// Handler method returns the Iris CORS Handler with basic features.\n// Note that the caller should NOT modify any of the CORS instance fields afterwards.\nfunc (c *CORS) Handler() context.Handler {\n\treturn func(ctx *context.Context) {\n\t\tc.addVaryHeaders(ctx) // add vary headers at any case.\n\n\t\torigin, ok := c.extractOriginFunc(ctx)\n\t\tif !ok || !c.allowOriginFunc(ctx, origin) {\n\t\t\tc.errorHandler(ctx, ErrOriginNotAllowed)\n\t\t\treturn\n\t\t}\n\n\t\tif origin == \"\" { // if we allow empty origins, set it to wildcard.\n\t\t\torigin = \"*\"\n\t\t}\n\n\t\tctx.Header(allowOriginHeader, origin)\n\t\tctx.Header(allowCredentialsHeader, c.allowCredentialsValue)\n\t\t// 08 July 2021 Mozzila updated the following document: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy\n\t\tctx.Header(referrerPolicyHeader, c.referrerPolicyValue)\n\t\tctx.Header(exposeHeadersHeader, c.exposeHeadersValue)\n\t\tif ctx.Method() == http.MethodOptions {\n\t\t\tctx.Header(allowMethodsHeader, allowAllMethodsValue)\n\t\t\tctx.Header(allowHeadersHeader, c.allowHeadersValue)\n\t\t\tctx.Header(maxAgeHeader, c.maxAgeSecondsValue)\n\t\t\tctx.StatusCode(http.StatusNoContent)\n\t\t\treturn\n\t\t}\n\n\t\tctx.Next()\n\t}\n}\n"
  },
  {
    "path": "middleware/grpc/grpc.go",
    "content": "package grpc\n\nimport (\n\t\"net/http\"\n\t\"strings\"\n\n\t\"github.com/kataras/iris/v12/core/router\"\n)\n\n// New returns a new gRPC Iris router wrapper for a gRPC server.\n// useful when you want to share one port (such as 443 for https) between gRPC and Iris.\n//\n// The Iris server SHOULD run under HTTP/2 and clients too.\n//\n// Usage:\n//\n//\timport grpcWrapper \"github.com/kataras/iris/v12/middleware/grpc\"\n//\t[...]\n//\tapp := iris.New()\n//\tgrpcServer := grpc.NewServer()\n//\tapp.WrapRouter(grpcWrapper.New(grpcServer))\nfunc New(grpcServer http.Handler) router.WrapperFunc {\n\treturn func(w http.ResponseWriter, r *http.Request, mux http.HandlerFunc) {\n\t\tif r.ProtoMajor == 2 && strings.HasPrefix(r.Header.Get(\"Content-Type\"), \"application/grpc\") {\n\t\t\tgrpcServer.ServeHTTP(w, r)\n\t\t\treturn\n\t\t}\n\n\t\tmux.ServeHTTP(w, r)\n\t}\n}\n"
  },
  {
    "path": "middleware/hcaptcha/ARTICLE.md",
    "content": "# How to use hCAPTCHA with Iris\n\nIn this article, we will learn how to use hCAPTCHA with Iris, a web framework for Go that provides fast and easy development of web applications. hCAPTCHA is a service that protects websites from bots and spam by presenting challenges to human visitors. By using hCAPTCHA, we can ensure that only real users can access our website and prevent automated attacks.\n\n## What is hCAPTCHA?\n\nhCAPTCHA is a service that provides a widget that can be embedded in any web page. The widget displays a challenge that requires human intelligence to solve, such as identifying objects in images or typing words. The widget communicates with the hCAPTCHA server and verifies if the user has passed the challenge or not. If the user passes the challenge, the widget generates a token that can be used to validate the user's request on the server side.\n\nhCAPTCHA is similar to reCAPTCHA, another popular service that offers similar functionality. However, hCAPTCHA claims to have some advantages over reCAPTCHA, such as:\n\n- Better privacy: hCAPTCHA does not track users across websites or collect personal data.\n- Better performance: hCAPTCHA uses less resources and loads faster than reCAPTCHA.\n- Better rewards: hCAPTCHA pays website owners for using their service and supports various causes and charities.\n\n## How to use hCAPTCHA with Iris?\n\nTo use hCAPTCHA with Iris, we need to do two things:\n\n- Import the `github.com/kataras/iris/v12/middleware/hcaptcha` package, which provides an Iris middleware for hCAPTCHA.\n- Use the `hcaptcha.New` function to create an `iris.Handler` and register it in our Iris app.\n\nFirst, we need to install the Iris Web Framework, which contains the middleware too:\n\n```sh\n$ go get github.com/kataras/iris/v12@latest\n```\n\nThen, we need to import it in our main.go file:\n\n```go\nimport (\n    \"github.com/kataras/iris/v12\"\n    \"github.com/kataras/iris/v12/middleware/hcaptcha\"\n)\n```\n\nNext, we need to use the `hcaptcha.New` function to create an `iris.Handler`. The function takes two arguments: a secret key and, optionally, one or more functions to configure the hCAPTCHA Client instance. The secret key is a string that we can obtain from the hCAPTCHA website after creating an account and registering our website. We'll register an error handler through the second article. The error handler (`FailureHandler`) is a function that handles any errors that may occur during the validation process.\n\nWe can use the following code to create the handler:\n\n```go\n// Replace with your own secret key.\nsecret := \"0x123456789abcdef\"\n\n// Create a hcaptcha middleware with the secret key and a custom error handler.\nhcaptchaMiddleware := hcaptcha.New(secret, func(c *hcaptcha.Client) {\n    c.FailureHandler = func(ctx iris.Context) {\n        // Handle the error as you wish, for example:\n        ctx.StopWithText(iris.StatusBadRequest, \"hcaptcha verification failed: %v\", err)\n    }\n})\n```\n\nThe secret key can be found through: https://dashboard.hcaptcha.com. Please store it on a secure and private place. To test hCAPTCHA on a local environment please read the following instructions at: https://docs.hcaptcha.com/#localdev.\n\nFinally, we need to register the hcaptcha middleware in our Iris app. We can use the `app.UseRouter` method to apply the middleware to all routes, or the `app.Use/UseGlobal` method to apply it to all non-error routes. For example:\n\n```go\n// Create an Iris app instance.\napp := iris.New()\n\n// Apply the hcaptcha middleware to all non-error routes.\napp.Use(hcaptchaMiddleware)\n\n// Or apply the hcaptcha middleware to specific routes.\napp.Get(\"/protected\", hcaptchaMiddleware, func(ctx iris.Context) {\n    // This route is protected by hcaptcha.\n    ctx.WriteString(\"Hello, human!\")\n})\n```\n\n## Conclusion\n\nIn this article, we have learned how to use basic hCAPTCHA middleware with Iris. For a more complete example please navigate through: https://github.com/kataras/iris/tree/main/_examples/auth/hcaptcha. By using these kind of features, we can create a more secure, user-friendly, and robust web application.\n\nI hope you enjoyed this article and found it useful. If you have any questions or feedback, please feel free to leave a comment below. Thank you for reading! 😊\n"
  },
  {
    "path": "middleware/hcaptcha/hcaptcha.go",
    "content": "package hcaptcha\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\n\t\"github.com/kataras/iris/v12/context\"\n)\n\nfunc init() {\n\tcontext.SetHandlerName(\"iris/middleware/hcaptcha.*\", \"iris.hCaptcha\")\n}\n\nvar (\n\t// ResponseContextKey is the default request's context key that response of a hcaptcha request is kept.\n\tResponseContextKey string = \"iris.hcaptcha\"\n\t// DefaultFailureHandler is the default HTTP handler that is fired on hcaptcha failures.\n\t// See `Client.FailureHandler`.\n\tDefaultFailureHandler = func(ctx *context.Context) {\n\t\tctx.StopWithStatus(http.StatusTooManyRequests)\n\t}\n)\n\n// Client represents the hcaptcha client.\ntype Client struct {\n\t// FailureHandler if specified, fired when user does not complete hcaptcha successfully.\n\t// Failure and error codes information are kept as `Response` type\n\t// at the Request's Context key of \"hcaptcha\".\n\t//\n\t// Defaults to a handler that writes a status code of 429 (Too Many Requests)\n\t// and without additional information.\n\tFailureHandler context.Handler\n\n\t// Optional checks for siteverify.\n\t//\n\t// The user's remote IP address.\n\tRemoteIP string\n\t// The sitekey form field you expect to see.\n\tSiteKey string\n\n\tsecret string\n}\n\n// Option declares an option for the hcaptcha client.\n// See `New` package-level function.\ntype Option func(*Client)\n\n// WithRemoteIP sets the remote ip field to the given value.\n// It sends the remoteip form field on \"SiteVerify\".\nfunc WithRemoteIP(remoteIP string) Option {\n\treturn func(c *Client) {\n\t\tc.RemoteIP = remoteIP\n\t}\n}\n\n// WithSiteKey sets the site key field to the given value.\n// It sends the sitekey form field on \"SiteVerify\".\nfunc WithSiteKey(siteKey string) Option {\n\treturn func(c *Client) {\n\t\tc.SiteKey = siteKey\n\t}\n}\n\n// Response is the hcaptcha JSON response.\ntype Response struct {\n\tChallengeTS string   `json:\"challenge_ts\"`\n\tHostname    string   `json:\"hostname\"`\n\tErrorCodes  []string `json:\"error-codes,omitempty\"`\n\tSuccess     bool     `json:\"success\"`\n\tCredit      bool     `json:\"credit,omitempty\"`\n}\n\n// New accepts a hpcatcha secret key and returns a new hcaptcha HTTP Client.\n//\n// Instructions at: https://docs.hcaptcha.com/.\n//\n// See its `Handler` and `SiteVerify` for details.\n// See the `WithRemoteIP` and `WithSiteKey` package-level functions too.\nfunc New(secret string, options ...Option) context.Handler {\n\tc := &Client{\n\t\tFailureHandler: DefaultFailureHandler,\n\t\tsecret:         secret,\n\t}\n\n\tfor _, opt := range options {\n\t\topt(c)\n\t}\n\n\treturn c.Handler\n}\n\n// Handler is the HTTP route middleware featured hcaptcha validation.\n// It calls the `SiteVerify` method and fires the \"next\" when user completed the hcaptcha successfully,\n//\n//\totherwise it calls the Client's `FailureHandler`.\n//\n// The hcaptcha's `Response` (which contains any `ErrorCodes`)\n// is saved on the Request's Context (see `GetResponseFromContext`).\nfunc (c *Client) Handler(ctx *context.Context) {\n\tv := SiteVerify(ctx, c.secret, c.RemoteIP, c.SiteKey)\n\tctx.Values().Set(ResponseContextKey, v)\n\tif v.Success {\n\t\tctx.Next()\n\t\treturn\n\t}\n\n\tif c.FailureHandler != nil {\n\t\tc.FailureHandler(ctx)\n\t}\n}\n\n// responseFormValue = \"h-captcha-response\"\nconst apiURL = \"https://hcaptcha.com/siteverify\"\n\n// SiteVerify accepts an Iris Context and a secret key (https://dashboard.hcaptcha.com/settings).\n// It returns the hcaptcha's `Response`.\n// The `response.Success` reports whether the validation passed.\n// Any errors are passed through the `response.ErrorCodes` field.\n//\n// The remoteIP and siteKey input arguments are optional.\nfunc SiteVerify(ctx *context.Context, secret, remoteIP, siteKey string) (response Response) {\n\tgeneratedResponseID := ctx.FormValue(\"h-captcha-response\")\n\n\tif generatedResponseID == \"\" {\n\t\tresponse.ErrorCodes = append(response.ErrorCodes,\n\t\t\t\"form[h-captcha-response] is empty\")\n\t\treturn\n\t}\n\n\tvalues := url.Values{\n\t\t\"secret\":   {secret},\n\t\t\"response\": {generatedResponseID},\n\t}\n\tif remoteIP != \"\" {\n\t\tvalues.Add(\"remoteip\", remoteIP)\n\t}\n\tif siteKey != \"\" {\n\t\tvalues.Add(\"sitekey\", siteKey)\n\t}\n\n\tresp, err := http.DefaultClient.PostForm(apiURL, values)\n\tif err != nil {\n\t\tresponse.ErrorCodes = append(response.ErrorCodes, err.Error())\n\t\treturn\n\t}\n\n\tbody, err := io.ReadAll(resp.Body)\n\tresp.Body.Close()\n\tif err != nil {\n\t\tresponse.ErrorCodes = append(response.ErrorCodes, err.Error())\n\t\treturn\n\t}\n\n\terr = json.Unmarshal(body, &response)\n\tif err != nil {\n\t\tresponse.ErrorCodes = append(response.ErrorCodes, err.Error())\n\t\treturn\n\t}\n\n\treturn\n}\n\n// Get returns the hcaptcha `Response` of the current request and reports whether was found or not.\nfunc Get(ctx *context.Context) (Response, bool) {\n\tv := ctx.Values().Get(ResponseContextKey)\n\tif v != nil {\n\t\tif response, ok := v.(Response); ok {\n\t\t\treturn response, true\n\t\t}\n\t}\n\n\treturn Response{}, false\n}\n\n// Script is the hCaptcha's javascript source file that should be incldued in the HTML head or body.\nconst Script = \"https://hcaptcha.com/1/api.js\"\n\n// HTMLForm is the default HTML form for clients.\n// It's totally optional, use your own code for the best possible result depending on your web application.\n// See `ParseForm` and `RenderForm` for more.\nvar HTMLForm = `<form action=\"%s\" method=\"POST\">\n\t    <script src=\"%s\"></script>\n\t\t<div class=\"h-captcha\" data-sitekey=\"%s\"></div>\n    \t<input type=\"submit\" name=\"button\" value=\"OK\">\n</form>`\n\n// ParseForm parses the `HTMLForm` with the necessary parameters and returns\n// its result for render.\nfunc ParseForm(dataSiteKey, postActionRelativePath string) string {\n\treturn fmt.Sprintf(HTMLForm, postActionRelativePath, Script, dataSiteKey)\n}\n\n// RenderForm writes the `HTMLForm` to \"w\" response writer.\n// See `_examples/auth/hcaptcha/templates/register_form.html` example for a custom form instead.\nfunc RenderForm(ctx *context.Context, dataSiteKey, postActionRelativePath string) (int, error) {\n\treturn ctx.HTML(ParseForm(dataSiteKey, postActionRelativePath))\n}\n"
  },
  {
    "path": "middleware/jwt/ARTICLE.md",
    "content": "# How to use JWT authentication with Iris\n\nIn this tutorial, we will learn how to use JWT (JSON Web Token) authentication with [Iris](https://www.iris-go.com/), a fast and simple web framework for Go. JWT is a standard for securely transmitting information between parties as a JSON object. It can be used to authenticate users and protect API endpoints from unauthorized access.\n\n## Prerequisites\n\nTo follow this tutorial, you will need:\n\n- Go 1.20 or higher installed on your machine\n- A basic understanding of Go and Iris\n- A text editor or IDE of your choice (e.g. [VS Code](https://code.visualstudio.com/))\n\n## Creating a new Iris project\n\nFirst, we will create a new Iris project using the `go mod` command. To do that, run the following commands:\n\n```bash\n$ mkdir iris-jwt\n$ cd iris-jwt\n$ go mod init iris-jwt\n$ go get github.com/kataras/iris/v12@latest\n```\n\nThis will create a new folder called `iris-jwt` with the following files:\n\n```bash\niris-jwt\n├── go.mod\n└── go.sum\n```\n\nThe `go.mod` file contains the module name and the dependency on Iris. The `go.sum` file contains the checksums of the dependencies.\n\nNext, we will create a file called `main.go` in the same folder and write some basic code to start an Iris server on port 8080. We will modify this file later to add our JWT logic.\n\n```go\npackage main\n\nimport \"github.com/kataras/iris/v12\"\n\nfunc main() {\n    // Create a new Iris application.\n    app := iris.New()\n\n    // Register a simple GET handler at the root path.\n    app.Get(\"/\", func(ctx iris.Context) {\n        ctx.WriteString(\"Hello, world!\")\n    })\n\n    // Start the server at http://localhost:8080.\n    app.Listen(\":8080\")\n}\n```\n\nTo run the server, run the following command:\n\n```bash\n$ go run main.go\n```\n\nYou should see something like this in your terminal:\n\n```bash\nNow listening on: http://localhost:8080\nApplication started. Press CTRL+C to shut down.\n```\n\nYou can also visit http://localhost:8080 in your browser and see the message \"Hello, world!\".\n\n## Defining the user model and the JWT claims\n\nNow, we will define a simple user model and a custom JWT claims struct in our `main.go` file. The user model will represent the data of our users, such as username and password. The JWT claims struct will contain the information that we want to store in our JWT tokens, such as user ID and expiration time.\n\nWe will also define some constants for our secret keys and token expiration durations.\n\n```go\npackage main\n\nimport (\n    \"time\"\n\n    \"github.com/kataras/iris/v12\"\n    \"github.com/kataras/iris/v12/middleware/jwt\"\n)\n\n// User is a simple user model.\ntype User struct {\n    ID       int64  `json:\"id\"`\n    Username string `json:\"username\"`\n    Password string `json:\"password\"`\n}\n\n// Claims is a custom JWT claims struct.\ntype Claims struct {\n    jwt.Claims // embeds standard claims iat, exp and sub.\n    UserID     int64  `json:\"user_id\"`\n}\n\n// Define some constants for our secret keys and token expiration durations.\nconst (\n    accessSecret  = \"my-access-secret\"\n    refreshSecret = \"my-refresh-secret\"\n    accessExpire  = 15 * time.Minute\n    refreshExpire = 24 * time.Hour\n)\n```\n\n## Creating a signer and a verifier for JWT\n\nNext, we will create a signer and a verifier for JWT using the Iris middleware/jwt package. The signer will be used to generate and sign JWT tokens with our secret keys and claims. The verifier will be used to verify and validate JWT tokens from the client requests.\n\nWe will create two signers and one verifiers, one pair for the access token and one signer for the refresh token. We will also use encryption for our tokens to add an extra layer of security.\n\n```go\npackage main\n\nimport (\n    \"time\"\n\n    \"github.com/kataras/iris/v12\"\n    \"github.com/kataras/iris/v12/middleware/jwt\"\n)\n\n// ...\n\n// Create a signer for the access token.\naccessSigner := jwt.NewSigner(jwt.HS256, accessSecret, accessExpire).\n    // Use encryption for the access token.\n    WithEncryption([]byte(\"my-access-encryption-key\"), nil)\n\n// Create a signer for the refresh token.\nrefreshSigner := jwt.NewSigner(jwt.HS256, refreshSecret, refreshExpire).\n    // Use encryption for the refresh token.\n    WithEncryption([]byte(\"my-refresh-encryption-key\"), nil)\n\n// Create a verifier for the access token.\naccessVerifier := jwt.NewVerifier(jwt.HS256, accessSecret).\n    // Use decryption for the access token.\n    WithDecryption([]byte(\"my-access-encryption-key\"), nil)\n    // Use a blocklist to revoke tokens.\n    // .WithDefaultBlocklist()\n```\n\n## Creating some mock users and a login handler\n\nFor the sake of simplicity, we will create some mock users in a map and use them to simulate authentication. In a real application, you would use a database or another storage system to store and retrieve your users.\n\nWe will also create a login handler that will take a username and password from the client request, check if they match with one of our mock users, and if so, generate an access token and a refresh token for that user and send them back to the client.\n\n```go\npackage main\n\nimport (\n    \"time\"\n\n    \"github.com/kataras/iris/v12\"\n    \"github.com/kataras/iris/v12/middleware/jwt\"\n)\n\n// ...\n\n// Create some mock users in a map.\nusers := map[string]User{\n    \"alice\": {ID: 1, Username: \"alice\", Password: \"1234\"},\n    \"bob\":   {ID: 2, Username: \"bob\", Password: \"5678\"},\n}\n\n// Create a login handler that will generate JWT tokens for authenticated users.\nloginHandler := func(ctx iris.Context) {\n    // Get the username and password from the request body.\n    var user User\n    if err := ctx.ReadJSON(&user); err != nil {\n        ctx.StopWithError(iris.StatusBadRequest, err)\n        return\n    }\n\n    // Check if the username and password match with one of our mock users.\n    if u, ok := users[user.Username]; !ok || u.Password != user.Password {\n        ctx.StopWithStatus(iris.StatusUnauthorized)\n        return\n    }\n\n    // Generate an access token with the user ID as the subject claim.\n    accessClaims := Claims{\n        Claims: jwt.Claims{Subject: u.Username},\n        UserID: u.ID,\n    }\n    accessToken, err := accessSigner.Sign(accessClaims)\n    if err != nil {\n        ctx.StopWithError(iris.StatusInternalServerError, err)\n        return\n    }\n\n    // Generate a refresh token with the user ID as the subject claim.\n    refreshClaims := Claims{\n        Claims: jwt.Claims{Subject: u.Username},\n        UserID: u.ID,\n    }\n    refreshToken, err := refreshSigner.Sign(refreshClaims)\n    if err != nil {\n        ctx.StopWithError(iris.StatusInternalServerError, err)\n        return\n    }\n\n    /* OR\n    tokenPair, err := refreshSigner.NewTokenPair(accessClaims, refreshClaims, refreshExpire)\n    // [handle err...]\n    ctx.JSON(tokenPair)\n    */\n\n    // Send the tokens to the client as JSON.\n    ctx.JSON(iris.Map{\n        \"access_token\":  string(accessToken),\n        \"refresh_token\": string(refreshToken),\n        \"expires_in\":    int64(accessExpire.Seconds()),\n    })\n}\n```\n\n## Creating a protected API handler\n\nNext, we will create a protected API handler that will only allow authorized users to access it. We will use the access verifier as a middleware to verify and validate the access token from the client request. If the token is valid, we will extract the user ID from it and send it back to the client as JSON.\n\n```go\npackage main\n\nimport (\n    \"time\"\n\n    \"github.com/kataras/iris/v12\"\n    \"github.com/kataras/iris/v12/middleware/jwt\"\n)\n\n// ...\n\n// Create a protected API handler that will only allow authorized users to access it.\nprotectedHandler := func(ctx iris.Context) {\n    // Get the verified token from the context.\n    token := jwt.GetVerifiedToken(ctx) // important step.\n\n    // Get the custom claims from the token.\n    var claims Claims\n    if err := token.Claims(&claims); err != nil { // important step.\n        ctx.StopWithError(iris.StatusInternalServerError, err)\n        return\n    }\n\n    // Get the user ID from the claims.\n    userID := claims.UserID\n\n    // Send the user ID to the client as JSON.\n    // This is just an example, you can do whatever you want here.\n    ctx.JSON(iris.Map{\n        \"user_id\": userID,\n    })\n}\n```\n\n## Creating a refresh handler\n\nFinally, we will create a refresh handler that will allow users to refresh their access tokens using their refresh tokens. We will use the refresh verifier as a middleware to verify and validate the refresh token from the client request. If the token is valid, we will generate a new access token and a new refresh token for the same user and send them back to the client.\n\n```go\npackage main\n\nimport (\n    \"time\"\n\n    \"github.com/kataras/iris/v12\"\n    \"github.com/kataras/iris/v12/middleware/jwt\"\n)\n\n// ...\n\n// Create a refresh handler that will allow users to refresh their access tokens using their refresh tokens.\nrefreshHandler := func(ctx iris.Context) {\n    // Get the verified token from the context.\n    token := jwt.GetVerifiedToken(ctx)\n\n    // Get the custom claims from the token.\n    var claims Claims\n    if err := token.Claims(&claims); err != nil {\n        ctx.StopWithError(iris.StatusInternalServerError, err)\n        return\n    }\n\n    // Get the user ID and username from the claims.\n    userID := claims.UserID\n    username := claims.Subject\n\n    // Generate a new access token with the same user ID and username as the subject claim.\n    accessClaims := Claims{\n        Claims: jwt.Claims{Subject: username},\n        UserID: userID,\n    }\n    accessToken, err := accessSigner.Sign(accessClaims)\n    if err != nil {\n        ctx.StopWithError(iris.StatusInternalServerError, err)\n        return\n    }\n\n    // Generate a new refresh token with the same user ID and username as the subject claim.\n    refreshClaims := Claims{\n        Claims: jwt.Claims{Subject: username},\n        UserID: userID,\n    }\n    refreshToken, err := refreshSigner.Sign(refreshClaims)\n    if err != nil {\n        ctx.StopWithError(iris.StatusInternalServerError, err)\n        return\n    }\n\n    // Send the new tokens to the client as JSON.\n    ctx.JSON(iris.Map{\n        \"access_token\":  string(accessToken),\n        \"refresh_token\": string(refreshToken),\n        \"expires_in\":    int64(accessExpire.Seconds()),\n    })\n}\n```\n\n## Registering the handlers and testing the application\n\nNow that we have created all our handlers, we can register them with our Iris application and test our application. We will use the `app.Post` method to register our login, protected, and refresh handlers with different paths. We will also use the `accessVerifier.Verify` method to register our verifiers as middlewares for the rest of the protected handlers.\n\n```go\npackage main\n\nimport (\n    \"time\"\n\n    \"github.com/kataras/iris/v12\"\n    \"github.com/kataras/iris/v12/middleware/jwt\"\n)\n\n// ...\n\nfunc main() {\n    // Create a new Iris application.\n    app := iris.New()\n\n    // Register our login handler at /login path.\n    app.Post(\"/login\", loginHandler)\n\n    // Register a single protected handler at /protected path.\n    // This handler verifies the request manually as we've seen above.\n    app.Post(\"/protected\", protectedHandler)\n\n    // Register our refresh handler at /refresh path with refresh signer.\n    app.Post(\"/refresh\", refreshHandler)\n\n    // You can also register the pre-defined jwt middleware for all protected routes\n    // which performs verification automatically and set the custom Claims to the Context\n    // for next handlers to use through: claims := jwt.Get(ctx).(*Claims).\n    app.Use(accessVerifier.Verify(func() any {\n\t\treturn new(Claims)\n\t}))\n\n    // [more routes...]\n\n    // Start the server at http://localhost:8080.\n    app.Listen(\":8080\")\n}\n```\n\nTo test our application, we can use a tool like curl or Postman to send HTTP requests to our server. Here are some examples of how to do that:\n\n- To login as Alice and get the access token and the refresh token, we can send a POST request to http://localhost:8080/login with the following JSON body:\n\n```json\n{\n    \"username\": \"alice\",\n    \"password\": \"1234\"\n}\n```\n\nWe should get a response like this:\n\n```json\n{\n    \"access_token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MzYwNjQ3MzEsImlhdCI6MTYzNjA2MzIzMSwic3ViIjoiYWxpY2UiLCJ1c2VySWQiOjF9.8f7a7b8f7a7b8f7a7b8f7a7b8f7a7b8f7a7b8f7a7b8f7a7b8f7a7b8f\",\n    \"refresh_token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MzYxNTAzMzEsImlhdCI6MTYzNjA2MzIzMSwic3ViIjoiYWxpY2UiLCJ1c2VySWQiOjF9.9f8a8b9f8a8b9f8a8b9f8a8b9f8a8b9f8a8b9f8a8b9f8a8b9f8a8b9f\",\n    \"expires_in\": 900\n}\n```\n\n- To access the protected API endpoint with the access token, we can send a POST request to http://localhost:8080/protected with the following header:\n\n```bash\nAuthorization: Bearer <access-token>\n```\n\nWe should get a response like this:\n\n```json\n{\n    \"user_id\": 1\n}\n```\n\n- To refresh the access token with the refresh token, we can send a POST request to http://localhost:8080/refresh with the following header:\n\n```bash\nAuthorization: Bearer <refresh-token>\n```\n\nWe should get a response like this:\n\n```json\n{\n    \"access_token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MzYwNjQ4NTMsImlhdCI6MTYzNjA2MzM1Mywic3ViIjoiYWxpY2UiLCJ1c2VySWQiOjF9.9g7a7c9g7a7c9g7a7c9g7a7c9g7a7c9g7a7c9g7a7c9g7a7c9g7a7c9g\",\n    \"refresh_token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MzYxNTA0NTMsImlhdCI6MTYzNjA2MzM1Mywic3ViIjoiYWxpY2UiLCJ1c2VySWQiOjF9.aG8aBbaG8aBbaG8aBbaG8aBbaG8aBbaG8aBbaG8aBbaG8aBbaG8aBbaG\",\n    \"expires_in\": 900\n}\n```\n\n## Conclusion\n\nIn this tutorial, we learned how to use JWT authentication with Iris, a fast and simple web framework for Go. We learned how to create a signer and a verifier for JWT, how to generate and validate JWT tokens, how to protect an API endpoint with JWT, and how to refresh JWT tokens. We also learned how to use the Iris middleware/jwt package, which provides a common Iris handler for JWT authentication.\n\nYou can find the complete code for this tutorial on GitHub: https://github.com/kataras/iris/tree/main/_examples/auth/jwt/tutorial and https://github.com/kataras/jwt.\n\nIf you want to learn more about Iris, you can visit its official website: https://www.iris-go.com/\n\nIf you want to learn more about JWT, you can visit its official website: https://jwt.io/\n\nI hope you enjoyed this tutorial and found it useful. Thank you for reading.😊"
  },
  {
    "path": "middleware/jwt/aliases.go",
    "content": "package jwt\n\nimport \"github.com/kataras/jwt\"\n\n// Error values.\nvar (\n\tErrBlocked           = jwt.ErrBlocked\n\tErrDecrypt           = jwt.ErrDecrypt\n\tErrExpected          = jwt.ErrExpected\n\tErrExpired           = jwt.ErrExpired\n\tErrInvalidKey        = jwt.ErrInvalidKey\n\tErrIssuedInTheFuture = jwt.ErrIssuedInTheFuture\n\tErrMissing           = jwt.ErrMissing\n\tErrMissingKey        = jwt.ErrMissingKey\n\tErrNotValidYet       = jwt.ErrNotValidYet\n\tErrTokenAlg          = jwt.ErrTokenAlg\n\tErrTokenForm         = jwt.ErrTokenForm\n\tErrTokenSignature    = jwt.ErrTokenSignature\n)\n\n// Signature algorithms.\nvar (\n\tEdDSA = jwt.EdDSA\n\tHS256 = jwt.HS256\n\tHS384 = jwt.HS384\n\tHS512 = jwt.HS512\n\tRS256 = jwt.RS256\n\tRS384 = jwt.RS384\n\tRS512 = jwt.RS512\n\tES256 = jwt.ES256\n\tES384 = jwt.ES384\n\tES512 = jwt.ES512\n\tPS256 = jwt.PS256\n\tPS384 = jwt.PS384\n\tPS512 = jwt.PS512\n)\n\n// Signature algorithm helpers.\nvar (\n\tMustLoadHMAC         = jwt.MustLoadHMAC\n\tLoadHMAC             = jwt.LoadHMAC\n\tMustLoadRSA          = jwt.MustLoadRSA\n\tLoadPrivateKeyRSA    = jwt.LoadPrivateKeyRSA\n\tLoadPublicKeyRSA     = jwt.LoadPublicKeyRSA\n\tParsePrivateKeyRSA   = jwt.ParsePrivateKeyRSA\n\tParsePublicKeyRSA    = jwt.ParsePublicKeyRSA\n\tMustLoadECDSA        = jwt.MustLoadECDSA\n\tLoadPrivateKeyECDSA  = jwt.LoadPrivateKeyECDSA\n\tLoadPublicKeyECDSA   = jwt.LoadPublicKeyECDSA\n\tParsePrivateKeyECDSA = jwt.ParsePrivateKeyECDSA\n\tParsePublicKeyECDSA  = jwt.ParsePublicKeyECDSA\n\tMustLoadEdDSA        = jwt.MustLoadEdDSA\n\tLoadPrivateKeyEdDSA  = jwt.LoadPrivateKeyEdDSA\n\tLoadPublicKeyEdDSA   = jwt.LoadPublicKeyEdDSA\n\tParsePrivateKeyEdDSA = jwt.ParsePrivateKeyEdDSA\n\tParsePublicKeyEdDSA  = jwt.ParsePublicKeyEdDSA\n)\n\n// Type alises for the underline jwt package.\ntype (\n\t// Alg is the signature algorithm interface alias.\n\tAlg = jwt.Alg\n\t// Claims represents the standard claim values (as specified in RFC 7519).\n\tClaims = jwt.Claims\n\t// Expected is a TokenValidator which performs simple checks\n\t// between standard claims values.\n\t//\n\t// Usage:\n\t//  expecteed := jwt.Expected{\n\t//\t  Issuer: \"my-app\",\n\t//  }\n\t//  verifiedToken, err := verifier.Verify(..., expected)\n\tExpected = jwt.Expected\n\n\t// TokenValidator is the token validator interface alias.\n\tTokenValidator = jwt.TokenValidator\n\t// VerifiedToken is the type alias for the verfieid token type,\n\t// the result of the VerifyToken function.\n\tVerifiedToken = jwt.VerifiedToken\n\t// SignOption used to set signing options at Sign function.\n\tSignOption = jwt.SignOption\n\t// TokenPair is just a helper structure which holds both access and refresh tokens.\n\tTokenPair = jwt.TokenPair\n)\n\n// Encryption algorithms.\nvar (\n\tGCM = jwt.GCM\n\t// Helper to generate random key,\n\t// can be used to generate hmac signature key and GCM+AES for testing.\n\tMustGenerateRandom = jwt.MustGenerateRandom\n)\n\nvar (\n\t// Leeway adds validation for a leeway expiration time.\n\t// If the token was not expired then a comparison between\n\t// this \"leeway\" and the token's \"exp\" one is expected to pass instead (now+leeway > exp).\n\t// Example of use case: disallow tokens that are going to be expired in 3 seconds from now,\n\t// this is useful to make sure that the token is valid when the when the user fires a database call for example.\n\t// Usage:\n\t//  verifiedToken, err := verifier.Verify(..., jwt.Leeway(5*time.Second))\n\tLeeway = jwt.Leeway\n\t// MaxAge is a SignOption to set the expiration \"exp\", \"iat\" JWT standard claims.\n\t// Can be passed as last input argument of the `Sign` function.\n\t//\n\t// If maxAge > second then sets expiration to the token.\n\t// It's a helper field to set the \"exp\" and \"iat\" claim values.\n\t// Usage:\n\t//  signer.Sign(..., jwt.MaxAge(15*time.Minute))\n\tMaxAge = jwt.MaxAge\n\n\t// ID is a shurtcut to set jwt ID on Sign.\n\tID = func(id string) jwt.SignOptionFunc {\n\t\treturn func(c *Claims) {\n\t\t\tc.ID = id\n\t\t}\n\t}\n)\n\n// Shortcuts for Signing and Verifying.\nvar (\n\tVerify               = jwt.Verify\n\tVerifyEncryptedToken = jwt.VerifyEncrypted\n\tSign                 = jwt.Sign\n\tSignEncrypted        = jwt.SignEncrypted\n)\n"
  },
  {
    "path": "middleware/jwt/blocklist/redis/blocklist.go",
    "content": "package redis\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"sync/atomic\"\n\n\t\"github.com/kataras/iris/v12/core/host\"\n\t\"github.com/kataras/iris/v12/middleware/jwt\"\n\n\t\"github.com/redis/go-redis/v9\"\n)\n\nvar defaultContext = context.Background()\n\ntype (\n\t// Options is just a type alias for the go-redis Client Options.\n\tOptions = redis.Options\n\t// ClusterOptions is just a type alias for the go-redis Cluster Client Options.\n\tClusterOptions = redis.ClusterOptions\n)\n\n// Client is the interface which both\n// go-redis Client and Cluster Client implements.\ntype Client interface {\n\tredis.Cmdable // Commands.\n\tio.Closer     // CloseConnection.\n}\n\n// Blocklist is a jwt.Blocklist backed by Redis.\ntype Blocklist struct {\n\t// GetKey is a function which can be used how to extract\n\t// the unique identifier for a token.\n\t// Required. By default the token key is extracted through the claims.ID (\"jti\").\n\tGetKey func(token []byte, claims jwt.Claims) string\n\t// Prefix the token key into the redis database.\n\t// Note that if you can also select a different database\n\t// through ClientOptions (or ClusterOptions).\n\t// Defaults to empty string (no prefix).\n\tPrefix string\n\t// Both Client and ClusterClient implements this interface.\n\tclient    Client\n\tconnected uint32\n\t// Customize any go-redis fields manually\n\t// before Connect.\n\tClientOptions  Options\n\tClusterOptions ClusterOptions\n}\n\nvar _ jwt.Blocklist = (*Blocklist)(nil)\n\n// NewBlocklist returns a new redis-based Blocklist.\n// Modify its ClientOptions or ClusterOptions depending the application needs\n// and call its Connect.\n//\n// Usage:\n//\n//\tblocklist := NewBlocklist()\n//\tblocklist.ClientOptions.Addr = ...\n//\terr := blocklist.Connect()\n//\n// And register it:\n//\n//\tverifier := jwt.NewVerifier(...)\n//\tverifier.Blocklist = blocklist\nfunc NewBlocklist() *Blocklist {\n\treturn &Blocklist{\n\t\tGetKey: defaultGetKey,\n\t\tPrefix: \"\",\n\t\tClientOptions: Options{\n\t\t\tAddr: \"127.0.0.1:6379\",\n\t\t\t// The rest are defaulted to good values already.\n\t\t},\n\t\t// If its Addrs > 0 before connect then cluster client is used instead.\n\t\tClusterOptions: ClusterOptions{},\n\t}\n}\n\nfunc defaultGetKey(_ []byte, claims jwt.Claims) string {\n\treturn claims.ID\n}\n\n// Connect prepares the redis client and fires a ping response to it.\nfunc (b *Blocklist) Connect() error {\n\tif b.Prefix != \"\" {\n\t\tgetKey := b.GetKey\n\t\tb.GetKey = func(token []byte, claims jwt.Claims) string {\n\t\t\treturn b.Prefix + getKey(token, claims)\n\t\t}\n\t}\n\n\tif len(b.ClusterOptions.Addrs) > 0 {\n\t\t// Use cluster client.\n\t\tb.client = redis.NewClusterClient(&b.ClusterOptions)\n\t} else {\n\t\tb.client = redis.NewClient(&b.ClientOptions)\n\t}\n\n\t_, err := b.client.Ping(defaultContext).Result()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\thost.RegisterOnInterrupt(func() {\n\t\tatomic.StoreUint32(&b.connected, 0)\n\t\tb.client.Close()\n\t})\n\tatomic.StoreUint32(&b.connected, 1)\n\n\treturn nil\n}\n\n// IsConnected reports whether the Connect function was called.\nfunc (b *Blocklist) IsConnected() bool {\n\treturn atomic.LoadUint32(&b.connected) > 0\n}\n\n// ValidateToken checks if the token exists and\nfunc (b *Blocklist) ValidateToken(token []byte, c jwt.Claims, err error) error {\n\tif err != nil {\n\t\tif err == jwt.ErrExpired {\n\t\t\tb.Del(b.GetKey(token, c))\n\t\t}\n\n\t\treturn err // respect the previous error.\n\t}\n\n\thas, err := b.Has(b.GetKey(token, c))\n\tif err != nil {\n\t\treturn err\n\t} else if has {\n\t\treturn jwt.ErrBlocked\n\t}\n\n\treturn nil\n}\n\n// InvalidateToken invalidates a verified JWT token.\nfunc (b *Blocklist) InvalidateToken(token []byte, c jwt.Claims) error {\n\tkey := b.GetKey(token, c)\n\treturn b.client.SetEx(defaultContext, key, token, c.Timeleft()).Err()\n}\n\n// Del removes a token from the storage.\nfunc (b *Blocklist) Del(key string) error {\n\treturn b.client.Del(defaultContext, key).Err()\n}\n\n// Has reports whether a specific token exists in the storage.\nfunc (b *Blocklist) Has(key string) (bool, error) {\n\tn, err := b.client.Exists(defaultContext, key).Result()\n\treturn n > 0, err\n}\n\n// Count returns the total amount of tokens stored.\nfunc (b *Blocklist) Count() (int64, error) {\n\tif b.Prefix == \"\" {\n\t\treturn b.client.DBSize(defaultContext).Result()\n\t}\n\n\tkeys, err := b.getKeys(0)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn int64(len(keys)), nil\n}\n\nfunc (b *Blocklist) getKeys(cursor uint64) ([]string, error) {\n\tkeys, cursor, err := b.client.Scan(defaultContext, cursor, b.Prefix+\"*\", 300000).Result()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif cursor != 0 {\n\t\tmoreKeys, err := b.getKeys(cursor)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tkeys = append(keys, moreKeys...)\n\t}\n\n\treturn keys, nil\n}\n"
  },
  {
    "path": "middleware/jwt/blocklist.go",
    "content": "package jwt\n\nimport (\n\t\"github.com/kataras/jwt\"\n)\n\n// Blocklist should hold and manage invalidated-by-server tokens.\n// The `NewBlocklist` and `NewBlocklistContext` functions\n// returns a memory storage of tokens,\n// it is the internal \"blocklist\" struct.\n//\n// The end-developer can implement her/his own blocklist,\n// e.g. a redis one to keep persistence of invalidated tokens on server restarts.\n// and bind to the JWT middleware's Blocklist field.\ntype Blocklist interface {\n\tjwt.TokenValidator\n\n\t// InvalidateToken should invalidate a verified JWT token.\n\tInvalidateToken(token []byte, c Claims) error\n\t// Del should remove a token from the storage.\n\tDel(key string) error\n\t// Has should report whether a specific token exists in the storage.\n\tHas(key string) (bool, error)\n\t// Count should return the total amount of tokens stored.\n\tCount() (int64, error)\n}\n\ntype blocklistConnect interface {\n\tConnect() error\n\tIsConnected() bool\n}\n"
  },
  {
    "path": "middleware/jwt/extractor.go",
    "content": "package jwt\n\nimport (\n\t\"strings\"\n\n\t\"github.com/kataras/iris/v12/context\"\n)\n\n// TokenExtractor is a function that takes a context as input and returns\n// a token. An empty string should be returned if no token found\n// without additional information.\ntype TokenExtractor func(*context.Context) string\n\n// FromHeader is a token extractor.\n// It reads the token from the Authorization request header of form:\n// Authorization: \"Bearer {token}\".\nfunc FromHeader(ctx *context.Context) string {\n\tauthHeader := ctx.GetHeader(\"Authorization\")\n\tif authHeader == \"\" {\n\t\treturn \"\"\n\t}\n\n\t// pure check: authorization header format must be Bearer {token}\n\tauthHeaderParts := strings.Split(authHeader, \" \")\n\tif len(authHeaderParts) != 2 || strings.ToLower(authHeaderParts[0]) != \"bearer\" {\n\t\treturn \"\"\n\t}\n\n\treturn authHeaderParts[1]\n}\n\n// FromQuery is a token extractor.\n// It reads the token from the \"token\" url query parameter.\nfunc FromQuery(ctx *context.Context) string {\n\treturn ctx.URLParam(\"token\")\n}\n\n// FromJSON is a token extractor.\n// Reads a json request body and extracts the json based on the given field.\n// The request content-type should contain the: application/json header value, otherwise\n// this method will not try to read and consume the body.\nfunc FromJSON(jsonKey string) TokenExtractor {\n\treturn func(ctx *context.Context) string {\n\t\tif ctx.GetContentTypeRequested() != context.ContentJSONHeaderValue {\n\t\t\treturn \"\"\n\t\t}\n\n\t\tvar m context.Map\n\t\tctx.RecordRequestBody(true)\n\t\tdefer ctx.RecordRequestBody(false)\n\t\tif err := ctx.ReadJSON(&m); err != nil {\n\t\t\treturn \"\"\n\t\t}\n\n\t\tif m == nil {\n\t\t\treturn \"\"\n\t\t}\n\n\t\tv, ok := m[jsonKey]\n\t\tif !ok {\n\t\t\treturn \"\"\n\t\t}\n\n\t\ttok, ok := v.(string)\n\t\tif !ok {\n\t\t\treturn \"\"\n\t\t}\n\n\t\treturn tok\n\t}\n}\n"
  },
  {
    "path": "middleware/jwt/jwt.go",
    "content": "package jwt\n\nimport \"github.com/kataras/iris/v12/context\"\n\nfunc init() {\n\tcontext.SetHandlerName(\"iris/middleware/jwt.*\", \"iris.jwt\")\n}\n"
  },
  {
    "path": "middleware/jwt/jwt_test.go",
    "content": "package jwt_test\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/httptest\"\n\t\"github.com/kataras/iris/v12/middleware/jwt\"\n)\n\nvar testAlg, testSecret = jwt.HS256, []byte(\"sercrethatmaycontainch@r$\")\n\ntype fooClaims struct {\n\tFoo string `json:\"foo\"`\n}\n\n// The actual tests are inside the kataras/jwt repository.\n// This runs simple checks of just the middleware part.\nfunc TestJWT(t *testing.T) {\n\tapp := iris.New()\n\n\tsigner := jwt.NewSigner(testAlg, testSecret, 3*time.Second)\n\tapp.Get(\"/\", func(ctx iris.Context) {\n\t\tclaims := fooClaims{Foo: \"bar\"}\n\t\ttoken, err := signer.Sign(claims)\n\t\tif err != nil {\n\t\t\tctx.StopWithError(iris.StatusInternalServerError, err)\n\t\t\treturn\n\t\t}\n\t\tctx.Write(token)\n\t})\n\n\tverifier := jwt.NewVerifier(testAlg, testSecret)\n\tverifier.ErrorHandler = func(ctx iris.Context, err error) { // app.OnErrorCode(401, ...)\n\t\tctx.StopWithError(iris.StatusUnauthorized, err)\n\t}\n\tmiddleware := verifier.Verify(func() any { return new(fooClaims) })\n\tapp.Get(\"/protected\", middleware, func(ctx iris.Context) {\n\t\tclaims := jwt.Get(ctx).(*fooClaims)\n\t\tctx.WriteString(claims.Foo)\n\t})\n\n\te := httptest.New(t, app)\n\n\t// Get generated token.\n\ttoken := e.GET(\"/\").Expect().Status(iris.StatusOK).Body().Raw()\n\t// Test Header.\n\theaderValue := fmt.Sprintf(\"Bearer %s\", token)\n\te.GET(\"/protected\").WithHeader(\"Authorization\", headerValue).Expect().\n\t\tStatus(iris.StatusOK).Body().IsEqual(\"bar\")\n\t// Test URL query.\n\te.GET(\"/protected\").WithQuery(\"token\", token).Expect().\n\t\tStatus(iris.StatusOK).Body().IsEqual(\"bar\")\n\n\t// Test unauthorized.\n\te.GET(\"/protected\").Expect().Status(iris.StatusUnauthorized)\n\te.GET(\"/protected\").WithHeader(\"Authorization\", \"missing bearer\").Expect().Status(iris.StatusUnauthorized)\n\te.GET(\"/protected\").WithQuery(\"token\", \"invalid_token\").Expect().Status(iris.StatusUnauthorized)\n\t// Test expired (note checks happen based on second round).\n\ttime.Sleep(5 * time.Second)\n\te.GET(\"/protected\").WithHeader(\"Authorization\", headerValue).Expect().\n\t\tStatus(iris.StatusUnauthorized).Body().IsEqual(\"jwt: token expired\")\n}\n"
  },
  {
    "path": "middleware/jwt/signer.go",
    "content": "package jwt\n\nimport (\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/kataras/jwt\"\n)\n\n// Signer holds common options to sign and generate a token.\n// Its Sign method can be used to generate a token which can be sent to the client.\n// Its NewTokenPair can be used to construct a token pair (access_token, refresh_token).\n//\n// It does not support JWE, JWK.\ntype Signer struct {\n\tAlg Alg\n\tKey any\n\n\t// MaxAge to set \"exp\" and \"iat\".\n\t// Recommended value for access tokens: 15 minutes.\n\t// Defaults to 0, no limit.\n\tMaxAge  time.Duration\n\tOptions []SignOption\n\n\tEncrypt func([]byte) ([]byte, error)\n}\n\n// NewSigner accepts the signature algorithm among with its (private or shared) key\n// and the max life time duration of generated tokens and returns a JWT signer.\n// See its Sign method.\n//\n// Usage:\n//\n//\tsigner := NewSigner(HS256, secret, 15*time.Minute)\n//\ttoken, err := signer.Sign(userClaims{Username: \"kataras\"})\nfunc NewSigner(signatureAlg Alg, signatureKey any, maxAge time.Duration) *Signer {\n\tif signatureAlg == HS256 {\n\t\t// A tiny helper if the end-developer uses string instead of []byte for hmac keys.\n\t\tif k, ok := signatureKey.(string); ok {\n\t\t\tsignatureKey = []byte(k)\n\t\t}\n\t}\n\n\ts := &Signer{\n\t\tAlg:    signatureAlg,\n\t\tKey:    signatureKey,\n\t\tMaxAge: maxAge,\n\t}\n\n\tif maxAge > 0 {\n\t\ts.Options = []SignOption{MaxAge(maxAge)}\n\t}\n\n\treturn s\n}\n\n// WithEncryption enables AES-GCM payload-only decryption.\nfunc (s *Signer) WithEncryption(key, additionalData []byte) *Signer {\n\tencrypt, _, err := jwt.GCM(key, additionalData)\n\tif err != nil {\n\t\tpanic(err) // important error before serve, stop everything.\n\t}\n\n\ts.Encrypt = encrypt\n\treturn s\n}\n\n// Sign generates a new token based on the given \"claims\" which is valid up to \"s.MaxAge\".\nfunc (s *Signer) Sign(claims any, opts ...SignOption) ([]byte, error) {\n\tif len(opts) > 0 {\n\t\topts = append(opts, s.Options...)\n\t} else {\n\t\topts = s.Options\n\t}\n\n\treturn SignEncrypted(s.Alg, s.Key, s.Encrypt, claims, opts...)\n}\n\n// NewTokenPair accepts the access and refresh claims plus the life time duration for the refresh token\n// and generates a new token pair which can be sent to the client.\n// The same token pair can be json-decoded.\nfunc (s *Signer) NewTokenPair(accessClaims any, refreshClaims any, refreshMaxAge time.Duration, accessOpts ...SignOption) (TokenPair, error) {\n\tif refreshMaxAge <= s.MaxAge {\n\t\treturn TokenPair{}, fmt.Errorf(\"refresh max age should be bigger than access token's one[%d - %d]\", refreshMaxAge, s.MaxAge)\n\t}\n\n\taccessToken, err := s.Sign(accessClaims, accessOpts...)\n\tif err != nil {\n\t\treturn TokenPair{}, err\n\t}\n\n\trefreshToken, err := Sign(s.Alg, s.Key, refreshClaims, MaxAge(refreshMaxAge))\n\tif err != nil {\n\t\treturn TokenPair{}, err\n\t}\n\n\ttokenPair := jwt.NewTokenPair(accessToken, refreshToken)\n\treturn tokenPair, nil\n}\n"
  },
  {
    "path": "middleware/jwt/verifier.go",
    "content": "package jwt\n\nimport (\n\t\"reflect\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\n\t\"github.com/kataras/jwt\"\n)\n\nconst (\n\tclaimsContextKey        = \"iris.jwt.claims\"\n\tverifiedTokenContextKey = \"iris.jwt.token\"\n)\n\n// Get returns the claims decoded by a verifier.\nfunc Get(ctx *context.Context) any {\n\tif v := ctx.Values().Get(claimsContextKey); v != nil {\n\t\treturn v\n\t}\n\n\treturn nil\n}\n\n// GetVerifiedToken returns the verified token structure\n// which holds information about the decoded token\n// and its standard claims.\nfunc GetVerifiedToken(ctx *context.Context) *VerifiedToken {\n\tif v := ctx.Values().Get(verifiedTokenContextKey); v != nil {\n\t\tif tok, ok := v.(*VerifiedToken); ok {\n\t\t\treturn tok\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// Verifier holds common options to verify an incoming token.\n// Its Verify method can be used as a middleware to allow authorized clients to access an API.\n//\n// It does not support JWE, JWK.\ntype Verifier struct {\n\tAlg Alg\n\tKey any\n\n\tDecrypt func([]byte) ([]byte, error)\n\n\tExtractors []TokenExtractor\n\tBlocklist  Blocklist\n\tValidators []TokenValidator\n\n\tErrorHandler func(ctx *context.Context, err error)\n\t// DisableContextUser disables the registration of the claims as context User.\n\tDisableContextUser bool\n}\n\n// NewVerifier accepts the algorithm for the token's signature among with its (public) key\n// and optionally some token validators for all verify middlewares that may initialized under this Verifier.\n// See its Verify method.\n//\n// Usage:\n//\n//\tverifier := NewVerifier(HS256, secret)\n//\n// OR\n//\n//\tverifier := NewVerifier(HS256, secret, Expected{Issuer: \"my-app\"})\n//\n//\tclaimsGetter := func() any { return new(userClaims) }\n//\tmiddleware := verifier.Verify(claimsGetter)\n//\n// OR\n//\n//\tmiddleware := verifier.Verify(claimsGetter, Expected{Issuer: \"my-app\"})\n//\n// Register the middleware, e.g.\n//\n//\tapp.Use(middleware)\n//\n// Get the claims:\n//\n//\tclaims := jwt.Get(ctx).(*userClaims)\n//\tusername := claims.Username\n//\n// Get the context user:\n//\n//\tusername, err := ctx.User().GetUsername()\nfunc NewVerifier(signatureAlg Alg, signatureKey any, validators ...TokenValidator) *Verifier {\n\tif signatureAlg == HS256 {\n\t\t// A tiny helper if the end-developer uses string instead of []byte for hmac keys.\n\t\tif k, ok := signatureKey.(string); ok {\n\t\t\tsignatureKey = []byte(k)\n\t\t}\n\t}\n\n\treturn &Verifier{\n\t\tAlg:        signatureAlg,\n\t\tKey:        signatureKey,\n\t\tExtractors: []TokenExtractor{FromHeader, FromQuery},\n\t\tErrorHandler: func(ctx *context.Context, err error) {\n\t\t\tctx.StopWithError(401, context.PrivateError(err))\n\t\t},\n\t\tValidators: validators,\n\t}\n}\n\n// WithDecryption enables AES-GCM payload-only encryption.\nfunc (v *Verifier) WithDecryption(key, additionalData []byte) *Verifier {\n\t_, decrypt, err := jwt.GCM(key, additionalData)\n\tif err != nil {\n\t\tpanic(err) // important error before serve, stop everything.\n\t}\n\n\tv.Decrypt = decrypt\n\treturn v\n}\n\n// WithDefaultBlocklist attaches an in-memory blocklist storage\n// to invalidate tokens through server-side.\n// To invalidate a token simply call the Context.Logout method.\nfunc (v *Verifier) WithDefaultBlocklist() *Verifier {\n\tv.Blocklist = jwt.NewBlocklist(30 * time.Minute)\n\treturn v\n}\n\nfunc (v *Verifier) invalidate(ctx *context.Context) {\n\tif verifiedToken := GetVerifiedToken(ctx); verifiedToken != nil {\n\t\tv.Blocklist.InvalidateToken(verifiedToken.Token, verifiedToken.StandardClaims)\n\t\tctx.Values().Remove(claimsContextKey)\n\t\tctx.Values().Remove(verifiedTokenContextKey)\n\t\tctx.SetUser(nil)\n\t\tctx.SetLogoutFunc(nil)\n\t}\n}\n\n// RequestToken extracts the token from the request.\nfunc (v *Verifier) RequestToken(ctx *context.Context) (token string) {\n\tfor _, extract := range v.Extractors {\n\t\tif token = extract(ctx); token != \"\" {\n\t\t\tbreak // ok we found it.\n\t\t}\n\t}\n\n\treturn\n}\n\ntype (\n\t// ClaimsValidator is a special interface which, if the destination claims\n\t// implements it then the verifier runs its Validate method before return.\n\tClaimsValidator interface {\n\t\tValidate() error\n\t}\n\n\t// ClaimsContextValidator same as ClaimsValidator but it accepts\n\t// a request context which can be used for further checks before\n\t// validating the incoming token's claims.\n\tClaimsContextValidator interface {\n\t\tValidate(*context.Context) error\n\t}\n)\n\n// VerifyToken simply verifies the given \"token\" and validates its standard claims (such as expiration).\n// Returns a structure which holds the token's information. See the Verify method instead.\nfunc (v *Verifier) VerifyToken(token []byte, validators ...TokenValidator) (*VerifiedToken, error) {\n\treturn jwt.VerifyEncrypted(v.Alg, v.Key, v.Decrypt, token, validators...)\n}\n\n// Verify is the most important piece of code inside the Verifier.\n// It accepts the \"claimsType\" function which should return a pointer to a custom structure\n// which the token's decode claims valuee will be binded and validated to.\n// Returns a common Iris handler which can be used as a middleware to protect an API\n// from unauthorized client requests. After this, the route handlers can access the claims\n// through the jwt.Get package-level function.\n//\n// By default it extracts the token from Authorization: Bearer $token header and ?token URL Query parameter,\n// to change that behavior modify its Extractors field.\n//\n// By default a 401 status code with a generic message will be sent to the client on\n// a token verification or claims validation failure, to change that behavior\n// modify its ErrorHandler field or register OnErrorCode(401, errorHandler) and\n// retrieve the error through Context.GetErr method.\n//\n// If the \"claimsType\" is nil then only the jwt.GetVerifiedToken is available\n// and the handler should unmarshal the payload to extract the claims by itself.\nfunc (v *Verifier) Verify(claimsType func() any, validators ...TokenValidator) context.Handler {\n\tunmarshal := jwt.Unmarshal\n\tif claimsType != nil {\n\t\tc := claimsType()\n\t\tif hasRequired(c) {\n\t\t\tunmarshal = jwt.UnmarshalWithRequired\n\t\t}\n\t}\n\n\tif v.Blocklist != nil {\n\t\t// If blocklist implements the connect interface,\n\t\t// try to connect if it's not already connected manually by developer,\n\t\t// if errored then just return a handler which will fire this error every single time.\n\t\tif bc, ok := v.Blocklist.(blocklistConnect); ok {\n\t\t\tif !bc.IsConnected() {\n\t\t\t\tif err := bc.Connect(); err != nil {\n\t\t\t\t\treturn func(ctx *context.Context) {\n\t\t\t\t\t\tv.ErrorHandler(ctx, err)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tvalidators = append([]TokenValidator{v.Blocklist}, append(v.Validators, validators...)...)\n\t}\n\n\treturn func(ctx *context.Context) {\n\t\ttoken := []byte(v.RequestToken(ctx))\n\t\tverifiedToken, err := v.VerifyToken(token, validators...)\n\t\tif err != nil {\n\t\t\tv.ErrorHandler(ctx, err)\n\t\t\treturn\n\t\t}\n\n\t\tif claimsType != nil {\n\t\t\tdest := claimsType()\n\t\t\tif err = unmarshal(verifiedToken.Payload, dest); err != nil {\n\t\t\t\tv.ErrorHandler(ctx, err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif validator, ok := dest.(ClaimsValidator); ok {\n\t\t\t\tif err = validator.Validate(); err != nil {\n\t\t\t\t\tv.ErrorHandler(ctx, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t} else if contextValidator, ok := dest.(ClaimsContextValidator); ok {\n\t\t\t\tif err = contextValidator.Validate(ctx); err != nil {\n\t\t\t\t\tv.ErrorHandler(ctx, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif !v.DisableContextUser {\n\t\t\t\tctx.SetUser(dest)\n\t\t\t}\n\n\t\t\tctx.Values().Set(claimsContextKey, dest)\n\t\t}\n\n\t\tif v.Blocklist != nil {\n\t\t\tctx.SetLogoutFunc(v.invalidate)\n\t\t}\n\n\t\tctx.Values().Set(verifiedTokenContextKey, verifiedToken)\n\t\tctx.Next()\n\t}\n}\n\nfunc hasRequired(i any) bool {\n\tval := reflect.Indirect(reflect.ValueOf(i))\n\ttyp := val.Type()\n\tif typ.Kind() != reflect.Struct {\n\t\treturn false\n\t}\n\n\tfor i := 0; i < val.NumField(); i++ {\n\t\tfield := typ.Field(i)\n\t\tif jwt.HasRequiredJSONTag(field) {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n"
  },
  {
    "path": "middleware/logger/config.go",
    "content": "package logger\n\nimport (\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/context\"\n)\n\n// The SkipperFunc signature, used to serve the main request without logs.\n// See `Configuration` too.\ntype SkipperFunc func(ctx *context.Context) bool\n\n// Config contains the options for the logger middleware\n// can be optionally be passed to the `New`.\ntype Config struct {\n\t// Status displays status code (bool).\n\t//\n\t// Defaults to true.\n\tStatus bool\n\t// IP displays request's remote address (bool).\n\t//\n\t// Defaults to true.\n\tIP bool\n\t// Method displays the http method (bool).\n\t//\n\t// Defaults to true.\n\tMethod bool\n\t// Path displays the request path (bool).\n\t// See `Query` and `PathAfterHandler` too.\n\t//\n\t// Defaults to true.\n\tPath bool\n\t// PathAfterHandler displays the request path\n\t// which may be set and modified\n\t// after the handler chain is executed.\n\t// See `Query` too.\n\t//\n\t// Defaults to false.\n\tPathAfterHandler bool\n\t// Query will append the URL Query to the Path.\n\t// Path should be true too.\n\t//\n\t// Defaults to false.\n\tQuery bool\n\t// TraceRoute displays the debug\n\t// information about the current route executed.\n\t//\n\t// Defaults to false.\n\tTraceRoute bool\n\n\t// MessageContextKeys if not empty,\n\t// the middleware will try to fetch\n\t// the contents with `ctx.Values().Get(MessageContextKey)`\n\t// and if available then these contents will be\n\t// appended as part of the logs (with `%v`, in order to be able to set a struct too),\n\t//\n\t// Defaults to empty.\n\tMessageContextKeys []string\n\n\t// MessageHeaderKeys if not empty,\n\t// the middleware will try to fetch\n\t// the contents with `ctx.Values().Get(MessageHeaderKey)`\n\t// and if available then these contents will be\n\t// appended as part of the logs (with `%v`, in order to be able to set a struct too),\n\t//\n\t// Defaults to empty.\n\tMessageHeaderKeys []string\n\n\t// LogFunc is the writer which logs are written to,\n\t// if missing the logger middleware uses the app.Logger().Infof instead.\n\t// Note that message argument can be empty.\n\tLogFunc func(endTime time.Time, latency time.Duration, status, ip, method, path string, message any, headerMessage any)\n\t// LogFuncCtx can be used instead of `LogFunc` if handlers need to customize the output based on\n\t// custom request-time information that the LogFunc isn't aware of.\n\tLogFuncCtx func(ctx *context.Context, latency time.Duration)\n\t// Skippers used to skip the logging i.e by `ctx.Path()` and serve\n\t// the next/main handler immediately.\n\tSkippers []SkipperFunc\n\t// the Skippers as one function in order to reduce the time needed to\n\t// combine them at serve time.\n\tskip SkipperFunc\n}\n\n// DefaultConfig returns a default config\n// that have all boolean fields to true,\n// all strings are empty,\n// LogFunc and Skippers to nil as well.\nfunc DefaultConfig() Config {\n\treturn Config{\n\t\tStatus:           true,\n\t\tIP:               true,\n\t\tMethod:           true,\n\t\tPath:             true,\n\t\tPathAfterHandler: false,\n\t\tQuery:            false,\n\t\tTraceRoute:       false,\n\t\tLogFunc:          nil,\n\t\tLogFuncCtx:       nil,\n\t\tSkippers:         nil,\n\t\tskip:             nil,\n\t}\n}\n\n// AddSkipper adds a skipper to the configuration.\nfunc (c *Config) AddSkipper(sk SkipperFunc) {\n\tc.Skippers = append(c.Skippers, sk)\n\tc.buildSkipper()\n}\n\nfunc (c *Config) buildSkipper() {\n\tif len(c.Skippers) == 0 {\n\t\treturn\n\t}\n\tskippersLocked := c.Skippers[0:]\n\tc.skip = func(ctx *context.Context) bool {\n\t\tfor _, s := range skippersLocked {\n\t\t\tif s(ctx) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t\treturn false\n\t}\n}\n"
  },
  {
    "path": "middleware/logger/logger.go",
    "content": "// Package logger provides request logging via middleware. See _examples/logging/request-logger\npackage logger\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/context\"\n)\n\nfunc init() {\n\tcontext.SetHandlerName(\"iris/middleware/logger.*\", \"iris.logger\")\n}\n\ntype requestLoggerMiddleware struct {\n\tconfig Config\n}\n\n// New creates and returns a new request logger middleware.\n// Do not confuse it with the framework's Logger.\n// This is for the http requests.\n//\n// Receives an optional configuation.\n// Usage: app.UseRouter(logger.New()).\nfunc New(cfg ...Config) context.Handler {\n\tc := DefaultConfig()\n\tif len(cfg) > 0 {\n\t\tc = cfg[0]\n\t}\n\tc.buildSkipper()\n\tl := &requestLoggerMiddleware{config: c}\n\n\treturn l.ServeHTTP\n}\n\nfunc (l *requestLoggerMiddleware) getPath(ctx *context.Context) string {\n\tif l.config.Query {\n\t\treturn ctx.Request().URL.RequestURI()\n\t}\n\treturn ctx.Path()\n}\n\n// Serve serves the middleware\nfunc (l *requestLoggerMiddleware) ServeHTTP(ctx *context.Context) {\n\t// skip logs and serve the main request immediately\n\tif l.config.skip != nil {\n\t\tif l.config.skip(ctx) {\n\t\t\tctx.Next()\n\t\t\treturn\n\t\t}\n\t}\n\n\t// all except latency to string\n\tvar status, ip, method, path string\n\tvar latency time.Duration\n\tvar startTime, endTime time.Time\n\tstartTime = time.Now()\n\n\t// Before Next.\n\tif l.config.IP {\n\t\tip = ctx.RemoteAddr()\n\t}\n\n\tif l.config.Method {\n\t\tmethod = ctx.Method()\n\t}\n\n\tif l.config.Path {\n\t\tpath = l.getPath(ctx)\n\t}\n\n\tctx.Next()\n\n\t// no time.Since in order to format it well after\n\tendTime = time.Now()\n\tlatency = endTime.Sub(startTime)\n\n\tif l.config.PathAfterHandler /* we don't care if Path is disabled */ {\n\t\tpath = l.getPath(ctx)\n\t\t// note: we could just use the r.RequestURI which is the original one,\n\t\t// but some users may need the stripped one (on HandleDir).\n\t}\n\n\tif l.config.Status {\n\t\tstatus = strconv.Itoa(ctx.GetStatusCode())\n\t}\n\n\tvar message any\n\tif ctxKeys := l.config.MessageContextKeys; len(ctxKeys) > 0 {\n\t\tfor _, key := range ctxKeys {\n\t\t\tmsg := ctx.Values().Get(key)\n\t\t\tif message == nil {\n\t\t\t\tmessage = msg\n\t\t\t} else {\n\t\t\t\tmessage = fmt.Sprintf(\" %v %v\", message, msg)\n\t\t\t}\n\t\t}\n\t}\n\tvar headerMessage any\n\tif headerKeys := l.config.MessageHeaderKeys; len(headerKeys) > 0 {\n\t\tfor _, key := range headerKeys {\n\t\t\tmsg := ctx.GetHeader(key)\n\t\t\tif headerMessage == nil {\n\t\t\t\theaderMessage = msg\n\t\t\t} else {\n\t\t\t\theaderMessage = fmt.Sprintf(\" %v %v\", headerMessage, msg)\n\t\t\t}\n\t\t}\n\t}\n\n\t// print the logs\n\tif logFunc := l.config.LogFunc; logFunc != nil {\n\t\tlogFunc(endTime, latency, status, ip, method, path, message, headerMessage)\n\t\treturn\n\t} else if logFuncCtx := l.config.LogFuncCtx; logFuncCtx != nil {\n\t\tlogFuncCtx(ctx, latency)\n\t\treturn\n\t}\n\n\t// no new line, the framework's logger is responsible how to render each log.\n\tline := fmt.Sprintf(\"%v %4v %s %s %s\", status, latency, ip, method, path)\n\tif message != nil {\n\t\tline += fmt.Sprintf(\" %v\", message)\n\t}\n\n\tif headerMessage != nil {\n\t\tline += fmt.Sprintf(\" %v\", headerMessage)\n\t}\n\n\tif context.StatusCodeNotSuccessful(ctx.GetStatusCode()) {\n\t\tctx.Application().Logger().Warn(line)\n\t} else {\n\t\tctx.Application().Logger().Info(line)\n\t}\n\n\tif l.config.TraceRoute && ctx.GetCurrentRoute() != nil /* it is nil on unhandled error codes */ {\n\t\t// Get the total length of handlers and see if all are executed.\n\t\t// Note(@kataras): we get those after handler executed, because\n\t\t// filters (and overlap) feature will set the handlers on router build\n\t\t// state to fullfil their needs. And we need to respect\n\t\t// any dev's custom SetHandlers&Do actions too so we don't give false info.\n\t\t// if n, idx := len(ctx.Handlers()), ctx.HandlerIndex(-1); idx < n-1 {\n\t\t//\n\t\t// }\n\t\t// Let's pass it into the Trace function itself which will \"mark\"\n\t\t// every handler that is eventually executed.\n\t\t// Note that if StopExecution is called, the index is always -1,\n\t\t// so no \"mark\" signs will be printed at all <- this can be fixed by introducing a new ctx field.\n\t\tctx.GetCurrentRoute().Trace(ctx.Application().Logger().Printer, ctx.HandlerIndex(-1))\n\t}\n}\n"
  },
  {
    "path": "middleware/methodoverride/methodoverride.go",
    "content": "package methodoverride\n\nimport (\n\tstdContext \"context\"\n\t\"net/http\"\n\t\"strings\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/core/router\"\n)\n\nfunc init() {\n\tcontext.SetHandlerName(\"iris/middleware/methodoverride.*\", \"iris.methodoverride\")\n}\n\ntype options struct {\n\tgetters                      []GetterFunc\n\tmethods                      []string\n\tsaveOriginalMethodContextKey any // if not nil original value will be saved.\n}\n\nfunc (o *options) configure(opts ...Option) {\n\tfor _, opt := range opts {\n\t\topt(o)\n\t}\n}\n\nfunc (o *options) canOverride(method string) bool {\n\tfor _, s := range o.methods {\n\t\tif s == method {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\nfunc (o *options) get(w http.ResponseWriter, r *http.Request) string {\n\tfor _, getter := range o.getters {\n\t\tif v := getter(w, r); v != \"\" {\n\t\t\treturn strings.ToUpper(v)\n\t\t}\n\t}\n\n\treturn \"\"\n}\n\n// Option sets options for a fresh method override wrapper.\n// See `New` package-level function for more.\ntype Option func(*options)\n\n// Methods can be used to add methods that can be overridden.\n// Defaults to \"POST\".\nfunc Methods(methods ...string) Option {\n\tfor i, s := range methods {\n\t\tmethods[i] = strings.ToUpper(s)\n\t}\n\n\treturn func(opts *options) {\n\t\topts.methods = append(opts.methods, methods...)\n\t}\n}\n\n// SaveOriginalMethod will save the original method\n// on Context.Request().Context().Value(requestContextKey).\n//\n// Defaults to nil, don't save it.\nfunc SaveOriginalMethod(requestContextKey any) Option {\n\treturn func(opts *options) {\n\t\tif requestContextKey == nil {\n\t\t\topts.saveOriginalMethodContextKey = nil\n\t\t}\n\t\topts.saveOriginalMethodContextKey = requestContextKey\n\t}\n}\n\n// GetterFunc is the type signature for declaring custom logic\n// to extract the method name which a POST request will be replaced with.\ntype GetterFunc func(http.ResponseWriter, *http.Request) string\n\n// Getter sets a custom logic to use to extract the method name\n// to override the POST method with.\n// Defaults to nil.\nfunc Getter(customFunc GetterFunc) Option {\n\treturn func(opts *options) {\n\t\topts.getters = append(opts.getters, customFunc)\n\t}\n}\n\n// Headers that client can send to specify a method\n// to override the POST method with.\n//\n// Defaults to:\n// X-HTTP-Method\n// X-HTTP-Method-Override\n// X-Method-Override\nfunc Headers(headers ...string) Option {\n\tgetter := func(w http.ResponseWriter, r *http.Request) string {\n\t\tfor _, s := range headers {\n\t\t\tif v := r.Header.Get(s); v != \"\" {\n\t\t\t\tw.Header().Add(\"Vary\", s)\n\t\t\t\treturn v\n\t\t\t}\n\t\t}\n\n\t\treturn \"\"\n\t}\n\n\treturn Getter(getter)\n}\n\n// FormField specifies a form field to use to determinate the method\n// to override the POST method with.\n//\n// Example Field:\n// <input type=\"hidden\" name=\"_method\" value=\"DELETE\">\n//\n// Defaults to: \"_method\".\nfunc FormField(fieldName string) Option {\n\treturn FormFieldWithConf(fieldName, nil)\n}\n\n// FormFieldWithConf same as `FormField` but it accepts the application's\n// configuration to parse the form based on the app core configuration.\nfunc FormFieldWithConf(fieldName string, conf context.ConfigurationReadOnly) Option {\n\tvar (\n\t\tpostMaxMemory int64 = 32 << 20 // 32 MB\n\t\tresetBody           = false\n\t)\n\n\tif conf != nil {\n\t\tpostMaxMemory = conf.GetPostMaxMemory()\n\t\tresetBody = conf.GetDisableBodyConsumptionOnUnmarshal()\n\t}\n\n\tgetter := func(w http.ResponseWriter, r *http.Request) string {\n\t\treturn context.FormValueDefault(r, fieldName, \"\", postMaxMemory, resetBody)\n\t}\n\n\treturn Getter(getter)\n}\n\n// Query specifies a url parameter name to use to determinate the method\n// to override the POST methos with.\n//\n// Example URL Query string:\n// http://localhost:8080/path?_method=DELETE\n//\n// Defaults to: \"_method\".\nfunc Query(paramName string) Option {\n\tgetter := func(w http.ResponseWriter, r *http.Request) string {\n\t\treturn r.URL.Query().Get(paramName)\n\t}\n\n\treturn Getter(getter)\n}\n\n// Only clears all default or previously registered values\n// and uses only the \"o\" option(s).\n//\n// The default behavior is to check for all the following by order:\n// headers, form field, query string\n// and any custom getter (if set).\n// Use this method to override that\n// behavior and use only the passed option(s)\n// to determinate the method to override with.\n//\n// Use cases:\n//\n//  1. When need to check only for headers and ignore other fields:\n//     New(Only(Headers(\"X-Custom-Header\")))\n//\n//  2. When need to check only for (first) form field and (second) custom getter:\n//     New(Only(FormField(\"fieldName\"), Getter(...)))\nfunc Only(o ...Option) Option {\n\treturn func(opts *options) {\n\t\topts.getters = opts.getters[0:0]\n\t\topts.configure(o...)\n\t}\n}\n\n// New returns a new method override wrapper\n// which can be registered with `Application.WrapRouter`.\n//\n// Use this wrapper when you expecting clients\n// that do not support certain HTTP operations such as DELETE or PUT for security reasons.\n// This wrapper will accept a method, based on criteria, to override the POST method with.\n//\n// Read more at:\n// https://github.com/kataras/iris/issues/1325\nfunc New(opt ...Option) router.WrapperFunc {\n\topts := new(options)\n\t// Default values.\n\topts.configure(\n\t\tMethods(http.MethodPost),\n\t\tHeaders(\"X-HTTP-Method\", \"X-HTTP-Method-Override\", \"X-Method-Override\"),\n\t\tFormField(\"_method\"),\n\t\tQuery(\"_method\"),\n\t)\n\topts.configure(opt...)\n\n\treturn func(w http.ResponseWriter, r *http.Request, proceed http.HandlerFunc) {\n\t\toriginalMethod := strings.ToUpper(r.Method)\n\t\tif opts.canOverride(originalMethod) {\n\t\t\tnewMethod := opts.get(w, r)\n\t\t\tif newMethod != \"\" {\n\t\t\t\tif opts.saveOriginalMethodContextKey != nil {\n\t\t\t\t\tr = r.WithContext(stdContext.WithValue(r.Context(), opts.saveOriginalMethodContextKey, originalMethod))\n\t\t\t\t}\n\t\t\t\tr.Method = newMethod\n\t\t\t}\n\t\t}\n\n\t\tproceed(w, r)\n\t}\n}\n"
  },
  {
    "path": "middleware/methodoverride/methodoverride_test.go",
    "content": "package methodoverride_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/httptest\"\n\t\"github.com/kataras/iris/v12/middleware/methodoverride\"\n)\n\nfunc TestMethodOverrideWrapper(t *testing.T) {\n\tapp := iris.New()\n\n\tmo := methodoverride.New(\n\t\t// Defaults to nil.\n\t\t//\n\t\tmethodoverride.SaveOriginalMethod(\"_originalMethod\"),\n\t\t// Default values.\n\t\t//\n\t\t// methodoverride.Methods(http.MethodPost),\n\t\t// methodoverride.Headers(\"X-HTTP-Method\", \"X-HTTP-Method-Override\", \"X-Method-Override\"),\n\t\t// methodoverride.FormField(\"_method\"),\n\t\t// methodoverride.Query(\"_method\"),\n\t)\n\t// Register it with `WrapRouter`.\n\tapp.WrapRouter(mo)\n\n\tvar (\n\t\texpectedDelResponse  = \"delete resp\"\n\t\texpectedPostResponse = \"post resp\"\n\t)\n\n\tapp.Post(\"/path\", func(ctx iris.Context) {\n\t\tctx.WriteString(expectedPostResponse)\n\t})\n\n\tapp.Delete(\"/path\", func(ctx iris.Context) {\n\t\tctx.WriteString(expectedDelResponse)\n\t})\n\n\tapp.Delete(\"/path2\", func(ctx iris.Context) {\n\t\t_, err := ctx.Writef(\"%s%s\", expectedDelResponse, ctx.Request().Context().Value(\"_originalMethod\"))\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t})\n\n\te := httptest.New(t, app)\n\n\t// Test headers.\n\te.POST(\"/path\").WithHeader(\"X-HTTP-Method\", iris.MethodDelete).Expect().\n\t\tStatus(iris.StatusOK).Body().IsEqual(expectedDelResponse)\n\te.POST(\"/path\").WithHeader(\"X-HTTP-Method-Override\", iris.MethodDelete).Expect().\n\t\tStatus(iris.StatusOK).Body().IsEqual(expectedDelResponse)\n\te.POST(\"/path\").WithHeader(\"X-Method-Override\", iris.MethodDelete).Expect().\n\t\tStatus(iris.StatusOK).Body().IsEqual(expectedDelResponse)\n\n\t// Test form field value.\n\te.POST(\"/path\").WithFormField(\"_method\", iris.MethodDelete).Expect().\n\t\tStatus(iris.StatusOK).Body().IsEqual(expectedDelResponse)\n\n\t// Test URL Query (although it's the same as form field in this case).\n\te.POST(\"/path\").WithQuery(\"_method\", iris.MethodDelete).Expect().\n\t\tStatus(iris.StatusOK).Body().IsEqual(expectedDelResponse)\n\n\t// Test saved original method and\n\t// Test without registered \"POST\" route.\n\te.POST(\"/path2\").WithQuery(\"_method\", iris.MethodDelete).Expect().\n\t\tStatus(iris.StatusOK).Body().IsEqual(expectedDelResponse + iris.MethodPost)\n\n\t// Test simple POST request without method override fields.\n\te.POST(\"/path\").Expect().Status(iris.StatusOK).Body().IsEqual(expectedPostResponse)\n\n\t// Test simple DELETE request.\n\te.DELETE(\"/path\").Expect().Status(iris.StatusOK).Body().IsEqual(expectedDelResponse)\n}\n"
  },
  {
    "path": "middleware/modrevision/modrevision.go",
    "content": "package modrevision\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/context\"\n)\n\nfunc init() {\n\tcontext.SetHandlerName(\"iris/middleware/modrevision.*\", \"iris.modrevision\")\n}\n\n// Options holds the necessary values to render the server name, environment and build information.\n// See the `New` package-level function.\ntype Options struct {\n\t// The ServerName, e.g. Iris Server.\n\tServerName string\n\t// The Environment, e.g. development.\n\tEnv string\n\t// The Developer, e.g. kataras.\n\tDeveloper string\n\t// True to display the build time as unix (seconds).\n\tUnixTime bool\n\t// A non nil time location value to customize the display of the build time.\n\tTimeLocation *time.Location\n}\n\n// New returns an Iris Handler which renders\n// the server name (env), build information (if available)\n// and an OK message. The handler displays simple debug information such as build commit id and time.\n// It does NOT render information about the Go language itself or any operating system confgiuration\n// for security reasons.\n//\n// Example Code:\n//\n//\tapp.Get(\"/health\", modrevision.New(modrevision.Options{\n//\t ServerName:   \"Iris Server\",\n//\t Env:          \"development\",\n//\t Developer:    \"kataras\",\n//\t TimeLocation: time.FixedZone(\"Greece/Athens\", 7200),\n//\t}))\nfunc New(opts Options) context.Handler {\n\tbuildTime, buildRevision := context.BuildTime, context.BuildRevision\n\tif opts.UnixTime {\n\t\tif t, err := time.Parse(time.RFC3339, buildTime); err == nil {\n\t\t\tbuildTime = fmt.Sprintf(\"%d\", t.Unix())\n\t\t}\n\t} else if opts.TimeLocation != nil {\n\t\tif t, err := time.Parse(time.RFC3339, buildTime); err == nil {\n\t\t\tbuildTime = t.In(opts.TimeLocation).String()\n\t\t}\n\t}\n\n\tvar buildInfo string\n\tif buildInfo = opts.ServerName; buildInfo != \"\" {\n\t\tif env := opts.Env; env != \"\" {\n\t\t\tbuildInfo += fmt.Sprintf(\" (%s)\", env)\n\t\t}\n\t}\n\n\tif buildRevision != \"\" && buildTime != \"\" {\n\t\tbuildTitle := \">>>> build\"\n\t\ttab := strings.Repeat(\" \", len(buildTitle))\n\t\tbuildInfo += fmt.Sprintf(\"\\n\\n%s\\n%[2]srevision        %[3]s\\n%[2]sbuildtime       %[4]s\\n%[2]sdeveloper       %[5]s\",\n\t\t\tbuildTitle, tab, buildRevision, buildTime, opts.Developer)\n\t}\n\n\tcontents := []byte(buildInfo)\n\tif len(contents) > 0 {\n\t\tcontents = append(contents, []byte(\"\\n\\nOK\")...)\n\t} else {\n\t\tcontents = []byte(\"OK\")\n\t}\n\n\treturn func(ctx *context.Context) {\n\t\tctx.Write(contents)\n\t}\n}\n"
  },
  {
    "path": "middleware/monitor/expvar_uint64.go",
    "content": "package monitor\n\nimport (\n\t\"expvar\"\n\t\"strconv\"\n\t\"sync/atomic\"\n)\n\n// Uint64 completes the expvar metric interface, holds an uint64 value.\ntype Uint64 struct {\n\tvalue uint64\n}\n\n// Set sets v to value.\nfunc (v *Uint64) Set(value uint64) {\n\tatomic.StoreUint64(&v.value, value)\n}\n\n// Value returns the underline uint64 value.\nfunc (v *Uint64) Value() uint64 {\n\treturn atomic.LoadUint64(&v.value)\n}\n\n// String returns the text representation of the underline uint64 value.\nfunc (v *Uint64) String() string {\n\treturn strconv.FormatUint(atomic.LoadUint64(&v.value), 10)\n}\n\nfunc newUint64(name string) *Uint64 {\n\tv := new(Uint64)\n\texpvar.Publish(name, v)\n\treturn v\n}\n"
  },
  {
    "path": "middleware/monitor/monitor.go",
    "content": "package monitor\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\n\t\"github.com/shirou/gopsutil/v3/process\"\n)\n\nfunc init() {\n\tcontext.SetHandlerName(\"iris/middleware/monitor.*\", \"iris.monitor\")\n}\n\n// Options holds the optional fields for the Monitor structure.\ntype Options struct {\n\t// Optional process id, defaults to the current one.\n\tPID int32 `json:\"pid\" yaml:\"PID\"`\n\n\tRefreshInterval     time.Duration `json:\"refresh_interval\" yaml:\"RefreshInterval\"`\n\tViewRefreshInterval time.Duration `json:\"view_refresh_interval\" yaml:\"ViewRefreshInterval\"`\n\t// If more than zero enables line animation. Defaults to zero.\n\tViewAnimationInterval time.Duration `json:\"view_animation_interval\" yaml:\"ViewAnimationInterval\"`\n\t// The title of the monitor HTML document.\n\tViewTitle string `json:\"view_title\" yaml:\"ViewTitle\"`\n}\n\n// Monitor tracks and renders the server's process and operating system statistics.\n//\n// Look its `Stats` and `View` methods.\n// Initialize with the `New` package-level function.\ntype Monitor struct {\n\topts   Options\n\tHolder *StatsHolder\n\n\tviewBody []byte\n}\n\n// New returns a new Monitor.\n// Metrics stored through expvar standard package:\n// - pid_cpu\n// - pid_ram\n// - pid_conns\n// - os_cpu\n// - os_ram\n// - os_total_ram\n// - os_load_avg\n// - os_conns\n//\n// Check https://github.com/iris-contrib/middleware/tree/master/expmetric\n// which can be integrated with datadog or other platforms.\nfunc New(opts Options) *Monitor {\n\tif opts.PID == 0 {\n\t\topts.PID = int32(os.Getpid())\n\t}\n\n\tif opts.RefreshInterval <= 0 {\n\t\topts.RefreshInterval = 2 * opts.RefreshInterval\n\t}\n\n\tif opts.ViewRefreshInterval <= 0 {\n\t\topts.ViewRefreshInterval = opts.RefreshInterval\n\t}\n\n\tviewRefreshIntervalBytes := []byte(fmt.Sprintf(\"%d\", opts.ViewRefreshInterval.Milliseconds()))\n\tviewBody := bytes.Replace(defaultViewBody, viewRefreshIntervalTmplVar, viewRefreshIntervalBytes, 1)\n\tviewAnimationIntervalBytes := []byte(fmt.Sprintf(\"%d\", opts.ViewAnimationInterval.Milliseconds()))\n\tviewBody = bytes.Replace(viewBody, viewAnimationIntervalTmplVar, viewAnimationIntervalBytes, 2)\n\tviewTitleBytes := []byte(opts.ViewTitle)\n\tviewBody = bytes.Replace(viewBody, viewTitleTmplVar, viewTitleBytes, 2)\n\tproc, err := process.NewProcess(opts.PID)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tsh := startNewStatsHolder(proc, opts.RefreshInterval)\n\tm := &Monitor{\n\t\topts:     opts,\n\t\tHolder:   sh,\n\t\tviewBody: viewBody,\n\t}\n\n\treturn m\n}\n\n// Stop terminates the retrieve stats loop for\n// the process and the operating system statistics.\n// No other monitor instance should be initialized after the first Stop call.\nfunc (m *Monitor) Stop() {\n\tm.Holder.Stop()\n}\n\n// Stats sends the stats as json.\nfunc (m *Monitor) Stats(ctx *context.Context) {\n\tctx.JSON(m.Holder.GetStats())\n}\n\n// View renders a default view for the stats.\nfunc (m *Monitor) View(ctx *context.Context) {\n\tctx.ContentType(\"text/html\")\n\tctx.Write(m.viewBody)\n}\n"
  },
  {
    "path": "middleware/monitor/stats.go",
    "content": "package monitor\n\nimport (\n\t\"expvar\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/shirou/gopsutil/v3/cpu\"\n\t\"github.com/shirou/gopsutil/v3/load\"\n\t\"github.com/shirou/gopsutil/v3/mem\"\n\t\"github.com/shirou/gopsutil/v3/net\"\n\t\"github.com/shirou/gopsutil/v3/process\"\n)\n\n// Stats holds the process and operating system statistics values.\n//\n// Note that each statistic has its own expvar metric that you can use\n// to render e.g. through statsd. Available values:\n// * pid_cpu\n// * pid_ram\n// * pid_conns\n// * os_cpu\n// * os_ram\n// * os_total_ram\n// * os_load_avg\n// * os_conns\ntype Stats struct {\n\tPIDCPU   float64 `json:\"pid_cpu\" yaml:\"PIDCPU\"`\n\tPIDRAM   uint64  `json:\"pid_ram\" yaml:\"PIDRAM\"`\n\tPIDConns int64   `json:\"pid_conns\" yaml:\"PIDConns\"`\n\n\tOSCPU      float64 `json:\"os_cpu\" yaml:\"OSCPU\"`\n\tOSRAM      uint64  `json:\"os_ram\" yaml:\"OSRAM\"`\n\tOSTotalRAM uint64  `json:\"os_total_ram\" yaml:\"OSTotalRAM\"`\n\tOSLoadAvg  float64 `json:\"os_load_avg\" yaml:\"OSLoadAvg\"`\n\tOSConns    int64   `json:\"os_conns\" yaml:\"OSConns\"`\n}\n\n// StatsHolder holds and refreshes the statistics.\ntype StatsHolder struct {\n\tproc *process.Process\n\n\tstats *Stats\n\tmu    sync.RWMutex\n\n\tstarted bool\n\tcloseCh chan struct{}\n\terrCh   chan error\n}\n\nfunc startNewStatsHolder(proc *process.Process, refreshInterval time.Duration) *StatsHolder {\n\tsh := newStatsHolder(proc)\n\tsh.start(refreshInterval)\n\treturn sh\n}\n\nfunc newStatsHolder(proc *process.Process) *StatsHolder {\n\tsh := &StatsHolder{\n\t\tproc:    proc,\n\t\tstats:   new(Stats),\n\t\tcloseCh: make(chan struct{}),\n\t\terrCh:   make(chan error, 1),\n\t}\n\n\treturn sh\n}\n\n// Err returns a read-only channel which may be filled with errors\n// came from the refresh stats operation.\nfunc (sh *StatsHolder) Err() <-chan error {\n\treturn sh.errCh\n}\n\n// Stop terminates the routine retrieves the stats.\n// Note that no other monitor can be initialized after Stop.\nfunc (sh *StatsHolder) Stop() {\n\tif !sh.started {\n\t\treturn\n\t}\n\n\tsh.closeCh <- struct{}{}\n\tsh.started = false\n}\n\nfunc (sh *StatsHolder) start(refreshInterval time.Duration) {\n\tif sh.started {\n\t\treturn\n\t}\n\tsh.started = true\n\n\tonce.Do(func() {\n\t\tgo func() {\n\t\t\tticker := time.NewTicker(refreshInterval)\n\t\t\tdefer ticker.Stop()\n\n\t\t\tfor {\n\t\t\t\tselect {\n\t\t\t\tcase <-sh.closeCh:\n\t\t\t\t\t// close(sh.errCh)\n\t\t\t\t\treturn\n\t\t\t\tcase <-ticker.C:\n\t\t\t\t\terr := refresh(sh.proc)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t// push the error to the channel and continue the execution,\n\t\t\t\t\t\t// the only way to stop it is through its \"Stop\" method.\n\t\t\t\t\t\tsh.errCh <- err\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}()\n\t})\n}\n\nvar (\n\tonce = new(sync.Once)\n\n\tmetricPidCPU   = expvar.NewFloat(\"pid_cpu\")\n\tmetricPidRAM   = newUint64(\"pid_ram\")\n\tmetricPidConns = expvar.NewInt(\"pid_conns\")\n\n\tmetricOsCPU      = expvar.NewFloat(\"os_cpu\")\n\tmetricOsRAM      = newUint64(\"os_ram\")\n\tmetricOsTotalRAM = newUint64(\"os_total_ram\")\n\tmetricOsLoadAvg  = expvar.NewFloat(\"os_load_avg\")\n\tmetricOsConns    = expvar.NewInt(\"os_conns\")\n)\n\n// refresh updates the process and operating system statistics.\nfunc refresh(proc *process.Process) error {\n\t// Collect the stats.\n\t//\n\t// Process.\n\tpidCPU, err := proc.CPUPercent()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tpidRAM, err := proc.MemoryInfo()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tpidConns, err := net.ConnectionsPid(\"tcp\", proc.Pid)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Operating System.\n\tosCPU, err := cpu.Percent(0, false)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tosRAM, err := mem.VirtualMemory()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tosLoadAvg, err := load.Avg()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tosConns, err := net.Connections(\"tcp\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Update the fields.\n\t//\n\t// Process.\n\tmetricPidCPU.Set(pidCPU / 10)\n\tmetricPidRAM.Set(pidRAM.RSS)\n\tmetricPidConns.Set(int64(len(pidConns)))\n\n\t// Operating System.\n\tif len(osCPU) > 0 {\n\t\tmetricOsCPU.Set(osCPU[0])\n\t}\n\tmetricOsRAM.Set(osRAM.Used)\n\tmetricOsTotalRAM.Set(osRAM.Total)\n\tmetricOsLoadAvg.Set(osLoadAvg.Load1)\n\tmetricOsConns.Set(int64(len(osConns)))\n\n\treturn nil\n}\n\n// GetStats returns a copy of the latest stats available.\nfunc (sh *StatsHolder) GetStats() Stats {\n\tsh.mu.Lock()\n\tstatsCopy := Stats{\n\t\tPIDCPU:   metricPidCPU.Value(),\n\t\tPIDRAM:   metricPidRAM.Value(),\n\t\tPIDConns: metricPidConns.Value(),\n\n\t\tOSCPU:      metricOsCPU.Value(),\n\t\tOSRAM:      metricOsRAM.Value(),\n\t\tOSTotalRAM: metricOsTotalRAM.Value(),\n\t\tOSLoadAvg:  metricOsLoadAvg.Value(),\n\t\tOSConns:    metricOsConns.Value(),\n\t}\n\tsh.mu.Unlock()\n\n\treturn statsCopy\n}\n"
  },
  {
    "path": "middleware/monitor/view.go",
    "content": "package monitor\n\nvar (\n\tviewRefreshIntervalTmplVar   = []byte(`{{.ViewRefreshInterval}}`)\n\tviewAnimationIntervalTmplVar = []byte(`{{.ViewAnimationInterval}}`)\n\tviewTitleTmplVar             = []byte(`{{.ViewTitle}}`)\n\tdefaultViewBody              = []byte(`<!doctypehtml><html lang=en><meta charset=UTF-8><meta content=\"width=device-width,initial-scale=1\"name=viewport><link href=\"https://fonts.googleapis.com/css2?family=Roboto:wght@400;900&display=swap\"rel=stylesheet><script src=https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.0/chart.min.js></script><script src=https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns/dist/chartjs-adapter-date-fns.bundle.min.js></script><title>` + string(viewTitleTmplVar) + `</title><style>body{margin:0;font:16px/1.6 Roboto,sans-serif}.wrapper{max-width:900px;margin:0 auto;padding:30px 0}.title{text-align:center;margin-bottom:2em}.title h1{font-size:2em;padding:0;margin:0}.row{display:flex;margin-bottom:20px;align-items:center}.row .column:first-child{width:35%}.row .column:last-child{width:65%}.metric{color:#777;font-weight:900}h2{padding:0;margin:0;font-size:1.8em}h2 span{font-size:12px;color:#777}h2 span.ram_os{color:rgba(255,150,0,.8)}h2 span.ram_total{color:rgba(0,200,0,.8)}canvas{width:200px;height:180px}</style><section class=wrapper><div class=title><h1>` + string(viewTitleTmplVar) + `</h1></div><section class=charts><div class=row><div class=column><div class=metric>CPU Usage</div><h2 id=cpuMetric>0.00%</h2></div><div class=column><canvas id=cpuChart></canvas></div></div><div class=row><div class=column><div class=metric>Memory Usage</div><h2 id=ramMetric title=\"PID used / OS used / OS total\">0.00 MB</h2></div><div class=column><canvas id=ramChart></canvas></div></div><div class=row><div class=column><div class=metric>Response Time</div><h2 id=rtimeMetric>0ms</h2></div><div class=column><canvas id=rtimeChart></canvas></div></div><div class=row><div class=column><div class=metric>Open Connections</div><h2 id=connsMetric>0</h2></div><div class=column><canvas id=connsChart></canvas></div></div></section></section><script>\n            Chart.defaults.plugins.legend.display=!1,Chart.defaults.font.size=8,Chart.defaults.elements.line.backgroundColor=\"rgba(0, 172, 215, 0.25)\",Chart.defaults.elements.line.borderColor=\"rgba(0, 172, 215, 1)\",Chart.defaults.elements.line.borderWidth=2,Chart.defaults.plugins.tooltip.enabled=!1,Chart.defaults.elements.line.tension=.2,Chart.defaults.elements.point.radius=0,Chart.defaults.animation.duration=\"` + string(viewAnimationIntervalTmplVar) + `\",Chart.defaults.animation.easing=\"easeOutQuart\";const options={scales:{y:{beginAtZero:!0},x:{type:\"time\",time:{stepSize:30,unit:\"second\"},grid:{display:!1}}},responsive:!0,maintainAspectRatio:!1,animation:` + string(viewAnimationIntervalTmplVar) + `>0};\n\t\t\tconst cpuMetric=document.querySelector(\"#cpuMetric\"),ramMetric=document.querySelector(\"#ramMetric\"),rtimeMetric=document.querySelector(\"#rtimeMetric\"),connsMetric=document.querySelector(\"#connsMetric\"),cpuChartCtx=document.querySelector(\"#cpuChart\").getContext(\"2d\"),ramChartCtx=document.querySelector(\"#ramChart\").getContext(\"2d\"),rtimeChartCtx=document.querySelector(\"#rtimeChart\").getContext(\"2d\"),connsChartCtx=document.querySelector(\"#connsChart\").getContext(\"2d\"),cpuChart=createChart(cpuChartCtx),ramChart=createChart(ramChartCtx),rtimeChart=createChart(rtimeChartCtx),connsChart=createChart(connsChartCtx),charts=[cpuChart,ramChart,rtimeChart,connsChart];\n\t\t\tfunction createChart(t){return new Chart(t,{type:\"line\",data:{labels:[],datasets:[{label:\"\",data:[],fill:\"start\"}]},options:options})}\n            ramChart.data.datasets.push({data:[],fill:\"start\",backgroundColor:[\"rgba(255, 200, 0, .6)\"],borderColor:[\"rgba(255, 150, 0, .8)\"]}),ramChart.data.datasets.push({data:[],fill:\"start\",backgroundColor:[\"rgba(0, 255, 0, .4)\"],borderColor:[\"rgba(0, 200, 0, .8)\"]});\n            function formatBytes(a,b=2,k=1024){with(Math){let d=floor(log(a)/log(k));return 0==a?\"0 Bytes\":parseFloat((a/pow(k,d)).toFixed(max(0,b)))+\" \"+[\"Bytes\",\"KB\",\"MB\",\"GB\",\"TB\",\"PB\",\"EB\",\"ZB\",\"YB\"][d]}}\n            function refreshChart(a,t){cpu=a.pid_cpu.toFixed(1),cpuOS=a.os_cpu.toFixed(1),cpuMetric.innerHTML=cpu+\"% <span>\"+cpuOS+\"%</span>\",ramMetric.innerHTML=formatBytes(a.pid_ram)+'<span> / </span><span class=\"ram_os\">'+formatBytes(a.os_ram)+'<span><span> / </span><span class=\"ram_total\">'+formatBytes(a.os_total_ram)+\"</span>\",rtimeMetric.innerHTML=t+\"ms <span>client</span>\",connsMetric.innerHTML=a.pid_conns+\" <span>\"+a.os_conns+\"</span>\",cpuChart.data.datasets[0].data.push(cpu),ramChart.data.datasets[2].data.push((a.os_total_ram/1e6).toFixed(2)),ramChart.data.datasets[1].data.push((a.os_ram/1e6).toFixed(2)),ramChart.data.datasets[0].data.push((a.pid_ram/1e6).toFixed(2)),rtimeChart.data.datasets[0].data.push(t),connsChart.data.datasets[0].data.push(a.pid_conns);const s=(new Date).getTime();charts.forEach(a=>{a.data.labels.length>50&&(a.data.datasets.forEach(function(a){a.data.shift()}),a.data.labels.shift()),a.data.labels.push(s),a.update()}),setTimeout(fetchData,\" ` + string(viewRefreshIntervalTmplVar) + `\")}\n\t\t\tfunction fetchData(){var e=\"\",n=performance.now();fetch(window.location.href,{method:\"POST\",headers:{Accept:\"application/json\"},credentials:\"same-origin\"}).then(n=>(e=performance.now(),n.json())).then(o=>{refreshChart(o,Math.round(e-n))}).catch(console.error)}fetchData();</script></body></html>`)\n)\n"
  },
  {
    "path": "middleware/pprof/pprof.go",
    "content": "// Package pprof provides native pprof support via middleware. See _examples/pprof\npackage pprof\n\nimport (\n\t\"html/template\"\n\t\"net/http/pprof\"\n\trpprof \"runtime/pprof\"\n\t\"sort\"\n\n\t\"github.com/kataras/iris/v12/context\"\n)\n\nfunc init() {\n\tcontext.SetHandlerName(\"iris/middleware/pprof.*\", \"iris.profiling\")\n\n\t/* for our readers:\n\tapps.OnApplicationRegistered(func(app *iris.Application) {\n\t\tapp.Any(\"/debug/pprof/cmdline\", iris.FromStd(pprof.Cmdline))\n\t\tapp.Any(\"/debug/pprof/profile\", iris.FromStd(pprof.Profile))\n\t\tapp.Any(\"/debug/pprof/symbol\", iris.FromStd(pprof.Symbol))\n\t\tapp.Any(\"/debug/pprof/trace\", iris.FromStd(pprof.Trace))\n\n\t\tapp.Any(\"/debug/pprof /debug/pprof/{action:string}\", New())\n\t})\n\t*/\n}\n\n// net/http/pprof copy:\nvar profileDescriptions = map[string]string{\n\t\"allocs\":       \"A sampling of all past memory allocations\",\n\t\"block\":        \"Stack traces that led to blocking on synchronization primitives\",\n\t\"cmdline\":      \"The command line invocation of the current program\",\n\t\"goroutine\":    \"Stack traces of all current goroutines\",\n\t\"heap\":         \"A sampling of memory allocations of live objects. You can specify the gc GET parameter to run GC before taking the heap sample.\",\n\t\"mutex\":        \"Stack traces of holders of contended mutexes\",\n\t\"profile\":      \"CPU profile. You can specify the duration in the seconds GET parameter. After you get the profile file, use the go tool pprof command to investigate the profile.\",\n\t\"threadcreate\": \"Stack traces that led to the creation of new OS threads\",\n\t\"trace\":        \"A trace of execution of the current program. You can specify the duration in the seconds GET parameter. After you get the trace file, use the go tool trace command to investigate the trace.\",\n}\n\n// New returns a new pprof (profile, cmdline, symbol, goroutine, heap, threadcreate, debug/block) Middleware.\n// Note: Route MUST have the last named parameter wildcard named '{action:path}'.\n// Example:\n//\n//\tapp.HandleMany(\"GET\", \"/debug/pprof /debug/pprof/{action:path}\", pprof.New())\nfunc New() context.Handler {\n\treturn func(ctx *context.Context) {\n\t\tif action := ctx.Params().Get(\"action\"); action != \"\" {\n\t\t\tpprof.Handler(action).ServeHTTP(ctx.ResponseWriter(), ctx.Request())\n\t\t\treturn\n\t\t}\n\n\t\tctx.Header(\"X-Content-Type-Options\", \"nosniff\")\n\t\tctx.Header(\"Content-Type\", \"text/html; charset=utf-8\")\n\n\t\ttype profile struct {\n\t\t\tName  string\n\t\t\tHref  string\n\t\t\tDesc  string\n\t\t\tCount int\n\t\t}\n\t\ttype page struct {\n\t\t\tPath     string\n\t\t\tProfiles []profile\n\t\t}\n\n\t\tvar profiles []profile\n\t\tfor _, p := range rpprof.Profiles() {\n\t\t\tprofiles = append(profiles, profile{\n\t\t\t\tName:  p.Name(),\n\t\t\t\tHref:  p.Name() + \"?debug=1\",\n\t\t\t\tDesc:  profileDescriptions[p.Name()],\n\t\t\t\tCount: p.Count(),\n\t\t\t})\n\t\t}\n\n\t\t// Adding other profiles exposed from within this package\n\t\tfor _, p := range []string{\"cmdline\", \"profile\", \"trace\"} {\n\t\t\tprofiles = append(profiles, profile{\n\t\t\t\tName: p,\n\t\t\t\tHref: p,\n\t\t\t\tDesc: profileDescriptions[p],\n\t\t\t})\n\t\t}\n\n\t\tsort.Slice(profiles, func(i, j int) bool {\n\t\t\treturn profiles[i].Name < profiles[j].Name\n\t\t})\n\n\t\tif err := indexTmpl.Execute(ctx, page{\n\t\t\tPath:     ctx.Path(),\n\t\t\tProfiles: profiles,\n\t\t}); err != nil {\n\t\t\tctx.Application().Logger().Error(err)\n\t\t}\n\t}\n}\n\nvar indexTmpl = template.Must(template.New(\"index\").Parse(`<html>\n<head>\n<title>{{.Path}}</title>\n<style>\n.profile-name{\n\tdisplay:inline-block;\n\twidth:6rem;\n}\n</style>\n</head>\n<body>\n{{.Path}}<br>\n<br>\nTypes of profiles available:\n<table>\n<thead><td>Count</td><td>Profile</td></thead>\n{{ $path := .Path}}\n{{range .Profiles}}\n\t<tr>\n\t<td>{{.Count}}</td><td><a href={{$path}}/{{.Href}}>{{.Name}}</a></td>\n\t</tr>\n{{end}}\n</table>\n<a href=\"{{.Path}}/goroutine?debug=2\">full goroutine stack dump</a>\n<br/>\n<p>\nProfile Descriptions:\n<ul>\n{{range .Profiles}}\n<li><div class=profile-name>{{.Name}}:</div> {{.Desc}}</li>\n{{end}}\n</ul>\n</p>\n</body>\n</html>\n`))\n"
  },
  {
    "path": "middleware/rate/rate.go",
    "content": "// Package rate implements rate limiter for Iris client requests.\n// Example can be found at: _examples/request-ratelimit/main.go.\npackage rate\n\nimport (\n\t\"math\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\n\t\"golang.org/x/time/rate\"\n)\n\nfunc init() {\n\tcontext.SetHandlerName(\"iris/middleware/rate.(*Limiter).serveHTTP-fm\", \"iris.ratelimit\")\n}\n\n// Option declares a function which can be passed on `Limit` package-level\n// to modify its internal fields. Available Options are:\n// * ExceedHandler\n// * ClientData\n// * PurgeEvery\ntype Option func(*Limiter)\n\n// ExceedHandler is an `Option` that can be passed at the `Limit` package-level function.\n// It accepts a handler that will be executed every time a client tries to reach a page/resource\n// which is not accessible for that moment.\nfunc ExceedHandler(handler context.Handler) Option {\n\treturn func(l *Limiter) {\n\t\tl.exceedHandler = handler\n\t}\n}\n\n// ClientData is an `Option` that can be passed at the `Limit` package-level function.\n// It accepts a function which provides the Iris Context and should return custom data\n// that will be stored to the Client and be retrieved as `Get(ctx).Client.Data` later on.\nfunc ClientData(clientDataFunc func(ctx *context.Context) any) Option {\n\treturn func(l *Limiter) {\n\t\tl.clientDataFunc = clientDataFunc\n\t}\n}\n\n// PurgeEvery is an `Option` that can be passed at the `Limit` package-level function.\n// This function will check for old entries and remove them.\n//\n// E.g. Limit(..., PurgeEvery(time.Minute, 5*time.Minute)) to\n// check every 1 minute if a client's last visit was 5 minutes ago (\"old\" entry)\n// and remove it from the memory.\nfunc PurgeEvery(every time.Duration, maxLifetime time.Duration) Option {\n\tcondition := func(c *Client) bool {\n\t\t// for a custom purger the end-developer may use the c.Data filled from a `ClientData` option.\n\t\treturn time.Since(c.LastSeen()) > maxLifetime\n\t}\n\n\treturn func(l *Limiter) {\n\t\tgo func() {\n\t\t\tfor {\n\t\t\t\ttime.Sleep(every)\n\n\t\t\t\tl.Purge(condition)\n\t\t\t}\n\t\t}()\n\t}\n}\n\n// Every converts a minimum time interval between events to a limit.\n// Usage: Limit(Every(1*time.Minute), 3, options...)\nfunc Every(interval time.Duration) float64 {\n\tif interval <= 0 {\n\t\treturn Inf\n\t}\n\treturn 1 / interval.Seconds()\n}\n\ntype (\n\t// Limiter is featured with the necessary functions to limit requests per second.\n\t// It has a single exported method `Purge` which helps to manually remove\n\t// old clients from the memory. Limiter is not exposed by a function,\n\t// callers should use it inside an `Option` for the `Limit` package-level function.\n\tLimiter struct {\n\t\tclientDataFunc func(ctx *context.Context) any // fill the Client's Data field.\n\t\texceedHandler  context.Handler                // when too many requests.\n\n\t\tlimit     rate.Limit\n\t\tburstSize int\n\n\t\tclients map[string]*Client\n\t\tmu      sync.RWMutex // mutex for clients.\n\t}\n\n\t// Client holds some request information and the rate limiter itself.\n\t// It can be retrieved by the `Get` package-level function.\n\t// It can be used to manually add RateLimit response headers.\n\tClient struct {\n\t\tID      string\n\t\tData    any\n\t\tLimiter *rate.Limiter\n\n\t\tlastSeen time.Time\n\t\tmu       sync.RWMutex // mutex for lastSeen.\n\t}\n)\n\n// Inf is the infinite rate limit; it allows all events (even if burst is zero).\nconst Inf = math.MaxFloat64\n\n// Limit returns a new rate limiter handler that allows requests up to rate \"limit\" and permits\n// bursts of at most \"burst\" tokens. See `rate.SetKey(ctx, key string)` and `rate.Get` too.\n//\n// E.g. Limit(1, 5) to allow 1 request per second, with a maximum burst size of 5.\n//\n// See `ExceedHandler`, `ClientData` and `PurgeEvery` for the available \"options\".\nfunc Limit(limit float64, burst int, options ...Option) context.Handler {\n\tl := &Limiter{\n\t\tclients:   make(map[string]*Client),\n\t\tlimit:     rate.Limit(limit),\n\t\tburstSize: burst,\n\t\texceedHandler: func(ctx *context.Context) {\n\t\t\tctx.StopWithStatus(429) // Too Many Requests.\n\t\t},\n\t}\n\n\tfor _, opt := range options {\n\t\topt(l)\n\t}\n\n\treturn l.serveHTTP\n}\n\n// Purge removes client entries from the memory based on the given \"condition\".\nfunc (l *Limiter) Purge(condition func(*Client) bool) {\n\tl.mu.Lock()\n\tfor id, client := range l.clients {\n\t\tif condition(client) {\n\t\t\tdelete(l.clients, id)\n\t\t}\n\t}\n\tl.mu.Unlock()\n}\n\nfunc (l *Limiter) serveHTTP(ctx *context.Context) {\n\tid := getIdentifier(ctx)\n\tl.mu.RLock()\n\tclient, ok := l.clients[id]\n\tl.mu.RUnlock()\n\n\tif !ok {\n\t\tclient = &Client{\n\t\t\tID:      id,\n\t\t\tLimiter: rate.NewLimiter(l.limit, l.burstSize),\n\t\t}\n\n\t\tif l.clientDataFunc != nil {\n\t\t\tclient.Data = l.clientDataFunc(ctx)\n\t\t}\n\n\t\t//  if l.store(ctx, client) {\n\t\t// ^ no, let's keep it simple.\n\t\tl.mu.Lock()\n\t\tl.clients[id] = client\n\t\tl.mu.Unlock()\n\t}\n\n\tclient.mu.Lock()\n\tclient.lastSeen = time.Now()\n\tclient.mu.Unlock()\n\n\tctx.Values().Set(clientContextKey, client)\n\n\tif client.Limiter.Allow() {\n\t\tctx.Next()\n\t\treturn\n\t}\n\n\tif l.exceedHandler != nil {\n\t\tl.exceedHandler(ctx)\n\t}\n}\n\nconst identifierContextKey = \"iris.ratelimit.identifier\"\n\n// SetIdentifier can be called manually from a handler or a middleare\n// to change the identifier per client. The default key for a client is its Remote IP.\nfunc SetIdentifier(ctx *context.Context, key string) {\n\tctx.Values().Set(identifierContextKey, key)\n}\n\nfunc getIdentifier(ctx *context.Context) string {\n\tif entry, ok := ctx.Values().GetEntry(identifierContextKey); ok {\n\t\treturn entry.ValueRaw.(string)\n\t}\n\n\treturn ctx.RemoteAddr()\n}\n\nconst clientContextKey = \"iris.ratelimit.client\"\n\n// Get returns the current rate limited `Client`.\n// Use it when you want to log or add response headers based on the current request limitation.\n//\n// You can read more about X-RateLimit response headers at:\n// https://tools.ietf.org/id/draft-polli-ratelimit-headers-00.html.\n// A good example of that is the GitHub API itself: https://developer.github.com/v3/#rate-limiting\nfunc Get(ctx *context.Context) *Client {\n\tif v := ctx.Values().Get(clientContextKey); v != nil {\n\t\tif c, ok := v.(*Client); ok {\n\t\t\treturn c\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// LastSeen reports the last Client's visit.\nfunc (c *Client) LastSeen() time.Time {\n\tc.mu.RLock()\n\tt := c.lastSeen\n\tc.mu.RUnlock()\n\treturn t\n}\n\n// TokensFromDuration is a unit conversion function from a time duration to the number of tokens\n// which could be accumulated during that duration at a rate of limit tokens per second.\nfunc (c *Client) TokensFromDuration(d time.Duration) float64 {\n\t// rate.go#tokensFromDuration\n\tlimit := float64(c.Limiter.Limit())\n\tsec := float64(d/time.Second) * limit\n\tnsec := float64(d%time.Second) * limit\n\treturn sec + nsec/1e9\n}\n\n// DurationFromTokens is a unit conversion function from the number of tokens to the duration\n// of time it takes to accumulate them at a rate of limit tokens per second.\nfunc (c *Client) DurationFromTokens(tokens float64) time.Duration {\n\t// rate.go#durationFromTokens\n\tseconds := tokens / float64(c.Limiter.Limit())\n\treturn time.Nanosecond * time.Duration(1e9*seconds)\n}\n"
  },
  {
    "path": "middleware/recaptcha/recaptcha.go",
    "content": "package recaptcha\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/url\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/core/netutil\"\n)\n\nfunc init() {\n\tcontext.SetHandlerName(\"iris/middleware/recaptcha.*\", \"iris.reCAPTCHA\")\n}\n\nconst (\n\t// responseFormValue = \"g-recaptcha-response\"\n\tapiURL = \"https://www.google.com/recaptcha/api/siteverify\"\n)\n\n// Response is the google's recaptcha response as JSON.\ntype Response struct {\n\tChallengeTS time.Time `json:\"challenge_ts\"`\n\tHostname    string    `json:\"hostname\"`\n\tErrorCodes  []string  `json:\"error-codes\"`\n\tSuccess     bool      `json:\"success\"`\n}\n\n// Client is the default `net/http#Client` instance which\n// is used to send requests to the Google API.\n//\n// Change Client only if you know what you're doing.\nvar Client = netutil.Client(time.Duration(20 * time.Second))\n\n// New accepts the google's recaptcha secret key and returns\n// a middleware that verifies the request by sending a response to the google's API(V2-latest).\n// Secret key can be obtained by https://www.google.com/recaptcha.\n//\n// Used for communication between your site and Google. Be sure to keep it a secret.\n//\n// Use `SiteVerify` to verify a request inside another handler if needed.\nfunc New(secret string) context.Handler {\n\treturn func(ctx *context.Context) {\n\t\tif SiteVerify(ctx, secret).Success {\n\t\t\tctx.Next()\n\t\t}\n\t}\n}\n\n// SiteVerify accepts  context and the secret key(https://www.google.com/recaptcha) and\n// returns the google's recaptcha response, if `response.Success` is true then validation passed.\n//\n// Use `New` for middleware use instead.\nfunc SiteVerify(ctx *context.Context, secret string) (response Response) {\n\tgeneratedResponseID := ctx.FormValue(\"g-recaptcha-response\")\n\tif generatedResponseID == \"\" {\n\t\tresponse.ErrorCodes = append(response.ErrorCodes,\n\t\t\t\"form value[g-recaptcha-response] is empty\")\n\t\treturn\n\t}\n\n\tr, err := Client.PostForm(apiURL,\n\t\turl.Values{\n\t\t\t\"secret\":   {secret},\n\t\t\t\"response\": {generatedResponseID},\n\t\t\t// optional: let's no track our users \"remoteip\": {ctx.RemoteAddr()},\n\t\t},\n\t)\n\tif err != nil {\n\t\tresponse.ErrorCodes = append(response.ErrorCodes, err.Error())\n\t\treturn\n\t}\n\n\tbody, err := io.ReadAll(r.Body)\n\tr.Body.Close()\n\tif err != nil {\n\t\tresponse.ErrorCodes = append(response.ErrorCodes, err.Error())\n\t\treturn\n\t}\n\n\terr = json.Unmarshal(body, &response)\n\tif err != nil {\n\t\tresponse.ErrorCodes = append(response.ErrorCodes, err.Error())\n\t\treturn\n\t}\n\n\treturn response\n}\n\nvar recaptchaForm = `<form action=\"%s\" method=\"POST\">\n\t    <script src=\"https://www.google.com/recaptcha/api.js\"></script>\n\t\t<div class=\"g-recaptcha\" data-sitekey=\"%s\"></div>\n    \t<input type=\"submit\" name=\"button\" value=\"OK\">\n</form>`\n\n// GetFormHTML can be used on pages where empty form\n// is enough to verify that the client is not a \"robot\".\n// i.e: GetFormHTML(\"public_key\", \"/contact\")\n// will return form tag which imports the google API script,\n// with a simple submit button where redirects to the\n// \"postActionRelativePath\".\n//\n// The \"postActionRelativePath\" MUST use the `SiteVerify` or\n// followed by the `New()`'s context.Handler  (with the secret key this time)\n// in order to validate if the recaptcha verified.\n//\n// The majority of applications will use a custom form,\n// this function is here for ridiculous simple use cases.\n//\n// Example Code:\n//\n// Method: \"POST\" | Path: \"/contact\"\n//\n//\tfunc postContact(ctx *context.Context) {\n//\t\t// [...]\n//\t\tresponse := recaptcha.SiteVerify(ctx, recaptchaSecret)\n//\n//\t\tif response.Success {\n//\t\t\t// [your action here, i.e sendEmail(...)]\n//\t\t}\n//\n//\t\tctx.JSON(response)\n//\t}\n//\n// Method: \"GET\" | Path: \"/contact\"\n//\n//\tfunc getContact(ctx *context.Context) {\n//\t\t// render the recaptcha form\n//\t\tctx.HTML(recaptcha.GetFormHTML(recaptchaPublic, \"/contact\"))\n//\t}\nfunc GetFormHTML(dataSiteKey string, postActionRelativePath string) string {\n\treturn fmt.Sprintf(recaptchaForm, postActionRelativePath, dataSiteKey)\n}\n"
  },
  {
    "path": "middleware/recover/recover.go",
    "content": "// Package recover provides recovery for specific routes or for the whole app via middleware. See _examples/recover\npackage recover\n\nimport (\n\t\"fmt\"\n\t\"net/http/httputil\"\n\t\"runtime\"\n\t\"runtime/debug\"\n\n\t\"github.com/kataras/iris/v12/context\"\n)\n\nfunc init() {\n\tcontext.SetHandlerName(\"iris/middleware/recover.*\", \"iris.recover\") // this name won't work because New() is a function that returns a handler.\n}\n\n// New returns a new recovery middleware,\n// it recovers from panics and logs the\n// panic message to the application's logger \"Warn\" level.\nfunc New() context.Handler {\n\treturn func(ctx *context.Context) {\n\t\tdefer func() {\n\t\t\tif err := PanicRecoveryError(ctx, recover()); err != nil {\n\t\t\t\tctx.StopWithPlainError(500, err)\n\t\t\t\tctx.Application().Logger().Warn(err.LogMessage())\n\t\t\t} // else it's already handled.\n\t\t}()\n\n\t\tctx.Next()\n\t}\n}\n\n// PanicRecoveryError returns a new ErrPanicRecovery error.\nfunc PanicRecoveryError(ctx *context.Context, err any) *context.ErrPanicRecovery {\n\tif recoveryErr, ok := ctx.IsRecovered(); ok {\n\t\t// If registered before any other recovery middleware, get its error.\n\t\t// Because of defer this will be executed last, after the recovery middleware in this case.\n\t\treturn recoveryErr\n\t}\n\n\tif err == nil {\n\t\treturn nil\n\t} else if ctx.IsStopped() {\n\t\treturn nil\n\t}\n\n\tvar callers []string\n\tfor i := 2; ; /* 1 for New() 2 for NewPanicRecoveryError */ i++ {\n\t\t_, file, line, got := runtime.Caller(i)\n\t\tif !got {\n\t\t\tbreak\n\t\t}\n\n\t\tcallers = append(callers, fmt.Sprintf(\"%s:%d\", file, line))\n\t}\n\n\t// get the list of registered handlers and the\n\t// handler which panic derived from.\n\thandlers := ctx.Handlers()\n\thandlersFileLines := make([]string, 0, len(handlers))\n\tcurrentHandlerIndex := ctx.HandlerIndex(-1)\n\tcurrentHandlerFileLine := \"???\"\n\tfor i, h := range ctx.Handlers() {\n\t\tfile, line := context.HandlerFileLine(h)\n\t\tfileline := fmt.Sprintf(\"%s:%d\", file, line)\n\t\thandlersFileLines = append(handlersFileLines, fileline)\n\t\tif i == currentHandlerIndex {\n\t\t\tcurrentHandlerFileLine = fileline\n\t\t}\n\t}\n\n\t// see accesslog.wasRecovered too.\n\trecoveryErr := &context.ErrPanicRecovery{\n\t\tCause:                  err,\n\t\tCallers:                callers,\n\t\tStack:                  debug.Stack(),\n\t\tRegisteredHandlers:     handlersFileLines,\n\t\tCurrentHandlerFileLine: currentHandlerFileLine,\n\t\tCurrentHandlerName:     ctx.HandlerName(),\n\t\tRequest:                getRequestLogs(ctx),\n\t}\n\n\treturn recoveryErr\n}\n\nfunc getRequestLogs(ctx *context.Context) string {\n\trawReq, _ := httputil.DumpRequest(ctx.Request(), false)\n\treturn string(rawReq)\n}\n"
  },
  {
    "path": "middleware/requestid/requestid.go",
    "content": "package requestid\n\nimport (\n\t\"crypto/sha256\"\n\t\"encoding/hex\"\n\t\"net/http/httputil\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\n\t\"github.com/google/uuid\"\n)\n\nfunc init() {\n\tcontext.SetHandlerName(\"iris/middleware/requestid.*\", \"iris.request.id\")\n}\n\nconst xRequestIDHeaderKey = \"X-Request-Id\"\n\n// Generator defines the function which should extract or generate\n// a Request ID. See `DefaultGenerator` and `New` package-level functions.\ntype Generator func(ctx *context.Context) string\n\n// DefaultGenerator is the default `Generator` that is used\n// when nil is passed on `New` package-level function.\n// It extracts the ID from the \"X-Request-ID\" request header value\n// or, if missing, it generates a new UUID(v4) and sets the header and context value.\n//\n// See `Get` package-level function too.\nvar DefaultGenerator Generator = func(ctx *context.Context) string {\n\tid := ctx.ResponseWriter().Header().Get(xRequestIDHeaderKey)\n\tif id != \"\" {\n\t\treturn id\n\t}\n\n\tid = ctx.GetHeader(xRequestIDHeaderKey)\n\tif id == \"\" {\n\t\tuid, err := uuid.NewRandom()\n\t\tif err != nil {\n\t\t\tctx.StopWithStatus(500)\n\t\t\treturn \"\"\n\t\t}\n\n\t\tid = uid.String()\n\t}\n\n\tctx.Header(xRequestIDHeaderKey, id)\n\treturn id\n}\n\n// HashGenerator uses the request's hash to generate a fixed-length Request ID.\n// Note that one or many requests may contain the same ID, so it's not unique.\nfunc HashGenerator(includeBody bool) Generator {\n\treturn func(ctx *context.Context) string {\n\t\tctx.Header(xRequestIDHeaderKey, Hash(ctx, includeBody))\n\t\treturn DefaultGenerator(ctx)\n\t}\n}\n\n// New returns a new request id middleware.\n// It optionally accepts an ID Generator.\n// The Generator can stop the handlers chain with an error or\n// return a valid ID (string).\n// If it's nil then the `DefaultGenerator` will be used instead.\nfunc New(generator ...Generator) context.Handler {\n\tgen := DefaultGenerator\n\tif len(generator) > 0 {\n\t\tgen = generator[0]\n\t}\n\n\treturn func(ctx *context.Context) {\n\t\tif Get(ctx) != \"\" {\n\t\t\tctx.Next()\n\t\t\treturn\n\t\t}\n\n\t\tid := gen(ctx)\n\t\tif ctx.IsStopped() {\n\t\t\t// ctx.Next checks that\n\t\t\t// but we don't want to call SetID if generator failed.\n\t\t\treturn\n\t\t}\n\n\t\tctx.SetID(id)\n\t\tctx.Next()\n\t}\n}\n\n// Get returns the Request ID or empty string.\n//\n// A shortcut of `context.GetID().(string)`.\nfunc Get(ctx *context.Context) string {\n\tv := ctx.GetID()\n\tif v != nil {\n\t\tif id, ok := v.(string); ok {\n\t\t\treturn id\n\t\t}\n\t}\n\n\treturn \"\"\n}\n\n// Hash returns the sha1 hash of the request.\n// It does not capture error, instead it returns an empty string.\nfunc Hash(ctx *context.Context, includeBody bool) string {\n\th := sha256.New() // sha1 fits here as well.\n\tb, err := httputil.DumpRequest(ctx.Request(), includeBody)\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\th.Write(b)\n\treturn hex.EncodeToString(h.Sum(nil))\n}\n"
  },
  {
    "path": "middleware/requestid/requestid_test.go",
    "content": "package requestid_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/httptest\"\n\t\"github.com/kataras/iris/v12/middleware/requestid\"\n)\n\nfunc TestRequestID(t *testing.T) {\n\tapp := iris.New()\n\th := func(ctx iris.Context) {\n\t\tctx.WriteString(requestid.Get(ctx))\n\t}\n\n\tdef := app.Party(\"/default\")\n\t{\n\t\tdef.Use(requestid.New())\n\t\tdef.Get(\"/\", h)\n\t}\n\n\tconst expectedCustomID = \"my_id\"\n\tcustom := app.Party(\"/custom\")\n\t{\n\t\tcustomGen := func(ctx *context.Context) string {\n\t\t\treturn expectedCustomID\n\t\t}\n\n\t\tcustom.Use(requestid.New(customGen))\n\t\tcustom.Get(\"/\", h)\n\t}\n\n\tconst expectedErrMsg = \"no id\"\n\tcustomWithErr := app.Party(\"/custom_err\")\n\t{\n\t\tcustomGen := func(ctx *context.Context) string {\n\t\t\tctx.StopWithText(iris.StatusUnauthorized, expectedErrMsg)\n\t\t\treturn \"\"\n\t\t}\n\n\t\tcustomWithErr.Use(requestid.New(customGen))\n\t\tcustomWithErr.Get(\"/\", h)\n\t}\n\n\tconst expectedCustomIDFromOtherMiddleware = \"my custom id\"\n\tchangeID := app.Party(\"/custom_change_id\")\n\t{\n\t\tchangeID.Use(func(ctx iris.Context) {\n\t\t\tctx.SetID(expectedCustomIDFromOtherMiddleware)\n\t\t\tctx.Next()\n\t\t})\n\t\tchangeID.Use(requestid.New())\n\t\tchangeID.Get(\"/\", h)\n\t}\n\n\tconst expectedClientSentID = \"client sent id\"\n\tclientSentID := app.Party(\"/client_id\")\n\t{\n\t\tclientSentID.Use(requestid.New())\n\t\tclientSentID.Get(\"/\", h)\n\t}\n\n\te := httptest.New(t, app)\n\te.GET(\"/default\").Expect().Status(httptest.StatusOK).Body().NotEmpty()\n\te.GET(\"/custom\").Expect().Status(httptest.StatusOK).Body().IsEqual(expectedCustomID)\n\te.GET(\"/custom_err\").Expect().Status(httptest.StatusUnauthorized).Body().IsEqual(expectedErrMsg)\n\te.GET(\"/custom_change_id\").Expect().Status(httptest.StatusOK).Body().IsEqual(expectedCustomIDFromOtherMiddleware)\n\te.GET(\"/client_id\").WithHeader(\"X-Request-Id\", expectedClientSentID).Expect().Header(\"X-Request-Id\").IsEqual(expectedClientSentID)\n}\n"
  },
  {
    "path": "middleware/rewrite/rewrite.go",
    "content": "package rewrite\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"os\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/core/router\"\n\n\t\"github.com/kataras/golog\"\n\t\"gopkg.in/yaml.v3\"\n)\n\n// Options holds the developer input to customize\n// the redirects for the Rewrite Engine.\n// Look the `New` and `Load` package-level functions.\ntype Options struct {\n\t// RedirectMatch accepts a slice of lines\n\t// of form:\n\t// REDIRECT_CODE PATH_PATTERN TARGET_PATH\n\t// Example: []{\"301 /seo/(.*) /$1\"}.\n\tRedirectMatch []string `json:\"redirectMatch\" yaml:\"RedirectMatch\"`\n\n\t// Root domain requests redirect automatically to primary subdomain.\n\t// Example: \"www\" to redirect always to www.\n\t// Note that you SHOULD NOT create a www subdomain inside the Iris Application.\n\t// This field takes care of it for you, the root application instance\n\t// will be used to serve the requests.\n\tPrimarySubdomain string `json:\"primarySubdomain\" yaml:\"PrimarySubdomain\"`\n}\n\n// LoadOptions loads rewrite Options from a system file.\nfunc LoadOptions(filename string) (opts Options) {\n\text := \".yml\"\n\tif index := strings.LastIndexByte(filename, '.'); index > 1 && len(filename)-1 > index {\n\t\text = filename[index:]\n\t}\n\n\tf, err := os.Open(filename)\n\tif err != nil {\n\t\tpanic(\"iris: rewrite: \" + err.Error())\n\t}\n\tdefer f.Close()\n\n\tswitch ext {\n\tcase \".yaml\", \".yml\":\n\t\terr = yaml.NewDecoder(f).Decode(&opts)\n\tcase \".json\":\n\t\terr = json.NewDecoder(f).Decode(&opts)\n\tdefault:\n\t\tpanic(\"iris: rewrite: unexpected file extension: \" + filename)\n\t}\n\n\tif err != nil {\n\t\tpanic(\"iris: rewrite: decode: \" + err.Error())\n\t}\n\n\treturn\n}\n\n// Rewrite is a struct that represents a rewrite engine for Iris web framework.\n// It contains a slice of redirect rules, an options struct, a logger, and a domain validator function.\n// It provides methods to create, configure, and apply rewrite rules to HTTP requests and responses.\n//\n// Navigate through _examples/routing/rewrite for more.\ntype Engine struct {\n\tredirects []*redirectMatch\n\toptions   Options\n\n\tlogger          *golog.Logger\n\tdomainValidator func(string) bool\n}\n\n// Load decodes the \"filename\" options\n// and returns a new Rewrite Engine Router Wrapper.\n// It panics on errors.\n// Usage:\n// redirects := Load(\"redirects.yml\")\n// app.WrapRouter(redirects)\n// See `New` too.\nfunc Load(filename string) router.WrapperFunc {\n\topts := LoadOptions(filename)\n\tengine, err := New(opts)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn engine.Rewrite\n}\n\n// New returns a new Rewrite Engine based on \"opts\".\n// It reports any parser error.\n// See its `Handler` or `Rewrite` methods. Depending\n// on the needs, select one.\nfunc New(opts Options) (*Engine, error) {\n\tredirects := make([]*redirectMatch, 0, len(opts.RedirectMatch))\n\n\tfor _, line := range opts.RedirectMatch {\n\t\tr, err := parseRedirectMatchLine(line)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tredirects = append(redirects, r)\n\t}\n\n\tif opts.PrimarySubdomain != \"\" && !strings.HasSuffix(opts.PrimarySubdomain, \".\") {\n\t\topts.PrimarySubdomain += \".\" // www -> www.\n\t}\n\n\te := &Engine{\n\t\toptions:   opts,\n\t\tredirects: redirects,\n\t\tdomainValidator: func(root string) bool {\n\t\t\treturn !strings.HasSuffix(root, localhost)\n\t\t},\n\t}\n\treturn e, nil\n}\n\n// SetLogger attachs a logger to the Rewrite Engine,\n// used only for debugging.\n// Defaults to nil.\nfunc (e *Engine) SetLogger(logger *golog.Logger) *Engine {\n\te.logger = logger.Child(e).SetChildPrefix(\"rewrite\")\n\treturn e\n}\n\n// init the request logging with [DBUG].\nfunc (e *Engine) initDebugf(format string, args ...any) {\n\tif e.logger == nil {\n\t\treturn\n\t}\n\n\te.logger.Debugf(format, args...)\n}\n\nvar skipDBUGSpace = strings.Repeat(\" \", 7)\n\n// continue debugging the same request with new lines and spacing,\n// easier to read.\nfunc (e *Engine) debugf(format string, args ...any) {\n\tif e.logger == nil || e.logger.Level < golog.DebugLevel {\n\t\treturn\n\t}\n\n\tfmt.Fprintf(e.logger.Printer, skipDBUGSpace+format, args...)\n}\n\n// Handler is an Iris Handler that can be used as a router or party or route middleware.\n// For a global alternative, if you want to wrap the entire Iris Application\n// use the `Wrapper` instead.\n// Usage:\n// app.UseRouter(engine.Handler)\nfunc (e *Engine) Handler(ctx *context.Context) {\n\te.Rewrite(ctx.ResponseWriter(), ctx.Request(), func(http.ResponseWriter, *http.Request) {\n\t\tctx.Next()\n\t})\n}\n\nconst localhost = \"localhost\"\n\n// Rewrite is used to wrap the entire Iris Router.\n// Rewrite is a bit faster than Handler because it's executed\n// even before any route matched and it stops on redirect pattern match.\n// Use it to wrap the entire Iris Application, otherwise look `Handler` instead.\n//\n// Usage:\n// app.WrapRouter(engine.Rewrite).\nfunc (e *Engine) Rewrite(w http.ResponseWriter, r *http.Request, routeHandler http.HandlerFunc) {\n\tif primarySubdomain := e.options.PrimarySubdomain; primarySubdomain != \"\" {\n\t\thostport := context.GetHost(r)\n\t\troot := context.GetDomain(hostport)\n\n\t\te.initDebugf(\"Begin request: full host: %s and root domain: %s\\n\", hostport, root)\n\t\t// Note:\n\t\t// localhost and 127.0.0.1 are not supported for subdomain rewrite, by purpose,\n\t\t// use a virtual host instead.\n\t\t// GetDomain will return will return localhost or www.localhost\n\t\t// on expected loopbacks.\n\t\tif e.domainValidator(root) {\n\t\t\troot += getPort(hostport)\n\t\t\tsubdomain := strings.TrimSuffix(hostport, root)\n\n\t\t\te.debugf(\"Domain is not a loopback, requested subdomain: %s\\n\", subdomain)\n\n\t\t\tif subdomain == \"\" {\n\t\t\t\t// we are in root domain, full redirect to its primary subdomain.\n\t\t\t\tnewHost := primarySubdomain + root\n\t\t\t\te.debugf(\"Redirecting from root domain to: %s\\n\", newHost)\n\t\t\t\tr.Host = newHost\n\t\t\t\tr.URL.Host = newHost\n\t\t\t\thttp.Redirect(w, r, r.URL.String(), http.StatusMovedPermanently)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif subdomain == primarySubdomain {\n\t\t\t\t// keep root domain as the Host field inside the next handlers,\n\t\t\t\t// for consistently use and\n\t\t\t\t// to bypass the subdomain router (`routeHandler`)\n\t\t\t\t// do not return, redirects should be respected.\n\t\t\t\trootHost := strings.TrimPrefix(hostport, subdomain)\n\t\t\t\te.debugf(\"Request host field was modified to: %s. Proceed without redirection\\n\", rootHost)\n\t\t\t\t// modify those for the next redirects or the route handler.\n\t\t\t\tr.Host = rootHost\n\t\t\t\tr.URL.Host = rootHost\n\t\t\t}\n\n\t\t\t// maybe other subdomain or not at all, let's continue.\n\t\t} else {\n\t\t\te.debugf(\"Primary subdomain is: %s but redirect response was not sent. Domain is a loopback?\\n\", primarySubdomain)\n\t\t}\n\t}\n\n\tfor _, rd := range e.redirects {\n\t\tsrc := r.URL.Path\n\t\tif !rd.isRelativePattern {\n\t\t\t// don't change the request, use a full redirect.\n\t\t\tsrc = context.GetScheme(r) + context.GetHost(r) + r.URL.RequestURI()\n\t\t}\n\n\t\tif target, ok := rd.matchAndReplace(src); ok {\n\t\t\tif target == src {\n\t\t\t\te.debugf(\"WARNING: source and target URLs match: %s\\n\", src)\n\t\t\t\trouteHandler(w, r)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif rd.noRedirect {\n\t\t\t\tu, err := r.URL.Parse(target)\n\t\t\t\tif err != nil {\n\t\t\t\t\thttp.Error(w, err.Error(), http.StatusMisdirectedRequest)\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\te.debugf(\"No redirect: handle request: %s as: %s\\n\", r.RequestURI, u)\n\t\t\t\tr.URL = u\n\t\t\t\trouteHandler(w, r)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif !rd.isRelativePattern {\n\t\t\t\t// this performs better, no need to check query or host,\n\t\t\t\t// the uri already built.\n\t\t\t\te.debugf(\"Full redirect: from: %s to: %s\\n\", src, target)\n\t\t\t\trouter.RedirectAbsolute(w, r, target, rd.code)\n\t\t\t} else {\n\t\t\t\te.debugf(\"Path redirect: from: %s to: %s\\n\", src, target)\n\t\t\t\thttp.Redirect(w, r, target, rd.code)\n\t\t\t}\n\n\t\t\treturn\n\t\t}\n\t}\n\n\trouteHandler(w, r)\n}\n\ntype redirectMatch struct {\n\tcode    int\n\tpattern *regexp.Regexp\n\ttarget  string\n\n\tisRelativePattern bool\n\tnoRedirect        bool\n}\n\nfunc (r *redirectMatch) matchAndReplace(src string) (string, bool) {\n\tif r.pattern.MatchString(src) {\n\t\tif match := r.pattern.ReplaceAllString(src, r.target); match != \"\" {\n\t\t\treturn match, true\n\t\t}\n\t}\n\n\treturn \"\", false\n}\n\nfunc parseRedirectMatchLine(s string) (*redirectMatch, error) {\n\tparts := strings.Split(strings.TrimSpace(s), \" \")\n\tif len(parts) != 3 {\n\t\treturn nil, fmt.Errorf(\"redirect match: invalid line: %s\", s)\n\t}\n\n\tcodeStr, pattern, target := parts[0], parts[1], parts[2]\n\n\tfor i, ch := range codeStr {\n\t\tif !isDigit(ch) {\n\t\t\treturn nil, fmt.Errorf(\"redirect match: status code digits: %s [%d:%c]\", codeStr, i, ch)\n\t\t}\n\t}\n\n\tcode, err := strconv.Atoi(codeStr)\n\tif err != nil {\n\t\t// this should not happen, we check abt digit\n\t\t// and correctly position the error too but handle it.\n\t\treturn nil, fmt.Errorf(\"redirect match: status code digits: %s: %v\", codeStr, err)\n\t}\n\n\tregex := regexp.MustCompile(pattern)\n\tif regex.MatchString(target) {\n\t\treturn nil, fmt.Errorf(\"redirect match: loop detected: pattern: %s vs target: %s\", pattern, target)\n\t}\n\n\tv := &redirectMatch{\n\t\tcode:              code,\n\t\tpattern:           regex,\n\t\ttarget:            target,\n\t\tnoRedirect:        code <= 0,\n\t\tisRelativePattern: pattern[0] == '/', // search by path.\n\t}\n\n\treturn v, nil\n}\n\nfunc isDigit(ch rune) bool {\n\treturn '0' <= ch && ch <= '9'\n}\n\nfunc getPort(hostport string) string { // returns :port, note that this is only called on non-loopbacks.\n\tif portIdx := strings.IndexByte(hostport, ':'); portIdx > 0 {\n\t\treturn hostport[portIdx:]\n\t}\n\n\treturn \"\"\n}\n"
  },
  {
    "path": "middleware/rewrite/rewrite_test.go",
    "content": "package rewrite\n\nimport \"testing\"\n\nfunc TestRedirectMatch(t *testing.T) {\n\ttests := []struct {\n\t\tline     string\n\t\tparseErr string\n\t\tinputs   map[string]string // input, expected. Order should not matter.\n\t}{\n\t\t{\n\t\t\t\"301 /seo/(.*) /$1\",\n\t\t\t\"\",\n\t\t\tmap[string]string{\n\t\t\t\t\"/seo/path\": \"/path\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"301 /old(.*) /deprecated$1\",\n\t\t\t\"\",\n\t\t\tmap[string]string{\n\t\t\t\t\"/old\":            \"/deprecated\",\n\t\t\t\t\"/old/any\":        \"/deprecated/any\",\n\t\t\t\t\"/old/thing/here\": \"/deprecated/thing/here\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"301 /old(.*) /\",\n\t\t\t\"\",\n\t\t\tmap[string]string{\n\t\t\t\t\"/oldblabla\":      \"/\",\n\t\t\t\t\"/old/any\":        \"/\",\n\t\t\t\t\"/old/thing/here\": \"/\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"301 /old/(.*) /deprecated/$1\",\n\t\t\t\"\",\n\t\t\tmap[string]string{\n\t\t\t\t\"/old/\":           \"/deprecated/\",\n\t\t\t\t\"/old/any\":        \"/deprecated/any\",\n\t\t\t\t\"/old/thing/here\": \"/deprecated/thing/here\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"3d /seo/(.*) /$1\",\n\t\t\t\"redirect match: status code digits: 3d [1:d]\",\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"301 /$1\",\n\t\t\t\"redirect match: invalid line: 301 /$1\",\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"301 /* /$1\",\n\t\t\t\"redirect match: loop detected: pattern: /* vs target: /$1\",\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"301 /* /\",\n\t\t\t\"redirect match: loop detected: pattern: /* vs target: /\",\n\t\t\tnil,\n\t\t},\n\t}\n\n\tfor i, tt := range tests {\n\t\tr, err := parseRedirectMatchLine(tt.line)\n\t\tif err != nil {\n\t\t\tif tt.parseErr == \"\" {\n\t\t\t\tt.Fatalf(\"[%d] unexpected parse error: %v\", i, err)\n\t\t\t}\n\n\t\t\terrStr := err.Error()\n\t\t\tif tt.parseErr != err.Error() {\n\t\t\t\tt.Fatalf(\"[%d] a parse error was expected but it differs: expected: %s but got: %s\", i, tt.parseErr, errStr)\n\t\t\t}\n\t\t} else if tt.parseErr != \"\" {\n\t\t\tt.Fatalf(\"[%d] expected an error of: %s but got nil\", i, tt.parseErr)\n\t\t}\n\n\t\tfor input, expected := range tt.inputs {\n\t\t\tgot, _ := r.matchAndReplace(input)\n\t\t\tif expected != got {\n\t\t\t\tt.Fatalf(`[%d:%s] expected: \"%s\" but got: \"%s\"`, i, input, expected, got)\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "mvc/aliases.go",
    "content": "package mvc\n\nimport (\n\t\"github.com/kataras/iris/v12/hero\"\n\t\"github.com/kataras/iris/v12/versioning\"\n)\n\ntype (\n\t// Result is a type alias for the `hero#Result`, useful for output controller's methods.\n\tResult = hero.Result\n\t// Response is a type alias for the `hero#Response`, useful for output controller's methods.\n\tResponse = hero.Response\n\t// View is a type alias for the `hero#View`, useful for output controller's methods.\n\tView = hero.View\n\t// Code is a type alias for the `hero#Code`, useful for\n\t// http error handling in controllers.\n\t// This can be one of the input parameters of the `Controller.HandleHTTPError`.\n\tCode = hero.Code\n\t// Err is a type alias for the `hero#Err`.\n\t// It is a special type for error stored in mvc responses or context.\n\t// It's used for a builtin dependency to map the error given by a previous\n\t// method or middleware.\n\tErr = hero.Err\n\t// DeprecationOptions describes the deprecation headers key-values.\n\t// Is a type alias for the `versioning#DeprecationOptions`.\n\t//\n\t// See `Deprecated` package-level option.\n\tDeprecationOptions = versioning.DeprecationOptions\n)\n\n// Try is a type alias for the `hero#Try`,\n// useful to return a result based on two cases: failure(including panics) and a succeess.\nvar Try = hero.Try\n"
  },
  {
    "path": "mvc/controller.go",
    "content": "package mvc\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/core/router\"\n\t\"github.com/kataras/iris/v12/hero\"\n\t\"github.com/kataras/iris/v12/macro\"\n)\n\n// BaseController is the optional controller interface, if it's\n// completed by the end controller then the BeginRequest and EndRequest\n// are called between the controller's method responsible for the incoming request.\ntype BaseController interface {\n\tBeginRequest(*context.Context)\n\tEndRequest(*context.Context)\n}\n\ntype shared interface {\n\tName() string\n\tRouter() router.Party\n\tGetRoute(methodName string) *router.Route\n\tGetRoutes(methodName string) []*router.Route\n\tHandle(httpMethod, path, funcName string, middleware ...context.Handler) *router.Route\n\tHandleMany(httpMethod, path, funcName string, middleware ...context.Handler) []*router.Route\n}\n\n// BeforeActivation is being used as the only one input argument of a\n// `func(c *Controller) BeforeActivation(b mvc.BeforeActivation) {}`.\n//\n// It's being called before the controller's dependencies binding to the fields or the input arguments\n// but before server ran.\n//\n// It's being used to customize a controller if needed inside the controller itself,\n// it's called once per application.\ntype BeforeActivation interface {\n\tshared\n\tDependencies() *hero.Container\n}\n\n// AfterActivation is being used as the only one input argument of a\n// `func(c *Controller) AfterActivation(a mvc.AfterActivation) {}`.\n//\n// It's being called after the `BeforeActivation`,\n// and after controller's dependencies bind-ed to the fields or the input arguments but before server ran.\n//\n// It's being used to customize a controller if needed inside the controller itself,\n// it's called once per application.\ntype AfterActivation interface {\n\tshared\n\tSingleton() bool\n\tDependenciesReadOnly() []*hero.Dependency\n}\n\nvar (\n\t_ BeforeActivation = (*ControllerActivator)(nil)\n\t_ AfterActivation  = (*ControllerActivator)(nil)\n)\n\n// IgnoreEmbeddedControllers is a global variable which indicates whether\n// the controller's method parser should skip converting embedded struct's methods to http handlers.\n//\n// If no global use is necessary, developers can do the same for individual controllers\n// through the `IgnoreEmbedded` Controller Option on `mvc.Application.Handle` method.\n//\n// Defaults to false.\nvar IgnoreEmbeddedControllers = false\n\n// ControllerActivator returns a new controller type info description.\n// Its functionality can be overridden by the end-dev.\ntype ControllerActivator struct {\n\tapp *Application\n\n\tinjector *hero.Struct\n\n\t// initRef BaseController // the BaseController as it's passed from the end-dev.\n\tValue reflect.Value // the BaseController's Value.\n\tType  reflect.Type  // raw type of the BaseController (initRef).\n\t// FullName it's the last package path segment + \".\" + the Name.\n\t// i.e: if login-example/user/controller.go, the FullName is \"user.Controller\".\n\tfullName string\n\n\t// the already-registered routes, key = the controller's function name.\n\t// End-devs can change some properties of the *Route on the `BeforeActivator` by using the\n\t// `GetRoute/GetRoutes(functionName)`.\n\troutes map[string][]*router.Route\n\n\tskipMethodNames []string\n\t// BeginHandlers is a slice of middleware for this controller.\n\t// These handlers will be prependend to each one of\n\t// the route that this controller will register(Handle/HandleMany/struct methods)\n\t// to the targeted Party.\n\t// Look the `Use` method too.\n\tBeginHandlers context.Handlers\n\n\t// true if this controller listens and serves to websocket events.\n\tservesWebsocket bool\n\n\t// true to skip the internal \"activate\".\n\tactivated bool\n}\n\n// NameOf returns the package name + the struct type's name,\n// it's used to take the full name of an Controller, the `ControllerActivator#Name`.\nfunc NameOf(v any) string {\n\telemTyp := indirectType(reflect.ValueOf(v).Type())\n\n\ttypName := elemTyp.Name()\n\tpkgPath := elemTyp.PkgPath()\n\tfullname := pkgPath[strings.LastIndexByte(pkgPath, '/')+1:] + \".\" + typName\n\n\treturn fullname\n}\n\nfunc newControllerActivator(app *Application, controller any) *ControllerActivator {\n\tif controller == nil {\n\t\treturn nil\n\t}\n\n\tif c, ok := controller.(*ControllerActivator); ok {\n\t\treturn c\n\t}\n\n\ttyp := reflect.TypeOf(controller)\n\tc := &ControllerActivator{\n\t\t// give access to the Router to the end-devs if they need it for some reason,\n\t\t// i.e register done handlers.\n\t\tapp:   app,\n\t\tValue: reflect.ValueOf(controller),\n\t\tType:  typ,\n\t\t// the full name of the controller: its type including the package path.\n\t\tfullName: NameOf(controller),\n\t\t// set some methods that end-dev cann't use accidentally\n\t\t// to register a route via the `Handle`,\n\t\t// all available exported and compatible methods\n\t\t// are being appended to the slice at the `parseMethods`,\n\t\t// if a new method is registered via `Handle` its function name\n\t\t// is also appended to that slice.\n\t\troutes: whatReservedMethods(typ),\n\t}\n\n\tif IgnoreEmbeddedControllers {\n\t\tc.SkipEmbeddedMethods()\n\t}\n\n\treturn c\n}\n\n// It's a dynamic method, can be exist or not, it can accept input arguments\n// and can write through output values like any other dev-designed method.\n// See 'parseHTTPErrorMethod'.\n// Example at: _examples/mvc/error-handler-http\nconst handleHTTPErrorMethodName = \"HandleHTTPError\"\n\nfunc whatReservedMethods(typ reflect.Type) map[string][]*router.Route {\n\tmethods := []string{\"BeforeActivation\", \"AfterActivation\", handleHTTPErrorMethodName}\n\t//  BeforeActivatior/AfterActivation are not routes but they are\n\t// reserved names*\n\tif isBaseController(typ) {\n\t\tmethods = append(methods, \"BeginRequest\", \"EndRequest\")\n\t}\n\n\troutes := make(map[string][]*router.Route, len(methods))\n\tfor _, m := range methods {\n\t\troutes[m] = []*router.Route{}\n\t}\n\n\treturn routes\n}\n\nfunc whatEmbeddedMethods(typ reflect.Type) []string {\n\tvar embeddedMethodsToIgnore []string\n\tcontrollerType := typ\n\tif controllerType.Kind() == reflect.Ptr {\n\t\tcontrollerType = controllerType.Elem()\n\t}\n\n\tfor i := 0; i < controllerType.NumField(); i++ {\n\t\tstructField := controllerType.Field(i)\n\t\tstructType := structField.Type\n\n\t\tif !structField.Anonymous {\n\t\t\tcontinue\n\t\t}\n\n\t\t// var structValuePtr reflect.Value\n\n\t\tif structType.Kind() == reflect.Ptr {\n\t\t\t// keep both ptr and value instances of the struct so we can ignore all of its methods.\n\t\t\tstructType = structType.Elem()\n\t\t\t// structValuePtr = reflect.ValueOf(reflect.ValueOf(controller).Field(i))\n\t\t}\n\n\t\tif structType.Kind() != reflect.Struct {\n\t\t\tcontinue\n\t\t}\n\n\t\tnewEmbeddedStructType := reflect.New(structField.Type).Type()\n\t\t// let's take its methods and add to methods to ignore from the parent, the controller itself.\n\t\tfor j := 0; j < newEmbeddedStructType.NumMethod(); j++ {\n\t\t\tembeddedMethod := newEmbeddedStructType.Method(j)\n\t\t\tembeddedMethodName := embeddedMethod.Name\n\t\t\t// An exception should happen if the controller itself overrides the embedded method,\n\t\t\t// but Go (1.20) so far doesn't support this detection, as it handles the structure as one.\n\t\t\t/*\n\t\t\t\tshouldKeepBecauseParentOverrides := false\n\n\t\t\t\tif v, existsOnParent := typ.MethodByName(embeddedMethodName); existsOnParent {\n\n\t\t\t\t\tembeddedIndex := newEmbeddedStructType.Method(j).Index\n\t\t\t\t\tcontrollerMethodIndex := v.Index\n\n\t\t\t\t\tif v.Type.In(0) == typ && embeddedIndex == controllerMethodIndex {\n\t\t\t\t\t\tfmt.Printf(\"%s exists on parent = true, receiver = %s\\n\", v.Name, typ.String())\n\t\t\t\t\t\tshouldKeepBecauseParentOverrides = true\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t*/\n\n\t\t\tembeddedMethodsToIgnore = append(embeddedMethodsToIgnore, embeddedMethodName)\n\t\t}\n\t}\n\n\treturn embeddedMethodsToIgnore\n}\n\n// Name returns the full name of the controller, its package name + the type name.\n// Can used at both `BeforeActivation` and `AfterActivation`.\nfunc (c *ControllerActivator) Name() string {\n\treturn c.fullName\n}\n\n// RelName returns the path relatively to the main package.\nfunc (c *ControllerActivator) RelName() string {\n\treturn strings.TrimPrefix(c.fullName, \"main.\")\n}\n\n// SkipMethods can be used to individually skip one or more controller's method handlers.\nfunc (c *ControllerActivator) SkipMethods(methodNames ...string) {\n\tc.skipMethodNames = append(c.skipMethodNames, methodNames...)\n}\n\n// SkipEmbeddedMethods should be ran before controller parsing.\n// It skips all embedded struct's methods conversation to http handlers.\n//\n// Note that even if the controller overrides the embedded methods\n// they will be still ignored because Go doesn't support this detection so far.\n//\n// See https://github.com/kataras/iris/issues/2103 for more.\nfunc (c *ControllerActivator) SkipEmbeddedMethods() {\n\tmethodsToIgnore := whatEmbeddedMethods(c.Type)\n\tc.SkipMethods(methodsToIgnore...)\n}\n\n// Router is the standard Iris router's public API.\n// With this you can register middleware, view layouts, subdomains, serve static files\n// and even add custom standard iris handlers as normally.\n//\n// This Router is the router instance that came from the parent MVC Application,\n// it's the `app.Party(...)` argument.\n//\n// Can used at both `BeforeActivation` and `AfterActivation`.\nfunc (c *ControllerActivator) Router() router.Party {\n\treturn c.app.Router\n}\n\n// GetRoute returns the first registered route based on the controller's method name.\n// It can be used to change the route's name, which is useful for reverse routing\n// inside views. Custom routes can be registered with `Handle`, which returns the *Route.\n// This method exists mostly for the automatic method parsing based on the known patterns\n// inside a controller.\n//\n// A check for `nil` is necessary for unregistered methods.\n//\n// See `GetRoutes` and `Handle` too.\nfunc (c *ControllerActivator) GetRoute(methodName string) *router.Route {\n\troutes := c.GetRoutes(methodName)\n\tif len(routes) > 0 {\n\t\treturn routes[0]\n\t}\n\n\treturn nil\n}\n\n// GetRoutes returns one or more registered route based on the controller's method name.\n// It can be used to change the route's name, which is useful for reverse routing\n// inside views. Custom routes can be registered with `Handle`, which returns the *Route.\n// This method exists mostly for the automatic method parsing based on the known patterns\n// inside a controller.\n//\n// A check for `nil` is necessary for unregistered methods.\n//\n// See `Handle` too.\nfunc (c *ControllerActivator) GetRoutes(methodName string) []*router.Route {\n\tfor name, routes := range c.routes {\n\t\tif name == methodName {\n\t\t\treturn routes\n\t\t}\n\t}\n\treturn nil\n}\n\n// Use registers a middleware for this Controller.\n// It appends one or more handlers to the `BeginHandlers`.\n// It's like the `Party.Use` but specifically\n// for the routes that this controller will register to the targeted `Party`.\nfunc (c *ControllerActivator) Use(handlers ...context.Handler) *ControllerActivator {\n\tc.BeginHandlers = append(c.BeginHandlers, handlers...)\n\treturn c\n}\n\n// Singleton returns new if all incoming clients' requests\n// have the same controller instance.\n// This is done automatically by iris to reduce the creation\n// of a new controller on each request, if the controller doesn't contain\n// any unexported fields and all fields are services-like, static.\nfunc (c *ControllerActivator) Singleton() bool {\n\tif c.injector == nil {\n\t\tpanic(\"MVC: Singleton called from wrong state the API gives access to it only `AfterActivation`, report this as bug\")\n\t}\n\treturn c.injector.Singleton\n}\n\n// DependenciesReadOnly returns a list of dependencies, including the controller's one.\nfunc (c *ControllerActivator) DependenciesReadOnly() []*hero.Dependency {\n\tif c.injector == nil {\n\t\tpanic(\"MVC: DependenciesReadOnly called from wrong state the API gives access to it only `AfterActivation`, report this as bug\")\n\t}\n\n\treturn c.injector.Container.Dependencies\n}\n\n// Dependencies returns a value which can manage the controller's dependencies.\nfunc (c *ControllerActivator) Dependencies() *hero.Container {\n\treturn c.app.container // although the controller's one are: c.injector.Container\n}\n\n// checks if a method is already registered.\nfunc (c *ControllerActivator) isReservedMethod(name string) bool {\n\tfor methodName := range c.routes {\n\t\tif methodName == name {\n\t\t\treturn true\n\t\t}\n\t}\n\n\tfor _, methodName := range c.skipMethodNames {\n\t\tif methodName == name {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\nfunc (c *ControllerActivator) isReservedMethodHandler(method, path string) bool {\n\tfor _, routes := range c.routes {\n\t\tfor _, r := range routes {\n\t\t\tif r.Method == method && r.Path == path {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t}\n\n\treturn false\n}\n\nfunc (c *ControllerActivator) markAsWebsocket() {\n\tc.servesWebsocket = true\n\tc.attachInjector()\n}\n\nfunc (c *ControllerActivator) attachInjector() {\n\tif c.injector == nil {\n\t\tpartyCountParams := macro.CountParams(c.app.Router.GetRelPath(), *c.app.Router.Macros())\n\t\tc.injector = c.app.container.Struct(c.Value, partyCountParams)\n\t}\n}\n\n// Activated can be called to skip the internal method parsing.\nfunc (c *ControllerActivator) Activated() bool {\n\tb := c.activated\n\tc.activated = true\n\treturn b\n}\n\nfunc (c *ControllerActivator) activate() {\n\tif c.Activated() {\n\t\treturn\n\t}\n\n\tc.parseMethods()\n\tc.parseHTTPErrorHandler()\n}\n\nfunc (c *ControllerActivator) parseHTTPErrorHandler() {\n\tif m, ok := c.Type.MethodByName(handleHTTPErrorMethodName); ok {\n\t\tc.handleHTTPError(m.Name)\n\t}\n}\n\n// register all available, exported methods to handlers if possible.\nfunc (c *ControllerActivator) parseMethods() {\n\tn := c.Type.NumMethod()\n\tfor i := 0; i < n; i++ {\n\t\tm := c.Type.Method(i)\n\t\tc.parseMethod(m)\n\t}\n}\n\nfunc (c *ControllerActivator) parseMethod(m reflect.Method) {\n\thttpMethod, httpPath, err := parseMethod(c.app.Router.Macros(), m, c.isReservedMethod, c.app.customPathWordFunc)\n\tif err != nil {\n\t\tif err != errSkip {\n\t\t\tc.logErrorf(\"MVC: fail to parse the route path and HTTP method for '%s.%s': %v\", c.fullName, m.Name, err)\n\t\t}\n\n\t\treturn\n\t}\n\n\tc.Handle(httpMethod, httpPath, m.Name)\n}\n\nfunc (c *ControllerActivator) logErrorf(format string, args ...any) {\n\tc.Router().Logger().Errorf(format, args...)\n}\n\n// Handle registers a route based on a http method, the route's path\n// and a function name that belongs to the controller, it accepts\n// a forth, optionally, variadic parameter which is the before handlers.\n//\n// Just like `Party#Handle`, it returns the `*router.Route`, if failed\n// then it logs the errors and it returns nil, you can check the errors\n// programmatically by the `Party#GetReporter`.\n//\n// Handle will add a route to the \"funcName\".\nfunc (c *ControllerActivator) Handle(method, path, funcName string, middleware ...context.Handler) *router.Route {\n\troutes := c.handleMany(method, path, funcName, false, middleware...)\n\tif len(routes) == 0 {\n\t\treturn nil\n\t}\n\n\treturn routes[0]\n}\n\n// handleHTTPError is called when a controller's method\n// with the \"HandleHTTPError\" is found. That method\n// can accept dependencies like the rest but if it's not called manually\n// then any dynamic dependencies depending on successful requests\n// may fail - this is end-developer's job;\n// to register the correct dependencies or not do it all on that method.\n//\n// Note that if more than one controller in the same Party\n// tries to register an http error handler then the\n// overlap route rule should be used and a dependency\n// on the controller (or method) level that will select\n// between the two should exist (see mvc/authenticated-controller example).\nfunc (c *ControllerActivator) handleHTTPError(funcName string) *router.Route {\n\thandler := c.handlerOf(\"/\", funcName)\n\n\troutes := c.app.Router.OnAnyErrorCode(handler)\n\tif len(routes) == 0 {\n\t\tc.logErrorf(\"MVC: unable to register an HTTP error code handler for '%s.%s'\", c.fullName, funcName)\n\t\treturn nil\n\t}\n\n\tc.saveRoutes(funcName, routes, true)\n\treturn routes[0]\n}\n\n// HandleMany like `Handle` but can register more than one path and HTTP method routes\n// separated by whitespace on the same controller's method.\n// Keep note that if the controller's method input arguments are path parameters dependencies\n// they should match with each of the given paths.\n//\n// Just like `Party#HandleMany`:, it returns the `[]*router.Routes`.\n// Usage:\n//\n//\tfunc (*Controller) BeforeActivation(b mvc.BeforeActivation) {\n//\t\tb.HandleMany(\"GET\", \"/path /path1\" /path2\", \"HandlePath\")\n//\t}\n//\n// HandleMany will override any routes of this \"funcName\".\nfunc (c *ControllerActivator) HandleMany(method, path, funcName string, middleware ...context.Handler) []*router.Route {\n\treturn c.handleMany(method, path, funcName, true, middleware...)\n}\n\nfunc (c *ControllerActivator) handleMany(method, path, funcName string, override bool, middleware ...context.Handler) []*router.Route {\n\tif method == \"\" || path == \"\" || funcName == \"\" ||\n\t\t(c.isReservedMethod(funcName) && c.isReservedMethodHandler(method, path)) {\n\t\t// isReservedMethod -> if it's already registered\n\t\t// by a previous Handle or analyze methods internally.\n\t\treturn nil\n\t}\n\n\thandler := c.handlerOf(path, funcName)\n\tmiddleware = context.JoinHandlers(c.BeginHandlers, middleware)\n\n\t// register the handler now.\n\troutes := c.app.Router.HandleMany(method, path, append(middleware, handler)...)\n\tif routes == nil {\n\t\tc.logErrorf(\"MVC: unable to register a route for the path for '%s.%s'\", c.fullName, funcName)\n\t\treturn nil\n\t}\n\n\tc.saveRoutes(funcName, routes, override)\n\treturn routes\n}\n\nfunc (c *ControllerActivator) saveRoutes(funcName string, routes []*router.Route, override bool) {\n\tm, ok := c.Type.MethodByName(funcName)\n\tif !ok {\n\t\treturn\n\t}\n\n\tsourceFileName, sourceLineNumber := getSourceFileLine(c.Type, m)\n\n\trelName := c.RelName()\n\tfor _, r := range routes {\n\t\tr.Description = relName\n\t\tr.MainHandlerName = fmt.Sprintf(\"%s.%s\", relName, funcName)\n\n\t\tr.SourceFileName, r.SourceLineNumber = sourceFileName, sourceLineNumber\n\t}\n\n\t// add this as a reserved method name in order to\n\t// be sure that the same route\n\t// (method is allowed to be registered more than one on different routes - v11.2).\n\texistingRoutes, exist := c.routes[funcName]\n\tif override || !exist {\n\t\tc.routes[funcName] = routes\n\t} else {\n\t\tc.routes[funcName] = append(existingRoutes, routes...)\n\t}\n}\n\nfunc (c *ControllerActivator) handlerOf(relPath, methodName string) context.Handler {\n\tc.attachInjector()\n\n\tfullpath := c.app.Router.GetRelPath() + relPath\n\tparamsCount := macro.CountParams(fullpath, *c.app.Router.Macros())\n\thandler := c.injector.MethodHandler(methodName, paramsCount)\n\n\tif isBaseController(c.Type) {\n\t\treturn func(ctx *context.Context) {\n\t\t\tctrl, err := c.injector.Acquire(ctx)\n\t\t\tif err != nil {\n\t\t\t\t// if err != hero.ErrStopExecution {\n\t\t\t\t// \tc.injector.Container.GetErrorHandler(ctx).HandleError(ctx, err)\n\t\t\t\t// }\n\t\t\t\tc.injector.Container.GetErrorHandler(ctx).HandleError(ctx, err)\n\t\t\t\t// allow skipping struct field bindings\n\t\t\t\t// errors by a custom error handler.\n\t\t\t\tif ctx.IsStopped() {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tb := ctrl.Interface().(BaseController)\n\t\t\t// init the request.\n\t\t\tb.BeginRequest(ctx)\n\n\t\t\t// if begin request stopped the execution.\n\t\t\tif ctx.IsStopped() {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\thandler(ctx)\n\n\t\t\tb.EndRequest(ctx)\n\t\t}\n\t}\n\n\treturn handler\n}\n"
  },
  {
    "path": "mvc/controller_handle_test.go",
    "content": "package mvc_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/httptest\"\n\n\t. \"github.com/kataras/iris/v12/mvc\"\n)\n\n// service\ntype (\n\t// these testService and testServiceImpl could be in lowercase, unexported\n\t// but the `Say` method should be exported however we have those exported\n\t// because of the controller handler test.\n\ttestService interface {\n\t\tSay(string) string\n\t}\n\ttestServiceImpl struct {\n\t\tprefix string\n\t}\n)\n\nfunc (s *testServiceImpl) Say(message string) string {\n\treturn s.prefix + \" \" + message\n}\n\ntype testControllerHandle struct {\n\tCtx     *context.Context\n\tService testService\n\n\treqField string\n}\n\nfunc (c *testControllerHandle) BeforeActivation(b BeforeActivation) {\n\tb.Handle(\"GET\", \"/histatic\", \"HiStatic\")\n\tb.Handle(\"GET\", \"/hiservice\", \"HiService\")\n\tb.Handle(\"GET\", \"/hiservice/{ps:string}\", \"HiServiceBy\")\n\tb.Handle(\"GET\", \"/hiparam/{ps:string}\", \"HiParamBy\")\n\tb.Handle(\"GET\", \"/hiparamempyinput/{ps:string}\", \"HiParamEmptyInputBy\")\n\tb.HandleMany(\"GET\", \"/custom/{ps:string} /custom2/{ps:string}\", \"CustomWithParameter\")\n\tb.HandleMany(\"GET\", \"/custom3/{ps:string}/{pssecond:string}\", \"CustomWithParameters\")\n}\n\n// test `GetRoute` for custom routes.\nfunc (c *testControllerHandle) AfterActivation(a AfterActivation) {\n\t// change automatic parser's route change name.\n\trget := a.GetRoute(\"Get\")\n\tif rget == nil {\n\t\tpanic(\"route from function name: 'Get' doesn't exist on `AfterActivation`\")\n\t}\n\trget.Name = \"index_route\"\n\n\t// change a custom route's name.\n\tr := a.GetRoute(\"HiStatic\")\n\tif r == nil {\n\t\tpanic(\"route from function name: HiStatic doesn't exist on `AfterActivation`\")\n\t}\n\t// change the name here, and test if name changed in the handler.\n\tr.Name = \"hi_static_route\"\n}\n\nfunc (c *testControllerHandle) BeginRequest(ctx iris.Context) {\n\tc.reqField = ctx.URLParam(\"reqfield\")\n}\n\nfunc (c *testControllerHandle) EndRequest(ctx iris.Context) {}\n\nfunc (c *testControllerHandle) Get() string {\n\tif c.Ctx.GetCurrentRoute().Name() != \"index_route\" {\n\t\treturn \"Get's route's name didn't change on AfterActivation\"\n\t}\n\treturn \"index\"\n}\n\nfunc (c *testControllerHandle) HiStatic() string {\n\tif c.Ctx.GetCurrentRoute().Name() != \"hi_static_route\" {\n\t\treturn \"HiStatic's route's name didn't change on AfterActivation\"\n\t}\n\n\treturn c.reqField\n}\n\nfunc (c *testControllerHandle) HiService() string {\n\treturn c.Service.Say(\"hi\")\n}\n\nfunc (c *testControllerHandle) HiServiceBy(v string) string {\n\treturn c.Service.Say(\"hi with param: \" + v)\n}\n\nfunc (c *testControllerHandle) HiParamBy(v string) string {\n\treturn v\n}\n\nfunc (c *testControllerHandle) HiParamEmptyInputBy() string {\n\treturn \"empty in but served with ctx.Params.Get('ps')=\" + c.Ctx.Params().Get(\"ps\")\n}\n\nfunc (c *testControllerHandle) CustomWithParameter(param1 string) string {\n\treturn param1\n}\n\nfunc (c *testControllerHandle) CustomWithParameters(param1, param2 string) string {\n\treturn param1 + param2\n}\n\ntype testSmallController struct{}\n\n// test ctx + id in the same time.\nfunc (c *testSmallController) GetHiParamEmptyInputWithCtxBy(ctx *context.Context, id string) string {\n\treturn \"empty in but served with ctx.Params.Get('param2')= \" + ctx.Params().Get(\"param2\") + \" == id == \" + id\n}\n\nfunc TestControllerHandle(t *testing.T) {\n\tapp := iris.New()\n\tm := New(app)\n\tm.Register(&testServiceImpl{prefix: \"service:\"})\n\tm.Handle(new(testControllerHandle))\n\tm.Handle(new(testSmallController))\n\n\te := httptest.New(t, app)\n\n\t// test the index, is not part of the current package's implementation but do it.\n\te.GET(\"/\").Expect().Status(httptest.StatusOK).Body().IsEqual(\"index\")\n\n\t// the important things now.\n\n\t// this test ensures that the BeginRequest of the controller will be\n\t// called correctly and also the controller is binded to the first input argument\n\t// (which is the function's receiver, if any, in this case the *testController in go).\n\texpectedReqField := \"this is a request field filled by this url param\"\n\te.GET(\"/histatic\").WithQuery(\"reqfield\", expectedReqField).Expect().Status(httptest.StatusOK).\n\t\tBody().IsEqual(expectedReqField)\n\t// this test makes sure that the binded values of the controller is handled correctly\n\t// and can be used in a user-defined, dynamic \"mvc handler\".\n\te.GET(\"/hiservice\").Expect().Status(httptest.StatusOK).\n\t\tBody().IsEqual(\"service: hi\")\n\te.GET(\"/hiservice/value\").Expect().Status(httptest.StatusOK).\n\t\tBody().IsEqual(\"service: hi with param: value\")\n\t// this worked with a temporary variadic on the resolvemethodfunc which is not\n\t// correct design, I should split the path and params with the rest of implementation\n\t// in order a simple template.Src can be given.\n\te.GET(\"/hiparam/value\").Expect().Status(httptest.StatusOK).\n\t\tBody().IsEqual(\"value\")\n\te.GET(\"/hiparamempyinput/value\").Expect().Status(httptest.StatusOK).\n\t\tBody().IsEqual(\"empty in but served with ctx.Params.Get('ps')=value\")\n\te.GET(\"/custom/value1\").Expect().Status(httptest.StatusOK).\n\t\tBody().IsEqual(\"value1\")\n\te.GET(\"/custom2/value2\").Expect().Status(httptest.StatusOK).\n\t\tBody().IsEqual(\"value2\")\n\te.GET(\"/custom3/value1/value2\").Expect().Status(httptest.StatusOK).\n\t\tBody().IsEqual(\"value1value2\")\n\te.GET(\"/custom3/value1\").Expect().Status(httptest.StatusNotFound)\n\n\te.GET(\"/hi/param/empty/input/with/ctx/value\").Expect().Status(httptest.StatusOK).\n\t\tBody().IsEqual(\"empty in but served with ctx.Params.Get('param2')= value == id == value\")\n}\n\ntype testControllerHandleWithDynamicPathPrefix struct {\n\tCtx iris.Context\n}\n\nfunc (c *testControllerHandleWithDynamicPathPrefix) GetBy(id string) string {\n\tparams := c.Ctx.Params()\n\treturn params.Get(\"model\") + params.Get(\"action\") + id\n}\n\nfunc TestControllerHandleWithDynamicPathPrefix(t *testing.T) {\n\tapp := iris.New()\n\tNew(app.Party(\"/api/data/{model:string}/{action:string}\")).Handle(new(testControllerHandleWithDynamicPathPrefix))\n\te := httptest.New(t, app)\n\te.GET(\"/api/data/mymodel/myaction/myid\").Expect().Status(httptest.StatusOK).\n\t\tBody().IsEqual(\"mymodelmyactionmyid\")\n}\n\ntype testControllerGetBy struct{}\n\nfunc (c *testControllerGetBy) GetBy(age int64) *testCustomStruct {\n\treturn &testCustomStruct{\n\t\tAge:  int(age),\n\t\tName: \"name\",\n\t}\n}\n\nfunc TestControllerGetByWithAllowMethods(t *testing.T) {\n\tapp := iris.New()\n\tapp.Configure(iris.WithFireMethodNotAllowed)\n\t// ^ this 405 status will not be fired on POST: project/... because of\n\t// .AllowMethods, but it will on PUT.\n\n\tNew(app.Party(\"/project\").AllowMethods(iris.MethodGet, iris.MethodPost)).Handle(new(testControllerGetBy))\n\n\te := httptest.New(t, app)\n\te.GET(\"/project/42\").Expect().Status(httptest.StatusOK).\n\t\tJSON().IsEqual(&testCustomStruct{Age: 42, Name: \"name\"})\n\te.POST(\"/project/42\").Expect().Status(httptest.StatusOK)\n\te.PUT(\"/project/42\").Expect().Status(httptest.StatusMethodNotAllowed)\n}\n"
  },
  {
    "path": "mvc/controller_method_parser.go",
    "content": "package mvc\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"strconv\"\n\t\"strings\"\n\t\"unicode\"\n\n\t\"github.com/kataras/iris/v12/core/router\"\n\t\"github.com/kataras/iris/v12/macro\"\n\n\t\"golang.org/x/text/cases\"\n\t\"golang.org/x/text/language\"\n)\n\nconst (\n\ttokenBy       = \"By\"\n\ttokenWildcard = \"Wildcard\" // \"ByWildcard\".\n)\n\n// word lexer, not characters.\ntype methodLexer struct {\n\twords []string\n\tcur   int\n}\n\nfunc newMethodLexer(s string) *methodLexer {\n\tl := new(methodLexer)\n\tl.reset(s)\n\treturn l\n}\n\n/*\nvar allowedCapitalWords = map[string]struct{}{\n\t\"ID\":   {},\n\t\"JSON\": {},\n}\n*/\n\nfunc (l *methodLexer) reset(s string) {\n\tl.cur = -1\n\tvar words []string\n\tif s != \"\" {\n\t\tend := len(s)\n\t\tstart := -1\n\n\t\t// outter:\n\t\tfor i, n := 0, end; i < n; i++ {\n\t\t\tc := rune(s[i])\n\t\t\tif unicode.IsUpper(c) {\n\t\t\t\t// it doesn't count the last uppercase\n\t\t\t\tif start != -1 {\n\t\t\t\t\t/*\n\t\t\t\t\t\tfor allowedCapitalWord := range allowedCapitalWords {\n\t\t\t\t\t\t\tcapitalWordEnd := i + len(allowedCapitalWord) // takes last char too, e.g. ReadJSON, we need the JSON.\n\t\t\t\t\t\t\tif len(s) >= capitalWordEnd {\n\t\t\t\t\t\t\t\tword := s[i:capitalWordEnd]\n\t\t\t\t\t\t\t\tif word == allowedCapitalWord {\n\t\t\t\t\t\t\t\t\twords = append(words, word)\n\t\t\t\t\t\t\t\t\ti = capitalWordEnd\n\t\t\t\t\t\t\t\t\tstart = i\n\t\t\t\t\t\t\t\t\tcontinue outter\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t*/\n\n\t\t\t\t\tend = i\n\t\t\t\t\twords = append(words, s[start:end])\n\t\t\t\t}\n\n\t\t\t\tstart = i\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tend = i + 1\n\t\t}\n\n\t\tif end > 0 && len(s) >= end {\n\t\t\twords = append(words, s[start:])\n\t\t}\n\t}\n\n\tl.words = words\n}\n\nfunc (l *methodLexer) next() (w string) {\n\tcur := l.cur + 1\n\n\tif w = l.peek(cur); w != \"\" {\n\t\tl.cur++\n\t}\n\n\treturn\n}\n\nfunc (l *methodLexer) skip() {\n\tif cur := l.cur + 1; cur < len(l.words) {\n\t\tl.cur = cur\n\t} else {\n\t\tl.cur = len(l.words) - 1\n\t}\n}\n\nfunc (l *methodLexer) peek(idx int) string {\n\tif idx < len(l.words) {\n\t\treturn l.words[idx]\n\t}\n\treturn \"\"\n}\n\nfunc (l *methodLexer) peekNext() (w string) {\n\treturn l.peek(l.cur + 1)\n}\n\nfunc genParamKey(argIdx int) string {\n\treturn \"param\" + strconv.Itoa(argIdx) // param0, param1, param2...\n}\n\ntype methodParser struct {\n\tlexer              *methodLexer\n\tfn                 reflect.Method\n\tmacros             *macro.Macros\n\tcustomPathWordFunc CustomPathWordFunc\n}\n\nfunc parseMethod(macros *macro.Macros, fn reflect.Method, skipper func(string) bool, wordFunc CustomPathWordFunc) (method, path string, err error) {\n\tif skipper(fn.Name) {\n\t\treturn \"\", \"\", errSkip\n\t}\n\n\tp := &methodParser{\n\t\tfn:                 fn,\n\t\tlexer:              newMethodLexer(fn.Name),\n\t\tmacros:             macros,\n\t\tcustomPathWordFunc: wordFunc,\n\t}\n\treturn p.parse()\n}\n\nfunc methodTitle(httpMethod string) string {\n\tcaser := cases.Title(language.English)\n\treturn caser.String(strings.ToLower(httpMethod))\n}\n\nvar errSkip = errors.New(\"skip\")\n\nvar allMethods = append(router.AllMethods[0:], []string{\"ALL\", \"ANY\"}...)\n\n// CustomPathWordFunc describes the function which can be passed\n// through `Application.SetCustomPathWordFunc` to customize\n// the controllers method parsing.\ntype CustomPathWordFunc func(path, w string, wordIndex int) string\n\nfunc addPathWord(path, w string) string {\n\tif path[len(path)-1] != '/' {\n\t\tpath += \"/\"\n\t}\n\tpath += strings.ToLower(w)\n\treturn path\n}\n\nfunc (p *methodParser) parse() (method, path string, err error) {\n\tfuncArgPos := 0\n\tpath = \"/\"\n\t// take the first word and check for the method.\n\tw := p.lexer.next()\n\n\tfor _, httpMethod := range allMethods {\n\t\tpossibleMethodFuncName := methodTitle(httpMethod)\n\t\tif strings.Index(w, possibleMethodFuncName) == 0 {\n\t\t\tmethod = httpMethod\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif method == \"\" {\n\t\t// this is not a valid method to parse, we just skip it,\n\t\t//  it may be used for end-dev's use cases.\n\t\treturn \"\", \"\", errSkip\n\t}\n\n\twordIndex := 0\n\tfor {\n\t\tw := p.lexer.next()\n\t\tif w == \"\" {\n\t\t\tbreak\n\t\t}\n\n\t\tif w == tokenBy {\n\t\t\tfuncArgPos++ // starting with 1 because in typ.NumIn() the first is the struct receiver.\n\n\t\t\t// No need for these:\n\t\t\t// ByBy will act like /{param:type}/{param:type} as users expected\n\t\t\t// if func input arguments are there, else act By like normal path /by.\n\t\t\t//\n\t\t\t// if p.lexer.peekPrev() == tokenBy || typ.NumIn() == 1 { // ByBy, then act this second By like a path\n\t\t\t// \ta.relPath += \"/\" + strings.ToLower(w)\n\t\t\t// \tcontinue\n\t\t\t// }\n\n\t\t\tif path, funcArgPos, err = p.parsePathParam(path, w, funcArgPos); err != nil {\n\t\t\t\treturn \"\", \"\", err\n\t\t\t}\n\n\t\t\tcontinue\n\t\t}\n\n\t\t// custom static path.\n\t\tif p.customPathWordFunc != nil {\n\t\t\tpath = p.customPathWordFunc(path, w, wordIndex)\n\t\t} else {\n\t\t\t// default static path.\n\t\t\tpath = addPathWord(path, w)\n\t\t}\n\t\twordIndex++\n\t}\n\treturn\n}\n\nfunc (p *methodParser) parsePathParam(path string, w string, funcArgPos int) (string, int, error) {\n\ttyp := p.fn.Type\n\n\tif typ.NumIn() <= funcArgPos {\n\n\t\t// By found but input arguments are not there, so act like /by path without restricts.\n\t\tpath = addPathWord(path, w)\n\t\treturn path, funcArgPos, nil\n\t}\n\n\tvar (\n\t\tparamKey  = genParamKey(funcArgPos) // argfirst, argsecond...\n\t\tm         = p.macros.GetMaster()    // default (String by-default)\n\t\ttrailings = p.macros.GetTrailings()\n\t)\n\n\t// string, int...\n\tgoType := typ.In(funcArgPos).Kind()\n\tnextWord := p.lexer.peekNext()\n\n\tif nextWord == tokenWildcard {\n\t\tp.lexer.skip() // skip the Wildcard word.\n\t\tif len(trailings) == 0 {\n\t\t\treturn \"\", 0, errors.New(\"no trailing path parameter found\")\n\t\t}\n\t\tm = trailings[0]\n\t} else {\n\t\t// validMacros := p.macros.LookupForGoType(goType)\n\n\t\t// instead of mapping with a reflect.Kind which has its limitation,\n\t\t// we map the param types with a go type as a string,\n\t\t// so custom structs such as \"user\" can be mapped to a macro with indent || alias == \"user\".\n\t\tm = p.macros.Get(strings.ToLower(goType.String()))\n\n\t\tif m == nil {\n\t\t\tif typ.NumIn() > funcArgPos {\n\t\t\t\t// has more input arguments but we are not in the correct\n\t\t\t\t// index now, maybe the first argument was an `iris/context.Context`\n\t\t\t\t// so retry with the \"funcArgPos\" incremented.\n\t\t\t\t//\n\t\t\t\t// the \"funcArgPos\" will be updated to the caller as well\n\t\t\t\t// because we return it among the path and the error.\n\t\t\t\treturn p.parsePathParam(path, w, funcArgPos+1)\n\t\t\t}\n\n\t\t\treturn \"\", 0, fmt.Errorf(\"invalid syntax: the standard go type: %s found in controller's function: %s at position: %d does not match any valid macro\", goType, p.fn.Name, funcArgPos)\n\t\t}\n\t}\n\n\t// /{argfirst:path}, /{argfirst:int64}...\n\tif path[len(path)-1] != '/' {\n\t\tpath += \"/\"\n\t}\n\tpath += fmt.Sprintf(\"{%s:%s}\", paramKey, m.Indent())\n\n\tif nextWord == \"\" && typ.NumIn() > funcArgPos+1 {\n\t\t// By is the latest word but func is expected\n\t\t// more path parameters values, i.e:\n\t\t// GetBy(name string, age int)\n\t\t// The caller (parse) doesn't need to know\n\t\t// about the incremental funcArgPos because\n\t\t// it will not need it.\n\t\treturn p.parsePathParam(path, nextWord, funcArgPos+1)\n\t}\n\n\treturn path, funcArgPos, nil\n}\n"
  },
  {
    "path": "mvc/controller_method_result_test.go",
    "content": "package mvc_test\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/httptest\"\n\n\t. \"github.com/kataras/iris/v12/mvc\"\n)\n\ntype testControllerMethodResult struct {\n\tCtx *context.Context\n}\n\nfunc (c *testControllerMethodResult) Get() Result {\n\treturn Response{\n\t\tText: \"Hello World!\",\n\t}\n}\n\nfunc (c *testControllerMethodResult) GetWithStatus() Response { // or Result again, no problem.\n\treturn Response{\n\t\tText: \"This page doesn't exist\",\n\t\tCode: iris.StatusNotFound,\n\t}\n}\n\ntype testCustomStruct struct {\n\tName string `json:\"name\" xml:\"name\"`\n\tAge  int    `json:\"age\" xml:\"age\"`\n}\n\nfunc (c *testControllerMethodResult) GetJson() Result {\n\tvar err error\n\tif c.Ctx.URLParamExists(\"err\") {\n\t\terr = errors.New(\"error here\")\n\t}\n\treturn Response{\n\t\tErr:    err, // if err != nil then it will fire the error's text with a BadRequest.\n\t\tObject: testCustomStruct{Name: \"Iris\", Age: 2},\n\t}\n}\n\nvar things = []string{\"thing 0\", \"thing 1\", \"thing 2\"}\n\nfunc (c *testControllerMethodResult) GetThingWithTryBy(index int) Result {\n\tfailure := Response{\n\t\tText: \"thing does not exist\",\n\t\tCode: iris.StatusNotFound,\n\t}\n\n\treturn Try(func() Result {\n\t\t// if panic because of index exceed the slice\n\t\t// then the \"failure\" response will be returned instead.\n\t\treturn Response{Text: things[index]}\n\t}, failure)\n}\n\nfunc (c *testControllerMethodResult) GetThingWithTryDefaultBy(index int) Result {\n\treturn Try(func() Result {\n\t\t// if panic because of index exceed the slice\n\t\t// then the default failure response will be returned instead (400 bad request).\n\t\treturn Response{Text: things[index]}\n\t})\n}\n\nfunc TestControllerMethodResult(t *testing.T) {\n\tapp := iris.New()\n\tNew(app).Handle(new(testControllerMethodResult))\n\n\te := httptest.New(t, app)\n\n\te.GET(\"/\").Expect().Status(iris.StatusOK).\n\t\tBody().IsEqual(\"Hello World!\")\n\n\te.GET(\"/with/status\").Expect().Status(iris.StatusNotFound).\n\t\tBody().IsEqual(\"This page doesn't exist\")\n\n\te.GET(\"/json\").Expect().Status(iris.StatusOK).\n\t\tJSON().IsEqual(iris.Map{\n\t\t\"name\": \"Iris\",\n\t\t\"age\":  2,\n\t})\n\n\te.GET(\"/json\").WithQuery(\"err\", true).Expect().\n\t\tStatus(iris.StatusBadRequest).\n\t\tBody().IsEqual(\"error here\")\n\n\te.GET(\"/thing/with/try/1\").Expect().\n\t\tStatus(iris.StatusOK).\n\t\tBody().IsEqual(\"thing 1\")\n\t// failure because of index exceed the slice\n\te.GET(\"/thing/with/try/3\").Expect().\n\t\tStatus(iris.StatusNotFound).\n\t\tBody().IsEqual(\"thing does not exist\")\n\n\te.GET(\"/thing/with/try/default/3\").Expect().\n\t\tStatus(iris.StatusBadRequest).\n\t\tBody().IsEqual(\"Bad Request\")\n}\n\ntype testControllerMethodResultTypes struct {\n\tCtx *context.Context\n}\n\nfunc (c *testControllerMethodResultTypes) GetText() string {\n\treturn \"text\"\n}\n\nfunc (c *testControllerMethodResultTypes) GetStatus() int {\n\treturn iris.StatusBadGateway\n}\n\nfunc (c *testControllerMethodResultTypes) GetTextWithStatusOk() (string, int) {\n\treturn \"OK\", iris.StatusOK\n}\n\n// tests should have output arguments mixed\nfunc (c *testControllerMethodResultTypes) GetStatusWithTextNotOkBy(first string, second string) (int, string) {\n\treturn iris.StatusForbidden, \"NOT_OK_\" + first + second\n}\n\nfunc (c *testControllerMethodResultTypes) GetTextAndContentType() (string, string) {\n\treturn \"<b>text</b>\", \"text/html\"\n}\n\ntype testControllerMethodCustomResult struct {\n\tHTML string\n}\n\n// The only one required function to make that a custom Response dispatcher.\nfunc (r testControllerMethodCustomResult) Dispatch(ctx *context.Context) {\n\tctx.HTML(r.HTML)\n}\n\nfunc (c *testControllerMethodResultTypes) GetCustomResponse() testControllerMethodCustomResult {\n\treturn testControllerMethodCustomResult{\"<b>text</b>\"}\n}\n\nfunc (c *testControllerMethodResultTypes) GetCustomResponseWithStatusOk() (testControllerMethodCustomResult, int) {\n\treturn testControllerMethodCustomResult{\"<b>OK</b>\"}, iris.StatusOK\n}\n\nfunc (c *testControllerMethodResultTypes) GetCustomResponseWithStatusNotOk() (testControllerMethodCustomResult, int) {\n\treturn testControllerMethodCustomResult{\"<b>internal server error</b>\"}, iris.StatusInternalServerError\n}\n\nfunc (c *testControllerMethodResultTypes) GetCustomStruct() testCustomStruct {\n\treturn testCustomStruct{\"Iris\", 2}\n}\n\nfunc (c *testControllerMethodResultTypes) GetCustomStructWithStatusNotOk() (testCustomStruct, int) {\n\treturn testCustomStruct{\"Iris\", 2}, iris.StatusInternalServerError\n}\n\nfunc (c *testControllerMethodResultTypes) GetCustomStructWithContentType() (testCustomStruct, string) {\n\treturn testCustomStruct{\"Iris\", 2}, \"text/xml\"\n}\n\nfunc (c *testControllerMethodResultTypes) GetCustomStructWithError() (s testCustomStruct, err error) {\n\ts = testCustomStruct{\"Iris\", 2}\n\tif c.Ctx.URLParamExists(\"err\") {\n\t\terr = errors.New(\"omit return of testCustomStruct and fire error\")\n\t}\n\n\t// it should send the testCustomStruct as JSON if error is nil\n\t// otherwise it should fire the default error(BadRequest) with the error's text.\n\treturn\n}\n\nfunc TestControllerMethodResultTypes(t *testing.T) {\n\tapp := iris.New()\n\tNew(app).Handle(new(testControllerMethodResultTypes))\n\n\te := httptest.New(t, app)\n\n\te.GET(\"/text\").Expect().Status(iris.StatusOK).\n\t\tBody().IsEqual(\"text\")\n\n\te.GET(\"/status\").Expect().Status(iris.StatusBadGateway)\n\n\te.GET(\"/text/with/status/ok\").Expect().Status(iris.StatusOK).\n\t\tBody().IsEqual(\"OK\")\n\n\te.GET(\"/status/with/text/not/ok/first/second\").Expect().Status(iris.StatusForbidden).\n\t\tBody().IsEqual(\"NOT_OK_firstsecond\")\n\t// Author's note: <-- if that fails means that the last binder called for both input args,\n\t// see path_param_binder.go\n\n\te.GET(\"/text/and/content/type\").Expect().Status(iris.StatusOK).\n\t\tContentType(\"text/html\", \"utf-8\").\n\t\tBody().IsEqual(\"<b>text</b>\")\n\n\te.GET(\"/custom/response\").Expect().Status(iris.StatusOK).\n\t\tContentType(\"text/html\", \"utf-8\").\n\t\tBody().IsEqual(\"<b>text</b>\")\n\te.GET(\"/custom/response/with/status/ok\").Expect().Status(iris.StatusOK).\n\t\tContentType(\"text/html\", \"utf-8\").\n\t\tBody().IsEqual(\"<b>OK</b>\")\n\te.GET(\"/custom/response/with/status/not/ok\").Expect().Status(iris.StatusInternalServerError).\n\t\tContentType(\"text/html\", \"utf-8\").\n\t\tBody().IsEqual(\"<b>internal server error</b>\")\n\n\texpectedResultFromCustomStruct := map[string]any{\n\t\t\"name\": \"Iris\",\n\t\t\"age\":  2,\n\t}\n\te.GET(\"/custom/struct\").Expect().Status(iris.StatusOK).\n\t\tJSON().IsEqual(expectedResultFromCustomStruct)\n\te.GET(\"/custom/struct/with/status/not/ok\").Expect().Status(iris.StatusInternalServerError).\n\t\tJSON().IsEqual(expectedResultFromCustomStruct)\n\te.GET(\"/custom/struct/with/content/type\").Expect().Status(iris.StatusOK).\n\t\tContentType(\"text/xml\", \"utf-8\")\n\te.GET(\"/custom/struct/with/error\").Expect().Status(iris.StatusOK).\n\t\tJSON().IsEqual(expectedResultFromCustomStruct)\n\te.GET(\"/custom/struct/with/error\").WithQuery(\"err\", true).Expect().\n\t\tStatus(iris.StatusBadRequest). // the default status code if error is not nil\n\t\t// the content should be not JSON it should be the status code's text\n\t\t// it will fire the error's text\n\t\tBody().IsEqual(\"omit return of testCustomStruct and fire error\")\n}\n\ntype testControllerViewResultRespectCtxViewData struct {\n\tT *testing.T\n}\n\nfunc (t *testControllerViewResultRespectCtxViewData) BeginRequest(ctx *context.Context) {\n\tctx.ViewData(\"name_begin\", \"iris_begin\")\n}\n\nfunc (t *testControllerViewResultRespectCtxViewData) EndRequest(ctx *context.Context) {\n\t// check if data is not overridden by return View {Data: context.Map...}\n\n\tdataWritten := ctx.GetViewData()\n\tif dataWritten == nil {\n\t\tt.T.Fatalf(\"view data is nil, both BeginRequest and Get failed to write the data\")\n\t\treturn\n\t}\n\n\tif dataWritten[\"name_begin\"] == nil {\n\t\tt.T.Fatalf(`view data[name_begin] is nil,\n\t\t\tBeginRequest's ctx.ViewData call have been overridden  by Get's return View {Data: }.\n\t\t\tTotal view data: %v`, dataWritten)\n\t}\n\n\tif dataWritten[\"name\"] == nil {\n\t\tt.T.Fatalf(\"view data[name] is nil, Get's return View {Data: } didn't work. Total view data: %v\", dataWritten)\n\t}\n}\n\nfunc (t *testControllerViewResultRespectCtxViewData) Get() Result {\n\treturn View{\n\t\tName: \"doesnt_exists.html\",\n\t\tData: context.Map{\"name\": \"iris\"}, // we care about this only.\n\t\tCode: iris.StatusInternalServerError,\n\t}\n}\n\nfunc TestControllerViewResultRespectCtxViewData(t *testing.T) {\n\tapp := iris.New()\n\tm := New(app.Party(\"/\"))\n\tm.Register(t)\n\tm.Handle(new(testControllerViewResultRespectCtxViewData))\n\n\te := httptest.New(t, app)\n\n\te.GET(\"/\").Expect().Status(iris.StatusInternalServerError)\n}\n"
  },
  {
    "path": "mvc/controller_overlap_test.go",
    "content": "// black-box testing\n// Note: there is a test, for end-devs, of Controllers overlapping at _examples/mvc/authenticated-controller too.\npackage mvc_test\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/httptest\"\n\t\"github.com/kataras/iris/v12/mvc\"\n)\n\nfunc TestControllerOverlap(t *testing.T) {\n\tapp := iris.New()\n\tuserRouter := app.Party(\"/user\")\n\t{\n\t\tuserRouter.SetRegisterRule(iris.RouteOverlap)\n\n\t\t// Initialize a new MVC application on top of the \"userRouter\".\n\t\tuserApp := mvc.New(userRouter)\n\t\t// Register Dependencies.\n\t\tuserApp.Register(authDependency)\n\n\t\t// Register Controllers.\n\t\tuserApp.Handle(new(AuthenticatedUserController))\n\t\tuserApp.Handle(new(UnauthenticatedUserController))\n\t}\n\n\te := httptest.New(t, app)\n\te.GET(\"/user\").Expect().Status(httptest.StatusUnauthorized).Body().IsEqual(\"unauth\")\n\t// Test raw stop execution with a status code sent on the controller's method.\n\te.GET(\"/user/with/status/on/method\").Expect().Status(httptest.StatusBadRequest).Body().IsEqual(\"unauth\")\n\t// Test stop execution with status but last code sent through the controller's method.\n\te.GET(\"/user/with/status/on/method/too\").Expect().Status(httptest.StatusInternalServerError).Body().IsEqual(\"unauth\")\n\t// Test raw stop execution and no status code sent on controller's method (should be OK).\n\te.GET(\"/user/with/no/status\").Expect().Status(httptest.StatusOK).Body().IsEqual(\"unauth\")\n\n\t// Test authenticated request.\n\te.GET(\"/user\").WithQuery(\"id\", 42).Expect().Status(httptest.StatusOK).Body().IsEqual(\"auth: 42\")\n\n\t// Test HandleHTTPError method accepts a not found and returns a 404\n\t// from a shared controller and overlapped, the url parameter matters because this method was overlapped.\n\te.GET(\"/user/notfound\").Expect().Status(httptest.StatusBadRequest).\n\t\tBody().IsEqual(\"error: *mvc_test.UnauthenticatedUserController: from: 404 to: 400\")\n\te.GET(\"/user/notfound\").WithQuery(\"id\", 42).Expect().Status(httptest.StatusBadRequest).\n\t\tBody().IsEqual(\"error: *mvc_test.AuthenticatedUserController: from: 404 to: 400\")\n}\n\ntype AuthenticatedTest uint64\n\nfunc authDependency(ctx iris.Context) AuthenticatedTest {\n\t// this will be executed on not found too and that's what we expect.\n\n\tuserID := ctx.URLParamUint64(\"id\") // just for the test.\n\tif userID == 0 {\n\t\tif ctx.GetStatusCode() == iris.StatusNotFound || // do not send 401 on not founds, keep 404 and let controller decide.\n\t\t\tctx.Path() == \"/user/with/status/on/method\" || ctx.Path() == \"/user/with/np/status\" { // leave controller method decide, raw stop execution.\n\t\t\tctx.StopExecution()\n\t\t} else {\n\t\t\tctx.StopWithStatus(iris.StatusUnauthorized)\n\t\t}\n\n\t\treturn 0\n\t}\n\n\treturn AuthenticatedTest(userID)\n}\n\ntype BaseControllerTest struct{}\n\nfunc (c *BaseControllerTest) HandleHTTPError(ctx iris.Context, code mvc.Code) (string, int) {\n\tif ctx.GetStatusCode() != int(code) {\n\t\t// should never happen.\n\t\tpanic(\"Context current status code and given mvc code do not match!\")\n\t}\n\n\tctrlName := ctx.Controller().Type().String()\n\tnewCode := 400\n\treturn fmt.Sprintf(\"error: %s: from: %d to: %d\", ctrlName, int(code), newCode), newCode\n}\n\ntype UnauthenticatedUserController struct {\n\tBaseControllerTest\n}\n\nfunc (c *UnauthenticatedUserController) Get() string {\n\treturn \"unauth\"\n}\n\nfunc (c *UnauthenticatedUserController) GetWithNoStatus() string {\n\treturn \"unauth\"\n}\n\nfunc (c *UnauthenticatedUserController) GetWithStatusOnMethod() (string, int) {\n\treturn \"unauth\", iris.StatusBadRequest\n}\n\nfunc (c *UnauthenticatedUserController) GetWithStatusOnMethodToo() (string, int) {\n\treturn \"unauth\", iris.StatusInternalServerError\n}\n\ntype AuthenticatedUserController struct {\n\tBaseControllerTest\n\n\tCurrentUserID AuthenticatedTest\n}\n\nfunc (c *AuthenticatedUserController) Get() string {\n\treturn fmt.Sprintf(\"auth: %d\", c.CurrentUserID)\n}\n"
  },
  {
    "path": "mvc/controller_test.go",
    "content": "// black-box testing\npackage mvc_test\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/core/router\"\n\t\"github.com/kataras/iris/v12/hero\"\n\t\"github.com/kataras/iris/v12/httptest\"\n\n\t. \"github.com/kataras/iris/v12/mvc\"\n)\n\ntype testController struct {\n\tCtx *context.Context\n}\n\nvar writeMethod = func(ctx *context.Context) {\n\tctx.WriteString(ctx.Method())\n}\n\nfunc (c *testController) Get() {\n\twriteMethod(c.Ctx)\n}\n\nfunc (c *testController) Post() {\n\twriteMethod(c.Ctx)\n}\n\nfunc (c *testController) Put() {\n\twriteMethod(c.Ctx)\n}\n\nfunc (c *testController) Delete() {\n\twriteMethod(c.Ctx)\n}\n\nfunc (c *testController) Connect() {\n\twriteMethod(c.Ctx)\n}\n\nfunc (c *testController) Head() {\n\twriteMethod(c.Ctx)\n}\n\nfunc (c *testController) Patch() {\n\twriteMethod(c.Ctx)\n}\n\nfunc (c *testController) Options() {\n\twriteMethod(c.Ctx)\n}\n\nfunc (c *testController) Trace() {\n\twriteMethod(c.Ctx)\n}\n\ntype (\n\ttestControllerAll struct{ Ctx *context.Context }\n\ttestControllerAny struct{ Ctx *context.Context } // exactly the same as All.\n)\n\nfunc (c *testControllerAll) All() {\n\twriteMethod(c.Ctx)\n}\n\nfunc (c *testControllerAny) Any() {\n\twriteMethod(c.Ctx)\n}\n\nfunc TestControllerMethodFuncs(t *testing.T) {\n\tapp := iris.New()\n\n\tNew(app).Handle(new(testController))\n\tNew(app.Party(\"/all\")).Handle(new(testControllerAll))\n\tNew(app.Party(\"/any\")).Handle(new(testControllerAny))\n\n\te := httptest.New(t, app)\n\tfor _, method := range router.AllMethods {\n\n\t\te.Request(method, \"/\").Expect().Status(iris.StatusOK).\n\t\t\tBody().IsEqual(method)\n\n\t\te.Request(method, \"/all\").Expect().Status(iris.StatusOK).\n\t\t\tBody().IsEqual(method)\n\n\t\te.Request(method, \"/any\").Expect().Status(iris.StatusOK).\n\t\t\tBody().IsEqual(method)\n\t}\n}\n\ntype testControllerBeginAndEndRequestFunc struct {\n\tCtx *context.Context\n\n\tUsername string\n}\n\n// called before of every method (Get() or Post()).\n//\n// useful when more than one methods using the\n// same request values or context's function calls.\nfunc (c *testControllerBeginAndEndRequestFunc) BeginRequest(ctx *context.Context) {\n\tc.Username = ctx.Params().Get(\"username\")\n}\n\n// called after every method (Get() or Post()).\nfunc (c *testControllerBeginAndEndRequestFunc) EndRequest(ctx *context.Context) {\n\tctx.WriteString(\"done\") // append \"done\" to the response\n}\n\nfunc (c *testControllerBeginAndEndRequestFunc) Get() {\n\tc.Ctx.WriteString(c.Username)\n}\n\nfunc (c *testControllerBeginAndEndRequestFunc) Post() {\n\tc.Ctx.WriteString(c.Username)\n}\n\nfunc TestControllerBeginAndEndRequestFunc(t *testing.T) {\n\tapp := iris.New()\n\tNew(app.Party(\"/profile/{username}\")).\n\t\tHandle(new(testControllerBeginAndEndRequestFunc))\n\n\te := httptest.New(t, app)\n\tusernames := []string{\n\t\t\"kataras\",\n\t\t\"makis\",\n\t\t\"efi\",\n\t\t\"rg\",\n\t\t\"bill\",\n\t\t\"whoisyourdaddy\",\n\t}\n\tdoneResponse := \"done\"\n\n\tfor _, username := range usernames {\n\t\te.GET(\"/profile/\" + username).Expect().Status(iris.StatusOK).\n\t\t\tBody().IsEqual(username + doneResponse)\n\t\te.POST(\"/profile/\" + username).Expect().Status(iris.StatusOK).\n\t\t\tBody().IsEqual(username + doneResponse)\n\t}\n}\n\nfunc TestControllerBeginAndEndRequestFuncBindMiddleware(t *testing.T) {\n\tapp := iris.New()\n\tusernames := map[string]bool{\n\t\t\"kataras\":        true,\n\t\t\"makis\":          false,\n\t\t\"efi\":            true,\n\t\t\"rg\":             false,\n\t\t\"bill\":           true,\n\t\t\"whoisyourdaddy\": false,\n\t}\n\tmiddlewareCheck := func(ctx *context.Context) {\n\t\tfor username, allow := range usernames {\n\t\t\tif ctx.Params().Get(\"username\") == username && allow {\n\t\t\t\tctx.Next()\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tctx.StatusCode(iris.StatusForbidden)\n\t\tctx.Writef(\"forbidden\")\n\t}\n\n\tapp.PartyFunc(\"/profile/{username}\", func(r iris.Party) {\n\t\tr.Use(middlewareCheck)\n\t\tNew(r).Handle(new(testControllerBeginAndEndRequestFunc))\n\t})\n\n\te := httptest.New(t, app)\n\n\tdoneResponse := \"done\"\n\n\tfor username, allow := range usernames {\n\t\tgetEx := e.GET(\"/profile/\" + username).Expect()\n\t\tif allow {\n\t\t\tgetEx.Status(iris.StatusOK).\n\t\t\t\tBody().IsEqual(username + doneResponse)\n\t\t} else {\n\t\t\tgetEx.Status(iris.StatusForbidden).Body().IsEqual(\"forbidden\")\n\t\t}\n\n\t\tpostEx := e.POST(\"/profile/\" + username).Expect()\n\t\tif allow {\n\t\t\tpostEx.Status(iris.StatusOK).\n\t\t\t\tBody().IsEqual(username + doneResponse)\n\t\t} else {\n\t\t\tpostEx.Status(iris.StatusForbidden).Body().IsEqual(\"forbidden\")\n\t\t}\n\t}\n}\n\ntype Model struct {\n\tUsername string\n}\n\ntype testControllerEndRequestAwareness struct {\n\tCtx *context.Context\n}\n\nfunc (c *testControllerEndRequestAwareness) Get() {\n\tusername := c.Ctx.Params().Get(\"username\")\n\tc.Ctx.Values().Set(c.Ctx.Application().ConfigurationReadOnly().GetViewDataContextKey(),\n\t\tmap[string]any{\n\t\t\t\"TestModel\": Model{Username: username},\n\t\t\t\"myModel\":   Model{Username: username + \"2\"},\n\t\t})\n}\n\nfunc writeModels(ctx *context.Context, names ...string) {\n\tif expected, got := len(names), len(ctx.GetViewData()); expected != got {\n\t\tctx.Writef(\"expected view data length: %d but got: %d for names: %s\", expected, got, names)\n\t\treturn\n\t}\n\n\tfor _, name := range names {\n\n\t\tm, ok := ctx.GetViewData()[name]\n\t\tif !ok {\n\t\t\tctx.Writef(\"fail load and set the %s\", name)\n\t\t\treturn\n\t\t}\n\n\t\tmodel, ok := m.(Model)\n\t\tif !ok {\n\t\t\tctx.Writef(\"fail to override the %s' name by the tag\", name)\n\t\t\treturn\n\t\t}\n\n\t\tctx.WriteString(model.Username)\n\t}\n}\n\nfunc (c *testControllerEndRequestAwareness) BeginRequest(ctx *context.Context) {}\nfunc (c *testControllerEndRequestAwareness) EndRequest(ctx *context.Context) {\n\twriteModels(ctx, \"TestModel\", \"myModel\")\n}\n\nfunc TestControllerEndRequestAwareness(t *testing.T) {\n\tapp := iris.New()\n\tNew(app.Party(\"/era/{username}\")).Handle(new(testControllerEndRequestAwareness))\n\n\te := httptest.New(t, app)\n\tusernames := []string{\n\t\t\"kataras\",\n\t\t\"makis\",\n\t}\n\n\tfor _, username := range usernames {\n\t\te.GET(\"/era/\" + username).Expect().Status(iris.StatusOK).\n\t\t\tBody().IsEqual(username + username + \"2\")\n\t}\n}\n\ntype testBindType struct {\n\ttitle string\n}\n\ntype testControllerBindStruct struct {\n\tCtx *context.Context\n\n\t//  should start with upper letter of course\n\tTitlePointer *testBindType // should have the value of the \"myTitlePtr\" on test\n\tTitleValue   testBindType  // should have the value of the \"myTitleV\" on test\n\tOther        string        // just another type to check the field collection, should be empty\n}\n\nfunc (t *testControllerBindStruct) Get() {\n\tt.Ctx.WriteString(t.TitlePointer.title + t.TitleValue.title + t.Other)\n}\n\n// test if context can be binded to the controller's function\n// without need to declare it to a struct if not needed.\nfunc (t *testControllerBindStruct) GetCtx(ctx iris.Context) {\n\tctx.StatusCode(iris.StatusContinue)\n}\n\ntype testControllerBindDeep struct {\n\ttestControllerBindStruct\n}\n\nfunc (t *testControllerBindDeep) BeforeActivation(b BeforeActivation) {\n\tb.Dependencies().Register(func(ctx iris.Context) (v testCustomStruct, err error) {\n\t\terr = ctx.ReadJSON(&v)\n\t\treturn\n\t})\n}\n\nfunc (t *testControllerBindDeep) Get() {\n\t// \tt.testControllerBindStruct.Get()\n\tt.Ctx.WriteString(t.TitlePointer.title + t.TitleValue.title + t.Other)\n}\n\nfunc (t *testControllerBindDeep) Post(v testCustomStruct) string {\n\treturn v.Name\n}\n\nfunc TestControllerDependencies(t *testing.T) {\n\tapp := iris.New()\n\t// app.Logger().SetLevel(\"debug\")\n\n\tt1, t2 := \"my pointer title\", \"val title\"\n\t// test bind pointer to pointer of the correct type\n\tmyTitlePtr := &testBindType{title: t1}\n\t// test bind value to value of the correct type\n\tmyTitleV := testBindType{title: t2}\n\tm := New(app)\n\tm.Register(myTitlePtr, myTitleV)\n\tm.Handle(new(testControllerBindStruct))\n\tm.Clone(app.Party(\"/deep\")).Handle(new(testControllerBindDeep))\n\n\te := httptest.New(t, app)\n\texpected := t1 + t2\n\te.GET(\"/\").Expect().Status(iris.StatusOK).\n\t\tBody().IsEqual(expected)\n\te.GET(\"/ctx\").Expect().Status(iris.StatusContinue)\n\n\te.GET(\"/deep\").Expect().Status(iris.StatusOK).\n\t\tBody().IsEqual(expected)\n\n\te.POST(\"/deep\").WithJSON(iris.Map{\"name\": \"kataras\"}).Expect().Status(iris.StatusOK).\n\t\tBody().IsEqual(\"kataras\")\n\n\te.POST(\"/deep\").Expect().Status(iris.StatusBadRequest).\n\t\tBody().IsEqual(\"EOF\")\n}\n\ntype testCtrl0 struct {\n\ttestCtrl00\n}\n\nfunc (c *testCtrl0) Get() string {\n\treturn c.Ctx.Params().Get(\"username\")\n}\n\nfunc (c *testCtrl0) EndRequest(ctx *context.Context) {\n\tif c.TitlePointer == nil {\n\t\tctx.WriteString(\"\\nTitlePointer is nil!\\n\")\n\t} else {\n\t\tctx.WriteString(c.TitlePointer.title)\n\t}\n\n\t// should be the same as `.testCtrl000.testCtrl0000.EndRequest(ctx)`\n\tc.testCtrl00.EndRequest(ctx)\n}\n\ntype testCtrl00 struct {\n\tCtx *context.Context\n\n\ttestCtrl000\n}\n\ntype testCtrl000 struct {\n\ttestCtrl0000\n\n\tTitlePointer *testBindType\n}\n\ntype testCtrl0000 struct {\n}\n\nfunc (c *testCtrl0000) BeginRequest(ctx *context.Context) {}\nfunc (c *testCtrl0000) EndRequest(ctx *context.Context) {\n\tctx.Writef(\"finish\")\n}\n\nfunc TestControllerInsideControllerRecursively(t *testing.T) {\n\tvar (\n\t\tusername = \"gerasimos\"\n\t\ttitle    = \"mytitle\"\n\t\texpected = username + title + \"finish\"\n\t)\n\n\tapp := iris.New()\n\tm := New(app.Party(\"/user/{username}\"))\n\tm.Register(&testBindType{title: title})\n\tm.Handle(new(testCtrl0))\n\n\te := httptest.New(t, app)\n\te.GET(\"/user/\" + username).Expect().\n\t\tStatus(iris.StatusOK).Body().IsEqual(expected)\n}\n\ntype testControllerRelPathFromFunc struct{}\n\nfunc (c *testControllerRelPathFromFunc) BeginRequest(ctx *context.Context) {}\nfunc (c *testControllerRelPathFromFunc) EndRequest(ctx *context.Context) {\n\tctx.Writef(\"%s:%s\", ctx.Method(), ctx.Path())\n}\n\nfunc (c *testControllerRelPathFromFunc) Get()                         {}\nfunc (c *testControllerRelPathFromFunc) GetBy(uint64)                 {}\nfunc (c *testControllerRelPathFromFunc) GetUint8RatioBy(uint8)        {}\nfunc (c *testControllerRelPathFromFunc) GetInt64RatioBy(int64)        {}\nfunc (c *testControllerRelPathFromFunc) GetAnythingByWildcard(string) {}\n\nfunc (c *testControllerRelPathFromFunc) GetLogin()  {}\nfunc (c *testControllerRelPathFromFunc) PostLogin() {}\n\nfunc (c *testControllerRelPathFromFunc) GetAdminLogin() {}\n\nfunc (c *testControllerRelPathFromFunc) PutSomethingIntoThis() {}\n\nfunc (c *testControllerRelPathFromFunc) GetSomethingBy(bool) {}\n\nfunc (c *testControllerRelPathFromFunc) GetSomethingByBy(string, int) {}\n\nfunc (c *testControllerRelPathFromFunc) GetSomethingNewBy(string, int)      {} // two input arguments, one By which is the latest word.\nfunc (c *testControllerRelPathFromFunc) GetSomethingByElseThisBy(bool, int) {} // two input arguments\n\nfunc (c *testControllerRelPathFromFunc) GetLocationX()      {}\nfunc (c *testControllerRelPathFromFunc) GetLocationXY()     {}\nfunc (c *testControllerRelPathFromFunc) GetLocationZBy(int) {}\n\nfunc TestControllerRelPathFromFunc(t *testing.T) {\n\tapp := iris.New()\n\tNew(app).Handle(new(testControllerRelPathFromFunc))\n\n\te := httptest.New(t, app)\n\te.GET(\"/\").Expect().Status(iris.StatusOK).\n\t\tBody().IsEqual(\"GET:/\")\n\n\te.GET(\"/18446744073709551615\").Expect().Status(iris.StatusOK).\n\t\tBody().IsEqual(\"GET:/18446744073709551615\")\n\te.GET(\"/uint8/ratio/255\").Expect().Status(iris.StatusOK).\n\t\tBody().IsEqual(\"GET:/uint8/ratio/255\")\n\te.GET(\"/uint8/ratio/256\").Expect().Status(iris.StatusNotFound)\n\te.GET(\"/int64/ratio/-42\").Expect().Status(iris.StatusOK).\n\t\tBody().IsEqual(\"GET:/int64/ratio/-42\")\n\te.GET(\"/something/true\").Expect().Status(iris.StatusOK).\n\t\tBody().IsEqual(\"GET:/something/true\")\n\te.GET(\"/something/false\").Expect().Status(iris.StatusOK).\n\t\tBody().IsEqual(\"GET:/something/false\")\n\te.GET(\"/something/truee\").Expect().Status(iris.StatusNotFound)\n\te.GET(\"/something/falsee\").Expect().Status(iris.StatusNotFound)\n\te.GET(\"/something/kataras/42\").Expect().Status(iris.StatusOK).\n\t\tBody().IsEqual(\"GET:/something/kataras/42\")\n\te.GET(\"/something/new/kataras/42\").Expect().Status(iris.StatusOK).\n\t\tBody().IsEqual(\"GET:/something/new/kataras/42\")\n\te.GET(\"/something/true/else/this/42\").Expect().Status(iris.StatusOK).\n\t\tBody().IsEqual(\"GET:/something/true/else/this/42\")\n\n\te.GET(\"/login\").Expect().Status(iris.StatusOK).\n\t\tBody().IsEqual(\"GET:/login\")\n\te.POST(\"/login\").Expect().Status(iris.StatusOK).\n\t\tBody().IsEqual(\"POST:/login\")\n\te.GET(\"/admin/login\").Expect().Status(iris.StatusOK).\n\t\tBody().IsEqual(\"GET:/admin/login\")\n\te.PUT(\"/something/into/this\").Expect().Status(iris.StatusOK).\n\t\tBody().IsEqual(\"PUT:/something/into/this\")\n\te.GET(\"/42\").Expect().Status(iris.StatusOK).\n\t\tBody().IsEqual(\"GET:/42\")\n\te.GET(\"/anything/here\").Expect().Status(iris.StatusOK).\n\t\tBody().IsEqual(\"GET:/anything/here\")\n\n\te.GET(\"/location/x\").Expect().Status(iris.StatusOK).\n\t\tBody().IsEqual(\"GET:/location/x\")\n\te.GET(\"/location/x/y\").Expect().Status(iris.StatusOK).\n\t\tBody().IsEqual(\"GET:/location/x/y\")\n\te.GET(\"/location/z/42\").Expect().Status(iris.StatusOK).\n\t\tBody().IsEqual(\"GET:/location/z/42\")\n}\n\ntype testControllerActivateListener struct {\n\tTitlePointer *testBindType\n}\n\nfunc (c *testControllerActivateListener) BeforeActivation(b BeforeActivation) {\n\tb.Dependencies().Register(&testBindType{title: \"overrides the dependency but not the field\"}) // overrides the `Register` previous calls.\n\n\t// b.Handle(\"POST\", \"/me/tos-read\", \"MeTOSRead\")\n\t// b.Handle(\"GET\", \"/me/tos-read\", \"MeTOSRead\")\n\t// OR:\n\tb.HandleMany(\"GET POST\", \"/me/tos-read\", \"MeTOSRead\")\n}\n\nfunc (c *testControllerActivateListener) Get() string {\n\treturn c.TitlePointer.title\n}\n\nfunc (c *testControllerActivateListener) MeTOSRead() string {\n\treturn \"MeTOSRead\"\n}\n\nfunc TestControllerActivateListener(t *testing.T) {\n\tapp := iris.New()\n\tNew(app).Handle(new(testControllerActivateListener))\n\tm := New(app)\n\tm.Register(&testBindType{\n\t\ttitle: \"my title\",\n\t})\n\tm.Party(\"/manual\").Handle(new(testControllerActivateListener))\n\t// or\n\tm.Party(\"/manual2\").Handle(&testControllerActivateListener{\n\t\tTitlePointer: &testBindType{\n\t\t\ttitle: \"my manual title\",\n\t\t},\n\t})\n\n\te := httptest.New(t, app)\n\te.GET(\"/\").Expect().Status(iris.StatusOK).\n\t\tBody().IsEqual(\"overrides the dependency but not the field\")\n\te.GET(\"/me/tos-read\").Expect().Status(iris.StatusOK).\n\t\tBody().IsEqual(\"MeTOSRead\")\n\te.POST(\"/me/tos-read\").Expect().Status(iris.StatusOK).\n\t\tBody().IsEqual(\"MeTOSRead\")\n\n\te.GET(\"/manual\").Expect().Status(iris.StatusOK).\n\t\tBody().IsEqual(\"overrides the dependency but not the field\")\n\te.GET(\"/manual2\").Expect().Status(iris.StatusOK).\n\t\tBody().IsEqual(\"my manual title\")\n}\n\ntype testControllerNotCreateNewDueManuallySettingAllFields struct {\n\tT *testing.T\n\n\tTitlePointer *testBindType\n}\n\nfunc (c *testControllerNotCreateNewDueManuallySettingAllFields) AfterActivation(a AfterActivation) {\n\tif n := len(a.DependenciesReadOnly()) - len(hero.BuiltinDependencies) - 1; /* Application */ n != 1 {\n\t\tc.T.Fatalf(`expecting 1 dependency;\n- the 'T' and the 'TitlePointer' are manually binded (nonzero fields on initilization)\n- controller has no more than these two fields, it's a singleton\n- however, the dependencies length here should be 1 because the injector's options handler dependencies contains the controller's value dependency itself\n--  got dependencies length: %d`, n)\n\t}\n\n\tif !a.Singleton() {\n\t\tc.T.Fatalf(`this controller should be tagged as Singleton. It shouldn't be tagged used as request scoped(create new instances on each request),\n\t\t it doesn't contain any dynamic value or dependencies that should be binded via the iris mvc engine`)\n\t}\n}\n\nfunc (c *testControllerNotCreateNewDueManuallySettingAllFields) Get() string {\n\treturn c.TitlePointer.title\n}\n\nfunc TestControllerNotCreateNewDueManuallySettingAllFields(t *testing.T) {\n\tapp := iris.New()\n\tNew(app).Handle(&testControllerNotCreateNewDueManuallySettingAllFields{\n\t\tT: t,\n\t\tTitlePointer: &testBindType{\n\t\t\ttitle: \"my title\",\n\t\t},\n\t})\n\n\te := httptest.New(t, app)\n\te.GET(\"/\").Expect().Status(iris.StatusOK).\n\t\tBody().IsEqual(\"my title\")\n}\n\ntype testControllerRequestScopedDependencies struct {\n\tMyContext    *testMyContext\n\tCustomStruct *testCustomStruct\n}\n\nfunc (c *testControllerRequestScopedDependencies) Get() *testCustomStruct {\n\treturn c.CustomStruct\n}\n\nfunc (c *testControllerRequestScopedDependencies) GetCustomContext() string {\n\treturn c.MyContext.OtherField\n}\n\nfunc newRequestDep1(ctx *context.Context) *testCustomStruct {\n\treturn &testCustomStruct{\n\t\tName: ctx.URLParam(\"name\"),\n\t\tAge:  ctx.URLParamIntDefault(\"age\", 0),\n\t}\n}\n\ntype testMyContext struct {\n\tContext    *context.Context\n\tOtherField string\n}\n\nfunc newRequestDep2(ctx *context.Context) *testMyContext {\n\treturn &testMyContext{\n\t\tContext:    ctx,\n\t\tOtherField: \"test\",\n\t}\n}\n\nfunc TestControllerRequestScopedDependencies(t *testing.T) {\n\tapp := iris.New()\n\tm := New(app)\n\tm.Register(newRequestDep1)\n\tm.Register(newRequestDep2)\n\tm.Handle(new(testControllerRequestScopedDependencies))\n\n\te := httptest.New(t, app)\n\te.GET(\"/\").WithQuery(\"name\", \"kataras\").WithQuery(\"age\", 27).\n\t\tExpect().Status(httptest.StatusOK).JSON().IsEqual(&testCustomStruct{\n\t\tName: \"kataras\",\n\t\tAge:  27,\n\t})\n\te.GET(\"/custom/context\").Expect().Status(httptest.StatusOK).Body().IsEqual(\"test\")\n}\n\ntype (\n\ttestServiceDoSomething struct{}\n\n\tTestControllerAsDeepDep struct {\n\t\tCtx     iris.Context\n\t\tService *testServiceDoSomething\n\t}\n\n\tFooController struct {\n\t\tTestControllerAsDeepDep\n\t}\n\n\tBarController struct {\n\t\tFooController\n\t}\n\n\tFinalController struct {\n\t\tBarController\n\t}\n)\n\nfunc (s *testServiceDoSomething) DoSomething(ctx iris.Context) {\n\tctx.WriteString(\"foo bar\")\n}\n\nfunc (c *FinalController) GetSomething() {\n\tc.Service.DoSomething(c.Ctx)\n}\n\nfunc TestControllersInsideControllerDeep(t *testing.T) {\n\tapp := iris.New()\n\tm := New(app)\n\tm.Register(new(testServiceDoSomething))\n\tm.Handle(new(FinalController))\n\n\te := httptest.New(t, app)\n\te.GET(\"/something\").Expect().Status(httptest.StatusOK).Body().IsEqual(\"foo bar\")\n}\n\ntype testApplicationDependency struct {\n\tApp *Application\n}\n\nfunc (c *testApplicationDependency) Get() string {\n\treturn c.App.Name\n}\n\nfunc TestApplicationDependency(t *testing.T) {\n\tapp := iris.New()\n\tm := New(app).SetName(\"app1\")\n\tm.Handle(new(testApplicationDependency))\n\n\tm2 := m.Clone(app.Party(\"/other\")).SetName(\"app2\")\n\tm2.Handle(new(testApplicationDependency))\n\n\te := httptest.New(t, app)\n\te.GET(\"/\").Expect().Status(httptest.StatusOK).Body().IsEqual(\"app1\")\n\te.GET(\"/other\").Expect().Status(httptest.StatusOK).Body().IsEqual(\"app2\")\n}\n\ntype testControllerMethodHandlerBindStruct struct{}\n\ntype bindStructData struct {\n\tName string `json:\"name\" url:\"name\"`\n}\n\nfunc (*testControllerMethodHandlerBindStruct) Any(data bindStructData) bindStructData {\n\treturn data\n}\n\nfunc (*testControllerMethodHandlerBindStruct) PostBySlice(id uint64, manyData []bindStructData) []bindStructData {\n\treturn manyData\n}\n\ntype dataSlice []bindStructData\n\nfunc (*testControllerMethodHandlerBindStruct) PostBySlicetype(id uint64, manyData dataSlice) dataSlice {\n\treturn manyData\n}\n\ntype dataSlicePtr []*bindStructData\n\nfunc (*testControllerMethodHandlerBindStruct) PostBySlicetypeptr(id uint64, manyData dataSlicePtr) dataSlicePtr {\n\treturn manyData\n}\n\nfunc TestControllerMethodHandlerBindStruct(t *testing.T) {\n\tapp := iris.New()\n\n\tm := New(app.Party(\"/data\"))\n\tm.HandleError(func(ctx iris.Context, err error) {\n\t\tt.Fatalf(\"Path: %s, Error: %v\", ctx.Path(), err)\n\t})\n\n\tm.Handle(new(testControllerMethodHandlerBindStruct))\n\n\tdata := bindStructData{Name: \"kataras\"}\n\tmanyData := []bindStructData{data, {\"john doe\"}}\n\n\te := httptest.New(t, app)\n\te.GET(\"/data\").WithQueryObject(data).Expect().Status(httptest.StatusOK).JSON().IsEqual(data)\n\te.PATCH(\"/data\").WithJSON(data).Expect().Status(httptest.StatusOK).JSON().IsEqual(data)\n\te.POST(\"/data/42/slice\").WithJSON(manyData).Expect().Status(httptest.StatusOK).JSON().IsEqual(manyData)\n\te.POST(\"/data/42/slicetype\").WithJSON(manyData).Expect().Status(httptest.StatusOK).JSON().IsEqual(manyData)\n\te.POST(\"/data/42/slicetypeptr\").WithJSON(manyData).Expect().Status(httptest.StatusOK).JSON().IsEqual(manyData)\n\t// more tests inside the hero package itself.\n}\n\nfunc TestErrorHandlerContinue(t *testing.T) {\n\tapp := iris.New()\n\tm := New(app)\n\tm.Handle(new(testControllerErrorHandlerContinue))\n\tm.Handle(new(testControllerFieldErrorHandlerContinue))\n\te := httptest.New(t, app)\n\n\tfor _, path := range []string{\"/test\", \"/test/field\"} {\n\t\te.POST(path).WithMultipart().\n\t\t\tWithFormField(\"username\", \"makis\").\n\t\t\tWithFormField(\"age\", \"27\").\n\t\t\tWithFormField(\"unknown\", \"continue\").\n\t\t\tExpect().Status(httptest.StatusOK).Body().IsEqual(\"makis is 27 years old\\n\")\n\t}\n}\n\ntype testControllerErrorHandlerContinue struct{}\n\ntype registerForm struct {\n\tUsername string `form:\"username\"`\n\tAge      int    `form:\"age\"`\n}\n\nfunc (c *testControllerErrorHandlerContinue) HandleError(ctx iris.Context, err error) {\n\tif iris.IsErrPath(err) {\n\t\treturn // continue.\n\t}\n\n\tctx.StopWithError(iris.StatusBadRequest, err)\n}\n\nfunc (c *testControllerErrorHandlerContinue) PostTest(form registerForm) string {\n\treturn fmt.Sprintf(\"%s is %d years old\\n\", form.Username, form.Age)\n}\n\ntype testControllerFieldErrorHandlerContinue struct {\n\tForm *registerForm\n}\n\nfunc (c *testControllerFieldErrorHandlerContinue) HandleError(ctx iris.Context, err error) {\n\tif iris.IsErrPath(err) {\n\t\treturn // continue.\n\t}\n\n\tctx.StopWithError(iris.StatusBadRequest, err)\n}\n\nfunc (c *testControllerFieldErrorHandlerContinue) PostTestField() string {\n\treturn fmt.Sprintf(\"%s is %d years old\\n\", c.Form.Username, c.Form.Age)\n}\n"
  },
  {
    "path": "mvc/grpc.go",
    "content": "package mvc\n\nimport (\n\t\"net/http\"\n\t\"path\"\n\n\t\"github.com/kataras/iris/v12/context\"\n)\n\n// GRPC registers a controller which serves gRPC clients.\n// It accepts the controller ptr to a struct value,\n// the gRPCServer itself, and a strict option which is explained below.\n//\n// The differences between an GRPC-based controller and a common one are:\n// HTTP verb: only POST (Party.AllowMethods can be used for more),\n// method parsing is disabled: path is the function name as it is,\n// if 'strictMode' option is true then this controller will only serve gRPC-based clients\n// and fires 404 on common HTTP clients,\n// otherwise HTTP clients can send and receive JSON (protos contain json struct fields by-default).\ntype GRPC struct {\n\t// Server is required and should be gRPC Server derives from google's grpc package.\n\tServer http.Handler\n\t// ServiceName is required and should be the name of the service (used to build the gRPC route path),\n\t// e.g. \"helloworld.Greeter\".\n\t// For a controller's method of \"SayHello\" and ServiceName \"helloworld.Greeter\",\n\t// both gRPC and common HTTP request path is: \"/helloworld.Greeter/SayHello\".\n\t//\n\t// Tip: the ServiceName can be fetched through proto's file descriptor, e.g.\n\t// serviceName := pb.File_helloworld_proto.Services().Get(0).FullName().\n\tServiceName string\n\n\t// When Strict option is true then this controller will only serve gRPC-based clients\n\t// and fires 404 on common HTTP clients.\n\tStrict bool\n}\n\nvar _ Option = GRPC{}\n\n// Apply parses the controller's methods and registers gRPC handlers to the application.\nfunc (g GRPC) Apply(c *ControllerActivator) {\n\tdefer c.Activated()\n\n\tpre := func(ctx *context.Context) {\n\t\tif ctx.IsGRPC() { // gRPC, consumes and produces protobuf.\n\t\t\tg.Server.ServeHTTP(ctx.ResponseWriter(), ctx.Request())\n\t\t\tctx.StopExecution()\n\t\t\treturn\n\t\t}\n\n\t\t// If strict was true fires 404 on common HTTP clients.\n\t\tif g.Strict {\n\t\t\tctx.NotFound()\n\t\t\tctx.StopExecution()\n\t\t\treturn\n\t\t}\n\n\t\t// Allow common HTTP clients, consumes and produces JSON.\n\t\tctx.Next()\n\t}\n\n\tfor i := 0; i < c.Type.NumMethod(); i++ {\n\t\tm := c.Type.Method(i)\n\t\tpath := path.Join(g.ServiceName, m.Name)\n\t\tif g.Strict {\n\t\t\tc.app.Router.HandleMany(http.MethodPost, path, pre)\n\t\t} else if route := c.Handle(http.MethodPost, path, m.Name, pre); route != nil {\n\t\t\tbckp := route.Description\n\t\t\troute.Description = \"gRPC\"\n\t\t\tif g.Strict {\n\t\t\t\troute.Description += \"-only\"\n\t\t\t}\n\t\t\troute.Description += \" \" + bckp // e.g. \"gRPC controller\"\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "mvc/mvc.go",
    "content": "package mvc\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/core/router\"\n\t\"github.com/kataras/iris/v12/hero\"\n\t\"github.com/kataras/iris/v12/websocket\"\n\n\t\"github.com/kataras/golog\"\n\t\"github.com/kataras/pio\"\n)\n\n// Application is the high-level component of the \"mvc\" package.\n// It's the API that you will be using to register controllers among with their\n// dependencies that your controllers may expecting.\n// It contains the Router(iris.Party) in order to be able to register\n// template layout, middleware, done handlers as you used with the\n// standard Iris APIBuilder.\n//\n// The Engine is created by the `New` method and it's the dependencies holder\n// and controllers factory.\n//\n// See `mvc#New` for more.\ntype Application struct {\n\tcontainer *hero.Container\n\t// This Application's Name. Keep names unique to each other.\n\tName string\n\n\tRouter               router.Party\n\tControllers          []*ControllerActivator\n\twebsocketControllers []websocket.ConnHandler\n\n\t// Disables verbose logging for controllers under this and its children mvc apps.\n\t// Defaults to false.\n\tcontrollersNoLog bool\n\n\t// Set custom path\n\tcustomPathWordFunc CustomPathWordFunc\n}\n\nfunc newApp(subRouter router.Party, container *hero.Container) *Application {\n\tapp := &Application{\n\t\tRouter:    subRouter,\n\t\tcontainer: container,\n\t}\n\n\t// Register this Application so any field or method's input argument of\n\t// *mvc.Application can point to the current MVC application that the controller runs on.\n\tregisterBuiltinDependencies(container, app)\n\treturn app\n}\n\n// See `hero.BuiltinDependencies` too, here we are registering dependencies per MVC Application.\nfunc registerBuiltinDependencies(container *hero.Container, deps ...any) {\n\tfor _, dep := range deps {\n\t\tdepTyp := reflect.TypeOf(dep)\n\t\tfor i, dependency := range container.Dependencies {\n\t\t\tif dependency.Static {\n\t\t\t\tif dependency.DestType == depTyp {\n\t\t\t\t\t// Remove any existing before register this one (see app.Clone).\n\t\t\t\t\tcopy(container.Dependencies[i:], container.Dependencies[i+1:])\n\t\t\t\t\tcontainer.Dependencies = container.Dependencies[:len(container.Dependencies)-1]\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tcontainer.Register(dep)\n\t}\n}\n\n// New returns a new mvc Application based on a \"party\".\n// Application creates a new engine which is responsible for binding the dependencies\n// and creating and activating the app's controller(s).\n//\n// Example: `New(app.Party(\"/todo\"))` or `New(app)` as it's the same as `New(app.Party(\"/\"))`.\nfunc New(party router.Party) *Application {\n\treturn newApp(party, party.ConfigureContainer().Container.Clone())\n}\n\n// Configure creates a new controller and configures it,\n// this function simply calls the `New(party)` and its `.Configure(configurators...)`.\n//\n// A call of `mvc.New(app.Party(\"/path\").Configure(buildMyMVC)` is equal to\n//\n//\t`mvc.Configure(app.Party(\"/path\"), buildMyMVC)`.\n//\n// Read more at `New() Application` and `Application#Configure` methods.\nfunc Configure(party router.Party, configurators ...func(*Application)) *Application {\n\t// Author's Notes->\n\t// About the Configure's comment: +5 space to be shown in equal width to the previous or after line.\n\t//\n\t// About the Configure's design chosen:\n\t// Yes, we could just have a `New(party, configurators...)`\n\t// but I think the `New()` and `Configure(configurators...)` API seems more native to programmers,\n\t// at least to me and the people I ask for their opinion between them.\n\t// Because the `New()` can actually return something that can be fully configured without its `Configure`,\n\t// its `Configure` is there just to design the apps better and help end-devs to split their code wisely.\n\treturn New(party).Configure(configurators...)\n}\n\n// Configure can be used to pass one or more functions that accept this\n// Application, use this to add dependencies and controller(s).\n//\n// Example: `New(app.Party(\"/todo\")).Configure(func(mvcApp *mvc.Application){...})`.\nfunc (app *Application) Configure(configurators ...func(*Application)) *Application {\n\tfor _, c := range configurators {\n\t\tc(app)\n\t}\n\treturn app\n}\n\n// SetName sets a unique name to this MVC Application.\n// Used for logging, not used in runtime yet, but maybe useful for future features.\n//\n// It returns this Application.\nfunc (app *Application) SetName(appName string) *Application {\n\tapp.Name = appName\n\treturn app\n}\n\n// SetCustomPathWordFunc sets a custom function\n// which is responsible to override the existing controllers method parsing.\nfunc (app *Application) SetCustomPathWordFunc(wordFunc CustomPathWordFunc) *Application {\n\tapp.customPathWordFunc = wordFunc\n\treturn app\n}\n\n// SetControllersNoLog disables verbose logging for next registered controllers\n// under this App and its children of `Application.Party` or `Application.Clone`.\n//\n// To disable logging for routes under a Party,\n// see `Party.SetRoutesNoLog` instead.\n//\n// Defaults to false when log level is \"debug\".\nfunc (app *Application) SetControllersNoLog(disable bool) *Application {\n\tapp.controllersNoLog = disable\n\treturn app\n}\n\n// EnableStructDependents will try to resolve\n// the fields of a struct value, if any, when it's a dependent struct value\n// based on the previous registered dependencies.\nfunc (app *Application) EnableStructDependents() *Application {\n\tapp.container.EnableStructDependents = true\n\treturn app\n}\n\n// Register appends one or more values as dependencies.\n// The value can be a single struct value-instance or a function\n// which has one input and one output, the input should be\n// an `iris.Context` and the output can be any type, that output type\n// will be bind-ed to the controller's field, if matching or to the\n// controller's methods, if matching.\n//\n// These dependencies \"dependencies\" can be changed per-controller as well,\n// via controller's `BeforeActivation` and `AfterActivation` methods,\n// look the `Handle` method for more.\n//\n// It returns this Application.\n//\n// Example: `.Register(loggerService{prefix: \"dev\"}, func(ctx iris.Context) User {...})`.\nfunc (app *Application) Register(dependencies ...any) *Application {\n\tif len(dependencies) > 0 && len(app.container.Dependencies) == len(hero.BuiltinDependencies) && len(app.Controllers) > 0 {\n\t\tallControllerNamesSoFar := make([]string, len(app.Controllers))\n\t\tfor i := range app.Controllers {\n\t\t\tallControllerNamesSoFar[i] = app.Controllers[i].Name()\n\t\t}\n\n\t\tgolog.Warnf(`mvc.Application#Register called after mvc.Application#Handle.\n\tThe controllers[%s] may miss required dependencies.\n\tSet the Logger's Level to \"debug\" to view the active dependencies per controller.`, strings.Join(allControllerNamesSoFar, \",\"))\n\t}\n\n\tfor _, dependency := range dependencies {\n\t\tapp.container.Register(dependency)\n\t}\n\n\treturn app\n}\n\ntype (\n\t// Option is an interface which does contain a single `Apply` method that accepts\n\t// a `ControllerActivator`. It can be passed on `Application.Handle` method to\n\t// mdoify the behavior right after the `BeforeActivation` state.\n\t//\n\t// See `GRPC` package-level structure\n\t// and `Version` package-level function too.\n\tOption interface {\n\t\tApply(*ControllerActivator)\n\t}\n\n\t// OptionFunc is the functional type of `Option`.\n\t// Read `Option` docs.\n\tOptionFunc func(*ControllerActivator)\n)\n\n// Apply completes the `Option` interface.\nfunc (opt OptionFunc) Apply(c *ControllerActivator) {\n\topt(c)\n}\n\n// IgnoreEmbedded is an Option which can be used to ignore all embedded struct's method handlers.\n// Note that even if the controller overrides the embedded methods\n// they will be still ignored because Go doesn't support this detection so far.\n// For global affect, set the `IgnoreEmbeddedControllers` package-level variable to true.\nvar IgnoreEmbedded OptionFunc = func(c *ControllerActivator) {\n\tc.SkipEmbeddedMethods()\n}\n\n// Handle serves a controller for the current mvc application's Router.\n// It accept any custom struct which its functions will be transformed\n// to routes.\n//\n// If \"controller\" has `BeforeActivation(b mvc.BeforeActivation)`\n// or/and `AfterActivation(a mvc.AfterActivation)` then these will be called between the controller's `.activate`,\n// use those when you want to modify the controller before or/and after\n// the controller will be registered to the main Iris Application.\n//\n// It returns this mvc Application.\n//\n// Usage: `.Handle(new(TodoController))`.\n//\n// Controller accepts a sub router and registers any custom struct\n// as controller, if struct doesn't have any compatible methods\n// neither are registered via `ControllerActivator`'s `Handle` method\n// then the controller is not registered at all.\n//\n// A Controller may have one or more methods\n// that are wrapped to a handler and registered as routes before the server ran.\n// The controller's method can accept any input argument that are previously binded\n// via the dependencies or route's path accepts dynamic path parameters.\n// The controller's fields are also bindable via the dependencies, either a\n// static value (service) or a function (dynamically) which accepts a context\n// and returns a single value (this type is being used to find the relative field or method's input argument).\n//\n// func(c *ExampleController) Get() string |\n// (string, string) |\n// (string, int) |\n// int |\n// (int, string |\n// (string, error) |\n// bool |\n// (any, bool) |\n// error |\n// (int, error) |\n// (customStruct, error) |\n// customStruct |\n// (customStruct, int) |\n// (customStruct, string) |\n// Result or (Result, error)\n// where Get is an HTTP Method func.\n//\n// Default behavior can be changed through second, variadic, variable \"options\",\n// e.g. Handle(controller, GRPC {Server: grpcServer, Strict: true})\n//\n// Examples at: https://github.com/kataras/iris/tree/main/_examples/mvc\nfunc (app *Application) Handle(controller any, options ...Option) *Application {\n\tc := app.handle(controller, options...)\n\t// Note: log on register-time, so they can catch any failures before build.\n\tif !app.controllersNoLog {\n\t\t// log only http (and versioned) or grpc controllers,\n\t\t// websocket is already logging itself.\n\t\tlogController(app.Router.Logger(), c)\n\t}\n\treturn app\n}\n\n// HandleWebsocket handles a websocket specific controller.\n// Its exported methods are the events.\n// If a \"Namespace\" field or method exists then namespace is set, otherwise empty namespace.\n// Note that a websocket controller is registered and ran under a specific connection connected to a namespace\n// and it cannot send HTTP responses on that state.\n// However all static and dynamic dependency injection features are working, as expected, like any regular MVC Controller.\nfunc (app *Application) HandleWebsocket(controller any) *websocket.Struct {\n\tc := app.handle(controller)\n\tc.markAsWebsocket()\n\n\twebsocketController := websocket.NewStruct(c.Value).SetInjector(makeInjector(c.injector))\n\tapp.websocketControllers = append(app.websocketControllers, websocketController)\n\treturn websocketController\n}\n\nfunc makeInjector(s *hero.Struct) websocket.StructInjector {\n\treturn func(_ reflect.Type, nsConn *websocket.NSConn) reflect.Value {\n\t\tv, _ := s.Acquire(websocket.GetContext(nsConn.Conn))\n\t\treturn v\n\t}\n}\n\nvar _ websocket.ConnHandler = (*Application)(nil)\n\n// GetNamespaces completes the websocket ConnHandler interface.\n// It returns a collection of namespace and events that\n// were registered through `HandleWebsocket` controllers.\nfunc (app *Application) GetNamespaces() websocket.Namespaces {\n\tif logger := app.Router.Logger(); logger.Level == golog.DebugLevel && !app.controllersNoLog {\n\t\twebsocket.EnableDebug(logger)\n\t}\n\n\treturn websocket.JoinConnHandlers(app.websocketControllers...).GetNamespaces()\n}\n\nfunc (app *Application) handle(controller any, options ...Option) *ControllerActivator {\n\t// initialize the controller's activator, nothing too magical so far.\n\tc := newControllerActivator(app, controller)\n\n\t// check the controller's \"BeforeActivation\" or/and \"AfterActivation\" method(s) between the `activate`\n\t// call, which is simply parses the controller's methods, end-dev can register custom controller's methods\n\t// by using the BeforeActivation's (a ControllerActivation) `.Handle` method.\n\tif before, ok := controller.(interface {\n\t\tBeforeActivation(BeforeActivation)\n\t}); ok {\n\t\tbefore.BeforeActivation(c)\n\t}\n\n\tfor _, opt := range options {\n\t\tif opt != nil {\n\t\t\topt.Apply(c)\n\t\t}\n\t}\n\n\tc.activate()\n\n\tif after, okAfter := controller.(interface {\n\t\tAfterActivation(AfterActivation)\n\t}); okAfter {\n\t\tafter.AfterActivation(c)\n\t}\n\n\tapp.Controllers = append(app.Controllers, c)\n\treturn c\n}\n\n// HandleError registers a `hero.ErrorHandlerFunc` which will be fired when\n// application's controllers' functions returns an non-nil error.\n// Each controller can override it by implementing the `hero.ErrorHandler`.\nfunc (app *Application) HandleError(handler func(ctx *context.Context, err error)) *Application {\n\terrorHandler := hero.ErrorHandlerFunc(handler)\n\tapp.container.GetErrorHandler = func(*context.Context) hero.ErrorHandler {\n\t\treturn errorHandler\n\t}\n\treturn app\n}\n\n// Clone returns a new mvc Application which has the dependencies\n// of the current mvc Application's `Dependencies` and its `ErrorHandler`.\n//\n// Example: `.Clone(app.Party(\"/path\")).Handle(new(TodoSubController))`.\nfunc (app *Application) Clone(party router.Party) *Application {\n\tcloned := newApp(party, app.container.Clone())\n\tcloned.controllersNoLog = app.controllersNoLog\n\treturn cloned\n}\n\n// Party returns a new child mvc Application based on the current path + \"relativePath\".\n// The new mvc Application has the same dependencies of the current mvc Application,\n// until otherwise specified later manually.\n//\n// The router's root path of this child will be the current mvc Application's root path + \"relativePath\".\nfunc (app *Application) Party(relativePath string, middleware ...context.Handler) *Application {\n\treturn app.Clone(app.Router.Party(relativePath, middleware...))\n}\n\nvar childNameReplacer = strings.NewReplacer(\"*\", \"\", \"(\", \"\", \")\", \"\")\n\nfunc getArrowSymbol(static bool, field bool) string {\n\tif field {\n\t\tif static {\n\t\t\treturn \"╺\"\n\t\t}\n\t\treturn \"⦿\"\n\n\t}\n\n\tif static {\n\t\treturn \"•\"\n\t}\n\n\treturn \"⦿\"\n}\n\n// TODO: instead of this I want to get in touch with tools like \"graphviz\"\n// so we can put all that information (and the API) inside web graphs,\n// it will be easier for developers to see the flow of the whole application,\n// but probalby I will never find time for that as we have higher priorities...just a reminder though.\nfunc logController(logger *golog.Logger, c *ControllerActivator) {\n\tif logger.Level != golog.DebugLevel {\n\t\treturn\n\t}\n\n\tif c.injector == nil { // when no actual controller methods are registered.\n\t\treturn\n\t}\n\n\t/*\n\t\t[DBUG] controller.GreetController\n\t\t  ╺ Service         → ./service/greet_service.go:16\n\t\t  ╺ Get\n\t\t      GET /greet\n\t\t\t• iris.Context\n\t\t\t• service.Other\t→ ./service/other_service.go:11\n\t*/\n\n\tbckpNewLine := logger.NewLine\n\tbckpTimeFormat := logger.TimeFormat\n\tlogger.NewLine = false\n\tlogger.TimeFormat = \"\"\n\n\tprinter := logger.Printer\n\treports := c.injector.Container.Reports\n\tctrlName := c.RelName()\n\tctrlScopeType := \"\"\n\tif !c.injector.Singleton {\n\t\tctrlScopeType = getArrowSymbol(false, false) + \" \"\n\t}\n\tlogger.Debugf(\"%s%s\\n\", ctrlScopeType, ctrlName)\n\n\tlongestNameLen := 0\n\tfor _, report := range reports {\n\t\tfor _, entry := range report.Entries {\n\t\t\tif n := len(entry.InputFieldName); n > longestNameLen {\n\t\t\t\tif strings.HasSuffix(entry.InputFieldName, ctrlName) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tlongestNameLen = n\n\t\t\t}\n\t\t}\n\t}\n\n\tlongestMethodName := 0\n\tfor methodName := range c.routes {\n\t\tif n := len(methodName); n > longestMethodName {\n\t\t\tlongestMethodName = n\n\t\t}\n\t}\n\n\tlastColorCode := -1\n\n\tfor _, report := range reports {\n\n\t\tchildName := childNameReplacer.Replace(report.Name)\n\t\tif idx := strings.Index(childName, c.Name()); idx >= 0 {\n\t\t\tchildName = childName[idx+len(c.Name()):] // it's always +1 otherwise should be reported as BUG.\n\t\t}\n\n\t\tif childName != \"\" && childName[0] == '.' {\n\t\t\t// It's a struct's method.\n\n\t\t\tchildName = childName[1:]\n\n\t\t\tfor _, route := range c.routes[childName] {\n\t\t\t\tif route.NoLog {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\t// Let them be logged again with the middlewares, e.g UseRouter or UseGlobal after this MVC app created.\n\t\t\t\t// route.NoLog = true\n\n\t\t\t\tcolorCode := router.TraceTitleColorCode(route.Method)\n\n\t\t\t\t// group same methods (or errors).\n\t\t\t\tif lastColorCode == -1 {\n\t\t\t\t\tlastColorCode = colorCode\n\t\t\t\t} else if lastColorCode != colorCode {\n\t\t\t\t\tlastColorCode = colorCode\n\t\t\t\t\tfmt.Fprintln(printer)\n\t\t\t\t}\n\n\t\t\t\tfmt.Fprint(printer, \"  ╺ \")\n\t\t\t\tpio.WriteRich(printer, childName, colorCode)\n\n\t\t\t\tentries := report.Entries[1:] // the ctrl value is always the first input argument so 1:..\n\t\t\t\tif len(entries) == 0 {\n\t\t\t\t\tfmt.Print(\"()\")\n\t\t\t\t}\n\t\t\t\tfmt.Fprintln(printer)\n\n\t\t\t\t// pio.WriteRich(printer, \"      \"+route.GetTitle(), colorCode)\n\t\t\t\tfmt.Fprintf(printer, \"      %s\\n\", route.String())\n\n\t\t\t\tfor _, entry := range entries {\n\t\t\t\t\tfileLine := \"\"\n\t\t\t\t\tif !strings.Contains(entry.DependencyFile, \"kataras/iris/\") {\n\t\t\t\t\t\tfileLine = fmt.Sprintf(\"→ %s:%d\", entry.DependencyFile, entry.DependencyLine)\n\t\t\t\t\t}\n\n\t\t\t\t\tfieldName := entry.InputFieldName\n\n\t\t\t\t\tspaceRequired := longestNameLen - len(fieldName)\n\t\t\t\t\tif spaceRequired < 0 {\n\t\t\t\t\t\tspaceRequired = 0\n\t\t\t\t\t}\n\t\t\t\t\t//    → ⊳ ↔\n\t\t\t\t\tfmt.Fprintf(printer, \"    • %s%s %s\\n\", fieldName, strings.Repeat(\" \", spaceRequired), fileLine)\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t// It's a struct's field.\n\t\t\tfor _, entry := range report.Entries {\n\t\t\t\tfileLine := \"\"\n\t\t\t\tif !strings.Contains(entry.DependencyFile, \"kataras/iris/\") {\n\t\t\t\t\tfileLine = fmt.Sprintf(\"→ %s:%d\", entry.DependencyFile, entry.DependencyLine)\n\t\t\t\t}\n\n\t\t\t\tfieldName := entry.InputFieldName\n\t\t\t\tspaceRequired := longestNameLen + 2 - len(fieldName) // plus the two spaces because it's not collapsed.\n\t\t\t\tif spaceRequired < 0 {\n\t\t\t\t\tspaceRequired = 0\n\t\t\t\t}\n\n\t\t\t\tarrowSymbol := getArrowSymbol(entry.Static, true)\n\t\t\t\tfmt.Fprintf(printer, \"  %s %s%s %s\\n\", arrowSymbol, fieldName, strings.Repeat(\" \", spaceRequired), fileLine)\n\t\t\t}\n\t\t}\n\t}\n\t// fmt.Fprintln(printer)\n\n\tlogger.NewLine = bckpNewLine\n\tlogger.TimeFormat = bckpTimeFormat\n}\n"
  },
  {
    "path": "mvc/reflect.go",
    "content": "package mvc\n\nimport (\n\t\"reflect\"\n\n\t\"github.com/kataras/iris/v12/context\"\n)\n\nvar baseControllerTyp = reflect.TypeOf((*BaseController)(nil)).Elem()\n\nfunc isBaseController(ctrlTyp reflect.Type) bool {\n\treturn ctrlTyp.Implements(baseControllerTyp)\n}\n\n// indirectType returns the value of a pointer-type \"typ\".\n// If \"typ\" is a pointer, array, chan, map or slice it returns its Elem,\n// otherwise returns the typ as it's.\nfunc indirectType(typ reflect.Type) reflect.Type {\n\tswitch typ.Kind() {\n\tcase reflect.Ptr, reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:\n\t\treturn typ.Elem()\n\t}\n\treturn typ\n}\n\nfunc getSourceFileLine(ctrlType reflect.Type, m reflect.Method) (string, int) { // used for debug logs.\n\tsourceFileName, sourceLineNumber := context.HandlerFileLineRel(m.Func)\n\tif sourceFileName == \"<autogenerated>\" {\n\t\telem := indirectType(ctrlType)\n\n\t\tfor i, n := 0, elem.NumField(); i < n; i++ {\n\t\t\tif f := elem.Field(i); f.Anonymous {\n\t\t\t\ttyp := indirectType(f.Type)\n\t\t\t\tif typ.Kind() != reflect.Struct {\n\t\t\t\t\tcontinue // field is not a struct.\n\t\t\t\t}\n\n\t\t\t\t// why we do that?\n\t\t\t\t// because if the element is not Ptr\n\t\t\t\t// then it's probably used as:\n\t\t\t\t// type ctrl {\n\t\t\t\t//   BaseCtrl\n\t\t\t\t// }\n\t\t\t\t// but BaseCtrl has not the method, *BaseCtrl does:\n\t\t\t\t// (c *BaseCtrl) HandleHTTPError(...)\n\t\t\t\t// so we are creating a new temporary value ptr of that type\n\t\t\t\t// and searching inside it for the method instead.\n\t\t\t\ttyp = reflect.New(typ).Type()\n\n\t\t\t\tif embeddedMethod, ok := typ.MethodByName(m.Name); ok {\n\t\t\t\t\tsourceFileName, sourceLineNumber = context.HandlerFileLineRel(embeddedMethod.Func)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn sourceFileName, sourceLineNumber\n}\n"
  },
  {
    "path": "mvc/versioning.go",
    "content": "package mvc\n\nimport (\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/core/router\"\n\t\"github.com/kataras/iris/v12/versioning\"\n)\n\n// Version returns a valid `Option` that can be passed to the `Application.Handle` method.\n// It requires a specific \"version\" constraint for a Controller,\n// e.g. \">1.0.0 <=2.0.0\".\n//\n// Usage:\n//\n//\tm := mvc.New(dataRouter)\n//\tm.Handle(new(v1Controller), mvc.Version(\"1.0.0\"), mvc.Deprecated(mvc.DeprecationOptions{}))\n//\tm.Handle(new(v2Controller), mvc.Version(\"2.3.0\"))\n//\tm.Handle(new(v3Controller), mvc.Version(\">=3.0.0 <4.0.0\"))\n//\tm.Handle(new(noVersionController))\n//\n// See the `versioning` package's documentation for more information on\n// how the version is extracted from incoming requests.\n//\n// Note that this Option will set the route register rule to `RouteOverlap`.\nfunc Version(version string) OptionFunc {\n\treturn func(c *ControllerActivator) {\n\t\tc.Router().SetRegisterRule(router.RouteOverlap) // required for this feature.\n\t\t// Note: Do not use a group, we need c.Use for the specific controller's routes.\n\t\tc.Use(versioning.Handler(version))\n\t}\n}\n\n// Deprecated marks a specific Controller as a deprecated one.\n// Deprecated can be used to tell the clients that\n// a newer version of that specific resource is available instead.\nfunc Deprecated(options DeprecationOptions) OptionFunc {\n\treturn func(c *ControllerActivator) {\n\t\tc.Use(func(ctx *context.Context) {\n\t\t\tversioning.WriteDeprecated(ctx, options)\n\t\t\tctx.Next()\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "sessions/config.go",
    "content": "package sessions\n\nimport (\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\n\t\"github.com/google/uuid\"\n\t\"github.com/kataras/golog\"\n)\n\nconst (\n\t// DefaultCookieName the secret cookie's name for sessions\n\tDefaultCookieName = \"irissessionid\"\n)\n\ntype (\n\t// Config is the configuration for sessions. Please read it before using sessions.\n\tConfig struct {\n\t\t// Logger instance for sessions usage, e.g. { Logger: app.Logger() }.\n\t\t// Defaults to a child of \"sessions\" of the latest Iris Application's main Logger.\n\t\tLogger *golog.Logger\n\t\t// Cookie string, the session's client cookie name, for example: \"mysessionid\"\n\t\t//\n\t\t// Defaults to \"irissessionid\".\n\t\tCookie string\n\n\t\t// CookieSecureTLS set to true if server is running over TLS\n\t\t// and you need the session's cookie \"Secure\" field to be set true.\n\t\t// Defaults to false.\n\t\tCookieSecureTLS bool\n\n\t\t// AllowReclaim will allow to\n\t\t// Destroy and Start a session in the same request handler.\n\t\t// All it does is that it removes the cookie for both `Request` and `ResponseWriter` while `Destroy`\n\t\t// or add a new cookie to `Request` while `Start`.\n\t\t//\n\t\t// Defaults to false.\n\t\tAllowReclaim bool\n\n\t\t// Encoding should encodes and decodes\n\t\t// authenticated and optionally encrypted cookie values.\n\t\t//\n\t\t// Defaults to nil.\n\t\tEncoding context.SecureCookie\n\n\t\t// Expires the duration of which the cookie must expires (created_time.Add(Expires)).\n\t\t// If you want to delete the cookie when the browser closes, set it to -1.\n\t\t// However, if you use a database storage setting this value to -1 may\n\t\t// cause you problems because of the fact that the database\n\t\t// may has its own expiration mechanism and value will be expired and removed immediately.\n\t\t//\n\t\t// 0 means no expire, (24 years)\n\t\t// -1 means when browser closes\n\t\t// > 0 is the time.Duration which the session cookies should expire.\n\t\t//\n\t\t// Defaults to infinitive/unlimited life duration(0).\n\t\tExpires time.Duration\n\n\t\t// SessionIDGenerator can be set to a function which\n\t\t// return a unique session id.\n\t\t// By default we will use a uuid impl package to generate\n\t\t// that, but developers can change that with simple assignment.\n\t\tSessionIDGenerator func(ctx *context.Context) string\n\n\t\t// DisableSubdomainPersistence set it to true in order dissallow your subdomains to have access to the session cookie\n\t\t//\n\t\t// Defaults to false.\n\t\tDisableSubdomainPersistence bool\n\t}\n)\n\n// Validate corrects missing fields configuration fields and returns the right configuration\nfunc (c Config) Validate() Config {\n\tif c.Logger == nil {\n\t\tc.Logger = context.DefaultLogger(\"sessions\")\n\t}\n\n\tif c.Cookie == \"\" {\n\t\tc.Cookie = DefaultCookieName\n\t}\n\n\tif c.SessionIDGenerator == nil {\n\t\tc.SessionIDGenerator = func(ctx *context.Context) string {\n\t\t\tid, err := uuid.NewRandom()\n\t\t\tif err != nil {\n\t\t\t\tctx.StopWithError(400, err)\n\t\t\t\treturn \"\"\n\t\t\t}\n\n\t\t\treturn id.String()\n\t\t}\n\t}\n\n\treturn c\n}\n"
  },
  {
    "path": "sessions/database.go",
    "content": "package sessions\n\nimport (\n\t\"errors\"\n\t\"reflect\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/core/memstore\"\n\n\t\"github.com/kataras/golog\"\n)\n\n// ErrNotImplemented is returned when a particular feature is not yet implemented yet.\n// It can be matched directly, i.e: `isNotImplementedError := sessions.ErrNotImplemented.Equal(err)`.\nvar ErrNotImplemented = errors.New(\"not implemented yet\")\n\n// Database is the interface which all session databases should implement\n// By design it doesn't support any type of cookie session like other frameworks.\n// I want to protect you, believe me.\n// The scope of the database is to store somewhere the sessions in order to\n// keep them after restarting the server, nothing more.\n//\n// Synchronization are made automatically, you can register one using `UseDatabase`.\n//\n// Look the `sessiondb` folder for databases implementations.\ntype Database interface {\n\t// SetLogger should inject a logger to this Database.\n\tSetLogger(*golog.Logger)\n\t// Acquire receives a session's lifetime from the database,\n\t// if the return value is LifeTime{} then the session manager sets the life time based on the expiration duration lives in configuration.\n\tAcquire(sid string, expires time.Duration) memstore.LifeTime\n\t// OnUpdateExpiration should re-set the expiration (ttl) of the session entry inside the database,\n\t// it is fired on `ShiftExpiration` and `UpdateExpiration`.\n\t// If the database does not support change of ttl then the session entry will be cloned to another one\n\t// and the old one will be removed, it depends on the chosen database storage.\n\t//\n\t// Check of error is required, if error returned then the rest session's keys are not proceed.\n\t//\n\t// If a database does not support this feature then an `ErrNotImplemented` will be returned instead.\n\tOnUpdateExpiration(sid string, newExpires time.Duration) error\n\t// Set sets a key value of a specific session.\n\t// The \"immutable\" input argument depends on the store, it may not implement it at all.\n\tSet(sid string, key string, value any, ttl time.Duration, immutable bool) error\n\t// Get retrieves a session value based on the key.\n\tGet(sid string, key string) any\n\t// Decode binds the \"outPtr\" to the value associated to the provided \"key\".\n\tDecode(sid, key string, outPtr any) error\n\t// Visit loops through all session keys and values.\n\tVisit(sid string, cb func(key string, value any)) error\n\t// Len returns the length of the session's entries (keys).\n\tLen(sid string) int\n\t// Delete removes a session key value based on its key.\n\tDelete(sid string, key string) (deleted bool)\n\t// Clear removes all session key values but it keeps the session entry.\n\tClear(sid string) error\n\t// Release destroys the session, it clears and removes the session entry,\n\t// session manager will create a new session ID on the next request after this call.\n\tRelease(sid string) error\n\t// Close should terminate the database connection. It's called automatically on interrupt signals.\n\tClose() error\n}\n\n// DatabaseRequestHandler is an optional interface that a sessions database\n// can implement. It contains a single EndRequest method which is fired\n// on the very end of the request life cycle. It should be used to Flush\n// any local session's values to the client.\ntype DatabaseRequestHandler interface {\n\tEndRequest(ctx *context.Context, session *Session)\n}\n\ntype mem struct {\n\tvalues map[string]*memstore.Store\n\tmu     sync.RWMutex\n}\n\nvar _ Database = (*mem)(nil)\n\nfunc newMemDB() Database { return &mem{values: make(map[string]*memstore.Store)} }\n\nfunc (s *mem) SetLogger(*golog.Logger) {}\n\nfunc (s *mem) Acquire(sid string, expires time.Duration) memstore.LifeTime {\n\ts.mu.Lock()\n\ts.values[sid] = new(memstore.Store)\n\ts.mu.Unlock()\n\treturn memstore.LifeTime{}\n}\n\n// Do nothing, the `LifeTime` of the Session will be managed by the callers automatically on memory-based storage.\nfunc (s *mem) OnUpdateExpiration(string, time.Duration) error { return nil }\n\n// immutable depends on the store, it may not implement it at all.\nfunc (s *mem) Set(sid string, key string, value any, _ time.Duration, immutable bool) error {\n\ts.mu.RLock()\n\tstore, ok := s.values[sid]\n\ts.mu.RUnlock()\n\tif ok {\n\t\tstore.Save(key, value, immutable)\n\t}\n\n\treturn nil\n}\n\nfunc (s *mem) Get(sid string, key string) any {\n\ts.mu.RLock()\n\tstore, ok := s.values[sid]\n\ts.mu.RUnlock()\n\tif ok {\n\t\treturn store.Get(key)\n\t}\n\n\treturn nil\n}\n\nfunc (s *mem) Decode(sid string, key string, outPtr any) error {\n\tv := s.Get(sid, key)\n\tif v != nil {\n\t\treflect.ValueOf(outPtr).Set(reflect.ValueOf(v))\n\t}\n\treturn nil\n}\n\nfunc (s *mem) Visit(sid string, cb func(key string, value any)) error {\n\ts.mu.RLock()\n\tstore, ok := s.values[sid]\n\ts.mu.RUnlock()\n\tif ok {\n\t\tstore.Visit(cb)\n\t}\n\n\treturn nil\n}\n\nfunc (s *mem) Len(sid string) int {\n\ts.mu.RLock()\n\tstore, ok := s.values[sid]\n\ts.mu.RUnlock()\n\tif ok {\n\t\treturn store.Len()\n\t}\n\n\treturn 0\n}\n\nfunc (s *mem) Delete(sid string, key string) (deleted bool) {\n\ts.mu.RLock()\n\tstore, ok := s.values[sid]\n\ts.mu.RUnlock()\n\tif ok {\n\t\tdeleted = store.Remove(key)\n\t}\n\n\treturn\n}\n\nfunc (s *mem) Clear(sid string) error {\n\ts.mu.RLock()\n\tstore, ok := s.values[sid]\n\ts.mu.RUnlock()\n\tif ok {\n\t\tstore.Reset()\n\t}\n\n\treturn nil\n}\n\nfunc (s *mem) Release(sid string) error {\n\ts.mu.Lock()\n\tdelete(s.values, sid)\n\ts.mu.Unlock()\n\treturn nil\n}\n\nfunc (s *mem) Close() error { return nil }\n"
  },
  {
    "path": "sessions/provider.go",
    "content": "package sessions\n\nimport (\n\t\"errors\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/context\"\n)\n\ntype (\n\t// provider contains the sessions and external databases (load and update).\n\t// It's the session memory manager\n\tprovider struct {\n\t\tmu               sync.RWMutex\n\t\tsessions         map[string]*Session\n\t\tdb               Database\n\t\tdbRequestHandler DatabaseRequestHandler\n\t\tdestroyListeners []DestroyListener\n\t}\n)\n\n// newProvider returns a new sessions provider\nfunc newProvider() *provider {\n\tp := &provider{\n\t\tsessions: make(map[string]*Session),\n\t\tdb:       newMemDB(),\n\t}\n\n\treturn p\n}\n\n// RegisterDatabase sets a session database.\nfunc (p *provider) RegisterDatabase(db Database) {\n\tif db == nil {\n\t\treturn\n\t}\n\n\tp.mu.Lock() // for any case\n\tp.db = db\n\tif dbreq, ok := db.(DatabaseRequestHandler); ok {\n\t\tp.dbRequestHandler = dbreq\n\t}\n\tp.mu.Unlock()\n}\n\n// newSession returns a new session from sessionid\nfunc (p *provider) newSession(man *Sessions, sid string, expires time.Duration) *Session {\n\tsess := &Session{\n\t\tsid:      sid,\n\t\tMan:      man,\n\t\tprovider: p,\n\t}\n\n\tonExpire := func() {\n\t\tp.mu.Lock()\n\t\tp.deleteSession(sess)\n\t\tp.mu.Unlock()\n\t}\n\n\tlifetime := p.db.Acquire(sid, expires)\n\n\t// simple and straight:\n\tif !lifetime.IsZero() {\n\t\t// if stored time is not zero\n\t\t// start a timer based on the stored time, if not expired.\n\t\tlifetime.Revive(onExpire)\n\t} else {\n\t\t// Remember:  if db not exist or it has been expired\n\t\t// then the stored time will be zero(see loadSessionFromDB) and the values will be empty.\n\t\t//\n\t\t// Even if the database has an unlimited session (possible by a previous app run)\n\t\t// priority to the \"expires\" is given,\n\t\t// again if <=0 then it does nothing.\n\t\tlifetime.Begin(expires, onExpire)\n\t}\n\n\tsess.Lifetime = &lifetime\n\treturn sess\n}\n\n// Init creates the session  and returns it\nfunc (p *provider) Init(man *Sessions, sid string, expires time.Duration) *Session {\n\tnewSession := p.newSession(man, sid, expires)\n\tnewSession.isNew = true\n\tp.mu.Lock()\n\tp.sessions[sid] = newSession\n\tp.mu.Unlock()\n\treturn newSession\n}\n\nfunc (p *provider) EndRequest(ctx *context.Context, session *Session) {\n\tif p.dbRequestHandler != nil {\n\t\tp.dbRequestHandler.EndRequest(ctx, session)\n\t}\n}\n\n// ErrNotFound may be returned from `UpdateExpiration` of a non-existing or\n// invalid session entry from memory storage or databases.\n// Usage:\n//\n//\tif err != nil && err.Is(err, sessions.ErrNotFound) {\n//\t    [handle error...]\n//\t}\nvar ErrNotFound = errors.New(\"session not found\")\n\n// UpdateExpiration resets the expiration of a session.\n// if expires > 0 then it will try to update the expiration and destroy task is delayed.\n// if expires <= 0 then it does nothing it returns nil, to destroy a session call the `Destroy` func instead.\n//\n// If the session is not found, it returns a `NotFound` error,  this can only happen when you restart the server and you used the memory-based storage(default),\n// because the call of the provider's `UpdateExpiration` is always called when the client has a valid session cookie.\n//\n// If a backend database is used then it may return an `ErrNotImplemented` error if the underline database does not support this operation.\nfunc (p *provider) UpdateExpiration(sid string, expires time.Duration) error {\n\tif expires <= 0 {\n\t\treturn nil\n\t}\n\n\tp.mu.RLock()\n\tsess, found := p.sessions[sid]\n\tp.mu.RUnlock()\n\tif !found {\n\t\treturn ErrNotFound\n\t}\n\n\tsess.Lifetime.Shift(expires)\n\treturn p.db.OnUpdateExpiration(sid, expires)\n}\n\n// Read returns the store which sid parameter belongs\nfunc (p *provider) Read(man *Sessions, sid string, expires time.Duration) *Session {\n\tp.mu.RLock()\n\tsess, found := p.sessions[sid]\n\tp.mu.RUnlock()\n\tif found {\n\t\tsess.mu.Lock()\n\t\tsess.isNew = false\n\t\tsess.mu.Unlock()\n\t\tsess.runFlashGC() // run the flash messages GC, new request here of existing session\n\n\t\treturn sess\n\t}\n\n\treturn p.Init(man, sid, expires) // if not found create new\n}\n\nfunc (p *provider) registerDestroyListener(ln DestroyListener) {\n\tif ln == nil {\n\t\treturn\n\t}\n\tp.destroyListeners = append(p.destroyListeners, ln)\n}\n\nfunc (p *provider) fireDestroy(sid string) {\n\tfor _, ln := range p.destroyListeners {\n\t\tln(sid)\n\t}\n}\n\n// Destroy destroys the session, removes all sessions and flash values,\n// the session itself and updates the registered session databases,\n// this called from sessionManager which removes the client's cookie also.\nfunc (p *provider) Destroy(sid string) {\n\tp.mu.Lock()\n\tif sess, found := p.sessions[sid]; found {\n\t\tp.deleteSession(sess)\n\t}\n\tp.mu.Unlock()\n}\n\n// DestroyAll removes all sessions\n// from the server-side memory (and database if registered).\n// Client's session cookie will still exist but it will be reseted on the next request.\nfunc (p *provider) DestroyAll() {\n\tp.mu.Lock()\n\tfor _, sess := range p.sessions {\n\t\tp.deleteSession(sess)\n\t}\n\tp.mu.Unlock()\n}\n\nfunc (p *provider) deleteSession(sess *Session) {\n\tsid := sess.sid\n\n\tdelete(p.sessions, sid)\n\tp.db.Release(sid)\n\tp.fireDestroy(sid)\n}\n\n/*\nfunc (p *provider) regenerateID(ctx *context.Context, oldsid string) {\n\tp.mu.RLock()\n\tsess, ok := p.sessions[oldsid]\n\tp.mu.RUnlock()\n\n\tif ok {\n\t\tnewsid := sess.Man.config.SessionIDGenerator(ctx)\n\t\tsess.mu.Lock()\n\t\tsess.sid = newsid\n\t\tsess.mu.Unlock()\n\n\t\tp.mu.Lock()\n\t\tp.sessions[newsid] = sess\n\t\tdelete(p.sessions, oldsid)\n\t\tp.mu.Unlock()\n\t}\n}\n*/\n"
  },
  {
    "path": "sessions/session.go",
    "content": "package sessions\n\nimport (\n\t\"reflect\"\n\t\"strconv\"\n\t\"sync\"\n\n\t\"github.com/kataras/iris/v12/core/memstore\"\n)\n\ntype (\n\t// Session should expose the Sessions's end-user API.\n\t// It is the session's storage controller which you can\n\t// save or retrieve values based on a key.\n\t//\n\t// This is what will be returned when sess := sessions.Start().\n\tSession struct {\n\t\tsid     string\n\t\tisNew   bool\n\t\tflashes map[string]*flashMessage\n\t\tmu      sync.RWMutex // for flashes.\n\t\t// Lifetime it contains the expiration data, use it for read-only information.\n\t\t// See `Sessions.UpdateExpiration` too.\n\t\tLifetime *memstore.LifeTime\n\t\t// Man is the sessions manager that this session created of.\n\t\tMan *Sessions\n\n\t\tprovider *provider\n\t}\n\n\tflashMessage struct {\n\t\t// if true then this flash message is removed on the flash gc\n\t\tshouldRemove bool\n\t\tvalue        any\n\t}\n)\n\n// Destroy destroys this session, it removes its session values and any flashes.\n// This session entry will be removed from the server,\n// the registered session databases will be notified for this deletion as well.\n//\n// Note that this method does NOT remove the client's cookie, although\n// it should be reseted if new session is attached to that (client).\n//\n// Use the session's manager `Destroy(ctx)` in order to remove the cookie instead.\nfunc (s *Session) Destroy() {\n\ts.provider.Destroy(s.sid)\n}\n\n// ID returns the session's ID.\nfunc (s *Session) ID() string {\n\treturn s.sid\n}\n\n// IsNew returns true if this session is just\n// created by the current application's process.\nfunc (s *Session) IsNew() bool {\n\treturn s.isNew\n}\n\n// Get returns a value based on its \"key\".\nfunc (s *Session) Get(key string) any {\n\treturn s.provider.db.Get(s.sid, key)\n}\n\n// Decode binds the given \"outPtr\" to the value associated to the provided \"key\".\nfunc (s *Session) Decode(key string, outPtr any) error {\n\treturn s.provider.db.Decode(s.sid, key, outPtr)\n}\n\n// when running on the session manager removes any 'old' flash messages.\nfunc (s *Session) runFlashGC() {\n\ts.mu.Lock()\n\tfor key, v := range s.flashes {\n\t\tif v.shouldRemove {\n\t\t\tdelete(s.flashes, key)\n\t\t}\n\t}\n\n\ts.mu.Unlock()\n}\n\n// HasFlash returns true if this session has available flash messages.\nfunc (s *Session) HasFlash() bool {\n\ts.mu.RLock()\n\thas := len(s.flashes) > 0\n\ts.mu.RUnlock()\n\treturn has\n}\n\n// GetFlash returns a stored flash message based on its \"key\"\n// which will be removed on the next request.\n//\n// To check for flash messages we use the HasFlash() Method\n// and to obtain the flash message we use the GetFlash() Method.\n// There is also a method GetFlashes() to fetch all the messages.\n//\n// Fetching a message deletes it from the session.\n// This means that a message is meant to be displayed only on the first page served to the user.\nfunc (s *Session) GetFlash(key string) any {\n\tfv, ok := s.peekFlashMessage(key)\n\tif !ok {\n\t\treturn nil\n\t}\n\tfv.shouldRemove = true\n\treturn fv.value\n}\n\n// PeekFlash returns a stored flash message based on its \"key\".\n// Unlike GetFlash, this will keep the message valid for the next requests,\n// until GetFlashes or GetFlash(\"key\").\nfunc (s *Session) PeekFlash(key string) any {\n\tfv, ok := s.peekFlashMessage(key)\n\tif !ok {\n\t\treturn nil\n\t}\n\treturn fv.value\n}\n\nfunc (s *Session) peekFlashMessage(key string) (*flashMessage, bool) {\n\ts.mu.RLock()\n\tfv, found := s.flashes[key]\n\ts.mu.RUnlock()\n\n\tif !found {\n\t\treturn nil, false\n\t}\n\n\treturn fv, true\n}\n\n// GetString same as Get but returns its string representation,\n// if key doesn't exist then it returns an empty string.\nfunc (s *Session) GetString(key string) string {\n\tif value := s.Get(key); value != nil {\n\t\tif v, ok := value.(string); ok {\n\t\t\treturn v\n\t\t}\n\n\t\tif v, ok := value.(int); ok {\n\t\t\treturn strconv.Itoa(v)\n\t\t}\n\n\t\tif v, ok := value.(int64); ok {\n\t\t\treturn strconv.FormatInt(v, 10)\n\t\t}\n\t}\n\n\treturn \"\"\n}\n\n// GetStringDefault same as Get but returns its string representation,\n// if key doesn't exist then it returns the \"defaultValue\".\nfunc (s *Session) GetStringDefault(key string, defaultValue string) string {\n\tif v := s.GetString(key); v != \"\" {\n\t\treturn v\n\t}\n\n\treturn defaultValue\n}\n\n// GetFlashString same as `GetFlash` but returns its string representation,\n// if key doesn't exist then it returns an empty string.\nfunc (s *Session) GetFlashString(key string) string {\n\treturn s.GetFlashStringDefault(key, \"\")\n}\n\n// GetFlashStringDefault same as `GetFlash` but returns its string representation,\n// if key doesn't exist then it returns the \"defaultValue\".\nfunc (s *Session) GetFlashStringDefault(key string, defaultValue string) string {\n\tif value := s.GetFlash(key); value != nil {\n\t\tif v, ok := value.(string); ok {\n\t\t\treturn v\n\t\t}\n\t}\n\n\treturn defaultValue\n}\n\n// ErrEntryNotFound similar to core/memstore#ErrEntryNotFound but adds\n// the value (if found) matched to the requested key-value pair of the session's memory storage.\ntype ErrEntryNotFound struct {\n\tErr   *memstore.ErrEntryNotFound\n\tValue any\n}\n\nfunc (e *ErrEntryNotFound) Error() string {\n\treturn e.Err.Error()\n}\n\n// Unwrap method implements the dynamic Unwrap interface of the std errors package.\nfunc (e *ErrEntryNotFound) Unwrap() error {\n\treturn e.Err\n}\n\n// As method implements the dynamic As interface of the std errors package.\n// As should be NOT used directly, use `errors.As` instead.\nfunc (e *ErrEntryNotFound) As(target any) bool {\n\tif v, ok := target.(*memstore.ErrEntryNotFound); ok && e.Err != nil {\n\t\treturn e.Err.As(v)\n\t}\n\n\tv, ok := target.(*ErrEntryNotFound)\n\tif !ok {\n\t\treturn false\n\t}\n\n\tif v.Value != nil {\n\t\tif v.Value != e.Value {\n\t\t\treturn false\n\t\t}\n\t}\n\n\tif v.Err != nil {\n\t\tif e.Err != nil {\n\t\t\treturn e.Err.As(v.Err)\n\t\t}\n\n\t\treturn false\n\t}\n\n\treturn true\n}\n\nfunc newErrEntryNotFound(key string, kind reflect.Kind, value any) *ErrEntryNotFound {\n\treturn &ErrEntryNotFound{Err: &memstore.ErrEntryNotFound{Key: key, Kind: kind}, Value: value}\n}\n\n// GetInt same as `Get` but returns its int representation,\n// if key doesn't exist then it returns -1 and a non-nil error.\nfunc (s *Session) GetInt(key string) (int, error) {\n\tv := s.Get(key)\n\n\tif v != nil {\n\t\tif vint, ok := v.(int); ok {\n\t\t\treturn vint, nil\n\t\t}\n\n\t\tif vfloat64, ok := v.(float64); ok {\n\t\t\treturn int(vfloat64), nil\n\t\t}\n\n\t\tif vint64, ok := v.(int64); ok {\n\t\t\treturn int(vint64), nil\n\t\t}\n\n\t\tif vstring, sok := v.(string); sok {\n\t\t\treturn strconv.Atoi(vstring)\n\t\t}\n\t}\n\n\treturn -1, newErrEntryNotFound(key, reflect.Int, v)\n}\n\n// GetIntDefault same as `Get` but returns its int representation,\n// if key doesn't exist then it returns the \"defaultValue\".\nfunc (s *Session) GetIntDefault(key string, defaultValue int) int {\n\tif v, err := s.GetInt(key); err == nil {\n\t\treturn v\n\t}\n\treturn defaultValue\n}\n\n// Increment increments the stored int value saved as \"key\" by +\"n\".\n// If value doesn't exist on that \"key\" then it creates one with the \"n\" as its value.\n// It returns the new, incremented, value.\nfunc (s *Session) Increment(key string, n int) (newValue int) {\n\tnewValue = s.GetIntDefault(key, 0)\n\tnewValue += n\n\ts.Set(key, newValue)\n\treturn\n}\n\n// Decrement decrements the stored int value saved as \"key\" by -\"n\".\n// If value doesn't exist on that \"key\" then it creates one with the \"n\" as its value.\n// It returns the new, decremented, value even if it's less than zero.\nfunc (s *Session) Decrement(key string, n int) (newValue int) {\n\tnewValue = s.GetIntDefault(key, 0)\n\tnewValue -= n\n\ts.Set(key, newValue)\n\treturn\n}\n\n// GetInt64 same as `Get` but returns its int64 representation,\n// if key doesn't exist then it returns -1 and a non-nil error.\nfunc (s *Session) GetInt64(key string) (int64, error) {\n\tv := s.Get(key)\n\tif v != nil {\n\t\tif vint64, ok := v.(int64); ok {\n\t\t\treturn vint64, nil\n\t\t}\n\n\t\tif vfloat64, ok := v.(float64); ok {\n\t\t\treturn int64(vfloat64), nil\n\t\t}\n\n\t\tif vint, ok := v.(int); ok {\n\t\t\treturn int64(vint), nil\n\t\t}\n\n\t\tif vstring, sok := v.(string); sok {\n\t\t\treturn strconv.ParseInt(vstring, 10, 64)\n\t\t}\n\t}\n\n\treturn -1, newErrEntryNotFound(key, reflect.Int64, v)\n}\n\n// GetInt64Default same as `Get` but returns its int64 representation,\n// if key doesn't exist it returns the \"defaultValue\".\nfunc (s *Session) GetInt64Default(key string, defaultValue int64) int64 {\n\tif v, err := s.GetInt64(key); err == nil {\n\t\treturn v\n\t}\n\n\treturn defaultValue\n}\n\n// GetUint64 same as `Get` but returns as uint64,\n// if key doesn't exist then it returns 0 and a non-nil error.\nfunc (s *Session) GetUint64(key string) (uint64, error) {\n\tv := s.Get(key)\n\tif v != nil {\n\t\tswitch vv := v.(type) {\n\t\tcase string:\n\t\t\tval, err := strconv.ParseUint(vv, 10, 64)\n\t\t\tif err != nil {\n\t\t\t\treturn 0, err\n\t\t\t}\n\t\t\treturn uint64(val), nil\n\t\tcase uint8:\n\t\t\treturn uint64(vv), nil\n\t\tcase uint16:\n\t\t\treturn uint64(vv), nil\n\t\tcase uint32:\n\t\t\treturn uint64(vv), nil\n\t\tcase uint64:\n\t\t\treturn vv, nil\n\t\tcase int64:\n\t\t\treturn uint64(vv), nil\n\t\tcase int:\n\t\t\treturn uint64(vv), nil\n\t\t}\n\t}\n\n\treturn 0, newErrEntryNotFound(key, reflect.Uint64, v)\n}\n\n// GetUint64Default same as `Get` but returns as uint64,\n// if key doesn't exist it returns the \"defaultValue\".\nfunc (s *Session) GetUint64Default(key string, defaultValue uint64) uint64 {\n\tif v, err := s.GetUint64(key); err == nil {\n\t\treturn v\n\t}\n\n\treturn defaultValue\n}\n\n// GetFloat32 same as `Get` but returns its float32 representation,\n// if key doesn't exist then it returns -1 and a non-nil error.\nfunc (s *Session) GetFloat32(key string) (float32, error) {\n\tv := s.Get(key)\n\n\tif vfloat32, ok := v.(float32); ok {\n\t\treturn vfloat32, nil\n\t}\n\n\tif vfloat64, ok := v.(float64); ok {\n\t\treturn float32(vfloat64), nil\n\t}\n\n\tif vint, ok := v.(int); ok {\n\t\treturn float32(vint), nil\n\t}\n\n\tif vint64, ok := v.(int64); ok {\n\t\treturn float32(vint64), nil\n\t}\n\n\tif vstring, sok := v.(string); sok {\n\t\tvfloat64, err := strconv.ParseFloat(vstring, 32)\n\t\tif err != nil {\n\t\t\treturn -1, err\n\t\t}\n\t\treturn float32(vfloat64), nil\n\t}\n\n\treturn -1, newErrEntryNotFound(key, reflect.Float32, v)\n}\n\n// GetFloat32Default same as `Get` but returns its float32 representation,\n// if key doesn't exist then it returns the \"defaultValue\".\nfunc (s *Session) GetFloat32Default(key string, defaultValue float32) float32 {\n\tif v, err := s.GetFloat32(key); err == nil {\n\t\treturn v\n\t}\n\n\treturn defaultValue\n}\n\n// GetFloat64 same as `Get` but returns its float64 representation,\n// if key doesn't exist then it returns -1 and a non-nil error.\nfunc (s *Session) GetFloat64(key string) (float64, error) {\n\tv := s.Get(key)\n\n\tif vfloat32, ok := v.(float32); ok {\n\t\treturn float64(vfloat32), nil\n\t}\n\n\tif vfloat64, ok := v.(float64); ok {\n\t\treturn vfloat64, nil\n\t}\n\n\tif vint, ok := v.(int); ok {\n\t\treturn float64(vint), nil\n\t}\n\n\tif vint64, ok := v.(int64); ok {\n\t\treturn float64(vint64), nil\n\t}\n\n\tif vstring, sok := v.(string); sok {\n\t\treturn strconv.ParseFloat(vstring, 32)\n\t}\n\n\treturn -1, newErrEntryNotFound(key, reflect.Float64, v)\n}\n\n// GetFloat64Default same as `Get` but returns its float64 representation,\n// if key doesn't exist then it returns the \"defaultValue\".\nfunc (s *Session) GetFloat64Default(key string, defaultValue float64) float64 {\n\tif v, err := s.GetFloat64(key); err == nil {\n\t\treturn v\n\t}\n\n\treturn defaultValue\n}\n\n// GetBoolean same as `Get` but returns its boolean representation,\n// if key doesn't exist then it returns false and a non-nil error.\nfunc (s *Session) GetBoolean(key string) (bool, error) {\n\tv := s.Get(key)\n\tif v == nil {\n\t\treturn false, newErrEntryNotFound(key, reflect.Bool, nil)\n\t}\n\n\t// here we could check for \"true\", \"false\" and 0 for false and 1 for true\n\t// but this may cause unexpected behavior from the developer if they expecting an error\n\t// so we just check if bool, if yes then return that bool, otherwise return false and an error.\n\tif vb, ok := v.(bool); ok {\n\t\treturn vb, nil\n\t}\n\tif vstring, ok := v.(string); ok {\n\t\treturn strconv.ParseBool(vstring)\n\t}\n\n\treturn false, newErrEntryNotFound(key, reflect.Bool, v)\n}\n\n// GetBooleanDefault same as `Get` but returns its boolean representation,\n// if key doesn't exist then it returns the \"defaultValue\".\nfunc (s *Session) GetBooleanDefault(key string, defaultValue bool) bool {\n\t/*\n\t\tNote that here we can't do more than duplicate the GetBoolean's code, because of the \"false\".\n\t*/\n\tv := s.Get(key)\n\tif v == nil {\n\t\treturn defaultValue\n\t}\n\n\t// here we could check for \"true\", \"false\" and 0 for false and 1 for true\n\t// but this may cause unexpected behavior from the developer if they expecting an error\n\t// so we just check if bool, if yes then return that bool, otherwise return false and an error.\n\tif vb, ok := v.(bool); ok {\n\t\treturn vb\n\t}\n\n\tif vstring, ok := v.(string); ok {\n\t\tif b, err := strconv.ParseBool(vstring); err == nil {\n\t\t\treturn b\n\t\t}\n\t}\n\n\treturn defaultValue\n}\n\n// GetAll returns a copy of all session's values.\nfunc (s *Session) GetAll() map[string]any {\n\titems := make(map[string]any, s.provider.db.Len(s.sid))\n\ts.mu.RLock()\n\ts.provider.db.Visit(s.sid, func(key string, value any) {\n\t\titems[key] = value\n\t})\n\ts.mu.RUnlock()\n\treturn items\n}\n\n// GetFlashes returns all flash messages as map[string](key) and any value\n// NOTE: this will cause at remove all current flash messages on the next request of the same user.\nfunc (s *Session) GetFlashes() map[string]any {\n\tflashes := make(map[string]any, len(s.flashes))\n\ts.mu.Lock()\n\tfor key, v := range s.flashes {\n\t\tflashes[key] = v.value\n\t\tv.shouldRemove = true\n\t}\n\ts.mu.Unlock()\n\treturn flashes\n}\n\n// Visit loops each of the entries and calls the callback function func(key, value).\nfunc (s *Session) Visit(cb func(k string, v any)) {\n\ts.provider.db.Visit(s.sid, cb)\n}\n\n// Len returns the total number of stored values in this session.\nfunc (s *Session) Len() int {\n\treturn s.provider.db.Len(s.sid)\n}\n\nfunc (s *Session) set(key string, value any, immutable bool) {\n\ts.provider.db.Set(s.sid, key, value, s.Lifetime.DurationUntilExpiration(), immutable)\n}\n\n// Set fills the session with an entry \"value\", based on its \"key\".\nfunc (s *Session) Set(key string, value any) {\n\ts.set(key, value, false)\n}\n\n// SetImmutable fills the session with an entry \"value\", based on its \"key\".\n// Unlike `Set`, the output value cannot be changed by the caller later on (when .Get)\n// An Immutable entry should be only changed with a `SetImmutable`, simple `Set` will not work\n// if the entry was immutable, for your own safety.\n// Use it consistently, it's far slower than `Set`.\n// Read more about muttable and immutable go types: https://stackoverflow.com/a/8021081\nfunc (s *Session) SetImmutable(key string, value any) {\n\ts.set(key, value, true)\n}\n\n// SetFlash sets a flash message by its key.\n//\n// A flash message is used in order to keep a message in session through one or several requests of the same user.\n// It is removed from session after it has been displayed to the user.\n// Flash messages are usually used in combination with HTTP redirections,\n// because in this case there is no view, so messages can only be displayed in the request that follows redirection.\n//\n// A flash message has a name and a content (AKA key and value).\n// It is an entry of an associative array. The name is a string: often \"notice\", \"success\", or \"error\", but it can be anything.\n// The content is usually a string. You can put HTML tags in your message if you display it raw.\n// You can also set the message value to a number or an array: it will be serialized and kept in session like a string.\n//\n// Flash messages can be set using the SetFlash() Method\n// For example, if you would like to inform the user that his changes were successfully saved,\n// you could add the following line to your Handler:\n//\n// SetFlash(\"success\", \"Data saved!\");\n//\n// In this example we used the key 'success'.\n// If you want to define more than one flash messages, you will have to use different keys.\nfunc (s *Session) SetFlash(key string, value any) {\n\ts.mu.Lock()\n\tif s.flashes == nil {\n\t\ts.flashes = make(map[string]*flashMessage)\n\t}\n\n\ts.flashes[key] = &flashMessage{value: value}\n\ts.mu.Unlock()\n}\n\n// Delete removes an entry by its key,\n// returns true if actually something was removed.\nfunc (s *Session) Delete(key string) bool {\n\tremoved := s.provider.db.Delete(s.sid, key)\n\treturn removed\n}\n\n// DeleteFlash removes a flash message by its key.\nfunc (s *Session) DeleteFlash(key string) {\n\ts.mu.Lock()\n\tdelete(s.flashes, key)\n\ts.mu.Unlock()\n}\n\n// Clear removes all entries.\nfunc (s *Session) Clear() {\n\ts.provider.db.Clear(s.sid)\n}\n\n// ClearFlashes removes all flash messages.\nfunc (s *Session) ClearFlashes() {\n\ts.mu.Lock()\n\tfor key := range s.flashes {\n\t\tdelete(s.flashes, key)\n\t}\n\ts.mu.Unlock()\n}\n"
  },
  {
    "path": "sessions/sessiondb/badger/database.go",
    "content": "package badger\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"os\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/core/memstore\"\n\t\"github.com/kataras/iris/v12/sessions\"\n\n\t\"github.com/dgraph-io/badger/v4\"\n\t\"github.com/kataras/golog\"\n)\n\n// DefaultFileMode used as the default database's \"fileMode\"\n// for creating the sessions directory path, opening and write the session file.\nvar (\n\tDefaultFileMode = 0755\n)\n\n// Database the badger(key-value file-based) session storage.\ntype Database struct {\n\t// Service is the underline badger database connection,\n\t// it's initialized at `New` or `NewFromDB`.\n\t// Can be used to get stats.\n\tService *badger.DB\n\tlogger  *golog.Logger\n\n\tclosed uint32 // if 1 is closed.\n}\n\nvar _ sessions.Database = (*Database)(nil)\n\n// New creates and returns a new badger(key-value file-based) storage\n// instance based on the \"directoryPath\".\n// DirectoryPath should is the directory which the badger database will store the sessions,\n// i.e ./sessions\n//\n// It will remove any old session files.\nfunc New(directoryPath string) (*Database, error) {\n\tif directoryPath == \"\" {\n\t\treturn nil, errors.New(\"directoryPath is empty\")\n\t}\n\n\tlindex := directoryPath[len(directoryPath)-1]\n\tif lindex != os.PathSeparator && lindex != '/' {\n\t\tdirectoryPath += string(os.PathSeparator)\n\t}\n\t// create directories if necessary\n\tif err := os.MkdirAll(directoryPath, os.FileMode(DefaultFileMode)); err != nil {\n\t\treturn nil, err\n\t}\n\n\topts := badger.DefaultOptions(directoryPath)\n\tbadgerLogger := context.DefaultLogger(\"sessionsdb.badger\").DisableNewLine()\n\topts.Logger = badgerLogger\n\n\tservice, err := badger.Open(opts)\n\tif err != nil {\n\t\tbadgerLogger.Errorf(\"unable to initialize the badger-based session database: %v\\n\", err)\n\t\treturn nil, err\n\t}\n\n\treturn NewFromDB(service), nil\n}\n\n// NewFromDB same as `New` but accepts an already-created custom badger connection instead.\nfunc NewFromDB(service *badger.DB) *Database {\n\tdb := &Database{Service: service}\n\n\t// runtime.SetFinalizer(db, closeDB)\n\treturn db\n}\n\n// SetLogger sets the logger once before server ran.\n// By default the Iris one is injected.\nfunc (db *Database) SetLogger(logger *golog.Logger) {\n\tdb.logger = logger\n}\n\n// Acquire receives a session's lifetime from the database,\n// if the return value is LifeTime{} then the session manager sets the life time based on the expiration duration lives in configuration.\nfunc (db *Database) Acquire(sid string, expires time.Duration) memstore.LifeTime {\n\ttxn := db.Service.NewTransaction(true)\n\tdefer txn.Commit()\n\n\tbsid := makePrefix(sid)\n\titem, err := txn.Get(bsid)\n\tif err == nil {\n\t\t// found, return the expiration.\n\t\treturn memstore.LifeTime{Time: time.Unix(int64(item.ExpiresAt()), 0)}\n\t}\n\n\t// not found, create an entry with ttl and return an empty lifetime, session manager will do its job.\n\tif err != nil {\n\t\tif err == badger.ErrKeyNotFound {\n\t\t\t// create it and set the expiration, we don't care about the value there.\n\t\t\terr = txn.SetEntry(badger.NewEntry(bsid, bsid).WithTTL(expires))\n\t\t}\n\t}\n\n\tif err != nil {\n\t\tdb.logger.Error(err)\n\t}\n\n\treturn memstore.LifeTime{} // session manager will handle the rest.\n}\n\n// OnUpdateExpiration not implemented here, yet.\n// Note that this error will not be logged, callers should catch it manually.\nfunc (db *Database) OnUpdateExpiration(sid string, newExpires time.Duration) error {\n\treturn sessions.ErrNotImplemented\n}\n\nvar delim = byte('_')\n\nfunc makePrefix(sid string) []byte {\n\treturn append([]byte(sid), delim)\n}\n\nfunc makeKey(sid, key string) []byte {\n\treturn append(makePrefix(sid), []byte(key)...)\n}\n\n// Set sets a key value of a specific session.\n// Ignore the \"immutable\".\nfunc (db *Database) Set(sid string, key string, value any, ttl time.Duration, immutable bool) error {\n\tvalueBytes, err := sessions.DefaultTranscoder.Marshal(value)\n\tif err != nil {\n\t\tdb.logger.Error(err)\n\t\treturn err\n\t}\n\n\terr = db.Service.Update(func(txn *badger.Txn) error {\n\t\treturn txn.SetEntry(badger.NewEntry(makeKey(sid, key), valueBytes).WithTTL(ttl))\n\t})\n\n\tif err != nil {\n\t\tdb.logger.Error(err)\n\t}\n\n\treturn err\n}\n\n// Get retrieves a session value based on the key.\nfunc (db *Database) Get(sid string, key string) (value any) {\n\tif err := db.Decode(sid, key, &value); err == nil {\n\t\treturn value\n\t}\n\n\treturn nil\n}\n\n// Decode binds the \"outPtr\" to the value associated to the provided \"key\".\nfunc (db *Database) Decode(sid, key string, outPtr any) error {\n\terr := db.Service.View(func(txn *badger.Txn) error {\n\t\titem, err := txn.Get(makeKey(sid, key))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\treturn item.Value(func(valueBytes []byte) error {\n\t\t\treturn sessions.DefaultTranscoder.Unmarshal(valueBytes, outPtr)\n\t\t})\n\t})\n\n\tif err != nil && err != badger.ErrKeyNotFound {\n\t\tdb.logger.Error(err)\n\t}\n\n\treturn err\n}\n\n// validSessionItem reports whether the current iterator's item key\n// is a value of the session id \"prefix\".\nfunc validSessionItem(key, prefix []byte) bool {\n\treturn len(key) > len(prefix) && bytes.Equal(key[0:len(prefix)], prefix)\n}\n\n// Visit loops through all session keys and values.\nfunc (db *Database) Visit(sid string, cb func(key string, value any)) error {\n\tprefix := makePrefix(sid)\n\n\ttxn := db.Service.NewTransaction(false)\n\tdefer txn.Discard()\n\n\titer := txn.NewIterator(badger.DefaultIteratorOptions)\n\tdefer iter.Close()\n\n\tfor iter.Rewind(); ; iter.Next() {\n\t\tif !iter.Valid() {\n\t\t\tbreak\n\t\t}\n\n\t\titem := iter.Item()\n\t\tkey := item.Key()\n\t\tif !validSessionItem(key, prefix) {\n\t\t\tcontinue\n\t\t}\n\n\t\tvar value any\n\n\t\terr := item.Value(func(valueBytes []byte) error {\n\t\t\treturn sessions.DefaultTranscoder.Unmarshal(valueBytes, &value)\n\t\t})\n\t\tif err != nil {\n\t\t\tdb.logger.Errorf(\"[sessionsdb.badger.Visit] %v\", err)\n\t\t\treturn err\n\t\t}\n\n\t\tcb(string(bytes.TrimPrefix(key, prefix)), value)\n\t}\n\n\treturn nil\n}\n\nvar iterOptionsNoValues = badger.IteratorOptions{\n\tPrefetchValues: false,\n\tPrefetchSize:   100,\n\tReverse:        false,\n\tAllVersions:    false,\n}\n\n// Len returns the length of the session's entries (keys).\nfunc (db *Database) Len(sid string) (n int) {\n\tprefix := makePrefix(sid)\n\n\ttxn := db.Service.NewTransaction(false)\n\titer := txn.NewIterator(iterOptionsNoValues)\n\n\tfor iter.Rewind(); ; iter.Next() {\n\t\tif !iter.Valid() {\n\t\t\tbreak\n\t\t}\n\n\t\tif validSessionItem(iter.Item().Key(), prefix) {\n\t\t\tn++\n\t\t}\n\t}\n\n\titer.Close()\n\ttxn.Discard()\n\treturn\n}\n\n// Delete removes a session key value based on its key.\nfunc (db *Database) Delete(sid string, key string) (deleted bool) {\n\ttxn := db.Service.NewTransaction(true)\n\terr := txn.Delete(makeKey(sid, key))\n\tif err != nil {\n\t\tdb.logger.Error(err)\n\t\treturn false\n\t}\n\treturn txn.Commit() == nil\n}\n\n// Clear removes all session key values but it keeps the session entry.\nfunc (db *Database) Clear(sid string) error {\n\tprefix := makePrefix(sid)\n\n\ttxn := db.Service.NewTransaction(true)\n\tdefer txn.Commit()\n\n\titer := txn.NewIterator(iterOptionsNoValues)\n\tdefer iter.Close()\n\n\tfor iter.Rewind(); iter.ValidForPrefix(prefix); iter.Next() {\n\t\tkey := iter.Item().Key()\n\t\tif err := txn.Delete(key); err != nil {\n\t\t\tdb.logger.Warnf(\"Database.Clear: %s: %v\", key, err)\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// Release destroys the session, it clears and removes the session entry,\n// session manager will create a new session ID on the next request after this call.\nfunc (db *Database) Release(sid string) error {\n\t// clear all $sid-$key.\n\terr := db.Clear(sid)\n\tif err != nil {\n\t\treturn err\n\t}\n\t// and remove the $sid.\n\ttxn := db.Service.NewTransaction(true)\n\tif err = txn.Delete([]byte(sid)); err != nil {\n\t\tdb.logger.Warnf(\"Database.Release.Delete: %s: %v\", sid, err)\n\t\treturn err\n\t}\n\tif err = txn.Commit(); err != nil {\n\t\tdb.logger.Debugf(\"Database.Release.Commit: %s: %v\", sid, err)\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\n// Close shutdowns the badger connection.\nfunc (db *Database) Close() error {\n\treturn closeDB(db)\n}\n\nfunc closeDB(db *Database) error {\n\tif atomic.LoadUint32(&db.closed) > 0 {\n\t\treturn nil\n\t}\n\terr := db.Service.Close()\n\tif err != nil {\n\t\tdb.logger.Warnf(\"closing the badger connection: %v\", err)\n\t} else {\n\t\tatomic.StoreUint32(&db.closed, 1)\n\t}\n\treturn err\n}\n"
  },
  {
    "path": "sessions/sessiondb/boltdb/database.go",
    "content": "package boltdb\n\nimport (\n\t\"errors\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/core/memstore\"\n\t\"github.com/kataras/iris/v12/sessions\"\n\n\t\"github.com/kataras/golog\"\n\tbolt \"go.etcd.io/bbolt\"\n)\n\n// DefaultFileMode used as the default database's \"fileMode\"\n// for creating the sessions directory path, opening and write\n// the session boltdb(file-based) storage.\nvar (\n\tDefaultFileMode = 0755\n)\n\n// Database the BoltDB(file-based) session storage.\ntype Database struct {\n\ttable []byte\n\t// Service is the underline BoltDB database connection,\n\t// it's initialized at `New` or `NewFromDB`.\n\t// Can be used to get stats.\n\tService *bolt.DB\n\tlogger  *golog.Logger\n}\n\nvar errPathMissing = errors.New(\"path is required\")\n\n// New creates and returns a new BoltDB(file-based) storage\n// instance based on the \"path\".\n// Path should include the filename and the directory(aka fullpath), i.e sessions/store.db.\n//\n// It will remove any old session files.\nfunc New(path string, fileMode os.FileMode) (*Database, error) {\n\tif path == \"\" {\n\t\tgolog.Error(errPathMissing)\n\t\treturn nil, errPathMissing\n\t}\n\n\tif fileMode == 0 {\n\t\tfileMode = os.FileMode(DefaultFileMode)\n\t}\n\n\t// create directories if necessary\n\tif err := os.MkdirAll(filepath.Dir(path), fileMode); err != nil {\n\t\tgolog.Errorf(\"error while trying to create the necessary directories for %s: %v\", path, err)\n\t\treturn nil, err\n\t}\n\n\tservice, err := bolt.Open(path, fileMode,\n\t\t&bolt.Options{Timeout: 20 * time.Second},\n\t)\n\tif err != nil {\n\t\tgolog.Errorf(\"unable to initialize the BoltDB-based session database: %v\", err)\n\t\treturn nil, err\n\t}\n\n\treturn NewFromDB(service, \"sessions\")\n}\n\n// NewFromDB same as `New` but accepts an already-created custom boltdb connection instead.\nfunc NewFromDB(service *bolt.DB, bucketName string) (*Database, error) {\n\tbucket := []byte(bucketName)\n\n\terr := service.Update(func(tx *bolt.Tx) (err error) {\n\t\t_, err = tx.CreateBucketIfNotExists(bucket)\n\t\treturn\n\t})\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tdb := &Database{table: bucket, Service: service}\n\n\t// runtime.SetFinalizer(db, closeDB)\n\treturn db, db.cleanup()\n}\n\nfunc (db *Database) getBucket(tx *bolt.Tx) *bolt.Bucket {\n\treturn tx.Bucket(db.table)\n}\n\nfunc (db *Database) getBucketForSession(tx *bolt.Tx, sid string) *bolt.Bucket {\n\tb := db.getBucket(tx).Bucket([]byte(sid))\n\tif b == nil {\n\t\t// session does not exist, it shouldn't happen, session bucket creation happens once at `Acquire`,\n\t\t// no need to accept the `bolt.bucket.CreateBucketIfNotExists`'s performance cost.\n\t\tdb.logger.Debugf(\"unreachable session access for '%s'\", sid)\n\t}\n\n\treturn b\n}\n\nvar (\n\texpirationBucketName = []byte(\"expiration\")\n\tdelim                = []byte(\"_\")\n)\n\n// expiration lives on its own bucket for each session bucket.\nfunc getExpirationBucketName(bsid []byte) []byte {\n\treturn append(bsid, append(delim, expirationBucketName...)...)\n}\n\n// Cleanup removes any invalid(have expired) session entries on initialization.\nfunc (db *Database) cleanup() error {\n\treturn db.Service.Update(func(tx *bolt.Tx) error {\n\t\tb := db.getBucket(tx)\n\t\tc := b.Cursor()\n\t\t// loop through all buckets, find one with expiration.\n\t\tfor bsid, v := c.First(); bsid != nil; bsid, v = c.Next() {\n\t\t\tif len(bsid) == 0 { // empty key, continue to the next session bucket.\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\texpirationName := getExpirationBucketName(bsid)\n\t\t\tif bExp := b.Bucket(expirationName); bExp != nil { // has expiration.\n\t\t\t\t_, expValue := bExp.Cursor().First() // the expiration bucket contains only one key(we don't care, see `Acquire`) value(time.Time) pair.\n\t\t\t\tif expValue == nil {\n\t\t\t\t\tdb.logger.Debugf(\"cleanup: expiration is there but its value is empty '%s'\", v) // should never happen.\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tvar expirationTime time.Time\n\t\t\t\tif err := sessions.DefaultTranscoder.Unmarshal(expValue, &expirationTime); err != nil {\n\t\t\t\t\tdb.logger.Debugf(\"cleanup: unable to retrieve expiration value for '%s'\", v)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tif expirationTime.Before(time.Now()) {\n\t\t\t\t\t// expired, delete the expiration bucket.\n\t\t\t\t\tif err := b.DeleteBucket(expirationName); err != nil {\n\t\t\t\t\t\tdb.logger.Debugf(\"cleanup: unable to destroy a session '%s'\", bsid)\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\n\t\t\t\t\t// and the session bucket, if any.\n\t\t\t\t\treturn b.DeleteBucket(bsid)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn nil\n\t})\n}\n\n// SetLogger sets the logger once before server ran.\n// By default the Iris one is injected.\nfunc (db *Database) SetLogger(logger *golog.Logger) {\n\tdb.logger = logger\n}\n\nvar expirationKey = []byte(\"exp\") // it can be random.\n\n// Acquire receives a session's lifetime from the database,\n// if the return value is LifeTime{} then the session manager sets the life time based on the expiration duration lives in configuration.\nfunc (db *Database) Acquire(sid string, expires time.Duration) (lifetime memstore.LifeTime) {\n\tbsid := []byte(sid)\n\terr := db.Service.Update(func(tx *bolt.Tx) (err error) {\n\t\troot := db.getBucket(tx)\n\n\t\tif expires > 0 { // should check or create the expiration bucket.\n\t\t\tname := getExpirationBucketName(bsid)\n\t\t\tb := root.Bucket(name)\n\t\t\tif b == nil {\n\t\t\t\t// not found, create a session bucket and an expiration bucket and save the given \"expires\" of time.Time,\n\t\t\t\t// don't return a lifetime, let it empty, session manager will do its job.\n\t\t\t\tb, err = root.CreateBucket(name)\n\t\t\t\tif err != nil {\n\t\t\t\t\tdb.logger.Debugf(\"unable to create a session bucket for '%s': %v\", sid, err)\n\t\t\t\t\treturn err\n\t\t\t\t}\n\n\t\t\t\texpirationTime := time.Now().Add(expires)\n\t\t\t\ttimeBytes, err := sessions.DefaultTranscoder.Marshal(expirationTime)\n\t\t\t\tif err != nil {\n\t\t\t\t\tdb.logger.Debugf(\"unable to set an expiration value on session expiration bucket for '%s': %v\", sid, err)\n\t\t\t\t\treturn err\n\t\t\t\t}\n\n\t\t\t\terr = b.Put(expirationKey, timeBytes)\n\t\t\t\tif err == nil {\n\t\t\t\t\t// create the session bucket now, so the rest of the calls can be easly get the bucket without any further checks.\n\t\t\t\t\t_, err = root.CreateBucket(bsid)\n\t\t\t\t}\n\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\t// found, get the associated expiration bucket, wrap its value and return.\n\t\t\t_, expValue := b.Cursor().First()\n\t\t\tif expValue == nil {\n\t\t\t\treturn nil // does not expire.\n\t\t\t}\n\n\t\t\tvar expirationTime time.Time\n\t\t\tif err = sessions.DefaultTranscoder.Unmarshal(expValue, &expirationTime); err != nil {\n\t\t\t\tdb.logger.Debugf(\"acquire: unable to retrieve expiration value for '%s', value was: '%s': %v\", sid, expValue, err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tlifetime = memstore.LifeTime{Time: expirationTime}\n\t\t\treturn nil\n\t\t}\n\n\t\t// does not expire, just create the session bucket if not exists so we can be ready later on.\n\t\t_, err = root.CreateBucketIfNotExists(bsid)\n\t\treturn\n\t})\n\tif err != nil {\n\t\tdb.logger.Debugf(\"unable to acquire session '%s': %v\", sid, err)\n\t\treturn memstore.LifeTime{}\n\t}\n\n\treturn\n}\n\n// OnUpdateExpiration will re-set the database's session's entry ttl.\nfunc (db *Database) OnUpdateExpiration(sid string, newExpires time.Duration) error {\n\texpirationTime := time.Now().Add(newExpires)\n\ttimeBytes, err := sessions.DefaultTranscoder.Marshal(expirationTime)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\terr = db.Service.Update(func(tx *bolt.Tx) error {\n\t\texpirationName := getExpirationBucketName([]byte(sid))\n\t\troot := db.getBucket(tx)\n\t\tb := root.Bucket(expirationName)\n\t\tif b == nil {\n\t\t\t// db.logger.Debugf(\"tried to reset the expiration value for '%s' while its configured lifetime is unlimited or the session is already expired and not found now\", sid)\n\t\t\treturn sessions.ErrNotFound\n\t\t}\n\n\t\treturn b.Put(expirationKey, timeBytes)\n\t})\n\n\tif err != nil {\n\t\tdb.logger.Debugf(\"unable to reset the expiration value for '%s': %v\", sid, err)\n\t}\n\n\treturn err\n}\n\nfunc makeKey(key string) []byte {\n\treturn []byte(key)\n}\n\n// Set sets a key value of a specific session.\n// Ignore the \"immutable\".\nfunc (db *Database) Set(sid string, key string, value any, ttl time.Duration, immutable bool) error {\n\tvalueBytes, err := sessions.DefaultTranscoder.Marshal(value)\n\tif err != nil {\n\t\tdb.logger.Debug(err)\n\t\treturn err\n\t}\n\n\terr = db.Service.Update(func(tx *bolt.Tx) error {\n\t\tb := db.getBucketForSession(tx, sid)\n\t\tif b == nil {\n\t\t\treturn nil\n\t\t}\n\n\t\t// Author's notes:\n\t\t// expiration is handlded by the session manager for the whole session, so the `db.Destroy` will be called when and if needed.\n\t\t// Therefore we don't have to implement a TTL here, but we need a `db.Cleanup`, as we did previously, method to delete any expired if server restarted\n\t\t// (badger does not need a `Cleanup` because we set the TTL based on the lifetime.DurationUntilExpiration()).\n\t\treturn b.Put(makeKey(key), valueBytes)\n\t})\n\n\tif err != nil {\n\t\tdb.logger.Debug(err)\n\t}\n\n\treturn err\n}\n\n// Get retrieves a session value based on the key.\nfunc (db *Database) Get(sid string, key string) (value any) {\n\tif err := db.Decode(sid, key, &value); err == nil {\n\t\treturn value\n\t}\n\n\treturn nil\n}\n\n// Decode binds the \"outPtr\" to the value associated to the provided \"key\".\nfunc (db *Database) Decode(sid, key string, outPtr any) error {\n\terr := db.Service.View(func(tx *bolt.Tx) error {\n\t\tb := db.getBucketForSession(tx, sid)\n\t\tif b == nil {\n\t\t\treturn nil\n\t\t}\n\n\t\tvalueBytes := b.Get(makeKey(key))\n\t\tif len(valueBytes) == 0 {\n\t\t\treturn nil\n\t\t}\n\n\t\treturn sessions.DefaultTranscoder.Unmarshal(valueBytes, outPtr)\n\t})\n\tif err != nil {\n\t\tdb.logger.Debugf(\"session '%s' key '%s' cannot be retrieved: %v\", sid, key, err)\n\t}\n\n\treturn err\n}\n\n// Visit loops through all session keys and values.\nfunc (db *Database) Visit(sid string, cb func(key string, value any)) error {\n\terr := db.Service.View(func(tx *bolt.Tx) error {\n\t\tb := db.getBucketForSession(tx, sid)\n\t\tif b == nil {\n\t\t\treturn nil\n\t\t}\n\n\t\treturn b.ForEach(func(k []byte, v []byte) error {\n\t\t\tvar value any\n\t\t\tif err := sessions.DefaultTranscoder.Unmarshal(v, &value); err != nil {\n\t\t\t\tdb.logger.Debugf(\"unable to retrieve value of key '%s' of '%s': %v\", k, sid, err)\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tcb(string(k), value)\n\t\t\treturn nil\n\t\t})\n\t})\n\n\tif err != nil {\n\t\tdb.logger.Debugf(\"Database.Visit: %s: %v\", sid, err)\n\t}\n\n\treturn err\n}\n\n// Len returns the length of the session's entries (keys).\nfunc (db *Database) Len(sid string) (n int) {\n\terr := db.Service.View(func(tx *bolt.Tx) error {\n\t\tb := db.getBucketForSession(tx, sid)\n\t\tif b == nil {\n\t\t\treturn nil\n\t\t}\n\n\t\tn = int(int64(b.Stats().KeyN))\n\t\treturn nil\n\t})\n\n\tif err != nil {\n\t\tdb.logger.Debugf(\"Database.Len: %s: %v\", sid, err)\n\t}\n\n\treturn\n}\n\n// Delete removes a session key value based on its key.\nfunc (db *Database) Delete(sid string, key string) (deleted bool) {\n\terr := db.Service.Update(func(tx *bolt.Tx) error {\n\t\tb := db.getBucketForSession(tx, sid)\n\t\tif b == nil {\n\t\t\treturn sessions.ErrNotFound\n\t\t}\n\n\t\treturn b.Delete(makeKey(key))\n\t})\n\n\treturn err == nil\n}\n\n// Clear removes all session key values but it keeps the session entry.\nfunc (db *Database) Clear(sid string) error {\n\terr := db.Service.Update(func(tx *bolt.Tx) error {\n\t\tb := db.getBucketForSession(tx, sid)\n\t\tif b == nil {\n\t\t\treturn nil\n\t\t}\n\n\t\treturn b.ForEach(func(k []byte, v []byte) error {\n\t\t\treturn b.Delete(k)\n\t\t})\n\t})\n\n\tif err != nil {\n\t\tdb.logger.Debugf(\"Database.Clear: %s: %v\", sid, err)\n\t}\n\n\treturn err\n}\n\n// Release destroys the session, it clears and removes the session entry,\n// session manager will create a new session ID on the next request after this call.\nfunc (db *Database) Release(sid string) error {\n\terr := db.Service.Update(func(tx *bolt.Tx) error {\n\t\t// delete the session bucket.\n\t\tb := db.getBucket(tx)\n\t\tbsid := []byte(sid)\n\t\t// try to delete the associated expiration bucket, if exists, ignore error.\n\t\t_ = b.DeleteBucket(getExpirationBucketName(bsid))\n\n\t\treturn b.DeleteBucket(bsid)\n\t})\n\n\tif err != nil {\n\t\tdb.logger.Debugf(\"Database.Release: %s: %v\", sid, err)\n\t}\n\n\treturn err\n}\n\n// Close shutdowns the BoltDB connection.\nfunc (db *Database) Close() error {\n\treturn closeDB(db)\n}\n\nfunc closeDB(db *Database) error {\n\terr := db.Service.Close()\n\tif err != nil {\n\t\tdb.logger.Warnf(\"closing the BoltDB connection: %v\", err)\n\t}\n\n\treturn err\n}\n"
  },
  {
    "path": "sessions/sessiondb/redis/database.go",
    "content": "package redis\n\nimport (\n\t\"crypto/tls\"\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/core/memstore\"\n\t\"github.com/kataras/iris/v12/sessions\"\n\n\t\"github.com/kataras/golog\"\n)\n\nconst (\n\t// DefaultRedisNetwork the redis network option, \"tcp\".\n\tDefaultRedisNetwork = \"tcp\"\n\t// DefaultRedisAddr the redis address option, \"127.0.0.1:6379\".\n\tDefaultRedisAddr = \"127.0.0.1:6379\"\n\t// DefaultRedisTimeout the redis idle timeout option, time.Duration(30) * time.Second.\n\tDefaultRedisTimeout = time.Duration(30) * time.Second\n)\n\n// Config the redis configuration used inside sessions\ntype Config struct {\n\t// Network protocol. Defaults to \"tcp\".\n\tNetwork string\n\t// Addr of a single redis server instance.\n\t// See \"Clusters\" field for clusters support.\n\t// Defaults to \"127.0.0.1:6379\".\n\tAddr string\n\t// Clusters a list of network addresses for clusters.\n\t// If not empty \"Addr\" is ignored and Redis clusters feature is used instead.\n\t// Note that this field is ignored when setgging a custom `GoRedisClient`.\n\tClusters []string\n\t// Use the specified Username to authenticate the current connection\n\t// with one of the connections defined in the ACL list when connecting\n\t// to a Redis 6.0 instance, or greater, that is using the Redis ACL system.\n\tUsername string\n\t// Optional password. Must match the password specified in the\n\t// requirepass server configuration option (if connecting to a Redis 5.0 instance, or lower),\n\t// or the User Password when connecting to a Redis 6.0 instance, or greater,\n\t// that is using the Redis ACL system.\n\tPassword string\n\t// If Database is empty \"\" then no 'SELECT'. Defaults to \"\".\n\tDatabase string\n\t// Maximum number of socket connections.\n\t// Default is 10 connections per every CPU as reported by runtime.NumCPU.\n\tMaxActive int\n\t// Timeout for connect, write and read, defaults to 30 seconds, 0 means no timeout.\n\tTimeout time.Duration\n\t// Prefix \"myprefix-for-this-website\". Defaults to \"\".\n\tPrefix string\n\n\t// TLSConfig will cause Dial to perform a TLS handshake using the provided\n\t// config. If is nil then no TLS is used.\n\t// See https://golang.org/pkg/crypto/tls/#Config\n\tTLSConfig *tls.Config\n\n\t// A Driver should support be a go client for redis communication.\n\t// It can be set to a custom one or a mock one (for testing).\n\t//\n\t// Defaults to `GoRedis()`.\n\tDriver Driver\n}\n\n// DefaultConfig returns the default configuration for Redis service.\nfunc DefaultConfig() Config {\n\treturn Config{\n\t\tNetwork:   DefaultRedisNetwork,\n\t\tAddr:      DefaultRedisAddr,\n\t\tUsername:  \"\",\n\t\tPassword:  \"\",\n\t\tDatabase:  \"\",\n\t\tMaxActive: 10,\n\t\tTimeout:   DefaultRedisTimeout,\n\t\tPrefix:    \"\",\n\t\tTLSConfig: nil,\n\t\tDriver:    GoRedis(),\n\t}\n}\n\n// Database the redis back-end session database for the sessions.\ntype Database struct {\n\tc      Config\n\tlogger *golog.Logger\n}\n\nvar _ sessions.Database = (*Database)(nil)\n\n// New returns a new redis sessions database.\nfunc New(cfg ...Config) *Database {\n\tc := DefaultConfig()\n\tif len(cfg) > 0 {\n\t\tc = cfg[0]\n\n\t\tif c.Timeout < 0 {\n\t\t\tc.Timeout = DefaultRedisTimeout\n\t\t}\n\n\t\tif c.Network == \"\" {\n\t\t\tc.Network = DefaultRedisNetwork\n\t\t}\n\n\t\tif c.Addr == \"\" {\n\t\t\tc.Addr = DefaultRedisAddr\n\t\t}\n\n\t\tif c.Driver == nil {\n\t\t\tc.Driver = GoRedis()\n\t\t}\n\t}\n\n\tif err := c.Driver.Connect(c); err != nil {\n\t\tpanic(err)\n\t}\n\n\tdb := &Database{c: c}\n\t_, err := db.c.Driver.PingPong()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\t// runtime.SetFinalizer(db, closeDB)\n\treturn db\n}\n\n// SetLogger sets the logger once before server ran.\n// By default the Iris one is injected.\nfunc (db *Database) SetLogger(logger *golog.Logger) {\n\tdb.logger = logger\n}\n\nfunc (db *Database) makeSID(sid string) string {\n\treturn db.c.Prefix + sid\n}\n\n// SessionIDKey the session ID stored to the redis session itself.\nconst SessionIDKey = \"session_id\"\n\n// Acquire receives a session's lifetime from the database,\n// if the return value is LifeTime{} then the session manager sets the life time based on the expiration duration lives in configuration.\nfunc (db *Database) Acquire(sid string, expires time.Duration) memstore.LifeTime {\n\tsidKey := db.makeSID(sid)\n\tif !db.c.Driver.Exists(sidKey) {\n\t\tif err := db.Set(sid, SessionIDKey, sid, 0, false); err != nil {\n\t\t\tdb.logger.Debug(err)\n\t\t} else if expires > 0 {\n\t\t\tif err := db.c.Driver.UpdateTTL(sidKey, expires); err != nil {\n\t\t\t\tdb.logger.Debug(err)\n\t\t\t}\n\t\t}\n\n\t\treturn memstore.LifeTime{} // session manager will handle the rest.\n\t}\n\n\tuntilExpire := db.c.Driver.TTL(sidKey)\n\treturn memstore.LifeTime{Time: time.Now().Add(untilExpire)}\n}\n\n// OnUpdateExpiration will re-set the database's session's entry ttl.\n// https://redis.io/commands/expire#refreshing-expires\nfunc (db *Database) OnUpdateExpiration(sid string, newExpires time.Duration) error {\n\treturn db.c.Driver.UpdateTTL(db.makeSID(sid), newExpires)\n}\n\n// Set sets a key value of a specific session.\n// Ignore the \"immutable\".\nfunc (db *Database) Set(sid string, key string, value any, _ time.Duration, _ bool) error {\n\tvalueBytes, err := sessions.DefaultTranscoder.Marshal(value)\n\tif err != nil {\n\t\tdb.logger.Error(err)\n\t\treturn err\n\t}\n\n\tif err = db.c.Driver.Set(db.makeSID(sid), key, valueBytes); err != nil {\n\t\tdb.logger.Debug(err)\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\n// Get retrieves a session value based on the key.\nfunc (db *Database) Get(sid string, key string) (value any) {\n\tif err := db.Decode(sid, key, &value); err == nil {\n\t\treturn value\n\t}\n\n\treturn nil\n}\n\n// Decode binds the \"outPtr\" to the value associated to the provided \"key\".\nfunc (db *Database) Decode(sid, key string, outPtr any) error {\n\tsidKey := db.makeSID(sid)\n\tdata, err := db.c.Driver.Get(sidKey, key)\n\tif err != nil {\n\t\t// not found.\n\t\treturn err\n\t}\n\n\tif err = db.decodeValue(data, outPtr); err != nil {\n\t\tdb.logger.Debugf(\"unable to unmarshal value of key: '%s%s': %v\", sid, key, err)\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (db *Database) decodeValue(val any, outPtr any) error {\n\tif val == nil {\n\t\treturn nil\n\t}\n\n\tswitch data := val.(type) {\n\tcase []byte:\n\t\t// this is the most common type, as we save all values as []byte,\n\t\t// the only exception is where the value is string on HGetAll command.\n\t\treturn sessions.DefaultTranscoder.Unmarshal(data, outPtr)\n\tcase string:\n\t\treturn sessions.DefaultTranscoder.Unmarshal([]byte(data), outPtr)\n\tdefault:\n\t\treturn fmt.Errorf(\"unknown value type of %T\", data)\n\t}\n}\n\nfunc (db *Database) keys(fullSID string) []string {\n\tkeys, err := db.c.Driver.GetKeys(fullSID)\n\tif err != nil {\n\t\tdb.logger.Debugf(\"unable to get all redis keys of session '%s': %v\", fullSID, err)\n\t\treturn nil\n\t}\n\n\treturn keys\n}\n\n// Visit loops through all session keys and values.\nfunc (db *Database) Visit(sid string, cb func(key string, value any)) error {\n\tkv, err := db.c.Driver.GetAll(db.makeSID(sid))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor k, v := range kv {\n\t\tvar value any // new value each time, we don't know what user will do in \"cb\".\n\t\tif err = db.decodeValue(v, &value); err != nil {\n\t\t\tdb.logger.Debugf(\"unable to decode %s:%s: %v\", sid, k, err)\n\t\t\treturn err\n\t\t}\n\n\t\tcb(k, value)\n\t}\n\n\treturn nil\n}\n\n// Len returns the length of the session's entries (keys).\nfunc (db *Database) Len(sid string) int {\n\treturn db.c.Driver.Len(sid)\n}\n\n// Delete removes a session key value based on its key.\nfunc (db *Database) Delete(sid string, key string) (deleted bool) {\n\terr := db.c.Driver.Delete(db.makeSID(sid), key)\n\tif err != nil {\n\t\tdb.logger.Error(err)\n\t}\n\treturn err == nil\n}\n\n// Clear removes all session key values but it keeps the session entry.\nfunc (db *Database) Clear(sid string) error {\n\tsid = db.makeSID(sid)\n\tkeys := db.keys(sid)\n\tfor _, key := range keys {\n\t\tif key == SessionIDKey {\n\t\t\tcontinue\n\t\t}\n\t\tif err := db.c.Driver.Delete(sid, key); err != nil {\n\t\t\tdb.logger.Debugf(\"unable to delete session '%s' value of key: '%s': %v\", sid, key, err)\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// Release destroys the session, it clears and removes the session entry,\n// session manager will create a new session ID on the next request after this call.\nfunc (db *Database) Release(sid string) error {\n\terr := db.c.Driver.Delete(db.makeSID(sid), \"\")\n\tif err != nil {\n\t\tdb.logger.Debugf(\"Database.Release.Driver.Delete: %s: %v\", sid, err)\n\t}\n\n\treturn err\n}\n\n// Close terminates the redis connection.\nfunc (db *Database) Close() error {\n\treturn closeDB(db)\n}\n\nfunc closeDB(db *Database) error {\n\treturn db.c.Driver.CloseConnection()\n}\n\nvar (\n\t// ErrRedisClosed an error with message 'redis: already closed'\n\tErrRedisClosed = errors.New(\"redis: already closed\")\n\t// ErrKeyNotFound a type of error of non-existing redis keys.\n\t// The producers(the library) of this error will dynamically wrap this error(fmt.Errorf) with the key name.\n\t// Usage:\n\t// if err != nil && errors.Is(err, ErrKeyNotFound) {\n\t// [...]\n\t// }\n\tErrKeyNotFound = errors.New(\"key not found\")\n)\n"
  },
  {
    "path": "sessions/sessiondb/redis/driver.go",
    "content": "package redis\n\nimport \"time\"\n\n// Driver is the interface which each supported redis client\n// should support in order to be used in the redis session database.\ntype Driver interface {\n\tConnect(c Config) error\n\tPingPong() (bool, error)\n\tCloseConnection() error\n\tSet(sid, key string, value any) error\n\tGet(sid, key string) (any, error)\n\tExists(sid string) bool\n\tTTL(sid string) time.Duration\n\tUpdateTTL(sid string, newLifetime time.Duration) error\n\tGetAll(sid string) (map[string]string, error)\n\tGetKeys(sid string) ([]string, error)\n\tLen(sid string) int\n\tDelete(sid, key string) error\n}\n\nvar (\n\t_ Driver = (*GoRedisDriver)(nil)\n)\n\n// GoRedis returns the default Driver for the redis sessions database\n// It's the go-redis client. Learn more at: https://github.com/go-redis/redis.\nfunc GoRedis() *GoRedisDriver {\n\treturn &GoRedisDriver{}\n}\n"
  },
  {
    "path": "sessions/sessiondb/redis/driver_goredis.go",
    "content": "package redis\n\nimport (\n\tstdContext \"context\"\n\t\"io\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/redis/go-redis/v9\"\n)\n\ntype (\n\t// Options is just a type alias for the go-redis Client Options.\n\tOptions = redis.Options\n\t// ClusterOptions is just a type alias for the go-redis Cluster Client Options.\n\tClusterOptions = redis.ClusterOptions\n)\n\n// GoRedisClient is the interface which both\n// go-redis's Client and Cluster Client implements.\ntype GoRedisClient interface {\n\tredis.Cmdable // Commands.\n\tio.Closer     // CloseConnection.\n}\n\n// GoRedisDriver implements the Sessions Database Driver\n// for the go-redis redis driver. See driver.go file.\ntype GoRedisDriver struct {\n\t// Both Client and ClusterClient implements this interface.\n\t// Custom one can be directly passed but if so, the\n\t// Connect method does nothing (so all connection and client settings are ignored).\n\tClient GoRedisClient\n\t// Customize any go-redis fields manually\n\t// before Connect.\n\tClientOptions  Options\n\tClusterOptions ClusterOptions\n}\n\nvar defaultContext = stdContext.Background()\n\nfunc (r *GoRedisDriver) mergeClientOptions(c Config) *Options {\n\topts := r.ClientOptions\n\tif opts.Addr == \"\" {\n\t\topts.Addr = c.Addr\n\t}\n\n\tif opts.Username == \"\" {\n\t\topts.Username = c.Username\n\t}\n\n\tif opts.Password == \"\" {\n\t\topts.Password = c.Password\n\t}\n\n\tif opts.DB == 0 {\n\t\topts.DB, _ = strconv.Atoi(c.Database)\n\t}\n\n\tif opts.ReadTimeout == 0 {\n\t\topts.ReadTimeout = c.Timeout\n\t}\n\n\tif opts.WriteTimeout == 0 {\n\t\topts.WriteTimeout = c.Timeout\n\t}\n\n\tif opts.Network == \"\" {\n\t\topts.Network = c.Network\n\t}\n\n\tif opts.TLSConfig == nil {\n\t\topts.TLSConfig = c.TLSConfig\n\t}\n\n\tif opts.PoolSize == 0 {\n\t\topts.PoolSize = c.MaxActive\n\t}\n\n\treturn &opts\n}\n\nfunc (r *GoRedisDriver) mergeClusterOptions(c Config) *ClusterOptions {\n\topts := r.ClusterOptions\n\n\tif opts.Username == \"\" {\n\t\topts.Username = c.Username\n\t}\n\n\tif opts.Password == \"\" {\n\t\topts.Password = c.Password\n\t}\n\n\tif opts.ReadTimeout == 0 {\n\t\topts.ReadTimeout = c.Timeout\n\t}\n\n\tif opts.WriteTimeout == 0 {\n\t\topts.WriteTimeout = c.Timeout\n\t}\n\n\tif opts.TLSConfig == nil {\n\t\topts.TLSConfig = c.TLSConfig\n\t}\n\n\tif opts.PoolSize == 0 {\n\t\topts.PoolSize = c.MaxActive\n\t}\n\n\tif len(opts.Addrs) == 0 {\n\t\topts.Addrs = c.Clusters\n\t}\n\n\treturn &opts\n}\n\n// SetClient sets an existing go redis client to the sessions redis driver.\n//\n// Returns itself.\nfunc (r *GoRedisDriver) SetClient(goRedisClient GoRedisClient) *GoRedisDriver {\n\tr.Client = goRedisClient\n\treturn r\n}\n\n// Connect initializes the redis client.\nfunc (r *GoRedisDriver) Connect(c Config) error {\n\tif r.Client != nil { // if a custom one was given through SetClient.\n\t\treturn nil\n\t}\n\n\tif len(c.Clusters) > 0 {\n\t\tr.Client = redis.NewClusterClient(r.mergeClusterOptions(c))\n\t} else {\n\t\tr.Client = redis.NewClient(r.mergeClientOptions(c))\n\t}\n\n\treturn nil\n}\n\n// PingPong sends a ping message and reports whether\n// the PONG message received successfully.\nfunc (r *GoRedisDriver) PingPong() (bool, error) {\n\tpong, err := r.Client.Ping(defaultContext).Result()\n\treturn pong == \"PONG\", err\n}\n\n// CloseConnection terminates the underline redis connection.\nfunc (r *GoRedisDriver) CloseConnection() error {\n\treturn r.Client.Close()\n}\n\n// Set stores a \"value\" based on the session's \"key\".\n// The value should be type of []byte, so unmarshal can happen.\nfunc (r *GoRedisDriver) Set(sid, key string, value any) error {\n\treturn r.Client.HSet(defaultContext, sid, key, value).Err()\n}\n\n// Get returns the associated value of the session's given \"key\".\nfunc (r *GoRedisDriver) Get(sid, key string) (any, error) {\n\treturn r.Client.HGet(defaultContext, sid, key).Bytes()\n}\n\n// Exists reports whether a session exists or not.\nfunc (r *GoRedisDriver) Exists(sid string) bool {\n\tn, err := r.Client.Exists(defaultContext, sid).Result()\n\tif err != nil {\n\t\treturn false\n\t}\n\n\treturn n > 0\n}\n\n// TTL returns any TTL value of the session.\nfunc (r *GoRedisDriver) TTL(sid string) time.Duration {\n\tdur, err := r.Client.TTL(defaultContext, sid).Result()\n\tif err != nil {\n\t\treturn 0\n\t}\n\n\treturn dur\n}\n\n// UpdateTTL sets expiration duration of the session.\nfunc (r *GoRedisDriver) UpdateTTL(sid string, newLifetime time.Duration) error {\n\t_, err := r.Client.Expire(defaultContext, sid, newLifetime).Result()\n\treturn err\n}\n\n// GetAll returns all the key values under the session.\nfunc (r *GoRedisDriver) GetAll(sid string) (map[string]string, error) {\n\treturn r.Client.HGetAll(defaultContext, sid).Result()\n}\n\n// GetKeys returns all keys under the session.\nfunc (r *GoRedisDriver) GetKeys(sid string) ([]string, error) {\n\treturn r.Client.HKeys(defaultContext, sid).Result()\n}\n\n// Len returns the total length of key-values of the session.\nfunc (r *GoRedisDriver) Len(sid string) int {\n\treturn int(r.Client.HLen(defaultContext, sid).Val())\n}\n\n// Delete removes a value from the redis store.\nfunc (r *GoRedisDriver) Delete(sid, key string) error {\n\tif key == \"\" {\n\t\treturn r.Client.Del(defaultContext, sid).Err()\n\t}\n\treturn r.Client.HDel(defaultContext, sid, key).Err()\n}\n"
  },
  {
    "path": "sessions/sessions.go",
    "content": "package sessions\n\nimport (\n\t\"net/http\"\n\t\"net/url\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/core/host\"\n)\n\nfunc init() {\n\tcontext.SetHandlerName(\"iris/sessions.*Handler\", \"iris.session\")\n}\n\n// A Sessions manager should be responsible to Start/Get a sesion, based\n// on a Context, which returns a *Session, type.\n// It performs automatic memory cleanup on expired sessions.\n// It can accept a `Database` for persistence across server restarts.\n// A session can set temporary values (flash messages).\ntype Sessions struct {\n\tconfig   Config\n\tprovider *provider\n\n\tcookieOptions []context.CookieOption // options added on each session cookie action.\n}\n\n// New returns a new fast, feature-rich sessions manager\n// it can be adapted to an iris station\nfunc New(cfg Config) *Sessions {\n\tvar cookieOptions []context.CookieOption\n\tif cfg.AllowReclaim {\n\t\tcookieOptions = append(cookieOptions, context.CookieAllowReclaim(cfg.Cookie))\n\t}\n\tif !cfg.DisableSubdomainPersistence {\n\t\tcookieOptions = append(cookieOptions, context.CookieAllowSubdomains(cfg.Cookie))\n\t}\n\tif cfg.CookieSecureTLS {\n\t\tcookieOptions = append(cookieOptions, context.CookieSecure)\n\t}\n\tif cfg.Encoding != nil {\n\t\tcookieOptions = append(cookieOptions, context.CookieEncoding(cfg.Encoding, cfg.Cookie))\n\t}\n\n\treturn &Sessions{\n\t\tcookieOptions: cookieOptions,\n\t\tconfig:        cfg.Validate(),\n\t\tprovider:      newProvider(),\n\t}\n}\n\n// UseDatabase adds a session database to the manager's provider,\n// a session db doesn't have write access\nfunc (s *Sessions) UseDatabase(db Database) {\n\tdb.SetLogger(s.config.Logger) // inject the logger.\n\thost.RegisterOnInterrupt(func() {\n\t\tdb.Close()\n\t})\n\ts.provider.RegisterDatabase(db)\n}\n\n// GetCookieOptions returns the cookie options registered\n// for this sessions manager based on the configuration.\nfunc (s *Sessions) GetCookieOptions() []context.CookieOption {\n\treturn s.cookieOptions\n}\n\n// updateCookie gains the ability of updating the session browser cookie to any method which wants to update it\nfunc (s *Sessions) updateCookie(ctx *context.Context, sid string, expires time.Duration, options ...context.CookieOption) {\n\tcookie := &http.Cookie{}\n\n\t// The RFC makes no mention of encoding url value, so here I think to encode both sessionid key and the value using the safe(to put and to use as cookie) url-encoding\n\tcookie.Name = s.config.Cookie\n\tcookie.Value = sid\n\tcookie.Path = \"/\"\n\tcookie.HttpOnly = true\n\n\t// MaxAge=0 means no 'Max-Age' attribute specified.\n\t// MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0'\n\t// MaxAge>0 means Max-Age attribute present and given in seconds\n\tif expires >= 0 {\n\t\tif expires == 0 { // unlimited life\n\t\t\tcookie.Expires = context.CookieExpireUnlimited\n\t\t} else { // > 0\n\t\t\tcookie.Expires = time.Now().Add(expires)\n\t\t}\n\t\tcookie.MaxAge = int(time.Until(cookie.Expires).Seconds())\n\t}\n\n\ts.upsertCookie(ctx, cookie, options)\n}\n\nfunc (s *Sessions) upsertCookie(ctx *context.Context, cookie *http.Cookie, cookieOptions []context.CookieOption) {\n\topts := s.cookieOptions\n\tif len(cookieOptions) > 0 {\n\t\topts = append(opts, cookieOptions...)\n\t}\n\n\tctx.UpsertCookie(cookie, opts...)\n}\n\nfunc (s *Sessions) getCookieValue(ctx *context.Context, cookieOptions []context.CookieOption) string {\n\tc := s.getCookie(ctx, cookieOptions)\n\tif c == nil {\n\t\treturn \"\"\n\t}\n\treturn c.Value\n}\n\nfunc (s *Sessions) getCookie(ctx *context.Context, cookieOptions []context.CookieOption) *http.Cookie {\n\topts := s.cookieOptions\n\tif len(cookieOptions) > 0 {\n\t\topts = append(opts, cookieOptions...)\n\t}\n\n\tcookie, err := ctx.GetRequestCookie(s.config.Cookie, opts...)\n\tif err != nil {\n\t\treturn nil\n\t}\n\n\tcookie.Value, _ = url.QueryUnescape(cookie.Value)\n\treturn cookie\n}\n\n// Start creates or retrieves an existing session for the particular request.\n// Note that `Start` method will not respect configuration's `AllowReclaim`, `DisableSubdomainPersistence`, `CookieSecureTLS`,\n// and `Encoding` settings.\n// Register sessions as a middleware through the `Handler` method instead,\n// which provides automatic resolution of a *sessions.Session input argument\n// on MVC and APIContainer as well.\n//\n// NOTE: Use `app.Use(sess.Handler())` instead, avoid using `Start` manually.\nfunc (s *Sessions) Start(ctx *context.Context, cookieOptions ...context.CookieOption) *Session {\n\t// cookieValue := s.getCookieValue(ctx, cookieOptions)\n\tcookie := s.getCookie(ctx, cookieOptions)\n\tif cookie != nil {\n\t\tsid := cookie.Value\n\t\tif sid == \"\" { // rare case: a client may contains a cookie with session name but with empty value.\n\t\t\t// ctx.RemoveCookie(cookie.Name)\n\t\t\tcookie = nil\n\t\t} else if cookie.Expires.Add(time.Second).After(time.Now()) { // rare case: of custom clients that may hold expired cookies.\n\t\t\ts.DestroyByID(sid)\n\t\t\t// ctx.RemoveCookie(cookie.Name)\n\t\t\tcookie = nil\n\t\t} else {\n\t\t\t// rare case: new expiration configuration that it's lower\n\t\t\t// than the previous setting.\n\t\t\texpiresTime := time.Now().Add(s.config.Expires)\n\t\t\tif cookie.Expires.After(expiresTime) {\n\t\t\t\ts.DestroyByID(sid)\n\t\t\t\t//\tctx.RemoveCookie(cookie.Name)\n\t\t\t\tcookie = nil\n\t\t\t} else {\n\t\t\t\t//\tuntilExpirationDur := time.Until(cookie.Expires)\n\t\t\t\t// ^ this should be\n\t\t\t\treturn s.provider.Read(s, sid, s.config.Expires) // cookie exists and it's valid, let's return its session.\n\t\t\t}\n\t\t}\n\t}\n\n\t// Cookie doesn't exist, let's generate a session and set a cookie.\n\tsid := s.config.SessionIDGenerator(ctx)\n\n\tsess := s.provider.Init(s, sid, s.config.Expires)\n\t// n := s.provider.db.Len(sid)\n\t// fmt.Printf(\"db.Len(%s) = %d\\n\", sid, n)\n\t// if n > 0 {\n\t// \ts.provider.db.Visit(sid, func(key string, value any) {\n\t// \t\tfmt.Printf(\"%s=%s\\n\", key, value)\n\t// \t})\n\t// }\n\ts.updateCookie(ctx, sid, s.config.Expires, cookieOptions...)\n\treturn sess\n}\n\nconst sessionContextKey = \"iris.session\"\n\n// Handler returns a sessions middleware to register on application routes.\n// To return the request's Session call the `Get(ctx)` package-level function.\n//\n// Call `Handler()` once per sessions manager.\nfunc (s *Sessions) Handler(requestOptions ...context.CookieOption) context.Handler {\n\treturn func(ctx *context.Context) {\n\t\tsession := s.Start(ctx, requestOptions...) // this cookie's end-developer's custom options.\n\n\t\tctx.Values().Set(sessionContextKey, session)\n\t\tctx.Next()\n\n\t\ts.provider.EndRequest(ctx, session)\n\t}\n}\n\n// Get returns a *Session from the same request life cycle,\n// can be used inside a chain of handlers of a route.\n//\n// The `Sessions.Start` should be called previously,\n// e.g. register the `Sessions.Handler` as middleware.\n// Then call `Get` package-level function as many times as you want.\n// Note: It will return nil if the session got destroyed by the same request.\n// If you need to destroy and start a new session in the same request you need to call\n// sessions manager's `Start` method after Destroy.\nfunc Get(ctx *context.Context) *Session {\n\tif v := ctx.Values().Get(sessionContextKey); v != nil {\n\t\tif sess, ok := v.(*Session); ok {\n\t\t\treturn sess\n\t\t}\n\t}\n\n\t// ctx.Application().Logger().Debugf(\"Sessions: Get: no session found, prior Destroy(ctx) calls in the same request should follow with a Start(ctx) call too\")\n\treturn nil\n}\n\n// StartWithPath same as `Start` but it explicitly accepts the cookie path option.\nfunc (s *Sessions) StartWithPath(ctx *context.Context, path string) *Session {\n\treturn s.Start(ctx, context.CookiePath(path))\n}\n\n// ShiftExpiration move the expire date of a session to a new date\n// by using session default timeout configuration.\n// It will return `ErrNotImplemented` if a database is used and it does not support this feature, yet.\nfunc (s *Sessions) ShiftExpiration(ctx *context.Context, cookieOptions ...context.CookieOption) error {\n\treturn s.UpdateExpiration(ctx, s.config.Expires, cookieOptions...)\n}\n\n// UpdateExpiration change expire date of a session to a new date\n// by using timeout value passed by `expires` receiver.\n// It will return `ErrNotFound` when trying to update expiration on a non-existence or not valid session entry.\n// It will return `ErrNotImplemented` if a database is used and it does not support this feature, yet.\nfunc (s *Sessions) UpdateExpiration(ctx *context.Context, expires time.Duration, cookieOptions ...context.CookieOption) error {\n\tcookieValue := s.getCookieValue(ctx, cookieOptions)\n\tif cookieValue == \"\" {\n\t\treturn ErrNotFound\n\t}\n\n\t// we should also allow it to expire when the browser closed\n\terr := s.provider.UpdateExpiration(cookieValue, expires)\n\tif err == nil || expires == -1 {\n\t\ts.updateCookie(ctx, cookieValue, expires, cookieOptions...)\n\t}\n\n\treturn err\n}\n\n// DestroyListener is the form of a destroy listener.\n// Look `OnDestroy` for more.\ntype DestroyListener func(sid string)\n\n// OnDestroy registers one or more destroy listeners.\n// A destroy listener is fired when a session has been removed entirely from the server (the entry) and client-side (the cookie).\n// Note that if a destroy listener is blocking, then the session manager will delay respectfully,\n// use a goroutine inside the listener to avoid that behavior.\nfunc (s *Sessions) OnDestroy(listeners ...DestroyListener) {\n\tfor _, ln := range listeners {\n\t\ts.provider.registerDestroyListener(ln)\n\t}\n}\n\n// Destroy removes the session data, the associated cookie\n// and the Context's session value.\n// Next calls of `sessions.Get` will occur to a nil Session,\n// use `Sessions#Start` method for renewal\n// or use the Session's Destroy method which does keep the session entry with its values cleared.\nfunc (s *Sessions) Destroy(ctx *context.Context) {\n\tcookieValue := s.getCookieValue(ctx, nil)\n\tif cookieValue == \"\" { // nothing to destroy\n\t\treturn\n\t}\n\n\tctx.Values().Remove(sessionContextKey)\n\n\tctx.RemoveCookie(s.config.Cookie, s.cookieOptions...)\n\ts.provider.Destroy(cookieValue)\n}\n\n// DestroyByID removes the session entry\n// from the server-side memory (and database if registered).\n// Client's session cookie will still exist but it will be reseted on the next request.\n//\n// It's safe to use it even if you are not sure if a session with that id exists.\n//\n// Note: the sid should be the original one (i.e: fetched by a store )\n// it's not decoded.\nfunc (s *Sessions) DestroyByID(sid string) {\n\ts.provider.Destroy(sid)\n}\n\n// DestroyAll removes all sessions\n// from the server-side memory (and database if registered).\n// Client's session cookie will still exist but it will be reseted on the next request.\nfunc (s *Sessions) DestroyAll() {\n\ts.provider.DestroyAll()\n}\n"
  },
  {
    "path": "sessions/sessions_test.go",
    "content": "package sessions_test\n\nimport (\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/httptest\"\n\t\"github.com/kataras/iris/v12/sessions\"\n)\n\nfunc TestSessions(t *testing.T) {\n\tapp := iris.New()\n\n\tsess := sessions.New(sessions.Config{Cookie: \"mycustomsessionid\"})\n\tapp.Use(sess.Handler())\n\n\ttestSessions(t, app)\n}\n\nconst (\n\ttestEnableSubdomain = true\n)\n\nfunc testSessions(t *testing.T, app *iris.Application) {\n\tvalues := map[string]any{\n\t\t\"Name\":   \"iris\",\n\t\t\"Months\": \"4\",\n\t\t\"Secret\": \"dsads£2132215£%%Ssdsa\",\n\t}\n\n\twriteValues := func(ctx *context.Context) {\n\t\ts := sessions.Get(ctx)\n\t\tsessValues := s.GetAll()\n\n\t\terr := ctx.JSON(sessValues)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t}\n\n\tif testEnableSubdomain {\n\t\tapp.Party(\"subdomain.\").Get(\"/get\", writeValues)\n\t}\n\n\tapp.Post(\"/set\", func(ctx *context.Context) {\n\t\ts := sessions.Get(ctx)\n\t\tvals := make(map[string]any)\n\t\tif err := ctx.ReadJSON(&vals); err != nil {\n\t\t\tt.Fatalf(\"Cannot read JSON. Trace %s\", err.Error())\n\t\t}\n\t\tfor k, v := range vals {\n\t\t\ts.Set(k, v)\n\t\t}\n\t})\n\n\tapp.Get(\"/get\", func(ctx *context.Context) {\n\t\twriteValues(ctx)\n\t})\n\n\tapp.Get(\"/clear\", func(ctx *context.Context) {\n\t\tsessions.Get(ctx).Clear()\n\t\twriteValues(ctx)\n\t})\n\n\tapp.Get(\"/destroy\", func(ctx *context.Context) {\n\t\tsession := sessions.Get(ctx)\n\t\tif session.IsNew() {\n\t\t\tt.Fatal(\"expected session not to be nil on destroy\")\n\t\t}\n\n\t\tsession.Man.Destroy(ctx)\n\n\t\tif sessions.Get(ctx) != nil {\n\t\t\tt.Fatal(\"expected session inside Context to be nil after Manager's Destroy call\")\n\t\t}\n\n\t\tctx.JSON(struct{}{})\n\t\t// the cookie and all values should be empty\n\t})\n\n\t// cookie should be new.\n\tapp.Get(\"/after_destroy_renew\", func(ctx *context.Context) {\n\t\tisNew := sessions.Get(ctx).IsNew()\n\t\tctx.Writef(\"%v\", isNew)\n\t})\n\n\tapp.Get(\"/multi_start_set_get\", func(ctx *context.Context) {\n\t\ts := sessions.Get(ctx)\n\t\ts.Set(\"key\", \"value\")\n\t\tctx.Next()\n\t}, func(ctx *context.Context) {\n\t\ts := sessions.Get(ctx)\n\t\t_, err := ctx.WriteString(s.GetString(\"key\"))\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t})\n\n\te := httptest.New(t, app, httptest.URL(\"http://example.com\"))\n\n\te.POST(\"/set\").WithJSON(values).Expect().Status(iris.StatusOK).Cookies().NotEmpty()\n\te.GET(\"/get\").Expect().Status(iris.StatusOK).JSON().Object().Equal(values)\n\tif testEnableSubdomain {\n\t\tes := e.Builder(func(req *httptest.Request) {\n\t\t\treq.WithURL(\"http://subdomain.example.com\")\n\t\t})\n\t\tes.Request(\"GET\", \"/get\").Expect().Status(iris.StatusOK).JSON().Object().Equal(values)\n\t}\n\t// test destroy which also clears first\n\td := e.GET(\"/destroy\").Expect().Status(iris.StatusOK)\n\td.JSON().Object().Empty()\n\n\td = e.GET(\"/after_destroy_renew\").Expect().Status(iris.StatusOK)\n\td.Body().IsEqual(\"true\")\n\td.Cookies().NotEmpty()\n\n\t// set and clear again\n\te.POST(\"/set\").WithJSON(values).Expect().Status(iris.StatusOK)\n\te.GET(\"/clear\").Expect().Status(iris.StatusOK).JSON().Object().Empty()\n\n\t// test start on the same request but more than one times\n\n\te.GET(\"/multi_start_set_get\").Expect().Status(iris.StatusOK).Body().IsEqual(\"value\")\n}\n\nfunc TestFlashMessages(t *testing.T) {\n\tapp := iris.New()\n\n\tsess := sessions.New(sessions.Config{Cookie: \"mycustomsessionid\"})\n\n\tvalueSingleKey := \"Name\"\n\tvalueSingleValue := \"iris-sessions\"\n\n\tvalues := map[string]any{\n\t\tvalueSingleKey: valueSingleValue,\n\t\t\"Days\":         \"1\",\n\t\t\"Secret\":       \"dsads£2132215£%%Ssdsa\",\n\t}\n\n\twriteValues := func(ctx *context.Context, values map[string]any) error {\n\t\treturn ctx.JSON(values)\n\t}\n\n\tapp.Post(\"/set\", func(ctx *context.Context) {\n\t\tvals := make(map[string]any)\n\t\tif err := ctx.ReadJSON(&vals); err != nil {\n\t\t\tt.Fatalf(\"Cannot readjson. Trace %s\", err.Error())\n\t\t}\n\t\ts := sess.Start(ctx)\n\t\tfor k, v := range vals {\n\t\t\ts.SetFlash(k, v)\n\t\t}\n\n\t\tctx.StatusCode(iris.StatusOK)\n\t})\n\n\twriteFlashValues := func(ctx *context.Context) {\n\t\ts := sess.Start(ctx)\n\n\t\tflashes := s.GetFlashes()\n\t\tif err := writeValues(ctx, flashes); err != nil {\n\t\t\tt.Fatalf(\"While serialize the flash values: %s\", err.Error())\n\t\t}\n\t}\n\n\tapp.Get(\"/get_single\", func(ctx *context.Context) {\n\t\ts := sess.Start(ctx)\n\t\tflashMsgString := s.GetFlashString(valueSingleKey)\n\t\tctx.WriteString(flashMsgString)\n\t})\n\n\tapp.Get(\"/get\", func(ctx *context.Context) {\n\t\twriteFlashValues(ctx)\n\t})\n\n\tapp.Get(\"/clear\", func(ctx *context.Context) {\n\t\ts := sess.Start(ctx)\n\t\ts.ClearFlashes()\n\t\twriteFlashValues(ctx)\n\t})\n\n\tapp.Get(\"/destroy\", func(ctx *context.Context) {\n\t\tsess.Destroy(ctx)\n\t\twriteFlashValues(ctx)\n\t\tctx.StatusCode(iris.StatusOK)\n\t\t// the cookie and all values should be empty\n\t})\n\n\t// request cookie should be empty\n\tapp.Get(\"/after_destroy\", func(ctx *context.Context) {\n\t\tctx.StatusCode(iris.StatusOK)\n\t})\n\n\te := httptest.New(t, app, httptest.URL(\"http://example.com\"))\n\n\te.POST(\"/set\").WithJSON(values).Expect().Status(iris.StatusOK).Cookies().NotEmpty()\n\t// get all\n\te.GET(\"/get\").Expect().Status(iris.StatusOK).JSON().Object().Equal(values)\n\t// get the same flash on other request should return nothing because the flash message is removed after fetch once\n\te.GET(\"/get\").Expect().Status(iris.StatusOK).JSON().Object().Empty()\n\t// test destroy which also clears first\n\td := e.GET(\"/destroy\").Expect().Status(iris.StatusOK)\n\td.JSON().Object().Empty()\n\te.GET(\"/after_destroy\").Expect().Status(iris.StatusOK).Cookies().Empty()\n\t// set and clear again\n\te.POST(\"/set\").WithJSON(values).Expect().Status(iris.StatusOK).Cookies().NotEmpty()\n\te.GET(\"/clear\").Expect().Status(iris.StatusOK).JSON().Object().Empty()\n\n\t// set again in order to take the single one ( we don't test Cookies.NotEmpty because httpexpect default conf reads that from the request-only)\n\te.POST(\"/set\").WithJSON(values).Expect().Status(iris.StatusOK)\n\te.GET(\"/get_single\").Expect().Status(iris.StatusOK).Body().IsEqual(valueSingleValue)\n}\n\nfunc TestSessionsUpdateExpiration(t *testing.T) {\n\tapp := iris.New()\n\n\tcookieName := \"mycustomsessionid\"\n\n\tsess := sessions.New(sessions.Config{\n\t\tCookie:       cookieName,\n\t\tExpires:      30 * time.Minute,\n\t\tAllowReclaim: true,\n\t})\n\n\tapp.Use(sess.Handler())\n\n\ttype response struct {\n\t\tSessionID string `json:\"sessionID\"`\n\t\tLogged    bool   `json:\"logged\"`\n\t}\n\n\tvar writeResponse = func(ctx *context.Context) {\n\t\tsession := sessions.Get(ctx)\n\t\tctx.JSON(response{\n\t\t\tSessionID: session.ID(),\n\t\t\tLogged:    session.GetBooleanDefault(\"logged\", false),\n\t\t})\n\t}\n\n\tapp.Get(\"/get\", func(ctx *context.Context) {\n\t\twriteResponse(ctx)\n\t})\n\n\tapp.Get(\"/set\", func(ctx iris.Context) {\n\t\tsessions.Get(ctx).Set(\"logged\", true)\n\t\twriteResponse(ctx)\n\t})\n\n\tapp.Post(\"/remember_me\", func(ctx iris.Context) {\n\t\t// re-sends the cookie with the new Expires and MaxAge fields,\n\t\t// test checks that on same session id too.\n\t\tsessions.Get(ctx).Man.UpdateExpiration(ctx, 24*time.Hour)\n\t\twriteResponse(ctx)\n\t})\n\n\tapp.Get(\"/destroy\", func(ctx iris.Context) {\n\t\tsessions.Get(ctx).Man.Destroy(ctx) // this will delete the cookie too.\n\t})\n\n\te := httptest.New(t, app, httptest.URL(\"http://example.com\"))\n\n\ttt := e.GET(\"/set\").Expect().Status(httptest.StatusOK)\n\ttt.Cookie(cookieName).MaxAge().InRange(29*time.Minute, 30*time.Minute)\n\tsessionID := tt.JSON().Object().Raw()[\"sessionID\"].(string)\n\n\texpectedResponse := response{SessionID: sessionID, Logged: true}\n\te.GET(\"/get\").Expect().Status(httptest.StatusOK).\n\t\tJSON().IsEqual(expectedResponse)\n\n\ttt = e.POST(\"/remember_me\").Expect().Status(httptest.StatusOK)\n\ttt.Cookie(cookieName).MaxAge().InRange(23*time.Hour, 24*time.Hour)\n\ttt.JSON().IsEqual(expectedResponse)\n\n\t// Test call `UpdateExpiration` when cookie is firstly created.\n\te.GET(\"/destroy\").Expect().Status(httptest.StatusOK)\n\te.POST(\"/remember_me\").Expect().Status(httptest.StatusOK).\n\t\tCookie(cookieName).MaxAge().InRange(23*time.Hour, 24*time.Hour)\n}\n\n// go test -v -count=100 -run=TestSessionsUpdateExpirationConcurrently$\n// #1488\nfunc TestSessionsUpdateExpirationConcurrently(t *testing.T) {\n\tcookieName := \"mycustomsessionid\"\n\tsess := sessions.New(sessions.Config{\n\t\tCookie:       cookieName,\n\t\tExpires:      30 * time.Minute,\n\t\tAllowReclaim: true,\n\t})\n\n\tapp := iris.New()\n\tapp.Use(sess.Handler())\n\tapp.Use(func(ctx iris.Context) {\n\t\t// session will expire after 30 minute at the last visit\n\t\tsess.UpdateExpiration(ctx, 30*time.Minute)\n\t\tctx.Next()\n\t})\n\n\tapp.Get(\"/get\", func(ctx iris.Context) {\n\t\tctx.WriteString(sessions.Get(ctx).ID())\n\t})\n\n\te := httptest.New(t, app, httptest.URL(\"http://example.com\"))\n\n\tid := e.GET(\"/get\").Expect().Status(httptest.StatusOK).Body().Raw()\n\n\ti := 0\n\twg := sync.WaitGroup{}\n\twg.Add(1000)\n\tfor i < 1000 {\n\t\tgo func() {\n\t\t\ttt := e.GET(\"/get\").Expect().Status(httptest.StatusOK)\n\t\t\ttt.Body().IsEqual(id)\n\t\t\ttt.Cookie(cookieName).MaxAge().InRange(29*time.Minute, 30*time.Minute)\n\t\t\twg.Done()\n\t\t}()\n\t\ti++\n\t}\n\twg.Wait()\n\ttt := e.GET(\"/get\").Expect()\n\ttt.Status(httptest.StatusOK).Body().IsEqual(id)\n\ttt.Cookie(cookieName).MaxAge().InRange(29*time.Minute, 30*time.Minute)\n}\n"
  },
  {
    "path": "sessions/transcoding.go",
    "content": "package sessions\n\nimport (\n\t\"bytes\"\n\t\"encoding/gob\"\n\t\"encoding/json\"\n\t\"reflect\"\n\t\"time\"\n)\n\nfunc init() {\n\tgob.Register(time.Time{})\n}\n\ntype (\n\t// Marshaler is the common marshaler interface, used by transcoder.\n\tMarshaler interface {\n\t\tMarshal(any) ([]byte, error)\n\t}\n\t// Unmarshaler is the common unmarshaler interface, used by transcoder.\n\tUnmarshaler interface {\n\t\tUnmarshal([]byte, any) error\n\t}\n\t// Transcoder is the interface that transcoders should implement, it includes just the `Marshaler` and the `Unmarshaler`.\n\tTranscoder interface {\n\t\tMarshaler\n\t\tUnmarshaler\n\t}\n)\n\ntype (\n\tdefaultTranscoder struct{}\n\t// GobTranscoder can be set to `DefaultTranscoder` to modify the database(s) transcoder.\n\tGobTranscoder struct{}\n)\n\nvar (\n\t_ Transcoder = (*defaultTranscoder)(nil)\n\t_ Transcoder = (*GobTranscoder)(nil)\n\n\t// DefaultTranscoder is the default transcoder across databases (when `UseDatabase` is used).\n\t//\n\t// The default database's values encoder and decoder\n\t// calls the value's `Marshal/Unmarshal` methods (if any)\n\t// otherwise JSON is selected,\n\t// the JSON format can be stored to any database and\n\t// it supports both builtin language types(e.g. string, int) and custom struct values.\n\t// Also, and the most important, the values can be\n\t// retrieved/logged/monitored by a third-party program\n\t// written in any other language as well.\n\t//\n\t// You can change this behavior by registering a custom `Transcoder`.\n\t// Iris provides a `GobTranscoder` which is mostly suitable\n\t// if your session values are going to be custom Go structs.\n\t// Select this if you always retrieving values through Go.\n\t// Don't forget to initialize a call of gob.Register when necessary.\n\t// Read https://golang.org/pkg/encoding/gob/ for more.\n\t//\n\t// You can also implement your own `sessions.Transcoder` and use it,\n\t// i.e: a transcoder which will allow(on Marshal: return its byte representation and nil error)\n\t// or dissalow(on Marshal: return non nil error) certain types.\n\t//\n\t// sessions.DefaultTranscoder = sessions.GobTranscoder{}\n\tDefaultTranscoder Transcoder = defaultTranscoder{}\n)\n\nfunc (defaultTranscoder) Marshal(value any) ([]byte, error) {\n\tif tr, ok := value.(Marshaler); ok {\n\t\treturn tr.Marshal(value)\n\t}\n\n\tif jsonM, ok := value.(json.Marshaler); ok {\n\t\treturn jsonM.MarshalJSON()\n\t}\n\n\treturn json.Marshal(value)\n}\n\nfunc (defaultTranscoder) Unmarshal(b []byte, outPtr any) error {\n\tif tr, ok := outPtr.(Unmarshaler); ok {\n\t\treturn tr.Unmarshal(b, outPtr)\n\t}\n\n\tif jsonUM, ok := outPtr.(json.Unmarshaler); ok {\n\t\treturn jsonUM.UnmarshalJSON(b)\n\t}\n\n\treturn json.Unmarshal(b, outPtr)\n}\n\n// Marshal returns the gob encoding of \"value\".\nfunc (GobTranscoder) Marshal(value any) ([]byte, error) {\n\tvar (\n\t\tw   = new(bytes.Buffer)\n\t\tenc = gob.NewEncoder(w)\n\t\terr error\n\t)\n\n\tif v, ok := value.(reflect.Value); ok {\n\t\terr = enc.EncodeValue(v)\n\t} else {\n\t\terr = enc.Encode(&value)\n\t}\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn w.Bytes(), nil\n}\n\n// Unmarshal parses the gob-encoded data \"b\" and stores the result\n// in the value pointed to by \"outPtr\".\nfunc (GobTranscoder) Unmarshal(b []byte, outPtr any) error {\n\tdec := gob.NewDecoder(bytes.NewBuffer(b))\n\n\tif v, ok := outPtr.(reflect.Value); ok {\n\t\treturn dec.DecodeValue(v)\n\t}\n\n\treturn dec.Decode(outPtr)\n}\n"
  },
  {
    "path": "versioning/deprecation.go",
    "content": "package versioning\n\nimport (\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/context\"\n)\n\n// The response header keys when a resource is deprecated by the server.\nconst (\n\tAPIWarnHeader            = \"X-Api-Warn\"\n\tAPIDeprecationDateHeader = \"X-Api-Deprecation-Date\"\n\tAPIDeprecationInfoHeader = \"X-Api-Deprecation-Info\"\n)\n\n// DeprecationOptions describes the deprecation headers key-values.\n// - \"X-Api-Warn\": options.WarnMessage\n// - \"X-Api-Deprecation-Date\": context.FormatTime(ctx, options.DeprecationDate))\n// - \"X-Api-Deprecation-Info\": options.DeprecationInfo\ntype DeprecationOptions struct {\n\tWarnMessage     string\n\tDeprecationDate time.Time\n\tDeprecationInfo string\n}\n\n// ShouldHandle reports whether the deprecation headers should be present or no.\nfunc (opts DeprecationOptions) ShouldHandle() bool {\n\treturn opts.WarnMessage != \"\" || !opts.DeprecationDate.IsZero() || opts.DeprecationInfo != \"\"\n}\n\n// DefaultDeprecationOptions are the default deprecation options,\n// it defaults the \"X-API-Warn\" header to a generic message.\nvar DefaultDeprecationOptions = DeprecationOptions{\n\tWarnMessage: \"WARNING! You are using a deprecated version of this API.\",\n}\n\n// WriteDeprecated writes the deprecated response headers\n// based on the given \"options\".\n// It can be used inside a middleware.\n//\n// See `Deprecated` to wrap an existing handler instead.\nfunc WriteDeprecated(ctx *context.Context, options DeprecationOptions) {\n\tif options.WarnMessage == \"\" {\n\t\toptions.WarnMessage = DefaultDeprecationOptions.WarnMessage\n\t}\n\n\tctx.Header(APIWarnHeader, options.WarnMessage)\n\n\tif !options.DeprecationDate.IsZero() {\n\t\tctx.Header(APIDeprecationDateHeader, context.FormatTime(ctx, options.DeprecationDate))\n\t}\n\n\tif options.DeprecationInfo != \"\" {\n\t\tctx.Header(APIDeprecationInfoHeader, options.DeprecationInfo)\n\t}\n}\n\n// Deprecated wraps an existing API handler and\n// marks it as a deprecated one.\n// Deprecated can be used to tell the clients that\n// a newer version of that specific resource is available instead.\nfunc Deprecated(handler context.Handler, options DeprecationOptions) context.Handler {\n\treturn func(ctx *context.Context) {\n\t\tWriteDeprecated(ctx, options)\n\t\thandler(ctx)\n\t}\n}\n"
  },
  {
    "path": "versioning/deprecation_test.go",
    "content": "package versioning_test\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/httptest\"\n\t\"github.com/kataras/iris/v12/versioning\"\n)\n\nfunc TestDeprecated(t *testing.T) {\n\tapp := iris.New()\n\n\twriteVesion := func(ctx iris.Context) {\n\t\tctx.WriteString(versioning.GetVersion(ctx))\n\t}\n\n\topts := versioning.DeprecationOptions{\n\t\tWarnMessage:     \"deprecated, see <this link>\",\n\t\tDeprecationDate: time.Now().UTC(),\n\t\tDeprecationInfo: \"a bigger version is available, see <this link> for more information\",\n\t}\n\tapp.Get(\"/\", versioning.Deprecated(writeVesion, opts))\n\n\te := httptest.New(t, app)\n\n\tex := e.GET(\"/\").WithHeader(versioning.AcceptVersionHeaderKey, \"1.0\").Expect()\n\tex.Status(iris.StatusOK).Body().IsEqual(\"1.0\")\n\tex.Header(\"X-API-Warn\").Equal(opts.WarnMessage)\n\texpectedDateStr := opts.DeprecationDate.Format(app.ConfigurationReadOnly().GetTimeFormat())\n\tex.Header(\"X-API-Deprecation-Date\").Equal(expectedDateStr)\n}\n"
  },
  {
    "path": "versioning/group.go",
    "content": "package versioning\n\nimport (\n\t\"strings\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/core/router\"\n\n\t\"github.com/blang/semver/v4\"\n)\n\n// API is a type alias of router.Party.\n// This is required in order for a Group instance\n// to implement the Party interface without field conflict.\ntype API = router.Party\n\n// Group represents a group of resources that should\n// be handled based on a version requested by the client.\n// See `NewGroup` for more.\ntype Group struct {\n\tAPI\n\n\tvalidate    semver.Range\n\tdeprecation DeprecationOptions\n}\n\n// NewGroup returns a version Group based on the given \"version\" constraint.\n// Group completes the Party interface.\n// The returned Group wraps a cloned Party of the given \"r\" Party therefore,\n// any changes to its parent won't affect this one (e.g. register global middlewares afterwards).\n//\n// A version is extracted through the versioning.GetVersion function:\n//\n//\tAccept-Version: 1.0.0\n//\tAccept: application/json; version=1.0.0\n//\n// You can customize it by setting a version based on the request context:\n//\n//\t api.Use(func(ctx *context.Context) {\n//\t\t if version := ctx.URLParam(\"version\"); version != \"\" {\n//\t\t  SetVersion(ctx, version)\n//\t\t }\n//\n//\t  ctx.Next()\n//\t })\n//\n// OR:\n//\n//\tapi.Use(versioning.FromQuery(\"version\", \"\"))\n//\n// Examples at: _examples/routing/versioning\n// Usage:\n//\n//\tapp := iris.New()\n//\tapi := app.Party(\"/api\")\n//\tv1 := versioning.NewGroup(api, \">=1.0.0 <2.0.0\")\n//\tv1.Get/Post/Put/Delete...\n//\n// Valid ranges are:\n//   - \"<1.0.0\"\n//   - \"<=1.0.0\"\n//   - \">1.0.0\"\n//   - \">=1.0.0\"\n//   - \"1.0.0\", \"=1.0.0\", \"==1.0.0\"\n//   - \"!1.0.0\", \"!=1.0.0\"\n//\n// A Range can consist of multiple ranges separated by space:\n// Ranges can be linked by logical AND:\n//   - \">1.0.0 <2.0.0\" would match between both ranges, so \"1.1.1\" and \"1.8.7\"\n//\n// but not \"1.0.0\" or \"2.0.0\"\n//   - \">1.0.0 <3.0.0 !2.0.3-beta.2\" would match every version between 1.0.0 and 3.0.0\n//\n// except 2.0.3-beta.2\n//\n// Ranges can also be linked by logical OR:\n//   - \"<2.0.0 || >=3.0.0\" would match \"1.x.x\" and \"3.x.x\" but not \"2.x.x\"\n//\n// AND has a higher precedence than OR. It's not possible to use brackets.\n//\n// Ranges can be combined by both AND and OR\n//\n//   - `>1.0.0 <2.0.0 || >3.0.0 !4.2.1` would match `1.2.3`, `1.9.9`, `3.1.1`,\n//\n// but not `4.2.1`, `2.1.1`\nfunc NewGroup(r API, version string) *Group {\n\tversion = strings.ReplaceAll(version, \",\", \" \")\n\tversion = strings.TrimSpace(version)\n\n\tverRange, err := semver.ParseRange(version)\n\tif err != nil {\n\t\tr.Logger().Errorf(\"versioning: %s: %s\", r.GetRelPath(), strings.ToLower(err.Error()))\n\t\treturn &Group{API: r}\n\t}\n\n\t// Clone this one.\n\tr = r.Party(\"/\")\n\n\t// Note that this feature alters the RouteRegisterRule to RouteOverlap\n\t// the RouteOverlap rule does not contain any performance downside\n\t// but it's good to know that if you registered other mode, this wanna change it.\n\tr.SetRegisterRule(router.RouteOverlap)\n\n\thandler := makeHandler(verRange)\n\t// This is required in order to not populate this middleware to the next group.\n\tr.UseOnce(handler)\n\t// This is required for versioned custom error handlers,\n\t// of course if the parent registered one then this will do nothing.\n\tr.UseError(handler)\n\n\treturn &Group{\n\t\tAPI:      r,\n\t\tvalidate: verRange,\n\t}\n}\n\n// Deprecated marks this group and all its versioned routes\n// as deprecated versions of that endpoint.\nfunc (g *Group) Deprecated(options DeprecationOptions) *Group {\n\t// store it for future use, e.g. collect all deprecated APIs and notify the developer.\n\tg.deprecation = options\n\n\tg.API.UseOnce(func(ctx *context.Context) {\n\t\tWriteDeprecated(ctx, options)\n\t\tctx.Next()\n\t})\n\treturn g\n}\n\nfunc makeHandler(validate semver.Range) context.Handler {\n\treturn func(ctx *context.Context) {\n\t\tif !matchVersionRange(ctx, validate) {\n\t\t\t// The overlapped handler has an exception\n\t\t\t// of a type of context.NotFound (which versioning.ErrNotFound wraps)\n\t\t\t// to clear the status code\n\t\t\t// and the error to ignore this\n\t\t\t// when available match version exists (see `NewGroup`).\n\t\t\tif h := NotFoundHandler; h != nil {\n\t\t\t\th(ctx)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tctx.Next()\n\t}\n}\n"
  },
  {
    "path": "versioning/group_test.go",
    "content": "package versioning_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/httptest\"\n\t\"github.com/kataras/iris/v12/versioning\"\n)\n\nconst (\n\tv10Response = \"v1.0 handler\"\n\tv2Response  = \"v2.x handler\"\n)\n\nfunc sendHandler(contents string) iris.Handler {\n\treturn func(ctx iris.Context) {\n\t\tctx.WriteString(contents)\n\t}\n}\n\nfunc TestNewGroup(t *testing.T) {\n\tapp := iris.New()\n\n\tuserAPI := app.Party(\"/api/user\")\n\t// [... static serving, middlewares and etc goes here].\n\n\tuserAPIV10 := versioning.NewGroup(userAPI, \"1.0.0\").Deprecated(versioning.DefaultDeprecationOptions)\n\n\tuserAPIV10.Get(\"/\", sendHandler(v10Response))\n\tuserAPIV2 := versioning.NewGroup(userAPI, \">= 2.0.0 < 3.0.0\")\n\n\tuserAPIV2.Get(\"/\", sendHandler(v2Response))\n\tuserAPIV2.Post(\"/\", sendHandler(v2Response))\n\tuserAPIV2.Put(\"/other\", sendHandler(v2Response))\n\n\te := httptest.New(t, app)\n\n\tex := e.GET(\"/api/user\").WithHeader(versioning.AcceptVersionHeaderKey, \"1.0.0\").Expect()\n\tex.Status(iris.StatusOK).Body().IsEqual(v10Response)\n\tex.Header(\"X-API-Warn\").Equal(versioning.DefaultDeprecationOptions.WarnMessage)\n\n\te.GET(\"/api/user\").WithHeader(versioning.AcceptVersionHeaderKey, \"2.0.0\").Expect().\n\t\tStatus(iris.StatusOK).Body().IsEqual(v2Response)\n\te.GET(\"/api/user\").WithHeader(versioning.AcceptVersionHeaderKey, \"2.1.0\").Expect().\n\t\tStatus(iris.StatusOK).Body().IsEqual(v2Response)\n\te.GET(\"/api/user\").WithHeader(versioning.AcceptVersionHeaderKey, \"2.9.9\").Expect().\n\t\tStatus(iris.StatusOK).Body().IsEqual(v2Response)\n\te.POST(\"/api/user\").WithHeader(versioning.AcceptVersionHeaderKey, \"2.0.0\").Expect().\n\t\tStatus(iris.StatusOK).Body().IsEqual(v2Response)\n\te.PUT(\"/api/user/other\").WithHeader(versioning.AcceptVersionHeaderKey, \"2.9.0\").Expect().\n\t\tStatus(iris.StatusOK).Body().IsEqual(v2Response)\n\n\te.GET(\"/api/user\").WithHeader(versioning.AcceptVersionHeaderKey, \"3.0\").Expect().\n\t\tStatus(iris.StatusNotImplemented).Body().IsEqual(\"version not found\")\n}\n"
  },
  {
    "path": "versioning/version.go",
    "content": "package versioning\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\n\t\"github.com/blang/semver/v4\"\n)\n\nconst (\n\t// APIVersionResponseHeader the response header which its value contains\n\t// the normalized semver matched version.\n\tAPIVersionResponseHeader = \"X-Api-Version\"\n\t// AcceptVersionHeaderKey is the header key of \"Accept-Version\".\n\tAcceptVersionHeaderKey = \"Accept-Version\"\n\t// AcceptHeaderKey is the header key of \"Accept\".\n\tAcceptHeaderKey = \"Accept\"\n\t// AcceptHeaderVersionValue is the Accept's header value search term the requested version.\n\tAcceptHeaderVersionValue = \"version\"\n\t// NotFound is the key that can be used inside a `Map` or inside `ctx.SetVersion(versioning.NotFound)`\n\t// to tell that a version wasn't found, therefore the `NotFoundHandler` should handle the request instead.\n\tNotFound = \"iris.api.version.notfound\"\n\t// Empty is just an empty string. Can be used as a key for a version alias\n\t// when the requested version of a resource was not even specified by the client.\n\t// The difference between NotFound and Empty is important when version aliases are registered:\n\t// - A NotFound cannot be registered as version alias, it\n\t//   means that the client sent a version with its request\n\t//   but that version was not implemented by the server.\n\t// - An Empty indicates that the client didn't send any version at all.\n\tEmpty = \"\"\n)\n\n// ErrNotFound reports whether a requested version\n// does not match with any of the server's implemented ones.\nvar ErrNotFound = fmt.Errorf(\"version %w\", context.ErrNotFound)\n\n// NotFoundHandler is the default version not found handler that\n// is executed from `NewMatcher` when no version is registered as available to dispatch a resource.\nvar NotFoundHandler = func(ctx *context.Context) {\n\t// 303 is an option too,\n\t// end-dev has the chance to change that behavior by using the NotFound in the map:\n\t//\n\t// https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html\n\t/*\n\t\t10.5.2 501 Not Implemented\n\n\t\tThe server does not support the functionality required to fulfill the request.\n\t\tThis is the appropriate response when the server does not\n\t\trecognize the request method and is not capable of supporting it for any resource.\n\t*/\n\n\tctx.StopWithPlainError(501, ErrNotFound)\n}\n\n// FromQuery is a simple helper which tries to\n// set the version constraint from a given URL Query Parameter.\n// The X-Api-Version is still valid.\nfunc FromQuery(urlQueryParameterName string, defaultVersion string) context.Handler {\n\treturn func(ctx *context.Context) {\n\t\tversion := ctx.URLParam(urlQueryParameterName)\n\t\tif version == \"\" {\n\t\t\tversion = defaultVersion\n\t\t}\n\n\t\tif version != \"\" {\n\t\t\tSetVersion(ctx, version)\n\t\t}\n\n\t\tctx.Next()\n\t}\n}\n\n// If reports whether the \"got\" matches the \"expected\" one.\n// the \"expected\" can be a constraint like \">=1.0.0 <2.0.0\".\n// This function is just a helper, better use the Group instead.\nfunc If(got string, expected string) bool {\n\tv, err := semver.Make(got)\n\tif err != nil {\n\t\treturn false\n\t}\n\n\tvalidate, err := semver.ParseRange(expected)\n\tif err != nil {\n\t\treturn false\n\t}\n\n\treturn validate(v)\n}\n\n// Match reports whether the request matches the expected version.\n// This function is just a helper, better use the Group instead.\nfunc Match(ctx *context.Context, expectedVersion string) bool {\n\tvalidate, err := semver.ParseRange(expectedVersion)\n\tif err != nil {\n\t\treturn false\n\t}\n\n\treturn matchVersionRange(ctx, validate)\n}\n\nfunc matchVersionRange(ctx *context.Context, validate semver.Range) bool {\n\tgotVersion := GetVersion(ctx)\n\n\talias, aliasFound := GetVersionAlias(ctx, gotVersion)\n\tif aliasFound {\n\t\tSetVersion(ctx, alias) // set the version so next routes have it already.\n\t\tgotVersion = alias\n\t}\n\n\tif gotVersion == \"\" {\n\t\treturn false\n\t}\n\n\tv, err := semver.Make(gotVersion)\n\tif err != nil {\n\t\treturn false\n\t}\n\n\tif !validate(v) {\n\t\treturn false\n\t}\n\n\tversionString := v.String()\n\n\tif !aliasFound { // don't lose any time to set if already set.\n\t\tSetVersion(ctx, versionString)\n\t}\n\n\tctx.Header(APIVersionResponseHeader, versionString)\n\treturn true\n}\n\n// GetVersion returns the current request version.\n//\n// By default the `GetVersion` will try to read from:\n// - \"Accept\" header, i.e Accept: \"application/json; version=1.0.0\"\n// - \"Accept-Version\" header, i.e Accept-Version: \"1.0.0\"\n//\n// However, the end developer can also set a custom version for a handler via a middleware by using the context's store key\n// for versions (see `Key` for further details on that).\n//\n// See `SetVersion` too.\nfunc GetVersion(ctx *context.Context) string {\n\t// firstly by context store, if manually set by a middleware.\n\tversion := ctx.Values().GetString(ctx.Application().ConfigurationReadOnly().GetVersionContextKey())\n\tif version != \"\" {\n\t\treturn version\n\t}\n\n\t// secondly by the \"Accept-Version\" header.\n\tif version = ctx.GetHeader(AcceptVersionHeaderKey); version != \"\" {\n\t\treturn version\n\t}\n\n\t// thirdly by the \"Accept\" header which is like\"...; version=1.0\"\n\tacceptValue := ctx.GetHeader(AcceptHeaderKey)\n\tif acceptValue != \"\" {\n\t\tif idx := strings.Index(acceptValue, AcceptHeaderVersionValue); idx != -1 {\n\t\t\trem := acceptValue[idx:]\n\t\t\tstartVersion := strings.Index(rem, \"=\")\n\t\t\tif startVersion == -1 || len(rem) < startVersion+1 {\n\t\t\t\treturn \"\"\n\t\t\t}\n\n\t\t\trem = rem[startVersion+1:]\n\n\t\t\tend := strings.Index(rem, \" \")\n\t\t\tif end == -1 {\n\t\t\t\tend = strings.Index(rem, \";\")\n\t\t\t}\n\t\t\tif end == -1 {\n\t\t\t\tend = len(rem)\n\t\t\t}\n\n\t\t\tif version = rem[:end]; version != \"\" {\n\t\t\t\treturn version\n\t\t\t}\n\t\t}\n\t}\n\n\treturn \"\"\n}\n\n// SetVersion force-sets the API Version.\n// It can be used inside a middleware.\n// Example of how you can change the default behavior to extract a requested version (which is by headers)\n// from a \"version\" url parameter instead:\n//\n//\t func(ctx iris.Context) { // &version=1\n//\t  version := ctx.URLParamDefault(\"version\", \"1.0.0\")\n//\t  versioning.SetVersion(ctx, version)\n//\t\t ctx.Next()\n//\t }\n//\n// See `GetVersion` too.\nfunc SetVersion(ctx *context.Context, constraint string) {\n\tctx.Values().Set(ctx.Application().ConfigurationReadOnly().GetVersionContextKey(), constraint)\n}\n\n// AliasMap is just a type alias of the standard map[string]string.\n// Head over to the `Aliases` function below for more.\ntype AliasMap = map[string]string\n\n// Aliases is a middleware which registers version constraint aliases\n// for the children Parties(routers). It's respected by versioning Groups.\n//\n// Example Code:\n//\n//\tapp := iris.New()\n//\n//\tapi := app.Party(\"/api\")\n//\tapi.Use(Aliases(map[string]string{\n//\t versioning.Empty: \"1.0.0\", // when no version was provided by the client.\n//\t \"beta\": \"4.0.0\",\n//\t \"stage\": \"5.0.0-alpha\"\n//\t}))\n//\n//\tv1 := NewGroup(api, \">=1.0.0 < 2.0.0\")\n//\tv1.Get/Post...\n//\n//\tv4 := NewGroup(api, \">=4.0.0 < 5.0.0\")\n//\tv4.Get/Post...\n//\n//\tstage := NewGroup(api, \"5.0.0-alpha\")\n//\tstage.Get/Post...\nfunc Aliases(aliases AliasMap) context.Handler {\n\tcp := make(AliasMap, len(aliases)) // copy the map here so we are safe of later modifications by end-dev.\n\tfor k, v := range aliases {\n\t\tcp[k] = v\n\t}\n\n\treturn func(ctx *context.Context) {\n\t\tSetVersionAliases(ctx, cp, true)\n\t\tctx.Next()\n\t}\n}\n\n// Handler returns a handler which is only fired\n// when the \"version\" is matched with the requested one.\n// It is not meant to be used by end-developers\n// (exported for version controller feature).\n// Use `NewGroup` instead.\nfunc Handler(version string) context.Handler {\n\tvalidate, err := semver.ParseRange(version)\n\tif err != nil {\n\t\treturn func(ctx *context.Context) {\n\t\t\tctx.StopWithError(500, err)\n\t\t}\n\t}\n\n\treturn makeHandler(validate)\n}\n\n// GetVersionAlias returns the version alias of the given \"gotVersion\"\n// or empty. It Reports whether the alias was found.\n// See `SetVersionAliases`, `Aliases` and `Match` for more.\nfunc GetVersionAlias(ctx *context.Context, gotVersion string) (string, bool) {\n\tkey := ctx.Application().ConfigurationReadOnly().GetVersionAliasesContextKey()\n\tif key == \"\" {\n\t\treturn \"\", false\n\t}\n\n\tv := ctx.Values().Get(key)\n\tif v == nil {\n\t\treturn \"\", false\n\t}\n\n\taliases, ok := v.(AliasMap)\n\tif !ok {\n\t\treturn \"\", false\n\t}\n\n\tversion, ok := aliases[gotVersion]\n\tif !ok {\n\t\treturn \"\", false\n\t}\n\n\treturn strings.TrimSpace(version), true\n}\n\n// SetVersionAliases sets a map of version aliases when a requested\n// version of a resource was not implemented by the server.\n// Can be used inside a middleware to the parent Party\n// and always before the child versioning groups (see `Aliases` function).\n//\n// The map's key (string) should be the \"got version\" (by the client)\n// and the value should be the \"version constraint to match\" instead.\n// The map's value(string) should be a registered version\n// otherwise it will hit the NotFoundHandler (501, \"version not found\" by default).\n//\n// The given \"aliases\" is a type of standard map[string]string and\n// should NOT be modified afterwards.\n//\n// The last \"override\" input argument indicates whether any\n// existing aliases, registered by previous handlers in the chain,\n// should be overridden or copied to the previous map one.\nfunc SetVersionAliases(ctx *context.Context, aliases AliasMap, override bool) {\n\tkey := ctx.Application().ConfigurationReadOnly().GetVersionAliasesContextKey()\n\tif key == \"\" {\n\t\treturn\n\t}\n\n\tv := ctx.Values().Get(key)\n\tif v == nil || override {\n\t\tctx.Values().Set(key, aliases)\n\t\treturn\n\t}\n\n\tif existing, ok := v.(AliasMap); ok {\n\t\tfor k, v := range aliases {\n\t\t\texisting[k] = v\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "versioning/version_test.go",
    "content": "package versioning_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/kataras/iris/v12\"\n\t\"github.com/kataras/iris/v12/httptest\"\n\t\"github.com/kataras/iris/v12/versioning\"\n)\n\nfunc TestIf(t *testing.T) {\n\tif expected, got := true, versioning.If(\"1.0.0\", \">=1.0.0\"); expected != got {\n\t\tt.Fatalf(\"expected %s to be %s\", \"1.0.0\", \">= 1.0.0\")\n\t}\n\tif expected, got := true, versioning.If(\"1.2.3\", \"> 1.2.0\"); expected != got {\n\t\tt.Fatalf(\"expected %s to be %s\", \"1.2.3\", \"> 1.2.0\")\n\t}\n}\n\nfunc TestGetVersion(t *testing.T) {\n\tapp := iris.New()\n\n\twriteVesion := func(ctx iris.Context) {\n\t\tctx.WriteString(versioning.GetVersion(ctx))\n\t}\n\n\tapp.Get(\"/\", writeVesion)\n\tapp.Get(\"/manual\", func(ctx iris.Context) {\n\t\tversioning.SetVersion(ctx, \"11.0.5\")\n\t\tctx.Next()\n\t}, writeVesion)\n\n\te := httptest.New(t, app)\n\n\te.GET(\"/\").WithHeader(versioning.AcceptVersionHeaderKey, \"1.0.0\").Expect().\n\t\tStatus(iris.StatusOK).Body().IsEqual(\"1.0.0\")\n\te.GET(\"/\").WithHeader(versioning.AcceptHeaderKey, \"application/vnd.api+json; version=2.1.0\").Expect().\n\t\tStatus(iris.StatusOK).Body().IsEqual(\"2.1.0\")\n\te.GET(\"/\").WithHeader(versioning.AcceptHeaderKey, \"application/vnd.api+json; version=2.1.0 ;other=dsa\").Expect().\n\t\tStatus(iris.StatusOK).Body().IsEqual(\"2.1.0\")\n\te.GET(\"/\").WithHeader(versioning.AcceptHeaderKey, \"version=2.1.0\").Expect().\n\t\tStatus(iris.StatusOK).Body().IsEqual(\"2.1.0\")\n\te.GET(\"/\").WithHeader(versioning.AcceptHeaderKey, \"version=1.0.0\").Expect().\n\t\tStatus(iris.StatusOK).Body().IsEqual(\"1.0.0\")\n\n\t// unknown versions.\n\te.GET(\"/\").WithHeader(versioning.AcceptVersionHeaderKey, \"\").Expect().\n\t\tStatus(iris.StatusOK).Body().IsEqual(\"\")\n\te.GET(\"/\").WithHeader(versioning.AcceptHeaderKey, \"application/vnd.api+json; version=\").Expect().\n\t\tStatus(iris.StatusOK).Body().IsEqual(\"\")\n\te.GET(\"/\").WithHeader(versioning.AcceptHeaderKey, \"application/vnd.api+json; version= ;other=dsa\").Expect().\n\t\tStatus(iris.StatusOK).Body().IsEqual(\"\")\n\te.GET(\"/\").WithHeader(versioning.AcceptHeaderKey, \"version=\").Expect().\n\t\tStatus(iris.StatusOK).Body().IsEqual(\"\")\n\n\te.GET(\"/manual\").Expect().Status(iris.StatusOK).Body().IsEqual(\"11.0.5\")\n}\n\nfunc TestVersionAliases(t *testing.T) {\n\tapp := iris.New()\n\n\tapi := app.Party(\"/api\")\n\tapi.Use(versioning.Aliases(map[string]string{\n\t\tversioning.Empty: \"1.0.0\",\n\t\t\"stage\":          \"2.0.0\",\n\t}))\n\n\twriteVesion := func(ctx iris.Context) {\n\t\tctx.WriteString(versioning.GetVersion(ctx))\n\t}\n\n\t// A group without registration order.\n\tv3 := versioning.NewGroup(api, \">= 3.0.0 < 4.0.0\")\n\tv3.Get(\"/\", writeVesion)\n\n\tv1 := versioning.NewGroup(api, \">= 1.0.0 < 2.0.0\")\n\tv1.Get(\"/\", writeVesion)\n\n\tv2 := versioning.NewGroup(api, \">= 2.0.0 < 3.0.0\")\n\tv2.Get(\"/\", writeVesion)\n\n\tapi.Get(\"/manual\", func(ctx iris.Context) {\n\t\tversioning.SetVersion(ctx, \"12.0.0\")\n\t\tctx.Next()\n\t}, writeVesion)\n\n\te := httptest.New(t, app)\n\n\t// Make sure the SetVersion still works.\n\te.GET(\"/api/manual\").Expect().Status(iris.StatusOK).Body().IsEqual(\"12.0.0\")\n\n\t// Test Empty default.\n\te.GET(\"/api\").WithHeader(versioning.AcceptVersionHeaderKey, \"\").Expect().\n\t\tStatus(iris.StatusOK).Body().IsEqual(\"1.0.0\")\n\t// Test NotFound error, aliases are not responsible for that.\n\te.GET(\"/api\").WithHeader(versioning.AcceptVersionHeaderKey, \"4.0.0\").Expect().\n\t\tStatus(iris.StatusNotImplemented).Body().IsEqual(\"version not found\")\n\t// Test \"stage\" alias.\n\te.GET(\"/api\").WithHeader(versioning.AcceptVersionHeaderKey, \"stage\").Expect().\n\t\tStatus(iris.StatusOK).Body().IsEqual(\"2.0.0\")\n\t// Test version 2.\n\te.GET(\"/api\").WithHeader(versioning.AcceptVersionHeaderKey, \"2.0.0\").Expect().\n\t\tStatus(iris.StatusOK).Body().IsEqual(\"2.0.0\")\n\t// Test version 3 (registered first).\n\te.GET(\"/api\").WithHeader(versioning.AcceptVersionHeaderKey, \"3.1.0\").Expect().\n\t\tStatus(iris.StatusOK).Body().IsEqual(\"3.1.0\")\n}\n"
  },
  {
    "path": "view/README.md",
    "content": "# View\n\nIris supports 7 template engines out-of-the-box, developers can still use any external golang template engine,\nas `Context.ResponseWriter()` is an `io.Writer`.\n\nAll template engines share a common API i.e.\nParse using embedded assets, Layouts and Party-specific layout, Template Funcs, Partial Render and more.\n\n| #  | Name       | Parser   |\n|:---|:-----------|----------|\n| 1 | HTML       | [html/template](https://pkg.go.dev/html/template) |\n| 2 | Blocks     | [kataras/blocks](https://github.com/kataras/blocks) |\n| 3 | Django     | [flosch/pongo2](https://github.com/flosch/pongo2) |\n| 4 | Pug        | [Joker/jade](https://github.com/Joker/jade) |\n| 5 | Handlebars | [mailgun/raymond](https://github.com/mailgun/raymond) |\n| 6 | Jet        | [CloudyKit/jet](https://github.com/CloudyKit/jet) |\n| 7 | Ace        | [yosssi/ace](https://github.com/yosssi/ace) |\n\n[List of Examples](https://github.com/kataras/iris/tree/main/_examples/view).\n\n[Benchmarks](https://github.com/kataras/iris/tree/main/_benchmarks/view).\n\nYou can serve [quicktemplate](https://github.com/valyala/quicktemplate) files too, simply by using the `Context.ResponseWriter`, take a look at the [iris/_examples/view/quicktemplate](https://github.com/kataras/iris/tree/main/_examples/view/quicktemplate) example.\n\n## Overview\n\n```go\n// file: main.go\npackage main\n\nimport \"github.com/kataras/iris/v12\"\n\nfunc main() {\n    app := iris.New()\n    // Load all templates from the \"./views\" folder\n    // where extension is \".html\" and parse them\n    // using the standard `html/template` package.\n    app.RegisterView(iris.HTML(\"./views\", \".html\"))\n\n    // Method:    GET\n    // Resource:  http://localhost:8080\n    app.Get(\"/\", func(ctx iris.Context) {\n        // Bind: {{.message}} with \"Hello world!\"\n        ctx.ViewData(\"message\", \"Hello world!\")\n        // Render template file: ./views/hello.html\n        if err := ctx.View(\"hello.html\"); err != nil {\n\t\t    ctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\t    return\n\t    }\n    })\n\n    // Method:    GET\n    // Resource:  http://localhost:8080/user/42\n    app.Get(\"/user/{id:int64}\", func(ctx iris.Context) {\n        userID, _ := ctx.Params().GetInt64(\"id\")\n        ctx.Writef(\"User ID: %d\", userID)\n    })\n\n    // Start the server using a network address.\n    app.Listen(\":8080\")\n}\n```\n\n```html\n<!-- file: ./views/hello.html -->\n<html>\n<head>\n    <title>Hello Page</title>\n</head>\n<body>\n    <h1>{{.message}}</h1>\n</body>\n</html>\n```\n\n## Template functions\n\n```go\npackage main\n\nimport \"github.com/kataras/iris/v12\"\n\nfunc main() {\n    app := iris.New()\n    tmpl := iris.HTML(\"./templates\", \".html\")\n\n    // builtin template funcs are:\n    //\n    // - {{ urlpath \"mynamedroute\" \"pathParameter_ifneeded\" }}\n    // - {{ render \"header.html\" . }}\n    // - {{ render_r \"header.html\" . }} // partial relative path to current page\n    // - {{ yield . }}\n    // - {{ current . }}\n\n    // register a custom template func.\n    tmpl.AddFunc(\"greet\", func(s string) string {\n        return \"Greetings \" + s + \"!\"\n    })\n\n    // register the view engine to the views, this will load the templates.\n    app.RegisterView(tmpl)\n\n    app.Get(\"/\", hi)\n\n    // http://localhost:8080\n    app.Listen(\":8080\")\n}\n\nfunc hi(ctx iris.Context) {\n    // render the template file \"./templates/hi.html\"\n    if err := ctx.View(\"hi.html\"); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n}\n```\n\n```html\n<!-- file: ./templates/hi.html -->\n<b>{{greet \"kataras\"}}</b> <!-- will be rendered as: <b>Greetings kataras!</b> -->\n```\n\n## Embedded\n\nView engine supports bundled(https://github.com/go-bindata/go-bindata) template files too. Latest\n`go-bindata` release gives you a compatible `http.FileSystem` that can be provided as the first argument of a view engine's initialization, e.g. `HTML(AssetFile(), \".html\")`.\n\n\n```sh\n$ go install github.com/go-bindata/go-bindata/v3/go-bindata@latest\n$ go-bindata -fs -prefix \"templates\" ./templates/...\n$ go run .\n```\n\nExample Code:\n\n```go\npackage main\n\nimport \"github.com/kataras/iris/v12\"\n\nfunc main() {\n    app := iris.New()\n    app.RegisterView(iris.HTML(AssetFile(), \".html\"))\n    app.Get(\"/\", hi)\n\n    // http://localhost:8080\n    app.Listen(\":8080\")\n}\n\ntype page struct {\n    Title, Name string\n}\n\nfunc hi(ctx iris.Context) {\n    //                      {{.Page.Title}} and {{Page.Name}}\n    ctx.ViewData(\"Page\", page{Title: \"Hi Page\", Name: \"iris\"})\n    if err := ctx.View(\"hi.html\"); err != nil {\n\t\tctx.HTML(fmt.Sprintf(\"<h3>%s</h3>\", err.Error()))\n\t\treturn\n\t}\n}\n```\n\nExamples can be found here: https://github.com/kataras/iris/tree/main/_examples/view/embedding-templates-into-app and  https://github.com/kataras/iris/tree/main/_examples/view/embedding-templates-into-app-bindata.\n\n## Reload\n\nEnable auto-reloading of templates on each request. Useful while developers are in dev mode\nas they no neeed to restart their app on every template edit.\n\nExample code:\n\n```go\npugEngine := iris.Pug(\"./templates\", \".jade\")\npugEngine.Reload(true) // <--- set to true to re-build the templates on each request.\napp.RegisterView(pugEngine)\n```"
  },
  {
    "path": "view/ace.go",
    "content": "package view\n\nimport (\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/yosssi/ace\"\n)\n\n// AceEngine represents the Ace view engine.\n// See the `Ace` package-level function for more.\ntype AceEngine struct {\n\t*HTMLEngine\n\n\tindent string\n}\n\n// SetIndent string used for indentation.\n// Do NOT use tabs, only spaces characters.\n// Defaults to minified response, no indentation.\nfunc (s *AceEngine) SetIndent(indent string) *AceEngine {\n\ts.indent = indent\n\treturn s\n}\n\n// Ace returns a new Ace view engine.\n// It shares the same exactly logic with the\n// html view engine, it uses the same exactly configuration.\n// The given \"extension\" MUST begin with a dot.\n// Ace minifies the response automatically unless\n// SetIndent() method is set.\n//\n// Read more about the Ace Go Parser: https://github.com/yosssi/ace\n//\n// Usage:\n// Ace(\"./views\", \".ace\") or\n// Ace(iris.Dir(\"./views\"), \".ace\") or\n// Ace(embed.FS, \".ace\") or Ace(AssetFile(), \".ace\") for embedded data.\nfunc Ace(fs any, extension string) *AceEngine {\n\ts := &AceEngine{HTMLEngine: HTML(fs, extension), indent: \"\"}\n\ts.name = \"Ace\"\n\n\tfuncs := make(map[string]any)\n\n\tonce := new(sync.Once)\n\n\ts.middleware = func(name string, text []byte) (contents string, err error) {\n\t\tonce.Do(func() { // on first template parse, all funcs are given.\n\t\t\tfor k, v := range s.getBuiltinFuncs(name) {\n\t\t\t\tfuncs[k] = v\n\t\t\t}\n\n\t\t\tfor k, v := range s.funcs {\n\t\t\t\tfuncs[k] = v\n\t\t\t}\n\t\t})\n\n\t\t//\tname = path.Join(path.Clean(directory), name)\n\n\t\tsrc := ace.NewSource(\n\t\t\tace.NewFile(name, text),\n\t\t\tace.NewFile(\"\", []byte{}),\n\t\t\t[]*ace.File{},\n\t\t)\n\n\t\tif strings.Contains(name, \"layout\") {\n\t\t\tfor k, v := range s.layoutFuncs {\n\t\t\t\tfuncs[k] = v\n\t\t\t}\n\t\t}\n\n\t\topts := &ace.Options{\n\t\t\tExtension:  extension[1:],\n\t\t\tFuncMap:    funcs,\n\t\t\tDelimLeft:  s.left,\n\t\t\tDelimRight: s.right,\n\t\t\tIndent:     s.indent,\n\t\t}\n\n\t\trslt, err := ace.ParseSource(src, opts)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\n\t\tt, err := ace.CompileResult(name, rslt, opts)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\n\t\treturn t.Lookup(name).Tree.Root.String(), nil\n\t}\n\n\treturn s\n}\n"
  },
  {
    "path": "view/blocks.go",
    "content": "package view\n\nimport (\n\t\"html/template\"\n\t\"io\"\n\n\t\"github.com/kataras/blocks\"\n)\n\n// BlocksEngine is an Iris view engine adapter for the blocks view engine.\n// The blocks engine is based on the html/template standard Go package.\n//\n// To initialize a fresh one use the `Blocks` function.\n// To wrap an existing one use the `WrapBlocks` function.\n//\n// It contains the following four default template functions:\n// - url \"routename\" parameters...\n// - urlpath \"routename\" parameters...\n// - tr \"language\" \"key\" arguments...\n// - partial \"template_name\" data\n//\n// Read more at: https://github.com/kataras/blocks.\ntype BlocksEngine struct {\n\tEngine *blocks.Blocks\n}\n\nvar (\n\t_ Engine       = (*BlocksEngine)(nil)\n\t_ EngineFuncer = (*BlocksEngine)(nil)\n)\n\n// WrapBlocks wraps an initialized blocks engine and returns its Iris adapter.\n// See `Blocks` package-level function too.\nfunc WrapBlocks(v *blocks.Blocks) *BlocksEngine {\n\treturn &BlocksEngine{Engine: v}\n}\n\n// Blocks returns a new blocks view engine.\n// The given \"extension\" MUST begin with a dot.\n//\n// See `WrapBlocks` package-level function too.\n//\n// Usage:\n// Blocks(\"./views\", \".html\") or\n// Blocks(iris.Dir(\"./views\"), \".html\") or\n// Blocks(embed.FS, \".html\") or Blocks(AssetFile(), \".html\") for embedded data.\nfunc Blocks(fs any, extension string) *BlocksEngine {\n\treturn WrapBlocks(blocks.New(fs).Extension(extension))\n}\n\n// Name returns the blocks engine's name.\nfunc (s *BlocksEngine) Name() string {\n\treturn \"Blocks\"\n}\n\n// RootDir sets the directory to use as the root one inside the provided File System.\nfunc (s *BlocksEngine) RootDir(root string) *BlocksEngine {\n\ts.Engine.RootDir(root)\n\treturn s\n}\n\n// LayoutDir sets a custom layouts directory,\n// always relative to the \"rootDir\" one.\n// Layouts are recognised by their prefix names.\n// Defaults to \"layouts\".\nfunc (s *BlocksEngine) LayoutDir(relToDirLayoutDir string) *BlocksEngine {\n\ts.Engine.LayoutDir(relToDirLayoutDir)\n\treturn s\n}\n\n// Ext returns empty ext as this template engine\n// supports template blocks without file suffix.\n// Note that, if more than one view engine is registered to a single\n// Iris application then, this Blocks engine should be the last entry one.\nfunc (s *BlocksEngine) Ext() string {\n\treturn \"\"\n}\n\n// AddFunc implements the `EngineFuncer` which is being used\n// by the framework to add template functions like:\n// - url func(routeName string, args ...string) string\n// - urlpath func(routeName string, args ...string) string\n// - tr func(lang, key string, args ...any) string\nfunc (s *BlocksEngine) AddFunc(funcName string, funcBody any) {\n\ts.Engine.Funcs(template.FuncMap{funcName: funcBody})\n}\n\n// AddLayoutFunc adds a template function for templates that are marked as layouts.\nfunc (s *BlocksEngine) AddLayoutFunc(funcName string, funcBody any) *BlocksEngine {\n\ts.Engine.LayoutFuncs(template.FuncMap{funcName: funcBody})\n\treturn s\n}\n\n// Layout sets the default layout which inside should use\n// the {{ template \"content\" . }} to render the main template.\n//\n// Example for ./views/layouts/main.html:\n// Blocks(\"./views\", \".html\").Layout(\"layouts/main\")\nfunc (s *BlocksEngine) Layout(layoutName string) *BlocksEngine {\n\ts.Engine.DefaultLayout(layoutName)\n\treturn s\n}\n\n// Reload if called with a true parameter,\n// each `ExecuteWriter` call will re-parse the templates.\n// Useful when the application is at a development stage.\nfunc (s *BlocksEngine) Reload(b bool) *BlocksEngine {\n\ts.Engine.Reload(b)\n\treturn s\n}\n\n// Load parses the files into templates.\nfunc (s *BlocksEngine) Load() error {\n\treturn s.Engine.Load()\n}\n\n// ExecuteWriter renders a template on \"w\".\nfunc (s *BlocksEngine) ExecuteWriter(w io.Writer, tmplName, layoutName string, data any) error {\n\tif layoutName == NoLayout {\n\t\tlayoutName = \"\"\n\t}\n\n\treturn s.Engine.ExecuteTemplate(w, tmplName, layoutName, data)\n}\n"
  },
  {
    "path": "view/django.go",
    "content": "package view\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"io/fs\"\n\t\"os\"\n\tstdPath \"path\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\n\t\"github.com/fatih/structs\"\n\t\"github.com/flosch/pongo2/v4\"\n)\n\ntype (\n\t// Value type alias for pongo2.Value\n\tValue = pongo2.Value\n\t// Error type alias for pongo2.Error\n\tError = pongo2.Error\n\t// FilterFunction type alias for pongo2.FilterFunction\n\tFilterFunction = pongo2.FilterFunction\n\n\t// Parser type alias for pongo2.Parser\n\tParser = pongo2.Parser\n\t// Token type alias for pongo2.Token\n\tToken = pongo2.Token\n\t// INodeTag type alias for pongo2.InodeTag\n\tINodeTag = pongo2.INodeTag\n\t// TagParser the function signature of the tag's parser you will have\n\t// to implement in order to create a new tag.\n\t//\n\t// 'doc' is providing access to the whole document while 'arguments'\n\t// is providing access to the user's arguments to the tag:\n\t//\n\t//     {% your_tag_name some \"arguments\" 123 %}\n\t//\n\t// start_token will be the *Token with the tag's name in it (here: your_tag_name).\n\t//\n\t// Please see the Parser documentation on how to use the parser.\n\t// See `RegisterTag` for more information about writing a tag as well.\n\tTagParser = pongo2.TagParser\n)\n\n// AsValue converts any given value to a pongo2.Value\n// Usually being used within own functions passed to a template\n// through a Context or within filter functions.\n//\n// Example:\n//\n//\tAsValue(\"my string\")\n//\n// Shortcut for `pongo2.AsValue`.\nvar AsValue = pongo2.AsValue\n\n// AsSafeValue works like AsValue, but does not apply the 'escape' filter.\n// Shortcut for `pongo2.AsSafeValue`.\nvar AsSafeValue = pongo2.AsSafeValue\n\ntype tDjangoAssetLoader struct {\n\trootDir string\n\tfs      fs.FS\n}\n\n// Abs calculates the path to a given template. Whenever a path must be resolved\n// due to an import from another template, the base equals the parent template's path.\nfunc (l *tDjangoAssetLoader) Abs(base, name string) string {\n\tif stdPath.IsAbs(name) {\n\t\treturn name\n\t}\n\n\treturn stdPath.Join(l.rootDir, name)\n}\n\n// Get returns an io.Reader where the template's content can be read from.\nfunc (l *tDjangoAssetLoader) Get(path string) (io.Reader, error) {\n\tif stdPath.IsAbs(path) {\n\t\tpath = path[1:]\n\t}\n\n\tres, err := asset(l.fs, path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn bytes.NewReader(res), nil\n}\n\n// DjangoEngine contains the django view engine structure.\ntype DjangoEngine struct {\n\tfs fs.FS\n\t// files configuration\n\trootDir   string\n\textension string\n\treload    bool\n\t//\n\trmu sync.RWMutex // locks for filters, globals and `ExecuteWiter` when `reload` is true.\n\t// filters for pongo2, map[name of the filter] the filter function . The filters are auto register\n\tfilters map[string]FilterFunction\n\t// globals share context fields between templates.\n\tglobals       map[string]any\n\tSet           *pongo2.TemplateSet\n\ttemplateCache map[string]*pongo2.Template\n}\n\nvar (\n\t_ Engine       = (*DjangoEngine)(nil)\n\t_ EngineFuncer = (*DjangoEngine)(nil)\n)\n\n// Django creates and returns a new django view engine.\n// The given \"extension\" MUST begin with a dot.\n//\n// Usage:\n// Django(\"./views\", \".html\") or\n// Django(iris.Dir(\"./views\"), \".html\") or\n// Django(embed.FS, \".html\") or Django(AssetFile(), \".html\") for embedded data.\nfunc Django(fs any, extension string) *DjangoEngine {\n\ts := &DjangoEngine{\n\t\tfs:            getFS(fs),\n\t\trootDir:       \"/\",\n\t\textension:     extension,\n\t\tglobals:       make(map[string]any),\n\t\tfilters:       make(map[string]FilterFunction),\n\t\ttemplateCache: make(map[string]*pongo2.Template),\n\t}\n\n\treturn s\n}\n\n// RootDir sets the directory to be used as a starting point\n// to load templates from the provided file system.\nfunc (s *DjangoEngine) RootDir(root string) *DjangoEngine {\n\tif s.fs != nil && root != \"\" && root != \"/\" && root != \".\" && root != s.rootDir {\n\t\tsub, err := fs.Sub(s.fs, s.rootDir)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\n\t\ts.fs = sub // here so the \"middleware\" can work.\n\t}\n\n\ts.rootDir = filepath.ToSlash(root)\n\treturn s\n}\n\n// Name returns the django engine's name.\nfunc (s *DjangoEngine) Name() string {\n\treturn \"Django\"\n}\n\n// Ext returns the file extension which this view engine is responsible to render.\n// If the filename extension on ExecuteWriter is empty then this is appended.\nfunc (s *DjangoEngine) Ext() string {\n\treturn s.extension\n}\n\n// Reload if set to true the templates are reloading on each render,\n// use it when you're in development and you're boring of restarting\n// the whole app when you edit a template file.\n//\n// Note that if `true` is passed then only one `View -> ExecuteWriter` will be render each time,\n// no concurrent access across clients, use it only on development status.\n// It's good to be used side by side with the https://github.com/kataras/rizla reloader for go source files.\nfunc (s *DjangoEngine) Reload(developmentMode bool) *DjangoEngine {\n\ts.reload = developmentMode\n\treturn s\n}\n\n// AddFunc adds the function to the template's Globals.\n// It is legal to overwrite elements of the default actions:\n// - url func(routeName string, args ...string) string\n// - urlpath func(routeName string, args ...string) string\n// - render func(fullPartialName string) (template.HTML, error).\nfunc (s *DjangoEngine) AddFunc(funcName string, funcBody any) {\n\ts.rmu.Lock()\n\ts.globals[funcName] = funcBody\n\ts.rmu.Unlock()\n}\n\n// AddFilter registers a new filter. If there's already a filter with the same\n// name, RegisterFilter will panic. You usually want to call this\n// function in the filter's init() function:\n// http://golang.org/doc/effective_go.html#init\n//\n// Same as `RegisterFilter`.\nfunc (s *DjangoEngine) AddFilter(filterName string, filterBody FilterFunction) *DjangoEngine {\n\treturn s.registerFilter(filterName, filterBody)\n}\n\n// RegisterFilter registers a new filter. If there's already a filter with the same\n// name, RegisterFilter will panic. You usually want to call this\n// function in the filter's init() function:\n// http://golang.org/doc/effective_go.html#init\n//\n// See http://www.florian-schlachter.de/post/pongo2/ for more about\n// writing filters and tags.\nfunc (s *DjangoEngine) RegisterFilter(filterName string, filterBody FilterFunction) *DjangoEngine {\n\treturn s.registerFilter(filterName, filterBody)\n}\n\nfunc (s *DjangoEngine) registerFilter(filterName string, fn FilterFunction) *DjangoEngine {\n\tpongo2.RegisterFilter(filterName, fn)\n\treturn s\n}\n\n// RegisterTag registers a new tag. You usually want to call this\n// function in the tag's init() function:\n// http://golang.org/doc/effective_go.html#init\n//\n// See http://www.florian-schlachter.de/post/pongo2/ for more about\n// writing filters and tags.\nfunc (s *DjangoEngine) RegisterTag(tagName string, fn TagParser) error {\n\treturn pongo2.RegisterTag(tagName, fn)\n}\n\n// Load parses the templates to the engine.\n// It is responsible to add the necessary global functions.\n//\n// Returns an error if something bad happens, user is responsible to catch it.\nfunc (s *DjangoEngine) Load() error {\n\t// If only custom templates should be loaded.\n\tif (s.fs == nil || context.IsNoOpFS(s.fs)) && len(s.templateCache) > 0 {\n\t\treturn nil\n\t}\n\n\trootDirName := getRootDirName(s.fs)\n\n\treturn walk(s.fs, \"\", func(path string, info os.FileInfo, err error) error {\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif info == nil || info.IsDir() {\n\t\t\treturn nil\n\t\t}\n\n\t\tif s.extension != \"\" {\n\t\t\tif !strings.HasSuffix(path, s.extension) {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\n\t\tif s.rootDir == rootDirName {\n\t\t\tpath = strings.TrimPrefix(path, rootDirName)\n\t\t\tpath = strings.TrimPrefix(path, \"/\")\n\t\t}\n\n\t\tcontents, err := asset(s.fs, path)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\treturn s.ParseTemplate(path, contents)\n\t})\n}\n\n// ParseTemplate adds a custom template from text.\n// This parser does not support funcs per template. Use the `AddFunc` instead.\nfunc (s *DjangoEngine) ParseTemplate(name string, contents []byte) error {\n\ts.rmu.Lock()\n\tdefer s.rmu.Unlock()\n\n\ts.initSet()\n\n\tname = strings.TrimPrefix(name, \"/\")\n\ttmpl, err := s.Set.FromBytes(contents)\n\tif err == nil {\n\t\ts.templateCache[name] = tmpl\n\t}\n\n\treturn err\n}\n\nfunc (s *DjangoEngine) initSet() { // protected by the caller.\n\tif s.Set == nil {\n\t\ts.Set = pongo2.NewSet(\"\", &tDjangoAssetLoader{fs: s.fs, rootDir: s.rootDir})\n\t\ts.Set.Globals = getPongoContext(s.globals)\n\t}\n}\n\n// getPongoContext returns the pongo2.Context from map[string]any or from pongo2.Context, used internaly\nfunc getPongoContext(templateData any) pongo2.Context {\n\tif templateData == nil {\n\t\treturn nil\n\t}\n\n\tswitch data := templateData.(type) {\n\tcase pongo2.Context:\n\t\treturn data\n\tcase context.Map:\n\t\treturn pongo2.Context(data)\n\tdefault:\n\t\t// if struct, convert it to map[string]any\n\t\tif structs.IsStruct(data) {\n\t\t\treturn pongo2.Context(structs.Map(data))\n\t\t}\n\n\t\tpanic(\"django: template data: should be a map or struct\")\n\t}\n}\n\nfunc (s *DjangoEngine) fromCache(relativeName string) *pongo2.Template {\n\tif s.reload {\n\t\ts.rmu.RLock()\n\t\tdefer s.rmu.RUnlock()\n\t}\n\n\tif tmpl, ok := s.templateCache[relativeName]; ok {\n\t\treturn tmpl\n\t}\n\treturn nil\n}\n\n// ExecuteWriter executes a templates and write its results to the w writer\n// layout here is useless.\nfunc (s *DjangoEngine) ExecuteWriter(w io.Writer, filename string, _ string, bindingData any) error {\n\t// re-parse the templates if reload is enabled.\n\tif s.reload {\n\t\tif err := s.Load(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif tmpl := s.fromCache(filename); tmpl != nil {\n\t\treturn tmpl.ExecuteWriter(getPongoContext(bindingData), w)\n\t}\n\n\treturn ErrNotExist{Name: filename, IsLayout: false, Data: bindingData}\n}\n"
  },
  {
    "path": "view/fs.go",
    "content": "package view\n\nimport (\n\t\"fmt\"\n\t\"io/fs\"\n\t\"path/filepath\"\n\n\t\"github.com/kataras/iris/v12/context\"\n)\n\n// walk recursively in \"fileSystem\" descends \"root\" path, calling \"walkFn\".\nfunc walk(fileSystem fs.FS, root string, walkFn filepath.WalkFunc) error {\n\tif root != \"\" && root != \"/\" && root != \".\" {\n\t\tsub, err := fs.Sub(fileSystem, root)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tfileSystem = sub\n\t}\n\n\tif root == \"\" {\n\t\troot = \".\"\n\t}\n\n\treturn fs.WalkDir(fileSystem, root, func(path string, d fs.DirEntry, err error) error {\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"walk: %s: %w\", path, err)\n\t\t}\n\n\t\tinfo, err := d.Info()\n\t\tif err != nil {\n\t\t\tif err != filepath.SkipDir {\n\t\t\t\treturn fmt.Errorf(\"walk stat: %s: %w\", path, err)\n\t\t\t}\n\n\t\t\treturn nil\n\t\t}\n\n\t\tif info.IsDir() {\n\t\t\treturn nil\n\t\t}\n\n\t\twalkFnErr := walkFn(path, info, err)\n\t\tif walkFnErr != nil {\n\t\t\treturn fmt.Errorf(\"walk: walkFn: %w\", walkFnErr)\n\t\t}\n\n\t\treturn nil\n\t})\n\n}\n\nfunc asset(fileSystem fs.FS, name string) ([]byte, error) {\n\tdata, err := fs.ReadFile(fileSystem, name)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"asset: read file: %w\", err)\n\t}\n\n\treturn data, nil\n}\n\nfunc getFS(fsOrDir any) fs.FS {\n\treturn context.ResolveFS(fsOrDir)\n}\n\nfunc getRootDirName(fileSystem fs.FS) string {\n\trootDirFile, err := fileSystem.Open(\".\")\n\tif err == nil {\n\t\trootDirStat, err := rootDirFile.Stat()\n\t\tif err == nil {\n\t\t\treturn rootDirStat.Name()\n\t\t}\n\t}\n\n\treturn \"\"\n}\n"
  },
  {
    "path": "view/handlebars.go",
    "content": "package view\n\nimport (\n\t\"fmt\"\n\t\"html/template\"\n\t\"io\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\n\t\"github.com/mailgun/raymond/v2\"\n)\n\n// HandlebarsEngine contains the handlebars view engine structure.\ntype HandlebarsEngine struct {\n\tfs fs.FS\n\t// files configuration\n\trootDir   string\n\textension string\n\t// Not used anymore.\n\t// assetFn   func(name string) ([]byte, error) // for embedded, in combination with directory & extension\n\t// namesFn   func() []string                   // for embedded, in combination with directory & extension\n\treload bool // if true, each time the ExecuteWriter is called the templates will be reloaded.\n\t// parser configuration\n\tlayout        string\n\trmu           sync.RWMutex\n\tfuncs         template.FuncMap\n\ttemplateCache map[string]*raymond.Template\n}\n\nvar (\n\t_ Engine       = (*HandlebarsEngine)(nil)\n\t_ EngineFuncer = (*HandlebarsEngine)(nil)\n)\n\n// Handlebars creates and returns a new handlebars view engine.\n// The given \"extension\" MUST begin with a dot.\n//\n// Usage:\n// Handlebars(\"./views\", \".html\") or\n// Handlebars(iris.Dir(\"./views\"), \".html\") or\n// Handlebars(embed.FS, \".html\") or Handlebars(AssetFile(), \".html\") for embedded data.\nfunc Handlebars(fs any, extension string) *HandlebarsEngine {\n\ts := &HandlebarsEngine{\n\t\tfs:            getFS(fs),\n\t\trootDir:       \"/\",\n\t\textension:     extension,\n\t\ttemplateCache: make(map[string]*raymond.Template),\n\t\tfuncs:         make(template.FuncMap), // global\n\t}\n\n\t// register the render helper here\n\traymond.RegisterHelper(\"render\", func(partial string, binding any) raymond.SafeString {\n\t\tcontents, err := s.executeTemplateBuf(partial, binding)\n\t\tif err != nil {\n\t\t\treturn raymond.SafeString(\"template with name: \" + partial + \" couldn't not be found.\")\n\t\t}\n\t\treturn raymond.SafeString(contents)\n\t})\n\n\treturn s\n}\n\n// RootDir sets the directory to be used as a starting point\n// to load templates from the provided file system.\nfunc (s *HandlebarsEngine) RootDir(root string) *HandlebarsEngine {\n\tif s.fs != nil && root != \"\" && root != \"/\" && root != \".\" && root != s.rootDir {\n\t\tsub, err := fs.Sub(s.fs, s.rootDir)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\n\t\ts.fs = sub // here so the \"middleware\" can work.\n\t}\n\n\ts.rootDir = filepath.ToSlash(root)\n\treturn s\n}\n\n// Name returns the handlebars engine's name.\nfunc (s *HandlebarsEngine) Name() string {\n\treturn \"Handlebars\"\n}\n\n// Ext returns the file extension which this view engine is responsible to render.\n// If the filename extension on ExecuteWriter is empty then this is appended.\nfunc (s *HandlebarsEngine) Ext() string {\n\treturn s.extension\n}\n\n// Reload if set to true the templates are reloading on each render,\n// use it when you're in development and you're boring of restarting\n// the whole app when you edit a template file.\n//\n// Note that if `true` is passed then only one `View -> ExecuteWriter` will be render each time,\n// no concurrent access across clients, use it only on development status.\n// It's good to be used side by side with the https://github.com/kataras/rizla reloader for go source files.\nfunc (s *HandlebarsEngine) Reload(developmentMode bool) *HandlebarsEngine {\n\ts.reload = developmentMode\n\treturn s\n}\n\n// Layout sets the layout template file which should use\n// the {{ yield . }} func to yield the main template file\n// and optionally {{partial/partial_r/render . }} to render\n// other template files like headers and footers.\nfunc (s *HandlebarsEngine) Layout(layoutFile string) *HandlebarsEngine {\n\ts.layout = layoutFile\n\treturn s\n}\n\n// AddFunc adds a function to the templates.\n// It is legal to overwrite elements of the default actions:\n// - url func(routeName string, args ...string) string\n// - urlpath func(routeName string, args ...string) string\n// - render func(fullPartialName string) (raymond.HTML, error).\nfunc (s *HandlebarsEngine) AddFunc(funcName string, funcBody any) {\n\ts.rmu.Lock()\n\ts.funcs[funcName] = funcBody\n\ts.rmu.Unlock()\n}\n\n// AddGlobalFunc registers a global template function for all Handlebars view engines.\nfunc (s *HandlebarsEngine) AddGlobalFunc(funcName string, funcBody any) {\n\ts.rmu.Lock()\n\traymond.RegisterHelper(funcName, funcBody)\n\ts.rmu.Unlock()\n}\n\n// Load parses the templates to the engine.\n// It is responsible to add the necessary global functions.\n//\n// Returns an error if something bad happens, user is responsible to catch it.\nfunc (s *HandlebarsEngine) Load() error {\n\t// If only custom templates should be loaded.\n\tif (s.fs == nil || context.IsNoOpFS(s.fs)) && len(s.templateCache) > 0 {\n\t\treturn nil\n\t}\n\n\trootDirName := getRootDirName(s.fs)\n\n\treturn walk(s.fs, \"\", func(path string, info os.FileInfo, _ error) error {\n\t\tif info == nil || info.IsDir() {\n\t\t\treturn nil\n\t\t}\n\n\t\tif s.extension != \"\" {\n\t\t\tif !strings.HasSuffix(path, s.extension) {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\n\t\tif s.rootDir == rootDirName {\n\t\t\tpath = strings.TrimPrefix(path, rootDirName)\n\t\t\tpath = strings.TrimPrefix(path, \"/\")\n\t\t}\n\n\t\tcontents, err := asset(s.fs, path)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn s.ParseTemplate(path, string(contents), nil)\n\t})\n}\n\n// ParseTemplate adds a custom template from text.\nfunc (s *HandlebarsEngine) ParseTemplate(name string, contents string, funcs template.FuncMap) error {\n\ts.rmu.Lock()\n\tdefer s.rmu.Unlock()\n\n\tname = strings.TrimPrefix(name, \"/\")\n\ttmpl, err := raymond.Parse(contents)\n\tif err == nil {\n\t\t// Add functions for this template.\n\t\tfor k, v := range s.funcs {\n\t\t\ttmpl.RegisterHelper(k, v)\n\t\t}\n\n\t\tfor k, v := range funcs {\n\t\t\ttmpl.RegisterHelper(k, v)\n\t\t}\n\n\t\ts.templateCache[name] = tmpl\n\t}\n\n\treturn err\n}\n\nfunc (s *HandlebarsEngine) fromCache(relativeName string) *raymond.Template {\n\tif s.reload {\n\t\ts.rmu.RLock()\n\t\tdefer s.rmu.RUnlock()\n\t}\n\n\tif tmpl, ok := s.templateCache[relativeName]; ok {\n\t\treturn tmpl\n\t}\n\n\treturn nil\n}\n\nfunc (s *HandlebarsEngine) executeTemplateBuf(name string, binding any) (string, error) {\n\tif tmpl := s.fromCache(name); tmpl != nil {\n\t\treturn tmpl.Exec(binding)\n\t}\n\treturn \"\", nil\n}\n\n// ExecuteWriter executes a template and writes its result to the w writer.\nfunc (s *HandlebarsEngine) ExecuteWriter(w io.Writer, filename string, layout string, bindingData any) error {\n\t// re-parse the templates if reload is enabled.\n\tif s.reload {\n\t\tif err := s.Load(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tisLayout := false\n\tlayout = getLayout(layout, s.layout)\n\trenderFilename := filename\n\n\tif layout != \"\" {\n\t\tisLayout = true\n\t\trenderFilename = layout // the render becomes the layout, and the name is the partial.\n\t}\n\n\tif tmpl := s.fromCache(renderFilename); tmpl != nil {\n\t\tbinding := bindingData\n\t\tif isLayout {\n\t\t\tvar context map[string]any\n\t\t\tif m, is := binding.(map[string]any); is { // handlebars accepts maps,\n\t\t\t\tcontext = m\n\t\t\t} else {\n\t\t\t\treturn fmt.Errorf(\"please provide a map[string]any type as the binding instead of the %#v\", binding)\n\t\t\t}\n\n\t\t\tcontents, err := s.executeTemplateBuf(filename, binding)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif context == nil {\n\t\t\t\tcontext = make(map[string]any, 1)\n\t\t\t}\n\t\t\t// I'm implemented the {{ yield . }} as with the rest of template engines, so this is not inneed for iris, but the user can do that manually if want\n\t\t\t// there is no performance cost: raymond.RegisterPartialTemplate(name, tmpl)\n\t\t\tcontext[\"yield\"] = raymond.SafeString(contents)\n\t\t}\n\n\t\tres, err := tmpl.Exec(binding)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_, err = fmt.Fprint(w, res)\n\t\treturn err\n\t}\n\n\treturn ErrNotExist{\n\t\tName:     fmt.Sprintf(\"%s (file: %s)\", renderFilename, filename),\n\t\tIsLayout: false,\n\t\tData:     bindingData,\n\t}\n}\n"
  },
  {
    "path": "view/html.go",
    "content": "package view\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"html/template\"\n\t\"io\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/kataras/iris/v12/context\"\n)\n\n// HTMLEngine contains the html view engine structure.\ntype HTMLEngine struct {\n\tname string // the view engine's name, can be HTML, Ace or Pug.\n\t// the file system to load from.\n\tfs fs.FS\n\t// files configuration\n\trootDir   string\n\textension string\n\t// if true, each time the ExecuteWriter is called the templates will be reloaded,\n\t// each ExecuteWriter waits to be finished before writing to a new one.\n\treload bool\n\t// parser configuration\n\toptions     []string // (text) template options\n\tleft        string\n\tright       string\n\tlayout      string\n\trmu         sync.RWMutex // locks for layoutFuncs and funcs\n\tlayoutFuncs template.FuncMap\n\tfuncs       template.FuncMap\n\n\t//\n\tmiddleware  func(name string, contents []byte) (string, error)\n\tonLoad      func()\n\tonLoaded    func()\n\tTemplates   *template.Template\n\tcustomCache []customTmp // required to load them again if reload is true.\n\tbufPool     *sync.Pool\n\t//\n}\n\ntype customTmp struct {\n\tname     string\n\tcontents []byte\n\tfuncs    template.FuncMap\n}\n\nvar (\n\t_ Engine       = (*HTMLEngine)(nil)\n\t_ EngineFuncer = (*HTMLEngine)(nil)\n)\n\n// HTML creates and returns a new html view engine.\n// The html engine used like the \"html/template\" standard go package\n// but with a lot of extra features.\n// The given \"extension\" MUST begin with a dot.\n//\n// Usage:\n// HTML(\"./views\", \".html\") or\n// HTML(iris.Dir(\"./views\"), \".html\") or\n// HTML(embed.FS, \".html\") or HTML(AssetFile(), \".html\") for embedded data or\n// HTML(\"\",\"\").ParseTemplate(\"hello\", `[]byte(\"hello {{.Name}}\")`, nil) for custom template parsing only.\nfunc HTML(dirOrFS any, extension string) *HTMLEngine {\n\ts := &HTMLEngine{\n\t\tname:      \"HTML\",\n\t\tfs:        getFS(dirOrFS),\n\t\trootDir:   \"/\",\n\t\textension: extension,\n\t\treload:    false,\n\t\tleft:      \"{{\",\n\t\tright:     \"}}\",\n\t\tlayout:    \"\",\n\t\tlayoutFuncs: template.FuncMap{\n\t\t\t\"yield\": func(binding any) template.HTML {\n\t\t\t\treturn template.HTML(\"\")\n\t\t\t},\n\t\t},\n\t\tfuncs: make(template.FuncMap),\n\t\tbufPool: &sync.Pool{New: func() any {\n\t\t\treturn new(bytes.Buffer)\n\t\t}},\n\t}\n\n\treturn s\n}\n\n// RootDir sets the directory to be used as a starting point\n// to load templates from the provided file system.\nfunc (s *HTMLEngine) RootDir(root string) *HTMLEngine {\n\tif s.fs != nil && root != \"\" && root != \"/\" && root != \".\" && root != s.rootDir {\n\t\tsub, err := fs.Sub(s.fs, root)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\ts.fs = sub // here so the \"middleware\" can work.\n\t}\n\n\ts.rootDir = filepath.ToSlash(root)\n\treturn s\n}\n\n// FS change templates DIR\nfunc (s *HTMLEngine) FS(dirOrFS any) *HTMLEngine {\n\ts.fs = getFS(dirOrFS)\n\treturn s\n}\n\n// Name returns the engine's name.\nfunc (s *HTMLEngine) Name() string {\n\treturn s.name\n}\n\n// Ext returns the file extension which this view engine is responsible to render.\n// If the filename extension on ExecuteWriter is empty then this is appended.\nfunc (s *HTMLEngine) Ext() string {\n\treturn s.extension\n}\n\n// Reload if set to true the templates are reloading on each render,\n// use it when you're in development and you're boring of restarting\n// the whole app when you edit a template file.\n//\n// Note that if `true` is passed then only one `View -> ExecuteWriter` will be render each time,\n// no concurrent access across clients, use it only on development status.\n// It's good to be used side by side with the https://github.com/kataras/rizla reloader for go source files.\nfunc (s *HTMLEngine) Reload(developmentMode bool) *HTMLEngine {\n\ts.reload = developmentMode\n\treturn s\n}\n\n// Option sets options for the template. Options are described by\n// strings, either a simple string or \"key=value\". There can be at\n// most one equals sign in an option string. If the option string\n// is unrecognized or otherwise invalid, Option panics.\n//\n// Known options:\n//\n// missingkey: Control the behavior during execution if a map is\n// indexed with a key that is not present in the map.\n//\n//\t\"missingkey=default\" or \"missingkey=invalid\"\n//\t\tThe default behavior: Do nothing and continue execution.\n//\t\tIf printed, the result of the index operation is the string\n//\t\t\"<no value>\".\n//\t\"missingkey=zero\"\n//\t\tThe operation returns the zero value for the map type's element.\n//\t\"missingkey=error\"\n//\t\tExecution stops immediately with an error.\nfunc (s *HTMLEngine) Option(opt ...string) *HTMLEngine {\n\ts.rmu.Lock()\n\ts.options = append(s.options, opt...)\n\ts.rmu.Unlock()\n\treturn s\n}\n\n// Delims sets the action delimiters to the specified strings, to be used in\n// templates. An empty delimiter stands for the\n// corresponding default: {{ or }}.\nfunc (s *HTMLEngine) Delims(left, right string) *HTMLEngine {\n\ts.left, s.right = left, right\n\treturn s\n}\n\n// Layout sets the layout template file which inside should use\n// the {{ yield . }} func to yield the main template file\n// and optionally {{partial/partial_r/render . }} to render other template files like headers and footers\n//\n// The 'tmplLayoutFile' is a relative path of the templates base directory,\n// for the template file with its extension.\n//\n// Example: HTML(\"./templates\", \".html\").Layout(\"layouts/mainLayout.html\")\n//\n//\t// mainLayout.html is inside: \"./templates/layouts/\".\n//\n// Note: Layout can be changed for a specific call\n// action with the option: \"layout\" on the iris' context.Render function.\nfunc (s *HTMLEngine) Layout(layoutFile string) *HTMLEngine {\n\ts.layout = layoutFile\n\treturn s\n}\n\n// AddLayoutFunc adds the function to the template's layout-only function map.\n// It is legal to overwrite elements of the default layout actions:\n// - yield func() (template.HTML, error)\n// - current  func() (string, error)\n// - partial func(partialName string) (template.HTML, error)\n// - partial_r func(partialName string) (template.HTML, error)\n// - render func(fullPartialName string) (template.HTML, error).\nfunc (s *HTMLEngine) AddLayoutFunc(funcName string, funcBody any) *HTMLEngine {\n\ts.rmu.Lock()\n\ts.layoutFuncs[funcName] = funcBody\n\ts.rmu.Unlock()\n\treturn s\n}\n\n// AddFunc adds the function to the template's function map.\n// It is legal to overwrite elements of the default actions:\n// - url func(routeName string, args ...string) string\n// - urlpath func(routeName string, args ...string) string\n// - render func(fullPartialName string) (template.HTML, error).\n// - tr func(lang, key string, args ...any) string\nfunc (s *HTMLEngine) AddFunc(funcName string, funcBody any) {\n\ts.rmu.Lock()\n\ts.funcs[funcName] = funcBody\n\ts.rmu.Unlock()\n}\n\n// SetFuncs overrides the template funcs with the given \"funcMap\".\nfunc (s *HTMLEngine) SetFuncs(funcMap template.FuncMap) *HTMLEngine {\n\ts.rmu.Lock()\n\ts.funcs = funcMap\n\ts.rmu.Unlock()\n\n\treturn s\n}\n\n// Funcs adds the elements of the argument map to the template's function map.\n// It is legal to overwrite elements of the map. The return\n// value is the template, so calls can be chained.\nfunc (s *HTMLEngine) Funcs(funcMap template.FuncMap) *HTMLEngine {\n\ts.rmu.Lock()\n\tfor k, v := range funcMap {\n\t\ts.funcs[k] = v\n\t}\n\ts.rmu.Unlock()\n\n\treturn s\n}\n\n// Load parses the templates to the engine.\n// It's also responsible to add the necessary global functions.\n//\n// Returns an error if something bad happens, caller is responsible to handle that.\nfunc (s *HTMLEngine) Load() error {\n\ts.rmu.Lock()\n\tdefer s.rmu.Unlock()\n\n\treturn s.load()\n}\n\nfunc (s *HTMLEngine) load() error {\n\tif s.onLoad != nil {\n\t\ts.onLoad()\n\t}\n\n\tif err := s.reloadCustomTemplates(); err != nil {\n\t\treturn err\n\t}\n\n\t// If only custom templates should be loaded.\n\tif (s.fs == nil || context.IsNoOpFS(s.fs)) && len(s.Templates.DefinedTemplates()) > 0 {\n\t\treturn nil\n\t}\n\n\trootDirName := getRootDirName(s.fs)\n\n\terr := walk(s.fs, \"\", func(path string, info os.FileInfo, err error) error {\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif info == nil || info.IsDir() {\n\t\t\treturn nil\n\t\t}\n\n\t\tif s.extension != \"\" {\n\t\t\tif !strings.HasSuffix(path, s.extension) {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\n\t\tif s.rootDir == rootDirName {\n\t\t\tpath = strings.TrimPrefix(path, rootDirName)\n\t\t\tpath = strings.TrimPrefix(path, \"/\")\n\t\t}\n\n\t\tbuf, err := asset(s.fs, path)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"%s: %w\", path, err)\n\t\t}\n\n\t\treturn s.parseTemplate(path, buf, nil)\n\t})\n\n\tif s.onLoaded != nil {\n\t\ts.onLoaded()\n\t}\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif s.Templates == nil {\n\t\treturn fmt.Errorf(\"no templates found\")\n\t}\n\n\treturn nil\n}\n\nfunc (s *HTMLEngine) reloadCustomTemplates() error {\n\tfor _, tmpl := range s.customCache {\n\t\tif err := s.parseTemplate(tmpl.name, tmpl.contents, tmpl.funcs); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// ParseTemplate adds a custom template to the root template.\nfunc (s *HTMLEngine) ParseTemplate(name string, contents []byte, funcs template.FuncMap) (err error) {\n\ts.rmu.Lock()\n\tdefer s.rmu.Unlock()\n\n\ts.customCache = append(s.customCache, customTmp{\n\t\tname:     name,\n\t\tcontents: contents,\n\t\tfuncs:    funcs,\n\t})\n\n\treturn s.parseTemplate(name, contents, funcs)\n}\n\nfunc (s *HTMLEngine) parseTemplate(name string, contents []byte, funcs template.FuncMap) (err error) {\n\ts.initRootTmpl()\n\n\tname = strings.TrimPrefix(name, \"/\")\n\ttmpl := s.Templates.New(name)\n\t// tmpl.Option(\"missingkey=error\")\n\ttmpl.Option(s.options...)\n\n\tvar text string\n\n\tif s.middleware != nil {\n\t\ttext, err = s.middleware(name, contents)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t} else {\n\t\ttext = string(contents)\n\t}\n\n\ttmpl.Funcs(s.getBuiltinFuncs(name)).Funcs(s.funcs)\n\n\tif strings.Contains(name, \"layout\") {\n\t\ttmpl.Funcs(s.layoutFuncs)\n\t}\n\n\tif len(funcs) > 0 {\n\t\ttmpl.Funcs(funcs) // custom for this template.\n\t}\n\t_, err = tmpl.Parse(text)\n\treturn\n}\n\nfunc (s *HTMLEngine) initRootTmpl() { // protected by the caller.\n\tif s.Templates == nil {\n\t\t// the root template should be the same,\n\t\t// no matter how many reloads as the\n\t\t// following unexported fields cannot be modified.\n\t\t// However, on reload they should be cleared otherwise we get an error.\n\t\ts.Templates = template.New(s.rootDir)\n\t\ts.Templates.Delims(s.left, s.right)\n\t}\n}\n\nfunc (s *HTMLEngine) executeTemplateBuf(name string, binding any) (string, error) {\n\tbuf := s.bufPool.Get().(*bytes.Buffer)\n\tbuf.Reset()\n\n\terr := s.Templates.ExecuteTemplate(buf, name, binding)\n\tresult := buf.String()\n\ts.bufPool.Put(buf)\n\treturn result, err\n}\n\nfunc (s *HTMLEngine) getBuiltinRuntimeLayoutFuncs(name string) template.FuncMap {\n\tfuncs := template.FuncMap{\n\t\t\"yield\": func(binding any) (template.HTML, error) {\n\t\t\tresult, err := s.executeTemplateBuf(name, binding)\n\t\t\t// Return safe HTML here since we are rendering our own template.\n\t\t\treturn template.HTML(result), err\n\t\t},\n\t}\n\n\treturn funcs\n}\n\nfunc (s *HTMLEngine) getBuiltinFuncs(name string) template.FuncMap {\n\tfuncs := template.FuncMap{\n\t\t\"part\": func(partName string, binding any) (template.HTML, error) {\n\t\t\tnameTemp := strings.ReplaceAll(name, s.extension, \"\")\n\t\t\tfullPartName := fmt.Sprintf(\"%s-%s\", nameTemp, partName)\n\t\t\tresult, err := s.executeTemplateBuf(fullPartName, binding)\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", nil\n\t\t\t}\n\t\t\treturn template.HTML(result), err\n\t\t},\n\t\t\"current\": func() (string, error) {\n\t\t\treturn name, nil\n\t\t},\n\t\t\"partial\": func(partialName string, binding any) (template.HTML, error) {\n\t\t\tfullPartialName := fmt.Sprintf(\"%s-%s\", partialName, name)\n\t\t\tif s.Templates.Lookup(fullPartialName) != nil {\n\t\t\t\tresult, err := s.executeTemplateBuf(fullPartialName, binding)\n\t\t\t\treturn template.HTML(result), err\n\t\t\t}\n\t\t\treturn \"\", nil\n\t\t},\n\t\t// partial related to current page,\n\t\t// it would be easier for adding pages' style/script inline\n\t\t// for example when using partial_r '.script' in layout.html\n\t\t// templates/users/index.html would load templates/users/index.script.html\n\t\t\"partial_r\": func(partialName string, binding any) (template.HTML, error) {\n\t\t\text := filepath.Ext(name)\n\t\t\troot := name[:len(name)-len(ext)]\n\t\t\tfullPartialName := fmt.Sprintf(\"%s%s%s\", root, partialName, ext)\n\t\t\tif s.Templates.Lookup(fullPartialName) != nil {\n\t\t\t\tresult, err := s.executeTemplateBuf(fullPartialName, binding)\n\t\t\t\treturn template.HTML(result), err\n\t\t\t}\n\t\t\treturn \"\", nil\n\t\t},\n\t\t\"render\": func(fullPartialName string, binding any) (template.HTML, error) {\n\t\t\tresult, err := s.executeTemplateBuf(fullPartialName, binding)\n\t\t\treturn template.HTML(result), err\n\t\t},\n\t}\n\n\treturn funcs\n}\n\n// ExecuteWriter executes a template and writes its result to the w writer.\nfunc (s *HTMLEngine) ExecuteWriter(w io.Writer, name string, layout string, bindingData any) error {\n\t// re-parse the templates if reload is enabled.\n\tif s.reload {\n\t\ts.rmu.Lock()\n\t\tdefer s.rmu.Unlock()\n\n\t\ts.Templates = nil\n\t\t// we lose the templates parsed manually, so store them when it's called\n\t\t// in order for load to take care of them too.\n\n\t\tif err := s.load(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif layout = getLayout(layout, s.layout); layout != \"\" {\n\t\tlt := s.Templates.Lookup(layout)\n\t\tif lt == nil {\n\t\t\treturn ErrNotExist{Name: layout, IsLayout: true, Data: bindingData}\n\t\t}\n\n\t\treturn lt.Funcs(s.getBuiltinRuntimeLayoutFuncs(name)).Execute(w, bindingData)\n\t}\n\n\tt := s.Templates.Lookup(name)\n\tif t == nil {\n\t\treturn ErrNotExist{Name: name, IsLayout: false, Data: bindingData}\n\t}\n\n\treturn t.Execute(w, bindingData)\n}\n"
  },
  {
    "path": "view/jet.go",
    "content": "package view\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\n\t\"github.com/CloudyKit/jet/v6\"\n)\n\nconst jetEngineName = \"jet\"\n\n// JetEngine is the jet template parser's view engine.\ntype JetEngine struct {\n\tfs          fs.FS\n\trootDir     string\n\textension   string\n\tleft, right string\n\n\tloader jet.Loader\n\n\tdevelopmentMode bool\n\n\t// The Set is the `*jet.Set`, exported to offer any custom capabilities that jet users may want.\n\t// Available after `Load`.\n\tSet *jet.Set\n\tmu  sync.Mutex\n\n\t// Note that global vars and functions are set in a single spot on the jet parser.\n\t// If AddFunc or AddVar called before `Load` then these will be set here to be used via `Load` and clear.\n\tvars map[string]any\n\n\tjetDataContextKey string\n}\n\nvar (\n\t_ Engine       = (*JetEngine)(nil)\n\t_ EngineFuncer = (*JetEngine)(nil)\n)\n\n// jet library does not export or give us any option to modify them via Set\n// (unless we parse the files by ourselves but this is not a smart choice).\nvar jetExtensions = [...]string{\n\t\".html.jet\",\n\t\".jet.html\",\n\t\".jet\",\n}\n\n// Jet creates and returns a new jet view engine.\n// The given \"extension\" MUST begin with a dot.\n//\n// Usage:\n// Jet(\"./views\", \".jet\") or\n// Jet(iris.Dir(\"./views\"), \".jet\") or\n// Jet(embed.FS, \".jet\") or Jet(AssetFile(), \".jet\") for embedded data.\nfunc Jet(dirOrFS any, extension string) *JetEngine {\n\textOK := false\n\tfor _, ext := range jetExtensions {\n\t\tif ext == extension {\n\t\t\textOK = true\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif !extOK {\n\t\tpanic(fmt.Sprintf(\"%s extension is not a valid jet engine extension[%s]\", extension, strings.Join(jetExtensions[0:], \", \")))\n\t}\n\n\ts := &JetEngine{\n\t\tfs:                getFS(dirOrFS),\n\t\trootDir:           \"/\",\n\t\textension:         extension,\n\t\tloader:            &jetLoader{fs: getFS(dirOrFS)},\n\t\tjetDataContextKey: \"_jet\",\n\t}\n\n\treturn s\n}\n\n// String returns the name of this view engine, the \"jet\".\nfunc (s *JetEngine) String() string {\n\treturn jetEngineName\n}\n\n// RootDir sets the directory to be used as a starting point\n// to load templates from the provided file system.\nfunc (s *JetEngine) RootDir(root string) *JetEngine {\n\tif s.fs != nil && root != \"\" && root != \"/\" && root != \".\" && root != s.rootDir {\n\t\tsub, err := fs.Sub(s.fs, s.rootDir)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\n\t\ts.fs = sub\n\t}\n\n\ts.rootDir = filepath.ToSlash(root)\n\treturn s\n}\n\n// Name returns the jet engine's name.\nfunc (s *JetEngine) Name() string {\n\treturn \"Jet\"\n}\n\n// Ext should return the final file extension which this view engine is responsible to render.\n// If the filename extension on ExecuteWriter is empty then this is appended.\nfunc (s *JetEngine) Ext() string {\n\treturn s.extension\n}\n\n// Delims sets the action delimiters to the specified strings, to be used in\n// templates. An empty delimiter stands for the\n// corresponding default: {{ or }}.\n// Should act before `Load` or `iris.Application#RegisterView`.\nfunc (s *JetEngine) Delims(left, right string) *JetEngine {\n\ts.left = left\n\ts.right = right\n\treturn s\n}\n\n// JetArguments is a type alias of `jet.Arguments`,\n// can be used on `AddFunc$funcBody`.\ntype JetArguments = jet.Arguments\n\n// AddFunc should adds a global function to the jet template set.\nfunc (s *JetEngine) AddFunc(funcName string, funcBody any) {\n\t// if something like \"urlpath\" is registered.\n\tif generalFunc, ok := funcBody.(func(string, ...any) string); ok {\n\t\t// jet, unlike others does not accept a func(string, ...any) string,\n\t\t// instead it wants:\n\t\t// func(JetArguments) reflect.Value.\n\n\t\ts.AddVar(funcName, jet.Func(func(args JetArguments) reflect.Value {\n\t\t\tn := args.NumOfArguments()\n\t\t\tif n == 0 { // no input, don't execute the function, panic instead.\n\t\t\t\tpanic(funcName + \" expects one or more input arguments\")\n\t\t\t}\n\n\t\t\tfirstInput := args.Get(0).String()\n\n\t\t\tif n == 1 { // if only the first argument is given.\n\t\t\t\treturn reflect.ValueOf(generalFunc(firstInput))\n\t\t\t}\n\n\t\t\t// if has variadic.\n\n\t\t\tvariadicN := n - 1\n\t\t\tvariadicInputs := make([]any, variadicN) // except the first one.\n\n\t\t\tfor i := 0; i < variadicN; i++ {\n\t\t\t\tvariadicInputs[i] = args.Get(i + 1).Interface()\n\t\t\t}\n\n\t\t\treturn reflect.ValueOf(generalFunc(firstInput, variadicInputs...))\n\t\t}))\n\n\t\treturn\n\t}\n\n\tif jetFunc, ok := funcBody.(jet.Func); !ok {\n\t\talternativeJetFunc, ok := funcBody.(func(JetArguments) reflect.Value)\n\t\tif !ok {\n\t\t\tpanic(fmt.Sprintf(\"JetEngine.AddFunc: funcBody argument is not a type of func(JetArguments) reflect.Value. Got %T instead\", funcBody))\n\t\t}\n\n\t\ts.AddVar(funcName, jet.Func(alternativeJetFunc))\n\t} else {\n\t\ts.AddVar(funcName, jetFunc)\n\t}\n}\n\n// AddVar adds a global variable to the jet template set.\nfunc (s *JetEngine) AddVar(key string, value any) {\n\tif s.Set != nil {\n\t\ts.Set.AddGlobal(key, value)\n\t} else {\n\t\tif s.vars == nil {\n\t\t\ts.vars = make(map[string]any)\n\t\t}\n\t\ts.vars[key] = value\n\t}\n}\n\n// Reload if setted to true the templates are reloading on each render,\n// use it when you're in development and you're boring of restarting\n// the whole app when you edit a template file.\n//\n// Note that if `true` is passed then only one `View -> ExecuteWriter` will be render each time,\n// not safe concurrent access across clients, use it only on development state.\nfunc (s *JetEngine) Reload(developmentMode bool) *JetEngine {\n\ts.developmentMode = developmentMode\n\treturn s\n}\n\n// SetLoader can be used when the caller wants to use something like\n// multi.Loader or httpfs.Loader.\nfunc (s *JetEngine) SetLoader(loader jet.Loader) *JetEngine {\n\ts.loader = loader\n\treturn s\n}\n\ntype jetLoader struct {\n\tfs fs.FS\n}\n\nvar _ jet.Loader = (*jetLoader)(nil)\n\n// Open opens a file from file system.\nfunc (l *jetLoader) Open(name string) (io.ReadCloser, error) {\n\tname = strings.TrimPrefix(name, \"/\")\n\treturn l.fs.Open(name)\n}\n\n// Exists checks if the template name exists by walking the list of template paths.\nfunc (l *jetLoader) Exists(name string) bool {\n\tname = strings.TrimPrefix(name, \"/\")\n\t_, err := l.fs.Open(name)\n\treturn err == nil\n}\n\n// Load should load the templates from a physical system directory or by an embedded one (assets/go-bindata).\nfunc (s *JetEngine) Load() error {\n\trootDirName := getRootDirName(s.fs)\n\n\treturn walk(s.fs, \"\", func(path string, info os.FileInfo, err error) error {\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif info == nil || info.IsDir() {\n\t\t\treturn nil\n\t\t}\n\n\t\tif s.extension != \"\" {\n\t\t\tif !strings.HasSuffix(path, s.extension) {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\n\t\tif s.rootDir == rootDirName {\n\t\t\tpath = strings.TrimPrefix(path, rootDirName)\n\t\t\tpath = strings.TrimPrefix(path, \"/\")\n\t\t}\n\n\t\tbuf, err := asset(s.fs, path)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"%s: %w\", path, err)\n\t\t}\n\n\t\treturn s.ParseTemplate(path, string(buf))\n\t})\n}\n\n// ParseTemplate accepts a name and contnets to parse and cache a template.\n// This parser does not support funcs per template. Use the `AddFunc` instead.\nfunc (s *JetEngine) ParseTemplate(name string, contents string) error {\n\ts.initSet()\n\n\t_, err := s.Set.Parse(name, contents)\n\treturn err\n}\n\nfunc (s *JetEngine) initSet() {\n\ts.mu.Lock()\n\tif s.Set == nil {\n\t\tvar opts = []jet.Option{\n\t\t\tjet.WithDelims(s.left, s.right),\n\t\t}\n\t\tif s.developmentMode && !context.IsNoOpFS(s.fs) {\n\t\t\t// this check is made to avoid jet's fs lookup on noOp fs (nil passed by the developer).\n\t\t\t// This can be produced when nil fs passed\n\t\t\t// and only `ParseTemplate` is used.\n\t\t\topts = append(opts, jet.InDevelopmentMode())\n\t\t}\n\n\t\ts.Set = jet.NewSet(s.loader, opts...)\n\t\tfor key, value := range s.vars {\n\t\t\ts.Set.AddGlobal(key, value)\n\t\t}\n\t}\n\ts.mu.Unlock()\n}\n\ntype (\n\t// JetRuntimeVars is a type alias for `jet.VarMap`.\n\t// Can be used at `AddJetRuntimeVars/JetEngine.AddRuntimeVars`\n\t// to set a runtime variable ${name} to the executing template.\n\tJetRuntimeVars = jet.VarMap\n\n\t// JetRuntime is a type alias of `jet.Runtime`,\n\t// can be used on RuntimeVariable input function.\n\tJetRuntime = jet.Runtime\n)\n\n// JetRuntimeVarsContextKey is the Iris Context key to keep any custom jet runtime variables.\n// See `AddJetRuntimeVars` package-level function and `JetEngine.AddRuntimeVars` method.\nconst JetRuntimeVarsContextKey = \"iris.jetvarmap\"\n\n// AddJetRuntimeVars sets or inserts runtime jet variables through the Iris Context.\n// This gives the ability to add runtime variables from different handlers in the request chain,\n// something that the jet template parser does not offer at all.\n//\n// Usage: view.AddJetRuntimeVars(ctx, view.JetRuntimeVars{...}).\n// See `JetEngine.AddRuntimeVars` too.\nfunc AddJetRuntimeVars(ctx *context.Context, jetVarMap JetRuntimeVars) {\n\tif v := ctx.Values().Get(JetRuntimeVarsContextKey); v != nil {\n\t\tif vars, ok := v.(JetRuntimeVars); ok {\n\t\t\tfor key, value := range jetVarMap {\n\t\t\t\tvars[key] = value\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t}\n\n\tctx.Values().Set(JetRuntimeVarsContextKey, jetVarMap)\n}\n\n// AddRuntimeVars sets or inserts runtime jet variables through the Iris Context.\n// This gives the ability to add runtime variables from different handlers in the request chain,\n// something that the jet template parser does not offer at all.\n//\n// Usage: view.AddJetRuntimeVars(ctx, view.JetRuntimeVars{...}).\n// See `view.AddJetRuntimeVars` if package-level access is more meanful to the code flow.\nfunc (s *JetEngine) AddRuntimeVars(ctx *context.Context, vars JetRuntimeVars) {\n\tAddJetRuntimeVars(ctx, vars)\n}\n\n// ExecuteWriter should execute a template by its filename with an optional layout and bindingData.\nfunc (s *JetEngine) ExecuteWriter(w io.Writer, filename string, layout string, bindingData any) error {\n\ttmpl, err := s.Set.GetTemplate(filename)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tvar vars JetRuntimeVars\n\n\tif ctx, ok := w.(*context.Context); ok {\n\t\truntimeVars := ctx.Values().Get(JetRuntimeVarsContextKey)\n\t\tif runtimeVars != nil {\n\t\t\tif jetVars, ok := runtimeVars.(JetRuntimeVars); ok {\n\t\t\t\tvars = jetVars\n\t\t\t}\n\t\t}\n\n\t\tif viewContextData := ctx.GetViewData(); len(viewContextData) > 0 { // fix #1876\n\t\t\tif vars == nil {\n\t\t\t\tvars = make(JetRuntimeVars)\n\t\t\t}\n\n\t\t\tfor k, v := range viewContextData {\n\t\t\t\tval, ok := v.(reflect.Value)\n\t\t\t\tif !ok {\n\t\t\t\t\tval = reflect.ValueOf(v)\n\t\t\t\t}\n\t\t\t\tvars[k] = val\n\t\t\t}\n\t\t}\n\n\t\tif v := ctx.Values().Get(s.jetDataContextKey); v != nil {\n\t\t\tif bindingData == nil {\n\t\t\t\t// if bindingData is nil, try to fill them by context key (a middleware can set data).\n\t\t\t\tbindingData = v\n\t\t\t} else if m, ok := bindingData.(context.Map); ok {\n\t\t\t\t// else if bindingData are passed to App/Context.View\n\t\t\t\t// and it's map try to fill with the new values passed from a middleware.\n\t\t\t\tif mv, ok := v.(context.Map); ok {\n\t\t\t\t\tfor key, value := range mv {\n\t\t\t\t\t\tm[key] = value\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t}\n\n\tif bindingData == nil {\n\t\treturn tmpl.Execute(w, vars, nil)\n\t}\n\n\tif vars == nil {\n\t\tvars = make(JetRuntimeVars)\n\t}\n\n\t/* fixed on jet v4.0.0, so no need of this:\n\tif m, ok := bindingData.(context.Map); ok {\n\t\tvar jetData any\n\t\tfor k, v := range m {\n\t\t\tif k == s.jetDataContextKey {\n\t\t\t\tjetData = v\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif value, ok := v.(reflect.Value); ok {\n\t\t\t\tvars[k] = value\n\t\t\t} else {\n\t\t\t\tvars[k] = reflect.ValueOf(v)\n\t\t\t}\n\t\t}\n\n\t\tif jetData != nil {\n\t\t\tbindingData = jetData\n\t\t}\n\t}*/\n\n\treturn tmpl.Execute(w, vars, bindingData)\n}\n"
  },
  {
    "path": "view/pug.go",
    "content": "package view\n\nimport (\n\t\"bytes\"\n\t\"os\"\n\n\t\"github.com/Joker/jade\"\n)\n\n// Pug (or Jade) returns a new pug view engine.\n// It shares the same exactly logic with the\n// html view engine, it uses the same exactly configuration.\n// The given \"extension\" MUST begin with a dot.\n//\n// Read more about the Jade Go Parser: https://github.com/Joker/jade\n//\n// Usage:\n// Pug(\"./views\", \".pug\") or\n// Pug(iris.Dir(\"./views\"), \".pug\") or\n// Pug(embed.FS, \".pug\") or Pug(AssetFile(), \".pug\") for embedded data.\n//\n// Examples:\n// https://github.com/kataras/iris/tree/main/_examples/view/template_pug_0\n// https://github.com/kataras/iris/tree/main/_examples/view/template_pug_1\n// https://github.com/kataras/iris/tree/main/_examples/view/template_pug_2\n// https://github.com/kataras/iris/tree/main/_examples/view/template_pug_3\nfunc Pug(fs any, extension string) *HTMLEngine {\n\ts := HTML(fs, extension)\n\ts.name = \"Pug\"\n\ts.middleware = func(name string, text []byte) (contents string, err error) {\n\t\tjade.ReadFunc = func(filename string) ([]byte, error) {\n\t\t\treturn asset(s.fs, filename)\n\t\t}\n\n\t\ttmpl := jade.New(name)\n\t\texec, err := tmpl.Parse(text)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\n\t\tb := new(bytes.Buffer)\n\t\texec.WriteIn(b)\n\t\tjade.ReadFunc = os.ReadFile // reset to original.\n\t\treturn b.String(), nil\n\t}\n\treturn s\n}\n"
  },
  {
    "path": "view/view.go",
    "content": "package view\n\nimport (\n\t\"fmt\"\n\t\"html/template\"\n\t\"io\"\n\t\"strings\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\n\t\"github.com/kataras/golog\"\n)\n\ntype (\n\t// Engine is the interface for a compatible Iris view engine.\n\t// It's an alias of context.ViewEngine.\n\tEngine = context.ViewEngine\n\t// EngineFuncer is the interface for a compatible Iris view engine\n\t// which accepts builtin framework functions such as url, urlpath and tr.\n\t// It's an alias of context.ViewEngineFuncer.\n\tEngineFuncer = context.ViewEngineFuncer\n)\n\n// ErrNotExist reports whether a template was not found in the parsed templates tree.\ntype ErrNotExist = context.ErrViewNotExist\n\n// View is just a wrapper on top of the registered template engine.\ntype View struct{ Engine }\n\n// Register registers a view engine.\nfunc (v *View) Register(e Engine) {\n\tif v.Engine != nil {\n\t\tgolog.Warnf(\"Engine already exists, replacing the old %q with the new one %q\", v.Engine.Name(), e.Name())\n\t}\n\n\tv.Engine = e\n}\n\n// Registered reports whether an engine was registered.\nfunc (v *View) Registered() bool {\n\treturn v.Engine != nil\n}\n\nfunc (v *View) ensureTemplateName(s string) string {\n\tif s == \"\" || s == NoLayout {\n\t\treturn s\n\t}\n\n\ts = strings.TrimPrefix(s, \"/\")\n\n\tif ext := v.Engine.Ext(); ext != \"\" {\n\t\tif !strings.HasSuffix(s, ext) {\n\t\t\treturn s + ext\n\t\t}\n\t}\n\n\treturn s\n}\n\n// ExecuteWriter calls the correct view Engine's ExecuteWriter func\nfunc (v *View) ExecuteWriter(w io.Writer, filename string, layout string, bindingData any) error {\n\tfilename = v.ensureTemplateName(filename)\n\tlayout = v.ensureTemplateName(layout)\n\n\treturn v.Engine.ExecuteWriter(w, filename, layout, bindingData)\n}\n\n// AddFunc adds a function to all registered engines.\n// Each template engine that supports functions has its own AddFunc too.\nfunc (v *View) AddFunc(funcName string, funcBody any) {\n\tif !v.Registered() {\n\t\treturn\n\t}\n\n\tif e, ok := v.Engine.(EngineFuncer); ok {\n\t\te.AddFunc(funcName, funcBody)\n\t}\n}\n\n// Funcs registers a template func map to the registered view engine(s).\nfunc (v *View) Funcs(m template.FuncMap) *View {\n\tif !v.Registered() {\n\t\treturn v\n\t}\n\n\tif e, ok := v.Engine.(EngineFuncer); ok {\n\t\tfor k, v := range m {\n\t\t\te.AddFunc(k, v)\n\t\t}\n\t}\n\n\treturn v\n}\n\n// Load compiles all the registered engines.\nfunc (v *View) Load() error {\n\tif !v.Registered() {\n\t\treturn fmt.Errorf(\"no engine was registered\")\n\t}\n\treturn v.Engine.Load()\n}\n\n// NoLayout disables the configuration's layout for a specific execution.\nconst NoLayout = \"iris.nolayout\"\n\n// returns empty if it's no layout or empty layout and empty configuration's layout.\nfunc getLayout(layout string, globalLayout string) string {\n\tif layout == NoLayout {\n\t\treturn \"\"\n\t}\n\n\tif layout == \"\" && globalLayout != \"\" {\n\t\treturn globalLayout\n\t}\n\n\treturn layout\n}\n"
  },
  {
    "path": "websocket/aliases.go",
    "content": "package websocket\n\nimport (\n\t\"github.com/kataras/neffos\"\n\t\"github.com/kataras/neffos/gobwas\"\n\t\"github.com/kataras/neffos/gorilla\"\n\t\"github.com/kataras/neffos/stackexchange/redis\"\n)\n\ntype (\n\t// Dialer is the definition type of a dialer, gorilla or gobwas or custom.\n\t// It is the second parameter of the `Dial` function.\n\tDialer = neffos.Dialer\n\t// GorillaDialerOptions is just an alias for the `gobwas/ws.Dialer` struct type.\n\tGorillaDialerOptions = gorilla.Options\n\t// GobwasDialerOptions is just an alias for the `gorilla/websocket.Dialer` struct type.\n\tGobwasDialerOptions = gobwas.Options\n\t// GobwasHeader is an alias to the adapter that allows the use of `http.Header` as\n\t// handshake headers.\n\tGobwasHeader = gobwas.Header\n\n\t// Conn describes the main websocket connection's functionality.\n\t// Its `Connection` will return a new `NSConn` instance.\n\t// Each connection can connect to one or more declared namespaces.\n\t// Each `NSConn` can join to multiple rooms.\n\tConn = neffos.Conn\n\t// NSConn describes a connection connected to a specific namespace,\n\t// it emits with the `Message.Namespace` filled and it can join to multiple rooms.\n\t// A single `Conn` can be connected to one or more namespaces,\n\t// each connected namespace is described by this structure.\n\tNSConn = neffos.NSConn\n\t// Room describes a connected connection to a room,\n\t// emits messages with the `Message.Room` filled to the specific room\n\t// and `Message.Namespace` to the underline `NSConn`'s namespace.\n\tRoom = neffos.Room\n\t// CloseError can be used to send and close a remote connection in the event callback's return statement.\n\tCloseError = neffos.CloseError\n\n\t// MessageHandlerFunc is the definition type of the events' callback.\n\t// Its error can be written to the other side on specific events,\n\t// i.e on `OnNamespaceConnect` it will abort a remote namespace connection.\n\t// See examples for more.\n\tMessageHandlerFunc = neffos.MessageHandlerFunc\n\t// ConnHandler is the interface which namespaces and events can be retrieved through.\n\tConnHandler = neffos.ConnHandler\n\t// Events completes the `ConnHandler` interface.\n\t// It is a map which its key is the event name\n\t// and its value the event's callback.\n\t//\n\t// Events type completes the `ConnHandler` itself therefore,\n\t// can be used as standalone value on the `New` and `Dial` functions\n\t// to register events on empty namespace as well.\n\t//\n\t// See `Namespaces`, `New` and `Dial` too.\n\tEvents = neffos.Events\n\t// Namespaces completes the `ConnHandler` interface.\n\t// Can be used to register one or more namespaces on the `New` and `Dial` functions.\n\t// The key is the namespace literal and the value is the `Events`,\n\t// a map with event names and their callbacks.\n\t//\n\t// See `WithTimeout`, `New` and `Dial` too.\n\tNamespaces = neffos.Namespaces\n\t// WithTimeout completes the `ConnHandler` interface.\n\t// Can be used to register namespaces and events or just events on an empty namespace\n\t// with Read and Write timeouts.\n\t//\n\t// See `New` and `Dial`.\n\tWithTimeout = neffos.WithTimeout\n\t// Struct completes the `ConnHandler` interface.\n\t// It uses a structure to register a specific namespace and its events.\n\tStruct = neffos.Struct\n\t// StructInjector can be used to customize the value creation that can is used on serving events.\n\tStructInjector = neffos.StructInjector\n\t// The Message is the structure which describes the incoming and outcoming data.\n\t// Emitter's \"body\" argument is the `Message.Body` field.\n\t// Emitter's return non-nil error is the `Message.Err` field.\n\t// If native message sent then the `Message.Body` is filled with the body and\n\t// when incoming native message then the `Message.Event` is the `OnNativeMessage`,\n\t// native messages are allowed only when an empty namespace(\"\") and its `OnNativeMessage` callback are present.\n\tMessage = neffos.Message\n\t// StackExchange is an optional interface\n\t// that can be used to change the way neffos\n\t// sends messages to its clients, i.e\n\t// communication between multiple neffos servers.\n\t//\n\t// See `NewRedisStackExchange` to create a new redis StackExchange.\n\tStackExchange = neffos.StackExchange\n\t// RedisStackExchange is a `neffos.StackExchange` for redis.\n\tRedisStackExchange = redis.StackExchange\n\t// RedisConfig is used on the `NewRedisStackExchange` package-level function.\n\t// Can be used to customize the redis client dialer.\n\tRedisConfig = redis.Config\n)\n"
  },
  {
    "path": "websocket/websocket.go",
    "content": "package websocket\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\n\t\"github.com/kataras/neffos\"\n\t\"github.com/kataras/neffos/gobwas\"\n\t\"github.com/kataras/neffos/gorilla\"\n\t\"github.com/kataras/neffos/stackexchange/nats\"\n\t\"github.com/kataras/neffos/stackexchange/redis\"\n)\n\nvar (\n\t// EnableDebug enables debug mode for websocket module,\n\t// for MVC this is done automatically\n\t// when the app's logger level is set to \"debug\".\n\tEnableDebug = neffos.EnableDebug\n\t// GorillaUpgrader is an upgrader type for the gorilla/websocket subprotocol implementation.\n\t// Should be used on `New` to construct the websocket server.\n\tGorillaUpgrader = gorilla.Upgrader\n\t// GobwasUpgrader is an upgrader type for the gobwas/ws subprotocol implementation.\n\t// Should be used on `New` to construct the websocket server.\n\tGobwasUpgrader = gobwas.Upgrader\n\t// DefaultGorillaUpgrader is a gorilla/websocket Upgrader with all fields set to the default values.\n\tDefaultGorillaUpgrader = gorilla.DefaultUpgrader\n\t// DefaultGobwasUpgrader is a gobwas/ws Upgrader with all fields set to the default values.\n\tDefaultGobwasUpgrader = gobwas.DefaultUpgrader\n\t// New constructs and returns a new websocket server.\n\t// Listens to incoming connections automatically, no further action is required from the caller.\n\t// The second parameter is the \"connHandler\", it can be\n\t// filled as `Namespaces`, `Events` or `WithTimeout`, same namespaces and events can be used on the client-side as well,\n\t// Use the `Conn#IsClient` on any event callback to determinate if it's a client-side connection or a server-side one.\n\t//\n\t// See examples for more.\n\tNew = neffos.New\n\t// DefaultIDGenerator returns a universal unique identifier for a new connection.\n\t// It's the default `IDGenerator` if missing.\n\tDefaultIDGenerator = func(ctx *context.Context) string {\n\t\treturn neffos.DefaultIDGenerator(ctx.ResponseWriter(), ctx.Request())\n\t}\n\n\t// NewRedisStackExchange returns a new redis StackExchange.\n\t// The \"channel\" input argument is the channel prefix for publish and subscribe.\n\tNewRedisStackExchange = redis.NewStackExchange\n\t// NewNatsStackExchange returns a new nats StackExchange.\n\t// The \"url\" input argument is the connection string of your nats server.\n\t// The second variadic input argument can be used to use custom `nats.Option`s\n\t// such as authentication and more nats servers addresses.\n\tNewNatsStackExchange = nats.NewStackExchange\n\t// WithNatsOptions can be used as the second input argument of `NewNatsStackExchange`\n\t// to declare a struct-based configuration for the nats server(s).\n\tWithNatsOptions = nats.With\n\n\t// GorillaDialer is a `Dialer` type for the gorilla/websocket subprotocol implementation.\n\t// Should be used on `Dial` to create a new client/client-side connection.\n\tGorillaDialer = gorilla.Dialer\n\t// GobwasDialer is a `Dialer` type for the gobwas/ws subprotocol implementation.\n\t// Should be used on `Dial` to create a new client/client-side connection.\n\tGobwasDialer = gobwas.Dialer\n\t// DefaultGorillaDialer is a gorilla/websocket dialer with all fields set to the default values.\n\tDefaultGorillaDialer = gorilla.DefaultDialer\n\t// DefaultGobwasDialer is a gobwas/ws dialer with all fields set to the default values.\n\tDefaultGobwasDialer = gobwas.DefaultDialer\n\t// Dial establishes a new websocket client connection.\n\t// Context \"ctx\" is used for handshake timeout.\n\t// Dialer \"dial\" can be either `GorillaDialer` or `GobwasDialer`,\n\t// custom dialers can be used as well when complete the `Socket` and `Dialer` interfaces for valid client.\n\t// URL \"url\" is the endpoint of the websocket server, i.e \"ws://localhost:8080/echo\".\n\t// The last parameter, and the most important one is the \"connHandler\", it can be\n\t// filled as `Namespaces`, `Events` or `WithTimeout`, same namespaces and events can be used on the server-side as well.\n\t//\n\t// See examples for more.\n\tDial = neffos.Dial\n\t// IsTryingToReconnect reports whether the returning \"err\" from the `Server#Upgrade`\n\t// is from a client that was trying to reconnect to the websocket server.\n\t//\n\t// Look the `Conn#WasReconnected` and `Conn#ReconnectTries` too.\n\tIsTryingToReconnect = neffos.IsTryingToReconnect\n\t// NewStruct returns the `Struct` Conn Handler based on ptr value.\n\tNewStruct = neffos.NewStruct\n\t// JoinConnHandlers combines two or more ConnHandlers as one.\n\tJoinConnHandlers = neffos.JoinConnHandlers\n\t// OnNamespaceConnect is the event name which its callback is fired right before namespace connect,\n\t// if non-nil error then the remote connection's `Conn.Connect` will fail and send that error text.\n\t// Connection is not ready to emit data to the namespace.\n\tOnNamespaceConnect = neffos.OnNamespaceConnect\n\t// OnNamespaceConnected is the event name which its callback is fired after namespace successfully connected.\n\t// Connection is ready to emit data back to the namespace.\n\tOnNamespaceConnected = neffos.OnNamespaceConnected\n\t// OnNamespaceDisconnect is the event name which its callback is fired when\n\t// remote namespace disconnection or local namespace disconnection is happening.\n\t// For server-side connections the reply matters, so if error returned then the client-side cannot disconnect yet,\n\t// for client-side the return value does not matter.\n\tOnNamespaceDisconnect = neffos.OnNamespaceDisconnect // if allowed to connect then it's allowed to disconnect as well.\n\t// OnRoomJoin is the event name which its callback is fired right before room join.\n\tOnRoomJoin = neffos.OnRoomJoin // able to check if allowed to join.\n\t// OnRoomJoined is the event name which its callback is fired after the connection has successfully joined to a room.\n\tOnRoomJoined = neffos.OnRoomJoined // able to broadcast messages to room.\n\t// OnRoomLeave is the event name which its callback is fired right before room leave.\n\tOnRoomLeave = neffos.OnRoomLeave // able to broadcast bye-bye messages to room.\n\t// OnRoomLeft is the event name which its callback is fired after the connection has successfully left from a room.\n\tOnRoomLeft = neffos.OnRoomLeft // if allowed to join to a room, then its allowed to leave from it.\n\t// OnAnyEvent is the event name which its callback is fired when incoming message's event is not declared to the ConnHandler(`Events` or `Namespaces`).\n\tOnAnyEvent = neffos.OnAnyEvent // when event no match.\n\t// OnNativeMessage is fired on incoming native/raw websocket messages.\n\t// If this event defined then an incoming message can pass the check (it's an invalid message format)\n\t// with just the Message's Body filled, the Event is \"OnNativeMessage\" and IsNative always true.\n\t// This event should be defined under an empty namespace in order this to work.\n\tOnNativeMessage = neffos.OnNativeMessage\n\n\t// IsSystemEvent reports whether the \"event\" is a system event,\n\t// OnNamespaceConnect, OnNamespaceConnected, OnNamespaceDisconnect,\n\t// OnRoomJoin, OnRoomJoined, OnRoomLeave and OnRoomLeft.\n\tIsSystemEvent = neffos.IsSystemEvent\n\t// Reply is a special type of custom error which sends a message back to the other side\n\t// with the exact same incoming Message's Namespace (and Room if specified)\n\t// except its body which would be the given \"body\".\n\tReply = neffos.Reply\n\t// Marshal marshals the \"v\" value and returns a Message's Body.\n\t// The \"v\" value's serialized value can be customized by implementing a `Marshal() ([]byte, error) ` method,\n\t// otherwise the default one will be used instead ( see `SetDefaultMarshaler` and `SetDefaultUnmarshaler`).\n\t// Errors are pushed to the result, use the object's Marshal method to catch those when necessary.\n\tMarshal = neffos.Marshal\n)\n\n// SetDefaultMarshaler changes the default json marshaler.\n// See `Marshal` package-level function and `Message.Unmarshal` method for more.\nfunc SetDefaultMarshaler(fn func(v any) ([]byte, error)) {\n\tneffos.DefaultMarshaler = fn\n}\n\n// SetDefaultUnmarshaler changes the default json unmarshaler.\n// See `Message.Unmarshal` method and package-level `Marshal` function for more.\nfunc SetDefaultUnmarshaler(fn func(data []byte, v any) error) {\n\tneffos.DefaultUnmarshaler = fn\n}\n\n// IDGenerator is an iris-specific IDGenerator for new connections.\ntype IDGenerator func(*context.Context) string\n\nfunc wrapIDGenerator(idGen IDGenerator) func(ctx *context.Context) neffos.IDGenerator {\n\treturn func(ctx *context.Context) neffos.IDGenerator {\n\t\treturn func(w http.ResponseWriter, r *http.Request) string {\n\t\t\treturn idGen(ctx)\n\t\t}\n\t}\n}\n\n// Handler returns an Iris handler to be served in a route of an Iris application.\n// Accepts the neffos websocket server as its first input argument\n// and optionally an Iris-specific `IDGenerator` as its second one.\n//\n// This SHOULD be the last handler in the route's chain as it hijacks the connection and the context.\nfunc Handler(s *neffos.Server, idGenerator ...IDGenerator) context.Handler {\n\tidGen := DefaultIDGenerator\n\tif len(idGenerator) > 0 {\n\t\tidGen = idGenerator[0]\n\t}\n\n\tif cb := s.OnDisconnect; cb != nil {\n\t\ts.OnDisconnect = func(c *neffos.Conn) {\n\t\t\tcb(c)\n\t\t\tmanualReleaseWithoutResp(GetContext(c))\n\t\t}\n\t} else {\n\t\ts.OnDisconnect = func(c *neffos.Conn) {\n\t\t\tmanualReleaseWithoutResp(GetContext(c))\n\t\t}\n\t}\n\n\treturn func(ctx *context.Context) {\n\t\tif ctx.IsStopped() {\n\t\t\t// let the framework release it as always;\n\t\t\t// socket was not created so disconnect event will not called and the\n\t\t\t// DisablePoolRelease was not even called yet.\n\t\t\treturn\n\t\t}\n\n\t\tUpgrade(ctx, idGen, s)\n\t}\n}\n\n// Upgrade upgrades the request and returns a new websocket Conn.\n// Use `Handler` for higher-level implementation instead.\nfunc Upgrade(ctx *context.Context, idGen IDGenerator, s *neffos.Server) *neffos.Conn {\n\t/* Do NOT return the error as it is captured on the OnUpgradeError listener,\n\tthe end-developer should not be able to write to this http client directly. */\n\tctx.DisablePoolRelease()\n\n\tconn, upgradeErr := s.Upgrade(ctx.ResponseWriter(), ctx.Request(), func(socket neffos.Socket) neffos.Socket {\n\t\treturn &socketWrapper{\n\t\t\tSocket: socket,\n\t\t\tctx:    ctx,\n\t\t}\n\t}, wrapIDGenerator(idGen)(ctx))\n\n\tif upgradeErr != nil {\n\t\tmanualReleaseWithoutResp(ctx)\n\t}\n\n\treturn conn\n}\n\nfunc manualReleaseWithoutResp(ctx *context.Context) {\n\tctx.ResponseWriter().EndResponse()                   // relases the response writer (common, recorder & compress).\n\tctx.Application().GetContextPool().ReleaseLight(ctx) // just releases the context.\n}\n\ntype socketWrapper struct {\n\tneffos.Socket\n\tctx *context.Context\n}\n\n// GetContext returns the Iris Context from a websocket connection.\n//\n// Note that writing to the client connection through this Context is not allowed\n// from a websocket event because the connection is hijacked.\n// If used, you are limited for read-only access of the request e.g. read the request headers.\nfunc GetContext(c *neffos.Conn) *context.Context {\n\tif sw, ok := c.Socket().(*socketWrapper); ok {\n\t\treturn sw.ctx\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "x/client/client.go",
    "content": "package client\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"mime/multipart\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"golang.org/x/time/rate\"\n)\n\n// A Client is an HTTP client. Initialize with the New package-level function.\ntype Client struct {\n\topts []Option // keep for clones.\n\n\tHTTPClient *http.Client\n\n\t// BaseURL prepends to all requests.\n\tBaseURL string\n\n\t// A list of persistent request options.\n\tPersistentRequestOptions []RequestOption\n\n\t// Optional rate limiter instance initialized by the RateLimit method.\n\trateLimiter *rate.Limiter\n\n\t// Optional handlers that are being fired before and after each new request.\n\trequestHandlers []RequestHandler\n\n\t// store it here for future use.\n\tkeepAlive bool\n}\n\n// New returns a new Iris HTTP Client.\n// Available options:\n// - BaseURL\n// - Timeout\n// - PersistentRequestOptions\n// - RateLimit\n//\n// Look the Client.Do/JSON/... methods to send requests and\n// ReadXXX methods to read responses.\n//\n// The default content type to send and receive data is JSON.\nfunc New(opts ...Option) *Client {\n\tc := &Client{\n\t\topts: opts,\n\n\t\tHTTPClient:               &http.Client{},\n\t\tPersistentRequestOptions: defaultRequestOptions,\n\t\trequestHandlers:          defaultRequestHandlers,\n\t}\n\n\tfor _, opt := range c.opts { // c.opts in order to make with `NoOption` work.\n\t\topt(c)\n\t}\n\n\tif transport, ok := c.HTTPClient.Transport.(*http.Transport); ok {\n\t\tc.keepAlive = !transport.DisableKeepAlives\n\t}\n\n\treturn c\n}\n\n// NoOption is a helper function that clears the previous options in the chain.\n// See `Client.Clone` method.\nvar NoOption = func(c *Client) { c.opts = make([]Option, 0) /* clear previous options */ }\n\n// Clone returns a new Client with the same options as the original.\n// If you want to override the options from the base \"c\" Client,\n// use the `NoOption` variable as the 1st argument.\nfunc (c *Client) Clone(opts ...Option) *Client {\n\treturn New(append(c.opts, opts...)...)\n}\n\n// RegisterRequestHandler registers one or more request handlers\n// to be ran before and after of each new request.\n//\n// Request handler's BeginRequest method run after each request constructed\n// and right before sent to the server.\n//\n// Request handler's EndRequest method run after response each received\n// and right before methods return back to the caller.\n//\n// Any request handlers MUST be set right after the Client's initialization.\nfunc (c *Client) RegisterRequestHandler(reqHandlers ...RequestHandler) {\n\treqHandlersToRegister := make([]RequestHandler, 0, len(reqHandlers))\n\tfor _, h := range reqHandlers {\n\t\tif h == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\treqHandlersToRegister = append(reqHandlersToRegister, h)\n\t}\n\n\tc.requestHandlers = append(c.requestHandlers, reqHandlersToRegister...)\n}\n\nfunc (c *Client) emitBeginRequest(ctx context.Context, req *http.Request) error {\n\tif len(c.requestHandlers) == 0 {\n\t\treturn nil\n\t}\n\n\tfor _, h := range c.requestHandlers {\n\t\tif hErr := h.BeginRequest(ctx, req); hErr != nil {\n\t\t\treturn hErr\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (c *Client) emitEndRequest(ctx context.Context, resp *http.Response, err error) error {\n\tif len(c.requestHandlers) == 0 {\n\t\treturn nil\n\t}\n\n\tfor _, h := range c.requestHandlers {\n\t\tif hErr := h.EndRequest(ctx, resp, err); hErr != nil {\n\t\t\treturn hErr\n\t\t}\n\t}\n\n\treturn err\n}\n\n// RequestOption declares the type of option one can pass\n// to the Do methods(JSON, Form, ReadJSON...).\n// Request options run before request constructed.\ntype RequestOption = func(*http.Request) error\n\n// We always add the following request headers, unless they're removed by custom ones.\nvar defaultRequestOptions = []RequestOption{\n\tRequestHeader(false, acceptKey, contentTypeJSON),\n}\n\n// RequestHeader adds or sets (if overridePrev is true) a header to the request.\nfunc RequestHeader(overridePrev bool, key string, values ...string) RequestOption {\n\tkey = http.CanonicalHeaderKey(key)\n\n\treturn func(req *http.Request) error {\n\t\tif overridePrev { // upsert.\n\t\t\treq.Header[key] = values\n\t\t} else { // just insert.\n\t\t\treq.Header[key] = append(req.Header[key], values...)\n\t\t}\n\n\t\treturn nil\n\t}\n}\n\n// RequestAuthorization sets an Authorization request header.\n// Note that we could do the same with a Transport RoundDrip too.\nfunc RequestAuthorization(value string) RequestOption {\n\treturn RequestHeader(true, \"Authorization\", value)\n}\n\n// RequestAuthorizationBearer sets an Authorization: Bearer $token request header.\nfunc RequestAuthorizationBearer(accessToken string) RequestOption {\n\theaderValue := \"Bearer \" + accessToken\n\treturn RequestAuthorization(headerValue)\n}\n\n// RequestQuery adds a set of URL query parameters to the request.\nfunc RequestQuery(query url.Values) RequestOption {\n\treturn func(req *http.Request) error {\n\t\tq := req.URL.Query()\n\t\tfor k, v := range query {\n\t\t\tq[k] = v\n\t\t}\n\t\treq.URL.RawQuery = q.Encode()\n\n\t\treturn nil\n\t}\n}\n\n// RequestParam sets a single URL query parameter to the request.\nfunc RequestParam(key string, values ...string) RequestOption {\n\treturn RequestQuery(url.Values{\n\t\tkey: values,\n\t})\n}\n\n// Do sends an HTTP request and returns an HTTP response.\n//\n// The payload can be:\n// - io.Reader\n// - raw []byte\n// - JSON raw message\n// - string\n// - struct (JSON).\n//\n// If method is empty then it defaults to \"GET\".\n// The final variadic, optional input argument sets\n// the custom request options to use before the request.\n//\n// Any HTTP returned error will be of type APIError\n// or a timeout error if the given context was canceled.\nfunc (c *Client) Do(ctx context.Context, method, urlpath string, payload any, opts ...RequestOption) (*http.Response, error) {\n\tif ctx == nil {\n\t\tctx = context.Background()\n\t}\n\n\tif c.rateLimiter != nil {\n\t\tif err := c.rateLimiter.Wait(ctx); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\t// Method defaults to GET.\n\tif method == \"\" {\n\t\tmethod = http.MethodGet\n\t}\n\n\t// Find the payload, if any.\n\tvar body io.Reader\n\tif payload != nil {\n\t\tswitch v := payload.(type) {\n\t\tcase io.Reader:\n\t\t\tbody = v\n\t\tcase []byte:\n\t\t\tbody = bytes.NewBuffer(v)\n\t\tcase json.RawMessage:\n\t\t\tbody = bytes.NewBuffer(v)\n\t\tcase string:\n\t\t\tbody = strings.NewReader(v)\n\t\tcase url.Values:\n\t\t\tbody = strings.NewReader(v.Encode())\n\t\tdefault:\n\t\t\tw := new(bytes.Buffer)\n\t\t\t// We assume it's a struct, we wont make use of reflection to find out though.\n\t\t\terr := json.NewEncoder(w).Encode(v)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tbody = w\n\t\t}\n\t}\n\n\tif c.BaseURL != \"\" {\n\t\turlpath = c.BaseURL + urlpath // note that we don't do any special checks here, the caller is responsible.\n\t}\n\n\t// Initialize the request.\n\treq, err := http.NewRequestWithContext(ctx, method, urlpath, body)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// We separate the error for the default options for now.\n\tfor i, opt := range c.PersistentRequestOptions {\n\t\tif opt == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tif err = opt(req); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"client.Do: default request option[%d]: %w\", i, err)\n\t\t}\n\t}\n\n\t// Apply any custom request options (e.g. content type, accept headers, query...)\n\tfor _, opt := range opts {\n\t\tif opt == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tif err = opt(req); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tif err = c.emitBeginRequest(ctx, req); err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Caller is responsible for closing the response body.\n\t// Also note that the gzip compression is handled automatically nowadays.\n\tresp, respErr := c.HTTPClient.Do(req)\n\n\tif err = c.emitEndRequest(ctx, resp, respErr); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn resp, respErr\n}\n\n// DrainResponseBody drains response body and close it, allowing the transport to reuse TCP connections.\n// It's automatically called on Client.ReadXXX methods on the end.\nfunc (c *Client) DrainResponseBody(resp *http.Response) {\n\t_, _ = io.Copy(io.Discard, resp.Body)\n\tresp.Body.Close()\n}\n\nconst (\n\tacceptKey                 = \"Accept\"\n\tcontentTypeKey            = \"Content-Type\"\n\tcontentLengthKey          = \"Content-Length\"\n\tcontentTypePlainText      = \"plain/text\"\n\tcontentTypeJSON           = \"application/json\"\n\tcontentTypeFormURLEncoded = \"application/x-www-form-urlencoded\"\n)\n\n// JSON writes data as JSON to the server.\nfunc (c *Client) JSON(ctx context.Context, method, urlpath string, payload any, opts ...RequestOption) (*http.Response, error) {\n\topts = append(opts, RequestHeader(true, contentTypeKey, contentTypeJSON))\n\treturn c.Do(ctx, method, urlpath, payload, opts...)\n}\n\n// JSON writes form data to the server.\nfunc (c *Client) Form(ctx context.Context, method, urlpath string, formValues url.Values, opts ...RequestOption) (*http.Response, error) {\n\tpayload := formValues.Encode()\n\n\topts = append(opts,\n\t\tRequestHeader(true, contentTypeKey, contentTypeFormURLEncoded),\n\t\tRequestHeader(true, contentLengthKey, strconv.Itoa(len(payload))),\n\t)\n\n\treturn c.Do(ctx, method, urlpath, payload, opts...)\n}\n\n// Uploader holds the necessary information for upload requests.\n//\n// Look the Client.NewUploader method.\ntype Uploader struct {\n\tclient *Client\n\n\tbody   *bytes.Buffer\n\tWriter *multipart.Writer\n}\n\n// AddFileSource adds a form field to the uploader with the given key.\nfunc (u *Uploader) AddField(key, value string) error {\n\tf, err := u.Writer.CreateFormField(key)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t_, err = io.Copy(f, strings.NewReader(value))\n\treturn err\n}\n\n// AddFileSource adds a form file to the uploader with the given key.\nfunc (u *Uploader) AddFileSource(key, filename string, source io.Reader) error {\n\tf, err := u.Writer.CreateFormFile(key, filename)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t_, err = io.Copy(f, source)\n\treturn err\n}\n\n// AddFile adds a local form file to the uploader with the given key.\nfunc (u *Uploader) AddFile(key, filename string) error {\n\tsource, err := os.Open(filename)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer source.Close()\n\n\treturn u.AddFileSource(key, filename, source)\n}\n\n// Uploads sends local data to the server.\nfunc (u *Uploader) Upload(ctx context.Context, method, urlpath string, opts ...RequestOption) (*http.Response, error) {\n\terr := u.Writer.Close()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tpayload := bytes.NewReader(u.body.Bytes())\n\topts = append(opts, RequestHeader(true, contentTypeKey, u.Writer.FormDataContentType()))\n\n\treturn u.client.Do(ctx, method, urlpath, payload, opts...)\n}\n\n// NewUploader returns a structure which is responsible for sending\n// file and form data to the server.\nfunc (c *Client) NewUploader() *Uploader {\n\tbody := new(bytes.Buffer)\n\twriter := multipart.NewWriter(body)\n\n\treturn &Uploader{\n\t\tclient: c,\n\t\tbody:   body,\n\t\tWriter: writer,\n\t}\n}\n\n// ReadJSON binds \"dest\" to the response's body.\n// After this call, the response body reader is closed.\nfunc (c *Client) ReadJSON(ctx context.Context, dest any, method, urlpath string, payload any, opts ...RequestOption) error {\n\tif payload != nil {\n\t\topts = append(opts, RequestHeader(true, contentTypeKey, contentTypeJSON))\n\t}\n\n\tresp, err := c.Do(ctx, method, urlpath, payload, opts...)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer c.DrainResponseBody(resp)\n\n\tif resp.StatusCode >= http.StatusBadRequest {\n\t\treturn ExtractError(resp)\n\t}\n\n\t// DBUG\n\t// b, _ := io.ReadAll(resp.Body)\n\t// println(string(b))\n\t// return json.Unmarshal(b, &dest)\n\n\tif dest != nil {\n\t\treturn json.NewDecoder(resp.Body).Decode(&dest)\n\t}\n\n\treturn json.NewDecoder(resp.Body).Decode(&dest)\n}\n\n// ReadPlain like ReadJSON but it accepts a pointer to a string or byte slice or integer\n// and it reads the body as plain text.\nfunc (c *Client) ReadPlain(ctx context.Context, dest any, method, urlpath string, payload any, opts ...RequestOption) error {\n\tresp, err := c.Do(ctx, method, urlpath, payload, opts...)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer c.DrainResponseBody(resp)\n\n\tif resp.StatusCode >= http.StatusBadRequest {\n\t\treturn ExtractError(resp)\n\t}\n\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tswitch ptr := dest.(type) {\n\tcase *[]byte:\n\t\t*ptr = body\n\t\treturn nil\n\tcase *string:\n\t\t*ptr = string(body)\n\t\treturn nil\n\tcase *int:\n\t\t*ptr, err = strconv.Atoi(string(body))\n\t\treturn err\n\tdefault:\n\t\treturn fmt.Errorf(\"unsupported response body type: %T\", ptr)\n\t}\n}\n\n// GetPlainUnquote reads the response body as raw text and tries to unquote it,\n// useful when the remote server sends a single key as a value but due to backend mistake\n// it sends it as JSON (quoted) instead of plain text.\nfunc (c *Client) GetPlainUnquote(ctx context.Context, method, urlpath string, payload any, opts ...RequestOption) (string, error) {\n\tvar bodyStr string\n\tif err := c.ReadPlain(ctx, &bodyStr, method, urlpath, payload, opts...); err != nil {\n\t\treturn \"\", err\n\t}\n\n\ts, err := strconv.Unquote(bodyStr)\n\tif err == nil {\n\t\tbodyStr = s\n\t}\n\n\treturn bodyStr, nil\n}\n\n// WriteTo reads the response and then copies its data to the \"dest\" writer.\n// If the \"dest\" is a type of HTTP response writer then it writes the\n// content-type and content-length of the original request.\n//\n// Returns the amount of bytes written to \"dest\".\nfunc (c *Client) WriteTo(ctx context.Context, dest io.Writer, method, urlpath string, payload any, opts ...RequestOption) (int64, error) {\n\tif payload != nil {\n\t\topts = append(opts, RequestHeader(true, contentTypeKey, contentTypeJSON))\n\t}\n\n\tresp, err := c.Do(ctx, method, urlpath, payload, opts...)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tdefer resp.Body.Close()\n\n\tif w, ok := dest.(http.ResponseWriter); ok {\n\t\t// Copy the content type and content-length.\n\t\tw.Header().Set(\"Content-Type\", resp.Header.Get(\"Content-Type\"))\n\t\tif resp.ContentLength > 0 {\n\t\t\tw.Header().Set(\"Content-Length\", strconv.FormatInt(resp.ContentLength, 10))\n\t\t}\n\t}\n\n\treturn io.Copy(dest, resp.Body)\n}\n\n// BindResponse consumes the response's body and binds the result to the \"dest\" pointer,\n// closing the response's body is up to the caller.\n//\n// The \"dest\" will be binded based on the response's content type header.\n// Note that this is strict in order to catch bad actioners fast,\n// e.g. it wont try to read plain text if not specified on\n// the response headers and the dest is a *string.\nfunc BindResponse(resp *http.Response, dest any) (err error) {\n\tcontentType := trimHeader(resp.Header.Get(contentTypeKey))\n\tswitch contentType {\n\tcase contentTypeJSON: // the most common scenario on successful responses.\n\t\treturn json.NewDecoder(resp.Body).Decode(&dest)\n\tcase contentTypePlainText:\n\t\tb, err := io.ReadAll(resp.Body)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tswitch v := dest.(type) {\n\t\tcase *string:\n\t\t\t*v = string(b)\n\t\tcase *[]byte:\n\t\t\t*v = b\n\t\tdefault:\n\t\t\treturn fmt.Errorf(\"plain text response should accept a *string or a *[]byte\")\n\t\t}\n\n\tdefault:\n\t\tacceptContentType := trimHeader(resp.Request.Header.Get(acceptKey))\n\t\tmsg := \"\"\n\t\tif acceptContentType == contentType {\n\t\t\t// Here we make a special case, if the content type\n\t\t\t// was explicitly set by the request but we cannot handle it.\n\t\t\tmsg = fmt.Sprintf(\"current implementation can not handle the received (and accepted) mime type: %s\", contentType)\n\t\t} else {\n\t\t\tmsg = fmt.Sprintf(\"unexpected mime type received: %s\", contentType)\n\t\t}\n\t\terr = errors.New(msg)\n\t}\n\n\treturn\n}\n\nfunc trimHeader(v string) string {\n\tfor i, char := range v {\n\t\tif char == ' ' || char == ';' {\n\t\t\treturn v[:i]\n\t\t}\n\t}\n\treturn v\n}\n"
  },
  {
    "path": "x/client/client_test.go",
    "content": "package client\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"reflect\"\n\t\"testing\"\n)\n\nvar defaultCtx = context.Background()\n\ntype testValue struct {\n\tFirstname string `json:\"firstname\"`\n}\n\nfunc TestClientJSON(t *testing.T) {\n\texpectedJSON := testValue{Firstname: \"Makis\"}\n\n\tapp := http.NewServeMux()\n\tapp.HandleFunc(\"/send\", sendJSON(t, expectedJSON))\n\n\tvar irisGotJSON testValue\n\tapp.HandleFunc(\"/read\", readJSON(t, &irisGotJSON, &expectedJSON))\n\n\tsrv := httptest.NewServer(app)\n\tclient := New(BaseURL(srv.URL))\n\n\t// Test ReadJSON (read from server).\n\tvar got testValue\n\tif err := client.ReadJSON(defaultCtx, &got, http.MethodGet, \"/send\", nil); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t// Test JSON (send to server).\n\tresp, err := client.JSON(defaultCtx, http.MethodPost, \"/read\", expectedJSON)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tclient.DrainResponseBody(resp)\n}\n\nfunc sendJSON(t *testing.T, v any) http.HandlerFunc {\n\treturn func(w http.ResponseWriter, r *http.Request) {\n\t\tw.Header().Set(\"Content-Type\", \"application/json; charset=utf-8\")\n\t\tif err := json.NewEncoder(w).Encode(v); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t}\n}\n\nfunc readJSON(t *testing.T, ptr any, expected any) http.HandlerFunc {\n\treturn func(w http.ResponseWriter, r *http.Request) {\n\t\tif err := json.NewDecoder(r.Body).Decode(ptr); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tif !reflect.DeepEqual(ptr, expected) {\n\t\t\tt.Fatalf(\"expected to read json: %#+v but got: %#+v\", ptr, expected)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "x/client/error.go",
    "content": "package client\n\nimport (\n\t\"encoding/json\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\n// APIError errors that may return from the Client.\ntype APIError struct {\n\tResponse *http.Response\n\tBody     json.RawMessage // may be any []byte, response body is closed at this point.\n}\n\n// Error implements the standard error type.\nfunc (e APIError) Error() string {\n\tvar b strings.Builder\n\tif e.Response != nil {\n\t\tb.WriteString(e.Response.Request.URL.String())\n\t\tb.WriteByte(':')\n\t\tb.WriteByte(' ')\n\n\t\tb.WriteString(http.StatusText(e.Response.StatusCode))\n\t\tb.WriteByte(' ')\n\t\tb.WriteByte('(')\n\t\tb.WriteString(e.Response.Status)\n\t\tb.WriteByte(')')\n\n\t\tif len(e.Body) > 0 {\n\t\t\tb.WriteByte(':')\n\t\t\tb.WriteByte(' ')\n\t\t\tb.Write(e.Body)\n\t\t}\n\t}\n\n\treturn b.String()\n}\n\n// ExtractError returns the response wrapped inside an APIError.\nfunc ExtractError(resp *http.Response) APIError {\n\tbody, _ := io.ReadAll(resp.Body)\n\n\treturn APIError{\n\t\tResponse: resp,\n\t\tBody:     body,\n\t}\n}\n\n// GetError reports whether the given \"err\" is an APIError.\nfunc GetError(err error) (APIError, bool) {\n\tif err == nil {\n\t\treturn APIError{}, false\n\t}\n\n\tapiErr, ok := err.(APIError)\n\tif !ok {\n\t\treturn APIError{}, false\n\t}\n\n\treturn apiErr, true\n}\n\n// DecodeError binds a json error to the \"destPtr\".\nfunc DecodeError(err error, destPtr any) error {\n\tapiErr, ok := GetError(err)\n\tif !ok {\n\t\treturn err\n\t}\n\n\treturn json.Unmarshal(apiErr.Body, destPtr)\n}\n\n// GetErrorCode reads an error, which should be a type of APIError,\n// and returns its status code.\n// If the given \"err\" is nil or is not an APIError it returns 200,\n// acting as we have no error.\nfunc GetErrorCode(err error) int {\n\tapiErr, ok := GetError(err)\n\tif !ok {\n\t\treturn http.StatusOK\n\t}\n\n\treturn apiErr.Response.StatusCode\n}\n"
  },
  {
    "path": "x/client/handler_transport.go",
    "content": "package client\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n)\n\n// See \"Handler\" client option.\ntype handlerTransport struct {\n\thandler http.Handler\n}\n\n// RoundTrip completes the http.RoundTripper interface.\n// It can be used to test calls to a server's handler.\nfunc (t *handlerTransport) RoundTrip(req *http.Request) (*http.Response, error) {\n\treqCopy := *req\n\n\tif reqCopy.Proto == \"\" {\n\t\treqCopy.Proto = fmt.Sprintf(\"HTTP/%d.%d\", reqCopy.ProtoMajor, reqCopy.ProtoMinor)\n\t}\n\n\tif reqCopy.Body != nil {\n\t\tif reqCopy.ContentLength == -1 {\n\t\t\treqCopy.TransferEncoding = []string{\"chunked\"}\n\t\t}\n\t} else {\n\t\treqCopy.Body = io.NopCloser(bytes.NewReader(nil))\n\t}\n\n\tif reqCopy.RequestURI == \"\" {\n\t\treqCopy.RequestURI = reqCopy.URL.RequestURI()\n\t}\n\n\trecorder := httptest.NewRecorder()\n\n\tt.handler.ServeHTTP(recorder, &reqCopy)\n\n\tresp := http.Response{\n\t\tRequest:    &reqCopy,\n\t\tStatusCode: recorder.Code,\n\t\tStatus:     http.StatusText(recorder.Code),\n\t\tHeader:     recorder.Result().Header,\n\t}\n\n\tif recorder.Flushed {\n\t\tresp.TransferEncoding = []string{\"chunked\"}\n\t}\n\n\tif recorder.Body != nil {\n\t\tresp.Body = io.NopCloser(recorder.Body)\n\t}\n\n\treturn &resp, nil\n}\n"
  },
  {
    "path": "x/client/option.go",
    "content": "package client\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"net/http/httputil\"\n\t\"time\"\n\n\t\"github.com/kataras/golog\"\n\t\"golang.org/x/time/rate\"\n)\n\n// All the builtin client options should live here, for easy discovery.\n\ntype Option = func(*Client)\n\n// BaseURL registers the base URL of this client.\n// All of its methods will prepend this url.\nfunc BaseURL(uri string) Option {\n\treturn func(c *Client) {\n\t\tc.BaseURL = uri\n\t}\n}\n\n// Timeout specifies a time limit for requests made by this\n// Client. The timeout includes connection time, any\n// redirects, and reading the response body.\n// A Timeout of zero means no timeout.\n//\n// Defaults to 15 seconds.\nfunc Timeout(d time.Duration) Option {\n\treturn func(c *Client) {\n\t\tc.HTTPClient.Timeout = d\n\t}\n}\n\n// Handler specifies an iris.Application or any http.Handler\n// instance which can be tested using this Client.\n//\n// It registers a custom HTTP client transport\n// which allows \"fake calls\" to the \"h\" server. Use it for testing.\nfunc Handler(h http.Handler) Option {\n\treturn func(c *Client) {\n\t\tc.HTTPClient.Transport = new(handlerTransport)\n\t}\n}\n\n// PersistentRequestOptions adds one or more persistent request options\n// that all requests made by this Client will respect.\nfunc PersistentRequestOptions(reqOpts ...RequestOption) Option {\n\treturn func(c *Client) {\n\t\tc.PersistentRequestOptions = append(c.PersistentRequestOptions, reqOpts...)\n\t}\n}\n\n// RateLimit configures the rate limit for requests.\n//\n// Defaults to zero which disables rate limiting.\nfunc RateLimit(requestsPerSecond int) Option {\n\treturn func(c *Client) {\n\t\tc.rateLimiter = rate.NewLimiter(rate.Limit(requestsPerSecond), requestsPerSecond)\n\t}\n}\n\n// Debug enables the client's debug logger.\n// It fires right before request is created\n// and right after a response from the server is received.\n//\n// Example Output for request:\n//\n//\t[DBUG] 2022/03/01 21:54 Iris HTTP Client: POST / HTTP/1.1\n//\tHost: 127.0.0.1:50948\n//\tUser-Agent: Go-http-client/1.1\n//\tContent-Length: 22\n//\tAccept: application/json\n//\tContent-Type: application/json\n//\tAccept-Encoding: gzip\n//\n//\t{\"firstname\":\"Makis\"}\n//\n// Example Output for response:\n//\n//\t[DBUG] 2022/03/01 21:54 Iris HTTP Client: HTTP/1.1 200 OK\n//\tContent-Length: 27\n//\tContent-Type: application/json; charset=utf-8\n//\tDate: Tue, 01 Mar 2022 19:54:03 GMT\n//\n//\t{\n//\t    \"firstname\": \"Makis\"\n//\t}\nfunc Debug(c *Client) {\n\thandler := &debugRequestHandler{\n\t\tlogger: golog.Child(\"Iris HTTP Client: \").SetLevel(\"debug\"),\n\t}\n\tc.requestHandlers = append(c.requestHandlers, handler)\n}\n\ntype debugRequestHandler struct {\n\tlogger *golog.Logger\n}\n\nfunc (h *debugRequestHandler) BeginRequest(ctx context.Context, req *http.Request) error {\n\tdump, err := httputil.DumpRequestOut(req, true)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\th.logger.Debug(string(dump))\n\treturn nil\n}\n\nfunc (h *debugRequestHandler) EndRequest(ctx context.Context, resp *http.Response, err error) error {\n\tif err != nil {\n\t\th.logger.Debugf(\"%s: %s: ERR: %s\", resp.Request.Method, resp.Request.URL.String(), err.Error())\n\t} else {\n\t\tdump, err := httputil.DumpResponse(resp, true)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\th.logger.Debug(string(dump))\n\t}\n\n\treturn err\n}\n"
  },
  {
    "path": "x/client/request_handler.go",
    "content": "package client\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"sync\"\n)\n\n// RequestHandler can be set to each Client instance and it should be\n// responsible to handle the begin and end states of each request.\n// Its BeginRequest fires right before the client talks to the server\n// and its EndRequest fires right after the client receives a response from the server.\n// If one of them return a non-nil error then the execution of client will stop and return that error.\ntype RequestHandler interface {\n\tBeginRequest(context.Context, *http.Request) error\n\tEndRequest(context.Context, *http.Response, error) error\n}\n\nvar (\n\tdefaultRequestHandlers []RequestHandler\n\tmu                     sync.Mutex\n)\n\n// RegisterRequestHandler registers one or more request handlers\n// to be ran before and after of each request on all newly created Iris HTTP Clients.\n// Useful for Iris HTTP Client 3rd-party libraries\n// e.g. on init register a custom request-response lifecycle logging.\nfunc RegisterRequestHandler(reqHandlers ...RequestHandler) {\n\tmu.Lock()\n\tdefaultRequestHandlers = append(defaultRequestHandlers, reqHandlers...)\n\tmu.Unlock()\n}\n"
  },
  {
    "path": "x/errors/aliases.go",
    "content": "package errors\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n)\n\nvar (\n\t// Is is an alias of the standard errors.Is function.\n\tIs = errors.Is\n\t// As is an alias of the standard errors.As function.\n\tAs = errors.As\n\t// New is an alias of the standard errors.New function.\n\tNew = errors.New\n\t// Unwrap is an alias of the standard errors.Unwrap function.\n\tUnwrap = errors.Unwrap\n\t// Join is an alias of the standard errors.Join function.\n\tJoin = errors.Join\n)\n\nfunc sprintf(format string, args ...any) string {\n\tif len(args) > 0 {\n\t\treturn fmt.Sprintf(format, args...)\n\t}\n\n\treturn format\n}\n"
  },
  {
    "path": "x/errors/context_error_handler.go",
    "content": "package errors\n\nimport \"github.com/kataras/iris/v12/context\"\n\n// DefaultContextErrorHandler returns a context error handler\n// which calls the HandleError on any incoming error when\n// a rich rest response failed to be written to the client.\n// Register it on Application.SetContextErrorHandler method.\nvar DefaultContextErrorHandler context.ErrorHandler = new(jsonErrorHandler)\n\ntype jsonErrorHandler struct{}\n\n// HandleContextError completes the context.ErrorHandler interface. It's fired on\n// rich rest response failures.\nfunc (e *jsonErrorHandler) HandleContextError(ctx *context.Context, err error) {\n\tHandleError(ctx, err)\n}\n"
  },
  {
    "path": "x/errors/errors.go",
    "content": "package errors\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"log/slog\"\n\t\"net/http\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/x/client\"\n)\n\n// LogErrorFunc is an alias of a function type which accepts the Iris request context and an error\n// and it's fired whenever an error should be logged.\n//\n// See \"OnErrorLog\" variable to change the way an error is logged,\n// by default the error is logged using the Application's Logger's Error method.\ntype LogErrorFunc = func(ctx *context.Context, err error)\n\n// LogError can be modified to customize the way an error is logged to the server (most common: internal server errors, database errors et.c.).\n// Can be used to customize the error logging, e.g. using Sentry (cloud-based error console).\nvar LogError LogErrorFunc = func(ctx *context.Context, err error) {\n\tif ctx == nil {\n\t\tslog.Error(err.Error())\n\t\treturn\n\t}\n\n\tctx.Application().Logger().Error(err)\n}\n\n// SkipCanceled is a package-level setting which by default\n// skips the logging of a canceled response or operation.\n// See the \"Context.IsCanceled()\" method and \"iris.IsCanceled()\" function\n// that decide if the error is caused by a canceled operation.\n//\n// Change of this setting MUST be done on initialization of the program.\nvar SkipCanceled = true\n\ntype (\n\t// ErrorCodeName is a custom string type represents canonical error names.\n\t//\n\t// It contains functionality for safe and easy error populating.\n\t// See its \"Message\", \"Details\", \"Data\" and \"Log\" methods.\n\tErrorCodeName string\n\n\t// ErrorCode represents the JSON form ErrorCode of the Error.\n\tErrorCode struct {\n\t\tCanonicalName ErrorCodeName `json:\"canonical_name\" yaml:\"CanonicalName\"`\n\t\tStatus        int           `json:\"status\" yaml:\"Status\"`\n\t}\n)\n\n// A read-only map of valid http error codes.\nvar errorCodeMap = make(map[ErrorCodeName]ErrorCode)\n\n// Deprecated: Use Register instead.\nvar E = Register\n\n// Register registers a custom HTTP Error and returns its canonical name for future use.\n// The method \"New\" is reserved and was kept as it is for compatibility\n// with the standard errors package, therefore the \"Register\" name was chosen instead.\n// The key stroke \"e\" is near and accessible while typing the \"errors\" word\n// so developers may find it easy to use.\n//\n// See \"RegisterErrorCode\" and \"RegisterErrorCodeMap\" for alternatives.\n//\n// Example:\n//\n//\t\tvar (\n//\t   \t\tNotFound = errors.Register(\"NOT_FOUND\", http.StatusNotFound)\n//\t\t)\n//\t\t...\n//\t\tNotFound.Details(ctx, \"resource not found\", \"user with id: %q was not found\", userID)\n//\n// This method MUST be called on initialization, before HTTP server starts as\n// the internal map is not protected by mutex.\nfunc Register(httpErrorCanonicalName string, httpStatusCode int) ErrorCodeName {\n\tcanonicalName := ErrorCodeName(httpErrorCanonicalName)\n\tRegisterErrorCode(canonicalName, httpStatusCode)\n\treturn canonicalName\n}\n\n// RegisterErrorCode registers a custom HTTP Error.\n//\n// This method MUST be called on initialization, before HTTP server starts as\n// the internal map is not protected by mutex.\nfunc RegisterErrorCode(canonicalName ErrorCodeName, httpStatusCode int) {\n\terrorCodeMap[canonicalName] = ErrorCode{\n\t\tCanonicalName: canonicalName,\n\t\tStatus:        httpStatusCode,\n\t}\n}\n\n// RegisterErrorCodeMap registers one or more custom HTTP Errors.\n//\n// This method MUST be called on initialization, before HTTP server starts as\n// the internal map is not protected by mutex.\nfunc RegisterErrorCodeMap(errorMap map[ErrorCodeName]int) {\n\tif len(errorMap) == 0 {\n\t\treturn\n\t}\n\n\tfor canonicalName, httpStatusCode := range errorMap {\n\t\tRegisterErrorCode(canonicalName, httpStatusCode)\n\t}\n}\n\n// List of default error codes a server should follow and send back to the client.\nvar (\n\tCancelled          ErrorCodeName = Register(\"CANCELLED\", context.StatusTokenRequired)\n\tUnknown            ErrorCodeName = Register(\"UNKNOWN\", http.StatusInternalServerError)\n\tInvalidArgument    ErrorCodeName = Register(\"INVALID_ARGUMENT\", http.StatusBadRequest)\n\tDeadlineExceeded   ErrorCodeName = Register(\"DEADLINE_EXCEEDED\", http.StatusGatewayTimeout)\n\tNotFound           ErrorCodeName = Register(\"NOT_FOUND\", http.StatusNotFound)\n\tAlreadyExists      ErrorCodeName = Register(\"ALREADY_EXISTS\", http.StatusConflict)\n\tPermissionDenied   ErrorCodeName = Register(\"PERMISSION_DENIED\", http.StatusForbidden)\n\tUnauthenticated    ErrorCodeName = Register(\"UNAUTHENTICATED\", http.StatusUnauthorized)\n\tResourceExhausted  ErrorCodeName = Register(\"RESOURCE_EXHAUSTED\", http.StatusTooManyRequests)\n\tFailedPrecondition ErrorCodeName = Register(\"FAILED_PRECONDITION\", http.StatusBadRequest)\n\tAborted            ErrorCodeName = Register(\"ABORTED\", http.StatusConflict)\n\tOutOfRange         ErrorCodeName = Register(\"OUT_OF_RANGE\", http.StatusBadRequest)\n\tUnimplemented      ErrorCodeName = Register(\"UNIMPLEMENTED\", http.StatusNotImplemented)\n\tInternal           ErrorCodeName = Register(\"INTERNAL\", http.StatusInternalServerError)\n\tUnavailable        ErrorCodeName = Register(\"UNAVAILABLE\", http.StatusServiceUnavailable)\n\tDataLoss           ErrorCodeName = Register(\"DATA_LOSS\", http.StatusInternalServerError)\n)\n\n// errorFuncCodeMap is a read-only map of error code names and their error functions.\n// See HandleError package-level function.\nvar errorFuncCodeMap = make(map[ErrorCodeName][]func(error) error)\n\n// HandleError handles an error by sending it to the client\n// based on the registered error code names and their error functions.\n// Returns true if the error was handled, otherwise false.\n// If the given \"err\" is nil then it returns false.\n// If the given \"err\" is a type of validation error then it sends it to the client\n// using the \"Validation\" method.\n// If the given \"err\" is a type of client.APIError then it sends it to the client\n// using the \"HandleAPIError\" function.\n//\n// See ErrorCodeName.MapErrorFunc and MapErrors methods too.\nfunc HandleError(ctx *context.Context, err error) bool {\n\tif err == nil {\n\t\treturn false\n\t}\n\n\tif ctx.IsStopped() {\n\t\treturn false\n\t}\n\n\tfor errorCodeName, errorFuncs := range errorFuncCodeMap {\n\t\tfor _, errorFunc := range errorFuncs {\n\t\t\tif errToSend := errorFunc(err); errToSend != nil {\n\t\t\t\terrorCodeName.Err(ctx, errToSend)\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t}\n\n\t// Unwrap and collect the errors slice so the error result doesn't contain the ErrorCodeName type\n\t// and fire the error status code and title based on this error code name itself.\n\tvar asErrCode ErrorCodeName\n\tif As(err, &asErrCode) {\n\t\tif unwrapJoined, ok := err.(joinedErrors); ok {\n\t\t\terrs := unwrapJoined.Unwrap()\n\t\t\terrsToKeep := make([]error, 0, len(errs)-1)\n\t\t\tfor _, src := range errs {\n\t\t\t\tif _, isErrorCodeName := src.(ErrorCodeName); !isErrorCodeName {\n\t\t\t\t\terrsToKeep = append(errsToKeep, src)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif len(errsToKeep) > 0 {\n\t\t\t\terr = errors.Join(errsToKeep...)\n\t\t\t}\n\t\t}\n\n\t\tasErrCode.Err(ctx, err)\n\t\treturn true\n\t}\n\n\tif handleJSONError(ctx, err) {\n\t\treturn true\n\t}\n\n\tif vErr, ok := err.(ValidationError); ok {\n\t\tif vErr == nil {\n\t\t\treturn false // consider as not error for any case, this should never happen.\n\t\t}\n\n\t\tInvalidArgument.Validation(ctx, vErr)\n\t\treturn true\n\t}\n\n\tif vErrs, ok := err.(ValidationErrors); ok {\n\t\tif len(vErrs) == 0 {\n\t\t\treturn false // consider as not error for any case, this should never happen.\n\t\t}\n\n\t\tInvalidArgument.Validation(ctx, vErrs...)\n\t\treturn true\n\t}\n\n\tif apiErr, ok := client.GetError(err); ok {\n\t\thandleAPIError(ctx, apiErr)\n\t\treturn true\n\t}\n\n\tInternal.LogErr(ctx, err)\n\treturn true\n}\n\n// Error returns an empty string, it is only declared as a method of ErrorCodeName type in order\n// to be a compatible error to be joined within other errors:\n//\n//\terr = fmt.Errorf(\"%w%w\", errors.InvalidArgument, err) OR\n//\terr = errors.InvalidArgument.Wrap(err)\nfunc (e ErrorCodeName) Error() string {\n\treturn \"\"\n}\n\ntype joinedErrors interface{ Unwrap() []error }\n\n// Wrap wraps the given error with this ErrorCodeName.\n// It calls the standard errors.Join package-level function.\n// See HandleError function for more.\nfunc (e ErrorCodeName) Wrap(err error) error {\n\treturn errors.Join(e, err)\n}\n\n// MapErrorFunc registers a function which will validate the incoming error and\n// return the same error or overriden in order to be sent to the client, wrapped by this ErrorCodeName \"e\".\n//\n// This method MUST be called on initialization, before HTTP server starts as\n// the internal map is not protected by mutex.\n//\n// Example Code:\n//\n//\terrors.InvalidArgument.MapErrorFunc(func(err error) error {\n//\t\tstripeErr, ok := err.(*stripe.Error)\n//\t\tif !ok {\n//\t\t\treturn nil\n//\t\t}\n//\n//\t\treturn &errors.Error{\n//\t\t\tMessage: stripeErr.Msg,\n//\t\t\tDetails: stripeErr.DocURL,\n//\t\t}\n//\t})\nfunc (e ErrorCodeName) MapErrorFunc(fn func(error) error) {\n\terrorFuncCodeMap[e] = append(errorFuncCodeMap[e], fn)\n}\n\n// MapError registers one or more errors which will be sent to the client wrapped by this \"e\" ErrorCodeName\n// when the incoming error matches to at least one of the given \"targets\" one.\n//\n// This method MUST be called on initialization, before HTTP server starts as\n// the internal map is not protected by mutex.\nfunc (e ErrorCodeName) MapErrors(targets ...error) {\n\te.MapErrorFunc(func(err error) error {\n\t\tfor _, target := range targets {\n\t\t\tif Is(err, target) {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\treturn nil\n\t})\n}\n\n// Message sends an error with a simple message to the client.\nfunc (e ErrorCodeName) Message(ctx *context.Context, msg string) {\n\tfail(ctx, e, msg, \"\", nil, nil)\n}\n\n// Details sends an error with a message and details to the client.\nfunc (e ErrorCodeName) Details(ctx *context.Context, msg, details string) {\n\tfail(ctx, e, msg, msg, nil, nil)\n}\n\n// Data sends an error with a message and json data to the client.\nfunc (e ErrorCodeName) Data(ctx *context.Context, msg string, data any) {\n\tfail(ctx, e, msg, \"\", nil, data)\n}\n\n// DataWithDetails sends an error with a message, details and json data to the client.\nfunc (e ErrorCodeName) DataWithDetails(ctx *context.Context, msg, details string, data any) {\n\tfail(ctx, e, msg, details, nil, data)\n}\n\n// Validation sends an error which renders the invalid fields to the client.\nfunc (e ErrorCodeName) Validation(ctx *context.Context, validationErrors ...ValidationError) {\n\te.validation(ctx, validationErrors)\n}\n\nfunc (e ErrorCodeName) validation(ctx *context.Context, validationErrors any) {\n\tfail(ctx, e, \"validation failure\", \"fields were invalid\", validationErrors, nil)\n}\n\n// Err sends the error's text as a message to the client.\n// In exception, if the given \"err\" is a type of validation error\n// then the Validation method is called instead.\nfunc (e ErrorCodeName) Err(ctx *context.Context, err error) {\n\tif err == nil {\n\t\treturn\n\t}\n\n\tif vErr, ok := err.(ValidationError); ok {\n\t\tif vErr == nil {\n\t\t\treturn // consider as not error for any case, this should never happen.\n\t\t}\n\n\t\te.Validation(ctx, vErr)\n\t}\n\n\tif vErrs, ok := err.(ValidationErrors); ok {\n\t\tif len(vErrs) == 0 {\n\t\t\treturn // consider as not error for any case, this should never happen.\n\t\t}\n\n\t\te.Validation(ctx, vErrs...)\n\t}\n\n\t// If it's already an Error type then send it directly.\n\tif httpErr, ok := err.(*Error); ok {\n\t\tif errorCode, ok := errorCodeMap[e]; ok {\n\t\t\thttpErr.ErrorCode = errorCode\n\t\t\tctx.StopWithJSON(errorCode.Status, httpErr) // here we override the fail function and send the error as it is.\n\t\t\treturn\n\t\t}\n\t}\n\n\te.Message(ctx, err.Error())\n}\n\n// Log sends an error of \"format\" and optional \"args\" to the client and prints that\n// error using the \"LogError\" package-level function, which can be customized.\n//\n// See \"LogErr\" too.\nfunc (e ErrorCodeName) Log(ctx *context.Context, format string, args ...any) {\n\tif SkipCanceled {\n\t\tif ctx.IsCanceled() {\n\t\t\treturn\n\t\t}\n\n\t\tfor _, arg := range args {\n\t\t\tif err, ok := arg.(error); ok {\n\t\t\t\tif context.IsErrCanceled(err) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\terr := fmt.Errorf(format, args...)\n\te.LogErr(ctx, err)\n}\n\n// LogErr sends the given \"err\" as message to the client and prints that\n// error to using the \"LogError\" package-level function, which can be customized.\nfunc (e ErrorCodeName) LogErr(ctx *context.Context, err error) {\n\tif SkipCanceled && (ctx.IsCanceled() || context.IsErrCanceled(err)) {\n\t\treturn\n\t}\n\n\tLogError(ctx, err)\n\n\te.Message(ctx, \"server error\")\n}\n\n// HandleAPIError handles remote server errors.\n// Optionally, use it when you write your server's HTTP clients using the the /x/client package.\n// When the HTTP Client sends data to a remote server but that remote server\n// failed to accept the request as expected, then the error will be proxied\n// to this server's end-client.\n//\n// When the given \"err\" is not a type of client.APIError then\n// the error will be sent using the \"Internal.LogErr\" method which sends\n// HTTP internal server error to the end-client and\n// prints the \"err\" using the \"LogError\" package-level function.\nfunc HandleAPIError(ctx *context.Context, err error) {\n\t// Error expected and came from the external server,\n\t// save its body so we can forward it to the end-client.\n\tif apiErr, ok := client.GetError(err); ok {\n\t\thandleAPIError(ctx, apiErr)\n\t\treturn\n\t}\n\n\tInternal.LogErr(ctx, err)\n}\n\nfunc handleAPIError(ctx *context.Context, apiErr client.APIError) {\n\t// Error expected and came from the external server,\n\t// save its body so we can forward it to the end-client.\n\tstatusCode := apiErr.Response.StatusCode\n\tif statusCode >= 400 && statusCode < 500 {\n\t\tInvalidArgument.DataWithDetails(ctx, \"remote server error\", \"invalid client request\", apiErr.Body)\n\t} else {\n\t\tInternal.Data(ctx, \"remote server error\", apiErr.Body)\n\t}\n\n\t// Unavailable.DataWithDetails(ctx, \"remote server error\", \"unavailable\", apiErr.Body)\n}\n\nfunc handleJSONError(ctx *context.Context, err error) bool {\n\tif err == nil {\n\t\treturn false\n\t}\n\n\tmessageText := \"unable to parse request body\"\n\twireError := InvalidArgument\n\n\tif errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) {\n\t\twireError.Details(ctx, messageText, \"empty body\")\n\t\treturn true\n\t}\n\n\tvar syntaxErr *json.SyntaxError\n\tif errors.As(err, &syntaxErr) {\n\t\twireError.Details(ctx, messageText, fmt.Sprintf(\"json: syntax error at byte offset %d\", syntaxErr.Offset))\n\t\treturn true\n\t}\n\n\tvar unmarshalErr *json.UnmarshalTypeError\n\tif errors.As(err, &unmarshalErr) {\n\t\twireError.Details(ctx, messageText, unmarshalErr.Error())\n\t\treturn true\n\t}\n\n\tvar unsupportedValueErr *json.UnsupportedValueError\n\tif errors.As(err, &unsupportedValueErr) {\n\t\twireError.Details(ctx, messageText, err.Error())\n\t\treturn true\n\t}\n\n\tvar unsupportedTypeErr *json.UnsupportedTypeError\n\tif errors.As(err, &unsupportedTypeErr) {\n\t\twireError.Details(ctx, messageText, err.Error())\n\t\treturn true\n\t}\n\n\tvar invalidUnmarshalErr *json.InvalidUnmarshalError\n\tif errors.As(err, &invalidUnmarshalErr) {\n\t\twireError.Details(ctx, messageText, err.Error())\n\t\treturn true\n\t}\n\n\treturn false\n}\n\nvar (\n\t// ErrUnexpected is the HTTP error which sent to the client\n\t// when server fails to send an error, it's a fallback error.\n\t// The server fails to send an error on two cases:\n\t// 1. when the provided error code name is not registered (the error value is the ErrUnexpectedErrorCode)\n\t// 2. when the error contains data but cannot be encoded to json (the value of the error is the result error of json.Marshal).\n\tErrUnexpected = Register(\"UNEXPECTED_ERROR\", http.StatusInternalServerError)\n\t// ErrUnexpectedErrorCode is the error which logged\n\t// when the given error code name is not registered.\n\tErrUnexpectedErrorCode = New(\"unexpected error code name\")\n)\n\n// Error represents the JSON form of \"http wire errors\".\n//\n// Examples can be found at:\n//\n//\thttps://github.com/kataras/iris/tree/main/_examples/routing/http-wire-errors.\ntype Error struct {\n\tErrorCode  ErrorCode       `json:\"http_error_code\" yaml:\"HTTPErrorCode\"`\n\tMessage    string          `json:\"message,omitempty\" yaml:\"Message\"`\n\tDetails    string          `json:\"details,omitempty\" yaml:\"Details\"`\n\tValidation any             `json:\"validation,omitempty\" yaml:\"Validation,omitempty\"`\n\tData       json.RawMessage `json:\"data,omitempty\" yaml:\"Data,omitempty\"` // any other custom json data.\n}\n\n// Error method completes the error interface. It just returns the canonical name, status code, message and details.\nfunc (err *Error) Error() string {\n\tif err.Message == \"\" {\n\t\terr.Message = \"<empty>\"\n\t}\n\n\tif err.Details == \"\" {\n\t\terr.Details = \"<empty>\"\n\t}\n\n\tif err.ErrorCode.CanonicalName == \"\" {\n\t\terr.ErrorCode.CanonicalName = ErrUnexpected\n\t}\n\n\tif err.ErrorCode.Status <= 0 {\n\t\terr.ErrorCode.Status = http.StatusInternalServerError\n\t}\n\n\treturn sprintf(\"iris http wire error: canonical name: %s, http status code: %d, message: %s, details: %s\", err.ErrorCode.CanonicalName, err.ErrorCode.Status, err.Message, err.Details)\n}\n\nfunc fail(ctx *context.Context, codeName ErrorCodeName, msg, details string, validationErrors any, dataValue any) {\n\terrorCode, ok := errorCodeMap[codeName]\n\tif !ok {\n\t\t// This SHOULD NEVER happen, all ErrorCodeNames MUST be registered.\n\t\tLogError(ctx, ErrUnexpectedErrorCode)\n\t\tfail(ctx, ErrUnexpected, msg, details, validationErrors, dataValue)\n\t\treturn\n\t}\n\n\tvar data json.RawMessage\n\tif dataValue != nil {\n\t\tswitch v := dataValue.(type) {\n\t\tcase json.RawMessage:\n\t\t\tdata = v\n\t\tcase []byte:\n\t\t\tdata = v\n\t\tcase error:\n\t\t\tif msg == \"\" {\n\t\t\t\tmsg = v.Error()\n\t\t\t} else if details == \"\" {\n\t\t\t\tdetails = v.Error()\n\t\t\t} else {\n\t\t\t\tdata = json.RawMessage(v.Error())\n\t\t\t}\n\t\tdefault:\n\t\t\tb, err := json.Marshal(v)\n\t\t\tif err != nil {\n\t\t\t\tLogError(ctx, err)\n\t\t\t\tfail(ctx, ErrUnexpected, err.Error(), \"\", nil, nil)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tdata = b\n\n\t\t}\n\t}\n\n\terr := Error{\n\t\tErrorCode:  errorCode,\n\t\tMessage:    msg,\n\t\tDetails:    details,\n\t\tData:       data,\n\t\tValidation: validationErrors,\n\t}\n\n\t// ctx.SetErr(&err)\n\tctx.StopWithJSON(errorCode.Status, err)\n}\n"
  },
  {
    "path": "x/errors/handlers.go",
    "content": "package errors\n\nimport (\n\tstdContext \"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"net/http\"\n\t\"reflect\"\n\n\t\"github.com/kataras/iris/v12/context\"\n\trecovery \"github.com/kataras/iris/v12/middleware/recover\"\n\t\"github.com/kataras/iris/v12/x/pagination\"\n\n\t\"golang.org/x/exp/constraints\"\n)\n\nfunc init() {\n\tcontext.SetHandlerName(\"iris/x/errors.RecoveryHandler.*\", \"iris.errors.recover\")\n}\n\n// RecoveryHandler is a middleware which recovers from panics and sends an appropriate error response\n// to the logger and the client.\nfunc RecoveryHandler(ctx *context.Context) {\n\tdefer func() {\n\t\tif err := recovery.PanicRecoveryError(ctx, recover()); err != nil {\n\t\t\tInternal.LogErr(ctx, err)\n\t\t\tctx.StopExecution()\n\t\t}\n\t}()\n\n\tctx.Next()\n}\n\n// Handle handles a generic response and error from a service call and sends a JSON response to the client.\n// It returns a boolean value indicating whether the handle was successful or not.\n// If the error is not nil, it calls HandleError to send an appropriate error response to the client.\nfunc Handle(ctx *context.Context, resp any, err error) bool {\n\tif HandleError(ctx, err) {\n\t\treturn false\n\t}\n\n\tctx.StatusCode(http.StatusOK)\n\n\tif resp != nil {\n\t\tif jsonErr := ctx.JSON(resp); jsonErr != nil { // this returns the original error as it's, even if it's handled by the HandleJSONError function.\n\t\t\tif unsupportedValueErr, ok := jsonErr.(*json.UnsupportedValueError); ok {\n\t\t\t\tif unsupportedValueErr.Str == \"NaN\" {\n\t\t\t\t\tif nanErr := checkNaN(resp); nanErr != nil {\n\t\t\t\t\t\tnewErr := fmt.Errorf(\"unable to parse response body: json: unspported value: %w\", nanErr)\n\t\t\t\t\t\tLogError(ctx, newErr)\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn false\n\t\t}\n\t}\n\n\treturn true\n}\n\n// checkNaN checks if any exported field in the struct is NaN and returns an error if it is.\nfunc checkNaN(v any) error {\n\tval := reflect.ValueOf(v)\n\treturn checkNaNRecursive(val, val.Type().Name())\n}\n\nfunc checkNaNRecursive(val reflect.Value, path string) error {\n\tif val.Kind() == reflect.Ptr {\n\t\tif val.IsNil() {\n\t\t\treturn nil\n\t\t}\n\t\tval = val.Elem()\n\t}\n\n\tswitch val.Kind() {\n\tcase reflect.Struct:\n\t\tfor i := 0; i < val.NumField(); i++ {\n\t\t\tfield := val.Field(i)\n\t\t\tfieldType := val.Type().Field(i)\n\t\t\tfieldPath := fmt.Sprintf(\"%s.%s\", path, fieldType.Name)\n\n\t\t\tif err := checkNaNRecursive(field, fieldPath); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\tcase reflect.Slice:\n\t\tfor i := 0; i < val.Len(); i++ {\n\t\t\telem := val.Index(i)\n\t\t\telemPath := fmt.Sprintf(\"%s[%d]\", path, i)\n\n\t\t\tif err := checkNaNRecursive(elem, elemPath); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\tcase reflect.Float64:\n\t\tif math.IsNaN(val.Float()) {\n\t\t\treturn fmt.Errorf(\"NaN value found in field: %s\", path)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// IDPayload is a simple struct which describes a json id value.\ntype IDPayload[T string | int] struct {\n\tID T `json:\"id\"`\n}\n\n// HandleCreate handles a create operation and sends a JSON response with the created resource to the client.\n// It returns a boolean value indicating whether the handle was successful or not.\n//\n// If the \"respOrID\" response is not nil, it sets the status code to 201 (Created) and sends the response as a JSON payload,\n// however if the given \"respOrID\" is a string or an int, it sends the response as a JSON payload of {\"id\": resp}.\n// If the \"err\" error is not nil, it calls HandleError to send an appropriate error response to the client.\n// It sets the status code to 201 (Created) and sends any response as a JSON payload,\nfunc HandleCreate(ctx *context.Context, respOrID any, err error) bool {\n\tif HandleError(ctx, err) {\n\t\treturn false\n\t}\n\n\tctx.StatusCode(http.StatusCreated)\n\n\tif respOrID != nil {\n\t\tswitch responseValue := respOrID.(type) {\n\t\tcase string:\n\t\t\tif ctx.JSON(IDPayload[string]{ID: responseValue}) != nil {\n\t\t\t\treturn false\n\t\t\t}\n\t\tcase int:\n\t\t\tif ctx.JSON(IDPayload[int]{ID: responseValue}) != nil {\n\t\t\t\treturn false\n\t\t\t}\n\t\tdefault:\n\t\t\tif ctx.JSON(responseValue) != nil {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t}\n\n\treturn true\n}\n\n// HandleUpdate handles an update operation and sends a status code to the client.\n// It returns a boolean value indicating whether the handle was successful or not.\n// If the error is not nil, it calls HandleError to send an appropriate error response to the client.\n// If the updated value is true, it sets the status code to 204 (No Content).\n// If the updated value is false, it sets the status code to 304 (Not Modified).\nfunc HandleUpdate(ctx *context.Context, updated bool, err error) bool {\n\tif HandleError(ctx, err) {\n\t\treturn false\n\t}\n\n\tif updated {\n\t\tctx.StatusCode(http.StatusNoContent)\n\t} else {\n\t\tctx.StatusCode(http.StatusNotModified)\n\t}\n\n\treturn true\n}\n\n// HandleDelete handles a delete operation and sends a status code to the client.\n// If the error is not nil, it calls HandleError to send an appropriate error response to the client.\n// If the deleted value is true, it sets the status code to 204 (No Content).\n// If the deleted value is false, it sets the status code to 304 (Not Modified).\nfunc HandleDelete(ctx *context.Context, deleted bool, err error) bool {\n\treturn HandleUpdate(ctx, deleted, err)\n}\n\n// HandleDelete handles a delete operation and sends a status code to the client.\n// If the error is not nil, it calls HandleError to send an appropriate error response to the client.\n// It sets the status code to 204 (No Content).\nfunc HandleDeleteNoContent(ctx *context.Context, err error) bool {\n\treturn HandleUpdate(ctx, true, err)\n}\n\n// ResponseFunc is a function which takes a context and a generic type T and returns a generic type R and an error.\n// It is used to bind a request payload to a generic type T and call a service function with it.\ntype ResponseFunc[T, R any] interface {\n\tfunc(stdContext.Context, T) (R, error)\n}\n\n// ResponseOnlyErrorFunc is a function which takes a context and a generic type T and returns an error.\n// It is used to bind a request payload to a generic type T and call a service function with it.\n// It is used for functions which do not return a response.\ntype ResponseOnlyErrorFunc[T any] interface {\n\tfunc(stdContext.Context, T) error\n}\n\n// ContextRequestFunc is a function which takes a context and a generic type T and returns an error.\n// It is used to validate the context before calling a service function.\n//\n// See Validation package-level function.\ntype ContextRequestFunc[T any] func(*context.Context, T) error\n\nconst contextRequestHandlerFuncKey = \"iris.errors.ContextRequestHandler\"\n\n// Validation adds a context validator function to the context.\n// It returns a middleware which can be used to validate the context before calling a service function.\n// It panics if the given validators are empty or nil.\n//\n// Example:\n//\n// r.Post(\"/\", Validation(validateCreateRequest), createHandler(service))\n//\n//\tfunc validateCreateRequest(ctx iris.Context, r *CreateRequest) error {\n//\t\treturn validation.Join(\n//\t\t\tvalidation.String(\"fullname\", r.Fullname).NotEmpty().Fullname().Length(3, 50),\n//\t\t\tvalidation.Number(\"age\", r.Age).InRange(18, 130),\n//\t\t\tvalidation.Slice(\"hobbies\", r.Hobbies).Length(1, 10),\n//\t\t)\n//\t}\nfunc Validation[T any](validators ...ContextRequestFunc[T]) context.Handler {\n\tif len(validators) == 0 {\n\t\treturn nil\n\t}\n\n\tvalidator := joinContextRequestFuncs(validators)\n\n\treturn func(ctx *context.Context) {\n\t\tctx.Values().Set(contextRequestHandlerFuncKey, validator)\n\t\tctx.Next()\n\t}\n}\n\nfunc joinContextRequestFuncs[T any](requestHandlerFuncs []ContextRequestFunc[T]) ContextRequestFunc[T] {\n\tif len(requestHandlerFuncs) == 0 || requestHandlerFuncs[0] == nil {\n\t\tpanic(\"at least one context request handler function is required\")\n\t}\n\n\tif len(requestHandlerFuncs) == 1 {\n\t\treturn requestHandlerFuncs[0]\n\t}\n\n\treturn func(ctx *context.Context, req T) error {\n\t\tfor _, handler := range requestHandlerFuncs {\n\t\t\tif handler == nil {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif err := handler(ctx, req); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\treturn nil\n\t}\n}\n\n// RequestHandler is an interface which can be implemented by a request payload struct\n// in order to validate the context before calling a service function.\ntype RequestHandler interface {\n\tHandleRequest(*context.Context) error\n}\n\nfunc validateRequest[T any](ctx *context.Context, req T) bool {\n\tvar err error\n\n\t// Always run the request's validator first,\n\t// so dynamic validators can be customized per path and method.\n\tif contextRequestHandler, ok := any(&req).(RequestHandler); ok {\n\t\terr = contextRequestHandler.HandleRequest(ctx)\n\t}\n\n\tif err == nil {\n\t\tif v := ctx.Values().Get(contextRequestHandlerFuncKey); v != nil {\n\t\t\tif contextRequestHandlerFunc, ok := v.(ContextRequestFunc[T]); ok && contextRequestHandlerFunc != nil {\n\t\t\t\terr = contextRequestHandlerFunc(ctx, req)\n\t\t\t} else if contextRequestHandlerFunc, ok := v.(ContextRequestFunc[*T]); ok && contextRequestHandlerFunc != nil { // or a pointer of T.\n\t\t\t\terr = contextRequestHandlerFunc(ctx, &req)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn err == nil || !HandleError(ctx, err)\n}\n\n// ResponseHandler is an interface which can be implemented by a request payload struct\n// in order to handle a response before sending it to the client.\ntype ResponseHandler[R any] interface {\n\tHandleResponse(ctx *context.Context, response *R) error\n}\n\n// ContextResponseFunc is a function which takes a context, a generic type T and a generic type R and returns an error.\ntype ContextResponseFunc[T, R any] func(*context.Context, T, *R) error\n\nconst contextResponseHandlerFuncKey = \"iris.errors.ContextResponseHandler\"\n\nfunc validateResponse[T, R any](ctx *context.Context, req T, resp *R) bool {\n\tvar err error\n\n\tif contextResponseHandler, ok := any(&req).(ResponseHandler[R]); ok {\n\t\terr = contextResponseHandler.HandleResponse(ctx, resp)\n\t}\n\n\tif err == nil {\n\t\tif v := ctx.Values().Get(contextResponseHandlerFuncKey); v != nil {\n\t\t\tif contextResponseHandlerFunc, ok := v.(ContextResponseFunc[T, R]); ok && contextResponseHandlerFunc != nil {\n\t\t\t\terr = contextResponseHandlerFunc(ctx, req, resp)\n\t\t\t} else if contextResponseHandlerFunc, ok := v.(ContextResponseFunc[*T, R]); ok && contextResponseHandlerFunc != nil {\n\t\t\t\terr = contextResponseHandlerFunc(ctx, &req, resp)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn err == nil || !HandleError(ctx, err)\n}\n\n// Intercept adds a context response handler function to the context.\n// It returns a middleware which can be used to intercept the response before sending it to the client.\n//\n// Example Code:\n//\n//\tapp.Post(\"/\", errors.Intercept(func(ctx iris.Context, req *CreateRequest, resp *CreateResponse) error{ ... }), errors.CreateHandler(service.Create))\nfunc Intercept[T, R any](responseHandlers ...ContextResponseFunc[T, R]) context.Handler {\n\tif len(responseHandlers) == 0 {\n\t\treturn nil\n\t}\n\n\tresponseHandler := joinContextResponseFuncs(responseHandlers)\n\n\treturn func(ctx *context.Context) {\n\t\tctx.Values().Set(contextResponseHandlerFuncKey, responseHandler)\n\t\tctx.Next()\n\t}\n}\n\nfunc joinContextResponseFuncs[T, R any](responseHandlerFuncs []ContextResponseFunc[T, R]) ContextResponseFunc[T, R] {\n\tif len(responseHandlerFuncs) == 0 || responseHandlerFuncs[0] == nil {\n\t\tpanic(\"at least one context response handler function is required\")\n\t}\n\n\tif len(responseHandlerFuncs) == 1 {\n\t\treturn responseHandlerFuncs[0]\n\t}\n\n\treturn func(ctx *context.Context, req T, resp *R) error {\n\t\tfor _, handler := range responseHandlerFuncs {\n\t\t\tif handler == nil {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif err := handler(ctx, req, resp); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\treturn nil\n\t}\n}\n\nfunc bindResponse[T, R any, F ResponseFunc[T, R]](ctx *context.Context, fn F, fnInput ...T) (R, bool) {\n\tvar req T\n\tswitch len(fnInput) {\n\tcase 0:\n\t\tvar ok bool\n\t\treq, ok = ReadPayload[T](ctx)\n\t\tif !ok {\n\t\t\tvar resp R\n\t\t\treturn resp, false\n\t\t}\n\tcase 1:\n\t\treq = fnInput[0]\n\tdefault:\n\t\tpanic(\"invalid number of arguments\")\n\t}\n\n\tif !validateRequest(ctx, req) {\n\t\tvar resp R\n\t\treturn resp, false\n\t}\n\n\tresp, err := fn(ctx, req)\n\tif err == nil {\n\t\tif !validateResponse(ctx, req, &resp) {\n\t\t\treturn resp, false\n\t\t}\n\t}\n\n\treturn resp, !HandleError(ctx, err)\n}\n\n// OK handles a generic response and error from a service call and sends a JSON response to the client.\n// It returns a boolean value indicating whether the handle was successful or not.\n// If the error is not nil, it calls HandleError to send an appropriate error response to the client.\n// It sets the status code to 200 (OK) and sends any response as a JSON payload.\n//\n// Useful for Get/List/Fetch operations.\nfunc OK[T, R any, F ResponseFunc[T, R]](ctx *context.Context, fn F, fnInput ...T) bool { // or Fetch.\n\tresp, ok := bindResponse(ctx, fn, fnInput...)\n\tif !ok {\n\t\treturn false\n\t}\n\n\treturn Handle(ctx, resp, nil)\n}\n\n// HandlerInputFunc is a function which takes a context and returns a generic type T.\n// It is used to call a service function with a generic type T.\n// It is used for functions which do not bind a request payload.\n// It is used for XHandler functions.\n// Developers can design their own HandlerInputFunc functions and use them with the XHandler functions.\n// To make a value required, stop the context execution through the context.StopExecution function and fire an error\n// or just use one of the [InvalidArgument].X methods.\n//\n// See PathParam, Query and Value package-level helpers too.\ntype HandlerInputFunc[T any] interface {\n\tfunc(ctx *context.Context) T\n}\n\n// GetRequestInputs returns a slice of generic type T from a slice of HandlerInputFunc[T].\n// It is exported so end-developers can use it to get the inputs from custom HandlerInputFunc[T] functions.\nfunc GetRequestInputs[T any, I HandlerInputFunc[T]](ctx *context.Context, fnInputFunc []I) ([]T, bool) {\n\tinputs := make([]T, 0, len(fnInputFunc))\n\tfor _, callIn := range fnInputFunc {\n\t\tif callIn == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tinput := callIn(ctx)\n\t\tif ctx.IsStopped() { // if the input is required and it's not provided, then the context is stopped.\n\t\t\treturn nil, false\n\t\t}\n\t\tinputs = append(inputs, input)\n\t}\n\n\treturn inputs, true\n}\n\n// PathParam returns a HandlerInputFunc which reads a path parameter from the context and returns it as a generic type T.\n// It is used for XHandler functions.\nfunc PathParam[T any, I HandlerInputFunc[T]](paramName string) I {\n\treturn func(ctx *context.Context) T {\n\t\tparamValue := ctx.Params().Store.Get(paramName)\n\t\tif paramValue == nil {\n\t\t\tvar t T\n\t\t\treturn t\n\t\t}\n\n\t\treturn paramValue.(T)\n\t}\n}\n\n// Value returns a HandlerInputFunc which returns a generic type T.\n// It is used for XHandler functions.\nfunc Value[T any, I HandlerInputFunc[T]](value T) I {\n\treturn func(ctx *context.Context) T {\n\t\treturn value\n\t}\n}\n\n// Query returns a HandlerInputFunc which reads a URL query from the context and returns it as a generic type T.\n// It is used for XHandler functions.\nfunc Query[T any, I HandlerInputFunc[T]]() I {\n\treturn func(ctx *context.Context) T {\n\t\tvalue, ok := ReadQuery[T](ctx)\n\t\tif !ok {\n\t\t\tvar t T\n\t\t\treturn t\n\t\t}\n\n\t\treturn value\n\t}\n}\n\n// Handler handles a generic response and error from a service call and sends a JSON response to the client with status code of 200.\n//\n// See OK package-level function for more.\nfunc Handler[T, R any, F ResponseFunc[T, R], I HandlerInputFunc[T]](fn F, fnInput ...I) context.Handler {\n\treturn func(ctx *context.Context) {\n\t\tinputs, ok := GetRequestInputs(ctx, fnInput)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\n\t\tOK(ctx, fn, inputs...)\n\t}\n}\n\n// ListResponseFunc is a function which takes a context,\n// a pagination.ListOptions and a generic type T and returns a slice []R, total count of the items and an error.\n//\n// It's used on the List function.\ntype ListResponseFunc[T, R any, C constraints.Integer | constraints.Float] interface {\n\tfunc(stdContext.Context, pagination.ListOptions, T /* filter options */) ([]R, C, error)\n}\n\n// List handles a generic response and error from a service paginated call and sends a JSON response to the client.\n// It returns a boolean value indicating whether the handle was successful or not.\n// If the error is not nil, it calls HandleError to send an appropriate error response to the client.\n// It reads the pagination.ListOptions from the URL Query and any filter options of generic T from the request body.\n// It sets the status code to 200 (OK) and sends a *pagination.List[R] response as a JSON payload.\nfunc List[T, R any, C constraints.Integer | constraints.Float, F ListResponseFunc[T, R, C]](ctx *context.Context, fn F, fnInput ...T) bool {\n\tlistOpts, filter, ok := ReadPaginationOptions[T](ctx)\n\tif !ok {\n\t\treturn false\n\t}\n\n\tif !validateRequest(ctx, filter) {\n\t\treturn false\n\t}\n\n\titems, totalCount, err := fn(ctx, listOpts, filter)\n\tif err != nil {\n\t\tHandleError(ctx, err)\n\t\treturn false\n\t}\n\n\tresp := pagination.NewList(items, int64(totalCount), filter, listOpts)\n\tif !validateResponse(ctx, filter, resp) {\n\t\treturn false\n\t}\n\n\treturn Handle(ctx, resp, err)\n}\n\n// ListHandler handles a generic response and error from a service paginated call and sends a JSON response to the client.\n//\n// See List package-level function for more.\nfunc ListHandler[T, R any, C constraints.Integer | constraints.Float, F ListResponseFunc[T, R, C], I HandlerInputFunc[T]](fn F, fnInput ...I) context.Handler {\n\treturn func(ctx *context.Context) {\n\t\tinputs, ok := GetRequestInputs(ctx, fnInput)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\n\t\tList(ctx, fn, inputs...)\n\t}\n}\n\n// Create handles a create operation and sends a JSON response with the created resource to the client.\n// It returns a boolean value indicating whether the handle was successful or not.\n// If the error is not nil, it calls HandleError to send an appropriate error response to the client.\n// It sets the status code to 201 (Created) and sends any response as a JSON payload\n// note that if the response is a string, then it sends an {\"id\": resp} JSON payload).\n//\n// Useful for Insert operations.\nfunc Create[T, R any, F ResponseFunc[T, R]](ctx *context.Context, fn F, fnInput ...T) bool {\n\tresp, ok := bindResponse(ctx, fn, fnInput...)\n\tif !ok {\n\t\treturn false\n\t}\n\n\treturn HandleCreate(ctx, resp, nil)\n}\n\n// CreateHandler handles a create operation and sends a JSON response with the created resource to the client with status code of 201.\n//\n// See Create package-level function for more.\nfunc CreateHandler[T, R any, F ResponseFunc[T, R], I HandlerInputFunc[T]](fn F, fnInput ...I) context.Handler {\n\treturn func(ctx *context.Context) {\n\t\tinputs, ok := GetRequestInputs(ctx, fnInput)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\n\t\tCreate(ctx, fn, inputs...)\n\t}\n}\n\n// NoContent handles a generic response and error from a service call and sends a JSON response to the client.\n// It returns a boolean value indicating whether the handle was successful or not.\n// If the error is not nil, it calls HandleError to send an appropriate error response to the client.\n// It sets the status code to 204 (No Content).\n//\n// Useful for Update and Deletion operations.\nfunc NoContent[T any, F ResponseOnlyErrorFunc[T]](ctx *context.Context, fn F, fnInput ...T) bool {\n\ttoFn := func(c stdContext.Context, req T) (bool, error) {\n\t\treturn true, fn(ctx, req)\n\t}\n\n\treturn NoContentOrNotModified(ctx, toFn, fnInput...)\n}\n\n// NoContentHandler handles a generic response and error from a service call and sends a JSON response to the client with status code of 204.\n//\n// See NoContent package-level function for more.\nfunc NoContentHandler[T any, F ResponseOnlyErrorFunc[T], I HandlerInputFunc[T]](fn F, fnInput ...I) context.Handler {\n\treturn func(ctx *context.Context) {\n\t\tinputs, ok := GetRequestInputs(ctx, fnInput)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\n\t\tNoContent(ctx, fn, inputs...)\n\t}\n}\n\n// NoContent handles a generic response and error from a service call and sends a JSON response to the client.\n// It returns a boolean value indicating whether the handle was successful or not.\n// If the error is not nil, it calls HandleError to send an appropriate error response to the client.\n// If the response is true, it sets the status code to 204 (No Content).\n// If the response is false, it sets the status code to 304 (Not Modified).\n//\n// Useful for Update and Deletion operations.\nfunc NoContentOrNotModified[T any, F ResponseFunc[T, bool]](ctx *context.Context, fn F, fnInput ...T) bool {\n\tresp, ok := bindResponse(ctx, fn, fnInput...)\n\tif !ok {\n\t\treturn false\n\t}\n\n\treturn HandleUpdate(ctx, bool(resp), nil)\n}\n\n// NoContentOrNotModifiedHandler handles a generic response and error from a service call and sends a JSON response to the client with status code of 204 or 304.\n//\n// See NoContentOrNotModified package-level function for more.\nfunc NoContentOrNotModifiedHandler[T any, F ResponseFunc[T, bool], I HandlerInputFunc[T]](fn F, fnInput ...I) context.Handler {\n\treturn func(ctx *context.Context) {\n\t\tinputs, ok := GetRequestInputs(ctx, fnInput)\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\n\t\tNoContentOrNotModified(ctx, fn, inputs...)\n\t}\n}\n\n// ReadPayload reads a JSON payload from the context and returns it as a generic type T.\n// It also returns a boolean value indicating whether the read was successful or not.\n// If the read fails, it sends an appropriate error response to the client.\nfunc ReadPayload[T any](ctx *context.Context) (T, bool) {\n\tvar payload T\n\terr := ctx.ReadJSON(&payload)\n\tif err != nil {\n\t\tif errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) {\n\t\t\tInvalidArgument.Details(ctx, \"unable to parse body\", \"empty body\")\n\t\t\treturn payload, false\n\t\t}\n\n\t\tHandleError(ctx, err)\n\t\treturn payload, false\n\t}\n\n\treturn payload, true\n}\n\n// ReadQuery reads URL query values from the context and returns it as a generic type T.\n// It also returns a boolean value indicating whether the read was successful or not.\n// If the read fails, it sends an appropriate error response to the client.\nfunc ReadQuery[T any](ctx *context.Context) (T, bool) {\n\tvar payload T\n\terr := ctx.ReadQuery(&payload)\n\tif err != nil {\n\t\tHandleError(ctx, err)\n\t\treturn payload, false\n\t}\n\n\treturn payload, true\n}\n\n// ReadPaginationOptions reads the ListOptions from the URL Query and\n// any filter options of generic T from the request body.\nfunc ReadPaginationOptions[T /* T is FilterOptions */ any](ctx *context.Context) (pagination.ListOptions, T, bool) {\n\tlist, ok := ReadQuery[pagination.ListOptions](ctx)\n\tif !ok {\n\t\tvar t T\n\t\treturn list, t, false\n\t}\n\n\tfilter, ok := ReadPayload[T](ctx)\n\tif !ok {\n\t\tvar t T\n\t\treturn list, t, false\n\t}\n\n\treturn list, filter, true\n}\n"
  },
  {
    "path": "x/errors/path_parameter_type_error_handler.go",
    "content": "package errors\n\nimport (\n\t\"github.com/kataras/iris/v12/context\"\n\t\"github.com/kataras/iris/v12/macro/handler\"\n)\n\n// DefaultPathParameterTypeErrorHandler registers an error handler for macro path type parameter.\n// Register it with Application.Macros().SetErrorHandler(DefaultPathParameterTypeErrorHandler).\nvar DefaultPathParameterTypeErrorHandler handler.ParamErrorHandler = func(ctx *context.Context, paramIndex int, err error) {\n\tparam := ctx.Params().GetEntryAt(paramIndex) // key, value fields.\n\tInvalidArgument.DataWithDetails(ctx, \"invalid path parameter\", err.Error(), param)\n}\n"
  },
  {
    "path": "x/errors/validation/error.go",
    "content": "package validation\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kataras/iris/v12/x/errors\"\n)\n\n// FieldError describes a field validation error.\n// It completes the errors.ValidationError interface.\ntype FieldError[T any] struct {\n\tField  string `json:\"field\"`\n\tValue  T      `json:\"value\"`\n\tReason string `json:\"reason\"`\n}\n\n// Field returns a new validation error.\n//\n// Use its Func method to add validations over this field.\nfunc Field[T any](field string, value T) *FieldError[T] {\n\treturn &FieldError[T]{Field: field, Value: value}\n}\n\n// Error completes the standard error interface.\nfunc (e *FieldError[T]) Error() string {\n\treturn fmt.Sprintf(\"field %q got invalid value of %v: reason: %s\", e.Field, e.Value, e.Reason)\n}\n\n// GetField returns the field name.\nfunc (e *FieldError[T]) GetField() string {\n\treturn e.Field\n}\n\n// GetValue returns the value of the field.\nfunc (e *FieldError[T]) GetValue() any {\n\treturn e.Value\n}\n\n// GetReason returns the reason of the validation error.\nfunc (e *FieldError[T]) GetReason() string {\n\treturn e.Reason\n}\n\n// IsZero reports whether the error is nil or has an empty reason.\nfunc (e *FieldError[T]) IsZero() bool {\n\treturn e == nil || e.Reason == \"\"\n}\n\nfunc (e *FieldError[T]) joinReason(reason string) {\n\tif reason == \"\" {\n\t\treturn\n\t}\n\n\tif e.Reason == \"\" {\n\t\te.Reason = reason\n\t} else {\n\t\te.Reason += \", \" + reason\n\t}\n}\n\n// Func accepts a variadic number of functions which accept the value of the field\n// and return a string message if the value is invalid.\n// It joins the reasons into one.\nfunc (e *FieldError[T]) Func(fns ...func(value T) string) *FieldError[T] {\n\tfor _, fn := range fns {\n\t\te.joinReason(fn(e.Value))\n\t}\n\n\treturn e\n}\n\n// Join joins the given validation errors into one.\nfunc Join(errs ...errors.ValidationError) error { // note that here we return the standard error type instead of the errors.ValidationError in order to make the error nil instead of ValidationErrors(nil) on empty slice.\n\tif len(errs) == 0 {\n\t\treturn nil\n\t}\n\n\tjoinedErrs := make(errors.ValidationErrors, 0, len(errs))\n\tfor _, err := range errs {\n\t\tif err == nil || err.GetReason() == \"\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tjoinedErrs = append(joinedErrs, err)\n\t}\n\n\tif len(joinedErrs) == 0 {\n\t\treturn nil\n\t}\n\n\treturn joinedErrs\n}\n"
  },
  {
    "path": "x/errors/validation/number.go",
    "content": "package validation\n\nimport (\n\t\"fmt\"\n\n\t\"golang.org/x/exp/constraints\"\n)\n\n// NumberType is a type constraint that accepts any numeric type.\ntype NumberType interface {\n\tconstraints.Integer | constraints.Float\n}\n\n// NumberError describes a number field validation error.\ntype NumberError[T NumberType] struct{ *FieldError[T] }\n\n// Number returns a new number validation error.\nfunc Number[T NumberType](field string, value T) *NumberError[T] {\n\treturn &NumberError[T]{Field(field, value)}\n}\n\n// Positive adds an error if the value is not positive.\nfunc (e *NumberError[T]) Positive() *NumberError[T] {\n\te.Func(Positive)\n\treturn e\n}\n\n// Negative adds an error if the value is not negative.\nfunc (e *NumberError[T]) Negative() *NumberError[T] {\n\te.Func(Negative)\n\treturn e\n}\n\n// Zero reports whether the value is zero.\nfunc (e *NumberError[T]) Zero() *NumberError[T] {\n\te.Func(Zero)\n\treturn e\n}\n\n// NonZero adds an error if the value is zero.\nfunc (e *NumberError[T]) NonZero() *NumberError[T] {\n\te.Func(NonZero)\n\treturn e\n}\n\n// InRange adds an error if the value is not in the range.\nfunc (e *NumberError[T]) InRange(min, max T) *NumberError[T] {\n\te.Func(InRange(min, max))\n\treturn e\n}\n\n// Positive accepts any numeric type and\n// returns a message if the value is not positive.\nfunc Positive[T NumberType](n T) string {\n\tif n <= 0 {\n\t\treturn \"must be positive\"\n\t}\n\n\treturn \"\"\n}\n\n// Negative accepts any numeric type and returns a message if the value is not negative.\nfunc Negative[T NumberType](n T) string {\n\tif n >= 0 {\n\t\treturn \"must be negative\"\n\t}\n\n\treturn \"\"\n}\n\n// Zero accepts any numeric type and returns a message if the value is not zero.\nfunc Zero[T NumberType](n T) string {\n\tif n != 0 {\n\t\treturn \"must be zero\"\n\t}\n\n\treturn \"\"\n}\n\n// NonZero accepts any numeric type and returns a message if the value is not zero.\nfunc NonZero[T NumberType](n T) string {\n\tif n == 0 {\n\t\treturn \"must not be zero\"\n\t}\n\n\treturn \"\"\n}\n\n// InRange accepts any numeric type and returns a message if the value is not in the range.\nfunc InRange[T NumberType](min, max T) func(T) string {\n\treturn func(n T) string {\n\t\tif n < min || n > max {\n\t\t\treturn \"must be in range of \" + FormatRange(min, max)\n\t\t}\n\n\t\treturn \"\"\n\t}\n}\n\n// FormatRange returns a string representation of a range of values, such as \"[1, 10]\".\n// It uses a type constraint NumberValue, which means that the parameters must be numeric types\n// that support comparison and formatting operations.\nfunc FormatRange[T NumberType](min, max T) string {\n\treturn fmt.Sprintf(\"[%v, %v]\", min, max)\n}\n"
  },
  {
    "path": "x/errors/validation/slice.go",
    "content": "package validation\n\nimport \"fmt\"\n\n// SliceType is a type constraint that accepts any slice type.\ntype SliceType[T any] interface {\n\t~[]T\n}\n\n// SliceError describes a slice field validation error.\ntype SliceError[T any, V SliceType[T]] struct{ *FieldError[V] }\n\n// Slice returns a new slice validation error.\nfunc Slice[T any, V SliceType[T]](field string, value V) *SliceError[T, V] {\n\treturn &SliceError[T, V]{Field(field, value)}\n}\n\n// NotEmpty adds an error if the slice is empty.\nfunc (e *SliceError[T, V]) NotEmpty() *SliceError[T, V] {\n\te.Func(NotEmptySlice)\n\treturn e\n}\n\n// Length adds an error if the slice length is not in the given range.\nfunc (e *SliceError[T, V]) Length(min, max int) *SliceError[T, V] {\n\te.Func(SliceLength[T, V](min, max))\n\treturn e\n}\n\n// NotEmptySlice accepts any slice and returns a message if the value is empty.\nfunc NotEmptySlice[T any, V SliceType[T]](s V) string {\n\tif len(s) == 0 {\n\t\treturn \"must not be empty\"\n\t}\n\n\treturn \"\"\n}\n\n// SliceLength accepts any slice and returns a message if the length is not in the given range.\nfunc SliceLength[T any, V SliceType[T]](min, max int) func(s V) string {\n\treturn func(s V) string {\n\t\tn := len(s)\n\n\t\tif min == max {\n\t\t\tif n != min {\n\t\t\t\treturn fmt.Sprintf(\"must be %d elements\", min)\n\t\t\t}\n\n\t\t\treturn \"\"\n\t\t}\n\n\t\tif n < min || n > max {\n\t\t\treturn fmt.Sprintf(\"must be between %d and %d elements\", min, max)\n\t\t}\n\n\t\treturn \"\"\n\t}\n}\n"
  },
  {
    "path": "x/errors/validation/string.go",
    "content": "package validation\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\n// StringError describes a string field validation error.\ntype StringError struct{ *FieldError[string] }\n\n// String returns a new string validation error.\nfunc String(field string, value string) *StringError {\n\treturn &StringError{Field(field, value)}\n}\n\n// NotEmpty adds an error if the string is empty.\nfunc (e *StringError) NotEmpty() *StringError {\n\te.Func(NotEmpty)\n\treturn e\n}\n\n// Fullname adds an error if the string is not a full name.\nfunc (e *StringError) Fullname() *StringError {\n\te.Func(Fullname)\n\treturn e\n}\n\n// Length adds an error if the string length is not in the given range.\nfunc (e *StringError) Length(min, max int) *StringError {\n\te.Func(StringLength(min, max))\n\treturn e\n}\n\n// NotEmpty accepts any string and returns a message if the value is empty.\nfunc NotEmpty(s string) string {\n\tif s == \"\" {\n\t\treturn \"must not be empty\"\n\t}\n\n\treturn \"\"\n}\n\n// Fullname accepts any string and returns a message if the value is not a full name.\nfunc Fullname(s string) string {\n\tif len(strings.Split(s, \" \")) < 2 {\n\t\treturn \"must contain first and last name\"\n\t}\n\n\treturn \"\"\n}\n\n// StringLength accepts any string and returns a message if the length is not in the given range.\nfunc StringLength(min, max int) func(s string) string {\n\treturn func(s string) string {\n\t\tn := len(s)\n\n\t\tif min == max {\n\t\t\tif n != min {\n\t\t\t\treturn fmt.Sprintf(\"must be %d characters\", min)\n\t\t\t}\n\t\t}\n\n\t\tif n < min || n > max {\n\t\t\treturn fmt.Sprintf(\"must be between %d and %d characters\", min, max)\n\t\t}\n\n\t\treturn \"\"\n\t}\n}\n"
  },
  {
    "path": "x/errors/validation_error.go",
    "content": "package errors\n\nimport (\n\t\"strconv\"\n\t\"strings\"\n)\n\n// ValidationError is an interface which IF\n// it custom error types completes, then\n// it can by mapped to a validation error.\n//\n// A validation error(s) can be given by ErrorCodeName's Validation or Err methods.\ntype ValidationError interface {\n\terror\n\n\tGetField() string\n\tGetValue() any\n\tGetReason() string\n}\n\ntype ValidationErrors []ValidationError\n\nfunc (errs ValidationErrors) Error() string {\n\tvar buf strings.Builder\n\tfor i, err := range errs {\n\t\tbuf.WriteByte('[')\n\t\tbuf.WriteString(strconv.Itoa(i))\n\t\tbuf.WriteByte(']')\n\t\tbuf.WriteByte(' ')\n\n\t\tbuf.WriteString(err.Error())\n\n\t\tif i < len(errs)-1 {\n\t\t\tbuf.WriteByte(',')\n\t\t\tbuf.WriteByte(' ')\n\t\t}\n\t}\n\n\treturn buf.String()\n}\n"
  },
  {
    "path": "x/jsonx/day_time.go",
    "content": "package jsonx\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n)\n\nconst (\n\t// DayTimeLayout holds the time layout for the the format of \"hour:minute:second\", hour can be 15, meaning 3 PM.\n\tDayTimeLayout = \"15:04:05\"\n)\n\n// DayTime describes a time compatible with DayTimeLayout.\ntype DayTime time.Time\n\n// ParseDayTime reads from \"s\" and returns the DayTime time.\nfunc ParseDayTime(s string) (DayTime, error) {\n\tif s == \"\" || s == \"null\" {\n\t\treturn DayTime{}, nil\n\t}\n\n\ttt, err := time.Parse(DayTimeLayout, s)\n\tif err != nil {\n\t\treturn DayTime{}, err\n\t}\n\n\treturn DayTime(tt), nil\n}\n\n// UnmarshalJSON parses the \"b\" into DayTime time.\nfunc (t *DayTime) UnmarshalJSON(b []byte) error {\n\tif len(b) == 0 {\n\t\treturn nil\n\t}\n\n\ts := strings.Trim(string(b), `\"`)\n\ttt, err := ParseDayTime(s)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*t = tt\n\treturn nil\n}\n\n// MarshalJSON writes a quoted string in the DayTime time format.\nfunc (t DayTime) MarshalJSON() ([]byte, error) {\n\tif s := t.String(); s != \"\" {\n\t\ts = strconv.Quote(s)\n\t\treturn []byte(s), nil\n\t}\n\n\treturn nullLiteral, nil // Note: if the front-end wants an empty string instead I must change that.\n}\n\n// ToTime returns the unwrapped *t to time.Time.\nfunc (t *DayTime) ToTime() time.Time {\n\ttt := time.Time(*t)\n\treturn tt\n}\n\n// IsZero reports whether \"t\" is zero time.\nfunc (t DayTime) IsZero() bool {\n\treturn time.Time(t).IsZero()\n}\n\n// String returns the text representation of the \"t\" using the DayTime time layout.\nfunc (t DayTime) String() string {\n\ttt := t.ToTime()\n\tif tt.IsZero() {\n\t\treturn \"\"\n\t}\n\n\treturn tt.Format(DayTimeLayout)\n}\n\n// Scan completes the sql driver.Scanner interface.\nfunc (t *DayTime) Scan(src any) error {\n\tswitch v := src.(type) {\n\tcase time.Time: // type was set to timestamp\n\t\tif v.IsZero() {\n\t\t\treturn nil // don't set zero, ignore it.\n\t\t}\n\t\t*t = DayTime(v)\n\tcase string:\n\t\ttt, err := ParseDayTime(v)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t*t = tt\n\tcase nil:\n\t\t*t = DayTime(time.Time{})\n\tdefault:\n\t\treturn fmt.Errorf(\"DayTime: unknown type of: %T\", v)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "x/jsonx/day_time_test.go",
    "content": "package jsonx\n\nimport (\n\t\"encoding/json\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestDayTime(t *testing.T) {\n\ttests := []struct {\n\t\trawData string\n\t}{\n\t\t{\n\t\t\trawData: `{\"start\": \"8:33:00\", \"end\": \"15:00:42\", \"nothing\": null, \"empty\": \"\"}`,\n\t\t},\n\t\t{\n\t\t\trawData: `{\"start\": \"8:33:00\", \"end\": \"15:00:42\", \"nothing\": null, \"empty\": \"\"}`,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tv := struct {\n\t\t\tStart   DayTime `json:\"start\"`\n\t\t\tEnd     DayTime `json:\"end\"`\n\t\t\tNothing DayTime `json:\"nothing\"`\n\t\t\tEmpty   DayTime `json:\"empty\"`\n\t\t}{}\n\n\t\terr := json.Unmarshal([]byte(tt.rawData), &v)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tif !v.Nothing.IsZero() {\n\t\t\tt.Fatalf(\"expected 'nothing' to be zero but got: %v\", v.Nothing)\n\t\t}\n\n\t\tif !v.Empty.IsZero() {\n\t\t\tt.Fatalf(\"expected 'empty' to be zero but got: %v\", v.Empty)\n\t\t}\n\n\t\tloc := time.UTC\n\n\t\tif expected, got := time.Date(0, time.January, 1, 8, 33, 0, 0, loc), v.Start.ToTime(); expected != got {\n\t\t\tt.Fatalf(\"expected 'start' to be: %v but got: %v\", expected, got)\n\t\t}\n\n\t\tif expected, got := time.Date(0, time.January, 1, 15, 0, 42, 0, loc), v.End.ToTime(); expected != got {\n\t\t\tt.Fatalf(\"expected 'start' to be: %v but got: %v\", expected, got)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "x/jsonx/duration.go",
    "content": "package jsonx\n\nimport (\n\t\"database/sql/driver\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"math\"\n\t\"time\"\n)\n\n// Duration is a JSON representation of the standard Duration type, until Go version 2 supports it under the hoods.\ntype Duration time.Duration\n\nfunc (d Duration) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(time.Duration(d).String())\n}\n\nfunc (d *Duration) UnmarshalJSON(b []byte) error {\n\tvar v any\n\tif err := json.Unmarshal(b, &v); err != nil {\n\t\treturn err\n\t}\n\tswitch value := v.(type) {\n\tcase float64:\n\t\t*d = Duration(value)\n\t\treturn nil\n\tcase string:\n\t\tv, err := time.ParseDuration(value)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t*d = Duration(v)\n\t\treturn nil\n\tdefault:\n\t\treturn errors.New(\"invalid duration\")\n\t}\n}\n\nfunc (d Duration) ToDuration() time.Duration {\n\treturn time.Duration(d)\n}\n\nfunc (d Duration) Value() (driver.Value, error) {\n\treturn int64(d), nil\n}\n\n// Set sets the value of duration in nanoseconds.\nfunc (d *Duration) Set(v float64) {\n\tif math.IsNaN(v) {\n\t\treturn\n\t}\n\n\t*d = Duration(v)\n}\n"
  },
  {
    "path": "x/jsonx/exampler.go",
    "content": "package jsonx\n\n// Exampler is an interface used by testing to generate examples.\ntype Exampler interface {\n\tListExamples() any\n}\n"
  },
  {
    "path": "x/jsonx/iso8601.go",
    "content": "package jsonx\n\nimport (\n\t\"database/sql/driver\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\t// To load all system and embedded locations by name:\n\t// _ \"time/tzdata\" \t// OR build with: -tags timetzdata\n)\n\nvar fixedEastUTCLocations = make(map[int]*time.Location)\n\n// RegisterFixedLocation should be called on initialization of the program.\n// It registers a fixed location to the time parser.\n//\n// E.g. for input of 2023-02-04T09:48:14+03:00 to result a time string of 2023-02-04 09:48:14 +0300 EEST\n// you have to RegisterFixedLocation(\"EEST\", 10800) otherwise it will result to: 2023-02-04 09:48:14 +0300 +0300.\nfunc RegisterFixedLocation(name string, secondsFromUTC int) {\n\tloc := time.FixedZone(name, secondsFromUTC)\n\tfixedEastUTCLocations[secondsFromUTC] = loc\n}\n\nfunc init() {\n\tRegisterFixedLocation(\"EEST\", 3*60*60) // + 3 hours.\n\tRegisterFixedLocation(\"UTC\", 0)\n}\n\nconst (\n\t// ISO8601Layout holds the time layout for the the javascript iso time.\n\t// Read more at: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString.\n\tISO8601Layout = \"2006-01-02T15:04:05\"\n\t// ISO8601LayoutWithTimezone same as ISO8601Layout but with the timezone suffix.\n\tISO8601LayoutWithTimezone = \"2006-01-02T15:04:05Z\"\n\n\t// To match Go’s standard time layout that pads zeroes for microseconds, you can use the format 2006-01-02T15:04:05.000000Z07:00.\n\t// This layout uses 0s instead of 9s for the fractional second part, which ensures that the microseconds are\n\t// always represented with six digits, padding with leading zeroes if necessary.\n\t// ISO8601ZUTCOffsetLayoutWithMicroseconds = \"2006-01-02T15:04:05.000000Z07:00\"\n\t// ISO8601ZUTCOffsetLayoutWithMicroseconds ISO 8601 format, with full time and zone with UTC offset.\n\t// Example: 2022-08-10T03:21:00.000000+03:00, 2023-02-04T09:48:14+00:00, 2022-08-09T00:00:00.000000.\n\tISO8601ZUTCOffsetLayoutWithMicroseconds = \"2006-01-02T15:04:05.999999Z07:00\"\n\t// ISO8601ZUTCOffsetLayoutWithoutMicroseconds ISO 8601 format, with full time and zone with UTC offset without microsecond precision.\n\tISO8601ZUTCOffsetLayoutWithoutMicroseconds = \"2006-01-02T15:04:05Z07:00\"\n\t/*\n\t\tThe difference between the two time layouts \"2006-01-02T15:04:05Z07:00\" and \"2006-01-02T15:04:05-07:00\" is the presence of the Z character:\n\n\t\t\"2006-01-02T15:04:05Z07:00\": The Z indicates that the time is in UTC (Coordinated Universal Time) if there’s no offset specified.\n\t\tWhen an offset is present, as in +03:00, it indicates the time is in a timezone that is 3 hours ahead of UTC.\n\t\tThe Z is combined with the offset (07:00), which can be positive or negative to represent the timezone difference from UTC.\n\n\t\t\"2006-01-02T15:04:05-07:00\": This layout does not have the Z character and directly uses the offset (-07:00).\n\t\tIt’s more straightforward and indicates that the time is in a timezone that is 7 hours behind UTC.\n\t\tIn summary, the Z in the first layout serves as a placeholder for UTC and is used when the time might be in UTC or might have an offset.\n\t\tThe second layout is used when you’re directly specifying the offset without any reference to UTC.\n\t\tBoth layouts can parse the timestamp \"2024-04-08T04:47:10+03:00\" correctly, as they include placeholders for the timezone offset.\n\t*/\n\n\t// ISO8601UnconventionalOffsetLayout is the layout for the unconventional offset.\n\t// Custom offset layout, e.g., 2024-05-21T18:06:07.000000-04:01:19.\n\tISO8601UnconventionalOffsetLayout = \"2006-01-02T15:04:05.000000\"\n)\n\n// ISO8601 describes a time compatible with javascript time format.\ntype ISO8601 time.Time\n\nvar _ Exampler = (*ISO8601)(nil)\n\n// ParseISO8601 reads from \"s\" and returns the ISO8601 time.\n//\n// The function supports the following formats:\n//   - 2024-01-02T15:04:05.999999Z\n//   - 2024-01-02T15:04:05+07:00\n//   - 2024-04-08T08:05:04.830140+00:00\n//   - 2024-01-02T15:04:05Z\n//   - 2024-04-08T08:05:04.830140\n//   - 2024-01-02T15:04:05\n//   - 2024-05-21T18:06:07.000000-04:01:19\nfunc ParseISO8601(s string) (ISO8601, error) {\n\tif s == \"\" || s == \"null\" {\n\t\treturn ISO8601{}, nil\n\t}\n\n\tvar (\n\t\ttt  time.Time\n\t\terr error\n\t)\n\n\t/*\n\t\t// Check if the string contains a timezone offset after the 'T' character.\n\t\thasOffset := strings.Contains(s, \"Z\") || (strings.Index(s, \"+\") > strings.Index(s, \"T\")) || (strings.Index(s, \"-\") > strings.Index(s, \"T\"))\n\n\t\tswitch {\n\t\tcase strings.HasSuffix(s, \"Z\"):\n\t\t\ttt, err = time.Parse(ISO8601LayoutWithTimezone, s)\n\t\tcase hasOffset && strings.Contains(s, \".\"):\n\t\t\ttt, err = time.Parse(ISO8601ZUTCOffsetLayoutWithMicroseconds, s)\n\t\tcase hasOffset:\n\t\t\ttt, err = parseWithOffset(s)\n\t\tdefault:\n\t\t\ttt, err = time.Parse(ISO8601Layout, s)\n\t\t}\n\n\t\tif err != nil {\n\t\t\treturn ISO8601{}, fmt.Errorf(\"ISO8601: %w\", err)\n\t\t}\n\n\t\treturn ISO8601(tt), nil\n\t*/\n\n\tif idx := strings.LastIndexFunc(s, startUTCOffsetIndexFunc); idx > 18 { // should have some distance, with and without milliseconds\n\t\tlength := parseSignedOffset(s[idx:])\n\n\t\t// Check if the offset is unconventional, e.g., -04:01:19\n\t\tif offset := s[idx:]; isUnconventionalOffset(offset) {\n\t\t\tmainPart := s[:idx]\n\t\t\ttt, err = time.Parse(\"2006-01-02T15:04:05.000000\", mainPart)\n\t\t\tif err != nil {\n\t\t\t\treturn ISO8601{}, fmt.Errorf(\"ISO8601: %w\", err)\n\t\t\t}\n\n\t\t\tadjustedTime, parseErr := adjustForUnconventionalOffset(tt, offset)\n\t\t\tif parseErr != nil {\n\t\t\t\treturn ISO8601{}, fmt.Errorf(\"ISO8601: %w\", parseErr)\n\t\t\t}\n\t\t\treturn ISO8601(adjustedTime), nil\n\t\t}\n\n\t\tif idx+1 > idx+length || len(s) <= idx+length+1 {\n\t\t\treturn ISO8601{}, fmt.Errorf(\"ISO8601: invalid timezone format: %s\", s[idx:])\n\t\t}\n\n\t\toffsetText := s[idx+1 : idx+length]\n\t\toffset, parseErr := strconv.Atoi(offsetText)\n\t\tif parseErr != nil {\n\t\t\treturn ISO8601{}, fmt.Errorf(\"ISO8601: %w\", parseErr)\n\t\t}\n\n\t\t// E.g. offset of +0300 is returned as 10800 which is - (3 * 60 * 60).\n\t\tsecondsEastUTC := offset * 60 * 60\n\n\t\t// fmt.Printf(\"parsing %s with offset %s, secondsEastUTC: %d, using time layout: %s\\n\", s, offsetText, secondsEastUTC, ISO8601ZUTCOffsetLayoutWithMicroseconds)\n\t\tif loc, ok := fixedEastUTCLocations[secondsEastUTC]; ok { // Specific (fixed) zone.\n\t\t\tif strings.Contains(s, \".\") {\n\t\t\t\ttt, err = time.ParseInLocation(ISO8601ZUTCOffsetLayoutWithMicroseconds, s, loc)\n\t\t\t} else {\n\t\t\t\ttt, err = time.ParseInLocation(ISO8601ZUTCOffsetLayoutWithoutMicroseconds, s, loc)\n\t\t\t}\n\t\t} else { // Local or UTC.\n\t\t\tif strings.Contains(s, \".\") {\n\t\t\t\ttt, err = time.Parse(ISO8601ZUTCOffsetLayoutWithMicroseconds, s)\n\t\t\t} else {\n\t\t\t\ttt, err = time.Parse(ISO8601ZUTCOffsetLayoutWithoutMicroseconds, s)\n\t\t\t}\n\t\t}\n\t} else if s[len(s)-1] == 'Z' {\n\t\ttt, err = time.Parse(ISO8601LayoutWithTimezone, s)\n\t} else {\n\t\ttt, err = time.Parse(ISO8601Layout, s)\n\t}\n\n\tif err != nil {\n\t\treturn ISO8601{}, fmt.Errorf(\"ISO8601: %w\", err)\n\t}\n\treturn ISO8601(tt), nil\n}\n\nfunc parseWithOffset(s string) (time.Time, error) {\n\tidx := strings.LastIndexFunc(s, startUTCOffsetIndexFunc)\n\tif idx == -1 {\n\t\treturn time.Time{}, fmt.Errorf(\"ISO8601: missing timezone offset\")\n\t}\n\n\toffsetText := s[idx:]\n\tsecondsEastUTC, err := parseOffsetToSeconds(offsetText)\n\tif err != nil {\n\t\treturn time.Time{}, err\n\t}\n\n\tloc, ok := fixedEastUTCLocations[secondsEastUTC]\n\tif !ok {\n\t\tloc = time.FixedZone(\"\", secondsEastUTC)\n\t}\n\n\treturn time.ParseInLocation(ISO8601ZUTCOffsetLayoutWithoutMicroseconds, s, loc)\n}\n\nfunc parseOffsetToSeconds(offsetText string) (int, error) {\n\tif len(offsetText) < 6 {\n\t\treturn 0, fmt.Errorf(\"ISO8601: invalid timezone offset length: %s\", offsetText)\n\t}\n\n\tsign := offsetText[0]\n\tif sign != '-' && sign != '+' {\n\t\treturn 0, fmt.Errorf(\"ISO8601: invalid timezone offset sign: %c\", sign)\n\t}\n\n\thours, err := strconv.Atoi(offsetText[1:3])\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"ISO8601: %w\", err)\n\t}\n\n\tminutes, err := strconv.Atoi(offsetText[4:6])\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"ISO8601: %w\", err)\n\t}\n\n\tsecondsEastUTC := (hours*60 + minutes) * 60\n\tif sign == '-' {\n\t\tsecondsEastUTC = -secondsEastUTC\n\t}\n\n\treturn secondsEastUTC, nil\n}\n\nfunc isUnconventionalOffset(offset string) bool {\n\tparts := strings.Split(offset, \":\")\n\treturn len(parts) == 3\n}\n\nfunc adjustForUnconventionalOffset(t time.Time, offset string) (time.Time, error) {\n\tsign := 1\n\tif offset[0] == '-' {\n\t\tsign = -1\n\t}\n\toffset = offset[1:]\n\n\toffsetParts := strings.Split(offset, \":\")\n\tif len(offsetParts) != 3 {\n\t\treturn time.Time{}, fmt.Errorf(\"invalid offset format: %s\", offset)\n\t}\n\n\thours, err := strconv.Atoi(offsetParts[0])\n\tif err != nil {\n\t\treturn time.Time{}, fmt.Errorf(\"error parsing offset hours: %s: %w\", offset, err)\n\t}\n\n\tif hours > 24 {\n\t\treturn time.Time{}, fmt.Errorf(\"invalid offset hours: %d: %s\", hours, offset)\n\t}\n\n\tminutes, err := strconv.Atoi(offsetParts[1])\n\tif err != nil {\n\t\treturn time.Time{}, fmt.Errorf(\"error parsing offset minutes: %s: %w\", offset, err)\n\t}\n\tif minutes > 60 {\n\t\treturn time.Time{}, fmt.Errorf(\"invalid offset minutes: %d: %s\", minutes, offset)\n\t}\n\n\tseconds, err := strconv.Atoi(offsetParts[2])\n\tif err != nil {\n\t\treturn time.Time{}, fmt.Errorf(\"error parsing offset seconds: %s: %w\", offset, err)\n\t}\n\n\tif seconds > 60 {\n\t\treturn time.Time{}, fmt.Errorf(\"invalid offset seconds: %d: %s\", seconds, offset)\n\t}\n\n\ttotalOffset := time.Duration(sign) * (time.Duration(hours)*time.Hour + time.Duration(minutes)*time.Minute + time.Duration(seconds)*time.Second)\n\treturn t.Add(-totalOffset), nil\n}\n\n// UnmarshalJSON parses the \"b\" into ISO8601 time.\nfunc (t *ISO8601) UnmarshalJSON(b []byte) error {\n\tif len(b) == 0 {\n\t\treturn nil\n\t}\n\n\ts := strings.Trim(string(b), `\"`)\n\ttt, err := ParseISO8601(s)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*t = tt\n\treturn nil\n}\n\n// MarshalJSON writes a quoted string in the ISO8601 time format.\nfunc (t ISO8601) MarshalJSON() ([]byte, error) {\n\tif s := t.String(); s != \"\" {\n\t\ts = strconv.Quote(s)\n\t\treturn []byte(s), nil\n\t}\n\n\treturn nullLiteral, nil // Note: if the front-end wants an empty string instead I must change that.\n}\n\n// Examples returns a list of example values.\nfunc (t ISO8601) ListExamples() any {\n\treturn []string{\n\t\t\"2024-01-02T15:04:05.999999Z\",\n\t\t\"2024-01-02T15:04:05+07:00\",\n\t\t\"2024-04-08T08:05:04.830140+00:00\",\n\t\t\"2024-01-02T15:04:05Z\",\n\t\t\"2024-04-08T08:05:04.830140\",\n\t\t\"2024-01-02T15:04:05\",\n\t}\n}\n\n// ToTime returns the unwrapped *t to time.Time.\nfunc (t ISO8601) ToTime() time.Time {\n\treturn time.Time(t)\n}\n\n// IsZero reports whether \"t\" is zero time.\n// It completes the pg.Zeroer interface.\nfunc (t ISO8601) IsZero() bool {\n\treturn time.Time(t).IsZero()\n}\n\n// After reports whether the time instant \"t\" is after \"u\".\nfunc (t ISO8601) After(u ISO8601) bool {\n\treturn t.ToTime().After(u.ToTime())\n}\n\n// Equal reports whether the time instant \"t\" is equal to \"u\".\nfunc (t ISO8601) Equal(u ISO8601) bool {\n\treturn t.ToTime().Equal(u.ToTime())\n}\n\n// Add returns the time \"t\" with the duration added.\nfunc (t ISO8601) Add(d time.Duration) ISO8601 {\n\treturn ISO8601(t.ToTime().Add(d))\n}\n\n// Sub returns the duration between \"t\" and \"u\".\nfunc (t ISO8601) Sub(u ISO8601) time.Duration {\n\treturn t.ToTime().Sub(u.ToTime())\n}\n\n// String returns the text representation of the \"t\" using the ISO8601 time layout.\nfunc (t ISO8601) String() string {\n\ttt := t.ToTime()\n\tif tt.IsZero() {\n\t\treturn \"\"\n\t}\n\n\treturn tt.Format(ISO8601Layout)\n}\n\n// To24Hour returns the 24-hour representation of the time.\nfunc (t ISO8601) To24Hour() string {\n\ttt := t.ToTime()\n\tif tt.IsZero() {\n\t\treturn \"\"\n\t}\n\n\treturn tt.Format(\"15:04\")\n}\n\n// ToSimpleDate converts the current ISO8601 \"t\" to SimpleDate.\nfunc (t ISO8601) ToSimpleDate() SimpleDate {\n\treturn SimpleDateFromTime(t.ToTime())\n}\n\n// ToSimpleDateIn converts the current ISO8601 \"t\" to SimpleDate in specific location.\nfunc (t ISO8601) ToSimpleDateIn(in *time.Location) SimpleDate {\n\tif in == nil {\n\t\tin = time.UTC\n\t}\n\n\treturn SimpleDateFromTime(t.ToTime().In(in))\n}\n\n// ToDayTime converts the current ISO8601 \"t\" to DayTime.\nfunc (t ISO8601) ToDayTime() DayTime {\n\treturn DayTime(t.ToTime())\n}\n\n// Value returns the database value of time.Time.\nfunc (t ISO8601) Value() (driver.Value, error) {\n\treturn time.Time(t), nil\n}\n\n// Scan completes the sql driver.Scanner interface.\nfunc (t *ISO8601) Scan(src any) error {\n\tswitch v := src.(type) {\n\tcase time.Time: // type was set to timestamp\n\t\tif v.IsZero() {\n\t\t\treturn nil // don't set zero, ignore it.\n\t\t}\n\t\t*t = ISO8601(v)\n\tcase string:\n\t\ttt, err := ParseISO8601(v)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t*t = tt\n\tcase []byte:\n\t\treturn t.Scan(string(v))\n\tcase nil:\n\t\t*t = ISO8601(time.Time{})\n\tdefault:\n\t\treturn fmt.Errorf(\"ISO8601: unknown type of: %T\", v)\n\t}\n\n\treturn nil\n}\n\n// parseSignedOffset parses a signed timezone offset (e.g. \"+03\" or \"-04\").\n// The function checks for a signed number in the range -23 through +23 excluding zero.\n// Returns length of the found offset string or 0 otherwise.\n//\n// Language internal function.\nfunc parseSignedOffset(value string) int {\n\tsign := value[0]\n\tif sign != '-' && sign != '+' {\n\t\treturn 0\n\t}\n\tx, rem, err := leadingInt(value[1:])\n\n\t// fail if nothing consumed by leadingInt\n\tif err != nil || value[1:] == rem {\n\t\treturn 0\n\t}\n\tif x > 23 {\n\t\treturn 0\n\t}\n\treturn len(value) - len(rem)\n}\n\nvar errLeadingInt = errors.New(\"ISO8601: time: bad [0-9]*\") // never printed.\n\n// leadingInt consumes the leading [0-9]* from s.\n//\n// Language internal function.\nfunc leadingInt(s string) (x uint64, rem string, err error) {\n\ti := 0\n\tfor ; i < len(s); i++ {\n\t\tc := s[i]\n\t\tif c < '0' || c > '9' {\n\t\t\tbreak\n\t\t}\n\t\tif x > 1<<63/10 {\n\t\t\t// overflow\n\t\t\treturn 0, \"\", errLeadingInt\n\t\t}\n\t\tx = x*10 + uint64(c) - '0'\n\t\tif x > 1<<63 {\n\t\t\t// overflow\n\t\t\treturn 0, \"\", errLeadingInt\n\t\t}\n\t}\n\treturn x, s[i:], nil\n}\n\nfunc startUTCOffsetIndexFunc(char rune) bool {\n\treturn char == '+' || char == '-'\n}\n"
  },
  {
    "path": "x/jsonx/iso8601_test.go",
    "content": "package jsonx\n\nimport (\n\t\"encoding/json\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestParseISO8601(t *testing.T) {\n\ttests := []struct {\n\t\tname    string\n\t\tinput   string\n\t\twant    ISO8601\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname:  \"Timestamp with microseconds\",\n\t\t\tinput: \"2024-01-02T15:04:05.999999Z\",\n\t\t\twant:  ISO8601(time.Date(2024, 01, 02, 15, 04, 05, 999999*1000, time.UTC)),\n\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"Timestamp with timezone but no microseconds\",\n\t\t\tinput:   \"2024-01-02T15:04:05+07:00\",\n\t\t\twant:    ISO8601(time.Date(2024, 01, 02, 15, 04, 05, 0, time.FixedZone(\"\", 7*3600))),\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:  \"Timestamp with timezone of UTC with microseconds\",\n\t\t\tinput: \"2024-04-08T08:05:04.830140+00:00\",\n\t\t\t// time.Date function interprets the nanosecond parameter. The time.Date function expects the nanosecond parameter to be the entire nanosecond part of the time, not just the microsecond part.\n\t\t\t// When we pass 830140 as the nanosecond argument, Go interprets this as 830140 nanoseconds,\n\t\t\t// which is equivalent to 000830140 microseconds (padded with leading zeros to fill the nanosecond precision).\n\t\t\t// This is why we see 2024-04-08 08:05:04.00083014 +0000 UTC as the output.\n\t\t\t// To correctly represent 830140 microseconds, we need to convert it to nanoseconds by multiplying by 1000 (or set the value to 830140000).\n\t\t\twant:    ISO8601(time.Date(2024, 04, 8, 8, 05, 04, 830140*1000, time.UTC)),\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"Timestamp with timezone but no microseconds (2)\",\n\t\t\tinput:   \"2024-04-08T04:47:10+03:00\",\n\t\t\twant:    ISO8601(time.Date(2024, 04, 8, 4, 47, 10, 0, time.FixedZone(\"\", 3*3600))),\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"Timestamp with Zulu time\",\n\t\t\tinput:   \"2024-01-02T15:04:05Z\",\n\t\t\twant:    ISO8601(time.Date(2024, 01, 02, 15, 04, 05, 0, time.UTC)),\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"Timestamp with Zulu time with microseconds\",\n\t\t\tinput:   \"2024-04-08T08:05:04.830140\",\n\t\t\twant:    ISO8601(time.Date(2024, 04, 8, 8, 05, 04, 830140*1000, time.UTC)),\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"Basic ISO8601 layout\",\n\t\t\tinput:   \"2024-01-02T15:04:05\",\n\t\t\twant:    ISO8601(time.Date(2024, 01, 02, 15, 04, 05, 0, time.UTC)),\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"Invalid format\",\n\t\t\tinput:   \"2024-01-02\",\n\t\t\twant:    ISO8601{},\n\t\t\twantErr: true,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot, err := ParseISO8601(tt.input)\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"ParseISO8601() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif !tt.wantErr && !time.Time(got).Equal(time.Time(tt.want)) {\n\t\t\t\tt.Errorf(\"ParseISO8601() = %v (%s), want %v (%s)\", got, got.ToTime().String(), tt.want, tt.want.ToTime().String())\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestISO8601(t *testing.T) {\n\tdata := `{\"start\": \"2021-08-20T10:05:01\", \"end\": \"2021-12-01T17:05:06\", \"nothing\": null, \"empty\": \"\"}`\n\tv := struct {\n\t\tStart   ISO8601 `json:\"start\"`\n\t\tEnd     ISO8601 `json:\"end\"`\n\t\tNothing ISO8601 `json:\"nothing\"`\n\t\tEmpty   ISO8601 `json:\"empty\"`\n\t}{}\n\terr := json.Unmarshal([]byte(data), &v)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif !v.Nothing.IsZero() {\n\t\tt.Fatalf(\"expected 'nothing' to be zero but got: %v\", v.Nothing)\n\t}\n\n\tif !v.Empty.IsZero() {\n\t\tt.Fatalf(\"expected 'empty' to be zero but got: %v\", v.Empty)\n\t}\n\n\tloc := time.UTC\n\n\tif expected, got := time.Date(2021, time.August, 20, 10, 5, 1, 0, loc), v.Start.ToTime(); expected != got {\n\t\tt.Fatalf(\"expected 'start' to be: %v but got: %v\", expected, got)\n\t}\n\n\tif expected, got := time.Date(2021, time.December, 1, 17, 5, 6, 0, loc), v.End.ToTime(); expected != got {\n\t\tt.Fatalf(\"expected 'start' to be: %v but got: %v\", expected, got)\n\t}\n}\n\nfunc TestISO8601WithZoneUTCOffset(t *testing.T) {\n\tdata := `{\"start\": \"2022-08-10T03:21:00.000000+03:00\", \"end\": \"2022-08-10T09:49:00.000000+03:00\", \"nothing\": null, \"empty\": \"\"}`\n\tv := struct {\n\t\tStart   ISO8601 `json:\"start\"`\n\t\tEnd     ISO8601 `json:\"end\"`\n\t\tNothing ISO8601 `json:\"nothing\"`\n\t\tEmpty   ISO8601 `json:\"empty\"`\n\t}{}\n\terr := json.Unmarshal([]byte(data), &v)\n\tif err != nil {\n\t\tt.Fatalf(\"unmarshal: %v\", err)\n\t}\n\n\t// t.Logf(\"Start: %s, location: %s\\n\", v.Start.String(), v.Start.ToTime().Location().String())\n\n\tif !v.Nothing.IsZero() {\n\t\tt.Fatalf(\"expected 'nothing' to be zero but got: %v\", v.Nothing)\n\t}\n\n\tif !v.Empty.IsZero() {\n\t\tt.Fatalf(\"expected 'empty' to be zero but got: %v\", v.Empty)\n\t}\n\n\tloc := time.FixedZone(\"EEST\", 10800)\n\n\tif expected, got := time.Date(2022, time.August, 10, 3, 21, 0, 0, loc).String(), v.Start.ToTime().String(); expected != got {\n\t\tt.Fatalf(\"expected 'start' string to be: %v but got: %v\", expected, got)\n\t}\n\n\tif expected, got := time.Date(2022, time.August, 10, 9, 49, 0, 0, loc).String(), v.End.ToTime().String(); expected != got {\n\t\tt.Fatalf(\"expected 'end' string to be: %v but got: %v\", expected, got)\n\t}\n\n\tif expected, got := time.Date(2022, time.August, 10, 3, 21, 0, 0, loc), v.Start.ToTime().In(loc); expected != got {\n\t\tt.Fatalf(\"expected 'start' to be: %v but got: %v\", expected, got)\n\t}\n\n\tif expected, got := time.Date(2022, time.August, 10, 9, 49, 0, 0, loc), v.End.ToTime().In(loc); expected != got {\n\t\tt.Fatalf(\"expected 'end' to be: %v but got: %v\", expected, got)\n\t}\n}\n\nfunc TestISO8601WithZoneUTCOffsetWithoutMilliseconds(t *testing.T) {\n\tdata := `{\"start\": \"2023-02-04T09:48:14+00:00\", \"end\": \"2023-02-05T00:03:16+00:00\", \"nothing\": null, \"empty\": \"\"}`\n\tv := struct {\n\t\tStart   ISO8601 `json:\"start\"`\n\t\tEnd     ISO8601 `json:\"end\"`\n\t\tNothing ISO8601 `json:\"nothing\"`\n\t\tEmpty   ISO8601 `json:\"empty\"`\n\t}{}\n\terr := json.Unmarshal([]byte(data), &v)\n\tif err != nil {\n\t\tt.Fatalf(\"unmarshal: %v\", err)\n\t}\n\n\t// t.Logf(\"Start: %s, location: %s\\n\", v.Start.String(), v.Start.ToTime().Location().String())\n\n\tif !v.Nothing.IsZero() {\n\t\tt.Fatalf(\"expected 'nothing' to be zero but got: %v\", v.Nothing)\n\t}\n\n\tif !v.Empty.IsZero() {\n\t\tt.Fatalf(\"expected 'empty' to be zero but got: %v\", v.Empty)\n\t}\n\n\tloc := time.FixedZone(\"UTC\", 0)\n\n\tif expected, got := time.Date(2023, time.February, 04, 9, 48, 14, 0, loc).String(), v.Start.ToTime().String(); expected != got {\n\t\tt.Fatalf(\"expected 'start' string to be: %v but got: %v\", expected, got)\n\t}\n\n\tif expected, got := time.Date(2023, time.February, 05, 0, 3, 16, 0, loc).String(), v.End.ToTime().String(); expected != got {\n\t\tt.Fatalf(\"expected 'end' string to be: %v but got: %v\", expected, got)\n\t}\n\n\tif expected, got := time.Date(2023, time.February, 04, 9, 48, 14, 0, loc), v.Start.ToTime().In(loc); expected != got {\n\t\tt.Fatalf(\"expected 'start' to be: %v but got: %v\", expected, got)\n\t}\n\n\tif expected, got := time.Date(2023, time.February, 05, 0, 3, 16, 0, loc), v.End.ToTime().In(loc); expected != got {\n\t\tt.Fatalf(\"expected 'end' to be: %v but got: %v\", expected, got)\n\t}\n}\n\nfunc TestParseISO8601_StandardLayouts(t *testing.T) {\n\ttests := []struct {\n\t\tinput    string\n\t\texpected time.Time\n\t\thasError bool\n\t}{\n\t\t{\n\t\t\tinput:    \"2024-05-21T18:06:07Z\",\n\t\t\texpected: time.Date(2024, 5, 21, 18, 6, 7, 0, time.UTC),\n\t\t\thasError: false,\n\t\t},\n\t\t{\n\t\t\tinput:    \"2024-05-21T18:06:07-04:00\",\n\t\t\texpected: time.Date(2024, 5, 21, 22, 6, 7, 0, time.UTC),\n\t\t\thasError: false,\n\t\t},\n\t\t{\n\t\t\tinput:    \"2024-05-21T18:06:07\",\n\t\t\texpected: time.Date(2024, 5, 21, 18, 6, 7, 0, time.UTC), // no time local.\n\t\t\thasError: false,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.input, func(t *testing.T) {\n\t\t\tparsedTime, err := ParseISO8601(tt.input)\n\t\t\tif (err != nil) != tt.hasError {\n\t\t\t\tt.Errorf(\"ParseISO8601() error = %v, wantErr %v\", err, tt.hasError)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif !tt.hasError && !parsedTime.ToTime().Equal(tt.expected) {\n\t\t\t\tt.Errorf(\"ParseISO8601() = %v, want %v\", parsedTime, tt.expected)\n\t\t\t}\n\t\t})\n\t}\n}\nfunc TestParseISO8601_UnconventionalOffset(t *testing.T) {\n\ttests := []struct {\n\t\tinput    string\n\t\texpected time.Time\n\t\thasError bool\n\t}{\n\t\t{\n\t\t\tinput:    \"2024-05-21T18:06:07.000000-04:01:19\",\n\t\t\texpected: time.Date(2024, 5, 21, 22, 7, 26, 0, time.UTC),\n\t\t\thasError: false,\n\t\t},\n\t\t{\n\t\t\tinput:    \"2024-12-31T23:59:59.000000-00:00:59\",\n\t\t\texpected: time.Date(2025, 1, 1, 0, 0, 58, 0, time.UTC),\n\t\t\thasError: false,\n\t\t},\n\t\t{\n\t\t\tinput:    \"2024-05-21T18:06:07.000000+03:30:15\",\n\t\t\texpected: time.Date(2024, 5, 21, 14, 35, 52, 0, time.UTC),\n\t\t\thasError: false,\n\t\t},\n\t\t{\n\t\t\tinput:    \"2024-05-21T18:06:07.000000-24:00:00\",\n\t\t\texpected: time.Date(2024, 5, 22, 18, 6, 7, 0, time.UTC),\n\t\t\thasError: false,\n\t\t},\n\t\t{\n\t\t\tinput:    \"2024-05-21T18:06:07.000000-04:61:19\", // Invalid minute part in offset\n\t\t\texpected: time.Time{},\n\t\t\thasError: true,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.input, func(t *testing.T) {\n\t\t\tparsedTime, err := ParseISO8601(tt.input)\n\t\t\tif (err != nil) != tt.hasError {\n\t\t\t\tt.Errorf(\"ParseISO8601() error = %v, wantErr %v\", err, tt.hasError)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif !tt.hasError && !parsedTime.ToTime().Equal(tt.expected) {\n\t\t\t\tt.Errorf(\"ParseISO8601() = %v, want %v\", parsedTime, tt.expected)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestISO8601WithMicroseconds(t *testing.T) {\n\tdata := `{\"start\": \"2025-08-20T08:06:42.611522\", \"end\": \"2025-12-31T23:59:59.999999\", \"nothing\": null, \"empty\": \"\"}`\n\tv := struct {\n\t\tStart   ISO8601 `json:\"start\"`\n\t\tEnd     ISO8601 `json:\"end\"`\n\t\tNothing ISO8601 `json:\"nothing\"`\n\t\tEmpty   ISO8601 `json:\"empty\"`\n\t}{}\n\terr := json.Unmarshal([]byte(data), &v)\n\tif err != nil {\n\t\tt.Fatalf(\"unmarshal: %v\", err)\n\t}\n\n\tif !v.Nothing.IsZero() {\n\t\tt.Fatalf(\"expected 'nothing' to be zero but got: %v\", v.Nothing)\n\t}\n\n\tif !v.Empty.IsZero() {\n\t\tt.Fatalf(\"expected 'empty' to be zero but got: %v\", v.Empty)\n\t}\n\n\tloc := time.UTC\n\n\t// Test the specific timestamp requested: 2025-08-20T08:06:42.611522\n\t// 611522 microseconds = 611522000 nanoseconds\n\tif expected, got := time.Date(2025, time.August, 20, 8, 6, 42, 611522000, loc), v.Start.ToTime(); expected != got {\n\t\tt.Fatalf(\"expected 'start' to be: %v but got: %v\", expected, got)\n\t}\n\n\t// Test another timestamp with maximum microseconds\n\tif expected, got := time.Date(2025, time.December, 31, 23, 59, 59, 999999000, loc), v.End.ToTime(); expected != got {\n\t\tt.Fatalf(\"expected 'end' to be: %v but got: %v\", expected, got)\n\t}\n\n\t// Verify the string format excludes microseconds (as per ISO8601Layout)\n\tif expected, got := \"2025-08-20T08:06:42\", v.Start.String(); expected != got {\n\t\tt.Fatalf(\"expected 'start' string to be: %v but got: %v\", expected, got)\n\t}\n}\n"
  },
  {
    "path": "x/jsonx/jsonx.go",
    "content": "package jsonx\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n)\n\nvar (\n\tquoteLiteral    = '\"'\n\temptyQuoteBytes = []byte(`\"\"`)\n\tnullLiteral     = []byte(\"null\")\n\n\t// ErrInvalid is returned when the value is invalid.\n\tErrInvalid = errors.New(\"invalid\")\n)\n\nfunc isNull(b []byte) bool {\n\treturn len(b) == 0 || bytes.Equal(b, nullLiteral)\n}\n\nfunc trimQuotesFunc(r rune) bool {\n\treturn r == quoteLiteral\n}\n\nfunc trimQuotes(b []byte) []byte {\n\treturn bytes.TrimFunc(b, trimQuotesFunc)\n}\n"
  },
  {
    "path": "x/jsonx/kitchen_time.go",
    "content": "package jsonx\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n)\n\n// KitchenTimeLayout represents the \"3:04 PM\" Go time format, similar to time.Kitchen.\nconst KitchenTimeLayout = \"3:04 PM\"\n\n// KitchenTime holds a json \"3:04 PM\" time.\ntype KitchenTime time.Time\n\nvar ErrParseKitchenTimeColon = fmt.Errorf(\"parse kitchen time: missing ':' character\")\n\nfunc parseKitchenTime(s string) (KitchenTime, error) {\n\t// Remove any second,millisecond variable (probably given by postgres 00:00:00.000000).\n\t// required(00:00)remove(:00.000000)\n\n\tfirstIndex := strings.IndexByte(s, ':')\n\tif firstIndex == -1 {\n\t\treturn KitchenTime{}, ErrParseKitchenTimeColon\n\t} else {\n\t\tnextIndex := strings.LastIndexByte(s, ':')\n\t\tspaceIdx := strings.LastIndexByte(s, ' ')\n\t\tif nextIndex > firstIndex && spaceIdx > 0 {\n\t\t\ttmp := s[0:nextIndex]\n\t\t\ts = tmp + s[spaceIdx:]\n\t\t}\n\t}\n\n\ttt, err := time.Parse(KitchenTimeLayout, s)\n\tif err != nil {\n\t\treturn KitchenTime{}, err\n\t}\n\n\treturn KitchenTime(tt), nil\n}\n\n// ParseKitchenTime reads from \"s\" and returns the KitchenTime time.\nfunc ParseKitchenTime(s string) (KitchenTime, error) {\n\tif s == \"\" || s == \"null\" {\n\t\treturn KitchenTime{}, nil\n\t}\n\n\treturn parseKitchenTime(s)\n}\n\n// UnmarshalJSON binds the json \"data\" to \"t\" with the `KitchenTimeLayout`.\nfunc (t *KitchenTime) UnmarshalJSON(data []byte) error {\n\tif t == nil {\n\t\treturn fmt.Errorf(\"kitchen time: dest is nil\")\n\t}\n\n\tif isNull(data) {\n\t\treturn nil\n\t}\n\n\tdata = trimQuotes(data)\n\n\tif len(data) == 0 {\n\t\treturn nil\n\t}\n\n\ttt, err := parseKitchenTime(string(data))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*t = KitchenTime(tt)\n\treturn nil\n}\n\n// MarshalJSON returns the json representation of the \"t\".\nfunc (t KitchenTime) MarshalJSON() ([]byte, error) {\n\tif s := t.String(); s != \"\" {\n\t\ts = strconv.Quote(s)\n\t\treturn []byte(s), nil\n\t}\n\n\treturn emptyQuoteBytes, nil\n}\n\n// IsZero reports whether \"t\" is zero time.\n// It completes the pg.Zeroer interface.\nfunc (t KitchenTime) IsZero() bool {\n\treturn t.Value().IsZero()\n}\n\n// Value returns the standard time type.\nfunc (t KitchenTime) Value() time.Time {\n\treturn time.Time(t)\n}\n\n// String returns the text representation of the date\n// formatted based on the `KitchenTimeLayout`.\n// If date is zero it returns an empty string.\nfunc (t KitchenTime) String() string {\n\ttt := t.Value()\n\tif tt.IsZero() {\n\t\treturn \"\"\n\t}\n\n\treturn tt.Format(KitchenTimeLayout)\n}\n\n// Scan completes the pg and native sql driver.Scanner interface\n// reading functionality of a custom type.\nfunc (t *KitchenTime) Scan(src any) error {\n\tswitch v := src.(type) {\n\tcase time.Time: // type was set to timestamp.\n\t\tif v.IsZero() {\n\t\t\treturn nil // don't set zero, ignore it.\n\t\t}\n\t\t*t = KitchenTime(v)\n\tcase string: // type was set to time, input example: 10:00:00.000000\n\t\td, err := ParseTimeNotationDuration(v)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"kitchen time: convert to time notation first: %w\", err)\n\t\t}\n\n\t\ts := kitchenTimeStringFromDuration(d.ToDuration())\n\t\t*t, err = ParseKitchenTime(s)\n\t\treturn err\n\tcase int64: // timestamp with integer.\n\t\tu := time.Unix(v/1000, v%1000)\n\t\ts := kitchenTimeStringFromHourAndMinute(u.Hour(), u.Minute())\n\n\t\ttt, err := ParseKitchenTime(s)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t*t = tt\n\tcase nil:\n\t\t*t = KitchenTime(time.Time{})\n\tdefault:\n\t\treturn fmt.Errorf(\"KitchenTime: unknown type of: %T\", v)\n\t}\n\n\treturn nil\n}\n\nfunc kitchenTimeStringFromDuration(dt time.Duration) string {\n\thour := int(dt.Hours())\n\tminute := 0\n\tif totalMins := dt.Minutes(); totalMins > 0 {\n\t\tminute := int(totalMins / 60)\n\t\tif minute < 0 {\n\t\t\tminute = 0\n\t\t}\n\t}\n\n\treturn kitchenTimeStringFromHourAndMinute(hour, minute)\n}\n\nfunc kitchenTimeStringFromHourAndMinute(hour, minute int) string {\n\tampm := \"AM\"\n\tif hour/12 == 1 {\n\t\tampm = \"PM\"\n\t}\n\tth := hour % 12\n\thh := strconv.Itoa(th)\n\tif th < 10 {\n\t\thh = \"0\" + hh\n\t}\n\ttm := minute\n\tmm := strconv.Itoa(tm)\n\tif tm < 10 {\n\t\tmm = \"0\" + mm\n\t}\n\treturn hh + \":\" + mm + \" \" + ampm\n}\n"
  },
  {
    "path": "x/jsonx/kitchen_time_test.go",
    "content": "package jsonx\n\nimport (\n\t\"encoding/json\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestJSONKitchenTime(t *testing.T) {\n\ttests := []struct {\n\t\trawData string\n\t}{\n\t\t{\n\t\t\trawData: `{\"start\": \"8:33 AM\", \"end\": \"3:04 PM\", \"nothing\": null, \"empty\": \"\"}`,\n\t\t},\n\t\t{\n\t\t\trawData: `{\"start\": \"08:33 AM\", \"end\": \"03:04 PM\", \"nothing\": null, \"empty\": \"\"}`,\n\t\t},\n\t\t{\n\t\t\trawData: `{\"start\": \"08:33:00.000000 AM\", \"end\": \"03:04 PM\", \"nothing\": null, \"empty\": \"\"}`,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tv := struct {\n\t\t\tStart   KitchenTime `json:\"start\"`\n\t\t\tEnd     KitchenTime `json:\"end\"`\n\t\t\tNothing KitchenTime `json:\"nothing\"`\n\t\t\tEmpty   KitchenTime `json:\"empty\"`\n\t\t}{}\n\n\t\terr := json.Unmarshal([]byte(tt.rawData), &v)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tif !v.Nothing.IsZero() {\n\t\t\tt.Fatalf(\"expected 'nothing' to be zero but got: %v\", v.Nothing)\n\t\t}\n\n\t\tif !v.Empty.IsZero() {\n\t\t\tt.Fatalf(\"expected 'empty' to be zero but got: %v\", v.Empty)\n\t\t}\n\n\t\tloc := time.UTC\n\n\t\tif expected, got := time.Date(0, time.January, 1, 8, 33, 0, 0, loc), v.Start.Value(); expected != got {\n\t\t\tt.Fatalf(\"expected 'start' to be: %v but got: %v\", expected, got)\n\t\t}\n\n\t\tif expected, got := time.Date(0, time.January, 1, 15, 4, 0, 0, loc), v.End.Value(); expected != got {\n\t\t\tt.Fatalf(\"expected 'start' to be: %v but got: %v\", expected, got)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "x/jsonx/season.go",
    "content": "package jsonx\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n)\n\n// GetSeasonByDate returns the season based on the given date.\nfunc GetSeasonByDate(date SimpleDate) Season {\n\tmonth := date.ToTime().Month()\n\n\tswitch month {\n\tcase time.December, time.January, time.February:\n\t\treturn Winter\n\tcase time.March, time.April, time.May:\n\t\treturn Spring\n\tcase time.June, time.July, time.August:\n\t\treturn Summer\n\tcase time.September, time.October, time.November:\n\t\treturn Autumn\n\tdefault:\n\t\treturn 0 // Should never happen.\n\t}\n}\n\n// Season is a bitmask of seasons\n// Winter, Spring, Summer, Autumn.\n// It is used to represent the seasons of a year.\n// It is a bitmask so that multiple seasons can be set at once.\n// It has methods to check if a season is set, add a season,\n// remove a season, and get a string representation of the seasons.\ntype Season int\n\nconst (\n\t// December, January, February.\n\tWinter Season = 1 << iota // 1 << 0 = 1\n\t// March, April, May.\n\tSpring // 1 << 1 = 2\n\t// June, July, August.\n\tSummer // 1 << 2 = 4\n\t// September, October, November.\n\tAutumn // 1 << 3 = 8\n\n\t// AllSeasons is a bitmask of all seasons.\n\t// It's the number 15 because it's the sum of all seasons.\n\tAllSeasons = Winter | Spring | Summer | Autumn // 1 + 2 + 4 + 8 = 15\n)\n\n// IsValid checks if the season is valid.\nfunc (s Season) IsValid() bool {\n\treturn s&AllSeasons == s\n}\n\n// Is checks if the season is set in the bitmask.\nfunc (s Season) Is(season Season) bool {\n\treturn s&season != 0\n}\n\n// Add adds a season to the bitmask.\nfunc (s *Season) Add(season Season) {\n\t*s |= season\n}\n\n// Remove removes a season from the bitmask.\nfunc (s *Season) Remove(season Season) {\n\t*s &= ^season\n}\n\n// String returns the string representation of the season(s).\nfunc (s Season) String() string {\n\tvar seasons []string\n\n\tif s.Is(Winter) {\n\t\tseasons = append(seasons, \"Winter\")\n\t}\n\tif s.Is(Spring) {\n\t\tseasons = append(seasons, \"Spring\")\n\t}\n\tif s.Is(Summer) {\n\t\tseasons = append(seasons, \"Summer\")\n\t}\n\tif s.Is(Autumn) {\n\t\tseasons = append(seasons, \"Autumn\")\n\t}\n\n\tif len(seasons) == 0 {\n\t\treturn \"None\"\n\t}\n\n\treturn strings.Join(seasons, \", \")\n}\n\n// MarshalJSON marshals the season to JSON.\n// It marshals the season as a string.\nfunc (s *Season) UnmarshalJSON(data []byte) error {\n\tif isNull(data) {\n\t\treturn nil\n\t}\n\n\tdata = trimQuotes(data)\n\tif len(data) == 0 {\n\t\treturn nil\n\t}\n\n\tstr := string(data)\n\tconstantAsInt, err := strconv.Atoi(str)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif constantAsInt == 0 { // if 0 is passed, it means All seasons.\n\t\tconstantAsInt = int(AllSeasons)\n\t}\n\n\t*s = Season(constantAsInt)\n\tif !s.IsValid() {\n\t\treturn fmt.Errorf(\"%w: season: %s\", ErrInvalid, str)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "x/jsonx/simple_date.go",
    "content": "package jsonx\n\nimport (\n\t\"database/sql/driver\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/kataras/iris/v12/x/timex\"\n)\n\nconst (\n\t// SimpleDateLayout represents the \"year-month-day\" Go time format.\n\tSimpleDateLayout         = \"2006-01-02\"\n\tsimpleDateLayoutPostgres = \"2006-1-2\"\n)\n\n// SimpleDate holds a json \"year-month-day\" time.\ntype SimpleDate time.Time\n\nvar _ Exampler = (*SimpleDate)(nil)\n\n// SimpleDateFromTime accepts a \"t\" Time and returns\n// a SimpleDate. If format fails, it returns the zero value of time.Time.\nfunc SimpleDateFromTime(t time.Time) SimpleDate {\n\tdate, _ := ParseSimpleDate(t.Format(SimpleDateLayout))\n\treturn date\n}\n\n// ParseSimpleDate reads from \"s\" and returns the SimpleDate time.\n//\n// The function supports the following formats:\n//   - \"2024-01-01\"\n//   - \"2024-1-1\"\nfunc ParseSimpleDate(s string) (SimpleDate, error) {\n\tif s == \"\" || s == \"null\" {\n\t\treturn SimpleDate{}, nil\n\t}\n\n\tvar (\n\t\ttt  time.Time\n\t\terr error\n\t)\n\n\ttt, err = time.Parse(SimpleDateLayout, s)\n\tif err != nil {\n\t\t// After v5.0.0-alpha.3 of pgx this is coming as \"1993-1-1\" instead of the stored\n\t\t// value \"1993-01-01\".\n\t\tvar err2 error\n\t\ttt, err2 = time.Parse(simpleDateLayoutPostgres, s)\n\t\tif err2 != nil {\n\t\t\treturn SimpleDate{}, fmt.Errorf(\"%s: %w\", err2.Error(), err)\n\t\t}\n\t}\n\n\treturn SimpleDate(tt), nil\n}\n\n// UnmarshalJSON binds the json \"data\" to \"t\" with the `SimpleDateLayout`.\nfunc (t *SimpleDate) UnmarshalJSON(data []byte) error {\n\tif isNull(data) {\n\t\treturn nil\n\t}\n\n\tdata = trimQuotes(data)\n\tdataStr := string(data)\n\tif len(dataStr) == 0 {\n\t\treturn nil // do not allow empty \"\" on simple dates.\n\t}\n\n\ttt, err := time.Parse(SimpleDateLayout, dataStr)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*t = SimpleDate(tt)\n\treturn nil\n}\n\n// MarshalJSON returns the json representation of the \"t\".\nfunc (t SimpleDate) MarshalJSON() ([]byte, error) {\n\tif s := t.String(); s != \"\" {\n\t\ts = strconv.Quote(s)\n\t\treturn []byte(s), nil\n\t}\n\n\treturn emptyQuoteBytes, nil\n}\n\n// Examples returns a list of example values.\nfunc (t SimpleDate) ListExamples() any {\n\treturn []string{\n\t\t\"2024-01-01\",\n\t\t\"2024-1-1\",\n\t}\n}\n\n// IsZero reports whether \"t\" is zero time.\n// It completes the pg.Zeroer interface.\nfunc (t SimpleDate) IsZero() bool {\n\treturn t.ToTime().IsZero()\n}\n\n// Add returns the date of \"t\" plus \"d\".\nfunc (t SimpleDate) Add(d time.Duration) SimpleDate {\n\treturn SimpleDateFromTime(t.ToTime().Add(d))\n}\n\n// CountPastDays returns the count of days between \"t\" and \"pastDate\".\nfunc (t SimpleDate) CountPastDays(pastDate SimpleDate) int {\n\tt1, t2 := t.ToTime(), pastDate.ToTime()\n\treturn int(t1.Sub(t2).Hours() / 24)\n}\n\n// Equal reports back if \"t\" and \"d\" equals to the same date.\nfunc (t SimpleDate) Equal(d SimpleDate) bool {\n\treturn t.String() == d.String()\n}\n\n// After reports whether the time instant t is after u.\nfunc (t SimpleDate) After(d2 SimpleDate) bool {\n\tt1, t2 := t.ToTime(), d2.ToTime()\n\treturn t1.Truncate(24 * time.Hour).After(t2.Truncate(24 * time.Hour))\n}\n\n// Before reports whether the time instant t is before u.\nfunc (t SimpleDate) Before(d2 SimpleDate) bool {\n\tt1, t2 := t.ToTime(), d2.ToTime()\n\treturn t1.Truncate(24 * time.Hour).Before(t2.Truncate(24 * time.Hour))\n\t// OR: compare year and year's day.\n}\n\n// ToTime returns the standard time type.\nfunc (t SimpleDate) ToTime() time.Time {\n\treturn time.Time(t)\n}\n\n// Value completes the pg and native sql driver.Valuer interface.\nfunc (t SimpleDate) Value() (driver.Value, error) {\n\treturn t.String(), nil\n}\n\n// String returns the text representation of the date\n// formatted based on the `SimpleDateLayout`.\n// If date is zero it returns an empty string.\nfunc (t SimpleDate) String() string {\n\ttt := t.ToTime()\n\tif tt.IsZero() {\n\t\treturn \"\"\n\t}\n\n\treturn tt.Format(SimpleDateLayout)\n}\n\n// Scan completes the pg and native sql driver.Scanner interface\n// reading functionality of a custom type.\nfunc (t *SimpleDate) Scan(src any) error {\n\tswitch v := src.(type) {\n\tcase time.Time: // type was set to timestamp\n\t\tif v.IsZero() {\n\t\t\treturn nil // don't set zero, ignore it.\n\t\t}\n\t\t*t = SimpleDate(v)\n\tcase string:\n\t\ttt, err := ParseSimpleDate(v)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t*t = tt\n\tcase nil:\n\t\t*t = SimpleDate(time.Time{})\n\tdefault:\n\t\treturn fmt.Errorf(\"SimpleDate: unknown type of: %T\", v)\n\t}\n\n\treturn nil\n}\n\n// Slice of SimpleDate.\ntype SimpleDates []SimpleDate\n\n// First returns the first element of the date slice.\nfunc (t SimpleDates) First() SimpleDate {\n\tif len(t) == 0 {\n\t\treturn SimpleDate{}\n\t}\n\n\treturn t[0]\n}\n\n// Last returns the last element of the date slice.\nfunc (t SimpleDates) Last() SimpleDate {\n\tif len(t) == 0 {\n\t\treturn SimpleDate{}\n\t}\n\n\treturn t[len(t)-1]\n}\n\n// DateStrings returns a slice of string representation of the dates.\nfunc (t SimpleDates) DateStrings() []string {\n\tlist := make([]string, 0, len(t))\n\tfor _, d := range t {\n\t\tlist = append(list, d.String())\n\t}\n\n\treturn list\n}\n\n// Scan completes the pg and native sql driver.Scanner interface.\nfunc (t *SimpleDates) Scan(src any) error {\n\tif src == nil {\n\t\treturn nil\n\t}\n\n\tvar data []byte\n\tswitch v := src.(type) {\n\tcase []byte:\n\t\tdata = v\n\tcase string:\n\t\tdata = []byte(v)\n\tdefault:\n\t\treturn fmt.Errorf(\"simple dates: scan: invalid type of: %T\", src)\n\t}\n\n\terr := json.Unmarshal(data, t)\n\treturn err\n}\n\n// Value completes the pg and native sql driver.Valuer interface.\nfunc (t SimpleDates) Value() (driver.Value, error) {\n\tif len(t) == 0 {\n\t\treturn nil, nil\n\t}\n\n\tb, err := json.Marshal(t)\n\treturn b, err\n}\n\n// Contains reports if the \"date\" exists inside \"t\".\nfunc (t SimpleDates) Contains(date SimpleDate) bool {\n\tfor _, v := range t {\n\t\tif v.Equal(date) {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\n// DateRangeType is the type of the date range.\ntype DateRangeType string\n\nconst (\n\t// DayRange is the date range type of a day.\n\tDayRange DateRangeType = \"day\"\n\t// MonthRange is the date range type of a month.\n\tMonthRange DateRangeType = \"month\"\n\t// WeekRange is the date range type of a week.\n\tWeekRange DateRangeType = \"week\"\n\t// YearRange is the date range type of a year.\n\tYearRange DateRangeType = \"year\"\n)\n\n// GetSimpleDateRange returns a slice of SimpleDate between \"start\" and \"end\" pf \"date\"\n// based on given \"typ\" (WeekRange, MonthRange...).\n//\n// Example Code:\n// date := jsonx.SimpleDateFromTime(time.Now())\n// dates := jsonx.GetSimpleDateRange(date, jsonx.WeekRange, time.Monday, time.Sunday)\nfunc GetSimpleDateRange(date SimpleDate, typ DateRangeType, startWeekday, endWeekday time.Weekday) SimpleDates {\n\tvar dates []time.Time\n\tswitch typ {\n\tcase WeekRange:\n\t\tdates = timex.GetWeekdays(date.ToTime(), startWeekday, endWeekday)\n\tcase MonthRange:\n\t\tdates = timex.GetMonthDays(date.ToTime())\n\tdefault:\n\t\tpanic(fmt.Sprintf(\"invalid DateRangeType given: %s\", typ))\n\t}\n\n\tsimpleDates := make(SimpleDates, len(dates))\n\tfor i, t := range dates {\n\t\tsimpleDates[i] = SimpleDateFromTime(t)\n\t}\n\n\treturn simpleDates\n}\n"
  },
  {
    "path": "x/jsonx/simple_date_test.go",
    "content": "package jsonx\n\nimport (\n\t\"encoding/json\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestJSONSimpleDate(t *testing.T) {\n\tdata := `{\"start\": \"2021-08-20\", \"end\": \"2021-12-01\", \"nothing\": null, \"empty\": \"\"}`\n\tv := struct {\n\t\tStart   SimpleDate `json:\"start\"`\n\t\tEnd     SimpleDate `json:\"end\"`\n\t\tNothing SimpleDate `json:\"nothing\"`\n\t\tEmpty   SimpleDate `json:\"empty\"`\n\t}{}\n\terr := json.Unmarshal([]byte(data), &v)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif !v.Nothing.IsZero() {\n\t\tt.Fatalf(\"expected 'nothing' to be zero but got: %v\", v.Nothing)\n\t}\n\n\tif !v.Empty.IsZero() {\n\t\tt.Fatalf(\"expected 'empty' to be zero but got: %v\", v.Empty)\n\t}\n\n\tloc := time.UTC\n\n\tif expected, got := time.Date(2021, time.August, 20, 0, 0, 0, 0, loc), v.Start.ToTime(); expected != got {\n\t\tt.Fatalf(\"expected 'start' to be: %v but got: %v\", expected, got)\n\t}\n\n\tif expected, got := time.Date(2021, time.December, 1, 0, 0, 0, 0, loc), v.End.ToTime(); expected != got {\n\t\tt.Fatalf(\"expected 'start' to be: %v but got: %v\", expected, got)\n\t}\n}\n\nfunc TestSimpleDateAterBefore(t *testing.T) {\n\td1, d2 := SimpleDateFromTime(time.Now()), SimpleDateFromTime(time.Now().AddDate(0, 0, 1))\n\n\tif d1.After(d2) {\n\t\tt.Fatalf(\"[after] expected d1 to be before d2\")\n\t}\n\n\tif !d1.Before(d2) {\n\t\tt.Fatalf(\"[before] expected d1 to be before d2\")\n\t}\n\n\tif d2.Before(d1) {\n\t\tt.Fatalf(\"[after] expected d2 to be after d1\")\n\t}\n\n\tif !d2.After(d1) {\n\t\tt.Fatalf(\"[after] expected d2 to be after d1\")\n\t}\n}\n\nfunc TestCountPastDays(t *testing.T) {\n\ttests := []struct {\n\t\tAfterDate        SimpleDate\n\t\tBeforeDate       SimpleDate\n\t\tExpectedPastDays int\n\t}{\n\t\t{\n\t\t\tAfterDate:        mustParseSimpleDate(\"2023-01-01\"),\n\t\t\tBeforeDate:       mustParseSimpleDate(\"2022-12-31\"),\n\t\t\tExpectedPastDays: 1,\n\t\t},\n\t\t{\n\t\t\tAfterDate:        mustParseSimpleDate(\"2023-01-01\"),\n\t\t\tBeforeDate:       mustParseSimpleDate(\"2022-01-01\"),\n\t\t\tExpectedPastDays: 365,\n\t\t},\n\t}\n\n\tfor i, tt := range tests {\n\t\tif expected, got := tt.ExpectedPastDays, tt.AfterDate.CountPastDays(tt.BeforeDate); expected != got {\n\t\t\tt.Fatalf(\"[%d] expected past days count: %d but got: %d\", i, expected, got)\n\t\t}\n\t}\n}\n\nfunc mustParseSimpleDate(s string) SimpleDate {\n\td, err := ParseSimpleDate(s)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treturn d\n}\n"
  },
  {
    "path": "x/jsonx/time_notation.go",
    "content": "package jsonx\n\nimport (\n\t\"bytes\"\n\t\"database/sql/driver\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"math\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n)\n\n// TimeNotationDuration is a JSON representation of the standard Duration type in 00:00:00 (hour, minute seconds).\ntype TimeNotationDuration time.Duration\n\nfunc fmtDuration(d time.Duration) string {\n\td = d.Round(time.Minute)\n\th := d / time.Hour\n\td -= h * time.Hour\n\tm := d / time.Minute\n\treturn fmt.Sprintf(\"%02d:%02d:00\", h, m) // Manos doesn't care about the seconds.\n}\n\n// ParseTimeNotationDuration parses a string to a time notation duration (00:00:00) hours:minutes:seconds.\nfunc ParseTimeNotationDuration(s string) (TimeNotationDuration, error) {\n\tentries := strings.SplitN(s, \":\", 3)\n\tif len(entries) < 3 {\n\t\treturn TimeNotationDuration(0), fmt.Errorf(\"invalid duration format: expected hours:minutes:seconds (e.g. 01:05:00) but got: %s\", s)\n\t}\n\n\thours, err := strconv.Atoi(entries[0])\n\tif err != nil {\n\t\treturn TimeNotationDuration(0), err\n\t}\n\tminutes, err := strconv.Atoi(entries[1])\n\tif err != nil {\n\t\treturn TimeNotationDuration(0), err\n\t}\n\n\t// remove any .0000\n\tsecondsStr := strings.TrimSuffix(entries[2], \".000000\")\n\tseconds, err := strconv.Atoi(secondsStr)\n\tif err != nil {\n\t\treturn TimeNotationDuration(0), err\n\t}\n\n\tformat := fmt.Sprintf(\"%02dh%02dm%02ds\", hours, minutes, seconds)\n\tv, err := time.ParseDuration(format)\n\tif err != nil {\n\t\treturn TimeNotationDuration(0), err\n\t}\n\n\treturn TimeNotationDuration(v), nil\n\n}\n\nfunc (d TimeNotationDuration) MarshalJSON() ([]byte, error) {\n\tv := d.ToDuration()\n\n\tformat := fmtDuration(v)\n\treturn []byte(strconv.Quote(format)), nil\n}\n\nfunc (d *TimeNotationDuration) UnmarshalJSON(b []byte) error {\n\tif len(b) == 0 || bytes.Equal(b, nullLiteral) || bytes.Equal(b, emptyQuoteBytes) { // if null or empty don't throw an error.\n\t\treturn nil\n\t}\n\n\tvar v any\n\tif err := json.Unmarshal(b, &v); err != nil {\n\t\treturn err\n\t}\n\tswitch value := v.(type) {\n\tcase float64:\n\t\t*d = TimeNotationDuration(value)\n\t\treturn nil\n\tcase string:\n\t\tdv, err := ParseTimeNotationDuration(value)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t*d = dv\n\t\treturn nil\n\tdefault:\n\t\treturn errors.New(\"invalid duration\")\n\t}\n}\n\nfunc (d TimeNotationDuration) ToDuration() time.Duration {\n\treturn time.Duration(d)\n}\n\nfunc (d TimeNotationDuration) Value() (driver.Value, error) {\n\treturn d.ToDuration(), nil\n}\n\n// Set sets the value of duration in nanoseconds.\nfunc (d *TimeNotationDuration) Set(v float64) {\n\tif math.IsNaN(v) {\n\t\treturn\n\t}\n\n\t*d = TimeNotationDuration(v)\n}\n"
  },
  {
    "path": "x/mathx/round.go",
    "content": "package mathx\n\nimport \"math\"\n\n// Round rounds the \"input\" on \"roundOn\" (e.g. 0.5) on \"places\" digits.\nfunc Round(input float64, roundOn float64, places float64) float64 {\n\tpow := math.Pow(10, places)\n\tdigit := pow * input\n\n\t_, div := math.Modf(digit)\n\tif div >= roundOn {\n\t\treturn math.Ceil(digit) / pow\n\t}\n\n\treturn math.Floor(digit) / pow\n}\n\n// RoundUp rounds up the \"input\" up to \"places\" digits.\nfunc RoundUp(input float64, places float64) float64 {\n\tpow := math.Pow(10, places)\n\treturn math.Ceil(pow*input) / pow\n}\n\n// RoundDown rounds down the \"input\" up to \"places\" digits.\nfunc RoundDown(input float64, places float64) float64 {\n\tpow := math.Pow(10, places)\n\treturn math.Floor(pow*input) / pow\n}\n\n// RoundToInteger rounds the given float64 to an integer.\nfunc RoundToInteger(x float64) int {\n\t// If the number is 1.1 round it to the previous integer (1), if >= 1.11 round it to the next one (2).\n\tt := math.Trunc(x)\n\todd := math.Remainder(t, 2) != 0\n\td := math.Abs(x - t)\n\td = Round(d, 0.5, 2) // round to 2 decimals so we can easily check 0.11 and 0.1 and 0.2.\n\tif d > 0.1 || (d == 0.2 && odd) {\n\t\t// fmt.Printf(\"%f-%f -> %f. Is > 0.1 -> %v \\n\", x, t, d, d > 0.1)\n\t\tt = t + math.Copysign(1, x)\n\t\treturn int(t)\n\t}\n\treturn int(t)\n}\n"
  },
  {
    "path": "x/pagination/pagination.go",
    "content": "//go:build go1.18\n// +build go1.18\n\n/*\nUntil go version 2, we can't really apply the type alias feature on a generic type or function,\nso keep it separated on x/pagination.\n\nimport \"github.com/kataras/iris/v12/context\"\n\ntype ListResponse[T any] = context.ListResponse[T]\nOR\ntype ListResponse = context.ListResponse doesn't work.\n\nThe only workable thing for generic aliases is when you know the type e.g.\ntype ListResponse = context.ListResponse[any] but that doesn't fit us.\n*/\n\npackage pagination\n\nimport (\n\t\"math\"\n\t\"net/http\"\n\t\"strconv\"\n)\n\nvar (\n\t// MaxSize defines the max size of items to display.\n\tMaxSize = 100000\n\t// DefaultSize defines the default size when ListOptions.Size is zero.\n\tDefaultSize = MaxSize\n)\n\n// ListOptions is the list request object which should be provided by the client through\n// URL Query. Then the server passes that options to a database query,\n// including any custom filters may be given from the request body and,\n// then the server responds back with a `Context.JSON(NewList(...))` response based\n// on the database query's results.\ntype ListOptions struct {\n\t// Current page number.\n\t// If Page > 0 then:\n\t// Limit = DefaultLimit\n\t// Offset = DefaultLimit * Page\n\t// If Page == 0 then no actual data is return,\n\t// internally we must check for this value\n\t// because in postgres LIMIT 0 returns the columns but with an empty set.\n\tPage int `json:\"page\" url:\"page\"`\n\t// The elements to get, this modifies the LIMIT clause,\n\t// this Size can't be higher than the MaxSize.\n\t// If Size is zero then size is set to DefaultSize.\n\tSize int `json:\"size\" url:\"size\"`\n}\n\n// GetLimit returns the LIMIT value of a query.\nfunc (opts ListOptions) GetLimit() int {\n\tif opts.Size > 0 && opts.Size < MaxSize {\n\t\treturn opts.Size\n\t}\n\n\treturn DefaultSize\n}\n\n// GetLimit returns the OFFSET value of a query.\nfunc (opts ListOptions) GetOffset() int {\n\tif opts.Page > 1 {\n\t\treturn (opts.Page - 1) * opts.GetLimit()\n\t}\n\n\treturn 0\n}\n\n// GetCurrentPage returns the Page or 1.\nfunc (opts ListOptions) GetCurrentPage() int {\n\tcurrent := opts.Page\n\tif current == 0 {\n\t\tcurrent = 1\n\t}\n\n\treturn current\n}\n\n// GetNextPage returns the next page, current page + 1.\nfunc (opts ListOptions) GetNextPage() int {\n\treturn opts.GetCurrentPage() + 1\n}\n\n// Bind binds the ListOptions values to a request value.\n// It should be used as an x/client.RequestOption to fire requests\n// on a server that supports pagination.\nfunc (opts ListOptions) Bind(r *http.Request) error {\n\tpage := strconv.Itoa(opts.GetCurrentPage())\n\tsize := strconv.Itoa(opts.GetLimit())\n\n\tq := r.URL.Query()\n\tq.Set(\"page\", page)\n\tq.Set(\"size\", size)\n\treturn nil\n}\n\n// List is the http response of a server handler which should render\n// items with pagination support.\ntype List[T any] struct {\n\tCurrentPage int   `json:\"current_page\"`  // the current page.\n\tPageSize    int   `json:\"page_size\"`     // the total amount of the entities return.\n\tTotalPages  int   `json:\"total_pages\"`   // the total number of pages based on page, size and total count.\n\tTotalItems  int64 `json:\"total_items\"`   // the total number of rows.\n\tHasNextPage bool  `json:\"has_next_page\"` // true if more data can be fetched, depending on the current page * page size and total pages.\n\tFilter      any   `json:\"filter\"`        // if any filter data.\n\tItems       []T   `json:\"items\"`         // Items is empty array if no objects returned. Do NOT modify from outside.\n}\n\n// NewList returns a new List response which holds\n// the current page, page size, total pages, total items count, any custom filter\n// and the items array.\n//\n// Example Code:\n//\n//\timport \"github.com/kataras/iris/v12/x/pagination\"\n//\t...more code\n//\n//\ttype User struct {\n//\t\tFirstname string `json:\"firstname\"`\n//\t\tLastname  string `json:\"lastname\"`\n//\t}\n//\n//\ttype ExtraUser struct {\n//\t\tUser\n//\t\tExtraData string\n//\t}\n//\n//\tfunc main() {\n//\t\tusers := []User{\n//\t\t\t{\"Gerasimos\", \"Maropoulos\"},\n//\t\t\t{\"Efi\", \"Kwfidou\"},\n//\t\t}\n//\n//\t\tt := pagination.NewList(users, 100, nil, pagination.ListOptions{\n//\t\t\tPage: 1,\n//\t\t\tSize: 50,\n//\t\t})\n//\n//\t\t// Optionally, transform a T list of objects to a V list of objects.\n//\t\tv, err := pagination.TransformList(t, func(u User) (ExtraUser, error) {\n//\t\t\treturn ExtraUser{\n//\t\t\t\tUser:      u,\n//\t\t\t\tExtraData: \"test extra data\",\n//\t\t\t}, nil\n//\t\t})\n//\t\tif err != nil { panic(err) }\n//\n//\t\tpaginationJSON, err := json.MarshalIndent(v, \"\", \"    \")\n//\t\tif err!=nil { panic(err) }\n//\t\tfmt.Println(paginationJSON)\n//\t}\nfunc NewList[T any](items []T, totalCount int64, filter any, opts ListOptions) *List[T] {\n\tpageSize := opts.GetLimit()\n\n\tn := len(items)\n\tif n == 0 || pageSize <= 0 {\n\t\treturn &List[T]{\n\t\t\tCurrentPage: 1,\n\t\t\tPageSize:    0,\n\t\t\tTotalItems:  0,\n\t\t\tTotalPages:  0,\n\t\t\tFilter:      filter,\n\t\t\tItems:       make([]T, 0),\n\t\t}\n\t}\n\n\tnumberOfPages := int(roundUp(float64(totalCount)/float64(pageSize), 0))\n\tif numberOfPages <= 0 {\n\t\tnumberOfPages = 1\n\t}\n\n\tvar hasNextPage bool\n\n\tcurrentPage := opts.GetCurrentPage()\n\tif totalCount == 0 {\n\t\tcurrentPage = 1\n\t}\n\n\tif n > 0 {\n\t\thasNextPage = currentPage < numberOfPages\n\t}\n\n\treturn &List[T]{\n\t\tCurrentPage: currentPage,\n\t\tPageSize:    n,\n\t\tTotalPages:  numberOfPages,\n\t\tTotalItems:  totalCount,\n\t\tHasNextPage: hasNextPage,\n\t\tFilter:      filter,\n\t\tItems:       items,\n\t}\n}\n\n// TransformList accepts a List response and converts to a list of V items.\n// T => from\n// V => to\n//\n// Example Code:\n//\n//\tlistOfUsers := pagination.NewList(...)\n//\tnewListOfExtraUsers, err := pagination.TransformList(listOfUsers, func(u User) (ExtraUser, error) {\n//\t\treturn ExtraUser{\n//\t\t\tUser:      u,\n//\t\t\tExtraData: \"test extra data\",\n//\t\t}, nil\n//\t})\nfunc TransformList[T any, V any](list *List[T], transform func(T) (V, error)) (*List[V], error) {\n\tif list == nil {\n\t\treturn &List[V]{\n\t\t\tCurrentPage: 1,\n\t\t\tPageSize:    0,\n\t\t\tTotalItems:  0,\n\t\t\tTotalPages:  0,\n\t\t\tFilter:      nil,\n\t\t\tItems:       make([]V, 0),\n\t\t}, nil\n\t}\n\n\titems := list.Items\n\n\ttoItems := make([]V, 0, len(items))\n\tfor _, fromItem := range items {\n\t\ttoItem, err := transform(fromItem)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\ttoItems = append(toItems, toItem)\n\t}\n\n\tnewList := &List[V]{\n\t\tCurrentPage: list.CurrentPage,\n\t\tPageSize:    list.PageSize,\n\t\tTotalItems:  list.TotalItems,\n\t\tTotalPages:  list.TotalPages,\n\t\tFilter:      list.Filter,\n\t\tItems:       toItems,\n\t}\n\treturn newList, nil\n}\n\nfunc roundUp(input float64, places float64) float64 {\n\tpow := math.Pow(10, places)\n\treturn math.Ceil(pow*input) / pow\n}\n"
  },
  {
    "path": "x/reflex/error.go",
    "content": "package reflex\n\nimport \"reflect\"\n\n// IsError reports whether \"typ\" is an error type.\nfunc IsError(typ interface{ Implements(reflect.Type) bool }) bool {\n\treturn typ.Implements(ErrTyp)\n}\n"
  },
  {
    "path": "x/reflex/func.go",
    "content": "package reflex\n\nimport \"reflect\"\n\n// IsFunc reports whether the \"kindable\" is a type of function.\nfunc IsFunc(typ interface{ Kind() reflect.Kind }) bool {\n\treturn typ.Kind() == reflect.Func\n}\n\n// FuncParam holds the properties of function input or output.\ntype FuncParam struct {\n\tIndex int\n\tType  reflect.Type\n}\n\n// LookupInputs returns the index and type of each function's input argument.\n// Panics if \"fn\" is not a type of Func.\nfunc LookupInputs(fn reflect.Type) []FuncParam {\n\tn := fn.NumIn()\n\tparams := make([]FuncParam, 0, n)\n\tfor i := 0; i < n; i++ {\n\t\tin := fn.In(i)\n\t\tparams = append(params, FuncParam{\n\t\t\tIndex: i,\n\t\t\tType:  in,\n\t\t})\n\t}\n\treturn params\n}\n\n// LookupOutputs returns the index and type of each function's output argument.\n// Panics if \"fn\" is not a type of Func.\nfunc LookupOutputs(fn reflect.Type) []FuncParam {\n\tn := fn.NumOut()\n\tparams := make([]FuncParam, 0, n)\n\tfor i := 0; i < n; i++ {\n\t\tout := fn.Out(i)\n\t\tparams = append(params, FuncParam{\n\t\t\tIndex: i,\n\t\t\tType:  out,\n\t\t})\n\t}\n\treturn params\n}\n"
  },
  {
    "path": "x/reflex/reflex.go",
    "content": "package reflex\n\nimport \"reflect\"\n\n// IndirectType returns the value of a pointer-type \"typ\".\n// If \"IndirectType\" is a pointer, array, chan, map or slice it returns its Elem,\n// otherwise returns the \"typ\" as it is.\nfunc IndirectType(typ reflect.Type) reflect.Type {\n\tswitch typ.Kind() {\n\tcase reflect.Ptr, reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:\n\t\treturn typ.Elem()\n\t}\n\treturn typ\n}\n\n// IndirectValue returns the element type (e.g. if pointer of *User it will return the User type).\nfunc IndirectValue(val reflect.Value) reflect.Value {\n\treturn reflect.Indirect(val)\n}\n"
  },
  {
    "path": "x/reflex/struct.go",
    "content": "package reflex\n\nimport \"reflect\"\n\n// LookupFields returns a slice of all fields containing a struct field\n// of the given \"fieldTag\" of the \"typ\" struct. The fields returned\n// are flatted and reclusive over fields with value of struct.\n// Panics if \"typ\" is not a type of Struct.\nfunc LookupFields(typ reflect.Type, fieldTag string) []reflect.StructField {\n\tfields := lookupFields(typ, fieldTag, nil)\n\treturn fields[0:len(fields):len(fields)]\n}\n\nfunc lookupFields(typ reflect.Type, fieldTag string, parentIndex []int) []reflect.StructField {\n\tn := typ.NumField()\n\tfields := make([]reflect.StructField, 0, n)\n\tcheckTag := fieldTag != \"\"\n\tfor i := 0; i < n; i++ {\n\t\tfield := typ.Field(i)\n\t\tif field.PkgPath != \"\" { // skip unexported fields.\n\t\t\tcontinue\n\t\t}\n\n\t\tif checkTag {\n\t\t\tif v := field.Tag.Get(fieldTag); v == \"\" || v == \"-\" {\n\t\t\t\t// Skip fields that don't contain the 'fieldTag' tag or has '-'.\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\tfieldType := IndirectType(field.Type)\n\n\t\tif fieldType.Kind() == reflect.Struct { // It's a struct inside a struct and it's not time, flat it.\n\t\t\tif fieldType != TimeType {\n\t\t\t\tstructFields := lookupFields(fieldType, fieldTag, append(parentIndex, i))\n\t\t\t\tif nn := len(structFields); nn > 0 {\n\t\t\t\t\tfields = append(fields, structFields...)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tindex := []int{i}\n\t\tif len(parentIndex) > 0 {\n\t\t\tindex = append(parentIndex, i)\n\t\t}\n\n\t\ttmp := make([]int, len(index))\n\t\tcopy(tmp, index)\n\t\tfield.Index = tmp\n\n\t\tfields = append(fields, field)\n\t}\n\n\treturn fields\n}\n\n// LookupUnderlineValueType returns the underline type of \"v\".\nfunc LookupUnderlineValueType(v reflect.Value) (reflect.Value, reflect.Type) {\n\ttyp := v.Type()\n\tfor typ.Kind() == reflect.Ptr {\n\t\ttyp = typ.Elem()\n\t\tv = reflect.New(typ).Elem()\n\t}\n\n\treturn v, typ\n}\n"
  },
  {
    "path": "x/reflex/types.go",
    "content": "package reflex\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net\"\n\t\"reflect\"\n\t\"time\"\n)\n\n// Common reflect types for go standard data types.\nvar (\n\tStringType            = reflect.TypeOf(\"\")\n\tBytesType             = reflect.TypeOf([]byte{})\n\tIntType               = reflect.TypeOf(int(0))\n\tInt16Type             = reflect.TypeOf(int16(0))\n\tInt32Type             = reflect.TypeOf(int32(0))\n\tInt64Type             = reflect.TypeOf(int64(0))\n\tFloat32Type           = reflect.TypeOf(float32(0))\n\tFloat64Type           = reflect.TypeOf(float64(0))\n\tTimeType              = reflect.TypeOf(time.Time{})\n\tIpTyp                 = reflect.TypeOf(net.IP{})\n\tJSONNumberTyp         = reflect.TypeOf(json.Number(\"\"))\n\tStringerTyp           = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()\n\tArrayIntegerTyp       = reflect.TypeOf([]int{})\n\tArrayStringTyp        = reflect.TypeOf([]string{})\n\tDoubleArrayIntegerTyp = reflect.TypeOf([][]int{})\n\tDoubleArrayStringTyp  = reflect.TypeOf([][]string{})\n\tErrTyp                = reflect.TypeOf((*error)(nil)).Elem()\n)\n"
  },
  {
    "path": "x/reflex/zero.go",
    "content": "package reflex\n\nimport (\n\t\"encoding/json\"\n\t\"net\"\n)\n\n// Zeroer can be implemented by custom types\n// to report whether its current value is zero.\n// Standard Time also implements that.\ntype Zeroer interface {\n\tIsZero() bool\n}\n\n// IsZero reports whether \"v\" is zero value or no.\n// The given \"v\" value can complete the Zeroer interface\n// which can be used to customize the behavior for each type of \"v\".\nfunc IsZero(v any) bool {\n\tswitch t := v.(type) {\n\tcase Zeroer: // completes the time.Time as well.\n\t\treturn t.IsZero()\n\tcase string:\n\t\treturn t == \"\"\n\tcase int:\n\t\treturn t == 0\n\tcase int8:\n\t\treturn t == 0\n\tcase int16:\n\t\treturn t == 0\n\tcase int32:\n\t\treturn t == 0\n\tcase int64:\n\t\treturn t == 0\n\tcase uint:\n\t\treturn t == 0\n\tcase uint8:\n\t\treturn t == 0\n\tcase uint16:\n\t\treturn t == 0\n\tcase uint32:\n\t\treturn t == 0\n\tcase uint64:\n\t\treturn t == 0\n\tcase float32:\n\t\treturn t == 0\n\tcase float64:\n\t\treturn t == 0\n\tcase bool:\n\t\treturn !t\n\tcase []int:\n\t\treturn len(t) == 0\n\tcase []string:\n\t\treturn len(t) == 0\n\tcase [][]int:\n\t\treturn len(t) == 0\n\tcase [][]string:\n\t\treturn len(t) == 0\n\tcase json.Number:\n\t\treturn t.String() == \"\"\n\tcase net.IP:\n\t\treturn len(t) == 0\n\tdefault:\n\t\treturn false\n\t}\n}\n"
  },
  {
    "path": "x/sqlx/sqlx.go",
    "content": "package sqlx\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n\t\"unsafe\"\n\n\t\"github.com/kataras/iris/v12/x/reflex\"\n)\n\ntype (\n\t// Schema holds the row definitions.\n\tSchema struct {\n\t\tName           string\n\t\tRows           map[reflect.Type]*Row\n\t\tColumnNameFunc ColumnNameFunc\n\t\tAutoCloseRows  bool\n\t}\n\n\t// Row holds the column definitions and the struct type & name.\n\tRow struct {\n\t\tSchema     string // e.g. public\n\t\tName       string // e.g. users. Must set to a custom one if the select query contains AS names.\n\t\tStructType reflect.Type\n\t\tColumns    map[string]*Column // e.g. \"id\":{\"id\", 0, [0]}\n\t}\n\n\t// Column holds the database column name and other properties extracted by a struct's field.\n\tColumn struct {\n\t\tName       string\n\t\tIndex      int\n\t\tFieldIndex []int\n\t}\n)\n\n// NewSchema returns a new Schema. Use its Register() method to cache\n// a structure value so Bind() can fill all struct's fields based on a query.\nfunc NewSchema() *Schema {\n\treturn &Schema{\n\t\tName:           \"public\",\n\t\tRows:           make(map[reflect.Type]*Row),\n\t\tColumnNameFunc: snakeCase,\n\t\tAutoCloseRows:  true,\n\t}\n}\n\n// DefaultSchema initializes a common Schema.\nvar DefaultSchema = NewSchema()\n\n// Register caches a struct value to the default schema.\nfunc Register(tableName string, value any) *Schema {\n\treturn DefaultSchema.Register(tableName, value)\n}\n\n// Query is a shortcut of executing a query and bind the result to \"dst\".\nfunc Query(ctx context.Context, db *sql.DB, dst any, query string, args ...any) error {\n\treturn DefaultSchema.Query(ctx, db, dst, query, args...)\n}\n\n// Bind sets \"dst\" to the result of \"src\" and reports any errors.\nfunc Bind(dst any, src *sql.Rows) error {\n\treturn DefaultSchema.Bind(dst, src)\n}\n\n// Register caches a struct value to the schema.\nfunc (s *Schema) Register(tableName string, value any) *Schema {\n\ttyp := reflect.TypeOf(value)\n\tfor typ.Kind() == reflect.Ptr {\n\t\ttyp = typ.Elem()\n\t}\n\n\tif tableName == \"\" {\n\t\t// convert to a human name, e.g. sqlx.Food -> food.\n\t\ttypeName := typ.String()\n\t\tif idx := strings.LastIndexByte(typeName, '.'); idx > 0 && len(typeName) > idx {\n\t\t\ttypeName = typeName[idx+1:]\n\t\t}\n\t\ttableName = snakeCase(typeName)\n\t}\n\n\tcolumns, err := convertStructToColumns(typ, s.ColumnNameFunc)\n\tif err != nil {\n\t\tpanic(fmt.Sprintf(\"sqlx: register: %q: %s\", reflect.TypeOf(value).String(), err.Error()))\n\t}\n\n\ts.Rows[typ] = &Row{\n\t\tSchema:     s.Name,\n\t\tName:       tableName,\n\t\tStructType: typ,\n\t\tColumns:    columns,\n\t}\n\n\treturn s\n}\n\n// Query is a shortcut of executing a query and bind the result to \"dst\".\nfunc (s *Schema) Query(ctx context.Context, db *sql.DB, dst any, query string, args ...any) error {\n\trows, err := db.QueryContext(ctx, query, args...)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif !s.AutoCloseRows { // if not close on bind, we must close it here.\n\t\tdefer rows.Close()\n\t}\n\n\terr = s.Bind(dst, rows)\n\treturn err\n}\n\n// Bind sets \"dst\" to the result of \"src\" and reports any errors.\nfunc (s *Schema) Bind(dst any, src *sql.Rows) error {\n\ttyp := reflect.TypeOf(dst)\n\tif typ.Kind() != reflect.Ptr {\n\t\treturn fmt.Errorf(\"sqlx: bind: destination not a pointer\")\n\t}\n\n\ttyp = typ.Elem()\n\n\toriginalKind := typ.Kind()\n\tif typ.Kind() == reflect.Slice {\n\t\ttyp = typ.Elem()\n\t}\n\n\tr, ok := s.Rows[typ]\n\tif !ok {\n\t\treturn fmt.Errorf(\"sqlx: bind: unregistered type: %q\", typ.String())\n\t}\n\n\tcolumnTypes, err := src.ColumnTypes()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"sqlx: bind: table: %q: %w\", r.Name, err)\n\t}\n\n\tif expected, got := len(r.Columns), len(columnTypes); expected != got {\n\t\treturn fmt.Errorf(\"sqlx: bind: table: %q: unexpected number of result columns: %d: expected: %d\", r.Name, got, expected)\n\t}\n\n\tval := reflex.IndirectValue(reflect.ValueOf(dst))\n\tif s.AutoCloseRows {\n\t\tdefer src.Close()\n\t}\n\n\tswitch originalKind {\n\tcase reflect.Struct:\n\t\tif src.Next() {\n\t\t\tif err = r.bindSingle(typ, val, columnTypes, src); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t} else {\n\t\t\treturn sql.ErrNoRows\n\t\t}\n\n\t\treturn src.Err()\n\tcase reflect.Slice:\n\t\tfor src.Next() {\n\t\t\telem := reflect.New(typ).Elem()\n\t\t\tif err = r.bindSingle(typ, elem, columnTypes, src); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tval = reflect.Append(val, elem)\n\t\t}\n\n\t\tif err = src.Err(); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\treflect.ValueOf(dst).Elem().Set(val)\n\t\treturn nil\n\tdefault:\n\t\treturn fmt.Errorf(\"sqlx: bind: table: %q: unexpected destination kind: %q\", r.Name, typ.Kind().String())\n\t}\n}\n\nfunc (r *Row) bindSingle(typ reflect.Type, val reflect.Value, columnTypes []*sql.ColumnType, scanner interface{ Scan(...any) error }) error {\n\tfieldPtrs, err := r.lookupStructFieldPtrs(typ, val, columnTypes)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"sqlx: bind: table: %q: %w\", r.Name, err)\n\t}\n\n\treturn scanner.Scan(fieldPtrs...)\n}\n\nfunc (r *Row) lookupStructFieldPtrs(typ reflect.Type, val reflect.Value, columnTypes []*sql.ColumnType) ([]any, error) {\n\tfieldPtrs := make([]any, 0, len(columnTypes))\n\n\tfor _, columnType := range columnTypes {\n\t\tcolumnName := columnType.Name()\n\t\ttableColumn, ok := r.Columns[columnName]\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\n\t\t// TODO: when go 1.18 released, replace with that:\n\t\t/*\n\t\t\ttableColumnField, err := val.FieldByIndexErr(tableColumn.FieldIndex)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"column: %q: %w\", tableColumn.Name, err)\n\t\t\t}\n\t\t*/\n\t\ttableColumnField := val.FieldByIndex(tableColumn.FieldIndex)\n\n\t\ttableColumnFieldType := tableColumnField.Type()\n\n\t\tfieldPtr := reflect.NewAt(tableColumnFieldType, unsafe.Pointer(tableColumnField.UnsafeAddr())).Elem().Addr().Interface()\n\t\tfieldPtrs = append(fieldPtrs, fieldPtr)\n\t}\n\n\treturn fieldPtrs, nil\n}\n"
  },
  {
    "path": "x/sqlx/sqlx_test.go",
    "content": "package sqlx\n\n/*\nimport (\n\t\"reflect\"\n\t\"testing\"\n\n\tsqlmock \"github.com/DATA-DOG/go-sqlmock\"\n)\n\ntype food struct {\n\tID        string\n\tName      string\n\tPresenter bool `db:\"-\"`\n}\n\nfunc TestTableBind(t *testing.T) {\n\tRegister(\"foods\", food{})\n\n\tdb, mock, err := sqlmock.New()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tmock.ExpectQuery(\"SELECT .* FROM foods WHERE id = ?\").\n\t\tWithArgs(\"42\").\n\t\tWillReturnRows(sqlmock.NewRows([]string{\"id\", \"name\"}).\n\t\t\tAddRow(\"42\", \"banana\").\n\t\t\tAddRow(\"43\", \"broccoli\"))\n\n\trows, err := db.Query(\"SELECT .* FROM foods WHERE id = ? LIMIT 1\", \"42\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tvar f food\n\terr = Bind(&f, rows)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\texpectedSingle := food{\"42\", \"banana\", false}\n\tif !reflect.DeepEqual(f, expectedSingle) {\n\t\tt.Fatalf(\"expected value: %#+v but got: %#+v\", expectedSingle, f)\n\t}\n\n\tmock.ExpectQuery(\"SELECT .* FROM foods\").\n\t\tWillReturnRows(sqlmock.NewRows([]string{\"id\", \"name\"}).\n\t\t\tAddRow(\"42\", \"banana\").\n\t\t\tAddRow(\"43\", \"broccoli\").\n\t\t\tAddRow(\"44\", \"chicken\"))\n\trows, err = db.Query(\"SELECT .* FROM foods\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tvar foods []food\n\terr = Bind(&foods, rows)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\texpectedMany := []food{\n\t\t{\"42\", \"banana\", false},\n\t\t{\"43\", \"broccoli\", false},\n\t\t{\"44\", \"chicken\", false},\n\t}\n\n\tfor i := range foods {\n\t\tif !reflect.DeepEqual(foods[i], expectedMany[i]) {\n\t\t\tt.Fatalf(\"[%d] expected: %#+v but got: %#+v\", i, expectedMany[i], foods[i])\n\t\t}\n\t}\n}\n*/\n"
  },
  {
    "path": "x/sqlx/struct_row.go",
    "content": "package sqlx\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n\n\t\"github.com/kataras/iris/v12/x/reflex\"\n)\n\n// DefaultTag is the default struct field tag.\nvar DefaultTag = \"db\"\n\n// ColumnNameFunc is the function which converts a struct field name to a database column name.\ntype ColumnNameFunc = func(string) string\n\nfunc convertStructToColumns(typ reflect.Type, nameFunc ColumnNameFunc) (map[string]*Column, error) {\n\tif kind := typ.Kind(); kind != reflect.Struct {\n\t\treturn nil, fmt.Errorf(\"convert struct: invalid type: expected a struct value but got: %q\", kind.String())\n\t}\n\n\t// Retrieve only fields valid for database.\n\tfields := reflex.LookupFields(typ, \"\")\n\n\tcolumns := make(map[string]*Column, len(fields))\n\tfor i, field := range fields {\n\t\tcolumn, ok, err := convertStructFieldToColumn(field, DefaultTag, nameFunc)\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"convert struct: field name: %q: %w\", field.Name, err)\n\t\t}\n\n\t\tcolumn.Index = i\n\t\tcolumns[column.Name] = column\n\t}\n\n\treturn columns, nil\n}\n\nfunc convertStructFieldToColumn(field reflect.StructField, optionalTag string, nameFunc ColumnNameFunc) (*Column, bool, error) {\n\tc := &Column{\n\t\tName:       nameFunc(field.Name),\n\t\tFieldIndex: field.Index,\n\t}\n\n\tfieldTag, ok := field.Tag.Lookup(optionalTag)\n\tif ok {\n\t\tif fieldTag == \"-\" {\n\t\t\treturn nil, false, nil\n\t\t}\n\n\t\tif err := parseOptions(fieldTag, c); err != nil {\n\t\t\treturn nil, false, err\n\t\t}\n\t}\n\n\treturn c, true, nil\n}\n\nfunc parseOptions(fieldTag string, c *Column) error {\n\toptions := strings.Split(fieldTag, \",\")\n\tfor _, opt := range options {\n\t\tif opt == \"\" {\n\t\t\tcontinue // skip empty.\n\t\t}\n\n\t\tvar key, value string\n\n\t\tkv := strings.Split(opt, \"=\") // When more options come to play.\n\t\tswitch len(kv) {\n\t\tcase 2:\n\t\t\tkey = kv[0]\n\t\t\tvalue = kv[1]\n\t\tcase 1:\n\t\t\tc.Name = kv[0]\n\t\t\treturn nil\n\t\tdefault:\n\t\t\treturn fmt.Errorf(\"option: %s: expected key value separated by '='\", opt)\n\t\t}\n\n\t\tswitch key {\n\t\tcase \"name\":\n\t\t\tc.Name = value\n\t\tdefault:\n\t\t\treturn fmt.Errorf(\"unexpected tag option: %s\", key)\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "x/sqlx/util.go",
    "content": "package sqlx\n\nimport \"strings\"\n\n// snakeCase converts a given string to a friendly snake case, e.g.\n// - userId to user_id\n// - ID     to id\n// - ProviderAPIKey to provider_api_key\n// - Option to option\nfunc snakeCase(camel string) string {\n\tvar (\n\t\tb            strings.Builder\n\t\tprevWasUpper bool\n\t)\n\n\tfor i, c := range camel {\n\t\tif isUppercase(c) { // it's upper.\n\t\t\tif b.Len() > 0 && !prevWasUpper { // it's not the first and the previous was not uppercased too (e.g  \"ID\").\n\t\t\t\tb.WriteRune('_')\n\t\t\t} else { // check for XxxAPIKey, it should be written as xxx_api_key.\n\t\t\t\tnext := i + 1\n\t\t\t\tif next > 1 && len(camel)-1 > next {\n\t\t\t\t\tif !isUppercase(rune(camel[next])) {\n\t\t\t\t\t\tb.WriteRune('_')\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tb.WriteRune(c - 'A' + 'a') // write its lowercase version.\n\t\t\tprevWasUpper = true\n\t\t} else {\n\t\t\tb.WriteRune(c) // write it as it is, it's already lowercased.\n\t\t\tprevWasUpper = false\n\t\t}\n\t}\n\n\treturn b.String()\n}\n\nfunc isUppercase(c rune) bool {\n\treturn 'A' <= c && c <= 'Z'\n}\n"
  },
  {
    "path": "x/timex/weekday.go",
    "content": "package timex\n\nimport \"time\"\n\n// RangeDate returns a function which returns a time\n// between \"start\" and \"end\". When the iteration finishes\n// the returned time is zero.\nfunc RangeDate(start, end time.Time) func() time.Time {\n\ty, m, d := start.Date()\n\tstart = time.Date(y, m, d, 0, 0, 0, 0, start.Location())\n\ty, m, d = end.Date()\n\tend = time.Date(y, m, d, 0, 0, 0, 0, end.Location())\n\n\treturn func() time.Time {\n\t\tif start.After(end) {\n\t\t\treturn time.Time{}\n\t\t}\n\t\tdate := start\n\t\tstart = start.AddDate(0, 0, 1)\n\t\treturn date\n\t}\n}\n\ntype DateRangeType string\n\nconst (\n\tDayRange   DateRangeType = \"day\"\n\tMonthRange DateRangeType = \"month\"\n\tWeekRange  DateRangeType = \"week\"\n\tYearRange  DateRangeType = \"year\"\n)\n\n// Between returns the dates from \"start\" to \"end\".\nfunc Between(start, end time.Time) []time.Time {\n\tvar dates []time.Time\n\tfor df := RangeDate(start, end); ; {\n\t\td := df()\n\t\tif d.IsZero() {\n\t\t\tbreak\n\t\t}\n\t\tdates = append(dates, d)\n\t}\n\treturn dates\n}\n\n// Backwards returns a list of dates between \"end\" and -n (years, months, weeks or days).\nfunc Backwards(typ DateRangeType, end time.Time, n int) []time.Time {\n\tvar start time.Time\n\n\tswitch typ {\n\tcase DayRange:\n\t\tn = n - 1 // -7 should be -6 to get the week from today.\n\t\tstart = end.AddDate(0, 0, -n)\n\tcase WeekRange:\n\t\t// 7 should be 6 to get the week.\n\t\tstart = end.AddDate(0, 0, -n*6)\n\tcase MonthRange:\n\t\tstart = end.AddDate(0, -n, 0)\n\tcase YearRange:\n\t\tstart = end.AddDate(-n, 0, 0)\n\t}\n\n\treturn Between(start, end)\n}\n\n// BackwardsN returns the dates from back to \"n\" years, months, weeks or days from today.\nfunc BackwardsN(typ DateRangeType, n int) []time.Time {\n\tend := time.Now()\n\treturn Backwards(typ, end, n)\n}\n\n// BackwardsToMonday returns the dates between \"end\" (including \"end\")\n// until the previous monday of the current week (including monday).\nfunc BackwardsToMonday(end time.Time) []time.Time {\n\tdates := []time.Time{end}\n\tfor end.Weekday() != time.Monday {\n\t\tend = end.AddDate(0, 0, -1)\n\t\tdates = append(dates, end)\n\t}\n\treturn dates\n}\n\n// GetWeekDate returns the date of the given weekday (monday, tuesday, etc.) of the current week.\nfunc GetWeekDate(now time.Time, weekday, start, end time.Weekday) time.Time {\n\tdates := GetWeekdays(now, start, end)\n\tfor _, d := range dates {\n\t\tif d.Weekday() == weekday {\n\t\t\treturn d\n\t\t}\n\t}\n\n\treturn time.Time{}\n}\n\n// GetWeekStart returns the date of the first week day (startWeekday) of the current now's week.\nfunc GetWeekStart(now time.Time, startWeekday time.Weekday) time.Time {\n\toffset := (int(startWeekday) - int(now.Weekday()) - 7) % 7\n\tdate := now.Add(time.Duration(offset*24) * time.Hour)\n\treturn date\n}\n\n// GetWeekEnd returns the date of the last week day (endWeekday) of the current now's week.\nfunc GetWeekEnd(now time.Time, endWeekday time.Weekday) time.Time {\n\toffset := (int(endWeekday) - int(now.Weekday()) + 7) % 7\n\tdate := now.Add(time.Duration(offset*24) * time.Hour)\n\treturn date\n}\n\n// GetWeekdays returns the range between \"startWeekday\" and \"endWeekday\" of the current week.\nfunc GetWeekdays(now time.Time, startWeekday, endWeekday time.Weekday) (dates []time.Time) {\n\treturn Between(GetWeekStart(now, startWeekday), GetWeekEnd(now, endWeekday))\n}\n\n// GetMonthStart returns the date of the first month day of the current now's month.\nfunc GetMonthStart(now time.Time) time.Time {\n\treturn time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location())\n}\n\n// GetMonthEnd returns the date of the last month day of the current now's month.\nfunc GetMonthEnd(now time.Time) time.Time {\n\tnow = now.UTC()\n\t// Add one month to the current date and subtract one day\n\treturn time.Date(now.Year(), now.Month()+1, 0, 0, 0, 0, 0, now.Location())\n}\n\n// GetMonthDays returns the range between first and last days the current month.\nfunc GetMonthDays(now time.Time) (dates []time.Time) {\n\treturn Between(GetMonthStart(now), GetMonthEnd(now))\n}\n\n// GetYearStart returns the date of the first year of the current now's year.\nfunc GetYearStart(now time.Time) time.Time {\n\treturn time.Date(now.Year(), 1, 1, 0, 0, 0, 0, now.Location())\n}\n"
  },
  {
    "path": "x/timex/weekday_test.go",
    "content": "package timex\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n)\n\nconst ISO8601Layout = \"2006-01-02T15:04:05\"\n\nfunc TestMonthAndYearStart(t *testing.T) {\n\tnow, err := time.Parse(ISO8601Layout, \"2021-04-21T00:00:00\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tstartMonthDate := GetMonthStart(now)\n\tif expected, got := \"2021-04-01 00:00:00 +0000 UTC\", startMonthDate.String(); expected != got {\n\t\tt.Logf(\"start of the current month: expected: %s but got: %s\", expected, got)\n\t}\n\n\tstartYearDate := GetYearStart(now)\n\tif expected, got := \"2021-01-01 00:00:00 +0000 UTC\", startYearDate.String(); expected != got {\n\t\tt.Logf(\"start of the current year: expected: %s but got: %s\", expected, got)\n\t}\n}\n\nfunc TestGetWeekEnd(t *testing.T) {\n\tvar tests = []struct {\n\t\tEnd             time.Weekday\n\t\tDates           []string\n\t\tExpectedDateEnd string\n\t}{\n\t\t{ // 1. Test sunday as end.\n\t\t\tEnd: time.Sunday,\n\t\t\tDates: []string{\n\t\t\t\t\"2022-01-17T00:00:00\", // 1.\n\t\t\t\t\"2022-01-18T00:00:00\", // 2.\n\t\t\t\t\"2022-01-19T00:00:00\", // 3.\n\t\t\t\t\"2022-01-20T00:00:00\", // 4.\n\t\t\t\t\"2022-01-21T00:00:00\", // 5.\n\t\t\t\t\"2022-01-22T00:00:00\", // 6.\n\t\t\t\t\"2022-01-23T00:00:00\", // 7.\n\t\t\t},\n\t\t\tExpectedDateEnd: \"2022-01-23T00:00:00\",\n\t\t},\n\t\t{ // 1. Test saturday as end.\n\t\t\tEnd: time.Saturday,\n\t\t\tDates: []string{\n\t\t\t\t\"2022-01-23T00:00:00\", // Sunday.\n\t\t\t\t\"2022-01-24T00:00:00\",\n\t\t\t\t\"2022-01-25T00:00:00\",\n\t\t\t\t\"2022-01-26T00:00:00\",\n\t\t\t\t\"2022-01-27T00:00:00\",\n\t\t\t\t\"2022-01-28T00:00:00\",\n\t\t\t\t\"2022-01-29T00:00:00\",\n\t\t\t},\n\t\t\tExpectedDateEnd: \"2022-01-29T00:00:00\",\n\t\t},\n\t}\n\n\tfor i := range tests {\n\t\ttt := tests[i]\n\t\tt.Run(fmt.Sprintf(\"%s[%d]\", t.Name(), i+1), func(t *testing.T) {\n\t\t\tfor j, date := range tt.Dates {\n\t\t\t\tnow, err := time.Parse(ISO8601Layout, date)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Fatal(err)\n\t\t\t\t}\n\n\t\t\t\tweekEndDate := GetWeekEnd(now, tt.End)\n\t\t\t\tif got := weekEndDate.Format(ISO8601Layout); got != tt.ExpectedDateEnd {\n\t\t\t\t\tt.Fatalf(\"[%d] expected week end date: %s but got: %s \", j+1, tt.ExpectedDateEnd, got)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestGetWeekDate(t *testing.T) {\n\tnow, err := time.Parse(ISO8601Layout, \"2022-02-10T00:00:00\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tvar tests = []struct {\n\t\tNow          time.Time\n\t\tStart        time.Weekday\n\t\tEnd          time.Weekday\n\t\tWeekday      time.Weekday\n\t\tExpectedDate string\n\t}{\n\t\t{\n\t\t\tNow:          now,\n\t\t\tStart:        time.Monday,\n\t\t\tEnd:          time.Sunday,\n\t\t\tWeekday:      time.Monday,\n\t\t\tExpectedDate: \"2022-02-07T00:00:00\",\n\t\t},\n\t\t{\n\t\t\tNow:          now,\n\t\t\tStart:        time.Monday,\n\t\t\tEnd:          time.Sunday,\n\t\t\tWeekday:      time.Tuesday,\n\t\t\tExpectedDate: \"2022-02-08T00:00:00\",\n\t\t},\n\t\t{\n\t\t\tNow:          now,\n\t\t\tStart:        time.Monday,\n\t\t\tEnd:          time.Sunday,\n\t\t\tWeekday:      time.Wednesday,\n\t\t\tExpectedDate: \"2022-02-09T00:00:00\",\n\t\t},\n\t\t{\n\t\t\tNow:          now,\n\t\t\tStart:        time.Monday,\n\t\t\tEnd:          time.Sunday,\n\t\t\tWeekday:      time.Thursday,\n\t\t\tExpectedDate: \"2022-02-10T00:00:00\",\n\t\t},\n\t\t{\n\t\t\tNow:          now,\n\t\t\tStart:        time.Monday,\n\t\t\tEnd:          time.Sunday,\n\t\t\tWeekday:      time.Friday,\n\t\t\tExpectedDate: \"2022-02-11T00:00:00\",\n\t\t},\n\t\t{\n\t\t\tNow:          now,\n\t\t\tStart:        time.Monday,\n\t\t\tEnd:          time.Sunday,\n\t\t\tWeekday:      time.Saturday,\n\t\t\tExpectedDate: \"2022-02-12T00:00:00\",\n\t\t},\n\t\t{\n\t\t\tNow:          now,\n\t\t\tStart:        time.Monday,\n\t\t\tEnd:          time.Sunday,\n\t\t\tWeekday:      time.Sunday,\n\t\t\tExpectedDate: \"2022-02-13T00:00:00\",\n\t\t},\n\t\t// Test sunday to saturday.\n\t\t{\n\t\t\tNow:          now,\n\t\t\tStart:        time.Sunday,\n\t\t\tEnd:          time.Saturday,\n\t\t\tWeekday:      time.Wednesday,\n\t\t\tExpectedDate: \"2022-02-09T00:00:00\",\n\t\t},\n\t}\n\n\tfor i := range tests {\n\t\ttt := tests[i]\n\t\tt.Run(fmt.Sprintf(\"%s[%s]\", t.Name(), tt.Weekday.String()), func(t *testing.T) {\n\t\t\tweekDate := GetWeekDate(tt.Now, tt.Weekday, tt.Start, tt.End)\n\t\t\tif got := weekDate.Format(ISO8601Layout); got != tt.ExpectedDate {\n\t\t\t\tt.Fatalf(\"[%d] expected week date: %s but got: %s \", i+1, tt.ExpectedDate, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestGetMonthDays(t *testing.T) {\n\tnow, err := time.Parse(ISO8601Layout, \"2023-12-12T00:00:00\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tdates := GetMonthDays(now)\n\texpectedDates := []string{\n\t\t\"2023-12-01 00:00:00 +0000 UTC\",\n\t\t\"2023-12-02 00:00:00 +0000 UTC\",\n\t\t\"2023-12-03 00:00:00 +0000 UTC\",\n\t\t\"2023-12-04 00:00:00 +0000 UTC\",\n\t\t\"2023-12-05 00:00:00 +0000 UTC\",\n\t\t\"2023-12-06 00:00:00 +0000 UTC\",\n\t\t\"2023-12-07 00:00:00 +0000 UTC\",\n\t\t\"2023-12-08 00:00:00 +0000 UTC\",\n\t\t\"2023-12-09 00:00:00 +0000 UTC\",\n\t\t\"2023-12-10 00:00:00 +0000 UTC\",\n\t\t\"2023-12-11 00:00:00 +0000 UTC\",\n\t\t\"2023-12-12 00:00:00 +0000 UTC\",\n\t\t\"2023-12-13 00:00:00 +0000 UTC\",\n\t\t\"2023-12-14 00:00:00 +0000 UTC\",\n\t\t\"2023-12-15 00:00:00 +0000 UTC\",\n\t\t\"2023-12-16 00:00:00 +0000 UTC\",\n\t\t\"2023-12-17 00:00:00 +0000 UTC\",\n\t\t\"2023-12-18 00:00:00 +0000 UTC\",\n\t\t\"2023-12-19 00:00:00 +0000 UTC\",\n\t\t\"2023-12-20 00:00:00 +0000 UTC\",\n\t\t\"2023-12-21 00:00:00 +0000 UTC\",\n\t\t\"2023-12-22 00:00:00 +0000 UTC\",\n\t\t\"2023-12-23 00:00:00 +0000 UTC\",\n\t\t\"2023-12-24 00:00:00 +0000 UTC\",\n\t\t\"2023-12-25 00:00:00 +0000 UTC\",\n\t\t\"2023-12-26 00:00:00 +0000 UTC\",\n\t\t\"2023-12-27 00:00:00 +0000 UTC\",\n\t\t\"2023-12-28 00:00:00 +0000 UTC\",\n\t\t\"2023-12-29 00:00:00 +0000 UTC\",\n\t\t\"2023-12-30 00:00:00 +0000 UTC\",\n\t\t\"2023-12-31 00:00:00 +0000 UTC\",\n\t}\n\n\tfor i, d := range dates {\n\t\tif expectedDates[i] != d.String() {\n\t\t\tt.Fatalf(\"expected: %s but got: %s\", expectedDates[i], d.String())\n\t\t}\n\t}\n}\n\nfunc TestGetWeekdays(t *testing.T) {\n\tvar tests = []struct {\n\t\tDate          string\n\t\tExpectedDates []string\n\t}{\n\t\t{\n\t\t\tDate: \"2021-02-04T00:00:00\",\n\t\t\tExpectedDates: []string{\n\t\t\t\t\"2021-02-01 00:00:00 +0000 UTC\",\n\t\t\t\t\"2021-02-02 00:00:00 +0000 UTC\",\n\t\t\t\t\"2021-02-03 00:00:00 +0000 UTC\",\n\t\t\t\t\"2021-02-04 00:00:00 +0000 UTC\",\n\t\t\t\t\"2021-02-05 00:00:00 +0000 UTC\",\n\t\t\t\t\"2021-02-06 00:00:00 +0000 UTC\",\n\t\t\t\t\"2021-02-07 00:00:00 +0000 UTC\",\n\t\t\t},\n\t\t},\n\t\t{ // It's monday.\n\t\t\tDate: \"2022-01-17T00:00:00\",\n\t\t\tExpectedDates: []string{\n\t\t\t\t\"2022-01-17 00:00:00 +0000 UTC\",\n\t\t\t\t\"2022-01-18 00:00:00 +0000 UTC\",\n\t\t\t\t\"2022-01-19 00:00:00 +0000 UTC\",\n\t\t\t\t\"2022-01-20 00:00:00 +0000 UTC\",\n\t\t\t\t\"2022-01-21 00:00:00 +0000 UTC\",\n\t\t\t\t\"2022-01-22 00:00:00 +0000 UTC\",\n\t\t\t\t\"2022-01-23 00:00:00 +0000 UTC\",\n\t\t\t},\n\t\t},\n\t\t{ // Test all other days by order.\n\t\t\tDate: \"2022-01-18T00:00:00\",\n\t\t\tExpectedDates: []string{\n\t\t\t\t\"2022-01-17 00:00:00 +0000 UTC\",\n\t\t\t\t\"2022-01-18 00:00:00 +0000 UTC\",\n\t\t\t\t\"2022-01-19 00:00:00 +0000 UTC\",\n\t\t\t\t\"2022-01-20 00:00:00 +0000 UTC\",\n\t\t\t\t\"2022-01-21 00:00:00 +0000 UTC\",\n\t\t\t\t\"2022-01-22 00:00:00 +0000 UTC\",\n\t\t\t\t\"2022-01-23 00:00:00 +0000 UTC\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tDate: \"2022-01-19T00:00:00\",\n\t\t\tExpectedDates: []string{\n\t\t\t\t\"2022-01-17 00:00:00 +0000 UTC\",\n\t\t\t\t\"2022-01-18 00:00:00 +0000 UTC\",\n\t\t\t\t\"2022-01-19 00:00:00 +0000 UTC\",\n\t\t\t\t\"2022-01-20 00:00:00 +0000 UTC\",\n\t\t\t\t\"2022-01-21 00:00:00 +0000 UTC\",\n\t\t\t\t\"2022-01-22 00:00:00 +0000 UTC\",\n\t\t\t\t\"2022-01-23 00:00:00 +0000 UTC\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tDate: \"2022-01-20T00:00:00\",\n\t\t\tExpectedDates: []string{\n\t\t\t\t\"2022-01-17 00:00:00 +0000 UTC\",\n\t\t\t\t\"2022-01-18 00:00:00 +0000 UTC\",\n\t\t\t\t\"2022-01-19 00:00:00 +0000 UTC\",\n\t\t\t\t\"2022-01-20 00:00:00 +0000 UTC\",\n\t\t\t\t\"2022-01-21 00:00:00 +0000 UTC\",\n\t\t\t\t\"2022-01-22 00:00:00 +0000 UTC\",\n\t\t\t\t\"2022-01-23 00:00:00 +0000 UTC\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tDate: \"2022-01-21T00:00:00\",\n\t\t\tExpectedDates: []string{\n\t\t\t\t\"2022-01-17 00:00:00 +0000 UTC\",\n\t\t\t\t\"2022-01-18 00:00:00 +0000 UTC\",\n\t\t\t\t\"2022-01-19 00:00:00 +0000 UTC\",\n\t\t\t\t\"2022-01-20 00:00:00 +0000 UTC\",\n\t\t\t\t\"2022-01-21 00:00:00 +0000 UTC\",\n\t\t\t\t\"2022-01-22 00:00:00 +0000 UTC\",\n\t\t\t\t\"2022-01-23 00:00:00 +0000 UTC\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tDate: \"2022-01-22T00:00:00\",\n\t\t\tExpectedDates: []string{\n\t\t\t\t\"2022-01-17 00:00:00 +0000 UTC\",\n\t\t\t\t\"2022-01-18 00:00:00 +0000 UTC\",\n\t\t\t\t\"2022-01-19 00:00:00 +0000 UTC\",\n\t\t\t\t\"2022-01-20 00:00:00 +0000 UTC\",\n\t\t\t\t\"2022-01-21 00:00:00 +0000 UTC\",\n\t\t\t\t\"2022-01-22 00:00:00 +0000 UTC\",\n\t\t\t\t\"2022-01-23 00:00:00 +0000 UTC\",\n\t\t\t},\n\t\t},\n\t\t{ // Sunday.\n\t\t\tDate: \"2022-01-23T00:00:00\",\n\t\t\tExpectedDates: []string{\n\t\t\t\t\"2022-01-17 00:00:00 +0000 UTC\",\n\t\t\t\t\"2022-01-18 00:00:00 +0000 UTC\",\n\t\t\t\t\"2022-01-19 00:00:00 +0000 UTC\",\n\t\t\t\t\"2022-01-20 00:00:00 +0000 UTC\",\n\t\t\t\t\"2022-01-21 00:00:00 +0000 UTC\",\n\t\t\t\t\"2022-01-22 00:00:00 +0000 UTC\",\n\t\t\t\t\"2022-01-23 00:00:00 +0000 UTC\",\n\t\t\t},\n\t\t},\n\t\t{ // Test 1st Jenuary (Saturday) .\n\t\t\tDate: \"2022-01-01T00:00:00\",\n\t\t\tExpectedDates: []string{\n\t\t\t\t\"2021-12-27 00:00:00 +0000 UTC\", // monday.\n\t\t\t\t\"2021-12-28 00:00:00 +0000 UTC\",\n\t\t\t\t\"2021-12-29 00:00:00 +0000 UTC\",\n\t\t\t\t\"2021-12-30 00:00:00 +0000 UTC\",\n\t\t\t\t\"2021-12-31 00:00:00 +0000 UTC\",\n\t\t\t\t\"2022-01-01 00:00:00 +0000 UTC\",\n\t\t\t\t\"2022-01-02 00:00:00 +0000 UTC\", // sunday.\n\t\t\t},\n\t\t},\n\t}\n\n\tfor i := range tests {\n\t\ttt := tests[i]\n\t\tt.Run(fmt.Sprintf(\"%s[%d]\", t.Name(), i+1), func(t *testing.T) {\n\t\t\tnow, err := time.Parse(ISO8601Layout, tt.Date)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\n\t\t\tdates := GetWeekdays(now, time.Monday, time.Sunday)\n\t\t\tcheckDates(t, \"\", tt.ExpectedDates, dates)\n\t\t})\n\t}\n\n\t// t.Logf(\"[%s] Current day of the week: %s\", now.String(), now.Weekday().String())\n}\n\nfunc TestBackwardsToMonday(t *testing.T) {\n\tend, err := time.Parse(ISO8601Layout, \"2021-04-05T00:00:00\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\texpected := []string{\n\t\t\"2021-04-05 00:00:00 +0000 UTC\",\n\t}\n\n\t// Test when when today is monday.\n\tdates := BackwardsToMonday(end)\n\tcheckDates(t, \"\", expected, dates)\n\n\t// Test when today is tuesday.\n\tend, err = time.Parse(ISO8601Layout, \"2021-04-06T00:00:00\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\texpected = []string{\n\t\t\"2021-04-06 00:00:00 +0000 UTC\",\n\t\t\"2021-04-05 00:00:00 +0000 UTC\",\n\t}\n\n\tdates = BackwardsToMonday(end)\n\tcheckDates(t, \"\", expected, dates)\n\n\t// Test when today is thursday.\n\tend, err = time.Parse(ISO8601Layout, \"2021-04-08T00:00:00\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\texpected = []string{\n\t\t\"2021-04-08 00:00:00 +0000 UTC\",\n\t\t\"2021-04-07 00:00:00 +0000 UTC\",\n\t\t\"2021-04-06 00:00:00 +0000 UTC\",\n\t\t\"2021-04-05 00:00:00 +0000 UTC\",\n\t}\n\n\tdates = BackwardsToMonday(end)\n\tcheckDates(t, \"\", expected, dates)\n\n\t// Test when today is sunday.\n\tend, err = time.Parse(ISO8601Layout, \"2021-04-10T00:00:00\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\texpected = []string{\n\t\t\"2021-04-10 00:00:00 +0000 UTC\",\n\t\t\"2021-04-09 00:00:00 +0000 UTC\",\n\t\t\"2021-04-08 00:00:00 +0000 UTC\",\n\t\t\"2021-04-07 00:00:00 +0000 UTC\",\n\t\t\"2021-04-06 00:00:00 +0000 UTC\",\n\t\t\"2021-04-05 00:00:00 +0000 UTC\",\n\t}\n\n\tdates = BackwardsToMonday(end)\n\tcheckDates(t, \"\", expected, dates)\n}\n\nfunc checkDates(t *testing.T, typ DateRangeType, expected []string, dates []time.Time) {\n\tt.Helper()\n\n\tt.Logf(\"[%s] length of days: %d\", typ, len(dates))\n\n\tif expectedLength, gotLength := len(expected), len(dates); expectedLength != gotLength {\n\t\tt.Logf(\"[%s] expected days length: %d but got: %d\", typ, expectedLength, gotLength)\n\n\t\tif gotLength > expectedLength {\n\t\t\tt.Logf(\"Got %d extra date(s), list of all dates we've got:\", gotLength-expectedLength)\n\t\t\tfor i, gotDate := range dates {\n\t\t\t\tt.Logf(\"[%d] %s \", i, gotDate.String())\n\t\t\t}\n\t\t}\n\n\t\tt.FailNow()\n\t}\n\n\tfor i, date := range dates {\n\t\t//\tt.Logf(\"[%d] %s\", i, date.String())\n\t\tif expected, got := expected[i], date.String(); expected != got {\n\t\t\tt.Fatalf(\"[%d] [%s] expected date: %s but got: %s\", i, typ, expected, got)\n\t\t}\n\t}\n}\n\nfunc TestBetweenAndBackwardsN(t *testing.T) {\n\tstart, err := time.Parse(ISO8601Layout, \"2021-03-26T00:00:00\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tend, err := time.Parse(ISO8601Layout, \"2021-04-01T00:00:00\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\texpected := []string{\n\t\t\"2021-03-26 00:00:00 +0000 UTC\",\n\t\t\"2021-03-27 00:00:00 +0000 UTC\",\n\t\t\"2021-03-28 00:00:00 +0000 UTC\",\n\t\t\"2021-03-29 00:00:00 +0000 UTC\",\n\t\t\"2021-03-30 00:00:00 +0000 UTC\",\n\t\t\"2021-03-31 00:00:00 +0000 UTC\",\n\t\t\"2021-04-01 00:00:00 +0000 UTC\",\n\t}\n\n\tdates := Between(start, end)\n\tcheckDates(t, \"\", expected, dates)\n\n\tdates = Backwards(DayRange, end, 7)\n\tcheckDates(t, DayRange, expected, dates)\n\n\tdates = Backwards(WeekRange, end, 1)\n\tcheckDates(t, WeekRange, expected, dates)\n\n\tdates = Backwards(MonthRange, end, 2)\n\texpectedMonthDates := []string{\n\t\t\"2021-02-01 00:00:00 +0000 UTC\",\n\t\t\"2021-02-02 00:00:00 +0000 UTC\",\n\t\t\"2021-02-03 00:00:00 +0000 UTC\",\n\t\t\"2021-02-04 00:00:00 +0000 UTC\",\n\t\t\"2021-02-05 00:00:00 +0000 UTC\",\n\t\t\"2021-02-06 00:00:00 +0000 UTC\",\n\t\t\"2021-02-07 00:00:00 +0000 UTC\",\n\t\t\"2021-02-08 00:00:00 +0000 UTC\",\n\t\t\"2021-02-09 00:00:00 +0000 UTC\",\n\t\t\"2021-02-10 00:00:00 +0000 UTC\",\n\t\t\"2021-02-11 00:00:00 +0000 UTC\",\n\t\t\"2021-02-12 00:00:00 +0000 UTC\",\n\t\t\"2021-02-13 00:00:00 +0000 UTC\",\n\t\t\"2021-02-14 00:00:00 +0000 UTC\",\n\t\t\"2021-02-15 00:00:00 +0000 UTC\",\n\t\t\"2021-02-16 00:00:00 +0000 UTC\",\n\t\t\"2021-02-17 00:00:00 +0000 UTC\",\n\t\t\"2021-02-18 00:00:00 +0000 UTC\",\n\t\t\"2021-02-19 00:00:00 +0000 UTC\",\n\t\t\"2021-02-20 00:00:00 +0000 UTC\",\n\t\t\"2021-02-21 00:00:00 +0000 UTC\",\n\t\t\"2021-02-22 00:00:00 +0000 UTC\",\n\t\t\"2021-02-23 00:00:00 +0000 UTC\",\n\t\t\"2021-02-24 00:00:00 +0000 UTC\",\n\t\t\"2021-02-25 00:00:00 +0000 UTC\",\n\t\t\"2021-02-26 00:00:00 +0000 UTC\",\n\t\t\"2021-02-27 00:00:00 +0000 UTC\",\n\t\t\"2021-02-28 00:00:00 +0000 UTC\",\n\t\t\"2021-03-01 00:00:00 +0000 UTC\",\n\t\t\"2021-03-02 00:00:00 +0000 UTC\",\n\t\t\"2021-03-03 00:00:00 +0000 UTC\",\n\t\t\"2021-03-04 00:00:00 +0000 UTC\",\n\t\t\"2021-03-05 00:00:00 +0000 UTC\",\n\t\t\"2021-03-06 00:00:00 +0000 UTC\",\n\t\t\"2021-03-07 00:00:00 +0000 UTC\",\n\t\t\"2021-03-08 00:00:00 +0000 UTC\",\n\t\t\"2021-03-09 00:00:00 +0000 UTC\",\n\t\t\"2021-03-10 00:00:00 +0000 UTC\",\n\t\t\"2021-03-11 00:00:00 +0000 UTC\",\n\t\t\"2021-03-12 00:00:00 +0000 UTC\",\n\t\t\"2021-03-13 00:00:00 +0000 UTC\",\n\t\t\"2021-03-14 00:00:00 +0000 UTC\",\n\t\t\"2021-03-15 00:00:00 +0000 UTC\",\n\t\t\"2021-03-16 00:00:00 +0000 UTC\",\n\t\t\"2021-03-17 00:00:00 +0000 UTC\",\n\t\t\"2021-03-18 00:00:00 +0000 UTC\",\n\t\t\"2021-03-19 00:00:00 +0000 UTC\",\n\t\t\"2021-03-20 00:00:00 +0000 UTC\",\n\t\t\"2021-03-21 00:00:00 +0000 UTC\",\n\t\t\"2021-03-22 00:00:00 +0000 UTC\",\n\t\t\"2021-03-23 00:00:00 +0000 UTC\",\n\t\t\"2021-03-24 00:00:00 +0000 UTC\",\n\t\t\"2021-03-25 00:00:00 +0000 UTC\",\n\t\t\"2021-03-26 00:00:00 +0000 UTC\",\n\t\t\"2021-03-27 00:00:00 +0000 UTC\",\n\t\t\"2021-03-28 00:00:00 +0000 UTC\",\n\t\t\"2021-03-29 00:00:00 +0000 UTC\",\n\t\t\"2021-03-30 00:00:00 +0000 UTC\",\n\t\t\"2021-03-31 00:00:00 +0000 UTC\",\n\t\t\"2021-04-01 00:00:00 +0000 UTC\",\n\t}\n\n\tcheckDates(t, MonthRange, expectedMonthDates, dates)\n}\n"
  }
]