{"id":45278163,"url":"https://github.com/ersinkoc/schemact","last_synced_at":"2026-02-21T02:04:06.368Z","repository":{"id":330399393,"uuid":"1106789748","full_name":"ersinkoc/Schemact","owner":"ersinkoc","description":"A Zero-Dependency, AST-Based Database Schema Management Tool","archived":false,"fork":false,"pushed_at":"2025-12-25T08:00:47.000Z","size":169,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-12-26T20:03:29.950Z","etag":null,"topics":["database","migration","mysql","postgresql","sql","sqlite"],"latest_commit_sha":null,"homepage":"http://schemact.oxog.dev/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ersinkoc.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":null,"code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-11-30T00:07:21.000Z","updated_at":"2025-12-25T08:00:32.000Z","dependencies_parsed_at":"2025-12-26T20:03:32.193Z","dependency_job_id":null,"html_url":"https://github.com/ersinkoc/Schemact","commit_stats":null,"previous_names":["ersinkoc/schemact"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/ersinkoc/Schemact","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ersinkoc%2FSchemact","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ersinkoc%2FSchemact/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ersinkoc%2FSchemact/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ersinkoc%2FSchemact/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ersinkoc","download_url":"https://codeload.github.com/ersinkoc/Schemact/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ersinkoc%2FSchemact/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29671513,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-21T00:11:43.526Z","status":"online","status_checked_at":"2026-02-21T02:00:07.432Z","response_time":107,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["database","migration","mysql","postgresql","sql","sqlite"],"created_at":"2026-02-21T02:03:55.152Z","updated_at":"2026-02-21T02:04:06.361Z","avatar_url":"https://github.com/ersinkoc.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Schemact\n\n[![npm version](https://img.shields.io/npm/v/schemact.svg)](https://www.npmjs.com/package/schemact)\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)\n[![Node.js Version](https://img.shields.io/badge/node-%3E%3D18.0.0-brightgreen.svg)](https://nodejs.org/)\n[![TypeScript](https://img.shields.io/badge/TypeScript-5.9-blue.svg)](https://www.typescriptlang.org/)\n[![Zero Dependencies](https://img.shields.io/badge/dependencies-0-success.svg)](package.json)\n[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md)\n\n**A Zero-Dependency, AST-Based Database Schema Management Tool**\n\n\u003e **Website:** [schemact.oxog.dev](https://schemact.oxog.dev) | **Documentation:** [schemact.oxog.dev/docs.html](https://schemact.oxog.dev/docs.html) | **GitHub:** [github.com/ersinkoc/schemact](https://github.com/ersinkoc/schemact)\nSchemact 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).\n\n## Features\n\n- **Zero Runtime Dependencies**: Only uses Node.js built-ins (`fs`, `path`, `crypto`, etc.)\n- **Multi-Database Support**: PostgreSQL, MySQL/MariaDB, and SQLite generators included\n- **Custom DSL**: Write schema definitions in `.sigl` files with a clean, intuitive syntax\n- **AST-Based**: Proper compiler pipeline (Lexer → Parser → AST → CodeGen)\n- **Database Agnostic**: Core logic is pure; database interaction via adapter pattern\n- **Two-Way Sync**: Apply migrations (Up) AND reverse-engineer existing databases (Introspection)\n- **Integrity Checking**: SHA-256 hashing ensures migration files haven't been tampered with\n- **Transaction Support**: All migrations run in transactions for safety\n\n## Installation\n\n```bash\nnpm install -g schemact\n```\n\nOr use it in your project:\n\n```bash\nnpm install --save-dev schemact\n```\n\n## Quick Start\n\n### 1. Initialize a new Schemact project\n\n```bash\nschemact init\n```\n\nThis creates:\n- `migrations/` directory for your schema files\n- `schemact.config.js` configuration file\n\n### 2. Configure your database adapter\n\nEdit `schemact.config.js` to set up your database connection:\n\n```javascript\nimport pg from 'pg';\nconst { Pool } = pg;\n\nconst pool = new Pool({\n  host: 'localhost',\n  port: 5432,\n  database: 'mydb',\n  user: 'postgres',\n  password: 'password',\n});\n\nconst adapter = {\n  async connect() {},\n  async disconnect() {\n    await pool.end();\n  },\n  async query(sql) {\n    const result = await pool.query(sql);\n    return result.rows;\n  },\n  async transaction(queries) {\n    const client = await pool.connect();\n    try {\n      await client.query('BEGIN');\n      for (const sql of queries) {\n        await client.query(sql);\n      }\n      await client.query('COMMIT');\n    } catch (error) {\n      await client.query('ROLLBACK');\n      throw error;\n    } finally {\n      client.release();\n    }\n  },\n};\n\nexport default {\n  adapter,\n  migrationsPath: './migrations',\n  ledgerPath: './.schemact_ledger.json',\n};\n```\n\n### 3. Create your first migration\n\n```bash\nschemact create users\n```\n\nThis generates a timestamped file like `migrations/20240101120000_users.sigl`.\n\n### 4. Edit the migration file\n\n```sigl\n# Define user table\nmodel User {\n  id        Serial        @pk\n  email     VarChar(255)  @unique @notnull\n  username  VarChar(50)   @unique @notnull\n  password  VarChar(255)  @notnull\n  role      Enum('admin', 'user', 'guest') @default('user')\n  isActive  Boolean       @default(true)\n  createdAt Timestamp     @default(now)\n}\n```\n\n### 5. Apply the migration\n\n```bash\nschemact up\n```\n\n### 6. Check migration status\n\n```bash\nschemact status\n```\n\n## The Schemact DSL Syntax\nSchemact uses `.sigl` files with a custom syntax designed for clarity and expressiveness.\n\n### Model Definition\n\n```sigl\nmodel TableName {\n  columnName  ColumnType  @decorator1 @decorator2\n}\n```\n\n### Supported Types\n\n- **Integers**: `Serial`, `Int`, `BigInt`, `SmallInt`\n- **Strings**: `VarChar(n)`, `Char(n)`, `Text`\n- **Boolean**: `Boolean`\n- **Dates/Times**: `Timestamp`, `Date`, `Time`\n- **Decimals**: `Decimal(p,s)`, `Numeric(p,s)`, `Real`, `DoublePrecision`\n- **JSON**: `Json`, `Jsonb`\n- **UUID**: `Uuid`\n- **Enums**: `Enum('value1', 'value2', ...)`\n\n### Decorators\n\n| Decorator | Description | Example |\n|-----------|-------------|---------|\n| `@pk` | Primary key | `id Serial @pk` |\n| `@unique` | Unique constraint | `email VarChar(255) @unique` |\n| `@notnull` | NOT NULL constraint | `name Text @notnull` |\n| `@default(value)` | Default value | `active Boolean @default(true)` |\n| `@ref(Table.column)` | Foreign key | `userId Int @ref(User.id)` |\n| `@onDelete(action)` | Foreign key delete action | `@ref(User.id) @onDelete('cascade')` |\n\n### Special Values\n\n- `now` - Maps to `CURRENT_TIMESTAMP`\n- `true`/`false` - Boolean literals\n- Strings must be quoted: `'value'`\n\n### Raw SQL Escape Hatch\n\nFor operations not covered by the DSL, prefix lines with `\u003e`:\n\n```sigl\n\u003e CREATE INDEX idx_users_email ON \"User\"(\"email\");\n\u003e CREATE VIEW active_users AS SELECT * FROM \"User\" WHERE \"isActive\" = true;\n```\n\n### Complete Example\n\n```sigl\n# Blog schema\n\nmodel User {\n  id        Serial        @pk\n  email     VarChar(255)  @unique @notnull\n  username  VarChar(50)   @unique @notnull\n  createdAt Timestamp     @default(now)\n}\n\nmodel Post {\n  id          Serial        @pk\n  title       VarChar(200)  @notnull\n  content     Text\n  authorId    Int           @ref(User.id) @onDelete('cascade')\n  published   Boolean       @default(false)\n  createdAt   Timestamp     @default(now)\n}\n\n# Create index for better query performance\n\u003e CREATE INDEX idx_posts_author ON \"Post\"(\"authorId\");\n```\n\n## CLI Commands\n\n### `schemact init`\n\nInitialize a new Schemact project. Creates:\n- `migrations/` directory\n- `schemact.config.js` configuration file\n\n### `schemact create \u003cname\u003e`\n\nCreate a new migration file with a timestamped filename.\n\n**Example:**\n```bash\nschemact create add_users_table\n# Creates: migrations/20240101120000_add_users_table.sigl\n```\n\n### `schemact up`\n\nApply all pending migrations. Migrations are executed in chronological order based on their filename timestamps.\n\n### `schemact down`\n\nRollback the last batch of migrations. Automatically generates DROP statements from your schema definitions.\n\n### `schemact status`\n\nShow the current state of migrations:\n- Applied migrations\n- Pending migrations\n- Current batch number\n\n### `schemact pull [schema]`\n\nIntrospect an existing database and generate `.sigl` files. This is the \"reverse engineering\" feature.\n\n**Example:**\n```bash\nschemact pull public\n# Generates migrations/2024-01-01_introspected.sigl\n```\n\n### `schemact help`\n\nDisplay help information.\n\n### `schemact version`\n\nDisplay version information.\n\n## Architecture\nSchemact follows a clean, modular architecture:\n\n### Compiler Pipeline\n\n```\n.sact file → Lexer → Tokens → Parser → AST → Generator → SQL\n```\n\n1. **Lexer** (`src/ast/lexer.ts`): Tokenizes input into meaningful chunks\n2. **Parser** (`src/ast/parser.ts`): Builds an Abstract Syntax Tree\n3. **Generator** (`src/generators/postgres.ts`): Converts AST to SQL (both UP and DOWN)\n\n### State Management\n\nThe **Ledger** (`src/engine/ledger.ts`) tracks applied migrations in `.schemact_ledger.json`:\n- Stores SHA-256 hash of migration content\n- Validates that applied migrations haven't been modified\n- Manages batch numbers for rollbacks\n\n### Migration Execution\n\nThe **Runner** (`src/engine/runner.ts`) orchestrates the entire migration flow:\n- Loads migration files\n- Validates integrity\n- Executes SQL in transactions\n- Updates the ledger\n\n### Introspection\n\nThe **Introspector** (`src/engine/introspector.ts`) reverse-engineers databases:\n- Queries `information_schema` tables\n- Maps SQL types back to Schemact types\n- Generates formatted `.sigl` files\n\n## Database Adapter Interface\nSchemact is database-agnostic through the adapter pattern. Implement this interface for your database:\n\n```typescript\ninterface DbAdapter {\n  connect(): Promise\u003cvoid\u003e;\n  disconnect(): Promise\u003cvoid\u003e;\n  query(sql: string): Promise\u003cany[]\u003e;\n  transaction(queries: string[]): Promise\u003cvoid\u003e;\n}\n```\n\n### PostgreSQL Example\n\n```javascript\nimport pg from 'pg';\nconst { Pool } = pg;\n\nconst pool = new Pool({ /* config */ });\n\nconst adapter = {\n  async connect() {},\n  async disconnect() {\n    await pool.end();\n  },\n  async query(sql) {\n    const result = await pool.query(sql);\n    return result.rows;\n  },\n  async transaction(queries) {\n    const client = await pool.connect();\n    try {\n      await client.query('BEGIN');\n      for (const sql of queries) {\n        await client.query(sql);\n      }\n      await client.query('COMMIT');\n    } catch (error) {\n      await client.query('ROLLBACK');\n      throw error;\n    } finally {\n      client.release();\n    }\n  },\n};\n```\n\n### MySQL Example\n\n```javascript\nimport mysql from 'mysql2/promise';\n\nconst pool = mysql.createPool({\n  host: 'localhost',\n  user: 'root',\n  password: 'password',\n  database: 'mydb',\n});\n\nconst adapter = {\n  async connect() {},\n  async disconnect() {\n    await pool.end();\n  },\n  async query(sql) {\n    const [rows] = await pool.query(sql);\n    return rows;\n  },\n  async transaction(queries) {\n    const connection = await pool.getConnection();\n    try {\n      await connection.beginTransaction();\n      for (const sql of queries) {\n        await connection.query(sql);\n      }\n      await connection.commit();\n    } catch (error) {\n      await connection.rollback();\n      throw error;\n    } finally {\n      connection.release();\n    }\n  },\n};\n\nexport default {\n  adapter,\n  generator: new MySQLGenerator(), // Use MySQLGenerator for MySQL\n  migrationsPath: './migrations',\n  ledgerPath: './.schemact_ledger.json',\n};\n```\n\n### SQLite Example\n\n```javascript\nimport Database from 'better-sqlite3';\n\nconst db = new Database('./mydb.sqlite');\n\nconst adapter = {\n  async connect() {\n    // Enable foreign keys\n    db.pragma('foreign_keys = ON');\n  },\n  async disconnect() {\n    db.close();\n  },\n  async query(sql) {\n    // For SELECT queries\n    if (sql.trim().toUpperCase().startsWith('SELECT')) {\n      return db.prepare(sql).all();\n    }\n    // For other queries\n    db.prepare(sql).run();\n    return [];\n  },\n  async transaction(queries) {\n    const transaction = db.transaction(() =\u003e {\n      for (const sql of queries) {\n        db.prepare(sql).run();\n      }\n    });\n    transaction();\n  },\n};\n\nexport default {\n  adapter,\n  generator: new SQLiteGenerator(), // Use SQLiteGenerator for SQLite\n  migrationsPath: './migrations',\n  ledgerPath: './.schemact_ledger.json',\n};\n```\n\n## Multi-Database Support\nSchemact provides dedicated SQL generators for different database systems, each optimized for the target database's specific syntax and features:\n\n### Database-Specific Features\n\n| Feature | PostgreSQL | MySQL | SQLite |\n|---------|-----------|-------|--------|\n| **Auto-increment** | `SERIAL` | `INT AUTO_INCREMENT` | `INTEGER PRIMARY KEY AUTOINCREMENT` |\n| **Enums** | CHECK constraint | Native `ENUM` type | CHECK constraint |\n| **Booleans** | Native `BOOLEAN` | `BOOLEAN` (TINYINT) | `INTEGER` (0/1) |\n| **JSON** | `JSON`, `JSONB` | `JSON` | `TEXT` (stored as JSON string) |\n| **Timestamps** | `TIMESTAMP` | `TIMESTAMP` | `TEXT` (ISO8601) |\n| **Identifiers** | Double quotes `\"table\"` | Backticks `` `table` `` | Double quotes `\"table\"` |\n| **Foreign Keys** | Native support + CASCADE | Native support + CASCADE | Native support (needs PRAGMA) |\n| **Character Sets** | UTF-8 default | UTF8MB4 with collation | UTF-8 default |\n\n### Choosing the Right Generator\n\nWhen configuring Schemact, import and use the appropriate generator:\n\n```javascript\n// For PostgreSQL\nimport { PostgresGenerator } from 'schemact';\ngenerator: new PostgresGenerator()\n\n// For MySQL/MariaDB\nimport { MySQLGenerator } from 'schemact';\ngenerator: new MySQLGenerator()\n\n// For SQLite\nimport { SQLiteGenerator } from 'schemact';\ngenerator: new SQLiteGenerator()\n```\n\nThe same `.sigl` files work across all databases - Schemact automatically generates the correct SQL syntax for each platform.\n\n## Programmatic Usage\n\nYou can also use Schemact programmatically:\n\n```typescript\nimport {\n  Parser,\n  PostgresGenerator,\n  MySQLGenerator,\n  SQLiteGenerator,\n  MigrationRunner\n} from 'schemact';\n\n// Parse a .sact file\nconst ast = Parser.parse(`\n  model User {\n    id    Serial  @pk\n    email Text    @unique\n  }\n`);\n\n// Generate SQL for different databases\nconst postgresGen = new PostgresGenerator();\nconst mysqlGen = new MySQLGenerator();\nconst sqliteGen = new SQLiteGenerator();\n\nconst postgresSQL = postgresGen.generateUp(ast);\nconst mysqlSQL = mysqlGen.generateUp(ast);\nconst sqliteSQL = sqliteGen.generateUp(ast);\n\nconsole.log(postgresSQL);\n// [\n//   'CREATE TABLE \"User\" (\\n  \"id\" SERIAL PRIMARY KEY,\\n  \"email\" TEXT UNIQUE\\n);'\n// ]\n\nconsole.log(mysqlSQL);\n// [\n//   'CREATE TABLE `User` (\\n  `id` INT AUTO_INCREMENT PRIMARY KEY,\\n  `email` TEXT UNIQUE\\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;'\n// ]\n\n// Run migrations programmatically\nconst runner = new MigrationRunner({\n  adapter: myAdapter,\n  generator: new PostgresGenerator(), // or MySQLGenerator(), SQLiteGenerator()\n  migrationsPath: './migrations',\n});\n\nawait runner.up();\n```\n\n## Error Handling\nSchemact provides clear, actionable error messages:\n\n### Integrity Errors\n\nIf a migration file is modified after being applied:\n\n```\n✗ Migration file \"20240101_users.sigl\" has been modified!\n  This file was applied on 2024-01-01T12:00:00Z and must not be changed.\n  Expected hash: abc123...\n  Current hash: def456...\n```\n\n### Parse Errors\n\nIf your `.sigl` file has syntax errors:\n\n```\n✗ Parse error at line 5, column 10: Expected column type\n```\n\n### Configuration Errors\n\nIf your adapter isn't configured:\n\n```\n✗ Database adapter not configured.\n  Please edit schemact.config.js and provide an adapter.\n```\n\n## Project Structure\n\n```\nsrc/\n├── ast/\n│   ├── types.ts       # AST interfaces and error types\n│   ├── lexer.ts       # Tokenizer\n│   └── parser.ts      # AST builder\n├── generators/\n│   ├── base.ts        # SQL generator interface\n│   └── postgres.ts    # PostgreSQL generator\n├── engine/\n│   ├── ledger.ts      # Migration state management\n│   ├── runner.ts      # Migration orchestration\n│   └── introspector.ts # Database reverse engineering\n├── utils/\n│   ├── colors.ts      # ANSI color codes\n│   └── formatting.ts  # String formatting helpers\n├── index.ts           # Main exports\n└── cli.ts             # CLI entry point\n```\n\n## Development\n\n### Build\n\n```bash\nnpm run build\n```\n\n### Watch Mode\n\n```bash\nnpm run dev\n```\n\n### Link for Local Development\n\n```bash\nnpm link\nschemact --help\n```\n\n## Philosophy\nSchemact is built on these core principles:\n\n1. **Zero Bloat**: No runtime dependencies. Ever.\n2. **Proper Parsing**: No regex hacks. Use a real compiler pipeline.\n3. **Type Safety**: Strict TypeScript with no `any`.\n4. **Integrity**: Cryptographic hashing ensures migrations are immutable.\n5. **Simplicity**: The DSL should be intuitive and readable.\n6. **Database Agnostic**: Core logic is pure; adapters handle DB specifics.\n\n## Comparison with Other Tools\n\n| Feature | Schemact | Knex | Prisma | TypeORM |\n|---------|-------|------|--------|---------|\n| Runtime Dependencies | 0 | ~20 | ~50 | ~100 |\n| Custom DSL | ✓ | ✗ | ✓ | ✗ |\n| AST-Based | ✓ | ✗ | ✓ | ✗ |\n| Two-Way Sync | ✓ | ✗ | ✓ | ✗ |\n| Integrity Checking | ✓ | ✗ | ✗ | ✗ |\n| Database Agnostic | ✓ | ✓ | ✓ | ✓ |\n| Transaction Support | ✓ | ✓ | ✓ | ✓ |\n\n## Roadmap\n\n- [x] PostgreSQL support\n- [x] MySQL/MariaDB support\n- [x] SQLite support\n- [ ] Column alterations (ALTER TABLE)\n- [ ] Index management\n- [ ] Enum type management\n- [ ] Migration squashing\n- [ ] Dry-run mode\n- [ ] Parallel migrations\n- [ ] Migration testing utilities\n\n## Contributing\n\nContributions are welcome! Please ensure:\n\n1. No new runtime dependencies\n2. Strict TypeScript (no `any`)\n3. Tests for new features\n4. Documentation updates\n\n## License\n\nMIT\n\n## Credits\n\nBuilt with Node.js built-ins and determination to avoid dependency bloat.\n\n---\n\n**Schemact**: Schema management, redefined.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fersinkoc%2Fschemact","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fersinkoc%2Fschemact","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fersinkoc%2Fschemact/lists"}