@stingerloom/orm 0.1.1 → 0.2.0

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 CHANGED
@@ -2,6 +2,7 @@
2
2
  <p align="center">Decorator-driven TypeScript ORM with multi-tenancy support.</p>
3
3
 
4
4
  <p align="center">
5
+ <a href="https://www.npmjs.com/package/@stingerloom/orm"><img src="https://img.shields.io/npm/v/@stingerloom/orm" alt="npm" /></a>
5
6
  <img src="https://img.shields.io/badge/tests-1%2C405%20passed-brightgreen" alt="Tests" />
6
7
  <img src="https://img.shields.io/badge/license-MIT-blue" alt="License" />
7
8
  <img src="https://img.shields.io/badge/TypeScript-strict-3178C6?logo=typescript&logoColor=white" alt="TypeScript" />
@@ -9,45 +10,170 @@
9
10
 
10
11
  ---
11
12
 
13
+ Stingerloom ORM is a decorator-driven TypeScript ORM designed for type-safe database access with first-class multi-tenancy support.
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ npm install @stingerloom/orm reflect-metadata
19
+ ```
20
+
21
+ Install only the driver you need:
22
+
23
+ ```bash
24
+ npm install mysql2 # MySQL / MariaDB
25
+ npm install pg # PostgreSQL
26
+ npm install better-sqlite3 # SQLite
27
+ ```
28
+
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`.
32
+
12
33
  ```typescript
13
34
  @Entity()
14
- class User {
15
- @PrimaryGeneratedColumn() id!: number;
16
- @Column() name!: string;
17
- @Column() email!: string;
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
+ }
18
55
  }
56
+ ```
57
+
58
+ ## CRUD with EntityManager
19
59
 
60
+ ```typescript
20
61
  const em = new EntityManager();
21
- await em.register({ type: "postgres", entities: [User], synchronize: true });
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:
92
+
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:
22
118
 
23
- await em.save(User, { name: "Alice", email: "alice@example.com" });
24
- await em.find(User, { where: { name: "Alice" } });
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
+ }
134
+ ```
135
+
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");
25
143
  ```
26
144
 
27
- ## Features
145
+ ## Additional Features
28
146
 
29
- - CRUD
30
- - Relations (ManyToOne, OneToMany, ManyToMany, OneToOne)
31
- - Eager & Lazy Loading
32
- - Transactions
33
- - Migrations
34
- - Query Builder
35
- - Multi-tenancy
36
- - Soft Delete
37
- - Upsert
38
- - Cursor Pagination
39
- - N+1 Detection
40
- - EntitySubscriber
41
- - NestJS Integration
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
42
158
 
43
159
  ## Databases
44
160
 
45
- PostgreSQL · MySQL · SQLite
161
+ | | MySQL | PostgreSQL | SQLite |
162
+ | ---------------- | ----- | ---------- | ------ |
163
+ | CRUD | ✓ | ✓ | ✓ |
164
+ | Transactions | ✓ | ✓ | ✓ |
165
+ | Schema Sync | ✓ | ✓ | ✓ |
166
+ | Migrations | ✓ | ✓ | ✓ |
167
+ | ENUM | ✓ | ✓ (native) | — |
168
+ | Schema Isolation | — | ✓ | — |
169
+ | Read Replica | ✓ | ✓ | — |
46
170
 
47
- ## Docs
171
+ ## Test Status
48
172
 
49
- See **[docs/](./docs/README.md)** for full documentation.
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
50
176
 
51
- ## License
177
+ ## Documentation
52
178
 
53
- [MIT](./LICENSE)
179
+ Full documentation available at [docs/](https://github.com/biud436/stingerloom-orm/tree/main/docs).
@@ -0,0 +1,5 @@
1
+ export { StinglerloomOrmModule, makeInjectRepositoryToken, STINGERLOOM_ORM_OPTION_TOKEN, INJECT_REPOSITORIES_TOKEN, } from "./stingerloom-orm.module";
2
+ export { StingerloomOrmCoreModule } from "./stingerloom-orm-core.module";
3
+ export { StinglerloomOrmService, STINGERLOOM_ORM_SERVICE_TOKEN, } from "./stingerloom-orm.service";
4
+ export { InjectRepository } from "./inject-repository.decorator";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/integration/nestjs/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,yBAAyB,EACzB,4BAA4B,EAC5B,yBAAyB,GAC1B,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AACzE,OAAO,EACL,sBAAsB,EACtB,6BAA6B,GAC9B,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC"}
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.InjectRepository = exports.STINGERLOOM_ORM_SERVICE_TOKEN = exports.StinglerloomOrmService = exports.StingerloomOrmCoreModule = exports.INJECT_REPOSITORIES_TOKEN = exports.STINGERLOOM_ORM_OPTION_TOKEN = exports.makeInjectRepositoryToken = exports.StinglerloomOrmModule = void 0;
4
+ var stingerloom_orm_module_1 = require("./stingerloom-orm.module");
5
+ Object.defineProperty(exports, "StinglerloomOrmModule", { enumerable: true, get: function () { return stingerloom_orm_module_1.StinglerloomOrmModule; } });
6
+ Object.defineProperty(exports, "makeInjectRepositoryToken", { enumerable: true, get: function () { return stingerloom_orm_module_1.makeInjectRepositoryToken; } });
7
+ Object.defineProperty(exports, "STINGERLOOM_ORM_OPTION_TOKEN", { enumerable: true, get: function () { return stingerloom_orm_module_1.STINGERLOOM_ORM_OPTION_TOKEN; } });
8
+ Object.defineProperty(exports, "INJECT_REPOSITORIES_TOKEN", { enumerable: true, get: function () { return stingerloom_orm_module_1.INJECT_REPOSITORIES_TOKEN; } });
9
+ var stingerloom_orm_core_module_1 = require("./stingerloom-orm-core.module");
10
+ Object.defineProperty(exports, "StingerloomOrmCoreModule", { enumerable: true, get: function () { return stingerloom_orm_core_module_1.StingerloomOrmCoreModule; } });
11
+ var stingerloom_orm_service_1 = require("./stingerloom-orm.service");
12
+ Object.defineProperty(exports, "StinglerloomOrmService", { enumerable: true, get: function () { return stingerloom_orm_service_1.StinglerloomOrmService; } });
13
+ Object.defineProperty(exports, "STINGERLOOM_ORM_SERVICE_TOKEN", { enumerable: true, get: function () { return stingerloom_orm_service_1.STINGERLOOM_ORM_SERVICE_TOKEN; } });
14
+ var inject_repository_decorator_1 = require("./inject-repository.decorator");
15
+ Object.defineProperty(exports, "InjectRepository", { enumerable: true, get: function () { return inject_repository_decorator_1.InjectRepository; } });
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/integration/nestjs/index.ts"],"names":[],"mappings":";;;AAAA,mEAKkC;AAJhC,+HAAA,qBAAqB,OAAA;AACrB,mIAAA,yBAAyB,OAAA;AACzB,sIAAA,4BAA4B,OAAA;AAC5B,mIAAA,yBAAyB,OAAA;AAE3B,6EAAyE;AAAhE,uIAAA,wBAAwB,OAAA;AACjC,qEAGmC;AAFjC,iIAAA,sBAAsB,OAAA;AACtB,wIAAA,6BAA6B,OAAA;AAE/B,6EAAiE;AAAxD,+HAAA,gBAAgB,OAAA"}
@@ -0,0 +1,3 @@
1
+ import type { ClazzType } from "../../utils/types";
2
+ export declare const InjectRepository: (entity: ClazzType<unknown>) => ParameterDecorator;
3
+ //# sourceMappingURL=inject-repository.decorator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inject-repository.decorator.d.ts","sourceRoot":"","sources":["../../../src/integration/nestjs/inject-repository.decorator.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAEnD,eAAO,MAAM,gBAAgB,GAC3B,QAAQ,SAAS,CAAC,OAAO,CAAC,KACzB,kBAEF,CAAC"}
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.InjectRepository = void 0;
4
+ const common_1 = require("@nestjs/common");
5
+ const stingerloom_orm_module_1 = require("./stingerloom-orm.module");
6
+ const InjectRepository = (entity) => {
7
+ return (0, common_1.Inject)((0, stingerloom_orm_module_1.makeInjectRepositoryToken)(entity));
8
+ };
9
+ exports.InjectRepository = InjectRepository;
10
+ //# sourceMappingURL=inject-repository.decorator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inject-repository.decorator.js","sourceRoot":"","sources":["../../../src/integration/nestjs/inject-repository.decorator.ts"],"names":[],"mappings":";;;AAAA,2CAAwC;AACxC,qEAAqE;AAG9D,MAAM,gBAAgB,GAAG,CAC9B,MAA0B,EACN,EAAE;IACtB,OAAO,IAAA,eAAM,EAAC,IAAA,kDAAyB,EAAC,MAAM,CAAC,CAAC,CAAC;AACnD,CAAC,CAAC;AAJW,QAAA,gBAAgB,oBAI3B"}
@@ -0,0 +1,6 @@
1
+ import { DynamicModule } from "@nestjs/common";
2
+ import type { DatabaseClientOptions } from "../../core/DatabaseClientOptions";
3
+ export declare class StingerloomOrmCoreModule {
4
+ static forRoot(options: DatabaseClientOptions): DynamicModule;
5
+ }
6
+ //# sourceMappingURL=stingerloom-orm-core.module.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stingerloom-orm-core.module.d.ts","sourceRoot":"","sources":["../../../src/integration/nestjs/stingerloom-orm-core.module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAU,MAAM,gBAAgB,CAAC;AAEvD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AAE9E,qBACa,wBAAwB;IACnC,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,qBAAqB,GAAG,aAAa;CAgB9D"}
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var StingerloomOrmCoreModule_1;
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.StingerloomOrmCoreModule = void 0;
11
+ const common_1 = require("@nestjs/common");
12
+ const EntityManager_1 = require("../../core/EntityManager");
13
+ let StingerloomOrmCoreModule = StingerloomOrmCoreModule_1 = class StingerloomOrmCoreModule {
14
+ static forRoot(options) {
15
+ return {
16
+ module: StingerloomOrmCoreModule_1,
17
+ providers: [
18
+ {
19
+ provide: EntityManager_1.EntityManager,
20
+ useFactory: async () => {
21
+ const em = new EntityManager_1.EntityManager();
22
+ await em.register(options);
23
+ return em;
24
+ },
25
+ },
26
+ ],
27
+ exports: [EntityManager_1.EntityManager],
28
+ };
29
+ }
30
+ };
31
+ exports.StingerloomOrmCoreModule = StingerloomOrmCoreModule;
32
+ exports.StingerloomOrmCoreModule = StingerloomOrmCoreModule = StingerloomOrmCoreModule_1 = __decorate([
33
+ (0, common_1.Module)({})
34
+ ], StingerloomOrmCoreModule);
35
+ //# sourceMappingURL=stingerloom-orm-core.module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stingerloom-orm-core.module.js","sourceRoot":"","sources":["../../../src/integration/nestjs/stingerloom-orm-core.module.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,2CAAuD;AACvD,4DAAyD;AAIlD,IAAM,wBAAwB,gCAA9B,MAAM,wBAAwB;IACnC,MAAM,CAAC,OAAO,CAAC,OAA8B;QAC3C,OAAO;YACL,MAAM,EAAE,0BAAwB;YAChC,SAAS,EAAE;gBACT;oBACE,OAAO,EAAE,6BAAa;oBACtB,UAAU,EAAE,KAAK,IAAI,EAAE;wBACrB,MAAM,EAAE,GAAG,IAAI,6BAAa,EAAE,CAAC;wBAC/B,MAAM,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;wBAC3B,OAAO,EAAE,CAAC;oBACZ,CAAC;iBACF;aACF;YACD,OAAO,EAAE,CAAC,6BAAa,CAAC;SACzB,CAAC;IACJ,CAAC;CACF,CAAA;AAjBY,4DAAwB;mCAAxB,wBAAwB;IADpC,IAAA,eAAM,EAAC,EAAE,CAAC;GACE,wBAAwB,CAiBpC"}
@@ -0,0 +1,11 @@
1
+ import { DynamicModule } from "@nestjs/common";
2
+ import type { ClazzType } from "../../utils/types";
3
+ import type { DatabaseClientOptions } from "../../core/DatabaseClientOptions";
4
+ export declare const STINGERLOOM_ORM_OPTION_TOKEN: unique symbol;
5
+ export declare const INJECT_REPOSITORIES_TOKEN = "INJECT_REPOSITORIES_TOKEN";
6
+ export declare function makeInjectRepositoryToken(entity: ClazzType<unknown>): symbol;
7
+ export declare class StinglerloomOrmModule {
8
+ static forFeature(entities: ClazzType<unknown>[]): DynamicModule;
9
+ static forRoot(options: DatabaseClientOptions): DynamicModule;
10
+ }
11
+ //# sourceMappingURL=stingerloom-orm.module.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stingerloom-orm.module.d.ts","sourceRoot":"","sources":["../../../src/integration/nestjs/stingerloom-orm.module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAU,MAAM,gBAAgB,CAAC;AAMvD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AAG9E,eAAO,MAAM,4BAA4B,eAExC,CAAC;AACF,eAAO,MAAM,yBAAyB,8BAA8B,CAAC;AAIrE,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,SAAS,CAAC,OAAO,CAAC,GAAG,MAAM,CAS5E;AAED,qBACa,qBAAqB;IAChC,MAAM,CAAC,UAAU,CAAC,QAAQ,EAAE,SAAS,CAAC,OAAO,CAAC,EAAE,GAAG,aAAa;IAgBhE,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,qBAAqB,GAAG,aAAa;CAiB9D"}
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var StinglerloomOrmModule_1;
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.StinglerloomOrmModule = exports.INJECT_REPOSITORIES_TOKEN = exports.STINGERLOOM_ORM_OPTION_TOKEN = void 0;
11
+ exports.makeInjectRepositoryToken = makeInjectRepositoryToken;
12
+ const common_1 = require("@nestjs/common");
13
+ const stingerloom_orm_service_1 = require("./stingerloom-orm.service");
14
+ const EntityManager_1 = require("../../core/EntityManager");
15
+ const stingerloom_orm_core_module_1 = require("./stingerloom-orm-core.module");
16
+ exports.STINGERLOOM_ORM_OPTION_TOKEN = Symbol.for("STINGERLOOM_ORM_OPTION_TOKEN");
17
+ exports.INJECT_REPOSITORIES_TOKEN = "INJECT_REPOSITORIES_TOKEN";
18
+ const repositoryTokenCache = new WeakMap();
19
+ function makeInjectRepositoryToken(entity) {
20
+ let token = repositoryTokenCache.get(entity);
21
+ if (!token) {
22
+ token = Symbol(`${exports.INJECT_REPOSITORIES_TOKEN}_${entity.name}`);
23
+ repositoryTokenCache.set(entity, token);
24
+ }
25
+ return token;
26
+ }
27
+ let StinglerloomOrmModule = StinglerloomOrmModule_1 = class StinglerloomOrmModule {
28
+ static forFeature(entities) {
29
+ const providers = entities.map((entity) => ({
30
+ provide: makeInjectRepositoryToken(entity),
31
+ useFactory: (entityManager) => {
32
+ return entityManager.getRepository(entity);
33
+ },
34
+ inject: [EntityManager_1.EntityManager],
35
+ }));
36
+ return {
37
+ module: StinglerloomOrmModule_1,
38
+ providers: [...providers],
39
+ exports: providers,
40
+ };
41
+ }
42
+ static forRoot(options) {
43
+ Reflect.defineMetadata(exports.STINGERLOOM_ORM_OPTION_TOKEN, options, StinglerloomOrmModule_1);
44
+ stingerloom_orm_service_1.StinglerloomOrmService.captured[stingerloom_orm_service_1.STINGERLOOM_ORM_SERVICE_TOKEN] = true;
45
+ return {
46
+ module: StinglerloomOrmModule_1,
47
+ imports: [stingerloom_orm_core_module_1.StingerloomOrmCoreModule.forRoot(options)],
48
+ providers: [stingerloom_orm_service_1.StinglerloomOrmService],
49
+ exports: [stingerloom_orm_service_1.StinglerloomOrmService, stingerloom_orm_core_module_1.StingerloomOrmCoreModule],
50
+ global: true,
51
+ };
52
+ }
53
+ };
54
+ exports.StinglerloomOrmModule = StinglerloomOrmModule;
55
+ exports.StinglerloomOrmModule = StinglerloomOrmModule = StinglerloomOrmModule_1 = __decorate([
56
+ (0, common_1.Module)({})
57
+ ], StinglerloomOrmModule);
58
+ //# sourceMappingURL=stingerloom-orm.module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stingerloom-orm.module.js","sourceRoot":"","sources":["../../../src/integration/nestjs/stingerloom-orm.module.ts"],"names":[],"mappings":";;;;;;;;;;AAiBA,8DASC;AA1BD,2CAAuD;AACvD,uEAGmC;AACnC,4DAAyD;AAGzD,+EAAyE;AAE5D,QAAA,4BAA4B,GAAG,MAAM,CAAC,GAAG,CACpD,8BAA8B,CAC/B,CAAC;AACW,QAAA,yBAAyB,GAAG,2BAA2B,CAAC;AAErE,MAAM,oBAAoB,GAAG,IAAI,OAAO,EAA8B,CAAC;AAEvE,SAAgB,yBAAyB,CAAC,MAA0B;IAClE,IAAI,KAAK,GAAG,oBAAoB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAE7C,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,KAAK,GAAG,MAAM,CAAC,GAAG,iCAAyB,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;QAC9D,oBAAoB,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC1C,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAGM,IAAM,qBAAqB,6BAA3B,MAAM,qBAAqB;IAChC,MAAM,CAAC,UAAU,CAAC,QAA8B;QAC9C,MAAM,SAAS,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;YAC1C,OAAO,EAAE,yBAAyB,CAAC,MAAM,CAAC;YAC1C,UAAU,EAAE,CAAC,aAA4B,EAAE,EAAE;gBAC3C,OAAO,aAAa,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;YAC7C,CAAC;YACD,MAAM,EAAE,CAAC,6BAAa,CAAC;SACxB,CAAC,CAAC,CAAC;QAEJ,OAAO;YACL,MAAM,EAAE,uBAAqB;YAC7B,SAAS,EAAE,CAAC,GAAG,SAAS,CAAC;YACzB,OAAO,EAAE,SAAS;SACnB,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,OAAO,CAAC,OAA8B;QAC3C,OAAO,CAAC,cAAc,CACpB,oCAA4B,EAC5B,OAAO,EACP,uBAAqB,CACtB,CAAC;QAEF,gDAAsB,CAAC,QAAQ,CAAC,uDAA6B,CAAC,GAAG,IAAI,CAAC;QAEtE,OAAO;YACL,MAAM,EAAE,uBAAqB;YAC7B,OAAO,EAAE,CAAC,sDAAwB,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YACpD,SAAS,EAAE,CAAC,gDAAsB,CAAC;YACnC,OAAO,EAAE,CAAC,gDAAsB,EAAE,sDAAwB,CAAC;YAC3D,MAAM,EAAE,IAAI;SACb,CAAC;IACJ,CAAC;CACF,CAAA;AAlCY,sDAAqB;gCAArB,qBAAqB;IADjC,IAAA,eAAM,EAAC,EAAE,CAAC;GACE,qBAAqB,CAkCjC"}
@@ -0,0 +1,18 @@
1
+ import "reflect-metadata";
2
+ import { OnModuleInit, OnApplicationShutdown } from "@nestjs/common";
3
+ import { EntityManager } from "../../core/EntityManager";
4
+ import type { ClazzType } from "../../utils/types";
5
+ export declare const STINGERLOOM_ORM_SERVICE_TOKEN: unique symbol;
6
+ export declare class StinglerloomOrmService implements OnModuleInit, OnApplicationShutdown {
7
+ private readonly entityManager;
8
+ private readonly logger;
9
+ static captured: Record<typeof STINGERLOOM_ORM_SERVICE_TOKEN, boolean>;
10
+ constructor(entityManager: EntityManager);
11
+ onModuleInit(): Promise<void>;
12
+ onApplicationShutdown(): Promise<void>;
13
+ private initEntityManager;
14
+ private propagateShutdown;
15
+ getRepository<T>(entity: ClazzType<T>): import("../..").BaseRepository<T>;
16
+ getEntityManager(): EntityManager;
17
+ }
18
+ //# sourceMappingURL=stingerloom-orm.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stingerloom-orm.service.d.ts","sourceRoot":"","sources":["../../../src/integration/nestjs/stingerloom-orm.service.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAC1B,OAAO,EAEL,YAAY,EACZ,qBAAqB,EACtB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAEzD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAGnD,eAAO,MAAM,6BAA6B,eAEzC,CAAC;AAEF,qBACa,sBACX,YAAW,YAAY,EAAE,qBAAqB;IASlC,OAAO,CAAC,QAAQ,CAAC,aAAa;IAP1C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA2C;IAElE,OAAc,QAAQ,EAAS,MAAM,CACnC,OAAO,6BAA6B,EACpC,OAAO,CACR,CAAC;gBAE2B,aAAa,EAAE,aAAa;IAInD,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAa7B,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC;YAK9B,iBAAiB;YAMjB,iBAAiB;IAM/B,aAAa,CAAC,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;IASrC,gBAAgB,IAAI,aAAa;CAQlC"}
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ var StinglerloomOrmService_1;
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.StinglerloomOrmService = exports.STINGERLOOM_ORM_SERVICE_TOKEN = void 0;
17
+ require("reflect-metadata");
18
+ const common_1 = require("@nestjs/common");
19
+ const EntityManager_1 = require("../../core/EntityManager");
20
+ const Logger_1 = require("../../utils/Logger");
21
+ const typedi_1 = __importDefault(require("typedi"));
22
+ exports.STINGERLOOM_ORM_SERVICE_TOKEN = Symbol.for("STINGERLOOM_ORM_SERVICE_TOKEN");
23
+ let StinglerloomOrmService = StinglerloomOrmService_1 = class StinglerloomOrmService {
24
+ constructor(entityManager) {
25
+ this.entityManager = entityManager;
26
+ this.logger = new Logger_1.Logger(StinglerloomOrmService_1.name);
27
+ this.logger.info("StinglerloomOrmService initialized");
28
+ }
29
+ async onModuleInit() {
30
+ this.logger.info("StinglerloomOrmService OnModuleInit");
31
+ if (!StinglerloomOrmService_1.captured[exports.STINGERLOOM_ORM_SERVICE_TOKEN]) {
32
+ this.logger.warn("StinglerloomOrmModule.forRoot() was not called");
33
+ return;
34
+ }
35
+ await this.initEntityManager();
36
+ }
37
+ async onApplicationShutdown() {
38
+ await this.propagateShutdown();
39
+ this.logger.info("Stingerloom ORM disconnected");
40
+ }
41
+ async initEntityManager() {
42
+ if (!typedi_1.default.has(EntityManager_1.EntityManager)) {
43
+ typedi_1.default.set(EntityManager_1.EntityManager, this.entityManager);
44
+ }
45
+ }
46
+ async propagateShutdown() {
47
+ if (this.entityManager) {
48
+ await this.entityManager.propagateShutdown();
49
+ }
50
+ }
51
+ getRepository(entity) {
52
+ if (!this.entityManager) {
53
+ throw new Error("EntityManager not initialized. Database connection may not be ready.");
54
+ }
55
+ return this.entityManager.getRepository(entity);
56
+ }
57
+ getEntityManager() {
58
+ if (!this.entityManager) {
59
+ throw new Error("EntityManager not initialized. Database connection may not be ready.");
60
+ }
61
+ return this.entityManager;
62
+ }
63
+ };
64
+ exports.StinglerloomOrmService = StinglerloomOrmService;
65
+ StinglerloomOrmService.captured = {};
66
+ exports.StinglerloomOrmService = StinglerloomOrmService = StinglerloomOrmService_1 = __decorate([
67
+ (0, common_1.Injectable)(),
68
+ __metadata("design:paramtypes", [EntityManager_1.EntityManager])
69
+ ], StinglerloomOrmService);
70
+ //# sourceMappingURL=stingerloom-orm.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stingerloom-orm.service.js","sourceRoot":"","sources":["../../../src/integration/nestjs/stingerloom-orm.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,4BAA0B;AAC1B,2CAIwB;AACxB,4DAAyD;AACzD,+CAA4C;AAE5C,oDAA+B;AAElB,QAAA,6BAA6B,GAAG,MAAM,CAAC,GAAG,CACrD,+BAA+B,CAChC,CAAC;AAGK,IAAM,sBAAsB,8BAA5B,MAAM,sBAAsB;IAUjC,YAA6B,aAA4B;QAA5B,kBAAa,GAAb,aAAa,CAAe;QAPxC,WAAM,GAAG,IAAI,eAAM,CAAC,wBAAsB,CAAC,IAAI,CAAC,CAAC;QAQhE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAC;IACzD,CAAC;IAED,KAAK,CAAC,YAAY;QAChB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,qCAAqC,CAAC,CAAC;QAExD,IACE,CAAC,wBAAsB,CAAC,QAAQ,CAAC,qCAA6B,CAAC,EAC/D,CAAC;YACD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,gDAAgD,CAAC,CAAC;YACnE,OAAO;QACT,CAAC;QAED,MAAM,IAAI,CAAC,iBAAiB,EAAE,CAAC;IACjC,CAAC;IAED,KAAK,CAAC,qBAAqB;QACzB,MAAM,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAC/B,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC;IACnD,CAAC;IAEO,KAAK,CAAC,iBAAiB;QAC7B,IAAI,CAAC,gBAAS,CAAC,GAAG,CAAC,6BAAa,CAAC,EAAE,CAAC;YAClC,gBAAS,CAAC,GAAG,CAAC,6BAAa,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,iBAAiB;QAC7B,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,MAAM,IAAI,CAAC,aAAa,CAAC,iBAAiB,EAAE,CAAC;QAC/C,CAAC;IACH,CAAC;IAED,aAAa,CAAI,MAAoB;QACnC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CACb,sEAAsE,CACvE,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC,aAAa,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;IAClD,CAAC;IAED,gBAAgB;QACd,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CACb,sEAAsE,CACvE,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC,aAAa,CAAC;IAC5B,CAAC;;AA5DU,wDAAsB;AAKnB,+BAAQ,GAAG,EAGxB,AAHqB,CAGpB;iCARS,sBAAsB;IADlC,IAAA,mBAAU,GAAE;qCAWiC,6BAAa;GAV9C,sBAAsB,CA6DlC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stingerloom/orm",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "A standalone, framework-agnostic TypeScript ORM that can be used with any Node.js framework",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -8,6 +8,17 @@
8
8
  ".": {
9
9
  "types": "./dist/index.d.ts",
10
10
  "require": "./dist/index.js"
11
+ },
12
+ "./nestjs": {
13
+ "types": "./dist/integration/nestjs/index.d.ts",
14
+ "require": "./dist/integration/nestjs/index.js"
15
+ }
16
+ },
17
+ "typesVersions": {
18
+ "*": {
19
+ "nestjs": [
20
+ "dist/integration/nestjs/index.d.ts"
21
+ ]
11
22
  }
12
23
  },
13
24
  "files": [
@@ -32,21 +43,18 @@
32
43
  "multi-tenancy",
33
44
  "stingerloom"
34
45
  ],
35
- "scripts": {
36
- "build": "rimraf dist && tsc",
37
- "prepublishOnly": "pnpm run build",
38
- "test": "jest"
39
- },
40
46
  "dependencies": {
41
47
  "class-transformer": "^0.5.1",
42
48
  "sql-template-tag": "^4.0.0",
43
49
  "typedi": "^0.10.0"
44
50
  },
45
51
  "peerDependencies": {
46
- "reflect-metadata": ">=0.1.0",
52
+ "@nestjs/common": ">=8.0.0",
53
+ "@nestjs/core": ">=8.0.0",
54
+ "better-sqlite3": ">=9.0.0",
47
55
  "mysql2": "^3.0.0",
48
56
  "pg": "^8.0.0",
49
- "better-sqlite3": ">=9.0.0"
57
+ "reflect-metadata": ">=0.1.0"
50
58
  },
51
59
  "peerDependenciesMeta": {
52
60
  "mysql2": {
@@ -57,9 +65,17 @@
57
65
  },
58
66
  "better-sqlite3": {
59
67
  "optional": true
68
+ },
69
+ "@nestjs/common": {
70
+ "optional": true
71
+ },
72
+ "@nestjs/core": {
73
+ "optional": true
60
74
  }
61
75
  },
62
76
  "devDependencies": {
77
+ "@nestjs/common": "^10.4.22",
78
+ "@nestjs/core": "^10.4.22",
63
79
  "@types/better-sqlite3": "^7.6.13",
64
80
  "@types/jest": "^29.5.4",
65
81
  "@types/node": "^20.4.8",
@@ -70,6 +86,7 @@
70
86
  "pg": "^8.18.0",
71
87
  "reflect-metadata": "^0.2.2",
72
88
  "rimraf": "^6.1.2",
89
+ "rxjs": "^7.8.2",
73
90
  "ts-jest": "^29.1.1",
74
91
  "typescript": "^5.6.2"
75
92
  },
@@ -84,9 +101,8 @@
84
101
  "publishConfig": {
85
102
  "access": "public"
86
103
  },
87
- "pnpm": {
88
- "onlyBuiltDependencies": [
89
- "better-sqlite3"
90
- ]
104
+ "scripts": {
105
+ "build": "rimraf dist && tsc",
106
+ "test": "jest"
91
107
  }
92
- }
108
+ }