@push.rocks/smartsocket 2.1.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,75 +4,182 @@ import * as pluginsTyped from './smartsocket.pluginstyped.js';
4
4
  import { Smartsocket } from './smartsocket.classes.smartsocket.js';
5
5
  import { logger } from './smartsocket.logging.js';
6
6
  /**
7
- * class socketServer
8
- * handles the attachment of socketIo to whatever server is in play
7
+ * class SocketServer
8
+ * handles the WebSocket server in standalone mode, or provides hooks for smartserve integration
9
9
  */
10
10
  export class SocketServer {
11
11
  constructor(smartSocketInstance) {
12
12
  /**
13
- * wether httpServer is standalone
13
+ * whether httpServer is standalone (created by us)
14
14
  */
15
15
  this.standaloneServer = false;
16
16
  this.smartsocket = smartSocketInstance;
17
17
  }
18
18
  /**
19
- * starts the server with another server
20
- * also works with an express style server
19
+ * Starts listening to incoming websocket connections (standalone mode).
20
+ * If no port is specified, this is a no-op (hooks mode via smartserve).
21
21
  */
22
- async setExternalServer(serverType, serverArg) {
23
- this.httpServerDeferred = plugins.smartpromise.defer();
24
- await serverArg.startedPromise;
25
- this.httpServer = serverArg.httpServer;
26
- this.httpServerDeferred.resolve();
27
- }
28
- /**
29
- * gets the server for socket.io
30
- */
31
- async getServerForSocketIo() {
32
- if (this.httpServerDeferred) {
33
- await this.httpServerDeferred.promise;
34
- }
35
- if (this.httpServer) {
36
- return this.httpServer;
37
- }
38
- else {
39
- const httpModule = await this.smartsocket.smartenv.getSafeNodeModule('http');
40
- this.httpServer = new httpModule.Server();
41
- this.standaloneServer = true;
42
- return this.httpServer;
22
+ async start() {
23
+ // If no port specified, we're in hooks mode - nothing to start
24
+ if (!this.smartsocket.options.port) {
25
+ return;
43
26
  }
27
+ // Standalone mode - create our own HTTP server and WebSocket server
28
+ const done = plugins.smartpromise.defer();
29
+ const httpModule = await this.smartsocket.smartenv.getSafeNodeModule('http');
30
+ const wsModule = await this.smartsocket.smartenv.getSafeNodeModule('ws');
31
+ this.httpServer = httpModule.createServer();
32
+ this.standaloneServer = true;
33
+ // Create WebSocket server attached to HTTP server
34
+ this.wsServer = new wsModule.WebSocketServer({ server: this.httpServer });
35
+ this.wsServer.on('connection', (ws) => {
36
+ this.smartsocket.handleNewConnection(ws);
37
+ });
38
+ this.httpServer.listen(this.smartsocket.options.port, () => {
39
+ logger.log('success', `Server started in standalone mode on port ${this.smartsocket.options.port}`);
40
+ done.resolve();
41
+ });
42
+ await done.promise;
44
43
  }
45
44
  /**
46
- * starts listening to incoming sockets:
45
+ * closes the server
47
46
  */
48
- async start() {
47
+ async stop() {
49
48
  const done = plugins.smartpromise.defer();
50
- // handle http servers
51
- // in case an external server has been set "this.standaloneServer" should be false
49
+ let resolved = false;
50
+ if (this.wsServer) {
51
+ // Close all WebSocket connections
52
+ this.wsServer.clients.forEach((client) => {
53
+ client.terminate();
54
+ });
55
+ this.wsServer.close();
56
+ this.wsServer = null;
57
+ }
52
58
  if (this.httpServer && this.standaloneServer) {
53
- if (!this.smartsocket.options.port) {
54
- logger.log('error', 'there should be a port specifed for smartsocket!');
55
- throw new Error('there should be a port specified for smartsocket');
56
- }
57
- this.httpServer.listen(this.smartsocket.options.port, () => {
58
- logger.log('success', `Server started in standalone mode on ${this.smartsocket.options.port}`);
59
- done.resolve();
59
+ const resolveOnce = () => {
60
+ if (!resolved) {
61
+ resolved = true;
62
+ this.httpServer = null;
63
+ this.standaloneServer = false;
64
+ done.resolve();
65
+ }
66
+ };
67
+ this.httpServer.close(() => {
68
+ resolveOnce();
60
69
  });
70
+ // Add a timeout in case close callback doesn't fire
71
+ const timeoutId = setTimeout(() => {
72
+ resolveOnce();
73
+ }, 2000);
74
+ // Ensure timeout doesn't keep process alive
75
+ if (timeoutId.unref) {
76
+ timeoutId.unref();
77
+ }
61
78
  }
62
79
  else {
63
80
  done.resolve();
64
81
  }
65
- // nothing else to do if express server is set
66
82
  await done.promise;
67
- return;
68
83
  }
69
84
  /**
70
- * closes the server
85
+ * Returns WebSocket hooks for integration with smartserve.
86
+ * Pass these hooks to SmartServe's websocket config.
71
87
  */
72
- async stop() {
73
- if (this.httpServer) {
74
- this.httpServer.close();
75
- }
88
+ getSmartserveWebSocketHooks() {
89
+ return {
90
+ onOpen: async (peer) => {
91
+ // Create a wrapper that adapts ISmartserveWebSocketPeer to WebSocket-like interface
92
+ const wsLikeSocket = this.createWsLikeFromPeer(peer);
93
+ await this.smartsocket.handleNewConnection(wsLikeSocket);
94
+ },
95
+ onMessage: async (peer, message) => {
96
+ // Dispatch message to the SocketConnection via the adapter
97
+ const adapter = peer.data.get('smartsocket_adapter');
98
+ if (adapter) {
99
+ let textData;
100
+ if (message.type === 'text' && message.text) {
101
+ textData = message.text;
102
+ }
103
+ else if (message.type === 'binary' && message.data) {
104
+ // Convert binary to text (Buffer/Uint8Array to string)
105
+ textData = new TextDecoder().decode(message.data);
106
+ }
107
+ if (textData) {
108
+ adapter.dispatchMessage(textData);
109
+ }
110
+ }
111
+ },
112
+ onClose: async (peer, code, reason) => {
113
+ // Dispatch close to the SocketConnection via the adapter
114
+ const adapter = peer.data.get('smartsocket_adapter');
115
+ if (adapter) {
116
+ adapter.dispatchClose();
117
+ }
118
+ },
119
+ onError: async (peer, error) => {
120
+ // Dispatch error to the SocketConnection via the adapter
121
+ const adapter = peer.data.get('smartsocket_adapter');
122
+ if (adapter) {
123
+ adapter.dispatchError();
124
+ }
125
+ },
126
+ };
127
+ }
128
+ /**
129
+ * Creates a WebSocket-like object from a smartserve peer
130
+ * This allows our SocketConnection to work with both native WebSocket and smartserve peers
131
+ */
132
+ createWsLikeFromPeer(peer) {
133
+ const messageListeners = [];
134
+ const closeListeners = [];
135
+ const errorListeners = [];
136
+ // Store the adapter on the peer for message routing
137
+ peer.data.set('smartsocket_adapter', {
138
+ dispatchMessage: (data) => {
139
+ messageListeners.forEach((listener) => {
140
+ listener({ data });
141
+ });
142
+ },
143
+ dispatchClose: () => {
144
+ closeListeners.forEach((listener) => listener());
145
+ },
146
+ dispatchError: () => {
147
+ errorListeners.forEach((listener) => listener());
148
+ },
149
+ });
150
+ return {
151
+ get readyState() { return peer.readyState; },
152
+ send: (data) => peer.send(data),
153
+ close: (code, reason) => peer.close(code, reason),
154
+ addEventListener: (event, listener) => {
155
+ if (event === 'message') {
156
+ messageListeners.push(listener);
157
+ }
158
+ else if (event === 'close') {
159
+ closeListeners.push(listener);
160
+ }
161
+ else if (event === 'error') {
162
+ errorListeners.push(listener);
163
+ }
164
+ },
165
+ removeEventListener: (event, listener) => {
166
+ if (event === 'message') {
167
+ const idx = messageListeners.indexOf(listener);
168
+ if (idx >= 0)
169
+ messageListeners.splice(idx, 1);
170
+ }
171
+ else if (event === 'close') {
172
+ const idx = closeListeners.indexOf(listener);
173
+ if (idx >= 0)
174
+ closeListeners.splice(idx, 1);
175
+ }
176
+ else if (event === 'error') {
177
+ const idx = errorListeners.indexOf(listener);
178
+ if (idx >= 0)
179
+ errorListeners.splice(idx, 1);
180
+ }
181
+ },
182
+ };
76
183
  }
77
184
  }
78
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRzb2NrZXQuY2xhc3Nlcy5zb2NrZXRzZXJ2ZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9zbWFydHNvY2tldC5jbGFzc2VzLnNvY2tldHNlcnZlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLDBCQUEwQixDQUFDO0FBQ3BELE9BQU8sS0FBSyxZQUFZLE1BQU0sK0JBQStCLENBQUM7QUFFOUQsMkNBQTJDO0FBQzNDLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxzQ0FBc0MsQ0FBQztBQUNuRSxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFFbEQ7OztHQUdHO0FBQ0gsTUFBTSxPQUFPLFlBQVk7SUFVdkIsWUFBWSxtQkFBZ0M7UUFMNUM7O1dBRUc7UUFDSyxxQkFBZ0IsR0FBRyxLQUFLLENBQUM7UUFHL0IsSUFBSSxDQUFDLFdBQVcsR0FBRyxtQkFBbUIsQ0FBQztJQUN6QyxDQUFDO0lBRUQ7OztPQUdHO0lBQ0ksS0FBSyxDQUFDLGlCQUFpQixDQUM1QixVQUEwQixFQUMxQixTQUFzRDtRQUV0RCxJQUFJLENBQUMsa0JBQWtCLEdBQUcsT0FBTyxDQUFDLFlBQVksQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUN2RCxNQUFNLFNBQVMsQ0FBQyxjQUFjLENBQUM7UUFDL0IsSUFBSSxDQUFDLFVBQVUsR0FBRyxTQUFTLENBQUMsVUFBVSxDQUFDO1FBQ3ZDLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxPQUFPLEVBQUUsQ0FBQztJQUNwQyxDQUFDO0lBRUQ7O09BRUc7SUFDSSxLQUFLLENBQUMsb0JBQW9CO1FBQy9CLElBQUksSUFBSSxDQUFDLGtCQUFrQixFQUFFLENBQUM7WUFDNUIsTUFBTSxJQUFJLENBQUMsa0JBQWtCLENBQUMsT0FBTyxDQUFDO1FBQ3hDLENBQUM7UUFDRCxJQUFJLElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQztZQUNwQixPQUFPLElBQUksQ0FBQyxVQUFVLENBQUM7UUFDekIsQ0FBQzthQUFNLENBQUM7WUFDTixNQUFNLFVBQVUsR0FBRyxNQUFNLElBQUksQ0FBQyxXQUFXLENBQUMsUUFBUSxDQUFDLGlCQUFpQixDQUFDLE1BQU0sQ0FBQyxDQUFDO1lBQzdFLElBQUksQ0FBQyxVQUFVLEdBQUcsSUFBSSxVQUFVLENBQUMsTUFBTSxFQUFFLENBQUM7WUFDMUMsSUFBSSxDQUFDLGdCQUFnQixHQUFHLElBQUksQ0FBQztZQUM3QixPQUFPLElBQUksQ0FBQyxVQUFVLENBQUM7UUFDekIsQ0FBQztJQUNILENBQUM7SUFFRDs7T0FFRztJQUNJLEtBQUssQ0FBQyxLQUFLO1FBQ2hCLE1BQU0sSUFBSSxHQUFHLE9BQU8sQ0FBQyxZQUFZLENBQUMsS0FBSyxFQUFFLENBQUM7UUFFMUMsc0JBQXNCO1FBQ3RCLGtGQUFrRjtRQUNsRixJQUFJLElBQUksQ0FBQyxVQUFVLElBQUksSUFBSSxDQUFDLGdCQUFnQixFQUFFLENBQUM7WUFDN0MsSUFBSSxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsT0FBTyxDQUFDLElBQUksRUFBRSxDQUFDO2dCQUNuQyxNQUFNLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxrREFBa0QsQ0FBQyxDQUFDO2dCQUN4RSxNQUFNLElBQUksS0FBSyxDQUFDLGtEQUFrRCxDQUFDLENBQUM7WUFDdEUsQ0FBQztZQUNELElBQUksQ0FBQyxVQUFVLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsT0FBTyxDQUFDLElBQUksRUFBRSxHQUFHLEVBQUU7Z0JBQ3pELE1BQU0sQ0FBQyxHQUFHLENBQ1IsU0FBUyxFQUNULHdDQUF3QyxJQUFJLENBQUMsV0FBVyxDQUFDLE9BQU8sQ0FBQyxJQUFJLEVBQUUsQ0FDeEUsQ0FBQztnQkFDRixJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7WUFDakIsQ0FBQyxDQUFDLENBQUM7UUFDTCxDQUFDO2FBQU0sQ0FBQztZQUNOLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUNqQixDQUFDO1FBRUQsOENBQThDO1FBQzlDLE1BQU0sSUFBSSxDQUFDLE9BQU8sQ0FBQztRQUNuQixPQUFPO0lBQ1QsQ0FBQztJQUVEOztPQUVHO0lBQ0ksS0FBSyxDQUFDLElBQUk7UUFDZixJQUFJLElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQztZQUNwQixJQUFJLENBQUMsVUFBVSxDQUFDLEtBQUssRUFBRSxDQUFDO1FBQzFCLENBQUM7SUFDSCxDQUFDO0NBQ0YifQ==
185
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRzb2NrZXQuY2xhc3Nlcy5zb2NrZXRzZXJ2ZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9zbWFydHNvY2tldC5jbGFzc2VzLnNvY2tldHNlcnZlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLDBCQUEwQixDQUFDO0FBQ3BELE9BQU8sS0FBSyxZQUFZLE1BQU0sK0JBQStCLENBQUM7QUFFOUQsMkNBQTJDO0FBQzNDLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxzQ0FBc0MsQ0FBQztBQUNuRSxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFFbEQ7OztHQUdHO0FBQ0gsTUFBTSxPQUFPLFlBQVk7SUFVdkIsWUFBWSxtQkFBZ0M7UUFMNUM7O1dBRUc7UUFDSyxxQkFBZ0IsR0FBRyxLQUFLLENBQUM7UUFHL0IsSUFBSSxDQUFDLFdBQVcsR0FBRyxtQkFBbUIsQ0FBQztJQUN6QyxDQUFDO0lBRUQ7OztPQUdHO0lBQ0ksS0FBSyxDQUFDLEtBQUs7UUFDaEIsK0RBQStEO1FBQy9ELElBQUksQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLE9BQU8sQ0FBQyxJQUFJLEVBQUUsQ0FBQztZQUNuQyxPQUFPO1FBQ1QsQ0FBQztRQUVELG9FQUFvRTtRQUNwRSxNQUFNLElBQUksR0FBRyxPQUFPLENBQUMsWUFBWSxDQUFDLEtBQUssRUFBRSxDQUFDO1FBQzFDLE1BQU0sVUFBVSxHQUFHLE1BQU0sSUFBSSxDQUFDLFdBQVcsQ0FBQyxRQUFRLENBQUMsaUJBQWlCLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDN0UsTUFBTSxRQUFRLEdBQUcsTUFBTSxJQUFJLENBQUMsV0FBVyxDQUFDLFFBQVEsQ0FBQyxpQkFBaUIsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUV6RSxJQUFJLENBQUMsVUFBVSxHQUFHLFVBQVUsQ0FBQyxZQUFZLEVBQUUsQ0FBQztRQUM1QyxJQUFJLENBQUMsZ0JBQWdC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
@@ -1,15 +1,58 @@
1
1
  import type * as http from 'http';
2
2
  import type * as https from 'https';
3
3
  export type { http, https };
4
- import type * as typedserver from '@api.global/typedserver';
5
- export type { typedserver };
6
- import type { Socket as ServerSocket, Server as ServerServer } from 'socket.io';
7
- import type { Socket as ClientSocket, connect as ClientIo } from 'socket.io-client';
8
- export declare namespace socketIo {
9
- type Socket = ServerSocket;
10
- type Server = ServerServer;
11
- }
12
- export declare namespace socketIoClient {
13
- type Socket = ClientSocket;
14
- type connect = typeof ClientIo;
4
+ import type * as wsTypes from 'ws';
5
+ export declare namespace ws {
6
+ type WebSocket = wsTypes.WebSocket;
7
+ type WebSocketServer = wsTypes.WebSocketServer;
8
+ type RawData = wsTypes.RawData;
9
+ }
10
+ /**
11
+ * Unified WebSocket type supporting both browser and Node.js environments
12
+ */
13
+ export type TWebSocket = WebSocket | ws.WebSocket;
14
+ /**
15
+ * Message event type for WebSocket messages (browser and Node.js compatible)
16
+ */
17
+ export type TMessageEvent = MessageEvent | {
18
+ data: string;
19
+ };
20
+ /**
21
+ * WebSocket-like interface for adapters (e.g., smartserve peer adapter)
22
+ */
23
+ export interface IWebSocketLike {
24
+ readyState: number;
25
+ send(data: string): void;
26
+ close(code?: number, reason?: string): void;
27
+ addEventListener(event: 'message', listener: (event: TMessageEvent) => void): void;
28
+ addEventListener(event: 'close', listener: () => void): void;
29
+ addEventListener(event: 'error', listener: () => void): void;
30
+ removeEventListener?(event: string, listener: (...args: any[]) => void): void;
31
+ }
32
+ export interface ISmartserveWebSocketPeer {
33
+ id: string;
34
+ url: string;
35
+ readyState: 0 | 1 | 2 | 3;
36
+ protocol: string;
37
+ extensions: string;
38
+ send(data: string): void;
39
+ sendBinary(data: Uint8Array | ArrayBuffer): void;
40
+ close(code?: number, reason?: string): void;
41
+ ping(data?: Uint8Array): void;
42
+ terminate(): void;
43
+ context: any;
44
+ data: Map<string, unknown>;
45
+ tags: Set<string>;
46
+ }
47
+ export interface ISmartserveWebSocketMessage {
48
+ type: 'text' | 'binary';
49
+ text?: string;
50
+ data?: Uint8Array;
51
+ size: number;
52
+ }
53
+ export interface ISmartserveWebSocketHooks {
54
+ onOpen?: (peer: ISmartserveWebSocketPeer) => void | Promise<void>;
55
+ onMessage?: (peer: ISmartserveWebSocketPeer, message: ISmartserveWebSocketMessage) => void | Promise<void>;
56
+ onClose?: (peer: ISmartserveWebSocketPeer, code: number, reason: string) => void | Promise<void>;
57
+ onError?: (peer: ISmartserveWebSocketPeer, error: Error) => void | Promise<void>;
15
58
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@push.rocks/smartsocket",
3
- "version": "2.1.0",
3
+ "version": "4.0.0",
4
4
  "description": "Provides easy and secure websocket communication mechanisms, including server and client implementation, function call routing, connection management, and tagging.",
5
5
  "main": "dist_ts/index.js",
6
6
  "typings": "dist_ts/index.d.ts",
@@ -9,36 +9,34 @@
9
9
  "type": "git",
10
10
  "url": "https://code.foss.global/push.rocks/smartsocket.git"
11
11
  },
12
- "author": "Lossless GmbH",
12
+ "author": "Task Venture Capital GmbH",
13
13
  "license": "MIT",
14
14
  "bugs": {
15
- "url": "https://gitlab.com/pushrocks/smartsocket/issues"
15
+ "url": "https://community.foss.global/"
16
16
  },
17
17
  "homepage": "https://code.foss.global/push.rocks/smartsocket",
18
18
  "dependencies": {
19
- "@api.global/typedrequest-interfaces": "^3.0.18",
20
- "@api.global/typedserver": "^3.0.27",
21
- "@push.rocks/isohash": "^2.0.0",
19
+ "@api.global/typedrequest-interfaces": "^3.0.19",
20
+ "@push.rocks/isohash": "^2.0.1",
22
21
  "@push.rocks/isounique": "^1.0.5",
23
- "@push.rocks/lik": "^6.0.14",
22
+ "@push.rocks/lik": "^6.2.2",
24
23
  "@push.rocks/smartdelay": "^3.0.5",
25
- "@push.rocks/smartenv": "^5.0.12",
26
- "@push.rocks/smartjson": "^5.0.19",
27
- "@push.rocks/smartlog": "^3.0.3",
28
- "@push.rocks/smartpromise": "^4.0.3",
29
- "@push.rocks/smartrx": "^3.0.7",
30
- "@push.rocks/smarttime": "^4.0.6",
31
- "engine.io": "6.6.4",
32
- "socket.io": "4.8.1",
33
- "socket.io-client": "4.8.1"
24
+ "@push.rocks/smartenv": "^6.0.0",
25
+ "@push.rocks/smartjson": "^5.2.0",
26
+ "@push.rocks/smartlog": "^3.1.10",
27
+ "@push.rocks/smartpromise": "^4.2.3",
28
+ "@push.rocks/smartrx": "^3.0.10",
29
+ "@push.rocks/smarttime": "^4.1.1",
30
+ "ws": "^8.18.3"
34
31
  },
35
32
  "devDependencies": {
36
- "@git.zone/tsbuild": "^2.1.66",
37
- "@git.zone/tsbundle": "^2.0.8",
38
- "@git.zone/tsrun": "^1.2.44",
39
- "@git.zone/tstest": "^1.0.77",
40
- "@push.rocks/tapbundle": "^5.0.23",
41
- "@types/node": "^22.13.10"
33
+ "@git.zone/tsbuild": "^3.1.2",
34
+ "@git.zone/tsbundle": "^2.6.3",
35
+ "@git.zone/tsrun": "^2.0.0",
36
+ "@git.zone/tstest": "^3.1.3",
37
+ "@push.rocks/smartserve": "^1.1.2",
38
+ "@types/node": "^24.10.1",
39
+ "@types/ws": "^8.18.1"
42
40
  },
43
41
  "private": false,
44
42
  "files": [
@@ -61,15 +59,16 @@
61
59
  "communication",
62
60
  "server",
63
61
  "client",
64
- "socket.io",
62
+ "native websocket",
65
63
  "authentication",
66
64
  "reconnection",
67
65
  "tagging",
68
66
  "function routing",
69
- "secure"
67
+ "secure",
68
+ "rpc"
70
69
  ],
71
70
  "scripts": {
72
- "test": "(tstest test/)",
71
+ "test": "(tstest test/ --verbose)",
73
72
  "build": "(tsbuild --web --allowimplicitany && tsbundle --from ./ts/index.ts --to dist_bundle/bundle.js)",
74
73
  "buildDocs": "tsdoc"
75
74
  }