@stingerloom/orm 0.1.2 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +139 -30
- package/dist/core/EntityManager.d.ts +2 -0
- package/dist/core/EntityManager.d.ts.map +1 -1
- package/dist/core/EntityManager.js +35 -0
- package/dist/core/EntityManager.js.map +1 -1
- package/dist/core/generators/SchemaGenerator.d.ts.map +1 -1
- package/dist/core/generators/SchemaGenerator.js +4 -0
- package/dist/core/generators/SchemaGenerator.js.map +1 -1
- package/dist/decorators/Column.d.ts +1 -1
- package/dist/decorators/Column.d.ts.map +1 -1
- package/dist/decorators/Column.js.map +1 -1
- package/dist/decorators/CreateTimestamp.d.ts +3 -0
- package/dist/decorators/CreateTimestamp.d.ts.map +1 -0
- package/dist/decorators/CreateTimestamp.js +16 -0
- package/dist/decorators/CreateTimestamp.js.map +1 -0
- package/dist/decorators/UpdateTimestamp.d.ts +3 -0
- package/dist/decorators/UpdateTimestamp.d.ts.map +1 -0
- package/dist/decorators/UpdateTimestamp.js +16 -0
- package/dist/decorators/UpdateTimestamp.js.map +1 -0
- package/dist/decorators/index.d.ts +2 -0
- package/dist/decorators/index.d.ts.map +1 -1
- package/dist/decorators/index.js +2 -0
- package/dist/decorators/index.js.map +1 -1
- package/dist/dialects/mysql/MySqlDriver.d.ts.map +1 -1
- package/dist/dialects/mysql/MySqlDriver.js +2 -0
- package/dist/dialects/mysql/MySqlDriver.js.map +1 -1
- package/dist/dialects/postgres/PostgresDriver.d.ts.map +1 -1
- package/dist/dialects/postgres/PostgresDriver.js +2 -0
- package/dist/dialects/postgres/PostgresDriver.js.map +1 -1
- package/dist/dialects/sqlite/SqliteDriver.d.ts.map +1 -1
- package/dist/dialects/sqlite/SqliteDriver.js +1 -0
- package/dist/dialects/sqlite/SqliteDriver.js.map +1 -1
- package/dist/integration/nestjs/index.d.ts +5 -0
- package/dist/integration/nestjs/index.d.ts.map +1 -0
- package/dist/integration/nestjs/index.js +16 -0
- package/dist/integration/nestjs/index.js.map +1 -0
- package/dist/integration/nestjs/inject-repository.decorator.d.ts +3 -0
- package/dist/integration/nestjs/inject-repository.decorator.d.ts.map +1 -0
- package/dist/integration/nestjs/inject-repository.decorator.js +10 -0
- package/dist/integration/nestjs/inject-repository.decorator.js.map +1 -0
- package/dist/integration/nestjs/stingerloom-orm-core.module.d.ts +6 -0
- package/dist/integration/nestjs/stingerloom-orm-core.module.d.ts.map +1 -0
- package/dist/integration/nestjs/stingerloom-orm-core.module.js +35 -0
- package/dist/integration/nestjs/stingerloom-orm-core.module.js.map +1 -0
- package/dist/integration/nestjs/stingerloom-orm.module.d.ts +11 -0
- package/dist/integration/nestjs/stingerloom-orm.module.d.ts.map +1 -0
- package/dist/integration/nestjs/stingerloom-orm.module.js +58 -0
- package/dist/integration/nestjs/stingerloom-orm.module.js.map +1 -0
- package/dist/integration/nestjs/stingerloom-orm.service.d.ts +18 -0
- package/dist/integration/nestjs/stingerloom-orm.service.d.ts.map +1 -0
- package/dist/integration/nestjs/stingerloom-orm.service.js +70 -0
- package/dist/integration/nestjs/stingerloom-orm.service.js.map +1 -0
- package/dist/metadata/MetadataContext.d.ts.map +1 -1
- package/dist/metadata/MetadataContext.js +3 -1
- package/dist/metadata/MetadataContext.js.map +1 -1
- package/package.json +29 -13
package/README.md
CHANGED
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
+
Stingerloom ORM is a decorator-driven TypeScript ORM designed for type-safe database access with first-class multi-tenancy support.
|
|
14
|
+
|
|
13
15
|
## Install
|
|
14
16
|
|
|
15
17
|
```bash
|
|
@@ -19,52 +21,159 @@ npm install @stingerloom/orm reflect-metadata
|
|
|
19
21
|
Install only the driver you need:
|
|
20
22
|
|
|
21
23
|
```bash
|
|
22
|
-
npm install mysql2
|
|
23
|
-
npm install pg
|
|
24
|
+
npm install mysql2 # MySQL / MariaDB
|
|
25
|
+
npm install pg # PostgreSQL
|
|
24
26
|
npm install better-sqlite3 # SQLite
|
|
25
27
|
```
|
|
26
28
|
|
|
27
|
-
##
|
|
29
|
+
## Defining Entities
|
|
30
|
+
|
|
31
|
+
Entities are defined with decorators. Column types are automatically inferred from TypeScript types — `string` maps to `VARCHAR`, `number` to `INT`, `boolean` to `TINYINT`/`BOOLEAN`.
|
|
28
32
|
|
|
29
33
|
```typescript
|
|
30
34
|
@Entity()
|
|
31
|
-
class
|
|
32
|
-
@PrimaryGeneratedColumn()
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
class Post {
|
|
36
|
+
@PrimaryGeneratedColumn()
|
|
37
|
+
id!: number;
|
|
38
|
+
|
|
39
|
+
@Column()
|
|
40
|
+
title!: string;
|
|
41
|
+
|
|
42
|
+
@Column({ type: "text" })
|
|
43
|
+
content!: string;
|
|
44
|
+
|
|
45
|
+
@DeletedAt()
|
|
46
|
+
deletedAt!: Date | null;
|
|
47
|
+
|
|
48
|
+
@ManyToOne(() => User, { joinColumn: "author_id", eager: true })
|
|
49
|
+
author!: User;
|
|
50
|
+
|
|
51
|
+
@BeforeInsert()
|
|
52
|
+
setDefaults() {
|
|
53
|
+
this.createdAt = new Date();
|
|
54
|
+
}
|
|
35
55
|
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## CRUD with EntityManager
|
|
36
59
|
|
|
60
|
+
```typescript
|
|
37
61
|
const em = new EntityManager();
|
|
38
|
-
await em.register({ type: "
|
|
62
|
+
await em.register({ type: "mysql", entities: [Post, User], synchronize: true });
|
|
63
|
+
|
|
64
|
+
// Create
|
|
65
|
+
const post = await em.save(Post, { title: "Hello", content: "World" });
|
|
66
|
+
|
|
67
|
+
// Read — eager relations are loaded automatically
|
|
68
|
+
const found = await em.findOne(Post, { where: { id: 1 } });
|
|
69
|
+
|
|
70
|
+
// Update
|
|
71
|
+
found.title = "Updated";
|
|
72
|
+
await em.save(Post, found);
|
|
73
|
+
|
|
74
|
+
// Soft Delete — find queries automatically exclude soft-deleted rows
|
|
75
|
+
await em.softDelete(Post, { id: 1 });
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Relationships
|
|
79
|
+
|
|
80
|
+
Four relationship types with eager, lazy, and explicit loading strategies:
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
@ManyToOne(() => User, { eager: true }) // N:1, auto-loads on find
|
|
84
|
+
@OneToMany(() => Comment, c => c.post) // 1:N, inverse side
|
|
85
|
+
@OneToOne(() => Profile, { joinColumn: "profile_id" })
|
|
86
|
+
@ManyToMany(() => Tag, { joinTable: "post_tags" }) // auto-generated join table DDL
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Query Builder
|
|
90
|
+
|
|
91
|
+
For complex queries beyond the `find` API:
|
|
39
92
|
|
|
40
|
-
|
|
41
|
-
|
|
93
|
+
```typescript
|
|
94
|
+
const qb = RawQueryBuilderFactory.create()
|
|
95
|
+
.select(["p.id", "p.title", "COUNT(c.id) AS comment_count"])
|
|
96
|
+
.from("post", "p")
|
|
97
|
+
.leftJoin("comment", "c", sql`c.post_id = p.id`)
|
|
98
|
+
.where([sql`p.deleted_at IS NULL`])
|
|
99
|
+
.groupBy(["p.id"])
|
|
100
|
+
.having([sql`COUNT(c.id) > ${5}`])
|
|
101
|
+
.orderBy([{ column: "comment_count", direction: "DESC" }])
|
|
102
|
+
.limit(10)
|
|
103
|
+
.build();
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Multi-Tenancy
|
|
107
|
+
|
|
108
|
+
Layered metadata system inspired by Docker OverlayFS. Each tenant gets an isolated metadata layer with Copy-on-Write semantics, powered by `AsyncLocalStorage` for safe concurrent access:
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
// Requests are isolated per tenant — no cross-tenant data leakage
|
|
112
|
+
app.use((req, res, next) => {
|
|
113
|
+
MetadataContext.run(req.headers["x-tenant-id"], () => next());
|
|
114
|
+
});
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
PostgreSQL schema-based isolation with automatic provisioning:
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
const runner = new PostgresTenantMigrationRunner(driver);
|
|
121
|
+
await runner.ensureSchema("tenant_42"); // CREATE SCHEMA IF NOT EXISTS
|
|
122
|
+
await runner.syncTenantSchemas(["t1", "t2"]); // batch provisioning
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Transactions
|
|
126
|
+
|
|
127
|
+
Method-level with `@Transactional()` or manual with savepoint support:
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
@Transactional("SERIALIZABLE")
|
|
131
|
+
async transfer(from: number, to: number, amount: number) {
|
|
132
|
+
// automatic BEGIN, COMMIT on success, ROLLBACK on error
|
|
133
|
+
}
|
|
42
134
|
```
|
|
43
135
|
|
|
44
|
-
##
|
|
45
|
-
|
|
46
|
-
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
- Cursor Pagination
|
|
56
|
-
-
|
|
57
|
-
-
|
|
58
|
-
-
|
|
136
|
+
## Migrations
|
|
137
|
+
|
|
138
|
+
Schema Diff compares entity metadata against the live database and auto-generates migration code:
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
const diff = await SchemaDiff.compare(em, [User, Post]);
|
|
142
|
+
const migration = new SchemaDiffMigrationGenerator().generate(diff, "mysql");
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Additional Features
|
|
146
|
+
|
|
147
|
+
- **Cursor Pagination** — `findWithCursor()` for consistent performance on large datasets
|
|
148
|
+
- **Upsert** — `INSERT ON CONFLICT` / `ON DUPLICATE KEY UPDATE` with native driver syntax
|
|
149
|
+
- **N+1 Detection** — QueryTracker warns on repeated single-row queries and slow queries
|
|
150
|
+
- **Aggregation** — `count`, `sum`, `avg`, `min`, `max` with optional WHERE
|
|
151
|
+
- **Lifecycle Hooks** — `@BeforeInsert`, `@AfterInsert`, `@BeforeUpdate`, `@AfterUpdate`, `@BeforeDelete`, `@AfterDelete`
|
|
152
|
+
- **EntitySubscriber** — event-driven observation pattern per entity class
|
|
153
|
+
- **Validation** — `@NotNull`, `@MinLength`, `@MaxLength`, `@Min`, `@Max`
|
|
154
|
+
- **Connection Pooling** — configurable pool size, acquire/idle timeouts
|
|
155
|
+
- **Retry & Timeout** — exponential backoff on connection failure, per-query timeout
|
|
156
|
+
- **EXPLAIN** — query execution plan analysis
|
|
157
|
+
- **NestJS Integration** — `@InjectRepository`, `@InjectEntityManager`, module system
|
|
59
158
|
|
|
60
159
|
## Databases
|
|
61
160
|
|
|
62
|
-
|
|
161
|
+
| | MySQL | PostgreSQL | SQLite |
|
|
162
|
+
| ---------------- | ----- | ---------- | ------ |
|
|
163
|
+
| CRUD | ✓ | ✓ | ✓ |
|
|
164
|
+
| Transactions | ✓ | ✓ | ✓ |
|
|
165
|
+
| Schema Sync | ✓ | ✓ | ✓ |
|
|
166
|
+
| Migrations | ✓ | ✓ | ✓ |
|
|
167
|
+
| ENUM | ✓ | ✓ (native) | — |
|
|
168
|
+
| Schema Isolation | — | ✓ | — |
|
|
169
|
+
| Read Replica | ✓ | ✓ | — |
|
|
63
170
|
|
|
64
|
-
##
|
|
171
|
+
## Test Status
|
|
65
172
|
|
|
66
|
-
|
|
173
|
+
- **1,405** unit tests passed, 0 failures
|
|
174
|
+
- **4** example projects type-checked (nestjs-cats, nestjs-blog, nestjs-multitenant, nestjs-todo)
|
|
175
|
+
- **6** SQL injection vectors patched and audited across all drivers
|
|
67
176
|
|
|
68
|
-
##
|
|
177
|
+
## Documentation
|
|
69
178
|
|
|
70
|
-
[
|
|
179
|
+
Full documentation available at [docs/](https://github.com/biud436/stingerloom-orm/tree/main/docs).
|
|
@@ -56,6 +56,8 @@ export declare class EntityManager implements BaseEntityManager {
|
|
|
56
56
|
getNameStrategy<T>(clazz: ClazzType<T>): string;
|
|
57
57
|
private resolveEntityMetadata;
|
|
58
58
|
private getDeletedAtColumn;
|
|
59
|
+
private getCreateTimestampColumn;
|
|
60
|
+
private getUpdateTimestampColumn;
|
|
59
61
|
private runHooks;
|
|
60
62
|
private createProxy;
|
|
61
63
|
private registerEntities;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EntityManager.d.ts","sourceRoot":"","sources":["../../src/core/EntityManager.ts"],"names":[],"mappings":"AACA,OAAO,kBAAkB,CAAC;AAC1B,OAAO,EAAE,SAAS,EAA0B,MAAM,UAAU,CAAC;AAW7D,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAInD,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AASnD,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAMjE,OAAY,EAAE,GAAG,EAAa,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"EntityManager.d.ts","sourceRoot":"","sources":["../../src/core/EntityManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -275,6 +275,14 @@ class EntityManager {
|
|
|
275
275
|
const column = Reflect.getMetadata(decorators_1.DELETED_AT_TOKEN, entity);
|
|
276
276
|
return column ?? null;
|
|
277
277
|
}
|
|
278
|
+
getCreateTimestampColumn(entity) {
|
|
279
|
+
const column = Reflect.getMetadata(decorators_1.CREATE_TIMESTAMP_TOKEN, entity);
|
|
280
|
+
return column ?? null;
|
|
281
|
+
}
|
|
282
|
+
getUpdateTimestampColumn(entity) {
|
|
283
|
+
const column = Reflect.getMetadata(decorators_1.UPDATE_TIMESTAMP_TOKEN, entity);
|
|
284
|
+
return column ?? null;
|
|
285
|
+
}
|
|
278
286
|
async runHooks(entity, item, event) {
|
|
279
287
|
const hooks = Reflect.getMetadata(decorators_1.HOOK_TOKEN, entity);
|
|
280
288
|
if (!hooks || hooks.length === 0)
|
|
@@ -1486,6 +1494,21 @@ class EntityManager {
|
|
|
1486
1494
|
const values = insertableColumns.map((column) => {
|
|
1487
1495
|
return item[column.name];
|
|
1488
1496
|
});
|
|
1497
|
+
const now = new Date().toISOString();
|
|
1498
|
+
const createTsCol = this.getCreateTimestampColumn(entity);
|
|
1499
|
+
if (createTsCol) {
|
|
1500
|
+
const idx = insertableColumns.findIndex((col) => col.name === createTsCol);
|
|
1501
|
+
if (idx >= 0) {
|
|
1502
|
+
values[idx] = item[createTsCol]?.toISOString?.() ?? now;
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
const updateTsCol = this.getUpdateTimestampColumn(entity);
|
|
1506
|
+
if (updateTsCol) {
|
|
1507
|
+
const idx = insertableColumns.findIndex((col) => col.name === updateTsCol);
|
|
1508
|
+
if (idx >= 0) {
|
|
1509
|
+
values[idx] = item[updateTsCol]?.toISOString?.() ?? now;
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1489
1512
|
const manyToOneRelations = this.resolveManyToOneMetadata(entity);
|
|
1490
1513
|
for (const rel of manyToOneRelations) {
|
|
1491
1514
|
if (!rel.joinColumn)
|
|
@@ -1595,6 +1618,18 @@ class EntityManager {
|
|
|
1595
1618
|
const updateMap = updatableColumns.map((column) => {
|
|
1596
1619
|
return (0, sql_template_tag_1.default) `${(0, sql_template_tag_1.raw)(this.wrap(column.name))} = ${item[column.name]}`;
|
|
1597
1620
|
});
|
|
1621
|
+
const updateTsColName = this.getUpdateTimestampColumn(entity);
|
|
1622
|
+
if (updateTsColName) {
|
|
1623
|
+
const existingIdx = updatableColumns.findIndex((col) => col.name === updateTsColName);
|
|
1624
|
+
const updateNow = new Date().toISOString();
|
|
1625
|
+
if (existingIdx >= 0) {
|
|
1626
|
+
updateMap[existingIdx] =
|
|
1627
|
+
(0, sql_template_tag_1.default) `${(0, sql_template_tag_1.raw)(this.wrap(updateTsColName))} = ${updateNow}`;
|
|
1628
|
+
}
|
|
1629
|
+
else {
|
|
1630
|
+
updateMap.push((0, sql_template_tag_1.default) `${(0, sql_template_tag_1.raw)(this.wrap(updateTsColName))} = ${updateNow}`);
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1598
1633
|
const updatedColumnNames = new Set(updatableColumns.map((col) => col.name));
|
|
1599
1634
|
const updateManyToOneRelations = this.resolveManyToOneMetadata(entity);
|
|
1600
1635
|
for (const rel of updateManyToOneRelations) {
|