@cityssm/unique-timed-entry-queue
Advanced tools
+17
-0
@@ -0,1 +1,3 @@ | ||
| export declare const eventTypes: readonly ["enqueue"]; | ||
| export type EventType = (typeof eventTypes)[number]; | ||
| /** | ||
@@ -6,2 +8,3 @@ * A queue that enqueues unique entries after a specified delay. | ||
| private readonly enqueueDelayMilliseconds; | ||
| private readonly eventListeners; | ||
| private readonly pendingEntries; | ||
@@ -15,2 +18,9 @@ private readonly queue; | ||
| /** | ||
| * Adds an event listener for the specified event type. | ||
| * @param eventType - The event type to listen for. | ||
| * @param listener - The listener function to call when the event occurs. | ||
| * @returns A unique ID for the listener. | ||
| */ | ||
| addEventListener(eventType: EventType, listener: (entry: T) => void): string; | ||
| /** | ||
| * Clears all entries from the queue. | ||
@@ -89,2 +99,8 @@ */ | ||
| /** | ||
| * Removes an event listener. | ||
| * @param eventType - The event type. | ||
| * @param listenerId - The unique ID of the listener to remove. | ||
| */ | ||
| removeEventListener(eventType: EventType, listenerId: string): void; | ||
| /** | ||
| * Gets the size of the queue. | ||
@@ -99,2 +115,3 @@ * @returns The number of entries in the queue. | ||
| toArray(): T[]; | ||
| private triggerEvents; | ||
| } |
+42
-2
| import Debug from 'debug'; | ||
| import exitHook from 'exit-hook'; | ||
| import { DEBUG_NAMESPACE } from './debug.config.js'; | ||
| import { valueToString } from './utilities.js'; | ||
| import { generateUniqueListenerId, valueToString } from './utilities.js'; | ||
| const debug = Debug(`${DEBUG_NAMESPACE}:index`); | ||
| export const eventTypes = ['enqueue']; | ||
| /** | ||
@@ -11,2 +12,3 @@ * A queue that enqueues unique entries after a specified delay. | ||
| enqueueDelayMilliseconds; | ||
| eventListeners; | ||
| pendingEntries; | ||
@@ -24,2 +26,5 @@ queue; | ||
| } | ||
| this.eventListeners = { | ||
| enqueue: {} | ||
| }; | ||
| this.pendingEntries = new Map(); | ||
@@ -33,2 +38,14 @@ this.queue = []; | ||
| /** | ||
| * Adds an event listener for the specified event type. | ||
| * @param eventType - The event type to listen for. | ||
| * @param listener - The listener function to call when the event occurs. | ||
| * @returns A unique ID for the listener. | ||
| */ | ||
| addEventListener(eventType, listener) { | ||
| const listenerId = generateUniqueListenerId(); | ||
| // eslint-disable-next-line security/detect-object-injection | ||
| this.eventListeners[eventType][listenerId] = listener; | ||
| return listenerId; | ||
| } | ||
| /** | ||
| * Clears all entries from the queue. | ||
@@ -90,2 +107,3 @@ */ | ||
| this.queue.push(entry); | ||
| this.triggerEvents('enqueue', entry); | ||
| debug(`Enqueued entry immediately (zero delay): ${valueToString(entry)}`); | ||
@@ -96,5 +114,6 @@ return; | ||
| const timeout = setTimeout(() => { | ||
| this.pendingEntries.delete(stringEntry); | ||
| this.queue.push(entry); | ||
| this.triggerEvents('enqueue', entry); | ||
| debug(`Enqueued entry: ${stringEntry}`); | ||
| this.pendingEntries.delete(stringEntry); | ||
| }, delay); | ||
@@ -130,2 +149,3 @@ this.pendingEntries.set(stringEntry, { timeout, value: entry }); | ||
| this.queue.push(pendingEntry.value); | ||
| this.triggerEvents('enqueue', pendingEntry.value); | ||
| debug(`Enqueued pending entry immediately: ${stringValue}`); | ||
@@ -176,2 +196,16 @@ } | ||
| /** | ||
| * Removes an event listener. | ||
| * @param eventType - The event type. | ||
| * @param listenerId - The unique ID of the listener to remove. | ||
| */ | ||
| removeEventListener(eventType, listenerId) { | ||
| // eslint-disable-next-line security/detect-object-injection | ||
| const listeners = this.eventListeners[eventType]; | ||
| // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition | ||
| if (listeners !== undefined && Object.hasOwn(listeners, listenerId)) { | ||
| // eslint-disable-next-line security/detect-object-injection, @typescript-eslint/no-dynamic-delete | ||
| delete listeners[listenerId]; | ||
| } | ||
| } | ||
| /** | ||
| * Gets the size of the queue. | ||
@@ -190,2 +224,8 @@ * @returns The number of entries in the queue. | ||
| } | ||
| triggerEvents(eventType, entry) { | ||
| // eslint-disable-next-line security/detect-object-injection | ||
| for (const listener of Object.values(this.eventListeners[eventType])) { | ||
| listener(entry); | ||
| } | ||
| } | ||
| } |
+61
-2
@@ -5,6 +5,10 @@ import Debug from 'debug' | ||
| import { DEBUG_NAMESPACE } from './debug.config.js' | ||
| import { valueToString } from './utilities.js' | ||
| import { generateUniqueListenerId, valueToString } from './utilities.js' | ||
| const debug = Debug(`${DEBUG_NAMESPACE}:index`) | ||
| export const eventTypes = ['enqueue'] as const | ||
| export type EventType = (typeof eventTypes)[number] | ||
| /** | ||
@@ -15,2 +19,8 @@ * A queue that enqueues unique entries after a specified delay. | ||
| private readonly enqueueDelayMilliseconds: number | ||
| private readonly eventListeners: Record< | ||
| EventType, | ||
| Record<string, (entry: T) => void> | ||
| > | ||
| private readonly pendingEntries: Map< | ||
@@ -23,2 +33,3 @@ string, | ||
| > | ||
| private readonly queue: T[] | ||
@@ -40,2 +51,6 @@ | ||
| this.eventListeners = { | ||
| enqueue: {} | ||
| } | ||
| this.pendingEntries = new Map() | ||
@@ -52,2 +67,17 @@ | ||
| /** | ||
| * Adds an event listener for the specified event type. | ||
| * @param eventType - The event type to listen for. | ||
| * @param listener - The listener function to call when the event occurs. | ||
| * @returns A unique ID for the listener. | ||
| */ | ||
| public addEventListener(eventType: EventType, listener: (entry: T) => void): string { | ||
| const listenerId = generateUniqueListenerId() | ||
| // eslint-disable-next-line security/detect-object-injection | ||
| this.eventListeners[eventType][listenerId] = listener | ||
| return listenerId | ||
| } | ||
| /** | ||
| * Clears all entries from the queue. | ||
@@ -119,2 +149,4 @@ */ | ||
| this.queue.push(entry) | ||
| this.triggerEvents('enqueue', entry) | ||
| debug(`Enqueued entry immediately (zero delay): ${valueToString(entry)}`) | ||
@@ -127,5 +159,8 @@ return | ||
| const timeout = setTimeout(() => { | ||
| this.pendingEntries.delete(stringEntry) | ||
| this.queue.push(entry) | ||
| this.triggerEvents('enqueue', entry) | ||
| debug(`Enqueued entry: ${stringEntry}`) | ||
| this.pendingEntries.delete(stringEntry) | ||
| }, delay) | ||
@@ -170,2 +205,3 @@ | ||
| this.queue.push(pendingEntry.value) | ||
| this.triggerEvents('enqueue', pendingEntry.value) | ||
@@ -225,2 +261,18 @@ debug(`Enqueued pending entry immediately: ${stringValue}`) | ||
| /** | ||
| * Removes an event listener. | ||
| * @param eventType - The event type. | ||
| * @param listenerId - The unique ID of the listener to remove. | ||
| */ | ||
| public removeEventListener(eventType: EventType, listenerId: string): void { | ||
| // eslint-disable-next-line security/detect-object-injection | ||
| const listeners = this.eventListeners[eventType] | ||
| // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition | ||
| if (listeners !== undefined && Object.hasOwn(listeners, listenerId)) { | ||
| // eslint-disable-next-line security/detect-object-injection, @typescript-eslint/no-dynamic-delete | ||
| delete listeners[listenerId] | ||
| } | ||
| } | ||
| /** | ||
| * Gets the size of the queue. | ||
@@ -240,2 +292,9 @@ * @returns The number of entries in the queue. | ||
| } | ||
| private triggerEvents(eventType: EventType, entry: T): void { | ||
| // eslint-disable-next-line security/detect-object-injection | ||
| for (const listener of Object.values(this.eventListeners[eventType])) { | ||
| listener(entry) | ||
| } | ||
| } | ||
| } |
+2
-2
| { | ||
| "name": "@cityssm/unique-timed-entry-queue", | ||
| "version": "0.2.0", | ||
| "version": "0.3.0", | ||
| "description": "A queue with delayed enqueue of unique entries, perfect for queuing update notifications.", | ||
@@ -34,3 +34,3 @@ "keywords": [ | ||
| "@types/debug": "^4.1.12", | ||
| "@types/node": "^25.0.8", | ||
| "@types/node": "^25.0.9", | ||
| "eslint-config-cityssm": "^36.1.0", | ||
@@ -37,0 +37,0 @@ "prettier-config-cityssm": "^1.0.0" |
+11
-0
@@ -90,2 +90,13 @@ # Unique, Timed-Entry Queue for Node | ||
| ### Event Listeners | ||
| Right now, only one event is available. | ||
| `addEventListener('enqueue', (entry) => {})`<br /> | ||
| Adds an event listener that is called when the entry moves to the queue from pending. | ||
| Returns an event listener id that can be used to remove the event listener. | ||
| `removeEventListener('enqueue', eventListenerId)`<br /> | ||
| Removes an event listener. | ||
| ### Export Functions | ||
@@ -92,0 +103,0 @@ |
+5
-0
@@ -7,1 +7,6 @@ /** | ||
| export declare function valueToString(value: unknown): string; | ||
| /** | ||
| * Generates a unique listener ID. | ||
| * @returns A unique string identifier. | ||
| */ | ||
| export declare function generateUniqueListenerId(): string; |
+11
-0
@@ -0,1 +1,3 @@ | ||
| // eslint-disable-next-line @eslint-community/eslint-comments/disable-enable-pair | ||
| /* eslint-disable sonarjs/pseudo-random */ | ||
| /** | ||
@@ -22,1 +24,10 @@ * Converts a value to its string representation. | ||
| } | ||
| /** | ||
| * Generates a unique listener ID. | ||
| * @returns A unique string identifier. | ||
| */ | ||
| export function generateUniqueListenerId() { | ||
| return (Date.now().toString(36) + | ||
| Math.random().toString(36).slice(2, 15) + | ||
| Math.random().toString(36).slice(2, 15)); | ||
| } |
+15
-0
@@ -0,1 +1,4 @@ | ||
| // eslint-disable-next-line @eslint-community/eslint-comments/disable-enable-pair | ||
| /* eslint-disable sonarjs/pseudo-random */ | ||
| /** | ||
@@ -21,1 +24,13 @@ * Converts a value to its string representation. | ||
| } | ||
| /** | ||
| * Generates a unique listener ID. | ||
| * @returns A unique string identifier. | ||
| */ | ||
| export function generateUniqueListenerId(): string { | ||
| return ( | ||
| Date.now().toString(36) + | ||
| Math.random().toString(36).slice(2, 15) + | ||
| Math.random().toString(36).slice(2, 15) | ||
| ) | ||
| } |
37258
18.08%674
23.9%123
9.82%