Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add type declarations. #69

Merged
merged 6 commits into from
Mar 11, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"subscribe"
],
"main": "src/index.js",
"types": "types/index.d.ts",
"scripts": {
"lint": "eslint . && prettier -c .",
"format": "eslint --fix . && prettier --write .",
Expand Down Expand Up @@ -39,6 +40,7 @@
"uuid": "^9.0.0"
},
"devDependencies": {
"@types/amqplib": "^0.10.5",
"child-process-promise": "^2.2.1",
"dot-only-hunter": "^1.0.3",
"eslint": "^8.25.0",
Expand All @@ -48,7 +50,8 @@
"mocha": "^10.0.0",
"nyc": "^15.1.0",
"prettier": "^3.0.0",
"sinon": "^17.0.1"
"sinon": "^17.0.1",
"typescript": "^5.3.3"
},
"engines": {
"node": ">=14"
Expand Down
28 changes: 28 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Just for parsing the types in the types folder. Not actually for compilation.
yosiat marked this conversation as resolved.
Show resolved Hide resolved

{
"compilerOptions": {
"allowJs": false,
"allowUnreachableCode": false,
"allowUnusedLabels": false,
"declaration": true,
"declarationMap": true,
"forceConsistentCasingInFileNames": true,
"inlineSources": true,
"module": "commonjs",
"noEmitOnError": true,
"noFallthroughCasesInSwitch": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
"noUnusedLocals": true,
"pretty": true,
"sourceMap": true,
"strict": true,
"strictNullChecks": true,
"target": "es2017",
"incremental": true,
"newLine": "LF",
"outDir": "types"
},
"files": ["src/index.js"]
}
61 changes: 61 additions & 0 deletions types/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Base types were generated using the command:
*
* npx -p typescript tsc src/index.js --declaration --allowJs --emitDeclarationOnly --outDir types
*
* Then, all private methods were removed from the types, and all the 'any' placeholders were replaced with handcrafted types to match the expected values, and exported in addition to the actual classes as part of the module.
* Any jsdoc comments with parameter descriptions were updated accordingly.
*/

import { ConnectionConfig, Connection } from './modules/connection';
import Consumer = require('./modules/consumer');
import Producer = require('./modules/producer');
import { ConnectionHooks, ConsumerHooks, ProducerHooks } from './modules/hooks';

declare function arnavmq(config: ConnectionConfig): {
yosiat marked this conversation as resolved.
Show resolved Hide resolved
connection: Connection;
consume: typeof Consumer.prototype.consume;
subscribe: typeof Consumer.prototype.consume;
produce: typeof Producer.prototype.produce;
publish: typeof Producer.prototype.produce;
consumer: {
consume: typeof Consumer.prototype.consume;
subscribe: typeof Consumer.prototype.consume;
};
producer: {
produce: typeof Producer.prototype.produce;
publish: typeof Producer.prototype.produce;
};
hooks: {
connection: ConnectionHooks;
consumer: ConsumerHooks;
producer: ProducerHooks;
};
};

declare namespace arnavmq {
export type ArnavmqFactory = (config: ConnectionConfig) => {
connection: Connection;
consume: typeof Consumer.prototype.consume;
subscribe: typeof Consumer.prototype.consume;
produce: typeof Producer.prototype.produce;
publish: typeof Producer.prototype.produce;
consumer: {
consume: typeof Consumer.prototype.consume;
subscribe: typeof Consumer.prototype.consume;
};
producer: {
produce: typeof Producer.prototype.produce;
publish: typeof Producer.prototype.produce;
};
hooks: {
connection: ConnectionHooks;
consumer: ConsumerHooks;
producer: ProducerHooks;
};
};

export { ConnectionConfig, Connection, Consumer, Producer, ConnectionHooks, ConsumerHooks, ProducerHooks };
}

export = arnavmq;
27 changes: 27 additions & 0 deletions types/modules/arnavmq.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import Producer = require('./producer');
import Consumer = require('./consumer');
import { Connection } from './connection';
import { ConnectionHooks, ConsumerHooks, ProducerHooks } from './hooks';

declare function arnavmq(connection: any): {
yosiat marked this conversation as resolved.
Show resolved Hide resolved
connection: Connection;
consume: typeof Consumer.prototype.consume;
subscribe: typeof Consumer.prototype.consume;
produce: typeof Producer.prototype.produce;
publish: typeof Producer.prototype.produce;
consumer: {
consume: typeof Consumer.prototype.consume;
subscribe: typeof Consumer.prototype.consume;
};
producer: {
produce: typeof Producer.prototype.produce;
publish: typeof Producer.prototype.produce;
};
hooks: {
connection: ConnectionHooks;
consumer: ConsumerHooks;
producer: ProducerHooks;
};
};

export = arnavmq;
32 changes: 32 additions & 0 deletions types/modules/channels.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import type amqp = require('amqplib');

interface ChannelConfig {
prefetch: number;
}

declare class Channels {
constructor(connection: amqp.Connection, config: ChannelConfig);
_connection: any;
yosiat marked this conversation as resolved.
Show resolved Hide resolved
_config: any;
_channels: Map<any, any>;
yosiat marked this conversation as resolved.
Show resolved Hide resolved
get(queue: any, config: any): Promise<any>;
yosiat marked this conversation as resolved.
Show resolved Hide resolved
defaultChannel(): Promise<any>;
/**
* Creates or returns an existing channel by it's key and config.
* @return {Promise} A promise that resolve with an amqp.node channel object
*/
_get(key: any, config?: {}): Promise<any>;
yosiat marked this conversation as resolved.
Show resolved Hide resolved
_initNewChannel(key: any, config: any): Promise<any>;
yosiat marked this conversation as resolved.
Show resolved Hide resolved
}

declare class ChannelAlreadyExistsError extends Error {
constructor(name: any, config: any);
name: any;
config: any;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
constructor(name: any, config: any);
name: any;
config: any;
}
constructor(name: string, config: any);
public readonly name: string;
public readonly config: any;
}

and please type config

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ramhr what about this ?


declare namespace channels {
export { Channels, ChannelAlreadyExistsError, ChannelConfig };
}

export = channels;
88 changes: 88 additions & 0 deletions types/modules/connection.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import amqp = require('amqplib');
import channels = require('./channels');
import { Logger } from './logger';

interface ConnectionConfig {
/**
* amqp connection string
* @default 'amqp://localhost'
*/
host?: string;

/**
* number of fetched messages at once on the channel
* @default 5
*/
prefetch?: number;

/**
* requeue put back message into the broker if consumer crashes/trigger exception
* @default true
*/
requeue?: boolean;

/**
* time between two reconnect (ms)
* @default 1000
*/
timeout?: number;

/**
* the maximum number of retries when trying to send a message before throwing error when failing. If set to '0' will not retry. If set to less then '0', will retry indefinitely.
* @default -1
*/
producerMaxRetries?: number;

/**
* default timeout for RPC calls. If set to '0' there will be none
* @default 15000
*/
rpcTimeout?: number;

/**
* Suffix all queues names. Defaults to empty string.
* For example, a queue service-something with suffix :ci becomes service-something:ci etc.
* @default ''
*/
consumerSuffix?: string;

/** generate a hostname so we can track this connection on the broker (rabbitmq management plugin) - defaults to host from environment or random uuid */
hostname?: string;

/** A logger object with a log function for each of the log levels ("debug", "info", "warn", or "error"). */
logger?: Logger;
}

declare class Connection {
constructor(config: ConnectionConfig);

getConnection(): Promise<amqp.Connection>;
getChannel(queue: string, config: channels.ChannelConfig): Promise<amqp.Channel>;
getDefaultChannel(): Promise<amqp.Channel>;
/**
* Register an event on the default amqp.node channel
* @param on the channel event name to be bound with
* @param func the callback function to execute when the event is called
*/
addListener(on: string, func: Function): Promise<void>;
}

declare function connection(config: ConnectionConfig): Connection;

declare namespace connection {
export interface Connection {
getConnection(): Promise<amqp.Connection>;
getChannel(queue: string, config: channels.ChannelConfig): Promise<amqp.Channel>;
getDefaultChannel(): Promise<amqp.Channel>;
/**
* Register an event on the default amqp.node channel
* @param on the channel event name to be bound with
* @param func the callback function to execute when the event is called
*/
addListener(on: string, func: Function): Promise<void>;
}

export { ConnectionConfig };
}

export = connection;
36 changes: 36 additions & 0 deletions types/modules/consumer.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { ChannelConfig } from './channels';
import { Connection } from './connection';
import { ConsumerHooks } from './hooks';
import type amqp = require('amqplib');

declare class Consumer {
constructor(connection: Connection);
hooks: ConsumerHooks;
set connection(value: Connection);
get connection(): Connection;
/**
* Sends the RPC reply to the response queue according to the message properties when required.
* @param messageProperties An amqp.node message properties object, containing the rpc settings
* @param queue The initial queue on which the handler received the message
* @param reply the received message to reply the rpc if needed:
* @return The message properties if it is not an rpc request, or a boolean indicating the produce result when an rpc response was produced.
*/
checkRpc(
yosiat marked this conversation as resolved.
Show resolved Hide resolved
messageProperties: amqp.MessageProperties,
queue: string,
reply: unknown,
): Promise<boolean | amqp.MessageProperties>;
/**
* Create a durable queue on RabbitMQ and consumes messages from it - executing a callback function.
* Automatically answers with the callback response (can be a Promise)
* @param queue The RabbitMQ queue name
* @param options (Optional) Options for the queue (durable, persistent, etc.) and channel (with prefetch, `{ channel: { prefetch: 100 } }`)
* @param callback Callback function executed when a message is received on the queue name, can return a promise
* @return A promise that resolves when connection is established and consumer is ready
*/
consume(queue: string, options: ChannelConfig, callback: ConsumeCallback): Promise<any>;
yosiat marked this conversation as resolved.
Show resolved Hide resolved
}

type ConsumeCallback = (body: unknown, properties: amqp.MessageProperties) => Promise<unknown> | unknown;

export = Consumer;
52 changes: 52 additions & 0 deletions types/modules/hooks/base_hooks.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
declare class BaseHooks {
/**
* A map between an event name to a set of callbacks registered for it.
* Function shape varies between different events.
* @private
*/
private _events: Map<string, Set<Function>>;
/**
* Registers a callback or array of callbacks to an event.
* Callback function shape may vary according to the event type.
* Upon a hook trigger, the callbacks for it will be invoked one by one, but without a particular order.
* The user who registers the callback has the responsibility to handle any error inside of it. Throwing an error inside a callback will propagate it outside to the top level, aborting the process that triggered it.
* @param {string} event The event name to register.
* @param {(Function|Function[])} callback A callback or array of callbacks to register for the event.
* @protected
*/
protected _on(event: string, callback: Function | Function[]): void;
/**
* Registers a number of callbacks for an event.
* @param {string} event The event name to register.
* @param {Function|Function[]} callbacks A callback array register for the event.
* @private
*/
private _manyOn;
yosiat marked this conversation as resolved.
Show resolved Hide resolved
/**
* Unregister a callback or array of callbacks from an event.
* Callbacks must be a reference to the same callbacks that registered.
* @param {string} event The event to unregister.
* @param {(Function|Function[])} callback A callback or array of callbacks to unregistered from the event.
* @protected
*/
protected _off(event: string, callback: Function | Function[]): void;
/**
* Unregister a number of callbacks from an event.
* @param {string} event The event to unregister.
* @param {Function[]} callbacks A callback array to unregistered from the event.
* @private
*/
private _manyOff;
/**
* Trigger an event, calling all callbacks registered to it with the given payload.
* @param source The class/object that triggered the event. Will be bound as the 'this' argument of the callbacks.
* @param eventName The name of the event to trigger.
* @param payload The event to pass to the registered callbacks as an argument.
* @public
*/
public trigger(source: unknown, eventName: string, payload: unknown): Promise<void>;
/** @private */
private _getCallbacks;
}

export = BaseHooks;
Loading
Loading