wenay-common 1.0.231 → 1.0.236
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/lib/Common/ByteStream.js +12 -2
- package/lib/Common/Listen.d.ts +3 -3
- package/lib/Common/Listen.js +1 -1
- package/lib/Common/Math.js +1 -1
- package/lib/Common/MemoFunc.d.ts +2 -1
- package/lib/Common/MemoFunc.js +60 -3
- package/lib/Common/SocketServerHook.d.ts +2 -2
- package/lib/Common/Time.js +1 -1
- package/lib/Common/WebHook2.js +1 -1
- package/lib/Common/commonsServerMini.d.ts +20 -1
- package/lib/Common/commonsServerMini.js +90 -7
- package/lib/Common/commonsServerMini2.d.ts +56 -0
- package/lib/Common/commonsServerMini2.js +327 -0
- package/lib/Common/funcTimeWait.d.ts +1 -2
- package/lib/Common/funcTimeWait.js +41 -12
- package/lib/Exchange/CParams.d.ts +0 -1
- package/lib/Exchange/CParams.js +1 -62
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/package.json +6 -6
- package/lib/Common/commonsServerTest.d.ts +0 -0
- package/lib/Common/commonsServerTest.js +0 -1
- package/lib/Common/node_test.d.ts +0 -1
- package/lib/Common/node_test.js +0 -14
package/lib/Common/ByteStream.js
CHANGED
|
@@ -48,7 +48,14 @@ class ByteStreamW {
|
|
|
48
48
|
let buf = createCopyOfBuffer(this._buffer(), size);
|
|
49
49
|
this._view = new DataView(buf);
|
|
50
50
|
}
|
|
51
|
-
constructor(view) {
|
|
51
|
+
constructor(view) {
|
|
52
|
+
if (view) {
|
|
53
|
+
this._view = view;
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
this.resize(0);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
52
59
|
get length() { return this._pos; }
|
|
53
60
|
get data() { return new DataView(this._buffer(), 0, this._pos); }
|
|
54
61
|
noThrow() { let other = new ByteStreamW(this._view); other._pos = this._pos; other._isThrowable = false; return other; }
|
|
@@ -183,6 +190,7 @@ class ByteStreamW {
|
|
|
183
190
|
array = new arrayClass(array.buffer, 0, length);
|
|
184
191
|
}
|
|
185
192
|
new arrayClass(this._buffer()).set(array, this._pos);
|
|
193
|
+
this._pos += length;
|
|
186
194
|
return this;
|
|
187
195
|
}
|
|
188
196
|
let func = (stream, item) => item.write(stream) != false;
|
|
@@ -343,7 +351,9 @@ class ByteStreamR_ {
|
|
|
343
351
|
if (size == null)
|
|
344
352
|
return null;
|
|
345
353
|
let bufpos = this._view.byteOffset + this._pos;
|
|
346
|
-
new arrayClass(this._view.buffer.slice(bufpos, bufpos + size));
|
|
354
|
+
const out = new arrayClass(this._view.buffer.slice(bufpos, bufpos + size));
|
|
355
|
+
this._pos += size;
|
|
356
|
+
return Array.from(out);
|
|
347
357
|
}
|
|
348
358
|
return this._readArrayByFunc(this._getReadFuncForNumeric(type));
|
|
349
359
|
}
|
package/lib/Common/Listen.d.ts
CHANGED
|
@@ -45,7 +45,7 @@ export declare function funcListenBySocket2<Z extends any[] = any[]>(e: t1<Z>, d
|
|
|
45
45
|
callback: (z: (...params: Parameters<tr2a<tr222<Z>>>) => void) => void;
|
|
46
46
|
removeCallback: () => boolean;
|
|
47
47
|
};
|
|
48
|
-
export declare function funcListenBySocket3<Z extends any[] = any[]>(e: t1<Z>, options
|
|
48
|
+
export declare function funcListenBySocket3<Z extends any[] = any[]>(e: t1<Z>, options?: Omit<Parameters<typeof funcListenBySocket2>[1], "paramsModify">): {
|
|
49
49
|
callback: (z: (...params: Parameters<tr2<tr222<Z>>>) => void) => void;
|
|
50
50
|
removeCallback: () => boolean;
|
|
51
51
|
};
|
|
@@ -81,8 +81,8 @@ export declare function DeepCompareKeys<T, T2 extends obj, T3 extends unknown>(o
|
|
|
81
81
|
[k: string]: any;
|
|
82
82
|
} | NonNullable<T> | trr2<T> | null;
|
|
83
83
|
export declare function deepModifyByListenSocket<T>(obj: T, status: () => boolean): ttt<T>;
|
|
84
|
-
export declare function deepModifyByListenSocket2<T>(obj: T, data
|
|
85
|
-
export declare function deepModifyByListenSocket3<T>(obj: T, data
|
|
84
|
+
export declare function deepModifyByListenSocket2<T>(obj: T, data?: Parameters<typeof funcListenBySocket2>[1]): ttt<T>;
|
|
85
|
+
export declare function deepModifyByListenSocket3<T>(obj: T, data?: Parameters<typeof funcListenBySocket3>[1]): ttt<T>;
|
|
86
86
|
export declare const funcListenBySocketObj: typeof deepModifyByListenSocket;
|
|
87
87
|
export declare function PromiseArrayListen<T extends any = unknown>(array: ((() => Promise<T>) | (() => any) | Promise<T>)[]): {
|
|
88
88
|
listenOk: (a: (...d: [data: T, i: number, countOk: number, countError: number, count: number]) => any) => () => void;
|
package/lib/Common/Listen.js
CHANGED
|
@@ -120,7 +120,7 @@ function funcListenBySocket2(e, d) {
|
|
|
120
120
|
};
|
|
121
121
|
}
|
|
122
122
|
function funcListenBySocket3(e, options) {
|
|
123
|
-
const r = funcListenBySocket2(e, { ...options, paramsModify: e => [e] });
|
|
123
|
+
const r = funcListenBySocket2(e, { ...(options ?? {}), paramsModify: e => [e] });
|
|
124
124
|
const callback = r.callback;
|
|
125
125
|
return {
|
|
126
126
|
callback,
|
package/lib/Common/Math.js
CHANGED
package/lib/Common/MemoFunc.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ export declare function MemoFunc(a?: {
|
|
|
2
2
|
memo?: Map<any, any>;
|
|
3
3
|
timeDelta?: number;
|
|
4
4
|
maxLimits?: number;
|
|
5
|
-
compareArguments?: (args: any) => string;
|
|
5
|
+
compareArguments?: (...args: any[]) => string;
|
|
6
6
|
eventUpdate?: () => void;
|
|
7
7
|
}): {
|
|
8
8
|
func: <T extends (...args: any[]) => any>(data: T, options?: {
|
|
@@ -17,3 +17,4 @@ export declare function MemoFunc(a?: {
|
|
|
17
17
|
};
|
|
18
18
|
export type MemoFuncOpt = Parameters<ReturnType<typeof MemoFunc>["func"]>[1];
|
|
19
19
|
export declare const MemoFuncConvert: <T extends () => any>(func: T, memo: ReturnType<typeof MemoFunc>) => (opt?: MemoFuncOpt) => ReturnType<T>;
|
|
20
|
+
export declare function runMemoFuncTests(): void;
|
package/lib/Common/MemoFunc.js
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.MemoFuncConvert = void 0;
|
|
4
4
|
exports.MemoFunc = MemoFunc;
|
|
5
|
+
exports.runMemoFuncTests = runMemoFuncTests;
|
|
5
6
|
function MemoFunc(a) {
|
|
6
|
-
const { memo = new Map(), timeDelta = 60 * 1000 * 100, maxLimits = 10000, compareArguments = (args) => JSON.stringify(args), eventUpdate } = a ?? {};
|
|
7
|
+
const { memo = new Map(), timeDelta = 60 * 1000 * 100, maxLimits = 10000, compareArguments = (...args) => JSON.stringify(args), eventUpdate } = a ?? {};
|
|
7
8
|
function cleanAll(obj) {
|
|
8
9
|
Object.keys(obj).forEach(key => delete obj[key]);
|
|
9
10
|
eventUpdate?.();
|
|
@@ -12,9 +13,9 @@ function MemoFunc(a) {
|
|
|
12
13
|
return ((...args) => {
|
|
13
14
|
const { reSave = false, key = "", compareArguments: cmp = compareArguments, timeDelta: td = timeDelta, old = false } = options ?? {};
|
|
14
15
|
const cacheForFunc = memo.get(data) ?? (() => { memo.set(data, {}); return memo.get(data); })();
|
|
15
|
-
const cacheKey = args.length ? key + cmp(args) : key;
|
|
16
|
+
const cacheKey = args.length ? key + cmp(...args) : key;
|
|
16
17
|
let entry = cacheForFunc[cacheKey];
|
|
17
|
-
if (old && entry
|
|
18
|
+
if (old && entry) {
|
|
18
19
|
return entry.volume;
|
|
19
20
|
}
|
|
20
21
|
if (Object.keys(cacheForFunc).length > maxLimits) {
|
|
@@ -42,3 +43,59 @@ function MemoFunc(a) {
|
|
|
42
43
|
}
|
|
43
44
|
const MemoFuncConvert = (func, memo) => ((opt) => memo.func(func, opt)());
|
|
44
45
|
exports.MemoFuncConvert = MemoFuncConvert;
|
|
46
|
+
function runMemoFuncTests() {
|
|
47
|
+
console.log("MemoFunc tests: start");
|
|
48
|
+
const memo = MemoFunc({ timeDelta: 1000 });
|
|
49
|
+
let callCount = 0;
|
|
50
|
+
const sum = (a, b) => {
|
|
51
|
+
callCount += 1;
|
|
52
|
+
return a + b;
|
|
53
|
+
};
|
|
54
|
+
const memoSum = memo.func(sum, {
|
|
55
|
+
compareArguments: (a, b) => `${a}|${b}`
|
|
56
|
+
});
|
|
57
|
+
memoSum(1, 2);
|
|
58
|
+
memoSum(1, 2);
|
|
59
|
+
memoSum(2, 3);
|
|
60
|
+
console.log("compareArguments spread ok:", callCount === 2, "callCount:", callCount);
|
|
61
|
+
let zeroCallCount = 0;
|
|
62
|
+
const returnZero = () => {
|
|
63
|
+
zeroCallCount += 1;
|
|
64
|
+
return 0;
|
|
65
|
+
};
|
|
66
|
+
const memoZero = memo.func(returnZero, { old: true });
|
|
67
|
+
memoZero();
|
|
68
|
+
memoZero();
|
|
69
|
+
console.log("old returns falsy cache:", zeroCallCount === 1, "zeroCallCount:", zeroCallCount);
|
|
70
|
+
const realNow = Date.now;
|
|
71
|
+
try {
|
|
72
|
+
let now = 1_000_000;
|
|
73
|
+
Date.now = () => now;
|
|
74
|
+
let tdCallCount = 0;
|
|
75
|
+
const bump = () => {
|
|
76
|
+
tdCallCount += 1;
|
|
77
|
+
return tdCallCount;
|
|
78
|
+
};
|
|
79
|
+
const memoBump = memo.func(bump, { timeDelta: 1000 });
|
|
80
|
+
memoBump();
|
|
81
|
+
now += 999;
|
|
82
|
+
memoBump();
|
|
83
|
+
now += 2;
|
|
84
|
+
memoBump();
|
|
85
|
+
console.log("timeDelta expiry:", tdCallCount === 2, "tdCallCount:", tdCallCount);
|
|
86
|
+
let reSaveCallCount = 0;
|
|
87
|
+
const touch = () => {
|
|
88
|
+
reSaveCallCount += 1;
|
|
89
|
+
return reSaveCallCount;
|
|
90
|
+
};
|
|
91
|
+
const memoTouch = memo.func(touch);
|
|
92
|
+
memoTouch();
|
|
93
|
+
memoTouch();
|
|
94
|
+
memo.func(touch, { reSave: true })();
|
|
95
|
+
console.log("reSave forces recompute:", reSaveCallCount === 2, "reSaveCallCount:", reSaveCallCount);
|
|
96
|
+
}
|
|
97
|
+
finally {
|
|
98
|
+
Date.now = realNow;
|
|
99
|
+
}
|
|
100
|
+
console.log("MemoFunc tests: end");
|
|
101
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { funcListenBySocket2 } from "./Listen";
|
|
1
|
+
import { funcListenBySocket2 as soc } from "./Listen";
|
|
2
2
|
type transformer = (func: (data: any) => any, tag: string, data: any) => any;
|
|
3
3
|
export declare function SocketServerHook(opt?: {
|
|
4
4
|
transformer?: transformer;
|
|
@@ -27,7 +27,7 @@ export declare function SocketServerHook(opt?: {
|
|
|
27
27
|
}];
|
|
28
28
|
provider: (tag: string, data: any) => void;
|
|
29
29
|
};
|
|
30
|
-
export declare function WebSocketServerHook(s: ReturnType<typeof SocketServerHook>, paramsSoc?: Parameters<typeof
|
|
30
|
+
export declare function WebSocketServerHook(s: ReturnType<typeof SocketServerHook>, paramsSoc?: Parameters<typeof soc>[1], disconnect?: () => any): {
|
|
31
31
|
disconnect(): void;
|
|
32
32
|
get: {
|
|
33
33
|
[k: string]: {
|
package/lib/Common/Time.js
CHANGED
|
@@ -188,7 +188,7 @@ class TF {
|
|
|
188
188
|
static MN2 = TF.get("MN2");
|
|
189
189
|
static MN3 = TF.get("MN3");
|
|
190
190
|
static MN4 = TF.get("MN4");
|
|
191
|
-
static MN6 = TF.get("
|
|
191
|
+
static MN6 = TF.get("MN6");
|
|
192
192
|
static Y1 = TF.get("Y1");
|
|
193
193
|
static min(...args) {
|
|
194
194
|
let tfs = ((args[0] && !(args[0] instanceof TF)) ? args[0] : args);
|
package/lib/Common/WebHook2.js
CHANGED
|
@@ -47,7 +47,7 @@ const loadSubscribers = () => {
|
|
|
47
47
|
fs.writeFileSync(SUBSCRIBERS_FILE, '{}', 'utf-8');
|
|
48
48
|
}
|
|
49
49
|
const data = JSON.parse(fs.readFileSync(SUBSCRIBERS_FILE, 'utf-8'));
|
|
50
|
-
return new Map(Object.entries(data).map(([key, sub]) => [key, { url: sub.url, tags: sub.tag, expireAt: new Date(sub.expireAt) }]));
|
|
50
|
+
return new Map(Object.entries(data).map(([key, sub]) => [key, { url: sub.url, tags: sub.tags ?? sub.tag, expireAt: new Date(sub.expireAt) }]));
|
|
51
51
|
};
|
|
52
52
|
const Queue = (0, waitRun_1.createAsyncQueue)(1);
|
|
53
53
|
const saveSubscribers = (subscribers) => {
|
|
@@ -21,17 +21,36 @@ type SocketData<T> = ({
|
|
|
21
21
|
wait?: boolean;
|
|
22
22
|
callbacksId?: number[];
|
|
23
23
|
};
|
|
24
|
+
type PromiseServerHooks<T> = {
|
|
25
|
+
onRequest?: (ctx: {
|
|
26
|
+
key: string[];
|
|
27
|
+
request: any[];
|
|
28
|
+
fnName: string;
|
|
29
|
+
fn: Func;
|
|
30
|
+
msg: SocketData<RequestScreener<T>>;
|
|
31
|
+
}) => boolean | Promise<boolean>;
|
|
32
|
+
onInvalid?: (ctx: {
|
|
33
|
+
reason: "invalid_payload" | "not_function" | "resolve_error" | "rate_limit";
|
|
34
|
+
key?: any;
|
|
35
|
+
request?: any;
|
|
36
|
+
error?: any;
|
|
37
|
+
msg: SocketData<RequestScreener<T>>;
|
|
38
|
+
}) => void | Promise<void>;
|
|
39
|
+
};
|
|
24
40
|
type ScreenerSoc<T> = {
|
|
25
41
|
sendMessage: (d: T) => void;
|
|
26
42
|
api: (h: {
|
|
27
43
|
onMessage: (m: T) => void | Promise<void>;
|
|
28
44
|
}) => void;
|
|
29
45
|
};
|
|
30
|
-
export declare function promiseServer<T extends Obj>(soc: ScreenerSoc<SocketData<RequestScreener<T
|
|
46
|
+
export declare function promiseServer<T extends Obj>(soc: ScreenerSoc<SocketData<RequestScreener<T>>> & {
|
|
47
|
+
hooks?: PromiseServerHooks<T>;
|
|
48
|
+
}, target: T): void;
|
|
31
49
|
type Func = (a: any) => any;
|
|
32
50
|
export type ScreenerSoc2<T> = {
|
|
33
51
|
send: (d: RequestScreener<T>, wait?: boolean, cbs?: Func[]) => Promise<any>;
|
|
34
52
|
api: ScreenerSocApi<T>;
|
|
53
|
+
abortAll: (textError: string) => void;
|
|
35
54
|
};
|
|
36
55
|
export type ScreenerSocApi<T> = {
|
|
37
56
|
log: (s: boolean) => void;
|
|
@@ -6,8 +6,43 @@ exports.wsWrapper = wsWrapper;
|
|
|
6
6
|
exports.createClientProxy = createClientProxy;
|
|
7
7
|
exports.createAPIFacadeClient = createAPIFacadeClient;
|
|
8
8
|
exports.createAPIFacadeServer = createAPIFacadeServer;
|
|
9
|
+
function createSimpleRateLimitHook(options) {
|
|
10
|
+
let count = 0;
|
|
11
|
+
let resetAt = 0;
|
|
12
|
+
return () => {
|
|
13
|
+
const now = Date.now();
|
|
14
|
+
if (now >= resetAt) {
|
|
15
|
+
resetAt = now + options.intervalMs;
|
|
16
|
+
count = 0;
|
|
17
|
+
}
|
|
18
|
+
count += 1;
|
|
19
|
+
if (count > options.max) {
|
|
20
|
+
throw new Error("Rate limit exceeded");
|
|
21
|
+
}
|
|
22
|
+
return true;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
9
25
|
function promiseServer(soc, target) {
|
|
26
|
+
const serializeError = (err) => {
|
|
27
|
+
if (err instanceof Error) {
|
|
28
|
+
return { name: err.name, message: err.message, stack: err.stack };
|
|
29
|
+
}
|
|
30
|
+
return err;
|
|
31
|
+
};
|
|
32
|
+
const hooks = soc.hooks;
|
|
10
33
|
soc.api({ onMessage: async (msg) => {
|
|
34
|
+
if (!msg || typeof msg !== "object" || !msg.data || !Array.isArray(msg.data.key) || !Array.isArray(msg.data.request)) {
|
|
35
|
+
const err = serializeError(new Error("Invalid request payload"));
|
|
36
|
+
try {
|
|
37
|
+
await hooks?.onInvalid?.({ reason: "invalid_payload", key: msg?.data?.key, request: msg?.data?.request, error: err, msg });
|
|
38
|
+
}
|
|
39
|
+
catch (hookErr) {
|
|
40
|
+
console.error({ error: serializeError(hookErr), where: "onInvalid" });
|
|
41
|
+
}
|
|
42
|
+
soc.sendMessage({ mapId: msg?.mapId ?? -1, error: { error: err, key: msg?.data?.key, arguments: msg?.data?.request } });
|
|
43
|
+
console.error({ error: err, key: msg?.data?.key, arguments: msg?.data?.request });
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
11
46
|
const { key, request } = msg.data;
|
|
12
47
|
let curr = target, fnName = "";
|
|
13
48
|
try {
|
|
@@ -19,11 +54,36 @@ function promiseServer(soc, target) {
|
|
|
19
54
|
}
|
|
20
55
|
}
|
|
21
56
|
catch (e) {
|
|
22
|
-
|
|
23
|
-
|
|
57
|
+
const err = serializeError(e);
|
|
58
|
+
try {
|
|
59
|
+
await hooks?.onInvalid?.({ reason: "resolve_error", key, request, error: err, msg });
|
|
60
|
+
}
|
|
61
|
+
catch (hookErr) {
|
|
62
|
+
console.error({ error: serializeError(hookErr), where: "onInvalid" });
|
|
63
|
+
}
|
|
64
|
+
soc.sendMessage({ mapId: msg.mapId, error: { error: err, key, arguments: request } });
|
|
65
|
+
console.error({ error: err, key, arguments: request });
|
|
24
66
|
return;
|
|
25
67
|
}
|
|
26
68
|
if (typeof curr[fnName] === "function") {
|
|
69
|
+
const fn = curr[fnName];
|
|
70
|
+
if (hooks?.onRequest) {
|
|
71
|
+
try {
|
|
72
|
+
const allowed = await hooks.onRequest({ key, request, fnName, fn, msg });
|
|
73
|
+
if (allowed === false) {
|
|
74
|
+
const err = serializeError(new Error("Request rejected by hook"));
|
|
75
|
+
soc.sendMessage({ mapId: msg.mapId, error: { error: err, key, arguments: request } });
|
|
76
|
+
console.error({ error: err, key, arguments: request });
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch (hookErr) {
|
|
81
|
+
const err = serializeError(hookErr);
|
|
82
|
+
soc.sendMessage({ mapId: msg.mapId, error: { error: err, key, arguments: request } });
|
|
83
|
+
console.error({ error: err, key, arguments: request });
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
27
87
|
const { callbacksId } = msg;
|
|
28
88
|
if (callbacksId && Array.isArray(callbacksId)) {
|
|
29
89
|
const cbArr = callbacksId.map(id => (data) => {
|
|
@@ -45,11 +105,18 @@ function promiseServer(soc, target) {
|
|
|
45
105
|
}
|
|
46
106
|
catch (e) {
|
|
47
107
|
console.log(fnName, request, key);
|
|
48
|
-
|
|
49
|
-
|
|
108
|
+
const err = serializeError(e);
|
|
109
|
+
soc.sendMessage({ mapId: msg.mapId, error: { error: err, key, arguments: request } });
|
|
110
|
+
console.error({ error: err, key, arguments: request });
|
|
50
111
|
}
|
|
51
112
|
}
|
|
52
113
|
else {
|
|
114
|
+
try {
|
|
115
|
+
await hooks?.onInvalid?.({ reason: "not_function", key, request, msg });
|
|
116
|
+
}
|
|
117
|
+
catch (hookErr) {
|
|
118
|
+
console.error({ error: serializeError(hookErr), where: "onInvalid" });
|
|
119
|
+
}
|
|
53
120
|
soc.sendMessage({ mapId: msg.mapId, error: JSON.stringify({ data: "это не функция", key, arguments: request }) });
|
|
54
121
|
console.error({ data: "это не функция", key, arguments: request });
|
|
55
122
|
}
|
|
@@ -59,6 +126,17 @@ function wsWrapper(soc) {
|
|
|
59
126
|
const max = soc.limit, sendMsg = soc.sendMessage;
|
|
60
127
|
const pool = (() => { const free = []; let tot = 0, pos = 0; return { log: () => console.log({ free, tot, pos }), next: () => pos > 0 ? free[--pos] : ++tot, release: (id) => { free[pos++] = id; } }; })();
|
|
61
128
|
const promises = new Map(), cbsMap = new Map();
|
|
129
|
+
const forceRejectAll = (reason) => {
|
|
130
|
+
promises.forEach((p, id) => {
|
|
131
|
+
p.reject({
|
|
132
|
+
error: { name: "RPC_ABORT", message: reason },
|
|
133
|
+
mapId: id
|
|
134
|
+
});
|
|
135
|
+
pool.release(id);
|
|
136
|
+
});
|
|
137
|
+
promises.clear();
|
|
138
|
+
cbsMap.clear();
|
|
139
|
+
};
|
|
62
140
|
soc.api({ onMessage: (msg) => {
|
|
63
141
|
const id = msg.mapId;
|
|
64
142
|
if (promises.has(id)) {
|
|
@@ -77,7 +155,8 @@ function wsWrapper(soc) {
|
|
|
77
155
|
}
|
|
78
156
|
else
|
|
79
157
|
console.error("Неожиданный ответ", msg);
|
|
80
|
-
}
|
|
158
|
+
}
|
|
159
|
+
});
|
|
81
160
|
let debug = false;
|
|
82
161
|
const api = {
|
|
83
162
|
log: (s) => { debug = s; },
|
|
@@ -95,7 +174,10 @@ function wsWrapper(soc) {
|
|
|
95
174
|
pool.release(key);
|
|
96
175
|
} }); }
|
|
97
176
|
};
|
|
98
|
-
return {
|
|
177
|
+
return {
|
|
178
|
+
abortAll: forceRejectAll,
|
|
179
|
+
api,
|
|
180
|
+
send: (data, wait, cbs) => new Promise((resolve, reject) => {
|
|
99
181
|
const msg = { mapId: pool.next(), data, wait, callbacksId: [] };
|
|
100
182
|
for (const fn of cbs ?? []) {
|
|
101
183
|
const id = pool.next();
|
|
@@ -115,7 +197,8 @@ function wsWrapper(soc) {
|
|
|
115
197
|
if (max && promises.size >= max)
|
|
116
198
|
console.log("promises.size =", promises.size);
|
|
117
199
|
sendMsg(msg);
|
|
118
|
-
})
|
|
200
|
+
})
|
|
201
|
+
};
|
|
119
202
|
}
|
|
120
203
|
function createClientProxy(soc2, wait) {
|
|
121
204
|
const chain = (path) => new Proxy(() => { }, {
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
type Socket = {
|
|
2
|
+
emit: (e: string, d: any) => void;
|
|
3
|
+
on: (e: string, cb: (d: any) => void) => void;
|
|
4
|
+
};
|
|
5
|
+
type Func = (...args: any[]) => any;
|
|
6
|
+
type PromiseServerHooks<T> = {
|
|
7
|
+
onRequest?: (ctx: {
|
|
8
|
+
key: string[];
|
|
9
|
+
request: any[];
|
|
10
|
+
fnName: string;
|
|
11
|
+
fn: Func;
|
|
12
|
+
}) => boolean | Promise<boolean>;
|
|
13
|
+
onInvalid?: (ctx: {
|
|
14
|
+
reason: "invalid_payload" | "not_function" | "resolve_error" | "rate_limit";
|
|
15
|
+
key?: any;
|
|
16
|
+
request?: any;
|
|
17
|
+
error?: any;
|
|
18
|
+
}) => void | Promise<void>;
|
|
19
|
+
};
|
|
20
|
+
type UnwrapPromise<T> = T extends Promise<infer R> ? R : T;
|
|
21
|
+
type ClientAPI<T> = {
|
|
22
|
+
[K in keyof T]: T[K] extends (...args: infer A) => infer R ? (...args: A) => Promise<UnwrapPromise<R>> : T[K] extends object ? ClientAPI<T[K]> : T[K];
|
|
23
|
+
};
|
|
24
|
+
type ClientAPIStrict<T> = ClientAPI<T>;
|
|
25
|
+
type ClientApiHandle = {
|
|
26
|
+
log: (s: boolean) => void;
|
|
27
|
+
promiseTotal: () => number;
|
|
28
|
+
callbackTotal: () => number;
|
|
29
|
+
promiseDeleteAll: (reject?: boolean) => void;
|
|
30
|
+
callbackDeleteAll: () => void;
|
|
31
|
+
callbackDelete: (fn: Function) => void;
|
|
32
|
+
callbackEnd: (fn: Function) => void;
|
|
33
|
+
};
|
|
34
|
+
declare function createAPIFacadeServer<T extends object>({ socket, object: target, socketKey: key, debug, hooks }: {
|
|
35
|
+
socket: Socket;
|
|
36
|
+
object: T;
|
|
37
|
+
socketKey: string;
|
|
38
|
+
debug?: boolean;
|
|
39
|
+
hooks?: PromiseServerHooks<T>;
|
|
40
|
+
}): void;
|
|
41
|
+
declare function createAPIFacadeClient<T extends object>({ socket, socketKey: key, limit }: {
|
|
42
|
+
socket: Socket;
|
|
43
|
+
socketKey: string;
|
|
44
|
+
limit?: number;
|
|
45
|
+
}): {
|
|
46
|
+
func: ClientAPI<T>;
|
|
47
|
+
space: ClientAPI<T>;
|
|
48
|
+
all: ClientAPI<T>;
|
|
49
|
+
strict: ClientAPI<T>;
|
|
50
|
+
api: ClientApiHandle;
|
|
51
|
+
abortAll: (reason: string) => void;
|
|
52
|
+
infoStrict: () => any;
|
|
53
|
+
strictInit(obj?: object): Promise<unknown>;
|
|
54
|
+
};
|
|
55
|
+
export { createAPIFacadeServer as CreatAPIFacadeServer2, createAPIFacadeClient as CreatAPIFacadeClient2 };
|
|
56
|
+
export type { ClientAPI, ClientAPIStrict, ClientApiHandle, PromiseServerHooks };
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CreatAPIFacadeServer2 = createAPIFacadeServer;
|
|
4
|
+
exports.CreatAPIFacadeClient2 = createAPIFacadeClient;
|
|
5
|
+
const Pkt = { CALL: 0, RESP: 1, CB: 2, MAP: 3, STRICT: 4 };
|
|
6
|
+
const FN_MARKER = "$_f";
|
|
7
|
+
const BANNED_KEYS = new Set(["__proto__", "constructor", "prototype"]);
|
|
8
|
+
const isSafeKey = (k) => !BANNED_KEYS.has(k);
|
|
9
|
+
const hasOwn = (obj, k) => Object.prototype.hasOwnProperty.call(obj, k);
|
|
10
|
+
class IdPool {
|
|
11
|
+
s = [];
|
|
12
|
+
p = 0;
|
|
13
|
+
n = 0;
|
|
14
|
+
next() { return this.p > 0 ? this.s[--this.p] : ++this.n; }
|
|
15
|
+
release(id) { this.s[this.p++] = id; }
|
|
16
|
+
}
|
|
17
|
+
function walk(val, onLeaf) {
|
|
18
|
+
if (val == null || typeof val !== "object")
|
|
19
|
+
return onLeaf(val);
|
|
20
|
+
if (val[FN_MARKER] !== undefined)
|
|
21
|
+
return onLeaf(val);
|
|
22
|
+
if (Array.isArray(val))
|
|
23
|
+
return val.map(v => walk(v, onLeaf));
|
|
24
|
+
const o = {};
|
|
25
|
+
for (const k of Object.keys(val))
|
|
26
|
+
if (isSafeKey(k))
|
|
27
|
+
o[k] = walk(val[k], onLeaf);
|
|
28
|
+
return o;
|
|
29
|
+
}
|
|
30
|
+
function pack(args, pool, cbStore, cbIds) {
|
|
31
|
+
return args.map(v => walk(v, leaf => {
|
|
32
|
+
if (typeof leaf === "function") {
|
|
33
|
+
const id = pool.next();
|
|
34
|
+
cbStore.set(id, leaf);
|
|
35
|
+
cbIds.push(id);
|
|
36
|
+
return { [FN_MARKER]: id };
|
|
37
|
+
}
|
|
38
|
+
return leaf;
|
|
39
|
+
}));
|
|
40
|
+
}
|
|
41
|
+
function unpack(args, sender) {
|
|
42
|
+
return args.map(v => walk(v, leaf => leaf != null && typeof leaf === "object" && leaf[FN_MARKER] !== undefined
|
|
43
|
+
? (...a) => sender(leaf[FN_MARKER], a)
|
|
44
|
+
: leaf));
|
|
45
|
+
}
|
|
46
|
+
const errToObj = (e) => e instanceof Error ? { name: e.name, message: e.message, stack: e.stack } : e;
|
|
47
|
+
const resolveCA = (path, args) => {
|
|
48
|
+
const last = path[path.length - 1];
|
|
49
|
+
if (last === "call")
|
|
50
|
+
return [path.slice(0, -1), args.slice(1)];
|
|
51
|
+
if (last === "apply")
|
|
52
|
+
return [path.slice(0, -1), args[1] ?? []];
|
|
53
|
+
return [path, args];
|
|
54
|
+
};
|
|
55
|
+
function createServer(socket, key, target, hooks) {
|
|
56
|
+
const methods = [];
|
|
57
|
+
const contexts = [];
|
|
58
|
+
const routeMap = {};
|
|
59
|
+
(function index(obj, prefix) {
|
|
60
|
+
for (const k of Object.keys(obj)) {
|
|
61
|
+
if (!isSafeKey(k))
|
|
62
|
+
continue;
|
|
63
|
+
const v = obj[k], path = prefix ? prefix + "." + k : k;
|
|
64
|
+
if (typeof v === "function") {
|
|
65
|
+
routeMap[path] = methods.length;
|
|
66
|
+
methods.push(v);
|
|
67
|
+
contexts.push(obj);
|
|
68
|
+
}
|
|
69
|
+
else if (v && typeof v === "object")
|
|
70
|
+
index(v, path);
|
|
71
|
+
}
|
|
72
|
+
})(target, "");
|
|
73
|
+
function serialize(obj) {
|
|
74
|
+
const out = {};
|
|
75
|
+
for (const k of Object.keys(obj)) {
|
|
76
|
+
if (!isSafeKey(k))
|
|
77
|
+
continue;
|
|
78
|
+
const v = obj[k];
|
|
79
|
+
out[k] = typeof v === "function" ? "func" : v != null && typeof v === "object" ? serialize(v) : v == null ? "null" : "unknown";
|
|
80
|
+
}
|
|
81
|
+
return out;
|
|
82
|
+
}
|
|
83
|
+
const strictSchema = serialize(target);
|
|
84
|
+
const send = (d) => socket.emit(key, d);
|
|
85
|
+
send([Pkt.MAP, routeMap, strictSchema]);
|
|
86
|
+
socket.on(key, async (msg) => {
|
|
87
|
+
if (msg === Pkt.STRICT) {
|
|
88
|
+
send([Pkt.MAP, routeMap, strictSchema]);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (!Array.isArray(msg) || msg[0] !== Pkt.CALL)
|
|
92
|
+
return;
|
|
93
|
+
const [, reqId, ref, rawArgs, w] = msg;
|
|
94
|
+
const wait = w !== false;
|
|
95
|
+
if (typeof reqId !== "number" || !Number.isFinite(reqId)) {
|
|
96
|
+
hooks?.onInvalid?.({ reason: "invalid_payload", key: ref, request: rawArgs, error: "reqId is not a valid number" });
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (typeof ref !== "number" && !Array.isArray(ref)) {
|
|
100
|
+
hooks?.onInvalid?.({ reason: "invalid_payload", key: ref, request: rawArgs, error: "ref must be number or string[]" });
|
|
101
|
+
if (wait)
|
|
102
|
+
send([Pkt.RESP, reqId, null, errToObj(new Error("Invalid ref type"))]);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (!Array.isArray(rawArgs)) {
|
|
106
|
+
hooks?.onInvalid?.({ reason: "invalid_payload", key: ref, request: rawArgs, error: "args must be an array" });
|
|
107
|
+
if (wait)
|
|
108
|
+
send([Pkt.RESP, reqId, null, errToObj(new Error("Invalid args: expected array"))]);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
let fn, ctx;
|
|
113
|
+
if (typeof ref === "number") {
|
|
114
|
+
fn = methods[ref];
|
|
115
|
+
ctx = contexts[ref];
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
if (!ref.every((s) => typeof s === "string" && isSafeKey(s))) {
|
|
119
|
+
hooks?.onInvalid?.({ reason: "invalid_payload", key: ref, request: rawArgs });
|
|
120
|
+
if (wait)
|
|
121
|
+
send([Pkt.RESP, reqId, null, errToObj(new Error("Forbidden path segment"))]);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
const idx = routeMap[ref.join(".")];
|
|
125
|
+
if (idx !== undefined) {
|
|
126
|
+
fn = methods[idx];
|
|
127
|
+
ctx = contexts[idx];
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
let curr = target;
|
|
131
|
+
for (let i = 0; i < ref.length - 1; i++) {
|
|
132
|
+
const seg = ref[i];
|
|
133
|
+
if (curr == null || typeof curr !== "object" || !hasOwn(curr, seg)) {
|
|
134
|
+
curr = undefined;
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
curr = curr[seg];
|
|
138
|
+
}
|
|
139
|
+
const last = ref[ref.length - 1];
|
|
140
|
+
if (curr != null && typeof curr === "object" && hasOwn(curr, last)) {
|
|
141
|
+
ctx = curr;
|
|
142
|
+
fn = curr[last];
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (typeof fn !== "function") {
|
|
147
|
+
hooks?.onInvalid?.({ reason: "not_function", key: ref, request: rawArgs });
|
|
148
|
+
if (wait)
|
|
149
|
+
send([Pkt.RESP, reqId, null, errToObj(new Error("Not a function: " + ref))]);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
if (hooks?.onRequest) {
|
|
153
|
+
const keyArr = typeof ref === "number"
|
|
154
|
+
? Object.keys(routeMap).find(k => routeMap[k] === ref)?.split(".") ?? []
|
|
155
|
+
: ref;
|
|
156
|
+
const allowed = await hooks.onRequest({ key: keyArr, request: rawArgs, fnName: keyArr[keyArr.length - 1] ?? "", fn: fn });
|
|
157
|
+
if (allowed === false) {
|
|
158
|
+
if (wait)
|
|
159
|
+
send([Pkt.RESP, reqId, null, errToObj(new Error("Rejected by hook"))]);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
const args = unpack(rawArgs, (cbId, cbArgs) => send([Pkt.CB, cbId, cbArgs]));
|
|
164
|
+
const res = await fn.apply(ctx, args);
|
|
165
|
+
if (wait)
|
|
166
|
+
send([Pkt.RESP, reqId, res]);
|
|
167
|
+
}
|
|
168
|
+
catch (e) {
|
|
169
|
+
if (wait)
|
|
170
|
+
send([Pkt.RESP, reqId, null, errToObj(e)]);
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
function createClient(socket, key, opts) {
|
|
175
|
+
const limit = opts?.limit ?? 10000;
|
|
176
|
+
const pool = new IdPool();
|
|
177
|
+
const pending = new Map();
|
|
178
|
+
const callbacks = new Map();
|
|
179
|
+
const routeCache = {};
|
|
180
|
+
let strictData = {};
|
|
181
|
+
let strictWaiters = [];
|
|
182
|
+
let debug = false;
|
|
183
|
+
socket.on(key, (msg) => {
|
|
184
|
+
if (!Array.isArray(msg))
|
|
185
|
+
return;
|
|
186
|
+
switch (msg[0]) {
|
|
187
|
+
case Pkt.RESP: {
|
|
188
|
+
const req = pending.get(msg[1]);
|
|
189
|
+
if (!req)
|
|
190
|
+
break;
|
|
191
|
+
pending.delete(msg[1]);
|
|
192
|
+
pool.release(msg[1]);
|
|
193
|
+
for (const cbId of req.cbs) {
|
|
194
|
+
callbacks.delete(cbId);
|
|
195
|
+
pool.release(cbId);
|
|
196
|
+
}
|
|
197
|
+
msg[3] ? req.fail(msg[3]) : req.ok(msg[2]);
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
case Pkt.CB: {
|
|
201
|
+
callbacks.get(msg[1])?.(...(msg[2] || []));
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
case Pkt.MAP: {
|
|
205
|
+
if (msg[1])
|
|
206
|
+
Object.assign(routeCache, msg[1]);
|
|
207
|
+
if (msg[2]) {
|
|
208
|
+
for (const k of Object.keys(strictData))
|
|
209
|
+
delete strictData[k];
|
|
210
|
+
Object.assign(strictData, msg[2]);
|
|
211
|
+
for (const r of strictWaiters)
|
|
212
|
+
r(undefined);
|
|
213
|
+
strictWaiters = [];
|
|
214
|
+
}
|
|
215
|
+
break;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
const sendCall = (path, args, wait) => {
|
|
220
|
+
const cbIds = [];
|
|
221
|
+
const clean = pack(args, pool, callbacks, cbIds);
|
|
222
|
+
const ref = routeCache[path.join(".")] ?? path;
|
|
223
|
+
if (!wait) {
|
|
224
|
+
socket.emit(key, [Pkt.CALL, 0, ref, clean, false]);
|
|
225
|
+
return Promise.resolve();
|
|
226
|
+
}
|
|
227
|
+
return new Promise((resolve, reject) => {
|
|
228
|
+
if (pending.size >= limit)
|
|
229
|
+
return reject(new Error("RPC limit"));
|
|
230
|
+
const reqId = pool.next();
|
|
231
|
+
pending.set(reqId, { ok: resolve, fail: reject, cbs: cbIds });
|
|
232
|
+
if (debug)
|
|
233
|
+
console.log("[RPC]", path.join("."), "id=", reqId);
|
|
234
|
+
socket.emit(key, [Pkt.CALL, reqId, ref, clean]);
|
|
235
|
+
});
|
|
236
|
+
};
|
|
237
|
+
const buildProxy = (path, wait) => new Proxy(function () { }, {
|
|
238
|
+
get(_, p) {
|
|
239
|
+
if (p === "then" || p === "catch" || p === Symbol.toPrimitive)
|
|
240
|
+
return undefined;
|
|
241
|
+
return buildProxy([...path, String(p)], wait);
|
|
242
|
+
},
|
|
243
|
+
apply(_, __, args) {
|
|
244
|
+
const [fp, fa] = resolveCA(path, args);
|
|
245
|
+
return sendCall(fp, fa, wait);
|
|
246
|
+
},
|
|
247
|
+
});
|
|
248
|
+
const buildStrict = (path, wait) => {
|
|
249
|
+
let tgt = strictData;
|
|
250
|
+
for (const seg of path) {
|
|
251
|
+
tgt = tgt?.[seg];
|
|
252
|
+
if (tgt == null || tgt === "null")
|
|
253
|
+
return undefined;
|
|
254
|
+
}
|
|
255
|
+
return new Proxy(tgt === "func" ? function () { } : {}, {
|
|
256
|
+
has: (_, p) => tgt?.[String(p)] !== "null",
|
|
257
|
+
ownKeys: () => tgt && typeof tgt === "object" ? Object.keys(tgt) : [],
|
|
258
|
+
getOwnPropertyDescriptor: () => ({ enumerable: true, configurable: true }),
|
|
259
|
+
getPrototypeOf: () => !tgt || tgt === "null" ? Object.prototype : tgt === "func" ? Function.prototype : null,
|
|
260
|
+
get(_, p) {
|
|
261
|
+
if (p === "then" || p === "catch" || p === Symbol.toPrimitive)
|
|
262
|
+
return undefined;
|
|
263
|
+
if (p === "call" && tgt === "func")
|
|
264
|
+
return (_, ...args) => sendCall(path, args, wait);
|
|
265
|
+
const child = tgt?.[String(p)];
|
|
266
|
+
return child === "null" || child === undefined ? undefined : buildStrict([...path, String(p)], wait);
|
|
267
|
+
},
|
|
268
|
+
apply(_, __, args) {
|
|
269
|
+
const [fp, fa] = resolveCA(path, args);
|
|
270
|
+
return sendCall(fp, fa, wait);
|
|
271
|
+
},
|
|
272
|
+
});
|
|
273
|
+
};
|
|
274
|
+
const releaseCbs = (fn) => {
|
|
275
|
+
callbacks.forEach((cb, id) => { if (cb === fn) {
|
|
276
|
+
callbacks.delete(id);
|
|
277
|
+
pool.release(id);
|
|
278
|
+
} });
|
|
279
|
+
};
|
|
280
|
+
const clearAll = (rejectReason) => {
|
|
281
|
+
pending.forEach((p, id) => { pool.release(id); p.fail(rejectReason ?? "aborted"); });
|
|
282
|
+
pending.clear();
|
|
283
|
+
callbacks.forEach((_, id) => pool.release(id));
|
|
284
|
+
callbacks.clear();
|
|
285
|
+
};
|
|
286
|
+
const api = {
|
|
287
|
+
log: s => { debug = s; },
|
|
288
|
+
promiseTotal: () => pending.size,
|
|
289
|
+
callbackTotal: () => callbacks.size,
|
|
290
|
+
promiseDeleteAll: (rej = true) => {
|
|
291
|
+
pending.forEach((p, id) => { pool.release(id); rej ? p.fail("promiseDeleteAll") : p.ok(undefined); });
|
|
292
|
+
pending.clear();
|
|
293
|
+
},
|
|
294
|
+
callbackDeleteAll: () => { callbacks.forEach((_, id) => pool.release(id)); callbacks.clear(); },
|
|
295
|
+
callbackDelete: releaseCbs,
|
|
296
|
+
callbackEnd: releaseCbs,
|
|
297
|
+
};
|
|
298
|
+
const func = buildProxy([], true);
|
|
299
|
+
return {
|
|
300
|
+
func,
|
|
301
|
+
space: buildProxy([], false),
|
|
302
|
+
all: func,
|
|
303
|
+
strict: buildStrict([], true),
|
|
304
|
+
api,
|
|
305
|
+
abortAll: (reason) => clearAll({ error: { name: "RPC_ABORT", message: reason } }),
|
|
306
|
+
infoStrict: () => strictData,
|
|
307
|
+
async strictInit(obj) {
|
|
308
|
+
if (obj) {
|
|
309
|
+
strictData = obj;
|
|
310
|
+
}
|
|
311
|
+
else {
|
|
312
|
+
socket.emit(key, Pkt.STRICT);
|
|
313
|
+
return new Promise(r => { strictWaiters.push(r); });
|
|
314
|
+
}
|
|
315
|
+
},
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
function createAPIFacadeServer({ socket, object: target, socketKey: key, debug = false, hooks }) {
|
|
319
|
+
if (debug) {
|
|
320
|
+
const origOn = socket.on.bind(socket);
|
|
321
|
+
socket.on = (e, cb) => origOn(e, (d) => { console.log("[RPC IN]", typeof d === "object" ? JSON.stringify(d) : d); cb(d); });
|
|
322
|
+
}
|
|
323
|
+
createServer(socket, key, target, hooks);
|
|
324
|
+
}
|
|
325
|
+
function createAPIFacadeClient({ socket, socketKey: key, limit }) {
|
|
326
|
+
return createClient(socket, key, { limit });
|
|
327
|
+
}
|
|
@@ -9,7 +9,6 @@ export declare function funcTimeW(): {
|
|
|
9
9
|
dStatic: {
|
|
10
10
|
[key: string]: [number, number][];
|
|
11
11
|
};
|
|
12
|
-
data: any[];
|
|
13
12
|
add(item: tFunc): void;
|
|
14
13
|
cleanByTime(type: tType, ms?: number): void;
|
|
15
14
|
weight(type: tType, ms?: number): number;
|
|
@@ -21,7 +20,6 @@ export declare const FuncTimeWait: {
|
|
|
21
20
|
dStatic: {
|
|
22
21
|
[key: string]: [number, number][];
|
|
23
22
|
};
|
|
24
|
-
data: any[];
|
|
25
23
|
add(item: tFunc): void;
|
|
26
24
|
cleanByTime(type: tType, ms?: number): void;
|
|
27
25
|
weight(type: tType, ms?: number): number;
|
|
@@ -29,4 +27,5 @@ export declare const FuncTimeWait: {
|
|
|
29
27
|
byWeight(type: tType, weight?: number): number;
|
|
30
28
|
byWeightTimeNow(type: tType, timeNow?: number, weight?: number): number;
|
|
31
29
|
};
|
|
30
|
+
export declare function testFuncTimeW(): void;
|
|
32
31
|
export {};
|
|
@@ -2,30 +2,46 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.FuncTimeWait = void 0;
|
|
4
4
|
exports.funcTimeW = funcTimeW;
|
|
5
|
+
exports.testFuncTimeW = testFuncTimeW;
|
|
6
|
+
const common_1 = require("./common");
|
|
5
7
|
function funcTimeW() {
|
|
6
8
|
const dStatic = {};
|
|
7
|
-
|
|
9
|
+
function getInsertIndex(arr, timeStamp) {
|
|
10
|
+
if (arr.length === 0)
|
|
11
|
+
return 0;
|
|
12
|
+
if (timeStamp >= arr[arr.length - 1][0])
|
|
13
|
+
return arr.length;
|
|
14
|
+
if (timeStamp <= arr[0][0])
|
|
15
|
+
return 0;
|
|
16
|
+
const index = (0, common_1.BSearch)(arr, timeStamp, (a, b) => a[0] - b, "greatOrEqual", "ascend");
|
|
17
|
+
return index === -1 ? arr.length : index;
|
|
18
|
+
}
|
|
8
19
|
return {
|
|
9
20
|
dStatic,
|
|
10
|
-
data,
|
|
11
21
|
add(item) {
|
|
12
|
-
|
|
13
|
-
|
|
22
|
+
const arr = (dStatic[item.type] ??= []);
|
|
23
|
+
const timeStamp = item.timeStamp ?? Date.now();
|
|
24
|
+
const insertIndex = getInsertIndex(arr, timeStamp);
|
|
25
|
+
if (insertIndex === arr.length) {
|
|
26
|
+
arr.push([timeStamp, item.weight]);
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
arr.splice(insertIndex, 0, [timeStamp, item.weight]);
|
|
14
30
|
}
|
|
15
|
-
dStatic[item.type].push([item.timeStamp ?? Date.now(), item.weight]);
|
|
16
31
|
},
|
|
17
32
|
cleanByTime(type, ms = 60 * 1000) {
|
|
18
33
|
const arr = dStatic[type];
|
|
19
34
|
if (!arr || arr.length === 0)
|
|
20
35
|
return;
|
|
21
36
|
const timeStamp = Date.now();
|
|
22
|
-
|
|
37
|
+
const cutoff = timeStamp - ms;
|
|
38
|
+
if (arr[0][0] > cutoff)
|
|
23
39
|
return;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
40
|
+
const cutIndex = (0, common_1.BSearch)(arr, cutoff, (a, b) => a[0] - b, "greatOrEqual", "ascend");
|
|
41
|
+
if (cutIndex === -1) {
|
|
42
|
+
arr.splice(0, arr.length);
|
|
27
43
|
}
|
|
28
|
-
if (cutIndex > 0) {
|
|
44
|
+
else if (cutIndex > 0) {
|
|
29
45
|
arr.splice(0, cutIndex);
|
|
30
46
|
}
|
|
31
47
|
},
|
|
@@ -43,7 +59,7 @@ function funcTimeW() {
|
|
|
43
59
|
sum += _weight;
|
|
44
60
|
}
|
|
45
61
|
if (i >= 0) {
|
|
46
|
-
arr.splice(0, i);
|
|
62
|
+
arr.splice(0, i + 1);
|
|
47
63
|
}
|
|
48
64
|
return sum;
|
|
49
65
|
},
|
|
@@ -61,7 +77,7 @@ function funcTimeW() {
|
|
|
61
77
|
sum += _weight;
|
|
62
78
|
}
|
|
63
79
|
if (i >= 0) {
|
|
64
|
-
arr.splice(0, i);
|
|
80
|
+
arr.splice(0, i + 1);
|
|
65
81
|
}
|
|
66
82
|
return sum;
|
|
67
83
|
},
|
|
@@ -112,3 +128,16 @@ function funcTimeW() {
|
|
|
112
128
|
};
|
|
113
129
|
}
|
|
114
130
|
exports.FuncTimeWait = funcTimeW();
|
|
131
|
+
function testFuncTimeW() {
|
|
132
|
+
const tracker = funcTimeW();
|
|
133
|
+
const type = "UID";
|
|
134
|
+
const now = Date.now();
|
|
135
|
+
tracker.add({ type, weight: 1, timeStamp: now });
|
|
136
|
+
tracker.add({ type, weight: 2, timeStamp: now - 500 });
|
|
137
|
+
tracker.add({ type, weight: 3, timeStamp: now + 500 });
|
|
138
|
+
console.log("funcTimeW order", tracker.dStatic[type]);
|
|
139
|
+
tracker.cleanByTime(type, 200);
|
|
140
|
+
console.log("funcTimeW cleanByTime", tracker.dStatic[type]);
|
|
141
|
+
const w = tracker.weight(type, 1000);
|
|
142
|
+
console.log("funcTimeW weight 1s", w, tracker.dStatic[type]);
|
|
143
|
+
}
|
|
@@ -200,7 +200,6 @@ export type IParamsExpandable = {
|
|
|
200
200
|
export type IParamsExpandableReadonly = ReadonlyFull<IParamsExpandable>;
|
|
201
201
|
export declare function isParamGroupOrArray<TParam extends IParamReadonly>(param: TParam): param is Extract<TParam, ReadonlyFull<IParamGroup | IParamArr<any>>>;
|
|
202
202
|
export declare function isParamGroup<TParam extends IParamReadonly>(param: TParam): param is Extract<TParam, ReadonlyFull<IParamGroup>>;
|
|
203
|
-
export declare function isSimpleParams2<TParams extends IParamsReadonly>(params: TParams | SimpleParams): boolean;
|
|
204
203
|
export declare function isSimpleParams<TParams extends IParamsReadonly>(params: TParams | SimpleParams): boolean;
|
|
205
204
|
type ObjectKeyPath<TObject extends object = object, TValue = unknown> = readonly string[];
|
|
206
205
|
export declare function iterateParams<TObj extends IParamsReadonly, TVal extends IParamReadonly = TObj[string]>(obj: TObj, currentPath?: ObjectKeyPath<TObj, TVal>): Generator<[key: string, value: TVal, path: ObjectKeyPath<TObj, TVal>]>;
|
package/lib/Exchange/CParams.js
CHANGED
|
@@ -4,7 +4,6 @@ exports.CParamsReadonly = exports.CParams = void 0;
|
|
|
4
4
|
exports.isParamBase = isParamBase;
|
|
5
5
|
exports.isParamGroupOrArray = isParamGroupOrArray;
|
|
6
6
|
exports.isParamGroup = isParamGroup;
|
|
7
|
-
exports.isSimpleParams2 = isSimpleParams2;
|
|
8
7
|
exports.isSimpleParams = isSimpleParams;
|
|
9
8
|
exports.iterateParams = iterateParams;
|
|
10
9
|
exports.enableAllParams = enableAllParams;
|
|
@@ -33,7 +32,7 @@ function isParamGroupOrArray(param) {
|
|
|
33
32
|
function isParamGroup(param) {
|
|
34
33
|
return isParamGroupOrArray(param) && !Array.isArray(param.value);
|
|
35
34
|
}
|
|
36
|
-
function
|
|
35
|
+
function isSimpleParams(params) {
|
|
37
36
|
let t = false;
|
|
38
37
|
for (let key in params) {
|
|
39
38
|
const tr = params[key]["value"];
|
|
@@ -49,24 +48,6 @@ function isSimpleParams2(params) {
|
|
|
49
48
|
}
|
|
50
49
|
return false;
|
|
51
50
|
}
|
|
52
|
-
function isSimpleParams(params) {
|
|
53
|
-
for (let key in params) {
|
|
54
|
-
const param = params[key];
|
|
55
|
-
if (param == null || typeof param !== "object") {
|
|
56
|
-
return true;
|
|
57
|
-
}
|
|
58
|
-
if (!("value" in param)) {
|
|
59
|
-
return true;
|
|
60
|
-
}
|
|
61
|
-
const val = param.value;
|
|
62
|
-
if (typeof val === "object" && val !== null && !(val instanceof Date) && !Array.isArray(val)) {
|
|
63
|
-
const r = isSimpleParams(val);
|
|
64
|
-
if (r)
|
|
65
|
-
return true;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
return false;
|
|
69
|
-
}
|
|
70
51
|
function* iterateParams(obj, currentPath = []) {
|
|
71
52
|
for (let [key, param] of Object.entries(obj)) {
|
|
72
53
|
let keyPath = currentPath.concat(key);
|
|
@@ -183,48 +164,6 @@ let p0 = p.p1;
|
|
|
183
164
|
let p3 = p.p3;
|
|
184
165
|
let p4 = p.p4;
|
|
185
166
|
let p5 = p.p5?.p1;
|
|
186
|
-
function test22() {
|
|
187
|
-
const base = {
|
|
188
|
-
kBorrow: {
|
|
189
|
-
value: 1,
|
|
190
|
-
name: 'borrow step',
|
|
191
|
-
range: { max: 3, min: 1, step: 0.1 }
|
|
192
|
-
},
|
|
193
|
-
checkBorrow: { value: false, name: 'check borrow' },
|
|
194
|
-
checkBorrowFutures: { value: false, name: 'check borrow for futures' },
|
|
195
|
-
depoMulti: {
|
|
196
|
-
value: 1,
|
|
197
|
-
name: 'koef depoMulti',
|
|
198
|
-
range: { min: 0.01, max: 4, step: 0.01 }
|
|
199
|
-
},
|
|
200
|
-
depoMulti2: {
|
|
201
|
-
value: 2,
|
|
202
|
-
name: 'koef depoMulti',
|
|
203
|
-
range: { min: 0.01, max: 4, step: 0.01 }
|
|
204
|
-
}
|
|
205
|
-
};
|
|
206
|
-
const r2 = {
|
|
207
|
-
kBorrow: {
|
|
208
|
-
value: 1,
|
|
209
|
-
name: 'borrow step',
|
|
210
|
-
range: { max: 3, min: 1, step: 0.1 }
|
|
211
|
-
},
|
|
212
|
-
checkBorrow: { value: false, name: 'check borrow' },
|
|
213
|
-
checkBorrowFutures: { value: true, name: 'check borrow for futures' },
|
|
214
|
-
depoMulti: {
|
|
215
|
-
value: 1.32,
|
|
216
|
-
name: 'koef depoMulti',
|
|
217
|
-
range: { min: 0.01, max: 4, step: 0.01 }
|
|
218
|
-
},
|
|
219
|
-
depoMulti3: {
|
|
220
|
-
value: 1,
|
|
221
|
-
name: 'koef depoMulti',
|
|
222
|
-
range: { min: 0.01, max: 4, step: 0.01 }
|
|
223
|
-
}
|
|
224
|
-
};
|
|
225
|
-
const i = mergeParamValuesToInfos(base, r2);
|
|
226
|
-
console.log(i);
|
|
227
|
-
}
|
|
228
167
|
function convert_(valuesObj, srcObj) {
|
|
229
168
|
let resObj = {};
|
|
230
169
|
if (srcObj instanceof Array)
|
package/lib/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export * from "./Common/Time";
|
|
|
7
7
|
export * from "./Common/common";
|
|
8
8
|
export * from "./Common/commonsServer";
|
|
9
9
|
export * from "./Common/commonsServerMini";
|
|
10
|
+
export * from "./Common/commonsServerMini2";
|
|
10
11
|
export * from "./Common/event";
|
|
11
12
|
export * from "./Common/funcTimeWait";
|
|
12
13
|
export * from "./Common/List";
|
package/lib/index.js
CHANGED
|
@@ -47,6 +47,7 @@ __exportStar(require("./Common/Time"), exports);
|
|
|
47
47
|
__exportStar(require("./Common/common"), exports);
|
|
48
48
|
__exportStar(require("./Common/commonsServer"), exports);
|
|
49
49
|
__exportStar(require("./Common/commonsServerMini"), exports);
|
|
50
|
+
__exportStar(require("./Common/commonsServerMini2"), exports);
|
|
50
51
|
__exportStar(require("./Common/event"), exports);
|
|
51
52
|
__exportStar(require("./Common/funcTimeWait"), exports);
|
|
52
53
|
__exportStar(require("./Common/List"), exports);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wenay-common",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.236",
|
|
4
4
|
"description": "Common library",
|
|
5
5
|
"strict": true,
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -19,16 +19,16 @@
|
|
|
19
19
|
"url": "https://github.com/wenayr/math.git"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"axios": "^1.
|
|
22
|
+
"axios": "^1.13.2",
|
|
23
23
|
"create-hmac": "^1.1.7",
|
|
24
|
-
"express": "^
|
|
25
|
-
"socket.io": "^4.8.
|
|
26
|
-
"socket.io-client": "^4.8.
|
|
24
|
+
"express": "^5.2.1",
|
|
25
|
+
"socket.io": "^4.8.3",
|
|
26
|
+
"socket.io-client": "^4.8.3",
|
|
27
27
|
"source-map-support": "^0.5.21",
|
|
28
28
|
"xmlhttprequest": "^1.8.0"
|
|
29
29
|
},
|
|
30
30
|
"optionalDependencies": {
|
|
31
|
-
"utf-8-validate": "^6.0.
|
|
31
|
+
"utf-8-validate": "^6.0.6"
|
|
32
32
|
},
|
|
33
33
|
"exports": {
|
|
34
34
|
".": "./lib/index.js",
|
|
File without changes
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/lib/Common/node_test.js
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
console.log("=== ОБЫЧНЫЙ ERROR.STACK ===");
|
|
4
|
-
console.log(new Error().stack);
|
|
5
|
-
console.log("=== PREPARE STACK TRACE ===");
|
|
6
|
-
const originalPrepareStackTrace = Error.prepareStackTrace;
|
|
7
|
-
Error.prepareStackTrace = (_, stack) => stack;
|
|
8
|
-
const stack = new Error().stack;
|
|
9
|
-
Error.prepareStackTrace = originalPrepareStackTrace;
|
|
10
|
-
console.log("Stack frames:");
|
|
11
|
-
for (let i = 0; i < Math.min(stack.length, 3); i++) {
|
|
12
|
-
const frame = stack[i];
|
|
13
|
-
console.log(`Frame[${i}]: ${frame.getFileName()}:${frame.getLineNumber()}:${frame.getColumnNumber()}`);
|
|
14
|
-
}
|