-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
23 changed files
with
555 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,17 @@ | ||
U2FsdGVkX19utyKMkhE4ZmeXOKYyMLrRwLLssEpByjjWw3vNO3CLHulQZm/y9qYN | ||
3Nymgm9K/8OcJAbKvOdKH8CANly0E4nXTsnnmNdcX4Lp7SJ0uPLT7Bun64/34JZW | ||
CzeBNAb5WxGCFva6KOyMqja0NjXSZEokIXVyt2DzaHOM6DqrBCN8TPNpJtKw0FQb | ||
l8wboKuf6FRaW6MRLgnl394rbQr3NJ0AmhbM58UaotYYGPS+TV4vvLjDTNNfJvkV | ||
ACL9h6xYEWdP0ub6Mk/pq6Mwa3dOks73BUCXMnitDhR2V11Ce9T88M2DkQ9YJAUJ | ||
YH+GOxw5m8f0VBHRZwe/XWAdaZJRJxfCx39mJpSwUV4r6hX7aFkjxJ4R+xyATACL | ||
L6jkcIkZFwtf+Cc4ITVjC7iQ8Xhh95PY5R0YgHrh/EeKCxPq4MqcTZX9ttlAy4Pq | ||
f5J+lTCOY8ZAH9Dfm97Ll6BwyB/rHOvhyAhydVYD9hQiBNBP/LNyPZ5fYacNbL6x | ||
Hd9JKIP2w/0UURptZoisVMngXDF5X2TNypFl4TRbYUsps7CRcNlhMymJq+veGfk9 | ||
SJCllxCVe3wJL+07nfRbQyHYZgsxPCbWER92yW4UPRfmRtWi5pClD7xtkUvq5kN0 | ||
esYHo+TIhEjn+DmQtF1HmfEKyQtY4rv598YyjNJ+LDCFGC9ZKNiOmXx6JDzTfoz4 | ||
Y59a+WjN5IJ3VO5nDTyr18EFDgDqJ6wxg+J7bQzmGH/HFvD/S5aeTCxbGVRjHmaK | ||
yYn3Rux6wf9cr2xpZjEZaZrNbjeJ+QRJX0dalzyO6hsuNWCnIvFKrKNp8AxjGqHN | ||
W7L7IdkOSS6eqNp2x5dEjA== | ||
U2FsdGVkX1+xuzPPE1ep+hVLPWUU56j5bvx8MiVNF9jKHjIXIB/9lIdENKGqceKm | ||
shaVMz3kNApDcQfe4A+PpRvmB+I4wk3PtjlCBWap7s4aJaI049hS6BiimCY+B5V0 | ||
KV9aqTApj7rQMueRopMIn6+hrrCjX09dUj3+0ftiOzN6Z+AABvTIdBpyBydQbJRD | ||
auRtgTOkoScLAYStfx+r3dHxabzjb17cbPaSTmpRnCo4zV863JDVFw3I4ate/3Fj | ||
Wfx2qhh++DGWuiNf/J6Yqq0wh8kkSPh0EV4areD9DtoJHtcClHnZeyZeGGI5FRVp | ||
71AR2aT0IiWGxh5pykAjx/7b74/nu5g7DaPHD01hu3OjdjZHKwLbXsYMJT7pLyI7 | ||
NYN5uCWXKU/IC+WyyKc42hqydi62g4FW+BeKLMU0N5bznOCwnl280of2wBGtADGV | ||
bfCvt2b+ZBoYAwCVerUXIm5dj4ARX/y1ZtX9+B/A2s9McyZktZwLPsFakXs83hrn | ||
BNDcdhVfEsE7cyqwoJHVAJIBXnB7S/SrdGQeFFG+LdRbufrNEvwwAheJKvxIn37h | ||
p8P6LsXUusM6eaEQk8jUlMy5zatw0b/V/lNi0AtiMDqe4ru2T15GasENVHldfQDE | ||
NN0xWAwAAZ1ovvKxL1PndhatWN5dqs+WfnB8gBj7//mjVusq8e+A1yDUiJeIgp1I | ||
7AbXEc1F7gHFGbOZIESxSnjzDZXBP7bEbRR6ytcA4/oJ3lj6p93pNwxPaTjucTep | ||
37fnyB9QUcM9w71cBtNJx8C1fRsvxjA7mHJ7uEKRSda0N5VzAKPGjNsS2VABTLlX | ||
PL905CqdiL3YOwLBG5A4rkQQnH8dlve15BpCIY/eOoU4FX13kl9SokbRavrOOKPY | ||
GNnenxFZZ6n+DHR9R68OfyIFOJhTuyVpfyzajD7kS6Plvq3DNtvZ+07ViNT7OaFl | ||
yoDlYHrfvMKNYxrbOIW874/yFerUs/e/v9APkTf6h4xz5RLVBhLx4PasrM35vqCt | ||
E1Ql0FyVmxsP3SqhfHSJAHcD15x9IJKkWKVN3f8KX6M= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# Alwatr Signal - `@alwatr/signal2` | ||
|
||
Elegant powerful event system for handle global signals and states written in tiny TypeScript module. | ||
|
||
Every signal has own value and can be used as a advance **state management** like redux and recoil without the complexities and unnecessary facilities of those libraries. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
{ | ||
"name": "@alwatr/signal2", | ||
"version": "0.32.0", | ||
"description": "Elegant powerful event system for handle global signals and states written in tiny TypeScript module.", | ||
"keywords": [ | ||
"signal", | ||
"typescript", | ||
"esm", | ||
"alwatr" | ||
], | ||
"main": "index.js", | ||
"type": "module", | ||
"types": "index.d.ts", | ||
"author": "S. Ali Mihandoost <[email protected]> (https://ali.mihandoost.com)", | ||
"license": "MIT", | ||
"private": true, | ||
"files": [ | ||
"**/*.{d.ts.map,d.ts,js.map,js,html,md}" | ||
], | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/AliMD/alwatr", | ||
"directory": "core/signal2" | ||
}, | ||
"homepage": "https://github.com/AliMD/alwatr/tree/main/core/signal2#readme", | ||
"bugs": { | ||
"url": "https://github.com/AliMD/alwatr/issues" | ||
}, | ||
"dependencies": { | ||
"@alwatr/logger": "^0.32.0", | ||
"@alwatr/type": "^0.32.0", | ||
"@alwatr/util": "^0.32.0", | ||
"tslib": "^2.5.2" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
import {createLogger} from '@alwatr/logger'; | ||
|
||
import type {SubscribeOptions, ListenerCallback, ListenerObject, SubscribeResult as SubscribeResult} from './type.js'; | ||
|
||
/** | ||
* Alwatr base signal. | ||
*/ | ||
export abstract class AlwatrBaseSignal<TDetail> { | ||
protected _logger; | ||
protected _$detail?: TDetail; | ||
protected _$listenerList: Array<ListenerObject<TDetail>> = []; | ||
|
||
constructor(protected name: string, loggerPrefix: string) { | ||
this._logger = createLogger(`{${loggerPrefix}: ${name}}`); | ||
this._logger.logMethod?.('constructor'); | ||
} | ||
|
||
/** | ||
* Get detail. | ||
* | ||
* Return undefined if context not set before or expired. | ||
*/ | ||
protected _getDetail(): TDetail | undefined { | ||
this._logger.logMethodFull?.('_getDetail', {}, this._$detail); | ||
return this._$detail; | ||
} | ||
|
||
/** | ||
* change _detail and execute all listeners. | ||
*/ | ||
protected _dispatch(detail: TDetail): void { | ||
this._logger.logMethodArgs?.('_dispatch', {detail}); | ||
this._$detail = detail; | ||
setTimeout(() => this._$executeListeners(), 0); | ||
} | ||
|
||
/** | ||
* Execute all listeners callback. | ||
*/ | ||
protected _$executeListeners(): void { | ||
this._logger.logMethod?.('_$executeListeners'); | ||
if (this._$detail === undefined) return; | ||
|
||
const removeList: Array<ListenerObject<TDetail>> = []; | ||
|
||
for (const listener of this._$listenerList) { | ||
if (listener.options.disabled) continue; | ||
if (listener.options.once) removeList.push(listener); | ||
|
||
try { | ||
const ret = listener.callback(this._$detail); | ||
if (ret instanceof Promise) { | ||
ret.catch((err) => this._logger.error('_executeListeners', 'call_listener_failed', err)); | ||
} | ||
} | ||
catch (err) { | ||
this._logger.error('_executeListeners', 'call_listener_failed', err); | ||
} | ||
} | ||
|
||
for (const listener of removeList) { | ||
this.unsubscribe(listener.callback); | ||
} | ||
} | ||
|
||
/** | ||
* Subscribe to context changes. | ||
*/ | ||
subscribe(listenerCallback: ListenerCallback<TDetail>, options: SubscribeOptions = {}): SubscribeResult { | ||
this._logger.logMethodArgs?.('subscribe', {options}); | ||
|
||
const _listenerObject: ListenerObject<TDetail> = { | ||
callback: listenerCallback, | ||
options, | ||
}; | ||
|
||
let callbackExecuted = false; | ||
const detail = this._$detail; | ||
if (detail !== undefined && options.receivePrevious === true && options.disabled !== true) { | ||
// Run callback for old dispatch signal | ||
callbackExecuted = true; | ||
setTimeout(() => { | ||
try { | ||
listenerCallback(detail); | ||
} | ||
catch (err) { | ||
this._logger.error('subscribe.receivePrevious', 'call_signal_callback_failed', err); | ||
} | ||
}, 0); | ||
} | ||
|
||
// if once then must remove listener after fist callback called! then why push it to listenerList?! | ||
if (options.once !== true || callbackExecuted === true) { | ||
if (options.priority === true) { | ||
this._$listenerList.unshift(_listenerObject); | ||
} | ||
else { | ||
this._$listenerList.push(_listenerObject); | ||
} | ||
} | ||
|
||
return { | ||
unsubscribe: this.unsubscribe.bind(this, listenerCallback), | ||
}; | ||
} | ||
|
||
/** | ||
* Unsubscribe from context. | ||
*/ | ||
unsubscribe(listenerCallback: ListenerCallback<TDetail>): void { | ||
this._logger.logMethod?.('unsubscribe'); | ||
const listenerIndex = this._$listenerList.findIndex((listener) => listener.callback === listenerCallback); | ||
if (listenerIndex !== -1) { | ||
void this._$listenerList.splice(listenerIndex, 1); | ||
} | ||
} | ||
|
||
/** | ||
* Clear current context detail without notify subscribers. | ||
* | ||
* `receivePrevious` in new subscribers not work until new context changes. | ||
*/ | ||
protected _expire(): void { | ||
this._logger.logMethod?.('expire'); | ||
this._$detail = undefined; | ||
} | ||
|
||
/** | ||
* Get the detail of the next context changes. | ||
*/ | ||
protected _untilChange(): Promise<TDetail> { | ||
this._logger.logMethod?.('untilNext'); | ||
return new Promise((resolve) => { | ||
this.subscribe(resolve, { | ||
once: true, | ||
priority: true, | ||
receivePrevious: false, | ||
}); | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import {AlwatrBaseSignal} from './base.js'; | ||
|
||
/** | ||
* Alwatr context signal. | ||
*/ | ||
export class AlwatrContextSignal<TValue> extends AlwatrBaseSignal<TValue> { | ||
constructor(public override name: string) { | ||
super(name, 'context-signal'); | ||
} | ||
|
||
/** | ||
* Get context value. | ||
* | ||
* Return undefined if context not set before or expired. | ||
*/ | ||
getValue(): TValue | undefined { | ||
return this._getDetail(); | ||
} | ||
|
||
/** | ||
* Set context value and notify all subscribers. | ||
*/ | ||
setValue(value: TValue): void { | ||
this._logger.logMethodArgs?.('setValue', {value}); | ||
this._dispatch(value); | ||
} | ||
|
||
/** | ||
* Clear current context value without notify subscribers. | ||
* | ||
* `receivePrevious` in new subscribers not work until new context changes. | ||
*/ | ||
expire(): void { | ||
this._expire(); | ||
} | ||
|
||
/** | ||
* Get the value of the next context changes. | ||
*/ | ||
untilChange(): Promise<TValue> { | ||
return this._untilChange(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import {AlwatrBaseSignal} from './base.js'; | ||
|
||
/** | ||
* Alwatr event signal. | ||
*/ | ||
export class AlwatrEventSignal<TDetail> extends AlwatrBaseSignal<TDetail> { | ||
constructor(public override name: string) { | ||
super(name, 'event-signal'); | ||
} | ||
|
||
/** | ||
* Dispatch an event to all listeners. | ||
*/ | ||
dispatch(detail: TDetail): void { | ||
this._dispatch(detail); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import {globalAlwatr} from '@alwatr/logger'; | ||
|
||
export * from './simple-signal.js'; | ||
export * from './event.js'; | ||
export * from './context.js'; | ||
export * from './multithread-context.js'; | ||
|
||
globalAlwatr.registeredList.push({ | ||
name: '@alwatr/signal2', | ||
version: _ALWATR_VERSION_, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import {createLogger} from '@alwatr/logger'; | ||
|
||
import {AlwatrContextSignal} from './context.js'; | ||
|
||
type AlwatrContextChangedMessage = { | ||
type: 'alwatr_context_changed'; | ||
name: string; | ||
payload: unknown; | ||
}; | ||
|
||
/** | ||
* Alwatr multithread context signal. | ||
*/ | ||
export class AlwatrMultithreadContextSignal<TValue> extends AlwatrContextSignal<TValue> { | ||
protected static _logger = createLogger(`alwatr/mt-context`); | ||
protected static _worker?: Worker; | ||
protected static _registry: Record<string, AlwatrMultithreadContextSignal<unknown> | undefined> = {}; | ||
|
||
static setupChannel(worker: Worker = self as unknown as Worker): void { | ||
AlwatrMultithreadContextSignal._worker = worker; | ||
worker.addEventListener('message', AlwatrMultithreadContextSignal._onMessage); | ||
} | ||
|
||
static _onMessage(event: MessageEvent): void { | ||
const message = event.data as AlwatrContextChangedMessage; | ||
if (message.type !== 'alwatr_context_changed') return; | ||
AlwatrMultithreadContextSignal._logger.logMethodArgs?.('_onMessage', {message}); | ||
const context = AlwatrMultithreadContextSignal._registry[message.name]; | ||
if (context === undefined) { | ||
throw new Error('context_not_define', {cause: 'context not define in this thread yet!'}); | ||
} | ||
context._dispatch(message.payload); | ||
} | ||
|
||
static _postMessage(name: string, payload: unknown): void { | ||
AlwatrMultithreadContextSignal._logger.logMethodArgs?.('_postMessage', {name, payload}); | ||
if (AlwatrMultithreadContextSignal._worker === undefined) { | ||
throw new Error('worker_not_defined', {cause: 'setupChannel must be called before any setValue.'}); | ||
} | ||
AlwatrMultithreadContextSignal._worker.postMessage(<AlwatrContextChangedMessage>{ | ||
type: 'alwatr_context_changed', | ||
name, | ||
payload, | ||
}); | ||
} | ||
|
||
constructor(name: string) { | ||
super(name); | ||
if (AlwatrMultithreadContextSignal._registry[name] !== undefined) { | ||
throw new Error('context_name_exist'); | ||
} | ||
AlwatrMultithreadContextSignal._registry[name] = this as AlwatrMultithreadContextSignal<unknown>; | ||
} | ||
|
||
/** | ||
* Set context value and notify all subscribers. | ||
*/ | ||
override setValue(value: TValue): void { | ||
super.setValue(value); | ||
AlwatrMultithreadContextSignal._postMessage(this.name, value); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import {AlwatrBaseSignal} from './base.js'; | ||
|
||
/** | ||
* Alwatr event signal without any detail. | ||
*/ | ||
export class AlwatrSimpleSignal extends AlwatrBaseSignal<undefined> { | ||
constructor(public override name: string) { | ||
super(name, 'event-signal'); | ||
} | ||
|
||
/** | ||
* Dispatch an event to all listeners. | ||
*/ | ||
dispatch(): void { | ||
this._dispatch(undefined); | ||
} | ||
} |
Oops, something went wrong.