{"id":19559996,"url":"https://github.com/plotdb/datahub","last_synced_at":"2026-06-09T01:04:09.426Z","repository":{"id":57137278,"uuid":"315659336","full_name":"plotdb/datahub","owner":"plotdb","description":"Access scoped data via piped operational transformation.","archived":false,"fork":false,"pushed_at":"2023-11-07T18:20:36.000Z","size":608,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-14T04:36:44.482Z","etag":null,"topics":["collaboration","data-flow","state-management"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","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/plotdb.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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}},"created_at":"2020-11-24T14:36:13.000Z","updated_at":"2021-10-01T13:19:11.000Z","dependencies_parsed_at":"2024-10-29T21:44:34.199Z","dependency_job_id":null,"html_url":"https://github.com/plotdb/datahub","commit_stats":{"total_commits":59,"total_committers":1,"mean_commits":59.0,"dds":0.0,"last_synced_commit":"5b7241d79f6493a74c66a377dda67fb625d3ab72"},"previous_names":[],"tags_count":23,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/plotdb%2Fdatahub","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/plotdb%2Fdatahub/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/plotdb%2Fdatahub/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/plotdb%2Fdatahub/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/plotdb","download_url":"https://codeload.github.com/plotdb/datahub/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240569146,"owners_count":19822249,"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":["collaboration","data-flow","state-management"],"created_at":"2024-11-11T05:05:28.450Z","updated_at":"2025-11-20T01:03:43.819Z","avatar_url":"https://github.com/plotdb.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# @plotdb/datahub\n\nAccess scoped data via piped operational transformation.\n\n\n## Installation\n\n    npm install @plotdb/datahub\n\n\n## Usage\n\nafter including `datahub.bundle.min.js`:\n\n    # this is our data source.\n    src = new datahub.src do\n      ops-out: (ops) -\u003e # update data src by incoming ops\n      get: -\u003e # return complete data. raw data is returned, users should make their own copy if to use.\n\n    # this is our view controller\n    des = new datahub.des do\n      ops-in (ops): -\u003e # update ui / widget\n\n    # data through pipe, scoped under \"my-view\"\n    view-hub = new datahub {scope: [\"my-view\"]}\n    # ... from srchub / through view-hub / to deshub\n    src.pipe view-hub .pipe des\n\n    # notify all users (deshub) about data changed\n    src.ops-in [... /* ops */ ...]\n\n    # update data source (srchub)\n    des.ops-out [... /* ops */ ...]\n\n\n## Memhub, Userhub\n\nDatahub also provides auxiliary hubs for quick testing and evaluation.\n\n - `memhub` - data source hub. store data in memory.\n - `usrhub` - simple destination hub. constructor options:\n   - render(ops): called when there are updates from data source.\n\nA sample usage of `memhub` and `usrhub` as follows:\n\n    mhub = new datahub.mem!\n    uhub = new datahub.usr {render: -\u003e console.log('ok'); }\n    mhub.pipe uhub\n    document.querySelector('textarea').addEventListener \\input, -\u003e\n      uhub.ops-out [\n        { p: ['str',0], si: @value[@value.length - 1] }\n      ]\n\n\n## Wrap constructor\n\nInstead of `userhub`, you can use `datahub.as` to wrap your constructor to support hub mechanism directly with your own class:\n\n    someClass = datahub.as (opt = {}) -\u003e @\n    someClass.prototype \u003c\u003c\u003c { ... }\n\n\nYou can pipe source hub to the constructed hub object:\n\n    hub = new myHub();\n    src.pipe(hub);\n\nWrapped hub only provides following methods:\n\n - `state()`\n - `on(name, cb)`\n - `fire(name, ...args)`\n - `ops-out(ops)`\n - `get()`\n\nAnd following member variables to use:\n\n - `hub`: the actual hub object used to accept source data.\n - `data`: data object from source.\n\nAs a wrapped hub user, you should do the following:\n\n - implement `ops-in` function to render / update your application. note:\n   - ops has been applied to `data` member before your `ops-in` is called.\n   - however any data kept locally should still be updated based on ops.\n - watch `open` / `close` event or use `state` method to track data stream status.\n\nFollowing is a sample wrapped hub:\n\n    form = datahub.as (o = {}) -\u003e\n      # udate based on a input element change\n      o.node.addEventListener \\input, (evt = {}) ~\u003e @update evt.target.value \n      # keep a local _data to diff original data for ops\n      @on \\open, ~\u003e @_data = JSON.parse(JSON.stringify(@data)); @render!\n      @ \u003c\u003c\u003c o{name}\n    form.prototype \u003c\u003c\u003c\n      # re-render on every ops-in\n      ops-in: (ops) -\u003e\n        json0.type.apply @_data, ops\n        @render!\n      render: -\u003e console.log @_data\n      update: (v) -\u003e\n        if @state! == \\closed =\u003e return\n        @_data[@name] = v\n        @ops-out json0.diff(@data, @_data)\n\nFor a live, working example, please check `web/src/pug/as.pug`.\n\n\n## Sharehub\n\nSharehub provides a simple interface and implementation reference for adopting ShareDB with data hub to keep edited data in database:\n\n    uhub = new datahub.usr!\n    shub = new sharehub({\n      id: 'my-sharedb-doc-id'\n      create: -\u003e {} # init obj if doc not found.\n    })\n    shub.init!\n      .then -\u003e shub.pipe uhub\n\nSharehub is in a standalone JS file. include `sharehub.js` / `sharehub.min.js` / `sharehub.bundle.min.js` if you want to use it.\n\nConstructor options:\n\n - `id`: id of the sharedb doc to connect.  optional\n - `collection`: collection of the sharedb collection to connect.  optional, default `doc` if omitted.\n - `initConnect`: default true. if true, auto connect to sharedb if `id` is given.\n - `create()`: empty object creator function. should return an object for initial value.\n   - when omitted, a creator function returing an empty object is used.\n - `watch(ops, src)`: watcher function. optional\n - `ews`: `@plotdb/ews` instance to use. required.\n\n\nAPIs:\n\n - `connect(opt)`: connect to sharedb. return Promise, resolved when connected.\n   - `opt` is either:\n     - omitted (undefined): `connect()` will use stored `id` and `collection` to (re)connect.\n     - a string for id of the sharedb doc to connect. In this case, `collection` will be set to `doc`.\n     - an object with fields:\n       - `id`: sharedb id. use stored `id` if omitted. this id will be stored for future use. (e.g., auto-reconnect)\n       - `collection`: `doc` if omitted.\n       - `force`: default true. when true, force to reconnect even if `id` / `collection` is the same.\n          - when `opt` is a string or omitted, `force` will also be true.\n   - if `id` / `collection` are provided in `opt`, they will be stored internal for future use.\n - `disconnect()`: disconnect current doc from sharedb. return Promise, resolved when disconnected.\n   - this will be automatically called when internal `sdb-client` is closed.\n - `config(opt)`: update configuration. opt has following fields:\n   - `id`: sharedb id.\n   - `collection`: `doc` if omitted.\n\nAdditionally, `sharehub` fire following events\n\n - `open`: fired when a connect is successfully done.\n - `close`: fired when `disconnect` is called.\n - `error`: fired when internal sdb-client object fires error events.\n\n\n## Scoping\n\nPassing `scope` option ( an array of strings / numbers ) into datahub constructor to filter incoming op and data based on the specified scope. For example, assume our data source keeps data in `datasrc` variable, then following hub:\n\n    new hub({scope: [\"users\", \"deleted\"]})\n\nwill only pass ops that affect object in `datasrc.users.deleted`. Furthermore, data get from this hub will only be the subtree in `datasrc.users.deleted`.\n\nYou can pipe data source to a hub that is scoped, and pipe this scoped hub to the destination hubs that use the same subtree.\n\n\n## Object API\n\n - `ops-in(ops)`: send ops from this hub to subscribers. ( inward / down to client )\n - `ops-out(ops)`: send ops from this hub to src. ( outward / up to server ) \n - `as-src(o)`: set this hub as a source hub (provide data and handle outward request).\n   - fields of `o`:\n     - `get()`: a function returning a full snapshot of data.\n     - `ops-out(ops)`: a function to handle outward ops.\n   - a source hub is responsible to call `ops-in(ops)` when there are data changes from source (inward ops)\n - `as-des(o)`: set this hub as destination hub ( handle inward events by such as rendering )\n   - fields of `o`:\n     - `ops-in(ops)`: a function to handle inward ops.\n   - a destination hub is responsible to call `ops-out(ops)`  when there are data changes to source (outward ops)\n - `get()`: return a snapshot of source data\n - `pipe(hub)`: pipe down events to `hub`.\n - `addon(ops)`: prepend `ops` to create node for ops accessing non-existed path\n - `cut(hub)`: remove `hub` from current object's subscriber list.\n - `state(s)`: change state. s can be either `opened` or `closed`.\n   - state propagates automatically. Should only be used by source hub.\n   - des hub should always call `get` to retrieve a new object and reinitialize if needed after each `open` event.\n\n\n## Class API\n\n - `as(constructor)`: wrap `constructor` as a hub that can be piped.\n   - constructor options depends on how `constructor` is implemented, with following additional fields:\n     - `scope`: the `scope` used by hub, as defined above.\n\n\n## Tabhub\n\nTabhub is a local-based hub that provides data sharing mechanism across browser tabs within a computer.\n\nConstructor options include all datahub's options, plus:\n\n - `channelName`: optional channel name. default `default-channel`\n - `initialData`: initial data. will be ignored if live data is available in broadcast channel.\n\n\nAPIs: include all datahub's APIs, plus:\n\n - `connect()`: connect to local tab group by the given channel name.\n\n\nSample usage:\n\n    hub = new tabhub({channelName: 'tabhub-test', initialData: content: \"some simple text\"})\n    \u003c- hub.connect!then _\n    hub.as-des ops-in: -\u003e /* ... call renderer to render content somewhere ... */\n    something.on 'update', -\u003e hub.ops-out json0.diff ... /* try diff a ops to send */\n\nFor demo check `web/static/tabhub/`.\n\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fplotdb%2Fdatahub","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fplotdb%2Fdatahub","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fplotdb%2Fdatahub/lists"}