socket-function 0.9.2 → 0.9.4

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 +2 -2
  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 +462 -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 +429 -429
  20. package/src/args.ts +21 -21
  21. package/src/batching.ts +170 -170
  22. package/src/caching.ts +324 -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 +303 -302
  31. package/src/nodeCache.ts +92 -92
  32. package/src/nodeProxy.ts +54 -54
  33. package/src/profiling/getOwnTime.ts +142 -142
  34. package/src/profiling/measure.ts +273 -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 +250 -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/caching.ts CHANGED
@@ -1,319 +1,325 @@
1
- import { arrayEqual } from "./misc";
2
- import { AnyFunction, Args, canHaveChildren } from "./types";
3
-
4
- export function lazy<T>(factory: () => T) {
5
- let value: { value: T } | undefined = undefined;
6
- function get() {
7
- if (!value) {
8
- value = { value: factory() };
9
- }
10
- return value.value;
11
- };
12
- get.reset = () => {
13
- value = undefined;
14
- };
15
- return get;
16
- }
17
-
18
- // NOTE: The reason we need to periodically clear, is because sometimes a very small
19
- // part of a large payload (ex, persisted overrides) is cached, which then results
20
- // in the whole payload being cached, which results in a lot of memory being used.
21
-
22
- // IMPORTANT! The cleanup functions CANNOT close upon anything, or else they will cause leaks!
23
- // All data they use should be in data.
24
- interface CleanupFnc<T extends object> {
25
- (data: T): void;
26
- }
27
-
28
-
29
- // NOTE: Empty arrays are so common, that it is useful to represent them as the same
30
- // emtpy array, to increase cache hit rates.
31
- const emptyArray: any[] = [];
32
- export function cacheEmptyArray<T>(array: T[]): T[] {
33
- if (array.length === 0) return emptyArray;
34
- return array;
35
- }
36
-
37
- export function cache<Output, Key>(getValue: (key: Key) => Output): {
38
- (key: Key): Output;
39
- // NOTE: If you want to clear all, just make a new cache!
40
- clear(key: Key): void;
41
- forceSet(key: Key, value: Output): void;
42
- getAllKeys(): Key[];
43
- } {
44
- let startingCalculating = new Set<Key>();
45
- let values = new Map<Key, Output>();
46
- function cache(input: Key) {
47
- let key = input;
48
- if (values.has(key)) {
49
- return values.get(key) as any;
50
- }
51
- if (startingCalculating.has(key)) {
52
- // TODO: Fix the types here, by throwing, and then for the cases
53
- // that don't throw, make our output type include undefined
54
- return undefined;
55
- }
56
- startingCalculating.add(key);
57
- let value = getValue(input);
58
- values.set(key, value);
59
- return value;
60
- }
61
- cache.clear = (key: Key) => {
62
- values.delete(key);
63
- startingCalculating.delete(key);
64
- };
65
- cache.forceSet = (key: Key, value: Output) => {
66
- values.set(key, value);
67
- startingCalculating.add(key);
68
- };
69
- cache.getAllKeys = () => {
70
- return [...values.keys()];
71
- };
72
- return cache;
73
- }
74
-
75
-
76
- /** Makes a cache that limits the number of entries, allowing you to put arbitrary data in it
77
- * without worrying about leaking memory
78
- */
79
- export function cacheLimited<Output, Key>(
80
- // NOTE: We can't calculate what limit should be based on comparing the evaluation time
81
- // and the time to compare against the values. Because, even if finding a match takes far longer than
82
- // calculating, keeping a consistent output can save (a considerable amount of) time in downstream caches.
83
- maxCount: number,
84
- getValue: (key: Key) => Output
85
- ): (key: Key) => Output {
86
- let startingCalculating = new Set<Key>();
87
- let values = new Map<Key, Output>();
88
- return (input) => {
89
- let key = input;
90
- if (values.has(key)) {
91
- return values.get(key) as any;
92
- }
93
- if (startingCalculating.has(key)) {
94
- throw new Error(`Cyclic access in cache`);
95
- }
96
- startingCalculating.add(key);
97
-
98
- // Clear when it gets too big. This is kind of like a worse
99
- // least recently used cache, because entries that are accessed
100
- // often will quickly get put back in. This is effective as long
101
- // as accesses take similar amounts of time. If there is a very slow
102
- // and very commonly accessed value, it could be evicted by many very
103
- // fast accesses, which would be unfortunate.
104
- if (values.size >= maxCount) {
105
- values.clear();
106
- startingCalculating.clear();
107
- }
108
-
109
- let value = getValue(input);
110
- values.set(key, value);
111
- return value;
112
- };
113
- }
114
-
115
- export function cacheWeak<Output, Key extends object>(getValue: (key: Key) => Output): (key: Key) => Output {
116
- let state = {
117
- startingCalculating: new WeakSet<Key>(),
118
- values: new WeakMap<Key, Output>(),
119
- };
120
-
121
- return (input) => {
122
- let key = input;
123
- if (state.values.has(key)) {
124
- return state.values.get(key) as any;
125
- }
126
- if (state.startingCalculating.has(key)) {
127
- throw new Error(`Cyclic access in cacheWeak`);
128
- }
129
- state.startingCalculating.add(key);
130
- let value = getValue(input);
131
- state.values.set(key, value);
132
- return value;
133
- };
134
- }
135
-
136
- // A list cache, which... maybe faster than a Map?
137
- export function cacheList<Value>(
138
- getLength: () => number,
139
- getValue: (index: number) => Value,
140
- ): { (index: number): Value; } {
141
- let state = {
142
- cache: [] as Value[],
143
- length: undefined as undefined | number,
144
- getLength,
145
- };
146
- function get(i: number) {
147
- let cache = state.cache;
148
- let length = state.length;
149
- if (length === undefined) {
150
- length = state.length = state.getLength();
151
- }
152
- if (i < 0 || i >= length) {
153
- throw new Error(`Index out of bounds`);
154
- }
155
- if (!(i in cache)) {
156
- cache[i] = getValue(i);
157
- }
158
- return cache[i];
159
- };
160
- return get;
161
- }
162
-
163
- function cacheArrayEqualCleanup(state: any) {
164
- state.cache = [];
165
- }
166
-
167
- /** A cache half way between caching based on === and caching based on hash. Caches
168
- * based on arrayEqual, which does === on all values in an array. Requires localized
169
- * caching (as the comparisons don't scale with many candidates, unlike hashing),
170
- * however works with non trival transformations (ex, resolving many persisted overrides
171
- * to get a value), unlike cache().
172
- * Also, limits itself, more of a performance optimization than memory optimization, as it scales
173
- * very poorly with the number of candidates.
174
- *
175
- * TIMING: About 6us with limit = 100, array size = 294, and the cache being full.
176
- */
177
- export function cacheArrayEqual<Input extends unknown[] | undefined, Output>(
178
- map: (arrays: Input) => Output,
179
- limit = 10
180
- ): {
181
- (array: Input): Output;
182
- clear(array: Input): void;
183
- } {
184
- let state: {
185
- cache: {
186
- input: Input;
187
- output: Output;
188
- }[]
189
- } = { cache: [] };
190
- function isMatch(lhs: Input, rhs: Input) {
191
- if (lhs === rhs) {
192
- return true;
193
- }
194
- if (lhs === undefined || rhs === undefined) {
195
- return false;
196
- }
197
- if (arrayEqual(lhs, rhs)) {
198
- return true;
199
- }
200
- return false;
201
- }
202
- return Object.assign(
203
- (input: Input) => {
204
- let cache = state.cache;
205
- for (let obj of cache) {
206
- if (isMatch(obj.input, input)) {
207
- return obj.output;
208
- }
209
- }
210
- let output = map(input);
211
- cache.unshift({ input, output });
212
- while (cache.length > limit) {
213
- cache.pop();
214
- }
215
- return output;
216
- },
217
- {
218
- clear(array: Input) {
219
- for (let i = state.cache.length - 1; i >= 0; i--) {
220
- if (isMatch(state.cache[i].input, array)) {
221
- state.cache.splice(i, 1);
222
- }
223
- }
224
- }
225
- }
226
- );
227
- }
228
-
229
- /** Caches when arguments are ===. See cacheArrayEqual */
230
- export function cacheArgsEqual<Fnc extends AnyFunction>(
231
- fnc: Fnc,
232
- limit = 10
233
- ): Fnc & { clear(...args: Args<Fnc>): void } {
234
- let cache = cacheArrayEqual((args: unknown[]) => {
235
- return fnc(...args);
236
- }, limit);
237
- return Object.assign(
238
- ((...args: unknown[]) => {
239
- return cache(args);
240
- }) as Fnc,
241
- {
242
- clear(...args: unknown[]) {
243
- cache.clear(args);
244
- }
245
- }
246
- );
247
- }
248
-
249
- export function cacheJSONArgsEqual<Fnc extends AnyFunction>(
250
- fnc: Fnc,
251
- limit = 10
252
- ): Fnc {
253
- let cache = cacheLimited(limit, (argsJSON: string) => {
254
- return fnc(...JSON.parse(argsJSON));
255
- });
256
- return ((...args: unknown[]) => {
257
- return cache(JSON.stringify(args));
258
- }) as Fnc;
259
- }
260
-
261
- export function cacheShallowConfigArgEqual<Fnc extends AnyFunction>(
262
- fnc: Fnc,
263
- limit = 10
264
- ): Fnc & {
265
- clear(...args: Args<Fnc>): void;
266
- } {
267
- let cache = cacheArrayEqual((kvpsFlat: unknown[]) => {
268
- output.missCount++;
269
- let arg: any;
270
- if (kvpsFlat.length === 1) {
271
- arg = kvpsFlat[0];
272
- } else {
273
- let kvps: [unknown, unknown][] = [];
274
- for (let i = 0; i < kvpsFlat.length; i += 2) {
275
- kvps.push([kvpsFlat[i], kvpsFlat[i + 1]]);
276
- }
277
- arg = Object.fromEntries(kvps);
278
- }
279
- return fnc(arg);
280
- }, limit);
281
- function getKVPs(configArg: object) {
282
- if (!canHaveChildren(configArg) || Array.isArray(configArg)) {
283
- return [configArg];
284
- }
285
- let keys = Object.keys(configArg);
286
- keys.sort();
287
- return keys.flatMap(key => [key, configArg[key]]);
288
- }
289
- let output = Object.assign(
290
- ((configArg: object) => {
291
- output.callCount++;
292
- return cache(getKVPs(configArg));
293
- }) as Fnc,
294
- {
295
- clear(configArg: object) {
296
- cache.clear(getKVPs(configArg));
297
- },
298
- callCount: 0,
299
- missCount: 0,
300
- }
301
- );
302
- return output;
303
- }
304
-
305
-
306
- export function externalCache<Key, Value>(): {
307
- get: (key: Key) => Value | undefined;
308
- set: (key: Key, value: Value) => void;
309
- } {
310
- let values = new Map<Key, Value>();
311
- return {
312
- get: (key) => {
313
- return values.get(key);
314
- },
315
- set: (key, value) => {
316
- values.set(key, value);
317
- },
318
- };
1
+ import { arrayEqual } from "./misc";
2
+ import { AnyFunction, Args, canHaveChildren } from "./types";
3
+
4
+ export function lazy<T>(factory: () => T) {
5
+ let value: { value: T } | undefined = undefined;
6
+ function get() {
7
+ if (!value) {
8
+ value = { value: factory() };
9
+ }
10
+ return value.value;
11
+ };
12
+ get.reset = () => {
13
+ value = undefined;
14
+ };
15
+ return get;
16
+ }
17
+
18
+ // NOTE: The reason we need to periodically clear, is because sometimes a very small
19
+ // part of a large payload (ex, persisted overrides) is cached, which then results
20
+ // in the whole payload being cached, which results in a lot of memory being used.
21
+
22
+ // IMPORTANT! The cleanup functions CANNOT close upon anything, or else they will cause leaks!
23
+ // All data they use should be in data.
24
+ interface CleanupFnc<T extends object> {
25
+ (data: T): void;
26
+ }
27
+
28
+
29
+ // NOTE: Empty arrays are so common, that it is useful to represent them as the same
30
+ // emtpy array, to increase cache hit rates.
31
+ const emptyArray: any[] = [];
32
+ export function cacheEmptyArray<T>(array: T[]): T[] {
33
+ if (array.length === 0) return emptyArray;
34
+ return array;
35
+ }
36
+
37
+ export function cache<Output, Key>(getValue: (key: Key) => Output): {
38
+ (key: Key): Output;
39
+ // NOTE: If you want to clear all, just make a new cache!
40
+ clear(key: Key): void;
41
+ forceSet(key: Key, value: Output): void;
42
+ getAllKeys(): Key[];
43
+ } {
44
+ let startingCalculating = new Set<Key>();
45
+ let values = new Map<Key, Output>();
46
+ function cache(input: Key) {
47
+ let key = input;
48
+ if (values.has(key)) {
49
+ return values.get(key) as any;
50
+ }
51
+ if (startingCalculating.has(key)) {
52
+ // TODO: Fix the types here, by throwing, and then for the cases
53
+ // that don't throw, make our output type include undefined
54
+ return undefined;
55
+ }
56
+ startingCalculating.add(key);
57
+ let value = getValue(input);
58
+ values.set(key, value);
59
+ return value;
60
+ }
61
+ cache.clear = (key: Key) => {
62
+ values.delete(key);
63
+ startingCalculating.delete(key);
64
+ };
65
+ cache.forceSet = (key: Key, value: Output) => {
66
+ values.set(key, value);
67
+ startingCalculating.add(key);
68
+ };
69
+ cache.getAllKeys = () => {
70
+ return [...values.keys()];
71
+ };
72
+ return cache;
73
+ }
74
+
75
+
76
+ /** Makes a cache that limits the number of entries, allowing you to put arbitrary data in it
77
+ * without worrying about leaking memory
78
+ */
79
+ export function cacheLimited<Output, Key>(
80
+ // NOTE: We can't calculate what limit should be based on comparing the evaluation time
81
+ // and the time to compare against the values. Because, even if finding a match takes far longer than
82
+ // calculating, keeping a consistent output can save (a considerable amount of) time in downstream caches.
83
+ maxCount: number,
84
+ getValue: (key: Key) => Output
85
+ ) {
86
+ let startingCalculating = new Set<Key>();
87
+ let values = new Map<Key, Output>();
88
+ function get(input: Key): Output {
89
+ let key = input;
90
+ if (values.has(key)) {
91
+ return values.get(key) as any;
92
+ }
93
+ if (startingCalculating.has(key)) {
94
+ throw new Error(`Cyclic access in cache`);
95
+ }
96
+ startingCalculating.add(key);
97
+
98
+ // Clear when it gets too big. This is kind of like a worse
99
+ // least recently used cache, because entries that are accessed
100
+ // often will quickly get put back in. This is effective as long
101
+ // as accesses take similar amounts of time. If there is a very slow
102
+ // and very commonly accessed value, it could be evicted by many very
103
+ // fast accesses, which would be unfortunate.
104
+ if (values.size >= maxCount) {
105
+ values.clear();
106
+ startingCalculating.clear();
107
+ }
108
+
109
+ let value = getValue(input);
110
+ values.set(key, value);
111
+ return value;
112
+ }
113
+ get["forceSet"] = (key: Key, value: Output) => {
114
+ values.set(key, value);
115
+ startingCalculating.add(key);
116
+ };
117
+
118
+ return get;
119
+ }
120
+
121
+ export function cacheWeak<Output, Key extends object>(getValue: (key: Key) => Output): (key: Key) => Output {
122
+ let state = {
123
+ startingCalculating: new WeakSet<Key>(),
124
+ values: new WeakMap<Key, Output>(),
125
+ };
126
+
127
+ return (input) => {
128
+ let key = input;
129
+ if (state.values.has(key)) {
130
+ return state.values.get(key) as any;
131
+ }
132
+ if (state.startingCalculating.has(key)) {
133
+ throw new Error(`Cyclic access in cacheWeak`);
134
+ }
135
+ state.startingCalculating.add(key);
136
+ let value = getValue(input);
137
+ state.values.set(key, value);
138
+ return value;
139
+ };
140
+ }
141
+
142
+ // A list cache, which... maybe faster than a Map?
143
+ export function cacheList<Value>(
144
+ getLength: () => number,
145
+ getValue: (index: number) => Value,
146
+ ): { (index: number): Value; } {
147
+ let state = {
148
+ cache: [] as Value[],
149
+ length: undefined as undefined | number,
150
+ getLength,
151
+ };
152
+ function get(i: number) {
153
+ let cache = state.cache;
154
+ let length = state.length;
155
+ if (length === undefined) {
156
+ length = state.length = state.getLength();
157
+ }
158
+ if (i < 0 || i >= length) {
159
+ throw new Error(`Index out of bounds`);
160
+ }
161
+ if (!(i in cache)) {
162
+ cache[i] = getValue(i);
163
+ }
164
+ return cache[i];
165
+ };
166
+ return get;
167
+ }
168
+
169
+ function cacheArrayEqualCleanup(state: any) {
170
+ state.cache = [];
171
+ }
172
+
173
+ /** A cache half way between caching based on === and caching based on hash. Caches
174
+ * based on arrayEqual, which does === on all values in an array. Requires localized
175
+ * caching (as the comparisons don't scale with many candidates, unlike hashing),
176
+ * however works with non trival transformations (ex, resolving many persisted overrides
177
+ * to get a value), unlike cache().
178
+ * Also, limits itself, more of a performance optimization than memory optimization, as it scales
179
+ * very poorly with the number of candidates.
180
+ *
181
+ * TIMING: About 6us with limit = 100, array size = 294, and the cache being full.
182
+ */
183
+ export function cacheArrayEqual<Input extends unknown[] | undefined, Output>(
184
+ map: (arrays: Input) => Output,
185
+ limit = 10
186
+ ): {
187
+ (array: Input): Output;
188
+ clear(array: Input): void;
189
+ } {
190
+ let state: {
191
+ cache: {
192
+ input: Input;
193
+ output: Output;
194
+ }[]
195
+ } = { cache: [] };
196
+ function isMatch(lhs: Input, rhs: Input) {
197
+ if (lhs === rhs) {
198
+ return true;
199
+ }
200
+ if (lhs === undefined || rhs === undefined) {
201
+ return false;
202
+ }
203
+ if (arrayEqual(lhs, rhs)) {
204
+ return true;
205
+ }
206
+ return false;
207
+ }
208
+ return Object.assign(
209
+ (input: Input) => {
210
+ let cache = state.cache;
211
+ for (let obj of cache) {
212
+ if (isMatch(obj.input, input)) {
213
+ return obj.output;
214
+ }
215
+ }
216
+ let output = map(input);
217
+ cache.unshift({ input, output });
218
+ while (cache.length > limit) {
219
+ cache.pop();
220
+ }
221
+ return output;
222
+ },
223
+ {
224
+ clear(array: Input) {
225
+ for (let i = state.cache.length - 1; i >= 0; i--) {
226
+ if (isMatch(state.cache[i].input, array)) {
227
+ state.cache.splice(i, 1);
228
+ }
229
+ }
230
+ }
231
+ }
232
+ );
233
+ }
234
+
235
+ /** Caches when arguments are ===. See cacheArrayEqual */
236
+ export function cacheArgsEqual<Fnc extends AnyFunction>(
237
+ fnc: Fnc,
238
+ limit = 10
239
+ ): Fnc & { clear(...args: Args<Fnc>): void } {
240
+ let cache = cacheArrayEqual((args: unknown[]) => {
241
+ return fnc(...args);
242
+ }, limit);
243
+ return Object.assign(
244
+ ((...args: unknown[]) => {
245
+ return cache(args);
246
+ }) as Fnc,
247
+ {
248
+ clear(...args: unknown[]) {
249
+ cache.clear(args);
250
+ }
251
+ }
252
+ );
253
+ }
254
+
255
+ export function cacheJSONArgsEqual<Fnc extends AnyFunction>(
256
+ fnc: Fnc,
257
+ limit = 10
258
+ ): Fnc {
259
+ let cache = cacheLimited(limit, (argsJSON: string) => {
260
+ return fnc(...JSON.parse(argsJSON));
261
+ });
262
+ return ((...args: unknown[]) => {
263
+ return cache(JSON.stringify(args));
264
+ }) as Fnc;
265
+ }
266
+
267
+ export function cacheShallowConfigArgEqual<Fnc extends AnyFunction>(
268
+ fnc: Fnc,
269
+ limit = 10
270
+ ): Fnc & {
271
+ clear(...args: Args<Fnc>): void;
272
+ } {
273
+ let cache = cacheArrayEqual((kvpsFlat: unknown[]) => {
274
+ output.missCount++;
275
+ let arg: any;
276
+ if (kvpsFlat.length === 1) {
277
+ arg = kvpsFlat[0];
278
+ } else {
279
+ let kvps: [unknown, unknown][] = [];
280
+ for (let i = 0; i < kvpsFlat.length; i += 2) {
281
+ kvps.push([kvpsFlat[i], kvpsFlat[i + 1]]);
282
+ }
283
+ arg = Object.fromEntries(kvps);
284
+ }
285
+ return fnc(arg);
286
+ }, limit);
287
+ function getKVPs(configArg: object) {
288
+ if (!canHaveChildren(configArg) || Array.isArray(configArg)) {
289
+ return [configArg];
290
+ }
291
+ let keys = Object.keys(configArg);
292
+ keys.sort();
293
+ return keys.flatMap(key => [key, configArg[key]]);
294
+ }
295
+ let output = Object.assign(
296
+ ((configArg: object) => {
297
+ output.callCount++;
298
+ return cache(getKVPs(configArg));
299
+ }) as Fnc,
300
+ {
301
+ clear(configArg: object) {
302
+ cache.clear(getKVPs(configArg));
303
+ },
304
+ callCount: 0,
305
+ missCount: 0,
306
+ }
307
+ );
308
+ return output;
309
+ }
310
+
311
+
312
+ export function externalCache<Key, Value>(): {
313
+ get: (key: Key) => Value | undefined;
314
+ set: (key: Key, value: Value) => void;
315
+ } {
316
+ let values = new Map<Key, Value>();
317
+ return {
318
+ get: (key) => {
319
+ return values.get(key);
320
+ },
321
+ set: (key, value) => {
322
+ values.set(key, value);
323
+ },
324
+ };
319
325
  }