Update various socketcluster types to newest versions (#42068)

* Add type definitions for async-stream-emitter, consumable-stream, writable-consumable-stream, stream-demux, ag-channel, ag-simple-broker, ncom, async-iterable-stream

* Upgrade sc-broker to 8.0

* Upgrade socketcluster-client to 15.1

* Rename definition files to match module file names

The files in the module were renamed.

* Move socketcluster-server to v14 folder

In preparation for socketcluster-server v15, since the old version is still used by other type packages.

* Update scc-broker-client to 7.0

* Add current socketcluster-server type definitions

Current version is v15.0

* Move sc-broker-cluster to v6 folder

In preparation for sc-broker-cluster v9, since the old version is still used by other type packages.

* Add current sc-broker-cluster type definitions

Current version is v9.0

* Move sc-channel to v1 folder

In preparation for sc-channel v2, since the old version is still used by other type packages.

* Add current sc-channel type definitions

Current version is v2.0

* Include the relevant sc-broker-cluster type-definitions directly in sc-channel

It can be run using older and newer version of sc-broker-cluster, which have differently versioned dependencies.

* Move sc-channel tests to sc-broker-cluster

In the tests we use sc-broker-cluster. If the tests are in sc-channel, they drag in all dependencies for sc-broker-cluster, including esnext.asynciterable, which we don't want.

* Simplify sc-errors tests

In the tests we used socketcluster-server. That dragged in all of its dependencies, including esnext.asynciterable, which we don't want.

* Move sc-channel to v1 folder

In preparation for sc-channel v2, since the old version is still used by other type packages.
This commit is contained in:
Daniel Rose 2020-02-05 17:59:09 +01:00 committed by GitHub
parent ca3bfdb158
commit 3b611ed646
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
83 changed files with 3724 additions and 816 deletions

View File

@ -0,0 +1,39 @@
import AGChannel = require('ag-channel');
import AGSimpleBroker = require('ag-simple-broker');
import StreamDemux = require('stream-demux');
const broker = new AGSimpleBroker();
const exchange = broker.exchange();
const channel = new AGChannel<number>('dummy', exchange, new StreamDemux(), new StreamDemux());
(async () => {
await channel.listener('subscribe').once();
console.log('subscribed');
})();
// $ExpectType ChannelState
channel.state;
(async () => {
// tslint:disable-next-line: await-promise Bug in tslint: https://github.com/palantir/tslint/issues/3997
for await (const message of channel) {
// $ExpectType number
message;
}
})();
(async () => {
// tslint:disable-next-line: await-promise Bug in tslint: https://github.com/palantir/tslint/issues/3997
for await (const event of channel.listener('subscribe')) {
// $ExpectType number
event;
}
await channel.transmitPublish(`hello`);
})();
// $ExpectType number
channel.getBackpressure();
channel.close();

120
types/ag-channel/index.d.ts vendored Normal file
View File

@ -0,0 +1,120 @@
// Type definitions for ag-channel 4.0
// Project: https://github.com/SocketCluster/ag-channel
// Definitions by: Daniel Rose <https://github.com/DanielRose>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
import ConsumableStream = require('consumable-stream');
import StreamDemux = require('stream-demux');
import DemuxedConsumableStream = require('stream-demux/demuxed-consumable-stream');
import Consumer = require('writable-consumable-stream/consumer');
declare class AGChannel<T> extends ConsumableStream<T> {
readonly PENDING: 'pending';
readonly SUBSCRIBED: 'subscribed';
readonly UNSUBSCRIBED: 'unsubscribed';
name: string;
client: AGChannel.Client;
state: AGChannel.ChannelState;
options: object;
constructor(name: string, client: AGChannel.Client, eventDemux: StreamDemux<T>, dataDemux: StreamDemux<T>);
createConsumer(timeout?: number): ConsumableStream.Consumer<T>;
listener(eventName: string): DemuxedConsumableStream<T>;
close(): void;
kill(): void;
killOutputConsumer(consumerId: number): void;
killListenerConsumer(consumerId: number): void;
getOutputConsumerStats(consumerId: number): Consumer.ConsumerStats | undefined;
getListenerConsumerStats(consumerId: number): Consumer.ConsumerStats | undefined;
getBackpressure(): number;
getListenerConsumerBackpressure(consumerId: number): number;
getOutputConsumerBackpressure(consumerId: number): any;
closeOutput(): void;
closeListener(eventName: string): void;
closeAllListeners(): void;
killOutput(): void;
killListener(eventName: string): void;
killAllListeners(): void;
getOutputConsumerStatsList(): Consumer.ConsumerStats;
getListenerConsumerStatsList(eventName: string): Consumer.ConsumerStats[];
getOutputBackpressure(): number;
getListenerBackpressure(eventName: string): number;
getAllListenersBackpressure(): number;
hasOutputConsumer(consumerId: number): boolean;
hasListenerConsumer(eventName: string, consumerId: number): boolean;
hasAnyListenerConsumer(consumerId: number): boolean;
subscribe(options?: any): void;
unsubscribe(): void;
isSubscribed(includePending?: boolean): boolean;
transmitPublish(data: any): Promise<void>;
invokePublish(data: any): Promise<void>;
}
export = AGChannel;
declare namespace AGChannel {
interface Client {
closeChannel(channelName: string): void;
killChannel(channelName: string): void;
killChannelOutputConsumer(consumerId: number): void;
killChannelListenerConsumer(consumerId: number): void;
getChannelOutputConsumerStats(consumerId: number): Consumer.ConsumerStats;
getChannelListenerConsumerStats(consumerId: number): Consumer.ConsumerStats;
getChannelBackpressure(channelName: string): number;
getChannelListenerConsumerBackpressure(consumerId: number): number;
getChannelOutputConsumerBackpressure(consumerId: number): number;
channelCloseOutput(channelName: string): void;
channelCloseListener(channelName: string, eventName: string): void;
channelCloseAllListeners(channelName: string): void;
channelKillOutput(channelName: string): void;
channelKillListener(channelName: string, eventName: string): void;
channelKillAllListeners(channelName: string): void;
channelGetOutputConsumerStatsList(channelName: string): Consumer.ConsumerStats[];
channelGetListenerConsumerStatsList(channelName: string, eventName: string): Consumer.ConsumerStats[];
channelGetAllListenersConsumerStatsList(channelName: string): Consumer.ConsumerStats[];
channelGetOutputBackpressure(channelName: string): number;
channelGetListenerBackpressure(channelName: string, eventName: string): number;
channelGetAllListenersBackpressure(channelName: string): number;
channelHasOutputConsumer(channelName: string, consumerId: number): boolean;
channelHasListenerConsumer(channelName: string, eventName: string, consumerId: number): boolean;
channelHasAnyListenerConsumer(channelName: string, consumerId: number): boolean;
getChannelState(channelName: string): ChannelState;
getChannelOptions(channelName: string): object;
subscribe(channelName: string): AGChannel<any>;
unsubscribe(channelName: string): Promise<void>;
isSubscribed(channelName: string, includePending?: boolean): boolean;
transmitPublish(channelName: string, data: any): Promise<void>;
invokePublish(channelName: string, data: any): Promise<any>;
}
type ChannelState = 'pending' | 'subscribed' | 'unsubscribed';
}

View File

@ -0,0 +1,29 @@
{
"compilerOptions": {
"module": "commonjs",
"lib": [
"es6",
"esnext.asynciterable"
],
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"baseUrl": "../",
"typeRoots": [
"../"
],
"types": [],
"paths": {
"sc-channel": [
"sc-channel/v1"
]
},
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.d.ts",
"ag-channel-tests.ts"
]
}

View File

@ -0,0 +1,3 @@
{
"extends": "dtslint/dt.json"
}

View File

@ -0,0 +1,28 @@
import AGSimpleBroker = require('ag-simple-broker');
const brokerEngine = new AGSimpleBroker();
(async () => {
// tslint:disable-next-line: await-promise Bug in tslint: https://github.com/palantir/tslint/issues/3997
for await (const { error } of brokerEngine.listener('error')) {
console.log(error);
}
})();
if (brokerEngine.isReady) {
console.log('ready');
} else {
(async () => {
await brokerEngine.listener('ready').once();
console.log('ready');
})();
}
const exchange = brokerEngine.exchange();
brokerEngine.subscribeSocket(exchange, 'test');
exchange.invokePublish('test', 'dummy');
exchange.invokePublish('test', {});
brokerEngine.unsubscribeSocket(exchange, 'test');

148
types/ag-simple-broker/index.d.ts vendored Normal file
View File

@ -0,0 +1,148 @@
// Type definitions for ag-simple-broker 4.0
// Project: https://github.com/SocketCluster/ag-simple-broker
// Definitions by: Daniel Rose <https://github.com/DanielRose>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
import AsyncStreamEmitter = require('async-stream-emitter');
import AGServer = require('socketcluster-server/server');
import ConsumableStream = require('consumable-stream');
import AGChannel = require('ag-channel');
import Consumer = require('writable-consumable-stream/consumer');
declare class AGSimpleBroker extends AsyncStreamEmitter<any> {
isReady: boolean;
emit(eventName: 'ready', data: {}): void;
emit(eventName: 'subscribe', data: AGSimpleBroker.SubscribeData): void;
emit(eventName: 'unsubscribe', data: AGSimpleBroker.UnsubscribeData): void;
emit(eventName: 'error', data: { error: Error }): void;
emit(eventName: 'publish', data: AGSimpleBroker.PublishData): void;
listener(eventName: 'ready'): ConsumableStream<{}>;
listener(eventName: 'subscribe'): ConsumableStream<AGSimpleBroker.SubscribeData>;
listener(eventName: 'unsubscribe'): ConsumableStream<AGSimpleBroker.UnsubscribeData>;
listener(eventName: 'error'): ConsumableStream<{ error: Error }>;
listener(eventName: 'publish'): ConsumableStream<AGSimpleBroker.PublishData>;
exchange(): AGSimpleBroker.SimpleExchange;
subscribeClient(client: { id: string }, channelName: string): Promise<void>;
subscribeSocket(client: { id: string }, channelName: string): Promise<void>;
unsubscribeClient(client: { id: string }, channelName: string): Promise<void>;
unsubscribeSocket(client: { id: string }, channelName: string): Promise<void>;
subscriptions(): string[];
isSubscribed(channelName: string): boolean;
setCodecEngine(codec: AGServer.CodecEngine): void;
invokePublish(channelName: string, data: any, suppressEvent?: boolean): Promise<void>;
transmitPublish(channelName: string, data: any, suppressEvent?: boolean): Promise<void>;
}
export = AGSimpleBroker;
declare namespace AGSimpleBroker {
class SimpleExchange extends AsyncStreamEmitter<any> implements AGChannel.Client {
id: string;
constructor(broker: AGSimpleBroker);
emit(eventName: 'subscribe', data: SubscribeData): void;
emit(eventName: 'unsubscribe', data: UnsubscribeData): void;
listener(eventName: 'subscribe'): ConsumableStream<SubscribeData>;
listener(eventName: 'unsubscribe'): ConsumableStream<UnsubscribeData>;
/* AGChannel.Client start */
closeChannel(channelName: string): void;
killChannel(channelName: string): void;
killChannelOutputConsumer(consumerId: number): void;
killChannelListenerConsumer(consumerId: number): void;
getChannelOutputConsumerStats(consumerId: number): Consumer.ConsumerStats;
getChannelListenerConsumerStats(consumerId: number): Consumer.ConsumerStats;
getChannelBackpressure(channelName: string): number;
getChannelListenerConsumerBackpressure(consumerId: number): number;
getChannelOutputConsumerBackpressure(consumerId: number): number;
channelCloseOutput(channelName: string): void;
channelCloseListener(channelName: string, eventName: string): void;
channelCloseAllListeners(channelName: string): void;
channelKillOutput(channelName: string): void;
channelKillListener(channelName: string, eventName: string): void;
channelKillAllListeners(channelName: string): void;
channelGetOutputConsumerStatsList(channelName: string): Consumer.ConsumerStats[];
channelGetListenerConsumerStatsList(channelName: string, eventName: string): Consumer.ConsumerStats[];
channelGetAllListenersConsumerStatsList(channelName: string): Consumer.ConsumerStats[];
channelGetOutputBackpressure(channelName: string): number;
channelGetListenerBackpressure(channelName: string, eventName: string): number;
channelGetAllListenersBackpressure(channelName: string): number;
channelHasOutputConsumer(channelName: string, consumerId: number): boolean;
channelHasListenerConsumer(channelName: string, eventName: string, consumerId: number): boolean;
channelHasAnyListenerConsumer(channelName: string, consumerId: number): boolean;
getChannelState(channelName: string): AGChannel.ChannelState;
getChannelOptions(channelName: string): object;
subscribe(channelName: string): AGChannel<any>;
unsubscribe(channelName: string): Promise<void>;
isSubscribed(channelName: string, includePending?: boolean): boolean;
transmitPublish(channelName: string, data: any): Promise<void>;
invokePublish(channelName: string, data: any): Promise<void>;
/* AGChannel.Client end */
transmit(event: string, packet: any): void;
destroy(): void;
channel(channelName: string): AGChannel<any>;
closeAllChannelOutputs(): void;
closeAllChannelListeners(): void;
closeAllChannels(): void;
killAllChannelOutputs(): void;
killAllChannelListeners(): void;
killAllChannels(): void;
getAllChannelOutputsConsumerStatsList(): Consumer.ConsumerStats[];
getAllChannelListenersConsumerStatsList(): Consumer.ConsumerStats[];
getBackpressure(): number;
getAllChannelOutputsBackpressure(): number;
getAllChannelListenersBackpressure(): number;
getAllChannelsBackpressure(): number;
hasAnyChannelOutputConsumer(consumerId: number): boolean;
hasAnyChannelListenerConsumer(consumerId: number): boolean;
subscriptions(includePending?: boolean): string[];
}
interface SubscribeData {
channel: string;
}
interface UnsubscribeData {
channel: string;
}
interface PublishData {
channel: string;
date: any;
}
}

View File

@ -0,0 +1,29 @@
{
"compilerOptions": {
"module": "commonjs",
"lib": [
"es6",
"esnext.asynciterable"
],
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"baseUrl": "../",
"typeRoots": [
"../"
],
"types": [],
"paths": {
"sc-channel": [
"sc-channel/v1"
]
},
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.d.ts",
"ag-simple-broker-tests.ts"
]
}

View File

@ -0,0 +1,3 @@
{
"extends": "dtslint/dt.json"
}

View File

@ -0,0 +1,56 @@
import AsyncIterableStream = require('async-iterable-stream');
// Simple, dummy implementation of the abstract class
class DummyAsyncIterator {
async next() {
return { done: true, value: 'dummy' };
}
return() {
return {};
}
}
class DummyAsyncIterableStream extends AsyncIterableStream<string> {
createAsyncIterator() {
return new DummyAsyncIterator();
}
}
// Actual tests
const asyncIterableStream = new DummyAsyncIterableStream();
// Consume data objects from consumableStream as they are written to the stream.
(async () => {
// tslint:disable-next-line: await-promise Bug in tslint: https://github.com/palantir/tslint/issues/3997
for await (const data of asyncIterableStream) {
// $ExpectType string
data;
console.log(data);
}
})();
// Consume only the next data object which is written to the stream.
(async () => {
// $ExpectType string
await asyncIterableStream.once();
})();
(async () => {
const asyncIterable = asyncIterableStream.createAsyncIterable(20);
for await (const data of asyncIterable) {
// $ExpectType string
data;
}
})();
(async () => {
const result = await asyncIterableStream.next();
if (!result.done) {
// $ExpectType string
result.value;
}
})();

23
types/async-iterable-stream/index.d.ts vendored Normal file
View File

@ -0,0 +1,23 @@
// Type definitions for async-iterable-stream 3.0
// Project: https://github.com/SocketCluster/async-iterable-stream
// Definitions by: Daniel Rose <https://github.com/DanielRose>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
/// <reference types="node" />
declare abstract class AsyncIterableStream<T> implements AsyncIterator<T>, AsyncIterable<T> {
next(timeout?: number): Promise<IteratorResult<T>>;
once(timeout?: number): Promise<T>;
abstract createAsyncIterator(timeout?: number): AsyncIterableStream.AsyncIterator<T>;
createAsyncIterable(timeout?: number): AsyncIterable<T>;
[Symbol.asyncIterator](): AsyncIterator<T>;
}
export = AsyncIterableStream;
declare namespace AsyncIterableStream {
interface AsyncIterator<T> {
next(): Promise<IteratorResult<T>>;
return(): void;
}
}

View File

@ -0,0 +1,24 @@
{
"compilerOptions": {
"module": "commonjs",
"lib": [
"es6",
"esnext.asynciterable"
],
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"baseUrl": "../",
"typeRoots": [
"../"
],
"types": [],
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.d.ts",
"async-iterable-stream-tests.ts"
]
}

View File

@ -0,0 +1,3 @@
{
"extends": "dtslint/dt.json"
}

View File

@ -0,0 +1,34 @@
import AsyncStreamEmitter = require('async-stream-emitter');
// From README.md
const emitter = new AsyncStreamEmitter<string>();
(async () => {
await wait(10);
emitter.emit('foo', 'hello');
// This will cause all for-await-of loops for that event to exit.
// Note that you can also use the 'break' statement inside
// individual for-await-of loops.
emitter.closeListener('foo');
})();
(async () => {
// tslint:disable-next-line: await-promise Bug in tslint: https://github.com/palantir/tslint/issues/3997
for await (const data of emitter.listener('foo')) {
// data is 'hello'
// $ExpectType string
data;
}
console.log('The listener was closed.');
})();
// Utility function.
function wait(duration: number) {
return new Promise(resolve => {
setTimeout(() => {
resolve();
}, duration);
});
}

33
types/async-stream-emitter/index.d.ts vendored Normal file
View File

@ -0,0 +1,33 @@
// Type definitions for async-stream-emitter 3.0
// Project: https://github.com/SocketCluster/async-stream-emitter
// Definitions by: Daniel Rose <https://github.com/DanielRose>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
import ConsumableStream = require('consumable-stream');
import Consumer = require('writable-consumable-stream/consumer');
declare class AsyncStreamEmitter<T> {
emit(eventName: string, data: T): void;
listener(eventName: string): ConsumableStream<T>;
closeListener(eventName: string): void;
closeAllListeners(): void;
getListenerConsumerStats(consumerId: number): Consumer.ConsumerStats;
getListenerConsumerStatsList(eventName: string): Consumer.ConsumerStats[];
getAllListenersConsumerStatsList(): Consumer.ConsumerStats[];
killListener(eventName: string): void;
killAllListeners(): void;
killListenerConsumer(consumerId: number): void;
getListenerBackpressure(eventName: string): number;
getAllListenersBackpressure(): number;
getListenerConsumerBackpressure(consumerId: number): number;
hasListenerConsumer(eventName: string, consumerId: number): boolean;
hasAnyListenerConsumer(consumerId: number): boolean;
}
export = AsyncStreamEmitter;

View File

@ -0,0 +1,24 @@
{
"compilerOptions": {
"module": "commonjs",
"lib": [
"es6",
"esnext.asynciterable"
],
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"baseUrl": "../",
"typeRoots": [
"../"
],
"types": [],
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.d.ts",
"async-stream-emitter-tests.ts"
]
}

View File

@ -0,0 +1,3 @@
{
"extends": "dtslint/dt.json"
}

View File

@ -0,0 +1,56 @@
import ConsumableStream = require('consumable-stream');
// Simple, dummy implementation of the abstract class
class DummyConsumer {
async next() {
return { done: true, value: 'dummy' };
}
return() {
return {};
}
}
class DummyConsumableStream extends ConsumableStream<string> {
createConsumer() {
return new DummyConsumer();
}
}
// Actual tests
const consumableStream = new DummyConsumableStream();
// Consume data objects from consumableStream as they are written to the stream.
(async () => {
// tslint:disable-next-line: await-promise Bug in tslint: https://github.com/palantir/tslint/issues/3997
for await (const data of consumableStream) {
// $ExpectType string
data;
console.log(data);
}
})();
// Consume only the next data object which is written to the stream.
(async () => {
// $ExpectType string
await consumableStream.once();
})();
(async () => {
const consumable = consumableStream.createConsumable(20);
for await (const data of consumable) {
// $ExpectType string
data;
}
})();
(async () => {
const result = await consumableStream.next();
if (!result.done) {
// $ExpectType string
result.value;
}
})();

23
types/consumable-stream/index.d.ts vendored Normal file
View File

@ -0,0 +1,23 @@
// Type definitions for consumable-stream 1.0
// Project: https://github.com/SocketCluster/consumable-stream
// Definitions by: Daniel Rose <https://github.com/DanielRose>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
/// <reference types="node" />
declare abstract class ConsumableStream<T> implements AsyncIterator<T>, AsyncIterable<T> {
next(timeout?: number): Promise<IteratorResult<T>>;
once(timeout?: number): Promise<T>;
abstract createConsumer(timeout?: number): ConsumableStream.Consumer<T>;
createConsumable(timeout?: number): AsyncIterable<T>;
[Symbol.asyncIterator](): AsyncIterator<T>;
}
export = ConsumableStream;
declare namespace ConsumableStream {
interface Consumer<T> {
next(): Promise<IteratorResult<T>>;
return(): void;
}
}

View File

@ -0,0 +1,24 @@
{
"compilerOptions": {
"module": "commonjs",
"lib": [
"es6",
"esnext.asynciterable"
],
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"baseUrl": "../",
"typeRoots": [
"../"
],
"types": [],
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.d.ts",
"consumable-stream-tests.ts"
]
}

View File

@ -0,0 +1,3 @@
{
"extends": "dtslint/dt.json"
}

134
types/ncom/index.d.ts vendored Normal file
View File

@ -0,0 +1,134 @@
// Type definitions for ncom 1.0
// Project: https://github.com/SocketCluster/ncom
// Definitions by: Daniel Rose <https://github.com/DanielRose>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
/// <reference types="node" />
import { EventEmitter } from 'events';
import { SocketConstructorOpts, Socket, ListenOptions } from 'net';
import { TlsOptions } from 'tls';
export class ComSocket extends EventEmitter {
id: string;
connected: boolean;
batchDuration: number;
socket: Socket;
constructor(options: SocketConstructorOpts | Socket, id: string);
/**
* events.EventEmitter
* 1. error
*/
addListener(event: string, listener: (...args: any[]) => void): this;
addListener(event: 'error', listener: (err: Error) => void): this;
emit(event: string | symbol, ...args: any[]): boolean;
emit(event: 'error', err: Error): boolean;
on(event: string, listener: (...args: any[]) => void): this;
on(event: 'error', listener: (err: Error) => void): this;
once(event: string, listener: (...args: any[]) => void): this;
once(event: 'error', listener: (err: Error) => void): this;
prependListener(event: string, listener: (...args: any[]) => void): this;
prependListener(event: 'error', listener: (err: Error) => void): this;
prependOnceListener(event: string, listener: (...args: any[]) => void): this;
prependOnceListener(event: 'error', listener: (err: Error) => void): this;
removeListener(event: string, listener: (...args: any[]) => void): this;
removeListener(event: 'error', listener: (err: Error) => void): this;
write(data: any, writeOptions?: WriteOptions): void;
end: () => void;
destroy: () => void;
}
export class ComServer extends EventEmitter {
constructor(options?: ComServerOptions);
constructor(options: SecureComServerOptions);
/**
* events.EventEmitter
* 1. error
* 2. connection
*
* All other events are forwarded to net.Server
*/
addListener(event: string, listener: (...args: any[]) => void): this;
addListener(event: 'error', listener: (err: Error) => void): this;
addListener(event: 'connection', listener: ConnectionListener): this;
emit(event: string | symbol, ...args: any[]): boolean;
emit(event: 'error', err: Error): boolean;
emit(event: 'connection', listener: ConnectionListener): this;
on(event: string, listener: (...args: any[]) => void): this;
on(event: 'error', listener: (err: Error) => void): this;
on(event: 'connection', listener: ConnectionListener): this;
once(event: string, listener: (...args: any[]) => void): this;
once(event: 'error', listener: (err: Error) => void): this;
once(event: 'connection', listener: ConnectionListener): this;
prependListener(event: string, listener: (...args: any[]) => void): this;
prependListener(event: 'error', listener: (err: Error) => void): this;
prependListener(event: 'connection', listener: ConnectionListener): this;
prependOnceListener(event: string, listener: (...args: any[]) => void): this;
prependOnceListener(event: 'error', listener: (err: Error) => void): this;
prependOnceListener(event: 'connection', listener: ConnectionListener): this;
removeListener(event: string, listener: (...args: any[]) => void): this;
removeListener(event: 'error', listener: (err: Error) => void): this;
removeListener(event: 'connection', listener: ConnectionListener): this;
/**
* Forwards to net.Server.listen()
*/
// tslint:disable:unified-signatures Copied from net.d.ts, where the rule is disabled
listen(port?: number, hostname?: string, backlog?: number, listeningListener?: () => void): this;
listen(port?: number, hostname?: string, listeningListener?: () => void): this;
listen(port?: number, backlog?: number, listeningListener?: () => void): this;
listen(port?: number, listeningListener?: () => void): this;
listen(path: string, backlog?: number, listeningListener?: () => void): this;
listen(path: string, listeningListener?: () => void): this;
listen(options: ListenOptions, listeningListener?: () => void): this;
listen(handle: any, backlog?: number, listeningListener?: () => void): this;
listen(handle: any, listeningListener?: () => void): this;
// tslint:enable:unified-signatures
/**
* Forwards to net.Server.close()
*/
close(callback?: (err?: Error) => void): this;
}
export function createServer(options?: ComServerOptions, listener?: ConnectionListener): ComServer;
export function createServer(options: SecureComServerOptions, listener?: ConnectionListener): ComServer;
export function createServer(listener?: ConnectionListener): ComServer;
export interface WriteOptions {
filters?: FilterFunction[];
batch?: boolean;
}
export type FilterFunction = (data: any) => string;
export interface SecureComServerOptions extends TlsOptions {
secure: true;
}
export interface ComServerOptions {
allowHalfOpen?: boolean;
pauseOnConnect?: boolean;
}
export type ConnectionListener = (socket: ComSocket) => void;

13
types/ncom/ncom-tests.ts Normal file
View File

@ -0,0 +1,13 @@
import { ComSocket, ComServer } from 'ncom';
const comSocket = new ComSocket({}, 'id');
comSocket.write('test');
comSocket.write('test', { batch: true });
comSocket.end();
comSocket.destroy();
const secureComServer = new ComServer({ secure: true });
secureComServer.close();

23
types/ncom/tsconfig.json Normal file
View File

@ -0,0 +1,23 @@
{
"compilerOptions": {
"module": "commonjs",
"lib": [
"es6"
],
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"baseUrl": "../",
"typeRoots": [
"../"
],
"types": [],
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.d.ts",
"ncom-tests.ts"
]
}

3
types/ncom/tslint.json Normal file
View File

@ -0,0 +1,3 @@
{
"extends": "dtslint/dt.json"
}

View File

@ -1,18 +1,16 @@
// Type definitions for sc-broker-cluster 6.1
// Type definitions for sc-broker-cluster 9.0
// Project: https://github.com/SocketCluster/sc-broker-cluster
// Definitions by: Daniel Rose <https://github.com/DanielRose>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.4
import { SCServerSocket, SCServer } from "socketcluster-server";
import { BrokerStartInfo, BrokerExitInfo } from "socketcluster";
import { SpliceOptions, QueryOptions } from "sc-broker";
import { SCChannel } from "sc-channel";
import { EventEmitter } from "events";
import { AsyncResultArrayCallback } from "async";
import { KeyChain, FlexiMap } from "fleximap";
import { Keys } from "expirymanager";
import { ClientCluster } from "./clientcluster";
import { CodecEngine, AGServerOptions } from 'socketcluster-server/server';
import { SpliceOptions, QueryOptions } from 'sc-broker';
import { SCChannel } from 'sc-channel';
import { EventEmitter } from 'events';
import { AsyncResultArrayCallback } from 'async';
import { KeyChain, FlexiMap } from 'fleximap';
import { Keys } from 'expirymanager';
import { ClientCluster } from './clientcluster';
export class AbstractDataClient extends EventEmitter {
constructor(dataClient: ClientCluster);
@ -105,23 +103,42 @@ export interface SCBrokerClusterServerOptions {
appBrokerControllerPath?: string;
processTermTimeout?: number;
ipcAckTimeout?: number;
brokerOptions?: SCServer.SCServerOptions;
brokerOptions?: AGServerOptions;
}
export class Server extends EventEmitter {
constructor(options: SCBrokerClusterServerOptions);
on(event: "brokerStart", listener: (brokerInfo: BrokerStartInfo) => void): this;
on(event: "brokerExit", listener: (brokerInfo: BrokerExitInfo) => void): this;
on(event: "brokerMessage", listener: (brokerId: string, data: any, callback: (err: Error | null, data: any) => void) => void): this;
on(event: "ready", listener: () => void): this;
on(event: "error", listener: (err?: Error) => void): this;
on(event: 'brokerStart', listener: (brokerInfo: BrokerStartInfo) => void): this;
on(event: 'brokerExit', listener: (brokerInfo: BrokerExitInfo) => void): this;
on(event: 'brokerMessage', listener: BrokerMessageListener): this;
on(event: 'ready', listener: () => void): this;
on(event: 'error', listener: (err?: Error) => void): this;
sendToBroker(brokerId: string, data: any, callback?: (err: Error | null, data: any) => void): void;
killBrokers(): void;
destroy(): void;
}
export interface BrokerStartInfo {
id: number;
pid: number;
respawn: boolean;
}
export interface BrokerExitInfo {
id: number;
pid: number;
code: number;
signal: string;
}
export type BrokerMessageListener = (
brokerId: string,
data: any,
callback: (err: Error | null, data: any) => void,
) => void;
export interface SCBrokerClusterClientOptions {
brokers: string[];
secretKey?: string;
@ -141,10 +158,10 @@ export class Client extends EventEmitter {
options: SCBrokerClusterClientOptions;
on(event: "error", listener: (err?: Error) => void): this;
on(event: "warning", listener: (warning?: Error) => void): this;
on(event: "ready", listener: () => void): this;
on(event: "message", listener: (packet: MessagePacket) => void): this;
on(event: 'error', listener: (err?: Error) => void): this;
on(event: 'warning', listener: (warning?: Error) => void): this;
on(event: 'ready', listener: () => void): this;
on(event: 'message', listener: (packet: MessagePacket) => void): this;
destroy(callback?: AsyncResultArrayCallback<SCExchange>): void;
@ -153,8 +170,17 @@ export class Client extends EventEmitter {
unsubscribeAll(callback?: AsyncResultArrayCallback<any>): void;
isSubscribed(channel: string, includePending?: boolean): boolean;
subscribeSocket(socket: SCServerSocket, channel: string, callback?: (err?: Error) => void): void;
unsubscribeSocket(socket: SCServerSocket, channel: string, callback?: () => void): void;
subscribeSocket(socket: ServerSocket, channel: string, callback?: (err?: Error) => void): void;
unsubscribeSocket(socket: ServerSocket, channel: string, callback?: () => void): void;
setSCServer(scServer: SCServer): void;
}
export interface ServerSocket {
id: string;
emit(eventName: string, ...args: any[]): void;
}
export interface SCServer {
codec: CodecEngine;
}

View File

@ -1,46 +1,47 @@
import { Client } from "sc-broker-cluster";
import { SCServer, SCServerSocket } from "socketcluster-server";
import WebSocket = require("ws");
import { Client } from 'sc-broker-cluster';
import { AGServer, AGServerSocket } from 'socketcluster-server';
import WebSocket = require('ws');
import { SCChannel } from 'sc-channel';
// Client tests
const client = new Client({
brokers: [],
secretKey: "secretKey",
secretKey: 'secretKey',
pubSubBatchDuration: 100,
connectRetryErrorThreshold: 5
connectRetryErrorThreshold: 5,
});
client.on("error", err => {});
client.on("warning", err => {});
client.on('error', err => {});
client.on('warning', err => {});
const scServer = new SCServer();
client.setSCServer(scServer);
const agServer = new AGServer();
client.setSCServer(agServer);
client.once("ready", () => {});
client.once('ready', () => {});
client.subscribe("channel");
client.subscribe("channel", () => {});
client.subscribe("channel", err => {});
client.subscribe('channel');
client.subscribe('channel', () => {});
client.subscribe('channel', err => {});
client.unsubscribe("channel");
client.unsubscribe("channel", () => {});
client.unsubscribe('channel');
client.unsubscribe('channel', () => {});
client.unsubscribeAll();
client.unsubscribeAll(() => {});
client.unsubscribeAll(err => {});
client.unsubscribeAll((err, results) => {});
client.isSubscribed("channel");
client.isSubscribed("channel", true);
client.isSubscribed('channel');
client.isSubscribed('channel', true);
const wsSocket = new WebSocket("address");
const socket = new SCServerSocket("id", scServer, wsSocket);
client.subscribeSocket(socket, "channelName");
client.subscribeSocket(socket, "channelName", err => {});
const wsSocket = new WebSocket('address');
const socket = new AGServerSocket('id', agServer, wsSocket, 2);
client.subscribeSocket(socket, 'channelName');
client.subscribeSocket(socket, 'channelName', err => {});
client.unsubscribeSocket(socket, "channelName");
client.unsubscribeSocket(socket, "channelName", () => {});
client.unsubscribeSocket(socket, 'channelName');
client.unsubscribeSocket(socket, 'channelName', () => {});
client.destroy();
client.destroy(() => {});
@ -52,9 +53,43 @@ client.destroy((err, results) => {});
const exchange = client.exchange();
exchange.send({}, null);
exchange.send("dummy", 1, () => {});
exchange.send("dummy", ["1", "2"], err => {});
exchange.send(123, "*", (err, results) => {});
exchange.send('dummy', 1, () => {});
exchange.send('dummy', ['1', '2'], err => {});
exchange.send(123, '*', (err, results) => {});
exchange.publish("channelName", {}, err => {});
exchange.publish("channelName", {});
exchange.publish('channelName', {}, err => {});
exchange.publish('channelName', {});
// SCChannel tests
//
// Placed here instead of in sc-channel, since we otherwise
// get a dependency to sc-broker-cluster there, which we don't
// want.
let channel = exchange.channel('channelName');
channel = new SCChannel('channelName', client.exchange(), {});
channel.state = channel.PENDING;
channel.state = channel.SUBSCRIBED;
channel.state = channel.UNSUBSCRIBED;
// $ExpectType string
channel.name;
channel.watch(() => {});
channel.unwatch();
channel.subscribe();
channel.subscribe({});
channel.unsubscribe();
// $ExpectType any
channel.data;
channel.emit('subscribe', channel.name);
channel.emit('unsubscribe', channel.name);
channel.setOptions({ waitForAuth: true });
channel.publish('test');
channel.publish(123);

View File

@ -2,7 +2,8 @@
"compilerOptions": {
"module": "commonjs",
"lib": [
"es6"
"es6",
"esnext.asynciterable"
],
"noImplicitAny": true,
"noImplicitThis": true,
@ -13,6 +14,11 @@
"../"
],
"types": [],
"paths": {
"sc-channel": [
"sc-channel/v1"
]
},
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
@ -20,4 +26,4 @@
"index.d.ts",
"sc-broker-cluster-tests.ts"
]
}
}

View File

@ -0,0 +1 @@
scbroker.d.ts

View File

@ -0,0 +1,10 @@
import { EventEmitter } from "events";
import { SCBrokerClient } from "sc-broker";
import { mapperFunction } from ".";
export class ClientCluster extends EventEmitter {
constructor(clients: SCBrokerClient[]);
setMapper(mapper: mapperFunction): void;
getMapper(): mapperFunction;
}

159
types/sc-broker-cluster/v6/index.d.ts vendored Normal file
View File

@ -0,0 +1,159 @@
// Type definitions for sc-broker-cluster 6.1
// Project: https://github.com/SocketCluster/sc-broker-cluster
// Definitions by: Daniel Rose <https://github.com/DanielRose>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
import { SCServerSocket, SCServer } from "socketcluster-server";
import { BrokerStartInfo, BrokerExitInfo } from "socketcluster";
import { SpliceOptions, QueryOptions } from "sc-broker";
import { SCChannel } from "sc-channel";
import { EventEmitter } from "events";
import { AsyncResultArrayCallback } from "async";
import { KeyChain, FlexiMap } from "fleximap";
import { Keys } from "expirymanager";
import { ClientCluster } from "./clientcluster";
export class AbstractDataClient extends EventEmitter {
constructor(dataClient: ClientCluster);
set(keyChain: KeyChain, value: any, getValue?: boolean, callback?: (err?: Error) => void): void;
set(keyChain: KeyChain, value: any, callback?: (err?: Error) => void): void;
expire(keys: Keys, seconds: number, callback?: (err?: Error) => void): void;
unexpire(keys: Keys, callback?: (err?: Error) => void): void;
add(keyChain: KeyChain, value: any, getValue?: boolean, callback?: (err?: Error) => void): void;
add(keyChain: KeyChain, value: any, callback?: (err?: Error) => void): void;
get(keyChain: KeyChain, callback: (err: Error | null, value: any) => void): void;
getRange(keyChain: KeyChain, fromIndex: number, toIndex: number, callback: (err: Error | null, value: any) => void): void;
getRange(keyChain: KeyChain, fromIndex: number, callback: (err: Error | null, value: any) => void): void;
getAll(callback: (err: Error | null, value: any[] | object) => void): void;
count(keyChain: KeyChain, callback: (err: Error | null, value: number) => void): void;
remove(keyChain: KeyChain, getValue?: boolean, callback?: (err?: Error) => void): void;
remove(keyChain: KeyChain, callback?: (err?: Error) => void): void;
removeRange(keyChain: KeyChain, fromIndex: number, toIndex?: number, getValue?: boolean, callback?: (err?: Error) => void): void;
removeRange(keyChain: KeyChain, fromIndex: number, toIndex?: number, callback?: (err?: Error) => void): void;
removeRange(keyChain: KeyChain, fromIndex: number, callback?: (err?: Error) => void): void;
removeAll(callback?: (err: Error) => void): void;
splice(keyChain: KeyChain, options?: SpliceOptions, callback?: (err?: Error) => void): void;
splice(keyChain: KeyChain, callback?: (err?: Error) => void): void;
pop(keyChain: KeyChain, callback: (err: Error | null, data: any) => void): void;
hasKey(keyChain: KeyChain, callback: (err: Error | null, data: boolean) => void): void;
extractKeys(keyChain: KeyChain): string[];
extractValues(keyChain: KeyChain): any[];
exec(query: (datamap: FlexiMap) => void, options?: QueryOptions, callback?: (err: Error | null, data: any) => void): void;
exec(query: (datamap: FlexiMap) => void, callback: (err: Error | null, data: any) => void): void;
}
export type handlerFunction = (data: any) => void;
export type mapperFunction = (keyChain: KeyChain, method: string, clientIds: number[]) => number | number[];
/**
* The exchange object is a top-level SCBrokerClient which lets you publish events and manipulate data within your brokers - It represents a cluster of 1 or more brokers.
*/
export class SCExchange extends AbstractDataClient {
constructor(privateClientCluster: ClientCluster, publicClientCluster: ClientCluster, ioClusterClient: Client);
send(data: any, mapIndex: number | string | string[] | null, callback?: AsyncResultArrayCallback<any>): void;
publish(channelName: string, data: any, callback?: (err?: Error) => void): void;
subscribe(channelName: string): SCChannel;
unsubscribe(channelName: string): void;
channel(channelName: string): SCChannel;
destroyChannel(channelName: string): void;
subscriptions(includePending?: boolean): string[];
isSubscribed(channelName: string, includePending?: boolean): boolean;
watch(channelName: string, handler: handlerFunction): void;
unwatch(channelName: string, handler?: handlerFunction): void;
watchers(channelName: string): handlerFunction[];
setMapper(mapper: mapperFunction): void;
getMapper(): mapperFunction;
map(keyChain: KeyChain, method: string): { type: string; targets: Client[] };
destroy(): void;
}
export interface SCBrokerClusterServerOptions {
brokers: string[];
debug?: boolean;
inspect?: boolean;
instanceId?: string;
secretKey?: string;
expiryAccuracy?: number;
downgradeToUser: number | string;
appBrokerControllerPath?: string;
processTermTimeout?: number;
ipcAckTimeout?: number;
brokerOptions?: SCServer.SCServerOptions;
}
export class Server extends EventEmitter {
constructor(options: SCBrokerClusterServerOptions);
on(event: "brokerStart", listener: (brokerInfo: BrokerStartInfo) => void): this;
on(event: "brokerExit", listener: (brokerInfo: BrokerExitInfo) => void): this;
on(event: "brokerMessage", listener: (brokerId: string, data: any, callback: (err: Error | null, data: any) => void) => void): this;
on(event: "ready", listener: () => void): this;
on(event: "error", listener: (err?: Error) => void): this;
sendToBroker(brokerId: string, data: any, callback?: (err: Error | null, data: any) => void): void;
killBrokers(): void;
destroy(): void;
}
export interface SCBrokerClusterClientOptions {
brokers: string[];
secretKey?: string;
pubSubBatchDuration?: number;
connectRetryErrorThreshold: number;
}
export interface MessagePacket {
channel: string;
data: any;
}
export class Client extends EventEmitter {
constructor(options: SCBrokerClusterClientOptions);
exchange(): SCExchange;
options: SCBrokerClusterClientOptions;
on(event: "error", listener: (err?: Error) => void): this;
on(event: "warning", listener: (warning?: Error) => void): this;
on(event: "ready", listener: () => void): this;
on(event: "message", listener: (packet: MessagePacket) => void): this;
destroy(callback?: AsyncResultArrayCallback<SCExchange>): void;
subscribe(channel: string, callback?: (err?: Error) => void): void;
unsubscribe(channel: string, callback?: () => void): void;
unsubscribeAll(callback?: AsyncResultArrayCallback<any>): void;
isSubscribed(channel: string, includePending?: boolean): boolean;
subscribeSocket(socket: SCServerSocket, channel: string, callback?: (err?: Error) => void): void;
unsubscribeSocket(socket: SCServerSocket, channel: string, callback?: () => void): void;
setSCServer(scServer: SCServer): void;
}

View File

@ -0,0 +1,95 @@
import { Client } from 'sc-broker-cluster';
import { SCServer, SCServerSocket } from 'socketcluster-server';
import WebSocket = require('ws');
import { SCChannel } from 'sc-channel';
// Client tests
const client = new Client({
brokers: [],
secretKey: 'secretKey',
pubSubBatchDuration: 100,
connectRetryErrorThreshold: 5,
});
client.on('error', err => {});
client.on('warning', err => {});
const scServer = new SCServer();
client.setSCServer(scServer);
client.once('ready', () => {});
client.subscribe('channel');
client.subscribe('channel', () => {});
client.subscribe('channel', err => {});
client.unsubscribe('channel');
client.unsubscribe('channel', () => {});
client.unsubscribeAll();
client.unsubscribeAll(() => {});
client.unsubscribeAll(err => {});
client.unsubscribeAll((err, results) => {});
client.isSubscribed('channel');
client.isSubscribed('channel', true);
const wsSocket = new WebSocket('address');
const socket = new SCServerSocket('id', scServer, wsSocket);
client.subscribeSocket(socket, 'channelName');
client.subscribeSocket(socket, 'channelName', err => {});
client.unsubscribeSocket(socket, 'channelName');
client.unsubscribeSocket(socket, 'channelName', () => {});
client.destroy();
client.destroy(() => {});
client.destroy(err => {});
client.destroy((err, results) => {});
// SCExchange tests
const exchange = client.exchange();
exchange.send({}, null);
exchange.send('dummy', 1, () => {});
exchange.send('dummy', ['1', '2'], err => {});
exchange.send(123, '*', (err, results) => {});
exchange.publish('channelName', {}, err => {});
exchange.publish('channelName', {});
// SCChannel tests
//
// Placed here instead of in sc-channel, since we otherwise
// get a dependency to sc-broker-cluster there, which we don't
// want.
let channel = exchange.channel('channelName');
channel = new SCChannel('channelName', client.exchange(), {});
channel.state = channel.PENDING;
channel.state = channel.SUBSCRIBED;
channel.state = channel.UNSUBSCRIBED;
// $ExpectType string
channel.name;
channel.watch(() => {});
channel.unwatch();
channel.subscribe();
channel.subscribe({});
channel.unsubscribe();
// $ExpectType any
channel.data;
channel.emit('subscribe', channel.name);
channel.emit('unsubscribe', channel.name);
channel.setOptions({ waitForAuth: true });
channel.publish('test');
channel.publish(123);

View File

@ -0,0 +1,3 @@
import SCBroker = require("sc-broker/scbroker");
export = SCBroker;

View File

@ -0,0 +1,37 @@
{
"compilerOptions": {
"module": "commonjs",
"lib": [
"es6"
],
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"baseUrl": "../../",
"typeRoots": [
"../../"
],
"types": [],
"paths": {
"sc-broker-cluster": [
"sc-broker-cluster/v6"
],
"sc-broker-cluster/*": [
"sc-broker-cluster/v6/*"
],
"socketcluster-server": [
"socketcluster-server/v14"
],
"sc-channel": [
"sc-channel/v1"
]
},
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.d.ts",
"sc-broker-cluster-tests.ts"
]
}

View File

@ -0,0 +1,3 @@
{
"extends": "dtslint/dt.json"
}

View File

@ -1,15 +1,13 @@
// Type definitions for sc-broker 5.1
// Type definitions for sc-broker 8.0
// Project: https://github.com/SocketCluster/sc-broker
// Definitions by: Daniel Rose <https://github.com/DanielRose>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.4
import { SCServer } from "socketcluster-server";
import { SCBrokerOptions } from "./scbroker";
import { WorkerExitInfo } from "socketcluster";
import { EventEmitter } from "events";
import { KeyChain, FlexiMap } from "fleximap";
import { Keys, Key } from "expirymanager";
import { EventEmitter } from 'events';
import { KeyChain, FlexiMap } from 'fleximap';
import { Keys, Key } from 'expirymanager';
import { SCBrokerOptions } from './scbroker';
export interface SCBrokerServerOptions {
id?: string;
@ -19,7 +17,7 @@ export interface SCBrokerServerOptions {
socketPath?: string;
port?: number;
expiryAccuracy?: number;
downgradeToUser: number | string;
downgradeToUser?: number | string;
brokerControllerPath?: string;
processTermTimeout?: number;
ipcAckTimeout?: number;
@ -33,16 +31,46 @@ export interface SCBrokerServer extends EventEmitter {
port?: number;
ipcAckTimeout: number;
on(event: "error", listener: (err?: Error) => void): this;
on(event: "brokerMessage", listener: (brokerId: string, data: any, callback: (err: Error | null, data: any) => void) => void): this;
on(event: "ready", listener: (data: any) => void): this;
on(event: "exit", listener: (data: WorkerExitInfo) => void): this;
on(event: 'error', listener: (err?: Error) => void): this;
on(event: 'brokerMessage', listener: BrokerMessageListener): this;
on(event: 'ready', listener: (data: any) => void): this;
on(event: 'exit', listener: (data: ExitData) => void): this;
once(event: 'error', listener: (err?: Error) => void): this;
once(event: 'brokerMessage', listener: BrokerMessageListener): this;
once(event: 'ready', listener: (data: any) => void): this;
once(event: 'exit', listener: (data: ExitData) => void): this;
removeListener(event: 'error', listener: (err?: Error) => void): this;
removeListener(event: 'brokerMessage', listener: BrokerMessageListener): this;
removeListener(event: 'ready', listener: (data: any) => void): this;
removeListener(event: 'exit', listener: (data: ExitData) => void): this;
off(event: 'error', listener: (err?: Error) => void): this;
off(event: 'brokerMessage', listener: BrokerMessageListener): this;
off(event: 'ready', listener: (data: any) => void): this;
off(event: 'exit', listener: (data: ExitData) => void): this;
sendToBroker(data: any, callback?: (err: Error | null, data: any, brokerId: string) => void): void;
destroy(): void;
}
export function createServer(options?: SCBrokerServerOptions): SCBrokerServer;
export type BrokerMessageListener = (
brokerId: string,
data: any,
callback: (err: Error | null, data: any) => void,
) => void;
export interface ExitData {
id: string;
pid: number;
code: number;
signal: string;
}
export interface AutoReconnectOptions {
initialDelay?: number;
randomness?: number;
@ -77,9 +105,9 @@ export interface SpliceOptions {
}
export interface SCBrokerClient extends EventEmitter {
readonly CONNECTED: "connected";
readonly CONNECTING: "connecting";
readonly DISCONNECTED: "disconnected";
readonly CONNECTED: 'connected';
readonly CONNECTING: 'connecting';
readonly DISCONNECTED: 'disconnected';
socketPath?: string;
port?: number;
@ -87,18 +115,18 @@ export interface SCBrokerClient extends EventEmitter {
autoReconnect: boolean;
autoReconnectOptions?: AutoReconnectOptions;
connectRetryErrorThreshold: number;
state: "connected" | "connecting" | "disconnected";
state: 'connected' | 'connecting' | 'disconnected';
connectAttempts: number;
pendingReconnect: boolean;
pendingReconnectTimeout: number | null;
on(event: "error", listener: (err?: Error) => void): this;
on(event: "warning", listener: (warning?: Error) => void): this;
on(event: "ready", listener: (data: any) => void): this;
on(event: "message", listener: (channel: string, data: any) => void): this;
on(event: "subscribeFail", listener: (err: Error | null, channel: string) => void): this;
on(event: "subscribe", listener: (channel: string) => void): this;
on(event: "unsubscribe", listener: () => void): this;
on(event: 'error', listener: (err?: Error) => void): this;
on(event: 'warning', listener: (warning?: Error) => void): this;
on(event: 'ready', listener: (data: any) => void): this;
on(event: 'message', listener: (channel: string, data: any) => void): this;
on(event: 'subscribeFail', listener: (err: Error | null, channel: string) => void): this;
on(event: 'subscribe', listener: (channel: string) => void): this;
on(event: 'unsubscribe', listener: () => void): this;
isConnected(): boolean;
@ -127,14 +155,23 @@ export interface SCBrokerClient extends EventEmitter {
get(keyChain: KeyChain, callback: (err: Error | null, value: any) => void): void;
getRange(keyChain: KeyChain, fromIndex: number, toIndex: number, callback: (err: Error | null, value: any) => void): void;
getRange(
keyChain: KeyChain,
fromIndex: number,
toIndex: number,
callback: (err: Error | null, value: any) => void,
): void;
getRange(keyChain: KeyChain, fromIndex: number, callback: (err: Error | null, value: any) => void): void;
getAll(callback: (err: Error | null, value: any[] | object) => void): void;
count(keyChain: KeyChain, callback: (err: Error | null, value: number) => void): void;
exec(query: (datamap: FlexiMap) => void, options?: QueryOptions, callback?: (err: Error | null, data: any) => void): void;
exec(
query: (datamap: FlexiMap) => void,
options?: QueryOptions,
callback?: (err: Error | null, data: any) => void,
): void;
exec(query: (datamap: FlexiMap) => void, callback: (err: Error | null, data: any) => void): void;
query(query: (datamap: FlexiMap) => void, data?: any, callback?: (err: Error | null, data: any) => void): void;
@ -143,7 +180,13 @@ export interface SCBrokerClient extends EventEmitter {
remove(keyChain: KeyChain, getValue?: boolean, callback?: (err?: Error) => void): void;
remove(keyChain: KeyChain, callback?: (err?: Error) => void): void;
removeRange(keyChain: KeyChain, fromIndex: number, toIndex?: number, getValue?: boolean, callback?: (err?: Error) => void): void;
removeRange(
keyChain: KeyChain,
fromIndex: number,
toIndex?: number,
getValue?: boolean,
callback?: (err?: Error) => void,
): void;
removeRange(keyChain: KeyChain, fromIndex: number, toIndex?: number, callback?: (err?: Error) => void): void;
removeRange(keyChain: KeyChain, fromIndex: number, callback?: (err?: Error) => void): void;

View File

@ -1,69 +1,117 @@
import { SCServerSocket } from "socketcluster-server";
import SCBroker = require("sc-broker/scbroker");
import { FlexiMap } from "fleximap";
import { ExpiryManager } from "expirymanager";
import * as scClusterBrokerClient from "scc-broker-client";
import scBroker = require('sc-broker');
import SCBroker = require('sc-broker/scbroker');
import * as scClusterBrokerClient from 'scc-broker-client';
// From the README
// $ExpectType SCBrokerServer
scBroker.createServer({ port: 9000, secretKey: 'mySecretKey' });
const conf = { port: 9000 };
const server = scBroker.createServer(conf);
server.on('ready', () => {
console.log('Server ready, create client');
const client = scBroker.createClient(conf);
// $ExpectType boolean
client.isConnected();
});
server.destroy();
const dataClient = scBroker.createClient({ port: 9000, secretKey: 'mySecretKey' });
dataClient.set(['this', 'is', 'a', 'deep', 'key'], 'Hello world');
dataClient.get(['this', 'is', 'a'], (err, val) => {
if (!err) console.log(val);
});
dataClient.add(['this', 'is', 'a'], 'foo');
dataClient.get(['this', 'is', 'a', 0], (err, val) => {
if (!err) console.log(val);
});
////////////////////////////////////////////////////
/// SCBroker tests
////////////////////////////////////////////////////
const run = () => {
console.log("run called!");
console.log('run called!');
};
let scBroker = new SCBroker();
scBroker = new SCBroker({ run });
scBroker.options = { environment: "prod" };
let broker = new SCBroker();
broker = new SCBroker({ run });
broker.options = { environment: 'prod' };
const id: number = scBroker.id;
const instanceId: number = scBroker.instanceId;
const dataMap: FlexiMap = scBroker.dataMap;
const dataExpirer: ExpiryManager = scBroker.dataExpirer;
const subscriptions = scBroker.subscriptions;
// $ExpectType number
broker.id;
const socket: SCServerSocket = subscriptions[1]["test"];
// $ExpectType number
broker.instanceId;
scBroker.on("subscribe", channel => {
const subscribeChannel: string = channel;
});
scBroker.on("unsubscribe", channel => {
const unsubscribeChannel: string = channel;
});
scBroker.on("publish", (channel, data) => {
const publishChannel: string = channel;
const publishData: any = data;
});
scBroker.on("masterMessage", (data, masterMessageResponse) => {
const masterMessageData: any = data;
masterMessageResponse(null, "test");
masterMessageResponse(new Error(), null);
// $ExpectType FlexiMap
broker.dataMap;
// $ExpectType ExpiryManager
broker.dataExpirer;
const subscriptions = broker.subscriptions;
// $ExpectType ComSocket
subscriptions[1]['test'];
broker
.on('subscribe', channel => {
// $ExpectType string
channel;
})
.on('unsubscribe', channel => {
// $ExpectType string
channel;
})
.on('publish', (channel, data) => {
// $ExpectType string
channel;
// $ExpectType any
data;
})
.on('masterMessage', (data, masterMessageResponse) => {
// $ExpectType any
data;
masterMessageResponse(null, 'test');
masterMessageResponse(new Error(), null);
});
broker.publish('testChannel', 123);
broker.exec(dataMap => {
dataMap.set(['main', 'message'], 'Message');
return dataMap.get(['main']);
});
scBroker.publish("testChannel", 123);
scBroker.exec(dataMap => {
dataMap.set(["main", "message"], "Message");
return dataMap.get(["main"]);
});
scBroker.sendToMaster("data");
scBroker.sendToMaster(123, (err, response) => {
broker.sendToMaster('data');
broker.sendToMaster(123, (err, response) => {
if (!err) {
const answer = response;
// $ExpectType any
response;
}
});
class MyBroker extends SCBroker {
run() {
this.on("subscribe", channel => {});
this.on('subscribe', channel => {});
}
}
// From the socketcluster sample
class Broker extends SCBroker {
run() {
console.log(" >> Broker PID:", process.pid);
console.log(' >> Broker PID:', process.pid);
if (this.options.clusterStateServerHost) {
scClusterBrokerClient.attach(this, {
@ -74,7 +122,7 @@ class Broker extends SCBroker {
authKey: this.options.clusterAuthKey,
stateServerConnectTimeout: this.options.clusterStateServerConnectTimeout,
stateServerAckTimeout: this.options.clusterStateServerAckTimeout,
stateServerReconnectRandomness: this.options.clusterStateServerReconnectRandomness
stateServerReconnectRandomness: this.options.clusterStateServerReconnectRandomness,
});
}
}

View File

@ -1,21 +1,19 @@
import { EventEmitter } from "events";
import { SCServer, SCServerSocket } from "socketcluster-server";
import { FlexiMap, KeyChain } from "fleximap";
import { ExpiryManager } from "expirymanager";
export = SCBroker;
import { EventEmitter } from 'events';
import { FlexiMap, KeyChain } from 'fleximap';
import { ExpiryManager } from 'expirymanager';
import { ComSocket } from 'ncom';
interface Subscriptions {
[socketId: number]: {
[channel: string]: SCServerSocket;
[channel: string]: ComSocket;
};
}
declare class SCBroker extends EventEmitter {
readonly type: "broker";
readonly type: 'broker';
readonly MIDDLEWARE_SUBSCRIBE: "subscribe";
readonly MIDDLEWARE_PUBLISH_IN: "publishIn";
readonly MIDDLEWARE_SUBSCRIBE: 'subscribe';
readonly MIDDLEWARE_PUBLISH_IN: 'publishIn';
id: number;
debugPort: number;
@ -26,6 +24,7 @@ declare class SCBroker extends EventEmitter {
subscriptions: Subscriptions;
constructor(options?: { run?: () => void });
static create(options?: { run?: () => void }): SCBroker;
on(event: "subscribe" | "unsubscribe", listener: (channel: string) => void): this;
on(event: "publish", listener: (channel: string, data: any) => void): this;
@ -35,10 +34,19 @@ declare class SCBroker extends EventEmitter {
publish(channel: string, message: any): void;
run(): void;
exec(query: (dataMap: FlexiMap, dataExpirer: ExpiryManager, subscriptions: Subscriptions) => any, baseKey?: KeyChain): any;
exec(query: SCBroker.QueryFunction, baseKey?: KeyChain): any;
addMiddleware(type: 'subscribe', middleware: SCBroker.SubscribeMiddleware): void;
addMiddleware(type: 'publish', middleware: SCBroker.PublishMiddleware): void;
removeMiddleware(type: 'subscribe', middleware: SCBroker.SubscribeMiddleware): void;
removeMiddleware(type: 'publish', middleware: SCBroker.PublishMiddleware): void;
sendToMaster(data: any, callback?: (err: Error | null, responseData: any) => void): void;
}
export = SCBroker;
declare namespace SCBroker {
interface SCBrokerOptions {
// An ID to associate with this specific instance of SC
@ -61,4 +69,21 @@ declare namespace SCBroker {
[additionalOptions: string]: any;
}
type SubscribeMiddleware = (req: SubscribeMiddlewareData) => void;
interface SubscribeMiddlewareData {
socket: ComSocket;
channel: string;
}
type PublishMiddleware = (req: PublishMiddlewareData) => void;
interface PublishMiddlewareData {
socket: ComSocket;
channel: string;
command: object;
}
type QueryFunction = (dataMap: FlexiMap, dataExpirer: ExpiryManager, subscriptions: Subscriptions) => any;
}

View File

@ -2,7 +2,8 @@
"compilerOptions": {
"module": "commonjs",
"lib": [
"es6"
"es6",
"esnext.asynciterable"
],
"noImplicitAny": true,
"noImplicitThis": true,
@ -13,6 +14,11 @@
"../"
],
"types": [],
"paths": {
"sc-channel": [
"sc-channel/v1"
]
},
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
@ -20,4 +26,4 @@
"index.d.ts",
"sc-broker-tests.ts"
]
}
}

View File

@ -1,45 +1,56 @@
// Type definitions for sc-channel 1.2
// Type definitions for sc-channel 2.0
// Project: https://github.com/SocketCluster/sc-channel
// Definitions by: Daniel Rose <https://github.com/DanielRose>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.4
import Emitter = require("component-emitter");
import { SCExchange, handlerFunction } from "sc-broker-cluster";
import AsyncIterableStream = require('async-iterable-stream');
import StreamDemux = require('stream-demux');
import DemuxedConsumableStream = require('stream-demux/demuxed-consumable-stream');
export interface SCChannelOptions {
waitForAuth?: boolean;
batch?: boolean;
data?: any;
}
export class SCChannel extends Emitter {
readonly PENDING: "pending";
readonly SUBSCRIBED: "subscribed";
readonly UNSUBSCRIBED: "unsubscribed";
declare class SCChannel<T> extends AsyncIterableStream<T> {
readonly PENDING: 'pending';
readonly SUBSCRIBED: 'subscribed';
readonly UNSUBSCRIBED: 'unsubscribed';
name: string;
state: ChannelState;
waitForAuth: boolean;
batch: boolean;
data: any;
client: SCChannel.Client;
constructor(name: string, client: SCExchange, options?: SCChannelOptions);
state: SCChannel.ChannelState;
options: object;
setOptions(options?: SCChannelOptions): void;
getState(): "pending" | "subscribed" | "unsubscribed";
constructor(name: string, client: SCChannel.Client, eventDemux: StreamDemux<T>, dataStream: AsyncIterableStream<T>);
createAsyncIterator(timeout?: number): AsyncIterableStream.AsyncIterator<T>;
listener(eventName: string): DemuxedConsumableStream<T>;
closeListener(eventName: string): void;
closeAllListeners(): void;
close(): void;
subscribe(options?: any): void;
unsubscribe(): void;
isSubscribed(includePending?: boolean): boolean;
publish(data: any, callback?: (err?: Error) => void): void;
watch(handler: handlerFunction): void;
unwatch(handler?: handlerFunction): void;
watchers(): handlerFunction[];
destroy(): void;
publish(data: any): any;
}
export type ChannelState = "pending" | "subscribed" | "unsubscribed";
export = SCChannel;
declare namespace SCChannel {
interface Client {
closeChannel(channelName: string): void;
getChannelState(channelName: string): ChannelState;
getChannelOptions(channelName: string): object;
subscribe(channelName: string): SCChannel<any>;
unsubscribe(channelName: string): void;
isSubscribed(channelName: string, includePending?: boolean): boolean;
publish(channelName: string, data: any): any;
}
type ChannelState = 'pending' | 'subscribed' | 'unsubscribed';
}

View File

@ -1,33 +0,0 @@
import { SCChannel, SCChannelOptions } from "sc-channel";
import { handlerFunction, Client, SCBrokerClusterClientOptions } from "sc-broker-cluster";
const clientOptions: SCBrokerClusterClientOptions = { brokers: [], connectRetryErrorThreshold: 0 };
const client = new Client(clientOptions);
let channel = new SCChannel("channelName", client.exchange());
const channelOptions: SCChannelOptions = {};
channel = new SCChannel("channelName", client.exchange(), channelOptions);
channel.state = channel.PENDING;
channel.state = channel.SUBSCRIBED;
channel.state = channel.UNSUBSCRIBED;
const channelName: string = channel.name;
const handler: handlerFunction = () => { };
channel.watch(handler);
channel.unwatch();
channel.subscribe();
channel.subscribe(channelOptions);
channel.unsubscribe();
const data: any = channel.data;
channel.emit("subscribe", channelName);
channel.emit("unsubscribe", channelName);
channel.setOptions(channelOptions);
channel.publish(data);

View File

@ -2,7 +2,8 @@
"compilerOptions": {
"module": "commonjs",
"lib": [
"es6"
"es6",
"esnext.asynciterable"
],
"noImplicitAny": true,
"noImplicitThis": true,
@ -17,7 +18,6 @@
"forceConsistentCasingInFileNames": true
},
"files": [
"index.d.ts",
"sc-channel-tests.ts"
"index.d.ts"
]
}

63
types/sc-channel/v1/index.d.ts vendored Normal file
View File

@ -0,0 +1,63 @@
// Type definitions for sc-channel 1.2
// Project: https://github.com/SocketCluster/sc-channel
// Definitions by: Daniel Rose <https://github.com/DanielRose>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
import Emitter = require('component-emitter');
declare class SCChannel extends Emitter {
readonly PENDING: 'pending';
readonly SUBSCRIBED: 'subscribed';
readonly UNSUBSCRIBED: 'unsubscribed';
name: string;
state: SCChannel.ChannelState;
waitForAuth: boolean;
batch: boolean;
data: any;
constructor(name: string, client: SCChannel.SCClient, options?: SCChannel.SCChannelOptions);
setOptions(options?: SCChannel.SCChannelOptions): void;
getState(): SCChannel.ChannelState;
subscribe(options?: any): void;
unsubscribe(): void;
isSubscribed(includePending?: boolean): boolean;
publish(data: any, callback?: (err?: Error) => void): void;
watch(handler: SCChannel.HandlerFunction): void;
unwatch(handler?: SCChannel.HandlerFunction): void;
watchers(): SCChannel.HandlerFunction[];
destroy(): void;
}
export { SCChannel };
declare namespace SCChannel {
type ChannelState = 'pending' | 'subscribed' | 'unsubscribed';
type HandlerFunction = (data: any) => void;
interface SCChannelOptions {
waitForAuth?: boolean;
batch?: boolean;
data?: any;
}
interface SCClient {
subscribe(channelName: string, options?: any): SCChannel;
unsubscribe(channelName: string): void;
isSubscribed(channelName: string, includePending?: boolean): boolean;
publish(channelName: string, data: any, callback?: (err?: Error) => void): void;
watch(channelName: string, handler: HandlerFunction): void;
unwatch(channelName: string, handler?: HandlerFunction): void;
watchers(channelName: string): HandlerFunction[];
destroyChannel(channelName: string): void;
}
}

View File

@ -0,0 +1,30 @@
{
"compilerOptions": {
"module": "commonjs",
"lib": [
"es6"
],
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"baseUrl": "../../",
"typeRoots": [
"../../"
],
"types": [],
"paths": {
"sc-channel": [
"sc-channel/v1"
],
"sc-channel/*": [
"sc-channel/v1/*"
]
},
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.d.ts"
]
}

View File

@ -0,0 +1,3 @@
{
"extends": "dtslint/dt.json"
}

View File

@ -1,55 +1,59 @@
import * as scErrors from "sc-errors";
import http = require("http");
import * as socketClusterServer from "socketcluster-server";
const httpServer = http.createServer();
const scServer = socketClusterServer.attach(httpServer);
import * as scErrors from 'sc-errors';
// Create the various errors.
const authTokenExpiredError = new scErrors.AuthTokenExpiredError("error", new Date());
const authTokenInvalidError = new scErrors.AuthTokenInvalidError("error");
const authTokenNotBeforeError = new scErrors.AuthTokenNotBeforeError("error", new Date());
const authTokenError = new scErrors.AuthTokenError("error");
const silentMiddlewareBlockedError = new scErrors.SilentMiddlewareBlockedError("error", scServer.MIDDLEWARE_AUTHENTICATE);
const invalidActionError = new scErrors.InvalidActionError("error");
const invalidArgumentsError = new scErrors.InvalidArgumentsError("error");
const invalidOptionsError = new scErrors.InvalidOptionsError("error");
const invalidMessageError = new scErrors.InvalidMessageError("error");
const socketProtocolError = new scErrors.SocketProtocolError("error", 1001);
const serverProtocolError = new scErrors.ServerProtocolError("error");
const httpServerError = new scErrors.HTTPServerError("error");
const resourceLimitError = new scErrors.ResourceLimitError("error");
const timeoutError = new scErrors.TimeoutError("error");
const badConnectionError = new scErrors.BadConnectionError("error", "connectAbort");
const brokerError = new scErrors.BrokerError("error");
const processExitError = new scErrors.ProcessExitError("error");
const unknownError = new scErrors.UnknownError("error");
new scErrors.AuthTokenExpiredError('error', new Date());
new scErrors.AuthTokenInvalidError('error');
new scErrors.AuthTokenNotBeforeError('error', new Date());
new scErrors.AuthTokenError('error');
new scErrors.SilentMiddlewareBlockedError('error', 'authenticate');
new scErrors.InvalidActionError('error');
new scErrors.InvalidArgumentsError('error');
new scErrors.InvalidOptionsError('error');
new scErrors.InvalidMessageError('error');
new scErrors.SocketProtocolError('error', 1001);
new scErrors.ServerProtocolError('error');
new scErrors.HTTPServerError('error');
new scErrors.ResourceLimitError('error');
new scErrors.TimeoutError('error');
new scErrors.BadConnectionError('error', 'connectAbort');
new scErrors.BrokerError('error');
new scErrors.ProcessExitError('error');
new scErrors.UnknownError('error');
// Check some of the error and ignore statusses
console.log(scErrors.socketProtocolErrorStatuses[1001]);
console.log(scErrors.socketProtocolErrorStatuses[4000]);
console.log(scErrors.socketProtocolIgnoreStatuses[1000]);
// $ExpectType string
scErrors.socketProtocolErrorStatuses[1001];
// $ExpectType string
scErrors.socketProtocolErrorStatuses[4000];
// $ExpectType string
scErrors.socketProtocolIgnoreStatuses[1000];
// Dehydrate and hydrate an error
const err = new Error();
let dehydratedError = scErrors.dehydrateError(err);
let hydratedError = scErrors.hydrateError(dehydratedError);
scErrors.hydrateError(dehydratedError);
dehydratedError = scErrors.dehydrateError(err, true);
hydratedError = scErrors.hydrateError(dehydratedError);
scErrors.hydrateError(dehydratedError);
// decycle
const foo = {
bar: 5
bar: 5,
};
const baz = {
a: 1,
b: "test",
b: 'test',
c: foo,
d: {
d1: foo
}
d1: foo,
},
};
const decycledBaz = scErrors.decycle(baz);
scErrors.decycle(baz);

View File

@ -13,6 +13,17 @@
"../"
],
"types": [],
"paths": {
"socketcluster-server": [
"socketcluster-server/v14"
],
"sc-broker-cluster": [
"sc-broker-cluster/v6"
],
"sc-channel": [
"sc-channel/v1"
]
},
"noEmit": true,
"forceConsistentCasingInFileNames": true
},

View File

@ -13,6 +13,17 @@
"../"
],
"types": [],
"paths": {
"socketcluster-server": [
"socketcluster-server/v14"
],
"sc-broker-cluster": [
"sc-broker-cluster/v6"
],
"sc-channel": [
"sc-channel/v1"
]
},
"noEmit": true,
"forceConsistentCasingInFileNames": true
},

View File

@ -1,7 +1,10 @@
import { EventEmitter } from "events";
import SCClientSocket = require("socketcluster-client/lib/scclientsocket");
import Hasher = require("./hasher");
import { Secret } from "jsonwebtoken";
import AsyncStreamEmitter = require('async-stream-emitter');
import { AGClientSocket } from 'socketcluster-client';
import { Secret } from 'jsonwebtoken';
import AGChannel = require('ag-channel');
import ConsumableStream = require('consumable-stream');
import Hasher = require('./hasher');
interface ClientPoolOptions {
clientCount?: number;
@ -15,34 +18,41 @@ interface BrokenDownURI {
secure?: true;
}
declare class ClientPool extends EventEmitter {
declare class ClientPool extends AsyncStreamEmitter<any> {
hasher: Hasher;
clientCount: number;
targetURI: string;
authKey?: Secret;
areClientListenersBound: boolean;
clients: SCClientSocket[];
clients: AGClientSocket[];
constructor(options?: ClientPoolOptions);
on(event: "error", listener: (err: Error) => void): this;
on(event: "subscribe", listener: (data: ClientPool.SubscribeData) => void): this;
on(event: "subscribeFail", listener: (data: ClientPool.SubscribeFailData) => void): this;
on(event: "publish" | "publishFail", listener: (data: ClientPool.PublishData) => void): this;
emit(eventName: 'error', data: { error: Error }): void;
emit(eventName: 'subscribe', data: ClientPool.SubscribeData): void;
emit(eventName: 'subscribeFail', data: ClientPool.SubscribeFailData): void;
emit(eventName: 'publish', data: ClientPool.PublishData): void;
emit(eventName: 'publishFail', data: ClientPool.PublishFailData): void;
bindClientListeners(): void;
unbindClientListeners(): void;
listener(eventName: 'error'): ConsumableStream<{ error: Error }>;
listener(eventName: 'subscribe'): ConsumableStream<ClientPool.SubscribeData>;
listener(eventName: 'subscribeFail'): ConsumableStream<ClientPool.SubscribeFailData>;
listener(eventName: 'publish'): ConsumableStream<ClientPool.PublishData>;
listener(eventName: 'publishFail'): ConsumableStream<ClientPool.PublishFailData>;
breakDownURI(uri: string): BrokenDownURI;
selectClient(key: string): SCClientSocket;
selectClient(key: string): AGClientSocket;
publish(channelName: string, data: any): void;
invokePublish(channelName: string, data: any): Promise<void>;
subscriptions(includePending?: boolean): string[];
subscribeAndWatch(channelName: string, handler: (data: any) => void): void;
destroyChannel(channelName: string): void;
subscribe(channelName: string, options?: AGClientSocket.SubscribeOptions): AGChannel<any>;
unsubscribe(channelName: string): Promise<void>;
isSubscribed(channelName: string, includePending?: boolean): boolean;
closeChannel(channelName: string): void;
destroy(): void;
}
@ -65,4 +75,11 @@ declare namespace ClientPool {
channel: string;
data: any;
}
interface PublishFailData {
targetURI: string;
poolIndex: number;
channel: string;
error: Error;
}
}

View File

@ -1,25 +1,46 @@
import { EventEmitter } from "events";
import SCBroker = require("sc-broker/scbroker");
import { Secret } from "jsonwebtoken";
import { MappingEngine, SCCBrokerClientOptions } from ".";
import ClientPool = require("./client-pool");
import AsyncStreamEmitter = require('async-stream-emitter');
import SCBroker = require('sc-broker/scbroker');
import { Secret } from 'jsonwebtoken';
import ConsumableStream = require('consumable-stream');
declare class ClusterBrokerClient extends EventEmitter {
import { MappingEngine, SCCBrokerClientOptions } from '.';
import ClientPool = require('./client-pool');
declare class ClusterBrokerClient extends AsyncStreamEmitter<any> {
broker: SCBroker;
sccBrokerClientPools: ClientPool[];
sccBrokerURIList: string[];
authKey?: Secret;
mappingEngine: "skeletonRendezvous" | "simple" | MappingEngine;
mappingEngine: 'skeletonRendezvous' | 'simple' | MappingEngine;
clientPoolSize: number;
mapper: MappingEngine;
isReady: boolean;
errors: {
NoMatchingSubscribeTargetError: (channelName: string) => Error;
NoMatchingUnsubscribeTargetError: (channelName: string) => Error;
NoMatchingPublishTargetError: (channelName: string) => Error;
};
constructor(broker: SCBroker, options?: SCCBrokerClientOptions);
on(event: "error", listener: (err: Error) => void): this;
on(event: "subscribe", listener: (data: ClientPool.SubscribeData) => void): this;
on(event: "subscribeFail", listener: (data: ClientPool.SubscribeFailData) => void): this;
on(event: "publish" | "publishFail", listener: (data: ClientPool.PublishData) => void): this;
on(event: "message", listener: (channelName: string, packet: any) => void): this;
emit(eventName: 'error', data: { error: Error }): void;
emit(eventName: 'updateWorkers', data: UpdateWorkersData): void;
emit(eventName: 'updateBrokers', data: UpdateBrokersData): void;
emit(event: 'subscribe', data: ClientPool.SubscribeData): void;
emit(event: 'subscribeFail', data: ClientPool.SubscribeFailData): void;
emit(eventName: 'publish', data: ClientPool.PublishData): void;
emit(eventName: 'publishFail', data: ClientPool.PublishFailData): void;
emit(eventName: 'message', data: MessageData): void;
listener(eventName: 'error'): ConsumableStream<{ error: Error }>;
listener(eventName: 'updateWorkers'): ConsumableStream<UpdateWorkersData>;
listener(eventName: 'updateBrokers'): ConsumableStream<UpdateBrokersData>;
listener(event: 'subscribe'): ConsumableStream<ClientPool.SubscribeData>;
listener(event: 'subscribeFail'): ConsumableStream<ClientPool.SubscribeFailData>;
listener(eventName: 'publish'): ConsumableStream<ClientPool.PublishData>;
listener(eventName: 'publishFail'): ConsumableStream<ClientPool.PublishFailData>;
listener(eventName: 'message'): ConsumableStream<MessageData>;
mapChannelNameToBrokerURI(channelName: string): string;
setBrokers(sccBrokerURIList: string[]): void;
@ -28,7 +49,20 @@ declare class ClusterBrokerClient extends EventEmitter {
subscribe(channelName: string): void;
unsubscribe(channelName: string): void;
publish(channelName: string, data: any): void;
invokePublish(channelName: string, data: any): void;
}
interface UpdateWorkersData {
workerURIs: string;
sourceWorkerURI: string;
}
interface UpdateBrokersData {
brokerURIs: string[];
}
interface MessageData {
channelName: string;
packet: any;
}
export = ClusterBrokerClient;

View File

@ -1,5 +1,5 @@
declare class Hasher {
hashToIndex(key: string, modulo: number): number;
hashToIndex(key: string | undefined | null, modulo: number): number;
hashToHex(key: string, algorithm?: string): string;
}

View File

@ -1,13 +1,13 @@
// Type definitions for scc-broker-client 6.1
// Type definitions for scc-broker-client 7.0
// Project: https://github.com/SocketCluster/scc-broker-client
// Definitions by: Daniel Rose <https://github.com/DanielRose>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.4
import SCBroker = require("sc-broker/scbroker");
import ClusterBrokerClient = require("./cluster-broker-client");
import { Secret } from "jsonwebtoken";
import ClusterBrokerClient = require("./cluster-broker-client");
export interface MappingEngine {
setSites(sites: string[]): void;
getSites(): string[];
@ -26,8 +26,15 @@ export interface SCCBrokerClientOptions {
stateServerConnectTimeout?: number;
stateServerAckTimeout?: number;
instancePort?: number;
instanceId?: string;
instanceIp?: string;
instanceIpFamily?: string;
noErrorLogging?: boolean;
brokerRetryDelay?: number;
pubSubBatchDuration?: number;
}
export function attach(broker: SCBroker, options: SCCBrokerClientOptions): ClusterBrokerClient;

View File

@ -1,41 +1,97 @@
import * as scClusterBrokerClient from "scc-broker-client";
import SCBroker = require("sc-broker/scbroker");
import { attach } from 'scc-broker-client';
import SCBroker = require('sc-broker/scbroker');
const scBroker = new SCBroker();
const clusterBrokerClient = scClusterBrokerClient
.attach(scBroker, {
stateServerHost: "localhost",
stateServerPort: 8000,
mappingEngine: "simple",
clientPoolSize: 100,
authKey: "secret-key",
stateServerConnectTimeout: 10000,
stateServerAckTimeout: 1000,
stateServerReconnectRandomness: 100
})
.on("error", err => {
console.log(`Received ${err}`);
})
.on("subscribe", data => {
console.log(`Subscribed to ${data.channel}, ${data.poolIndex}, ${data.targetURI}`);
})
.on("subscribeFail", data => {
console.log(`Error ${data.error} while subscribing to ${data.channel}, ${data.poolIndex}, ${data.targetURI}`);
})
.on("publish", data => {
console.log(`Published ${data.data} to ${data.channel}, ${data.poolIndex}, ${data.targetURI}`);
})
.on("publishFail", data => {
console.log(`Error while publishing ${data.data} to ${data.channel}, ${data.poolIndex}, ${data.targetURI}`);
})
.on("message", (channelName, packet) => {
console.log(`Received ${packet} on channel ${channelName}`);
});
const clusterBrokerClient = attach(scBroker, {
stateServerHost: 'localhost',
stateServerPort: 8000,
mappingEngine: 'simple',
clientPoolSize: 100,
authKey: 'secret-key',
stateServerConnectTimeout: 10000,
stateServerAckTimeout: 1000,
stateServerReconnectRandomness: 100,
});
clusterBrokerClient.subscribe("test-channel");
clusterBrokerClient.publish("test-channel", "lalala");
(async () => {
// tslint:disable-next-line: await-promise Bug in tslint: https://github.com/palantir/tslint/issues/3997
for await (const { error } of clusterBrokerClient.listener('error')) {
// $ExpectType Error
error;
}
const subs = clusterBrokerClient.getAllSubscriptions();
// tslint:disable-next-line: await-promise Bug in tslint: https://github.com/palantir/tslint/issues/3997
for await (const { channel, poolIndex, targetURI } of clusterBrokerClient.listener('subscribe')) {
// $ExpectType string
channel;
clusterBrokerClient.unsubscribe("test-channel");
// $ExpectType number
poolIndex;
// $ExpectType string
targetURI;
}
// tslint:disable-next-line: await-promise Bug in tslint: https://github.com/palantir/tslint/issues/3997
for await (const { channel, poolIndex, targetURI, error } of clusterBrokerClient.listener('subscribeFail')) {
// $ExpectType string
channel;
// $ExpectType number
poolIndex;
// $ExpectType string
targetURI;
// $ExpectType Error
error;
}
// tslint:disable-next-line: await-promise Bug in tslint: https://github.com/palantir/tslint/issues/3997
for await (const { targetURI, poolIndex, channel, data } of clusterBrokerClient.listener('publish')) {
// $ExpectType string
targetURI;
// $ExpectType number
poolIndex;
// $ExpectType string
channel;
// $ExpectType any
data;
}
// tslint:disable-next-line: await-promise Bug in tslint: https://github.com/palantir/tslint/issues/3997
for await (const { targetURI, poolIndex, channel, error } of clusterBrokerClient.listener('publishFail')) {
// $ExpectType string
targetURI;
// $ExpectType number
poolIndex;
// $ExpectType string
channel;
// $ExpectType Error
error;
}
// tslint:disable-next-line: await-promise Bug in tslint: https://github.com/palantir/tslint/issues/3997
for await (const { channelName, packet } of clusterBrokerClient.listener('message')) {
// $ExpectType string
channelName;
// $ExpectType any
packet;
}
})();
clusterBrokerClient.subscribe('test-channel');
clusterBrokerClient.invokePublish('test-channel', 'lalala');
// $ExpectType string[]
clusterBrokerClient.getAllSubscriptions();
clusterBrokerClient.unsubscribe('test-channel');

View File

@ -2,7 +2,8 @@
"compilerOptions": {
"module": "commonjs",
"lib": [
"es6"
"es6",
"esnext.asynciterable"
],
"noImplicitAny": true,
"noImplicitThis": true,
@ -13,6 +14,11 @@
"../"
],
"types": [],
"paths": {
"sc-channel": [
"sc-channel/v1"
]
},
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
@ -20,4 +26,4 @@
"index.d.ts",
"scc-broker-client-tests.ts"
]
}
}

View File

@ -1,23 +1,13 @@
// Type definitions for socketcluster-client 13.0
// Type definitions for socketcluster-client 15.1
// Project: https://github.com/SocketCluster/socketcluster-client, http://socketcluster.io
// Definitions by: Daniel Rose <https://github.com/DanielRose>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.4
import { SCAuthEngine } from "sc-auth";
import { SCServer } from "socketcluster-server";
export import SCClientSocket = require("./lib/scclientsocket");
export import factory = require("./lib/factory");
export import AGClientSocket = require("./lib/clientsocket");
export function create(options?: SCClientSocket.ClientOptions): SCClientSocket;
/** @deprecated */
export function connect(options?: SCClientSocket.ClientOptions): SCClientSocket;
export function destroy(socket: SCClientSocket): void;
export const clients: {
[id: string]: SCClientSocket;
};
export function create(options?: AGClientSocket.ClientOptions): AGClientSocket;
export const version: string;

View File

@ -0,0 +1,13 @@
export interface AGAuthEngine {
saveToken(name: string, token: object, options?: object): Promise<object>;
removeToken(name: string): Promise<object>;
loadToken(name: string): Promise<object>;
}
export class AuthEngine implements AGAuthEngine {
constructor();
saveToken(name: string, token: object, options?: object): Promise<object>;
removeToken(name: string): Promise<object>;
loadToken(name: string): Promise<object>;
}

View File

@ -0,0 +1,450 @@
import { SocketProtocolIgnoreStatuses, SocketProtocolErrorStatuses } from 'sc-errors';
import WebSocket = require('ws');
import AsyncStreamEmitter = require('async-stream-emitter');
import AGChannel = require('ag-channel');
import Consumer = require('writable-consumable-stream/consumer');
import AGServer = require('socketcluster-server/server');
import DemuxedConsumableStream = require('stream-demux/demuxed-consumable-stream');
import ConsumableStream = require('consumable-stream');
import { AGAuthEngine } from './auth';
import AGTransport = require('./transport');
declare class AGClientSocket extends AsyncStreamEmitter<any> implements AGChannel.Client {
readonly CONNECTING: 'connecting';
readonly OPEN: 'open';
readonly CLOSED: 'closed';
readonly AUTHENTICATED: 'authenticated';
readonly UNAUTHENTICATED: 'unauthenticated';
readonly SUBSCRIBED: 'subscribed';
readonly PENDING: 'pending';
readonly UNSUBSCRIBED: 'unsubscribed';
readonly ignoreStatuses: SocketProtocolIgnoreStatuses;
readonly errorStatuses: SocketProtocolErrorStatuses;
options: AGClientSocket.ClientOptions;
id: string | null;
clientId?: string;
version: string | null;
protocolVersion: AGClientSocket.ProtocolVersions;
state: AGClientSocket.States;
authState: AGClientSocket.AuthStates;
signedAuthToken: string | null;
authToken: object | null;
authTokenName: string;
wsOptions?: WebSocket.ClientOptions;
pendingReconnect: boolean;
pendingReconnectTimeout: number;
preparingPendingSubscriptions: boolean;
ackTimeout: number;
connectTimeout: number;
pingTimeout: number;
pingTimeoutDisabled: boolean;
channelPrefix: string | null;
disconnectOnUnload: boolean;
connectAttempts: number;
isBufferingBatch: boolean;
isBatching: boolean;
batchOnHandshake: boolean;
batchOnHandshakeDuration: number;
auth: AGAuthEngine;
codec: AGServer.CodecEngine;
transport?: AGTransport;
poolIndex?: number;
constructor(opts: AGClientSocket.ClientOptions);
emit(eventName: 'removeAuthToken', data: { oldAuthToken: object }): void;
emit(eventName: 'connect', data: AGClientSocket.ConnectData): void;
emit(eventName: 'connecting', data: {}): void;
emit(eventName: 'authStateChange', data: AGClientSocket.AuthStateChangeData): void;
emit(eventName: 'authenticate', data: AGClientSocket.AuthenticateData): void;
emit(eventName: 'deauthenticate', data: AGClientSocket.DeauthenticateData): void;
emit(eventName: 'error', data: { error: Error }): void;
emit(eventName: 'connectAbort' | 'disconnect' | 'close', data: AGClientSocket.CloseData): void;
emit(eventName: 'subscribeStateChange', data: AGClientSocket.SubscribeStateChangeData): void;
emit(eventName: 'subscribe' | 'subscribeRequest', data: AGClientSocket.SubscribeData): void;
emit(eventName: 'subscribeFail', data: AGClientSocket.SubscribeFailData): void;
emit(eventName: 'unsubscribe', data: AGClientSocket.UnsubscribeData): void;
emit(eventName: 'kickOut', data: AGClientSocket.KickOutData): void;
listener(eventName: 'removeAuthToken'): ConsumableStream<{ oldAuthToken: object }>;
listener(eventName: 'connect'): ConsumableStream<AGClientSocket.ConnectData>;
listener(eventName: 'connecting'): ConsumableStream<{}>;
listener(eventName: 'authStateChange'): ConsumableStream<AGClientSocket.AuthStateChangeData>;
listener(eventName: 'authenticate'): ConsumableStream<AGClientSocket.AuthenticateData>;
listener(eventName: 'deauthenticate'): ConsumableStream<AGClientSocket.DeauthenticateData>;
listener(eventName: 'error'): ConsumableStream<{ error: Error }>;
listener(eventName: 'connectAbort' | 'disconnect' | 'close'): ConsumableStream<AGClientSocket.CloseData>;
listener(eventName: 'subscribeStateChange'): ConsumableStream<AGClientSocket.SubscribeStateChangeData>;
listener(eventName: 'subscribe' | 'subscribeRequest'): ConsumableStream<AGClientSocket.SubscribeData>;
listener(eventName: 'subscribeFail'): ConsumableStream<AGClientSocket.SubscribeFailData>;
listener(eventName: 'unsubscribe'): ConsumableStream<AGClientSocket.UnsubscribeData>;
listener(eventName: 'kickOut'): ConsumableStream<AGClientSocket.KickOutData>;
/* AGChannel.Client start */
closeChannel(channelName: string): void;
killChannel(channelName: string): void;
killChannelOutputConsumer(consumerId: number): void;
killChannelListenerConsumer(consumerId: number): void;
getChannelOutputConsumerStats(consumerId: number): Consumer.ConsumerStats;
getChannelListenerConsumerStats(consumerId: number): Consumer.ConsumerStats;
getChannelBackpressure(channelName: string): number;
getChannelListenerConsumerBackpressure(consumerId: number): number;
getChannelOutputConsumerBackpressure(consumerId: number): number;
channelCloseOutput(channelName: string): void;
channelCloseListener(channelName: string, eventName: string): void;
channelCloseAllListeners(channelName: string): void;
channelKillOutput(channelName: string): void;
channelKillListener(channelName: string, eventName: string): void;
channelKillAllListeners(channelName: string): void;
channelGetOutputConsumerStatsList(channelName: string): Consumer.ConsumerStats[];
channelGetListenerConsumerStatsList(channelName: string, eventName: string): Consumer.ConsumerStats[];
channelGetAllListenersConsumerStatsList(channelName: string): Consumer.ConsumerStats[];
channelGetOutputBackpressure(channelName: string): number;
channelGetListenerBackpressure(channelName: string, eventName: string): number;
channelGetAllListenersBackpressure(channelName: string): number;
channelHasOutputConsumer(channelName: string, consumerId: number): boolean;
channelHasListenerConsumer(channelName: string, eventName: string, consumerId: number): boolean;
channelHasAnyListenerConsumer(channelName: string, consumerId: number): boolean;
getChannelState(channelName: string): AGChannel.ChannelState;
getChannelOptions(channelName: string): object;
subscribe(channelName: string, options?: AGClientSocket.SubscribeOptions): AGChannel<any>;
unsubscribe(channelName: string): Promise<void>;
isSubscribed(channelName: string, includePending?: boolean): boolean;
transmitPublish(channelName: string, data: any): Promise<void>;
invokePublish<T>(channelName: string, data: T): Promise<{ channel: string; data: T }>;
/* AGChannel.Client end */
getBackpressure(): number;
getState(): AGClientSocket.States;
getBytesReceived(): number;
deauthenticate(): Promise<void>;
connect(): void;
disconnect(code?: number, reason?: string): void;
reconnect(code?: number, reason?: string): void;
decodeBase64(encodedString: string): string;
encodeBase64(decodedString: string): string;
getAuthToken(): object | null;
getSignedAuthToken(): string | null;
// Perform client-initiated authentication by providing an encrypted token string.
authenticate(signedAuthToken: string): Promise<AGClientSocket.AuthStatus>;
decode(message: any): any;
encode(object: any): any;
send(data: any): void;
transmit(event: string, data: any, options?: { ackTimeout?: number }): Promise<void>;
invoke<T>(event: string, data: T, options?: { ackTimeout?: number }): Promise<T>;
startBatch(): void;
flushBatch(): void;
cancelBatch(): void;
startBatching(): void;
stopBatching(): void;
cancelBatching(): void;
// ---- Receiver logic ----
receiver(receiverName: string): DemuxedConsumableStream<any>;
closeReceiver(receiverName: string): void;
closeAllReceivers(): void;
killReceiver(receiverName: string): void;
killAllReceivers(): void;
killReceiverConsumer(consumerId: number): void;
getReceiverConsumerStats(consumerId: number): Consumer.ConsumerStats;
getReceiverConsumerStatsList(receiverName: string): Consumer.ConsumerStats[];
getAllReceiversConsumerStatsList(): Consumer.ConsumerStats[];
getReceiverBackpressure(receiverName: string): number;
getAllReceiversBackpressure(): number;
getReceiverConsumerBackpressure(consumerId: number): number;
hasReceiverConsumer(receiverName: string, consumerId: number): boolean;
hasAnyReceiverConsumer(consumerId: number): boolean;
// ---- Procedure logic ----
procedure(procedureName: string): DemuxedConsumableStream<any>;
closeProcedure(procedureName: string): void;
closeAllProcedures(): void;
killProcedure(procedureName: string): void;
killAllProcedures(): void;
killProcedureConsumer(consumerId: number): void;
getProcedureConsumerStats(consumerId: number): Consumer.ConsumerStats;
getProcedureConsumerStatsList(procedureName: string): Consumer.ConsumerStats[];
getAllProceduresConsumerStatsList(): Consumer.ConsumerStats[];
getProcedureBackpressure(procedureName: string): number;
getAllProceduresBackpressure(): number;
getProcedureConsumerBackpressure(consumerId: number): number;
hasProcedureConsumer(procedureName: string, consumerId: number): boolean;
hasAnyProcedureConsumer(consumerId: number): boolean;
// ---- Channel logic ----
channel(channelName: string): AGChannel<any>;
closeAllChannelOutputs(): void;
closeAllChannelListeners(): void;
closeAllChannels(): void;
killAllChannelOutputs(): void;
killAllChannelListeners(): void;
killAllChannels(): void;
getAllChannelOutputsConsumerStatsList(): any[];
getAllChannelListenersConsumerStatsList(): any[];
getAllChannelOutputsBackpressure(): number;
getAllChannelListenersBackpressure(): number;
getAllChannelsBackpressure(): number;
hasAnyChannelOutputConsumer(consumerId: any): boolean;
hasAnyChannelListenerConsumer(consumerId: any): boolean;
// ---- Subscriptions ----
subscriptions(includePending?: boolean): string[];
processPendingSubscriptions(): void;
}
export = AGClientSocket;
declare namespace AGClientSocket {
type AnyFunction = (...args: any[]) => any;
interface ClientOptions {
socketPath?: string;
host?: string;
// Defaults to the current host (read from the URL).
hostname?: string;
// Defaults to false.
secure?: boolean;
// Defaults to 80 if !secure otherwise defaults to 443.
port?: number;
// The URL which SocketCluster uses to make the initial handshake for the WebSocket. Defaults to '/socketcluster/'.
path?: string;
// The protocol scheme for the transport. Defaults to 'ws' or 'wss', depending upon the valur of secure.
protocolScheme?: string;
// A map of key-value pairs which will be used as query parameters for the initial HTTP handshake which will initiate the WebSocket connection.
query?: string | { [key: string]: string };
// (milliseconds) - This is the timeout for getting a response to a AGClientSocket invoke action.
ackTimeout?: number;
// (milliseconds)
connectTimeout?: number;
// Whether or not to automatically connect the socket as soon as it is created. Default is true.
autoConnect?: boolean;
// Whether or not to automatically reconnect the socket when it loses the connection. Default is true.
autoReconnect?: boolean;
// Valid properties are: initialDelay (milliseconds), randomness (milliseconds), multiplier (decimal; default is 1.5) and maxDelay (milliseconds).
autoReconnectOptions?: AutoReconnectOptions;
// Whether or not a client automatically disconnects on page unload. If enabled, the client will disconnect when a user navigates away from the page.
// This can happen when a user closes the tab/window, clicks a link to leave the page, or types a new URL into the address bar. Defaults to true.
disconnectOnUnload?: boolean;
// Whether or not to add a timestamp to the WebSocket handshake request.
timestampRequests?: boolean;
// The query parameter name to use to hold the timestamp.
timestampParam?: string;
// A custom engine to use for storing and loading JWT auth tokens on the client side.
authEngine?: AGAuthEngine | null;
// The name of the JWT auth token (provided to the authEngine - By default this is the localStorage variable name); defaults to 'socketcluster.authToken'.
authTokenName?: string;
// The type to use to represent binary on the client. Defaults to 'arraybuffer'.
binaryType?: string;
// If you set this to true, any data/objects/arrays that you pass to the client socket will be cloned before being sent/queued up. If the socket
// is disconnected and you emit an event, it will be added to a queue which will be processed upon reconnection. The cloneData option is false
// by default; this means that if you emit/publish an object and that object changes somewhere else in your code before the queue is processed,
// then the changed version of that object will be sent out to the server.
cloneData?: boolean;
// This is true by default. If you set this to false, then the socket will not automatically try to subscribe to pending subscriptions on
// connect - Instead, you will have to manually invoke the processSubscriptions callback from inside the 'connect' event handler on the client side.
// See AGClientSocket API. This gives you more fine-grained control with regards to when pending subscriptions are processed after the socket
// connection is established (or re-established).
autoSubscribeOnConnect?: boolean;
// Lets you set a custom codec engine. This allows you to specify how data gets encoded before being sent over the wire and how it gets decoded
// once it reaches the other side. The codecEngine must be an object which exposes an encode(object) and a decode(encodedData) function.
// The encode function can return any data type - Commonly a string or a Buffer/ArrayBuffer. The decode function needs to return a JavaScript
// object which adheres to the SC protocol. The idea of using a custom codec is that it allows you to compress SocketCluster packets in any format
// you like (optimized for any use case) - By decoding these packets back into their original protocol form, SocketCluster will be able process
// them appropriately. Note that if you provide a codecEngine when creating a client socket, you will need to make sure that the server uses the
// same codec by passing the same engine to the AGServer constructor (using the codecEngine option).
codecEngine?: AGServer.CodecEngine | null;
// A prefix to add to the channel names.
channelPrefix?: string | null;
subscriptionRetryOptions?: object | null;
// Whether or not to start batching messages immediately after the connection handshake completes. This is useful for handling connection recovery
// when the client tries to resubscribe to a large number of channels in a very short amount of time. Defaults to false.
batchOnHandshake?: boolean;
// The amount of time in milliseconds after the handshake completes during which all socket messages will be batched. Defaults to 100.
batchOnHandshakeDuration?: number;
// The amount of milliseconds to wait before flushing each batch of messages. Defaults to 50.
batchInterval?: number;
protocolVersion?: ProtocolVersions;
// This object will be passed to the constructor of the ws WebSocket instance.
wsOptions?: WebSocket.ClientOptions;
version?: string;
clientId?: string;
// pingTimeout will be connectTimeout at the start, but it will be updated with values provided by the 'connect' event.
pingTimeout?: number;
pingTimeoutDisabled?: boolean;
callIdGenerator?: CallIdGenerator;
}
interface AutoReconnectOptions {
initialDelay?: number;
randomness?: number;
multiplier?: number;
maxDelay?: number;
}
interface AuthStatus {
isAuthenticated: AuthStates;
authError: Error;
}
interface AuthStateChangeData {
oldAuthState: AuthStates;
newAuthState: AuthStates;
signedAuthToken?: string;
authToken?: object;
}
interface AuthenticateData {
signedAuthToken: string;
authToken: object;
}
interface DeauthenticateData {
oldSignedAuthToken: string | null;
oldAuthToken: object | null;
}
interface CloseData {
code: number;
reason: string;
}
interface ConnectData extends AGTransport.OnOpenValue {
processPendingSubscriptions: () => void;
}
interface SubscribeStateChangeData extends SubscribeData {
oldState: AGChannel.ChannelState;
newState: AGChannel.ChannelState;
}
interface SubscribeData {
channel: string;
subscriptionOptions: SubscribeOptions;
}
interface SubscribeFailData {
error: Error;
channel: string;
subscriptionOptions: SubscribeOptions;
}
interface UnsubscribeData {
channel: string;
}
interface KickOutData {
channel: string;
message?: string;
}
interface SubscribeOptions {
waitForAuth?: boolean;
priority?: number;
data?: any;
}
// type WatcherFunction = (data: any) => void;
type AuthStates = 'authenticated' | 'unauthenticated';
type States = 'connecting' | 'open' | 'closed';
type ProtocolVersions = 1 | 2;
type CallIdGenerator = () => number;
}

View File

@ -0,0 +1,3 @@
import AGClientSocket = require('./clientsocket');
export function create(options?: AGClientSocket.ClientOptions): AGClientSocket;

View File

@ -1,294 +0,0 @@
import Emitter = require("component-emitter");
import { SCServer } from "socketcluster-server";
import { SCAuthEngine } from "sc-auth";
import { SocketProtocolIgnoreStatuses, SocketProtocolErrorStatuses } from "sc-errors";
import { SCChannel, SCChannelOptions, ChannelState } from "sc-channel";
import WebSocket = require("ws");
declare class SCClientSocket extends Emitter {
constructor(opts: SCClientSocket.ClientOptions);
id: string;
clientId: string;
channels: {
[channelName: string]: SCChannel;
};
readonly CONNECTING: "connecting";
readonly OPEN: "open";
readonly CLOSED: "closed";
state: SCClientSocket.States;
getState(): SCClientSocket.States;
readonly AUTHENTICATED: "authenticated";
readonly UNAUTHENTICATED: "unauthenticated";
authState: SCClientSocket.AuthStates;
readonly PENDING: "pending";
pendingReconnect: boolean;
pendingReconnectTimeout: number;
ackTimeout: number;
connectTimeout: number;
pingTimeout: number;
pingTimeoutDisabled: boolean;
channelPrefix: string | null;
disconnectOnUnload: boolean;
authTokenName: string;
connectAttempts: number;
options: SCClientSocket.ClientOptions;
authEngine: SCAuthEngine;
codecEngine: SCServer.SCCodecEngine;
readonly ignoreStatuses: SocketProtocolIgnoreStatuses;
readonly errorStatuses: SocketProtocolErrorStatuses;
getBytesReceived(): number;
deauthenticate(callback?: (err: Error) => void): void;
connect(): void;
open(): void;
disconnect(code?: number, data?: string | object): void;
reconnect(code?: number, data?: string | object): void;
destroy(code?: number, data?: string | object): void;
decodeBase64(encodedString: string): string;
encodeBase64(decodedString: string): string;
getAuthToken(): object | null;
authToken: object | null;
getSignedAuthToken(): string | null;
signedAuthToken: string | null;
// Perform client-initiated authentication by providing an encrypted token string.
authenticate(signedAuthToken: string, callback?: (err: Error, authStatus: SCClientSocket.AuthStatus) => void): void;
decode(message: any): any;
encode(object: any): any;
send(data: any): void;
emit(event: string, ...args: any[]): this;
emit(event: string, data: any, callback?: (err: Error, responseData: any) => void): void;
publish(channelName: string, data: any, callback?: (err: Error, ackData: any) => void): void;
subscribe(channelName: string, options?: SCChannelOptions): SCChannel;
unsubscribe(channelName: string): void;
channel(channelName: string, options?: SCChannelOptions): SCChannel;
destroyChannel(channelName: string): void;
subscriptions(includePending?: boolean): string[];
isSubscribed(channelName: string, includePending?: boolean): boolean;
processPendingSubscriptions(): void;
watch(channelName: string, handler: SCClientSocket.WatcherFunction): void;
unwatch(channelName: string, handler?: SCClientSocket.WatcherFunction): void;
watchers(channelName: string): SCClientSocket.WatcherFunction[];
on(event: string, listener: SCClientSocket.AnyFunction): this;
on(event: "connecting", listener: () => void): this;
on(event: "connect", listener: (status: SCClientSocket.ConnectStatus, processSubscriptions: () => void) => void): this;
on(event: "connectAbort" | "disconnect" | "close", listener: (code: number, data: string | object) => void): this;
on(event: "kickOut", listener: (message: string, channelName: string) => void): this;
on(event: "authenticate", listener: (signedAuthToken: string | null) => void): this;
on(event: "deauthenticate", listener: (oldSignedToken: string | null) => void): this;
on(event: "authStateChange", listener: (stateChangeData: SCClientSocket.AuthStateChangeData) => void): this;
on(event: "removeAuthToken", listener: (oldToken: object | null) => void): this;
on(event: "subscribe" | "subscribeRequest", listener: (channelName: string, subscriptionOptions: SCChannelOptions) => void): this;
on(event: "subscribeStateChange", listener: (stateChangeData: SCClientSocket.SubscribeStateChangeData) => void): this;
on(event: "subscribeFail", listener: (err: Error, channelName: string, subscriptionOptions: SCChannelOptions) => void): this;
on(event: "unsubscribe", listener: (channelName: string) => void): this;
on(event: "error", listener: (err: Error) => void): this;
on(event: "raw", listener: (data: any) => void): this;
on(event: "message", listener: (message: WebSocket.Data) => void): this;
once(event: string, listener: SCClientSocket.AnyFunction): this;
once(event: "connecting", listener: () => void): this;
once(event: "connect", listener: (status: SCClientSocket.ConnectStatus, processSubscriptions: () => void) => void): this;
once(event: "connectAbort" | "disconnect" | "close", listener: (code: number, data: string | object) => void): this;
once(event: "kickOut", listener: (message: string, channelName: string) => void): this;
once(event: "authenticate", listener: (signedAuthToken: string | null) => void): this;
once(event: "deauthenticate", listener: (oldSignedToken: string | null) => void): this;
once(event: "authStateChange", listener: (stateChangeData: SCClientSocket.AuthStateChangeData) => void): this;
once(event: "removeAuthToken", listener: (oldToken: object | null) => void): this;
once(event: "subscribe" | "subscribeRequest", listener: (channelName: string, subscriptionOptions: SCChannelOptions) => void): this;
once(event: "subscribeStateChange", listener: (stateChangeData: SCClientSocket.SubscribeStateChangeData) => void): this;
once(event: "subscribeFail", listener: (err: Error, channelName: string, subscriptionOptions: SCChannelOptions) => void): this;
once(event: "unsubscribe", listener: (channelName: string) => void): this;
once(event: "error", listener: (err: Error) => void): this;
once(event: "raw", listener: (data: any) => void): this;
once(event: "message", listener: (message: WebSocket.Data) => void): this;
off(event?: string, listener?: SCClientSocket.AnyFunction): this;
off(event: "connecting", listener?: () => void): this;
off(event: "connect", listener?: (status: SCClientSocket.ConnectStatus, processSubscriptions: () => void) => void): this;
off(event: "connectAbort" | "disconnect" | "close", listener?: (code: number, data: string | object) => void): this;
off(event: "kickOut", listener?: (message: string, channelName: string) => void): this;
off(event: "authenticate", listener?: (signedAuthToken: string | null) => void): this;
off(event: "deauthenticate", listener?: (oldSignedToken: string | null) => void): this;
off(event: "authStateChange", listener?: (stateChangeData: SCClientSocket.AuthStateChangeData) => void): this;
off(event: "removeAuthToken", listener?: (oldToken: object | null) => void): this;
off(event: "subscribe" | "subscribeRequest", listener?: (channelName: string, subscriptionOptions: SCChannelOptions) => void): this;
off(event: "subscribeStateChange", listener?: (stateChangeData: SCClientSocket.SubscribeStateChangeData) => void): this;
off(event: "subscribeFail", listener?: (err: Error, channelName: string, subscriptionOptions: SCChannelOptions) => void): this;
off(event: "unsubscribe", listener?: (channelName: string) => void): this;
off(event: "error", listener?: (err: Error) => void): this;
off(event: "raw", listener?: (data: any) => void): this;
off(event: "message", listener?: (message: WebSocket.Data) => void): this;
}
export = SCClientSocket;
declare namespace SCClientSocket {
type AnyFunction = (...args: any[]) => any;
interface ClientOptions {
host?: string;
// Defaults to the current host (read from the URL).
hostname?: string;
// Defaults to false.
secure?: boolean;
// Defaults to 80 if !secure otherwise defaults to 443.
port?: number;
// The URL which SC uses to make the initial handshake for the WebSocket. Defaults to '/socketcluster/'.
path?: string;
// A map of key-value pairs which will be used as query parameters for the initial HTTP handshake which will initiate the WebSocket connection.
query?: string | { [key: string]: string };
// (milliseconds) - This is the timeout for getting a response to a SCSocket emit event (when a callback is provided).
ackTimeout?: number;
// (milliseconds)
connectTimeout?: number;
// Whether or not to automatically connect the socket as soon as it is created. Default is true.
autoConnect?: boolean;
// Whether or not to automatically reconnect the socket when it loses the connection.
autoReconnect?: boolean;
// Valid properties are: initialDelay (milliseconds), randomness (milliseconds), multiplier (decimal; default is 1.5) and maxDelay (milliseconds).
autoReconnectOptions?: AutoReconnectOptions;
// Whether or not a client automatically disconnects on page unload. If enabled, the client will disconnect when a user navigates away from the page.
// This can happen when a user closes the tab/window, clicks a link to leave the page, or types a new URL into the address bar. Defaults to true.
disconnectOnUnload?: boolean;
// Turn on/off per-message deflate compression. If this is true, you need to make sure that this property is also set to true on the server-side.
// Note that this option is only relevant when running the client from Node.js. Most modern browsers will automatically use perMessageDeflate so
// you only need to turn it on from the server-side.
perMessageDeflate?: boolean;
// Defaults to true; multiplexing allows you to reuse a socket instead of creating a second socket to the same address.
multiplex?: boolean;
// Defaults to null (0 milliseconds); this property affects channel subscription batching; it determines the period in milliseconds for batching
// multiple subscription requests together. It only affects channels that have the batch option set to true. A value of null or 0 means that all
// subscribe or unsubscribe requests which were made within the same call stack will be batched together. This property was introduced on the
// client-side in SC version 8 (both the client and server versions need to be >= 8.0.0). Note that there is also a separate property with the
// same name which can be configured on the server.
pubSubBatchDuration?: number;
// Whether or not to add a timestamp to the WebSocket handshake request.
timestampRequests?: boolean;
// The query parameter name to use to hold the timestamp.
timestampParam?: string;
// A custom engine to use for storing and loading JWT auth tokens on the client side.
authEngine?: SCAuthEngine | null;
// The name of the JWT auth token (provided to the authEngine - By default this is the localStorage variable name); defaults to 'socketCluster.authToken'.
authTokenName?: string;
// The type to use to represent binary on the client. Defaults to 'arraybuffer'.
binaryType?: string;
// Set this to false during debugging - Otherwise client connection will fail when using self-signed certificates.
rejectUnauthorized?: boolean;
// If you set this to true, any data/objects/arrays that you pass to the client socket will be cloned before being sent/queued up. If the socket
// is disconnected and you emit an event, it will be added to a queue which will be processed upon reconnection. The cloneData option is false
// by default; this means that if you emit/publish an object and that object changes somewhere else in your code before the queue is processed,
// then the changed version of that object will be sent out to the server.
cloneData?: boolean;
// This is true by default. If you set this to false, then the socket will not automatically try to subscribe to pending subscriptions on
// connect - Instead, you will have to manually invoke the processSubscriptions callback from inside the 'connect' event handler on the client side.
// See SCSocket Client API. This gives you more fine-grained control with regards to when pending subscriptions are processed after the socket
// connection is established (or re-established).
autoSubscribeOnConnect?: boolean;
// Lets you set a custom codec engine. This allows you to specify how data gets encoded before being sent over the wire and how it gets decoded
// once it reaches the other side. The codecEngine must be an object which exposes an encode(object) and a decode(encodedData) function.
// The encode function can return any data type - Commonly a string or a Buffer/ArrayBuffer. The decode function needs to return a JavaScript
// object which adheres to the SC protocol. The idea of using a custom codec is that it allows you to compress SC packets in any format you like
// (optimized for any use case) - By decoding these packets back into their original protocol form, SC will be able process them appropriately.
// Note that if you provide a codecEngine when creating a client socket see 'codecEngine', you will need to make sure that the server uses the
// same codec by passing the same engine to `worker.scServer.setCodecEngine(codecEngine)` when your SC worker initializes on the server side
// (see 'setCodecEngine' method here). The default codec engine used by SC is here.
codecEngine?: SCServer.SCCodecEngine | null;
// A prefix to add to the channel names.
channelPrefix?: string | null;
subscriptionRetryOptions?: object | null;
}
interface AutoReconnectOptions {
initialDelay?: number;
randomness?: number;
multiplier?: number;
maxDelay?: number;
}
interface AuthStatus {
isAuthenticated: AuthStates;
authError: Error;
}
interface AuthStateChangeData {
oldState: AuthStates;
newState: AuthStates;
}
interface ConnectStatus {
id: string;
pingTimeout: number;
isAuthenticated: boolean;
authToken?: object;
authError?: Error;
}
interface SubscribeStateChangeData {
channel: string;
oldState: ChannelState;
newState: ChannelState;
subscriptionOptions: SCChannelOptions;
}
type WatcherFunction = (data: any) => void;
type AuthStates = "authenticated" | "unauthenticated";
type States = "connecting" | "open" | "closed";
}

View File

@ -0,0 +1,142 @@
import AGServer = require('socketcluster-server/server');
import WebSocket = require('ws');
import { AGAuthEngine } from './auth';
import { CallIdGenerator, ClientOptions, ProtocolVersions, States } from './clientsocket';
declare class AGTransport {
readonly CONNECTING: 'connecting';
readonly OPEN: 'open';
readonly CLOSED: 'closed';
state: States;
auth: AGAuthEngine;
codec: AGServer.CodecEngine;
options: ClientOptions;
wsOptions?: WebSocket.ClientOptions;
protocolVersion: ProtocolVersions;
connectTimeout: number;
pingTimeout: number;
pingTimeoutDisabled: boolean;
callIdGenerator: CallIdGenerator;
authTokenName: string;
isBufferingBatch: boolean;
socket: WebSocket;
constructor(
authEngine: AGAuthEngine,
codecEngine: AGServer.CodecEngine,
options: ClientOptions,
wsOptions?: WebSocket.ClientOptions,
handlers?: AGTransport.TransportHandlers,
);
uri(): string;
clearAllListeners(): void;
startBatch(): void;
flushBatch(): void;
cancelBatch(): void;
close(code?: number, reason?: string): void;
transmitObject(eventObject: AGTransport.EventObject): number | null;
transmit(event: string, data: any, options: AGTransport.TransmitOptions): Promise<void>;
invokeRaw(
event: string,
data: any,
options: AGTransport.InvokeOptions,
callback?: AGTransport.EventObjectCallback,
): number | null;
invoke<T>(event: string, data: T, options: AGTransport.InvokeOptions): Promise<T>;
cancelPendingResponse(cid: number): void;
decode(message: any): any;
encode(object: any): any;
send(data: any): void;
serializeObject(object: any): any;
sendObject(object: any): void;
}
export = AGTransport;
declare namespace AGTransport {
interface TransportHandlers {
onOpen: (value?: OnOpenValue) => void;
onOpenAbort: (value: OnOpenAbortValue) => void;
onClose: (value: OnCloseValue) => void;
onEvent: (value: OnEventValue) => void;
onError: (value: OnErrorValue) => void;
onInboundInvoke: (value: OnInboundInvokeValue) => void;
onInboundTransmit: (value: OnInboundTransmitValue) => void;
}
interface OnOpenValue {
id: string;
pingTimeout: number;
isAuthenticated: boolean;
authToken: object | null;
}
interface OnOpenAbortValue {
code: number;
reason: string;
}
interface OnCloseValue {
code: number;
reason: string;
}
interface OnEventValue {
event: string;
data: any;
}
interface OnErrorValue {
error: Error;
}
interface OnInboundInvokeValue {
procedure: string;
data: any;
}
interface OnInboundTransmitValue {
event: string;
data: any;
}
interface EventObject {
event: string;
data: any;
callback?: EventObjectCallback;
cid?: number;
timeout?: NodeJS.Timer;
}
interface TransmitOptions {
force?: boolean;
}
interface InvokeOptions {
force?: boolean;
noTimeout?: boolean;
ackTimeout?: number;
}
type EventObjectCallback = (error: Error, eventObject: EventObject) => void;
}

View File

@ -1,99 +1,120 @@
// Adapted from README
import { create, destroy } from "socketcluster-client";
import { ClientOptions, SubscribeStateChangeData } from "socketcluster-client/lib/scclientsocket";
import { SCChannelOptions } from "sc-channel";
import WebSocket = require("ws");
import { create } from 'socketcluster-client';
const secureClientOptions: ClientOptions = {
hostname: "securedomain.com",
secure: true,
port: 443,
rejectUnauthorized: false
};
let socket = create(secureClientOptions);
socket.on("connect", () => {
console.log("CONNECTED");
});
// Listen to an event called 'rand' from the server
socket.on("rand", (num: any) => {
console.log("RANDOM: " + num);
});
const options: ClientOptions = {
path: "/socketcluster/",
const socket = create({
hostname: 'localhost',
port: 8000,
hostname: "127.0.0.1",
});
socket.transmit('foo', 123);
(async () => {
// $ExpectType number
await socket.invoke('myProc', 123);
})();
(async () => {
// Subscribe to a channel.
const myChannel = socket.subscribe('myChannel');
await myChannel.listener('subscribe').once();
// $ExpectType ChannelState
myChannel.state;
})();
(async () => {
const myChannel = socket.channel('myChannel');
myChannel.subscribe();
await myChannel.listener('subscribe').once();
// $ExpectType ChannelState
myChannel.state;
myChannel.transmitPublish('This is a message');
})();
socket.transmitPublish('myChannel', 'This is a message');
(async () => {
const myChannel = socket.channel('myChannel');
try {
await myChannel.invokePublish('This is a message');
} catch (error) {}
try {
const response = await socket.invokePublish('myChannel', 'This is a message');
// $ExpectType string
response.channel;
// $ExpectType string
response.data;
} catch (error) {}
})();
(async () => {
const myChannel = socket.channel('myChannel');
// tslint:disable-next-line: await-promise Bug in tslint: https://github.com/palantir/tslint/issues/3997
for await (const data of myChannel) {
// $ExpectType any
data;
}
})();
(async () => {
const secureOptions = {
hostname: 'securedomain.com',
secure: true,
port: 443,
rejectUnauthorized: false, // Only necessary during debug if using a self-signed certificate
};
const secureSocket = create(secureOptions);
const authStatus = await secureSocket.authenticate('abc');
// $ExpectType Error
authStatus.authError;
// $ExpectType AuthStates
authStatus.isAuthenticated;
})();
const mostOptions = {
path: '/socketcluster/',
port: 8000,
hostname: '127.0.0.1',
autoConnect: true,
secure: false,
rejectUnauthorized: false,
connectTimeout: 10000, // milliseconds
ackTimeout: 10000, // milliseconds
connectTimeout: 10000,
ackTimeout: 10000,
channelPrefix: null,
disconnectOnUnload: true,
multiplex: true,
autoReconnectOptions: {
initialDelay: 10000, // milliseconds
randomness: 10000, // milliseconds
multiplier: 1.5, // decimal
maxDelay: 60000 // milliseconds
initialDelay: 10000,
randomness: 10000,
multiplier: 1.5,
maxDelay: 60000,
},
authEngine: null,
codecEngine: null,
subscriptionRetryOptions: {},
query: {
yourparam: "hello"
}
yourparam: 'hello',
},
};
socket = create(options);
// Check some of the standard events, with normal subscription,
// one-time subscription and unsubscription.
const subscribeListener: (channelName: string, subscriptionOptions: SCChannelOptions) => void = channelname => {
console.log("subscribe:" + channelname);
};
socket.on("subscribe", subscribeListener);
socket.once("subscribe", subscribeListener);
socket.off("subscribe", subscribeListener);
socket.off("subscribe");
create(mostOptions);
const subscribeFailListener: (err: Error, channelName: string, subscriptionOptions: SCChannelOptions) => void = channelname => {
console.log("subscribeFail:" + channelname);
};
socket.on("subscribeFail", subscribeFailListener);
socket.once("subscribeFail", subscribeFailListener);
socket.off("subscribeFail", subscribeFailListener);
socket.off("subscribeFail");
const oldVersionSocket = create({
protocolVersion: 1,
path: '/socketcluster/',
});
const unsubscribeListener: (channelName: string) => void = channelname => {
console.log("unsubscribe:" + channelname);
};
socket.on("unsubscribe", unsubscribeListener);
socket.once("unsubscribe", unsubscribeListener);
socket.off("unsubscribe", unsubscribeListener);
socket.off("unsubscribe");
const subscribeStateChangeListener: (stateChangeData: SubscribeStateChangeData) => void = data => {
console.log("subscribeStateChange:" + JSON.stringify(data));
};
socket.on("subscribeStateChange", subscribeStateChangeListener);
socket.once("subscribeStateChange", subscribeStateChangeListener);
socket.off("subscribeStateChange", subscribeStateChangeListener);
socket.off("subscribeStateChange");
const messageListener: (message: WebSocket.Data) => void = data => {
console.log("message:" + data);
};
socket.on("message", messageListener);
socket.once("message", messageListener);
socket.off("message", messageListener);
socket.off("message");
const channels = socket.channels;
const testChannel = channels["test"];
const channelState = testChannel.getState();
destroy(socket);
// $ExpectType ProtocolVersions
oldVersionSocket.protocolVersion;

View File

@ -2,7 +2,8 @@
"compilerOptions": {
"module": "commonjs",
"lib": [
"es6"
"es6",
"esnext.asynciterable"
],
"noImplicitAny": true,
"noImplicitThis": true,
@ -13,6 +14,11 @@
"../"
],
"types": [],
"paths": {
"sc-channel": [
"sc-channel/v1"
]
},
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
@ -20,4 +26,4 @@
"index.d.ts",
"socketcluster-client-tests.ts"
]
}
}

View File

@ -1,15 +1,14 @@
// Type definitions for socketcluster-server 14.2
// Type definitions for socketcluster-server 15.0
// Project: https://github.com/SocketCluster/socketcluster-server
// Definitions by: Daniel Rose <https://github.com/DanielRose>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.4
import { Server } from "http";
export import SCServer = require("./scserver");
export import SCServerSocket = require("./scserversocket");
export import AGServer = require("./server");
export import AGServerSocket = require("./serversocket");
export function listen(port?: number, options?: SCServer.SCServerOptions, listeningListener?: () => void): SCServer;
export function listen(port?: number, listeningListener?: () => void): SCServer;
export function listen(port?: number, options?: AGServer.AGServerOptions, listeningListener?: () => void): AGServer;
export function listen(port?: number, listeningListener?: () => void): AGServer;
export function attach(server: Server, options?: SCServer.SCServerOptions): SCServer;
export function attach(server: Server, options?: AGServer.AGServerOptions): AGServer;

285
types/socketcluster-server/server.d.ts vendored Normal file
View File

@ -0,0 +1,285 @@
import AsyncStreamEmitter = require('async-stream-emitter');
import { Secret } from 'jsonwebtoken';
import { Server } from 'http';
import { SCAuthEngine } from 'sc-auth';
import WebSocket = require('ws');
import WritableConsumableStream = require('writable-consumable-stream');
import ConsumableStream = require('consumable-stream');
import AGSimpleBroker = require('ag-simple-broker');
import AGServerSocket = require('./serversocket');
declare class AGServer extends AsyncStreamEmitter<any> {
readonly MIDDLEWARE_HANDSHAKE: 'handshake';
readonly MIDDLEWARE_INBOUND_RAW: 'inboundRaw';
readonly MIDDLEWARE_INBOUND: 'inbound';
readonly MIDDLEWARE_OUTBOUND: 'outbound';
readonly SYMBOL_MIDDLEWARE_HANDSHAKE_STREAM: symbol;
options: AGServer.AGServerOptions;
origins: string;
ackTimeout: number;
handshakeTimeout: number;
pingInterval: number;
pingTimeout: number;
pingTimeoutDisabled: boolean;
allowClientPublish: boolean;
perMessageDeflate?: boolean | {};
httpServer: Server;
socketChannelLimit?: number;
protocolVersion: 1 | 2;
strictHandshake: boolean;
brokerEngine: AGSimpleBroker;
middlewareEmitFailures: boolean;
isReady: boolean;
signatureKey?: Secret;
verificationKey?: Secret;
defaultVerificationOptions: {
algorithms?: string[];
};
defaultSignatureOptions: {
expiresIn: number;
algorithm?: string;
};
exchange: AGSimpleBroker.SimpleExchange;
clients: {
[id: string]: AGServerSocket;
};
clientsCount: number;
pendingClients: {
[id: string]: AGServerSocket;
};
pendingClientsCount: number;
wsServer: WebSocket.Server;
constructor(options?: AGServer.AGServerOptions);
emitError(error: Error): void;
emitWarning(warning: Error): void;
emit(eventName: 'error', data: { error: Error }): void;
emit(eventName: 'warning', data: { warning: Error }): void;
emit(eventName: 'handshake', data: { socket: AGServerSocket }): void;
emit(eventName: 'authenticationStateChange', data: AGServer.AuthStateChangeData): void;
emit(eventName: 'authentication', data: AGServer.AuthenticationData): void;
emit(eventName: 'deauthentication', data: AGServer.DeauthenticationData): void;
emit(eventName: 'badSocketAuthToken', data: AGServer.BadSocketAuthTokenData): void;
emit(eventName: 'connection', data: AGServer.ConnectionData): void;
emit(eventName: 'subscription', data: AGServer.SubscriptionData): void;
emit(eventName: 'unsubscription', data: AGServer.UnsubscriptionData): void;
emit(eventName: 'connectionAbort', data: AGServer.ConnectionAbortData): void;
emit(eventName: 'disconnection', data: AGServer.DisconnectionData): void;
emit(eventName: 'closure', data: AGServer.ClosureData): void;
listener(eventName: 'error'): ConsumableStream<{ error: Error }>;
listener(eventName: 'warning'): ConsumableStream<{ warning: Error }>;
listener(eventName: 'handshake'): ConsumableStream<{ socket: AGServerSocket }>;
listener(eventName: 'authenticationStateChange'): ConsumableStream<AGServer.AuthStateChangeData>;
listener(eventName: 'authentication'): ConsumableStream<AGServer.AuthenticationData>;
listener(eventName: 'deauthentication'): ConsumableStream<AGServer.DeauthenticationData>;
listener(eventName: 'badSocketAuthToken'): ConsumableStream<AGServer.BadSocketAuthTokenData>;
listener(eventName: 'connection'): ConsumableStream<AGServer.ConnectionData>;
listener(eventName: 'subscription'): ConsumableStream<AGServer.SubscriptionData>;
listener(eventName: 'unsubscription'): ConsumableStream<AGServer.UnsubscriptionData>;
listener(eventName: 'connectionAbort'): ConsumableStream<AGServer.ConnectionAbortData>;
listener(eventName: 'disconnection'): ConsumableStream<AGServer.DisconnectionData>;
listener(eventName: 'closure'): ConsumableStream<AGServer.ClosureData>;
setMiddleware(type: 'handshake', middleware: AGServer.handshakeMiddlewareFunction): void;
setMiddleware(type: 'inboundRaw', middleware: AGServer.inboundRawMiddlewareFunction): void;
setMiddleware(type: 'inbound', middleware: AGServer.inboundMiddlewareFunction): void;
setMiddleware(type: 'outbound', middleware: AGServer.outboundMiddlewareFunction): void;
removeMiddleware(type: AGServer.Middlewares): void;
hasMiddleware(type: AGServer.Middlewares): boolean;
setAuthEngine(authEngine: SCAuthEngine): void;
auth: SCAuthEngine;
setCodecEngine(codecEngine: AGServer.CodecEngine): void;
codec: AGServer.CodecEngine;
close(): Promise<void>;
getPath(): string;
generateId(): string;
verifyHandshake: WebSocket.VerifyClientCallbackAsync;
}
export = AGServer;
declare namespace AGServer {
interface AuthStateChangeData extends AGServerSocket.StateChangeData {
socket: AGServerSocket;
}
interface AuthenticationData extends AGServerSocket.AuthenticateData {
socket: AGServerSocket;
}
interface DeauthenticationData extends AGServerSocket.DeauthenticateData {
socket: AGServerSocket;
}
interface BadSocketAuthTokenData extends AGServerSocket.BadAuthTokenData {
socket: AGServerSocket;
}
interface ConnectionData extends AGServerSocket.ConnectData {
socket: AGServerSocket;
}
interface SubscriptionData extends AGServerSocket.SubscribeData {
socket: AGServerSocket;
}
interface UnsubscriptionData extends AGServerSocket.UnsubscribeData {
socket: AGServerSocket;
}
interface ConnectionAbortData extends AGServerSocket.ConnectAbortData {
socket: AGServerSocket;
}
interface DisconnectionData extends AGServerSocket.DisconnectData {
socket: AGServerSocket;
}
interface ClosureData extends AGServerSocket.CloseData {
socket: AGServerSocket;
}
interface AGServerOptions {
// An instance of a Node.js HTTP server.
// https://nodejs.org/api/http.html#http_class_http_server
// This option should not be set if the server is created
// with socketClusterServer.attach(...).
httpServer?: Server;
// This can be the name of an npm module or a path to a
// Node.js module to use as the WebSocket server engine.
wsEngine?: any;
// Custom options to pass to the wsEngine when it is being
// instantiated.
wsEngineServerOptions?: WebSocket.ClientOptions;
// The key which SC will use to encrypt/decrypt authTokens,
// defaults to a 256 bits cryptographically random hex
// string. The default JWT algorithm used is 'HS256'.
// If you want to use RSA or ECDSA, you should provide an
// authPrivateKey and authPublicKey instead of authKey.
authKey?: Secret;
// perMessageDeflate compression. Note that this option is
// passed directly to the wsEngine's Server object.
// So if you're using 'ws' as the engine, you can pass an
// object instead of a boolean.
// Note that by default, per-message deflate only kicks in
// for messages > 1024 bytes.
perMessageDeflate?: boolean | {};
// If using an RSA or ECDSA algorithm to sign the
// authToken, you will need to provide an authPrivateKey
// and authPublicKey in PEM format (string or Buffer).
authPrivateKey?: Secret;
authPublicKey?: Secret;
// The default expiry for auth tokens in seconds
authDefaultExpiry?: number;
// The algorithm to use to sign and verify JWT tokens.
authAlgorithm?: string;
// Can be 1 or 2. Version 1 is for maximum backwards
// compatibility with SocketCluster clients.
protocolVersion: 1 | 2;
// In milliseconds - If the socket handshake hasn't been
// completed before this timeout is reached, the new
// connection attempt will be terminated.
handshakeTimeout?: number;
// In milliseconds, the timeout for receiving a response
// when using invoke() or invokePublish().
ackTimeout?: number;
// Origins which are allowed to connect to the server.
origins?: string;
// The maximum number of unique channels which a single
// socket can subscribe to.
socketChannelLimit?: number;
// The interval in milliseconds on which to
// send a ping to the client to check that
// it is still alive.
pingInterval?: number;
// How many milliseconds to wait without receiving a ping
// before closing the socket.
pingTimeout?: number;
// Whether or not an error should be emitted on
// the socket whenever an action is blocked by a
// middleware function
middlewareEmitFailures?: boolean;
// The URL path reserved by SocketCluster clients to
// interact with the server.
path?: string;
// Whether or not clients are allowed to publish messages
// to channels.
allowClientPublish?: boolean;
// Whether or not to batch all socket messages
// for some time immediately after completing
// a handshake. This can be useful in failure-recovery
// scenarios (e.g. batch resubscribe).
batchOnHandshake?: boolean;
// If batchOnHandshake is true, this lets you specify
// How long to enable batching (in milliseconds) following
// a successful socket handshake.
batchOnHandshakeDuration?: number;
// If batchOnHandshake is true, this lets you specify
// the size of each batch in milliseconds.
batchInterval?: number;
// Lets you specify the default cleanup behaviour for
// when a socket becomes disconnected.
// Can be either 'kill' or 'close'. Kill mode means
// that all of the socket's streams will be killed and
// so consumption will stop immediately.
// Close mode means that consumers on the socket will
// be able to finish processing their stream backlogs
// bebfore they are ended.
socketStreamCleanupMode?: 'kill' | 'close';
authVerifyAlgorithms?: string[];
authEngine?: SCAuthEngine;
codecEngine?: CodecEngine;
cloneData?: boolean;
[additionalOptions: string]: any;
}
type handshakeMiddlewareFunction = (stream: WritableConsumableStream<any>) => void;
type inboundRawMiddlewareFunction = (stream: WritableConsumableStream<any>) => void;
type inboundMiddlewareFunction = (stream: WritableConsumableStream<any>) => void;
type outboundMiddlewareFunction = (stream: WritableConsumableStream<any>) => void;
type Middlewares = 'handshake' | 'inboundRaw' | 'inbound' | 'outbound';
interface CodecEngine {
decode: (input: any) => any;
encode: (object: any) => any;
}
}

View File

@ -0,0 +1,265 @@
import { IncomingMessage } from 'http';
import { SCExchange } from 'sc-broker-cluster';
import { SignOptions } from 'jsonwebtoken';
import { SocketProtocolIgnoreStatuses, SocketProtocolErrorStatuses } from 'sc-errors';
import WebSocket = require('ws');
import AsyncStreamEmitter = require('async-stream-emitter');
import WritableConsumableStream = require('writable-consumable-stream');
import DemuxedConsumableStream = require('stream-demux/demuxed-consumable-stream');
import Consumer = require('writable-consumable-stream/consumer');
import ConsumableStream = require('consumable-stream');
import AGSimpleBroker = require('ag-simple-broker');
import AGServer = require('./server');
declare class AGServerSocket extends AsyncStreamEmitter<any> {
readonly CONNECTING: 'connecting';
readonly OPEN: 'open';
readonly CLOSED: 'closed';
readonly AUTHENTICATED: 'authenticated';
readonly UNAUTHENTICATED: 'unauthenticated';
readonly ignoreStatuses: SocketProtocolIgnoreStatuses;
readonly errorStatuses: SocketProtocolErrorStatuses;
id: string;
server: AGServer;
socket: WebSocket;
protocolVersion: number;
request: IncomingMessage;
inboundReceivedMessageCount: number;
inboundProcessedMessageCount: number;
outboundPreparedMessageCount: number;
outboundSentMessageCount: number;
cloneData: boolean;
inboundMessageStream: WritableConsumableStream<any>;
outboundPacketStream: WritableConsumableStream<any>;
middlewareHandshakeStream: WritableConsumableStream<any>;
middlewareInboundRawStream: WritableConsumableStream<any>;
middlewareInboundStream: WritableConsumableStream<any>;
middlewareOutboundStream: WritableConsumableStream<any>;
remoteAddress: string;
remoteFamily: string;
remotePort: number;
forwardedForAddress?: string;
isBufferingBatch: boolean;
isBatching: boolean;
batchOnHandshake: boolean;
batchOnHandshakeDuration: number;
batchInterval: number;
channelSubscriptions: {
[channelName: string]: boolean;
};
channelSubscriptionsCount: number;
exchange: AGSimpleBroker.SimpleExchange;
state: 'connecting' | 'open' | 'closed';
authState: 'authenticated' | 'unauthenticated';
authToken?: AGServerSocket.AuthToken;
signedAuthToken?: string;
constructor(id: string, server: AGServer, socket: WebSocket, protocolVersion: number);
emit(eventName: 'message' | 'raw', data: { message: { data: any; type: string; target: WebSocket } }): void;
emit(eventName: 'error', data: { error: Error }): void;
emit(eventName: 'authStateChange', data: AGServerSocket.StateChangeData): void;
emit(eventName: 'authenticate', data: AGServerSocket.AuthenticateData): void;
emit(eventName: 'authTokenSigned', data: { signedAuthToken: string }): void;
emit(eventName: 'deauthenticate', data: AGServerSocket.DeauthenticateData): void;
emit(eventName: 'badAuthToken', data: AGServerSocket.BadAuthTokenData): void;
emit(eventName: 'connect', data: AGServerSocket.ConnectData): void;
emit(eventName: 'subscribe', data: AGServerSocket.SubscribeData): void;
emit(eventName: 'unsubscribe', data: AGServerSocket.UnsubscribeData): void;
emit(eventName: 'connectAbort', data: AGServerSocket.ConnectAbortData): void;
emit(eventName: 'disconnect', data: AGServerSocket.DisconnectData): void;
emit(eventName: 'close', data: AGServerSocket.CloseData): void;
emit(eventName: 'message' | 'raw'): ConsumableStream<{ message: { data: any; type: string; target: WebSocket } }>;
emit(eventName: 'error'): ConsumableStream<{ error: Error }>;
emit(eventName: 'authStateChange'): ConsumableStream<AGServerSocket.StateChangeData>;
emit(eventName: 'authenticate'): ConsumableStream<AGServerSocket.AuthenticateData>;
emit(eventName: 'authTokenSigned'): ConsumableStream<{ signedAuthToken: string }>;
emit(eventName: 'deauthenticate'): ConsumableStream<AGServerSocket.DeauthenticateData>;
emit(eventName: 'badAuthToken'): ConsumableStream<AGServerSocket.BadAuthTokenData>;
emit(eventName: 'connect'): ConsumableStream<AGServerSocket.ConnectData>;
emit(eventName: 'subscribe'): ConsumableStream<AGServerSocket.SubscribeData>;
emit(eventName: 'unsubscribe'): ConsumableStream<AGServerSocket.UnsubscribeData>;
emit(eventName: 'connectAbort'): ConsumableStream<AGServerSocket.ConnectAbortData>;
emit(eventName: 'disconnect'): ConsumableStream<AGServerSocket.DisconnectData>;
emit(eventName: 'close'): ConsumableStream<AGServerSocket.CloseData>;
emitError(error: Error): void;
getBackpressure(): number;
getInboundBackpressure(): number;
getOutboundBackpressure(): number;
receiver(receiverName: string): DemuxedConsumableStream<any>;
closeReceiver(receiverName: string): void;
closeAllReceivers(): void;
killReceiver(receiverName: string): void;
killAllReceivers(): void;
killReceiverConsumer(consumerId: number): void;
getReceiverConsumerStats(consumerId: number): Consumer.ConsumerStats;
getReceiverConsumerStatsList(receiverName: string): Consumer.ConsumerStats[];
getAllReceiversConsumerStatsList(): Consumer.ConsumerStats[];
getReceiverBackpressure(receiverName: string): number;
getAllReceiversBackpressure(): number;
getReceiverConsumerBackpressure(consumerId: number): number;
hasReceiverConsumer(receiverName: string, consumerId: number): boolean;
hasAnyReceiverConsumer(consumerId: number): boolean;
procedure(procedureName: string): DemuxedConsumableStream<any>;
closeProcedure(procedureName: string): void;
closeAllProcedures(): void;
killProcedure(procedureName: string): void;
killAllProcedures(): void;
killProcedureConsumer(consumerId: number): void;
getProcedureConsumerStats(consumerId: number): Consumer.ConsumerStats;
getProcedureConsumerStatsList(procedureName: string): Consumer.ConsumerStats[];
getAllProceduresConsumerStatsList(): Consumer.ConsumerStats[];
getProcedureBackpressure(procedureName: string): number;
getAllProceduresBackpressure(): number;
getProcedureConsumerBackpressure(consumerId: number): number;
hasProcedureConsumer(procedureName: string, consumerId: number): boolean;
hasAnyProcedureConsumer(consumerId: number): boolean;
getState(): 'connecting' | 'open' | 'closed';
getBytesReceived(): number;
closeAllMiddlewares(): void;
closeInput(): void;
closeOutput(): void;
closeIO(): void;
closeAllStreams(): void;
killAllMiddlewares(): void;
killInput(): void;
killOutput(): void;
killIO(): void;
killAllStreams(): void;
disconnect(code?: number, reason?: string): void;
terminate(): void;
send(data: any, options: { mask?: boolean; binary?: boolean; compress?: boolean; fin?: boolean }): void;
decode(message: any): any;
encode(object: any): any;
startBatch(): void;
flushBatch(): void;
cancelBatch(): void;
startBatching(): void;
stopBatching(): void;
cancelBatching(): void;
serializeObject(object: any): any;
sendObject(object: any): void;
transmit(event: string, data: any, options: any): Promise<void>;
invoke(event: string, data: any, options: any): Promise<any>;
triggerAuthenticationEvents(oldAuthState: 'authenticated' | 'unauthenticated'): void;
getAuthToken(): AGServerSocket.AuthToken;
setAuthToken(data: AGServerSocket.AuthToken, options?: AGServerSocket.AuthTokenOptions): Promise<void>;
isAuthTokenExpired(token: AGServerSocket.AuthToken): boolean;
deauthenticateSelf(): void;
deauthenticate(options?: { rejectOnFailedDelivery: boolean }): Promise<void>;
kickOut(channel?: string, message?: string): any;
subscriptions(): string[];
isSubscribed(channel: string): boolean;
}
export = AGServerSocket;
declare namespace AGServerSocket {
interface AuthToken {
[x: string]: any;
}
interface AuthTokenOptions extends SignOptions {
rejectOnFailedDelivery?: boolean;
}
interface StateChangeData {
oldState: 'authenticated' | 'unauthenticated';
newState: 'authenticated' | 'unauthenticated';
authToken?: AuthToken;
}
interface AuthenticateData {
authToken?: AuthToken;
}
interface DeauthenticateData {
oldAuthToken?: AuthToken;
}
interface BadAuthTokenData {
authError: Error;
signedAuthToken: string;
}
interface ConnectData {
id: string;
pingTimeout: number;
authError?: Error;
isAuthenticated: boolean;
}
interface SubscribeData {
channel: string;
subscriptionOptions: SubscriptionOptions;
}
interface SubscriptionOptions {
channel: string;
waitForAuth?: boolean;
data?: any;
}
interface UnsubscribeData {
channel: string;
}
interface ConnectAbortData {
code: number;
reason: string;
}
interface DisconnectData {
code: number;
reason: string;
}
interface CloseData {
code: number;
reason: string;
}
}

View File

@ -1,120 +1,43 @@
// Adapted from README
// Using with basic http(s) module (example)
import http = require('http');
import socketClusterServer = require('socketcluster-server');
import http = require("http");
import WebSocket = require("ws");
import * as socketClusterServer from "socketcluster-server";
const httpServer = http.createServer();
let agServer = socketClusterServer.attach(httpServer);
let httpServer = http.createServer();
let scServer = socketClusterServer.attach(httpServer);
(async () => {
// Handle new inbound sockets.
// tslint:disable-next-line: await-promise Bug in tslint: https://github.com/palantir/tslint/issues/3997
for await (const { socket } of agServer.listener('connection')) {
(async () => {
// Set up a loop to handle and respond to RPCs for a procedure.
// tslint:disable-next-line: await-promise Bug in tslint: https://github.com/palantir/tslint/issues/3997
for await (const req of socket.procedure('customProc')) {
if (req.data.bad) {
const error = new Error('Server failed to execute the procedure');
error.name = 'BadCustomError';
req.error(error);
} else {
req.end('Success');
}
}
})();
scServer.on("connection", socket => {
// ... Handle new socket connections here
});
(async () => {
// Set up a loop to handle remote transmitted events.
// tslint:disable-next-line: await-promise Bug in tslint: https://github.com/palantir/tslint/issues/3997
for await (const data of socket.receiver('customRemoteEvent')) {
// $ExpectType any
data;
}
})();
}
})();
httpServer.listen(8000);
// Using with Express (example)
import serveStatic = require("serve-static");
import path = require("path");
import express = require("express");
const app = express();
app.use(serveStatic(path.resolve(__dirname, "public")));
httpServer = http.createServer();
// Attach express to our httpServer
httpServer.on("request", app);
// Attach socketcluster-server to our httpServer
scServer = socketClusterServer.attach(httpServer);
scServer.on("connection", socket => {
// ... Handle new socket connections here
});
httpServer.listen(8000);
// Tests of the server-side socket
scServer.on("connection", socket => {
// Check the standard events, with normal subscription,
// one-time subscription and unsubscription.
const errorListener: (error: Error) => void = err => {
console.log(err);
};
socket.on("error", errorListener);
socket.once("error", errorListener);
socket.off("error", errorListener);
socket.off("error");
const messageListener: (message: WebSocket.Data) => void = message => {
console.log(message);
};
socket.on("message", messageListener);
socket.once("message", messageListener);
socket.off("message", messageListener);
socket.off("message");
socket.on("raw", messageListener);
socket.once("raw", messageListener);
socket.off("raw", messageListener);
socket.off("raw");
const closeListener: (code: number, data?: any) => void = (code, data) => {
console.log(`${code} ${data}`);
};
socket.on("connectAbort", closeListener);
socket.once("connectAbort", closeListener);
socket.off("connectAbort", closeListener);
socket.off("connectAbort");
socket.on("disconnect", closeListener);
socket.once("disconnect", closeListener);
socket.off("disconnect", closeListener);
socket.off("disconnect");
socket.on("close", closeListener);
socket.once("close", closeListener);
socket.off("close", closeListener);
socket.off("close");
const authStateChangeListener: (stateChangeData: socketClusterServer.SCServerSocket.StateChangeData) => void = data => {
console.log(data);
};
socket.on("authStateChange", authStateChangeListener);
socket.once("authStateChange", authStateChangeListener);
socket.off("authStateChange", authStateChangeListener);
socket.off("authStateChange");
const authenticateListener: (authToken?: socketClusterServer.SCServer.AuthToken) => void = authToken => {
console.log(authToken);
};
socket.on("authenticate", authenticateListener);
socket.once("authenticate", authenticateListener);
socket.off("authenticate", authenticateListener);
socket.off("authenticate");
const deauthenticateListener: (oldToken?: socketClusterServer.SCServer.AuthToken) => void = oldToken => {
console.log(oldToken);
};
socket.on("deauthenticate", deauthenticateListener);
socket.once("deauthenticate", deauthenticateListener);
socket.off("deauthenticate", deauthenticateListener);
socket.off("deauthenticate");
// Check custom events, with normal subscription,
// one-time subscription and unsubscription.
const customEventListener: (data?: any) => void = data => {
console.log(data);
};
socket.on("custom-event", customEventListener);
socket.once("custom-event", customEventListener);
socket.off("custom-event", customEventListener);
socket.off("custom-event");
agServer = socketClusterServer.attach(httpServer, {
protocolVersion: 1,
path: '/socketcluster/',
});

View File

@ -2,7 +2,8 @@
"compilerOptions": {
"module": "commonjs",
"lib": [
"es6"
"es6",
"esnext.asynciterable"
],
"noImplicitAny": true,
"noImplicitThis": true,
@ -13,6 +14,11 @@
"../"
],
"types": [],
"paths": {
"sc-channel": [
"sc-channel/v1"
]
},
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
@ -20,4 +26,4 @@
"index.d.ts",
"socketcluster-server-tests.ts"
]
}
}

View File

@ -0,0 +1,15 @@
// Type definitions for socketcluster-server 14.2
// Project: https://github.com/SocketCluster/socketcluster-server
// Definitions by: Daniel Rose <https://github.com/DanielRose>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.4
import { Server } from "http";
export import SCServer = require("./scserver");
export import SCServerSocket = require("./scserversocket");
export function listen(port?: number, options?: SCServer.SCServerOptions, listeningListener?: () => void): SCServer;
export function listen(port?: number, listeningListener?: () => void): SCServer;
export function attach(server: Server, options?: SCServer.SCServerOptions): SCServer;

View File

@ -0,0 +1,120 @@
// Adapted from README
// Using with basic http(s) module (example)
import http = require("http");
import WebSocket = require("ws");
import * as socketClusterServer from "socketcluster-server";
let httpServer = http.createServer();
let scServer = socketClusterServer.attach(httpServer);
scServer.on("connection", socket => {
// ... Handle new socket connections here
});
httpServer.listen(8000);
// Using with Express (example)
import serveStatic = require("serve-static");
import path = require("path");
import express = require("express");
const app = express();
app.use(serveStatic(path.resolve(__dirname, "public")));
httpServer = http.createServer();
// Attach express to our httpServer
httpServer.on("request", app);
// Attach socketcluster-server to our httpServer
scServer = socketClusterServer.attach(httpServer);
scServer.on("connection", socket => {
// ... Handle new socket connections here
});
httpServer.listen(8000);
// Tests of the server-side socket
scServer.on("connection", socket => {
// Check the standard events, with normal subscription,
// one-time subscription and unsubscription.
const errorListener: (error: Error) => void = err => {
console.log(err);
};
socket.on("error", errorListener);
socket.once("error", errorListener);
socket.off("error", errorListener);
socket.off("error");
const messageListener: (message: WebSocket.Data) => void = message => {
console.log(message);
};
socket.on("message", messageListener);
socket.once("message", messageListener);
socket.off("message", messageListener);
socket.off("message");
socket.on("raw", messageListener);
socket.once("raw", messageListener);
socket.off("raw", messageListener);
socket.off("raw");
const closeListener: (code: number, data?: any) => void = (code, data) => {
console.log(`${code} ${data}`);
};
socket.on("connectAbort", closeListener);
socket.once("connectAbort", closeListener);
socket.off("connectAbort", closeListener);
socket.off("connectAbort");
socket.on("disconnect", closeListener);
socket.once("disconnect", closeListener);
socket.off("disconnect", closeListener);
socket.off("disconnect");
socket.on("close", closeListener);
socket.once("close", closeListener);
socket.off("close", closeListener);
socket.off("close");
const authStateChangeListener: (stateChangeData: socketClusterServer.SCServerSocket.StateChangeData) => void = data => {
console.log(data);
};
socket.on("authStateChange", authStateChangeListener);
socket.once("authStateChange", authStateChangeListener);
socket.off("authStateChange", authStateChangeListener);
socket.off("authStateChange");
const authenticateListener: (authToken?: socketClusterServer.SCServer.AuthToken) => void = authToken => {
console.log(authToken);
};
socket.on("authenticate", authenticateListener);
socket.once("authenticate", authenticateListener);
socket.off("authenticate", authenticateListener);
socket.off("authenticate");
const deauthenticateListener: (oldToken?: socketClusterServer.SCServer.AuthToken) => void = oldToken => {
console.log(oldToken);
};
socket.on("deauthenticate", deauthenticateListener);
socket.once("deauthenticate", deauthenticateListener);
socket.off("deauthenticate", deauthenticateListener);
socket.off("deauthenticate");
// Check custom events, with normal subscription,
// one-time subscription and unsubscription.
const customEventListener: (data?: any) => void = data => {
console.log(data);
};
socket.on("custom-event", customEventListener);
socket.once("custom-event", customEventListener);
socket.off("custom-event", customEventListener);
socket.off("custom-event");
});

View File

@ -0,0 +1,37 @@
{
"compilerOptions": {
"module": "commonjs",
"lib": [
"es6"
],
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"baseUrl": "../../",
"typeRoots": [
"../../"
],
"types": [],
"paths": {
"socketcluster-server": [
"socketcluster-server/v14"
],
"socketcluster-server/*": [
"socketcluster-server/v14/*"
],
"sc-broker-cluster": [
"sc-broker-cluster/v6"
],
"sc-channel": [
"sc-channel/v1"
]
},
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.d.ts",
"socketcluster-server-tests.ts"
]
}

View File

@ -0,0 +1,3 @@
{
"extends": "dtslint/dt.json"
}

View File

@ -13,6 +13,17 @@
"../"
],
"types": [],
"paths": {
"socketcluster-server": [
"socketcluster-server/v14"
],
"sc-broker-cluster": [
"sc-broker-cluster/v6"
],
"sc-channel": [
"sc-channel/v1"
]
},
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
@ -20,4 +31,4 @@
"index.d.ts",
"socketcluster-tests.ts"
]
}
}

View File

@ -0,0 +1,13 @@
import ConsumableStream = require('consumable-stream');
import StreamDemux = require('.');
declare class DemuxedConsumableStream<T> extends ConsumableStream<T> {
name: string;
constructor(streamDemux: StreamDemux<T>, name: string);
createConsumer(timeout?: number): ConsumableStream.Consumer<T>;
}
export = DemuxedConsumableStream;

38
types/stream-demux/index.d.ts vendored Normal file
View File

@ -0,0 +1,38 @@
// Type definitions for stream-demux 7.0
// Project: https://github.com/SocketCluster/stream-demux
// Definitions by: Daniel Rose <https://github.com/DanielRose>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
import Consumer = require('writable-consumable-stream/consumer');
import DemuxedConsumableStream = require('./demuxed-consumable-stream');
declare class StreamDemux<T> {
write(streamName: string, value: T): void;
close(streamName: string, value?: T): void;
closeAll(value?: T): void;
writeToConsumer(consumerId: number, value: T): void;
closeConsumer(consumerId: number, value: T): void;
kill(streamName: string, value?: T): void;
killAll(value?: T): void;
killConsumer(consumerId: number, value?: T): void;
getBackpressure(streamName: string): number;
getBackpressureAll(): number;
getConsumerBackpressure(consumerId: number): number;
hasConsumer(streamName: string, consumerId: number): boolean;
hasConsumerAll(consumerId: number): boolean;
getConsumerStats(consumerId: number): Consumer.ConsumerStats;
getConsumerStatsList(streamName: string): Consumer.ConsumerStats[];
getConsumerStatsListAll(): Consumer.ConsumerStats[];
createConsumer(streamName: string, timeout?: number): Consumer<T>;
stream(streamName: string): DemuxedConsumableStream<T>;
}
export = StreamDemux;

View File

@ -0,0 +1,85 @@
import StreamDemux = require('stream-demux');
const demux = new StreamDemux<string>();
(async () => {
// Consume data from 'abc' stream.
const substream = demux.stream('abc');
// tslint:disable-next-line: await-promise Bug in tslint: https://github.com/palantir/tslint/issues/3997
for await (const packet of substream) {
console.log('ABC:', packet);
}
})();
(async () => {
// Consume data from 'def' stream.
const substream = demux.stream('def');
// tslint:disable-next-line: await-promise Bug in tslint: https://github.com/palantir/tslint/issues/3997
for await (const packet of substream) {
// $ExpectType string
packet;
console.log('DEF:', packet);
}
})();
(async () => {
// Consume data from 'def' stream.
// Can also work with a while loop for older environments.
// Can have multiple loops consuming the same stream at
// the same time.
// Note that you can optionally pass a number n to the
// createConsumer(n) method to force the iteration to
// timeout after n milliseconds of innactivity.
const consumer = demux.stream('def').createConsumer();
while (true) {
const packet = await consumer.next();
if (packet.done) break;
// $ExpectType string
packet.value;
console.log('DEF (while loop):', packet.value);
}
})();
(async () => {
for (let i = 0; i < 10; i++) {
await wait(10);
demux.write('abc', 'message-abc-' + i);
demux.write('def', 'message-def-' + i);
}
demux.close('abc');
demux.close('def');
})();
// Utility function for using setTimeout() with async/await.
function wait(duration: number) {
return new Promise(resolve => {
setTimeout(() => {
resolve();
}, duration);
});
}
// Log the next received packet from the abc stream.
(async () => {
// The returned promise never times out.
// $ExpectType string
const packet = await demux.stream('abc').once();
console.log('Packet:', packet);
})();
// Same as above, except with a timeout of 10 seconds.
(async () => {
try {
// $ExpectType string
const packet = await demux.stream('abc').once(10000);
console.log('Packet:', packet);
} catch (err) {
// If no packets are written to the 'abc' stream before
// the timeout, an error will be thrown and handled here.
// The err.name property will be 'TimeoutError'.
console.log('Error:', err);
}
})();

View File

@ -0,0 +1,24 @@
{
"compilerOptions": {
"module": "commonjs",
"lib": [
"es6",
"esnext.asynciterable"
],
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"baseUrl": "../",
"typeRoots": [
"../"
],
"types": [],
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.d.ts",
"stream-demux-tests.ts"
]
}

View File

@ -0,0 +1,3 @@
{
"extends": "dtslint/dt.json"
}

View File

@ -0,0 +1,43 @@
import ConsumableStream = require('consumable-stream');
import WritableConsumableStream = require('.');
declare class Consumer<T> implements ConsumableStream.Consumer<T> {
id: number;
stream: WritableConsumableStream<T>;
currentNode: Consumer.Node<T>;
timeout: number;
constructor(stream: WritableConsumableStream<T>, id: number, startNode: Consumer.Node<T>, timeout: number);
getStats(): Consumer.ConsumerStats;
resetBackpressure(): void;
releaseBackpressure(packet: any): void;
getBackpressure(): number;
write(packet: any): void;
kill(value?: any): void;
next(): Promise<IteratorResult<T>>;
return(): {};
}
export = Consumer;
declare namespace Consumer {
interface ConsumerStats {
id: number;
backpressure: number;
timeout?: number;
}
interface Node<T> {
next: Node<T> | null;
data: {
value: T;
done: boolean;
};
}
}

View File

@ -0,0 +1,36 @@
// Type definitions for writable-consumable-stream 1.1
// Project: https://github.com/SocketCluster/writable-consumable-stream
// Definitions by: Daniel Rose <https://github.com/DanielRose>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
import ConsumableStream = require('consumable-stream');
import Consumer = require('./consumer');
declare class WritableConsumableStream<T> extends ConsumableStream<T> {
nextConsumerId: number;
write(value: T): void;
close(value?: T): void;
writeToConsumer(consumerId: number, value: T): void;
closeConsumer(consumerId: number, value?: T): void;
kill(value?: T): void;
killConsumer(consumerId: number, value?: T): void;
getBackpressure(): number;
getConsumerBackpressure(consumerId: number): number;
hasConsumer(consumerId: number): boolean;
setConsumer(consumerId: number, consumer: Consumer<T>): void;
removeConsumer(consumerId: number): void;
getConsumerStats(consumerId: number): Consumer.ConsumerStats;
getConsumerStatsList(): Consumer.ConsumerStats[];
createConsumer(timeout?: number): Consumer<T>;
}
export = WritableConsumableStream;

View File

@ -0,0 +1,24 @@
{
"compilerOptions": {
"module": "commonjs",
"lib": [
"es6",
"esnext.asynciterable"
],
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"baseUrl": "../",
"typeRoots": [
"../"
],
"types": [],
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.d.ts",
"writable-consumable-stream-tests.ts"
]
}

View File

@ -0,0 +1,3 @@
{
"extends": "dtslint/dt.json"
}

View File

@ -0,0 +1,49 @@
import WritableConsumableStream = require('writable-consumable-stream');
const consumableStream = new WritableConsumableStream<string>();
async function consumeAsyncIterable1(asyncIterable: WritableConsumableStream<string>) {
// Consume iterable data asynchronously.
// tslint:disable-next-line: await-promise Bug in tslint: https://github.com/palantir/tslint/issues/3997
for await (const packet of asyncIterable) {
console.log('Packet:', packet);
}
}
consumeAsyncIterable1(consumableStream);
setInterval(() => {
// Write data to the stream asynchronously,
consumableStream.write(`Timestamp: ${Date.now()}`);
}, 100);
async function consumeAsyncIterable2(asyncIterable: WritableConsumableStream<string>) {
// Consume iterable data asynchronously.
// Works in older environments.
const asyncIterator = asyncIterable.createConsumer();
while (true) {
const packet = await asyncIterator.next();
if (packet.done) break;
console.log('Packet:', packet.value);
}
}
consumeAsyncIterable2(consumableStream);
setInterval(() => {
// Write data to the stream asynchronously,
consumableStream.write(`Timestamp: ${Date.now()}`);
}, 100);
setInterval(() => {
// Write data to the stream asynchronously,
consumableStream.write(`Timestamp: ${Date.now()}`);
}, 100);
(async () => {
const data = await consumableStream.once();
console.log(data);
})();
setInterval(() => {
// Write data to the stream asynchronously,
consumableStream.write(`Timestamp: ${Date.now()}`);
}, 100);