Streaming API client for linking to lanes of stateful Web Agents using the WARP protocol, enabling massively real-time applications that continuously synchronize all shared states with ping latency
npm install @swim/clientThe Swim Client library implements a streaming API client for linking to lanes
of stateful Web Agents using the multiplxed WARP streaming protocol.
Command line tool for linking to Web Agent lanes over the WARP protocol.
``sh`
npm install -g swim-client
`sh
$ swim-client help
Usage: swim-client
Commands:
link stream changes to a lane of a remote node
sync stream the current state and changes to a lane of a remote node
get fetch the current state of a lane of a remote node
reflect stream introspection metadata
help
`
`sh
$ swim-client sync help
Usage: swim-client sync [options]
Options:
-h, --host
-n, --node
-l, --lane
-f, --format
Commands:
help
`
`sh
$ swim-client reflect help
Usage: swim-client reflect [options]
Options:
-e, --edge
-m, --mesh
-p, --part
-h, --host
-n, --node
-l, --lane
-k, --link introspect link behavior
-r, --router introspect router behavior
--data introspect data behavior
--system introspect system behavior
--process introspect process behavior
-s, --stats stream introspection statistics
-f, --format
Commands:
log stream log events
help
`
A WarpRef is a handle through which WARP downlinks can be opened.WarpClient implements the WarpRef interface, as does the exportedswim
Swim Client module object, and by extension, the global namespace
object used web browsers and other non-module contexts.
WarpRef instances have four methods that open different kinds of downlinks.downlink
The method creates an EventDownlink for streaming raw events fromvalueDownlink
any Web Agent lane. The method creates a ValueDownlink formapDownlink
synchronizing state with a Web Agent value lane. The methodMapDownlink
creates a for synchronizing state with a Web Agent map lane.listDownlink
And the method creates a ListDownlink for synchronizing state
with a Web Agent list lane.
`typescript`
swim.downlink()
.hostUri("warp://traffic.swim.services")
.nodeUri("swim:meta:mesh")
.laneUri("linkStats")
.onEvent((value) => console.log(value.toLike()))
.open();
WarpRef instances can also be used to observe key lifecycle events.WarpRef.didConnect
The method registers an observer callback thatWarpRef.didDisconnect
gets invoked whenever a connection to a WARP host is establishes.
The method registers an observer callback thatWarpRef.didAuthenticate
gets invoked whenever a WARP host disconnects. WarpRef.didDeauthenticate
registers an observer callback that gets invoked whenever the client
successfully authenticates with a WARP host. WarpRef.didFail
gets invoked when a WARP host rejects the client's authentication credentials.
And the method registers an observer callback that gets
invoked when the client encounters an unexpected error.
`typescript`
swim.didConnect((host) => console.log("connected to", host));
swim.didDisconnect((host) => console.log("disconnected from", host));
swim.didAuthenticate((session, host) => console.log("authenticated to", host, "with session", session.toLike()));
swim.didDeauthenticate((reason, host) => console.log("deauthenticated from", host, "because", reason.toLike()));
swim.didFail((error, host) => console.log("host", host, "failed because", error));
The WarpClient class handles connection management and link routing,WarpRef
and implements the interface. In addition to opening downlinks,WarpClient instances can be used to send arbitrary WARP commands, to provideHostRef
authentication credentials for hosts, to control network reconnection behavior,
and to create , NodeRef, and LaneRef scopes to facilitate downlink
management.
The WarpClient.authenticate method associates a credentials structure with@auth
a particular host URI. The credentials will be sent in a WARP envelope
whenever the client connects to the specified host.
`typescript`
swim.authenticate("warps://example.com", {"@openId": jwt});
Distinct WarpClient instances can be used to create isolated connection pools
for different security domains.
`typescript
const userClient = new WarpClient();
userClient.authenticate("warps://example.com", {"@openId": userJwt});
const toolClient = new WarpClient();
toolClient.authenticate("warps://example.com", {"@oauth": toolJwt});
`
The WarpClient.command method sends a WARP command message to a lane ofWarpClient.command
a remote node. takes either three our four arguments.command
The three argument overload takes a node URI, a lane URI, and acommand
command payload. The node URI must have an authority component that specifies
the host to which the command should be sent. The four argument
overload takes a host URI, a node URI, a lane URI, and a command payload;
the node URI is interpreted relative to the host URI.
`typescript`
swim.command("warp://example.com/house/kitchen", "light", "on");
swim.command("warp://example.com", "/house/kitchen", "light", "off");
The WarpClient.isOnline method returns true when the the client hasWarpClient.keepOnline
access to a network; it can also be used to force a client online or offline.
The method controls whether or not the client shouldkeepOnline
automatically reopen connections after a network failure. Note that the state of the client overrides the keepLinked state ofkeepOnline
individual downlinks. Setting to false can be useful fortrue
ephemeral clients, but should typically be left .
`typescript
swim.isOnline(); // true most of the time
swim.isOnline(false); // force offline
swim.isOnline(true); // force online
swim.keepOnline(); // defaults to true
swim.keepOnline(false); // disable network reconnection
`
The WarpClient.hostRef method returns a new HostRef bound to the givenWarpClient.nodeRef
host URI. The method returns a new NodeRef boundWarpClient.laneRef
to the given host and node URIs. The method returnsLaneRef
a new bound to the given host, node, and lane URIs.
A HostRef is a WarpRef that automatically provides its bound host URI whenHostRef
opening downlinks, sending commands, and providing authentication credentials. instances keep track of all the downlinks they directly open. WhenHostRef
a is closed, it automatically closes all of its open downlink views.
`typescript`
const hostRef = swim.hostRef("warp://traffic.swim.services");
hostRef.downlink()
.nodeUri("swim:meta:mesh")
.laneUri("linkStats")
.onEvent((value) => console.log(value.toLike())})
.open();
// ...
hostRef.close();
The HostRef.nodeRef and HostRef.laneRef instance methods can be used toWarpRef
create further resolved scopes.
`typescript`
const hostRef = swim.hostRef("warp://traffic.swim.services");
const nodeRef = hostRef.nodeRef("swim:meta:mesh");
const laneRef = hostRef.laneRef("swim:meta:mesh", "linkStats");
A NodeRef is a WarpRef that automatically provides its bound host and nodeNodeRef
URIs when opening downlinks and sending commands. instances keepNodeRef
track of all the downlinks they directly open. When a is closed,
it automatically closes all of its open downlink views.
`typescript`
const nodeRef = swim.nodeRef("warp://traffic.swim.services", "swim:meta:mesh");
nodeRef.downlink()
.laneUri("linkStats")
.onEvent((value) => console.log(value.toLike())})
.open();
// ...
nodeRef.close();
The NodeRef.laneRef instance method can be used to create further resolvedWarpRef scopes.
`typescript`
const nodeRef = swim.nodeRef("warp://traffic.swim.services", "swim:meta:mesh");
const laneRef = nodeRef.laneRef("linkStats");
A LaneRef is a WarpRef that automatically provides its bound host, node,LaneRef
and lane URIs when opening downlinks and sending commands. instancesLaneRef
keep track of all the downlinks they directly open. When a is closed,
it automatically closes all of its open downlink views.
`typescript`
const laneRef = swim.laneRef("warp://traffic.swim.services", "swim:meta:mesh", "linkStats");
laneRef.downlink()
.onEvent((value) => console.log(value.toLike())})
.open();
// ...
laneRef.close();
A Downlink provides a virtual bidirectional stream between the client and a
lane of a remote Web Agent. WARP clients transparently multiplex all links to
Web Agents on a given host over a single WebSocket connection, and automatically
manage the network connection to each host, including reconnection and
resynchronization after a network failure. WARP clients also seamlessly handle
multicast event routing when multiple downlinks are opened to the same lane of
the same remote Web Agent.
Downlinks come in several flavors, depending on the WARP subprotocol to which
they conform. An EventDownlink observes raw WARP events, and can be used toValueDownlink
observe lanes of any kind. A synchronizes a structured valueMapDownlink
with a remote value lane. A implements the WARP map subprotocolListDownlink
to synchronize key-value state with a remote map lane. A
implements the WARP list subprotocol to to synchronize sequential list state
with a remote list lane.
Before opening, a downlink must be addressed with the hostUri, nodeUri,laneUri
and to which the link should connect. A downlink may also beprio
configured with a relative rity, a max rate, and an optional body
structure that can contain query or other link parameters to be passed to the
remote lane.
The keepLinked parameter determines whether or not a downlink should betrue
automatically reopened after a network failure; it defaults to . ThekeepSynced parameter determines whether or not a downlink should synchronizetrue
with the remote lane when opened; it defaults to for stateful lanes.
The open method is used to open a downlink after it has been configured.close
The method closes a downlink. Closing a downlink does not necessarily
close the underlying WARP link. The WARP client will keep a link open so long
as at least one downlink to a given node and lane URI remains open. This
prevents application components from stepping on each other's toes when they
link to the same lanes of the same Web Agents. This can happen, for example,
when a UI has a summary view and a detail view both display information derived
from the same remote lane. The WARP link should not be closed when a detail
view is hidden, if state updates are still required by the summary view.
Events should also not be sent twice: once for the summary view, and once for
the detail view. Neither the summary view nor the detail view should have to
know about each other. And no global event dispatcher should be required,
which could introduce consistency problems. WARP clients efficiently, and
transparently handle all of these cases on behalf of all downlinks.
The isConnected method returns true if the underlying connection to theisAuthenticated
remote host is currently open. The method returns trueisLinked
if the underlying connection to the remote host is currently authenticated.
The method returns true if the logical WARP link is currentlyisSynced
open. And the method returns true if the WARP link is currently
synchronized.
All downlinks support registering onEvent, onCommand, willLink, didLink,willSync, didSync, willUnlink, didUnlink, willConnect, didConnect,didDisconnect, didClose, and didFail callbacks.
An EventDownlink provides a raw view of a WARP link.
`typescript`
swim.downlink()
.hostUri("warp://example.com")
.nodeUri("/house")
.laneUri("power/meter")
.onEvent((body) => / ... /)
.open();
A ValueDownlink synchronizes a shared real-time value with a remote valueDownlink
lane. In addition to the standard callbacks, ValueDownlinkwillSet
supports registering and didSet callbacks to observe all changes
to downlinked state—whether remote or local.
A ValueDownlink views its state as an [Swim Structure][structure] ValuevalueForm
by default. Use the method to create a typed projection of aValueDownlink that automatically transforms its state using an SwimForm
Structure . For example, you can use Form.foString() to create aValueDownlink that coerces its state to a string; and you can also useForm.forAny() to create a ValueDownlink that coerces its state to a
plain old JavaScript value.
`typescript`
const value = swim.downlinkValue()
.hostUri("warp://example.com")
.nodeUri("/house/kitchen")
.laneUri("light")
.valueForm(swim.Form.forAny())
.didSet((value) => / ... /)
.open();
Use the ValueDownlink.get method to get the current state value. Use theValueDownlink.set method to set the current state value.
`typescript`
value.get(); // get the current local state of the downlink
value.set(newValue); // update the local and remote state of the downlink
For the most part, client code can treat a ValueDownlink like an ordinarydidSet
mutable variable; the WARP client will ensure that the downlink is continuously
made consistent with the remote lane. Using callbacks, applications
can update UI views, and other dependent components, to keep them consistent
with the shared state of the remote value lane in network real-time.
`typescript`
swim.downlinkValue()
.didSet((value) => {
// update UI view with latest value
document.getElementById("value").innerText = value;
})
A MapDownlink synchronizes a shared real-time key-value map with a remote mapDownlink
lane. In addition to the standard callbacks, MapDownlink supportswillUpdate
registering , didUpdate, willRemove, and didRemove callbacks
to observe all changes to downlinked map state—whether remote or local.
A MapDownlink views its keys and values as [Swim Structure][structure]Values by default. Use the keyForm and valueForm methods to create aMapDownlink
typed projection of a that automatically transforms its keys andForm
values using Swim Structure s.
`typescript`
const map = swim.downlinkMap()
.hostUri("warp://example.com")
.nodeUri("/house")
.laneUri("rooms")
.keyForm(swim.Form.forString())
.valueForm(swim.Form.forAny())
.didUpdate((key, value) => / ... /)
.didRemove((key) => / ... /)
.open();
MapDownlink implements the standard JavaScript Map interface. Use theMapDownlink.get method to get the value associated with a given key. Use theMapDownlink.set method to update the value associated with a key. And useMapDownlink.delete
the method to remove a key and its associated value.
`typescript`
map.get("kitchen"); // get the locally cached value associated with the key
map.set("garage", newRoom); // locally and remotely insert a new entry
For the most part, client code can treat a MapDownlink like anMap
ordinary JavaScript ; the WARP client will ensure that the downlink isdidUpdate
continuously made consistent with the remote lane. Using anddidRemove callbacks, applications can update UI collection views, and other
dependent components, to keep them consistent with the shared state of the
remote map lane in network real-time.
`typescript`
swim.downlinkMap()
.didUpdate((key, value) => {
if (hasChildElement(key)) {
// update existing UI view for key
} else {
// insert new UI view for key
}
})
.didRemove((key) => {
// remove UI view for key
})
A ListDownlink synchronizes a shared real-time list with a remote list lane.Downlink
In addition to the standard callbacks, ListDownlink supportswillUpdate
registering , didUpdate, willMove, didMove, willRemove,didRemove
and callbacks to observe all changes to downlinked list
state—whether remote or local.
A ListDownlink views its items as [Swim Structure][structure] ValuesvalueForm
by default. Use the method to create a typed projection of aListDownlink that automatically transforms its items using an SwimForm
Structure .
`typescript`
const list = swim.downlinkList()
.hostUri("warp://example.com")
.nodeUri("/house")
.laneUri("todo")
.valueForm(swim.Form.forAny())
.didUpdate((index, value) => / ... /)
.didMove((fromIndex, toIndex, value) => / ... /)
.didRemove((index) => / ... /)
.open();
ListDownlink behaves similarly to a JavaScript array. Use theListDownlink.get method to get the item at a given index. Use theListDownlink.set method to update the item at some index. And use theListDownlink.splice method to insert and remove items from the list.push
You can also , pop, shift, and unshift items, and move an
item from one index to another.
`typescript`
list.get(0); // get the first item in the list
list.set(0, "build"); // locally and remotely update an item
list.push("paint"); // locally and remotely append an item
For the most part, client code can treat a ListDownlink like an ordinarydidUpdate
JavaScript list; the WARP client will ensure that the downlink is continuously
made consistent with the remote lane. Using , didMove, anddidRemove callbacks, applications can update UI list views, and other
dependent components, to keep them consistent with the shared state of the
remote list lane in network real-time.
`typescript``
swim.downlinkList()
.didUpdate((index, value) => {
if (hasChildElement(index)) {
// update existing UI view at index
} else {
// insert new UI view at index
}
})
.didMove((fromIndex, toIndex, value)) {
// move existing UI view from old index to new index
}
.didRemove((index) => {
// remove UI view at index
})
[structure]: swim-core/@swim/structure