Compare commits

...

11 Commits

Author SHA1 Message Date
Henry Chan
cb1284c8c0
feat: init version in postgres driver only if not set (#11373) 2025-11-24 13:10:30 +01:00
Naor Peled
9383799b3d
chore: add Qodo config (#11791) 2025-11-24 08:18:00 +02:00
Oleg "OSA413" Sokolov
ea0f155532
ci(oracle): add extra sleep after container starts (#11795) 2025-11-24 10:54:49 +05:00
Daniel Harvey
ade198c77c
feat: export QueryPartialEntity and QueryDeepPartialEntity types (#11748)
Co-authored-by: Giorgio Boa <35845425+gioboa@users.noreply.github.com>
Co-authored-by: Piotr Kuczynski <piotr.kuczynski@gmail.com>
Co-authored-by: Oleg "OSA413" Sokolov <OSA413@users.noreply.github.com>
2025-11-23 18:18:51 +05:00
Copilot
5fa8a0bf6c
chore: add GitHub Copilot instructions (#11781)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Naor Peled <me@naor.dev>
2025-11-21 23:03:26 +02:00
Piotr Kuczynski
6eda13884e
docs: fix build status badge url (#11790) 2025-11-21 20:27:19 +01:00
Mike Guida
6ed24f8235
ci: run tests on commits to master and next (#11783)
Co-authored-by: Oleg "OSA413" Sokolov <OSA413@users.noreply.github.com>
2025-11-21 01:11:49 +05:00
dependabot[bot]
cad0921827
build(deps): bump js-yaml in /docs (#11779)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-20 19:52:17 +01:00
Pablo Thiele
dc74f5374e
fix(deps): upgrade glob to fix CVE-2025-64756 (#11784)
Co-authored-by: Oleg "OSA413" Sokolov <OSA413@users.noreply.github.com>
2025-11-20 23:20:05 +05:00
Piotr Kuczynski
bec548a7d4
ci: migrate from nyc to c8 (#11759)
Co-authored-by: Oleg "OSA413" Sokolov <OSA413@users.noreply.github.com>
2025-11-20 22:15:53 +05:00
Edge-Seven
02e7b713ed
docs(cockroach): fix typo in CockroachDriver jsdoc for mappedDataTypes (#11775)
Co-authored-by: khanhkhanhlele <namkhanh20xx@gmail.com>
2025-11-18 11:07:22 +01:00
14 changed files with 468 additions and 1523 deletions

View File

@ -1,8 +1,9 @@
{
"all": true,
"cache": false,
"exclude": ["**/*.d.ts"],
"exclude": ["node_modules", "**/*.d.ts"],
"exclude-after-remap": true,
"extension": [".ts"],
"include": ["build/compiled/src/**", "src/**"],
"reporter": "lcov"
"reporter": ["lcov"]
}

283
.github/copilot-instructions.md vendored Normal file
View File

@ -0,0 +1,283 @@
# GitHub Copilot Instructions for TypeORM
This document provides guidance for GitHub Copilot when working with the TypeORM codebase.
## Project Overview
TypeORM is a TypeScript-based Object-Relational Mapping (ORM) library that supports multiple databases including MySQL/MariaDB, PostgreSQL, MS SQL Server, Oracle, SAP HANA, SQLite, MongoDB, and Google Spanner. It implements both Active Record and Data Mapper patterns and runs on Node.js, Browser, React Native, and Electron platforms.
## Architecture & Structure
### Core Components
- **`src/data-source/`** - DataSource (formerly Connection) management
- **`src/entity-manager/`** - Entity management and operations
- **`src/repository/`** - Repository pattern implementation
- **`src/query-builder/`** - SQL query building
- **`src/decorator/`** - TypeScript decorators for entities, columns, relations
- **`src/driver/`** - Database-specific drivers
- **`src/metadata/`** - Entity metadata management
- **`src/schema-builder/`** - Schema creation and migration
- **`src/migration/`** - Database migration system
- **`src/subscriber/`** - Event subscriber system
- **`src/persistence/`** - Entity persistence logic
### Design Patterns
- **Active Record Pattern**: Entities have methods to save, remove, and query themselves
- **Data Mapper Pattern**: Repositories handle entity persistence separately from business logic
- **Decorator Pattern**: Extensive use of TypeScript decorators for metadata definition
- **Builder Pattern**: QueryBuilder for constructing complex queries
## Coding Standards
### TypeScript Configuration
- Target: ES2021+ with CommonJS modules
- Decorators: `experimentalDecorators` and `emitDecoratorMetadata` enabled
### Code Style
- **Formatting**: Use Prettier with these settings:
- No semicolons (`"semi": false`)
- Arrow function parentheses always (`"arrowParens": "always"`)
- Trailing commas everywhere (`"trailingComma": "all"`)
- **Linting**: ESLint with TypeScript support
- Use `@typescript-eslint` rules
- Warnings allowed for some `@typescript-eslint/no-*` rules
- Unused variables starting with `_` are ignored
- **Naming Conventions**:
- Classes: PascalCase (e.g., `DataSource`, `EntityManager`)
- Interfaces: PascalCase (e.g., `ColumnOptions`, `RelationOptions`)
- Variables/functions: camelCase
- Constants: UPPER_SNAKE_CASE for true constants
- Private members: Use standard camelCase (no underscore prefix)
### TypeScript Patterns
- Use explicit types for public APIs
- Prefer interfaces over type aliases for object shapes
- Use generics for reusable components
- Avoid `any` where possible; use `unknown` or proper types
- Use optional chaining (`?.`) and nullish coalescing (`??`) operators
- Leverage TypeScript utility types (`Partial<T>`, `Required<T>`, `Pick<T>`, etc.)
## Testing
### Test Structure
Tests are organized in `test/` directory:
- **`test/functional/`** - Feature and integration tests organized by functionality (preferred)
- **`test/github-issues/`** - Tests for specific GitHub issues
- **`test/unit/`** - Unit tests for individual components
- **`test/utils/`** - Test utilities and helpers
**Note**: Prefer writing functional tests over per-issue tests.
### Test Writing Guidelines
1. **Use the standard test template**:
```typescript
import "reflect-metadata"
import { createTestingConnections, closeTestingConnections, reloadTestingDatabases } from "../../utils/test-utils"
import { DataSource } from "../../../src/data-source/DataSource"
import { expect } from "chai"
describe("description of functionality", () => {
let dataSources: DataSource[]
before(async () => dataSources = await createTestingConnections({
entities: [__dirname + "/entity/*{.js,.ts}"],
schemaCreate: true,
dropSchema: true,
}))
beforeEach(() => reloadTestingDatabases(dataSources))
after(() => closeTestingConnections(dataSources))
it("should do something specific", () => Promise.all(dataSources.map(async dataSource => {
// Test implementation
})))
})
```
2. **Test Configuration**:
- Tests run against multiple databases (as configured in `ormconfig.json`)
- Each test should work across all supported databases unless database-specific
- Place entity files in `./entity/` relative to test file for automatic loading
- Use `Promise.all(dataSources.map(...))` pattern to test against all databases
3. **Test Naming**:
- Use descriptive `describe()` blocks for features
- Use "should..." format for `it()` descriptions
- Reference GitHub issue numbers when fixing specific issues
4. **Running Tests**:
- Full test suite: `npm test` (compiles then runs tests)
- Fast iteration: `npm run test:fast` (runs without recompiling)
- Specific tests: `npm run test:fast -- --grep "pattern"`
- Watch mode: `npm run compile -- --watch` + `npm run test:fast`
## Database-Specific Considerations
### Multi-Database Support
When writing code or tests:
- Ensure compatibility across all supported databases
- Use driver-specific code only in `src/driver/` directory
- Test database-agnostic code against multiple databases
- Use `DataSource.options.type` to check database type when needed
- Be aware of SQL dialect differences (LIMIT vs TOP, etc.)
### Driver Implementation
Each driver in `src/driver/` implements common interfaces:
- Connection management
- Query execution
- Schema synchronization
- Type mapping
- Transaction handling
## Common Development Tasks
### Adding a New Feature
1. Create entities in appropriate test directory
2. Write tests first (TDD approach encouraged)
3. Implement feature in `src/`
4. Ensure tests pass across all databases
5. Update documentation if public API changes
6. Follow commit message conventions
### Adding a New Decorator
1. Create decorator file in `src/decorator/`
2. Create metadata args in `src/metadata-args/`
3. Update metadata builder in `src/metadata-builder/`
4. Export from `src/index.ts`
5. Add comprehensive tests
6. Update TypeScript type definitions if needed
### Working with Migrations
- Migrations are in `src/migration/`
- Migration files should be timestamped
- Support both up and down migrations
- Test migrations against all supported databases
- Ensure schema changes are reversible
## Build & Development Workflow
### Commands
- **Build**: `npm run compile` - Compiles TypeScript to `build/compiled/`
- **Package**: `npm run package` - Creates distribution in `build/package/`
- **Pack**: `npm run pack` - Creates `.tgz` file in `build/`
- **Test**: `npm test` - Compile and run all tests
- **Lint**: `npm run lint` - Run ESLint
- **Format**: `npm run format` - Run Prettier
- **Watch**: `npm run watch` - Watch mode for TypeScript compilation
### Development Setup
1. Install dependencies: `npm install`
2. Copy config: `cp ormconfig.sample.json ormconfig.json`
3. Configure database connections in `ormconfig.json`
4. Optionally use Docker: `docker-compose up` for database services
### Pre-commit Hooks
- Husky runs pre-commit hooks
- Lint-staged runs on staged files
- Format and lint checks must pass
## Contribution Guidelines
### Commit Message Format
Follow conventional commits:
```
<type>: <subject>
<body>
<footer>
```
**Types**: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `chore`, `revert`
**Subject**:
- Use imperative, present tense
- Don't capitalize first letter
- No period at the end
- Max 100 characters per line
### Pull Request Requirements
- All tests must pass
- Include appropriate tests for changes
- Follow existing code style
- Update documentation for API changes
- Reference related GitHub issues
- Get approval before merging
## Common Patterns & Idioms
### Entity Definition
```typescript
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number
@Column()
name: string
@OneToMany(() => Photo, photo => photo.user)
photos: Photo[]
}
```
### Repository Usage
```typescript
const userRepository = dataSource.getRepository(User)
const user = await userRepository.findOne({ where: { id: 1 } })
```
### QueryBuilder
```typescript
const users = await dataSource
.getRepository(User)
.createQueryBuilder("user")
.leftJoinAndSelect("user.photos", "photo")
.where("user.name = :name", { name: "John" })
.getMany()
```
### Transactions
```typescript
await dataSource.transaction(async (manager) => {
await manager.save(user)
await manager.save(photo)
})
```
## Important Notes
- Always import `reflect-metadata` before TypeORM
- Be careful with circular dependencies between entities
- Use lazy relations or forward references for circular entity references
- Connection pooling is handled automatically by drivers
- Be mindful of N+1 query problems; use joins or eager loading
- Repository methods are async; always use `await`
- Entity instances should be plain objects, not class instances with methods (Data Mapper pattern)
## Resources
- [Main Documentation](https://typeorm.io)
- [Contributing Guide](../CONTRIBUTING.md)
- [Developer Guide](../DEVELOPER.md)
- [GitHub Repository](https://github.com/typeorm/typeorm)
- [Issue Tracker](https://github.com/typeorm/typeorm/issues)

View File

@ -31,7 +31,7 @@ jobs:
path: build/
- run: npm ci
- run: cp .github/workflows/test/cockroachdb.ormconfig.json ormconfig.json
- run: npx nyc npm run test:ci
- run: npx c8 npm run test:ci
- name: Coveralls Parallel
uses: coverallsapp/github-action@v2
with:
@ -58,7 +58,7 @@ jobs:
path: build/
- run: npm ci
- run: cp .github/workflows/test/mongodb.ormconfig.json ormconfig.json
- run: npx nyc npm run test:ci
- run: npx c8 npm run test:ci
- name: Coveralls Parallel
uses: coverallsapp/github-action@v2
with:
@ -89,7 +89,7 @@ jobs:
path: build/
- run: npm ci
- run: cp .github/workflows/test/mssql.ormconfig.json ormconfig.json
- run: npx nyc npm run test:ci
- run: npx c8 npm run test:ci
- name: Coveralls Parallel
uses: coverallsapp/github-action@v2
with:
@ -130,7 +130,7 @@ jobs:
path: build/
- run: npm ci
- run: cp .github/workflows/test/mysql-mariadb.ormconfig.json ormconfig.json
- run: npx nyc npm run test:ci
- run: npx c8 npm run test:ci
- name: Coveralls Parallel
uses: coverallsapp/github-action@v2
with:
@ -171,7 +171,7 @@ jobs:
path: build/
- run: npm ci
- run: cp .github/workflows/test/mysql-mariadb-latest.ormconfig.json ormconfig.json
- run: npx nyc npm run test:ci
- run: npx c8 npm run test:ci
- name: Coveralls Parallel
uses: coverallsapp/github-action@v2
with:
@ -192,7 +192,7 @@ jobs:
path: build/
- run: npm ci
- run: cp .github/workflows/test/better-sqlite3.ormconfig.json ormconfig.json
- run: npx nyc npm run test:ci
- run: npx c8 npm run test:ci
- name: Coveralls Parallel
uses: coverallsapp/github-action@v2
with:
@ -213,7 +213,7 @@ jobs:
path: build/
- run: npm ci
- run: cp .github/workflows/test/sqlite.ormconfig.json ormconfig.json
- run: npx nyc npm run test:ci
- run: npx c8 npm run test:ci
- name: Coveralls Parallel
uses: coverallsapp/github-action@v2
with:
@ -234,7 +234,7 @@ jobs:
path: build/
- run: npm ci
- run: cp .github/workflows/test/sqljs.ormconfig.json ormconfig.json
- run: npx nyc npm run test:ci
- run: npx c8 npm run test:ci
- name: Coveralls Parallel
uses: coverallsapp/github-action@v2
with:
@ -274,7 +274,7 @@ jobs:
path: build/
- run: npm ci
- run: cp .github/workflows/test/postgres.ormconfig.json ormconfig.json
- run: npx nyc npm run test:ci
- run: npx c8 npm run test:ci
- name: Coveralls Parallel
uses: coverallsapp/github-action@v2
with:
@ -300,7 +300,8 @@ jobs:
- run: npm ci
- run: cat ormconfig.sample.json | jq 'map(select(.name == "oracle"))' > ormconfig.json
- run: docker compose up oracle --no-recreate --wait
- run: npx nyc npm run test:ci
- run: sleep 10
- run: npx c8 npm run test:ci
- name: Coveralls Parallel
uses: coverallsapp/github-action@v2
@ -327,7 +328,7 @@ jobs:
- run: npm ci
- run: cat ormconfig.sample.json | jq 'map(select(.name == "hanaexpress"))' > ormconfig.json
- run: docker compose up hanaexpress --no-recreate --wait
- run: npx nyc npm run test:ci
- run: npx c8 npm run test:ci
- name: Coveralls Parallel
uses: coverallsapp/github-action@v2

View File

@ -22,7 +22,7 @@ jobs:
- run: npm ci
- run: cp .github/workflows/test/better-sqlite3.ormconfig.json ormconfig.json
- run: npx nyc npm run test:ci
- run: npx c8 npm run test:ci
- name: Coveralls Parallel
uses: coverallsapp/github-action@v2
@ -45,7 +45,7 @@ jobs:
- run: npm ci
- run: cp .github/workflows/test/sqlite.ormconfig.json ormconfig.json
- run: npx nyc npm run test:ci
- run: npx c8 npm run test:ci
- name: Coveralls Parallel
uses: coverallsapp/github-action@v2
@ -68,7 +68,7 @@ jobs:
- run: npm ci
- run: cp .github/workflows/test/sqljs.ormconfig.json ormconfig.json
- run: npx nyc npm run test:ci
- run: npx c8 npm run test:ci
- name: Coveralls Parallel
uses: coverallsapp/github-action@v2

View File

@ -1,5 +1,9 @@
name: Tests
on:
push:
branches:
- master
- next
pull_request:
jobs:

5
.gitignore vendored
View File

@ -3,12 +3,11 @@
._*
### Node ###
npm-debug.log*
build/
coverage/
*.lcov
.nyc_output/
node_modules/
npm-debug.log*
*.lcov
### VisualStudioCode ###
.vscode/*

14
.pr_agent.toml Normal file
View File

@ -0,0 +1,14 @@
[github_app]
pr_commands = [
"/review",
"/improve",
]
handle_push_trigger = true
push_commands = [
"/improve",
]
[auto_best_practices]
enable_auto_best_practices = true
utilize_auto_best_practices = true

View File

@ -8,11 +8,11 @@
</a>
<br>
<br>
<a href="https://www.npmjs.com/package/typeorm"><img src="https://img.shields.io/npm/v/typeorm" alt="NPM Version" /></a>
<a href="https://www.npmjs.com/package/typeorm"><img src="https://img.shields.io/npm/dm/typeorm" alt="NPM Downloads" /></a>
<a href="https://github.com/typeorm/typeorm/actions/workflows/commit-validation.yml?query=branch%3Amaster"><img src="https://github.com/typeorm/typeorm/actions/workflows/commit-validation.yml/badge.svg?branch=master" alt="Commit Validation"/></a>
<a href="https://coveralls.io/github/typeorm/typeorm?branch=master"><img src="https://coveralls.io/repos/github/typeorm/typeorm/badge.svg?branch=master" alt="Coverage Status" /></a>
<a href=""><img src="https://img.shields.io/badge/License-MIT-teal.svg" alt="MIT License" /></a>
<a href="https://www.npmjs.com/package/typeorm"><img src="https://img.shields.io/npm/v/typeorm" alt="NPM Version"/></a>
<a href="https://www.npmjs.com/package/typeorm"><img src="https://img.shields.io/npm/dm/typeorm" alt="NPM Downloads"/></a>
<a href="https://github.com/typeorm/typeorm/actions/workflows/tests.yml?query=branch%3Amaster"><img src="https://github.com/typeorm/typeorm/actions/workflows/tests.yml/badge.svg?branch=master" alt="Commit Validation"/></a>
<a href="https://coveralls.io/github/typeorm/typeorm?branch=master"><img src="https://coveralls.io/repos/github/typeorm/typeorm/badge.svg?branch=master" alt="Coverage Status"/></a>
<a href=""><img src="https://img.shields.io/badge/License-MIT-teal.svg" alt="MIT License"/></a>
<br>
<br>
</div>

12
docs/package-lock.json generated
View File

@ -8639,9 +8639,9 @@
}
},
"node_modules/gray-matter/node_modules/js-yaml": {
"version": "3.14.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
"version": "3.14.2",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz",
"integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==",
"license": "MIT",
"dependencies": {
"argparse": "^1.0.7",
@ -9981,9 +9981,9 @@
"license": "MIT"
},
"node_modules/js-yaml": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
"license": "MIT",
"dependencies": {
"argparse": "^2.0.1"

1592
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -103,7 +103,7 @@
"debug": "^4.4.3",
"dedent": "^1.7.0",
"dotenv": "^16.6.1",
"glob": "^10.4.5",
"glob": "^10.5.0",
"reflect-metadata": "^0.2.2",
"sha.js": "^2.4.12",
"sql-highlight": "^6.1.0",
@ -129,6 +129,7 @@
"@types/source-map-support": "^0.5.10",
"@types/yargs": "^17.0.33",
"better-sqlite3": "^8.7.0",
"c8": "^10.1.3",
"chai": "^4.5.0",
"chai-as-promised": "^7.1.2",
"class-transformer": "^0.5.1",
@ -150,7 +151,6 @@
"mssql": "^11.0.1",
"mysql": "^2.18.1",
"mysql2": "^3.15.0",
"nyc": "^17.1.0",
"oracledb": "^6.9.0",
"pg": "^8.16.3",
"pg-query-stream": "^4.10.3",

View File

@ -196,7 +196,7 @@ export class CockroachDriver implements Driver {
/**
* Orm has special columns and we need to know what database column types should be for those types.
* Column types are driver dependant.
* Column types are driver dependent.
*/
mappedDataTypes: MappedColumnTypes = {
createDate: "timestamptz",

View File

@ -73,7 +73,8 @@ export class PostgresDriver implements Driver {
options: PostgresConnectionOptions
/**
* Version of Postgres. Requires a SQL query to the DB, so it is not always set
* Version of Postgres. Requires a SQL query to the DB, so it is set on the first
* connection attempt.
*/
version?: string
@ -362,20 +363,24 @@ export class PostgresDriver implements Driver {
this.master = await this.createPool(this.options, this.options)
}
const queryRunner = this.createQueryRunner("master")
if (!this.version || !this.database || !this.searchSchema) {
const queryRunner = this.createQueryRunner("master")
this.version = await queryRunner.getVersion()
if (!this.version) {
this.version = await queryRunner.getVersion()
}
if (!this.database) {
this.database = await queryRunner.getCurrentDatabase()
if (!this.database) {
this.database = await queryRunner.getCurrentDatabase()
}
if (!this.searchSchema) {
this.searchSchema = await queryRunner.getCurrentSchema()
}
await queryRunner.release()
}
if (!this.searchSchema) {
this.searchSchema = await queryRunner.getCurrentSchema()
}
await queryRunner.release()
if (!this.schema) {
this.schema = this.searchSchema
}

View File

@ -160,6 +160,10 @@ export { WhereExpression } from "./query-builder/WhereExpressionBuilder"
export { InsertResult } from "./query-builder/result/InsertResult"
export { UpdateResult } from "./query-builder/result/UpdateResult"
export { DeleteResult } from "./query-builder/result/DeleteResult"
export {
QueryPartialEntity,
QueryDeepPartialEntity,
} from "./query-builder/QueryPartialEntity"
export { QueryResult } from "./query-runner/QueryResult"
export { QueryRunner } from "./query-runner/QueryRunner"
export { MongoEntityManager } from "./entity-manager/MongoEntityManager"