@tanstack/pacer 0.21.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -2
- package/dist/async-batcher.cjs +1 -1
- package/dist/async-batcher.cjs.map +1 -1
- package/dist/async-batcher.d.cts +0 -1
- package/dist/async-batcher.d.ts +0 -1
- package/dist/async-batcher.js +1 -1
- package/dist/async-batcher.js.map +1 -1
- package/dist/async-debouncer.cjs +2 -5
- package/dist/async-debouncer.cjs.map +1 -1
- package/dist/async-debouncer.d.cts +5 -6
- package/dist/async-debouncer.d.ts +5 -6
- package/dist/async-debouncer.js +2 -5
- package/dist/async-debouncer.js.map +1 -1
- package/dist/async-queuer.cjs +49 -17
- package/dist/async-queuer.cjs.map +1 -1
- package/dist/async-queuer.d.cts +1 -1
- package/dist/async-queuer.d.ts +1 -1
- package/dist/async-queuer.js +49 -17
- package/dist/async-queuer.js.map +1 -1
- package/dist/async-rate-limiter.cjs +3 -6
- package/dist/async-rate-limiter.cjs.map +1 -1
- package/dist/async-rate-limiter.d.cts +4 -5
- package/dist/async-rate-limiter.d.ts +4 -5
- package/dist/async-rate-limiter.js +3 -6
- package/dist/async-rate-limiter.js.map +1 -1
- package/dist/async-retryer.cjs +2 -11
- package/dist/async-retryer.cjs.map +1 -1
- package/dist/async-retryer.d.cts +4 -2
- package/dist/async-retryer.d.ts +4 -2
- package/dist/async-retryer.js +2 -11
- package/dist/async-retryer.js.map +1 -1
- package/dist/async-throttler.cjs +2 -5
- package/dist/async-throttler.cjs.map +1 -1
- package/dist/async-throttler.d.cts +5 -6
- package/dist/async-throttler.d.ts +5 -6
- package/dist/async-throttler.js +2 -5
- package/dist/async-throttler.js.map +1 -1
- package/dist/batcher.cjs.map +1 -1
- package/dist/batcher.d.cts +0 -1
- package/dist/batcher.d.ts +0 -1
- package/dist/batcher.js.map +1 -1
- package/dist/debouncer.cjs.map +1 -1
- package/dist/debouncer.d.cts +0 -1
- package/dist/debouncer.d.ts +0 -1
- package/dist/debouncer.js.map +1 -1
- package/dist/event-client.cjs +8 -0
- package/dist/event-client.cjs.map +1 -1
- package/dist/event-client.d.cts +0 -1
- package/dist/event-client.d.ts +0 -1
- package/dist/event-client.js +8 -0
- package/dist/event-client.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/queuer.cjs +7 -2
- package/dist/queuer.cjs.map +1 -1
- package/dist/queuer.d.cts +1 -1
- package/dist/queuer.d.ts +1 -1
- package/dist/queuer.js +7 -2
- package/dist/queuer.js.map +1 -1
- package/dist/rate-limiter.cjs.map +1 -1
- package/dist/rate-limiter.d.cts +0 -1
- package/dist/rate-limiter.d.ts +0 -1
- package/dist/rate-limiter.js.map +1 -1
- package/dist/throttler.cjs.map +1 -1
- package/dist/throttler.d.cts +0 -1
- package/dist/throttler.d.ts +0 -1
- package/dist/throttler.js.map +1 -1
- package/dist/utils.cjs +0 -1
- package/dist/utils.cjs.map +1 -1
- package/dist/utils.d.cts +0 -1
- package/dist/utils.d.ts +0 -1
- package/dist/utils.js.map +1 -1
- package/package.json +3 -3
- package/src/async-debouncer.ts +30 -29
- package/src/async-queuer.ts +83 -20
- package/src/async-rate-limiter.ts +11 -10
- package/src/async-retryer.ts +4 -15
- package/src/async-throttler.ts +33 -32
- package/src/queuer.ts +13 -2
package/dist/debouncer.d.cts
CHANGED
package/dist/debouncer.d.ts
CHANGED
package/dist/debouncer.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"debouncer.js","names":["#getEnabled","#setState","#execute","#timeoutId","#getWait","#clearTimeout"],"sources":["../src/debouncer.ts"],"sourcesContent":["import { Store } from '@tanstack/store'\nimport { parseFunctionOrValue } from './utils'\nimport { emitChange, pacerEventClient } from './event-client'\nimport type { AnyFunction } from './types'\n\nexport interface DebouncerState<TFn extends AnyFunction> {\n /**\n * Whether the debouncer can execute on the leading edge of the timeout\n */\n canLeadingExecute: boolean\n /**\n * Number of function executions that have been completed\n */\n executionCount: number\n /**\n * Whether the debouncer is waiting for the timeout to trigger execution\n */\n isPending: boolean\n /**\n * The arguments from the most recent call to maybeExecute\n */\n lastArgs: Parameters<TFn> | undefined\n /**\n * Number of times maybeExecute has been called (for reduction calculations)\n */\n maybeExecuteCount: number\n /**\n * Current execution status - 'idle' when not active, 'pending' when waiting for timeout\n */\n status: 'disabled' | 'idle' | 'pending'\n}\n\nfunction getDefaultDebouncerState<\n TFn extends AnyFunction,\n>(): DebouncerState<TFn> {\n return {\n canLeadingExecute: true,\n executionCount: 0,\n isPending: false,\n lastArgs: undefined,\n status: 'idle',\n maybeExecuteCount: 0,\n }\n}\n\n/**\n * Options for configuring a debounced function\n */\nexport interface DebouncerOptions<TFn extends AnyFunction> {\n /**\n * Whether the debouncer is enabled. When disabled, maybeExecute will not trigger any executions.\n * Can be a boolean or a function that returns a boolean.\n * Defaults to true.\n */\n enabled?: boolean | ((debouncer: Debouncer<TFn>) => boolean)\n /**\n * Initial state for the debouncer\n */\n initialState?: Partial<DebouncerState<TFn>>\n /**\n * A key to identify the debouncer.\n * If provided, the debouncer will be identified by this key in the devtools and PacerProvider if applicable.\n */\n key?: string\n /**\n * Whether to execute on the leading edge of the timeout.\n * The first call will execute immediately and the rest will wait the delay.\n * Defaults to false.\n */\n leading?: boolean\n /**\n * Callback function that is called after the function is executed\n */\n onExecute?: (args: Parameters<TFn>, debouncer: Debouncer<TFn>) => void\n /**\n * Whether to execute on the trailing edge of the timeout.\n * Defaults to true.\n */\n trailing?: boolean\n /**\n * Delay in milliseconds before executing the function.\n * Can be a number or a function that returns a number.\n * Defaults to 0ms\n */\n wait: number | ((debouncer: Debouncer<TFn>) => number)\n}\n\n/**\n * Utility function for sharing common `DebouncerOptions` options between different `Debouncer` instances.\n */\nexport function debouncerOptions<\n TFn extends AnyFunction = AnyFunction,\n TOptions extends Partial<DebouncerOptions<TFn>> = Partial<\n DebouncerOptions<TFn>\n >,\n>(options: TOptions): TOptions {\n return options\n}\n\nconst defaultOptions: Omit<\n Required<DebouncerOptions<any>>,\n 'initialState' | 'onExecute' | 'key'\n> = {\n enabled: true,\n leading: false,\n trailing: true,\n wait: 0,\n}\n\n/**\n * A class that creates a debounced function.\n *\n * Debouncing ensures that a function is only executed after a certain amount of time has passed\n * since its last invocation. This is useful for handling frequent events like window resizing,\n * scroll events, or input changes where you want to limit the rate of execution.\n * This synchronous version is lighter weight and often all you need - upgrade to AsyncDebouncer when you need promises, retry support, abort/cancel capabilities, or advanced error handling.\n *\n * The debounced function can be configured to execute either at the start of the delay period\n * (leading edge) or at the end (trailing edge, default). Each new call during the wait period\n * will reset the timer.\n *\n * State Management:\n * - Uses TanStack Store for reactive state management\n * - Use `initialState` to provide initial state values when creating the debouncer\n * - Use `onExecute` callback to react to function execution and implement custom logic\n * - The state includes canLeadingExecute, execution count, and isPending status\n * - State can be accessed via `debouncer.store.state` when using the class directly\n * - When using framework adapters (React/Solid), state is accessed from `debouncer.state`\n *\n * @example\n * ```ts\n * const debouncer = new Debouncer((value: string) => {\n * saveToDatabase(value);\n * }, { wait: 500 });\n *\n * // Will only save after 500ms of no new input\n * inputElement.addEventListener('input', () => {\n * debouncer.maybeExecute(inputElement.value);\n * });\n * ```\n */\nexport class Debouncer<TFn extends AnyFunction> {\n readonly store: Store<Readonly<DebouncerState<TFn>>> = new Store(\n getDefaultDebouncerState<TFn>(),\n )\n key: string | undefined\n options: DebouncerOptions<TFn>\n #timeoutId: ReturnType<typeof setTimeout> | undefined\n\n constructor(\n public fn: TFn,\n initialOptions: DebouncerOptions<TFn>,\n ) {\n this.key = initialOptions.key\n this.options = {\n ...defaultOptions,\n ...initialOptions,\n }\n this.#setState(this.options.initialState ?? {})\n\n if (this.key) {\n pacerEventClient.on('d-Debouncer', (event) => {\n if (event.payload.key !== this.key) return\n this.#setState(\n event.payload.store.state as Partial<DebouncerState<TFn>>,\n )\n this.setOptions(event.payload.options as Partial<DebouncerOptions<TFn>>)\n })\n }\n }\n\n /**\n * Updates the debouncer options\n */\n setOptions = (newOptions: Partial<DebouncerOptions<TFn>>): void => {\n this.options = { ...this.options, ...newOptions }\n\n // Cancel pending execution if the debouncer is disabled\n if (!this.#getEnabled()) {\n this.cancel()\n }\n }\n\n #setState = (newState: Partial<DebouncerState<TFn>>): void => {\n this.store.setState((state) => {\n const combinedState = {\n ...state,\n ...newState,\n }\n const { isPending } = combinedState\n return {\n ...combinedState,\n status: !this.#getEnabled()\n ? 'disabled'\n : isPending\n ? 'pending'\n : 'idle',\n }\n })\n emitChange('Debouncer', this)\n }\n\n /**\n * Returns the current enabled state of the debouncer\n */\n #getEnabled = (): boolean => {\n return !!parseFunctionOrValue(this.options.enabled, this)\n }\n\n /**\n * Returns the current wait time in milliseconds\n */\n #getWait = (): number => {\n return parseFunctionOrValue(this.options.wait, this)\n }\n\n /**\n * Attempts to execute the debounced function\n * If a call is already in progress, it will be queued\n */\n maybeExecute = (...args: Parameters<TFn>): void => {\n if (!this.#getEnabled()) return undefined\n\n this.#setState({\n maybeExecuteCount: this.store.state.maybeExecuteCount + 1,\n })\n\n let _didLeadingExecute = false\n\n // Handle leading execution\n if (this.options.leading && this.store.state.canLeadingExecute) {\n this.#setState({ canLeadingExecute: false })\n _didLeadingExecute = true\n this.#execute(...args)\n }\n\n // Start pending state to indicate that the debouncer is waiting for the trailing edge\n if (this.options.trailing) {\n this.#setState({ isPending: true, lastArgs: args })\n }\n\n // Clear any existing timeout\n if (this.#timeoutId) clearTimeout(this.#timeoutId)\n\n // Set new timeout that will reset canLeadingExecute and execute trailing only if enabled and did not execute leading\n this.#timeoutId = setTimeout(() => {\n this.#setState({ canLeadingExecute: true })\n if (this.options.trailing && !_didLeadingExecute) {\n this.#execute(...args)\n }\n }, this.#getWait())\n }\n\n #execute = (...args: Parameters<TFn>): void => {\n if (!this.#getEnabled()) return undefined\n this.fn(...args) // EXECUTE!\n this.#setState({\n executionCount: this.store.state.executionCount + 1,\n isPending: false,\n lastArgs: undefined,\n })\n this.options.onExecute?.(args, this)\n }\n\n /**\n * Processes the current pending execution immediately\n */\n flush = (): void => {\n if (this.store.state.isPending && this.store.state.lastArgs) {\n this.#clearTimeout() // clear any pending timeout\n this.#execute(...this.store.state.lastArgs) // execute immediately\n }\n }\n\n #clearTimeout = (): void => {\n if (this.#timeoutId) {\n clearTimeout(this.#timeoutId)\n this.#timeoutId = undefined\n }\n }\n\n /**\n * Cancels any pending execution\n */\n cancel = (): void => {\n this.#clearTimeout()\n this.#setState({\n canLeadingExecute: true,\n isPending: false,\n })\n }\n\n /**\n * Resets the debouncer state to its default values\n */\n reset = (): void => {\n this.#setState(getDefaultDebouncerState<TFn>())\n }\n}\n\n/**\n * Creates a debounced function that delays invoking the provided function until after a specified wait time.\n * Multiple calls during the wait period will cancel previous pending invocations and reset the timer.\n *\n * This synchronous version is lighter weight and often all you need - upgrade to asyncDebounce when you need promises, retry support, abort/cancel capabilities, or advanced error handling.\n *\n * If leading option is true, the function will execute immediately on the first call, then wait the delay\n * before allowing another execution.\n *\n * State Management:\n * - Uses TanStack Store for reactive state management\n * - Use `initialState` to provide initial state values when creating the debouncer\n * - Use `onExecute` callback to react to function execution and implement custom logic\n * - The state includes canLeadingExecute, execution count, and isPending status\n * - State can be accessed via the underlying Debouncer instance's `store.state` property\n * - When using framework adapters (React/Solid), state is accessed from the hook's state property\n *\n * @example\n * ```ts\n * const debounced = debounce(() => {\n * saveChanges();\n * }, { wait: 1000 });\n *\n * // Called repeatedly but executes at most once per second\n * inputElement.addEventListener('input', debounced);\n * ```\n */\nexport function debounce<TFn extends AnyFunction>(\n fn: TFn,\n initialOptions: DebouncerOptions<TFn>,\n): (...args: Parameters<TFn>) => void {\n const debouncer = new Debouncer(fn, initialOptions)\n return debouncer.maybeExecute\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"debouncer.js","names":["#getEnabled","#setState","#execute","#timeoutId","#getWait","#clearTimeout"],"sources":["../src/debouncer.ts"],"sourcesContent":["import { Store } from '@tanstack/store'\nimport { parseFunctionOrValue } from './utils'\nimport { emitChange, pacerEventClient } from './event-client'\nimport type { AnyFunction } from './types'\n\nexport interface DebouncerState<TFn extends AnyFunction> {\n /**\n * Whether the debouncer can execute on the leading edge of the timeout\n */\n canLeadingExecute: boolean\n /**\n * Number of function executions that have been completed\n */\n executionCount: number\n /**\n * Whether the debouncer is waiting for the timeout to trigger execution\n */\n isPending: boolean\n /**\n * The arguments from the most recent call to maybeExecute\n */\n lastArgs: Parameters<TFn> | undefined\n /**\n * Number of times maybeExecute has been called (for reduction calculations)\n */\n maybeExecuteCount: number\n /**\n * Current execution status - 'idle' when not active, 'pending' when waiting for timeout\n */\n status: 'disabled' | 'idle' | 'pending'\n}\n\nfunction getDefaultDebouncerState<\n TFn extends AnyFunction,\n>(): DebouncerState<TFn> {\n return {\n canLeadingExecute: true,\n executionCount: 0,\n isPending: false,\n lastArgs: undefined,\n status: 'idle',\n maybeExecuteCount: 0,\n }\n}\n\n/**\n * Options for configuring a debounced function\n */\nexport interface DebouncerOptions<TFn extends AnyFunction> {\n /**\n * Whether the debouncer is enabled. When disabled, maybeExecute will not trigger any executions.\n * Can be a boolean or a function that returns a boolean.\n * Defaults to true.\n */\n enabled?: boolean | ((debouncer: Debouncer<TFn>) => boolean)\n /**\n * Initial state for the debouncer\n */\n initialState?: Partial<DebouncerState<TFn>>\n /**\n * A key to identify the debouncer.\n * If provided, the debouncer will be identified by this key in the devtools and PacerProvider if applicable.\n */\n key?: string\n /**\n * Whether to execute on the leading edge of the timeout.\n * The first call will execute immediately and the rest will wait the delay.\n * Defaults to false.\n */\n leading?: boolean\n /**\n * Callback function that is called after the function is executed\n */\n onExecute?: (args: Parameters<TFn>, debouncer: Debouncer<TFn>) => void\n /**\n * Whether to execute on the trailing edge of the timeout.\n * Defaults to true.\n */\n trailing?: boolean\n /**\n * Delay in milliseconds before executing the function.\n * Can be a number or a function that returns a number.\n * Defaults to 0ms\n */\n wait: number | ((debouncer: Debouncer<TFn>) => number)\n}\n\n/**\n * Utility function for sharing common `DebouncerOptions` options between different `Debouncer` instances.\n */\nexport function debouncerOptions<\n TFn extends AnyFunction = AnyFunction,\n TOptions extends Partial<DebouncerOptions<TFn>> = Partial<\n DebouncerOptions<TFn>\n >,\n>(options: TOptions): TOptions {\n return options\n}\n\nconst defaultOptions: Omit<\n Required<DebouncerOptions<any>>,\n 'initialState' | 'onExecute' | 'key'\n> = {\n enabled: true,\n leading: false,\n trailing: true,\n wait: 0,\n}\n\n/**\n * A class that creates a debounced function.\n *\n * Debouncing ensures that a function is only executed after a certain amount of time has passed\n * since its last invocation. This is useful for handling frequent events like window resizing,\n * scroll events, or input changes where you want to limit the rate of execution.\n * This synchronous version is lighter weight and often all you need - upgrade to AsyncDebouncer when you need promises, retry support, abort/cancel capabilities, or advanced error handling.\n *\n * The debounced function can be configured to execute either at the start of the delay period\n * (leading edge) or at the end (trailing edge, default). Each new call during the wait period\n * will reset the timer.\n *\n * State Management:\n * - Uses TanStack Store for reactive state management\n * - Use `initialState` to provide initial state values when creating the debouncer\n * - Use `onExecute` callback to react to function execution and implement custom logic\n * - The state includes canLeadingExecute, execution count, and isPending status\n * - State can be accessed via `debouncer.store.state` when using the class directly\n * - When using framework adapters (React/Solid), state is accessed from `debouncer.state`\n *\n * @example\n * ```ts\n * const debouncer = new Debouncer((value: string) => {\n * saveToDatabase(value);\n * }, { wait: 500 });\n *\n * // Will only save after 500ms of no new input\n * inputElement.addEventListener('input', () => {\n * debouncer.maybeExecute(inputElement.value);\n * });\n * ```\n */\nexport class Debouncer<TFn extends AnyFunction> {\n readonly store: Store<Readonly<DebouncerState<TFn>>> = new Store(\n getDefaultDebouncerState<TFn>(),\n )\n key: string | undefined\n options: DebouncerOptions<TFn>\n #timeoutId: ReturnType<typeof setTimeout> | undefined\n\n constructor(\n public fn: TFn,\n initialOptions: DebouncerOptions<TFn>,\n ) {\n this.key = initialOptions.key\n this.options = {\n ...defaultOptions,\n ...initialOptions,\n }\n this.#setState(this.options.initialState ?? {})\n\n if (this.key) {\n pacerEventClient.on('d-Debouncer', (event) => {\n if (event.payload.key !== this.key) return\n this.#setState(\n event.payload.store.state as Partial<DebouncerState<TFn>>,\n )\n this.setOptions(event.payload.options as Partial<DebouncerOptions<TFn>>)\n })\n }\n }\n\n /**\n * Updates the debouncer options\n */\n setOptions = (newOptions: Partial<DebouncerOptions<TFn>>): void => {\n this.options = { ...this.options, ...newOptions }\n\n // Cancel pending execution if the debouncer is disabled\n if (!this.#getEnabled()) {\n this.cancel()\n }\n }\n\n #setState = (newState: Partial<DebouncerState<TFn>>): void => {\n this.store.setState((state) => {\n const combinedState = {\n ...state,\n ...newState,\n }\n const { isPending } = combinedState\n return {\n ...combinedState,\n status: !this.#getEnabled()\n ? 'disabled'\n : isPending\n ? 'pending'\n : 'idle',\n }\n })\n emitChange('Debouncer', this)\n }\n\n /**\n * Returns the current enabled state of the debouncer\n */\n #getEnabled = (): boolean => {\n return !!parseFunctionOrValue(this.options.enabled, this)\n }\n\n /**\n * Returns the current wait time in milliseconds\n */\n #getWait = (): number => {\n return parseFunctionOrValue(this.options.wait, this)\n }\n\n /**\n * Attempts to execute the debounced function\n * If a call is already in progress, it will be queued\n */\n maybeExecute = (...args: Parameters<TFn>): void => {\n if (!this.#getEnabled()) return undefined\n\n this.#setState({\n maybeExecuteCount: this.store.state.maybeExecuteCount + 1,\n })\n\n let _didLeadingExecute = false\n\n // Handle leading execution\n if (this.options.leading && this.store.state.canLeadingExecute) {\n this.#setState({ canLeadingExecute: false })\n _didLeadingExecute = true\n this.#execute(...args)\n }\n\n // Start pending state to indicate that the debouncer is waiting for the trailing edge\n if (this.options.trailing) {\n this.#setState({ isPending: true, lastArgs: args })\n }\n\n // Clear any existing timeout\n if (this.#timeoutId) clearTimeout(this.#timeoutId)\n\n // Set new timeout that will reset canLeadingExecute and execute trailing only if enabled and did not execute leading\n this.#timeoutId = setTimeout(() => {\n this.#setState({ canLeadingExecute: true })\n if (this.options.trailing && !_didLeadingExecute) {\n this.#execute(...args)\n }\n }, this.#getWait())\n }\n\n #execute = (...args: Parameters<TFn>): void => {\n if (!this.#getEnabled()) return undefined\n this.fn(...args) // EXECUTE!\n this.#setState({\n executionCount: this.store.state.executionCount + 1,\n isPending: false,\n lastArgs: undefined,\n })\n this.options.onExecute?.(args, this)\n }\n\n /**\n * Processes the current pending execution immediately\n */\n flush = (): void => {\n if (this.store.state.isPending && this.store.state.lastArgs) {\n this.#clearTimeout() // clear any pending timeout\n this.#execute(...this.store.state.lastArgs) // execute immediately\n }\n }\n\n #clearTimeout = (): void => {\n if (this.#timeoutId) {\n clearTimeout(this.#timeoutId)\n this.#timeoutId = undefined\n }\n }\n\n /**\n * Cancels any pending execution\n */\n cancel = (): void => {\n this.#clearTimeout()\n this.#setState({\n canLeadingExecute: true,\n isPending: false,\n })\n }\n\n /**\n * Resets the debouncer state to its default values\n */\n reset = (): void => {\n this.#setState(getDefaultDebouncerState<TFn>())\n }\n}\n\n/**\n * Creates a debounced function that delays invoking the provided function until after a specified wait time.\n * Multiple calls during the wait period will cancel previous pending invocations and reset the timer.\n *\n * This synchronous version is lighter weight and often all you need - upgrade to asyncDebounce when you need promises, retry support, abort/cancel capabilities, or advanced error handling.\n *\n * If leading option is true, the function will execute immediately on the first call, then wait the delay\n * before allowing another execution.\n *\n * State Management:\n * - Uses TanStack Store for reactive state management\n * - Use `initialState` to provide initial state values when creating the debouncer\n * - Use `onExecute` callback to react to function execution and implement custom logic\n * - The state includes canLeadingExecute, execution count, and isPending status\n * - State can be accessed via the underlying Debouncer instance's `store.state` property\n * - When using framework adapters (React/Solid), state is accessed from the hook's state property\n *\n * @example\n * ```ts\n * const debounced = debounce(() => {\n * saveChanges();\n * }, { wait: 1000 });\n *\n * // Called repeatedly but executes at most once per second\n * inputElement.addEventListener('input', debounced);\n * ```\n */\nexport function debounce<TFn extends AnyFunction>(\n fn: TFn,\n initialOptions: DebouncerOptions<TFn>,\n): (...args: Parameters<TFn>) => void {\n const debouncer = new Debouncer(fn, initialOptions)\n return debouncer.maybeExecute\n}\n"],"mappings":"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"}
|
package/dist/event-client.cjs
CHANGED
|
@@ -33,6 +33,14 @@ var PacerEventClient = class extends _tanstack_devtools_event_client.EventClient
|
|
|
33
33
|
super({
|
|
34
34
|
pluginId: "pacer",
|
|
35
35
|
debug: props?.debug,
|
|
36
|
+
/**
|
|
37
|
+
* TanStack Devtools starts `ClientEventBus` inside an async dynamic import
|
|
38
|
+
* after the first commit. Pacer utils often emit during the first render
|
|
39
|
+
* (constructor → store sync). `EventClient` only retries `tanstack-connect`
|
|
40
|
+
* `#maxRetries` times at `reconnectEveryMs` before it gives up and drops
|
|
41
|
+
* all queued events permanently. Default 300ms × 5 ≈ 1.5s is often too
|
|
42
|
+
* short; Form uses 1000ms here for the same reason.
|
|
43
|
+
*/
|
|
36
44
|
reconnectEveryMs: 1e3
|
|
37
45
|
});
|
|
38
46
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"event-client.cjs","names":["EventClient"],"sources":["../src/event-client.ts"],"sourcesContent":["import { EventClient } from '@tanstack/devtools-event-client'\n\n/**\n * Payload on the devtools event bus must be JSON-serializable: `ClientEventBus`\n * always stringifies events for `emitToServer` and `BroadcastChannel`, so live\n * util instances (with reactive `store` graphs) cannot be sent as-is.\n *\n * Live instances are kept in {@link pacerDevtoolsInstancesByKey} for the panel.\n */\nexport interface PacerDevtoolsWirePayload {\n key: string\n store: { state: unknown }\n options: unknown\n}\n\nconst pacerDevtoolsInstancesByKey = new Map<string, unknown>()\n\nexport function registerPacerDevtoolsInstance(\n key: string,\n instance: unknown,\n): void {\n pacerDevtoolsInstancesByKey.set(key, instance)\n}\n\nexport function getPacerDevtoolsInstance(key: string): unknown {\n return pacerDevtoolsInstancesByKey.get(key)\n}\n\nfunction cloneJsonSafe(value: unknown): unknown {\n if (value === undefined) {\n return undefined\n }\n try {\n return JSON.parse(JSON.stringify(value)) as unknown\n } catch {\n return null\n }\n}\n\nfunction readStoreSnapshot(store: {\n get?: () => unknown\n state?: unknown\n}): unknown {\n if (typeof store.get === 'function') {\n return store.get()\n }\n return store.state\n}\n\nfunction toPacerDevtoolsWirePayload(instance: {\n key: string\n store: { get?: () => unknown; state?: unknown }\n options: unknown\n}): PacerDevtoolsWirePayload {\n return {\n key: instance.key,\n store: { state: cloneJsonSafe(readStoreSnapshot(instance.store)) },\n options: cloneJsonSafe(instance.options),\n }\n}\n\n/**\n * Suffix-only keys: {@link EventClient} prepends `pluginId:` (`pacer:`) at runtime\n * for `emit` / `on`. Wire `type` values are `pacer:${key}`.\n */\nexport interface PacerEventMap {\n 'd-AsyncBatcher': PacerDevtoolsWirePayload\n 'd-AsyncDebouncer': PacerDevtoolsWirePayload\n 'd-AsyncQueuer': PacerDevtoolsWirePayload\n 'd-AsyncRateLimiter': PacerDevtoolsWirePayload\n 'd-AsyncRetryer': PacerDevtoolsWirePayload\n 'd-AsyncThrottler': PacerDevtoolsWirePayload\n 'd-Batcher': PacerDevtoolsWirePayload\n 'd-Debouncer': PacerDevtoolsWirePayload\n 'd-Queuer': PacerDevtoolsWirePayload\n 'd-RateLimiter': PacerDevtoolsWirePayload\n 'd-Throttler': PacerDevtoolsWirePayload\n AsyncBatcher: PacerDevtoolsWirePayload\n AsyncDebouncer: PacerDevtoolsWirePayload\n AsyncQueuer: PacerDevtoolsWirePayload\n AsyncRateLimiter: PacerDevtoolsWirePayload\n AsyncRetryer: PacerDevtoolsWirePayload\n AsyncThrottler: PacerDevtoolsWirePayload\n Batcher: PacerDevtoolsWirePayload\n Debouncer: PacerDevtoolsWirePayload\n Queuer: PacerDevtoolsWirePayload\n RateLimiter: PacerDevtoolsWirePayload\n Throttler: PacerDevtoolsWirePayload\n}\n\nexport type PacerEventName = keyof PacerEventMap\n\ntype PacerUtilEmitSource = {\n key?: string\n store: { get?: () => unknown; state?: unknown }\n options: unknown\n}\n\nclass PacerEventClient extends EventClient<PacerEventMap> {\n constructor(props?: { debug: boolean }) {\n super({\n pluginId: 'pacer',\n debug: props?.debug,\n /**\n * TanStack Devtools starts `ClientEventBus` inside an async dynamic import\n * after the first commit. Pacer utils often emit during the first render\n * (constructor → store sync). `EventClient` only retries `tanstack-connect`\n * `#maxRetries` times at `reconnectEveryMs` before it gives up and drops\n * all queued events permanently. Default 300ms × 5 ≈ 1.5s is often too\n * short; Form uses 1000ms here for the same reason.\n */\n reconnectEveryMs: 1000,\n })\n }\n}\n\nexport const emitChange = <TEvent extends keyof PacerEventMap>(\n event: TEvent,\n instance: PacerUtilEmitSource,\n) => {\n const key = instance.key\n if (!key) {\n return\n }\n registerPacerDevtoolsInstance(key, instance)\n pacerEventClient.emit(event, toPacerDevtoolsWirePayload({ ...instance, key }))\n}\n\nexport const pacerEventClient = new PacerEventClient()\n"],"mappings":";;;;AAeA,MAAM,8CAA8B,IAAI,
|
|
1
|
+
{"version":3,"file":"event-client.cjs","names":["EventClient"],"sources":["../src/event-client.ts"],"sourcesContent":["import { EventClient } from '@tanstack/devtools-event-client'\n\n/**\n * Payload on the devtools event bus must be JSON-serializable: `ClientEventBus`\n * always stringifies events for `emitToServer` and `BroadcastChannel`, so live\n * util instances (with reactive `store` graphs) cannot be sent as-is.\n *\n * Live instances are kept in {@link pacerDevtoolsInstancesByKey} for the panel.\n */\nexport interface PacerDevtoolsWirePayload {\n key: string\n store: { state: unknown }\n options: unknown\n}\n\nconst pacerDevtoolsInstancesByKey = new Map<string, unknown>()\n\nexport function registerPacerDevtoolsInstance(\n key: string,\n instance: unknown,\n): void {\n pacerDevtoolsInstancesByKey.set(key, instance)\n}\n\nexport function getPacerDevtoolsInstance(key: string): unknown {\n return pacerDevtoolsInstancesByKey.get(key)\n}\n\nfunction cloneJsonSafe(value: unknown): unknown {\n if (value === undefined) {\n return undefined\n }\n try {\n return JSON.parse(JSON.stringify(value)) as unknown\n } catch {\n return null\n }\n}\n\nfunction readStoreSnapshot(store: {\n get?: () => unknown\n state?: unknown\n}): unknown {\n if (typeof store.get === 'function') {\n return store.get()\n }\n return store.state\n}\n\nfunction toPacerDevtoolsWirePayload(instance: {\n key: string\n store: { get?: () => unknown; state?: unknown }\n options: unknown\n}): PacerDevtoolsWirePayload {\n return {\n key: instance.key,\n store: { state: cloneJsonSafe(readStoreSnapshot(instance.store)) },\n options: cloneJsonSafe(instance.options),\n }\n}\n\n/**\n * Suffix-only keys: {@link EventClient} prepends `pluginId:` (`pacer:`) at runtime\n * for `emit` / `on`. Wire `type` values are `pacer:${key}`.\n */\nexport interface PacerEventMap {\n 'd-AsyncBatcher': PacerDevtoolsWirePayload\n 'd-AsyncDebouncer': PacerDevtoolsWirePayload\n 'd-AsyncQueuer': PacerDevtoolsWirePayload\n 'd-AsyncRateLimiter': PacerDevtoolsWirePayload\n 'd-AsyncRetryer': PacerDevtoolsWirePayload\n 'd-AsyncThrottler': PacerDevtoolsWirePayload\n 'd-Batcher': PacerDevtoolsWirePayload\n 'd-Debouncer': PacerDevtoolsWirePayload\n 'd-Queuer': PacerDevtoolsWirePayload\n 'd-RateLimiter': PacerDevtoolsWirePayload\n 'd-Throttler': PacerDevtoolsWirePayload\n AsyncBatcher: PacerDevtoolsWirePayload\n AsyncDebouncer: PacerDevtoolsWirePayload\n AsyncQueuer: PacerDevtoolsWirePayload\n AsyncRateLimiter: PacerDevtoolsWirePayload\n AsyncRetryer: PacerDevtoolsWirePayload\n AsyncThrottler: PacerDevtoolsWirePayload\n Batcher: PacerDevtoolsWirePayload\n Debouncer: PacerDevtoolsWirePayload\n Queuer: PacerDevtoolsWirePayload\n RateLimiter: PacerDevtoolsWirePayload\n Throttler: PacerDevtoolsWirePayload\n}\n\nexport type PacerEventName = keyof PacerEventMap\n\ntype PacerUtilEmitSource = {\n key?: string\n store: { get?: () => unknown; state?: unknown }\n options: unknown\n}\n\nclass PacerEventClient extends EventClient<PacerEventMap> {\n constructor(props?: { debug: boolean }) {\n super({\n pluginId: 'pacer',\n debug: props?.debug,\n /**\n * TanStack Devtools starts `ClientEventBus` inside an async dynamic import\n * after the first commit. Pacer utils often emit during the first render\n * (constructor → store sync). `EventClient` only retries `tanstack-connect`\n * `#maxRetries` times at `reconnectEveryMs` before it gives up and drops\n * all queued events permanently. Default 300ms × 5 ≈ 1.5s is often too\n * short; Form uses 1000ms here for the same reason.\n */\n reconnectEveryMs: 1000,\n })\n }\n}\n\nexport const emitChange = <TEvent extends keyof PacerEventMap>(\n event: TEvent,\n instance: PacerUtilEmitSource,\n) => {\n const key = instance.key\n if (!key) {\n return\n }\n registerPacerDevtoolsInstance(key, instance)\n pacerEventClient.emit(event, toPacerDevtoolsWirePayload({ ...instance, key }))\n}\n\nexport const pacerEventClient = new PacerEventClient()\n"],"mappings":";;;;AAeA,MAAM,8CAA8B,IAAI,IAAqB;AAE7D,SAAgB,8BACd,KACA,UACM;CACN,4BAA4B,IAAI,KAAK,QAAQ;AAC/C;AAEA,SAAgB,yBAAyB,KAAsB;CAC7D,OAAO,4BAA4B,IAAI,GAAG;AAC5C;AAEA,SAAS,cAAc,OAAyB;CAC9C,IAAI,UAAU,QACZ;CAEF,IAAI;EACF,OAAO,KAAK,MAAM,KAAK,UAAU,KAAK,CAAC;CACzC,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAS,kBAAkB,OAGf;CACV,IAAI,OAAO,MAAM,QAAQ,YACvB,OAAO,MAAM,IAAI;CAEnB,OAAO,MAAM;AACf;AAEA,SAAS,2BAA2B,UAIP;CAC3B,OAAO;EACL,KAAK,SAAS;EACd,OAAO,EAAE,OAAO,cAAc,kBAAkB,SAAS,KAAK,CAAC,EAAE;EACjE,SAAS,cAAc,SAAS,OAAO;CACzC;AACF;AAuCA,IAAM,mBAAN,cAA+BA,4CAA2B;CACxD,YAAY,OAA4B;EACtC,MAAM;GACJ,UAAU;GACV,OAAO,OAAO;;;;;;;;;GASd,kBAAkB;EACpB,CAAC;CACH;AACF;AAEA,MAAa,cACX,OACA,aACG;CACH,MAAM,MAAM,SAAS;CACrB,IAAI,CAAC,KACH;CAEF,8BAA8B,KAAK,QAAQ;CAC3C,iBAAiB,KAAK,OAAO,2BAA2B;EAAE,GAAG;EAAU;CAAI,CAAC,CAAC;AAC/E;AAEA,MAAa,mBAAmB,IAAI,iBAAiB"}
|
package/dist/event-client.d.cts
CHANGED
package/dist/event-client.d.ts
CHANGED
package/dist/event-client.js
CHANGED
|
@@ -32,6 +32,14 @@ var PacerEventClient = class extends EventClient {
|
|
|
32
32
|
super({
|
|
33
33
|
pluginId: "pacer",
|
|
34
34
|
debug: props?.debug,
|
|
35
|
+
/**
|
|
36
|
+
* TanStack Devtools starts `ClientEventBus` inside an async dynamic import
|
|
37
|
+
* after the first commit. Pacer utils often emit during the first render
|
|
38
|
+
* (constructor → store sync). `EventClient` only retries `tanstack-connect`
|
|
39
|
+
* `#maxRetries` times at `reconnectEveryMs` before it gives up and drops
|
|
40
|
+
* all queued events permanently. Default 300ms × 5 ≈ 1.5s is often too
|
|
41
|
+
* short; Form uses 1000ms here for the same reason.
|
|
42
|
+
*/
|
|
35
43
|
reconnectEveryMs: 1e3
|
|
36
44
|
});
|
|
37
45
|
}
|
package/dist/event-client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"event-client.js","names":[],"sources":["../src/event-client.ts"],"sourcesContent":["import { EventClient } from '@tanstack/devtools-event-client'\n\n/**\n * Payload on the devtools event bus must be JSON-serializable: `ClientEventBus`\n * always stringifies events for `emitToServer` and `BroadcastChannel`, so live\n * util instances (with reactive `store` graphs) cannot be sent as-is.\n *\n * Live instances are kept in {@link pacerDevtoolsInstancesByKey} for the panel.\n */\nexport interface PacerDevtoolsWirePayload {\n key: string\n store: { state: unknown }\n options: unknown\n}\n\nconst pacerDevtoolsInstancesByKey = new Map<string, unknown>()\n\nexport function registerPacerDevtoolsInstance(\n key: string,\n instance: unknown,\n): void {\n pacerDevtoolsInstancesByKey.set(key, instance)\n}\n\nexport function getPacerDevtoolsInstance(key: string): unknown {\n return pacerDevtoolsInstancesByKey.get(key)\n}\n\nfunction cloneJsonSafe(value: unknown): unknown {\n if (value === undefined) {\n return undefined\n }\n try {\n return JSON.parse(JSON.stringify(value)) as unknown\n } catch {\n return null\n }\n}\n\nfunction readStoreSnapshot(store: {\n get?: () => unknown\n state?: unknown\n}): unknown {\n if (typeof store.get === 'function') {\n return store.get()\n }\n return store.state\n}\n\nfunction toPacerDevtoolsWirePayload(instance: {\n key: string\n store: { get?: () => unknown; state?: unknown }\n options: unknown\n}): PacerDevtoolsWirePayload {\n return {\n key: instance.key,\n store: { state: cloneJsonSafe(readStoreSnapshot(instance.store)) },\n options: cloneJsonSafe(instance.options),\n }\n}\n\n/**\n * Suffix-only keys: {@link EventClient} prepends `pluginId:` (`pacer:`) at runtime\n * for `emit` / `on`. Wire `type` values are `pacer:${key}`.\n */\nexport interface PacerEventMap {\n 'd-AsyncBatcher': PacerDevtoolsWirePayload\n 'd-AsyncDebouncer': PacerDevtoolsWirePayload\n 'd-AsyncQueuer': PacerDevtoolsWirePayload\n 'd-AsyncRateLimiter': PacerDevtoolsWirePayload\n 'd-AsyncRetryer': PacerDevtoolsWirePayload\n 'd-AsyncThrottler': PacerDevtoolsWirePayload\n 'd-Batcher': PacerDevtoolsWirePayload\n 'd-Debouncer': PacerDevtoolsWirePayload\n 'd-Queuer': PacerDevtoolsWirePayload\n 'd-RateLimiter': PacerDevtoolsWirePayload\n 'd-Throttler': PacerDevtoolsWirePayload\n AsyncBatcher: PacerDevtoolsWirePayload\n AsyncDebouncer: PacerDevtoolsWirePayload\n AsyncQueuer: PacerDevtoolsWirePayload\n AsyncRateLimiter: PacerDevtoolsWirePayload\n AsyncRetryer: PacerDevtoolsWirePayload\n AsyncThrottler: PacerDevtoolsWirePayload\n Batcher: PacerDevtoolsWirePayload\n Debouncer: PacerDevtoolsWirePayload\n Queuer: PacerDevtoolsWirePayload\n RateLimiter: PacerDevtoolsWirePayload\n Throttler: PacerDevtoolsWirePayload\n}\n\nexport type PacerEventName = keyof PacerEventMap\n\ntype PacerUtilEmitSource = {\n key?: string\n store: { get?: () => unknown; state?: unknown }\n options: unknown\n}\n\nclass PacerEventClient extends EventClient<PacerEventMap> {\n constructor(props?: { debug: boolean }) {\n super({\n pluginId: 'pacer',\n debug: props?.debug,\n /**\n * TanStack Devtools starts `ClientEventBus` inside an async dynamic import\n * after the first commit. Pacer utils often emit during the first render\n * (constructor → store sync). `EventClient` only retries `tanstack-connect`\n * `#maxRetries` times at `reconnectEveryMs` before it gives up and drops\n * all queued events permanently. Default 300ms × 5 ≈ 1.5s is often too\n * short; Form uses 1000ms here for the same reason.\n */\n reconnectEveryMs: 1000,\n })\n }\n}\n\nexport const emitChange = <TEvent extends keyof PacerEventMap>(\n event: TEvent,\n instance: PacerUtilEmitSource,\n) => {\n const key = instance.key\n if (!key) {\n return\n }\n registerPacerDevtoolsInstance(key, instance)\n pacerEventClient.emit(event, toPacerDevtoolsWirePayload({ ...instance, key }))\n}\n\nexport const pacerEventClient = new PacerEventClient()\n"],"mappings":";;;AAeA,MAAM,8CAA8B,IAAI,
|
|
1
|
+
{"version":3,"file":"event-client.js","names":[],"sources":["../src/event-client.ts"],"sourcesContent":["import { EventClient } from '@tanstack/devtools-event-client'\n\n/**\n * Payload on the devtools event bus must be JSON-serializable: `ClientEventBus`\n * always stringifies events for `emitToServer` and `BroadcastChannel`, so live\n * util instances (with reactive `store` graphs) cannot be sent as-is.\n *\n * Live instances are kept in {@link pacerDevtoolsInstancesByKey} for the panel.\n */\nexport interface PacerDevtoolsWirePayload {\n key: string\n store: { state: unknown }\n options: unknown\n}\n\nconst pacerDevtoolsInstancesByKey = new Map<string, unknown>()\n\nexport function registerPacerDevtoolsInstance(\n key: string,\n instance: unknown,\n): void {\n pacerDevtoolsInstancesByKey.set(key, instance)\n}\n\nexport function getPacerDevtoolsInstance(key: string): unknown {\n return pacerDevtoolsInstancesByKey.get(key)\n}\n\nfunction cloneJsonSafe(value: unknown): unknown {\n if (value === undefined) {\n return undefined\n }\n try {\n return JSON.parse(JSON.stringify(value)) as unknown\n } catch {\n return null\n }\n}\n\nfunction readStoreSnapshot(store: {\n get?: () => unknown\n state?: unknown\n}): unknown {\n if (typeof store.get === 'function') {\n return store.get()\n }\n return store.state\n}\n\nfunction toPacerDevtoolsWirePayload(instance: {\n key: string\n store: { get?: () => unknown; state?: unknown }\n options: unknown\n}): PacerDevtoolsWirePayload {\n return {\n key: instance.key,\n store: { state: cloneJsonSafe(readStoreSnapshot(instance.store)) },\n options: cloneJsonSafe(instance.options),\n }\n}\n\n/**\n * Suffix-only keys: {@link EventClient} prepends `pluginId:` (`pacer:`) at runtime\n * for `emit` / `on`. Wire `type` values are `pacer:${key}`.\n */\nexport interface PacerEventMap {\n 'd-AsyncBatcher': PacerDevtoolsWirePayload\n 'd-AsyncDebouncer': PacerDevtoolsWirePayload\n 'd-AsyncQueuer': PacerDevtoolsWirePayload\n 'd-AsyncRateLimiter': PacerDevtoolsWirePayload\n 'd-AsyncRetryer': PacerDevtoolsWirePayload\n 'd-AsyncThrottler': PacerDevtoolsWirePayload\n 'd-Batcher': PacerDevtoolsWirePayload\n 'd-Debouncer': PacerDevtoolsWirePayload\n 'd-Queuer': PacerDevtoolsWirePayload\n 'd-RateLimiter': PacerDevtoolsWirePayload\n 'd-Throttler': PacerDevtoolsWirePayload\n AsyncBatcher: PacerDevtoolsWirePayload\n AsyncDebouncer: PacerDevtoolsWirePayload\n AsyncQueuer: PacerDevtoolsWirePayload\n AsyncRateLimiter: PacerDevtoolsWirePayload\n AsyncRetryer: PacerDevtoolsWirePayload\n AsyncThrottler: PacerDevtoolsWirePayload\n Batcher: PacerDevtoolsWirePayload\n Debouncer: PacerDevtoolsWirePayload\n Queuer: PacerDevtoolsWirePayload\n RateLimiter: PacerDevtoolsWirePayload\n Throttler: PacerDevtoolsWirePayload\n}\n\nexport type PacerEventName = keyof PacerEventMap\n\ntype PacerUtilEmitSource = {\n key?: string\n store: { get?: () => unknown; state?: unknown }\n options: unknown\n}\n\nclass PacerEventClient extends EventClient<PacerEventMap> {\n constructor(props?: { debug: boolean }) {\n super({\n pluginId: 'pacer',\n debug: props?.debug,\n /**\n * TanStack Devtools starts `ClientEventBus` inside an async dynamic import\n * after the first commit. Pacer utils often emit during the first render\n * (constructor → store sync). `EventClient` only retries `tanstack-connect`\n * `#maxRetries` times at `reconnectEveryMs` before it gives up and drops\n * all queued events permanently. Default 300ms × 5 ≈ 1.5s is often too\n * short; Form uses 1000ms here for the same reason.\n */\n reconnectEveryMs: 1000,\n })\n }\n}\n\nexport const emitChange = <TEvent extends keyof PacerEventMap>(\n event: TEvent,\n instance: PacerUtilEmitSource,\n) => {\n const key = instance.key\n if (!key) {\n return\n }\n registerPacerDevtoolsInstance(key, instance)\n pacerEventClient.emit(event, toPacerDevtoolsWirePayload({ ...instance, key }))\n}\n\nexport const pacerEventClient = new PacerEventClient()\n"],"mappings":";;;AAeA,MAAM,8CAA8B,IAAI,IAAqB;AAE7D,SAAgB,8BACd,KACA,UACM;CACN,4BAA4B,IAAI,KAAK,QAAQ;AAC/C;AAEA,SAAgB,yBAAyB,KAAsB;CAC7D,OAAO,4BAA4B,IAAI,GAAG;AAC5C;AAEA,SAAS,cAAc,OAAyB;CAC9C,IAAI,UAAU,QACZ;CAEF,IAAI;EACF,OAAO,KAAK,MAAM,KAAK,UAAU,KAAK,CAAC;CACzC,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAS,kBAAkB,OAGf;CACV,IAAI,OAAO,MAAM,QAAQ,YACvB,OAAO,MAAM,IAAI;CAEnB,OAAO,MAAM;AACf;AAEA,SAAS,2BAA2B,UAIP;CAC3B,OAAO;EACL,KAAK,SAAS;EACd,OAAO,EAAE,OAAO,cAAc,kBAAkB,SAAS,KAAK,CAAC,EAAE;EACjE,SAAS,cAAc,SAAS,OAAO;CACzC;AACF;AAuCA,IAAM,mBAAN,cAA+B,YAA2B;CACxD,YAAY,OAA4B;EACtC,MAAM;GACJ,UAAU;GACV,OAAO,OAAO;;;;;;;;;GASd,kBAAkB;EACpB,CAAC;CACH;AACF;AAEA,MAAa,cACX,OACA,aACG;CACH,MAAM,MAAM,SAAS;CACrB,IAAI,CAAC,KACH;CAEF,8BAA8B,KAAK,QAAQ;CAC3C,iBAAiB,KAAK,OAAO,2BAA2B;EAAE,GAAG;EAAU;CAAI,CAAC,CAAC;AAC/E;AAEA,MAAa,mBAAmB,IAAI,iBAAiB"}
|
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
const require_utils = require('./utils.cjs');
|
|
3
|
-
const require_event_client = require('./event-client.cjs');
|
|
4
3
|
const require_async_retryer = require('./async-retryer.cjs');
|
|
4
|
+
const require_event_client = require('./event-client.cjs');
|
|
5
5
|
const require_async_batcher = require('./async-batcher.cjs');
|
|
6
6
|
const require_async_debouncer = require('./async-debouncer.cjs');
|
|
7
7
|
const require_async_queuer = require('./async-queuer.cjs');
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isFunction, parseFunctionOrValue } from "./utils.js";
|
|
2
|
-
import { emitChange, getPacerDevtoolsInstance, pacerEventClient } from "./event-client.js";
|
|
3
2
|
import { AsyncRetryer, asyncRetry, asyncRetryerOptions } from "./async-retryer.js";
|
|
3
|
+
import { emitChange, getPacerDevtoolsInstance, pacerEventClient } from "./event-client.js";
|
|
4
4
|
import { AsyncBatcher, asyncBatch, asyncBatcherOptions } from "./async-batcher.js";
|
|
5
5
|
import { AsyncDebouncer, asyncDebounce, asyncDebouncerOptions } from "./async-debouncer.js";
|
|
6
6
|
import { AsyncQueuer, asyncQueue, asyncQueuerOptions } from "./async-queuer.js";
|
package/dist/queuer.cjs
CHANGED
|
@@ -170,17 +170,22 @@ var Queuer = class {
|
|
|
170
170
|
};
|
|
171
171
|
this.addItem = (item, position = this.options.addItemsTo ?? "back", runOnItemsChange = true) => {
|
|
172
172
|
this.#setState({ addItemCount: this.store.state.addItemCount + 1 });
|
|
173
|
+
if (item === void 0) {
|
|
174
|
+
this.#setState({ rejectionCount: this.store.state.rejectionCount + 1 });
|
|
175
|
+
this.options.onReject?.(item, this);
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
173
178
|
if (this.store.state.items.length >= (this.options.maxSize ?? Infinity)) {
|
|
174
179
|
this.#setState({ rejectionCount: this.store.state.rejectionCount + 1 });
|
|
175
180
|
this.options.onReject?.(item, this);
|
|
176
181
|
return false;
|
|
177
182
|
}
|
|
178
|
-
const priority = this.options.getPriority !== defaultOptions.getPriority ? this.options.getPriority(item) : item
|
|
183
|
+
const priority = this.options.getPriority !== defaultOptions.getPriority ? this.options.getPriority(item) : item?.priority;
|
|
179
184
|
const items = this.store.state.items;
|
|
180
185
|
const itemTimestamps = this.store.state.itemTimestamps;
|
|
181
186
|
if (priority !== void 0) {
|
|
182
187
|
const insertIndex = items.findIndex((existing) => {
|
|
183
|
-
return (this.options.getPriority !== defaultOptions.getPriority ? this.options.getPriority(existing) : existing
|
|
188
|
+
return (this.options.getPriority !== defaultOptions.getPriority ? this.options.getPriority(existing) : existing?.priority) < priority;
|
|
184
189
|
});
|
|
185
190
|
if (insertIndex === -1) {
|
|
186
191
|
items.push(item);
|
package/dist/queuer.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"queuer.cjs","names":["Store","parseFunctionOrValue","#setState","#checkExpiredItems","#getWait","#timeoutId","#tick","#clearTimeout","#getAllItems"],"sources":["../src/queuer.ts"],"sourcesContent":["import { Store } from '@tanstack/store'\nimport { parseFunctionOrValue } from './utils'\nimport { emitChange, pacerEventClient } from './event-client'\n\nexport interface QueuerState<TValue> {\n /**\n * Number of times addItem has been called (for reduction calculations)\n */\n addItemCount: number\n /**\n * Number of items that have been processed by the queuer\n */\n executionCount: number\n /**\n * Number of items that have been removed from the queue due to expiration\n */\n expirationCount: number\n /**\n * Whether the queuer has no items to process (items array is empty)\n */\n isEmpty: boolean\n /**\n * Whether the queuer has reached its maximum capacity\n */\n isFull: boolean\n /**\n * Whether the queuer is not currently processing any items\n */\n isIdle: boolean\n /**\n * Whether the queuer is active and will process items automatically\n */\n isRunning: boolean\n /**\n * Array of items currently waiting to be processed\n */\n items: Array<TValue>\n /**\n * Timestamps when items were added to the queue for expiration tracking\n */\n itemTimestamps: Array<number>\n /**\n * Whether the queuer has a pending timeout for processing the next item\n */\n pendingTick: boolean\n /**\n * Number of items that have been rejected from being added to the queue\n */\n rejectionCount: number\n /**\n * Number of items currently in the queue\n */\n size: number\n /**\n * Current processing status - 'idle' when not processing, 'running' when active, 'stopped' when paused\n */\n status: 'idle' | 'running' | 'stopped'\n}\n\nfunction getDefaultQueuerState<TValue>(): QueuerState<TValue> {\n return {\n executionCount: 0,\n expirationCount: 0,\n isEmpty: true,\n isFull: false,\n isIdle: true,\n isRunning: true,\n itemTimestamps: [],\n items: [],\n pendingTick: false,\n rejectionCount: 0,\n size: 0,\n status: 'idle',\n addItemCount: 0,\n }\n}\n\n/**\n * Options for configuring a Queuer instance.\n *\n * These options control queue behavior, item expiration, callbacks, and more.\n */\nexport interface QueuerOptions<TValue> {\n /**\n * Default position to add items to the queuer\n * @default 'back'\n */\n addItemsTo?: QueuePosition\n /**\n * Maximum time in milliseconds that an item can stay in the queue\n * If not provided, items will never expire\n */\n expirationDuration?: number\n /**\n * Function to determine if an item has expired\n * If provided, this overrides the expirationDuration behavior\n */\n getIsExpired?: (item: TValue, addedAt: number) => boolean\n /**\n * Default position to get items from during processing\n * @default 'front'\n */\n getItemsFrom?: QueuePosition\n /**\n * Function to determine priority of items in the queuer\n * Higher priority items will be processed first\n */\n getPriority?: (item: TValue) => number\n /**\n * Initial items to populate the queuer with\n */\n initialItems?: Array<TValue>\n /**\n * Initial state for the queuer\n */\n initialState?: Partial<QueuerState<TValue>>\n /**\n * Optional key to identify this queuer instance.\n * If provided, the queuer will be identified by this key in the devtools and PacerProvider if applicable.\n */\n key?: string\n /**\n * Maximum number of items allowed in the queuer\n */\n maxSize?: number\n /**\n * Callback fired whenever an item is removed from the queuer\n */\n onExecute?: (item: TValue, queuer: Queuer<TValue>) => void\n /**\n * Callback fired whenever an item expires in the queuer\n */\n onExpire?: (item: TValue, queuer: Queuer<TValue>) => void\n /**\n * Callback fired whenever an item is added or removed from the queuer\n */\n onItemsChange?: (queuer: Queuer<TValue>) => void\n /**\n * Callback fired whenever an item is rejected from being added to the queuer\n */\n onReject?: (item: TValue, queuer: Queuer<TValue>) => void\n /**\n * Whether the queuer should start processing tasks immediately\n */\n started?: boolean\n /**\n * Time in milliseconds to wait between processing items.\n * Can be a number or a function that returns a number.\n * @default 0\n */\n wait?: number | ((queuer: Queuer<TValue>) => number)\n}\n\n/**\n * Utility function for sharing common `QueuerOptions` options between different `Queuer` instances.\n */\nexport function queuerOptions<\n TValue = any,\n TOptions extends Partial<QueuerOptions<TValue>> = Partial<\n QueuerOptions<TValue>\n >,\n>(options: TOptions): TOptions {\n return options\n}\n\nconst defaultOptions: Omit<\n Required<QueuerOptions<any>>,\n | 'initialState'\n | 'onExecute'\n | 'onIsRunningChange'\n | 'onItemsChange'\n | 'onReject'\n | 'onExpire'\n | 'key'\n> = {\n addItemsTo: 'back',\n getItemsFrom: 'front',\n getPriority: (item) => item?.priority ?? 0,\n getIsExpired: () => false,\n expirationDuration: Infinity,\n initialItems: [],\n maxSize: Infinity,\n started: true,\n wait: 0,\n}\n\n/**\n * Position type for addItem and getNextItem operations.\n *\n * - 'front': Operate on the front of the queue (FIFO)\n * - 'back': Operate on the back of the queue (LIFO)\n */\nexport type QueuePosition = 'front' | 'back'\n\n/**\n * A flexible queue that processes items with configurable wait times, expiration, and priority.\n *\n * This synchronous version is lighter weight and often all you need - upgrade to AsyncQueuer when you need promises, retry support, abort capabilities, concurrent execution, or advanced error handling.\n *\n * Features:\n * - Automatic or manual processing of items\n * - FIFO (First In First Out), LIFO (Last In First Out), or double-ended queue behavior\n * - Priority-based ordering when getPriority is provided\n * - Item expiration and removal of stale items\n * - Callbacks for queue state changes, execution, rejection, and expiration\n *\n * Running behavior:\n * - `start()`: Begins automatically processing items in the queue (defaults to isRunning)\n * - `stop()`: Pauses processing but maintains queue state\n * - `wait`: Configurable delay between processing items\n * - `onItemsChange`/`onExecute`: Callbacks for monitoring queue state\n *\n * Manual processing is also supported when automatic processing is disabled:\n * - `execute()`: Processes the next item using the provided function\n * - `getNextItem()`: Removes and returns the next item without processing\n *\n * Queue behavior defaults to FIFO:\n * - `addItem(item)`: Adds to the back of the queue\n * - Items processed from the front of the queue\n *\n * Priority queue:\n * - Provide a `getPriority` function; higher values are processed first\n *\n * Stack (LIFO):\n * - `addItem(item, 'back')`: Adds to the back\n * - `getNextItem('back')`: Removes from the back\n *\n * Double-ended queue:\n * - `addItem(item, position)`: Adds to specified position ('front'/'back')\n * - `getNextItem(position)`: Removes from specified position\n *\n * Item expiration:\n * - `expirationDuration`: Maximum time items can stay in the queue\n * - `getIsExpired`: Function to override default expiration\n * - `onExpire`: Callback for expired items\n *\n * State Management:\n * - Uses TanStack Store for reactive state management\n * - Use `initialState` to provide initial state values when creating the queuer\n * - Use `onExecute` callback to react to item execution and implement custom logic\n * - Use `onItemsChange` callback to react to items being added or removed from the queue\n * - Use `onExpire` callback to react to items expiring and implement custom logic\n * - Use `onReject` callback to react to items being rejected when the queue is full\n * - The state includes execution count, expiration count, rejection count, and isRunning status\n * - State can be accessed via `queuer.store.state` when using the class directly\n * - When using framework adapters (React/Solid), state is accessed from `queuer.state`\n *\n * Example usage:\n * ```ts\n * // Auto-processing queue with wait time\n * const autoQueue = new Queuer<number>((n) => console.log(n), {\n * started: true, // Begin processing immediately\n * wait: 1000, // Wait 1s between items\n * onExecute: (item, queuer) => console.log(`Processed ${item}`)\n * });\n * autoQueue.addItem(1); // Will process after 1s\n * autoQueue.addItem(2); // Will process 1s after first item\n *\n * // Manual processing queue\n * const manualQueue = new Queuer<number>((n) => console.log(n), {\n * started: false\n * });\n * manualQueue.addItem(1); // [1]\n * manualQueue.addItem(2); // [1, 2]\n * manualQueue.execute(); // logs 1, queue is [2]\n * manualQueue.getNextItem(); // returns 2, queue is empty\n * ```\n */\nexport class Queuer<TValue> {\n readonly store: Store<Readonly<QueuerState<TValue>>> = new Store(\n getDefaultQueuerState<TValue>(),\n )\n key: string | undefined\n options: QueuerOptions<TValue>\n #timeoutId: ReturnType<typeof setTimeout> | null = null\n\n constructor(\n public fn: (item: TValue) => void,\n initialOptions: QueuerOptions<TValue> = {},\n ) {\n this.key = initialOptions.key\n this.options = {\n ...defaultOptions,\n ...initialOptions,\n }\n const isInitiallyRunning =\n this.options.initialState?.isRunning ?? this.options.started ?? true\n this.#setState({\n ...this.options.initialState,\n isRunning: isInitiallyRunning,\n })\n\n if (this.options.initialState?.items) {\n if (this.store.state.isRunning) {\n this.#tick()\n }\n } else {\n for (let i = 0; i < (this.options.initialItems?.length ?? 0); i++) {\n const item = this.options.initialItems![i]!\n const isLast = i === (this.options.initialItems?.length ?? 0) - 1\n this.addItem(item, this.options.addItemsTo ?? 'back', isLast)\n }\n }\n\n if (this.key) {\n pacerEventClient.on('d-Queuer', (event) => {\n if (event.payload.key !== this.key) return\n this.#setState(\n event.payload.store.state as Partial<QueuerState<TValue>>,\n )\n this.setOptions(event.payload.options as Partial<QueuerOptions<TValue>>)\n })\n }\n }\n\n /**\n * Updates the queuer options. New options are merged with existing options.\n */\n setOptions = (newOptions: Partial<QueuerOptions<TValue>>): void => {\n this.options = { ...this.options, ...newOptions }\n }\n\n #setState = (newState: Partial<QueuerState<TValue>>): void => {\n this.store.setState((state) => {\n const combinedState = {\n ...state,\n ...newState,\n }\n\n const { items, isRunning } = combinedState\n\n const size = items.length\n const isFull = size >= (this.options.maxSize ?? Infinity)\n const isEmpty = size === 0\n const isIdle = isRunning && isEmpty\n\n const status = isIdle ? 'idle' : isRunning ? 'running' : 'stopped'\n\n return {\n ...combinedState,\n isEmpty,\n isFull,\n isIdle,\n size,\n status,\n }\n })\n emitChange('Queuer', this)\n }\n\n /**\n * Returns the current wait time (in milliseconds) between processing items.\n * If a function is provided, it is called with the queuer instance.\n */\n #getWait = (): number => {\n return parseFunctionOrValue(this.options.wait ?? 0, this)\n }\n\n /**\n * Processes items in the queue up to the wait interval. Internal use only.\n */\n #tick = () => {\n if (!this.store.state.isRunning) {\n this.#setState({ pendingTick: false })\n return\n }\n\n this.#setState({ pendingTick: true })\n\n // Check for expired items\n this.#checkExpiredItems()\n\n while (this.store.state.items.length > 0) {\n const nextItem = this.execute(this.options.getItemsFrom ?? 'front')\n if (nextItem === undefined) {\n break\n }\n\n const wait = this.#getWait()\n if (wait > 0) {\n // Use setTimeout to wait before processing next item\n this.#timeoutId = setTimeout(() => this.#tick(), wait)\n return\n }\n\n this.#tick()\n }\n this.#setState({ pendingTick: false })\n }\n\n /**\n * Adds an item to the queue. If the queue is full, the item is rejected and onReject is called.\n * Items can be inserted based on priority or at the front/back depending on configuration.\n *\n * Returns true if the item was added, false if the queue is full.\n *\n * Example usage:\n * ```ts\n * queuer.addItem('task');\n * queuer.addItem('task2', 'front');\n * ```\n */\n addItem = (\n item: TValue,\n position: QueuePosition = this.options.addItemsTo ?? 'back',\n runOnItemsChange: boolean = true,\n ): boolean => {\n this.#setState({\n addItemCount: this.store.state.addItemCount + 1,\n })\n\n if (this.store.state.items.length >= (this.options.maxSize ?? Infinity)) {\n this.#setState({\n rejectionCount: this.store.state.rejectionCount + 1,\n })\n this.options.onReject?.(item, this)\n return false\n }\n\n // Get priority either from the function or from getPriority option\n const priority =\n this.options.getPriority !== defaultOptions.getPriority\n ? this.options.getPriority!(item)\n : (item as any).priority\n\n const items = this.store.state.items\n const itemTimestamps = this.store.state.itemTimestamps\n\n if (priority !== undefined) {\n // Insert based on priority - higher priority items go to front\n const insertIndex = items.findIndex((existing) => {\n const existingPriority: number =\n this.options.getPriority !== defaultOptions.getPriority\n ? this.options.getPriority!(existing)\n : (existing as any).priority\n return existingPriority < priority\n })\n\n if (insertIndex === -1) {\n items.push(item)\n itemTimestamps.push(Date.now())\n } else {\n items.splice(insertIndex, 0, item)\n itemTimestamps.splice(insertIndex, 0, Date.now())\n }\n } else {\n if (position === 'front') {\n // Default FIFO/LIFO behavior\n items.unshift(item)\n itemTimestamps.unshift(Date.now())\n } else {\n // LIFO\n items.push(item)\n itemTimestamps.push(Date.now())\n }\n }\n\n this.#setState({\n items,\n itemTimestamps,\n })\n\n if (runOnItemsChange) {\n this.options.onItemsChange?.(this)\n }\n\n if (this.store.state.isRunning && !this.store.state.pendingTick) {\n this.#setState({ pendingTick: true })\n this.#tick()\n }\n\n return true\n }\n\n /**\n * Removes and returns the next item from the queue without executing the function.\n * Use for manual queue management. Normally, use execute() to process items.\n *\n * Example usage:\n * ```ts\n * // FIFO\n * queuer.getNextItem();\n * // LIFO\n * queuer.getNextItem('back');\n * ```\n */\n getNextItem = (\n position: QueuePosition = this.options.getItemsFrom ?? 'front',\n ): TValue | undefined => {\n const { items, itemTimestamps } = this.store.state\n let item: TValue | undefined\n\n // When priority function is provided, always get from front (highest priority)\n // Priority takes precedence over FIFO/LIFO behavior\n if (\n this.options.getPriority !== defaultOptions.getPriority ||\n position === 'front'\n ) {\n item = items[0]\n if (item !== undefined) {\n this.#setState({\n items: items.slice(1),\n itemTimestamps: itemTimestamps.slice(1),\n })\n }\n } else {\n item = items[items.length - 1]\n if (item !== undefined) {\n this.#setState({\n items: items.slice(0, -1),\n itemTimestamps: itemTimestamps.slice(0, -1),\n })\n }\n }\n\n if (item !== undefined) {\n this.options.onItemsChange?.(this)\n }\n\n return item\n }\n\n #getAllItems = (): Array<TValue> => {\n const items = this.peekAllItems()\n this.clear()\n return items\n }\n\n /**\n * Removes and returns the next item from the queue and processes it using the provided function.\n *\n * Example usage:\n * ```ts\n * queuer.execute();\n * // LIFO\n * queuer.execute('back');\n * ```\n */\n execute = (position?: QueuePosition): TValue | undefined => {\n const item = this.getNextItem(position)\n if (item !== undefined) {\n this.fn(item)\n this.#setState({\n executionCount: this.store.state.executionCount + 1,\n })\n this.options.onExecute?.(item, this)\n }\n return item\n }\n\n /**\n * Processes a specified number of items to execute immediately with no wait time\n * If no numberOfItems is provided, all items will be processed\n */\n flush = (\n numberOfItems: number = this.store.state.items.length,\n position?: QueuePosition,\n ): void => {\n this.#clearTimeout() // clear any pending timeout\n for (let i = 0; i < numberOfItems; i++) {\n this.execute(position)\n }\n this.#tick()\n }\n\n /**\n * Processes all items in the queue as a batch using the provided function as an argument\n * The queue is cleared after processing\n */\n flushAsBatch = (batchFunction: (items: Array<TValue>) => void): void => {\n const items = this.#getAllItems()\n this.clear()\n batchFunction(items)\n }\n\n /**\n * Checks for expired items in the queue and removes them. Calls onExpire for each expired item.\n * Internal use only.\n */\n #checkExpiredItems = (): void => {\n if (\n (this.options.expirationDuration ?? Infinity) === Infinity &&\n this.options.getIsExpired === defaultOptions.getIsExpired\n ) {\n return\n }\n\n const now = Date.now()\n const expiredIndices: Array<number> = []\n\n // Find indices of expired items\n for (let i = 0; i < this.store.state.items.length; i++) {\n const timestamp = this.store.state.itemTimestamps[i]\n if (timestamp === undefined) continue\n\n const item = this.store.state.items[i]\n if (item === undefined) continue\n\n const isExpired =\n this.options.getIsExpired !== defaultOptions.getIsExpired\n ? this.options.getIsExpired!(item, timestamp)\n : now - timestamp > (this.options.expirationDuration ?? Infinity)\n\n if (isExpired) {\n expiredIndices.push(i)\n }\n }\n\n // Remove expired items from back to front to maintain indices\n for (let i = expiredIndices.length - 1; i >= 0; i--) {\n const index = expiredIndices[i]\n if (index === undefined) continue\n\n const expiredItem = this.store.state.items[index]\n if (expiredItem === undefined) continue\n\n const newItems = [...this.store.state.items]\n const newTimestamps = [...this.store.state.itemTimestamps]\n newItems.splice(index, 1)\n newTimestamps.splice(index, 1)\n this.#setState({\n items: newItems,\n itemTimestamps: newTimestamps,\n expirationCount: this.store.state.expirationCount + 1,\n })\n this.options.onExpire?.(expiredItem, this)\n }\n\n if (expiredIndices.length > 0) {\n this.options.onItemsChange?.(this)\n }\n }\n\n /**\n * Returns the next item in the queue without removing it.\n *\n * Example usage:\n * ```ts\n * queuer.peekNextItem(); // front\n * queuer.peekNextItem('back'); // back\n * ```\n */\n peekNextItem = (position: QueuePosition = 'front'): TValue | undefined => {\n if (position === 'front') {\n return this.store.state.items[0]\n }\n return this.store.state.items[this.store.state.items.length - 1]\n }\n\n /**\n * Returns a copy of all items in the queue.\n */\n peekAllItems = (): Array<TValue> => {\n return [...this.store.state.items]\n }\n\n /**\n * Starts processing items in the queue. If already isRunning, does nothing.\n */\n start = () => {\n this.#setState({ isRunning: true })\n if (!this.store.state.pendingTick && this.store.state.items.length > 0) {\n this.#tick()\n }\n }\n\n /**\n * Stops processing items in the queue. Does not clear the queue.\n */\n stop = () => {\n this.#clearTimeout()\n this.#setState({ isRunning: false, pendingTick: false })\n }\n\n #clearTimeout = (): void => {\n if (this.#timeoutId) {\n clearTimeout(this.#timeoutId)\n this.#timeoutId = null\n }\n }\n\n /**\n * Removes all pending items from the queue. Does not affect items being processed.\n */\n clear = (): void => {\n this.#setState({ items: [], itemTimestamps: [] })\n this.options.onItemsChange?.(this)\n }\n\n /**\n * Resets the queuer state to its default values\n */\n reset = (): void => {\n this.#setState(getDefaultQueuerState<TValue>())\n this.options.onItemsChange?.(this)\n }\n}\n\n/**\n * Creates a queue that processes items immediately upon addition.\n * Items are processed sequentially in FIFO order by default.\n *\n * This synchronous version is lighter weight and often all you need - upgrade to asyncQueue when you need promises, retry support, abort capabilities, concurrent execution, or advanced error handling.\n *\n * State Management:\n * - Uses TanStack Store for reactive state management\n * - Use `initialState` to provide initial state values when creating the queuer\n * - Use `onExecute` callback to react to item execution and implement custom logic\n * - Use `onItemsChange` callback to react to items being added or removed from the queue\n * - Use `onExpire` callback to react to items expiring and implement custom logic\n * - Use `onReject` callback to react to items being rejected when the queue is full\n * - The state includes execution count, expiration count, rejection count, and isRunning status\n * - State can be accessed via the underlying Queuer instance's `store.state` property\n * - When using framework adapters (React/Solid), state is accessed from the hook's state property\n *\n * Example usage:\n * ```ts\n * // Basic sequential processing\n * const processItems = queue<number>((n) => console.log(n), {\n * wait: 1000,\n * onItemsChange: (queuer) => console.log(queuer.peekAllItems())\n * });\n * processItems(1); // Logs: 1\n * processItems(2); // Logs: 2 after 1 completes\n *\n * // Priority queue\n * const processPriority = queue<number>((n) => console.log(n), {\n * getPriority: n => n // Higher numbers processed first\n * });\n * processPriority(1);\n * processPriority(3); // Processed before 1\n * ```\n */\nexport function queue<TValue>(\n fn: (item: TValue) => void,\n initialOptions: QueuerOptions<TValue>,\n) {\n const queuer = new Queuer<TValue>(fn, initialOptions)\n return queuer.addItem\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"queuer.cjs","names":["Store","parseFunctionOrValue","#setState","#checkExpiredItems","#getWait","#timeoutId","#tick","#clearTimeout","#getAllItems"],"sources":["../src/queuer.ts"],"sourcesContent":["import { Store } from '@tanstack/store'\nimport { parseFunctionOrValue } from './utils'\nimport { emitChange, pacerEventClient } from './event-client'\n\nexport interface QueuerState<TValue> {\n /**\n * Number of times addItem has been called (for reduction calculations)\n */\n addItemCount: number\n /**\n * Number of items that have been processed by the queuer\n */\n executionCount: number\n /**\n * Number of items that have been removed from the queue due to expiration\n */\n expirationCount: number\n /**\n * Whether the queuer has no items to process (items array is empty)\n */\n isEmpty: boolean\n /**\n * Whether the queuer has reached its maximum capacity\n */\n isFull: boolean\n /**\n * Whether the queuer is not currently processing any items\n */\n isIdle: boolean\n /**\n * Whether the queuer is active and will process items automatically\n */\n isRunning: boolean\n /**\n * Array of items currently waiting to be processed\n */\n items: Array<TValue>\n /**\n * Timestamps when items were added to the queue for expiration tracking\n */\n itemTimestamps: Array<number>\n /**\n * Whether the queuer has a pending timeout for processing the next item\n */\n pendingTick: boolean\n /**\n * Number of items that have been rejected from being added to the queue\n */\n rejectionCount: number\n /**\n * Number of items currently in the queue\n */\n size: number\n /**\n * Current processing status - 'idle' when not processing, 'running' when active, 'stopped' when paused\n */\n status: 'idle' | 'running' | 'stopped'\n}\n\nfunction getDefaultQueuerState<TValue>(): QueuerState<TValue> {\n return {\n executionCount: 0,\n expirationCount: 0,\n isEmpty: true,\n isFull: false,\n isIdle: true,\n isRunning: true,\n itemTimestamps: [],\n items: [],\n pendingTick: false,\n rejectionCount: 0,\n size: 0,\n status: 'idle',\n addItemCount: 0,\n }\n}\n\n/**\n * Options for configuring a Queuer instance.\n *\n * These options control queue behavior, item expiration, callbacks, and more.\n */\nexport interface QueuerOptions<TValue> {\n /**\n * Default position to add items to the queuer\n * @default 'back'\n */\n addItemsTo?: QueuePosition\n /**\n * Maximum time in milliseconds that an item can stay in the queue\n * If not provided, items will never expire\n */\n expirationDuration?: number\n /**\n * Function to determine if an item has expired\n * If provided, this overrides the expirationDuration behavior\n */\n getIsExpired?: (item: TValue, addedAt: number) => boolean\n /**\n * Default position to get items from during processing\n * @default 'front'\n */\n getItemsFrom?: QueuePosition\n /**\n * Function to determine priority of items in the queuer\n * Higher priority items will be processed first\n */\n getPriority?: (item: TValue) => number\n /**\n * Initial items to populate the queuer with\n */\n initialItems?: Array<TValue>\n /**\n * Initial state for the queuer\n */\n initialState?: Partial<QueuerState<TValue>>\n /**\n * Optional key to identify this queuer instance.\n * If provided, the queuer will be identified by this key in the devtools and PacerProvider if applicable.\n */\n key?: string\n /**\n * Maximum number of items allowed in the queuer\n */\n maxSize?: number\n /**\n * Callback fired whenever an item is removed from the queuer\n */\n onExecute?: (item: TValue, queuer: Queuer<TValue>) => void\n /**\n * Callback fired whenever an item expires in the queuer\n */\n onExpire?: (item: TValue, queuer: Queuer<TValue>) => void\n /**\n * Callback fired whenever an item is added or removed from the queuer\n */\n onItemsChange?: (queuer: Queuer<TValue>) => void\n /**\n * Callback fired whenever an item is rejected from being added to the queuer\n */\n onReject?: (item: TValue, queuer: Queuer<TValue>) => void\n /**\n * Whether the queuer should start processing tasks immediately\n */\n started?: boolean\n /**\n * Time in milliseconds to wait between processing items.\n * Can be a number or a function that returns a number.\n * @default 0\n */\n wait?: number | ((queuer: Queuer<TValue>) => number)\n}\n\n/**\n * Utility function for sharing common `QueuerOptions` options between different `Queuer` instances.\n */\nexport function queuerOptions<\n TValue = any,\n TOptions extends Partial<QueuerOptions<TValue>> = Partial<\n QueuerOptions<TValue>\n >,\n>(options: TOptions): TOptions {\n return options\n}\n\nconst defaultOptions: Omit<\n Required<QueuerOptions<any>>,\n | 'initialState'\n | 'onExecute'\n | 'onIsRunningChange'\n | 'onItemsChange'\n | 'onReject'\n | 'onExpire'\n | 'key'\n> = {\n addItemsTo: 'back',\n getItemsFrom: 'front',\n getPriority: (item) => item?.priority ?? 0,\n getIsExpired: () => false,\n expirationDuration: Infinity,\n initialItems: [],\n maxSize: Infinity,\n started: true,\n wait: 0,\n}\n\n/**\n * Position type for addItem and getNextItem operations.\n *\n * - 'front': Operate on the front of the queue (FIFO)\n * - 'back': Operate on the back of the queue (LIFO)\n */\nexport type QueuePosition = 'front' | 'back'\n\n/**\n * A flexible queue that processes items with configurable wait times, expiration, and priority.\n *\n * This synchronous version is lighter weight and often all you need - upgrade to AsyncQueuer when you need promises, retry support, abort capabilities, concurrent execution, or advanced error handling.\n *\n * Features:\n * - Automatic or manual processing of items\n * - FIFO (First In First Out), LIFO (Last In First Out), or double-ended queue behavior\n * - Priority-based ordering when getPriority is provided\n * - Item expiration and removal of stale items\n * - Callbacks for queue state changes, execution, rejection, and expiration\n *\n * Running behavior:\n * - `start()`: Begins automatically processing items in the queue (defaults to isRunning)\n * - `stop()`: Pauses processing but maintains queue state\n * - `wait`: Configurable delay between processing items\n * - `onItemsChange`/`onExecute`: Callbacks for monitoring queue state\n *\n * Manual processing is also supported when automatic processing is disabled:\n * - `execute()`: Processes the next item using the provided function\n * - `getNextItem()`: Removes and returns the next item without processing\n *\n * Queue behavior defaults to FIFO:\n * - `addItem(item)`: Adds to the back of the queue\n * - Items processed from the front of the queue\n *\n * Priority queue:\n * - Provide a `getPriority` function; higher values are processed first\n *\n * Stack (LIFO):\n * - `addItem(item, 'back')`: Adds to the back\n * - `getNextItem('back')`: Removes from the back\n *\n * Double-ended queue:\n * - `addItem(item, position)`: Adds to specified position ('front'/'back')\n * - `getNextItem(position)`: Removes from specified position\n *\n * Item expiration:\n * - `expirationDuration`: Maximum time items can stay in the queue\n * - `getIsExpired`: Function to override default expiration\n * - `onExpire`: Callback for expired items\n *\n * State Management:\n * - Uses TanStack Store for reactive state management\n * - Use `initialState` to provide initial state values when creating the queuer\n * - Use `onExecute` callback to react to item execution and implement custom logic\n * - Use `onItemsChange` callback to react to items being added or removed from the queue\n * - Use `onExpire` callback to react to items expiring and implement custom logic\n * - Use `onReject` callback to react to items being rejected when the queue is full\n * - The state includes execution count, expiration count, rejection count, and isRunning status\n * - State can be accessed via `queuer.store.state` when using the class directly\n * - When using framework adapters (React/Solid), state is accessed from `queuer.state`\n *\n * Example usage:\n * ```ts\n * // Auto-processing queue with wait time\n * const autoQueue = new Queuer<number>((n) => console.log(n), {\n * started: true, // Begin processing immediately\n * wait: 1000, // Wait 1s between items\n * onExecute: (item, queuer) => console.log(`Processed ${item}`)\n * });\n * autoQueue.addItem(1); // Will process after 1s\n * autoQueue.addItem(2); // Will process 1s after first item\n *\n * // Manual processing queue\n * const manualQueue = new Queuer<number>((n) => console.log(n), {\n * started: false\n * });\n * manualQueue.addItem(1); // [1]\n * manualQueue.addItem(2); // [1, 2]\n * manualQueue.execute(); // logs 1, queue is [2]\n * manualQueue.getNextItem(); // returns 2, queue is empty\n * ```\n */\nexport class Queuer<TValue> {\n readonly store: Store<Readonly<QueuerState<TValue>>> = new Store(\n getDefaultQueuerState<TValue>(),\n )\n key: string | undefined\n options: QueuerOptions<TValue>\n #timeoutId: ReturnType<typeof setTimeout> | null = null\n\n constructor(\n public fn: (item: TValue) => void,\n initialOptions: QueuerOptions<TValue> = {},\n ) {\n this.key = initialOptions.key\n this.options = {\n ...defaultOptions,\n ...initialOptions,\n }\n const isInitiallyRunning =\n this.options.initialState?.isRunning ?? this.options.started ?? true\n this.#setState({\n ...this.options.initialState,\n isRunning: isInitiallyRunning,\n })\n\n if (this.options.initialState?.items) {\n if (this.store.state.isRunning) {\n this.#tick()\n }\n } else {\n for (let i = 0; i < (this.options.initialItems?.length ?? 0); i++) {\n const item = this.options.initialItems![i]!\n const isLast = i === (this.options.initialItems?.length ?? 0) - 1\n this.addItem(item, this.options.addItemsTo ?? 'back', isLast)\n }\n }\n\n if (this.key) {\n pacerEventClient.on('d-Queuer', (event) => {\n if (event.payload.key !== this.key) return\n this.#setState(\n event.payload.store.state as Partial<QueuerState<TValue>>,\n )\n this.setOptions(event.payload.options as Partial<QueuerOptions<TValue>>)\n })\n }\n }\n\n /**\n * Updates the queuer options. New options are merged with existing options.\n */\n setOptions = (newOptions: Partial<QueuerOptions<TValue>>): void => {\n this.options = { ...this.options, ...newOptions }\n }\n\n #setState = (newState: Partial<QueuerState<TValue>>): void => {\n this.store.setState((state) => {\n const combinedState = {\n ...state,\n ...newState,\n }\n\n const { items, isRunning } = combinedState\n\n const size = items.length\n const isFull = size >= (this.options.maxSize ?? Infinity)\n const isEmpty = size === 0\n const isIdle = isRunning && isEmpty\n\n const status = isIdle ? 'idle' : isRunning ? 'running' : 'stopped'\n\n return {\n ...combinedState,\n isEmpty,\n isFull,\n isIdle,\n size,\n status,\n }\n })\n emitChange('Queuer', this)\n }\n\n /**\n * Returns the current wait time (in milliseconds) between processing items.\n * If a function is provided, it is called with the queuer instance.\n */\n #getWait = (): number => {\n return parseFunctionOrValue(this.options.wait ?? 0, this)\n }\n\n /**\n * Processes items in the queue up to the wait interval. Internal use only.\n */\n #tick = () => {\n if (!this.store.state.isRunning) {\n this.#setState({ pendingTick: false })\n return\n }\n\n this.#setState({ pendingTick: true })\n\n // Check for expired items\n this.#checkExpiredItems()\n\n while (this.store.state.items.length > 0) {\n const nextItem = this.execute(this.options.getItemsFrom ?? 'front')\n if (nextItem === undefined) {\n break\n }\n\n const wait = this.#getWait()\n if (wait > 0) {\n // Use setTimeout to wait before processing next item\n this.#timeoutId = setTimeout(() => this.#tick(), wait)\n return\n }\n\n this.#tick()\n }\n this.#setState({ pendingTick: false })\n }\n\n /**\n * Adds an item to the queue. If the queue is full, the item is rejected and onReject is called.\n * `undefined` cannot be queued (it is the internal \"no item\" sentinel) and is always rejected.\n * Items can be inserted based on priority or at the front/back depending on configuration.\n *\n * Returns true if the item was added, false if the queue is full.\n *\n * Example usage:\n * ```ts\n * queuer.addItem('task');\n * queuer.addItem('task2', 'front');\n * ```\n */\n addItem = (\n item: TValue,\n position: QueuePosition = this.options.addItemsTo ?? 'back',\n runOnItemsChange: boolean = true,\n ): boolean => {\n this.#setState({\n addItemCount: this.store.state.addItemCount + 1,\n })\n\n // undefined is the internal \"no item\" sentinel (peekNextItem/getNextItem);\n // queuing it would break the processing loop and block items behind it\n if (item === undefined) {\n this.#setState({\n rejectionCount: this.store.state.rejectionCount + 1,\n })\n this.options.onReject?.(item, this)\n return false\n }\n\n if (this.store.state.items.length >= (this.options.maxSize ?? Infinity)) {\n this.#setState({\n rejectionCount: this.store.state.rejectionCount + 1,\n })\n this.options.onReject?.(item, this)\n return false\n }\n\n // Get priority either from the function or from getPriority option\n const priority =\n this.options.getPriority !== defaultOptions.getPriority\n ? this.options.getPriority!(item)\n : (item as any)?.priority\n\n const items = this.store.state.items\n const itemTimestamps = this.store.state.itemTimestamps\n\n if (priority !== undefined) {\n // Insert based on priority - higher priority items go to front\n const insertIndex = items.findIndex((existing) => {\n const existingPriority: number =\n this.options.getPriority !== defaultOptions.getPriority\n ? this.options.getPriority!(existing)\n : (existing as any)?.priority\n return existingPriority < priority\n })\n\n if (insertIndex === -1) {\n items.push(item)\n itemTimestamps.push(Date.now())\n } else {\n items.splice(insertIndex, 0, item)\n itemTimestamps.splice(insertIndex, 0, Date.now())\n }\n } else {\n if (position === 'front') {\n // Default FIFO/LIFO behavior\n items.unshift(item)\n itemTimestamps.unshift(Date.now())\n } else {\n // LIFO\n items.push(item)\n itemTimestamps.push(Date.now())\n }\n }\n\n this.#setState({\n items,\n itemTimestamps,\n })\n\n if (runOnItemsChange) {\n this.options.onItemsChange?.(this)\n }\n\n if (this.store.state.isRunning && !this.store.state.pendingTick) {\n this.#setState({ pendingTick: true })\n this.#tick()\n }\n\n return true\n }\n\n /**\n * Removes and returns the next item from the queue without executing the function.\n * Use for manual queue management. Normally, use execute() to process items.\n *\n * Example usage:\n * ```ts\n * // FIFO\n * queuer.getNextItem();\n * // LIFO\n * queuer.getNextItem('back');\n * ```\n */\n getNextItem = (\n position: QueuePosition = this.options.getItemsFrom ?? 'front',\n ): TValue | undefined => {\n const { items, itemTimestamps } = this.store.state\n let item: TValue | undefined\n\n // When priority function is provided, always get from front (highest priority)\n // Priority takes precedence over FIFO/LIFO behavior\n if (\n this.options.getPriority !== defaultOptions.getPriority ||\n position === 'front'\n ) {\n item = items[0]\n if (item !== undefined) {\n this.#setState({\n items: items.slice(1),\n itemTimestamps: itemTimestamps.slice(1),\n })\n }\n } else {\n item = items[items.length - 1]\n if (item !== undefined) {\n this.#setState({\n items: items.slice(0, -1),\n itemTimestamps: itemTimestamps.slice(0, -1),\n })\n }\n }\n\n if (item !== undefined) {\n this.options.onItemsChange?.(this)\n }\n\n return item\n }\n\n #getAllItems = (): Array<TValue> => {\n const items = this.peekAllItems()\n this.clear()\n return items\n }\n\n /**\n * Removes and returns the next item from the queue and processes it using the provided function.\n *\n * Example usage:\n * ```ts\n * queuer.execute();\n * // LIFO\n * queuer.execute('back');\n * ```\n */\n execute = (position?: QueuePosition): TValue | undefined => {\n const item = this.getNextItem(position)\n if (item !== undefined) {\n this.fn(item)\n this.#setState({\n executionCount: this.store.state.executionCount + 1,\n })\n this.options.onExecute?.(item, this)\n }\n return item\n }\n\n /**\n * Processes a specified number of items to execute immediately with no wait time\n * If no numberOfItems is provided, all items will be processed\n */\n flush = (\n numberOfItems: number = this.store.state.items.length,\n position?: QueuePosition,\n ): void => {\n this.#clearTimeout() // clear any pending timeout\n for (let i = 0; i < numberOfItems; i++) {\n this.execute(position)\n }\n this.#tick()\n }\n\n /**\n * Processes all items in the queue as a batch using the provided function as an argument\n * The queue is cleared after processing\n */\n flushAsBatch = (batchFunction: (items: Array<TValue>) => void): void => {\n const items = this.#getAllItems()\n this.clear()\n batchFunction(items)\n }\n\n /**\n * Checks for expired items in the queue and removes them. Calls onExpire for each expired item.\n * Internal use only.\n */\n #checkExpiredItems = (): void => {\n if (\n (this.options.expirationDuration ?? Infinity) === Infinity &&\n this.options.getIsExpired === defaultOptions.getIsExpired\n ) {\n return\n }\n\n const now = Date.now()\n const expiredIndices: Array<number> = []\n\n // Find indices of expired items\n for (let i = 0; i < this.store.state.items.length; i++) {\n const timestamp = this.store.state.itemTimestamps[i]\n if (timestamp === undefined) continue\n\n const item = this.store.state.items[i]\n if (item === undefined) continue\n\n const isExpired =\n this.options.getIsExpired !== defaultOptions.getIsExpired\n ? this.options.getIsExpired!(item, timestamp)\n : now - timestamp > (this.options.expirationDuration ?? Infinity)\n\n if (isExpired) {\n expiredIndices.push(i)\n }\n }\n\n // Remove expired items from back to front to maintain indices\n for (let i = expiredIndices.length - 1; i >= 0; i--) {\n const index = expiredIndices[i]\n if (index === undefined) continue\n\n const expiredItem = this.store.state.items[index]\n if (expiredItem === undefined) continue\n\n const newItems = [...this.store.state.items]\n const newTimestamps = [...this.store.state.itemTimestamps]\n newItems.splice(index, 1)\n newTimestamps.splice(index, 1)\n this.#setState({\n items: newItems,\n itemTimestamps: newTimestamps,\n expirationCount: this.store.state.expirationCount + 1,\n })\n this.options.onExpire?.(expiredItem, this)\n }\n\n if (expiredIndices.length > 0) {\n this.options.onItemsChange?.(this)\n }\n }\n\n /**\n * Returns the next item in the queue without removing it.\n *\n * Example usage:\n * ```ts\n * queuer.peekNextItem(); // front\n * queuer.peekNextItem('back'); // back\n * ```\n */\n peekNextItem = (position: QueuePosition = 'front'): TValue | undefined => {\n if (position === 'front') {\n return this.store.state.items[0]\n }\n return this.store.state.items[this.store.state.items.length - 1]\n }\n\n /**\n * Returns a copy of all items in the queue.\n */\n peekAllItems = (): Array<TValue> => {\n return [...this.store.state.items]\n }\n\n /**\n * Starts processing items in the queue. If already isRunning, does nothing.\n */\n start = () => {\n this.#setState({ isRunning: true })\n if (!this.store.state.pendingTick && this.store.state.items.length > 0) {\n this.#tick()\n }\n }\n\n /**\n * Stops processing items in the queue. Does not clear the queue.\n */\n stop = () => {\n this.#clearTimeout()\n this.#setState({ isRunning: false, pendingTick: false })\n }\n\n #clearTimeout = (): void => {\n if (this.#timeoutId) {\n clearTimeout(this.#timeoutId)\n this.#timeoutId = null\n }\n }\n\n /**\n * Removes all pending items from the queue. Does not affect items being processed.\n */\n clear = (): void => {\n this.#setState({ items: [], itemTimestamps: [] })\n this.options.onItemsChange?.(this)\n }\n\n /**\n * Resets the queuer state to its default values\n */\n reset = (): void => {\n this.#setState(getDefaultQueuerState<TValue>())\n this.options.onItemsChange?.(this)\n }\n}\n\n/**\n * Creates a queue that processes items immediately upon addition.\n * Items are processed sequentially in FIFO order by default.\n *\n * This synchronous version is lighter weight and often all you need - upgrade to asyncQueue when you need promises, retry support, abort capabilities, concurrent execution, or advanced error handling.\n *\n * State Management:\n * - Uses TanStack Store for reactive state management\n * - Use `initialState` to provide initial state values when creating the queuer\n * - Use `onExecute` callback to react to item execution and implement custom logic\n * - Use `onItemsChange` callback to react to items being added or removed from the queue\n * - Use `onExpire` callback to react to items expiring and implement custom logic\n * - Use `onReject` callback to react to items being rejected when the queue is full\n * - The state includes execution count, expiration count, rejection count, and isRunning status\n * - State can be accessed via the underlying Queuer instance's `store.state` property\n * - When using framework adapters (React/Solid), state is accessed from the hook's state property\n *\n * Example usage:\n * ```ts\n * // Basic sequential processing\n * const processItems = queue<number>((n) => console.log(n), {\n * wait: 1000,\n * onItemsChange: (queuer) => console.log(queuer.peekAllItems())\n * });\n * processItems(1); // Logs: 1\n * processItems(2); // Logs: 2 after 1 completes\n *\n * // Priority queue\n * const processPriority = queue<number>((n) => console.log(n), {\n * getPriority: n => n // Higher numbers processed first\n * });\n * processPriority(1);\n * processPriority(3); // Processed before 1\n * ```\n */\nexport function queue<TValue>(\n fn: (item: TValue) => void,\n initialOptions: QueuerOptions<TValue>,\n) {\n const queuer = new Queuer<TValue>(fn, initialOptions)\n return queuer.addItem\n}\n"],"mappings":"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"}
|
package/dist/queuer.d.cts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Store } from "@tanstack/store";
|
|
2
|
-
|
|
3
2
|
//#region src/queuer.d.ts
|
|
4
3
|
interface QueuerState<TValue> {
|
|
5
4
|
/**
|
|
@@ -228,6 +227,7 @@ declare class Queuer<TValue> {
|
|
|
228
227
|
setOptions: (newOptions: Partial<QueuerOptions<TValue>>) => void;
|
|
229
228
|
/**
|
|
230
229
|
* Adds an item to the queue. If the queue is full, the item is rejected and onReject is called.
|
|
230
|
+
* `undefined` cannot be queued (it is the internal "no item" sentinel) and is always rejected.
|
|
231
231
|
* Items can be inserted based on priority or at the front/back depending on configuration.
|
|
232
232
|
*
|
|
233
233
|
* Returns true if the item was added, false if the queue is full.
|
package/dist/queuer.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Store } from "@tanstack/store";
|
|
2
|
-
|
|
3
2
|
//#region src/queuer.d.ts
|
|
4
3
|
interface QueuerState<TValue> {
|
|
5
4
|
/**
|
|
@@ -228,6 +227,7 @@ declare class Queuer<TValue> {
|
|
|
228
227
|
setOptions: (newOptions: Partial<QueuerOptions<TValue>>) => void;
|
|
229
228
|
/**
|
|
230
229
|
* Adds an item to the queue. If the queue is full, the item is rejected and onReject is called.
|
|
230
|
+
* `undefined` cannot be queued (it is the internal "no item" sentinel) and is always rejected.
|
|
231
231
|
* Items can be inserted based on priority or at the front/back depending on configuration.
|
|
232
232
|
*
|
|
233
233
|
* Returns true if the item was added, false if the queue is full.
|
package/dist/queuer.js
CHANGED
|
@@ -169,17 +169,22 @@ var Queuer = class {
|
|
|
169
169
|
};
|
|
170
170
|
this.addItem = (item, position = this.options.addItemsTo ?? "back", runOnItemsChange = true) => {
|
|
171
171
|
this.#setState({ addItemCount: this.store.state.addItemCount + 1 });
|
|
172
|
+
if (item === void 0) {
|
|
173
|
+
this.#setState({ rejectionCount: this.store.state.rejectionCount + 1 });
|
|
174
|
+
this.options.onReject?.(item, this);
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
172
177
|
if (this.store.state.items.length >= (this.options.maxSize ?? Infinity)) {
|
|
173
178
|
this.#setState({ rejectionCount: this.store.state.rejectionCount + 1 });
|
|
174
179
|
this.options.onReject?.(item, this);
|
|
175
180
|
return false;
|
|
176
181
|
}
|
|
177
|
-
const priority = this.options.getPriority !== defaultOptions.getPriority ? this.options.getPriority(item) : item
|
|
182
|
+
const priority = this.options.getPriority !== defaultOptions.getPriority ? this.options.getPriority(item) : item?.priority;
|
|
178
183
|
const items = this.store.state.items;
|
|
179
184
|
const itemTimestamps = this.store.state.itemTimestamps;
|
|
180
185
|
if (priority !== void 0) {
|
|
181
186
|
const insertIndex = items.findIndex((existing) => {
|
|
182
|
-
return (this.options.getPriority !== defaultOptions.getPriority ? this.options.getPriority(existing) : existing
|
|
187
|
+
return (this.options.getPriority !== defaultOptions.getPriority ? this.options.getPriority(existing) : existing?.priority) < priority;
|
|
183
188
|
});
|
|
184
189
|
if (insertIndex === -1) {
|
|
185
190
|
items.push(item);
|
package/dist/queuer.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"queuer.js","names":["#setState","#checkExpiredItems","#getWait","#timeoutId","#tick","#clearTimeout","#getAllItems"],"sources":["../src/queuer.ts"],"sourcesContent":["import { Store } from '@tanstack/store'\nimport { parseFunctionOrValue } from './utils'\nimport { emitChange, pacerEventClient } from './event-client'\n\nexport interface QueuerState<TValue> {\n /**\n * Number of times addItem has been called (for reduction calculations)\n */\n addItemCount: number\n /**\n * Number of items that have been processed by the queuer\n */\n executionCount: number\n /**\n * Number of items that have been removed from the queue due to expiration\n */\n expirationCount: number\n /**\n * Whether the queuer has no items to process (items array is empty)\n */\n isEmpty: boolean\n /**\n * Whether the queuer has reached its maximum capacity\n */\n isFull: boolean\n /**\n * Whether the queuer is not currently processing any items\n */\n isIdle: boolean\n /**\n * Whether the queuer is active and will process items automatically\n */\n isRunning: boolean\n /**\n * Array of items currently waiting to be processed\n */\n items: Array<TValue>\n /**\n * Timestamps when items were added to the queue for expiration tracking\n */\n itemTimestamps: Array<number>\n /**\n * Whether the queuer has a pending timeout for processing the next item\n */\n pendingTick: boolean\n /**\n * Number of items that have been rejected from being added to the queue\n */\n rejectionCount: number\n /**\n * Number of items currently in the queue\n */\n size: number\n /**\n * Current processing status - 'idle' when not processing, 'running' when active, 'stopped' when paused\n */\n status: 'idle' | 'running' | 'stopped'\n}\n\nfunction getDefaultQueuerState<TValue>(): QueuerState<TValue> {\n return {\n executionCount: 0,\n expirationCount: 0,\n isEmpty: true,\n isFull: false,\n isIdle: true,\n isRunning: true,\n itemTimestamps: [],\n items: [],\n pendingTick: false,\n rejectionCount: 0,\n size: 0,\n status: 'idle',\n addItemCount: 0,\n }\n}\n\n/**\n * Options for configuring a Queuer instance.\n *\n * These options control queue behavior, item expiration, callbacks, and more.\n */\nexport interface QueuerOptions<TValue> {\n /**\n * Default position to add items to the queuer\n * @default 'back'\n */\n addItemsTo?: QueuePosition\n /**\n * Maximum time in milliseconds that an item can stay in the queue\n * If not provided, items will never expire\n */\n expirationDuration?: number\n /**\n * Function to determine if an item has expired\n * If provided, this overrides the expirationDuration behavior\n */\n getIsExpired?: (item: TValue, addedAt: number) => boolean\n /**\n * Default position to get items from during processing\n * @default 'front'\n */\n getItemsFrom?: QueuePosition\n /**\n * Function to determine priority of items in the queuer\n * Higher priority items will be processed first\n */\n getPriority?: (item: TValue) => number\n /**\n * Initial items to populate the queuer with\n */\n initialItems?: Array<TValue>\n /**\n * Initial state for the queuer\n */\n initialState?: Partial<QueuerState<TValue>>\n /**\n * Optional key to identify this queuer instance.\n * If provided, the queuer will be identified by this key in the devtools and PacerProvider if applicable.\n */\n key?: string\n /**\n * Maximum number of items allowed in the queuer\n */\n maxSize?: number\n /**\n * Callback fired whenever an item is removed from the queuer\n */\n onExecute?: (item: TValue, queuer: Queuer<TValue>) => void\n /**\n * Callback fired whenever an item expires in the queuer\n */\n onExpire?: (item: TValue, queuer: Queuer<TValue>) => void\n /**\n * Callback fired whenever an item is added or removed from the queuer\n */\n onItemsChange?: (queuer: Queuer<TValue>) => void\n /**\n * Callback fired whenever an item is rejected from being added to the queuer\n */\n onReject?: (item: TValue, queuer: Queuer<TValue>) => void\n /**\n * Whether the queuer should start processing tasks immediately\n */\n started?: boolean\n /**\n * Time in milliseconds to wait between processing items.\n * Can be a number or a function that returns a number.\n * @default 0\n */\n wait?: number | ((queuer: Queuer<TValue>) => number)\n}\n\n/**\n * Utility function for sharing common `QueuerOptions` options between different `Queuer` instances.\n */\nexport function queuerOptions<\n TValue = any,\n TOptions extends Partial<QueuerOptions<TValue>> = Partial<\n QueuerOptions<TValue>\n >,\n>(options: TOptions): TOptions {\n return options\n}\n\nconst defaultOptions: Omit<\n Required<QueuerOptions<any>>,\n | 'initialState'\n | 'onExecute'\n | 'onIsRunningChange'\n | 'onItemsChange'\n | 'onReject'\n | 'onExpire'\n | 'key'\n> = {\n addItemsTo: 'back',\n getItemsFrom: 'front',\n getPriority: (item) => item?.priority ?? 0,\n getIsExpired: () => false,\n expirationDuration: Infinity,\n initialItems: [],\n maxSize: Infinity,\n started: true,\n wait: 0,\n}\n\n/**\n * Position type for addItem and getNextItem operations.\n *\n * - 'front': Operate on the front of the queue (FIFO)\n * - 'back': Operate on the back of the queue (LIFO)\n */\nexport type QueuePosition = 'front' | 'back'\n\n/**\n * A flexible queue that processes items with configurable wait times, expiration, and priority.\n *\n * This synchronous version is lighter weight and often all you need - upgrade to AsyncQueuer when you need promises, retry support, abort capabilities, concurrent execution, or advanced error handling.\n *\n * Features:\n * - Automatic or manual processing of items\n * - FIFO (First In First Out), LIFO (Last In First Out), or double-ended queue behavior\n * - Priority-based ordering when getPriority is provided\n * - Item expiration and removal of stale items\n * - Callbacks for queue state changes, execution, rejection, and expiration\n *\n * Running behavior:\n * - `start()`: Begins automatically processing items in the queue (defaults to isRunning)\n * - `stop()`: Pauses processing but maintains queue state\n * - `wait`: Configurable delay between processing items\n * - `onItemsChange`/`onExecute`: Callbacks for monitoring queue state\n *\n * Manual processing is also supported when automatic processing is disabled:\n * - `execute()`: Processes the next item using the provided function\n * - `getNextItem()`: Removes and returns the next item without processing\n *\n * Queue behavior defaults to FIFO:\n * - `addItem(item)`: Adds to the back of the queue\n * - Items processed from the front of the queue\n *\n * Priority queue:\n * - Provide a `getPriority` function; higher values are processed first\n *\n * Stack (LIFO):\n * - `addItem(item, 'back')`: Adds to the back\n * - `getNextItem('back')`: Removes from the back\n *\n * Double-ended queue:\n * - `addItem(item, position)`: Adds to specified position ('front'/'back')\n * - `getNextItem(position)`: Removes from specified position\n *\n * Item expiration:\n * - `expirationDuration`: Maximum time items can stay in the queue\n * - `getIsExpired`: Function to override default expiration\n * - `onExpire`: Callback for expired items\n *\n * State Management:\n * - Uses TanStack Store for reactive state management\n * - Use `initialState` to provide initial state values when creating the queuer\n * - Use `onExecute` callback to react to item execution and implement custom logic\n * - Use `onItemsChange` callback to react to items being added or removed from the queue\n * - Use `onExpire` callback to react to items expiring and implement custom logic\n * - Use `onReject` callback to react to items being rejected when the queue is full\n * - The state includes execution count, expiration count, rejection count, and isRunning status\n * - State can be accessed via `queuer.store.state` when using the class directly\n * - When using framework adapters (React/Solid), state is accessed from `queuer.state`\n *\n * Example usage:\n * ```ts\n * // Auto-processing queue with wait time\n * const autoQueue = new Queuer<number>((n) => console.log(n), {\n * started: true, // Begin processing immediately\n * wait: 1000, // Wait 1s between items\n * onExecute: (item, queuer) => console.log(`Processed ${item}`)\n * });\n * autoQueue.addItem(1); // Will process after 1s\n * autoQueue.addItem(2); // Will process 1s after first item\n *\n * // Manual processing queue\n * const manualQueue = new Queuer<number>((n) => console.log(n), {\n * started: false\n * });\n * manualQueue.addItem(1); // [1]\n * manualQueue.addItem(2); // [1, 2]\n * manualQueue.execute(); // logs 1, queue is [2]\n * manualQueue.getNextItem(); // returns 2, queue is empty\n * ```\n */\nexport class Queuer<TValue> {\n readonly store: Store<Readonly<QueuerState<TValue>>> = new Store(\n getDefaultQueuerState<TValue>(),\n )\n key: string | undefined\n options: QueuerOptions<TValue>\n #timeoutId: ReturnType<typeof setTimeout> | null = null\n\n constructor(\n public fn: (item: TValue) => void,\n initialOptions: QueuerOptions<TValue> = {},\n ) {\n this.key = initialOptions.key\n this.options = {\n ...defaultOptions,\n ...initialOptions,\n }\n const isInitiallyRunning =\n this.options.initialState?.isRunning ?? this.options.started ?? true\n this.#setState({\n ...this.options.initialState,\n isRunning: isInitiallyRunning,\n })\n\n if (this.options.initialState?.items) {\n if (this.store.state.isRunning) {\n this.#tick()\n }\n } else {\n for (let i = 0; i < (this.options.initialItems?.length ?? 0); i++) {\n const item = this.options.initialItems![i]!\n const isLast = i === (this.options.initialItems?.length ?? 0) - 1\n this.addItem(item, this.options.addItemsTo ?? 'back', isLast)\n }\n }\n\n if (this.key) {\n pacerEventClient.on('d-Queuer', (event) => {\n if (event.payload.key !== this.key) return\n this.#setState(\n event.payload.store.state as Partial<QueuerState<TValue>>,\n )\n this.setOptions(event.payload.options as Partial<QueuerOptions<TValue>>)\n })\n }\n }\n\n /**\n * Updates the queuer options. New options are merged with existing options.\n */\n setOptions = (newOptions: Partial<QueuerOptions<TValue>>): void => {\n this.options = { ...this.options, ...newOptions }\n }\n\n #setState = (newState: Partial<QueuerState<TValue>>): void => {\n this.store.setState((state) => {\n const combinedState = {\n ...state,\n ...newState,\n }\n\n const { items, isRunning } = combinedState\n\n const size = items.length\n const isFull = size >= (this.options.maxSize ?? Infinity)\n const isEmpty = size === 0\n const isIdle = isRunning && isEmpty\n\n const status = isIdle ? 'idle' : isRunning ? 'running' : 'stopped'\n\n return {\n ...combinedState,\n isEmpty,\n isFull,\n isIdle,\n size,\n status,\n }\n })\n emitChange('Queuer', this)\n }\n\n /**\n * Returns the current wait time (in milliseconds) between processing items.\n * If a function is provided, it is called with the queuer instance.\n */\n #getWait = (): number => {\n return parseFunctionOrValue(this.options.wait ?? 0, this)\n }\n\n /**\n * Processes items in the queue up to the wait interval. Internal use only.\n */\n #tick = () => {\n if (!this.store.state.isRunning) {\n this.#setState({ pendingTick: false })\n return\n }\n\n this.#setState({ pendingTick: true })\n\n // Check for expired items\n this.#checkExpiredItems()\n\n while (this.store.state.items.length > 0) {\n const nextItem = this.execute(this.options.getItemsFrom ?? 'front')\n if (nextItem === undefined) {\n break\n }\n\n const wait = this.#getWait()\n if (wait > 0) {\n // Use setTimeout to wait before processing next item\n this.#timeoutId = setTimeout(() => this.#tick(), wait)\n return\n }\n\n this.#tick()\n }\n this.#setState({ pendingTick: false })\n }\n\n /**\n * Adds an item to the queue. If the queue is full, the item is rejected and onReject is called.\n * Items can be inserted based on priority or at the front/back depending on configuration.\n *\n * Returns true if the item was added, false if the queue is full.\n *\n * Example usage:\n * ```ts\n * queuer.addItem('task');\n * queuer.addItem('task2', 'front');\n * ```\n */\n addItem = (\n item: TValue,\n position: QueuePosition = this.options.addItemsTo ?? 'back',\n runOnItemsChange: boolean = true,\n ): boolean => {\n this.#setState({\n addItemCount: this.store.state.addItemCount + 1,\n })\n\n if (this.store.state.items.length >= (this.options.maxSize ?? Infinity)) {\n this.#setState({\n rejectionCount: this.store.state.rejectionCount + 1,\n })\n this.options.onReject?.(item, this)\n return false\n }\n\n // Get priority either from the function or from getPriority option\n const priority =\n this.options.getPriority !== defaultOptions.getPriority\n ? this.options.getPriority!(item)\n : (item as any).priority\n\n const items = this.store.state.items\n const itemTimestamps = this.store.state.itemTimestamps\n\n if (priority !== undefined) {\n // Insert based on priority - higher priority items go to front\n const insertIndex = items.findIndex((existing) => {\n const existingPriority: number =\n this.options.getPriority !== defaultOptions.getPriority\n ? this.options.getPriority!(existing)\n : (existing as any).priority\n return existingPriority < priority\n })\n\n if (insertIndex === -1) {\n items.push(item)\n itemTimestamps.push(Date.now())\n } else {\n items.splice(insertIndex, 0, item)\n itemTimestamps.splice(insertIndex, 0, Date.now())\n }\n } else {\n if (position === 'front') {\n // Default FIFO/LIFO behavior\n items.unshift(item)\n itemTimestamps.unshift(Date.now())\n } else {\n // LIFO\n items.push(item)\n itemTimestamps.push(Date.now())\n }\n }\n\n this.#setState({\n items,\n itemTimestamps,\n })\n\n if (runOnItemsChange) {\n this.options.onItemsChange?.(this)\n }\n\n if (this.store.state.isRunning && !this.store.state.pendingTick) {\n this.#setState({ pendingTick: true })\n this.#tick()\n }\n\n return true\n }\n\n /**\n * Removes and returns the next item from the queue without executing the function.\n * Use for manual queue management. Normally, use execute() to process items.\n *\n * Example usage:\n * ```ts\n * // FIFO\n * queuer.getNextItem();\n * // LIFO\n * queuer.getNextItem('back');\n * ```\n */\n getNextItem = (\n position: QueuePosition = this.options.getItemsFrom ?? 'front',\n ): TValue | undefined => {\n const { items, itemTimestamps } = this.store.state\n let item: TValue | undefined\n\n // When priority function is provided, always get from front (highest priority)\n // Priority takes precedence over FIFO/LIFO behavior\n if (\n this.options.getPriority !== defaultOptions.getPriority ||\n position === 'front'\n ) {\n item = items[0]\n if (item !== undefined) {\n this.#setState({\n items: items.slice(1),\n itemTimestamps: itemTimestamps.slice(1),\n })\n }\n } else {\n item = items[items.length - 1]\n if (item !== undefined) {\n this.#setState({\n items: items.slice(0, -1),\n itemTimestamps: itemTimestamps.slice(0, -1),\n })\n }\n }\n\n if (item !== undefined) {\n this.options.onItemsChange?.(this)\n }\n\n return item\n }\n\n #getAllItems = (): Array<TValue> => {\n const items = this.peekAllItems()\n this.clear()\n return items\n }\n\n /**\n * Removes and returns the next item from the queue and processes it using the provided function.\n *\n * Example usage:\n * ```ts\n * queuer.execute();\n * // LIFO\n * queuer.execute('back');\n * ```\n */\n execute = (position?: QueuePosition): TValue | undefined => {\n const item = this.getNextItem(position)\n if (item !== undefined) {\n this.fn(item)\n this.#setState({\n executionCount: this.store.state.executionCount + 1,\n })\n this.options.onExecute?.(item, this)\n }\n return item\n }\n\n /**\n * Processes a specified number of items to execute immediately with no wait time\n * If no numberOfItems is provided, all items will be processed\n */\n flush = (\n numberOfItems: number = this.store.state.items.length,\n position?: QueuePosition,\n ): void => {\n this.#clearTimeout() // clear any pending timeout\n for (let i = 0; i < numberOfItems; i++) {\n this.execute(position)\n }\n this.#tick()\n }\n\n /**\n * Processes all items in the queue as a batch using the provided function as an argument\n * The queue is cleared after processing\n */\n flushAsBatch = (batchFunction: (items: Array<TValue>) => void): void => {\n const items = this.#getAllItems()\n this.clear()\n batchFunction(items)\n }\n\n /**\n * Checks for expired items in the queue and removes them. Calls onExpire for each expired item.\n * Internal use only.\n */\n #checkExpiredItems = (): void => {\n if (\n (this.options.expirationDuration ?? Infinity) === Infinity &&\n this.options.getIsExpired === defaultOptions.getIsExpired\n ) {\n return\n }\n\n const now = Date.now()\n const expiredIndices: Array<number> = []\n\n // Find indices of expired items\n for (let i = 0; i < this.store.state.items.length; i++) {\n const timestamp = this.store.state.itemTimestamps[i]\n if (timestamp === undefined) continue\n\n const item = this.store.state.items[i]\n if (item === undefined) continue\n\n const isExpired =\n this.options.getIsExpired !== defaultOptions.getIsExpired\n ? this.options.getIsExpired!(item, timestamp)\n : now - timestamp > (this.options.expirationDuration ?? Infinity)\n\n if (isExpired) {\n expiredIndices.push(i)\n }\n }\n\n // Remove expired items from back to front to maintain indices\n for (let i = expiredIndices.length - 1; i >= 0; i--) {\n const index = expiredIndices[i]\n if (index === undefined) continue\n\n const expiredItem = this.store.state.items[index]\n if (expiredItem === undefined) continue\n\n const newItems = [...this.store.state.items]\n const newTimestamps = [...this.store.state.itemTimestamps]\n newItems.splice(index, 1)\n newTimestamps.splice(index, 1)\n this.#setState({\n items: newItems,\n itemTimestamps: newTimestamps,\n expirationCount: this.store.state.expirationCount + 1,\n })\n this.options.onExpire?.(expiredItem, this)\n }\n\n if (expiredIndices.length > 0) {\n this.options.onItemsChange?.(this)\n }\n }\n\n /**\n * Returns the next item in the queue without removing it.\n *\n * Example usage:\n * ```ts\n * queuer.peekNextItem(); // front\n * queuer.peekNextItem('back'); // back\n * ```\n */\n peekNextItem = (position: QueuePosition = 'front'): TValue | undefined => {\n if (position === 'front') {\n return this.store.state.items[0]\n }\n return this.store.state.items[this.store.state.items.length - 1]\n }\n\n /**\n * Returns a copy of all items in the queue.\n */\n peekAllItems = (): Array<TValue> => {\n return [...this.store.state.items]\n }\n\n /**\n * Starts processing items in the queue. If already isRunning, does nothing.\n */\n start = () => {\n this.#setState({ isRunning: true })\n if (!this.store.state.pendingTick && this.store.state.items.length > 0) {\n this.#tick()\n }\n }\n\n /**\n * Stops processing items in the queue. Does not clear the queue.\n */\n stop = () => {\n this.#clearTimeout()\n this.#setState({ isRunning: false, pendingTick: false })\n }\n\n #clearTimeout = (): void => {\n if (this.#timeoutId) {\n clearTimeout(this.#timeoutId)\n this.#timeoutId = null\n }\n }\n\n /**\n * Removes all pending items from the queue. Does not affect items being processed.\n */\n clear = (): void => {\n this.#setState({ items: [], itemTimestamps: [] })\n this.options.onItemsChange?.(this)\n }\n\n /**\n * Resets the queuer state to its default values\n */\n reset = (): void => {\n this.#setState(getDefaultQueuerState<TValue>())\n this.options.onItemsChange?.(this)\n }\n}\n\n/**\n * Creates a queue that processes items immediately upon addition.\n * Items are processed sequentially in FIFO order by default.\n *\n * This synchronous version is lighter weight and often all you need - upgrade to asyncQueue when you need promises, retry support, abort capabilities, concurrent execution, or advanced error handling.\n *\n * State Management:\n * - Uses TanStack Store for reactive state management\n * - Use `initialState` to provide initial state values when creating the queuer\n * - Use `onExecute` callback to react to item execution and implement custom logic\n * - Use `onItemsChange` callback to react to items being added or removed from the queue\n * - Use `onExpire` callback to react to items expiring and implement custom logic\n * - Use `onReject` callback to react to items being rejected when the queue is full\n * - The state includes execution count, expiration count, rejection count, and isRunning status\n * - State can be accessed via the underlying Queuer instance's `store.state` property\n * - When using framework adapters (React/Solid), state is accessed from the hook's state property\n *\n * Example usage:\n * ```ts\n * // Basic sequential processing\n * const processItems = queue<number>((n) => console.log(n), {\n * wait: 1000,\n * onItemsChange: (queuer) => console.log(queuer.peekAllItems())\n * });\n * processItems(1); // Logs: 1\n * processItems(2); // Logs: 2 after 1 completes\n *\n * // Priority queue\n * const processPriority = queue<number>((n) => console.log(n), {\n * getPriority: n => n // Higher numbers processed first\n * });\n * processPriority(1);\n * processPriority(3); // Processed before 1\n * ```\n */\nexport function queue<TValue>(\n fn: (item: TValue) => void,\n initialOptions: QueuerOptions<TValue>,\n) {\n const queuer = new Queuer<TValue>(fn, initialOptions)\n return queuer.addItem\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"queuer.js","names":["#setState","#checkExpiredItems","#getWait","#timeoutId","#tick","#clearTimeout","#getAllItems"],"sources":["../src/queuer.ts"],"sourcesContent":["import { Store } from '@tanstack/store'\nimport { parseFunctionOrValue } from './utils'\nimport { emitChange, pacerEventClient } from './event-client'\n\nexport interface QueuerState<TValue> {\n /**\n * Number of times addItem has been called (for reduction calculations)\n */\n addItemCount: number\n /**\n * Number of items that have been processed by the queuer\n */\n executionCount: number\n /**\n * Number of items that have been removed from the queue due to expiration\n */\n expirationCount: number\n /**\n * Whether the queuer has no items to process (items array is empty)\n */\n isEmpty: boolean\n /**\n * Whether the queuer has reached its maximum capacity\n */\n isFull: boolean\n /**\n * Whether the queuer is not currently processing any items\n */\n isIdle: boolean\n /**\n * Whether the queuer is active and will process items automatically\n */\n isRunning: boolean\n /**\n * Array of items currently waiting to be processed\n */\n items: Array<TValue>\n /**\n * Timestamps when items were added to the queue for expiration tracking\n */\n itemTimestamps: Array<number>\n /**\n * Whether the queuer has a pending timeout for processing the next item\n */\n pendingTick: boolean\n /**\n * Number of items that have been rejected from being added to the queue\n */\n rejectionCount: number\n /**\n * Number of items currently in the queue\n */\n size: number\n /**\n * Current processing status - 'idle' when not processing, 'running' when active, 'stopped' when paused\n */\n status: 'idle' | 'running' | 'stopped'\n}\n\nfunction getDefaultQueuerState<TValue>(): QueuerState<TValue> {\n return {\n executionCount: 0,\n expirationCount: 0,\n isEmpty: true,\n isFull: false,\n isIdle: true,\n isRunning: true,\n itemTimestamps: [],\n items: [],\n pendingTick: false,\n rejectionCount: 0,\n size: 0,\n status: 'idle',\n addItemCount: 0,\n }\n}\n\n/**\n * Options for configuring a Queuer instance.\n *\n * These options control queue behavior, item expiration, callbacks, and more.\n */\nexport interface QueuerOptions<TValue> {\n /**\n * Default position to add items to the queuer\n * @default 'back'\n */\n addItemsTo?: QueuePosition\n /**\n * Maximum time in milliseconds that an item can stay in the queue\n * If not provided, items will never expire\n */\n expirationDuration?: number\n /**\n * Function to determine if an item has expired\n * If provided, this overrides the expirationDuration behavior\n */\n getIsExpired?: (item: TValue, addedAt: number) => boolean\n /**\n * Default position to get items from during processing\n * @default 'front'\n */\n getItemsFrom?: QueuePosition\n /**\n * Function to determine priority of items in the queuer\n * Higher priority items will be processed first\n */\n getPriority?: (item: TValue) => number\n /**\n * Initial items to populate the queuer with\n */\n initialItems?: Array<TValue>\n /**\n * Initial state for the queuer\n */\n initialState?: Partial<QueuerState<TValue>>\n /**\n * Optional key to identify this queuer instance.\n * If provided, the queuer will be identified by this key in the devtools and PacerProvider if applicable.\n */\n key?: string\n /**\n * Maximum number of items allowed in the queuer\n */\n maxSize?: number\n /**\n * Callback fired whenever an item is removed from the queuer\n */\n onExecute?: (item: TValue, queuer: Queuer<TValue>) => void\n /**\n * Callback fired whenever an item expires in the queuer\n */\n onExpire?: (item: TValue, queuer: Queuer<TValue>) => void\n /**\n * Callback fired whenever an item is added or removed from the queuer\n */\n onItemsChange?: (queuer: Queuer<TValue>) => void\n /**\n * Callback fired whenever an item is rejected from being added to the queuer\n */\n onReject?: (item: TValue, queuer: Queuer<TValue>) => void\n /**\n * Whether the queuer should start processing tasks immediately\n */\n started?: boolean\n /**\n * Time in milliseconds to wait between processing items.\n * Can be a number or a function that returns a number.\n * @default 0\n */\n wait?: number | ((queuer: Queuer<TValue>) => number)\n}\n\n/**\n * Utility function for sharing common `QueuerOptions` options between different `Queuer` instances.\n */\nexport function queuerOptions<\n TValue = any,\n TOptions extends Partial<QueuerOptions<TValue>> = Partial<\n QueuerOptions<TValue>\n >,\n>(options: TOptions): TOptions {\n return options\n}\n\nconst defaultOptions: Omit<\n Required<QueuerOptions<any>>,\n | 'initialState'\n | 'onExecute'\n | 'onIsRunningChange'\n | 'onItemsChange'\n | 'onReject'\n | 'onExpire'\n | 'key'\n> = {\n addItemsTo: 'back',\n getItemsFrom: 'front',\n getPriority: (item) => item?.priority ?? 0,\n getIsExpired: () => false,\n expirationDuration: Infinity,\n initialItems: [],\n maxSize: Infinity,\n started: true,\n wait: 0,\n}\n\n/**\n * Position type for addItem and getNextItem operations.\n *\n * - 'front': Operate on the front of the queue (FIFO)\n * - 'back': Operate on the back of the queue (LIFO)\n */\nexport type QueuePosition = 'front' | 'back'\n\n/**\n * A flexible queue that processes items with configurable wait times, expiration, and priority.\n *\n * This synchronous version is lighter weight and often all you need - upgrade to AsyncQueuer when you need promises, retry support, abort capabilities, concurrent execution, or advanced error handling.\n *\n * Features:\n * - Automatic or manual processing of items\n * - FIFO (First In First Out), LIFO (Last In First Out), or double-ended queue behavior\n * - Priority-based ordering when getPriority is provided\n * - Item expiration and removal of stale items\n * - Callbacks for queue state changes, execution, rejection, and expiration\n *\n * Running behavior:\n * - `start()`: Begins automatically processing items in the queue (defaults to isRunning)\n * - `stop()`: Pauses processing but maintains queue state\n * - `wait`: Configurable delay between processing items\n * - `onItemsChange`/`onExecute`: Callbacks for monitoring queue state\n *\n * Manual processing is also supported when automatic processing is disabled:\n * - `execute()`: Processes the next item using the provided function\n * - `getNextItem()`: Removes and returns the next item without processing\n *\n * Queue behavior defaults to FIFO:\n * - `addItem(item)`: Adds to the back of the queue\n * - Items processed from the front of the queue\n *\n * Priority queue:\n * - Provide a `getPriority` function; higher values are processed first\n *\n * Stack (LIFO):\n * - `addItem(item, 'back')`: Adds to the back\n * - `getNextItem('back')`: Removes from the back\n *\n * Double-ended queue:\n * - `addItem(item, position)`: Adds to specified position ('front'/'back')\n * - `getNextItem(position)`: Removes from specified position\n *\n * Item expiration:\n * - `expirationDuration`: Maximum time items can stay in the queue\n * - `getIsExpired`: Function to override default expiration\n * - `onExpire`: Callback for expired items\n *\n * State Management:\n * - Uses TanStack Store for reactive state management\n * - Use `initialState` to provide initial state values when creating the queuer\n * - Use `onExecute` callback to react to item execution and implement custom logic\n * - Use `onItemsChange` callback to react to items being added or removed from the queue\n * - Use `onExpire` callback to react to items expiring and implement custom logic\n * - Use `onReject` callback to react to items being rejected when the queue is full\n * - The state includes execution count, expiration count, rejection count, and isRunning status\n * - State can be accessed via `queuer.store.state` when using the class directly\n * - When using framework adapters (React/Solid), state is accessed from `queuer.state`\n *\n * Example usage:\n * ```ts\n * // Auto-processing queue with wait time\n * const autoQueue = new Queuer<number>((n) => console.log(n), {\n * started: true, // Begin processing immediately\n * wait: 1000, // Wait 1s between items\n * onExecute: (item, queuer) => console.log(`Processed ${item}`)\n * });\n * autoQueue.addItem(1); // Will process after 1s\n * autoQueue.addItem(2); // Will process 1s after first item\n *\n * // Manual processing queue\n * const manualQueue = new Queuer<number>((n) => console.log(n), {\n * started: false\n * });\n * manualQueue.addItem(1); // [1]\n * manualQueue.addItem(2); // [1, 2]\n * manualQueue.execute(); // logs 1, queue is [2]\n * manualQueue.getNextItem(); // returns 2, queue is empty\n * ```\n */\nexport class Queuer<TValue> {\n readonly store: Store<Readonly<QueuerState<TValue>>> = new Store(\n getDefaultQueuerState<TValue>(),\n )\n key: string | undefined\n options: QueuerOptions<TValue>\n #timeoutId: ReturnType<typeof setTimeout> | null = null\n\n constructor(\n public fn: (item: TValue) => void,\n initialOptions: QueuerOptions<TValue> = {},\n ) {\n this.key = initialOptions.key\n this.options = {\n ...defaultOptions,\n ...initialOptions,\n }\n const isInitiallyRunning =\n this.options.initialState?.isRunning ?? this.options.started ?? true\n this.#setState({\n ...this.options.initialState,\n isRunning: isInitiallyRunning,\n })\n\n if (this.options.initialState?.items) {\n if (this.store.state.isRunning) {\n this.#tick()\n }\n } else {\n for (let i = 0; i < (this.options.initialItems?.length ?? 0); i++) {\n const item = this.options.initialItems![i]!\n const isLast = i === (this.options.initialItems?.length ?? 0) - 1\n this.addItem(item, this.options.addItemsTo ?? 'back', isLast)\n }\n }\n\n if (this.key) {\n pacerEventClient.on('d-Queuer', (event) => {\n if (event.payload.key !== this.key) return\n this.#setState(\n event.payload.store.state as Partial<QueuerState<TValue>>,\n )\n this.setOptions(event.payload.options as Partial<QueuerOptions<TValue>>)\n })\n }\n }\n\n /**\n * Updates the queuer options. New options are merged with existing options.\n */\n setOptions = (newOptions: Partial<QueuerOptions<TValue>>): void => {\n this.options = { ...this.options, ...newOptions }\n }\n\n #setState = (newState: Partial<QueuerState<TValue>>): void => {\n this.store.setState((state) => {\n const combinedState = {\n ...state,\n ...newState,\n }\n\n const { items, isRunning } = combinedState\n\n const size = items.length\n const isFull = size >= (this.options.maxSize ?? Infinity)\n const isEmpty = size === 0\n const isIdle = isRunning && isEmpty\n\n const status = isIdle ? 'idle' : isRunning ? 'running' : 'stopped'\n\n return {\n ...combinedState,\n isEmpty,\n isFull,\n isIdle,\n size,\n status,\n }\n })\n emitChange('Queuer', this)\n }\n\n /**\n * Returns the current wait time (in milliseconds) between processing items.\n * If a function is provided, it is called with the queuer instance.\n */\n #getWait = (): number => {\n return parseFunctionOrValue(this.options.wait ?? 0, this)\n }\n\n /**\n * Processes items in the queue up to the wait interval. Internal use only.\n */\n #tick = () => {\n if (!this.store.state.isRunning) {\n this.#setState({ pendingTick: false })\n return\n }\n\n this.#setState({ pendingTick: true })\n\n // Check for expired items\n this.#checkExpiredItems()\n\n while (this.store.state.items.length > 0) {\n const nextItem = this.execute(this.options.getItemsFrom ?? 'front')\n if (nextItem === undefined) {\n break\n }\n\n const wait = this.#getWait()\n if (wait > 0) {\n // Use setTimeout to wait before processing next item\n this.#timeoutId = setTimeout(() => this.#tick(), wait)\n return\n }\n\n this.#tick()\n }\n this.#setState({ pendingTick: false })\n }\n\n /**\n * Adds an item to the queue. If the queue is full, the item is rejected and onReject is called.\n * `undefined` cannot be queued (it is the internal \"no item\" sentinel) and is always rejected.\n * Items can be inserted based on priority or at the front/back depending on configuration.\n *\n * Returns true if the item was added, false if the queue is full.\n *\n * Example usage:\n * ```ts\n * queuer.addItem('task');\n * queuer.addItem('task2', 'front');\n * ```\n */\n addItem = (\n item: TValue,\n position: QueuePosition = this.options.addItemsTo ?? 'back',\n runOnItemsChange: boolean = true,\n ): boolean => {\n this.#setState({\n addItemCount: this.store.state.addItemCount + 1,\n })\n\n // undefined is the internal \"no item\" sentinel (peekNextItem/getNextItem);\n // queuing it would break the processing loop and block items behind it\n if (item === undefined) {\n this.#setState({\n rejectionCount: this.store.state.rejectionCount + 1,\n })\n this.options.onReject?.(item, this)\n return false\n }\n\n if (this.store.state.items.length >= (this.options.maxSize ?? Infinity)) {\n this.#setState({\n rejectionCount: this.store.state.rejectionCount + 1,\n })\n this.options.onReject?.(item, this)\n return false\n }\n\n // Get priority either from the function or from getPriority option\n const priority =\n this.options.getPriority !== defaultOptions.getPriority\n ? this.options.getPriority!(item)\n : (item as any)?.priority\n\n const items = this.store.state.items\n const itemTimestamps = this.store.state.itemTimestamps\n\n if (priority !== undefined) {\n // Insert based on priority - higher priority items go to front\n const insertIndex = items.findIndex((existing) => {\n const existingPriority: number =\n this.options.getPriority !== defaultOptions.getPriority\n ? this.options.getPriority!(existing)\n : (existing as any)?.priority\n return existingPriority < priority\n })\n\n if (insertIndex === -1) {\n items.push(item)\n itemTimestamps.push(Date.now())\n } else {\n items.splice(insertIndex, 0, item)\n itemTimestamps.splice(insertIndex, 0, Date.now())\n }\n } else {\n if (position === 'front') {\n // Default FIFO/LIFO behavior\n items.unshift(item)\n itemTimestamps.unshift(Date.now())\n } else {\n // LIFO\n items.push(item)\n itemTimestamps.push(Date.now())\n }\n }\n\n this.#setState({\n items,\n itemTimestamps,\n })\n\n if (runOnItemsChange) {\n this.options.onItemsChange?.(this)\n }\n\n if (this.store.state.isRunning && !this.store.state.pendingTick) {\n this.#setState({ pendingTick: true })\n this.#tick()\n }\n\n return true\n }\n\n /**\n * Removes and returns the next item from the queue without executing the function.\n * Use for manual queue management. Normally, use execute() to process items.\n *\n * Example usage:\n * ```ts\n * // FIFO\n * queuer.getNextItem();\n * // LIFO\n * queuer.getNextItem('back');\n * ```\n */\n getNextItem = (\n position: QueuePosition = this.options.getItemsFrom ?? 'front',\n ): TValue | undefined => {\n const { items, itemTimestamps } = this.store.state\n let item: TValue | undefined\n\n // When priority function is provided, always get from front (highest priority)\n // Priority takes precedence over FIFO/LIFO behavior\n if (\n this.options.getPriority !== defaultOptions.getPriority ||\n position === 'front'\n ) {\n item = items[0]\n if (item !== undefined) {\n this.#setState({\n items: items.slice(1),\n itemTimestamps: itemTimestamps.slice(1),\n })\n }\n } else {\n item = items[items.length - 1]\n if (item !== undefined) {\n this.#setState({\n items: items.slice(0, -1),\n itemTimestamps: itemTimestamps.slice(0, -1),\n })\n }\n }\n\n if (item !== undefined) {\n this.options.onItemsChange?.(this)\n }\n\n return item\n }\n\n #getAllItems = (): Array<TValue> => {\n const items = this.peekAllItems()\n this.clear()\n return items\n }\n\n /**\n * Removes and returns the next item from the queue and processes it using the provided function.\n *\n * Example usage:\n * ```ts\n * queuer.execute();\n * // LIFO\n * queuer.execute('back');\n * ```\n */\n execute = (position?: QueuePosition): TValue | undefined => {\n const item = this.getNextItem(position)\n if (item !== undefined) {\n this.fn(item)\n this.#setState({\n executionCount: this.store.state.executionCount + 1,\n })\n this.options.onExecute?.(item, this)\n }\n return item\n }\n\n /**\n * Processes a specified number of items to execute immediately with no wait time\n * If no numberOfItems is provided, all items will be processed\n */\n flush = (\n numberOfItems: number = this.store.state.items.length,\n position?: QueuePosition,\n ): void => {\n this.#clearTimeout() // clear any pending timeout\n for (let i = 0; i < numberOfItems; i++) {\n this.execute(position)\n }\n this.#tick()\n }\n\n /**\n * Processes all items in the queue as a batch using the provided function as an argument\n * The queue is cleared after processing\n */\n flushAsBatch = (batchFunction: (items: Array<TValue>) => void): void => {\n const items = this.#getAllItems()\n this.clear()\n batchFunction(items)\n }\n\n /**\n * Checks for expired items in the queue and removes them. Calls onExpire for each expired item.\n * Internal use only.\n */\n #checkExpiredItems = (): void => {\n if (\n (this.options.expirationDuration ?? Infinity) === Infinity &&\n this.options.getIsExpired === defaultOptions.getIsExpired\n ) {\n return\n }\n\n const now = Date.now()\n const expiredIndices: Array<number> = []\n\n // Find indices of expired items\n for (let i = 0; i < this.store.state.items.length; i++) {\n const timestamp = this.store.state.itemTimestamps[i]\n if (timestamp === undefined) continue\n\n const item = this.store.state.items[i]\n if (item === undefined) continue\n\n const isExpired =\n this.options.getIsExpired !== defaultOptions.getIsExpired\n ? this.options.getIsExpired!(item, timestamp)\n : now - timestamp > (this.options.expirationDuration ?? Infinity)\n\n if (isExpired) {\n expiredIndices.push(i)\n }\n }\n\n // Remove expired items from back to front to maintain indices\n for (let i = expiredIndices.length - 1; i >= 0; i--) {\n const index = expiredIndices[i]\n if (index === undefined) continue\n\n const expiredItem = this.store.state.items[index]\n if (expiredItem === undefined) continue\n\n const newItems = [...this.store.state.items]\n const newTimestamps = [...this.store.state.itemTimestamps]\n newItems.splice(index, 1)\n newTimestamps.splice(index, 1)\n this.#setState({\n items: newItems,\n itemTimestamps: newTimestamps,\n expirationCount: this.store.state.expirationCount + 1,\n })\n this.options.onExpire?.(expiredItem, this)\n }\n\n if (expiredIndices.length > 0) {\n this.options.onItemsChange?.(this)\n }\n }\n\n /**\n * Returns the next item in the queue without removing it.\n *\n * Example usage:\n * ```ts\n * queuer.peekNextItem(); // front\n * queuer.peekNextItem('back'); // back\n * ```\n */\n peekNextItem = (position: QueuePosition = 'front'): TValue | undefined => {\n if (position === 'front') {\n return this.store.state.items[0]\n }\n return this.store.state.items[this.store.state.items.length - 1]\n }\n\n /**\n * Returns a copy of all items in the queue.\n */\n peekAllItems = (): Array<TValue> => {\n return [...this.store.state.items]\n }\n\n /**\n * Starts processing items in the queue. If already isRunning, does nothing.\n */\n start = () => {\n this.#setState({ isRunning: true })\n if (!this.store.state.pendingTick && this.store.state.items.length > 0) {\n this.#tick()\n }\n }\n\n /**\n * Stops processing items in the queue. Does not clear the queue.\n */\n stop = () => {\n this.#clearTimeout()\n this.#setState({ isRunning: false, pendingTick: false })\n }\n\n #clearTimeout = (): void => {\n if (this.#timeoutId) {\n clearTimeout(this.#timeoutId)\n this.#timeoutId = null\n }\n }\n\n /**\n * Removes all pending items from the queue. Does not affect items being processed.\n */\n clear = (): void => {\n this.#setState({ items: [], itemTimestamps: [] })\n this.options.onItemsChange?.(this)\n }\n\n /**\n * Resets the queuer state to its default values\n */\n reset = (): void => {\n this.#setState(getDefaultQueuerState<TValue>())\n this.options.onItemsChange?.(this)\n }\n}\n\n/**\n * Creates a queue that processes items immediately upon addition.\n * Items are processed sequentially in FIFO order by default.\n *\n * This synchronous version is lighter weight and often all you need - upgrade to asyncQueue when you need promises, retry support, abort capabilities, concurrent execution, or advanced error handling.\n *\n * State Management:\n * - Uses TanStack Store for reactive state management\n * - Use `initialState` to provide initial state values when creating the queuer\n * - Use `onExecute` callback to react to item execution and implement custom logic\n * - Use `onItemsChange` callback to react to items being added or removed from the queue\n * - Use `onExpire` callback to react to items expiring and implement custom logic\n * - Use `onReject` callback to react to items being rejected when the queue is full\n * - The state includes execution count, expiration count, rejection count, and isRunning status\n * - State can be accessed via the underlying Queuer instance's `store.state` property\n * - When using framework adapters (React/Solid), state is accessed from the hook's state property\n *\n * Example usage:\n * ```ts\n * // Basic sequential processing\n * const processItems = queue<number>((n) => console.log(n), {\n * wait: 1000,\n * onItemsChange: (queuer) => console.log(queuer.peekAllItems())\n * });\n * processItems(1); // Logs: 1\n * processItems(2); // Logs: 2 after 1 completes\n *\n * // Priority queue\n * const processPriority = queue<number>((n) => console.log(n), {\n * getPriority: n => n // Higher numbers processed first\n * });\n * processPriority(1);\n * processPriority(3); // Processed before 1\n * ```\n */\nexport function queue<TValue>(\n fn: (item: TValue) => void,\n initialOptions: QueuerOptions<TValue>,\n) {\n const queuer = new Queuer<TValue>(fn, initialOptions)\n return queuer.addItem\n}\n"],"mappings":"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"}
|