@v1nt1248/3nclient-lib 0.0.14 → 0.0.15
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.
- package/package.json +3 -2
- package/src/libs/index.ts +11 -0
- package/src/libs/ipc-service-caller.ts +121 -0
- package/src/libs/ipc-service.ts +422 -0
- package/src/libs/serialization-for-ipc/copy-json.ts +55 -0
- package/src/libs/serialization-for-ipc/json-ipc.proto.d.ts +432 -0
- package/src/libs/serialization-for-ipc/json-ipc.proto.js +1093 -0
- package/src/libs/serialization-for-ipc/json-n-binary.ts +275 -0
- package/src/libs/serialization-for-ipc/protobuf-type.ts +61 -0
- package/src/libs/sqlite-on-3nstorage/deferred.ts +32 -0
- package/src/libs/sqlite-on-3nstorage/index.ts +291 -0
- package/src/libs/sqlite-on-3nstorage/sqljs.d.ts +279 -0
- package/src/libs/sqlite-on-3nstorage/sqljs.js +225 -0
- package/src/libs/sqlite-on-3nstorage/synced.ts +194 -0
package/package.json
CHANGED
|
@@ -3,12 +3,13 @@
|
|
|
3
3
|
"private": false,
|
|
4
4
|
"license": "AGPL-3.0-or-later",
|
|
5
5
|
"author": "v1nt1248",
|
|
6
|
-
"version": "0.0.
|
|
6
|
+
"version": "0.0.15",
|
|
7
7
|
"description": "Library for 3NWeb clients",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"files": [
|
|
10
10
|
"dist",
|
|
11
|
-
"src/components/"
|
|
11
|
+
"src/components/",
|
|
12
|
+
"src/libs"
|
|
12
13
|
],
|
|
13
14
|
"module": "./dist/ui-3n-lib.js",
|
|
14
15
|
"types": "./dist/index.d.ts",
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from './sqlite-on-3nstorage'
|
|
2
|
+
export {
|
|
3
|
+
makeObservableMethodCaller,
|
|
4
|
+
makeReqRepMethodCaller,
|
|
5
|
+
makeServiceCaller,
|
|
6
|
+
} from './ipc-service-caller'
|
|
7
|
+
export {
|
|
8
|
+
SingleConnectionIPCWrap,
|
|
9
|
+
MultiConnectionIPCWrap,
|
|
10
|
+
IPCWrap,
|
|
11
|
+
} from './ipc-service'
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright (C) 2022 - 2023 3NSoft Inc.
|
|
3
|
+
|
|
4
|
+
This program is free software: you can redistribute it and/or modify it under
|
|
5
|
+
the terms of the GNU General Public License as published by the Free Software
|
|
6
|
+
Foundation, either version 3 of the License, or (at your option) any later
|
|
7
|
+
version.
|
|
8
|
+
|
|
9
|
+
This program is distributed in the hope that it will be useful, but
|
|
10
|
+
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
12
|
+
See the GNU General Public License for more details.
|
|
13
|
+
|
|
14
|
+
You should have received a copy of the GNU General Public License along with
|
|
15
|
+
this program. If not, see <http://www.gnu.org/licenses/>.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/// <reference path="../@types/platform-defs/w3n.d.ts" />
|
|
19
|
+
|
|
20
|
+
import { deserializeArgs, serializeArgs } from "./serialization-for-ipc/json-n-binary";
|
|
21
|
+
|
|
22
|
+
type RPCConnection = web3n.rpc.client.RPCConnection;
|
|
23
|
+
type PassedDatum = web3n.rpc.PassedDatum;
|
|
24
|
+
type Observer<T> = web3n.Observer<T>;
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
export interface TransformOpts {
|
|
28
|
+
unpackReply?: ((reply: PassedDatum | undefined) => any) | 'noop';
|
|
29
|
+
packRequest?: ((args: any[]) => PassedDatum | undefined) | 'noop';
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function replyFromPassedDatum(
|
|
33
|
+
data: PassedDatum|undefined, unpack: TransformOpts['unpackReply']
|
|
34
|
+
): any {
|
|
35
|
+
if (!data) { return; }
|
|
36
|
+
if (unpack) {
|
|
37
|
+
if (unpack === 'noop') {
|
|
38
|
+
return [data.bytes];
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
return unpack(data);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
const { bytes, passedByReference } = data;
|
|
46
|
+
return (bytes ? deserializeArgs(bytes, passedByReference)[0] : undefined);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function argsToPassedDatum(
|
|
51
|
+
args: any[], pack: TransformOpts['packRequest']
|
|
52
|
+
): PassedDatum|undefined {
|
|
53
|
+
if (args === undefined) { return; }
|
|
54
|
+
if (pack) {
|
|
55
|
+
if (pack === 'noop') {
|
|
56
|
+
if (!ArrayBuffer.isView(args[0])) {
|
|
57
|
+
throw new Error(`Method returned non-binary, while no serialization is set`);
|
|
58
|
+
}
|
|
59
|
+
return { bytes: args[0] as Uint8Array };
|
|
60
|
+
}
|
|
61
|
+
return pack(args);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
return serializeArgs(args);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function makeReqRepMethodCaller<F extends Function>(
|
|
69
|
+
connection: RPCConnection, method: string, transforms?: TransformOpts
|
|
70
|
+
): F {
|
|
71
|
+
return (async (...args: any[]) => {
|
|
72
|
+
const req = argsToPassedDatum(args, transforms?.packRequest);
|
|
73
|
+
const reply = await connection.makeRequestReplyCall(method, req);
|
|
74
|
+
return replyFromPassedDatum(reply, transforms?.unpackReply);
|
|
75
|
+
}) as any as F;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function makeObservableMethodCaller<F extends Function>(
|
|
79
|
+
connection: RPCConnection, method: string, transforms?: TransformOpts
|
|
80
|
+
): F {
|
|
81
|
+
return ((obs: Observer<any>, ...args: any[]) => {
|
|
82
|
+
const req = argsToPassedDatum(args, transforms?.packRequest);
|
|
83
|
+
const obsWrap: Observer<any> = {
|
|
84
|
+
next: data => {
|
|
85
|
+
if (!obs.next) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const ev = replyFromPassedDatum(data, transforms?.unpackReply);
|
|
89
|
+
obs.next(ev);
|
|
90
|
+
},
|
|
91
|
+
complete: obs.complete,
|
|
92
|
+
error: obs.error
|
|
93
|
+
};
|
|
94
|
+
return connection.startObservableCall(method, req, obsWrap);
|
|
95
|
+
}) as any as F;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function makeServiceCaller<T>(
|
|
99
|
+
connection: RPCConnection,
|
|
100
|
+
reqRepMethods?: (keyof T)[], obsMethods?: (keyof T)[],
|
|
101
|
+
transforms?: { [method in keyof T]: TransformOpts; }
|
|
102
|
+
): Partial<T> {
|
|
103
|
+
const caller = {} as T;
|
|
104
|
+
if (reqRepMethods) {
|
|
105
|
+
for (const method of reqRepMethods) {
|
|
106
|
+
caller[method] = makeReqRepMethodCaller(
|
|
107
|
+
connection, method as string,
|
|
108
|
+
(transforms ? transforms[method] : undefined)
|
|
109
|
+
) as any;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (obsMethods) {
|
|
113
|
+
for (const method of obsMethods) {
|
|
114
|
+
caller[method] = makeObservableMethodCaller(
|
|
115
|
+
connection, method as string,
|
|
116
|
+
(transforms ? transforms[method] : undefined)
|
|
117
|
+
) as any;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return caller;
|
|
121
|
+
}
|
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright (C) 2022 - 2023 3NSoft Inc.
|
|
3
|
+
|
|
4
|
+
This program is free software: you can redistribute it and/or modify it under
|
|
5
|
+
the terms of the GNU General Public License as published by the Free Software
|
|
6
|
+
Foundation, either version 3 of the License, or (at your option) any later
|
|
7
|
+
version.
|
|
8
|
+
|
|
9
|
+
This program is distributed in the hope that it will be useful, but
|
|
10
|
+
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
12
|
+
See the GNU General Public License for more details.
|
|
13
|
+
|
|
14
|
+
You should have received a copy of the GNU General Public License along with
|
|
15
|
+
this program. If not, see <http://www.gnu.org/licenses/>.
|
|
16
|
+
*/
|
|
17
|
+
import { deserializeArgs, serializeArgs } from "./serialization-for-ipc/json-n-binary";
|
|
18
|
+
|
|
19
|
+
declare var w3n: web3n.caps.W3N;
|
|
20
|
+
type IncomingConnection = web3n.rpc.service.IncomingConnection;
|
|
21
|
+
type IncomingMsg = web3n.rpc.service.IncomingMsg;
|
|
22
|
+
type OutgoingMsg = web3n.rpc.service.OutgoingMsg;
|
|
23
|
+
type PassedDatum = web3n.rpc.PassedDatum;
|
|
24
|
+
type Observer<T> = web3n.Observer<T>;
|
|
25
|
+
|
|
26
|
+
export type HandleObservingCall = (
|
|
27
|
+
obs: Observer<any>, ...requestArgs: any[]
|
|
28
|
+
) => (() => void);
|
|
29
|
+
|
|
30
|
+
export type HandleReqReplyCall = (...requestArgs: any[]) => Promise<any>;
|
|
31
|
+
|
|
32
|
+
export interface TransformOpts {
|
|
33
|
+
unpackRequest?: ((req: PassedDatum|undefined) => any[]) | 'noop';
|
|
34
|
+
packReply?: ((reply: any) => PassedDatum|undefined) | 'noop';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function surroundWithReqReplySerialization(
|
|
38
|
+
srv: object|undefined, func: HandleReqReplyCall,
|
|
39
|
+
transforms: TransformOpts|undefined
|
|
40
|
+
): ReqReplyHandler {
|
|
41
|
+
return async requestData => {
|
|
42
|
+
const args = argsFromPassedDatum(
|
|
43
|
+
requestData, transforms?.unpackRequest
|
|
44
|
+
);
|
|
45
|
+
const result = (args ?
|
|
46
|
+
await func.call(srv, ...args) :
|
|
47
|
+
await func.call(srv)
|
|
48
|
+
);
|
|
49
|
+
return toPassedDatum(result, transforms?.packReply);
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function argsFromPassedDatum(
|
|
54
|
+
data: PassedDatum|undefined, unpack: TransformOpts['unpackRequest']
|
|
55
|
+
): (any[])|undefined {
|
|
56
|
+
if (!data) { return; }
|
|
57
|
+
if (unpack) {
|
|
58
|
+
if (unpack === 'noop') {
|
|
59
|
+
return [ data.bytes ];
|
|
60
|
+
} else {
|
|
61
|
+
return unpack(data);
|
|
62
|
+
}
|
|
63
|
+
} else {
|
|
64
|
+
const { bytes, passedByReference } = data;
|
|
65
|
+
return (bytes ? deserializeArgs(bytes, passedByReference) : undefined);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function toPassedDatum(
|
|
70
|
+
data: any, pack: TransformOpts['packReply']
|
|
71
|
+
): PassedDatum|undefined {
|
|
72
|
+
if (data === undefined) { return; }
|
|
73
|
+
if (pack) {
|
|
74
|
+
if (pack === 'noop') {
|
|
75
|
+
if (!ArrayBuffer.isView(data)) { throw new Error(
|
|
76
|
+
`Method returned non-binary, while no serialization is set`
|
|
77
|
+
); }
|
|
78
|
+
return { bytes: data as Uint8Array };
|
|
79
|
+
}
|
|
80
|
+
return pack(data);
|
|
81
|
+
} else {
|
|
82
|
+
return serializeArgs([ data ]);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
type ReqReplyHandler = (
|
|
87
|
+
req: PassedDatum|undefined
|
|
88
|
+
) => Promise<PassedDatum|undefined>;
|
|
89
|
+
|
|
90
|
+
function surroundObsWithSerialization(
|
|
91
|
+
srv: object|undefined, func: HandleObservingCall,
|
|
92
|
+
transforms: TransformOpts|undefined
|
|
93
|
+
): ObservableHandler {
|
|
94
|
+
return (requestData, obs) => {
|
|
95
|
+
const args = argsFromPassedDatum(
|
|
96
|
+
requestData, transforms?.unpackRequest
|
|
97
|
+
);
|
|
98
|
+
const obsWrap: Observer<any> = {
|
|
99
|
+
next: ev => obs.next!(toPassedDatum(ev, transforms?.packReply)),
|
|
100
|
+
complete: obs.complete,
|
|
101
|
+
error: obs.error
|
|
102
|
+
};
|
|
103
|
+
return (args ?
|
|
104
|
+
func.call(srv, obsWrap, ...args) :
|
|
105
|
+
func.call(srv, obsWrap)
|
|
106
|
+
);
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
type ObservableHandler = (
|
|
111
|
+
req: PassedDatum|undefined, obs: Observer<PassedDatum|undefined>
|
|
112
|
+
) => (() => void);
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class ConnectionState {
|
|
116
|
+
|
|
117
|
+
private readonly calls = new Map<number, { cancel?: () => void; }>();
|
|
118
|
+
private isRunning = true;
|
|
119
|
+
|
|
120
|
+
constructor(
|
|
121
|
+
private readonly disconnect: () => void
|
|
122
|
+
) {}
|
|
123
|
+
|
|
124
|
+
acceptsMsgs(): boolean {
|
|
125
|
+
return this.isRunning;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
stop(): void {
|
|
129
|
+
if (!this.isRunning) { return; }
|
|
130
|
+
this.isRunning = false;
|
|
131
|
+
this.disconnect();
|
|
132
|
+
for (const { cancel } of this.calls.values()) {
|
|
133
|
+
if (cancel) {
|
|
134
|
+
cancel();
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
this.calls.clear();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
hasCall(callNum: number): boolean {
|
|
141
|
+
return this.calls.has(callNum);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
cancelCall(callNum: number): void {
|
|
145
|
+
const call = this.calls.get(callNum);
|
|
146
|
+
if (!call) { return; }
|
|
147
|
+
this.calls.delete(callNum);
|
|
148
|
+
if (call.cancel) {
|
|
149
|
+
call.cancel();
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
completeCall(callNum: number): void {
|
|
154
|
+
this.calls.delete(callNum);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
registerReqReplyCall(callNum: number): void {
|
|
158
|
+
this.calls.set(callNum, {});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
registerObservableCall(callNum: number, cancel: () => void): void {
|
|
162
|
+
this.calls.set(callNum, { cancel });
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
export abstract class IPCWrap {
|
|
169
|
+
|
|
170
|
+
protected readonly connections =
|
|
171
|
+
new Map<IncomingConnection, ConnectionState>();
|
|
172
|
+
private readonly methods = new Map<string, {
|
|
173
|
+
obs?: ObservableHandler; reqRep?: ReqReplyHandler;
|
|
174
|
+
}>();
|
|
175
|
+
|
|
176
|
+
constructor(
|
|
177
|
+
public readonly srvName: string
|
|
178
|
+
) {}
|
|
179
|
+
|
|
180
|
+
addReqReplyMethod(
|
|
181
|
+
method: string, srv: object|undefined, func: HandleReqReplyCall,
|
|
182
|
+
transforms?: TransformOpts
|
|
183
|
+
): void {
|
|
184
|
+
this.methods.set(method, {
|
|
185
|
+
reqRep: surroundWithReqReplySerialization(srv, func, transforms)
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
exposeReqReplyMethods<T extends object>(
|
|
190
|
+
srv: T, methods: (keyof T)[],
|
|
191
|
+
transforms?: { [method in keyof T]: TransformOpts; }
|
|
192
|
+
): void {
|
|
193
|
+
for (const method of methods) {
|
|
194
|
+
const func = srv[method] as HandleReqReplyCall;
|
|
195
|
+
this.addReqReplyMethod(
|
|
196
|
+
func.name, srv, func, (transforms ? transforms[method] : undefined)
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
addObservableMethod(
|
|
202
|
+
method: string, srv: object|undefined, func: HandleObservingCall,
|
|
203
|
+
transforms?: TransformOpts
|
|
204
|
+
): void {
|
|
205
|
+
this.methods.set(method as string, {
|
|
206
|
+
obs: surroundObsWithSerialization(srv, func, transforms)
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
exposeObservableMethods<T extends object>(
|
|
211
|
+
srv: T, methods: (keyof T)[],
|
|
212
|
+
transforms?: { [method in keyof T]: TransformOpts; }
|
|
213
|
+
): void {
|
|
214
|
+
for (const method of methods) {
|
|
215
|
+
const func = srv[method] as HandleObservingCall;
|
|
216
|
+
this.addObservableMethod(
|
|
217
|
+
func.name, srv, func, (transforms ? transforms[method] : undefined)
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
private async onMsg(
|
|
223
|
+
connection: IncomingConnection, connectionState: ConnectionState,
|
|
224
|
+
msg: IncomingMsg
|
|
225
|
+
): Promise<void> {
|
|
226
|
+
if (!connectionState.acceptsMsgs()) { return; }
|
|
227
|
+
if (msg.msgType === 'start') {
|
|
228
|
+
const { callNum, method, data: requestData } = msg;
|
|
229
|
+
if (connectionState.hasCall(callNum)) { return; }
|
|
230
|
+
const m = this.methods.get(method);
|
|
231
|
+
if (!m) {
|
|
232
|
+
await connection.send({
|
|
233
|
+
callNum, callStatus: 'error', err: `Method ${method} not found`
|
|
234
|
+
});
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
const { obs, reqRep } = m;
|
|
238
|
+
if (reqRep) {
|
|
239
|
+
connectionState.registerReqReplyCall(callNum);
|
|
240
|
+
await this.callReqReplyHandler(
|
|
241
|
+
connection, connectionState, reqRep, callNum, requestData
|
|
242
|
+
);
|
|
243
|
+
} else if (obs) {
|
|
244
|
+
const cancelCall = this.callObsHandler(
|
|
245
|
+
connection, connectionState, obs, callNum, requestData
|
|
246
|
+
);
|
|
247
|
+
connectionState.registerObservableCall(callNum, cancelCall);
|
|
248
|
+
}
|
|
249
|
+
} else if (msg.msgType === 'cancel') {
|
|
250
|
+
const { callNum } = msg;
|
|
251
|
+
connectionState.cancelCall(callNum);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
private async callReqReplyHandler(
|
|
256
|
+
connection: IncomingConnection, connectionState: ConnectionState,
|
|
257
|
+
reqRep: ReqReplyHandler,
|
|
258
|
+
callNum: number, requestData: PassedDatum|undefined
|
|
259
|
+
): Promise<void> {
|
|
260
|
+
let reply: OutgoingMsg;
|
|
261
|
+
try {
|
|
262
|
+
const data = await reqRep(requestData);
|
|
263
|
+
reply = {
|
|
264
|
+
callNum, callStatus: 'end', data
|
|
265
|
+
};
|
|
266
|
+
} catch (err) {
|
|
267
|
+
reply = {
|
|
268
|
+
callNum, callStatus: 'error', err
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
if (!connectionState.hasCall(callNum)) { return; }
|
|
272
|
+
connectionState.completeCall(callNum);
|
|
273
|
+
await connection.send(reply);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
private callObsHandler(
|
|
277
|
+
connection: IncomingConnection, connectionState: ConnectionState,
|
|
278
|
+
obs: ObservableHandler,
|
|
279
|
+
callNum: number, requestData: PassedDatum|undefined
|
|
280
|
+
): () => void {
|
|
281
|
+
return obs(requestData, {
|
|
282
|
+
next: data => connection.send({
|
|
283
|
+
callNum, callStatus: 'interim', data
|
|
284
|
+
}),
|
|
285
|
+
complete: async () => {
|
|
286
|
+
if (!connectionState.hasCall(callNum)) { return; }
|
|
287
|
+
connectionState.completeCall(callNum);
|
|
288
|
+
await connection.send({
|
|
289
|
+
callNum, callStatus: 'end'
|
|
290
|
+
});
|
|
291
|
+
},
|
|
292
|
+
error: async err => {
|
|
293
|
+
if (!connectionState.hasCall(callNum)) { return; }
|
|
294
|
+
connectionState.completeCall(callNum);
|
|
295
|
+
await connection.send({
|
|
296
|
+
callNum, callStatus: 'error', err
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
startIPC(): () => void {
|
|
303
|
+
return w3n.rpc!.exposeService!(this.srvName, {
|
|
304
|
+
next: connection => this.onConnection(connection),
|
|
305
|
+
complete: () => this.onListeningCompletion(),
|
|
306
|
+
error: err => this.onListeningError(err)
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
stopIPC(): void {
|
|
311
|
+
for (const connectionState of this.connections.values()) {
|
|
312
|
+
connectionState.stop();
|
|
313
|
+
}
|
|
314
|
+
this.connections.clear();
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
protected async onConnection(
|
|
318
|
+
connection: IncomingConnection
|
|
319
|
+
): Promise<void> {
|
|
320
|
+
const disconnect = connection.watch({
|
|
321
|
+
next: msg => this.onMsg(connection, connectionState, msg),
|
|
322
|
+
complete: () => this.onConnectionCompletion(
|
|
323
|
+
connection, connectionState
|
|
324
|
+
),
|
|
325
|
+
error: err => this.onConnectionError(connection, connectionState, err)
|
|
326
|
+
});
|
|
327
|
+
const connectionState = new ConnectionState(() => {
|
|
328
|
+
this.connections.delete(connection);
|
|
329
|
+
disconnect();
|
|
330
|
+
});
|
|
331
|
+
this.connections.set(connection, connectionState);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
protected async onConnectionCompletion(
|
|
335
|
+
connection: IncomingConnection, connectionState: ConnectionState
|
|
336
|
+
): Promise<void> {
|
|
337
|
+
this.connections.delete(connection);
|
|
338
|
+
connectionState.stop();
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
protected async onConnectionError(
|
|
342
|
+
connection: IncomingConnection, connectionState: ConnectionState, err: any
|
|
343
|
+
): Promise<void> {
|
|
344
|
+
this.connections.delete(connection);
|
|
345
|
+
connectionState.stop();
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
protected abstract onListeningCompletion(): Promise<void>;
|
|
349
|
+
|
|
350
|
+
protected abstract onListeningError(err: any): Promise<void>;
|
|
351
|
+
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
export class SingleConnectionIPCWrap extends IPCWrap {
|
|
356
|
+
|
|
357
|
+
constructor(srvName: string) {
|
|
358
|
+
super(srvName);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
protected async onListeningCompletion(): Promise<void> {
|
|
362
|
+
w3n.closeSelf!();
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
protected async onListeningError(err: any): Promise<void> {
|
|
366
|
+
if (w3n.log) {
|
|
367
|
+
await w3n.log(
|
|
368
|
+
'error', `Error in listening for incoming connections`, err
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
w3n.closeSelf!();
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
protected async onConnectionCompletion(
|
|
375
|
+
connection: IncomingConnection, connectionState: ConnectionState
|
|
376
|
+
): Promise<void> {
|
|
377
|
+
super.onConnectionCompletion(connection, connectionState);
|
|
378
|
+
w3n.closeSelf!();
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
protected async onConnectionError(
|
|
382
|
+
connection: IncomingConnection, connectionState: ConnectionState, err: any
|
|
383
|
+
): Promise<void> {
|
|
384
|
+
super.onConnectionError(connection, connectionState, err);
|
|
385
|
+
if (w3n.log) {
|
|
386
|
+
await w3n.log(
|
|
387
|
+
'error', `Error in listening for calls`, err
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
w3n.closeSelf!();
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
export class MultiConnectionIPCWrap extends IPCWrap {
|
|
396
|
+
|
|
397
|
+
constructor(srvName: string) {
|
|
398
|
+
super(srvName);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
protected async onListeningCompletion(): Promise<void> {}
|
|
402
|
+
|
|
403
|
+
protected async onListeningError(err: any): Promise<void> {
|
|
404
|
+
if (w3n.log) {
|
|
405
|
+
await w3n.log(
|
|
406
|
+
'error', `Error in listening for incoming connections`, err
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
protected async onConnectionError(
|
|
412
|
+
connection: IncomingConnection, connectionState: ConnectionState, err: any
|
|
413
|
+
): Promise<void> {
|
|
414
|
+
super.onConnectionError(connection, connectionState, err);
|
|
415
|
+
if (w3n.log) {
|
|
416
|
+
await w3n.log(
|
|
417
|
+
'error', `Error in listening for calls`, err
|
|
418
|
+
);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright (C) 2016 - 2018 3NSoft Inc.
|
|
3
|
+
|
|
4
|
+
This program is free software: you can redistribute it and/or modify it under
|
|
5
|
+
the terms of the GNU General Public License as published by the Free Software
|
|
6
|
+
Foundation, either version 3 of the License, or (at your option) any later
|
|
7
|
+
version.
|
|
8
|
+
|
|
9
|
+
This program is distributed in the hope that it will be useful, but
|
|
10
|
+
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
12
|
+
See the GNU General Public License for more details.
|
|
13
|
+
|
|
14
|
+
You should have received a copy of the GNU General Public License along with
|
|
15
|
+
this program. If not, see <http://www.gnu.org/licenses/>.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* This function creates a copy of json entity with a caveat that Buffer and
|
|
21
|
+
* byte arrays are shared between original and a copy.
|
|
22
|
+
* @param orig is an object, which copy is created. Buffer and byte arrays
|
|
23
|
+
* passed through like primitives.
|
|
24
|
+
* @param excludeTopFields is an optional list of fields to exclude from copy.
|
|
25
|
+
*/
|
|
26
|
+
export function copyJSON<T>(orig: T, excludeTopFields?: string[]): T {
|
|
27
|
+
const origType = typeof orig;
|
|
28
|
+
if (origType !== 'object') {
|
|
29
|
+
return ((origType !== 'function') ? orig : (undefined as any));
|
|
30
|
+
}
|
|
31
|
+
if (orig === null) { return (null as any); }
|
|
32
|
+
if (ArrayBuffer.isView(orig)) { return (orig as any); }
|
|
33
|
+
if (Array.isArray(orig)) {
|
|
34
|
+
const arr: any[] = orig;
|
|
35
|
+
const c: any[] = [];
|
|
36
|
+
for (let i=0; i < arr.length; i+=1) {
|
|
37
|
+
c[i] = copyJSON(arr[i]);
|
|
38
|
+
}
|
|
39
|
+
return (c as any);
|
|
40
|
+
} else {
|
|
41
|
+
const c = ({} as T);
|
|
42
|
+
const fields = Object.keys(orig!);
|
|
43
|
+
if (excludeTopFields) {
|
|
44
|
+
for (const f of fields) {
|
|
45
|
+
if (excludeTopFields.includes(f)) { continue; }
|
|
46
|
+
(c as any)[f] = copyJSON<any>((orig as any)[f]);
|
|
47
|
+
}
|
|
48
|
+
} else {
|
|
49
|
+
for (const f of fields) {
|
|
50
|
+
(c as any)[f] = copyJSON<any>((orig as any)[f]);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return c;
|
|
54
|
+
}
|
|
55
|
+
}
|