You're Invited: Meet the Socket team at BSidesSF and RSAC - April 27 - May 1.RSVP
Socket
Sign inDemoInstall
Socket

@visisoft/staticland

Package Overview
Dependencies
Maintainers
1
Versions
72
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@visisoft/staticland - npm Package Compare versions

Comparing version

to
2.0.0

src/cancelable/internal/emitter.js

695

dist/cjs/cancelable.js
/* @license Apache-2.0
@visisoft/staticland v.2.0.0-alpha.3 visisoft.de
(Build date: 10/24/2023 - 9:47:10 PM)
@visisoft/staticland v.2.0.0 visisoft.de
(Build date: 4/16/2024 - 6:25:26 PM)
*/

@@ -121,539 +121,81 @@ 'use strict';

const anyMap = new WeakMap();
const eventsMap = new WeakMap();
const producersMap = new WeakMap();
/**
* @template T
* @typedef {{ resolve(t:T): void, reject(e: Error): void }} Sink
*/
const anyProducer = Symbol('anyProducer');
const resolvedPromise = Promise.resolve();
/**
* @template T
* @typedef {{isSuccess: boolean, outcome: T|Error}} EmitterEvent
*/
// Define symbols for "meta" events.
const listenerAdded = Symbol('listenerAdded');
const listenerRemoved = Symbol('listenerRemoved');
/**
* @template T
* @typedef {{ emitOnce: (event: EmitterEvent<T>) => void, removeAllSinks: () => void, addSink: (sink: Sink<T>) => void, removeSink: (s: Sink<T>) => void }} Emitter
*/
let canEmitMetaEvents = false;
let isGlobalDebugEnabled = false;
const
/**
* @template T
* @param onLastSinkRemoved
* @param onFirstSinkAdded
* @return Emitter<T>
*/
emitterForSingleEmission = ({
onLastSinkRemoved,
onFirstSinkAdded
}) => {
const
/** @type {Sink[]} */
sinks = [],
function assertEventName(eventName) {
if (typeof eventName !== 'string' && typeof eventName !== 'symbol' && typeof eventName !== 'number') {
throw new TypeError('`eventName` must be a string, symbol, or number');
}
}
function assertListener(listener) {
if (typeof listener !== 'function') {
throw new TypeError('listener must be a function');
}
}
function getListeners(instance, eventName) {
const events = eventsMap.get(instance);
if (!events.has(eventName)) {
return;
}
return events.get(eventName);
}
function getEventProducers(instance, eventName) {
const key = typeof eventName === 'string' || typeof eventName === 'symbol' || typeof eventName === 'number' ? eventName : anyProducer;
const producers = producersMap.get(instance);
if (!producers.has(key)) {
return;
}
return producers.get(key);
}
function enqueueProducers(instance, eventName, eventData) {
const producers = producersMap.get(instance);
if (producers.has(eventName)) {
for (const producer of producers.get(eventName)) {
producer.enqueue(eventData);
}
}
if (producers.has(anyProducer)) {
const item = Promise.all([eventName, eventData]);
for (const producer of producers.get(anyProducer)) {
producer.enqueue(item);
}
}
}
function iterator(instance, eventNames) {
eventNames = Array.isArray(eventNames) ? eventNames : [eventNames];
let isFinished = false;
let flush = () => {};
let queue = [];
const producer = {
enqueue(item) {
queue.push(item);
flush();
},
finish() {
isFinished = true;
flush();
},
};
for (const eventName of eventNames) {
let set = getEventProducers(instance, eventName);
if (!set) {
set = new Set();
const producers = producersMap.get(instance);
producers.set(eventName, set);
}
set.add(producer);
}
return {
async next() {
if (!queue) {
return {done: true};
}
if (queue.length === 0) {
if (isFinished) {
queue = undefined;
return this.next();
/**
* @template T
* @param {Sink<T>} sink
*/
addSink = sink => {
sinks.push(sink);
if (sinks.length === 1) {
onFirstSinkAdded();
}
},
await new Promise(resolve => {
flush = resolve;
});
return this.next();
}
return {
done: false,
value: await queue.shift(),
};
},
async return(value) {
queue = undefined;
for (const eventName of eventNames) {
const set = getEventProducers(instance, eventName);
if (set) {
set.delete(producer);
if (set.size === 0) {
const producers = producersMap.get(instance);
producers.delete(eventName);
removeSink = sink => {
const
index = sinks.indexOf(sink);
if (index !== -1) {
sinks.splice(index, 1);
if (sinks.length === 0) {
onLastSinkRemoved();
}
}
}
},
flush();
return arguments.length > 0
? {done: true, value: await value}
: {done: true};
},
[Symbol.asyncIterator]() {
return this;
},
};
}
function defaultMethodNamesOrAssert(methodNames) {
if (methodNames === undefined) {
return allEmitteryMethods;
}
if (!Array.isArray(methodNames)) {
throw new TypeError('`methodNames` must be an array of strings');
}
for (const methodName of methodNames) {
if (!allEmitteryMethods.includes(methodName)) {
if (typeof methodName !== 'string') {
throw new TypeError('`methodNames` element must be a string');
}
throw new Error(`${methodName} is not Emittery method`);
}
}
return methodNames;
}
const isMetaEvent = eventName => eventName === listenerAdded || eventName === listenerRemoved;
function emitMetaEvent(emitter, eventName, eventData) {
if (isMetaEvent(eventName)) {
try {
canEmitMetaEvents = true;
emitter.emit(eventName, eventData);
} finally {
canEmitMetaEvents = false;
}
}
}
class Emittery {
static mixin(emitteryPropertyName, methodNames) {
methodNames = defaultMethodNamesOrAssert(methodNames);
return target => {
if (typeof target !== 'function') {
throw new TypeError('`target` must be function');
}
for (const methodName of methodNames) {
if (target.prototype[methodName] !== undefined) {
throw new Error(`The property \`${methodName}\` already exists on \`target\``);
removeAllSinks = () => {
if (sinks.length > 0) {
sinks.splice(0);
onLastSinkRemoved();
}
}
},
function getEmitteryProperty() {
Object.defineProperty(this, emitteryPropertyName, {
enumerable: false,
value: new Emittery(),
/**
* @template T
* @param {EmitterEvent<T>} event
*/
emitOnce = event => {
const {isSuccess, outcome} = event;
sinks.forEach(({resolve, reject}) => {
(isSuccess ? resolve : reject)(outcome);
});
return this[emitteryPropertyName];
}
Object.defineProperty(target.prototype, emitteryPropertyName, {
enumerable: false,
get: getEmitteryProperty,
});
const emitteryMethodCaller = methodName => function (...args) {
return this[emitteryPropertyName][methodName](...args);
removeAllSinks();
};
for (const methodName of methodNames) {
Object.defineProperty(target.prototype, methodName, {
enumerable: false,
value: emitteryMethodCaller(methodName),
});
}
return target;
return {
addSink,
removeSink,
removeAllSinks,
emitOnce
};
}
};
static get isDebugEnabled() {
// In a browser environment, `globalThis.process` can potentially reference a DOM Element with a `#process` ID,
// so instead of just type checking `globalThis.process`, we need to make sure that `globalThis.process.env` exists.
// eslint-disable-next-line n/prefer-global/process
if (typeof globalThis.process?.env !== 'object') {
return isGlobalDebugEnabled;
}
// eslint-disable-next-line n/prefer-global/process
const {env} = globalThis.process ?? {env: {}};
return env.DEBUG === 'emittery' || env.DEBUG === '*' || isGlobalDebugEnabled;
}
static set isDebugEnabled(newValue) {
isGlobalDebugEnabled = newValue;
}
constructor(options = {}) {
anyMap.set(this, new Set());
eventsMap.set(this, new Map());
producersMap.set(this, new Map());
producersMap.get(this).set(anyProducer, new Set());
this.debug = options.debug ?? {};
if (this.debug.enabled === undefined) {
this.debug.enabled = false;
}
if (!this.debug.logger) {
this.debug.logger = (type, debugName, eventName, eventData) => {
try {
// TODO: Use https://github.com/sindresorhus/safe-stringify when the package is more mature. Just copy-paste the code.
eventData = JSON.stringify(eventData);
} catch {
eventData = `Object with the following keys failed to stringify: ${Object.keys(eventData).join(',')}`;
}
if (typeof eventName === 'symbol' || typeof eventName === 'number') {
eventName = eventName.toString();
}
const currentTime = new Date();
const logTime = `${currentTime.getHours()}:${currentTime.getMinutes()}:${currentTime.getSeconds()}.${currentTime.getMilliseconds()}`;
console.log(`[${logTime}][emittery:${type}][${debugName}] Event Name: ${eventName}\n\tdata: ${eventData}`);
};
}
}
logIfDebugEnabled(type, eventName, eventData) {
if (Emittery.isDebugEnabled || this.debug.enabled) {
this.debug.logger(type, this.debug.name, eventName, eventData);
}
}
on(eventNames, listener) {
assertListener(listener);
eventNames = Array.isArray(eventNames) ? eventNames : [eventNames];
for (const eventName of eventNames) {
assertEventName(eventName);
let set = getListeners(this, eventName);
if (!set) {
set = new Set();
const events = eventsMap.get(this);
events.set(eventName, set);
}
set.add(listener);
this.logIfDebugEnabled('subscribe', eventName, undefined);
if (!isMetaEvent(eventName)) {
emitMetaEvent(this, listenerAdded, {eventName, listener});
}
}
return this.off.bind(this, eventNames, listener);
}
off(eventNames, listener) {
assertListener(listener);
eventNames = Array.isArray(eventNames) ? eventNames : [eventNames];
for (const eventName of eventNames) {
assertEventName(eventName);
const set = getListeners(this, eventName);
if (set) {
set.delete(listener);
if (set.size === 0) {
const events = eventsMap.get(this);
events.delete(eventName);
}
}
this.logIfDebugEnabled('unsubscribe', eventName, undefined);
if (!isMetaEvent(eventName)) {
emitMetaEvent(this, listenerRemoved, {eventName, listener});
}
}
}
once(eventNames) {
let off_;
const promise = new Promise(resolve => {
off_ = this.on(eventNames, data => {
off_();
resolve(data);
});
});
promise.off = off_;
return promise;
}
events(eventNames) {
eventNames = Array.isArray(eventNames) ? eventNames : [eventNames];
for (const eventName of eventNames) {
assertEventName(eventName);
}
return iterator(this, eventNames);
}
async emit(eventName, eventData) {
assertEventName(eventName);
if (isMetaEvent(eventName) && !canEmitMetaEvents) {
throw new TypeError('`eventName` cannot be meta event `listenerAdded` or `listenerRemoved`');
}
this.logIfDebugEnabled('emit', eventName, eventData);
enqueueProducers(this, eventName, eventData);
const listeners = getListeners(this, eventName) ?? new Set();
const anyListeners = anyMap.get(this);
const staticListeners = [...listeners];
const staticAnyListeners = isMetaEvent(eventName) ? [] : [...anyListeners];
await resolvedPromise;
await Promise.all([
...staticListeners.map(async listener => {
if (listeners.has(listener)) {
return listener(eventData);
}
}),
...staticAnyListeners.map(async listener => {
if (anyListeners.has(listener)) {
return listener(eventName, eventData);
}
}),
]);
}
async emitSerial(eventName, eventData) {
assertEventName(eventName);
if (isMetaEvent(eventName) && !canEmitMetaEvents) {
throw new TypeError('`eventName` cannot be meta event `listenerAdded` or `listenerRemoved`');
}
this.logIfDebugEnabled('emitSerial', eventName, eventData);
const listeners = getListeners(this, eventName) ?? new Set();
const anyListeners = anyMap.get(this);
const staticListeners = [...listeners];
const staticAnyListeners = [...anyListeners];
await resolvedPromise;
/* eslint-disable no-await-in-loop */
for (const listener of staticListeners) {
if (listeners.has(listener)) {
await listener(eventData);
}
}
for (const listener of staticAnyListeners) {
if (anyListeners.has(listener)) {
await listener(eventName, eventData);
}
}
/* eslint-enable no-await-in-loop */
}
onAny(listener) {
assertListener(listener);
this.logIfDebugEnabled('subscribeAny', undefined, undefined);
anyMap.get(this).add(listener);
emitMetaEvent(this, listenerAdded, {listener});
return this.offAny.bind(this, listener);
}
anyEvent() {
return iterator(this);
}
offAny(listener) {
assertListener(listener);
this.logIfDebugEnabled('unsubscribeAny', undefined, undefined);
emitMetaEvent(this, listenerRemoved, {listener});
anyMap.get(this).delete(listener);
}
clearListeners(eventNames) {
eventNames = Array.isArray(eventNames) ? eventNames : [eventNames];
for (const eventName of eventNames) {
this.logIfDebugEnabled('clear', eventName, undefined);
if (typeof eventName === 'string' || typeof eventName === 'symbol' || typeof eventName === 'number') {
const set = getListeners(this, eventName);
if (set) {
set.clear();
}
const producers = getEventProducers(this, eventName);
if (producers) {
for (const producer of producers) {
producer.finish();
}
producers.clear();
}
} else {
anyMap.get(this).clear();
for (const [eventName, listeners] of eventsMap.get(this).entries()) {
listeners.clear();
eventsMap.get(this).delete(eventName);
}
for (const [eventName, producers] of producersMap.get(this).entries()) {
for (const producer of producers) {
producer.finish();
}
producers.clear();
producersMap.get(this).delete(eventName);
}
}
}
}
listenerCount(eventNames) {
eventNames = Array.isArray(eventNames) ? eventNames : [eventNames];
let count = 0;
for (const eventName of eventNames) {
if (typeof eventName === 'string') {
count += anyMap.get(this).size
+ (getListeners(this, eventName)?.size ?? 0)
+ (getEventProducers(this, eventName)?.size ?? 0)
+ (getEventProducers(this)?.size ?? 0);
continue;
}
if (typeof eventName !== 'undefined') {
assertEventName(eventName);
}
count += anyMap.get(this).size;
for (const value of eventsMap.get(this).values()) {
count += value.size;
}
for (const value of producersMap.get(this).values()) {
count += value.size;
}
}
return count;
}
bindMethods(target, methodNames) {
if (typeof target !== 'object' || target === null) {
throw new TypeError('`target` must be an object');
}
methodNames = defaultMethodNamesOrAssert(methodNames);
for (const methodName of methodNames) {
if (target[methodName] !== undefined) {
throw new Error(`The property \`${methodName}\` already exists on \`target\``);
}
Object.defineProperty(target, methodName, {
enumerable: false,
value: this[methodName].bind(this),
});
}
}
}
const allEmitteryMethods = Object.getOwnPropertyNames(Emittery.prototype).filter(v => v !== 'constructor');
Object.defineProperty(Emittery, 'listenerAdded', {
value: listenerAdded,
writable: false,
enumerable: true,
configurable: false,
});
Object.defineProperty(Emittery, 'listenerRemoved', {
value: listenerRemoved,
writable: false,
enumerable: true,
configurable: false,
});
/**

@@ -679,3 +221,3 @@ * StaticLand: deferred.js

const
emitter = new Emittery(),
emitter = emitterForSingleEmission({onLastSinkRemoved: noop, onFirstSinkAdded: noop}),

@@ -686,3 +228,3 @@ resolve = value => {

// in case anybody already listens, tell them…
emitter.emit("settle", {outcome: value, isSuccess: true});
emitter.emitOnce({outcome: value, isSuccess: true});
},

@@ -694,3 +236,3 @@

// in case anybody already listens, tell them…
emitter.emit("settle", {outcome: error, isSuccess: false});
emitter.emitOnce({outcome: error, isSuccess: false});
},

@@ -701,28 +243,34 @@

// in case anybody listens, forget them all…
emitter.clearListeners(["settle"]);
emitter.removeAllSinks();
},
cancelable = (propagateResolve, propagateReject) => {
const
resolveAsap = ramda.thunkify(propagateResolve)(outcome),
rejectAsap = ramda.thunkify(propagateReject)(outcome);
switch (state) {
case "pending":
const
unSubscribe =
emitter.on("settle", ({ isSuccess, outcome: theOutcome }) => {
(isSuccess ? propagateResolve : propagateReject)(theOutcome);
unSubscribe();
});
sink = {resolve: propagateResolve, reject: propagateReject},
unsubscribe = () => {
emitter.removeSink(sink);
};
return unSubscribe;
emitter.addSink(sink);
return unsubscribe;
case "cancelled":
return noop;
break;
case "rejected":
setTimeout(propagateReject, 0, outcome);
return noop;
queueMicrotask(rejectAsap);
break;
case "resolved":
setTimeout(propagateResolve, 0, outcome);
return noop;
queueMicrotask(resolveAsap);
break;
default:
setTimeout(propagateReject, 0, new Error(`Unexpected cancelable deferred state: "${state}"`));
return noop;
queueMicrotask(() => {
propagateReject(new Error(`Unexpected cancelable deferred state: "${state}"`));
});
}
return noop;
};

@@ -901,3 +449,2 @@

const
emitter = new Emittery(),
doNothing = () => undefined;

@@ -910,44 +457,34 @@

isFinallyRejected = false,
previousListenerCount = 0,
finalOutcome;
emitter.on(Emittery.listenerRemoved, (function() {
return function onListenerRemoved() {
//console.log(`listenerRemoved: count=${emitter.listenerCount("settle")}, previousCount=${previousListenerCount}`);
if ((emitter.listenerCount("settle") === 0) && (previousListenerCount > 0)
&& !isFinallyResolved && !isFinallyRejected) {
const
onLastSinkRemoved = () => {
if (!isFinallyResolved && !isFinallyRejected) {
// abort the running computation if the number of consumers drops to zero
cancelRunningComputation();
}
},
previousListenerCount = emitter.listenerCount("settle");
};
}()));
// run once for all when the number of consumers exceeds zero
onFirstSinkAdded = () => {
cancelRunningComputation = cc(
value => {
finalOutcome = value;
isFinallyResolved = true;
emitter.emitOnce({outcome: value, isSuccess: true});
},
error => {
finalOutcome = error;
isFinallyRejected = true;
emitter.emitOnce({outcome: error, isSuccess: false});
}
);
},
emitter.on(Emittery.listenerAdded, (function() {
return function onListenerAdded() {
//console.log(`listenerAdded: count=${emitter.listenerCount("settle")}, previousCount=${previousListenerCount}`);
if ((emitter.listenerCount("settle") > 0) && (previousListenerCount === 0)) {
// run once for all when the number of consumers exceeds zero
cancelRunningComputation = cc(
value => {
finalOutcome = value;
isFinallyResolved = true;
emitter.emit("settle", {outcome: value, isSuccess: true});
},
error => {
finalOutcome = error;
isFinallyRejected = true;
emitter.emit("settle", {outcome: error, isSuccess: false});
}
);
}
emitter = emitterForSingleEmission({onFirstSinkAdded, onLastSinkRemoved});
previousListenerCount = emitter.listenerCount("settle");
};
}()));
return (resolve, reject) => {
if (isFinallyResolved) {
setTimeout(resolve, 0, finalOutcome);
const resolveAsap = ramda.thunkify(resolve)(finalOutcome);
queueMicrotask(resolveAsap);
return doNothing;

@@ -957,3 +494,4 @@ }

if (isFinallyRejected) {
setTimeout(reject, 0, finalOutcome);
const rejectAsap = ramda.thunkify(reject)(finalOutcome);
queueMicrotask(rejectAsap);
return doNothing;

@@ -963,8 +501,7 @@ }

const
unConsume =
emitter.on("settle", ({isSuccess, outcome}) => {
(isSuccess ? resolve : reject)(outcome);
unConsume();
});
sink = { resolve, reject },
unConsume = () => { emitter.removeSink(sink); };
emitter.addSink(sink);
return unConsume;

@@ -971,0 +508,0 @@ };

/* @license Apache-2.0
@visisoft/staticland v.2.0.0-alpha.3 visisoft.de
(Build date: 10/24/2023 - 9:47:10 PM)
@visisoft/staticland v.2.0.0 visisoft.de
(Build date: 4/16/2024 - 6:25:26 PM)
*/

@@ -5,0 +5,0 @@ 'use strict';

/* @license Apache-2.0
@visisoft/staticland v.2.0.0-alpha.3 visisoft.de
(Build date: 10/24/2023 - 9:47:10 PM)
@visisoft/staticland v.2.0.0 visisoft.de
(Build date: 4/16/2024 - 6:25:26 PM)
*/

@@ -5,0 +5,0 @@ 'use strict';

/* @license Apache-2.0
@visisoft/staticland v.2.0.0-alpha.3 visisoft.de
(Build date: 10/24/2023 - 9:47:10 PM)
@visisoft/staticland v.2.0.0 visisoft.de
(Build date: 4/16/2024 - 6:25:26 PM)
*/

@@ -5,0 +5,0 @@ 'use strict';

/* @license Apache-2.0
@visisoft/staticland v.2.0.0-alpha.3 visisoft.de
(Build date: 10/24/2023 - 9:47:10 PM)
@visisoft/staticland v.2.0.0 visisoft.de
(Build date: 4/16/2024 - 6:25:26 PM)
*/

@@ -5,0 +5,0 @@ 'use strict';

/* @license Apache-2.0
@visisoft/staticland v.2.0.0-alpha.3 visisoft.de
(Build date: 10/24/2023 - 9:47:10 PM)
@visisoft/staticland v.2.0.0 visisoft.de
(Build date: 4/16/2024 - 6:25:26 PM)
*/

@@ -5,0 +5,0 @@ 'use strict';

/* @license Apache-2.0
@visisoft/staticland v.2.0.0-alpha.3 visisoft.de
(Build date: 10/24/2023 - 9:47:10 PM)
@visisoft/staticland v.2.0.0 visisoft.de
(Build date: 4/16/2024 - 6:25:26 PM)
*/

@@ -5,0 +5,0 @@ 'use strict';

/* @license Apache-2.0
@visisoft/staticland v.2.0.0-alpha.3 visisoft.de
(Build date: 10/24/2023 - 9:47:10 PM)
@visisoft/staticland v.2.0.0 visisoft.de
(Build date: 4/16/2024 - 6:25:26 PM)
*/

@@ -1264,539 +1264,81 @@ 'use strict';

const anyMap = new WeakMap();
const eventsMap = new WeakMap();
const producersMap = new WeakMap();
/**
* @template T
* @typedef {{ resolve(t:T): void, reject(e: Error): void }} Sink
*/
const anyProducer = Symbol('anyProducer');
const resolvedPromise = Promise.resolve();
/**
* @template T
* @typedef {{isSuccess: boolean, outcome: T|Error}} EmitterEvent
*/
// Define symbols for "meta" events.
const listenerAdded = Symbol('listenerAdded');
const listenerRemoved = Symbol('listenerRemoved');
/**
* @template T
* @typedef {{ emitOnce: (event: EmitterEvent<T>) => void, removeAllSinks: () => void, addSink: (sink: Sink<T>) => void, removeSink: (s: Sink<T>) => void }} Emitter
*/
let canEmitMetaEvents = false;
let isGlobalDebugEnabled = false;
const
/**
* @template T
* @param onLastSinkRemoved
* @param onFirstSinkAdded
* @return Emitter<T>
*/
emitterForSingleEmission = ({
onLastSinkRemoved,
onFirstSinkAdded
}) => {
const
/** @type {Sink[]} */
sinks = [],
function assertEventName(eventName) {
if (typeof eventName !== 'string' && typeof eventName !== 'symbol' && typeof eventName !== 'number') {
throw new TypeError('`eventName` must be a string, symbol, or number');
}
}
function assertListener(listener) {
if (typeof listener !== 'function') {
throw new TypeError('listener must be a function');
}
}
function getListeners(instance, eventName) {
const events = eventsMap.get(instance);
if (!events.has(eventName)) {
return;
}
return events.get(eventName);
}
function getEventProducers(instance, eventName) {
const key = typeof eventName === 'string' || typeof eventName === 'symbol' || typeof eventName === 'number' ? eventName : anyProducer;
const producers = producersMap.get(instance);
if (!producers.has(key)) {
return;
}
return producers.get(key);
}
function enqueueProducers(instance, eventName, eventData) {
const producers = producersMap.get(instance);
if (producers.has(eventName)) {
for (const producer of producers.get(eventName)) {
producer.enqueue(eventData);
}
}
if (producers.has(anyProducer)) {
const item = Promise.all([eventName, eventData]);
for (const producer of producers.get(anyProducer)) {
producer.enqueue(item);
}
}
}
function iterator(instance, eventNames) {
eventNames = Array.isArray(eventNames) ? eventNames : [eventNames];
let isFinished = false;
let flush = () => {};
let queue = [];
const producer = {
enqueue(item) {
queue.push(item);
flush();
},
finish() {
isFinished = true;
flush();
},
};
for (const eventName of eventNames) {
let set = getEventProducers(instance, eventName);
if (!set) {
set = new Set();
const producers = producersMap.get(instance);
producers.set(eventName, set);
}
set.add(producer);
}
return {
async next() {
if (!queue) {
return {done: true};
}
if (queue.length === 0) {
if (isFinished) {
queue = undefined;
return this.next();
/**
* @template T
* @param {Sink<T>} sink
*/
addSink = sink => {
sinks.push(sink);
if (sinks.length === 1) {
onFirstSinkAdded();
}
},
await new Promise(resolve => {
flush = resolve;
});
return this.next();
}
return {
done: false,
value: await queue.shift(),
};
},
async return(value) {
queue = undefined;
for (const eventName of eventNames) {
const set = getEventProducers(instance, eventName);
if (set) {
set.delete(producer);
if (set.size === 0) {
const producers = producersMap.get(instance);
producers.delete(eventName);
removeSink = sink => {
const
index = sinks.indexOf(sink);
if (index !== -1) {
sinks.splice(index, 1);
if (sinks.length === 0) {
onLastSinkRemoved();
}
}
}
},
flush();
return arguments.length > 0
? {done: true, value: await value}
: {done: true};
},
[Symbol.asyncIterator]() {
return this;
},
};
}
function defaultMethodNamesOrAssert(methodNames) {
if (methodNames === undefined) {
return allEmitteryMethods;
}
if (!Array.isArray(methodNames)) {
throw new TypeError('`methodNames` must be an array of strings');
}
for (const methodName of methodNames) {
if (!allEmitteryMethods.includes(methodName)) {
if (typeof methodName !== 'string') {
throw new TypeError('`methodNames` element must be a string');
}
throw new Error(`${methodName} is not Emittery method`);
}
}
return methodNames;
}
const isMetaEvent = eventName => eventName === listenerAdded || eventName === listenerRemoved;
function emitMetaEvent(emitter, eventName, eventData) {
if (isMetaEvent(eventName)) {
try {
canEmitMetaEvents = true;
emitter.emit(eventName, eventData);
} finally {
canEmitMetaEvents = false;
}
}
}
class Emittery {
static mixin(emitteryPropertyName, methodNames) {
methodNames = defaultMethodNamesOrAssert(methodNames);
return target => {
if (typeof target !== 'function') {
throw new TypeError('`target` must be function');
}
for (const methodName of methodNames) {
if (target.prototype[methodName] !== undefined) {
throw new Error(`The property \`${methodName}\` already exists on \`target\``);
removeAllSinks = () => {
if (sinks.length > 0) {
sinks.splice(0);
onLastSinkRemoved();
}
}
},
function getEmitteryProperty() {
Object.defineProperty(this, emitteryPropertyName, {
enumerable: false,
value: new Emittery(),
/**
* @template T
* @param {EmitterEvent<T>} event
*/
emitOnce = event => {
const {isSuccess, outcome} = event;
sinks.forEach(({resolve, reject}) => {
(isSuccess ? resolve : reject)(outcome);
});
return this[emitteryPropertyName];
}
Object.defineProperty(target.prototype, emitteryPropertyName, {
enumerable: false,
get: getEmitteryProperty,
});
const emitteryMethodCaller = methodName => function (...args) {
return this[emitteryPropertyName][methodName](...args);
removeAllSinks();
};
for (const methodName of methodNames) {
Object.defineProperty(target.prototype, methodName, {
enumerable: false,
value: emitteryMethodCaller(methodName),
});
}
return target;
return {
addSink,
removeSink,
removeAllSinks,
emitOnce
};
}
};
static get isDebugEnabled() {
// In a browser environment, `globalThis.process` can potentially reference a DOM Element with a `#process` ID,
// so instead of just type checking `globalThis.process`, we need to make sure that `globalThis.process.env` exists.
// eslint-disable-next-line n/prefer-global/process
if (typeof globalThis.process?.env !== 'object') {
return isGlobalDebugEnabled;
}
// eslint-disable-next-line n/prefer-global/process
const {env} = globalThis.process ?? {env: {}};
return env.DEBUG === 'emittery' || env.DEBUG === '*' || isGlobalDebugEnabled;
}
static set isDebugEnabled(newValue) {
isGlobalDebugEnabled = newValue;
}
constructor(options = {}) {
anyMap.set(this, new Set());
eventsMap.set(this, new Map());
producersMap.set(this, new Map());
producersMap.get(this).set(anyProducer, new Set());
this.debug = options.debug ?? {};
if (this.debug.enabled === undefined) {
this.debug.enabled = false;
}
if (!this.debug.logger) {
this.debug.logger = (type, debugName, eventName, eventData) => {
try {
// TODO: Use https://github.com/sindresorhus/safe-stringify when the package is more mature. Just copy-paste the code.
eventData = JSON.stringify(eventData);
} catch {
eventData = `Object with the following keys failed to stringify: ${Object.keys(eventData).join(',')}`;
}
if (typeof eventName === 'symbol' || typeof eventName === 'number') {
eventName = eventName.toString();
}
const currentTime = new Date();
const logTime = `${currentTime.getHours()}:${currentTime.getMinutes()}:${currentTime.getSeconds()}.${currentTime.getMilliseconds()}`;
console.log(`[${logTime}][emittery:${type}][${debugName}] Event Name: ${eventName}\n\tdata: ${eventData}`);
};
}
}
logIfDebugEnabled(type, eventName, eventData) {
if (Emittery.isDebugEnabled || this.debug.enabled) {
this.debug.logger(type, this.debug.name, eventName, eventData);
}
}
on(eventNames, listener) {
assertListener(listener);
eventNames = Array.isArray(eventNames) ? eventNames : [eventNames];
for (const eventName of eventNames) {
assertEventName(eventName);
let set = getListeners(this, eventName);
if (!set) {
set = new Set();
const events = eventsMap.get(this);
events.set(eventName, set);
}
set.add(listener);
this.logIfDebugEnabled('subscribe', eventName, undefined);
if (!isMetaEvent(eventName)) {
emitMetaEvent(this, listenerAdded, {eventName, listener});
}
}
return this.off.bind(this, eventNames, listener);
}
off(eventNames, listener) {
assertListener(listener);
eventNames = Array.isArray(eventNames) ? eventNames : [eventNames];
for (const eventName of eventNames) {
assertEventName(eventName);
const set = getListeners(this, eventName);
if (set) {
set.delete(listener);
if (set.size === 0) {
const events = eventsMap.get(this);
events.delete(eventName);
}
}
this.logIfDebugEnabled('unsubscribe', eventName, undefined);
if (!isMetaEvent(eventName)) {
emitMetaEvent(this, listenerRemoved, {eventName, listener});
}
}
}
once(eventNames) {
let off_;
const promise = new Promise(resolve => {
off_ = this.on(eventNames, data => {
off_();
resolve(data);
});
});
promise.off = off_;
return promise;
}
events(eventNames) {
eventNames = Array.isArray(eventNames) ? eventNames : [eventNames];
for (const eventName of eventNames) {
assertEventName(eventName);
}
return iterator(this, eventNames);
}
async emit(eventName, eventData) {
assertEventName(eventName);
if (isMetaEvent(eventName) && !canEmitMetaEvents) {
throw new TypeError('`eventName` cannot be meta event `listenerAdded` or `listenerRemoved`');
}
this.logIfDebugEnabled('emit', eventName, eventData);
enqueueProducers(this, eventName, eventData);
const listeners = getListeners(this, eventName) ?? new Set();
const anyListeners = anyMap.get(this);
const staticListeners = [...listeners];
const staticAnyListeners = isMetaEvent(eventName) ? [] : [...anyListeners];
await resolvedPromise;
await Promise.all([
...staticListeners.map(async listener => {
if (listeners.has(listener)) {
return listener(eventData);
}
}),
...staticAnyListeners.map(async listener => {
if (anyListeners.has(listener)) {
return listener(eventName, eventData);
}
}),
]);
}
async emitSerial(eventName, eventData) {
assertEventName(eventName);
if (isMetaEvent(eventName) && !canEmitMetaEvents) {
throw new TypeError('`eventName` cannot be meta event `listenerAdded` or `listenerRemoved`');
}
this.logIfDebugEnabled('emitSerial', eventName, eventData);
const listeners = getListeners(this, eventName) ?? new Set();
const anyListeners = anyMap.get(this);
const staticListeners = [...listeners];
const staticAnyListeners = [...anyListeners];
await resolvedPromise;
/* eslint-disable no-await-in-loop */
for (const listener of staticListeners) {
if (listeners.has(listener)) {
await listener(eventData);
}
}
for (const listener of staticAnyListeners) {
if (anyListeners.has(listener)) {
await listener(eventName, eventData);
}
}
/* eslint-enable no-await-in-loop */
}
onAny(listener) {
assertListener(listener);
this.logIfDebugEnabled('subscribeAny', undefined, undefined);
anyMap.get(this).add(listener);
emitMetaEvent(this, listenerAdded, {listener});
return this.offAny.bind(this, listener);
}
anyEvent() {
return iterator(this);
}
offAny(listener) {
assertListener(listener);
this.logIfDebugEnabled('unsubscribeAny', undefined, undefined);
emitMetaEvent(this, listenerRemoved, {listener});
anyMap.get(this).delete(listener);
}
clearListeners(eventNames) {
eventNames = Array.isArray(eventNames) ? eventNames : [eventNames];
for (const eventName of eventNames) {
this.logIfDebugEnabled('clear', eventName, undefined);
if (typeof eventName === 'string' || typeof eventName === 'symbol' || typeof eventName === 'number') {
const set = getListeners(this, eventName);
if (set) {
set.clear();
}
const producers = getEventProducers(this, eventName);
if (producers) {
for (const producer of producers) {
producer.finish();
}
producers.clear();
}
} else {
anyMap.get(this).clear();
for (const [eventName, listeners] of eventsMap.get(this).entries()) {
listeners.clear();
eventsMap.get(this).delete(eventName);
}
for (const [eventName, producers] of producersMap.get(this).entries()) {
for (const producer of producers) {
producer.finish();
}
producers.clear();
producersMap.get(this).delete(eventName);
}
}
}
}
listenerCount(eventNames) {
eventNames = Array.isArray(eventNames) ? eventNames : [eventNames];
let count = 0;
for (const eventName of eventNames) {
if (typeof eventName === 'string') {
count += anyMap.get(this).size
+ (getListeners(this, eventName)?.size ?? 0)
+ (getEventProducers(this, eventName)?.size ?? 0)
+ (getEventProducers(this)?.size ?? 0);
continue;
}
if (typeof eventName !== 'undefined') {
assertEventName(eventName);
}
count += anyMap.get(this).size;
for (const value of eventsMap.get(this).values()) {
count += value.size;
}
for (const value of producersMap.get(this).values()) {
count += value.size;
}
}
return count;
}
bindMethods(target, methodNames) {
if (typeof target !== 'object' || target === null) {
throw new TypeError('`target` must be an object');
}
methodNames = defaultMethodNamesOrAssert(methodNames);
for (const methodName of methodNames) {
if (target[methodName] !== undefined) {
throw new Error(`The property \`${methodName}\` already exists on \`target\``);
}
Object.defineProperty(target, methodName, {
enumerable: false,
value: this[methodName].bind(this),
});
}
}
}
const allEmitteryMethods = Object.getOwnPropertyNames(Emittery.prototype).filter(v => v !== 'constructor');
Object.defineProperty(Emittery, 'listenerAdded', {
value: listenerAdded,
writable: false,
enumerable: true,
configurable: false,
});
Object.defineProperty(Emittery, 'listenerRemoved', {
value: listenerRemoved,
writable: false,
enumerable: true,
configurable: false,
});
/**

@@ -1822,3 +1364,3 @@ * StaticLand: deferred.js

const
emitter = new Emittery(),
emitter = emitterForSingleEmission({onLastSinkRemoved: noop$1, onFirstSinkAdded: noop$1}),

@@ -1829,3 +1371,3 @@ resolve = value => {

// in case anybody already listens, tell them…
emitter.emit("settle", {outcome: value, isSuccess: true});
emitter.emitOnce({outcome: value, isSuccess: true});
},

@@ -1837,3 +1379,3 @@

// in case anybody already listens, tell them…
emitter.emit("settle", {outcome: error, isSuccess: false});
emitter.emitOnce({outcome: error, isSuccess: false});
},

@@ -1844,28 +1386,34 @@

// in case anybody listens, forget them all…
emitter.clearListeners(["settle"]);
emitter.removeAllSinks();
},
cancelable = (propagateResolve, propagateReject) => {
const
resolveAsap = ramda.thunkify(propagateResolve)(outcome),
rejectAsap = ramda.thunkify(propagateReject)(outcome);
switch (state) {
case "pending":
const
unSubscribe =
emitter.on("settle", ({ isSuccess, outcome: theOutcome }) => {
(isSuccess ? propagateResolve : propagateReject)(theOutcome);
unSubscribe();
});
sink = {resolve: propagateResolve, reject: propagateReject},
unsubscribe = () => {
emitter.removeSink(sink);
};
return unSubscribe;
emitter.addSink(sink);
return unsubscribe;
case "cancelled":
return noop$1;
break;
case "rejected":
setTimeout(propagateReject, 0, outcome);
return noop$1;
queueMicrotask(rejectAsap);
break;
case "resolved":
setTimeout(propagateResolve, 0, outcome);
return noop$1;
queueMicrotask(resolveAsap);
break;
default:
setTimeout(propagateReject, 0, new Error(`Unexpected cancelable deferred state: "${state}"`));
return noop$1;
queueMicrotask(() => {
propagateReject(new Error(`Unexpected cancelable deferred state: "${state}"`));
});
}
return noop$1;
};

@@ -2044,3 +1592,2 @@

const
emitter = new Emittery(),
doNothing = () => undefined;

@@ -2053,44 +1600,34 @@

isFinallyRejected = false,
previousListenerCount = 0,
finalOutcome;
emitter.on(Emittery.listenerRemoved, (function() {
return function onListenerRemoved() {
//console.log(`listenerRemoved: count=${emitter.listenerCount("settle")}, previousCount=${previousListenerCount}`);
if ((emitter.listenerCount("settle") === 0) && (previousListenerCount > 0)
&& !isFinallyResolved && !isFinallyRejected) {
const
onLastSinkRemoved = () => {
if (!isFinallyResolved && !isFinallyRejected) {
// abort the running computation if the number of consumers drops to zero
cancelRunningComputation();
}
},
previousListenerCount = emitter.listenerCount("settle");
};
}()));
// run once for all when the number of consumers exceeds zero
onFirstSinkAdded = () => {
cancelRunningComputation = cc(
value => {
finalOutcome = value;
isFinallyResolved = true;
emitter.emitOnce({outcome: value, isSuccess: true});
},
error => {
finalOutcome = error;
isFinallyRejected = true;
emitter.emitOnce({outcome: error, isSuccess: false});
}
);
},
emitter.on(Emittery.listenerAdded, (function() {
return function onListenerAdded() {
//console.log(`listenerAdded: count=${emitter.listenerCount("settle")}, previousCount=${previousListenerCount}`);
if ((emitter.listenerCount("settle") > 0) && (previousListenerCount === 0)) {
// run once for all when the number of consumers exceeds zero
cancelRunningComputation = cc(
value => {
finalOutcome = value;
isFinallyResolved = true;
emitter.emit("settle", {outcome: value, isSuccess: true});
},
error => {
finalOutcome = error;
isFinallyRejected = true;
emitter.emit("settle", {outcome: error, isSuccess: false});
}
);
}
emitter = emitterForSingleEmission({onFirstSinkAdded, onLastSinkRemoved});
previousListenerCount = emitter.listenerCount("settle");
};
}()));
return (resolve, reject) => {
if (isFinallyResolved) {
setTimeout(resolve, 0, finalOutcome);
const resolveAsap = ramda.thunkify(resolve)(finalOutcome);
queueMicrotask(resolveAsap);
return doNothing;

@@ -2100,3 +1637,4 @@ }

if (isFinallyRejected) {
setTimeout(reject, 0, finalOutcome);
const rejectAsap = ramda.thunkify(reject)(finalOutcome);
queueMicrotask(rejectAsap);
return doNothing;

@@ -2106,8 +1644,7 @@ }

const
unConsume =
emitter.on("settle", ({isSuccess, outcome}) => {
(isSuccess ? resolve : reject)(outcome);
unConsume();
});
sink = { resolve, reject },
unConsume = () => { emitter.removeSink(sink); };
emitter.addSink(sink);
return unConsume;

@@ -2114,0 +1651,0 @@ };

/* @license Apache-2.0
@visisoft/staticland v.2.0.0-alpha.3 visisoft.de
(Build date: 10/24/2023 - 9:47:10 PM)
@visisoft/staticland v.2.0.0 visisoft.de
(Build date: 4/16/2024 - 6:25:26 PM)
*/

@@ -963,539 +963,81 @@ 'use strict';

const anyMap = new WeakMap();
const eventsMap = new WeakMap();
const producersMap = new WeakMap();
/**
* @template T
* @typedef {{ resolve(t:T): void, reject(e: Error): void }} Sink
*/
const anyProducer = Symbol('anyProducer');
const resolvedPromise = Promise.resolve();
/**
* @template T
* @typedef {{isSuccess: boolean, outcome: T|Error}} EmitterEvent
*/
// Define symbols for "meta" events.
const listenerAdded = Symbol('listenerAdded');
const listenerRemoved = Symbol('listenerRemoved');
/**
* @template T
* @typedef {{ emitOnce: (event: EmitterEvent<T>) => void, removeAllSinks: () => void, addSink: (sink: Sink<T>) => void, removeSink: (s: Sink<T>) => void }} Emitter
*/
let canEmitMetaEvents = false;
let isGlobalDebugEnabled = false;
const
/**
* @template T
* @param onLastSinkRemoved
* @param onFirstSinkAdded
* @return Emitter<T>
*/
emitterForSingleEmission = ({
onLastSinkRemoved,
onFirstSinkAdded
}) => {
const
/** @type {Sink[]} */
sinks = [],
function assertEventName(eventName) {
if (typeof eventName !== 'string' && typeof eventName !== 'symbol' && typeof eventName !== 'number') {
throw new TypeError('`eventName` must be a string, symbol, or number');
}
}
function assertListener(listener) {
if (typeof listener !== 'function') {
throw new TypeError('listener must be a function');
}
}
function getListeners(instance, eventName) {
const events = eventsMap.get(instance);
if (!events.has(eventName)) {
return;
}
return events.get(eventName);
}
function getEventProducers(instance, eventName) {
const key = typeof eventName === 'string' || typeof eventName === 'symbol' || typeof eventName === 'number' ? eventName : anyProducer;
const producers = producersMap.get(instance);
if (!producers.has(key)) {
return;
}
return producers.get(key);
}
function enqueueProducers(instance, eventName, eventData) {
const producers = producersMap.get(instance);
if (producers.has(eventName)) {
for (const producer of producers.get(eventName)) {
producer.enqueue(eventData);
}
}
if (producers.has(anyProducer)) {
const item = Promise.all([eventName, eventData]);
for (const producer of producers.get(anyProducer)) {
producer.enqueue(item);
}
}
}
function iterator(instance, eventNames) {
eventNames = Array.isArray(eventNames) ? eventNames : [eventNames];
let isFinished = false;
let flush = () => {};
let queue = [];
const producer = {
enqueue(item) {
queue.push(item);
flush();
},
finish() {
isFinished = true;
flush();
},
};
for (const eventName of eventNames) {
let set = getEventProducers(instance, eventName);
if (!set) {
set = new Set();
const producers = producersMap.get(instance);
producers.set(eventName, set);
}
set.add(producer);
}
return {
async next() {
if (!queue) {
return {done: true};
}
if (queue.length === 0) {
if (isFinished) {
queue = undefined;
return this.next();
/**
* @template T
* @param {Sink<T>} sink
*/
addSink = sink => {
sinks.push(sink);
if (sinks.length === 1) {
onFirstSinkAdded();
}
},
await new Promise(resolve => {
flush = resolve;
});
return this.next();
}
return {
done: false,
value: await queue.shift(),
};
},
async return(value) {
queue = undefined;
for (const eventName of eventNames) {
const set = getEventProducers(instance, eventName);
if (set) {
set.delete(producer);
if (set.size === 0) {
const producers = producersMap.get(instance);
producers.delete(eventName);
removeSink = sink => {
const
index = sinks.indexOf(sink);
if (index !== -1) {
sinks.splice(index, 1);
if (sinks.length === 0) {
onLastSinkRemoved();
}
}
}
},
flush();
return arguments.length > 0
? {done: true, value: await value}
: {done: true};
},
[Symbol.asyncIterator]() {
return this;
},
};
}
function defaultMethodNamesOrAssert(methodNames) {
if (methodNames === undefined) {
return allEmitteryMethods;
}
if (!Array.isArray(methodNames)) {
throw new TypeError('`methodNames` must be an array of strings');
}
for (const methodName of methodNames) {
if (!allEmitteryMethods.includes(methodName)) {
if (typeof methodName !== 'string') {
throw new TypeError('`methodNames` element must be a string');
}
throw new Error(`${methodName} is not Emittery method`);
}
}
return methodNames;
}
const isMetaEvent = eventName => eventName === listenerAdded || eventName === listenerRemoved;
function emitMetaEvent(emitter, eventName, eventData) {
if (isMetaEvent(eventName)) {
try {
canEmitMetaEvents = true;
emitter.emit(eventName, eventData);
} finally {
canEmitMetaEvents = false;
}
}
}
class Emittery {
static mixin(emitteryPropertyName, methodNames) {
methodNames = defaultMethodNamesOrAssert(methodNames);
return target => {
if (typeof target !== 'function') {
throw new TypeError('`target` must be function');
}
for (const methodName of methodNames) {
if (target.prototype[methodName] !== undefined) {
throw new Error(`The property \`${methodName}\` already exists on \`target\``);
removeAllSinks = () => {
if (sinks.length > 0) {
sinks.splice(0);
onLastSinkRemoved();
}
}
},
function getEmitteryProperty() {
Object.defineProperty(this, emitteryPropertyName, {
enumerable: false,
value: new Emittery(),
/**
* @template T
* @param {EmitterEvent<T>} event
*/
emitOnce = event => {
const {isSuccess, outcome} = event;
sinks.forEach(({resolve, reject}) => {
(isSuccess ? resolve : reject)(outcome);
});
return this[emitteryPropertyName];
}
Object.defineProperty(target.prototype, emitteryPropertyName, {
enumerable: false,
get: getEmitteryProperty,
});
const emitteryMethodCaller = methodName => function (...args) {
return this[emitteryPropertyName][methodName](...args);
removeAllSinks();
};
for (const methodName of methodNames) {
Object.defineProperty(target.prototype, methodName, {
enumerable: false,
value: emitteryMethodCaller(methodName),
});
}
return target;
return {
addSink,
removeSink,
removeAllSinks,
emitOnce
};
}
};
static get isDebugEnabled() {
// In a browser environment, `globalThis.process` can potentially reference a DOM Element with a `#process` ID,
// so instead of just type checking `globalThis.process`, we need to make sure that `globalThis.process.env` exists.
// eslint-disable-next-line n/prefer-global/process
if (typeof globalThis.process?.env !== 'object') {
return isGlobalDebugEnabled;
}
// eslint-disable-next-line n/prefer-global/process
const {env} = globalThis.process ?? {env: {}};
return env.DEBUG === 'emittery' || env.DEBUG === '*' || isGlobalDebugEnabled;
}
static set isDebugEnabled(newValue) {
isGlobalDebugEnabled = newValue;
}
constructor(options = {}) {
anyMap.set(this, new Set());
eventsMap.set(this, new Map());
producersMap.set(this, new Map());
producersMap.get(this).set(anyProducer, new Set());
this.debug = options.debug ?? {};
if (this.debug.enabled === undefined) {
this.debug.enabled = false;
}
if (!this.debug.logger) {
this.debug.logger = (type, debugName, eventName, eventData) => {
try {
// TODO: Use https://github.com/sindresorhus/safe-stringify when the package is more mature. Just copy-paste the code.
eventData = JSON.stringify(eventData);
} catch {
eventData = `Object with the following keys failed to stringify: ${Object.keys(eventData).join(',')}`;
}
if (typeof eventName === 'symbol' || typeof eventName === 'number') {
eventName = eventName.toString();
}
const currentTime = new Date();
const logTime = `${currentTime.getHours()}:${currentTime.getMinutes()}:${currentTime.getSeconds()}.${currentTime.getMilliseconds()}`;
console.log(`[${logTime}][emittery:${type}][${debugName}] Event Name: ${eventName}\n\tdata: ${eventData}`);
};
}
}
logIfDebugEnabled(type, eventName, eventData) {
if (Emittery.isDebugEnabled || this.debug.enabled) {
this.debug.logger(type, this.debug.name, eventName, eventData);
}
}
on(eventNames, listener) {
assertListener(listener);
eventNames = Array.isArray(eventNames) ? eventNames : [eventNames];
for (const eventName of eventNames) {
assertEventName(eventName);
let set = getListeners(this, eventName);
if (!set) {
set = new Set();
const events = eventsMap.get(this);
events.set(eventName, set);
}
set.add(listener);
this.logIfDebugEnabled('subscribe', eventName, undefined);
if (!isMetaEvent(eventName)) {
emitMetaEvent(this, listenerAdded, {eventName, listener});
}
}
return this.off.bind(this, eventNames, listener);
}
off(eventNames, listener) {
assertListener(listener);
eventNames = Array.isArray(eventNames) ? eventNames : [eventNames];
for (const eventName of eventNames) {
assertEventName(eventName);
const set = getListeners(this, eventName);
if (set) {
set.delete(listener);
if (set.size === 0) {
const events = eventsMap.get(this);
events.delete(eventName);
}
}
this.logIfDebugEnabled('unsubscribe', eventName, undefined);
if (!isMetaEvent(eventName)) {
emitMetaEvent(this, listenerRemoved, {eventName, listener});
}
}
}
once(eventNames) {
let off_;
const promise = new Promise(resolve => {
off_ = this.on(eventNames, data => {
off_();
resolve(data);
});
});
promise.off = off_;
return promise;
}
events(eventNames) {
eventNames = Array.isArray(eventNames) ? eventNames : [eventNames];
for (const eventName of eventNames) {
assertEventName(eventName);
}
return iterator(this, eventNames);
}
async emit(eventName, eventData) {
assertEventName(eventName);
if (isMetaEvent(eventName) && !canEmitMetaEvents) {
throw new TypeError('`eventName` cannot be meta event `listenerAdded` or `listenerRemoved`');
}
this.logIfDebugEnabled('emit', eventName, eventData);
enqueueProducers(this, eventName, eventData);
const listeners = getListeners(this, eventName) ?? new Set();
const anyListeners = anyMap.get(this);
const staticListeners = [...listeners];
const staticAnyListeners = isMetaEvent(eventName) ? [] : [...anyListeners];
await resolvedPromise;
await Promise.all([
...staticListeners.map(async listener => {
if (listeners.has(listener)) {
return listener(eventData);
}
}),
...staticAnyListeners.map(async listener => {
if (anyListeners.has(listener)) {
return listener(eventName, eventData);
}
}),
]);
}
async emitSerial(eventName, eventData) {
assertEventName(eventName);
if (isMetaEvent(eventName) && !canEmitMetaEvents) {
throw new TypeError('`eventName` cannot be meta event `listenerAdded` or `listenerRemoved`');
}
this.logIfDebugEnabled('emitSerial', eventName, eventData);
const listeners = getListeners(this, eventName) ?? new Set();
const anyListeners = anyMap.get(this);
const staticListeners = [...listeners];
const staticAnyListeners = [...anyListeners];
await resolvedPromise;
/* eslint-disable no-await-in-loop */
for (const listener of staticListeners) {
if (listeners.has(listener)) {
await listener(eventData);
}
}
for (const listener of staticAnyListeners) {
if (anyListeners.has(listener)) {
await listener(eventName, eventData);
}
}
/* eslint-enable no-await-in-loop */
}
onAny(listener) {
assertListener(listener);
this.logIfDebugEnabled('subscribeAny', undefined, undefined);
anyMap.get(this).add(listener);
emitMetaEvent(this, listenerAdded, {listener});
return this.offAny.bind(this, listener);
}
anyEvent() {
return iterator(this);
}
offAny(listener) {
assertListener(listener);
this.logIfDebugEnabled('unsubscribeAny', undefined, undefined);
emitMetaEvent(this, listenerRemoved, {listener});
anyMap.get(this).delete(listener);
}
clearListeners(eventNames) {
eventNames = Array.isArray(eventNames) ? eventNames : [eventNames];
for (const eventName of eventNames) {
this.logIfDebugEnabled('clear', eventName, undefined);
if (typeof eventName === 'string' || typeof eventName === 'symbol' || typeof eventName === 'number') {
const set = getListeners(this, eventName);
if (set) {
set.clear();
}
const producers = getEventProducers(this, eventName);
if (producers) {
for (const producer of producers) {
producer.finish();
}
producers.clear();
}
} else {
anyMap.get(this).clear();
for (const [eventName, listeners] of eventsMap.get(this).entries()) {
listeners.clear();
eventsMap.get(this).delete(eventName);
}
for (const [eventName, producers] of producersMap.get(this).entries()) {
for (const producer of producers) {
producer.finish();
}
producers.clear();
producersMap.get(this).delete(eventName);
}
}
}
}
listenerCount(eventNames) {
eventNames = Array.isArray(eventNames) ? eventNames : [eventNames];
let count = 0;
for (const eventName of eventNames) {
if (typeof eventName === 'string') {
count += anyMap.get(this).size
+ (getListeners(this, eventName)?.size ?? 0)
+ (getEventProducers(this, eventName)?.size ?? 0)
+ (getEventProducers(this)?.size ?? 0);
continue;
}
if (typeof eventName !== 'undefined') {
assertEventName(eventName);
}
count += anyMap.get(this).size;
for (const value of eventsMap.get(this).values()) {
count += value.size;
}
for (const value of producersMap.get(this).values()) {
count += value.size;
}
}
return count;
}
bindMethods(target, methodNames) {
if (typeof target !== 'object' || target === null) {
throw new TypeError('`target` must be an object');
}
methodNames = defaultMethodNamesOrAssert(methodNames);
for (const methodName of methodNames) {
if (target[methodName] !== undefined) {
throw new Error(`The property \`${methodName}\` already exists on \`target\``);
}
Object.defineProperty(target, methodName, {
enumerable: false,
value: this[methodName].bind(this),
});
}
}
}
const allEmitteryMethods = Object.getOwnPropertyNames(Emittery.prototype).filter(v => v !== 'constructor');
Object.defineProperty(Emittery, 'listenerAdded', {
value: listenerAdded,
writable: false,
enumerable: true,
configurable: false,
});
Object.defineProperty(Emittery, 'listenerRemoved', {
value: listenerRemoved,
writable: false,
enumerable: true,
configurable: false,
});
var __promiseToCancelable = promise => (res, rej) => {

@@ -1540,3 +1082,2 @@ let

const
emitter = new Emittery(),
doNothing = () => undefined;

@@ -1549,44 +1090,34 @@

isFinallyRejected = false,
previousListenerCount = 0,
finalOutcome;
emitter.on(Emittery.listenerRemoved, (function() {
return function onListenerRemoved() {
//console.log(`listenerRemoved: count=${emitter.listenerCount("settle")}, previousCount=${previousListenerCount}`);
if ((emitter.listenerCount("settle") === 0) && (previousListenerCount > 0)
&& !isFinallyResolved && !isFinallyRejected) {
const
onLastSinkRemoved = () => {
if (!isFinallyResolved && !isFinallyRejected) {
// abort the running computation if the number of consumers drops to zero
cancelRunningComputation();
}
},
previousListenerCount = emitter.listenerCount("settle");
};
}()));
// run once for all when the number of consumers exceeds zero
onFirstSinkAdded = () => {
cancelRunningComputation = cc(
value => {
finalOutcome = value;
isFinallyResolved = true;
emitter.emitOnce({outcome: value, isSuccess: true});
},
error => {
finalOutcome = error;
isFinallyRejected = true;
emitter.emitOnce({outcome: error, isSuccess: false});
}
);
},
emitter.on(Emittery.listenerAdded, (function() {
return function onListenerAdded() {
//console.log(`listenerAdded: count=${emitter.listenerCount("settle")}, previousCount=${previousListenerCount}`);
if ((emitter.listenerCount("settle") > 0) && (previousListenerCount === 0)) {
// run once for all when the number of consumers exceeds zero
cancelRunningComputation = cc(
value => {
finalOutcome = value;
isFinallyResolved = true;
emitter.emit("settle", {outcome: value, isSuccess: true});
},
error => {
finalOutcome = error;
isFinallyRejected = true;
emitter.emit("settle", {outcome: error, isSuccess: false});
}
);
}
emitter = emitterForSingleEmission({onFirstSinkAdded, onLastSinkRemoved});
previousListenerCount = emitter.listenerCount("settle");
};
}()));
return (resolve, reject) => {
if (isFinallyResolved) {
setTimeout(resolve, 0, finalOutcome);
const resolveAsap = ramda.thunkify(resolve)(finalOutcome);
queueMicrotask(resolveAsap);
return doNothing;

@@ -1596,3 +1127,4 @@ }

if (isFinallyRejected) {
setTimeout(reject, 0, finalOutcome);
const rejectAsap = ramda.thunkify(reject)(finalOutcome);
queueMicrotask(rejectAsap);
return doNothing;

@@ -1602,8 +1134,7 @@ }

const
unConsume =
emitter.on("settle", ({isSuccess, outcome}) => {
(isSuccess ? resolve : reject)(outcome);
unConsume();
});
sink = { resolve, reject },
unConsume = () => { emitter.removeSink(sink); };
emitter.addSink(sink);
return unConsume;

@@ -1610,0 +1141,0 @@ };

@@ -7,4 +7,3 @@ {

"dependencies": {
"emittery": "^1.0.1",
"ramda": "^0.28.0"
"ramda": "^0.29.1"
},

@@ -17,8 +16,8 @@ "description": "StaticLand functions for Algebraic Data Types based on native JavaScript types",

"@rollup/plugin-node-resolve": "^15.2.1",
"@types/ramda": "^0.28.23",
"@types/ramda": "^0.29.12",
"chai": "^4.3.8",
"es-module-shims": "^0.4.6",
"hirestime": "^6.1.0",
"istanbul-badges-readme": "^1.8.5",
"mocha": "^10.2.0",
"msw": "^2.0.0",
"rollup": "^3.29.2"

@@ -108,3 +107,3 @@ },

"type": "module",
"version": "2.0.0-alpha.3"
"version": "2.0.0"
}
[![NPM Version](https://img.shields.io/npm/v/@visisoft/staticland.svg?style=flat-square)](https://www.npmjs.com/package/@visisoft/staticland) ![Statements](https://img.shields.io/badge/statements-91.2%25-brightgreen.svg?style=flat-square)
[![Reactive Programming](https://img.shields.io/badge/code%20style-reactive%2C%20functional-blue?color=b7178c)](http://reactivex.io)
[@visisoft/staticland](https://semmel.github.io/StaticLand/) v{{ config.meta.version }}
[@visisoft/staticland](https://semmel.github.io/StaticLand/)
====================

@@ -11,3 +12,3 @@ Support programming in functional pipelines by exposing a familiar set of operations on asynchronous, optional and faulty data.

-----
For the motivation I'd like to refer to [James Sinclair's post on StaticLand][sinclair-static-land].
Article series on FP by [James Sinclair][sinclair-static-land] and [Tom Harding][tom-harding-series].

@@ -148,3 +149,4 @@ Data Types

[crocks]: https://crocks.dev/docs/crocks/
[sinclair-static-land]: https://jrsinclair.com/articles/2020/whats-more-fantastic-than-fantasy-land-static-land
[sinclair-static-land]: https://jrsinclair.com/web-development/
[adispring-comment]: https://github.com/ramda/ramda/issues/3264#issuecomment-1101877126
[tom-harding-series]: http://www.tomharding.me/fantasy-land

@@ -8,3 +8,4 @@ /**

import Emittery from "emittery";
import createEmitter from './internal/emitter.js';
import { thunkify } from "ramda";

@@ -23,3 +24,3 @@ const

const
emitter = new Emittery(),
emitter = createEmitter({onLastSinkRemoved: noop, onFirstSinkAdded: noop}),

@@ -30,3 +31,3 @@ resolve = value => {

// in case anybody already listens, tell them…
emitter.emit("settle", {outcome: value, isSuccess: true});
emitter.emitOnce({outcome: value, isSuccess: true});
},

@@ -38,3 +39,3 @@

// in case anybody already listens, tell them…
emitter.emit("settle", {outcome: error, isSuccess: false});
emitter.emitOnce({outcome: error, isSuccess: false});
},

@@ -45,28 +46,34 @@

// in case anybody listens, forget them all…
emitter.clearListeners(["settle"]);
emitter.removeAllSinks();
},
cancelable = (propagateResolve, propagateReject) => {
const
resolveAsap = thunkify(propagateResolve)(outcome),
rejectAsap = thunkify(propagateReject)(outcome)
switch (state) {
case "pending":
const
unSubscribe =
emitter.on("settle", ({ isSuccess, outcome: theOutcome }) => {
(isSuccess ? propagateResolve : propagateReject)(theOutcome);
unSubscribe();
});
sink = {resolve: propagateResolve, reject: propagateReject},
unsubscribe = () => {
emitter.removeSink(sink);
};
return unSubscribe;
emitter.addSink(sink);
return unsubscribe;
case "cancelled":
return noop;
break;
case "rejected":
setTimeout(propagateReject, 0, outcome);
return noop;
queueMicrotask(rejectAsap);
break;
case "resolved":
setTimeout(propagateResolve, 0, outcome);
return noop;
queueMicrotask(resolveAsap);
break;
default:
setTimeout(propagateReject, 0, new Error(`Unexpected cancelable deferred state: "${state}"`));
return noop;
queueMicrotask(() => {
propagateReject(new Error(`Unexpected cancelable deferred state: "${state}"`));
});
}
return noop;
};

@@ -73,0 +80,0 @@

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

import Emittery from "emittery";
import createEmitter from './internal/emitter.js';
import { thunkify } from "ramda";

@@ -6,3 +7,2 @@ const

const
emitter = new Emittery(),
doNothing = () => undefined;

@@ -15,44 +15,34 @@

isFinallyRejected = false,
previousListenerCount = 0,
finalOutcome;
emitter.on(Emittery.listenerRemoved, (function() {
return function onListenerRemoved() {
//console.log(`listenerRemoved: count=${emitter.listenerCount("settle")}, previousCount=${previousListenerCount}`);
if ((emitter.listenerCount("settle") === 0) && (previousListenerCount > 0)
&& !isFinallyResolved && !isFinallyRejected) {
const
onLastSinkRemoved = () => {
if (!isFinallyResolved && !isFinallyRejected) {
// abort the running computation if the number of consumers drops to zero
cancelRunningComputation();
}
},
previousListenerCount = emitter.listenerCount("settle");
};
}()));
// run once for all when the number of consumers exceeds zero
onFirstSinkAdded = () => {
cancelRunningComputation = cc(
value => {
finalOutcome = value;
isFinallyResolved = true;
emitter.emitOnce({outcome: value, isSuccess: true});
},
error => {
finalOutcome = error;
isFinallyRejected = true;
emitter.emitOnce({outcome: error, isSuccess: false});
}
);
},
emitter.on(Emittery.listenerAdded, (function() {
return function onListenerAdded() {
//console.log(`listenerAdded: count=${emitter.listenerCount("settle")}, previousCount=${previousListenerCount}`);
if ((emitter.listenerCount("settle") > 0) && (previousListenerCount === 0)) {
// run once for all when the number of consumers exceeds zero
cancelRunningComputation = cc(
value => {
finalOutcome = value;
isFinallyResolved = true;
emitter.emit("settle", {outcome: value, isSuccess: true});
},
error => {
finalOutcome = error;
isFinallyRejected = true;
emitter.emit("settle", {outcome: error, isSuccess: false});
}
);
}
emitter = createEmitter({onFirstSinkAdded, onLastSinkRemoved});
previousListenerCount = emitter.listenerCount("settle");
};
}()));
return (resolve, reject) => {
if (isFinallyResolved) {
setTimeout(resolve, 0, finalOutcome);
const resolveAsap = thunkify(resolve)(finalOutcome);
queueMicrotask(resolveAsap);
return doNothing;

@@ -62,3 +52,4 @@ }

if (isFinallyRejected) {
setTimeout(reject, 0, finalOutcome);
const rejectAsap = thunkify(reject)(finalOutcome);
queueMicrotask(rejectAsap);
return doNothing;

@@ -68,8 +59,7 @@ }

const
unConsume =
emitter.on("settle", ({isSuccess, outcome}) => {
(isSuccess ? resolve : reject)(outcome);
unConsume();
});
sink = { resolve, reject },
unConsume = () => { emitter.removeSink(sink); };
emitter.addSink(sink);
return unConsume;

@@ -76,0 +66,0 @@ };