Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@logux/client

Package Overview
Dependencies
Maintainers
1
Versions
54
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@logux/client - npm Package Compare versions

Comparing version 0.9.3 to 0.10.0

encrypt-actions/index.d.ts

2

attention/index.d.ts

@@ -14,2 +14,2 @@ import { Client } from '../client/index.js'

*/
export function attention (client: Client): () => void
export function attention(client: Client): () => void

@@ -1,2 +0,2 @@

function attention (client) {
export function attention(client) {
let doc = document

@@ -59,3 +59,1 @@ let originTitle = false

}
export { attention }
import { Client } from '../client/index.js'
export type BadgeMessages = {
export interface BadgeMessages {
synchronized: string

@@ -14,3 +14,3 @@ disconnected: string

export type BadgeStyles = {
export interface BadgeStyles {
base: object

@@ -35,3 +35,3 @@ text: object

type BadgeOptions = {
interface BadgeOptions {
/**

@@ -88,3 +88,3 @@ * Widget text for different states.

*/
export function badge (client: Client, opts: BadgeOptions): () => void
export function badge(client: Client, opts: BadgeOptions): () => void

@@ -91,0 +91,0 @@ /**

import { status } from '../status/index.js'
function injectStyles (element, styles) {
function injectStyles(element, styles) {
for (let i in styles) {

@@ -9,3 +9,3 @@ element.style[i] = styles[i]

function setPosition (element, position) {
function setPosition(element, position) {
let style = element.style

@@ -44,3 +44,3 @@ if (position === 'middle-center' || position === 'center-middle') {

function badge (client, opts) {
export function badge(client, opts) {
let messages = opts.messages

@@ -105,3 +105,3 @@ let position = opts.position || 'bottom-right'

let badgeRu = {
export let badgeRu = {
synchronized: 'Ваши данные сохранены',

@@ -117,3 +117,3 @@ disconnected: 'Нет интернета',

let badgeEn = {
export let badgeEn = {
synchronized: 'Your data has been saved',

@@ -128,3 +128,1 @@ disconnected: 'No Internet connection',

}
export { badge, badgeEn, badgeRu }

@@ -6,3 +6,3 @@ import refresh from './refresh.svg'

let badgeStyles = {
export let badgeStyles = {
base: {

@@ -54,3 +54,1 @@ position: 'fixed',

}
export { badgeStyles }

@@ -10,3 +10,4 @@ import { Unsubscribe } from 'nanoevents'

Meta,
TokenGenerator
TokenGenerator,
AnyAction
} from '@logux/core'

@@ -16,7 +17,7 @@

export interface ClientActionListener {
(action: Action, meta: ClientMeta): void
export interface ClientActionListener<ListenAction extends Action> {
(action: ListenAction, meta: ClientMeta): void
}
export type ClientMeta = Meta & {
export interface ClientMeta extends Meta {
/**

@@ -38,3 +39,3 @@ * Action should be visible only for browser tab with the same `client.tabId`.

export type ClientOptions = {
export interface ClientOptions {
/**

@@ -130,7 +131,10 @@ * Server URL.

*/
export class Client<H extends object = {}, L extends Log = Log<ClientMeta>> {
export class Client<
Headers extends object = {},
ClientLog extends Log = Log<ClientMeta>
> {
/**
* @param opts Client options.
*/
constructor (opts: ClientOptions)
constructor(opts: ClientOptions)

@@ -176,3 +180,3 @@ /**

*/
log: L
log: ClientLog

@@ -186,5 +190,24 @@ /**

*/
node: ClientNode<H, L>
node: ClientNode<Headers, ClientLog>
/**
* Leader tab synchronization state. It can differs
* from `client.node.state` (because only the leader tab keeps connection).
*
* ```js
* client.on('state', () => {
* if (client.state === 'disconnected' && client.state === 'sending') {
* showCloseWarning()
* }
* })
* ```
*/
state: ClientNode['state']
/**
* Is leader tab connected to server.
*/
connected: boolean
/**
* Connect to server and reconnect on any connection problem.

@@ -196,5 +219,48 @@ *

*/
start (): void
start(): void
/**
* Send action to the server (by setting `meta.sync` and adding to the log)
* and track server processing.
*
* ```js
* showLoader()
* client.sync(
* { type: 'CHANGE_NAME', name }
* ).then(() => {
* hideLoader()
* }).catch(error => {
* hideLoader()
* showError(error.action.reason)
* })
* ```
*
* @param action The action
* @param meta Optional meta.
* @returns Promise for server processing.
*/
sync(action: AnyAction, meta?: Partial<ClientMeta>): Promise<ClientMeta>
/**
* Add listener for adding action with specific type.
* Works faster than `on('add', cb)` with `if`.
*
* ```js
* client.type('rename', (action, meta) => {
* name = action.name
* })
* ```
*
* @param type Action’s type.
* @param ActionListener The listener function.
* @param event
* @returns Unbind listener from event.
*/
type<TypeAction extends Action = Action>(
type: TypeAction['type'],
listener: ClientActionListener<TypeAction>,
opts?: { id?: string; event?: 'preadd' | 'add' | 'clean' }
): Unsubscribe
/**
* Subscribe for synchronization events. It implements Nano Events API.

@@ -208,2 +274,4 @@ * Supported events:

*
* Note, that `Log#type()` will work faster than `on` event with `if`.
*
* ```js

@@ -219,7 +287,8 @@ * client.on('add', (action, meta) => {

*/
on (
on(event: 'state', listener: () => void): Unsubscribe
on(
event: 'preadd' | 'add' | 'clean',
listener: ClientActionListener
listener: ClientActionListener<Action>
): Unsubscribe
on (event: 'user', listener: (userId: string) => void): Unsubscribe
on(event: 'user', listener: (userId: string) => void): Unsubscribe

@@ -244,5 +313,17 @@ /**

*/
changeUser (userId: string, token?: string): void
changeUser(userId: string, token?: string): void
/**
* Wait for specific state of the leader tab.
*
* ```js
* await client.waitFor('synchronized')
* hideLoader()
* ```
*
* @param state State name
*/
waitFor(state: ClientNode['state']): Promise<void>
/**
* Disconnect and stop synchronization.

@@ -256,3 +337,3 @@ *

*/
destroy (): void
destroy(): void

@@ -270,3 +351,3 @@ /**

*/
clean (): Promise<void>
clean(): Promise<void>
}
import { createNanoEvents } from 'nanoevents'
import { isFirstOlder } from '@logux/core/is-first-older'
import { WsConnection } from '@logux/core/ws-connection'
import { MemoryStore } from '@logux/core/memory-store'
import { ClientNode } from '@logux/core/client-node'
import { Reconnect } from '@logux/core/reconnect'
import { parseId } from '@logux/core/parse-id'
import {
isFirstOlder,
WsConnection,
MemoryStore,
ClientNode,
Reconnect,
parseId,
Log
} from '@logux/core'
import { nanoid } from 'nanoid'
import { Log } from '@logux/core/log'
import { LoguxUndoError } from '../logux-undo-error/index.js'
import { track } from '../track/index.js'
let ALLOWED_META = ['id', 'time', 'subprotocol']
function tabPing (c) {
function tabPing(c) {
localStorage.setItem(c.options.prefix + ':tab:' + c.tabId, Date.now())
}
function cleanTabActions (client, id) {
function cleanTabActions(client, id) {
client.log.removeReason('tab' + id).then(() => {

@@ -25,4 +30,4 @@ if (client.isLocalStorage) {

class Client {
constructor (opts = {}) {
export class Client {
constructor(opts = {}) {
this.options = opts

@@ -110,27 +115,38 @@

unsubscribing[meta.id] = action
} else if (type === 'logux/processed' && unsubscribing[action.id]) {
let unsubscription = unsubscribing[action.id]
json = JSON.stringify({ ...unsubscription, type: 'logux/subscribe' })
let subscribers = this.subscriptions[json]
if (subscribers) {
if (subscribers === 1) {
delete this.subscriptions[json]
} else if (type === 'logux/processed') {
if (unsubscribing[action.id]) {
let unsubscription = unsubscribing[action.id]
json = JSON.stringify({ ...unsubscription, type: 'logux/subscribe' })
let subscribers = this.subscriptions[json]
if (subscribers) {
if (subscribers === 1) {
delete this.subscriptions[json]
} else {
this.subscriptions[json] = subscribers - 1
}
}
}
if (subscribing[action.id]) {
let subscription = subscribing[action.id]
delete subscribing[action.id]
json = JSON.stringify(subscription)
if (this.subscriptions[json]) {
this.subscriptions[json] += 1
} else {
this.subscriptions[json] = subscribers - 1
this.subscriptions[json] = 1
}
last = this.last[subscription.channel]
if (!last || isFirstOlder(last, meta)) {
this.last[subscription.channel] = { id: meta.id, time: meta.time }
}
}
} else if (type === 'logux/processed' && subscribing[action.id]) {
let subscription = subscribing[action.id]
delete subscribing[action.id]
json = JSON.stringify(subscription)
if (this.subscriptions[json]) {
this.subscriptions[json] += 1
} else {
this.subscriptions[json] = 1
if (type === 'logux/processed' && this.processing[action.id]) {
this.processing[action.id][1](meta)
delete this.processing[action.id]
}
last = this.last[subscription.channel]
if (!last || isFirstOlder(last, meta)) {
this.last[subscription.channel] = { id: meta.id, time: meta.time }
} else if (type === 'logux/undo') {
if (this.processing[action.id]) {
this.processing[action.id][2](new LoguxUndoError(action))
delete this.processing[action.id]
}
} else if (type === 'logux/undo') {
delete subscribing[action.id]

@@ -202,2 +218,11 @@ delete unsubscribing[action.id]

if (!this.options.time) {
if (typeof this.options.timeout === 'undefined') {
this.options.timeout = 20000
}
if (typeof this.options.ping === 'undefined') {
this.options.ping = 5000
}
}
this.node = new ClientNode(this.nodeId, this.log, connection, {

@@ -207,2 +232,3 @@ subprotocol: this.options.subprotocol,

timeout: this.options.timeout,
fixTime: !this.options.time,
outMap,

@@ -256,5 +282,15 @@ token: this.options.token,

}
this.processing = {}
}
start () {
get state() {
return this.node.state
}
get connected() {
return this.state !== 'disconnected' && this.state !== 'connecting'
}
start() {
this.cleanPrevActions()

@@ -264,4 +300,20 @@ this.node.connection.connect()

on (event, listener) {
if (event === 'user') {
sync(action, meta = {}) {
meta.sync = true
if (typeof meta.id === 'undefined') {
meta.id = this.log.generateId()
}
this.log.add(action, meta)
return track(this, meta.id)
}
type(type, listener, opts) {
return this.log.type(type, listener, opts)
}
on(event, listener) {
if (event === 'state') {
return this.node.emitter.on(event, listener)
} else if (event === 'user') {
return this.emitter.on(event, listener)

@@ -273,3 +325,3 @@ } else {

changeUser (userId, token) {
changeUser(userId, token) {
if (process.env.NODE_ENV !== 'production') {

@@ -300,3 +352,17 @@ if (typeof userId !== 'string') {

destroy () {
waitFor(state) {
if (this.state === state) {
return Promise.resolve()
}
return new Promise(resolve => {
let unbind = this.on('state', () => {
if (this.state === state) {
unbind()
resolve()
}
})
})
}
destroy() {
this.onUnload()

@@ -310,3 +376,3 @@ this.node.destroy()

clean () {
clean() {
this.destroy()

@@ -316,3 +382,3 @@ return this.log.store.clean ? this.log.store.clean() : Promise.resolve()

cleanPrevActions () {
cleanPrevActions() {
if (!this.isLocalStorage) return

@@ -331,11 +397,9 @@

onUnload () {
onUnload() {
if (this.pinging) cleanTabActions(this, this.tabId)
}
getClientId () {
getClientId() {
return nanoid(8)
}
}
export { Client }

@@ -14,2 +14,2 @@ import { Client } from '../client/index.js'

*/
export function confirm (client: Client): () => void
export function confirm(client: Client): () => void

@@ -1,2 +0,2 @@

function block (e) {
function block(e) {
e.returnValue = 'unsynced'

@@ -6,3 +6,3 @@ return 'unsynced'

function confirm (client) {
export function confirm(client) {
let disconnected = client.state === 'disconnected'

@@ -51,3 +51,1 @@ let wait = false

}
export { confirm }

@@ -0,3 +1,3 @@

import { Action, Log } from '@logux/core'
import { Unsubscribe } from 'nanoevents'
import { Log } from '@logux/core'

@@ -28,5 +28,5 @@ import { Client, ClientActionListener, ClientMeta } from '../client/index.js'

export class CrossTabClient<
H extends object = {},
L extends Log = Log<ClientMeta>
> extends Client<H, L> {
Headers extends object = {},
ClientLog extends Log = Log<ClientMeta>
> extends Client<Headers, ClientLog> {
/**

@@ -45,21 +45,8 @@ * Current tab role. Only `leader` tab connects to server. `followers` just

/**
* Leader tab synchronization state. It can differs
* from `client.node.state` (because only the leader tab keeps connection).
*
* ```js
* client.on('state', () => {
* if (client.state === 'disconnected' && client.state === 'sending') {
* showCloseWarning()
* }
* })
* ```
* Cache for localStorage detection. Can be overriden to disable leader tab
* election in tests.
*/
state: 'disconnected' | 'connecting' | 'sending' | 'synchronized'
isLocalStorage: boolean
/**
* Is leader tab connected to server.
*/
connected: boolean
/**
* Subscribe for synchronization events. It implements nanoevents API.

@@ -85,22 +72,8 @@ * Supported events:

*/
on (event: 'role' | 'state', listener: () => void): Unsubscribe
on (event: 'user', listener: (userId: string) => void): Unsubscribe
on (
on(event: 'role' | 'state', listener: () => void): Unsubscribe
on(event: 'user', listener: (userId: string) => void): Unsubscribe
on(
event: 'preadd' | 'add' | 'clean',
listener: ClientActionListener
listener: ClientActionListener<Action>
): Unsubscribe
/**
* Wait for specific state of the leader tab.
*
* ```js
* await client.waitFor('synchronized')
* hideLoader()
* ```
*
* @param state State name
*/
waitFor (
state: 'disconnected' | 'connecting' | 'sending' | 'synchronized'
): Promise<void>
}

@@ -1,10 +0,10 @@

import { LoguxError } from '@logux/core/logux-error'
import { LoguxError, actionEvents } from '@logux/core'
import { Client } from '../client/index.js'
function storageKey (client, name) {
function storageKey(client, name) {
return client.options.prefix + ':' + client.options.userId + ':' + name
}
function sendToTabs (client, event, data) {
function sendToTabs(client, event, data) {
if (!client.isLocalStorage) return

@@ -24,3 +24,3 @@ let key = storageKey(client, event)

function getLeader (client) {
function getLeader(client) {
let data = localStorage.getItem(storageKey(client, 'leader'))

@@ -32,7 +32,7 @@ let json = []

function leaderPing (client) {
function leaderPing(client) {
sendToTabs(client, 'leader', [client.tabId, Date.now()])
}
function onDeadLeader (client) {
function onDeadLeader(client) {
if (client.state !== 'disconnected') {

@@ -44,3 +44,3 @@ setState(client, 'disconnected')

function watchForLeader (client) {
function watchForLeader(client) {
clearTimeout(client.watching)

@@ -56,26 +56,18 @@ client.watching = setTimeout(() => {

function areWeOutdates (client, meta) {
if (!meta.subprotocol) return false
if (client.options.subprotocol === meta.subprotocol) return false
let id = meta.id.split(' ')[1]
let prefix = client.clientId + ':'
if (id.slice(0, prefix.length) !== prefix) return false
let ourParts = client.options.subprotocol.split('.')
let remoteParts = meta.subprotocol.split('.')
// eslint-disable-next-line
for (let i = 0; i < ourParts.length; i++) {
let ourNumber = parseInt(ourParts[i])
let remoteNumber = parseInt(remoteParts[i])
if (ourNumber > remoteNumber) {
return false
} else if (ourNumber < remoteNumber) {
return true
function compareSubprotocols(left, right) {
let leftParts = left.split('.')
let rightParts = right.split('.')
for (let i = 0; i < 3; i++) {
let leftNumber = parseInt(leftParts[i] || 0)
let rightNumber = parseInt(rightParts[i] || 0)
if (leftNumber > rightNumber) {
return 1
} else if (leftNumber < rightNumber) {
return -1
}
}
return false
return 0
}
function setRole (client, role) {
function setRole(client, role) {
if (client.role !== role) {

@@ -115,3 +107,3 @@ let node = client.node

function isActiveLeader (client) {
function isActiveLeader(client) {
let leader = getLeader(client)

@@ -121,3 +113,3 @@ return leader[1] && leader[1] >= Date.now() - client.leaderTimeout

function startElection (client) {
function startElection(client) {
leaderPing(client)

@@ -136,3 +128,3 @@ setRole(client, 'candidate')

function setState (client, state) {
function setState(client, state) {
client.state = state

@@ -143,8 +135,8 @@ client.emitter.emit('state')

function isMemory (store) {
function isMemory(store) {
return Array.isArray(store.entries) && Array.isArray(store.added)
}
class CrossTabClient extends Client {
constructor (opts = {}) {
export class CrossTabClient extends Client {
constructor(opts = {}) {
super(opts)

@@ -159,3 +151,3 @@

this.state = this.node.state
this.leaderState = this.node.state

@@ -169,3 +161,3 @@ this.node.on('state', () => {

this.log.on('add', (action, meta) => {
this.emitter.emit('add', action, meta)
actionEvents(this.emitter, 'add', action, meta)
if (meta.tab !== this.tabId) {

@@ -176,3 +168,3 @@ sendToTabs(this, 'add', [this.tabId, action, meta])

this.log.on('clean', (action, meta) => {
this.emitter.emit('clean', action, meta)
actionEvents(this.emitter, 'clean', action, meta)
})

@@ -184,5 +176,20 @@

}
if (this.isLocalStorage) {
let subprotocolKey = storageKey(this, 'subprotocol')
if (localStorage.getItem(subprotocolKey) !== this.options.subprotocol) {
sendToTabs(this, 'subprotocol', this.options.subprotocol)
}
}
}
start () {
get state() {
return this.leaderState
}
set state(value) {
this.leaderState = value
}
start() {
this.cleanPrevActions()

@@ -205,3 +212,3 @@

destroy () {
destroy() {
super.destroy()

@@ -217,3 +224,3 @@

clean () {
clean() {
if (this.isLocalStorage) {

@@ -228,3 +235,3 @@ localStorage.removeItem(storageKey(this, 'add'))

changeUser (userId, token) {
changeUser(userId, token) {
sendToTabs(this, 'user', [this.tabId, userId])

@@ -234,3 +241,13 @@ super.changeUser(userId, token)

on (event, listener) {
type(type, listener, opts = {}) {
if (opts.event === 'preadd') {
return this.log.type(type, listener, opts)
} else {
let event = opts.event || 'add'
let id = opts.id || ''
return this.emitter.on(`${event}-${type}-${id}`, listener)
}
}
on(event, listener) {
if (event === 'preadd') {

@@ -243,17 +260,3 @@ return this.log.emitter.on(event, listener)

waitFor (state) {
if (this.state === state) {
return Promise.resolve()
}
return new Promise(resolve => {
let unbind = this.on('state', () => {
if (this.state === state) {
unbind()
resolve()
}
})
})
}
onStorage (e) {
onStorage(e) {
if (e.newValue === null) return

@@ -267,13 +270,2 @@

let meta = data[2]
if (areWeOutdates(this, meta)) {
let err = new LoguxError(
'wrong-subprotocol',
{
supported: meta.subprotocol,
used: this.node.options.subprotocol
},
true
)
this.node.emitter.emit('error', err)
}
if (!meta.tab || meta.tab === this.tabId) {

@@ -283,3 +275,3 @@ if (isMemory(this.log.store)) {

}
this.emitter.emit('add', action, meta)
actionEvents(this.emitter, 'add', action, meta)
if (this.role === 'leader') {

@@ -300,4 +292,4 @@ this.node.onAdd(action, meta)

let state = JSON.parse(localStorage.getItem(e.key))
if (this.state !== state) {
this.state = state
if (this.leaderState !== state) {
this.leaderState = state
this.emitter.emit('state')

@@ -310,6 +302,19 @@ }

}
} else if (e.key === storageKey(this, 'subprotocol')) {
let other = JSON.parse(e.newValue)
let compare = compareSubprotocols(this.options.subprotocol, other)
if (compare === 1) {
sendToTabs(this, 'subprotocol', this.options.subprotocol)
} else if (compare === -1) {
let err = new LoguxError(
'wrong-subprotocol',
{ supported: other, used: this.options.subprotocol },
true
)
this.node.emitter.emit('error', err)
}
}
}
onUnload () {
onUnload() {
if (this.role === 'leader') {

@@ -322,3 +327,3 @@ this.unloading = true

getClientId () {
getClientId() {
let key = storageKey(this, 'client')

@@ -335,8 +340,2 @@ if (!this.isLocalStorage) {

}
get connected () {
return this.state !== 'disconnected' && this.state !== 'connecting'
}
}
export { CrossTabClient }
import { Client } from '../client/index.js'
type FaviconLinks = {
interface FaviconLinks {
/**

@@ -36,2 +36,2 @@ * Default favicon link. By default, it will be taken from current favicon.

*/
export function favicon (client: Client, links: FaviconLinks): () => void
export function favicon(client: Client, links: FaviconLinks): () => void

@@ -1,2 +0,2 @@

function favicon (client, links) {
export function favicon(client, links) {
let normal = links.normal

@@ -11,3 +11,3 @@ let offline = links.offline

function update () {
function update() {
if (client.connected && prevFav !== normal) {

@@ -25,3 +25,3 @@ fav.href = prevFav = normal

function setError () {
function setError() {
if (error && prevFav !== error) {

@@ -66,3 +66,1 @@ fav.href = prevFav = error

}
export { favicon }

@@ -8,5 +8,16 @@ export {

} from './badge/index.js'
export {
LoguxUndoError,
ChannelNotFoundError,
ChannelDeniedError,
ChannelServerError,
ChannelError
} from './logux-undo-error/index.js'
export { Client, ClientMeta, ClientOptions } from './client/index.js'
export { request, RequestOptions } from './request/index.js'
export { encryptActions } from './encrypt-actions/index.js'
export { CrossTabClient } from './cross-tab-client/index.js'
export { IndexedStore } from './indexed-store/index.js'
export { TestServer } from './test-server/index.js'
export { TestClient } from './test-client/index.js'
export { attention } from './attention/index.js'

@@ -16,2 +27,3 @@ export { confirm } from './confirm/index.js'

export { status } from './status/index.js'
export { track } from './track/index.js'
export { log } from './log/index.js'

@@ -1,23 +0,15 @@

import { badge, badgeRu, badgeEn } from './badge/index.js'
import { CrossTabClient } from './cross-tab-client/index.js'
import { IndexedStore } from './indexed-store/index.js'
import { attention } from './attention/index.js'
import { confirm } from './confirm/index.js'
import { favicon } from './favicon/index.js'
import { Client } from './client/index.js'
import { status } from './status/index.js'
import { log } from './log/index.js'
export {
CrossTabClient,
IndexedStore,
attention,
confirm,
badgeRu,
badgeEn,
favicon,
Client,
status,
badge,
log
}
export { badge, badgeRu, badgeEn } from './badge/index.js'
export { LoguxUndoError } from './logux-undo-error/index.js'
export { CrossTabClient } from './cross-tab-client/index.js'
export { encryptActions } from './encrypt-actions/index.js'
export { IndexedStore } from './indexed-store/index.js'
export { TestServer } from './test-server/index.js'
export { TestClient } from './test-client/index.js'
export { attention } from './attention/index.js'
export { confirm } from './confirm/index.js'
export { favicon } from './favicon/index.js'
export { request } from './request/index.js'
export { Client } from './client/index.js'
export { status } from './status/index.js'
export { track } from './track/index.js'
export { log } from './log/index.js'

@@ -26,3 +26,3 @@ import { LogStore } from '@logux/core'

*/
constructor (name?: string)
constructor(name?: string)

@@ -29,0 +29,0 @@ /**

import { isFirstOlder } from '@logux/core'
const VERSION = 1
const VERSION = 2
function rejectify (request, reject) {
function rejectify(request, reject) {
request.onerror = e => {

@@ -11,3 +11,3 @@ reject(e.target.error)

function promisify (request) {
function promisify(request) {
return new Promise((resolve, reject) => {

@@ -21,25 +21,8 @@ rejectify(request, reject)

function nextEntry (request) {
return cursor => {
if (cursor) {
cursor.value.meta.added = cursor.value.added
return {
entries: [[cursor.value.action, cursor.value.meta]],
next () {
cursor.continue()
return promisify(request).then(nextEntry(request))
}
}
} else {
return { entries: [] }
}
}
}
function isDefined (value) {
function isDefined(value) {
return typeof value !== 'undefined'
}
class IndexedStore {
constructor (name = 'logux') {
export class IndexedStore {
constructor(name = 'logux') {
this.name = name

@@ -49,3 +32,3 @@ this.adding = {}

init () {
init() {
if (this.initing) return this.initing

@@ -59,11 +42,20 @@

let log = db.createObjectStore('log', {
keyPath: 'added',
autoIncrement: true
})
log.createIndex('id', 'id', { unique: true })
log.createIndex('created', 'created', { unique: true })
log.createIndex('reasons', 'reasons', { multiEntry: true })
db.createObjectStore('extra', { keyPath: 'key' })
let log
if (e.oldVersion < 1) {
log = db.createObjectStore('log', {
keyPath: 'added',
autoIncrement: true
})
log.createIndex('id', 'id', { unique: true })
log.createIndex('created', 'created', { unique: true })
log.createIndex('reasons', 'reasons', { multiEntry: true })
db.createObjectStore('extra', { keyPath: 'key' })
}
if (e.oldVersion < 2) {
if (!log) {
/* istanbul ignore next */
log = opening.transaction.objectStore('log')
}
log.createIndex('indexes', 'indexes', { multiEntry: true })
}
}

@@ -85,15 +77,38 @@

async get (opts) {
let request
async get({ index, order }) {
let store = await this.init()
let log = store.os('log')
if (opts.order === 'created') {
request = log.index('created').openCursor(null, 'prev')
} else {
request = log.openCursor(null, 'prev')
}
return promisify(request).then(nextEntry(request))
return new Promise((resolve, reject) => {
let log = store.os('log')
let request
if (index) {
if (order === 'created') {
request = log.index('created').openCursor(null, 'prev')
} else {
let keyRange = IDBKeyRange.only(index)
request = log.index('indexes').openCursor(keyRange, 'prev')
}
} else if (order === 'created') {
request = log.index('created').openCursor(null, 'prev')
} else {
request = log.openCursor(null, 'prev')
}
rejectify(request, reject)
let entries = []
request.onsuccess = function (e) {
let cursor = e.target.result
if (!cursor) {
resolve({ entries })
return
}
if (!index || cursor.value.indexes.includes(index)) {
cursor.value.meta.added = cursor.value.added
entries.unshift([cursor.value.action, cursor.value.meta])
}
cursor.continue()
}
})
}
async byId (id) {
async byId(id) {
let store = await this.init()

@@ -108,10 +123,9 @@ let result = await promisify(store.os('log').index('id').get(id))

async remove (id) {
async remove(id) {
let store = await this.init()
let log = store.os('log', 'write')
let entry = await promisify(log.index('id').get(id))
let entry = await promisify(store.os('log').index('id').get(id))
if (!entry) {
return false
} else {
await promisify(log.delete(entry.added))
await promisify(store.os('log', 'write').delete(entry.added))
entry.meta.added = entry.added

@@ -122,3 +136,3 @@ return [entry.action, entry.meta]

async add (action, meta) {
async add(action, meta) {
let id = meta.id.split(' ')

@@ -131,2 +145,3 @@ let entry = {

reasons: meta.reasons,
indexes: meta.indexes || [],
created: [meta.time, id[1], id[2], id[0]].join(' ')

@@ -141,8 +156,7 @@ }

let store = await this.init()
let log = store.os('log', 'write')
let exist = await promisify(log.index('id').get(meta.id))
let exist = await promisify(store.os('log').index('id').get(meta.id))
if (exist) {
return false
} else {
let added = await promisify(log.add(entry))
let added = await promisify(store.os('log', 'write').add(entry))
delete store.adding[entry.created]

@@ -154,6 +168,5 @@ meta.added = added

async changeMeta (id, diff) {
async changeMeta(id, diff) {
let store = await this.init()
let log = store.os('log', 'write')
let entry = await promisify(log.index('id').get(id))
let entry = await promisify(store.os('log').index('id').get(id))
if (!entry) {

@@ -164,3 +177,3 @@ return false

if (diff.reasons) entry.reasons = diff.reasons
await promisify(log.put(entry))
await promisify(store.os('log', 'write').put(entry))
return true

@@ -170,7 +183,6 @@ }

async removeReason (reason, criteria, callback) {
async removeReason(reason, criteria, callback) {
let store = await this.init()
let log = store.os('log', 'write')
if (criteria.id) {
let entry = await promisify(log.index('id').get(criteria.id))
let entry = await promisify(store.os('log').index('id').get(criteria.id))
if (entry) {

@@ -183,5 +195,5 @@ let index = entry.meta.reasons.indexOf(reason)

callback(entry.action, entry.meta)
await promisify(log.delete(entry.added))
await promisify(store.os('log', 'write').delete(entry.added))
} else {
await promisify(log.put(entry))
await promisify(store.os('log', 'write').put(entry))
}

@@ -191,4 +203,5 @@ }

} else {
let request = log.index('reasons').openCursor(reason)
await new Promise((resolve, reject) => {
let log = store.os('log', 'write')
let request = log.index('reasons').openCursor(reason)
rejectify(request, reject)

@@ -243,3 +256,3 @@ request.onsuccess = function (e) {

async getLastAdded () {
async getLastAdded() {
let store = await this.init()

@@ -250,3 +263,3 @@ let cursor = await promisify(store.os('log').openCursor(null, 'prev'))

async getLastSynced () {
async getLastSynced() {
let store = await this.init()

@@ -261,6 +274,5 @@ let data = await promisify(store.os('extra').get('lastSynced'))

async setLastSynced (values) {
async setLastSynced(values) {
let store = await this.init()
let extra = store.os('extra', 'write')
let data = await promisify(extra.get('lastSynced'))
let data = await promisify(store.os('extra').get('lastSynced'))
if (!data) data = { key: 'lastSynced', sent: 0, received: 0 }

@@ -273,6 +285,6 @@ if (typeof values.sent !== 'undefined') {

}
await promisify(extra.put(data))
await promisify(store.os('extra', 'write').put(data))
}
os (name, write) {
os(name, write) {
let mode = write ? 'readwrite' : 'readonly'

@@ -282,3 +294,3 @@ return this.db.transaction(name, mode).objectStore(name)

async clean () {
async clean() {
let store = await this.init()

@@ -289,3 +301,1 @@ store.db.close()

}
export { IndexedStore }
import { Client } from '../client/index.js'
type LogMessages = {
interface LogMessages {
/**

@@ -52,2 +52,2 @@ * Disable action messages with specific types.

*/
export function log (client: Client, messages?: LogMessages): () => void
export function log(client: Client, messages?: LogMessages): () => void

@@ -1,8 +0,8 @@

import { parseId } from '@logux/core/parse-id'
import { parseId } from '@logux/core'
function bold (string) {
function bold(string) {
return '%c' + string + '%c'
}
function showLog (text, details) {
function showLog(text, details) {
text = '%cLogux%c ' + text

@@ -34,3 +34,3 @@ let args = Array.from(text.match(/%c/g)).map((_, i) => {

function log (client, messages = {}) {
export function log(client, messages = {}) {
let node = client.node

@@ -92,2 +92,6 @@

}
} else if (action.type === 'logux/subscribed') {
showLog(
'subscribed to ' + bold(action.channel) + ' channel by server'
)
} else if (action.type === 'logux/unsubscribe') {

@@ -122,15 +126,17 @@ message = 'unsubscribed from channel ' + bold(action.channel)

} else if (action.type === 'logux/undo') {
message =
'action ' +
bold(action.id) +
' was undid because of ' +
bold(action.reason)
let details = {}
if (action.action.type === 'logux/subscribe') {
message = 'subscription to ' + bold(action.action.channel)
} else {
message = 'action ' + bold(action.action.type)
}
message += ' was undone because of ' + bold(action.reason)
let details = {
'Reverted Action': action.action
}
if (Object.keys(action).length > 4) {
details['Undo Action'] = action
}
if (sent[action.id]) {
details.Action = sent[action.id]
delete sent[action.id]
}
if (Object.keys(action).length > 3) {
details.Undo = action
}
showLog(message, details)

@@ -184,3 +190,1 @@ } else {

}
export { log }
{
"name": "@logux/client",
"version": "0.9.3",
"version": "0.10.0",
"description": "Logux base components to build web client",

@@ -17,71 +17,21 @@ "keywords": [

"sideEffects": false,
"type": "module",
"types": "./index.d.ts",
"exports": {
".": "./index.js",
"./package.json": "./package.json"
},
"engines": {
"node": ">=10.0.0"
"node": "^12.0.0 || ^14.0.0 || >=16.0.0"
},
"peerDependencies": {
"@logux/core": "^0.7.0"
},
"dependencies": {
"@logux/core": "^0.6.2",
"nanoevents": "^5.1.8",
"nanoid": "^3.1.12"
},
"type": "module",
"main": "index.cjs",
"module": "index.js",
"react-native": "index.js",
"exports": {
".": {
"require": "./index.cjs",
"import": "./index.js"
},
"./package.json": "./package.json",
"./attention/package.json": "./attention/package.json",
"./attention": {
"require": "./attention/index.cjs",
"import": "./attention/index.js"
},
"./badge/package.json": "./badge/package.json",
"./badge": {
"require": "./badge/index.cjs",
"import": "./badge/index.js"
},
"./client/package.json": "./client/package.json",
"./client": {
"require": "./client/index.cjs",
"import": "./client/index.js"
},
"./cross-tab-client/package.json": "./cross-tab-client/package.json",
"./cross-tab-client": {
"require": "./cross-tab-client/index.cjs",
"import": "./cross-tab-client/index.js"
},
"./favicon/package.json": "./favicon/package.json",
"./favicon": {
"require": "./favicon/index.cjs",
"import": "./favicon/index.js"
},
"./confirm/package.json": "./confirm/package.json",
"./confirm": {
"require": "./confirm/index.cjs",
"import": "./confirm/index.js"
},
"./indexed-store/package.json": "./indexed-store/package.json",
"./indexed-store": {
"require": "./indexed-store/index.cjs",
"import": "./indexed-store/index.js"
},
"./log/package.json": "./log/package.json",
"./log": {
"require": "./log/index.cjs",
"import": "./log/index.js"
},
"./status/package.json": "./status/package.json",
"./status": {
"require": "./status/index.cjs",
"import": "./status/index.js"
},
"./badge/styles/package.json": "./badge/styles/package.json",
"./badge/styles": {
"require": "./badge/styles/index.cjs",
"import": "./badge/styles/index.js"
}
"@logux/actions": "^0.1.0",
"fast-json-stable-stringify": "^2.1.0",
"nanodelay": "^2.0.0",
"nanoevents": "^6.0.0",
"nanoid": "^3.1.22"
}
}
}

@@ -13,3 +13,3 @@ # Logux Client [![Cult Of Martians][cult-img]][cult]

* **[Issues](https://github.com/logux/logux/issues)**
and **[roadmap](https://github.com/logux/logux/projects/1)**
and **[roadmap](https://github.com/orgs/logux/projects/1)**
* **[Projects](https://logux.io/guide/architecture/parts/)**

@@ -43,3 +43,3 @@ inside Logux ecosystem

```sh
npm install @logux/client
npm install @logux/core @logux/client
```

@@ -46,0 +46,0 @@

@@ -28,3 +28,3 @@ import { Action } from '@logux/core'

type StatusOptions = {
interface StatusOptions {
/**

@@ -51,3 +51,3 @@ * Synchronized state duration. Default is `3000`.

*/
export function status (
export function status(
client: Client,

@@ -54,0 +54,0 @@ callback: StatusListener,

@@ -1,2 +0,2 @@

function status (client, callback, options = {}) {
export function status(client, callback, options = {}) {
let observable = client.on ? client : client.node

@@ -13,3 +13,3 @@ let disconnected = observable.state === 'disconnected'

function setSynchronized () {
function setSynchronized() {
if (Object.keys(processing).length === 0) {

@@ -28,3 +28,3 @@ if (wait) {

function changeState () {
function changeState() {
clearTimeout(timeout)

@@ -107,3 +107,1 @@

}
export { status }
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc