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,135 @@
1
+
2
+ import { MaybePromise } from "socket-function/src/types";
3
+ import { JSXFormatter } from "./GenericFormat";
4
+ import { SocketFunction } from "socket-function/SocketFunction";
5
+ import { requiresNetworkTrustHook } from "../-d-trust/NetworkTrust2";
6
+ import { cache } from "socket-function/src/caching";
7
+ import { TimeGrouper } from "./TimeGrouper";
8
+ import { runInfinitePollCallAtStart } from "socket-function/src/batching";
9
+ import { timeInMinute } from "socket-function/src/misc";
10
+ import { formatNumber, formatPercent, formatTime } from "socket-function/src/formatting/format";
11
+ import os from "os";
12
+ import { isNode } from "typesafecss";
13
+
14
+ // Undefined means we infer the column
15
+ // Null means the column is removed
16
+ export type ColumnType = undefined | null | {
17
+ // Defaults to column name
18
+ title?: string;
19
+ formatter?: JSXFormatter;
20
+ };
21
+ export type RowType = {
22
+ [columnName: string]: unknown;
23
+ };
24
+ export type ColumnsType = { [columnName: string]: ColumnType };
25
+ export type TableType<ColumnsT extends ColumnsType = ColumnsType> = {
26
+ columns: ColumnsT;
27
+ rows: { [columnName in keyof ColumnsT]: unknown }[];
28
+ };
29
+
30
+ export type ExtraMetadata = {
31
+ columnName: string;
32
+ column?: ColumnType;
33
+ getValue: () => MaybePromise<unknown>;
34
+ };
35
+ let extraMetadatas: ExtraMetadata[] = [];
36
+ export function registerNodeMetadata(getter: ExtraMetadata) {
37
+ //console.log(`Registering metadata: ${getter.columnName}`);
38
+ extraMetadatas.push(getter);
39
+ }
40
+
41
+ let stateValues = cache((title: string, format: (value: number) => string): { (value: number): void } => {
42
+ let timeGrouper = new TimeGrouper();
43
+ function onValue(value: number) {
44
+ timeGrouper.onValueChanged(value);
45
+ }
46
+ registerNodeMetadata({
47
+ columnName: title,
48
+ column: {},
49
+ getValue() {
50
+ return timeGrouper.getStateSummary(format);
51
+ },
52
+ });
53
+ return onValue;
54
+ });
55
+ let eventValues = cache((title: string, format: (value: number) => string): { (value: number): void } => {
56
+ let timeGrouper = new TimeGrouper();
57
+ function onValue(value: number) {
58
+ timeGrouper.onValueChanged(value);
59
+ }
60
+ registerNodeMetadata({
61
+ columnName: title,
62
+ column: {},
63
+ getValue() {
64
+ return timeGrouper.getEventSummary(format);
65
+ },
66
+ });
67
+ return onValue;
68
+ });
69
+
70
+ export function logNodeStats(title: string, format: (value: number) => string): { (value: number): void } {
71
+ return eventValues(title, format);
72
+ }
73
+ export function logNodeStateStats(title: string, format: (value: number) => string): { (value: number): void } {
74
+ return stateValues(title, format);
75
+ }
76
+
77
+ if (isNode()) {
78
+ void runInfinitePollCallAtStart(timeInMinute, async () => {
79
+ // Get system memory usage
80
+
81
+ const free = os.freemem();
82
+ const total = os.totalmem();
83
+
84
+ let constrainedMemory = process.constrainedMemory();
85
+
86
+ logNodeStateStats("System|Memory Free", x => formatNumber(x) + "B")(free);
87
+ logNodeStateStats("System|Memory Free %", formatPercent)(free / total);
88
+ if (constrainedMemory !== undefined) {
89
+ logNodeStateStats("System|Constrained Memory", x => formatNumber(x) + "B")(constrainedMemory);
90
+ }
91
+
92
+ let usedCPU = process.cpuUsage();
93
+ logNodeStateStats("System|CPU User", formatTime)(usedCPU.user * 1000);
94
+ logNodeStateStats("System|CPU System", formatTime)(usedCPU.system * 1000);
95
+
96
+ let cpuCount = os.cpus().length;
97
+ logNodeStateStats("System|CPU Count", formatNumber)(cpuCount);
98
+
99
+ let load = os.loadavg();
100
+ logNodeStateStats("System|Average Free Cores", formatPercent)((1 - load[0]) * os.cpus().length);
101
+
102
+ });
103
+ }
104
+
105
+ registerNodeMetadata({
106
+ columnName: "System|Uptime",
107
+ column: { formatter: "timeSpan" },
108
+ getValue() {
109
+ return process.uptime() * 1000;
110
+ },
111
+ });
112
+
113
+ class NodeMetadataBase {
114
+ public async verifyAccess() {
115
+ return true;
116
+ }
117
+ public async getExtraMetadatas() {
118
+ return await Promise.all(extraMetadatas.map(async ({ columnName, column, getValue }) => {
119
+ return {
120
+ columnName,
121
+ column,
122
+ value: await getValue(),
123
+ };
124
+ }));
125
+ }
126
+ }
127
+ export const NodeMetadataController = SocketFunction.register(
128
+ "NodeMetadataController-8be51a16-27c2-449e-b044-dcef6c3efcaf",
129
+ new NodeMetadataBase(),
130
+ () => ({
131
+ getExtraMetadatas: {
132
+ hooks: [requiresNetworkTrustHook],
133
+ },
134
+ }),
135
+ );
@@ -0,0 +1,309 @@
1
+ import { lazy } from "socket-function/src/caching";
2
+ import { ExternalRenderClass, __INTERNAL__QRenderClass, getSchemaPrefix, getSourceVSCodeLink, qreact, triggerRerenderAll } from "../4-dom/qreact";
3
+ import { blue, green, magenta, yellow } from "socket-function/src/formatting/logColors";
4
+ import { getPathFromStr, getPathStr } from "../path";
5
+ import { delay } from "socket-function/src/batching";
6
+ import { logErrors } from "../errors";
7
+ import { clientWatcher } from "../1-path-client/pathValueClientWatcher";
8
+ import { Querysub } from "../4-querysub/QuerysubController";
9
+ import { closeAllModals, showModal } from "./Modal";
10
+ import { FullscreenModal } from "./FullscreenModal";
11
+ import { css } from "typesafecss";
12
+ import { Button } from "../library-components/Button";
13
+ import { authorityStorage } from "../0-path-value-core/pathValueCore";
14
+ import { pathValueSerializer } from "../-h-path-value-serialize/PathValueSerializer";
15
+ import { PathValueProxyWatcher } from "../2-proxy/PathValueProxyWatcher";
16
+ import { InputLabel, InputLabelURL } from "../library-components/InputLabel";
17
+ import { URLParam } from "../library-components/URLParam";
18
+ import { hotReloadingGuard, isHotReloading, onHotReload } from "socket-function/hot/HotReloadController";
19
+
20
+ // Map, so hot reloading doesn't break things
21
+ let componentButtons = new Map<string, { title: string, callback: (component: ExternalRenderClass) => void }>();
22
+
23
+ export function addComponentButton(config: {
24
+ title: string;
25
+ callback: (component: ExternalRenderClass) => void;
26
+ }) {
27
+ if (!isHotReloading() && componentButtons.has(config.title)) {
28
+ throw new Error(`Component button with title ${config.title} already exists`);
29
+ }
30
+ componentButtons.set(config.title, config);
31
+ }
32
+
33
+ export const enableDebugComponents = lazy(function enableDebugComponents() {
34
+ console.log(magenta(`middleclick + shift on components to debug them`));
35
+ // mousedown is more reliable than click, because the click will be aborted if they middle click
36
+ // in something with a scrollbar.
37
+ document.addEventListener("mousedown", function (e) {
38
+ // Middle click to debug
39
+ if (e.button === 1 && (e.altKey || e.shiftKey)) {
40
+ logErrors(triggerDebug(e.target as HTMLElement, e));
41
+ }
42
+ });
43
+
44
+ async function triggerDebug(target: HTMLElement, event: MouseEvent) {
45
+ const component = __INTERNAL__QRenderClass.getInstanceFromDOM(target);
46
+ if (!component) return;
47
+ event.preventDefault();
48
+ event.stopPropagation();
49
+
50
+ let prevInject = qreact.INJECT_LINE_NUMBERS;
51
+ try {
52
+ qreact.INJECT_LINE_NUMBERS = true;
53
+ triggerRerenderAll();
54
+ await clientWatcher.waitForTriggerFinished();
55
+ } finally {
56
+ qreact.INJECT_LINE_NUMBERS = prevInject;
57
+ }
58
+ // Get new target under the mouse
59
+ target = document.elementFromPoint(event.clientX, event.clientY) as HTMLElement;
60
+
61
+ let source = getSourceVSCodeLink(target);
62
+ if (event.shiftKey) {
63
+ let newComponent = __INTERNAL__QRenderClass.getInstanceFromDOM(target) || component;
64
+ showModal({ content: <WatchModal component={newComponent} /> });
65
+ return;
66
+ }
67
+
68
+ let ancestors = component.getDebugComponentStack().slice(1).reverse();
69
+ let schemaPrefix = getSchemaPrefix();
70
+ let schemaLength = getPathFromStr(schemaPrefix).length;
71
+ function encodePath(pathStr: string) {
72
+ let path = getPathFromStr(pathStr);
73
+ if (pathStr.startsWith(schemaPrefix)) {
74
+ let underPath = path.slice(schemaLength);
75
+ let componentId = Number(underPath[0]);
76
+ let component = __INTERNAL__QRenderClass.getInstanceAllowedUndefined(componentId);
77
+ if (component) {
78
+ path = [component.debugName].concat(underPath.slice(1));
79
+ }
80
+ }
81
+ // If paths are to other components, replace the prefix with the component debugName
82
+ return path.map(x => green(x)).join(".");
83
+ }
84
+
85
+ console.log(" ");
86
+ console.log(" ");
87
+ console.log(" ");
88
+ if (event.altKey) {
89
+ window.open(source);
90
+ }
91
+ // VSCode link
92
+ if (source) {
93
+ console.log(source);
94
+ }
95
+ console.group(`Component ${blue(component.debugName)}`);
96
+ console.groupCollapsed(` Ancestors (${ancestors.length})`);
97
+ for (let ancestor of ancestors) {
98
+ console.log(" " + ancestor.debugName);
99
+ let source = ancestor.getVSCodeLink();
100
+ if (source) {
101
+ console.log(` ${source}`);
102
+ }
103
+ }
104
+ console.groupEnd();
105
+ let lastWatches = component.renderWatcher!.lastWatches;
106
+
107
+ let watchPaths = Array.from(lastWatches.paths).filter(x => getPathFromStr(x).length > schemaLength);
108
+ console.groupCollapsed(` Watches (${watchPaths.length}):`);
109
+ for (let key of watchPaths) {
110
+ console.log(" " + encodePath(key));
111
+ }
112
+ console.groupEnd();
113
+
114
+ let parentPaths = Array.from(lastWatches.parentPaths).filter(x => getPathFromStr(x).length > schemaLength);
115
+ if (parentPaths.length > 0) {
116
+ console.groupCollapsed(` Parent Watches (${parentPaths.length}):`);
117
+ for (let key of parentPaths) {
118
+ if (getPathFromStr(key).length <= schemaLength) continue;
119
+ console.log(" " + encodePath(key));
120
+ }
121
+ console.groupEnd();
122
+ }
123
+
124
+ console.log(` debugHolder = `, component);
125
+ (globalThis as any).debugHolder = component;
126
+ console.log(` debugInstance`, component.instance);
127
+ (globalThis as any).debugInstance = component.instance;
128
+
129
+ console.groupEnd();
130
+ }
131
+ });
132
+
133
+
134
+ const pathFilter = new URLParam("pathfilter", "");
135
+ class WatchModal extends qreact.Component<{
136
+ component: ExternalRenderClass;
137
+ }> {
138
+ state = {
139
+ parentNavigate: 0,
140
+ pos: "fill" as "top" | "fill" | "bottom",
141
+ };
142
+ render() {
143
+ let component = this.props.component;
144
+ for (let i = 0; i < this.state.parentNavigate; i++) {
145
+ component = component.getParent() || component;
146
+ }
147
+ let lastWatches = component.renderWatcher.lastWatches;
148
+ let parent = component.getParent();
149
+ function nicerPath(path: string[]) {
150
+ if (path[1] === "PathFunctionRunner") {
151
+ path.splice(1, 1);
152
+ }
153
+ return path;
154
+ }
155
+ let pos = this.state.pos;
156
+ let filter = pathFilter.value.toLowerCase();
157
+ return (
158
+ <FullscreenModal
159
+ outerStyle={
160
+ pos === "fill" && {}
161
+ || pos === "top" && { height: "25vh", padding: 10 }
162
+ || pos === "bottom" && { height: "25vh", padding: 10, top: undefined, bottom: 0 }
163
+ || {}
164
+ }
165
+ style={
166
+ pos === "fill" && {}
167
+ || pos === "top" && { maxHeight: "100%" }
168
+ || pos === "bottom" && { maxHeight: "100%" }
169
+ || {}
170
+ }
171
+ onCancel={() => closeAllModals()}
172
+ >
173
+ <div class={css.hsl(0, 0, 100).vbox(6)}>
174
+ <div class={css.hbox(10).fillWidth}>
175
+ {component.debugName}
176
+ {parent && <Button onClick={() => this.state.parentNavigate++}>
177
+ Go to parent ({parent.debugName})
178
+ </Button>}
179
+ {this.state.parentNavigate > 0 && <Button onClick={() => this.state.parentNavigate--}>
180
+ Child
181
+ </Button>}
182
+ <div class={css.marginAuto} />
183
+ <Button onClick={() => this.state.pos = "top"}>
184
+ Top
185
+ </Button>
186
+ <Button onClick={() => this.state.pos = "fill"}>
187
+ Fill
188
+ </Button>
189
+ <Button onClick={() => this.state.pos = "bottom"}>
190
+ Bottom
191
+ </Button>
192
+ </div>
193
+
194
+ <div class={css.hbox(8)}>
195
+ <Button onClick={() => component.instance.forceUpdate()}>
196
+ Rerender
197
+ </Button>
198
+ {componentButtons.size > 0 && Array.from(componentButtons.values()).map(({ title, callback }) => (
199
+ <Button onClick={() => callback(component)}>
200
+ {title}
201
+ </Button>
202
+ ))}
203
+ </div>
204
+
205
+
206
+ <InputLabelURL hot label="Filter" url={pathFilter} />
207
+
208
+ <h4>Parent Paths ({lastWatches.parentPaths.size})</h4>
209
+ <div class={css.vbox(2)}>
210
+ {Array.from(lastWatches.parentPaths).map(path =>
211
+ <div class={css.hbox(4).wrap.button} onClick={() => {
212
+ console.log(path);
213
+ return navigator.clipboard.writeText(path);
214
+ }}>
215
+ {nicerPath(getPathFromStr(path)).map(x => <span class={css.pad2(3, 0).hsl(0, 0, 80)}>{x}</span>)}
216
+
217
+ <span class={css.pad2(3, 2).hsl(180, 75, 75)}>({authorityStorage.getPathsFromParent(path)?.size})</span>
218
+ </div>
219
+ )}
220
+ </div>
221
+ <h4>Paths ({lastWatches.paths.size})</h4>
222
+ {/* TODO: If we render a hierarchy, we will have space where the indent is, to show buttons. */}
223
+ <div class={css.vbox(2).fillWidth}>
224
+ {Array.from(lastWatches.paths).map(path => {
225
+ if (filter && !path.toLowerCase().includes(filter)) {
226
+ return undefined;
227
+ }
228
+ let value = pathValueSerializer.getPathValue(authorityStorage.getValueAtTime(path));
229
+ let valueStr = String(value);
230
+ try {
231
+ valueStr = String(JSON.stringify(value));
232
+ } catch { }
233
+ if (valueStr === "undefined" && typeof value === "function") {
234
+ valueStr = String(value).slice(0, 100);
235
+ }
236
+
237
+ if (valueStr.length > 500) {
238
+ valueStr = valueStr.slice(0, 500);
239
+ }
240
+ let pathArray = nicerPath(getPathFromStr(path));
241
+ let breakOnWrites = PathValueProxyWatcher.BREAK_ON_WRITES.has(path);
242
+ let breakOnReads = PathValueProxyWatcher.BREAK_ON_READS.has(path);
243
+ let logOnRead = PathValueProxyWatcher.LOG_WRITES_INCLUDES.has(path);
244
+ let fncBreak = PathValueProxyWatcher.SET_FUNCTION_WATCH_ON_WRITES.has(path);
245
+ let selectedButton = css.hsl(120, 75, 75);
246
+ return (
247
+ <div class={css.hbox(10, 0).fillWidth.wrap}>
248
+ <button class={breakOnWrites && selectedButton || ""} onClick={() => {
249
+ if (breakOnWrites) {
250
+ PathValueProxyWatcher.BREAK_ON_WRITES.delete(path);
251
+ } else {
252
+ PathValueProxyWatcher.BREAK_ON_WRITES.add(path);
253
+ }
254
+ this.forceUpdate();
255
+ }}>
256
+ =
257
+ </button>
258
+ <button class={breakOnReads && selectedButton || ""} onClick={() => {
259
+ if (breakOnReads) {
260
+ PathValueProxyWatcher.BREAK_ON_READS.delete(path);
261
+ } else {
262
+ PathValueProxyWatcher.BREAK_ON_READS.add(path);
263
+ }
264
+ this.forceUpdate();
265
+ }}>
266
+ ===
267
+ </button>
268
+ <button class={logOnRead && selectedButton || ""} onClick={() => {
269
+ if (logOnRead) {
270
+ PathValueProxyWatcher.LOG_WRITES_INCLUDES.delete(path);
271
+ } else {
272
+ PathValueProxyWatcher.LOG_WRITES_INCLUDES.add(path);
273
+ }
274
+ this.forceUpdate();
275
+ }}>
276
+ Log
277
+ </button>
278
+ <button class={fncBreak && selectedButton || ""} title="Breaks on the caller of the function that mutates this. Only works on the second call (and only if the function is input predicted)." onClick={() => {
279
+ if (breakOnWrites) {
280
+ PathValueProxyWatcher.SET_FUNCTION_WATCH_ON_WRITES.delete(path);
281
+ } else {
282
+ PathValueProxyWatcher.SET_FUNCTION_WATCH_ON_WRITES.add(path);
283
+ }
284
+ this.forceUpdate();
285
+ }}>
286
+ Caller*
287
+ </button>
288
+ <div class={css.hbox(4).wrap.button} onClick={() => {
289
+ console.log(path);
290
+ return navigator.clipboard.writeText(path);
291
+ }}>
292
+ {pathArray.map((x, index) => <span class={css.pad2(3, 2).hsl(0, 0, 80)}>
293
+ {x}
294
+ </span>)}
295
+ </div>
296
+ <div class={css.pad2(3, 2).hsl(180, 75, 75).ellipsis.button} onClick={() => {
297
+ console.log(value);
298
+ }}>
299
+ {valueStr}
300
+ </div>
301
+ </div>
302
+ );
303
+ })}
304
+ </div>
305
+ </div>
306
+ </FullscreenModal>
307
+ );
308
+ }
309
+ }
@@ -0,0 +1,26 @@
1
+ import { isBufferType } from "socket-function/src/misc";
2
+ import { pathValueSerializer } from "../-h-path-value-serialize/PathValueSerializer";
3
+ import { PathValue } from "../0-path-value-core/pathValueCore";
4
+
5
+ function getSingleSizeEstimateValue(node: PathValue) {
6
+ let value = pathValueSerializer.getPathValueOrBuffer(node);
7
+ if (isBufferType(value)) {
8
+ return value.byteLength;
9
+ }
10
+ if (typeof value === "string") {
11
+ return value.length * 2;
12
+ }
13
+ return 8;
14
+ }
15
+ /** NOTE: This will never be accurate, because of how strings are shared and compressed.
16
+ * But, if we need a conservative (over-estimate) of the size of a PathValue, this works well enough.
17
+ */
18
+ export function getSingleSizeEstimate(node: PathValue) {
19
+ return (
20
+ getSingleSizeEstimateValue(node)
21
+ + node.path.length * 2
22
+ + (node.source?.length || 0)
23
+ // Plus space for other misc fields
24
+ + 32
25
+ );
26
+ }
@@ -0,0 +1,47 @@
1
+ import { runInfinitePoll } from "socket-function/src/batching";
2
+ import { formatTime } from "socket-function/src/formatting/format";
3
+ import { registerMeasureInfo } from "socket-function/src/profiling/measure";
4
+ import { isNode } from "typesafecss";
5
+ import { monitorEventLoopDelay } from "perf_hooks";
6
+ import { registerNodeMetadata } from "./nodeMetadata";
7
+
8
+ const POLL_INTERVAL = 350;
9
+
10
+ export function trackSynchronousLag() {
11
+ if (isNode()) {
12
+ const histogram = monitorEventLoopDelay({});
13
+ histogram.enable();
14
+
15
+ registerMeasureInfo(() => {
16
+ const max = histogram.max;
17
+ const p99 = histogram.percentile(99);
18
+ const p50 = histogram.percentile(50);
19
+ histogram.reset();
20
+
21
+ return `BLOCKING LAG < ${formatTime(max / 1e6)} | 99% < ${formatTime(p99 / 1e6)} | 50% < ${formatTime(p50 / 1e6)}`;
22
+ });
23
+ registerNodeMetadata({
24
+ columnName: "Blocking Lag",
25
+ getValue: () => {
26
+ const max = histogram.max;
27
+ const p99 = histogram.percentile(99);
28
+ const p50 = histogram.percentile(50);
29
+ return [`< ${formatTime(max / 1e6)}`, `99% < ${formatTime(p99 / 1e6)}`, `50% < ${formatTime(p50 / 1e6)}`];
30
+ }
31
+ });
32
+ } else {
33
+ let lastLag = POLL_INTERVAL;
34
+ let time = Date.now();
35
+ runInfinitePoll(POLL_INTERVAL, function synchronousPoll() {
36
+ let newTime = Date.now();
37
+ let newLag = newTime - time;
38
+ lastLag = Math.max(lastLag, newLag);
39
+ time = newTime;
40
+ });
41
+ registerMeasureInfo(() => {
42
+ let curLag = lastLag;
43
+ lastLag = POLL_INTERVAL;
44
+ return `BLOCKING LAG < ${formatTime(curLag)}`;
45
+ });
46
+ }
47
+ }
@@ -0,0 +1,35 @@
1
+ module.allowclient = true;
2
+
3
+ import debugbreak from "debugbreak";
4
+ import { SocketFunction } from "socket-function/SocketFunction";
5
+ import { IdentityController_getReconnectNodeId } from "./-c-identity/IdentityController";
6
+
7
+ import { requiresNetworkTrustHook } from "./-d-trust/NetworkTrust2";
8
+ import os from "os";
9
+
10
+ class TestControllerBase {
11
+ async test() {
12
+ const caller = SocketFunction.getCaller();
13
+ console.log(`test called from ${caller.nodeId}`);
14
+ return {
15
+ nodeId: caller?.nodeId,
16
+ reconnectNodeId: IdentityController_getReconnectNodeId(caller),
17
+ };
18
+ }
19
+ async getProcessId() {
20
+ return process.pid;
21
+ }
22
+ }
23
+
24
+
25
+ export const TestController = SocketFunction.register(
26
+ "TestController-fbdd0aa9-a8ed-4d8c-80a8-8ab2f57c99db",
27
+ new TestControllerBase(),
28
+ () => ({
29
+ getProcessId: {},
30
+ test: {},
31
+ }),
32
+ () => ({
33
+ hooks: [requiresNetworkTrustHook],
34
+ })
35
+ );
File without changes
package/src/bits.ts ADDED
@@ -0,0 +1,86 @@
1
+
2
+ /** Subtracts the smallest possible value from a number (a double). This makes it possible to convert an exclusive range end
3
+ * to an inclusive range end, which is sometimes required (as in, < x is the same as <= minusEpsilon(x)).
4
+ */
5
+ export function minusEpsilon(value: number) {
6
+ let high = getHighUint32(value);
7
+ let low = getLowUint32(value);
8
+
9
+ if (low === 0) {
10
+ low = 2 ** 32 - 1;
11
+ high--;
12
+ } else {
13
+ low--;
14
+ }
15
+
16
+ return setLowHighUint32(low, high);
17
+ }
18
+ const maxUint32 = 2 ** 32 - 1;
19
+ export function addEpsilons(value: number, count: number) {
20
+ let high = getHighUint32(value);
21
+ let low = getLowUint32(value);
22
+
23
+ low += count;
24
+ if (low < 0) {
25
+ low += maxUint32;
26
+ high++;
27
+ } else if (low > maxUint32) {
28
+ low -= maxUint32;
29
+ high--;
30
+ }
31
+
32
+ return setLowHighUint32(low, high);
33
+ }
34
+
35
+ let conversionBuffer = new Float64Array(1);
36
+ let conversionUint8Buffer = new Uint8Array(conversionBuffer.buffer);
37
+ let conversionUint32Buffer = new Uint32Array(conversionBuffer.buffer);
38
+ export function getHighUint32(num: number): number {
39
+ conversionBuffer[0] = num;
40
+ return conversionUint32Buffer[1];
41
+ }
42
+ export function getLowUint32(num: number): number {
43
+ conversionBuffer[0] = num;
44
+ return conversionUint32Buffer[0];
45
+ }
46
+
47
+ /** IMPORTANT! Beware of comparisons with 64 bit numbers. getFloat64_fromBytes(4294136438, 168) !== getFloat64_fromBytes(4294136438, 168).
48
+ * USE is64BitEqual instead, OR, ensure the 2 highest bits are always 0.
49
+ */
50
+ export function setLowHighUint32(low: number, high: number): number {
51
+ conversionUint32Buffer[0] = low;
52
+ conversionUint32Buffer[1] = high;
53
+ return conversionBuffer[0];
54
+ }
55
+
56
+ // Gets bits that can be stored in a number. Specifically, the first 62 bits,
57
+ // as 64 bits will not compare correctly when treated as a double.
58
+ export function getShortNumber(buffer: Buffer): number {
59
+ let high = buffer.readUInt32BE(0) & 0x3FFFFFFF;
60
+ let low = buffer.readUInt32BE(4);
61
+ return setLowHighUint32(low, high);
62
+ }
63
+ /* Returns a number between 0 and 2**48 */
64
+ export function getBufferInt(buffer: Buffer): number {
65
+ let num = 0;
66
+ for (let i = 0; i < Math.min(buffer.length, 6); i++) {
67
+ num = num * 256 + buffer[i];
68
+ }
69
+ return num;
70
+ }
71
+ const intMax = 2 ** 48;
72
+ /** Returns a number between 0 (inclusive) and 1 (exclusive) */
73
+ export function getBufferFraction(buffer: Buffer): number {
74
+ let int = getBufferInt(buffer);
75
+ return int / intMax;
76
+ }
77
+
78
+ /*
79
+ export function numberToBase64(num: number): string {
80
+ conversionBuffer[0] = num;
81
+ return Buffer.from(conversionBuffer.buffer).toString("base64");
82
+ }
83
+ export function numberFromBase64(base64: string) {
84
+ return new Float64Array(Buffer.from(base64, "base64"))[0];
85
+ }
86
+ */