@logux/client
Advanced tools
Comparing version 0.20.1 to 0.21.0
@@ -285,4 +285,6 @@ import type { AbstractActionCreator } from '@logux/actions' | ||
* ``` | ||
* | ||
* @param connect Start connection immediately. | ||
*/ | ||
start(): void | ||
start(connect?: boolean): void | ||
@@ -289,0 +291,0 @@ /** |
@@ -226,18 +226,18 @@ import { | ||
let outFilter = async (action, meta) => { | ||
return !!meta.sync && parseId(meta.id).userId === this.options.userId | ||
} | ||
let outMap = async (action, meta) => { | ||
let filtered = {} | ||
for (let i in meta) { | ||
if (i === 'subprotocol') { | ||
if (meta.subprotocol !== this.options.subprotocol) { | ||
filtered.subprotocol = meta.subprotocol | ||
let onSend = async (action, meta) => { | ||
if (!!meta.sync && parseId(meta.id).userId === this.options.userId) { | ||
let filtered = {} | ||
for (let i in meta) { | ||
if (i === 'subprotocol') { | ||
if (meta.subprotocol !== this.options.subprotocol) { | ||
filtered.subprotocol = meta.subprotocol | ||
} | ||
} else if (ALLOWED_META.includes(i)) { | ||
filtered[i] = meta[i] | ||
} | ||
} else if (ALLOWED_META.includes(i)) { | ||
filtered[i] = meta[i] | ||
} | ||
return [action, filtered] | ||
} else { | ||
return false | ||
} | ||
return [action, filtered] | ||
} | ||
@@ -247,4 +247,3 @@ | ||
fixTime: !this.options.time, | ||
outFilter, | ||
outMap, | ||
onSend, | ||
ping: this.options.ping, | ||
@@ -297,3 +296,3 @@ subprotocol: this.options.subprotocol, | ||
if (typeof window !== 'undefined' && window.addEventListener) { | ||
window.addEventListener('unload', this.onUnload) | ||
window.addEventListener('pagehide', this.onUnload) | ||
} | ||
@@ -349,6 +348,2 @@ | ||
get connected() { | ||
return this.state !== 'disconnected' && this.state !== 'connecting' | ||
} | ||
destroy() { | ||
@@ -359,3 +354,3 @@ this.onUnload() | ||
if (typeof window !== 'undefined' && window.removeEventListener) { | ||
window.removeEventListener('unload', this.onUnload) | ||
window.removeEventListener('pagehide', this.onUnload) | ||
} | ||
@@ -382,11 +377,7 @@ } | ||
start() { | ||
start(connect = true) { | ||
this.cleanPrevActions() | ||
this.node.connection.connect() | ||
if (connect) this.node.connection.connect() | ||
} | ||
get state() { | ||
return this.node.state | ||
} | ||
sync(action, meta = {}) { | ||
@@ -420,2 +411,10 @@ meta.sync = true | ||
} | ||
get connected() { | ||
return this.state !== 'disconnected' && this.state !== 'connecting' | ||
} | ||
get state() { | ||
return this.node.state | ||
} | ||
} |
@@ -23,3 +23,3 @@ import type { ReadableAtom } from 'nanostores' | ||
* ```js | ||
* import { createAuth } from '@logux/client'\ | ||
* import { createAuth } from '@logux/client' | ||
* | ||
@@ -26,0 +26,0 @@ * let auth = createAuth(client) |
@@ -19,10 +19,15 @@ import type { SyncMapValues } from '@logux/actions' | ||
export interface FilterStore< | ||
Value extends SyncMapValues = any | ||
> extends MapStore<{ | ||
isEmpty: boolean | ||
isLoading: boolean | ||
list: LoadedSyncMapValue<Value>[] | ||
stores: Map<string, SyncMapStore<Value>> | ||
}> { | ||
export type LoadedFilterValue<Value extends SyncMapValues> = { | ||
isEmpty: boolean | ||
isLoading: false | ||
list: LoadedSyncMapValue<Value>[] | ||
stores: Map<string, SyncMapStore<Value>> | ||
} | ||
export type FilterValue<Value extends SyncMapValues> = | ||
| { isLoading: true } | ||
| LoadedFilterValue<Value> | ||
export interface FilterStore<Value extends SyncMapValues = any> | ||
extends MapStore<FilterValue<Value>> { | ||
/** | ||
@@ -29,0 +34,0 @@ * While store is loading initial data from server or log. |
@@ -27,9 +27,11 @@ import { isFirstOlder } from '@logux/core' | ||
let stores = new Map() | ||
filterStore.setKey('stores', stores) | ||
let isLoading = true | ||
filterStore.setKey('isLoading', true) | ||
filterStore.setKey('isEmpty', true) | ||
let list = [] | ||
filterStore.setKey('list', list) | ||
filterStore.set({ | ||
isEmpty: true, | ||
isLoading: true, | ||
list, | ||
stores | ||
}) | ||
@@ -110,4 +112,4 @@ let channelPrefix = Template.plural + '/' | ||
filterStore.loading = new Promise((resolve, reject) => { | ||
async function processSubscribe(subscribtion) { | ||
await subscribtion | ||
async function processSubscribe(subscription) { | ||
await subscription | ||
.then(() => { | ||
@@ -132,7 +134,10 @@ if (isLoading) { | ||
let clear = child.listen(() => {}) | ||
if (child.value.isLoading) await child.loading | ||
if (checkAllFields(child.value)) { | ||
add(child) | ||
try { | ||
if (child.value.isLoading) await child.loading | ||
if (checkAllFields(child.value)) { | ||
await add(child) | ||
} | ||
} finally { | ||
clear() | ||
} | ||
clear() | ||
} | ||
@@ -176,6 +181,3 @@ | ||
if (checkSomeFields(action.fields)) { | ||
let check = async () => { | ||
loadAndCheck(Template(action.id, client)) | ||
} | ||
checking.push(check()) | ||
checking.push(loadAndCheck(Template(action.id, client))) | ||
ignore.add(action.id) | ||
@@ -197,4 +199,8 @@ } | ||
} else if (Template.remote) { | ||
let subscribeSinceLatest = latestMeta !== undefined | ||
? { ...subscribe, since: { id: latestMeta.id, time: latestMeta.time } } | ||
let subscribeSinceLatest = | ||
latestMeta !== undefined | ||
? { | ||
...subscribe, | ||
since: { id: latestMeta.id, time: latestMeta.time } | ||
} | ||
: subscribe | ||
@@ -222,14 +228,23 @@ await processSubscribe(client.sync(subscribeSinceLatest)) | ||
let removeAndListen = (childId, actionId) => { | ||
let child = Template(childId, client) | ||
let clear = child.listen(() => {}) | ||
remove(childId) | ||
track(client, actionId) | ||
.catch(() => { | ||
add(child) | ||
}) | ||
.finally(() => { | ||
clear() | ||
}) | ||
if (Template.remote) { | ||
let child = Template(childId, client) | ||
let clear = child.listen(() => {}) | ||
track(client, actionId) | ||
.catch(() => { | ||
add(child) | ||
}) | ||
.finally(() => { | ||
clear() | ||
}) | ||
} | ||
} | ||
if (Template.remote) { | ||
unbinds.push( | ||
client.type(createdType, setReason, { event: 'preadd' }), | ||
client.type(createType, setReason, { event: 'preadd' }) | ||
) | ||
} | ||
unbinds.push( | ||
@@ -241,4 +256,2 @@ client.type('logux/subscribed', action => { | ||
}), | ||
client.type(createdType, setReason, { event: 'preadd' }), | ||
client.type(createType, setReason, { event: 'preadd' }), | ||
client.type(createdType, async (action, meta) => { | ||
@@ -295,3 +308,8 @@ if (checkAllFields(action.fields)) { | ||
let clear = child.listen(() => {}) | ||
if (child.value.isLoading) await child.loading | ||
try { | ||
if (child.value.isLoading) await child.loading | ||
/* c8 ignore next 3 */ | ||
} catch { | ||
return | ||
} | ||
if (checkAllFields(child.value)) { | ||
@@ -298,0 +316,0 @@ clear() |
@@ -20,3 +20,3 @@ import { actionEvents, LoguxError } from '@logux/core' | ||
client.emitter.emit('role') | ||
client.node.connection.connect() | ||
if (client.autoconnect) client.node.connection.connect() | ||
} | ||
@@ -74,3 +74,3 @@ } | ||
window.addEventListener('storage', e => this.onStorage(e)) | ||
window.addEventListener('unload', e => this.onUnload(e)) | ||
window.addEventListener('pagehide', e => this.onUnload(e)) | ||
} | ||
@@ -177,3 +177,4 @@ | ||
start() { | ||
start(connect = true) { | ||
this.autoconnect = connect | ||
this.cleanPrevActions() | ||
@@ -188,3 +189,3 @@ | ||
this.emitter.emit('role') | ||
this.node.connection.connect() | ||
if (connect) this.node.connection.connect() | ||
return | ||
@@ -202,3 +203,3 @@ } | ||
this.emitter.emit('role') | ||
this.node.connection.connect() | ||
if (connect) this.node.connection.connect() | ||
return new Promise(resolve => { | ||
@@ -210,10 +211,2 @@ this.unlead = resolve | ||
set state(value) { | ||
this.leaderState = value | ||
} | ||
get state() { | ||
return this.leaderState | ||
} | ||
type(type, listener, opts = {}) { | ||
@@ -228,2 +221,10 @@ if (opts.event === 'preadd') { | ||
} | ||
set state(value) { | ||
this.leaderState = value | ||
} | ||
get state() { | ||
return this.leaderState | ||
} | ||
} |
@@ -16,4 +16,5 @@ import type { Client } from '../client/index.js' | ||
* @param client Observed Client instance. | ||
* @param secret Password for encryption. | ||
* @param opts Encryption options. | ||
* @param secret Password for encryption, or a CryptoKey AES key. | ||
* @param opts Encryption options -- can pass in strings | ||
* to *not* encrypt. | ||
* @returns Unbind listener. | ||
@@ -23,8 +24,8 @@ */ | ||
client: Client, | ||
secret: string, | ||
secret: CryptoKey | string, | ||
opts?: { | ||
ignore: string[] | ||
} | ||
): () => void | ||
): void | ||
export function getRandomSpaces(): string |
@@ -63,5 +63,6 @@ let pool = new Uint8Array(128) | ||
let iv = getRandomBytes(12) | ||
let crypted = await crypto.subtle.encrypt(aes(iv), key, objToBytes(action)) | ||
let encrypted = await crypto.subtle.encrypt(aes(iv), key, objToBytes(action)) | ||
return { | ||
d: bytesToBase64(new Uint8Array(crypted)), | ||
d: bytesToBase64(new Uint8Array(encrypted)), | ||
iv: bytesToBase64(iv), | ||
@@ -83,4 +84,8 @@ type: '0' | ||
let key | ||
async function getKey() { | ||
key = await crypto.subtle.importKey( | ||
if (secret instanceof CryptoKey) { | ||
key = secret | ||
} | ||
async function buildKey() { | ||
return crypto.subtle.importKey( | ||
'raw', | ||
@@ -92,19 +97,9 @@ await sha256(secret), | ||
) | ||
return key | ||
} | ||
let ignore = new Set(opts.ignore || []) | ||
async function outMap(action, meta) { | ||
if (action.type === '0/clean' || ignore.has(action.type)) { | ||
return [action, meta] | ||
} else { | ||
if (!key) key = await getKey() | ||
let encrypted = await encrypt(action, key) | ||
return [encrypted, meta] | ||
} | ||
} | ||
async function inMap(action, meta) { | ||
async function onReceive(action, meta) { | ||
if (action.type === '0') { | ||
if (!key) key = await getKey() | ||
if (!key) key = await buildKey() | ||
let decrypted = await decrypt(action, key) | ||
@@ -117,9 +112,17 @@ return [decrypted, meta] | ||
let originOutMap = client.node.options.outMap | ||
client.node.options.outMap = async (action, meta) => { | ||
let converted = await originOutMap(action, meta) | ||
return outMap(...converted) | ||
let originOnSend = client.node.options.onSend | ||
client.node.options.onSend = async (action, meta) => { | ||
let result = await originOnSend(action, meta) | ||
if (!result) { | ||
return false | ||
} else if (result[0].type === '0/clean' || ignore.has(result[0].type)) { | ||
return [result[0], result[1]] | ||
} else { | ||
if (!key) key = await buildKey() | ||
let encrypted = await encrypt(result[0], key) | ||
return [encrypted, result[1]] | ||
} | ||
} | ||
client.node.options.inMap = inMap | ||
client.node.options.onReceive = onReceive | ||
@@ -126,0 +129,0 @@ client.log.on('clean', (action, meta) => { |
@@ -18,3 +18,5 @@ export { attention } from './attention/index.js' | ||
FilterOptions, | ||
FilterStore | ||
FilterStore, | ||
FilterValue, | ||
LoadedFilterValue | ||
} from './create-filter/index.js' | ||
@@ -43,3 +45,6 @@ export { CrossTabClient } from './cross-tab-client/index.js' | ||
deleteSyncMapById, | ||
ensureLoaded, | ||
LoadedSyncMapValue, | ||
LoadedValue, | ||
loadValue, | ||
SyncMapStore, | ||
@@ -46,0 +51,0 @@ syncMapTemplate, |
@@ -24,2 +24,4 @@ export { attention } from './attention/index.js' | ||
deleteSyncMapById, | ||
ensureLoaded, | ||
loadValue, | ||
syncMapTemplate | ||
@@ -26,0 +28,0 @@ } from './sync-map-template/index.js' |
@@ -249,5 +249,7 @@ import { isFirstOlder } from '@logux/core' | ||
entry.reasons = entry.reasons.filter(i => i !== reason) | ||
entry.meta.reasons = entry.reasons | ||
let process | ||
if (entry.reasons.length === 1) { | ||
entry.meta.reasons = [] | ||
if (entry.reasons.length === 0) { | ||
entry.meta.added = entry.added | ||
@@ -257,4 +259,2 @@ callback(entry.action, entry.meta) | ||
} else { | ||
entry.reasons.splice(entry.reasons.indexOf(reason), 1) | ||
entry.meta.reasons = entry.reasons | ||
process = log.put(entry) | ||
@@ -261,0 +261,0 @@ } |
{ | ||
"name": "@logux/client", | ||
"version": "0.20.1", | ||
"version": "0.21.0", | ||
"description": "Logux base components to build web client", | ||
@@ -28,10 +28,10 @@ "keywords": [ | ||
"engines": { | ||
"node": "^16.0.0 || ^18.0.0 || >=20.0.0" | ||
"node": "^18.0.0 || ^20.0.0 || >=22.0.0" | ||
}, | ||
"peerDependencies": { | ||
"@logux/core": "^0.8.0", | ||
"@logux/core": "^0.9.0", | ||
"@nanostores/preact": ">=0.0.0", | ||
"@nanostores/react": ">=0.0.0", | ||
"@nanostores/vue": ">=0.0.0", | ||
"nanostores": "^0.9.0", | ||
"nanostores": "^0.9.0 || ^0.10.0 || ^0.11.0", | ||
"preact": ">=10.0.0", | ||
@@ -66,8 +66,8 @@ "react": ">=18.0.0", | ||
"dependencies": { | ||
"@logux/actions": "^0.3.0", | ||
"@logux/actions": "^0.4.0", | ||
"fast-json-stable-stringify": "^2.1.0", | ||
"nanodelay": "^2.0.2", | ||
"nanoevents": "^8.0.0", | ||
"nanoid": "^4.0.2" | ||
"nanoevents": "^9.0.0", | ||
"nanoid": "^5.0.7" | ||
} | ||
} |
@@ -127,7 +127,7 @@ import type { LoguxNotFoundError, SyncMapValues } from '@logux/actions' | ||
export function useSync<Value extends SyncMapValues>( | ||
Template: SyncMapTemplateLike<Value>, | ||
Template: SyncMapTemplate<Value> | SyncMapTemplateLike<Value>, | ||
id: string | ||
): SyncMapValue<Value> | ||
export function useSync<Value extends object, Args extends any[]>( | ||
Template: SyncMapTemplateLike<Value, [Client, ...Args]>, | ||
Template: SyncMapTemplate<Value> | SyncMapTemplateLike<Value, Args>, | ||
id: string, | ||
@@ -160,3 +160,3 @@ ...args: Args | ||
export function useFilter<Value extends SyncMapValues>( | ||
Template: SyncMapTemplate<Value>, | ||
Template: SyncMapTemplate<Value> | SyncMapTemplateLike<Value>, | ||
filter?: Filter<Value>, | ||
@@ -163,0 +163,0 @@ opts?: FilterOptions |
@@ -15,3 +15,3 @@ import type { SyncMapValues } from '@logux/actions' | ||
Template: SyncMapTemplateLike<Value>, | ||
value: Omit<Value, 'id'> & { id?: string } | ||
value: { id?: string } & Omit<Value, 'id'> | ||
): MapStore<Value> | ||
@@ -21,3 +21,3 @@ <Value extends SyncMapValues>( | ||
Template: SyncMapTemplate<Value>, | ||
value: Omit<Value, 'id'> & { id?: string } | ||
value: { id?: string } & Omit<Value, 'id'> | ||
): SyncMapStore<Value> | ||
@@ -24,0 +24,0 @@ } |
@@ -19,2 +19,3 @@ import type { LoguxNotFoundError, SyncMapValues } from '@logux/actions' | ||
import type { | ||
SyncMapTemplate, | ||
SyncMapTemplateLike, | ||
@@ -96,3 +97,3 @@ SyncMapValue | ||
export class ChannelErrors extends Component< | ||
ReactErrorHandlers & { children?: ReactNode } | ||
{ children?: ReactNode } & ReactErrorHandlers | ||
> {} | ||
@@ -124,7 +125,7 @@ | ||
export function useSync<Value extends SyncMapValues>( | ||
Template: SyncMapTemplateLike<Value>, | ||
Template: SyncMapTemplate<Value> | SyncMapTemplateLike<Value>, | ||
id: string | ||
): SyncMapValue<Value> | ||
export function useSync<Value extends object, Args extends any[]>( | ||
Template: SyncMapTemplateLike<Value, Args>, | ||
Template: SyncMapTemplate<Value> | SyncMapTemplateLike<Value, Args>, | ||
id: string, | ||
@@ -157,3 +158,3 @@ ...args: Args | ||
export function useFilter<Value extends SyncMapValues>( | ||
Template: SyncMapTemplateLike<Value>, | ||
Template: SyncMapTemplate<Value> | SyncMapTemplateLike<Value>, | ||
filter?: Filter<Value>, | ||
@@ -160,0 +161,0 @@ opts?: FilterOptions |
@@ -27,7 +27,2 @@ # Logux Client [![Cult Of Martians][cult-img]][cult] | ||
<a href="https://evilmartians.com/?utm_source=logux-client"> | ||
<img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg" | ||
alt="Sponsored by Evil Martians" width="236" height="54"> | ||
</a> | ||
[demo page]: https://logux.github.io/client/ | ||
@@ -37,3 +32,9 @@ [cult-img]: http://cultofmartians.com/assets/badges/badge.svg | ||
--- | ||
<img src="https://cdn.evilmartians.com/badges/logo-no-label.svg" alt="" width="22" height="16" /> Made at <b><a href="https://evilmartians.com/devtools?utm_source=logux-client&utm_campaign=devtools-button&utm_medium=github">Evil Martians</a></b>, product consulting for <b>developer tools</b>. | ||
--- | ||
## Install | ||
@@ -40,0 +41,0 @@ |
import type { SyncMapValues } from '@logux/actions' | ||
import type { Action, Meta } from '@logux/core' | ||
import type { MapCreator, MapStore } from 'nanostores' | ||
import type { MapCreator, MapStore, ReadableAtom, StoreValue } from 'nanostores' | ||
import type { Client } from '../client/index.js' | ||
import type { FilterValue, LoadedFilterValue } from '../create-filter/index.js' | ||
@@ -19,2 +20,7 @@ interface SyncMapStoreExt { | ||
/** | ||
* Mark that store was deleted. | ||
*/ | ||
deleted?: true | ||
/** | ||
* While store is loading initial data from server or log. | ||
@@ -40,6 +46,6 @@ */ | ||
export type LoadedSyncMapValue<Value extends SyncMapValues> = Value & { | ||
export type LoadedSyncMapValue<Value extends SyncMapValues> = { | ||
id: string | ||
isLoading: false | ||
} | ||
} & Value | ||
@@ -63,5 +69,5 @@ export type SyncMapValue<Value extends SyncMapValues> = | ||
...args: [] | [Action, Meta, boolean | undefined] | ||
): SyncMapStore<Value> & StoreExt | ||
): StoreExt & SyncMapStore<Value> | ||
cache: { | ||
[id: string]: SyncMapStore<Value> & StoreExt | ||
[id: string]: StoreExt & SyncMapStore<Value> | ||
} | ||
@@ -135,3 +141,3 @@ offline: boolean | ||
Template: SyncMapTemplate<Value>, | ||
value: Value & { id: string } | ||
value: { id: string } & Value | ||
): Promise<void> | ||
@@ -159,3 +165,3 @@ | ||
Template: SyncMapTemplate<Value>, | ||
value: Value & { id: string } | ||
value: { id: string } & Value | ||
): Promise<SyncMapStore<Value>> | ||
@@ -264,1 +270,52 @@ | ||
export function deleteSyncMap(store: SyncMapStore): Promise<void> | ||
/** | ||
* Change store’s value type to value with `isLoaded: false`. | ||
* | ||
* If store is still loading, this function will trow an error. | ||
* | ||
* Use it for tests written on TypeScript. | ||
* | ||
* ```js | ||
* import { ensureLoaded } from '@logux/client' | ||
* | ||
* expect(ensureLoaded($currentUser)).toEqual({ id: 1, name: 'User' }) | ||
* ``` | ||
* | ||
* @param value Store’s value. | ||
*/ | ||
export function ensureLoaded<Value extends SyncMapValues>( | ||
value: SyncMapValue<Value> | ||
): LoadedSyncMapValue<Value> | ||
export function ensureLoaded<Value extends SyncMapValues>( | ||
value: FilterValue<Value> | ||
): LoadedFilterValue<Value> | ||
export type LoadedValue<Type extends { isLoading: boolean }> = { | ||
isLoading: false | ||
} & Type | ||
type LoadableStore = { | ||
readonly loading: Promise<void> | ||
} & ReadableAtom<{ isLoading: boolean }> | ||
/** | ||
* Return store’s value if store is loaded or wait until store will be loaded | ||
* and return its value. | ||
* | ||
* Returns `undefined` on 404. | ||
* | ||
* ```js | ||
* import { loadValue } from '@logux/client' | ||
* | ||
* let user = loadValue($currentUser) | ||
* ``` | ||
* | ||
* @param store Store to load. | ||
*/ | ||
export function loadValue<Store extends SyncMapStore>( | ||
store: Store | ||
): Promise<LoadedValue<StoreValue<Store>> | undefined> | ||
export function loadValue<Store extends LoadableStore>( | ||
store: Store | ||
): Promise<LoadedValue<StoreValue<Store>>> |
@@ -98,5 +98,6 @@ import { isFirstOlder } from '@logux/core' | ||
if (store.remote && !alreadySubscribed) { | ||
let action = createAction.type === createType | ||
? { ...subscribe, creating: true } | ||
: subscribe | ||
let action = | ||
createAction.type === createType | ||
? { ...subscribe, creating: true } | ||
: subscribe | ||
client.log.add(action, { sync: true }) | ||
@@ -106,3 +107,3 @@ } | ||
let endTask = startTask() | ||
let loadingResolve, loadingReject | ||
let loadingReject, loadingResolve | ||
store.loading = new Promise((resolve, reject) => { | ||
@@ -222,4 +223,11 @@ loadingResolve = () => { | ||
client.type(changeType, reasonsForFields, { event: 'preadd', id }), | ||
client.type(deletedType, removeReasons, { id }), | ||
client.type( | ||
deletedType, | ||
() => { | ||
store.deleted = true | ||
removeReasons() | ||
}, | ||
{ id } | ||
), | ||
client.type( | ||
deleteType, | ||
@@ -230,2 +238,3 @@ async (action, meta) => { | ||
await track(client, meta.id) | ||
store.deleted = true | ||
removeReasons() | ||
@@ -346,7 +355,4 @@ } catch { | ||
function addSyncAction(client, Template, action) { | ||
let meta = { indexes: getIndexes(Template.plural, action.id) } | ||
if (!Template.remote) { | ||
action.type += 'd' | ||
} | ||
function addSyncAction(client, Template, action, meta = {}) { | ||
meta.indexes = getIndexes(Template.plural, action.id) | ||
if (Template.remote) { | ||
@@ -361,7 +367,22 @@ return task(() => client.sync(action, meta)) | ||
let { id, ...fields } = value | ||
return addSyncAction(client, Template, { | ||
fields, | ||
id, | ||
type: `${Template.plural}/create` | ||
}) | ||
if (Template.remote) { | ||
return addSyncAction(client, Template, { | ||
fields, | ||
id, | ||
type: `${Template.plural}/create` | ||
}) | ||
} else { | ||
return addSyncAction( | ||
client, | ||
Template, | ||
{ | ||
fields, | ||
id, | ||
type: `${Template.plural}/created` | ||
}, | ||
{ | ||
reasons: Object.keys(fields).map(i => `${Template.plural}/${id}/${i}`) | ||
} | ||
) | ||
} | ||
} | ||
@@ -381,3 +402,7 @@ | ||
} | ||
if (Template.remote) meta.sync = true | ||
if (Template.remote) { | ||
meta.sync = true | ||
} else { | ||
meta.reasons = Object.keys(fields).map(i => `${Template.plural}/${id}/${i}`) | ||
} | ||
await task(() => client.log.add(action, meta)) | ||
@@ -389,9 +414,29 @@ | ||
export function changeSyncMapById(client, Template, id, fields, value) { | ||
export async function changeSyncMapById(client, Template, id, fields, value) { | ||
if (value) fields = { [fields]: value } | ||
return addSyncAction(client, Template, { | ||
fields, | ||
id, | ||
type: `${Template.plural}/change` | ||
}) | ||
if (Template.remote) { | ||
return addSyncAction(client, Template, { | ||
fields, | ||
id, | ||
type: `${Template.plural}/change` | ||
}) | ||
} else { | ||
let reasons = Object.keys(fields).map(i => `${Template.plural}/${id}/${i}`) | ||
let meta = await addSyncAction( | ||
client, | ||
Template, | ||
{ | ||
fields, | ||
id, | ||
type: `${Template.plural}/changed` | ||
}, | ||
{ reasons: [...reasons] } | ||
) | ||
return Promise.all( | ||
reasons.map(reason => { | ||
return client.log.removeReason(reason, { olderThan: meta }) | ||
}) | ||
) | ||
} | ||
} | ||
@@ -405,7 +450,21 @@ | ||
export function deleteSyncMapById(client, Template, id) { | ||
return addSyncAction(client, Template, { | ||
id, | ||
type: `${Template.plural}/delete` | ||
}) | ||
export async function deleteSyncMapById(client, Template, id) { | ||
if (Template.remote) { | ||
return addSyncAction(client, Template, { | ||
id, | ||
type: `${Template.plural}/delete` | ||
}) | ||
} else { | ||
let store = Template.client ? Template : Template(id, client) | ||
if (store.get().isLoading) await store.loading | ||
await Promise.all( | ||
Object.keys(store.get()) | ||
.filter(i => i !== 'id' && i !== 'isLoading') | ||
.map(key => client.log.removeReason(`${Template.plural}/${id}/${key}`)) | ||
) | ||
return addSyncAction(client, Template, { | ||
id, | ||
type: `${Template.plural}/deleted` | ||
}) | ||
} | ||
} | ||
@@ -416,1 +475,28 @@ | ||
} | ||
export function ensureLoaded(value) { | ||
if (value.isLoading) throw new Error('Store was not loaded yet') | ||
return value | ||
} | ||
export async function loadValue(store) { | ||
let value = store.get() | ||
if (value.isLoading) { | ||
let unbind = store.listen(() => {}) | ||
try { | ||
await store.loading | ||
} catch (e) { | ||
if (e.name === 'LoguxUndoError' && e.action.reason === 'notFound') { | ||
return undefined | ||
/* c8 ignore next 3 */ | ||
} else { | ||
throw e | ||
} | ||
} finally { | ||
unbind() | ||
} | ||
return store.get() | ||
} else { | ||
return value | ||
} | ||
} |
@@ -47,26 +47,29 @@ import { parseId, ServerNode, TestTime } from '@logux/core' | ||
this.connected.add(nodeId) | ||
let server = this | ||
let node = new ServerNode('server:id', this.log, connection, { | ||
async inMap(action, meta) { | ||
onReceive(action, meta) { | ||
return [action, { ...meta, subprotocol: node.remoteSubprotocol }] | ||
}, | ||
outFilter: async (action, meta) => { | ||
onSend(action, meta) { | ||
let access | ||
if (meta.channels) { | ||
return meta.channels.some(channel => { | ||
let nodes = this.subscriptions[channel] || {} | ||
access = meta.channels.some(channel => { | ||
let nodes = server.subscriptions[channel] || {} | ||
return !!nodes[nodeId] | ||
}) | ||
} else if (meta.nodes) { | ||
access = meta.nodes.includes(nodeId) | ||
} else { | ||
access = | ||
!action.type.startsWith('logux/') && !meta.channels && !meta.nodes | ||
} | ||
if (meta.nodes) { | ||
return meta.nodes.includes(nodeId) | ||
if (access) { | ||
let cleaned = {} | ||
for (let i in meta) { | ||
if (i !== 'nodes' && i !== 'channels') cleaned[i] = meta[i] | ||
} | ||
return [action, cleaned] | ||
} else { | ||
return false | ||
} | ||
return ( | ||
!action.type.startsWith('logux/') && !meta.channels && !meta.nodes | ||
) | ||
}, | ||
async outMap(action, meta) { | ||
let cleaned = {} | ||
for (let i in meta) { | ||
if (i !== 'nodes' && i !== 'channels') cleaned[i] = meta[i] | ||
} | ||
return [action, cleaned] | ||
} | ||
@@ -73,0 +76,0 @@ }) |
export function track(client, id) { | ||
if (client.processing[id]) return client.processing[id][0] | ||
let resolveCallback, rejectCallback | ||
let rejectCallback, resolveCallback | ||
let promise = new Promise((resolve, reject) => { | ||
@@ -6,0 +6,0 @@ resolveCallback = resolve |
@@ -14,3 +14,7 @@ import type { LoguxNotFoundError, SyncMapValues } from '@logux/actions' | ||
import type { Client } from '../client/index.js' | ||
import type { Filter, FilterOptions, FilterStore } from '../create-filter/index.js' | ||
import type { | ||
Filter, | ||
FilterOptions, | ||
FilterStore | ||
} from '../create-filter/index.js' | ||
import type { ChannelError } from '../logux-undo-error/index.js' | ||
@@ -93,7 +97,7 @@ import type { | ||
export function useSync<Value extends SyncMapValues>( | ||
Template: SyncMapTemplateLike<Value>, | ||
Template: SyncMapTemplate<Value> | SyncMapTemplateLike<Value>, | ||
id: Refable<string> | ||
): ReadonlyRef<SyncMapValue<Value>> | ||
export function useSync<Value extends object, Args extends any[]>( | ||
Template: SyncMapTemplateLike<Value, Args>, | ||
Template: SyncMapTemplate<Value> | SyncMapTemplateLike<Value, Args>, | ||
id: Refable<string>, | ||
@@ -133,3 +137,3 @@ ...args: Args | ||
export function useFilter<Value extends SyncMapValues>( | ||
Template: SyncMapTemplate<Value>, | ||
Template: SyncMapTemplate<Value> | SyncMapTemplateLike<Value>, | ||
filter?: Refable<Filter<Value>>, | ||
@@ -136,0 +140,0 @@ opts?: Refable<FilterOptions> |
@@ -21,3 +21,3 @@ import { registerStore, useStore } from '@nanostores/vue' | ||
const createSymbol = name => { | ||
function createSymbol(name) { | ||
return process.env.NODE_ENV !== 'production' ? Symbol(name) : Symbol() | ||
@@ -71,5 +71,6 @@ } | ||
getCurrentScope() && onScopeDispose(() => { | ||
unsubscribe() | ||
}) | ||
getCurrentScope() && | ||
onScopeDispose(() => { | ||
unsubscribe() | ||
}) | ||
@@ -83,6 +84,10 @@ if (process.env.NODE_ENV !== 'production') { | ||
function syncRefs (source, target) { | ||
return watch(source, value => { | ||
target.value = value | ||
}, { deep: true, flush: 'sync', immediate: true }) | ||
function syncRefs(source, target) { | ||
return watch( | ||
source, | ||
value => { | ||
target.value = value | ||
}, | ||
{ deep: true, flush: 'sync', immediate: true } | ||
) | ||
} | ||
@@ -89,0 +94,0 @@ |
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
154501
5180
70
+ Added@logux/actions@0.4.0(transitive)
+ Added@logux/core@0.9.0(transitive)
+ Addednanoevents@9.1.0(transitive)
+ Addednanoid@5.0.9(transitive)
- Removed@logux/actions@0.3.1(transitive)
- Removed@logux/core@0.8.5(transitive)
- Removednanoevents@7.0.18.0.0(transitive)
- Removednanoid@4.0.2(transitive)
- Removednanostores@0.9.5(transitive)
Updated@logux/actions@^0.4.0
Updatednanoevents@^9.0.0
Updatednanoid@^5.0.7