Full Code of Melkeydev/go-blueprint for AI

main 81f56f8c2463 cached
163 files
350.9 KB
103.3k tokens
249 symbols
1 requests
Download .txt
Showing preview only (390K chars total). Download the full file or copy to clipboard to get everything.
Repository: Melkeydev/go-blueprint
Branch: main
Commit: 81f56f8c2463
Files: 163
Total size: 350.9 KB

Directory structure:
gitextract_afvolof3/

├── .github/
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug-report.yml
│   │   ├── config.yml
│   │   └── feature-request.yml
│   ├── pull_request_template.md
│   ├── semantic.yml
│   └── workflows/
│       ├── ci.yml
│       ├── docs.yml
│       ├── generate-linter-advanced.yml
│       ├── generate-linter-core.yml
│       ├── npm-publish.yml
│       ├── release.yml
│       ├── testcontainers.yml
│       └── update-htmx-version.yml
├── .gitignore
├── .goreleaser.yml
├── .pre-commit-config.yaml
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── cmd/
│   ├── create.go
│   ├── flags/
│   │   ├── advancedFeatures.go
│   │   ├── database.go
│   │   ├── frameworks.go
│   │   └── git.go
│   ├── program/
│   │   └── program.go
│   ├── root.go
│   ├── steps/
│   │   └── steps.go
│   ├── template/
│   │   ├── advanced/
│   │   │   ├── docker.go
│   │   │   ├── files/
│   │   │   │   ├── docker/
│   │   │   │   │   ├── docker_compose.yml.tmpl
│   │   │   │   │   └── dockerfile.tmpl
│   │   │   │   ├── htmx/
│   │   │   │   │   ├── base.templ.tmpl
│   │   │   │   │   ├── efs.go.tmpl
│   │   │   │   │   ├── hello.go.tmpl
│   │   │   │   │   ├── hello.templ.tmpl
│   │   │   │   │   ├── hello_fiber.go.tmpl
│   │   │   │   │   ├── htmx.min.js.tmpl
│   │   │   │   │   ├── imports/
│   │   │   │   │   │   ├── fiber.tmpl
│   │   │   │   │   │   ├── gin.tmpl
│   │   │   │   │   │   └── standard_library.tmpl
│   │   │   │   │   ├── routes/
│   │   │   │   │   │   ├── chi.tmpl
│   │   │   │   │   │   ├── echo.tmpl
│   │   │   │   │   │   ├── fiber.tmpl
│   │   │   │   │   │   ├── gin.tmpl
│   │   │   │   │   │   ├── gorilla.tmpl
│   │   │   │   │   │   ├── http_router.tmpl
│   │   │   │   │   │   └── standard_library.tmpl
│   │   │   │   │   └── tailwind/
│   │   │   │   │       └── tailwind.config.js.tmpl
│   │   │   │   ├── react/
│   │   │   │   │   ├── app.tsx.tmpl
│   │   │   │   │   └── tailwind/
│   │   │   │   │       ├── app.tsx.tmpl
│   │   │   │   │       ├── index.css.tmpl
│   │   │   │   │       └── vite.config.ts.tmpl
│   │   │   │   ├── tailwind/
│   │   │   │   │   ├── input.css.tmpl
│   │   │   │   │   └── output.css.tmpl
│   │   │   │   ├── websocket/
│   │   │   │   │   └── imports/
│   │   │   │   │       ├── fiber.tmpl
│   │   │   │   │       └── standard_library.tmpl
│   │   │   │   └── workflow/
│   │   │   │       └── github/
│   │   │   │           ├── github_action_goreleaser.yml.tmpl
│   │   │   │           ├── github_action_gotest.yml.tmpl
│   │   │   │           └── github_action_releaser_config.yml.tmpl
│   │   │   ├── gitHubAction.go
│   │   │   └── routes.go
│   │   ├── dbdriver/
│   │   │   ├── files/
│   │   │   │   ├── env/
│   │   │   │   │   ├── mongo.tmpl
│   │   │   │   │   ├── mysql.tmpl
│   │   │   │   │   ├── postgres.tmpl
│   │   │   │   │   ├── redis.tmpl
│   │   │   │   │   ├── scylla.tmpl
│   │   │   │   │   └── sqlite.tmpl
│   │   │   │   ├── service/
│   │   │   │   │   ├── mongo.tmpl
│   │   │   │   │   ├── mysql.tmpl
│   │   │   │   │   ├── postgres.tmpl
│   │   │   │   │   ├── redis.tmpl
│   │   │   │   │   ├── scylla.tmpl
│   │   │   │   │   └── sqlite.tmpl
│   │   │   │   └── tests/
│   │   │   │       ├── mongo.tmpl
│   │   │   │       ├── mysql.tmpl
│   │   │   │       ├── postgres.tmpl
│   │   │   │       ├── redis.tmpl
│   │   │   │       └── scylla.tmpl
│   │   │   ├── mongo.go
│   │   │   ├── mysql.go
│   │   │   ├── postgres.go
│   │   │   ├── redis.go
│   │   │   ├── scylla.go
│   │   │   └── sqlite.go
│   │   ├── docker/
│   │   │   ├── files/
│   │   │   │   └── docker-compose/
│   │   │   │       ├── mongo.tmpl
│   │   │   │       ├── mysql.tmpl
│   │   │   │       ├── postgres.tmpl
│   │   │   │       ├── redis.tmpl
│   │   │   │       └── scylla.tmpl
│   │   │   ├── mongo.go
│   │   │   ├── mysql.go
│   │   │   ├── postgres.go
│   │   │   ├── redis.go
│   │   │   └── scylla.go
│   │   ├── framework/
│   │   │   ├── chiRoutes.go
│   │   │   ├── echoRoutes.go
│   │   │   ├── fiberServer.go
│   │   │   ├── files/
│   │   │   │   ├── README.md.tmpl
│   │   │   │   ├── air.toml.tmpl
│   │   │   │   ├── gitignore.tmpl
│   │   │   │   ├── globalenv.tmpl
│   │   │   │   ├── main/
│   │   │   │   │   ├── fiber_main.go.tmpl
│   │   │   │   │   └── main.go.tmpl
│   │   │   │   ├── makefile.tmpl
│   │   │   │   ├── routes/
│   │   │   │   │   ├── chi.go.tmpl
│   │   │   │   │   ├── echo.go.tmpl
│   │   │   │   │   ├── fiber.go.tmpl
│   │   │   │   │   ├── gin.go.tmpl
│   │   │   │   │   ├── gorilla.go.tmpl
│   │   │   │   │   ├── http_router.go.tmpl
│   │   │   │   │   └── standard_library.go.tmpl
│   │   │   │   ├── server/
│   │   │   │   │   ├── fiber.go.tmpl
│   │   │   │   │   └── standard_library.go.tmpl
│   │   │   │   └── tests/
│   │   │   │       ├── default-test.go.tmpl
│   │   │   │       ├── echo-test.go.tmpl
│   │   │   │       ├── fiber-test.go.tmpl
│   │   │   │       └── gin-test.go.tmpl
│   │   │   ├── ginRoutes.go
│   │   │   ├── gorillaRoutes.go
│   │   │   ├── httpRoutes.go
│   │   │   ├── main.go
│   │   │   └── routerRoutes.go
│   │   └── globalEnv.go
│   ├── ui/
│   │   ├── multiInput/
│   │   │   └── multiInput.go
│   │   ├── multiSelect/
│   │   │   └── multiSelect.go
│   │   ├── spinner/
│   │   │   └── spinner.go
│   │   └── textinput/
│   │       ├── textinput.go
│   │       └── textinput_test.go
│   ├── utils/
│   │   ├── utils.go
│   │   └── utils_test.go
│   └── version.go
├── contributors.yml
├── docs/
│   ├── Makefile
│   ├── custom_theme/
│   │   └── main.html
│   ├── docs/
│   │   ├── advanced-flag/
│   │   │   ├── advanced-flag.md
│   │   │   ├── docker.md
│   │   │   ├── goreleaser.md
│   │   │   ├── htmx-templ.md
│   │   │   ├── react-vite.md
│   │   │   ├── tailwind.md
│   │   │   └── websocket.md
│   │   ├── blueprint-core/
│   │   │   ├── db-drivers.md
│   │   │   └── frameworks.md
│   │   ├── blueprint-ui.md
│   │   ├── creating-project/
│   │   │   ├── air.md
│   │   │   ├── makefile.md
│   │   │   └── project-init.md
│   │   ├── endpoints-test/
│   │   │   ├── mongo.md
│   │   │   ├── redis.md
│   │   │   ├── scylladb.md
│   │   │   ├── server.md
│   │   │   ├── sql.md
│   │   │   ├── web.md
│   │   │   └── websocket.md
│   │   ├── index.md
│   │   └── installation.md
│   ├── mkdocs.yml
│   └── requirements.txt
├── go.mod
├── go.sum
├── main.go
└── scripts/
    ├── completions.sh
    └── create-npm-packages.sh

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms

github: [melkeydev]


================================================
FILE: .github/ISSUE_TEMPLATE/bug-report.yml
================================================
name: Bug Report
description: Found a bug? Please let us know!
title: "[Bug]"
labels: ["Bug"]
body:
  - type: markdown
    attributes:
      value: |
        Please provide as much information as possible. This will help us resolve the Bug quickly and accurately.
  - type: markdown
    attributes:
      value: ---
  - type: textarea
    id: what-happened
    attributes:
      label: What is the problem?
      description: |
        Description of what needs to be fixed.
      placeholder: Tell us what you see!
    validations:
      required: true
  - type: input
    id: os
    attributes:
      label: Operating System
      description: What is the affected operating system?
    validations:
      required: true
  - type: input
    id: arch
    attributes:
      label: Architecture Version (x86, x64, arm, etc)
      description: (x86, x64, arm, etc)
    validations:
      required: true
  - type: textarea
    id: reproduce
    attributes:
      label: Steps to reproduce
      description: |
        This includes the steps for reproducing the problem, the expected result, and the actual result.
      placeholder: |
        1. ...
        2. ...
        3. ...
    validations:
      required: true
  - type: textarea
    id: logs
    attributes:
      label: Relevant log output
      description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
      render: shell


================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: true
contact_links:
  - name: Melkeydev Discord
    url: https://discord.gg/HHZMSCu
    about: Chat with the community.

================================================
FILE: .github/ISSUE_TEMPLATE/feature-request.yml
================================================
name: Feature Request
description: Suggest a new idea for go-blueprint.
title: "[Feature Request] "
labels: ["enhancement"]
body:
  - type: markdown
    attributes:
      value: |
        Please provide as much information as possible by filling out form below.
  - type: markdown
    attributes:
      value: ---
  - type: textarea
    id: idea
    attributes:
      label: Tell us about your feature request
      description: |
        Describe what you would like go-blueprint to be able to do.
    validations:
      required: true
  - type: checkboxes
    id: disclaimer
    attributes:
      label: Disclaimer
      description: I have verified that this has not been suggested before.
      options:
        - label: I agree
          required: true


================================================
FILE: .github/pull_request_template.md
================================================
By submitting this pull request, I confirm that my contribution is made under the terms of the MIT license.

## Problem/Feature

Please include a description of the problem or feature this PR is addressing.

## Description of Changes: 

- Item 1
- Item 2

## Checklist

- [ ] I have self-reviewed the changes being requested
- [ ] I have updated the documentation (check issue ticket #218)


================================================
FILE: .github/semantic.yml
================================================
titleOnly: true

================================================
FILE: .github/workflows/ci.yml
================================================
name: continuous integration

on:
  push:
    paths:
      - '**.go'
      - go.sum
      - go.mod
    branches-ignore:
      - main
  pull_request:

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Go
        uses: actions/setup-go@v5
        with:
          go-version: '1.23.x'

      - name: Deps cache
        id: cache-go-deps
        uses: actions/cache@v4
        env:
          cache-name: go-deps-cache
        with:
          path: ~/godeps
          key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/go.sum') }}
          restore-keys: |
            ${{ runner.os }}-build-${{ env.cache-name }}-

      - if: ${{ steps.cache-go-deps.outputs.cache-hit != 'true' }}
        name: List the state of go modules
        continue-on-error: true
        run: go mod graph

      - name: Install dependencies
        run: |
          go mod tidy
          go mod download
          go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.63.4

      - name: Run golangci-lint
        run: golangci-lint run

      - name: Run tests
        run: |
          go test ./...



================================================
FILE: .github/workflows/docs.yml
================================================
name: Deploy Docs

on:
  push:
    branches:
      - main

jobs:
  build-deploy:
    name: Build and deploy docs

    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5

      - name: Cache dependencies
        uses: actions/cache@v4
        with:
          path: ~/.cache/pip
          key: ${{ runner.os }}-pip-${{ hashFiles('docs/requirements.txt') }}
          restore-keys: |
            ${{ runner.os }}-pip-
            ${{ runner.os }}-

      - name: Install dependencies
        working-directory: docs
        run: make install

      - name: Build and deploy to GitHub Pages
        working-directory: docs
        run: make deploy

      # config for custom domain on gh-pages
      # - name: Create and push CNAME file to gh-pages
      #   run: |
      #     git config --local user.email "actions@github.com"
      #     git config --local user.name "GitHub Actions"
      #     git checkout gh-pages

      #     echo "<domain>" > CNAME

      #     git add CNAME
      #     git commit -m "Add CNAME file"
      #     git push origin gh-pages


================================================
FILE: .github/workflows/generate-linter-advanced.yml
================================================
name: Linting Generated Blueprints Advanced

on:
  pull_request: {}
  workflow_dispatch: {}

jobs:
  framework_matrix:
    strategy:
      matrix:
        framework: [chi, gin, fiber, gorilla/mux, httprouter, standard-library, echo]
        driver: [postgres]
        git: [commit]
        advanced: [htmx, githubaction, websocket, tailwind, docker, react]

    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Go
        uses: actions/setup-go@v5
        with:
          go-version: '1.23.x'

      - name: Install golangci-lint
        run: go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.63.4

      - name: Commit report
        run: |
          git config --global user.name 'testname'
          git config --global user.email 'testemail@users.noreply.github.com'

      - name: Set framework variable
        id: set-proejct-directory
        run: echo "PROJECT_DIRECTORY=${{ matrix.framework }}" | sed 's/\//-/g' >> $GITHUB_ENV

      - name: build templates
        run: script -q /dev/null -c "go run main.go create -n ${{ env.PROJECT_DIRECTORY }} -f ${{ matrix.framework}} -d ${{ matrix.driver }} -g ${{ matrix.git}} --advanced --feature ${{ matrix.advanced }}"

      - if: ${{ matrix.advanced == 'htmx' || matrix.advanced == 'tailwind' }}
        name: Install Templ & gen templates
        run: | 
          go install github.com/a-h/templ/cmd/templ@latest
          /home/runner/go/bin/templ generate -path ${{ env.PROJECT_DIRECTORY }}

      - name: golangci-lint
        run: | 
          cd ${{ env.PROJECT_DIRECTORY }}
          golangci-lint run

      - name: remove templates
        run: rm -rf ${{ env.PROJECT_DIRECTORY }}


================================================
FILE: .github/workflows/generate-linter-core.yml
================================================
name: Linting Generated Blueprints Core

on:
  pull_request: {}
  workflow_dispatch: {}

jobs:
  framework_matrix:
    strategy:
      matrix:
        framework: [chi, gin, fiber, gorilla/mux, httprouter, standard-library, echo]
        driver: [mysql, postgres, sqlite, mongo, redis, scylla, none]
        git: [commit, stage, skip]

    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Go
        uses: actions/setup-go@v5
        with:
          go-version: '1.23.x'

      - name: Install golangci-lint
        run: go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.63.4

      - name: Commit report
        run: |
          git config --global user.name 'testname'
          git config --global user.email 'testemail@users.noreply.github.com'

      - name: Set framework variable
        id: set-proejct-directory
        run: echo "PROJECT_DIRECTORY=${{ matrix.framework }}" | sed 's/\//-/g' >> $GITHUB_ENV

      - name: build templates
        run: script -q /dev/null -c "go run main.go create -n ${{ env.PROJECT_DIRECTORY }} -f ${{ matrix.framework}} -d ${{ matrix.driver }} -g ${{ matrix.git}}"

      - name: golangci-lint
        run: | 
          cd ${{ env.PROJECT_DIRECTORY }}
          golangci-lint run

      - name: remove templates
        run: rm -rf ${{ env.PROJECT_DIRECTORY }}


================================================
FILE: .github/workflows/npm-publish.yml
================================================
name: npm-publish

on:
  workflow_call:
    inputs:
      tag:
        description: "Release tag to publish (e.g., v1.0.0)"
        required: true
        type: string
  workflow_dispatch:
    inputs:
      tag:
        description: "Release tag to publish (e.g., v1.0.0)"
        required: true
        type: string

jobs:
  npm-publish:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      id-token: write
    env:
      NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
    steps:
      - uses: actions/checkout@v4

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 22
          registry-url: "https://registry.npmjs.org"

      - name: Download release assets
        run: |
          TAG="${{ inputs.tag }}"
          VERSION=${TAG#v}
          mkdir -p dist
          gh release download "$TAG" --dir dist
          echo "VERSION=$VERSION" >> $GITHUB_ENV
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      - name: Create npm packages
        run: |
          chmod +x ./scripts/create-npm-packages.sh
          ./scripts/create-npm-packages.sh "$VERSION"

      - name: Publish platform-specific packages to npm
        run: |
          for platform_dir in platform-packages/go-blueprint-*; do
            if [ -d "$platform_dir" ]; then
              cd "$platform_dir"
              npm publish --provenance --access public
              cd - > /dev/null
            fi
          done

      - name: Publish main package to npm
        run: |
          cd npm-package
          npm publish --provenance --access public



================================================
FILE: .github/workflows/release.yml
================================================
name: goreleaser

on:
  push:
    tags:
      - "v*.*.*"

permissions:
  contents: write

jobs:
  goreleaser:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Set up Go
        uses: actions/setup-go@v4
        with:
          go-version: "1.21.1"
      - name: Run GoReleaser
        uses: goreleaser/goreleaser-action@v5.0.0
        with:
          distribution: goreleaser
          version: ${{ env.GITHUB_REF_NAME }}
          args: release --clean
          workdir: ./
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

  npm-publish:
    needs: goreleaser
    permissions:
      contents: read
      id-token: write
    uses: ./.github/workflows/npm-publish.yml
    with:
      tag: ${{ github.ref_name }}
    secrets: inherit



================================================
FILE: .github/workflows/testcontainers.yml
================================================
name: Integrations Test for the Generated Blueprints

on:
  pull_request: {}
  workflow_dispatch: {}

jobs:
  itests_matrix:
    strategy:
      matrix:
        driver:
          [mysql, postgres, mongo, redis, scylla]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Setup Go
        uses: actions/setup-go@v5
        with:
          go-version: '1.23.x'

      - name: Commit report
        run: |
          git config --global user.name 'testname'
          git config --global user.email 'testemail@users.noreply.github.com'

      - name: build ${{ matrix.driver }} template
        run: script -q /dev/null -c "go run main.go create -n ${{ matrix.driver }} -g commit -f fiber -d ${{matrix.driver}}"

      - name: run ${{ matrix.driver }} integration tests
        working-directory: ${{ matrix.driver }}
        run: make itest

      - name: remove ${{ matrix.driver }} template
        run: rm -rf ${{ matrix.driver }}


================================================
FILE: .github/workflows/update-htmx-version.yml
================================================
name: Check for new htmx release

on:
  schedule:
    - cron: '0 0 * * Sun'

jobs:
  check_release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Get version from file
        id: get_version_file
        run: |
          VERSION_FILE=$(curl -s https://raw.githubusercontent.com/Melkeydev/go-blueprint/main/cmd/template/advanced/files/htmx/htmx.min.js.tmpl | grep version | awk -F'"' '{print "v" $2}')
          echo "version file: $VERSION_FILE"
          if [[ "$VERSION_FILE" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
            echo "version_file=$VERSION_FILE" >> $GITHUB_OUTPUT
          else
            echo "Invalid VERSION_FILE format: $VERSION_FILE" >&2
            exit 1
          fi

      - name: Get version from GitHub API
        id: get_version_api
        run: |
          VERSION_API=$(curl -s https://api.github.com/repos/bigskysoftware/htmx/releases/latest | jq -r '.tag_name')
          echo "version api: $VERSION_API"
          if [[ "$VERSION_API" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
            echo "version_api=$VERSION_API" >> $GITHUB_OUTPUT
          else
            echo "Invalid VERSION_API format: $VERSION_API" >&2
            exit 1
          fi

      - name: Compare versions
        id: compare_versions
        run: |
          if [ "${{ steps.get_version_api.outputs.version_api }}" != "${{ steps.get_version_file.outputs.version_file }}" ]; then
            echo "release_changed=true" >> $GITHUB_OUTPUT
            echo "Release changed: true"
          else
            echo "release_changed=false" >> $GITHUB_OUTPUT
            echo "Release changed: false"
          fi

      - name: dump latest htmx version
        if: steps.compare_versions.outputs.release_changed == 'true'
        run: curl -L https://github.com/bigskysoftware/htmx/releases/latest/download/htmx.min.js -o cmd/template/advanced/files/htmx/htmx.min.js

      - name: Prettify code
        if: steps.compare_versions.outputs.release_changed == 'true'
        run: |
          npm install --save-dev --save-exact prettier
          npx prettier --write cmd/template/advanced/files/htmx/htmx.min.js
          rm -rf node_modules
          rm package-lock.json
          rm package.json

      - name: Create tmpl after Prettify
        if: steps.compare_versions.outputs.release_changed == 'true'
        run: mv cmd/template/advanced/files/htmx/htmx.min.js cmd/template/advanced/files/htmx/htmx.min.js.tmpl

      - name: Create Pull Request
        if: steps.compare_versions.outputs.release_changed == 'true'
        uses: peter-evans/create-pull-request@v6
        with:
          commit-message: update htmx version ${{ steps.get_version_api.outputs.version_api }}
          title: Update htmx to version ${{ steps.get_version_api.outputs.version_api }} [Bot]
          body: New htmx ${{ steps.get_version_api.outputs.version_api }} version is available. This is an automatic PR to update changes.
          branch: htmx-version-update
          base: main


================================================
FILE: .gitignore
================================================
go-blueprint
site


================================================
FILE: .goreleaser.yml
================================================
before:
  hooks:
    - go mod tidy
    - ./scripts/completions.sh
builds:
  - binary: go-blueprint
    main: ./
    goos:
      - darwin
      - linux
      - windows
    goarch:
      - amd64
      - arm64
    env:
      - CGO_ENABLED=0
    ldflags:
      - -s -w -X github.com/melkeydev/go-blueprint/cmd.GoBlueprintVersion={{.Version}}

release:
  prerelease: auto

universal_binaries:
  - replace: true
archives:
  - name_template: >-
      {{- .ProjectName }}_ {{- .Version }}_ {{- title .Os }}_ {{- if eq .Arch "amd64" }}x86_64 {{- else if eq .Arch "386" }}i386 {{- else }}{{ .Arch }}{{ end }} {{- if .Arm }}v{{ .Arm }}{{ end -}}
    format_overrides:
      - goos: windows
        format: zip
    builds_info:
      group: root
      owner: root
    files:
      - README.md
      - LICENSE
      - completions/*

checksum:
  name_template: "checksums.txt"


================================================
FILE: .pre-commit-config.yaml
================================================
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v3.2.0
    hooks:
        - id: trailing-whitespace
        - id: end-of-file-fixer
        - id: check-added-large-files
  - repo: https://github.com/golangci/golangci-lint
    rev: v1.55.2
    hooks:
      - id: golangci-lint


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing

Thanks for helping make go-blueprint better! 
- [Contributing](#contributing)
  - [Design Principles](#design-principles)
  - [Report an Issue](#report-an-issue)
  - [Contributing Code with Pull Requests](#contributing-code-with-pull-requests)
    - [Requirements](#requirements)
  - [Licensing](#licensing)

## Design Principles

Contributions to go-blueprint should align with the project’s design principles:

 * Maintain backwards compatibility whenever possible.

## Report an Issue

If you have run into a bug or want to discuss a new feature, please [file an issue](https://github.com/Melkeydev/go-blueprint/issues).

## Contributing Code with Pull Requests

go-blueprint uses [Github pull requests](https://github.com/Melkeydev/go-blueprint/pulls). Feel free to fork, hack away at your changes and submit.

### Requirements

 *  All commands and functionality should be documented appropriately
 *  All new functionality/features should have appropriate unit testing 

go-blueprint strives to have a consistent set of documentation that matches the command structure and any new functionality must have accompanying documentation in the PR.

## Licensing

See the [LICENSE](https://github.com/melkeydev/go-blueprint/blob/main/LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution.


================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2023 Melkeydev

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
![logo](./public/logo.png)

<div style="text-align: center;">
  <h1>
    Introducing the Ultimate Golang Blueprint Library
  </h1>
</div>

Go Blueprint is a CLI tool that allows users to spin up a Go project with the corresponding structure seamlessly. It also
gives the option to integrate with one of the more popular Go frameworks (and the list is growing with new features)!

### Why Would I use this?

- Easy to set up and install
- Have the entire Go structure already established
- Setting up a Go HTTP server (or Fasthttp with Fiber)
- Integrate with a popular frameworks
- Focus on the actual code of your application

## Table of Contents

- [Install](#install)
- [Frameworks Supported](#frameworks-supported)
- [Database Support](#database-support)
- [Advanced Features](#advanced-features)
- [Blueprint UI](#blueprint-ui)
- [Usage Example](#usage-example)
- [GitHub Stats](#github-stats)
- [License](#license)

<a id="install"></a>

<h2>
  <picture>
    <img src="./public/install.gif?raw=true" width="60px" style="margin-right: 1px;">
  </picture>
  Install
</h2>

### Go Install
```bash
go install github.com/melkeydev/go-blueprint@latest
```

This installs a go binary that will automatically bind to your $GOPATH

> if you’re using Zsh, you’ll need to add it manually to `~/.zshrc`.

```bash
GOPATH=$HOME/go  PATH=$PATH:/usr/local/go/bin:$GOPATH/bin
```

don't forget to update

```bash
source ~/.zshrc
```

### NPM Install
```bash
npm install -g @melkeydev/go-blueprint
```

### Homebrew Install
```bash
brew install go-blueprint
```

Then in a new terminal run:

```bash
go-blueprint create
```

You can also use the provided flags to set up a project without interacting with the UI.

```bash
go-blueprint create --name my-project --framework gin --driver postgres --git commit
```

See `go-blueprint create -h` for all the options and shorthands.

<a id="frameworks-supported"></a>

<h2>
  <picture>
    <img src="./public/frameworks.gif?raw=true" width="60px" style="margin-right: 1px;">
  </picture>
  Frameworks Supported
</h2>

- [Chi](https://github.com/go-chi/chi)
- [Gin](https://github.com/gin-gonic/gin)
- [Fiber](https://github.com/gofiber/fiber)
- [HttpRouter](https://github.com/julienschmidt/httprouter)
- [Gorilla/mux](https://github.com/gorilla/mux)
- [Echo](https://github.com/labstack/echo)

<a id="database-support"></a>

<h2>
  <picture>
    <img src="./public/database.gif?raw=true" width="45px" style="margin-right: 15px;">
  </picture>
  Database Support
</h2>

Go Blueprint now offers enhanced database support, allowing you to choose your preferred database driver during project setup. Use the `--driver` or `-d` flag to specify the database driver you want to integrate into your project.

### Supported Database Drivers

Choose from a variety of supported database drivers:

- [Mysql](https://github.com/go-sql-driver/mysql)
- [Postgres](https://github.com/jackc/pgx/)
- [Sqlite](https://github.com/mattn/go-sqlite3)
- [Mongo](https://go.mongodb.org/mongo-driver)
- [Redis](https://github.com/redis/go-redis)
- [ScyllaDB GoCQL](https://github.com/scylladb/gocql)

<a id="advanced-features"></a>

<h2>
  <picture>
    <img src="./public/advanced.gif?raw=true" width="70px" style="margin-right: 1px;">
  </picture>
  Advanced Features
</h2>

Blueprint is focused on being as minimalistic as possible. That being said, we wanted to offer the ability to add other features people may want without bloating the overall experience.

You can now use the `--advanced` flag when running the `create` command to get access to the following features. This is a multi-option prompt; one or more features can be used at the same time:

- [HTMX](https://htmx.org/) support using [Templ](https://templ.guide/)
- CI/CD workflow setup using [Github Actions](https://docs.github.com/en/actions)
- [Websocket](https://pkg.go.dev/github.com/coder/websocket) sets up a websocket endpoint
- [Tailwind](https://tailwindcss.com/) css framework
- Docker configuration for go project
- [React](https://react.dev/) frontend written in TypeScript, including an example fetch request to the backend

Note: Selecting Tailwind option will automatically select HTMX unless React is explicitly selected

<a id="blueprint-ui"></a>

<h2>
  <picture>
    <img src="./public/ui.gif?raw=true" width="100px" style="margin-right: 1px;">
  </picture>
  Blueprint UI
</h2>

Blueprint UI is a web application that allows you to create commands for the CLI and preview the structure of your project. You will be able to see directories and files that will be created upon command execution. Check it out at [go-blueprint.dev](https://go-blueprint.dev)

<a id="usage-example"></a>

<h2>
  <picture>
    <img src="./public/example.gif?raw=true" width="60px" style="margin-right: 1px;">
  </picture>
  Usage Example
</h2>

Here's an example of setting up a project with a specific database driver:

```bash
go-blueprint create --name my-project --framework gin --driver postgres --git commit
```

<p align="center">
  <img src="./public/blueprint_1.png" alt="Starter Image" width="800"/>
</p>

Advanced features are accessible with the --advanced flag

```bash
go-blueprint create --advanced
```

Advanced features can be enabled using the `--feature` flag along with the `--advanced` flag.

HTMX:

```bash
go-blueprint create --advanced --feature htmx
```

CI/CD workflow:

```bash
go-blueprint create --advanced --feature githubaction
```

Websocket:

```bash
go-blueprint create --advanced --feature websocket
```

Tailwind:

```bash
go-blueprint create --advanced --feature tailwind
```

Docker:

```bash
go-blueprint create --advanced --feature docker
```

React:

```bash
go-blueprint create --advanced --feature react
```

Or all features at once:

```bash
go-blueprint create --name my-project --framework chi --driver mysql --advanced --feature htmx --feature githubaction --feature websocket --feature tailwind --feature docker --git commit --feature react
```

<p align="center">
  <img src="./public/blueprint_advanced.png" alt="Advanced Options" width="800"/>
</p>

**Visit [documentation](https://docs.go-blueprint.dev) to learn more about blueprint and its features.**

<a id="github-stats"></a>

<h2>
  <picture>
    <img src="./public/stats.gif?raw=true" width="45px" style="margin-right: 10px;">
  </picture>
  GitHub Stats
</h2>

<p align="center">
  <img alt="Alt" src="https://repobeats.axiom.co/api/embed/7c4be18864d441f961be61186ce49b5471a9e7bf.svg" title="Repobeats analytics image"/>
</p>

<a id="license"></a>

<h2>
  <picture>
    <img src="./public/license.gif?raw=true" width="50px" style="margin-right: 1px;">
  </picture>
  License
</h2>

Licensed under [MIT License](./LICENSE)


================================================
FILE: cmd/create.go
================================================
package cmd

import (
	"fmt"
	"log"
	"os"
	"strings"
	"sync"

	tea "github.com/charmbracelet/bubbletea"
	"github.com/charmbracelet/lipgloss"
	"github.com/melkeydev/go-blueprint/cmd/flags"
	"github.com/melkeydev/go-blueprint/cmd/program"
	"github.com/melkeydev/go-blueprint/cmd/steps"
	"github.com/melkeydev/go-blueprint/cmd/ui/multiInput"
	"github.com/melkeydev/go-blueprint/cmd/ui/multiSelect"
	"github.com/melkeydev/go-blueprint/cmd/ui/spinner"
	"github.com/melkeydev/go-blueprint/cmd/ui/textinput"
	"github.com/melkeydev/go-blueprint/cmd/utils"
	"github.com/spf13/cobra"
)

const logo = `

 ____  _                       _       _   
|  _ \| |                     (_)     | |  
| |_) | |_   _  ___ _ __  _ __ _ _ __ | |_ 
|  _ <| | | | |/ _ \ '_ \| '__| | '_ \| __|
| |_) | | |_| |  __/ |_) | |  | | | | | |_ 
|____/|_|\__,_|\___| .__/|_|  |_|_| |_|\__|
				   | |                     
				   |_|                     

`

var (
	logoStyle      = lipgloss.NewStyle().Foreground(lipgloss.Color("#01FAC6")).Bold(true)
	tipMsgStyle    = lipgloss.NewStyle().PaddingLeft(1).Foreground(lipgloss.Color("190")).Italic(true)
	endingMsgStyle = lipgloss.NewStyle().PaddingLeft(1).Foreground(lipgloss.Color("170")).Bold(true)
)

func init() {
	var flagFramework flags.Framework
	var flagDBDriver flags.Database
	var advancedFeatures flags.AdvancedFeatures
	var flagGit flags.Git
	rootCmd.AddCommand(createCmd)

	createCmd.Flags().StringP("name", "n", "", "Name of project to create")
	createCmd.Flags().VarP(&flagFramework, "framework", "f", fmt.Sprintf("Framework to use. Allowed values: %s", strings.Join(flags.AllowedProjectTypes, ", ")))
	createCmd.Flags().VarP(&flagDBDriver, "driver", "d", fmt.Sprintf("Database drivers to use. Allowed values: %s", strings.Join(flags.AllowedDBDrivers, ", ")))
	createCmd.Flags().BoolP("advanced", "a", false, "Get prompts for advanced features")
	createCmd.Flags().Var(&advancedFeatures, "feature", fmt.Sprintf("Advanced feature to use. Allowed values: %s", strings.Join(flags.AllowedAdvancedFeatures, ", ")))
	createCmd.Flags().VarP(&flagGit, "git", "g", fmt.Sprintf("Git to use. Allowed values: %s", strings.Join(flags.AllowedGitsOptions, ", ")))

	utils.RegisterStaticCompletions(createCmd, "framework", flags.AllowedProjectTypes)
	utils.RegisterStaticCompletions(createCmd, "driver", flags.AllowedDBDrivers)
	utils.RegisterStaticCompletions(createCmd, "feature", flags.AllowedAdvancedFeatures)
	utils.RegisterStaticCompletions(createCmd, "git", flags.AllowedGitsOptions)
}

type Options struct {
	ProjectName *textinput.Output
	ProjectType *multiInput.Selection
	DBDriver    *multiInput.Selection
	Advanced    *multiSelect.Selection
	Workflow    *multiInput.Selection
	Git         *multiInput.Selection
}

// createCmd defines the "create" command for the CLI
var createCmd = &cobra.Command{
	Use:   "create",
	Short: "Create a Go project and don't worry about the structure",
	Long:  "Go Blueprint is a CLI tool that allows you to focus on the actual Go code, and not the project structure. Perfect for someone new to the Go language",

	Run: func(cmd *cobra.Command, args []string) {
		var tprogram *tea.Program
		var err error

		isInteractive := false
		flagName := cmd.Flag("name").Value.String()

		if flagName != "" && !utils.ValidateModuleName(flagName) {
			err = fmt.Errorf("'%s' is not a valid module name. Please choose a different name", flagName)
			cobra.CheckErr(textinput.CreateErrorInputModel(err).Err())
		}

		rootDirName := utils.GetRootDir(flagName)
		if rootDirName != "" && doesDirectoryExistAndIsNotEmpty(rootDirName) {
			err = fmt.Errorf("directory '%s' already exists and is not empty. Please choose a different name", rootDirName)
			cobra.CheckErr(textinput.CreateErrorInputModel(err).Err())
		}

		// VarP already validates the contents of the framework flag.
		// If this flag is filled, it is always valid
		flagFramework := flags.Framework(cmd.Flag("framework").Value.String())
		flagDBDriver := flags.Database(cmd.Flag("driver").Value.String())
		flagGit := flags.Git(cmd.Flag("git").Value.String())

		options := Options{
			ProjectName: &textinput.Output{},
			ProjectType: &multiInput.Selection{},
			DBDriver:    &multiInput.Selection{},
			Advanced: &multiSelect.Selection{
				Choices: make(map[string]bool),
			},
			Git: &multiInput.Selection{},
		}

		project := &program.Project{
			ProjectName:     flagName,
			ProjectType:     flagFramework,
			DBDriver:        flagDBDriver,
			FrameworkMap:    make(map[flags.Framework]program.Framework),
			DBDriverMap:     make(map[flags.Database]program.Driver),
			AdvancedOptions: make(map[string]bool),
			GitOptions:      flagGit,
		}

		steps := steps.InitSteps(flagFramework, flagDBDriver)
		fmt.Printf("%s\n", logoStyle.Render(logo))

		// Advanced option steps:
		flagAdvanced, err := cmd.Flags().GetBool("advanced")
		if err != nil {
			log.Fatal("failed to retrieve advanced flag")
		}

		if flagAdvanced {
			fmt.Println(tipMsgStyle.Render("*** You are in advanced mode ***\n\n"))
		}

		if project.ProjectName == "" {
			isInteractive = true
			tprogram := tea.NewProgram(textinput.InitialTextInputModel(options.ProjectName, "What is the name of your project?", project))
			if _, err := tprogram.Run(); err != nil {
				log.Printf("Name of project contains an error: %v", err)
				cobra.CheckErr(textinput.CreateErrorInputModel(err).Err())
			}

			if options.ProjectName.Output != "" && !utils.ValidateModuleName(options.ProjectName.Output) {
				err = fmt.Errorf("'%s' is not a valid module name. Please choose a different name", options.ProjectName.Output)
				cobra.CheckErr(textinput.CreateErrorInputModel(err).Err())
			}

			rootDirName = utils.GetRootDir(options.ProjectName.Output)
			if doesDirectoryExistAndIsNotEmpty(rootDirName) {
				err = fmt.Errorf("directory '%s' already exists and is not empty. Please choose a different name", rootDirName)
				cobra.CheckErr(textinput.CreateErrorInputModel(err).Err())
			}
			project.ExitCLI(tprogram)

			project.ProjectName = options.ProjectName.Output
			err := cmd.Flag("name").Value.Set(project.ProjectName)
			if err != nil {
				log.Fatal("failed to set the name flag value", err)
			}
		}

		if project.ProjectType == "" {
			isInteractive = true
			step := steps.Steps["framework"]
			tprogram = tea.NewProgram(multiInput.InitialModelMulti(step.Options, options.ProjectType, step.Headers, project))
			if _, err := tprogram.Run(); err != nil {
				cobra.CheckErr(textinput.CreateErrorInputModel(err).Err())
			}
			project.ExitCLI(tprogram)

			step.Field = options.ProjectType.Choice

			// this type casting is always safe since the user interface can
			// only pass strings that can be cast to a flags.Framework instance
			project.ProjectType = flags.Framework(strings.ToLower(options.ProjectType.Choice))
			err := cmd.Flag("framework").Value.Set(project.ProjectType.String())
			if err != nil {
				log.Fatal("failed to set the framework flag value", err)
			}
		}

		if project.DBDriver == "" {
			isInteractive = true
			step := steps.Steps["driver"]
			tprogram = tea.NewProgram(multiInput.InitialModelMulti(step.Options, options.DBDriver, step.Headers, project))
			if _, err := tprogram.Run(); err != nil {
				cobra.CheckErr(textinput.CreateErrorInputModel(err).Err())
			}
			project.ExitCLI(tprogram)

			// this type casting is always safe since the user interface can
			// only pass strings that can be cast to a flags.Database instance
			project.DBDriver = flags.Database(strings.ToLower(options.DBDriver.Choice))
			err := cmd.Flag("driver").Value.Set(project.DBDriver.String())
			if err != nil {
				log.Fatal("failed to set the driver flag value", err)
			}
		}

		if flagAdvanced {

			featureFlags := cmd.Flag("feature").Value.String()

			if featureFlags != "" {
				featuresFlagValues := strings.Split(featureFlags, ",")
				for _, key := range featuresFlagValues {
					project.AdvancedOptions[key] = true
				}
			} else {
				isInteractive = true
				step := steps.Steps["advanced"]
				tprogram = tea.NewProgram((multiSelect.InitialModelMultiSelect(step.Options, options.Advanced, step.Headers, project)))
				if _, err := tprogram.Run(); err != nil {
					cobra.CheckErr(textinput.CreateErrorInputModel(err).Err())
				}
				project.ExitCLI(tprogram)
				for key, opt := range options.Advanced.Choices {
					project.AdvancedOptions[strings.ToLower(key)] = opt
					err := cmd.Flag("feature").Value.Set(strings.ToLower(key))
					if err != nil {
						log.Fatal("failed to set the feature flag value", err)
					}
				}
				if err != nil {
					log.Fatal("failed to set the htmx option", err)
				}
			}

		}

		if project.GitOptions == "" {
			isInteractive = true
			step := steps.Steps["git"]
			tprogram = tea.NewProgram(multiInput.InitialModelMulti(step.Options, options.Git, step.Headers, project))
			if _, err := tprogram.Run(); err != nil {
				cobra.CheckErr(textinput.CreateErrorInputModel(err).Err())
			}
			project.ExitCLI(tprogram)

			project.GitOptions = flags.Git(strings.ToLower(options.Git.Choice))
			err := cmd.Flag("git").Value.Set(project.GitOptions.String())
			if err != nil {
				log.Fatal("failed to set the git flag value", err)
			}
		}

		currentWorkingDir, err := os.Getwd()
		if err != nil {
			log.Printf("could not get current working directory: %v", err)
			cobra.CheckErr(textinput.CreateErrorInputModel(err).Err())
		}
		project.AbsolutePath = currentWorkingDir

		spinner := tea.NewProgram(spinner.InitialModelNew())

		// add synchronization to wait for spinner to finish
		wg := sync.WaitGroup{}
		wg.Add(1)
		go func() {
			defer wg.Done()
			if _, err := spinner.Run(); err != nil {
				cobra.CheckErr(err)
			}
		}()

		defer func() {
			if r := recover(); r != nil {
				fmt.Println("The program encountered an unexpected issue and had to exit. The error was:", r)
				fmt.Println("If you continue to experience this issue, please post a message on our GitHub page or join our Discord server for support.")
				if releaseErr := spinner.ReleaseTerminal(); releaseErr != nil {
					log.Printf("Problem releasing terminal: %v", releaseErr)
				}
			}
		}()

		// This calls the templates
		err = project.CreateMainFile()
		if err != nil {
			if releaseErr := spinner.ReleaseTerminal(); releaseErr != nil {
				log.Printf("Problem releasing terminal: %v", releaseErr)
			}
			log.Printf("Problem creating files for project.")
			cobra.CheckErr(textinput.CreateErrorInputModel(err).Err())
		}

		fmt.Println(endingMsgStyle.Render("\nNext steps:"))
		fmt.Println(endingMsgStyle.Render(fmt.Sprintf("• cd into the newly created project with: `cd %s`\n", utils.GetRootDir(project.ProjectName))))

		if options.Advanced.Choices["React"] {
			options.Advanced.Choices["Htmx"] = false
			options.Advanced.Choices["Tailwind"] = false
			fmt.Println(endingMsgStyle.Render("• cd into frontend\n"))
			fmt.Println(endingMsgStyle.Render("• npm install\n"))
			fmt.Println(endingMsgStyle.Render("• npm run dev\n"))
		}

		if options.Advanced.Choices["Tailwind"] {
			options.Advanced.Choices["Htmx"] = true
			fmt.Println(endingMsgStyle.Render("• Install the tailwind standalone cli if you haven't already, grab the executable for your platform from the latest release on GitHub\n"))
			fmt.Println(endingMsgStyle.Render("• More info about the Tailwind CLI: https://tailwindcss.com/blog/standalone-cli\n"))
		}

		if options.Advanced.Choices["Htmx"] {
			options.Advanced.Choices["react"] = false
			fmt.Println(endingMsgStyle.Render("• Install the templ cli if you haven't already by running `go install github.com/a-h/templ/cmd/templ@latest`\n"))
			fmt.Println(endingMsgStyle.Render("• Generate templ function files by running `templ generate`\n"))
		}

		if isInteractive {
			nonInteractiveCommand := utils.NonInteractiveCommand(cmd.Use, cmd.Flags())
			fmt.Println(tipMsgStyle.Render("Tip: Repeat the equivalent Blueprint with the following non-interactive command:"))
			fmt.Println(tipMsgStyle.Italic(false).Render(fmt.Sprintf("• %s\n", nonInteractiveCommand)))
		}
		err = spinner.ReleaseTerminal()
		if err != nil {
			log.Printf("Could not release terminal: %v", err)
			cobra.CheckErr(err)
		}
	},
}

// doesDirectoryExistAndIsNotEmpty checks if the directory exists and is not empty
func doesDirectoryExistAndIsNotEmpty(name string) bool {
	if _, err := os.Stat(name); err == nil {
		dirEntries, err := os.ReadDir(name)
		if err != nil {
			log.Printf("could not read directory: %v", err)
			cobra.CheckErr(textinput.CreateErrorInputModel(err))
		}
		if len(dirEntries) > 0 {
			return true
		}
	}
	return false
}


================================================
FILE: cmd/flags/advancedFeatures.go
================================================
package flags

import (
	"fmt"
	"strings"
)

type AdvancedFeatures []string

const (
	Htmx              string = "htmx"
	GoProjectWorkflow string = "githubaction"
	Websocket         string = "websocket"
	Tailwind          string = "tailwind"
	React             string = "react"
	Docker            string = "docker"
)

var AllowedAdvancedFeatures = []string{string(React), string(Htmx), string(GoProjectWorkflow), string(Websocket), string(Tailwind), string(Docker)}

func (f AdvancedFeatures) String() string {
	return strings.Join(f, ",")
}

func (f *AdvancedFeatures) Type() string {
	return "AdvancedFeatures"
}

func (f *AdvancedFeatures) Set(value string) error {
	// Contains isn't available in 1.20 yet
	// if AdvancedFeatures.Contains(value) {
	for _, advancedFeature := range AllowedAdvancedFeatures {
		if advancedFeature == value {
			*f = append(*f, advancedFeature)
			return nil
		}
	}

	return fmt.Errorf("advanced Feature to use. Allowed values: %s", strings.Join(AllowedAdvancedFeatures, ", "))
}


================================================
FILE: cmd/flags/database.go
================================================
package flags

import (
	"fmt"
	"strings"
)

type Database string

// These are all the current databases supported. If you want to add one, you
// can simply copy and paste a line here. Do not forget to also add it into the
// AllowedDBDrivers slice too!
const (
	MySql    Database = "mysql"
	Postgres Database = "postgres"
	Sqlite   Database = "sqlite"
	Mongo    Database = "mongo"
	Redis    Database = "redis"
	Scylla   Database = "scylla"
	None     Database = "none"
)

var AllowedDBDrivers = []string{string(MySql), string(Postgres), string(Sqlite), string(Mongo), string(Redis), string(Scylla), string(None)}

func (f Database) String() string {
	return string(f)
}

func (f *Database) Type() string {
	return "Database"
}

func (f *Database) Set(value string) error {
	// Contains isn't available in 1.20 yet
	// if AllowedDBDrivers.Contains(value) {
	for _, database := range AllowedDBDrivers {
		if database == value {
			*f = Database(value)
			return nil
		}
	}

	return fmt.Errorf("Database to use. Allowed values: %s", strings.Join(AllowedDBDrivers, ", "))
}


================================================
FILE: cmd/flags/frameworks.go
================================================
package flags

import (
	"fmt"
	"strings"
)

type Framework string

// These are all the current frameworks supported. If you want to add one, you
// can simply copy and paste a line here. Do not forget to also add it into the
// AllowedProjectTypes slice too!
const (
	Chi             Framework = "chi"
	Gin             Framework = "gin"
	Fiber           Framework = "fiber"
	GorillaMux      Framework = "gorilla/mux"
	HttpRouter      Framework = "httprouter"
	StandardLibrary Framework = "standard-library"
	Echo            Framework = "echo"
)

var AllowedProjectTypes = []string{string(Chi), string(Gin), string(Fiber), string(GorillaMux), string(HttpRouter), string(StandardLibrary), string(Echo)}

func (f Framework) String() string {
	return string(f)
}

func (f *Framework) Type() string {
	return "Framework"
}

func (f *Framework) Set(value string) error {
	// Contains isn't available in 1.20 yet
	// if AllowedProjectTypes.Contains(value) {
	for _, project := range AllowedProjectTypes {
		if project == value {
			*f = Framework(value)
			return nil
		}
	}

	return fmt.Errorf("Framework to use. Allowed values: %s", strings.Join(AllowedProjectTypes, ", "))
}


================================================
FILE: cmd/flags/git.go
================================================
package flags

import (
	"fmt"
	"strings"
)

type Git string

const (
	Commit = "commit"
	Stage  = "stage"
	Skip   = "skip"
)

var AllowedGitsOptions = []string{string(Commit), string(Stage), string(Skip)}

func (f Git) String() string {
	return string(f)
}

func (f *Git) Type() string {
	return "Git"
}

func (f *Git) Set(value string) error {
	for _, gitOption := range AllowedGitsOptions {
		if gitOption == value {
			*f = Git(value)
			return nil
		}
	}

	return fmt.Errorf("Git to use. Allowed values: %s", strings.Join(AllowedGitsOptions, ", "))
}


================================================
FILE: cmd/program/program.go
================================================
// Package program provides the
// main functionality of Blueprint
package program

import (
	"bytes"
	"fmt"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"text/template"

	tea "github.com/charmbracelet/bubbletea"
	"github.com/melkeydev/go-blueprint/cmd/flags"
	tpl "github.com/melkeydev/go-blueprint/cmd/template"
	"github.com/melkeydev/go-blueprint/cmd/template/advanced"
	"github.com/melkeydev/go-blueprint/cmd/template/dbdriver"
	"github.com/melkeydev/go-blueprint/cmd/template/docker"
	"github.com/melkeydev/go-blueprint/cmd/template/framework"
	"github.com/melkeydev/go-blueprint/cmd/utils"
)

// A Project contains the data for the project folder
// being created, and methods that help with that process
type Project struct {
	ProjectName       string
	Exit              bool
	AbsolutePath      string
	ProjectType       flags.Framework
	DBDriver          flags.Database
	Docker            flags.Database
	FrameworkMap      map[flags.Framework]Framework
	DBDriverMap       map[flags.Database]Driver
	DockerMap         map[flags.Database]Docker
	AdvancedOptions   map[string]bool
	AdvancedTemplates AdvancedTemplates
	GitOptions        flags.Git
	OSCheck           map[string]bool
}

type AdvancedTemplates struct {
	TemplateRoutes  string
	TemplateImports string
}

// A Framework contains the name and templater for a
// given Framework
type Framework struct {
	packageName []string
	templater   Templater
}

type Driver struct {
	packageName []string
	templater   DBDriverTemplater
}

type Docker struct {
	packageName []string
	templater   DockerTemplater
}

// A Templater has the methods that help build the files
// in the Project folder, and is specific to a Framework
type Templater interface {
	Main() []byte
	Server() []byte
	Routes() []byte
	TestHandler() []byte
	HtmxTemplRoutes() []byte
	HtmxTemplImports() []byte
	WebsocketImports() []byte
}

type DBDriverTemplater interface {
	Service() []byte
	Env() []byte
	Tests() []byte
}

type DockerTemplater interface {
	Docker() []byte
}

type WorkflowTemplater interface {
	Releaser() []byte
	Test() []byte
	ReleaserConfig() []byte
}

var (
	chiPackage     = []string{"github.com/go-chi/chi/v5"}
	gorillaPackage = []string{"github.com/gorilla/mux"}
	routerPackage  = []string{"github.com/julienschmidt/httprouter"}
	ginPackage     = []string{"github.com/gin-gonic/gin"}
	fiberPackage   = []string{"github.com/gofiber/fiber/v2"}
	echoPackage    = []string{"github.com/labstack/echo/v4", "github.com/labstack/echo/v4/middleware"}

	mysqlDriver    = []string{"github.com/go-sql-driver/mysql"}
	postgresDriver = []string{"github.com/jackc/pgx/v5/stdlib"}
	sqliteDriver   = []string{"github.com/mattn/go-sqlite3"}
	redisDriver    = []string{"github.com/redis/go-redis/v9"}
	mongoDriver    = []string{"go.mongodb.org/mongo-driver"}
	gocqlDriver    = []string{"github.com/gocql/gocql"}
	scyllaDriver   = "github.com/scylladb/gocql@v1.14.4" // Replacement for GoCQL

	godotenvPackage = []string{"github.com/joho/godotenv"}
	templPackage    = []string{"github.com/a-h/templ"}
)

const (
	root                 = "/"
	cmdApiPath           = "cmd/api"
	cmdWebPath           = "cmd/web"
	internalServerPath   = "internal/server"
	internalDatabasePath = "internal/database"
	gitHubActionPath     = ".github/workflows"
)

// CheckOs checks Operation system and generates MakeFile and `go build` command
// Based on Project.Unixbase
func (p *Project) CheckOS() {
	p.OSCheck = make(map[string]bool)

	if runtime.GOOS != "windows" {
		p.OSCheck["UnixBased"] = true
	}
	if runtime.GOOS == "linux" {
		p.OSCheck["linux"] = true
	}
	if runtime.GOOS == "darwin" {
		p.OSCheck["darwin"] = true
	}
}

// ExitCLI checks if the Project has been exited, and closes
// out of the CLI if it has
func (p *Project) ExitCLI(tprogram *tea.Program) {
	if p.Exit {
		// logo render here
		if err := tprogram.ReleaseTerminal(); err != nil {
			log.Fatal(err)
		}
		os.Exit(1)
	}
}

// createFrameWorkMap adds the current supported
// Frameworks into a Project's FrameworkMap
func (p *Project) createFrameworkMap() {
	p.FrameworkMap[flags.Chi] = Framework{
		packageName: chiPackage,
		templater:   framework.ChiTemplates{},
	}

	p.FrameworkMap[flags.StandardLibrary] = Framework{
		packageName: []string{},
		templater:   framework.StandardLibTemplate{},
	}

	p.FrameworkMap[flags.Gin] = Framework{
		packageName: ginPackage,
		templater:   framework.GinTemplates{},
	}

	p.FrameworkMap[flags.Fiber] = Framework{
		packageName: fiberPackage,
		templater:   framework.FiberTemplates{},
	}

	p.FrameworkMap[flags.GorillaMux] = Framework{
		packageName: gorillaPackage,
		templater:   framework.GorillaTemplates{},
	}

	p.FrameworkMap[flags.HttpRouter] = Framework{
		packageName: routerPackage,
		templater:   framework.RouterTemplates{},
	}

	p.FrameworkMap[flags.Echo] = Framework{
		packageName: echoPackage,
		templater:   framework.EchoTemplates{},
	}
}

func (p *Project) createDBDriverMap() {
	p.DBDriverMap[flags.MySql] = Driver{
		packageName: mysqlDriver,
		templater:   dbdriver.MysqlTemplate{},
	}
	p.DBDriverMap[flags.Postgres] = Driver{
		packageName: postgresDriver,
		templater:   dbdriver.PostgresTemplate{},
	}
	p.DBDriverMap[flags.Sqlite] = Driver{
		packageName: sqliteDriver,
		templater:   dbdriver.SqliteTemplate{},
	}
	p.DBDriverMap[flags.Mongo] = Driver{
		packageName: mongoDriver,
		templater:   dbdriver.MongoTemplate{},
	}
	p.DBDriverMap[flags.Redis] = Driver{
		packageName: redisDriver,
		templater:   dbdriver.RedisTemplate{},
	}

	p.DBDriverMap[flags.Scylla] = Driver{
		packageName: gocqlDriver,
		templater:   dbdriver.ScyllaTemplate{},
	}
}

func (p *Project) createDockerMap() {
	p.DockerMap = make(map[flags.Database]Docker)

	p.DockerMap[flags.MySql] = Docker{
		packageName: []string{},
		templater:   docker.MysqlDockerTemplate{},
	}
	p.DockerMap[flags.Postgres] = Docker{
		packageName: []string{},
		templater:   docker.PostgresDockerTemplate{},
	}
	p.DockerMap[flags.Mongo] = Docker{
		packageName: []string{},
		templater:   docker.MongoDockerTemplate{},
	}
	p.DockerMap[flags.Redis] = Docker{
		packageName: []string{},
		templater:   docker.RedisDockerTemplate{},
	}
	p.DockerMap[flags.Scylla] = Docker{
		packageName: []string{},
		templater:   docker.ScyllaDockerTemplate{},
	}
}

// CreateMainFile creates the project folders and files,
// and writes to them depending on the selected options
func (p *Project) CreateMainFile() error {
	// check if AbsolutePath exists
	if _, err := os.Stat(p.AbsolutePath); os.IsNotExist(err) {
		// create directory
		if err := os.Mkdir(p.AbsolutePath, 0o754); err != nil {
			log.Printf("Could not create directory: %v", err)
			return err
		}
	}

	// Check if user.email is set.
	if p.GitOptions.String() != flags.Skip {

		emailSet, err := utils.CheckGitConfig("user.email")
		if err != nil {
			return err
		}
		if !emailSet {
			fmt.Println("user.email is not set in git config.")
			fmt.Println("Please set up git config before trying again.")
			panic("\nGIT CONFIG ISSUE: user.email is not set in git config.\n")
		}
	}

	p.ProjectName = strings.TrimSpace(p.ProjectName)

	// Create a new directory with the project name
	projectPath := filepath.Join(p.AbsolutePath, utils.GetRootDir(p.ProjectName))
	if _, err := os.Stat(projectPath); os.IsNotExist(err) {
		err := os.MkdirAll(projectPath, 0o751)
		if err != nil {
			log.Printf("Error creating root project directory %v\n", err)
			return err
		}
	}

	// Define Operating system
	p.CheckOS()

	// Create the map for our program
	p.createFrameworkMap()

	// Create go.mod
	err := utils.InitGoMod(p.ProjectName, projectPath)
	if err != nil {
		log.Printf("Could not initialize go.mod in new project %v\n", err)
		return err
	}

	// Install the correct package for the selected framework
	if p.ProjectType != flags.StandardLibrary {
		err = utils.GoGetPackage(projectPath, p.FrameworkMap[p.ProjectType].packageName)
		if err != nil {
			log.Println("Could not install go dependency for the chosen framework")
			return err
		}
	}

	// Install the correct package for the selected driver
	if p.DBDriver != "none" {
		p.createDBDriverMap()
		err = utils.GoGetPackage(projectPath, p.DBDriverMap[p.DBDriver].packageName)
		if err != nil {
			log.Println("Could not install go dependency for chosen driver")
			return err
		}

		err = p.CreatePath(internalDatabasePath, projectPath)
		if err != nil {
			log.Printf("Error creating path: %s", internalDatabasePath)
			return err
		}

		err = p.CreateFileWithInjection(internalDatabasePath, projectPath, "database.go", "database")
		if err != nil {
			log.Printf("Error injecting database.go file: %v", err)
			return err
		}

		if p.DBDriver != "sqlite" {
			err = p.CreateFileWithInjection(internalDatabasePath, projectPath, "database_test.go", "integration-tests")
			if err != nil {
				log.Printf("Error injecting database_test.go file: %v", err)
				return err
			}
		}
	}

	// Create correct docker compose for the selected driver
	if p.DBDriver != "none" {
		if p.DBDriver != "sqlite" {
			p.createDockerMap()
			p.Docker = p.DBDriver

			err = p.CreateFileWithInjection(root, projectPath, "docker-compose.yml", "db-docker")
			if err != nil {
				log.Printf("Error injecting docker-compose.yml file: %v", err)
				return err
			}
		} else {
			fmt.Println(" SQLite doesn't support docker-compose.yml configuration")
		}
	}

	// Install the godotenv package
	err = utils.GoGetPackage(projectPath, godotenvPackage)
	if err != nil {
		log.Println("Could not install go dependency")

		return err
	}

	if p.DBDriver == flags.Scylla {
		replace := fmt.Sprintf("%s=%s", gocqlDriver[0], scyllaDriver)
		err = utils.GoModReplace(projectPath, replace)
		if err != nil {
			log.Printf("Could not replace go dependency %v\n", err)
			return err
		}
	}

	err = p.CreatePath(cmdApiPath, projectPath)
	if err != nil {
		log.Printf("Error creating path: %s", projectPath)
		return err
	}

	err = p.CreateFileWithInjection(cmdApiPath, projectPath, "main.go", "main")
	if err != nil {
		return err
	}

	makeFile, err := os.Create(filepath.Join(projectPath, "Makefile"))
	if err != nil {
		return err
	}

	defer makeFile.Close()

	// inject makefile template
	makeFileTemplate := template.Must(template.New("makefile").Parse(string(framework.MakeTemplate())))
	err = makeFileTemplate.Execute(makeFile, p)
	if err != nil {
		return err
	}

	readmeFile, err := os.Create(filepath.Join(projectPath, "README.md"))
	if err != nil {
		return err
	}
	defer readmeFile.Close()

	// inject readme template
	readmeFileTemplate := template.Must(template.New("readme").Parse(string(framework.ReadmeTemplate())))
	err = readmeFileTemplate.Execute(readmeFile, p)
	if err != nil {
		return err
	}

	err = p.CreatePath(internalServerPath, projectPath)
	if err != nil {
		log.Printf("Error creating path: %s", internalServerPath)
		return err
	}

	if p.AdvancedOptions[string(flags.React)] {
		// deselect htmx option automatically since react is selected
		p.AdvancedOptions[string(flags.Htmx)] = false
		if err := p.CreateViteReactProject(projectPath); err != nil {
			return fmt.Errorf("failed to set up React project: %w", err)
		}

		// if everything went smoothly, remove tailwing flag option
		p.AdvancedOptions[string(flags.Tailwind)] = false
	}

	if p.AdvancedOptions[string(flags.Tailwind)] {
		// select htmx option automatically since tailwind is selected
		p.AdvancedOptions[string(flags.Htmx)] = true

		err = os.MkdirAll(fmt.Sprintf("%s/%s/assets/css", projectPath, cmdWebPath), 0o755)
		if err != nil {
			return err
		}

		err = os.MkdirAll(fmt.Sprintf("%s/%s/styles", projectPath, cmdWebPath), 0o755)
		if err != nil {
			return fmt.Errorf("failed to create styles directory: %w", err)
		}

		inputCssFile, err := os.Create(fmt.Sprintf("%s/%s/styles/input.css", projectPath, cmdWebPath))
		if err != nil {
			return err
		}
		defer inputCssFile.Close()

		inputCssTemplate := advanced.InputCssTemplate()
		err = os.WriteFile(fmt.Sprintf("%s/%s/styles/input.css", projectPath, cmdWebPath), inputCssTemplate, 0o644)
		if err != nil {
			return err
		}

		outputCssFile, err := os.Create(fmt.Sprintf("%s/%s/assets/css/output.css", projectPath, cmdWebPath))
		if err != nil {
			return err
		}
		defer outputCssFile.Close()

		outputCssTemplate := advanced.OutputCssTemplate()
		err = os.WriteFile(fmt.Sprintf("%s/%s/assets/css/output.css", projectPath, cmdWebPath), outputCssTemplate, 0o644)
		if err != nil {
			return err
		}
	}

	if p.AdvancedOptions[string(flags.Htmx)] {
		// create folders and hello world file
		err = p.CreatePath(cmdWebPath, projectPath)
		if err != nil {
			return err
		}
		helloTemplFile, err := os.Create(fmt.Sprintf("%s/%s/hello.templ", projectPath, cmdWebPath))
		if err != nil {
			return err
		}
		defer helloTemplFile.Close()

		// inject hello.templ template
		helloTemplTemplate := template.Must(template.New("hellotempl").Parse((string(advanced.HelloTemplTemplate()))))
		err = helloTemplTemplate.Execute(helloTemplFile, p)
		if err != nil {
			return err
		}

		baseTemplFile, err := os.Create(fmt.Sprintf("%s/%s/base.templ", projectPath, cmdWebPath))
		if err != nil {
			return err
		}
		defer baseTemplFile.Close()

		baseTemplTemplate := template.Must(template.New("basetempl").Parse((string(advanced.BaseTemplTemplate()))))
		err = baseTemplTemplate.Execute(baseTemplFile, p)
		if err != nil {
			return err
		}

		err = os.MkdirAll(fmt.Sprintf("%s/%s/assets/js", projectPath, cmdWebPath), 0o755)
		if err != nil {
			return err
		}

		htmxMinJsFile, err := os.Create(fmt.Sprintf("%s/%s/assets/js/htmx.min.js", projectPath, cmdWebPath))
		if err != nil {
			return err
		}
		defer htmxMinJsFile.Close()

		htmxMinJsTemplate := advanced.HtmxJSTemplate()
		err = os.WriteFile(fmt.Sprintf("%s/%s/assets/js/htmx.min.js", projectPath, cmdWebPath), htmxMinJsTemplate, 0o644)
		if err != nil {
			return err
		}

		htmxTailwindConfigJsFile, err := os.Create(fmt.Sprintf("%s/tailwind.config.js", projectPath))
		if err != nil {
			return err
		}
		defer htmxTailwindConfigJsFile.Close()

		htmxTailwindConfigJsTemplate := advanced.HtmxTailwindConfigJsTemplate()
		err = os.WriteFile(fmt.Sprintf("%s/tailwind.config.js", projectPath), htmxTailwindConfigJsTemplate, 0o644)
		if err != nil {
			return err
		}

		efsFile, err := os.Create(fmt.Sprintf("%s/%s/efs.go", projectPath, cmdWebPath))
		if err != nil {
			return err
		}
		defer efsFile.Close()

		efsTemplate := template.Must(template.New("efs").Parse((string(advanced.EfsTemplate()))))
		err = efsTemplate.Execute(efsFile, p)
		if err != nil {
			return err
		}
		err = utils.GoGetPackage(projectPath, templPackage)
		if err != nil {
			log.Println("Could not install go dependency")
			return err
		}

		helloGoFile, err := os.Create(fmt.Sprintf("%s/%s/hello.go", projectPath, cmdWebPath))
		if err != nil {
			return err
		}
		defer efsFile.Close()

		if p.ProjectType == "fiber" {
			helloGoTemplate := template.Must(template.New("efs").Parse((string(advanced.HelloFiberGoTemplate()))))
			err = helloGoTemplate.Execute(helloGoFile, p)
			if err != nil {
				return err
			}
			err = utils.GoGetPackage(projectPath, []string{"github.com/gofiber/fiber/v2/middleware/adaptor"})
			if err != nil {
				log.Println("Could not install go dependency")
				return err
			}
		} else {
			helloGoTemplate := template.Must(template.New("efs").Parse((string(advanced.HelloGoTemplate()))))
			err = helloGoTemplate.Execute(helloGoFile, p)
			if err != nil {
				return err
			}
		}

		p.CreateHtmxTemplates()
	}

	// Create .github/workflows folder and inject release.yml and go-test.yml
	if p.AdvancedOptions[string(flags.GoProjectWorkflow)] {
		err = p.CreatePath(gitHubActionPath, projectPath)
		if err != nil {
			log.Printf("Error creating path: %s", gitHubActionPath)
			return err
		}

		err = p.CreateFileWithInjection(gitHubActionPath, projectPath, "release.yml", "releaser")
		if err != nil {
			log.Printf("Error injecting release.yml file: %v", err)
			return err
		}

		err = p.CreateFileWithInjection(gitHubActionPath, projectPath, "go-test.yml", "go-test")
		if err != nil {
			log.Printf("Error injecting go-test.yml file: %v", err)
			return err
		}

		err = p.CreateFileWithInjection(root, projectPath, ".goreleaser.yml", "releaser-config")
		if err != nil {
			log.Printf("Error injecting .goreleaser.yml file: %v", err)
			return err
		}
	}

	// if the websocket option is checked, a websocket dependency needs to
	// be added to the routes depending on the framework choosen.
	// Only fiber uses a different websocket library, the other frameworks
	// all work with the same one
	if p.AdvancedOptions[string(flags.Websocket)] {
		p.CreateWebsocketImports(projectPath)
	}

	if p.AdvancedOptions[string(flags.Docker)] {
		Dockerfile, err := os.Create(filepath.Join(projectPath, "Dockerfile"))
		if err != nil {
			return err
		}
		defer Dockerfile.Close()

		// inject Docker template
		dockerfileTemplate := template.Must(template.New("Dockerfile").Parse(string(advanced.Dockerfile())))
		err = dockerfileTemplate.Execute(Dockerfile, p)
		if err != nil {
			return err
		}

		if p.DBDriver == "none" || p.DBDriver == "sqlite" {

			Dockercompose, err := os.Create(filepath.Join(projectPath, "docker-compose.yml"))
			if err != nil {
				return err
			}
			defer Dockercompose.Close()

			// inject DockerCompose template
			dockerComposeTemplate := template.Must(template.New("docker-compose.yml").Parse(string(advanced.DockerCompose())))
			err = dockerComposeTemplate.Execute(Dockercompose, p)
			if err != nil {
				return err
			}
		}
	}

	err = p.CreateFileWithInjection(internalServerPath, projectPath, "routes.go", "routes")
	if err != nil {
		log.Printf("Error injecting routes.go file: %v", err)
		return err
	}

	err = p.CreateFileWithInjection(internalServerPath, projectPath, "routes_test.go", "tests")
	if err != nil {
		return err
	}

	err = p.CreateFileWithInjection(internalServerPath, projectPath, "server.go", "server")
	if err != nil {
		log.Printf("Error injecting server.go file: %v", err)
		return err
	}

	err = p.CreateFileWithInjection(root, projectPath, ".env", "env")
	if err != nil {
		log.Printf("Error injecting .env file: %v", err)
		return err
	}

	// Create gitignore
	gitignoreFile, err := os.Create(filepath.Join(projectPath, ".gitignore"))
	if err != nil {
		return err
	}
	defer gitignoreFile.Close()

	// inject gitignore template
	gitignoreTemplate := template.Must(template.New(".gitignore").Parse(string(framework.GitIgnoreTemplate())))
	err = gitignoreTemplate.Execute(gitignoreFile, p)
	if err != nil {
		return err
	}

	// Create .air.toml file
	airTomlFile, err := os.Create(filepath.Join(projectPath, ".air.toml"))
	if err != nil {
		return err
	}

	defer airTomlFile.Close()

	// inject air.toml template
	airTomlTemplate := template.Must(template.New("airtoml").Parse(string(framework.AirTomlTemplate())))
	err = airTomlTemplate.Execute(airTomlFile, p)
	if err != nil {
		return err
	}

	err = utils.GoTidy(projectPath)
	if err != nil {
		log.Printf("Could not go tidy in new project %v\n", err)
		return err
	}

	err = utils.GoFmt(projectPath)
	if err != nil {
		log.Printf("Could not gofmt in new project %v\n", err)
		return err
	}

	if p.GitOptions != flags.Skip {
		nameSet, err := utils.CheckGitConfig("user.name")
		if err != nil {
			return err
		}

		if !nameSet {
			fmt.Println("user.name is not set in git config.")
			fmt.Println("Please set up git config before trying again.")
			panic("\nGIT CONFIG ISSUE: user.name is not set in git config.\n")
		}
		// Initialize git repo
		err = utils.ExecuteCmd("git", []string{"init"}, projectPath)
		if err != nil {
			log.Printf("Error initializing git repo: %v", err)
			return err
		}

		// Git add files
		err = utils.ExecuteCmd("git", []string{"add", "."}, projectPath)
		if err != nil {
			log.Printf("Error adding files to git repo: %v", err)
			return err
		}

		if p.GitOptions == flags.Commit {
			// Git commit files
			err = utils.ExecuteCmd("git", []string{"commit", "-m", "Initial commit"}, projectPath)
			if err != nil {
				log.Printf("Error committing files to git repo: %v", err)
				return err
			}
		}
	}
	return nil
}

// CreatePath creates the given directory in the projectPath
func (p *Project) CreatePath(pathToCreate string, projectPath string) error {
	path := filepath.Join(projectPath, pathToCreate)
	if _, err := os.Stat(path); os.IsNotExist(err) {
		err := os.MkdirAll(path, 0o751)
		if err != nil {
			log.Printf("Error creating directory %v\n", err)
			return err
		}
	}

	return nil
}

// CreateFileWithInjection creates the given file at the
// project path, and injects the appropriate template
func (p *Project) CreateFileWithInjection(pathToCreate string, projectPath string, fileName string, methodName string) error {
	createdFile, err := os.Create(filepath.Join(projectPath, pathToCreate, fileName))
	if err != nil {
		return err
	}

	defer createdFile.Close()

	switch methodName {
	case "main":
		createdTemplate := template.Must(template.New(fileName).Parse(string(p.FrameworkMap[p.ProjectType].templater.Main())))
		err = createdTemplate.Execute(createdFile, p)
	case "server":
		createdTemplate := template.Must(template.New(fileName).Parse(string(p.FrameworkMap[p.ProjectType].templater.Server())))
		err = createdTemplate.Execute(createdFile, p)
	case "routes":
		routeFileBytes := p.FrameworkMap[p.ProjectType].templater.Routes()
		createdTemplate := template.Must(template.New(fileName).Parse(string(routeFileBytes)))
		err = createdTemplate.Execute(createdFile, p)
	case "releaser":
		createdTemplate := template.Must(template.New(fileName).Parse(string(advanced.Releaser())))
		err = createdTemplate.Execute(createdFile, p)
	case "go-test":
		createdTemplate := template.Must(template.New(fileName).Parse(string(advanced.Test())))
		err = createdTemplate.Execute(createdFile, p)
	case "releaser-config":
		createdTemplate := template.Must(template.New(fileName).Parse(string(advanced.ReleaserConfig())))
		err = createdTemplate.Execute(createdFile, p)
	case "database":
		createdTemplate := template.Must(template.New(fileName).Parse(string(p.DBDriverMap[p.DBDriver].templater.Service())))
		err = createdTemplate.Execute(createdFile, p)
	case "db-docker":
		createdTemplate := template.Must(template.New(fileName).Parse(string(p.DockerMap[p.Docker].templater.Docker())))
		err = createdTemplate.Execute(createdFile, p)
	case "integration-tests":
		createdTemplate := template.Must(template.New(fileName).Parse(string(p.DBDriverMap[p.DBDriver].templater.Tests())))
		err = createdTemplate.Execute(createdFile, p)
	case "tests":
		createdTemplate := template.Must(template.New(fileName).Parse(string(p.FrameworkMap[p.ProjectType].templater.TestHandler())))
		err = createdTemplate.Execute(createdFile, p)
	case "env":
		if p.DBDriver != "none" {

			envBytes := [][]byte{
				tpl.GlobalEnvTemplate(),
				p.DBDriverMap[p.DBDriver].templater.Env(),
			}
			createdTemplate := template.Must(template.New(fileName).Parse(string(bytes.Join(envBytes, []byte("\n")))))
			err = createdTemplate.Execute(createdFile, p)

		} else {
			createdTemplate := template.Must(template.New(fileName).Parse(string(tpl.GlobalEnvTemplate())))
			err = createdTemplate.Execute(createdFile, p)
		}
	}

	if err != nil {
		return err
	}

	return nil
}

func (p *Project) CreateViteReactProject(projectPath string) error {
	if err := checkNpmInstalled(); err != nil {
		return err
	}

	originalDir, err := os.Getwd()
	if err != nil {
		return fmt.Errorf("failed to get current directory: %w", err)
	}
	defer func() {
		if err := os.Chdir(originalDir); err != nil {
			fmt.Fprintf(os.Stderr, "failed to change back to original directory: %v\n", err)
		}
	}()

	// change into the project directory to run vite command
	err = os.Chdir(projectPath)
	if err != nil {
		fmt.Println("failed to change into project directory: %w", err)
	}

	// the interactive vite command will not work as we can't interact with it
	fmt.Println("Installing create-vite (using cache if available)...")
	cmd := exec.Command("npm", "create", "vite@latest", "frontend", "--",
		"--template", "react-ts",
		"--prefer-offline",
		"--no-fund")
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("failed to use create-vite: %w", err)
	}

	frontendPath := filepath.Join(projectPath, "frontend")
	if err := os.MkdirAll(frontendPath, 0755); err != nil {
		return fmt.Errorf("failed to create frontend directory: %w", err)
	}

	if err := os.Chdir(frontendPath); err != nil {
		return fmt.Errorf("failed to change to frontend directory: %w", err)
	}

	srcDir := filepath.Join(frontendPath, "src")
	if err := os.MkdirAll(srcDir, 0755); err != nil {
		return fmt.Errorf("failed to create src directory: %w", err)
	}

	if err := os.WriteFile(filepath.Join(srcDir, "App.tsx"), advanced.ReactAppfile(), 0644); err != nil {
		return fmt.Errorf("failed to write App.tsx template: %w", err)
	}

	// Create the global `.env` file from the template
	err = p.CreateFileWithInjection("", projectPath, ".env", "env")
	if err != nil {
		return fmt.Errorf("failed to create global .env file: %w", err)
	}

	// Read from the global `.env` file and create the frontend-specific `.env`
	globalEnvPath := filepath.Join(projectPath, ".env")
	vitePort := "8080" // Default fallback

	// Read the global .env file
	if data, err := os.ReadFile(globalEnvPath); err == nil {
		lines := strings.Split(string(data), "\n")
		for _, line := range lines {
			if strings.HasPrefix(line, "PORT=") {
				vitePort = strings.SplitN(line, "=", 2)[1] // Get the backend port value
				break
			}
		}
	}

	// Use a template to generate the frontend .env file
	frontendEnvContent := fmt.Sprintf("VITE_PORT=%s\n", vitePort)
	if err := os.WriteFile(filepath.Join(frontendPath, ".env"), []byte(frontendEnvContent), 0644); err != nil {
		return fmt.Errorf("failed to create frontend .env file: %w", err)
	}

	// Handle Tailwind configuration if selected
	if p.AdvancedOptions[string(flags.Tailwind)] {
		fmt.Println("Installing Tailwind dependencies (using cache if available)...")
		cmd := exec.Command("npm", "install",
			"--prefer-offline",
			"--no-fund",
			"tailwindcss@^4", "@tailwindcss/vite")
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		if err := cmd.Run(); err != nil {
			return fmt.Errorf("failed to install Tailwind: %w", err)
		}

		// Create the vite + react + Tailwind v4 configuration
		if err := os.WriteFile(filepath.Join(frontendPath, "vite.config.ts"), advanced.ViteTailwindConfigFile(), 0644); err != nil {
			return fmt.Errorf("failed to write vite.config.ts: %w", err)
		}

		srcDir := filepath.Join(frontendPath, "src")
		if err := os.MkdirAll(srcDir, 0755); err != nil {
			return fmt.Errorf("failed to create src directory: %w", err)
		}

		err = os.WriteFile(filepath.Join(srcDir, "index.css"), advanced.InputCssTemplateReact(), 0644)
		if err != nil {
			return fmt.Errorf("failed to update index.css: %w", err)
		}

		if err := os.WriteFile(filepath.Join(srcDir, "App.tsx"), advanced.ReactTailwindAppfile(), 0644); err != nil {
			return fmt.Errorf("failed to write App.tsx template: %w", err)
		}

		if err := os.Remove(filepath.Join(srcDir, "App.css")); err != nil {
			// Don't return error if file doesn't exist
			if !os.IsNotExist(err) {
				return fmt.Errorf("failed to remove App.css: %w", err)
			}
		}

		// set to false to not re-do in next step
		p.AdvancedOptions[string(flags.Tailwind)] = false
	}

	return nil
}

func (p *Project) CreateHtmxTemplates() {
	routesPlaceHolder := ""
	importsPlaceHolder := ""
	if p.AdvancedOptions[string(flags.Htmx)] {
		routesPlaceHolder += string(p.FrameworkMap[p.ProjectType].templater.HtmxTemplRoutes())
		importsPlaceHolder += string(p.FrameworkMap[p.ProjectType].templater.HtmxTemplImports())
	}

	routeTmpl, err := template.New("routes").Parse(routesPlaceHolder)
	if err != nil {
		log.Fatal(err)
	}
	importTmpl, err := template.New("imports").Parse(importsPlaceHolder)
	if err != nil {
		log.Fatal(err)
	}
	var routeBuffer bytes.Buffer
	var importBuffer bytes.Buffer
	err = routeTmpl.Execute(&routeBuffer, p)
	if err != nil {
		log.Fatal(err)
	}
	err = importTmpl.Execute(&importBuffer, p)
	if err != nil {
		log.Fatal(err)
	}
	p.AdvancedTemplates.TemplateRoutes = routeBuffer.String()
	p.AdvancedTemplates.TemplateImports = importBuffer.String()
}

func (p *Project) CreateWebsocketImports(appDir string) {
	websocketDependency := []string{"github.com/coder/websocket"}
	if p.ProjectType == flags.Fiber {
		websocketDependency = []string{"github.com/gofiber/contrib/websocket"}
	}

	// Websockets require a different package depending on what framework is
	// choosen. The application calls go mod tidy at the end so we don't
	// have to here
	err := utils.GoGetPackage(appDir, websocketDependency)
	if err != nil {
		log.Fatal(err)
	}

	importsPlaceHolder := string(p.FrameworkMap[p.ProjectType].templater.WebsocketImports())

	importTmpl, err := template.New("imports").Parse(importsPlaceHolder)
	if err != nil {
		log.Fatalf("CreateWebsocketImports failed to create template: %v", err)
	}
	var importBuffer bytes.Buffer
	err = importTmpl.Execute(&importBuffer, p)
	if err != nil {
		log.Fatalf("CreateWebsocketImports failed write template: %v", err)
	}
	newImports := strings.Join([]string{string(p.AdvancedTemplates.TemplateImports), importBuffer.String()}, "\n")
	p.AdvancedTemplates.TemplateImports = newImports
}

func checkNpmInstalled() error {
	cmd := exec.Command("npm", "--version")
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("npm is not installed: %w", err)
	}
	return nil
}


================================================
FILE: cmd/root.go
================================================
/*
Copyright © 2023 Melkey melkeydev@gmail.com
*/
package cmd

import (
	"os"

	"github.com/spf13/cobra"
)

var rootCmd = &cobra.Command{
	Use:   "go-blueprint",
	Short: "A program to spin up a quick Go project using a popular framework",
	Long: `Go Blueprint is a CLI tool that allows users to spin up a Go project with the corresponding structure seamlessly. 
It also gives the option to integrate with one of the more popular Go frameworks!`,
}

func Execute() {
	err := rootCmd.Execute()
	if err != nil {
		os.Exit(1)
	}
}

func init() {
	rootCmd.AddCommand(versionCmd)
	rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}


================================================
FILE: cmd/steps/steps.go
================================================
// Package steps provides utility for creating
// each step of the CLI
package steps

import "github.com/melkeydev/go-blueprint/cmd/flags"

// A StepSchema contains the data that is used
// for an individual step of the CLI
type StepSchema struct {
	StepName string // The name of a given step
	Options  []Item // The slice of each option for a given step
	Headers  string // The title displayed at the top of a given step
	Field    string
}

// Steps contains a slice of steps
type Steps struct {
	Steps map[string]StepSchema
}

// An Item contains the data for each option
// in a StepSchema.Options
type Item struct {
	Flag, Title, Desc string
}

// InitSteps initializes and returns the *Steps to be used in the CLI program
func InitSteps(projectType flags.Framework, databaseType flags.Database) *Steps {
	steps := &Steps{
		map[string]StepSchema{
			"framework": {
				StepName: "Go Project Framework",
				Options: []Item{
					{
						Title: "Standard-library",
						Desc:  "The built-in Go standard library HTTP package",
					},
					{
						Title: "Chi",
						Desc:  "A lightweight, idiomatic and composable router for building Go HTTP services",
					},
					{
						Title: "Gin",
						Desc:  "Features a martini-like API with performance that is up to 40 times faster thanks to httprouter",
					},
					{
						Title: "Fiber",
						Desc:  "An Express inspired web framework built on top of Fasthttp",
					},
					{
						Title: "Gorilla/Mux",
						Desc:  "Package gorilla/mux implements a request router and dispatcher for matching incoming requests to their respective handler",
					},
					{
						Title: "HttpRouter",
						Desc:  "HttpRouter is a lightweight high performance HTTP request router for Go",
					},
					{
						Title: "Echo",
						Desc:  "High performance, extensible, minimalist Go web framework",
					},
				},
				Headers: "What framework do you want to use in your Go project?",
				Field:   projectType.String(),
			},
			"driver": {
				StepName: "Go Project Database Driver",
				Options: []Item{
					{
						Title: "Mysql",
						Desc:  "MySQL-Driver for Go's database/sql package",
					},
					{
						Title: "Postgres",
						Desc:  "Go postgres driver for Go's database/sql package"},
					{
						Title: "Sqlite",
						Desc:  "sqlite3 driver conforming to the built-in database/sql interface"},
					{
						Title: "Mongo",
						Desc:  "The MongoDB supported driver for Go."},
					{
						Title: "Redis",
						Desc:  "Redis driver for Go."},
					{
						Title: "Scylla",
						Desc:  "ScyllaDB Enhanced driver from GoCQL."},
					{
						Title: "None",
						Desc:  "Choose this option if you don't wish to install a specific database driver."},
				},
				Headers: "What database driver do you want to use in your Go project?",
				Field:   databaseType.String(),
			},
			"advanced": {
				StepName: "Advanced Features",
				Headers:  "Which advanced features do you want?",
				Options: []Item{
					{
						Flag:  "React",
						Title: "React",
						Desc:  "Use Vite to spin up a React project in TypeScript. This disables selecting HTMX/Templ",
					},
					{
						Flag:  "Htmx",
						Title: "HTMX/Templ",
						Desc:  "Add starter HTMX and Templ files. This disables selecting React",
					},
					{
						Flag:  "GitHubAction",
						Title: "Go Project Workflow",
						Desc:  "Workflow templates for testing, cross-compiling and releasing Go projects",
					},
					{
						Flag:  "Websocket",
						Title: "Websocket endpoint",
						Desc:  "Add a websocket endpoint",
					},
					{
						Flag:  "Tailwind",
						Title: "TailwindCSS",
						Desc:  "A utility-first CSS framework (selecting this will automatically add HTMX unless React is specified)",
					},
					{
						Flag:  "Docker",
						Title: "Docker",
						Desc:  "Dockerfile and docker-compose generic configuration for go project",
					},
				},
			},
			"git": {
				StepName: "Git Repository",
				Headers:  "Which git option would you like to select for your project?",
				Options: []Item{
					{
						Title: "Commit",
						Desc:  "Initialize a new git repository and commit all the changes",
					},
					{
						Title: "Stage",
						Desc:  "Initialize a new git repository but only stage the changes",
					},
					{
						Title: "Skip",
						Desc:  "Proceed without initializing a git repository",
					},
				},
			},
		},
	}

	return steps
}


================================================
FILE: cmd/template/advanced/docker.go
================================================
package advanced

import (
	_ "embed"
)

//go:embed files/docker/dockerfile.tmpl
var dockerfileTemplate []byte

//go:embed files/docker/docker_compose.yml.tmpl
var dockerComposeTemplate []byte

func Dockerfile() []byte {
	return dockerfileTemplate
}

func DockerCompose() []byte {
	return dockerComposeTemplate
}


================================================
FILE: cmd/template/advanced/files/docker/docker_compose.yml.tmpl
================================================
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
      target: prod
    restart: unless-stopped
    ports:
      - ${PORT}:${PORT}
    environment:
      APP_ENV: ${APP_ENV}
      PORT: ${PORT}
{{- if and (.AdvancedOptions.docker) (eq .DBDriver "sqlite") }}
      BLUEPRINT_DB_URL: ${BLUEPRINT_DB_URL}
    volumes:
      - sqlite_bp:/app/db
{{- end }}
{{- if .AdvancedOptions.react }}
  frontend:
    build:
      context: .
      dockerfile: Dockerfile
      target: frontend
    restart: unless-stopped
    ports:
      - 5173:5173
    depends_on:
      - app
{{- end }}

{{- if and (.AdvancedOptions.docker) (eq .DBDriver "sqlite") }}
volumes:
  sqlite_bp:
{{- end }}


================================================
FILE: cmd/template/advanced/files/docker/dockerfile.tmpl
================================================
FROM golang:1.24.4-alpine AS build
{{- if or (.AdvancedOptions.tailwind) (eq .DBDriver "sqlite") }}
RUN apk add --no-cache{{- if .AdvancedOptions.tailwind }} curl libstdc++ libgcc{{ end }}{{- if (eq .DBDriver "sqlite") }} alpine-sdk{{ end }}
{{- end }}

WORKDIR /app

COPY go.mod go.sum ./
RUN go mod download

COPY . .

{{- if or .AdvancedOptions.htmx .AdvancedOptions.tailwind }}
RUN go install github.com/a-h/templ/cmd/templ@latest && \
    templ generate{{- if .AdvancedOptions.tailwind}} && \{{- end}}
{{- end}}

{{- if .AdvancedOptions.tailwind}}
    curl -sL https://github.com/tailwindlabs/tailwindcss/releases/latest/download/tailwindcss-linux-x64-musl -o tailwindcss && \
    chmod +x tailwindcss && \
    ./tailwindcss -i cmd/web/styles/input.css -o cmd/web/assets/css/output.css
{{- end }}

RUN {{ if (eq .DBDriver "sqlite") }}CGO_ENABLED=1 GOOS=linux {{ end }}go build -o main cmd/api/main.go

FROM alpine:3.20.1 AS prod
WORKDIR /app
COPY --from=build /app/main /app/main
EXPOSE ${PORT}
CMD ["./main"]

{{ if .AdvancedOptions.react}}
FROM node:20 AS frontend_builder
WORKDIR /frontend

COPY frontend/package*.json ./
RUN npm install
COPY frontend/. .
RUN npm run build

FROM node:23-slim AS frontend
RUN npm install -g serve
COPY --from=frontend_builder /frontend/dist /app/dist
EXPOSE 5173
CMD ["serve", "-s", "/app/dist", "-l", "5173"]
{{- end}}


================================================
FILE: cmd/template/advanced/files/htmx/base.templ.tmpl
================================================
package web

templ Base() {
	<!DOCTYPE html>
	<html lang="en" {{if .AdvancedOptions.tailwind}}class="h-screen"{{end}}>
		<head>
			<meta charset="utf-8"/>
			<meta name="viewport" content="width=device-width,initial-scale=1"/>
			<title>Go Blueprint Hello</title>
			<link href="assets/css/output.css" rel="stylesheet"/>
			<script src="assets/js/htmx.min.js"></script>
		</head>
		<body {{if .AdvancedOptions.tailwind}}class="bg-gray-100"{{end}}>
			<main {{if .AdvancedOptions.tailwind}}class="max-w-sm mx-auto p-4"{{end}}>
				{ children... }
			</main>
		</body>
	</html>
}


================================================
FILE: cmd/template/advanced/files/htmx/efs.go.tmpl
================================================
package web

import "embed"

//go:embed "assets"
var Files embed.FS


================================================
FILE: cmd/template/advanced/files/htmx/hello.go.tmpl
================================================
package web

import (
	"log"
	"net/http"
)

func HelloWebHandler(w http.ResponseWriter, r *http.Request) {
	err := r.ParseForm()
	if err != nil {
		http.Error(w, "Bad Request", http.StatusBadRequest)
	}

	name := r.FormValue("name")
	component := HelloPost(name)
	err = component.Render(r.Context(), w)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		log.Fatalf("Error rendering in HelloWebHandler: %e", err)
	}
}


================================================
FILE: cmd/template/advanced/files/htmx/hello.templ.tmpl
================================================
package web

templ HelloForm() {
	@Base() {
		<form hx-post="/hello" method="POST" hx-target="#hello-container">
			<input {{if .AdvancedOptions.tailwind}}class="bg-gray-200 text-black p-2 border border-gray-400 rounded-lg"{{end}}id="name" name="name" type="text"/>
			<button type="submit" {{if .AdvancedOptions.tailwind}}class="bg-orange-500 hover:bg-orange-700 text-white py-2 px-4 rounded"{{end}}>Submit</button>
		</form>
		<div id="hello-container"></div>
	}
}

templ HelloPost(name string) {
	<div {{if .AdvancedOptions.tailwind}}class="bg-green-100 p-4 shadow-md rounded-lg mt-6"{{end}}>
		<p>Hello, { name }</p>
	</div>
}


================================================
FILE: cmd/template/advanced/files/htmx/hello_fiber.go.tmpl
================================================
package web

import (
	"bytes"
	"fmt"
	"log"

	"github.com/gofiber/fiber/v2"
)

func HelloWebHandler(c *fiber.Ctx) error {
	// Parse form data
	if err := c.BodyParser(c); err != nil {
		innerErr := c.Status(fiber.StatusBadRequest).SendString("Bad Request")
		if innerErr != nil {
			log.Fatalf("Could not send error in HelloWebHandler: %e", innerErr)
		}
	}

	// Get the name from the form data
	name := c.FormValue("name")

	// Render the component
	component := HelloPost(name)
	buf := new(bytes.Buffer)
	err := component.Render(c.Context(), buf)
	if err != nil {
		errorString := fmt.Sprintf("Error rendering in HelloWebHandler: %e", err)
		innerErr := c.Status(fiber.StatusBadRequest).SendString(errorString)
		if innerErr != nil {
			log.Fatalf("Could not send error in HelloWebHandler: %e", innerErr)
		}
		log.Fatalf("%s", errorString)
	}

	// Send the response
	err = c.Status(fiber.StatusOK).SendString(buf.String())
	if err != nil {
		log.Fatalf("Could not send OK in HelloWebHandler: %e", err)
	}
	return nil
}


================================================
FILE: cmd/template/advanced/files/htmx/htmx.min.js.tmpl
================================================
var htmx = (function () {
  "use strict";
  const Q = {
    onLoad: null,
    process: null,
    on: null,
    off: null,
    trigger: null,
    ajax: null,
    find: null,
    findAll: null,
    closest: null,
    values: function (e, t) {
      const n = dn(e, t || "post");
      return n.values;
    },
    remove: null,
    addClass: null,
    removeClass: null,
    toggleClass: null,
    takeClass: null,
    swap: null,
    defineExtension: null,
    removeExtension: null,
    logAll: null,
    logNone: null,
    logger: null,
    config: {
      historyEnabled: true,
      historyCacheSize: 10,
      refreshOnHistoryMiss: false,
      defaultSwapStyle: "innerHTML",
      defaultSwapDelay: 0,
      defaultSettleDelay: 20,
      includeIndicatorStyles: true,
      indicatorClass: "htmx-indicator",
      requestClass: "htmx-request",
      addedClass: "htmx-added",
      settlingClass: "htmx-settling",
      swappingClass: "htmx-swapping",
      allowEval: true,
      allowScriptTags: true,
      inlineScriptNonce: "",
      inlineStyleNonce: "",
      attributesToSettle: ["class", "style", "width", "height"],
      withCredentials: false,
      timeout: 0,
      wsReconnectDelay: "full-jitter",
      wsBinaryType: "blob",
      disableSelector: "[hx-disable], [data-hx-disable]",
      scrollBehavior: "instant",
      defaultFocusScroll: false,
      getCacheBusterParam: false,
      globalViewTransitions: false,
      methodsThatUseUrlParams: ["get", "delete"],
      selfRequestsOnly: true,
      ignoreTitle: false,
      scrollIntoViewOnBoost: true,
      triggerSpecsCache: null,
      disableInheritance: false,
      responseHandling: [
        { code: "204", swap: false },
        { code: "[23]..", swap: true },
        { code: "[45]..", swap: false, error: true },
      ],
      allowNestedOobSwaps: true,
      historyRestoreAsHxRequest: true,
    },
    parseInterval: null,
    location: location,
    _: null,
    version: "2.0.6",
  };
  Q.onLoad = j;
  Q.process = Ft;
  Q.on = xe;
  Q.off = be;
  Q.trigger = ae;
  Q.ajax = Ln;
  Q.find = f;
  Q.findAll = x;
  Q.closest = g;
  Q.remove = z;
  Q.addClass = K;
  Q.removeClass = G;
  Q.toggleClass = W;
  Q.takeClass = Z;
  Q.swap = $e;
  Q.defineExtension = zn;
  Q.removeExtension = $n;
  Q.logAll = V;
  Q.logNone = _;
  Q.parseInterval = d;
  Q._ = e;
  const n = {
    addTriggerHandler: St,
    bodyContains: se,
    canAccessLocalStorage: B,
    findThisElement: Se,
    filterValues: yn,
    swap: $e,
    hasAttribute: s,
    getAttributeValue: a,
    getClosestAttributeValue: ne,
    getClosestMatch: q,
    getExpressionVars: Tn,
    getHeaders: mn,
    getInputValues: dn,
    getInternalData: oe,
    getSwapSpecification: bn,
    getTriggerSpecs: st,
    getTarget: Ee,
    makeFragment: P,
    mergeObjects: le,
    makeSettleInfo: Sn,
    oobSwap: He,
    querySelectorExt: ue,
    settleImmediately: Yt,
    shouldCancel: ht,
    triggerEvent: ae,
    triggerErrorEvent: fe,
    withExtensions: jt,
  };
  const de = ["get", "post", "put", "delete", "patch"];
  const T = de
    .map(function (e) {
      return "[hx-" + e + "], [data-hx-" + e + "]";
    })
    .join(", ");
  function d(e) {
    if (e == undefined) {
      return undefined;
    }
    let t = NaN;
    if (e.slice(-2) == "ms") {
      t = parseFloat(e.slice(0, -2));
    } else if (e.slice(-1) == "s") {
      t = parseFloat(e.slice(0, -1)) * 1e3;
    } else if (e.slice(-1) == "m") {
      t = parseFloat(e.slice(0, -1)) * 1e3 * 60;
    } else {
      t = parseFloat(e);
    }
    return isNaN(t) ? undefined : t;
  }
  function ee(e, t) {
    return e instanceof Element && e.getAttribute(t);
  }
  function s(e, t) {
    return (
      !!e.hasAttribute && (e.hasAttribute(t) || e.hasAttribute("data-" + t))
    );
  }
  function a(e, t) {
    return ee(e, t) || ee(e, "data-" + t);
  }
  function u(e) {
    const t = e.parentElement;
    if (!t && e.parentNode instanceof ShadowRoot) return e.parentNode;
    return t;
  }
  function te() {
    return document;
  }
  function y(e, t) {
    return e.getRootNode ? e.getRootNode({ composed: t }) : te();
  }
  function q(e, t) {
    while (e && !t(e)) {
      e = u(e);
    }
    return e || null;
  }
  function o(e, t, n) {
    const r = a(t, n);
    const o = a(t, "hx-disinherit");
    var i = a(t, "hx-inherit");
    if (e !== t) {
      if (Q.config.disableInheritance) {
        if (i && (i === "*" || i.split(" ").indexOf(n) >= 0)) {
          return r;
        } else {
          return null;
        }
      }
      if (o && (o === "*" || o.split(" ").indexOf(n) >= 0)) {
        return "unset";
      }
    }
    return r;
  }
  function ne(t, n) {
    let r = null;
    q(t, function (e) {
      return !!(r = o(t, ce(e), n));
    });
    if (r !== "unset") {
      return r;
    }
  }
  function h(e, t) {
    return e instanceof Element && e.matches(t);
  }
  function A(e) {
    const t = /<([a-z][^\/\0>\x20\t\r\n\f]*)/i;
    const n = t.exec(e);
    if (n) {
      return n[1].toLowerCase();
    } else {
      return "";
    }
  }
  function L(e) {
    const t = new DOMParser();
    return t.parseFromString(e, "text/html");
  }
  function N(e, t) {
    while (t.childNodes.length > 0) {
      e.append(t.childNodes[0]);
    }
  }
  function r(e) {
    const t = te().createElement("script");
    ie(e.attributes, function (e) {
      t.setAttribute(e.name, e.value);
    });
    t.textContent = e.textContent;
    t.async = false;
    if (Q.config.inlineScriptNonce) {
      t.nonce = Q.config.inlineScriptNonce;
    }
    return t;
  }
  function i(e) {
    return (
      e.matches("script") &&
      (e.type === "text/javascript" || e.type === "module" || e.type === "")
    );
  }
  function I(e) {
    Array.from(e.querySelectorAll("script")).forEach((e) => {
      if (i(e)) {
        const t = r(e);
        const n = e.parentNode;
        try {
          n.insertBefore(t, e);
        } catch (e) {
          R(e);
        } finally {
          e.remove();
        }
      }
    });
  }
  function P(e) {
    const t = e.replace(/<head(\s[^>]*)?>[\s\S]*?<\/head>/i, "");
    const n = A(t);
    let r;
    if (n === "html") {
      r = new DocumentFragment();
      const i = L(e);
      N(r, i.body);
      r.title = i.title;
    } else if (n === "body") {
      r = new DocumentFragment();
      const i = L(t);
      N(r, i.body);
      r.title = i.title;
    } else {
      const i = L(
        '<body><template class="internal-htmx-wrapper">' +
          t +
          "</template></body>",
      );
      r = i.querySelector("template").content;
      r.title = i.title;
      var o = r.querySelector("title");
      if (o && o.parentNode === r) {
        o.remove();
        r.title = o.innerText;
      }
    }
    if (r) {
      if (Q.config.allowScriptTags) {
        I(r);
      } else {
        r.querySelectorAll("script").forEach((e) => e.remove());
      }
    }
    return r;
  }
  function re(e) {
    if (e) {
      e();
    }
  }
  function t(e, t) {
    return Object.prototype.toString.call(e) === "[object " + t + "]";
  }
  function D(e) {
    return typeof e === "function";
  }
  function k(e) {
    return t(e, "Object");
  }
  function oe(e) {
    const t = "htmx-internal-data";
    let n = e[t];
    if (!n) {
      n = e[t] = {};
    }
    return n;
  }
  function M(t) {
    const n = [];
    if (t) {
      for (let e = 0; e < t.length; e++) {
        n.push(t[e]);
      }
    }
    return n;
  }
  function ie(t, n) {
    if (t) {
      for (let e = 0; e < t.length; e++) {
        n(t[e]);
      }
    }
  }
  function F(e) {
    const t = e.getBoundingClientRect();
    const n = t.top;
    const r = t.bottom;
    return n < window.innerHeight && r >= 0;
  }
  function se(e) {
    return e.getRootNode({ composed: true }) === document;
  }
  function X(e) {
    return e.trim().split(/\s+/);
  }
  function le(e, t) {
    for (const n in t) {
      if (t.hasOwnProperty(n)) {
        e[n] = t[n];
      }
    }
    return e;
  }
  function v(e) {
    try {
      return JSON.parse(e);
    } catch (e) {
      R(e);
      return null;
    }
  }
  function B() {
    const e = "htmx:sessionStorageTest";
    try {
      sessionStorage.setItem(e, e);
      sessionStorage.removeItem(e);
      return true;
    } catch (e) {
      return false;
    }
  }
  function U(e) {
    const t = new URL(e, "http://x");
    if (t) {
      e = t.pathname + t.search;
    }
    if (e != "/") {
      e = e.replace(/\/+$/, "");
    }
    return e;
  }
  function e(e) {
    return On(te().body, function () {
      return eval(e);
    });
  }
  function j(t) {
    const e = Q.on("htmx:load", function (e) {
      t(e.detail.elt);
    });
    return e;
  }
  function V() {
    Q.logger = function (e, t, n) {
      if (console) {
        console.log(t, e, n);
      }
    };
  }
  function _() {
    Q.logger = null;
  }
  function f(e, t) {
    if (typeof e !== "string") {
      return e.querySelector(t);
    } else {
      return f(te(), e);
    }
  }
  function x(e, t) {
    if (typeof e !== "string") {
      return e.querySelectorAll(t);
    } else {
      return x(te(), e);
    }
  }
  function b() {
    return window;
  }
  function z(e, t) {
    e = w(e);
    if (t) {
      b().setTimeout(function () {
        z(e);
        e = null;
      }, t);
    } else {
      u(e).removeChild(e);
    }
  }
  function ce(e) {
    return e instanceof Element ? e : null;
  }
  function $(e) {
    return e instanceof HTMLElement ? e : null;
  }
  function J(e) {
    return typeof e === "string" ? e : null;
  }
  function p(e) {
    return e instanceof Element ||
      e instanceof Document ||
      e instanceof DocumentFragment
      ? e
      : null;
  }
  function K(e, t, n) {
    e = ce(w(e));
    if (!e) {
      return;
    }
    if (n) {
      b().setTimeout(function () {
        K(e, t);
        e = null;
      }, n);
    } else {
      e.classList && e.classList.add(t);
    }
  }
  function G(e, t, n) {
    let r = ce(w(e));
    if (!r) {
      return;
    }
    if (n) {
      b().setTimeout(function () {
        G(r, t);
        r = null;
      }, n);
    } else {
      if (r.classList) {
        r.classList.remove(t);
        if (r.classList.length === 0) {
          r.removeAttribute("class");
        }
      }
    }
  }
  function W(e, t) {
    e = w(e);
    e.classList.toggle(t);
  }
  function Z(e, t) {
    e = w(e);
    ie(e.parentElement.children, function (e) {
      G(e, t);
    });
    K(ce(e), t);
  }
  function g(e, t) {
    e = ce(w(e));
    if (e) {
      return e.closest(t);
    }
    return null;
  }
  function l(e, t) {
    return e.substring(0, t.length) === t;
  }
  function Y(e, t) {
    return e.substring(e.length - t.length) === t;
  }
  function pe(e) {
    const t = e.trim();
    if (l(t, "<") && Y(t, "/>")) {
      return t.substring(1, t.length - 2);
    } else {
      return t;
    }
  }
  function m(t, r, n) {
    if (r.indexOf("global ") === 0) {
      return m(t, r.slice(7), true);
    }
    t = w(t);
    const o = [];
    {
      let t = 0;
      let n = 0;
      for (let e = 0; e < r.length; e++) {
        const l = r[e];
        if (l === "," && t === 0) {
          o.push(r.substring(n, e));
          n = e + 1;
          continue;
        }
        if (l === "<") {
          t++;
        } else if (l === "/" && e < r.length - 1 && r[e + 1] === ">") {
          t--;
        }
      }
      if (n < r.length) {
        o.push(r.substring(n));
      }
    }
    const i = [];
    const s = [];
    while (o.length > 0) {
      const r = pe(o.shift());
      let e;
      if (r.indexOf("closest ") === 0) {
        e = g(ce(t), pe(r.slice(8)));
      } else if (r.indexOf("find ") === 0) {
        e = f(p(t), pe(r.slice(5)));
      } else if (r === "next" || r === "nextElementSibling") {
        e = ce(t).nextElementSibling;
      } else if (r.indexOf("next ") === 0) {
        e = ge(t, pe(r.slice(5)), !!n);
      } else if (r === "previous" || r === "previousElementSibling") {
        e = ce(t).previousElementSibling;
      } else if (r.indexOf("previous ") === 0) {
        e = me(t, pe(r.slice(9)), !!n);
      } else if (r === "document") {
        e = document;
      } else if (r === "window") {
        e = window;
      } else if (r === "body") {
        e = document.body;
      } else if (r === "root") {
        e = y(t, !!n);
      } else if (r === "host") {
        e = t.getRootNode().host;
      } else {
        s.push(r);
      }
      if (e) {
        i.push(e);
      }
    }
    if (s.length > 0) {
      const e = s.join(",");
      const c = p(y(t, !!n));
      i.push(...M(c.querySelectorAll(e)));
    }
    return i;
  }
  var ge = function (t, e, n) {
    const r = p(y(t, n)).querySelectorAll(e);
    for (let e = 0; e < r.length; e++) {
      const o = r[e];
      if (o.compareDocumentPosition(t) === Node.DOCUMENT_POSITION_PRECEDING) {
        return o;
      }
    }
  };
  var me = function (t, e, n) {
    const r = p(y(t, n)).querySelectorAll(e);
    for (let e = r.length - 1; e >= 0; e--) {
      const o = r[e];
      if (o.compareDocumentPosition(t) === Node.DOCUMENT_POSITION_FOLLOWING) {
        return o;
      }
    }
  };
  function ue(e, t) {
    if (typeof e !== "string") {
      return m(e, t)[0];
    } else {
      return m(te().body, e)[0];
    }
  }
  function w(e, t) {
    if (typeof e === "string") {
      return f(p(t) || document, e);
    } else {
      return e;
    }
  }
  function ye(e, t, n, r) {
    if (D(t)) {
      return { target: te().body, event: J(e), listener: t, options: n };
    } else {
      return { target: w(e), event: J(t), listener: n, options: r };
    }
  }
  function xe(t, n, r, o) {
    Gn(function () {
      const e = ye(t, n, r, o);
      e.target.addEventListener(e.event, e.listener, e.options);
    });
    const e = D(n);
    return e ? n : r;
  }
  function be(t, n, r) {
    Gn(function () {
      const e = ye(t, n, r);
      e.target.removeEventListener(e.event, e.listener);
    });
    return D(n) ? n : r;
  }
  const ve = te().createElement("output");
  function we(t, n) {
    const e = ne(t, n);
    if (e) {
      if (e === "this") {
        return [Se(t, n)];
      } else {
        const r = m(t, e);
        const o = /(^|,)(\s*)inherit(\s*)($|,)/.test(e);
        if (o) {
          const i = ce(
            q(t, function (e) {
              return e !== t && s(ce(e), n);
            }),
          );
          if (i) {
            r.push(...we(i, n));
          }
        }
        if (r.length === 0) {
          R('The selector "' + e + '" on ' + n + " returned no matches!");
          return [ve];
        } else {
          return r;
        }
      }
    }
  }
  function Se(e, t) {
    return ce(
      q(e, function (e) {
        return a(ce(e), t) != null;
      }),
    );
  }
  function Ee(e) {
    const t = ne(e, "hx-target");
    if (t) {
      if (t === "this") {
        return Se(e, "hx-target");
      } else {
        return ue(e, t);
      }
    } else {
      const n = oe(e);
      if (n.boosted) {
        return te().body;
      } else {
        return e;
      }
    }
  }
  function Ce(e) {
    return Q.config.attributesToSettle.includes(e);
  }
  function Oe(t, n) {
    ie(t.attributes, function (e) {
      if (!n.hasAttribute(e.name) && Ce(e.name)) {
        t.removeAttribute(e.name);
      }
    });
    ie(n.attributes, function (e) {
      if (Ce(e.name)) {
        t.setAttribute(e.name, e.value);
      }
    });
  }
  function Re(t, e) {
    const n = Jn(e);
    for (let e = 0; e < n.length; e++) {
      const r = n[e];
      try {
        if (r.isInlineSwap(t)) {
          return true;
        }
      } catch (e) {
        R(e);
      }
    }
    return t === "outerHTML";
  }
  function He(e, o, i, t) {
    t = t || te();
    let n = "#" + CSS.escape(ee(o, "id"));
    let s = "outerHTML";
    if (e === "true") {
    } else if (e.indexOf(":") > 0) {
      s = e.substring(0, e.indexOf(":"));
      n = e.substring(e.indexOf(":") + 1);
    } else {
      s = e;
    }
    o.removeAttribute("hx-swap-oob");
    o.removeAttribute("data-hx-swap-oob");
    const r = m(t, n, false);
    if (r.length) {
      ie(r, function (e) {
        let t;
        const n = o.cloneNode(true);
        t = te().createDocumentFragment();
        t.appendChild(n);
        if (!Re(s, e)) {
          t = p(n);
        }
        const r = { shouldSwap: true, target: e, fragment: t };
        if (!ae(e, "htmx:oobBeforeSwap", r)) return;
        e = r.target;
        if (r.shouldSwap) {
          qe(t);
          _e(s, e, e, t, i);
          Te();
        }
        ie(i.elts, function (e) {
          ae(e, "htmx:oobAfterSwap", r);
        });
      });
      o.parentNode.removeChild(o);
    } else {
      o.parentNode.removeChild(o);
      fe(te().body, "htmx:oobErrorNoTarget", { content: o });
    }
    return e;
  }
  function Te() {
    const e = f("#--htmx-preserve-pantry--");
    if (e) {
      for (const t of [...e.children]) {
        const n = f("#" + t.id);
        n.parentNode.moveBefore(t, n);
        n.remove();
      }
      e.remove();
    }
  }
  function qe(e) {
    ie(x(e, "[hx-preserve], [data-hx-preserve]"), function (e) {
      const t = a(e, "id");
      const n = te().getElementById(t);
      if (n != null) {
        if (e.moveBefore) {
          let e = f("#--htmx-preserve-pantry--");
          if (e == null) {
            te().body.insertAdjacentHTML(
              "afterend",
              "<div id='--htmx-preserve-pantry--'></div>",
            );
            e = f("#--htmx-preserve-pantry--");
          }
          e.moveBefore(n, null);
        } else {
          e.parentNode.replaceChild(n, e);
        }
      }
    });
  }
  function Ae(l, e, c) {
    ie(e.querySelectorAll("[id]"), function (t) {
      const n = ee(t, "id");
      if (n && n.length > 0) {
        const r = n.replace("'", "\\'");
        const o = t.tagName.replace(":", "\\:");
        const e = p(l);
        const i = e && e.querySelector(o + "[id='" + r + "']");
        if (i && i !== e) {
          const s = t.cloneNode();
          Oe(t, i);
          c.tasks.push(function () {
            Oe(t, s);
          });
        }
      }
    });
  }
  function Le(e) {
    return function () {
      G(e, Q.config.addedClass);
      Ft(ce(e));
      Ne(p(e));
      ae(e, "htmx:load");
    };
  }
  function Ne(e) {
    const t = "[autofocus]";
    const n = $(h(e, t) ? e : e.querySelector(t));
    if (n != null) {
      n.focus();
    }
  }
  function c(e, t, n, r) {
    Ae(e, n, r);
    while (n.childNodes.length > 0) {
      const o = n.firstChild;
      K(ce(o), Q.config.addedClass);
      e.insertBefore(o, t);
      if (o.nodeType !== Node.TEXT_NODE && o.nodeType !== Node.COMMENT_NODE) {
        r.tasks.push(Le(o));
      }
    }
  }
  function Ie(e, t) {
    let n = 0;
    while (n < e.length) {
      t = ((t << 5) - t + e.charCodeAt(n++)) | 0;
    }
    return t;
  }
  function Pe(t) {
    let n = 0;
    for (let e = 0; e < t.attributes.length; e++) {
      const r = t.attributes[e];
      if (r.value) {
        n = Ie(r.name, n);
        n = Ie(r.value, n);
      }
    }
    return n;
  }
  function De(t) {
    const n = oe(t);
    if (n.onHandlers) {
      for (let e = 0; e < n.onHandlers.length; e++) {
        const r = n.onHandlers[e];
        be(t, r.event, r.listener);
      }
      delete n.onHandlers;
    }
  }
  function ke(e) {
    const t = oe(e);
    if (t.timeout) {
      clearTimeout(t.timeout);
    }
    if (t.listenerInfos) {
      ie(t.listenerInfos, function (e) {
        if (e.on) {
          be(e.on, e.trigger, e.listener);
        }
      });
    }
    De(e);
    ie(Object.keys(t), function (e) {
      if (e !== "firstInitCompleted") delete t[e];
    });
  }
  function S(e) {
    ae(e, "htmx:beforeCleanupElement");
    ke(e);
    ie(e.children, function (e) {
      S(e);
    });
  }
  function Me(t, e, n) {
    if (t.tagName === "BODY") {
      return Ve(t, e, n);
    }
    let r;
    const o = t.previousSibling;
    const i = u(t);
    if (!i) {
      return;
    }
    c(i, t, e, n);
    if (o == null) {
      r = i.firstChild;
    } else {
      r = o.nextSibling;
    }
    n.elts = n.elts.filter(function (e) {
      return e !== t;
    });
    while (r && r !== t) {
      if (r instanceof Element) {
        n.elts.push(r);
      }
      r = r.nextSibling;
    }
    S(t);
    t.remove();
  }
  function Fe(e, t, n) {
    return c(e, e.firstChild, t, n);
  }
  function Xe(e, t, n) {
    return c(u(e), e, t, n);
  }
  function Be(e, t, n) {
    return c(e, null, t, n);
  }
  function Ue(e, t, n) {
    return c(u(e), e.nextSibling, t, n);
  }
  function je(e) {
    S(e);
    const t = u(e);
    if (t) {
      return t.removeChild(e);
    }
  }
  function Ve(e, t, n) {
    const r = e.firstChild;
    c(e, r, t, n);
    if (r) {
      while (r.nextSibling) {
        S(r.nextSibling);
        e.removeChild(r.nextSibling);
      }
      S(r);
      e.removeChild(r);
    }
  }
  function _e(t, e, n, r, o) {
    switch (t) {
      case "none":
        return;
      case "outerHTML":
        Me(n, r, o);
        return;
      case "afterbegin":
        Fe(n, r, o);
        return;
      case "beforebegin":
        Xe(n, r, o);
        return;
      case "beforeend":
        Be(n, r, o);
        return;
      case "afterend":
        Ue(n, r, o);
        return;
      case "delete":
        je(n);
        return;
      default:
        var i = Jn(e);
        for (let e = 0; e < i.length; e++) {
          const s = i[e];
          try {
            const l = s.handleSwap(t, n, r, o);
            if (l) {
              if (Array.isArray(l)) {
                for (let e = 0; e < l.length; e++) {
                  const c = l[e];
                  if (
                    c.nodeType !== Node.TEXT_NODE &&
                    c.nodeType !== Node.COMMENT_NODE
                  ) {
                    o.tasks.push(Le(c));
                  }
                }
              }
              return;
            }
          } catch (e) {
            R(e);
          }
        }
        if (t === "innerHTML") {
          Ve(n, r, o);
        } else {
          _e(Q.config.defaultSwapStyle, e, n, r, o);
        }
    }
  }
  function ze(e, n, r) {
    var t = x(e, "[hx-swap-oob], [data-hx-swap-oob]");
    ie(t, function (e) {
      if (Q.config.allowNestedOobSwaps || e.parentElement === null) {
        const t = a(e, "hx-swap-oob");
        if (t != null) {
          He(t, e, n, r);
        }
      } else {
        e.removeAttribute("hx-swap-oob");
        e.removeAttribute("data-hx-swap-oob");
      }
    });
    return t.length > 0;
  }
  function $e(h, d, p, g) {
    if (!g) {
      g = {};
    }
    let m = null;
    let n = null;
    let e = function () {
      re(g.beforeSwapCallback);
      h = w(h);
      const r = g.contextElement ? y(g.contextElement, false) : te();
      const e = document.activeElement;
      let t = {};
      t = {
        elt: e,
        start: e ? e.selectionStart : null,
        end: e ? e.selectionEnd : null,
      };
      const o = Sn(h);
      if (p.swapStyle === "textContent") {
        h.textContent = d;
      } else {
        let n = P(d);
        o.title = g.title || n.title;
        if (g.historyRequest) {
          n = n.querySelector("[hx-history-elt],[data-hx-history-elt]") || n;
        }
        if (g.selectOOB) {
          const i = g.selectOOB.split(",");
          for (let t = 0; t < i.length; t++) {
            const s = i[t].split(":", 2);
            let e = s[0].trim();
            if (e.indexOf("#") === 0) {
              e = e.substring(1);
            }
            const l = s[1] || "true";
            const c = n.querySelector("#" + e);
            if (c) {
              He(l, c, o, r);
            }
          }
        }
        ze(n, o, r);
        ie(x(n, "template"), function (e) {
          if (e.content && ze(e.content, o, r)) {
            e.remove();
          }
        });
        if (g.select) {
          const u = te().createDocumentFragment();
          ie(n.querySelectorAll(g.select), function (e) {
            u.appendChild(e);
          });
          n = u;
        }
        qe(n);
        _e(p.swapStyle, g.contextElement, h, n, o);
        Te();
      }
      if (t.elt && !se(t.elt) && ee(t.elt, "id")) {
        const f = document.getElementById(ee(t.elt, "id"));
        const a = {
          preventScroll:
            p.focusScroll !== undefined
              ? !p.focusScroll
              : !Q.config.defaultFocusScroll,
        };
        if (f) {
          if (t.start && f.setSelectionRange) {
            try {
              f.setSelectionRange(t.start, t.end);
            } catch (e) {}
          }
          f.focus(a);
        }
      }
      h.classList.remove(Q.config.swappingClass);
      ie(o.elts, function (e) {
        if (e.classList) {
          e.classList.add(Q.config.settlingClass);
        }
        ae(e, "htmx:afterSwap", g.eventInfo);
      });
      re(g.afterSwapCallback);
      if (!p.ignoreTitle) {
        Bn(o.title);
      }
      const n = function () {
        ie(o.tasks, function (e) {
          e.call();
        });
        ie(o.elts, function (e) {
          if (e.classList) {
            e.classList.remove(Q.config.settlingClass);
          }
          ae(e, "htmx:afterSettle", g.eventInfo);
        });
        if (g.anchor) {
          const e = ce(w("#" + g.anchor));
          if (e) {
            e.scrollIntoView({ block: "start", behavior: "auto" });
          }
        }
        En(o.elts, p);
        re(g.afterSettleCallback);
        re(m);
      };
      if (p.settleDelay > 0) {
        b().setTimeout(n, p.settleDelay);
      } else {
        n();
      }
    };
    let t = Q.config.globalViewTransitions;
    if (p.hasOwnProperty("transition")) {
      t = p.transition;
    }
    const r = g.contextElement || te();
    if (
      t &&
      ae(r, "htmx:beforeTransition", g.eventInfo) &&
      typeof Promise !== "undefined" &&
      document.startViewTransition
    ) {
      const o = new Promise(function (e, t) {
        m = e;
        n = t;
      });
      const i = e;
      e = function () {
        document.startViewTransition(function () {
          i();
          return o;
        });
      };
    }
    try {
      if (p?.swapDelay && p.swapDelay > 0) {
        b().setTimeout(e, p.swapDelay);
      } else {
        e();
      }
    } catch (e) {
      fe(r, "htmx:swapError", g.eventInfo);
      re(n);
      throw e;
    }
  }
  function Je(e, t, n) {
    const r = e.getResponseHeader(t);
    if (r.indexOf("{") === 0) {
      const o = v(r);
      for (const i in o) {
        if (o.hasOwnProperty(i)) {
          let e = o[i];
          if (k(e)) {
            n = e.target !== undefined ? e.target : n;
          } else {
            e = { value: e };
          }
          ae(n, i, e);
        }
      }
    } else {
      const s = r.split(",");
      for (let e = 0; e < s.length; e++) {
        ae(n, s[e].trim(), []);
      }
    }
  }
  const Ke = /\s/;
  const E = /[\s,]/;
  const Ge = /[_$a-zA-Z]/;
  const We = /[_$a-zA-Z0-9]/;
  const Ze = ['"', "'", "/"];
  const C = /[^\s]/;
  const Ye = /[{(]/;
  const Qe = /[})]/;
  function et(e) {
    const t = [];
    let n = 0;
    while (n < e.length) {
      if (Ge.exec(e.charAt(n))) {
        var r = n;
        while (We.exec(e.charAt(n + 1))) {
          n++;
        }
        t.push(e.substring(r, n + 1));
      } else if (Ze.indexOf(e.charAt(n)) !== -1) {
        const o = e.charAt(n);
        var r = n;
        n++;
        while (n < e.length && e.charAt(n) !== o) {
          if (e.charAt(n) === "\\") {
            n++;
          }
          n++;
        }
        t.push(e.substring(r, n + 1));
      } else {
        const i = e.charAt(n);
        t.push(i);
      }
      n++;
    }
    return t;
  }
  function tt(e, t, n) {
    return (
      Ge.exec(e.charAt(0)) &&
      e !== "true" &&
      e !== "false" &&
      e !== "this" &&
      e !== n &&
      t !== "."
    );
  }
  function nt(r, o, i) {
    if (o[0] === "[") {
      o.shift();
      let e = 1;
      let t = " return (function(" + i + "){ return (";
      let n = null;
      while (o.length > 0) {
        const s = o[0];
        if (s === "]") {
          e--;
          if (e === 0) {
            if (n === null) {
              t = t + "true";
            }
            o.shift();
            t += ")})";
            try {
              const l = On(
                r,
                function () {
                  return Function(t)();
                },
                function () {
                  return true;
                },
              );
              l.source = t;
              return l;
            } catch (e) {
              fe(te().body, "htmx:syntax:error", { error: e, source: t });
              return null;
            }
          }
        } else if (s === "[") {
          e++;
        }
        if (tt(s, n, i)) {
          t +=
            "((" +
            i +
            "." +
            s +
            ") ? (" +
            i +
            "." +
            s +
            ") : (window." +
            s +
            "))";
        } else {
          t = t + s;
        }
        n = o.shift();
      }
    }
  }
  function O(e, t) {
    let n = "";
    while (e.length > 0 && !t.test(e[0])) {
      n += e.shift();
    }
    return n;
  }
  function rt(e) {
    let t;
    if (e.length > 0 && Ye.test(e[0])) {
      e.shift();
      t = O(e, Qe).trim();
      e.shift();
    } else {
      t = O(e, E);
    }
    return t;
  }
  const ot = "input, textarea, select";
  function it(e, t, n) {
    const r = [];
    const o = et(t);
    do {
      O(o, C);
      const l = o.length;
      const c = O(o, /[,\[\s]/);
      if (c !== "") {
        if (c === "every") {
          const u = { trigger: "every" };
          O(o, C);
          u.pollInterval = d(O(o, /[,\[\s]/));
          O(o, C);
          var i = nt(e, o, "event");
          if (i) {
            u.eventFilter = i;
          }
          r.push(u);
        } else {
          const f = { trigger: c };
          var i = nt(e, o, "event");
          if (i) {
            f.eventFilter = i;
          }
          O(o, C);
          while (o.length > 0 && o[0] !== ",") {
            const a = o.shift();
            if (a === "changed") {
              f.changed = true;
            } else if (a === "once") {
              f.once = true;
            } else if (a === "consume") {
              f.consume = true;
            } else if (a === "delay" && o[0] === ":") {
              o.shift();
              f.delay = d(O(o, E));
            } else if (a === "from" && o[0] === ":") {
              o.shift();
              if (Ye.test(o[0])) {
                var s = rt(o);
              } else {
                var s = O(o, E);
                if (
                  s === "closest" ||
                  s === "find" ||
                  s === "next" ||
                  s === "previous"
                ) {
                  o.shift();
                  const h = rt(o);
                  if (h.length > 0) {
                    s += " " + h;
                  }
                }
              }
              f.from = s;
            } else if (a === "target" && o[0] === ":") {
              o.shift();
              f.target = rt(o);
            } else if (a === "throttle" && o[0] === ":") {
              o.shift();
              f.throttle = d(O(o, E));
            } else if (a === "queue" && o[0] === ":") {
              o.shift();
              f.queue = O(o, E);
            } else if (a === "root" && o[0] === ":") {
              o.shift();
              f[a] = rt(o);
            } else if (a === "threshold" && o[0] === ":") {
              o.shift();
              f[a] = O(o, E);
            } else {
              fe(e, "htmx:syntax:error", { token: o.shift() });
            }
            O(o, C);
          }
          r.push(f);
        }
      }
      if (o.length === l) {
        fe(e, "htmx:syntax:error", { token: o.shift() });
      }
      O(o, C);
    } while (o[0] === "," && o.shift());
    if (n) {
      n[t] = r;
    }
    return r;
  }
  function st(e) {
    const t = a(e, "hx-trigger");
    let n = [];
    if (t) {
      const r = Q.config.triggerSpecsCache;
      n = (r && r[t]) || it(e, t, r);
    }
    if (n.length > 0) {
      return n;
    } else if (h(e, "form")) {
      return [{ trigger: "submit" }];
    } else if (h(e, 'input[type="button"], input[type="submit"]')) {
      return [{ trigger: "click" }];
    } else if (h(e, ot)) {
      return [{ trigger: "change" }];
    } else {
      return [{ trigger: "click" }];
    }
  }
  function lt(e) {
    oe(e).cancelled = true;
  }
  function ct(e, t, n) {
    const r = oe(e);
    r.timeout = b().setTimeout(function () {
      if (se(e) && r.cancelled !== true) {
        if (!pt(n, e, Bt("hx:poll:trigger", { triggerSpec: n, target: e }))) {
          t(e);
        }
        ct(e, t, n);
      }
    }, n.pollInterval);
  }
  function ut(e) {
    return (
      location.hostname === e.hostname &&
      ee(e, "href") &&
      ee(e, "href").indexOf("#") !== 0
    );
  }
  function ft(e) {
    return g(e, Q.config.disableSelector);
  }
  function at(t, n, e) {
    if (
      (t instanceof HTMLAnchorElement &&
        ut(t) &&
        (t.target === "" || t.target === "_self")) ||
      (t.tagName === "FORM" &&
        String(ee(t, "method")).toLowerCase() !== "dialog")
    ) {
      n.boosted = true;
      let r, o;
      if (t.tagName === "A") {
        r = "get";
        o = ee(t, "href");
      } else {
        const i = ee(t, "method");
        r = i ? i.toLowerCase() : "get";
        o = ee(t, "action");
        if (o == null || o === "") {
          o = location.href;
        }
        if (r === "get" && o.includes("?")) {
          o = o.replace(/\?[^#]+/, "");
        }
      }
      e.forEach(function (e) {
        gt(
          t,
          function (e, t) {
            const n = ce(e);
            if (ft(n)) {
              S(n);
              return;
            }
            he(r, o, n, t);
          },
          n,
          e,
          true,
        );
      });
    }
  }
  function ht(e, t) {
    if (e.type === "submit" || e.type === "click") {
      t = ce(e.target) || t;
      if (t.tagName === "FORM") {
        return true;
      }
      if (t.form && t.type === "submit") {
        return true;
      }
      t = t.closest("a");
      if (
        t &&
        t.href &&
        (t.getAttribute("href") === "#" ||
          t.getAttribute("href").indexOf("#") !== 0)
      ) {
        return true;
      }
    }
    return false;
  }
  function dt(e, t) {
    return (
      oe(e).boosted &&
      e instanceof HTMLAnchorElement &&
      t.type === "click" &&
      (t.ctrlKey || t.metaKey)
    );
  }
  function pt(e, t, n) {
    const r = e.eventFilter;
    if (r) {
      try {
        return r.call(t, n) !== true;
      } catch (e) {
        const o = r.source;
        fe(te().body, "htmx:eventFilter:error", { error: e, source: o });
        return true;
      }
    }
    return false;
  }
  function gt(l, c, e, u, f) {
    const a = oe(l);
    let t;
    if (u.from) {
      t = m(l, u.from);
    } else {
      t = [l];
    }
    if (u.changed) {
      if (!("lastValue" in a)) {
        a.lastValue = new WeakMap();
      }
      t.forEach(function (e) {
        if (!a.lastValue.has(u)) {
          a.lastValue.set(u, new WeakMap());
        }
        a.lastValue.get(u).set(e, e.value);
      });
    }
    ie(t, function (i) {
      const s = function (e) {
        if (!se(l)) {
          i.removeEventListener(u.trigger, s);
          return;
        }
        if (dt(l, e)) {
          return;
        }
        if (f || ht(e, l)) {
          e.preventDefault();
        }
        if (pt(u, l, e)) {
          return;
        }
        const t = oe(e);
        t.triggerSpec = u;
        if (t.handledFor == null) {
          t.handledFor = [];
        }
        if (t.handledFor.indexOf(l) < 0) {
          t.handledFor.push(l);
          if (u.consume) {
            e.stopPropagation();
          }
          if (u.target && e.target) {
            if (!h(ce(e.target), u.target)) {
              return;
            }
          }
          if (u.once) {
            if (a.triggeredOnce) {
              return;
            } else {
              a.triggeredOnce = true;
            }
          }
          if (u.changed) {
            const n = e.target;
            const r = n.value;
            const o = a.lastValue.get(u);
            if (o.has(n) && o.get(n) === r) {
              return;
            }
            o.set(n, r);
          }
          if (a.delayed) {
            clearTimeout(a.delayed);
          }
          if (a.throttle) {
            return;
          }
          if (u.throttle > 0) {
            if (!a.throttle) {
              ae(l, "htmx:trigger");
              c(l, e);
              a.throttle = b().setTimeout(function () {
                a.throttle = null;
              }, u.throttle);
            }
          } else if (u.delay > 0) {
            a.delayed = b().setTimeout(function () {
              ae(l, "htmx:trigger");
              c(l, e);
            }, u.delay);
          } else {
            ae(l, "htmx:trigger");
            c(l, e);
          }
        }
      };
      if (e.listenerInfos == null) {
        e.listenerInfos = [];
      }
      e.listenerInfos.push({ trigger: u.trigger, listener: s, on: i });
      i.addEventListener(u.trigger, s);
    });
  }
  let mt = false;
  let yt = null;
  function xt() {
    if (!yt) {
      yt = function () {
        mt = true;
      };
      window.addEventListener("scroll", yt);
      window.addEventListener("resize", yt);
      setInterval(function () {
        if (mt) {
          mt = false;
          ie(
            te().querySelectorAll(
              "[hx-trigger*='revealed'],[data-hx-trigger*='revealed']",
            ),
            function (e) {
              bt(e);
            },
          );
        }
      }, 200);
    }
  }
  function bt(e) {
    if (!s(e, "data-hx-revealed") && F(e)) {
      e.setAttribute("data-hx-revealed", "true");
      const t = oe(e);
      if (t.initHash) {
        ae(e, "revealed");
      } else {
        e.addEventListener(
          "htmx:afterProcessNode",
          function () {
            ae(e, "revealed");
          },
          { once: true },
        );
      }
    }
  }
  function vt(e, t, n, r) {
    const o = function () {
      if (!n.loaded) {
        n.loaded = true;
        ae(e, "htmx:trigger");
        t(e);
      }
    };
    if (r > 0) {
      b().setTimeout(o, r);
    } else {
      o();
    }
  }
  function wt(t, n, e) {
    let i = false;
    ie(de, function (r) {
      if (s(t, "hx-" + r)) {
        const o = a(t, "hx-" + r);
        i = true;
        n.path = o;
        n.verb = r;
        e.forEach(function (e) {
          St(t, e, n, function (e, t) {
            const n = ce(e);
            if (ft(n)) {
              S(n);
              return;
            }
            he(r, o, n, t);
          });
        });
      }
    });
    return i;
  }
  function St(r, e, t, n) {
    if (e.trigger === "revealed") {
      xt();
      gt(r, n, t, e);
      bt(ce(r));
    } else if (e.trigger === "intersect") {
      const o = {};
      if (e.root) {
        o.root = ue(r, e.root);
      }
      if (e.threshold) {
        o.threshold = parseFloat(e.threshold);
      }
      const i = new IntersectionObserver(function (t) {
        for (let e = 0; e < t.length; e++) {
          const n = t[e];
          if (n.isIntersecting) {
            ae(r, "intersect");
            break;
          }
        }
      }, o);
      i.observe(ce(r));
      gt(ce(r), n, t, e);
    } else if (!t.firstInitCompleted && e.trigger === "load") {
      if (!pt(e, r, Bt("load", { elt: r }))) {
        vt(ce(r), n, t, e.delay);
      }
    } else if (e.pollInterval > 0) {
      t.polling = true;
      ct(ce(r), n, e);
    } else {
      gt(r, n, t, e);
    }
  }
  function Et(e) {
    const t = ce(e);
    if (!t) {
      return false;
    }
    const n = t.attributes;
    for (let e = 0; e < n.length; e++) {
      const r = n[e].name;
      if (
        l(r, "hx-on:") ||
        l(r, "data-hx-on:") ||
        l(r, "hx-on-") ||
        l(r, "data-hx-on-")
      ) {
        return true;
      }
    }
    return false;
  }
  const Ct = new XPathEvaluator().createExpression(
    './/*[@*[ starts-with(name(), "hx-on:") or starts-with(name(), "data-hx-on:") or' +
      ' starts-with(name(), "hx-on-") or starts-with(name(), "data-hx-on-") ]]',
  );
  function Ot(e, t) {
    if (Et(e)) {
      t.push(ce(e));
    }
    const n = Ct.evaluate(e);
    let r = null;
    while ((r = n.iterateNext())) t.push(ce(r));
  }
  function Rt(e) {
    const t = [];
    if (e instanceof DocumentFragment) {
      for (const n of e.childNodes) {
        Ot(n, t);
      }
    } else {
      Ot(e, t);
    }
    return t;
  }
  function Ht(e) {
    if (e.querySelectorAll) {
      const n =
        ", [hx-boost] a, [data-hx-boost] a, a[hx-boost], a[data-hx-boost]";
      const r = [];
      for (const i in Vn) {
        const s = Vn[i];
        if (s.getSelectors) {
          var t = s.getSelectors();
          if (t) {
            r.push(t);
          }
        }
      }
      const o = e.querySelectorAll(
        T +
          n +
          ", form, [type='submit']," +
          " [hx-ext], [data-hx-ext], [hx-trigger], [data-hx-trigger]" +
          r
            .flat()
            .map((e) => ", " + e)
            .join(""),
      );
      return o;
    } else {
      return [];
    }
  }
  function Tt(e) {
    const t = At(e.target);
    const n = Nt(e);
    if (n) {
      n.lastButtonClicked = t;
    }
  }
  function qt(e) {
    const t = Nt(e);
    if (t) {
      t.lastButtonClicked = null;
    }
  }
  function At(e) {
    return g(ce(e), "button, input[type='submit']");
  }
  function Lt(e) {
    return e.form || g(e, "form");
  }
  function Nt(e) {
    const t = At(e.target);
    if (!t) {
      return;
    }
    const n = Lt(t);
    return oe(n);
  }
  function It(e) {
    e.addEventListener("click", Tt);
    e.addEventListener("focusin", Tt);
    e.addEventListener("focusout", qt);
  }
  function Pt(t, e, n) {
    const r = oe(t);
    if (!Array.isArray(r.onHandlers)) {
      r.onHandlers = [];
    }
    let o;
    const i = function (e) {
      On(t, function () {
        if (ft(t)) {
          return;
        }
        if (!o) {
          o = new Function("event", n);
        }
        o.call(t, e);
      });
    };
    t.addEventListener(e, i);
    r.onHandlers.push({ event: e, listener: i });
  }
  function Dt(t) {
    De(t);
    for (let e = 0; e < t.attributes.length; e++) {
      const n = t.attributes[e].name;
      const r = t.attributes[e].value;
      if (l(n, "hx-on") || l(n, "data-hx-on")) {
        const o = n.indexOf("-on") + 3;
        const i = n.slice(o, o + 1);
        if (i === "-" || i === ":") {
          let e = n.slice(o + 1);
          if (l(e, ":")) {
            e = "htmx" + e;
          } else if (l(e, "-")) {
            e = "htmx:" + e.slice(1);
          } else if (l(e, "htmx-")) {
            e = "htmx:" + e.slice(5);
          }
          Pt(t, e, r);
        }
      }
    }
  }
  function kt(t) {
    ae(t, "htmx:beforeProcessNode");
    const n = oe(t);
    const e = st(t);
    const r = wt(t, n, e);
    if (!r) {
      if (ne(t, "hx-boost") === "true") {
        at(t, n, e);
      } else if (s(t, "hx-trigger")) {
        e.forEach(function (e) {
          St(t, e, n, function () {});
        });
      }
    }
    if (t.tagName === "FORM" || (ee(t, "type") === "submit" && s(t, "form"))) {
      It(t);
    }
    n.firstInitCompleted = true;
    ae(t, "htmx:afterProcessNode");
  }
  function Mt(e) {
    if (!(e instanceof Element)) {
      return false;
    }
    const t = oe(e);
    const n = Pe(e);
    if (t.initHash !== n) {
      ke(e);
      t.initHash = n;
      return true;
    }
    return false;
  }
  function Ft(e) {
    e = w(e);
    if (ft(e)) {
      S(e);
      return;
    }
    const t = [];
    if (Mt(e)) {
      t.push(e);
    }
    ie(Ht(e), function (e) {
      if (ft(e)) {
        S(e);
        return;
      }
      if (Mt(e)) {
        t.push(e);
      }
    });
    ie(Rt(e), Dt);
    ie(t, kt);
  }
  function Xt(e) {
    return e.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
  }
  function Bt(e, t) {
    return new CustomEvent(e, {
      bubbles: true,
      cancelable: true,
      composed: true,
      detail: t,
    });
  }
  function fe(e, t, n) {
    ae(e, t, le({ error: t }, n));
  }
  function Ut(e) {
    return e === "htmx:afterProcessNode";
  }
  function jt(e, t, n) {
    ie(Jn(e, [], n), function (e) {
      try {
        t(e);
      } catch (e) {
        R(e);
      }
    });
  }
  function R(e) {
    console.error(e);
  }
  function ae(e, t, n) {
    e = w(e);
    if (n == null) {
      n = {};
    }
    n.elt = e;
    const r = Bt(t, n);
    if (Q.logger && !Ut(t)) {
      Q.logger(e, t, n);
    }
    if (n.error) {
      R(n.error);
      ae(e, "htmx:error", { errorInfo: n });
    }
    let o = e.dispatchEvent(r);
    const i = Xt(t);
    if (o && i !== t) {
      const s = Bt(i, r.detail);
      o = o && e.dispatchEvent(s);
    }
    jt(ce(e), function (e) {
      o = o && e.onEvent(t, r) !== false && !r.defaultPrevented;
    });
    return o;
  }
  let Vt = location.pathname + location.search;
  function _t(e) {
    Vt = e;
    if (B()) {
      sessionStorage.setItem("htmx-current-path-for-history", e);
    }
  }
  function zt() {
    const e = te().querySelector("[hx-history-elt],[data-hx-history-elt]");
    return e || te().body;
  }
  function $t(t, e) {
    if (!B()) {
      return;
    }
    const n = Kt(e);
    const r = te().title;
    const o = window.scrollY;
    if (Q.config.historyCacheSize <= 0) {
      sessionStorage.removeItem("htmx-history-cache");
      return;
    }
    t = U(t);
    const i = v(sessionStorage.getItem("htmx-history-cache")) || [];
    for (let e = 0; e < i.length; e++) {
      if (i[e].url === t) {
        i.splice(e, 1);
        break;
      }
    }
    const s = { url: t, content: n, title: r, scroll: o };
    ae(te().body, "htmx:historyItemCreated", { item: s, cache: i });
    i.push(s);
    while (i.length > Q.config.historyCacheSize) {
      i.shift();
    }
    while (i.length > 0) {
      try {
        sessionStorage.setItem("htmx-history-cache", JSON.stringify(i));
        break;
      } catch (e) {
        fe(te().body, "htmx:historyCacheError", { cause: e, cache: i });
        i.shift();
      }
    }
  }
  function Jt(t) {
    if (!B()) {
      return null;
    }
    t = U(t);
    const n = v(sessionStorage.getItem("htmx-history-cache")) || [];
    for (let e = 0; e < n.length; e++) {
      if (n[e].url === t) {
        return n[e];
      }
    }
    return null;
  }
  function Kt(e) {
    const t = Q.config.requestClass;
    const n = e.cloneNode(true);
    ie(x(n, "." + t), function (e) {
      G(e, t);
    });
    ie(x(n, "[data-disabled-by-htmx]"), function (e) {
      e.removeAttribute("disabled");
    });
    return n.innerHTML;
  }
  function Gt() {
    const e = zt();
    let t = Vt;
    if (B()) {
      t = sessionStorage.getItem("htmx-current-path-for-history");
    }
    t = t || location.pathname + location.search;
    const n = te().querySelector(
      '[hx-history="false" i],[data-hx-history="false" i]',
    );
    if (!n) {
      ae(te().body, "htmx:beforeHistorySave", { path: t, historyElt: e });
      $t(t, e);
    }
    if (Q.config.historyEnabled)
      history.replaceState({ htmx: true }, te().title, location.href);
  }
  function Wt(e) {
    if (Q.config.getCacheBusterParam) {
      e = e.replace(/org\.htmx\.cache-buster=[^&]*&?/, "");
      if (Y(e, "&") || Y(e, "?")) {
        e = e.slice(0, -1);
      }
    }
    if (Q.config.historyEnabled) {
      history.pushState({ htmx: true }, "", e);
    }
    _t(e);
  }
  function Zt(e) {
    if (Q.config.historyEnabled) history.replaceState({ htmx: true }, "", e);
    _t(e);
  }
  function Yt(e) {
    ie(e, function (e) {
      e.call(undefined);
    });
  }
  function Qt(e) {
    const t = new XMLHttpRequest();
    const n = { swapStyle: "innerHTML", swapDelay: 0, settleDelay: 0 };
    const r = { path: e, xhr: t, historyElt: zt(), swapSpec: n };
    t.open("GET", e, true);
    if (Q.config.historyRestoreAsHxRequest) {
      t.setRequestHeader("HX-Request", "true");
    }
    t.setRequestHeader("HX-History-Restore-Request", "true");
    t.setRequestHeader("HX-Current-URL", location.href);
    t.onload = function () {
      if (this.status >= 200 && this.status < 400) {
        r.response = this.response;
        ae(te().body, "htmx:historyCacheMissLoad", r);
        $e(r.historyElt, r.response, n, {
          contextElement: r.historyElt,
          historyRequest: true,
        });
        _t(r.path);
        ae(te().body, "htmx:historyRestore", {
          path: e,
          cacheMiss: true,
          serverResponse: r.response,
        });
      } else {
        fe(te().body, "htmx:historyCacheMissLoadError", r);
      }
    };
    if (ae(te().body, "htmx:historyCacheMiss", r)) {
      t.send();
    }
  }
  function en(e) {
    Gt();
    e = e || location.pathname + location.search;
    const t = Jt(e);
    if (t) {
      const n = {
        swapStyle: "innerHTML",
        swapDelay: 0,
        settleDelay: 0,
        scroll: t.scroll,
      };
      const r = { path: e, item: t, historyElt: zt(), swapSpec: n };
      if (ae(te().body, "htmx:historyCacheHit", r)) {
        $e(r.historyElt, t.content, n, {
          contextElement: r.historyElt,
          title: t.title,
        });
        _t(r.path);
        ae(te().body, "htmx:historyRestore", r);
      }
    } else {
      if (Q.config.refreshOnHistoryMiss) {
        Q.location.reload(true);
      } else {
        Qt(e);
      }
    }
  }
  function tn(e) {
    let t = we(e, "hx-indicator");
    if (t == null) {
      t = [e];
    }
    ie(t, function (e) {
      const t = oe(e);
      t.requestCount = (t.requestCount || 0) + 1;
      e.classList.add.call(e.classList, Q.config.requestClass);
    });
    return t;
  }
  function nn(e) {
    let t = we(e, "hx-disabled-elt");
    if (t == null) {
      t = [];
    }
    ie(t, function (e) {
      const t = oe(e);
      t.requestCount = (t.requestCount || 0) + 1;
      e.setAttribute("disabled", "");
      e.setAttribute("data-disabled-by-htmx", "");
    });
    return t;
  }
  function rn(e, t) {
    ie(e.concat(t), function (e) {
      const t = oe(e);
      t.requestCount = (t.requestCount || 1) - 1;
    });
    ie(e, function (e) {
      const t = oe(e);
      if (t.requestCount === 0) {
        e.classList.remove.call(e.classList, Q.config.requestClass);
      }
    });
    ie(t, function (e) {
      const t = oe(e);
      if (t.requestCount === 0) {
        e.removeAttribute("disabled");
        e.removeAttribute("data-disabled-by-htmx");
      }
    });
  }
  function on(t, n) {
    for (let e = 0; e < t.length; e++) {
      const r = t[e];
      if (r.isSameNode(n)) {
        return true;
      }
    }
    return false;
  }
  function sn(e) {
    const t = e;
    if (
      t.name === "" ||
      t.name == null ||
      t.disabled ||
      g(t, "fieldset[disabled]")
    ) {
      return false;
    }
    if (
      t.type === "button" ||
      t.type === "submit" ||
      t.tagName === "image" ||
      t.tagName === "reset" ||
      t.tagName === "file"
    ) {
      return false;
    }
    if (t.type === "checkbox" || t.type === "radio") {
      return t.checked;
    }
    return true;
  }
  function ln(t, e, n) {
    if (t != null && e != null) {
      if (Array.isArray(e)) {
        e.forEach(function (e) {
          n.append(t, e);
        });
      } else {
        n.append(t, e);
      }
    }
  }
  function cn(t, n, r) {
    if (t != null && n != null) {
      let e = r.getAll(t);
      if (Array.isArray(n)) {
        e = e.filter((e) => n.indexOf(e) < 0);
      } else {
        e = e.filter((e) => e !== n);
      }
      r.delete(t);
      ie(e, (e) => r.append(t, e));
    }
  }
  function un(e) {
    if (e instanceof HTMLSelectElement && e.multiple) {
      return M(e.querySelectorAll("option:checked")).map(function (e) {
        return e.value;
      });
    }
    if (e instanceof HTMLInputElement && e.files) {
      return M(e.files);
    }
    return e.value;
  }
  function fn(t, n, r, e, o) {
    if (e == null || on(t, e)) {
      return;
    } else {
      t.push(e);
    }
    if (sn(e)) {
      const i = ee(e, "name");
      ln(i, un(e), n);
      if (o) {
        an(e, r);
      }
    }
    if (e instanceof HTMLFormElement) {
      ie(e.elements, function (e) {
        if (t.indexOf(e) >= 0) {
          cn(e.name, un(e), n);
        } else {
          t.push(e);
        }
        if (o) {
          an(e, r);
        }
      });
      new FormData(e).forEach(function (e, t) {
        if (e instanceof File && e.name === "") {
          return;
        }
        ln(t, e, n);
      });
    }
  }
  function an(e, t) {
    const n = e;
    if (n.willValidate) {
      ae(n, "htmx:validation:validate");
      if (!n.checkValidity()) {
        t.push({ elt: n, message: n.validationMessage, validity: n.validity });
        ae(n, "htmx:validation:failed", {
          message: n.validationMessage,
          validity: n.validity,
        });
      }
    }
  }
  function hn(n, e) {
    for (const t of e.keys()) {
      n.delete(t);
    }
    e.forEach(function (e, t) {
      n.append(t, e);
    });
    return n;
  }
  function dn(e, t) {
    const n = [];
    const r = new FormData();
    const o = new FormData();
    const i = [];
    const s = oe(e);
    if (s.lastButtonClicked && !se(s.lastButtonClicked)) {
      s.lastButtonClicked = null;
    }
    let l =
      (e instanceof HTMLFormElement && e.noValidate !== true) ||
      a(e, "hx-validate") === "true";
    if (s.lastButtonClicked) {
      l = l && s.lastButtonClicked.formNoValidate !== true;
    }
    if (t !== "get") {
      fn(n, o, i, Lt(e), l);
    }
    fn(n, r, i, e, l);
    if (
      s.lastButtonClicked ||
      e.tagName === "BUTTON" ||
      (e.tagName === "INPUT" && ee(e, "type") === "submit")
    ) {
      const u = s.lastButtonClicked || e;
      const f = ee(u, "name");
      ln(f, u.value, o);
    }
    const c = we(e, "hx-include");
    ie(c, function (e) {
      fn(n, r, i, ce(e), l);
      if (!h(e, "form")) {
        ie(p(e).querySelectorAll(ot), function (e) {
          fn(n, r, i, e, l);
        });
      }
    });
    hn(r, o);
    return { errors: i, formData: r, values: kn(r) };
  }
  function pn(e, t, n) {
    if (e !== "") {
      e += "&";
    }
    if (String(n) === "[object Object]") {
      n = JSON.stringify(n);
    }
    const r = encodeURIComponent(n);
    e += encodeURIComponent(t) + "=" + r;
    return e;
  }
  function gn(e) {
    e = Pn(e);
    let n = "";
    e.forEach(function (e, t) {
      n = pn(n, t, e);
    });
    return n;
  }
  function mn(e, t, n) {
    const r = {
      "HX-Request": "true",
      "HX-Trigger": ee(e, "id"),
      "HX-Trigger-Name": ee(e, "name"),
      "HX-Target": a(t, "id"),
      "HX-Current-URL": location.href,
    };
    Cn(e, "hx-headers", false, r);
    if (n !== undefined) {
      r["HX-Prompt"] = n;
    }
    if (oe(e).boosted) {
      r["HX-Boosted"] = "true";
    }
    return r;
  }
  function yn(n, e) {
    const t = ne(e, "hx-params");
    if (t) {
      if (t === "none") {
        return new FormData();
      } else if (t === "*") {
        return n;
      } else if (t.indexOf("not ") === 0) {
        ie(t.slice(4).split(","), function (e) {
          e = e.trim();
          n.delete(e);
        });
        return n;
      } else {
        const r = new FormData();
        ie(t.split(","), function (t) {
          t = t.trim();
          if (n.has(t)) {
            n.getAll(t).forEach(function (e) {
              r.append(t, e);
            });
          }
        });
        return r;
      }
    } else {
      return n;
    }
  }
  function xn(e) {
    return !!ee(e, "href") && ee(e, "href").indexOf("#") >= 0;
  }
  function bn(e, t) {
    const n = t || ne(e, "hx-swap");
    const r = {
      swapStyle: oe(e).boosted ? "innerHTML" : Q.config.defaultSwapStyle,
      swapDelay: Q.config.defaultSwapDelay,
      settleDelay: Q.config.defaultSettleDelay,
    };
    if (Q.config.scrollIntoViewOnBoost && oe(e).boosted && !xn(e)) {
      r.show = "top";
    }
    if (n) {
      const s = X(n);
      if (s.length > 0) {
        for (let e = 0; e < s.length; e++) {
          const l = s[e];
          if (l.indexOf("swap:") === 0) {
            r.swapDelay = d(l.slice(5));
          } else if (l.indexOf("settle:") === 0) {
            r.settleDelay = d(l.slice(7));
          } else if (l.indexOf("transition:") === 0) {
            r.transition = l.slice(11) === "true";
          } else if (l.indexOf("ignoreTitle:") === 0) {
            r.ignoreTitle = l.slice(12) === "true";
          } else if (l.indexOf("scroll:") === 0) {
            const c = l.slice(7);
            var o = c.split(":");
            const u = o.pop();
            var i = o.length > 0 ? o.join(":") : null;
            r.scroll = u;
            r.scrollTarget = i;
          } else if (l.indexOf("show:") === 0) {
            const f = l.slice(5);
            var o = f.split(":");
            const a = o.pop();
            var i = o.length > 0 ? o.join(":") : null;
            r.show = a;
            r.showTarget = i;
          } else if (l.indexOf("focus-scroll:") === 0) {
            const h = l.slice("focus-scroll:".length);
            r.focusScroll = h == "true";
          } else if (e == 0) {
            r.swapStyle = l;
          } else {
            R("Unknown modifier in hx-swap: " + l);
          }
        }
      }
    }
    return r;
  }
  function vn(e) {
    return (
      ne(e, "hx-encoding") === "multipart/form-data" ||
      (h(e, "form") && ee(e, "enctype") === "multipart/form-data")
    );
  }
  function wn(t, n, r) {
    let o = null;
    jt(n, function (e) {
      if (o == null) {
        o = e.encodeParameters(t, r, n);
      }
    });
    if (o != null) {
      return o;
    } else {
      if (vn(n)) {
        return hn(new FormData(), Pn(r));
      } else {
        return gn(r);
      }
    }
  }
  function Sn(e) {
    return { tasks: [], elts: [e] };
  }
  function En(e, t) {
    const n = e[0];
    const r = e[e.length - 1];
    if (t.scroll) {
      var o = null;
      if (t.scrollTarget) {
        o = ce(ue(n, t.scrollTarget));
      }
      if (t.scroll === "top" && (n || o)) {
        o = o || n;
        o.scrollTop = 0;
      }
      if (t.scroll === "bottom" && (r || o)) {
        o = o || r;
        o.scrollTop = o.scrollHeight;
      }
      if (typeof t.scroll === "number") {
        b().setTimeout(function () {
          window.scrollTo(0, t.scroll);
        }, 0);
      }
    }
    if (t.show) {
      var o = null;
      if (t.showTarget) {
        let e = t.showTarget;
        if (t.showTarget === "window") {
          e = "body";
        }
        o = ce(ue(n, e));
      }
      if (t.show === "top" && (n || o)) {
        o = o || n;
        o.scrollIntoView({ block: "start", behavior: Q.config.scrollBehavior });
      }
      if (t.show === "bottom" && (r || o)) {
        o = o || r;
        o.scrollIntoView({ block: "end", behavior: Q.config.scrollBehavior });
      }
    }
  }
  function Cn(r, e, o, i, s) {
    if (i == null) {
      i = {};
    }
    if (r == null) {
      return i;
    }
    const l = a(r, e);
    if (l) {
      let e = l.trim();
      let t = o;
      if (e === "unset") {
        return null;
      }
      if (e.indexOf("javascript:") === 0) {
        e = e.slice(11);
        t = true;
      } else if (e.indexOf("js:") === 0) {
        e = e.slice(3);
        t = true;
      }
      if (e.indexOf("{") !== 0) {
        e = "{" + e + "}";
      }
      let n;
      if (t) {
        n = On(
          r,
          function () {
            if (s) {
              return Function("event", "return (" + e + ")").call(r, s);
            } else {
              return Function("return (" + e + ")").call(r);
            }
          },
          {},
        );
      } else {
        n = v(e);
      }
      for (const c in n) {
        if (n.hasOwnProperty(c)) {
          if (i[c] == null) {
            i[c] = n[c];
          }
        }
      }
    }
    return Cn(ce(u(r)), e, o, i, s);
  }
  function On(e, t, n) {
    if (Q.config.allowEval) {
      return t();
    } else {
      fe(e, "htmx:evalDisallowedError");
      return n;
    }
  }
  function Rn(e, t, n) {
    return Cn(e, "hx-vars", true, n, t);
  }
  function Hn(e, t, n) {
    return Cn(e, "hx-vals", false, n, t);
  }
  function Tn(e, t) {
    return le(Rn(e, t), Hn(e, t));
  }
  function qn(t, n, r) {
    if (r !== null) {
      try {
        t.setRequestHeader(n, r);
      } catch (e) {
        t.setRequestHeader(n, encodeURIComponent(r));
        t.setRequestHeader(n + "-URI-AutoEncoded", "true");
      }
    }
  }
  function An(t) {
    if (t.responseURL) {
      try {
        const e = new URL(t.responseURL);
        return e.pathname + e.search;
      } catch (e) {
        fe(te().body, "htmx:badResponseUrl", { url: t.responseURL });
      }
    }
  }
  function H(e, t) {
    return t.test(e.getAllResponseHeaders());
  }
  function Ln(t, n, r) {
    t = t.toLowerCase();
    if (r) {
      if (r instanceof Element || typeof r === "string") {
        return he(t, n, null, null, {
          targetOverride: w(r) || ve,
          returnPromise: true,
        });
      } else {
        let e = w(r.target);
        if ((r.target && !e) || (r.source && !e && !w(r.source))) {
          e = ve;
        }
        return he(t, n, w(r.source), r.event, {
          handler: r.handler,
          headers: r.headers,
          values: r.values,
          targetOverride: e,
          swapOverride: r.swap,
          select: r.select,
          returnPromise: true,
        });
      }
    } else {
      return he(t, n, null, null, { returnPromise: true });
    }
  }
  function Nn(e) {
    const t = [];
    while (e) {
      t.push(e);
      e = e.parentElement;
    }
    return t;
  }
  function In(e, t, n) {
    const r = new URL(
      t,
      location.protocol !== "about:" ? location.href : window.origin,
    );
    const o = location.protocol !== "about:" ? location.origin : window.origin;
    const i = o === r.origin;
    if (Q.config.selfRequestsOnly) {
      if (!i) {
        return false;
      }
    }
    return ae(e, "htmx:validateUrl", le({ url: r, sameHost: i }, n));
  }
  function Pn(e) {
    if (e instanceof FormData) return e;
    const t = new FormData();
    for (const n in e) {
      if (e.hasOwnProperty(n)) {
        if (e[n] && typeof e[n].forEach === "function") {
          e[n].forEach(function (e) {
            t.append(n, e);
          });
        } else if (typeof e[n] === "object" && !(e[n] instanceof Blob)) {
          t.append(n, JSON.stringify(e[n]));
        } else {
          t.append(n, e[n]);
        }
      }
    }
    return t;
  }
  function Dn(r, o, e) {
    return new Proxy(e, {
      get: function (t, e) {
        if (typeof e === "number") return t[e];
        if (e === "length") return t.length;
        if (e === "push") {
          return function (e) {
            t.push(e);
            r.append(o, e);
          };
        }
        if (typeof t[e] === "function") {
          return function () {
            t[e].apply(t, arguments);
            r.delete(o);
            t.forEach(function (e) {
              r.append(o, e);
            });
          };
        }
        if (t[e] && t[e].length === 1) {
          return t[e][0];
        } else {
          return t[e];
        }
      },
      set: function (e, t, n) {
        e[t] = n;
        r.delete(o);
        e.forEach(function (e) {
          r.append(o, e);
        });
        return true;
      },
    });
  }
  function kn(o) {
    return new Proxy(o, {
      get: function (e, t) {
        if (typeof t === "symbol") {
          const r = Reflect.get(e, t);
          if (typeof r === "function") {
            return function () {
              return r.apply(o, arguments);
            };
          } else {
            return r;
          }
        }
        if (t === "toJSON") {
          return () => Object.fromEntries(o);
        }
        if (t in e) {
          if (typeof e[t] === "function") {
            return function () {
              return o[t].apply(o, arguments);
            };
          }
        }
        const n = o.getAll(t);
        if (n.length === 0) {
          return undefined;
        } else if (n.length === 1) {
          return n[0];
        } else {
          return Dn(e, t, n);
        }
      },
      set: function (t, n, e) {
        if (typeof n !== "string") {
          return false;
        }
        t.delete(n);
        if (e && typeof e.forEach === "function") {
          e.forEach(function (e) {
            t.append(n, e);
          });
        } else if (typeof e === "object" && !(e instanceof Blob)) {
          t.append(n, JSON.stringify(e));
        } else {
          t.append(n, e);
        }
        return true;
      },
      deleteProperty: function (e, t) {
        if (typeof t === "string") {
          e.delete(t);
        }
        return true;
      },
      ownKeys: function (e) {
        return Reflect.ownKeys(Object.fromEntries(e));
      },
      getOwnPropertyDescriptor: function (e, t) {
        return Reflect.getOwnPropertyDescriptor(Object.fromEntries(e), t);
      },
    });
  }
  function he(t, n, r, o, i, k) {
    let s = null;
    let l = null;
    i = i != null ? i : {};
    if (i.returnPromise && typeof Promise !== "undefined") {
      var e = new Promise(function (e, t) {
        s = e;
        l = t;
      });
    }
    if (r == null) {
      r = te().body;
    }
    const M = i.handler || jn;
    const F = i.select || null;
    if (!se(r)) {
      re(s);
      return e;
    }
    const c = i.targetOverride || ce(Ee(r));
    if (c == null || c == ve) {
      fe(r, "htmx:targetError", { target: ne(r, "hx-target") });
      re(l);
      return e;
    }
    let u = oe(r);
    const f = u.lastButtonClicked;
    if (f) {
      const A = ee(f, "formaction");
      if (A != null) {
        n = A;
      }
      const L = ee(f, "formmethod");
      if (L != null) {
        if (de.includes(L.toLowerCase())) {
          t = L;
        } else {
          re(s);
          return e;
        }
      }
    }
    const a = ne(r, "hx-confirm");
    if (k === undefined) {
      const K = function (e) {
        return he(t, n, r, o, i, !!e);
      };
      const G = {
        target: c,
        elt: r,
        path: n,
        verb: t,
        triggeringEvent: o,
        etc: i,
        issueRequest: K,
        question: a,
      };
      if (ae(r, "htmx:confirm", G) === false) {
        re(s);
        return e;
      }
    }
    let h = r;
    let d = ne(r, "hx-sync");
    let p = null;
    let X = false;
    if (d) {
      const N = d.split(":");
      const I = N[0].trim();
      if (I === "this") {
        h = Se(r, "hx-sync");
      } else {
        h = ce(ue(r, I));
      }
      d = (N[1] || "drop").trim();
      u = oe(h);
      if (d === "drop" && u.xhr && u.abortable !== true) {
        re(s);
        return e;
      } else if (d === "abort") {
        if (u.xhr) {
          re(s);
          return e;
        } else {
          X = true;
        }
      } else if (d === "replace") {
        ae(h, "htmx:abort");
      } else if (d.indexOf("queue") === 0) {
        const W = d.split(" ");
        p = (W[1] || "last").trim();
      }
    }
    if (u.xhr) {
      if (u.abortable) {
        ae(h, "htmx:abort");
      } else {
        if (p == null) {
          if (o) {
            const P = oe(o);
            if (P && P.triggerSpec && P.triggerSpec.queue) {
              p = P.triggerSpec.queue;
            }
          }
          if (p == null) {
            p = "last";
          }
        }
        if (u.queuedRequests == null) {
          u.queuedRequests = [];
        }
        if (p === "first" && u.queuedRequests.length === 0) {
          u.queuedRequests.push(function () {
            he(t, n, r, o, i);
          });
        } else if (p === "all") {
          u.queuedRequests.push(function () {
            he(t, n, r, o, i);
          });
        } else if (p === "last") {
          u.queuedRequests = [];
          u.queuedRequests.push(function () {
            he(t, n, r, o, i);
          });
        }
        re(s);
        return e;
      }
    }
    const g = new XMLHttpRequest();
    u.xhr = g;
    u.abortable = X;
    const m = function () {
      u.xhr = null;
      u.abortable = false;
      if (u.queuedRequests != null && u.queuedRequests.length > 0) {
        const e = u.queuedRequests.shift();
        e();
      }
    };
    const B = ne(r, "hx-prompt");
    if (B) {
      var y = prompt(B);
      if (y === null || !ae(r, "htmx:prompt", { prompt: y, target: c })) {
        re(s);
        m();
        return e;
      }
    }
    if (a && !k) {
      if (!confirm(a)) {
        re(s);
        m();
        return e;
      }
    }
    let x = mn(r, c, y);
    if (t !== "get" && !vn(r)) {
      x["Content-Type"] = "application/x-www-form-urlencoded";
    }
    if (i.headers) {
      x = le(x, i.headers);
    }
    const U = dn(r, t);
    let b = U.errors;
    const j = U.formData;
    if (i.values) {
      hn(j, Pn(i.values));
    }
    const V = Pn(Tn(r, o));
    const v = hn(j, V);
    let w = yn(v, r);
    if (Q.config.getCacheBusterParam && t === "get") {
      w.set("org.htmx.cache-buster", ee(c, "id") || "true");
    }
    if (n == null || n === "") {
      n = location.href;
    }
    const S = Cn(r, "hx-request");
    const _ = oe(r).boosted;
    let E = Q.config.methodsThatUseUrlParams.indexOf(t) >= 0;
    const C = {
      boosted: _,
      useUrlParams: E,
      formData: w,
      parameters: kn(w),
      unfilteredFormData: v,
      unfilteredParameters: kn(v),
      headers: x,
      elt: r,
      target: c,
      verb: t,
      errors: b,
      withCredentials:
        i.credentials || S.credentials || Q.config.withCredentials,
      timeout: i.timeout || S.timeout || Q.config.timeout,
      path: n,
      triggeringEvent: o,
    };
    if (!ae(r, "htmx:configRequest", C)) {
      re(s);
      m();
      return e;
    }
    n = C.path;
    t = C.verb;
    x = C.headers;
    w = Pn(C.parameters);
    b = C.errors;
    E = C.useUrlParams;
    if (b && b.length > 0) {
      ae(r, "htmx:validation:halted", C);
      re(s);
      m();
      return e;
    }
    const z = n.split("#");
    const $ = z[0];
    const O = z[1];
    let R = n;
    if (E) {
      R = $;
      const Z = !w.keys().next().done;
      if (Z) {
        if (R.indexOf("?") < 0) {
          R += "?";
        } else {
          R += "&";
        }
        R += gn(w);
        if (O) {
          R += "#" + O;
        }
      }
    }
    if (!In(r, R, C)) {
      fe(r, "htmx:invalidPath", C);
      re(l);
      m();
      return e;
    }
    g.open(t.toUpperCase(), R, true);
    g.overrideMimeType("text/html");
    g.withCredentials = C.withCredentials;
    g.timeout = C.timeout;
    if (S.noHeaders) {
    } else {
      for (const D in x) {
        if (x.hasOwnProperty(D)) {
          const Y = x[D];
          qn(g, D, Y);
        }
      }
    }
    const H = {
      xhr: g,
      target: c,
      requestConfig: C,
      etc: i,
      boosted: _,
      select: F,
      pathInfo: {
        requestPath: n,
        finalRequestPath: R,
        responsePath: null,
        anchor: O,
      },
    };
    g.onload = function () {
      try {
        const t = Nn(r);
        H.pathInfo.responsePath = An(g);
        M(r, H);
        if (H.keepIndicators !== true) {
          rn(T, q);
        }
        ae(r, "htmx:afterRequest", H);
        ae(r, "htmx:afterOnLoad", H);
        if (!se(r)) {
          let e = null;
          while (t.length > 0 && e == null) {
            const n = t.shift();
            if (se(n)) {
              e = n;
            }
          }
          if (e) {
            ae(e, "htmx:afterRequest", H);
            ae(e, "htmx:afterOnLoad", H);
          }
        }
        re(s);
      } catch (e) {
        fe(r, "htmx:onLoadError", le({ error: e }, H));
        throw e;
      } finally {
        m();
      }
    };
    g.onerror = function () {
      rn(T, q);
      fe(r, "htmx:afterRequest", H);
      fe(r, "htmx:sendError", H);
      re(l);
      m();
    };
    g.onabort = function () {
      rn(T, q);
      fe(r, "htmx:afterRequest", H);
      fe(r, "htmx:sendAbort", H);
      re(l);
      m();
    };
    g.ontimeout = function () {
      rn(T, q);
      fe(r, "htmx:afterRequest", H);
      fe(r, "htmx:timeout", H);
      re(l);
      m();
    };
    if (!ae(r, "htmx:beforeRequest", H)) {
      re(s);
      m();
      return e;
    }
    var T = tn(r);
    var q = nn(r);
    ie(["loadstart", "loadend", "progress", "abort"], function (t) {
      ie([g, g.upload], function (e) {
        e.addEventListener(t, function (e) {
          ae(r, "htmx:xhr:" + t, {
            lengthComputable: e.lengthComputable,
            loaded: e.loaded,
            total: e.total,
          });
        });
      });
    });
    ae(r, "htmx:beforeSend", H);
    const J = E ? null : wn(g, r, w);
    g.send(J);
    return e;
  }
  function Mn(e, t) {
    const n = t.xhr;
    let r = null;
    let o = null;
    if (H(n, /HX-Push:/i)) {
      r = n.getResponseHeader("HX-Push");
      o = "push";
    } else if (H(n, /HX-Push-Url:/i)) {
      r = n.getResponseHeader("HX-Push-Url");
      o = "push";
    } else if (H(n, /HX-Replace-Url:/i)) {
      r = n.getResponseHeader("HX-Replace-Url");
      o = "replace";
    }
    if (r) {
      if (r === "false") {
        return {};
      } else {
        return { type: o, path: r };
      }
    }
    const i = t.pathInfo.finalRequestPath;
    const s = t.pathInfo.responsePath;
    const l = ne(e, "hx-push-url");
    const c = ne(e, "hx-replace-url");
    const u = oe(e).boosted;
    let f = null;
    let a = null;
    if (l) {
      f = "push";
      a = l;
    } else if (c) {
      f = "replace";
      a = c;
    } else if (u) {
      f = "push";
      a = s || i;
    }
    if (a) {
      if (a === "false") {
        return {};
      }
      if (a === "true") {
        a = s || i;
      }
      if (t.pathInfo.anchor && a.indexOf("#") === -1) {
        a = a + "#" + t.pathInfo.anchor;
      }
      return { type: f, path: a };
    } else {
      return {};
    }
  }
  function Fn(e, t) {
    var n = new RegExp(e.code);
    return n.test(t.toString(10));
  }
  function Xn(e) {
    for (var t = 0; t < Q.config.responseHandling.length; t++) {
      var n = Q.config.responseHandling[t];
      if (Fn(n, e.status)) {
        return n;
      }
    }
    return { swap: false };
  }
  function Bn(e) {
    if (e) {
      const t = f("title");
      if (t) {
        t.textContent = e;
      } else {
        window.document.title = e;
      }
    }
  }
  function Un(e, t) {
    if (t === "this") {
      return e;
    }
    const n = ce(ue(e, t));
    if (n == null) {
      fe(e, "htmx:targetError", { target: t });
      throw new Error(`Invalid re-target ${t}`);
    }
    return n;
  }
  function jn(t, e) {
    const n = e.xhr;
    let r = e.target;
    const o = e.etc;
    const i = e.select;
    if (!ae(t, "htmx:beforeOnLoad", e)) return;
    if (H(n, /HX-Trigger:/i)) {
      Je(n, "HX-Trigger", t);
    }
    if (H(n, /HX-Location:/i)) {
      Gt();
      let e = n.getResponseHeader("HX-Location");
      var s;
      if (e.indexOf("{") === 0) {
        s = v(e);
        e = s.path;
        delete s.path;
      }
      Ln("get", e, s).then(function () {
        Wt(e);
      });
      return;
    }
    const l =
      H(n, /HX-Refresh:/i) && n.getResponseHeader("HX-Refresh") === "true";
    if (H(n, /HX-Redirect:/i)) {
      e.keepIndicators = true;
      Q.location.href = n.getResponseHeader("HX-Redirect");
      l && Q.location.reload();
      return;
    }
    if (l) {
      e.keepIndicators = true;
      Q.location.reload();
      return;
    }
    const c = Mn(t, e);
    const u = Xn(n);
    const f = u.swap;
    let a = !!u.error;
    let h = Q.config.ignoreTitle || u.ignoreTitle;
    let d = u.select;
    if (u.target) {
      e.target = Un(t, u.target);
    }
    var p = o.swapOverride;
    if (p == null && u.swapOverride) {
      p = u.swapOverride;
    }
    if (H(n, /HX-Retarget:/i)) {
      e.target = Un(t, n.getResponseHeader("HX-Retarget"));
    }
    if (H(n, /HX-Reswap:/i)) {
      p = n.getResponseHeader("HX-Reswap");
    }
    var g = n.response;
    var m = le(
      {
        shouldSwap: f,
        serverResponse: g,
        isError: a,
        ignoreTitle: h,
        selectOverride: d,
        swapOverride: p,
      },
      e,
    );
    if (u.event && !ae(r, u.event, m)) return;
    if (!ae(r, "htmx:beforeSwap", m)) return;
    r = m.target;
    g = m.serverResponse;
    a = m.isError;
    h = m.ignoreTitle;
    d = m.selectOverride;
    p = m.swapOverride;
    e.target = r;
    e.failed = a;
    e.successful = !a;
    if (m.shouldSwap) {
      if (n.status === 286) {
        lt(t);
      }
      jt(t, function (e) {
        g = e.transformResponse(g, n, t);
      });
      if (c.type) {
        Gt();
      }
      var y = bn(t, p);
      if (!y.hasOwnProperty("ignoreTitle")) {
        y.ignoreTitle = h;
      }
      r.classList.add(Q.config.swappingClass);
      if (i) {
        d = i;
      }
      if (H(n, /HX-Reselect:/i)) {
        d = n.getResponseHeader("HX-Reselect");
      }
      const x = ne(t, "hx-select-oob");
      const b = ne(t, "hx-select");
      $e(r, g, y, {
        select: d === "unset" ? null : d || b,
        selectOOB: x,
        eventInfo: e,
        anchor: e.pathInfo.anchor,
        contextElement: t,
        afterSwapCallback: function () {
          if (H(n, /HX-Trigger-After-Swap:/i)) {
            let e = t;
            if (!se(t)) {
              e = te().body;
            }
            Je(n, "HX-Trigger-After-Swap", e);
          }
        },
        afterSettleCallback: function () {
          if (H(n, /HX-Trigger-After-Settle:/i)) {
            let e = t;
            if (!se(t)) {
              e = te().body;
            }
            Je(n, "HX-Trigger-After-Settle", e);
          }
        },
        beforeSwapCallback: function () {
          if (c.type) {
            ae(te().body, "htmx:beforeHistoryUpdate", le({ history: c }, e));
            if (c.type === "push") {
              Wt(c.path);
              ae(te().body, "htmx:pushedIntoHistory", { path: c.path });
            } else {
              Zt(c.path);
              ae(te().body, "htmx:replacedInHistory", { path: c.path });
            }
          }
        },
      });
    }
    if (a) {
      fe(
        t,
        "htmx:responseError",
        le(
          {
            error:
              "Response Status Error Code " +
              n.status +
              " from " +
              e.pathInfo.requestPath,
          },
          e,
        ),
      );
    }
  }
  const Vn = {};
  function _n() {
    return {
      init: function (e) {
        return null;
      },
      getSelectors: function () {
        return null;
      },
      onEvent: function (e, t) {
        return true;
      },
      transformResponse: function (e, t, n) {
        return e;
      },
      isInlineSwap: function (e) {
        return false;
      },
      handleSwap: function (e, t, n, r) {
        return false;
      },
      encodeParameters: function (e, t, n) {
        return null;
      },
    };
  }
  function zn(e, t) {
    if (t.init) {
      t.init(n);
    }
    Vn[e] = le(_n(), t);
  }
  function $n(e) {
    delete Vn[e];
  }
  function Jn(e, n, r) {
    if (n == undefined) {
      n = [];
    }
    if (e == undefined) {
      return n;
    }
    if (r == undefined) {
      r = [];
    }
    const t = a(e, "hx-ext");
    if (t) {
      ie(t.split(","), function (e) {
        e = e.replace(/ /g, "");
        if (e.slice(0, 7) == "ignore:") {
          r.push(e.slice(7));
          return;
        }
        if (r.indexOf(e) < 0) {
          const t = Vn[e];
          if (t && n.indexOf(t) < 0) {
            n.push(t);
          }
        }
      });
    }
    return Jn(ce(u(e)), n, r);
  }
  var Kn = false;
  te().addEventListener("DOMContentLoaded", function () {
    Kn = true;
  });
  function Gn(e) {
    if (Kn || te().readyState === "complete") {
      e();
    } else {
      te().addEventListener("DOMContentLoaded", e);
    }
  }
  function Wn() {
    if (Q.config.includeIndicatorStyles !== false) {
      const e = Q.config.inlineStyleNonce
        ? ` nonce="${Q.config.inlineStyleNonce}"`
        : "";
      te().head.insertAdjacentHTML(
        "beforeend",
        "<style" +
          e +
          ">      ." +
          Q.config.indicatorClass +
          "{opacity:0}      ." +
          Q.config.requestClass +
          " ." +
          Q.config.indicatorClass +
          "{opacity:1; transition: opacity 200ms ease-in;}      ." +
          Q.config.requestClass +
          "." +
          Q.config.indicatorClass +
          "{opacity:1; transition: opacity 200ms ease-in;}      </style>",
      );
    }
  }
  function Zn() {
    const e = te().querySelector('meta[name="htmx-config"]');
    if (e) {
      return v(e.content);
    } else {
      return null;
    }
  }
  function Yn() {
    const e = Zn();
    if (e) {
      Q.config = le(Q.config, e);
    }
  }
  Gn(function () {
    Yn();
    Wn();
    let e = te().body;
    Ft(e);
    const t = te().querySelectorAll(
      "[hx-trigger='restored'],[data-hx-trigger='restored']",
    );
    e.addEventListener("htmx:abort", function (e) {
      const t = e.target;
      const n = oe(t);
      if (n && n.xhr) {
        n.xhr.abort();
      }
    });
    const n = window.onpopstate ? window.onpopstate.bind(window) : null;
    window.onpopstate = function (e) {
      if (e.state && e.state.htmx) {
        en();
        ie(t, function (e) {
          ae(e, "htmx:restored", { document: te(), triggerEvent: ae });
        });
      } else {
        if (n) {
          n(e);
        }
      }
    };
    b().setTimeout(function () {
      ae(e, "htmx:load", {});
      e = null;
    }, 0);
  });
  return Q;
})();


================================================
FILE: cmd/template/advanced/files/htmx/imports/fiber.tmpl
================================================
"github.com/a-h/templ"
"{{.ProjectName}}/cmd/web"
"github.com/gofiber/fiber/v2/middleware/adaptor"
"github.com/gofiber/fiber/v2/middleware/filesystem"
"net/http"

================================================
FILE: cmd/template/advanced/files/htmx/imports/gin.tmpl
================================================
"github.com/a-h/templ"
"{{.ProjectName}}/cmd/web"
"io/fs"


================================================
FILE: cmd/template/advanced/files/htmx/imports/standard_library.tmpl
================================================
"github.com/a-h/templ"
"{{.ProjectName}}/cmd/web"

================================================
FILE: cmd/template/advanced/files/htmx/routes/chi.tmpl
================================================
fileServer := http.FileServer(http.FS(web.Files))
	r.Handle("/assets/*", fileServer)
	r.Get("/web", templ.Handler(web.HelloForm()).ServeHTTP)
	r.Post("/hello", web.HelloWebHandler)


================================================
FILE: cmd/template/advanced/files/htmx/routes/echo.tmpl
================================================
fileServer := http.FileServer(http.FS(web.Files))
e.GET("/assets/*", echo.WrapHandler(fileServer))

e.GET("/web", echo.WrapHandler(templ.Handler(web.HelloForm())))
e.POST("/hello", echo.WrapHandler(http.HandlerFunc(web.HelloWebHandler)))


================================================
FILE: cmd/template/advanced/files/htmx/routes/fiber.tmpl
================================================
s.App.Use("/assets", filesystem.New(filesystem.Config{
		Root:       http.FS(web.Files),
		PathPrefix: "assets",
		Browse:     false,
	}))

s.App.Get("/web", adaptor.HTTPHandler(templ.Handler(web.HelloForm())))

s.App.Post("/hello", func(c *fiber.Ctx) error {
  return web.HelloWebHandler(c)
})


================================================
FILE: cmd/template/advanced/files/htmx/routes/gin.tmpl
================================================
staticFiles, _ := fs.Sub(web.Files, "assets")
r.StaticFS("/assets", http.FS(staticFiles))

r.GET("/web", func(c *gin.Context) {
  templ.Handler(web.HelloForm()).ServeHTTP(c.Writer, c.Request)
})

r.POST("/hello", func(c *gin.Context) {
  web.HelloWebHandler(c.Writer, c.Request)
})


================================================
FILE: cmd/template/advanced/files/htmx/routes/gorilla.tmpl
================================================
fileServer := http.FileServer(http.FS(web.Files))
r.PathPrefix("/assets/").Handler(fileServer)

r.HandleFunc("/web", func(w http.ResponseWriter, r *http.Request) {
  templ.Handler(web.HelloForm()).ServeHTTP(w, r)
})

r.HandleFunc("/hello", web.HelloWebHandler)


================================================
FILE: cmd/template/advanced/files/htmx/routes/http_router.tmpl
================================================
  fileServer := http.FileServer(http.FS(web.Files))
  r.Handler(http.MethodGet, "/assets/*filepath", fileServer)
  r.Handler(http.MethodGet, "/web", templ.Handler(web.HelloForm()))
  r.HandlerFunc(http.MethodPost, "/hello", web.HelloWebHandler)
  


================================================
FILE: cmd/template/advanced/files/htmx/routes/standard_library.tmpl
================================================
  fileServer := http.FileServer(http.FS(web.Files))
	mux.Handle("/assets/", fileServer)
	mux.Handle("/web", templ.Handler(web.HelloForm()))
	mux.HandleFunc("/hello", web.HelloWebHandler)


================================================
FILE: cmd/template/advanced/files/htmx/tailwind/tailwind.config.js.tmpl
================================================
module.exports = {
	content: ["./**/*.html", "./**/*.templ", "./**/*.go",],
	theme: { extend: {}, },
	plugins: [],
}


================================================
FILE: cmd/template/advanced/files/react/app.tsx.tmpl
================================================
import { useState } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'

function App() {
  const [count, setCount] = useState(0)

  const fetchData = () => {
    fetch(`http://localhost:${import.meta.env.VITE_PORT}/`)
      .then(response => response.text())
      .then(data => setMessage(data))
      .catch(error => console.error('Error fetching data:', error))
  }
  const [message, setMessage] = useState<string>('')

  return (
    <>
      <div>
        <a href="https://vite.dev" target="_blank">
          <img src={viteLogo} className="logo" alt="Vite logo" />
        </a>
        <a href="https://react.dev" target="_blank">
          <img src={reactLogo} className="logo react" alt="React logo" />
        </a>
      </div>
      <h1>Vite + React</h1>
      <div className="card">
        <button onClick={() => setCount((count) => count + 1)}>
          count is {count}
        </button>
        <p>
          Edit <code>src/App.tsx</code> and save to test HMR
        </p>
      </div>
      <p className="read-the-docs">
        Click on the Vite and React logos to learn more
      </p>
      <button onClick={fetchData}>
        Click to fetch from Go server
      </button>
      {message && (
        <div>
          <h2>Server Response:</h2>
          <p>{message}</p>
        </div>
      )}
    </>
  )
}

export default App


================================================
FILE: cmd/template/advanced/files/react/tailwind/app.tsx.tmpl
================================================
import { useState } from 'react'

function App() {
  const [count, setCount] = useState(0)
  const [message, setMessage] = useState<string>('')

  const fetchData = () => {
    fetch(`http://localhost:${import.meta.env.VITE_PORT}/`)
      .then(response => response.text())
      .then(data => setMessage(data))
      .catch(error => console.error('Error fetching data:', error))
  }

  return (
    <div className="min-h-screen bg-gray-50 py-12 px-4 sm:px-6 lg:px-8">
      <div className="max-w-md mx-auto space-y-8">
        <div className="text-center">
          <h1 className="text-4xl font-bold text-gray-900 mb-2">
            Welcome to Vite + React
          </h1>
          <p className="text-gray-600">
            Get started by editing <code className="text-sm bg-gray-100 p-1 rounded">src/App.tsx</code>
          </p>
        </div>

        <div className="bg-white p-6 rounded-lg shadow-md">
          <div className="text-center space-y-4">
            <button
              onClick={() => setCount((count) => count + 1)}
              className="bg-blue-500 hover:bg-blue-600 text-white font-semibold py-2 px-4 rounded-md transition-colors"
            >
              Count is {count}
            </button>
            
            <button
              onClick={fetchData}
              className="block w-full bg-green-500 hover:bg-green-600 text-white font-semibold py-2 px-4 rounded-md transition-colors"
            >
              Fetch from Server
            </button>

            {message && (
              <div className="mt-4 p-4 bg-gray-50 rounded-md">
                <p className="text-gray-700">Server Response:</p>
                <p className="text-gray-900 font-medium">{message}</p>
              </div>
            )}
          </div>
        </div>

        <div className="text-center text-gray-500 text-sm">
          Built with Vite, React, and Tailwind CSS
        </div>
      </div>
    </div>
  )
}

export default App

================================================
FILE: cmd/template/advanced/files/react/tailwind/index.css.tmpl
================================================
@import "tailwindcss";

================================================
FILE: cmd/template/advanced/files/react/tailwind/vite.config.ts.tmpl
================================================
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'

// https://vite.dev/config/
export default defineConfig({
  plugins: [react(),tailwindcss()],
})

================================================
FILE: cmd/template/advanced/files/tailwind/input.css.tmpl
================================================
@import "tailwindcss"


================================================
FILE: cmd/template/advanced/files/tailwind/output.css.tmpl
================================================


================================================
FILE: cmd/template/advanced/files/websocket/imports/fiber.tmpl
================================================
"github.com/gofiber/contrib/websocket"


================================================
FILE: cmd/template/advanced/files/websocket/imports/standard_library.tmpl
================================================
"github.com/coder/websocket"


================================================
FILE: cmd/template/advanced/files/workflow/github/github_action_goreleaser.yml.tmpl
================================================
name: goreleaser

on:
  push:
    tags:
      - "v*.*.*"

permissions:
  contents: write

jobs:
  goreleaser:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Set up Go
        uses: actions/setup-go@v4
        with:
          go-version: '1.22.x'
{{if or ( .AdvancedOptions.htmx ) ( .AdvancedOptions.tailwind )}}
      - name: Install templ
        shell: bash
        run: go install github.com/a-h/templ/cmd/templ@latest
      - name: Run templ generate
        shell: bash
        run: templ generate -path .
{{end}}
      - name: Run GoReleaser
        uses: goreleaser/goreleaser-action@v6
        with:
          distribution: goreleaser
          version: ${{"{{"}} env.GITHUB_REF_NAME {{"}}"}}
          args: release --clean
          workdir: ./
        env:
          GITHUB_TOKEN: ${{"{{"}} secrets.GITHUB_TOKEN {{"}}"}}


================================================
FILE: cmd/template/advanced/files/workflow/github/github_action_gotest.yml.tmpl
================================================
name: Go-test
on: [push, pull_request]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4
      - name: Setup Go
        uses: actions/setup-go@v4
        with:
          go-version: '1.22.x'
{{if or ( .AdvancedOptions.htmx ) ( .AdvancedOptions.tailwind )}}
      - name: Install templ
        shell: bash
        run: go install github.com/a-h/templ/cmd/templ@latest
      - name: Run templ generate
        shell: bash
        run: templ generate -path .
{{end}}
      - name: Build
        run: go build -v ./...
      - name: Test with the Go CLI
        run: go test ./... 


================================================
FILE: cmd/template/advanced/files/workflow/github/github_action_releaser_config.yml.tmpl
================================================
version: 2
before:
  hooks:
  - go mod tidy

env:
  - PACKAGE_PATH=github.com/<user>/<repo>/cmd

builds:
- binary: "{{"{{"}} .ProjectName {{"}}"}}"
  main: ./cmd/api
  goos:
  - darwin
  - linux
  - windows
  goarch:
  - amd64
  - arm64
  env:
  - CGO_ENABLED=0
  ldflags:
  - -s -w -X {{"{{"}}.Env.PACKAGE_PATH{{"}}"}}={{"{{"}}.Version{{"}}"}} 
release:
  prerelease: auto

universal_binaries:
- replace: true

archives:
  - name_template: >
      {{"{{"}}- .ProjectName {{"}}"}}_{{"{{"}}- .Version {{"}}"}}_{{"{{"}}- title .Os {{"}}"}}_{{"{{"}}- if eq .Arch "amd64" {{"}}"}}x86_64{{"{{"}}- else if eq .Arch "386" {{"}}"}}i386{{"{{"}}- else {{"}}"}}{{"{{"}} .Arch {{"}}"}}{{"{{"}} end {{"}}"}}{{"{{"}}- if .Arm {{"}}"}}v{{"{{"}} .Arm {{"}}"}}{{"{{"}} end -{{"}}"}}
    format_overrides:
      - goos: windows
        format: zip
    builds_info:
      group: root
      owner: root
    files:
      - README.md

checksum:
  name_template: 'checksums.txt'


================================================
FILE: cmd/template/advanced/gitHubAction.go
================================================
package advanced

import (
	_ "embed"
)

//go:embed files/workflow/github/github_action_goreleaser.yml.tmpl
var gitHubActionBuildTemplate []byte

//go:embed files/workflow/github/github_action_gotest.yml.tmpl
var gitHubActionTestTemplate []byte

//go:embed files/workflow/github/github_action_releaser_config.yml.tmpl
var gitHubActionConfigTemplate []byte

func Releaser() []byte {
	return gitHubActionBuildTemplate
}

func Test() []byte {
	return gitHubActionTestTemplate
}

func ReleaserConfig() []byte {
	return gitHubActionConfigTemplate
}


================================================
FILE: cmd/template/advanced/routes.go
================================================
package advanced

import (
	_ "embed"
)

//go:embed files/htmx/hello.templ.tmpl
var helloTemplTemplate []byte

//go:embed files/htmx/base.templ.tmpl
var baseTemplTemplate []byte

//go:embed files/react/tailwind/index.css.tmpl
var inputCssTemplateReact []byte

//go:embed files/react/tailwind/vite.config.ts.tmpl
var viteTailwindConfigFile []byte

//go:embed files/react/tailwind/app.tsx.tmpl
var reactTailwindAppFile []byte

//go:embed files/react/app.tsx.tmpl
var reactAppFile []byte

//go:embed files/tailwind/input.css.tmpl
var inputCssTemplate []byte

//go:embed files/tailwind/output.css.tmpl
var outputCssTemplate []byte

//go:embed files/htmx/tailwind/tailwind.config.js.tmpl
var htmxTailwindConfigJsTemplate []byte

//go:embed files/htmx/htmx.min.js.tmpl
var htmxMinJsTemplate []byte

//go:embed files/htmx/efs.go.tmpl
var efsTemplate []byte

//go:embed files/htmx/hello.go.tmpl
var helloGoTemplate []byte

//go:embed files/htmx/hello_fiber.go.tmpl
var helloFiberGoTemplate []byte

//go:embed files/htmx/routes/http_router.tmpl
var httpRouterHtmxTemplRoutes []byte

//go:embed files/htmx/routes/standard_library.tmpl
var stdLibHtmxTemplRoutes []byte

//go:embed files/htmx/imports/standard_library.tmpl
var stdLibHtmxTemplImports []byte

//go:embed files/websocket/imports/standard_library.tmpl
var stdLibWebsocketImports []byte

//go:embed files/htmx/routes/chi.tmpl
var chiHtmxTemplRoutes []byte

//go:embed files/htmx/routes/gin.tmpl
var ginHtmxTemplRoutes []byte

//go:embed files/htmx/imports/gin.tmpl
var ginHtmxTemplImports []byte

//go:embed files/htmx/routes/gorilla.tmpl
var gorillaHtmxTemplRoutes []byte

//go:embed files/htmx/routes/echo.tmpl
var echoHtmxTemplRoutes []byte

//go:embed files/htmx/routes/fiber.tmpl
var fiberHtmxTemplRoutes []byte

//go:embed files/htmx/imports/fiber.tmpl
var fiberHtmxTemplImports []byte

//go:embed files/websocket/imports/fiber.tmpl
var fiberWebsocketTemplImports []byte

func EchoHtmxTemplRoutesTemplate() []byte {
	return echoHtmxTemplRoutes
}

func GorillaHtmxTemplRoutesTemplate() []byte {
	return gorillaHtmxTemplRoutes
}

func ChiHtmxTemplRoutesTemplate() []byte {
	return chiHtmxTemplRoutes
}

func GinHtmxTemplRoutesTemplate() []byte {
	return ginHtmxTemplRoutes
}

func HttpRouterHtmxTemplRoutesTemplate() []byte {
	return httpRouterHtmxTemplRoutes
}

func StdLibHtmxTemplRoutesTemplate() []byte {
	return stdLibHtmxTemplRoutes
}

func StdLibHtmxTemplImportsTemplate() []byte {
	return stdLibHtmxTemplImports
}

func StdLibWebsocketTemplImportsTemplate() []byte {
	return stdLibWebsocketImports
}

func HelloTemplTemplate() []byte {
	return helloTemplTemplate
}

func BaseTemplTemplate() []byte {
	return baseTemplTemplate
}

func ReactTailwindAppfile() []byte {
	return reactTailwindAppFile
}

func ReactAppfile() []byte {
	return reactAppFile
}

func InputCssTemplateReact() []byte {
	return inputCssTemplateReact
}

func ViteTailwindConfigFile() []byte {
	return viteTailwindConfigFile
}

func InputCssTemplate() []byte {
	return inputCssTemplate
}

func OutputCssTemplate() []byte {
	return outputCssTemplate
}

func HtmxTailwindConfigJsTemplate() []byte {
	return htmxTailwindConfigJsTemplate
}

func HtmxJSTemplate() []byte {
	return htmxMinJsTemplate
}

func EfsTemplate() []byte {
	return efsTemplate
}

func HelloGoTemplate() []byte {
	return helloGoTemplate
}

func HelloFiberGoTemplate() []byte {
	return helloFiberGoTemplate
}

func FiberHtmxTemplRoutesTemplate() []byte {
	return fiberHtmxTemplRoutes
}

func FiberHtmxTemplImportsTemplate() []byte {
	return fiberHtmxTemplImports
}

func FiberWebsocketTemplImportsTemplate() []byte {
	return fiberWebsocketTemplImports
}

func GinHtmxTemplImportsTemplate() []byte {
	return ginHtmxTemplImports
}


================================================
FILE: cmd/template/dbdriver/files/env/mongo.tmpl
================================================
{{ if .AdvancedOptions.docker }}
BLUEPRINT_DB_HOST=mongo_bp
{{- else }}
BLUEPRINT_DB_HOST=localhost
{{- end }}
BLUEPRINT_DB_PORT=27017
BLUEPRINT_DB_USERNAME=melkey
BLUEPRINT_DB_ROOT_PASSWORD=password1234


================================================
FILE: cmd/template/dbdriver/files/env/mysql.tmpl
================================================
{{- if .AdvancedOptions.docker }}
BLUEPRINT_DB_HOST=mysql_bp
{{- else }}
BLUEPRINT_DB_HOST=localhost
{{- end }}
BLUEPRINT_DB_PORT=3306
BLUEPRINT_DB_DATABASE=blueprint
BLUEPRINT_DB_USERNAME=melkey
BLUEPRINT_DB_PASSWORD=password1234
BLUEPRINT_DB_ROOT_PASSWORD=password4321


================================================
FILE: cmd/template/dbdriver/files/env/postgres.tmpl
================================================
{{- if .AdvancedOptions.docker }}
BLUEPRINT_DB_HOST=psql_bp
{{- else }}
BLUEPRINT_DB_HOST=localhost
{{- end }}
BLUEPRINT_DB_PORT=5432
BLUEPRINT_DB_DATABASE=blueprint
BLUEPRINT_DB_USERNAME=melkey
BLUEPRINT_DB_PASSWORD=password1234
BLUEPRINT_DB_SCHEMA=public


================================================
FILE: cmd/template/dbdriver/files/env/redis.tmpl
================================================
{{- if .AdvancedOptions.docker }}
BLUEPRINT_DB_ADDRESS=redis_bp
{{- else }}
BLUEPRINT_DB_ADDRESS=localhost
{{- end }}
BLUEPRINT_DB_PORT=6379
BLUEPRINT_DB_PASSWORD=
BLUEPRINT_DB_DATABASE=0


================================================
FILE: cmd/template/dbdriver/files/env/scylla.tmpl
================================================
{{- if .AdvancedOptions.docker }}
# BLUEPRINT_DB_HOSTS=scylla_bp:9042 # ScyllaDB default port
BLUEPRINT_DB_HOSTS=scylla_bp:19042 # ScyllaDB Shard-Aware port
{{- else }}
# BLUEPRINT_DB_HOSTS=localhost:9042 # ScyllaDB default port
BLUEPRINT_DB_HOSTS=localhost:19042 # ScyllaDB Shard-Aware port
{{- end }}
BLUEPRINT_DB_CONSISTENCY="LOCAL_QUORUM"
# BLUEPRINT_DB_USERNAME=
# BLUEPRINT_DB_PASSWORD=

================================================
FILE: cmd/template/dbdriver/files/env/sqlite.tmpl
================================================
{{- if .AdvancedOptions.docker }}
BLUEPRINT_DB_URL=./db/test.db
{{- else }}
BLUEPRINT_DB_URL=./test.db
{{- end }}


================================================
FILE: cmd/template/dbdriver/files/service/mongo.tmpl
================================================
package database

import (
	"context"
	"fmt"
	"log"
	"os"
	"time"

	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
	_ "github.com/joho/godotenv/autoload"
)

type Service interface {
	Health() map[string]string
}

type service struct {
	db *mongo.Client
}

var (
	host     = os.Getenv("BLUEPRINT_DB_HOST")
	port     = os.Getenv("BLUEPRINT_DB_PORT")
	//database = os.Getenv("BLUEPRINT_DB_DATABASE")
)

func New() Service {
	client, err := mongo.Connect(context.Background(), options.Client().ApplyURI(fmt.Sprintf("mongodb://%s:%s", host, port)))

	if err != nil {
		log.Fatal(err)

	}
	return &service{
		db: client,
	}
}

func (s *service) Health() map[string]string {
	ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
	defer cancel()

	err := s.db.Ping(ctx, nil)
	if err != nil {
		log.Fatalf("db down: %v", err) 
	}

	return map[string]string{
		"message": "It's healthy",
	}
}


================================================
FILE: cmd/template/dbdriver/files/service/mysql.tmpl
================================================
package database

import (
	"context"
	"database/sql"
	"fmt"
	"log"
	"os"
	"strconv"
	"time"

	_ "github.com/go-sql-driver/mysql"
	_ "github.com/joho/godotenv/autoload"
)

// Service represents a service that interacts with a database.
type Service interface {
	// Health returns a map of health status information.
	// The keys and values in the map are service-specific.
	Health() map[string]string

	// Close terminates the database connection.
	// It returns an error if the connection cannot be closed.
	Close() error
}

type service struct {
	db *sql.DB
}

var (
	dbname     = os.Getenv("BLUEPRINT_DB_DATABASE")
	password   = os.Getenv("BLUEPRINT_DB_PASSWORD")
	username   = os.Getenv("BLUEPRINT_DB_USERNAME")
	port       = os.Getenv("BLUEPRINT_DB_PORT")
	host       = os.Getenv("BLUEPRINT_DB_HOST")
	dbInstance *service
)

func New() Service {
	// Reuse Connection
	if dbInstance != nil {
		return dbInstance
	}

	// Opening a driver typically will not attempt to connect to the database.
	db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s:%s)/%s", username, password, host, port, dbname))
	if err != nil {
		// This will not be a connection error, but a DSN parse error or
		// another initialization error.
		log.Fatal(err)
	}
	db.SetConnMaxLifetime(0)
	db.SetMaxIdleConns(50)
	db.SetMaxOpenConns(50)

	dbInstance = &service{
		db: db,
	}
	return dbInstance
}

// Health checks the health of the database connection by pinging the database.
// It returns a map with keys indicating various health statistics.
func (s *service) Health() map[string]string {
	ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
	defer cancel()

	stats := make(map[string]string)

	// Ping the database
	err := s.db.PingContext(ctx)
	if err != nil {
		stats["status"] = "down"
		stats["error"] = fmt.Sprintf("db down: %v", err)
		log.Fatalf("db down: %v", err)  // Log the error and terminate the program
		return stats
	}

	// Database is up, add more statistics
	stats["status"] = "up"
	stats["message"] = "It's healthy"

	// Get database stats (like open connections, in use, idle, etc.)
	dbStats := s.db.Stats()
	stats["open_connections"] = strconv.Itoa(dbStats.OpenConnections)
	stats["in_use"] = strconv.Itoa(dbStats.InUse)
	stats["idle"] = strconv.Itoa(dbStats.Idle)
	stats["wait_count"] = strconv.FormatInt(dbStats.WaitCount, 10)
	stats["wait_duration"] = dbStats.WaitDuration.String()
	stats["max_idle_closed"] = strconv.FormatInt(dbStats.MaxIdleClosed, 10)
	stats["max_lifetime_closed"] = strconv.FormatInt(dbStats.MaxLifetimeClosed, 10)

	// Evaluate stats to provide a health message
	if dbStats.OpenConnections > 40 { // Assuming 50 is the max for this example
		stats["message"] = "The database is experiencing heavy load."
	}
	if dbStats.WaitCount > 1000 {
		stats["message"] = "The database has a high number of wait events, indicating potential bottlenecks."
	}

	if dbStats.MaxIdleClosed > int64(dbStats.OpenConnections)/2 {
		stats["message"] = "Many idle connections are being closed, consider revising the connection pool settings."
	}

	if dbStats.MaxLifetimeClosed > int64(dbStats.OpenConnections)/2 {
		stats["message"] = "Many connections are being closed due to max lifetime, consider increasing max lifetime or revising the connection usage pattern."
	}

	return stats
}

// Close closes the database connection.
// It logs a message indicating the disconnection from the specific database.
// If the connection is successfully closed, it returns nil.
// If an error occurs while closing the connection, it returns the error.
func (s *service) Close() error {
	log.Printf("Disconnected from database: %s", dbname)
	return s.db.Close()
}


================================================
FILE: cmd/template/dbdriver/files/service/postgres.tmpl
================================================
package database

import (
	"context"
	"database/sql"
	"fmt"
	"log"
	"os"
	"strconv"
	"time"

	_ "github.com/jackc/pgx/v5/stdlib"
	_ "github.com/joho/godotenv/autoload"
)

// Service represents a service that interacts with a database.
type Service interface {
	// Health returns a map of health status information.
	// The keys and values in the map are service-specific.
	Health() map[string]string

	// Close terminates the database connection.
	// It returns an error if the connection cannot be closed.
	Close() error
}

type service struct {
	db *sql.DB
}

var (
	database = os.Getenv("BLUEPRINT_DB_DATABASE")
	password = os.Getenv("BLUEPRINT_DB_PASSWORD")
	username = os.Getenv("BLUEPRINT_DB_USERNAME")
	port     = os.Getenv("BLUEPRINT_DB_PORT")
	host     = os.Getenv("BLUEPRINT_DB_HOST")
        schema   = os.Getenv("BLUEPRINT_DB_SCHEMA")
	dbInstance *service
)

func New() Service {
	// Reuse Connection
	if dbInstance != nil {
		return dbInstance
	}
	connStr := fmt.Sprintf("postgres://%s:%s@%s:%s/%s?sslmode=disable&search_path=%s", username, password, host, port, database, schema)
	db, err := sql.Open("pgx", connStr)
	if err != nil {
		log.Fatal(err)
	}
	dbInstance = &service{
		db: db,
	}
	return dbInstance
}

// Health checks the health of the database connection by pinging the database.
// It returns a map with keys indicating various health statistics.
func (s *service) Health() map[string]string {
	ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
	defer cancel()

	stats := make(map[string]string)

	// Ping the database
	err := s.db.PingContext(ctx)
	if err != nil {
		stats["status"] = "down"
		stats["error"] = fmt.Sprintf("db down: %v", err)
		log.Fatalf("db down: %v", err)  // Log the error and terminate the program
		return stats
	}

	// Database is up, add more statistics
	stats["status"] = "up"
	stats["message"] = "It's healthy"

	// Get database stats (like open connections, in use, idle, etc.)
	dbStats := s.db.Stats()
	stats["open_connections"] = strconv.Itoa(dbStats.OpenConnections)
	stats["in_use"] = strconv.Itoa(dbStats.InUse)
	stats["idle"] = strconv.Itoa(dbStats.Idle)
	stats["wait_count"] = strconv.FormatInt(dbStats.WaitCount, 10)
	stats["wait_duration"] = dbStats.WaitDuration.String()
	stats["max_idle_closed"] = strconv.FormatInt(dbStats.MaxIdleClosed, 10)
	stats["max_lifetime_closed"] = strconv.FormatInt(dbStats.MaxLifetimeClosed, 10)

	// Evaluate stats to provide a health message
	if dbStats.OpenConnections > 40 { // Assuming 50 is the max for this example
		stats["message"] = "The database is experiencing heavy load."
	}

	if dbStats.WaitCount > 1000 {
		stats["message"] = "The database has a high number of wait events, indicating potential bottlenecks."
	}

	if dbStats.MaxIdleClosed > int64(dbStats.OpenConnections)/2 {
		stats["message"] = "Many idle connections are being closed, consider revising the connection pool settings."
	}

	if dbStats.MaxLifetimeClosed > int64(dbStats.OpenConnections)/2 {
		stats["message"] = "Many connections are being closed due to max lifetime, consider increasing max lifetime or revising the connection usage pattern."
	}

	return stats
}

// Close closes the database connection.
// It logs a message indicating the disconnection from the specific database.
// If the connection is successfully closed, it returns nil.
// If an error occurs while closing the connection, it returns the error.
func (s *service) Close() error {
	log.Printf("Disconnected from database: %s", database)
	return s.db.Close()
}


================================================
FILE: cmd/template/dbdriver/files/service/redis.tmpl
================================================
package database

import (
	"context"
	"fmt"
	"log"
	"math"
	"os"
	"strconv"
	"strings"
	"time"

	_ "github.com/joho/godotenv/autoload"
	"github.com/redis/go-redis/v9"
)

type Service interface {
	Health() map[string]string
}

type service struct {
	db *redis.Client
}

var (
	address  = os.Getenv("BLUEPRINT_DB_ADDRESS")
	port     = os.Getenv("BLUEPRINT_DB_PORT")
	password = os.Getenv("BLUEPRINT_DB_PASSWORD")
	database = os.Getenv("BLUEPRINT_DB_DATABASE")
)

func New() Service {
	num, err := strconv.Atoi(database)
	if err != nil {
		log.Fatalf("database incorrect %v", err)
	}

	fullAddress := fmt.Sprintf("%s:%s", address, port)

	rdb := redis.NewClient(&redis.Options{
		Addr:     fullAddress,
		Password: password,
		DB:       num,
		// Note: It's important to add this for a secure connection. Most cloud services that offer Redis should already have this configured in their services.
		// For manual setup, please refer to the Redis documentation: https://redis.io/docs/latest/operate/oss_and_stack/management/security/encryption/
		// TLSConfig: &tls.Config{
		// 	MinVersion:   tls.VersionTLS12,
		// },
	})

	s := &service{db: rdb}

	return s
}

// Health returns the health status and statistics of the Redis server.
func (s *service) Health() map[string]string {
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) // Default is now 5s
	defer cancel()

	stats := make(map[string]string)

	// Check Redis health and populate the stats map
	stats = s.checkRedisHealth(ctx, stats)

	return stats
}

// checkRedisHealth checks the health of the Redis server and adds the relevant statistics to the stats map.
func (s *service) checkRedisHealth(ctx context.Context, stats map[string]string) map[string]string {
	// Ping the Redis server to check its availability.
	pong, err := s.db.Ping(ctx).Result()
	// Note: By extracting and simplifying like this, `log.Fatalf("db down: %v", err)`
	// can be changed into a standard error instead of a fatal error.
	if err != nil {
		log.Fatalf("db down: %v", err)
	}

	// Redis is up
	stats["redis_status"] = "up"
	stats["redis_message"] = "It's healthy"
	stats["redis_ping_response"] = pong

	// Retrieve Redis server information.
	info, err := s.db.Info(ctx).Result()
	if err != nil {
		stats["redis_message"] = fmt.Sprintf("Failed to retrieve Redis info: %v", err)
		return stats
	}

	// Parse the Redis info response.
	redisInfo := parseRedisInfo(info)

	// Get the pool stats of the Redis client.
	poolStats := s.db.PoolStats()

	// Prepare the stats map with Redis server information and pool statistics.
	// Note: The "stats" map in the code uses string keys and values,
	// which is suitable for structuring and serializing the data for the frontend (e.g., JSON, XML, HTMX).
	// Using string types allows for easy conversion and compatibility with various data formats,
	// making it convenient to create health stats for monitoring or other purposes.
	// Also note that any raw "memory" (e.g., used_memory) value here is in bytes and can be converted to megabytes or gigabytes as a float64.
	stats["redis_version"] = redisInfo["redis_version"]
	stats["redis_mode"] = redisI
Download .txt
gitextract_afvolof3/

├── .github/
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug-report.yml
│   │   ├── config.yml
│   │   └── feature-request.yml
│   ├── pull_request_template.md
│   ├── semantic.yml
│   └── workflows/
│       ├── ci.yml
│       ├── docs.yml
│       ├── generate-linter-advanced.yml
│       ├── generate-linter-core.yml
│       ├── npm-publish.yml
│       ├── release.yml
│       ├── testcontainers.yml
│       └── update-htmx-version.yml
├── .gitignore
├── .goreleaser.yml
├── .pre-commit-config.yaml
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── cmd/
│   ├── create.go
│   ├── flags/
│   │   ├── advancedFeatures.go
│   │   ├── database.go
│   │   ├── frameworks.go
│   │   └── git.go
│   ├── program/
│   │   └── program.go
│   ├── root.go
│   ├── steps/
│   │   └── steps.go
│   ├── template/
│   │   ├── advanced/
│   │   │   ├── docker.go
│   │   │   ├── files/
│   │   │   │   ├── docker/
│   │   │   │   │   ├── docker_compose.yml.tmpl
│   │   │   │   │   └── dockerfile.tmpl
│   │   │   │   ├── htmx/
│   │   │   │   │   ├── base.templ.tmpl
│   │   │   │   │   ├── efs.go.tmpl
│   │   │   │   │   ├── hello.go.tmpl
│   │   │   │   │   ├── hello.templ.tmpl
│   │   │   │   │   ├── hello_fiber.go.tmpl
│   │   │   │   │   ├── htmx.min.js.tmpl
│   │   │   │   │   ├── imports/
│   │   │   │   │   │   ├── fiber.tmpl
│   │   │   │   │   │   ├── gin.tmpl
│   │   │   │   │   │   └── standard_library.tmpl
│   │   │   │   │   ├── routes/
│   │   │   │   │   │   ├── chi.tmpl
│   │   │   │   │   │   ├── echo.tmpl
│   │   │   │   │   │   ├── fiber.tmpl
│   │   │   │   │   │   ├── gin.tmpl
│   │   │   │   │   │   ├── gorilla.tmpl
│   │   │   │   │   │   ├── http_router.tmpl
│   │   │   │   │   │   └── standard_library.tmpl
│   │   │   │   │   └── tailwind/
│   │   │   │   │       └── tailwind.config.js.tmpl
│   │   │   │   ├── react/
│   │   │   │   │   ├── app.tsx.tmpl
│   │   │   │   │   └── tailwind/
│   │   │   │   │       ├── app.tsx.tmpl
│   │   │   │   │       ├── index.css.tmpl
│   │   │   │   │       └── vite.config.ts.tmpl
│   │   │   │   ├── tailwind/
│   │   │   │   │   ├── input.css.tmpl
│   │   │   │   │   └── output.css.tmpl
│   │   │   │   ├── websocket/
│   │   │   │   │   └── imports/
│   │   │   │   │       ├── fiber.tmpl
│   │   │   │   │       └── standard_library.tmpl
│   │   │   │   └── workflow/
│   │   │   │       └── github/
│   │   │   │           ├── github_action_goreleaser.yml.tmpl
│   │   │   │           ├── github_action_gotest.yml.tmpl
│   │   │   │           └── github_action_releaser_config.yml.tmpl
│   │   │   ├── gitHubAction.go
│   │   │   └── routes.go
│   │   ├── dbdriver/
│   │   │   ├── files/
│   │   │   │   ├── env/
│   │   │   │   │   ├── mongo.tmpl
│   │   │   │   │   ├── mysql.tmpl
│   │   │   │   │   ├── postgres.tmpl
│   │   │   │   │   ├── redis.tmpl
│   │   │   │   │   ├── scylla.tmpl
│   │   │   │   │   └── sqlite.tmpl
│   │   │   │   ├── service/
│   │   │   │   │   ├── mongo.tmpl
│   │   │   │   │   ├── mysql.tmpl
│   │   │   │   │   ├── postgres.tmpl
│   │   │   │   │   ├── redis.tmpl
│   │   │   │   │   ├── scylla.tmpl
│   │   │   │   │   └── sqlite.tmpl
│   │   │   │   └── tests/
│   │   │   │       ├── mongo.tmpl
│   │   │   │       ├── mysql.tmpl
│   │   │   │       ├── postgres.tmpl
│   │   │   │       ├── redis.tmpl
│   │   │   │       └── scylla.tmpl
│   │   │   ├── mongo.go
│   │   │   ├── mysql.go
│   │   │   ├── postgres.go
│   │   │   ├── redis.go
│   │   │   ├── scylla.go
│   │   │   └── sqlite.go
│   │   ├── docker/
│   │   │   ├── files/
│   │   │   │   └── docker-compose/
│   │   │   │       ├── mongo.tmpl
│   │   │   │       ├── mysql.tmpl
│   │   │   │       ├── postgres.tmpl
│   │   │   │       ├── redis.tmpl
│   │   │   │       └── scylla.tmpl
│   │   │   ├── mongo.go
│   │   │   ├── mysql.go
│   │   │   ├── postgres.go
│   │   │   ├── redis.go
│   │   │   └── scylla.go
│   │   ├── framework/
│   │   │   ├── chiRoutes.go
│   │   │   ├── echoRoutes.go
│   │   │   ├── fiberServer.go
│   │   │   ├── files/
│   │   │   │   ├── README.md.tmpl
│   │   │   │   ├── air.toml.tmpl
│   │   │   │   ├── gitignore.tmpl
│   │   │   │   ├── globalenv.tmpl
│   │   │   │   ├── main/
│   │   │   │   │   ├── fiber_main.go.tmpl
│   │   │   │   │   └── main.go.tmpl
│   │   │   │   ├── makefile.tmpl
│   │   │   │   ├── routes/
│   │   │   │   │   ├── chi.go.tmpl
│   │   │   │   │   ├── echo.go.tmpl
│   │   │   │   │   ├── fiber.go.tmpl
│   │   │   │   │   ├── gin.go.tmpl
│   │   │   │   │   ├── gorilla.go.tmpl
│   │   │   │   │   ├── http_router.go.tmpl
│   │   │   │   │   └── standard_library.go.tmpl
│   │   │   │   ├── server/
│   │   │   │   │   ├── fiber.go.tmpl
│   │   │   │   │   └── standard_library.go.tmpl
│   │   │   │   └── tests/
│   │   │   │       ├── default-test.go.tmpl
│   │   │   │       ├── echo-test.go.tmpl
│   │   │   │       ├── fiber-test.go.tmpl
│   │   │   │       └── gin-test.go.tmpl
│   │   │   ├── ginRoutes.go
│   │   │   ├── gorillaRoutes.go
│   │   │   ├── httpRoutes.go
│   │   │   ├── main.go
│   │   │   └── routerRoutes.go
│   │   └── globalEnv.go
│   ├── ui/
│   │   ├── multiInput/
│   │   │   └── multiInput.go
│   │   ├── multiSelect/
│   │   │   └── multiSelect.go
│   │   ├── spinner/
│   │   │   └── spinner.go
│   │   └── textinput/
│   │       ├── textinput.go
│   │       └── textinput_test.go
│   ├── utils/
│   │   ├── utils.go
│   │   └── utils_test.go
│   └── version.go
├── contributors.yml
├── docs/
│   ├── Makefile
│   ├── custom_theme/
│   │   └── main.html
│   ├── docs/
│   │   ├── advanced-flag/
│   │   │   ├── advanced-flag.md
│   │   │   ├── docker.md
│   │   │   ├── goreleaser.md
│   │   │   ├── htmx-templ.md
│   │   │   ├── react-vite.md
│   │   │   ├── tailwind.md
│   │   │   └── websocket.md
│   │   ├── blueprint-core/
│   │   │   ├── db-drivers.md
│   │   │   └── frameworks.md
│   │   ├── blueprint-ui.md
│   │   ├── creating-project/
│   │   │   ├── air.md
│   │   │   ├── makefile.md
│   │   │   └── project-init.md
│   │   ├── endpoints-test/
│   │   │   ├── mongo.md
│   │   │   ├── redis.md
│   │   │   ├── scylladb.md
│   │   │   ├── server.md
│   │   │   ├── sql.md
│   │   │   ├── web.md
│   │   │   └── websocket.md
│   │   ├── index.md
│   │   └── installation.md
│   ├── mkdocs.yml
│   └── requirements.txt
├── go.mod
├── go.sum
├── main.go
└── scripts/
    ├── completions.sh
    └── create-npm-packages.sh
Download .txt
SYMBOL INDEX (249 symbols across 40 files)

FILE: cmd/create.go
  constant logo (line 23) | logo = `
  function init (line 42) | func init() {
  type Options (line 62) | type Options struct
  function doesDirectoryExistAndIsNotEmpty (line 322) | func doesDirectoryExistAndIsNotEmpty(name string) bool {

FILE: cmd/flags/advancedFeatures.go
  type AdvancedFeatures (line 8) | type AdvancedFeatures
    method String (line 21) | func (f AdvancedFeatures) String() string {
    method Type (line 25) | func (f *AdvancedFeatures) Type() string {
    method Set (line 29) | func (f *AdvancedFeatures) Set(value string) error {
  constant Htmx (line 11) | Htmx              string = "htmx"
  constant GoProjectWorkflow (line 12) | GoProjectWorkflow string = "githubaction"
  constant Websocket (line 13) | Websocket         string = "websocket"
  constant Tailwind (line 14) | Tailwind          string = "tailwind"
  constant React (line 15) | React             string = "react"
  constant Docker (line 16) | Docker            string = "docker"

FILE: cmd/flags/database.go
  type Database (line 8) | type Database
    method String (line 25) | func (f Database) String() string {
    method Type (line 29) | func (f *Database) Type() string {
    method Set (line 33) | func (f *Database) Set(value string) error {
  constant MySql (line 14) | MySql    Database = "mysql"
  constant Postgres (line 15) | Postgres Database = "postgres"
  constant Sqlite (line 16) | Sqlite   Database = "sqlite"
  constant Mongo (line 17) | Mongo    Database = "mongo"
  constant Redis (line 18) | Redis    Database = "redis"
  constant Scylla (line 19) | Scylla   Database = "scylla"
  constant None (line 20) | None     Database = "none"

FILE: cmd/flags/frameworks.go
  type Framework (line 8) | type Framework
    method String (line 25) | func (f Framework) String() string {
    method Type (line 29) | func (f *Framework) Type() string {
    method Set (line 33) | func (f *Framework) Set(value string) error {
  constant Chi (line 14) | Chi             Framework = "chi"
  constant Gin (line 15) | Gin             Framework = "gin"
  constant Fiber (line 16) | Fiber           Framework = "fiber"
  constant GorillaMux (line 17) | GorillaMux      Framework = "gorilla/mux"
  constant HttpRouter (line 18) | HttpRouter      Framework = "httprouter"
  constant StandardLibrary (line 19) | StandardLibrary Framework = "standard-library"
  constant Echo (line 20) | Echo            Framework = "echo"

FILE: cmd/flags/git.go
  type Git (line 8) | type Git
    method String (line 18) | func (f Git) String() string {
    method Type (line 22) | func (f *Git) Type() string {
    method Set (line 26) | func (f *Git) Set(value string) error {
  constant Commit (line 11) | Commit = "commit"
  constant Stage (line 12) | Stage  = "stage"
  constant Skip (line 13) | Skip   = "skip"

FILE: cmd/program/program.go
  type Project (line 28) | type Project struct
    method CheckOS (line 125) | func (p *Project) CheckOS() {
    method ExitCLI (line 141) | func (p *Project) ExitCLI(tprogram *tea.Program) {
    method createFrameworkMap (line 153) | func (p *Project) createFrameworkMap() {
    method createDBDriverMap (line 190) | func (p *Project) createDBDriverMap() {
    method createDockerMap (line 218) | func (p *Project) createDockerMap() {
    method CreateMainFile (line 245) | func (p *Project) CreateMainFile() error {
    method CreatePath (line 733) | func (p *Project) CreatePath(pathToCreate string, projectPath string) ...
    method CreateFileWithInjection (line 748) | func (p *Project) CreateFileWithInjection(pathToCreate string, project...
    method CreateViteReactProject (line 811) | func (p *Project) CreateViteReactProject(projectPath string) error {
    method CreateHtmxTemplates (line 935) | func (p *Project) CreateHtmxTemplates() {
    method CreateWebsocketImports (line 965) | func (p *Project) CreateWebsocketImports(appDir string) {
  type AdvancedTemplates (line 44) | type AdvancedTemplates struct
  type Framework (line 51) | type Framework struct
  type Driver (line 56) | type Driver struct
  type Docker (line 61) | type Docker struct
  type Templater (line 68) | type Templater interface
  type DBDriverTemplater (line 78) | type DBDriverTemplater interface
  type DockerTemplater (line 84) | type DockerTemplater interface
  type WorkflowTemplater (line 88) | type WorkflowTemplater interface
  constant root (line 115) | root                 = "/"
  constant cmdApiPath (line 116) | cmdApiPath           = "cmd/api"
  constant cmdWebPath (line 117) | cmdWebPath           = "cmd/web"
  constant internalServerPath (line 118) | internalServerPath   = "internal/server"
  constant internalDatabasePath (line 119) | internalDatabasePath = "internal/database"
  constant gitHubActionPath (line 120) | gitHubActionPath     = ".github/workflows"
  function checkNpmInstalled (line 994) | func checkNpmInstalled() error {

FILE: cmd/root.go
  function Execute (line 19) | func Execute() {
  function init (line 26) | func init() {

FILE: cmd/steps/steps.go
  type StepSchema (line 9) | type StepSchema struct
  type Steps (line 17) | type Steps struct
  type Item (line 23) | type Item struct
  function InitSteps (line 28) | func InitSteps(projectType flags.Framework, databaseType flags.Database)...

FILE: cmd/template/advanced/docker.go
  function Dockerfile (line 13) | func Dockerfile() []byte {
  function DockerCompose (line 17) | func DockerCompose() []byte {

FILE: cmd/template/advanced/gitHubAction.go
  function Releaser (line 16) | func Releaser() []byte {
  function Test (line 20) | func Test() []byte {
  function ReleaserConfig (line 24) | func ReleaserConfig() []byte {

FILE: cmd/template/advanced/routes.go
  function EchoHtmxTemplRoutesTemplate (line 82) | func EchoHtmxTemplRoutesTemplate() []byte {
  function GorillaHtmxTemplRoutesTemplate (line 86) | func GorillaHtmxTemplRoutesTemplate() []byte {
  function ChiHtmxTemplRoutesTemplate (line 90) | func ChiHtmxTemplRoutesTemplate() []byte {
  function GinHtmxTemplRoutesTemplate (line 94) | func GinHtmxTemplRoutesTemplate() []byte {
  function HttpRouterHtmxTemplRoutesTemplate (line 98) | func HttpRouterHtmxTemplRoutesTemplate() []byte {
  function StdLibHtmxTemplRoutesTemplate (line 102) | func StdLibHtmxTemplRoutesTemplate() []byte {
  function StdLibHtmxTemplImportsTemplate (line 106) | func StdLibHtmxTemplImportsTemplate() []byte {
  function StdLibWebsocketTemplImportsTemplate (line 110) | func StdLibWebsocketTemplImportsTemplate() []byte {
  function HelloTemplTemplate (line 114) | func HelloTemplTemplate() []byte {
  function BaseTemplTemplate (line 118) | func BaseTemplTemplate() []byte {
  function ReactTailwindAppfile (line 122) | func ReactTailwindAppfile() []byte {
  function ReactAppfile (line 126) | func ReactAppfile() []byte {
  function InputCssTemplateReact (line 130) | func InputCssTemplateReact() []byte {
  function ViteTailwindConfigFile (line 134) | func ViteTailwindConfigFile() []byte {
  function InputCssTemplate (line 138) | func InputCssTemplate() []byte {
  function OutputCssTemplate (line 142) | func OutputCssTemplate() []byte {
  function HtmxTailwindConfigJsTemplate (line 146) | func HtmxTailwindConfigJsTemplate() []byte {
  function HtmxJSTemplate (line 150) | func HtmxJSTemplate() []byte {
  function EfsTemplate (line 154) | func EfsTemplate() []byte {
  function HelloGoTemplate (line 158) | func HelloGoTemplate() []byte {
  function HelloFiberGoTemplate (line 162) | func HelloFiberGoTemplate() []byte {
  function FiberHtmxTemplRoutesTemplate (line 166) | func FiberHtmxTemplRoutesTemplate() []byte {
  function FiberHtmxTemplImportsTemplate (line 170) | func FiberHtmxTemplImportsTemplate() []byte {
  function FiberWebsocketTemplImportsTemplate (line 174) | func FiberWebsocketTemplImportsTemplate() []byte {
  function GinHtmxTemplImportsTemplate (line 178) | func GinHtmxTemplImportsTemplate() []byte {

FILE: cmd/template/dbdriver/mongo.go
  type MongoTemplate (line 7) | type MongoTemplate struct
    method Service (line 18) | func (m MongoTemplate) Service() []byte {
    method Env (line 22) | func (m MongoTemplate) Env() []byte {
    method Tests (line 26) | func (m MongoTemplate) Tests() []byte {

FILE: cmd/template/dbdriver/mysql.go
  type MysqlTemplate (line 7) | type MysqlTemplate struct
    method Service (line 18) | func (m MysqlTemplate) Service() []byte {
    method Env (line 22) | func (m MysqlTemplate) Env() []byte {
    method Tests (line 26) | func (m MysqlTemplate) Tests() []byte {

FILE: cmd/template/dbdriver/postgres.go
  type PostgresTemplate (line 7) | type PostgresTemplate struct
    method Service (line 18) | func (m PostgresTemplate) Service() []byte {
    method Env (line 22) | func (m PostgresTemplate) Env() []byte {
    method Tests (line 26) | func (m PostgresTemplate) Tests() []byte {

FILE: cmd/template/dbdriver/redis.go
  type RedisTemplate (line 7) | type RedisTemplate struct
    method Service (line 18) | func (r RedisTemplate) Service() []byte {
    method Env (line 22) | func (r RedisTemplate) Env() []byte {
    method Tests (line 26) | func (r RedisTemplate) Tests() []byte {

FILE: cmd/template/dbdriver/scylla.go
  type ScyllaTemplate (line 7) | type ScyllaTemplate struct
    method Service (line 18) | func (r ScyllaTemplate) Service() []byte {
    method Env (line 22) | func (r ScyllaTemplate) Env() []byte {
    method Tests (line 26) | func (r ScyllaTemplate) Tests() []byte {

FILE: cmd/template/dbdriver/sqlite.go
  type SqliteTemplate (line 7) | type SqliteTemplate struct
    method Service (line 15) | func (m SqliteTemplate) Service() []byte {
    method Env (line 19) | func (m SqliteTemplate) Env() []byte {
    method Tests (line 23) | func (m SqliteTemplate) Tests() []byte {

FILE: cmd/template/docker/mongo.go
  type MongoDockerTemplate (line 7) | type MongoDockerTemplate struct
    method Docker (line 12) | func (m MongoDockerTemplate) Docker() []byte {

FILE: cmd/template/docker/mysql.go
  type MysqlDockerTemplate (line 7) | type MysqlDockerTemplate struct
    method Docker (line 12) | func (m MysqlDockerTemplate) Docker() []byte {

FILE: cmd/template/docker/postgres.go
  type PostgresDockerTemplate (line 7) | type PostgresDockerTemplate struct
    method Docker (line 12) | func (m PostgresDockerTemplate) Docker() []byte {

FILE: cmd/template/docker/redis.go
  type RedisDockerTemplate (line 7) | type RedisDockerTemplate struct
    method Docker (line 12) | func (r RedisDockerTemplate) Docker() []byte {

FILE: cmd/template/docker/scylla.go
  type ScyllaDockerTemplate (line 7) | type ScyllaDockerTemplate struct
    method Docker (line 12) | func (r ScyllaDockerTemplate) Docker() []byte {

FILE: cmd/template/framework/chiRoutes.go
  type ChiTemplates (line 17) | type ChiTemplates struct
    method Main (line 19) | func (c ChiTemplates) Main() []byte {
    method Server (line 23) | func (c ChiTemplates) Server() []byte {
    method Routes (line 27) | func (c ChiTemplates) Routes() []byte {
    method TestHandler (line 31) | func (c ChiTemplates) TestHandler() []byte {
    method HtmxTemplImports (line 35) | func (c ChiTemplates) HtmxTemplImports() []byte {
    method HtmxTemplRoutes (line 39) | func (c ChiTemplates) HtmxTemplRoutes() []byte {
    method WebsocketImports (line 43) | func (c ChiTemplates) WebsocketImports() []byte {

FILE: cmd/template/framework/echoRoutes.go
  type EchoTemplates (line 17) | type EchoTemplates struct
    method Main (line 19) | func (e EchoTemplates) Main() []byte {
    method Server (line 22) | func (e EchoTemplates) Server() []byte {
    method Routes (line 26) | func (e EchoTemplates) Routes() []byte {
    method TestHandler (line 30) | func (e EchoTemplates) TestHandler() []byte {
    method HtmxTemplImports (line 34) | func (e EchoTemplates) HtmxTemplImports() []byte {
    method HtmxTemplRoutes (line 38) | func (e EchoTemplates) HtmxTemplRoutes() []byte {
    method WebsocketImports (line 42) | func (e EchoTemplates) WebsocketImports() []byte {

FILE: cmd/template/framework/fiberServer.go
  type FiberTemplates (line 23) | type FiberTemplates struct
    method Main (line 25) | func (f FiberTemplates) Main() []byte {
    method Server (line 28) | func (f FiberTemplates) Server() []byte {
    method Routes (line 32) | func (f FiberTemplates) Routes() []byte {
    method TestHandler (line 36) | func (f FiberTemplates) TestHandler() []byte {
    method HtmxTemplImports (line 40) | func (f FiberTemplates) HtmxTemplImports() []byte {
    method HtmxTemplRoutes (line 44) | func (f FiberTemplates) HtmxTemplRoutes() []byte {
    method WebsocketImports (line 48) | func (f FiberTemplates) WebsocketImports() []byte {

FILE: cmd/template/framework/ginRoutes.go
  type GinTemplates (line 17) | type GinTemplates struct
    method Main (line 19) | func (g GinTemplates) Main() []byte {
    method Server (line 23) | func (g GinTemplates) Server() []byte {
    method Routes (line 27) | func (g GinTemplates) Routes() []byte {
    method TestHandler (line 31) | func (g GinTemplates) TestHandler() []byte {
    method HtmxTemplImports (line 35) | func (g GinTemplates) HtmxTemplImports() []byte {
    method HtmxTemplRoutes (line 39) | func (g GinTemplates) HtmxTemplRoutes() []byte {
    method WebsocketImports (line 43) | func (g GinTemplates) WebsocketImports() []byte {

FILE: cmd/template/framework/gorillaRoutes.go
  type GorillaTemplates (line 17) | type GorillaTemplates struct
    method Main (line 19) | func (g GorillaTemplates) Main() []byte {
    method Server (line 23) | func (g GorillaTemplates) Server() []byte {
    method Routes (line 27) | func (g GorillaTemplates) Routes() []byte {
    method TestHandler (line 31) | func (g GorillaTemplates) TestHandler() []byte {
    method HtmxTemplImports (line 35) | func (g GorillaTemplates) HtmxTemplImports() []byte {
    method HtmxTemplRoutes (line 39) | func (g GorillaTemplates) HtmxTemplRoutes() []byte {
    method WebsocketImports (line 43) | func (g GorillaTemplates) WebsocketImports() []byte {

FILE: cmd/template/framework/httpRoutes.go
  type StandardLibTemplate (line 20) | type StandardLibTemplate struct
    method Main (line 22) | func (s StandardLibTemplate) Main() []byte {
    method Server (line 26) | func (s StandardLibTemplate) Server() []byte {
    method Routes (line 30) | func (s StandardLibTemplate) Routes() []byte {
    method TestHandler (line 34) | func (s StandardLibTemplate) TestHandler() []byte {
    method HtmxTemplImports (line 38) | func (s StandardLibTemplate) HtmxTemplImports() []byte {
    method HtmxTemplRoutes (line 42) | func (s StandardLibTemplate) HtmxTemplRoutes() []byte {
    method WebsocketImports (line 46) | func (s StandardLibTemplate) WebsocketImports() []byte {

FILE: cmd/template/framework/main.go
  function MakeTemplate (line 26) | func MakeTemplate() []byte {
  function GitIgnoreTemplate (line 30) | func GitIgnoreTemplate() []byte {
  function AirTomlTemplate (line 34) | func AirTomlTemplate() []byte {
  function ReadmeTemplate (line 40) | func ReadmeTemplate() []byte {

FILE: cmd/template/framework/routerRoutes.go
  type RouterTemplates (line 17) | type RouterTemplates struct
    method Main (line 19) | func (r RouterTemplates) Main() []byte {
    method Server (line 22) | func (r RouterTemplates) Server() []byte {
    method Routes (line 26) | func (r RouterTemplates) Routes() []byte {
    method TestHandler (line 30) | func (r RouterTemplates) TestHandler() []byte {
    method HtmxTemplImports (line 34) | func (r RouterTemplates) HtmxTemplImports() []byte {
    method HtmxTemplRoutes (line 38) | func (r RouterTemplates) HtmxTemplRoutes() []byte {
    method WebsocketImports (line 42) | func (r RouterTemplates) WebsocketImports() []byte {

FILE: cmd/template/globalEnv.go
  function GlobalEnvTemplate (line 10) | func GlobalEnvTemplate() []byte {

FILE: cmd/ui/multiInput/multiInput.go
  type Selection (line 25) | type Selection struct
    method Update (line 30) | func (s *Selection) Update(value string) {
  type model (line 37) | type model struct
    method Init (line 46) | func (m model) Init() tea.Cmd {
    method Update (line 65) | func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    method View (line 104) | func (m model) View() string {
  function InitialModelMulti (line 52) | func InitialModelMulti(choices []steps.Item, selection *Selection, heade...

FILE: cmd/ui/multiSelect/multiSelect.go
  type Selection (line 25) | type Selection struct
    method Update (line 30) | func (s *Selection) Update(optionName string, value bool) {
  type model (line 37) | type model struct
    method Init (line 46) | func (m model) Init() tea.Cmd {
    method Update (line 65) | func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    method View (line 99) | func (m model) View() string {
  function InitialModelMultiSelect (line 52) | func InitialModelMultiSelect(options []steps.Item, selection *Selection,...

FILE: cmd/ui/spinner/spinner.go
  type errMsg (line 11) | type errMsg
  type model (line 13) | type model struct
    method Init (line 26) | func (m model) Init() tea.Cmd {
    method Update (line 30) | func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    method View (line 52) | func (m model) View() string {
  function InitialModelNew (line 19) | func InitialModelNew() model {

FILE: cmd/ui/textinput/textinput.go
  type errMsg (line 22) | type errMsg
  type Output (line 26) | type Output struct
    method update (line 31) | func (o *Output) update(val string) {
  type model (line 38) | type model struct
    method Init (line 93) | func (m model) Init() tea.Cmd {
    method Update (line 99) | func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    method View (line 127) | func (m model) View() string {
    method Err (line 134) | func (m model) Err() string {
  function sanitizeInput (line 47) | func sanitizeInput(input string) error {
  function InitialTextInputModel (line 57) | func InitialTextInputModel(output *Output, header string, program *progr...
  function CreateErrorInputModel (line 75) | func CreateErrorInputModel(err error) model {

FILE: cmd/ui/textinput/textinput_test.go
  function TestInputSanitization (line 5) | func TestInputSanitization(t *testing.T) {

FILE: cmd/utils/utils.go
  constant ProgramName (line 17) | ProgramName = "go-blueprint"
  function NonInteractiveCommand (line 21) | func NonInteractiveCommand(use string, flagSet *pflag.FlagSet) string {
  function RegisterStaticCompletions (line 52) | func RegisterStaticCompletions(cmd *cobra.Command, flag string, options ...
  function ExecuteCmd (line 63) | func ExecuteCmd(name string, args []string, dir string) error {
  function InitGoMod (line 78) | func InitGoMod(projectName string, appDir string) error {
  function GoGetPackage (line 90) | func GoGetPackage(appDir string, packages []string) error {
  function GoFmt (line 104) | func GoFmt(appDir string) error {
  function GoModReplace (line 116) | func GoModReplace(appDir string, replace string) error {
  function GoTidy (line 127) | func GoTidy(appDir string) error {
  function CheckGitConfig (line 135) | func CheckGitConfig(key string) (bool, error) {
  function ValidateModuleName (line 154) | func ValidateModuleName(moduleName string) bool {
  function GetRootDir (line 161) | func GetRootDir(moduleName string) string {

FILE: cmd/utils/utils_test.go
  function TestValidateModuleName (line 5) | func TestValidateModuleName(t *testing.T) {
  function TestGeRootDir (line 41) | func TestGeRootDir(t *testing.T) {

FILE: cmd/version.go
  function getGoBlueprintVersion (line 23) | func getGoBlueprintVersion() string {

FILE: main.go
  function main (line 5) | func main() {
Condensed preview — 163 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (395K chars).
[
  {
    "path": ".github/FUNDING.yml",
    "chars": 67,
    "preview": "# These are supported funding model platforms\n\ngithub: [melkeydev]\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug-report.yml",
    "chars": 1457,
    "preview": "name: Bug Report\ndescription: Found a bug? Please let us know!\ntitle: \"[Bug]\"\nlabels: [\"Bug\"]\nbody:\n  - type: markdown\n "
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "chars": 141,
    "preview": "blank_issues_enabled: true\ncontact_links:\n  - name: Melkeydev Discord\n    url: https://discord.gg/HHZMSCu\n    about: Cha"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature-request.yml",
    "chars": 758,
    "preview": "name: Feature Request\ndescription: Suggest a new idea for go-blueprint.\ntitle: \"[Feature Request] \"\nlabels: [\"enhancemen"
  },
  {
    "path": ".github/pull_request_template.md",
    "chars": 390,
    "preview": "By submitting this pull request, I confirm that my contribution is made under the terms of the MIT license.\n\n## Problem/"
  },
  {
    "path": ".github/semantic.yml",
    "chars": 15,
    "preview": "titleOnly: true"
  },
  {
    "path": ".github/workflows/ci.yml",
    "chars": 1163,
    "preview": "name: continuous integration\n\non:\n  push:\n    paths:\n      - '**.go'\n      - go.sum\n      - go.mod\n    branches-ignore:\n"
  },
  {
    "path": ".github/workflows/docs.yml",
    "chars": 1169,
    "preview": "name: Deploy Docs\n\non:\n  push:\n    branches:\n      - main\n\njobs:\n  build-deploy:\n    name: Build and deploy docs\n\n    ru"
  },
  {
    "path": ".github/workflows/generate-linter-advanced.yml",
    "chars": 1704,
    "preview": "name: Linting Generated Blueprints Advanced\n\non:\n  pull_request: {}\n  workflow_dispatch: {}\n\njobs:\n  framework_matrix:\n "
  },
  {
    "path": ".github/workflows/generate-linter-core.yml",
    "chars": 1359,
    "preview": "name: Linting Generated Blueprints Core\n\non:\n  pull_request: {}\n  workflow_dispatch: {}\n\njobs:\n  framework_matrix:\n    s"
  },
  {
    "path": ".github/workflows/npm-publish.yml",
    "chars": 1612,
    "preview": "name: npm-publish\n\non:\n  workflow_call:\n    inputs:\n      tag:\n        description: \"Release tag to publish (e.g., v1.0."
  },
  {
    "path": ".github/workflows/release.yml",
    "chars": 788,
    "preview": "name: goreleaser\n\non:\n  push:\n    tags:\n      - \"v*.*.*\"\n\npermissions:\n  contents: write\n\njobs:\n  goreleaser:\n    runs-o"
  },
  {
    "path": ".github/workflows/testcontainers.yml",
    "chars": 965,
    "preview": "name: Integrations Test for the Generated Blueprints\n\non:\n  pull_request: {}\n  workflow_dispatch: {}\n\njobs:\n  itests_mat"
  },
  {
    "path": ".github/workflows/update-htmx-version.yml",
    "chars": 3020,
    "preview": "name: Check for new htmx release\n\non:\n  schedule:\n    - cron: '0 0 * * Sun'\n\njobs:\n  check_release:\n    runs-on: ubuntu-"
  },
  {
    "path": ".gitignore",
    "chars": 18,
    "preview": "go-blueprint\nsite\n"
  },
  {
    "path": ".goreleaser.yml",
    "chars": 863,
    "preview": "before:\n  hooks:\n    - go mod tidy\n    - ./scripts/completions.sh\nbuilds:\n  - binary: go-blueprint\n    main: ./\n    goos"
  },
  {
    "path": ".pre-commit-config.yaml",
    "chars": 406,
    "preview": "# See https://pre-commit.com for more information\n# See https://pre-commit.com/hooks.html for more hooks\nrepos:\n  - repo"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 1356,
    "preview": "# Contributing\n\nThanks for helping make go-blueprint better! \n- [Contributing](#contributing)\n  - [Design Principles](#d"
  },
  {
    "path": "LICENSE",
    "chars": 1097,
    "preview": "The MIT License (MIT)\r\n\r\nCopyright (c) 2023 Melkeydev\r\n\r\nPermission is hereby granted, free of charge, to any person obt"
  },
  {
    "path": "README.md",
    "chars": 6710,
    "preview": "![logo](./public/logo.png)\n\n<div style=\"text-align: center;\">\n  <h1>\n    Introducing the Ultimate Golang Blueprint Libra"
  },
  {
    "path": "cmd/create.go",
    "chars": 12585,
    "preview": "package cmd\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"strings\"\n\t\"sync\"\n\n\ttea \"github.com/charmbracelet/bubbletea\"\n\t\"github.com/cha"
  },
  {
    "path": "cmd/flags/advancedFeatures.go",
    "chars": 1014,
    "preview": "package flags\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\ntype AdvancedFeatures []string\n\nconst (\n\tHtmx              string = \"htmx\"\n"
  },
  {
    "path": "cmd/flags/database.go",
    "chars": 1072,
    "preview": "package flags\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\ntype Database string\n\n// These are all the current databases supported. If "
  },
  {
    "path": "cmd/flags/frameworks.go",
    "chars": 1173,
    "preview": "package flags\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\ntype Framework string\n\n// These are all the current frameworks supported. I"
  },
  {
    "path": "cmd/flags/git.go",
    "chars": 556,
    "preview": "package flags\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\ntype Git string\n\nconst (\n\tCommit = \"commit\"\n\tStage  = \"stage\"\n\tSkip   = \"sk"
  },
  {
    "path": "cmd/program/program.go",
    "chars": 29622,
    "preview": "// Package program provides the\n// main functionality of Blueprint\npackage program\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"log\"\n\t\"os"
  },
  {
    "path": "cmd/root.go",
    "chars": 648,
    "preview": "/*\nCopyright © 2023 Melkey melkeydev@gmail.com\n*/\npackage cmd\n\nimport (\n\t\"os\"\n\n\t\"github.com/spf13/cobra\"\n)\n\nvar rootCmd "
  },
  {
    "path": "cmd/steps/steps.go",
    "chars": 4395,
    "preview": "// Package steps provides utility for creating\n// each step of the CLI\npackage steps\n\nimport \"github.com/melkeydev/go-bl"
  },
  {
    "path": "cmd/template/advanced/docker.go",
    "chars": 313,
    "preview": "package advanced\n\nimport (\n\t_ \"embed\"\n)\n\n//go:embed files/docker/dockerfile.tmpl\nvar dockerfileTemplate []byte\n\n//go:emb"
  },
  {
    "path": "cmd/template/advanced/files/docker/docker_compose.yml.tmpl",
    "chars": 696,
    "preview": "services:\n  app:\n    build:\n      context: .\n      dockerfile: Dockerfile\n      target: prod\n    restart: unless-stopped"
  },
  {
    "path": "cmd/template/advanced/files/docker/dockerfile.tmpl",
    "chars": 1361,
    "preview": "FROM golang:1.24.4-alpine AS build\n{{- if or (.AdvancedOptions.tailwind) (eq .DBDriver \"sqlite\") }}\nRUN apk add --no-cac"
  },
  {
    "path": "cmd/template/advanced/files/htmx/base.templ.tmpl",
    "chars": 578,
    "preview": "package web\n\ntempl Base() {\n\t<!DOCTYPE html>\n\t<html lang=\"en\" {{if .AdvancedOptions.tailwind}}class=\"h-screen\"{{end}}>\n\t"
  },
  {
    "path": "cmd/template/advanced/files/htmx/efs.go.tmpl",
    "chars": 68,
    "preview": "package web\n\nimport \"embed\"\n\n//go:embed \"assets\"\nvar Files embed.FS\n"
  },
  {
    "path": "cmd/template/advanced/files/htmx/hello.go.tmpl",
    "chars": 437,
    "preview": "package web\n\nimport (\n\t\"log\"\n\t\"net/http\"\n)\n\nfunc HelloWebHandler(w http.ResponseWriter, r *http.Request) {\n\terr := r.Par"
  },
  {
    "path": "cmd/template/advanced/files/htmx/hello.templ.tmpl",
    "chars": 631,
    "preview": "package web\n\ntempl HelloForm() {\n\t@Base() {\n\t\t<form hx-post=\"/hello\" method=\"POST\" hx-target=\"#hello-container\">\n\t\t\t<inp"
  },
  {
    "path": "cmd/template/advanced/files/htmx/hello_fiber.go.tmpl",
    "chars": 1022,
    "preview": "package web\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"log\"\n\n\t\"github.com/gofiber/fiber/v2\"\n)\n\nfunc HelloWebHandler(c *fiber.Ctx) error"
  },
  {
    "path": "cmd/template/advanced/files/htmx/htmx.min.js.tmpl",
    "chars": 83055,
    "preview": "var htmx = (function () {\n  \"use strict\";\n  const Q = {\n    onLoad: null,\n    process: null,\n    on: null,\n    off: null"
  },
  {
    "path": "cmd/template/advanced/files/htmx/imports/fiber.tmpl",
    "chars": 161,
    "preview": "\"github.com/a-h/templ\"\n\"{{.ProjectName}}/cmd/web\"\n\"github.com/gofiber/fiber/v2/middleware/adaptor\"\n\"github.com/gofiber/f"
  },
  {
    "path": "cmd/template/advanced/files/htmx/imports/gin.tmpl",
    "chars": 58,
    "preview": "\"github.com/a-h/templ\"\n\"{{.ProjectName}}/cmd/web\"\n\"io/fs\"\n"
  },
  {
    "path": "cmd/template/advanced/files/htmx/imports/standard_library.tmpl",
    "chars": 49,
    "preview": "\"github.com/a-h/templ\"\n\"{{.ProjectName}}/cmd/web\""
  },
  {
    "path": "cmd/template/advanced/files/htmx/routes/chi.tmpl",
    "chars": 181,
    "preview": "fileServer := http.FileServer(http.FS(web.Files))\n\tr.Handle(\"/assets/*\", fileServer)\n\tr.Get(\"/web\", templ.Handler(web.He"
  },
  {
    "path": "cmd/template/advanced/files/htmx/routes/echo.tmpl",
    "chars": 238,
    "preview": "fileServer := http.FileServer(http.FS(web.Files))\ne.GET(\"/assets/*\", echo.WrapHandler(fileServer))\n\ne.GET(\"/web\", echo.W"
  },
  {
    "path": "cmd/template/advanced/files/htmx/routes/fiber.tmpl",
    "chars": 295,
    "preview": "s.App.Use(\"/assets\", filesystem.New(filesystem.Config{\n\t\tRoot:       http.FS(web.Files),\n\t\tPathPrefix: \"assets\",\n\t\tBrows"
  },
  {
    "path": "cmd/template/advanced/files/htmx/routes/gin.tmpl",
    "chars": 282,
    "preview": "staticFiles, _ := fs.Sub(web.Files, \"assets\")\nr.StaticFS(\"/assets\", http.FS(staticFiles))\n\nr.GET(\"/web\", func(c *gin.Con"
  },
  {
    "path": "cmd/template/advanced/files/htmx/routes/gorilla.tmpl",
    "chars": 261,
    "preview": "fileServer := http.FileServer(http.FS(web.Files))\nr.PathPrefix(\"/assets/\").Handler(fileServer)\n\nr.HandleFunc(\"/web\", fun"
  },
  {
    "path": "cmd/template/advanced/files/htmx/routes/http_router.tmpl",
    "chars": 248,
    "preview": "  fileServer := http.FileServer(http.FS(web.Files))\n  r.Handler(http.MethodGet, \"/assets/*filepath\", fileServer)\n  r.Han"
  },
  {
    "path": "cmd/template/advanced/files/htmx/routes/standard_library.tmpl",
    "chars": 187,
    "preview": "  fileServer := http.FileServer(http.FS(web.Files))\n\tmux.Handle(\"/assets/\", fileServer)\n\tmux.Handle(\"/web\", templ.Handle"
  },
  {
    "path": "cmd/template/advanced/files/htmx/tailwind/tailwind.config.js.tmpl",
    "chars": 117,
    "preview": "module.exports = {\n\tcontent: [\"./**/*.html\", \"./**/*.templ\", \"./**/*.go\",],\n\ttheme: { extend: {}, },\n\tplugins: [],\n}\n"
  },
  {
    "path": "cmd/template/advanced/files/react/app.tsx.tmpl",
    "chars": 1406,
    "preview": "import { useState } from 'react'\nimport reactLogo from './assets/react.svg'\nimport viteLogo from '/vite.svg'\nimport './A"
  },
  {
    "path": "cmd/template/advanced/files/react/tailwind/app.tsx.tmpl",
    "chars": 1969,
    "preview": "import { useState } from 'react'\n\nfunction App() {\n  const [count, setCount] = useState(0)\n  const [message, setMessage]"
  },
  {
    "path": "cmd/template/advanced/files/react/tailwind/index.css.tmpl",
    "chars": 22,
    "preview": "@import \"tailwindcss\";"
  },
  {
    "path": "cmd/template/advanced/files/react/tailwind/vite.config.ts.tmpl",
    "chars": 218,
    "preview": "import { defineConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\nimport tailwindcss from '@tailwindcss/vite'"
  },
  {
    "path": "cmd/template/advanced/files/tailwind/input.css.tmpl",
    "chars": 22,
    "preview": "@import \"tailwindcss\"\n"
  },
  {
    "path": "cmd/template/advanced/files/tailwind/output.css.tmpl",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "cmd/template/advanced/files/websocket/imports/fiber.tmpl",
    "chars": 39,
    "preview": "\"github.com/gofiber/contrib/websocket\"\n"
  },
  {
    "path": "cmd/template/advanced/files/websocket/imports/standard_library.tmpl",
    "chars": 29,
    "preview": "\"github.com/coder/websocket\"\n"
  },
  {
    "path": "cmd/template/advanced/files/workflow/github/github_action_goreleaser.yml.tmpl",
    "chars": 871,
    "preview": "name: goreleaser\n\non:\n  push:\n    tags:\n      - \"v*.*.*\"\n\npermissions:\n  contents: write\n\njobs:\n  goreleaser:\n    runs-o"
  },
  {
    "path": "cmd/template/advanced/files/workflow/github/github_action_gotest.yml.tmpl",
    "chars": 617,
    "preview": "name: Go-test\non: [push, pull_request]\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions/chec"
  },
  {
    "path": "cmd/template/advanced/files/workflow/github/github_action_releaser_config.yml.tmpl",
    "chars": 956,
    "preview": "version: 2\nbefore:\n  hooks:\n  - go mod tidy\n\nenv:\n  - PACKAGE_PATH=github.com/<user>/<repo>/cmd\n\nbuilds:\n- binary: \"{{\"{"
  },
  {
    "path": "cmd/template/advanced/gitHubAction.go",
    "chars": 544,
    "preview": "package advanced\n\nimport (\n\t_ \"embed\"\n)\n\n//go:embed files/workflow/github/github_action_goreleaser.yml.tmpl\nvar gitHubAc"
  },
  {
    "path": "cmd/template/advanced/routes.go",
    "chars": 3724,
    "preview": "package advanced\n\nimport (\n\t_ \"embed\"\n)\n\n//go:embed files/htmx/hello.templ.tmpl\nvar helloTemplTemplate []byte\n\n//go:embe"
  },
  {
    "path": "cmd/template/dbdriver/files/env/mongo.tmpl",
    "chars": 204,
    "preview": "{{ if .AdvancedOptions.docker }}\nBLUEPRINT_DB_HOST=mongo_bp\n{{- else }}\nBLUEPRINT_DB_HOST=localhost\n{{- end }}\nBLUEPRINT"
  },
  {
    "path": "cmd/template/dbdriver/files/env/mysql.tmpl",
    "chars": 271,
    "preview": "{{- if .AdvancedOptions.docker }}\nBLUEPRINT_DB_HOST=mysql_bp\n{{- else }}\nBLUEPRINT_DB_HOST=localhost\n{{- end }}\nBLUEPRIN"
  },
  {
    "path": "cmd/template/dbdriver/files/env/postgres.tmpl",
    "chars": 257,
    "preview": "{{- if .AdvancedOptions.docker }}\nBLUEPRINT_DB_HOST=psql_bp\n{{- else }}\nBLUEPRINT_DB_HOST=localhost\n{{- end }}\nBLUEPRINT"
  },
  {
    "path": "cmd/template/dbdriver/files/env/redis.tmpl",
    "chars": 188,
    "preview": "{{- if .AdvancedOptions.docker }}\nBLUEPRINT_DB_ADDRESS=redis_bp\n{{- else }}\nBLUEPRINT_DB_ADDRESS=localhost\n{{- end }}\nBL"
  },
  {
    "path": "cmd/template/dbdriver/files/env/scylla.tmpl",
    "chars": 392,
    "preview": "{{- if .AdvancedOptions.docker }}\n# BLUEPRINT_DB_HOSTS=scylla_bp:9042 # ScyllaDB default port\nBLUEPRINT_DB_HOSTS=scylla_"
  },
  {
    "path": "cmd/template/dbdriver/files/env/sqlite.tmpl",
    "chars": 114,
    "preview": "{{- if .AdvancedOptions.docker }}\nBLUEPRINT_DB_URL=./db/test.db\n{{- else }}\nBLUEPRINT_DB_URL=./test.db\n{{- end }}\n"
  },
  {
    "path": "cmd/template/dbdriver/files/service/mongo.tmpl",
    "chars": 936,
    "preview": "package database\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/mongo\"\n\t\"go.mongodb.org"
  },
  {
    "path": "cmd/template/dbdriver/files/service/mysql.tmpl",
    "chars": 3678,
    "preview": "package database\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"strconv\"\n\t\"time\"\n\n\t_ \"github.com/go-sql-driv"
  },
  {
    "path": "cmd/template/dbdriver/files/service/postgres.tmpl",
    "chars": 3534,
    "preview": "package database\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"strconv\"\n\t\"time\"\n\n\t_ \"github.com/jackc/pgx/v"
  },
  {
    "path": "cmd/template/dbdriver/files/service/redis.tmpl",
    "chars": 7354,
    "preview": "package database\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"math\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t_ \"github.com/joho/god"
  },
  {
    "path": "cmd/template/dbdriver/files/service/scylla.tmpl",
    "chars": 4763,
    "preview": "package database\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/gocql/gocql\"\n\t_ \"g"
  },
  {
    "path": "cmd/template/dbdriver/files/service/sqlite.tmpl",
    "chars": 3260,
    "preview": "package database\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"strconv\"\n\t\"time\"\n\n\t_ \"github.com/mattn/go-sq"
  },
  {
    "path": "cmd/template/dbdriver/files/tests/mongo.tmpl",
    "chars": 1292,
    "preview": "package database\n\nimport (\n\t\"context\"\n\t\"log\"\n\t\"testing\"\n\n\t\"github.com/testcontainers/testcontainers-go\"\n\t\"github.com/tes"
  },
  {
    "path": "cmd/template/dbdriver/files/tests/mysql.tmpl",
    "chars": 1997,
    "preview": "package database\n\nimport (\n\t\"context\"\n\t\"log\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/testcontainers/testcontainers-go\"\n\t\"github"
  },
  {
    "path": "cmd/template/dbdriver/files/tests/postgres.tmpl",
    "chars": 2069,
    "preview": "package database\n\nimport (\n\t\"context\"\n\t\"log\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/testcontainers/testcontainers-go\"\n\t\"github"
  },
  {
    "path": "cmd/template/dbdriver/files/tests/redis.tmpl",
    "chars": 1514,
    "preview": "package database\n\nimport (\n\t\"context\"\n\t\"log\"\n\t\"testing\"\n\n\t\"github.com/testcontainers/testcontainers-go\"\n\t\"github.com/tes"
  },
  {
    "path": "cmd/template/dbdriver/files/tests/scylla.tmpl",
    "chars": 2972,
    "preview": "package database\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"github.com/docker/go-connections/nat\"\n\t\"github.com/testcontainers/testcon"
  },
  {
    "path": "cmd/template/dbdriver/mongo.go",
    "chars": 492,
    "preview": "package dbdriver\n\nimport (\n\t_ \"embed\"\n)\n\ntype MongoTemplate struct{}\n\n//go:embed files/service/mongo.tmpl\nvar mongoServi"
  },
  {
    "path": "cmd/template/dbdriver/mysql.go",
    "chars": 492,
    "preview": "package dbdriver\n\nimport (\n\t_ \"embed\"\n)\n\ntype MysqlTemplate struct{}\n\n//go:embed files/service/mysql.tmpl\nvar mysqlServi"
  },
  {
    "path": "cmd/template/dbdriver/postgres.go",
    "chars": 531,
    "preview": "package dbdriver\n\nimport (\n\t_ \"embed\"\n)\n\ntype PostgresTemplate struct{}\n\n//go:embed files/service/postgres.tmpl\nvar post"
  },
  {
    "path": "cmd/template/dbdriver/redis.go",
    "chars": 492,
    "preview": "package dbdriver\n\nimport (\n\t_ \"embed\"\n)\n\ntype RedisTemplate struct{}\n\n//go:embed files/service/redis.tmpl\nvar redisServi"
  },
  {
    "path": "cmd/template/dbdriver/scylla.go",
    "chars": 505,
    "preview": "package dbdriver\n\nimport (\n\t_ \"embed\"\n)\n\ntype ScyllaTemplate struct{}\n\n//go:embed files/service/scylla.tmpl\nvar scyllaSe"
  },
  {
    "path": "cmd/template/dbdriver/sqlite.go",
    "chars": 409,
    "preview": "package dbdriver\n\nimport (\n\t_ \"embed\"\n)\n\ntype SqliteTemplate struct{}\n\n//go:embed files/service/sqlite.tmpl\nvar sqliteSe"
  },
  {
    "path": "cmd/template/docker/files/docker-compose/mongo.tmpl",
    "chars": 1497,
    "preview": "services:\n{{- if .AdvancedOptions.docker }}\n  app:\n    build:\n      context: .\n      dockerfile: Dockerfile\n      target"
  },
  {
    "path": "cmd/template/docker/files/docker-compose/mysql.tmpl",
    "chars": 1695,
    "preview": "services:\n{{- if .AdvancedOptions.docker }}\n  app:\n    build:\n      context: .\n      dockerfile: Dockerfile\n      target"
  },
  {
    "path": "cmd/template/docker/files/docker-compose/postgres.tmpl",
    "chars": 1658,
    "preview": "services:\n{{- if .AdvancedOptions.docker }}\n  app:\n    build:\n      context: .\n      dockerfile: Dockerfile\n      target"
  },
  {
    "path": "cmd/template/docker/files/docker-compose/redis.tmpl",
    "chars": 1201,
    "preview": "services:\n{{- if .AdvancedOptions.docker }}\n  app:\n    build:\n      context: .\n      dockerfile: Dockerfile\n      target"
  },
  {
    "path": "cmd/template/docker/files/docker-compose/scylla.tmpl",
    "chars": 1684,
    "preview": "services:\n{{- if .AdvancedOptions.docker }}\n  app:\n    build:\n      context: .\n      dockerfile: Dockerfile\n      target"
  },
  {
    "path": "cmd/template/docker/mongo.go",
    "chars": 225,
    "preview": "package docker\n\nimport (\n\t_ \"embed\"\n)\n\ntype MongoDockerTemplate struct{}\n\n//go:embed files/docker-compose/mongo.tmpl\nvar"
  },
  {
    "path": "cmd/template/docker/mysql.go",
    "chars": 225,
    "preview": "package docker\n\nimport (\n\t_ \"embed\"\n)\n\ntype MysqlDockerTemplate struct{}\n\n//go:embed files/docker-compose/mysql.tmpl\nvar"
  },
  {
    "path": "cmd/template/docker/postgres.go",
    "chars": 240,
    "preview": "package docker\n\nimport (\n\t_ \"embed\"\n)\n\ntype PostgresDockerTemplate struct{}\n\n//go:embed files/docker-compose/postgres.tm"
  },
  {
    "path": "cmd/template/docker/redis.go",
    "chars": 226,
    "preview": "package docker\n\nimport (\n\t_ \"embed\"\n)\n\ntype RedisDockerTemplate struct{}\n\n//go:embed files/docker-compose/redis.tmpl\nvar"
  },
  {
    "path": "cmd/template/docker/scylla.go",
    "chars": 231,
    "preview": "package docker\n\nimport (\n\t_ \"embed\"\n)\n\ntype ScyllaDockerTemplate struct{}\n\n//go:embed files/docker-compose/scylla.tmpl\nv"
  },
  {
    "path": "cmd/template/framework/chiRoutes.go",
    "chars": 966,
    "preview": "package framework\n\nimport (\n\t_ \"embed\"\n\n\t\"github.com/melkeydev/go-blueprint/cmd/template/advanced\"\n)\n\n//go:embed files/r"
  },
  {
    "path": "cmd/template/framework/echoRoutes.go",
    "chars": 980,
    "preview": "package framework\n\nimport (\n\t_ \"embed\"\n\n\t\"github.com/melkeydev/go-blueprint/cmd/template/advanced\"\n)\n\n//go:embed files/r"
  },
  {
    "path": "cmd/template/framework/fiberServer.go",
    "chars": 1137,
    "preview": "package framework\n\nimport (\n\t_ \"embed\"\n\n\t\"github.com/melkeydev/go-blueprint/cmd/template/advanced\"\n)\n\n//go:embed files/r"
  },
  {
    "path": "cmd/template/framework/files/README.md.tmpl",
    "chars": 858,
    "preview": "# Project {{.ProjectName}}\n\nOne Paragraph of project description goes here\n\n## Getting Started\n\nThese instructions will "
  },
  {
    "path": "cmd/template/framework/files/air.toml.tmpl",
    "chars": 986,
    "preview": "root = \".\"\ntestdata_dir = \"testdata\"\ntmp_dir = \"tmp\"\n\n[build]\n  args_bin = []\n  bin = {{if .OSCheck.UnixBased }}\"./main\""
  },
  {
    "path": "cmd/template/framework/files/gitignore.tmpl",
    "chars": 525,
    "preview": "# Binaries for programs and plugins\n*.exe\n*.exe~\n*.dll\n*.so\n*.dylib\n\n# Test binary, built with \"go test -c\"\n*.test\n\n# Ou"
  },
  {
    "path": "cmd/template/framework/files/globalenv.tmpl",
    "chars": 24,
    "preview": "PORT=8080\nAPP_ENV=local\n"
  },
  {
    "path": "cmd/template/framework/files/main/fiber_main.go.tmpl",
    "chars": 1568,
    "preview": "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"os/signal\"\n\t\"strconv\"\n\t\"syscall\"\n\t\"time\"\n\t\"{{.ProjectName}}/inte"
  },
  {
    "path": "cmd/template/framework/files/main/main.go.tmpl",
    "chars": 1425,
    "preview": "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\t\"os/signal\"\n\t\"syscall\"\n\t\"time\"\n\n\t\"{{.ProjectName}}/internal"
  },
  {
    "path": "cmd/template/framework/files/makefile.tmpl",
    "chars": 5166,
    "preview": "# Simple Makefile for a Go project\n\n# Build the application\nall: build test\n\n{{- if and (or .AdvancedOptions.htmx .Advan"
  },
  {
    "path": "cmd/template/framework/files/routes/chi.go.tmpl",
    "chars": 2074,
    "preview": "package server\n\nimport (\n\t\"encoding/json\"\n\t\"log\"\n\t\"net/http\"\n  {{if .AdvancedOptions.websocket}}\n\t\"fmt\"\n\t\"time\"\n  {{end}"
  },
  {
    "path": "cmd/template/framework/files/routes/echo.go.tmpl",
    "chars": 1970,
    "preview": "package server\n\nimport (\n\t\"net/http\"\n  {{if .AdvancedOptions.websocket}}\n\t\"log\"\n\t\"fmt\"\n\t\"time\"\n  {{end}}\n\n\t\"github.com/l"
  },
  {
    "path": "cmd/template/framework/files/routes/fiber.go.tmpl",
    "chars": 1739,
    "preview": "package server\n\nimport (\n  {{if .AdvancedOptions.websocket}}\n\t\"context\"\n\t\"log\"\n\t\"fmt\"\n\t\"time\"\n  {{end}}\n\t\"github.com/gof"
  },
  {
    "path": "cmd/template/framework/files/routes/gin.go.tmpl",
    "chars": 1828,
    "preview": "package server\n\nimport (\n\t\"net/http\"\n  {{if .AdvancedOptions.websocket}}\n\t\"log\"\n\t\"fmt\"\n\t\"time\"\n  {{end}}\n\n\t\"github.com/g"
  },
  {
    "path": "cmd/template/framework/files/routes/gorilla.go.tmpl",
    "chars": 2546,
    "preview": "package server\n\nimport (\n\t\"encoding/json\"\n\t\"log\"\n\t\"net/http\"\n  {{if .AdvancedOptions.websocket}}\n\t\"fmt\"\n\t\"time\"\n  {{end}"
  },
  {
    "path": "cmd/template/framework/files/routes/http_router.go.tmpl",
    "chars": 2684,
    "preview": "package server\n\nimport (\n\t\"encoding/json\"\n\t\"log\"\n\t\"net/http\"\n  {{if .AdvancedOptions.websocket}}\n\t\"fmt\"\n\t\"time\"\n  {{end}"
  },
  {
    "path": "cmd/template/framework/files/routes/standard_library.go.tmpl",
    "chars": 2872,
    "preview": "package server\n\nimport (\n\t\"encoding/json\"\n\t\"log\"\n\t\"net/http\"\n  {{if .AdvancedOptions.websocket}}\n\t\"fmt\"\n\t\"time\"\n  {{end}"
  },
  {
    "path": "cmd/template/framework/files/server/fiber.go.tmpl",
    "chars": 501,
    "preview": "package server\n\nimport (\n\t\"github.com/gofiber/fiber/v2\"\n  {{if ne .DBDriver \"none\"}}\n\t\"{{.ProjectName}}/internal/databas"
  },
  {
    "path": "cmd/template/framework/files/server/standard_library.go.tmpl",
    "chars": 730,
    "preview": "package server\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"os\"\n\t\"strconv\"\n\t\"time\"\n\n\t_ \"github.com/joho/godotenv/autoload\"\n  {{if ne ."
  },
  {
    "path": "cmd/template/framework/files/tests/default-test.go.tmpl",
    "chars": 806,
    "preview": "package server\nimport (\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\nfunc TestHandler(t *testing.T) {\n    s := &S"
  },
  {
    "path": "cmd/template/framework/files/tests/echo-test.go.tmpl",
    "chars": 1011,
    "preview": "package server\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"reflect\"\n\t\"testing\"\n\t\"github.com/labstack/ec"
  },
  {
    "path": "cmd/template/framework/files/tests/fiber-test.go.tmpl",
    "chars": 1027,
    "preview": "package server\nimport (\n\t\"io\"\n\t\"net/http\"\n\t\"testing\"\n\t\"github.com/gofiber/fiber/v2\"\n)\nfunc TestHandler(t *testing.T) {\n\t"
  },
  {
    "path": "cmd/template/framework/files/tests/gin-test.go.tmpl",
    "chars": 838,
    "preview": "package server\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\t\"github.com/gin-gonic/gin\"\n)\nfunc TestHelloWorldHan"
  },
  {
    "path": "cmd/template/framework/ginRoutes.go",
    "chars": 962,
    "preview": "package framework\n\nimport (\n\t_ \"embed\"\n\n\t\"github.com/melkeydev/go-blueprint/cmd/template/advanced\"\n)\n\n//go:embed files/r"
  },
  {
    "path": "cmd/template/framework/gorillaRoutes.go",
    "chars": 1027,
    "preview": "package framework\n\nimport (\n\t_ \"embed\"\n\n\t\"github.com/melkeydev/go-blueprint/cmd/template/advanced\"\n)\n\n//go:embed files/r"
  },
  {
    "path": "cmd/template/framework/httpRoutes.go",
    "chars": 1136,
    "preview": "package framework\n\nimport (\n\t_ \"embed\"\n\n\t\"github.com/melkeydev/go-blueprint/cmd/template/advanced\"\n)\n\n//go:embed files/r"
  },
  {
    "path": "cmd/template/framework/main.go",
    "chars": 849,
    "preview": "// Package template provides utility functions that\n// help with the templating of created files.\npackage framework\n\nimp"
  },
  {
    "path": "cmd/template/framework/routerRoutes.go",
    "chars": 1049,
    "preview": "package framework\n\nimport (\n\t_ \"embed\"\n\n\t\"github.com/melkeydev/go-blueprint/cmd/template/advanced\"\n)\n\n//go:embed files/r"
  },
  {
    "path": "cmd/template/globalEnv.go",
    "chars": 175,
    "preview": "package template\n\nimport (\n\t_ \"embed\"\n)\n\n//go:embed framework/files/globalenv.tmpl\nvar globalEnvTemplate []byte\n\nfunc Gl"
  },
  {
    "path": "cmd/ui/multiInput/multiInput.go",
    "chars": 3373,
    "preview": "// Package multiInput provides functions that\n// help define and draw a multi-input step\npackage multiInput\n\nimport (\n\t\""
  },
  {
    "path": "cmd/ui/multiSelect/multiSelect.go",
    "chars": 3318,
    "preview": "// Package multiSelect provides functions that\n// help define and draw a multi-select step\npackage multiSelect\n\nimport ("
  },
  {
    "path": "cmd/ui/spinner/spinner.go",
    "chars": 1043,
    "preview": "package spinner\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/charmbracelet/bubbles/spinner\"\n\ttea \"github.com/charmbracelet/bubbletea\"\n"
  },
  {
    "path": "cmd/ui/textinput/textinput.go",
    "chars": 3097,
    "preview": "// Package textinput provides functions that\n// help define and draw a text-input step\npackage textinput\n\nimport (\n\t\"err"
  },
  {
    "path": "cmd/ui/textinput/textinput_test.go",
    "chars": 626,
    "preview": "package textinput\n\nimport \"testing\"\n\nfunc TestInputSanitization(t *testing.T) {\n\tpassTestCases := []string{\n\t\t\"chi\",\n\t\t\""
  },
  {
    "path": "cmd/utils/utils.go",
    "chars": 4432,
    "preview": "// Package utils provides extra utility\n// for the program\npackage utils\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"log\"\n\t\"os/exec\"\n\t\"r"
  },
  {
    "path": "cmd/utils/utils_test.go",
    "chars": 1214,
    "preview": "package utils\n\nimport \"testing\"\n\nfunc TestValidateModuleName(t *testing.T) {\n\tpassTestCases := []string{\n\t\t\"github.com/u"
  },
  {
    "path": "cmd/version.go",
    "chars": 2316,
    "preview": "/*\nGo blueprint version\n*/\npackage cmd\n\nimport (\n\t\"fmt\"\n\t\"runtime/debug\"\n\t\"time\"\n\n\t\"github.com/spf13/cobra\"\n)\n\n// GoBlue"
  },
  {
    "path": "contributors.yml",
    "chars": 860,
    "preview": "- Melkeydev\n- Ujstor\n- tylermeekel\n- MitchellBerend\n- H0llyW00dzZ\n- SudoSurya\n- mimatache\n- SputNikPlop\n- limesten\n- its"
  },
  {
    "path": "docs/Makefile",
    "chars": 351,
    "preview": ".PHONY: docs\n\ndefault: install\n\nall: install build\n\n\nh help:\n\t@grep '^[a-z]' Makefile\n\n\ninstall:\n\tpip install pip --upgr"
  },
  {
    "path": "docs/custom_theme/main.html",
    "chars": 194,
    "preview": "{% extends \"base.html\" %}\n\n{% block libs %}\n    {{ super() }}\n    <script defer data-domain=\"docs.go-blueprint.dev\" src="
  },
  {
    "path": "docs/docs/advanced-flag/advanced-flag.md",
    "chars": 1553,
    "preview": "# Advanced Flag in Blueprint\n\nThe `--advanced` flag in Blueprint serves as a switch to enable additional features during"
  },
  {
    "path": "docs/docs/advanced-flag/docker.md",
    "chars": 3332,
    "preview": "The Docker advanced flag provides the app's Dockerfile configuration and creates or updates the docker-compose.yml file,"
  },
  {
    "path": "docs/docs/advanced-flag/goreleaser.md",
    "chars": 1735,
    "preview": "Release process for Go projects, providing extensive customization options through its configuration file, `.goreleaser."
  },
  {
    "path": "docs/docs/advanced-flag/htmx-templ.md",
    "chars": 2143,
    "preview": "The WEB directory contains the web-related components and assets for the project. It leverages [htmx](https://github.com"
  },
  {
    "path": "docs/docs/advanced-flag/react-vite.md",
    "chars": 6306,
    "preview": "This template provides a minimal setup for getting React working with Vite for the frontend and go on the backend. It al"
  },
  {
    "path": "docs/docs/advanced-flag/tailwind.md",
    "chars": 2344,
    "preview": "Tailwind is closely coupled with the advanced HTMX flag, and HTMX will be automatically used if you select Tailwind in y"
  },
  {
    "path": "docs/docs/advanced-flag/websocket.md",
    "chars": 1048,
    "preview": "A `/websocket` endpoint is added in `routes.go` to facilitate websocket connections. Upon accessing this endpoint, the s"
  },
  {
    "path": "docs/docs/blueprint-core/db-drivers.md",
    "chars": 3652,
    "preview": "To extend the project with database functionality, users can choose from a variety of Go database drivers. Each driver i"
  },
  {
    "path": "docs/docs/blueprint-core/frameworks.md",
    "chars": 1160,
    "preview": "Created projects can utilize several Go web frameworks to handle HTTP routing and server functionality. The chosen frame"
  },
  {
    "path": "docs/docs/blueprint-ui.md",
    "chars": 636,
    "preview": "The Blueprint UI is a crucial component of the Go Blueprint ecosystem, providing a user-friendly interface for creating "
  },
  {
    "path": "docs/docs/creating-project/air.md",
    "chars": 2411,
    "preview": "## Air - Live Reloading Utility\n\n[Air](https://github.com/cosmtrek/air) is a live-reloading utility designed to enhance "
  },
  {
    "path": "docs/docs/creating-project/makefile.md",
    "chars": 2003,
    "preview": "## Makefile Project Management\n\nMakefile is designed for building, running, and testing a Go project. It includes suppor"
  },
  {
    "path": "docs/docs/creating-project/project-init.md",
    "chars": 2565,
    "preview": "## Creating a Project\n\nAfter installing the Go-Blueprint CLI tool, you can create a new project with the default setting"
  },
  {
    "path": "docs/docs/endpoints-test/mongo.md",
    "chars": 1283,
    "preview": "To test the MongoDB Health Check endpoint, use the following curl command:\n\n```bash\ncurl http://localhost:PORT/health\n``"
  },
  {
    "path": "docs/docs/endpoints-test/redis.md",
    "chars": 8379,
    "preview": "To test the Redis Health Check endpoint, use the following curl command:\n\n```bash\ncurl http://localhost:PORT/health\n```\n"
  },
  {
    "path": "docs/docs/endpoints-test/scylladb.md",
    "chars": 4870,
    "preview": "To test the ScyllaDB Health Check endpoint, use the following curl command:\n\n```bash\ncurl http://localhost:PORT/health\n`"
  },
  {
    "path": "docs/docs/endpoints-test/server.md",
    "chars": 1940,
    "preview": "## Testing Endpoints with CURL and WebSocat\n\nTesting endpoints is an essential part of ensuring the correctness and func"
  },
  {
    "path": "docs/docs/endpoints-test/sql.md",
    "chars": 3440,
    "preview": "To test the SQL DB Health Check endpoint, use the following curl command:\n\n```bash\ncurl http://localhost:PORT/health\n```"
  },
  {
    "path": "docs/docs/endpoints-test/web.md",
    "chars": 513,
    "preview": "\nTo test the /web endpoint when HTMX and Temp are used, you can simply open it in a web browser. This endpoint serves an"
  },
  {
    "path": "docs/docs/endpoints-test/websocket.md",
    "chars": 1202,
    "preview": "## Testing with WebSocat\n[WebSocat](https://github.com/vi/websocat) is a versatile tool for working with websockets from"
  },
  {
    "path": "docs/docs/index.md",
    "chars": 5143,
    "preview": "---\nhide:\n  - toc\n---\n\n## Go Blueprint - Ultimate Golang Blueprint Library\n\n![logo](./public/logo.png)\n\nPowerful CLI too"
  },
  {
    "path": "docs/docs/installation.md",
    "chars": 2439,
    "preview": "---\nhide:\n  - toc\n---\n\nGo-Blueprint provides a convenient CLI tool to effortlessly set up your Go projects. Follow the s"
  },
  {
    "path": "docs/mkdocs.yml",
    "chars": 2252,
    "preview": "### Site metadata ###\n\nsite_name: Go-Blueprint Docs\nsite_description: Official documentation for Go-Blueprint project\nsi"
  },
  {
    "path": "docs/requirements.txt",
    "chars": 37,
    "preview": "mkdocs==1.5.3\nmkdocs-material==9.5.15"
  },
  {
    "path": "go.mod",
    "chars": 1125,
    "preview": "module github.com/melkeydev/go-blueprint\n\ngo 1.23.0\n\nrequire (\n\tgithub.com/charmbracelet/bubbles v0.16.1\n\tgithub.com/cha"
  },
  {
    "path": "go.sum",
    "chars": 4623,
    "preview": "github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=\ngithub.com/atotto/clipboard v0.1.4/go"
  },
  {
    "path": "main.go",
    "chars": 93,
    "preview": "package main\n\nimport \"github.com/melkeydev/go-blueprint/cmd\"\n\nfunc main() {\n\tcmd.Execute()\n}\n"
  },
  {
    "path": "scripts/completions.sh",
    "chars": 153,
    "preview": "#!/bin/sh\nset -e\nrm -rf completions\nmkdir completions\nfor sh in bash zsh fish; do\n  go run main.go completion \"$sh\" >\"co"
  },
  {
    "path": "scripts/create-npm-packages.sh",
    "chars": 7308,
    "preview": "#!/bin/bash\n\nset -euo pipefail\n\nVERSION=\"$1\"\nPACKAGE_NAME=\"@melkeydev/go-blueprint\"\nMAIN_PACKAGE_DIR=\"npm-package\"\nPLATF"
  }
]

About this extraction

This page contains the full source code of the Melkeydev/go-blueprint GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 163 files (350.9 KB), approximately 103.3k tokens, and a symbol index with 249 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!