mirror of
https://github.com/gosticks/DefinitelyTyped.git
synced 2026-01-30 21:47:35 +00:00
[opossum] v4.0 updates. (#38017)
The lib now exposes a constructor directly, so the export has been changed to use the `export =` with `declare` style instead of `export default`. - `promisify` removed. - References to `hystrix` were removed. - The primary class now has generics the pulls the argument and return types from the constructor then passes them to the result of `fire` and some of the event listeners. - Event listeners were added to give users types in the specific callbacks. - Doc updates for the methods. - The `action` function must now return a Promise.
This commit is contained in:
parent
970c733cdd
commit
5db94ca7fd
395
types/opossum/index.d.ts
vendored
395
types/opossum/index.d.ts
vendored
@ -1,20 +1,17 @@
|
||||
// Type definitions for opossum 1.10
|
||||
// Type definitions for opossum 4.0
|
||||
// Project: https://github.com/nodeshift/opossum, https://nodeshift.dev/opossum
|
||||
// Definitions by: Quinn Langille <https://github.com/quinnlangille>
|
||||
// Willy Zhang <https://github.com/merufm>
|
||||
// Lance Ball <https://github.com/lance>
|
||||
// Matt R. Wilson <https://github.com/mastermatt>
|
||||
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
||||
// TypeScript Version: 2.8
|
||||
// TypeScript Version: 3.0
|
||||
|
||||
/// <reference types="node"/>
|
||||
import * as stream from "stream";
|
||||
import { EventEmitter } from "events";
|
||||
|
||||
export type Action = (...args: any[]) => any;
|
||||
|
||||
export class CircuitBreaker extends EventEmitter {
|
||||
constructor(action: Action, options: CircuitBreakerOptions);
|
||||
declare class CircuitBreaker<TI extends unknown[] = unknown[], TR = unknown> extends EventEmitter {
|
||||
constructor(action: (...args: TI) => Promise<TR>, options?: CircuitBreaker.Options);
|
||||
|
||||
readonly name: string;
|
||||
readonly group: string;
|
||||
@ -24,182 +21,228 @@ export class CircuitBreaker extends EventEmitter {
|
||||
readonly opened: boolean;
|
||||
readonly halfOpen: boolean;
|
||||
readonly isShutdown: boolean;
|
||||
readonly status: Status;
|
||||
readonly stats: Stats;
|
||||
readonly hystrixStats: HystrixStats;
|
||||
readonly status: CircuitBreaker.Status;
|
||||
readonly stats: CircuitBreaker.Stats;
|
||||
readonly warmUp: boolean;
|
||||
readonly volumeThreshold: number;
|
||||
|
||||
/**
|
||||
* Clears the cache of this CircuitBreaker
|
||||
*/
|
||||
clearCache(): void;
|
||||
open(): void;
|
||||
|
||||
/**
|
||||
* Closes the breaker, allowing the action to execute again
|
||||
*/
|
||||
close(): void;
|
||||
disable(): void;
|
||||
enable(): void;
|
||||
fallback(func: Action | CircuitBreaker): this;
|
||||
fire(...args: any[]): Promise<any>;
|
||||
healthCheck(
|
||||
func: (...args: any[]) => Promise<any>,
|
||||
interval?: number
|
||||
): void;
|
||||
|
||||
/**
|
||||
* Opens the breaker.
|
||||
* Each time the breaker is fired while the circuit is opened, a failed Promise is
|
||||
* returned, or if any fallback function has been provided, it is invoked.
|
||||
*/
|
||||
open(): void;
|
||||
|
||||
/**
|
||||
* Shuts down this circuit breaker.
|
||||
* All subsequent calls to the circuit will fail, returning a rejected promise.
|
||||
*/
|
||||
shutdown(): void;
|
||||
|
||||
/**
|
||||
* Disables this circuit, causing all calls to the circuit's function to be
|
||||
* executed without circuit or fallback protection.
|
||||
*/
|
||||
disable(): void;
|
||||
|
||||
/**
|
||||
* Enables this circuit. If the circuit is the disabled state, it will be re-enabled.
|
||||
* If not, this is essentially a noop.
|
||||
*/
|
||||
enable(): void;
|
||||
|
||||
/**
|
||||
* Provide a fallback function for this CircuitBreaker.
|
||||
* This function will be executed when the circuit is fired and fails.
|
||||
* It will always be preceded by a `failure` event, and `breaker.fire` returns a rejected Promise.
|
||||
*/
|
||||
fallback(func: ((...args: any[]) => any) | CircuitBreaker): this;
|
||||
|
||||
/**
|
||||
* Execute the action for this circuit.
|
||||
* If the action fails or times out, the returned promise will be rejected.
|
||||
* If the action succeeds, the promise will resolve with the resolved value from action.
|
||||
* If a fallback function was provided, it will be invoked in the event of any failure or timeout.
|
||||
*/
|
||||
fire(...args: TI): Promise<TR>;
|
||||
|
||||
/**
|
||||
* Provide a health check function to be called periodically.
|
||||
* The function should return a Promise. If the promise is rejected the circuit will open.
|
||||
* This is in addition to the existing circuit behavior as defined by the
|
||||
* `errorThresholdPercentage` option passed to the constructor.
|
||||
* For example, if the health check function provided here always returns a resolved promise,
|
||||
* the circuit can still trip and open if there are failures exceeding the configured threshold.
|
||||
* The health check function is executed within the circuit breaker's execution context,
|
||||
* so this within the function is the circuit breaker itself.
|
||||
*
|
||||
* The interval is the amount of time between calls to the health check function.
|
||||
* Default: 5000 (5 seconds)
|
||||
*/
|
||||
healthCheck(func: () => Promise<void>, interval?: number): void;
|
||||
|
||||
/* tslint:disable:unified-signatures */
|
||||
on(event: "halfOpen", listener: (resetTimeout: number) => void): this;
|
||||
on(event: "close", listener: () => void): this;
|
||||
on(event: "open", listener: () => void): this;
|
||||
on(event: "shutdown", listener: () => void): this;
|
||||
on(event: "fire", listener: (args: TI) => void): this;
|
||||
on(event: "cacheHit", listener: () => void): this;
|
||||
on(event: "cacheMiss", listener: () => void): this;
|
||||
on(event: "reject", listener: (err: Error) => void): this;
|
||||
on(event: "timeout", listener: (err: Error) => void): this;
|
||||
on(event: "success", listener: (result: TR, latencyMs: number) => void): this;
|
||||
on(event: "semaphoreLocked", listener: (err: Error) => void): this;
|
||||
on(event: "healthCheckFailed", listener: (err: Error) => void): this;
|
||||
on(event: "fallback", listener: (result: unknown, err: Error) => void): this;
|
||||
on(event: "failure", listener: (err: Error, latencyMs: number, args: TI) => void): this;
|
||||
/* tslint:enable:unified-signatures */
|
||||
}
|
||||
|
||||
export enum Event {
|
||||
cacheHit = "cacheHit",
|
||||
cacheMiss = "cacheMiss",
|
||||
close = "close",
|
||||
failure = "failure",
|
||||
fallback = "fallback",
|
||||
fire = "fire",
|
||||
halfOpen = "halfOpen",
|
||||
healthCheckFailed = "health-check-failed",
|
||||
open = "open",
|
||||
reject = "reject",
|
||||
semaphoreLocked = "semaphore-locked",
|
||||
success = "success",
|
||||
timeout = "timeout"
|
||||
declare namespace CircuitBreaker {
|
||||
interface Options {
|
||||
/**
|
||||
* The time in milliseconds that action should be allowed to execute before timing out.
|
||||
* @default 10000 (10 seconds)
|
||||
*/
|
||||
timeout?: number;
|
||||
|
||||
/**
|
||||
* The number of times the circuit can fail before opening.
|
||||
* @default 10
|
||||
* @deprecated see options.errorThresholdPercentage
|
||||
*/
|
||||
maxFailures?: number;
|
||||
|
||||
/**
|
||||
* The time in milliseconds to wait before setting the breaker to `halfOpen` state, and trying the action again.
|
||||
* @default 30000 (30 seconds)
|
||||
*/
|
||||
resetTimeout?: number;
|
||||
|
||||
/**
|
||||
* Sets the duration of the statistical rolling window, in milliseconds.
|
||||
* This is how long Opossum keeps metrics for the circuit breaker to use and for publishing.
|
||||
* @default 10000
|
||||
*/
|
||||
rollingCountTimeout?: number;
|
||||
|
||||
/**
|
||||
* Sets the number of buckets the rolling statistical window is divided into.
|
||||
* So, if options.rollingCountTimeout is 10,000, and options.rollingCountBuckets is 10, then the
|
||||
* statistical window will be 1,000 1 second snapshots in the statistical window.
|
||||
* @default 10
|
||||
*/
|
||||
rollingCountBuckets?: number;
|
||||
|
||||
/**
|
||||
* The circuit name to use when reporting stats.
|
||||
* Defaults to the name of the function this circuit controls then falls back to a UUID
|
||||
*/
|
||||
name?: string;
|
||||
|
||||
/**
|
||||
* A grouping key for reporting.
|
||||
* Defaults to the computed value of `name`
|
||||
*/
|
||||
group?: string;
|
||||
|
||||
/**
|
||||
* This property indicates whether execution latencies should be tracked and calculated as percentiles.
|
||||
* If they are disabled, all summary statistics (mean, percentiles) are returned as -1.
|
||||
* @default false
|
||||
*/
|
||||
rollingPercentilesEnabled?: boolean;
|
||||
|
||||
/**
|
||||
* The number of concurrent requests allowed.
|
||||
* If the number currently executing function calls is equal to options.capacity, further calls
|
||||
* to `fire()` are rejected until at least one of the current requests completes.
|
||||
* @default MAX_SAFE_INTEGER
|
||||
*/
|
||||
capacity?: number;
|
||||
|
||||
/**
|
||||
* The error percentage at which to open the circuit and start short-circuiting requests to fallback.
|
||||
* @default 50
|
||||
*/
|
||||
errorThresholdPercentage?: number;
|
||||
|
||||
/**
|
||||
* Whether this circuit is enabled upon construction.
|
||||
* @default true
|
||||
*/
|
||||
enabled?: boolean;
|
||||
|
||||
/**
|
||||
* Determines whether to allow failures without opening the circuit during a brief warmup period (`rollingCountDuration`)
|
||||
* This can help in situations where no matter what your `errorThresholdPercentage` is, if the
|
||||
* first execution times out or fails, the circuit immediately opens.
|
||||
* @default false
|
||||
*/
|
||||
allowWarmUp?: boolean;
|
||||
|
||||
/**
|
||||
* The minimum number of requests within the rolling statistical window that must exist before
|
||||
* the circuit breaker can open. This is similar to `allowWarmUp` in that no matter how many
|
||||
* failures there are, if the number of requests within the statistical window does not exceed
|
||||
* this threshold, the circuit will remain closed.
|
||||
* @default 0
|
||||
*/
|
||||
volumeThreshold?: number;
|
||||
|
||||
/**
|
||||
* An optional function that will be called when the circuit's function fails (returns a rejected Promise).
|
||||
* If this function returns truthy, the circuit's `failPure` statistics will not be incremented.
|
||||
* This is useful, for example, when you don't want HTTP 404 to trip the circuit, but still want to handle it as a failure case.
|
||||
*/
|
||||
errorFilter?: () => boolean;
|
||||
|
||||
/**
|
||||
* Whether the return value of the first successful execution of the circuit's function will be cached.
|
||||
* Once a value has been cached that value will be returned for every subsequent execution: the cache can be cleared using `clearCache`.
|
||||
* (The metrics cacheHit and cacheMiss reflect cache activity.)
|
||||
* @default false
|
||||
*/
|
||||
cache?: boolean;
|
||||
}
|
||||
|
||||
interface Status extends EventEmitter {
|
||||
stats: Stats;
|
||||
window: Window;
|
||||
|
||||
on(event: "snapshot", listener: (snapshot: Stats) => void): this;
|
||||
}
|
||||
|
||||
interface Bucket {
|
||||
failures: number;
|
||||
fallbacks: number;
|
||||
successes: number;
|
||||
rejects: number;
|
||||
fires: number;
|
||||
timeouts: number;
|
||||
cacheHits: number;
|
||||
cacheMisses: number;
|
||||
semaphoreRejections: number;
|
||||
percentiles: { [percentile: number]: number };
|
||||
latencyTimes: number[];
|
||||
}
|
||||
|
||||
type Window = Bucket[];
|
||||
|
||||
interface Stats extends Bucket {
|
||||
latencyMean: number;
|
||||
}
|
||||
}
|
||||
|
||||
export interface CircuitBreakerOptions {
|
||||
/**
|
||||
* The time in milliseconds that action should be allowed to execute before timing out.
|
||||
* Timeout can be disabled by setting this to `false`.
|
||||
*/
|
||||
timeout?: number | false;
|
||||
|
||||
/**
|
||||
* The number of times the circuit can fail before opening.
|
||||
* @deprecated see options.errorThresholdPercentage
|
||||
*/
|
||||
maxFailures?: number;
|
||||
|
||||
/**
|
||||
* The time in milliseconds to wait before setting the breaker to `halfOpen` state, and trying the action again.
|
||||
*/
|
||||
resetTimeout?: number;
|
||||
|
||||
/**
|
||||
* Sets the duration of the statistical rolling window, in milliseconds.
|
||||
* This is how long Opossum keeps metrics for the circuit breaker to use and for publishing.
|
||||
* @default 10000
|
||||
*/
|
||||
rollingCountTimeout?: number;
|
||||
|
||||
/**
|
||||
* Sets the number of buckets the rolling statistical window is divided into.
|
||||
* So, if options.rollingCountTimeout is 10,000, and options.rollingCountBuckets is 10, then the
|
||||
* statistical window will be 1,000 1 second snapshots in the statistical window.
|
||||
* @default 10
|
||||
*/
|
||||
rollingCountBuckets?: number;
|
||||
|
||||
/**
|
||||
* The circuit name to use when reporting stats.
|
||||
* Defaults to the name of the action function then falls back to a UUID
|
||||
*/
|
||||
name?: string;
|
||||
|
||||
/**
|
||||
* A grouping key for reporting.
|
||||
* Defaults to the computed value of options.name
|
||||
*/
|
||||
group?: string;
|
||||
|
||||
/**
|
||||
* This property indicates whether execution latencies should be tracked and calculated as percentiles.
|
||||
* If they are disabled, all summary statistics (mean, percentiles) are returned as -1.
|
||||
* @default true
|
||||
*/
|
||||
rollingPercentilesEnabled?: boolean;
|
||||
|
||||
/**
|
||||
* The number of concurrent requests allowed.
|
||||
* If the number currently executing function calls is equal to options.capacity, further calls
|
||||
* to `fire()` are rejected until at least one of the current requests completes.
|
||||
* @default MAX_SAFE_INTEGER
|
||||
*/
|
||||
capacity?: number;
|
||||
|
||||
/**
|
||||
* The error percentage at which to open the circuit and start short-circuiting requests to fallback.
|
||||
*/
|
||||
errorThresholdPercentage?: number;
|
||||
|
||||
/**
|
||||
* Whether this circuit is enabled upon construction.
|
||||
* @default true
|
||||
*/
|
||||
enabled?: boolean;
|
||||
|
||||
/**
|
||||
* Determines whether to allow failures without opening the circuit during a brief warmup period
|
||||
* This can help in situations where no matter what your `errorThresholdPercentage` is, if the
|
||||
* first execution times out or fails, the circuit immediately opens.
|
||||
* @default false
|
||||
*/
|
||||
allowWarmUp?: boolean;
|
||||
|
||||
/**
|
||||
* The minimum number of requests within the rolling statistical window that must exist before
|
||||
* the circuit breaker can open. This is similar to `allowWarmUp` in that no matter how many
|
||||
* failures there are, if the number of requests within the statistical window does not exceed
|
||||
* this threshold, the circuit will remain closed.
|
||||
* @default 0
|
||||
*/
|
||||
volumeThreshold?: number;
|
||||
|
||||
/**
|
||||
* If set to true, the value from the first call to `fire` will be cached an subsequent calls
|
||||
* will not execute the `action` function, but return the cached value instead.
|
||||
* @default false
|
||||
*/
|
||||
cache?: boolean;
|
||||
}
|
||||
|
||||
export interface Status extends EventEmitter {
|
||||
stats: Stats;
|
||||
window: Window;
|
||||
|
||||
increment(property: string, latencyRunTime?: number): void;
|
||||
open(): void;
|
||||
close(): void;
|
||||
}
|
||||
|
||||
export interface Bucket {
|
||||
failures: number;
|
||||
fallbacks: number;
|
||||
successes: number;
|
||||
rejects: number;
|
||||
fires: number;
|
||||
timeouts: number;
|
||||
cacheHits: number;
|
||||
cacheMisses: number;
|
||||
semaphoreRejections: number;
|
||||
percentiles: { [percentile: number]: number };
|
||||
latencyTimes: number[];
|
||||
}
|
||||
|
||||
export type Window = Bucket[];
|
||||
|
||||
export interface Stats extends Bucket {
|
||||
latencyMean: number;
|
||||
}
|
||||
|
||||
export class HystrixStats {
|
||||
constructor(circuit: CircuitBreaker);
|
||||
|
||||
getHystrixStream(): stream.Transform;
|
||||
}
|
||||
|
||||
export function promisify(action: Action): (...args: any[]) => Promise<any>;
|
||||
export const stats: stream.Transform;
|
||||
|
||||
interface index {
|
||||
(action: Action, options: CircuitBreakerOptions): CircuitBreaker;
|
||||
|
||||
promisify: (action: Action) => (...args: any[]) => Promise<any>;
|
||||
stats: stream.Transform;
|
||||
}
|
||||
|
||||
export const circuitBreaker: index;
|
||||
export default circuitBreaker;
|
||||
export = CircuitBreaker;
|
||||
|
||||
@ -1,34 +1,25 @@
|
||||
import * as fs from "fs";
|
||||
import circuitBreaker, {
|
||||
CircuitBreaker,
|
||||
CircuitBreakerOptions,
|
||||
promisify,
|
||||
stats,
|
||||
Stats,
|
||||
Window
|
||||
} from "opossum";
|
||||
|
||||
let readFile = promisify(fs.readFile);
|
||||
stats.removeAllListeners();
|
||||
import * as CircuitBreaker from "opossum";
|
||||
import { promisify } from "util";
|
||||
|
||||
let breaker: CircuitBreaker;
|
||||
const callbackNoArgs = () => console.log("foo");
|
||||
const callbackNoArgs = async () => console.log("foo");
|
||||
|
||||
breaker = circuitBreaker(() => true, {
|
||||
timeout: false,
|
||||
maxFailures: 50,
|
||||
resetTimeout: 10,
|
||||
rollingCountTimeout: 500,
|
||||
rollingCountBuckets: 20,
|
||||
name: "test",
|
||||
group: "group",
|
||||
rollingPercentilesEnabled: true,
|
||||
capacity: 1,
|
||||
errorThresholdPercentage: 1,
|
||||
enabled: true,
|
||||
allowWarmUp: true,
|
||||
volumeThreshold: 1,
|
||||
cache: true
|
||||
breaker = new CircuitBreaker(async () => true, {
|
||||
timeout: 1000,
|
||||
maxFailures: 50,
|
||||
resetTimeout: 10,
|
||||
rollingCountTimeout: 500,
|
||||
rollingCountBuckets: 20,
|
||||
name: "test",
|
||||
group: "group",
|
||||
rollingPercentilesEnabled: true,
|
||||
capacity: 1,
|
||||
errorThresholdPercentage: 1,
|
||||
enabled: true,
|
||||
allowWarmUp: true,
|
||||
volumeThreshold: 1,
|
||||
cache: true
|
||||
});
|
||||
|
||||
breaker.name; // $ExpectType string
|
||||
@ -43,7 +34,6 @@ breaker.isShutdown; // $ExpectType boolean
|
||||
breaker.volumeThreshold; // $ExpectType number
|
||||
breaker.status.stats.latencyMean; // $ExpectType number
|
||||
breaker.stats.latencyTimes; // $ExpectType number[]
|
||||
breaker.hystrixStats.getHystrixStream().removeAllListeners();
|
||||
|
||||
breaker.clearCache(); // $ExpectType void
|
||||
breaker.open(); // $ExpectType void
|
||||
@ -52,6 +42,22 @@ breaker.disable(); // $ExpectType void
|
||||
breaker.enable(); // $ExpectType void
|
||||
breaker.shutdown(); // $ExpectType void
|
||||
|
||||
// Check the generic types pass down correctly from constructor to `fire` and events.
|
||||
const action = async (foo: string, bar: number) => {
|
||||
return foo ? bar : bar * 2;
|
||||
};
|
||||
const typedBreaker = new CircuitBreaker(action);
|
||||
typedBreaker.fire(5, "hello"); // $ExpectError
|
||||
typedBreaker.fire("hello world", 42); // $ExpectType Promise<number>
|
||||
typedBreaker.on("success", (result, latencyMs) => {
|
||||
result; // $ExpectType number
|
||||
latencyMs; // $ExpectType number
|
||||
});
|
||||
typedBreaker.on("fire", ([foo, bar]) => {
|
||||
foo; // $ExpectType string
|
||||
bar; // $ExpectType number
|
||||
});
|
||||
|
||||
// The following are examples are from the libs README and official documentation
|
||||
// https://nodeshift.github.io/opossum/index.html.
|
||||
|
||||
@ -62,25 +68,25 @@ function asyncFunctionThatCouldFail(x: any, y: any) {
|
||||
});
|
||||
}
|
||||
|
||||
const options: CircuitBreakerOptions = {
|
||||
timeout: 3000, // If our function takes longer than 3 seconds, trigger a failure
|
||||
errorThresholdPercentage: 50, // When 50% of requests fail, trip the circuit
|
||||
resetTimeout: 30000 // After 30 seconds, try again.
|
||||
const options: CircuitBreaker.Options = {
|
||||
timeout: 3000, // If our function takes longer than 3 seconds, trigger a failure
|
||||
errorThresholdPercentage: 50, // When 50% of requests fail, trip the circuit
|
||||
resetTimeout: 30000 // After 30 seconds, try again.
|
||||
};
|
||||
breaker = circuitBreaker(asyncFunctionThatCouldFail, options);
|
||||
breaker = new CircuitBreaker(asyncFunctionThatCouldFail, options);
|
||||
|
||||
breaker
|
||||
.fire("foo")
|
||||
.then(console.log)
|
||||
.catch(console.error);
|
||||
|
||||
breaker = circuitBreaker(asyncFunctionThatCouldFail, options);
|
||||
breaker = new CircuitBreaker(asyncFunctionThatCouldFail, options);
|
||||
// if asyncFunctionThatCouldFail starts to fail, firing the breaker
|
||||
// will trigger our fallback function
|
||||
breaker.fallback(() => "Sorry, out of service right now");
|
||||
breaker.on("fallback", result => console.log(result));
|
||||
|
||||
breaker = circuitBreaker(callbackNoArgs, options);
|
||||
breaker = new CircuitBreaker(callbackNoArgs, options);
|
||||
|
||||
breaker.fallback(callbackNoArgs);
|
||||
|
||||
@ -92,28 +98,26 @@ breaker.on("halfOpen", callbackNoArgs);
|
||||
breaker.on("close", callbackNoArgs);
|
||||
breaker.on("fallback", data => console.log(data));
|
||||
|
||||
readFile = circuitBreaker.promisify(fs.readFile);
|
||||
breaker = circuitBreaker(readFile, options);
|
||||
const readFile = promisify(fs.readFile);
|
||||
breaker = new CircuitBreaker(readFile, options);
|
||||
|
||||
breaker
|
||||
.fire("./package.json", "utf-8")
|
||||
.then(console.log)
|
||||
.catch(console.error);
|
||||
|
||||
breaker = circuitBreaker(fs.readFile, {});
|
||||
|
||||
breaker.hystrixStats.getHystrixStream().pipe(process.stdout);
|
||||
breaker = new CircuitBreaker(readFile, {});
|
||||
|
||||
// Creates a 1 second window consisting of ten time slices,
|
||||
// each 100ms long.
|
||||
const circuit = circuitBreaker(fs.readFile, {
|
||||
rollingCountBuckets: 10,
|
||||
rollingCountTimeout: 1000
|
||||
const circuit = new CircuitBreaker(readFile, {
|
||||
rollingCountBuckets: 10,
|
||||
rollingCountTimeout: 1000
|
||||
});
|
||||
|
||||
// get the cumulative statistics for the last second
|
||||
const theStats: Stats = breaker.status.stats;
|
||||
const theStats: CircuitBreaker.Stats = breaker.status.stats;
|
||||
|
||||
// get the array of 10, 1 second time slices for the last second
|
||||
const window: Window = breaker.status.window;
|
||||
const window: CircuitBreaker.Window = breaker.status.window;
|
||||
window[0].fires; // $ExpectType number
|
||||
|
||||
Loading…
Reference in New Issue
Block a user