Releases: flvmnt/pgfence
v0.4.1: Trust Contract Hardening
Trust Contract
- 18 silent failure paths eliminated in Knex (6) and Sequelize (12) transpilers: every early return that silently dropped migration statements now emits an
ExtractionWarning - Plugin crashes surfaced in structured output (JSON, GitHub, SARIF), not just stderr
- Coverage % fixed: new
unanalyzableflag onExtractionWarningdistinguishes truly unanalyzable statements from informational warnings
Bug Fixes
- Trace mode DB connection leak: close clients in
finallyblock - Policy ignore bleed:
fileIgnoredRulesrestricted to first statement only lock-timeout-after-dangerous-statementnow suppressible via inline ignore- Stale
adjustedRiskcleared on trace-merge mismatch override - NaN guard on
--max-lock-timeout/--max-statement-timeout - Stats file and package.json errors include file path and message
LSP
- Format auto-detection failure emits warning instead of silent fallback
- Analysis crash clears stale diagnostics
- Config fetch logs non-capability errors
Docs
- 11 stale comments fixed, verified against PostgreSQL documentation
Tests
- 393 tests (was 371)
- 10 SARIF reporter tests (was zero)
- DROP SCHEMA, DROP SCHEMA CASCADE, DROP CONSTRAINT tests
- 6
adjustRiskboundary tests (exact thresholds) - REFRESH MATERIALIZED VIEW WITH NO DATA test
- 2 coverage calculation accuracy tests
Full changelog: https://github.com/flvmnt/pgfence/blob/main/CHANGELOG.md
v0.4.0: Trace Mode
Trace Mode
Run your migrations against a real Postgres instance and verify every lock prediction:
pgfence trace migrations/*.sqlSpins up a disposable Docker container, executes each statement, queries pg_locks + system catalog, and compares what actually happened against static analysis predictions. Every check gets a verification status: confirmed, mismatch, trace-only, static-only, or error.
Highlights
- Observer polling for CONCURRENTLY: opens a second connection that polls
pg_lockswhile the main connection runs the statement, capturing transient locks that other tools skip - Catalog diffing: detects table rewrites (relfilenode changes), column modifications, constraint validation state, and index creation
- Custom images:
--docker-image postgis/postgis:17for extensions - Version targeting:
--pg-version 14to test against your production version - CI mode:
--cifails on mismatches, execution errors, or risk threshold violations
Security
- Credential sanitization covers both
postgres://andpostgresql://URL schemes - Plugin loader rejects paths outside the project directory
- Container: random password,
127.0.0.1only, cleaned up on crash
Trust Contract Fixes
- trace-only findings: risk derived from observed lock mode (was hard-coded LOW)
- mismatch findings: risk upgraded to max(static, traced) when trace is stronger
- CI now fails on execution errors, not just mismatches
Rules
- Default minimum PostgreSQL version bumped from 11 to 14
rename-columnsuppresses expand/contract recipe on PG14+ (instant)- Removed stale PG10/PG12 references from safe rewrites
Stats
- 371 tests (was 176)
- 3,400+ lines added across 32 files
Full changelog: https://github.com/flvmnt/pgfence/blob/main/CHANGELOG.md
v0.3.2
What's Changed
Bug Fixes
- Coverage formula: reports 0% when parse errors prevent analysis (was falsely reporting 100%, Trust Contract violation)
- Safe rewrite recipes: SET NOT NULL migration boundary comments (PG12+), ADD COLUMN NOT NULL + volatile default appends constraint steps, smallserial maps to smallint
- LSP server: wrap
workspace/configurationin try/catch (prevents crash on Neovim/Helix), code action safe rewrite trailing newline - Policy checks: REINDEX TABLE no longer classified as ACCESS EXCLUSIVE (takes SHARE lock on table), DROP TRIGGER now tracked for wide-lock-window detection, pgfence-ignore comments suppress file-level policy violations
- RENAME TABLE recipe: corrected view note (simple views ARE auto-updatable in PostgreSQL)
Security
- FK action allowlist: knex/sequelize transpilers now validate onDelete/onUpdate values against an allowlist (CASCADE, RESTRICT, NO ACTION, SET NULL, SET DEFAULT), preventing crafted migration files from injecting extra statements into the analysis pipeline
- Sequelize.literal(): no longer interpolates user content into synthetic SQL
Cleanup
pgmoved to optionalDependencies (only needed for--db-url)- Removed dead code (
src/git-diff.ts) - Deduplicated
RISK_ORDER(LSP imports from analyzer instead of redeclaring) --formathelp text now lists drizzle and sequelize
v0.3.1
Bug fixes
- CREATE INDEX safe rewrite: Quick fix no longer uses
(...)placeholder for columns. Now derives the safe SQL from the original statement, preserving columns, WHERE clause, UNIQUE keyword, and USING clause. - LSP initializationOptions: Server now reads config from
initializationOptionsat startup instead of using defaults until the firstworkspace/didChangeConfigurationevent.
Docs
- CLAUDE.md: added LSP server and VS Code extension architecture section.
- Removed em dashes from CLAUDE.md.
v0.3.0
VS Code Extension
pgfence now runs inside your editor. Install from the VS Code Marketplace or search "pgfence" in the Extensions panel.
- Inline diagnostics: lock modes, risk levels, and policy violations as you type
- Quick fixes: one-click safe rewrite replacements (lightbulb menu)
- Hover info: lock mode, blocked operations, and safe alternatives
- Status bar: error/warning count for the current file
The extension is a thin client that discovers @flvmnt/pgfence from your project's node_modules. One source of truth for CLI and editor.
LSP Server
New pgfence lsp command starts the Language Server Protocol server over stdio. The VS Code extension uses this, but any LSP-compatible editor can too.
New Rules
| Rule | Risk | Description |
|---|---|---|
ban-char-field |
LOW | Flag char(n)/character(n), prefer text |
prefer-identity |
LOW | Flag serial/bigserial, prefer IDENTITY (PG10+) |
drop-database |
CRITICAL | DROP DATABASE is irreversible |
ban-alter-domain-add-constraint |
HIGH | Validates against all tables using the domain |
ban-create-domain-with-constraint |
LOW | Domains with constraints have poor migration support |
Parser Improvements
- Character-level source offsets (
startOffset/endOffset) with correct UTF-8 byte-to-char conversion - Policy violations now carry
statementIndexfor precise source mapping
Stats
- 42 check patterns (up from 37)
- 306 tests (up from 286)
- 54 items across 4 risk levels on the homepage
Install
npm install -D @flvmnt/pgfence@0.3.0
code --install-extension flvmnt.pgfenceFull Changelog: v0.2.4...v0.3.0
v0.2.4
What's new
- fix: Regenerate lockfile after optionalDependencies change (CI fix)
- fix: GitHub PR reporter URL and action.yml format list
- fix: Extractor silent failures in Knex and Sequelize transpilers
- fix: Remove em dashes from all user-visible messages
- fix: Correct DROP INDEX CONCURRENTLY and lock_timeout=0 in policy checks
- fix: Correct lock modes for FK, ATTACH PARTITION, and REINDEX
- chore: Move typescript-estree to optionalDependencies
v0.2.3
Open Source (MIT License)
pgfence is now fully open source under the MIT license, replacing the previous FSL-1.1-MIT.
Changes since v0.2.2
- License changed from FSL-1.1-MIT to MIT
- Updated all documentation and badges to reflect MIT license
- Added pgfence.com domain rule to project guidelines
This release includes all the ORM extractor improvements from v0.2.2:
- Knex: .alter() modifier, setNullable/dropNullable, references/inTable chain, timestamps, identifier quoting
- Sequelize: up-only filtering, addConstraint/removeConstraint, addIndex options (concurrently, unique), literal() detection
- TypeORM: builder API detection with 40+ methods, auto-commit detection
- 167 tests, all passing
v0.2.2
ORM Extractor Overhaul
Comprehensive audit and fix of all 6 ORM extractors (TypeORM, Knex, Sequelize, Prisma, Drizzle, raw SQL), cross-referenced against official ORM documentation.
Knex
- Fix
references().inTable()chain producing invalid SQL - Fix unescaped single quotes in DEFAULT values
- Add
createTableIfNotExists,dropTableIfExists,table()alias support - Add
timestamps()column generation (created_at, updated_at) - Add
.alter()modifier: emitsALTER COLUMN TYPEinstead ofADD COLUMN - Add
setNullable()/dropNullable()support - Add
dropColumns()plural form - Double-quote all identifiers to handle reserved words
Sequelize
- Filter to
up()method only (was incorrectly analyzingdown()) - Add
addConstraint/removeConstraintsupport (UNIQUE, FK, CHECK) - Add
onDelete/onUpdatesupport on constraints and column-level references - Parse
addIndexoptions:concurrently,unique,name - Detect
Sequelize.literal()as volatile default - Fix unescaped single quotes in DEFAULT values
- Thread
filePaththrough all transpile functions - Double-quote all identifiers
TypeORM
- Detect and warn on builder API usage (createTable, addColumn, etc.)
- Expand from 13 to 40+ builder methods (dropTable, clearTable, check/exclusion constraints, views, schemas, all plural variants)
- Fix
addUniqueConstrainttocreateUniqueConstraint(matching actual API)
Testing
- 167 tests (up from 153), all passing
- 14 new fixture files covering every fix
v0.2.1
Security
- Fix shell injection in GitHub Action — all inputs now passed through env vars and bash array, eliminating injection vectors and fixing paths with spaces
Bug Fixes
- Correct lock mode for REFRESH MATERIALIZED VIEW CONCURRENTLY — was EXCLUSIVE, now correctly SHARE UPDATE EXCLUSIVE per PostgreSQL docs (allows reads AND writes)
- Eliminate silent failures in plugin system — plugin errors logged to stderr instead of silently swallowed
- Eliminate silent failures in extractors — TypeORM now matches
queryRunner.manager.query(), Knex/Sequelize transpilers emit warnings for dynamic table names instead of returning empty results - Add missing rule coverage — default case for unknown constraint types, DROP SCHEMA CASCADE detection, DropStmt/TruncateStmt/RenameStmt added to lock tracking
- Per-file parse error handling — a syntax error in one file no longer aborts analysis of the entire batch
- maxRisk includes policy violations — error-level policy violations now reflected in maxRisk (was showing SAFE with error-level violations)
- SARIF coverage summary — Trust Contract compliance: SARIF output now includes coverage stats
- Error handling improvements — start-after only catches ENOENT, cloud hooks always log errors, stats file schema validated, config JSON validated
Chores
- Exclude
tests/cloud/from vitest config - Add
@iarna/tomlas optional dependency - Fix duplicate drizzle detection condition
- Flag
CreateProcedureStmtas unanalyzable
v0.2.0
What's new
5 new DDL rules
ALTER TYPE ... ADD VALUE(enum evolution)ATTACH/DETACH PARTITIONREFRESH MATERIALIZED VIEWREINDEX TABLE/INDEX/SCHEMA/DATABASECREATE/DROP/ENABLE/DISABLE TRIGGER
Expanded policy checks
- Lock timeout ordering validation (must come before DDL)
- Wide lock window detection
- Savepoint/rollback awareness
- Timeout-too-permissive warnings
ORM extractor improvements
- Knex: builder chain extraction (
table.string(),table.integer(), etc.) - Knex: conditional migration support
- TypeORM: conditional
queryRunner.query()extraction - Sequelize:
queryInterfacemethod extraction
New CLI flags
--pg-version— tune analysis for your Postgres version (11–17)--stats-file— provide table stats JSON for size-aware risk scoring
Other
- Plugin system for custom rules
- Schema snapshot support
- Transaction state tracking
- 48 new tests (153 total)
Full Changelog: v0.1.5...v0.2.0