katagami 1.1.0 → 2.1.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.
@@ -0,0 +1,86 @@
1
+ import {
2
+ ContainerError,
3
+ buildCircularPath,
4
+ tokenToString
5
+ } from "../index-g50fxds1.js";
6
+ import {
7
+ INTERNALS
8
+ } from "../index-jx8b52m0.js";
9
+
10
+ // src/scope/index.ts
11
+ function createScope(source) {
12
+ const internals = source[INTERNALS];
13
+ if (internals.isDisposed()) {
14
+ throw new ContainerError("Cannot create a scope from a disposed container.");
15
+ }
16
+ return new Scope(internals.registrations, internals.instances);
17
+ }
18
+
19
+ class Scope {
20
+ registrations;
21
+ singletonInstances;
22
+ scopedInstances;
23
+ resolvingTokens;
24
+ disposed = false;
25
+ [INTERNALS];
26
+ constructor(registrations, singletonInstances) {
27
+ this.registrations = registrations;
28
+ this.singletonInstances = singletonInstances;
29
+ this.scopedInstances = new Map;
30
+ this.resolvingTokens = new Set;
31
+ this[INTERNALS] = {
32
+ instances: this.singletonInstances,
33
+ isDisposed: () => this.disposed,
34
+ markDisposed: () => {
35
+ this.disposed = true;
36
+ },
37
+ ownInstances: this.scopedInstances,
38
+ registrations: this.registrations
39
+ };
40
+ }
41
+ resolve(token) {
42
+ return this.resolveToken(token, true);
43
+ }
44
+ tryResolve(token) {
45
+ return this.resolveToken(token, false);
46
+ }
47
+ resolveToken(token, required) {
48
+ if (this.disposed) {
49
+ throw new ContainerError("Cannot resolve from a disposed scope.");
50
+ }
51
+ const singletonCached = this.singletonInstances.get(token);
52
+ if (singletonCached !== undefined) {
53
+ return singletonCached;
54
+ }
55
+ const scopedCached = this.scopedInstances.get(token);
56
+ if (scopedCached !== undefined) {
57
+ return scopedCached;
58
+ }
59
+ const registration = this.registrations.get(token);
60
+ if (registration === undefined) {
61
+ if (required) {
62
+ throw new ContainerError(`Token "${tokenToString(token)}" is not registered.`);
63
+ }
64
+ return;
65
+ }
66
+ if (this.resolvingTokens.has(token)) {
67
+ throw new ContainerError(`Circular dependency detected: ${buildCircularPath(this.resolvingTokens, token)}`);
68
+ }
69
+ this.resolvingTokens.add(token);
70
+ try {
71
+ const instance = registration.factory(this);
72
+ if (registration.lifetime === "singleton") {
73
+ this.singletonInstances.set(token, instance);
74
+ } else if (registration.lifetime === "scoped") {
75
+ this.scopedInstances.set(token, instance);
76
+ }
77
+ return instance;
78
+ } finally {
79
+ this.resolvingTokens.delete(token);
80
+ }
81
+ }
82
+ }
83
+ export {
84
+ createScope,
85
+ Scope
86
+ };
package/package.json CHANGED
@@ -1,14 +1,25 @@
1
1
  {
2
2
  "name": "katagami",
3
- "version": "1.1.0",
3
+ "version": "2.1.0",
4
4
  "description": "Lightweight DI container for TypeScript and JavaScript — full type inference, no decorators, no reflect-metadata, hybrid class & PropertyKey tokens.",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
+ "sideEffects": false,
7
8
  "exports": {
8
9
  ".": {
9
10
  "types": "./dist/index.d.ts",
10
11
  "import": "./dist/index.js",
11
12
  "require": "./dist/index.cjs"
13
+ },
14
+ "./scope": {
15
+ "types": "./dist/scope/index.d.ts",
16
+ "import": "./dist/scope/index.js",
17
+ "require": "./dist/scope/index.cjs"
18
+ },
19
+ "./disposable": {
20
+ "types": "./dist/disposable/index.d.ts",
21
+ "import": "./dist/disposable/index.js",
22
+ "require": "./dist/disposable/index.cjs"
12
23
  }
13
24
  },
14
25
  "main": "./dist/index.cjs",
@@ -34,8 +45,8 @@
34
45
  "clean": "rm -rf dist",
35
46
  "build": "bun run clean && bun run build:types && bun run build:esm && bun run build:cjs",
36
47
  "build:types": "tsc -p tsconfig.build.json",
37
- "build:esm": "bun build ./src/index.ts --outdir dist --format esm",
38
- "build:cjs": "bun build ./src/index.ts --outfile dist/index.cjs --format cjs",
48
+ "build:esm": "bun build ./src/index.ts ./src/scope/index.ts ./src/disposable/index.ts --outdir dist --format esm --splitting",
49
+ "build:cjs": "bun build ./src/index.ts --outfile dist/index.cjs --format cjs && bun build ./src/scope/index.ts --outfile dist/scope/index.cjs --format cjs && bun build ./src/disposable/index.ts --outfile dist/disposable/index.cjs --format cjs",
39
50
  "test": "bun test --coverage --dots",
40
51
  "prepublishOnly": "bun run build",
41
52
  "check": "bun run format",
package/README.de.md DELETED
@@ -1,438 +0,0 @@
1
- [English](./README.md) | [日本語](./README.ja.md) | [한국어](./README.ko.md) | [繁體中文](./README.zh-TW.md) | [简体中文](./README.zh-CN.md) | [Español](./README.es.md) | [Deutsch](./README.de.md) | [Français](./README.fr.md)
2
-
3
- # Katagami
4
-
5
- Leichtgewichtiger TypeScript-DI-Container mit vollständiger Typinferenz.
6
-
7
- [![npm version](https://img.shields.io/npm/v/katagami)](https://www.npmjs.com/package/katagami)
8
- [![license](https://img.shields.io/npm/l/katagami)](https://github.com/hiroiku/katagami/blob/master/LICENSE)
9
- [![bundle size](https://img.shields.io/bundlephobia/minzip/katagami)](https://bundlephobia.com/package/katagami)
10
-
11
- > Der Name stammt von 型紙 _(katagami)_ — präzise Schablonenpapiere, die in der traditionellen japanischen Färbetechnik verwendet werden, um exakte Muster auf Stoff zu übertragen. Mehrere Schablonen werden übereinandergelegt, um kunstvolle Designs zu komponieren, genau wie sich Typen mit jedem Methodenkettenaufruf ansammeln. Eine Schablone benötigt nur Papier und einen Pinsel, keine aufwendige Maschinerie — ebenso benötigt Katagami keine Decorators oder Metadaten-Mechanismen und funktioniert mit jedem Build-Tool direkt einsatzbereit. Und wie Schablonen, die mit verschiedenen Stoffen und Techniken funktionieren, passt sich Katagami an TypeScript und JavaScript, Klassen-Token und PropertyKey-Token an — ein hybrider Ansatz für strikte, komponierbare DI.
12
-
13
- ## Funktionen
14
-
15
- | Funktion | Beschreibung |
16
- | -------------------------------------- | ------------------------------------------------------------------------------------------------------- |
17
- | Vollständige Typinferenz | Typen akkumulieren sich durch Methodenverkettung; nicht registrierte Token erzeugen Kompilierzeitfehler |
18
- | Drei Lebenszyklen | Singleton, Transient und Scoped mit Kind-Containern |
19
- | Asynchrone Factories | Promise-zurückgebende Factories werden automatisch vom Typsystem verfolgt |
20
- | Erkennung zirkulärer Abhängigkeiten | Klare Fehlermeldungen mit dem vollständigen Zykluspfad |
21
- | Disposable-Unterstützung | TC39 Explicit Resource Management (`Symbol.dispose` / `Symbol.asyncDispose` / `await using`) |
22
- | Verhinderung gefangener Abhängigkeiten | Singleton-/Transient-Factories können nicht auf Scoped-Token zugreifen; wird zur Kompilierzeit erkannt |
23
- | Optionale Auflösung | `tryResolve` gibt `undefined` für nicht registrierte Token zurück statt zu werfen |
24
- | Hybride Token-Strategie | Klassen-Token für strikte Typsicherheit, PropertyKey-Token für Flexibilität |
25
- | Interface-Typ-Map | Übergeben Sie ein Interface an `createContainer<T>()` für reihenfolgeunabhängige Registrierung |
26
- | Null Abhängigkeiten | Keine Decorators, kein reflect-metadata, keine Polyfills |
27
-
28
- ## Installation
29
-
30
- ```bash
31
- npm install katagami
32
- ```
33
-
34
- ## Schnellstart
35
-
36
- ```ts
37
- import { createContainer } from 'katagami';
38
-
39
- class Logger {
40
- log(msg: string) {
41
- console.log(msg);
42
- }
43
- }
44
-
45
- class UserService {
46
- constructor(private logger: Logger) {}
47
- greet(name: string) {
48
- this.logger.log(`Hello, ${name}`);
49
- }
50
- }
51
-
52
- const container = createContainer()
53
- .registerSingleton(Logger, () => new Logger())
54
- .registerSingleton(UserService, r => new UserService(r.resolve(Logger)));
55
-
56
- const userService = container.resolve(UserService);
57
- // ^? UserService (vollständig inferiert)
58
- userService.greet('world');
59
- ```
60
-
61
- ## Warum Katagami
62
-
63
- Die meisten TypeScript-DI-Container basieren auf Decorators, reflect-metadata oder stringbasierten Token — jeder mit Kompromissen bei Toolkompatibilität, Typsicherheit oder Bundle-Größe. Katagami verfolgt einen anderen Ansatz.
64
-
65
- ### Keine Decorators, kein reflect-metadata
66
-
67
- Decorator-basierte DI erfordert die Compiler-Optionen `experimentalDecorators` und `emitDecoratorMetadata`. Moderne Build-Tools wie esbuild und Vite (Standardkonfiguration) unterstützen `emitDecoratorMetadata` nicht, und der TC39-Standarddecorators-Vorschlag enthält kein Äquivalent für die automatische Typ-Metadaten-Emission. Katagami ist von nichts davon abhängig — es funktioniert mit jedem Build-Tool direkt einsatzbereit.
68
-
69
- ### Vollständige Typinferenz durch Klassen-Token
70
-
71
- String-Token-DI zwingt Sie, manuelle Token-zu-Typ-Zuordnungen zu pflegen. Parameternamen-Matching bricht bei Minifizierung. Katagami verwendet Klassen direkt als Token, sodass `resolve` automatisch den korrekten Rückgabetyp inferiert — synchron oder `Promise` — ohne zusätzliche Annotationen.
72
-
73
- ### Methodenketten-Typakkumulation
74
-
75
- Typen akkumulieren sich mit jedem `register`-Aufruf. Innerhalb einer Factory akzeptiert der Resolver nur Token, die an diesem Punkt in der Kette bereits registriert wurden. Das Auflösen eines nicht registrierten Tokens ist ein Kompilierzeitfehler, keine Laufzeitüberraschung.
76
-
77
- ### Hybride Token-Strategie
78
-
79
- Klassen-Token bieten strikte, reihenfolgeabhängige Typsicherheit durch Methodenverkettung. Aber manchmal möchten Sie eine Reihe von Services vorab definieren und in beliebiger Reihenfolge registrieren. Übergeben Sie ein Interface an `createContainer<T>()` und verwenden Sie PropertyKey-Token — die Typ-Map wird zum Erstellungszeitpunkt festgelegt, sodass die Registrierungsreihenfolge keine Rolle spielt.
80
-
81
- ### Null Abhängigkeiten
82
-
83
- Keine Laufzeitabhängigkeiten, keine Polyfills. Kein Bedarf, reflect-metadata (~50 KB unminifiziert) zu Ihrem Bundle hinzuzufügen.
84
-
85
- ## Leitfaden
86
-
87
- ### Singleton und Transient
88
-
89
- Singleton erstellt die Instanz beim ersten `resolve` und speichert sie im Cache. Transient erstellt jedes Mal eine neue Instanz.
90
-
91
- ```ts
92
- import { createContainer } from 'katagami';
93
-
94
- class Database {
95
- constructor(public id = Math.random()) {}
96
- }
97
-
98
- class RequestHandler {
99
- constructor(public id = Math.random()) {}
100
- }
101
-
102
- const container = createContainer()
103
- .registerSingleton(Database, () => new Database())
104
- .registerTransient(RequestHandler, () => new RequestHandler());
105
-
106
- // Singleton — jedes Mal dieselbe Instanz
107
- container.resolve(Database) === container.resolve(Database); // true
108
-
109
- // Transient — jedes Mal eine neue Instanz
110
- container.resolve(RequestHandler) === container.resolve(RequestHandler); // false
111
- ```
112
-
113
- ### Scoped-Lebenszyklus und Kind-Container
114
-
115
- Scoped-Registrierungen verhalten sich innerhalb eines Scopes wie Singletons, erzeugen aber in jedem neuen Scope eine neue Instanz. Verwenden Sie `createScope()`, um einen Kind-Container zu erstellen. Scoped-Token können nicht vom Root-Container aufgelöst werden.
116
-
117
- ```ts
118
- import { createContainer } from 'katagami';
119
-
120
- class DbPool {
121
- constructor(public name = 'main') {}
122
- }
123
-
124
- class RequestContext {
125
- constructor(public id = Math.random()) {}
126
- }
127
-
128
- const root = createContainer()
129
- .registerSingleton(DbPool, () => new DbPool())
130
- .registerScoped(RequestContext, () => new RequestContext());
131
-
132
- // Einen Scope für jede Anfrage erstellen
133
- const scope1 = root.createScope();
134
- const scope2 = root.createScope();
135
-
136
- // Scoped — gleich innerhalb eines Scopes, unterschiedlich zwischen Scopes
137
- scope1.resolve(RequestContext) === scope1.resolve(RequestContext); // true
138
- scope1.resolve(RequestContext) === scope2.resolve(RequestContext); // false
139
-
140
- // Singleton — über alle Scopes geteilt
141
- scope1.resolve(DbPool) === scope2.resolve(DbPool); // true
142
- ```
143
-
144
- Scopes können auch verschachtelt werden. Jeder verschachtelte Scope hat seinen eigenen Scoped-Instanz-Cache, während Singletons mit dem Eltern-Scope geteilt werden:
145
-
146
- ```ts
147
- const parentScope = root.createScope();
148
- const childScope = parentScope.createScope();
149
-
150
- // Jeder verschachtelte Scope bekommt eigene Scoped-Instanzen
151
- parentScope.resolve(RequestContext) === childScope.resolve(RequestContext); // false
152
-
153
- // Singletons werden weiterhin geteilt
154
- parentScope.resolve(DbPool) === childScope.resolve(DbPool); // true
155
- ```
156
-
157
- ### Asynchrone Factories
158
-
159
- Factories, die ein `Promise` zurückgeben, werden automatisch vom Typsystem verfolgt. Wenn Sie ein asynchrones Token auflösen, ist der Rückgabetyp `Promise<V>` statt `V`:
160
-
161
- ```ts
162
- import { createContainer } from 'katagami';
163
-
164
- class Database {
165
- constructor(public connected: boolean) {}
166
- }
167
-
168
- class Logger {
169
- log(msg: string) {
170
- console.log(msg);
171
- }
172
- }
173
-
174
- const container = createContainer()
175
- .registerSingleton(Logger, () => new Logger())
176
- .registerSingleton(Database, async () => {
177
- await new Promise(r => setTimeout(r, 100)); // Asynchrone Initialisierung simulieren
178
- return new Database(true);
179
- });
180
-
181
- const logger = container.resolve(Logger);
182
- // ^? Logger
183
-
184
- const db = await container.resolve(Database);
185
- // ^? Promise<Database> (nach await → Database)
186
- db.connected; // true
187
- ```
188
-
189
- Asynchrone Factories können sowohl von synchronen als auch von asynchronen Registrierungen abhängen:
190
-
191
- ```ts
192
- const container = createContainer()
193
- .registerSingleton(Logger, () => new Logger())
194
- .registerSingleton(Database, async r => {
195
- const logger = r.resolve(Logger); // synchron → Logger
196
- logger.log('Verbindung wird hergestellt...');
197
- return new Database(true);
198
- });
199
- ```
200
-
201
- ### Erkennung zirkulärer Abhängigkeiten
202
-
203
- Katagami verfolgt, welche Token gerade aufgelöst werden. Wenn eine zirkuläre Abhängigkeit gefunden wird, wird ein `ContainerError` mit einer klaren Nachricht geworfen, die den vollständigen Zykluspfad zeigt:
204
-
205
- ```ts
206
- import { createContainer } from 'katagami';
207
-
208
- class ServiceA {
209
- constructor(public b: ServiceB) {}
210
- }
211
-
212
- class ServiceB {
213
- constructor(public a: ServiceA) {}
214
- }
215
-
216
- const container = createContainer()
217
- .registerSingleton(ServiceA, r => new ServiceA(r.resolve(ServiceB)))
218
- .registerSingleton(ServiceB, r => new ServiceB(r.resolve(ServiceA)));
219
-
220
- container.resolve(ServiceA);
221
- // ContainerError: Circular dependency detected: ServiceA -> ServiceB -> ServiceA
222
- ```
223
-
224
- Indirekte Zyklen werden ebenfalls erkannt:
225
-
226
- ```
227
- ContainerError: Circular dependency detected: ServiceX -> ServiceY -> ServiceZ -> ServiceX
228
- ```
229
-
230
- ### Disposable-Unterstützung
231
-
232
- Sowohl `Container` als auch `Scope` implementieren `AsyncDisposable`. Bei der Freigabe werden verwaltete Instanzen in umgekehrter Erstellungsreihenfolge (LIFO) durchlaufen und ihre `[Symbol.asyncDispose]()` oder `[Symbol.dispose]()` Methoden werden automatisch aufgerufen.
233
-
234
- ```ts
235
- import { createContainer } from 'katagami';
236
-
237
- class Connection {
238
- async [Symbol.asyncDispose]() {
239
- console.log('Connection closed');
240
- }
241
- }
242
-
243
- // Manuelle Freigabe
244
- const container = createContainer().registerSingleton(Connection, () => new Connection());
245
-
246
- container.resolve(Connection);
247
- await container[Symbol.asyncDispose]();
248
- // => "Connection closed"
249
- ```
250
-
251
- Mit `await using` werden Scopes am Ende des Blocks automatisch freigegeben:
252
-
253
- ```ts
254
- const root = createContainer()
255
- .registerSingleton(DbPool, () => new DbPool())
256
- .registerScoped(Connection, () => new Connection());
257
-
258
- {
259
- await using scope = root.createScope();
260
- const conn = scope.resolve(Connection);
261
- // ... conn verwenden ...
262
- } // Scope wird hier freigegeben — Connection wird bereinigt, DbPool nicht
263
- ```
264
-
265
- Die Scope-Freigabe betrifft nur Scoped-Instanzen. Singleton-Instanzen gehören dem Root-Container und werden freigegeben, wenn der Container selbst freigegeben wird.
266
-
267
- ### Interface-Typ-Map
268
-
269
- Wenn Sie ein Interface an `createContainer<T>()` übergeben, werden PropertyKey-Token vom Interface typisiert, anstatt durch Verkettung akkumuliert zu werden. Das bedeutet, Sie können Token in beliebiger Reihenfolge registrieren und auflösen:
270
-
271
- ```ts
272
- import { createContainer } from 'katagami';
273
-
274
- class Logger {
275
- log(msg: string) {
276
- console.log(msg);
277
- }
278
- }
279
-
280
- interface Services {
281
- logger: Logger;
282
- greeting: string;
283
- }
284
-
285
- const container = createContainer<Services>()
286
- // 'greeting' kann 'logger' referenzieren, obwohl es später registriert wird
287
- .registerSingleton('greeting', r => {
288
- r.resolve('logger').log('Greeting wird erstellt...');
289
- return 'Hello!';
290
- })
291
- .registerSingleton('logger', () => new Logger());
292
-
293
- const greeting = container.resolve('greeting');
294
- // ^? string
295
- ```
296
-
297
- ### Hybride Token-Strategie
298
-
299
- Sie können beide Ansätze mischen — verwenden Sie Klassen-Token für reihenfolgeabhängige Typsicherheit und PropertyKey-Token für reihenfolgeunabhängige Flexibilität:
300
-
301
- ```ts
302
- const container = createContainer<Services>()
303
- .registerSingleton(Logger, () => new Logger())
304
- .registerSingleton('logger', () => new Logger())
305
- .registerSingleton('greeting', r => {
306
- r.resolve(Logger).log('Greeting wird erstellt...');
307
- return 'Hello!';
308
- });
309
- ```
310
-
311
- ### Verhinderung gefangener Abhängigkeiten
312
-
313
- Eine „gefangene Abhängigkeit" tritt auf, wenn ein langlebiger Service (Singleton oder Transient) einen kurzlebigen Service (Scoped) einfängt und über seinen vorgesehenen Scope hinaus am Leben hält. Katagami verhindert dies zur Kompilierzeit — Singleton- und Transient-Factories erhalten nur einen Resolver, der auf nicht-Scoped-Token beschränkt ist:
314
-
315
- ```ts
316
- import { createContainer } from 'katagami';
317
-
318
- class DbPool {}
319
- class RequestContext {}
320
-
321
- const container = createContainer()
322
- .registerScoped(RequestContext, () => new RequestContext())
323
- // @ts-expect-error — Singleton-Factory kann keine Scoped-Token auflösen
324
- .registerSingleton(DbPool, r => new DbPool(r.resolve(RequestContext)));
325
- ```
326
-
327
- Scoped-Factories hingegen können sowohl Scoped- als auch nicht-Scoped-Token auflösen:
328
-
329
- ```ts
330
- const container = createContainer()
331
- .registerSingleton(DbPool, () => new DbPool())
332
- .registerScoped(RequestContext, r => {
333
- r.resolve(DbPool); // OK — Scoped-Factory kann Singleton-Token auflösen
334
- return new RequestContext();
335
- });
336
- ```
337
-
338
- ### Optionale Auflösung (tryResolve)
339
-
340
- Wenn Sie optionale Abhängigkeiten handhaben möchten oder prüfen wollen, ob ein Token registriert ist, ohne einen Fehler zu werfen, verwenden Sie `tryResolve`. Im Gegensatz zu `resolve` gibt es `undefined` für nicht registrierte Token zurück, anstatt einen `ContainerError` zu werfen:
341
-
342
- ```ts
343
- import { createContainer } from 'katagami';
344
-
345
- class Logger {
346
- log(msg: string) {
347
- console.log(msg);
348
- }
349
- }
350
-
351
- class Analytics {
352
- track(event: string) {
353
- console.log(`Track: ${event}`);
354
- }
355
- }
356
-
357
- const container = createContainer().registerSingleton(Logger, () => new Logger());
358
-
359
- // resolve wirft für nicht registrierte Token
360
- container.resolve(Analytics); // ContainerError: Token "Analytics" is not registered.
361
-
362
- // tryResolve gibt undefined für nicht registrierte Token zurück
363
- const analytics = container.tryResolve(Analytics);
364
- // ^? Analytics | undefined
365
- if (analytics) {
366
- analytics.track('event');
367
- }
368
- ```
369
-
370
- `tryResolve` ist besonders nützlich für optionale Abhängigkeiten in Factories. Im Gegensatz zu `resolve` akzeptiert es nicht registrierte Token ohne Kompilierungsfehler:
371
-
372
- ```ts
373
- const container = createContainer()
374
- .registerSingleton(Logger, () => new Logger())
375
- .registerSingleton('UserService', r => {
376
- const logger = r.tryResolve(Logger); // Optionale Abhängigkeit
377
- const analytics = r.tryResolve(Analytics); // Kein Kompilierungsfehler, obwohl Analytics nicht registriert ist
378
-
379
- return {
380
- greet(name: string) {
381
- logger?.log(`Hello, ${name}`);
382
- analytics?.track('user_greeted');
383
- },
384
- };
385
- });
386
- ```
387
-
388
- `tryResolve` wirft immer noch einen `ContainerError` bei zirkulären Abhängigkeiten und Operationen auf freigegebenen Containern/Scopes — nur nicht registrierte Token geben `undefined` zurück.
389
-
390
- ## API
391
-
392
- ### `createContainer<T, ScopedT>()`
393
-
394
- Erstellt einen neuen DI-Container. Übergeben Sie ein Interface als `T`, um die Typ-Map für PropertyKey-Token zu definieren. Übergeben Sie `ScopedT`, um eine separate Typ-Map für Scoped-PropertyKey-Token zu definieren (reihenfolgeunabhängig, genau wie `T`).
395
-
396
- ### `container.registerSingleton(token, factory)`
397
-
398
- Registriert eine Factory als Singleton. Die Instanz wird beim ersten `resolve` erstellt und danach gecacht. Gibt den Container für Methodenverkettung zurück.
399
-
400
- ### `container.registerTransient(token, factory)`
401
-
402
- Registriert eine Factory als Transient. Bei jedem `resolve` wird eine neue Instanz erstellt. Gibt den Container für Methodenverkettung zurück.
403
-
404
- ### `container.registerScoped(token, factory)`
405
-
406
- Registriert eine Factory als Scoped. Innerhalb eines Scopes wird die Instanz beim ersten `resolve` erstellt und für diesen Scope gecacht. Jeder Scope pflegt seinen eigenen Cache. Scoped-Token können nicht vom Root-Container aufgelöst werden. Gibt den Container für Methodenverkettung zurück.
407
-
408
- ### `container.resolve(token)`
409
-
410
- Löst die Instanz für das gegebene Token auf und gibt sie zurück. Wirft `ContainerError`, wenn das Token nicht registriert ist oder eine zirkuläre Abhängigkeit erkannt wird.
411
-
412
- ### `container.tryResolve(token)` / `scope.tryResolve(token)`
413
-
414
- Versucht, die Instanz für das gegebene Token aufzulösen. Gibt `undefined` zurück, wenn das Token nicht registriert ist, anstatt zu werfen. Wirft immer noch `ContainerError` bei zirkulären Abhängigkeiten oder Operationen auf freigegebenen Containern/Scopes.
415
-
416
- ### `container.createScope()`
417
-
418
- Erstellt einen neuen `Scope` (Kind-Container). Der Scope erbt alle Registrierungen vom Eltern-Container. Singleton-Instanzen werden mit dem Eltern-Container geteilt, während Scoped-Instanzen lokal zum Scope sind.
419
-
420
- ### `Scope`
421
-
422
- Ein Scoped-Kind-Container, erstellt durch `createScope()`. Bietet `resolve(token)`, `tryResolve(token)`, `createScope()` (für verschachtelte Scopes) und `[Symbol.asyncDispose]()`.
423
-
424
- ### `container[Symbol.asyncDispose]()` / `scope[Symbol.asyncDispose]()`
425
-
426
- Gibt alle verwalteten Instanzen in umgekehrter Erstellungsreihenfolge (LIFO) frei. Ruft `[Symbol.asyncDispose]()` oder `[Symbol.dispose]()` für jede Instanz auf, die diese implementiert. Idempotent — nachfolgende Aufrufe sind No-Ops. Nach der Freigabe werfen `resolve()` und `createScope()` einen `ContainerError`.
427
-
428
- ### `ContainerError`
429
-
430
- Fehlerklasse, die bei Container-Fehlern geworfen wird, wie z. B. das Auflösen eines nicht registrierten Tokens, zirkuläre Abhängigkeiten oder Operationen auf einem freigegebenen Container/Scope.
431
-
432
- ### `Resolver`
433
-
434
- Typexport, der den an Factory-Callbacks übergebenen Resolver repräsentiert. Nützlich, wenn Sie eine Funktion typisieren müssen, die einen Resolver-Parameter akzeptiert.
435
-
436
- ## Lizenz
437
-
438
- MIT