querysub 0.324.0 → 0.326.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/package.json +3 -2
- package/src/-a-archives/archiveCache.ts +2 -1
- package/src/-a-auth/certs.ts +2 -1
- package/src/-c-identity/IdentityController.ts +1 -0
- package/src/-d-trust/NetworkTrust2.ts +26 -27
- package/src/-e-certs/EdgeCertController.ts +13 -4
- package/src/0-path-value-core/archiveLocks/ArchiveLocks2.ts +9 -5
- package/src/4-querysub/Querysub.ts +3 -1
- package/src/diagnostics/logs/FastArchiveAppendable.ts +5 -3
- package/src/diagnostics/logs/FastArchiveViewer.tsx +43 -35
- package/src/diagnostics/logs/LogViewer2.tsx +35 -34
- package/src/diagnostics/logs/TimeRangeSelector.tsx +18 -2
- package/src/diagnostics/logs/errorNotifications/ErrorNotificationController.ts +171 -34
- package/src/diagnostics/logs/errorNotifications/ErrorSuppressionUI.tsx +13 -7
- package/src/diagnostics/logs/errorNotifications/ErrorWarning.tsx +16 -4
- package/src/diagnostics/logs/errorNotifications/errorWatchEntry.tsx +78 -0
- package/src/diagnostics/logs/lifeCycleAnalysis/spec.md +27 -83
- package/src/diagnostics/managementPages.tsx +2 -1
- package/src/functional/SocketChannel.ts +5 -1
- package/src/library-components/ATag.tsx +1 -0
- package/src/library-components/SyncedController.ts +14 -3
- package/src/library-components/SyncedControllerLoadingIndicator.tsx +3 -2
- package/src/library-components/URLParam.ts +35 -5
- package/src/library-components/icons.tsx +3 -0
- package/src/library-components/niceStringify.ts +1 -1
- package/src/library-components/urlResetGroups.ts +14 -0
- package/src/misc/formatJSX.tsx +7 -1
- package/src/server.ts +2 -1
- package/testEntry2.ts +16 -5
|
@@ -4,6 +4,8 @@ import { getAllNodeIds, watchNodeIds } from "../-f-node-discovery/NodeDiscovery"
|
|
|
4
4
|
import { requiresNetworkTrustHook } from "../-d-trust/NetworkTrust2";
|
|
5
5
|
import { errorToUndefinedSilent } from "../errors";
|
|
6
6
|
import { assertIsManagementUser } from "../diagnostics/managementPages";
|
|
7
|
+
import { IdentityController_getCurrentReconnectNodeId, IdentityController_getMachineId } from "../-c-identity/IdentityController";
|
|
8
|
+
import { blue } from "socket-function/src/formatting/logColors";
|
|
7
9
|
|
|
8
10
|
export class SocketChannel<T> {
|
|
9
11
|
constructor(private globalUniqueChannelName: string) { }
|
|
@@ -34,7 +36,9 @@ export class SocketChannel<T> {
|
|
|
34
36
|
private remoteWatchers = new Set<string>();
|
|
35
37
|
public async _internal_watchMessages() {
|
|
36
38
|
let caller = SocketFunction.getCaller();
|
|
39
|
+
let callerNodeId = IdentityController_getCurrentReconnectNodeId();
|
|
37
40
|
this.remoteWatchers.add(caller.nodeId);
|
|
41
|
+
console.info(blue(`New watcher for channel ${this.globalUniqueChannelName} from ${caller.nodeId} (${callerNodeId})`));
|
|
38
42
|
SocketFunction.onNextDisconnect(caller.nodeId, () => {
|
|
39
43
|
this.remoteWatchers.delete(caller.nodeId);
|
|
40
44
|
});
|
|
@@ -44,7 +48,7 @@ export class SocketChannel<T> {
|
|
|
44
48
|
void Array.from(this.remoteWatchers).map(async (nodeId) => {
|
|
45
49
|
try {
|
|
46
50
|
await this.controller.nodes[nodeId]._internal_onMessage(message);
|
|
47
|
-
} catch {
|
|
51
|
+
} catch (e) {
|
|
48
52
|
this.remoteWatchers.delete(nodeId);
|
|
49
53
|
}
|
|
50
54
|
});
|
|
@@ -2,6 +2,7 @@ import preact from "preact";
|
|
|
2
2
|
import { css, isNode } from "typesafecss";
|
|
3
3
|
import { URLParam, parseSearchString, encodeSearchString } from "./URLParam";
|
|
4
4
|
import { qreact } from "../4-dom/qreact";
|
|
5
|
+
import { niceStringify } from "../niceStringify";
|
|
5
6
|
|
|
6
7
|
export type URLOverride<T = unknown> = {
|
|
7
8
|
param: URLParam<T>;
|
|
@@ -40,11 +40,11 @@ onHotReload(() => {
|
|
|
40
40
|
export function syncedIsAnyLoading() {
|
|
41
41
|
return Querysub.fastRead(() => {
|
|
42
42
|
for (let controllerId in syncedData()) {
|
|
43
|
-
for (let fncs of Object.
|
|
44
|
-
for (let fnc of Object.
|
|
43
|
+
for (let [nodeId, fncs] of Object.entries(syncedData()[controllerId])) {
|
|
44
|
+
for (let [fncName, fnc] of Object.entries(fncs)) {
|
|
45
45
|
for (let obj of Object.values(fnc)) {
|
|
46
46
|
if (atomic(obj.promise)) {
|
|
47
|
-
return
|
|
47
|
+
return `${fncName} (on ${nodeId})`;
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
}
|
|
@@ -109,6 +109,7 @@ export function getSyncedController<T extends SocketRegistered>(
|
|
|
109
109
|
resetAll(): void;
|
|
110
110
|
refreshAll(): void;
|
|
111
111
|
isAnyLoading(): boolean;
|
|
112
|
+
base: T;
|
|
112
113
|
} {
|
|
113
114
|
if (isNode()) {
|
|
114
115
|
let result = cache((nodeId: string) => {
|
|
@@ -160,6 +161,7 @@ export function getSyncedController<T extends SocketRegistered>(
|
|
|
160
161
|
result.isAnyLoading = () => {
|
|
161
162
|
notAllowedOnServer();
|
|
162
163
|
};
|
|
164
|
+
result.base = controller;
|
|
163
165
|
return result;
|
|
164
166
|
}
|
|
165
167
|
let id = nextId();
|
|
@@ -307,11 +309,19 @@ export function getSyncedController<T extends SocketRegistered>(
|
|
|
307
309
|
obj.promise = undefined;
|
|
308
310
|
obj.invalidated = true;
|
|
309
311
|
call(...args);
|
|
312
|
+
// Assign to itself, to preset the type assumptions typescript makes (otherwise we get an error below)
|
|
313
|
+
obj = obj as any;
|
|
310
314
|
let promise = atomic(obj.promise);
|
|
311
315
|
if (!promise) {
|
|
312
316
|
debugger;
|
|
313
317
|
throw new Error(`Impossible, called function, but promise is not found for ${fncName}`);
|
|
314
318
|
}
|
|
319
|
+
// Don't cache promise calls
|
|
320
|
+
void promise.finally(() => {
|
|
321
|
+
if (obj.promise === promise) {
|
|
322
|
+
obj.promise = undefined;
|
|
323
|
+
}
|
|
324
|
+
});
|
|
315
325
|
return promise;
|
|
316
326
|
});
|
|
317
327
|
};
|
|
@@ -404,5 +414,6 @@ export function getSyncedController<T extends SocketRegistered>(
|
|
|
404
414
|
}
|
|
405
415
|
});
|
|
406
416
|
};
|
|
417
|
+
result.base = controller;
|
|
407
418
|
return result;
|
|
408
419
|
}
|
|
@@ -7,7 +7,8 @@ export class SyncedControllerLoadingIndicator extends qreact.Component {
|
|
|
7
7
|
timeLastLoaded = Date.now();
|
|
8
8
|
lastWasLoaded = false;
|
|
9
9
|
render() {
|
|
10
|
-
|
|
10
|
+
let loadingPath = syncedIsAnyLoading();
|
|
11
|
+
if (!loadingPath) {
|
|
11
12
|
if (!this.lastWasLoaded) {
|
|
12
13
|
console.log(`Loaded all SyncedController calls in ${formatTime(Date.now() - this.timeLastLoaded)}`);
|
|
13
14
|
}
|
|
@@ -33,7 +34,7 @@ export class SyncedControllerLoadingIndicator extends qreact.Component {
|
|
|
33
34
|
.borderRadius("50%")
|
|
34
35
|
+ " " + spinAnimationClass
|
|
35
36
|
}></div>
|
|
36
|
-
<span>Syncing data...</span>
|
|
37
|
+
<span title={loadingPath}>Syncing data...</span>
|
|
37
38
|
<style>
|
|
38
39
|
{`
|
|
39
40
|
@keyframes ${spinAnimationClass} {
|
|
@@ -34,16 +34,37 @@ if (!isNode()) {
|
|
|
34
34
|
loadSearchCache = parseSearchString(location.search);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
declare global {
|
|
38
|
+
var urlResetLinks: Map<string, Set<string>>;
|
|
39
|
+
}
|
|
40
|
+
// changed => reset
|
|
41
|
+
let resetLinks: Map<string, Set<string>> = globalThis.urlResetLinks || (globalThis.urlResetLinks = new Map());
|
|
42
|
+
|
|
37
43
|
|
|
38
44
|
export class URLParam<T = unknown> {
|
|
39
|
-
constructor(urlKey: string, defaultValue: T) {
|
|
40
|
-
return createURLSync(urlKey, defaultValue);
|
|
45
|
+
constructor(urlKey: string, defaultValue: T, config?: URLParamConfig) {
|
|
46
|
+
return createURLSync(urlKey, defaultValue, config);
|
|
41
47
|
}
|
|
42
48
|
}
|
|
43
|
-
|
|
44
|
-
export function createURLSync<T>(urlKey: string, defaultValue: T, config?: {
|
|
49
|
+
export type URLParamConfig = {
|
|
45
50
|
storage?: "url" | "localStorage";
|
|
46
|
-
|
|
51
|
+
// Reset when any of these urlKeys change
|
|
52
|
+
reset?: { (): { urlKey: string }[] }[];
|
|
53
|
+
};
|
|
54
|
+
/** const myVariable = createURLSync("myvar", 0) */
|
|
55
|
+
export function createURLSync<T>(urlKey: string, defaultValue: T, config?: URLParamConfig): URLParam<T> {
|
|
56
|
+
setImmediate(() => {
|
|
57
|
+
for (let reset of config?.reset || []) {
|
|
58
|
+
for (let resetSource of reset()) {
|
|
59
|
+
let lookup = resetLinks.get(resetSource.urlKey);
|
|
60
|
+
if (!lookup) {
|
|
61
|
+
lookup = new Set();
|
|
62
|
+
resetLinks.set(resetSource.urlKey, lookup);
|
|
63
|
+
}
|
|
64
|
+
lookup.add(urlKey);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
});
|
|
47
68
|
|
|
48
69
|
let prev = globalURLLookup[urlKey];
|
|
49
70
|
if (prev) {
|
|
@@ -104,6 +125,15 @@ export function createURLSync<T>(urlKey: string, defaultValue: T, config?: {
|
|
|
104
125
|
}
|
|
105
126
|
},
|
|
106
127
|
set value(value: T) {
|
|
128
|
+
let resetTargets = resetLinks.get(urlKey);
|
|
129
|
+
if (resetTargets) {
|
|
130
|
+
for (let resetTarget of resetTargets) {
|
|
131
|
+
let param = globalURLLookup[resetTarget];
|
|
132
|
+
if (param) {
|
|
133
|
+
param.reset();
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
107
137
|
if (!proxyWatcher.inWatcher()) {
|
|
108
138
|
Querysub.commit(() => {
|
|
109
139
|
param.value = value;
|
|
@@ -9,6 +9,9 @@ import { renderToString } from "./renderToString";
|
|
|
9
9
|
import { debugTime } from "../../src/0-path-value-core/pathValueCore";
|
|
10
10
|
import { measureBlock } from "socket-function/src/profiling/measure";
|
|
11
11
|
|
|
12
|
+
// TODO: Create more icons with: https://www.recraft.ai/project/e2a2200f-bed2-4426-b8f1-e5120b9dc990
|
|
13
|
+
// - This should also make creating animated SVGs a lot easier. If the base is nice, and we add comments, we can probably get the AI to help animate it
|
|
14
|
+
|
|
12
15
|
// Most icons are from https://www.figma.com/file/eVpKKmt8uOKmSYKW4LyusF/Free-Icon-Pack-1600%2B-icons-(Community)?node-id=1654-9894&t=0bDbK0bA9KGpswRE-0
|
|
13
16
|
|
|
14
17
|
// TODO: Add a build step that does this (storing the .svgs in a file), so we don't
|
|
@@ -10,7 +10,7 @@ let specialStringValuesEncode = new Map([...specialStringValuesDecode].map(([key
|
|
|
10
10
|
export function serializeURLParam(value: unknown): string | undefined {
|
|
11
11
|
if (value === true) return undefined;
|
|
12
12
|
// If it is a string and doesn't look JSON encoded, then just use it raw.
|
|
13
|
-
if (typeof value === "string" && !lookJSONEncoded(value)) {
|
|
13
|
+
if (typeof value === "string" && !lookJSONEncoded(value) && value !== "") {
|
|
14
14
|
return value;
|
|
15
15
|
}
|
|
16
16
|
let specialValue = specialStringValuesEncode.get(value);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { URLParam, URLParamConfig } from "./URLParam";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
export const pageURL = new URLParam("page", "");
|
|
5
|
+
export const tabURL = new URLParam("tab", "");
|
|
6
|
+
export const managementPageURL = new URLParam("managementpage", "");
|
|
7
|
+
|
|
8
|
+
export const filterURL = new URLParam("filter", "", { reset: [mainResets] });
|
|
9
|
+
export const startTimeURL = new URLParam("startTime", 0, { reset: [mainResets] });
|
|
10
|
+
export const endTimeURL = new URLParam("endTime", 0, { reset: [mainResets] });
|
|
11
|
+
|
|
12
|
+
export function mainResets(): URLParam[] {
|
|
13
|
+
return [pageURL, tabURL, managementPageURL];
|
|
14
|
+
}
|
package/src/misc/formatJSX.tsx
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { qreact } from "../4-dom/qreact";
|
|
2
2
|
import { formatTime, formatVeryNiceDateTime } from "socket-function/src/formatting/format";
|
|
3
|
+
import { Querysub } from "../4-querysub/QuerysubController";
|
|
3
4
|
|
|
4
5
|
export function formatDateJSX(time: number) {
|
|
5
|
-
|
|
6
|
+
let diff = time - Querysub.nowDelayed(10 * 1000);
|
|
7
|
+
let ago = diff < 0;
|
|
8
|
+
if (ago) {
|
|
9
|
+
diff = -diff;
|
|
10
|
+
}
|
|
11
|
+
return <span title={formatVeryNiceDateTime(time)}>{!ago && "IN "}{formatTime(diff)}{ago && " AGO"}</span>;
|
|
6
12
|
}
|
package/src/server.ts
CHANGED
|
@@ -33,7 +33,8 @@ async function main() {
|
|
|
33
33
|
|
|
34
34
|
Error.stackTraceLimit = 20;
|
|
35
35
|
|
|
36
|
-
//SocketFunction.logMessages = true;
|
|
36
|
+
// SocketFunction.logMessages = true;
|
|
37
|
+
// SocketFunction.silent = false;
|
|
37
38
|
|
|
38
39
|
// ClientWatcher.DEBUG_READS = true;
|
|
39
40
|
// ClientWatcher.DEBUG_WRITES = true;
|
package/testEntry2.ts
CHANGED
|
@@ -3,13 +3,23 @@ import { getOwnMachineId } from "./src/-a-auth/certs";
|
|
|
3
3
|
import { getOwnThreadId } from "./src/-f-node-discovery/NodeDiscovery";
|
|
4
4
|
import { shutdown } from "./src/diagnostics/periodic";
|
|
5
5
|
import { testTCPIsListening } from "socket-function/src/networking";
|
|
6
|
+
import { Querysub } from "./src/4-querysub/QuerysubController";
|
|
7
|
+
import { timeInSecond } from "socket-function/src/misc";
|
|
6
8
|
|
|
7
9
|
export async function testMain() {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
//
|
|
10
|
+
Querysub;
|
|
11
|
+
//let test = await testTCPIsListening("1.1.1.1", 443);
|
|
12
|
+
//console.log(test);
|
|
13
|
+
// Writing heartbeat 2025/09/14 08:37:46 PM for self (5ac8a2fa78fce4ea.971ed8b01743d123.querysubtest.com:13900)
|
|
14
|
+
await delay(timeInSecond);
|
|
15
|
+
await Querysub.hostService("test");
|
|
16
|
+
await delay(timeInSecond * 5);
|
|
11
17
|
// console.log(getOwnThreadId());
|
|
12
|
-
//
|
|
18
|
+
// Log an error every 30 seconds forever.
|
|
19
|
+
while (true) {
|
|
20
|
+
console.error(`Test warning for im testing ${Date.now()}`);
|
|
21
|
+
await delay(timeInSecond * 30);
|
|
22
|
+
}
|
|
13
23
|
// console.log(getOwnThreadId());
|
|
14
24
|
// await shutdown();
|
|
15
25
|
//await Querysub.hostService("test");
|
|
@@ -24,7 +34,8 @@ export async function testMain() {
|
|
|
24
34
|
// }
|
|
25
35
|
// await testLogs.flushNow();
|
|
26
36
|
// }
|
|
27
|
-
|
|
37
|
+
await delay(timeInSecond * 15);
|
|
38
|
+
await shutdown();
|
|
28
39
|
}
|
|
29
40
|
async function main() {
|
|
30
41
|
|