Join chat

Base methods for synchronization nodes. Client and server nodes are based on this module.

ParameterTypeDescription
nodeIdstringUnique current machine name.
logLLogux log instance to be synchronized.
connectionConnectionConnection to remote node.
options ?NodeOptions<H>Synchronization options.

BaseNode#authenticated

Did we finish remote node authentication.

Type: boolean.

BaseNode#connected

Is synchronization in process.

node.on('disconnect', () => {
  node.connected //=> false
})

Type: boolean.

BaseNode#connection

Connection used to communicate to remote node.

Type: Connection.

BaseNode#initializing

Promise for node data initial loadiging.

Type: Promise<void>.

BaseNode#lastReceived

Latest remote node’s log added time, which was successfully synchronized. It will be saves in log store.

Type: number.

BaseNode#lastSent

Latest current log added time, which was successfully synchronized. It will be saves in log store.

Type: number.

BaseNode#localNodeId

Unique current machine name.

console.log(node.localNodeId + ' is started')

Type: string.

BaseNode#localProtocol

Used Logux protocol.

if (tool.node.localProtocol !== 1) {
  throw new Error('Unsupported Logux protocol')
}

Type: number.

BaseNode#log

Log for synchronization.

Type: L.

BaseNode#minProtocol

Minimum version of Logux protocol, which is supported.

console.log(`You need Logux protocol ${node.minProtocol} or higher`)

Type: number.

BaseNode#options

Synchronization options.

Type: NodeOptions<H>.

BaseNode#remoteHeaders

Headers set by remote node. By default, it is an empty object.

let message = I18N_ERRORS[node.remoteHeaders.language || 'en']
node.log.add({ type: 'error', message })

Type: H | EmptyHeaders.

BaseNode#remoteNodeId

Unique name of remote machine. It is undefined until nodes handshake.

console.log('Connected to ' + node.remoteNodeId)

Type: string | undefined.

BaseNode#remoteProtocol

Remote node Logux protocol. It is undefined until nodes handshake.

if (node.remoteProtocol >= 5) {
  useNewAPI()
} else {
  useOldAPI()
}

Type: number | undefined.

BaseNode#remoteSubprotocol

Remote node’s application subprotocol version in SemVer format.

It is undefined until nodes handshake. If remote node will not send on handshake its subprotocol, it will be set to 0.0.0.

if (semver.satisfies(node.remoteSubprotocol, '>= 5.0.0') {
  useNewAPI()
} else {
  useOldAPI()
}

Type: string | undefined.

BaseNode#state

Current synchronization state.

  • disconnected: no connection.
  • connecting: connection was started and we wait for node answer.
  • sending: new actions was sent, waiting for answer.
  • synchronized: all actions was synchronized and we keep connection.
node.on('state', () => {
  if (node.state === 'sending') {
    console.log('Do not close browser')
  }
})

Type: 'disconnected' | 'connecting' | 'sending' | 'synchronized'.

BaseNode#timeFix

Time difference between nodes.

Type: number.

BaseNode#catch(listener)

Disable throwing a error on error message and create error listener.

node.catch(error => {
  console.error(error)
})
ParameterTypeDescription
listener(error: LoguxError) => voidThe error listener.

Returns Unsubscribe. Unbind listener from event.

BaseNode#destroy()

Shut down the connection and unsubscribe from log events.

connection.on('disconnect', () => {
  server.destroy()
})

BaseNode#on(event, listener)

Subscribe for synchronization events. It implements nanoevents API. Supported events:

  • state: synchronization state was changed.
  • connect: custom check before node authentication. You can throw a LoguxError to send error to remote node.
  • error: synchronization error was raised.
  • clientError: when error was sent to remote node.
  • debug: when debug information received from remote node.
  • headers: headers was receive from remote node.
node.on('clientError', error => {
  logError(error)
})
ParameterTypeDescription
event'state' | 'connect' | 'debug' | 'headers'Event name.
listener() => voidThe listener function.
ParameterType
event'error' | 'clientError'
listener(error: Error) => void
ParameterType
event'debug'
listener(type: 'error', data: string) => void
ParameterType
event'headers'
listener(headers: H) => void

Returns Unsubscribe. Unbind listener from event.

BaseNode#setLocalHeaders(headers)

Set headers for current node.

if (navigator) {
  node.setLocalHeaders({ language: navigator.language })
}
node.connection.connect()
ParameterTypeDescription
headersobjectThe data object will be set as headers for current node.

BaseNode#waitFor(state)

Return Promise until sync will have specific state.

If current state is correct, method will return resolved Promise.

await node.waitFor('synchronized')
console.log('Everything is synchronized')
ParameterTypeDescription
state'disconnected' | 'connecting' | 'sending' | 'synchronized'The expected synchronization state value.

Returns Promise<void>. Promise until specific state.


Base class for browser API to be extended in CrossTabClient.

Because this class could have conflicts between different browser tab, you should use it only if you are really sure, that application will not be run in different tab (for instance, if you are developing a kiosk app).

import { Client } from '@logux/client'

const userId = document.querySelector('meta[name=user]').content
const token = document.querySelector('meta[name=token]').content

const client = new Client({
  credentials: token,
  subprotocol: '1.0.0',
  server: 'wss://example.com:1337',
  userId: userId
})
client.start()
ParameterTypeDescription
optsClientOptionsClient options.

Client#clientId

Unique permanent client ID. Can be used to track this machine.

Type: string.

Client#log

Client events log.

client.log.add(action)

Type: L.

Client#node

Node instance to synchronize logs.

if (client.node.state === 'synchronized')

Type: ClientNode<H, L>.

Client#nodeId

Unique Logux node ID.

console.log('Client ID: ', client.nodeId)

Type: string.

Client#options

Client options.

console.log('Connecting to ' + client.options.server)

Type: ClientOptions.

Client#tabId

Unique tab ID. Can be used to add an action to the specific tab.

client.log.add(action, { tab: client.tabId })

Type: TabID.

Client#changeUser(userId, token?)

Disconnect from the server, update user, and connect again with new credentials.

onAuth(async (userId, token) => {
  showLoader()
  client.changeUser(userId, token)
  await client.node.waitFor('synchronized')
  hideLoader()
})

You need manually chang user ID in all browser tabs.

ParameterTypeDescription
userIdstringThe new user ID.
token ?stringCredentials for new user.

Client#clean()

Clear stored data. Removes action log from IndexedDB if you used it.

signout.addEventListener('click', () => {
  client.clean()
})

Returns Promise<void>. Promise when all data will be removed.

Client#destroy()

Disconnect and stop synchronization.

shutdown.addEventListener('click', () => {
  client.destroy()
})

Client#on(event, listener)

Subscribe for synchronization events. It implements Nano Events API. Supported events:

  • preadd: action is going to be added (in current tab).
  • add: action has been added to log (by any tab).
  • clean: action has been removed from log (by any tab).
  • user: user ID was changed.
client.on('add', (action, meta) => {
  dispatch(action)
})
ParameterTypeDescription
event'preadd' | 'add' | 'clean'The event name.
listenerClientActionListenerThe listener function.
ParameterType
event'user'
listener(userId: string) => void

Returns Unsubscribe. Unbind listener from event.

Client#start()

Connect to server and reconnect on any connection problem.

client.start()

Extends BaseNode.

Client node in synchronization pair.

Instead of server node, it initializes synchronization and sends connect message.

import { ClientNode } from '@logux/core'
const connection = new BrowserConnection(url)
const node = new ClientNode(nodeId, log, connection)
ParameterTypeDescription
nodeIdstringUnique current machine name.
logLLogux log instance to be synchronized.
connectionConnectionConnection to remote node.
options ?NodeOptions<H>Synchronization options.

ClientNode#authenticated

Did we finish remote node authentication.

Type: boolean.

ClientNode#connected

Is synchronization in process.

node.on('disconnect', () => {
  node.connected //=> false
})

Type: boolean.

ClientNode#connection

Connection used to communicate to remote node.

Type: Connection.

ClientNode#initializing

Promise for node data initial loadiging.

Type: Promise<void>.

ClientNode#lastReceived

Latest remote node’s log added time, which was successfully synchronized. It will be saves in log store.

Type: number.

ClientNode#lastSent

Latest current log added time, which was successfully synchronized. It will be saves in log store.

Type: number.

ClientNode#localNodeId

Unique current machine name.

console.log(node.localNodeId + ' is started')

Type: string.

ClientNode#localProtocol

Used Logux protocol.

if (tool.node.localProtocol !== 1) {
  throw new Error('Unsupported Logux protocol')
}

Type: number.

ClientNode#log

Log for synchronization.

Type: L.

ClientNode#minProtocol

Minimum version of Logux protocol, which is supported.

console.log(`You need Logux protocol ${node.minProtocol} or higher`)

Type: number.

ClientNode#options

Synchronization options.

Type: NodeOptions<H>.

ClientNode#remoteHeaders

Headers set by remote node. By default, it is an empty object.

let message = I18N_ERRORS[node.remoteHeaders.language || 'en']
node.log.add({ type: 'error', message })

Type: H | EmptyHeaders.

ClientNode#remoteNodeId

Unique name of remote machine. It is undefined until nodes handshake.

console.log('Connected to ' + node.remoteNodeId)

Type: string | undefined.

ClientNode#remoteProtocol

Remote node Logux protocol. It is undefined until nodes handshake.

if (node.remoteProtocol >= 5) {
  useNewAPI()
} else {
  useOldAPI()
}

Type: number | undefined.

ClientNode#remoteSubprotocol

Remote node’s application subprotocol version in SemVer format.

It is undefined until nodes handshake. If remote node will not send on handshake its subprotocol, it will be set to 0.0.0.

if (semver.satisfies(node.remoteSubprotocol, '>= 5.0.0') {
  useNewAPI()
} else {
  useOldAPI()
}

Type: string | undefined.

ClientNode#state

Current synchronization state.

  • disconnected: no connection.
  • connecting: connection was started and we wait for node answer.
  • sending: new actions was sent, waiting for answer.
  • synchronized: all actions was synchronized and we keep connection.
node.on('state', () => {
  if (node.state === 'sending') {
    console.log('Do not close browser')
  }
})

Type: 'disconnected' | 'connecting' | 'sending' | 'synchronized'.

ClientNode#timeFix

Time difference between nodes.

Type: number.

ClientNode#catch(listener)

Disable throwing a error on error message and create error listener.

node.catch(error => {
  console.error(error)
})
ParameterTypeDescription
listener(error: LoguxError) => voidThe error listener.

Returns Unsubscribe. Unbind listener from event.

ClientNode#destroy()

Shut down the connection and unsubscribe from log events.

connection.on('disconnect', () => {
  server.destroy()
})

ClientNode#on(event, listener)

Subscribe for synchronization events. It implements nanoevents API. Supported events:

  • state: synchronization state was changed.
  • connect: custom check before node authentication. You can throw a LoguxError to send error to remote node.
  • error: synchronization error was raised.
  • clientError: when error was sent to remote node.
  • debug: when debug information received from remote node.
  • headers: headers was receive from remote node.
node.on('clientError', error => {
  logError(error)
})
ParameterTypeDescription
event'state' | 'connect' | 'debug' | 'headers'Event name.
listener() => voidThe listener function.
ParameterType
event'error' | 'clientError'
listener(error: Error) => void
ParameterType
event'debug'
listener(type: 'error', data: string) => void
ParameterType
event'headers'
listener(headers: H) => void

Returns Unsubscribe. Unbind listener from event.

ClientNode#setLocalHeaders(headers)

Set headers for current node.

if (navigator) {
  node.setLocalHeaders({ language: navigator.language })
}
node.connection.connect()
ParameterTypeDescription
headersobjectThe data object will be set as headers for current node.

ClientNode#waitFor(state)

Return Promise until sync will have specific state.

If current state is correct, method will return resolved Promise.

await node.waitFor('synchronized')
console.log('Everything is synchronized')
ParameterTypeDescription
state'disconnected' | 'connecting' | 'sending' | 'synchronized'The expected synchronization state value.

Returns Promise<void>. Promise until specific state.


Abstract interface for connection to synchronize logs over it. For example, WebSocket or Loopback.

Connection#connected

Is connection is enabled.

Type: boolean.

Connection#destroy

Optional method to disconnect and unbind all even listeners.

Type: () => void.

Connection#connect()

Start connection. Connection should be in disconnected state from the beginning and start connection only on this method call.

This method could be called again if connection moved to disconnected state.

Returns Promise<void>. Promise until connection will be established.

Connection#disconnect(reason?)

Finish current connection.

ParameterTypeDescription
reason ?'error' | 'timeout' | 'destroy'Disconnection reason.

Connection#on(event, listener)

Subscribe for connection events. It implements nanoevents API. Supported events:

  • connecting: connection establishing was started.
  • connect: connection was established by any side.
  • disconnect: connection was closed by any side.
  • message: message was receive from remote node.
  • error: error during connection, sending or receiving.
ParameterTypeDescription
event'connecting' | 'connect' | 'disconnect'Event name.
listener() => voidEvent listener.
ParameterType
event'error'
listener(error: Error) => void
ParameterType
event'message'
listener(msg: Message) => void
ParameterType
event'disconnect'
listener(reason: string) => void

Returns Unsubscribe. Unbind listener from event.

Connection#send(message)

Send message to connection.

ParameterTypeDescription
messageMessageThe message to be sent.

Extends Client.

Low-level browser API for Logux.

Instead of Client, this class prevents conflicts between Logux instances in different tabs on single browser.

import { CrossTabClient } from '@logux/client'

const userId = document.querySelector('meta[name=user]').content
const token = document.querySelector('meta[name=token]').content

const client = new CrossTabClient({
  subprotocol: '1.0.0',
  server: 'wss://example.com:1337',
  userId,
  token
})
client.start()
ParameterTypeDescription
optsClientOptionsClient options.

CrossTabClient#clientId

Unique permanent client ID. Can be used to track this machine.

Type: string.

CrossTabClient#connected

Is leader tab connected to server.

Type: boolean.

CrossTabClient#log

Client events log.

client.log.add(action)

Type: L.

CrossTabClient#node

Node instance to synchronize logs.

if (client.node.state === 'synchronized')

Type: ClientNode<H, L>.

CrossTabClient#nodeId

Unique Logux node ID.

console.log('Client ID: ', client.nodeId)

Type: string.

CrossTabClient#options

Client options.

console.log('Connecting to ' + client.options.server)

Type: ClientOptions.

CrossTabClient#role

Current tab role. Only leader tab connects to server. followers just listen to events from leader.

client.on('role', () => {
  console.log('Tab role:', client.role)
})

Type: 'leader' | 'candidate' | 'follower'.

CrossTabClient#state

Leader tab synchronization state. It can differs from client.node.state (because only the leader tab keeps connection).

client.on('state', () => {
  if (client.state === 'disconnected' && client.state === 'sending') {
    showCloseWarning()
  }
})

Type: 'disconnected' | 'connecting' | 'sending' | 'synchronized'.

CrossTabClient#tabId

Unique tab ID. Can be used to add an action to the specific tab.

client.log.add(action, { tab: client.tabId })

Type: TabID.

CrossTabClient#changeUser(userId, token?)

Disconnect from the server, update user, and connect again with new credentials.

onAuth(async (userId, token) => {
  showLoader()
  client.changeUser(userId, token)
  await client.node.waitFor('synchronized')
  hideLoader()
})

You need manually chang user ID in all browser tabs.

ParameterTypeDescription
userIdstringThe new user ID.
token ?stringCredentials for new user.

CrossTabClient#clean()

Clear stored data. Removes action log from IndexedDB if you used it.

signout.addEventListener('click', () => {
  client.clean()
})

Returns Promise<void>. Promise when all data will be removed.

CrossTabClient#destroy()

Disconnect and stop synchronization.

shutdown.addEventListener('click', () => {
  client.destroy()
})

CrossTabClient#on(event, listener)

Subscribe for synchronization events. It implements nanoevents API. Supported events:

  • preadd: action is going to be added (in current tab).
  • add: action has been added to log (by any tab).
  • clean: action has been removed from log (by any tab).
  • role: tab role has been changed.
  • state: leader tab synchronization state has been changed.
  • user: user ID was changed.
client.on('add', (action, meta) => {
  dispatch(action)
})
ParameterTypeDescription
event'role' | 'state'The event name.
listener() => voidThe listener function.
ParameterType
event'user'
listener(userId: string) => void
ParameterType
event'preadd' | 'add' | 'clean'
listenerClientActionListener

Returns Unsubscribe. Unbind listener from event.

CrossTabClient#start()

Connect to server and reconnect on any connection problem.

client.start()

CrossTabClient#waitFor(state)

Wait for specific state of the leader tab.

await client.waitFor('synchronized')
hideLoader()
ParameterTypeDescription
state'disconnected' | 'connecting' | 'sending' | 'synchronized'State name

Returns Promise<void>.


Extends LogStore.

IndexedDB store for Logux log.

import { IndexedStore } from '@logux/client'
const client = new CrossTabClient({
  …,
  store: new IndexedStore()
})
import IndexedStore from '@logux/client/indexed-store'
const createStore = createLoguxCreator({
  …,
  store: new IndexedStore()
})
ParameterTypeDescription
name ?stringDatabase name to run multiple Logux instances on same web page.

IndexedStore#name

Database name.

Type: string.

IndexedStore#add(action, meta)

Add action to store. Action always will have type property.

ParameterTypeDescription
actionActionThe action to add.
metaMetaAction’s metadata.

Returns Promise<Meta | false>. Promise with meta for new action or false if action with same meta.id was already in store.

IndexedStore#byId(id)

Return action by action ID.

ParameterTypeDescription
idIDAction ID.

Returns Promise <[Action, Meta] | [null, null]>. Promise with array of action and metadata.

IndexedStore#changeMeta(id, diff)

Change action metadata.

ParameterTypeDescription
idIDAction ID.
diffPartial<Meta>Object with values to change in action metadata.

Returns Promise<boolean>. Promise with true if metadata was changed or false on unknown ID.

IndexedStore#clean()

Remove all data from the store.

Returns Promise<void>. Promise when cleaning will be finished.

IndexedStore#get(opts?)

Return a Promise with first page. Page object has entries property with part of actions and next property with function to load next page. If it was a last page, next property should be empty.

This tricky API is used, because log could be very big. So we need pagination to keep them in memory.

ParameterTypeDescription
opts ?GetOptionsQuery options.

Returns Promise<Page>. Promise with first page.

IndexedStore#getLastAdded()

Return biggest added number in store. All actions in this log have less or same added time.

Returns Promise<number>. Promise with biggest added number.

IndexedStore#getLastSynced()

Get added values for latest synchronized received/sent events.

Returns Promise<LastSynced>. Promise with added values

IndexedStore#remove(id)

Remove action from store.

ParameterTypeDescription
idIDAction ID.

Returns Promise<[Action, Meta] | false>. Promise with entry if action was in store.

IndexedStore#removeReason(reason, criteria, callback)

Remove reason from action’s metadata and remove actions without reasons.

ParameterTypeDescription
reasonstringThe reason name.
criteriaCriteriaCriteria to select action for reason removing.
callbackActionListenerCallback for every removed action.

Returns Promise<void>. Promise when cleaning will be finished.

IndexedStore#setLastSynced(values)

Set added value for latest synchronized received or/and sent events.

ParameterTypeDescription
valuesLastSyncedObject with latest sent or received values.

Returns Promise<void>. Promise when values will be saved to store.


Extends Connection.

LocalConnection#connected

Is connection is enabled.

Type: boolean.

LocalConnection#destroy

Optional method to disconnect and unbind all even listeners.

Type: () => void.

LocalConnection#connect()

Start connection. Connection should be in disconnected state from the beginning and start connection only on this method call.

This method could be called again if connection moved to disconnected state.

Returns Promise<void>. Promise until connection will be established.

LocalConnection#disconnect(reason?)

Finish current connection.

ParameterTypeDescription
reason ?'error' | 'timeout' | 'destroy'Disconnection reason.

LocalConnection#on(event, listener)

Subscribe for connection events. It implements nanoevents API. Supported events:

  • connecting: connection establishing was started.
  • connect: connection was established by any side.
  • disconnect: connection was closed by any side.
  • message: message was receive from remote node.
  • error: error during connection, sending or receiving.
ParameterTypeDescription
event'connecting' | 'connect' | 'disconnect'Event name.
listener() => voidEvent listener.
ParameterType
event'error'
listener(error: Error) => void
ParameterType
event'message'
listener(msg: Message) => void
ParameterType
event'disconnect'
listener(reason: string) => void

Returns Unsubscribe. Unbind listener from event.

LocalConnection#other()

Returns LocalConnection.

LocalConnection#send(message)

Send message to connection.

ParameterTypeDescription
messageMessageThe message to be sent.

Two paired loopback connections.

import { LocalPair, ClientNode, ServerNode } from '@logux/core'
const pair = new LocalPair()
const client = new ClientNode('client', log1, pair.left)
const server = new ServerNode('server', log2, pair.right)
ParameterTypeDescription
delay ?numberDelay for connection and send events. Default is 1.

LocalPair#delay

Delay for connection and send events to emulate real connection latency.

Type: number.

LocalPair#left

First connection. Will be connected to right one after connect().

new ClientNode('client, log1, pair.left)

Type: LocalConnection.

LocalPair#right

Second connection. Will be connected to right one after connect().

new ServerNode('server, log2, pair.right)

Type: LocalConnection.


Stores actions with time marks. Log is main idea in Logux. In most end-user tools you will work with log and should know log API.

import Log from '@logux/core'
const log = new Log({
  store: new MemoryStore(),
  nodeId: 'client:134'
})

log.on('add', beeper)
log.add({ type: 'beep' })
ParameterTypeDescription
optsLogOptions<S>Log options.

Log#nodeId

Unique node ID. It is used in action IDs.

Type: string.

Log#store

Log store.

Type: S.

Log#add(action, meta?)

Add action to log.

It will set id, time (if they was missed) and added property to meta and call all listeners.

removeButton.addEventListener('click', () => {
  log.add({ type: 'users:remove', user: id })
})
ParameterTypeDescription
actionActionThe new action.
meta ?Partial<ClientMeta>Open structure for action metadata.

Returns Promise<ClientMeta | false>. Promise with meta if action was added to log or false if action was already in log.

Log#byId(id)

Does log already has action with this ID.

if (action.type === 'logux/undo') {
  const [undidAction, undidMeta] = await log.byId(action.id)
  log.changeMeta(meta.id, { reasons: undidMeta.reasons })
}
ParameterTypeDescription
idIDAction ID.

Returns Promise <[Action, ClientMeta] | [null, null]>. Promise with array of action and metadata.

Log#changeMeta(id, diff)

Change action metadata. You will remove action by setting reasons: [].

await process(action)
log.changeMeta(action, { status: 'processed' })
ParameterTypeDescription
idIDAction ID.
diffPartial<ClientMeta>Object with values to change in action metadata.

Returns Promise<boolean>. Promise with true if metadata was changed or false on unknown ID.

Log#each(callback)

Iterates through all actions, from last to first.

Return false from callback if you want to stop iteration.

log.each((action, meta) => {
  if (compareTime(meta.id, lastBeep) <= 0) {
    return false;
  } else if (action.type === 'beep') {
    beep()
    lastBeep = meta.id
    return false;
  }
})
ParameterTypeDescription
callbackActionIteratorFunction will be executed on every action.
ParameterTypeDescription
optsGetOptionsIterator options.
callbackActionIteratorFunction will be executed on every action.
ParameterType
callbackActionIterator

Returns Promise<void>. When iteration will be finished by iterator or end of actions.

Log#generateId()

Generate next unique action ID.

const id = log.generateId()

Returns ID. Unique ID for action.

Log#on(event, listener)

Subscribe for log events. It implements nanoevents API. Supported events:

  • preadd: when somebody try to add action to log. It fires before ID check. The best place to add reason.
  • add: when new action was added to log.
  • clean: when action was cleaned from store.
const unbind = log.on('add', (action, meta) => {
  if (action.type === 'beep') beep()
})
function disableBeeps () {
  unbind()
}
ParameterTypeDescription
event'preadd' | 'add' | 'clean'The event name.
listenerActionListenerThe listener function.

Returns Unsubscribe. Unbind listener from event.

Log#removeReason(reason, criteria?)

Remove reason tag from action’s metadata and remove actions without reason from log.

onSync(lastSent) {
  log.removeReason('unsynchronized', { maxAdded: lastSent })
}
ParameterTypeDescription
reasonstringThe reason name.
criteria ?CriteriaCriteria to select action for reason removing.

Returns Promise<void>. Promise when cleaning will be finished.


Every Store class should provide 8 standard methods.

ParameterTypeDescription
actionActionThe action to add.
metaMetaAction’s metadata.

LogStore#add(action, meta)

Add action to store. Action always will have type property.

ParameterTypeDescription
actionActionThe action to add.
metaMetaAction’s metadata.

Returns Promise<Meta | false>. Promise with meta for new action or false if action with same meta.id was already in store.

LogStore#byId(id)

Return action by action ID.

ParameterTypeDescription
idIDAction ID.

Returns Promise <[Action, Meta] | [null, null]>. Promise with array of action and metadata.

LogStore#changeMeta(id, diff)

Change action metadata.

ParameterTypeDescription
idIDAction ID.
diffPartial<Meta>Object with values to change in action metadata.

Returns Promise<boolean>. Promise with true if metadata was changed or false on unknown ID.

LogStore#clean()

Remove all data from the store.

Returns Promise<void>. Promise when cleaning will be finished.

LogStore#get(opts?)

Return a Promise with first page. Page object has entries property with part of actions and next property with function to load next page. If it was a last page, next property should be empty.

This tricky API is used, because log could be very big. So we need pagination to keep them in memory.

ParameterTypeDescription
opts ?GetOptionsQuery options.

Returns Promise<Page>. Promise with first page.

LogStore#getLastAdded()

Return biggest added number in store. All actions in this log have less or same added time.

Returns Promise<number>. Promise with biggest added number.

LogStore#getLastSynced()

Get added values for latest synchronized received/sent events.

Returns Promise<LastSynced>. Promise with added values

LogStore#remove(id)

Remove action from store.

ParameterTypeDescription
idIDAction ID.

Returns Promise<[Action, Meta] | false>. Promise with entry if action was in store.

LogStore#removeReason(reason, criteria, callback)

Remove reason from action’s metadata and remove actions without reasons.

ParameterTypeDescription
reasonstringThe reason name.
criteriaCriteriaCriteria to select action for reason removing.
callbackActionListenerCallback for every removed action.

Returns Promise<void>. Promise when cleaning will be finished.

LogStore#setLastSynced(values)

Set added value for latest synchronized received or/and sent events.

ParameterTypeDescription
valuesLastSyncedObject with latest sent or received values.

Returns Promise<void>. Promise when values will be saved to store.


Extends Error.

Logux error in logs synchronization.

if (error.name === 'LoguxError') {
  console.log('Server throws: ' + error.description)
}
ParameterTypeDescription
typeTThe error code.
options ?LoguxErrorOptions[T]The error option.
received ?booleanWas error received from remote node.

LoguxError.description(type, options?)

Return a error description by it code.

ParameterTypeDescription
typeKThe error code.
options ?LoguxErrorOptions[K]The errors options depends on error code.

Returns string.

LoguxError#description

Human-readable error description.

console.log('Server throws: ' + error.description)

Type: string.

LoguxError#message

Full text of error to print in debug message.

Type: string.

LoguxError#name

Always equal to LoguxError. The best way to check error class.

if (error.name === 'LoguxError') {

Type: 'LoguxError'.

LoguxError#options

Error options depends on error type.

if (error.type === 'timeout') {
  console.error('A timeout was reached (' + error.options + ' ms)')
}

Type: LoguxErrorOptions[T].

LoguxError#received

Was error received from remote client.

Type: boolean.

LoguxError#stack

Calls which cause the error.

Type: string.

LoguxError#type

The error code.

if (error.type === 'timeout') {
  fixNetwork()
}

Type: T.


LoguxReduxStore#client

Logux synchronization client.

Type: CrossTabClient<H, L>.

LoguxReduxStore#dispatch

Add action to log with Redux compatible API.

Type: LoguxDispatch<A>.

LoguxReduxStore#initialize

Promise until loading the state from IndexedDB.

Type: Promise<void>.

LoguxReduxStore#log

The Logux log.

Type: L.

LoguxReduxStore#[Symbol.observable]()

Returns Observable<S>.

LoguxReduxStore#getState()

Reads the state tree managed by the store.

Returns S. The current state tree of your application.

LoguxReduxStore#on(event, listener)

Subscribe for store events. Supported events:

  • change: when store was changed by action.
store.on('change', (state, prevState, action, meta) => {
  console.log(state, prevState, action, meta)
})
ParameterTypeDescription
event'change'The event name.
listenerReduxStateListener<S, A>The listener function.

Returns Unsubscribe. Unbind listener from event.

LoguxReduxStore#replaceReducer(nextReducer)

Replaces the reducer currently used by the store to calculate the state.

ParameterTypeDescription
nextReducerReducer<S, A>The reducer for the store to use instead.

LoguxReduxStore#subscribe(listener)

Adds a change listener.

ParameterTypeDescription
listener() => voidA callback to be invoked on every dispatch.

Returns Unsubscribe. A function to remove this change listener.


Extends LogStore.

Simple memory-based log store.

It is good for tests, but not for server or client usage, because it store all data in memory and will lose log on exit.

import { MemoryStore } from '@logux/core'

var log = new Log({
  nodeId: 'server',
  store: new MemoryStore()
})

MemoryStore#entries

Actions in the store.

Type: [Action, Meta][].

MemoryStore#add(action, meta)

Add action to store. Action always will have type property.

ParameterTypeDescription
actionActionThe action to add.
metaMetaAction’s metadata.

Returns Promise<Meta | false>. Promise with meta for new action or false if action with same meta.id was already in store.

MemoryStore#byId(id)

Return action by action ID.

ParameterTypeDescription
idIDAction ID.

Returns Promise <[Action, Meta] | [null, null]>. Promise with array of action and metadata.

MemoryStore#changeMeta(id, diff)

Change action metadata.

ParameterTypeDescription
idIDAction ID.
diffPartial<Meta>Object with values to change in action metadata.

Returns Promise<boolean>. Promise with true if metadata was changed or false on unknown ID.

MemoryStore#clean()

Remove all data from the store.

Returns Promise<void>. Promise when cleaning will be finished.

MemoryStore#get(opts?)

Return a Promise with first page. Page object has entries property with part of actions and next property with function to load next page. If it was a last page, next property should be empty.

This tricky API is used, because log could be very big. So we need pagination to keep them in memory.

ParameterTypeDescription
opts ?GetOptionsQuery options.

Returns Promise<Page>. Promise with first page.

MemoryStore#getLastAdded()

Return biggest added number in store. All actions in this log have less or same added time.

Returns Promise<number>. Promise with biggest added number.

MemoryStore#getLastSynced()

Get added values for latest synchronized received/sent events.

Returns Promise<LastSynced>. Promise with added values

MemoryStore#remove(id)

Remove action from store.

ParameterTypeDescription
idIDAction ID.

Returns Promise<[Action, Meta] | false>. Promise with entry if action was in store.

MemoryStore#removeReason(reason, criteria, callback)

Remove reason from action’s metadata and remove actions without reasons.

ParameterTypeDescription
reasonstringThe reason name.
criteriaCriteriaCriteria to select action for reason removing.
callbackActionListenerCallback for every removed action.

Returns Promise<void>. Promise when cleaning will be finished.

MemoryStore#setLastSynced(values)

Set added value for latest synchronized received or/and sent events.

ParameterTypeDescription
valuesLastSyncedObject with latest sent or received values.

Returns Promise<void>. Promise when values will be saved to store.


Extends Connection.

Wrapper for Connection for reconnecting it on every disconnect.

import { ClientNode, Reconnect } from '@logux/core'
const recon = new Reconnect(connection)
new ClientNode(nodeId, log, recon, options)
ParameterTypeDescription
connectionConnectionThe connection to be reconnectable.
options ?ReconnectOptionsReconnection options.

Reconnect#attempts

Fails attempts since the last connected state.

Type: number.

Reconnect#connected

Is connection is enabled.

Type: boolean.

Reconnect#connecting

Are we in the middle of connecting.

Type: boolean.

Reconnect#connection

Wrapped connection.

Type: Connection.

Reconnect#destroy

Unbind all listeners and disconnect. Use it if you will not need this class anymore.

Type: () => void.

Reconnect#options

Reconnection options.

Type: ReconnectOptions.

Reconnect#reconnecting

Should we reconnect connection on next connection break. Next connect call will set to true.

function lastTry () {
  recon.reconnecting = false
}

Type: boolean.

Reconnect#connect()

Start connection. Connection should be in disconnected state from the beginning and start connection only on this method call.

This method could be called again if connection moved to disconnected state.

Returns Promise<void>. Promise until connection will be established.

Reconnect#disconnect(reason?)

Finish current connection.

ParameterTypeDescription
reason ?'error' | 'timeout' | 'destroy'Disconnection reason.

Reconnect#on(event, listener)

Subscribe for connection events. It implements nanoevents API. Supported events:

  • connecting: connection establishing was started.
  • connect: connection was established by any side.
  • disconnect: connection was closed by any side.
  • message: message was receive from remote node.
  • error: error during connection, sending or receiving.
ParameterTypeDescription
event'connecting' | 'connect' | 'disconnect'Event name.
listener() => voidEvent listener.
ParameterType
event'error'
listener(error: Error) => void
ParameterType
event'message'
listener(msg: Message) => void
ParameterType
event'disconnect'
listener(reason: string) => void

Returns Unsubscribe. Unbind listener from event.

Reconnect#send(message)

Send message to connection.

ParameterTypeDescription
messageMessageThe message to be sent.

Extends Log.

Log to be used in tests. It already has memory store, node ID, and special test timer.

Use TestTime to create test log.

import { TestTime } from '@logux/core'

it('tests log', () => {
  const log = TestTime.getLog()
})

it('tests 2 logs', () => {
  const time = new TestTime()
  const log1 = time.nextLog()
  const log2 = time.nextLog()
})

TestLog#nodeId

Unique node ID. It is used in action IDs.

Type: string.

TestLog#store

Log store.

Type: LogStore.

TestLog#actions()

Return all action (without metadata) inside log, sorted by created time.

This shortcut works only with MemoryStore.

expect(log.action).toEqual([
  { type: 'A' }
])

Returns Action[].

TestLog#add(action, meta?)

Add action to log.

It will set id, time (if they was missed) and added property to meta and call all listeners.

removeButton.addEventListener('click', () => {
  log.add({ type: 'users:remove', user: id })
})
ParameterTypeDescription
actionActionThe new action.
meta ?Partial<ClientMeta>Open structure for action metadata.

Returns Promise<ClientMeta | false>. Promise with meta if action was added to log or false if action was already in log.

TestLog#byId(id)

Does log already has action with this ID.

if (action.type === 'logux/undo') {
  const [undidAction, undidMeta] = await log.byId(action.id)
  log.changeMeta(meta.id, { reasons: undidMeta.reasons })
}
ParameterTypeDescription
idIDAction ID.

Returns Promise <[Action, ClientMeta] | [null, null]>. Promise with array of action and metadata.

TestLog#changeMeta(id, diff)

Change action metadata. You will remove action by setting reasons: [].

await process(action)
log.changeMeta(action, { status: 'processed' })
ParameterTypeDescription
idIDAction ID.
diffPartial<ClientMeta>Object with values to change in action metadata.

Returns Promise<boolean>. Promise with true if metadata was changed or false on unknown ID.

TestLog#each(callback)

Iterates through all actions, from last to first.

Return false from callback if you want to stop iteration.

log.each((action, meta) => {
  if (compareTime(meta.id, lastBeep) <= 0) {
    return false;
  } else if (action.type === 'beep') {
    beep()
    lastBeep = meta.id
    return false;
  }
})
ParameterTypeDescription
callbackActionIteratorFunction will be executed on every action.
ParameterTypeDescription
optsGetOptionsIterator options.
callbackActionIteratorFunction will be executed on every action.
ParameterType
callbackActionIterator

Returns Promise<void>. When iteration will be finished by iterator or end of actions.

TestLog#entries()

Return all entries (with metadata) inside log, sorted by created time.

This shortcut works only with MemoryStore.

expect(log.action).toEqual([
  [{ type: 'A' }, { id: '1 test1 0', time: 1, added: 1, reasons: ['t'] }]
])

Returns [Action, Meta][].

TestLog#generateId()

Generate next unique action ID.

const id = log.generateId()

Returns ID. Unique ID for action.

TestLog#on(event, listener)

Subscribe for log events. It implements nanoevents API. Supported events:

  • preadd: when somebody try to add action to log. It fires before ID check. The best place to add reason.
  • add: when new action was added to log.
  • clean: when action was cleaned from store.
const unbind = log.on('add', (action, meta) => {
  if (action.type === 'beep') beep()
})
function disableBeeps () {
  unbind()
}
ParameterTypeDescription
event'preadd' | 'add' | 'clean'The event name.
listenerActionListenerThe listener function.

Returns Unsubscribe. Unbind listener from event.

TestLog#removeReason(reason, criteria?)

Remove reason tag from action’s metadata and remove actions without reason from log.

onSync(lastSent) {
  log.removeReason('unsynchronized', { maxAdded: lastSent })
}
ParameterTypeDescription
reasonstringThe reason name.
criteria ?CriteriaCriteria to select action for reason removing.

Returns Promise<void>. Promise when cleaning will be finished.


Extends LocalPair.

Two paired loopback connections with events tracking to be used in Logux tests.

import { TestPair } from '@logux/core'
it('tracks events', async () => {
  const pair = new TestPair()
  const client = new ClientNode(pair.right)
  await pair.left.connect()
  expect(pair.leftEvents).toEqual('connect')
  await pair.left.send(msg)
  expect(pair.leftSent).toEqual([msg])
})
ParameterTypeDescription
delay ?numberDelay for connection and send events. Default is 1.

TestPair#delay

Delay for connection and send events to emulate real connection latency.

Type: number.

TestPair#left

First connection. Will be connected to right one after connect().

new ClientNode('client, log1, pair.left)

Type: LocalConnection.

TestPair#leftEvents

Emitted events from left connection.

await pair.left.connect()
pair.leftEvents //=> [['connect']]

Type: string[][].

TestPair#leftNode

Node instance used in this test, connected with left.

function createTest () {
  test = new TestPair()
  test.leftNode = ClientNode('client', log, test.left)
  return test
}

Type: BaseNode<{ }, TestLog>.

TestPair#leftSent

Sent messages from left connection.

await pair.left.send(msg)
pair.leftSent //=> [msg]

Type: Message[].

TestPair#right

Second connection. Will be connected to right one after connect().

new ServerNode('server, log2, pair.right)

Type: LocalConnection.

TestPair#rightEvents

Emitted events from right connection.

await pair.right.connect()
pair.rightEvents //=> [['connect']]

Type: string[][].

TestPair#rightNode

Node instance used in this test, connected with right.

function createTest () {
  test = new TestPair()
  test.rightNode = ServerNode('client', log, test.right)
  return test
}

Type: BaseNode<{ }, TestLog>.

TestPair#rightSent

Sent messages from right connection.

await pair.right.send(msg)
pair.rightSent //=> [msg]

Type: Message[].

TestPair#clear()

Clear all connections events and messages to test only last events.

await client.connection.connect()
pair.clear() // Remove all connecting messages
await client.log.add({ type: 'a' })
expect(pair.leftSent).toEqual([
  ['sync', …]
])

TestPair#wait(receiver?)

Return Promise until next event.

pair.left.send(['test'])
await pair.wait('left')
pair.leftSend //=> [['test']]
ParameterTypeDescription
receiver ?'left' | 'right'Wait for specific receiver event.

Returns Promise<void>. Promise until next event.


Creates special logs for test purposes.

Real logs use real time in actions ID, so log content will be different on every test execution.

To fix it Logux has special logs for tests with simple sequence timer. All logs from one test should share same time. This is why you should use log creator to share time between all logs in one test.

import { TestTime } from '@logux/core'

it('tests log', () => {
  const log = TestTime.getLog()
})

it('tests 2 logs', () => {
  const time = new TestTime()
  const log1 = time.nextLog()
  const log2 = time.nextLog()
})

TestTime.getLog(opts?)

Shortcut to create time and generate single log. Use it only if you need one log in test.

it('tests log', () => {
  const log = TestTime.getLog()
})
ParameterTypeDescription
opts ?TestLogOptionsLog options.

Returns TestLog.

TestTime#lastId

Last letd number in log’s nodeId.

Type: number.

TestTime#nextLog(opts?)

Return next test log in same time.

it('tests 2 logs', () => {
  const time = new TestTime()
  const log1 = time.nextLog()
  const log2 = time.nextLog()
})
ParameterTypeDescription
opts ?TestLogOptionsLog options.

Returns TestLog.


Extends Connection.

Logux connection for browser WebSocket.

import { WsConnection } from '@logux/core'

const connection = new WsConnection('wss://logux.example.com/')
const node = new ClientNode(nodeId, log, connection, opts)
ParameterTypeDescription
urlstringWebSocket server URL.
Class ?any
opts ?anyExtra option for WebSocket constructor.

WsConnection#connected

Is connection is enabled.

Type: boolean.

WsConnection#destroy

Optional method to disconnect and unbind all even listeners.

Type: () => void.

WsConnection#ws

WebSocket instance.

Type: WS.

WsConnection#connect()

Start connection. Connection should be in disconnected state from the beginning and start connection only on this method call.

This method could be called again if connection moved to disconnected state.

Returns Promise<void>. Promise until connection will be established.

WsConnection#disconnect(reason?)

Finish current connection.

ParameterTypeDescription
reason ?'error' | 'timeout' | 'destroy'Disconnection reason.

WsConnection#on(event, listener)

Subscribe for connection events. It implements nanoevents API. Supported events:

  • connecting: connection establishing was started.
  • connect: connection was established by any side.
  • disconnect: connection was closed by any side.
  • message: message was receive from remote node.
  • error: error during connection, sending or receiving.
ParameterTypeDescription
event'connecting' | 'connect' | 'disconnect'Event name.
listener() => voidEvent listener.
ParameterType
event'error'
listener(error: Error) => void
ParameterType
event'message'
listener(msg: Message) => void
ParameterType
event'disconnect'
listener(reason: string) => void

Returns Unsubscribe. Unbind listener from event.

WsConnection#send(message)

Send message to connection.

ParameterTypeDescription
messageMessageThe message to be sent.

Functions

attention(client)

Highlight tabs on synchronization errors.

import { attention } from '@logux/client'
attention(client)
ParameterTypeDescription
clientClientObserved Client instance.

Returns () => void. Unbind listener.

badge(client, opts)

Display Logux widget in browser.

import { badge, badgeEn } from '@logux/client'
import { badgeStyles } from '@logux/client/badge/styles'

badge(client, {
 messages: badgeEn,
 styles: {
   ...badgeStyles,
   synchronized: { backgroundColor: 'green' }
 },
 position: 'top-left'
})
ParameterTypeDescription
clientClientObserved Client instance.
optsBadgeOptionsWidget settings.

Returns () => void. Unbind badge listener and remove widget from DOM.

confirm(client)

Show confirm popup, when user close tab with non-synchronized actions.

import { confirm } from '@logux/client'
confirm(client)
ParameterTypeDescription
clientClientObserved Client instance.

Returns () => void. Unbind listener.

createLoguxCreator(config)

Creates Logux client and connect it to Redux createStore function.

import { createLoguxCreator } from '@logux/redux'

const createStore = createLoguxCreator({
  subprotocol: '1.0.0',
  server: process.env.NODE_ENV === 'development'
    ? 'ws://localhost:31337'
    : 'wss://logux.example.com',
  userId: userId.content
  token: token.content
})

const store = createStore(reducer)
store.client.start()
ParameterTypeDescription
configLoguxReduxOptionsLogux Client config.

Returns LoguxStoreCreator<H, L>. Redux’s createStore compatible function.

eachStoreCheck(test)

Pass all common tests for Logux store to callback.

import { eachStoreCheck } from '@logux/core'

eachStoreCheck((desc, creator) => {
  it(desc, creator(() => new CustomStore()))
})
ParameterTypeDescription
test(name: string, testCreator: (storeCreator: () => LogStore) => () => void) => voidCallback to create tests in your test framework.

favicon(client, links)

Change favicon to show Logux synchronization status.

import { favicon } from '@logux/client'
favicon(client, {
  normal: '/favicon.ico',
  offline: '/offline.ico',
  error: '/error.ico'
})
ParameterTypeDescription
clientClientObserved Client instance.
linksFaviconLinksFavicon links.

Returns () => void. Unbind listener.

isFirstOlder(firstMeta, secondMeta)

Compare time, when log entries were created.

It uses meta.time and meta.id to detect entries order.

import { isFirstOlder } from '@logux/core'
if (isFirstOlder(lastBeep, meta) {
  beep(action)
  lastBeep = meta
}
ParameterTypeDescription
firstMetaMeta | undefinedSome action’s metadata.
secondMetaMeta | undefinedOther action’s metadata.

Returns boolean.

log(client, messages?)

Display Logux events in browser console.

import { log } from '@logux/client'
log(client, { ignoreActions: ['user/add'] })
ParameterTypeDescription
clientClientObserved Client instance.
messages ?LogMessagesDisable specific message types.

Returns () => void. Unbind listener.

parseId(id)

Parse meta.id or Node ID into component: user ID, client ID, node ID.

import { parseId } from '@logux/core'
const { userId, clientId } = parseId(meta.id)
ParameterTypeDescription
idstringAction or Node ID

Returns IDComponents.

status(client, callback, options?)

Low-level function to show Logux synchronization status with your custom UI. It is used in badge widget.

import { status } from '@logux/client'
status(client, current => {
  updateUI(current)
})
ParameterTypeDescription
clientClientObserved Client instance.
callbackStatusListener
options ?StatusOptions

Returns () => void. Unbind listener.

subscribe(subscriber, opts?)

Decorator to add subscribe action on component mount and unsubscribe on unmount.

import subscribe from '@logux/redux'
class User extends React.Component { … }
export default subscribe(({ id }) => `user/${ id }`)(User)
import subscribe from '@logux/redux'
class User extends React.Component { … }
const SubscribeUser = subscribe(props => {
  return { channel: `user/${ props.id }`, fields: ['name'] }
})(User)
ParameterTypeDescription
subscriberSubscriber<P> | Channel[] | ChannelCallback to return subscribe action properties according to component props.
opts ?SubscribeOptionsOptions.

Returns Wrapper<P>. Class wrapper.

useSubscription(channels, opts?)

Hook to subscribe for channel during component render and unsubscribe on component unmount.

import useSubscription from '@logux/redux'
import { useSelector } from 'react-redux'

const UserPage = ({ userId }) => {
  const isSubscribing = useSubscription([`user/${ userId }`])
  const user = useSelector(state => state.users.find(i => i.id === userId))
  if (isSubscribing) {
    return <Loader />
  } else {
    return <h1>{ user.name }</h1>
  }
}
ParameterTypeDescription
channelsChannel[]Channels to subscribe.
opts ?SubscribingOptionsOptions

Returns boolean. true during data loading.


Variables

badgeEn

English translation for widget.

Type: BadgeMessages.

badgeRu

Russian translation for widget.

Type: BadgeMessages.

badgeStyles

Type: BadgeStyles.


Types

Action

PropertyTypeDescription
typestringAction type name.

ActionIterator(action, meta)

ParameterType
actionAction
metaClientMeta

Returns boolean | void.

ActionListener(action, meta)

ParameterType
actionAction
metaClientMeta

AnyAction

Type: Action & { [extra: string]: any }.

Authentificator(nodeId, token, headers)

ParameterType
nodeIdstring
tokenstring
headersH | { }

Returns Promise<boolean>.

BadgeMessages

Type: { denied: string, disconnected: string, error: string, protocolError: string, sending: string, syncError: string, synchronized: string, wait: string }.

BadgeOptions

PropertyTypeDescription
duration ?numberSynchronized state duration. Default is 3000.
messagesBadgeMessagesWidget text for different states.
position ?'top-left' | 'top-center' | 'top-right' | 'middle-left' | 'middle-center' | 'middle-right' | 'bottom-left' | 'bottom-center' | 'bottom-right'Widget position. Default is bottom-right.
stylesBadgeStylesInline styles for different states.

BadgeStyles

Type: { base: object, connecting: object, disconnected: object, error: object, icon: { disconnected: string, error: string, protocolError: string, sending: string, synchronized: string, wait: string }, protocolError: object, sending: object, synchronized: object, text: object, wait: object }.

Channel

Type: string | { channel: string }.

ClientActionListener(action, meta)

ParameterType
actionAction
metaClientMeta

ClientMeta

Extends Meta.

PropertyTypeDescription
addednumberSequence number of action in current log. Log fills it.
idIDAction unique ID. Log sets it automatically.
keepLast ?stringSet value to reasons and this reason from old action.
reasonsstring[]Why action should be kept in log. Action without reasons will be removed.
subprotocol ?stringSet code as reason and remove this reasons from previous actions.
timenumberAction created time in current node time. Milliseconds since UNIX epoch.
noAutoReason ?booleanDisable setting timeTravel reason.
sync ?booleanThis action should be synchronized with other browser tabs and server.
tab ?TabIDAction should be visible only for browser tab with the same client.tabId.

ClientOptions

PropertyTypeDescription
allowDangerousProtocol ?booleanDo not show warning when using ws:// in production.
attempts ?numberMaximum reconnection attempts. Default is Infinity.
maxDelay ?numberMaximum delay between reconnections. Default is 5000.
minDelay ?numberMinimum delay between reconnections. Default is 1000.
ping ?numberMilliseconds since last message to test connection by sending ping. Default is 10000.
prefix ?stringPrefix for IndexedDB database to run multiple Logux instances in the same browser. Default is logux.
serverstring | ConnectionServer URL.
store ?LogStoreStore to save log data. Default is MemoryStore.
subprotocolstringClient subprotocol version in SemVer format.
time ?TestTimeTest time to test client.
timeout ?numberTimeout in milliseconds to break connection. Default is 20000.
token ?string | TokenGeneratorClient credentials for authentication.
userIdstringUser ID.

Criteria

PropertyTypeDescription
id ?IDRemove reason only for action with id.
maxAdded ?numberRemove reason only for actions with lower added.
minAdded ?numberRemove reason only for actions with bigger added.
olderThan ?MetaRemove reason only older than specific action.
youngerThan ?MetaRemove reason only younger than specific action.

EmptyHeaders

Type: { [key: string]: undefined }.

PropertyTypeDescription
error ?stringError favicon link.
normal ?stringDefault favicon link. By default, it will be taken from current favicon.
offline ?stringOffline favicon link.

Filter(action, meta)

ParameterType
actionAction
metaMeta

Returns Promise<boolean>.

GetOptions

PropertyTypeDescription
order ?'created' | 'added'Sort entries by created time or when they was added to current log.

ID

Action unique ID accross all nodes.

"1564508138460 380:R7BNGAP5:px3-J3oc 0"

Type: string.

IDComponents

Type: { clientId: string, nodeId: string, userId: string | undefined }.

LastSynced

PropertyTypeDescription
receivednumberThe added value of latest received event.
sentnumberThe added value of latest sent event.

LogMessages

PropertyTypeDescription
add ?booleanDisable action added messages.
clean ?booleanDisable action cleaned messages.
error ?booleanDisable error messages.
ignoreActions ?string[]Disable action messages with specific types.
role ?booleanDisable tab role messages.
state ?booleanDisable connection state messages.
user ?booleanDisable user ID changing.

LogOptions

PropertyTypeDescription
nodeIdstringUnique current machine name.
storeSStore for log.

LoguxDispatch(action)

ParameterType
actionT

Returns T.

LoguxDispatch#crossTab(action, meta?)

Add cross-tab action to log and update store state. This action will be visible only for all tabs.

store.dispatch.crossTab(
  { type: 'CHANGE_FAVICON', favicon },
  { reasons: ['lastFavicon'] }
).then(meta => {
  store.log.removeReason('lastFavicon', { maxAdded: meta.added - 1 })
})
ParameterTypeDescription
actionTThe new action.
meta ?Partial<ClientMeta>Action’s metadata.

Returns Promise<ClientMeta>. Promise when action will be saved to the log.

LoguxDispatch#local(action, meta?)

Add local action to log and update store state. This action will be visible only for current tab.


store.dispatch.local(
  { type: 'OPEN_MENU' },
  { reasons: ['lastMenu'] }
).then(meta => {
  store.log.removeReason('lastMenu', { maxAdded: meta.added - 1 })
})
ParameterTypeDescription
actionTThe new action.
meta ?Partial<ClientMeta>Action’s metadata.

Returns Promise<ClientMeta>. Promise when action will be saved to the log.

LoguxDispatch#sync(action, meta?)

Add sync action to log and update store state. This action will be visible only for server and all browser tabs.

store.dispatch.sync(
  { type: 'CHANGE_NAME', name },
  { reasons: ['lastName'] }
).then(meta => {
  store.log.removeReason('lastName', { maxAdded: meta.added - 1 })
})
ParameterTypeDescription
actionTThe new action.
meta ?Partial<ClientMeta>Action’s metadata.

Returns Promise<ClientMeta>. Promise when action will be processed by the server.

LoguxErrorOptions

Type: { bruteforce: void, timeout: number, unknown-message: string, wrong-credentials: void, wrong-format: string, wrong-protocol: Versions, wrong-subprotocol: Versions }.

LoguxReduxOptions

Extends ClientOptions.

PropertyTypeDescription
allowDangerousProtocol ?booleanDo not show warning when using ws:// in production.
attempts ?numberMaximum reconnection attempts. Default is Infinity.
maxDelay ?numberMaximum delay between reconnections. Default is 5000.
minDelay ?numberMinimum delay between reconnections. Default is 1000.
ping ?numberMilliseconds since last message to test connection by sending ping. Default is 10000.
prefix ?stringPrefix for IndexedDB database to run multiple Logux instances in the same browser. Default is logux.
serverstring | ConnectionServer URL.
store ?LogStoreStore to save log data. Default is MemoryStore.
subprotocolstringClient subprotocol version in SemVer format.
time ?TestTimeTest time to test client.
timeout ?numberTimeout in milliseconds to break connection. Default is 20000.
token ?string | TokenGeneratorClient credentials for authentication.
userIdstringUser ID.
cleanEvery ?numberHow often we need to clean log from old actions. Default is every 25 actions.
onMissedHistory ?(action: Action) => voidCallback when there is no history to replay actions accurate.
reasonlessHistory ?numberHow many actions without meta.reasons will be kept for time travel. Default is 1000.
saveStateEvery ?numberHow often save state to history. Default is 50.

LoguxStoreCreator(reducer, enhancer?)

ParameterType
reducerReducer<S, A>
enhancer ?StoreEnhancer<Ext, StateExt>
ParameterType
reducerReducer<S, A>
preloadedState ?PreloadedState<S>
enhancer ?StoreEnhancer<Ext>

Returns LoguxReduxStore<S & StateExt, A, H, L> & Ext.

LoguxUndoAction

Type: { id: string, reason?: string, type: 'logux/undo' }.

LoguxUndoError

Type: Error & { action: LoguxUndoAction }.

Mapper(action, meta)

ParameterType
actionAction
metaMeta

Returns Promise<[Action, Meta]>.

Message

Type: ['error', keyof LoguxErrorOptions, any] | ['connect', number, string, number, object] | ['connected', number, string, [number, number], object] | ['ping', number] | ['pong', number] | ['sync', number, object, object] | ['synced', number] | ['debug', 'error', string] | ['headers', object].

Meta

PropertyTypeDescription
addednumberSequence number of action in current log. Log fills it.
idIDAction unique ID. Log sets it automatically.
keepLast ?stringSet value to reasons and this reason from old action.
reasonsstring[]Why action should be kept in log. Action without reasons will be removed.
subprotocol ?stringSet code as reason and remove this reasons from previous actions.
timenumberAction created time in current node time. Milliseconds since UNIX epoch.

NodeOptions

PropertyTypeDescription
auth ?Authentificator<H>Function to check client credentials.
fixTime ?booleanDetect difference between client and server and fix time in synchronized actions.
inFilter ?FilterFunction to filter actions from remote node. Best place for access control.
inMap ?MapperMap function to change remote node’s action before put it to current log.
outFilter ?FilterFilter function to select actions to synchronization.
outMap ?MapperMap function to change action before sending it to remote client.
ping ?numberMilliseconds since last message to test connection by sending ping.
subprotocol ?stringApplication subprotocol version in SemVer format.
timeout ?numberTimeout in milliseconds to wait answer before disconnect.
token ?string | TokenGeneratorClient credentials. For example, access token.

Page

PropertyTypeDescription
entries[Action, Meta][]Pagination page.
next ?() => Promise<Page>

ReconnectOptions

PropertyTypeDescription
attempts ?numberMaximum reconnecting attempts.
maxDelay ?numberMaximum delay between reconnecting.
minDelay ?numberMinimum delay between reconnecting.

ReduxStateListener(state, prevState, action, meta)

ParameterType
stateS
prevStateS
actionA
metaClientMeta

StatusListener(current, details)

ParameterType
current'synchronized' | 'synchronizedAfterWait' | 'disconnected' | 'connecting' | 'connectingAfterWait' | 'protocolError' | 'syncError' | 'error' | 'denied' | 'wait'
detailsundefined | Error | { action: Action, meta: ClientMeta }

StatusOptions

PropertyTypeDescription
duration ?numberSynchronized state duration. Default is 3000.

SubscribeOptions

PropertyTypeDescription
context ?ReduxContext <{ store: LoguxReduxStore }>Context with the store.
subscribingProp ?stringChange default isSubscribing property.

Subscriber(props)

ParameterType
propsP

Returns Channel[] | Channel.

SubscribingOptions

PropertyTypeDescription
context ?ReduxContext <{ store: LoguxReduxStore }>Context with the store.

TabID

Type: string.

TestLogOptions

PropertyTypeDescription
nodeIdstringUnique log name.
storeLogStoreStore for log. Will use MemoryStore by default.

TokenGenerator()

Returns string | Promise<string>.

Versions

Type: { supported: string, used: string }.

WrappedComponent

Type: ComponentType<P> & { WrappedComponent: ComponentType }.

Wrapper(component)

ParameterType
componentComponentType <{ isSubscribing: boolean } & P>

Returns WrappedComponent<P>.