@sentzunhat/zacatl 0.0.21 β 0.0.22
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 +5 -3
- package/build/service/architecture/infrastructure/orm/adapter-loader.d.ts +2 -2
- package/build/service/architecture/infrastructure/orm/adapter-loader.d.ts.map +1 -1
- package/build/service/architecture/infrastructure/orm/adapter-loader.js +8 -6
- package/build/service/architecture/infrastructure/orm/adapter-loader.js.map +1 -1
- package/build/service/architecture/infrastructure/repositories/abstract.d.ts +5 -1
- package/build/service/architecture/infrastructure/repositories/abstract.d.ts.map +1 -1
- package/build/service/architecture/infrastructure/repositories/abstract.js +34 -5
- package/build/service/architecture/infrastructure/repositories/abstract.js.map +1 -1
- package/build/test/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -2
- package/src/service/architecture/infrastructure/orm/adapter-loader.ts +14 -12
- package/src/service/architecture/infrastructure/repositories/abstract.ts +61 -14
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
[](https://opensource.org/licenses/MIT)
|
|
6
6
|
[](https://www.typescriptlang.org/)
|
|
7
7
|
[](https://nodejs.org/)
|
|
8
|
-
[](#testing)
|
|
9
9
|
[](#testing)
|
|
10
10
|
|
|
11
11
|
**Production-ready TypeScript framework for building scalable microservices, APIs, and distributed systems.**
|
|
@@ -21,7 +21,7 @@ Zacatl enforces clean, layered architecture with dependency injection, validatio
|
|
|
21
21
|
- **ποΈ Pluggable ORM Adapters** - Sequelize, Mongoose, or build your own
|
|
22
22
|
- **π Multi-Language Support** - Pluggable i18n with filesystem/memory adapters
|
|
23
23
|
- **π Adapter Pattern** - Easy integration with any database or service
|
|
24
|
-
- **π§ͺ Comprehensive Testing** -
|
|
24
|
+
- **π§ͺ Comprehensive Testing** - 169 tests, 79% coverage, Vitest
|
|
25
25
|
- **β‘ Runtime Detection** - Works with Node.js 22+ and Bun
|
|
26
26
|
- **π Production Ready** - Structured logging, monitoring, and error tracking
|
|
27
27
|
|
|
@@ -47,7 +47,9 @@ Zacatl is MIT-licensed (permissive). Please donβt use it to harm people.
|
|
|
47
47
|
npm install @sentzunhat/zacatl
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
## π¦ Import Shortcuts (v0.0.
|
|
50
|
+
## π¦ Import Shortcuts (v0.0.22+)
|
|
51
|
+
|
|
52
|
+
> **New in v0.0.22:** ESM runtime compatibility fix. Adapters now use dynamic `import()` instead of `require()`. [Migration Guide](./docs/migration/v0.0.22.md)
|
|
51
53
|
|
|
52
54
|
```typescript
|
|
53
55
|
// Short imports for better DX
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Model } from "sequelize";
|
|
2
2
|
import type { MongooseRepositoryConfig, SequelizeRepositoryConfig, ORMAdapter } from "../repositories/types";
|
|
3
|
-
export declare function loadMongooseAdapter<D, I, O>(config: MongooseRepositoryConfig<D>): ORMAdapter<D, I, O
|
|
4
|
-
export declare function loadSequelizeAdapter<D extends Model, I, O>(config: SequelizeRepositoryConfig<D>): ORMAdapter<D, I, O
|
|
3
|
+
export declare function loadMongooseAdapter<D, I, O>(config: MongooseRepositoryConfig<D>): Promise<ORMAdapter<D, I, O>>;
|
|
4
|
+
export declare function loadSequelizeAdapter<D extends Model, I, O>(config: SequelizeRepositoryConfig<D>): Promise<ORMAdapter<D, I, O>>;
|
|
5
5
|
//# sourceMappingURL=adapter-loader.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter-loader.d.ts","sourceRoot":"./src/","sources":["service/architecture/infrastructure/orm/adapter-loader.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,KAAK,EACV,wBAAwB,EACxB,yBAAyB,EACzB,UAAU,EACX,MAAM,uBAAuB,CAAC;AAM/B,
|
|
1
|
+
{"version":3,"file":"adapter-loader.d.ts","sourceRoot":"./src/","sources":["service/architecture/infrastructure/orm/adapter-loader.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,KAAK,EACV,wBAAwB,EACxB,yBAAyB,EACzB,UAAU,EACX,MAAM,uBAAuB,CAAC;AAM/B,wBAAsB,mBAAmB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAC/C,MAAM,EAAE,wBAAwB,CAAC,CAAC,CAAC,GAClC,OAAO,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAgB9B;AAMD,wBAAsB,oBAAoB,CAAC,CAAC,SAAS,KAAK,EAAE,CAAC,EAAE,CAAC,EAC9D,MAAM,EAAE,yBAAyB,CAAC,CAAC,CAAC,GACnC,OAAO,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAgB9B"}
|
|
@@ -1,22 +1,24 @@
|
|
|
1
|
-
export function loadMongooseAdapter(config) {
|
|
1
|
+
export async function loadMongooseAdapter(config) {
|
|
2
2
|
try {
|
|
3
|
-
const adapters =
|
|
3
|
+
const adapters = await import("./adapters/mongoose-adapter");
|
|
4
4
|
return new adapters.MongooseAdapter(config);
|
|
5
5
|
}
|
|
6
6
|
catch (error) {
|
|
7
|
-
if (error.code === "
|
|
7
|
+
if (error.code === "ERR_MODULE_NOT_FOUND" ||
|
|
8
|
+
error.code === "MODULE_NOT_FOUND") {
|
|
8
9
|
throw new Error("Mongoose is not installed. Install it with: npm install mongoose");
|
|
9
10
|
}
|
|
10
11
|
throw error;
|
|
11
12
|
}
|
|
12
13
|
}
|
|
13
|
-
export function loadSequelizeAdapter(config) {
|
|
14
|
+
export async function loadSequelizeAdapter(config) {
|
|
14
15
|
try {
|
|
15
|
-
const adapters =
|
|
16
|
+
const adapters = await import("./adapters/sequelize-adapter");
|
|
16
17
|
return new adapters.SequelizeAdapter(config);
|
|
17
18
|
}
|
|
18
19
|
catch (error) {
|
|
19
|
-
if (error.code === "
|
|
20
|
+
if (error.code === "ERR_MODULE_NOT_FOUND" ||
|
|
21
|
+
error.code === "MODULE_NOT_FOUND") {
|
|
20
22
|
throw new Error("Sequelize is not installed. Install it with: npm install sequelize");
|
|
21
23
|
}
|
|
22
24
|
throw error;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter-loader.js","sourceRoot":"./src/","sources":["service/architecture/infrastructure/orm/adapter-loader.ts"],"names":[],"mappings":"AAWA,MAAM,UAAU,mBAAmB,
|
|
1
|
+
{"version":3,"file":"adapter-loader.js","sourceRoot":"./src/","sources":["service/architecture/infrastructure/orm/adapter-loader.ts"],"names":[],"mappings":"AAWA,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,MAAmC;IAEnC,IAAI,CAAC;QAEH,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,6BAA6B,CAAC,CAAC;QAC7D,OAAO,IAAI,QAAQ,CAAC,eAAe,CAAU,MAAM,CAAC,CAAC;IACvD,CAAC;IAAC,OAAO,KAAU,EAAE,CAAC;QACpB,IACE,KAAK,CAAC,IAAI,KAAK,sBAAsB;YACrC,KAAK,CAAC,IAAI,KAAK,kBAAkB,EACjC,CAAC;YACD,MAAM,IAAI,KAAK,CACb,kEAAkE,CACnE,CAAC;QACJ,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAMD,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,MAAoC;IAEpC,IAAI,CAAC;QAEH,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,8BAA8B,CAAC,CAAC;QAC9D,OAAO,IAAI,QAAQ,CAAC,gBAAgB,CAAU,MAAM,CAAC,CAAC;IACxD,CAAC;IAAC,OAAO,KAAU,EAAE,CAAC;QACpB,IACE,KAAK,CAAC,IAAI,KAAK,sBAAsB;YACrC,KAAK,CAAC,IAAI,KAAK,kBAAkB,EACjC,CAAC;YACD,MAAM,IAAI,KAAK,CACb,oEAAoE,CACrE,CAAC;QACJ,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC","sourcesContent":["import type { Model } from \"sequelize\";\nimport type {\n MongooseRepositoryConfig,\n SequelizeRepositoryConfig,\n ORMAdapter,\n} from \"../repositories/types\";\n\n/**\n * Loads MongooseAdapter dynamically - only when needed\n * Throws helpful error if mongoose is not installed\n */\nexport async function loadMongooseAdapter<D, I, O>(\n config: MongooseRepositoryConfig<D>,\n): Promise<ORMAdapter<D, I, O>> {\n try {\n // Dynamic import - only loads when Mongoose is actually used\n const adapters = await import(\"./adapters/mongoose-adapter\");\n return new adapters.MongooseAdapter<D, I, O>(config);\n } catch (error: any) {\n if (\n error.code === \"ERR_MODULE_NOT_FOUND\" ||\n error.code === \"MODULE_NOT_FOUND\"\n ) {\n throw new Error(\n \"Mongoose is not installed. Install it with: npm install mongoose\",\n );\n }\n throw error;\n }\n}\n\n/**\n * Loads SequelizeAdapter dynamically - only when needed\n * Throws helpful error if sequelize is not installed\n */\nexport async function loadSequelizeAdapter<D extends Model, I, O>(\n config: SequelizeRepositoryConfig<D>,\n): Promise<ORMAdapter<D, I, O>> {\n try {\n // Dynamic import - only loads when Sequelize is actually used\n const adapters = await import(\"./adapters/sequelize-adapter\");\n return new adapters.SequelizeAdapter<D, I, O>(config);\n } catch (error: any) {\n if (\n error.code === \"ERR_MODULE_NOT_FOUND\" ||\n error.code === \"MODULE_NOT_FOUND\"\n ) {\n throw new Error(\n \"Sequelize is not installed. Install it with: npm install sequelize\",\n );\n }\n throw error;\n }\n}\n"]}
|
|
@@ -3,9 +3,13 @@ import type { ModelStatic } from "sequelize";
|
|
|
3
3
|
import { BaseRepositoryConfig, Repository } from "./types";
|
|
4
4
|
export * from "./types";
|
|
5
5
|
export declare abstract class BaseRepository<D, I, O> implements Repository<D, I, O> {
|
|
6
|
-
private adapter
|
|
6
|
+
private adapter?;
|
|
7
7
|
private readonly ormType;
|
|
8
|
+
private readonly config;
|
|
9
|
+
private initPromise?;
|
|
8
10
|
constructor(config: BaseRepositoryConfig<D>);
|
|
11
|
+
private ensureInitialized;
|
|
12
|
+
private loadAdapter;
|
|
9
13
|
get model(): MongooseModel<D> | ModelStatic<any>;
|
|
10
14
|
isMongoose(): boolean;
|
|
11
15
|
isSequelize(): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"abstract.d.ts","sourceRoot":"./src/","sources":["service/architecture/infrastructure/repositories/abstract.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,IAAI,aAAa,EAAE,MAAM,UAAU,CAAC;AACvD,OAAO,KAAK,EAAS,WAAW,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EACL,oBAAoB,EACpB,UAAU,EAKX,MAAM,SAAS,CAAC;AAMjB,cAAc,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"abstract.d.ts","sourceRoot":"./src/","sources":["service/architecture/infrastructure/repositories/abstract.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,IAAI,aAAa,EAAE,MAAM,UAAU,CAAC;AACvD,OAAO,KAAK,EAAS,WAAW,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EACL,oBAAoB,EACpB,UAAU,EAKX,MAAM,SAAS,CAAC;AAMjB,cAAc,SAAS,CAAC;AA8BxB,8BAAsB,cAAc,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAE,YAAW,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IAC1E,OAAO,CAAC,OAAO,CAAC,CAAsB;IACtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA0B;IACjD,OAAO,CAAC,WAAW,CAAC,CAAgB;gBAExB,MAAM,EAAE,oBAAoB,CAAC,CAAC,CAAC;YAS7B,iBAAiB;YAajB,WAAW;IAgBzB,IAAI,KAAK,IAAI,aAAa,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,GAAG,CAAC,CAO/C;IAEM,UAAU,IAAI,OAAO;IAIrB,WAAW,IAAI,OAAO;IAItB,gBAAgB,IAAI,aAAa,CAAC,CAAC,CAAC;IAYpC,iBAAiB,IAAI,WAAW,CAAC,GAAG,CAAC;IAYrC,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,CAAC,GAAG,IAAI;IASjC,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAKvC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAK7B,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAKzD,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;CAI5C"}
|
|
@@ -10,20 +10,36 @@ const isSequelizeConfig = (config) => {
|
|
|
10
10
|
export class BaseRepository {
|
|
11
11
|
adapter;
|
|
12
12
|
ormType;
|
|
13
|
+
config;
|
|
14
|
+
initPromise;
|
|
13
15
|
constructor(config) {
|
|
14
16
|
this.ormType = config.type;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
+
this.config = config;
|
|
18
|
+
}
|
|
19
|
+
async ensureInitialized() {
|
|
20
|
+
if (this.adapter)
|
|
21
|
+
return;
|
|
22
|
+
if (!this.initPromise) {
|
|
23
|
+
this.initPromise = this.loadAdapter();
|
|
24
|
+
}
|
|
25
|
+
await this.initPromise;
|
|
26
|
+
}
|
|
27
|
+
async loadAdapter() {
|
|
28
|
+
if (isMongooseConfig(this.config)) {
|
|
29
|
+
this.adapter = await loadMongooseAdapter(this.config);
|
|
17
30
|
}
|
|
18
|
-
else if (isSequelizeConfig(config)) {
|
|
19
|
-
this.adapter = loadSequelizeAdapter(config);
|
|
31
|
+
else if (isSequelizeConfig(this.config)) {
|
|
32
|
+
this.adapter = (await loadSequelizeAdapter(this.config));
|
|
20
33
|
}
|
|
21
34
|
else {
|
|
22
|
-
const exhaustive = config;
|
|
35
|
+
const exhaustive = this.config;
|
|
23
36
|
throw new Error(`Invalid repository configuration. Received: ${JSON.stringify(exhaustive)}`);
|
|
24
37
|
}
|
|
25
38
|
}
|
|
26
39
|
get model() {
|
|
40
|
+
if (!this.adapter) {
|
|
41
|
+
throw new Error("Repository not initialized. Call an async method first or await repository.ensureInitialized()");
|
|
42
|
+
}
|
|
27
43
|
return this.adapter.model;
|
|
28
44
|
}
|
|
29
45
|
isMongoose() {
|
|
@@ -33,30 +49,43 @@ export class BaseRepository {
|
|
|
33
49
|
return this.ormType === ORMType.Sequelize;
|
|
34
50
|
}
|
|
35
51
|
getMongooseModel() {
|
|
52
|
+
if (!this.adapter) {
|
|
53
|
+
throw new Error("Repository not initialized. Call an async method first or await repository.ensureInitialized()");
|
|
54
|
+
}
|
|
36
55
|
if (!this.isMongoose()) {
|
|
37
56
|
throw new Error("Repository is not using Mongoose");
|
|
38
57
|
}
|
|
39
58
|
return this.adapter.model;
|
|
40
59
|
}
|
|
41
60
|
getSequelizeModel() {
|
|
61
|
+
if (!this.adapter) {
|
|
62
|
+
throw new Error("Repository not initialized. Call an async method first or await repository.ensureInitialized()");
|
|
63
|
+
}
|
|
42
64
|
if (!this.isSequelize()) {
|
|
43
65
|
throw new Error("Repository is not using Sequelize");
|
|
44
66
|
}
|
|
45
67
|
return this.adapter.model;
|
|
46
68
|
}
|
|
47
69
|
toLean(input) {
|
|
70
|
+
if (!this.adapter) {
|
|
71
|
+
throw new Error("Repository not initialized. Call an async method first or await repository.ensureInitialized()");
|
|
72
|
+
}
|
|
48
73
|
return this.adapter.toLean(input);
|
|
49
74
|
}
|
|
50
75
|
async findById(id) {
|
|
76
|
+
await this.ensureInitialized();
|
|
51
77
|
return this.adapter.findById(id);
|
|
52
78
|
}
|
|
53
79
|
async create(entity) {
|
|
80
|
+
await this.ensureInitialized();
|
|
54
81
|
return this.adapter.create(entity);
|
|
55
82
|
}
|
|
56
83
|
async update(id, update) {
|
|
84
|
+
await this.ensureInitialized();
|
|
57
85
|
return this.adapter.update(id, update);
|
|
58
86
|
}
|
|
59
87
|
async delete(id) {
|
|
88
|
+
await this.ensureInitialized();
|
|
60
89
|
return this.adapter.delete(id);
|
|
61
90
|
}
|
|
62
91
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"abstract.js","sourceRoot":"./src/","sources":["service/architecture/infrastructure/repositories/abstract.ts"],"names":[],"mappings":"AAEA,OAAO,EAML,OAAO,GACR,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,uBAAuB,CAAC;AAE/B,cAAc,SAAS,CAAC;AAKxB,MAAM,gBAAgB,GAAG,CACvB,MAA+B,EACQ,EAAE;IACzC,OAAO,MAAM,CAAC,IAAI,KAAK,OAAO,CAAC,QAAQ,CAAC;AAC1C,CAAC,CAAC;AAKF,MAAM,iBAAiB,GAAG,CACxB,MAA+B,EACS,EAAE;IAC1C,OAAO,MAAM,CAAC,IAAI,KAAK,OAAO,CAAC,SAAS,CAAC;AAC3C,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"abstract.js","sourceRoot":"./src/","sources":["service/architecture/infrastructure/repositories/abstract.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Model as MongooseModel } from \"mongoose\";\nimport type { Model, ModelStatic } from \"sequelize\";\nimport {\n BaseRepositoryConfig,\n Repository,\n MongooseRepositoryConfig,\n SequelizeRepositoryConfig,\n ORMAdapter,\n ORMType,\n} from \"./types\";\nimport {\n loadMongooseAdapter,\n loadSequelizeAdapter,\n} from \"../orm/adapter-loader\";\n\nexport * from \"./types\";\n\n/**\n * Type guard to check if config is for Mongoose\n */\nconst isMongooseConfig = <D>(\n config: BaseRepositoryConfig<D>,\n): config is MongooseRepositoryConfig<D> => {\n return config.type === ORMType.Mongoose;\n};\n\n/**\n * Type guard to check if config is for Sequelize\n */\nconst isSequelizeConfig = <D extends Model>(\n config: BaseRepositoryConfig<D>,\n): config is SequelizeRepositoryConfig<D> => {\n return config.type === ORMType.Sequelize;\n};\n\n/**\n * BaseRepository - Abstract base class for all repositories\n *\n * Supports multiple ORMs (Mongoose, Sequelize) through adapter pattern.\n * Adapters are lazy-loaded - only the ORM you use gets imported.\n * This allows projects to install only one ORM without unused dependencies.\n *\n * Uses lazy initialization to support ESM dynamic imports while maintaining\n * backward compatibility with synchronous constructors.\n */\nexport abstract class BaseRepository<D, I, O> implements Repository<D, I, O> {\n private adapter?: ORMAdapter<D, I, O>;\n private readonly ormType: ORMType;\n private readonly config: BaseRepositoryConfig<D>;\n private initPromise?: Promise<void>;\n\n constructor(config: BaseRepositoryConfig<D>) {\n this.ormType = config.type;\n this.config = config;\n }\n\n /**\n * Ensures the adapter is initialized before any operation\n * Uses a promise to prevent multiple concurrent initializations\n */\n private async ensureInitialized(): Promise<void> {\n if (this.adapter) return;\n\n if (!this.initPromise) {\n this.initPromise = this.loadAdapter();\n }\n\n await this.initPromise;\n }\n\n /**\n * Loads the appropriate ORM adapter based on configuration\n */\n private async loadAdapter(): Promise<void> {\n if (isMongooseConfig<D>(this.config)) {\n this.adapter = await loadMongooseAdapter<D, I, O>(this.config);\n } else if (isSequelizeConfig(this.config)) {\n // Type assertion needed here because D could be either Mongoose or Sequelize model\n this.adapter = (await loadSequelizeAdapter<Model, I, O>(\n this.config,\n )) as ORMAdapter<D, I, O>;\n } else {\n const exhaustive: never = this.config;\n throw new Error(\n `Invalid repository configuration. Received: ${JSON.stringify(exhaustive)}`,\n );\n }\n }\n\n get model(): MongooseModel<D> | ModelStatic<any> {\n if (!this.adapter) {\n throw new Error(\n \"Repository not initialized. Call an async method first or await repository.ensureInitialized()\",\n );\n }\n return this.adapter.model;\n }\n\n public isMongoose(): boolean {\n return this.ormType === ORMType.Mongoose;\n }\n\n public isSequelize(): boolean {\n return this.ormType === ORMType.Sequelize;\n }\n\n public getMongooseModel(): MongooseModel<D> {\n if (!this.adapter) {\n throw new Error(\n \"Repository not initialized. Call an async method first or await repository.ensureInitialized()\",\n );\n }\n if (!this.isMongoose()) {\n throw new Error(\"Repository is not using Mongoose\");\n }\n return this.adapter.model as MongooseModel<D>;\n }\n\n public getSequelizeModel(): ModelStatic<any> {\n if (!this.adapter) {\n throw new Error(\n \"Repository not initialized. Call an async method first or await repository.ensureInitialized()\",\n );\n }\n if (!this.isSequelize()) {\n throw new Error(\"Repository is not using Sequelize\");\n }\n return this.adapter.model as ModelStatic<any>;\n }\n\n public toLean(input: unknown): O | null {\n if (!this.adapter) {\n throw new Error(\n \"Repository not initialized. Call an async method first or await repository.ensureInitialized()\",\n );\n }\n return this.adapter.toLean(input);\n }\n\n async findById(id: string): Promise<O | null> {\n await this.ensureInitialized();\n return this.adapter!.findById(id);\n }\n\n async create(entity: I): Promise<O> {\n await this.ensureInitialized();\n return this.adapter!.create(entity);\n }\n\n async update(id: string, update: Partial<I>): Promise<O | null> {\n await this.ensureInitialized();\n return this.adapter!.update(id, update);\n }\n\n async delete(id: string): Promise<O | null> {\n await this.ensureInitialized();\n return this.adapter!.delete(id);\n }\n}\n"]}
|