socket-function 0.8.16 → 0.8.18
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/SocketFunctionTypes.ts
CHANGED
|
@@ -23,6 +23,10 @@ export type SocketExposedInterfaceClass = {
|
|
|
23
23
|
};
|
|
24
24
|
export interface SocketExposedShape<ExposedType extends SocketExposedInterface = SocketExposedInterface, CallContext extends CallContextType = CallContextType> {
|
|
25
25
|
[functionName: string]: {
|
|
26
|
+
/** Indicates with the same input, we give the same output, forever,
|
|
27
|
+
* independent of code changes. This only works for data storage.
|
|
28
|
+
*/
|
|
29
|
+
dataImmutable?: boolean;
|
|
26
30
|
hooks?: SocketFunctionHook<ExposedType, CallContext>[];
|
|
27
31
|
clientHooks?: SocketFunctionClientHook<ExposedType, CallContext>[];
|
|
28
32
|
};
|
|
@@ -34,9 +34,9 @@ export function promiseToObservable<T>(promise: Promise<T>): { value: T | undefi
|
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
export function asyncObservable<Output,
|
|
38
|
-
(
|
|
39
|
-
invalidate(
|
|
37
|
+
export function asyncObservable<Output, Args extends any[]>(maxCount: number, getValue: (...args: Args) => Promise<Output>): {
|
|
38
|
+
(...args: Args): Output | undefined;
|
|
39
|
+
invalidate(...args: Args): void;
|
|
40
40
|
invalidateAll(): void;
|
|
41
41
|
} {
|
|
42
42
|
let invalidateAllSeqNum = observable({ seqNum: 1 }, undefined, { deep: false, proxy: false });
|
|
@@ -46,8 +46,8 @@ export function asyncObservable<Output, Key>(maxCount: number, getValue: (key: K
|
|
|
46
46
|
seqNum: { value: number };
|
|
47
47
|
}>();
|
|
48
48
|
|
|
49
|
-
get["invalidate"] = (
|
|
50
|
-
let hash = JSON.stringify(
|
|
49
|
+
get["invalidate"] = (...args: Args) => {
|
|
50
|
+
let hash = JSON.stringify(args);
|
|
51
51
|
let value = values.get(hash);
|
|
52
52
|
if (!value) return;
|
|
53
53
|
values.delete(hash);
|
|
@@ -59,9 +59,9 @@ export function asyncObservable<Output, Key>(maxCount: number, getValue: (key: K
|
|
|
59
59
|
values.clear();
|
|
60
60
|
invalidateAllSeqNum.seqNum++;
|
|
61
61
|
};
|
|
62
|
-
function get(
|
|
62
|
+
function get(...args: Args) {
|
|
63
63
|
|
|
64
|
-
let hash = JSON.stringify(
|
|
64
|
+
let hash = JSON.stringify(args);
|
|
65
65
|
let value = values.get(hash);
|
|
66
66
|
if (!value) {
|
|
67
67
|
|
|
@@ -79,7 +79,7 @@ export function asyncObservable<Output, Key>(maxCount: number, getValue: (key: K
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
// We call inside another function so that synchronous errors still get wrapped in the observable
|
|
82
|
-
let valueObs = promiseToObservable((async () => getValue(
|
|
82
|
+
let valueObs = promiseToObservable((async () => getValue(...args))());
|
|
83
83
|
value = {
|
|
84
84
|
valueObs,
|
|
85
85
|
seqNum: observable({ value: 1 }, undefined, { deep: false, proxy: false }),
|
package/package.json
CHANGED
package/src/callHTTPHandler.ts
CHANGED
|
@@ -3,7 +3,7 @@ import http from "http";
|
|
|
3
3
|
import net from "net";
|
|
4
4
|
import tls from "tls";
|
|
5
5
|
import { CallerContext, CallType, NetworkLocation, setCertInfo } from "../SocketFunctionTypes";
|
|
6
|
-
import { performLocalCall } from "./callManager";
|
|
6
|
+
import { isDataImmutable, performLocalCall } from "./callManager";
|
|
7
7
|
import { getNodeIdRaw } from "./nodeAuthentication";
|
|
8
8
|
import debugbreak from "debugbreak";
|
|
9
9
|
import * as cookie from "cookie";
|
|
@@ -150,6 +150,22 @@ export async function httpCallHandler(request: http.IncomingMessage, response: h
|
|
|
150
150
|
args,
|
|
151
151
|
};
|
|
152
152
|
|
|
153
|
+
if (isDataImmutable(call)) {
|
|
154
|
+
/** ETag cache, BUT, hashes only the input. Only valid for fully immutable resources
|
|
155
|
+
* (ex data storage, as any endpoints that run code could have that code change).
|
|
156
|
+
* - Shouldn't be needed, but I am seeing chrome fail to cache a lot of requests,
|
|
157
|
+
* which could cost us multiple dollars in server costs from atlas.
|
|
158
|
+
*/
|
|
159
|
+
response.setHeader("cache-control", "public, max-age=15206400, immutable");
|
|
160
|
+
let hash = sha256Hash(Buffer.from(JSON.stringify(call)));
|
|
161
|
+
response.setHeader("ETag", hash);
|
|
162
|
+
if (request.headers["if-none-match"] === hash) {
|
|
163
|
+
response.writeHead(304);
|
|
164
|
+
console.log(`CACHED Immutable HTTP response (${request.method}) ${url}`);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
153
169
|
let result = await performLocalCall({
|
|
154
170
|
caller,
|
|
155
171
|
call
|
|
@@ -174,9 +190,9 @@ export async function httpCallHandler(request: http.IncomingMessage, response: h
|
|
|
174
190
|
|
|
175
191
|
|
|
176
192
|
// NOTE: Our ETag caching is only to reduce data sent on the wire, we evaluate the calls
|
|
177
|
-
// every time (so it is strictly a wire cache, not computation cache)
|
|
193
|
+
// every time (so it is strictly a wire cache, not a computation cache)
|
|
178
194
|
if (SocketFunction.httpETagCache) {
|
|
179
|
-
response.setHeader("cache-control", "private,
|
|
195
|
+
response.setHeader("cache-control", "private, max-age=0, must-revalidate");
|
|
180
196
|
let hash = sha256Hash(resultBuffer);
|
|
181
197
|
response.setHeader("ETag", hash);
|
|
182
198
|
if (request.headers["if-none-match"] === hash) {
|
package/src/callManager.ts
CHANGED
|
@@ -53,6 +53,10 @@ export async function performLocalCall(
|
|
|
53
53
|
return await result;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
export function isDataImmutable(call: CallType) {
|
|
57
|
+
return !!classes[call.classGuid]?.shape[call.functionName]?.dataImmutable;
|
|
58
|
+
}
|
|
59
|
+
|
|
56
60
|
export function registerClass(classGuid: string, controller: SocketExposedInterface, shape: SocketExposedShape) {
|
|
57
61
|
if (classes[classGuid]) {
|
|
58
62
|
throw new Error(`Class ${classGuid} already registered`);
|