@logux/core
Advanced tools
Comparing version 0.5.3 to 0.6.0
@@ -6,4 +6,4 @@ import { Unsubscribe } from 'nanoevents' | ||
interface Authentificator { | ||
(nodeId: string, token: string): Promise<boolean> | ||
interface Authentificator<H> { | ||
(nodeId: string, token: string, headers: H | {}): Promise<boolean> | ||
} | ||
@@ -19,2 +19,6 @@ | ||
type EmptyHeaders = { | ||
[key: string]: undefined | ||
} | ||
export interface TokenGenerator { | ||
@@ -24,14 +28,19 @@ (): string | Promise<string> | ||
type NodeState = 'disconnected' | 'connecting' | 'sending' | 'synchronized' | ||
export type NodeState = | ||
| 'disconnected' | ||
| 'connecting' | ||
| 'sending' | ||
| 'synchronized' | ||
type Message = | ||
['error', keyof LoguxErrorOptions, any?] | | ||
['connect', number, string, number, object?] | | ||
['connected', number, string, [number, number], object?] | | ||
['ping', number] | | ||
['pong', number] | | ||
export type Message = | ||
| ['error', keyof LoguxErrorOptions, any?] | ||
| ['connect', number, string, number, object?] | ||
| ['connected', number, string, [number, number], object?] | ||
| ['ping', number] | ||
| ['pong', number] | ||
// Inaccurate type until https://github.com/microsoft/TypeScript/issues/26113 | ||
['sync', number, object, object] | | ||
['synced', number] | | ||
['debug', 'error', string] | ||
| ['sync', number, object, object] | ||
| ['synced', number] | ||
| ['debug', 'error', string] | ||
| ['headers', object] | ||
@@ -70,6 +79,10 @@ /** | ||
on ( | ||
event: 'connecting' | 'connect' | 'disconnect' | 'message' | 'error', | ||
event: 'connecting' | 'connect' | 'disconnect', | ||
listener: () => void | ||
): Unsubscribe | ||
on (event: 'error', listener: (error: Error) => void): Unsubscribe | ||
on (event: 'message', listener: (msg: Message) => void): Unsubscribe | ||
on (event: 'disconnect', listener: (reason: string) => void): Unsubscribe | ||
/** | ||
@@ -92,5 +105,10 @@ * Start connection. Connection should be in disconnected state | ||
disconnect (reason?: 'error' | 'timeout' | 'destroy'): void | ||
/** | ||
* Optional method to disconnect and unbind all even listeners. | ||
*/ | ||
destroy?: () => void | ||
} | ||
type NodeOptions = { | ||
export type NodeOptions<H extends object = {}> = { | ||
/** | ||
@@ -104,3 +122,3 @@ * Client credentials. For example, access token. | ||
*/ | ||
auth?: Authentificator | ||
auth?: Authentificator<H> | ||
@@ -126,3 +144,3 @@ /** | ||
*/ | ||
subprotocol?: string, | ||
subprotocol?: string | ||
@@ -155,4 +173,5 @@ /** | ||
* @template M Meta’s type. | ||
* @template H Remote headers type. | ||
*/ | ||
export class BaseNode<M extends Meta = Meta> { | ||
export class BaseNode<H extends object = {}, L extends Log = Log<Meta>> { | ||
/** | ||
@@ -165,3 +184,6 @@ * @param nodeId Unique current machine name. | ||
constructor ( | ||
nodeId: string, log: Log, connection: Connection, options?: NodeOptions | ||
nodeId: string, | ||
log: L, | ||
connection: Connection, | ||
options?: NodeOptions<H> | ||
) | ||
@@ -210,2 +232,13 @@ | ||
/** | ||
* Headers set by remote node. | ||
* By default, it is an empty object. | ||
* | ||
* ```js | ||
* let message = I18N_ERRORS[node.remoteHeaders.language || 'en'] | ||
* node.log.add({ type: 'error', message }) | ||
* ``` | ||
*/ | ||
remoteHeaders: H | EmptyHeaders | ||
/** | ||
* Minimum version of Logux protocol, which is supported. | ||
@@ -242,3 +275,3 @@ * | ||
*/ | ||
log: Log<M> | ||
log: L | ||
@@ -253,3 +286,3 @@ /** | ||
*/ | ||
options: NodeOptions | ||
options: NodeOptions<H> | ||
@@ -302,2 +335,12 @@ /** | ||
/** | ||
* Promise for node data initial loadiging. | ||
*/ | ||
initializing: Promise<void> | ||
/** | ||
* Time difference between nodes. | ||
*/ | ||
timeFix: number | ||
/** | ||
* Subscribe for synchronization events. It implements nanoevents API. | ||
@@ -312,2 +355,3 @@ * Supported events: | ||
* * `debug`: when debug information received from remote node. | ||
* * `headers`: headers was receive from remote node. | ||
* | ||
@@ -325,6 +369,18 @@ * ```js | ||
on ( | ||
event: 'state' | 'connect' | 'error' | 'clientError' | 'debug', | ||
event: 'state' | 'connect' | 'debug' | 'headers', | ||
listener: () => void | ||
): Unsubscribe | ||
on ( | ||
event: 'error' | 'clientError', | ||
listener: (error: Error) => void | ||
): Unsubscribe | ||
on ( | ||
event: 'debug', | ||
listener: (type: 'error', data: string) => void | ||
): Unsubscribe | ||
on (event: 'headers', listener: (headers: H) => void): Unsubscribe | ||
/** | ||
@@ -340,4 +396,5 @@ * Disable throwing a error on error message and create error listener. | ||
* @param listener The error listener. | ||
* @returns Unbind listener from event. | ||
*/ | ||
catch (listener: (error: LoguxError) => void): void | ||
catch (listener: (error: LoguxError) => void): Unsubscribe | ||
@@ -369,2 +426,16 @@ /** | ||
destroy (): void | ||
/** | ||
* Set headers for current node. | ||
* | ||
* ```js | ||
* if (navigator) { | ||
* node.setLocalHeaders({ language: navigator.language }) | ||
* } | ||
* node.connection.connect() | ||
* ``` | ||
* | ||
* @param headers The data object will be set as headers for current node. | ||
*/ | ||
setLocalHeaders (headers: object): void | ||
} |
import { createNanoEvents } from 'nanoevents' | ||
import { | ||
sendConnect, sendConnected, connectMessage, connectedMessage | ||
sendConnect, | ||
sendConnected, | ||
connectMessage, | ||
connectedMessage | ||
} from '../connect/index.js' | ||
import { | ||
sendSync, sendSynced, syncMessage, syncedMessage | ||
} from '../sync/index.js' | ||
import { sendSync, sendSynced, syncMessage, syncedMessage } from '../sync/index.js' | ||
import { sendPing, pingMessage, pongMessage } from '../ping/index.js' | ||
import { sendDebug, debugMessage } from '../debug/index.js' | ||
import { sendError, errorMessage } from '../error/index.js' | ||
import { sendHeaders, headersMessage } from '../headers/index.js' | ||
import { LoguxError } from '../logux-error/index.js' | ||
@@ -20,3 +22,3 @@ | ||
const BEFORE_AUTH = ['connect', 'connected', 'error', 'debug'] | ||
const BEFORE_AUTH = ['connect', 'connected', 'error', 'debug', 'headers'] | ||
@@ -42,3 +44,3 @@ async function syncMappedEvent (node, action, meta) { | ||
class BaseNode { | ||
constructor (nodeId, log, connection, options = { }) { | ||
constructor (nodeId, log, connection, options = {}) { | ||
this.remoteNodeId = undefined | ||
@@ -49,3 +51,3 @@ this.remoteProtocol = undefined | ||
this.minProtocol = 3 | ||
this.localProtocol = 3 | ||
this.localProtocol = 4 | ||
this.localNodeId = nodeId | ||
@@ -67,3 +69,3 @@ | ||
this.syncing = 0 | ||
this.received = { } | ||
this.received = {} | ||
@@ -79,26 +81,27 @@ this.lastSent = 0 | ||
this.unbind = [] | ||
this.unbind.push(log.on('add', (action, meta) => { | ||
this.onAdd(action, meta) | ||
})) | ||
this.unbind.push(connection.on('connecting', () => { | ||
this.onConnecting() | ||
})) | ||
this.unbind.push(connection.on('connect', () => { | ||
this.onConnect() | ||
})) | ||
this.unbind.push(connection.on('message', message => { | ||
this.onMessage(message) | ||
})) | ||
this.unbind.push(connection.on('error', error => { | ||
if (error.message === 'Wrong message format') { | ||
this.sendError(new LoguxError('wrong-format', error.received)) | ||
this.connection.disconnect('error') | ||
} else { | ||
this.error(error) | ||
} | ||
})) | ||
this.unbind.push(connection.on('disconnect', () => { | ||
this.onDisconnect() | ||
})) | ||
this.unbind = [ | ||
log.on('add', (action, meta) => { | ||
this.onAdd(action, meta) | ||
}), | ||
connection.on('connecting', () => { | ||
this.onConnecting() | ||
}), | ||
connection.on('connect', () => { | ||
this.onConnect() | ||
}), | ||
connection.on('message', message => { | ||
this.onMessage(message) | ||
}), | ||
connection.on('error', error => { | ||
if (error.message === 'Wrong message format') { | ||
this.sendError(new LoguxError('wrong-format', error.received)) | ||
this.connection.disconnect('error') | ||
} else { | ||
this.error(error) | ||
} | ||
}), | ||
connection.on('disconnect', () => { | ||
this.onDisconnect() | ||
}) | ||
] | ||
@@ -108,2 +111,4 @@ this.initialized = false | ||
this.initializing = this.initialize() | ||
this.localHeaders = {} | ||
this.remoteHeaders = {} | ||
} | ||
@@ -117,3 +122,7 @@ | ||
this.throwsError = false | ||
this.on('error', listener) | ||
let unbind = this.on('error', listener) | ||
return () => { | ||
this.throwsError = true | ||
unbind() | ||
} | ||
} | ||
@@ -146,2 +155,9 @@ | ||
setLocalHeaders (headers) { | ||
this.localHeaders = headers | ||
if (this.connected) { | ||
this.sendHeaders(headers) | ||
} | ||
} | ||
send (msg) { | ||
@@ -266,11 +282,16 @@ if (!this.connected) return | ||
if (this.options.outFilter) { | ||
promises.push(this.options.outFilter(action, meta).then(r => { | ||
if (r) { | ||
return [action, meta] | ||
} else { | ||
return false | ||
} | ||
}).catch(e => { | ||
this.error(e) | ||
})) | ||
promises.push( | ||
this.options | ||
.outFilter(action, meta) | ||
.then(r => { | ||
if (r) { | ||
return [action, meta] | ||
} else { | ||
return false | ||
} | ||
}) | ||
.catch(e => { | ||
this.error(e) | ||
}) | ||
) | ||
} else { | ||
@@ -299,9 +320,13 @@ promises.push(Promise.resolve([action, meta])) | ||
if (this.options.outMap) { | ||
Promise.all(data.entries.map(i => { | ||
return this.options.outMap(i[0], i[1]) | ||
})).then(changed => { | ||
this.sendSync(data.added, changed) | ||
}).catch(e => { | ||
this.error(e) | ||
}) | ||
Promise.all( | ||
data.entries.map(i => { | ||
return this.options.outMap(i[0], i[1]) | ||
}) | ||
) | ||
.then(changed => { | ||
this.sendSync(data.added, changed) | ||
}) | ||
.catch(e => { | ||
this.error(e) | ||
}) | ||
} else { | ||
@@ -374,2 +399,5 @@ this.sendSync(data.added, data.entries) | ||
BaseNode.prototype.sendHeaders = sendHeaders | ||
BaseNode.prototype.headersMessage = headersMessage | ||
const DUILIANS = { | ||
@@ -376,0 +404,0 @@ 金木水火土: '板城烧锅酒' |
# Change Log | ||
This project adheres to [Semantic Versioning](http://semver.org/). | ||
## 0.6 ᐁ | ||
* Use WebSocket Protocol version 4. | ||
* Remove `reasons: string` support. It must be always an array. | ||
* Add `parseId()` helper. | ||
* Add headers (by Ivan Menshykov). | ||
* Add `MemoryStore#entries`. | ||
* Allow to pass `undefined` to `isFirstOlder()`. | ||
* Return unbind function from `Node#catch`. | ||
* Rename `WsConnection#WS` to `WsConnection#Class`. | ||
* Rename `Store` type to `LogStore`. | ||
* Fix WebSocket connectivity. | ||
* Improve types (by Nikolay Govorov). | ||
## 0.5.3 | ||
@@ -5,0 +18,0 @@ * Fix types. |
import { BaseNode } from '../base-node' | ||
import { Meta } from '../log' | ||
import { Log, Meta } from '../log' | ||
@@ -18,2 +18,5 @@ /** | ||
*/ | ||
export class ClientNode<M extends Meta = Meta> extends BaseNode<M> { } | ||
export class ClientNode< | ||
H extends object = {}, | ||
L extends Log = Log<Meta> | ||
> extends BaseNode<H, L> {} |
@@ -10,3 +10,3 @@ import { BaseNode } from '../base-node/index.js' | ||
class ClientNode extends BaseNode { | ||
constructor (nodeId, log, connection, options = { }) { | ||
constructor (nodeId, log, connection, options = {}) { | ||
options = { ...DEFAULT_OPTIONS, ...options } | ||
@@ -13,0 +13,0 @@ super(nodeId, log, connection, options) |
@@ -11,3 +11,3 @@ import { LoguxError } from '../logux-error/index.js' | ||
try { | ||
let access = await node.options.auth(nodeId, token) | ||
let access = await node.options.auth(nodeId, token, node.remoteHeaders) | ||
if (access) { | ||
@@ -40,5 +40,8 @@ node.authenticated = true | ||
} else { | ||
node.sendError(new LoguxError('wrong-protocol', { | ||
supported: node.minProtocol, used: ver | ||
})) | ||
node.sendError( | ||
new LoguxError('wrong-protocol', { | ||
supported: node.minProtocol, | ||
used: ver | ||
}) | ||
) | ||
node.destroy() | ||
@@ -71,3 +74,3 @@ return false | ||
let options = { } | ||
let options = {} | ||
if (this.options.token) { | ||
@@ -86,2 +89,7 @@ if (typeof this.options.token === 'function') { | ||
if (this.options.fixTime) this.connectSended = this.now() | ||
if (Object.keys(this.localHeaders).length > 0) { | ||
this.sendHeaders(this.localHeaders) | ||
} | ||
this.startTimeout() | ||
@@ -99,3 +107,3 @@ this.send(message) | ||
let options = { } | ||
let options = {} | ||
if (this.options.token) { | ||
@@ -113,2 +121,6 @@ if (typeof this.options.token === 'function') { | ||
if (Object.keys(this.localHeaders).length > 0) { | ||
this.sendHeaders(this.localHeaders) | ||
} | ||
this.send(message) | ||
@@ -119,3 +131,3 @@ } | ||
let start = this.now() | ||
if (!options) options = { } | ||
if (!options) options = {} | ||
@@ -140,3 +152,3 @@ this.remoteNodeId = nodeId | ||
function connectedMessage (ver, nodeId, time, options) { | ||
if (!options) options = { } | ||
if (!options) options = {} | ||
@@ -153,3 +165,3 @@ this.endTimeout() | ||
let roundTrip = now - this.connectSended - authTime | ||
this.timeFix = Math.floor(this.connectSended - time[0] + (roundTrip / 2)) | ||
this.timeFix = Math.floor(this.connectSended - time[0] + roundTrip / 2) | ||
} | ||
@@ -170,3 +182,6 @@ | ||
export { | ||
sendConnect, sendConnected, connectMessage, connectedMessage | ||
sendConnect, | ||
sendConnected, | ||
connectMessage, | ||
connectedMessage | ||
} |
@@ -1,3 +0,7 @@ | ||
import { Store } from '../log' | ||
import { LogStore } from '../log' | ||
interface storeCreator { | ||
(): LogStore | ||
} | ||
/** | ||
@@ -17,3 +21,6 @@ * Pass all common tests for Logux store to callback. | ||
export function eachStoreCheck ( | ||
test: (name: string, creator: () => Store) => void | ||
test: ( | ||
name: string, | ||
testCreator: (storeCreator: storeCreator) => () => void | ||
) => void | ||
): void |
@@ -38,6 +38,3 @@ import assert from 'assert' | ||
let store = factory() | ||
await Promise.all([ | ||
checkLastAdded(store, 0), | ||
checkLastSynced(store, 0, 0) | ||
]) | ||
await Promise.all([checkLastAdded(store, 0), checkLastSynced(store, 0, 0)]) | ||
}) | ||
@@ -92,7 +89,7 @@ | ||
let store = factory() | ||
await store.add({ }, { id: '1 n 0', time: 1, a: 1 }) | ||
await store.add({}, { id: '1 n 0', time: 1, a: 1 }) | ||
let result = await store.changeMeta('1 n 0', { a: 2, b: 2 }) | ||
assert.strictEqual(result, true) | ||
await checkBoth(store, [ | ||
[{ }, { id: '1 n 0', time: 1, added: 1, a: 2, b: 2 }] | ||
[{}, { id: '1 n 0', time: 1, added: 1, a: 2, b: 2 }] | ||
]) | ||
@@ -120,3 +117,4 @@ }) | ||
assert.deepStrictEqual(result, [ | ||
{ type: '2' }, { id: '1 node1 1', time: 2, added: 2 } | ||
{ type: '2' }, | ||
{ id: '1 node1 1', time: 2, added: 2 } | ||
]) | ||
@@ -165,3 +163,3 @@ await checkBoth(store, [ | ||
]) | ||
await store.removeReason('a', { }, (action, meta) => { | ||
await store.removeReason('a', {}, (action, meta) => { | ||
removed.push([action, meta]) | ||
@@ -263,6 +261,6 @@ }) | ||
let store = factory() | ||
await store.add({ }, { id: '1 n 0', time: 1, reasons: ['a'] }) | ||
await store.add({}, { id: '1 n 0', time: 1, reasons: ['a'] }) | ||
await store.removeReason('a', { maxAdded: 0 }, () => {}) | ||
await checkBoth(store, [ | ||
[{ }, { added: 1, id: '1 n 0', time: 1, reasons: ['a'] }] | ||
[{}, { added: 1, id: '1 n 0', time: 1, reasons: ['a'] }] | ||
]) | ||
@@ -321,5 +319,3 @@ }) | ||
assert.ok(!meta2) | ||
await checkBoth(store, [ | ||
[{ a: 1 }, { id, time: 1, added: 1 }] | ||
]) | ||
await checkBoth(store, [[{ a: 1 }, { id, time: 1, added: 1 }]]) | ||
}) | ||
@@ -351,20 +347,16 @@ | ||
let store = factory() | ||
if (typeof store.clean === 'undefined') { | ||
await Promise.resolve() | ||
} else { | ||
await Promise.all([ | ||
store.add({ type: 'A' }, { id: '1', time: 1 }), | ||
store.add({ type: 'B' }, { id: '2', time: 2 }), | ||
store.add({ type: 'C' }, { id: '3', time: 3 }), | ||
store.add({ type: 'D' }, { id: '4', time: 4 }), | ||
store.add({ type: 'E' }, { id: '5', time: 5 }) | ||
]) | ||
await store.clean() | ||
let another = factory() | ||
await Promise.all([ | ||
checkBoth(another, []), | ||
checkLastAdded(another, 0), | ||
checkLastSynced(another, 0, 0) | ||
]) | ||
} | ||
await Promise.all([ | ||
store.add({ type: 'A' }, { id: '1', time: 1 }), | ||
store.add({ type: 'B' }, { id: '2', time: 2 }), | ||
store.add({ type: 'C' }, { id: '3', time: 3 }), | ||
store.add({ type: 'D' }, { id: '4', time: 4 }), | ||
store.add({ type: 'E' }, { id: '5', time: 5 }) | ||
]) | ||
await store.clean() | ||
let another = factory() | ||
await Promise.all([ | ||
checkBoth(another, []), | ||
checkLastAdded(another, 0), | ||
checkLastSynced(another, 0, 0) | ||
]) | ||
}) | ||
@@ -371,0 +363,0 @@ } |
@@ -1,3 +0,10 @@ | ||
export { Log, ID, Action, AnyAction, Meta, Store } from './log' | ||
export { BaseNode, Connection, TokenGenerator } from './base-node' | ||
export { | ||
BaseNode, | ||
Connection, | ||
TokenGenerator, | ||
NodeOptions, | ||
NodeState, | ||
Message | ||
} from './base-node' | ||
export { Log, ID, Action, AnyAction, Meta, LogStore, Page } from './log' | ||
export { LoguxError, LoguxErrorOptions } from './logux-error' | ||
@@ -15,2 +22,3 @@ export { ServerConnection } from './server-connection' | ||
export { TestTime } from './test-time' | ||
export { parseId } from './parse-id' | ||
export { TestLog } from './test-log' |
@@ -14,2 +14,3 @@ import { ServerConnection } from './server-connection/index.js' | ||
import { TestPair } from './test-pair/index.js' | ||
import { parseId } from './parse-id/index.js' | ||
import { Log } from './log/index.js' | ||
@@ -31,3 +32,4 @@ | ||
TestPair, | ||
parseId, | ||
Log | ||
} |
@@ -19,2 +19,5 @@ import { Meta } from '..' | ||
*/ | ||
export function isFirstOlder(firstMeta: Meta, secondMeta: Meta): boolean | ||
export function isFirstOlder ( | ||
firstMeta: Meta | undefined, | ||
secondMeta: Meta | undefined | ||
): boolean |
import { Connection } from '../base-node' | ||
export class LocalConnection extends Connection { | ||
other (): LocalConnection | ||
} | ||
/** | ||
@@ -31,3 +35,3 @@ * Two paired loopback connections. | ||
*/ | ||
left: Connection | ||
left: LocalConnection | ||
@@ -41,3 +45,3 @@ /** | ||
*/ | ||
right: Connection | ||
right: LocalConnection | ||
} |
@@ -24,3 +24,3 @@ import { Unsubscribe } from 'nanoevents' | ||
*/ | ||
added: number, | ||
added: number | ||
@@ -30,3 +30,3 @@ /** | ||
*/ | ||
time: number, | ||
time: number | ||
@@ -36,3 +36,3 @@ /** | ||
*/ | ||
id: ID, | ||
id: ID | ||
@@ -47,3 +47,3 @@ /** | ||
*/ | ||
subprotocol?: string, | ||
subprotocol?: string | ||
@@ -85,3 +85,3 @@ /** | ||
*/ | ||
olderThan?: Meta, | ||
olderThan?: Meta | ||
@@ -91,3 +91,3 @@ /** | ||
*/ | ||
youngerThan?: Meta, | ||
youngerThan?: Meta | ||
@@ -112,3 +112,3 @@ /** | ||
type Page = { | ||
export type Page = { | ||
/** | ||
@@ -122,3 +122,3 @@ * Pagination page. | ||
*/ | ||
next? (): Promise<Page> | ||
next?(): Promise<Page> | ||
} | ||
@@ -136,3 +136,3 @@ | ||
*/ | ||
export abstract class Store { | ||
export abstract class LogStore { | ||
/** | ||
@@ -146,3 +146,3 @@ * Add action to store. Action always will have `type` property. | ||
*/ | ||
add (action: Action, meta: Meta): Promise<Meta|false> | ||
add (action: Action, meta: Meta): Promise<Meta | false> | ||
@@ -168,3 +168,3 @@ /** | ||
*/ | ||
remove (id: ID): Promise<[Action, Meta]|false> | ||
remove (id: ID): Promise<[Action, Meta] | false> | ||
@@ -187,3 +187,3 @@ /** | ||
*/ | ||
byId (id: ID): Promise<[Action, Meta]|[null, null]> | ||
byId (id: ID): Promise<[Action, Meta] | [null, null]> | ||
@@ -199,3 +199,5 @@ /** | ||
removeReason ( | ||
reason: string, criteria: Criteria, callback: ActionListener<Meta> | ||
reason: string, | ||
criteria: Criteria, | ||
callback: ActionListener<Meta> | ||
): Promise<void> | ||
@@ -233,7 +235,8 @@ | ||
type LogOptions = { | ||
type LogOptions<S extends LogStore = LogStore> = { | ||
/** | ||
* Store for log. | ||
*/ | ||
store: Store, | ||
store: S | ||
/** | ||
@@ -262,9 +265,14 @@ * Unique current machine name. | ||
*/ | ||
export class Log<M extends Meta = Meta> { | ||
export class Log<M extends Meta = Meta, S extends LogStore = LogStore> { | ||
/** | ||
* @param opts Log options. | ||
*/ | ||
constructor (opts: LogOptions) | ||
constructor (opts: LogOptions<S>) | ||
/** | ||
* Log store. | ||
*/ | ||
store: S | ||
/** | ||
* Unique node ID. It is used in action IDs. | ||
@@ -292,3 +300,3 @@ */ | ||
*/ | ||
add (action: Action, meta?: Partial<M>): Promise<M|false> | ||
add (action: Action, meta?: Partial<M>): Promise<M | false> | ||
@@ -317,3 +325,4 @@ /** | ||
on ( | ||
event: 'preadd' | 'add' | 'clean', listener: ActionListener<M> | ||
event: 'preadd' | 'add' | 'clean', | ||
listener: ActionListener<M> | ||
): Unsubscribe | ||
@@ -349,7 +358,13 @@ | ||
* | ||
* @param opts Iterator options. | ||
* @param callback Function will be executed on every action. | ||
* @returns When iteration will be finished by iterator or end of actions. | ||
*/ | ||
each (callback: ActionIterator<M>): Promise<void> | ||
/** | ||
* @param opts Iterator options. | ||
* @param callback Function will be executed on every action. | ||
*/ | ||
each (opts: GetOptions, callback: ActionIterator<M>): Promise<void> | ||
each (callback: ActionIterator<M>): Promise<void> | ||
@@ -385,3 +400,3 @@ /** | ||
*/ | ||
removeReason (reason: string, criteria: Criteria): Promise<void> | ||
removeReason (reason: string, criteria?: Criteria): Promise<void> | ||
@@ -401,3 +416,3 @@ /** | ||
*/ | ||
byId (id: ID): Promise<[Action, M]|[null, null]> | ||
byId (id: ID): Promise<[Action, M] | [null, null]> | ||
} |
import { createNanoEvents } from 'nanoevents' | ||
class Log { | ||
constructor (opts = { }) { | ||
constructor (opts = {}) { | ||
if (typeof opts.nodeId === 'undefined') { | ||
@@ -29,3 +29,3 @@ throw new Error('Expected node ID') | ||
async add (action, meta = { }) { | ||
async add (action, meta = {}) { | ||
if (typeof action.type === 'undefined') { | ||
@@ -48,3 +48,3 @@ throw new Error('Expected "type" in action') | ||
} else if (!Array.isArray(meta.reasons)) { | ||
meta.reasons = [meta.reasons] | ||
throw new Error('Expected "reasons" to be an array of strings') | ||
} | ||
@@ -54,3 +54,3 @@ | ||
if (typeof reason !== 'string') { | ||
throw new Error('Expected "reasons" to be strings') | ||
throw new Error('Expected "reasons" to be an array of strings') | ||
} | ||
@@ -149,3 +149,3 @@ } | ||
removeReason (reason, criteria = { }) { | ||
removeReason (reason, criteria = {}) { | ||
return this.store.removeReason(reason, criteria, (action, meta) => { | ||
@@ -152,0 +152,0 @@ this.emitter.emit('clean', action, meta) |
@@ -1,10 +0,13 @@ | ||
type Versions = { supported: string, used: string } | ||
type Versions = { | ||
supported: string | ||
used: string | ||
} | ||
export type LoguxErrorOptions = { | ||
'timeout': number, | ||
'bruteforce': void, | ||
'wrong-format': string, | ||
'wrong-protocol': Versions, | ||
'unknown-message': string, | ||
'wrong-credentials': void, | ||
'timeout': number | ||
'bruteforce': void | ||
'wrong-format': string | ||
'wrong-protocol': Versions | ||
'unknown-message': string | ||
'wrong-credentials': void | ||
'wrong-subprotocol': Versions | ||
@@ -37,5 +40,6 @@ } | ||
*/ | ||
static description< | ||
T extends keyof LoguxErrorOptions | ||
>(type: T, options?: LoguxErrorOptions[T]): string | ||
static description<T extends keyof LoguxErrorOptions> ( | ||
type: T, | ||
options?: LoguxErrorOptions[T] | ||
): string | ||
@@ -47,3 +51,3 @@ /** | ||
*/ | ||
constructor(type: T, options?: LoguxErrorOptions[T], received?: boolean) | ||
constructor (type: T, options?: LoguxErrorOptions[T], received?: boolean) | ||
@@ -50,0 +54,0 @@ /** |
class LoguxError extends Error { | ||
static describe (type, options) { | ||
if (type === 'timeout') { | ||
return 'A timeout was reached (' + options + 'ms)' | ||
return 'A timeout was reached (' + options + ' ms)' | ||
} else if (type === 'wrong-format') { | ||
@@ -12,7 +12,11 @@ return 'Wrong message format in ' + options | ||
} else if (type === 'wrong-protocol') { | ||
return 'Logux supports protocols only from version ' + options.supported + | ||
', but you use ' + options.used | ||
return ( | ||
`Logux supports protocols only from version ${options.supported}` + | ||
`, but you use ${options.used}` | ||
) | ||
} else if (type === 'wrong-subprotocol') { | ||
return 'Only ' + options.supported + ' application subprotocols are ' + | ||
'supported, but you use ' + options.used | ||
return ( | ||
`Only ${options.supported} application subprotocols are ` + | ||
`supported, but you use ${options.used}` | ||
) | ||
} else if (type === 'wrong-credentials') { | ||
@@ -19,0 +23,0 @@ return 'Wrong credentials' |
@@ -1,2 +0,2 @@ | ||
import { Store } from '../log' | ||
import { LogStore, Action, Meta } from '../log' | ||
@@ -18,2 +18,7 @@ /** | ||
*/ | ||
export class MemoryStore extends Store { } | ||
export class MemoryStore extends LogStore { | ||
/** | ||
* Actions in the store. | ||
*/ | ||
entries: [Action, Meta][] | ||
} |
@@ -25,3 +25,3 @@ import { isFirstOlder } from '../is-first-older/index.js' | ||
constructor () { | ||
this.created = [] | ||
this.entries = [] | ||
this.added = [] | ||
@@ -37,3 +37,3 @@ this.lastReceived = 0 | ||
let list = this.created | ||
let list = this.entries | ||
for (let i = 0; i < list.length; i++) { | ||
@@ -54,7 +54,7 @@ let [, otherMeta] = list[i] | ||
async byId (id) { | ||
let created = find(this.created, id) | ||
let created = find(this.entries, id) | ||
if (created === -1) { | ||
return [null, null] | ||
} else { | ||
let [action, meta] = this.created[created] | ||
let [action, meta] = this.entries[created] | ||
return [action, meta] | ||
@@ -66,8 +66,8 @@ } | ||
if (typeof created === 'undefined') { | ||
created = find(this.created, id) | ||
created = find(this.entries, id) | ||
if (created === -1) return Promise.resolve(false) | ||
} | ||
let entry = [this.created[created][0], this.created[created][1]] | ||
this.created.splice(created, 1) | ||
let entry = [this.entries[created][0], this.entries[created][1]] | ||
this.entries.splice(created, 1) | ||
@@ -96,3 +96,3 @@ let added = entry[1].added | ||
if (opts.order === 'created') { | ||
entries = this.created | ||
entries = this.entries | ||
} else { | ||
@@ -105,7 +105,7 @@ entries = this.added | ||
async changeMeta (id, diff) { | ||
let index = find(this.created, id) | ||
let index = find(this.entries, id) | ||
if (index === -1) { | ||
return false | ||
} else { | ||
let meta = this.created[index][1] | ||
let meta = this.entries[index][1] | ||
for (let key in diff) meta[key] = diff[key] | ||
@@ -120,5 +120,5 @@ return true | ||
if (criteria.id) { | ||
let index = find(this.created, criteria.id) | ||
let index = find(this.entries, criteria.id) | ||
if (index !== -1) { | ||
let meta = this.created[index][1] | ||
let meta = this.entries[index][1] | ||
let reasonPos = meta.reasons.indexOf(reason) | ||
@@ -128,3 +128,3 @@ if (reasonPos !== -1) { | ||
if (meta.reasons.length === 0) { | ||
callback(this.created[index][0], meta) | ||
callback(this.entries[index][0], meta) | ||
this.remove(criteria.id) | ||
@@ -135,3 +135,3 @@ } | ||
} else { | ||
this.created = this.created.filter(([action, meta]) => { | ||
this.entries = this.entries.filter(([action, meta]) => { | ||
let c = criteria | ||
@@ -170,3 +170,3 @@ | ||
async clean () { | ||
this.created = [] | ||
this.entries = [] | ||
this.added = [] | ||
@@ -173,0 +173,0 @@ this.lastReceived = 0 |
{ | ||
"name": "@logux/core", | ||
"version": "0.5.3", | ||
"version": "0.6.0", | ||
"description": "Logux core components", | ||
@@ -23,3 +23,3 @@ "keywords": [ | ||
"dependencies": { | ||
"nanoevents": "^5.1.5" | ||
"nanoevents": "^5.1.7" | ||
}, | ||
@@ -66,2 +66,7 @@ "type": "module", | ||
}, | ||
"./headers/package.json": "./headers/package.json", | ||
"./headers": { | ||
"require": "./headers/index.cjs", | ||
"import": "./headers/index.js" | ||
}, | ||
"./is-first-older/package.json": "./is-first-older/package.json", | ||
@@ -92,2 +97,7 @@ "./is-first-older": { | ||
}, | ||
"./parse-id/package.json": "./parse-id/package.json", | ||
"./parse-id": { | ||
"require": "./parse-id/index.cjs", | ||
"import": "./parse-id/index.js" | ||
}, | ||
"./ping/package.json": "./ping/package.json", | ||
@@ -94,0 +104,0 @@ "./ping": { |
@@ -10,3 +10,8 @@ # Logux Core [![Cult Of Martians][cult-img]][cult] | ||
**Documentation: [logux.io]** | ||
* **[Guide, recipes, and API](https://logux.io/)** | ||
* **[Chat](https://gitter.im/logux/logux)** for any questions | ||
* **[Issues](https://github.com/logux/logux/issues)** | ||
and **[roadmap](https://github.com/logux/logux/projects/1)** | ||
* **[Projects](https://logux.io/guide/architecture/parts/)** | ||
inside Logux ecosystem | ||
@@ -13,0 +18,0 @@ This repository contains Logux core components for JavaScript: |
@@ -31,8 +31,12 @@ import { Connection } from '../base-node' | ||
/** | ||
* @param connection The connection to be reconnectable. | ||
* @param options Reconnection options. | ||
* Reconnection options. | ||
*/ | ||
constructor (connection: Connection, options?: ReconnectOptions) | ||
options: ReconnectOptions | ||
/** | ||
* Fails attempts since the last connected state. | ||
*/ | ||
attempts: number | ||
/** | ||
* Should we reconnect connection on next connection break. | ||
@@ -50,6 +54,22 @@ * Next `connect` call will set to `true`. | ||
/** | ||
* Are we in the middle of connecting. | ||
*/ | ||
connecting: boolean | ||
/** | ||
* Wrapped connection. | ||
*/ | ||
connection: Connection | ||
/** | ||
* @param connection The connection to be reconnectable. | ||
* @param options Reconnection options. | ||
*/ | ||
constructor (connection: Connection, options?: ReconnectOptions) | ||
/** | ||
* Unbind all listeners and disconnect. Use it if you will not need | ||
* this class anymore. | ||
*/ | ||
destroy (): void | ||
destroy: () => void | ||
} |
@@ -14,3 +14,3 @@ const DEFAULT_OPTIONS = { | ||
class Reconnect { | ||
constructor (connection, options = { }) { | ||
constructor (connection, options = {}) { | ||
this.connection = connection | ||
@@ -23,23 +23,23 @@ this.options = { ...DEFAULT_OPTIONS, ...options } | ||
this.unbind = [] | ||
this.unbind.push(this.connection.on('message', msg => { | ||
if (msg[0] === 'error' && FATAL_ERRORS.includes(msg[1])) { | ||
this.reconnecting = false | ||
this.unbind = [ | ||
this.connection.on('message', msg => { | ||
if (msg[0] === 'error' && FATAL_ERRORS.includes(msg[1])) { | ||
this.reconnecting = false | ||
} | ||
}), | ||
this.connection.on('connecting', () => { | ||
this.connecting = true | ||
}), | ||
this.connection.on('connect', () => { | ||
this.attempts = 0 | ||
this.connecting = false | ||
}), | ||
this.connection.on('disconnect', () => { | ||
this.connecting = false | ||
if (this.reconnecting) this.reconnect() | ||
}), | ||
() => { | ||
clearTimeout(this.timer) | ||
} | ||
})) | ||
this.unbind.push(this.connection.on('connecting', () => { | ||
this.connecting = true | ||
})) | ||
this.unbind.push(this.connection.on('connect', () => { | ||
this.attempts = 0 | ||
this.connecting = false | ||
})) | ||
this.unbind.push(this.connection.on('disconnect', () => { | ||
this.connecting = false | ||
if (this.reconnecting) this.reconnect() | ||
})) | ||
this.unbind.push(() => { | ||
clearTimeout(this.timer) | ||
}) | ||
] | ||
@@ -46,0 +46,0 @@ let visibility = () => { |
import { Connection } from '../base-node' | ||
import WebSocket = require('ws') | ||
/** | ||
@@ -18,5 +20,10 @@ * Logux connection for server WebSocket. | ||
/** | ||
* @param ws WebSocket instance | ||
* WebSocket connection instance | ||
*/ | ||
constructor (ws: object) | ||
ws: WebSocket | ||
/** | ||
* @param ws WebSocket connection instance | ||
*/ | ||
constructor (ws: WebSocket) | ||
} |
@@ -13,3 +13,3 @@ import { WsConnection } from '../ws-connection/index.js' | ||
'ServerConnection accepts already connected WebSocket ' + | ||
'instance and could not reconnect it' | ||
'instance and could not reconnect it' | ||
) | ||
@@ -16,0 +16,0 @@ } |
import { BaseNode } from '../base-node' | ||
import { Meta } from '../log' | ||
import { Log, Meta } from '../log' | ||
@@ -20,2 +20,5 @@ /** | ||
*/ | ||
export class ServerNode<M extends Meta = Meta> extends BaseNode<M> { } | ||
export class ServerNode< | ||
H extends object = {}, | ||
L extends Log = Log<Meta> | ||
> extends BaseNode<H, L> {} |
@@ -10,3 +10,3 @@ import { BaseNode } from '../base-node/index.js' | ||
class ServerNode extends BaseNode { | ||
constructor (nodeId, log, connection, options = { }) { | ||
constructor (nodeId, log, connection, options = {}) { | ||
options = { ...DEFAULT_OPTIONS, ...options } | ||
@@ -13,0 +13,0 @@ super(nodeId, log, connection, options) |
@@ -6,3 +6,3 @@ function sendSync (added, entries) { | ||
for (let [action, originMeta] of entries) { | ||
let meta = { } | ||
let meta = {} | ||
for (let key in originMeta) { | ||
@@ -49,3 +49,3 @@ if (key === 'id') { | ||
if (typeof meta.id === 'number') { | ||
meta.id = (meta.id + this.baseTime) + ' ' + this.remoteNodeId + ' ' + 0 | ||
meta.id = meta.id + this.baseTime + ' ' + this.remoteNodeId + ' ' + 0 | ||
} else { | ||
@@ -66,25 +66,32 @@ meta.id[0] = meta.id[0] + this.baseTime | ||
if (this.options.inMap) { | ||
process = process.then(([action2, meta2]) => { | ||
return this.options.inMap(action2, meta2) | ||
}).catch(e => { | ||
this.error(e) | ||
}) | ||
} | ||
process.then(filtered => { | ||
if (filtered && this.options.inFilter) { | ||
return this.options.inFilter(...filtered).then(res => { | ||
return res ? filtered : false | ||
}).catch(e => { | ||
process = process | ||
.then(([action2, meta2]) => { | ||
return this.options.inMap(action2, meta2) | ||
}) | ||
.catch(e => { | ||
this.error(e) | ||
}) | ||
} else { | ||
return filtered | ||
} | ||
}).then(changed => { | ||
if (!changed) return false | ||
if (this.received) this.received[changed[1].id] = true | ||
return this.log.add(changed[0], changed[1]) | ||
}) | ||
} | ||
process | ||
.then(filtered => { | ||
if (filtered && this.options.inFilter) { | ||
return this.options | ||
.inFilter(...filtered) | ||
.then(res => { | ||
return res ? filtered : false | ||
}) | ||
.catch(e => { | ||
this.error(e) | ||
}) | ||
} else { | ||
return filtered | ||
} | ||
}) | ||
.then(changed => { | ||
if (!changed) return false | ||
if (this.received) this.received[changed[1].id] = true | ||
return this.log.add(changed[0], changed[1]) | ||
}) | ||
promises.push(process) | ||
@@ -91,0 +98,0 @@ } |
@@ -23,3 +23,3 @@ import { Log, Action, Meta } from '../log' | ||
*/ | ||
export class TestLog extends Log { | ||
export class TestLog<M extends Meta = Meta> extends Log<M> { | ||
/** | ||
@@ -49,3 +49,3 @@ * Return all entries (with metadata) inside log, sorted by created time. | ||
*/ | ||
actions: Action[] | ||
actions (): Action[] | ||
} |
@@ -5,3 +5,3 @@ import { MemoryStore } from '../memory-store/index.js' | ||
class TestLog extends Log { | ||
constructor (time, id, opts = { }) { | ||
constructor (time, id, opts = {}) { | ||
if (!opts.store) opts.store = new MemoryStore() | ||
@@ -17,3 +17,3 @@ if (typeof opts.nodeId === 'undefined') { | ||
entries () { | ||
return this.store.created | ||
return this.store.entries | ||
} | ||
@@ -20,0 +20,0 @@ |
@@ -0,3 +1,4 @@ | ||
import { BaseNode, Message } from '../base-node' | ||
import { LocalPair } from '../local-pair' | ||
import { BaseNode } from '../base-node' | ||
import { TestLog } from '../test-log' | ||
@@ -32,3 +33,3 @@ /** | ||
*/ | ||
leftNode: BaseNode | ||
leftNode: BaseNode<{}, TestLog> | ||
@@ -46,3 +47,3 @@ /** | ||
*/ | ||
rightNode: BaseNode | ||
rightNode: BaseNode<{}, TestLog> | ||
@@ -57,3 +58,3 @@ /** | ||
*/ | ||
leftSent: string[][] | ||
leftSent: Message[] | ||
@@ -68,3 +69,3 @@ /** | ||
*/ | ||
rightSent: string[][] | ||
rightSent: Message[] | ||
@@ -117,3 +118,3 @@ /** | ||
*/ | ||
wait (receiver: 'left' | 'right'): Promise<void> | ||
wait (receiver?: 'left' | 'right'): Promise<void> | ||
} |
@@ -7,3 +7,3 @@ import { LocalPair } from '../local-pair/index.js' | ||
} else if (typeof obj === 'object') { | ||
let cloned = { } | ||
let cloned = {} | ||
for (let i in obj) { | ||
@@ -26,32 +26,34 @@ cloned[i] = clone(obj[i]) | ||
this.left.on('connect', () => { | ||
this.leftEvents.push(['connect']) | ||
if (this.waiting) this.waiting('left') | ||
}) | ||
this.right.on('connect', () => { | ||
this.rightEvents.push(['connect']) | ||
if (this.waiting) this.waiting('right') | ||
}) | ||
this.unbind = [ | ||
this.left.on('connect', () => { | ||
this.leftEvents.push(['connect']) | ||
if (this.waiting) this.waiting('left') | ||
}), | ||
this.right.on('connect', () => { | ||
this.rightEvents.push(['connect']) | ||
if (this.waiting) this.waiting('right') | ||
}), | ||
this.left.on('message', msg => { | ||
let cloned = clone(msg) | ||
this.rightSent.push(cloned) | ||
this.leftEvents.push(['message', cloned]) | ||
if (this.waiting) this.waiting('left') | ||
}) | ||
this.right.on('message', msg => { | ||
let cloned = clone(msg) | ||
this.leftSent.push(cloned) | ||
this.rightEvents.push(['message', cloned]) | ||
if (this.waiting) this.waiting('right') | ||
}) | ||
this.left.on('message', msg => { | ||
let cloned = clone(msg) | ||
this.rightSent.push(cloned) | ||
this.leftEvents.push(['message', cloned]) | ||
if (this.waiting) this.waiting('left') | ||
}), | ||
this.right.on('message', msg => { | ||
let cloned = clone(msg) | ||
this.leftSent.push(cloned) | ||
this.rightEvents.push(['message', cloned]) | ||
if (this.waiting) this.waiting('right') | ||
}), | ||
this.left.on('disconnect', reason => { | ||
this.leftEvents.push(['disconnect', reason]) | ||
if (this.waiting) this.waiting('left') | ||
}) | ||
this.right.on('disconnect', () => { | ||
this.rightEvents.push(['disconnect']) | ||
if (this.waiting) this.waiting('right') | ||
}) | ||
this.left.on('disconnect', reason => { | ||
this.leftEvents.push(['disconnect', reason]) | ||
if (this.waiting) this.waiting('left') | ||
}), | ||
this.right.on('disconnect', () => { | ||
this.rightEvents.push(['disconnect']) | ||
if (this.waiting) this.waiting('right') | ||
}) | ||
] | ||
} | ||
@@ -58,0 +60,0 @@ |
import { TestLog } from '../test-log' | ||
import { Store } from '../log' | ||
import { LogStore } from '../log' | ||
@@ -8,7 +8,7 @@ type TestLogOptions = { | ||
*/ | ||
nodeId: string, | ||
nodeId: string | ||
/** | ||
* Store for log. Will use {@link MemoryStore} by default. | ||
*/ | ||
store: Store | ||
store: LogStore | ||
} | ||
@@ -53,3 +53,3 @@ | ||
*/ | ||
static getLog (opts: TestLogOptions): TestLog | ||
static getLog (opts?: TestLogOptions): TestLog | ||
@@ -76,3 +76,3 @@ constructor () | ||
*/ | ||
nextLog (opts: TestLogOptions): TestLog | ||
nextLog (opts?: TestLogOptions): TestLog | ||
} |
@@ -20,14 +20,26 @@ import { LoguxError } from '../logux-error/index.js' | ||
function isTwoNumbers (value) { | ||
return isArray(value) && value.length === 2 && | ||
isNumber(value[0]) && isNumber(value[1]) | ||
return ( | ||
isArray(value) && | ||
value.length === 2 && | ||
isNumber(value[0]) && | ||
isNumber(value[1]) | ||
) | ||
} | ||
function isID (value) { | ||
return isArray(value) && value.length === 3 && | ||
isNumber(value[0]) && isString(value[1]) && isNumber(value[2]) | ||
return ( | ||
isArray(value) && | ||
value.length === 3 && | ||
isNumber(value[0]) && | ||
isString(value[1]) && | ||
isNumber(value[2]) | ||
) | ||
} | ||
function isMeta (value) { | ||
return isObject(value) && isNumber(value.time) && | ||
(isNumber(value.id) || isTwoNumbers(value.id) || isID(value.id)) | ||
return ( | ||
isObject(value) && | ||
isNumber(value.time) && | ||
(isNumber(value.id) || isTwoNumbers(value.id) || isID(value.id)) | ||
) | ||
} | ||
@@ -37,9 +49,17 @@ | ||
connect (msg) { | ||
return isNumber(msg[1]) && isString(msg[2]) && isNumber(msg[3]) && | ||
return ( | ||
isNumber(msg[1]) && | ||
isString(msg[2]) && | ||
isNumber(msg[3]) && | ||
(msg.length === 4 || (msg.length === 5 && isObject(msg[4]))) | ||
) | ||
}, | ||
connected (msg) { | ||
return isNumber(msg[1]) && isString(msg[2]) && isTwoNumbers(msg[3]) && | ||
return ( | ||
isNumber(msg[1]) && | ||
isString(msg[2]) && | ||
isTwoNumbers(msg[3]) && | ||
(msg.length === 4 || (msg.length === 5 && isObject(msg[4]))) | ||
) | ||
}, | ||
@@ -79,10 +99,17 @@ | ||
duilian (msg) { | ||
return (msg.length === 2) && isString(msg[1]) | ||
return msg.length === 2 && isString(msg[1]) | ||
}, | ||
debug (msg) { | ||
return (msg.length === 3) && isString(msg[1]) && | ||
(msg[1] === 'error' && isString(msg[2])) | ||
return ( | ||
msg.length === 3 && | ||
isString(msg[1]) && | ||
msg[1] === 'error' && | ||
isString(msg[2]) | ||
) | ||
}, | ||
headers (msg) { | ||
return msg.length === 2 && isObject(msg[1]) | ||
} | ||
} | ||
@@ -89,0 +116,0 @@ |
import { Connection } from '../base-node' | ||
import WebSocket = require('ws') | ||
/** | ||
@@ -13,4 +15,9 @@ * Logux connection for browser WebSocket. | ||
*/ | ||
export class WsConnection extends Connection { | ||
export class WsConnection<WS = WebSocket> extends Connection { | ||
/** | ||
* WebSocket instance. | ||
*/ | ||
ws?: WS | ||
/** | ||
* @param url WebSocket server URL. | ||
@@ -20,3 +27,3 @@ * @param WS WebSocket class if you want change implementation. | ||
*/ | ||
constructor (url: string, WS?: any, opts?: any) | ||
constructor (url: string, Class?: any, opts?: any) | ||
} |
import { createNanoEvents } from 'nanoevents' | ||
class WsConnection { | ||
constructor (url, WS, opts) { | ||
constructor (url, Class, opts) { | ||
this.connected = false | ||
this.emitter = createNanoEvents() | ||
if (WS) { | ||
this.WS = WS | ||
if (Class) { | ||
this.Class = Class | ||
} else if (typeof WebSocket !== 'undefined') { | ||
this.WS = WebSocket | ||
this.Class = WebSocket | ||
} else { | ||
@@ -24,6 +24,3 @@ throw new Error('No WebSocket support') | ||
ws.onclose = () => { | ||
if (this.ws) { | ||
this.connected = false | ||
this.emitter.emit('disconnect') | ||
} | ||
this.onclose() | ||
} | ||
@@ -35,3 +32,3 @@ | ||
data = JSON.parse(event.data) | ||
} catch (e) { | ||
} catch { | ||
this.error(event.data) | ||
@@ -47,4 +44,6 @@ return | ||
connect () { | ||
if (this.ws) return Promise.resolve() | ||
this.emitter.emit('connecting') | ||
this.init(new this.WS(this.url, undefined, this.opts)) | ||
this.init(new this.Class(this.url, undefined, this.opts)) | ||
@@ -62,5 +61,5 @@ return new Promise(resolve => { | ||
if (this.ws) { | ||
this.ws.onclose() | ||
this.ws.onclose = undefined | ||
this.ws.close() | ||
this.ws = undefined | ||
this.onclose() | ||
} | ||
@@ -86,4 +85,12 @@ } | ||
} | ||
onclose () { | ||
if (this.ws) { | ||
this.connected = false | ||
this.emitter.emit('disconnect') | ||
this.ws = undefined | ||
} | ||
} | ||
} | ||
export { WsConnection } |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
160827
92
5222
73
Updatednanoevents@^5.1.7