@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/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"main": "build/index.js",
|
|
5
5
|
"module": "build/index.js",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
7
|
-
"version": "0.0.
|
|
7
|
+
"version": "0.0.22",
|
|
8
8
|
"packageManager": "npm@10.9.0",
|
|
9
9
|
"exports": {
|
|
10
10
|
".": {
|
|
@@ -100,7 +100,9 @@
|
|
|
100
100
|
"test:coverage": "npm run test -- --coverage",
|
|
101
101
|
"test:bun": "bun test",
|
|
102
102
|
"test:node": "vitest run",
|
|
103
|
-
"type:check": "
|
|
103
|
+
"type:check": "npm run type:check:src && npm run type:check:test",
|
|
104
|
+
"type:check:src": "tsc --noEmit -p ./tsconfig.json",
|
|
105
|
+
"type:check:test": "tsc --noEmit -p ./test/tsconfig.json",
|
|
104
106
|
"lint": "DEBUG=eslint:cli-engine bun eslint .",
|
|
105
107
|
"prepublish": "npm run test:coverage && npm run type:check && npm run lint && npm run build",
|
|
106
108
|
"publish:latest": "npm run prepublish && npm publish --access public --tag latest",
|
|
@@ -9,17 +9,18 @@ import type {
|
|
|
9
9
|
* Loads MongooseAdapter dynamically - only when needed
|
|
10
10
|
* Throws helpful error if mongoose is not installed
|
|
11
11
|
*/
|
|
12
|
-
export function loadMongooseAdapter<D, I, O>(
|
|
12
|
+
export async function loadMongooseAdapter<D, I, O>(
|
|
13
13
|
config: MongooseRepositoryConfig<D>,
|
|
14
|
-
): ORMAdapter<D, I, O
|
|
14
|
+
): Promise<ORMAdapter<D, I, O>> {
|
|
15
15
|
try {
|
|
16
16
|
// Dynamic import - only loads when Mongoose is actually used
|
|
17
|
-
|
|
18
|
-
const adapters =
|
|
19
|
-
require("./adapters/mongoose-adapter") as typeof import("./adapters/mongoose-adapter");
|
|
17
|
+
const adapters = await import("./adapters/mongoose-adapter");
|
|
20
18
|
return new adapters.MongooseAdapter<D, I, O>(config);
|
|
21
19
|
} catch (error: any) {
|
|
22
|
-
if (
|
|
20
|
+
if (
|
|
21
|
+
error.code === "ERR_MODULE_NOT_FOUND" ||
|
|
22
|
+
error.code === "MODULE_NOT_FOUND"
|
|
23
|
+
) {
|
|
23
24
|
throw new Error(
|
|
24
25
|
"Mongoose is not installed. Install it with: npm install mongoose",
|
|
25
26
|
);
|
|
@@ -32,17 +33,18 @@ export function loadMongooseAdapter<D, I, O>(
|
|
|
32
33
|
* Loads SequelizeAdapter dynamically - only when needed
|
|
33
34
|
* Throws helpful error if sequelize is not installed
|
|
34
35
|
*/
|
|
35
|
-
export function loadSequelizeAdapter<D extends Model, I, O>(
|
|
36
|
+
export async function loadSequelizeAdapter<D extends Model, I, O>(
|
|
36
37
|
config: SequelizeRepositoryConfig<D>,
|
|
37
|
-
): ORMAdapter<D, I, O
|
|
38
|
+
): Promise<ORMAdapter<D, I, O>> {
|
|
38
39
|
try {
|
|
39
40
|
// Dynamic import - only loads when Sequelize is actually used
|
|
40
|
-
|
|
41
|
-
const adapters =
|
|
42
|
-
require("./adapters/sequelize-adapter") as typeof import("./adapters/sequelize-adapter");
|
|
41
|
+
const adapters = await import("./adapters/sequelize-adapter");
|
|
43
42
|
return new adapters.SequelizeAdapter<D, I, O>(config);
|
|
44
43
|
} catch (error: any) {
|
|
45
|
-
if (
|
|
44
|
+
if (
|
|
45
|
+
error.code === "ERR_MODULE_NOT_FOUND" ||
|
|
46
|
+
error.code === "MODULE_NOT_FOUND"
|
|
47
|
+
) {
|
|
46
48
|
throw new Error(
|
|
47
49
|
"Sequelize is not installed. Install it with: npm install sequelize",
|
|
48
50
|
);
|
|
@@ -39,25 +39,48 @@ const isSequelizeConfig = <D extends Model>(
|
|
|
39
39
|
* Supports multiple ORMs (Mongoose, Sequelize) through adapter pattern.
|
|
40
40
|
* Adapters are lazy-loaded - only the ORM you use gets imported.
|
|
41
41
|
* This allows projects to install only one ORM without unused dependencies.
|
|
42
|
+
*
|
|
43
|
+
* Uses lazy initialization to support ESM dynamic imports while maintaining
|
|
44
|
+
* backward compatibility with synchronous constructors.
|
|
42
45
|
*/
|
|
43
46
|
export abstract class BaseRepository<D, I, O> implements Repository<D, I, O> {
|
|
44
|
-
private adapter
|
|
47
|
+
private adapter?: ORMAdapter<D, I, O>;
|
|
45
48
|
private readonly ormType: ORMType;
|
|
49
|
+
private readonly config: BaseRepositoryConfig<D>;
|
|
50
|
+
private initPromise?: Promise<void>;
|
|
46
51
|
|
|
47
52
|
constructor(config: BaseRepositoryConfig<D>) {
|
|
48
53
|
this.ormType = config.type;
|
|
54
|
+
this.config = config;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Ensures the adapter is initialized before any operation
|
|
59
|
+
* Uses a promise to prevent multiple concurrent initializations
|
|
60
|
+
*/
|
|
61
|
+
private async ensureInitialized(): Promise<void> {
|
|
62
|
+
if (this.adapter) return;
|
|
63
|
+
|
|
64
|
+
if (!this.initPromise) {
|
|
65
|
+
this.initPromise = this.loadAdapter();
|
|
66
|
+
}
|
|
49
67
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
68
|
+
await this.initPromise;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Loads the appropriate ORM adapter based on configuration
|
|
73
|
+
*/
|
|
74
|
+
private async loadAdapter(): Promise<void> {
|
|
75
|
+
if (isMongooseConfig<D>(this.config)) {
|
|
76
|
+
this.adapter = await loadMongooseAdapter<D, I, O>(this.config);
|
|
77
|
+
} else if (isSequelizeConfig(this.config)) {
|
|
53
78
|
// Type assertion needed here because D could be either Mongoose or Sequelize model
|
|
54
|
-
this.adapter = loadSequelizeAdapter<Model, I, O>(
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
O
|
|
58
|
-
>;
|
|
79
|
+
this.adapter = (await loadSequelizeAdapter<Model, I, O>(
|
|
80
|
+
this.config,
|
|
81
|
+
)) as ORMAdapter<D, I, O>;
|
|
59
82
|
} else {
|
|
60
|
-
const exhaustive: never = config;
|
|
83
|
+
const exhaustive: never = this.config;
|
|
61
84
|
throw new Error(
|
|
62
85
|
`Invalid repository configuration. Received: ${JSON.stringify(exhaustive)}`,
|
|
63
86
|
);
|
|
@@ -65,6 +88,11 @@ export abstract class BaseRepository<D, I, O> implements Repository<D, I, O> {
|
|
|
65
88
|
}
|
|
66
89
|
|
|
67
90
|
get model(): MongooseModel<D> | ModelStatic<any> {
|
|
91
|
+
if (!this.adapter) {
|
|
92
|
+
throw new Error(
|
|
93
|
+
"Repository not initialized. Call an async method first or await repository.ensureInitialized()",
|
|
94
|
+
);
|
|
95
|
+
}
|
|
68
96
|
return this.adapter.model;
|
|
69
97
|
}
|
|
70
98
|
|
|
@@ -77,6 +105,11 @@ export abstract class BaseRepository<D, I, O> implements Repository<D, I, O> {
|
|
|
77
105
|
}
|
|
78
106
|
|
|
79
107
|
public getMongooseModel(): MongooseModel<D> {
|
|
108
|
+
if (!this.adapter) {
|
|
109
|
+
throw new Error(
|
|
110
|
+
"Repository not initialized. Call an async method first or await repository.ensureInitialized()",
|
|
111
|
+
);
|
|
112
|
+
}
|
|
80
113
|
if (!this.isMongoose()) {
|
|
81
114
|
throw new Error("Repository is not using Mongoose");
|
|
82
115
|
}
|
|
@@ -84,6 +117,11 @@ export abstract class BaseRepository<D, I, O> implements Repository<D, I, O> {
|
|
|
84
117
|
}
|
|
85
118
|
|
|
86
119
|
public getSequelizeModel(): ModelStatic<any> {
|
|
120
|
+
if (!this.adapter) {
|
|
121
|
+
throw new Error(
|
|
122
|
+
"Repository not initialized. Call an async method first or await repository.ensureInitialized()",
|
|
123
|
+
);
|
|
124
|
+
}
|
|
87
125
|
if (!this.isSequelize()) {
|
|
88
126
|
throw new Error("Repository is not using Sequelize");
|
|
89
127
|
}
|
|
@@ -91,22 +129,31 @@ export abstract class BaseRepository<D, I, O> implements Repository<D, I, O> {
|
|
|
91
129
|
}
|
|
92
130
|
|
|
93
131
|
public toLean(input: unknown): O | null {
|
|
132
|
+
if (!this.adapter) {
|
|
133
|
+
throw new Error(
|
|
134
|
+
"Repository not initialized. Call an async method first or await repository.ensureInitialized()",
|
|
135
|
+
);
|
|
136
|
+
}
|
|
94
137
|
return this.adapter.toLean(input);
|
|
95
138
|
}
|
|
96
139
|
|
|
97
140
|
async findById(id: string): Promise<O | null> {
|
|
98
|
-
|
|
141
|
+
await this.ensureInitialized();
|
|
142
|
+
return this.adapter!.findById(id);
|
|
99
143
|
}
|
|
100
144
|
|
|
101
145
|
async create(entity: I): Promise<O> {
|
|
102
|
-
|
|
146
|
+
await this.ensureInitialized();
|
|
147
|
+
return this.adapter!.create(entity);
|
|
103
148
|
}
|
|
104
149
|
|
|
105
150
|
async update(id: string, update: Partial<I>): Promise<O | null> {
|
|
106
|
-
|
|
151
|
+
await this.ensureInitialized();
|
|
152
|
+
return this.adapter!.update(id, update);
|
|
107
153
|
}
|
|
108
154
|
|
|
109
155
|
async delete(id: string): Promise<O | null> {
|
|
110
|
-
|
|
156
|
+
await this.ensureInitialized();
|
|
157
|
+
return this.adapter!.delete(id);
|
|
111
158
|
}
|
|
112
159
|
}
|