{"id":15582968,"url":"https://github.com/nfroidure/knifecycle","last_synced_at":"2025-04-09T11:09:35.800Z","repository":{"id":10714037,"uuid":"66135588","full_name":"nfroidure/knifecycle","owner":"nfroidure","description":"Manage your NodeJS processes's lifecycle automatically with an unobtrusive dependency injection implementation.","archived":false,"fork":false,"pushed_at":"2025-02-17T13:46:49.000Z","size":2844,"stargazers_count":33,"open_issues_count":11,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-02T10:12:24.318Z","etag":null,"topics":["dependency-injection-container","function-decorator","hacktoberfest","handler-functions","inversion-of-control","javascript","nodejs"],"latest_commit_sha":null,"homepage":"https://insertafter.com/en/blog/unobstrusive_dependency_injection_with_knifecycle.html","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/nfroidure.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":".github/CONTRIBUTING","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":".github/CODE_OF_CONDUCT.md","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},"funding":{"github":["nfroidure"]}},"created_at":"2016-08-20T07:58:36.000Z","updated_at":"2025-02-17T13:46:52.000Z","dependencies_parsed_at":"2024-05-28T16:07:33.195Z","dependency_job_id":"d88d6a0f-8956-4231-82ce-43292913adb1","html_url":"https://github.com/nfroidure/knifecycle","commit_stats":{"total_commits":278,"total_committers":5,"mean_commits":55.6,"dds":"0.38848920863309355","last_synced_commit":"796f9e0348a2bcb24b2558bed2163fb48cba7391"},"previous_names":[],"tags_count":105,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nfroidure%2Fknifecycle","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nfroidure%2Fknifecycle/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nfroidure%2Fknifecycle/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nfroidure%2Fknifecycle/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nfroidure","download_url":"https://codeload.github.com/nfroidure/knifecycle/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248027406,"owners_count":21035594,"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-container","function-decorator","hacktoberfest","handler-functions","inversion-of-control","javascript","nodejs"],"created_at":"2024-10-02T20:03:09.993Z","updated_at":"2025-04-09T11:09:35.778Z","avatar_url":"https://github.com/nfroidure.png","language":"TypeScript","readme":"[//]: # ( )\n[//]: # (This file is automatically generated by a `metapak`)\n[//]: # (module. Do not change it  except between the)\n[//]: # (`content:start/end` flags, your changes would)\n[//]: # (be overridden.)\n[//]: # ( )\n# knifecycle\n\u003e Manage your NodeJS processes's lifecycle automatically with an unobtrusive dependency injection implementation.\n\n[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/nfroidure/knifecycle/blob/main/LICENSE)\n\n\n[//]: # (::contents:start)\n\n![The Knifecycle logo](./knifecycle.svg)\n\nMost (maybe all) applications rely on two kinds of dependencies.\n\n![The lifecycle of most (all?) applications](./dependency-injection.svg)\n\n**The code dependencies** are fully covered by JavaScript modules in a testable\nmanner (with `mockery` or `System` directly). There is no need for another\ndependency management system if those libraries are pure functions (involve no\nglobal states at all).\n\nUnfortunately, applications often rely on **global states** where the JavaScript\nmodule system shows its limits. This is where `knifecycle` enters the game.\n\n![The app lifecycle sequence graph](./dependencies-graph-sequences.svg)\n\nIt is largely inspired by the Angular service system except it should not\nprovide code but access to global states (time, filesystem, db). It also have an\nimportant additional feature to shutdown processes which is really useful for\nback-end servers and doesn't exists in Angular.\n\nLast but not least, you can build your code with Knifecycle so that once in\nproduction, it do not have to resolve the dependency tree leading to better\nperformances and reduce the bundle size (especially for tools like AWS Lambda /\nGCP Functions where each endpoint has its own zip).\n\nYou may want to look at the [architecture notes](./ARCHITECTURE.md) to better\nhandle the reasonning behind `knifecycle` and its implementation.\n\nAt this point you may think that a DI system is useless. My advice is that it\ndepends. But at least, you should not make a definitive choice and allow both\napproaches, Knifecycle permits this, most modules made usable by Knifecycle can\nin fact be used without it (this is also why static build works). See\n[this blog post](https://insertafter.com/en/blog/unobstrusive_dependency_injection_with_knifecycle.html)\nfor more context about this statement.\n\n## Features\n\n- services management: start services taking their dependencies in count and\n  shut them down the same way for graceful exits (namely dependency injection\n  with inverted control),\n- singleton: maintain singleton services across several running execution silos,\n- easy end to end testing: just replace your services per your own mocks and\n  stubs while ensuring your application integrity between testing and\n  production,\n- isolation: isolate processing in a clean manner, per concerns;\n- functional programming ready: encapsulate global states allowing the rest of\n  your application to be purely functional,\n- no circular dependencies for services: while circular dependencies are not a\n  problem within purely functional libraries (require allows it), it may be\n  harmful for your services, `knifecycle` impeach that while providing an\n  `$injector` service à la Angular to allow accessing existing services\n  references if you really need to,\n- generate Mermaid graphs of the dependency tree,\n- auto-detect injected services names,\n- build raw initialization modules to avoid embedding Knifecycle in your builds,\n- optionally autoload services dependencies with custom logic.\n\nYou can find all Knifecycle comptabile modules on NPM with the\n[knifecycle keyword](https://www.npmjs.com/search?q=keywords:knifecycle).\n\n## Usage\n\nUsing `knifecycle` is all about declaring the services our application needs and\nrunning your application over it.\n\nLet's say we are building a CLI script. Here is how we would proceed with\nKnifecycle:\n\n```js\n// bin.js\nimport fs from 'fs';\nimport { YError } from 'YError';\nimport {\n  Knifecycle,\n  initializer,\n  constant,\n  inject,\n  name\n} from 'knifecycle';\n\n// First of all we create a new Knifecycle instance\nconst $ = new Knifecycle();\n\n// Some of our code with rely on the process environment\n// let's inject it as a constant instead of directly\n// pickking env vars in `process.env` to make our code\n// easily testable\n$.register(constant('ENV', process.env));\n\n// Let's do so for CLI args with another constant\n// in real world apps we would have created a service\n// that would parse args in a complexer way\n$.register(constant('ARGS', process.argv));\n\n// We want our CLI tool to rely on some configuration\n// Let's build an injectable service initializer that\n// reads environment variables via an injected but\n// optional `ENV` object\n// In a real world app, you may use the\n// `application-services` module services instead.\nasync function initConfig({ ENV = { CONFIG_PATH: '.' } }) {\n  await fs.promises.readFile(\n    ENV.CONFIG_PATH,\n    'utf-8',\n    (err, data) =\u003e {\n      if (err) {\n        reject(err);\n        return;\n      }\n      try {\n        resolve(JSON.parse(data));\n      } catch (err) {\n        reject(err);\n      }\n    },\n  );\n}\n\n// We are using the `initializer` decorator to\n// declare our service initializer specificities\n// and register it with our Knifecycle instance\n$.register(\n  initializer(\n    {\n      // we have to give our final service a name\n      // for further use in other services injections\n      name: 'CONFIG',\n      // we will need an `ENV` variable in the initializer\n      // so adding it in the injected dependencies. The `?`\n      // sign tells Knifecycle that the ENV dependency\n      // is optional\n      inject: ['?ENV'],\n      // our initializer is simple so we use the `service`\n      // type for the initializer which just indicate that\n      // the initializer will return a promise of the actual\n      // service\n      type: 'service',\n      // We don't want to read the config file everytime we\n      // inject it so declaring it as a singleton\n      singleton: true,\n    },\n    initConfig,\n  ),\n);\n\n// Our CLI also uses a database so let's write an\n// initializer for it (in a real world app you\n// can use `postgresql-service` instead):\nconst initDB = initializer(\n  {\n    name: 'db',\n    // Here we are injecting the previous `CONFIG` service\n    // as required so that our DB cannot be connected without\n    // having a proper config.\n    inject: ['CONFIG', 'DB_URI', '?log'],\n    // The initializer type is slightly different. Indeed,\n    // we need to manage the database connection errors\n    // and wait for it to flush before shutting down the\n    // process.\n    // A service provider returns a promise of a provider\n    // descriptor exposing:\n    // - a mandatory `service` property containing the\n    // actual service;\n    // - an optional `dispose` function allowing to\n    // gracefully close the service;\n    // - an optional `fatalErrorPromise` property to\n    // handle the service unrecoverable failure.\n    type: 'provider',\n    singleton: true,\n  },\n  async ({ CONFIG, DB_URI, log }) =\u003e {\n    const db = await MongoClient.connect(DB_URI, CONFIG.databaseOptions);\n    let fatalErrorPromise = new Promise((resolve, reject) =\u003e {\n      db.once('error', reject);\n    });\n\n    // Logging only if the `log` service is defined\n    log \u0026\u0026 log('info', 'db service initialized!');\n\n    return {\n      service: db,\n      dispose: db.close.bind(db, true),\n      fatalErrorPromise,\n    };\n  },\n);\n\n// Here we are registering our initializer apart to\n// be able to reuse it, we also declare the required\n// DB_URI constant it needs\n$.register(constant('DB_URI', 'posgresql://xxxx'));\n$.register(initDB);\n\n// Say we need to use two different DB server\n// We can reuse our initializer by tweaking\n// some of its properties\n$.register(constant('DB_URI2', 'posgresql://yyyy'));\n$.register(\n  // First we remap the injected dependencies. It will\n  // take the `DB_URI2` constant and inject it as\n  // `DB_URI`\n  inject(\n    ['CONFIG', 'DB_URI2\u003eDB_URI', '?log'],\n    // Then we override its name to make it\n    // available as a different service\n    name('db2', initDB),\n  ),\n);\n\n// A lot of NodeJS functions have some side effects\n// declaring them as constants allows you to easily\n// mock/monitor/patch it. The `common-services` NPM\n// module contains a few useful ones\n$.register(constant('now', Date.now.bind(Date)))\n  .register(constant('log', console.log.bind(console)))\n  .register(constant('exit', process.exit.bind(process)));\n\n// Finally, let's declare an `$autoload` service\n// to allow us to load only the initializers needed\n// to run the given commands\n$.register(\n  initializer(\n    {\n      name: '$autoload',\n      type: 'service',\n      inject: ['CONFIG', 'ARGS'],\n      // Note that the auto loader must be a singleton\n      singleton: true,\n    },\n    async ({ CONFIG, ARGS }) =\u003e\n      async (serviceName) =\u003e {\n        if ('command' !== serviceName) {\n          // Allows to signal that the dependency is not found\n          // so that optional dependencies doesn't impeach the\n          // injector to resolve the dependency tree\n          throw new YError('E_UNMATCHED_DEPENDENCY', serviceName);\n        }\n        try {\n          const path = CONFIG.commands + '/' + ARGS[2];\n          return {\n            path,\n            initializer: require(path).default,\n          };\n        } catch (err) {\n          throw new Error(`Cannot load ${serviceName}: ${ARGS[2]}!`);\n        }\n      },\n  ),\n);\n\n// At this point, nothing is running. To instanciate the\n// services, we have to create an execution silo using\n// them. Note that we required the `$instance` service\n// implicitly created by `knifecycle`\n$.run(['command', '$instance', 'exit', 'log'])\n  // Here, command contains the initializer eventually\n  // found by automatically loading a NodeJS module\n  // in the above `$autoload` service. The db connection\n  // will only be instanciated if that command needs it\n  .then(async ({ command, $instance, exit, log }) =\u003e {\n    try {\n      command();\n\n      log('It worked!');\n    } catch (err) {\n      log('It failed!', err);\n    } finally {\n      // Here we ensure every db connections are closed\n      // properly. We could have use `$.destroy()` the same\n      // way but this is to illustrate that the Knifecycle\n      // instance can be injected in services contexts\n      // (rarely done but good to know it exists)\n      await $instance.destroy().catch((err) =\u003e {\n        console.error('Could not exit gracefully:', err);\n        exit(1);\n      });\n    }\n  })\n  .catch((err) =\u003e {\n    console.error('Could not launch the app:', err);\n    process.exit(1);\n  });\n```\n\nRunning the following should make the magic happen:\n\n```sh\ncat \"{ commands: './commands'}\" \u003e config.json\nDEBUG=knifecycle CONFIG_PATH=./config.json node -r @babel/register bin.js mycommand test\n// Prints: Could not launch the app: Error: Cannot load command: mycommand!\n// (...stack trace)\n```\n\nOr at least, we still have to create commands, let's create the `mycommand` one:\n\n```js\n// commands/mycommand.js\nimport { initializer } from './dist';\n\n// A simple command that prints the given args\nexport default initializer(\n  {\n    name: 'command',\n    type: 'service',\n    // Here we could have injected whatever we declared\n    // in the previous file: db, now, exit...\n    inject: ['ARGS', 'log'],\n  },\n  async ({ ARGS, log }) =\u003e {\n    return () =\u003e log('Command args:', ARGS.slice(2));\n  },\n);\n```\n\nSo now, it works:\n\n```sh\nDEBUG=knifecycle CONFIG_PATH=./config.json node -r @babel/register bin.js mycommand test\n// Prints: Command args: [ 'mycommand', 'test' ]\n// It worked!\n```\n\nThis is a very simple example but you can find a complexer CLI usage with\n`(metapak)[https://github.com/nfroidure/metapak/blob/master/bin/metapak.js]`.\n\n## Auto detection\n\nKnifecycle also provide some utility function to automatically assign the\ninitializer property declarations, the following 3 ways to declare the `getUser`\nservice are equivalent:\n\n```js\nimport noop from 'noop';\nimport { autoInject, inject, initializer, autoService } from 'knifecycle';\n\ninitializer({\n  name: 'getUser',\n  inject: ['db', '?log'],\n  type: 'service',\n}, getUser);\n\nservice('getUser', autoInject(getUser)));\n\nautoService(getUser);\n\nasync function getUser({ db, log = noop}) {}\n```\n\nThat said, if you need to build your code with `webpack`/`babel` you may have to\nconvert auto-detections to raw declarations with the\n[babel-plugin-knifecycle](https://github.com/nfroidure/babel-plugin-knifecycle)\nplugin. You can also do this only for the performance improvements it brings.\n\nAlso, keep in mind that the auto-detection is based on a simple regular\nexpression so you should care to keep initializer signatures simple to avoid\nhaving a `E_AUTO_INJECTION_FAILURE` error. As a rule of thumb, avoid setting\ncomplex default values.\n\n```js\n// Won't work\nautoInject(async ({ log = () =\u003e {} }) =\u003e {});\n\n// Will work\nfunction noop() {}\nautoInject(async ({ log = noop }) =\u003e {});\n```\n\n## Debugging\n\nSimply use the DEBUG environment variable by setting it to 'knifecycle':\n\n```sh\nDEBUG=knifecycle npm t\n```\n\nThe output is very verbose but lead to a deep understanding of mechanisms that\ntake place under the hood.\n\n## Plans\n\nThe scope of this library won't change. However the plan is:\n\n- improve performances;\n- track bugs ;).\n\nI'll also share most of my own initializers and their stubs/mocks in order to\nlet you reuse it through your projects easily. Here are the current projects\nthat use this DI lib:\n\n- [common-services](https://github.com/nfroidure/common-services): contains the\n  services I use the most in my apps,\n- [memory-kv-store](https://github.com/nfroidure/memory-kv-store): a simple in\n  memory key-value store,\n- [whook](https://github.com/nfroidure/whook): a framework to build REST web\n  services.\n- [postgresql-service](https://github.com/nfroidure/postgresql-service): a\n  simple wrapper around the `pg` module,\n- [jwt-service](https://github.com/nfroidure/jwt-service): a simple wrapper\n  around the `jwt` module to simplify its use,\n- [ftp-service](https://github.com/nfroidure/ftp-service): a FTP client with\n  clean defaults.\n\nNotice that those modules remains usable without using Knifecycle at all which\nis maybe the best feature of this library 😉.\n\n[//]: # (::contents:end)\n\n# API\n## Classes\n\n\u003cdl\u003e\n\u003cdt\u003e\u003ca href=\"#Knifecycle\"\u003eKnifecycle\u003c/a\u003e\u003c/dt\u003e\n\u003cdd\u003e\u003c/dd\u003e\n\u003c/dl\u003e\n\n## Functions\n\n\u003cdl\u003e\n\u003cdt\u003e\u003ca href=\"#initInitializerBuilder\"\u003einitInitializerBuilder(services)\u003c/a\u003e ⇒ \u003ccode\u003ePromise.\u0026lt;function()\u0026gt;\u003c/code\u003e\u003c/dt\u003e\n\u003cdd\u003e\u003cp\u003eInstantiate the initializer builder service\u003c/p\u003e\n\u003c/dd\u003e\n\u003cdt\u003e\u003ca href=\"#initDispose\"\u003einitDispose()\u003c/a\u003e\u003c/dt\u003e\n\u003cdd\u003e\u003cp\u003eAllow to dispose the services of an\ninitialized silo content.\u003c/p\u003e\n\u003c/dd\u003e\n\u003cdt\u003e\u003ca href=\"#constant\"\u003econstant(name, value)\u003c/a\u003e ⇒ \u003ccode\u003efunction\u003c/code\u003e\u003c/dt\u003e\n\u003cdd\u003e\u003cp\u003eDecorator that creates an initializer for a constant value\u003c/p\u003e\n\u003c/dd\u003e\n\u003cdt\u003e\u003ca href=\"#service\"\u003eservice(serviceBuilder, [name], [dependencies], [singleton], [extra])\u003c/a\u003e ⇒ \u003ccode\u003efunction\u003c/code\u003e\u003c/dt\u003e\n\u003cdd\u003e\u003cp\u003eDecorator that creates an initializer from a service builder\u003c/p\u003e\n\u003c/dd\u003e\n\u003cdt\u003e\u003ca href=\"#autoService\"\u003eautoService(serviceBuilder)\u003c/a\u003e ⇒ \u003ccode\u003efunction\u003c/code\u003e\u003c/dt\u003e\n\u003cdd\u003e\u003cp\u003eDecorator that creates an initializer from a service\n builder by automatically detecting its name\n and dependencies\u003c/p\u003e\n\u003c/dd\u003e\n\u003cdt\u003e\u003ca href=\"#provider\"\u003eprovider(providerBuilder, [name], [dependencies], [singleton], [extra])\u003c/a\u003e ⇒ \u003ccode\u003efunction\u003c/code\u003e\u003c/dt\u003e\n\u003cdd\u003e\u003cp\u003eDecorator that creates an initializer for a provider\n builder\u003c/p\u003e\n\u003c/dd\u003e\n\u003cdt\u003e\u003ca href=\"#autoProvider\"\u003eautoProvider(providerBuilder)\u003c/a\u003e ⇒ \u003ccode\u003efunction\u003c/code\u003e\u003c/dt\u003e\n\u003cdd\u003e\u003cp\u003eDecorator that creates an initializer from a provider\n builder by automatically detecting its name\n and dependencies\u003c/p\u003e\n\u003c/dd\u003e\n\u003cdt\u003e\u003ca href=\"#parseDependencyDeclaration\"\u003eparseDependencyDeclaration(dependencyDeclaration)\u003c/a\u003e ⇒ \u003ccode\u003eObject\u003c/code\u003e\u003c/dt\u003e\n\u003cdd\u003e\u003cp\u003eExplode a dependency declaration an returns its parts.\u003c/p\u003e\n\u003c/dd\u003e\n\u003cdt\u003e\u003ca href=\"#stringifyDependencyDeclaration\"\u003estringifyDependencyDeclaration(dependencyDeclarationParts)\u003c/a\u003e ⇒ \u003ccode\u003eString\u003c/code\u003e\u003c/dt\u003e\n\u003cdd\u003e\u003cp\u003eStringify a dependency declaration from its parts.\u003c/p\u003e\n\u003c/dd\u003e\n\u003c/dl\u003e\n\n\u003ca name=\"Knifecycle\"\u003e\u003c/a\u003e\n\n## Knifecycle\n**Kind**: global class  \n\n* [Knifecycle](#Knifecycle)\n    * [new Knifecycle(options)](#new_Knifecycle_new)\n    * [.register(initializer)](#Knifecycle+register) ⇒ [\u003ccode\u003eKnifecycle\u003c/code\u003e](#Knifecycle)\n    * [.toMermaidGraph(options)](#Knifecycle+toMermaidGraph) ⇒ \u003ccode\u003eString\u003c/code\u003e\n    * [.run(dependenciesDeclarations)](#Knifecycle+run) ⇒ \u003ccode\u003ePromise\u003c/code\u003e\n    * [.destroy()](#Knifecycle+destroy) ⇒ \u003ccode\u003ePromise\u003c/code\u003e\n\n\u003ca name=\"new_Knifecycle_new\"\u003e\u003c/a\u003e\n\n### new Knifecycle(options)\nCreate a new Knifecycle instance\n\n**Returns**: [\u003ccode\u003eKnifecycle\u003c/code\u003e](#Knifecycle) - The Knifecycle instance  \n\n| Param | Type | Description |\n| --- | --- | --- |\n| options | \u003ccode\u003eObject\u003c/code\u003e | An object with options |\n| options.sequential | \u003ccode\u003eboolean\u003c/code\u003e | Allows to load dependencies sequentially (usefull for debugging) |\n\n**Example**  \n```js\nimport Knifecycle from 'knifecycle'\n\nconst $ = new Knifecycle();\n```\n\u003ca name=\"Knifecycle+register\"\u003e\u003c/a\u003e\n\n### knifecycle.register(initializer) ⇒ [\u003ccode\u003eKnifecycle\u003c/code\u003e](#Knifecycle)\nRegister an initializer\n\n**Kind**: instance method of [\u003ccode\u003eKnifecycle\u003c/code\u003e](#Knifecycle)  \n**Returns**: [\u003ccode\u003eKnifecycle\u003c/code\u003e](#Knifecycle) - The Knifecycle instance (for chaining)  \n\n| Param | Type | Description |\n| --- | --- | --- |\n| initializer | \u003ccode\u003efunction\u003c/code\u003e | An initializer |\n\n\u003ca name=\"Knifecycle+toMermaidGraph\"\u003e\u003c/a\u003e\n\n### knifecycle.toMermaidGraph(options) ⇒ \u003ccode\u003eString\u003c/code\u003e\nOutputs a Mermaid compatible dependency graph of the declared services.\nSee [Mermaid docs](https://github.com/knsv/mermaid)\n\n**Kind**: instance method of [\u003ccode\u003eKnifecycle\u003c/code\u003e](#Knifecycle)  \n**Returns**: \u003ccode\u003eString\u003c/code\u003e - Returns a string containing the Mermaid dependency graph  \n\n| Param | Type | Description |\n| --- | --- | --- |\n| options | \u003ccode\u003eObject\u003c/code\u003e | Options for generating the graph (destructured) |\n| options.shapes | \u003ccode\u003eArray.\u0026lt;Object\u0026gt;\u003c/code\u003e | Various shapes to apply |\n| options.styles | \u003ccode\u003eArray.\u0026lt;Object\u0026gt;\u003c/code\u003e | Various styles to apply |\n| options.classes | \u003ccode\u003eObject\u003c/code\u003e | A hash of various classes contents |\n\n**Example**  \n```js\nimport Knifecycle, { inject, constant, service } from 'knifecycle';\nimport appInitializer from './app';\n\nconst $ = new Knifecycle();\n\n$.register(constant('ENV', process.env));\n$.register(constant('OS', require('os')));\n$.register(service('app', inject(['ENV', 'OS'], appInitializer)));\n$.toMermaidGraph();\n\n// returns\ngraph TD\n  app--\u003eENV\n  app--\u003eOS\n```\n\u003ca name=\"Knifecycle+run\"\u003e\u003c/a\u003e\n\n### knifecycle.run(dependenciesDeclarations) ⇒ \u003ccode\u003ePromise\u003c/code\u003e\nCreates a new execution silo\n\n**Kind**: instance method of [\u003ccode\u003eKnifecycle\u003c/code\u003e](#Knifecycle)  \n**Returns**: \u003ccode\u003ePromise\u003c/code\u003e - Service descriptor promise  \n\n| Param | Type | Description |\n| --- | --- | --- |\n| dependenciesDeclarations | \u003ccode\u003eArray.\u0026lt;String\u0026gt;\u003c/code\u003e | Service name. |\n\n**Example**  \n```js\nimport Knifecycle, { constant } from 'knifecycle'\n\nconst $ = new Knifecycle();\n\n$.register(constant('ENV', process.env));\n$.run(['ENV'])\n.then(({ ENV }) =\u003e {\n // Here goes your code\n})\n```\n\u003ca name=\"Knifecycle+destroy\"\u003e\u003c/a\u003e\n\n### knifecycle.destroy() ⇒ \u003ccode\u003ePromise\u003c/code\u003e\nDestroy the Knifecycle instance\n\n**Kind**: instance method of [\u003ccode\u003eKnifecycle\u003c/code\u003e](#Knifecycle)  \n**Returns**: \u003ccode\u003ePromise\u003c/code\u003e - Full destruction promise  \n**Example**  \n```js\nimport Knifecycle, { constant } from 'knifecycle'\n\nconst $ = new Knifecycle();\n\n$.register(constant('ENV', process.env));\n$.run(['ENV'])\n.then(({ ENV }) =\u003e {\n   // Here goes your code\n\n   // Finally destroy the instance\n   $.destroy()\n})\n```\n\u003ca name=\"initInitializerBuilder\"\u003e\u003c/a\u003e\n\n## initInitializerBuilder(services) ⇒ \u003ccode\u003ePromise.\u0026lt;function()\u0026gt;\u003c/code\u003e\nInstantiate the initializer builder service\n\n**Kind**: global function  \n**Returns**: \u003ccode\u003ePromise.\u0026lt;function()\u0026gt;\u003c/code\u003e - A promise of the buildInitializer function  \n\n| Param | Type | Description |\n| --- | --- | --- |\n| services | \u003ccode\u003eObject\u003c/code\u003e | The services to inject |\n| services.$autoload | \u003ccode\u003eObject\u003c/code\u003e | The dependencies autoloader |\n\n**Example**  \n```js\nimport initInitializerBuilder from 'knifecycle/dist/build';\n\nconst buildInitializer = await initInitializerBuilder({\n  $autoload: async () =\u003e {},\n});\n```\n\u003ca name=\"initInitializerBuilder..buildInitializer\"\u003e\u003c/a\u003e\n\n### initInitializerBuilder~buildInitializer(dependencies) ⇒ \u003ccode\u003ePromise.\u0026lt;String\u0026gt;\u003c/code\u003e\nCreate a JavaScript module that initialize\na set of dependencies with hardcoded\nimport/awaits.\n\n**Kind**: inner method of [\u003ccode\u003einitInitializerBuilder\u003c/code\u003e](#initInitializerBuilder)  \n**Returns**: \u003ccode\u003ePromise.\u0026lt;String\u0026gt;\u003c/code\u003e - The JavaScript module content  \n\n| Param | Type | Description |\n| --- | --- | --- |\n| dependencies | \u003ccode\u003eArray.\u0026lt;String\u0026gt;\u003c/code\u003e | The main dependencies |\n\n**Example**  \n```js\nimport initInitializerBuilder from 'knifecycle/dist/build';\n\nconst buildInitializer = await initInitializerBuilder({\n  $autoload: async () =\u003e {},\n});\n\nconst content = await buildInitializer(['entryPoint']);\n```\n\u003ca name=\"initDispose\"\u003e\u003c/a\u003e\n\n## initDispose()\nAllow to dispose the services of an\ninitialized silo content.\n\n**Kind**: global function  \n\u003ca name=\"constant\"\u003e\u003c/a\u003e\n\n## constant(name, value) ⇒ \u003ccode\u003efunction\u003c/code\u003e\nDecorator that creates an initializer for a constant value\n\n**Kind**: global function  \n**Returns**: \u003ccode\u003efunction\u003c/code\u003e - Returns a new constant initializer  \n\n| Param | Type | Description |\n| --- | --- | --- |\n| name | \u003ccode\u003eString\u003c/code\u003e | The constant's name. |\n| value | \u003ccode\u003eany\u003c/code\u003e | The constant's value |\n\n**Example**  \n```js\nimport Knifecycle, { constant, service } from 'knifecycle';\n\nconst { printAnswer } = new Knifecycle()\n  .register(constant('THE_NUMBER', value))\n  .register(constant('log', console.log.bind(console)))\n  .register(service(\n    async ({ THE_NUMBER, log }) =\u003e () =\u003e log(THE_NUMBER),\n    'printAnswer',\n    ['THE_NUMBER', 'log'],\n  ))\n  .run(['printAnswer']);\n\nprintAnswer(); // 42\n```\n\u003ca name=\"service\"\u003e\u003c/a\u003e\n\n## service(serviceBuilder, [name], [dependencies], [singleton], [extra]) ⇒ \u003ccode\u003efunction\u003c/code\u003e\nDecorator that creates an initializer from a service builder\n\n**Kind**: global function  \n**Returns**: \u003ccode\u003efunction\u003c/code\u003e - Returns a new initializer  \n\n| Param | Type | Description |\n| --- | --- | --- |\n| serviceBuilder | \u003ccode\u003efunction\u003c/code\u003e | An async function to build the service |\n| [name] | \u003ccode\u003eString\u003c/code\u003e | The service's name |\n| [dependencies] | \u003ccode\u003eArray.\u0026lt;String\u0026gt;\u003c/code\u003e | The service's injected dependencies |\n| [singleton] | \u003ccode\u003eBoolean\u003c/code\u003e | Whether the service is a singleton or not |\n| [extra] | \u003ccode\u003eany\u003c/code\u003e | Eventual extra information |\n\n**Example**  \n```js\nimport Knifecycle, { constant, service } from 'knifecycle';\n\nconst { printAnswer } = new Knifecycle()\n  .register(constant('THE_NUMBER', value))\n  .register(constant('log', console.log.bind(console)))\n  .register(service(\n    async ({ THE_NUMBER, log }) =\u003e () =\u003e log(THE_NUMBER),\n    'printAnswer',\n    ['THE_NUMBER', 'log'],\n    true\n  ))\n  .run(['printAnswer']);\n\nprintAnswer(); // 42\n```\n\u003ca name=\"autoService\"\u003e\u003c/a\u003e\n\n## autoService(serviceBuilder) ⇒ \u003ccode\u003efunction\u003c/code\u003e\nDecorator that creates an initializer from a service\n builder by automatically detecting its name\n and dependencies\n\n**Kind**: global function  \n**Returns**: \u003ccode\u003efunction\u003c/code\u003e - Returns a new initializer  \n\n| Param | Type | Description |\n| --- | --- | --- |\n| serviceBuilder | \u003ccode\u003efunction\u003c/code\u003e | An async function to build the service |\n\n\u003ca name=\"provider\"\u003e\u003c/a\u003e\n\n## provider(providerBuilder, [name], [dependencies], [singleton], [extra]) ⇒ \u003ccode\u003efunction\u003c/code\u003e\nDecorator that creates an initializer for a provider\n builder\n\n**Kind**: global function  \n**Returns**: \u003ccode\u003efunction\u003c/code\u003e - Returns a new provider initializer  \n\n| Param | Type | Description |\n| --- | --- | --- |\n| providerBuilder | \u003ccode\u003efunction\u003c/code\u003e | An async function to build the service provider |\n| [name] | \u003ccode\u003eString\u003c/code\u003e | The service's name |\n| [dependencies] | \u003ccode\u003eArray.\u0026lt;String\u0026gt;\u003c/code\u003e | The service's dependencies |\n| [singleton] | \u003ccode\u003eBoolean\u003c/code\u003e | Whether the service is a singleton or not |\n| [extra] | \u003ccode\u003eany\u003c/code\u003e | Eventual extra information |\n\n**Example**  \n```js\nimport Knifecycle, { provider } from 'knifecycle'\nimport fs from 'fs';\n\nconst $ = new Knifecycle();\n\n$.register(provider(configProvider, 'config'));\n\nasync function configProvider() {\n  return new Promise((resolve, reject) {\n    fs.readFile('config.js', function(err, data) {\n      let config;\n\n      if(err) {\n        reject(err);\n        return;\n      }\n\n      try {\n        config = JSON.parse(data.toString);\n      } catch (err) {\n        reject(err);\n        return;\n      }\n\n      resolve({\n        service: config,\n      });\n    });\n  });\n}\n```\n\u003ca name=\"autoProvider\"\u003e\u003c/a\u003e\n\n## autoProvider(providerBuilder) ⇒ \u003ccode\u003efunction\u003c/code\u003e\nDecorator that creates an initializer from a provider\n builder by automatically detecting its name\n and dependencies\n\n**Kind**: global function  \n**Returns**: \u003ccode\u003efunction\u003c/code\u003e - Returns a new provider initializer  \n\n| Param | Type | Description |\n| --- | --- | --- |\n| providerBuilder | \u003ccode\u003efunction\u003c/code\u003e | An async function to build the service provider |\n\n\u003ca name=\"parseDependencyDeclaration\"\u003e\u003c/a\u003e\n\n## parseDependencyDeclaration(dependencyDeclaration) ⇒ \u003ccode\u003eObject\u003c/code\u003e\nExplode a dependency declaration an returns its parts.\n\n**Kind**: global function  \n**Returns**: \u003ccode\u003eObject\u003c/code\u003e - The various parts of it  \n\n| Param | Type | Description |\n| --- | --- | --- |\n| dependencyDeclaration | \u003ccode\u003eString\u003c/code\u003e | A dependency declaration string |\n\n**Example**  \n```js\nparseDependencyDeclaration('pgsql\u003edb');\n// Returns\n{\n  serviceName: 'pgsql',\n  mappedName: 'db',\n  optional: false,\n}\n```\n\u003ca name=\"stringifyDependencyDeclaration\"\u003e\u003c/a\u003e\n\n## stringifyDependencyDeclaration(dependencyDeclarationParts) ⇒ \u003ccode\u003eString\u003c/code\u003e\nStringify a dependency declaration from its parts.\n\n**Kind**: global function  \n**Returns**: \u003ccode\u003eString\u003c/code\u003e - The various parts of it  \n\n| Param | Type | Description |\n| --- | --- | --- |\n| dependencyDeclarationParts | \u003ccode\u003eObject\u003c/code\u003e | A dependency declaration string |\n\n**Example**  \n```js\nstringifyDependencyDeclaration({\n  serviceName: 'pgsql',\n  mappedName: 'db',\n  optional: false,\n});\n\n// Returns\n'pgsql\u003edb'\n```\n\n# Authors\n- [Nicolas Froidure](http://insertafter.com/en/index.html)\n\n# License\n[MIT](https://github.com/nfroidure/knifecycle/blob/main/LICENSE)\n","funding_links":["https://github.com/sponsors/nfroidure"],"categories":["Angular-Inspired Solutions"],"sub_categories":["Wrappers"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnfroidure%2Fknifecycle","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnfroidure%2Fknifecycle","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnfroidure%2Fknifecycle/lists"}