querysub 0.155.0 → 0.157.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "querysub",
3
- "version": "0.155.0",
3
+ "version": "0.157.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",
@@ -24,7 +24,7 @@
24
24
  "node-forge": "https://github.com/sliftist/forge#e618181b469b07bdc70b968b0391beb8ef5fecd6",
25
25
  "pako": "^2.1.0",
26
26
  "preact": "^10.11.3",
27
- "socket-function": "^0.88.0",
27
+ "socket-function": "^0.89.0",
28
28
  "terser": "^5.31.0",
29
29
  "typesafecss": "^0.6.3",
30
30
  "yaml": "^2.5.0",
@@ -3,7 +3,8 @@ import { getArchives } from "../-a-archives/archives";
3
3
  import { lazy } from "socket-function/src/caching";
4
4
  import { getStorageDir, getSubFolder } from "../fs";
5
5
  import fs from "fs";
6
- export const keys = getArchives("keys");
6
+ import { isNodeTrue } from "socket-function/src/misc";
7
+ export const keys = isNodeTrue() && getArchives("keys");
7
8
 
8
9
  export const getCloudflareCreds = lazy(async (): Promise<{ key: string; email: string }> => {
9
10
  let credsJSON = await keys.get("cloudflare.json");
@@ -1,7 +1,7 @@
1
1
  import os from "os";
2
2
  import * as fs from "fs";
3
3
  import { cache, lazy } from "socket-function/src/caching";
4
- import { isNode } from "socket-function/src/misc";
4
+ import { isNode, isNodeTrue } from "socket-function/src/misc";
5
5
  import { httpsRequest } from "../https";
6
6
  import { getStorageDir } from "../fs";
7
7
  import { SocketFunction } from "socket-function/SocketFunction";
@@ -16,7 +16,7 @@ const DNS_TTLSeconds = {
16
16
  "A": 60,
17
17
  };
18
18
 
19
- export const keys = getArchives("keys");
19
+ export const keys = isNodeTrue() && getArchives("keys");
20
20
 
21
21
  export const hasDNSWritePermissions = lazy(async () => {
22
22
  if (!isNode()) return false;
@@ -243,13 +243,12 @@ export async function triggerNodeChange() {
243
243
  }));
244
244
  }
245
245
 
246
- let seenNodesForEvilDNS = new Set<string>();
247
246
  async function syncArchives() {
248
247
  if (isServer()) {
249
248
  // Make sure we are present
250
249
  await writeHeartbeat();
251
250
  let nodeIds = await archives().find("");
252
- console.log(green(`Syncing archives`), { nodeIds });
251
+ console.log(green(`Syncing node ids from archives`), { nodeIds });
253
252
  setNodeIds(nodeIds);
254
253
  } else {
255
254
  if (isNoNetwork() || !isNode()) {
@@ -18,7 +18,6 @@ import { logNodeStateStats, logNodeStats } from "../../-0-hooks/hooks";
18
18
  /** Clean up old files after a while */
19
19
  const DEAD_CREATE_THRESHOLD = timeInHour * 12;
20
20
  const ARCHIVE_PROPAGATION_TIME = 5000;
21
- const LOG = false;
22
21
  // NOTE: There isn't a reason for transaction to not apply. If applying throws, we throw,
23
22
  // and can throw infinite times, but if it doesn't throw, it SHOULD be applied. We retry
24
23
  // a few times though, just in case the storage system was having some issues. After enough
@@ -309,9 +308,7 @@ class TransactionLocker {
309
308
  }
310
309
  public async createConfirm(key: string) {
311
310
  let path = this.getConfirmKey(key);
312
- if (LOG) {
313
- console.log(`Creating confirmation for ${key}`);
314
- }
311
+ diskLog(`Creating confirmation for ${key}`);
315
312
  await this.storage.setValue(path, Buffer.from(""));
316
313
  return path;
317
314
  }
@@ -363,9 +360,10 @@ class TransactionLocker {
363
360
  delete: ellipsize(deletes.map(a => debugFileInfo(a.key)).join(","), 50),
364
361
  });
365
362
 
366
- if (LOG) {
367
- console.log(`Writing transaction ${transaction.seqNum} with ${transaction.ops.length} ops`);
368
- }
363
+ diskLog(`Writing transaction`, {
364
+ name,
365
+ ops: transaction.ops.length,
366
+ });
369
367
 
370
368
  let key = `transaction_${name}.transaction`;
371
369
  let strippedTransaction: Transaction = { ops: transaction.ops.map(a => ({ ...a, value: undefined })) };
@@ -490,10 +488,8 @@ class TransactionLocker {
490
488
  while (true) {
491
489
  let result = await tryToRead();
492
490
  if (result) {
493
- if (LOG) {
494
- let timeToRead = Date.now() - startTime;
495
- console.log(`Read data state in ${formatTime(timeToRead)}`);
496
- }
491
+ let timeToRead = Date.now() - startTime;
492
+ diskLog(`Read data state in ${formatTime(timeToRead)}`);
497
493
  return result;
498
494
  }
499
495
  }
@@ -532,20 +528,20 @@ class TransactionLocker {
532
528
  let rawLookup = new Set(Array.from(rawDataFiles).map(a => a.file));
533
529
  // If any creates are not confirmed, it must not have been applied
534
530
  if (transaction.ops.some(a => a.type === "create" && rawLookup.has(a.key) && !confirmedKeys.has(a.key))) {
535
- if (LOG) {
536
- console.log(`Transaction has pending confirmations of creates.`);
537
- let pending = transaction.ops.filter(a => a.type === "create" && rawLookup.has(a.key) && !confirmedKeys.has(a.key));
538
- for (let p of pending) {
539
- console.log(` ${p.key}`);
540
- }
541
- }
531
+ diskLog(`Transaction not applied (has pending confirmations of creates)`, {
532
+ keys: transaction.ops
533
+ .filter(a => a.type === "create" && rawLookup.has(a.key) && !confirmedKeys.has(a.key))
534
+ .map(a => a.key)
535
+ });
542
536
  return false;
543
537
  }
544
538
  // If any deletes still exist, it must not have been applied
545
539
  if (transaction.ops.some(a => a.type === "delete" && confirmedKeys.has(a.key))) {
546
- if (LOG) {
547
- console.log(`transaction has pending deletes`);
548
- }
540
+ diskLog(`Transaction not applied (has pending deletes)`, {
541
+ keys: transaction.ops
542
+ .filter(a => a.type === "delete" && confirmedKeys.has(a.key))
543
+ .map(a => a.key)
544
+ });
549
545
  return false;
550
546
  }
551
547
  return true;
@@ -554,9 +550,9 @@ class TransactionLocker {
554
550
  let createCount = transaction.ops.filter(a => a.type === "create").length;
555
551
  let deleteCount = transaction.ops.filter(a => a.type === "delete").length;
556
552
  let lockedFiles = transaction.lockedFilesMustEqual?.length;
557
- if (LOG) {
558
- console.log(`Applying transaction with ${createCount} creates and ${deleteCount} deletes. ${lockedFiles !== undefined && `Lock state depends on ${lockedFiles} files` || ""}`);
559
- }
553
+ diskLog(`Applying transaction with ${createCount} creates and ${deleteCount} deletes. ${lockedFiles !== undefined && `Lock state depends on ${lockedFiles} files` || ""}`, {
554
+ transactions: transaction.ops.map(x => JSON.stringify(x)),
555
+ });
560
556
  logNodeStats(`archives|TΔ Apply`, formatNumber, 1);
561
557
  let opsRemaining = transaction.ops.slice();
562
558
  // NOTE: Order doesn't matter here. If anything is reading the values
@@ -580,9 +576,9 @@ class TransactionLocker {
580
576
  };
581
577
  await Promise.all(list(CONCURRENT_WRITE_COUNT).map(runThread));
582
578
 
583
- if (LOG) {
584
- console.log(`Applied transaction with ${createCount} creates and ${deleteCount} deletes. ${lockedFiles !== undefined && `Lock state depends on ${lockedFiles} files` || ""}`);
585
- }
579
+ diskLog(`Applied transaction with ${createCount} creates and ${deleteCount} deletes. ${lockedFiles !== undefined && `Lock state depends on ${lockedFiles} files` || ""}`, {
580
+ transactions: transaction.ops.map(x => JSON.stringify(x)),
581
+ });
586
582
  }
587
583
 
588
584
  /** Only returns data files (no transaction files, or confirmations).
@@ -646,9 +642,7 @@ class TransactionLocker {
646
642
  let threshold = activeT.createTime + this.storage.propagationTime;
647
643
  if (Date.now() < threshold) {
648
644
  let waitTime = threshold - Date.now();
649
- if (LOG) {
650
- console.log(`Waiting ${formatTime(waitTime)} for transaction ${activeT.seqNum} to settle.`);
651
- }
645
+ diskLog(`Waiting ${formatTime(waitTime)} for transaction ${activeT.seqNum} to settle.`);
652
646
  await new Promise(resolve => setTimeout(resolve, waitTime));
653
647
  return this.getFilesBase();
654
648
  }
@@ -688,9 +682,7 @@ class TransactionLocker {
688
682
  // triggered (without this check), and deletes all of our files...
689
683
  let unconfirmedOldFiles2 = veryOldFiles.filter(a => !doubleCheckLookup.has(a) && doubleCheckDataFiles.has(a.file));
690
684
  console.warn(red(`Deleted ${unconfirmedOldFiles2.length} very old unconfirmed files`));
691
- if (LOG) {
692
- logNodeStats(`archives|TΔ Delete Old Rejected File`, formatNumber, unconfirmedOldFiles2.length);
693
- }
685
+ diskLog(`archives|TΔ Delete Old Rejected File`, formatNumber, unconfirmedOldFiles2.length);
694
686
  // At the point the file was very old when we started reading, not part of the active transaction.
695
687
  for (let file of unconfirmedOldFiles2) {
696
688
  await this.deleteDataFile(file.file, `old unconfirmed file (${getOwnNodeId()}, ${process.argv[1]})`);
@@ -706,10 +698,8 @@ class TransactionLocker {
706
698
  let oldEnoughConfirms = dataState.rawDataFiles.filter(x => x.file.endsWith(".confirm") && x.createTime < oldThreshold);
707
699
  let deprecatedFiles = oldEnoughConfirms.filter(a => !usedConfirmations.has(a.file));
708
700
  if (deprecatedFiles.length > 0) {
709
- if (LOG) {
710
- console.warn(red(`Deleted ${deprecatedFiles.length} / ${oldEnoughConfirms.length} confirmations, for not having corresponding data files`));
711
- logNodeStats(`archives|TΔ Delete Deprecated Confirm`, formatNumber, deprecatedFiles.length);
712
- }
701
+ console.warn(red(`Deleted ${deprecatedFiles.length} / ${oldEnoughConfirms.length} confirmations, for not having corresponding data files`));
702
+ logNodeStats(`archives|TΔ Delete Deprecated Confirm`, formatNumber, deprecatedFiles.length);
713
703
  for (let file of deprecatedFiles) {
714
704
  await this.storage.deleteKey(file.file);
715
705
  }
@@ -744,12 +734,10 @@ class TransactionLocker {
744
734
 
745
735
  /** If any deleted files were deleted by other transactions, then we will be rejected. */
746
736
  public async addTransaction(transaction: Transaction): Promise<"accepted" | "rejected"> {
747
- if (LOG) {
748
- let dels = transaction.ops.filter(a => a.type === "delete").length;
749
- let creates = transaction.ops.filter(a => a.type === "create").length;
750
- let createBytes = transaction.ops.map(a => a.type === "create" && a.value?.length || 0).reduce((a, b) => a + b, 0);
751
- console.log(blue(`Starting transaction with ${creates} creates and ${dels} deletes, ${formatNumber(createBytes)}B`));
752
- }
737
+ let dels = transaction.ops.filter(a => a.type === "delete").length;
738
+ let creates = transaction.ops.filter(a => a.type === "create").length;
739
+ let createBytes = transaction.ops.map(a => a.type === "create" && a.value?.length || 0).reduce((a, b) => a + b, 0);
740
+ diskLog(`Starting transaction with ${creates} creates and ${dels} deletes, ${formatNumber(createBytes)}B`);
753
741
  transaction = { ...transaction, ops: transaction.ops.slice() };
754
742
  function normalizePath(path: string) {
755
743
  // Replace duplicate slashes with a single slash
@@ -775,9 +763,7 @@ class TransactionLocker {
775
763
  let beforeData = await this.getFilesBase();
776
764
  if (!this.isTransactionValid(transaction, beforeData.dataFiles, beforeData.rawDataFiles)) {
777
765
  logNodeStats(`archives|TΔ Rejected`, formatNumber, 1);
778
- if (LOG) {
779
- console.log(red(`Finished transaction with rejection, ${transaction.ops.length} ops`));
780
- }
766
+ diskLog(`Finished transaction with rejection, ${transaction.ops.length} ops`);
781
767
  return "rejected";
782
768
  }
783
769
 
@@ -786,9 +772,7 @@ class TransactionLocker {
786
772
  let afterData = await this.getFilesBase();
787
773
  if (this.wasTransactionApplied(transaction, afterData.dataFiles, afterData.rawDataFiles)) {
788
774
  logNodeStats(`archives|TΔ Accepted`, formatNumber, 1);
789
- if (LOG) {
790
- console.log(green(`Finished transaction with ${transaction.ops.length} ops`));
791
- }
775
+ diskLog(`Finished transaction with ${transaction.ops.length} ops`);
792
776
  return "accepted";
793
777
  }
794
778
  }
@@ -1,7 +1,3 @@
1
-
2
-
3
- // yarn typenode src\framework-beta\archiveMoveBase\archiveMoveHarness.ts --nonetwork
4
-
5
1
  import { formatNumber, formatTime } from "socket-function/src/formatting/format";
6
2
  import { blue, green, red } from "socket-function/src/formatting/logColors";
7
3
  import { measureBlock, measureWrap } from "socket-function/src/profiling/measure";
@@ -22,6 +18,7 @@ import debugbreak from "debugbreak";
22
18
  let lastFileWrites = new Map<string, string[]>();
23
19
 
24
20
  export async function runArchiveMover(config: {
21
+ debugName: string;
25
22
  // Defaults to getOurAuthorities
26
23
  // NOTE: Runs once per authority, as we want to keep data as sharded as possible
27
24
  authorities?: AuthorityPath[];
@@ -307,10 +304,11 @@ export async function runArchiveMover(config: {
307
304
  || lastResult === "aborted" && green("noop")
308
305
  || red("rejected")
309
306
  );
310
- console.log(`ArchiveMover ${config.runMover.name} on ${authorityDir} in ${formatTime(time)}. Result = ${resultFormat}.`);
307
+ console.log(`ArchiveMover ${config.debugName ?? config.runMover.name} on ${authorityDir} in ${formatTime(time)}. Result = ${resultFormat}.`);
311
308
  if (lastResult === "accepted" && !abortedDueToInsufficientReduction) {
312
309
  console.log(
313
- ` { files: ${formatNumber(inputFiles)}, values: ${formatNumber(inputValueCount)}, bytes: ${formatNumber(inputBytes)} }`
310
+ `ArchiveMover free committed ${config.debugName ?? config.runMover.name} on ${authorityDir}`
311
+ + ` { files: ${formatNumber(inputFiles)}, values: ${formatNumber(inputValueCount)}, bytes: ${formatNumber(inputBytes)} }`
314
312
  + "\n=>\n"
315
313
  + ` { files: ${formatNumber(outputFiles)}, values: ${formatNumber(outputValueCount)}, bytes: ${formatNumber(outputBytes)} }`
316
314
  ,
@@ -542,6 +542,7 @@ export async function runAliveCheckerIteration(config?: {
542
542
 
543
543
  let lockWrites: PathValue[] = [];
544
544
  await runArchiveMover({
545
+ debugName: type,
545
546
  outputType: type,
546
547
  readLiveData: example.unsafeLiveReads,
547
548
  force: config?.force,
@@ -719,7 +720,15 @@ export async function runAliveCheckerIteration(config?: {
719
720
  }
720
721
 
721
722
  let allFrees = new Set(freeLists.flat().flat().map(x => x.value));
723
+ let allValues = values;
722
724
  values = values.filter(x => !allFrees.has(x));
725
+ if (allValues.length !== values.length) {
726
+ console.log("Freeding values due to garbage collection", {
727
+ before: allValues.length,
728
+ after: values.length,
729
+ freed: allValues.length - values.length,
730
+ });
731
+ }
723
732
 
724
733
  // NOTE: We don't notify any servers about this. We will eventually have to write
725
734
  // audit code to periodically check against the disk to get updates.
@@ -23,6 +23,7 @@ import { getControllerNodeId, getControllerNodeIdList } from "../-g-core-values/
23
23
  import { sha256 } from "js-sha256";
24
24
  import os from "os";
25
25
  import { formatTime } from "socket-function/src/formatting/format";
26
+ import path from "path";
26
27
 
27
28
  export type LoadFunctionSpec = {
28
29
  gitURL: string;
@@ -169,7 +170,7 @@ let moduleResolver = async (spec: {
169
170
 
170
171
  // Remove any previous attempt to sync it
171
172
  if (fs.existsSync(repoPath)) {
172
- await fs.promises.rename(repoPath, repoPath.slice(0, -1) + "_" + Date.now());
173
+ await fs.promises.rmdir(repoPath, { recursive: true });
173
174
  }
174
175
  // Clone it
175
176
  await executeCommand("git", ["clone", gitURL, repoPath]);
@@ -178,6 +179,14 @@ let moduleResolver = async (spec: {
178
179
  // Yarn install
179
180
  await executeCommand("yarn", ["install"], { cwd: repoPath });
180
181
 
182
+ // Delete querysub, and replace it with a symlink. Otherwise the synchronization code
183
+ // will run again, and a lot of setup code will run again, etc, and nothing will work correctly.
184
+ let querysubPath = repoPath + "node_modules/querysub";
185
+ await fs.promises.rmdir(querysubPath, { recursive: true });
186
+
187
+ let actualQuerysubPath = path.resolve("./node_modules/querysub");
188
+ await fs.promises.symlink(actualQuerysubPath, querysubPath, "junction");
189
+
181
190
  // Mark it as loaded. If we don't reach this point we will move the folder and try again next time
182
191
  await fs.promises.writeFile(repoPath + loadTimeIndicatorFileName, Date.now() + "");
183
192
 
@@ -186,15 +195,6 @@ let moduleResolver = async (spec: {
186
195
  });
187
196
  }
188
197
 
189
- let querysubPath = repoPath + "node_modules/querysub";
190
- if (fs.existsSync(querysubPath)) {
191
- // By moving querysub, it forces the repo to use the parent querysub. This is nice, as it even
192
- // works when this code is required clientside!
193
- // - Recursive deletes are slow, but moving is fast, so just move it. This repos don't update anyways
194
- // (we have one per hash), so it's not this will fill up with moved files.
195
- await fs.promises.rename(querysubPath, querysubPath + "_" + Date.now());
196
- }
197
-
198
198
  return repoPath;
199
199
  };
200
200
 
@@ -7,7 +7,7 @@ import { getArchivesBackblazePublic } from "../-a-archives/archivesBackBlaze";
7
7
  import { nestArchives } from "../-a-archives/archives";
8
8
  import { SocketFunction } from "socket-function/SocketFunction";
9
9
  import { runInSerial, runInfinitePoll, runInfinitePollCallAtStart } from "socket-function/src/batching";
10
- import { compare, compareArray, timeInMinute } from "socket-function/src/misc";
10
+ import { compare, compareArray, isNodeTrue, timeInMinute } from "socket-function/src/misc";
11
11
  import { cacheLimited, lazy } from "socket-function/src/caching";
12
12
  import { canHaveChildren } from "socket-function/src/types";
13
13
  import { shutdown } from "../diagnostics/periodic";
@@ -29,7 +29,7 @@ import { startEdgeNotifier } from "./edgeClientWatcher";
29
29
  const UPDATE_POLL_INTERVAL = timeInMinute;
30
30
  const DEAD_NODE_COUNT_THRESHOLD = 15;
31
31
 
32
- const edgeNodeStorage = nestArchives("edgenodes/", getArchivesBackblazePublic(getDomain()));
32
+ const edgeNodeStorage = isNodeTrue() && nestArchives("edgenodes/", getArchivesBackblazePublic(getDomain()));
33
33
  const edgeNodeIndexFile = "edge-nodes-index.json";
34
34
 
35
35
  const getEdgeNodeConfig = cacheLimited(10000, async (fileName: string): Promise<EdgeNodeConfig | undefined> => {
@@ -206,6 +206,7 @@ async function runEdgeNodesAliveCheck() {
206
206
  for (let [fileName, count] of Array.from(deadCounts)) {
207
207
  if (count < DEAD_NODE_COUNT_THRESHOLD) continue;
208
208
  console.log(`Node is dead. Removing from edgeNodes.`, { fileName, count });
209
+ deadCounts.delete(fileName);
209
210
  await edgeNodeStorage.del(fileName);
210
211
  }
211
212
  }
@@ -43,7 +43,6 @@ import { measureBlock, measureFnc } from "socket-function/src/profiling/measure"
43
43
  import { delay } from "socket-function/src/batching";
44
44
  import { MaybePromise } from "socket-function/src/types";
45
45
  import { devDebugbreak, getDomain, isDevDebugbreak, isDynamicallyLoading, isNoNetwork, isPublic } from "../config";
46
- import { hookErrors } from "../diagnostics/errorLogs/hookErrors";
47
46
  import { Schema2, Schema2T, t } from "../2-proxy/schema2";
48
47
  import { CALL_PERMISSIONS_KEY } from "./permissionsShared";
49
48
  import { isDynamicModule } from "../3-path-functions/pathFunctionLoader";
@@ -953,9 +952,12 @@ setImmediate(() => {
953
952
  });
954
953
  });
955
954
 
956
- setImmediate(() => {
957
- hookErrors();
958
- });
955
+ if (isNode()) {
956
+ setImmediate(async () => {
957
+ const { hookErrors } = await import("../diagnostics/errorLogs/hookErrors");
958
+ hookErrors();
959
+ });
960
+ }
959
961
 
960
962
  setImmediate(async () => {
961
963
  // Import, so it registers addStatPeriodic
@@ -10,6 +10,7 @@ const MERGE_DELAY = timeInHour;
10
10
 
11
11
  async function mergeFiles() {
12
12
  await runArchiveMover({
13
+ debugName: "merge",
13
14
  outputType: "merge",
14
15
  async runMover(config) {
15
16
  let values = config.values;
package/src/config.ts CHANGED
@@ -62,7 +62,7 @@ export function authorityRaw() {
62
62
 
63
63
  export function isPublic() {
64
64
  if (!isNode()) {
65
- return !location.hostname.startsWith("noproxy.");
65
+ return !location.hostname.startsWith("127-0-0-1.");
66
66
  }
67
67
  return !!yargObj.public;
68
68
  }
@@ -19,7 +19,6 @@ import { atomic, doAtomicWrites } from "../../2-proxy/PathValueProxyWatcher";
19
19
  import { isClient } from "../../config2";
20
20
  import { ControllerPick, SocketRegistered } from "socket-function/SocketFunctionTypes";
21
21
 
22
-
23
22
  const NOTIFY_HISTORY = timeInDay * 7;
24
23
  // Only error / fatal are tracked, for now... we might also add warns?
25
24
  const logIssueNotifyTypes: LogType[] = ["error", "fatal"];
@@ -14,7 +14,6 @@ import { ButtonSelector } from "../../library-components/ButtonSelector";
14
14
  import { Icon } from "../../library-components/icons";
15
15
  import { MeasuredDiv } from "../../library-components/MeasuredDiv";
16
16
 
17
-
18
17
  type RangeSize = "lastYear" | "lastMonth" | "lastWeek" | "lastDay" | "lastHour";
19
18
  export let logNowTime = Date.now();
20
19
  const logFilterStart = new URLParam<number | RangeSize>("logFilterStartTime", "lastWeek");
@@ -18,7 +18,6 @@ import { getNextTime } from "../0-path-value-core/pathValueCore";
18
18
  import { encodeArgs } from "../3-path-functions/PathFunctionHelpers";
19
19
  import { CallSpec, getCurrentCall, getCurrentCallAllowUndefined, overrideCurrentCall } from "../3-path-functions/PathFunctionRunner";
20
20
  import { createLazyComponent } from "../library-components/LazyComponent";
21
- import { LogNotify } from "./errorLogs/LogNotify";
22
21
  import { isTrusted } from "../-d-trust/NetworkTrust2";
23
22
  import { devDebugbreak, getDomain } from "../config";
24
23
  import { getCallWrites } from "../4-querysub/querysubPrediction";
@@ -27,7 +26,9 @@ import * as hooks from "../-0-hooks/hooks";
27
26
  import { addComponentUI } from "../5-diagnostics/qreactDebug";
28
27
  import { addComponentButton } from "../5-diagnostics/qreactDebug";
29
28
  import { closeAllModals } from "../5-diagnostics/Modal";
29
+ import { delay } from "socket-function/src/batching";
30
30
 
31
+ const LogNotify = createLazyComponent(() => import("./errorLogs/LogNotify"))("LogNotify");
31
32
 
32
33
  export const managementPageURL = new URLParam("managementpage", "");
33
34
  export const showingManagementURL = new URLParam("showingmanagement", false);
@@ -134,14 +135,21 @@ export async function registerManagementPages2(config: {
134
135
  __schema = config.schema;
135
136
 
136
137
  setImmediate(async () => {
137
- if (!isNode() && !await ManagementController.nodes[getBrowserUrlNode()].isManagementUser()) return;
138
+ if (!isNode() &&
139
+ !await ManagementController.nodes[getBrowserUrlNode()].isManagementUser()
140
+ && new URLSearchParams(window.location.search).get("dropto") !== "user"
141
+ ) return;
142
+
143
+ // Wait, so the import system knows the modules are async imports
144
+ await delay(0);
145
+
146
+ const ComponentSyncStats = createLazyComponent(() => import("./misc-pages/ComponentSyncStats"))("ComponentSyncStats");
138
147
 
139
- const { filtersURL } = await import("./misc-pages/archiveViewerShared");
140
- const { watchValueType } = await import("./misc-pages/LocalWatchViewer");
141
- const { ComponentSyncStats } = await import("./misc-pages/ComponentSyncStats");
142
148
  addComponentButton({
143
149
  title: "View in Path Navigator",
144
- callback(component) {
150
+ async callback(component) {
151
+ const { filtersURL } = await import("./misc-pages/archiveViewerShared");
152
+ const { watchValueType } = await import("./misc-pages/LocalWatchViewer");
145
153
  filtersURL.value[nextId()] = { column: "path", match: component.debugName, type: "and" };
146
154
  managementPageURL.value = "LocalWatchViewer";
147
155
  watchValueType.value = "component";
@@ -168,6 +176,8 @@ export async function registerManagementPages2(config: {
168
176
  }
169
177
 
170
178
  if (isServer()) {
179
+ // Wait, so the import system knows the modules are async imports
180
+ await delay(0);
171
181
  for (let page of inputPages) {
172
182
  // NOTE: If we split this into a module for component/controller, we need to make sure we
173
183
  // import both serverside, so we can whitelist them for import clientside.
@@ -114,11 +114,11 @@ export function generateLoginEmail(config: {
114
114
  <td {...{ "nowrap": true }} style={{ paddingLeft: "40px", }}></td>
115
115
  </tr></tbody></table>
116
116
  <table><tbody><tr><td style={{ paddingTop: "20px" }}></td></tr></tbody></table>
117
- {line(<div style={{ color: "rgb(140, 140, 140)", width: "100%" }}><b>Do NOT share or forward this email with anyone.</b></div>)}
117
+ {line(<div style={{ color: "rgb(140, 140, 140)", width: "100%" }}><b>Do NOT share or forward this email to anyone.</b></div>)}
118
118
  <table><tbody><tr><td style={{ paddingTop: "10px" }}></td></tr></tbody></table>
119
119
  {line(<div style={{ color: "rgb(140, 140, 140)", width: "100%" }}>
120
- This link will only work with the current device and browser.
121
- This link can only be used one time, until {new Date(config.timeoutTime).toLocaleString()}.
120
+ This link will only work in the current device and browser.
121
+ This link can only be used one time, and will expire at {new Date(config.timeoutTime).toLocaleString()}.
122
122
  </div>)}
123
123
  </>
124
124
  );