querysub 0.2.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.
Files changed (169) hide show
  1. package/.dependency-cruiser.js +304 -0
  2. package/.eslintrc.js +51 -0
  3. package/.github/copilot-instructions.md +1 -0
  4. package/.vscode/settings.json +25 -0
  5. package/bin/deploy.js +4 -0
  6. package/bin/function.js +4 -0
  7. package/bin/server.js +4 -0
  8. package/costsBenefits.txt +112 -0
  9. package/deploy.ts +3 -0
  10. package/inject.ts +1 -0
  11. package/package.json +60 -0
  12. package/prompts.txt +54 -0
  13. package/spec.txt +820 -0
  14. package/src/-a-archives/archiveCache.ts +913 -0
  15. package/src/-a-archives/archives.ts +148 -0
  16. package/src/-a-archives/archivesBackBlaze.ts +792 -0
  17. package/src/-a-archives/archivesDisk.ts +418 -0
  18. package/src/-a-archives/copyLocalToBackblaze.ts +24 -0
  19. package/src/-a-auth/certs.ts +517 -0
  20. package/src/-a-auth/der.ts +122 -0
  21. package/src/-a-auth/ed25519.ts +1015 -0
  22. package/src/-a-auth/node-forge-ed25519.d.ts +17 -0
  23. package/src/-b-authorities/dnsAuthority.ts +203 -0
  24. package/src/-b-authorities/emailAuthority.ts +57 -0
  25. package/src/-c-identity/IdentityController.ts +200 -0
  26. package/src/-d-trust/NetworkTrust2.ts +150 -0
  27. package/src/-e-certs/EdgeCertController.ts +288 -0
  28. package/src/-e-certs/certAuthority.ts +192 -0
  29. package/src/-f-node-discovery/NodeDiscovery.ts +543 -0
  30. package/src/-g-core-values/NodeCapabilities.ts +134 -0
  31. package/src/-g-core-values/oneTimeForward.ts +91 -0
  32. package/src/-h-path-value-serialize/PathValueSerializer.ts +769 -0
  33. package/src/-h-path-value-serialize/stringSerializer.ts +176 -0
  34. package/src/0-path-value-core/LoggingClient.tsx +24 -0
  35. package/src/0-path-value-core/NodePathAuthorities.ts +978 -0
  36. package/src/0-path-value-core/PathController.ts +1 -0
  37. package/src/0-path-value-core/PathValueCommitter.ts +565 -0
  38. package/src/0-path-value-core/PathValueController.ts +231 -0
  39. package/src/0-path-value-core/archiveLocks/ArchiveLocks.ts +154 -0
  40. package/src/0-path-value-core/archiveLocks/ArchiveLocks2.ts +820 -0
  41. package/src/0-path-value-core/archiveLocks/archiveSnapshots.ts +180 -0
  42. package/src/0-path-value-core/debugLogs.ts +90 -0
  43. package/src/0-path-value-core/pathValueArchives.ts +483 -0
  44. package/src/0-path-value-core/pathValueCore.ts +2217 -0
  45. package/src/1-path-client/RemoteWatcher.ts +558 -0
  46. package/src/1-path-client/pathValueClientWatcher.ts +702 -0
  47. package/src/2-proxy/PathValueProxyWatcher.ts +1857 -0
  48. package/src/2-proxy/archiveMoveHarness.ts +376 -0
  49. package/src/2-proxy/garbageCollection.ts +753 -0
  50. package/src/2-proxy/pathDatabaseProxyBase.ts +37 -0
  51. package/src/2-proxy/pathValueProxy.ts +139 -0
  52. package/src/2-proxy/schema2.ts +518 -0
  53. package/src/3-path-functions/PathFunctionHelpers.ts +129 -0
  54. package/src/3-path-functions/PathFunctionRunner.ts +619 -0
  55. package/src/3-path-functions/PathFunctionRunnerMain.ts +67 -0
  56. package/src/3-path-functions/deployBlock.ts +10 -0
  57. package/src/3-path-functions/deployCheck.ts +7 -0
  58. package/src/3-path-functions/deployMain.ts +160 -0
  59. package/src/3-path-functions/pathFunctionLoader.ts +282 -0
  60. package/src/3-path-functions/syncSchema.ts +475 -0
  61. package/src/3-path-functions/tests/functionsTest.ts +135 -0
  62. package/src/3-path-functions/tests/rejectTest.ts +77 -0
  63. package/src/4-dom/css.tsx +29 -0
  64. package/src/4-dom/cssTypes.d.ts +212 -0
  65. package/src/4-dom/qreact.tsx +2322 -0
  66. package/src/4-dom/qreactTest.tsx +417 -0
  67. package/src/4-querysub/Querysub.ts +877 -0
  68. package/src/4-querysub/QuerysubController.ts +620 -0
  69. package/src/4-querysub/copyEvent.ts +0 -0
  70. package/src/4-querysub/permissions.ts +289 -0
  71. package/src/4-querysub/permissionsShared.ts +1 -0
  72. package/src/4-querysub/querysubPrediction.ts +525 -0
  73. package/src/5-diagnostics/FullscreenModal.tsx +67 -0
  74. package/src/5-diagnostics/GenericFormat.tsx +165 -0
  75. package/src/5-diagnostics/Modal.tsx +79 -0
  76. package/src/5-diagnostics/Table.tsx +183 -0
  77. package/src/5-diagnostics/TimeGrouper.tsx +114 -0
  78. package/src/5-diagnostics/diskValueAudit.ts +216 -0
  79. package/src/5-diagnostics/memoryValueAudit.ts +442 -0
  80. package/src/5-diagnostics/nodeMetadata.ts +135 -0
  81. package/src/5-diagnostics/qreactDebug.tsx +309 -0
  82. package/src/5-diagnostics/shared.ts +26 -0
  83. package/src/5-diagnostics/synchronousLagTracking.ts +47 -0
  84. package/src/TestController.ts +35 -0
  85. package/src/allowclient.flag +0 -0
  86. package/src/bits.ts +86 -0
  87. package/src/buffers.ts +69 -0
  88. package/src/config.ts +53 -0
  89. package/src/config2.ts +48 -0
  90. package/src/diagnostics/ActionsHistory.ts +56 -0
  91. package/src/diagnostics/NodeViewer.tsx +503 -0
  92. package/src/diagnostics/SizeLimiter.ts +62 -0
  93. package/src/diagnostics/TimeDebug.tsx +18 -0
  94. package/src/diagnostics/benchmark.ts +139 -0
  95. package/src/diagnostics/errorLogs/ErrorLogController.ts +515 -0
  96. package/src/diagnostics/errorLogs/ErrorLogCore.ts +274 -0
  97. package/src/diagnostics/errorLogs/LogClassifiers.tsx +302 -0
  98. package/src/diagnostics/errorLogs/LogFilterUI.tsx +84 -0
  99. package/src/diagnostics/errorLogs/LogNotify.tsx +101 -0
  100. package/src/diagnostics/errorLogs/LogTimeSelector.tsx +724 -0
  101. package/src/diagnostics/errorLogs/LogViewer.tsx +757 -0
  102. package/src/diagnostics/errorLogs/hookErrors.ts +60 -0
  103. package/src/diagnostics/errorLogs/logFiltering.tsx +149 -0
  104. package/src/diagnostics/heapTag.ts +13 -0
  105. package/src/diagnostics/listenOnDebugger.ts +77 -0
  106. package/src/diagnostics/logs/DiskLoggerPage.tsx +572 -0
  107. package/src/diagnostics/logs/ObjectDisplay.tsx +165 -0
  108. package/src/diagnostics/logs/ansiFormat.ts +108 -0
  109. package/src/diagnostics/logs/diskLogGlobalContext.ts +38 -0
  110. package/src/diagnostics/logs/diskLogger.ts +305 -0
  111. package/src/diagnostics/logs/diskShimConsoleLogs.ts +32 -0
  112. package/src/diagnostics/logs/injectFileLocationToConsole.ts +50 -0
  113. package/src/diagnostics/logs/logGitHashes.ts +30 -0
  114. package/src/diagnostics/managementPages.tsx +289 -0
  115. package/src/diagnostics/periodic.ts +89 -0
  116. package/src/diagnostics/runSaturationTest.ts +416 -0
  117. package/src/diagnostics/satSchema.ts +64 -0
  118. package/src/diagnostics/trackResources.ts +82 -0
  119. package/src/diagnostics/watchdog.ts +55 -0
  120. package/src/errors.ts +132 -0
  121. package/src/forceProduction.ts +3 -0
  122. package/src/fs.ts +72 -0
  123. package/src/heapDumps.ts +666 -0
  124. package/src/https.ts +2 -0
  125. package/src/inject.ts +1 -0
  126. package/src/library-components/ATag.tsx +84 -0
  127. package/src/library-components/Button.tsx +344 -0
  128. package/src/library-components/ButtonSelector.tsx +64 -0
  129. package/src/library-components/DropdownCustom.tsx +151 -0
  130. package/src/library-components/DropdownSelector.tsx +32 -0
  131. package/src/library-components/Input.tsx +334 -0
  132. package/src/library-components/InputLabel.tsx +198 -0
  133. package/src/library-components/InputPicker.tsx +125 -0
  134. package/src/library-components/LazyComponent.tsx +62 -0
  135. package/src/library-components/MeasureHeightCSS.tsx +48 -0
  136. package/src/library-components/MeasuredDiv.tsx +47 -0
  137. package/src/library-components/ShowMore.tsx +51 -0
  138. package/src/library-components/SyncedController.ts +171 -0
  139. package/src/library-components/TimeRangeSelector.tsx +407 -0
  140. package/src/library-components/URLParam.ts +263 -0
  141. package/src/library-components/colors.tsx +14 -0
  142. package/src/library-components/drag.ts +114 -0
  143. package/src/library-components/icons.tsx +692 -0
  144. package/src/library-components/niceStringify.ts +50 -0
  145. package/src/library-components/renderToString.ts +52 -0
  146. package/src/misc/PromiseRace.ts +101 -0
  147. package/src/misc/color.ts +30 -0
  148. package/src/misc/getParentProcessId.cs +53 -0
  149. package/src/misc/getParentProcessId.ts +53 -0
  150. package/src/misc/hash.ts +83 -0
  151. package/src/misc/ipPong.js +13 -0
  152. package/src/misc/networking.ts +2 -0
  153. package/src/misc/random.ts +45 -0
  154. package/src/misc.ts +19 -0
  155. package/src/noserverhotreload.flag +0 -0
  156. package/src/path.ts +226 -0
  157. package/src/persistentLocalStore.ts +37 -0
  158. package/src/promise.ts +15 -0
  159. package/src/server.ts +73 -0
  160. package/src/src.d.ts +1 -0
  161. package/src/test/heapProcess.ts +36 -0
  162. package/src/test/mongoSatTest.tsx +55 -0
  163. package/src/test/satTest.ts +193 -0
  164. package/src/test/test.tsx +552 -0
  165. package/src/zip.ts +92 -0
  166. package/src/zipThreaded.ts +106 -0
  167. package/src/zipThreadedWorker.js +19 -0
  168. package/tsconfig.json +27 -0
  169. package/yarnSpec.txt +56 -0
@@ -0,0 +1,50 @@
1
+ import { css, isNode } from "typesafecss";
2
+ import { compileTransformBefore } from "typenode";
3
+ // IMPORTANT! Don't add any imports BEFORE we add compileTransform
4
+ if (isNode()) {
5
+ // NOTE: While mapping line numbers would be nice, just adding the file is easy,
6
+ // and provides a huge benefit. And... you should be able to trace down the specific
7
+ // line from the message (and if not, you should make the message more specific).
8
+ compileTransformBefore((contents, path, module) => {
9
+ function shimConsole() {
10
+ let baseConsole = globalThis.console;
11
+ if (typeof window !== "undefined") return baseConsole;
12
+
13
+ let name = __filename.replaceAll("\\", "/").split("/").pop();
14
+ let dirName = __dirname.replaceAll("\\", "/").split("/").pop();
15
+ let context = {
16
+ __FILE__: __filename,
17
+ __DIR__: __dirname,
18
+ __NAME__: name,
19
+ __DIRNAME__: dirName
20
+ };
21
+ let newConsole = {
22
+ ...baseConsole
23
+ };
24
+ for (let shim of ["log", "info", "warn", "error"] as const) {
25
+ newConsole[shim] = (...args: unknown[]) => {
26
+ args.push(context);
27
+ baseConsole[shim](...args);
28
+ };
29
+ }
30
+ (newConsole as any).__context = context;
31
+ return newConsole;
32
+ }
33
+
34
+ // Use line numbers BEFORE we inject the compile transform. The line numbers are for vs code,
35
+ // so this makes them accurate for original source.
36
+ contents = contents.split("\n").map((line, index) => {
37
+ return line
38
+ .replaceAll(`(0, diskLogger_1.diskLog)(`, `(0, diskLogger_1.diskLog)(console.__context, { __LINE__: ${index} }, `)
39
+ .replaceAll(` diskLog(`, ` diskLog(console.__context, { __LINE__: ${index} },`)
40
+ .replaceAll(`(0, diskLogger_1.logDisk)(`, `(0, diskLogger_1.logDisk)(console.__context, { __LINE__: ${index} }, `)
41
+ .replaceAll(` logDisk(`, ` logDisk(console.__context, { __LINE__: ${index} },`)
42
+ ;
43
+ }).join("\n");
44
+ return `var console = (${shimConsole.toString()})();\n${contents}`;
45
+ });
46
+
47
+ // We want to call shimConsoleLogs early. Before we call it, our additional context isn't
48
+ // screened out, which makes the terminal hard to read.
49
+ void import("./diskShimConsoleLogs").then(x => x.shimConsoleLogs());
50
+ }
@@ -0,0 +1,30 @@
1
+ import child_process from "child_process";
2
+ import { isNode } from "socket-function/src/misc";
3
+
4
+ export async function logGitHashes() {
5
+ let shardHash = await getGitHashFromDir(__dirname);
6
+ let appHash = await getGitHashFromDir(".");
7
+
8
+ console.log("Git hashes", { shardHash, appHash });
9
+ }
10
+
11
+ async function getGitHashFromDir(dir: string) {
12
+ if (!isNode()) return "CLIENTSIDE";
13
+ try {
14
+ return await execPromise(`git rev-parse HEAD`, { cwd: dir });
15
+ } catch {
16
+ return "ERROR";
17
+ }
18
+ }
19
+
20
+ async function execPromise(command: string, options: child_process.ExecOptions) {
21
+ return new Promise<string>((resolve, reject) => {
22
+ child_process.exec(command, options, (err, stdout, stderr) => {
23
+ if (err) {
24
+ reject(err);
25
+ } else {
26
+ resolve(stdout.trim());
27
+ }
28
+ });
29
+ });
30
+ }
@@ -0,0 +1,289 @@
1
+ import { Querysub } from "../../src/4-querysub/QuerysubController";
2
+ import { isClient, isServer } from "../../src/config2";
3
+ import { logErrors } from "../../src/errors";
4
+ import { SocketFunction } from "socket-function/SocketFunction";
5
+ import { css, isNode } from "typesafecss";
6
+ import preact from "preact"; import { qreact } from "../../src/4-dom/qreact";
7
+ import { getBrowserUrlNode, onNodeDiscoveryReady } from "../../src/-f-node-discovery/NodeDiscovery";
8
+ import { URLParam } from "../../src/library-components/URLParam";
9
+ import { ATag } from "../../src/library-components/ATag";
10
+ import { lazy } from "socket-function/src/caching";
11
+ import { t } from "../../src/2-proxy/schema2";
12
+
13
+ import { SocketRegistered } from "socket-function/SocketFunctionTypes";
14
+ import { nextId } from "socket-function/src/misc";
15
+ import { IdentityController_getSecureIP, IdentityController_getMachineId } from "../-c-identity/IdentityController";
16
+ import { LOCAL_DOMAIN } from "../0-path-value-core/PathController";
17
+ import { getNextTime } from "../0-path-value-core/pathValueCore";
18
+ import { encodeArgs } from "../3-path-functions/PathFunctionHelpers";
19
+ import { CallSpec, getCurrentCall, getCurrentCallAllowUndefined, overrideCurrentCall } from "../3-path-functions/PathFunctionRunner";
20
+ import { createLazyComponent } from "../library-components/LazyComponent";
21
+ import { LogNotify } from "./errorLogs/LogNotify";
22
+ import { isTrusted } from "../-d-trust/NetworkTrust2";
23
+ import { getDomain } from "../config";
24
+ import { getCallWrites } from "../4-querysub/querysubPrediction";
25
+
26
+
27
+ export const managementPageURL = new URLParam("managementpage", "");
28
+ export const showingManagementURL = new URLParam("showingmanagement", false);
29
+
30
+ let pages: {
31
+ componentClass: preact.ComponentClass;
32
+ componentName: string;
33
+ title: string;
34
+ }[] = [];
35
+
36
+ let internalIsManagementUser: () => boolean = () => {
37
+ throw new Error("registerManagementPages2 must be called during deploy for management functions to be exposed");
38
+ };
39
+
40
+ // NOTE: We don't store the UI in the database (here, or anywhere else, at least
41
+ // not yet), but we do want to store the permission function call, as we may
42
+ // need to update it immediately, across all servers.
43
+ const schema = Querysub.createSchema()({
44
+ module,
45
+ moduleId: "managementPages",
46
+ functions: {
47
+ schemaIsManagementUser,
48
+ },
49
+ });
50
+ function schemaIsManagementUser() {
51
+ return internalIsManagementUser();
52
+ }
53
+
54
+ export async function registerManagementPages2(config: {
55
+ isManagementUser: () => boolean;
56
+ pages: {
57
+ componentName: string;
58
+ controllerName?: string;
59
+ getModule: () => Promise<any>;
60
+
61
+ title?: string;
62
+ }[];
63
+ }) {
64
+ baseIsManagementUser = config.isManagementUser;
65
+ let inputPages = config.pages.slice();
66
+
67
+ inputPages.push({
68
+ title: "Nodes",
69
+ componentName: "NodeViewer",
70
+ controllerName: "NodeViewerController",
71
+ getModule: () => import("./NodeViewer"),
72
+ });
73
+ inputPages.push({
74
+ title: "Logs",
75
+ componentName: "DiskLoggerPage",
76
+ controllerName: "DiskLoggerController",
77
+ getModule: () => import("./logs/DiskLoggerPage"),
78
+ });
79
+ inputPages.push({
80
+ title: "Error Notifications",
81
+ componentName: "LogViewer",
82
+ controllerName: "LogViewerController",
83
+ getModule: () => import("./errorLogs/LogViewer"),
84
+ });
85
+ inputPages.push({
86
+ title: "Error Classes",
87
+ componentName: "LogClassifiers",
88
+ getModule: () => import("./errorLogs/LogClassifiers"),
89
+ });
90
+ inputPages.push({
91
+ title: "Time",
92
+ componentName: "TimeDebug",
93
+ controllerName: "",
94
+ getModule: () => import("./TimeDebug"),
95
+ });
96
+
97
+ if (isServer()) {
98
+ for (let page of inputPages) {
99
+ // NOTE: If we split this into a module for component/controller, we need to make sure we
100
+ // import both serverside, so we can whitelist them for import clientside.
101
+ let mod = await page.getModule();
102
+ if (!page.controllerName) continue;
103
+ if (!(page.controllerName in mod)) {
104
+ console.error(`Controller ${page.controllerName} not found in module`, mod);
105
+ throw new Error(`Controller ${page.controllerName} not found in module`);
106
+ }
107
+ let controller = mod[page.controllerName] as SocketRegistered;
108
+ if (!controller) {
109
+ throw new Error(`Controller ${page.controllerName} not found in module`);
110
+ }
111
+ if (!controller._classGuid) {
112
+ throw new Error(`Controller ${page.controllerName} does not have a class guid`);
113
+ }
114
+ SocketFunction.expose(controller);
115
+ }
116
+ } else {
117
+ for (let page of inputPages) {
118
+ pages.push({
119
+ title: page.title ?? page.componentName,
120
+ componentName: page.componentName,
121
+ componentClass: createLazyComponent(page.getModule)(page.componentName),
122
+ });
123
+ }
124
+ }
125
+ }
126
+
127
+ let baseIsManagementUser: () => boolean = () => {
128
+ // If we are clientside, all calls come from the server, and so are allowed.
129
+ if (isClient()) return true;
130
+ throw new Error("baseIsManagementUser not set");
131
+ };
132
+
133
+ export async function isManagementUser() {
134
+ let caller = SocketFunction.getCaller();
135
+ let callerMachineId = IdentityController_getMachineId(caller);
136
+
137
+ // Always trust other servers. They can read/write any data anyways. This helps
138
+ // when clients are given callbacks by servers, for management apis which
139
+ // have every endpoint protected by isManagementUser.
140
+ if (await isTrusted(callerMachineId)) return true;
141
+
142
+ let testCall: CallSpec = {
143
+ callerIP: IdentityController_getSecureIP(caller),
144
+ callerMachineId,
145
+ argsEncoded: encodeArgs([]),
146
+ CallId: nextId(),
147
+ DomainName: getDomain(),
148
+ ModuleId: "managementPages",
149
+ FunctionId: "schemaIsManagementUser",
150
+ runAtTime: getNextTime(),
151
+ };
152
+
153
+ let writes = await getCallWrites({
154
+ debugName: "isManagementUser",
155
+ call: testCall,
156
+ });
157
+ return !!writes.result;
158
+ }
159
+
160
+ export async function assertIsManagementUser() {
161
+ if (!await isManagementUser()) {
162
+ throw new Error(`User is not a management user, and so does not have permissions for this endpoint.`);
163
+ }
164
+ }
165
+
166
+ let populateIsManagementUser = lazy(() => {
167
+ setImmediate(async () => {
168
+ let result = await ManagementController.nodes[getBrowserUrlNode()].isManagementUser();
169
+ Querysub.commit(() => {
170
+ isManagementUserValue().value = result;
171
+ });
172
+ });
173
+ });
174
+
175
+ let isManagementUserValue = Querysub.createLocalSchema("isManagementUser", {
176
+ value: t.boolean
177
+ });
178
+ export function renderIsManagementUser() {
179
+ if (!isClient()) throw new Error(`renderIsManagementUser must be called clientside. Instead, call isManagementUser() serverside`);
180
+ populateIsManagementUser();
181
+ return isManagementUserValue().value;
182
+ }
183
+
184
+ class ManagementRoot extends qreact.Component {
185
+ state = {
186
+ ready: false
187
+ };
188
+ componentDidMount() {
189
+ document.addEventListener("keydown", (e) => {
190
+ if (
191
+ e.target instanceof HTMLInputElement
192
+ || e.target instanceof HTMLTextAreaElement
193
+ || !(e.target instanceof HTMLElement)
194
+ || e.target.getAttribute("role") === "textbox"
195
+ || e.target.hasAttribute("data-input")
196
+ ) return;
197
+ if (e.key === "`") {
198
+ Querysub.commit(() => {
199
+ showingManagementURL.value = !showingManagementURL.value;
200
+ });
201
+ }
202
+ });
203
+ // Have to wait for node discovery, which handles detecting the localhost optimization
204
+ logErrors(onNodeDiscoveryReady().finally(async () => {
205
+ Querysub.localCommit(() => this.state.ready = true);
206
+ }));
207
+ }
208
+ render() {
209
+ if (!showingManagementURL.value) return undefined;
210
+ if (!this.state.ready) return undefined;
211
+
212
+ let currentPage: preact.ComponentChild | undefined = undefined;
213
+
214
+ let urlValue = managementPageURL.value;
215
+ let ComponentClass = pages.find(a => a.componentName === urlValue)?.componentClass;
216
+ if (ComponentClass) {
217
+ currentPage = <ComponentClass />;
218
+ }
219
+
220
+ return (
221
+ <>
222
+ <div class={
223
+ css
224
+ .fixed.pos(0, 0).size("100vw", "100vh").zIndex(10).vbox0
225
+ .color("white")
226
+ .color("hsl(0, 0%, 7%)")
227
+ }>
228
+ <style>
229
+ {`
230
+ .ManagementRoot-page a {
231
+ color: hsl(210, 75%, 50%);
232
+ }
233
+ `}
234
+ </style>
235
+ <div class={css.fillWidth.hbox(30).wrap.hsl(245, 25, 60).pad2(10)}>
236
+ <LogNotify />
237
+ {pages.map(page =>
238
+ <ATag values={[{ param: managementPageURL, value: page.componentName }]}>{page.title}</ATag>
239
+ )}
240
+ </div>
241
+ {currentPage &&
242
+ <div
243
+ class={
244
+ css.fillBoth.vbox0.overflowAuto.hsl(145, 50, 75)
245
+ + " ManagementRoot-page"
246
+ }
247
+ onKeyDown={e => {
248
+ if (e.currentTarget === e.target) {
249
+ // Ignore keyboard events
250
+ e.preventDefault();
251
+ }
252
+ }}
253
+ >
254
+ {currentPage}
255
+ </div>
256
+ }
257
+ </div>
258
+
259
+ </>
260
+ );
261
+ }
262
+ }
263
+
264
+ class ManagementControllerBase {
265
+ public async isManagementUser() {
266
+ return await isManagementUser();
267
+ }
268
+ }
269
+ const ManagementController = SocketFunction.register(
270
+ "ManagementControllerBase-fa482fbb-ed3b-4377-bf8e-847357d17244",
271
+ new ManagementControllerBase(),
272
+ () => ({
273
+ isManagementUser: {},
274
+ }),
275
+ () => ({
276
+
277
+ }),
278
+ );
279
+
280
+ async function clientMain() {
281
+ let root = document.createElement("div");
282
+ root.id = "managementRoot";
283
+ document.body.appendChild(root);
284
+ Querysub.render(<ManagementRoot />, root);
285
+ }
286
+
287
+ if (!isNode()) {
288
+ logErrors(clientMain());
289
+ }
@@ -0,0 +1,89 @@
1
+ import { runInfinitePoll, runInfinitePollCallAtStart } from "socket-function/src/batching";
2
+ import { isNode } from "socket-function/src/misc";
3
+ import { logErrors } 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
+
8
+ let periodicFncs: (() => void)[] = [];
9
+ export function registerPeriodic(fnc: () => void) {
10
+ periodicFncs.push(fnc);
11
+ }
12
+
13
+ let shutdownHandlers: (() => Promise<void>)[] = [];
14
+ // NOTE: Won't be always called, but at least makes it possible to gracefully shutdown.
15
+ export function registerShutdownHandler(fnc: () => Promise<void>) {
16
+ shutdownHandlers.push(fnc);
17
+ }
18
+
19
+ function logAll() {
20
+ for (let fnc of periodicFncs) {
21
+ fnc();
22
+ }
23
+ }
24
+
25
+ logErrors(runInfinitePollCallAtStart(1000 * 60, logAll));
26
+
27
+ async function shutdown() {
28
+ const { authorityStorage } = await import("../0-path-value-core/pathValueCore");
29
+ try {
30
+ await authorityStorage.onShutdown();
31
+ await nodeDiscoveryShutdown();
32
+ } catch (e) {
33
+ console.log("Error on shutdown", e);
34
+ }
35
+ for (let fnc of shutdownHandlers) {
36
+ try {
37
+ await fnc();
38
+ } catch (e) {
39
+ console.log(`Error on shutdown handler ${fnc.name}`, e);
40
+ }
41
+ }
42
+ process.exit();
43
+ }
44
+
45
+ if (isNode()) {
46
+ let lineBuffer = "";
47
+ process.stdin.on("data", data => {
48
+ lineBuffer += data.toString();
49
+ let lines = lineBuffer.split("\r");
50
+ lineBuffer = lines.pop()!;
51
+ for (let line of lines) {
52
+ if (line === "SHUTDOWN_NOW_MULTIRUN") {
53
+ logErrors(shutdown());
54
+ }
55
+ }
56
+
57
+ if (data.toString().includes("\r")) {
58
+ logAll();
59
+ }
60
+ });
61
+
62
+
63
+ // NOTE: This extra code is required to actual capture ctrl+c
64
+ if (process.platform === "win32") {
65
+ var rl = require("readline").createInterface({
66
+ input: process.stdin,
67
+ output: process.stdout
68
+ });
69
+
70
+ rl.on("SIGINT", function () {
71
+ process.emit("SIGINT");
72
+ });
73
+ }
74
+ let killCount = 0;
75
+ function doShutdown() {
76
+ killCount++;
77
+ console.log(`Caught interrupt signal. Attempt number ${killCount}`);
78
+ if (killCount >= 3) {
79
+ console.log("Force exit");
80
+ process.exit();
81
+ }
82
+
83
+ logErrors(shutdown());
84
+ }
85
+ process.on("SIGINT", doShutdown);
86
+
87
+ }
88
+ (globalThis as any).logAll = logAll;
89
+ (globalThis as any).logNow = logAll;