diff --git a/types/cometd/cometd-tests.ts b/types/cometd/cometd-tests.ts new file mode 100644 index 0000000000..ada9412c04 --- /dev/null +++ b/types/cometd/cometd-tests.ts @@ -0,0 +1,241 @@ +import { CometD, Listener, Message } from "cometd"; + +const cometd = new CometD(); + +// Configuring +// =========== + +cometd.configure("http://localhost:8080/cometd"); + +cometd.configure({ + url: "http://localhost:8080/cometd" +}); + +cometd.registerExtension("ack", { incoming: () => {}, outgoing: () => {} }); + +cometd.unregisterTransport("websocket"); + +// Handshaking +// =========== + +cometd.handshake(handshakeReply => { + if (handshakeReply.successful) { + // Successfully connected to the server. + } +}); + +const additionalInfoHandshake = { + "com.acme.credentials": { + user: "cometd", + token: "xyzsecretabc" + } +}; +cometd.handshake(additionalInfoHandshake, handshakeReply => { + if (handshakeReply.successful) { + // Successfully connected to the server. + } +}); + +cometd.init("http://host1:8080/cometd"); + +// Subscribing and Unsubscribing +// ============================= + +cometd.subscribe( + "/foo", + message => {}, + subscribeReply => { + if (subscribeReply.successful) { + // The server successfully subscribed this client to the "/foo" channel. + } + } +); + +const additionalInfoSubscribe = { + "com.acme.priority": 10 +}; +cometd.subscribe("/foo", message => {}, additionalInfoSubscribe, subscribeReply => { + if (subscribeReply.successful) { + // The server successfully subscribed this client to the "/foo" channel. + } +}); + +const subscription1 = cometd.addListener("/meta/connect", () => {}); +const subscription2 = cometd.subscribe("/foo/bar/", () => {}); + +cometd.unsubscribe(subscription2); +cometd.removeListener(subscription1); + +const subscription3 = cometd.subscribe("/foo/bar/", () => {}); + +const additionalInfoUnsubscribe = { + "com.acme.discard": true +}; +cometd.unsubscribe(subscription3, additionalInfoUnsubscribe, unsubscribeReply => { + // Your logic here. +}); + +// Subscribers versus Listeners +// ============================ + +let _reportListener: Listener | undefined; + +cometd.addListener("/meta/handshake", message => { + // Only subscribe if the handshake is successful + if (message.successful) { + // Batch all subscriptions together + cometd.batch(() => { + // Correct to subscribe to broadcast channels + cometd.subscribe("/members", m => {}); + + // Correct to subscribe to service channels + cometd.subscribe("/service/status", m => {}); + + // Messy to add listeners after removal, prefer using cometd.subscribe(...) + if (_reportListener) { + cometd.removeListener(_reportListener); + _reportListener = cometd.addListener("/service/report", m => {}); + } + + // Wrong to add listeners without removal + cometd.addListener("/service/notification", m => {}); + }); + } +}); + +// Dynamic Resubscription +// ====================== + +let _subscription: Listener | undefined; + +class Controller { + dynamicSubscribe = () => { + _subscription = cometd.subscribe("/dynamic", this.onEvent); + } + + onEvent = (message: Message) => { + if (message.successful) { + // Your logic here. + } + } + + dynamicUnsubscribe = () => { + if (_subscription) { + cometd.unsubscribe(_subscription); + _subscription = undefined; + } + } +} + +cometd.addListener("/meta/handshake", message => { + if (message.successful) { + cometd.batch(() => { + // Static subscription, no need to remember the subscription handle + cometd.subscribe("/static", () => {}); + + // Dynamic re-subscription + if (_subscription) { + _subscription = cometd.resubscribe(_subscription); + } + }); + } +}); + +// Listeners and Subscribers Exception Handling +// ============================================ + +cometd.onListenerException = function(exception, subscriptionHandle, isListener, message) { + // Uh-oh, something went wrong, disable this listener/subscriber + // Object "this" points to the CometD object + if (isListener) { + this.removeListener(subscriptionHandle); + } else { + this.unsubscribe(subscriptionHandle); + } +}; + +// Meta Channel List +// ================= + +let _connected = false; + +cometd.addListener("/meta/connect", message => { + if (cometd.isDisconnected()) { + return; + } + + const wasConnected = _connected; + _connected = message.successful; + if (!wasConnected && _connected) { + // Reconnected + } else if (wasConnected && !_connected) { + // Disconnected + } +}); + +cometd.addListener("/meta/disconnect", message => { + if (message.successful) { + _connected = false; + } +}); + +// Publishing +// ========== + +cometd.publish("/mychannel", { mydata: { foo: "bar" } }); + +cometd.publish("/mychannel", { mydata: { foo: "bar" } }, publishAck => { + if (publishAck.successful) { + // The message reached the server + } +}); + +// Publishing Binary Data +// ====================== + +// Create an ArrayBuffer. +const buffer = new ArrayBuffer(4); + +// Fill it with the bytes. +const view = new DataView(buffer); +view.setUint8(0, 0xca); +view.setUint8(1, 0xfe); +view.setUint8(2, 0xba); +view.setUint8(3, 0xbe); + +// Send it. +cometd.publishBinary("/binary", view, true, { prolog: "java" }); + +// Disconnecting +// ============= + +cometd.disconnect(disconnectReply => { + if (disconnectReply.successful) { + // Server truly received the disconnect request + } +}); + +const additionalInfoDisconnect = { + "com.acme.reset": false +}; +cometd.disconnect(additionalInfoDisconnect, disconnectReply => { + if (disconnectReply.successful) { + // Server truly received the disconnect request + } +}); + +// Message Batching +// ================ + +cometd.batch(() => { + cometd.publish("/channel1", { product: "foo" }); + cometd.publish("/channel2", { notificationType: "all" }); + cometd.publish("/channel3", { update: false }); +}); + +// Alternatively, but not recommended: +cometd.startBatch(); +cometd.publish("/channel1", { product: "foo" }); +cometd.publish("/channel2", { notificationType: "all" }); +cometd.publish("/channel3", { update: false }); +cometd.endBatch(); diff --git a/types/cometd/index.d.ts b/types/cometd/index.d.ts index cc97b304fe..32e4f962bb 100644 --- a/types/cometd/index.d.ts +++ b/types/cometd/index.d.ts @@ -1,55 +1,467 @@ -// Type definitions for CometD 2.5.1 +// Type definitions for CometD 4.0 // Project: http://cometd.org -// Definitions by: Derek Cicerone +// Definitions by: Derek Cicerone , Daniel Perez Alvarez // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// TypeScript Version: 2.3 -declare namespace CometD { - - interface ConfigurationOptions { - url: string; - logLevel?: string; - maxConnections?: number; - backoffIncrement?: number; - maxBackoff?: number; - reverseIncomingExtensions?: boolean; - maxNetworkDelay?: number; - requestHeaders?: any; - appendMessageTypeToURL?: boolean; - autoBatch?: boolean; - } - +export interface Configuration { + /** + * The URL of the Bayeux server this client will connect to. + */ + url: string; + /** + * The log level. Possible values are: "warn", "info", "debug". Output to `window.console` if + * available. + */ + logLevel?: string; + /** + * The maximum number of connections used to connect to the Bayeux server. Change this value + * only if you know exactly the client’s connection limit and what "request queued behind long + * poll" means. + */ + maxConnections?: number; + /** + * The number of milliseconds that the backoff time increments every time a connection with the + * Bayeux server fails. CometD attempts to reconnect after the backoff time elapses. + */ + backoffIncrement?: number; + /** + * The maximum number of milliseconds of the backoff time after which the backoff time is not + * incremented further. + */ + maxBackoff?: number; + /** + * The maximum number of milliseconds to wait before considering a request to the Bayeux server + * failed. + */ + maxNetworkDelay?: number; + /** + * An object containing the request headers to be sent for every Bayeux request (for example, + * `{"My-Custom-Header": "MyValue"}`). + */ + requestHeaders?: object; + /** + * Determines whether or not the Bayeux message type (handshake, connect, disconnect) is + * appended to the URL of the Bayeux server (see above). + */ + appendMessageTypeToURL?: boolean; + /** + * Determines whether multiple publishes that get queued are sent as a batch on the first + * occasion, without requiring explicit batching. + */ + autoBatch?: boolean; + /** + * The maximum number of milliseconds to wait for a WebSocket connection to be opened. It does + * not apply to HTTP connections. A timeout value of 0 means to wait forever. + */ + connectTimeout?: number; + /** + * Only applies to the websocket transport. Determines whether to stick using the websocket + * transport when a websocket transport failure has been detected after the websocket transport + * was able to successfully connect to the server. + */ + stickyReconnect?: boolean; + /** + * The max length of the URI for a request made with the callback-polling transport. Microsoft + * Internet Explorer 7 and 8 are known to limit the URI length, so single large messages sent by + * CometD may fail to remain within the max URI length when encoded in JSON. + */ + maxURILength?: number; } -interface CometD { - - websocketEnabled?: boolean; +export interface Message { + successful: boolean; + data: any; +} - onListenerException: (exception: any, subscriptionHandle: any, isListener: boolean, message: string) => void; +export type Listener = (message: Message) => void; - init(options: CometD.ConfigurationOptions): void; +export interface Extension { + incoming?: Listener; + outgoing?: Listener; +} - configure(config: CometD.ConfigurationOptions): void; - - subscribe(channel: string, listener: (message: any) => void): void; +export class CometD { + constructor(options?: Configuration); - addListener(channel: string, listener: (message: any) => void): void; - removeListener(listener: (message: any) => void): void; + /** + * Registers the given transport under the given transport type. + * + * The optional index parameter specifies the priority at which the transport is registered + * (where `0` is the max priority). + * + * If a transport with the same type is already registered, this function does nothing and + * returns `false`. + * + * @param type the transport type + * @param transport the transport object + * @param index the index at which this transport is to be registered + * @return true if the transport has been registered, false otherwise + */ + registerTransport(type: string, transport: object, index?: number): boolean; + /** + * Unregisters the transport with the given transport type. + * + * @param type the transport type to unregister + * @return the transport that has been unregistered, or null if no transport was previously + * registered under the given transport type + */ + unregisterTransport(type: string): void; + + /** + * Unregisters all transports. + */ + unregisterTransports(): void; + + /** + * Configures and establishes the Bayeux communication with the Bayeux server via a handshake + * and a subsequent connect. + * + * @param configuration the configuration object + * @param handshakeProps an object to be merged with the handshake message + */ + init(configuration: string | Configuration, handshakeProps?: object): void; + + /** + * Configures the initial Bayeux communication with the Bayeux server. + * + * @param configuration the URL of the Bayeux server, or a configuration object that must + * contain a mandatory field `url` + */ + configure(config: string | Configuration): void; + + /** + * Establishes the Bayeux communication with the Bayeux server via a handshake and a subsequent + * connect. + * + * @param handshakeCallback a function to be invoked when the handshake is acknowledged + */ + handshake(handshakeCallback: Listener): void; + + /** + * Establishes the Bayeux communication with the Bayeux server via a handshake and a subsequent + * connect. + * + * @param handshakeProps an object to be merged with the handshake message + * @param handshakeCallback a function to be invoked when the handshake is acknowledged + */ + handshake(handshakeProps: object, handshakeCallback: Listener): void; + + /** + * Disconnects from the Bayeux server. + * + * @param disconnectCallback a function to be invoked when the disconnect is acknowledged + */ + disconnect(disconnectCallback: Listener): void; + + /** + * Disconnects from the Bayeux server. + * + * @param disconnectProps an object to be merged with the disconnect message + * @param disconnectCallback a function to be invoked when the disconnect is acknowledged + */ + disconnect(disconnectProps: object, disconnectCallback: Listener): void; + + /** + * Marks the start of a batch of application messages to be sent to the server in a single + * request, obtaining a single response containing (possibly) many application reply messages. + * + * Messages are held in a queue and not sent until `endBatch` is called. If `startBatch` is + * called multiple times, then an equal number of `endBatch` calls must be made to close and + * send the batch of messages. + */ + startBatch(): void; + + /** + * Marks the end of a batch of application messages to be sent to the server in a single + * request. + */ + endBatch(): void; + + /** + * Executes the given callback in the given scope, surrounded by a `startBatch` and `endBatch` + * calls. + * + * @param callback the callback to be executed within `startBatch` and `endBatch` calls + */ + batch(callback: () => void): void; + + /** + * Adds a listener for Bayeux messages, performing the given callback in the given scope when a + * message for the given channel arrives. + * + * - Must be used to listen to meta channel messages. + * - May be used to listen to service channel messages. + * - Should not be used to listen broadcast channel messages (use `subscribe` instead). + * - Does not involve any communication with the Bayeux server, and as such can be called before + * calling `handshake`. + * - Is synchronous: when it returns, you are guaranteed that the listener has been added. + * + * @param channel the channel the listener is interested to + * @param callback the callback to call when a message is sent to the channel + * @returns the subscription handle to be passed to `removeListener` + */ + addListener(channel: string, callback: Listener): Listener; + + /** + * Removes the subscription obtained with a call to `addListener`. + * + * @param subscription the subscription to unsubscribe. + */ + removeListener(subscription: Listener): void; + + /** + * Removes all listeners registered with `addListener` or `subscribe`. + */ clearListeners(): void; + /** + * Subscribes to the given channel, performing the given callback in the given scope when a + * message for the channel arrives. + * + * - Must not be used to listen to meta channels messages (if attempted, the server returns an + * error). + * - May be used to listen to service channel messages. + * - Should be used to listen to broadcast channel messages. + * - Involves a communication with the Bayeux server and as such cannot be called before calling + * `handshake`. + * - Is asynchronous: it returns immediately, well before the Bayeux server has received the + * subscription request. + * + * @param channel the channel to subscribe to + * @param callback the callback to call when a message is sent to the channel + * @param subscribeCallback a function to be invoked when the subscription is acknowledged + * @return the subscription handle to be passed to `unsubscribe` + */ + subscribe(channel: string, callback: Listener, subscribeCallback?: Listener): Listener; + + /** + * Subscribes to the given channel, performing the given callback in the given scope when a + * message for the channel arrives. + * + * - Must not be used to listen to meta channels messages (if attempted, the server returns an + * error). + * - May be used to listen to service channel messages. + * - Should be used to listen to broadcast channel messages. + * - Involves a communication with the Bayeux server and as such cannot be called before calling + * `handshake`. + * - Is asynchronous: it returns immediately, well before the Bayeux server has received the + * subscription request. + * + * @param channel the channel to subscribe to + * @param callback the callback to call when a message is sent to the channel + * @param subscribeProps an object to be merged with the subscribe message + * @param subscribeCallback a function to be invoked when the subscription is acknowledged + * @return the subscription handle to be passed to `unsubscribe` + */ + subscribe(channel: string, callback: Listener, subscribeProps: object, subscribeCallback?: Listener): Listener; + + /** + * Unsubscribes the subscription obtained with a call to `subscribe`. + * + * @param subscription the subscription to unsubscribe. + * @param unsubscribeCallback a function to be invoked when the unsubscription is acknowledged + */ + unsubscribe(subscription: Listener, unsubscribeCallback?: Listener): void; + + /** + * Unsubscribes the subscription obtained with a call to `subscribe`. + * + * @param subscription the subscription to unsubscribe. + * @param unsubscribeProps an object to be merged with the unsubscribe message + * @param unsubscribeCallback a function to be invoked when the unsubscription is acknowledged + */ + unsubscribe(subscription: Listener, unsubscribeProps: object, unsubscribeCallback?: Listener): void; + + /** + * Resubscribes as necessary in case of a re-handshake. + */ + resubscribe(subscription: Listener, subscribeProps?: object): Listener; + + /** + * Removes all subscriptions added via `subscribe`, but does not remove the listeners added via + * `addListener`. + */ clearSubscriptions(): void; - handshake(handshake_params: any): void; + /** + * Publishes a message on the given channel, containing the given content. + * + * @param channel the channel to publish the message to + * @param content the content of the message + * @param publishCallback a function to be invoked when the publish is acknowledged by the + * server + */ + publish(channel: string, content: object, publishCallback?: Listener): void; - publish(channel: string, message: any): void; + /** + * Publishes a message on the given channel, containing the given content. + * + * @param channel the channel to publish the message to + * @param content the content of the message + * @param publishProps an object to be merged with the publish message + * @param publishCallback a function to be invoked when the publish is acknowledged by the + * server + */ + publish(channel: string, content: object, publishProps: object, publishCallback?: Listener): void; + /** + * Publishes a message with binary data on the given channel. + * + * The binary data chunk may be an `ArrayBuffer`, a `DataView`, a `TypedArray` (such as + * `Uint8Array`) or a plain integer array. + * + * The meta data object may contain additional application data such as a file name, a mime + * type, etc. + * + * @param channel the channel to publish the message to + * @param data the binary data to publish + * @param last whether the binary data chunk is the last + * @param meta an object containing meta data associated to the binary chunk + * @param callback a function to be invoked when the publish is acknowledged by the server + */ + publishBinary( + channel: string, + data: ArrayBuffer | DataView | Uint8Array | Uint16Array | Uint32Array, + last: boolean, + meta?: object, + callback?: Listener + ): void; - disconnect(): void; + /** + * Returns a string representing the status of the Bayeux communication with the Bayeux server. + * + * @return the status of the Bayeux communication + */ + getStatus(): string; -} - - - -interface JQueryStatic { - cometd: CometD; + /** + * Returns whether this instance has been disconnected. + * + * @return whether this instance has been disconnected. + */ + isDisconnected(): boolean; + + /** + * Sets the backoff period used to increase the backoff time when retrying an unsuccessful or + * failed message. + * + * Default value is 1 second, which means if there is a persistent failure the retries will + * happen after 1 second, then after 2 seconds, then after 3 seconds, etc. So for example with + * 15 seconds of elapsed time, there will be 5 retries (at 1, 3, 6, 10 and 15 seconds elapsed). + * + * @param period the backoff period to set + */ + setBackoffIncrement(period: number): void; + + /** + * Returns the backoff period used to increase the backoff time when retrying an unsuccessful or + * failed message. + * + * @returns the backoff increment + */ + getBackoffIncrement(): void; + + /** + * Returns the backoff period to wait before retrying an unsuccessful or failed message. + * + * @returns the backoff period + */ + getBackoffPeriod(): void; + + /** + * Increases the backoff period up to the maximum value configured. + * + * @returns the backoff period after increment + */ + increaseBackoffPeriod(): number; + + /** + * Resets the backoff period to zero. + */ + resetBackoffPeriod(): void; + + /** + * Sets the log level for console logging. + * + * @param level the log level string + */ + setLogLevel(level: "error" | "warn" | "info" | "debug"): void; + + /** + * Registers an extension whose callbacks are called for every incoming message (that comes from + * the server to this client implementation) and for every outgoing message (that originates + * from this client implementation for the server). + * + * The format of the extension object is the following: + * + * { + * incoming: (message) => { ... }, + * outgoing: (message) => { ... } + * } + * + * Both properties are optional, but if they are present they will be called respectively for + * each incoming message and for each outgoing message. + * + * @param name the name of the extension + * @param extension the extension to register + * @return true if the extension was registered, false otherwise + */ + registerExtension(name: string, extension: Extension): boolean; + + /** + * Unregister an extension previously registered with `registerExtension`. + * + * @param name the name of the extension to unregister. + * @return true if the extension was unregistered, false otherwise + */ + unregisterExtension(name: string): boolean; + + /** + * Find the extension registered with the given name. + * + * @param name the name of the extension to find + * @return the extension found or null if no extension with the given name has been registered + */ + getExtension(name: string): Extension; + + /** + * Returns the name assigned to this CometD object, or the string 'default' if no name has been + * explicitly passed as parameter to the constructor. + * + * @return the name assigned to this CometD object, or `'default'` + */ + getName(): string; + + /** + * Returns the client ID assigned by the Bayeux server during handshake. + * + * @return the client ID assigned by the Bayeux server + */ + getClientId(): string; + + /** + * Returns the URL of the Bayeux server. + * + * @return the URL of the Bayeux server + */ + getURL(): string; + + /** + * Returns the configuration for this CometD object. + * + * @return the configuration for this CometD object + */ + getConfiguration(): Configuration; + + /** + * Handler invoked every time a listener or subscriber throws an exception. + * + * @param exception the exception thrown + * @param subscriptionHandle the listener or subscription that threw the exception + * @param isListener whether it was a listener + * @param message the message received from the Bayeux server + */ + onListenerException: (exception: any, subscriptionHandle: Listener, isListener: boolean, message: string) => void; } diff --git a/types/cometd/tsconfig.json b/types/cometd/tsconfig.json index 02963e3afd..6533a1a225 100644 --- a/types/cometd/tsconfig.json +++ b/types/cometd/tsconfig.json @@ -6,7 +6,7 @@ ], "noImplicitAny": true, "noImplicitThis": true, - "strictNullChecks": false, + "strictNullChecks": true, "strictFunctionTypes": true, "baseUrl": "../", "typeRoots": [ @@ -17,6 +17,7 @@ "forceConsistentCasingInFileNames": true }, "files": [ - "index.d.ts" + "index.d.ts", + "cometd-tests.ts" ] } \ No newline at end of file diff --git a/types/cometd/tslint.json b/types/cometd/tslint.json index a41bf5d19a..f93cf8562a 100644 --- a/types/cometd/tslint.json +++ b/types/cometd/tslint.json @@ -1,79 +1,3 @@ { - "extends": "dtslint/dt.json", - "rules": { - "adjacent-overload-signatures": false, - "array-type": false, - "arrow-return-shorthand": false, - "ban-types": false, - "callable-types": false, - "comment-format": false, - "dt-header": false, - "eofline": false, - "export-just-namespace": false, - "import-spacing": false, - "interface-name": false, - "interface-over-type-literal": false, - "jsdoc-format": false, - "max-line-length": false, - "member-access": false, - "new-parens": false, - "no-any-union": false, - "no-boolean-literal-compare": false, - "no-conditional-assignment": false, - "no-consecutive-blank-lines": false, - "no-construct": false, - "no-declare-current-package": false, - "no-duplicate-imports": false, - "no-duplicate-variable": false, - "no-empty-interface": false, - "no-for-in-array": false, - "no-inferrable-types": false, - "no-internal-module": false, - "no-irregular-whitespace": false, - "no-mergeable-namespace": false, - "no-misused-new": false, - "no-namespace": false, - "no-object-literal-type-assertion": false, - "no-padding": false, - "no-redundant-jsdoc": false, - "no-redundant-jsdoc-2": false, - "no-redundant-undefined": false, - "no-reference-import": false, - "no-relative-import-in-test": false, - "no-self-import": false, - "no-single-declare-module": false, - "no-string-throw": false, - "no-unnecessary-callback-wrapper": false, - "no-unnecessary-class": false, - "no-unnecessary-generics": false, - "no-unnecessary-qualifier": false, - "no-unnecessary-type-assertion": false, - "no-useless-files": false, - "no-var-keyword": false, - "no-var-requires": false, - "no-void-expression": false, - "no-trailing-whitespace": false, - "object-literal-key-quotes": false, - "object-literal-shorthand": false, - "one-line": false, - "one-variable-per-declaration": false, - "only-arrow-functions": false, - "prefer-conditional-expression": false, - "prefer-const": false, - "prefer-declare-function": false, - "prefer-for-of": false, - "prefer-method-signature": false, - "prefer-template": false, - "radix": false, - "semicolon": false, - "space-before-function-paren": false, - "space-within-parens": false, - "strict-export-declare-modifiers": false, - "trim-file": false, - "triple-equals": false, - "typedef-whitespace": false, - "unified-signatures": false, - "void-return": false, - "whitespace": false - } + "extends": "dtslint/dt.json" }