querysub 0.403.0 → 0.405.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 (108) hide show
  1. package/.cursorrules +2 -0
  2. package/bin/audit-imports.js +4 -0
  3. package/bin/join.js +1 -1
  4. package/package.json +7 -4
  5. package/spec.txt +77 -0
  6. package/src/-a-archives/archiveCache.ts +9 -4
  7. package/src/-a-archives/archivesBackBlaze.ts +1039 -1039
  8. package/src/-a-auth/certs.ts +0 -12
  9. package/src/-c-identity/IdentityController.ts +12 -3
  10. package/src/-f-node-discovery/NodeDiscovery.ts +32 -26
  11. package/src/-g-core-values/NodeCapabilities.ts +12 -2
  12. package/src/0-path-value-core/AuthorityLookup.ts +239 -0
  13. package/src/0-path-value-core/LockWatcher2.ts +150 -0
  14. package/src/0-path-value-core/PathRouter.ts +543 -0
  15. package/src/0-path-value-core/PathRouterRouteOverride.ts +72 -0
  16. package/src/0-path-value-core/PathRouterServerAuthoritySpec.tsx +73 -0
  17. package/src/0-path-value-core/PathValueCommitter.ts +222 -488
  18. package/src/0-path-value-core/PathValueController.ts +277 -239
  19. package/src/0-path-value-core/PathWatcher.ts +534 -0
  20. package/src/0-path-value-core/ShardPrefixes.ts +31 -0
  21. package/src/0-path-value-core/ValidStateComputer.ts +303 -0
  22. package/src/0-path-value-core/archiveLocks/ArchiveLocks.ts +1 -1
  23. package/src/0-path-value-core/archiveLocks/ArchiveLocks2.ts +80 -44
  24. package/src/0-path-value-core/archiveLocks/archiveSnapshots.ts +13 -16
  25. package/src/0-path-value-core/auditLogs.ts +2 -0
  26. package/src/0-path-value-core/hackedPackedPathParentFiltering.ts +97 -0
  27. package/src/0-path-value-core/pathValueArchives.ts +491 -492
  28. package/src/0-path-value-core/pathValueCore.ts +195 -1496
  29. package/src/0-path-value-core/startupAuthority.ts +74 -0
  30. package/src/1-path-client/RemoteWatcher.ts +90 -82
  31. package/src/1-path-client/pathValueClientWatcher.ts +808 -815
  32. package/src/2-proxy/PathValueProxyWatcher.ts +10 -8
  33. package/src/2-proxy/archiveMoveHarness.ts +182 -214
  34. package/src/2-proxy/garbageCollection.ts +9 -8
  35. package/src/2-proxy/schema2.ts +21 -1
  36. package/src/3-path-functions/PathFunctionHelpers.ts +206 -180
  37. package/src/3-path-functions/PathFunctionRunner.ts +943 -766
  38. package/src/3-path-functions/PathFunctionRunnerMain.ts +5 -3
  39. package/src/3-path-functions/pathFunctionLoader.ts +2 -2
  40. package/src/3-path-functions/syncSchema.ts +596 -521
  41. package/src/4-deploy/deployFunctions.ts +19 -4
  42. package/src/4-deploy/deployGetFunctionsInner.ts +8 -2
  43. package/src/4-deploy/deployMain.ts +51 -68
  44. package/src/4-deploy/edgeClientWatcher.tsx +6 -1
  45. package/src/4-deploy/edgeNodes.ts +2 -2
  46. package/src/4-dom/qreact.tsx +2 -4
  47. package/src/4-dom/qreactTest.tsx +7 -13
  48. package/src/4-querysub/Querysub.ts +21 -8
  49. package/src/4-querysub/QuerysubController.ts +45 -29
  50. package/src/4-querysub/permissions.ts +2 -2
  51. package/src/4-querysub/querysubPrediction.ts +80 -70
  52. package/src/4-querysub/schemaHelpers.ts +5 -1
  53. package/src/5-diagnostics/GenericFormat.tsx +14 -9
  54. package/src/archiveapps/archiveGCEntry.tsx +9 -2
  55. package/src/archiveapps/archiveJoinEntry.ts +96 -84
  56. package/src/bits.ts +19 -0
  57. package/src/config.ts +21 -3
  58. package/src/config2.ts +23 -48
  59. package/src/deployManager/components/DeployPage.tsx +7 -3
  60. package/src/deployManager/machineSchema.ts +4 -1
  61. package/src/diagnostics/ActionsHistory.ts +3 -8
  62. package/src/diagnostics/AuditLogPage.tsx +2 -3
  63. package/src/diagnostics/FunctionCallInfo.tsx +141 -0
  64. package/src/diagnostics/FunctionCallInfoState.ts +162 -0
  65. package/src/diagnostics/MachineThreadInfo.tsx +1 -1
  66. package/src/diagnostics/NodeViewer.tsx +37 -48
  67. package/src/diagnostics/SyncTestPage.tsx +241 -0
  68. package/src/diagnostics/auditImportViolations.ts +185 -0
  69. package/src/diagnostics/listenOnDebugger.ts +3 -3
  70. package/src/diagnostics/logs/IndexedLogs/BufferUnitSet.ts +10 -4
  71. package/src/diagnostics/logs/IndexedLogs/IndexedLogs.ts +2 -2
  72. package/src/diagnostics/logs/IndexedLogs/LogViewer3.tsx +24 -22
  73. package/src/diagnostics/logs/IndexedLogs/moveIndexLogsToPublic.ts +1 -1
  74. package/src/diagnostics/logs/diskLogGlobalContext.ts +1 -0
  75. package/src/diagnostics/logs/errorNotifications2/logWatcher.ts +1 -3
  76. package/src/diagnostics/logs/lifeCycleAnalysis/LifeCycleEntryEditor.tsx +34 -16
  77. package/src/diagnostics/logs/lifeCycleAnalysis/LifeCycleEntryReadMode.tsx +4 -6
  78. package/src/diagnostics/logs/lifeCycleAnalysis/LifeCycleInstanceTableView.tsx +36 -5
  79. package/src/diagnostics/logs/lifeCycleAnalysis/LifeCyclePage.tsx +19 -5
  80. package/src/diagnostics/logs/lifeCycleAnalysis/LifeCycleRenderer.tsx +15 -7
  81. package/src/diagnostics/logs/lifeCycleAnalysis/NestedLifeCycleInfo.tsx +28 -106
  82. package/src/diagnostics/logs/lifeCycleAnalysis/lifeCycleMatching.ts +2 -0
  83. package/src/diagnostics/logs/lifeCycleAnalysis/lifeCycleMisc.ts +0 -0
  84. package/src/diagnostics/logs/lifeCycleAnalysis/lifeCycleSearch.tsx +18 -7
  85. package/src/diagnostics/logs/lifeCycleAnalysis/lifeCycles.tsx +3 -0
  86. package/src/diagnostics/managementPages.tsx +10 -3
  87. package/src/diagnostics/misc-pages/ArchiveViewer.tsx +20 -26
  88. package/src/diagnostics/misc-pages/ArchiveViewerTree.tsx +6 -4
  89. package/src/diagnostics/misc-pages/ComponentSyncStats.tsx +2 -2
  90. package/src/diagnostics/misc-pages/LocalWatchViewer.tsx +7 -9
  91. package/src/diagnostics/misc-pages/SnapshotViewer.tsx +23 -12
  92. package/src/diagnostics/misc-pages/archiveViewerShared.tsx +1 -1
  93. package/src/diagnostics/pathAuditer.ts +486 -0
  94. package/src/diagnostics/pathAuditerCallback.ts +20 -0
  95. package/src/diagnostics/watchdog.ts +8 -1
  96. package/src/library-components/URLParam.ts +1 -1
  97. package/src/misc/hash.ts +1 -0
  98. package/src/path.ts +21 -7
  99. package/src/server.ts +54 -47
  100. package/src/user-implementation/loginEmail.tsx +1 -1
  101. package/tempnotes.txt +65 -0
  102. package/test.ts +298 -97
  103. package/src/0-path-value-core/NodePathAuthorities.ts +0 -1057
  104. package/src/0-path-value-core/PathController.ts +0 -1
  105. package/src/5-diagnostics/diskValueAudit.ts +0 -218
  106. package/src/5-diagnostics/memoryValueAudit.ts +0 -438
  107. package/src/archiveapps/archiveMergeEntry.tsx +0 -48
  108. package/src/archiveapps/lockTest.ts +0 -127
@@ -1,7 +1,8 @@
1
1
  import { formatPercent, formatNumber } from "socket-function/src/formatting/format";
2
2
  import { red } from "socket-function/src/formatting/logColors";
3
- import { measureFnc } from "socket-function/src/profiling/measure";
3
+ import { measureFnc, measureWrap } from "socket-function/src/profiling/measure";
4
4
  import { Unit, remapUnit } from "./BufferIndexHelpers";
5
+ import { devDebugbreak } from "../../../config";
5
6
 
6
7
  export class UnitSet {
7
8
  // Hash-based set for checking unit membership (no positions stored)
@@ -27,7 +28,7 @@ export class UnitSet {
27
28
  return Buffer.from(new Uint32Array([0]).buffer);
28
29
  }
29
30
 
30
- function tryEncodeWithRatio(estimatedUniqueRatio: number): { hashTable: Uint32Array; collisions: number; totalInserts: number; } | undefined {
31
+ const tryEncodeWithRatio = measureWrap(function tryEncodeWithRatio(estimatedUniqueRatio: number): { hashTable: Uint32Array; collisions: number; totalInserts: number; } | undefined {
31
32
  const HASH_FACTOR = 2;
32
33
  const estimatedUnique = Math.max(Math.ceil(totalUnits / estimatedUniqueRatio), 16);
33
34
  const desiredCapacity = estimatedUnique * HASH_FACTOR;
@@ -87,7 +88,7 @@ export class UnitSet {
87
88
  }
88
89
 
89
90
  return { hashTable, collisions, totalInserts };
90
- }
91
+ });
91
92
 
92
93
  // Try with increasingly conservative ratios until we succeed
93
94
  let ratio = 1000;
@@ -95,7 +96,12 @@ export class UnitSet {
95
96
  while (true) {
96
97
  result = tryEncodeWithRatio(ratio);
97
98
  if (result) break;
98
- ratio /= 2;
99
+ if (ratio === 0.1) {
100
+ devDebugbreak();
101
+ console.error(`Failed to encode UnitSet even with max ratio of ${ratio}. Aborting. What kind of data is this?`);
102
+ return Buffer.alloc(0);
103
+ }
104
+ ratio = Math.max(ratio / 2, 0.1);
99
105
  }
100
106
 
101
107
  const collisionRate = result.totalInserts > 0 ? result.collisions / result.totalInserts : 0;
@@ -541,7 +541,7 @@ export class IndexedLogs<T> {
541
541
  if (config.params.searchFromStart) {
542
542
  sort(paths, x => x.startTime);
543
543
  } else {
544
- sort(paths, x => - x.startTime);
544
+ sort(paths, x => -x.startTime);
545
545
  }
546
546
 
547
547
  let localPaths = paths.filter(x => x.logCount === undefined);
@@ -605,7 +605,7 @@ export class IndexedLogs<T> {
605
605
  },
606
606
  keepIterating: () => !results.cancel && progressTracker.isSourceRelevant(path),
607
607
  onResult: (match: Buffer) => {
608
- if (results.matchCount >= config.params.limit) return;
608
+ // NOTE: We absolutely cannot do any limiting here, as that would just result in a lot of values that might be newer than the old values being discarded. Each loop itself has to do some kind of limiting as it knows how many it's found in its specific loop, and it knows the direction it's iterating and the direction of the values.
609
609
  progressTracker.addResult(match, path);
610
610
  },
611
611
  results,
@@ -30,7 +30,7 @@ import { readProductionLogsURL, searchTextURL } from "./LogViewerParams";
30
30
  import { RenderSearchStats } from "./RenderSearchStats";
31
31
  import { LifeCyclesController, LifeCycle, LifeCycleEntry } from "../lifeCycleAnalysis/lifeCycles";
32
32
  import { getLifecycleMatchesForDatum } from "../lifeCycleAnalysis/lifeCycleMatching";
33
- import { lifecycleIdURL, additionalSearchURL } from "../lifeCycleAnalysis/LifeCyclePage";
33
+ import { lifecycleIdURL, additionalSearchURL, phase2AdditionalSearchURL } from "../lifeCycleAnalysis/LifeCyclePage";
34
34
  import { ATag } from "../../../library-components/ATag";
35
35
  import { SocketFunction } from "socket-function/SocketFunction";
36
36
  import { managementPageURL } from "../../managementPages";
@@ -39,6 +39,10 @@ import { formatSearchString } from "./LogViewerParams";
39
39
 
40
40
  let excludePendingResults = new URLParam("excludePendingResults", false);
41
41
  let limitURL = new URLParam("limit", 16);
42
+ let enableLogsURL = new URLParam("enableLogs", true);
43
+ let enableInfosURL = new URLParam("enableInfos", true);
44
+ let enableWarningsURL = new URLParam("enableWarnings", true);
45
+ let enableErrorsURL = new URLParam("enableErrors", true);
42
46
 
43
47
  let savedPathsURL = new URLParam("savedPaths", "");
44
48
  let selectedFieldsURL = new URLParam("selectedFields", {} as Record<string, boolean>);
@@ -58,10 +62,6 @@ const defaultSelectedFields = {
58
62
  export class LogViewer3 extends qreact.Component {
59
63
  static renderInProgress = true;
60
64
  state = t.state({
61
- enableLogs: t.boolean(true),
62
- enableInfos: t.boolean(true),
63
- enableWarnings: t.boolean(true),
64
- enableErrors: t.boolean(true),
65
65
  results: t.atomic<LogDatum[]>([]),
66
66
  searching: t.boolean,
67
67
  searchingLogs: t.boolean,
@@ -137,10 +137,10 @@ export class LogViewer3 extends qreact.Component {
137
137
  let loggers = await getLoggers2Async();
138
138
  let selectedLoggers: typeof loggers.logLogs[] = [];
139
139
  Querysub.localRead(() => {
140
- if (this.state.enableLogs) selectedLoggers.push(loggers.logLogs);
141
- if (this.state.enableInfos) selectedLoggers.push(loggers.infoLogs);
142
- if (this.state.enableWarnings) selectedLoggers.push(loggers.warnLogs);
143
- if (this.state.enableErrors) selectedLoggers.push(loggers.errorLogs);
140
+ if (enableLogsURL.value) selectedLoggers.push(loggers.logLogs);
141
+ if (enableInfosURL.value) selectedLoggers.push(loggers.infoLogs);
142
+ if (enableWarningsURL.value) selectedLoggers.push(loggers.warnLogs);
143
+ if (enableErrorsURL.value) selectedLoggers.push(loggers.errorLogs);
144
144
  });
145
145
 
146
146
  if (selectedLoggers.length === 0) {
@@ -215,19 +215,19 @@ export class LogViewer3 extends qreact.Component {
215
215
 
216
216
  let selectedLoggers: typeof loggers.logLogs[] = [];
217
217
  Querysub.localRead(() => {
218
- if (this.state.enableLogs) {
218
+ if (enableLogsURL.value) {
219
219
  selectedLoggers.push(loggers.logLogs);
220
220
  this.state.searchingLogs = true;
221
221
  }
222
- if (this.state.enableInfos) {
222
+ if (enableInfosURL.value) {
223
223
  selectedLoggers.push(loggers.infoLogs);
224
224
  this.state.searchingInfos = true;
225
225
  }
226
- if (this.state.enableWarnings) {
226
+ if (enableWarningsURL.value) {
227
227
  selectedLoggers.push(loggers.warnLogs);
228
228
  this.state.searchingWarnings = true;
229
229
  }
230
- if (this.state.enableErrors) {
230
+ if (enableErrorsURL.value) {
231
231
  selectedLoggers.push(loggers.errorLogs);
232
232
  this.state.searchingErrors = true;
233
233
  }
@@ -560,9 +560,10 @@ export class LogViewer3 extends qreact.Component {
560
560
  managementPageURL.getOverride("LifeCyclePage"),
561
561
  lifecycleIdURL.getOverride(match.lifecycle.id),
562
562
  additionalSearchURL.getOverride(formatSearchString(match.datum)),
563
+ phase2AdditionalSearchURL.getOverride(""),
563
564
  startTimeParam.getOverride(datum.time - timeInHour),
564
565
  endTimeParam.getOverride(datum.time + timeInHour),
565
- ]}>
566
+ ]} title={match.entry.matchPattern}>
566
567
  View {JSON.stringify(match.lifecycle.title)}
567
568
  </ATag>
568
569
  </div>
@@ -571,6 +572,7 @@ export class LogViewer3 extends qreact.Component {
571
572
  managementPageURL.getOverride("LifeCyclePage"),
572
573
  lifecycleIdURL.getOverride(firstMatch.lifecycle.id),
573
574
  additionalSearchURL.getOverride(searchFilter),
575
+ phase2AdditionalSearchURL.getOverride(""),
574
576
  startTimeParam.getOverride(minTime - timeInHour),
575
577
  endTimeParam.getOverride(maxTime + timeInHour),
576
578
  ]}>
@@ -690,40 +692,40 @@ export class LogViewer3 extends qreact.Component {
690
692
  <div className={css.hbox(10)}>
691
693
  <Button
692
694
  onClick={() => {
693
- this.state.enableLogs = !this.state.enableLogs;
695
+ enableLogsURL.value = !enableLogsURL.value;
694
696
  this.clearPaths();
695
697
  }}
696
- hue={this.state.enableLogs && 210 || undefined}
698
+ hue={enableLogsURL.value && 210 || undefined}
697
699
  className={this.state.searchingLogs && css.opacity(0.5) || undefined}
698
700
  >
699
701
  Logs
700
702
  </Button>
701
703
  <Button
702
704
  onClick={() => {
703
- this.state.enableInfos = !this.state.enableInfos;
705
+ enableInfosURL.value = !enableInfosURL.value;
704
706
  this.clearPaths();
705
707
  }}
706
- hue={this.state.enableInfos && 200 || undefined}
708
+ hue={enableInfosURL.value && 200 || undefined}
707
709
  className={this.state.searchingInfos && css.opacity(0.5) || undefined}
708
710
  >
709
711
  Infos
710
712
  </Button>
711
713
  <Button
712
714
  onClick={() => {
713
- this.state.enableWarnings = !this.state.enableWarnings;
715
+ enableWarningsURL.value = !enableWarningsURL.value;
714
716
  this.clearPaths();
715
717
  }}
716
- hue={this.state.enableWarnings && 40 || undefined}
718
+ hue={enableWarningsURL.value && 40 || undefined}
717
719
  className={this.state.searchingWarnings && css.opacity(0.5) || undefined}
718
720
  >
719
721
  Warnings
720
722
  </Button>
721
723
  <Button
722
724
  onClick={() => {
723
- this.state.enableErrors = !this.state.enableErrors;
725
+ enableErrorsURL.value = !enableErrorsURL.value;
724
726
  this.clearPaths();
725
727
  }}
726
- hue={this.state.enableErrors ? 0 : undefined}
728
+ hue={enableErrorsURL.value ? 0 : undefined}
727
729
  className={this.state.searchingErrors && css.opacity(0.5) || undefined}
728
730
  >
729
731
  Errors
@@ -129,7 +129,7 @@ export async function moveLogsToPublic(config: {
129
129
 
130
130
  if (!await tryToGetMoveLock()) return;
131
131
 
132
- console.log(magenta(`Moving ${localPaths.length} log files to public (${blue(config.loggerName)})`), { loggerName: config.loggerName, localPaths });
132
+ console.log(magenta(`Moving ${localPaths.length} log files to public (${blue(config.loggerName)})`), { loggerName: config.loggerName });
133
133
 
134
134
  let byStartTime = keyByArray(localPaths, x => x.startTime);
135
135
 
@@ -24,6 +24,7 @@ export function addBuiltInContext() {
24
24
  __externalIP: cachedExternalIP(),
25
25
  __os: process.platform,
26
26
  __pid: process.pid,
27
+ __call: SocketFunction.TOTAL_CALLS,
27
28
  };
28
29
  });
29
30
  const getHostname = lazy(() => child_process.execSync("hostname").toString().trim());
@@ -1,5 +1,5 @@
1
1
  import { SocketFunction } from "socket-function/SocketFunction";
2
- import { isNodeIdLocal, isOwnNodeId, watchDeltaNodeIds } from "../../../-f-node-discovery/NodeDiscovery";
2
+ import { isOwnNodeId, watchDeltaNodeIds } from "../../../-f-node-discovery/NodeDiscovery";
3
3
  import { IndexedLogs, loggerByName } from "../IndexedLogs/IndexedLogs";
4
4
  import { nextId } from "socket-function/src/misc";
5
5
  import { assertIsManagementUser, isManagementUser } from "../../managementPages";
@@ -30,8 +30,6 @@ export async function* watchAllValues<T>(logs: IndexedLogs<T>): AsyncGenerator<T
30
30
  watchDeltaNodeIds((delta) => {
31
31
  for (let nodeId of delta.newNodeIds) {
32
32
  if (isOwnNodeId(nodeId)) continue;
33
- let isPublicNode = !isNodeIdLocal(nodeId);
34
- if (isPublicNode !== isPublic()) continue;
35
33
  process.stdout.write(`Watching errors for ${logs.config.name} on node ${nodeId}\n`);
36
34
  ignoreErrors(ErrorNotificationControllerRegistered.nodes[nodeId].watchErrors({
37
35
  loggerName: logs.config.name,
@@ -229,7 +229,9 @@ export class LifeCycleEntryEditor extends qreact.Component<{
229
229
  hue={0}
230
230
  onClick={() => {
231
231
  let updatedLifeCycle = deepCloneJSON(lifeCycle);
232
- updatedLifeCycle.entries[entryIndex].groupByKeys.splice(keyIdx, 1);
232
+ updatedLifeCycle.entries.forEach((e, idx) => {
233
+ e.groupByKeys = e.groupByKeys.filter(k => k.ourKey !== keyConfig.ourKey);
234
+ });
233
235
  Querysub.onCommitFinished(async () => {
234
236
  await this.controller.setLifeCycle.promise(updatedLifeCycle);
235
237
  });
@@ -256,7 +258,7 @@ export class LifeCycleEntryEditor extends qreact.Component<{
256
258
  </div>
257
259
 
258
260
  <div className={css.vbox(4)}>
259
- {Object.entries(entry.variables).map(([key, varData]) => {
261
+ {Object.entries(entry.variables).map(([key, varData], varIdx) => {
260
262
  let keyExistsInAllOtherEntries = lifeCycle.entries.every((e, idx) => {
261
263
  if (idx === entryIndex) return true;
262
264
  return key in e.variables;
@@ -281,21 +283,37 @@ export class LifeCycleEntryEditor extends qreact.Component<{
281
283
  >
282
284
  Copy to All
283
285
  </Button>
284
- <Button hue={160} onClick={() => {
285
- let updatedLifeCycle = deepCloneJSON(lifeCycle);
286
- updatedLifeCycle.entries.forEach((e) => {
287
- if (key in e.variables) {
288
- let varData = e.variables[key];
289
- delete e.variables[key];
290
- e.variables = { [key]: varData, ...e.variables };
286
+ <InputLabel
287
+ label="Pos"
288
+ number
289
+ value={varIdx.toString()}
290
+ onChangeValue={(value) => {
291
+ let newPos = parseInt(value);
292
+ if (isNaN(newPos)) return;
293
+
294
+ let updatedLifeCycle = deepCloneJSON(lifeCycle);
295
+ for (let e of updatedLifeCycle.entries) {
296
+ if (!(key in e.variables)) continue;
297
+ let keys = Object.keys(e.variables);
298
+ let currentPos = keys.indexOf(key);
299
+ if (currentPos < 0) continue;
300
+
301
+ let usedNewPos = newPos;
302
+ if (currentPos < usedNewPos) {
303
+ usedNewPos--;
304
+ }
305
+
306
+ keys.splice(currentPos, 1);
307
+ keys.splice(usedNewPos, 0, key);
308
+
309
+ e.variables = Object.fromEntries(keys.map(k => [k, e.variables[k]]));
291
310
  }
292
- });
293
- Querysub.onCommitFinished(async () => {
294
- await this.controller.setLifeCycle.promise(updatedLifeCycle);
295
- });
296
- }}>
297
- Move to start
298
- </Button>
311
+ Querysub.onCommitFinished(async () => {
312
+ await this.controller.setLifeCycle.promise(updatedLifeCycle);
313
+ });
314
+ }}
315
+ className={css.width(60)}
316
+ />
299
317
  <span className={css.minWidth(150).boldStyle}>{key}</span>
300
318
  <InputLabel
301
319
  placeholder="Variable title"
@@ -9,6 +9,7 @@ import { niceStringify } from "../../../niceStringify";
9
9
  import { LogDatum } from "../diskLogger";
10
10
  import { LifeCycle, LifeCycleEntry, LifeCyclesController, getVariables } from "./lifeCycles";
11
11
  import { NestedLifeCycleInfo } from "./NestedLifeCycleInfo";
12
+ import { formatValue } from "../../../5-diagnostics/GenericFormat";
12
13
 
13
14
  export class LifeCycleEntryReadMode extends qreact.Component<{
14
15
  lifeCycle: LifeCycle;
@@ -89,13 +90,10 @@ export class LifeCycleEntryReadMode extends qreact.Component<{
89
90
  let renderNestedInfo = (key: string, value: unknown) => {
90
91
  if (value === undefined) return null;
91
92
 
92
- let isGroupByKey = Array.isArray(entry.groupByKeys) && entry.groupByKeys.some(k => k.ourKey === key);
93
- let aliases = isGroupByKey && entry.groupByKeys.find(k => k.ourKey === key)?.aliases || entry.variables[key]?.aliases || [];
94
-
95
93
  return <NestedLifeCycleInfo
96
94
  keyName={key}
97
95
  value={value}
98
- aliases={aliases}
96
+ entry={entry}
99
97
  currentLifeCycleId={lifeCycle.id}
100
98
  />;
101
99
  };
@@ -116,7 +114,7 @@ export class LifeCycleEntryReadMode extends qreact.Component<{
116
114
  <span className={css.minWidth(150).colorhsl(0, 0, 50)}>
117
115
  {title || key}{title && ` (${key})` || ""}:
118
116
  </span>
119
- <span className={css.whiteSpace("pre-wrap")}>{niceStringify(value)}</span>
117
+ <span className={css.whiteSpace("pre-wrap")}>{formatValue(value)}</span>
120
118
  {renderNestedInfo(key, value)}
121
119
  </div>;
122
120
  })}
@@ -130,7 +128,7 @@ export class LifeCycleEntryReadMode extends qreact.Component<{
130
128
  <span className={css.minWidth(150).colorhsl(0, 0, 50)}>
131
129
  {key}
132
130
  </span>
133
- <span className={css.whiteSpace("pre-wrap")}>{niceStringify(value)}</span>
131
+ <span className={css.whiteSpace("pre-wrap")}>{String(value)}</span>
134
132
  {renderNestedInfo(key, value)}
135
133
  </div>;
136
134
  });
@@ -1,8 +1,11 @@
1
1
  import { qreact } from "../../../4-dom/qreact";
2
- import { Table } from "../../../5-diagnostics/Table";
2
+ import { ColumnsType, Table } from "../../../5-diagnostics/Table";
3
3
  import { formatValue } from "../../../5-diagnostics/GenericFormat";
4
- import { LifeCycle, getVariables } from "./lifeCycles";
4
+ import { LifeCycle, LifeCycleEntry, getVariables } from "./lifeCycles";
5
5
  import { LifecycleInstance } from "./lifeCycleSearch";
6
+ import { Button } from "../../../library-components/Button";
7
+ import { NestedLifeCycleInfo } from "./NestedLifeCycleInfo";
8
+ import { formatDateTimeDetailed } from "socket-function/src/formatting/format";
6
9
 
7
10
  export class LifeCycleInstanceTableView extends qreact.Component<{
8
11
  lifeCycle: LifeCycle;
@@ -32,8 +35,10 @@ export class LifeCycleInstanceTableView extends qreact.Component<{
32
35
  let entry = lifeCycle.entries.find(e => e.matchPattern === entryData.matchPattern);
33
36
  let entryTitle = entry && (entry.description || entry.matchPattern) || entryData.matchPattern;
34
37
 
35
- let row: { entryTitle: string;[key: string]: unknown } = {
38
+ let row: { entryTitle: string; logDatum: unknown; entry: unknown;[key: string]: unknown } = {
36
39
  entryTitle: entryTitle,
40
+ logDatum: entryData.datum,
41
+ entry: entry,
37
42
  };
38
43
 
39
44
  if (entry) {
@@ -46,17 +51,43 @@ export class LifeCycleInstanceTableView extends qreact.Component<{
46
51
  return row;
47
52
  });
48
53
 
49
- let columns: any = {
54
+ let columns: ColumnsType = {
50
55
  entryTitle: { title: "Entry" },
51
56
  };
52
57
 
53
58
  for (let columnKey of allColumnKeys) {
59
+ let hasDefinedValue = rows.some(row => row[columnKey] !== undefined);
60
+ if (!hasDefinedValue) continue;
61
+
54
62
  columns[columnKey] = {
55
63
  title: columnKeyToTitle.get(columnKey) || columnKey,
56
- formatter: (value: unknown) => formatValue(value),
64
+ formatter: (value, context) => {
65
+ const entry = context?.row?.entry;
66
+ let formattedValue = formatValue(value);
67
+ if (value && context?.columnName?.toString().toLowerCase().includes("timeid")) {
68
+ formattedValue = formatDateTimeDetailed(Number(value));
69
+ }
70
+ if (value && context?.columnName === "time") {
71
+ formattedValue = formatDateTimeDetailed(Number(value));
72
+ }
73
+ return <>
74
+ {formattedValue}
75
+ {entry && value && <NestedLifeCycleInfo
76
+ keyName={columnKey}
77
+ value={value}
78
+ entry={entry as LifeCycleEntry}
79
+ currentLifeCycleId={lifeCycle.id}
80
+ /> || undefined}
81
+ </>;
82
+ },
57
83
  };
58
84
  }
59
85
 
86
+ columns.logDatum = {
87
+ title: "Log",
88
+ formatter: datum => <Button onClick={() => console.log(datum)}>Log</Button>,
89
+ };
90
+
60
91
  return <Table rows={rows} columns={columns} />;
61
92
  }
62
93
  }
@@ -32,7 +32,8 @@ import { isPublic } from "../../../config";
32
32
  export let lifecycleIdURL = new URLParam("lifecycleid", "");
33
33
  export let limitURL = new URLParam("lifecyclelimit", 16);
34
34
  export let additionalSearchURL = new URLParam("lifecyclesearch", "");
35
- export let lifecycleTableViewURL = new URLParam("lifecycletable", false);
35
+ export let phase2AdditionalSearchURL = new URLParam("lifecyclephase2search", "");
36
+ export let lifecycleTableViewURL = new URLParam("lifecycletable", true);
36
37
 
37
38
  export class LifeCyclePage extends qreact.Component {
38
39
  controller = LifeCyclesController(SocketFunction.browserNodeId());
@@ -54,7 +55,7 @@ export class LifeCyclePage extends qreact.Component {
54
55
  search = createLifeCycleSearch(this);
55
56
 
56
57
  searchLifeCycle = (lifeCycleId: string) => {
57
- return this.search.searchLifeCycle({ lifeCycleId, additionalSearch: additionalSearchURL.value.trim(), limit: limitURL.value });
58
+ return this.search.searchLifeCycle({ lifeCycleId, additionalSearch: additionalSearchURL.value.trim(), phase2AdditionalSearch: phase2AdditionalSearchURL.value.trim(), limit: limitURL.value });
58
59
  };
59
60
 
60
61
  componentDidMount() {
@@ -109,7 +110,7 @@ export class LifeCyclePage extends qreact.Component {
109
110
  </div>
110
111
 
111
112
  <InputLabelURL
112
- label="Additional Search"
113
+ label="Additional Search (Phase 1)"
113
114
  placeholder="Additional search filter..."
114
115
  url={additionalSearchURL}
115
116
  fillWidth
@@ -121,6 +122,19 @@ export class LifeCyclePage extends qreact.Component {
121
122
  }}
122
123
  />
123
124
 
125
+ <InputLabelURL
126
+ label="Additional Search (Phase 2)"
127
+ placeholder="Additional search filter..."
128
+ url={phase2AdditionalSearchURL}
129
+ fillWidth
130
+ onKeyDown={(e) => {
131
+ if (e.key === "Enter" && lifecycleIdURL.value) {
132
+ phase2AdditionalSearchURL.value = e.currentTarget.value;
133
+ void this.searchLifeCycle(lifecycleIdURL.value);
134
+ }
135
+ }}
136
+ />
137
+
124
138
  <div className={css.hbox(12).wrap}>
125
139
  <InputLabel
126
140
  placeholder="Add new life cycle"
@@ -134,7 +148,7 @@ export class LifeCyclePage extends qreact.Component {
134
148
  matchPattern: value,
135
149
  groupByKeys: [],
136
150
  isStart: true,
137
- sourceType: "log",
151
+ sourceType: "info",
138
152
  variables: {},
139
153
  }],
140
154
  };
@@ -188,7 +202,7 @@ export class LifeCyclePage extends qreact.Component {
188
202
  {(this.state.phase1Searching || this.state.phase1Results.length > 0) && (
189
203
  <div className={css.vbox(8)}>
190
204
  <div className={css.boldStyle}>
191
- Phase 1: Start Entries ({this.state.phase1Results.length} valid)
205
+ Phase 1: Start Entries ({this.state.phase1Results.length} valid{this.state.phase1Results.length >= limitURL.value && ", IMPORTANT! If recent events have an old start, we might miss them due to time filtering. Restrict your input with additional search until results < limit to ensure you don't miss any values"})
192
206
  {this.state.phase1Searching && " - Searching..."}
193
207
  </div>
194
208
  {this.state.phase1Stats && (
@@ -1,6 +1,6 @@
1
1
  import { SocketFunction } from "socket-function/SocketFunction";
2
- import { formatDateTime, formatTime } from "socket-function/src/formatting/format";
3
- import { deepCloneJSON } from "socket-function/src/misc";
2
+ import { formatDateTime, formatDateTimeDetailed, formatTime } from "socket-function/src/formatting/format";
3
+ import { deepCloneJSON, nextId } from "socket-function/src/misc";
4
4
  import { css } from "typesafecss";
5
5
  import { t } from "../../../2-proxy/schema2";
6
6
  import { qreact } from "../../../4-dom/qreact";
@@ -78,6 +78,15 @@ export class LifeCycleRenderer extends qreact.Component<{
78
78
  Pin
79
79
  </Button>
80
80
  )}
81
+ <Button hue={200} onClick={() => {
82
+ let newLifeCycle = deepCloneJSON(lifeCycle);
83
+ newLifeCycle.id = nextId();
84
+ Querysub.onCommitFinished(async () => {
85
+ await this.controller.setLifeCycle.promise(newLifeCycle);
86
+ });
87
+ }}>
88
+ Clone
89
+ </Button>
81
90
  <Button
82
91
  hue={0}
83
92
  onClick={() => {
@@ -125,12 +134,11 @@ export class LifeCycleRenderer extends qreact.Component<{
125
134
  onChangeValue={(value) => {
126
135
  let matchPattern = value.trim();
127
136
  if (matchPattern) {
128
- let defaultGroupByKeys = lifeCycle.entries.length > 0 && lifeCycle.entries[0].groupByKeys && deepCloneJSON(lifeCycle.entries[0].groupByKeys) || [];
129
137
  let newEntry: LifeCycleEntry = {
130
138
  matchPattern,
131
139
  sourceType: "info",
132
- groupByKeys: defaultGroupByKeys,
133
- variables: {},
140
+ groupByKeys: deepCloneJSON(lifeCycle.entries[0]?.groupByKeys || []),
141
+ variables: deepCloneJSON(lifeCycle.entries[0]?.variables || {}),
134
142
  };
135
143
  let updatedLifeCycle = deepCloneJSON(lifeCycle);
136
144
  let firstEndIndex = updatedLifeCycle.entries.findIndex(e => e.isEnd);
@@ -213,9 +221,9 @@ export class LifeCycleInstanceRenderer extends qreact.Component<{
213
221
  >
214
222
  {this.state.expanded && "▼" || "▶"}
215
223
  </Button>
216
- <span className={css.minWidth(200).hbox(12)}>
224
+ <span className={css.minWidth(200).hbox(12)} title={`${instance.startTime} to ${instance.endTime}`}>
217
225
  <span className={css.colorhsl(220, 60, 50)}>
218
- {formatDateTime(instance.startTime)}
226
+ {formatDateTimeDetailed(instance.startTime)}
219
227
  </span>
220
228
  {instance.endTime !== undefined && (
221
229
  <span className={css.colorhsl(220, 60, 50)}>