socket-function 0.9.3 → 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.
Files changed (45) hide show
  1. package/.eslintrc.js +50 -50
  2. package/SocketFunction.ts +280 -280
  3. package/SocketFunctionTypes.ts +90 -90
  4. package/hot/HotReloadController.ts +105 -105
  5. package/mobx/UrlParam.ts +39 -39
  6. package/mobx/observer.tsx +49 -49
  7. package/mobx/promiseToObservable.tsx +41 -41
  8. package/package.json +1 -1
  9. package/require/CSSShim.ts +19 -19
  10. package/require/RequireController.ts +252 -252
  11. package/require/buffer.js +2368 -2368
  12. package/require/compileFlags.ts +44 -44
  13. package/require/require.html +13 -13
  14. package/require/require.js +464 -462
  15. package/spec.txt +115 -115
  16. package/src/CallFactory.ts +389 -389
  17. package/src/JSONLACKS/JSONLACKS.generated.js +17 -17
  18. package/src/JSONLACKS/JSONLACKS.pegjs +247 -247
  19. package/src/JSONLACKS/JSONLACKS.ts +441 -429
  20. package/src/args.ts +21 -21
  21. package/src/batching.ts +177 -170
  22. package/src/caching.ts +359 -318
  23. package/src/callHTTPHandler.ts +203 -203
  24. package/src/callManager.ts +134 -134
  25. package/src/certStore.ts +29 -29
  26. package/src/fixLargeNetworkCalls.ts +8 -8
  27. package/src/formatting/colors.ts +78 -78
  28. package/src/formatting/format.ts +160 -160
  29. package/src/formatting/logColors.ts +17 -17
  30. package/src/misc.ts +315 -302
  31. package/src/nodeCache.ts +92 -92
  32. package/src/nodeProxy.ts +54 -54
  33. package/src/profiling/getOwnTime.ts +107 -142
  34. package/src/profiling/measure.ts +289 -273
  35. package/src/profiling/stats.ts +212 -212
  36. package/src/profiling/tcpLagProxy.ts +63 -63
  37. package/src/storagePath.ts +10 -10
  38. package/src/tlsParsing.ts +96 -96
  39. package/src/types.ts +8 -8
  40. package/src/webSocketServer.ts +254 -250
  41. package/test/client.css +2 -2
  42. package/test/client.ts +46 -46
  43. package/test/server.ts +43 -43
  44. package/test/shared.ts +52 -52
  45. package/tsconfig.json +26 -26
package/src/misc.ts CHANGED
@@ -1,303 +1,316 @@
1
- import * as crypto from "crypto";
2
- import { canHaveChildren, MaybePromise } from "./types";
3
-
4
- export type Watchable<T> = (callback: (value: T) => void) => MaybePromise<void>;
5
-
6
- export function convertErrorStackToError(error: string): Error {
7
- let errorObj = new Error();
8
- errorObj.stack = String(error);
9
- errorObj.message = String(error).split("\n")[0].slice("Error: ".length);
10
- return errorObj;
11
- }
12
-
13
- export function sha256Hash(buffer: Buffer | string): string {
14
- return crypto.createHash("sha256").update(buffer).digest("hex");
15
- }
16
- export function sha256HashBuffer(buffer: Buffer | string): Buffer {
17
- return crypto.createHash("sha256").update(buffer).digest();
18
- }
19
- /** Async, but works both clientside and serverside. */
20
- export async function sha256HashPromise(buffer: Buffer) {
21
- if (isNode()) {
22
- return crypto.createHash("sha256").update(buffer).digest("hex");
23
- } else {
24
- let buf = await window.crypto.subtle.digest("SHA-256", buffer);
25
- return Buffer.from(buf).toString("hex");
26
- }
27
- }
28
- export async function sha256BufferPromise(buffer: Buffer): Promise<Buffer> {
29
- if (isNode()) {
30
- return crypto.createHash("sha256").update(buffer).digest();
31
- } else {
32
- let buf = await window.crypto.subtle.digest("SHA-256", buffer);
33
- return Buffer.from(buf);
34
- }
35
- }
36
-
37
-
38
- export function arrayEqual(a: unknown[], b: unknown[]) {
39
- if (a.length !== b.length) return false;
40
- for (let i = 0; i < a.length; i++) {
41
- if (a[i] !== b[i]) return false;
42
- }
43
- return true;
44
- }
45
- export function isNode() {
46
- return typeof document === "undefined";
47
- }
48
-
49
- export function isNodeTrue() {
50
- return isNode() as true;
51
- }
52
-
53
- export function formatNumberSuffixed(count: number): string {
54
- if (typeof count !== "number") return "0";
55
- if (count < 0) {
56
- return "-" + formatNumberSuffixed(-count);
57
- }
58
-
59
- let absValue = Math.abs(count);
60
-
61
- const extraFactor = 10;
62
- let divisor = 1;
63
- let suffix = "";
64
- if (absValue < 1000 * extraFactor) {
65
-
66
- } else if (absValue < 1000 * 1000 * extraFactor) {
67
- suffix = "K";
68
- divisor = 1000;
69
- } else if (absValue < 1000 * 1000 * 1000 * extraFactor) {
70
- suffix = "M";
71
- divisor = 1000 * 1000;
72
- } else {
73
- suffix = "B";
74
- divisor = 1000 * 1000 * 1000;
75
- }
76
- count /= divisor;
77
- absValue /= divisor;
78
-
79
- return Math.round(count).toString() + suffix;
80
- }
81
-
82
- export function list(count: number) {
83
- let arr: number[] = [];
84
- for (let i = 0; i < count; i++) {
85
- arr.push(i);
86
- }
87
- return arr;
88
- }
89
-
90
- export function recursiveFreeze<T>(obj: T): T {
91
- if (!canHaveChildren(obj)) return obj;
92
- let visited = new Set<unknown>();
93
- function iterate(obj: unknown) {
94
- if (!canHaveChildren(obj)) return;
95
- if (visited.has(obj)) return;
96
- visited.add(obj);
97
- Object.freeze(obj);
98
- let keys = getKeys(obj);
99
- for (let key of keys) {
100
- iterate(obj[key]);
101
- }
102
- }
103
- iterate(obj);
104
- return obj;
105
- }
106
- export type ArrayBufferViewTypes = Uint8Array | Int8Array | Uint16Array | Int16Array | Uint32Array | Int32Array | BigUint64Array | BigInt64Array | Float64Array | Float32Array | Uint8ClampedArray;
107
- export type BufferType = ArrayBuffer | SharedArrayBuffer | ArrayBufferViewTypes;
108
- export function isBufferType(obj: unknown): obj is BufferType {
109
- if (typeof obj !== "object") return false;
110
- if (!obj) return false;
111
- if (ArrayBuffer.isView(obj)) return true;
112
- if (obj instanceof ArrayBuffer) return true;
113
- if (global.SharedArrayBuffer && obj instanceof global.SharedArrayBuffer) return true;
114
- return false;
115
- }
116
- export function getKeys(obj: unknown) {
117
- if (typeof obj !== "object" && typeof obj !== "function" || obj === null) {
118
- return [];
119
- }
120
- if (obj instanceof MessagePort) {
121
- return [];
122
- }
123
- let keyArray: PropertyKey[];
124
- if (isBufferType(obj)) {
125
- keyArray = [];
126
- } else if (Array.isArray(obj)) {
127
- // NOTE: We convert the indexes to strings, because that is what javascript does,
128
- // and differing from it causes regressions that we simply cannot rectify (it breaks hashing
129
- // consistency).
130
- keyArray = Array(obj.length).fill(0).map((x, i) => String(i));
131
- } else {
132
- keyArray = Object.keys(obj);
133
- }
134
- for (let symbol of Object.getOwnPropertySymbols(obj)) {
135
- let key = Symbol.keyFor(symbol);
136
- if (key) {
137
- keyArray.push(symbol);
138
- }
139
- }
140
- return keyArray;
141
- }
142
- export function getStringKeys<T extends {}>(obj: T): ((keyof T) & string)[] {
143
- return Object.keys(obj) as any;
144
- }
145
-
146
- if (isNode()) {
147
- // TODO: Find a better place for this...
148
- process.on("unhandledRejection", async (reason: any, promise) => {
149
- console.error(`Uncaught promise rejection: ${String(reason.stack || reason)}`);
150
- });
151
- }
152
-
153
- export function keyBy<T, K>(arr: T[], getKey: (value: T) => K): Map<K, T> {
154
- let map = new Map<K, T>();
155
- for (let item of arr) {
156
- map.set(getKey(item), item);
157
- }
158
- return map;
159
- }
160
- export function keyByArray<T, K>(arr: T[], getKey: (value: T) => K): Map<K, T[]> {
161
- let map = new Map<K, T[]>();
162
- for (let item of arr) {
163
- let key = getKey(item);
164
- let arr = map.get(key);
165
- if (!arr) {
166
- arr = [];
167
- map.set(key, arr);
168
- }
169
- arr.push(item);
170
- }
171
- return map;
172
- }
173
-
174
- export function deepCloneJSON<T>(obj: T): T {
175
- return JSON.parse(JSON.stringify(obj));
176
- }
177
-
178
-
179
-
180
- export interface PromiseObj<T = void> {
181
- resolve: (value: T | Promise<T>) => void;
182
- reject: (error: any) => void;
183
- promise: Promise<T>;
184
- value: { value?: T; error?: string } | undefined;
185
- /** Resolve called does not mean the value is ready, as it may be resolved with a promise. */
186
- resolveCalled?: boolean;
187
- }
188
-
189
- export function promiseObj<T = void>(): PromiseObj<T> {
190
- let resolve!: (value: T | Promise<T>) => void;
191
- let reject!: (error: any) => void;
192
- let promise = new Promise<T>((_resolve, _reject) => {
193
- resolve = _resolve;
194
- reject = _reject;
195
- });
196
- let obj: PromiseObj<T> = {
197
- resolve(value: T | Promise<T>) {
198
- obj.resolveCalled = true;
199
- if (typeof value === "object" && value !== null && value instanceof Promise) {
200
- value.then(
201
- value => obj.value = { value },
202
- error => obj.value = { error },
203
- );
204
- } else {
205
- obj.value = { value };
206
- }
207
- resolve(value);
208
- },
209
- reject,
210
- promise,
211
- value: undefined
212
- };
213
- promise.then(value => obj.value = { value }, error => obj.value = { error });
214
- return obj;
215
- }
216
-
217
-
218
- // Allows an immediate call, then delays the next call until the first call finishes + delay
219
- // - Drops all but the latest call, but only resolves the promises return to all
220
- // calls once the latest call finishes.
221
- // - Esentially the same as saying "don't run this function too often, don't run it in parallel,
222
- // and don't let functions runs be too close together".
223
- export function throttleFunction<Args extends any[]>(
224
- delay: number,
225
- fnc: (...args: Args) => MaybePromise<void>
226
- ): (...args: Args) => Promise<void> {
227
- let nextAllowedCall = 0;
228
- let pendingArgs: { args: Args; promiseObj: PromiseObj<void> } | undefined = undefined;
229
- function doCall(args: Args, promiseObj: PromiseObj<void>) {
230
- nextAllowedCall = Number.POSITIVE_INFINITY;
231
- try {
232
- let result = fnc(...args);
233
- promiseObj.resolve(result);
234
- if (result instanceof Promise) {
235
- result.finally(() => {
236
- afterCall(Date.now() + delay);
237
- });
238
- } else {
239
- afterCall(Date.now() + delay);
240
- }
241
- } catch (e: any) {
242
- debugger;
243
- promiseObj.reject(e);
244
- afterCall(Date.now() + delay);
245
- }
246
- }
247
- function afterCall(setNextAllowedCall: number | undefined) {
248
-
249
- // NOTE: Ignore error, we really shouldn't have any here
250
- if (setNextAllowedCall) {
251
- nextAllowedCall = setNextAllowedCall;
252
- } else {
253
- if (nextAllowedCall === Number.POSITIVE_INFINITY) return;
254
- }
255
- if (!pendingArgs) return;
256
- if (Date.now() > nextAllowedCall) {
257
- let args = pendingArgs;
258
- pendingArgs = undefined;
259
- // Delay, so we don't turn a series of sequential calls to a series of nested calls
260
- // (which will cause a stack overflow)
261
- nextAllowedCall = Number.POSITIVE_INFINITY;
262
- setImmediate(() => doCall(args.args, args.promiseObj));
263
- } else {
264
- setTimeout(() => {
265
- if (pendingArgs) {
266
- let args = pendingArgs;
267
- pendingArgs = undefined;
268
- doCall(args.args, args.promiseObj);
269
- }
270
- }, nextAllowedCall - Date.now());
271
- }
272
- }
273
- return function (...args: Args): Promise<void> {
274
- if (pendingArgs) {
275
- pendingArgs.args = args;
276
- return pendingArgs.promiseObj.promise;
277
- }
278
- let time = Date.now();
279
- if (time > nextAllowedCall) {
280
- let promise = promiseObj();
281
- doCall(args, promise);
282
- return promise.promise;
283
- } else {
284
- pendingArgs = { args, promiseObj: promiseObj() };
285
- afterCall(undefined);
286
- return pendingArgs.promiseObj.promise;
287
- }
288
- };
289
- }
290
-
291
-
292
- export function nextId() {
293
- return Date.now() + "_" + Math.random();
294
- }
295
-
296
- export function arrayFromOrderObject<T>(obj: { [order: number]: T }): T[] {
297
- if (Array.isArray(obj)) return obj.slice();
298
- return Object.entries(obj).sort((a, b) => +a[0] - +b[0]).map(x => x[1]).filter(x => x !== undefined && x !== null);
299
- }
300
-
301
- export function last<T>(arr: T[]): T | undefined {
302
- return arr[arr.length - 1];
1
+ import * as crypto from "crypto";
2
+ import { canHaveChildren, MaybePromise } from "./types";
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
+
11
+ export type Watchable<T> = (callback: (value: T) => void) => MaybePromise<void>;
12
+
13
+ export function convertErrorStackToError(error: string): Error {
14
+ let errorObj = new Error();
15
+ errorObj.stack = String(error);
16
+ errorObj.message = String(error).split("\n")[0].slice("Error: ".length);
17
+ return errorObj;
18
+ }
19
+
20
+ export function sha256Hash(buffer: Buffer | string): string {
21
+ return crypto.createHash("sha256").update(buffer).digest("hex");
22
+ }
23
+ export function sha256HashBuffer(buffer: Buffer | string): Buffer {
24
+ return crypto.createHash("sha256").update(buffer).digest();
25
+ }
26
+ /** Async, but works both clientside and serverside. */
27
+ export async function sha256HashPromise(buffer: Buffer) {
28
+ if (isNode()) {
29
+ return crypto.createHash("sha256").update(buffer).digest("hex");
30
+ } else {
31
+ let buf = await window.crypto.subtle.digest("SHA-256", buffer);
32
+ return Buffer.from(buf).toString("hex");
33
+ }
34
+ }
35
+ export async function sha256BufferPromise(buffer: Buffer): Promise<Buffer> {
36
+ if (isNode()) {
37
+ return crypto.createHash("sha256").update(buffer).digest();
38
+ } else {
39
+ let buf = await window.crypto.subtle.digest("SHA-256", buffer);
40
+ return Buffer.from(buf);
41
+ }
42
+ }
43
+
44
+
45
+ export function arrayEqual(a: unknown[], b: unknown[]) {
46
+ if (a.length !== b.length) return false;
47
+ for (let i = 0; i < a.length; i++) {
48
+ if (a[i] !== b[i]) return false;
49
+ }
50
+ return true;
51
+ }
52
+ export function isNode() {
53
+ return typeof document === "undefined";
54
+ }
55
+
56
+ export function isNodeTrue() {
57
+ return isNode() as true;
58
+ }
59
+
60
+ export function formatNumberSuffixed(count: number): string {
61
+ if (typeof count !== "number") return "0";
62
+ if (count < 0) {
63
+ return "-" + formatNumberSuffixed(-count);
64
+ }
65
+
66
+ let absValue = Math.abs(count);
67
+
68
+ const extraFactor = 10;
69
+ let divisor = 1;
70
+ let suffix = "";
71
+ if (absValue < 1000 * extraFactor) {
72
+
73
+ } else if (absValue < 1000 * 1000 * extraFactor) {
74
+ suffix = "K";
75
+ divisor = 1000;
76
+ } else if (absValue < 1000 * 1000 * 1000 * extraFactor) {
77
+ suffix = "M";
78
+ divisor = 1000 * 1000;
79
+ } else {
80
+ suffix = "B";
81
+ divisor = 1000 * 1000 * 1000;
82
+ }
83
+ count /= divisor;
84
+ absValue /= divisor;
85
+
86
+ return Math.round(count).toString() + suffix;
87
+ }
88
+
89
+ export function list(count: number) {
90
+ let arr: number[] = [];
91
+ for (let i = 0; i < count; i++) {
92
+ arr.push(i);
93
+ }
94
+ return arr;
95
+ }
96
+
97
+ export function recursiveFreeze<T>(obj: T): T {
98
+ if (!canHaveChildren(obj)) return obj;
99
+ let visited = new Set<unknown>();
100
+ function iterate(obj: unknown) {
101
+ if (!canHaveChildren(obj)) return;
102
+ if (visited.has(obj)) return;
103
+ visited.add(obj);
104
+ Object.freeze(obj);
105
+ let keys = getKeys(obj);
106
+ for (let key of keys) {
107
+ iterate(obj[key]);
108
+ }
109
+ }
110
+ iterate(obj);
111
+ return obj;
112
+ }
113
+ export type ArrayBufferViewTypes = Uint8Array | Int8Array | Uint16Array | Int16Array | Uint32Array | Int32Array | BigUint64Array | BigInt64Array | Float64Array | Float32Array | Uint8ClampedArray;
114
+ export type BufferType = ArrayBuffer | SharedArrayBuffer | ArrayBufferViewTypes;
115
+ export function isBufferType(obj: unknown): obj is BufferType {
116
+ if (typeof obj !== "object") return false;
117
+ if (!obj) return false;
118
+ if (ArrayBuffer.isView(obj)) return true;
119
+ if (obj instanceof ArrayBuffer) return true;
120
+ if (global.SharedArrayBuffer && obj instanceof global.SharedArrayBuffer) return true;
121
+ return false;
122
+ }
123
+ export function getKeys(obj: unknown) {
124
+ if (typeof obj !== "object" && typeof obj !== "function" || obj === null) {
125
+ return [];
126
+ }
127
+ if (obj instanceof MessagePort) {
128
+ return [];
129
+ }
130
+ let keyArray: PropertyKey[];
131
+ if (isBufferType(obj)) {
132
+ keyArray = [];
133
+ } else if (Array.isArray(obj)) {
134
+ // NOTE: We convert the indexes to strings, because that is what javascript does,
135
+ // and differing from it causes regressions that we simply cannot rectify (it breaks hashing
136
+ // consistency).
137
+ keyArray = Array(obj.length).fill(0).map((x, i) => String(i));
138
+ } else {
139
+ keyArray = Object.keys(obj);
140
+ }
141
+ for (let symbol of Object.getOwnPropertySymbols(obj)) {
142
+ let key = Symbol.keyFor(symbol);
143
+ if (key) {
144
+ keyArray.push(symbol);
145
+ }
146
+ }
147
+ return keyArray;
148
+ }
149
+ export function getStringKeys<T extends {}>(obj: T): ((keyof T) & string)[] {
150
+ return Object.keys(obj) as any;
151
+ }
152
+
153
+ if (isNode()) {
154
+ // TODO: Find a better place for this...
155
+ process.on("unhandledRejection", async (reason: any, promise) => {
156
+ console.error(`Uncaught promise rejection: ${String(reason.stack || reason)}`);
157
+ });
158
+ }
159
+
160
+ export function keyBy<T, K>(arr: T[], getKey: (value: T) => K): Map<K, T> {
161
+ let map = new Map<K, T>();
162
+ for (let item of arr) {
163
+ map.set(getKey(item), item);
164
+ }
165
+ return map;
166
+ }
167
+ export function keyByArray<T, K>(arr: T[], getKey: (value: T) => K): Map<K, T[]> {
168
+ let map = new Map<K, T[]>();
169
+ for (let item of arr) {
170
+ let key = getKey(item);
171
+ let arr = map.get(key);
172
+ if (!arr) {
173
+ arr = [];
174
+ map.set(key, arr);
175
+ }
176
+ arr.push(item);
177
+ }
178
+ return map;
179
+ }
180
+
181
+ export function deepCloneJSON<T>(obj: T): T {
182
+ if (obj === undefined) return obj;
183
+ return JSON.parse(JSON.stringify(obj));
184
+ }
185
+
186
+
187
+
188
+ export interface PromiseObj<T = void> {
189
+ resolve: (value: T | Promise<T>) => void;
190
+ reject: (error: any) => void;
191
+ promise: Promise<T>;
192
+ value: { value?: T; error?: string } | undefined;
193
+ /** Resolve called does not mean the value is ready, as it may be resolved with a promise. */
194
+ resolveCalled?: boolean;
195
+ }
196
+
197
+ export function promiseObj<T = void>(): PromiseObj<T> {
198
+ let resolve!: (value: T | Promise<T>) => void;
199
+ let reject!: (error: any) => void;
200
+ let promise = new Promise<T>((_resolve, _reject) => {
201
+ resolve = _resolve;
202
+ reject = _reject;
203
+ });
204
+ let obj: PromiseObj<T> = {
205
+ resolve(value: T | Promise<T>) {
206
+ obj.resolveCalled = true;
207
+ if (typeof value === "object" && value !== null && value instanceof Promise) {
208
+ value.then(
209
+ value => obj.value = { value },
210
+ error => obj.value = { error },
211
+ );
212
+ } else {
213
+ obj.value = { value };
214
+ }
215
+ resolve(value);
216
+ },
217
+ reject,
218
+ promise,
219
+ value: undefined
220
+ };
221
+ promise.then(value => obj.value = { value }, error => obj.value = { error });
222
+ return obj;
223
+ }
224
+
225
+
226
+ // Allows an immediate call, then delays the next call until the first call finishes + delay
227
+ // - Drops all but the latest call, but only resolves the promises return to all
228
+ // calls once the latest call finishes.
229
+ // - Esentially the same as saying "don't run this function too often, don't run it in parallel,
230
+ // and don't let functions runs be too close together".
231
+ export function throttleFunction<Args extends any[]>(
232
+ delay: number,
233
+ fnc: (...args: Args) => MaybePromise<void>
234
+ ): (...args: Args) => Promise<void> {
235
+ let nextAllowedCall = 0;
236
+ let pendingArgs: { args: Args; promiseObj: PromiseObj<void> } | undefined = undefined;
237
+ function doCall(args: Args, promiseObj: PromiseObj<void>) {
238
+ nextAllowedCall = Number.POSITIVE_INFINITY;
239
+ try {
240
+ let result = fnc(...args);
241
+ promiseObj.resolve(result);
242
+ if (result instanceof Promise) {
243
+ result.finally(() => {
244
+ afterCall(Date.now() + delay);
245
+ });
246
+ } else {
247
+ afterCall(Date.now() + delay);
248
+ }
249
+ } catch (e: any) {
250
+ debugger;
251
+ promiseObj.reject(e);
252
+ afterCall(Date.now() + delay);
253
+ }
254
+ }
255
+ function afterCall(setNextAllowedCall: number | undefined) {
256
+
257
+ // NOTE: Ignore error, we really shouldn't have any here
258
+ if (setNextAllowedCall) {
259
+ nextAllowedCall = setNextAllowedCall;
260
+ } else {
261
+ if (nextAllowedCall === Number.POSITIVE_INFINITY) return;
262
+ }
263
+ if (!pendingArgs) return;
264
+ if (Date.now() > nextAllowedCall) {
265
+ let args = pendingArgs;
266
+ pendingArgs = undefined;
267
+ // Delay, so we don't turn a series of sequential calls to a series of nested calls
268
+ // (which will cause a stack overflow)
269
+ nextAllowedCall = Number.POSITIVE_INFINITY;
270
+ setImmediate(() => doCall(args.args, args.promiseObj));
271
+ } else {
272
+ setTimeout(() => {
273
+ if (pendingArgs) {
274
+ let args = pendingArgs;
275
+ pendingArgs = undefined;
276
+ doCall(args.args, args.promiseObj);
277
+ }
278
+ }, nextAllowedCall - Date.now());
279
+ }
280
+ }
281
+ return function (...args: Args): Promise<void> {
282
+ if (pendingArgs) {
283
+ pendingArgs.args = args;
284
+ return pendingArgs.promiseObj.promise;
285
+ }
286
+ let time = Date.now();
287
+ if (time > nextAllowedCall) {
288
+ let promise = promiseObj();
289
+ doCall(args, promise);
290
+ return promise.promise;
291
+ } else {
292
+ pendingArgs = { args, promiseObj: promiseObj() };
293
+ afterCall(undefined);
294
+ return pendingArgs.promiseObj.promise;
295
+ }
296
+ };
297
+ }
298
+
299
+
300
+ export function nextId() {
301
+ return Date.now() + "_" + Math.random();
302
+ }
303
+
304
+ export function arrayFromOrderObject<T>(obj: { [order: number]: T }): T[] {
305
+ if (Array.isArray(obj)) return obj.slice();
306
+ return Object.entries(obj).sort((a, b) => +a[0] - +b[0]).map(x => x[1]).filter(x => x !== undefined && x !== null);
307
+ }
308
+
309
+ export function last<T>(arr: T[]): T | undefined {
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;
303
316
  }