@lukman-ss/moke-core 0.1.0 → 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/LICENSE +21 -0
- package/README.md +274 -21
- package/dist/application.d.ts +27 -2
- package/dist/application.d.ts.map +1 -1
- package/dist/application.js +194 -6
- package/dist/application.js.map +1 -1
- package/dist/container.d.ts +37 -4
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +272 -30
- package/dist/container.js.map +1 -1
- package/dist/decorators.d.ts +0 -8
- package/dist/decorators.d.ts.map +1 -1
- package/dist/decorators.js +3 -12
- package/dist/decorators.js.map +1 -1
- package/dist/errors.d.ts +38 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +98 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/internal/formatter.d.ts +3 -0
- package/dist/internal/formatter.d.ts.map +1 -0
- package/dist/internal/formatter.js +20 -0
- package/dist/internal/formatter.js.map +1 -0
- package/dist/lifecycle.d.ts +13 -0
- package/dist/lifecycle.d.ts.map +1 -0
- package/dist/lifecycle.js +2 -0
- package/dist/lifecycle.js.map +1 -0
- package/dist/logger.d.ts +14 -3
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +19 -5
- package/dist/logger.js.map +1 -1
- package/dist/module.d.ts +10 -0
- package/dist/module.d.ts.map +1 -0
- package/dist/module.js +7 -0
- package/dist/module.js.map +1 -0
- package/dist/providers.d.ts +15 -11
- package/dist/providers.d.ts.map +1 -1
- package/dist/providers.js.map +1 -1
- package/dist/reflection.d.ts +25 -0
- package/dist/reflection.d.ts.map +1 -0
- package/dist/reflection.js +34 -0
- package/dist/reflection.js.map +1 -0
- package/dist/service-provider.d.ts +8 -0
- package/dist/service-provider.d.ts.map +1 -0
- package/dist/service-provider.js +7 -0
- package/dist/service-provider.js.map +1 -0
- package/dist/types.js.map +1 -1
- package/package.json +30 -6
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 lukman-ss
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,37 +1,290 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Moke Core
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The transport-agnostic Dependency Injection and Application Lifecycle foundation of the Moke web framework.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## What is Moke Core?
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
- **Decorators**: `@Injectable()` for class registration.
|
|
9
|
-
- **Factory**: `MokeFactory` to bootstrap the application.
|
|
10
|
-
- **Logger**: Standard `MokeLogger`.
|
|
7
|
+
Moke Core provides a robust, strictly typed Inversion of Control (IoC) container designed for ESM. It manages dependency resolution, scopes, lifecycle hooks, and modular composition without tying you to any specific transport (like HTTP).
|
|
11
8
|
|
|
12
|
-
##
|
|
9
|
+
## Requirements
|
|
10
|
+
|
|
11
|
+
- **Node.js**: >= 22.14.0
|
|
12
|
+
- **Format**: ESM strictly. CommonJS is not supported.
|
|
13
|
+
- **TypeScript**: Experimental Decorators must be enabled.
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @lukman-ss/moke-core
|
|
19
|
+
# or
|
|
20
|
+
pnpm add @lukman-ss/moke-core
|
|
21
|
+
# or
|
|
22
|
+
yarn add @lukman-ss/moke-core
|
|
23
|
+
# or
|
|
24
|
+
bun add @lukman-ss/moke-core
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## TypeScript Configuration
|
|
28
|
+
|
|
29
|
+
Moke Core relies on `reflect-metadata` and experimental decorators. Ensure your `tsconfig.json` contains:
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"compilerOptions": {
|
|
34
|
+
"experimentalDecorators": true,
|
|
35
|
+
"emitDecoratorMetadata": true,
|
|
36
|
+
"module": "NodeNext",
|
|
37
|
+
"moduleResolution": "NodeNext"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Quick Start
|
|
13
43
|
|
|
14
44
|
```typescript
|
|
15
|
-
import { Injectable, MokeFactory
|
|
45
|
+
import { Injectable, MokeFactory } from '@lukman-ss/moke-core';
|
|
16
46
|
|
|
17
47
|
@Injectable()
|
|
18
|
-
class
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
run() {
|
|
22
|
-
this.logger.log('App is running');
|
|
48
|
+
class Logger {
|
|
49
|
+
log(message: string) {
|
|
50
|
+
console.log(`[LOG]: ${message}`);
|
|
23
51
|
}
|
|
24
52
|
}
|
|
25
53
|
|
|
26
54
|
@Injectable()
|
|
27
|
-
class
|
|
28
|
-
constructor(private
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
this.
|
|
55
|
+
class UserService {
|
|
56
|
+
constructor(private logger: Logger) {}
|
|
57
|
+
|
|
58
|
+
async create() {
|
|
59
|
+
this.logger.log('Creating user...');
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const app = await MokeFactory.createApplicationContext(UserService);
|
|
64
|
+
await app.init();
|
|
65
|
+
|
|
66
|
+
const service = app.get(UserService);
|
|
67
|
+
await service.create();
|
|
68
|
+
|
|
69
|
+
await app.close();
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Dependency Injection
|
|
73
|
+
|
|
74
|
+
Moke Core uses class decorators and constructor injection to resolve dependencies automatically.
|
|
75
|
+
|
|
76
|
+
### @Injectable & @Inject
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
import { Injectable, Inject, createToken } from '@lukman-ss/moke-core';
|
|
80
|
+
|
|
81
|
+
const CACHE = createToken<Cache>('CACHE');
|
|
82
|
+
|
|
83
|
+
@Injectable()
|
|
84
|
+
export class UserService {
|
|
85
|
+
constructor(
|
|
86
|
+
private readonly repo: UserRepository, // Auto-inferred via reflection
|
|
87
|
+
@Inject(CACHE) private readonly cache: Cache // Interface/Primitive overrides
|
|
88
|
+
) {}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Tokens
|
|
93
|
+
|
|
94
|
+
For interfaces, primitives, or values that cannot be inferred via reflection:
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
import { createToken } from '@lukman-ss/moke-core';
|
|
98
|
+
|
|
99
|
+
const PORT = createToken<number>('PORT');
|
|
100
|
+
const DATABASE_URL = createToken<string>('DATABASE_URL');
|
|
101
|
+
|
|
102
|
+
@Injectable()
|
|
103
|
+
class Database {
|
|
104
|
+
constructor(@Inject(DATABASE_URL) private url: string) {}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Provider Types
|
|
109
|
+
|
|
110
|
+
Moke supports four provider types:
|
|
111
|
+
|
|
112
|
+
- **ClassProvider**: `{ provide: Token, useClass: Constructor }`
|
|
113
|
+
- **ValueProvider**: `{ provide: Token, useValue: any }`
|
|
114
|
+
- **FactoryProvider**: `{ provide: Token, useFactory: (c: Container) => any }`
|
|
115
|
+
- **ExistingProvider (Alias)**: `{ provide: Token, useExisting: Token }`
|
|
116
|
+
|
|
117
|
+
## Scopes
|
|
118
|
+
|
|
119
|
+
- **singleton**: One instance across the entire application and all child scopes.
|
|
120
|
+
- **scoped**: One instance per Container instance (useful for request-scoped instances).
|
|
121
|
+
- **transient**: A new instance is created every time it is injected.
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
container.singleton(DatabasePool);
|
|
125
|
+
container.scoped(RequestContext);
|
|
126
|
+
container.transient(Logger);
|
|
127
|
+
|
|
128
|
+
const childScope = container.createChild();
|
|
129
|
+
const service = childScope.resolve(UserService);
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Async Providers
|
|
133
|
+
|
|
134
|
+
Async providers are supported safely without corrupting synchronous APIs:
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
container.factory(DB_TOKEN, async () => await connectDb());
|
|
138
|
+
|
|
139
|
+
// container.resolve(DB_TOKEN) // Throws AsyncProviderResolutionError
|
|
140
|
+
|
|
141
|
+
const db = await container.resolveAsync(DB_TOKEN);
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Child Containers
|
|
145
|
+
|
|
146
|
+
Use `createChild()` to create isolated scopes:
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
const requestScope = app.container.createChild();
|
|
150
|
+
const user = requestScope.resolve(CurrentUser);
|
|
151
|
+
const unitOfWork = requestScope.resolve(RequestUnitOfWork);
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Modules
|
|
155
|
+
|
|
156
|
+
Group features using the `@Module` decorator.
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
import { Module } from '@lukman-ss/moke-core';
|
|
160
|
+
|
|
161
|
+
@Module({
|
|
162
|
+
imports: [ConfigModule],
|
|
163
|
+
providers: [UserService, UserRepository]
|
|
164
|
+
})
|
|
165
|
+
export class UserModule {}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Application Context
|
|
169
|
+
|
|
170
|
+
`MokeApplicationContext` encapsulates the container and manages the application lifecycle.
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
import { MokeFactory } from '@lukman-ss/moke-core';
|
|
174
|
+
|
|
175
|
+
const app = await MokeFactory.createApplicationContext(AppModule);
|
|
176
|
+
|
|
177
|
+
const service = app.get(UserService);
|
|
178
|
+
|
|
179
|
+
// Start lifecycle hooks
|
|
180
|
+
await app.init();
|
|
181
|
+
|
|
182
|
+
// Graceful shutdown
|
|
183
|
+
await app.close();
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Lifecycle Hooks
|
|
187
|
+
|
|
188
|
+
Classes can implement lifecycle interfaces. They are executed deterministically:
|
|
189
|
+
- `OnModuleInit`
|
|
190
|
+
- `OnApplicationBootstrap`
|
|
191
|
+
- `OnModuleDestroy`
|
|
192
|
+
- `OnApplicationShutdown`
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
import { OnModuleInit, OnApplicationBootstrap } from '@lukman-ss/moke-core';
|
|
196
|
+
|
|
197
|
+
@Injectable()
|
|
198
|
+
class DatabaseService implements OnModuleInit, OnApplicationBootstrap {
|
|
199
|
+
async onModuleInit() {
|
|
200
|
+
// Called during app.init()
|
|
201
|
+
await this.connect();
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
async onApplicationBootstrap() {
|
|
205
|
+
// Called after onModuleInit for all modules
|
|
32
206
|
}
|
|
33
207
|
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## Errors
|
|
211
|
+
|
|
212
|
+
Moke throws specific error types:
|
|
213
|
+
|
|
214
|
+
- `UnknownProviderError`: Token not found
|
|
215
|
+
- `CircularDependencyError`: Circular dependency detected
|
|
216
|
+
- `AsyncProviderResolutionError`: Sync resolve on async provider
|
|
217
|
+
- `DependencyResolutionError`: General resolution failure
|
|
218
|
+
- `MokeShutdownError`: Aggregated shutdown errors
|
|
219
|
+
- `DuplicateProviderError`: Duplicate provider registration
|
|
220
|
+
- `InvalidProviderError`: Invalid provider definition
|
|
221
|
+
- `PrimitiveDependencyError`: Cannot infer primitive dependency
|
|
222
|
+
- `MokeCircularModuleError`: Circular module imports
|
|
223
|
+
|
|
224
|
+
## Testing
|
|
225
|
+
|
|
226
|
+
### Override Providers for Testing
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
import { createToken } from '@lukman-ss/moke-core';
|
|
230
|
+
|
|
231
|
+
const DATABASE = createToken('DATABASE');
|
|
232
|
+
|
|
233
|
+
// Production implementation
|
|
234
|
+
container.singleton(DATABASE, RealDatabase);
|
|
235
|
+
|
|
236
|
+
// Test override
|
|
237
|
+
container.override(DATABASE, FakeDatabase);
|
|
34
238
|
|
|
35
|
-
const
|
|
36
|
-
|
|
239
|
+
const service = container.resolve(UserService);
|
|
240
|
+
// Uses FakeDatabase instead of RealDatabase
|
|
37
241
|
```
|
|
242
|
+
|
|
243
|
+
### Integration Testing Pattern
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
import { MokeFactory } from '@lukman-ss/moke-core';
|
|
247
|
+
import { describe, it, beforeEach } from 'mocha';
|
|
248
|
+
|
|
249
|
+
describe('UserService Integration', () => {
|
|
250
|
+
let app: MokeApplicationContext;
|
|
251
|
+
|
|
252
|
+
beforeEach(async () => {
|
|
253
|
+
const appContext = await MokeFactory.createApplicationContext(AppModule);
|
|
254
|
+
|
|
255
|
+
// Override production DB with in-memory fake
|
|
256
|
+
appContext.container.override(DATABASE, InMemoryDatabase);
|
|
257
|
+
|
|
258
|
+
await appContext.init();
|
|
259
|
+
app = appContext;
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it('creates user', async () => {
|
|
263
|
+
const service = app.get(UserService);
|
|
264
|
+
await service.create({ name: 'Alice' });
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
afterEach(async () => {
|
|
268
|
+
await app.close();
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## Package Status
|
|
274
|
+
|
|
275
|
+
| Package | Version | Status |
|
|
276
|
+
|---------|---------|--------|
|
|
277
|
+
| `@lukman-ss/moke-core` | 0.1.16 | Alpha |
|
|
278
|
+
| `@lukman-ss/moke-http` | — | Not yet released |
|
|
279
|
+
|
|
280
|
+
### 0.x Stability
|
|
281
|
+
|
|
282
|
+
Moke Core is in **0.x alpha**. Public APIs may evolve before 1.0.0. We follow semantic versioning with awareness of early-stage instability.
|
|
283
|
+
|
|
284
|
+
## Architecture
|
|
285
|
+
|
|
286
|
+
See `ARCHITECTURE.md` for detailed design documentation.
|
|
287
|
+
|
|
288
|
+
## License
|
|
289
|
+
|
|
290
|
+
MIT
|
package/dist/application.d.ts
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Container } from './container.js';
|
|
2
|
+
import { Constructor, Token } from './types.js';
|
|
3
|
+
import { ServiceProvider } from './service-provider.js';
|
|
4
|
+
import { ProviderDefinition, Scope } from './providers.js';
|
|
5
|
+
export type ApplicationState = 'created' | 'initializing' | 'ready' | 'closing' | 'closed';
|
|
6
|
+
export declare class MokeApplicationContext {
|
|
7
|
+
readonly container: Container;
|
|
8
|
+
private _state;
|
|
9
|
+
private initPromise?;
|
|
10
|
+
private isRegistrationFrozen;
|
|
11
|
+
constructor(container: Container);
|
|
12
|
+
get state(): ApplicationState;
|
|
13
|
+
get<T>(token: Token<T>): T;
|
|
14
|
+
getAsync<T>(token: Token<T>): Promise<T>;
|
|
15
|
+
register<T>(token: Token<T>, providerDef: ProviderDefinition<T>, scope?: Scope): void;
|
|
16
|
+
registerProvider(provider: ServiceProvider): Promise<void>;
|
|
17
|
+
bootProviders(): Promise<void>;
|
|
18
|
+
init(): Promise<void>;
|
|
19
|
+
private _initImpl;
|
|
20
|
+
close(signal?: string): Promise<void>;
|
|
21
|
+
}
|
|
2
22
|
export declare class MokeFactory {
|
|
3
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Creates a MokeApplicationContext, compiling the module tree.
|
|
25
|
+
* Does not automatically call `init()`.
|
|
26
|
+
*/
|
|
27
|
+
static createApplicationContext(module: Constructor<unknown>): Promise<MokeApplicationContext>;
|
|
28
|
+
private static compileModuleAsync;
|
|
4
29
|
}
|
|
5
30
|
//# sourceMappingURL=application.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"application.d.ts","sourceRoot":"","sources":["../src/application.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"application.d.ts","sourceRoot":"","sources":["../src/application.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,KAAK,EAAY,MAAM,gBAAgB,CAAC;AAKrE,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,cAAc,GAAG,OAAO,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE3F,qBAAa,sBAAsB;aAKL,SAAS,EAAE,SAAS;IAJhD,OAAO,CAAC,MAAM,CAA+B;IAC7C,OAAO,CAAC,WAAW,CAAC,CAAgB;IACpC,OAAO,CAAC,oBAAoB,CAAS;gBAET,SAAS,EAAE,SAAS;IAEhD,IAAI,KAAK,IAAI,gBAAgB,CAE5B;IAED,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC;IAI1B,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAIxC,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,kBAAkB,CAAC,CAAC,CAAC,EAAE,KAAK,GAAE,KAAmB,GAAG,IAAI;IAO5F,gBAAgB,CAAC,QAAQ,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAe1D,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAiB9B,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YAyBb,SAAS;IA0BjB,KAAK,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAsC5C;AAED,qBAAa,WAAW;IACtB;;;OAGG;WACU,wBAAwB,CAAC,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,sBAAsB,CAAC;mBAa/E,kBAAkB;CAqDxC"}
|
package/dist/application.js
CHANGED
|
@@ -1,12 +1,200 @@
|
|
|
1
1
|
import { Container } from './container.js';
|
|
2
|
-
import {
|
|
2
|
+
import { ConsoleLogger } from './logger.js';
|
|
3
|
+
import { ServiceProvider } from './service-provider.js';
|
|
4
|
+
import { ReflectionHost } from './reflection.js';
|
|
5
|
+
import { MokeCircularModuleError, MokeBootstrapError, MokeShutdownError } from './errors.js';
|
|
6
|
+
export class MokeApplicationContext {
|
|
7
|
+
container;
|
|
8
|
+
_state = 'created';
|
|
9
|
+
initPromise;
|
|
10
|
+
isRegistrationFrozen = false;
|
|
11
|
+
constructor(container) {
|
|
12
|
+
this.container = container;
|
|
13
|
+
}
|
|
14
|
+
get state() {
|
|
15
|
+
return this._state;
|
|
16
|
+
}
|
|
17
|
+
get(token) {
|
|
18
|
+
return this.container.resolve(token);
|
|
19
|
+
}
|
|
20
|
+
getAsync(token) {
|
|
21
|
+
return this.container.resolveAsync(token);
|
|
22
|
+
}
|
|
23
|
+
register(token, providerDef, scope = 'singleton') {
|
|
24
|
+
if (this._state !== 'created') {
|
|
25
|
+
throw new Error('Cannot register providers after application has been initialized');
|
|
26
|
+
}
|
|
27
|
+
this.container.bind(token, providerDef, scope);
|
|
28
|
+
}
|
|
29
|
+
async registerProvider(provider) {
|
|
30
|
+
if (this._state !== 'created') {
|
|
31
|
+
throw new Error('Cannot register providers after application has been initialized');
|
|
32
|
+
}
|
|
33
|
+
const registeredProviders = Reflect.getMetadata('moke:registeredProviders', provider) || [];
|
|
34
|
+
if (registeredProviders.includes(provider.constructor)) {
|
|
35
|
+
throw new Error(`ServiceProvider ${provider.constructor.name} has already been registered. Use register() instead of registerProvider() for class-based providers.`);
|
|
36
|
+
}
|
|
37
|
+
Reflect.defineMetadata('moke:registeredProviders', [...registeredProviders, provider.constructor], provider);
|
|
38
|
+
await provider.register?.();
|
|
39
|
+
}
|
|
40
|
+
async bootProviders() {
|
|
41
|
+
const bootQueue = [];
|
|
42
|
+
for (const instance of this.container.getInstantiatedInstances()) {
|
|
43
|
+
if (instance instanceof ServiceProvider) {
|
|
44
|
+
const registered = Reflect.getMetadata('moke:registeredProviders', instance) || [];
|
|
45
|
+
if (registered.length > 0) {
|
|
46
|
+
bootQueue.push(instance);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
for (const provider of bootQueue) {
|
|
51
|
+
await provider.boot?.();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
async init() {
|
|
55
|
+
if (this._state === 'ready')
|
|
56
|
+
return;
|
|
57
|
+
if (this._state === 'closing' || this._state === 'closed') {
|
|
58
|
+
throw new Error('Cannot initialize an application that is closing or closed.');
|
|
59
|
+
}
|
|
60
|
+
if (this._state === 'initializing' && this.initPromise) {
|
|
61
|
+
return this.initPromise;
|
|
62
|
+
}
|
|
63
|
+
this._state = 'initializing';
|
|
64
|
+
this.initPromise = this._initImpl();
|
|
65
|
+
try {
|
|
66
|
+
await this.initPromise;
|
|
67
|
+
this.container.freeze();
|
|
68
|
+
this._state = 'ready';
|
|
69
|
+
}
|
|
70
|
+
catch (e) {
|
|
71
|
+
this._state = 'created';
|
|
72
|
+
throw e;
|
|
73
|
+
}
|
|
74
|
+
finally {
|
|
75
|
+
this.initPromise = undefined;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
async _initImpl() {
|
|
79
|
+
const instances = this.container.getInstantiatedInstances();
|
|
80
|
+
// 1. onModuleInit
|
|
81
|
+
for (const instance of instances) {
|
|
82
|
+
if (typeof instance.onModuleInit === 'function') {
|
|
83
|
+
try {
|
|
84
|
+
await instance.onModuleInit();
|
|
85
|
+
}
|
|
86
|
+
catch (e) {
|
|
87
|
+
throw new MokeBootstrapError('onModuleInit', e, instance.constructor?.name);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// 2. onApplicationBootstrap
|
|
92
|
+
for (const instance of instances) {
|
|
93
|
+
if (typeof instance.onApplicationBootstrap === 'function') {
|
|
94
|
+
try {
|
|
95
|
+
await instance.onApplicationBootstrap();
|
|
96
|
+
}
|
|
97
|
+
catch (e) {
|
|
98
|
+
throw new MokeBootstrapError('onApplicationBootstrap', e, instance.constructor?.name);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
async close(signal) {
|
|
104
|
+
if (this._state === 'closed')
|
|
105
|
+
return;
|
|
106
|
+
this._state = 'closing';
|
|
107
|
+
const shutdownErrors = [];
|
|
108
|
+
// Reverse order for teardown
|
|
109
|
+
const instances = this.container.getInstantiatedInstances().reverse();
|
|
110
|
+
// 1. onApplicationShutdown
|
|
111
|
+
for (const instance of instances) {
|
|
112
|
+
if (typeof instance.onApplicationShutdown === 'function') {
|
|
113
|
+
try {
|
|
114
|
+
await instance.onApplicationShutdown(signal);
|
|
115
|
+
}
|
|
116
|
+
catch (e) {
|
|
117
|
+
shutdownErrors.push(e);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// 2. onModuleDestroy
|
|
122
|
+
for (const instance of instances) {
|
|
123
|
+
if (typeof instance.onModuleDestroy === 'function') {
|
|
124
|
+
try {
|
|
125
|
+
await instance.onModuleDestroy();
|
|
126
|
+
}
|
|
127
|
+
catch (e) {
|
|
128
|
+
shutdownErrors.push(e);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
this.container.dispose();
|
|
133
|
+
this._state = 'closed';
|
|
134
|
+
if (shutdownErrors.length > 0) {
|
|
135
|
+
throw new MokeShutdownError(shutdownErrors);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
3
139
|
export class MokeFactory {
|
|
4
|
-
|
|
140
|
+
/**
|
|
141
|
+
* Creates a MokeApplicationContext, compiling the module tree.
|
|
142
|
+
* Does not automatically call `init()`.
|
|
143
|
+
*/
|
|
144
|
+
static async createApplicationContext(module) {
|
|
5
145
|
const container = new Container();
|
|
6
|
-
//
|
|
7
|
-
container.instance(
|
|
8
|
-
|
|
9
|
-
|
|
146
|
+
// Core default registrations
|
|
147
|
+
container.instance(ConsoleLogger, new ConsoleLogger());
|
|
148
|
+
container.bind('Logger', { useExisting: ConsoleLogger });
|
|
149
|
+
await this.compileModuleAsync(module, container, new Set(), []);
|
|
150
|
+
await container.resolveAsync(module); // Resolve the root module to trigger graph instantiation
|
|
151
|
+
return new MokeApplicationContext(container);
|
|
152
|
+
}
|
|
153
|
+
static async compileModuleAsync(module, container, resolved, path) {
|
|
154
|
+
if (path.includes(module)) {
|
|
155
|
+
throw new MokeCircularModuleError([...path, module]);
|
|
156
|
+
}
|
|
157
|
+
if (resolved.has(module))
|
|
158
|
+
return;
|
|
159
|
+
path.push(module);
|
|
160
|
+
const metadata = ReflectionHost.getModuleMetadata(module);
|
|
161
|
+
if (!metadata) {
|
|
162
|
+
container.singleton(module);
|
|
163
|
+
resolved.add(module);
|
|
164
|
+
path.pop();
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
if (metadata.imports) {
|
|
168
|
+
for (const imported of metadata.imports) {
|
|
169
|
+
await this.compileModuleAsync(imported, container, resolved, path);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (metadata.providers) {
|
|
173
|
+
for (const provider of metadata.providers) {
|
|
174
|
+
if (typeof provider === 'function') {
|
|
175
|
+
// Provide default singleton bindings only if not already bound
|
|
176
|
+
// In an encapsulated model, this logic will branch. Here in the flattened model, we skip if already explicitly bound.
|
|
177
|
+
if (!container.hasOwn(provider)) {
|
|
178
|
+
container.singleton(provider);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
const providerDef = provider;
|
|
183
|
+
if (providerDef.provide && !container.hasOwn(providerDef.provide)) {
|
|
184
|
+
container.register(providerDef);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
// Exports behavior in core is currently flattened globally.
|
|
190
|
+
// In Model B (Encapsulated), each module would get a child container,
|
|
191
|
+
// and exports would be forwarded.
|
|
192
|
+
// For Moke's simplicity vs NestJS-like complexity, we use a flattened global DI by default.
|
|
193
|
+
if (!container.hasOwn(module)) {
|
|
194
|
+
container.singleton(module);
|
|
195
|
+
}
|
|
196
|
+
resolved.add(module);
|
|
197
|
+
path.pop();
|
|
10
198
|
}
|
|
11
199
|
}
|
|
12
200
|
//# sourceMappingURL=application.js.map
|
package/dist/application.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"application.js","sourceRoot":"","sources":["../src/application.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGzC,MAAM,OAAO,WAAW;IACtB,MAAM,CAAC,MAAM,CAAI,MAAsB;QACrC,MAAM,SAAS,GAAG,IAAI,SAAS,EAAE,CAAC;QAElC,oBAAoB;QACpB,SAAS,CAAC,QAAQ,CAAC,UAAU,EAAE,IAAI,UAAU,EAAE,CAAC,CAAC;QAEjD,MAAM,GAAG,GAAG,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACtC,OAAO,GAAG,CAAC;IACb,CAAC;CACF"}
|
|
1
|
+
{"version":3,"file":"application.js","sourceRoot":"","sources":["../src/application.ts"],"names":[],"mappings":"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","sourcesContent":["import { Container } from './container.js';\nimport { ConsoleLogger, Logger } from './logger.js';\nimport { Constructor, Token } from './types.js';\nimport { ServiceProvider } from './service-provider.js';\nimport { ProviderDefinition, Scope, Provider } from './providers.js';\nimport { ReflectionHost } from './reflection.js';\n\nimport { MokeCircularModuleError, MokeBootstrapError, MokeShutdownError } from './errors.js';\n\nexport type ApplicationState = 'created' | 'initializing' | 'ready' | 'closing' | 'closed';\n\nexport class MokeApplicationContext {\n private _state: ApplicationState = 'created';\n private initPromise?: Promise<void>;\n private isRegistrationFrozen = false;\n\n constructor(public readonly container: Container) {}\n\n get state(): ApplicationState {\n return this._state;\n }\n\n get<T>(token: Token<T>): T {\n return this.container.resolve(token);\n }\n\n getAsync<T>(token: Token<T>): Promise<T> {\n return this.container.resolveAsync(token);\n }\n\n register<T>(token: Token<T>, providerDef: ProviderDefinition<T>, scope: Scope = 'singleton'): void {\n if (this._state !== 'created') {\n throw new Error('Cannot register providers after application has been initialized');\n }\n this.container.bind(token, providerDef, scope);\n }\n\n async registerProvider(provider: ServiceProvider): Promise<void> {\n if (this._state !== 'created') {\n throw new Error('Cannot register providers after application has been initialized');\n }\n \n const registeredProviders = Reflect.getMetadata('moke:registeredProviders', provider) || [];\n if (registeredProviders.includes(provider.constructor)) {\n throw new Error(`ServiceProvider ${provider.constructor.name} has already been registered. Use register() instead of registerProvider() for class-based providers.`);\n }\n \n Reflect.defineMetadata('moke:registeredProviders', [...registeredProviders, provider.constructor], provider);\n \n await provider.register?.();\n }\n\n async bootProviders(): Promise<void> {\n const bootQueue: ServiceProvider[] = [];\n \n for (const instance of this.container.getInstantiatedInstances()) {\n if (instance instanceof ServiceProvider) {\n const registered = Reflect.getMetadata('moke:registeredProviders', instance) || [];\n if (registered.length > 0) {\n bootQueue.push(instance);\n }\n }\n }\n \n for (const provider of bootQueue) {\n await provider.boot?.();\n }\n }\n\n async init(): Promise<void> {\n if (this._state === 'ready') return;\n if (this._state === 'closing' || this._state === 'closed') {\n throw new Error('Cannot initialize an application that is closing or closed.');\n }\n\n if (this._state === 'initializing' && this.initPromise) {\n return this.initPromise;\n }\n\n this._state = 'initializing';\n this.initPromise = this._initImpl();\n\n try {\n await this.initPromise;\n this.container.freeze();\n this._state = 'ready';\n } catch (e) {\n this._state = 'created';\n throw e;\n } finally {\n this.initPromise = undefined;\n }\n }\n\n private async _initImpl(): Promise<void> {\n const instances = this.container.getInstantiatedInstances();\n\n // 1. onModuleInit\n for (const instance of instances as any[]) {\n if (typeof instance.onModuleInit === 'function') {\n try {\n await instance.onModuleInit();\n } catch (e: any) {\n throw new MokeBootstrapError('onModuleInit', e, instance.constructor?.name);\n }\n }\n }\n\n // 2. onApplicationBootstrap\n for (const instance of instances as any[]) {\n if (typeof instance.onApplicationBootstrap === 'function') {\n try {\n await instance.onApplicationBootstrap();\n } catch (e: any) {\n throw new MokeBootstrapError('onApplicationBootstrap', e, instance.constructor?.name);\n }\n }\n }\n }\n\n async close(signal?: string): Promise<void> {\n if (this._state === 'closed') return;\n this._state = 'closing';\n\n const shutdownErrors: Error[] = [];\n\n // Reverse order for teardown\n const instances = this.container.getInstantiatedInstances().reverse();\n\n // 1. onApplicationShutdown\n for (const instance of instances as any[]) {\n if (typeof instance.onApplicationShutdown === 'function') {\n try {\n await instance.onApplicationShutdown(signal);\n } catch (e: any) {\n shutdownErrors.push(e);\n }\n }\n }\n\n // 2. onModuleDestroy\n for (const instance of instances as any[]) {\n if (typeof instance.onModuleDestroy === 'function') {\n try {\n await instance.onModuleDestroy();\n } catch (e: any) {\n shutdownErrors.push(e);\n }\n }\n }\n\n this.container.dispose();\n this._state = 'closed';\n\n if (shutdownErrors.length > 0) {\n throw new MokeShutdownError(shutdownErrors);\n }\n }\n}\n\nexport class MokeFactory {\n /**\n * Creates a MokeApplicationContext, compiling the module tree.\n * Does not automatically call `init()`.\n */\n static async createApplicationContext(module: Constructor<unknown>): Promise<MokeApplicationContext> {\n const container = new Container();\n \n // Core default registrations\n container.instance(ConsoleLogger, new ConsoleLogger());\n container.bind('Logger', { useExisting: ConsoleLogger });\n \n await this.compileModuleAsync(module, container, new Set(), []);\n await container.resolveAsync(module); // Resolve the root module to trigger graph instantiation\n \n return new MokeApplicationContext(container);\n }\n\n private static async compileModuleAsync(module: Constructor<unknown>, container: Container, resolved: Set<unknown>, path: Constructor<unknown>[]) {\n if (path.includes(module)) {\n throw new MokeCircularModuleError([...path, module]);\n }\n \n if (resolved.has(module)) return;\n\n path.push(module);\n\n const metadata = ReflectionHost.getModuleMetadata(module);\n \n if (!metadata) {\n container.singleton(module);\n resolved.add(module);\n path.pop();\n return;\n }\n\n if (metadata.imports) {\n for (const imported of metadata.imports) {\n await this.compileModuleAsync(imported, container, resolved, path);\n }\n }\n\n if (metadata.providers) {\n for (const provider of metadata.providers) {\n if (typeof provider === 'function') {\n // Provide default singleton bindings only if not already bound\n // In an encapsulated model, this logic will branch. Here in the flattened model, we skip if already explicitly bound.\n if (!container.hasOwn(provider)) {\n container.singleton(provider);\n }\n } else {\n const providerDef = provider as Provider;\n if (providerDef.provide && !container.hasOwn(providerDef.provide)) {\n container.register(providerDef);\n }\n }\n }\n }\n\n // Exports behavior in core is currently flattened globally.\n // In Model B (Encapsulated), each module would get a child container,\n // and exports would be forwarded.\n // For Moke's simplicity vs NestJS-like complexity, we use a flattened global DI by default.\n\n if (!container.hasOwn(module)) {\n container.singleton(module);\n }\n \n resolved.add(module);\n path.pop();\n }\n}\n"]}
|
package/dist/container.d.ts
CHANGED
|
@@ -1,17 +1,50 @@
|
|
|
1
1
|
import 'reflect-metadata';
|
|
2
2
|
import { Token } from './types.js';
|
|
3
|
-
import { ProviderDefinition, Scope } from './providers.js';
|
|
3
|
+
import { Provider, ProviderDefinition, Scope, Resolver } from './providers.js';
|
|
4
|
+
interface ProviderRegistration {
|
|
5
|
+
provider: Provider;
|
|
6
|
+
scope: Scope;
|
|
7
|
+
instance?: unknown;
|
|
8
|
+
asyncPromise?: Promise<unknown>;
|
|
9
|
+
}
|
|
4
10
|
export declare class Container {
|
|
11
|
+
private parent?;
|
|
5
12
|
private registrations;
|
|
13
|
+
private instantiatedInstances;
|
|
14
|
+
private isDisposed;
|
|
15
|
+
private isActiveResolution;
|
|
16
|
+
private isFrozen;
|
|
17
|
+
constructor(parent?: Container | undefined);
|
|
18
|
+
createChild(): Container;
|
|
19
|
+
freeze(): void;
|
|
20
|
+
unfrozen(): boolean;
|
|
21
|
+
register<T>(provider: Provider<T>, scope?: Scope): void;
|
|
6
22
|
bind<T>(token: Token<T>, providerDef: ProviderDefinition<T>, scope?: Scope): void;
|
|
23
|
+
override<T>(token: Token<T>, providerDef: ProviderDefinition<T>, scope?: Scope): void;
|
|
24
|
+
private internalBind;
|
|
7
25
|
singleton<T>(token: Token<T>, providerDef?: ProviderDefinition<T>): void;
|
|
26
|
+
scoped<T>(token: Token<T>, providerDef?: ProviderDefinition<T>): void;
|
|
8
27
|
transient<T>(token: Token<T>, providerDef?: ProviderDefinition<T>): void;
|
|
9
28
|
instance<T>(token: Token<T>, value: T): void;
|
|
10
|
-
factory<T>(token: Token<T>, factory: (
|
|
29
|
+
factory<T>(token: Token<T>, factory: (resolver: Resolver) => T | Promise<T>, scope?: Scope): void;
|
|
11
30
|
has<T>(token: Token<T>): boolean;
|
|
31
|
+
hasOwn<T>(token: Token<T>): boolean;
|
|
12
32
|
resolve<T>(token: Token<T>): T;
|
|
13
|
-
|
|
14
|
-
|
|
33
|
+
resolveAsync<T>(token: Token<T>): Promise<T>;
|
|
34
|
+
dispose(): void;
|
|
35
|
+
getInstantiatedInstances(): unknown[];
|
|
36
|
+
protected hasRegistration(key: unknown): boolean;
|
|
37
|
+
protected getRegistrationRecursively(key: unknown): ProviderRegistration | undefined;
|
|
38
|
+
protected ensureRegistered(key: unknown, token: Token<unknown>, path: unknown[]): void;
|
|
39
|
+
private createResolutionProxy;
|
|
40
|
+
private internalResolveSync;
|
|
41
|
+
private internalResolveAsync;
|
|
42
|
+
private resolveProviderSync;
|
|
43
|
+
private resolveProviderAsync;
|
|
44
|
+
private instantiateClassSync;
|
|
45
|
+
private instantiateClassAsync;
|
|
46
|
+
private getConstructorInjections;
|
|
15
47
|
private getTokenKey;
|
|
16
48
|
}
|
|
49
|
+
export {};
|
|
17
50
|
//# sourceMappingURL=container.d.ts.map
|
package/dist/container.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../src/container.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAC1B,OAAO,EAAe,KAAK,EAAkB,MAAM,YAAY,CAAC;AAChE,OAAO,
|
|
1
|
+
{"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../src/container.ts"],"names":[],"mappings":"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"}
|