querysub 0.437.0 → 0.439.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 (81) hide show
  1. package/.eslintrc.js +50 -50
  2. package/bin/deploy.js +0 -0
  3. package/bin/function.js +0 -0
  4. package/bin/server.js +0 -0
  5. package/costsBenefits.txt +115 -115
  6. package/deploy.ts +2 -2
  7. package/package.json +2 -2
  8. package/spec.txt +1192 -1192
  9. package/src/-a-archives/archives.ts +202 -202
  10. package/src/-a-archives/archivesDisk.ts +454 -454
  11. package/src/-a-auth/certs.ts +540 -540
  12. package/src/-a-auth/node-forge-ed25519.d.ts +16 -16
  13. package/src/-b-authorities/dnsAuthority.ts +138 -138
  14. package/src/-c-identity/IdentityController.ts +258 -258
  15. package/src/-d-trust/NetworkTrust2.ts +180 -180
  16. package/src/-e-certs/EdgeCertController.ts +252 -252
  17. package/src/-e-certs/certAuthority.ts +201 -201
  18. package/src/-f-node-discovery/NodeDiscovery.ts +640 -640
  19. package/src/-g-core-values/NodeCapabilities.ts +200 -200
  20. package/src/-h-path-value-serialize/stringSerializer.ts +175 -175
  21. package/src/0-path-value-core/PathValueCommitter.ts +468 -468
  22. package/src/0-path-value-core/PathValueController.ts +0 -2
  23. package/src/0-path-value-core/archiveLocks/archiveSnapshots.ts +37 -1
  24. package/src/0-path-value-core/pathValueCore.ts +12 -0
  25. package/src/2-proxy/PathValueProxyWatcher.ts +2542 -2542
  26. package/src/2-proxy/TransactionDelayer.ts +94 -94
  27. package/src/2-proxy/pathDatabaseProxyBase.ts +36 -36
  28. package/src/2-proxy/pathValueProxy.ts +159 -159
  29. package/src/3-path-functions/PathFunctionRunner.ts +24 -13
  30. package/src/3-path-functions/PathFunctionRunnerMain.ts +87 -87
  31. package/src/3-path-functions/pathFunctionLoader.ts +516 -516
  32. package/src/3-path-functions/tests/rejectTest.ts +76 -76
  33. package/src/4-deploy/deployCheck.ts +6 -6
  34. package/src/4-dom/css.tsx +29 -29
  35. package/src/4-dom/cssTypes.d.ts +211 -211
  36. package/src/4-dom/qreact.tsx +2799 -2799
  37. package/src/4-dom/qreactTest.tsx +410 -410
  38. package/src/4-querysub/permissions.ts +335 -335
  39. package/src/4-querysub/querysubPrediction.ts +483 -483
  40. package/src/5-diagnostics/qreactDebug.tsx +400 -346
  41. package/src/TestController.ts +34 -34
  42. package/src/bits.ts +104 -104
  43. package/src/buffers.ts +69 -69
  44. package/src/diagnostics/ActionsHistory.ts +57 -57
  45. package/src/diagnostics/PathDistributionInfo.tsx +9 -1
  46. package/src/diagnostics/listenOnDebugger.ts +71 -71
  47. package/src/diagnostics/logs/IndexedLogs/BufferUnitIndex.ts +1 -1
  48. package/src/diagnostics/logs/diskLogger.ts +6 -0
  49. package/src/diagnostics/misc-pages/SnapshotViewer.tsx +78 -1
  50. package/src/diagnostics/periodic.ts +111 -111
  51. package/src/diagnostics/trackResources.ts +91 -91
  52. package/src/diagnostics/watchdog.ts +120 -120
  53. package/src/errors.ts +133 -133
  54. package/src/forceProduction.ts +2 -2
  55. package/src/fs.ts +80 -80
  56. package/src/functional/diff.ts +857 -857
  57. package/src/functional/promiseCache.ts +78 -78
  58. package/src/functional/random.ts +8 -8
  59. package/src/functional/stats.ts +60 -60
  60. package/src/heapDumps.ts +665 -665
  61. package/src/https.ts +1 -1
  62. package/src/library-components/AspectSizedComponent.tsx +87 -87
  63. package/src/library-components/ButtonSelector.tsx +64 -64
  64. package/src/library-components/DropdownCustom.tsx +150 -150
  65. package/src/library-components/DropdownSelector.tsx +31 -31
  66. package/src/library-components/InlinePopup.tsx +66 -66
  67. package/src/library-components/uncaughtToast.tsx +2 -0
  68. package/src/misc/color.ts +29 -29
  69. package/src/misc/hash.ts +83 -83
  70. package/src/misc/ipPong.js +13 -13
  71. package/src/misc/networking.ts +1 -1
  72. package/src/misc/random.ts +44 -44
  73. package/src/misc.ts +196 -196
  74. package/src/path.ts +255 -255
  75. package/src/persistentLocalStore.ts +41 -41
  76. package/src/promise.ts +14 -14
  77. package/src/storage/fileSystemPointer.ts +71 -71
  78. package/src/test/heapProcess.ts +35 -35
  79. package/src/zip.ts +15 -15
  80. package/tsconfig.json +26 -26
  81. package/yarnSpec.txt +56 -56
@@ -1,347 +1,401 @@
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
- import { isCallerDynamicModule, isDynamicModule } from "../3-path-functions/pathFunctionLoader";
20
-
21
- // Map, so hot reloading doesn't break things
22
- let componentButtons = new Map<string, { title: string, callback: (component: ExternalRenderClass) => void }>();
23
- let componentUIs = new Map<string, (component: ExternalRenderClass) => qreact.ComponentChildren>();
24
-
25
- export function addComponentButton(config: {
26
- title: string;
27
- callback: (component: ExternalRenderClass) => void;
28
- }) {
29
- if (isCallerDynamicModule()) return;
30
- if (!isHotReloading() && componentButtons.has(config.title)) {
31
- throw new Error(`Component button with title ${config.title} already exists`);
32
- }
33
- componentButtons.set(config.title, config);
34
- }
35
-
36
- export function addComponentUI(config: {
37
- key: string;
38
- createUI: (component: ExternalRenderClass) => qreact.ComponentChildren;
39
- }) {
40
- if (isCallerDynamicModule()) return;
41
- if (!isHotReloading() && componentUIs.has(config.key)) {
42
- throw new Error(`Component UI with key ${config.key} already exists`);
43
- }
44
- componentUIs.set(config.key, config.createUI);
45
- }
46
-
47
- export const enableDebugComponents = lazy(function enableDebugComponents() {
48
- console.log(magenta(`middleclick + shift on components to debug them`));
49
- // mousedown is more reliable than click, because the click will be aborted if they middle click
50
- // in something with a scrollbar.
51
- document.addEventListener("mousedown", function (e) {
52
- // Middle click to debug
53
- if (e.button === 1 && (e.altKey || e.shiftKey)) {
54
- logErrors(triggerDebug(e.target as HTMLElement, e));
55
- }
56
- });
57
-
58
- async function triggerDebug(target: HTMLElement, event: MouseEvent) {
59
- const component = __INTERNAL__QRenderClass.getInstanceFromDOM(target);
60
- if (!component) return;
61
- event.preventDefault();
62
- event.stopPropagation();
63
-
64
- let prevInject = qreact.INJECT_LINE_NUMBERS;
65
- try {
66
- qreact.INJECT_LINE_NUMBERS = true;
67
- triggerRerenderAll();
68
- await clientWatcher.waitForTriggerFinished();
69
- } finally {
70
- qreact.INJECT_LINE_NUMBERS = prevInject;
71
- }
72
- // Get new target under the mouse
73
- target = document.elementFromPoint(event.clientX, event.clientY) as HTMLElement;
74
-
75
- let source = getSourceVSCodeLink(target);
76
- if (event.shiftKey) {
77
- let newComponent = __INTERNAL__QRenderClass.getInstanceFromDOM(target) || component;
78
- showModal({ content: <WatchModal component={newComponent} />, onClose: () => "abortClose" });
79
- return;
80
- }
81
-
82
- let ancestors = component.getDebugComponentStack().slice(1).reverse();
83
- let schemaPrefix = getSchemaPrefix();
84
- let schemaLength = getPathFromStr(schemaPrefix).length;
85
- function encodePath(pathStr: string) {
86
- let path = getPathFromStr(pathStr);
87
- if (pathStr.startsWith(schemaPrefix)) {
88
- let underPath = path.slice(schemaLength);
89
- let componentId = Number(underPath[0]);
90
- let component = __INTERNAL__QRenderClass.getInstanceAllowedUndefined(componentId);
91
- if (component) {
92
- path = [component.debugName].concat(underPath.slice(1));
93
- }
94
- }
95
- // If paths are to other components, replace the prefix with the component debugName
96
- return path.map(x => green(x)).join(".");
97
- }
98
-
99
- console.log(" ");
100
- console.log(" ");
101
- console.log(" ");
102
- if (event.altKey) {
103
- window.open(source);
104
- }
105
- // VSCode link
106
- if (source) {
107
- console.log(source);
108
- }
109
- console.group(`Component ${blue(component.debugName)}`);
110
- console.groupCollapsed(` Ancestors (${ancestors.length})`);
111
- for (let ancestor of ancestors) {
112
- console.log(" " + ancestor.debugName);
113
- let source = ancestor.getVSCodeLink();
114
- if (source) {
115
- console.log(` ${source}`);
116
- }
117
- }
118
- console.groupEnd();
119
- let lastWatches = component.renderWatcher!.lastWatches;
120
-
121
- let watchPaths = Array.from(lastWatches.paths).filter(x => getPathFromStr(x).length > schemaLength);
122
- console.groupCollapsed(` Watches (${watchPaths.length}):`);
123
- for (let key of watchPaths) {
124
- console.log(" " + encodePath(key));
125
- }
126
- console.groupEnd();
127
-
128
- let parentPaths = Array.from(lastWatches.parentPaths).filter(x => getPathFromStr(x).length > schemaLength);
129
- if (parentPaths.length > 0) {
130
- console.groupCollapsed(` Parent Watches (${parentPaths.length}):`);
131
- for (let key of parentPaths) {
132
- if (getPathFromStr(key).length <= schemaLength) continue;
133
- console.log(" " + encodePath(key));
134
- }
135
- console.groupEnd();
136
- }
137
-
138
- console.log(` debugHolder = `, component);
139
- (globalThis as any).debugHolder = component;
140
- console.log(` debugInstance`, component.instance);
141
- (globalThis as any).debugInstance = component.instance;
142
-
143
- console.groupEnd();
144
- }
145
- });
146
-
147
-
148
- const pathFilter = new URLParam("pathfilter", "");
149
- class WatchModal extends qreact.Component<{
150
- component: ExternalRenderClass;
151
- }> {
152
- state = {
153
- parentNavigate: 0,
154
- pos: "fill" as "top" | "fill" | "bottom",
155
- };
156
- render() {
157
- let component = this.props.component;
158
- for (let i = 0; i < this.state.parentNavigate; i++) {
159
- component = component.getParent() || component;
160
- }
161
- let lastWatches = component.renderWatcher?.lastWatches || {
162
- paths: new Set(),
163
- parentPaths: new Set(),
164
- };
165
- let parent: ExternalRenderClass | undefined;
166
- try {
167
- parent = component.getParent();
168
- } catch (e) {
169
- console.log(`Failed to get parent`, e);
170
- }
171
- function nicerPath(path: string[]) {
172
- if (path[1] === "PathFunctionRunner") {
173
- path.splice(1, 1);
174
- }
175
- return path;
176
- }
177
- let pos = this.state.pos;
178
- let filter = pathFilter.value.toLowerCase();
179
- return (
180
- <FullscreenModal
181
- onlyExplicitClose
182
- outerStyle={
183
- pos === "fill" && {}
184
- || pos === "top" && { height: "25vh", padding: 10 }
185
- || pos === "bottom" && { height: "25vh", padding: 10, top: undefined, bottom: 0 }
186
- || {}
187
- }
188
- style={
189
- {
190
- ...(
191
- pos === "fill" && {}
192
- || pos === "top" && { maxHeight: "100%" }
193
- || pos === "bottom" && { maxHeight: "100%" }
194
- || {}
195
- ),
196
- background: "rgb(255, 255, 255)!important",
197
- }
198
- }
199
- onCancel={() => {
200
- if (this.state.pos !== "fill") {
201
- return "abortClose";
202
- }
203
- }}
204
- >
205
- <div class={css.hsl(0, 0, 100).hslcolor(0, 0, 10).vbox(6)}>
206
- <div class={css.hbox(10).fillWidth}>
207
- {component.debugName}
208
- {parent && <Button onClick={() => this.state.parentNavigate++}>
209
- Go to parent ({parent.debugName})
210
- </Button>}
211
- {this.state.parentNavigate > 0 && <Button onClick={() => this.state.parentNavigate--}>
212
- Child
213
- </Button>}
214
- <div class={css.marginAuto} />
215
- <Button onClick={() => this.state.pos = "top"}>
216
- Top
217
- </Button>
218
- <Button onClick={() => this.state.pos = "fill"}>
219
- Fill
220
- </Button>
221
- <Button onClick={() => this.state.pos = "bottom"}>
222
- Bottom
223
- </Button>
224
- <Button onClick={() => {
225
- closeAllModals();
226
- }}>
227
- Close
228
- </Button>
229
- </div>
230
-
231
- <div class={css.hbox(8)}>
232
- <Button onClick={() => component.instance.forceUpdate()}>
233
- Rerender
234
- </Button>
235
- {Array.from(componentButtons.values()).map(({ title, callback }) => (
236
- <Button onClick={() => callback(component)}>
237
- {title}
238
- </Button>
239
- ))}
240
- </div>
241
- {Array.from(componentUIs.values()).map((createUI) => createUI(component))}
242
-
243
-
244
- <InputLabelURL hot label="Filter" url={pathFilter} />
245
-
246
- <h4>Parent Paths ({lastWatches.parentPaths.size})</h4>
247
- <div class={css.vbox(2)}>
248
- {Array.from(lastWatches.parentPaths).map(path =>
249
- <div class={css.hbox(4).wrap.button} onClick={() => {
250
- console.log(path);
251
- return navigator.clipboard.writeText(path);
252
- }}>
253
- {nicerPath(getPathFromStr(path)).map(x => <span class={css.pad2(3, 0).hsl(0, 0, 80)}>{x}</span>)}
254
-
255
- <span class={css.pad2(3, 2).hsl(180, 75, 75)}>({authorityStorage.getPathsFromParent(path)?.size})</span>
256
- </div>
257
- )}
258
- </div>
259
- <h4>Paths ({lastWatches.paths.size})</h4>
260
- {/* TODO: If we render a hierarchy, we will have space where the indent is, to show buttons. */}
261
- <div class={css.vbox(2).fillWidth}>
262
- {Array.from(lastWatches.paths).map(path => {
263
- if (filter && !path.toLowerCase().includes(filter)) {
264
- return undefined;
265
- }
266
- let value = pathValueSerializer.getPathValue(authorityStorage.getValueAtTime(path));
267
- let valueStr = String(value);
268
- try {
269
- valueStr = String(JSON.stringify(value));
270
- } catch { }
271
- if (valueStr === "undefined" && typeof value === "function") {
272
- valueStr = String(value).slice(0, 100);
273
- }
274
-
275
- if (valueStr.length > 500) {
276
- valueStr = valueStr.slice(0, 500);
277
- }
278
- let pathArray = nicerPath(getPathFromStr(path));
279
- let breakOnWrites = PathValueProxyWatcher.BREAK_ON_WRITES.has(path);
280
- let breakOnReads = PathValueProxyWatcher.BREAK_ON_READS.has(path);
281
- let logOnRead = PathValueProxyWatcher.LOG_WRITES_INCLUDES.has(path);
282
- let fncBreak = PathValueProxyWatcher.SET_FUNCTION_WATCH_ON_WRITES.has(path);
283
- let selectedButton = css.hsl(120, 75, 75);
284
- return (
285
- <div class={css.hbox(10, 0).fillWidth.wrap}>
286
- <button class={breakOnWrites && selectedButton || ""} onClick={() => {
287
- if (breakOnWrites) {
288
- PathValueProxyWatcher.BREAK_ON_WRITES.delete(path);
289
- } else {
290
- PathValueProxyWatcher.BREAK_ON_WRITES.add(path);
291
- }
292
- this.forceUpdate();
293
- }}>
294
- =
295
- </button>
296
- <button class={breakOnReads && selectedButton || ""} onClick={() => {
297
- if (breakOnReads) {
298
- PathValueProxyWatcher.BREAK_ON_READS.delete(path);
299
- } else {
300
- PathValueProxyWatcher.BREAK_ON_READS.add(path);
301
- }
302
- this.forceUpdate();
303
- }}>
304
- ===
305
- </button>
306
- <button class={logOnRead && selectedButton || ""} onClick={() => {
307
- if (logOnRead) {
308
- PathValueProxyWatcher.LOG_WRITES_INCLUDES.delete(path);
309
- } else {
310
- PathValueProxyWatcher.LOG_WRITES_INCLUDES.add(path);
311
- }
312
- this.forceUpdate();
313
- }}>
314
- Log
315
- </button>
316
- <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={() => {
317
- if (breakOnWrites) {
318
- PathValueProxyWatcher.SET_FUNCTION_WATCH_ON_WRITES.delete(path);
319
- } else {
320
- PathValueProxyWatcher.SET_FUNCTION_WATCH_ON_WRITES.add(path);
321
- }
322
- this.forceUpdate();
323
- }}>
324
- Caller*
325
- </button>
326
- <div class={css.hbox(4).wrap.button} onClick={() => {
327
- console.log(path);
328
- return navigator.clipboard.writeText(path);
329
- }}>
330
- {pathArray.map((x, index) => <span class={css.pad2(3, 2).hsl(0, 0, 80)}>
331
- {x}
332
- </span>)}
333
- </div>
334
- <div class={css.pad2(3, 2).hsl(180, 75, 75).ellipsis.button} onClick={() => {
335
- console.log(value);
336
- }}>
337
- {valueStr}
338
- </div>
339
- </div>
340
- );
341
- })}
342
- </div>
343
- </div>
344
- </FullscreenModal>
345
- );
346
- }
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, showFullscreenModal } 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
+ import { isCallerDynamicModule, isDynamicModule } from "../3-path-functions/pathFunctionLoader";
20
+ import { ATag } from "../library-components/ATag";
21
+ import { managementPageURL, showingManagementURL } from "../diagnostics/managementPages";
22
+ import { endTimeParam, startTimeParam } from "../diagnostics/logs/TimeRangeSelector";
23
+ import { timeInHour } from "socket-function/src/misc";
24
+ import { additionalSearchURL, lifecycleIdURL } from "../diagnostics/logs/lifeCycleAnalysis/LifeCyclePage";
25
+ import { parseDebugName } from "../3-path-functions/PathFunctionRunner";
26
+
27
+ // Map, so hot reloading doesn't break things
28
+ let componentButtons = new Map<string, { title: string, callback: (component: ExternalRenderClass) => void }>();
29
+ let componentUIs = new Map<string, (component: ExternalRenderClass) => qreact.ComponentChildren>();
30
+
31
+ export function addComponentButton(config: {
32
+ title: string;
33
+ callback: (component: ExternalRenderClass) => void;
34
+ }) {
35
+ if (isCallerDynamicModule()) return;
36
+ if (!isHotReloading() && componentButtons.has(config.title)) {
37
+ throw new Error(`Component button with title ${config.title} already exists`);
38
+ }
39
+ componentButtons.set(config.title, config);
40
+ }
41
+
42
+ export function addComponentUI(config: {
43
+ key: string;
44
+ createUI: (component: ExternalRenderClass) => qreact.ComponentChildren;
45
+ }) {
46
+ if (isCallerDynamicModule()) return;
47
+ if (!isHotReloading() && componentUIs.has(config.key)) {
48
+ throw new Error(`Component UI with key ${config.key} already exists`);
49
+ }
50
+ componentUIs.set(config.key, config.createUI);
51
+ }
52
+
53
+ export const enableDebugComponents = lazy(function enableDebugComponents() {
54
+ console.log(magenta(`middleclick + shift on components to debug them`));
55
+ // mousedown is more reliable than click, because the click will be aborted if they middle click
56
+ // in something with a scrollbar.
57
+ document.addEventListener("mousedown", function (e) {
58
+ // Middle click to debug
59
+ if (e.button === 1 && (e.altKey || e.shiftKey)) {
60
+ logErrors(triggerDebug(e.target as HTMLElement, e));
61
+ }
62
+ });
63
+
64
+ async function triggerDebug(target: HTMLElement, event: MouseEvent) {
65
+ const component = __INTERNAL__QRenderClass.getInstanceFromDOM(target);
66
+ if (!component) return;
67
+ event.preventDefault();
68
+ event.stopPropagation();
69
+
70
+ let prevInject = qreact.INJECT_LINE_NUMBERS;
71
+ try {
72
+ qreact.INJECT_LINE_NUMBERS = true;
73
+ triggerRerenderAll();
74
+ await clientWatcher.waitForTriggerFinished();
75
+ } finally {
76
+ qreact.INJECT_LINE_NUMBERS = prevInject;
77
+ }
78
+ // Get new target under the mouse
79
+ target = document.elementFromPoint(event.clientX, event.clientY) as HTMLElement;
80
+
81
+ let source = getSourceVSCodeLink(target);
82
+ if (event.shiftKey) {
83
+ let newComponent = __INTERNAL__QRenderClass.getInstanceFromDOM(target) || component;
84
+ let doClose: { close: boolean } = { close: false };
85
+ showModal({
86
+ content: <WatchModal component={newComponent} doClose={doClose} />,
87
+ onClose: () => doClose.close ? undefined : "abortClose"
88
+ });
89
+ return;
90
+ }
91
+
92
+ let ancestors = component.getDebugComponentStack().slice(1).reverse();
93
+ let schemaPrefix = getSchemaPrefix();
94
+ let schemaLength = getPathFromStr(schemaPrefix).length;
95
+ function encodePath(pathStr: string) {
96
+ let path = getPathFromStr(pathStr);
97
+ if (pathStr.startsWith(schemaPrefix)) {
98
+ let underPath = path.slice(schemaLength);
99
+ let componentId = Number(underPath[0]);
100
+ let component = __INTERNAL__QRenderClass.getInstanceAllowedUndefined(componentId);
101
+ if (component) {
102
+ path = [component.debugName].concat(underPath.slice(1));
103
+ }
104
+ }
105
+ // If paths are to other components, replace the prefix with the component debugName
106
+ return path.map(x => green(x)).join(".");
107
+ }
108
+
109
+ console.log(" ");
110
+ console.log(" ");
111
+ console.log(" ");
112
+ if (event.altKey) {
113
+ window.open(source);
114
+ }
115
+ // VSCode link
116
+ if (source) {
117
+ console.log(source);
118
+ }
119
+ console.group(`Component ${blue(component.debugName)}`);
120
+ console.groupCollapsed(` Ancestors (${ancestors.length})`);
121
+ for (let ancestor of ancestors) {
122
+ console.log(" " + ancestor.debugName);
123
+ let source = ancestor.getVSCodeLink();
124
+ if (source) {
125
+ console.log(` ${source}`);
126
+ }
127
+ }
128
+ console.groupEnd();
129
+ let lastWatches = component.renderWatcher!.lastWatches;
130
+
131
+ let watchPaths = Array.from(lastWatches.paths).filter(x => getPathFromStr(x).length > schemaLength);
132
+ console.groupCollapsed(` Watches (${watchPaths.length}):`);
133
+ for (let key of watchPaths) {
134
+ console.log(" " + encodePath(key));
135
+ }
136
+ console.groupEnd();
137
+
138
+ let parentPaths = Array.from(lastWatches.parentPaths).filter(x => getPathFromStr(x).length > schemaLength);
139
+ if (parentPaths.length > 0) {
140
+ console.groupCollapsed(` Parent Watches (${parentPaths.length}):`);
141
+ for (let key of parentPaths) {
142
+ if (getPathFromStr(key).length <= schemaLength) continue;
143
+ console.log(" " + encodePath(key));
144
+ }
145
+ console.groupEnd();
146
+ }
147
+
148
+ console.log(` debugHolder = `, component);
149
+ (globalThis as any).debugHolder = component;
150
+ console.log(` debugInstance`, component.instance);
151
+ (globalThis as any).debugInstance = component.instance;
152
+
153
+ console.groupEnd();
154
+ }
155
+ });
156
+
157
+
158
+ const pathFilter = new URLParam("pathfilter", "");
159
+ class WatchModal extends qreact.Component<{
160
+ component: ExternalRenderClass;
161
+ doClose: { close: boolean };
162
+ }> {
163
+ state = {
164
+ parentNavigate: 0,
165
+ pos: "fill" as "top" | "fill" | "bottom",
166
+ };
167
+ componentDidMount() {
168
+ document.addEventListener("keydown", this.keyDown, true);
169
+ Querysub.onDispose(() => {
170
+ document.removeEventListener("keydown", this.keyDown, true);
171
+ });
172
+ }
173
+ keyDown = (e: KeyboardEvent) => {
174
+ if (e.key === "Escape") {
175
+ Querysub.commit(() => {
176
+ this.props.doClose.close = true;
177
+ closeAllModals();
178
+ });
179
+ }
180
+ };
181
+ render() {
182
+ let component = this.props.component;
183
+ for (let i = 0; i < this.state.parentNavigate; i++) {
184
+ component = component.getParent() || component;
185
+ }
186
+ let lastWatches = component.renderWatcher?.lastWatches || {
187
+ paths: new Set(),
188
+ parentPaths: new Set(),
189
+ };
190
+ let parent: ExternalRenderClass | undefined;
191
+ try {
192
+ parent = component.getParent();
193
+ } catch (e) {
194
+ console.log(`Failed to get parent`, e);
195
+ }
196
+ function nicerPath(path: string[]) {
197
+ if (path[1] === "PathFunctionRunner") {
198
+ path.splice(1, 1);
199
+ }
200
+ return path;
201
+ }
202
+ let pos = this.state.pos;
203
+ let filter = pathFilter.value.toLowerCase();
204
+ return (
205
+ <div
206
+ style={{
207
+ position: "fixed",
208
+ top: 0,
209
+ left: 0,
210
+ width: "100vw",
211
+ height: "100vh",
212
+ padding: 100,
213
+ display: "flex",
214
+ alignItems: "center",
215
+ justifyContent: "center",
216
+ overflow: "auto",
217
+ ...(pos === "fill" && {}
218
+ || pos === "top" && { height: "25vh", padding: 10 }
219
+ || pos === "bottom" && { height: "25vh", padding: 10, top: undefined, bottom: 0 }
220
+ || {}),
221
+ }}>
222
+ <div
223
+ className="keepModalsOpen"
224
+ style={{
225
+ background: "hsl(0, 0%, 100%)",
226
+ padding: 20,
227
+ color: "hsl(0, 0%, 7%)",
228
+ cursor: "default",
229
+ width: "100%",
230
+ display: "flex",
231
+ flexDirection: "column",
232
+ gap: 10,
233
+ height: "100%",
234
+ overflow: "auto",
235
+ }}
236
+ >
237
+ <div class={css.hsl(0, 0, 100).hslcolor(0, 0, 10).vbox(6).fillBoth}>
238
+ <div class={css.hbox(10).fillWidth}>
239
+ {component.debugName}
240
+ {parent && <Button onClick={() => this.state.parentNavigate++}>
241
+ Go to parent ({parent.debugName})
242
+ </Button>}
243
+ {this.state.parentNavigate > 0 && <Button onClick={() => this.state.parentNavigate--}>
244
+ Child
245
+ </Button>}
246
+ <div class={css.marginAuto} />
247
+ <Button onClick={() => this.state.pos = "top"}>
248
+ Top
249
+ </Button>
250
+ <Button onClick={() => this.state.pos = "fill"}>
251
+ Fill
252
+ </Button>
253
+ <Button onClick={() => this.state.pos = "bottom"}>
254
+ Bottom
255
+ </Button>
256
+ <Button onClick={() => {
257
+ this.props.doClose.close = true;
258
+ closeAllModals();
259
+ }}>
260
+ Close
261
+ </Button>
262
+ </div>
263
+
264
+ <div class={css.hbox(8)}>
265
+ <Button onClick={() => component.instance.forceUpdate()}>
266
+ Rerender
267
+ </Button>
268
+ {Array.from(componentButtons.values()).map(({ title, callback }) => (
269
+ <Button onClick={() => callback(component)}>
270
+ {title}
271
+ </Button>
272
+ ))}
273
+ </div>
274
+ {Array.from(componentUIs.values()).map((createUI) => createUI(component))}
275
+
276
+
277
+ <InputLabelURL hot label="Filter" url={pathFilter} />
278
+
279
+ <h4>Parent Paths ({lastWatches.parentPaths.size})</h4>
280
+ <div class={css.vbox(2)}>
281
+ {Array.from(lastWatches.parentPaths).map(path =>
282
+ <div class={css.hbox(4).wrap.button} onClick={() => {
283
+ console.log(path);
284
+ return navigator.clipboard.writeText(path);
285
+ }}>
286
+ {nicerPath(getPathFromStr(path)).map(x => <span class={css.pad2(3, 0).hsl(0, 0, 80)}>{x}</span>)}
287
+
288
+ <span class={css.pad2(3, 2).hsl(180, 75, 75)}>({authorityStorage.getPathsFromParent(path)?.size})</span>
289
+ </div>
290
+ )}
291
+ </div>
292
+ <h4>Paths ({lastWatches.paths.size})</h4>
293
+ <div class={css.vbox(2).fillBoth.overflowAuto}>
294
+ {Array.from(lastWatches.paths).map(path => {
295
+ if (filter && !path.toLowerCase().includes(filter)) {
296
+ return undefined;
297
+ }
298
+ let valueObj = authorityStorage.getValueAtTime(path);
299
+ let value = pathValueSerializer.getPathValue(valueObj);
300
+ let valueStr = String(value);
301
+ try {
302
+ valueStr = String(JSON.stringify(value));
303
+ } catch { }
304
+ if (valueStr === "undefined" && typeof value === "function") {
305
+ valueStr = String(value).slice(0, 100);
306
+ }
307
+
308
+ if (valueStr.length > 500) {
309
+ valueStr = valueStr.slice(0, 500);
310
+ }
311
+ let pathArray = nicerPath(getPathFromStr(path));
312
+ let breakOnWrites = PathValueProxyWatcher.BREAK_ON_WRITES.has(path);
313
+ let breakOnReads = PathValueProxyWatcher.BREAK_ON_READS.has(path);
314
+ let logOnRead = PathValueProxyWatcher.LOG_WRITES_INCLUDES.has(path);
315
+ let fncBreak = PathValueProxyWatcher.SET_FUNCTION_WATCH_ON_WRITES.has(path);
316
+ let selectedButton = css.hsl(120, 75, 75);
317
+
318
+ let sourceObj = parseDebugName(valueObj?.source || "");
319
+
320
+ return (
321
+ <div class={css.hbox(10, 0).fillWidth.wrap}>
322
+ <button class={breakOnWrites && selectedButton || ""} onClick={() => {
323
+ if (breakOnWrites) {
324
+ PathValueProxyWatcher.BREAK_ON_WRITES.delete(path);
325
+ } else {
326
+ PathValueProxyWatcher.BREAK_ON_WRITES.add(path);
327
+ }
328
+ this.forceUpdate();
329
+ }}>
330
+ =
331
+ </button>
332
+ <button class={breakOnReads && selectedButton || ""} onClick={() => {
333
+ if (breakOnReads) {
334
+ PathValueProxyWatcher.BREAK_ON_READS.delete(path);
335
+ } else {
336
+ PathValueProxyWatcher.BREAK_ON_READS.add(path);
337
+ }
338
+ this.forceUpdate();
339
+ }}>
340
+ ===
341
+ </button>
342
+ <button class={logOnRead && selectedButton || ""} onClick={() => {
343
+ if (logOnRead) {
344
+ PathValueProxyWatcher.LOG_WRITES_INCLUDES.delete(path);
345
+ } else {
346
+ PathValueProxyWatcher.LOG_WRITES_INCLUDES.add(path);
347
+ }
348
+ this.forceUpdate();
349
+ }}>
350
+ Log
351
+ </button>
352
+ <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={() => {
353
+ if (breakOnWrites) {
354
+ PathValueProxyWatcher.SET_FUNCTION_WATCH_ON_WRITES.delete(path);
355
+ } else {
356
+ PathValueProxyWatcher.SET_FUNCTION_WATCH_ON_WRITES.add(path);
357
+ }
358
+ this.forceUpdate();
359
+ }}>
360
+ Caller*
361
+ </button>
362
+ {
363
+ valueObj && sourceObj && <ATag values={[
364
+ showingManagementURL.getOverride(true),
365
+ managementPageURL.getOverride("LifeCyclePage"),
366
+ startTimeParam.getOverride(valueObj.time.time - timeInHour),
367
+ endTimeParam.getOverride(valueObj.time.time + timeInHour),
368
+ lifecycleIdURL.getOverride("1772917451506.018_0.441679673600166"),
369
+ additionalSearchURL.getOverride(`${sourceObj.functionId} & ${sourceObj.runAtTime.time}`),
370
+ ]}
371
+ title={valueObj.source || ""}
372
+ >
373
+ View Call
374
+ </ATag>
375
+ || <div title={valueObj?.source || ""}>
376
+ Source
377
+ </div>
378
+ }
379
+ <div class={css.hbox(4).wrap.button} onClick={() => {
380
+ console.log(path);
381
+ return navigator.clipboard.writeText(path);
382
+ }}>
383
+ {pathArray.map((x, index) => <span class={css.pad2(3, 2).hsl(0, 0, 80)}>
384
+ {x}
385
+ </span>)}
386
+ </div>
387
+ <div class={css.pad2(3, 2).hsl(180, 75, 75).ellipsis.button} onClick={() => {
388
+ console.log(value);
389
+ }}>
390
+ {valueStr}
391
+ </div>
392
+ </div>
393
+ );
394
+ })}
395
+ </div>
396
+ </div>
397
+ </div>
398
+ </div>
399
+ );
400
+ }
347
401
  }