diff --git a/.c8rc.json b/.c8rc.json new file mode 100644 index 0000000..0d1962b --- /dev/null +++ b/.c8rc.json @@ -0,0 +1,20 @@ +{ + "check-coverage": true, + "lines": 85, + "branches": 75, + "statements": 85, + "functions": 80, + "include": [ + "src/services/handlers/**/*.ts", + "src/providers/kernel/SqlParser.ts", + "src/lib/**/*.ts", + "src/utils/**/*.ts" + ], + "exclude": [ + "src/test/**", + "node_modules/**", + "**/*.d.ts" + ], + "reporter": ["text", "text-summary", "lcov"], + "all": true +} diff --git a/.c8rc.phase-handlers.json b/.c8rc.phase-handlers.json new file mode 100644 index 0000000..f2e894f --- /dev/null +++ b/.c8rc.phase-handlers.json @@ -0,0 +1,17 @@ +{ + "check-coverage": true, + "lines": 85, + "branches": 75, + "statements": 85, + "functions": 80, + "include": [ + "src/services/handlers/**/*.ts" + ], + "exclude": [ + "src/test/**", + "node_modules/**", + "**/*.d.ts" + ], + "reporter": ["text", "text-summary", "lcov"], + "all": true +} diff --git a/.c8rc.phase-utils.json b/.c8rc.phase-utils.json new file mode 100644 index 0000000..7ce8bc1 --- /dev/null +++ b/.c8rc.phase-utils.json @@ -0,0 +1,17 @@ +{ + "check-coverage": true, + "lines": 85, + "branches": 75, + "statements": 85, + "functions": 80, + "include": [ + "src/utils/**/*.ts" + ], + "exclude": [ + "src/test/**", + "node_modules/**", + "**/*.d.ts" + ], + "reporter": ["text", "text-summary", "lcov"], + "all": true +} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..b00abbf --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,101 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL Advanced" + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + schedule: + - cron: '18 5 * * 6' + +jobs: + analyze: + name: Analyze (${{ matrix.language }}) + # Runner size impacts CodeQL analysis time. To learn more, please see: + # - https://gh.io/recommended-hardware-resources-for-running-codeql + # - https://gh.io/supported-runners-and-hardware-resources + # - https://gh.io/using-larger-runners (GitHub.com only) + # Consider using larger runners or machines with greater resources for possible analysis time improvements. + runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + permissions: + # required for all workflows + security-events: write + + # required to fetch internal or private CodeQL packs + packages: read + + # only required for workflows in private repositories + actions: read + contents: read + + strategy: + fail-fast: false + matrix: + include: + - language: actions + build-mode: none + - language: javascript-typescript + build-mode: none + # CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'rust', 'swift' + # Use `c-cpp` to analyze code written in C, C++ or both + # Use 'java-kotlin' to analyze code written in Java, Kotlin or both + # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both + # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, + # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. + # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how + # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Add any setup steps before running the `github/codeql-action/init` action. + # This includes steps like installing compilers or runtimes (`actions/setup-node` + # or others). This is typically only required for manual builds. + # - name: Setup runtime (example) + # uses: actions/setup-example@v1 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v4 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + # If the analyze step fails for one of the languages you are analyzing with + # "We were unable to automatically build your code", modify the matrix above + # to set the build mode to "manual" for that language. Then modify this step + # to build your code. + # ℹ️ Command-line programs to run using the OS shell. + # πŸ“š See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + - name: Run manual build steps + if: matrix.build-mode == 'manual' + shell: bash + run: | + echo 'If you are using a "manual" build mode for one or more of the' \ + 'languages you are analyzing, replace this with the commands to build' \ + 'your code, for example:' + echo ' make bootstrap' + echo ' make release' + exit 1 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v4 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/fortify.yml b/.github/workflows/fortify.yml new file mode 100644 index 0000000..798b224 --- /dev/null +++ b/.github/workflows/fortify.yml @@ -0,0 +1,129 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +################################################################################################################################################ +# Fortify Application Security provides your team with solutions to empower DevSecOps practices, enable cloud transformation, and secure your # +# software supply chain. To learn more about Fortify, start a free trial or contact our sales team, visit fortify.com. # +# # +# Use this starter workflow as a basis for integrating Fortify Application Security Testing into your GitHub workflows. This template # +# demonstrates the steps to package the code+dependencies, initiate a scan, and optionally import SAST vulnerabilities into GitHub Security # +# Code Scanning Alerts. Additional information is available in the workflow comments and the Fortify AST Action / fcli / Fortify product # +# documentation. If you need additional assistance, please contact Fortify support. # +################################################################################################################################################ + +name: Fortify AST Scan + +# Customize trigger events based on your DevSecOps process and/or policy +on: + push: + branches: [ "main" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "main" ] + schedule: + - cron: '44 22 * * 1' + workflow_dispatch: + +jobs: + Fortify-AST-Scan: + # Use the appropriate runner for building your source code. Ensure dev tools required to build your code are present and configured appropriately (MSBuild, Python, etc). + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + # pull-requests: write # Required if DO_PR_COMMENT is set to true + + steps: + # Check out source code + - name: Check Out Source Code + uses: actions/checkout@v4 + + # Perform SAST and/or SCA scan via Fortify on Demand/Fortify Hosted/ScanCentral SAST/Debricked. Based on + # configuration, the Fortify GitHub Action can optionally set up the application version/release, generate + # job summaries and Pull Request comments, and/or export SAST results to the GitHub code scanning dashboard. + # The Fortify GitHub Action provides many customization capabilities, but in case further customization is + # required, you can use sub-actions like fortify/github-action/setup@v1 to set up the various Fortify tools + # and run them directly from within your pipeline. It is recommended to review the Fortify GitHub Action + # documentation at https://github.com/fortify/github-action#readme for more information on the various + # configuration options and available sub-actions. + - name: Run Fortify Scan + # Specify Fortify GitHub Action version to run. As per GitHub starter workflow requirements, this example + # uses the commit id corresponding to version 1.6.2. It is recommended to check whether any later releases + # are available at https://github.com/fortify/github-action/releases. Depending on the amount of stability + # required, you may want to consider using fortify/github-action@v1 instead to use the latest 1.x.y version + # of this action, allowing your workflows to automatically benefit from any new features and bug fixes. + uses: fortify/github-action@ef5539bf4bd9c45c0bd971978f635a69eae55297 + with: + sast-scan: true # Run a SAST scan; if not specified or set to false, no SAST scan will be run + debricked-sca-scan: true # For FoD, run an open-source scan as part of the SAST scan (ignored if SAST scan + # is disabled). For SSC, run a Debricked scan and import results into SSC. + env: + ############################################################# + ##### Fortify on Demand configuration + ##### Remove this section if you're integrating with Fortify Hosted/Software Security Center (see below) + ### Required configuration + FOD_URL: https://ams.fortify.com # Must be hardcoded or configured through GitHub variable, not secret + FOD_TENANT: ${{secrets.FOD_TENANT}} # Either tenant/user/password or client id/secret are required; + FOD_USER: ${{secrets.FOD_USER}} # these should be configured through GitHub secrets. + FOD_PASSWORD: ${{secrets.FOD_PAT}} + # FOD_CLIENT_ID: ${{secrets.FOD_CLIENT_ID}} + # FOD_CLIENT_SECRET: ${{secrets.FOD_CLIENT_SECRET}} + ### Optional configuration + # FOD_LOGIN_EXTRA_OPTS: --socket-timeout=60s # Extra 'fcli fod session login' options + # FOD_RELEASE: MyApp:MyRelease # FoD release name, default: /: + # DO_SETUP: true # Setup FoD application, release & static scan configuration + # SETUP_ACTION: # Customize setup action + # Pass extra options to setup action: + # SETUP_EXTRA_OPTS: --copy-from "${{ github.repository }}:${{ github.event.repository.default_branch }}" + # PACKAGE_EXTRA_OPTS: -oss -bt mvn # Extra 'scancentral package' options + # FOD_SAST_SCAN_EXTRA_OPTS: # Extra 'fcli fod sast-scan start' options + # DO_WAIT: true # Wait for successful scan completion (implied if post-scan actions enabled) + # DO_POLICY_CHECK: true # Fail pipeline if security policy outcome is FAIL + # POLICY_CHECK_ACTION: # Customize security policy checks + # POLICY_CHECK_EXTRA_OPTS: --on-unsigned=ignore # Pass extra options to policy check action + # DO_JOB_SUMMARY: true # Generate workflow job summary + # JOB_SUMMARY_ACTION: # Customize job summary + # JOB_SUMMARY_EXTRA_OPTS: --on-unsigned=ignore # Pass extra options to job summary action + # DO_PR_COMMENT: true # Generate PR comments, only used on pull_request triggers + # PR_COMMENT_ACTION: # Customize PR comments + # PR_COMMENT_EXTRA_OPTS: --on-unsigned=ignore # Pass extra options to PR comment action + # DO_EXPORT: true # Export vulnerability data to GitHub code scanning dashboard + # EXPORT_ACTION: # Customize export action + # EXPORT_EXTRA_OPTS: --on-unsigned=ignore # Pass extra options to export action + # TOOL_DEFINITIONS: # URL from where to retrieve Fortify tool definitions + + ############################################################# + ##### Fortify Hosted / Software Security Center & ScanCentral + ##### Remove this section if you're integrating with Fortify on Demand (see above) + ### Required configuration + SSC_URL: ${{vars.SSC_URL}} # Must be hardcoded or configured through GitHub variable, not secret + SSC_TOKEN: ${{secrets.SSC_TOKEN}} # SSC CIToken; credentials should be configured through GitHub secrets + SC_SAST_TOKEN: ${{secrets.SC_CLIENT_AUTH_TOKEN}} # ScanCentral SAST client_auth_token, required if SAST scan is enabled + DEBRICKED_TOKEN: ${{secrets.DEBRICKED_TOKEN}} # Debricked token, required if Debricked scan is enabled + SC_SAST_SENSOR_VERSION: 24.4.0 # Sensor version to use for the scan, required if SAST scan is enabled + ### Optional configuration + # SSC_LOGIN_EXTRA_OPTS: --socket-timeout=60s # Extra 'fcli ssc session login' options + # SC_SAST_LOGIN_EXTRA_OPTS: --socket-timeout=60s # Extra 'fcli sc-sast session login' options + # SSC_APPVERSION: MyApp:MyVersion # SSC application version name, default: /: + # DO_SETUP: true # Set up SSC application & version + # SETUP_ACTION: # Customize setup action + # SETUP_EXTRA_OPTS: --on-unsigned=ignore # Pass extra options to setup action + # PACKAGE_EXTRA_OPTS: -bt mvn # Extra 'scancentral package' options + # EXTRA_SC_SAST_SCAN_OPTS: # Extra 'fcli sc-sast scan start' options + # DO_WAIT: true # Wait for successful scan completion (implied if post-scan actions enabled) + # DO_POLICY_CHECK: true # Fail pipeline if security policy outcome is FAIL + # POLICY_CHECK_ACTION: # Customize security policy checks + # POLICY_CHECK_EXTRA_OPTS: --on-unsigned=ignore # Pass extra options to policy check action + # DO_JOB_SUMMARY: true # Generate workflow job summary + # JOB_SUMMARY_ACTION: # Customize job summary + # JOB_SUMMARY_EXTRA_OPTS: --on-unsigned=ignore # Pass extra options to job summary action + # DO_PR_COMMENT: true # Generate PR comments, only used on pull_request triggers + # PR_COMMENT_ACTION: # Customize PR comments + # PR_COMMENT_EXTRA_OPTS: --on-unsigned=ignore # Pass extra options to PR comment action + # DO_EXPORT: true # Export vulnerability data to GitHub code scanning dashboard + # EXPORT_ACTION: # Customize export action + # EXPORT_EXTRA_OPTS: --on-unsigned=ignore # Pass extra options to export action + # TOOL_DEFINITIONS: # URL from where to retrieve Fortify tool definitions diff --git a/.github/workflows/summary.yml b/.github/workflows/summary.yml index 9b07bb8..9336692 100644 --- a/.github/workflows/summary.yml +++ b/.github/workflows/summary.yml @@ -27,7 +27,7 @@ jobs: - name: Comment with AI summary run: | - gh issue comment $ISSUE_NUMBER --body '${{ steps.inference.outputs.response }}' + gh issue comment "$ISSUE_NUMBER" --body "$RESPONSE" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} ISSUE_NUMBER: ${{ github.event.issue.number }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 27e4fb5..d521c5b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,5 +1,8 @@ name: Test PostgreSQL Integration +permissions: + contents: read + on: push: branches: [main, develop] @@ -32,7 +35,7 @@ jobs: run: npm run test - name: Generate coverage report - run: npm run coverage + run: npm run coverage:phased - name: Upload coverage to Codecov uses: codecov/codecov-action@v3 diff --git a/.gitignore b/.gitignore index 00e229f..7883255 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ node_modules out +src/**/*.js +src/**/*.js.map yarn.lock pat @@ -8,6 +10,7 @@ dist pat-open-vsx .nyc_output/ coverage/ +test-results/ out_test/ test_* mock_ai_server.js diff --git a/.nycrc.json b/.nycrc.json index 83af6c8..a257a1a 100644 --- a/.nycrc.json +++ b/.nycrc.json @@ -1,7 +1,10 @@ { "all": true, "include": [ - "src/**/*.ts" + "src/services/handlers/**/*.ts", + "src/providers/kernel/SqlParser.ts", + "src/lib/**/*.ts", + "src/utils/**/*.ts" ], "exclude": [ "src/**/*.test.ts", @@ -19,11 +22,11 @@ ], "report-dir": "./coverage", "temp-dir": "./.nyc_output", - "check-coverage": false, - "lines": 50, - "statements": 50, - "functions": 50, - "branches": 50, + "check-coverage": true, + "lines": 85, + "statements": 85, + "functions": 80, + "branches": 75, "per-file": false, "cache": true, "produce-source-map": true, @@ -33,5 +36,11 @@ ], "extension": [ ".ts" - ] + ], + "watermarks": { + "lines": [75, 85], + "functions": [70, 80], + "branches": [65, 75], + "statements": [75, 85] + } } diff --git a/CHANGELOG.md b/CHANGELOG.md index eacf7bb..f21347d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 --- +## [0.8.8] - 2026-03-21 + +### Added +- **Command palette β€” release notes**: **PgStudio: Show Release Notes / What's New** is registered in the manifest for discovery (changelog opens in an editor-area webview panel). +- **Tests**: Integration coverage for notebook renderer message flow (`NotebookRendererFlow.test.ts`) and unit tests for query save/delete handlers (`QueryHandlers.test.ts`). +- **Table Designer (create mode)**: Drag-and-drop column reorder with clear create-vs-edit UI behavior. +- **AI chat**: Explicit **production safety** rules in the system prompt (read-first bias, transaction/rollback guidance, guarded writes). + +### Changed +- **Sidebar layout**: **Connections** and **SQL Assistant** are listed first; **Saved Queries** and **Query History** use new view identifiers and start **collapsed by default** (VS Code only applies manifest defaults when it has no prior UI state for that view). +- **Release notes**: **What's New** is no longer a sidebar section; use the command palette command. Automatic release-note panels on upgrade are not shown on activation. +- **Notebook inline edits**: `SaveChangesHandler` and bulk/table deletes use **parameterized** `UPDATE`/`DELETE`, run inside a **transaction** (`BEGIN` / `COMMIT` / `ROLLBACK` on failure), and build predicates with proper identifier quoting (fixes composite-key and NULL edge cases). + +--- + ## [0.8.6...0.8.7] - 2026-03-15 ### Added diff --git a/Makefile b/Makefile index ef274e4..61adf4c 100644 --- a/Makefile +++ b/Makefile @@ -105,6 +105,11 @@ docker-clean: docker-compose -f docker-compose.test.yml down -v @echo "Test containers and volumes removed" +# update npm dependencies +npm-update: + $(NPM_BIN) update + @echo "npm dependencies updated" + # Full test suite test-full: docker-up test-all coverage docker-down @echo "Full test suite completed" diff --git a/README.md b/README.md index f26b581..9228735 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ [![Version](https://img.shields.io/visual-studio-marketplace/v/ric-v.postgres-explorer?style=for-the-badge&logo=visual-studio-code&logoColor=white&color=0066CC)](https://marketplace.visualstudio.com/items?itemName=ric-v.postgres-explorer) [![Downloads](https://img.shields.io/visual-studio-marketplace/d/ric-v.postgres-explorer?style=for-the-badge&logo=visual-studio-code&logoColor=white&color=2ECC71)](https://marketplace.visualstudio.com/items?itemName=ric-v.postgres-explorer) [![Rating](https://img.shields.io/visual-studio-marketplace/r/ric-v.postgres-explorer?style=for-the-badge&logo=visual-studio-code&logoColor=white&color=F39C12)](https://marketplace.visualstudio.com/items?itemName=ric-v.postgres-explorer) -[![Status](https://img.shields.io/badge/status-beta-blue?style=for-the-badge&logo=git&logoColor=white)](https://github.com/dev-asterix/yape) +[![Status](https://img.shields.io/badge/status-beta-blue?style=for-the-badge&logo=git&logoColor=white)](https://github.com/dev-asterix/PgStudio) **PgStudio** (formerly YAPE) is a comprehensive PostgreSQL database management extension featuring interactive SQL notebooks, real-time monitoring dashboard, AI-powered assistance, and advanced database operationsβ€”all within VS Code. @@ -119,12 +119,25 @@ code --install-extension ric-v.postgres-explorer Then: **PostgreSQL icon** β†’ **Add Connection** β†’ Enter details β†’ **Connect!** +--- + +## πŸ“š Documentation Map + +- `README.md` - Product overview, installation, development, and troubleshooting +- `docs/ARCHITECTURE.md` - System architecture and component/data-flow details +- `docs/STYLING_GUIDE.md` - Centralized styling/templates and UI refactoring patterns +- `docs/ROADMAP.md` - Active pipeline and upcoming phases +- `SECURITY.md` - Security policy and vulnerability reporting guidance +- `CHANGELOG.md` - Release notes and what changed across versions + +**v0.8.8 (latest) β€”** Sidebar puts **Connections** and **SQL Assistant** first; **Saved Queries** and **Query History** start collapsed for fresh view state; **What’s New** is command-palette only. Notebook inline edits use **parameterized SQL inside transactions**. **Table Designer** adds **create-mode column reorder** and improved **SQL preview** styling. Details: `CHANGELOG.md`. + --- ## πŸ—οΈ Project Structure ``` -yape/ +PgStudio/ β”œβ”€β”€ src/ β”‚ β”œβ”€β”€ extension.ts # Extension entry point β”‚ β”œβ”€β”€ commands/ # Command implementations @@ -243,8 +256,8 @@ Turn any query result into beautiful, interactive charts in seconds. ```bash # Clone the repository -git clone https://github.com/dev-asterix/yape.git -cd yape +git clone https://github.com/dev-asterix/PgStudio.git +cd PgStudio # Install dependencies npm install @@ -349,14 +362,14 @@ PgStudio includes comprehensive testing infrastructure: - **Docker Containers**: PostgreSQL 12, 14, 15, 16, 17 for compatibility testing - **CI/CD Pipeline**: GitHub Actions with Matrix testing (Node 18-22, PostgreSQL 12-17) -πŸ“– **Full documentation**: See [TESTING.md](TESTING.md) and [TESTING_QUICKSTART.md](TESTING_QUICKSTART.md) +πŸ“– **Testing docs**: Use the scripts listed above and CI workflow in `.github/workflows/test.yml`. --- ## 🀝 Contributing -- πŸ› [Report Bugs](https://github.com/dev-asterix/yape/issues/new?template=bug_report.md) -- πŸ’‘ [Request Features](https://github.com/dev-asterix/yape/issues/new?template=feature_request.md) +- πŸ› [Report Bugs](https://github.com/dev-asterix/PgStudio/issues/new?template=bug_report.md) +- πŸ’‘ [Request Features](https://github.com/dev-asterix/PgStudio/issues/new?template=feature_request.md) - πŸ”§ Fork β†’ Branch β†’ PR - πŸ§ͺ Ensure all tests pass: `npm run test:all && npm run coverage` @@ -410,7 +423,7 @@ Also on [Open VSX](https://open-vsx.org/extension/ric-v/postgres-explorer) --- -## ???? Troubleshooting +## πŸ”§ Troubleshooting ### Connection Issues diff --git a/SECURITY.md b/SECURITY.md index f0b8fe3..deb1d26 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -6,12 +6,12 @@ We actively support security updates for the latest major release. | Version | Supported | | ------- | ------------------ | -| 0.5.x | :white_check_mark: | -| < 0.5.0 | :x: | +| 0.8.x | :white_check_mark: | +| < 0.8.0 | :x: | ## Reporting a Vulnerability -If you discover a security vulnerability in this extension (e.g., improper credential storage, SQL injection risks within the helper, or data leakage), please report it through github issues. +If you discover a security vulnerability in this extension (for example, credential storage issues, SQL injection risks, or data leakage), please report it via GitHub issues and clearly mark it as a security report. ### What to Include * Description of the vulnerability. diff --git a/analyze-coverage.js b/analyze-coverage.js new file mode 100644 index 0000000..239a3e7 --- /dev/null +++ b/analyze-coverage.js @@ -0,0 +1,89 @@ +const fs = require('fs'); +const path = require('path'); + +const coverageFile = path.join(__dirname, 'coverage/coverage-final.json'); +const c8rc = JSON.parse(fs.readFileSync(path.join(__dirname, '.c8rc.json'), 'utf8')); + +const coverage = JSON.parse(fs.readFileSync(coverageFile, 'utf8')); + +const files = Object.keys(coverage); + +let totalStatements = 0; +let coveredStatements = 0; +let totalBranches = 0; +let coveredBranches = 0; +let totalFunctions = 0; +let coveredFunctions = 0; + +const perFileStats = []; + +files.forEach(file => { + const data = coverage[file]; + // statements + const s = data.s; + let fileStatements = 0; + let fileCoveredStatements = 0; + Object.keys(s).forEach(key => { + fileStatements++; + totalStatements++; + if (s[key] > 0) { + fileCoveredStatements++; + coveredStatements++; + } + }); + // branches + const b = data.b; + let fileBranches = 0; + let fileCoveredBranches = 0; + Object.keys(b).forEach(key => { + const counts = b[key]; + counts.forEach(count => { + fileBranches++; + totalBranches++; + if (count > 0) { + fileCoveredBranches++; + coveredBranches++; + } + }); + }); + // functions + const f = data.f; + let fileFunctions = 0; + let fileCoveredFunctions = 0; + Object.keys(f).forEach(key => { + fileFunctions++; + totalFunctions++; + if (f[key] > 0) { + fileCoveredFunctions++; + coveredFunctions++; + } + }); + // lines: approximate using statements (c8 uses statement map for lines) + // We'll compute line coverage as statements coverage per file + perFileStats.push({ + file: path.relative(__dirname, file), + statements: fileCoveredStatements / fileStatements * 100, + branches: fileBranches ? fileCoveredBranches / fileBranches * 100 : 100, + functions: fileFunctions ? fileCoveredFunctions / fileFunctions * 100 : 100, + }); +}); + +console.log('=== Coverage Summary ==='); +console.log(`Total files: ${files.length}`); +console.log(''); +console.log(`Statements: ${coveredStatements}/${totalStatements} (${(coveredStatements/totalStatements*100).toFixed(2)}%)`); +console.log(`Branches: ${coveredBranches}/${totalBranches} (${(coveredBranches/totalBranches*100).toFixed(2)}%)`); +console.log(`Functions: ${coveredFunctions}/${totalFunctions} (${(coveredFunctions/totalFunctions*100).toFixed(2)}%)`); +console.log(`Lines (approx): ${coveredStatements}/${totalStatements} (${(coveredStatements/totalStatements*100).toFixed(2)}%)`); +console.log(''); +console.log('Thresholds:'); +console.log(`Lines >= 85%: ${(coveredStatements/totalStatements*100).toFixed(2)}% ${coveredStatements/totalStatements*100 >= 85 ? 'βœ“' : 'βœ—'}`); +console.log(`Branches >= 75%: ${(coveredBranches/totalBranches*100).toFixed(2)}% ${coveredBranches/totalBranches*100 >= 75 ? 'βœ“' : 'βœ—'}`); +console.log(`Statements >= 85%: ${(coveredStatements/totalStatements*100).toFixed(2)}% ${coveredStatements/totalStatements*100 >= 85 ? 'βœ“' : 'βœ—'}`); +console.log(`Functions >= 80%: ${(coveredFunctions/totalFunctions*100).toFixed(2)}% ${coveredFunctions/totalFunctions*100 >= 80 ? 'βœ“' : 'βœ—'}`); +console.log(''); +console.log('Per-file coverage (statements):'); +perFileStats.sort((a,b) => a.statements - b.statements); +perFileStats.forEach(stat => { + console.log(`${stat.file.padEnd(60)} ${stat.statements.toFixed(2)}%`); +}); \ No newline at end of file diff --git a/config/.c8rc.json b/config/.c8rc.json new file mode 100644 index 0000000..0d1962b --- /dev/null +++ b/config/.c8rc.json @@ -0,0 +1,20 @@ +{ + "check-coverage": true, + "lines": 85, + "branches": 75, + "statements": 85, + "functions": 80, + "include": [ + "src/services/handlers/**/*.ts", + "src/providers/kernel/SqlParser.ts", + "src/lib/**/*.ts", + "src/utils/**/*.ts" + ], + "exclude": [ + "src/test/**", + "node_modules/**", + "**/*.d.ts" + ], + "reporter": ["text", "text-summary", "lcov"], + "all": true +} diff --git a/config/.c8rc.phase-handlers.json b/config/.c8rc.phase-handlers.json new file mode 100644 index 0000000..f2e894f --- /dev/null +++ b/config/.c8rc.phase-handlers.json @@ -0,0 +1,17 @@ +{ + "check-coverage": true, + "lines": 85, + "branches": 75, + "statements": 85, + "functions": 80, + "include": [ + "src/services/handlers/**/*.ts" + ], + "exclude": [ + "src/test/**", + "node_modules/**", + "**/*.d.ts" + ], + "reporter": ["text", "text-summary", "lcov"], + "all": true +} diff --git a/config/.c8rc.phase-utils.json b/config/.c8rc.phase-utils.json new file mode 100644 index 0000000..7ce8bc1 --- /dev/null +++ b/config/.c8rc.phase-utils.json @@ -0,0 +1,17 @@ +{ + "check-coverage": true, + "lines": 85, + "branches": 75, + "statements": 85, + "functions": 80, + "include": [ + "src/utils/**/*.ts" + ], + "exclude": [ + "src/test/**", + "node_modules/**", + "**/*.d.ts" + ], + "reporter": ["text", "text-summary", "lcov"], + "all": true +} diff --git a/config/.nycrc.json b/config/.nycrc.json new file mode 100644 index 0000000..a257a1a --- /dev/null +++ b/config/.nycrc.json @@ -0,0 +1,46 @@ +{ + "all": true, + "include": [ + "src/services/handlers/**/*.ts", + "src/providers/kernel/SqlParser.ts", + "src/lib/**/*.ts", + "src/utils/**/*.ts" + ], + "exclude": [ + "src/**/*.test.ts", + "src/test/**", + "src/activation/**", + "**/*.d.ts", + "node_modules/**" + ], + "reporter": [ + "text", + "text-summary", + "html", + "lcov", + "json" + ], + "report-dir": "./coverage", + "temp-dir": "./.nyc_output", + "check-coverage": true, + "lines": 85, + "statements": 85, + "functions": 80, + "branches": 75, + "per-file": false, + "cache": true, + "produce-source-map": true, + "instrument": true, + "require": [ + "ts-node/register" + ], + "extension": [ + ".ts" + ], + "watermarks": { + "lines": [75, 85], + "functions": [70, 80], + "branches": [65, 75], + "statements": [75, 85] + } +} diff --git a/debug-coverage.js b/debug-coverage.js new file mode 100644 index 0000000..6da71fa --- /dev/null +++ b/debug-coverage.js @@ -0,0 +1,13 @@ +const fs = require('fs'); +const path = require('path'); + +const coverageFile = path.join(__dirname, 'coverage/coverage-final.json'); +const coverage = JSON.parse(fs.readFileSync(coverageFile, 'utf8')); + +const firstFile = Object.keys(coverage)[0]; +console.log(firstFile); +const data = coverage[firstFile]; +console.log('Statement map keys:', Object.keys(data.s).length); +console.log('Statement counts:', data.s); +console.log('Branch map:', Object.keys(data.b).length); +console.log('Function map:', Object.keys(data.f).length); \ No newline at end of file diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 1106eb5..b67d28f 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -1,6 +1,6 @@ # PgStudio Architecture -> **Last Updated**: December 2025 +> **Last Updated**: March 2026 This document provides a comprehensive overview of PgStudio's architecture, design decisions, and component interactions. @@ -414,6 +414,19 @@ Default pool configuration: - Session clients closed on notebook disposal - Result truncation prevents OOM errors +### Large Operation Optimizations + +- **Adaptive schema cache TTL** in `src/lib/schema-cache.ts` + - Hot entries expire faster, cold entries live longer. + - Balances freshness and query volume under large schemas. +- **Connection pool metrics and idle cleanup** in `src/services/ConnectionManager.ts` + - Tracks pool health and closes idle pools after inactivity. + - Helps prevent pool exhaustion and stale resource usage. +- **Debounced tree refresh** in `src/providers/DatabaseTreeProvider.ts` + - Batches rapid refresh triggers to reduce UI flicker and redundant renders. +- **Large-tree prioritization** + - Prioritizes favorites/recent objects for faster perceived responsiveness on databases with many objects. + --- ## Security diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md deleted file mode 100644 index 4800a13..0000000 --- a/docs/CONTRIBUTING.md +++ /dev/null @@ -1,478 +0,0 @@ -# Contributing to PgStudio - -Thank you for your interest in contributing to PgStudio! This guide will help you get started with development. - ---- - -## Table of Contents - -- [Development Setup](#development-setup) -- [Project Structure](#project-structure) -- [Code Style Guide](#code-style-guide) -- [Testing Guidelines](#testing-guidelines) -- [Pull Request Process](#pull-request-process) -- [Commit Message Format](#commit-message-format) - ---- - -## Development Setup - -### Prerequisites - -- **Node.js** >= 18.x -- **npm** >= 9.x -- **VS Code** >= 1.80.0 -- **PostgreSQL** >= 12.x (for testing) - -### Initial Setup - -```bash -# Clone the repository -git clone https://github.com/dev-asterix/yape.git -cd yape - -# Install dependencies -npm install - -# Compile TypeScript -npm run compile - -# Watch mode for development -npm run watch -``` - -### Running the Extension - -1. Open the project in VS Code -2. Press `F5` to launch Extension Development Host -3. The extension will be loaded in a new VS Code window - -### Project Scripts - -```bash -npm run compile # Compile TypeScript -npm run watch # Watch mode (auto-compile on save) -npm run test # Run unit tests -npm run lint # Run ESLint -npm run esbuild-renderer # Bundle renderer for production -``` - ---- - -## Project Structure - -``` -yape/ -β”œβ”€β”€ src/ -β”‚ β”œβ”€β”€ extension.ts # Extension entry point -β”‚ β”œβ”€β”€ commands/ # Command implementations -β”‚ β”‚ β”œβ”€β”€ connections.ts # Connection management -β”‚ β”‚ β”œβ”€β”€ tables.ts # Table operations -β”‚ β”‚ β”œβ”€β”€ views.ts # View operations -β”‚ β”‚ β”œβ”€β”€ functions.ts # Function operations -β”‚ β”‚ β”œβ”€β”€ fdw.ts # Foreign Data Wrapper ops -β”‚ β”‚ β”œβ”€β”€ ai.ts # AI commands -β”‚ β”‚ └── helper.ts # Shared utilities -β”‚ β”œβ”€β”€ providers/ # VS Code providers -β”‚ β”‚ β”œβ”€β”€ DatabaseTreeProvider.ts -β”‚ β”‚ β”œβ”€β”€ SqlCompletionProvider.ts -β”‚ β”‚ β”œβ”€β”€ NotebookKernel.ts -β”‚ β”‚ └── DashboardPanel.ts -β”‚ β”œβ”€β”€ services/ # Core services -β”‚ β”‚ β”œβ”€β”€ ConnectionManager.ts # Connection pooling -β”‚ β”‚ β”œβ”€β”€ SecretStorageService.ts -β”‚ β”‚ β”œβ”€β”€ SSHService.ts -β”‚ β”‚ β”œβ”€β”€ AIService.ts -β”‚ β”‚ β”œβ”€β”€ HistoryService.ts -β”‚ β”‚ β”œβ”€β”€ ErrorService.ts -β”‚ β”‚ └── DbObjectService.ts -β”‚ β”œβ”€β”€ renderer_v2.ts # Notebook renderer -β”‚ β”œβ”€β”€ renderer/ # Renderer modules -β”‚ β”‚ β”œβ”€β”€ components/ui.ts -β”‚ β”‚ └── features/ -β”‚ β”‚ β”œβ”€β”€ export.ts -β”‚ β”‚ └── ai.ts -β”‚ β”œβ”€β”€ common/ # Shared types -β”‚ β”‚ └── types.ts -β”‚ └── test/ # Tests -β”‚ └── unit/ -β”œβ”€β”€ docs/ # Documentation -β”œβ”€β”€ package.json # Extension manifest -└── tsconfig.json # TypeScript config -``` - ---- - -## Code Style Guide - -### TypeScript Conventions - -#### 1. **Strict Typing** -Always use explicit types. Avoid `any` unless absolutely necessary. - -```typescript -// βœ… Good -async function getTableData( - client: PoolClient, - schema: string, - table: string -): Promise { - return await client.query('SELECT * FROM $1.$2', [schema, table]); -} - -// ❌ Bad -async function getTableData(client: any, schema: any, table: any): Promise { - return await client.query('SELECT * FROM $1.$2', [schema, table]); -} -``` - -#### 2. **Naming Conventions** - -| Type | Convention | Example | -|------|------------|---------| -| Classes | PascalCase | `ConnectionManager` | -| Interfaces | PascalCase with `I` prefix (optional) | `ConnectionConfig` | -| Functions | camelCase | `getPooledClient` | -| Constants | UPPER_SNAKE_CASE | `MAX_ROWS` | -| Private members | camelCase with `_` prefix | `_pools` | - -#### 3. **Async/Await** -Prefer `async/await` over `.then()` chains. - -```typescript -// βœ… Good -async function fetchData() { - try { - const result = await client.query('SELECT ...'); - return result.rows; - } catch (error) { - ErrorService.getInstance().handleError(error); - } -} - -// ❌ Bad -function fetchData() { - return client.query('SELECT ...') - .then(result => result.rows) - .catch(error => ErrorService.getInstance().handleError(error)); -} -``` - ---- - -### Connection Management Best Practices - -#### 1. **Always Use Pooling** - -```typescript -// βœ… Good - Pooled client (auto-released) -const client = await ConnectionManager.getInstance().getPooledClient(config); -try { - await client.query('SELECT ...'); -} finally { - client.release(); // CRITICAL: Always release in finally -} - -// ❌ Bad - Direct client creation -const client = new Client(config); -await client.connect(); -await client.query('SELECT ...'); -await client.end(); // May not execute if error occurs -``` - -#### 2. **Session Clients for Notebooks** - -```typescript -// βœ… Good - Session client for stateful operations -const client = await ConnectionManager.getInstance() - .getSessionClient(config, notebook.uri.toString()); - -// Client persists across cells -// Automatically closed when notebook closes -``` - -#### 3. **Error Handling** - -```typescript -// βœ… Good - Centralized error handling -try { - await client.query('...'); -} catch (error) { - ErrorService.getInstance().handleError(error, { - context: 'Table Operations', - operation: 'INSERT', - table: tableName - }); - throw error; // Re-throw if caller needs to handle -} -``` - ---- - -### SQL Query Patterns - -#### 1. **Parameterized Queries** -Always use parameterized queries to prevent SQL injection. - -```typescript -// βœ… Good -await client.query( - 'SELECT * FROM $1.$2 WHERE id = $3', - [schema, table, id] -); - -// ❌ Bad - SQL injection risk -await client.query( - `SELECT * FROM ${schema}.${table} WHERE id = ${id}` -); -``` - -#### 2. **Identifier Quoting** -Use double quotes for identifiers to handle special characters. - -```typescript -// βœ… Good -const query = `SELECT * FROM "${schema}"."${table}"`; - -// Handles schemas/tables with spaces, uppercase, etc. -``` - ---- - -### Error Handling Patterns - -#### 1. **Service Layer Errors** - -```typescript -export class MyService { - private static instance: MyService; - - public static getInstance(): MyService { - if (!MyService.instance) { - MyService.instance = new MyService(); - } - return MyService.instance; - } - - async performOperation(): Promise { - try { - // Operation logic - } catch (error) { - ErrorService.getInstance().handleError(error, { - context: 'MyService', - operation: 'performOperation' - }); - throw error; - } - } -} -``` - -#### 2. **Command Handler Errors** - -```typescript -export async function myCommandHandler(node: TreeNode) { - try { - const client = await ConnectionManager.getInstance() - .getPooledClient(node.connection); - try { - await client.query('...'); - vscode.window.showInformationMessage('Success!'); - } finally { - client.release(); - } - } catch (error) { - vscode.window.showErrorMessage(`Failed: ${error.message}`); - } -} -``` - ---- - -## Testing Guidelines - -### Unit Tests - -Located in `src/test/unit/`. Use Mocha + Chai. - -```typescript -import { expect } from 'chai'; -import * as sinon from 'sinon'; - -describe('ConnectionManager', () => { - let sandbox: sinon.SinonSandbox; - - beforeEach(() => { - sandbox = sinon.createSandbox(); - }); - - afterEach(() => { - sandbox.restore(); - }); - - it('should create a new pool if one does not exist', async () => { - const manager = ConnectionManager.getInstance(); - const config = { /* ... */ }; - - const poolStub = { - connect: sandbox.stub().resolves({ release: sandbox.stub() }), - on: sandbox.stub(), - end: sandbox.stub().resolves() - }; - - sandbox.stub(require('pg'), 'Pool').returns(poolStub); - - const client = await manager.getPooledClient(config); - - expect(poolStub.connect.calledOnce).to.be.true; - expect(client).to.exist; - }); -}); -``` - -### Integration Tests - -Require a local PostgreSQL instance. Use environment variables for configuration: - -```bash -export PGHOST=localhost -export PGPORT=5432 -export PGUSER=postgres -export PGPASSWORD=postgres -export PGDATABASE=test_db -``` - ---- - -## Pull Request Process - -### 1. **Fork and Branch** - -```bash -# Fork the repository on GitHub -# Clone your fork -git clone https://github.com/YOUR_USERNAME/yape.git - -# Create a feature branch -git checkout -b feature/my-new-feature -``` - -### 2. **Make Changes** - -- Follow the code style guide -- Add tests for new functionality -- Update documentation if needed - -### 3. **Test Your Changes** - -```bash -npm run compile -npm run test -npm run lint -``` - -### 4. **Commit** - -Follow the [commit message format](#commit-message-format). - -### 5. **Push and Create PR** - -```bash -git push origin feature/my-new-feature -``` - -Then create a Pull Request on GitHub with: -- Clear description of changes -- Reference to related issues -- Screenshots/GIFs for UI changes - -### 6. **Code Review** - -- Address reviewer feedback -- Keep commits clean (squash if needed) -- Ensure CI passes - ---- - -## Commit Message Format - -We follow the [Conventional Commits](https://www.conventionalcommits.org/) specification. - -### Format - -``` -(): - - - -