{"id":24355560,"url":"https://github.com/slavamuravey/vorarbeiter","last_synced_at":"2025-04-10T03:13:25.205Z","repository":{"id":271878532,"uuid":"914847479","full_name":"slavamuravey/vorarbeiter","owner":"slavamuravey","description":":syringe: A simple service container :rocket:","archived":false,"fork":false,"pushed_at":"2025-02-18T06:52:34.000Z","size":310,"stargazers_count":12,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-10T03:13:13.767Z","etag":null,"topics":["dependency-injection","inversion-of-control","javascript-dependency-injection","javascript-library","service-container","vorarbeiter"],"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/slavamuravey.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":"2025-01-10T12:36:04.000Z","updated_at":"2025-03-14T06:59:49.000Z","dependencies_parsed_at":"2025-01-10T13:46:19.043Z","dependency_job_id":"acb32634-4d46-47e7-8e31-73b512157bb7","html_url":"https://github.com/slavamuravey/vorarbeiter","commit_stats":null,"previous_names":["slavamuravey/vorarbeiter"],"tags_count":19,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slavamuravey%2Fvorarbeiter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slavamuravey%2Fvorarbeiter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slavamuravey%2Fvorarbeiter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slavamuravey%2Fvorarbeiter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/slavamuravey","download_url":"https://codeload.github.com/slavamuravey/vorarbeiter/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248148240,"owners_count":21055548,"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":["dependency-injection","inversion-of-control","javascript-dependency-injection","javascript-library","service-container","vorarbeiter"],"created_at":"2025-01-18T17:58:03.550Z","updated_at":"2025-04-10T03:13:25.198Z","avatar_url":"https://github.com/slavamuravey.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg\n      alt=\"Vorarbeiter\"\n      src=\"https://repository-images.githubusercontent.com/914847479/afa74c78-891d-4717-a0ca-9470ee294d63\"\n  /\u003e\n\u003c/p\u003e\n\n## A simple service container\n\n### Installation\n\n```shell\nnpm install vorarbeiter\n```\n\n### Basic usage\n\n1. Create some services:\n```typescript\ninterface Car {\n  getDriverName(): string;\n}\n\nclass CarImpl implements Car {\n  constructor(private readonly driver: Driver) {}\n  getDriverName() {\n    return this.driver.getName();\n  }\n}\n\ninterface Driver {\n  getName(): string;\n}\n\nclass DriverImpl implements Driver {\n  getName() {\n    return \"Michael Schumacher\";\n  }\n}\n```\n\n2. Explain to _Service Container_ how to create services, use factories for this:\n```typescript\nclass CarFactory implements ServiceFactory {\n  create(container: ServiceContainer): CarImpl {\n    const driver = container.get(\"driver\");\n    return new CarImpl(driver);\n  }\n}\n```\n\n3. Create _Service Specification_:\n\n```typescript\nimport { createServiceSpecBuilder } from \"vorarbeiter\";\n\nconst specBuilder = createServiceSpecBuilder();\n\nspecBuilder.set(\"car\", new CarFactory());\nspecBuilder.set(\"driver\", () =\u003e new DriverImpl());\n\nconst spec = specBuilder.getServiceSpec();\n```\nIf creating a service is trivial, as for `driver`, we can simply pass a function as a factory.\nAs for class based factory we can pass `ServiceContainer` as a function parameter.\n\n4. Create _Service Container_ with this _Service Specification_:\n\n```typescript\nimport { createServiceContainer } from \"vorarbeiter\";\n\nconst serviceContainer = createServiceContainer(spec);\n```\n\n5. Get some service and call its method:\n```typescript\nconst car: Car = serviceContainer.get(\"car\");\n\nconsole.log(car.getDriverName());\n```\n\n6. Get string \"Michael Schumacher\".\n\n### Service scope\n\n#### Shared\n\nBy default, services have global scope. It means that the same service will be shared across whole application.\n\nExample:\n```typescript\nlet serviceInstance1;\nlet serviceInstance2;\n// In some part of our application we get a service\nserviceInstance1 = serviceContainer.get(\"myService\");\n// In some another part of our application we get a service again\nserviceInstance2 = serviceContainer.get(\"myService\");\n\nconsole.log(serviceInstance1 === serviceInstance2); // true\n```\n\n#### Scoped\n\nSometimes we need to have service uniqueness within a specific scope, for example, within one user request.\nTo do that we should specify the _Context Resolver_ when configure _Service Specification_. Resolving result of the _Context Resolver_ should be **any object**.\nTo imitate situation when we have two different contexts we can use AsyncLocalStorage from \"node:async_hooks\" package.\n\nExample:\n```typescript\nconst asyncLocalStorage = new AsyncLocalStorage\u003cobject\u003e();\nspecBuilder\n  .set(\"myScopedService\", () =\u003e ({ serviceName: \"Awesome service\" }))\n  .scoped(() =\u003e asyncLocalStorage.getStore());\n\nconst serviceContainer = createServiceContainer(specBuilder.getServiceSpec());\n\nlet scopedService1;\n{\n  let scopedService2;\n\n  asyncLocalStorage.run({}, () =\u003e {\n    scopedService1 = serviceContainer.get(\"myScopedService\");\n    scopedService2 = serviceContainer.get(\"myScopedService\");\n  });\n  console.log(scopedService1 === scopedService2);\n}\n\n{\n  let scopedService3;\n  let scopedService4;\n  asyncLocalStorage.run({}, () =\u003e {\n    scopedService3 = serviceContainer.get(\"myScopedService\");\n    scopedService4 = serviceContainer.get(\"myScopedService\");\n  });\n  console.log(scopedService1 === scopedService3);\n  console.log(scopedService3 === scopedService4);\n}\n\n// Output:\n// true\n// false\n// true\n```\n\n#### Transient\n\nSometimes we need get new instance of a service each time we get it. \nTo do this, we should make the service as _transient_.\n\nExample:\n\n```typescript\nconst specBuilder = createServiceSpecBuilder();\nspecBuilder.set(\"myService\", () =\u003e ({ serviceName: \"My service\" })).transient();\n\nconst spec = specBuilder.getServiceSpec();\n\nconst serviceContainer = createServiceContainer(spec);\n\nconsole.log(serviceContainer.get(\"myService\") === serviceContainer.get(\"myService\")); // false\n```\n\n### Injection after service creation\n\nThe most common type of injection is constructor injection. \nThis type of injection occurs when creating service.\nBut sometimes we want to inject after the service has been created.\nFor this we can specify _Service Injector_ for the service:\n```typescript\nspecBuilder.set(\"injectorService\", () =\u003e {\n  return new class {\n    car!: Car;\n    driver!: Driver;\n    setDriver(driver: Driver) {\n      this.driver = driver;\n    }\n  };\n}).withInjector((service, container) =\u003e {\n  service.car = container.get(\"car\");\n  service.setDriver(container.get(\"driver\"));\n});\n```\nThis way we can perform property and setter injection.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fslavamuravey%2Fvorarbeiter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fslavamuravey%2Fvorarbeiter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fslavamuravey%2Fvorarbeiter/lists"}