socket-function 1.2.28 → 1.2.30
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.d.ts +18 -45
- package/SocketFunction.ts +58 -82
- package/index.d.ts +25 -53
- package/package.json +1 -1
- package/src/CallFactory.ts +5 -5
- package/src/JSONLACKS/JSONLACKS.ts +11 -6
- package/src/createSingleton.d.ts +7 -8
- package/src/createSingleton.ts +15 -19
- package/src/misc.ts +5 -1
package/SocketFunction.d.ts
CHANGED
|
@@ -5,15 +5,12 @@ import { SocketExposedInterface, SocketFunctionHook, SocketFunctionClientHook, S
|
|
|
5
5
|
import { SocketServerConfig } from "./src/webSocketServer";
|
|
6
6
|
import { Args, MaybePromise } from "./src/types";
|
|
7
7
|
import "./SetProcessVariables";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
export interface SocketFunctionConfig {
|
|
15
|
-
logMessages: boolean;
|
|
16
|
-
trackMessageSizes: {
|
|
8
|
+
type ExtractShape<ClassType, Shape> = {
|
|
9
|
+
[key in keyof ClassType]: (key extends keyof Shape ? ClassType[key] extends SocketExposedInterface[""] ? ClassType[key] : ClassType[key] extends Function ? "All exposed function must be async (or return a Promise)" : never : "Function has implementation but is not exposed in the SocketFunction.register call");
|
|
10
|
+
};
|
|
11
|
+
export declare class SocketFunction {
|
|
12
|
+
static logMessages: boolean;
|
|
13
|
+
static trackMessageSizes: {
|
|
17
14
|
upload: ((size: number, nodeId: string) => void)[];
|
|
18
15
|
download: ((size: number, nodeId: string) => void)[];
|
|
19
16
|
callTimes: ((obj: {
|
|
@@ -22,48 +19,24 @@ export interface SocketFunctionConfig {
|
|
|
22
19
|
nodeId: string;
|
|
23
20
|
}) => void)[];
|
|
24
21
|
};
|
|
25
|
-
MAX_MESSAGE_SIZE: number;
|
|
26
|
-
HTTP_ETAG_CACHE: boolean;
|
|
27
|
-
silent: boolean;
|
|
28
|
-
HTTP_COMPRESS: boolean;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
COOP: string;
|
|
35
|
-
TOTAL_CALLS: number;
|
|
36
|
-
ENABLE_CLIENT_MODE: boolean;
|
|
37
|
-
WIRE_SERIALIZER: {
|
|
22
|
+
static MAX_MESSAGE_SIZE: number;
|
|
23
|
+
static HTTP_ETAG_CACHE: boolean;
|
|
24
|
+
static silent: boolean;
|
|
25
|
+
static HTTP_COMPRESS: boolean;
|
|
26
|
+
static COEP: string;
|
|
27
|
+
static COOP: string;
|
|
28
|
+
static TOTAL_CALLS: number;
|
|
29
|
+
static ENABLE_CLIENT_MODE: boolean;
|
|
30
|
+
static readonly WIRE_SERIALIZER: {
|
|
38
31
|
serialize: (obj: unknown) => MaybePromise<Buffer[]>;
|
|
39
32
|
deserialize: (buffers: Buffer[]) => MaybePromise<unknown>;
|
|
40
33
|
};
|
|
41
34
|
/** We will try the alternate node IDs first, however, if they fail, we will go through all of them and then eventually try the original node ID.
|
|
42
35
|
* VERY useful, allowing us to change global ips to local ones, which short-circuits the router, massively increasing bandwidth and decreasing latency.
|
|
43
36
|
*/
|
|
44
|
-
GET_ALTERNATE_NODE_IDS: (nodeId: string) => MaybePromise<string[] | undefined>;
|
|
45
|
-
WIRE_WARN_TIME: number;
|
|
46
|
-
|
|
47
|
-
DISABLE_COMPRESSION: boolean;
|
|
48
|
-
}
|
|
49
|
-
type ExtractShape<ClassType, Shape> = {
|
|
50
|
-
[key in keyof ClassType]: (key extends keyof Shape ? ClassType[key] extends SocketExposedInterface[""] ? ClassType[key] : ClassType[key] extends Function ? "All exposed function must be async (or return a Promise)" : never : "Function has implementation but is not exposed in the SocketFunction.register call");
|
|
51
|
-
};
|
|
52
|
-
export declare class SocketFunction {
|
|
53
|
-
static logMessages: SocketFunctionConfig["logMessages"];
|
|
54
|
-
static trackMessageSizes: SocketFunctionConfig["trackMessageSizes"];
|
|
55
|
-
static MAX_MESSAGE_SIZE: SocketFunctionConfig["MAX_MESSAGE_SIZE"];
|
|
56
|
-
static HTTP_ETAG_CACHE: SocketFunctionConfig["HTTP_ETAG_CACHE"];
|
|
57
|
-
static silent: SocketFunctionConfig["silent"];
|
|
58
|
-
static HTTP_COMPRESS: SocketFunctionConfig["HTTP_COMPRESS"];
|
|
59
|
-
static COEP: SocketFunctionConfig["COEP"];
|
|
60
|
-
static COOP: SocketFunctionConfig["COOP"];
|
|
61
|
-
static TOTAL_CALLS: SocketFunctionConfig["TOTAL_CALLS"];
|
|
62
|
-
static ENABLE_CLIENT_MODE: SocketFunctionConfig["ENABLE_CLIENT_MODE"];
|
|
63
|
-
static readonly WIRE_SERIALIZER: SocketFunctionConfig["WIRE_SERIALIZER"];
|
|
64
|
-
static GET_ALTERNATE_NODE_IDS: SocketFunctionConfig["GET_ALTERNATE_NODE_IDS"];
|
|
65
|
-
static WIRE_WARN_TIME: SocketFunctionConfig["WIRE_WARN_TIME"];
|
|
66
|
-
static DISABLE_COMPRESSION: SocketFunctionConfig["DISABLE_COMPRESSION"];
|
|
37
|
+
static GET_ALTERNATE_NODE_IDS: (nodeId: string) => MaybePromise<string[] | undefined>;
|
|
38
|
+
static WIRE_WARN_TIME: number;
|
|
39
|
+
static DISABLE_COMPRESSION: boolean;
|
|
67
40
|
static isClient(): boolean;
|
|
68
41
|
private static onMountCallbacks;
|
|
69
42
|
private static exposedClassesSingleton;
|
package/SocketFunction.ts
CHANGED
|
@@ -45,86 +45,69 @@ const socketContext = createSingleton("SocketFunction.socketContext", 1, () => (
|
|
|
45
45
|
caller: undefined as CallerContext | undefined,
|
|
46
46
|
}));
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
48
|
+
type ExtractShape<ClassType, Shape> = {
|
|
49
|
+
[key in keyof ClassType]: (
|
|
50
|
+
key extends keyof Shape
|
|
51
|
+
? ClassType[key] extends SocketExposedInterface[""]
|
|
52
|
+
? ClassType[key]
|
|
53
|
+
: ClassType[key] extends Function ? "All exposed function must be async (or return a Promise)" : never
|
|
54
|
+
: "Function has implementation but is not exposed in the SocketFunction.register call"
|
|
55
|
+
);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export class SocketFunction {
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
// #region Shared config statics. EVERYTHING in this region (and nothing outside it) is redefined
|
|
62
|
+
// below the class by defineSingletonConfig, which replaces each property (via defineProperty)
|
|
63
|
+
// with accessors onto a singleton shared by every copy of this package - the inline values here
|
|
64
|
+
// are just the defaults.
|
|
65
|
+
public static logMessages = false;
|
|
66
|
+
public static trackMessageSizes = {
|
|
67
|
+
upload: [] as ((size: number, nodeId: string) => void)[],
|
|
68
|
+
download: [] as ((size: number, nodeId: string) => void)[],
|
|
69
|
+
callTimes: [] as ((obj: { start: number; end: number; nodeId: string; }) => void)[],
|
|
60
70
|
};
|
|
61
71
|
|
|
62
|
-
MAX_MESSAGE_SIZE
|
|
72
|
+
public static MAX_MESSAGE_SIZE = 1024 * 1024 * 32;
|
|
63
73
|
|
|
64
|
-
HTTP_ETAG_CACHE
|
|
65
|
-
silent
|
|
74
|
+
public static HTTP_ETAG_CACHE = false;
|
|
75
|
+
public static silent = true;
|
|
66
76
|
|
|
67
|
-
HTTP_COMPRESS
|
|
77
|
+
public static HTTP_COMPRESS = false;
|
|
68
78
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
COOP: string;
|
|
79
|
+
// If you have HTTP resources that require cookies you might to set `SocketFunction.COEP = "require-corp"`
|
|
80
|
+
// - Cross-origin-resource-policy.
|
|
81
|
+
public static COEP = "credentialless";
|
|
82
|
+
// NOTE: This COOP and COEP defaults are required so window.crossOriginIsolated will be true.
|
|
83
|
+
public static COOP = "same-origin";
|
|
75
84
|
|
|
76
|
-
TOTAL_CALLS
|
|
85
|
+
public static TOTAL_CALLS = 0;
|
|
77
86
|
|
|
78
|
-
ENABLE_CLIENT_MODE
|
|
87
|
+
public static ENABLE_CLIENT_MODE = false;
|
|
79
88
|
|
|
80
89
|
// In retrospect... dynamically changing the wire serializer is a BAD idea. If any calls happen
|
|
81
90
|
// before it is changed, things just break. Also, it needs to be changed on both sides,
|
|
82
91
|
// or else things break. Also, it is very hard to detect when the issue is different serializers
|
|
83
92
|
// NOTE: The only reason this is still exposed is in case in the future we want to intercept our traffic, and we want convenient functions to know how to decode it (although there are a still few other layers under this, for compression and Buffer[] sending efficiency).
|
|
84
|
-
WIRE_SERIALIZER
|
|
85
|
-
serialize: (obj: unknown)
|
|
86
|
-
deserialize: (buffers: Buffer[])
|
|
93
|
+
public static readonly WIRE_SERIALIZER = {
|
|
94
|
+
serialize: measureWrap((obj: unknown): MaybePromise<Buffer[]> => [cborxInstance.encode(obj)], "WIRE_SERIALIZER|serialize"),
|
|
95
|
+
deserialize: measureWrap((buffers: Buffer[]): MaybePromise<unknown> => cborxInstance.decode(buffers[0]), "WIRE_SERIALIZER|deserialize"),
|
|
87
96
|
};
|
|
88
97
|
|
|
89
98
|
/** We will try the alternate node IDs first, however, if they fail, we will go through all of them and then eventually try the original node ID.
|
|
90
99
|
* VERY useful, allowing us to change global ips to local ones, which short-circuits the router, massively increasing bandwidth and decreasing latency.
|
|
91
100
|
*/
|
|
92
|
-
GET_ALTERNATE_NODE_IDS
|
|
101
|
+
public static GET_ALTERNATE_NODE_IDS = (nodeId: string): MaybePromise<string[] | undefined> => undefined;
|
|
93
102
|
|
|
94
|
-
WIRE_WARN_TIME
|
|
103
|
+
public static WIRE_WARN_TIME = 100;
|
|
104
|
+
|
|
105
|
+
// Process-wide compression kill switch. When set before connections are established, LZ4 is left out of the protocol negotiation entirely (both for connections we initiate and ones we accept), so NEITHER side compresses — the wire format stays the plain backwards-compatible one. Overrides per-function `compress` flags.
|
|
106
|
+
public static DISABLE_COMPRESSION = false;
|
|
107
|
+
// #endregion Shared config statics.
|
|
95
108
|
|
|
96
|
-
/** Process-wide compression kill switch. When set before connections are established, LZ4 is left out of the protocol negotiation entirely (both for connections we initiate and ones we accept), so NEITHER side compresses — the wire format stays the plain backwards-compatible one. Overrides per-function `compress` flags. */
|
|
97
|
-
DISABLE_COMPRESSION: boolean;
|
|
98
|
-
}
|
|
99
109
|
|
|
100
|
-
type ExtractShape<ClassType, Shape> = {
|
|
101
|
-
[key in keyof ClassType]: (
|
|
102
|
-
key extends keyof Shape
|
|
103
|
-
? ClassType[key] extends SocketExposedInterface[""]
|
|
104
|
-
? ClassType[key]
|
|
105
|
-
: ClassType[key] extends Function ? "All exposed function must be async (or return a Promise)" : never
|
|
106
|
-
: "Function has implementation but is not exposed in the SocketFunction.register call"
|
|
107
|
-
);
|
|
108
|
-
};
|
|
109
110
|
|
|
110
|
-
export class SocketFunction {
|
|
111
|
-
// All of these are accessors onto a shared singleton, installed after the class declaration.
|
|
112
|
-
// See SocketFunctionConfig for their documentation, and defineSingletonConfig for why they
|
|
113
|
-
// cannot be plain static fields.
|
|
114
|
-
public declare static logMessages: SocketFunctionConfig["logMessages"];
|
|
115
|
-
public declare static trackMessageSizes: SocketFunctionConfig["trackMessageSizes"];
|
|
116
|
-
public declare static MAX_MESSAGE_SIZE: SocketFunctionConfig["MAX_MESSAGE_SIZE"];
|
|
117
|
-
public declare static HTTP_ETAG_CACHE: SocketFunctionConfig["HTTP_ETAG_CACHE"];
|
|
118
|
-
public declare static silent: SocketFunctionConfig["silent"];
|
|
119
|
-
public declare static HTTP_COMPRESS: SocketFunctionConfig["HTTP_COMPRESS"];
|
|
120
|
-
public declare static COEP: SocketFunctionConfig["COEP"];
|
|
121
|
-
public declare static COOP: SocketFunctionConfig["COOP"];
|
|
122
|
-
public declare static TOTAL_CALLS: SocketFunctionConfig["TOTAL_CALLS"];
|
|
123
|
-
public declare static ENABLE_CLIENT_MODE: SocketFunctionConfig["ENABLE_CLIENT_MODE"];
|
|
124
|
-
public declare static readonly WIRE_SERIALIZER: SocketFunctionConfig["WIRE_SERIALIZER"];
|
|
125
|
-
public declare static GET_ALTERNATE_NODE_IDS: SocketFunctionConfig["GET_ALTERNATE_NODE_IDS"];
|
|
126
|
-
public declare static WIRE_WARN_TIME: SocketFunctionConfig["WIRE_WARN_TIME"];
|
|
127
|
-
public declare static DISABLE_COMPRESSION: SocketFunctionConfig["DISABLE_COMPRESSION"];
|
|
128
111
|
|
|
129
112
|
// Places where we decide if we want to act as a client. Most places we check for is node, but some places it's not, depending on if we're in Node.js or not, it's depending on if we're a client or not.
|
|
130
113
|
public static isClient() { return !isNode() || SocketFunction.ENABLE_CLIENT_MODE; }
|
|
@@ -541,29 +524,22 @@ export class SocketFunction {
|
|
|
541
524
|
}
|
|
542
525
|
}
|
|
543
526
|
|
|
544
|
-
defineSingletonConfig
|
|
545
|
-
logMessages
|
|
546
|
-
trackMessageSizes
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
serialize: measureWrap((obj: unknown): MaybePromise<Buffer[]> => [cborxInstance.encode(obj)], "WIRE_SERIALIZER|serialize"),
|
|
561
|
-
deserialize: measureWrap((buffers: Buffer[]): MaybePromise<unknown> => cborxInstance.decode(buffers[0]), "WIRE_SERIALIZER|deserialize"),
|
|
562
|
-
},
|
|
563
|
-
GET_ALTERNATE_NODE_IDS: (nodeId: string): MaybePromise<string[] | undefined> => undefined,
|
|
564
|
-
WIRE_WARN_TIME: 100,
|
|
565
|
-
DISABLE_COMPRESSION: false,
|
|
566
|
-
}));
|
|
527
|
+
defineSingletonConfig(SocketFunction, "SocketFunction.config", 1, [
|
|
528
|
+
"logMessages",
|
|
529
|
+
"trackMessageSizes",
|
|
530
|
+
"MAX_MESSAGE_SIZE",
|
|
531
|
+
"HTTP_ETAG_CACHE",
|
|
532
|
+
"silent",
|
|
533
|
+
"HTTP_COMPRESS",
|
|
534
|
+
"COEP",
|
|
535
|
+
"COOP",
|
|
536
|
+
"TOTAL_CALLS",
|
|
537
|
+
"ENABLE_CLIENT_MODE",
|
|
538
|
+
"WIRE_SERIALIZER",
|
|
539
|
+
"GET_ALTERNATE_NODE_IDS",
|
|
540
|
+
"WIRE_WARN_TIME",
|
|
541
|
+
"DISABLE_COMPRESSION",
|
|
542
|
+
]);
|
|
567
543
|
|
|
568
544
|
declare global {
|
|
569
545
|
var BOOTED_EDGE_NODE: { host: string } | undefined;
|
package/index.d.ts
CHANGED
|
@@ -14,15 +14,12 @@ declare module "socket-function/SocketFunction" {
|
|
|
14
14
|
import { SocketServerConfig } from "socket-function/src/webSocketServer";
|
|
15
15
|
import { Args, MaybePromise } from "socket-function/src/types";
|
|
16
16
|
import "./SetProcessVariables";
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
export interface SocketFunctionConfig {
|
|
24
|
-
logMessages: boolean;
|
|
25
|
-
trackMessageSizes: {
|
|
17
|
+
type ExtractShape<ClassType, Shape> = {
|
|
18
|
+
[key in keyof ClassType]: (key extends keyof Shape ? ClassType[key] extends SocketExposedInterface[""] ? ClassType[key] : ClassType[key] extends Function ? "All exposed function must be async (or return a Promise)" : never : "Function has implementation but is not exposed in the SocketFunction.register call");
|
|
19
|
+
};
|
|
20
|
+
export declare class SocketFunction {
|
|
21
|
+
static logMessages: boolean;
|
|
22
|
+
static trackMessageSizes: {
|
|
26
23
|
upload: ((size: number, nodeId: string) => void)[];
|
|
27
24
|
download: ((size: number, nodeId: string) => void)[];
|
|
28
25
|
callTimes: ((obj: {
|
|
@@ -31,48 +28,24 @@ declare module "socket-function/SocketFunction" {
|
|
|
31
28
|
nodeId: string;
|
|
32
29
|
}) => void)[];
|
|
33
30
|
};
|
|
34
|
-
MAX_MESSAGE_SIZE: number;
|
|
35
|
-
HTTP_ETAG_CACHE: boolean;
|
|
36
|
-
silent: boolean;
|
|
37
|
-
HTTP_COMPRESS: boolean;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
COOP: string;
|
|
44
|
-
TOTAL_CALLS: number;
|
|
45
|
-
ENABLE_CLIENT_MODE: boolean;
|
|
46
|
-
WIRE_SERIALIZER: {
|
|
31
|
+
static MAX_MESSAGE_SIZE: number;
|
|
32
|
+
static HTTP_ETAG_CACHE: boolean;
|
|
33
|
+
static silent: boolean;
|
|
34
|
+
static HTTP_COMPRESS: boolean;
|
|
35
|
+
static COEP: string;
|
|
36
|
+
static COOP: string;
|
|
37
|
+
static TOTAL_CALLS: number;
|
|
38
|
+
static ENABLE_CLIENT_MODE: boolean;
|
|
39
|
+
static readonly WIRE_SERIALIZER: {
|
|
47
40
|
serialize: (obj: unknown) => MaybePromise<Buffer[]>;
|
|
48
41
|
deserialize: (buffers: Buffer[]) => MaybePromise<unknown>;
|
|
49
42
|
};
|
|
50
43
|
/** We will try the alternate node IDs first, however, if they fail, we will go through all of them and then eventually try the original node ID.
|
|
51
44
|
* VERY useful, allowing us to change global ips to local ones, which short-circuits the router, massively increasing bandwidth and decreasing latency.
|
|
52
45
|
*/
|
|
53
|
-
GET_ALTERNATE_NODE_IDS: (nodeId: string) => MaybePromise<string[] | undefined>;
|
|
54
|
-
WIRE_WARN_TIME: number;
|
|
55
|
-
|
|
56
|
-
DISABLE_COMPRESSION: boolean;
|
|
57
|
-
}
|
|
58
|
-
type ExtractShape<ClassType, Shape> = {
|
|
59
|
-
[key in keyof ClassType]: (key extends keyof Shape ? ClassType[key] extends SocketExposedInterface[""] ? ClassType[key] : ClassType[key] extends Function ? "All exposed function must be async (or return a Promise)" : never : "Function has implementation but is not exposed in the SocketFunction.register call");
|
|
60
|
-
};
|
|
61
|
-
export declare class SocketFunction {
|
|
62
|
-
static logMessages: SocketFunctionConfig["logMessages"];
|
|
63
|
-
static trackMessageSizes: SocketFunctionConfig["trackMessageSizes"];
|
|
64
|
-
static MAX_MESSAGE_SIZE: SocketFunctionConfig["MAX_MESSAGE_SIZE"];
|
|
65
|
-
static HTTP_ETAG_CACHE: SocketFunctionConfig["HTTP_ETAG_CACHE"];
|
|
66
|
-
static silent: SocketFunctionConfig["silent"];
|
|
67
|
-
static HTTP_COMPRESS: SocketFunctionConfig["HTTP_COMPRESS"];
|
|
68
|
-
static COEP: SocketFunctionConfig["COEP"];
|
|
69
|
-
static COOP: SocketFunctionConfig["COOP"];
|
|
70
|
-
static TOTAL_CALLS: SocketFunctionConfig["TOTAL_CALLS"];
|
|
71
|
-
static ENABLE_CLIENT_MODE: SocketFunctionConfig["ENABLE_CLIENT_MODE"];
|
|
72
|
-
static readonly WIRE_SERIALIZER: SocketFunctionConfig["WIRE_SERIALIZER"];
|
|
73
|
-
static GET_ALTERNATE_NODE_IDS: SocketFunctionConfig["GET_ALTERNATE_NODE_IDS"];
|
|
74
|
-
static WIRE_WARN_TIME: SocketFunctionConfig["WIRE_WARN_TIME"];
|
|
75
|
-
static DISABLE_COMPRESSION: SocketFunctionConfig["DISABLE_COMPRESSION"];
|
|
46
|
+
static GET_ALTERNATE_NODE_IDS: (nodeId: string) => MaybePromise<string[] | undefined>;
|
|
47
|
+
static WIRE_WARN_TIME: number;
|
|
48
|
+
static DISABLE_COMPRESSION: boolean;
|
|
76
49
|
static isClient(): boolean;
|
|
77
50
|
private static onMountCallbacks;
|
|
78
51
|
private static exposedClassesSingleton;
|
|
@@ -876,17 +849,16 @@ declare module "socket-function/src/createSingleton" {
|
|
|
876
849
|
* get()/set() at each access so every copy sees the latest.
|
|
877
850
|
*/
|
|
878
851
|
export declare function createSingleton<T>(name: string, version: string | number, getDefault: () => T): Singleton<T>;
|
|
879
|
-
/**
|
|
880
|
-
*
|
|
881
|
-
*
|
|
882
|
-
*
|
|
883
|
-
*
|
|
884
|
-
*
|
|
885
|
-
* types from the config interface keeps the two from drifting apart.
|
|
852
|
+
/** Redefines the given (already initialized) properties of target as accessors onto a singleton, so
|
|
853
|
+
* `Target.SOME_SETTING = x` configures every copy of the package instead of only the copy the
|
|
854
|
+
* caller happened to import (which is otherwise decided by module resolution, and so is
|
|
855
|
+
* essentially arbitrary from the caller's perspective). The properties' current values become
|
|
856
|
+
* the defaults, so config statics stay defined inline in the class as usual - just call this
|
|
857
|
+
* below the class with the list of statics to share.
|
|
886
858
|
* - Same versioning rules as createSingleton, except that adding a property is not a shape change:
|
|
887
859
|
* a copy that knows about a property older copies don't backfills it below.
|
|
888
860
|
*/
|
|
889
|
-
export declare function defineSingletonConfig<T extends object>(target:
|
|
861
|
+
export declare function defineSingletonConfig<T extends object>(target: T, name: string, version: string | number, keys: (keyof T & string)[]): void;
|
|
890
862
|
|
|
891
863
|
}
|
|
892
864
|
|
package/package.json
CHANGED
package/src/CallFactory.ts
CHANGED
|
@@ -316,7 +316,7 @@ export async function createCallFactory(
|
|
|
316
316
|
registerOnce();
|
|
317
317
|
callFactory.receivedInitializeState = undefined;
|
|
318
318
|
|
|
319
|
-
function onClose(
|
|
319
|
+
function onClose(message: string) {
|
|
320
320
|
// We try various connections, and if they fail, we will just try other node IDs until we finally do connect, and then we stick with that nodeId, and when it disconnects we need to handle disconnections normally.
|
|
321
321
|
if (skipCloseHandling && !hasEverConnected) {
|
|
322
322
|
return;
|
|
@@ -335,7 +335,7 @@ export async function createCallFactory(
|
|
|
335
335
|
call.callback({
|
|
336
336
|
isReturn: true,
|
|
337
337
|
result: undefined,
|
|
338
|
-
error:
|
|
338
|
+
error: `Call failed for ${call.call.classGuid}.${call.call.functionName}: ${message}`,
|
|
339
339
|
seqNum: call.call.seqNum,
|
|
340
340
|
});
|
|
341
341
|
}
|
|
@@ -353,12 +353,12 @@ export async function createCallFactory(
|
|
|
353
353
|
// NOTE: No more logging, as we throw, so the caller should be logging the
|
|
354
354
|
// error (or swallowing it, if that is what it wants to do).
|
|
355
355
|
//console.log(`Websocket error for ${niceConnectionName}`, e.message);
|
|
356
|
-
onClose(
|
|
356
|
+
onClose(`Connection error for ${niceConnectionName}: ${e.message}`);
|
|
357
357
|
});
|
|
358
358
|
|
|
359
359
|
newWebSocket.addEventListener("close", async () => {
|
|
360
360
|
//console.log(`Websocket closed ${niceConnectionName}`);
|
|
361
|
-
onClose(
|
|
361
|
+
onClose(`Connection closed to ${niceConnectionName}`);
|
|
362
362
|
});
|
|
363
363
|
|
|
364
364
|
newWebSocket.addEventListener("message", onMessage);
|
|
@@ -381,7 +381,7 @@ export async function createCallFactory(
|
|
|
381
381
|
callFactory.isConnected = true;
|
|
382
382
|
hasEverConnected = true;
|
|
383
383
|
} else {
|
|
384
|
-
onClose(
|
|
384
|
+
onClose(`Websocket received in closed state`);
|
|
385
385
|
}
|
|
386
386
|
|
|
387
387
|
if (callFactory.lastClosed && callFactory.isConnected) {
|
|
@@ -45,9 +45,14 @@ interface HydrateState {
|
|
|
45
45
|
export class JSONLACKS {
|
|
46
46
|
public static readonly LACKS_KEY = "__JSONLACKS__98cfb4a05fa34d828661cae15b8779ce__";
|
|
47
47
|
|
|
48
|
+
// #region Shared config statics. EVERYTHING in this region (and nothing outside it) is redefined
|
|
49
|
+
// below the class by defineSingletonConfig, which replaces each property (via defineProperty)
|
|
50
|
+
// with accessors onto a singleton shared by every copy of this package - the inline values here
|
|
51
|
+
// are just the defaults.
|
|
48
52
|
/** If set to true parses non-quoted field names, comments, trailing commas, etc */
|
|
49
|
-
public
|
|
50
|
-
public
|
|
53
|
+
public static EXTENDED_PARSER = false;
|
|
54
|
+
public static IGNORE_MISSING_REFERENCES = false;
|
|
55
|
+
// #endregion Shared config statics.
|
|
51
56
|
|
|
52
57
|
public static stringify(obj: unknown, config?: JSONLACKS_StringifyConfig): string {
|
|
53
58
|
let serialized = JSONLACKS.escapeSpecialObjects(obj, config);
|
|
@@ -300,10 +305,10 @@ export class JSONLACKS {
|
|
|
300
305
|
}
|
|
301
306
|
}
|
|
302
307
|
|
|
303
|
-
defineSingletonConfig(JSONLACKS, "JSONLACKS.config", 1,
|
|
304
|
-
EXTENDED_PARSER
|
|
305
|
-
IGNORE_MISSING_REFERENCES
|
|
306
|
-
|
|
308
|
+
defineSingletonConfig(JSONLACKS, "JSONLACKS.config", 1, [
|
|
309
|
+
"EXTENDED_PARSER",
|
|
310
|
+
"IGNORE_MISSING_REFERENCES",
|
|
311
|
+
]);
|
|
307
312
|
|
|
308
313
|
async function benchmark() {
|
|
309
314
|
const loops = 1000 * 100;
|
package/src/createSingleton.d.ts
CHANGED
|
@@ -17,14 +17,13 @@ export interface Singleton<T> {
|
|
|
17
17
|
* get()/set() at each access so every copy sees the latest.
|
|
18
18
|
*/
|
|
19
19
|
export declare function createSingleton<T>(name: string, version: string | number, getDefault: () => T): Singleton<T>;
|
|
20
|
-
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* types from the config interface keeps the two from drifting apart.
|
|
20
|
+
/** Redefines the given (already initialized) properties of target as accessors onto a singleton, so
|
|
21
|
+
* `Target.SOME_SETTING = x` configures every copy of the package instead of only the copy the
|
|
22
|
+
* caller happened to import (which is otherwise decided by module resolution, and so is
|
|
23
|
+
* essentially arbitrary from the caller's perspective). The properties' current values become
|
|
24
|
+
* the defaults, so config statics stay defined inline in the class as usual - just call this
|
|
25
|
+
* below the class with the list of statics to share.
|
|
27
26
|
* - Same versioning rules as createSingleton, except that adding a property is not a shape change:
|
|
28
27
|
* a copy that knows about a property older copies don't backfills it below.
|
|
29
28
|
*/
|
|
30
|
-
export declare function defineSingletonConfig<T extends object>(target:
|
|
29
|
+
export declare function defineSingletonConfig<T extends object>(target: T, name: string, version: string | number, keys: (keyof T & string)[]): void;
|
package/src/createSingleton.ts
CHANGED
|
@@ -75,39 +75,35 @@ export function createSingleton<T>(
|
|
|
75
75
|
};
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
/**
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
* types from the config interface keeps the two from drifting apart.
|
|
78
|
+
/** Redefines the given (already initialized) properties of target as accessors onto a singleton, so
|
|
79
|
+
* `Target.SOME_SETTING = x` configures every copy of the package instead of only the copy the
|
|
80
|
+
* caller happened to import (which is otherwise decided by module resolution, and so is
|
|
81
|
+
* essentially arbitrary from the caller's perspective). The properties' current values become
|
|
82
|
+
* the defaults, so config statics stay defined inline in the class as usual - just call this
|
|
83
|
+
* below the class with the list of statics to share.
|
|
85
84
|
* - Same versioning rules as createSingleton, except that adding a property is not a shape change:
|
|
86
85
|
* a copy that knows about a property older copies don't backfills it below.
|
|
87
86
|
*/
|
|
88
87
|
export function defineSingletonConfig<T extends object>(
|
|
89
|
-
target:
|
|
88
|
+
target: T,
|
|
90
89
|
name: string,
|
|
91
90
|
version: string | number,
|
|
92
|
-
|
|
93
|
-
):
|
|
94
|
-
const
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
if (!(key in values)) {
|
|
99
|
-
values[key] = (defaults as { [key: string]: unknown })[key];
|
|
91
|
+
keys: (keyof T & string)[],
|
|
92
|
+
): void {
|
|
93
|
+
const store = createSingleton<{ [key: string]: unknown }>(name, version, () => Object.create(null)).get();
|
|
94
|
+
for (const key of keys) {
|
|
95
|
+
if (!(key in store)) {
|
|
96
|
+
store[key] = target[key];
|
|
100
97
|
}
|
|
101
98
|
Object.defineProperty(target, key, {
|
|
102
99
|
get() {
|
|
103
|
-
return
|
|
100
|
+
return store[key];
|
|
104
101
|
},
|
|
105
102
|
set(value: unknown) {
|
|
106
|
-
|
|
103
|
+
store[key] = value;
|
|
107
104
|
},
|
|
108
105
|
enumerable: true,
|
|
109
106
|
configurable: true,
|
|
110
107
|
});
|
|
111
108
|
}
|
|
112
|
-
return singleton;
|
|
113
109
|
}
|
package/src/misc.ts
CHANGED
|
@@ -15,7 +15,11 @@ export type Watchable<T> = (callback: (value: T) => void) => MaybePromise<void>;
|
|
|
15
15
|
export function convertErrorStackToError(error: string): Error {
|
|
16
16
|
let errorObj = new Error();
|
|
17
17
|
errorObj.stack = String(error);
|
|
18
|
-
|
|
18
|
+
let message = String(error).split("\n")[0];
|
|
19
|
+
if (message.startsWith("Error: ")) {
|
|
20
|
+
message = message.slice("Error: ".length);
|
|
21
|
+
}
|
|
22
|
+
errorObj.message = message;
|
|
19
23
|
return errorObj;
|
|
20
24
|
}
|
|
21
25
|
|