{"id":19241674,"url":"https://github.com/exuanbo/di-wise","last_synced_at":"2025-04-05T13:10:09.125Z","repository":{"id":258213477,"uuid":"869816990","full_name":"exuanbo/di-wise","owner":"exuanbo","description":"🧙‍♀️ Lightweight and flexible dependency injection library for JavaScript and TypeScript, w/wo ECMAScript decorators.","archived":false,"fork":false,"pushed_at":"2025-02-12T17:13:16.000Z","size":4692,"stargazers_count":98,"open_issues_count":2,"forks_count":3,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-28T20:11:19.538Z","etag":null,"topics":["container","dependency-injection","di","ecmascript","ecmascript-decorator","injector","ioc","typescript"],"latest_commit_sha":null,"homepage":"https://exuanbo.xyz/di-wise/","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/exuanbo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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}},"created_at":"2024-10-09T00:07:13.000Z","updated_at":"2025-03-25T03:37:07.000Z","dependencies_parsed_at":"2024-10-26T13:43:12.292Z","dependency_job_id":"1bfba771-7b27-480f-a72b-9ae0e78c8be8","html_url":"https://github.com/exuanbo/di-wise","commit_stats":null,"previous_names":["exuanbo/di-wise"],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exuanbo%2Fdi-wise","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exuanbo%2Fdi-wise/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exuanbo%2Fdi-wise/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exuanbo%2Fdi-wise/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/exuanbo","download_url":"https://codeload.github.com/exuanbo/di-wise/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247339158,"owners_count":20923014,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["container","dependency-injection","di","ecmascript","ecmascript-decorator","injector","ioc","typescript"],"created_at":"2024-11-09T17:12:13.825Z","updated_at":"2025-04-05T13:10:09.096Z","avatar_url":"https://github.com/exuanbo.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# di-wise 🧙‍♀️\n\n[![NPM Version](https://img.shields.io/npm/v/di-wise.svg?color=blue\u0026logo=npm)](https://www.npmjs.com/package/di-wise)\n[![npm package minimized gzipped size](https://img.shields.io/bundlejs/size/di-wise@latest.svg?label=bundle%20size)](https://bundlejs.com/?q=di-wise)\n[![GitHub Workflow Status (with branch)](https://img.shields.io/github/actions/workflow/status/exuanbo/di-wise/test.yml.svg?branch=main)](https://github.com/exuanbo/di-wise/actions)\n[![Codecov (with branch)](https://img.shields.io/codecov/c/gh/exuanbo/di-wise/main.svg?token=65EfrU4Qnl)](https://app.codecov.io/gh/exuanbo/di-wise/tree/main/src)\n\nLightweight and flexible dependency injection library for JavaScript and TypeScript, w/wo ECMAScript decorators.\n\n## Table of Contents\n\n- [di-wise 🧙‍♀️](#di-wise-️)\n  - [Table of Contents](#table-of-contents)\n  - [Installation](#installation)\n  - [Features](#features)\n    - [Zero dependencies](#zero-dependencies)\n    - [Modern decorator implementation](#modern-decorator-implementation)\n    - [Context-based DI system](#context-based-di-system)\n    - [Multiple provider types](#multiple-provider-types)\n    - [Hierarchical injection](#hierarchical-injection)\n    - [Full control over registration and caching](#full-control-over-registration-and-caching)\n    - [Various injection scopes](#various-injection-scopes)\n      - [Inherited (default)](#inherited-default)\n      - [Transient](#transient)\n      - [Resolution](#resolution)\n      - [Container](#container)\n    - [Flexible token-based injection](#flexible-token-based-injection)\n    - [Automatic circular dependency resolution](#automatic-circular-dependency-resolution)\n    - [Dynamic injection](#dynamic-injection)\n    - [Constructor Injection](#constructor-injection)\n    - [Middleware](#middleware)\n  - [Usage](#usage)\n  - [API](#api)\n  - [Credits](#credits)\n  - [License](#license)\n\n## Installation\n\n```sh\nnpm install di-wise\n\npnpm add di-wise\n\nyarn add di-wise\n```\n\nAlso available on [JSR](https://jsr.io/@exuanbo/di-wise):\n\n```sh\ndeno add jsr:@exuanbo/di-wise\n```\n\n## Features\n\n### Zero dependencies\n\n- No need for [`reflect-metadata`](https://www.npmjs.com/package/reflect-metadata)\n- No TypeScript legacy [`experimentalDecorators`](https://www.typescriptlang.org/tsconfig/#experimentalDecorators) required\n\n### Modern decorator implementation\n\n- Built on ECMAScript Stage 3 Decorators: [tc39/proposal-decorators](https://github.com/tc39/proposal-decorators)\n- Native support in TypeScript [5.0+](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-0.html#decorators), swc [1.3.47+](https://swc.rs/docs/configuration/compilation#jsctransformdecoratorversion), and esbuild [0.21.0+](https://github.com/evanw/esbuild/releases/tag/v0.21.0)\n\n### Context-based DI system\n\n- Flexible decorator-based or function-based injection\n- Full type inference support ✨\n- Optional decorators with equivalent function alternatives\n\nExample:\n\n\u003c!-- prettier-ignore --\u003e\n```ts\nimport {createContainer, Inject, inject, Injectable, Scope, Scoped, Type} from \"di-wise\";\n\ninterface Spell {\n  cast(): void;\n}\nconst Spell = Type\u003cSpell\u003e(\"Spell\");\n\n@Scoped(Scope.Container)\n@Injectable(Spell)\nclass Fireball implements Spell {\n  cast() {\n    console.log(\"🔥\");\n  }\n}\n\nclass Wizard {\n  @Inject(Wand)\n  wand!: Wand;\n\n  // Equivalent to\n  wand = inject(Wand);\n\n  constructor(spell = inject(Spell)) {\n    // inject() can be used anywhere during construction\n    this.wand.store(spell);\n  }\n}\n\nconst container = createContainer();\ncontainer.register(Fireball);\n\n// Under the hood\n[Fireball, Spell].forEach((token) =\u003e {\n  container.register(\n    token,\n    {useClass: Fireball},\n    {scope: Scope.Container},\n  );\n});\n\nconst wizard = container.resolve(Wizard);\nwizard.wand.activate(); // =\u003e 🔥\n```\n\n### Multiple provider types\n\n- Class, Factory, and Value providers\n- Built-in helpers for one-off providers: `Build()`, `Value()`\n- Seamless integration with existing classes\n\nExample:\n\n```ts\nimport {Build, createContainer, inject, Value} from \"di-wise\";\n\nclass Wizard {\n  equipment = inject(\n    Cloak,\n    // Provide a default value\n    Value({\n      activate() {\n        console.log(\"👻\");\n      },\n    }),\n  );\n\n  wand: Wand;\n\n  constructor(wand: Wand) {\n    this.wand = wand;\n  }\n}\n\nconst container = createContainer();\n\nconst wizard = container.resolve(\n  Build(() =\u003e {\n    // inject() can be used in factory functions\n    const wand = inject(Wand);\n    return new Wizard(wand);\n  }),\n);\n\nwizard.equipment.activate(); // =\u003e 👻\n```\n\n### Hierarchical injection\n\n- Parent-child container relationships\n- Automatic token resolution through the container hierarchy\n- Isolated registration with shared dependencies\n\nExample:\n\n\u003c!-- prettier-ignore --\u003e\n```ts\nimport {createContainer, inject, Injectable, Type} from \"di-wise\";\n\nconst MagicSchool = Type\u003cstring\u003e(\"MagicSchool\");\nconst Spell = Type\u003c{cast(): void}\u003e(\"Spell\");\n\n// Parent container with shared config\nconst hogwarts = createContainer();\nhogwarts.register(MagicSchool, {useValue: \"Hogwarts\"});\n\n@Injectable(Spell)\nclass Fireball {\n  school = inject(MagicSchool);\n  cast() {\n    console.log(`🔥 from ${this.school}`);\n  }\n}\n\n// Child containers with isolated spells\nconst gryffindor = hogwarts.createChild();\ngryffindor.register(Fireball);\n\nconst slytherin = hogwarts.createChild();\nslytherin.register(Spell, {\n  useValue: {cast: () =\u003e console.log(\"🐍\")},\n});\n\ngryffindor.resolve(Spell).cast(); // =\u003e 🔥 from Hogwarts\nslytherin.resolve(Spell).cast();  // =\u003e 🐍\n```\n\n### Full control over registration and caching\n\n- Explicit container management without global state\n- Fine-grained control over instance lifecycle\n- Transparent registry access for testing\n\n### Various injection scopes\n\n- Flexible scoping system: `Inherited` (default), `Transient`, `Resolution`, `Container`\n- Smart scope resolution for dependencies\n- Configurable default scopes per container\n\nExample for singleton pattern:\n\n```ts\nimport {createContainer, Scope} from \"di-wise\";\n\nexport const singletons = createContainer({\n  defaultScope: Scope.Container,\n  autoRegister: true,\n});\n\n// Always resolves to the same instance\nconst wizard = singletons.resolve(Wizard);\n```\n\n#### Inherited (default)\n\nInherits the scope from its dependent. If there is no dependent (top-level resolution), behaves like `Transient`.\n\n\u003cdetails\u003e\n\u003csummary\u003eExample\u003c/summary\u003e\n\n\u003c!-- prettier-ignore --\u003e\n```ts\nimport {createContainer, Scope, Scoped} from \"di-wise\";\n\n@Scoped(Scope.Container)\nclass Wizard {\n  wand = inject(Wand);\n}\n\nconst container = createContainer();\ncontainer.register(\n  Wand,\n  {useClass: Wand},\n  {scope: Scope.Inherited},\n);\ncontainer.register(Wizard);\n\n// Dependency Wand will be resolved with \"Container\" scope\nconst wizard = container.resolve(Wizard);\n```\n\n\u003c/details\u003e\n\n#### Transient\n\nCreates a new instance every time the dependency is requested. No caching occurs.\n\n#### Resolution\n\nCreates one instance per resolution graph. The same instance will be reused within a single dependency resolution, but new instances are created for separate resolutions.\n\n\u003cdetails\u003e\n\u003csummary\u003eExample\u003c/summary\u003e\n\n```ts\n@Scoped(Scope.Resolution)\nclass Wand {}\n\nclass Inventory {\n  wand = inject(Wand);\n}\n\nclass Wizard {\n  inventory = inject(Inventory);\n  wand = inject(Wand);\n}\n\nconst container = createContainer();\nconst wizard = container.resolve(Wizard);\n\nexpect(wizard.inventory.wand).toBe(wizard.wand);\n```\n\n\u003c/details\u003e\n\n#### Container\n\nCreates one instance per container (singleton pattern). The instance is cached and reused for all subsequent resolutions within the same container.\n\n### Flexible token-based injection\n\n- Multiple token resolution with union type inference ✨\n- Support for optional dependencies via `Type.Null` and `Type.Undefined`\n- Interface-based token system\n\nExample:\n\n```ts\nimport {inject, Type} from \"di-wise\";\n\nclass Wizard {\n  wand = inject(Wand, Type.Null);\n  // ^? (property) Wizard.wand: Wand | null\n\n  spells = injectAll(Spell, Type.Null);\n  // ^? (property) Wizard.spells: Spell[]\n  // =\u003e []\n}\n```\n\n### Automatic circular dependency resolution\n\n- Smart handling of circular dependencies\n- Multiple resolution strategies (`@Inject()` or `inject.by()`)\n- Maintains type safety\n\nExample:\n\n```ts\nimport {createContainer, Inject, inject} from \"di-wise\";\n\nclass Wand {\n  owner = inject(Wizard);\n}\n\nclass Wizard {\n  @Inject(Wand)\n  wand!: Wand;\n\n  // Equivalent to\n  wand = inject.by(this, Wand);\n}\n\nconst container = createContainer();\nconst wizard = container.resolve(Wizard);\n\nexpect(wizard.wand.owner).toBe(wizard);\n```\n\n### Dynamic injection\n\n- On-demand dependency resolution via `Injector`\n- Context-aware lazy loading\n- Preserves proper scoping and circular dependency handling\n\nExample:\n\n```ts\nimport {createContainer, inject, Injector} from \"di-wise\";\n\nclass Wizard {\n  private injector = inject(Injector);\n  private wand?: Wand;\n\n  getWand() {\n    // Lazy load wand only when needed\n    return (this.wand ??= this.injector.inject(Wand));\n  }\n\n  castAllSpells() {\n    // Get all registered spells\n    const spells = this.injector.injectAll(Spell);\n    spells.forEach((spell) =\u003e spell.cast());\n  }\n}\n\nconst container = createContainer();\nconst wizard = container.resolve(Wizard);\n\nwizard.getWand(); // =\u003e Wand\n```\n\nThe injector maintains the same resolution context as its injection point, allowing proper handling of scopes and circular dependencies:\n\n```ts\nimport {createContainer, inject, Injector} from \"di-wise\";\n\nclass Wand {\n  owner = inject(Wizard);\n}\n\nclass Wizard {\n  private injector = inject.by(this, Injector);\n\n  getWand() {\n    return this.injector.inject(Wand);\n  }\n}\n\nconst container = createContainer();\nconst wizard = container.resolve(Wizard);\n\nconst wand = wizard.getWand();\nexpect(wand.owner).toBe(wizard);\n```\n\n### Constructor Injection\n\nSee discussion [Does di-wise support constructor injection? #12](https://github.com/exuanbo/di-wise/discussions/12#discussioncomment-11202986)\n\n### Middleware\n\n- Extensible container behavior through middleware\n- Composable middleware chain with predictable execution order\n- Full access to container lifecycle\n\nExample:\n\n```ts\nimport {applyMiddleware, createContainer, type Middleware} from \"di-wise\";\n\nconst logger: Middleware = (composer, _api) =\u003e {\n  composer\n    .use(\"resolve\", (next) =\u003e (token) =\u003e {\n      console.log(\"Resolving:\", token.name);\n      const result = next(token);\n      console.log(\"Resolved:\", token.name);\n      return result;\n    })\n    .use(\"resolveAll\", (next) =\u003e (token) =\u003e {\n      console.log(\"Resolving all:\", token.name);\n      const result = next(token);\n      console.log(\"Resolved all:\", token.name);\n      return result;\n    });\n};\n\nconst performanceTracker: Middleware = (composer, _api) =\u003e {\n  composer.use(\"resolve\", (next) =\u003e (token) =\u003e {\n    const start = performance.now();\n    const result = next(token);\n    const end = performance.now();\n    console.log(`Resolution time for ${token.name}: ${end - start}ms`);\n    return result;\n  });\n};\n\nconst container = applyMiddleware(createContainer(), [logger, performanceTracker]);\n\n// Use the container with applied middlewares\nconst wizard = container.resolve(Wizard);\n```\n\nMiddlewares are applied in array order but execute in reverse order, allowing outer middlewares to wrap and control the behavior of inner middlewares.\n\n## Usage\n\n🏗️ WIP (PR welcome)\n\n## API\n\nSee [API documentation](https://exuanbo.github.io/di-wise/modules.html).\n\n## Credits\n\nInspired by:\n\n- [jeffijoe/awilix](https://github.com/jeffijoe/awilix)\n- [inversify/InversifyJS](https://github.com/inversify/InversifyJS)\n- [microsoft/tsyringe](https://github.com/microsoft/tsyringe)\n\n## License\n\n[MIT License](https://github.com/exuanbo/di-wise/blob/main/LICENSE) @ 2024-Present [Xuanbo Cheng](https://github.com/exuanbo)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fexuanbo%2Fdi-wise","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fexuanbo%2Fdi-wise","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fexuanbo%2Fdi-wise/lists"}