socket-function 0.9.4 → 0.9.5
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 +1 -1
- package/require/require.js +3 -1
- package/src/JSONLACKS/JSONLACKS.ts +12 -0
- package/src/batching.ts +13 -6
- package/src/caching.ts +41 -6
- package/src/misc.ts +12 -0
- package/src/profiling/getOwnTime.ts +17 -52
- package/src/profiling/measure.ts +22 -6
- package/src/webSocketServer.ts +5 -1
package/package.json
CHANGED
package/require/require.js
CHANGED
|
@@ -27,7 +27,9 @@
|
|
|
27
27
|
// https://nodejs.org/api/util.html#util_util_inherits_constructor_superconstructor
|
|
28
28
|
inherits(constructor, superConstructor) {
|
|
29
29
|
Object.setPrototypeOf(constructor.prototype, superConstructor.prototype);
|
|
30
|
-
}
|
|
30
|
+
},
|
|
31
|
+
TextDecoder: TextDecoder,
|
|
32
|
+
TextEncoder: TextEncoder,
|
|
31
33
|
},
|
|
32
34
|
buffer: { Buffer },
|
|
33
35
|
stream: {
|
|
@@ -68,6 +68,18 @@ export class JSONLACKS {
|
|
|
68
68
|
return Buffer.concat(buffers);
|
|
69
69
|
});
|
|
70
70
|
}
|
|
71
|
+
public static stringifyFileSync(obj: unknown[], config?: JSONLACKS_StringifyConfig): Buffer {
|
|
72
|
+
let serialized = JSONLACKS.escapeSpecialObjects(obj, config) as unknown[];
|
|
73
|
+
return measureBlock(function JSONstringifyAndJoin() {
|
|
74
|
+
let buffers: Buffer[] = [];
|
|
75
|
+
for (let i = 0; i < serialized.length; i += SERIALIZE_OBJECT_BATCH_COUNT) {
|
|
76
|
+
let str = serialized.slice(i, i + SERIALIZE_OBJECT_BATCH_COUNT).map(x => JSON.stringify(x) + "\n").join("");
|
|
77
|
+
buffers.push(Buffer.from(str));
|
|
78
|
+
}
|
|
79
|
+
// Break up into chunks, as string => Buffer i
|
|
80
|
+
return Buffer.concat(buffers);
|
|
81
|
+
});
|
|
82
|
+
}
|
|
71
83
|
// TIMING: Seems to be about 40X slower than JSON.parse unless extended is set to false,
|
|
72
84
|
// then it is about 2X slower (although it depends on the size and complexity of the objects!)
|
|
73
85
|
@measureFnc
|
package/src/batching.ts
CHANGED
|
@@ -113,15 +113,22 @@ export function batchFunction<Arg, Result = void>(
|
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
export function runInSerial<T extends (...args: any[]) => Promise<any>>(fnc: T): T {
|
|
116
|
-
let updateQueue: (
|
|
116
|
+
let updateQueue: { promise: Promise<void>; resolve: () => void; }[] = [];
|
|
117
117
|
|
|
118
118
|
return (async (...args: any[]) => {
|
|
119
|
+
let promise = {
|
|
120
|
+
promise: null as any as Promise<void>,
|
|
121
|
+
resolve: () => { },
|
|
122
|
+
};
|
|
123
|
+
promise.promise = new Promise<void>(resolve => {
|
|
124
|
+
promise.resolve = resolve;
|
|
125
|
+
});
|
|
119
126
|
const queueWasEmpty = updateQueue.length === 0;
|
|
120
|
-
if (
|
|
121
|
-
|
|
122
|
-
await new Promise<void>(resolve => updateQueue.push(resolve));
|
|
127
|
+
if (queueWasEmpty) {
|
|
128
|
+
promise.resolve();
|
|
123
129
|
}
|
|
124
|
-
updateQueue.push(
|
|
130
|
+
updateQueue.push(promise);
|
|
131
|
+
await promise.promise;
|
|
125
132
|
|
|
126
133
|
try {
|
|
127
134
|
return await fnc(...args);
|
|
@@ -129,7 +136,7 @@ export function runInSerial<T extends (...args: any[]) => Promise<any>>(fnc: T):
|
|
|
129
136
|
// Pop ourself off
|
|
130
137
|
updateQueue.shift();
|
|
131
138
|
// Resolve the next promise
|
|
132
|
-
updateQueue[0]?.();
|
|
139
|
+
updateQueue[0]?.resolve();
|
|
133
140
|
}
|
|
134
141
|
}) as T;
|
|
135
142
|
}
|
package/src/caching.ts
CHANGED
|
@@ -38,8 +38,10 @@ export function cache<Output, Key>(getValue: (key: Key) => Output): {
|
|
|
38
38
|
(key: Key): Output;
|
|
39
39
|
// NOTE: If you want to clear all, just make a new cache!
|
|
40
40
|
clear(key: Key): void;
|
|
41
|
+
clearAll(): void;
|
|
41
42
|
forceSet(key: Key, value: Output): void;
|
|
42
43
|
getAllKeys(): Key[];
|
|
44
|
+
get(key: Key): Output | undefined;
|
|
43
45
|
} {
|
|
44
46
|
let startingCalculating = new Set<Key>();
|
|
45
47
|
let values = new Map<Key, Output>();
|
|
@@ -69,6 +71,13 @@ export function cache<Output, Key>(getValue: (key: Key) => Output): {
|
|
|
69
71
|
cache.getAllKeys = () => {
|
|
70
72
|
return [...values.keys()];
|
|
71
73
|
};
|
|
74
|
+
cache.get = (key: Key) => {
|
|
75
|
+
return values.get(key);
|
|
76
|
+
};
|
|
77
|
+
cache.clearAll = () => {
|
|
78
|
+
values.clear();
|
|
79
|
+
startingCalculating.clear();
|
|
80
|
+
};
|
|
72
81
|
return cache;
|
|
73
82
|
}
|
|
74
83
|
|
|
@@ -114,6 +123,14 @@ export function cacheLimited<Output, Key>(
|
|
|
114
123
|
values.set(key, value);
|
|
115
124
|
startingCalculating.add(key);
|
|
116
125
|
};
|
|
126
|
+
get["clearKey"] = (key: Key) => {
|
|
127
|
+
values.delete(key);
|
|
128
|
+
startingCalculating.delete(key);
|
|
129
|
+
};
|
|
130
|
+
get["clear"] = () => {
|
|
131
|
+
values.clear();
|
|
132
|
+
startingCalculating.clear();
|
|
133
|
+
};
|
|
117
134
|
|
|
118
135
|
return get;
|
|
119
136
|
}
|
|
@@ -186,6 +203,7 @@ export function cacheArrayEqual<Input extends unknown[] | undefined, Output>(
|
|
|
186
203
|
): {
|
|
187
204
|
(array: Input): Output;
|
|
188
205
|
clear(array: Input): void;
|
|
206
|
+
clearAll(): void;
|
|
189
207
|
} {
|
|
190
208
|
let state: {
|
|
191
209
|
cache: {
|
|
@@ -227,7 +245,10 @@ export function cacheArrayEqual<Input extends unknown[] | undefined, Output>(
|
|
|
227
245
|
state.cache.splice(i, 1);
|
|
228
246
|
}
|
|
229
247
|
}
|
|
230
|
-
}
|
|
248
|
+
},
|
|
249
|
+
clearAll() {
|
|
250
|
+
state.cache = [];
|
|
251
|
+
},
|
|
231
252
|
}
|
|
232
253
|
);
|
|
233
254
|
}
|
|
@@ -247,7 +268,7 @@ export function cacheArgsEqual<Fnc extends AnyFunction>(
|
|
|
247
268
|
{
|
|
248
269
|
clear(...args: unknown[]) {
|
|
249
270
|
cache.clear(args);
|
|
250
|
-
}
|
|
271
|
+
},
|
|
251
272
|
}
|
|
252
273
|
);
|
|
253
274
|
}
|
|
@@ -255,13 +276,23 @@ export function cacheArgsEqual<Fnc extends AnyFunction>(
|
|
|
255
276
|
export function cacheJSONArgsEqual<Fnc extends AnyFunction>(
|
|
256
277
|
fnc: Fnc,
|
|
257
278
|
limit = 10
|
|
258
|
-
)
|
|
279
|
+
) {
|
|
259
280
|
let cache = cacheLimited(limit, (argsJSON: string) => {
|
|
260
281
|
return fnc(...JSON.parse(argsJSON));
|
|
261
282
|
});
|
|
262
|
-
return (
|
|
263
|
-
|
|
264
|
-
|
|
283
|
+
return Object.assign(
|
|
284
|
+
((...args: unknown[]) => {
|
|
285
|
+
return cache(JSON.stringify(args));
|
|
286
|
+
}) as Fnc,
|
|
287
|
+
{
|
|
288
|
+
clear(...args: unknown[]) {
|
|
289
|
+
cache.clearKey(JSON.stringify(args));
|
|
290
|
+
},
|
|
291
|
+
clearAll() {
|
|
292
|
+
cache.clear();
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
);
|
|
265
296
|
}
|
|
266
297
|
|
|
267
298
|
export function cacheShallowConfigArgEqual<Fnc extends AnyFunction>(
|
|
@@ -269,6 +300,7 @@ export function cacheShallowConfigArgEqual<Fnc extends AnyFunction>(
|
|
|
269
300
|
limit = 10
|
|
270
301
|
): Fnc & {
|
|
271
302
|
clear(...args: Args<Fnc>): void;
|
|
303
|
+
clearAll(): void;
|
|
272
304
|
} {
|
|
273
305
|
let cache = cacheArrayEqual((kvpsFlat: unknown[]) => {
|
|
274
306
|
output.missCount++;
|
|
@@ -301,6 +333,9 @@ export function cacheShallowConfigArgEqual<Fnc extends AnyFunction>(
|
|
|
301
333
|
clear(configArg: object) {
|
|
302
334
|
cache.clear(getKVPs(configArg));
|
|
303
335
|
},
|
|
336
|
+
clearAll() {
|
|
337
|
+
cache.clearAll();
|
|
338
|
+
},
|
|
304
339
|
callCount: 0,
|
|
305
340
|
missCount: 0,
|
|
306
341
|
}
|
package/src/misc.ts
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import * as crypto from "crypto";
|
|
2
2
|
import { canHaveChildren, MaybePromise } from "./types";
|
|
3
3
|
|
|
4
|
+
export const timeInSecond = 1000;
|
|
5
|
+
export const timeInMinute = timeInSecond * 60;
|
|
6
|
+
export const timeInHour = timeInMinute * 60;
|
|
7
|
+
export const timeInDay = timeInHour * 24;
|
|
8
|
+
export const timeInWeek = timeInDay * 7;
|
|
9
|
+
export const timeInYear = timeInDay * 365;
|
|
10
|
+
|
|
4
11
|
export type Watchable<T> = (callback: (value: T) => void) => MaybePromise<void>;
|
|
5
12
|
|
|
6
13
|
export function convertErrorStackToError(error: string): Error {
|
|
@@ -301,4 +308,9 @@ export function arrayFromOrderObject<T>(obj: { [order: number]: T }): T[] {
|
|
|
301
308
|
|
|
302
309
|
export function last<T>(arr: T[]): T | undefined {
|
|
303
310
|
return arr[arr.length - 1];
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
export type ObjectValues<T> = T[keyof T];
|
|
314
|
+
export function entries<Obj extends { [key: string]: unknown }>(obj: Obj): [keyof Obj, ObjectValues<Obj>][] {
|
|
315
|
+
return Object.entries(obj) as any;
|
|
304
316
|
}
|
|
@@ -8,39 +8,18 @@ export type OwnTimeObj = {
|
|
|
8
8
|
time: number;
|
|
9
9
|
ownTime: number;
|
|
10
10
|
};
|
|
11
|
-
type OwnTimeObjInternal = OwnTimeObj & {
|
|
11
|
+
export type OwnTimeObjInternal = OwnTimeObj & {
|
|
12
12
|
lastStartTime: number;
|
|
13
13
|
firstStartTime: number;
|
|
14
|
-
parent: OwnTimeObjInternal | undefined;
|
|
15
|
-
child: OwnTimeObjInternal | undefined;
|
|
16
14
|
};
|
|
17
15
|
|
|
18
|
-
let
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if (!instances) return undefined;
|
|
23
|
-
if (!pendingCallTime) return undefined;
|
|
24
|
-
let results = instances.map((instance) => ({
|
|
25
|
-
name: instance.name,
|
|
26
|
-
ownTime: instance.ownTime,
|
|
27
|
-
time: time - instance.firstStartTime,
|
|
28
|
-
source: instance
|
|
29
|
-
}));
|
|
30
|
-
results[0].ownTime += time - pendingCallTime.lastStartTime;
|
|
31
|
-
return results;
|
|
32
|
-
}
|
|
33
|
-
export function getPendingOwnTimeInstances(): OwnTimeObjInternal[] | undefined {
|
|
34
|
-
if (!pendingCallTime) return undefined;
|
|
35
|
-
let results: OwnTimeObjInternal[] = [];
|
|
36
|
-
let current: OwnTimeObjInternal | undefined = pendingCallTime;
|
|
37
|
-
while (current) {
|
|
38
|
-
results.push(current);
|
|
39
|
-
current = current.parent;
|
|
40
|
-
}
|
|
41
|
-
return results;
|
|
16
|
+
let openTimes: OwnTimeObjInternal[] = [];
|
|
17
|
+
|
|
18
|
+
export function getOpenTimesBase(): OwnTimeObjInternal[] {
|
|
19
|
+
return openTimes;
|
|
42
20
|
}
|
|
43
|
-
|
|
21
|
+
|
|
22
|
+
(global as any).pendingOwnCallTime = openTimes;
|
|
44
23
|
|
|
45
24
|
// NOTE: This overhead time is actually mostly for aggregate time, but it is needed,
|
|
46
25
|
// otherwise we consistently underestimate the time spent.
|
|
@@ -85,39 +64,25 @@ export function getOwnTime<T>(
|
|
|
85
64
|
ownTime: 0,
|
|
86
65
|
firstStartTime: time,
|
|
87
66
|
lastStartTime: time,
|
|
88
|
-
parent: pendingCallTime,
|
|
89
|
-
child: undefined,
|
|
90
67
|
};
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
pendingCallTime = obj;
|
|
95
|
-
if (obj.parent) {
|
|
96
|
-
obj.parent.ownTime += obj.lastStartTime - obj.parent.lastStartTime;
|
|
68
|
+
let prevOwnTime = openTimes[openTimes.length - 1];
|
|
69
|
+
if (prevOwnTime) {
|
|
70
|
+
prevOwnTime.ownTime += time - prevOwnTime.lastStartTime;
|
|
97
71
|
}
|
|
72
|
+
openTimes.push(obj);
|
|
98
73
|
|
|
99
74
|
function finish() {
|
|
100
75
|
let time = now();
|
|
101
76
|
obj.time = time - obj.firstStartTime;
|
|
102
|
-
if (
|
|
103
|
-
// Good case, all of our children call ended before us.
|
|
104
|
-
|
|
105
|
-
// End our own time calculation
|
|
77
|
+
if (obj === openTimes[openTimes.length - 1]) {
|
|
106
78
|
obj.ownTime += time - obj.lastStartTime;
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
if (pendingCallTime) {
|
|
111
|
-
// Resume our parent ownTime counting
|
|
112
|
-
pendingCallTime.lastStartTime = time;
|
|
79
|
+
let newOwnTime = openTimes[openTimes.length - 2];
|
|
80
|
+
if (newOwnTime) {
|
|
81
|
+
newOwnTime.lastStartTime = time;
|
|
113
82
|
}
|
|
114
83
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
obj.parent.child = obj.child;
|
|
118
|
-
}
|
|
119
|
-
obj.parent = undefined;
|
|
120
|
-
obj.child = undefined;
|
|
84
|
+
let index = openTimes.indexOf(obj);
|
|
85
|
+
openTimes.splice(index, 1);
|
|
121
86
|
|
|
122
87
|
obj.time += addMeasureOverheadTime;
|
|
123
88
|
obj.ownTime += addMeasureOverheadTime;
|
package/src/profiling/measure.ts
CHANGED
|
@@ -2,12 +2,14 @@ import debugbreak from "debugbreak";
|
|
|
2
2
|
import { formatTime, formatNumber } from "../formatting/format";
|
|
3
3
|
import { red, yellow, blue, magenta } from "../formatting/logColors";
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { getOpenTimesBase, getOwnTime, OwnTimeObj } from "./getOwnTime";
|
|
6
6
|
import { addToStats, addToStatsValue, createStatsValue, getStatsTop, StatsValue } from "./stats";
|
|
7
7
|
import { white } from "../formatting/logColors";
|
|
8
8
|
import { isNode } from "../misc";
|
|
9
9
|
|
|
10
|
-
/** NOTE: Must be called BEFORE anything else is imported!
|
|
10
|
+
/** NOTE: Must be called BEFORE anything else is imported!
|
|
11
|
+
* NOTE: Measurements on on by default now, so this doesn't really need to be called...
|
|
12
|
+
*/
|
|
11
13
|
export function enableMeasurements() {
|
|
12
14
|
if (functionsSkipped) {
|
|
13
15
|
console.warn(red(`Skipped measure shimming ${functionsSkipped} functions. Fix this by calling enableMeasurements before any other imports.`));
|
|
@@ -75,15 +77,25 @@ export function startMeasure(): {
|
|
|
75
77
|
let profile: MeasureProfile = {
|
|
76
78
|
entries: Object.create(null),
|
|
77
79
|
};
|
|
78
|
-
let openAtStart = new Set(
|
|
80
|
+
let openAtStart = new Set(getOpenTimesBase());
|
|
79
81
|
|
|
80
82
|
outstandingProfiles.push(profile);
|
|
81
83
|
return {
|
|
82
84
|
finish() {
|
|
83
|
-
let pending =
|
|
85
|
+
let pending = getOpenTimesBase();
|
|
86
|
+
let last = pending[pending.length - 1];
|
|
87
|
+
let time = Date.now();
|
|
84
88
|
for (let timeObj of pending) {
|
|
85
|
-
|
|
86
|
-
|
|
89
|
+
// Ignore any values that were already open, as they are clearly not
|
|
90
|
+
// caused by our code.
|
|
91
|
+
if (openAtStart.has(timeObj)) continue;
|
|
92
|
+
timeObj = { ...timeObj };
|
|
93
|
+
|
|
94
|
+
if (timeObj === last) {
|
|
95
|
+
timeObj.ownTime += time - timeObj.lastStartTime;
|
|
96
|
+
}
|
|
97
|
+
timeObj.time = time - timeObj.firstStartTime;
|
|
98
|
+
addToProfile(profile, timeObj);
|
|
87
99
|
}
|
|
88
100
|
outstandingProfiles.splice(outstandingProfiles.indexOf(profile), 1);
|
|
89
101
|
return profile;
|
|
@@ -96,6 +108,8 @@ export interface LogMeasureTableConfig {
|
|
|
96
108
|
name?: string;
|
|
97
109
|
// Defaults to 0.05
|
|
98
110
|
thresholdInTable?: number;
|
|
111
|
+
// Details to 50
|
|
112
|
+
minTimeToLog?: number;
|
|
99
113
|
}
|
|
100
114
|
|
|
101
115
|
export function logMeasureTable(
|
|
@@ -106,6 +120,7 @@ export function logMeasureTable(
|
|
|
106
120
|
if (thresholdInTable === undefined) {
|
|
107
121
|
thresholdInTable = 0.05;
|
|
108
122
|
}
|
|
123
|
+
let minTimeToLog = config?.minTimeToLog ?? 50;
|
|
109
124
|
|
|
110
125
|
function getTime(entry: ProfileEntry) {
|
|
111
126
|
return useTotalTime ? entry.totalTime : entry.ownTime;
|
|
@@ -114,6 +129,7 @@ export function logMeasureTable(
|
|
|
114
129
|
entries.sort((a, b) => getTime(b).sum - getTime(a).sum);
|
|
115
130
|
|
|
116
131
|
let totalTime = entries.map(x => getTime(x).sum).reduce((a, b) => a + b, 0);
|
|
132
|
+
if (totalTime < minTimeToLog) return;
|
|
117
133
|
|
|
118
134
|
console.log();
|
|
119
135
|
let title = yellow(`Profiled ${formatTime(totalTime)} (logged at ${new Date().toISOString()})`);
|
package/src/webSocketServer.ts
CHANGED
|
@@ -96,7 +96,11 @@ export async function startSocketServer(
|
|
|
96
96
|
console.error(`Connection attempt error ${e.message}`);
|
|
97
97
|
});
|
|
98
98
|
httpsServer.on("tlsClientError", e => {
|
|
99
|
-
|
|
99
|
+
// NOTE: This happens a lot when we have tabs open that connected to an old
|
|
100
|
+
// server (with old certs, that the browser will reject?)
|
|
101
|
+
if (!SocketFunction.silent) {
|
|
102
|
+
console.error(`TLS client error ${e.message}`);
|
|
103
|
+
}
|
|
100
104
|
});
|
|
101
105
|
|
|
102
106
|
httpsServer.on("request", httpCallHandler);
|