{"id":20094558,"url":"https://github.com/denostack/monopole","last_synced_at":"2025-12-11T21:14:06.902Z","repository":{"id":62422226,"uuid":"393616884","full_name":"denostack/monopole","owner":"denostack","description":" A versatile dependency injection container with features like value bindings, resolvers, aliases, and support for singleton, transient, and scoped lifetimes.","archived":false,"fork":false,"pushed_at":"2025-02-08T09:20:08.000Z","size":144,"stargazers_count":9,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-28T00:14:10.333Z","etag":null,"topics":["deno","dependency-injection"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/denostack.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-08-07T07:47:16.000Z","updated_at":"2025-02-09T18:54:24.000Z","dependencies_parsed_at":"2022-11-01T17:31:57.363Z","dependency_job_id":null,"html_url":"https://github.com/denostack/monopole","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/denostack%2Fmonopole","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/denostack%2Fmonopole/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/denostack%2Fmonopole/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/denostack%2Fmonopole/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/denostack","download_url":"https://codeload.github.com/denostack/monopole/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252622612,"owners_count":21778033,"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":["deno","dependency-injection"],"created_at":"2024-11-13T16:51:12.917Z","updated_at":"2025-12-11T21:14:06.870Z","avatar_url":"https://github.com/denostack.png","language":"TypeScript","readme":"# monopole \u003ca href=\"https://github.com/denostack\"\u003e\u003cimg src=\"https://raw.githubusercontent.com/denostack/images/main/logo.svg\" width=\"160\" align=\"right\" /\u003e\u003c/a\u003e\n\n\u003cp\u003e\n  \u003ca href=\"https://github.com/denostack/monopole/actions\"\u003e\u003cimg alt=\"Build\" src=\"https://img.shields.io/github/actions/workflow/status/denostack/monopole/ci.yml?branch=main\u0026logo=github\u0026style=flat-square\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://codecov.io/gh/denostack/monopole\"\u003e\u003cimg alt=\"Coverage\" src=\"https://img.shields.io/codecov/c/gh/denostack/monopole?style=flat-square\" /\u003e\u003c/a\u003e\n  \u003cimg alt=\"License\" src=\"https://img.shields.io/npm/l/monopole.svg?style=flat-square\" /\u003e\n  \u003cimg alt=\"Language Typescript\" src=\"https://img.shields.io/badge/language-Typescript-007acc.svg?style=flat-square\" /\u003e\n  \u003cbr /\u003e\n  \u003ca href=\"https://jsr.io/@denostack/monopole\"\u003e\u003cimg alt=\"JSR version\" src=\"https://jsr.io/badges/@denostack/monopole?style=flat-square\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://deno.land/x/monopole\"\u003e\u003cimg alt=\"Deno version\" src=\"https://deno.land/badge/monopole/version?style=flat-square\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://www.npmjs.com/package/monopole\"\u003e\u003cimg alt=\"NPM Version\" src=\"https://img.shields.io/npm/v/monopole.svg?style=flat-square\u0026logo=npm\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://npmcharts.com/compare/monopole?minimal=true\"\u003e\u003cimg alt=\"Downloads\" src=\"https://img.shields.io/npm/dt/monopole.svg?style=flat-square\" /\u003e\u003c/a\u003e\n\u003c/p\u003e\n\nA powerful and flexible dependency injection container for TypeScript/JavaScript\napplications. Monopole provides a modern, module-based approach to dependency\ninjection with support for async resolution, property injection using TC39 Stage\n3 decorators, and comprehensive lifecycle management.\n\n## Features\n\n- **Module-based architecture** - Organize dependencies with modules that\n  support imports/exports\n- **Multiple provider types** - Class, value, factory, and existing providers\n- **Property injection** - Using TC39 Stage 3 decorators (`@inject`)\n- **Async resolution** - Full support for async providers and initialization\n- **Circular dependency support** - Automatic resolution of circular\n  dependencies\n- **Lifecycle management** - Module boot and dispose hooks\n- **TypeScript first** - Full TypeScript support with type inference\n- **Framework agnostic** - Works with Deno, Node.js, and browsers\n  _(Node/Bun/browsers require a build step; see below)_\n\n## Runtime compatibility \u0026 build requirements\n\nMonopole relies on the TC39 Stage 3 decorators proposal. Today, only Deno\n(v1.40+)/Deno Deploy ship this syntax natively.\n\n| Runtime                                 | Native Stage 3 decorators              | What you need                                                                                                    |\n| --------------------------------------- | -------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |\n| **Deno**                                | ✅ (TS/TSX/JSX)                        | Works out of the box. Make sure you run `deno test`/`deno run` directly against the source files.                |\n| **Node.js / Bun**                       | ❌ (syntax error or legacy decorators) | Compile with TypeScript 5+ or Babel before running. The emitted JS no longer contains raw `@decorator` syntax.   |\n| **Browsers / Edge Functions / Workers** | ❌                                     | Bundle/transpile with your existing toolchain (Vite, Webpack, Rollup, etc.) so the shipped JS is decorator-free. |\n\n### Using TypeScript\n\nTypeScript 5.0 implements the new decorators proposal and accepts the syntax\nwithout `--experimentalDecorators`. A minimal `tsconfig.json`:\n\n```jsonc\n{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"module\": \"ESNext\",\n    \"experimentalDecorators\": false,\n    \"emitDecoratorMetadata\": false,\n    \"moduleResolution\": \"bundler\"\n  }\n}\n```\n\nCompile your sources (`tsc -p tsconfig.json` or via `ts-node --transpile-only`)\nand run the generated JS with Node/Bun.\n\n### Using Babel (via Vite/Webpack/Rollup)\n\nIf you stay in JavaScript, enable the official Stage 3 transform:\n\n```jsonc\n{\n  \"plugins\": [\n    [\"@babel/plugin-proposal-decorators\", { \"version\": \"2023-11\" }],\n    \"@babel/plugin-transform-class-properties\"\n  ]\n}\n```\n\nEnsure your bundler (Vite, Next.js, etc.) runs Babel on Monopole-using files so\nthe output no longer contains raw decorators before it reaches browsers/Node\nruntimes.\n\n## Installation\n\n### Deno\n\n```bash\ndeno add @denostack/monopole\n```\n\n```ts\nimport { createContainer } from \"@denostack/monopole\";\n```\n\n### Node.js \u0026 Browser (after transpiling)\n\n```bash\nnpm install monopole\n```\n\n```ts\nimport { createContainer } from \"monopole\";\n```\n\n\u003e ℹ️ Remember: runtimes other than Deno must load the transpiled output from the\n\u003e \"Runtime compatibility\" section above. Install the package, run it through\n\u003e TypeScript/Babel in your build, and execute/bundle the generated JavaScript.\n\n## Quick Start\n\n```ts\nimport { createContainer, inject, type Module } from \"monopole\";\n\n// Define services\nclass Logger {\n  log(message: string) {\n    console.log(`[LOG] ${message}`);\n  }\n}\n\nclass UserService {\n  @inject(Logger)\n  logger!: Logger;\n\n  getUser(id: string) {\n    this.logger.log(`Fetching user ${id}`);\n    return { id, name: \"John Doe\" };\n  }\n}\n\n// Create a module\nconst appModule: Module = {\n  providers: [\n    Logger,\n    UserService,\n  ],\n  exports: [UserService],\n};\n\n// Create and use container\nconst container = await createContainer(appModule);\nconst userService = container.get(UserService);\nuserService.getUser(\"123\");\n```\n\n## Core Concepts\n\n### Modules\n\nModules are the building blocks of your application. They encapsulate providers\nand can import other modules to compose your dependency graph.\n\n```ts\nimport type { Container, Module } from \"monopole\";\n\nconst databaseModule: Module = {\n  providers: [\n    { id: \"dbConfig\", useValue: { host: \"localhost\", port: 5432 } },\n    {\n      id: DatabaseConnection,\n      useFactory: async (config) =\u003e {\n        const conn = new DatabaseConnection(config);\n        await conn.connect();\n        return conn;\n      },\n      inject: [\"dbConfig\"],\n    },\n  ],\n  exports: [DatabaseConnection],\n  async dispose(container: Container) {\n    const conn = container.get(DatabaseConnection);\n    await conn.disconnect();\n  },\n};\n\nconst appModule: Module = {\n  imports: [databaseModule],\n  providers: [UserRepository],\n  exports: [UserRepository],\n};\n```\n\n### Providers\n\nMonopole supports four types of providers:\n\n#### Class Provider\n\n```ts\n// Direct class registration\nproviders: [MyService];\n\n// With explicit ID\nproviders: [{\n  id: \"myService\",\n  useClass: MyService,\n}];\n```\n\n#### Value Provider\n\n```ts\nproviders: [\n  { id: \"apiUrl\", useValue: \"https://api.example.com\" },\n  { id: \"config\", useValue: Promise.resolve({ key: \"value\" }) },\n];\n```\n\n#### Factory Provider\n\n```ts\nproviders: [{\n  id: HttpClient,\n  useFactory: (apiUrl: string) =\u003e new HttpClient(apiUrl),\n  inject: [\"apiUrl\"],\n}];\n```\n\n#### Existing Provider (Alias)\n\n```ts\nproviders: [\n  { id: Logger, useClass: ConsoleLogger },\n  { id: \"logger\", useExisting: Logger },\n];\n```\n\n### Property Injection\n\nUse the `@inject` decorator with TC39 Stage 3 decorator syntax:\n\n```ts\nimport { inject } from \"monopole\";\n\nclass OrderService {\n  @inject(Logger)\n  logger!: Logger;\n\n  @inject(DatabaseConnection)\n  db!: DatabaseConnection;\n\n  @inject(\"config\")\n  config!: Config;\n\n  // With transformation\n  @inject(UserService, (service) =\u003e service.getUser.bind(service))\n  getUser!: (id: string) =\u003e User;\n}\n```\n\n### Optional Dependencies\n\nFactory providers can specify optional dependencies:\n\n```ts\nproviders: [{\n  id: Service,\n  useFactory: (required, optional) =\u003e {\n    return new Service(required, optional ?? defaultValue);\n  },\n  inject: [\n    RequiredDep,\n    [OptionalDep, true], // true marks it as optional\n  ],\n}];\n```\n\n## Advanced Usage\n\n### Circular Dependencies\n\nMonopole automatically handles circular dependencies:\n\n```ts\nclass Parent {\n  @inject(Child)\n  child!: Child;\n}\n\nclass Child {\n  @inject(Parent)\n  parent!: Parent;\n}\n\nconst module: Module = {\n  providers: [Parent, Child],\n  exports: [Parent, Child],\n};\n\nconst container = await createContainer(module);\nconst parent = container.get(Parent);\nconst child = container.get(Child);\n\nconsole.log(parent.child === child); // true\nconsole.log(child.parent === parent); // true\n```\n\n### Module Composition\n\nCompose complex applications from smaller modules:\n\n```ts\n// Feature modules\nconst authModule: Module = {\n  providers: [AuthService, JwtService],\n  exports: [AuthService],\n};\n\nconst dataModule: Module = {\n  providers: [Database, UserRepository],\n  exports: [UserRepository],\n};\n\n// Application module\nconst appModule: Module = {\n  imports: [authModule, dataModule],\n  providers: [\n    {\n      id: AppService,\n      useFactory: (auth, repo) =\u003e new AppService(auth, repo),\n      inject: [AuthService, UserRepository],\n    },\n  ],\n  exports: [AppService],\n  async boot(container) {\n    // Initialize application\n    const app = container.get(AppService);\n    await app.initialize();\n  },\n  async dispose(container) {\n    // Cleanup\n    const app = container.get(AppService);\n    await app.shutdown();\n  },\n};\n\n// Create application\nconst container = await createContainer(appModule);\nconst app = container.get(AppService);\n```\n\n### Async Disposal\n\nContainers support the async disposal pattern:\n\n```ts\n// Using async disposal\nawait using container = await createContainer(appModule);\n// Container will be automatically disposed when going out of scope\n\n// Manual disposal\nconst container = await createContainer(appModule);\ntry {\n  // Use container\n} finally {\n  await container.dispose();\n}\n```\n\n## Examples\n\n- [Deno HTTP Server](./examples/deno-http-server) - Web application with modular\n  architecture\n\n## API Reference\n\n### `createContainer(module: Module): Promise\u003cContainer\u003e`\n\nCreates a new container from a module definition.\n\n### `Container`\n\n- `get\u003cT\u003e(id: ServiceIdentifier\u003cT\u003e): T` - Get a resolved instance\n- `has(id: ServiceIdentifier): boolean` - Check if a service exists\n- `entries(): IterableIterator\u003c[ServiceIdentifier, unknown]\u003e` - Get all entries\n- `dispose(): Promise\u003cvoid\u003e` - Dispose the container and all modules\n\n### `Module`\n\n- `imports?: Module[]` - Modules to import\n- `providers?: Provider[]` - Service providers\n- `exports?: ServiceIdentifier[]` - Exported service identifiers\n- `boot?(container: Container): MaybePromise\u003cvoid\u003e` - Initialization hook\n- `dispose?(container: Container): MaybePromise\u003cvoid\u003e` - Cleanup hook\n\n### `@inject(id: ServiceIdentifier, transformer?: (instance: T) =\u003e any)`\n\nProperty decorator for dependency injection.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdenostack%2Fmonopole","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdenostack%2Fmonopole","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdenostack%2Fmonopole/lists"}