{"id":26679640,"url":"https://github.com/zpxp/channel-event","last_synced_at":"2025-04-12T11:33:20.470Z","repository":{"id":35046195,"uuid":"200339799","full_name":"zpxp/channel-event","owner":"zpxp","description":"A javascript event channel library that allows cross component communication and has the ability to scope event broadcasts. An event channel can also run stateless generator functions to create async data flows, that can be reused for different data sets.","archived":false,"fork":false,"pushed_at":"2023-02-04T07:49:09.000Z","size":959,"stargazers_count":6,"open_issues_count":5,"forks_count":0,"subscribers_count":1,"default_branch":"v3","last_synced_at":"2024-10-16T11:02:00.745Z","etag":null,"topics":["async","cross-component-communication","events","pubsub"],"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/zpxp.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":"2019-08-03T06:17:31.000Z","updated_at":"2022-09-12T17:26:40.000Z","dependencies_parsed_at":"2023-02-18T14:45:49.525Z","dependency_job_id":null,"html_url":"https://github.com/zpxp/channel-event","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zpxp%2Fchannel-event","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zpxp%2Fchannel-event/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zpxp%2Fchannel-event/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zpxp%2Fchannel-event/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zpxp","download_url":"https://codeload.github.com/zpxp/channel-event/tar.gz/refs/heads/v3","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248560186,"owners_count":21124608,"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":["async","cross-component-communication","events","pubsub"],"created_at":"2025-03-26T06:17:16.452Z","updated_at":"2025-04-12T11:33:20.444Z","avatar_url":"https://github.com/zpxp.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# channel-event\n\nA simple and highly extensible javascript event channel and control flow library. It can run generator functions, which allows async data flows, simplifies cross component communication and hoists control\nflows to the parent scope. This allows creating highly modular and decoupled architectures.\n\nEvents are scoped to hub instances which means that the scale of the event channels can be controlled and event collisions are non existent. This makes for easy debugging.\n\n![Bundlephobia gzip + minified](https://badgen.net/bundlephobia/minzip/channel-event)\n\nTypescript typings are built in, however it will still work in vanilla javascript \n\n### Installation\n\n`yarn add channel-event`\n\n`npn install channel-event`\n\n\n### Use \n\n- [Fiddle](https://jsfiddle.net/dgf29k1n/)\n\nFirst, create a hub\n\n``` tsx\nimport { createHub } from \"channel-event\";\n\nconst hub = createHub({ enableLogging: process.env.NODE_ENV === \"development\" });\n```\n\nThen, create a channel. All channels created will be able to communicate with all other channels created by the same hub. Channels created by different hubs will not be able to communicate.\nIf a channel is created with an id, that channel may return values from `listen` and those values will be returned to the sender channel in a dictionary `{ [channelId]: value }`. Multiple channels can return values to a single sender, as long as their ids differ.\n\n``` tsx\nconst channel = hub.newChannel();\nconst channel2 = hub.newChannel(\"channel id\");\n\nchannel2.listen(\"test\", data =\u003e {\n   console.log(data); // -\u003e { type: \"test\", payload: 10 }\n   // since this channel was created with an id passed to newChannel\n   // we can return a value here\n   return true;\n});\n\nconst result = channel.send(\"test\", 10);\nconsole.log(result); // -\u003e { \"channel id\": true }\n```\n\nGenerator functions allow for reusable async logic. See [Generator actions](#generator-actions) for more generator actions.\n\n``` tsx\nfunction* test(): IterableIterator\u003cEventIterable\u003e {\n   for (let count = 0; count \u003c 10; count++) {\n      // `put` is the equivelent of channel.send, called on the current channel\n      yield put(\"test\", count);\n      // sleep for 200 ms\n      yield delay(200);\n   }\n}\n\nfunction* check(): IterableIterator\u003cEventIterable\u003e {\n   for (let index = 0; index \u003c 10; index++) {\n      // `take` blocks until the specified event is \n      // sent within the context of the current hub\n      const count = yield take(\"test\");\n      console.log(count);\n   }\n   console.log(\"done!\");\n}\n\n// regular listen will pick up events sent from `put`\nchannel.listen(\"test\", data =\u003e {\n   console.log(data); // -\u003e { type: \"test\", payload: \u003cvalue of count\u003e }\n});\n\n// this will print out the numbers 0-9 with 200 ms delays between prints\nchannel.generator\n   .addGenerator(check)\n   .addGenerator(test)\n   .restartOnAsyncError()\n   .run();\n```\n\nCall `dispose` when finished using\n\n``` tsx \nchannel.dispose();\n// or\nhub.dispose(); // calls dispose on every channel inside the hub\n```\n\nPromises can also be yielded and the function will block until their completion.\n\n```tsx\nconst promResult = yield promiseObject;\n```\n\n### Event middleware\n\nMiddleware can be added to the event chain to change the fundamental behaviour of events. Event middleware is added to the hub with `addEventMiddleware`.\nSee https://github.com/zpxp/channel-store for an example of event middleware.\n\n``` tsx\nhub.addEventMiddleware((context, next) =\u003e {\n   // log all events\n   console.log(context.type);\n   return next(context);\n})\n\nhub.addEventMiddleware((context, next) =\u003e {\n   next(context);\n   // override the return value from listens\n   // channel.send(...) will now always return 42\n   return 42;\n})\n```\n\n### Generator middleware\n\nNew generator actions can be defined by calling `IHub.addGlobalGeneratorMiddleware` or `static IHub.addGlobalGeneratorMiddleware`. The static function will add the new middleware to all future instances of `IHub`, while the instance function will only add the middleware to that hub instance. Whenever a generator `yield`s an `EventIterable`, the hub will look for\nany middleware whos function name matches the `EventIterable.function`.\n\nGenerator middleware takes 2 arguments, the first contains all the arguments that the yielded function was called with, the second is the `IChannel` instance.\nMiddleware must return a `Promise`, that when resolved, will return the resolved data from the `yield` statement.\n\n``` tsx\nexport function pow(power: number): EventIterable {\n   return {\n      function: \"power\",\n      value: { power: power }\n   };\n}\n\n\nhub.addGeneratorMiddleware(\"power\", function(data: EventIterable\u003c{ power: number }\u003e, channel: IChannel): Promise\u003cany\u003e {\n   return Promise.resolve(Math.pow(42, data.value.power));\n});\n\n// the action can now be used like this\nchannel.runGenerator(function*(): IterableIterator\u003cEventIterable\u003e {\n   const num = yield pow(2);\n   console.log(num); // -\u003e 1764\n});\n```\n\nNOTE: If the promised returned from the generator middleware implementation is pending, it is a good idea to add the `reject` function to the `channel.onDispose` function to prevent hanging promises\n\n``` tsx\nhub.addGeneratorMiddleware(\"take\", function(data: EventIterable\u003cstring | string[]\u003e, channel: IChannel): Promise\u003cany\u003e {\n   return new Promise((resolve, reject) =\u003e {\n      const unsub = channel.listen(data.value, result =\u003e {\n         unsub();\n         resolve(result);\n      });\n      // call reject when channel is disposed\n      channel.onDispose(reject);\n   });\n});\n```\n\nYou may also create a new channel that exists for the scope of the generator action to make disposing all listeners easy, such as in this race action implentation\n\n``` ts\nhub.addGeneratorMiddleware(\"race\", function (data: EventIterable\u003cArray\u003cEventIterable | Promise\u003cany\u003e\u003e\u003e, channel): Promise\u003cany\u003e {\n   // create an isolated channel that will be disposed on completion\n   // all listeners that did not win the race need to be rejected\n   // and cleaned up to prevent mem leaks\n   const chan = channel.hub.newChannel();\n\n   return Promise.race(\n      data.value.map(item =\u003e {\n         if (item instanceof Promise) {\n            return item;\n         } else {\n            // is iterator. yield it and see what happens\n            return new Promise((resolve, reject) =\u003e {\n               chan.runGenerator(function*() {\n                  return yield item;\n               }, resolve);\n               chan.onDispose(reject);\n            });\n         }\n      })\n   ).finally(() =\u003e {\n      // when one action wins the race, dispose the channel \n      // cleaning up all pending listeners/generators\n      chan.dispose();\n   });\n});\n```\n\n### Generator actions\n\nCurrent available actions implemented in all hub instances\n\n``` ts\n/**\n * waits until the dispatch of a specified type then returns the data\n * @param type the string type or types to wait on\n */\nexport declare function take(type: string | string[]): EventIterable;\n\n/**\n * Calls `channel.send` on the current hub\n * @param type action type\n * @param data optional data to send to all listeners\n */\nexport declare function put(type: string, data?: any): EventIterable;\n\n/**\n * Calls an async func (promise) or generator func and waits until its completion, returning\n * the result\n * @param func async func (promise) or generator function\n * @param args\n */\nexport declare function call\u003cA extends any[]\u003e(func: (...args: A) =\u003e any, ...args: A): EventIterable;\n\n/**\n * Same as `call` except does not block until the function returns. Returns a cancel function that will cancel the forked task\n * @param func plain function, async func (promise) or generator function\n * @param args\n */\nexport declare function fork\u003cA extends any[]\u003e(func: (...args: A) =\u003e any, ...args: A): EventIterable;\n\n/**\n * Blocks for the given ms duration\n * @param durationMs\n */\nexport declare function delay(durationMs: number): EventIterable;\n\n/**\n * Call `func` whenever `type` is dispatched. Cancels any existing instances `func` that may be running\n * @param type Call func whenever this type is dispatched\n * @param func\n */\nexport declare function takeLatest(type: string | string[], func: (data?: any) =\u003e IterableIterator\u003cEventIterable\u003e): EventIterable;\n\n/**\n * Call `func` whenever `type` is dispatched.\n * @param type Call func whenever this type is dispatched\n * @param func\n */\nexport declare function takeEvery(type: string | string[], func: (data?: any) =\u003e IterableIterator\u003cEventIterable\u003e): EventIterable;\n\n/**\n * Call `func` whenever `type` is dispatched if no instances of `func` are running\n * @param type Call func whenever this type is dispatched\n * @param func\n */\nexport declare function takeLast(type: string | string[], func: (data?: any) =\u003e IterableIterator\u003cEventIterable\u003e): EventIterable;\n```\n\n### Extensions, middleware\n\nLibrary | Description\n--- | ---\n [channel-store](https://github.com/zpxp/channel-store) | An event middleware that creates an ambient state for channel hubs that can be accessed anywhere that a channel exists\n [react-channel-event](https://github.com/zpxp/react-channel-event) | A react provider and HoC wrapper for `channel-event`\n [react-channel-store](https://github.com/zpxp/react-channel-store) | A react provider and HoC wrapper for `channel-store`\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzpxp%2Fchannel-event","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzpxp%2Fchannel-event","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzpxp%2Fchannel-event/lists"}