querysub 0.377.0 → 0.379.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 (29) hide show
  1. package/.cursorrules +2 -0
  2. package/bin/error-watch-public.js +7 -0
  3. package/bin/error-watch.js +6 -0
  4. package/package.json +7 -4
  5. package/src/-f-node-discovery/NodeDiscovery.ts +7 -0
  6. package/src/-g-core-values/NodeCapabilities.ts +28 -14
  7. package/src/3-path-functions/PathFunctionRunnerMain.ts +0 -4
  8. package/src/diagnostics/MachineThreadInfo.tsx +33 -10
  9. package/src/diagnostics/logs/IndexedLogs/IndexedLogs.ts +24 -1
  10. package/src/diagnostics/logs/IndexedLogs/LogViewer3.tsx +6 -2
  11. package/src/diagnostics/logs/diskLogger.ts +26 -27
  12. package/src/diagnostics/logs/diskShimConsoleLogs.ts +4 -0
  13. package/src/diagnostics/logs/errorNotifications2/ErrorNotificationPage.tsx +505 -0
  14. package/src/diagnostics/logs/errorNotifications2/ErrorWarning.tsx +32 -0
  15. package/src/diagnostics/logs/errorNotifications2/errorNotifications.ts +629 -0
  16. package/src/diagnostics/logs/errorNotifications2/errorWatchEntry.ts +13 -0
  17. package/src/diagnostics/logs/errorNotifications2/errorWatcher.ts +168 -0
  18. package/src/diagnostics/logs/errorNotifications2/logWatcher.ts +104 -0
  19. package/src/diagnostics/logs/errorNotifications2/openRouterHelper.ts +77 -0
  20. package/src/diagnostics/logs/lifeCycleAnalysis/lifeCycles.tsx +17 -19
  21. package/src/diagnostics/managementPages.tsx +12 -2
  22. package/src/server.ts +0 -8
  23. package/src/user-implementation/SecurityPage.tsx +6 -0
  24. package/src/user-implementation/userData.ts +6 -1
  25. package/test.ts +16 -6
  26. package/test2.ts +20 -0
  27. package/src/diagnostics/logs/errorNotifications2/errorNotifications2.ts +0 -9
  28. package/src/diagnostics/logs/lifeCycleAnalysis/test.ts +0 -0
  29. /package/src/library-components/{errorNotifications.tsx → uncaughtToast.tsx} +0 -0
package/.cursorrules CHANGED
@@ -10,6 +10,8 @@ When running a command in the current project, don't "cd" to it. It's redundant,
10
10
 
11
11
  Don't use redundant comments. If it's a single line and the function name says the same thing that the comment is going to say, you don't need the comment.
12
12
 
13
+ When you're inline passing a lambda as an argument, never specify the type. The type should always be inferred. Specifying the type is bad, and it's hard coding, and we don't like hard coding.
14
+
13
15
  NEVER EVER pass state to qreact.Component as a template parameter. It should ALSO be declared like so (inside the class):
14
16
  state = t.state({
15
17
  num: t.number,
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+
3
+ process.argv.push("--nobreak");
4
+ process.argv.push("--public");
5
+
6
+ require("typenode");
7
+ require("../src/diagnostics/logs/errorNotifications2/errorWatchEntry.ts");
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+
3
+ process.argv.push("--local");
4
+
5
+ require("typenode");
6
+ require("../src/diagnostics/logs/errorNotifications2/errorWatchEntry.ts");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "querysub",
3
- "version": "0.377.0",
3
+ "version": "0.379.0",
4
4
  "main": "index.js",
5
5
  "license": "MIT",
6
6
  "note1": "note on node-forge fork, see https://github.com/digitalbazaar/forge/issues/744 for details",
@@ -12,9 +12,10 @@
12
12
  "servershardedtest": "yarn server --authority ./pathremain.json & yarn server --authority ./patha.json & yarn server --authority ./pathb.json & yarn server --authority ./pathc.json & yarn server --authority ./pathd.json",
13
13
  "type": "yarn tsc --noEmit",
14
14
  "depend": "yarn --silent depcruise src --include-only \"^src\" --config --output-type dot | dot -T svg > dependency-graph.svg",
15
- "t": "yarn typenode ./src/diagnostics/logs/lifeCycleAnalysis/testHoist.ts",
15
+ "t": "yarn typenode ./test.ts",
16
+ "t2": "yarn typenode ./test2.ts",
16
17
  "test-wat": "yarn typenode ./src/wat/watCompiler.ts",
17
- "error-watch": "yarn typenode ./src/diagnostics/logs/errorNotifications/errorWatchEntry.tsx",
18
+ "error-watch": "yarn typenode ./src/diagnostics/logs/errorNotifications2/errorWatchEntry.ts",
18
19
  "error-email": "yarn typenode ./src/diagnostics/logs/errorNotifications/errorDigestEntry.tsx",
19
20
  "build-native": "cd src/diagnostics/logs/IndexedLogs && node-gyp rebuild"
20
21
  },
@@ -33,7 +34,9 @@
33
34
  "join": "./bin/join.js",
34
35
  "join-public": "./bin/join-public.js",
35
36
  "movelogs": "./bin/movelogs.js",
36
- "addsuperuser": "./bin/addsuperuser.js"
37
+ "addsuperuser": "./bin/addsuperuser.js",
38
+ "error-watch": "./bin/error-watch.js",
39
+ "error-watch-public": "./bin/error-watch-public.js"
37
40
  },
38
41
  "dependencies": {
39
42
  "@types/fs-ext": "^2.0.3",
@@ -113,6 +113,13 @@ export function isNodeIdOnOwnMachineId(nodeId: string): boolean {
113
113
  return certs.getMachineId(nodeId) === getOwnMachineId() || nodeId.startsWith("127-0-0-1.");
114
114
  }
115
115
 
116
+ /** Often, when getting all nodes, you're going to receive duplicates of all local nodes with the duplicate containing this prefix. Using the local ID is faster as it will actually use the local IP.
117
+ TODO: We need a robust way to give a deduplicated set that prefers the local ids. At the moment we just filter out the id locals if we can't have duplicates.
118
+ */
119
+ export function isNodeIdLocal(nodeId: string): boolean {
120
+ return nodeId.startsWith("127-0-0-1.");
121
+ }
122
+
116
123
  let nodeOverrides: string[] | undefined;
117
124
  let beforeGetNodeAllId = async () => { };
118
125
  export async function getAllNodeIds() {
@@ -16,15 +16,14 @@ import { requiresNetworkTrustHook } from "../-d-trust/NetworkTrust2";
16
16
  import { isNoNetwork } from "../config";
17
17
  import { getDebuggerUrl } from "../diagnostics/listenOnDebugger";
18
18
  import { hackDevtoolsWebsocketForward } from "./oneTimeForward";
19
- import { getOwnMachineId, decodeNodeId, decodeNodeIdAssert } from "../-a-auth/certs";
19
+ import { getOwnMachineId, decodeNodeId, decodeNodeIdAssert, getMachineId } from "../-a-auth/certs";
20
20
  import { sort } from "socket-function/src/misc";
21
21
  import { getPathStr2 } from "../path";
22
+ import { PromiseObj } from "../promise";
22
23
  setImmediate(() => {
23
24
  import("../diagnostics/MachineThreadInfo");
24
25
  });
25
26
 
26
- let loadTime = Date.now();
27
-
28
27
  let controllerNodeIdCache = new Map<string, string | Promise<string | undefined>>();
29
28
 
30
29
  // NOTE: If this becomes slow (because we are just trying all servers), we could start to store capabilities
@@ -39,32 +38,45 @@ export async function getControllerNodeId(
39
38
  if (cached && typeof cached !== "string") {
40
39
  cached = await cached;
41
40
  }
41
+ // TODO: We are relying on is node connected to do a lot of work here. If the node's connected, but somehow doesn't work, we can get in an unrecoverable state, even if there are plenty of other working nodes.
42
42
  if (cached && !SocketFunction.isNodeConnected(cached)) {
43
43
  controllerNodeIdCache.delete(controller._classGuid);
44
44
  cached = undefined;
45
45
  }
46
46
  if (cached) return cached;
47
- let promise = getInternal();
47
+ let promise = getInternal(retryCount);
48
48
  controllerNodeIdCache.set(controller._classGuid, promise);
49
49
  return promise;
50
50
 
51
- async function getInternal() {
52
-
51
+ async function getInternal(retryCount: number) {
53
52
  let nodeIdsToTest = await getAllNodeIds();
54
53
  // Shuffle, so we aren't always using the same node!
55
54
  nodeIdsToTest = shuffle(nodeIdsToTest, Date.now());
56
- for (let nodeId of nodeIdsToTest) {
55
+ let resolvedNode = new PromiseObj<string>();
56
+
57
+ if (!quiet) {
58
+ console.log(`Checking ${nodeIdsToTest.length} nodes for capability ${controller._classGuid}`);
59
+ }
60
+ let allFinished = Promise.all(nodeIdsToTest.map(async nodeId => {
57
61
  if (await doesNodeExposeController(nodeId, controller)) {
58
62
  if (!quiet) {
59
63
  let duration = Date.now() - initialTime;
60
64
  console.log(green(`Resolved capability ${controller._classGuid} with node ${nodeId}, in ${formatTime(duration)}`));
61
65
  }
62
- return nodeId;
66
+ resolvedNode.resolve(nodeId);
63
67
  }
68
+ }));
69
+ let result = await Promise.race([allFinished, resolvedNode.promise]);
70
+ if (typeof result === "string") {
71
+ return result;
64
72
  }
73
+
65
74
  if (retryCount > 0) {
75
+ if (!quiet) {
76
+ console.log(`Did not find capability in ${nodeIdsToTest.length} nodes, retrying... ${controller._classGuid}`);
77
+ }
66
78
  await delay(2000);
67
- return getControllerNodeId(controller, quiet, retryCount - 1, initialTime);
79
+ return getInternal(retryCount - 1);
68
80
  }
69
81
  if (!quiet) {
70
82
  console.warn(yellow(`Could not find a node that exposes controller ${controller._classGuid}, tried: ${JSON.stringify(nodeIdsToTest)}`));
@@ -81,7 +93,11 @@ export async function getControllerNodeIdList(
81
93
  await Promise.all(nodeIdsToTest.map(async nodeId => {
82
94
  let result = await doesNodeExposeController(nodeId, controller);
83
95
  if (result) {
84
- passedNodeIds.set(nodeId, result);
96
+ let entryPoint = await NodeCapabilitiesController.nodes[nodeId].getEntryPoint();
97
+ passedNodeIds.set(nodeId, {
98
+ machineId: getMachineId(nodeId),
99
+ entryPoint,
100
+ });
85
101
  }
86
102
  }));
87
103
 
@@ -99,13 +115,11 @@ export async function getControllerNodeIdList(
99
115
  }
100
116
 
101
117
 
102
- export async function doesNodeExposeController(reconnectNodeId: string, controller: SocketRegistered<{}>): Promise<{ machineId: string, entryPoint: string } | false> {
118
+ export async function doesNodeExposeController(reconnectNodeId: string, controller: SocketRegistered<{}>): Promise<boolean> {
103
119
  let exposedControllers = await timeoutToUndefinedSilent(10_000, NodeCapabilitiesController.nodes[reconnectNodeId].getExposedControllers());
104
- let machineId = await getOwnMachineId();
105
- let entryPoint = await new NodeCapabilitiesControllerBase().getEntryPoint();
106
120
 
107
121
  if (exposedControllers?.includes(controller._classGuid)) {
108
- return { machineId, entryPoint };
122
+ return true;
109
123
  }
110
124
  return false;
111
125
  }
@@ -40,10 +40,6 @@ async function main() {
40
40
 
41
41
  await Querysub.hostService("PathFunctionRunnerMain");
42
42
 
43
- if (!isPublic()) {
44
- void IndexedLogs.runLogMoveLoop();
45
- }
46
-
47
43
  // Use a fairly high stick time (the default is 10s), because having wait to sync data is very slow,
48
44
  // and the function runner SHOULD have more memory than the clients, and much faster network speeds
49
45
  // (it should be on the same local network as the path value authorities).
@@ -40,6 +40,7 @@ export class MachineThreadInfo extends qreact.Component<{
40
40
  machineId: string;
41
41
  threadId?: string;
42
42
  onlyShowThread?: boolean;
43
+ noButtons?: boolean;
43
44
  }> {
44
45
  static renderInProgress = true;
45
46
 
@@ -93,7 +94,17 @@ export class MachineThreadInfo extends qreact.Component<{
93
94
  <>
94
95
  <div className={css.vbox(4)}>
95
96
  <strong>Entry Point</strong>
96
- <div>{threadInfo.entrypoint}</div>
97
+ <div>
98
+ {threadInfo.entrypoint && (
99
+ <a
100
+ href={`cursor://file/${threadInfo.entrypoint}`}
101
+ className={css.color("hsl(210, 100%, 80%)").textDecoration("none").textDecoration("underline", "hover")}
102
+ >
103
+ {threadInfo.entrypoint}
104
+ </a>
105
+ )}
106
+ {!threadInfo.entrypoint && threadInfo.entrypoint}
107
+ </div>
97
108
  </div>
98
109
 
99
110
  <div className={css.vbox(4)}>
@@ -112,9 +123,9 @@ export class MachineThreadInfo extends qreact.Component<{
112
123
  </div>
113
124
 
114
125
  <div className={css.hbox(12)}>
115
- <Button onClick={() => this.handleAttach(threadInfo)}>
126
+ {threadInfo && <Button onClick={() => this.handleAttach(threadInfo)}>
116
127
  Attach Debugger
117
- </Button>
128
+ </Button>}
118
129
 
119
130
  <Button onClick={this.handleRefresh}>
120
131
  Refresh
@@ -145,8 +156,8 @@ export class MachineThreadInfo extends qreact.Component<{
145
156
 
146
157
  return (
147
158
  <div
148
- className={css.button.hbox(4).pad2(4).hsla(0, 0, 100, 0.5)}
149
- onClick={(e) => {
159
+ className={(this.props.noButtons && "" || css.button) + css.hbox(4).pad2(4).hsla(0, 0, 100, 0.5)}
160
+ onClick={this.props.noButtons ? undefined : (e) => {
150
161
  e.stopPropagation();
151
162
  this.showExpandedView(machineInfo, threadInfo);
152
163
  }}
@@ -155,10 +166,22 @@ export class MachineThreadInfo extends qreact.Component<{
155
166
  {machineInfo?.hostname || this.props.machineId}
156
167
  </span>}
157
168
  {!this.props.onlyShowThread && this.props.threadId && " | "}
158
- {this.props.threadId && <span title={this.props.threadId}>
159
- {/* TODO: Create a mapping from entrypoints to nice names. The full folder path will likely be too large, but just the filename is too similar (it's server.ts both both the PathValueServer and the application server). */}
160
- {threadInfo?.entrypoint || this.props.threadId}
161
- </span>}
169
+ {this.props.threadId && (
170
+ threadInfo?.entrypoint ? (
171
+ <a
172
+ href={`cursor://file/${threadInfo.entrypoint}`}
173
+ title={this.props.threadId}
174
+ className={css.color("hsl(210, 100%, 80%)").textDecoration("none").textDecoration("underline", "hover")}
175
+ onClick={(e) => e.stopPropagation()}
176
+ >
177
+ {threadInfo.entrypoint}
178
+ </a>
179
+ ) : (
180
+ <span title={this.props.threadId}>
181
+ {this.props.threadId}
182
+ </span>
183
+ )
184
+ )}
162
185
  </div>
163
186
  );
164
187
  }
@@ -213,7 +236,7 @@ class MachineThreadInfoBase {
213
236
 
214
237
  // This function is only intended to be called between nodes
215
238
  public async getNodeInfo() {
216
- const hostname = await errorToUndefinedSilent(runPromise("hostname"));
239
+ const hostname = await errorToUndefinedSilent(runPromise("hostname", { quiet: true }));
217
240
  return {
218
241
  hostname: hostname?.trim() || "",
219
242
  entrypoint: process.argv[1] || "",
@@ -26,6 +26,9 @@ import { LimitGroup } from "../../../functional/limitProcessing";
26
26
  import { getAllNodeIds } from "../../../-f-node-discovery/NodeDiscovery";
27
27
  import { NodeCapabilitiesController } from "../../../-g-core-values/NodeCapabilities";
28
28
  import { getLoggers2Async } from "../diskLogger";
29
+ import { watchAllValues } from "../errorNotifications2/logWatcher";
30
+ //
31
+ watchAllValues;
29
32
 
30
33
  export type TimeFilePathWithSize = TimeFilePath & {
31
34
  size: number;
@@ -33,7 +36,8 @@ export type TimeFilePathWithSize = TimeFilePath & {
33
36
  sourceName: string;
34
37
  };
35
38
 
36
- let loggerByName = new Map<string, IndexedLogs<unknown>>();
39
+ export let loggerByName = new Map<string, IndexedLogs<unknown>>();
40
+
37
41
 
38
42
 
39
43
  export class IndexedLogs<T> {
@@ -218,11 +222,30 @@ export class IndexedLogs<T> {
218
222
  return groups;
219
223
  }
220
224
 
225
+ private errorWatchers = new Set<(datum: T) => void>();
226
+ public watchErrors(callback: (datum: T) => void): () => void {
227
+ console.log(blue(`Watching errors: ${this.config.name}`));
228
+ this.errorWatchers.add(callback);
229
+ return () => {
230
+ this.errorWatchers.delete(callback);
231
+ };
232
+ }
233
+
221
234
  public append(datum: T) {
222
235
  this.getCurrentLogStream().append(datum);
223
236
  if (IndexedLogs.shouldRunLoop) {
224
237
  this.runLogMoverLoop();
225
238
  }
239
+
240
+ for (let callback of this.errorWatchers.values()) {
241
+ console.log(blue(`Calling error callback: ${this.config.name}`));
242
+ try {
243
+ callback(datum);
244
+ } catch (e) {
245
+ this.errorWatchers.delete(callback);
246
+ console.warn("Error in watchErrors callback, removing callback (likely just a client disconnect)", e);
247
+ }
248
+ }
226
249
  }
227
250
 
228
251
  private machineNodeCache = new Map<string, string>();
@@ -4,7 +4,7 @@ import { css } from "../../../4-dom/css";
4
4
  import { t } from "../../../2-proxy/schema2";
5
5
  import { Button } from "../../../library-components/Button";
6
6
  import { InputLabel, InputLabelURL } from "../../../library-components/InputLabel";
7
- import { getLoggers2, getLoggers2Async, LogDatum } from "../diskLogger";
7
+ import { getLoggers2Async, LogDatum } from "../diskLogger";
8
8
  import { list, timeInDay, keyByArray, sort, throttleFunction } from "socket-function/src/misc";
9
9
  import { formatDateTime, formatDateTimeDetailed, formatNumber, formatTime, formatPercent } from "socket-function/src/formatting/format";
10
10
  import { IndexedLogs, TimeFilePathWithSize } from "./IndexedLogs";
@@ -69,7 +69,11 @@ export class LogViewer3 extends qreact.Component {
69
69
  private searchSequenceNumber = 0;
70
70
 
71
71
  componentDidMount(): void {
72
- void this.loadPaths();
72
+ if (searchTextURL.value) {
73
+ void this.search();
74
+ } else {
75
+ void this.loadPaths();
76
+ }
73
77
  }
74
78
 
75
79
  getPaths(): TimeFilePathWithSize[] {
@@ -7,6 +7,7 @@ import { formatTime } from "socket-function/src/formatting/format";
7
7
  import { addEpsilons } from "../../bits";
8
8
  import { getPathStr2 } from "../../path";
9
9
  import { isPublic } from "../../config";
10
+ import type { IndexedLogs } from "./IndexedLogs/IndexedLogs";
10
11
  // IMPORTANT! We can't have any real imports here, because we are depended on so early in startup!
11
12
 
12
13
  if (isNode()) {
@@ -65,32 +66,35 @@ export const LOG_LINE_LIMIT_FLAG = String.fromCharCode(44534) + "LOGS_LINE_LIMIT
65
66
  /** If this key exists in the logged object, as in a key in one of the objects logged, then we will use the value of it as the limit ID. This is useful as it allows us to either override a limit or limit something independently from other logs in the file. */
66
67
  export const LOG_LINE_LIMIT_ID = "LIMIT_LINE_ID";
67
68
 
68
- export const getLoggers2 = lazy(function () {
69
- const { IndexedLogs } = require("./IndexedLogs/IndexedLogs") as typeof import("./IndexedLogs/IndexedLogs");
70
-
71
- if (!IndexedLogs) {
72
- setImmediate(() => {
73
- getLoggers2.reset();
74
- });
75
- return undefined;
76
- }
77
-
78
- return {
79
- logLogs: new IndexedLogs<LogDatum>({ name: "logs/log", getTime: x => x.time }),
80
- warnLogs: new IndexedLogs<LogDatum>({ name: "logs/warn", getTime: x => x.time }),
81
- infoLogs: new IndexedLogs<LogDatum>({ name: "logs/info", getTime: x => x.time }),
82
- errorLogs: new IndexedLogs<LogDatum>({ name: "logs/error", getTime: x => x.time }),
83
- };
84
- });
69
+ let loggersObj: {
70
+ logLogs: IndexedLogs<LogDatum>;
71
+ warnLogs: IndexedLogs<LogDatum>;
72
+ infoLogs: IndexedLogs<LogDatum>;
73
+ errorLogs: IndexedLogs<LogDatum>;
74
+ } | undefined;
85
75
  export const getLoggers2Async = lazy(async () => {
86
76
  const { IndexedLogs } = await import("./IndexedLogs/IndexedLogs");
87
- return {
77
+ loggersObj = {
88
78
  logLogs: new IndexedLogs<LogDatum>({ name: "logs/log", getTime: x => x.time }),
89
79
  warnLogs: new IndexedLogs<LogDatum>({ name: "logs/warn", getTime: x => x.time }),
90
80
  infoLogs: new IndexedLogs<LogDatum>({ name: "logs/info", getTime: x => x.time }),
91
81
  errorLogs: new IndexedLogs<LogDatum>({ name: "logs/error", getTime: x => x.time }),
92
82
  };
83
+ return loggersObj;
93
84
  });
85
+ export function getLogLogs(): Promise<IndexedLogs<LogDatum>> {
86
+ return getLoggers2Async().then(x => x.logLogs);
87
+ }
88
+ export function getWarnLogs(): Promise<IndexedLogs<LogDatum>> {
89
+ return getLoggers2Async().then(x => x.warnLogs);
90
+ }
91
+ export function getInfoLogs(): Promise<IndexedLogs<LogDatum>> {
92
+ return getLoggers2Async().then(x => x.infoLogs);
93
+ }
94
+ export function getErrorLogs(): Promise<IndexedLogs<LogDatum>> {
95
+ return getLoggers2Async().then(x => x.errorLogs);
96
+ }
97
+
94
98
 
95
99
  // NOTE: If any message (first param) starts with this, we don't log it to the disk. VERY useful for multi-line logging where it wouldn't make sense in the logs
96
100
  // NOTE: This is visible, otherwise it's easy to accidentally copy it, and not know why the text is behaving strangely (not === other seemingly equal text, etc).
@@ -105,10 +109,6 @@ export function addGlobalContext(fnc: () => { [key: string]: unknown }) {
105
109
  globalContextParts.push(fnc);
106
110
  }
107
111
 
108
- let startupDone = false;
109
- void Promise.resolve().then(() => {
110
- startupDone = true;
111
- });
112
112
 
113
113
 
114
114
 
@@ -136,15 +136,14 @@ export function logDisk(type: "log" | "warn" | "info" | "error", ...args: unknow
136
136
  // NOTE: Local logs now log to their local disk instead of backblaze, so we can log even if in development (they just won't show up on the remote server).
137
137
  //if (isPublic())
138
138
  {
139
- let loggers = startupDone ? getLoggers2() : undefined;
140
- if (!loggers) {
141
- getLoggers2.reset();
142
- setImmediate(() => {
139
+ let promise = getLoggers2Async();
140
+ if (!loggersObj) {
141
+ void promise.finally(() => {
143
142
  logDiskDontShim(type, ...args);
144
143
  });
145
144
  return;
146
145
  }
147
- const { logLogs, warnLogs, infoLogs, errorLogs } = loggers;
146
+ const { logLogs, warnLogs, infoLogs, errorLogs } = loggersObj;
148
147
  if (type === "log") {
149
148
  logLogs.append(logObj);
150
149
  } else if (type === "warn") {
@@ -20,6 +20,10 @@ export function shimConsoleLogs() {
20
20
  let console = globalThis.console;
21
21
  let originalFnc = console[fncName];
22
22
  console[fncName] = (...args: any[]) => {
23
+ if (!(globalThis as any).shimmedConsoleLogs) {
24
+ if (fncName === "info") return;
25
+ return originalFnc(...args);
26
+ }
23
27
  try {
24
28
  if (
25
29
  args.length > 0