npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

extended-ds-client

v7.0.0

Published

Promise based additional methods on the deepstream js client (polyfilled)

Downloads

30

Readme

Extended Deepstream client

Promise based deepstream client. It's basically the deepstream.io-client-js with basic calls promisified, plus some extra methods. All methods are added as polyfills.

Install

npm i -S extended-ds-client

Overview

Creating a client through this package will give you additional methods on the client object, leaving everything from the default client untouched (getRecord, getList etc).

These are the additional functions:

In case of rejection on any of these functions, the rejected argument is always an instance of Error.

There is also a utility function to import from this module:

Tunneling export of CONSTANTS & MERGE_STRATEGIES (so that you don't also have to import deepstream.io-client-js for these).

Example 1

import getClient from 'extended-ds-client';

const client = getClient('localhost:6020');

client.p
  .login({})
  .then(data => {
    console.log('Successful login.', data);
  })
  .catch(data => {
    console.log('Login failed.', data);
  });

const users = {};
client.record.p.getList('users').then(list => {
  // With the list of entries, map each entry to a record promise and
  // wait for all to get finished:
  Promise.all(list.getEntries().map(path => client.record.p.getRecord(path))).then(records =>
    records.forEach(record => {
      // Now save all data etc
      const user = record.get();
      users[user.id] = user;
    }),
  );
});

// Default functions still work
client.record.getList('article/x35b/comments').whenReady(l => {
  // ...
});

Example 2

import getClient, { CONSTANTS, ds } from 'extended-ds-client';

ds.client = getClient('localhost:6020'); // use singleton feature

ds.client.loginP({}); // using alias pattern (instead of p.login)

console.log(CONSTANTS); // MERGE_STRATEGIES is also available as import

Example 3

// Given Example 2 file is imported together with this one

import { ds } from 'extended-ds-client';

// And why not use async-await now...

async function fetchUsers() {
  const l = await ds.client.record.p.getExistingList('users');
  const records = await Promise.all(
    l.getEntries().map(path => ds.client.record.p.getExistingRecord(path)),
  );
  const users = {};
  records.forEach(r => {
    const user = r.get();
    users[user.id] = user;
  });
  return users;
}

getClient

Default export from this module is the function getClient to create a client (just like deepstream() in original client). In the same way it also accepts an options object.

const client = getClient('localhost:6020', {
  datasetRecordFullPaths: false,
  datasetRecordIdKey: 'rid',
  splitChar: '.',
});

| Option | Type | Default | Description | | ------------------------ | --------- | ------- | ----------------------------------------------------------- | | datasetRecordFullPaths | boolean | true | Full paths in lists (or only id) for datasetRecord methods. | | datasetRecordIdKey | string | 'id' | ID key for dataset records. | | splitChar | string | '/' | Splitting character in paths (for dataset records). |

Promisification API

The promises will be resolved with the same argument(s) as the default client callbacks would get (or the whenReady when applicable). See the deepstream JS client documentation.

p.login

Alias: loginP

Straightforward promisification of login. See Example 1 above.

Note: Default login still works, you can still do the standard line with callback: client.login({}, success => console.log(success));

record.p.getRecord

Alias: record.getRecordP

Promisification of record.getRecord.

client.record.p.getRecord(name)
  .then(dsRecord => ...)
  .catch(error => ...);

record.p.getList

Alias: record.getListP

Promisification of record.getList.

client.record.p.getList(name)
  .then(dsList => ...)
  .catch(error => ...);

record.p.setData

Alias: record.setDataP

Promisification of record.setData.

client.record.p.setData(name, path, data)
  .then(() => ...)
  .catch(error => ...);

record.p.snapshot

Alias: record.snapshotP

Promisification of record.snapshot.

client.record.p.snapshot(name)
  .then(record => ...)
  .catch(error => ...);

record.p.has

Alias: record.hasP

Promisification of record.has, but will reject if it does not exist (and on error of course).

A second optional argument makes the check inverted -- rejecting on existing record. (This can be handy to make sure an id is free).

client.record.p.has(name)
  .then(() => ...)
  .catch(error => ...);

await client.record.p.has('record1');
// if we get here, record1 is a non-taken record id

rpc.p.make

Alias: rpc.makeP

Promisification of rpc.make.

client.rpc.p.make(name, data)
  .then(result => ...)
  .catch(error => ...);

Additional API functions

record.p.getExistingRecord

Alias: record.getExistingRecordP

Additional method that does a .has-check before .getRecord to get a record handler without implicit record creation (Compare with snapshot that also fails if the record does not exist, but returns the actual record instead of a record handler). It rejects the promise if the record does not exist.

client.record.p.getExistingRecord(name)
  .then(dsRecord => ...)
  .catch(error => ...);

record.p.getExistingList

Alias: record.getExistingListP

Like p.getExistingRecord above, but for List.

client.record.p.getExistingList(name)
  .then(dsList => ...)
  .catch(error => ...);

record.p.deleteRecord

Alias: record.deleteRecordP

Will resolve when the delete event is emitted (avoiding the race condition risk).

client.record.p.deleteRecord(arg)
  .then(() => ...)
  .catch(error => ...);

| Argument | Type | Default | Description | | -------- | ----------------- | ------- | ----------------------------------------------- | | arg | string/Object | | The path to the record OR a DS Record object. |

record.p.deleteList

Alias: record.deleteListP

Like p.deleteRecord above, but for List.

client.record.p.deleteList(arg)
  .then(() => ...)
  .catch(error => ...);

| Argument | Type | Default | Description | | -------- | ----------------- | ------- | ------------------------------------------- | | arg | string/Object | | The path to the list OR a DS List object. |

record.p.addToList

Alias: record.addToListP

Add an entry or multiple entries to an existing list, without duplicates.

client.record.p.addToList('books', 'selfish-gene')
  .then(dsList => ...)
  .catch(error => ...);

| Argument | Type | Default | Description | | ---------- | ---------------- | ------- | ---------------------------- | | listPath | string | | The name/path of the record. | | id | string/Array | | Entry(ies) to add. |

record.p.removeFromList

Alias: record.removeFromListP

Remove an entry or multiple entries from an existing list.

client.record.p.removeFromList('books', 'selfish-gene')
  .then(dsList => ...)
  .catch(error => ...);

| Argument | Type | Default | Description | | ---------- | ---------------- | ------- | ---------------------------- | | listPath | string | | The name/path of the record. | | id | string/Array | | Entry(ies) to add. |

record.p.updateExistingRecord

Alias: record.updateExistingRecordP

Update a record, choosing from one of multiple update modes:

  • shallow: Default mode. Overwrite each property at the base level.
  • overwrite: Replace the whole record.
  • deep: Deep merge of the updates into record (using lodash.merge).
  • deepConcat: Deep merge, but concatenate arrays with only simple values.
  • deepConcatAll: Merge like deepConcat, but concatenate ALL arrays.
  • deepIgnore: Deep merge, but leave unchanged if some value in updates is '%IGNORE%'.
  • deepConcatIgnore: Merge like deepConcat, but skip values like deepIgnore.
  • removeKeys: With updates as an array, remove corresponding keys in record.

Two additional array arguments, lockedKeys and protectedKeys, makes it possible to lock or protect given keys, regardless of update mode. Locked keys won't be altered and protected ones won't be removed.

client.record.p.updateExistingRecord('record1', { a: 1, data: { b: 2 }})
  .then(() => ...)
  .catch(error => ...);

client.record.p.updateExistingRecord(
  'records/r-x',
  { data: { configs: [{ items: ['toBeConcatenated'] }] } },
  'deepConcat',
)
  .then(() => ...)
  .catch(error => ...);

client.record.p.updateExistingRecord(
  'record1',
  { data: { confs: ['%IGNORE%', { items: ['replacingFirstItem'] }] }, id: 'xb24' },
  'deepIgnore',
  ['id']
)
  .then(() => ...)
  .catch(error => ...);

| Argument | Type | Default | Description | | --------------- | ---------------- | ----------- | ------------------------------------------ | | name | string | | The name/path of the record. | | updates | Object/Array | | The updates. Array for mode = removeKeys | | mode | string | "shallow" | Update mode, see details above. | | lockedKeys | Array | | Keys which values can't be altered. | | protectedKeys | Array | | Keys which can't be removed. |

record.p.getDatasetRecord

Alias: record.getDatasetRecordP

In case you often end up with the structure of having a list of some type of records as the "parent" of those records. For example a list of all bookings at bookings and the bookings at bookings/room-19, bookings/room-27 etc.

On resolve you get back both the deepstream list handle and record handle.

The options described in getClient above will influence how this function operates.

client.record.p.getDatasetRecord('bookings', 'room-27').then(([dsList, dsRecord]) => {
  const booking = dsRecord.get();
  console.log(dsList.getEntries());
  console.log(booking.time, '-', booking.customer);
});

| Argument | Type | Default | Description | | ------------ | -------- | ------- | -------------------------------------- | | listPath | string | | The path to the list. | | recordId | string | | The ID of the record. | | initiation | Object | | If created, initiate record with this. |

record.p.deleteDatasetRecord

Alias: record.deleteDatasetRecordP

Removes both a record and its entry in the list, as created with getDatasetRecord.

client.record.p.deleteDatasetRecord('bookings', 'room-27').then(dsList => {
  console.log('List of records after delete:', dsList.getEntries());
});

| Argument | Type | Default | Description | | ---------- | -------- | ------- | --------------------- | | listPath | string | | The path to the list. | | recordId | string | | The ID of the record. |

Utility functions

These are not extensions of the client object, but freely importable functions.

addEntry

An alternative way to add entries to a deepstream list, that prevents duplicates.

See also above method record.p.addToList that utilizes this one.

import { addEntry } from 'extended-ds-client';

client.record.p.getExistingList('books')
  .then(dsList => addEntry(dsList, 'selfish-gene'));
  .catch(error => ...);

| Argument | Type | Default | Description | | -------- | -------- | ------- | ----------------- | | list | Object | | A DS List object. | | str | string | | The entry to add. |

Licence

MIT

Change log

6.0

  • Removed \*ListedRecord in favor of new getDatasetRecord & deleteDatasetRecord
  • Name changes of options
    • listedRecordFullPaths -> datasetRecordFullPaths
    • listedRecordIdKey -> datasetRecordIdKey
  • Added a very versatile updateExistingRecord method
  • record.p.has now rejects on non-existent record

5.0

  • Added getListedRecord that returns list & record handles
    • Will create both list & record if non-existent
    • Consistent with original getList/getRecord
  • setListedRecord now only returns the record id
  • Added method deleteListedRecord
  • addToList & removeFromList now also accepts multiple entries
  • Options added that controls how *listedRecord operates
    • datasetRecordFullPaths
    • datasetRecordIdKey
    • splitChar

4.0

  • New primary naming / method access, using p as scope
    • Keeping old naming as aliases
  • Full test coverage
  • Much smaller footprint in node_modules
  • Dropping unofficial tenant extension
  • Dropping deprecated methods
  • Improved documentation with more examples/code

3.0

  • Methods added as polyfills
  • New method addToList
  • Re-exporting of deepstream client constants

Please create an Issue in github if you feel something is missing!