修改后台权限
This commit is contained in:
9
node_modules/rettime/LICENSE.md
generated
vendored
Normal file
9
node_modules/rettime/LICENSE.md
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024—present Artem Zakharchenko
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
343
node_modules/rettime/README.md
generated
vendored
Normal file
343
node_modules/rettime/README.md
generated
vendored
Normal file
@@ -0,0 +1,343 @@
|
||||
# Rettime
|
||||
|
||||
A type-safe marriage of `EventTarget` and `EventEmitter`.
|
||||
|
||||
## Features
|
||||
|
||||
- 🎯 **Event-based**. Control event flow: prevent defaults, stop propagation, cancel events. Something your common `Emitter` can't do.
|
||||
- 🗼 **Emitter-inspired**. Emit event types and data, don't bother with creating `Event` instances. A bit less verbosity than a common `EventTarget`.
|
||||
- ⛑️ **Type-safe**. Describe the exact event types and payloads accepted by the emitter. Never emit or listen to unknown events.
|
||||
- 🧰 **Convenience methods** like `.emitAsPromise()` and `.emitAsGenerator()` to build more complex event-driven systems.
|
||||
- 🐙 **Tiny**. 700B gzipped.
|
||||
|
||||
> [!WARNING]
|
||||
> This library **does not** have performance as the end goal. In fact, since it operates on events and supports event cancellation, it will likely be _slower_ than the emitters that don't do that.
|
||||
|
||||
## Motivation
|
||||
|
||||
### Why not just `EventTarget`?
|
||||
|
||||
The `EventTarget` API is fantastic. It works in the browser and in Node.js, dispatches actual events, supports cancellation, etc. At the same time, it has a number of flaws that prevent me from using it for anything serious:
|
||||
|
||||
- Complete lack of type safety. The `type` in `new Event(type)` is not a type argument in `lib.dom.ts`. It's always `string`. It means it's impossible to narrow it down to a literal string type to achieve type safety.
|
||||
- No concept of `.prependListener()`. There is no way to add a listener to run _first_, before other existing listeners.
|
||||
- No concept of `.removeAllListeners()`. You have to remove each individual listener by hand. Good if you own the listeners, not so good if you don't.
|
||||
- No concept of `.listenerCount()` or knowing if a dispatched event had any listeners (the `boolean` returned from `.dispatch()` indicates if the event has been prevented, not whether it had any listeners).
|
||||
- (Opinionated) Verbose. I prefer `.on()` over `.addEventListener()`. I prefer passing data than constructing `new MessageEvent()` all the time.
|
||||
|
||||
### Why not just `Emitter` (in Node.js)?
|
||||
|
||||
The `Emitter` API in Node.js is great, but it has its own downsides:
|
||||
|
||||
- Node.js-specific. `Emitter` does not work in the browser.
|
||||
- Lacks any type safety.
|
||||
- No concept of `.stopPropagation()` and `.stopImmediatePropagation()`. Those methods are defined but literally do nothing.
|
||||
|
||||
## Install
|
||||
|
||||
```sh
|
||||
npm install rettime
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### `TypedEvent`
|
||||
|
||||
`TypedEvent` is a subset of `MessageEvent` that allows for type-safe event declaration.
|
||||
|
||||
```ts
|
||||
new TypedEvent<DataType, ReturnType, EventType>(type: EventType, { data: DataType })
|
||||
```
|
||||
|
||||
> The `data` argument depends on the `DataType` of your event. Use `void` if the event must not send any data.
|
||||
|
||||
|
||||
#### Reserved events
|
||||
|
||||
_Reserved events_ refer to event types reserved for specific behaviors by the library.
|
||||
|
||||
#### `*`
|
||||
|
||||
Using a wildcard (`*`) event type allows you to listen to _any_ events emitted on the emitter. This is similar to methods like `.onAny()` you might find in the wild.
|
||||
|
||||
```ts
|
||||
const emitter = new Emitter<{
|
||||
greeting: TypedEvent<string>()
|
||||
cart: TypedEvent<CartItem[]>()
|
||||
}>()
|
||||
|
||||
emitter
|
||||
.on('*', (event) => {
|
||||
event.type // "greeting" | "cart"
|
||||
console.log(`Caught: ${event.type}, ${event.data}`)
|
||||
})
|
||||
.on('greeting', (event) => {
|
||||
console.log(`Hello, ${event.data}!`)
|
||||
})
|
||||
|
||||
emitter.emit('greeting', 'John')
|
||||
// "Caught: greeting, John"
|
||||
// "Hello, John!"
|
||||
````
|
||||
|
||||
Wildcard listeners are supported by all subscription methods, like `.on()`, `.once()`, `.earlyOn()`, and .`earlyOnce()`, are type-safe and fully support the listener order sensitivity.
|
||||
|
||||
#### Custom events
|
||||
|
||||
You can implement custom events by extending the default `TypedEvent` class and forwarding the type arguments that it expects:
|
||||
|
||||
```ts
|
||||
class GreetingEvent<
|
||||
DataType = void,
|
||||
ReturnType = any,
|
||||
EventType extends string = string,
|
||||
> extends TypedEvent<DataType, ReturnType, EventType> {
|
||||
public id: string
|
||||
}
|
||||
|
||||
const emitter = new Emitter<{ greeting: GreetingEvent<'john'> }>()
|
||||
|
||||
emitter.on('greeting', (event) => {
|
||||
console.log(event instanceof GreetingEvent) // true
|
||||
console.log(event instanceof TypedEvent) // true
|
||||
console.log(event instanceof MessageEvent) // true
|
||||
|
||||
console.log(event.type) // "greeting"
|
||||
console.log(event.data) // "john"
|
||||
console.log(event.id) // string
|
||||
})
|
||||
```
|
||||
|
||||
### `Emitter`
|
||||
|
||||
```ts
|
||||
new Emitter<EventMap>()
|
||||
```
|
||||
|
||||
The `EventMap` type argument allows you describe the supported event types, their payload, and the return type of their event listeners.
|
||||
|
||||
```ts
|
||||
import { Emitter, TypedEvent } from 'rettime'
|
||||
|
||||
const emitter = new Emitter<{ hello: TypedEvent<string, number> }>()
|
||||
|
||||
emitter.on('hello', () => 1) // ✅
|
||||
emitter.on('hello', () => 'oops') // ❌ string not assignable to type number
|
||||
|
||||
emitter.emit(new TypedEvent('hello', { data: 'John' })) // ✅
|
||||
emitter.emit(new TypedEvent('hello', { data: 123 })) // ❌ number is not assignable to type string
|
||||
emitter.emit(new TypedEvent('hello')) // ❌ missing data argument of type string
|
||||
|
||||
emitter.emit(new TypedEvent('unknown')) // ❌ "unknown" does not satisfy "hello"
|
||||
```
|
||||
|
||||
#### Describing events
|
||||
|
||||
The `Emitter` class requires a type argument that describes the event map. If you do not provide that argument, adding listeners or emitting events will produce a type error as your emitter doesn't have an event map defined.
|
||||
|
||||
An event map is an object of the following shape:
|
||||
|
||||
```ts
|
||||
{
|
||||
[type: string]: TypedEvent
|
||||
}
|
||||
```
|
||||
|
||||
The `type` is a string indicating the event type (e.g. `greet` or `ping`). The array it accepts has two members: `args` describes the arguments accepted by this event (can also be `never` for events without arguments) and `returnValue` is an optional type for the data returned from the listeners for this event.
|
||||
|
||||
Let's say you want to define a `greet` event that expects a user name as data and returns a greeting string:
|
||||
|
||||
```ts
|
||||
import { Emitter, TypedEvent } from 'rettime'
|
||||
|
||||
const emitter = new Emitter<{ greet: TypedEvent<string, string> }>()
|
||||
|
||||
emitter.on('greet', (event) => {
|
||||
console.log(`Hello, ${event.data}!`)
|
||||
})
|
||||
emitter.emit(new TypedEvent('greet', { data: 'John' }))
|
||||
// "Hello, John!"
|
||||
```
|
||||
|
||||
Here's another example where we define a `ping` event that has no arguments but returns a timestamp for each ping:
|
||||
|
||||
```ts
|
||||
const emitter = new Emitter<{ ping: TypedEvent<void, number> }>()
|
||||
|
||||
emitter.on('ping', () => Date.now())
|
||||
|
||||
const results = await emitter.emitAsPromise(new TypedEvent('ping'))
|
||||
// [1745658424732]
|
||||
```
|
||||
|
||||
> [!IMPORTANT]
|
||||
> When providing type arguments to your `TypedEvents`, you **do not** need to provide the `EventType` argument—it will be inferred from your event map.
|
||||
|
||||
### `.on(type, listener[, options])`
|
||||
|
||||
Adds an event listener for the given event type.
|
||||
|
||||
```ts
|
||||
import { Emitter, TypedEvent } from 'rettime'
|
||||
|
||||
const emitter = new Emitter<{ hello: TypedEvent<string> }>()
|
||||
|
||||
emitter.on('hello', (event) => {
|
||||
// `event` is a `TypedEvent` instance derived from `MessageEvent`.
|
||||
console.log(event.data)
|
||||
})
|
||||
```
|
||||
|
||||
All methods that add new listeners return an `AbortController` instance bound to that listener. You can use that controller to cancel the event handling, including mid-air:
|
||||
|
||||
```ts
|
||||
const controller = emitter.on('hello', listener)
|
||||
controller.abort(reason)
|
||||
```
|
||||
|
||||
All methods that add new listeners also accept an optional `options` argument. You can use it to configure event handling behavior. For example, you can provide an existing `AbortController` signal as the `options.signal` value so the attached listener abides by your controller:
|
||||
|
||||
```ts
|
||||
emitter.on('hello', listener, { signal: controller.signal })
|
||||
```
|
||||
|
||||
> Both the public controller of the event and your custom controller are combined using `AbortSignal.any()`.
|
||||
|
||||
### `.once(type, listener[, options])`
|
||||
|
||||
Adds a one-time event listener for the given event type.
|
||||
|
||||
### `.earlyOn(type, listener[, options])`
|
||||
|
||||
Prepends a listener for the given event type.
|
||||
|
||||
```ts
|
||||
import { Emitter, TypedEvent } from 'rettime'
|
||||
|
||||
const emitter = new Emitter<{ hello: TypedEvent<void, number> }>()
|
||||
|
||||
emitter.on('hello', () => 1)
|
||||
emitter.earlyOn('hello', () => 2)
|
||||
|
||||
const results = await emitter.emitAsPromise(new TypedEvent('hello'))
|
||||
// [2, 1]
|
||||
```
|
||||
|
||||
### `.earlyOnce(type, listener[, options])`
|
||||
|
||||
Prepends a one-time listener for the given event type.
|
||||
|
||||
### `.emit(type[, data])`
|
||||
|
||||
Emits the given event with optional data.
|
||||
|
||||
```ts
|
||||
import { Emitter, TypedEvent } from 'rettime'
|
||||
|
||||
const emitter = new Emitter<{ hello: TypedEvent<string> }>()
|
||||
|
||||
emitter.on('hello', (event) => console.log(event.data))
|
||||
|
||||
emitter.emit(new TypedEvent('hello', 'John'))
|
||||
```
|
||||
|
||||
### `.emitAsPromise(type[, data])`
|
||||
|
||||
Emits the given event and returns a Promise that resolves with the returned data of all matching event listeners, or rejects whenever any of the matching event listeners throws an error.
|
||||
|
||||
```ts
|
||||
import { Emitter, TypedEvent } from 'rettime'
|
||||
|
||||
const emitter = new Emitter<{ hello: TypedEvent<number, Promise<number>> }>()
|
||||
|
||||
emitter.on('hello', async (event) => {
|
||||
await sleep(100)
|
||||
return event.data + 1
|
||||
})
|
||||
emitter.on('hello', async (event) => event.data + 2)
|
||||
|
||||
const values = await emitter.emitAsPromise(new TypedEvent('hello', { data: 1 }))
|
||||
// [2, 3]
|
||||
```
|
||||
|
||||
> Unlike `.emit()`, the `.emitAsPromise()` method _awaits asynchronous listeners_.
|
||||
|
||||
### `.emitAsGenerator(type[, data])`
|
||||
|
||||
Emits the given event and returns a generator function that exhausts all matching event listeners. Using a generator gives you granular control over what listeners are called.
|
||||
|
||||
```ts
|
||||
import { Emitter, TypedEvent } from 'rettime'
|
||||
|
||||
const emitter = new Emitter<{ hello: TypedEvent<string, number> }>()
|
||||
|
||||
emitter.on('hello', () => 1)
|
||||
emitter.on('hello', () => 2)
|
||||
|
||||
for (const listenerResult of emitter.emitAsGenerator(
|
||||
new TypedEvent('hello', { data: 'John' }),
|
||||
)) {
|
||||
// Stop event emission if a listener returns a particular value.
|
||||
if (listenerResult === 1) {
|
||||
break
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `.listeners([type])`
|
||||
|
||||
Returns the list of all event listeners matching the given event type. If no event `type` is provided, returns the list of all existing event listeners.
|
||||
|
||||
### `.listenerCount([type])`
|
||||
|
||||
Returns the number of the event listeners matching the given event type. If no event `type` is provided, returns the total number of existing listeners.
|
||||
|
||||
### `.removeListener(type, listener)`
|
||||
|
||||
Removes the event listener for the given event type.
|
||||
|
||||
### `.removeAllListeners([type])`
|
||||
|
||||
Removes all event listeners for the given event type. If no event `type` is provided, removes all existing event listeners.
|
||||
|
||||
## Types
|
||||
|
||||
This library also comes with a set of helper types to make your life easier.
|
||||
|
||||
### `Emitter.EventType`
|
||||
|
||||
Returns the `Event` type (or its subtype) representing the given listener.
|
||||
|
||||
```ts
|
||||
import { Emitter, TypedEvent } from 'rettime'
|
||||
|
||||
const emitter = new Emitter<{ greeting: TypedEvent<'john'> }>()
|
||||
type GreetingEvent = Emitter.EventType<typeof emitter, 'greeting'>
|
||||
// TypedEvent<'john'>
|
||||
```
|
||||
|
||||
### `Emitter.ListenerType`
|
||||
|
||||
Returns the type of the given event's listener.
|
||||
|
||||
```ts
|
||||
import { Emitter, TypedEvent } from 'rettime'
|
||||
|
||||
const emitter = new Emitter<{ greeting: TypedEvent<string, number[]> }>()
|
||||
type GreetingListener = Emitter.ListenerType<typeof emitter, 'greeting'>
|
||||
// (event: TypedEvent<string>) => number[]
|
||||
```
|
||||
|
||||
> The `ListenerType` helper is in itself type-safe, allowing only known event types as the second argument.
|
||||
|
||||
### `Emitter.ListenerReturnType`
|
||||
|
||||
Returns the return type of the given event's listener.
|
||||
|
||||
```ts
|
||||
import { Emitter, TypedEvent } from 'rettime'
|
||||
|
||||
const emitter = new Emitter<{ getTotalPrice: TypedEvent<Cart, number> }>()
|
||||
type CartTotal = Emitter.ListenerReturnType<typeof emitter, 'getTotalPrice'>
|
||||
// number
|
||||
```
|
||||
152
node_modules/rettime/build/index.d.mts
generated
vendored
Normal file
152
node_modules/rettime/build/index.d.mts
generated
vendored
Normal file
@@ -0,0 +1,152 @@
|
||||
//#region src/index.d.ts
|
||||
type DefaultEventMap = {
|
||||
[eventType: string]: TypedEvent<any, any>;
|
||||
};
|
||||
/**
|
||||
* Reserved event map containing special event types like '*' for catch-all listeners.
|
||||
*/
|
||||
type ReservedEventMap = {
|
||||
'*': TypedEvent<any, any, '*'>;
|
||||
};
|
||||
type IsReservedEvent<Type extends string> = Type extends keyof ReservedEventMap ? true : false;
|
||||
interface TypedEvent<DataType = void, ReturnType = void, EventType extends string = string> extends Omit<MessageEvent<DataType>, 'type'> {
|
||||
type: EventType;
|
||||
}
|
||||
declare const kDefaultPrevented: unique symbol;
|
||||
declare const kPropagationStopped: unique symbol;
|
||||
declare const kImmediatePropagationStopped: unique symbol;
|
||||
declare class TypedEvent<DataType = void, ReturnType = void, EventType extends string = string> extends MessageEvent<DataType> implements TypedEvent<DataType, ReturnType, EventType> {
|
||||
#private;
|
||||
[kDefaultPrevented]: boolean;
|
||||
[kPropagationStopped]?: Emitter<any>;
|
||||
[kImmediatePropagationStopped]?: boolean;
|
||||
constructor(...args: [DataType] extends [void] ? [type: EventType] : [type: EventType, init: {
|
||||
data: DataType;
|
||||
}]);
|
||||
get defaultPrevented(): boolean;
|
||||
preventDefault(): void;
|
||||
stopImmediatePropagation(): void;
|
||||
}
|
||||
/**
|
||||
* Brands a TypedEvent or its subclass while preserving its (narrower) type.
|
||||
*/
|
||||
type Brand<Event extends TypedEvent, EventType extends string, Loose extends boolean = false> = Loose extends true ? Event extends TypedEvent<infer Data, any, any> ?
|
||||
/**
|
||||
* @note Omit the `ReturnType` so emit methods can accept type events
|
||||
* where infering the return type is impossible.
|
||||
*/
|
||||
TypedEvent<Data, any, EventType> & {
|
||||
type: EventType;
|
||||
} : never : Event & {
|
||||
type: EventType;
|
||||
};
|
||||
type InferEventMap<Target extends Emitter<any>> = Target extends Emitter<infer EventMap> ? MergedEventMap<EventMap> : never;
|
||||
/**
|
||||
* Extracts only user-defined events, excluding reserved event types.
|
||||
*/
|
||||
type UserEventMap<EventMap$1 extends DefaultEventMap> = Omit<EventMap$1, keyof ReservedEventMap>;
|
||||
/**
|
||||
* Merges the user EventMap with the ReservedEventMap.
|
||||
* The '*' event type accepts a union of all user-defined events.
|
||||
*/
|
||||
type MergedEventMap<EventMap$1 extends DefaultEventMap> = EventMap$1 & ReservedEventMap;
|
||||
/**
|
||||
* Creates a union of all events in the EventMap with their literal type strings.
|
||||
*/
|
||||
type AllEvents<EventMap$1 extends DefaultEventMap> = { [K in keyof EventMap$1 & string]: Brand<EventMap$1[K], K> }[keyof EventMap$1 & string];
|
||||
type TypedListenerOptions = {
|
||||
once?: boolean;
|
||||
signal?: AbortSignal;
|
||||
};
|
||||
declare namespace Emitter {
|
||||
/**
|
||||
* Returns an appropriate `Event` type for the given event type.
|
||||
*
|
||||
* @example
|
||||
* const emitter = new Emitter<{ greeting: TypedEvent<string> }>()
|
||||
* type GreetingEvent = Emitter.InferEventType<typeof emitter, 'greeting'>
|
||||
* // TypedEvent<string>
|
||||
*/
|
||||
type EventType<Target extends Emitter<any>, EventType extends keyof EventMap$1 & string, EventMap$1 extends DefaultEventMap = InferEventMap<Target>> = IsReservedEvent<EventType> extends true ? AllEvents<UserEventMap<EventMap$1>> : Brand<EventMap$1[EventType], EventType>;
|
||||
type EventDataType<Target extends Emitter<any>, EventType extends keyof EventMap$1 & string, EventMap$1 extends DefaultEventMap = InferEventMap<Target>> = EventMap$1[EventType] extends TypedEvent<infer DataType> ? DataType : never;
|
||||
/**
|
||||
* Returns the listener type for the given event type.
|
||||
*
|
||||
* @example
|
||||
* const emitter = new Emitter<{ getTotalPrice: TypedEvent<Cart, number> }>()
|
||||
* type Listener = Emitter.ListenerType<typeof emitter, 'getTotalPrice'>
|
||||
* // (event: TypedEvent<Cart>) => number
|
||||
*/
|
||||
type ListenerType<Target extends Emitter<any>, EventType extends keyof EventMap$1 & string, EventMap$1 extends DefaultEventMap = InferEventMap<Target>> = IsReservedEvent<EventType> extends true ? (event: AllEvents<UserEventMap<EventMap$1>>) => void : (event: Emitter.EventType<Target, EventType, EventMap$1>) => Emitter.ListenerReturnType<Target, EventType, EventMap$1> extends [void] ? void : Emitter.ListenerReturnType<Target, EventType, EventMap$1>;
|
||||
/**
|
||||
* Returns the return type of the listener for the given event type.
|
||||
*
|
||||
* @example
|
||||
* const emitter = new Emitter<{ getTotalPrice: TypedEvent<Cart, number> }>()
|
||||
* type ListenerReturnType = Emitter.InferListenerReturnType<typeof emitter, 'getTotalPrice'>
|
||||
* // number
|
||||
*/
|
||||
type ListenerReturnType<Target extends Emitter<any>, EventType extends keyof EventMap$1 & string, EventMap$1 extends DefaultEventMap = InferEventMap<Target>> = IsReservedEvent<EventType> extends true ? void : EventMap$1[EventType] extends TypedEvent<unknown, infer ReturnType> ? ReturnType : never;
|
||||
}
|
||||
declare class Emitter<EventMap$1 extends DefaultEventMap> {
|
||||
#private;
|
||||
constructor();
|
||||
/**
|
||||
* Adds a listener for the given event type.
|
||||
*/
|
||||
on<EventType extends keyof MergedEventMap<EventMap$1> & string>(type: EventType, listener: Emitter.ListenerType<typeof this, EventType, MergedEventMap<EventMap$1>>, options?: TypedListenerOptions): typeof this;
|
||||
/**
|
||||
* Adds a one-time listener for the given event type.
|
||||
*/
|
||||
once<EventType extends keyof MergedEventMap<EventMap$1> & string>(type: EventType, listener: Emitter.ListenerType<typeof this, EventType, MergedEventMap<EventMap$1>>, options?: Omit<TypedListenerOptions, 'once'>): typeof this;
|
||||
/**
|
||||
* Prepends a listener for the given event type.
|
||||
*/
|
||||
earlyOn<EventType extends keyof MergedEventMap<EventMap$1> & string>(type: EventType, listener: Emitter.ListenerType<typeof this, EventType, MergedEventMap<EventMap$1>>, options?: TypedListenerOptions): typeof this;
|
||||
/**
|
||||
* Prepends a one-time listener for the given event type.
|
||||
*/
|
||||
earlyOnce<EventType extends keyof MergedEventMap<EventMap$1> & string>(type: EventType, listener: Emitter.ListenerType<typeof this, EventType, MergedEventMap<EventMap$1>>, options?: Omit<TypedListenerOptions, 'once'>): typeof this;
|
||||
/**
|
||||
* Emits the given typed event.
|
||||
*
|
||||
* @returns {boolean} Returns `true` if the event had any listeners, `false` otherwise.
|
||||
*/
|
||||
emit<EventType extends keyof EventMap$1 & string>(event: Brand<EventMap$1[EventType], EventType, true>): boolean;
|
||||
/**
|
||||
* Emits the given typed event and returns a promise that resolves
|
||||
* when all the listeners for that event have settled.
|
||||
*
|
||||
* @returns {Promise<Array<Emitter.ListenerReturnType>>} A promise that resolves
|
||||
* with the return values of all listeners.
|
||||
*/
|
||||
emitAsPromise<EventType extends keyof EventMap$1 & string>(event: Brand<EventMap$1[EventType], EventType, true>): Promise<Array<Emitter.ListenerReturnType<typeof this, EventType, EventMap$1>>>;
|
||||
/**
|
||||
* Emits the given event and returns a generator that yields
|
||||
* the result of each listener in the order of their registration.
|
||||
* This way, you stop exhausting the listeners once you get the expected value.
|
||||
*/
|
||||
emitAsGenerator<EventType extends keyof EventMap$1 & string>(event: Brand<EventMap$1[EventType], EventType, true>): Generator<Emitter.ListenerReturnType<typeof this, EventType, EventMap$1>>;
|
||||
/**
|
||||
* Removes a listener for the given event type.
|
||||
*/
|
||||
removeListener<EventType extends keyof MergedEventMap<EventMap$1> & string>(type: EventType, listener: Emitter.ListenerType<typeof this, EventType, MergedEventMap<EventMap$1>>): void;
|
||||
/**
|
||||
* Removes all listeners for the given event type.
|
||||
* If no event type is provided, removes all existing listeners.
|
||||
*/
|
||||
removeAllListeners<EventType extends keyof MergedEventMap<EventMap$1> & string>(type?: EventType): void;
|
||||
/**
|
||||
* Returns the list of listeners for the given event type.
|
||||
* If no even type is provided, returns all listeners.
|
||||
*/
|
||||
listeners<EventType extends keyof MergedEventMap<EventMap$1> & string>(type?: EventType): Array<Emitter.ListenerType<typeof this, EventType, MergedEventMap<EventMap$1>>>;
|
||||
/**
|
||||
* Returns the number of listeners for the given event type.
|
||||
* If no even type is provided, returns the total number of listeners.
|
||||
*/
|
||||
listenerCount<EventType extends keyof MergedEventMap<EventMap$1> & string>(type?: EventType): number;
|
||||
}
|
||||
//#endregion
|
||||
export { DefaultEventMap, Emitter, ReservedEventMap, TypedEvent, TypedListenerOptions };
|
||||
//# sourceMappingURL=index.d.mts.map
|
||||
225
node_modules/rettime/build/index.mjs
generated
vendored
Normal file
225
node_modules/rettime/build/index.mjs
generated
vendored
Normal file
@@ -0,0 +1,225 @@
|
||||
import { LensList } from "./lens-list.mjs";
|
||||
|
||||
//#region src/index.ts
|
||||
const kDefaultPrevented = Symbol("kDefaultPrevented");
|
||||
const kPropagationStopped = Symbol("kPropagationStopped");
|
||||
const kImmediatePropagationStopped = Symbol("kImmediatePropagationStopped");
|
||||
var TypedEvent = class extends MessageEvent {
|
||||
/**
|
||||
* @note Keep a placeholder property with the return type
|
||||
* because the type must be set somewhere in order to be
|
||||
* correctly associated and inferred from the event.
|
||||
*/
|
||||
#returnType;
|
||||
[kDefaultPrevented];
|
||||
[kPropagationStopped];
|
||||
[kImmediatePropagationStopped];
|
||||
constructor(...args) {
|
||||
super(args[0], args[1]);
|
||||
this[kDefaultPrevented] = false;
|
||||
}
|
||||
get defaultPrevented() {
|
||||
return this[kDefaultPrevented];
|
||||
}
|
||||
preventDefault() {
|
||||
super.preventDefault();
|
||||
this[kDefaultPrevented] = true;
|
||||
}
|
||||
stopImmediatePropagation() {
|
||||
/**
|
||||
* @note Despite `.stopPropagation()` and `.stopImmediatePropagation()` being defined
|
||||
* in Node.js, they do nothing. It is safe to re-define them.
|
||||
*/
|
||||
super.stopImmediatePropagation();
|
||||
this[kImmediatePropagationStopped] = true;
|
||||
}
|
||||
};
|
||||
const kListenerOptions = Symbol("kListenerOptions");
|
||||
var Emitter = class {
|
||||
#listeners;
|
||||
constructor() {
|
||||
this.#listeners = new LensList();
|
||||
}
|
||||
/**
|
||||
* Adds a listener for the given event type.
|
||||
*/
|
||||
on(type, listener, options) {
|
||||
this.#addListener(type, listener, options);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Adds a one-time listener for the given event type.
|
||||
*/
|
||||
once(type, listener, options) {
|
||||
return this.on(type, listener, {
|
||||
...options || {},
|
||||
once: true
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Prepends a listener for the given event type.
|
||||
*/
|
||||
earlyOn(type, listener, options) {
|
||||
this.#addListener(type, listener, options, "prepend");
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Prepends a one-time listener for the given event type.
|
||||
*/
|
||||
earlyOnce(type, listener, options) {
|
||||
return this.earlyOn(type, listener, {
|
||||
...options || {},
|
||||
once: true
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Emits the given typed event.
|
||||
*
|
||||
* @returns {boolean} Returns `true` if the event had any listeners, `false` otherwise.
|
||||
*/
|
||||
emit(event) {
|
||||
if (this.#listeners.size === 0) return false;
|
||||
/**
|
||||
* @note Calculate matching listeners before calling them
|
||||
* since one-time listeners will self-destruct.
|
||||
*/
|
||||
const hasListeners = this.listenerCount(event.type) > 0;
|
||||
const proxiedEvent = this.#proxyEvent(event);
|
||||
for (const listener of this.#matchListeners(event.type)) {
|
||||
if (proxiedEvent.event[kPropagationStopped] != null && proxiedEvent.event[kPropagationStopped] !== this) {
|
||||
proxiedEvent.revoke();
|
||||
return false;
|
||||
}
|
||||
if (proxiedEvent.event[kImmediatePropagationStopped]) break;
|
||||
this.#callListener(proxiedEvent.event, listener);
|
||||
}
|
||||
proxiedEvent.revoke();
|
||||
return hasListeners;
|
||||
}
|
||||
/**
|
||||
* Emits the given typed event and returns a promise that resolves
|
||||
* when all the listeners for that event have settled.
|
||||
*
|
||||
* @returns {Promise<Array<Emitter.ListenerReturnType>>} A promise that resolves
|
||||
* with the return values of all listeners.
|
||||
*/
|
||||
async emitAsPromise(event) {
|
||||
if (this.#listeners.size === 0) return [];
|
||||
const pendingListeners = [];
|
||||
const proxiedEvent = this.#proxyEvent(event);
|
||||
for (const listener of this.#matchListeners(event.type)) {
|
||||
if (proxiedEvent.event[kPropagationStopped] != null && proxiedEvent.event[kPropagationStopped] !== this) {
|
||||
proxiedEvent.revoke();
|
||||
return [];
|
||||
}
|
||||
if (proxiedEvent.event[kImmediatePropagationStopped]) break;
|
||||
const returnValue = await Promise.resolve(this.#callListener(proxiedEvent.event, listener));
|
||||
if (!this.#isTypelessListener(listener)) pendingListeners.push(returnValue);
|
||||
}
|
||||
proxiedEvent.revoke();
|
||||
return Promise.allSettled(pendingListeners).then((results) => {
|
||||
return results.map((result) => result.status === "fulfilled" ? result.value : result.reason);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Emits the given event and returns a generator that yields
|
||||
* the result of each listener in the order of their registration.
|
||||
* This way, you stop exhausting the listeners once you get the expected value.
|
||||
*/
|
||||
*emitAsGenerator(event) {
|
||||
if (this.#listeners.size === 0) return;
|
||||
const proxiedEvent = this.#proxyEvent(event);
|
||||
for (const listener of this.#matchListeners(event.type)) {
|
||||
if (proxiedEvent.event[kPropagationStopped] != null && proxiedEvent.event[kPropagationStopped] !== this) {
|
||||
proxiedEvent.revoke();
|
||||
return;
|
||||
}
|
||||
if (proxiedEvent.event[kImmediatePropagationStopped]) break;
|
||||
const returnValue = this.#callListener(proxiedEvent.event, listener);
|
||||
if (!this.#isTypelessListener(listener)) yield returnValue;
|
||||
}
|
||||
proxiedEvent.revoke();
|
||||
}
|
||||
/**
|
||||
* Removes a listener for the given event type.
|
||||
*/
|
||||
removeListener(type, listener) {
|
||||
this.#listeners.delete(type, listener);
|
||||
}
|
||||
/**
|
||||
* Removes all listeners for the given event type.
|
||||
* If no event type is provided, removes all existing listeners.
|
||||
*/
|
||||
removeAllListeners(type) {
|
||||
if (type == null) {
|
||||
this.#listeners.clear();
|
||||
return;
|
||||
}
|
||||
this.#listeners.deleteAll(type);
|
||||
}
|
||||
/**
|
||||
* Returns the list of listeners for the given event type.
|
||||
* If no even type is provided, returns all listeners.
|
||||
*/
|
||||
listeners(type) {
|
||||
if (type == null) return this.#listeners.getAll();
|
||||
return this.#listeners.get(type);
|
||||
}
|
||||
/**
|
||||
* Returns the number of listeners for the given event type.
|
||||
* If no even type is provided, returns the total number of listeners.
|
||||
*/
|
||||
listenerCount(type) {
|
||||
if (type == null) return this.#listeners.size;
|
||||
return this.listeners(type).length;
|
||||
}
|
||||
#addListener(type, listener, options, insertMode = "append") {
|
||||
if (insertMode === "prepend") this.#listeners.prepend(type, listener);
|
||||
else this.#listeners.append(type, listener);
|
||||
if (options) {
|
||||
Object.defineProperty(listener, kListenerOptions, {
|
||||
value: options,
|
||||
enumerable: false,
|
||||
writable: false
|
||||
});
|
||||
if (options.signal) options.signal.addEventListener("abort", () => {
|
||||
this.removeListener(type, listener);
|
||||
}, { once: true });
|
||||
}
|
||||
}
|
||||
#proxyEvent(event) {
|
||||
const { stopPropagation } = event;
|
||||
event.stopPropagation = new Proxy(event.stopPropagation, { apply: (target, thisArg, argArray) => {
|
||||
event[kPropagationStopped] = this;
|
||||
return Reflect.apply(target, thisArg, argArray);
|
||||
} });
|
||||
return {
|
||||
event,
|
||||
revoke() {
|
||||
event.stopPropagation = stopPropagation;
|
||||
}
|
||||
};
|
||||
}
|
||||
#callListener(event, listener) {
|
||||
const returnValue = listener.call(this, event);
|
||||
if (listener[kListenerOptions]?.once) {
|
||||
const key = this.#isTypelessListener(listener) ? "*" : event.type;
|
||||
this.#listeners.delete(key, listener);
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
/**
|
||||
* Return a list of all event listeners relevant for the given event type.
|
||||
* This includes the explicit event listeners and also typeless event listeners.
|
||||
*/
|
||||
*#matchListeners(type) {
|
||||
for (const [key, listener] of this.#listeners) if (key === "*" || key === type) yield listener;
|
||||
}
|
||||
#isTypelessListener(listener) {
|
||||
return this.#listeners.get("*").includes(listener);
|
||||
}
|
||||
};
|
||||
|
||||
//#endregion
|
||||
export { Emitter, TypedEvent };
|
||||
//# sourceMappingURL=index.mjs.map
|
||||
1
node_modules/rettime/build/index.mjs.map
generated
vendored
Normal file
1
node_modules/rettime/build/index.mjs.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
36
node_modules/rettime/build/lens-list.d.mts
generated
vendored
Normal file
36
node_modules/rettime/build/lens-list.d.mts
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
//#region src/lens-list.d.ts
|
||||
declare class LensList<T> {
|
||||
#private;
|
||||
constructor();
|
||||
get [Symbol.iterator](): any;
|
||||
entries(): MapIterator<[string, T[]]>;
|
||||
/**
|
||||
* Return an order-sensitive list of values by the given key.
|
||||
*/
|
||||
get(key: string): Array<T>;
|
||||
/**
|
||||
* Return an order-sensitive list of all values.
|
||||
*/
|
||||
getAll(): Array<T>;
|
||||
/**
|
||||
* Append a new value to the given key.
|
||||
*/
|
||||
append(key: string, value: T): void;
|
||||
/**
|
||||
* Prepend a new value to the given key.
|
||||
*/
|
||||
prepend(key: string, value: T): void;
|
||||
/**
|
||||
* Delete the value belonging to the given key.
|
||||
*/
|
||||
delete(key: string, value: T): void;
|
||||
/**
|
||||
* Delete all values belogning to the given key.
|
||||
*/
|
||||
deleteAll(key: string): void;
|
||||
get size(): number;
|
||||
clear(): void;
|
||||
}
|
||||
//#endregion
|
||||
export { LensList };
|
||||
//# sourceMappingURL=lens-list.d.mts.map
|
||||
72
node_modules/rettime/build/lens-list.mjs
generated
vendored
Normal file
72
node_modules/rettime/build/lens-list.mjs
generated
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
//#region src/lens-list.ts
|
||||
var LensList = class {
|
||||
#list;
|
||||
#lens;
|
||||
constructor() {
|
||||
this.#list = [];
|
||||
this.#lens = /* @__PURE__ */ new Map();
|
||||
}
|
||||
get [Symbol.iterator]() {
|
||||
return this.#list[Symbol.iterator].bind(this.#list);
|
||||
}
|
||||
entries() {
|
||||
return this.#lens.entries();
|
||||
}
|
||||
/**
|
||||
* Return an order-sensitive list of values by the given key.
|
||||
*/
|
||||
get(key) {
|
||||
return this.#lens.get(key) || [];
|
||||
}
|
||||
/**
|
||||
* Return an order-sensitive list of all values.
|
||||
*/
|
||||
getAll() {
|
||||
return this.#list.map(([, value]) => value);
|
||||
}
|
||||
/**
|
||||
* Append a new value to the given key.
|
||||
*/
|
||||
append(key, value) {
|
||||
this.#list.push([key, value]);
|
||||
this.#openLens(key, (list) => list.push(value));
|
||||
}
|
||||
/**
|
||||
* Prepend a new value to the given key.
|
||||
*/
|
||||
prepend(key, value) {
|
||||
this.#list.unshift([key, value]);
|
||||
this.#openLens(key, (list) => list.unshift(value));
|
||||
}
|
||||
/**
|
||||
* Delete the value belonging to the given key.
|
||||
*/
|
||||
delete(key, value) {
|
||||
if (this.size === 0) return;
|
||||
this.#list = this.#list.filter((item) => item[1] !== value);
|
||||
for (const [existingKey, values] of this.#lens) if (existingKey === key && values.includes(value)) values.splice(values.indexOf(value), 1);
|
||||
}
|
||||
/**
|
||||
* Delete all values belogning to the given key.
|
||||
*/
|
||||
deleteAll(key) {
|
||||
if (this.size === 0) return;
|
||||
this.#list = this.#list.filter((item) => item[0] !== key);
|
||||
this.#lens.delete(key);
|
||||
}
|
||||
get size() {
|
||||
return this.#list.length;
|
||||
}
|
||||
clear() {
|
||||
if (this.size === 0) return;
|
||||
this.#list.length = 0;
|
||||
this.#lens.clear();
|
||||
}
|
||||
#openLens(key, setter) {
|
||||
setter(this.#lens.get(key) || this.#lens.set(key, []).get(key));
|
||||
}
|
||||
};
|
||||
|
||||
//#endregion
|
||||
export { LensList };
|
||||
//# sourceMappingURL=lens-list.mjs.map
|
||||
1
node_modules/rettime/build/lens-list.mjs.map
generated
vendored
Normal file
1
node_modules/rettime/build/lens-list.mjs.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"lens-list.mjs","names":["#list","#lens","#openLens"],"sources":["../src/lens-list.ts"],"sourcesContent":["export class LensList<T> {\n #list: Array<[string, T]>\n #lens: Map<string, Array<T>>\n\n constructor() {\n this.#list = []\n this.#lens = new Map()\n }\n\n get [Symbol.iterator]() {\n // Return the list's iterator so iteration is order-sensitive.\n return this.#list[Symbol.iterator].bind(this.#list)\n }\n\n public entries() {\n return this.#lens.entries()\n }\n\n /**\n * Return an order-sensitive list of values by the given key.\n */\n public get(key: string): Array<T> {\n return this.#lens.get(key) || []\n }\n\n /**\n * Return an order-sensitive list of all values.\n */\n public getAll(): Array<T> {\n return this.#list.map(([, value]) => value)\n }\n\n /**\n * Append a new value to the given key.\n */\n public append(key: string, value: T): void {\n this.#list.push([key, value])\n this.#openLens(key, (list) => list.push(value))\n }\n\n /**\n * Prepend a new value to the given key.\n */\n public prepend(key: string, value: T): void {\n this.#list.unshift([key, value])\n this.#openLens(key, (list) => list.unshift(value))\n }\n\n /**\n * Delete the value belonging to the given key.\n */\n public delete(key: string, value: T): void {\n if (this.size === 0) {\n return\n }\n\n this.#list = this.#list.filter((item) => item[1] !== value)\n\n for (const [existingKey, values] of this.#lens) {\n if (existingKey === key && values.includes(value)) {\n values.splice(values.indexOf(value), 1)\n }\n }\n }\n\n /**\n * Delete all values belogning to the given key.\n */\n public deleteAll(key: string): void {\n if (this.size === 0) {\n return\n }\n\n this.#list = this.#list.filter((item) => item[0] !== key)\n this.#lens.delete(key)\n }\n\n get size(): number {\n return this.#list.length\n }\n\n public clear(): void {\n if (this.size === 0) {\n return\n }\n\n this.#list.length = 0\n this.#lens.clear()\n }\n\n #openLens(key: string, setter: (target: Array<T>) => void): void {\n setter(this.#lens.get(key) || this.#lens.set(key, []).get(key))\n }\n}\n"],"mappings":";AAAA,IAAa,WAAb,MAAyB;CACvB;CACA;CAEA,cAAc;AACZ,QAAKA,OAAQ,EAAE;AACf,QAAKC,uBAAQ,IAAI,KAAK;;CAGxB,KAAK,OAAO,YAAY;AAEtB,SAAO,MAAKD,KAAM,OAAO,UAAU,KAAK,MAAKA,KAAM;;CAGrD,AAAO,UAAU;AACf,SAAO,MAAKC,KAAM,SAAS;;;;;CAM7B,AAAO,IAAI,KAAuB;AAChC,SAAO,MAAKA,KAAM,IAAI,IAAI,IAAI,EAAE;;;;;CAMlC,AAAO,SAAmB;AACxB,SAAO,MAAKD,KAAM,KAAK,GAAG,WAAW,MAAM;;;;;CAM7C,AAAO,OAAO,KAAa,OAAgB;AACzC,QAAKA,KAAM,KAAK,CAAC,KAAK,MAAM,CAAC;AAC7B,QAAKE,SAAU,MAAM,SAAS,KAAK,KAAK,MAAM,CAAC;;;;;CAMjD,AAAO,QAAQ,KAAa,OAAgB;AAC1C,QAAKF,KAAM,QAAQ,CAAC,KAAK,MAAM,CAAC;AAChC,QAAKE,SAAU,MAAM,SAAS,KAAK,QAAQ,MAAM,CAAC;;;;;CAMpD,AAAO,OAAO,KAAa,OAAgB;AACzC,MAAI,KAAK,SAAS,EAChB;AAGF,QAAKF,OAAQ,MAAKA,KAAM,QAAQ,SAAS,KAAK,OAAO,MAAM;AAE3D,OAAK,MAAM,CAAC,aAAa,WAAW,MAAKC,KACvC,KAAI,gBAAgB,OAAO,OAAO,SAAS,MAAM,CAC/C,QAAO,OAAO,OAAO,QAAQ,MAAM,EAAE,EAAE;;;;;CAQ7C,AAAO,UAAU,KAAmB;AAClC,MAAI,KAAK,SAAS,EAChB;AAGF,QAAKD,OAAQ,MAAKA,KAAM,QAAQ,SAAS,KAAK,OAAO,IAAI;AACzD,QAAKC,KAAM,OAAO,IAAI;;CAGxB,IAAI,OAAe;AACjB,SAAO,MAAKD,KAAM;;CAGpB,AAAO,QAAc;AACnB,MAAI,KAAK,SAAS,EAChB;AAGF,QAAKA,KAAM,SAAS;AACpB,QAAKC,KAAM,OAAO;;CAGpB,UAAU,KAAa,QAA0C;AAC/D,SAAO,MAAKA,KAAM,IAAI,IAAI,IAAI,MAAKA,KAAM,IAAI,KAAK,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC"}
|
||||
48
node_modules/rettime/package.json
generated
vendored
Normal file
48
node_modules/rettime/package.json
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"type": "module",
|
||||
"name": "rettime",
|
||||
"version": "0.10.1",
|
||||
"description": "A type-safe marriage of `EventTarget` and `EventEmitter`.",
|
||||
"main": "./build/index.mjs",
|
||||
"types": "./build/index.d.mts",
|
||||
"files": [
|
||||
"build",
|
||||
"src"
|
||||
],
|
||||
"imports": {
|
||||
"#src/*": "./src/*"
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./build/index.d.mts",
|
||||
"default": "./build/index.mjs"
|
||||
}
|
||||
},
|
||||
"keywords": [
|
||||
"emitter",
|
||||
"event",
|
||||
"dispatcher",
|
||||
"type-safe",
|
||||
"strict",
|
||||
"event-target"
|
||||
],
|
||||
"author": "Artem Zakharchenko <kettanaito@gmail.com>",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"url": "https://github.com/kettanaito/rettime"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ossjs/release": "^0.10.1",
|
||||
"@rolldown/binding-darwin-arm64": "1.0.0-beta.40",
|
||||
"tsdown": "^0.19.0",
|
||||
"typescript": "^5.9.3",
|
||||
"vitest": "^3.2.4"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "tsdown -w",
|
||||
"test": "vitest",
|
||||
"benchmark": "vitest bench",
|
||||
"build": "tsdown",
|
||||
"release": "release publish"
|
||||
}
|
||||
}
|
||||
556
node_modules/rettime/src/index.ts
generated
vendored
Normal file
556
node_modules/rettime/src/index.ts
generated
vendored
Normal file
@@ -0,0 +1,556 @@
|
||||
import { LensList } from './lens-list'
|
||||
|
||||
export type DefaultEventMap = {
|
||||
[eventType: string]: TypedEvent<any, any>
|
||||
}
|
||||
|
||||
/**
|
||||
* Reserved event map containing special event types like '*' for catch-all listeners.
|
||||
*/
|
||||
export type ReservedEventMap = {
|
||||
'*': TypedEvent<any, any, '*'>
|
||||
}
|
||||
|
||||
type IsReservedEvent<Type extends string> = Type extends keyof ReservedEventMap
|
||||
? true
|
||||
: false
|
||||
|
||||
export interface TypedEvent<
|
||||
DataType = void,
|
||||
ReturnType = void,
|
||||
EventType extends string = string,
|
||||
> extends Omit<MessageEvent<DataType>, 'type'> {
|
||||
type: EventType
|
||||
}
|
||||
|
||||
const kDefaultPrevented = Symbol('kDefaultPrevented')
|
||||
const kPropagationStopped = Symbol('kPropagationStopped')
|
||||
const kImmediatePropagationStopped = Symbol('kImmediatePropagationStopped')
|
||||
|
||||
export class TypedEvent<
|
||||
DataType = void,
|
||||
ReturnType = void,
|
||||
EventType extends string = string,
|
||||
>
|
||||
extends MessageEvent<DataType>
|
||||
implements TypedEvent<DataType, ReturnType, EventType>
|
||||
{
|
||||
/**
|
||||
* @note Keep a placeholder property with the return type
|
||||
* because the type must be set somewhere in order to be
|
||||
* correctly associated and inferred from the event.
|
||||
*/
|
||||
#returnType: ReturnType;
|
||||
|
||||
[kDefaultPrevented]: boolean;
|
||||
[kPropagationStopped]?: Emitter<any>;
|
||||
[kImmediatePropagationStopped]?: boolean
|
||||
|
||||
constructor(
|
||||
...args: [DataType] extends [void]
|
||||
? [type: EventType]
|
||||
: [type: EventType, init: { data: DataType }]
|
||||
) {
|
||||
super(args[0], args[1])
|
||||
this[kDefaultPrevented] = false
|
||||
}
|
||||
|
||||
get defaultPrevented(): boolean {
|
||||
return this[kDefaultPrevented]
|
||||
}
|
||||
|
||||
public preventDefault(): void {
|
||||
super.preventDefault()
|
||||
this[kDefaultPrevented] = true
|
||||
}
|
||||
|
||||
public stopImmediatePropagation(): void {
|
||||
/**
|
||||
* @note Despite `.stopPropagation()` and `.stopImmediatePropagation()` being defined
|
||||
* in Node.js, they do nothing. It is safe to re-define them.
|
||||
*/
|
||||
super.stopImmediatePropagation()
|
||||
this[kImmediatePropagationStopped] = true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Brands a TypedEvent or its subclass while preserving its (narrower) type.
|
||||
*/
|
||||
type Brand<
|
||||
Event extends TypedEvent,
|
||||
EventType extends string,
|
||||
Loose extends boolean = false,
|
||||
> = Loose extends true
|
||||
? Event extends TypedEvent<infer Data, any, any>
|
||||
? /**
|
||||
* @note Omit the `ReturnType` so emit methods can accept type events
|
||||
* where infering the return type is impossible.
|
||||
*/
|
||||
TypedEvent<Data, any, EventType> & {
|
||||
type: EventType
|
||||
}
|
||||
: never
|
||||
: Event & { type: EventType }
|
||||
|
||||
type InferEventMap<Target extends Emitter<any>> =
|
||||
Target extends Emitter<infer EventMap> ? MergedEventMap<EventMap> : never
|
||||
|
||||
/**
|
||||
* Extracts only user-defined events, excluding reserved event types.
|
||||
*/
|
||||
type UserEventMap<EventMap extends DefaultEventMap> = Omit<
|
||||
EventMap,
|
||||
keyof ReservedEventMap
|
||||
>
|
||||
|
||||
/**
|
||||
* Merges the user EventMap with the ReservedEventMap.
|
||||
* The '*' event type accepts a union of all user-defined events.
|
||||
*/
|
||||
type MergedEventMap<EventMap extends DefaultEventMap> = EventMap &
|
||||
ReservedEventMap
|
||||
|
||||
/**
|
||||
* Creates a union of all events in the EventMap with their literal type strings.
|
||||
*/
|
||||
type AllEvents<EventMap extends DefaultEventMap> = {
|
||||
[K in keyof EventMap & string]: Brand<EventMap[K], K>
|
||||
}[keyof EventMap & string]
|
||||
|
||||
export type TypedListenerOptions = {
|
||||
once?: boolean
|
||||
signal?: AbortSignal
|
||||
}
|
||||
|
||||
const kListenerOptions = Symbol('kListenerOptions')
|
||||
|
||||
export namespace Emitter {
|
||||
/**
|
||||
* Returns an appropriate `Event` type for the given event type.
|
||||
*
|
||||
* @example
|
||||
* const emitter = new Emitter<{ greeting: TypedEvent<string> }>()
|
||||
* type GreetingEvent = Emitter.InferEventType<typeof emitter, 'greeting'>
|
||||
* // TypedEvent<string>
|
||||
*/
|
||||
export type EventType<
|
||||
Target extends Emitter<any>,
|
||||
EventType extends keyof EventMap & string,
|
||||
EventMap extends DefaultEventMap = InferEventMap<Target>,
|
||||
> =
|
||||
IsReservedEvent<EventType> extends true
|
||||
? AllEvents<UserEventMap<EventMap>>
|
||||
: Brand<EventMap[EventType], EventType>
|
||||
|
||||
export type EventDataType<
|
||||
Target extends Emitter<any>,
|
||||
EventType extends keyof EventMap & string,
|
||||
EventMap extends DefaultEventMap = InferEventMap<Target>,
|
||||
> = EventMap[EventType] extends TypedEvent<infer DataType> ? DataType : never
|
||||
|
||||
/**
|
||||
* Returns the listener type for the given event type.
|
||||
*
|
||||
* @example
|
||||
* const emitter = new Emitter<{ getTotalPrice: TypedEvent<Cart, number> }>()
|
||||
* type Listener = Emitter.ListenerType<typeof emitter, 'getTotalPrice'>
|
||||
* // (event: TypedEvent<Cart>) => number
|
||||
*/
|
||||
export type ListenerType<
|
||||
Target extends Emitter<any>,
|
||||
EventType extends keyof EventMap & string,
|
||||
EventMap extends DefaultEventMap = InferEventMap<Target>,
|
||||
> =
|
||||
IsReservedEvent<EventType> extends true
|
||||
? (event: AllEvents<UserEventMap<EventMap>>) => void
|
||||
: (
|
||||
event: Emitter.EventType<Target, EventType, EventMap>,
|
||||
) => Emitter.ListenerReturnType<Target, EventType, EventMap> extends [
|
||||
void,
|
||||
]
|
||||
? void
|
||||
: Emitter.ListenerReturnType<Target, EventType, EventMap>
|
||||
|
||||
/**
|
||||
* Returns the return type of the listener for the given event type.
|
||||
*
|
||||
* @example
|
||||
* const emitter = new Emitter<{ getTotalPrice: TypedEvent<Cart, number> }>()
|
||||
* type ListenerReturnType = Emitter.InferListenerReturnType<typeof emitter, 'getTotalPrice'>
|
||||
* // number
|
||||
*/
|
||||
export type ListenerReturnType<
|
||||
Target extends Emitter<any>,
|
||||
EventType extends keyof EventMap & string,
|
||||
EventMap extends DefaultEventMap = InferEventMap<Target>,
|
||||
> =
|
||||
IsReservedEvent<EventType> extends true
|
||||
? void
|
||||
: EventMap[EventType] extends TypedEvent<unknown, infer ReturnType>
|
||||
? ReturnType
|
||||
: never
|
||||
}
|
||||
|
||||
export class Emitter<EventMap extends DefaultEventMap> {
|
||||
#listeners: LensList<
|
||||
Emitter.ListenerType<
|
||||
typeof this,
|
||||
keyof MergedEventMap<EventMap> & string,
|
||||
MergedEventMap<EventMap>
|
||||
>
|
||||
>
|
||||
|
||||
constructor() {
|
||||
this.#listeners = new LensList()
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener for the given event type.
|
||||
*/
|
||||
public on<EventType extends keyof MergedEventMap<EventMap> & string>(
|
||||
type: EventType,
|
||||
listener: Emitter.ListenerType<
|
||||
typeof this,
|
||||
EventType,
|
||||
MergedEventMap<EventMap>
|
||||
>,
|
||||
options?: TypedListenerOptions,
|
||||
): typeof this {
|
||||
this.#addListener(type, listener, options)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a one-time listener for the given event type.
|
||||
*/
|
||||
public once<EventType extends keyof MergedEventMap<EventMap> & string>(
|
||||
type: EventType,
|
||||
listener: Emitter.ListenerType<
|
||||
typeof this,
|
||||
EventType,
|
||||
MergedEventMap<EventMap>
|
||||
>,
|
||||
options?: Omit<TypedListenerOptions, 'once'>,
|
||||
): typeof this {
|
||||
return this.on(type, listener, {
|
||||
...(options || {}),
|
||||
once: true,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepends a listener for the given event type.
|
||||
*/
|
||||
public earlyOn<EventType extends keyof MergedEventMap<EventMap> & string>(
|
||||
type: EventType,
|
||||
listener: Emitter.ListenerType<
|
||||
typeof this,
|
||||
EventType,
|
||||
MergedEventMap<EventMap>
|
||||
>,
|
||||
options?: TypedListenerOptions,
|
||||
): typeof this {
|
||||
this.#addListener(type, listener, options, 'prepend')
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepends a one-time listener for the given event type.
|
||||
*/
|
||||
public earlyOnce<EventType extends keyof MergedEventMap<EventMap> & string>(
|
||||
type: EventType,
|
||||
listener: Emitter.ListenerType<
|
||||
typeof this,
|
||||
EventType,
|
||||
MergedEventMap<EventMap>
|
||||
>,
|
||||
options?: Omit<TypedListenerOptions, 'once'>,
|
||||
): typeof this {
|
||||
return this.earlyOn(type, listener, {
|
||||
...(options || {}),
|
||||
once: true,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits the given typed event.
|
||||
*
|
||||
* @returns {boolean} Returns `true` if the event had any listeners, `false` otherwise.
|
||||
*/
|
||||
public emit<EventType extends keyof EventMap & string>(
|
||||
event: Brand<EventMap[EventType], EventType, true>,
|
||||
): boolean {
|
||||
if (this.#listeners.size === 0) {
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* @note Calculate matching listeners before calling them
|
||||
* since one-time listeners will self-destruct.
|
||||
*/
|
||||
const hasListeners = this.listenerCount(event.type) > 0
|
||||
|
||||
const proxiedEvent = this.#proxyEvent(event)
|
||||
|
||||
for (const listener of this.#matchListeners(event.type)) {
|
||||
if (
|
||||
proxiedEvent.event[kPropagationStopped] != null &&
|
||||
proxiedEvent.event[kPropagationStopped] !== this
|
||||
) {
|
||||
proxiedEvent.revoke()
|
||||
return false
|
||||
}
|
||||
|
||||
if (proxiedEvent.event[kImmediatePropagationStopped]) {
|
||||
break
|
||||
}
|
||||
|
||||
this.#callListener(proxiedEvent.event, listener)
|
||||
}
|
||||
|
||||
proxiedEvent.revoke()
|
||||
|
||||
return hasListeners
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits the given typed event and returns a promise that resolves
|
||||
* when all the listeners for that event have settled.
|
||||
*
|
||||
* @returns {Promise<Array<Emitter.ListenerReturnType>>} A promise that resolves
|
||||
* with the return values of all listeners.
|
||||
*/
|
||||
public async emitAsPromise<EventType extends keyof EventMap & string>(
|
||||
event: Brand<EventMap[EventType], EventType, true>,
|
||||
): Promise<
|
||||
Array<Emitter.ListenerReturnType<typeof this, EventType, EventMap>>
|
||||
> {
|
||||
if (this.#listeners.size === 0) {
|
||||
return []
|
||||
}
|
||||
|
||||
const pendingListeners: Array<
|
||||
Promise<Emitter.ListenerReturnType<typeof this, EventType, EventMap>>
|
||||
> = []
|
||||
|
||||
const proxiedEvent = this.#proxyEvent(event)
|
||||
|
||||
for (const listener of this.#matchListeners(event.type)) {
|
||||
if (
|
||||
proxiedEvent.event[kPropagationStopped] != null &&
|
||||
proxiedEvent.event[kPropagationStopped] !== this
|
||||
) {
|
||||
proxiedEvent.revoke()
|
||||
return []
|
||||
}
|
||||
|
||||
if (proxiedEvent.event[kImmediatePropagationStopped]) {
|
||||
break
|
||||
}
|
||||
|
||||
const listenerPromise = Promise.resolve(
|
||||
this.#callListener(proxiedEvent.event, listener),
|
||||
)
|
||||
|
||||
const returnValue = await listenerPromise
|
||||
|
||||
if (!this.#isTypelessListener(listener)) {
|
||||
pendingListeners.push(returnValue)
|
||||
}
|
||||
}
|
||||
|
||||
proxiedEvent.revoke()
|
||||
|
||||
return Promise.allSettled(pendingListeners).then((results) => {
|
||||
return results.map((result) =>
|
||||
result.status === 'fulfilled' ? result.value : result.reason,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits the given event and returns a generator that yields
|
||||
* the result of each listener in the order of their registration.
|
||||
* This way, you stop exhausting the listeners once you get the expected value.
|
||||
*/
|
||||
public *emitAsGenerator<EventType extends keyof EventMap & string>(
|
||||
event: Brand<EventMap[EventType], EventType, true>,
|
||||
): Generator<Emitter.ListenerReturnType<typeof this, EventType, EventMap>> {
|
||||
if (this.#listeners.size === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
const proxiedEvent = this.#proxyEvent(event)
|
||||
|
||||
for (const listener of this.#matchListeners(event.type)) {
|
||||
if (
|
||||
proxiedEvent.event[kPropagationStopped] != null &&
|
||||
proxiedEvent.event[kPropagationStopped] !== this
|
||||
) {
|
||||
proxiedEvent.revoke()
|
||||
return
|
||||
}
|
||||
|
||||
if (proxiedEvent.event[kImmediatePropagationStopped]) {
|
||||
break
|
||||
}
|
||||
|
||||
const returnValue = this.#callListener(proxiedEvent.event, listener)
|
||||
|
||||
if (!this.#isTypelessListener(listener)) {
|
||||
yield returnValue
|
||||
}
|
||||
}
|
||||
|
||||
proxiedEvent.revoke()
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a listener for the given event type.
|
||||
*/
|
||||
public removeListener<
|
||||
EventType extends keyof MergedEventMap<EventMap> & string,
|
||||
>(
|
||||
type: EventType,
|
||||
listener: Emitter.ListenerType<
|
||||
typeof this,
|
||||
EventType,
|
||||
MergedEventMap<EventMap>
|
||||
>,
|
||||
): void {
|
||||
this.#listeners.delete(type, listener)
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all listeners for the given event type.
|
||||
* If no event type is provided, removes all existing listeners.
|
||||
*/
|
||||
public removeAllListeners<
|
||||
EventType extends keyof MergedEventMap<EventMap> & string,
|
||||
>(type?: EventType): void {
|
||||
if (type == null) {
|
||||
this.#listeners.clear()
|
||||
return
|
||||
}
|
||||
|
||||
this.#listeners.deleteAll(type)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of listeners for the given event type.
|
||||
* If no even type is provided, returns all listeners.
|
||||
*/
|
||||
public listeners<EventType extends keyof MergedEventMap<EventMap> & string>(
|
||||
type?: EventType,
|
||||
): Array<
|
||||
Emitter.ListenerType<typeof this, EventType, MergedEventMap<EventMap>>
|
||||
> {
|
||||
if (type == null) {
|
||||
return this.#listeners.getAll()
|
||||
}
|
||||
|
||||
return this.#listeners.get(type)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of listeners for the given event type.
|
||||
* If no even type is provided, returns the total number of listeners.
|
||||
*/
|
||||
public listenerCount<
|
||||
EventType extends keyof MergedEventMap<EventMap> & string,
|
||||
>(type?: EventType): number {
|
||||
if (type == null) {
|
||||
return this.#listeners.size
|
||||
}
|
||||
|
||||
return this.listeners(type).length
|
||||
}
|
||||
|
||||
#addListener<EventType extends keyof MergedEventMap<EventMap> & string>(
|
||||
type: EventType,
|
||||
listener: Emitter.ListenerType<
|
||||
typeof this,
|
||||
EventType,
|
||||
MergedEventMap<EventMap>
|
||||
>,
|
||||
options: TypedListenerOptions | undefined,
|
||||
insertMode: 'append' | 'prepend' = 'append',
|
||||
): void {
|
||||
if (insertMode === 'prepend') {
|
||||
this.#listeners.prepend(type, listener)
|
||||
} else {
|
||||
this.#listeners.append(type, listener)
|
||||
}
|
||||
|
||||
if (options) {
|
||||
Object.defineProperty(listener, kListenerOptions, {
|
||||
value: options,
|
||||
enumerable: false,
|
||||
writable: false,
|
||||
})
|
||||
|
||||
if (options.signal) {
|
||||
options.signal.addEventListener(
|
||||
'abort',
|
||||
() => {
|
||||
this.removeListener(type, listener)
|
||||
},
|
||||
{ once: true },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#proxyEvent<Event extends TypedEvent>(
|
||||
event: Event,
|
||||
): { event: Event; revoke: () => void } {
|
||||
const { stopPropagation } = event
|
||||
|
||||
event.stopPropagation = new Proxy(event.stopPropagation, {
|
||||
apply: (target, thisArg, argArray) => {
|
||||
event[kPropagationStopped] = this
|
||||
return Reflect.apply(target, thisArg, argArray)
|
||||
},
|
||||
})
|
||||
|
||||
return {
|
||||
event,
|
||||
revoke() {
|
||||
event.stopPropagation = stopPropagation
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#callListener(
|
||||
event: Event,
|
||||
listener: ((event: any) => any) & {
|
||||
[kListenerOptions]?: TypedListenerOptions
|
||||
},
|
||||
) {
|
||||
const returnValue = listener.call(this, event)
|
||||
|
||||
if (listener[kListenerOptions]?.once) {
|
||||
const key = this.#isTypelessListener(listener) ? '*' : event.type
|
||||
this.#listeners.delete(key, listener)
|
||||
}
|
||||
|
||||
return returnValue
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of all event listeners relevant for the given event type.
|
||||
* This includes the explicit event listeners and also typeless event listeners.
|
||||
*/
|
||||
*#matchListeners<EventType extends keyof EventMap & string>(type: EventType) {
|
||||
for (const [key, listener] of this.#listeners) {
|
||||
if (key === '*' || key === type) {
|
||||
yield listener
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#isTypelessListener(listener: any): boolean {
|
||||
return this.#listeners.get('*').includes(listener)
|
||||
}
|
||||
}
|
||||
94
node_modules/rettime/src/lens-list.ts
generated
vendored
Normal file
94
node_modules/rettime/src/lens-list.ts
generated
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
export class LensList<T> {
|
||||
#list: Array<[string, T]>
|
||||
#lens: Map<string, Array<T>>
|
||||
|
||||
constructor() {
|
||||
this.#list = []
|
||||
this.#lens = new Map()
|
||||
}
|
||||
|
||||
get [Symbol.iterator]() {
|
||||
// Return the list's iterator so iteration is order-sensitive.
|
||||
return this.#list[Symbol.iterator].bind(this.#list)
|
||||
}
|
||||
|
||||
public entries() {
|
||||
return this.#lens.entries()
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an order-sensitive list of values by the given key.
|
||||
*/
|
||||
public get(key: string): Array<T> {
|
||||
return this.#lens.get(key) || []
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an order-sensitive list of all values.
|
||||
*/
|
||||
public getAll(): Array<T> {
|
||||
return this.#list.map(([, value]) => value)
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a new value to the given key.
|
||||
*/
|
||||
public append(key: string, value: T): void {
|
||||
this.#list.push([key, value])
|
||||
this.#openLens(key, (list) => list.push(value))
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepend a new value to the given key.
|
||||
*/
|
||||
public prepend(key: string, value: T): void {
|
||||
this.#list.unshift([key, value])
|
||||
this.#openLens(key, (list) => list.unshift(value))
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the value belonging to the given key.
|
||||
*/
|
||||
public delete(key: string, value: T): void {
|
||||
if (this.size === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
this.#list = this.#list.filter((item) => item[1] !== value)
|
||||
|
||||
for (const [existingKey, values] of this.#lens) {
|
||||
if (existingKey === key && values.includes(value)) {
|
||||
values.splice(values.indexOf(value), 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all values belogning to the given key.
|
||||
*/
|
||||
public deleteAll(key: string): void {
|
||||
if (this.size === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
this.#list = this.#list.filter((item) => item[0] !== key)
|
||||
this.#lens.delete(key)
|
||||
}
|
||||
|
||||
get size(): number {
|
||||
return this.#list.length
|
||||
}
|
||||
|
||||
public clear(): void {
|
||||
if (this.size === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
this.#list.length = 0
|
||||
this.#lens.clear()
|
||||
}
|
||||
|
||||
#openLens(key: string, setter: (target: Array<T>) => void): void {
|
||||
setter(this.#lens.get(key) || this.#lens.set(key, []).get(key))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user