tinkerhub-discovery
This library contains base classes for building and consuming service discovery mechanisms using JavaScript and TypeScript.
It is intended to be used to implement some sort of discovery mechanism, such as those for SSDP and MDNS.
The library is made available via NPM:
npm install tinkerhub-discovery
API
The basic functionality of a discovery instance is exposed via the type
ServiceDiscovery
:
; // Fetch services availablefor of discovery.services // Get or find services;;; // Listen to when services become available or unavailablediscovery.onAvailable/* service is now available */;discovery.onUnavailable/* service is no longer available */; // Some discoveries support updatesdiscovery.onUpdate/* service has been updated */ // When done using a discovery it should be destroyedawait discovery.destroy;
Filtered, mapping and merging services
Filtering services
Discovery instances can be filtered, which provides a live filtered view that supports events:
; // Listen to events as with the main discovery instancefiltered.onAvailable...; // Destroy the filtered view and its parentawait filtered.destroy; // Alternative to destroy - release the view, but do not destroy the parentawait filtered.release;
Mapping services into more specific types
Similar to filtering it is possible to map services into another object, as long as the mapped object contains an identifier:
;
Mapping supports returning promises to perform asynchronous functions during mapping:
;
Mappers may return null
if they do not wish to map a service, in which case
the discovery acts as it was filtered out.
Mapping has an advanced mode where it's possible to optionally react to updated and unavailable services:
Merging services from multiple discoveries
It's possible to create a merged view that returns services from multiple discoveries. Such services will be merged using their identifier and the first service seen will be returned for as long as it is valid.
// Merge two discoveries together; // To combine more than two discoveries use `MergedDiscovery` directly;new MergedDiscovery; // Destroy the discovery and the merged discoveriesawait discovery.destroy; // Release this discovery without destroying the merged discoveriesawait discovery.release;
Combining filtering and mapping
Combining filtering and mapping can make it easy to find and create a more specific API for a service, like this example that looks for Philips Hue bridges:
; .filterservice.headers .mapnew HueBridgeservice; discovery.onAvailable/* instance of HueBridge is available */; // To shutdown discovery (will destroy the root discovery, the filtered and mapped view)await discovery.destroy;
Manual discovery
If you have a need to keep a manually updated list of services, it's possible
to create an instance of ManualDiscovery
and add/remove services as needed:
; ; // Add servicesdiscovery.addnew ServiceType...; // Remove servicesdiscovery.remove'idOfService';discovery.removeserviceInstance; // Set the exact services availablediscovery.set;
Building a custom discovery
There are a few ways to build a custom discovery, with the two main ways being:
- Event-based discovery, services are added or removed when changed
- Sync-based discovery, services are discovered in bulk
A very basic example would be this discovery that listens for incoming UDP packets and just adds them as services:
const dgram = ;const BasicServiceDiscovery = ; { super'custom'; thissocket = dgram; thissocket; thissocket; thissocket; } { thissocket; super; }
The above discovery would never remove any services, but extending
ExpiringServiceDiscovery
would activate time based and remove services based
on when they were last seen:
const dgram = ;const ExpiringServiceDiscovery = ; { super'custom' expirationTime: 60*1000 /* milliseconds */ ; thissocket = dgram; thissocket; thissocket; thissocket; } { thissocket; super; }