@git-stunts/git-warp 10.8.0 → 11.3.3

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 (136) hide show
  1. package/README.md +53 -32
  2. package/SECURITY.md +64 -0
  3. package/bin/cli/commands/check.js +168 -0
  4. package/bin/cli/commands/doctor/checks.js +422 -0
  5. package/bin/cli/commands/doctor/codes.js +46 -0
  6. package/bin/cli/commands/doctor/index.js +239 -0
  7. package/bin/cli/commands/doctor/types.js +89 -0
  8. package/bin/cli/commands/history.js +80 -0
  9. package/bin/cli/commands/info.js +139 -0
  10. package/bin/cli/commands/install-hooks.js +128 -0
  11. package/bin/cli/commands/materialize.js +99 -0
  12. package/bin/cli/commands/patch.js +142 -0
  13. package/bin/cli/commands/path.js +88 -0
  14. package/bin/cli/commands/query.js +235 -0
  15. package/bin/cli/commands/registry.js +32 -0
  16. package/bin/cli/commands/seek.js +598 -0
  17. package/bin/cli/commands/tree.js +230 -0
  18. package/bin/cli/commands/trust.js +154 -0
  19. package/bin/cli/commands/verify-audit.js +114 -0
  20. package/bin/cli/commands/view.js +46 -0
  21. package/bin/cli/infrastructure.js +350 -0
  22. package/bin/cli/schemas.js +177 -0
  23. package/bin/cli/shared.js +244 -0
  24. package/bin/cli/types.js +96 -0
  25. package/bin/presenters/index.js +41 -9
  26. package/bin/presenters/json.js +14 -12
  27. package/bin/presenters/text.js +286 -28
  28. package/bin/warp-graph.js +5 -2346
  29. package/index.d.ts +111 -21
  30. package/index.js +2 -0
  31. package/package.json +10 -8
  32. package/src/domain/WarpGraph.js +109 -3252
  33. package/src/domain/crdt/ORSet.js +8 -8
  34. package/src/domain/errors/EmptyMessageError.js +2 -2
  35. package/src/domain/errors/ForkError.js +1 -1
  36. package/src/domain/errors/IndexError.js +1 -1
  37. package/src/domain/errors/OperationAbortedError.js +1 -1
  38. package/src/domain/errors/QueryError.js +3 -3
  39. package/src/domain/errors/SchemaUnsupportedError.js +1 -1
  40. package/src/domain/errors/ShardCorruptionError.js +2 -2
  41. package/src/domain/errors/ShardLoadError.js +2 -2
  42. package/src/domain/errors/ShardValidationError.js +4 -4
  43. package/src/domain/errors/StorageError.js +2 -2
  44. package/src/domain/errors/SyncError.js +1 -1
  45. package/src/domain/errors/TraversalError.js +1 -1
  46. package/src/domain/errors/TrustError.js +29 -0
  47. package/src/domain/errors/WarpError.js +2 -2
  48. package/src/domain/errors/WormholeError.js +1 -1
  49. package/src/domain/errors/index.js +1 -0
  50. package/src/domain/services/AuditMessageCodec.js +137 -0
  51. package/src/domain/services/AuditReceiptService.js +471 -0
  52. package/src/domain/services/AuditVerifierService.js +707 -0
  53. package/src/domain/services/BitmapIndexBuilder.js +3 -3
  54. package/src/domain/services/BitmapIndexReader.js +28 -19
  55. package/src/domain/services/BoundaryTransitionRecord.js +18 -17
  56. package/src/domain/services/CheckpointSerializerV5.js +17 -16
  57. package/src/domain/services/CheckpointService.js +2 -2
  58. package/src/domain/services/CommitDagTraversalService.js +13 -13
  59. package/src/domain/services/DagPathFinding.js +7 -7
  60. package/src/domain/services/DagTopology.js +1 -1
  61. package/src/domain/services/DagTraversal.js +1 -1
  62. package/src/domain/services/HealthCheckService.js +1 -1
  63. package/src/domain/services/HookInstaller.js +1 -1
  64. package/src/domain/services/HttpSyncServer.js +120 -55
  65. package/src/domain/services/IndexRebuildService.js +7 -7
  66. package/src/domain/services/IndexStalenessChecker.js +4 -3
  67. package/src/domain/services/JoinReducer.js +11 -11
  68. package/src/domain/services/LogicalTraversal.js +1 -1
  69. package/src/domain/services/MessageCodecInternal.js +4 -1
  70. package/src/domain/services/MessageSchemaDetector.js +2 -2
  71. package/src/domain/services/MigrationService.js +1 -1
  72. package/src/domain/services/ObserverView.js +8 -8
  73. package/src/domain/services/PatchBuilderV2.js +42 -26
  74. package/src/domain/services/ProvenanceIndex.js +1 -1
  75. package/src/domain/services/ProvenancePayload.js +1 -1
  76. package/src/domain/services/QueryBuilder.js +3 -3
  77. package/src/domain/services/StateDiff.js +14 -11
  78. package/src/domain/services/StateSerializerV5.js +2 -2
  79. package/src/domain/services/StreamingBitmapIndexBuilder.js +26 -24
  80. package/src/domain/services/SyncAuthService.js +71 -4
  81. package/src/domain/services/SyncProtocol.js +25 -11
  82. package/src/domain/services/TemporalQuery.js +9 -6
  83. package/src/domain/services/TranslationCost.js +7 -5
  84. package/src/domain/services/WarpMessageCodec.js +4 -1
  85. package/src/domain/services/WormholeService.js +16 -7
  86. package/src/domain/trust/TrustCanonical.js +42 -0
  87. package/src/domain/trust/TrustCrypto.js +111 -0
  88. package/src/domain/trust/TrustEvaluator.js +195 -0
  89. package/src/domain/trust/TrustRecordService.js +281 -0
  90. package/src/domain/trust/TrustStateBuilder.js +222 -0
  91. package/src/domain/trust/canonical.js +68 -0
  92. package/src/domain/trust/reasonCodes.js +64 -0
  93. package/src/domain/trust/schemas.js +160 -0
  94. package/src/domain/trust/verdict.js +42 -0
  95. package/src/domain/types/TickReceipt.js +1 -1
  96. package/src/domain/types/WarpErrors.js +45 -0
  97. package/src/domain/types/WarpOptions.js +29 -0
  98. package/src/domain/types/WarpPersistence.js +41 -0
  99. package/src/domain/types/WarpTypes.js +2 -2
  100. package/src/domain/types/WarpTypesV2.js +2 -2
  101. package/src/domain/types/git-cas.d.ts +20 -0
  102. package/src/domain/utils/MinHeap.js +6 -5
  103. package/src/domain/utils/RefLayout.js +59 -0
  104. package/src/domain/utils/canonicalStringify.js +5 -4
  105. package/src/domain/utils/roaring.js +31 -5
  106. package/src/domain/warp/PatchSession.js +26 -17
  107. package/src/domain/warp/Writer.js +18 -3
  108. package/src/domain/warp/_internal.js +26 -0
  109. package/src/domain/warp/_wire.js +58 -0
  110. package/src/domain/warp/_wiredMethods.d.ts +254 -0
  111. package/src/domain/warp/checkpoint.methods.js +401 -0
  112. package/src/domain/warp/fork.methods.js +323 -0
  113. package/src/domain/warp/materialize.methods.js +238 -0
  114. package/src/domain/warp/materializeAdvanced.methods.js +350 -0
  115. package/src/domain/warp/patch.methods.js +554 -0
  116. package/src/domain/warp/provenance.methods.js +286 -0
  117. package/src/domain/warp/query.methods.js +280 -0
  118. package/src/domain/warp/subscribe.methods.js +272 -0
  119. package/src/domain/warp/sync.methods.js +554 -0
  120. package/src/globals.d.ts +64 -0
  121. package/src/infrastructure/adapters/BunHttpAdapter.js +14 -9
  122. package/src/infrastructure/adapters/CasSeekCacheAdapter.js +9 -4
  123. package/src/infrastructure/adapters/DenoHttpAdapter.js +5 -6
  124. package/src/infrastructure/adapters/GitGraphAdapter.js +79 -11
  125. package/src/infrastructure/adapters/InMemoryGraphAdapter.js +36 -0
  126. package/src/infrastructure/adapters/NodeHttpAdapter.js +2 -2
  127. package/src/infrastructure/adapters/WebCryptoAdapter.js +2 -2
  128. package/src/ports/CommitPort.js +10 -0
  129. package/src/ports/RefPort.js +17 -0
  130. package/src/visualization/layouts/converters.js +2 -2
  131. package/src/visualization/layouts/elkAdapter.js +1 -1
  132. package/src/visualization/layouts/elkLayout.js +10 -7
  133. package/src/visualization/layouts/index.js +1 -1
  134. package/src/visualization/renderers/ascii/seek.js +16 -6
  135. package/src/visualization/renderers/svg/index.js +1 -1
  136. package/src/hooks/post-merge.sh +0 -60
@@ -0,0 +1,401 @@
1
+ /**
2
+ * Checkpoint, GC, and coverage methods for WarpGraph.
3
+ *
4
+ * Every function uses `this` bound to a WarpGraph instance at runtime
5
+ * via wireWarpMethods().
6
+ *
7
+ * @module domain/warp/checkpoint.methods
8
+ */
9
+
10
+ import { QueryError, E_NO_STATE_MSG } from './_internal.js';
11
+ import { buildWriterRef, buildCheckpointRef, buildCoverageRef } from '../utils/RefLayout.js';
12
+ import { createFrontier, updateFrontier } from '../services/Frontier.js';
13
+ import { loadCheckpoint, create as createCheckpointCommit } from '../services/CheckpointService.js';
14
+ import { decodePatchMessage, detectMessageKind, encodeAnchorMessage } from '../services/WarpMessageCodec.js';
15
+ import { shouldRunGC, executeGC } from '../services/GCPolicy.js';
16
+ import { collectGCMetrics } from '../services/GCMetrics.js';
17
+ import { computeAppliedVV } from '../services/CheckpointSerializerV5.js';
18
+
19
+ /** @typedef {import('../types/WarpPersistence.js').CorePersistence} CorePersistence */
20
+
21
+ /**
22
+ * Creates a checkpoint of the current graph state.
23
+ *
24
+ * Discovers all writers, builds a frontier of writer tips, materializes
25
+ * the current state, and creates a checkpoint commit with provenance.
26
+ *
27
+ * @this {import('../WarpGraph.js').default}
28
+ * @returns {Promise<string>} The checkpoint commit SHA
29
+ * @throws {Error} If materialization or commit creation fails
30
+ */
31
+ export async function createCheckpoint() {
32
+ const t0 = this._clock.now();
33
+ try {
34
+ // 1. Discover all writers
35
+ const writers = await this.discoverWriters();
36
+
37
+ // 2. Build frontier (map of writerId → tip SHA)
38
+ const frontier = createFrontier();
39
+ const parents = [];
40
+
41
+ for (const writerId of writers) {
42
+ const writerRef = buildWriterRef(this._graphName, writerId);
43
+ const sha = await this._persistence.readRef(writerRef);
44
+ if (sha) {
45
+ updateFrontier(frontier, writerId, sha);
46
+ parents.push(sha);
47
+ }
48
+ }
49
+
50
+ // 3. Materialize current state (reuse cached if fresh, guard against recursion)
51
+ const prevCheckpointing = this._checkpointing;
52
+ this._checkpointing = true;
53
+ /** @type {import('../services/JoinReducer.js').WarpStateV5} */
54
+ let state;
55
+ try {
56
+ state = /** @type {import('../services/JoinReducer.js').WarpStateV5} */ ((this._cachedState && !this._stateDirty)
57
+ ? this._cachedState
58
+ : await this.materialize());
59
+ } finally {
60
+ this._checkpointing = prevCheckpointing;
61
+ }
62
+
63
+ // 4. Call CheckpointService.create() with provenance index if available
64
+ /** @type {CorePersistence} */
65
+ const persistence = this._persistence;
66
+ const checkpointSha = await createCheckpointCommit({
67
+ persistence,
68
+ graphName: this._graphName,
69
+ state,
70
+ frontier,
71
+ parents,
72
+ provenanceIndex: this._provenanceIndex || undefined,
73
+ crypto: this._crypto,
74
+ codec: this._codec,
75
+ });
76
+
77
+ // 5. Update checkpoint ref
78
+ const checkpointRef = buildCheckpointRef(this._graphName);
79
+ await this._persistence.updateRef(checkpointRef, checkpointSha);
80
+
81
+ this._logTiming('createCheckpoint', t0);
82
+
83
+ // 6. Return checkpoint SHA
84
+ return checkpointSha;
85
+ } catch (err) {
86
+ this._logTiming('createCheckpoint', t0, { error: /** @type {Error} */ (err) });
87
+ throw err;
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Syncs coverage information across writers.
93
+ *
94
+ * Creates an octopus anchor commit with all writer tips as parents,
95
+ * then updates the coverage ref to point to this anchor. The "octopus anchor"
96
+ * is a merge commit that records which writer tips have been observed,
97
+ * enabling efficient replication and consistency checks.
98
+ *
99
+ * @this {import('../WarpGraph.js').default}
100
+ * @returns {Promise<void>}
101
+ * @throws {Error} If ref access or commit creation fails
102
+ */
103
+ export async function syncCoverage() {
104
+ // 1. Discover all writers
105
+ const writers = await this.discoverWriters();
106
+
107
+ // If no writers exist, do nothing
108
+ if (writers.length === 0) {
109
+ return;
110
+ }
111
+
112
+ // 2. Get tip SHA for each writer's ref
113
+ const parents = [];
114
+ for (const writerId of writers) {
115
+ const writerRef = buildWriterRef(this._graphName, writerId);
116
+ const sha = await this._persistence.readRef(writerRef);
117
+ if (sha) {
118
+ parents.push(sha);
119
+ }
120
+ }
121
+
122
+ // If no refs have SHAs, do nothing
123
+ if (parents.length === 0) {
124
+ return;
125
+ }
126
+
127
+ // 3. Create octopus anchor commit with all tips as parents
128
+ const message = encodeAnchorMessage({ graph: this._graphName });
129
+ const anchorSha = await this._persistence.commitNode({ message, parents });
130
+
131
+ // 4. Update coverage ref
132
+ const coverageRef = buildCoverageRef(this._graphName);
133
+ await this._persistence.updateRef(coverageRef, anchorSha);
134
+ }
135
+
136
+ /**
137
+ * Loads the latest checkpoint for this graph.
138
+ *
139
+ * @this {import('../WarpGraph.js').default}
140
+ * @returns {Promise<{state: import('../services/JoinReducer.js').WarpStateV5, frontier: Map<string, string>, stateHash: string, schema: number, provenanceIndex?: import('../services/ProvenanceIndex.js').ProvenanceIndex}|null>} The checkpoint or null
141
+ * @private
142
+ */
143
+ export async function _loadLatestCheckpoint() {
144
+ const checkpointRef = buildCheckpointRef(this._graphName);
145
+ const checkpointSha = await this._persistence.readRef(checkpointRef);
146
+
147
+ if (!checkpointSha) {
148
+ return null;
149
+ }
150
+
151
+ try {
152
+ return await loadCheckpoint(this._persistence, checkpointSha, { codec: this._codec });
153
+ } catch {
154
+ return null;
155
+ }
156
+ }
157
+
158
+ /**
159
+ * Loads patches since a checkpoint for incremental materialization.
160
+ *
161
+ * @this {import('../WarpGraph.js').default}
162
+ * @param {{state: import('../services/JoinReducer.js').WarpStateV5, frontier: Map<string, string>, stateHash: string, schema: number}} checkpoint - The checkpoint to start from
163
+ * @returns {Promise<Array<{patch: import('../types/WarpTypesV2.js').PatchV2, sha: string}>>} Patches since checkpoint
164
+ * @private
165
+ */
166
+ export async function _loadPatchesSince(checkpoint) {
167
+ const writerIds = await this.discoverWriters();
168
+ const allPatches = [];
169
+
170
+ for (const writerId of writerIds) {
171
+ const checkpointSha = checkpoint.frontier?.get(writerId) || null;
172
+ const patches = await this._loadWriterPatches(writerId, checkpointSha);
173
+
174
+ // Validate each patch against checkpoint frontier
175
+ for (const { sha } of patches) {
176
+ await this._validatePatchAgainstCheckpoint(writerId, sha, checkpoint);
177
+ }
178
+
179
+ for (const p of patches) {
180
+ allPatches.push(p);
181
+ }
182
+ }
183
+
184
+ return allPatches;
185
+ }
186
+
187
+ /**
188
+ * Validates migration boundary for graphs.
189
+ *
190
+ * Graphs cannot be opened if there is schema:1 history without
191
+ * a migration checkpoint. This ensures data consistency during migration.
192
+ *
193
+ * @this {import('../WarpGraph.js').default}
194
+ * @returns {Promise<void>}
195
+ * @throws {Error} If v1 history exists without migration checkpoint
196
+ * @private
197
+ */
198
+ export async function _validateMigrationBoundary() {
199
+ const checkpoint = await this._loadLatestCheckpoint();
200
+ if (checkpoint?.schema === 2 || checkpoint?.schema === 3) {
201
+ return; // Already migrated
202
+ }
203
+
204
+ const hasSchema1History = await this._hasSchema1Patches();
205
+ if (hasSchema1History) {
206
+ throw new Error(
207
+ 'Cannot open graph with v1 history. ' +
208
+ 'Run MigrationService.migrate() first to create migration checkpoint.'
209
+ );
210
+ }
211
+ }
212
+
213
+ /**
214
+ * Checks if there are any schema:1 patches in the graph.
215
+ *
216
+ * @this {import('../WarpGraph.js').default}
217
+ * @returns {Promise<boolean>} True if schema:1 patches exist
218
+ * @private
219
+ */
220
+ export async function _hasSchema1Patches() {
221
+ const writerIds = await this.discoverWriters();
222
+
223
+ for (const writerId of writerIds) {
224
+ const writerRef = buildWriterRef(this._graphName, writerId);
225
+ const tipSha = await this._persistence.readRef(writerRef);
226
+
227
+ if (!tipSha) {
228
+ continue;
229
+ }
230
+
231
+ // Check the first (most recent) patch from this writer
232
+ const nodeInfo = await this._persistence.getNodeInfo(tipSha);
233
+ const kind = detectMessageKind(nodeInfo.message);
234
+
235
+ if (kind === 'patch') {
236
+ const patchMeta = decodePatchMessage(nodeInfo.message);
237
+ const patchBuffer = await this._persistence.readBlob(patchMeta.patchOid);
238
+ const patch = /** @type {{schema?: number}} */ (this._codec.decode(patchBuffer));
239
+
240
+ // If any patch has schema:1, we have v1 history
241
+ if (patch.schema === 1 || patch.schema === undefined) {
242
+ return true;
243
+ }
244
+ }
245
+ }
246
+
247
+ return false;
248
+ }
249
+
250
+ /**
251
+ * Post-materialize GC check. Warn by default; execute only when enabled.
252
+ * GC failure never breaks materialize.
253
+ *
254
+ * @this {import('../WarpGraph.js').default}
255
+ * @param {import('../services/JoinReducer.js').WarpStateV5} state
256
+ * @private
257
+ */
258
+ export function _maybeRunGC(state) {
259
+ try {
260
+ const metrics = collectGCMetrics(state);
261
+ /** @type {import('../services/GCPolicy.js').GCInputMetrics} */
262
+ const inputMetrics = {
263
+ ...metrics,
264
+ patchesSinceCompaction: this._patchesSinceGC,
265
+ timeSinceCompaction: this._lastGCTime > 0 ? this._clock.now() - this._lastGCTime : 0,
266
+ };
267
+ const { shouldRun, reasons } = shouldRunGC(inputMetrics, /** @type {import('../services/GCPolicy.js').GCPolicy} */ (this._gcPolicy));
268
+
269
+ if (!shouldRun) {
270
+ return;
271
+ }
272
+
273
+ if (/** @type {import('../services/GCPolicy.js').GCPolicy} */ (this._gcPolicy).enabled) {
274
+ const appliedVV = computeAppliedVV(state);
275
+ const result = executeGC(state, appliedVV);
276
+ this._lastGCTime = this._clock.now();
277
+ this._patchesSinceGC = 0;
278
+ if (this._logger) {
279
+ this._logger.info('Auto-GC completed', { ...result, reasons });
280
+ }
281
+ } else if (this._logger) {
282
+ this._logger.warn(
283
+ 'GC thresholds exceeded but auto-GC is disabled. Set gcPolicy: { enabled: true } to auto-compact.',
284
+ { reasons },
285
+ );
286
+ }
287
+ } catch {
288
+ // GC failure never breaks materialize
289
+ }
290
+ }
291
+
292
+ /**
293
+ * Checks if GC should run based on current metrics and policy.
294
+ * If thresholds are exceeded, runs GC on the cached state.
295
+ *
296
+ * **Requires a cached state.**
297
+ *
298
+ * @this {import('../WarpGraph.js').default}
299
+ * @returns {{ran: boolean, result: Object|null, reasons: string[]}} GC result
300
+ *
301
+ * @example
302
+ * await graph.materialize();
303
+ * const { ran, result, reasons } = graph.maybeRunGC();
304
+ * if (ran) {
305
+ * console.log(`GC ran: ${result.tombstonesRemoved} tombstones removed`);
306
+ * }
307
+ */
308
+ export function maybeRunGC() {
309
+ if (!this._cachedState) {
310
+ return { ran: false, result: null, reasons: [] };
311
+ }
312
+
313
+ const rawMetrics = collectGCMetrics(this._cachedState);
314
+ /** @type {import('../services/GCPolicy.js').GCInputMetrics} */
315
+ const metrics = {
316
+ ...rawMetrics,
317
+ patchesSinceCompaction: this._patchesSinceGC,
318
+ timeSinceCompaction: this._lastGCTime > 0 ? this._clock.now() - this._lastGCTime : 0,
319
+ };
320
+
321
+ const { shouldRun, reasons } = shouldRunGC(metrics, /** @type {import('../services/GCPolicy.js').GCPolicy} */ (this._gcPolicy));
322
+
323
+ if (!shouldRun) {
324
+ return { ran: false, result: null, reasons: [] };
325
+ }
326
+
327
+ const result = this.runGC();
328
+ return { ran: true, result, reasons };
329
+ }
330
+
331
+ /**
332
+ * Explicitly runs GC on the cached state.
333
+ * Compacts tombstoned dots that are covered by the appliedVV.
334
+ *
335
+ * **Requires a cached state.**
336
+ *
337
+ * @this {import('../WarpGraph.js').default}
338
+ * @returns {{nodesCompacted: number, edgesCompacted: number, tombstonesRemoved: number, durationMs: number}}
339
+ * @throws {QueryError} If no cached state exists (code: `E_NO_STATE`)
340
+ *
341
+ * @example
342
+ * await graph.materialize();
343
+ * const result = graph.runGC();
344
+ * console.log(`Removed ${result.tombstonesRemoved} tombstones in ${result.durationMs}ms`);
345
+ */
346
+ export function runGC() {
347
+ const t0 = this._clock.now();
348
+ try {
349
+ if (!this._cachedState) {
350
+ throw new QueryError(E_NO_STATE_MSG, {
351
+ code: 'E_NO_STATE',
352
+ });
353
+ }
354
+
355
+ // Compute appliedVV from current state
356
+ const appliedVV = computeAppliedVV(this._cachedState);
357
+
358
+ // Execute GC (mutates cached state)
359
+ const result = executeGC(this._cachedState, appliedVV);
360
+
361
+ // Update GC tracking
362
+ this._lastGCTime = this._clock.now();
363
+ this._patchesSinceGC = 0;
364
+
365
+ this._logTiming('runGC', t0, { metrics: `${result.tombstonesRemoved} tombstones removed` });
366
+
367
+ return result;
368
+ } catch (err) {
369
+ this._logTiming('runGC', t0, { error: /** @type {Error} */ (err) });
370
+ throw err;
371
+ }
372
+ }
373
+
374
+ /**
375
+ * Gets current GC metrics for the cached state.
376
+ *
377
+ * @this {import('../WarpGraph.js').default}
378
+ * @returns {{
379
+ * nodeCount: number,
380
+ * edgeCount: number,
381
+ * tombstoneCount: number,
382
+ * tombstoneRatio: number,
383
+ * patchesSinceCompaction: number,
384
+ * lastCompactionTime: number
385
+ * }|null} GC metrics or null if no cached state
386
+ */
387
+ export function getGCMetrics() {
388
+ if (!this._cachedState) {
389
+ return null;
390
+ }
391
+
392
+ const rawMetrics = collectGCMetrics(this._cachedState);
393
+ return {
394
+ nodeCount: rawMetrics.nodeLiveDots,
395
+ edgeCount: rawMetrics.edgeLiveDots,
396
+ tombstoneCount: rawMetrics.totalTombstones,
397
+ tombstoneRatio: rawMetrics.tombstoneRatio,
398
+ patchesSinceCompaction: this._patchesSinceGC,
399
+ lastCompactionTime: this._lastGCTime,
400
+ };
401
+ }