querysub 0.437.0 → 0.439.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/.eslintrc.js +50 -50
- package/bin/deploy.js +0 -0
- package/bin/function.js +0 -0
- package/bin/server.js +0 -0
- package/costsBenefits.txt +115 -115
- package/deploy.ts +2 -2
- package/package.json +2 -2
- package/spec.txt +1192 -1192
- package/src/-a-archives/archives.ts +202 -202
- package/src/-a-archives/archivesDisk.ts +454 -454
- package/src/-a-auth/certs.ts +540 -540
- package/src/-a-auth/node-forge-ed25519.d.ts +16 -16
- package/src/-b-authorities/dnsAuthority.ts +138 -138
- package/src/-c-identity/IdentityController.ts +258 -258
- package/src/-d-trust/NetworkTrust2.ts +180 -180
- package/src/-e-certs/EdgeCertController.ts +252 -252
- package/src/-e-certs/certAuthority.ts +201 -201
- package/src/-f-node-discovery/NodeDiscovery.ts +640 -640
- package/src/-g-core-values/NodeCapabilities.ts +200 -200
- package/src/-h-path-value-serialize/stringSerializer.ts +175 -175
- package/src/0-path-value-core/PathValueCommitter.ts +468 -468
- package/src/0-path-value-core/PathValueController.ts +0 -2
- package/src/0-path-value-core/archiveLocks/archiveSnapshots.ts +37 -1
- package/src/0-path-value-core/pathValueCore.ts +12 -0
- package/src/2-proxy/PathValueProxyWatcher.ts +2542 -2542
- package/src/2-proxy/TransactionDelayer.ts +94 -94
- package/src/2-proxy/pathDatabaseProxyBase.ts +36 -36
- package/src/2-proxy/pathValueProxy.ts +159 -159
- package/src/3-path-functions/PathFunctionRunner.ts +24 -13
- package/src/3-path-functions/PathFunctionRunnerMain.ts +87 -87
- package/src/3-path-functions/pathFunctionLoader.ts +516 -516
- package/src/3-path-functions/tests/rejectTest.ts +76 -76
- package/src/4-deploy/deployCheck.ts +6 -6
- package/src/4-dom/css.tsx +29 -29
- package/src/4-dom/cssTypes.d.ts +211 -211
- package/src/4-dom/qreact.tsx +2799 -2799
- package/src/4-dom/qreactTest.tsx +410 -410
- package/src/4-querysub/permissions.ts +335 -335
- package/src/4-querysub/querysubPrediction.ts +483 -483
- package/src/5-diagnostics/qreactDebug.tsx +400 -346
- package/src/TestController.ts +34 -34
- package/src/bits.ts +104 -104
- package/src/buffers.ts +69 -69
- package/src/diagnostics/ActionsHistory.ts +57 -57
- package/src/diagnostics/PathDistributionInfo.tsx +9 -1
- package/src/diagnostics/listenOnDebugger.ts +71 -71
- package/src/diagnostics/logs/IndexedLogs/BufferUnitIndex.ts +1 -1
- package/src/diagnostics/logs/diskLogger.ts +6 -0
- package/src/diagnostics/misc-pages/SnapshotViewer.tsx +78 -1
- package/src/diagnostics/periodic.ts +111 -111
- package/src/diagnostics/trackResources.ts +91 -91
- package/src/diagnostics/watchdog.ts +120 -120
- package/src/errors.ts +133 -133
- package/src/forceProduction.ts +2 -2
- package/src/fs.ts +80 -80
- package/src/functional/diff.ts +857 -857
- package/src/functional/promiseCache.ts +78 -78
- package/src/functional/random.ts +8 -8
- package/src/functional/stats.ts +60 -60
- package/src/heapDumps.ts +665 -665
- package/src/https.ts +1 -1
- package/src/library-components/AspectSizedComponent.tsx +87 -87
- package/src/library-components/ButtonSelector.tsx +64 -64
- package/src/library-components/DropdownCustom.tsx +150 -150
- package/src/library-components/DropdownSelector.tsx +31 -31
- package/src/library-components/InlinePopup.tsx +66 -66
- package/src/library-components/uncaughtToast.tsx +2 -0
- package/src/misc/color.ts +29 -29
- package/src/misc/hash.ts +83 -83
- package/src/misc/ipPong.js +13 -13
- package/src/misc/networking.ts +1 -1
- package/src/misc/random.ts +44 -44
- package/src/misc.ts +196 -196
- package/src/path.ts +255 -255
- package/src/persistentLocalStore.ts +41 -41
- package/src/promise.ts +14 -14
- package/src/storage/fileSystemPointer.ts +71 -71
- package/src/test/heapProcess.ts +35 -35
- package/src/zip.ts +15 -15
- package/tsconfig.json +26 -26
- package/yarnSpec.txt +56 -56
|
@@ -1,72 +1,72 @@
|
|
|
1
|
-
import { isNode, list } from "socket-function/src/misc";
|
|
2
|
-
import inspector from "inspector";
|
|
3
|
-
import fs from "fs";
|
|
4
|
-
import { getSubFolder } from "../fs";
|
|
5
|
-
import { SocketFunction } from "socket-function/SocketFunction";
|
|
6
|
-
import debugbreak from "debugbreak";
|
|
7
|
-
import http from "http";
|
|
8
|
-
import { lazy } from "socket-function/src/caching";
|
|
9
|
-
|
|
10
|
-
let devToolsUrl = "";
|
|
11
|
-
|
|
12
|
-
const listenOnDebugger = lazy(function listenOnDebugger() {
|
|
13
|
-
if (devToolsUrl) return;
|
|
14
|
-
if (!isNode()) return;
|
|
15
|
-
// IMPORTANT! NEVER use the built-in port. This adds a tiny bit of security. Not much,
|
|
16
|
-
// as port scanning is easier (there are only 65K ports!), but... at least port scanning
|
|
17
|
-
// is loud, so if a site port scans all of it's users it will probably get flagged by
|
|
18
|
-
// chrome fairly quickly as being malicious.
|
|
19
|
-
let url = baseGetInspectorUrl();
|
|
20
|
-
|
|
21
|
-
devToolsUrl = `devtools://devtools/bundled/inspector.html?experiments=true&v8only=true&${url.replace("://", "=")}`;
|
|
22
|
-
devToolsUrl = devToolsUrl.replace("devtools://devtools/bundled", "https://notdevtools.com/devtools");
|
|
23
|
-
console.log(`Debugger listening on ${devToolsUrl}`);
|
|
24
|
-
|
|
25
|
-
const ext = ".url";
|
|
26
|
-
const debuggerFolder = getSubFolder("debugger");
|
|
27
|
-
let otherDebuggers = fs.readdirSync(debuggerFolder);
|
|
28
|
-
otherDebuggers = otherDebuggers.filter(x => x.endsWith(ext));
|
|
29
|
-
// Delete all files for processes that no longer exist
|
|
30
|
-
for (let other of otherDebuggers) {
|
|
31
|
-
let pid = +other.split("-")[0];
|
|
32
|
-
let ppid = +other.split("-")[1];
|
|
33
|
-
if (!doesProcessExist(pid) || !doesProcessExist(ppid)) {
|
|
34
|
-
try {
|
|
35
|
-
fs.unlinkSync(debuggerFolder + other);
|
|
36
|
-
} catch { }
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
export function getDebuggerUrl() {
|
|
41
|
-
// NOTE: Very unfortunate, as this means that if a developer is debugging a server for a bit,
|
|
42
|
-
// the inspector is left open. It's kind of hard to tell when they are done though.
|
|
43
|
-
// - This is still somewhat secure, as only localhost can connect, but... having local network
|
|
44
|
-
// accessing turning into remote execution access isn't great (especially for developers,
|
|
45
|
-
// who could then get pwned just by clicking on a link).
|
|
46
|
-
// TODO: The security of this can be improved, see NodeViewer.tsx:getExternalInspectURL
|
|
47
|
-
listenOnDebugger();
|
|
48
|
-
return devToolsUrl;
|
|
49
|
-
}
|
|
50
|
-
function doesProcessExist(pid: number) {
|
|
51
|
-
try {
|
|
52
|
-
process.kill(pid, 0);
|
|
53
|
-
return true;
|
|
54
|
-
} catch {
|
|
55
|
-
return false;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
function baseGetInspectorUrl() {
|
|
59
|
-
function getNextPort() {
|
|
60
|
-
return 49152 + ~~((65535 - 49152) * Math.random());
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
while (true) {
|
|
64
|
-
let url = inspector.url();
|
|
65
|
-
if (url) return url;
|
|
66
|
-
try {
|
|
67
|
-
inspector.open(getNextPort());
|
|
68
|
-
} catch {
|
|
69
|
-
continue;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
1
|
+
import { isNode, list } from "socket-function/src/misc";
|
|
2
|
+
import inspector from "inspector";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import { getSubFolder } from "../fs";
|
|
5
|
+
import { SocketFunction } from "socket-function/SocketFunction";
|
|
6
|
+
import debugbreak from "debugbreak";
|
|
7
|
+
import http from "http";
|
|
8
|
+
import { lazy } from "socket-function/src/caching";
|
|
9
|
+
|
|
10
|
+
let devToolsUrl = "";
|
|
11
|
+
|
|
12
|
+
const listenOnDebugger = lazy(function listenOnDebugger() {
|
|
13
|
+
if (devToolsUrl) return;
|
|
14
|
+
if (!isNode()) return;
|
|
15
|
+
// IMPORTANT! NEVER use the built-in port. This adds a tiny bit of security. Not much,
|
|
16
|
+
// as port scanning is easier (there are only 65K ports!), but... at least port scanning
|
|
17
|
+
// is loud, so if a site port scans all of it's users it will probably get flagged by
|
|
18
|
+
// chrome fairly quickly as being malicious.
|
|
19
|
+
let url = baseGetInspectorUrl();
|
|
20
|
+
|
|
21
|
+
devToolsUrl = `devtools://devtools/bundled/inspector.html?experiments=true&v8only=true&${url.replace("://", "=")}`;
|
|
22
|
+
devToolsUrl = devToolsUrl.replace("devtools://devtools/bundled", "https://notdevtools.com/devtools");
|
|
23
|
+
console.log(`Debugger listening on ${devToolsUrl}`);
|
|
24
|
+
|
|
25
|
+
const ext = ".url";
|
|
26
|
+
const debuggerFolder = getSubFolder("debugger");
|
|
27
|
+
let otherDebuggers = fs.readdirSync(debuggerFolder);
|
|
28
|
+
otherDebuggers = otherDebuggers.filter(x => x.endsWith(ext));
|
|
29
|
+
// Delete all files for processes that no longer exist
|
|
30
|
+
for (let other of otherDebuggers) {
|
|
31
|
+
let pid = +other.split("-")[0];
|
|
32
|
+
let ppid = +other.split("-")[1];
|
|
33
|
+
if (!doesProcessExist(pid) || !doesProcessExist(ppid)) {
|
|
34
|
+
try {
|
|
35
|
+
fs.unlinkSync(debuggerFolder + other);
|
|
36
|
+
} catch { }
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
export function getDebuggerUrl() {
|
|
41
|
+
// NOTE: Very unfortunate, as this means that if a developer is debugging a server for a bit,
|
|
42
|
+
// the inspector is left open. It's kind of hard to tell when they are done though.
|
|
43
|
+
// - This is still somewhat secure, as only localhost can connect, but... having local network
|
|
44
|
+
// accessing turning into remote execution access isn't great (especially for developers,
|
|
45
|
+
// who could then get pwned just by clicking on a link).
|
|
46
|
+
// TODO: The security of this can be improved, see NodeViewer.tsx:getExternalInspectURL
|
|
47
|
+
listenOnDebugger();
|
|
48
|
+
return devToolsUrl;
|
|
49
|
+
}
|
|
50
|
+
function doesProcessExist(pid: number) {
|
|
51
|
+
try {
|
|
52
|
+
process.kill(pid, 0);
|
|
53
|
+
return true;
|
|
54
|
+
} catch {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function baseGetInspectorUrl() {
|
|
59
|
+
function getNextPort() {
|
|
60
|
+
return 49152 + ~~((65535 - 49152) * Math.random());
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
while (true) {
|
|
64
|
+
let url = inspector.url();
|
|
65
|
+
if (url) return url;
|
|
66
|
+
try {
|
|
67
|
+
inspector.open(getNextPort());
|
|
68
|
+
} catch {
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
72
|
}
|
|
@@ -276,7 +276,7 @@ export class BufferUnitIndex {
|
|
|
276
276
|
// Only inaccurate if unique units approaches or exceeds 65k
|
|
277
277
|
return uniqueCount * 2;
|
|
278
278
|
}
|
|
279
|
-
}, `
|
|
279
|
+
}, `estimatedUniqueUnits`);
|
|
280
280
|
|
|
281
281
|
// Step 3: Calculate hash table size and mask
|
|
282
282
|
const { hashTableCapacity, mask } = measureBlock(() => {
|
|
@@ -21,6 +21,11 @@ if (isNode()) {
|
|
|
21
21
|
}));
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
let loggingToDiskDisabled = false;
|
|
25
|
+
export function disableLoggingToDisk() {
|
|
26
|
+
loggingToDiskDisabled = true;
|
|
27
|
+
}
|
|
28
|
+
|
|
24
29
|
// NOTE: When logging we spread objects. If we encounter strings, we set the field `param${index}`
|
|
25
30
|
export type LogDatum = Record<string, unknown> & {
|
|
26
31
|
time: number;
|
|
@@ -109,6 +114,7 @@ const logDiskDontShim = logDisk;
|
|
|
109
114
|
/** @deprecated, Don't call this directly, call console info instead, which our shim will prevent from logging to the console, but it will still call logDisk. */
|
|
110
115
|
export function logDisk(type: "log" | "warn" | "info" | "error", ...args: unknown[]) {
|
|
111
116
|
if (!isNode()) return;
|
|
117
|
+
if (loggingToDiskDisabled) return;
|
|
112
118
|
try {
|
|
113
119
|
if (args.length === 0) return;
|
|
114
120
|
let logType = args.find(x => typeof x === "string") as string | undefined;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
module.allowclient = true;
|
|
2
2
|
|
|
3
|
-
import { ArchiveSnapshotOverview, ArchiveSnapshotRead, getSnapshot, getSnapshotList, loadSnapshot, saveSnapshot } from "../../0-path-value-core/archiveLocks/archiveSnapshots";
|
|
3
|
+
import { ArchiveSnapshotOverview, ArchiveSnapshotRead, downloadSnapshot, getSnapshot, getSnapshotList, loadSnapshot, saveSnapshot, uploadSnapshot } from "../../0-path-value-core/archiveLocks/archiveSnapshots";
|
|
4
4
|
import { qreact } from "../../4-dom/qreact";
|
|
5
5
|
import { SocketFunction } from "socket-function/SocketFunction";
|
|
6
6
|
import { getBrowserUrlNode } from "../../-f-node-discovery/NodeDiscovery";
|
|
@@ -30,6 +30,8 @@ export class SnapshotViewer extends qreact.Component {
|
|
|
30
30
|
// file => true
|
|
31
31
|
expanded: t.lookup(t.boolean),
|
|
32
32
|
saving: t.boolean(false),
|
|
33
|
+
uploading: t.boolean(false),
|
|
34
|
+
downloading: t.lookup(t.boolean),
|
|
33
35
|
});
|
|
34
36
|
render() {
|
|
35
37
|
let controller = SnapshotViewerSynced(getBrowserUrlNode());
|
|
@@ -40,6 +42,11 @@ export class SnapshotViewer extends qreact.Component {
|
|
|
40
42
|
return (
|
|
41
43
|
<div class={css.pad2(10).vbox(10)}>
|
|
42
44
|
<h1>Snapshots are taken before every change. "zombie" means the file exists, but it has no confirm (so it will be ignored).</h1>
|
|
45
|
+
<div class={css.hbox(10)}>
|
|
46
|
+
<Button onClick={() => triggerSnapshotUpload(this)}>
|
|
47
|
+
{this.state.uploading && "Uploading..." || "Upload Snapshot"}
|
|
48
|
+
</Button>
|
|
49
|
+
</div>
|
|
43
50
|
<Table
|
|
44
51
|
rows={snapshotList}
|
|
45
52
|
columns={{
|
|
@@ -68,6 +75,9 @@ export class SnapshotViewer extends qreact.Component {
|
|
|
68
75
|
{this.state.saving ? "Saving..." : "Save"}
|
|
69
76
|
</Button>
|
|
70
77
|
}
|
|
78
|
+
<Button onClick={() => downloadSnapshotFile(this, file)}>
|
|
79
|
+
{this.state.downloading[file] && "Downloading..." || "Download"}
|
|
80
|
+
</Button>
|
|
71
81
|
</div>;
|
|
72
82
|
}
|
|
73
83
|
}
|
|
@@ -142,6 +152,61 @@ export class SnapshotViewer extends qreact.Component {
|
|
|
142
152
|
}
|
|
143
153
|
}
|
|
144
154
|
|
|
155
|
+
async function downloadSnapshotFile(component: SnapshotViewer, file: string) {
|
|
156
|
+
if (component.state.downloading[file]) return;
|
|
157
|
+
Querysub.localCommit(() => {
|
|
158
|
+
component.state.downloading[file] = true;
|
|
159
|
+
});
|
|
160
|
+
try {
|
|
161
|
+
let buffer = await SnapshotViewerController.nodes[getBrowserUrlNode()].downloadSnapshot(file);
|
|
162
|
+
let blob = new Blob([buffer]);
|
|
163
|
+
let url = URL.createObjectURL(blob);
|
|
164
|
+
let anchor = document.createElement("a");
|
|
165
|
+
anchor.href = url;
|
|
166
|
+
anchor.download = file + ".cborx";
|
|
167
|
+
if (file === "live") {
|
|
168
|
+
anchor.download = `${formatDateTime(Date.now())}.cborx`;
|
|
169
|
+
}
|
|
170
|
+
document.body.appendChild(anchor);
|
|
171
|
+
anchor.click();
|
|
172
|
+
document.body.removeChild(anchor);
|
|
173
|
+
URL.revokeObjectURL(url);
|
|
174
|
+
} catch (e: any) {
|
|
175
|
+
console.error(red(`Failed to download snapshot ${file}: ${e.stack}`));
|
|
176
|
+
} finally {
|
|
177
|
+
Querysub.localCommit(() => {
|
|
178
|
+
delete component.state.downloading[file];
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async function triggerSnapshotUpload(component: SnapshotViewer) {
|
|
184
|
+
if (component.state.uploading) return;
|
|
185
|
+
let input = document.createElement("input");
|
|
186
|
+
input.type = "file";
|
|
187
|
+
input.accept = ".cborx";
|
|
188
|
+
input.onchange = async () => {
|
|
189
|
+
let selected = input.files?.[0];
|
|
190
|
+
if (!selected) return;
|
|
191
|
+
Querysub.localCommit(() => {
|
|
192
|
+
component.state.uploading = true;
|
|
193
|
+
});
|
|
194
|
+
try {
|
|
195
|
+
let arrayBuffer = await selected.arrayBuffer();
|
|
196
|
+
let buffer = Buffer.from(arrayBuffer);
|
|
197
|
+
await SnapshotViewerController.nodes[getBrowserUrlNode()].uploadSnapshot(buffer);
|
|
198
|
+
console.log(green("Uploaded snapshot"));
|
|
199
|
+
} catch (e: any) {
|
|
200
|
+
console.error(red(`Failed to upload snapshot: ${e.stack}`));
|
|
201
|
+
} finally {
|
|
202
|
+
Querysub.localCommit(() => {
|
|
203
|
+
component.state.uploading = false;
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
input.click();
|
|
208
|
+
}
|
|
209
|
+
|
|
145
210
|
async function seriousConfirm(config: {
|
|
146
211
|
message: string;
|
|
147
212
|
}): Promise<boolean> {
|
|
@@ -192,6 +257,14 @@ class SnapshotViewerControllerBase {
|
|
|
192
257
|
let files = await getSnapshot("live");
|
|
193
258
|
await saveSnapshot({ files: files.files.map(x => x.file) });
|
|
194
259
|
}
|
|
260
|
+
|
|
261
|
+
public async downloadSnapshot(snapshotFile: string | "live"): Promise<Buffer> {
|
|
262
|
+
return await downloadSnapshot(snapshotFile);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
public async uploadSnapshot(buffer: Buffer): Promise<ArchiveSnapshotOverview> {
|
|
266
|
+
return await uploadSnapshot(buffer);
|
|
267
|
+
}
|
|
195
268
|
}
|
|
196
269
|
|
|
197
270
|
export const SnapshotViewerController = SocketFunction.register(
|
|
@@ -202,6 +275,9 @@ export const SnapshotViewerController = SocketFunction.register(
|
|
|
202
275
|
loadSnapshot: {},
|
|
203
276
|
getSnapshot: {},
|
|
204
277
|
saveLiveSnapshot: {},
|
|
278
|
+
downloadSnapshot: {},
|
|
279
|
+
uploadSnapshot: {},
|
|
280
|
+
example: {}
|
|
205
281
|
}),
|
|
206
282
|
() => ({
|
|
207
283
|
hooks: [assertIsManagementUser],
|
|
@@ -217,5 +293,6 @@ const SnapshotViewerSynced = getSyncedController(SnapshotViewerController, {
|
|
|
217
293
|
},
|
|
218
294
|
writes: {
|
|
219
295
|
saveLiveSnapshot: ["snapshots"],
|
|
296
|
+
uploadSnapshot: ["snapshots"],
|
|
220
297
|
}
|
|
221
298
|
});
|
|
@@ -1,112 +1,112 @@
|
|
|
1
|
-
import { delay, runInfinitePoll, runInfinitePollCallAtStart, shutdownPolling } from "socket-function/src/batching";
|
|
2
|
-
import { isNode, timeInMinute } from "socket-function/src/misc";
|
|
3
|
-
import { logErrors, timeoutToError } from "../errors";
|
|
4
|
-
import debugbreak from "debugbreak";
|
|
5
|
-
import { nodeDiscoveryShutdown } from "../-f-node-discovery/NodeDiscovery";
|
|
6
|
-
import { authorityStorage } from "../0-path-value-core/pathValueCore";
|
|
7
|
-
import { red } from "socket-function/src/formatting/logColors";
|
|
8
|
-
|
|
9
|
-
// Import querysub, so all the hooks we use will exist at some point
|
|
10
|
-
setImmediate(async () => {
|
|
11
|
-
await import("../4-querysub/QuerysubController");
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
let periodicFncs: (() => void)[] = [];
|
|
15
|
-
export function registerPeriodic(fnc: () => void) {
|
|
16
|
-
periodicFncs.push(fnc);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
let shutdownHandlers: (() => Promise<void>)[] = [];
|
|
20
|
-
// NOTE: Won't be always called, but at least makes it possible to gracefully shutdown.
|
|
21
|
-
export function registerShutdownHandler(fnc: () => Promise<void>) {
|
|
22
|
-
shutdownHandlers.push(fnc);
|
|
23
|
-
}
|
|
24
|
-
let preshutdownHandlers: (() => Promise<void>)[] = [];
|
|
25
|
-
export function registerPreshutdownHandler(fnc: () => Promise<void>) {
|
|
26
|
-
preshutdownHandlers.push(fnc);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function logAll() {
|
|
30
|
-
for (let fnc of periodicFncs) {
|
|
31
|
-
fnc();
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
logErrors(runInfinitePollCallAtStart(timeInMinute * 5, logAll));
|
|
36
|
-
|
|
37
|
-
let shuttingDown = false;
|
|
38
|
-
export async function shutdown() {
|
|
39
|
-
if (shuttingDown) {
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
console.log(red("Starting shutdown"));
|
|
43
|
-
shuttingDown = true;
|
|
44
|
-
const { authorityStorage } = await import("../0-path-value-core/pathValueCore");
|
|
45
|
-
try {
|
|
46
|
-
await Promise.allSettled([
|
|
47
|
-
...preshutdownHandlers,
|
|
48
|
-
].map(fnc => timeoutToError(timeInMinute, fnc(), () => new Error(`Preshutdown handler ${fnc.name} timed out`))));
|
|
49
|
-
} catch (e) {
|
|
50
|
-
console.log(`Error on preshutdown handlers`, e);
|
|
51
|
-
}
|
|
52
|
-
try {
|
|
53
|
-
await Promise.allSettled([
|
|
54
|
-
function authorityStorageShutdown() { return authorityStorage.onShutdown(); },
|
|
55
|
-
nodeDiscoveryShutdown,
|
|
56
|
-
shutdownPolling,
|
|
57
|
-
...shutdownHandlers,
|
|
58
|
-
].map(fnc => timeoutToError(timeInMinute, fnc(), () => new Error(`Shutdown handler ${fnc.name} timed out`))));
|
|
59
|
-
} catch (e) {
|
|
60
|
-
console.log("Error on shutdown", e);
|
|
61
|
-
}
|
|
62
|
-
// Wait to allow any logged errors to hopefully be written somewhere?
|
|
63
|
-
await delay(2000);
|
|
64
|
-
process.exit();
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// IMPORTANT! Yarn detaches the processes, so they keep running when you ctrl+c, even though the shell shows back up. We can fix this by using `node -r ./node_modules/typenode/index.js ./test.ts`. However, it's probably fine, as we still run the shutdown code, it's just that the manager doesn't know if we've shutdown or not.
|
|
68
|
-
if (isNode()) {
|
|
69
|
-
let lineBuffer = "";
|
|
70
|
-
process.stdin.on("data", data => {
|
|
71
|
-
lineBuffer += data.toString();
|
|
72
|
-
let lines = lineBuffer.split("\r");
|
|
73
|
-
lineBuffer = lines.pop()!;
|
|
74
|
-
for (let line of lines) {
|
|
75
|
-
if (line === "SHUTDOWN_NOW_MULTIRUN") {
|
|
76
|
-
logErrors(shutdown());
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (data.toString().includes("\r")) {
|
|
81
|
-
logAll();
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
// NOTE: This extra code is required to actual capture ctrl+c
|
|
87
|
-
if (process.platform === "win32") {
|
|
88
|
-
var rl = require("readline").createInterface({
|
|
89
|
-
input: process.stdin,
|
|
90
|
-
output: process.stdout
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
rl.on("SIGINT", function () {
|
|
94
|
-
process.emit("SIGINT");
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
let killCount = 0;
|
|
98
|
-
function doShutdown() {
|
|
99
|
-
killCount++;
|
|
100
|
-
console.log(`Caught interrupt signal. Attempt number ${killCount}`);
|
|
101
|
-
if (killCount >= 3) {
|
|
102
|
-
console.log("Force exit");
|
|
103
|
-
process.exit();
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
logErrors(shutdown());
|
|
107
|
-
}
|
|
108
|
-
process.on("SIGINT", doShutdown);
|
|
109
|
-
|
|
110
|
-
}
|
|
111
|
-
(globalThis as any).logAll = logAll;
|
|
1
|
+
import { delay, runInfinitePoll, runInfinitePollCallAtStart, shutdownPolling } from "socket-function/src/batching";
|
|
2
|
+
import { isNode, timeInMinute } from "socket-function/src/misc";
|
|
3
|
+
import { logErrors, timeoutToError } from "../errors";
|
|
4
|
+
import debugbreak from "debugbreak";
|
|
5
|
+
import { nodeDiscoveryShutdown } from "../-f-node-discovery/NodeDiscovery";
|
|
6
|
+
import { authorityStorage } from "../0-path-value-core/pathValueCore";
|
|
7
|
+
import { red } from "socket-function/src/formatting/logColors";
|
|
8
|
+
|
|
9
|
+
// Import querysub, so all the hooks we use will exist at some point
|
|
10
|
+
setImmediate(async () => {
|
|
11
|
+
await import("../4-querysub/QuerysubController");
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
let periodicFncs: (() => void)[] = [];
|
|
15
|
+
export function registerPeriodic(fnc: () => void) {
|
|
16
|
+
periodicFncs.push(fnc);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let shutdownHandlers: (() => Promise<void>)[] = [];
|
|
20
|
+
// NOTE: Won't be always called, but at least makes it possible to gracefully shutdown.
|
|
21
|
+
export function registerShutdownHandler(fnc: () => Promise<void>) {
|
|
22
|
+
shutdownHandlers.push(fnc);
|
|
23
|
+
}
|
|
24
|
+
let preshutdownHandlers: (() => Promise<void>)[] = [];
|
|
25
|
+
export function registerPreshutdownHandler(fnc: () => Promise<void>) {
|
|
26
|
+
preshutdownHandlers.push(fnc);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function logAll() {
|
|
30
|
+
for (let fnc of periodicFncs) {
|
|
31
|
+
fnc();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
logErrors(runInfinitePollCallAtStart(timeInMinute * 5, logAll));
|
|
36
|
+
|
|
37
|
+
let shuttingDown = false;
|
|
38
|
+
export async function shutdown() {
|
|
39
|
+
if (shuttingDown) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
console.log(red("Starting shutdown"));
|
|
43
|
+
shuttingDown = true;
|
|
44
|
+
const { authorityStorage } = await import("../0-path-value-core/pathValueCore");
|
|
45
|
+
try {
|
|
46
|
+
await Promise.allSettled([
|
|
47
|
+
...preshutdownHandlers,
|
|
48
|
+
].map(fnc => timeoutToError(timeInMinute, fnc(), () => new Error(`Preshutdown handler ${fnc.name} timed out`))));
|
|
49
|
+
} catch (e) {
|
|
50
|
+
console.log(`Error on preshutdown handlers`, e);
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
await Promise.allSettled([
|
|
54
|
+
function authorityStorageShutdown() { return authorityStorage.onShutdown(); },
|
|
55
|
+
nodeDiscoveryShutdown,
|
|
56
|
+
shutdownPolling,
|
|
57
|
+
...shutdownHandlers,
|
|
58
|
+
].map(fnc => timeoutToError(timeInMinute, fnc(), () => new Error(`Shutdown handler ${fnc.name} timed out`))));
|
|
59
|
+
} catch (e) {
|
|
60
|
+
console.log("Error on shutdown", e);
|
|
61
|
+
}
|
|
62
|
+
// Wait to allow any logged errors to hopefully be written somewhere?
|
|
63
|
+
await delay(2000);
|
|
64
|
+
process.exit();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// IMPORTANT! Yarn detaches the processes, so they keep running when you ctrl+c, even though the shell shows back up. We can fix this by using `node -r ./node_modules/typenode/index.js ./test.ts`. However, it's probably fine, as we still run the shutdown code, it's just that the manager doesn't know if we've shutdown or not.
|
|
68
|
+
if (isNode()) {
|
|
69
|
+
let lineBuffer = "";
|
|
70
|
+
process.stdin.on("data", data => {
|
|
71
|
+
lineBuffer += data.toString();
|
|
72
|
+
let lines = lineBuffer.split("\r");
|
|
73
|
+
lineBuffer = lines.pop()!;
|
|
74
|
+
for (let line of lines) {
|
|
75
|
+
if (line === "SHUTDOWN_NOW_MULTIRUN") {
|
|
76
|
+
logErrors(shutdown());
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (data.toString().includes("\r")) {
|
|
81
|
+
logAll();
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
// NOTE: This extra code is required to actual capture ctrl+c
|
|
87
|
+
if (process.platform === "win32") {
|
|
88
|
+
var rl = require("readline").createInterface({
|
|
89
|
+
input: process.stdin,
|
|
90
|
+
output: process.stdout
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
rl.on("SIGINT", function () {
|
|
94
|
+
process.emit("SIGINT");
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
let killCount = 0;
|
|
98
|
+
function doShutdown() {
|
|
99
|
+
killCount++;
|
|
100
|
+
console.log(`Caught interrupt signal. Attempt number ${killCount}`);
|
|
101
|
+
if (killCount >= 3) {
|
|
102
|
+
console.log("Force exit");
|
|
103
|
+
process.exit();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
logErrors(shutdown());
|
|
107
|
+
}
|
|
108
|
+
process.on("SIGINT", doShutdown);
|
|
109
|
+
|
|
110
|
+
}
|
|
111
|
+
(globalThis as any).logAll = logAll;
|
|
112
112
|
(globalThis as any).logNow = logAll;
|