
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
A Zero-Dependency, AST-Based Database Schema Management Tool
Website: schemact.oxog.dev | Documentation: schemact.oxog.dev/docs.html | GitHub: github.com/ersinkoc/schemact Schemact is a revolutionary database migration tool that rejects the complexity of modern ORMs in favor of a native, zero-dependency approach using a custom declarative DSL (Domain Specific Language).
fs, path, crypto, etc.).sigl files with a clean, intuitive syntaxnpm install -g schemact
Or use it in your project:
npm install --save-dev schemact
schemact init
This creates:
migrations/ directory for your schema filesschemact.config.js configuration fileEdit schemact.config.js to set up your database connection:
import pg from 'pg';
const { Pool } = pg;
const pool = new Pool({
host: 'localhost',
port: 5432,
database: 'mydb',
user: 'postgres',
password: 'password',
});
const adapter = {
async connect() {},
async disconnect() {
await pool.end();
},
async query(sql) {
const result = await pool.query(sql);
return result.rows;
},
async transaction(queries) {
const client = await pool.connect();
try {
await client.query('BEGIN');
for (const sql of queries) {
await client.query(sql);
}
await client.query('COMMIT');
} catch (error) {
await client.query('ROLLBACK');
throw error;
} finally {
client.release();
}
},
};
export default {
adapter,
migrationsPath: './migrations',
ledgerPath: './.schemact_ledger.json',
};
schemact create users
This generates a timestamped file like migrations/20240101120000_users.sigl.
# Define user table
model User {
id Serial @pk
email VarChar(255) @unique @notnull
username VarChar(50) @unique @notnull
password VarChar(255) @notnull
role Enum('admin', 'user', 'guest') @default('user')
isActive Boolean @default(true)
createdAt Timestamp @default(now)
}
schemact up
schemact status
Schemact uses .sigl files with a custom syntax designed for clarity and expressiveness.
model TableName {
columnName ColumnType @decorator1 @decorator2
}
Serial, Int, BigInt, SmallIntVarChar(n), Char(n), TextBooleanTimestamp, Date, TimeDecimal(p,s), Numeric(p,s), Real, DoublePrecisionJson, JsonbUuidEnum('value1', 'value2', ...)| Decorator | Description | Example |
|---|---|---|
@pk | Primary key | id Serial @pk |
@unique | Unique constraint | email VarChar(255) @unique |
@notnull | NOT NULL constraint | name Text @notnull |
@default(value) | Default value | active Boolean @default(true) |
@ref(Table.column) | Foreign key | userId Int @ref(User.id) |
@onDelete(action) | Foreign key delete action | @ref(User.id) @onDelete('cascade') |
now - Maps to CURRENT_TIMESTAMPtrue/false - Boolean literals'value'For operations not covered by the DSL, prefix lines with >:
> CREATE INDEX idx_users_email ON "User"("email");
> CREATE VIEW active_users AS SELECT * FROM "User" WHERE "isActive" = true;
# Blog schema
model User {
id Serial @pk
email VarChar(255) @unique @notnull
username VarChar(50) @unique @notnull
createdAt Timestamp @default(now)
}
model Post {
id Serial @pk
title VarChar(200) @notnull
content Text
authorId Int @ref(User.id) @onDelete('cascade')
published Boolean @default(false)
createdAt Timestamp @default(now)
}
# Create index for better query performance
> CREATE INDEX idx_posts_author ON "Post"("authorId");
schemact initInitialize a new Schemact project. Creates:
migrations/ directoryschemact.config.js configuration fileschemact create <name>Create a new migration file with a timestamped filename.
Example:
schemact create add_users_table
# Creates: migrations/20240101120000_add_users_table.sigl
schemact upApply all pending migrations. Migrations are executed in chronological order based on their filename timestamps.
schemact downRollback the last batch of migrations. Automatically generates DROP statements from your schema definitions.
schemact statusShow the current state of migrations:
schemact pull [schema]Introspect an existing database and generate .sigl files. This is the "reverse engineering" feature.
Example:
schemact pull public
# Generates migrations/2024-01-01_introspected.sigl
schemact helpDisplay help information.
schemact versionDisplay version information.
Schemact follows a clean, modular architecture:
.sact file → Lexer → Tokens → Parser → AST → Generator → SQL
src/ast/lexer.ts): Tokenizes input into meaningful chunkssrc/ast/parser.ts): Builds an Abstract Syntax Treesrc/generators/postgres.ts): Converts AST to SQL (both UP and DOWN)The Ledger (src/engine/ledger.ts) tracks applied migrations in .schemact_ledger.json:
The Runner (src/engine/runner.ts) orchestrates the entire migration flow:
The Introspector (src/engine/introspector.ts) reverse-engineers databases:
information_schema tables.sigl filesSchemact is database-agnostic through the adapter pattern. Implement this interface for your database:
interface DbAdapter {
connect(): Promise<void>;
disconnect(): Promise<void>;
query(sql: string): Promise<any[]>;
transaction(queries: string[]): Promise<void>;
}
import pg from 'pg';
const { Pool } = pg;
const pool = new Pool({ /* config */ });
const adapter = {
async connect() {},
async disconnect() {
await pool.end();
},
async query(sql) {
const result = await pool.query(sql);
return result.rows;
},
async transaction(queries) {
const client = await pool.connect();
try {
await client.query('BEGIN');
for (const sql of queries) {
await client.query(sql);
}
await client.query('COMMIT');
} catch (error) {
await client.query('ROLLBACK');
throw error;
} finally {
client.release();
}
},
};
import mysql from 'mysql2/promise';
const pool = mysql.createPool({
host: 'localhost',
user: 'root',
password: 'password',
database: 'mydb',
});
const adapter = {
async connect() {},
async disconnect() {
await pool.end();
},
async query(sql) {
const [rows] = await pool.query(sql);
return rows;
},
async transaction(queries) {
const connection = await pool.getConnection();
try {
await connection.beginTransaction();
for (const sql of queries) {
await connection.query(sql);
}
await connection.commit();
} catch (error) {
await connection.rollback();
throw error;
} finally {
connection.release();
}
},
};
export default {
adapter,
generator: new MySQLGenerator(), // Use MySQLGenerator for MySQL
migrationsPath: './migrations',
ledgerPath: './.schemact_ledger.json',
};
import Database from 'better-sqlite3';
const db = new Database('./mydb.sqlite');
const adapter = {
async connect() {
// Enable foreign keys
db.pragma('foreign_keys = ON');
},
async disconnect() {
db.close();
},
async query(sql) {
// For SELECT queries
if (sql.trim().toUpperCase().startsWith('SELECT')) {
return db.prepare(sql).all();
}
// For other queries
db.prepare(sql).run();
return [];
},
async transaction(queries) {
const transaction = db.transaction(() => {
for (const sql of queries) {
db.prepare(sql).run();
}
});
transaction();
},
};
export default {
adapter,
generator: new SQLiteGenerator(), // Use SQLiteGenerator for SQLite
migrationsPath: './migrations',
ledgerPath: './.schemact_ledger.json',
};
Schemact provides dedicated SQL generators for different database systems, each optimized for the target database's specific syntax and features:
| Feature | PostgreSQL | MySQL | SQLite |
|---|---|---|---|
| Auto-increment | SERIAL | INT AUTO_INCREMENT | INTEGER PRIMARY KEY AUTOINCREMENT |
| Enums | CHECK constraint | Native ENUM type | CHECK constraint |
| Booleans | Native BOOLEAN | BOOLEAN (TINYINT) | INTEGER (0/1) |
| JSON | JSON, JSONB | JSON | TEXT (stored as JSON string) |
| Timestamps | TIMESTAMP | TIMESTAMP | TEXT (ISO8601) |
| Identifiers | Double quotes "table" | Backticks `table` | Double quotes "table" |
| Foreign Keys | Native support + CASCADE | Native support + CASCADE | Native support (needs PRAGMA) |
| Character Sets | UTF-8 default | UTF8MB4 with collation | UTF-8 default |
When configuring Schemact, import and use the appropriate generator:
// For PostgreSQL
import { PostgresGenerator } from 'schemact';
generator: new PostgresGenerator()
// For MySQL/MariaDB
import { MySQLGenerator } from 'schemact';
generator: new MySQLGenerator()
// For SQLite
import { SQLiteGenerator } from 'schemact';
generator: new SQLiteGenerator()
The same .sigl files work across all databases - Schemact automatically generates the correct SQL syntax for each platform.
You can also use Schemact programmatically:
import {
Parser,
PostgresGenerator,
MySQLGenerator,
SQLiteGenerator,
MigrationRunner
} from 'schemact';
// Parse a .sact file
const ast = Parser.parse(`
model User {
id Serial @pk
email Text @unique
}
`);
// Generate SQL for different databases
const postgresGen = new PostgresGenerator();
const mysqlGen = new MySQLGenerator();
const sqliteGen = new SQLiteGenerator();
const postgresSQL = postgresGen.generateUp(ast);
const mysqlSQL = mysqlGen.generateUp(ast);
const sqliteSQL = sqliteGen.generateUp(ast);
console.log(postgresSQL);
// [
// 'CREATE TABLE "User" (\n "id" SERIAL PRIMARY KEY,\n "email" TEXT UNIQUE\n);'
// ]
console.log(mysqlSQL);
// [
// 'CREATE TABLE `User` (\n `id` INT AUTO_INCREMENT PRIMARY KEY,\n `email` TEXT UNIQUE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;'
// ]
// Run migrations programmatically
const runner = new MigrationRunner({
adapter: myAdapter,
generator: new PostgresGenerator(), // or MySQLGenerator(), SQLiteGenerator()
migrationsPath: './migrations',
});
await runner.up();
Schemact provides clear, actionable error messages:
If a migration file is modified after being applied:
✗ Migration file "20240101_users.sigl" has been modified!
This file was applied on 2024-01-01T12:00:00Z and must not be changed.
Expected hash: abc123...
Current hash: def456...
If your .sigl file has syntax errors:
✗ Parse error at line 5, column 10: Expected column type
If your adapter isn't configured:
✗ Database adapter not configured.
Please edit schemact.config.js and provide an adapter.
src/
├── ast/
│ ├── types.ts # AST interfaces and error types
│ ├── lexer.ts # Tokenizer
│ └── parser.ts # AST builder
├── generators/
│ ├── base.ts # SQL generator interface
│ └── postgres.ts # PostgreSQL generator
├── engine/
│ ├── ledger.ts # Migration state management
│ ├── runner.ts # Migration orchestration
│ └── introspector.ts # Database reverse engineering
├── utils/
│ ├── colors.ts # ANSI color codes
│ └── formatting.ts # String formatting helpers
├── index.ts # Main exports
└── cli.ts # CLI entry point
npm run build
npm run dev
npm link
schemact --help
Schemact is built on these core principles:
any.| Feature | Schemact | Knex | Prisma | TypeORM |
|---|---|---|---|---|
| Runtime Dependencies | 0 | ~20 | ~50 | ~100 |
| Custom DSL | ✓ | ✗ | ✓ | ✗ |
| AST-Based | ✓ | ✗ | ✓ | ✗ |
| Two-Way Sync | ✓ | ✗ | ✓ | ✗ |
| Integrity Checking | ✓ | ✗ | ✗ | ✗ |
| Database Agnostic | ✓ | ✓ | ✓ | ✓ |
| Transaction Support | ✓ | ✓ | ✓ | ✓ |
Contributions are welcome! Please ensure:
any)MIT
Built with Node.js built-ins and determination to avoid dependency bloat.
Schemact: Schema management, redefined.
FAQs
A Zero-Dependency, AST-Based Database Schema Management Tool
The npm package schemact receives a total of 0 weekly downloads. As such, schemact popularity was classified as not popular.
We found that schemact demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.