{"id":16670490,"url":"https://github.com/hlapp/node-red-embedded-start","last_synced_at":"2025-04-09T19:41:45.361Z","repository":{"id":65424736,"uuid":"83221984","full_name":"hlapp/node-red-embedded-start","owner":"hlapp","description":"Enables waiting for flows to have started for embedded Node-RED applications","archived":false,"fork":false,"pushed_at":"2018-02-04T19:58:50.000Z","size":32,"stargazers_count":11,"open_issues_count":0,"forks_count":1,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-28T02:49:46.669Z","etag":null,"topics":["embedded-applications","iot","node-red"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","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/hlapp.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}},"created_at":"2017-02-26T16:08:16.000Z","updated_at":"2023-05-18T01:39:49.000Z","dependencies_parsed_at":"2023-01-23T02:05:11.787Z","dependency_job_id":null,"html_url":"https://github.com/hlapp/node-red-embedded-start","commit_stats":{"total_commits":29,"total_committers":1,"mean_commits":29.0,"dds":0.0,"last_synced_commit":"b53632d8813a6f3d0af46229e389362422f78218"},"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hlapp%2Fnode-red-embedded-start","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hlapp%2Fnode-red-embedded-start/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hlapp%2Fnode-red-embedded-start/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hlapp%2Fnode-red-embedded-start/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hlapp","download_url":"https://codeload.github.com/hlapp/node-red-embedded-start/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247726662,"owners_count":20985952,"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":["embedded-applications","iot","node-red"],"created_at":"2024-10-12T11:38:36.674Z","updated_at":"2025-04-09T19:41:45.344Z","avatar_url":"https://github.com/hlapp.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Build Status](https://travis-ci.org/hlapp/node-red-embedded-start.svg?branch=master)](https://travis-ci.org/hlapp/node-red-embedded-start)\n[![npm](https://img.shields.io/npm/v/node-red-embedded-start.svg)](https://www.npmjs.com/package/node-red-embedded-start)\n[![npm](https://img.shields.io/npm/dt/node-red-embedded-start.svg)](https://www.npmjs.com/package/node-red-embedded-start)\n[![david-dm](https://david-dm.org/hlapp/node-red-embedded-start.svg)](https://david-dm.org/hlapp/node-red-embedded-start)\n[![david-dm](https://david-dm.org/hlapp/node-red-embedded-start/dev-status.svg)](https://david-dm.org/hlapp/node-red-embedded-start?type=dev)\n\n# node-red-embedded-start\n\nWhen running Node-RED as an embedded application, the method to start\nNode-RED (`RED.start()`) returns (more precisely, the promise it returns\nresolves) before the runtime API is ready to be interacted with, resulting\nin obscure internal Node-RED errors (typically, `TypeError` being thrown)\nif one calls certain runtime or admin API method too early. This module\nallows waiting deterministically for the runtime API for flows to be ready.\n\n## Use cases\n\nIf your primary need is to interact programmatically with the flows API of\nan [embedded Node-RED] instance once it starts up, then this module is for\nyou.\n\nOne use case likely faced by many developers (such as myself)\nof nodes for Node-RED is running automatic testing for their node(s). Ideally\nsuch testing can include testing discovery, loading, and running of nodes\nby a live embedded Node-RED instance. Usually, one of the first steps in\ngetting a node to \"run\" (i.e., Node-RED invoking the constructor for the\nnode type) is to add it to a flow, whether one that exists or one that you\ncreate from scratch programmatically.\n\nAnother use case would be to programmatically control which nodes are available\nin the palette, by disabling after startup those that are undesired or unneeded.\nSee [node-red/node-red#1221](https://github.com/node-red/node-red/issues/1221).\n\n## Installation\n\n```\n$ npm install --save node-red-embedded-start\n```\n\nIf your use case is automatic testing of a Node-RED node, then you will only\nneed this for development and not in production, and hence use `--save-dev`\nthen instead of `--save`:\n\n```\n$ npm install --save-dev node-red-embedded-start\n```\n\nNote that the module does not itself have a dependency declared on `node-red`\n(except for development, because it is used for testing itself). To use it,\nyou will have to have either a Node-RED instance (usually called `RED`), or\nthe `node-red` module you use for your embedded application needs to be\ninstalled in a place where this module can find it (meaning either global,\nor in the same or a higher `node_modules/` directory).\n\n## Usage\n\nThere are two general ways to use this module. One is to insert it into the\npromise chain from RED.start():\n\n```js\nvar embeddedStart = require('node-red-embedded-start');\nRED.start().then(embeddedStart(RED)).then((result) =\u003e {\n    // result is whatever RED.start() resolved to\n    // RED.node.getFlows() etc are now ready to use\n}).catch((err) =\u003e {\n    if (/^timed out/.test(err.message)) {\n        // embeddedStart() timed out\n        // the value that RED.start() resolved to is available as err.result\n    }\n});\n```\n\nThe other way is to inject it into `RED.start()`:\n\n```js\nvar RED = require('node-red');\nvar embeddedStart = require('node-red-embedded-start');\nembeddedStart.inject(RED);\n\n// then use RED.start() just as you would normally\nRED.start().then((result) =\u003e {\n    // RED.node.getFlow() etc are now ready to use\n}).catch((err) =\u003e {\n    // same as above example\n});\n```\n\nIn either case, the promise returned by the function settles in one of the\nfollowing three ways (the \"result value\" is optional and should normally be\nthe value to which `RED.start()` resolves):\n1. If the flows API is ready already, the promise resolves immediately with\n   the result value passed in.\n2. When the `nodes-started` event fires, the promise resolves with the result\n   value passed in.\n3. If the timeout is reached before the `nodes-started` event fires, the\n   promise rejects with an error. If a result value different from `undefined`\n   is passed in, it is passed through as the `result` property of the error.\n\n### Usage \u0026 API details\n\n#### Generate a wait function: `embeddedStart([RED[, timeout]])`\n\n```js\nvar waitFunc = embeddedStart(RED, timeout);\n\nRED.start().then(waitFunc).then(() =\u003e {\n    // do whatever\n});\n```\n\nUsually, because the wait function will only be used once, one will write\nthe call directly into the promise chain:\n```js\nRED.start().then(embeddedStart(RED, 5000)).then((result) =\u003e {\n    // do whatever\n});\n```\n\n* `RED` is the embedded Node-RED instance.\n* `timeout` is the time in milliseconds after which waiting for the\n  `nodes-started` event should time out. Optional, default is 30 seconds.\n* If both parameters are omitted, `RED` will be loaded from the `node-red`\n  module, and hence if that fails, the call will fail.\n\nThe returned function takes one optional parameter, the value that the\nreturned promise should resolve to. Normally, this should be the value to\nwhich `RED.start()` resolves, so that it is passed through.\n\n#### Call a wait function: `embeddedStart([RED, timeout,] result)`\n\n```js\nRED.start().then((result) =\u003e embeddedStart(RED, undefined, result)).then(() =\u003e {\n    // do whatever\n});\n```\n\n* `RED` is the embedded Node-RED instance.\n* `timeout` is the time in milliseconds after which waiting for the\n  `nodes-started` event should time out. Defaults to 30 seconds if undefined.\n* If only the `result` parameter is provided, `RED` will be loaded from the\n  `node-red` module, and hence if that fails, the call will fail.\n\n#### Inject a wait function: `embeddedStart.inject([RED [, timeout]])`\n\n```js\nembeddedStart.inject(RED, timeout);\n\nRED.start().then((result) =\u003e {\n    // do whatever\n});\n```\n\n* `RED` is the embedded Node-RED instance.\n* `timeout` is the time in milliseconds after which waiting for the\n  `nodes-started` event should time out. Optional, default is 30 seconds.\n* If both parameters are omitted, `RED` will be loaded from the `node-red`\n  module, and hence if that fails, the call will fail.\n* The injected function will pass through any arguments passed to\n  `RED.start()`, and also value to which the original method resolves to. \n\n## Motivation\n\nCalling the Node-RED runtime flow API methods such as `RED.nodes.addFlow()`\nafter `RED.start()` resolves results in a `TypeError`. `RED.nodes.getFlows()`\nreturns `null`, not a valid data structure. One solution could be to wait for\nsome time and hope for the best. Although a couple of seconds will often\nsuffice, the time needed will depend on processor speed, storage speed,\nnumber of flows, number of nodes in those flows, and various other factors.\nA wait-and-hope-for-the-best solution seems therefore unsatisfactory, and\nnevertheless also requires code to implement.\n\nThis issue has been brought to the attention of the Node-RED developers\n(see Node-RED issues [#1168] and [#902]), but they responded that the behaviour\nis intentional and will thus not be fixed until the behavior of an embedded\napplication is fully defined and implemented.\n\nThis module allows waiting instead for a certain event (`nodes-started`) to\nfire. The event fires at a point in time during the Node-RED startup when\nthe code driving the flow API is initialized and thus ready for interaction.\n\n## Caveats\n\n* If you run Node-RED standalone rather than embedded, the problem does not\n  apply in practice, and hence this module will have no benefits for you.\n* The method for waiting implemented here will only work while the\n  `nodes-started` event fires and at the right timing. Presumably, the tests\n  should indicate when that's no longer the case.\n* Due to the highly asynchronous nature of the Node-RED startup process,\n  the flow API being ready does _not_ (and _should_ not) mean that any other\n  components have fully completed their initialization as well. For example,\n  the constructors of nodes may themselves have launched initialization tasks\n  asynchronously, and there is no way of knowing in which stage\n  these are. See Node-RED issue [#698] for discussion.\n\n## License\n\nAvailable under the [MIT License](LICENSE).\n\n[#1168]: https://github.com/node-red/node-red/issues/1168\n[#902]: https://github.com/node-red/node-red/issues/902\n[#698]: https://github.com/node-red/node-red/issues/698\n[embedded Node-RED]: http://nodered.org/docs/embedding\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhlapp%2Fnode-red-embedded-start","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhlapp%2Fnode-red-embedded-start","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhlapp%2Fnode-red-embedded-start/lists"}