@illuma/core 1.5.0 → 1.5.2

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
@@ -1,35 +1,37 @@
1
- # 🔥 **Illuma** – Angular-style Dependency Injection for TypeScript
1
+ # **Illuma** – Dependency Injection for TypeScript
2
2
 
3
3
  ![NPM Version](https://img.shields.io/npm/v/%40illuma%2Fcore)
4
4
  ![NPM Downloads](https://img.shields.io/npm/dw/%40illuma%2Fcore)
5
5
  ![npm bundle size](https://img.shields.io/bundlephobia/min/%40illuma%2Fcore)
6
6
  ![Test coverage](./badges/coverage.svg)
7
7
 
8
- A lightweight, type-safe dependency injection container for TypeScript. Zero dependencies.
8
+ A universal, lightweight and type-safe dependency injection container for TypeScript.
9
+ Heavily inspired by Angular's DI system, but designed to work in any environment (Node.js, Bun, Deno, browsers, and more).
9
10
 
10
- ## Features
11
+ ## Features
11
12
 
12
- - 🎯 **Type-Safe** – Full TypeScript support with excellent type inference
13
- - 🪶 **Lightweight** – Zero dependencies, minimal bundle size
14
- - 🔄 **Flexible** – Classes, factories, values, and aliases
15
- - 🎨 **Optional decorators** – Angular-style `@NodeInjectable()` decorator
16
- - 🔗 **Multi-Tokens** – Built-in multi-provider support
17
- - 🔌 **Plugin System** – Extensible architecture with custom middlewares, scanners, and diagnostics
18
- - 🧪 **Testkit** – Utilities for easy unit testing and mocking
19
- - 🌍 **Universal** – Node.js, Deno, browser, and Electron
20
- ## 📦 Installation
13
+ - **Type-Safe** – Excellent type inference
14
+ - **Lightweight** – Zero dependencies, minimal bundle size
15
+ - **Flexible** – Classes, factories, values, and aliases
16
+ - **Optional decorators** – Injectable classes with `@NodeInjectable()` decorator
17
+ - **Multi-Tokens** – Built-in multi-provider support
18
+ - **Plugin System** – Extensible architecture with custom middlewares, scanners, and diagnostics
19
+ - **TestKit** – Unit testing and mocking utilities for any testing framework
20
+ - **Universal** – Node.js, Bun, Deno, browser, and Electron
21
+
22
+ ## Installation
21
23
 
22
24
  ```bash
23
25
  npm install @illuma/core
24
26
  ```
25
27
 
26
- ## 🤝 Compatibility
28
+ ## Compatibility
27
29
 
28
- Anything that supports ES2015+ (ES6+).
29
- Practically the library is compatible with Node.js (v14+) and all modern browsers.
30
- For older environments, consider using a transpiler like Babel or TypeScript or provide polyfills as needed.
30
+ Compatible with virtually anything supporting ES2015+ (ES6+).
31
+ Practically the library is compatible with Node.js (v14+), Bun, Deno and all modern browsers.
32
+ For older environments, consider using a transpiler or provide polyfills as needed.
31
33
 
32
- ## 🚀 Quick Start
34
+ ## Quick start
33
35
 
34
36
  ```typescript
35
37
  import { NodeContainer, NodeInjectable, nodeInject } from '@illuma/core';
@@ -58,9 +60,11 @@ container.bootstrap();
58
60
  const userService = container.get(UserService);
59
61
  ```
60
62
 
61
- > **Note:** Requires `experimentalDecorators` and `emitDecoratorMetadata` in tsconfig. See [Getting Started](./docs/GETTING_STARTED.md) for decorator-free alternatives.
63
+ > **Note:**
64
+ > Example above requires `experimentalDecorators` and `emitDecoratorMetadata` in tsconfig.
65
+ > See [Getting Started](./docs/GETTING_STARTED.md) for decorator-free alternatives.
62
66
 
63
- ## 🏷️ Using Tokens
67
+ ## Using Tokens
64
68
 
65
69
  ```typescript
66
70
  import { NodeToken, MultiNodeToken, NodeContainer } from '@illuma/core';
@@ -95,7 +99,7 @@ const plugins = container.get(PLUGINS); // Plugin[]: [AnalyticsPlugin, LoggingP
95
99
 
96
100
  See [Tokens Guide](./docs/TOKENS.md) for more details.
97
101
 
98
- ## 🎨 Provider Types
102
+ ## Provider types
99
103
 
100
104
  ```typescript
101
105
  // Class provider
@@ -106,6 +110,7 @@ container.provide({ provide: CONFIG, value: { apiUrl: '...' } });
106
110
 
107
111
  // Factory provider
108
112
  container.provide({ provide: DATABASE, factory: () => {
113
+ // You can use nodeInject inside factories!
109
114
  const env = nodeInject(ENV);
110
115
  return createDatabase(env.connectionString);
111
116
  } });
@@ -119,7 +124,7 @@ container.provide({ provide: Database, alias: ExistingDatabase });
119
124
 
120
125
  See [Providers Guide](./docs/PROVIDERS.md) for details.
121
126
 
122
- ## 🧪 Testing
127
+ ## Testing
123
128
 
124
129
  ```typescript
125
130
  import { createTestFactory } from '@illuma/core/testkit';
@@ -135,36 +140,43 @@ it('should fetch user', () => {
135
140
  });
136
141
  ```
137
142
 
138
- See [Testing Guide](./docs/TESTKIT.md) for comprehensive examples.
143
+ See [Testing Guide](./docs/TESTKIT.md) for examples.
139
144
 
140
- ## 📚 Documentation
145
+ ## Documentation
141
146
 
142
147
  | Guide | Description |
143
- | -------------------------------------------------- | ----------------------------------------------------- |
148
+ | :-- | :-- |
144
149
  | [Getting Started](./docs/GETTING_STARTED.md) | Installation, setup, and basic usage |
145
150
  | [Providers](./docs/PROVIDERS.md) | Value, factory, class, and alias providers |
146
151
  | [Tokens](./docs/TOKENS.md) | NodeToken and MultiNodeToken |
147
152
  | [Async Injection](./docs/ASYNC_INJECTION.md) | Lazy loading and sub-containers |
148
- | [Testing](./docs/TESTKIT.md) | Testkit and mocking |
153
+ | [Testing](./docs/TESTKIT.md) | TestKit and mocking |
149
154
  | [Plugins](./docs/PLUGINS.md) | Extending Illuma with custom scanners and diagnostics |
150
155
  | [Technical Overview](./docs/TECHNICAL_OVERVIEW.md) | Deep dive into how Illuma works |
151
156
  | [API Reference](./docs/API.md) | Complete API documentation |
152
157
  | [Troubleshooting](./docs/TROUBLESHOOTING.md) | Error codes and solutions |
153
158
 
154
- ## 🔌 Plugins
159
+ ## Plugins
155
160
 
156
- Illuma supports a plugin system for extending functionality. Check out these plugins:
161
+ Illuma supports plugins! Check these out:
157
162
 
158
163
  - **[@illuma/reflect](https://github.com/git-illuma/reflect)** – Constructor metadata and property decorator injection support
159
164
 
160
165
  See [Plugins Guide](./docs/PLUGINS.md) for creating your own plugins.
161
166
 
162
- ## 📄 License
167
+ ## Contributing
168
+
169
+ Thank you for considering contributing to Illuma! I deeply appreciate your interest in making this project better.
170
+
171
+ Anyways, to get you started, please take a look at the [Contributing Guide](./CONTRIBUTING.md) for guidelines on how to setup development environment, run tests, and submit pull requests.
172
+
173
+ ## License
163
174
 
164
- MIT © [bebrasmell](https://github.com/bebrasmell)
175
+ MIT
165
176
 
166
- ## 🔗 Links
177
+ Created by [bebrasmell](https://github.com/bebrasmell)
167
178
 
179
+ ## Links
180
+ - [NPM](https://npmjs.com/package/@illuma/core)
168
181
  - [GitHub](https://github.com/git-illuma/core)
169
- - [NPM](https://www.npmjs.com/package/@illuma/core)
170
182
  - [Issues](https://github.com/git-illuma/core/issues)
package/dist/index.cjs CHANGED
@@ -377,7 +377,7 @@ __name(isNotFoundError, "isNotFoundError");
377
377
 
378
378
  // src/lib/api/decorator.ts
379
379
  var tokenRegistry = /* @__PURE__ */ new WeakMap();
380
- var INJECTION_SYMBOL = Symbol("Injectable");
380
+ var INJECTION_SYMBOL = /* @__PURE__ */ Symbol("Injectable");
381
381
  function NodeInjectable() {
382
382
  return (ctor) => {
383
383
  const nodeToken = new NodeToken(`_${ctor.name}`, {
@@ -521,6 +521,17 @@ __name(nodeInject, "nodeInject");
521
521
  // src/lib/container/container.ts
522
522
  init_plugin_container();
523
523
 
524
+ // src/lib/plugins/middlewares/runner.ts
525
+ function runMiddlewares(middlewares, params) {
526
+ const ms = middlewares;
527
+ const next = /* @__PURE__ */ __name((i, current) => {
528
+ if (i >= ms.length) return current.factory();
529
+ return ms[i](current, (nextParams) => next(i + 1, nextParams));
530
+ }, "next");
531
+ return next(0, params);
532
+ }
533
+ __name(runMiddlewares, "runMiddlewares");
534
+
524
535
  // src/lib/provider/extractor.ts
525
536
  function extractProvider(provider) {
526
537
  if ("value" in provider) return () => provider.value;
@@ -626,24 +637,15 @@ var InjectorImpl = class {
626
637
  var Injector = new NodeToken("Injector");
627
638
 
628
639
  // src/lib/provider/tree-node.ts
629
- function runMiddlewares(middlewares, params) {
630
- const ms = middlewares;
631
- const next = /* @__PURE__ */ __name((i, current) => {
632
- if (i >= ms.length) return current.factory();
633
- return ms[i](current, (nextParams) => next(i + 1, nextParams));
634
- }, "next");
635
- return next(0, params);
636
- }
637
- __name(runMiddlewares, "runMiddlewares");
638
640
  function retrieverFactory(node, deps, transparentDeps) {
639
641
  return (token, optional) => {
640
642
  const depNode = deps.get(token);
641
643
  if (!depNode && !optional) {
642
- const transparent = Array.from(transparentDeps).find((n) => "proto" in n && n.proto.parent.token === token);
644
+ const transparent = transparentDeps.get(token);
643
645
  if (transparent) return transparent.instance;
644
646
  throw InjectionError.untracked(token, node);
645
647
  }
646
- return depNode?.instance ?? null;
648
+ return depNode ? depNode.instance : null;
647
649
  };
648
650
  }
649
651
  __name(retrieverFactory, "retrieverFactory");
@@ -654,7 +656,7 @@ var TreeRootNode = class {
654
656
  instant;
655
657
  middlewares;
656
658
  _deps = /* @__PURE__ */ new Set();
657
- _treePool = /* @__PURE__ */ new WeakMap();
659
+ _treePool = /* @__PURE__ */ new Map();
658
660
  constructor(instant = true, middlewares = []) {
659
661
  this.instant = instant;
660
662
  this.middlewares = middlewares;
@@ -719,7 +721,11 @@ var TreeNodeSingle = class {
719
721
  if (this._resolved) return;
720
722
  for (const node of this._deps.values()) node.instantiate(pool, middlewares);
721
723
  for (const dep of this._transparent) dep.instantiate(pool, middlewares);
722
- const retriever = retrieverFactory(this.proto.token, this._deps, this._transparent);
724
+ const transparentMap = /* @__PURE__ */ new Map();
725
+ for (const tNode of this._transparent) {
726
+ transparentMap.set(tNode.proto.parent.token, tNode);
727
+ }
728
+ const retriever = retrieverFactory(this.proto.token, this._deps, transparentMap);
723
729
  const factory = this.proto.factory ?? this.proto.token.opts?.factory;
724
730
  if (!factory) throw InjectionError.notFound(this.proto.token);
725
731
  const contextFactory = /* @__PURE__ */ __name(() => InjectionContext.instantiate(factory, retriever), "contextFactory");
@@ -768,7 +774,11 @@ var TreeNodeTransparent = class _TreeNodeTransparent {
768
774
  if (this._resolved) return;
769
775
  for (const dep of this._transparent) dep.instantiate(pool, middlewares);
770
776
  for (const node of this._deps.values()) node.instantiate(pool, middlewares);
771
- const retriever = retrieverFactory(this.proto.parent.token, this._deps, this._transparent);
777
+ const transparentMap = /* @__PURE__ */ new Map();
778
+ for (const tNode of this._transparent) {
779
+ transparentMap.set(tNode.proto.parent.token, tNode);
780
+ }
781
+ const retriever = retrieverFactory(this.proto.parent.token, this._deps, transparentMap);
772
782
  const refFactory = /* @__PURE__ */ __name(() => InjectionContext.instantiate(this.proto.factory, retriever), "refFactory");
773
783
  this._instance = runMiddlewares(middlewares, {
774
784
  token: this.proto.parent.token,
@@ -1301,12 +1311,33 @@ var NodeContainer = class extends Illuma {
1301
1311
  if (isConstructor(fn) && !isInjectable(fn)) {
1302
1312
  throw InjectionError.invalidCtor(fn);
1303
1313
  }
1304
- const factory = isInjectable(fn) ? () => new fn() : fn;
1305
- return InjectionContext.instantiate(factory, (t, optional) => {
1306
- if (!this._rootNode) throw InjectionError.notBootstrapped();
1307
- const node = this._rootNode.find(t);
1308
- if (!node && !optional) throw InjectionError.notFound(t);
1314
+ let factory;
1315
+ if (isInjectable(fn)) {
1316
+ const f = getInjectableToken(fn).opts?.factory;
1317
+ if (!f) factory = /* @__PURE__ */ __name(() => new fn(), "factory");
1318
+ else factory = /* @__PURE__ */ __name(() => getInjectableToken(fn).opts?.factory?.(), "factory");
1319
+ } else {
1320
+ factory = fn;
1321
+ }
1322
+ const rootNode = this._rootNode;
1323
+ if (!rootNode) throw InjectionError.notBootstrapped();
1324
+ const retriever = /* @__PURE__ */ __name((token, optional) => {
1325
+ const node = rootNode.find(token);
1326
+ if (!node && !optional) throw InjectionError.notFound(token);
1309
1327
  return node ? node.instance : null;
1328
+ }, "retriever");
1329
+ const deps = InjectionContext.scan(factory);
1330
+ const middlewares = [
1331
+ ...Illuma._middlewares,
1332
+ ...this.collectMiddlewares()
1333
+ ];
1334
+ const contextFactory = /* @__PURE__ */ __name(() => InjectionContext.instantiate(factory, retriever), "contextFactory");
1335
+ return runMiddlewares(middlewares, {
1336
+ token: new NodeToken("ProducedNode"),
1337
+ deps: new Set([
1338
+ ...deps.values()
1339
+ ].map((d) => d.token)),
1340
+ factory: contextFactory
1310
1341
  });
1311
1342
  }
1312
1343
  };