socket-function 0.8.12 → 0.8.15

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 CHANGED
@@ -1,7 +1,7 @@
1
- import { SocketExposedInterface, CallContextType, SocketFunctionHook, SocketFunctionClientHook, SocketExposedShape, SocketRegistered, NetworkLocation, CallerContext, SocketExposedInterfaceClass, CallType } from "./SocketFunctionTypes";
1
+ import { SocketExposedInterface, CallContextType, SocketFunctionHook, SocketFunctionClientHook, SocketExposedShape, SocketRegistered, NetworkLocation, CallerContext, SocketExposedInterfaceClass, CallType, FullCallType } from "./SocketFunctionTypes";
2
2
  import { exposeClass, registerClass, registerGlobalClientHook, registerGlobalHook, runClientHooks } from "./src/callManager";
3
3
  import { SocketServerConfig, startSocketServer } from "./src/webSocketServer";
4
- import { getCallFactoryNodeId, getCreateCallFactoryLocation, getNetworkLocationHash } from "./src/nodeCache";
4
+ import { getCallFactoryFromNodeId, getCreateCallFactoryLocation, getLocationFromNodeId, getNetworkLocationHash } from "./src/nodeCache";
5
5
  import { getCallProxy } from "./src/nodeProxy";
6
6
  import { Args } from "./src/types";
7
7
  import { setDefaultHTTPCall } from "./src/callHTTPHandler";
@@ -53,13 +53,15 @@ export class SocketFunction {
53
53
 
54
54
  registerClass(classGuid, instance as SocketExposedInterface, shape as any as SocketExposedShape);
55
55
 
56
- let nodeProxy = getCallProxy(classGuid, async (nodeId, functionName, args) => {
56
+ let nodeProxy = getCallProxy(classGuid, async (call) => {
57
+ let nodeId = call.nodeId;
58
+ let functionName = call.functionName;
57
59
  let time = Date.now();
58
60
  if (SocketFunction.logMessages) {
59
61
  console.log(`START\t\t\t${classGuid}.${functionName}`);
60
62
  }
61
63
  try {
62
- let callFactory = await getCallFactoryNodeId(nodeId);
64
+ let callFactory = await getCallFactoryFromNodeId(nodeId);
63
65
  if (!callFactory) {
64
66
  throw new Error(`Cannot reach node ${nodeId}. It might have been incorrect provided to us via another node, which should have provided us a NetworkLocation instead.`);
65
67
  }
@@ -69,12 +71,6 @@ export class SocketFunction {
69
71
  throw new Error(`Function ${functionName} is not in shape`);
70
72
  }
71
73
 
72
- let call: CallType = {
73
- classGuid,
74
- args,
75
- functionName,
76
- };
77
-
78
74
  let hookResult = await runClientHooks(call, shapeObj as SocketExposedShape[""]);
79
75
 
80
76
  if ("overrideResult" in hookResult) {
@@ -99,6 +95,20 @@ export class SocketFunction {
99
95
  return output as any;
100
96
  }
101
97
 
98
+ /** NOTE: Only works if the call has been loaded from a url (we can't convert arbitrary nodeIds into urls,
99
+ * as we have no way of knowing how to contain a nodeId). */
100
+ public static getHTTPCallLink(call: FullCallType): string {
101
+ let location = getLocationFromNodeId(call.nodeId);
102
+ if (!location) {
103
+ throw new Error(`Cannot find call location for nodeId, and so do not know where call location is. NodeId ${call.nodeId}`);
104
+ }
105
+ let url = new URL(`https://${location.address}:${location.listeningPorts[0]}`);
106
+ url.searchParams.set("classGuid", call.classGuid);
107
+ url.searchParams.set("functionName", call.functionName);
108
+ url.searchParams.set("args", JSON.stringify(call.args));
109
+ return url.toString();
110
+ }
111
+
102
112
  /** Expose should be called before your mounting occurs. It mostly just exists to ensure you include the class type,
103
113
  * so the class type's module construction runs, which should trigger register. Otherwise you would have
104
114
  * to add additional imports to ensure the register call runs.
@@ -2,12 +2,20 @@ module.allowclient = true;
2
2
 
3
3
  import debugbreak from "debugbreak";
4
4
  import * as tls from "tls";
5
+ import { getCallObj } from "./src/nodeProxy";
6
+ import { Args } from "./src/types";
5
7
 
6
8
  export const socket = Symbol("socket");
7
9
 
8
10
  export type SocketExposedInterface = {
9
11
  [functionName: string]: (...args: any[]) => Promise<unknown>;
10
12
  };
13
+ export type SocketInternalInterface = {
14
+ [functionName: string]: {
15
+ [getCallObj]: (...args: any[]) => FullCallType;
16
+ (...args: any[]): Promise<unknown>;
17
+ }
18
+ }
11
19
  export type SocketExposedInterfaceClass = {
12
20
  //new(): SocketExposedInterface;
13
21
  new(): unknown;
@@ -28,6 +36,9 @@ export interface CallType {
28
36
  // To set a timeout on returns, you can set it in the server hook.
29
37
  reconnectTimeout?: number;
30
38
  }
39
+ export interface FullCallType extends CallType {
40
+ nodeId: string;
41
+ }
31
42
 
32
43
  export interface SocketFunctionHook<ExposedType extends SocketExposedInterface = SocketExposedInterface, CallContext extends CallContextType = CallContextType> {
33
44
  (config: HookContext<ExposedType, CallContext>): Promise<void>;
@@ -56,7 +67,11 @@ export interface SocketRegistered<ExposedType extends SocketExposedInterface = S
56
67
  nodes: {
57
68
  // NOTE: Don't pass around nodeId to other nodes, instead pass around NetworkLocation (which they
58
69
  // then turn into a nodeId, which they can then check permissions on themself).
59
- [nodeId: string]: ExposedType;
70
+ [nodeId: string]: {
71
+ [functionName in keyof ExposedType]: ExposedType[functionName] & {
72
+ [getCallObj]: (...args: Args<ExposedType[functionName]>) => FullCallType;
73
+ }
74
+ };
60
75
  };
61
76
  context: {
62
77
  // If undefined we are not synchronously in a call
@@ -59,6 +59,7 @@ export function asyncObservable<Output, Key>(maxCount: number, getValue: (key: K
59
59
  invalidateSeqNum.seqNum++;
60
60
  };
61
61
  function get(key: Key) {
62
+ invalidateSeqNum.seqNum;
62
63
  let hash = JSON.stringify(key);
63
64
  let value = values.get(hash);
64
65
  if (value) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "socket-function",
3
- "version": "0.8.12",
3
+ "version": "0.8.15",
4
4
  "main": "index.js",
5
5
  "license": "MIT",
6
6
  "dependencies": {
package/src/nodeCache.ts CHANGED
@@ -15,6 +15,8 @@ import { MaybePromise } from "./types";
15
15
  // nodeId =>
16
16
  const nodeCache = new Map<string, {
17
17
  callFactory: MaybePromise<CallFactory>;
18
+ // Just used for getCallFactoryFromNodeId
19
+ location: NetworkLocation | undefined;
18
20
  }>();
19
21
  const locationLookup = new Map<string, MaybePromise<string>>();
20
22
 
@@ -47,6 +49,7 @@ export function registerNodeClient(callFactory: CallFactory) {
47
49
  // TODO: Maybe even preserve the address in some cases, such as if it was a domain, and is now an ip?
48
50
  nodeCache.set(nodeId, {
49
51
  callFactory,
52
+ location: undefined,
50
53
  });
51
54
  }
52
55
 
@@ -64,6 +67,7 @@ export function getCreateCallFactoryLocation(location: NetworkLocation, tempNode
64
67
  if (tempNodeId !== undefined) {
65
68
  nodeCache.set(tempNodeId, {
66
69
  callFactory: callFactoryPromise,
70
+ location,
67
71
  });
68
72
  }
69
73
 
@@ -80,6 +84,7 @@ export function getCreateCallFactoryLocation(location: NetworkLocation, tempNode
80
84
  }
81
85
  nodeCache.set(nodeId, {
82
86
  callFactory,
87
+ location,
83
88
  });
84
89
  return nodeId;
85
90
  });
@@ -87,6 +92,10 @@ export function getCreateCallFactoryLocation(location: NetworkLocation, tempNode
87
92
 
88
93
 
89
94
  // TODO: Give a special error if the nodeId has been seen, but is only one-way (from HTTP requests).
90
- export async function getCallFactoryNodeId(nodeId: string): Promise<CallFactory | undefined> {
95
+ export async function getCallFactoryFromNodeId(nodeId: string): Promise<CallFactory | undefined> {
91
96
  return await nodeCache.get(nodeId)?.callFactory;
97
+ }
98
+ // NOTE: Only works if the nodeId has been loaded with getCreateCallFactoryLocation
99
+ export function getLocationFromNodeId(nodeId: string): NetworkLocation | undefined {
100
+ return nodeCache.get(nodeId)?.location;
92
101
  }
package/src/nodeProxy.ts CHANGED
@@ -1,29 +1,52 @@
1
1
  import { lazy } from "./caching";
2
- import { SocketExposedInterface } from "../SocketFunctionTypes";
2
+ import { FullCallType, SocketExposedInterface, SocketInternalInterface } from "../SocketFunctionTypes";
3
3
 
4
4
  type CallProxyType = {
5
- [controllerName: string]: SocketExposedInterface;
5
+ [nodeId: string]: SocketInternalInterface;
6
6
  };
7
7
 
8
+ export const getCallObj = Symbol.for("getCallObj");
9
+
8
10
  let proxyCache = new Map<string, CallProxyType>();
9
- export function getCallProxy(id: string, callback: (controllerName: string, functionName: string, args: unknown[]) => Promise<unknown>): CallProxyType {
11
+ export function getCallProxy(id: string, callback: (callType: FullCallType) => Promise<unknown>): CallProxyType {
10
12
  let value = proxyCache.get(id);
11
13
  if (!value) {
12
- let controllerCache = new Map<string, CallProxyType[""]>();
14
+ let nodeCache = new Map<string, CallProxyType[""]>();
13
15
  value = new Proxy(Object.create(null), {
14
- get(target, controllerName) {
15
- if (typeof controllerName !== "string") return undefined;
16
- let controller = controllerCache.get(controllerName);
17
- if (!controller) {
18
- controller = new Proxy(Object.create(null), {
16
+ get(target, nodeId) {
17
+ if (typeof nodeId !== "string") return undefined;
18
+ let nodeProxy = nodeCache.get(nodeId);
19
+ if (!nodeProxy) {
20
+ nodeProxy = new Proxy(Object.create(null), {
19
21
  get(target, functionName) {
20
22
  if (typeof functionName !== "string") return undefined;
21
- return (...args: unknown[]) => callback(controllerName, functionName, args);
23
+ return Object.assign(
24
+ (...args: unknown[]) => {
25
+ let call: FullCallType = {
26
+ classGuid: id,
27
+ nodeId,
28
+ functionName,
29
+ args,
30
+ };
31
+ return callback(call);
32
+ },
33
+ {
34
+ [getCallObj]: (...args: unknown[]) => {
35
+ let call: FullCallType = {
36
+ classGuid: id,
37
+ nodeId,
38
+ functionName,
39
+ args,
40
+ };
41
+ return call;
42
+ }
43
+ }
44
+ );
22
45
  }
23
46
  }) as CallProxyType[""];
24
- controllerCache.set(controllerName, controller);
47
+ nodeCache.set(nodeId, nodeProxy);
25
48
  }
26
- return controller;
49
+ return nodeProxy;
27
50
  },
28
51
  }) as CallProxyType;
29
52
  proxyCache.set(id, value);
package/test/client.ts CHANGED
@@ -10,6 +10,7 @@ import { Test } from "./shared";
10
10
  import "../require/CSSShim";
11
11
  import "./client.css";
12
12
  import { isNode } from "../src/misc";
13
+ import { getCallObj } from "../src/nodeProxy";
13
14
 
14
15
  module.allowclient = true;
15
16
 
@@ -23,6 +24,8 @@ void main();
23
24
  async function main() {
24
25
  if (isNode()) return;
25
26
 
27
+ SocketFunction.rejectUnauthorized = false;
28
+
26
29
  SocketFunction.expose(Test);
27
30
 
28
31
  console.log("cool");
package/test/server.ts CHANGED
@@ -16,11 +16,14 @@ import "../require/CSSShim";
16
16
 
17
17
  // Import clientside files, so they can be whitelisted
18
18
  import "./client";
19
+ import { getCallObj } from "../src/nodeProxy";
19
20
 
20
21
 
21
22
  void main();
22
23
 
23
24
  async function main() {
25
+ SocketFunction.rejectUnauthorized = false;
26
+
24
27
  SocketFunction.expose(Test);
25
28
 
26
29
  RequireController._classGuid;
@@ -31,4 +34,12 @@ async function main() {
31
34
  const port = 2542;
32
35
 
33
36
  await SocketFunction.mount({ port });
37
+
38
+
39
+ {
40
+ let serverId = await SocketFunction.connect({ port, address: "letx.ca" });
41
+ let call = Test.nodes[serverId].add[getCallObj](1, 2);
42
+ console.log(call);
43
+ console.log(await SocketFunction.getHTTPCallLink(call));
44
+ }
34
45
  }