{"id":20801920,"url":"https://github.com/philips-software/odin","last_synced_at":"2026-03-04T09:31:12.244Z","repository":{"id":41132242,"uuid":"162426280","full_name":"philips-software/odin","owner":"philips-software","description":"On-demand dependency injection for JavaScript.","archived":false,"fork":false,"pushed_at":"2024-06-18T18:13:12.000Z","size":1726,"stargazers_count":18,"open_issues_count":3,"forks_count":5,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-10-26T19:03:04.163Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/philips-software.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2018-12-19T11:13:21.000Z","updated_at":"2024-09-20T17:03:27.000Z","dependencies_parsed_at":"2025-05-07T00:45:53.200Z","dependency_job_id":"d4c1a7fd-f3b3-4bd0-9df0-60a5f51e568c","html_url":"https://github.com/philips-software/odin","commit_stats":{"total_commits":52,"total_committers":6,"mean_commits":8.666666666666666,"dds":0.3076923076923077,"last_synced_commit":"bdb52c65a847c7ec00bf85dd2e6fb09fca781637"},"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/philips-software/odin","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/philips-software%2Fodin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/philips-software%2Fodin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/philips-software%2Fodin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/philips-software%2Fodin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/philips-software","download_url":"https://codeload.github.com/philips-software/odin/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/philips-software%2Fodin/sbom","scorecard":{"id":731481,"data":{"date":"2025-08-11","repo":{"name":"github.com/philips-software/odin","commit":"1c47e065b75be7dcfba32f78e32387da1d30ed16"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.7,"checks":[{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Code-Review","score":6,"reason":"Found 10/16 approved changesets -- score normalized to 6","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/ci.yml:1","Warn: topLevel 'contents' permission set to 'write': .github/workflows/release.yml:10","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":5,"reason":"dependency not pinned by hash detected -- score normalized to 5","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:23: update your workflow using https://app.stepsecurity.io/secureworkflow/philips-software/odin/ci.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:24: update your workflow using https://app.stepsecurity.io/secureworkflow/philips-software/odin/ci.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/ci.yml:33: update your workflow using https://app.stepsecurity.io/secureworkflow/philips-software/odin/ci.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/release.yml:19: update your workflow using https://app.stepsecurity.io/secureworkflow/philips-software/odin/release.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/release.yml:23: update your workflow using https://app.stepsecurity.io/secureworkflow/philips-software/odin/release.yml/main?enable=pin","Info:   0 out of   4 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   1 third-party GitHubAction dependencies pinned","Info:   2 out of   2 npmCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE.md:0","Info: FSF or OSI recognized license: MIT License: LICENSE.md:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 26 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":0,"reason":"25 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-h5c3-5r3r-rr8q","Warn: Project is vulnerable to: GHSA-rmvr-2pp2-xj38","Warn: Project is vulnerable to: GHSA-xx4v-prfh-6cgc","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-67mh-4wv8-2f99","Warn: Project is vulnerable to: GHSA-fjxv-7rqg-78g4","Warn: Project is vulnerable to: GHSA-78xj-cgh5-2h22","Warn: Project is vulnerable to: GHSA-2p57-rm9w-gvfp","Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv","Warn: Project is vulnerable to: GHSA-mwcw-c2x4-8c55","Warn: Project is vulnerable to: GHSA-gcx4-mw62-g8wm","Warn: Project is vulnerable to: GHSA-f5x3-32g6-xq36","Warn: Project is vulnerable to: GHSA-8jhw-289h-jh2g","Warn: Project is vulnerable to: GHSA-64vr-g452-qvp3","Warn: Project is vulnerable to: GHSA-9cwx-2883-4wfx","Warn: Project is vulnerable to: GHSA-vg6x-rcgg-rjx6","Warn: Project is vulnerable to: GHSA-x574-m823-4x7w","Warn: Project is vulnerable to: GHSA-4r4m-qw57-chr8","Warn: Project is vulnerable to: GHSA-xcj6-pq6g-qj4x","Warn: Project is vulnerable to: GHSA-356w-63v5-8wf4","Warn: Project is vulnerable to: GHSA-859w-5945-r5v3","Warn: Project is vulnerable to: GHSA-g3ch-rx76-35fx","Warn: Project is vulnerable to: GHSA-3h5v-q93c-6h6q"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-22T14:33:27.108Z","repository_id":41132242,"created_at":"2025-08-22T14:33:27.108Z","updated_at":"2025-08-22T14:33:27.108Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30077066,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-04T08:01:56.766Z","status":"ssl_error","status_checked_at":"2026-03-04T08:00:42.919Z","response_time":59,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-17T18:26:10.184Z","updated_at":"2026-03-04T09:31:12.007Z","avatar_url":"https://github.com/philips-software.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# odin\n\n[![ci][badge-workflow-ci]][badge-workflow-ci-link]\n[![codecov][badge-codecov]][badge-codecov-link]\n[![npm][badge-npm]][badge-npm-link]\n[![license][badge-license]][badge-license-link]\n\n[badge-codecov]: https://codecov.io/gh/philips-software/odin/graph/badge.svg?token=18EKctyaW9\n[badge-codecov-link]: https://codecov.io/gh/philips-software/odin\n[badge-license]: https://img.shields.io/github/license/philips-software/odin\n[badge-license-link]: LICENSE.md\n[badge-npm]: https://img.shields.io/npm/v/@philips-software/odin?logo=npm\n[badge-npm-link]: https://www.npmjs.com/package/@philips-software/odin\n[badge-workflow-ci]: https://img.shields.io/github/actions/workflow/status/philips-software/odin/ci.yml?branch=main\u0026label=ci\u0026logo=github\n[badge-workflow-ci-link]: https://github.com/philips-software/odin/actions/workflows/ci.yml\n\nStands for on-demand dependency injection, enables a lazy loading pattern in JavaScript.\n\nDependency injection is a technique used to reduce concern about object creation and lifecycle. When delegating resource management to a dependency injection engine, it's possible to build a **flexible coupling** among resources.\n\n\u003e Resources are only instantiated when necessary. When a class is instantiated, not necessarily its dependencies will. By providing dependencies at the last possible moment, it's possible to save computational resources, improving performance and decreasing memory footprint.\n\n## Disclaimers\n\n#### Decorators\nSince `v3`, `odin` uses the new [stage 3 decorators](https://arai-a.github.io/ecma262-compare/?pr=2417) released with [TypeScript `v5`](https://devblogs.microsoft.com/typescript/announcing-typescript-5-0/#decorators). Ensure your build tool outputs code in a modern version of ECMAScript that supports this version of decorators.\n\u003e See:\n\u003e - https://arai-a.github.io/ecma262-compare/?pr=2417\n\u003e - https://devblogs.microsoft.com/typescript/announcing-typescript-5-0/#decorators\n\nIn `v1` and `v2` (legacy versions), `odin` used the [experimental stage 2 decorators proposal from TC39](https://tc39.es/proposal-decorators). Since that implementation of decorators is not natively supported by browsers or node, projects using `odin` had to rely on [babel](https://babeljs.io/) and its [decorators polyfill (in legacy mode)](https://babeljs.io/docs/en/babel-plugin-proposal-decorators).\n\u003e See:\n\u003e - https://tc39.es/proposal-decorators\n\u003e - https://www.typescriptlang.org/docs/handbook/decorators\n\u003e - https://babeljs.io\n\u003e - https://babeljs.io/docs/en/babel-plugin-proposal-decorators\n\n## Roadmap\n#### Breaking changes planned for future major versions\n- The [`@Injectable` decorator](#injectable-decorator) will no longer use the class name to register the injectable, which will instead have to be explicitly supplied through `options.name`. This allows build tools to change the class name during optimization and minification.\n  - [`@Injectable` with options](#injectable-with-options) will require `options.name`.\n  - [`@Injectable` without any arguments](#injectable-without-any-arguments) will be removed.\n- The [`@Inject` decorator](#inject-decorator) will no longer use the class field name to register the injection. The wanted name will instead have to be explicitly supplied through `options.name`. This allows build tools to change the class field name during optimization and minification.\n  - [`@Inject` with options](#inject-with-options) will require `options.name`.\n  - [`@Inject` with a string](#inject-with-a-string) will be removed, use [`@Inject` with options](#inject-with-options) instead.\n  - [`@Inject` without any arguments](#inject-without-any-arguments) will be removed, use [`@Inject` with options](#inject-with-options) instead.\n- The [`@Configuration` decorator](#configuration-decorator) and its [`configuration`](#configuration) will be removed and the `strict` mode behavior will be the default. Less magic is magic.\n\n## Documentation\n- [Installation](#installation)\n- [Getting started](#getting-started)\n- [API](#api)\n  - [Domain](#domain)\n  - [Bundle](#bundle)\n  - [Container](#container)\n  - Providers\n    - [CustomProvider](#customprovider)\n  - Resolvers\n    - [ValueResolver](#valueresolver)\n    - [FinalValueResolver](#finalvalueresolver)\n- [Decorators](#decorators)\n  - [Configuration](#configuration-decorator)\n  - [Injectable](#injectable-decorator)\n  - [Inject](#inject-decorator)\n  - [Initializer](#initializer-decorator)\n- [Configuration](#configuration)\n- [In-dept](#in-dept)\n  - [Registration lifecycle](#registration-lifecycle)\n  - [Consumption lifecycle](#consumption-lifecycle)\n  - [Provisioning lifecycle](#provisioning-lifecycle)\n  - [Resolution lifecycle](#resolution-lifecycle)\n  - [Instantiation lifecycle](#instantiation-lifecycle)\n- [Contributing](#contributing)\n\n## Installation\n```shell\nnpm i --save @philips-software/odin\n```\n\n## Getting started\n\nDeclare an injectable:\n```typescript\nimport { Injectable } from '@philips-software/odin';\n\n@Injectable\nclass InjectableExample {\n  sayHi() {\n    console.log('We rode here in their minds, and we took root.');\n  }\n}\n```\n\nInject the injectable into another injectable:\n```typescript\nimport { Inject, Injectable } from '@philips-software/odin';\n\n@Injectable\nclass UsageExample {\n\n  @Inject({ name: InjectableExample.name })\n  injectableExample;\n\n  run() {\n    this.injectableExample.sayHi();\n  }\n}\n```\n\nUse [`odin`](#api) to instantiate the injectable, which will inject its dependencies automagically:\n```typescript\nimport { odin } from '@philips-software/odin';\n\n// creates a container based on the root bundle\n// the container provides instances and dependencies\nconst container = odin.container();\n\n// provides an instance of UsageExample\nconst usageExample = container.provide(UsageExample.name, true);\n\n// uses the provided instance, which will inject and use its dependency \nusageExample.run(); // We rode here in their minds, and we took root.\n```\n\n## API\nThe exported `odin` core instance works like a facade object that exposes methods that can be used to manage injectables using [`bundles`](#bundle), and to provide/resolve injectables using [`containers`](#container).\n\nThis instance holds the root [`bundle`](#bundle), which is managed by `odin` itself. This instance a unique singleton, so everything that is registered using it will be available to the entire application.\n\n### Domain\nThe `domain` is used to identify [`bundles`](#bundle) within a hierarchy.\n\nDomains are useful to reduce coupling among different parts of the application by splitting it into smaller parts, making each part of application responsible for managing its own context-bound injectables. Injectables registered within a domain will only be available within the domain itself and its child domains, and not by parent or sibling domains.\n\nCheck the [Resolution lifecycle](#resolution-lifecycle).\n\n\u003e A `domain` is a `string` that represents a chain of [`bundles`](#bundle), hierarchically organized, with just a single level, or multiple levels separated by forward slashes (`/`).\n\n### `Bundle`\nThe `bundle` is used to manage the registration of injectables, working like a hierarchy of dictionaries. Injectables registered within a bundle will only be available within the bundle itself and its child bundles, and not by parent or sibling bundles.\n\nWhen using Odin's [`Injectable` decorator](#injectable-decorator), the injectables are registered into the bundles automatically.\n\nThis snippet shows how to register an injectable manually:\n```typescript\nimport { odin } from '@philips-software/odin';\n\nclass Injectable { }\n\n// gets odin's root bundle\nconst bundle = odin.bundle(); // accepts a domain\n\n// registers the injectable\nbundle.register(Injectable, { name: 'Injectable' });\n```\n\n### `Container`\nThe `container` is used to provide dependencies and/or values by resolving injectables registered into a [`bundle`](#bundle) or one of its parents or [custom providers](#customprovider).\n\nBy default, [`odin`](#api) containers can only provide instances of `class`, so the values provided by the containers are typically instances of those classes. However, `odin` also offers the possibility of implementing a [`CustomProvider`](#customprovider) to handle specific scenarios, when injecting another type of value is required, for example.\n\nThis snippet shows how to create/obtain a container:\n```typescript\nimport { odin } from '@philips-software/odin';\n\nclass Injectable { }\n\n// gets odin's root bundle\nconst bundle = odin.bundle(); // accepts a domain\n\n// registers the injectable\nbundle.register(Injectable, { name: 'Injectable' });\n\n// creates a new container based on odin's root bundle\nconst container = odin.container(); // accepts a domain\n```\n\n#### `provide(nameOrIdentifier: string, resolve: boolean): Instance | Resolver | undefined`\nThis method provides an `instance` of the injectable matching the `nameOrIdentifier` argument, or a `resolver` for it, depending on the `resolve` argument. Each time it's called, a new `resolver` is created, but a new `instance` will only be created if the injectable is not a `singleton` (or if the singleton hasn't yet been created or has already been discarded).\n\nWhen a new `instance` is created, its dependencies (declared using the [`@Inject` decorator](#inject-decorator)) will be bound to an accessor that will provide their value upon their first access (lazy inject by default). If a dependency is declared as `eager`, it will be bound to its provided value during instantiation.\n\n\u003e See: [Provisioning lifecycle](#provisioning-lifecycle).\n\nThis snippet shows how to provide an instance of a previously registered injectable using the container:\n```typescript\n// provides an instance of the previously registered injectable\ncontainer.provide(Injectable.name, true); // returns instance of Injectable\n```\n\n### `CustomProvider`\nThe `CustomProvider` is used to provide values for injects that are not provided by the [`container`](#container). Resolvers can be registered into the `CustomProvider`, which will be called to resolve identifiers when the container is not able to. An instance of `CustomProvider` can be shared with multiple containers, instead of creating duplicates. Sharing or not, beware of resource deallocation and leaks.\n\n\u003e The `CustomProvider` can be used directly, or be extended into your own custom provider.\n\nThis snippet shows how to create a `CustomProvider` and supply it to a [`container`](#container):\n```typescript\nimport { odin, CustomProvider } from '@philips-software/odin';\n\n// creates a new provider\nconst provider = new CustomProvider();\n\n// creates a new container, supplying the provider\nconst container = odin.container('domain', provider);\n```\n\n#### `register(nameOrIdentifier: string, resolver: Resolver): string`\nThis method is used to register a resolver to resolve a name or identifier when the [`container`](#container) is not able to resolve it itself. It receives a name or identifier and an instance of `ValueResolver` (itself or extended).\n\nThis snippet shows how to provide a counter that automatically increments itself every time it's injected:\n```typescript\nimport { odin, CustomProvider, ValueResolver } from '@philips-software/odin';\n\n// creates a new resolver\nconst resolver = new ValueResolver(() =\u003e {\n  // ...\n});\n\n// creates a new provider and registers the resolver with the provider\nconst provider = new CustomProvider();\nprovider.register('nameOrIdentifier', resolver);\n\n// creates a new container, supplying the provider\nconst container = odin.container('domain', provider);\n```\n\n### `ValueResolver`\nThis is the base of all resolvers. Any place that accepts a resolver will accept a `ValueResolver` or an extension of it. Its implementation is as simple as it gets: it accepts a resolver function in the constructor and where every time a resolution is requested, the resolver function is called to resolve the value.\n\n#### `get(injectable?: Injectable): Value`\nThis method is used to request a resolution, returning the resolved value.\n\nThis snippet shows a resolver of a counter that automatically increments itself every time it's injected:\n```typescript\nimport { ValueResolver } from '@philips-software/odin';\n\nlet autoIncrementCounter = 0;\n\nconst resolver = new ValueResolver(() =\u003e {\n  return ++autoIncrementCounter; // called 3 times\n});\n\nresolver.get(); // returns 1 (resolved)\nresolver.get(); // returns 2 (resolved)\nresolver.get(); // returns 3 (resolved)\n```\n\nThis method optionally accepts an instance of an injectable as the argument, which will be forwarded to the resolver function. When the resolution is requested by a [`container`](#container) while injecting the value into an injectable, the instance will be the instance into which the value being resolved will be injected.\n\nThis snippet shows a resolver that uses the instance argument:\n```typescript\nimport { odin, CustomProvider, ValueResolver } from '@philips-software/odin';\n\n@Injectable({ domain: 'domain' })\nclass Injectable {\n  @Inject({ name: 'nameOrIdentifier' })\n  value;\n}\n\n// creates a new resolver\nconst resolver = new ValueResolver((instance) =\u003e {\n  return instance;\n});\n\n// creates a new provider and registers the resolver with the provider\nconst provider = new CustomProvider();\nprovider.register('nameOrIdentifier', resolver);\n\n// creates a new container, supplying the provider\nconst container = odin.container('domain', provider);\n\n// requesting the resolution without being injected by a parent\ncontainer.provide('nameOrIdentifier', true); // returns undefined\n\n// requesting the resolution during injection within a parent\nconst instance = container.provide(Injectable.name, true); // returns instance of Injectable\ninstance.value // instance of Injectable\n```\n\n### `FinalValueResolver`\nThis resolver is used to speed up resolution by resolving only once and caching the value. The resolver function will be called only once, during the first resolution request. Any resolution requested after the first will be resolved to the cached value.\n\nThis snippet shows the effect this resolver would have on a counter that automatically increments itself every time it's injected:\n```typescript\nimport { FinalValueResolver } from '@philips-software/odin';\n\nlet autoIncrementCounter = 0;\n\nconst resolver = new FinalValueResolver(() =\u003e {\n  return ++autoIncrementCounter; // called only once\n});\n\nresolver.get(); // returns 1 (resolved)\nresolver.get(); // returns 1 (cached)\nresolver.get(); // returns 1 (cached)\n```\n\n## Decorators\n\n### `@Configuration` decorator\n\u003e _Deprecated, to be removed in a future major version. Strict mode will be the default and only behavior._\n\nThis decorator is used to apply a [configuration](#configuration) to [`odin`](#api) before other decorators are evaluated. For it to work, this decorator needs to be evaluated before any other decorator (in the import/evaluation tree). The options can be seen in the [`ConfigurationOptions` interface](./src/decorators/configuration.ts).\n\nThis snippet shows how to apply a configuration using the decorator:\n```typescript\nimport { Configuration } from '@philips-software/odin';\n\n@Configuration({ strict: true })\nclass OdinConfiguration { }\n```\n\n### `@Injectable` decorator\nThis decorator can only decorate injectable classes.\n\nIt registers the injectable in a [`bundle`](#bundle) using the class name.\n\n\u003e See: [Instantiation lifecycle](#instantiation-lifecycle).\n\n#### `@Injectable` with options\nThis is the recommended use of this decorator. It accepts options that configure the registration behavior. The options can be seen in the [`InjectableOptions` interface](./src/decorators/injectable.ts).\n\nThis snippet shows multiple variations of options and their effect:\n```typescript\n@Injectable({ domain: 'sample' })\nclass StandardInjectable { } // registerd in the sample domain, new instance every time\n\n@Injectable({ name: 'Custom' })\nclass NamedInjectable { } // registered with the supplied name\n\n@Injectable({ singleton: true })\nclass SingletonInjectable { } // registered in the root domain, same instance every time\n```\n\n#### `@Injectable` without any arguments\n\u003e _Deprecated, to be removed in a future major version. Use [`@Injectable` with options](#injectable-with-options) instead._\n\nThis variation is a syntax sugar for [`@Injectable` with options](#injectable-with-options). When no argument is provided, the decorator will use the class name as the `options.name` value.\n\nThis snippet shows how the class field name could be used as the name or identifier:\n```typescript\n@Injectable\nclass Injectable { }\n```\n\n### `@Inject` decorator\nThis decorator can only decorate class fields of injectables. Can be used multiple times per class.\n\nLearn more about its purpose in the [`container.provide` method](#providenameoridentifier-string-resolve-boolean-instance--resolver--undefined) documentation.\n\n\u003e See: [Instantiation lifecycle](#instantiation-lifecycle).\n\n#### `@Inject` with options\nThis is the recommended use of this decorator. It accepts options that configure the inject behavior. The options can be seen in the [`InjectOptions` interface](./src/decorators/inject.ts).\n\nThis snippet shows multiple variations of options and their effect:\n```typescript\n@Injectable\nclass Injectable { }\n\n@Injectable\nclass Usage {\n\n  @Inject({ name: 'Injectable' })\n  standardField; // provided upon first access\n\n  @Inject({ name: 'Injectable', eager: true })\n  eagerField; // provided after construction and before the initializer is called\n\n  @Inject({ name: 'Injectable', optional: true })\n  optionalField; // not provided if not found in the container\n\n}\n```\n\n#### `@Inject` with a string\n\u003e _Deprecated, to be removed in a future major version. Use [`@Inject` with options](#inject-with-options) instead._\n\nThis variation is a syntax sugar for [`@Inject` with options](#inject-with-options). When provided with a simple `string` as its argument, the `@Inject` decorator will use the `string` as the `options.name` value.\n\nThis snippet shows how the name or identifier can be supplied directly to the decorator:\n```typescript\n@Injectable\nclass Injectable { }\n\n@Injectable\nclass Usage {\n\n  @Inject('Injectable')\n  injectableField; // matches Injectable\n\n}\n```\n\n#### `@Inject` without any arguments\n\u003e _Deprecated, to be removed in a future major version. Use [`@Inject` with options](#inject-with-options) instead._\n\nThis variation is a syntax sugar for [`@Inject` with options](#inject-with-options). When no argument is provided, the decorator will use the class field name as the `options.name` value.\n\nThis snippet shows how the class field name could be used as the name or identifier:\n```typescript\n@Injectable\nclass Injectable { }\n\n@Injectable\nclass Usage {\n\n  @Inject\n  injectable; // matches Injectable\n\n}\n```\n\n### `@Initializer` decorator\nThis decorator can only decorate class methods of injectables. Only one per class is allowed.\n\nWhen a new injectable is created, its default constructor is called before injects are provided. The initializer method is called right after the injects are provided and available for use.\n\n\u003e See: [Instantiation lifecycle](#instantiation-lifecycle).\n\nThis snippet shows how to use this decorator to access injected fields after they are initialized:\n```typescript\n@Injectable\nclass Injectable { }\n\n@Injectable\nclass Usage {\n\n  @Inject({ name: 'Injectable' })\n  injectableField;\n\n  constructor() {\n    console.log(this.injectableField); // null\n  }\n\n  @Initializer\n  initialize(): void {\n    console.log(this.injectableField); // instance of Injectable\n  }\n}\n```\n\n## Configuration\n\u003e _Deprecated, to be removed in a future major version. Strict mode will be the default and only behavior._\n\nBehavior can be customized through this configuration.\n\nCheck the [`@Configuration` decorator](#configuration-decorator) to understand how to configure it.  \n\n### Strict mode\n\u003e _Deprecated, to be removed in a future major version. Strict mode will be the default and only behavior._\n\nThe `strict` mode indicates whether dependency injection is case-sensitive or not. It means that a name could be repeated when used with a different case, as seen below:\n```typescript\n@Injectable\nclass Injectable { }\n\n@Injectable\nclass INJECTABLE { }\n\n@Injectable\nclass Usage {\n\n  @Inject({ name: 'Injectable' })\n  injectable1;\n\n  @Inject({ name: 'INJECTABLE' })\n  injectable2;\n\n}\n```\n\n## In-dept\nTo better understand and use this library, or any other, it's very important to be aware of how things happen under the hood. Thus, being aware of the `odin` lifecycle and what exactly this on-demand feature means, is a good idea.\n\n### Registration lifecycle\nRepresents how the decorators are evaluated and used. The code is evaluated by the JavaScript engine (browser or node), and every time an [`@Injectable` decorator](#injectable-decorator) is found, it's registered in [`odin`](#api). In-class decorators (like [`@Inject`](#inject-decorator) and [`@Initializer`](#initializer-decorator)) are evaluated along with its respective injectable, and used to configure its behavior during other lifecycles.\n\n```mermaid\nflowchart TB\n    evaluation[code evaluation by the engine] --\u003e decorator[found a decorator \\n register in odin]\n    decorator --\u003e validation[odin validates injectable]\n    validation --\u003e available[injectable registered \\n available for use]\n    available --\u003e ready([application ready])\n    available -.-\u003e decorator\n```\n\n### Consumption lifecycle\nRepresents how the application consumes injectable instances during its execution.\n\nThe application is responsible for retrieving [`containers`](#container) from [`odin`](#api), which are then used to retrieve instances of injectables and/or values previously registered in [`bundles`](#bundle) or [`providers`](#customprovider), while considering their scope and managing their lifecycle. There is no rule regarding the number of active [`containers`](#container) at the same time during the application execution and each container is isolated and never interacts with other [`containers`](#container).\n\n\u003e The `container provides instance` step is detailed in [Provisioning lifecycle](#provisioning-lifecycle).\n\n```mermaid\nflowchart TB\n    ready[[application ready]] --\u003e run[container is created \\n application runs]\n    run --\u003e execution[normal execution]\n    execution --\u003e requestInjectable[requests injectable instance]\n    requestInjectable --\u003e provide[container provides instance]\n    provide --\u003e execution\n    execution -.-\u003e requestToDiscard[requests to discard a singleton instance]\n    requestToDiscard -.-\u003e discard[odin discards singleton instance]\n    discard -.-\u003e execution\n```\n\n### Provisioning lifecycle\nRepresents the logic flow used in [`container.provide`](#providenameoridentifier-string-resolve-boolean-instance--resolver--undefined).\n\n\u003e The `instantiate` step is detailed in [Instantiation lifecycle](#instantiation-lifecycle).\n\n```mermaid\nflowchart TB\n    provide[[container.provide]] --\u003e cached{is cached?}\n    cached --\u003e|yes| return{should resolve?}\n    cached --\u003e|no| exists{in container? \\n resolution}\n    return --\u003e|yes| returnInstance([returns instance/value])\n    return --\u003e|no| returnResolver([returns resolver])\n    exists --\u003e|yes| instantiate[instantiate]\n    exists --\u003e|no| provider{in custom provider?}\n    instantiate --\u003e singleton{is singleton?}\n    provider --\u003e|yes| return\n    provider --\u003e|no| returnUndefined([returns undefined])\n    singleton --\u003e|yes| cache[add to the cache]\n    singleton --\u003e|no| return\n    cache --\u003e return\n```\n\n### Resolution lifecycle\nRepresents placement within domains and the flow of resolution through the hierarchy.\n\nInjectables can only be found within the hierarchy by searching through the parent domain when not found in the child domain, recursively. A domain that is not directly in the hierarchy line (like a sibling or an uncle), cannot be reached. The hierarchy is only searched upwards and never downwards.\n\n\u003e See: [`domain`](#domain), [`bundle`](#bundle) and [`container`](#container).\n\n```mermaid\nflowchart\n    subgraph R [root domain]\n        direction BT\n        RI(injectables)\n\n        subgraph A1 [domain A1]\n            A1I(injectables)\n\n            subgraph A2 [domain A2]\n                A2I(injectables)\n            end\n        end\n\n        subgraph B [domain B]\n            BI(injectables)\n        end\n    end\n\n\n    A1I --\u003e|check parent| RI\n    BI --\u003e|check parent| RI\n    A2I --\u003e|check parent| A1I\n\n    A1I x-.-x|not in hierarchy| BI\n    A2I x-.-x|not in hierarchy| BI\n```\n\n### Instantiation lifecycle\nRepresents how an injectable is instantiated.\n\nAn inject will only be resolved when necessary. When an instance is created by `odin`, it doesn't eagerly resolve the injections. This strategy is adopted to reduce as much of the unnecessary creation of resources as possible. By default, all injections are lazy, but this behavior can be changed through in a case-by-case basis through [`@Inject` with options](#inject-with-options).\n\n\u003e See: [`@Injectable`](#injectable-decorator), [`@Inject`](#inject-decorator) and [`@Initializer`](#initializer-decorator).\n\n```mermaid\nflowchart TB\n    instantiation[[Injectable instantiation]] --\u003e invokeConstructor[invoke class constructor]\n    invokeConstructor --\u003e inject[bind inject to field]\n    inject --\u003e eager{is eager?}\n    eager --\u003e|yes| resolve{in container?}\n    eager --\u003e|no| bindAccessor[bind get accessor to field]\n    resolve --\u003e bindInstance[bind resolved instance/value to field]\n    bindAccessor --\u003e  invokeInitializer[invoke initializer]\n    bindInstance --\u003e invokeInitializer\n    invokeInitializer --\u003e ready[instance ready for use]\n    invokeInitializer -.-\u003e fieldAccess\n\n    subgraph R [normal instance usage]\n        ready -.-\u003e fieldAccess[field access]\n        fieldAccess --\u003e firstAccess{first access?}\n        firstAccess --\u003e|yes| bindInstanceUponFirstUse([bind resolved instance/value\\nif not already resolved])\n        firstAccess --\u003e|no| secondaryAccess([use already bound instance/value])\n        bindInstanceUponFirstUse --\u003e ready\n        secondaryAccess --\u003e ready\n    end\n```\n\n## Contributing\n\nSee our [contribution guide](./CONTRIBUTING.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fphilips-software%2Fodin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fphilips-software%2Fodin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fphilips-software%2Fodin/lists"}