Join chat

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

ParameterTypeDescription
nodeIdstringUnique current machine name.
logLogLogux log instance to be synchronized.
connectionConnectionConnection to remote node.
options ?NodeOptionsSynchronization 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#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: Log.

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.

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#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.

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.
node.on('clientError', error => {
  logError(error)
})
ParameterTypeDescription
event'state' | 'connect' | 'error' | 'clientError' | 'debug'Event name.
listener() => voidThe listener function.

Returns Unsubscribe. Unbind listener from event.

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.


Extends Context.

Subscription context.

server.channel('user/:id', {
  access (ctx, action, meta) {
    return ctx.params.id === ctx.userId
  }
})

ChannelContext#clientId

Unique persistence client ID.

server.clientIds[node.clientId]

Type: string.

ChannelContext#data

Open structure to save some data between different steps of processing.

server.type('RENAME', {
  access (ctx, action, meta) {
    ctx.data.user = findUser(ctx.userId)
    return ctx.data.user.hasAccess(action.projectId)
  }
  process (ctx, action, meta) {
    return ctx.data.user.rename(action.projectId, action.name)
  }
})

Type: D.

ChannelContext#isServer

Was action created by Logux server.

access: (ctx, action, meta) => ctx.isServer

Type: boolean.

ChannelContext#nodeId

Unique node ID.

server.nodeIds[node.nodeId]

Type: string.

ChannelContext#params

Parsed variable parts of channel pattern.

server.channel('user/:id', {
  access (ctx, action, meta) {
    action.channel //=> user/10
    ctx.params //=> { id: '10' }
  }
})
server.channel(/post\/(\d+)/, {
  access (ctx, action, meta) {
    action.channel //=> post/10
    ctx.params //=> ['post/10', '10']
  }
})

Type: P.

ChannelContext#server

Logux server

Type: Server.

ChannelContext#subprotocol

Action creator application subprotocol version in SemVer format. Use Context#isSubprotocol to check it.

Type: string.

ChannelContext#userId

User ID taken node ID.

async access (ctx, action, meta) {
  const user = await db.getUser(ctx.userId)
  return user.admin
}

Type: 'server' | string | undefined.

ChannelContext#isSubprotocol(range)

Check creator subprotocol version. It uses semver npm package to parse requirements.

if (ctx.isSubprotocol('2.x')) {
  useOldAPI()
}
ParameterTypeDescription
rangestringnpm’s version requirements.

Returns boolean. Is version satisfies requirements.

ChannelContext#sendBack(action, meta?)

Send action back to the client.

ctx.sendBack({ type: 'login/success', token })
ParameterTypeDescription
actionActionThe action.
meta ?ServerMetaAction’s meta.

Returns Promise<void>. Promise until action was added to the server log.


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

Connection#connected

Is connection is enabled.

Type: boolean.

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' | 'message' | 'error'Event name.
listener() => voidEvent listener.

Returns Unsubscribe. Unbind listener from event.

Connection#send(message)

Send message to connection.

ParameterTypeDescription
messageMessageThe message to be sent.

Action context.

server.type('FOO', {
  access (ctx, action, meta) {
    return ctx.isSubprotocol('3.x') ? check3(action) : check4(action)
  }
})

Context#clientId

Unique persistence client ID.

server.clientIds[node.clientId]

Type: string.

Context#data

Open structure to save some data between different steps of processing.

server.type('RENAME', {
  access (ctx, action, meta) {
    ctx.data.user = findUser(ctx.userId)
    return ctx.data.user.hasAccess(action.projectId)
  }
  process (ctx, action, meta) {
    return ctx.data.user.rename(action.projectId, action.name)
  }
})

Type: D.

Context#isServer

Was action created by Logux server.

access: (ctx, action, meta) => ctx.isServer

Type: boolean.

Context#nodeId

Unique node ID.

server.nodeIds[node.nodeId]

Type: string.

Context#server

Logux server

Type: Server.

Context#subprotocol

Action creator application subprotocol version in SemVer format. Use Context#isSubprotocol to check it.

Type: string.

Context#userId

User ID taken node ID.

async access (ctx, action, meta) {
  const user = await db.getUser(ctx.userId)
  return user.admin
}

Type: 'server' | string | undefined.

Context#isSubprotocol(range)

Check creator subprotocol version. It uses semver npm package to parse requirements.

if (ctx.isSubprotocol('2.x')) {
  useOldAPI()
}
ParameterTypeDescription
rangestringnpm’s version requirements.

Returns boolean. Is version satisfies requirements.

Context#sendBack(action, meta?)

Send action back to the client.

ctx.sendBack({ type: 'login/success', token })
ParameterTypeDescription
actionActionThe action.
meta ?ServerMetaAction’s meta.

Returns Promise<void>. Promise until action was added to the server log.


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: Connection.

LocalPair#right

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

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

Type: Connection.


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
optsLogOptionsLog options.

Log#nodeId

Unique node ID. It is used in action IDs.

Type: string.

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<ServerMeta>Open structure for action metadata.

Returns Promise<ServerMeta | 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, ServerMeta] | [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<ServerMeta>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(opts, 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
optsGetOptionsIterator options.
callbackActionIteratorFunction will be executed on every action.

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.
criteriaCriteriaCriteria to select action for reason removing.

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


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
typeTThe error code.
options ?LoguxErrorOptions[T]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.


Extends Store.

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()
})
ParameterTypeDescription
actionActionThe action to add.
metaMetaAction’s metadata.

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.


End-user API to create Logux server.

const { Server } = require('@logux/server')

const env = process.env.NODE_ENV || 'development'
const envOptions = {}
if (env === 'production') {
  envOptions.cert = 'cert.pem'
  envOptions.key = 'key.pem'
}

const server = new Server(Object.assign({
  subprotocol: '1.0.0',
  supports: '1.x || 0.x',
  root: __dirname
}, envOptions))

server.listen()
ParameterTypeDescription
optsServerOptionsServer options.

Server.loadOptions(process, defaults)

Load options from command-line arguments and/or environment

const server = new Server(Server.loadOptions(process, {
  subprotocol: '1.0.0',
  supports: '1.x',
  root: __dirname,
  port: 31337
}))
ParameterTypeDescription
processProcessCurrent process object.
defaultsServerOptionsDefault server options. Arguments and environment variables will override them.

Returns ServerOptions. Parsed options object.

Server#connected

Connected clients.

for (let i in server.connected) {
  console.log(server.connected[i].remoteAddress)
}

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

Server#env

Production or development mode.

if (server.env === 'development') {
  logDebugData()
}

Type: 'production' | 'development'.

Server#log

Server actions log.

server.log.each(finder)

Type: Log.

Server#nodeId

Server unique ID.

console.log('Error was raised on ' + server.nodeId)

Type: string.

Server#options

Server options.

console.log('Server options', server.options.subprotocol)

Type: ServerOptions.

Server#reporter

Function to show current server status.

Type: Reporter.

Server#addClient(connection)

Add new client for server. You should call this method manually mostly for test purposes.

server.addClient(test.right)
ParameterTypeDescription
connectionServerConnectionLogux connection to client.

Returns number. Client ID.

Server#auth(authenticator)

Set authenticate function. It will receive client credentials and node ID. It should return a Promise with true or false.

server.auth(async (userId, token) => {
  const user = await findUserByToken(token)
  return !!user && userId === user.id
})
ParameterTypeDescription
authenticatorAuthenticatorThe authentication callback.

Server#channel(pattern, callbacks)

Define the channel.

server.channel('user/:id', {
  access (ctx, action, meta) {
    return ctx.params.id === ctx.userId
  }
  filter (ctx, action, meta) {
    return (otherCtx, otherAction, otherMeta) => {
      return !action.hidden
    }
  }
  async init (ctx, action, meta) {
    const user = await db.loadUser(ctx.params.id)
    ctx.sendBack({ type: 'USER_NAME', name: user.name })
  }
})
ParameterTypeDescription
patternstringPattern or regular expression for channel name.
callbacksChannelCallbacks<A, D, P>Callback during subscription process.
ParameterType
patternRegExp
callbacksChannelCallbacks<A, D, P>

Type templates for TypeScript:

TemplatesTypeDescription
PobjectType for ctx.params.
DobjectType for ctx.data.
ALoguxSubscribeActionlogux/subscribe Action’s type.

Server#debugError(error)

Send runtime error stacktrace to all clients.

process.on('uncaughtException', e => {
  server.debugError(e)
})
ParameterTypeDescription
errorErrorRuntime error instance.

Server#destroy()

Stop server and unbind all listeners.

afterEach(() => {
  testServer.destroy()
})

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

Server#listen()

Start WebSocket server and listen for clients.

Returns Promise<void>. When the server has been bound.

Server#on(event, listener)

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

  • error: server error during action processing.
  • fatal: server error during loading.
  • clientError: wrong client behaviour.
  • connected: new client was connected.
  • disconnected: client was disconnected.
  • preadd: action is going to be added to the log. The best place to set reasons.
  • add: action was added to the log.
  • clean: action was cleaned from the log.
  • processed: action processing was finished.
  • subscribed: channel initial data was loaded.
server.on('error', error => {
  trackError(error)
})
ParameterTypeDescription
event'fatal' | 'clientError'The event name.
listener(err: Error) => voidThe listener function.
ParameterType
event'error'
listener(err: Error, action: Action, meta: ServerMeta) => void
ParameterType
event'connected' | 'disconnected'
listener(client: ServerClient) => void
ParameterType
event'preadd' | 'add' | 'clean'
listener(action: Action, meta: ServerMeta) => void
ParameterType
event'processed'
listener(action: Action, meta: ServerMeta, latencyMilliseconds: number) => void
ParameterType
event'subscribed'
listener(action: LoguxSubscribeAction, meta: ServerMeta, latencyMilliseconds: number) => void

Returns Unsubscribe. Unbind listener from event.

Server#otherChannel(callbacks)

Set callbacks for unknown channel subscription.

server.otherChannel({
  async access (ctx, action, meta) {
    const res = await phpBackend.checkChannel(ctx.params[0], ctx.userId)
    if (res.code === 404) {
      this.wrongChannel(action, meta)
      return false
    } else {
      return response.body === 'granted'
    }
  }
})
ParameterTypeDescription
callbacksChannelCallbacks<LoguxSubscribeAction, D, { }>Callback during subscription process.

Type templates for TypeScript:

TemplatesTypeDescription
DobjectType for ctx.data.

Server#otherType(callbacks)

Define callbacks for actions, which type was not defined by any Server#type. Useful for proxy or some hacks.

Without this settings, server will call Server#unknownType on unknown type.

server.otherType(
  async access (ctx, action, meta) {
    const response = await phpBackend.checkByHTTP(action, meta)
    if (response.code === 404) {
      this.unknownType(action, meta)
      retur false
    } else {
      return response.body === 'granted'
    }
  }
  async process (ctx, action, meta) {
    return await phpBackend.sendHTTP(action, meta)
  }
})
ParameterTypeDescription
callbacksActionCallbacks<Action, D>Callbacks for actions with this type.

Type templates for TypeScript:

TemplatesTypeDescription
DobjectType for ctx.data.

Server#sendAction(action, meta)

Send action, received by other server, to all clients of current server. This method is for multi-server configuration only.

server.on('add', (action, meta) => {
  if (meta.server === server.nodeId) {
    sendToOtherServers(action, meta)
  }
})
onReceivingFromOtherServer((action, meta) => {
  server.sendAction(action, meta)
})
ParameterTypeDescription
actionActionNew action.
metaServerMetaAction’s metadata.

Server#type(name, callbacks)

Define action type’s callbacks.

server.type('CHANGE_NAME', {
  access (ctx, action, meta) {
    return action.user === ctx.userId
  },
  resend (ctx, action) {
    return { channel: `user/${ action.user }` }
  }
  process (ctx, action, meta) {
    if (isFirstOlder(lastNameChange(action.user), meta)) {
      return db.changeUserName({ id: action.user, name: action.name })
    }
  }
})
ParameterTypeDescription
nameA['type']The action’s type.
callbacksActionCallbacks<A, D>Callbacks for actions with this type.

Type templates for TypeScript:

TemplatesTypeDescription
AActionAction’s type.
DobjectType for ctx.data.

Server#undo(meta, reason?, extra?)

Undo action from client.

if (couldNotFixConflict(action, meta)) {
  server.undo(meta)
}
ParameterTypeDescription
metaServerMetaThe action’s metadata.
reason ?stringOptional code for reason. Default is 'error'
extra ?objectExtra fields to logux/undo action.

Returns Promise<void>. When action was saved to the log.

Server#unknownType(action, meta)

If you receive action with unknown type, this method will mark this action with error status and undo it on the clients.

If you didn’t set Server#otherType, Logux will call it automatically.

server.otherType({
  access (ctx, action, meta) {
    if (action.type.startsWith('myapp/')) {
      return proxy.access(action, meta)
    } else {
      server.unknownType(action, meta)
    }
  }
})
ParameterTypeDescription
actionActionThe action with unknown type.
metaServerMetaAction’s metadata.

Server#wrongChannel(action, meta)

Report that client try to subscribe for unknown channel.

Logux call it automatically, if you will not set Server#otherChannel.

server.otherChannel({
  async access (ctx, action, meta) {
    const res = phpBackend.checkChannel(params[0], ctx.userId)
    if (res.code === 404) {
      this.wrongChannel(action, meta)
      return false
    } else {
      return response.body === 'granted'
    }
  }
})
ParameterTypeDescription
actionLoguxSubscribeActionThe subscribe action.
metaServerMetaAction’s metadata.

Logux client connected to server.

const client = server.connected[0]

ServerClient#app

Server, which received client.

Type: Server.

ServerClient#clientId

Unique persistence machine ID. It will be undefined before correct authentication.

Type: string.

ServerClient#connection

The Logux wrapper to WebSocket connection.

console.log(client.connection.ws.upgradeReq.headers)

Type: ServerConnection.

ServerClient#key

Client number used as app.connected key.

function stillConnected (client) {
  return !!app.connected[client.key]
}

Type: string.

ServerClient#node

Node instance to synchronize logs.

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

Type: ServerNode.

ServerClient#nodeId

Unique node ID. It will be undefined before correct authentication.

Type: string.

ServerClient#processing

Does server process some action from client.

console.log('Clients in processing:', clients.map(i => i.processing))

Type: boolean.

ServerClient#remoteAddress

Client IP address.

const clientCity = detectLocation(client.remoteAddress)

Type: string.

ServerClient#userId

User ID. It will be filled from client’s node ID. It will be undefined before correct authentication.

Type: string.

ServerClient#destroy()

Disconnect client.

ServerClient#isSubprotocol(range)

Check client subprotocol version. It uses semver npm package to parse requirements.

if (client.isSubprotocol('4.x')) {
  useOldAPI()
}
ParameterTypeDescription
rangestringnpm’s version requirements.

Returns boolean. Is version satisfies requirements.


Extends Connection.

Logux connection for server WebSocket.

import { ServerConnection } from '@logux/core'
import { Server } from 'ws'

wss.on('connection', function connection(ws) {
  const connection = new ServerConnection(ws)
  const node = new ServerNode('server', log, connection, opts)
})
ParameterTypeDescription
wsobjectWebSocket instance

ServerConnection#connected

Is connection is enabled.

Type: boolean.

ServerConnection#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.

ServerConnection#disconnect(reason?)

Finish current connection.

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

ServerConnection#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' | 'message' | 'error'Event name.
listener() => voidEvent listener.

Returns Unsubscribe. Unbind listener from event.

ServerConnection#send(message)

Send message to connection.

ParameterTypeDescription
messageMessageThe message to be sent.

Extends BaseNode.

Server node in synchronization pair.

Instead of client node, it doesn’t initialize synchronization and destroy itself on disconnect.

import { ServerNode } from '@logux/core'
startServer(ws => {
  const connection = new ServerConnection(ws)
  const node = new ServerNode('server' + id, log, connection)
})
ParameterTypeDescription
nodeIdstringUnique current machine name.
logLogLogux log instance to be synchronized.
connectionConnectionConnection to remote node.
options ?NodeOptionsSynchronization options.

ServerNode#authenticated

Did we finish remote node authentication.

Type: boolean.

ServerNode#connected

Is synchronization in process.

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

Type: boolean.

ServerNode#connection

Connection used to communicate to remote node.

Type: Connection.

ServerNode#lastReceived

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

Type: number.

ServerNode#lastSent

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

Type: number.

ServerNode#localNodeId

Unique current machine name.

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

Type: string.

ServerNode#localProtocol

Used Logux protocol.

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

Type: number.

ServerNode#log

Log for synchronization.

Type: Log.

ServerNode#minProtocol

Minimum version of Logux protocol, which is supported.

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

Type: number.

ServerNode#options

Synchronization options.

Type: NodeOptions.

ServerNode#remoteNodeId

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

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

Type: string | undefined.

ServerNode#remoteProtocol

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


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

Type: number | undefined.

ServerNode#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.

ServerNode#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'.

ServerNode#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.

ServerNode#destroy()

Shut down the connection and unsubscribe from log events.

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

ServerNode#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.
node.on('clientError', error => {
  logError(error)
})
ParameterTypeDescription
event'state' | 'connect' | 'error' | 'clientError' | 'debug'Event name.
listener() => voidThe listener function.

Returns Unsubscribe. Unbind listener from event.

ServerNode#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.


Every Store class should provide 8 standard methods.

ParameterTypeDescription
actionActionThe action to add.
metaMetaAction’s metadata.

Store#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.

Store#byId(id)

Return action by action ID.

ParameterTypeDescription
idIDAction ID.

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

Store#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.

Store#clean()

Remove all data from the store.

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

Store#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.

Store#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.

Store#getLastSynced()

Get added values for latest synchronized received/sent events.

Returns Promise<LastSynced>. Promise with added values

Store#remove(id)

Remove action from store.

ParameterTypeDescription
idIDAction ID.

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

Store#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.

Store#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 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#actions

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

This shortcut works only with MemoryStore.

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

Type: Action[].

TestLog#nodeId

Unique node ID. It is used in action IDs.

Type: string.

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<ServerMeta>Open structure for action metadata.

Returns Promise<ServerMeta | 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, ServerMeta] | [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<ServerMeta>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(opts, 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
optsGetOptionsIterator options.
callbackActionIteratorFunction will be executed on every action.

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.
criteriaCriteriaCriteria 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: Connection.

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.

TestPair#leftSent

Sent messages from left connection.

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

Type: string[][].

TestPair#right

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

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

Type: Connection.

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.

TestPair#rightSent

Sent messages from right connection.

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

Type: string[][].

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
optsTestLogOptionsLog 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
optsTestLogOptionsLog options.

Returns TestLog.


Functions

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, creator: () => Store) => voidCallback to create tests in your test framework.

Returns any.

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
firstMetaMetaSome action’s metadata.
secondMetaMetaOther action’s metadata.

Returns boolean.


Variables

ALLOWED_META

List of meta keys permitted for clients.

const { ALLOWED_META } = require('@logux/server')
async function outMap (action, meta) {
  const filtered = { }
  for (const i in meta) {
    if (ALLOWED_META.includes(i)) {
      filtered[i] = meta[i]
    }
  }
  return [action, filtered]
}

Type: string[].


Types

Action

PropertyTypeDescription
typestringAction type name.

ActionCallbacks

Type: { access: Authorizer<A, D>, finally?: ActionFinally<A, D>, process?: Processor<A, D>, resend?: Resender<A, D> }.

ActionFinally(ctx, action, meta)

Callback which will be run on the end of subscription processing or on an error.

ParameterTypeDescription
ctxContext<D>Information about node, who create this action.
actionAThe action data.
metaServerMetaThe action metadata.

ActionIterator(action, meta)

ParameterType
actionAction
metaServerMeta

Returns boolean | void.

ActionListener(action, meta)

ParameterType
actionAction
metaServerMeta

ActionReporter

Type: { action: Action, meta: ServerMeta }.

AnyAction

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

AuthenticationReporter

Type: { connectionId: string, nodeId: string, subprotocol: string }.

Authenticator(userId, credentials, server)

The authentication callback.

ParameterTypeDescription
userIdstring | falseUser ID.
credentialsstring | undefinedThe client credentials.
serverServerClient

Returns boolean | Promise<boolean>. true if credentials was correct

Authentificator(credentials, nodeId)

ParameterType
credentialsstring
nodeIdstring

Returns Promise<boolean>.

Authorizer(ctx, action, meta)

Check does user can do this action.

ParameterTypeDescription
ctxContext<D>Information about node, who create this action.
actionAThe action data.
metaServerMetaThe action metadata.

Returns boolean | Promise<boolean>. true if client are allowed to use this action.

ChannelAuthorizer(ctx, action, meta)

Channel authorizer callback

ParameterTypeDescription
ctxChannelContext<D, P>Information about node, who create this action.
actionAThe action data.
metaServerMetaThe action metadata.

Returns boolean | Promise<boolean>. true if client are allowed to subscribe to this channel.

ChannelCallbacks

Type: { access: ChannelAuthorizer<A, D, P>, filter?: FilterCreator<A, D, P>, finally?: ChannelFinally<A, D, P>, init?: ChannelInitialized<A, D, P> }.

ChannelFilter(ctx, action, meta)

Channel filter callback

ParameterTypeDescription
ctxContext<{ }>Information about node, who create this action.
actionActionThe action data.
metaServerMetaThe action metadata.

Returns boolean. Should action be sent to client.

ChannelFinally(ctx, action, meta)

Callback which will be run on the end of subscription processing or on an error.

ParameterTypeDescription
ctxChannelContext<D, P>Information about node, who create this action.
actionAThe action data.
metaServerMetaThe action metadata.

ChannelInitialized(ctx, action, meta)

Send actions with initial state.

ParameterTypeDescription
ctxChannelContext<D, P>Information about node, who create this action.
actionAThe action data.
metaServerMetaThe action metadata.

Returns void | Promise<void>. Promise during initial actions loading.

CleanReporter

Type: { actionId: 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.

Filter(action, meta)

ParameterType
actionAction
metaMeta

Returns Promise<boolean>.

FilterCreator(ctx, action, meta)

Generates custom filter for channel’s actions.

ParameterTypeDescription
ctxChannelContext<D, P>Information about node, who create this action.
actionAThe action data.
metaServerMetaThe action metadata.

Returns Promise<ChannelFilter> | ChannelFilter | void. Actions filter.

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.

LastSynced

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

LogOptions

PropertyTypeDescription
nodeIdstringUnique current machine name.
storeStoreStore for log.

LoguxAction

Type: LoguxSubscribeAction | LoguxUnsubscribeAction | LoguxProcessedAction | LoguxUndoAction.

LoguxErrorOptions

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

LoguxProcessedAction

Type: { id: ID, type: 'logux/processed' }.

LoguxSubscribeAction

Type: { channel: string, since?: { id: string, time: number }, type: 'logux/subscribe' }.

LoguxUndoAction

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

LoguxUnsubscribeAction

Type: { channel: string, type: 'logux/unsubscribe' }.

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].

Meta

PropertyTypeDescription
addednumberSequence number of action in current log. Log fills it.
idIDAction unique ID. Log sets it automatically.
keepLaststringSet value to reasons and this reason from old action.
reasons ?string[]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 ?AuthentificatorFunction to check client credentials.
credentials ?stringClient credentials. For example, access token.
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.

Page

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

Processor(ctx, action, meta)

Action business logic.

ParameterTypeDescription
ctxContext<D>Information about node, who create this action.
actionAThe action data.
metaServerMetaThe action metadata.

Returns void | Promise<void>. Promise when processing will be finished.

ReconnectOptions

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

Reporter

Type: (event: E, payload: ReportersArguments[E]) => void.

ReportersArguments

Type: { add: ActionReporter, authenticated: AuthenticationReporter, clean: CleanReporter, clientError: { connectionId?: string, err: Error, nodeId?: string }, connect: { connectionId: string, ipAddress: string }, denied: CleanReporter, destroy: void, disconnect: { connectionId?: string, nodeId?: string }, error: { actionId?: ID, connectionId?: string, err: Error, fatal?: true, nodeId?: string }, listen: { backend: string, cert: boolean, controlHost: string, controlPassword: string, controlPort: string, environment: 'production' | 'development', host: string, loguxServer: string, nodeId: string, notes: object, port: string, redis: string, server: boolean, subprotocol: string, supports: string }, processed: { actionId: ID, latency: number }, subscribed: SubscriptionReporter, unauthenticated: AuthenticationReporter, unknownType: { actionId: ID, type: string }, unsubscribed: SubscriptionReporter, useless: ActionReporter, wrongChannel: SubscriptionReporter, zombie: { nodeId: string } }.

Resend

Type: { channel?: string, channels?: string[], client?: string, clients?: string[], node?: string, nodes?: string[], user?: string, users?: string[] }.

Resender(ctx, action, meta)

Return object with keys for meta to resend action to other users.

ParameterTypeDescription
ctxContext<D>Information about node, who create this action.
actionAThe action data.
metaServerMetaThe action metadata.

Returns Resend | Promise<Resend>. Meta’s keys.

ServerMeta

Extends Meta.

PropertyTypeDescription
addednumberSequence number of action in current log. Log fills it.
idIDAction unique ID. Log sets it automatically.
keepLaststringSet value to reasons and this reason from old action.
reasons ?string[]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.
channel ?stringAll clients subscribed to channel will receive the action.
channels ?string[]All clients subscribed to listed channels will receive the action.
client ?stringAll clients with listed client ID will receive the action.
clients ?string[]All clients with listed client IDs will receive the action.
node ?stringClient with listed node ID will receive the action.
nodes ?string[]All clients with listed node IDs will receive the action.
serverstringNode ID of the server received the action.
status ?'waiting' | 'processed' | 'error'Action processing status
user ?stringAll clients with listed user ID will receive the action.
users ?string[]All clients with listed user IDs will receive the action.

ServerOptions

Extends { backend?: string, cert?: string, controlHost?: string, controlPassword?: string, controlPort?: number, env?: 'production' | 'development', host?: string, id?: string, key?: string, pid?: number, ping?: number, port?: number, redis?: string, reporter?: Reporter, root?: string, server?: HTTPServer, store?: Store, subprotocol: string, supports: string, time?: TestTime, timeout?: number }.

PropertyTypeDescription
backend ?stringURL to PHP, Ruby on Rails, or other backend to process actions and authentication.
cert ?stringSSL certificate or path to it. Path could be relative from server root. It is required in production mode, because WSS is highly recommended.
controlHost ?stringHost to bind HTTP server to control Logux server. Default is 127.0.0.1.
controlPassword ?stringPassword to control the server.
controlPort ?numberPort to control the server. Default is 31338.
env ?'production' | 'development'Development or production server mode. By default, it will be taken from NODE_ENV environment variable. On empty NODE_ENV it will be 'development'.
host ?stringIP-address to bind server. Default is 127.0.0.1.
id ?stringCustom random ID to be used in node ID.
key ?stringSSL key or path to it. Path could be relative from server root. It is required in production mode, because WSS is highly recommended.
pid ?numberProcess ID, to display in reporter.
ping ?numberMilliseconds since last message to test connection by sending ping. Default is 10000.
port ?numberPort to bind server. It will create HTTP server manually to connect WebSocket server to it. Default is 31337.
redis ?stringURL to Redis for Logux Server Pro scaling.
reporter ?ReporterFunction to show current server status.
root ?stringApplication root to load files and show errors. Default is process.cwd().
server ?HTTPServerHTTP server to connect WebSocket server to it. Same as in ws.Server.
store ?StoreStore to save log. Will be {@link @logux/core:MemoryStore}, by default.
subprotocolstringServer current application subprotocol version in SemVer format.
supportsstringnpm’s version requirements for client subprotocol version.
time ?TestTimeTest time to test server.
timeout ?numberTimeout in milliseconds to disconnect connection. Default is 20000.
bunyan ?BunyanLoggerBunyan logger with custom settings.
reporter ?'human' | 'json' | ReporterReport process/errors to CLI in bunyan JSON or in human readable format. It can be also a function to show current server status. Default is 'human'.

SubscriptionReporter

Type: { actionId: ID, channel: string }.

TestLogOptions

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

Versions

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