Compare commits

..

48 Commits

Author SHA1 Message Date
870f37d403 1.0.73 2019-09-10 01:19:10 +02:00
64c4b91678 fix(core): update 2019-09-10 01:19:10 +02:00
f3e13292d8 1.0.72 2019-09-10 00:39:18 +02:00
7e1c405cb1 fix(core): update 2019-09-10 00:39:18 +02:00
d1b4672eff 1.0.71 2019-09-10 00:29:08 +02:00
0dd9fee52b fix(core): update 2019-09-10 00:29:08 +02:00
37e1ee7970 1.0.70 2019-09-01 21:34:01 +02:00
bd0bb3acf5 fix(core): update 2019-09-01 21:34:01 +02:00
f60497474e 1.0.69 2019-09-01 21:27:45 +02:00
1d84cefa84 fix(core): update 2019-09-01 21:27:45 +02:00
6792acd533 1.0.68 2019-09-01 18:22:44 +02:00
9397d89cf5 fix(core): update 2019-09-01 18:22:44 +02:00
37cf4a91f4 1.0.67 2019-09-01 17:04:25 +02:00
52db86c929 fix(core): update 2019-09-01 17:04:25 +02:00
e8f09c1b7a 1.0.66 2019-09-01 17:01:26 +02:00
79edea873f fix(core): update 2019-09-01 17:01:26 +02:00
97666a623d 1.0.65 2019-09-01 16:54:36 +02:00
ef61ea9ad7 fix(core): update 2019-09-01 16:54:36 +02:00
9c1504ef02 1.0.64 2019-08-13 18:43:33 +02:00
e8f2e04d1c fix(core): update 2019-08-13 18:43:33 +02:00
e12aa7e961 1.0.63 2019-08-13 18:41:28 +02:00
857b7cd010 fix(core): update 2019-08-13 18:41:27 +02:00
e100dea160 1.0.62 2019-08-13 18:16:17 +02:00
e8e87fcdba fix(core): update 2019-08-13 18:16:16 +02:00
0d18b11721 1.0.61 2019-08-13 18:06:14 +02:00
eaaefddbe3 fix(core): update 2019-08-13 18:06:13 +02:00
8c6946ddb6 1.0.60 2019-08-13 15:55:01 +02:00
3a7ebcdd80 fix(core): update 2019-08-13 15:55:01 +02:00
ec2afbfd55 1.0.59 2019-08-13 15:48:21 +02:00
89feeca735 fix(core): update 2019-08-13 15:48:20 +02:00
c4261765ec 1.0.58 2019-08-13 13:04:49 +02:00
33fe6bcd41 fix(core): update 2019-08-13 13:04:49 +02:00
1baf1c318c 1.0.57 2019-08-12 17:23:11 +02:00
051aba3299 fix(core): update 2019-08-12 17:23:10 +02:00
7998d79b13 1.0.56 2019-08-12 15:12:32 +02:00
6838a8729a fix(core): update 2019-08-12 15:12:31 +02:00
67f4e33ca0 1.0.55 2019-08-12 15:10:40 +02:00
8a8277ae9f fix(core): update 2019-08-12 15:10:40 +02:00
ff9cb9132c 1.0.54 2019-08-12 14:59:38 +02:00
f4ce784a59 fix(core): update 2019-08-12 14:59:37 +02:00
b34be4dcba 1.0.53 2019-07-30 10:31:36 +02:00
6cc69efe2d fix(core): update 2019-07-30 10:31:35 +02:00
8c30f294bc 1.0.52 2019-06-11 03:06:18 +02:00
228eb791b7 fix(core): update 2019-06-11 03:06:17 +02:00
057476ae66 1.0.51 2019-06-10 17:46:07 +02:00
cb80e4dc2e fix(core): update 2019-06-10 17:46:06 +02:00
8410e09a4d 1.0.50 2019-06-07 11:49:10 +02:00
eb04abddbf fix(core): update 2019-06-07 11:49:10 +02:00
24 changed files with 816 additions and 486 deletions

View File

@ -1,5 +1,5 @@
# gitzone ci_default # gitzone ci_default
image: hosttoday/ht-docker-node:npmci image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
cache: cache:
paths: paths:
@ -38,19 +38,7 @@ snyk:
# test stage # test stage
# ==================== # ====================
testLTS: testStable:
stage: test
script:
- npmci npm prepare
- npmci node install lts
- npmci npm install
- npmci npm test
coverage: /\d+.?\d+?\%\s*coverage/
tags:
- docker
- notpriv
testSTABLE:
stage: test stage: test
script: script:
- npmci npm prepare - npmci npm prepare
@ -60,12 +48,24 @@ testSTABLE:
coverage: /\d+.?\d+?\%\s*coverage/ coverage: /\d+.?\d+?\%\s*coverage/
tags: tags:
- docker - docker
- priv
testBuild:
stage: test
script:
- npmci npm prepare
- npmci node install lts
- npmci npm install
- npmci command npm run build
coverage: /\d+.?\d+?\%\s*coverage/
tags:
- docker
- notpriv - notpriv
release: release:
stage: release stage: release
script: script:
- npmci node install stable - npmci node install lts
- npmci npm publish - npmci npm publish
only: only:
- tags - tags
@ -98,7 +98,9 @@ trigger:
- notpriv - notpriv
pages: pages:
image: hosttoday/ht-docker-node:npmci image: hosttoday/ht-docker-dbase:npmci
services:
- docker:stable-dind
stage: metadata stage: metadata
script: script:
- npmci command npm install -g @gitzone/tsdoc - npmci command npm install -g @gitzone/tsdoc

29
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,29 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "current file",
"type": "node",
"request": "launch",
"args": [
"${relativeFile}"
],
"runtimeArgs": ["-r", "@gitzone/tsrun"],
"cwd": "${workspaceRoot}",
"protocol": "inspector",
"internalConsoleOptions": "openOnSessionStart"
},
{
"name": "test.ts",
"type": "node",
"request": "launch",
"args": [
"test/test.ts"
],
"runtimeArgs": ["-r", "@gitzone/tsrun"],
"cwd": "${workspaceRoot}",
"protocol": "inspector",
"internalConsoleOptions": "openOnSessionStart"
}
]
}

630
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "@pushrocks/smartuniverse", "name": "@pushrocks/smartuniverse",
"version": "1.0.49", "version": "1.0.73",
"private": false, "private": false,
"description": "messaging service for your micro services", "description": "messaging service for your micro services",
"main": "dist/index.js", "main": "dist/index.js",
@ -14,27 +14,28 @@
"format": "(gitzone format)" "format": "(gitzone format)"
}, },
"devDependencies": { "devDependencies": {
"@gitzone/tsbuild": "^2.1.11", "@gitzone/tsbuild": "^2.1.17",
"@gitzone/tstest": "^1.0.24", "@gitzone/tstest": "^1.0.24",
"@pushrocks/tapbundle": "^3.0.9", "@pushrocks/tapbundle": "^3.0.13",
"@types/node": "^12.0.6", "@types/node": "^12.7.4",
"tslint": "^5.17.0", "tslint": "^5.19.0",
"tslint-config-prettier": "^1.18.0" "tslint-config-prettier": "^1.18.0"
}, },
"peerDependencies": { "peerDependencies": {
"rxjs": "*" "rxjs": "*"
}, },
"dependencies": { "dependencies": {
"@pushrocks/lik": "^3.0.5", "@pushrocks/lik": "^3.0.11",
"@pushrocks/smartdelay": "^2.0.3", "@pushrocks/smartdelay": "^2.0.3",
"@pushrocks/smartexpress": "^3.0.21", "@pushrocks/smartexpress": "^3.0.40",
"@pushrocks/smartfile": "^7.0.2", "@pushrocks/smartfile": "^7.0.4",
"@pushrocks/smarthash": "^2.0.4", "@pushrocks/smarthash": "^2.0.6",
"@pushrocks/smartlog": "^2.0.19",
"@pushrocks/smartpromise": "^3.0.2", "@pushrocks/smartpromise": "^3.0.2",
"@pushrocks/smartrequest": "^1.1.15", "@pushrocks/smartrequest": "^1.1.27",
"@pushrocks/smartrx": "^2.0.3", "@pushrocks/smartrx": "^2.0.3",
"@pushrocks/smartsocket": "^1.1.37", "@pushrocks/smartsocket": "^1.1.49",
"@pushrocks/smarttime": "^3.0.7", "@pushrocks/smarttime": "^3.0.12",
"@pushrocks/smartunique": "^3.0.1" "@pushrocks/smartunique": "^3.0.1"
}, },
"files": [ "files": [
@ -42,6 +43,7 @@
"ts_web/*", "ts_web/*",
"dist/*", "dist/*",
"dist_web/*", "dist_web/*",
"dist_ts_web/*",
"assets/*", "assets/*",
"cli.js", "cli.js",
"npmextra.json", "npmextra.json",

View File

@ -50,6 +50,6 @@ All your microservices represents clients in the universe that may talk to each
For further information read the linked docs at the top of this readme. For further information read the linked docs at the top of this readme.
> MIT licensed | **©** [Lossless GmbH](https://lossless.gmbh) > MIT licensed | **©** [Lossless GmbH](https://lossless.gmbh)
| By using this npm module you agree to our [privacy policy](https://lossless.gmbH/privacy.html) | By using this npm module you agree to our [privacy policy](https://lossless.gmbH/privacy)
[![repo-footer](https://pushrocks.gitlab.io/assets/repo-footer.svg)](https://maintainedby.lossless.com) [![repo-footer](https://lossless.gitlab.io/publicrelations/repofooter.svg)](https://maintainedby.lossless.com)

View File

@ -15,7 +15,7 @@ const testServerData = {
const testChannelData = { const testChannelData = {
channelName: 'awesomeTestChannel', channelName: 'awesomeTestChannel',
channelPass: 'awesomeChannelPAss' channelPass: 'awesomeChannelPass'
}; };
tap.test('first test', async () => { tap.test('first test', async () => {
@ -37,22 +37,25 @@ tap.test('create smartuniverse client', async () => {
}); });
tap.test('should add a channel to the universe', async () => { tap.test('should add a channel to the universe', async () => {
await testUniverse.addChannel(testChannelData.channelName, testChannelData.channelPass); testUniverse.addChannel(testChannelData.channelName, testChannelData.channelPass);
}); });
tap.test('should add the same channel to the client universe in the same way', async () => { tap.test('should add the same channel to the client universe in the same way', async () => {
await testClientUniverse.addChannel(testChannelData.channelName, testChannelData.channelPass); testClientUniverse.addChannel(testChannelData.channelName, testChannelData.channelPass);
});
tap.test('should start the ClientUniverse', async () => {
await testClientUniverse.start();
}); });
tap.test('should get a observable correctly', async () => { tap.test('should get a observable correctly', async () => {
testClientChannel = await testClientUniverse.getChannel(testChannelData.channelName); testClientChannel = testClientUniverse.getChannel(testChannelData.channelName);
expect(testClientChannel).to.be.instanceof(smartuniverse.ClientUniverseChannel); expect(testClientChannel).to.be.instanceof(smartuniverse.ClientUniverseChannel);
}); });
tap.test('should send a message correctly', async () => { tap.test('should send a message correctly', async () => {
await testClientUniverse.sendMessage({ await testClientUniverse.getChannel(testChannelData.channelName).sendMessage({
messageText: 'hello', messageText: 'hello'
targetChannelName: testChannelData.channelName
}); });
}); });
@ -71,7 +74,7 @@ tap.test('a second client should be able to subscibe', async () => {
tap.test('should receive a message correctly', async () => {}); tap.test('should receive a message correctly', async () => {});
tap.test('should disconnect the client correctly', async () => { tap.test('should disconnect the client correctly', async () => {
testClientUniverse.close(); await testClientUniverse.stop();
}); });
tap.test('should end the server correctly', async tools => { tap.test('should end the server correctly', async tools => {

View File

@ -1,6 +1,7 @@
// Client classes // Client classes
export * from './smartuniverse.classes.clientuniverse'; export * from './smartuniverse.classes.clientuniverse';
export * from './smartuniverse.classes.clientuniversechannel'; export * from './smartuniverse.classes.clientuniversechannel';
export * from './smartuniverse.classes.clientuniversemessage';
// Server classes // Server classes
export * from './smartuniverse.classes.universe'; export * from './smartuniverse.classes.universe';

View File

@ -1,4 +1,5 @@
export * from './http.interfaces'; export * from './http.interfaces';
export * from './socketfunctionrequests';
export * from './universechannel.interfaces'; export * from './universechannel.interfaces';
export * from './universemessage.interfaces'; export * from './universemessage.interfaces';
export * from './universeactions.interfaces'; export * from './universeactions.interfaces';

View File

@ -0,0 +1,20 @@
import * as interfaces from './index';
export interface ISocketRequest_SubscribeChannel {
method: 'subscribeChannel';
request: {
name: string;
passphrase: string;
};
response: {
subscriptionStatus: 'subscribed' | 'unsubscribed'
};
}
export interface ISocketRequest_ProcessMessage {
method: 'processMessage';
request: interfaces.IUniverseMessage;
response: {
messageStatus: 'ok' | 'channel not found'
};
}

View File

@ -1,12 +1,9 @@
export type IServerCallActions = 'subscribe' | 'sendmessage' | 'unsubscribe'; export type IServerCallActions =
| 'channelSubscription'
| 'processMessage'
| 'channelUnsubscribe'
| 'terminateConnection';
/**
* the interface for a subscription
*/
export interface IServerCallSubscribeActionPayload {
name: string;
passphrase: string;
}
export interface IServerUnsubscribeActionPayload { export interface IServerUnsubscribeActionPayload {
name: string; name: string;

View File

@ -2,9 +2,11 @@ export interface IMessageCreator {
messageText: string; messageText: string;
payload?: string | number | any; payload?: string | number | any;
payloadStringType?: 'Buffer' | 'string' | 'object'; payloadStringType?: 'Buffer' | 'string' | 'object';
targetChannelName: string;
} }
/**
* A universe
*/
export interface IUniverseMessage extends IMessageCreator { export interface IUniverseMessage extends IMessageCreator {
id: string; id: string;
/** /**
@ -12,4 +14,5 @@ export interface IUniverseMessage extends IMessageCreator {
*/ */
timestamp: number; timestamp: number;
passphrase: string; passphrase: string;
targetChannelName: string;
} }

View File

@ -7,7 +7,7 @@ import * as url from 'url';
import * as interfaces from './interfaces'; import * as interfaces from './interfaces';
import { ClientUniverseChannel, UniverseMessage } from './'; import { ClientUniverseChannel, ClientUniverseMessage } from './';
import { ClientUniverseCache } from './smartuniverse.classes.clientuniversecache'; import { ClientUniverseCache } from './smartuniverse.classes.clientuniversecache';
export interface IClientOptions { export interface IClientOptions {
@ -21,9 +21,7 @@ export interface IClientOptions {
export class ClientUniverse { export class ClientUniverse {
public options; public options;
public smartsocketClient: plugins.smartsocket.SmartsocketClient; public smartsocketClient: plugins.smartsocket.SmartsocketClient;
public observableIntake: plugins.smartrx.ObservableIntake<UniverseMessage>; public observableIntake: plugins.smartrx.ObservableIntake<ClientUniverseMessage>;
public channelStore = new Objectmap<ClientUniverseChannel>();
public clientUniverseCache = new ClientUniverseCache(); public clientUniverseCache = new ClientUniverseCache();
constructor(optionsArg: IClientOptions) { constructor(optionsArg: IClientOptions) {
@ -34,15 +32,20 @@ export class ClientUniverse {
* adds a channel to the channelcache * adds a channel to the channelcache
* TODO: verify channel before adding it to the channel cache * TODO: verify channel before adding it to the channel cache
*/ */
public async addChannel(channelNameArg: string, passphraseArg: string) { public addChannel(channelNameArg: string, passphraseArg: string) {
const existingChannel = await this.getChannel(channelNameArg); const existingChannel = this.getChannel(channelNameArg);
if (existingChannel) { if (existingChannel) {
throw new Error('channel exists'); throw new Error('channel exists');
} }
// lets create the channel // lets create the channel
ClientUniverseChannel.createClientUniverseChannel(this, channelNameArg, passphraseArg); const clientUniverseChannel = ClientUniverseChannel.createClientUniverseChannel(
this,
channelNameArg,
passphraseArg
);
return clientUniverseChannel;
} }
/** /**
@ -50,9 +53,8 @@ export class ClientUniverse {
* @param channelName * @param channelName
* @param passphraseArg * @param passphraseArg
*/ */
public async getChannel(channelName: string): Promise<ClientUniverseChannel> { public getChannel(channelName: string): ClientUniverseChannel {
await this.checkConnection(); const clientUniverseChannel = this.clientUniverseCache.channelMap.find(channel => {
const clientUniverseChannel = this.channelStore.find(channel => {
return channel.name === channelName; return channel.name === channelName;
}); });
return clientUniverseChannel; return clientUniverseChannel;
@ -63,45 +65,35 @@ export class ClientUniverse {
* @param messageArg * @param messageArg
*/ */
public removeChannel(channelNameArg, notifyServer = true) { public removeChannel(channelNameArg, notifyServer = true) {
const clientUniverseChannel = this.channelStore.findOneAndRemove(channelItemArg => { const clientUniverseChannel = this.clientUniverseCache.channelMap.findOneAndRemove(
channelItemArg => {
return channelItemArg.name === channelNameArg; return channelItemArg.name === channelNameArg;
}); }
);
} }
/** public async start() {
* sends a message towards the server
* @param messageArg
*/
public async sendMessage(messageArg: interfaces.IMessageCreator) {
await this.checkConnection(); await this.checkConnection();
const requestBody: interfaces.IUniverseMessage = {
id: plugins.smartunique.shortId(),
timestamp: Date.now(),
passphrase: (await this.getChannel(messageArg.targetChannelName)).passphrase,
...messageArg
};
// TODO: User websocket connection if available
} }
public close() { public async stop() {
this.smartsocketClient.disconnect(); await this.smartsocketClient.disconnect();
} }
/** /**
* checks the connection towards a universe server * checks the connection towards a universe server
* since password validation is done through other means, a connection should always be possible * since password validation is done through other means, a connection should always be possible
*/ */
private async checkConnection(): Promise<void> { public async checkConnection(): Promise<void> {
if (!this.smartsocketClient && !this.observableIntake) { if (!this.smartsocketClient && !this.observableIntake) {
const parsedURL = url.parse(this.options.serverAddress); const parsedURL = url.parse(this.options.serverAddress);
const socketConfig: plugins.smartsocket.ISmartsocketClientOptions = { const socketConfig: plugins.smartsocket.ISmartsocketClientOptions = {
alias: process.env.SOCKET_ALIAS || 'someclient', alias: 'universeclient',
password: 'UniverseClient', password: 'UniverseClient',
port: parseInt(parsedURL.port, 10), port: parseInt(parsedURL.port, 10),
role: 'UniverseClient', role: 'UniverseClient',
url: parsedURL.protocol + '//' + parsedURL.hostname url: parsedURL.protocol + '//' + parsedURL.hostname
}; };
console.log(socketConfig);
this.smartsocketClient = new SmartsocketClient(socketConfig); this.smartsocketClient = new SmartsocketClient(socketConfig);
this.observableIntake = new plugins.smartrx.ObservableIntake(); this.observableIntake = new plugins.smartrx.ObservableIntake();
@ -110,18 +102,53 @@ export class ClientUniverse {
/** /**
* should handle a forced unsubscription by the server * should handle a forced unsubscription by the server
*/ */
const unsubscribe = new plugins.smartsocket.SocketFunction({ const socketFunctionUnsubscribe = new plugins.smartsocket.SocketFunction({
funcName: 'unsubscribe', funcName: 'unsubscribe',
allowedRoles: [], allowedRoles: [],
funcDef: async (data: interfaces.IServerUnsubscribeActionPayload) => {} funcDef: async (data: interfaces.IServerUnsubscribeActionPayload) => {
throw new Error('TODO');
}
}); });
/** /**
* should handle a message reception * handles message reception
*/ */
const receiveMessage = async () => {}; const socketFunctionProcessMessage = new plugins.smartsocket.SocketFunction<
interfaces.ISocketRequest_ProcessMessage
>({
funcName: 'processMessage',
allowedRoles: [],
funcDef: async messageDescriptorArg => {
plugins.smartlog.defaultLogger.log('info', 'Got message from server');
const clientUniverseMessage = ClientUniverseMessage.createMessageFromMessageDescriptor(
messageDescriptorArg
);
this.observableIntake.push(clientUniverseMessage);
// lets find the corresponding channel
const targetChannel = this.getChannel(clientUniverseMessage.targetChannelName);
if (targetChannel) {
await targetChannel.emitMessageLocally(clientUniverseMessage);
return {
messageStatus: 'ok'
};
} else {
return {
messageStatus: 'channel not found'
};
}
}
});
// add functions
this.smartsocketClient.addSocketFunction(socketFunctionUnsubscribe);
this.smartsocketClient.addSocketFunction(socketFunctionProcessMessage);
await this.smartsocketClient.connect(); await this.smartsocketClient.connect();
plugins.smartlog.defaultLogger.log('info', 'universe client connected successfully');
await this.clientUniverseCache.channelMap.forEach(async clientUniverseChannelArg => {
await clientUniverseChannelArg.subscribe();
});
} }
} }
} }

View File

@ -1,8 +1,11 @@
import * as plugins from './smartuniverse.plugins'; import * as plugins from './smartuniverse.plugins';
import { ClientUniverseChannel } from './smartuniverse.classes.clientuniversechannel';
/** /**
* a cache for clients * a cache for clients
* keeps track of which messages have already been received * keeps track of which messages have already been received
* good for deduplication in mesh environments * good for deduplication in mesh environments
*/ */
export class ClientUniverseCache {} export class ClientUniverseCache {
public channelMap = new plugins.lik.Objectmap<ClientUniverseChannel>();
}

View File

@ -2,6 +2,7 @@ import * as plugins from './smartuniverse.plugins';
import * as interfaces from './interfaces'; import * as interfaces from './interfaces';
import { ClientUniverse } from './'; import { ClientUniverse } from './';
import { ClientUniverseMessage } from './smartuniverse.classes.clientuniversemessage';
export class ClientUniverseChannel implements interfaces.IUniverseChannel { export class ClientUniverseChannel implements interfaces.IUniverseChannel {
// ====== // ======
@ -13,18 +14,17 @@ export class ClientUniverseChannel implements interfaces.IUniverseChannel {
* @param channelNameArg * @param channelNameArg
* @param passphraseArg * @param passphraseArg
*/ */
public static async createClientUniverseChannel( public static createClientUniverseChannel(
clientUniverseArg: ClientUniverse, clientUniverseArg: ClientUniverse,
channelNameArg: string, channelNameArg: string,
passphraseArg: string passphraseArg: string
): Promise<ClientUniverseChannel> { ): ClientUniverseChannel {
const clientChannel = new ClientUniverseChannel( const clientChannel = new ClientUniverseChannel(
clientUniverseArg, clientUniverseArg,
channelNameArg, channelNameArg,
passphraseArg passphraseArg
); );
clientUniverseArg.channelStore.add(clientChannel); clientUniverseArg.clientUniverseCache.channelMap.add(clientChannel);
await clientChannel.subscribe();
return clientChannel; return clientChannel;
} }
@ -35,12 +35,14 @@ export class ClientUniverseChannel implements interfaces.IUniverseChannel {
// properties // properties
public name: string; public name: string;
public passphrase: string; public passphrase: string;
public status: 'subscribed' | 'unsubscribed' = 'unsubscribed';
private subject = new plugins.smartrx.rxjs.Subject();
// refs // refs
public clientUniverse: ClientUniverse; public clientUniverseRef: ClientUniverse;
constructor(clientUniverseArg: ClientUniverse, nameArg: string, passphraseArg: string) { constructor(clientUniverseArg: ClientUniverse, nameArg: string, passphraseArg: string) {
this.clientUniverse = clientUniverseArg; this.clientUniverseRef = clientUniverseArg;
this.name = nameArg; this.name = nameArg;
this.passphrase = passphraseArg; this.passphrase = passphraseArg;
} }
@ -49,12 +51,47 @@ export class ClientUniverseChannel implements interfaces.IUniverseChannel {
* subscribes to a channel * subscribes to a channel
* tells the universe about this instances interest into a channel * tells the universe about this instances interest into a channel
*/ */
public async subscribe() { public async subscribe(observerArg?: plugins.smartrx.rxjs.Observer<any>) {
const serverCallActionName: interfaces.IServerCallActions = 'subscribe'; // lets make sure the channel is connected
const serverCallActionPayload: interfaces.IServerCallSubscribeActionPayload = { if (this.status === 'unsubscribed') {
const response = await this.clientUniverseRef.smartsocketClient.serverCall<interfaces.ISocketRequest_SubscribeChannel>(
'subscribeChannel',
{
name: this.name, name: this.name,
passphrase: this.passphrase passphrase: this.passphrase
}
);
this.status = response.subscriptionStatus;
}
if (observerArg) {
return this.subject.subscribe(observerArg);
}
}
public async emitMessageLocally(messageArg: ClientUniverseMessage) {
}
/**
* sends a message towards the server
* @param messageArg
*/
public async sendMessage(messageArg: interfaces.IMessageCreator) {
await this.clientUniverseRef.checkConnection();
const universeMessageToSend: interfaces.IUniverseMessage = {
id: plugins.smartunique.shortId(),
timestamp: Date.now(),
passphrase: this.passphrase,
targetChannelName: this.name,
messageText: messageArg.messageText,
payload: messageArg.payload,
payloadStringType: messageArg.payloadStringType
}; };
this.clientUniverse.smartsocketClient.serverCall(serverCallActionName, serverCallActionPayload); await this.clientUniverseRef.smartsocketClient.serverCall(
'processMessage',
universeMessageToSend
);
} }
} }

View File

@ -1,13 +1,15 @@
import * as plugins from './smartuniverse.plugins'; import * as plugins from './smartuniverse.plugins';
import * as interfaces from './interfaces'; import * as interfaces from './interfaces';
import { IUniverseMessage } from './interfaces';
export class ClientUniverseMessage implements interfaces.IUniverseMessage { export class ClientUniverseMessage implements interfaces.IUniverseMessage {
// ====== // ======
// STATIC // STATIC
// ====== // ======
public static createMessageFromPayload(messageDescriptor: interfaces.IUniverseMessage) {} public static createMessageFromMessageDescriptor(messageDescriptor: interfaces.IUniverseMessage) {
const clientuniverseMessage = new ClientUniverseMessage(messageDescriptor);
return clientuniverseMessage;
}
// ======== // ========
// INSTANCE // INSTANCE
@ -24,11 +26,14 @@ export class ClientUniverseMessage implements interfaces.IUniverseMessage {
public payloadStringType; public payloadStringType;
public targetChannelName: string; public targetChannelName: string;
constructor(messageArg: IUniverseMessage, payloadArg) { constructor(messageArg: interfaces.IUniverseMessage) {
for (const key of Object.keys(messageArg)) { for (const key of Object.keys(messageArg)) {
this[key] = messageArg[key]; this[key] = messageArg[key];
} }
} }
/**
* gets json for payload
*/
getAsJsonForPayload() {} getAsJsonForPayload() {}
} }

View File

@ -0,0 +1,5 @@
import * as plugins from './smartuniverse.plugins';
export class ReactionRequest {
}

View File

@ -0,0 +1,3 @@
import * as plugins from './smartuniverse.plugins';
export class ReactionResponse {}

View File

@ -6,20 +6,19 @@ import { UniverseCache, UniverseChannel, UniverseMessage } from './';
import * as paths from './smartuniverse.paths'; import * as paths from './smartuniverse.paths';
import * as interfaces from './interfaces'; import * as interfaces from './interfaces';
import { UniverseConnectionManager } from './smartuniverse.classes.universeconnectionmanager';
import { UniverseConnection } from './smartuniverse.classes.universeconnection'; import { UniverseConnection } from './smartuniverse.classes.universeconnection';
export interface ISmartUniverseConstructorOptions { export interface ISmartUniverseConstructorOptions {
messageExpiryInMilliseconds: number; messageExpiryInMilliseconds: number;
externalServer?: plugins.smartexpress.Server;
} }
/** /**
* main class that setsup a Universe * main class that setups a Universe
*/ */
export class Universe { export class Universe {
// subinstances // subinstances
public universeCache: UniverseCache; public universeCache: UniverseCache;
public universeConnectionManager: UniverseConnectionManager;
// options // options
private options: ISmartUniverseConstructorOptions; private options: ISmartUniverseConstructorOptions;
@ -36,8 +35,7 @@ export class Universe {
constructor(optionsArg: ISmartUniverseConstructorOptions) { constructor(optionsArg: ISmartUniverseConstructorOptions) {
this.options = optionsArg; this.options = optionsArg;
this.universeCache = new UniverseCache(this.options.messageExpiryInMilliseconds); this.universeCache = new UniverseCache(this, this.options.messageExpiryInMilliseconds);
this.universeConnectionManager = new UniverseConnectionManager();
} }
/** /**
@ -62,8 +60,8 @@ export class Universe {
/** /**
* adds a channel to the Universe * adds a channel to the Universe
*/ */
public async addChannel(nameArg: string, passphraseArg: string) { public addChannel(nameArg: string, passphraseArg: string) {
const newChannel = UniverseChannel.createChannel(this.universeCache, nameArg, passphraseArg); const newChannel = UniverseChannel.createChannel(this, nameArg, passphraseArg);
} }
/** /**
@ -71,6 +69,7 @@ export class Universe {
*/ */
public async start(portArg: number) { public async start(portArg: number) {
// lets create the base smartexpress server // lets create the base smartexpress server
if (!this.options.externalServer) {
this.smartexpressServer = new plugins.smartexpress.Server({ this.smartexpressServer = new plugins.smartexpress.Server({
cors: true, cors: true,
defaultAnswer: async () => { defaultAnswer: async () => {
@ -79,6 +78,10 @@ export class Universe {
forceSsl: false, forceSsl: false,
port: portArg port: portArg
}); });
} else {
console.log('Universe is using externally supplied server');
this.smartexpressServer = this.options.externalServer;
}
// add websocket upgrade // add websocket upgrade
this.smartsocket = new plugins.smartsocket.Smartsocket({}); this.smartsocket = new plugins.smartsocket.Smartsocket({});
@ -92,27 +95,74 @@ export class Universe {
// add the role to smartsocket // add the role to smartsocket
this.smartsocket.addSocketRoles([ClientRole]); this.smartsocket.addSocketRoles([ClientRole]);
const SubscriptionSocketFunction = new plugins.smartsocket.SocketFunction({ const socketFunctionSubscription = new plugins.smartsocket.SocketFunction<interfaces.ISocketRequest_SubscribeChannel>({
allowedRoles: [ClientRole], // there is only one client role, Authentication happens on another level allowedRoles: [ClientRole], // there is only one client role, Authentication happens on another level
funcName: 'channelSubscription', funcName: 'subscribeChannel',
funcDef: async (dataArg, socketConnectionArg) => { funcDef: async (
// run in "this context" of this class dataArg,
(() => { socketConnectionArg
// TODO: properly add the connection ) => {
const universeConnection = new UniverseConnection({ const universeConnection = new UniverseConnection({
socketConnection: socketConnectionArg,
}) authenticationRequests: [dataArg]
this.universeConnectionManager.addConnection(); });
})(); await UniverseConnection.addConnectionToCache(this, universeConnection);
return {
subscriptionStatus: 'subscribed'
};
} }
}); });
// add smartsocket to the running smartexpress app const socketFunctionProcessMessage = new plugins.smartsocket.SocketFunction({
this.smartsocket.setExternalServer('smartexpress', this.smartexpressServer as any); allowedRoles: [ClientRole], // there is only one client role, Authentication happens on another level
// start everything funcName: 'processMessage',
funcDef: async (dataArg: interfaces.IUniverseMessage, socketConnectionArg) => {
const universeConnection = UniverseConnection.findUniverseConnectionBySocketConnection(
this.universeCache,
socketConnectionArg
);
if (universeConnection) {
plugins.smartlog.defaultLogger.log(
'ok',
'found UniverseConnection for socket for incoming message'
);
} else {
plugins.smartlog.defaultLogger.log(
'warn',
'found no Authorized channel for incoming message'
);
return {
error: 'You need to authenticate for a channel'
};
}
const unauthenticatedMessage = UniverseMessage.createMessageFromPayload(
socketConnectionArg,
dataArg
);
const foundChannel = await UniverseChannel.authorizeAMessageForAChannel(
this.universeCache,
unauthenticatedMessage
);
if (foundChannel && unauthenticatedMessage.authenticated) {
const authenticatedMessage = unauthenticatedMessage;
await this.universeCache.addMessage(authenticatedMessage);
}
}
});
// add socket functions
this.smartsocket.addSocketFunction(socketFunctionSubscription);
this.smartsocket.addSocketFunction(socketFunctionProcessMessage);
// start the server
if (!this.options.externalServer) {
await this.smartexpressServer.start(); await this.smartexpressServer.start();
}
// add smartsocket to the running smartexpress app
await this.smartsocket.setExternalServer('smartexpress', this.smartexpressServer);
await this.smartsocket.start(); await this.smartsocket.start();
console.log('started universe'); plugins.smartlog.defaultLogger.log('success', 'started universe');
} }
/** /**
@ -120,6 +170,8 @@ export class Universe {
*/ */
public async stopServer() { public async stopServer() {
await this.smartsocket.stop(); await this.smartsocket.stop();
if (!this.options.externalServer) {
await this.smartexpressServer.stop(); await this.smartexpressServer.stop();
} }
}
} }

View File

@ -8,6 +8,8 @@ import { Objectmap } from '@pushrocks/lik';
import { Observable, from } from 'rxjs'; import { Observable, from } from 'rxjs';
import { filter } from 'rxjs/operators'; import { filter } from 'rxjs/operators';
import { rxjs } from '@pushrocks/smartrx'; import { rxjs } from '@pushrocks/smartrx';
import { UniverseConnection } from './smartuniverse.classes.universeconnection';
import { Universe } from './smartuniverse.classes.universe';
/** /**
* universe store handles the creation, storage and retrieval of messages. * universe store handles the creation, storage and retrieval of messages.
@ -29,13 +31,22 @@ export class UniverseCache {
*/ */
public channelMap = new Objectmap<UniverseChannel>(); public channelMap = new Objectmap<UniverseChannel>();
/**
* stores all connections
*/
public connectionMap = new plugins.lik.Objectmap<UniverseConnection>();
/** /**
* allows messages to be processed in a blacklist mode for further analysis * allows messages to be processed in a blacklist mode for further analysis
*/ */
public blackListChannel = new UniverseChannel(this, 'blacklist', 'nada'); public blackListChannel: UniverseChannel;
constructor(standardMessageExpiryArg: number) { public universeRef: Universe;
constructor(universeArg: Universe, standardMessageExpiryArg: number) {
this.universeRef = universeArg;
this.standardMessageExpiry = standardMessageExpiryArg; this.standardMessageExpiry = standardMessageExpiryArg;
this.blackListChannel = new UniverseChannel(this.universeRef, 'blacklist', 'nada');
} }
/** /**
@ -47,6 +58,9 @@ export class UniverseCache {
messageArg.setUniverseCache(this); messageArg.setUniverseCache(this);
UniverseChannel.authorizeAMessageForAChannel(this, messageArg); UniverseChannel.authorizeAMessageForAChannel(this, messageArg);
this.messageMap.add(messageArg); this.messageMap.add(messageArg);
messageArg.universeChannelList.forEach(universeChannel => {
universeChannel.pushToClients(messageArg);
});
} }
/** /**

View File

@ -1,8 +1,10 @@
import * as plugins from './smartuniverse.plugins'; import * as plugins from './smartuniverse.plugins';
import * as interfaces from './interfaces';
import { Objectmap } from '@pushrocks/lik';
import { UniverseCache } from './smartuniverse.classes.universecache'; import { UniverseCache } from './smartuniverse.classes.universecache';
import { UniverseMessage } from './smartuniverse.classes.universemessage'; import { UniverseMessage } from './smartuniverse.classes.universemessage';
import { UniverseConnection } from './smartuniverse.classes.universeconnection';
import { Universe } from './smartuniverse.classes.universe';
/** /**
* enables messages to stay within a certain scope. * enables messages to stay within a certain scope.
@ -18,12 +20,12 @@ export class UniverseChannel {
* @param passphraseArg the secret thats used for a certain topic. * @param passphraseArg the secret thats used for a certain topic.
*/ */
public static createChannel( public static createChannel(
universeCacheArg: UniverseCache, universeArg: Universe,
channelNameArg: string, channelNameArg: string,
passphraseArg: string passphraseArg: string
) { ) {
const newChannel = new UniverseChannel(universeCacheArg, channelNameArg, passphraseArg); const newChannel = new UniverseChannel(universeArg, channelNameArg, passphraseArg);
universeCacheArg.channelMap.add(newChannel); universeArg.universeCache.channelMap.add(newChannel);
return newChannel; return newChannel;
} }
@ -41,10 +43,17 @@ export class UniverseChannel {
} }
} }
/**
* a static message authorization function that takes the UniverseCache
* (where messages and channels are stored and their lifetime is managed)
* and the universemessage to find a fitting channel for the message
* @param universeCacheArg
* @param universeMessageArg
*/
public static authorizeAMessageForAChannel( public static authorizeAMessageForAChannel(
universeCacheArg: UniverseCache, universeCacheArg: UniverseCache,
universeMessageArg: UniverseMessage universeMessageArg: UniverseMessage
) { ): UniverseChannel {
const foundChannel = universeCacheArg.channelMap.find(universeChannel => { const foundChannel = universeCacheArg.channelMap.find(universeChannel => {
const result = universeChannel.authenticate(universeMessageArg); const result = universeChannel.authenticate(universeMessageArg);
return result; return result;
@ -52,13 +61,22 @@ export class UniverseChannel {
if (foundChannel) { if (foundChannel) {
universeMessageArg.authenticated = true; universeMessageArg.authenticated = true;
universeMessageArg.universeChannelList.add(foundChannel); universeMessageArg.universeChannelList.add(foundChannel);
plugins.smartlog.defaultLogger.log('ok', 'message authorized');
return foundChannel; return foundChannel;
} else { } else {
universeMessageArg.authenticated = false; universeMessageArg.authenticated = false;
universeMessageArg.universeChannelList.add(universeCacheArg.blackListChannel); universeMessageArg.universeChannelList.add(universeCacheArg.blackListChannel);
plugins.smartlog.defaultLogger.log('warn', 'message not valid');
return null;
} }
} }
public static getUniverseChannelByName(universeRef: Universe, universeChannelName: string) {
return universeRef.universeCache.channelMap.find(channelArg => {
return channelArg.name === universeChannelName;
});
}
// ======== // ========
// INSTANCE // INSTANCE
// ======== // ========
@ -66,20 +84,23 @@ export class UniverseChannel {
* the name of the channel * the name of the channel
*/ */
public name: string; public name: string;
public universeCacheInstance: UniverseCache; public universeRef: Universe;
/** /**
* the passphrase for the channel * the passphrase for the channel
*/ */
public passphrase: string; public passphrase: string;
constructor(universeCacheArg: UniverseCache, channelNameArg: string, passphraseArg: string) { constructor(universeArg: Universe, channelNameArg: string, passphraseArg: string) {
this.universeRef = universeArg;
this.name = channelNameArg; this.name = channelNameArg;
this.passphrase = passphraseArg; this.passphrase = passphraseArg;
} }
/** /**
* authenticates a client on the server side * authenticates a client on the server side by matching
* # the messages channelName against the unverseChannel's name
* # the messages password against the universeChannel's password
*/ */
public authenticate(universeMessageArg: UniverseMessage): boolean { public authenticate(universeMessageArg: UniverseMessage): boolean {
return ( return (
@ -88,5 +109,34 @@ export class UniverseChannel {
); );
} }
public pushToClients(messageArg: UniverseMessage) {} /**
* pushes a message to clients
* @param messageArg
*/
public async pushToClients(messageArg: UniverseMessage) {
const universeConnectionsWithChannelAccess: UniverseConnection[] = [];
this.universeRef.universeCache.connectionMap.forEach(async socketConnection => {
if (socketConnection.authenticatedChannels.includes(this)) {
universeConnectionsWithChannelAccess.push(socketConnection);
}
});
for (const universeConnection of universeConnectionsWithChannelAccess) {
const smartsocket = universeConnection.socketConnection
.smartsocketRef as plugins.smartsocket.Smartsocket;
const universeMessageToSend: interfaces.IUniverseMessage = {
id: messageArg.id,
timestamp: messageArg.timestamp,
passphrase: messageArg.passphrase,
targetChannelName: this.name,
messageText: messageArg.messageText,
payload: messageArg.payload,
payloadStringType: messageArg.payloadStringType
};
smartsocket.clientCall(
'processMessage',
universeMessageToSend,
universeConnection.socketConnection
);
}
}
} }

View File

@ -1,26 +1,121 @@
import * as plugins from './smartuniverse.plugins'; import * as plugins from './smartuniverse.plugins';
import * as interfaces from './interfaces';
import { UniverseChannel } from './smartuniverse.classes.universechannel'; import { UniverseChannel } from './smartuniverse.classes.universechannel';
import { UniverseCache } from './smartuniverse.classes.universecache';
import { Universe } from './smartuniverse.classes.universe';
/** /**
* represents a connection to the universe * represents a connection to the universe
*/ */
export class UniverseConnection { export class UniverseConnection {
/**
*
* @param universeConnectionArg
*/
public static async addConnectionToCache(
universeRef: Universe,
universeConnectionArg: UniverseConnection
) {
let universeConnection = universeConnectionArg;
universeConnection = await UniverseConnection.deduplicateUniverseConnection(
universeRef.universeCache,
universeConnection
);
universeConnection = await UniverseConnection.authenticateAuthenticationRequests(
universeRef,
universeConnection
);
universeRef.universeCache.connectionMap.add(universeConnection);
}
/**
* deduplicates UniverseConnections
*/
public static async deduplicateUniverseConnection(
universeCache: UniverseCache,
universeConnectionArg: UniverseConnection
): Promise<UniverseConnection> {
let connectionToReturn: UniverseConnection;
universeCache.connectionMap.forEach(async existingConnection => {
if (existingConnection.socketConnection === universeConnectionArg.socketConnection) {
connectionToReturn = await this.mergeUniverseConnections(
existingConnection,
universeConnectionArg
);
}
});
if (!connectionToReturn) {
connectionToReturn = universeConnectionArg;
}
return connectionToReturn;
}
/**
* authenticate AuthenticationRequests
*/
public static async authenticateAuthenticationRequests(
universeRef: Universe,
universeConnectionArg: UniverseConnection
): Promise<UniverseConnection> {
for (const authenticationRequest of universeConnectionArg.authenticationRequests) {
const universeChannelToAuthenticateAgainst = UniverseChannel.getUniverseChannelByName(
universeRef,
authenticationRequest.name
);
if (universeChannelToAuthenticateAgainst.passphrase === authenticationRequest.passphrase) {
universeConnectionArg.authenticatedChannels.push(universeChannelToAuthenticateAgainst);
}
}
return universeConnectionArg;
}
/**
* merges two UniverseConnections
*/
public static mergeUniverseConnections(
connectionArg1: UniverseConnection,
connectionArg2: UniverseConnection
) {
return connectionArg1;
}
/**
* finds a UniverseConnection by providing a socket connection
*/
public static findUniverseConnectionBySocketConnection(
universeCache: UniverseCache,
socketConnectionArg: plugins.smartsocket.SocketConnection
): UniverseConnection {
const universeConnection = universeCache.connectionMap.find(universeConnectionArg => {
return universeConnectionArg.socketConnection === socketConnectionArg;
});
return universeConnection;
}
public terminatedDeferred = plugins.smartpromise.defer();
/** /**
* the socketClient to ping * the socketClient to ping
*/ */
public socketConnection: plugins.smartsocket.SocketConnection; public socketConnection: plugins.smartsocket.SocketConnection;
public authenticationRequests = [] public authenticationRequests: Array<interfaces.ISocketRequest_SubscribeChannel['request']> = [];
public subscribedChannels: UniverseChannel[] = []; public subscribedChannels: UniverseChannel[] = [];
public authenticatedChannels: UniverseChannel[] = []; public authenticatedChannels: UniverseChannel[] = [];
public failedToJoinChannels: UniverseChannel[] = []; public failedToJoinChannels: UniverseChannel[] = [];
public terminateConnection () { /**
this.socketConnection * terminates the connection
*/
public terminateConnection() {
this.socketConnection.socket.disconnect();
this.terminatedDeferred.resolve();
} }
constructor(optionsArg: { constructor(optionsArg: {
socketConnection: plugins.smartsocket.SocketConnection; socketConnection: plugins.smartsocket.SocketConnection;
authenticationRequests: Array<interfaces.ISocketRequest_SubscribeChannel['request']>;
}) { }) {
this.socketConnection, this.authenticationRequests = optionsArg.authenticationRequests;
this.socketConnection = optionsArg.socketConnection;
} }
} }

View File

@ -1,11 +0,0 @@
import * as plugins from './smartuniverse.plugins';
import { UniverseConnection } from './smartuniverse.classes.universeconnection';
/**
* manages connections to a universe
*/
export class UniverseConnectionManager {
public connectionMap = new plugins.lik.Objectmap<UniverseConnection>();
public addConnection() {}
}

View File

@ -7,23 +7,31 @@ import { Timer, TimeStamp } from '@pushrocks/smarttime';
import { Universe } from './smartuniverse.classes.universe'; import { Universe } from './smartuniverse.classes.universe';
import { UniverseChannel } from './smartuniverse.classes.universechannel'; import { UniverseChannel } from './smartuniverse.classes.universechannel';
import { UniverseCache } from './smartuniverse.classes.universecache'; import { UniverseCache } from './smartuniverse.classes.universecache';
import { IUniverseMessage } from './interfaces'; import { SocketConnection } from '@pushrocks/smartsocket';
/** /**
* represents a message within a universe * represents a message within a universe
* acts as a container to save message states like authentication status * acts as a container to save message states like authentication status
*/ */
export class UniverseMessage implements interfaces.IUniverseMessage { export class UniverseMessage implements interfaces.IUniverseMessage {
public id: string; public static createMessageFromPayload(
socketConnectionArg: SocketConnection,
dataArg: interfaces.IUniverseMessage
) {
const universeMessageInstance = new UniverseMessage(dataArg);
universeMessageInstance.socketConnection = socketConnectionArg;
return universeMessageInstance;
}
public id: string;
public timestamp: number; public timestamp: number;
public smartTimestamp: TimeStamp; public smartTimestamp: TimeStamp;
public messageText: string; public messageText: string;
public passphrase: string; public passphrase: string;
public payload: any; public payload: any;
public payloadStringType; public payloadStringType;
public targetChannelName: string; public targetChannelName: string;
public socketConnection: SocketConnection;
/** /**
* the UniverseCache the message is attached to * the UniverseCache the message is attached to
@ -38,7 +46,7 @@ export class UniverseMessage implements interfaces.IUniverseMessage {
/** /**
* wether the message is authenticated * wether the message is authenticated
*/ */
public authenticated: boolean = null; public authenticated: boolean = false;
/** /**
* a destruction timer for this message * a destruction timer for this message
@ -50,7 +58,7 @@ export class UniverseMessage implements interfaces.IUniverseMessage {
* @param messageArg * @param messageArg
* @param attachedPayloadArg * @param attachedPayloadArg
*/ */
constructor(messageDescriptor: IUniverseMessage) { constructor(messageDescriptor: interfaces.IUniverseMessage) {
this.smartTimestamp = new TimeStamp(this.timestamp); this.smartTimestamp = new TimeStamp(this.timestamp);
this.messageText = messageDescriptor.messageText; this.messageText = messageDescriptor.messageText;
this.targetChannelName = messageDescriptor.targetChannelName; this.targetChannelName = messageDescriptor.targetChannelName;
@ -64,6 +72,8 @@ export class UniverseMessage implements interfaces.IUniverseMessage {
this.universeCache = universeCacheArg; this.universeCache = universeCacheArg;
} }
public setTargetChannel() {}
public setDestructionTimer(selfdestructAfterArg: number) { public setDestructionTimer(selfdestructAfterArg: number) {
if (selfdestructAfterArg) { if (selfdestructAfterArg) {
this.destructionTimer = new Timer(selfdestructAfterArg); this.destructionTimer = new Timer(selfdestructAfterArg);
@ -82,7 +92,7 @@ export class UniverseMessage implements interfaces.IUniverseMessage {
* handles bad messages for further analysis * handles bad messages for further analysis
*/ */
public handleAsBadMessage() { public handleAsBadMessage() {
console.log('received a bad message'); plugins.smartlog.defaultLogger.log('warn', 'received a bad message');
} }
/** /**

View File

@ -9,6 +9,7 @@ import * as smarthash from '@pushrocks/smarthash';
import * as smartdelay from '@pushrocks/smartdelay'; import * as smartdelay from '@pushrocks/smartdelay';
import * as smartexpress from '@pushrocks/smartexpress'; import * as smartexpress from '@pushrocks/smartexpress';
import * as smartfile from '@pushrocks/smartfile'; import * as smartfile from '@pushrocks/smartfile';
import * as smartlog from '@pushrocks/smartlog';
import * as smartpromise from '@pushrocks/smartpromise'; import * as smartpromise from '@pushrocks/smartpromise';
import * as smartrequest from '@pushrocks/smartrequest'; import * as smartrequest from '@pushrocks/smartrequest';
import * as smartrx from '@pushrocks/smartrx'; import * as smartrx from '@pushrocks/smartrx';
@ -22,6 +23,7 @@ export {
smartdelay, smartdelay,
smartexpress, smartexpress,
smartfile, smartfile,
smartlog,
smartpromise, smartpromise,
smartrx, smartrx,
smartrequest, smartrequest,