{"id":20688787,"url":"https://github.com/71/ricochet","last_synced_at":"2026-03-03T00:38:52.855Z","repository":{"id":95481166,"uuid":"167081926","full_name":"71/ricochet","owner":"71","description":"Tiny (\u003c 5kB) framework for efficient and direct DOM rendering, using JSX / observables.","archived":false,"fork":false,"pushed_at":"2019-04-19T17:45:24.000Z","size":289,"stargazers_count":6,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-09T22:24:30.889Z","etag":null,"topics":["jsx","reactive","typescript","web","web-framework"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/71.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2019-01-22T23:12:42.000Z","updated_at":"2022-08-24T09:28:14.000Z","dependencies_parsed_at":null,"dependency_job_id":"ff394d02-4ae9-4ca8-8f8d-f54db120f3a8","html_url":"https://github.com/71/ricochet","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/71/ricochet","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/71%2Fricochet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/71%2Fricochet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/71%2Fricochet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/71%2Fricochet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/71","download_url":"https://codeload.github.com/71/ricochet/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/71%2Fricochet/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30027520,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-03T00:31:48.536Z","status":"ssl_error","status_checked_at":"2026-03-03T00:30:56.176Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["jsx","reactive","typescript","web","web-framework"],"created_at":"2024-11-16T23:07:00.174Z","updated_at":"2026-03-03T00:38:52.842Z","avatar_url":"https://github.com/71.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"Ricochet\n========\n\nA small and unopinionated web framework meant to be simple, fast and lightweight.\n\n\n## Overview\n\nRicochet is a small web framework which uses the [JSX](https://reactjs.org/docs/introducing-jsx.html)\nsyntax and [observable sequences](http://reactivex.io/documentation/observable.html) to define layouts.\nIts main ability is to render [`NestedNode`](#type-nestednode)s into the DOM efficiently, where\na [`NestedNode`](#type-nestednode) is defined as follows:\n\n```typescript\n// A nested node can be...\ntype NestedNode =\n  // ... a DOM element,\n  | Element\n  // ... a primitive,\n  | number | string\n  // ... a list of nested nodes,\n  | ReadonlyArray\u003cNestedNode\u003e\n  // ... or an observable sequence of nested nodes.\n  | Observable\u003cNestedNode\u003e\n```\n\nTherefore, Ricochet can render any array, [observable sequence](#interface-observablet)\nor node into the DOM, and make sure the rendered node stays in sync with the\nvalue that was rendered, without rendering the node multiple times.\n\nRicochet does not optimize its operations by default, but [makes it extremely easy to add\nfeatures such as memoization, update batching and efficient list rendering](#performances).\n\n\n## Getting started\n\nRicochet components are only rendered once (when they are instantiated),\nand updated when a reactive stream they depend on changes.\n\nFor instance, we can compare a clock in React and Ricochet.\n\n```tsx\nconst Clock = () =\u003e {\n  // State is introduced explicitely using 'useState'.\n  //\n  // The 'Clock' function will be called several times per second,\n  // but 'date' will be persisted thanks to 'useState'.\n  const [date, setDate] = React.useState(new Date())\n\n  // Effects are introduced using 'useEffect'.\n  React.useEffect(() =\u003e {\n    const interval = setInterval(() =\u003e setDate(new Date()), 1000)\n\n    return () =\u003e {\n      clearInterval(interval)\n    }\n  })\n\n  return (\n    \u003cspan\u003e{date.toString()}\u003c/span\u003e\n  )\n}\n```\n\nNow here it is in Ricochet. Please note that [`subject`](#function-subjecttinitialvalue-extendedsubjectt)\n(defined in [`ricochet/reactive`](./src/reactive.ts)), creates an observable similar to a\n[BehaviorSubject](https://rxjs-dev.firebaseapp.com/guide/subject#behaviorsubject).\n\n```tsx\nconst Clock = () =\u003e {\n  // Since 'Clock' is only called once, there is no need for a\n  // tool such as 'useState'.\n  const date$ = subject(new Date())\n\n  // Once again, there is no need to wrap this logic at all.\n  const interval = setInterval(() =\u003e date$.next(new Date()), 1000)\n\n  // Resources are bound to elements and can be disposed of by\n  // using `element.destroy()`. To give ownership of a subscription\n  // to an element, we can use the 'attach' function.\n  attach({\n    unsubscribe() {\n      clearInterval(interval)\n    }\n  })\n\n  return (\n    // Ricochet has first class support for streams. When 'date'\n    // will be updated, the content of the span will be as well.\n    \u003cspan\u003e{date$}\u003c/span\u003e\n  )\n}\n```\n\n\n## Event handlers and data binding\n\nTwo different ways are provided to subscribe to event handlers on rendered elements.\n\nThe first way is to simply set an element's `on*` property on creation, ie.\n\n```tsx\nconst onClickHandler = () =\u003e {\n  alert('Clicked!')\n}\n\n\u003cbutton onclick={onClickHandler} /\u003e\n```\n\nThe second way, unique to Ricochet, is to create a `Connectable`, which can be\nattached to an element during its creation. This provides an easy way to convert\nan event handler into an observable sequence, while playing nicely with the\ndeclarative JSX syntax.\n\n```tsx\nconst click$ = eventListener('click')\n\nattach(\n  click$.subscribe(() =\u003e {\n    alert('Clicked!')\n  })\n)\n\n\u003cbutton connect={click$} /\u003e\n```\n\nAnother `Connectable` is provided for data binding.\n\n```tsx\nconst name$ = subject('John')\nconst nameBinding$ = valueBinder(name$)\n\n// Any change to 'name$' will update the value of the input, and any\n// change to the input's value will update 'name$'.\n\u003cinput type='text' connect={nameBinding$} /\u003e\n```\n\n\n## Performances\n\nRicochet's goal is to avoid using a VDOM, and instead to create redraws, virtual\nor real, as rarely as possible. Since only the parts of the DOM that depend on a stream\nwill be recomputed when the stream changes, allocations should be less common,\nand changes to the DOM should be as rare as if a diffing algorithm had been used.\n\n**However**, in some cases a simple change to a reactive stream may\nimpact large parts of the DOM, which will lead to a slower redraw. In these\ncases, performances may still be tuned easily in different ways.\n\n\n### Exploiting observables\n\n[`Observable`](#interface-observablet) streams are very powerful abstractions, and\ncan be manipulated to optimize their performances.\n\nFor instance, [RxJS](https://github.com/ReactiveX/rxjs) provides the following features\nfor improving how streams are processed:\n- [Schedulers](https://rxjs-dev.firebaseapp.com/guide/scheduler), which can be used to\n  batch updates together, or to perform them [at the right time](https://rxjs-dev.firebaseapp.com/api/index/const/animationFrameScheduler).\n- Operators such as [`throttle`](https://rxjs-dev.firebaseapp.com/api/operators/throttle) and\n  [`debounce`](https://rxjs-dev.firebaseapp.com/api/operators/debounce).\n\n\n### Customizing the rendering process\n\nAs stated in the [overview](#overview), Ricochet can render many kinds of nodes. However, a supported type\nof node that wasn't mentioned before is the [`CustomNode`](#interface-customnode).\n\nNodes that implement this interface are given full control over how they and their children are rendered,\nmaking operations such as batch processing and node caching extremely easy to implement.\n\nFor instance, here is how you would implement a node that only updates if a condition is respected:\n\n```typescript\nclass PredicateNode implements CustomNode {\n  constructor(\n    readonly node: NestedNode,\n    readonly predicate: (node: NestedNode) =\u003e boolean\n  ) {}\n\n  render(parent: Element, prev: NodeRef, next: NodeRef, r: RenderFunction) {\n    // NodeRef is equivalent to [Node]; it is used to override variables accross function bodies.\n    if (!this.predicate(this.node))\n      return\n\n    r(this.node, prev, next)\n  }\n}\n```\n\n\n### Caching elements\n\nSince all Ricochet elements are regular DOM elements, we may want to cache elements that\nwe know we may have to reuse.\n\n```tsx\nconst App = ({ dialogProps, ...props }) =\u003e {\n  const dialogOpen$ = subject(false)\n  \n  // Keep a reference to the dialog here, so that it is only rendered once\n  const dialog = \u003cDialog { ...dialogProps } /\u003e\n\n  return (\n    \u003cdiv class='app'\u003e\n      \u003cContent { ...props } /\u003e\n\n      { dialogOpen$.map(dialogOpen =\u003e dialogOpen \u0026\u0026 dialog) }\n    \u003c/div\u003e\n  )\n}\n```\n\nThis is unfortunately not that simple. Here, as soon as `dialog` gets rendered for the first time,\nsome resources will be attached to it via `attach`. Then, as soon as `dialogOpen$` becomes `false`,\nall these resources will be disposed of, and `dialog`, while still existing, will be invalid.\n\nTherefore, we must tell Ricochet that `dialog` is owned by `App`, and that it should\nonly be disposed when `App` itself is disposed of.\n\n```tsx\n// This line:\nconst dialog = \u003cDialog { ...dialogProps } /\u003e\n\n// Becomes this line:\nconst dialog = \u003cDialog noimplicitdispose { ...dialogProps } /\u003e\n```\n\nNow, when dialog is removed by its parent element, it will not be disposed automatically.  \nIn order to remove it, `element.destroy(true)` must be called, `true` precising that it\nthe element will be both destroyed (removed from the DOM) and disposed.\n\nA common pattern is to cache an element in a component, and to dispose it when the parent\ncomponent itself is disposed, rather than when the element is hidden. This can be easily\naccomplished by calling `attach` with an element rather than with a subscription.\n\n```tsx\nconst TodoList = ({ todos }: { todos: Todo[] }) =\u003e {\n  const cache: Record\u003cnumber, TodoItem\u003e = {}\n\n  for (const todo of todos) {\n    const todoItem = \u003cTodoItem text={todo.text} done={todo.done} noimplicitdispose /\u003e\n\n    attach(cache[todo.id] = todoItem)\n  }\n\n  const query$: Subject\u003cstring\u003e = subject('')\n\n  return (\n    \u003cdiv\u003e\n      \u003cinput connect={valueBinder(query$)} /\u003e\n\n      \u003cul\u003e\n        { query$.map(query =\u003e\n            todos\n              .filter(todo =\u003e todo.text.includes(query))\n              .map(todo =\u003e cache[todo.id])\n        ) }\n      \u003c/ul\u003e\n    \u003c/div\u003e\n  )\n}\n```\n\nIn the above example, if `noimplicitdispose` had not been used, each `TodoItem` would have been\ndisposed as soon as it was filtered out by the query, making subsequent renders invalid.\n\nThanks to `noimplicitdispose`, `TodoItem`s will only be disposed when `TodoList` itself is disposed.\n\n\n### Built-in optimizations\n\nRicochet provides several utilities designed to make things faster, which are listed below. Other\nutilities may be added later on, such as keyed lists, memoization, and batch rendering.\n\n##### Efficient list rendering\n\nThe [`ricochet/array`](#ricochetarray) module provides the\n[`observableArray\u003cT\u003e`](#function-observablearraytarray-observablearrayt) function, which takes an array\nand returns an [`ObservableArray\u003cT\u003e`](#interface-observablearrayt-extends-arrayt-readonlyobservablearraymemberst).\nThis specialized array provides the same interface as a regular array, but is able to efficiently\nmap changes to its underlying data to the DOM by implementing [`CustomNode`](#interface-customnode).\n\n```tsx\nconst numbers = observableArray\u003cnumber\u003e()\n\nreturn (\n  \u003cdiv\u003e\n    {/* 'push' is specialized to directly add new nodes to the DOM,\n        without updating the rest of the elements. */}\n    \u003cbutton onclick={() =\u003e numbers.push(numbers.length)}\u003eAdd number\u003c/button\u003e\n\n    {/* 'sync' returns a `ReadonlyObservableArray` that is synced with its source. */}\n    { numbers.sync(x =\u003e \u003ch2\u003e{x}\u003c/h2\u003e) }\n  \u003c/div\u003e\n)\n```\n\n\n## Examples, unit tests and benchmarks\n\n[Jest](https://jestjs.io) unit tests are available in the [src](./src) directory,\nand [benchmarks](https://github.com/krausest/js-framework-benchmark) will be added\nshortly.\n\n**Notice**: Due to Jest (or rather [jsdom](https://github.com/jsdom/jsdom))\nnot supporting Web Components and behaving differently from the browser,\ntests currently fail if ran via Jest. While waiting for these bugs to be fixed,\nthe [`examples`](./examples) page currently runs all tests in the browser,\nensuring that Ricochet keeps working as intended.\n\n\n## Tips\n\n- Sometimes, a component may want to receive either an observable value or a regular\n  value, depending on what the caller needs. However, in most cases `T` and `Observable\u003cT\u003e`\n  have very different interfaces, which makes it hard to manipulate one or the other\n  via a single API. In cases where an `Observable\u003cT\u003e` *could* be accepted, it is best to\n  always accept an `Observable\u003cT\u003e` sequence, since such sequences can be made to emit\n  a single element before completing, therefore acting exactly like a regular function (see:\n  [`constant`](#function-constanttvalue-constantt),\n  [`of`](https://rxjs-dev.firebaseapp.com/api/index/function/of)).  \n  Since these observable sequences complete right after emitting an item, their\n  resources will be disposed of quickly, and it will be as if a non-observable value\n  had been used.\n\n  Additionally, the built-in functions\n  [`combine`](#function-combineoobservables-subscribable-k-in-keyof-o-ok-extends-observableinfer-t--t--never)\n  and\n  [`compute`](#function-computetcomputation-computeobservablet--constantt) both accept observable sequences as\n  inputs, but may receive non-observable values, avoiding a useless subscription / unsubscription\n  operation.\n\n- Ricochet was designed with [RxJS](https://github.com/ReactiveX/rxjs)'s API in mind, but works\n  with many different reactive libraries. In fact, it only requires of observable\n  values to define a way to subscribe to them; therefore the\n  [`constant`](#function-constanttvalue-constantt) /\n  [`of`](https://rxjs-dev.firebaseapp.com/api/index/function/of) observable\n  may be implement as follows:\n\n  ```typescript\n  function of\u003cT\u003e(value: T): Subscribable\u003cT\u003e \u0026 Observable\u003cT\u003e {\n    const observable = {\n      [Symbol.observable]() {\n        return observable\n      },\n\n      subscribe(observer: Observer\u003cT\u003e) {\n        observer.next(value)\n        observer.complete()\n      }\n    }\n\n    return observable\n  })\n  ```\n\n\n\n# API\n\n### [`ricochet`](src/index.ts)\nThe core Ricochet API, used to render JSX nodes.\n\n#### [`type Observer\u003cT\u003e`](src/index.ts#L17-L24)\n\nDefines an observer.\n\n\nDefined as:\n```typescript\ntype Observer\u003cT\u003e = ((newValue: T) =\u003e void) | {\n  next(newValue: T): void\n  complete?(): void\n}\n```\n\n\n#### [`interface Subscription`](src/index.ts#L25-L28)\nDefines a subscription, which can be unsubscribed of.\n\n\n\n##### [`unsubscribe(): void`](src/index.ts#L29-L34)\nCancels the subscription, disposing of resources and cancelling pending operations.\n\n\n\n#### [`interface Subscribable\u003cT\u003e extends Observable\u003cT\u003e`](src/index.ts#L35-L38)\nDefines a value whose changes can be subscribed to.\n\n\n\n##### [`subscribe(observer): Subscription`](src/index.ts#L39-L44)\n| Parameter | Type | Description |\n| --------- | ---- | ----------- |\n| observer | `Observer\u003cT\u003e` | None |\n\nSubscribes to changes to the value.\n\n\n\n#### [`interface Observable\u003cT\u003e`](src/index.ts#L45-L56)\nDefines an observable value.\n\n\n\nThis interface defines the bare minimum for Ricochet\nto interface with reactive libraries that implement this\n[ECMAScript Observable proposal](https://github.com/tc39/proposal-observable#api),\nsuch as [RxJS](https://github.com/ReactiveX/rxjs).\n\n\n\n#### [`type MaybeObservable\u003cT\u003e`](src/index.ts#L57-L62)\n\nDefines a value that may be `Observable`.\n\n\nDefined as:\n```typescript\ntype MaybeObservable\u003cT\u003e = Observable\u003cT\u003e | T\n```\n\n\n#### [`type NestedNode`](src/index.ts#L63-L67)\n\nAn arbitrarily nested DOM `Node`.\n\n\nDefined as:\n```typescript\ntype NestedNode = Node | CustomNode | string | number | NodeArray | ObservableNode\n```\n\n\n#### [`interface ObservableNode extends Observable\u003cNestedNode\u003e`](src/index.ts#L68-L72)\nAn observable `NestedNode`.\n\n\n\n#### [`interface NodeArray extends Array\u003cNestedNode\u003e`](src/index.ts#L73-L77)\nA list of `NestedNode`s.\n\n\n\n#### [`type NodeRef`](src/index.ts#L78-L82)\n\nA mutable reference to a `Node`.\n\n\nDefined as:\n```typescript\ntype NodeRef = [Node]\n```\n\n\n#### [`type RenderFunction`](src/index.ts#L83-L87)\n\nThe function used to render a `NestedNode`.\n\n\nDefined as:\n```typescript\ntype RenderFunction = (value: NestedNode, previous: NodeRef, next: NodeRef) =\u003e void\n```\n\n\n#### [`interface CustomNode`](src/index.ts#L88-L91)\nA custom-rendered node.\n\n\n\n##### [`render(parent, previous, next, r): void`](src/index.ts#L92-L101)\n| Parameter | Type | Description |\n| --------- | ---- | ----------- |\n| parent | `Element` | None |\n| previous | `NodeRef` | None |\n| next | `NodeRef` | None |\n| r | `RenderFunction` | None |\n\nRenders the node in the DOM, as a child of the given parent.\n\n\n\nIn Ricochet, nodes must be rendered between other nodes. Since a single `CustomNode`\nmay be rendered as several DOM nodes, these DOM nodes should be inserted **before**\n`next`, and `previous` must be set to the **first** node that was inserted.\n\n\n\n#### [`type Connectable\u003cT\u003e`](src/index.ts#L102-L109)\n - `T`: `Node`\n\nDefines an element that can be connected to a node.\n\n\nDefined as:\n```typescript\ntype Connectable\u003cT extends Node\u003e =\n    ((element: T, attachSubscriptions: (...subscriptions: Subscription[]) =\u003e void) =\u003e void)\n  | { connect: (element: T, attachSubscriptions: (...subscriptions: Subscription[]) =\u003e void) =\u003e void }\n```\n\n\n#### [`type Component\u003cProps, ReturnType\u003e`](src/index.ts#L122-L127)\n - `Props`: `object`\n - `ReturnType`: `Node | Observable\u003cNode\u003e`\n\nDefines a function that takes properties and returns a self-updating element.\n\n\nDefined as:\n```typescript\ntype Component\u003cProps extends object, ReturnType extends Node | Observable\u003cNode\u003e\u003e = (props: Props) =\u003e ReturnType\n```\n\n\n#### [`function h\u003cTag\u003e(tag, attrs, ...children): JSX.IntrinsicElements[Tag]`](src/index.ts#L128-L136)\n - `Tag`: `keyof JSX.IntrinsicElements`\n\n| Parameter | Type | Description |\n| --------- | ---- | ----------- |\n| tag | `Tag` | None |\n| attrs | `JSX.IntrinsicAttributes \u0026 WritablePart\u003cJSX.IntrinsicElements[Tag]\u003e` | None |\n| children | `NodeArray` | None |\n\nRenders an intrinsic element.\n\n\n\n#### [`function h(tag, attrs, ...children): JSX.Element`](src/index.ts#L137-L145)\n\n| Parameter | Type | Description |\n| --------- | ---- | ----------- |\n| tag | `string` | None |\n| attrs | `JSX.IntrinsicAttributes \u0026 WritablePart\u003cElement\u003e` | None |\n| children | `NodeArray` | None |\n\nRenders an unknown intrinsic element.\n\n\n\n#### [`function h\u003cP, E, K\u003e(component, props, ...children): E`](src/index.ts#L146-L154)\n - `P`: `object`\n - `E`: `JSX.Element`\n - `K`: `Component\u003cP, E\u003e`\n\n| Parameter | Type | Description |\n| --------- | ---- | ----------- |\n| component | `K` | None |\n| props | `JSX.IntrinsicAttributes \u0026 P \u0026 WritablePart\u003cE\u003e` | None |\n| children | `NodeArray` | None |\n\nRenders a component.\n\n\n\n#### [`function attach(...subscriptions): typeof attach`](src/index.ts#L168-L179)\n\n| Parameter | Type | Description |\n| --------- | ---- | ----------- |\n| subscriptions | `(Subscription | Element)[]` | None |\n\nAttaches the given subscriptions or explicitly-disposed elements\nto the element that is currently being initialized.\n\n\n\n#### [`function mount(node): Element`](src/index.ts#L180-L184)\n\n| Parameter | Type | Description |\n| --------- | ---- | ----------- |\n| node | `ObservableNode` | None |\n\nMounts an observable node as a simple element.\n\n\n\n#### [`function mount(node, el): Subscription`](src/index.ts#L185-L189)\n\n| Parameter | Type | Description |\n| --------- | ---- | ----------- |\n| node | `NestedNode` | None |\n| el | `Element` | None |\n\nMounts the given observable node as a child of the given element.\n\n\n\n#### [`function eventListener\u003cN, E\u003e(type, opts): Connectable\u003cN\u003e \u0026 Subscribable\u003cE\u003e`](src/index.ts#L419-L447)\n - `N`: `Node`\n - `E`: `Event`\n\n| Parameter | Type | Description |\n| --------- | ---- | ----------- |\n| type | `string` | None |\n| opts | `boolean | AddEventListenerOptions` | None |\n\nReturns a `Connectable\u003cT\u003e` that can be used to register to events on one\nor more elements.\n\n\n\n\n### [`ricochet/array`](src/array.ts)\nUtilities for rendering lists efficiently with the `ObservableArray` type.\n\n#### [`type ArrayObserver\u003cT\u003e`](src/array.ts#L6-L17)\n\nDefines an object that can listen to changes to an `ObservableArray`.\n\n\nDefined as:\n```typescript\ntype ArrayObserver\u003cT\u003e = {\n  [key in string \u0026 keyof Array\u003cT\u003e]?:\n    Array\u003cT\u003e[key] extends (...args: infer Args) =\u003e any\n      ? (...args: Args) =\u003e void\n      : never\n} \u0026 {\n  set: (index: number, value: T) =\u003e void\n}\n```\n\n\n#### [`interface ReadonlyObservableArrayMembers\u003cT\u003e`](src/array.ts#L18-L22)\nInterfaces which exports all members implemented by `ReadonlyObservableArray\u003cT\u003e`,\nbut not `ReadonlyArray\u003cT\u003e`. Required by TypeScript.\n\n\n\n##### [`readonly length$: Subscribable\u003cnumber\u003e`](src/array.ts#L23-L27)\nReturns an observable sequence that emits whenever the length of this array changes.\n\n\n\n##### [`readonly change$: Subscribable\u003c[number, T]\u003e`](src/array.ts#L28-L32)\nReturns an observable sequence that emits whenever an item is modified.\n\n\n\n##### [`observe(observer, init): Subscription`](src/array.ts#L33-L40)\n| Parameter | Type | Description |\n| --------- | ---- | ----------- |\n| observer | `ArrayObserver\u003cT\u003e` | None |\n| init | `boolean` | If `true`, `push` will be called on initialization with the content of the array.  |\n\nObserves changes made to the array.\n\n\n\n\n##### [`mapArray\u003cR\u003e(f, thisArg): Observable\u003cR\u003e`](src/array.ts#L41-L46)\n| Parameter | Type | Description |\n| --------- | ---- | ----------- |\n| f | `(array: ReadonlyArray\u003cT\u003e) =\u003e R` | None |\n| thisArg | `any` | None |\n\nReturns an observable sequence that gets updated everytime this array\nchanges.\n\n\n\n##### [`sync\u003cR\u003e(f): ReadonlyObservableArray\u003cR\u003e`](src/array.ts#L47-L53)\n| Parameter | Type | Description |\n| --------- | ---- | ----------- |\n| f | `(value: T, index: number) =\u003e R` | None |\n\nPropagates changes to the items of the given list to items of a new list,\naccording to a `map` function.\n\n\n\n#### [`interface ReadonlyObservableArray\u003cT\u003e extends ReadonlyArray\u003cT\u003e, ReadonlyObservableArrayMembers\u003cT\u003e`](src/array.ts#L54-L58)\nDefines a readonly array whose changes can be observed.\n\n\n\n#### [`interface ObservableArray\u003cT\u003e extends Array\u003cT\u003e, ReadonlyObservableArrayMembers\u003cT\u003e`](src/array.ts#L59-L62)\nDefines an array whose changes can be observed.\n\n\n\n##### [`sync\u003cR\u003e(f, g): ObservableArray\u003cR\u003e`](src/array.ts#L63-L68)\n| Parameter | Type | Description |\n| --------- | ---- | ----------- |\n| f | `(value: T, index: number) =\u003e R` | None |\n| g | `(value: R, index: number) =\u003e T` | None |\n\nPropagates changes to the items of the given list to items of a new list,\nand back again.\n\n\n\n##### [`swap(a, b): T extends NestedNode ? void : never`](src/array.ts#L69-L155)\n| Parameter | Type | Description |\n| --------- | ---- | ----------- |\n| a | `number` | None |\n| b | `number` | None |\n\nSwaps the values at the two given indices.\n\n\n\n#### [`function isObservableArray\u003cT\u003e(array): array is ObservableArray\u003cT\u003e`](src/array.ts#L156-L162)\n\n| Parameter | Type | Description |\n| --------- | ---- | ----------- |\n| array | `any` | None |\n\nReturns whether the given array is an `ObservableArray`.\n\n\n\n\n### [`ricochet/async`](src/async.ts)\nUtilities for rendering with promises.\n\n#### [`function async\u003cE\u003e(component): E`](src/async.ts#L14-L19)\n - `E`: `Element`\n\n| Parameter | Type | Description |\n| --------- | ---- | ----------- |\n| component | `Promise\u003cE\u003e` | None |\n\nReturns an element that will be replaced by the result of the given\npromise when it resolves.\n\n\n\n#### [`function async\u003cP, E\u003e(component): Component\u003cP, E\u003e`](src/async.ts#L20-L25)\n - `P`: `{}`\n - `E`: `Element`\n\n| Parameter | Type | Description |\n| --------- | ---- | ----------- |\n| component | `(props: P) =\u003e Promise\u003cE\u003e` | None |\n\nWraps a component that asynchronously resolves as a regular component\nwhose element will be replaced once the promise resolves.\n\n\n\n#### [`function Async\u003cP, E, K\u003e({ component, props }): E`](src/async.ts#L46-L52)\n - `P`: `{}`\n - `E`: `Element`\n - `K`: `{}`\n\n| Parameter | Type | Description |\n| --------- | ---- | ----------- |\n| component | `Component\u003cP \u0026 K, E\u003e` | None |\n| props | `Promise\u003cK\u003e` | None |\n\nGiven a component, some of its properties, and a promise that resolves\nto the rest of its properties, returns an element that will be replaced\nby the resolved element when the promise finishes.\n\n\n\n#### [`function Async\u003cP, E\u003e({ component, ...props }): E`](src/async.ts#L53-L58)\n - `P`: `{}`\n - `E`: `Element`\n\n| Parameter | Type | Description |\n| --------- | ---- | ----------- |\n| component | `Promise\u003cComponent\u003cP, E\u003e\u003e` | None |\n| props | `P` | None |\n\nGiven a promise that resolves to a component and its properties, returns\nan element that will be replaced by the resolved element when the promise finishes.\n\n\n\n\n### [`ricochet/reactive`](src/reactive.ts)\nUtilities for creating and combining observable streams and subjects.\n\n#### [`interface Subject\u003cT\u003e extends Subscribable\u003cT\u003e`](src/reactive.ts#L5-L8)\nDefines a value that is both observable and observer.\n\n\n\n##### [`next(newValue): void`](src/reactive.ts#L9-L13)\n| Parameter | Type | Description |\n| --------- | ---- | ----------- |\n| newValue | `T` | None |\n\nUpdates the underlying value, notifying all observers of a change.\n\n\n\n#### [`function isSubject\u003cT\u003e(value): value is BuiltinSubject\u003cT\u003e`](src/reactive.ts#L14-L20)\n\n| Parameter | Type | Description |\n| --------- | ---- | ----------- |\n| value | `any` | None |\n\nReturns whether the given value is a subject created with `subject`.\n\n\n\n#### [`class BuiltinSubject\u003cT\u003e implements Subject\u003cT\u003e`](src/reactive.ts#L21-L37)\n`Subject` augmented with some specialized operations, returned by `subject`.\n\n\n\n##### [`value: T`](src/reactive.ts#L38-L56)\nGets or sets the underlying value.\n\n\n\n##### [`setUnderlyingValue(value)`](src/reactive.ts#L57-L76)\n| Parameter | Type | Description |\n| --------- | ---- | ----------- |\n| value | `T` | None |\n\nSets the underlying value without notifying observers.\n\n\n\n##### [`map\u003cR\u003e(map): Subscribable\u003cR\u003e`](src/reactive.ts#L77-L81)\n| Parameter | Type | Description |\n| --------- | ---- | ----------- |\n| map | `(input: T) =\u003e R` | None |\n\nReturns a new `Observable` that gets updated when this subject changes.\n\n\n\n##### [`map\u003cR\u003e(map, unmap): BuiltinSubject\u003cR\u003e`](src/reactive.ts#L82-L116)\n| Parameter | Type | Description |\n| --------- | ---- | ----------- |\n| map | `(input: T) =\u003e R` | None |\n| unmap | `(input: R) =\u003e T` | None |\n\nReturns a new `Subject` value that propagates changes to values both ways.\n\n\n\n#### [`function subject\u003cT\u003e(initialValue): BuiltinSubject\u003cT\u003e`](src/reactive.ts#L117-L124)\n\n| Parameter | Type | Description |\n| --------- | ---- | ----------- |\n| initialValue | `T` | None |\n\nReturns a reactive wrapper around the given value.\n\n\n\n#### [`class Constant\u003cT\u003e implements Subscribable\u003cT\u003e`](src/reactive.ts#L125-L150)\n`Subscrible` that represents a constant value, returned by `constant`.\n\n\n\n#### [`function constant\u003cT\u003e(value): Constant\u003cT\u003e`](src/reactive.ts#L151-L162)\n\n| Parameter | Type | Description |\n| --------- | ---- | ----------- |\n| value | `T` | None |\n\nReturns an observable value that emits a single value,\nand then immediately completes.\n\n\n\nThis function should be used when an observable stream\nis expected somewhere, but a single constant value can be provided.\n\n\n\n#### [`class ComputeObservable\u003cT\u003e extends BuiltinObservable\u003cT\u003e`](src/reactive.ts#L163-L215)\nAn `Observable` that computes its values based on an arbitrary computation,\nwhich may itself depend on other observable sequences; returned by `compute`.\n\n\n\n#### [`function compute\u003cT\u003e(computation): ComputeObservable\u003cT\u003e | Constant\u003cT\u003e`](src/reactive.ts#L216-L244)\n\n| Parameter | Type | Description |\n| --------- | ---- | ----------- |\n| computation | `($: \u003cU\u003e(observable: Observable\u003cU\u003e, defaultValue?: U) =\u003e U) =\u003e T` | None |\n\nReturns an observable that will be updated when any of the given observables\nchanges.\n\n\n\nSee [S.js](https://github.com/adamhaile/S) for the inspiration for this function. Please note\nthat unlike S, changes are propagated immediately, without waiting for the next time unit.\n\n\n\n##### Example\n\n\n\n```typescript\nconst a = subject(1)\nconst b = subject(1)\n\nconst c = compute($ =\u003e $(a) + $(b))\n\nc.subscribe(console.log).unsubscribe() // Prints '2'\n\na.next(10)\n\nc.subscribe(console.log) // Prints '11'\n\nb.next(20) // Prints '30'\n\n```\n\n#### [`type ObservableValueTypes\u003cO\u003e`](src/reactive.ts#L245-L251)\n\nMaps `Observable\u003cT\u003e` properties of an object to `T`.\n\n\nDefined as:\n```typescript\ntype ObservableValueTypes\u003cO\u003e = {\n  [K in keyof O]: O[K] extends Observable\u003cinfer T\u003e ? T : O[K]\n}\n```\n\n\n#### [`class CombineObservable\u003cO extends any[]\u003e extends BuiltinObservable\u003cObservableValueTypes\u003cO\u003e\u003e`](src/reactive.ts#L252-L278)\nAn `Observable` sequence that emits values when any of its dependencies\nis updated; returned by `combine`.\n\n\n\n\n### [`ricochet/interop/rxjs`](src/interop/rxjs.ts)\nInterop helpers for [RxJS](https://github.com/ReactiveX/rxjs).\n\n\n### [`ricochet/interop/wc`](src/interop/wc.ts)\nUtilities for defining Web Components.\n\n#### [`function makeCustomElement\u003cP, E\u003e(component, translateProperties): typeof HTMLElement`](src/interop/wc.ts#L5-L28)\n - `P`: `object`\n - `E`: `JSX.Element`\n\n| Parameter | Type | Description |\n| --------- | ---- | ----------- |\n| component | `Component\u003cP, E\u003e` | None |\n| translateProperties | `object \u0026 { [K in keyof P]: (value?: string) =\u003e P[K] }` | None |\n\nCreates a custom element (or web component) out of a JSX component.\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F71%2Fricochet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F71%2Fricochet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F71%2Fricochet/lists"}