tsoa
This commit is contained in:
332
node_modules/@hapi/podium/lib/index.d.ts
generated
vendored
Normal file
332
node_modules/@hapi/podium/lib/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,332 @@
|
||||
|
||||
type IfUndefinedElse<T, If, Else> = T extends undefined ? If : Else;
|
||||
|
||||
type EventNames<Events> = IfUndefinedElse<Events, string, keyof Events>;
|
||||
|
||||
type EventListenerParameters<Events> = Events[keyof Events] extends (...args: any) => any
|
||||
? Parameters<Events[keyof Events]>
|
||||
: never
|
||||
;
|
||||
|
||||
type EmitData<Events> = IfUndefinedElse<Events, any, EventListenerParameters<Events>>;
|
||||
|
||||
type EventListener<Events, TArgs extends unknown[], TContext extends object> = Podium.Listener<
|
||||
TContext,
|
||||
IfUndefinedElse<Events, TArgs, EventListenerParameters<Events>>
|
||||
>;
|
||||
|
||||
type WithRequiredProperty<Type, Key extends keyof Type> = Type & {
|
||||
[Property in Key]-?: Type[Property];
|
||||
};
|
||||
|
||||
/**
|
||||
* Node (semi) compatible event emitter with extra features.
|
||||
*/
|
||||
export class Podium<Events = undefined> {
|
||||
/**
|
||||
* Creates a new podium emitter.
|
||||
*
|
||||
* @param events - If present, the value is passed to podium.registerEvent().
|
||||
* @param options - optional configuration options passed to podium.registerEvent().
|
||||
*/
|
||||
constructor(events?: Podium.Event<EventNames<Events>> | Podium.Event<EventNames<Events>>[], options?: Podium.EventSettings);
|
||||
|
||||
/**
|
||||
* Register the specified events and their optional configuration. Events must be registered
|
||||
* before they can be emitted or subscribed to. This is done to detect event name mispelling
|
||||
* and invalid event activities.
|
||||
*
|
||||
* @param events - The event(s) to register.
|
||||
* @param options - optional configuration options.
|
||||
*/
|
||||
registerEvent(events: Podium.Event<EventNames<Events>> | Podium.Event<EventNames<Events>>[], options?: Podium.EventSettings): void;
|
||||
|
||||
/**
|
||||
* Emits an event update to all the subscribed listeners.
|
||||
|
||||
* @param criteria - The event update criteria.
|
||||
* @param data - The value emitted to the subscribers.
|
||||
*/
|
||||
emit(criteria: Podium.EmitCriteria<EventNames<Events>>, data?: EmitData<Events>): void;
|
||||
|
||||
/**
|
||||
* Emits an event update to all the subscribed listeners and resolves an array of their results.
|
||||
|
||||
* @param criteria - The event update criteria.
|
||||
* @param data - The value emitted to the subscribers.
|
||||
*/
|
||||
gauge<T = unknown>(criteria: Podium.EmitCriteria<EventNames<Events>>, data?: EmitData<Events>): Promise<PromiseSettledResult<T>[]>;
|
||||
|
||||
/**
|
||||
* Subscribe a handler to an event.
|
||||
*
|
||||
* @param criteria - The subscription criteria.
|
||||
* @param listener - The handler method set to receive event updates. The function signature
|
||||
* depends on the block, spread, and tags options.
|
||||
* @param context - Optional object that binds to the listener handler.
|
||||
*
|
||||
* @returns A reference to the current emitter.
|
||||
*/
|
||||
on<TArgs extends any[] = unknown[], Tcontext extends object = this>
|
||||
(criteria: Podium.Criteria<EventNames<Events>>, listener: EventListener<Events, TArgs, Tcontext>, context?: Tcontext): this
|
||||
|
||||
/**
|
||||
* Subscribe a handler to an event. Same as podium.on().
|
||||
*
|
||||
* @param criteria - The subscription criteria.
|
||||
* @param listener - The handler method set to receive event updates. The function signature
|
||||
* depends on the block, spread, and tags options.
|
||||
* @param context - Optional object that binds to the listener handler.
|
||||
*
|
||||
* @returns A reference to the current emitter.
|
||||
*/
|
||||
addListener<TArgs extends any[] = unknown[], Tcontext extends object = this>
|
||||
(criteria: Podium.Criteria<EventNames<Events>>, listener: EventListener<Events, TArgs, Tcontext>, context?: Tcontext): this;
|
||||
|
||||
/**
|
||||
* Same as podium.on() with the count option set to 1.
|
||||
*
|
||||
* Can also be called without an listener to wait for a single event.
|
||||
*
|
||||
* @param criteria - The subscription criteria.
|
||||
* @param listener - The handler method set to receive event updates. The function signature
|
||||
* depends on the block, spread, and tags options.
|
||||
* @param context - Optional object that binds to the listener handler.
|
||||
*
|
||||
* @returns A reference to the current emitter.
|
||||
*/
|
||||
once<TArgs extends any[] = unknown[], Tcontext extends object = this>
|
||||
(criteria: Podium.OnceCriteria<EventNames<Events>>, listener: EventListener<Events, TArgs, Tcontext>, context?: Tcontext): this;
|
||||
|
||||
/**
|
||||
* Subscribes to an event by returning a promise that resolves when the event is emitted.
|
||||
*
|
||||
* @param criteria - The subscription criteria.
|
||||
*
|
||||
* @returns Promise with array of emitted parameters.
|
||||
*/
|
||||
once<TArgs extends any[] = unknown[], Tcontext extends object = this>(criteria: Podium.OnceCriteria<EventNames<Events>>): Promise<TArgs>;
|
||||
|
||||
/**
|
||||
* Subscribes to an event by returning a promise that resolves when the event is emitted `count` times.
|
||||
*
|
||||
* @param criteria - The subscription criteria.
|
||||
*
|
||||
* @returns Promise with array where each item is an array of emitted arguments.
|
||||
*/
|
||||
few<TArgs extends any[] = unknown[], Tcontext extends object = this>(criteria: Podium.FewCriteria<EventNames<Events>>): Promise<TArgs>;
|
||||
|
||||
/**
|
||||
* Removes all listeners subscribed to a given event name matching the provided listener method.
|
||||
*
|
||||
* @param name - The event name string.
|
||||
* @param listener - The function reference provided when subscribed.
|
||||
*
|
||||
* @returns A reference to the current emitter.
|
||||
*/
|
||||
off(name: string, listener: Podium.Listener): this;
|
||||
|
||||
/**
|
||||
* Removes all listeners subscribed to a given event name matching the provided listener method.
|
||||
*
|
||||
* @param name - The event name string.
|
||||
* @param listener - The function reference provided when subscribed.
|
||||
*
|
||||
* @returns A reference to the current emitter.
|
||||
*/
|
||||
removeListener(name: string, listener: Podium.Listener): this;
|
||||
|
||||
/**
|
||||
* Removes all listeners subscribed to a given event name.
|
||||
*
|
||||
* @param name - The event name string.
|
||||
*
|
||||
* @returns A reference to the current emitter.
|
||||
*/
|
||||
removeAllListeners(name: string): this;
|
||||
|
||||
/**
|
||||
* Returns whether an event has any listeners subscribed.
|
||||
*
|
||||
* @param name the event name string.
|
||||
*
|
||||
* @returns true if the event name has any listeners, otherwise false.
|
||||
*/
|
||||
hasListeners(name: string): boolean;
|
||||
}
|
||||
|
||||
declare namespace Podium {
|
||||
|
||||
type EventName = string | number | symbol
|
||||
|
||||
interface EmitCriteriaInterface<TName extends EventName> {
|
||||
|
||||
/**
|
||||
* Event name.
|
||||
*/
|
||||
readonly name: TName;
|
||||
|
||||
/**
|
||||
* Channel name.
|
||||
*/
|
||||
readonly channel?: string | undefined;
|
||||
|
||||
/**
|
||||
* The tags to apply.
|
||||
*/
|
||||
readonly tags?: string | string[] | { [tag: string]: boolean } | undefined;
|
||||
}
|
||||
|
||||
export type EmitCriteria<TName extends EventName> = EmitCriteriaInterface<TName> | TName
|
||||
|
||||
interface EventOptionsInterface<TName extends EventName> {
|
||||
|
||||
/**
|
||||
* Event name.
|
||||
*/
|
||||
readonly name: TName;
|
||||
|
||||
/**
|
||||
* A string or array of strings specifying the event channels available.
|
||||
*
|
||||
* Defaults to no channel restrictions - Event updates can specify a channel or not.
|
||||
*/
|
||||
readonly channels?: string | string[] | undefined;
|
||||
|
||||
/**
|
||||
* Set to make podium.emit() clone the data object passed to it, before it is passed to the
|
||||
* listeners (unless an override specified by each listener).
|
||||
*
|
||||
* Defaults to false - Data is passed as-is.
|
||||
*/
|
||||
readonly clone?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Set to require the data object passed to podium.emit() to be an array, and make the
|
||||
* listener method called with each array element passed as a separate argument (unless an
|
||||
* override specified by each listener).
|
||||
*
|
||||
* This should only be used when the emitted data structure is known and predictable.
|
||||
*
|
||||
* Defaults to false - Data is emitted as a single argument regardless of its type.
|
||||
*/
|
||||
readonly spread?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Set to make any tags in the critieria object passed to podium.emit() map to an object
|
||||
* (where each tag string is the key and the value is true) which is appended to the emitted
|
||||
* arguments list at the end.
|
||||
*
|
||||
* A configuration override can be set by each listener.
|
||||
*
|
||||
* Defaults to false.
|
||||
*/
|
||||
readonly tags?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Set to allow the same event name to be registered multiple times, ignoring all but the
|
||||
* first.
|
||||
*
|
||||
* Note that if the registration config is changed between registrations, only the first
|
||||
* configuration is used.
|
||||
*
|
||||
* Defaults to false - A duplicate registration will throw an error.
|
||||
*/
|
||||
readonly shared?: boolean | undefined;
|
||||
}
|
||||
|
||||
type EventOptions<TName extends EventName> = EventOptionsInterface<TName> | TName
|
||||
|
||||
type Event<TName extends EventName> = TName | EventOptions<TName>;
|
||||
|
||||
export interface EventSettings {
|
||||
|
||||
/**
|
||||
* If false, events are not validated. This is only allowed when the events
|
||||
* value is returned from Podium.validate().
|
||||
*
|
||||
* Defaults to true
|
||||
*/
|
||||
readonly validate?: boolean | undefined;
|
||||
}
|
||||
|
||||
type Listener<TContext extends object = any, TArgs extends any[] = any[]> =
|
||||
(this: TContext, ...args: TArgs) => unknown;
|
||||
|
||||
interface CriteriaFilterOptionsObject {
|
||||
|
||||
/**
|
||||
* A tag string or array of tag strings.
|
||||
*/
|
||||
readonly tags?: string | string[] | undefined;
|
||||
|
||||
/**
|
||||
* Require all tags to be present for the event update to match the subscription.
|
||||
*
|
||||
* Default false - Require at least one matching tag.
|
||||
*/
|
||||
readonly all?: boolean | undefined;
|
||||
}
|
||||
|
||||
interface CriteriaInterface<TName extends EventName> {
|
||||
|
||||
/**
|
||||
* Event name.
|
||||
*/
|
||||
readonly name: TName;
|
||||
|
||||
/**
|
||||
* The event channels to subscribe to.
|
||||
*
|
||||
* If the event registration specified a list of allowed channels, the channels array must
|
||||
* match the allowed channels. If channels are specified, event updates without any channel
|
||||
* designation will not be included in the subscription.
|
||||
*
|
||||
* Defaults to no channels filter.
|
||||
*/
|
||||
readonly channels?: string | string[] | undefined;
|
||||
|
||||
/**
|
||||
* Set to clone the data object passed to podium.emit() before it is passed to the listener
|
||||
* method.
|
||||
*
|
||||
* Defaults to the event registration option (which defaults to false).
|
||||
*/
|
||||
readonly clone?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* A positive non-zero integer indicating the number of times the listener can be called
|
||||
* after which the subscription is automatically removed.
|
||||
*
|
||||
* Does nothing when calling once(), where it will use the value 1.
|
||||
*
|
||||
* Defaults to no limit.
|
||||
*/
|
||||
readonly count?: number | undefined;
|
||||
|
||||
/**
|
||||
* The event tags (if present) to subscribe to.
|
||||
*/
|
||||
readonly filter?: string | string[] | CriteriaFilterOptionsObject | undefined;
|
||||
|
||||
/**
|
||||
* Override the value of spread from the event registraiont when the listener is called.
|
||||
*
|
||||
* This should only be used when the emitted data structure is known and predictable.
|
||||
*
|
||||
* Defaults to the event registration option (which defaults to false).
|
||||
*/
|
||||
readonly spread?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Override the value of tags from the event registraiont when the listener is called.
|
||||
*
|
||||
* Defaults to the event registration option (which defaults to false).
|
||||
*/
|
||||
readonly tags?: boolean | undefined;
|
||||
}
|
||||
|
||||
export type Criteria<TName extends EventName = string> = CriteriaInterface<TName> | TName
|
||||
export type OnceCriteria<TName extends EventName = string> = Omit<CriteriaInterface<TName>, 'count'> | TName
|
||||
export type FewCriteria<TName extends EventName = string> = WithRequiredProperty<CriteriaInterface<TName>, 'count'>
|
||||
}
|
||||
330
node_modules/@hapi/podium/lib/index.js
generated
vendored
Executable file
330
node_modules/@hapi/podium/lib/index.js
generated
vendored
Executable file
@@ -0,0 +1,330 @@
|
||||
'use strict';
|
||||
|
||||
const Hoek = require('@hapi/hoek');
|
||||
const Teamwork = require('@hapi/teamwork');
|
||||
const Validate = require('@hapi/validate');
|
||||
|
||||
|
||||
const internals = {
|
||||
schema: {
|
||||
base: Validate.object({
|
||||
name: Validate.string().required(),
|
||||
clone: Validate.boolean(),
|
||||
tags: Validate.boolean(),
|
||||
spread: Validate.boolean(),
|
||||
channels: Validate.array().items(Validate.string()).single().unique().min(1).cast('set')
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
internals.schema.event = internals.schema.base.keys({
|
||||
shared: Validate.boolean()
|
||||
});
|
||||
|
||||
|
||||
internals.schema.listener = internals.schema.base.keys({
|
||||
listener: Validate.func().required(),
|
||||
context: Validate.object(),
|
||||
count: Validate.number().integer().min(1),
|
||||
filter: {
|
||||
tags: Validate.array().items(Validate.string()).single().unique().min(1).required(),
|
||||
all: Validate.boolean()
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
exports.validate = function (events) {
|
||||
|
||||
const normalized = [];
|
||||
events = [].concat(events);
|
||||
for (let event of events) {
|
||||
if (typeof event === 'string') {
|
||||
event = { name: event };
|
||||
}
|
||||
|
||||
normalized.push(Validate.attempt(event, internals.schema.event, 'Invalid event options'));
|
||||
}
|
||||
|
||||
return normalized;
|
||||
};
|
||||
|
||||
|
||||
exports.Podium = class {
|
||||
|
||||
/** @type {Map<string,internals.EventListener>} */
|
||||
#listeners = new Map();
|
||||
|
||||
constructor(events, options) {
|
||||
|
||||
if (events) {
|
||||
this.registerEvent(events, options);
|
||||
}
|
||||
}
|
||||
|
||||
registerEvent(events, options) {
|
||||
|
||||
events = [].concat(events);
|
||||
for (let event of events) {
|
||||
if (typeof event === 'string') {
|
||||
event = { name: event };
|
||||
}
|
||||
|
||||
if (options?.validate !== false) { // Defaults to true
|
||||
event = Validate.attempt(event, internals.schema.event, 'Invalid event options');
|
||||
}
|
||||
|
||||
const name = event.name;
|
||||
if (this.#listeners.has(name)) {
|
||||
Hoek.assert(event.shared, `Event ${name} exists`);
|
||||
continue;
|
||||
}
|
||||
|
||||
this.#listeners.set(name, new internals.EventListener(event));
|
||||
}
|
||||
}
|
||||
|
||||
emit(criteria, data) {
|
||||
|
||||
let thrownErr;
|
||||
|
||||
this.#emitToEachListener(criteria, data, ([err]) => {
|
||||
|
||||
thrownErr = thrownErr ?? err;
|
||||
});
|
||||
|
||||
if (thrownErr) {
|
||||
throw thrownErr;
|
||||
}
|
||||
}
|
||||
|
||||
async gauge(criteria, data) {
|
||||
|
||||
const promises = [];
|
||||
|
||||
this.#emitToEachListener(criteria, data, ([err, result]) => {
|
||||
|
||||
promises.push(err ? Promise.reject(err) : result);
|
||||
});
|
||||
|
||||
return await Promise.allSettled(promises);
|
||||
}
|
||||
|
||||
#emitToEachListener(criteria, data, fn) {
|
||||
|
||||
criteria = internals.criteria(criteria);
|
||||
|
||||
const name = criteria.name;
|
||||
Hoek.assert(name, 'Criteria missing event name');
|
||||
|
||||
const event = this.#listeners.get(name);
|
||||
Hoek.assert(event, `Unknown event ${name}`);
|
||||
|
||||
if (!event.handlers) {
|
||||
return;
|
||||
}
|
||||
|
||||
Hoek.assert(!criteria.channel || typeof criteria.channel === 'string', 'Invalid channel name');
|
||||
Hoek.assert(!criteria.channel || !event.flags.channels || event.flags.channels.has(criteria.channel), `Unknown ${criteria.channel} channel`);
|
||||
Hoek.assert(!event.flags.spread || Array.isArray(data) || typeof data === 'function', 'Data must be an array for spread event');
|
||||
|
||||
if (typeof criteria.tags === 'string') {
|
||||
criteria = { ...criteria };
|
||||
criteria.tags = { [criteria.tags]: true };
|
||||
}
|
||||
|
||||
if (criteria.tags &&
|
||||
Array.isArray(criteria.tags)) {
|
||||
|
||||
// Map array to object
|
||||
|
||||
const tags = {};
|
||||
for (const tag of criteria.tags) {
|
||||
tags[tag] = true;
|
||||
}
|
||||
|
||||
criteria = { ...criteria };
|
||||
criteria.tags = tags;
|
||||
}
|
||||
|
||||
let generated = false;
|
||||
|
||||
for (const handler of event.handlers) {
|
||||
if (handler.channels &&
|
||||
!(criteria.channel && handler.channels.has(criteria.channel))) {
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (handler.filter) {
|
||||
if (!criteria.tags) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const match = Hoek.intersect(criteria.tags, handler.filter.tags, { first: !handler.filter.all });
|
||||
if (!match ||
|
||||
handler.filter.all && match.length !== handler.filter.tags.length) {
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Found a listener, now prepare to call it
|
||||
|
||||
if (handler.count) {
|
||||
--handler.count;
|
||||
if (handler.count < 1) {
|
||||
event.removeListener(handler.listener);
|
||||
}
|
||||
}
|
||||
|
||||
if (!generated &&
|
||||
typeof data === 'function') {
|
||||
|
||||
data = data();
|
||||
generated = true;
|
||||
}
|
||||
|
||||
const update = event.flagged('clone', handler) ? Hoek.clone(data) : data;
|
||||
const args = event.flagged('spread', handler) && Array.isArray(update) ? update.slice(0) : [update];
|
||||
|
||||
if (event.flagged('tags', handler) &&
|
||||
criteria.tags) {
|
||||
|
||||
args.push(criteria.tags);
|
||||
}
|
||||
|
||||
// Call the listener
|
||||
|
||||
try {
|
||||
if (handler.context) {
|
||||
fn([null, handler.listener.apply(handler.context, args)]);
|
||||
}
|
||||
else {
|
||||
fn([null, handler.listener(...args)]);
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
fn([err, null]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addListener(criteria, listener, context) {
|
||||
|
||||
criteria = internals.criteria(criteria);
|
||||
criteria.listener = listener;
|
||||
criteria.context = context;
|
||||
|
||||
if (criteria.filter &&
|
||||
(typeof criteria.filter === 'string' || Array.isArray(criteria.filter))) {
|
||||
|
||||
criteria = { ...criteria };
|
||||
criteria.filter = { tags: criteria.filter };
|
||||
}
|
||||
|
||||
criteria = Validate.attempt(criteria, internals.schema.listener, 'Invalid event listener options');
|
||||
|
||||
const name = criteria.name;
|
||||
const event = this.#listeners.get(name);
|
||||
Hoek.assert(event, `Unknown event ${name}`);
|
||||
|
||||
event.addHandler(criteria);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
on(criteria, listener, context) {
|
||||
|
||||
return this.addListener(criteria, listener, context);
|
||||
}
|
||||
|
||||
once(criteria, listener, context) {
|
||||
|
||||
criteria = { ...internals.criteria(criteria), count: 1 };
|
||||
|
||||
if (listener) {
|
||||
return this.addListener(criteria, listener, context);
|
||||
}
|
||||
|
||||
return new Promise((resolve) => {
|
||||
|
||||
this.addListener(criteria, (...args) => resolve(args));
|
||||
});
|
||||
}
|
||||
|
||||
few(criteria) {
|
||||
|
||||
Hoek.assert(typeof criteria === 'object', 'Criteria must be an object');
|
||||
Hoek.assert(criteria.count, 'Criteria must include a count limit');
|
||||
|
||||
const team = new Teamwork.Team({ meetings: criteria.count });
|
||||
this.addListener(criteria, (...args) => team.attend(args));
|
||||
return team.work;
|
||||
}
|
||||
|
||||
removeListener(name, listener) {
|
||||
|
||||
Hoek.assert(this.#listeners.has(name), `Unknown event ${name}`);
|
||||
Hoek.assert(typeof listener === 'function', 'Listener must be a function');
|
||||
|
||||
this.#listeners.get(name).removeListener(listener);
|
||||
return this;
|
||||
}
|
||||
|
||||
off(name, listener) {
|
||||
|
||||
return this.removeListener(name, listener);
|
||||
}
|
||||
|
||||
removeAllListeners(name) {
|
||||
|
||||
Hoek.assert(this.#listeners.has(name), `Unknown event ${name}`);
|
||||
this.#listeners.get(name).handlers = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
hasListeners(name) {
|
||||
|
||||
Hoek.assert(this.#listeners.has(name), `Unknown event ${name}`);
|
||||
return !!this.#listeners.get(name).handlers;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
internals.EventListener = class {
|
||||
|
||||
constructor(flags) {
|
||||
|
||||
this.flags = flags;
|
||||
this.handlers = null;
|
||||
}
|
||||
|
||||
addHandler(handler) {
|
||||
|
||||
Hoek.assert(!handler.channels || !this.flags.channels || Hoek.intersect(this.flags.channels, handler.channels).length === handler.channels.size, `Unknown event channels ${handler.channels && [...handler.channels].join(', ')}`);
|
||||
|
||||
this.handlers = this.handlers ? [...this.handlers, handler] : [handler]; // Don't mutate
|
||||
}
|
||||
|
||||
removeListener(listener) {
|
||||
|
||||
const filtered = this.handlers?.filter((item) => item.listener !== listener);
|
||||
this.handlers = filtered?.length ? filtered : null;
|
||||
}
|
||||
|
||||
flagged(name, handler) {
|
||||
|
||||
return handler[name] ?? this.flags[name] ?? false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
internals.criteria = function (criteria) {
|
||||
|
||||
if (typeof criteria === 'string') {
|
||||
return { name: criteria };
|
||||
}
|
||||
|
||||
return criteria;
|
||||
};
|
||||
Reference in New Issue
Block a user