@stingerloom/orm 0.1.2 → 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
@@ -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 # MySQL
23
- npm install pg # PostgreSQL
24
+ npm install mysql2 # MySQL / MariaDB
25
+ npm install pg # PostgreSQL
24
26
  npm install better-sqlite3 # SQLite
25
27
  ```
26
28
 
27
- ## Quick Start
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 User {
32
- @PrimaryGeneratedColumn() id!: number;
33
- @Column() name!: string;
34
- @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
+ }
35
55
  }
56
+ ```
57
+
58
+ ## CRUD with EntityManager
36
59
 
60
+ ```typescript
37
61
  const em = new EntityManager();
38
- 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:
39
92
 
40
- await em.save(User, { name: "Alice", email: "alice@example.com" });
41
- await em.find(User, { where: { name: "Alice" } });
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
- ## Features
45
-
46
- - CRUD
47
- - Relations (ManyToOne, OneToMany, ManyToMany, OneToOne)
48
- - Eager & Lazy Loading
49
- - Transactions
50
- - Migrations
51
- - Query Builder
52
- - Multi-tenancy
53
- - Soft Delete
54
- - Upsert
55
- - Cursor Pagination
56
- - N+1 Detection
57
- - EntitySubscriber
58
- - NestJS Integration
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
- 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 | ✓ | ✓ | — |
63
170
 
64
- ## Docs
171
+ ## Test Status
65
172
 
66
- 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
67
176
 
68
- ## License
177
+ ## Documentation
69
178
 
70
- [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.2",
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
+ }