querysub 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/.dependency-cruiser.js +304 -0
  2. package/.eslintrc.js +51 -0
  3. package/.github/copilot-instructions.md +1 -0
  4. package/.vscode/settings.json +25 -0
  5. package/bin/deploy.js +4 -0
  6. package/bin/function.js +4 -0
  7. package/bin/server.js +4 -0
  8. package/costsBenefits.txt +112 -0
  9. package/deploy.ts +3 -0
  10. package/inject.ts +1 -0
  11. package/package.json +60 -0
  12. package/prompts.txt +54 -0
  13. package/spec.txt +820 -0
  14. package/src/-a-archives/archiveCache.ts +913 -0
  15. package/src/-a-archives/archives.ts +148 -0
  16. package/src/-a-archives/archivesBackBlaze.ts +792 -0
  17. package/src/-a-archives/archivesDisk.ts +418 -0
  18. package/src/-a-archives/copyLocalToBackblaze.ts +24 -0
  19. package/src/-a-auth/certs.ts +517 -0
  20. package/src/-a-auth/der.ts +122 -0
  21. package/src/-a-auth/ed25519.ts +1015 -0
  22. package/src/-a-auth/node-forge-ed25519.d.ts +17 -0
  23. package/src/-b-authorities/dnsAuthority.ts +203 -0
  24. package/src/-b-authorities/emailAuthority.ts +57 -0
  25. package/src/-c-identity/IdentityController.ts +200 -0
  26. package/src/-d-trust/NetworkTrust2.ts +150 -0
  27. package/src/-e-certs/EdgeCertController.ts +288 -0
  28. package/src/-e-certs/certAuthority.ts +192 -0
  29. package/src/-f-node-discovery/NodeDiscovery.ts +543 -0
  30. package/src/-g-core-values/NodeCapabilities.ts +134 -0
  31. package/src/-g-core-values/oneTimeForward.ts +91 -0
  32. package/src/-h-path-value-serialize/PathValueSerializer.ts +769 -0
  33. package/src/-h-path-value-serialize/stringSerializer.ts +176 -0
  34. package/src/0-path-value-core/LoggingClient.tsx +24 -0
  35. package/src/0-path-value-core/NodePathAuthorities.ts +978 -0
  36. package/src/0-path-value-core/PathController.ts +1 -0
  37. package/src/0-path-value-core/PathValueCommitter.ts +565 -0
  38. package/src/0-path-value-core/PathValueController.ts +231 -0
  39. package/src/0-path-value-core/archiveLocks/ArchiveLocks.ts +154 -0
  40. package/src/0-path-value-core/archiveLocks/ArchiveLocks2.ts +820 -0
  41. package/src/0-path-value-core/archiveLocks/archiveSnapshots.ts +180 -0
  42. package/src/0-path-value-core/debugLogs.ts +90 -0
  43. package/src/0-path-value-core/pathValueArchives.ts +483 -0
  44. package/src/0-path-value-core/pathValueCore.ts +2217 -0
  45. package/src/1-path-client/RemoteWatcher.ts +558 -0
  46. package/src/1-path-client/pathValueClientWatcher.ts +702 -0
  47. package/src/2-proxy/PathValueProxyWatcher.ts +1857 -0
  48. package/src/2-proxy/archiveMoveHarness.ts +376 -0
  49. package/src/2-proxy/garbageCollection.ts +753 -0
  50. package/src/2-proxy/pathDatabaseProxyBase.ts +37 -0
  51. package/src/2-proxy/pathValueProxy.ts +139 -0
  52. package/src/2-proxy/schema2.ts +518 -0
  53. package/src/3-path-functions/PathFunctionHelpers.ts +129 -0
  54. package/src/3-path-functions/PathFunctionRunner.ts +619 -0
  55. package/src/3-path-functions/PathFunctionRunnerMain.ts +67 -0
  56. package/src/3-path-functions/deployBlock.ts +10 -0
  57. package/src/3-path-functions/deployCheck.ts +7 -0
  58. package/src/3-path-functions/deployMain.ts +160 -0
  59. package/src/3-path-functions/pathFunctionLoader.ts +282 -0
  60. package/src/3-path-functions/syncSchema.ts +475 -0
  61. package/src/3-path-functions/tests/functionsTest.ts +135 -0
  62. package/src/3-path-functions/tests/rejectTest.ts +77 -0
  63. package/src/4-dom/css.tsx +29 -0
  64. package/src/4-dom/cssTypes.d.ts +212 -0
  65. package/src/4-dom/qreact.tsx +2322 -0
  66. package/src/4-dom/qreactTest.tsx +417 -0
  67. package/src/4-querysub/Querysub.ts +877 -0
  68. package/src/4-querysub/QuerysubController.ts +620 -0
  69. package/src/4-querysub/copyEvent.ts +0 -0
  70. package/src/4-querysub/permissions.ts +289 -0
  71. package/src/4-querysub/permissionsShared.ts +1 -0
  72. package/src/4-querysub/querysubPrediction.ts +525 -0
  73. package/src/5-diagnostics/FullscreenModal.tsx +67 -0
  74. package/src/5-diagnostics/GenericFormat.tsx +165 -0
  75. package/src/5-diagnostics/Modal.tsx +79 -0
  76. package/src/5-diagnostics/Table.tsx +183 -0
  77. package/src/5-diagnostics/TimeGrouper.tsx +114 -0
  78. package/src/5-diagnostics/diskValueAudit.ts +216 -0
  79. package/src/5-diagnostics/memoryValueAudit.ts +442 -0
  80. package/src/5-diagnostics/nodeMetadata.ts +135 -0
  81. package/src/5-diagnostics/qreactDebug.tsx +309 -0
  82. package/src/5-diagnostics/shared.ts +26 -0
  83. package/src/5-diagnostics/synchronousLagTracking.ts +47 -0
  84. package/src/TestController.ts +35 -0
  85. package/src/allowclient.flag +0 -0
  86. package/src/bits.ts +86 -0
  87. package/src/buffers.ts +69 -0
  88. package/src/config.ts +53 -0
  89. package/src/config2.ts +48 -0
  90. package/src/diagnostics/ActionsHistory.ts +56 -0
  91. package/src/diagnostics/NodeViewer.tsx +503 -0
  92. package/src/diagnostics/SizeLimiter.ts +62 -0
  93. package/src/diagnostics/TimeDebug.tsx +18 -0
  94. package/src/diagnostics/benchmark.ts +139 -0
  95. package/src/diagnostics/errorLogs/ErrorLogController.ts +515 -0
  96. package/src/diagnostics/errorLogs/ErrorLogCore.ts +274 -0
  97. package/src/diagnostics/errorLogs/LogClassifiers.tsx +302 -0
  98. package/src/diagnostics/errorLogs/LogFilterUI.tsx +84 -0
  99. package/src/diagnostics/errorLogs/LogNotify.tsx +101 -0
  100. package/src/diagnostics/errorLogs/LogTimeSelector.tsx +724 -0
  101. package/src/diagnostics/errorLogs/LogViewer.tsx +757 -0
  102. package/src/diagnostics/errorLogs/hookErrors.ts +60 -0
  103. package/src/diagnostics/errorLogs/logFiltering.tsx +149 -0
  104. package/src/diagnostics/heapTag.ts +13 -0
  105. package/src/diagnostics/listenOnDebugger.ts +77 -0
  106. package/src/diagnostics/logs/DiskLoggerPage.tsx +572 -0
  107. package/src/diagnostics/logs/ObjectDisplay.tsx +165 -0
  108. package/src/diagnostics/logs/ansiFormat.ts +108 -0
  109. package/src/diagnostics/logs/diskLogGlobalContext.ts +38 -0
  110. package/src/diagnostics/logs/diskLogger.ts +305 -0
  111. package/src/diagnostics/logs/diskShimConsoleLogs.ts +32 -0
  112. package/src/diagnostics/logs/injectFileLocationToConsole.ts +50 -0
  113. package/src/diagnostics/logs/logGitHashes.ts +30 -0
  114. package/src/diagnostics/managementPages.tsx +289 -0
  115. package/src/diagnostics/periodic.ts +89 -0
  116. package/src/diagnostics/runSaturationTest.ts +416 -0
  117. package/src/diagnostics/satSchema.ts +64 -0
  118. package/src/diagnostics/trackResources.ts +82 -0
  119. package/src/diagnostics/watchdog.ts +55 -0
  120. package/src/errors.ts +132 -0
  121. package/src/forceProduction.ts +3 -0
  122. package/src/fs.ts +72 -0
  123. package/src/heapDumps.ts +666 -0
  124. package/src/https.ts +2 -0
  125. package/src/inject.ts +1 -0
  126. package/src/library-components/ATag.tsx +84 -0
  127. package/src/library-components/Button.tsx +344 -0
  128. package/src/library-components/ButtonSelector.tsx +64 -0
  129. package/src/library-components/DropdownCustom.tsx +151 -0
  130. package/src/library-components/DropdownSelector.tsx +32 -0
  131. package/src/library-components/Input.tsx +334 -0
  132. package/src/library-components/InputLabel.tsx +198 -0
  133. package/src/library-components/InputPicker.tsx +125 -0
  134. package/src/library-components/LazyComponent.tsx +62 -0
  135. package/src/library-components/MeasureHeightCSS.tsx +48 -0
  136. package/src/library-components/MeasuredDiv.tsx +47 -0
  137. package/src/library-components/ShowMore.tsx +51 -0
  138. package/src/library-components/SyncedController.ts +171 -0
  139. package/src/library-components/TimeRangeSelector.tsx +407 -0
  140. package/src/library-components/URLParam.ts +263 -0
  141. package/src/library-components/colors.tsx +14 -0
  142. package/src/library-components/drag.ts +114 -0
  143. package/src/library-components/icons.tsx +692 -0
  144. package/src/library-components/niceStringify.ts +50 -0
  145. package/src/library-components/renderToString.ts +52 -0
  146. package/src/misc/PromiseRace.ts +101 -0
  147. package/src/misc/color.ts +30 -0
  148. package/src/misc/getParentProcessId.cs +53 -0
  149. package/src/misc/getParentProcessId.ts +53 -0
  150. package/src/misc/hash.ts +83 -0
  151. package/src/misc/ipPong.js +13 -0
  152. package/src/misc/networking.ts +2 -0
  153. package/src/misc/random.ts +45 -0
  154. package/src/misc.ts +19 -0
  155. package/src/noserverhotreload.flag +0 -0
  156. package/src/path.ts +226 -0
  157. package/src/persistentLocalStore.ts +37 -0
  158. package/src/promise.ts +15 -0
  159. package/src/server.ts +73 -0
  160. package/src/src.d.ts +1 -0
  161. package/src/test/heapProcess.ts +36 -0
  162. package/src/test/mongoSatTest.tsx +55 -0
  163. package/src/test/satTest.ts +193 -0
  164. package/src/test/test.tsx +552 -0
  165. package/src/zip.ts +92 -0
  166. package/src/zipThreaded.ts +106 -0
  167. package/src/zipThreadedWorker.js +19 -0
  168. package/tsconfig.json +27 -0
  169. package/yarnSpec.txt +56 -0
@@ -0,0 +1,724 @@
1
+
2
+ import preact from "preact"; import { qreact } from "../../../src/4-dom/qreact";
3
+ import { css } from "typesafecss";
4
+ import { cacheArgsEqual } from "socket-function/src/caching";
5
+ import { entries, keys, sort, timeInDay, timeInHour, timeInMinute, timeInYear } from "socket-function/src/misc";
6
+ import { URLParam } from "../../../src/library-components/URLParam";
7
+ import { ATag } from "../../../src/library-components/ATag";
8
+ import { Button, getAnimationFrames } from "../../../src/library-components/Button";
9
+ import { performDrag2 } from "../../../src/library-components/drag";
10
+ import { atomic, atomicObjectWrite } from "../../../src/2-proxy/PathValueProxyWatcher";
11
+ import { formatNumber, formatPercent } from "socket-function/src/formatting/format";
12
+ import { Counts, PickLogType, countColors, countTypes, showErrorURL, showFatalURL, showInfoURL, showWarningURL } from "./logFiltering";
13
+ import { ButtonSelector } from "../../library-components/ButtonSelector";
14
+ import { Icon } from "../../library-components/icons";
15
+ import { MeasuredDiv } from "../../library-components/MeasuredDiv";
16
+
17
+
18
+ type RangeSize = "lastYear" | "lastMonth" | "lastWeek" | "lastDay" | "lastHour";
19
+ export let logNowTime = Date.now();
20
+ const logFilterStart = new URLParam<number | RangeSize>("logFilterStartTime", "lastWeek");
21
+ const logFilterEnd = new URLParam<number>("logFilterEndTime", 0);
22
+ let rangeThreshold: { [K in RangeSize]: number } = {
23
+ lastHour: timeInHour * 2,
24
+ lastDay: timeInHour * 30,
25
+ lastWeek: timeInDay * 10,
26
+ lastMonth: timeInDay * 60,
27
+ lastYear: Number.MAX_SAFE_INTEGER,
28
+ };
29
+ export function getLogFilterRange(config?: {
30
+ start?: number | RangeSize;
31
+ end?: number;
32
+ }) {
33
+ let startValue = config?.start || logFilterStart.value;
34
+ let endTime = config?.end || logFilterEnd.value || (logNowTime + 15000);
35
+ let startTime: number;
36
+ let size: RangeSize;
37
+ if (typeof startValue === "number") {
38
+ startTime = startValue;
39
+ if (startTime > endTime) {
40
+ [startTime, endTime] = [endTime, startTime];
41
+ }
42
+ let curRange = endTime - startTime;
43
+ size = entries(rangeThreshold).find(([key, value]) => value > curRange)?.[0] || "lastYear";
44
+ } else {
45
+ size = startValue;
46
+ if (startValue === "lastYear") {
47
+ startTime = endTime - timeInYear;
48
+ } else if (startValue === "lastMonth") {
49
+ startTime = endTime - timeInDay * 31;
50
+ } else if (startValue === "lastWeek") {
51
+ startTime = endTime - timeInDay * 7;
52
+ } else if (startValue === "lastDay") {
53
+ startTime = endTime - timeInDay;
54
+ } else if (startValue === "lastHour") {
55
+ startTime = endTime - timeInHour;
56
+ } else {
57
+ let unhandled: never = startValue;
58
+ throw new Error("Invalid size: " + startValue);
59
+ }
60
+ }
61
+ return { startTime, endTime, rangeSize: size };
62
+ }
63
+ export function resetLogFilter() {
64
+ logNowTime = Date.now();
65
+ }
66
+
67
+ export type TimelineInfo = {
68
+ startTime: number;
69
+ endTime: number;
70
+
71
+ count: number;
72
+
73
+ fatalCount: number;
74
+ errorCount: number;
75
+ warnCount: number;
76
+ infoCount: number;
77
+ };
78
+
79
+ export class LogTimeSelector extends qreact.Component<{
80
+ infos: TimelineInfo[] | undefined;
81
+ hotkeys?: boolean;
82
+ }> {
83
+ state = {
84
+ width: 1000,
85
+ height: 1000,
86
+ // atomic
87
+ drag: undefined as { startFraction: number; endFraction: number; } | undefined,
88
+ };
89
+ canvasObj: undefined | {
90
+ canvas: HTMLCanvasElement;
91
+ ctx: CanvasRenderingContext2D;
92
+ };
93
+ render() {
94
+ let infos = this.props.infos;
95
+
96
+ let timeObj = getLogFilterRange();
97
+ function deltaTimes(fraction: number) {
98
+ let time = (timeObj.endTime - timeObj.startTime) * fraction;
99
+ logFilterStart.value = timeObj.startTime + time;
100
+ logFilterEnd.value = timeObj.endTime + time;
101
+ }
102
+ let blockHisto = getBlockHistoBreakdown({
103
+ infos,
104
+ barCount: this.state.width,
105
+ ...timeObj,
106
+ showFatal: showFatalURL.value,
107
+ showError: showErrorURL.value,
108
+ showWarn: showWarningURL.value,
109
+ showInfo: showInfoURL.value,
110
+ });
111
+ function formatDateTimeForInput(value: number) {
112
+ value -= new Date(value).getTimezoneOffset() * 60 * 1000;
113
+ return new Date(value).toISOString().slice(0, -1);
114
+ }
115
+ const drag = atomic(this.state.drag);
116
+
117
+ let canvasObj = this.canvasObj;
118
+ if (canvasObj) {
119
+ if (
120
+ canvasObj.canvas.width !== this.state.width
121
+ || canvasObj.canvas.height !== this.state.height
122
+ ) {
123
+ canvasObj.canvas.width = this.state.width;
124
+ canvasObj.canvas.height = this.state.height;
125
+ }
126
+ const context = canvasObj.ctx;
127
+ const bars = blockHisto.bars;
128
+ let width = this.state.width;
129
+ let height = this.state.height;
130
+ context.clearRect(0, 0, width, height);
131
+ for (let i = 0; i < bars.length; i++) {
132
+ let xFrac = i / bars.length;
133
+ let isDragging = drag && drag.startFraction <= xFrac && xFrac <= drag.endFraction;
134
+
135
+ let bar = bars[i];
136
+ let yPos = height;
137
+ for (let type of countTypes) {
138
+ let color = countColors[type];
139
+ let l = color.l;
140
+ if (isDragging) {
141
+ l += 30;
142
+ }
143
+ context.fillStyle = `hsl(${color.h}, ${color.s}%, ${l}%)`;
144
+ let curHeight = bar.count[type] / blockHisto.maxBarCount * height * 0.95;
145
+ if (curHeight > 0 && curHeight < 3) {
146
+ curHeight = 3;
147
+ }
148
+ context.fillRect(i, yPos - curHeight, 1, curHeight);
149
+ yPos -= curHeight;
150
+ }
151
+ }
152
+ }
153
+
154
+ function formatTitleNumber(config: {
155
+ value: Counts;
156
+ title: string;
157
+ showHeight?: boolean;
158
+ fixedWidth?: boolean;
159
+ }) {
160
+ const { value, title } = config;
161
+ return (
162
+ <div class={
163
+ css
164
+ .fontSize(60).boldStyle
165
+ .center.hbox(10)
166
+ .pad2(40)
167
+ .fillHeight.relative.zIndex(0)
168
+ .pointerEvents("none")
169
+ .userSelect("none")
170
+ .flexShrink0
171
+ .relative.zIndex(1)
172
+ + (config.showHeight && css.border("1px solid hsl(0, 0%, 20%)"))
173
+ + (config.fixedWidth && css.width("20%"))
174
+ }>
175
+ {config.showHeight && <div
176
+ class={
177
+ css.fillWidth.bottom0.absolute.height(`${value.total / blockHisto.totalCount * 100}%`)
178
+ .zIndex(-1)
179
+ .transition("height 0.5s")
180
+ .vbox0
181
+ }
182
+ >
183
+ {countTypes.map(type => {
184
+ let color = countColors[type];
185
+ return (
186
+ <div
187
+ class={css
188
+ .fillWidth.height(`${value[type] / value.total * 100}%`)
189
+ .hsl(color.h, color.s, color.l)
190
+ }
191
+ />
192
+ );
193
+ })}
194
+ </div>}
195
+ <div>
196
+ {formatNumber(value.total)}
197
+ </div>
198
+ <div class={css.vbox(10).fontSize(12).fontWeight("normal").color("hsl(0, 0%, 20%)")}>
199
+ <div>{title}</div>
200
+ <div>{formatPercent(value.total / blockHisto.totalCount)}</div>
201
+ </div>
202
+ </div>
203
+ );
204
+ }
205
+ function renderUIHorizontal(title: string, count: Counts, key: string) {
206
+ let isSelected = logFilterStart.value === key;
207
+ return (
208
+ <div class={
209
+ css.relative.pad2(10, 4).color("white").border("")
210
+ .hsla(0, 0, 20, 1)
211
+ .border("1px solid hsl(0, 0%, 10%)")
212
+ + (!isSelected && css.opacity(0.5))
213
+ }>
214
+ <div class={css.pos(0, 0).fillBoth.absolute.zIndex(0).hbox0}>
215
+ {countTypes.map(type => {
216
+ let color = countColors[type];
217
+ let fraction = count[type] / count.total;
218
+ return (
219
+ <div
220
+ class={
221
+ css
222
+ .fillHeight.width(`calc(max(${fraction > 0 ? 8 : 0}px, ${fraction * 100}%))`)
223
+ .hsl(color.h, color.s, color.l)
224
+ }
225
+ />
226
+ );
227
+ })}
228
+ </div>
229
+ <div class={css.relative}>{title} ({formatNumber(count.total)})</div>
230
+ </div>
231
+ );
232
+ }
233
+
234
+ return (
235
+ <div class={css.width("100%").vbox0}>
236
+ <div class={css.hbox(10).fillWidth}>
237
+ <ButtonSelector
238
+ title="Show Last"
239
+ value={logFilterStart.value}
240
+ noDefault
241
+ noUI
242
+ onChange={value => {
243
+ logFilterStart.value = value;
244
+ logFilterEnd.value = 0;
245
+ }}
246
+ options={[
247
+ { value: "lastYear", title: renderUIHorizontal("Year", blockHisto.counts.lastYear, "lastYear"), },
248
+ { value: "lastMonth", title: renderUIHorizontal("Month", blockHisto.counts.lastMonth, "lastMonth"), },
249
+ { value: "lastWeek", title: renderUIHorizontal("Week", blockHisto.counts.lastWeek, "lastWeek"), },
250
+ { value: "lastDay", title: renderUIHorizontal("Day", blockHisto.counts.lastDay, "lastDay"), },
251
+ { value: "lastHour", title: renderUIHorizontal("Hour", blockHisto.counts.lastHour, "lastHour"), },
252
+ ]}
253
+ />
254
+ <div class={css.marginAuto} />
255
+ <PickLogType infos={this.props.infos} />
256
+ <div class={css.marginAuto} />
257
+ <div class={css.hbox(10).flexShrink0.wrap}>
258
+ {(logFilterEnd.value || logFilterStart.value !== logFilterStart.default) &&
259
+ <Button onClick={() => {
260
+ logFilterStart.reset();
261
+ logFilterEnd.reset();
262
+ }}>
263
+ Reset to Present
264
+ </Button>
265
+ }
266
+ <input
267
+ type="datetime-local"
268
+ value={formatDateTimeForInput(getLogFilterRange().startTime)}
269
+ onInput={e => {
270
+ let date = new Date(e.currentTarget.value);
271
+ logFilterStart.value = date.getTime();
272
+ }}
273
+ />
274
+ <span>to</span>
275
+ <input
276
+ type="datetime-local"
277
+ value={formatDateTimeForInput(getLogFilterRange().endTime)}
278
+ onInput={e => {
279
+ let date = new Date(e.currentTarget.value);
280
+ logFilterEnd.value = date.getTime();
281
+ }}
282
+ />
283
+ </div>
284
+ </div>
285
+ <div class={css.fillWidth.hbox(10).pad2(10)}>
286
+ {formatTitleNumber({ value: blockHisto.countBefore, title: "Before", showHeight: true, fixedWidth: true })}
287
+ <div class={css.fillWidth.outline("1px solid hsl(0, 0%, 20%)")}>
288
+ <div className={css.size("100%", 20).relative.overflowHidden}>
289
+ {blockHisto.labels.map(label => {
290
+ return (
291
+ <ATag
292
+ title={label.label}
293
+ data-indexStart={label.indexStart}
294
+ data-indexEnd={label.indexEnd}
295
+ values={[
296
+ { param: logFilterEnd, value: label.endTime },
297
+ { param: logFilterStart, value: label.startTime },
298
+ ]}
299
+ class={
300
+ css.width(label.indexEnd - label.indexStart)
301
+ .absolute
302
+ .left(label.indexStart)
303
+ .bottom0
304
+ .fillHeight
305
+ .center
306
+ .overflowHidden
307
+ .textAlign("center")
308
+ .whiteSpace("nowrap")
309
+ .hsla(0, 0, 20, 1)
310
+ .color("hsl(0, 0%, 90%)", "important")
311
+ .boldStyle
312
+ .border("1px solid hsl(0, 0%, 75%)")
313
+ .borderTopWidth(0)
314
+ .borderBottomWidth(0)
315
+ }
316
+ >
317
+ {label.label}
318
+ </ATag>
319
+ );
320
+ })}
321
+ <Button
322
+ flavor="noui"
323
+ class={
324
+ css.absolute.top0.left0
325
+ .fillHeight
326
+ .hbox0
327
+ .background(`linear-gradient(
328
+ 90deg,
329
+ hsla(0, 0%, 20%, 1) 10%,
330
+ hsla(0, 0%, 20%, 0.95),
331
+ hsla(0, 0%, 20%, 0.5)
332
+ )`)
333
+ .cursor("pointer").userSelect("none").filter("brightness(1.5)", "hover")
334
+ .width(70)
335
+ .paddingLeft(10)
336
+ .border("0")
337
+ }
338
+ hotkeys={["Global+ArrowLeft"]}
339
+ onClick={() => deltaTimes(-0.008 * getAnimationFrames())}
340
+ immediateRepeat
341
+ >
342
+ {Icon.chevronDoubleLeft({ fill: "hsl(0, 0%, 50%)", stroke: "hsl(0, 0%, 60%)" })}
343
+ </Button>
344
+
345
+ <Button
346
+ flavor="noui"
347
+ class={
348
+ css.absolute.top0.right0
349
+ .fillHeight
350
+ .hbox0
351
+ .background(`linear-gradient(
352
+ 90deg,
353
+ transparent 0%,
354
+ transparent 60%,
355
+ hsla(0, 0%, 20%, 0.5) 60%,
356
+ hsla(0, 0%, 20%, 0.95) 70%,
357
+ hsla(0, 0%, 20%, 1) 100%
358
+ )`)
359
+ .cursor("pointer").userSelect("none").filter("brightness(1.5)", "hover")
360
+ .width(160)
361
+ .border("0")
362
+ .justifyContent("end")
363
+ }
364
+ hotkeys={["Global+ArrowRight"]}
365
+ immediateRepeat
366
+ onClick={() => deltaTimes(0.008 * getAnimationFrames())}
367
+ >
368
+ {Icon.chevronDoubleRight({ fill: "hsl(0, 0%, 50%)", stroke: "hsl(0, 0%, 60%)" })}
369
+ </Button>
370
+ </div>
371
+ <MeasuredDiv
372
+ class={
373
+ css.size("100%", 100).relative.overflowHidden.flex.center
374
+ }
375
+ onNewSize={(width, height) => { this.state.width = width; this.state.height = height; }}
376
+ onMouseDown={e => {
377
+ e.preventDefault();
378
+ let rect = e.currentTarget.getBoundingClientRect();
379
+ let self = this;
380
+ let startFraction = (e.clientX - rect.left) / rect.width;
381
+ performDrag2({
382
+ e,
383
+ onMove(offset) {
384
+ let endFraction = startFraction + offset.x / rect.width;
385
+ let newStart = startFraction;
386
+ let newEnd = endFraction;
387
+ if (newEnd < newStart) {
388
+ [newStart, newEnd] = [newEnd, newStart];
389
+ }
390
+ self.state.drag = atomicObjectWrite({ startFraction: newStart, endFraction: newEnd, });
391
+ },
392
+ onDone(offset) {
393
+ let endFraction = startFraction + offset.x / rect.width;
394
+ let timeRange = timeObj.endTime - timeObj.startTime;
395
+ logFilterStart.value = timeObj.startTime + timeRange * startFraction;
396
+ logFilterEnd.value = timeObj.startTime + timeRange * endFraction;
397
+ },
398
+ onFinally() {
399
+ self.state.drag = undefined;
400
+ },
401
+ });
402
+ }}
403
+ onWheel={e => {
404
+ e.preventDefault();
405
+ e.stopPropagation();
406
+ let timeRange = timeObj.endTime - timeObj.startTime;
407
+ let deltaAmount = timeRange * 0.20 * (e.deltaY < 0 ? -1 : 1);
408
+ logFilterStart.value = timeObj.startTime - deltaAmount;
409
+ logFilterEnd.value = timeObj.endTime + deltaAmount;
410
+ }}
411
+ >
412
+ {drag && <div
413
+ class={
414
+ css.left(`${drag.startFraction * 100}%`)
415
+ .right(`${(1 - drag.endFraction) * 100}%`)
416
+ .fillHeight
417
+ .absolute
418
+ .hsl(200, 50, 50)
419
+ .opacity(0.5)
420
+ }
421
+ />}
422
+ <canvas
423
+ class={css.fillWidth.fillHeight.absolute}
424
+ key="canvas"
425
+ ref2={e => {
426
+ if (this.canvasObj?.canvas === e) return;
427
+ this.canvasObj = { canvas: e, ctx: e.getContext("2d")! };
428
+ }}
429
+ />
430
+ {formatTitleNumber({ value: blockHisto.countInside, title: "Matched" })}
431
+ </MeasuredDiv>
432
+ <div className={css.size("100%", 20).relative.overflowHidden}>
433
+ {blockHisto.labels.map(label => {
434
+ return (
435
+ <ATag
436
+ title={label.label}
437
+ data-indexStart={label.indexStart}
438
+ data-indexEnd={label.indexEnd}
439
+ values={[
440
+ { param: logFilterEnd, value: label.endTime },
441
+ { param: logFilterStart, value: label.startTime },
442
+ ]}
443
+ class={
444
+ css.width(label.indexEnd - label.indexStart)
445
+ .absolute
446
+ .left(label.indexStart)
447
+ .bottom0
448
+ .fillHeight
449
+ .center
450
+ .overflowHidden
451
+ .textAlign("center")
452
+ .whiteSpace("nowrap")
453
+ .hsla(0, 0, 20, 1)
454
+ .color("hsl(0, 0%, 90%)", "important")
455
+ .boldStyle
456
+ .border("1px solid hsl(0, 0%, 75%)")
457
+ .borderTopWidth(0)
458
+ .borderBottomWidth(0)
459
+ }
460
+ >
461
+ {label.label}
462
+ </ATag>
463
+ );
464
+ })}
465
+ </div>
466
+ </div>
467
+ {formatTitleNumber({ value: blockHisto.countAfter, title: "After", showHeight: true, fixedWidth: true })}
468
+ </div>
469
+ </div>
470
+ );
471
+ }
472
+ }
473
+
474
+ const getBlockHistoBreakdown = cacheArgsEqual(function getBlockHistoBreakdown(config: {
475
+ infos: TimelineInfo[] | undefined;
476
+ barCount: number;
477
+ rangeSize: RangeSize;
478
+ startTime: number;
479
+ endTime: number;
480
+
481
+ showFatal: boolean;
482
+ showError: boolean;
483
+ showWarn: boolean;
484
+ showInfo: boolean;
485
+ }): {
486
+ totalCount: number;
487
+ maxBarCount: number;
488
+ bars: {
489
+ startTime: number;
490
+ endTime: number;
491
+
492
+ count: Counts;
493
+ }[];
494
+ labels: {
495
+ indexStart: number;
496
+ indexEnd: number;
497
+ startTime: number;
498
+ endTime: number;
499
+ label: string;
500
+ }[];
501
+
502
+ countBefore: Counts;
503
+ countInside: Counts;
504
+ countAfter: Counts;
505
+
506
+ counts: { [key in RangeSize]: Counts; }
507
+ } {
508
+ let { infos, barCount, startTime, endTime } = config;
509
+
510
+ let countBefore: Counts = { total: 0, fatal: 0, error: 0, warn: 0, info: 0 };
511
+ let countInside: Counts = { total: 0, fatal: 0, error: 0, warn: 0, info: 0 };
512
+ let countAfter: Counts = { total: 0, fatal: 0, error: 0, warn: 0, info: 0 };
513
+ function addToCount(counts: Counts, checkShow: boolean, info: {
514
+ count: number;
515
+ fatalCount: number;
516
+ errorCount: number;
517
+ warnCount: number;
518
+ infoCount: number;
519
+ }) {
520
+ if (!checkShow || config.showFatal) {
521
+ counts.fatal += info.fatalCount;
522
+ counts.total += info.fatalCount;
523
+ }
524
+
525
+ if (!checkShow || config.showError) {
526
+ counts.error += info.errorCount;
527
+ counts.total += info.errorCount;
528
+ }
529
+
530
+ if (!checkShow || config.showWarn) {
531
+ counts.warn += info.warnCount;
532
+ counts.total += info.warnCount;
533
+ }
534
+
535
+ if (!checkShow || config.showInfo) {
536
+ counts.info += info.infoCount;
537
+ counts.total += info.infoCount;
538
+ }
539
+ }
540
+
541
+ let totalCount = 0;
542
+ infos = infos || [];
543
+ infos = infos.slice();
544
+ sort(infos, a => a.startTime);
545
+
546
+ let countsByType: { [key in RangeSize]: Counts } = {} as any;
547
+ for (let key of keys(rangeThreshold)) {
548
+ let timeRange = getLogFilterRange({ start: key, end: logNowTime });
549
+ let counts = { total: 0, fatal: 0, error: 0, warn: 0, info: 0 };
550
+ for (let info of infos) {
551
+ if (!(info.endTime <= timeRange.startTime || info.startTime >= timeRange.endTime)) {
552
+ addToCount(counts, false, info);
553
+ }
554
+ }
555
+ countsByType[key] = counts;
556
+ }
557
+
558
+ infos = infos.filter(info => {
559
+ totalCount += info.count;
560
+ if (info.endTime <= startTime) {
561
+ addToCount(countBefore, true, info);
562
+ return false;
563
+ }
564
+ if (info.startTime >= endTime) {
565
+ addToCount(countAfter, true, info);
566
+ return false;
567
+ }
568
+ addToCount(countInside, true, info);
569
+ return true;
570
+ });
571
+
572
+ let bars: {
573
+ startTime: number;
574
+ endTime: number;
575
+ count: Counts;
576
+ }[] = [];
577
+ function getBarRange(barIndex: number) {
578
+ let f = barIndex / barCount;
579
+ let fEnd = (barIndex + 1) / barCount;
580
+ let barStartTime = startTime * (1 - f) + endTime * f;
581
+ let barEndTime = startTime * (1 - fEnd) + endTime * fEnd;
582
+ return { startTime: barStartTime, endTime: barEndTime };
583
+ }
584
+ function getBarIndex(time: number) {
585
+ let f = (time - startTime) / (endTime - startTime);
586
+ return Math.floor(f * barCount);
587
+ }
588
+ for (let i = 0; i < barCount; i++) {
589
+ bars.push({ ...getBarRange(i), count: { total: 0, fatal: 0, error: 0, warn: 0, info: 0 }, });
590
+ }
591
+ for (let info of infos) {
592
+ let index = getBarIndex(info.startTime);
593
+ if (index < 0) index = 0;
594
+ if (index >= bars.length) continue;
595
+ while (index < bars.length && bars[index].startTime < info.endTime) {
596
+ addToCount(bars[index++].count, true, info);
597
+ }
598
+ }
599
+
600
+ let maxBarCount = Math.max(...bars.map(a => a.count.total));
601
+
602
+ const labels = createLabels({
603
+ rangeSize: config.rangeSize,
604
+ startTime,
605
+ endTime,
606
+ getBarIndex,
607
+ });
608
+
609
+ return {
610
+ totalCount, maxBarCount, bars, labels, countBefore, countInside, countAfter, counts: countsByType,
611
+ };
612
+ }, 10);
613
+ function createLabels(config: {
614
+ rangeSize: RangeSize;
615
+ startTime: number;
616
+ endTime: number;
617
+ getBarIndex: (time: number) => number;
618
+ }) {
619
+ const { rangeSize, startTime, endTime, getBarIndex } = config;
620
+ let labels: {
621
+ indexStart: number;
622
+ indexEnd: number;
623
+ startTime: number;
624
+ endTime: number;
625
+ label: string;
626
+ }[] = [];
627
+ let periodAverageSize = 0;
628
+ let startOfPeriod: (date: Date) => Date;
629
+ let periods = 0;
630
+ let formatPeriodStart = (date: Date) => date.toDateString();
631
+
632
+ const MONTHS = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
633
+ const WEEKDAYS = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
634
+ function formatPlace(place: number) {
635
+ if (place === 1) return "1st";
636
+ if (place === 2) return "2nd";
637
+ if (place === 3) return "3rd";
638
+ return place + "th";
639
+ }
640
+ if (rangeSize === "lastYear") {
641
+ periodAverageSize = timeInDay * 31;
642
+ startOfPeriod = date => new Date(date.getFullYear(), date.getMonth(), 1);
643
+ formatPeriodStart = (date: Date) => `${date.getFullYear()} ${MONTHS[date.getMonth()].slice(0, 3)}`;
644
+ periods = 12;
645
+ } else if (rangeSize === "lastMonth") {
646
+ periodAverageSize = timeInDay * 7;
647
+ startOfPeriod = date => {
648
+ let dayOfWeek = date.getDay();
649
+ let daysBack = dayOfWeek % 7;
650
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate() - daysBack);
651
+ };
652
+ formatPeriodStart = (date: Date) => {
653
+ let endDate = new Date(date.getFullYear(), date.getMonth(), date.getDate() + 7);
654
+ // April 15
655
+ function formatInner(date: Date) {
656
+ return `${MONTHS[date.getMonth()]} ${date.getDate()}`;
657
+ }
658
+ return formatInner(date) + " - " + formatInner(endDate);
659
+ };
660
+ periods = 31;
661
+ } else if (rangeSize === "lastWeek") {
662
+ periodAverageSize = timeInDay;
663
+ startOfPeriod = date => new Date(date.getFullYear(), date.getMonth(), date.getDate());
664
+ // Monday 5th
665
+ formatPeriodStart = (date: Date) => `${WEEKDAYS[date.getDay()]} (${formatPlace(date.getDate())})`;
666
+ periods = 7;
667
+ } else if (rangeSize === "lastDay") {
668
+ periodAverageSize = timeInHour;
669
+ startOfPeriod = date => new Date(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours());
670
+ formatPeriodStart = (date: Date) => {
671
+ // Ex, 6 PM
672
+ let hour = date.getHours();
673
+ let ampm = hour < 12 ? "AM" : "PM";
674
+ hour = hour % 12;
675
+ if (hour === 0) hour = 12;
676
+ return hour + " " + ampm;
677
+ };
678
+ periods = 24;
679
+ } else if (rangeSize === "lastHour") {
680
+ periodAverageSize = timeInMinute * 10;
681
+ startOfPeriod = date => new Date(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), Math.floor(date.getMinutes() / 10) * 10);
682
+ formatPeriodStart = (date: Date) => {
683
+ let minutes = date.getMinutes();
684
+ let minutesStr = minutes.toString().padStart(2, "0");
685
+ // Render as PM/AM
686
+ let hour = date.getHours();
687
+ let ampm = hour < 12 ? "AM" : "PM";
688
+ hour = hour % 12;
689
+ if (hour === 0) hour = 12;
690
+ return `${hour}:${minutesStr} ${ampm}`;
691
+ };
692
+ periods = 2;
693
+ } else {
694
+ let unhandled: never = rangeSize;
695
+ throw new Error(`Unhandled rangeSize: ${rangeSize}`);
696
+ }
697
+
698
+ let date = new Date(endTime);
699
+ date = new Date(+date + periodAverageSize * 1.5);
700
+ date = startOfPeriod(date);
701
+ let times: number[] = [];
702
+ times.push(date.getTime());
703
+ while (true) {
704
+ date = startOfPeriod(new Date(+date - periodAverageSize * 0.5));
705
+ times.push(date.getTime());
706
+ if (date.getTime() <= startTime) break;
707
+ }
708
+ times.reverse();
709
+ for (let i = 0; i < times.length - 1; i++) {
710
+ let startTime = times[i];
711
+ let endTime = times[i + 1];
712
+ let firstBarIndex = getBarIndex(startTime);
713
+ let endBarIndex = getBarIndex(endTime);
714
+ labels.push({
715
+ indexStart: firstBarIndex,
716
+ indexEnd: endBarIndex,
717
+ startTime,
718
+ endTime,
719
+ label: formatPeriodStart(new Date(startTime)),
720
+ });
721
+ }
722
+
723
+ return labels;
724
+ }