Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions .oxlintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,14 @@
}
],
"no-await-in-loop": "off",
"no-underscore-dangle": "off", // Disabled because it's a common pattern in the codebase
// Leading `_` marks internal members (paired with `/** @internal */` +
// stripInternal) that tests intentionally reach into; `__name__` members
// are the Redis connection protocol (`__runScript__`, `__addLimiter__`, ...).
"no-underscore-dangle": "off",
"no-array-constructor": "error",
"prefer-arrow-callback": "error",
"typescript/no-explicit-any": "off", // Disabled because it's a common pattern in the codebase
"typescript/no-require-imports": "off", // Disabled because it's a common pattern in the codebase
"typescript/no-explicit-any": "error",
"typescript/no-require-imports": "error",
"typescript/no-unsafe-function-type": "error",
"typescript/consistent-type-imports": ["error", { "fixStyle": "inline-type-imports" }],
"typescript/ban-ts-comment": "error",
Expand Down
6 changes: 3 additions & 3 deletions src/Batcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ type BatcherDefaults = {
maxSize: number | null;
};

class Batcher<T = any> {
class Batcher<T = unknown> {
/** @internal */
defaults: BatcherDefaults = { maxTime: null, maxSize: null };
/** @internal */
Expand All @@ -34,11 +34,11 @@ class Batcher<T = any> {
// Installed on the instance by Events (see Events constructor).
declare on: {
<E extends keyof BatcherEvents<T>>(event: E, listener: BatcherEvents<T>[E]): unknown;
(event: string, listener: (...args: any[]) => unknown): unknown;
(event: string, listener: (...args: never[]) => unknown): unknown;
};
declare once: {
<E extends keyof BatcherEvents<T>>(event: E, listener: BatcherEvents<T>[E]): unknown;
(event: string, listener: (...args: any[]) => unknown): unknown;
(event: string, listener: (...args: never[]) => unknown): unknown;
};
declare removeAllListeners: (name?: string | null) => void;

Expand Down
4 changes: 2 additions & 2 deletions src/Bottleneck.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,11 +164,11 @@ class Bottleneck {
// is the public event contract.
declare on: {
<E extends keyof BottleneckEvents>(event: E, listener: BottleneckEvents[E]): unknown;
(event: string, listener: (...args: any[]) => unknown): unknown;
(event: string, listener: (...args: never[]) => unknown): unknown;
};
declare once: {
<E extends keyof BottleneckEvents>(event: E, listener: BottleneckEvents[E]): unknown;
(event: string, listener: (...args: any[]) => unknown): unknown;
(event: string, listener: (...args: never[]) => unknown): unknown;
};
declare removeAllListeners: (name?: string | null) => void;

Expand Down
4 changes: 2 additions & 2 deletions src/Group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ class Group {
// Installed on the instance by Events (see Events constructor).
declare on: {
<E extends keyof GroupEvents>(event: E, listener: GroupEvents[E]): unknown;
(event: string, listener: (...args: any[]) => unknown): unknown;
(event: string, listener: (...args: never[]) => unknown): unknown;
};
declare once: {
<E extends keyof GroupEvents>(event: E, listener: GroupEvents[E]): unknown;
(event: string, listener: (...args: any[]) => unknown): unknown;
(event: string, listener: (...args: never[]) => unknown): unknown;
};
declare removeAllListeners: (name?: string | null) => void;

Expand Down
4 changes: 2 additions & 2 deletions src/Queues.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ class Queues<J extends JobLike = Job> {
_lists: DLList<J>[];

// Installed on the instance by Events (see Events constructor).
declare on: (name: string, cb: (...args: any[]) => void) => unknown;
declare once: (name: string, cb: (...args: any[]) => void) => unknown;
declare on: (name: string, cb: (...args: never[]) => void) => unknown;
declare once: (name: string, cb: (...args: never[]) => void) => unknown;
declare removeAllListeners: (name?: string | null) => void;

constructor(num_priorities: number) {
Expand Down
4 changes: 2 additions & 2 deletions src/async-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import { AsyncResource } from "node:async_hooks";
* Falls back to the unbound task outside Node (the light/browser build stubs
* this module out entirely).
*/
export function bindTask<T extends (...args: any[]) => unknown>(task: T): T {
export function bindTask<T extends (...args: never[]) => unknown>(task: T): T {
try {
return AsyncResource.bind(task);
return AsyncResource.bind(task as T & ((...args: unknown[]) => unknown));
} catch {
return task;
}
Expand Down
4 changes: 2 additions & 2 deletions src/cluster/IORedisConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ class IORedisConnection {
// Installed on the instance by Events (see Events constructor).
declare on: {
(event: "error", listener: (error: unknown) => void): unknown;
(event: string, listener: (...args: any[]) => unknown): unknown;
(event: string, listener: (...args: never[]) => unknown): unknown;
};
declare once: {
(event: "error", listener: (error: unknown) => void): unknown;
(event: string, listener: (...args: any[]) => unknown): unknown;
(event: string, listener: (...args: never[]) => unknown): unknown;
};
declare removeAllListeners: (name?: string | null) => void;

Expand Down
4 changes: 2 additions & 2 deletions src/cluster/RedisConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,11 @@ class RedisConnection {
// Installed on the instance by Events (see Events constructor).
declare on: {
(event: "error", listener: (error: unknown) => void): unknown;
(event: string, listener: (...args: any[]) => unknown): unknown;
(event: string, listener: (...args: never[]) => unknown): unknown;
};
declare once: {
(event: "error", listener: (error: unknown) => void): unknown;
(event: string, listener: (...args: any[]) => unknown): unknown;
(event: string, listener: (...args: never[]) => unknown): unknown;
};
declare removeAllListeners: (name?: string | null) => void;

Expand Down
4 changes: 2 additions & 2 deletions src/cluster/redis-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import type { ScriptName } from "./Scripts";

type BaseClient = {
on(event: string, cb: (...args: any[]) => void): unknown;
on(event: string, cb: (...args: never[]) => void): unknown;
removeAllListeners?(event?: string): unknown;
publish(channel: string, message: string): Promise<unknown>;
unsubscribe(channel: string): unknown;
Expand Down Expand Up @@ -40,7 +40,7 @@ type IORedisScriptCommands = Record<

export type IORedisClient = BaseClient & {
status: string;
once(event: string, cb: (...args: any[]) => void): unknown;
once(event: string, cb: (...args: never[]) => void): unknown;
setMaxListeners(n: number): unknown;
/** Absent on ioredis Cluster clients. */
duplicate?(): IORedisClient;
Expand Down
14 changes: 7 additions & 7 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,12 +161,12 @@ export type IORedisConnectionOptions =
};

/** Datastore-specific map of raw redis clients. */
export type ClientsList = { client?: any; subscriber?: any };
export type ClientsList = { client?: unknown; subscriber?: unknown };

export type GroupLimiterPair = { key: string; limiter: Bottleneck };

export type EventInfo = {
readonly args: any[];
readonly args: readonly unknown[];
readonly options: {
readonly id: string;
readonly priority: number;
Expand All @@ -175,8 +175,8 @@ export type EventInfo = {
};
};
export type EventInfoDropped = EventInfo & {
readonly task: (...args: any[]) => any;
readonly promise: Promise<any>;
readonly task: (...args: never[]) => unknown;
readonly promise: Promise<unknown>;
};
export type EventInfoQueued = EventInfo & {
readonly reachedHWM: boolean;
Expand All @@ -199,7 +199,7 @@ export type Counts = {

/** Event map for the limiter. The `"failed"` listener returns a retry delay in ms (or nothing). */
export type BottleneckEvents = {
debug: (message: string, info: any) => void;
debug: (message: string, info: unknown) => void;
message: (message: string) => void;
error: (error: unknown) => void;
empty: () => void;
Expand All @@ -220,13 +220,13 @@ export type BottleneckEvents = {
};

export type GroupEvents = {
debug: (message: string, info: any) => void;
debug: (message: string, info: unknown) => void;
error: (error: unknown) => void;
created: (limiter: Bottleneck, key: string) => void;
};

export type BatcherEvents<T> = {
debug: (message: string, info: any) => void;
debug: (message: string, info: unknown) => void;
error: (error: unknown) => void;
batch: (batch: T[]) => void;
};
Expand Down
12 changes: 6 additions & 6 deletions test/batcher.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ useFakeClock();

describe("Batcher", () => {
test("Should batch by time and size", async () => {
const batcher = new Bottleneck.Batcher({ maxTime: 100, maxSize: 3 });
const batcher = new Bottleneck.Batcher<number>({ maxTime: 100, maxSize: 3 });
const batches: number[][] = [];
const batchTimes: number[] = [];

Expand All @@ -31,7 +31,7 @@ describe("Batcher", () => {
});

test("Should batch by time", async () => {
const batcher = new Bottleneck.Batcher({ maxTime: 100 });
const batcher = new Bottleneck.Batcher<number>({ maxTime: 100 });
const batches: number[][] = [];
const batchTimes: number[] = [];

Expand All @@ -57,7 +57,7 @@ describe("Batcher", () => {
});

test("Should batch by size", async () => {
const batcher = new Bottleneck.Batcher({ maxSize: 2 });
const batcher = new Bottleneck.Batcher<number>({ maxSize: 2 });
const batches: number[][] = [];

batcher.on("batch", (groups) => {
Expand All @@ -75,7 +75,7 @@ describe("Batcher", () => {
});

test("Should stagger flushes", async () => {
const batcher = new Bottleneck.Batcher({ maxTime: 100, maxSize: 3 });
const batcher = new Bottleneck.Batcher<number>({ maxTime: 100, maxSize: 3 });
const batches: number[][] = [];
const batchTimes: number[] = [];

Expand All @@ -95,7 +95,7 @@ describe("Batcher", () => {
});

test("Should force then stagger flushes", async () => {
const batcher = new Bottleneck.Batcher({ maxTime: 100, maxSize: 3 });
const batcher = new Bottleneck.Batcher<number>({ maxTime: 100, maxSize: 3 });
const batches: number[][] = [];
const batchTimes: number[] = [];

Expand Down Expand Up @@ -123,7 +123,7 @@ describe("Batcher", () => {
});

test("Should default to no time/size limits when constructed without options", () => {
const batcher = new Bottleneck.Batcher();
const batcher = new Bottleneck.Batcher<number>();
expect(batcher.maxTime).toBeNull();
expect(batcher.maxSize).toBeNull();
});
Expand Down
18 changes: 10 additions & 8 deletions test/bottleneck.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ const Bottleneck: BottleneckClass =
// child limiters created from a "connection-only" Group inherit the 5000ms
// production default and produce 5-second flakes (see cluster.test.ts:75
// and similar).
const isRedisBacked = (options: Record<string, any> | undefined): options is Record<string, any> =>
const isRedisBacked = (
options: Record<string, unknown> | undefined,
): options is Record<string, unknown> =>
options != null &&
typeof options === "object" &&
(options.datastore === "redis" || options.datastore === "ioredis" || options.connection != null);
Expand Down Expand Up @@ -59,17 +61,17 @@ async function makeTestBottleneck(Base: BottleneckClass): Promise<BottleneckClas
// top-level host/port (defaulting to localhost:6379).
const buildClientOptions = (await import("./redis-client-options.ts")).default;

const withRedis = (options: Record<string, any> | undefined) => {
const withRedis = (options: Record<string, unknown> | undefined) => {
if (!isRedisBacked(options)) return options;
const next = { ...options } as Record<string, any>;
const next = { ...options };
// Only inject the Redis library / clientOptions when the test isn't
// bringing its own pre-built client or connection. Both of those carry
// their own clientOptions and the Bottleneck constructor would reject
// duplicates.
if (options.connection == null && options.client == null) {
if (next.Redis == null) next.Redis = Redis;
if (next.clientOptions == null) {
next.clientOptions = buildClientOptions(next.datastore);
next.clientOptions = buildClientOptions(String(next.datastore));
}
}
// The default `heartbeatInterval` for Redis-backed limiters is 5000ms,
Expand All @@ -94,10 +96,10 @@ async function makeTestBottleneck(Base: BottleneckClass): Promise<BottleneckClas
// Group.key() builds child ids as `${groupId}-${key}`, so child limiters
// arrive here pre-prefixed. Re-prefixing them would break Group.clusterKeys
// (which SCANs `b_${this.id}-*`).
if (options.id != null && options.id.startsWith(FILE_PREFIX)) {
if (typeof options.id === "string" && options.id.startsWith(FILE_PREFIX)) {
next.id = options.id;
} else {
next.id = FILE_PREFIX + (options.id ?? "no-id");
next.id = FILE_PREFIX + String(options.id ?? "no-id");
}
return next;
};
Expand All @@ -108,7 +110,7 @@ async function makeTestBottleneck(Base: BottleneckClass): Promise<BottleneckClas
static override [Symbol.hasInstance](instance: unknown) {
return instance instanceof Base;
}
constructor(options?: Record<string, any>) {
constructor(options?: Record<string, unknown>) {
super(withRedis(options));
}
}
Expand All @@ -117,7 +119,7 @@ async function makeTestBottleneck(Base: BottleneckClass): Promise<BottleneckClas
static override [Symbol.hasInstance](instance: unknown) {
return instance instanceof Base.Group;
}
constructor(options?: Record<string, any>) {
constructor(options?: Record<string, unknown>) {
super(withRedis(options));
// Group.key() instantiates child limiters via `this.Bottleneck`, which
// the parent Group constructor sets to the library's Bottleneck class.
Expand Down
Loading
Loading