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,666 @@
1
+ // See https://github.com/nodejs/node/blob/main/deps/v8/src/profiler/heap-snapshot-generator.cc
2
+
3
+ import v8 from "v8";
4
+ import { red, yellow, blue } from "socket-function/src/formatting/logColors";
5
+ import { logErrors } from "./errors";
6
+ import { formatNumber } from "socket-function/src/formatting/format";
7
+ import fs from "fs";
8
+ import { delay } from "socket-function/src/batching";
9
+ import { getSubFolder } from "./fs";
10
+
11
+ // Made to be called by stepping in via the debugger, ex:
12
+ // require("D:/repos/shard/src/heapDumps.ts").writeHeapSnapshot(undefined, "0_afterboot")
13
+ // (If no one imports this it won't be available until someone requires it, so you always have to require it...)
14
+
15
+ (async function pasteToDump() {
16
+ const v8 = require("v8");
17
+ const fs = require("fs");
18
+ // @ts-ignore
19
+ let parts = [];
20
+ let stream = v8.getHeapSnapshot();
21
+ // @ts-ignore
22
+ stream.on("data", chunk => parts.push(chunk));
23
+ await new Promise(resolve => stream.on("end", resolve));
24
+ // @ts-ignore
25
+ let buffer = Buffer.concat(parts);
26
+ fs.writeFileSync("./heapdump.heapsnapshot", buffer);
27
+ })().catch(e => console.error(e)).finally(() => console.log("done"));
28
+
29
+ export async function writeHeapSnapshot(writeDir = getSubFolder("heapdumps"), name: string): Promise<Buffer> {
30
+ global.gc?.();
31
+ let parts: Buffer[] = [];
32
+ let stream = v8.getHeapSnapshot();
33
+ stream.on("data", chunk => parts.push(chunk));
34
+ await new Promise(resolve => stream.on("end", resolve));
35
+ let buffer = Buffer.concat(parts);
36
+ if (writeDir) {
37
+ let size = getHeapsnapshotMemoryUsage(buffer);
38
+ let filename = `heapdump-${name}-pid-${process.pid}-time-${new Date().toISOString().replace(/:/g, "-")}-size-${formatNumber(size)}B.heapsnapshot`;
39
+ if (!writeDir.endsWith("/")) {
40
+ writeDir += "/";
41
+ }
42
+ await fs.promises.writeFile(writeDir + filename, buffer);
43
+ }
44
+ return buffer;
45
+ }
46
+
47
+ const progressCalls = 100;
48
+ const exportsGlobal = true;
49
+
50
+ export type HeapSummary = {
51
+ name: string;
52
+ total: number;
53
+ summary: {
54
+ [key: string]: {
55
+ key: string;
56
+ sum: number;
57
+ count: number;
58
+ firstId: number;
59
+ };
60
+ };
61
+ };
62
+
63
+ export interface HeapsnapshotFormat {
64
+ snapshot: {
65
+ meta: {
66
+ edge_fields: ("type" | "name_or_index" | "to_node")[];
67
+ location_fields: ("object_index" | "script_id" | "line" | "column")[];
68
+ node_fields: ("type" | "name" | "id" | "self_size" | "edge_count" | "trace_node_id" | "detachedness")[];
69
+
70
+ edge_types: (string | string[])[];
71
+ node_types: (string | string[])[];
72
+ };
73
+
74
+ node_count: number;
75
+ edge_count: number;
76
+ trace_function_count: number;
77
+ };
78
+
79
+ edges: number[];
80
+ locations: number[];
81
+ nodes: number[];
82
+
83
+ strings: string[];
84
+
85
+ name: string;
86
+ }
87
+
88
+ /** TIMING: 38ms for a 90MB file, so effectively free (this code doesn't even have to read the entire buffer to get the memory usage). */
89
+ export function getHeapsnapshotMemoryUsage(buffer: Buffer): number {
90
+ let sum = 0;
91
+
92
+ let index = 0;
93
+ const newLine = "\n".charCodeAt(0);
94
+ const end = "]".charCodeAt(0);
95
+ const comma = ",".charCodeAt(0);
96
+
97
+ // Skip until the second line
98
+ while (buffer[index] !== 10) {
99
+ index++;
100
+ }
101
+ index++;
102
+
103
+ let metadata = JSON.parse(buffer.toString("utf8", 0, index - 2) + "}") as HeapsnapshotFormat;
104
+
105
+ index += `"nodes":`.length;
106
+
107
+ let nodeSelfSizeIndex = metadata.snapshot.meta.node_fields.indexOf("self_size");
108
+ let selfSizes: number[] = [];
109
+
110
+ while (true) {
111
+ let firstCh = buffer[index];
112
+ if (firstCh === end) break;
113
+ index++;
114
+ let skippedCommas = 0;
115
+ while (skippedCommas < nodeSelfSizeIndex) {
116
+ if (buffer[index] === comma) {
117
+ skippedCommas++;
118
+ }
119
+ index++;
120
+ }
121
+
122
+ let nextComma = index;
123
+ while (buffer[nextComma] !== comma && buffer[nextComma] !== newLine) {
124
+ nextComma++;
125
+ }
126
+
127
+ let selfSize = 0;
128
+ for (let i = index; i < nextComma; i++) {
129
+ selfSize = selfSize * 10 + (buffer[i] - 48);
130
+ }
131
+ selfSizes.push(selfSize);
132
+ sum += selfSize;
133
+
134
+ // Skip until next line
135
+ while (buffer[index] !== newLine) {
136
+ index++;
137
+ }
138
+ index++;
139
+ }
140
+
141
+ return sum;
142
+ }
143
+
144
+ export async function parseHeapSnapshot(name: string, buffer: Buffer, setProgress: (fraction: number, stage: string) => void): Promise<HeapsnapshotFormat> {
145
+ let index = 0;
146
+ function parseNextLine(): string {
147
+ let start = index;
148
+ while (true) {
149
+ if (index >= buffer.length || buffer[index] === 10) {
150
+ let line = buffer.toString("utf8", start, index);
151
+ index++;
152
+ return line;
153
+ }
154
+ index++;
155
+ }
156
+ }
157
+
158
+ let metadataLine = parseNextLine();
159
+ let metadata = JSON.parse(metadataLine.slice(0, -1) + "}") as HeapsnapshotFormat;
160
+ metadata.name = name;
161
+
162
+ let nextProgressIndex = 0;
163
+ while (index < buffer.length) {
164
+
165
+ let startIndex = index;
166
+ let line = parseNextLine();
167
+ let field = line.split(":")[0];
168
+ index = startIndex + field.length + ":".length;
169
+
170
+ field = JSON.parse(field);
171
+
172
+ if (field === "strings") {
173
+ let strings = metadata.strings = [] as string[];
174
+ while (index < buffer.length) {
175
+ let line = parseNextLine();
176
+ if (line === "]," || line === "[],") break;
177
+ if (line[0] === "[") {
178
+ line = line.slice(1, -1);
179
+ } else if (line.endsWith("]}")) {
180
+ line = line.slice(0, -2);
181
+ } else {
182
+ line = line.slice(0, -1);
183
+ }
184
+ strings.push(JSON.parse(line));
185
+
186
+ if (index >= nextProgressIndex) {
187
+ setProgress(index / buffer.length, "buffer => numbers");
188
+ // Wait for progress to render
189
+ await delay(0);
190
+ nextProgressIndex = index + Math.floor(buffer.length / progressCalls);
191
+ }
192
+ }
193
+ } else {
194
+ let numbers: number[] = [];
195
+ if (field === "nodes" || field === "edges") {
196
+ let count = metadata.snapshot[field.slice(0, -1) + "_count" as "node_count"];
197
+ let fields = metadata.snapshot.meta[field.slice(0, -1) + "_fields" as "node_fields"];
198
+ let numberIndex = 0;
199
+ numbers = new Int32Array(count * fields.length) as any;
200
+ while (index < buffer.length) {
201
+ let line = parseNextLine();
202
+ if (line === "]," || line === "[],") break;
203
+ let parsed = JSON.parse("[" + line.slice(1) + "]");
204
+ for (let parsedPart of parsed) {
205
+ numbers[numberIndex++] = parsedPart;
206
+ }
207
+
208
+ if (index >= nextProgressIndex) {
209
+ setProgress(index / buffer.length, "buffer => numbers");
210
+ // Wait for progress to render
211
+ await delay(0);
212
+ nextProgressIndex = index + Math.floor(buffer.length / progressCalls);
213
+ }
214
+ }
215
+ } else {
216
+ while (index < buffer.length) {
217
+ let line = parseNextLine();
218
+ if (line === "]," || line === "[],") break;
219
+ let parsed = JSON.parse("[" + line.slice(1) + "]");
220
+ for (let parsedPart of parsed) {
221
+ numbers.push(parsedPart);
222
+ }
223
+
224
+ if (index >= nextProgressIndex) {
225
+ setProgress(index / buffer.length, "buffer => numbers");
226
+ // Wait for progress to render
227
+ await delay(0);
228
+ nextProgressIndex = index + Math.floor(buffer.length / progressCalls);
229
+ }
230
+ }
231
+ }
232
+
233
+ metadata[field as "edges"] = numbers;
234
+ }
235
+ }
236
+ setProgress(1, "buffer => numbers");
237
+
238
+ return metadata;
239
+ }
240
+
241
+ export async function getHeapsnapshotIds(snapshot: HeapsnapshotFormat) {
242
+ let ids = new Set<number>();
243
+ const nodeFields = snapshot.snapshot.meta.node_fields.length;
244
+ const nodeIdIndex = snapshot.snapshot.meta.node_fields.indexOf("id");
245
+ const nodes = snapshot.nodes;
246
+
247
+ for (let i = 0; i < nodes.length; i += nodeFields) {
248
+ ids.add(nodes[i + nodeIdIndex]);
249
+ }
250
+ return ids;
251
+ }
252
+
253
+ export async function processAndSummarizeHeapsnapshot(
254
+ snapshot: HeapsnapshotFormat,
255
+ setProgress: (fraction: number, stage: string) => void,
256
+ config?: {
257
+ diff?: {
258
+ baseIds: Set<number>;
259
+ // NOTE: "removed" just requires reversing the snapshots and then doing "new"
260
+ type: "new" | "kept";
261
+ };
262
+ filter?: { filter: string; depth: number };
263
+ maxDepth?: number;
264
+ includesFilter?: string,
265
+ }
266
+ ): Promise<HeapSummary> {
267
+ const filter = config?.filter ?? { filter: "", depth: 0 };
268
+ const diff = config?.diff;
269
+ const maxDepth = config?.maxDepth ?? 20;
270
+ const includesFilter = config?.includesFilter;
271
+
272
+ const nodeFields = snapshot.snapshot.meta.node_fields.length;
273
+ const edgeFields = snapshot.snapshot.meta.edge_fields.length;
274
+
275
+ const nodeTypeIndex = snapshot.snapshot.meta.node_fields.indexOf("type");
276
+ let nodeTypes = snapshot.snapshot.meta.node_types[nodeTypeIndex] as string[];
277
+ const nodeTypeString = nodeTypes.indexOf("string");
278
+ const nodeNameIndex = snapshot.snapshot.meta.node_fields.indexOf("name");
279
+ const nodeEdgeCountIndex = snapshot.snapshot.meta.node_fields.indexOf("edge_count");
280
+ const nodeSelfSizeIndex = snapshot.snapshot.meta.node_fields.indexOf("self_size");
281
+ const nodeIdIndex = snapshot.snapshot.meta.node_fields.indexOf("id");
282
+ const nodes = snapshot.nodes;
283
+ const strings = snapshot.strings;
284
+
285
+ const edges = snapshot.edges;
286
+ const edgeToNodeIndex = snapshot.snapshot.meta.edge_fields.indexOf("to_node");
287
+ const edgeTypeIndex = snapshot.snapshot.meta.edge_fields.indexOf("type");
288
+ const edgeNameOrIndexIndex = snapshot.snapshot.meta.edge_fields.indexOf("name_or_index");
289
+
290
+ const edgeTypeWeak = (snapshot.snapshot.meta.edge_types[edgeTypeIndex] as string[]).indexOf("weak");
291
+ const edgeTypeHidden = (snapshot.snapshot.meta.edge_types[edgeTypeIndex] as string[]).indexOf("hidden");
292
+ const edgeTypeClosure = (snapshot.snapshot.meta.edge_types[edgeTypeIndex] as string[]).indexOf("closure");
293
+ const edgeTypeContext = (snapshot.snapshot.meta.edge_types[edgeTypeIndex] as string[]).indexOf("context");
294
+ const edgeTypeProperty = (snapshot.snapshot.meta.edge_types[edgeTypeIndex] as string[]).indexOf("property");
295
+
296
+ const locations = snapshot.locations;
297
+ const locationFields = snapshot.snapshot.meta.location_fields.length;
298
+ const locationObjectIndex = snapshot.snapshot.meta.location_fields.indexOf("object_index");
299
+ const locationScriptIdIndex = snapshot.snapshot.meta.location_fields.indexOf("script_id");
300
+
301
+ let nodeIndexToScriptId = new Map<number, number>();
302
+ for (let i = 0; i < locations.length; i += locationFields) {
303
+ let objectIndex = locations[i + locationObjectIndex];
304
+ let scriptId = locations[i + locationScriptIdIndex];
305
+ nodeIndexToScriptId.set(objectIndex, scriptId);
306
+ }
307
+
308
+ // nodeIndex => [edgeIndex, nodeIndex, ...]
309
+ let keptAliveBy = new Map<number, number[]>();
310
+ {
311
+ let nextProgress = 0;
312
+ let edgeIndex = 0;
313
+ for (let nodeIndex = 0; nodeIndex < nodes.length; nodeIndex += nodeFields) {
314
+ let edgeCount = nodes[nodeIndex + nodeEdgeCountIndex];
315
+
316
+ for (let j = 0; j < edgeCount; j++) {
317
+ let toNodeIndex = edges[edgeIndex + edgeToNodeIndex];
318
+
319
+ let toNodeKeptAlive = keptAliveBy.get(toNodeIndex);
320
+ if (!toNodeKeptAlive) {
321
+ toNodeKeptAlive = [];
322
+ keptAliveBy.set(toNodeIndex, toNodeKeptAlive);
323
+ }
324
+ toNodeKeptAlive.push(edgeIndex);
325
+ toNodeKeptAlive.push(nodeIndex);
326
+
327
+ edgeIndex += edgeFields;
328
+ }
329
+ if (nodeIndex >= nextProgress) {
330
+ setProgress(nodeIndex / nodes.length, "Inverting references");
331
+ nextProgress = nodeIndex + Math.floor(nodes.length / progressCalls);
332
+ await delay(0);
333
+ }
334
+ }
335
+ }
336
+
337
+
338
+ function getNodeDebugPath(nodeIndex: number, takeNonRootPath = false): string[] {
339
+ let visitedNodes = new Set<number>();
340
+ let path: string[] = [];
341
+ const intermediateDepthLimit = Math.max(100, maxDepth);
342
+ addNode(nodeIndex);
343
+ if (path.length === 0 && !takeNonRootPath) {
344
+ return getNodeDebugPath(nodeIndex, true);
345
+ }
346
+ if (path.length > maxDepth) {
347
+ path = path.slice(0, maxDepth);
348
+ path.push(red("(further path cut off)"));
349
+ }
350
+ if (path.length === 0) {
351
+ return [red("No path to exports/global")];
352
+ }
353
+ let type = nodeTypes[nodes[nodeIndex + nodeTypeIndex]];
354
+ path.unshift(`(type=${type})`);
355
+ return path;
356
+ function addNode(node: number): boolean {
357
+ if (path.length > intermediateDepthLimit) {
358
+ return true;
359
+ }
360
+ if (visitedNodes.has(node)) return false;
361
+ visitedNodes.add(node);
362
+
363
+ function ellipsize(text: string, limit: number) {
364
+ if (text.length <= limit) return text;
365
+ return text.slice(0, limit - 3) + "...";
366
+ }
367
+
368
+ const name = strings[nodes[node + nodeNameIndex]];
369
+
370
+ let previousPathLength = path.length;
371
+ if (nodes[node + nodeTypeIndex] === nodeTypeString) {
372
+ path.push(`string`);
373
+ } else {
374
+ path.push(`${JSON.stringify(ellipsize(name, 50))}`);
375
+ }
376
+
377
+ let location = nodeIndexToScriptId.get(node);
378
+ if (location !== undefined) {
379
+ path.push(`(scriptId=${location})`);
380
+ }
381
+
382
+ if (name === "global") {
383
+ // If it is a global we only really need to know what the global property is called
384
+ path = path.slice(-3 - filter?.depth);
385
+ return true;
386
+ }
387
+ if (name === "(GC roots)") {
388
+ return true;
389
+ }
390
+ // Find the first node keeping it alive (not weak references, as those certainly aren't causing any leaks!)
391
+ let keptAliveArray = keptAliveBy.get(node);
392
+ let anyNotWeak = false;
393
+ if (keptAliveArray) {
394
+ let forcedEdge: number | undefined;
395
+ {
396
+ for (let i = 0; i < keptAliveArray.length; i += 2) {
397
+ let edgeIndex = keptAliveArray[i];
398
+ let fromNodeIndex = keptAliveArray[i + 1];
399
+
400
+ let edgeType = edges[edgeIndex + edgeTypeIndex];
401
+ if (edgeType === edgeTypeWeak) continue;
402
+ const name = strings[edges[edgeIndex + edgeNameOrIndexIndex]];
403
+ const toName = strings[nodes[fromNodeIndex + nodeNameIndex]];
404
+
405
+ if (edgeType === edgeTypeProperty && name === "exports" && exportsGlobal) {
406
+ forcedEdge = i;
407
+ break;
408
+ } else if (toName === "global" || toName === "(GC roots)") {
409
+ forcedEdge = i;
410
+ break;
411
+ }
412
+ }
413
+ }
414
+
415
+ for (let i = forcedEdge ?? 0; i < keptAliveArray.length; i += 2) {
416
+ let edgeIndex = keptAliveArray[i];
417
+ let fromNodeIndex = keptAliveArray[i + 1];
418
+
419
+ let edgeType = edges[edgeIndex + edgeTypeIndex];
420
+
421
+ if (edgeType === edgeTypeWeak) continue;
422
+ // Not sure what hidden edges are... but maybe best to skip them
423
+ // (as that seems to be what devtools does).
424
+ if (edgeType === edgeTypeHidden) continue;
425
+ anyNotWeak = true;
426
+
427
+ // https://github.com/nodejs/node/blob/main/deps/v8/src/profiler/heap-snapshot-generator.cc#L212
428
+ // - For "element" and "hidden" name is actually an index (not a string)
429
+ const name = strings[edges[edgeIndex + edgeNameOrIndexIndex]];
430
+ if (
431
+ edgeType === edgeTypeClosure
432
+ || edgeType === edgeTypeContext
433
+ ) {
434
+ path.push(yellow(`${name}`));
435
+ } else if (
436
+ edgeType === edgeTypeProperty
437
+ //|| edge.type === "element"
438
+ ) {
439
+ path.push(blue(`[${name}]`));
440
+ // Exports are kept alive by default, so we don't need to show a random referenced
441
+ // location and pretend that is the reason we are keeping the export alive.
442
+ if (name === "exports" && exportsGlobal) {
443
+ // If it is an export we only really need to know what the exported property is called
444
+ path = path.slice(-3 - filter?.depth);
445
+ return true;
446
+ }
447
+ }
448
+ if (addNode(fromNodeIndex)) {
449
+ return true;
450
+ }
451
+ }
452
+ }
453
+ if (takeNonRootPath) {
454
+ if (!keptAliveArray?.length) {
455
+ path.push(yellow("(no references)"));
456
+ return true;
457
+ }
458
+ if (!anyNotWeak) {
459
+ path.push(yellow("(weak)"));
460
+ return true;
461
+ }
462
+ }
463
+ // This is the case where all references were cyclic references
464
+ // (We have to undo the path we added as well)
465
+ path = path.slice(0, previousPathLength);
466
+ return false;
467
+ }
468
+ }
469
+
470
+ let nodeGroupings: Map<string, number[]> = new Map();
471
+
472
+ let diffFilter = (id: number) => true;
473
+ if (diff?.type === "new") {
474
+ diffFilter = (id: number) => !diff.baseIds.has(id);
475
+ } else if (diff?.type === "kept") {
476
+ diffFilter = (id: number) => diff.baseIds.has(id);
477
+ }
478
+
479
+ let total = 0;
480
+ let suffixFilter = config?.filter?.filter ?? "";
481
+ let nextProgress = 0;
482
+ for (let i = 0; i < nodes.length; i += nodeFields) {
483
+ if (i >= nextProgress) {
484
+ setProgress(i / nodes.length, "Grouping nodes");
485
+ nextProgress = i + Math.floor(nodes.length / progressCalls);
486
+ await delay(0);
487
+ }
488
+ let node = i;
489
+
490
+ let id = nodes[node + nodeIdIndex];
491
+ if (!diffFilter(id)) {
492
+ continue;
493
+ }
494
+
495
+ let key = getNodeDebugPath(node).join(".");
496
+ if (includesFilter && !key.includes(includesFilter)) continue;
497
+
498
+ total += nodes[node + nodeSelfSizeIndex];
499
+
500
+ if (suffixFilter && !key.endsWith(suffixFilter)) continue;
501
+ let group = nodeGroupings.get(key);
502
+ if (group === undefined) {
503
+ group = [];
504
+ nodeGroupings.set(key, group);
505
+ }
506
+ group.push(node);
507
+ }
508
+
509
+ let summary: HeapSummary["summary"] = {};
510
+ for (let [key, nodesInGroup] of nodeGroupings) {
511
+ let sum = 0;
512
+ for (let node of nodesInGroup) {
513
+ sum += nodes[node + nodeSelfSizeIndex];
514
+ }
515
+ summary[key] = {
516
+ key,
517
+ sum,
518
+ count: nodesInGroup.length,
519
+ firstId: nodes[nodesInGroup[0] + nodeIdIndex],
520
+ };
521
+ }
522
+
523
+ return {
524
+ name: snapshot.name,
525
+ summary,
526
+ total,
527
+ };
528
+ }
529
+
530
+ export async function debugAllocation(snapshot: HeapsnapshotFormat, id: number, setProgress: (fraction: number, stage: string) => void) {
531
+ type LocationType = {
532
+ object_index: number;
533
+ script_id: number;
534
+ line: number;
535
+ column: number;
536
+ };
537
+ type NodeType = {
538
+ type: string;
539
+ name: string;
540
+ id: number;
541
+ self_size: number;
542
+ edge_count: number;
543
+ trace_node_id: string;
544
+ detachedness: number;
545
+ // Everything that this node is keeping alive
546
+ keepingAlive: EdgeType[];
547
+ keptAliveBy: { edge: EdgeType; node: NodeType; }[];
548
+ location: LocationType | undefined;
549
+ };
550
+ type EdgeType = {
551
+ type: string;
552
+ name_or_index: number | string;
553
+ to_node: NodeType;
554
+ };
555
+
556
+ async function dumpObjects(
557
+ data: number[],
558
+ fields: string[],
559
+ types: (string | string[])[],
560
+ strings: string[],
561
+ setProgress: (fraction: number) => void
562
+ ) {
563
+ let output: unknown[] = [];
564
+ let fieldCount = fields.length;
565
+ let nextProgress = 0;
566
+ for (let i = 0; i < data.length; i += fieldCount) {
567
+ let obj: any = {};
568
+ for (let j = 0; j < fieldCount; j++) {
569
+ let value = data[i + j] as any;
570
+ let type = types[j];
571
+ if (type === "string") {
572
+ value = strings[value];
573
+ } else if (Array.isArray(type)) {
574
+ value = type[value];
575
+ }
576
+ obj[fields[j]] = value;
577
+ }
578
+ output.push(obj);
579
+ if (i >= nextProgress) {
580
+ setProgress(i / data.length);
581
+ nextProgress = i + Math.floor(data.length / progressCalls);
582
+ await delay(0);
583
+ }
584
+ }
585
+ return output;
586
+ }
587
+
588
+ let metadata = snapshot.snapshot.meta;
589
+ let strings = snapshot.strings;
590
+ let locations = await dumpObjects(snapshot.locations, metadata.location_fields, ["number", "number", "number", "number"], strings,
591
+ progress => setProgress(progress, "numbers => locations")
592
+ ) as LocationType[];
593
+ let nodes = await dumpObjects(snapshot.nodes, metadata.node_fields, metadata.node_types, strings,
594
+ progress => setProgress(progress, "numbers => nodes")
595
+ ) as NodeType[];
596
+ let edges = await dumpObjects(snapshot.edges, metadata.edge_fields, metadata.edge_types, strings,
597
+ progress => setProgress(progress, "numbers => edges")
598
+ ) as EdgeType[];
599
+
600
+ setProgress(0, "initing ref arrays");
601
+ await delay(0);
602
+
603
+ for (let i = 0; i < nodes.length; i++) {
604
+ nodes[i].keepingAlive = [];
605
+ nodes[i].keptAliveBy = [];
606
+ }
607
+ setProgress(1, "initing ref arrays");
608
+ await delay(0);
609
+
610
+ // Update object references
611
+ for (let i = 0; i < edges.length; i++) {
612
+ let edge = edges[i];
613
+ edge.to_node = nodes[(edge.to_node as any as number) / metadata.node_fields.length];
614
+
615
+ // https://github.com/nodejs/node/blob/main/deps/v8/src/profiler/heap-snapshot-generator.cc#L212
616
+ if (edge.type === "element" || edge.type === "hidden") {
617
+ // Then it is an (node?) index, which we should do something with
618
+ } else {
619
+ edge.name_or_index = strings[edge.name_or_index as any as number];
620
+ }
621
+ }
622
+ setProgress(1, "settings edge objects");
623
+ await delay(0);
624
+
625
+ let edgeIndex = 0;
626
+ for (let i = 0; i < nodes.length; i++) {
627
+ let node = nodes[i];
628
+ for (let j = 0; j < node.edge_count; j++) {
629
+ let edge = edges[edgeIndex];
630
+ node.keepingAlive.push(edge);
631
+ edge.to_node.keptAliveBy.push({ edge, node, });
632
+ edgeIndex++;
633
+ }
634
+ }
635
+ setProgress(1, "populating ref arrays");
636
+ await delay(0);
637
+
638
+ for (let i = 0; i < locations.length; i++) {
639
+ let location = locations[i];
640
+ let node = nodes[location.object_index / metadata.node_fields.length];
641
+ node.location = location;
642
+ }
643
+ setProgress(1, "settings locations");
644
+ await delay(0);
645
+
646
+ let node = nodes.find(x => x.id === id);
647
+ let specialNodes: unknown[] = [];
648
+ //node = nodes.find(x => x.id === 8727);
649
+ //specialNodes = nodes.filter(x => x.location?.script_id === 693);
650
+ logErrors(stepIntoNode(node, specialNodes));
651
+
652
+ return nodes;
653
+ }
654
+
655
+ async function stepIntoNode(node: unknown, nodes: unknown[]) {
656
+ console.log("Garbage collect NOW!");
657
+ // Wait... so hopefully garbage collection occurs, so only node and nodes
658
+ // it references stay alive.
659
+ await delay(5000);
660
+ await delay(5000);
661
+ await delay(5000);
662
+ await delay(5000);
663
+ debugger;
664
+ console.log({ node });
665
+ console.log({ nodes });
666
+ }
package/src/https.ts ADDED
@@ -0,0 +1,2 @@
1
+ import { httpsRequest } from "socket-function/src/https";
2
+ export { httpsRequest };
package/src/inject.ts ADDED
@@ -0,0 +1 @@
1
+ import "./diagnostics/logs/injectFileLocationToConsole";