socket-function 1.2.26 → 1.2.28
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/.claude/settings.local.json +2 -1
- package/SocketFunction.d.ts +46 -19
- package/SocketFunction.ts +90 -38
- package/index.d.ts +57 -19
- package/package.json +1 -1
- package/src/JSONLACKS/JSONLACKS.ts +8 -2
- package/src/callHTTPHandler.ts +12 -3
- package/src/createSingleton.d.ts +11 -0
- package/src/createSingleton.ts +37 -0
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
"Bash(cp -r 'C:/Users/quent/AppData/Local/BraveSoftware/Brave-Browser/User Data/Default/Local Extension Settings/dhdgffkkebhmkfjojejmpbldmpobfkfo' C:/Users/quent/AppData/Local/Temp/claude/D--repos-socket-function/117d10bd-31c6-4c68-938d-1fff47788fa6/scratchpad/tm-recover/LocalExtSettings)",
|
|
6
6
|
"Bash(yarn test-dns-claude *)",
|
|
7
7
|
"Bash(yarn type *)",
|
|
8
|
-
"Bash(yarn test *)"
|
|
8
|
+
"Bash(yarn test *)",
|
|
9
|
+
"PowerShell(yarn type *)"
|
|
9
10
|
]
|
|
10
11
|
}
|
|
11
12
|
}
|
package/SocketFunction.d.ts
CHANGED
|
@@ -5,12 +5,15 @@ 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
|
-
|
|
8
|
+
/** The values behind SocketFunction's configuration statics. They are exposed on the class as
|
|
9
|
+
* accessors onto a singleton (see the defineSingletonConfig call at the bottom of this file), so
|
|
10
|
+
* that configuration set through one copy of this package applies to all of them. This interface
|
|
11
|
+
* is the single source of truth for their types - the class declares each one as
|
|
12
|
+
* `declare static X: SocketFunctionConfig["X"]`.
|
|
13
|
+
*/
|
|
14
|
+
export interface SocketFunctionConfig {
|
|
15
|
+
logMessages: boolean;
|
|
16
|
+
trackMessageSizes: {
|
|
14
17
|
upload: ((size: number, nodeId: string) => void)[];
|
|
15
18
|
download: ((size: number, nodeId: string) => void)[];
|
|
16
19
|
callTimes: ((obj: {
|
|
@@ -19,25 +22,49 @@ export declare class SocketFunction {
|
|
|
19
22
|
nodeId: string;
|
|
20
23
|
}) => void)[];
|
|
21
24
|
};
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
25
|
+
MAX_MESSAGE_SIZE: number;
|
|
26
|
+
HTTP_ETAG_CACHE: boolean;
|
|
27
|
+
silent: boolean;
|
|
28
|
+
HTTP_COMPRESS: boolean;
|
|
29
|
+
/** If you have HTTP resources that require cookies you might to set `SocketFunction.COEP = "require-corp"`
|
|
30
|
+
* - Cross-origin-resource-policy.
|
|
31
|
+
* NOTE: This COOP and COEP defaults are required so window.crossOriginIsolated will be true.
|
|
32
|
+
*/
|
|
33
|
+
COEP: string;
|
|
34
|
+
COOP: string;
|
|
35
|
+
TOTAL_CALLS: number;
|
|
36
|
+
ENABLE_CLIENT_MODE: boolean;
|
|
37
|
+
WIRE_SERIALIZER: {
|
|
32
38
|
serialize: (obj: unknown) => MaybePromise<Buffer[]>;
|
|
33
39
|
deserialize: (buffers: Buffer[]) => MaybePromise<unknown>;
|
|
34
40
|
};
|
|
35
41
|
/** 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.
|
|
36
42
|
* VERY useful, allowing us to change global ips to local ones, which short-circuits the router, massively increasing bandwidth and decreasing latency.
|
|
37
43
|
*/
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
44
|
+
GET_ALTERNATE_NODE_IDS: (nodeId: string) => MaybePromise<string[] | undefined>;
|
|
45
|
+
WIRE_WARN_TIME: number;
|
|
46
|
+
/** 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. */
|
|
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"];
|
|
67
|
+
static isClient(): boolean;
|
|
41
68
|
private static onMountCallbacks;
|
|
42
69
|
private static exposedClassesSingleton;
|
|
43
70
|
static get exposedClasses(): Set<string>;
|
package/SocketFunction.ts
CHANGED
|
@@ -9,7 +9,7 @@ import { Args, MaybePromise } from "./src/types";
|
|
|
9
9
|
import { setDefaultHTTPCall } from "./src/callHTTPHandler";
|
|
10
10
|
import debugbreak from "debugbreak";
|
|
11
11
|
import { lazy } from "./src/caching";
|
|
12
|
-
import { createSingleton } from "./src/createSingleton";
|
|
12
|
+
import { createSingleton, defineSingletonConfig } from "./src/createSingleton";
|
|
13
13
|
import { delay } from "./src/batching";
|
|
14
14
|
import { blue, magenta } from "./src/formatting/logColors";
|
|
15
15
|
import { JSONLACKS } from "./src/JSONLACKS/JSONLACKS";
|
|
@@ -45,61 +45,89 @@ 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
|
-
|
|
60
|
-
public static trackMessageSizes = {
|
|
61
|
-
upload: [] as ((size: number, nodeId: string) => void)[],
|
|
62
|
-
download: [] as ((size: number, nodeId: string) => void)[],
|
|
63
|
-
callTimes: [] as ((obj: { start: number; end: number; nodeId: string; }) => void)[],
|
|
48
|
+
/** The values behind SocketFunction's configuration statics. They are exposed on the class as
|
|
49
|
+
* accessors onto a singleton (see the defineSingletonConfig call at the bottom of this file), so
|
|
50
|
+
* that configuration set through one copy of this package applies to all of them. This interface
|
|
51
|
+
* is the single source of truth for their types - the class declares each one as
|
|
52
|
+
* `declare static X: SocketFunctionConfig["X"]`.
|
|
53
|
+
*/
|
|
54
|
+
export interface SocketFunctionConfig {
|
|
55
|
+
logMessages: boolean;
|
|
56
|
+
trackMessageSizes: {
|
|
57
|
+
upload: ((size: number, nodeId: string) => void)[];
|
|
58
|
+
download: ((size: number, nodeId: string) => void)[];
|
|
59
|
+
callTimes: ((obj: { start: number; end: number; nodeId: string; }) => void)[];
|
|
64
60
|
};
|
|
65
61
|
|
|
66
|
-
|
|
62
|
+
MAX_MESSAGE_SIZE: number;
|
|
67
63
|
|
|
68
|
-
|
|
69
|
-
|
|
64
|
+
HTTP_ETAG_CACHE: boolean;
|
|
65
|
+
silent: boolean;
|
|
70
66
|
|
|
71
|
-
|
|
67
|
+
HTTP_COMPRESS: boolean;
|
|
72
68
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
69
|
+
/** If you have HTTP resources that require cookies you might to set `SocketFunction.COEP = "require-corp"`
|
|
70
|
+
* - Cross-origin-resource-policy.
|
|
71
|
+
* NOTE: This COOP and COEP defaults are required so window.crossOriginIsolated will be true.
|
|
72
|
+
*/
|
|
73
|
+
COEP: string;
|
|
74
|
+
COOP: string;
|
|
78
75
|
|
|
79
|
-
|
|
76
|
+
TOTAL_CALLS: number;
|
|
80
77
|
|
|
81
|
-
|
|
82
|
-
// 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.
|
|
83
|
-
public static isClient() { return !isNode() || SocketFunction.ENABLE_CLIENT_MODE; }
|
|
78
|
+
ENABLE_CLIENT_MODE: boolean;
|
|
84
79
|
|
|
85
80
|
// In retrospect... dynamically changing the wire serializer is a BAD idea. If any calls happen
|
|
86
81
|
// before it is changed, things just break. Also, it needs to be changed on both sides,
|
|
87
82
|
// or else things break. Also, it is very hard to detect when the issue is different serializers
|
|
88
83
|
// 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).
|
|
89
|
-
|
|
90
|
-
serialize:
|
|
91
|
-
deserialize:
|
|
84
|
+
WIRE_SERIALIZER: {
|
|
85
|
+
serialize: (obj: unknown) => MaybePromise<Buffer[]>;
|
|
86
|
+
deserialize: (buffers: Buffer[]) => MaybePromise<unknown>;
|
|
92
87
|
};
|
|
93
88
|
|
|
94
|
-
/** 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.
|
|
89
|
+
/** 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.
|
|
95
90
|
* VERY useful, allowing us to change global ips to local ones, which short-circuits the router, massively increasing bandwidth and decreasing latency.
|
|
96
91
|
*/
|
|
97
|
-
|
|
92
|
+
GET_ALTERNATE_NODE_IDS: (nodeId: string) => MaybePromise<string[] | undefined>;
|
|
93
|
+
|
|
94
|
+
WIRE_WARN_TIME: number;
|
|
95
|
+
|
|
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
|
+
|
|
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
|
+
};
|
|
98
109
|
|
|
99
|
-
|
|
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"];
|
|
100
128
|
|
|
101
|
-
//
|
|
102
|
-
public static
|
|
129
|
+
// 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
|
+
public static isClient() { return !isNode() || SocketFunction.ENABLE_CLIENT_MODE; }
|
|
103
131
|
|
|
104
132
|
// Shared across copies of this package, so onMount callbacks registered through one copy still
|
|
105
133
|
// fire when another copy mounts. See createSingleton.
|
|
@@ -513,6 +541,30 @@ export class SocketFunction {
|
|
|
513
541
|
}
|
|
514
542
|
}
|
|
515
543
|
|
|
544
|
+
defineSingletonConfig<SocketFunctionConfig>(SocketFunction, "SocketFunction.config", 1, () => ({
|
|
545
|
+
logMessages: false,
|
|
546
|
+
trackMessageSizes: {
|
|
547
|
+
upload: [],
|
|
548
|
+
download: [],
|
|
549
|
+
callTimes: [],
|
|
550
|
+
},
|
|
551
|
+
MAX_MESSAGE_SIZE: 1024 * 1024 * 32,
|
|
552
|
+
HTTP_ETAG_CACHE: false,
|
|
553
|
+
silent: true,
|
|
554
|
+
HTTP_COMPRESS: false,
|
|
555
|
+
COEP: "credentialless",
|
|
556
|
+
COOP: "same-origin",
|
|
557
|
+
TOTAL_CALLS: 0,
|
|
558
|
+
ENABLE_CLIENT_MODE: false,
|
|
559
|
+
WIRE_SERIALIZER: {
|
|
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
|
+
}));
|
|
567
|
+
|
|
516
568
|
declare global {
|
|
517
569
|
var BOOTED_EDGE_NODE: { host: string } | undefined;
|
|
518
570
|
}
|
package/index.d.ts
CHANGED
|
@@ -14,12 +14,15 @@ 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
|
-
|
|
17
|
+
/** The values behind SocketFunction's configuration statics. They are exposed on the class as
|
|
18
|
+
* accessors onto a singleton (see the defineSingletonConfig call at the bottom of this file), so
|
|
19
|
+
* that configuration set through one copy of this package applies to all of them. This interface
|
|
20
|
+
* is the single source of truth for their types - the class declares each one as
|
|
21
|
+
* `declare static X: SocketFunctionConfig["X"]`.
|
|
22
|
+
*/
|
|
23
|
+
export interface SocketFunctionConfig {
|
|
24
|
+
logMessages: boolean;
|
|
25
|
+
trackMessageSizes: {
|
|
23
26
|
upload: ((size: number, nodeId: string) => void)[];
|
|
24
27
|
download: ((size: number, nodeId: string) => void)[];
|
|
25
28
|
callTimes: ((obj: {
|
|
@@ -28,25 +31,49 @@ declare module "socket-function/SocketFunction" {
|
|
|
28
31
|
nodeId: string;
|
|
29
32
|
}) => void)[];
|
|
30
33
|
};
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
34
|
+
MAX_MESSAGE_SIZE: number;
|
|
35
|
+
HTTP_ETAG_CACHE: boolean;
|
|
36
|
+
silent: boolean;
|
|
37
|
+
HTTP_COMPRESS: boolean;
|
|
38
|
+
/** If you have HTTP resources that require cookies you might to set `SocketFunction.COEP = "require-corp"`
|
|
39
|
+
* - Cross-origin-resource-policy.
|
|
40
|
+
* NOTE: This COOP and COEP defaults are required so window.crossOriginIsolated will be true.
|
|
41
|
+
*/
|
|
42
|
+
COEP: string;
|
|
43
|
+
COOP: string;
|
|
44
|
+
TOTAL_CALLS: number;
|
|
45
|
+
ENABLE_CLIENT_MODE: boolean;
|
|
46
|
+
WIRE_SERIALIZER: {
|
|
41
47
|
serialize: (obj: unknown) => MaybePromise<Buffer[]>;
|
|
42
48
|
deserialize: (buffers: Buffer[]) => MaybePromise<unknown>;
|
|
43
49
|
};
|
|
44
50
|
/** 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.
|
|
45
51
|
* VERY useful, allowing us to change global ips to local ones, which short-circuits the router, massively increasing bandwidth and decreasing latency.
|
|
46
52
|
*/
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
53
|
+
GET_ALTERNATE_NODE_IDS: (nodeId: string) => MaybePromise<string[] | undefined>;
|
|
54
|
+
WIRE_WARN_TIME: number;
|
|
55
|
+
/** 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. */
|
|
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"];
|
|
76
|
+
static isClient(): boolean;
|
|
50
77
|
private static onMountCallbacks;
|
|
51
78
|
private static exposedClassesSingleton;
|
|
52
79
|
static get exposedClasses(): Set<string>;
|
|
@@ -849,6 +876,17 @@ declare module "socket-function/src/createSingleton" {
|
|
|
849
876
|
* get()/set() at each access so every copy sees the latest.
|
|
850
877
|
*/
|
|
851
878
|
export declare function createSingleton<T>(name: string, version: string | number, getDefault: () => T): Singleton<T>;
|
|
879
|
+
/** Installs accessors on target for every key of the config object, with the values living in a
|
|
880
|
+
* singleton, so `Target.SOME_SETTING = x` configures every copy of the package instead of only
|
|
881
|
+
* the copy the caller happened to import (which is otherwise decided by module resolution, and
|
|
882
|
+
* so is essentially arbitrary from the caller's perspective).
|
|
883
|
+
* - Declare the properties on the class with `declare static X: Config["X"]`. `declare` emits no
|
|
884
|
+
* field, so there is no own property to shadow the accessors installed here, and taking the
|
|
885
|
+
* types from the config interface keeps the two from drifting apart.
|
|
886
|
+
* - Same versioning rules as createSingleton, except that adding a property is not a shape change:
|
|
887
|
+
* a copy that knows about a property older copies don't backfills it below.
|
|
888
|
+
*/
|
|
889
|
+
export declare function defineSingletonConfig<T extends object>(target: object, name: string, version: string | number, getDefaults: () => T): Singleton<T>;
|
|
852
890
|
|
|
853
891
|
}
|
|
854
892
|
|
package/package.json
CHANGED
|
@@ -9,6 +9,7 @@ import parser from "./JSONLACKS.generated.js";
|
|
|
9
9
|
import { recursiveFreeze } from "../misc";
|
|
10
10
|
import { canHaveChildren } from "../types";
|
|
11
11
|
import { delay } from "../batching";
|
|
12
|
+
import { defineSingletonConfig } from "../createSingleton";
|
|
12
13
|
|
|
13
14
|
const SERIALIZE_OBJECT_BATCH_COUNT = 1000;
|
|
14
15
|
const PARSE_BYTE_CHUNK_SIZE = 1024 * 1024 * 10;
|
|
@@ -45,8 +46,8 @@ export class JSONLACKS {
|
|
|
45
46
|
public static readonly LACKS_KEY = "__JSONLACKS__98cfb4a05fa34d828661cae15b8779ce__";
|
|
46
47
|
|
|
47
48
|
/** If set to true parses non-quoted field names, comments, trailing commas, etc */
|
|
48
|
-
public static EXTENDED_PARSER
|
|
49
|
-
public static IGNORE_MISSING_REFERENCES
|
|
49
|
+
public declare static EXTENDED_PARSER: boolean;
|
|
50
|
+
public declare static IGNORE_MISSING_REFERENCES: boolean;
|
|
50
51
|
|
|
51
52
|
public static stringify(obj: unknown, config?: JSONLACKS_StringifyConfig): string {
|
|
52
53
|
let serialized = JSONLACKS.escapeSpecialObjects(obj, config);
|
|
@@ -299,6 +300,11 @@ export class JSONLACKS {
|
|
|
299
300
|
}
|
|
300
301
|
}
|
|
301
302
|
|
|
303
|
+
defineSingletonConfig(JSONLACKS, "JSONLACKS.config", 1, () => ({
|
|
304
|
+
EXTENDED_PARSER: false,
|
|
305
|
+
IGNORE_MISSING_REFERENCES: false,
|
|
306
|
+
}));
|
|
307
|
+
|
|
302
308
|
async function benchmark() {
|
|
303
309
|
const loops = 1000 * 100;
|
|
304
310
|
let inputs: string[] = [];
|
package/src/callHTTPHandler.ts
CHANGED
|
@@ -13,6 +13,9 @@ import { createSingleton } from "./createSingleton";
|
|
|
13
13
|
// the HTTP handler of any other copy. See createSingleton.
|
|
14
14
|
const defaultHTTPCall = createSingleton("callHTTPHandler.defaultHTTPCall", 1, () => ({ call: undefined as CallType | undefined }));
|
|
15
15
|
|
|
16
|
+
// Statuses where the spec forbids a message body, so we must not write resultBuffer
|
|
17
|
+
const BODYLESS_STATUS_CODES = new Set([204, 205, 304]);
|
|
18
|
+
|
|
16
19
|
export function setDefaultHTTPCall(call: CallType) {
|
|
17
20
|
defaultHTTPCall.get().call = call;
|
|
18
21
|
}
|
|
@@ -253,12 +256,18 @@ export async function httpCallHandler(request: http.IncomingMessage, response: h
|
|
|
253
256
|
|
|
254
257
|
if (headers) {
|
|
255
258
|
for (let headerName in headers) {
|
|
259
|
+
// "status" is the status line, not a header, so don't emit it on the wire
|
|
260
|
+
if (headerName.toLowerCase() === "status") continue;
|
|
256
261
|
response.setHeader(headerName, headers[headerName]);
|
|
257
262
|
}
|
|
258
|
-
let status = headers["status"];
|
|
263
|
+
let status = headers["status"] ?? headers["Status"];
|
|
259
264
|
if (status) {
|
|
260
|
-
|
|
261
|
-
|
|
265
|
+
// Only set statusCode (NOT writeHead), as writeHead locks the header block, which would
|
|
266
|
+
// drop the Content-Type / Content-Length / compression handling below
|
|
267
|
+
response.statusCode = +status;
|
|
268
|
+
if (BODYLESS_STATUS_CODES.has(+status) || +status < 200) {
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
262
271
|
}
|
|
263
272
|
}
|
|
264
273
|
// With nosniff set, an untyped response is unusable to the browser, so results without explicit headers need their actual type (JSON) declared
|
package/src/createSingleton.d.ts
CHANGED
|
@@ -17,3 +17,14 @@ 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
|
+
/** Installs accessors on target for every key of the config object, with the values living in a
|
|
21
|
+
* singleton, so `Target.SOME_SETTING = x` configures every copy of the package instead of only
|
|
22
|
+
* the copy the caller happened to import (which is otherwise decided by module resolution, and
|
|
23
|
+
* so is essentially arbitrary from the caller's perspective).
|
|
24
|
+
* - Declare the properties on the class with `declare static X: Config["X"]`. `declare` emits no
|
|
25
|
+
* field, so there is no own property to shadow the accessors installed here, and taking the
|
|
26
|
+
* types from the config interface keeps the two from drifting apart.
|
|
27
|
+
* - Same versioning rules as createSingleton, except that adding a property is not a shape change:
|
|
28
|
+
* a copy that knows about a property older copies don't backfills it below.
|
|
29
|
+
*/
|
|
30
|
+
export declare function defineSingletonConfig<T extends object>(target: object, name: string, version: string | number, getDefaults: () => T): Singleton<T>;
|
package/src/createSingleton.ts
CHANGED
|
@@ -74,3 +74,40 @@ export function createSingleton<T>(
|
|
|
74
74
|
},
|
|
75
75
|
};
|
|
76
76
|
}
|
|
77
|
+
|
|
78
|
+
/** Installs accessors on target for every key of the config object, with the values living in a
|
|
79
|
+
* singleton, so `Target.SOME_SETTING = x` configures every copy of the package instead of only
|
|
80
|
+
* the copy the caller happened to import (which is otherwise decided by module resolution, and
|
|
81
|
+
* so is essentially arbitrary from the caller's perspective).
|
|
82
|
+
* - Declare the properties on the class with `declare static X: Config["X"]`. `declare` emits no
|
|
83
|
+
* field, so there is no own property to shadow the accessors installed here, and taking the
|
|
84
|
+
* types from the config interface keeps the two from drifting apart.
|
|
85
|
+
* - Same versioning rules as createSingleton, except that adding a property is not a shape change:
|
|
86
|
+
* a copy that knows about a property older copies don't backfills it below.
|
|
87
|
+
*/
|
|
88
|
+
export function defineSingletonConfig<T extends object>(
|
|
89
|
+
target: object,
|
|
90
|
+
name: string,
|
|
91
|
+
version: string | number,
|
|
92
|
+
getDefaults: () => T,
|
|
93
|
+
): Singleton<T> {
|
|
94
|
+
const defaults = getDefaults();
|
|
95
|
+
const singleton = createSingleton<T>(name, version, () => defaults);
|
|
96
|
+
const values = singleton.get() as { [key: string]: unknown };
|
|
97
|
+
for (const key of Object.keys(defaults)) {
|
|
98
|
+
if (!(key in values)) {
|
|
99
|
+
values[key] = (defaults as { [key: string]: unknown })[key];
|
|
100
|
+
}
|
|
101
|
+
Object.defineProperty(target, key, {
|
|
102
|
+
get() {
|
|
103
|
+
return (singleton.get() as { [key: string]: unknown })[key];
|
|
104
|
+
},
|
|
105
|
+
set(value: unknown) {
|
|
106
|
+
(singleton.get() as { [key: string]: unknown })[key] = value;
|
|
107
|
+
},
|
|
108
|
+
enumerable: true,
|
|
109
|
+
configurable: true,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
return singleton;
|
|
113
|
+
}
|