socket-function 0.15.0 → 0.17.0
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/SocketFunction.ts +74 -45
- package/SocketFunctionTypes.ts +13 -3
- package/package.json +1 -1
- package/src/batching.ts +6 -1
package/SocketFunction.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference path="./require/RequireController.ts" />
|
|
2
2
|
|
|
3
|
-
import { SocketExposedInterface, SocketFunctionHook, SocketFunctionClientHook, SocketExposedShape, SocketRegistered, CallerContext, FullCallType, CallType } from "./SocketFunctionTypes";
|
|
3
|
+
import { SocketExposedInterface, SocketFunctionHook, SocketFunctionClientHook, SocketExposedShape, SocketRegistered, CallerContext, FullCallType, CallType, FncType, SocketRegisterType } from "./SocketFunctionTypes";
|
|
4
4
|
import { exposeClass, registerClass, registerGlobalClientHook, registerGlobalHook, runClientHooks } from "./src/callManager";
|
|
5
5
|
import { SocketServerConfig, startSocketServer } from "./src/webSocketServer";
|
|
6
6
|
import { getCallFactory, getCreateCallFactory, getNodeId, getNodeIdLocation } from "./src/nodeCache";
|
|
@@ -84,7 +84,6 @@ export class SocketFunction {
|
|
|
84
84
|
return caller;
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
private static getShapeHotReloadable = new Map<string, () => SocketExposedShape<SocketExposedInterface>>();
|
|
88
87
|
// NOTE: We use callbacks we don't run into issues with cyclic dependencies
|
|
89
88
|
// (ex, using a hook in a controller where the hook also calls the controller).
|
|
90
89
|
public static register<
|
|
@@ -113,6 +112,18 @@ export class SocketFunction {
|
|
|
113
112
|
noFunctionMeasure?: boolean;
|
|
114
113
|
}
|
|
115
114
|
): SocketRegistered<ExtractShape<ClassInstance, Shape>> & Statics {
|
|
115
|
+
void Promise.resolve().then(() => {
|
|
116
|
+
let onMount = getDefaultHooks?.().onMount;
|
|
117
|
+
if (onMount) {
|
|
118
|
+
let callbacks = SocketFunction.onMountCallbacks.get(classGuid);
|
|
119
|
+
if (!callbacks) {
|
|
120
|
+
callbacks = [];
|
|
121
|
+
SocketFunction.onMountCallbacks.set(classGuid, callbacks);
|
|
122
|
+
}
|
|
123
|
+
callbacks.push(onMount);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
|
|
116
127
|
let getDefaultHooks = defaultHooksFnc && lazy(defaultHooksFnc);
|
|
117
128
|
const getShape = lazy(() => {
|
|
118
129
|
let shape = shapeFnc() as SocketExposedShape;
|
|
@@ -142,58 +153,76 @@ export class SocketFunction {
|
|
|
142
153
|
});
|
|
143
154
|
});
|
|
144
155
|
|
|
145
|
-
let
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
if (SocketFunction.logMessages) {
|
|
150
|
-
console.log(`START\t\t\t${classGuid}.${functionName} at ${Date.now()}`);
|
|
151
|
-
}
|
|
152
|
-
try {
|
|
153
|
-
let callFactory = await getCreateCallFactory(nodeId);
|
|
156
|
+
let socketCaller = SocketFunction.rehydrateSocketCaller({
|
|
157
|
+
_classGuid: classGuid,
|
|
158
|
+
_internalType: null as any,
|
|
159
|
+
}, getShape);
|
|
154
160
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
161
|
+
if (!config?.noAutoExpose) {
|
|
162
|
+
this.expose(socketCaller);
|
|
163
|
+
}
|
|
164
|
+
return Object.assign(socketCaller, config?.statics);
|
|
165
|
+
}
|
|
159
166
|
|
|
160
|
-
|
|
167
|
+
private static socketCache = new Map<string, SocketRegistered>();
|
|
168
|
+
public static rehydrateSocketCaller<Controller>(
|
|
169
|
+
socketRegistered: SocketRegisterType<Controller>,
|
|
170
|
+
// Shape is required for client hooks.
|
|
171
|
+
shapeFnc?: () => SocketExposedShape,
|
|
172
|
+
): SocketRegistered<Controller> {
|
|
173
|
+
let cached = this.socketCache.get(socketRegistered._classGuid);
|
|
174
|
+
if (!cached) {
|
|
175
|
+
let getShape = lazy(() => shapeFnc?.());
|
|
176
|
+
let classGuid = socketRegistered._classGuid;
|
|
177
|
+
let nodeProxy = getCallProxy(classGuid, async (call) => {
|
|
178
|
+
return await SocketFunction.callFromGuid(call, classGuid, getShape());
|
|
179
|
+
});
|
|
161
180
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
181
|
+
cached = {
|
|
182
|
+
nodes: nodeProxy,
|
|
183
|
+
_classGuid: classGuid,
|
|
184
|
+
_internalType: null as any,
|
|
185
|
+
};
|
|
165
186
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
console.log(`FINISHED\t${time}ms\t${classGuid}.${functionName} at ${Date.now()}`);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
});
|
|
187
|
+
this.socketCache.set(classGuid, cached);
|
|
188
|
+
}
|
|
189
|
+
return cached;
|
|
190
|
+
}
|
|
174
191
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
192
|
+
private static async callFromGuid<FncT extends FncType>(
|
|
193
|
+
call: FullCallType<FncT>,
|
|
194
|
+
classGuid: string,
|
|
195
|
+
shape?: SocketExposedShape,
|
|
196
|
+
): Promise<ReturnType<FncType>> {
|
|
197
|
+
let nodeId = call.nodeId;
|
|
198
|
+
let functionName = call.functionName;
|
|
199
|
+
let time = Date.now();
|
|
200
|
+
if (SocketFunction.logMessages) {
|
|
201
|
+
console.log(`START\t\t\t${classGuid}.${functionName} at ${Date.now()}`);
|
|
202
|
+
}
|
|
203
|
+
try {
|
|
204
|
+
let callFactory = await getCreateCallFactory(nodeId);
|
|
179
205
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
if
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
206
|
+
let shapeObj = shape?.[functionName];
|
|
207
|
+
// NOTE: Actually... this just means the client doesn't have a definition for it. The server
|
|
208
|
+
// might, so call it, and let them throw if it is unrecognized.
|
|
209
|
+
// if (!shapeObj) {
|
|
210
|
+
// throw new Error(`Function ${functionName} is not in shape`);
|
|
211
|
+
// }
|
|
212
|
+
|
|
213
|
+
let hookResult = await runClientHooks(call, shapeObj as Exclude<SocketExposedShape[""], undefined>, callFactory.connectionId);
|
|
214
|
+
|
|
215
|
+
if ("overrideResult" in hookResult) {
|
|
216
|
+
return hookResult.overrideResult;
|
|
189
217
|
}
|
|
190
|
-
});
|
|
191
218
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
219
|
+
return await callFactory.performCall(call);
|
|
220
|
+
} finally {
|
|
221
|
+
time = Date.now() - time;
|
|
222
|
+
if (SocketFunction.logMessages) {
|
|
223
|
+
console.log(`FINISHED\t${time}ms\t${classGuid}.${functionName} at ${Date.now()}`);
|
|
224
|
+
}
|
|
195
225
|
}
|
|
196
|
-
return Object.assign(result, config?.statics);
|
|
197
226
|
}
|
|
198
227
|
|
|
199
228
|
public static onNextDisconnect(nodeId: string, callback: () => void) {
|
package/SocketFunctionTypes.ts
CHANGED
|
@@ -42,12 +42,13 @@ export type SocketExposedShape<ExposedType extends SocketExposedInterface = Sock
|
|
|
42
42
|
};
|
|
43
43
|
};
|
|
44
44
|
|
|
45
|
-
export
|
|
45
|
+
export type FncType = (...args: any[]) => Promise<unknown>;
|
|
46
|
+
export interface CallType<FncT extends FncType = FncType, FncName extends string = string> {
|
|
46
47
|
classGuid: string;
|
|
47
|
-
functionName:
|
|
48
|
+
functionName: FncName;
|
|
48
49
|
args: unknown[];
|
|
49
50
|
}
|
|
50
|
-
export interface FullCallType extends CallType {
|
|
51
|
+
export interface FullCallType<FncT extends FncType = FncType, FncName extends string = string> extends CallType<FncT, FncName> {
|
|
51
52
|
nodeId: string;
|
|
52
53
|
}
|
|
53
54
|
|
|
@@ -72,6 +73,11 @@ export interface SocketFunctionClientHook<ExposedType extends SocketExposedInter
|
|
|
72
73
|
(config: ClientHookContext<ExposedType>): MaybePromise<void>;
|
|
73
74
|
}
|
|
74
75
|
|
|
76
|
+
export interface SocketRegisterType<ExposedType = any> {
|
|
77
|
+
_classGuid: string;
|
|
78
|
+
_internalType: ExposedType;
|
|
79
|
+
}
|
|
80
|
+
|
|
75
81
|
export interface SocketRegistered<ExposedType = any> {
|
|
76
82
|
nodes: {
|
|
77
83
|
// NOTE: Don't pass around nodeId to other nodes, instead pass around NetworkLocation (which they
|
|
@@ -83,7 +89,11 @@ export interface SocketRegistered<ExposedType = any> {
|
|
|
83
89
|
};
|
|
84
90
|
};
|
|
85
91
|
_classGuid: string;
|
|
92
|
+
_internalType: ExposedType;
|
|
86
93
|
}
|
|
94
|
+
export type ControllerPick<T extends SocketRegistered, K extends keyof T["_internalType"]> = (
|
|
95
|
+
SocketRegistered<Pick<T["_internalType"], K>>
|
|
96
|
+
);
|
|
87
97
|
export type CallerContext = Readonly<CallerContextBase>;
|
|
88
98
|
export type CallerContextBase = {
|
|
89
99
|
// IMPORTANT! Do not pass nodeId to other nodes with the intention of having
|
package/package.json
CHANGED
package/src/batching.ts
CHANGED
|
@@ -149,7 +149,12 @@ export function batchFunction<Arg, Result = void>(
|
|
|
149
149
|
let curPrevPromise = prevPromise;
|
|
150
150
|
let args: Arg[] = [arg];
|
|
151
151
|
let promise = Promise.resolve().then(async () => {
|
|
152
|
-
|
|
152
|
+
// Ignore the error. New callers don't care about errors in previous calls,
|
|
153
|
+
// as they are unrelated to the current call, and just break valid calls
|
|
154
|
+
// due to invalid calls.
|
|
155
|
+
try {
|
|
156
|
+
await curPrevPromise;
|
|
157
|
+
} catch { }
|
|
153
158
|
await delay(curDelay, "immediateShortDelays");
|
|
154
159
|
// Reset batching, as we once we start the function we can't accept args. `prevPromise` will block
|
|
155
160
|
// the next batch from starting before we finish.
|