@wixc3/patterns
Advanced tools
Comparing version 15.3.1 to 16.0.0
@@ -28,3 +28,2 @@ import { Signal } from './signal'; | ||
protected events: Map<EventId, Signal<any>>; | ||
protected emitOnce: Map<EventId, Signal<any>>; | ||
/** | ||
@@ -37,2 +36,3 @@ * Check if an event has subscribers | ||
* Subscribe a handler for event | ||
* | ||
* @returns unsubscribe fn | ||
@@ -46,3 +46,4 @@ */ | ||
/** | ||
* Adds a handler that will be called at most once | ||
* Adds a handler that will be called at most once. | ||
* | ||
* @returns unsubscribe fn | ||
@@ -49,0 +50,0 @@ */ |
@@ -32,3 +32,2 @@ "use strict"; | ||
this.events = new Map(); | ||
this.emitOnce = new Map(); | ||
/** | ||
@@ -41,2 +40,3 @@ * Check if an event has subscribers | ||
* Subscribe a handler for event | ||
* | ||
* @returns unsubscribe fn | ||
@@ -46,3 +46,3 @@ */ | ||
const bucket = this.events.get(event); | ||
bucket ? bucket.add(handler) : this.events.set(event, new signal_1.Signal([handler])); | ||
bucket ? bucket.subscribe(handler) : this.events.set(event, new signal_1.Signal([handler])); | ||
return () => this.unsubscribe(event, handler); | ||
@@ -55,9 +55,13 @@ }; | ||
/** | ||
* Adds a handler that will be called at most once | ||
* Adds a handler that will be called at most once. | ||
* | ||
* @returns unsubscribe fn | ||
*/ | ||
this.once = (event, handler) => { | ||
this.off(event, handler); | ||
const bucket = this.emitOnce.get(event); | ||
bucket ? bucket.add(handler) : this.emitOnce.set(event, new signal_1.Signal([handler])); | ||
let bucket = this.events.get(event); | ||
if (!bucket) { | ||
bucket = new signal_1.Signal(); | ||
this.events.set(event, bucket); | ||
} | ||
bucket.once(handler); | ||
return () => this.unsubscribe(event, handler); | ||
@@ -69,8 +73,7 @@ }; | ||
this.unsubscribe = (event, handler) => { | ||
let bucket = this.events.get(event); | ||
bucket === null || bucket === void 0 ? void 0 : bucket.delete(handler); | ||
(bucket === null || bucket === void 0 ? void 0 : bucket.size) === 0 && this.events.delete(event); | ||
bucket = this.emitOnce.get(event); | ||
bucket === null || bucket === void 0 ? void 0 : bucket.delete(handler); | ||
(bucket === null || bucket === void 0 ? void 0 : bucket.size) === 0 && this.events.delete(event); | ||
const bucket = this.events.get(event); | ||
bucket === null || bucket === void 0 ? void 0 : bucket.unsubscribe(handler); | ||
if ((bucket === null || bucket === void 0 ? void 0 : bucket.size) === 0) { | ||
this.events.delete(event); | ||
} | ||
}; | ||
@@ -87,3 +90,2 @@ /** | ||
this.events.delete(event); | ||
this.emitOnce.delete(event); | ||
}; | ||
@@ -94,4 +96,4 @@ /** | ||
this.clear = () => { | ||
this.events.forEach((bucket) => bucket.clear()); | ||
this.events = new Map(); | ||
this.emitOnce = new Map(); | ||
}; | ||
@@ -104,6 +106,4 @@ /** | ||
this.notify = (event, data) => { | ||
var _a, _b; | ||
var _a; | ||
(_a = this.events.get(event)) === null || _a === void 0 ? void 0 : _a.notify(data); | ||
(_b = this.emitOnce.get(event)) === null || _b === void 0 ? void 0 : _b.notify(data); | ||
this.emitOnce.delete(event); | ||
}; | ||
@@ -110,0 +110,0 @@ /** |
@@ -33,14 +33,26 @@ export type Listener<T> = (data: T) => void; | ||
*/ | ||
export declare class Signal<T> extends Set<Listener<T>> { | ||
private onceHandlers; | ||
export declare class Signal<T> { | ||
private handlers; | ||
constructor(handlers?: Listener<T>[]); | ||
/** | ||
* Subscribe a notification callback | ||
* | ||
* @param handler - Will be executed with a data arg when a notification occurs | ||
*/ | ||
subscribe: (handler: Listener<T>) => void; | ||
/** | ||
* Subscribe to only the next notification | ||
* | ||
* @param handler - Will be executed with a data arg when a notification occurs | ||
*/ | ||
once: (handler: Listener<T>) => void; | ||
/** | ||
* @returns true if a listener is subscribed | ||
*/ | ||
has(value: Listener<T>): boolean; | ||
/** | ||
* Unsubscribe an existing callback | ||
*/ | ||
unsubscribe: (handler: Listener<T>) => void; | ||
get size(): number; | ||
/** | ||
@@ -47,0 +59,0 @@ * Notify all subscribers with arg data |
@@ -35,15 +35,30 @@ "use strict"; | ||
*/ | ||
class Signal extends Set { | ||
constructor() { | ||
super(...arguments); | ||
this.onceHandlers = new Set(); | ||
class Signal { | ||
constructor(handlers) { | ||
this.handlers = new Map(); | ||
/** | ||
* Subscribe a notification callback | ||
* | ||
* @param handler - Will be executed with a data arg when a notification occurs | ||
*/ | ||
this.subscribe = (handler) => { | ||
this.add(handler); | ||
if (this.handlers.get(handler) !== true) { | ||
this.handlers.set(handler, false); | ||
} | ||
else { | ||
throw new Error(`handler already exists as "once" listener`); | ||
} | ||
}; | ||
/** | ||
* Subscribe to only the next notification | ||
* | ||
* @param handler - Will be executed with a data arg when a notification occurs | ||
*/ | ||
this.once = (handler) => { | ||
this.onceHandlers.add(handler); | ||
if (this.handlers.get(handler) !== false) { | ||
this.handlers.set(handler, true); | ||
} | ||
else { | ||
throw new Error(`handler already exists as persistent listener`); | ||
} | ||
}; | ||
@@ -54,4 +69,3 @@ /** | ||
this.unsubscribe = (handler) => { | ||
this.onceHandlers.delete(handler); | ||
this.delete(handler); | ||
this.handlers.delete(handler); | ||
}; | ||
@@ -62,14 +76,22 @@ /** | ||
this.notify = (data) => { | ||
for (const handler of this) { | ||
for (const [handler, isOnce] of this.handlers) { | ||
handler(data); | ||
if (isOnce) { | ||
this.handlers.delete(handler); | ||
} | ||
} | ||
for (const handler of this.onceHandlers) { | ||
handler(data); | ||
this.onceHandlers.delete(handler); | ||
} | ||
}; | ||
handlers === null || handlers === void 0 ? void 0 : handlers.forEach((handler) => this.subscribe(handler)); | ||
} | ||
/** | ||
* @returns true if a listener is subscribed | ||
*/ | ||
has(value) { | ||
return this.handlers.has(value); | ||
} | ||
get size() { | ||
return this.handlers.size; | ||
} | ||
clear() { | ||
super.clear(); | ||
this.onceHandlers.clear(); | ||
this.handlers.clear(); | ||
} | ||
@@ -76,0 +98,0 @@ } |
@@ -28,3 +28,2 @@ import { Signal } from './signal'; | ||
protected events: Map<EventId, Signal<any>>; | ||
protected emitOnce: Map<EventId, Signal<any>>; | ||
/** | ||
@@ -37,2 +36,3 @@ * Check if an event has subscribers | ||
* Subscribe a handler for event | ||
* | ||
* @returns unsubscribe fn | ||
@@ -46,3 +46,4 @@ */ | ||
/** | ||
* Adds a handler that will be called at most once | ||
* Adds a handler that will be called at most once. | ||
* | ||
* @returns unsubscribe fn | ||
@@ -49,0 +50,0 @@ */ |
@@ -29,3 +29,2 @@ import { Signal } from './signal'; | ||
this.events = new Map(); | ||
this.emitOnce = new Map(); | ||
/** | ||
@@ -38,2 +37,3 @@ * Check if an event has subscribers | ||
* Subscribe a handler for event | ||
* | ||
* @returns unsubscribe fn | ||
@@ -43,3 +43,3 @@ */ | ||
const bucket = this.events.get(event); | ||
bucket ? bucket.add(handler) : this.events.set(event, new Signal([handler])); | ||
bucket ? bucket.subscribe(handler) : this.events.set(event, new Signal([handler])); | ||
return () => this.unsubscribe(event, handler); | ||
@@ -52,9 +52,13 @@ }; | ||
/** | ||
* Adds a handler that will be called at most once | ||
* Adds a handler that will be called at most once. | ||
* | ||
* @returns unsubscribe fn | ||
*/ | ||
this.once = (event, handler) => { | ||
this.off(event, handler); | ||
const bucket = this.emitOnce.get(event); | ||
bucket ? bucket.add(handler) : this.emitOnce.set(event, new Signal([handler])); | ||
let bucket = this.events.get(event); | ||
if (!bucket) { | ||
bucket = new Signal(); | ||
this.events.set(event, bucket); | ||
} | ||
bucket.once(handler); | ||
return () => this.unsubscribe(event, handler); | ||
@@ -66,8 +70,7 @@ }; | ||
this.unsubscribe = (event, handler) => { | ||
let bucket = this.events.get(event); | ||
bucket === null || bucket === void 0 ? void 0 : bucket.delete(handler); | ||
(bucket === null || bucket === void 0 ? void 0 : bucket.size) === 0 && this.events.delete(event); | ||
bucket = this.emitOnce.get(event); | ||
bucket === null || bucket === void 0 ? void 0 : bucket.delete(handler); | ||
(bucket === null || bucket === void 0 ? void 0 : bucket.size) === 0 && this.events.delete(event); | ||
const bucket = this.events.get(event); | ||
bucket === null || bucket === void 0 ? void 0 : bucket.unsubscribe(handler); | ||
if ((bucket === null || bucket === void 0 ? void 0 : bucket.size) === 0) { | ||
this.events.delete(event); | ||
} | ||
}; | ||
@@ -84,3 +87,2 @@ /** | ||
this.events.delete(event); | ||
this.emitOnce.delete(event); | ||
}; | ||
@@ -91,4 +93,4 @@ /** | ||
this.clear = () => { | ||
this.events.forEach((bucket) => bucket.clear()); | ||
this.events = new Map(); | ||
this.emitOnce = new Map(); | ||
}; | ||
@@ -101,6 +103,4 @@ /** | ||
this.notify = (event, data) => { | ||
var _a, _b; | ||
var _a; | ||
(_a = this.events.get(event)) === null || _a === void 0 ? void 0 : _a.notify(data); | ||
(_b = this.emitOnce.get(event)) === null || _b === void 0 ? void 0 : _b.notify(data); | ||
this.emitOnce.delete(event); | ||
}; | ||
@@ -107,0 +107,0 @@ /** |
@@ -33,14 +33,26 @@ export type Listener<T> = (data: T) => void; | ||
*/ | ||
export declare class Signal<T> extends Set<Listener<T>> { | ||
private onceHandlers; | ||
export declare class Signal<T> { | ||
private handlers; | ||
constructor(handlers?: Listener<T>[]); | ||
/** | ||
* Subscribe a notification callback | ||
* | ||
* @param handler - Will be executed with a data arg when a notification occurs | ||
*/ | ||
subscribe: (handler: Listener<T>) => void; | ||
/** | ||
* Subscribe to only the next notification | ||
* | ||
* @param handler - Will be executed with a data arg when a notification occurs | ||
*/ | ||
once: (handler: Listener<T>) => void; | ||
/** | ||
* @returns true if a listener is subscribed | ||
*/ | ||
has(value: Listener<T>): boolean; | ||
/** | ||
* Unsubscribe an existing callback | ||
*/ | ||
unsubscribe: (handler: Listener<T>) => void; | ||
get size(): number; | ||
/** | ||
@@ -47,0 +59,0 @@ * Notify all subscribers with arg data |
@@ -32,15 +32,30 @@ /** | ||
*/ | ||
export class Signal extends Set { | ||
constructor() { | ||
super(...arguments); | ||
this.onceHandlers = new Set(); | ||
export class Signal { | ||
constructor(handlers) { | ||
this.handlers = new Map(); | ||
/** | ||
* Subscribe a notification callback | ||
* | ||
* @param handler - Will be executed with a data arg when a notification occurs | ||
*/ | ||
this.subscribe = (handler) => { | ||
this.add(handler); | ||
if (this.handlers.get(handler) !== true) { | ||
this.handlers.set(handler, false); | ||
} | ||
else { | ||
throw new Error(`handler already exists as "once" listener`); | ||
} | ||
}; | ||
/** | ||
* Subscribe to only the next notification | ||
* | ||
* @param handler - Will be executed with a data arg when a notification occurs | ||
*/ | ||
this.once = (handler) => { | ||
this.onceHandlers.add(handler); | ||
if (this.handlers.get(handler) !== false) { | ||
this.handlers.set(handler, true); | ||
} | ||
else { | ||
throw new Error(`handler already exists as persistent listener`); | ||
} | ||
}; | ||
@@ -51,4 +66,3 @@ /** | ||
this.unsubscribe = (handler) => { | ||
this.onceHandlers.delete(handler); | ||
this.delete(handler); | ||
this.handlers.delete(handler); | ||
}; | ||
@@ -59,16 +73,24 @@ /** | ||
this.notify = (data) => { | ||
for (const handler of this) { | ||
for (const [handler, isOnce] of this.handlers) { | ||
handler(data); | ||
if (isOnce) { | ||
this.handlers.delete(handler); | ||
} | ||
} | ||
for (const handler of this.onceHandlers) { | ||
handler(data); | ||
this.onceHandlers.delete(handler); | ||
} | ||
}; | ||
handlers === null || handlers === void 0 ? void 0 : handlers.forEach((handler) => this.subscribe(handler)); | ||
} | ||
/** | ||
* @returns true if a listener is subscribed | ||
*/ | ||
has(value) { | ||
return this.handlers.has(value); | ||
} | ||
get size() { | ||
return this.handlers.size; | ||
} | ||
clear() { | ||
super.clear(); | ||
this.onceHandlers.clear(); | ||
this.handlers.clear(); | ||
} | ||
} | ||
//# sourceMappingURL=signal.js.map |
{ | ||
"name": "@wixc3/patterns", | ||
"version": "15.3.1", | ||
"version": "16.0.0", | ||
"description": "A utility for saving objects to be disposed", | ||
@@ -21,5 +21,5 @@ "main": "dist/cjs/index.js", | ||
"dependencies": { | ||
"@wixc3/common": "^15.1.1", | ||
"@wixc3/common": "^16.0.0", | ||
"promise-assist": "^2.0.1" | ||
} | ||
} |
@@ -29,3 +29,2 @@ import { Signal } from './signal'; | ||
protected events = new Map<EventId, Signal<any>>(); | ||
protected emitOnce = new Map<EventId, Signal<any>>(); | ||
@@ -40,2 +39,3 @@ /** | ||
* Subscribe a handler for event | ||
* | ||
* @returns unsubscribe fn | ||
@@ -45,3 +45,3 @@ */ | ||
const bucket = this.events.get(event); | ||
bucket ? bucket.add(handler) : this.events.set(event, new Signal([handler])); | ||
bucket ? bucket.subscribe(handler) : this.events.set(event, new Signal([handler])); | ||
return () => this.unsubscribe(event, handler); | ||
@@ -56,9 +56,14 @@ }; | ||
/** | ||
* Adds a handler that will be called at most once | ||
* Adds a handler that will be called at most once. | ||
* | ||
* @returns unsubscribe fn | ||
*/ | ||
once = <Event extends EventId>(event: Event, handler: (data: Events[Event]) => void) => { | ||
this.off(event, handler); | ||
const bucket = this.emitOnce.get(event); | ||
bucket ? bucket.add(handler) : this.emitOnce.set(event, new Signal([handler])); | ||
let bucket = this.events.get(event); | ||
if (!bucket) { | ||
bucket = new Signal(); | ||
this.events.set(event, bucket); | ||
} | ||
bucket.once(handler); | ||
return () => this.unsubscribe(event, handler); | ||
@@ -71,8 +76,7 @@ }; | ||
unsubscribe = <Event extends EventId>(event: Event, handler: (data: Events[Event]) => void) => { | ||
let bucket = this.events.get(event); | ||
bucket?.delete(handler); | ||
bucket?.size === 0 && this.events.delete(event); | ||
bucket = this.emitOnce.get(event); | ||
bucket?.delete(handler); | ||
bucket?.size === 0 && this.events.delete(event); | ||
const bucket = this.events.get(event); | ||
bucket?.unsubscribe(handler); | ||
if (bucket?.size === 0) { | ||
this.events.delete(event); | ||
} | ||
}; | ||
@@ -91,3 +95,2 @@ | ||
this.events.delete(event); | ||
this.emitOnce.delete(event); | ||
}; | ||
@@ -99,4 +102,4 @@ | ||
clear = () => { | ||
this.events.forEach((bucket) => bucket.clear()); | ||
this.events = new Map<EventId, Signal<any>>(); | ||
this.emitOnce = new Map<EventId, Signal<any>>(); | ||
}; | ||
@@ -111,4 +114,2 @@ | ||
this.events.get(event)?.notify(data); | ||
this.emitOnce.get(event)?.notify(data); | ||
this.emitOnce.delete(event); | ||
}; | ||
@@ -115,0 +116,0 @@ |
export type Listener<T> = (data: T) => void; | ||
type IS_ONCE = boolean; | ||
@@ -34,22 +35,52 @@ /** | ||
*/ | ||
export class Signal<T> extends Set<Listener<T>> { | ||
private onceHandlers = new Set<Listener<T>>(); | ||
export class Signal<T> { | ||
private handlers = new Map<Listener<T>, IS_ONCE>(); | ||
constructor(handlers?: Listener<T>[]) { | ||
handlers?.forEach((handler) => this.subscribe(handler)); | ||
} | ||
/** | ||
* Subscribe a notification callback | ||
* | ||
* @param handler - Will be executed with a data arg when a notification occurs | ||
*/ | ||
subscribe = (handler: Listener<T>) => { | ||
this.add(handler); | ||
if (this.handlers.get(handler) !== true) { | ||
this.handlers.set(handler, false); | ||
} else { | ||
throw new Error(`handler already exists as "once" listener`); | ||
} | ||
}; | ||
/** | ||
* Subscribe to only the next notification | ||
* | ||
* @param handler - Will be executed with a data arg when a notification occurs | ||
*/ | ||
once = (handler: Listener<T>) => { | ||
this.onceHandlers.add(handler); | ||
if (this.handlers.get(handler) !== false) { | ||
this.handlers.set(handler, true); | ||
} else { | ||
throw new Error(`handler already exists as persistent listener`); | ||
} | ||
}; | ||
/** | ||
* @returns true if a listener is subscribed | ||
*/ | ||
has(value: Listener<T>): boolean { | ||
return this.handlers.has(value); | ||
} | ||
/** | ||
* Unsubscribe an existing callback | ||
*/ | ||
unsubscribe = (handler: Listener<T>) => { | ||
this.onceHandlers.delete(handler); | ||
this.delete(handler); | ||
this.handlers.delete(handler); | ||
}; | ||
get size(): number { | ||
return this.handlers.size; | ||
} | ||
/** | ||
@@ -59,15 +90,13 @@ * Notify all subscribers with arg data | ||
notify = (data: T) => { | ||
for (const handler of this) { | ||
for (const [handler, isOnce] of this.handlers) { | ||
handler(data); | ||
if (isOnce) { | ||
this.handlers.delete(handler); | ||
} | ||
} | ||
for (const handler of this.onceHandlers) { | ||
handler(data); | ||
this.onceHandlers.delete(handler); | ||
} | ||
}; | ||
override clear(): void { | ||
super.clear(); | ||
this.onceHandlers.clear(); | ||
clear(): void { | ||
this.handlers.clear(); | ||
} | ||
} |
@@ -39,2 +39,15 @@ import chai, { expect } from 'chai'; | ||
}); | ||
it('notifies handlers in the order they were subscribed', () => { | ||
const listener1 = stub(); | ||
const listener2 = stub(); | ||
const listener3 = stub(); | ||
signal.subscribe(listener1); | ||
signal.once(listener2); | ||
signal.subscribe(listener3); | ||
signal.subscribe(listener1); // should have no effect | ||
signal.notify({ a: 'value', b: 5 }); | ||
expect(listener1.calledBefore(listener2), 'listener1 called before listener2').to.eql(true); | ||
expect(listener2.calledBefore(listener3), 'listener2 called before listener3').to.eql(true); | ||
}); | ||
describe('once', () => { | ||
@@ -64,2 +77,10 @@ it('calls "once" listeners only one time', () => { | ||
}); | ||
it('throws when a handler changes from "once" to persistent', () => { | ||
signal.once(listener); | ||
expect(() => signal.subscribe(listener)).to.throw(`handler already exists as "once" listener`); | ||
}); | ||
it('throws when a handler changes from persistent to "once"', () => { | ||
signal.subscribe(listener); | ||
expect(() => signal.once(listener)).to.throw(`handler already exists as persistent listener`); | ||
}); | ||
}); | ||
@@ -66,0 +87,0 @@ it(`doesn't call listeners after "unsubscribe"`, () => { |
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
265008
5180
+ Added@wixc3/common@16.3.0(transitive)
- Removed@file-services/path@8.3.3(transitive)
- Removed@file-services/types@8.3.3(transitive)
- Removed@wixc3/common@15.1.1(transitive)
Updated@wixc3/common@^16.0.0