diff --git a/artifacts/coverage/coverage-summary.json b/artifacts/coverage/coverage-summary.json index 4c5eb7b..3b6c618 100644 --- a/artifacts/coverage/coverage-summary.json +++ b/artifacts/coverage/coverage-summary.json @@ -1,25 +1,25 @@ -{"total": {"lines":{"total":788,"covered":756,"skipped":0,"pct":95.93},"statements":{"total":915,"covered":858,"skipped":0,"pct":93.77},"functions":{"total":178,"covered":169,"skipped":0,"pct":94.94},"branches":{"total":476,"covered":422,"skipped":0,"pct":88.65},"branchesTrue":{"total":0,"covered":0,"skipped":0,"pct":100}} -,"/tmp/workspace/git-illuma/core/src/plugins.ts": {"lines":{"total":0,"covered":0,"skipped":0,"pct":100},"functions":{"total":0,"covered":0,"skipped":0,"pct":100},"statements":{"total":0,"covered":0,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} -,"/tmp/workspace/git-illuma/core/src/testkit.ts": {"lines":{"total":0,"covered":0,"skipped":0,"pct":100},"functions":{"total":0,"covered":0,"skipped":0,"pct":100},"statements":{"total":0,"covered":0,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} -,"/tmp/workspace/git-illuma/core/src/lib/api/decorator.ts": {"lines":{"total":18,"covered":18,"skipped":0,"pct":100},"functions":{"total":13,"covered":13,"skipped":0,"pct":100},"statements":{"total":19,"covered":19,"skipped":0,"pct":100},"branches":{"total":7,"covered":7,"skipped":0,"pct":100}} -,"/tmp/workspace/git-illuma/core/src/lib/api/injection.ts": {"lines":{"total":9,"covered":9,"skipped":0,"pct":100},"functions":{"total":1,"covered":1,"skipped":0,"pct":100},"statements":{"total":9,"covered":9,"skipped":0,"pct":100},"branches":{"total":14,"covered":14,"skipped":0,"pct":100}} -,"/tmp/workspace/git-illuma/core/src/lib/api/proxy.ts": {"lines":{"total":3,"covered":3,"skipped":0,"pct":100},"functions":{"total":3,"covered":2,"skipped":0,"pct":66.66},"statements":{"total":3,"covered":3,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} -,"/tmp/workspace/git-illuma/core/src/lib/api/token-utils.ts": {"lines":{"total":7,"covered":7,"skipped":0,"pct":100},"functions":{"total":2,"covered":2,"skipped":0,"pct":100},"statements":{"total":10,"covered":10,"skipped":0,"pct":100},"branches":{"total":11,"covered":11,"skipped":0,"pct":100}} -,"/tmp/workspace/git-illuma/core/src/lib/api/token.ts": {"lines":{"total":19,"covered":19,"skipped":0,"pct":100},"functions":{"total":9,"covered":9,"skipped":0,"pct":100},"statements":{"total":19,"covered":19,"skipped":0,"pct":100},"branches":{"total":2,"covered":2,"skipped":0,"pct":100}} -,"/tmp/workspace/git-illuma/core/src/lib/container/container.ts": {"lines":{"total":220,"covered":205,"skipped":0,"pct":93.18},"functions":{"total":33,"covered":29,"skipped":0,"pct":87.87},"statements":{"total":266,"covered":236,"skipped":0,"pct":88.72},"branches":{"total":194,"covered":163,"skipped":0,"pct":84.02}} -,"/tmp/workspace/git-illuma/core/src/lib/container/lifecycle.ts": {"lines":{"total":29,"covered":27,"skipped":0,"pct":93.1},"functions":{"total":11,"covered":8,"skipped":0,"pct":72.72},"statements":{"total":38,"covered":33,"skipped":0,"pct":86.84},"branches":{"total":4,"covered":4,"skipped":0,"pct":100}} -,"/tmp/workspace/git-illuma/core/src/lib/context/context.ts": {"lines":{"total":49,"covered":49,"skipped":0,"pct":100},"functions":{"total":14,"covered":14,"skipped":0,"pct":100},"statements":{"total":52,"covered":52,"skipped":0,"pct":100},"branches":{"total":12,"covered":12,"skipped":0,"pct":100}} -,"/tmp/workspace/git-illuma/core/src/lib/global/global.ts": {"lines":{"total":24,"covered":24,"skipped":0,"pct":100},"functions":{"total":12,"covered":12,"skipped":0,"pct":100},"statements":{"total":25,"covered":25,"skipped":0,"pct":100},"branches":{"total":4,"covered":4,"skipped":0,"pct":100}} -,"/tmp/workspace/git-illuma/core/src/lib/plugins/diagnostics/built-in.ts": {"lines":{"total":6,"covered":6,"skipped":0,"pct":100},"functions":{"total":2,"covered":2,"skipped":0,"pct":100},"statements":{"total":7,"covered":6,"skipped":0,"pct":85.71},"branches":{"total":2,"covered":1,"skipped":0,"pct":50}} -,"/tmp/workspace/git-illuma/core/src/lib/plugins/diagnostics/default-impl.ts": {"lines":{"total":4,"covered":4,"skipped":0,"pct":100},"functions":{"total":1,"covered":1,"skipped":0,"pct":100},"statements":{"total":5,"covered":5,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} -,"/tmp/workspace/git-illuma/core/src/lib/plugins/middlewares/diagnostics.middleware.ts": {"lines":{"total":9,"covered":9,"skipped":0,"pct":100},"functions":{"total":1,"covered":1,"skipped":0,"pct":100},"statements":{"total":9,"covered":9,"skipped":0,"pct":100},"branches":{"total":2,"covered":2,"skipped":0,"pct":100}} -,"/tmp/workspace/git-illuma/core/src/lib/plugins/middlewares/runner.ts": {"lines":{"total":7,"covered":7,"skipped":0,"pct":100},"functions":{"total":2,"covered":2,"skipped":0,"pct":100},"statements":{"total":9,"covered":8,"skipped":0,"pct":88.88},"branches":{"total":4,"covered":3,"skipped":0,"pct":75}} -,"/tmp/workspace/git-illuma/core/src/lib/provider/extractor.ts": {"lines":{"total":5,"covered":5,"skipped":0,"pct":100},"functions":{"total":3,"covered":3,"skipped":0,"pct":100},"statements":{"total":11,"covered":11,"skipped":0,"pct":100},"branches":{"total":8,"covered":8,"skipped":0,"pct":100}} -,"/tmp/workspace/git-illuma/core/src/lib/provider/proto.ts": {"lines":{"total":29,"covered":29,"skipped":0,"pct":100},"functions":{"total":10,"covered":10,"skipped":0,"pct":100},"statements":{"total":30,"covered":30,"skipped":0,"pct":100},"branches":{"total":12,"covered":11,"skipped":0,"pct":91.66}} -,"/tmp/workspace/git-illuma/core/src/lib/provider/resolver.ts": {"lines":{"total":88,"covered":85,"skipped":0,"pct":96.59},"functions":{"total":7,"covered":7,"skipped":0,"pct":100},"statements":{"total":100,"covered":97,"skipped":0,"pct":97},"branches":{"total":73,"covered":65,"skipped":0,"pct":89.04}} -,"/tmp/workspace/git-illuma/core/src/lib/provider/tree-node.ts": {"lines":{"total":176,"covered":166,"skipped":0,"pct":94.31},"functions":{"total":31,"covered":31,"skipped":0,"pct":100},"statements":{"total":207,"covered":193,"skipped":0,"pct":93.23},"branches":{"total":89,"covered":80,"skipped":0,"pct":89.88}} -,"/tmp/workspace/git-illuma/core/src/lib/testkit/helpers.ts": {"lines":{"total":14,"covered":14,"skipped":0,"pct":100},"functions":{"total":3,"covered":3,"skipped":0,"pct":100},"statements":{"total":15,"covered":15,"skipped":0,"pct":100},"branches":{"total":6,"covered":6,"skipped":0,"pct":100}} -,"/tmp/workspace/git-illuma/core/src/lib/utils/defer.ts": {"lines":{"total":24,"covered":24,"skipped":0,"pct":100},"functions":{"total":3,"covered":3,"skipped":0,"pct":100},"statements":{"total":26,"covered":26,"skipped":0,"pct":100},"branches":{"total":8,"covered":8,"skipped":0,"pct":100}} -,"/tmp/workspace/git-illuma/core/src/lib/utils/inheritance.ts": {"lines":{"total":33,"covered":33,"skipped":0,"pct":100},"functions":{"total":11,"covered":11,"skipped":0,"pct":100},"statements":{"total":36,"covered":36,"skipped":0,"pct":100},"branches":{"total":14,"covered":13,"skipped":0,"pct":92.85}} -,"/tmp/workspace/git-illuma/core/src/lib/utils/injector.ts": {"lines":{"total":15,"covered":13,"skipped":0,"pct":86.66},"functions":{"total":6,"covered":5,"skipped":0,"pct":83.33},"statements":{"total":19,"covered":16,"skipped":0,"pct":84.21},"branches":{"total":10,"covered":8,"skipped":0,"pct":80}} +{"total": {"lines":{"total":797,"covered":763,"skipped":0,"pct":95.73},"statements":{"total":924,"covered":865,"skipped":0,"pct":93.61},"functions":{"total":183,"covered":172,"skipped":0,"pct":93.98},"branches":{"total":478,"covered":424,"skipped":0,"pct":88.7},"branchesTrue":{"total":0,"covered":0,"skipped":0,"pct":100}} +,"/home/zak/dev/opensource/illuma/core/src/plugins.ts": {"lines":{"total":0,"covered":0,"skipped":0,"pct":100},"functions":{"total":0,"covered":0,"skipped":0,"pct":100},"statements":{"total":0,"covered":0,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} +,"/home/zak/dev/opensource/illuma/core/src/testkit.ts": {"lines":{"total":0,"covered":0,"skipped":0,"pct":100},"functions":{"total":0,"covered":0,"skipped":0,"pct":100},"statements":{"total":0,"covered":0,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} +,"/home/zak/dev/opensource/illuma/core/src/lib/api/decorator.ts": {"lines":{"total":18,"covered":18,"skipped":0,"pct":100},"functions":{"total":13,"covered":13,"skipped":0,"pct":100},"statements":{"total":19,"covered":19,"skipped":0,"pct":100},"branches":{"total":7,"covered":7,"skipped":0,"pct":100}} +,"/home/zak/dev/opensource/illuma/core/src/lib/api/injection.ts": {"lines":{"total":9,"covered":9,"skipped":0,"pct":100},"functions":{"total":1,"covered":1,"skipped":0,"pct":100},"statements":{"total":9,"covered":9,"skipped":0,"pct":100},"branches":{"total":14,"covered":14,"skipped":0,"pct":100}} +,"/home/zak/dev/opensource/illuma/core/src/lib/api/proxy.ts": {"lines":{"total":3,"covered":3,"skipped":0,"pct":100},"functions":{"total":3,"covered":2,"skipped":0,"pct":66.66},"statements":{"total":3,"covered":3,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} +,"/home/zak/dev/opensource/illuma/core/src/lib/api/token-utils.ts": {"lines":{"total":7,"covered":7,"skipped":0,"pct":100},"functions":{"total":2,"covered":2,"skipped":0,"pct":100},"statements":{"total":10,"covered":10,"skipped":0,"pct":100},"branches":{"total":11,"covered":11,"skipped":0,"pct":100}} +,"/home/zak/dev/opensource/illuma/core/src/lib/api/token.ts": {"lines":{"total":19,"covered":19,"skipped":0,"pct":100},"functions":{"total":9,"covered":9,"skipped":0,"pct":100},"statements":{"total":19,"covered":19,"skipped":0,"pct":100},"branches":{"total":2,"covered":2,"skipped":0,"pct":100}} +,"/home/zak/dev/opensource/illuma/core/src/lib/container/container.ts": {"lines":{"total":220,"covered":205,"skipped":0,"pct":93.18},"functions":{"total":33,"covered":29,"skipped":0,"pct":87.87},"statements":{"total":266,"covered":236,"skipped":0,"pct":88.72},"branches":{"total":194,"covered":163,"skipped":0,"pct":84.02}} +,"/home/zak/dev/opensource/illuma/core/src/lib/container/lifecycle.ts": {"lines":{"total":29,"covered":27,"skipped":0,"pct":93.1},"functions":{"total":11,"covered":8,"skipped":0,"pct":72.72},"statements":{"total":38,"covered":33,"skipped":0,"pct":86.84},"branches":{"total":4,"covered":4,"skipped":0,"pct":100}} +,"/home/zak/dev/opensource/illuma/core/src/lib/context/context.ts": {"lines":{"total":49,"covered":49,"skipped":0,"pct":100},"functions":{"total":14,"covered":14,"skipped":0,"pct":100},"statements":{"total":52,"covered":52,"skipped":0,"pct":100},"branches":{"total":12,"covered":12,"skipped":0,"pct":100}} +,"/home/zak/dev/opensource/illuma/core/src/lib/global/global.ts": {"lines":{"total":32,"covered":30,"skipped":0,"pct":93.75},"functions":{"total":17,"covered":15,"skipped":0,"pct":88.23},"statements":{"total":33,"covered":31,"skipped":0,"pct":93.93},"branches":{"total":6,"covered":6,"skipped":0,"pct":100}} +,"/home/zak/dev/opensource/illuma/core/src/lib/plugins/diagnostics/built-in.ts": {"lines":{"total":6,"covered":6,"skipped":0,"pct":100},"functions":{"total":2,"covered":2,"skipped":0,"pct":100},"statements":{"total":7,"covered":6,"skipped":0,"pct":85.71},"branches":{"total":2,"covered":1,"skipped":0,"pct":50}} +,"/home/zak/dev/opensource/illuma/core/src/lib/plugins/diagnostics/default-impl.ts": {"lines":{"total":5,"covered":5,"skipped":0,"pct":100},"functions":{"total":1,"covered":1,"skipped":0,"pct":100},"statements":{"total":6,"covered":6,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} +,"/home/zak/dev/opensource/illuma/core/src/lib/plugins/middlewares/diagnostics.middleware.ts": {"lines":{"total":9,"covered":9,"skipped":0,"pct":100},"functions":{"total":1,"covered":1,"skipped":0,"pct":100},"statements":{"total":9,"covered":9,"skipped":0,"pct":100},"branches":{"total":2,"covered":2,"skipped":0,"pct":100}} +,"/home/zak/dev/opensource/illuma/core/src/lib/plugins/middlewares/runner.ts": {"lines":{"total":7,"covered":7,"skipped":0,"pct":100},"functions":{"total":2,"covered":2,"skipped":0,"pct":100},"statements":{"total":9,"covered":8,"skipped":0,"pct":88.88},"branches":{"total":4,"covered":3,"skipped":0,"pct":75}} +,"/home/zak/dev/opensource/illuma/core/src/lib/provider/extractor.ts": {"lines":{"total":5,"covered":5,"skipped":0,"pct":100},"functions":{"total":3,"covered":3,"skipped":0,"pct":100},"statements":{"total":11,"covered":11,"skipped":0,"pct":100},"branches":{"total":8,"covered":8,"skipped":0,"pct":100}} +,"/home/zak/dev/opensource/illuma/core/src/lib/provider/proto.ts": {"lines":{"total":29,"covered":29,"skipped":0,"pct":100},"functions":{"total":10,"covered":10,"skipped":0,"pct":100},"statements":{"total":30,"covered":30,"skipped":0,"pct":100},"branches":{"total":12,"covered":11,"skipped":0,"pct":91.66}} +,"/home/zak/dev/opensource/illuma/core/src/lib/provider/resolver.ts": {"lines":{"total":88,"covered":85,"skipped":0,"pct":96.59},"functions":{"total":7,"covered":7,"skipped":0,"pct":100},"statements":{"total":100,"covered":97,"skipped":0,"pct":97},"branches":{"total":73,"covered":65,"skipped":0,"pct":89.04}} +,"/home/zak/dev/opensource/illuma/core/src/lib/provider/tree-node.ts": {"lines":{"total":176,"covered":166,"skipped":0,"pct":94.31},"functions":{"total":31,"covered":31,"skipped":0,"pct":100},"statements":{"total":207,"covered":193,"skipped":0,"pct":93.23},"branches":{"total":89,"covered":80,"skipped":0,"pct":89.88}} +,"/home/zak/dev/opensource/illuma/core/src/lib/testkit/helpers.ts": {"lines":{"total":14,"covered":14,"skipped":0,"pct":100},"functions":{"total":3,"covered":3,"skipped":0,"pct":100},"statements":{"total":15,"covered":15,"skipped":0,"pct":100},"branches":{"total":6,"covered":6,"skipped":0,"pct":100}} +,"/home/zak/dev/opensource/illuma/core/src/lib/utils/defer.ts": {"lines":{"total":24,"covered":24,"skipped":0,"pct":100},"functions":{"total":3,"covered":3,"skipped":0,"pct":100},"statements":{"total":26,"covered":26,"skipped":0,"pct":100},"branches":{"total":8,"covered":8,"skipped":0,"pct":100}} +,"/home/zak/dev/opensource/illuma/core/src/lib/utils/inheritance.ts": {"lines":{"total":33,"covered":33,"skipped":0,"pct":100},"functions":{"total":11,"covered":11,"skipped":0,"pct":100},"statements":{"total":36,"covered":36,"skipped":0,"pct":100},"branches":{"total":14,"covered":13,"skipped":0,"pct":92.85}} +,"/home/zak/dev/opensource/illuma/core/src/lib/utils/injector.ts": {"lines":{"total":15,"covered":13,"skipped":0,"pct":86.66},"functions":{"total":6,"covered":5,"skipped":0,"pct":83.33},"statements":{"total":19,"covered":16,"skipped":0,"pct":84.21},"branches":{"total":10,"covered":8,"skipped":0,"pct":80}} } diff --git a/bun.lock b/bun.lock index 8dd735b..0e0f325 100644 --- a/bun.lock +++ b/bun.lock @@ -8,11 +8,12 @@ "@biomejs/biome": "2.3.8", "@swc/core": "1.15.3", "@types/node": "24.10.1", - "@vitest/coverage-v8": "^4.0.18", + "@vitest/coverage-v8": "4.0.18", "make-coverage-badge": "1.2.0", + "terser": "^5.48.0", "tsup": "8.5.1", "typescript": "5.9.2", - "vitest": "^4.0.18", + "vitest": "4.0.18", }, }, }, @@ -101,6 +102,8 @@ "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], + "@jridgewell/source-map": ["@jridgewell/source-map@0.3.11", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25" } }, "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA=="], + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], @@ -215,6 +218,8 @@ "ast-v8-to-istanbul": ["ast-v8-to-istanbul@0.3.11", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.31", "estree-walker": "^3.0.3", "js-tokens": "^10.0.0" } }, "sha512-Qya9fkoofMjCBNVdWINMjB5KZvkYfaO9/anwkWnjxibpWUxo5iHl2sOdP7/uAqaRuUYuoo8rDwnbaaKVFxoUvw=="], + "buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="], + "bundle-require": ["bundle-require@5.1.0", "", { "dependencies": { "load-tsconfig": "^0.2.3" }, "peerDependencies": { "esbuild": ">=0.18" } }, "sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA=="], "cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="], @@ -223,7 +228,7 @@ "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], - "commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="], + "commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], "confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="], @@ -315,6 +320,8 @@ "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + "source-map-support": ["source-map-support@0.5.21", "", { "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w=="], + "stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="], "std-env": ["std-env@3.10.0", "", {}, "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg=="], @@ -323,6 +330,8 @@ "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + "terser": ["terser@5.48.0", "", { "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.15.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, "bin": { "terser": "bin/terser" } }, "sha512-J/9An6vs9Us6wKRriSFXBWdRZapREHqFzdNUKk0pmu804EMR6dr6winwo7e5JDxN4xahxQsuysyYFwlwj4XN/Q=="], + "thenify": ["thenify@3.3.1", "", { "dependencies": { "any-promise": "^1.0.0" } }, "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw=="], "thenify-all": ["thenify-all@1.6.0", "", { "dependencies": { "thenify": ">= 3.1.0 < 4" } }, "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA=="], @@ -353,6 +362,10 @@ "why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="], + "source-map-support/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], + + "sucrase/commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="], + "vitest/tinyexec": ["tinyexec@1.0.2", "", {}, "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="], } } diff --git a/docs/TECHNICAL_OVERVIEW.md b/docs/TECHNICAL_OVERVIEW.md index bb0dafe..a8d1e00 100644 --- a/docs/TECHNICAL_OVERVIEW.md +++ b/docs/TECHNICAL_OVERVIEW.md @@ -500,6 +500,48 @@ During scanning: - Factory execution may throw errors (which are caught and ignored) - The goal is to discover what dependencies are needed, not to construct final instances +> [!IMPORTANT] +> **Factories and constructors run twice.** +> +> Illuma discovers a provider's dependencies by *invoking* its factory in a +> scan context, with every `nodeInject(...)` call short-circuited to a +> placeholder proxy ([SHAPE_SHIFTER](#injection-context)). That scan runs once +> at registration time (`provide()` for factories, `useClass`, and +> `@NodeInjectable()` classes), and the factory then runs again for real +> during instantiation (`bootstrap()` or first `get()` under `instant: false`). +> +> The proxy makes the scan invocation safe for typical code โ€” property +> accesses, method calls, and constructor calls on injected values all return +> the same proxy without throwing. But the **outer factory body still +> executes**, which means any side effects in a constructor or factory +> (timers, network calls, `console.log`, throwing on bad config, mutating +> module-level state) will fire twice. +> +> **Guidance:** keep constructors and factories side-effect free. Reserve +> side-effecting initialization for an `afterBootstrap` hook on `LifecycleRef`, +> or perform it lazily on the first method call after construction: +> +> ```typescript +> @NodeInjectable() +> class DatabaseService { +> private readonly _lifecycle = nodeInject(LifecycleRef); +> private _connection?: Connection; +> +> constructor() { +> // โŒ this runs twice +> // this._connection = connectToDb(); +> +> // โœ… this runs once, after bootstrap completes +> this._lifecycle.afterBootstrap(() => { +> this._connection = connectToDb(); +> }); +> } +> } +> ``` +> +> Value providers (`{ provide: TOKEN, value: ... }`) and alias providers are +> not affected โ€” their factories are trivial and have no observable effect. + #### 2. Instantiation Phase (Bootstrap) When instantiating a factory, Illuma provides actual dependencies: diff --git a/jsr.json b/jsr.json index 95e4ed1..05b0834 100644 --- a/jsr.json +++ b/jsr.json @@ -5,7 +5,7 @@ "private": false, "repository": { "type": "git", - "url": "https://github.com/git-illuma/core" + "url": "git+https://github.com/git-illuma/core.git" }, "version": "2.2.0", "license": "MIT", diff --git a/package.json b/package.json index 5cad26c..73b033a 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "author": "bebrasmell", "repository": { "type": "git", - "url": "https://github.com/git-illuma/core" + "url": "git+https://github.com/git-illuma/core.git" }, "version": "2.2.0", "type": "module", @@ -67,6 +67,7 @@ "@types/node": "24.10.1", "@vitest/coverage-v8": "4.0.18", "make-coverage-badge": "1.2.0", + "terser": "5.48.0", "tsup": "8.5.1", "typescript": "5.9.2", "vitest": "4.0.18" diff --git a/src/entrypoints.spec.ts b/src/entrypoints.spec.ts index eae6aab..565e648 100644 --- a/src/entrypoints.spec.ts +++ b/src/entrypoints.spec.ts @@ -19,9 +19,13 @@ describe("Package Entrypoints", () => { expect(mainExports.makeScoped).toBeDefined(); expect(mainExports.isInjectable).toBeDefined(); expect(mainExports.getInjectableToken).toBeDefined(); - expect(mainExports.isNodeBase).toBeDefined(); - expect(mainExports.extractToken).toBeDefined(); expect(mainExports.registerClassAsInjectable).toBeDefined(); + + // Internal token utilities are not part of the public surface + // @ts-expect-error Accessing internal API for testing + expect(mainExports.isNodeBase).not.toBeDefined(); + // @ts-expect-error Accessing internal API for testing + expect(mainExports.extractToken).not.toBeDefined(); expect(mainExports.Injector).toBeDefined(); expect(mainExports.LifecycleRef).toBeDefined(); expect(mainExports.NodeContainer).toBeDefined(); @@ -114,5 +118,14 @@ describe("Package Entrypoints", () => { expect(pluginsExports.enableIllumaDiagnostics).toBeDefined(); expect(typeof pluginsExports.enableIllumaDiagnostics).toBe("function"); }); + + it("should expose token utilities for plugin authors", async () => { + const pluginsExports = await import("./plugins"); + + expect(pluginsExports.isNodeBase).toBeDefined(); + expect(pluginsExports.extractToken).toBeDefined(); + expect(typeof pluginsExports.isNodeBase).toBe("function"); + expect(typeof pluginsExports.extractToken).toBe("function"); + }); }); }); diff --git a/src/lib/api/index.ts b/src/lib/api/index.ts index 4725c3f..6d06d3c 100644 --- a/src/lib/api/index.ts +++ b/src/lib/api/index.ts @@ -1,5 +1,4 @@ export * from "./decorator"; export * from "./injection"; export * from "./token"; -export * from "./token-utils"; export * from "./types"; diff --git a/src/lib/api/token-utils.ts b/src/lib/api/token-utils.ts index 30f9f4d..8427aac 100644 --- a/src/lib/api/token-utils.ts +++ b/src/lib/api/token-utils.ts @@ -4,29 +4,32 @@ import { getInjectableToken, isInjectable } from "./decorator"; import { MultiNodeToken, NodeToken } from "./token"; /** - * Type guard to check if a value is a valid dependency injection token. + * Type guard to check if a value is a valid dependency injection token + * (a `NodeToken` or `MultiNodeToken`). Useful when authoring plugins or + * scanners that need to discriminate tokens from constructors or other values. + * + * Exposed via `@illuma/core/plugins`. * * @template T - The type of value the token represents * @param obj - The value to check * @returns True if the object is a NodeToken or MultiNodeToken, false otherwise - * @internal */ export function isNodeBase(obj: unknown): obj is NodeToken | MultiNodeToken { return obj instanceof NodeToken || obj instanceof MultiNodeToken; } /** - * Extracts a valid NodeBase token from a given provider. - * If the provider is a class constructor decorated with @NodeInjectable, it retrieves the associated token. - * If the provider is already a NodeBase token, it returns it directly. - * Throws an InjectionError if the provider is invalid. + * Extracts a `NodeBase` token from a value that may be either a token itself + * or a constructor decorated with `@NodeInjectable`. Useful when authoring + * plugins that accept the same `Token` shape Illuma itself does. + * + * Exposed via `@illuma/core/plugins`. * * @template T - The type of value the token represents * @param provider - The provider to extract the token from * @param isAlias - Whether the provider is being used as an alias * @returns The extracted NodeBase token * @throws {InjectionError} If the provider is invalid - * @internal */ export function extractToken( diff --git a/src/lib/container/container.spec.ts b/src/lib/container/container.spec.ts index 2794f21..03daa4c 100644 --- a/src/lib/container/container.spec.ts +++ b/src/lib/container/container.spec.ts @@ -1,12 +1,12 @@ import { describe, expect, it, vi } from "vitest"; import { - extractToken, MultiNodeToken, makeInjectable, NodeInjectable, NodeToken, nodeInject, } from "../api"; +import { extractToken } from "../api/token-utils"; import { InjectionError } from "../errors"; import { NodeContainer } from "./container"; diff --git a/src/lib/container/container.ts b/src/lib/container/container.ts index 13ffd3a..16687c0 100644 --- a/src/lib/container/container.ts +++ b/src/lib/container/container.ts @@ -238,7 +238,7 @@ export class NodeContainer extends Illuma implements iDIContainer { const end = performance.now(); const duration = end - start; if (this._opts?.measurePerformance) { - console.log(`[Illuma] ๐Ÿš€ Bootstrapped in ${duration.toFixed(2)} ms`); + Illuma.logger.log(`[Illuma] ๐Ÿš€ Bootstrapped in ${duration.toFixed(2)} ms`); } this._lifecycle.runBootstrapHooks(); diff --git a/src/lib/global/global.spec.ts b/src/lib/global/global.spec.ts index 52a2917..bd7097c 100644 --- a/src/lib/global/global.spec.ts +++ b/src/lib/global/global.spec.ts @@ -105,4 +105,38 @@ describe("Illuma", () => { expect(Array.isArray(scanners)).toBe(true); }); }); + + describe("setLogger", () => { + it("should default to a console-backed logger", () => { + expect(typeof Illuma.logger.log).toBe("function"); + expect(typeof Illuma.logger.warn).toBe("function"); + expect(typeof Illuma.logger.error).toBe("function"); + }); + + it("should route output through the registered logger", () => { + const log = vi.fn(); + const warn = vi.fn(); + const error = vi.fn(); + + Illuma.setLogger({ log, warn, error }); + + Illuma.logger.log("a"); + Illuma.logger.warn("b"); + Illuma.logger.error("c"); + + expect(log).toHaveBeenCalledWith("a"); + expect(warn).toHaveBeenCalledWith("b"); + expect(error).toHaveBeenCalledWith("c"); + }); + + it("should restore the default logger when given null", () => { + const log = vi.fn(); + Illuma.setLogger({ log, warn: vi.fn(), error: vi.fn() }); + Illuma.setLogger(null); + + Illuma.logger.log("x"); + + expect(log).not.toHaveBeenCalled(); + }); + }); }); diff --git a/src/lib/global/global.ts b/src/lib/global/global.ts index f937120..52211b5 100644 --- a/src/lib/global/global.ts +++ b/src/lib/global/global.ts @@ -6,11 +6,30 @@ import type { } from "../plugins/diagnostics/types"; import type { iMiddleware } from "../plugins/middlewares/types"; +/** + * Minimal logger surface used by Illuma for diagnostics, bootstrap timing, + * and built-in middleware output. `console` satisfies this interface and is + * the default. Provide a custom implementation via {@link Illuma.setLogger} + * to silence, redirect, or structure these messages. + */ +export interface iLogger { + log: (...args: unknown[]) => void; + warn: (...args: unknown[]) => void; + error: (...args: unknown[]) => void; +} + +const defaultLogger: iLogger = { + log: (...args) => console.log(...args), + warn: (...args) => console.warn(...args), + error: (...args) => console.error(...args), +}; + interface iIllumaGlobalState { diagnostics: Set; scanners: iContextScanner[]; middlewares: iMiddleware[]; classRegistry: WeakMap>; + logger: iLogger; } const ILLUMA_CLASS_KEY = Symbol.for("@illuma/core/Illuma"); @@ -29,10 +48,14 @@ if (!illumaGlobal[ILLUMA_STATE_KEY]) { scanners: [] as iContextScanner[], middlewares: [] as iMiddleware[], classRegistry: new WeakMap>(), + logger: defaultLogger, }; } const illumaState = illumaGlobal[ILLUMA_STATE_KEY]; +// Backfill if state was created by an older version of @illuma/core sharing +// the same globalThis (npm + jsr, dual-installs, etc.) +illumaState.logger ??= defaultLogger; /** * Global plugin container for managing core plugins such as diagnostics and context scanners. @@ -63,6 +86,36 @@ abstract class IllumaBase { return IllumaBase._scanners; } + /** + * The logger used by Illuma for diagnostics, bootstrap timing, and built-in + * middleware output. Defaults to `console`. Replace via {@link setLogger}. + */ + public static get logger(): iLogger { + return illumaState.logger; + } + + /** + * Replaces the logger used by Illuma's diagnostics, bootstrap timing, and + * built-in middleware output. Pass `null` to restore the default `console` + * logger. + * + * @param logger - The logger implementation, or `null` to reset to default + * + * @example + * ```typescript + * import { Illuma } from '@illuma/core/plugins'; + * + * Illuma.setLogger({ + * log: (...args) => myLogger.info(...args), + * warn: (...args) => myLogger.warn(...args), + * error: (...args) => myLogger.error(...args), + * }); + * ``` + */ + public static setLogger(logger: iLogger | null): void { + illumaState.logger = logger ?? defaultLogger; + } + /** * Extends the diagnostics with a new diagnostics module. * These will be run on diagnostics reports after container bootstrap. @@ -119,6 +172,7 @@ abstract class IllumaBase { IllumaBase._diagnostics.clear(); IllumaBase._scanners.length = 0; IllumaBase._middlewares.length = 0; + illumaState.logger = defaultLogger; } } diff --git a/src/lib/plugins/diagnostics/default-impl.ts b/src/lib/plugins/diagnostics/default-impl.ts index a64724c..4d37c2e 100644 --- a/src/lib/plugins/diagnostics/default-impl.ts +++ b/src/lib/plugins/diagnostics/default-impl.ts @@ -1,11 +1,13 @@ +import { Illuma } from "../../global"; import type { iDiagnosticsModule, iDiagnosticsReport } from "./types"; /** @internal */ export class DiagnosticsDefaultReporter implements iDiagnosticsModule { public onReport(report: iDiagnosticsReport): void { - console.log("[Illuma] ๐Ÿงน Diagnostics:"); - console.log(` Total: ${report.totalNodes} node(s)`); - console.log(` ${report.unusedNodes.length} were not used while bootstrap:`); - for (const node of report.unusedNodes) console.log(` - ${node.toString()}`); + const logger = Illuma.logger; + logger.log("[Illuma] ๐Ÿงน Diagnostics:"); + logger.log(` Total: ${report.totalNodes} node(s)`); + logger.log(` ${report.unusedNodes.length} were not used while bootstrap:`); + for (const node of report.unusedNodes) logger.log(` - ${node.toString()}`); } } diff --git a/src/lib/plugins/diagnostics/diagnostics.spec.ts b/src/lib/plugins/diagnostics/diagnostics.spec.ts index af7fb76..3ee330f 100644 --- a/src/lib/plugins/diagnostics/diagnostics.spec.ts +++ b/src/lib/plugins/diagnostics/diagnostics.spec.ts @@ -1,12 +1,7 @@ import type { MockInstance } from "vitest"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -import { - extractToken, - MultiNodeToken, - NodeInjectable, - NodeToken, - nodeInject, -} from "../../api"; +import { MultiNodeToken, NodeInjectable, NodeToken, nodeInject } from "../../api"; +import { extractToken } from "../../api/token-utils"; import { NodeContainer } from "../../container"; import { Illuma } from "../../global"; import type { TreeNode } from "../../provider"; diff --git a/src/lib/plugins/index.ts b/src/lib/plugins/index.ts index b70ff0b..ce694a0 100644 --- a/src/lib/plugins/index.ts +++ b/src/lib/plugins/index.ts @@ -1,3 +1,4 @@ +export { extractToken, isNodeBase } from "../api/token-utils"; export * from "../global"; export * from "./context"; export * from "./diagnostics"; diff --git a/src/lib/plugins/middlewares/diagnostics.middleware.ts b/src/lib/plugins/middlewares/diagnostics.middleware.ts index 28ac09d..b094b21 100644 --- a/src/lib/plugins/middlewares/diagnostics.middleware.ts +++ b/src/lib/plugins/middlewares/diagnostics.middleware.ts @@ -1,3 +1,4 @@ +import { Illuma } from "../../global"; import type { iMiddleware } from "./types"; export const performanceDiagnostics: iMiddleware = (params, next) => { @@ -10,6 +11,6 @@ export const performanceDiagnostics: iMiddleware = (params, next) => { const end = performance.now(); const duration = end - start; - console.log(`Instantiated ${params.token.name} in ${duration.toFixed(2)} ms`); + Illuma.logger.log(`Instantiated ${params.token.name} in ${duration.toFixed(2)} ms`); return instance; }; diff --git a/tsup.config.ts b/tsup.config.ts index a514c25..6003548 100644 --- a/tsup.config.ts +++ b/tsup.config.ts @@ -7,4 +7,14 @@ export default defineConfig({ splitting: false, sourcemap: true, clean: true, + minify: 'terser', + terserOptions: { + format: { + // Preserve JSDoc blocks (/** ... */) and bang-comments (/*! */) so + // hover docs survive for plain-JS consumers. terser exposes the + // comment body without the surrounding /* */, so a JSDoc block + // appears as "* foo". + comments: /^[*!]|@preserve|@license|@cc_on|@__PURE__/i, + }, + }, });