{"id":26643687,"url":"https://github.com/kosich/rxjs-proxify","last_synced_at":"2025-04-10T23:51:20.288Z","repository":{"id":55641084,"uuid":"296418877","full_name":"kosich/rxjs-proxify","owner":"kosich","description":"Turns a Stream of Objects into an Object of Streams","archived":false,"fork":false,"pushed_at":"2021-06-13T08:48:19.000Z","size":1515,"stargazers_count":36,"open_issues_count":7,"forks_count":3,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-24T20:38:44.311Z","etag":null,"topics":["observable","proxy","reactive-programming","rxjs","rxjs-observables"],"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/kosich.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":"2020-09-17T19:08:35.000Z","updated_at":"2025-02-23T09:22:39.000Z","dependencies_parsed_at":"2022-08-15T05:20:13.733Z","dependency_job_id":null,"html_url":"https://github.com/kosich/rxjs-proxify","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kosich%2Frxjs-proxify","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kosich%2Frxjs-proxify/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kosich%2Frxjs-proxify/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kosich%2Frxjs-proxify/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kosich","download_url":"https://codeload.github.com/kosich/rxjs-proxify/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248317707,"owners_count":21083528,"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":["observable","proxy","reactive-programming","rxjs","rxjs-observables"],"created_at":"2025-03-24T20:34:40.146Z","updated_at":"2025-04-10T23:51:20.273Z","avatar_url":"https://github.com/kosich.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003ch1\u003e\n    \u003cbr/\u003e\n    { 👓 }\n    \u003cbr/\u003e\n    \u003csub\u003e\u003csub\u003eTurn a Stream of Objects into an Object of Streams\u003c/sub\u003e\u003c/sub\u003e\n    \u003cbr/\u003e\n    \u003cbr/\u003e\n    \u003ca href=\"https://www.npmjs.com/package/rxjs-proxify\"\u003e\u003cimg src=\"https://img.shields.io/npm/v/rxjs-proxify\" alt=\"NPM\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://bundlephobia.com/result?p=rxjs-proxify@latest\"\u003e\u003cimg src=\"https://img.shields.io/bundlephobia/minzip/rxjs-proxify?label=gzipped\" alt=\"Bundlephobia\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://opensource.org/licenses/MIT\" rel=\"nofollow\"\u003e\u003cimg src=\"https://img.shields.io/npm/l/rxjs-proxify\" alt=\"MIT license\"\u003e\u003c/a\u003e\n    \u003cbr/\u003e\n    \u003cbr/\u003e\n    \u003cbr/\u003e\n  \u003c/h1\u003e\n\u003c/div\u003e\n\nAccess values inside RxJS Observables as if they were directly available on the stream!\n\n```ts\nstream.pipe(pluck('msg')).subscribe(…);\n// turn ↑ into ↓\nstream.msg.subscribe(…);\n```\n\nWith good TypeScript support! 😲 Roughly speaking:\n\n```ts\nproxify( Observable\u003c{ msg: string }\u003e ) ≈ Observable\u003c{ msg: string }\u003e \u0026 { msg: Observable\u003cstring\u003e }\n```\n\n**But recursively.** So `stream.msg` is a Proxy itself, allowing you to `stream.msg.length.subscribe(…)`!\n\nProxify lets you **access Observable API** as well as **pluck props** and **call methods** at any depth of an Observable, Subject, or BehaviorSubject! See the [API](#-api) and [Examples](#-examples) sections to learn more.\n\n\n## 📦 Install\n\n```\nnpm i rxjs-proxify\n```\n\nor [try it online](https://stackblitz.com/edit/rxjs-proxify-repl?file=index.ts)!\n\n## 🛠 API\n\nThere are two methods available to you: [`proxify`](#proxify) and [`statify`](#statify)\n\n## Proxify\n\n`proxify(stream)` will wrap your Observable, Subject or BehaviorSubject in a Proxy:\n\n**Observable Proxy**  \nsubscribe at any depth\n\n```ts\nconst observable = proxify( of({ p: '🐑' }) );\nobservable.subscribe(console.log); // \u003e { p: 🐑 }\nobservable.p.subscribe(console.log); // \u003e 🐑\n```\n\n**Subject Proxy**  \nsubscribe at any depth, push at the root\n\n```ts\nconst subject = proxify(new Subject\u003c{ p: string }\u003e());\nsubject.subscribe(console.log);\nsubject.p.subscribe(console.log);\nsubject.next({ p: '🐥' }); // \u003e { p: 🐥 } // \u003e 🐥\n```\n\n**BehaviorSubject Proxy**  \nsubscribe at any depth, push at any depth, synchronously read the current state\n\n```ts\nconst behavior = proxify(new BehaviorSubject({ p: '🐖' }));\nbehavior.p.subscribe(console.log); // \u003e 🐖\nbehavior.p.next('🐇'); // \u003e 🐇\nconsole.log(behavior.p.value) // \u003e 🐇\n```\n\n### Statify\n\n`statify(value)` will put the value in a BehaviorSubject Proxy and add a `distinctUntilChanged` operator on each property access.\n\n**State Proxy**  \nsubscribe to distinct updates at any depth, push at any depth, synchronously read the current state\n\n```ts\n// create a state\nconst state = statify({ a: '🐰', z: '🏡' });\n\n// listen to \u0026 log root state changes\nstate.subscribe(console.log); //\u003e { a:🐰 z:🏡 }\n\n// update particular substate\nstate.a.next('🐇'); //\u003e { a:🐇 z:🏡 }\n\n// read current values\nconsole.log(state.z.value + state.a.value); //\u003e 🏡🐇\n\n// update root state, still logging\nstate.next({ a: '🐇', z: '☁️' }) //\u003e { a:🐇 z:☁️ }\n\n// and then…\nstate.z.next('🌙');   //\u003e { a:🐇  z:🌙 }\nstate.a.next('🐇👀'); //\u003e { a:🐇👀 z:🌙 }\nstate.z.next('🛸')    //\u003e { a:🐇👀 z:🛸 }\nstate.a.next('💨');   //\u003e { a:💨  z:🛸 }\n```\n\nSee Examples section for more details.\n\n## 📖 Examples\n\n### Basic\n\n```ts\nimport { proxify } from \"rxjs-proxify\";\nimport { of } from \"rxjs\";\n\nconst o = of({ msg: 'Hello' }, { msg: 'World' });\nconst p = proxify(o);\np.msg.subscribe(console.log);\n\n// equivalent to\n// o.pipe(pluck('msg')).subscribe(console.log);\n```\n\n### With JS destructuring\n\nConvenient stream props splitting\n\n```ts\nimport { proxify } from \"rxjs-proxify\";\nimport { of } from \"rxjs\";\n\nconst o = of({ msg: 'Hello', status: 'ok'  }, { msg: 'World', status: 'ok' });\nconst { msg, status } = proxify(o);\nmsg.subscribe(console.log);\nstatus.subscribe(console.log);\n\n// equivalent to\n// const msg = o.pipe(pluck('msg'));\n// const status = o.pipe(pluck('status'));\n// msg.subscribe(console.log);\n// status.subscribe(console.log);\n```\n\n**⚠️ WARNING:** as shown in \"equivalent\" comment, this operation creates several Observables from the source Observable. Which means that if your source is _cold_ — then you might get undesired subscriptions. This is a well-known nuance of working with Observables. To avoid this, you can use a multicasting operator on source before applying `proxify`, e.g. with [`shareReplay`](https://rxjs.dev/api/operators/shareReplay):\n\n```ts\nconst { msg, status } = proxify(o.pipe(shareReplay(1)));\n```\n\n### With pipe\n\nConcatenate all messages using `pipe` with `scan` operator:\n\n```ts\nimport { proxify } from \"rxjs-proxify\";\nimport { of } from \"rxjs\";\nimport { scan } from \"rxjs/operators\";\n\nconst o = of({ msg: 'Hello' }, { msg: 'World' });\nconst p = proxify(o);\np.msg.pipe(scan((a,c)=\u003e a + c)).subscribe(console.log);\n\n// equivalent to\n// o.pipe(pluck('msg'), scan((a,c)=\u003e a + c)).subscribe(console.log);\n```\n\n### Calling methods\n\nPick a method and call it:\n\n```ts\nimport { proxify } from \"rxjs-proxify\";\nimport { of } from \"rxjs\";\n\nconst o = of({ msg: () =\u003e 'Hello' }, { msg: () =\u003e 'World' });\nconst p = proxify(o);\np.msg().subscribe(console.log);\n\n// equivalent to\n// o.pipe(map(x =\u003e x?.map())).subscribe(console.log);\n```\n\n### Accessing array values\n\nProxify is recursive, so you can keep chaining props or indices\n\n```ts\nimport { proxify } from \"rxjs-proxify\";\nimport { of } from \"rxjs\";\n\nconst o = of({ msg: () =\u003e ['Hello'] }, { msg: () =\u003e ['World'] });\nconst p = proxify(o);\np.msg()[0].subscribe(console.log);\n\n// equivalent to\n// o.pipe(map(x =\u003e x?.map()), pluck(0)).subscribe(console.log);\n```\n\n## 🤝 Want to contribute to this project?\n\nThat will be awesome!\n\nPlease create an issue before submiting a PR — we'll be able to discuss it first!\n\nThanks!\n\n## Enjoy 🙂\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkosich%2Frxjs-proxify","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkosich%2Frxjs-proxify","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkosich%2Frxjs-proxify/lists"}