{"id":13809000,"url":"https://github.com/chrisguttandin/subscribable-things","last_synced_at":"2025-05-07T00:02:24.240Z","repository":{"id":43716973,"uuid":"243833305","full_name":"chrisguttandin/subscribable-things","owner":"chrisguttandin","description":"A collection of reactive wrappers for various browser APIs.","archived":false,"fork":false,"pushed_at":"2025-04-11T01:16:05.000Z","size":14290,"stargazers_count":44,"open_issues_count":1,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-11T02:29:46.916Z","etag":null,"topics":["browser","observables","reactive","rxjs"],"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/chrisguttandin.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}},"created_at":"2020-02-28T18:55:04.000Z","updated_at":"2025-04-11T01:16:09.000Z","dependencies_parsed_at":"2024-01-17T02:08:16.418Z","dependency_job_id":"c93c688d-1aa5-43e3-acd3-c92925aef9b1","html_url":"https://github.com/chrisguttandin/subscribable-things","commit_stats":{"total_commits":1380,"total_committers":2,"mean_commits":690.0,"dds":0.0007246376811593791,"last_synced_commit":"891431d674d1edc03f035b955177af1cbb73f1c8"},"previous_names":[],"tags_count":105,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisguttandin%2Fsubscribable-things","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisguttandin%2Fsubscribable-things/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisguttandin%2Fsubscribable-things/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisguttandin%2Fsubscribable-things/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chrisguttandin","download_url":"https://codeload.github.com/chrisguttandin/subscribable-things/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252788514,"owners_count":21804284,"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":["browser","observables","reactive","rxjs"],"created_at":"2024-08-04T01:01:57.402Z","updated_at":"2025-05-07T00:02:24.165Z","avatar_url":"https://github.com/chrisguttandin.png","language":"JavaScript","funding_links":[],"categories":["Underlying Technologies"],"sub_categories":["RxJS"],"readme":"![logo](https://repository-images.githubusercontent.com/243833305/16fbe600-64ca-11ea-8f60-736c8d74ec0f)\n\n# subscribable-things\n\n**A collection of reactive wrappers for various browser APIs.**\n\n[![version](https://img.shields.io/npm/v/subscribable-things.svg?style=flat-square)](https://www.npmjs.com/package/subscribable-things)\n\nThis package provides factory functions which can be used to turn browser APIs into subscribable things. A subscribable thing can either be consumed directly with callback functions or by utilzing one of the popular libraries for reactive programming.\n\n## Usage\n\nThe `subscribable-things` package is published on [npm](https://www.npmjs.com/package/subscribable-things) and can be installed as usual.\n\n```shell\nnpm install subscribable-things\n```\n\nIt exports individual functions for each wrapped browser API which are described in greater detail below. They can either be used directly by providing a callback function ...\n\n```js\nimport { mediaQueryMatch } from 'subscribable-things';\n\nconst subscribe = mediaQueryMatch('(max-width:600px)');\n\nconst unsubscribe = subscribe((isMatching) =\u003e console.log(isMatching));\n\nunsubscribe();\n```\n\n... or by utilizing a library for reactive programming like [RxJS](https://rxjs-dev.firebaseapp.com) ...\n\n```js\nimport { from } from 'rxjs';\nimport { mediaQueryMatch } from 'subscribable-things';\n\nconst mediaQueryMatch$ = from(mediaQueryMatch('(max-width:600px)'));\n\nconst subscription = mediaQueryMatch$.subscribe((isMatching) =\u003e console.log(isMatching));\n\nsubscription.unsubscribe();\n```\n\n... or [Callbags](https://github.com/callbag/callbag) ...\n\n```js\nimport fromObs from 'callbag-from-obs';\nimport observe from 'callbag-observe';\nimport { mediaQueryMatch } from 'subscribable-things';\n\nconst source = fromObs(mediaQueryMatch('(max-width:600px)'));\n\nobserve((isMatching) =\u003e console.log(isMatching))(source);\n```\n\n... or [XStream](https://staltz.github.io/xstream) ...\n\n```js\nimport { mediaQueryMatch } from 'subscribable-things';\nimport { fromObservable } from 'xstream';\n\nconst stream = fromObservable(mediaQueryMatch('(max-width:600px)'));\n\nconst unsubscribe = stream.subscribe((isMatching) =\u003e console.log(isMatching));\n\nunsubscribe();\n```\n\n... or [Bacon.js](https://baconjs.github.io) ...\n\n```js\nimport { fromESObservable } from 'baconjs';\nimport { mediaQueryMatch } from 'subscribable-things';\n\nconst eventStream = fromESObservable(mediaQueryMatch('(max-width:600px)'));\n\nconst unsubscribe = eventStream.onValue((isMatching) =\u003e console.log(isMatching));\n\nunsubscribe();\n```\n\n... or [Kefir.js](https://kefirjs.github.io/kefir).\n\n```js\nimport { fromESObservable } from 'kefir';\nimport { mediaQueryMatch } from 'subscribable-things';\n\nconst stream = fromESObservable(mediaQueryMatch('(max-width:600px)'));\n\nconst subscription = stream.observe({\n    value(isMatching) {\n        console.log(isMatching);\n    }\n});\n\nsubscription.unsubscribe();\n```\n\nIt is even possible to consume `subscribable-things` as an async iterable by taking the little detour over RxJS and [rxjs-for-await](https://github.com/benlesh/rxjs-for-await).\n\n```js\nimport { eachValueFrom } from 'rxjs-for-await';\nimport { from } from 'rxjs';\nimport { mediaQueryMatch } from 'subscribable-things';\n\nconst source$ = from(mediaQueryMatch('(max-width:600px)'));\n\nfor await (const isMatching of eachValueFrom(source$)) {\n    console.log(isMatching);\n}\n```\n\nAlso it's possible to output values directly to HTML via [hyperf](https://github.com/spectjs/hyperf).\n\n```js\nimport h from 'hyperf';\nimport { mediaQueryMatch } from 'subscribable-things';\n\nconst element = h`\u003cdiv\u003eis matching: ${mediaQueryMatch('(max-width:600px)')}\u003c/div\u003e`;\n\ndocument.body.appendChild(element);\n```\n\n### animationFrame()\n\n```ts\nfunction animationFrame(): SubscribableThing\u003cnumber\u003e;\n```\n\nThis function wraps the [`requestAnimationFrame()`](https://html.spec.whatwg.org/multipage/imagebitmap-and-animations.html#dom-animationframeprovider-requestanimationframe) method. It emits the current timestamp of each animation frame.\n\n### attribute(htmlElement, name)\n\n```ts\nfunction attribute(htmlElement: HTMLElement, name: string): TSubscribableThing\u003cnull | string\u003e;\n```\n\nThis function uses `mutations()` on the inside to emit the latest value of the attribute with the given name.\n\n### geolocation([options])\n\n```ts\nfunction geolocation(options?: PositionOptions): SubscribableThing\u003cGeolocationPosition\u003e;\n```\n\nThis is a wrapper for the [Geolocation API](https://w3c.github.io/geolocation-api/). It uses [`watchPosition()`](https://w3c.github.io/geolocation-api/#watchposition-method) to gather the most recent [`GeolocationPosition`](https://w3c.github.io/geolocation-api/#dfn-a-new-geolocationposition) whenever it changes.\n\n### intersections(htmlElement, [options])\n\n```ts\nfunction intersections(\n    htmlElement: HTMLElement,\n    options?: IntersectionObserverInit\n): SubscribableThing\u003cIntersectionObserverEntry[]\u003e;\n```\n\nThis function is a wrapper for the [`IntersectionObserver`](https://developer.mozilla.org/docs/Web/API/IntersectionObserver).\n\n### mediaDevices()\n\n```ts\nfunction mediaDevices(): SubscribableThing\u003cMediaDeviceInfo[]\u003e;\n```\n\nThis function is a wrapper for the [`enumerateDevices()`](https://developer.mozilla.org/docs/Web/API/MediaDevices/enumerateDevices) method of the [Media Capture and Streams specification](https://w3c.github.io/mediacapture-main). It will also listen for the [`devicechange` event](https://developer.mozilla.org/docs/Web/API/MediaDevices/devicechange_event) to emit a fresh list of devices whenever they change.\n\n### mediaQueryMatch(mediaQueryString)\n\n```ts\nfunction mediaQueryMatch(mediaQueryString: string): SubscribableThing\u003cboolean\u003e;\n```\n\nThis function is a wrapper for the [`matchMedia()`](https://developer.mozilla.org/docs/Web/API/Window/matchMedia) method. It will emit a new value whenever the result of `matchMedia()` changes.\n\n### midiInputs(midiAccess)\n\n```ts\nfunction midiInputs(midiAccess: IMidiAccess): SubscribableThing\u003cIMidiInput[]\u003e;\n```\n\nThis function returns the currently available MIDI input devices. It accepts a [`MIDIAccess`](https://developer.mozilla.org/docs/Web/API/MIDIAccess) object of the [Web MIDI API](https://webaudio.github.io/web-midi-api).\n\n### midiOutputs(midiAccess)\n\n```ts\nfunction midiOutputs(midiAccess: IMidiAccess): SubscribableThing\u003cIMidiOutput[]\u003e;\n```\n\nThis function returns the currently available MIDI output devices. It accepts a [`MIDIAccess`](https://developer.mozilla.org/docs/Web/API/MIDIAccess) object of the [Web MIDI API](https://webaudio.github.io/web-midi-api).\n\n### metrics(options)\n\n```ts\nfunction metrics(options: PerformanceObserverInit): SubscribableThing\u003cPerformanceEntry[]\u003e;\n```\n\nThis function is a wrapper for the [`PerformanceObserver`](https://developer.mozilla.org/docs/Web/API/PerformanceObserver) as defined by the [Performance Timeline Level 2 specification](https://w3c.github.io/performance-timeline).\n\n### mutations(htmlElement, options)\n\n```ts\nfunction mutations(\n    htmlElement: HTMLElement,\n    options: MutationObserverInit\n): SubscribableThing\u003cMutationRecord[]\u003e;\n```\n\nThis function is a wrapper for the [`MutationObserver`](https://developer.mozilla.org/docs/Web/API/MutationObserver).\n\n### on(target, type, [options])\n\n```ts\nfunction on(\n    target: EventTarget,\n    type: string,\n    options?: boolean | AddEventListenerOptions\n): SubscribableThing\u003cEvent\u003e;\n```\n\nThis function can be used to subscribe to events of a certain type dispatched from an [`EventTarget`](https://dom.spec.whatwg.org/#interface-eventtarget).\n\n### online()\n\n```ts\nfunction online(): SubscribableThing\u003cboolean\u003e;\n```\n\nThis function wraps the [`onLine`](https://developer.mozilla.org/docs/Web/API/NavigatorOnLine/onLine) property of the [`Navigator`](https://developer.mozilla.org/docs/Web/API/Navigator) and listens for the corresponding [`'online'`](https://developer.mozilla.org/docs/Web/API/Window/online_event) and [`'offline'`](https://developer.mozilla.org/docs/Web/API/Window/offline_event) events on the [`Window`](https://developer.mozilla.org/docs/Web/API/Window) to emit updates.\n\n### permissionState(permissionDescriptor)\n\n```ts\nfunction permissionState(\n    permissionDescriptor: PermissionDescriptor\n): SubscribableThing\u003cPermissionState\u003e;\n```\n\nThis function is a wrapper for the [`query()`](https://developer.mozilla.org/docs/Web/API/Permissions/query) method of the [Permissions API](https://w3c.github.io/permissions). It will monitor the permission status to emit a new state whenever it gets updated.\n\n### reports([options])\n\n```ts\nfunction reports(options?: IReportingObserverOptions): SubscribableThing\u003cIReport[]\u003e;\n```\n\nThis function is a wrapper for the [`ReportingObserver`](https://developer.mozilla.org/docs/Web/API/ReportingObserver) of the [Reporting API](https://w3c.github.io/reporting).\n\n### resizes(htmlElement, [options])\n\n```ts\nfunction resizes(\n    htmlElement: HTMLElement,\n    options?: IResizesObserverOptions\n): SubscribableThing\u003cIResizeObserverEntry[]\u003e;\n```\n\nThis function is a wrapper for the [`ResizeObserver`](https://developer.mozilla.org/docs/Web/API/ResizeObserver) of the [Resize Observer specification](https://drafts.csswg.org/resize-observer).\n\n### unhandledRejection(coolingOffPeriod)\n\n```ts\nfunction unhandledRejection(coolingOffPeriod: number): SubscribableThing\u003cany\u003e;\n```\n\nThis function emits unhandled rejections. It will listen for the [`unhandledrejection` event](https://developer.mozilla.org/docs/Web/API/Window/unhandledrejection_event) to register possibly unhandled rejections. It will then wait for the cooling-off period to elapse before it emits the reason (aka the error) that caused the unhandled rejection. It is possible that a previously unhandled rejection gets handled later on in which case a [`rejectionhandled` event](https://developer.mozilla.org/docs/Web/API/Window/rejectionhandled_event) will be fired. If that happens during the cooling-off period nothing will be emitted by this function.\n\n### videoFrame(videoElement)\n\n```ts\nfunction videoFrame(\n    videoElement: HTMLVideoElement\n): SubscribableThing\u003c{ now: number } \u0026 IVideoFrameMetadata\u003e;\n```\n\nThis function wraps the [`requestVideoFrameCallback()`](https://wicg.github.io/video-rvfc) method of the given [`HTMLVideoElement`](https://html.spec.whatwg.org/multipage/media.html#htmlvideoelement). It emits the current timestamp combined with the [`VideoFrameMetadata`](https://wicg.github.io/video-rvfc/#video-frame-metadata) object.\n\n### wakeLock(type)\n\n```ts\nfunction wakeLock(type: TWakeLockType): SubscribableThing\u003cboolen\u003e;\n```\n\nThis function simplifies the usage of the [Screen Wake Lock API](https://w3c.github.io/screen-wake-lock). It emits true when a wake lock could be acquired and emits false once the wake lock gets released by the browser. As long as the subscription is alive it will continuosly try to get a new wake lock if the current one gets released.\n\n## Alternatives\n\nThere are two similar packages available which are based directly on RxJS. They are [rx-use](https://github.com/streamich/rx-use) and [rxjs-web](https://github.com/niklas-wortmann/rxjs-web).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchrisguttandin%2Fsubscribable-things","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchrisguttandin%2Fsubscribable-things","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchrisguttandin%2Fsubscribable-things/lists"}