katagami 3.0.2 → 3.0.3

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
@@ -57,43 +57,88 @@ These checks assume narrow tokens and preserved registration types; see the
57
57
 
58
58
  ### Library comparison
59
59
 
60
- Reviewed **2026-09-11**, against the npm `latest` versions below and official documentation.
61
- The tables describe the built-in APIs; add-ons and application-specific wrappers can change the
62
- trade-offs. [Version sources and detailed comparison notes](./docs/choosing-di.md#comparison-sources)
63
- explain the distinctions.
64
-
65
- | Library / reviewed version | Dependency typing and missing registrations | Scoped-dependency handling | DI setup |
66
- | --- | --- | --- | --- |
67
- | **Katagami 3.0.2** | **Accumulated literal/unique-symbol tokens; missing required tokens rejected** | **Scoped tokens excluded from singleton/transient factory resolvers** | **No decorators or metadata; zero runtime dependencies** |
68
- | [InversifyJS 8.2.3](https://inversify.io/docs/fundamentals/binding/) | Typed identifiers and bindings; binding existence checked at runtime | Declared binding scopes; not a scope-filtered resolver type | Metadata-based class injection; explicit value/factory bindings also available |
69
- | [tsyringe 4.10.0](https://github.com/microsoft/tsyringe#readme) | Class/generic resolution types; registrations checked at runtime | Runtime lifetime settings; factory receives the container | Decorators and a Reflect metadata polyfill for class injection |
70
- | [TypeDI 0.10.0](https://github.com/typestack/typedi/tree/v0.10.0) | Class and `Token<T>` types; registrations checked at runtime | Shared/transient services and named containers | Decorators and `reflect-metadata` in the TypeScript setup |
71
- | [Awilix 13.0.5](https://github.com/jeffijoe/awilix#readme) | Registration-derived cradle types; broad `resolve` overload still accepts unknown names | `strict: true` checks lifetime leaks at runtime | No decorators or metadata |
72
- | [NestJS 12.0.1](https://docs.nestjs.com/fundamentals/custom-providers) | Typed providers; module/provider graph resolved at runtime | Request scope propagates to dependent providers | Framework modules and metadata-based class injection |
73
- | [Effect 3.22.2](https://effect.website/docs/v3/requirements-management/layers) | Service requirements tracked in `Effect` / `Layer` types | Typed `Scope` requirements and resource finalizers; a different lifetime model | No decorators or metadata; Effect's service/layer model |
74
- | [typed-inject 5.0.0](https://github.com/nicojs/typed-inject#readme) | Accumulated string tokens and checked `inject` tuples | Singleton/transient providers and child injectors; no separate scoped lifetime | No decorators or metadata; zero runtime dependencies |
75
-
76
- Awilix's inferred `cradle` and typed-inject's registration types are real compile-time features.
77
- Katagami's distinction is the combination of accumulated registration checks with **scope-filtered
78
- factory resolvers**, using direct `r.resolve(token)` calls. Effect also checks unsatisfied service
79
- requirements, within its broader effect and resource model.
80
-
81
- | Library | Lifetimes / scope model | Asynchronous services | Resource cleanup |
82
- | --- | --- | --- | --- |
83
- | **Katagami** | **Singleton, transient, scoped; nested scopes** | **Inferred `Promise<T>`; explicitly await dependencies** | **`Symbol.dispose` / `Symbol.asyncDispose` via `disposable()`; `await using`** |
84
- | InversifyJS | Singleton, transient, request (one resolution graph); container hierarchy | Async bindings via `getAsync` / `getAllAsync`; awaits dependencies | Singleton deactivation handlers |
85
- | tsyringe | Singleton, transient, resolution-scoped, container-scoped | Factories can return Promise-valued services; consumer handles the Promise | `container.dispose()` for constructed disposable instances |
86
- | TypeDI | Shared or transient services; named containers | Promise-valued services; consumer handles the Promise | `reset()` / removal can call `destroy()`; returned Promise is not awaited |
87
- | Awilix | Singleton, transient, scoped | Promise-valued factories; consumer handles the Promise | Registered disposers for cached singleton/scoped values |
88
- | NestJS | Singleton, transient, HTTP request; scope propagation | Async providers are awaited before dependent construction | Application lifecycle hooks; not called for request-scoped classes |
89
- | Effect | Memoized layers and explicit resource scopes | Effectful acquisition, including async effects | Scope finalizers and `acquireRelease` |
90
- | typed-inject | Singleton, transient; disposable child injectors | Promise-valued factories with inferred return types | `injector.dispose()` awaits owned instances' `dispose()` |
91
-
92
- “Request” is not identical across libraries: InversifyJS uses a resolution graph, while NestJS can
93
- use an HTTP request. Katagami and Awilix let you create an explicit scope per request.
94
- Returning a Promise and automatically awaiting dependencies are also different capabilities.
95
- See [composition, optional/multiple resolution and tooling](./docs/choosing-di.md#composition-and-tooling)
96
- for the rest of the feature comparison.
60
+ **27 features across eight libraries.** Reviewed **2026-09-11**, against the npm `latest`
61
+ versions shown below and official documentation. [Versions, sources and detailed notes](./docs/choosing-di.md#comparison-sources).
62
+
63
+ **✅ Built-in support · ⚠️ Conditions, a different model or application composition · ➖ No built-in support for this specific capability.**
64
+ Short labels identify the actual API or limitation.
65
+
66
+ #### Type safety and setup
67
+
68
+ | Feature | **Katagami**<br>**3.0.3** | InversifyJS<br>8.2.3 | tsyringe<br>4.10.0 | TypeDI<br>0.10.0 | Awilix<br>13.0.5 | NestJS<br>12.0.1 | Effect<br>3.22.2 | typed-inject<br>5.0.0 |
69
+ | --- | --- | --- | --- | --- | --- | --- | --- | --- |
70
+ | **Runtime requirements** | **✅ Standard TypeScript** | ⚠️ Reflect metadata for class DI | ⚠️ Reflect metadata for class DI | ⚠️ Reflect metadata setup | No DI metadata | ⚠️ Nest modules / metadata | ✅ Effect / Layer APIs | ✅ Standard TypeScript |
71
+ | **Injection style** | **Explicit factories / constructors** | Constructor / property / factory | Constructor / factory | Constructor / property / factory | Proxy / classic / factory | Constructor / property / factory | Functional services / layers | Constructor / factory + `inject` |
72
+ | **Token types** | **Class / string / number / symbol** | Class / string / symbol | Class / string / symbol | Class / string / `Token<T>` | String / symbol | Class / string / symbol | `Context.Tag` | String literals |
73
+ | **Type safety** | **✅ Inferred services + scope checks** | ✅ Typed identifiers / bindings | ✅ Class / generic types | Class / `Token<T>` | Inferred cradle | Typed providers | Typed service requirements | Tokens + `inject` tuples |
74
+ | **Registration-derived types** | **✅ Accumulated tokens** | | | | `register` cradle | | ⚠️ Layer requirements | Accumulated tokens |
75
+ | **Missing required tokens: compile-time check¹** | **✅ Literal / unique-symbol keys** | ➖ Runtime check | ➖ Runtime check | ➖ Runtime check | ⚠️ Cradle only | ➖ Runtime graph | ✅ Unsatisfied requirements | ✅ Literal keys |
76
+ | **Scoped access from singleton/transient factories: compile-time check¹** | **✅ Scoped tokens excluded** | ➖ | ➖ | ➖ | ⚠️ Runtime strict mode | ⚠️ Request-scope propagation | ⚠️ Different `Scope` model | ➖ No scoped lifetime |
77
+ | **Zero runtime dependency packages²** | **✅** | | ➖ | ⚠️ Reflect polyfill installed separately | ⚠️ Browser entry differs | ➖ | ➖ | ✅ |
78
+ | **Tree-shaking support²** | **✅ ESM / subpaths / `sideEffects: false`** | ⚠️ ESM; `sideEffects: true` | ⚠️ ESM build | ✅ ESM / `sideEffects: false` | ⚠️ ESM / browser builds | ⚠️ ESM / framework setup | ✅ ESM / subpaths / side-effect declaration | ⚠️ ESM build |
79
+
80
+ #### Lifetimes, async services and cleanup
81
+
82
+ | Feature | **Katagami**<br>**3.0.3** | InversifyJS<br>8.2.3 | tsyringe<br>4.10.0 | TypeDI<br>0.10.0 | Awilix<br>13.0.5 | NestJS<br>12.0.1 | Effect<br>3.22.2 | typed-inject<br>5.0.0 |
83
+ | --- | --- | --- | --- | --- | --- | --- | --- | --- |
84
+ | **Lifetimes** | **✅ Singleton / Transient / Scoped** | Singleton / Transient / Request | Singleton / Transient / Resolution / Container | ✅ Shared / Transient | ✅ Singleton / Transient / Scoped | ✅ Singleton / Transient / Request | ⚠️ Memoized / fresh layers + scopes | ✅ Singleton / Transient |
85
+ | **Request / scoped lifetime³** | **✅ Explicit per-request scope** | ⚠️ One resolution graph | ✅ Container / resolution scoped | ⚠️ Named containers | ✅ Explicit per-request scope | HTTP request scope | ⚠️ Resource scopes | ⚠️ Child injectors; no Scoped provider |
86
+ | **Child containers / nested scopes³** | **✅ Nested scopes** | Container hierarchy | ✅ Child containers | ⚠️ Named containers | Child scopes | ⚠️ Module / request contexts | ⚠️ Nested resource scopes | Child injectors |
87
+ | **Async factories** | **✅ Promise-valued factories** | ✅ Async bindings | ✅ Promise-valued factories | Promise-valued services | ✅ Promise-valued factories | Async providers | Effectful acquisition | ✅ Promise-valued factories |
88
+ | **Async result type tracking** | **✅ Inferred `Promise<T>`** | `getAsync<T>` | ⚠️ Promise-valued service type | ⚠️ Promise-valued service type | Inferred `Promise<T>` | ⚠️ Provider / consumer types | ✅ Effect result / error / requirements | ✅ Inferred `Promise<T>` |
89
+ | **Automatically await async dependencies⁴** | **➖ Explicit `await`** | `getAsync` / `getAllAsync` | Consumer awaits | Consumer awaits | Consumer awaits | ✅ Before consumer construction | ✅ Effect composition | ➖ Consumer awaits |
90
+ | **Resource cleanup⁵** | **✅ Disposal symbols / `await using`** | ⚠️ Singleton deactivation | Constructed disposables | ⚠️ `destroy()` on reset / removal | ⚠️ Cached values + disposer | ⚠️ App lifecycle hooks | ✅ Scope finalizers | ✅ Owned disposable instances |
91
+ | **Await asynchronous cleanup⁵** | **✅ `Symbol.asyncDispose`** | ✅ Async deactivation | ✅ `container.dispose()` | ➖ `destroy()` is not awaited | ✅ `container.dispose()` | ⚠️ App hooks; not request-scoped classes | ✅ Effect finalizers | ✅ `injector.dispose()` |
92
+
93
+ #### Composition and advanced features
94
+
95
+ | Feature | **Katagami**<br>**3.0.3** | InversifyJS<br>8.2.3 | tsyringe<br>4.10.0 | TypeDI<br>0.10.0 | Awilix<br>13.0.5 | NestJS<br>12.0.1 | Effect<br>3.22.2 | typed-inject<br>5.0.0 |
96
+ | --- | --- | --- | --- | --- | --- | --- | --- | --- |
97
+ | **Optional resolution** | **✅ `tryResolve` / `tryResolveAll`** | ✅ Optional get / inject | ✅ Optional injection | ⚠️ `has` then `get` | ✅ `allowUnregistered` | ✅ Optional injection | ✅ `serviceOption` | ⚠️ Compose optional values |
98
+ | **Multi-binding** | **✅ `resolveAll`** | ✅ `getAll` / `getAllAsync` | ✅ `injectAll` / `resolveAll` | ✅ `getMany` | ⚠️ Collection-valued service | ⚠️ Array provider | ⚠️ Collection-valued service | ⚠️ Collection-valued service |
99
+ | **Lazy resolution⁶** | **✅ `lazy()`; sync class tokens** | ⚠️ Deferred identifiers / factories | ✅ `delay()` proxy | ⚠️ Deferred type reference | ⚠️ Cradle property access | ⚠️ `LazyModuleLoader` | ⚠️ Lazy effect execution | ⚠️ Inject a factory |
100
+ | **Conditional bindings** | **⚠️ Tokens / factory logic** | ✅ Contextual constraints | ✅ Predicate-aware factory | ⚠️ Factory logic | ⚠️ Local injection / factory logic | ⚠️ Dynamic modules / factories | ⚠️ Select / compose layers | ⚠️ Factory logic |
101
+ | **Auto-loading / discovery⁶** | **➖ Explicit `use()`** | ⚠️ Class autobinding | ➖ Explicit registrations | ➖ Explicit imports | ✅ `loadModules` (Node) | ⚠️ `DiscoveryService` | ➖ Explicit layers | ➖ Explicit providers |
102
+ | **Module system / composition** | **✅ `use()`** | ✅ Container modules | ✅ `@registry` | ⚠️ Group registrations | ✅ `loadModules` / `register` | ✅ Modules / dynamic modules | ✅ Layer composition | ⚠️ Compose provider chains |
103
+ | **Circular dependency detection⁷** | **✅ Runtime cycle path** | ✅ Runtime detection | ⚠️ Constructor error / `delay` | ⚠️ Deferred type references | ✅ Runtime cycle path | ⚠️ Cycle errors / `forwardRef` | ⚠️ Typed Layer requirements | ⚠️ Registration order constrains dependencies |
104
+ | **Middleware / interceptors⁶** | **⚠️ Higher-order factories** | ✅ Activation / deactivation hooks | ✅ Before / after resolution | ⚠️ Factory wrappers | ⚠️ Factory wrappers | ⚠️ Request interceptors, not DI hooks | ⚠️ Effect composition | ⚠️ Provider decoration |
105
+ | **Snapshot / restore⁶** | **➖** | ✅ `snapshot` / `restore` | ➖ | ➖ | ➖ | ➖ | ➖ | ➖ |
106
+ | **Test substitution / isolation** | **✅ Fresh scopes / containers + `use()`** | ✅ Rebind / snapshots | ✅ Child container overrides | ✅ Named containers / reset | ✅ Child scopes / overrides | ✅ `overrideProvider` | ✅ Substitute test layers | ✅ Child injector overrides |
107
+
108
+ **What stands out:** Katagami combines **accumulated registration types, scope-filtered factory
109
+ resolvers, three lifetimes and zero runtime dependencies** with direct `r.resolve(token)` calls.
110
+ Optional/multiple resolution, module composition, lazy class resolution and standards-based cleanup
111
+ stay available without decorator setup. Awilix, Effect and typed-inject also provide meaningful
112
+ compile-time checks, as shown above.
113
+
114
+ <details>
115
+ <summary>Comparison notes: type guarantees, scopes, async behavior and feature boundaries</summary>
116
+
117
+ 1. **Type guarantees:** Katagami's missing-token guarantee applies to accumulated literal keys and
118
+ unique symbols with their types preserved. Class tokens, predeclared maps and mutable aliases
119
+ have [documented boundaries](./docs/type-safety.md). Awilix rejects unknown cradle properties,
120
+ but its broad `resolve` overload accepts unknown names. Effect checks service requirements in
121
+ its own model. Scope checks here mean excluding scoped tokens from singleton/transient resolvers.
122
+ 2. **Setup and bundles:** Metadata notes describe the documented class-injection path; explicit
123
+ value/factory bindings can avoid decorating individual services. Core Katagami needs no polyfills;
124
+ disposal has [host/compiler requirements](./docs/guide.md#compatibility). ESM and side-effect
125
+ declarations help tree shaking, but these are packaging comparisons, not measured bundle sizes.
126
+ 3. **Scopes:** InversifyJS Request means one resolution graph, not an HTTP request. Named containers,
127
+ module contexts, child injectors and Effect resource scopes are not identical lifetime policies.
128
+ 4. **Async:** Returning a Promise is distinct from awaiting dependencies before injection.
129
+ Katagami keeps the Promise in the inferred type and leaves `await` explicit.
130
+ 5. **Cleanup:** Katagami's opt-in `disposable()` integrates `Symbol.dispose`, `Symbol.asyncDispose`
131
+ and `await using`. Ownership varies by library; InversifyJS deactivation is for singletons,
132
+ Awilix disposers are for cached values, and Nest hooks exclude request-scoped classes.
133
+ 6. **Composition versus dedicated APIs:** A service proxy, deferred token and lazy module are
134
+ different features. Autobinding/discovery is not filesystem loading. Katagami's `use()` copies
135
+ registrations; containers are mutable. Factory wrappers are not interceptor APIs, and fresh
136
+ containers are not snapshots. [Composition details](./docs/choosing-di.md#composition-and-tooling).
137
+ 7. **Cycles:** Runtime cycle detection, deferred references and static dependency requirements are
138
+ different mechanisms. A ⚠️ entry does not promise a general cycle detector; runtime checks do
139
+ not imply detection of every asynchronous deadlock.
140
+
141
+ </details>
97
142
 
98
143
  ## Why Katagami for AI-assisted development?
99
144
 
package/docs/README.de.md CHANGED
@@ -33,7 +33,7 @@ Katagami verbindet **aus Registrierungen abgeleitete Typen, Scope-Prüfungen zur
33
33
 
34
34
  | Bibliothek / Version | Abhängigkeitstypen und Registrierungsprüfung | Scope-Verhalten |
35
35
  | --- | --- | --- |
36
- | **Katagami 3.0.2** | **Sammelt Literal-/unique-symbol-Tokens; fehlende erforderliche Tokens sind Typfehler** | **Scoped-Tokens im Resolver von Singleton-/Transient-Factories ausgeschlossen** |
36
+ | **Katagami 3.0.3** | **Sammelt Literal-/unique-symbol-Tokens; fehlende erforderliche Tokens sind Typfehler** | **Scoped-Tokens im Resolver von Singleton-/Transient-Factories ausgeschlossen** |
37
37
  | InversifyJS 8.2.3 | Typisierte Bindings; Existenzprüfung zur Laufzeit | Konfigurierte Binding-Lebensdauer |
38
38
  | tsyringe 4.10.0 | Klassen-/generische Typen; Registrierungsprüfung zur Laufzeit | Lebensdauer und Kindcontainer |
39
39
  | TypeDI 0.10.0 | Klassen und `Token<T>`; Registrierungsprüfung zur Laufzeit | Geteilte/Transient-Services, benannte Container |
package/docs/README.es.md CHANGED
@@ -33,7 +33,7 @@ Katagami combina **tipos derivados del registro, restricciones de ámbito en com
33
33
 
34
34
  | Biblioteca / versión | Tipos y comprobación de registros | Política de ámbitos |
35
35
  | --- | --- | --- |
36
- | **Katagami 3.0.2** | **Acumula tokens literales/unique symbol; rechaza tokens requeridos sin registrar** | **Excluye tokens Scoped del resolver de factorías Singleton/Transient** |
36
+ | **Katagami 3.0.3** | **Acumula tokens literales/unique symbol; rechaza tokens requeridos sin registrar** | **Excluye tokens Scoped del resolver de factorías Singleton/Transient** |
37
37
  | InversifyJS 8.2.3 | Bindings tipados; existencia comprobada en ejecución | Ciclo de vida configurado por binding |
38
38
  | tsyringe 4.10.0 | Tipos de clase/genéricos; registros comprobados en ejecución | Ciclos de vida y contenedores hijos |
39
39
  | TypeDI 0.10.0 | Clases y `Token<T>`; registros comprobados en ejecución | Servicios compartidos/Transient y contenedores con nombre |
package/docs/README.fr.md CHANGED
@@ -33,7 +33,7 @@ Katagami associe **types déduits des enregistrements, restrictions de portée
33
33
 
34
34
  | Bibliothèque / version | Types et vérification des enregistrements | Politique de portée |
35
35
  | --- | --- | --- |
36
- | **Katagami 3.0.2** | **Accumule les tokens littéraux/unique symbol ; rejette les tokens requis non enregistrés** | **Exclut les tokens Scoped du résolveur des fabriques Singleton/Transient** |
36
+ | **Katagami 3.0.3** | **Accumule les tokens littéraux/unique symbol ; rejette les tokens requis non enregistrés** | **Exclut les tokens Scoped du résolveur des fabriques Singleton/Transient** |
37
37
  | InversifyJS 8.2.3 | Bindings typés ; existence vérifiée à l'exécution | Durée de vie configurée par binding |
38
38
  | tsyringe 4.10.0 | Types de classe/génériques ; enregistrements vérifiés à l'exécution | Durées de vie et conteneurs enfants |
39
39
  | TypeDI 0.10.0 | Classes et `Token<T>` ; enregistrements vérifiés à l'exécution | Services partagés/Transient, conteneurs nommés |
package/docs/README.ja.md CHANGED
@@ -36,33 +36,66 @@ Katagamiの強みは、**登録からの型推論・コンパイル時のスコ
36
36
 
37
37
  ### 他ライブラリとの比較
38
38
 
39
- **2026-09-11確認。** npmの`latest`安定版と公式資料をもとに、標準APIの動作を比較しています。[対象バージョン・出典・詳細な注記](./choosing-di.md#comparison-sources)も参照してください。
40
-
41
- | ライブラリ/確認した版 | 依存の型付けと登録漏れ | スコープの扱い | DIの導入設定 |
42
- | --- | --- | --- | --- |
43
- | **Katagami 3.0.2** | **リテラル・unique symbolの登録型を蓄積し、未登録の必須トークンを拒否** | **Singleton・TransientのresolverからScopedを型で除外** | **デコレータ・メタデータ不要、ランタイム依存ゼロ** |
44
- | InversifyJS 8.2.3 | 型付きの識別子・binding。登録の有無は実行時に確認 | bindingのスコープ指定。resolverの型では分離しない | クラス注入はメタデータ方式。明示的な値・ファクトリbindingも可能 |
45
- | tsyringe 4.10.0 | クラス・ジェネリックの型を利用。登録の有無は実行時に確認 | 実行時のライフタイム設定。ファクトリにはコンテナを渡す | クラス注入にデコレータとReflectメタデータのポリフィル |
46
- | TypeDI 0.10.0 | クラス・`Token<T>`の型を利用。登録の有無は実行時に確認 | 共有・Transientと名前付きコンテナ | TypeScriptの導入手順はデコレータと`reflect-metadata`を使用 |
47
- | Awilix 13.0.5 | 登録からcradleの型を推論。`resolve`の広いオーバーロードは未登録名も許可 | `strict: true`でライフタイム漏れを実行時に検出 | デコレータ・メタデータ不要 |
48
- | NestJS 12.0.1 | 型付きprovider。モジュールとproviderの依存グラフは実行時に解決 | Requestスコープが依存元へ伝播 | フレームワークのモジュールとメタデータ方式のクラス注入 |
49
- | Effect 3.22.2 | `Effect`・`Layer`の型で必要なサービスを追跡 | 型付き`Scope`とfinalizer。DIコンテナとは異なるライフタイムモデル | デコレータ・メタデータ不要。Effectのサービス・Layerを使用 |
50
- | typed-inject 5.0.0 | 文字列トークンを蓄積し、`inject`タプルも検査 | Singleton・Transientと子injector。独立したScoped登録はない | デコレータ・メタデータ不要、ランタイム依存ゼロ |
51
-
52
- Awilixのcradle推論、typed-injectの登録検査、Effectのサービス要求の型検査も、それぞれコンパイル時の機能です。Katagamiは、**登録集合とスコープ制約を、直接`r.resolve(token)`を呼べるファクトリAPIで組み合わせられる**点を重視しています。[型の保証範囲](./type-safety.md)にあるクラストークン・事前宣言・変更可能な参照の条件も適用されます。
53
-
54
- | ライブラリ | ライフタイム/スコープ | 非同期サービス | リソース破棄 |
55
- | --- | --- | --- | --- |
56
- | **Katagami** | **Singleton・Transient・Scoped、ネストしたスコープ** | **`Promise<T>`を推論。依存は明示的にawait** | **`disposable()`でdisposalシンボルと`await using`に連携** |
57
- | InversifyJS | Singleton・Transient・Request(1回の解決グラフ)、コンテナ階層 | `getAsync`・`getAllAsync`が依存の完了を待機 | Singletonのdeactivation handler |
58
- | tsyringe | Singleton・Transient・ResolutionScoped・ContainerScoped | Promiseを返すファクトリを登録可能。利用側で処理 | 構築したDisposableを`container.dispose()`で破棄 |
59
- | TypeDI | 共有・Transient、名前付きコンテナ | Promiseをサービス値として扱い、利用側で処理 | reset・削除時に`destroy()`を呼べるが、戻り値のPromiseは待機しない |
60
- | Awilix | Singleton・Transient・Scoped | Promiseを返すファクトリを登録可能。利用側で処理 | キャッシュしたSingleton・Scopedに登録済みdisposerを適用 |
61
- | NestJS | Singleton・Transient・HTTPリクエスト、スコープ伝播 | 依存providerの非同期初期化を待ってから構築 | アプリのライフサイクルフック。Requestスコープのクラスは対象外 |
62
- | Effect | メモ化されたLayerと明示的なリソーススコープ | 非同期処理を含むEffectによる取得 | Scopeのfinalizerと`acquireRelease` |
63
- | typed-inject | Singleton・Transient、破棄可能な子injector | ファクトリが返すPromiseの型も推論 | 所有するインスタンスの`dispose()`をawait |
64
-
65
- InversifyJSのRequestは1回の解決グラフであり、HTTPリクエストとは異なります。また、Promiseを値として返せることと、依存のPromiseを自動的に待って注入することは別の機能です。[モジュール合成・複数解決・拡張機能の比較](./choosing-di.md#composition-and-tooling)も掲載しています。
39
+ **8ライブラリ・27項目の比較。2026-09-11確認。** 表中のnpm `latest`安定版と公式資料をもとにしています。[対象バージョン・出典・詳細な注記](./choosing-di.md#comparison-sources)
40
+
41
+ **✅ 標準で対応 · ⚠️ 条件付き・異なるモデル・利用側での合成 · その機能の標準対応なし。** 各セルに具体的なAPIや条件を添えています。
42
+
43
+ #### 型安全性・導入設定
44
+
45
+ | 比較項目 | **Katagami**<br>**3.0.3** | InversifyJS<br>8.2.3 | tsyringe<br>4.10.0 | TypeDI<br>0.10.0 | Awilix<br>13.0.5 | NestJS<br>12.0.1 | Effect<br>3.22.2 | typed-inject<br>5.0.0 |
46
+ | --- | --- | --- | --- | --- | --- | --- | --- | --- |
47
+ | **ランタイム要件** | **✅ 通常のTypeScript** | ⚠️ クラスDIにReflectメタデータ | ⚠️ クラスDIにReflectメタデータ | ⚠️ Reflectメタデータ設定 | ✅ DIメタデータ不要 | ⚠️ Nestモジュール・メタデータ | ✅ Effect・Layer API | ✅ 通常のTypeScript |
48
+ | **注入方式** | **明示的なファクトリ・コンストラクタ** | コンストラクタ・プロパティ・ファクトリ | コンストラクタ・ファクトリ | コンストラクタ・プロパティ・ファクトリ | Proxy・Classic・ファクトリ | コンストラクタ・プロパティ・ファクトリ | 関数型サービス・Layer | コンストラクタ・ファクトリ+`inject` |
49
+ | **トークンの種類** | **クラス・文字列・数値・symbol** | クラス・文字列・symbol | クラス・文字列・symbol | クラス・文字列・`Token<T>` | 文字列・symbol | クラス・文字列・symbol | `Context.Tag` | 文字列リテラル |
50
+ | **型安全性** | **✅ サービス推論+スコープ検査** | 型付き識別子・binding | クラス・ジェネリック | クラス・`Token<T>` | ✅ cradleの推論 | ✅ 型付きprovider | ✅ サービス要求の型検査 | ✅ トークン+`inject`タプル |
51
+ | **登録からの型の蓄積** | **✅ トークンの型を蓄積** | ➖ | ➖ | ➖ | ✅ `register` → cradle | ➖ | ⚠️ Layerの要求型 | ✅ トークンの型を蓄積 |
52
+ | **未登録の必須トークンをコンパイル時に検出¹** | **✅ リテラル・unique symbol** | ➖ 実行時に検査 | ➖ 実行時に検査 | ➖ 実行時に検査 | ⚠️ cradleでの参照のみ | ➖ 実行時の依存グラフ | ✅ 未充足のサービス要求 | ✅ リテラルキー |
53
+ | **Singleton・TransientからのScoped参照を型で制限¹** | **✅ Scopedトークンを除外** | ➖ | ➖ | ➖ | ⚠️ strictモードで実行時検査 | ⚠️ Requestスコープが伝播 | ⚠️ 異なる`Scope`モデル | ➖ Scopedライフタイムなし |
54
+ | **ランタイム依存パッケージゼロ²** | **✅** | | | ⚠️ Reflectポリフィルを別途導入 | ⚠️ ブラウザ版は構成が異なる | ➖ | ➖ | ✅ |
55
+ | **ツリーシェイキング対応²** | **✅ ESM・サブパス・`sideEffects: false`** | ⚠️ ESM・`sideEffects: true` | ⚠️ ESMビルド | ✅ ESM・`sideEffects: false` | ⚠️ ESM・ブラウザビルド | ⚠️ ESM・フレームワーク設定 | ✅ ESM・サブパス・副作用宣言 | ⚠️ ESMビルド |
56
+
57
+ #### ライフタイム・非同期・リソース破棄
58
+
59
+ | 比較項目 | **Katagami**<br>**3.0.3** | InversifyJS<br>8.2.3 | tsyringe<br>4.10.0 | TypeDI<br>0.10.0 | Awilix<br>13.0.5 | NestJS<br>12.0.1 | Effect<br>3.22.2 | typed-inject<br>5.0.0 |
60
+ | --- | --- | --- | --- | --- | --- | --- | --- | --- |
61
+ | **ライフタイム** | **✅ Singleton・Transient・Scoped** | Singleton・Transient・Request | ✅ Singleton・Transient・Resolution・Container | ✅ 共有・Transient | ✅ Singleton・Transient・Scoped | ✅ Singleton・Transient・Request | ⚠️ メモ化/fresh Layer+Scope | ✅ Singleton・Transient |
62
+ | **リクエスト/Scopedライフタイム³** | **✅ リクエストごとに明示的なScope** | ⚠️ 1回の解決グラフ | ✅ Container/Resolution単位 | ⚠️ 名前付きコンテナ | ✅ リクエストごとに明示的なScope | ✅ HTTPリクエスト単位 | ⚠️ リソースScope | ⚠️ 子injector。Scoped登録なし |
63
+ | **子コンテナ/ネストしたスコープ³** | **✅ ネストしたScope** | コンテナ階層 | 子コンテナ | ⚠️ 名前付きコンテナ | ✅ 子Scope | ⚠️ モジュール/Requestのコンテキスト | ⚠️ ネストしたリソースScope | ✅ 子injector |
64
+ | **非同期ファクトリ** | **✅ Promiseを返すファクトリ** | ✅ 非同期binding | ✅ Promiseを返すファクトリ | ✅ Promise値のサービス | ✅ Promiseを返すファクトリ | ✅ 非同期provider | ✅ Effectによる取得 | ✅ Promiseを返すファクトリ |
65
+ | **非同期の戻り値の型追跡** | **✅ `Promise<T>`を推論** | ✅ `getAsync<T>` | ⚠️ Promise値として型付け | ⚠️ Promise値として型付け | ✅ `Promise<T>`を推論 | ⚠️ provider・利用側の型指定 | ✅ 結果・エラー・要求の型 | ✅ `Promise<T>`を推論 |
66
+ | **非同期の依存を自動的にawait⁴** | **➖ 明示的に`await`** | ✅ `getAsync`・`getAllAsync` | ➖ 利用側でawait | ➖ 利用側でawait | ➖ 利用側でawait | ✅ 依存元の構築前に待機 | ✅ Effectの合成 | ➖ 利用側でawait |
67
+ | **リソース破棄⁵** | **✅ disposalシンボル・`await using`** | ⚠️ Singletonのdeactivation | ✅ 構築したDisposable | ⚠️ reset・削除時の`destroy()` | ⚠️ キャッシュした値+disposer | ⚠️ アプリのライフサイクルフック | ✅ Scopeのfinalizer | ✅ 所有するDisposable |
68
+ | **非同期の破棄完了を待機⁵** | **✅ `Symbol.asyncDispose`** | ✅ 非同期deactivation | ✅ `container.dispose()` | ➖ `destroy()`を待機しない | ✅ `container.dispose()` | ⚠️ アプリのフック。Requestは対象外 | ✅ Effectのfinalizer | ✅ `injector.dispose()` |
69
+
70
+ #### 合成・拡張機能
71
+
72
+ | 比較項目 | **Katagami**<br>**3.0.3** | InversifyJS<br>8.2.3 | tsyringe<br>4.10.0 | TypeDI<br>0.10.0 | Awilix<br>13.0.5 | NestJS<br>12.0.1 | Effect<br>3.22.2 | typed-inject<br>5.0.0 |
73
+ | --- | --- | --- | --- | --- | --- | --- | --- | --- |
74
+ | **オプショナル解決** | **✅ `tryResolve`・`tryResolveAll`** | ✅ オプショナルget・inject | ✅ オプショナル注入 | ⚠️ `has`で確認して`get` | ✅ `allowUnregistered` | ✅ オプショナル注入 | ✅ `serviceOption` | ⚠️ オプショナル値を合成 |
75
+ | **複数登録・一括解決** | **✅ `resolveAll`** | ✅ `getAll`・`getAllAsync` | ✅ `injectAll`・`resolveAll` | ✅ `getMany` | ⚠️ コレクション値を登録 | ⚠️ 配列provider | ⚠️ コレクション値を登録 | ⚠️ コレクション値を登録 |
76
+ | **遅延解決⁶** | **✅ `lazy()`・同期クラストークン** | ⚠️ 識別子の遅延・ファクトリ | ✅ `delay()`のProxy | ⚠️ 型の遅延参照 | ⚠️ cradleのプロパティ参照 | ⚠️ `LazyModuleLoader` | ⚠️ Effectの遅延実行 | ⚠️ ファクトリを注入 |
77
+ | **条件付きbinding** | **⚠️ トークン分離・ファクトリ内の分岐** | ✅ コンテキスト制約 | ✅ 条件付きファクトリ | ⚠️ ファクトリ内の分岐 | ⚠️ ローカル注入・分岐 | ⚠️ 動的モジュール・ファクトリ | ⚠️ Layerの選択・合成 | ⚠️ ファクトリ内の分岐 |
78
+ | **自動ロード/検出⁶** | **➖ 明示的な`use()`** | ⚠️ クラスの自動binding | ➖ 明示的な登録 | ➖ 明示的なimport | ✅ `loadModules`(Node) | ⚠️ `DiscoveryService` | ➖ 明示的なLayer | ➖ 明示的なprovider |
79
+ | **モジュールシステム・合成** | **✅ `use()`** | ✅ コンテナモジュール | ✅ `@registry` | ⚠️ 登録をまとめる | ✅ `loadModules`・`register` | ✅ モジュール・動的モジュール | ✅ Layerの合成 | ⚠️ providerチェーンの合成 |
80
+ | **循環依存の検出⁷** | **✅ 実行時に循環経路を表示** | ✅ 実行時に検出 | ⚠️ コンストラクタのエラー・`delay` | ⚠️ 型の遅延参照 | ✅ 実行時に循環経路を表示 | ⚠️ 循環エラー・`forwardRef` | ⚠️ Layerの要求型 | ⚠️ 登録順で依存を制限 |
81
+ | **ミドルウェア/インターセプタ⁶** | **⚠️ 高階ファクトリで合成** | ✅ activation・deactivationフック | ✅ 解決前後のフック | ⚠️ ファクトリをラップ | ⚠️ ファクトリをラップ | ⚠️ Request用。DIフックとは異なる | ⚠️ Effectを合成 | ⚠️ providerの装飾 |
82
+ | **スナップショット/復元⁶** | **➖** | ✅ `snapshot`・`restore` | ➖ | ➖ | ➖ | ➖ | ➖ | ➖ |
83
+ | **テスト用の差し替え・分離** | **✅ 新しいScope・コンテナ+`use()`** | ✅ 再binding・スナップショット | ✅ 子コンテナで上書き | ✅ 名前付きコンテナ・reset | ✅ 子Scopeで上書き | ✅ `overrideProvider` | ✅ テスト用Layerに差し替え | ✅ 子injectorで上書き |
84
+
85
+ **Katagamiの強み:登録型の蓄積・ファクトリのスコープ制約・3つのライフタイム・ランタイム依存ゼロ**を、直接`r.resolve(token)`を呼べるAPIで組み合わせられます。オプショナル/複数解決、モジュール合成、クラスの遅延解決、標準のdisposalシンボルによる破棄も、デコレータ設定なしで利用できます。Awilix・Effect・typed-injectにも、表に示したコンパイル時の検査機能があります。
86
+
87
+ <details>
88
+ <summary>比較の注記:型の保証・スコープ・非同期・各機能の対応範囲</summary>
89
+
90
+ 1. **型の保証:** Katagamiは、型が保たれたリテラルキー・unique symbolを蓄積する方式で未登録トークンを検出します。クラストークン・事前宣言・変更可能な参照には[保証範囲の条件](./type-safety.md)があります。Awilixは未登録のcradleプロパティを拒否しますが、広い`resolve`オーバーロードは未登録名も許可します。Effectは独自のサービス要求を検査します。スコープ制約の行は、Singleton・TransientのresolverからScopedを型で除外する機能の比較です。
91
+ 2. **導入とバンドル:** メタデータの記述は公式のクラス注入手順が対象です。明示的な値・ファクトリの登録では、各サービスへのデコレータを省ける場合があります。Katagamiのコアにポリフィルは不要ですが、破棄機能には[ホスト・コンパイラの要件](./guide.md#compatibility)があります。ESM・副作用宣言はツリーシェイキングを助ける構成であり、バンドルサイズの実測比較ではありません。
92
+ 3. **スコープ:** InversifyJSのRequestは1回の解決グラフで、HTTPリクエストとは異なります。名前付きコンテナ・モジュールのコンテキスト・子injector・EffectのリソースScopeも、それぞれ異なる管理方式です。
93
+ 4. **非同期:** Promiseを返せることと、注入前に依存の完了を自動的に待つことは別です。KatagamiはPromiseを型に残し、`await`を明示します。
94
+ 5. **破棄:** Katagamiは追加の`disposable()`で`Symbol.dispose`・`Symbol.asyncDispose`・`await using`に連携します。所有権と破棄対象はライブラリごとに異なり、InversifyJSのdeactivationはSingleton、Awilixのdisposerはキャッシュした値が対象です。NestのフックはRequestスコープのクラスには適用されません。
95
+ 6. **合成と専用API:** サービスの遅延Proxy・型参照の遅延・モジュールの遅延ロードは別の機能です。自動binding・検出も、ファイルの自動ロードとは異なります。Katagamiの`use()`は登録をコピーし、コンテナ自体は変更可能です。ファクトリのラップは専用インターセプタではなく、新しいコンテナでの分離はスナップショットではありません。[合成機能の詳細](./choosing-di.md#composition-and-tooling)。
96
+ 7. **循環依存:** 実行時の循環検出・参照の遅延・依存の型検査は異なる仕組みです。⚠️は汎用的な循環検出器の存在を意味しません。また、実行時の検出が、あらゆる非同期デッドロックの検出を保証するわけではありません。
97
+
98
+ </details>
66
99
 
67
100
  ## AIエージェントによるコーディングで役立つ理由
68
101
 
package/docs/README.ko.md CHANGED
@@ -33,7 +33,7 @@ console.log(greeting);
33
33
 
34
34
  | 라이브러리 / 버전 | 의존성 타입과 등록 확인 | 스코프 정책 |
35
35
  | --- | --- | --- |
36
- | **Katagami 3.0.2** | **리터럴·unique symbol 등록을 누적하고 미등록 필수 토큰 거부** | **Singleton·Transient 팩터리에서 Scoped 토큰을 타입으로 제외** |
36
+ | **Katagami 3.0.3** | **리터럴·unique symbol 등록을 누적하고 미등록 필수 토큰 거부** | **Singleton·Transient 팩터리에서 Scoped 토큰을 타입으로 제외** |
37
37
  | InversifyJS 8.2.3 | 타입이 있는 binding, 등록 여부는 런타임 확인 | binding의 라이프타임 설정 |
38
38
  | tsyringe 4.10.0 | 클래스·제네릭 타입, 등록 여부는 런타임 확인 | 라이프타임 설정과 자식 컨테이너 |
39
39
  | TypeDI 0.10.0 | 클래스·`Token<T>`, 등록 여부는 런타임 확인 | 공유·Transient 서비스와 이름 있는 컨테이너 |
@@ -33,7 +33,7 @@ Katagami 在普通 TypeScript 工厂中结合了**从注册推导类型、编译
33
33
 
34
34
  | 库/版本 | 依赖类型与注册检查 | 作用域策略 |
35
35
  | --- | --- | --- |
36
- | **Katagami 3.0.2** | **累积字面量、unique symbol 的注册类型,拒绝未注册的必需 token** | **从 Singleton、Transient 工厂的 resolver 类型中排除 Scoped token** |
36
+ | **Katagami 3.0.3** | **累积字面量、unique symbol 的注册类型,拒绝未注册的必需 token** | **从 Singleton、Transient 工厂的 resolver 类型中排除 Scoped token** |
37
37
  | InversifyJS 8.2.3 | 有类型的 binding;运行时检查是否已绑定 | binding 的生命周期设置 |
38
38
  | tsyringe 4.10.0 | 类与泛型类型;运行时检查注册 | 生命周期设置与子容器 |
39
39
  | TypeDI 0.10.0 | 类与 `Token<T>`;运行时检查注册 | 共享、Transient 服务与命名容器 |
@@ -33,7 +33,7 @@ Katagami 在一般 TypeScript 工廠中結合了**從註冊推導型別、編譯
33
33
 
34
34
  | 函式庫/版本 | 依賴型別與註冊檢查 | 作用域策略 |
35
35
  | --- | --- | --- |
36
- | **Katagami 3.0.2** | **累積字面值、unique symbol 的註冊型別,拒絕未註冊的必要 token** | **從 Singleton、Transient 工廠的 resolver 型別中排除 Scoped token** |
36
+ | **Katagami 3.0.3** | **累積字面值、unique symbol 的註冊型別,拒絕未註冊的必要 token** | **從 Singleton、Transient 工廠的 resolver 型別中排除 Scoped token** |
37
37
  | InversifyJS 8.2.3 | 具型別的 binding;執行時檢查是否已綁定 | binding 的生命週期設定 |
38
38
  | tsyringe 4.10.0 | 類別與泛型型別;執行時檢查註冊 | 生命週期設定與子容器 |
39
39
  | TypeDI 0.10.0 | 類別與 `Token<T>`;執行時檢查註冊 | 共用、Transient 服務與具名容器 |
@@ -2,8 +2,8 @@
2
2
 
3
3
  Katagami is a strong fit when you want **registration-derived types, explicit request scopes and
4
4
  ordinary TypeScript factories without decorators or runtime dependencies**. The
5
- [README comparison](../README.md#library-comparison) covers all eight libraries, including their
6
- typing, setup, lifetimes, asynchronous behavior and cleanup. This guide records the evidence and
5
+ [README comparison](../README.md#library-comparison) covers 27 features across eight libraries,
6
+ including typing, setup, lifetimes, asynchronous behavior, cleanup and composition. This guide records the evidence and
7
7
  the features that need more explanation than a table cell.
8
8
 
9
9
  ## What distinguishes Katagami
@@ -37,7 +37,7 @@ typing, mutable aliases and predeclared-map boundaries.
37
37
  Reviewed **2026-09-11**. Competitor versions are the npm `latest` dist-tag versions observed on that date,
38
38
  not prerelease versions or unreleased features from repository default branches. TypeDI refers to
39
39
  the `typedi` package maintained under TypeStack, not similarly named forks. NestJS's version is
40
- the version of `@nestjs/core`. The comparison uses Effect **3** documentation explicitly. Katagami 3.0.2 is this documentation
40
+ the version of `@nestjs/core`. The comparison uses Effect **3** documentation explicitly. Katagami 3.0.3 is this documentation
41
41
  release; its runtime and public APIs are unchanged from the reviewed 3.0.1 package.
42
42
 
43
43
  | Package | Reviewed version and registry metadata | Primary documentation / shipped API |
@@ -117,7 +117,7 @@ application code can implement the pattern; it does not claim a dedicated contai
117
117
  | --- | --- | --- |
118
118
  | Katagami | `tryResolve`, `resolveAll`, `tryResolveAll` | `use()` copies registrations; opt-in `lazy()`; ordinary higher-order factories |
119
119
  | InversifyJS | Optional get/inject, `getAll`, `getAllAsync` | Container modules/hierarchy, autobinding, contextual constraints, activation/deactivation, snapshot/restore |
120
- | tsyringe | Optional inject, `injectAll`, `resolveAll` | `@registry`, child containers, before/after resolution interceptors, `delay()` |
120
+ | tsyringe | Optional inject, `injectAll`, `resolveAll` | `@registry`, child containers, before/after resolution interceptors, `delay()`, `predicateAwareClassFactory` |
121
121
  | TypeDI | `has` before `get`; `getMany` with multiple registrations | Named containers, factory configuration and service decorators |
122
122
  | Awilix | `allowUnregistered`; compose collections as values/factories | `loadModules`, child scopes, local injections and proxy/classic injection |
123
123
  | NestJS | Optional injection/provider dependencies; compose array providers | Modules/dynamic modules, provider overrides, discovery service and lazy modules |
@@ -130,6 +130,30 @@ substitution. Factory wrappers are an application pattern, not a middleware/inte
130
130
  Similarly, a lazy module loader is not the same feature as a proxy that constructs one service on
131
131
  first property access, and a resolution graph scope is not an HTTP request scope.
132
132
 
133
+ The README retains all 18 original comparison aspects and adds nine more specific checks:
134
+ registration-derived types, missing-token checking, compile-time scope restrictions, dependency
135
+ packages, scoped lifetime behavior, async result typing, automatic awaiting, async cleanup and
136
+ test substitution. Its three feature-by-library matrices use the same columns. ✅ means built-in
137
+ support, ⚠️ marks a condition, different model or application composition, and ➖ means that the
138
+ specific capability has no built-in support. The icons are not a total score.
139
+
140
+ Additional API notes for the expanded rows:
141
+
142
+ - tsyringe's release README documents `predicateAwareClassFactory` for conditional construction
143
+ and `delay()` for a proxy that constructs an instance on first use. Its circular-dependency
144
+ example reports an undefined constructor; that is narrower than a general graph cycle detector.
145
+ - InversifyJS has a dedicated [snapshot API](https://inversify.io/docs/fundamentals/snapshot/).
146
+ Its activation/deactivation hooks are the extension mechanism shown here; the table does not
147
+ claim that the old `applyMiddleware` API exists in version 8. A deferred service identifier
148
+ delays reading a token, which is different from a proxy that delays constructing its service.
149
+ - Effect 3's [layer memoization guide](https://effect.website/docs/v3/requirements-management/layer-memoization)
150
+ documents both shared layer construction and `Layer.fresh`. These are not container
151
+ Singleton/Transient registration flags. Typed requirements are not a claim of a general
152
+ runtime cycle detector.
153
+ - Nest's `forwardRef` and TypeDI's deferred type references address reference timing. Neither
154
+ should be marked as equivalent to Katagami's lazy service proxy. A runtime cycle guard also
155
+ does not establish detection of every asynchronous deadlock.
156
+
133
157
  Start with the [runnable starter](../examples/request-scope/README.md) and
134
158
  [composition guide](./guide.md#composition-and-test-substitution). For a project already built on
135
159
  NestJS or Effect, adopting its existing DI model can be simpler than maintaining a second container.
@@ -137,6 +161,8 @@ NestJS or Effect, adopting its existing DI model can be simpler than maintaining
137
161
  ## Updating the comparison
138
162
 
139
163
  Preserve a comparison and Katagami's supported advantages in the README when editing positioning.
164
+ Keep the feature-by-library layout, status icons and all existing comparison aspects when refreshing
165
+ it; add detail rather than replacing the matrix with a smaller library-by-library summary.
140
166
  Refresh the date, npm dist-tag versions and official sources together. Check published declarations
141
167
  when a claim involves inference or registration guarantees, and distinguish built-in features from
142
168
  wrappers, configuration and framework behavior. Keep localized summaries aligned with the English
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "katagami",
3
- "version": "3.0.2",
3
+ "version": "3.0.3",
4
4
  "description": "Type-safe dependency injection for TypeScript, with inferred types and scope checks for AI-assisted development. No decorators or reflect-metadata.",
5
5
  "license": "MIT",
6
6
  "type": "module",