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/.eslintrc.js CHANGED
@@ -1,51 +1,51 @@
1
- module.exports = {
2
- "root": true,
3
- "env": {
4
- "browser": true,
5
- "es6": true,
6
- "node": true
7
- },
8
- "extends": [],
9
- "globals": {
10
- "Atomics": "readonly",
11
- "SharedArrayBuffer": "readonly"
12
- },
13
- "overrides": [
14
- {
15
- "files": [
16
- "**/*.ts",
17
- "**/*.tsx"
18
- ]
19
- }
20
- ],
21
- "parser": "@typescript-eslint/parser",
22
- "parserOptions": {
23
- "ecmaVersion": 2018,
24
- "sourceType": "module",
25
- "project": "./tsconfig.json",
26
- "tsconfigRootDir": __dirname,
27
- },
28
- "plugins": [
29
- "@typescript-eslint"
30
- ],
31
- "rules": {
32
- "@typescript-eslint/unbound-method": [
33
- "error",
34
- {
35
- "ignoreStatic": true
36
- }
37
- ],
38
- "@typescript-eslint/no-floating-promises": [
39
- "error"
40
- ],
41
- "quotes": [
42
- "error",
43
- "double",
44
- {
45
- "allowTemplateLiterals": true
46
- }
47
- ],
48
- "eqeqeq": "error",
49
- "semi": "error"
50
- }
1
+ module.exports = {
2
+ "root": true,
3
+ "env": {
4
+ "browser": true,
5
+ "es6": true,
6
+ "node": true
7
+ },
8
+ "extends": [],
9
+ "globals": {
10
+ "Atomics": "readonly",
11
+ "SharedArrayBuffer": "readonly"
12
+ },
13
+ "overrides": [
14
+ {
15
+ "files": [
16
+ "**/*.ts",
17
+ "**/*.tsx"
18
+ ]
19
+ }
20
+ ],
21
+ "parser": "@typescript-eslint/parser",
22
+ "parserOptions": {
23
+ "ecmaVersion": 2018,
24
+ "sourceType": "module",
25
+ "project": "./tsconfig.json",
26
+ "tsconfigRootDir": __dirname,
27
+ },
28
+ "plugins": [
29
+ "@typescript-eslint"
30
+ ],
31
+ "rules": {
32
+ "@typescript-eslint/unbound-method": [
33
+ "error",
34
+ {
35
+ "ignoreStatic": true
36
+ }
37
+ ],
38
+ "@typescript-eslint/no-floating-promises": [
39
+ "error"
40
+ ],
41
+ "quotes": [
42
+ "error",
43
+ "double",
44
+ {
45
+ "allowTemplateLiterals": true
46
+ }
47
+ ],
48
+ "eqeqeq": "error",
49
+ "semi": "error"
50
+ }
51
51
  };
package/SocketFunction.ts CHANGED
@@ -1,281 +1,281 @@
1
- /// <reference path="./require/RequireController.ts" />
2
-
3
- import { SocketExposedInterface, SocketFunctionHook, SocketFunctionClientHook, SocketExposedShape, SocketRegistered, CallerContext, FullCallType } from "./SocketFunctionTypes";
4
- import { exposeClass, registerClass, registerGlobalClientHook, registerGlobalHook, runClientHooks } from "./src/callManager";
5
- import { SocketServerConfig, startSocketServer } from "./src/webSocketServer";
6
- import { getCallFactory, getCreateCallFactory, getNodeId, getNodeIdLocation } from "./src/nodeCache";
7
- import { getCallProxy } from "./src/nodeProxy";
8
- import { Args, MaybePromise } from "./src/types";
9
- import { setDefaultHTTPCall } from "./src/callHTTPHandler";
10
- import debugbreak from "debugbreak";
11
- import { lazy } from "./src/caching";
12
- import { delay } from "./src/batching";
13
-
14
- module.allowclient = true;
15
-
16
- type ExtractShape<ClassType, Shape> = {
17
- [key in keyof Shape]: (
18
- key extends keyof ClassType
19
- ? ClassType[key] extends SocketExposedInterface[""]
20
- ? ClassType[key]
21
- : ClassType[key] extends Function ? "All exposed function must be async (or return a Promise)" : never
22
- : "Function is in shape, but not in class"
23
- );
24
- };
25
- // https://stackoverflow.com/a/69756175/1117119
26
- type PickByType<T, Value> = {
27
- [P in keyof T as T[P] extends Value | undefined ? P : never]: T[P]
28
- };
29
-
30
- export class SocketFunction {
31
- public static logMessages = false;
32
-
33
- public static MAX_MESSAGE_SIZE = 1024 * 1024 * 32;
34
- public static compression: undefined | {
35
- type: "gzip";
36
- };
37
-
38
- public static httpETagCache = false;
39
- public static silent = true;
40
-
41
- public static WIRE_WARN_TIME = 100;
42
-
43
- private static onMountCallbacks = new Map<string, (() => MaybePromise<void>)[]>();
44
- public static exposedClasses = new Set<string>();
45
-
46
- public static callerContext: CallerContext | undefined;
47
- public static getCaller(): CallerContext {
48
- const caller = SocketFunction.callerContext;
49
- if (!caller) throw new Error(`Tried to access caller when not in the synchronous phase of a function call`);
50
- return caller;
51
- }
52
-
53
- // NOTE: We use callbacks we don't run into issues with cyclic dependencies
54
- // (ex, using a hook in a controller where the hook also calls the controller).
55
- public static register<
56
- ClassInstance extends object,
57
- Shape extends SocketExposedShape<SocketExposedInterface>,
58
- >(
59
- classGuid: string,
60
- instance: ClassInstance,
61
- shapeFnc: () => Shape,
62
- defaultHooksFnc?: () => SocketExposedShape[""] & {
63
- onMount?: () => MaybePromise<void>;
64
- }
65
- ): SocketRegistered<ExtractShape<ClassInstance, Shape>> {
66
- let getDefaultHooks = defaultHooksFnc && lazy(defaultHooksFnc);
67
- const getShape = lazy(() => {
68
- let shape = shapeFnc();
69
- let defaultHooks = getDefaultHooks?.();
70
-
71
- for (let value of Object.values(shape)) {
72
- if (!value) continue;
73
- value.clientHooks = [...(defaultHooks?.clientHooks || []), ...(value.clientHooks || [])];
74
- value.hooks = [...(defaultHooks?.hooks || []), ...(value.hooks || [])];
75
- value.dataImmutable = defaultHooks?.dataImmutable ?? value.dataImmutable;
76
- }
77
- return shape as any as SocketExposedShape;
78
- });
79
-
80
- void Promise.resolve().then(() => {
81
- registerClass(classGuid, instance as SocketExposedInterface, getShape());
82
- });
83
-
84
- let nodeProxy = getCallProxy(classGuid, async (call) => {
85
- let nodeId = call.nodeId;
86
- let functionName = call.functionName;
87
- let time = Date.now();
88
- if (SocketFunction.logMessages) {
89
- console.log(`START\t\t\t${classGuid}.${functionName} at ${Date.now()}`);
90
- }
91
- try {
92
- let callFactory = await getCreateCallFactory(nodeId);
93
-
94
- let shapeObj = getShape()[functionName];
95
- if (!shapeObj) {
96
- throw new Error(`Function ${functionName} is not in shape`);
97
- }
98
-
99
- let hookResult = await runClientHooks(call, shapeObj as SocketExposedShape[""], callFactory.connectionId);
100
-
101
- if ("overrideResult" in hookResult) {
102
- return hookResult.overrideResult;
103
- }
104
-
105
- return await callFactory.performCall(call);
106
- } finally {
107
- time = Date.now() - time;
108
- if (SocketFunction.logMessages) {
109
- console.log(`FINISHED\t${time}ms\t${classGuid}.${functionName} at ${Date.now()}`);
110
- }
111
- }
112
- });
113
-
114
- let output: SocketRegistered = {
115
- nodes: nodeProxy,
116
- _classGuid: classGuid,
117
- };
118
-
119
- void Promise.resolve().then(() => {
120
- let onMount = getDefaultHooks?.().onMount;
121
- if (onMount) {
122
- let callbacks = SocketFunction.onMountCallbacks.get(classGuid);
123
- if (!callbacks) {
124
- callbacks = [];
125
- SocketFunction.onMountCallbacks.set(classGuid, callbacks);
126
- }
127
- callbacks.push(onMount);
128
- }
129
- });
130
-
131
- return output as any;
132
- }
133
-
134
- public static onNextDisconnect(nodeId: string, callback: () => void) {
135
- (async () => {
136
- let factory = await getCallFactory(nodeId);
137
- if (!factory) {
138
- callback();
139
- return;
140
- }
141
-
142
- factory.onNextDisconnect(callback);
143
- })().catch(() => {
144
- callback();
145
- });
146
- }
147
- public static getLastDisconnectTime(nodeId: string): number | undefined {
148
- let factory = getCallFactory(nodeId);
149
- if (!factory) {
150
- return undefined;
151
- }
152
- if (factory instanceof Promise) {
153
- return undefined;
154
- }
155
- return factory.lastClosed;
156
- }
157
- public static isNodeConnected(nodeId: string): boolean {
158
- let factory = getCallFactory(nodeId);
159
- if (!factory) {
160
- return false;
161
- }
162
- if (factory instanceof Promise) {
163
- return false;
164
- }
165
- return !!factory.isConnected;
166
- }
167
-
168
- /** NOTE: Only works if the nodeIs used is from SocketFunction.connect (we can't convert arbitrary nodeIds into urls,
169
- * as we have no way of knowing how to contain a nodeId).
170
- * */
171
- public static getHTTPCallLink(call: FullCallType): string {
172
- let location = getNodeIdLocation(call.nodeId);
173
- if (!location) {
174
- throw new Error(`Cannot find call location for nodeId, and so do not know where call location is. NodeId ${call.nodeId}`);
175
- }
176
- let url = new URL(`https://${location.address}:${location.port}`);
177
- url.searchParams.set("classGuid", call.classGuid);
178
- url.searchParams.set("functionName", call.functionName);
179
- url.searchParams.set("args", JSON.stringify(call.args));
180
- return url.toString();
181
- }
182
-
183
- /** Expose should be called before your mounting occurs. It mostly just exists to ensure you include the class type,
184
- * so the class type's module construction runs, which should trigger register. Otherwise you would have
185
- * to add additional imports to ensure the register call runs.
186
- */
187
- public static expose(socketRegistered: SocketRegistered) {
188
- exposeClass(socketRegistered);
189
- SocketFunction.exposedClasses.add(socketRegistered._classGuid);
190
-
191
- if (this.hasMounted) {
192
- let mountCallbacks = SocketFunction.onMountCallbacks.get(socketRegistered._classGuid);
193
- for (let onMount of mountCallbacks || []) {
194
- Promise.resolve(onMount()).catch(e => {
195
- console.error("Error in onMount callback exposed after mount", e);
196
- });
197
- }
198
- }
199
- }
200
-
201
- public static mountedNodeId: string = "";
202
- public static mountedIP: string = "";
203
- private static hasMounted = false;
204
- private static onMountCallback: () => void = () => { };
205
- public static mountPromise: Promise<void> = new Promise(r => this.onMountCallback = r);
206
- public static async mount(config: SocketServerConfig) {
207
- if (this.mountedNodeId) {
208
- throw new Error("SocketFunction already mounted, mounting twice in one thread is not allowed.");
209
- }
210
-
211
- this.mountedIP = config.public ? "0.0.0.0" : "127.0.0.1";
212
- if (config.ip) {
213
- this.mountedIP = config.ip;
214
- }
215
-
216
- // Wait for any additionals functions to expose themselves
217
- await delay("immediate");
218
-
219
- this.mountedNodeId = await startSocketServer(config);
220
- this.hasMounted = true;
221
- for (let classGuid of SocketFunction.exposedClasses) {
222
- let callbacks = SocketFunction.onMountCallbacks.get(classGuid);
223
- if (!callbacks) continue;
224
- for (let callback of callbacks) {
225
- await callback();
226
- }
227
- }
228
- this.onMountCallback();
229
- return this.mountedNodeId;
230
- }
231
-
232
- /** Sets the default call when an http request is made, but no classGuid is set. */
233
- public static setDefaultHTTPCall<
234
- Registered extends SocketRegistered,
235
- FunctionName extends keyof Registered["nodes"][""] & string,
236
- >(
237
- registered: Registered,
238
- functionName: FunctionName,
239
- ...args: Args<Registered["nodes"][""][FunctionName]>
240
- ) {
241
- setDefaultHTTPCall({
242
- classGuid: registered._classGuid,
243
- functionName,
244
- args,
245
- });
246
- }
247
-
248
- public static connect(location: { address: string, port: number }): string {
249
- return getNodeId(location.address, location.port);
250
- }
251
-
252
- public static locationNode() {
253
- return SocketFunction.connect({ address: location.hostname, port: +location.port });
254
- }
255
-
256
- public static addGlobalHook(hook: SocketFunctionHook<SocketExposedInterface>) {
257
- registerGlobalHook(hook as SocketFunctionHook);
258
- }
259
- public static addGlobalClientHook(hook: SocketFunctionClientHook<SocketExposedInterface>) {
260
- registerGlobalClientHook(hook as SocketFunctionClientHook);
261
- }
262
- }
263
-
264
-
265
- let socketContextSeqNum = 1;
266
-
267
- export function _setSocketContext<T>(
268
- caller: CallerContext,
269
- code: () => T,
270
- ) {
271
- socketContextSeqNum++;
272
- let seqNum = socketContextSeqNum;
273
- SocketFunction.callerContext = caller;
274
- try {
275
- return code();
276
- } finally {
277
- if (seqNum === socketContextSeqNum) {
278
- SocketFunction.callerContext = undefined;
279
- }
280
- }
1
+ /// <reference path="./require/RequireController.ts" />
2
+
3
+ import { SocketExposedInterface, SocketFunctionHook, SocketFunctionClientHook, SocketExposedShape, SocketRegistered, CallerContext, FullCallType } from "./SocketFunctionTypes";
4
+ import { exposeClass, registerClass, registerGlobalClientHook, registerGlobalHook, runClientHooks } from "./src/callManager";
5
+ import { SocketServerConfig, startSocketServer } from "./src/webSocketServer";
6
+ import { getCallFactory, getCreateCallFactory, getNodeId, getNodeIdLocation } from "./src/nodeCache";
7
+ import { getCallProxy } from "./src/nodeProxy";
8
+ import { Args, MaybePromise } from "./src/types";
9
+ import { setDefaultHTTPCall } from "./src/callHTTPHandler";
10
+ import debugbreak from "debugbreak";
11
+ import { lazy } from "./src/caching";
12
+ import { delay } from "./src/batching";
13
+
14
+ module.allowclient = true;
15
+
16
+ type ExtractShape<ClassType, Shape> = {
17
+ [key in keyof Shape]: (
18
+ key extends keyof ClassType
19
+ ? ClassType[key] extends SocketExposedInterface[""]
20
+ ? ClassType[key]
21
+ : ClassType[key] extends Function ? "All exposed function must be async (or return a Promise)" : never
22
+ : "Function is in shape, but not in class"
23
+ );
24
+ };
25
+ // https://stackoverflow.com/a/69756175/1117119
26
+ type PickByType<T, Value> = {
27
+ [P in keyof T as T[P] extends Value | undefined ? P : never]: T[P]
28
+ };
29
+
30
+ export class SocketFunction {
31
+ public static logMessages = false;
32
+
33
+ public static MAX_MESSAGE_SIZE = 1024 * 1024 * 32;
34
+ public static compression: undefined | {
35
+ type: "gzip";
36
+ };
37
+
38
+ public static httpETagCache = false;
39
+ public static silent = true;
40
+
41
+ public static WIRE_WARN_TIME = 100;
42
+
43
+ private static onMountCallbacks = new Map<string, (() => MaybePromise<void>)[]>();
44
+ public static exposedClasses = new Set<string>();
45
+
46
+ public static callerContext: CallerContext | undefined;
47
+ public static getCaller(): CallerContext {
48
+ const caller = SocketFunction.callerContext;
49
+ if (!caller) throw new Error(`Tried to access caller when not in the synchronous phase of a function call`);
50
+ return caller;
51
+ }
52
+
53
+ // NOTE: We use callbacks we don't run into issues with cyclic dependencies
54
+ // (ex, using a hook in a controller where the hook also calls the controller).
55
+ public static register<
56
+ ClassInstance extends object,
57
+ Shape extends SocketExposedShape<SocketExposedInterface>,
58
+ >(
59
+ classGuid: string,
60
+ instance: ClassInstance,
61
+ shapeFnc: () => Shape,
62
+ defaultHooksFnc?: () => SocketExposedShape[""] & {
63
+ onMount?: () => MaybePromise<void>;
64
+ }
65
+ ): SocketRegistered<ExtractShape<ClassInstance, Shape>> {
66
+ let getDefaultHooks = defaultHooksFnc && lazy(defaultHooksFnc);
67
+ const getShape = lazy(() => {
68
+ let shape = shapeFnc();
69
+ let defaultHooks = getDefaultHooks?.();
70
+
71
+ for (let value of Object.values(shape)) {
72
+ if (!value) continue;
73
+ value.clientHooks = [...(defaultHooks?.clientHooks || []), ...(value.clientHooks || [])];
74
+ value.hooks = [...(defaultHooks?.hooks || []), ...(value.hooks || [])];
75
+ value.dataImmutable = defaultHooks?.dataImmutable ?? value.dataImmutable;
76
+ }
77
+ return shape as any as SocketExposedShape;
78
+ });
79
+
80
+ void Promise.resolve().then(() => {
81
+ registerClass(classGuid, instance as SocketExposedInterface, getShape());
82
+ });
83
+
84
+ let nodeProxy = getCallProxy(classGuid, async (call) => {
85
+ let nodeId = call.nodeId;
86
+ let functionName = call.functionName;
87
+ let time = Date.now();
88
+ if (SocketFunction.logMessages) {
89
+ console.log(`START\t\t\t${classGuid}.${functionName} at ${Date.now()}`);
90
+ }
91
+ try {
92
+ let callFactory = await getCreateCallFactory(nodeId);
93
+
94
+ let shapeObj = getShape()[functionName];
95
+ if (!shapeObj) {
96
+ throw new Error(`Function ${functionName} is not in shape`);
97
+ }
98
+
99
+ let hookResult = await runClientHooks(call, shapeObj as SocketExposedShape[""], callFactory.connectionId);
100
+
101
+ if ("overrideResult" in hookResult) {
102
+ return hookResult.overrideResult;
103
+ }
104
+
105
+ return await callFactory.performCall(call);
106
+ } finally {
107
+ time = Date.now() - time;
108
+ if (SocketFunction.logMessages) {
109
+ console.log(`FINISHED\t${time}ms\t${classGuid}.${functionName} at ${Date.now()}`);
110
+ }
111
+ }
112
+ });
113
+
114
+ let output: SocketRegistered = {
115
+ nodes: nodeProxy,
116
+ _classGuid: classGuid,
117
+ };
118
+
119
+ void Promise.resolve().then(() => {
120
+ let onMount = getDefaultHooks?.().onMount;
121
+ if (onMount) {
122
+ let callbacks = SocketFunction.onMountCallbacks.get(classGuid);
123
+ if (!callbacks) {
124
+ callbacks = [];
125
+ SocketFunction.onMountCallbacks.set(classGuid, callbacks);
126
+ }
127
+ callbacks.push(onMount);
128
+ }
129
+ });
130
+
131
+ return output as any;
132
+ }
133
+
134
+ public static onNextDisconnect(nodeId: string, callback: () => void) {
135
+ (async () => {
136
+ let factory = await getCallFactory(nodeId);
137
+ if (!factory) {
138
+ callback();
139
+ return;
140
+ }
141
+
142
+ factory.onNextDisconnect(callback);
143
+ })().catch(() => {
144
+ callback();
145
+ });
146
+ }
147
+ public static getLastDisconnectTime(nodeId: string): number | undefined {
148
+ let factory = getCallFactory(nodeId);
149
+ if (!factory) {
150
+ return undefined;
151
+ }
152
+ if (factory instanceof Promise) {
153
+ return undefined;
154
+ }
155
+ return factory.lastClosed;
156
+ }
157
+ public static isNodeConnected(nodeId: string): boolean {
158
+ let factory = getCallFactory(nodeId);
159
+ if (!factory) {
160
+ return false;
161
+ }
162
+ if (factory instanceof Promise) {
163
+ return false;
164
+ }
165
+ return !!factory.isConnected;
166
+ }
167
+
168
+ /** NOTE: Only works if the nodeIs used is from SocketFunction.connect (we can't convert arbitrary nodeIds into urls,
169
+ * as we have no way of knowing how to contain a nodeId).
170
+ * */
171
+ public static getHTTPCallLink(call: FullCallType): string {
172
+ let location = getNodeIdLocation(call.nodeId);
173
+ if (!location) {
174
+ throw new Error(`Cannot find call location for nodeId, and so do not know where call location is. NodeId ${call.nodeId}`);
175
+ }
176
+ let url = new URL(`https://${location.address}:${location.port}`);
177
+ url.searchParams.set("classGuid", call.classGuid);
178
+ url.searchParams.set("functionName", call.functionName);
179
+ url.searchParams.set("args", JSON.stringify(call.args));
180
+ return url.toString();
181
+ }
182
+
183
+ /** Expose should be called before your mounting occurs. It mostly just exists to ensure you include the class type,
184
+ * so the class type's module construction runs, which should trigger register. Otherwise you would have
185
+ * to add additional imports to ensure the register call runs.
186
+ */
187
+ public static expose(socketRegistered: SocketRegistered) {
188
+ exposeClass(socketRegistered);
189
+ SocketFunction.exposedClasses.add(socketRegistered._classGuid);
190
+
191
+ if (this.hasMounted) {
192
+ let mountCallbacks = SocketFunction.onMountCallbacks.get(socketRegistered._classGuid);
193
+ for (let onMount of mountCallbacks || []) {
194
+ Promise.resolve(onMount()).catch(e => {
195
+ console.error("Error in onMount callback exposed after mount", e);
196
+ });
197
+ }
198
+ }
199
+ }
200
+
201
+ public static mountedNodeId: string = "";
202
+ public static mountedIP: string = "";
203
+ private static hasMounted = false;
204
+ private static onMountCallback: () => void = () => { };
205
+ public static mountPromise: Promise<void> = new Promise(r => this.onMountCallback = r);
206
+ public static async mount(config: SocketServerConfig) {
207
+ if (this.mountedNodeId) {
208
+ throw new Error("SocketFunction already mounted, mounting twice in one thread is not allowed.");
209
+ }
210
+
211
+ this.mountedIP = config.public ? "0.0.0.0" : "127.0.0.1";
212
+ if (config.ip) {
213
+ this.mountedIP = config.ip;
214
+ }
215
+
216
+ // Wait for any additionals functions to expose themselves
217
+ await delay("immediate");
218
+
219
+ this.mountedNodeId = await startSocketServer(config);
220
+ this.hasMounted = true;
221
+ for (let classGuid of SocketFunction.exposedClasses) {
222
+ let callbacks = SocketFunction.onMountCallbacks.get(classGuid);
223
+ if (!callbacks) continue;
224
+ for (let callback of callbacks) {
225
+ await callback();
226
+ }
227
+ }
228
+ this.onMountCallback();
229
+ return this.mountedNodeId;
230
+ }
231
+
232
+ /** Sets the default call when an http request is made, but no classGuid is set. */
233
+ public static setDefaultHTTPCall<
234
+ Registered extends SocketRegistered,
235
+ FunctionName extends keyof Registered["nodes"][""] & string,
236
+ >(
237
+ registered: Registered,
238
+ functionName: FunctionName,
239
+ ...args: Args<Registered["nodes"][""][FunctionName]>
240
+ ) {
241
+ setDefaultHTTPCall({
242
+ classGuid: registered._classGuid,
243
+ functionName,
244
+ args,
245
+ });
246
+ }
247
+
248
+ public static connect(location: { address: string, port: number }): string {
249
+ return getNodeId(location.address, location.port);
250
+ }
251
+
252
+ public static locationNode() {
253
+ return SocketFunction.connect({ address: location.hostname, port: +location.port });
254
+ }
255
+
256
+ public static addGlobalHook(hook: SocketFunctionHook<SocketExposedInterface>) {
257
+ registerGlobalHook(hook as SocketFunctionHook);
258
+ }
259
+ public static addGlobalClientHook(hook: SocketFunctionClientHook<SocketExposedInterface>) {
260
+ registerGlobalClientHook(hook as SocketFunctionClientHook);
261
+ }
262
+ }
263
+
264
+
265
+ let socketContextSeqNum = 1;
266
+
267
+ export function _setSocketContext<T>(
268
+ caller: CallerContext,
269
+ code: () => T,
270
+ ) {
271
+ socketContextSeqNum++;
272
+ let seqNum = socketContextSeqNum;
273
+ SocketFunction.callerContext = caller;
274
+ try {
275
+ return code();
276
+ } finally {
277
+ if (seqNum === socketContextSeqNum) {
278
+ SocketFunction.callerContext = undefined;
279
+ }
280
+ }
281
281
  }