socket-function 0.8.38 → 0.8.40
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/SocketFunction.ts +10 -2
- package/package.json +2 -2
- package/require/RequireController.ts +8 -2
- package/require/require.js +11 -26
- package/src/CallFactory.ts +48 -7
- package/src/batching.ts +33 -0
- package/src/caching.ts +5 -1
- package/src/fixLargeNetworkCalls.ts +9 -0
- package/src/misc.ts +3 -0
- package/src/profiling/measure.ts +9 -5
- package/src/webSocketServer.ts +1 -0
- package/src/websocketFactory.ts +1 -1
package/SocketFunction.ts
CHANGED
|
@@ -9,6 +9,7 @@ import { Args, MaybePromise } from "./src/types";
|
|
|
9
9
|
import { setDefaultHTTPCall } from "./src/callHTTPHandler";
|
|
10
10
|
import debugbreak from "debugbreak";
|
|
11
11
|
import { lazy } from "./src/caching";
|
|
12
|
+
import { delay } from "./src/batching";
|
|
12
13
|
|
|
13
14
|
module.allowclient = true;
|
|
14
15
|
|
|
@@ -76,7 +77,7 @@ export class SocketFunction {
|
|
|
76
77
|
return shape as any as SocketExposedShape;
|
|
77
78
|
});
|
|
78
79
|
|
|
79
|
-
|
|
80
|
+
void Promise.resolve().then(() => {
|
|
80
81
|
registerClass(classGuid, instance as SocketExposedInterface, getShape());
|
|
81
82
|
});
|
|
82
83
|
|
|
@@ -115,7 +116,7 @@ export class SocketFunction {
|
|
|
115
116
|
_classGuid: classGuid,
|
|
116
117
|
};
|
|
117
118
|
|
|
118
|
-
|
|
119
|
+
void Promise.resolve().then(() => {
|
|
119
120
|
let onMount = getDefaultHooks?.().onMount;
|
|
120
121
|
if (onMount) {
|
|
121
122
|
let callbacks = SocketFunction.onMountCallbacks.get(classGuid);
|
|
@@ -200,6 +201,8 @@ export class SocketFunction {
|
|
|
200
201
|
public static mountedNodeId: string = "";
|
|
201
202
|
public static mountedIP: string = "";
|
|
202
203
|
private static hasMounted = false;
|
|
204
|
+
private static onMountCallback: () => void = () => { };
|
|
205
|
+
public static mountPromise: Promise<void> = new Promise(r => this.onMountCallback = r);
|
|
203
206
|
public static async mount(config: SocketServerConfig) {
|
|
204
207
|
if (this.mountedNodeId) {
|
|
205
208
|
throw new Error("SocketFunction already mounted, mounting twice in one thread is not allowed.");
|
|
@@ -209,6 +212,10 @@ export class SocketFunction {
|
|
|
209
212
|
if (config.ip) {
|
|
210
213
|
this.mountedIP = config.ip;
|
|
211
214
|
}
|
|
215
|
+
|
|
216
|
+
// Wait for any additionals functions to expose themselves
|
|
217
|
+
await delay("immediate");
|
|
218
|
+
|
|
212
219
|
this.mountedNodeId = await startSocketServer(config);
|
|
213
220
|
this.hasMounted = true;
|
|
214
221
|
for (let classGuid of SocketFunction.exposedClasses) {
|
|
@@ -218,6 +225,7 @@ export class SocketFunction {
|
|
|
218
225
|
await callback();
|
|
219
226
|
}
|
|
220
227
|
}
|
|
228
|
+
this.onMountCallback();
|
|
221
229
|
return this.mountedNodeId;
|
|
222
230
|
}
|
|
223
231
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "socket-function",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.40",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"note1": "note on node-forge fork, see https://github.com/digitalbazaar/forge/issues/744 for details",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"node-forge": "https://github.com/sliftist/forge#name",
|
|
11
11
|
"preact": "^10.10.6",
|
|
12
12
|
"rdtsc-now": "^0.3.0",
|
|
13
|
-
"typenode": "^4.9.4-
|
|
13
|
+
"typenode": "^4.9.4-f",
|
|
14
14
|
"ws": "^8.8.0"
|
|
15
15
|
},
|
|
16
16
|
"scripts": {
|
|
@@ -13,6 +13,11 @@ declare global {
|
|
|
13
13
|
/** Indicates the module is allowed clientside. */
|
|
14
14
|
allowclient?: boolean;
|
|
15
15
|
|
|
16
|
+
/** Causes the module to not preload, requiring `await import()` for it to load correctly
|
|
17
|
+
* - Shouldn't be set recursively, otherwise nested packages will break.
|
|
18
|
+
*/
|
|
19
|
+
lazyload?: boolean;
|
|
20
|
+
|
|
16
21
|
/** Indicates the module is definitely not allowed clientside */
|
|
17
22
|
serveronly?: boolean;
|
|
18
23
|
|
|
@@ -118,7 +123,8 @@ class RequireControllerBase {
|
|
|
118
123
|
let modules: {
|
|
119
124
|
[resolvedPath: string]: SerializedModule;
|
|
120
125
|
} = Object.create(null);
|
|
121
|
-
function addModule(module: NodeJS.Module) {
|
|
126
|
+
function addModule(module: NodeJS.Module, rootImport = false) {
|
|
127
|
+
if (!rootImport && module.lazyload) return;
|
|
122
128
|
if (!module.requireControllerSeqNum) {
|
|
123
129
|
module.requireControllerSeqNum = nextModuleSeqNum++;
|
|
124
130
|
}
|
|
@@ -223,7 +229,7 @@ class RequireControllerBase {
|
|
|
223
229
|
clientModule = createNotFoundModule(`Module ${pathRequest} (resolved to ${resolvedPath}) is not allowed clientside (set module.allowclient in it, or call setFlag when it is imported).`);
|
|
224
230
|
}
|
|
225
231
|
|
|
226
|
-
addModule(clientModule);
|
|
232
|
+
addModule(clientModule, true);
|
|
227
233
|
}
|
|
228
234
|
|
|
229
235
|
return { requestsResolvedPaths, modules, requireSeqNumProcessId };
|
package/require/require.js
CHANGED
|
@@ -242,6 +242,15 @@
|
|
|
242
242
|
}
|
|
243
243
|
|
|
244
244
|
let resolvedPath = serializedModule.requests[request];
|
|
245
|
+
if (resolvedPath !== "NOTALLOWEDCLIENTSIDE" && !serializedModules[resolvedPath]) {
|
|
246
|
+
if (!asyncIsFine) {
|
|
247
|
+
console.warn(`Accessed unexpected module %c${request}%c in %c${module.id}%c\n\tTreating it as an async require.\n\tAll modules require synchronously clientside must be required serverside at a module level.`,
|
|
248
|
+
"color: red", "color: unset",
|
|
249
|
+
"color: red", "color: unset",
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
return rootRequire(resolvedPath);
|
|
253
|
+
}
|
|
245
254
|
|
|
246
255
|
let exportsOverride = undefined;
|
|
247
256
|
if (resolvedPath === "NOTALLOWEDCLIENTSIDE" || !serializedModules[resolvedPath].allowclient) {
|
|
@@ -337,6 +346,7 @@
|
|
|
337
346
|
module.id = resolvedId;
|
|
338
347
|
module.filename = serializedModule?.filename;
|
|
339
348
|
module.exports = {};
|
|
349
|
+
module.exports.default = module.exports;
|
|
340
350
|
module.children = [];
|
|
341
351
|
|
|
342
352
|
module.load = load;
|
|
@@ -383,19 +393,6 @@
|
|
|
383
393
|
|
|
384
394
|
let dirname = module.filename.replace(/\\/g, "/").split("/").slice(0, -1).join("/");
|
|
385
395
|
|
|
386
|
-
var __createBinding = (Object.create ? (function (o, m, k, k2) {
|
|
387
|
-
if (k2 === undefined) k2 = k;
|
|
388
|
-
Object.defineProperty(o, k2, { enumerable: true, get: function () { return m[k]; } });
|
|
389
|
-
}) : (function (o, m, k, k2) {
|
|
390
|
-
if (k2 === undefined) k2 = k;
|
|
391
|
-
o[k2] = m[k];
|
|
392
|
-
}));
|
|
393
|
-
var __setModuleDefault = (Object.create ? (function (o, v) {
|
|
394
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
395
|
-
}) : function (o, v) {
|
|
396
|
-
o["default"] = v;
|
|
397
|
-
});
|
|
398
|
-
|
|
399
396
|
let time = Date.now();
|
|
400
397
|
currentModuleEvaluationStack.push(module.filename);
|
|
401
398
|
try {
|
|
@@ -405,21 +402,9 @@
|
|
|
405
402
|
// which checks for unloadedModule and returns undefined in that case.
|
|
406
403
|
__importStar(mod) {
|
|
407
404
|
if (mod[unloadedModule]) return undefined;
|
|
408
|
-
|
|
409
|
-
var result = {};
|
|
410
|
-
if (mod !== null && mod !== undefined) {
|
|
411
|
-
for (var k in mod) {
|
|
412
|
-
if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) {
|
|
413
|
-
__createBinding(result, mod, k);
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
__setModuleDefault(result, mod);
|
|
418
|
-
return result;
|
|
405
|
+
return mod;
|
|
419
406
|
},
|
|
420
407
|
__importDefault(mod) {
|
|
421
|
-
// If typescript isn't going to complain about importing from a module with no default export,
|
|
422
|
-
// then we'll just change our implementation to work the same way as typescript types...
|
|
423
408
|
return mod.default ? mod : { default: mod };
|
|
424
409
|
},
|
|
425
410
|
},
|
package/src/CallFactory.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CallerContext, CallerContextBase, CallType, FullCallType } from "../SocketFunctionTypes";
|
|
2
2
|
import * as ws from "ws";
|
|
3
3
|
import { performLocalCall } from "./callManager";
|
|
4
|
-
import { convertErrorStackToError, formatNumberSuffixed, isNode } from "./misc";
|
|
4
|
+
import { convertErrorStackToError, formatNumberSuffixed, isNode, list } from "./misc";
|
|
5
5
|
import { createWebsocketFactory, getTLSSocket } from "./websocketFactory";
|
|
6
6
|
import { SocketFunction } from "../SocketFunction";
|
|
7
7
|
import { gzip } from "zlib";
|
|
@@ -10,7 +10,9 @@ import { getClientNodeId, getNodeIdLocation, registerNodeClient } from "./nodeCa
|
|
|
10
10
|
import debugbreak from "debugbreak";
|
|
11
11
|
import { lazy } from "./caching";
|
|
12
12
|
import { JSONLACKS } from "./JSONLACKS/JSONLACKS";
|
|
13
|
-
import { red } from "./formatting/logColors";
|
|
13
|
+
import { red, yellow } from "./formatting/logColors";
|
|
14
|
+
import { isSplitableArray, markArrayAsSplitable } from "./fixLargeNetworkCalls";
|
|
15
|
+
import { delay } from "./batching";
|
|
14
16
|
|
|
15
17
|
const MIN_RETRY_DELAY = 1000;
|
|
16
18
|
|
|
@@ -55,6 +57,8 @@ export interface SenderInterface {
|
|
|
55
57
|
addEventListener(event: "message", listener: (data: ws.RawData | ws.MessageEvent | string) => void): void;
|
|
56
58
|
|
|
57
59
|
readyState: number;
|
|
60
|
+
|
|
61
|
+
ping?(): void;
|
|
58
62
|
}
|
|
59
63
|
|
|
60
64
|
export async function createCallFactory(
|
|
@@ -81,6 +85,18 @@ export async function createCallFactory(
|
|
|
81
85
|
// in return calls.
|
|
82
86
|
let nextSeqNum = Date.now() + Math.random();
|
|
83
87
|
|
|
88
|
+
// NOTE: I'm not sure if this is needed, I thought it was, but... now I think
|
|
89
|
+
// it probably isn't...
|
|
90
|
+
// if (webSocketBase?.readyState === 1 /* OPEN */ && webSocketBase.ping) {
|
|
91
|
+
// // Heartbeat loop, otherwise onDisconnect is never called.
|
|
92
|
+
// ((async () => {
|
|
93
|
+
// while (webSocketBase?.readyState === 1 /* OPEN */ && webSocketBase.ping) {
|
|
94
|
+
// await delay(1000 * 60);
|
|
95
|
+
// webSocketBase.ping?.();
|
|
96
|
+
// }
|
|
97
|
+
// }))().catch(() => { });
|
|
98
|
+
// }
|
|
99
|
+
|
|
84
100
|
let lastConnectionAttempt = 0;
|
|
85
101
|
|
|
86
102
|
let callerContext: CallerContextBase = {
|
|
@@ -115,6 +131,35 @@ export async function createCallFactory(
|
|
|
115
131
|
if (time > SocketFunction.WIRE_WARN_TIME) {
|
|
116
132
|
console.log(red(`Slow serialize, took ${time}ms to serialize ${data.byteLength} bytes. For ${call.classGuid}.${call.functionName}`));
|
|
117
133
|
}
|
|
134
|
+
|
|
135
|
+
if (data.byteLength > SocketFunction.MAX_MESSAGE_SIZE * 1.5) {
|
|
136
|
+
let splitArgIndex = call.args.findIndex(isSplitableArray);
|
|
137
|
+
if (splitArgIndex >= 0) {
|
|
138
|
+
console.log(yellow(`Splitting large call due to large args: ${call.classGuid}.${call.functionName}`));
|
|
139
|
+
let SPLIT_GROUPS = 10;
|
|
140
|
+
let splitArg = call.args[splitArgIndex] as unknown[];
|
|
141
|
+
let subCalls = list(SPLIT_GROUPS).map(index => {
|
|
142
|
+
let start = Math.floor(index / SPLIT_GROUPS * splitArg.length);
|
|
143
|
+
let end = Math.floor((index + 1) / SPLIT_GROUPS * splitArg.length);
|
|
144
|
+
return splitArg.slice(start, end);
|
|
145
|
+
}).filter(x => x.length > 0);
|
|
146
|
+
|
|
147
|
+
let calls = subCalls.map(async splitList => {
|
|
148
|
+
let subCall = { ...call };
|
|
149
|
+
subCall.args = subCall.args.slice();
|
|
150
|
+
subCall.args[splitArgIndex] = markArrayAsSplitable(splitList);
|
|
151
|
+
await callFactory.performCall(subCall);
|
|
152
|
+
});
|
|
153
|
+
await Promise.allSettled(calls);
|
|
154
|
+
await Promise.all(calls);
|
|
155
|
+
// Eh... we COULD return the array of results, but... then the result would sometimes be an array,
|
|
156
|
+
// some times not, so, it is better to return a string which will make it more clear why it sometimes varies.
|
|
157
|
+
return "CALLS_SPLIT_DUE_TO_LARGE_ARGS";
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
throw new Error(`Call too large to send (${call.classGuid}.${call.functionName}, size: ${data.byteLength} > ${SocketFunction.MAX_MESSAGE_SIZE}). If you need to handle very large static data use some external service, such as Backblaze B2 or AWS S3. Or consider fragmenting data at an application level, because sending large data will cause large lag spikes for other clients using this server. Or, if absolutely required, set SocketFunction.MAX_MESSAGE_SIZE to a higher value.`);
|
|
161
|
+
}
|
|
162
|
+
|
|
118
163
|
let resultPromise = new Promise((resolve, reject) => {
|
|
119
164
|
let callback = (result: InternalReturnType) => {
|
|
120
165
|
if (SocketFunction.logMessages) {
|
|
@@ -130,10 +175,6 @@ export async function createCallFactory(
|
|
|
130
175
|
pendingCalls.set(seqNum, { callback, data, call: fullCall });
|
|
131
176
|
});
|
|
132
177
|
|
|
133
|
-
if (data.byteLength > SocketFunction.MAX_MESSAGE_SIZE * 1.5) {
|
|
134
|
-
throw new Error(`Call too large to send (${call.classGuid}.${call.functionName}, size: ${data.byteLength} > ${SocketFunction.MAX_MESSAGE_SIZE}). If you need to handle very large static data use some external service, such as Backblaze B2 or AWS S3. Or consider fragmenting data at an application level, because sending large data will cause large lag spikes for other clients using this server. Or, if absolutely required, set SocketFunction.MAX_MESSAGE_SIZE to a higher value.`);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
178
|
await send(data);
|
|
138
179
|
|
|
139
180
|
return await resultPromise;
|
|
@@ -333,7 +374,7 @@ export async function createCallFactory(
|
|
|
333
374
|
}
|
|
334
375
|
throw new Error(`Unhandled data type ${typeof message}`);
|
|
335
376
|
} catch (e: any) {
|
|
336
|
-
debugbreak(
|
|
377
|
+
debugbreak(2);
|
|
337
378
|
debugger;
|
|
338
379
|
console.error(e.stack);
|
|
339
380
|
}
|
package/src/batching.ts
CHANGED
|
@@ -91,4 +91,37 @@ export function runInSerial<T extends (...args: any[]) => Promise<any>>(fnc: T):
|
|
|
91
91
|
updateQueue[0]?.();
|
|
92
92
|
}
|
|
93
93
|
}) as T;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function runInfinitePoll(
|
|
97
|
+
delayTime: number,
|
|
98
|
+
fnc: () => Promise<void> | void
|
|
99
|
+
) {
|
|
100
|
+
void (async () => {
|
|
101
|
+
while (true) {
|
|
102
|
+
await delay(delayTime);
|
|
103
|
+
try {
|
|
104
|
+
await fnc();
|
|
105
|
+
} catch (e: any) {
|
|
106
|
+
console.error(`Error in infinite poll ${fnc.name || fnc.toString().slice(0, 100).split("\n").slice(0, 2).join("\n")} (continuing poll loop)\n${e.stack}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
})();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export async function runInfinitePollCallAtStart(
|
|
113
|
+
delayTime: number,
|
|
114
|
+
fnc: () => Promise<void> | void
|
|
115
|
+
) {
|
|
116
|
+
void (async () => {
|
|
117
|
+
while (true) {
|
|
118
|
+
await delay(delayTime);
|
|
119
|
+
try {
|
|
120
|
+
await fnc();
|
|
121
|
+
} catch (e: any) {
|
|
122
|
+
console.error(`Error in infinite poll ${fnc.name} (continuing poll loop)\n${e.stack}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
})();
|
|
126
|
+
return await fnc();
|
|
94
127
|
}
|
package/src/caching.ts
CHANGED
|
@@ -3,7 +3,6 @@ import { AnyFunction, Args, canHaveChildren } from "./types";
|
|
|
3
3
|
|
|
4
4
|
export function lazy<T>(factory: () => T): () => T {
|
|
5
5
|
let value: { value: T } | undefined = undefined;
|
|
6
|
-
|
|
7
6
|
return () => {
|
|
8
7
|
if (!value) {
|
|
9
8
|
value = { value: factory() };
|
|
@@ -33,8 +32,10 @@ export function cacheEmptyArray<T>(array: T[]): T[] {
|
|
|
33
32
|
|
|
34
33
|
export function cache<Output, Key>(getValue: (key: Key) => Output): {
|
|
35
34
|
(key: Key): Output;
|
|
35
|
+
// NOTE: If you want to clear all, just make a new cache!
|
|
36
36
|
clear(key: Key): void;
|
|
37
37
|
forceSet(key: Key, value: Output): void;
|
|
38
|
+
getAllKeys(): Key[];
|
|
38
39
|
} {
|
|
39
40
|
let startingCalculating = new Set<Key>();
|
|
40
41
|
let values = new Map<Key, Output>();
|
|
@@ -61,6 +62,9 @@ export function cache<Output, Key>(getValue: (key: Key) => Output): {
|
|
|
61
62
|
values.set(key, value);
|
|
62
63
|
startingCalculating.add(key);
|
|
63
64
|
};
|
|
65
|
+
cache.getAllKeys = () => {
|
|
66
|
+
return [...values.keys()];
|
|
67
|
+
};
|
|
64
68
|
return cache;
|
|
65
69
|
}
|
|
66
70
|
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const arrayIsSplitable = Symbol.for("arrayIsSplitable");
|
|
2
|
+
export function markArrayAsSplitable<T>(data: T[]): T[] {
|
|
3
|
+
(data as any)[arrayIsSplitable] = true;
|
|
4
|
+
return data;
|
|
5
|
+
}
|
|
6
|
+
export function isSplitableArray<T>(data: T): data is T & (unknown[]) {
|
|
7
|
+
if (!Array.isArray(data)) return false;
|
|
8
|
+
return !!(data as any)[arrayIsSplitable];
|
|
9
|
+
}
|
package/src/misc.ts
CHANGED
|
@@ -139,6 +139,9 @@ export function getKeys(obj: unknown) {
|
|
|
139
139
|
}
|
|
140
140
|
return keyArray;
|
|
141
141
|
}
|
|
142
|
+
export function getStringKeys<T extends {}>(obj: T): ((keyof T) & string)[] {
|
|
143
|
+
return Object.keys(obj) as any;
|
|
144
|
+
}
|
|
142
145
|
|
|
143
146
|
if (isNode()) {
|
|
144
147
|
// TODO: Find a better place for this...
|
package/src/profiling/measure.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import debugbreak from "debugbreak";
|
|
2
2
|
import { formatTime, formatNumber } from "../formatting/format";
|
|
3
|
-
import { red, yellow, blue } from "../formatting/logColors";
|
|
3
|
+
import { red, yellow, blue, magenta } from "../formatting/logColors";
|
|
4
4
|
|
|
5
5
|
import { getOwnTime, getPendingOwnTimeInstances, getPendingOwnTimeObjs, OwnTimeObj } from "./getOwnTime";
|
|
6
6
|
import { addToStats, addToStatsValue, createStatsValue, getStatsTop, StatsValue } from "./stats";
|
|
@@ -12,6 +12,10 @@ export function enableMeasurements() {
|
|
|
12
12
|
}
|
|
13
13
|
measurementsEnabled = true;
|
|
14
14
|
}
|
|
15
|
+
/** NOTE: Must be called BEFORE anything else is imported! */
|
|
16
|
+
export function disableMeasurements() {
|
|
17
|
+
measurementsEnabled = false;
|
|
18
|
+
}
|
|
15
19
|
|
|
16
20
|
let functionsSkipped = 0;
|
|
17
21
|
|
|
@@ -104,9 +108,9 @@ export function logMeasureTable(
|
|
|
104
108
|
let totalTime = entries.map(x => getTime(x).sum).reduce((a, b) => a + b, 0);
|
|
105
109
|
|
|
106
110
|
console.log();
|
|
107
|
-
let title = yellow(`Profiled ${formatTime(totalTime)}`);
|
|
111
|
+
let title = yellow(`Profiled ${formatTime(totalTime)} (logged at ${new Date().toISOString()})`);
|
|
108
112
|
if (name) {
|
|
109
|
-
title
|
|
113
|
+
title = `(${blue(name)}) ${title}`;
|
|
110
114
|
}
|
|
111
115
|
console.log(title);
|
|
112
116
|
function percent(value: number) {
|
|
@@ -138,7 +142,7 @@ export function logMeasureTable(
|
|
|
138
142
|
|
|
139
143
|
let ownTimeTop = getStatsTop(getTime(entry));
|
|
140
144
|
if (ownTimeTop.topHeavy) {
|
|
141
|
-
output += red(` TOP ${percent(ownTimeTop.valueFraction)} of the time is owned by ${percent(ownTimeTop.countFraction)} of the calls`);
|
|
145
|
+
output += red(` TOP ${percent(ownTimeTop.valueFraction)} of the time is owned by ${percent(ownTimeTop.countFraction)} of the calls (${formatTime(ownTimeTop.value / ownTimeTop.count)} per * ${formatNumber(ownTimeTop.count)} = ${formatTime(ownTimeTop.value)})`);
|
|
142
146
|
}
|
|
143
147
|
|
|
144
148
|
if (entry.stillOpenCount > 0) {
|
|
@@ -209,7 +213,7 @@ interface ProfileEntry {
|
|
|
209
213
|
stillOpenCount: number;
|
|
210
214
|
}
|
|
211
215
|
|
|
212
|
-
let measurementsEnabled =
|
|
216
|
+
let measurementsEnabled = true;
|
|
213
217
|
|
|
214
218
|
let outstandingProfiles: MeasureProfile[] = [];
|
|
215
219
|
function recordOwnTime(ownTimeObj: OwnTimeObj) {
|
package/src/webSocketServer.ts
CHANGED
|
@@ -12,6 +12,7 @@ import debugbreak from "debugbreak";
|
|
|
12
12
|
import { getNodeId } from "./nodeCache";
|
|
13
13
|
import crypto from "crypto";
|
|
14
14
|
import { Watchable } from "./misc";
|
|
15
|
+
import { delay, runInfinitePoll } from "./batching";
|
|
15
16
|
|
|
16
17
|
export type SocketServerConfig = (
|
|
17
18
|
https.ServerOptions & {
|
package/src/websocketFactory.ts
CHANGED
|
@@ -38,7 +38,7 @@ export function createWebsocketFactory(): (nodeId: string) => SenderInterface {
|
|
|
38
38
|
console.log(`Connecting to ${address}:${port}`);
|
|
39
39
|
}
|
|
40
40
|
let webSocket = new ws.WebSocket(`wss://${address}:${port}`, {
|
|
41
|
-
ca:
|
|
41
|
+
ca: getTrustedCertificates()
|
|
42
42
|
});
|
|
43
43
|
let result = Object.assign(webSocket, { socket: undefined as tls.TLSSocket | undefined });
|
|
44
44
|
webSocket.once("upgrade", e => {
|