@git-stunts/git-warp 10.3.2 → 10.7.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 (108) hide show
  1. package/README.md +6 -3
  2. package/SECURITY.md +89 -1
  3. package/bin/warp-graph.js +574 -208
  4. package/index.d.ts +55 -0
  5. package/index.js +4 -0
  6. package/package.json +8 -4
  7. package/src/domain/WarpGraph.js +334 -161
  8. package/src/domain/crdt/LWW.js +1 -1
  9. package/src/domain/crdt/ORSet.js +10 -6
  10. package/src/domain/crdt/VersionVector.js +5 -1
  11. package/src/domain/errors/EmptyMessageError.js +2 -4
  12. package/src/domain/errors/ForkError.js +4 -0
  13. package/src/domain/errors/IndexError.js +4 -0
  14. package/src/domain/errors/OperationAbortedError.js +4 -0
  15. package/src/domain/errors/QueryError.js +4 -0
  16. package/src/domain/errors/SchemaUnsupportedError.js +4 -0
  17. package/src/domain/errors/ShardCorruptionError.js +2 -6
  18. package/src/domain/errors/ShardLoadError.js +2 -6
  19. package/src/domain/errors/ShardValidationError.js +2 -7
  20. package/src/domain/errors/StorageError.js +2 -6
  21. package/src/domain/errors/SyncError.js +4 -0
  22. package/src/domain/errors/TraversalError.js +4 -0
  23. package/src/domain/errors/WarpError.js +2 -4
  24. package/src/domain/errors/WormholeError.js +4 -0
  25. package/src/domain/services/AnchorMessageCodec.js +1 -4
  26. package/src/domain/services/BitmapIndexBuilder.js +10 -6
  27. package/src/domain/services/BitmapIndexReader.js +27 -21
  28. package/src/domain/services/BoundaryTransitionRecord.js +22 -15
  29. package/src/domain/services/CheckpointMessageCodec.js +1 -7
  30. package/src/domain/services/CheckpointSerializerV5.js +20 -19
  31. package/src/domain/services/CheckpointService.js +18 -18
  32. package/src/domain/services/CommitDagTraversalService.js +13 -1
  33. package/src/domain/services/DagPathFinding.js +40 -18
  34. package/src/domain/services/DagTopology.js +7 -6
  35. package/src/domain/services/DagTraversal.js +5 -3
  36. package/src/domain/services/Frontier.js +7 -6
  37. package/src/domain/services/HealthCheckService.js +15 -14
  38. package/src/domain/services/HookInstaller.js +64 -13
  39. package/src/domain/services/HttpSyncServer.js +88 -19
  40. package/src/domain/services/IndexRebuildService.js +12 -12
  41. package/src/domain/services/IndexStalenessChecker.js +13 -6
  42. package/src/domain/services/JoinReducer.js +28 -27
  43. package/src/domain/services/LogicalTraversal.js +7 -6
  44. package/src/domain/services/MessageCodecInternal.js +2 -0
  45. package/src/domain/services/ObserverView.js +6 -6
  46. package/src/domain/services/PatchBuilderV2.js +9 -9
  47. package/src/domain/services/PatchMessageCodec.js +1 -7
  48. package/src/domain/services/ProvenanceIndex.js +6 -8
  49. package/src/domain/services/ProvenancePayload.js +1 -2
  50. package/src/domain/services/QueryBuilder.js +29 -23
  51. package/src/domain/services/StateDiff.js +7 -7
  52. package/src/domain/services/StateSerializerV5.js +8 -6
  53. package/src/domain/services/StreamingBitmapIndexBuilder.js +29 -23
  54. package/src/domain/services/SyncAuthService.js +396 -0
  55. package/src/domain/services/SyncProtocol.js +23 -26
  56. package/src/domain/services/TemporalQuery.js +4 -3
  57. package/src/domain/services/TranslationCost.js +4 -4
  58. package/src/domain/services/WormholeService.js +19 -15
  59. package/src/domain/types/TickReceipt.js +10 -6
  60. package/src/domain/types/WarpTypesV2.js +2 -3
  61. package/src/domain/utils/CachedValue.js +1 -1
  62. package/src/domain/utils/LRUCache.js +3 -3
  63. package/src/domain/utils/MinHeap.js +2 -2
  64. package/src/domain/utils/RefLayout.js +19 -0
  65. package/src/domain/utils/WriterId.js +2 -2
  66. package/src/domain/utils/defaultCodec.js +9 -2
  67. package/src/domain/utils/defaultCrypto.js +36 -0
  68. package/src/domain/utils/roaring.js +5 -5
  69. package/src/domain/utils/seekCacheKey.js +32 -0
  70. package/src/domain/warp/PatchSession.js +3 -3
  71. package/src/domain/warp/Writer.js +2 -2
  72. package/src/infrastructure/adapters/BunHttpAdapter.js +21 -8
  73. package/src/infrastructure/adapters/CasSeekCacheAdapter.js +311 -0
  74. package/src/infrastructure/adapters/ClockAdapter.js +2 -2
  75. package/src/infrastructure/adapters/DenoHttpAdapter.js +22 -9
  76. package/src/infrastructure/adapters/GitGraphAdapter.js +25 -83
  77. package/src/infrastructure/adapters/InMemoryGraphAdapter.js +488 -0
  78. package/src/infrastructure/adapters/NodeCryptoAdapter.js +16 -3
  79. package/src/infrastructure/adapters/NodeHttpAdapter.js +33 -11
  80. package/src/infrastructure/adapters/WebCryptoAdapter.js +21 -11
  81. package/src/infrastructure/adapters/adapterValidation.js +90 -0
  82. package/src/infrastructure/codecs/CborCodec.js +16 -8
  83. package/src/ports/BlobPort.js +2 -2
  84. package/src/ports/CodecPort.js +2 -2
  85. package/src/ports/CommitPort.js +8 -21
  86. package/src/ports/ConfigPort.js +3 -3
  87. package/src/ports/CryptoPort.js +7 -7
  88. package/src/ports/GraphPersistencePort.js +12 -14
  89. package/src/ports/HttpServerPort.js +1 -5
  90. package/src/ports/IndexStoragePort.js +1 -0
  91. package/src/ports/LoggerPort.js +9 -9
  92. package/src/ports/RefPort.js +5 -5
  93. package/src/ports/SeekCachePort.js +73 -0
  94. package/src/ports/TreePort.js +3 -3
  95. package/src/visualization/layouts/converters.js +14 -7
  96. package/src/visualization/layouts/elkAdapter.js +17 -4
  97. package/src/visualization/layouts/elkLayout.js +23 -7
  98. package/src/visualization/layouts/index.js +3 -3
  99. package/src/visualization/renderers/ascii/check.js +30 -17
  100. package/src/visualization/renderers/ascii/graph.js +92 -1
  101. package/src/visualization/renderers/ascii/history.js +28 -26
  102. package/src/visualization/renderers/ascii/info.js +9 -7
  103. package/src/visualization/renderers/ascii/materialize.js +20 -16
  104. package/src/visualization/renderers/ascii/opSummary.js +15 -7
  105. package/src/visualization/renderers/ascii/path.js +1 -1
  106. package/src/visualization/renderers/ascii/seek.js +187 -23
  107. package/src/visualization/renderers/ascii/table.js +1 -1
  108. package/src/visualization/renderers/svg/index.js +5 -1
@@ -82,10 +82,10 @@ const BTR_VERSION = 1;
82
82
  * @param {string} fields.h_in - Hash of input state
83
83
  * @param {string} fields.h_out - Hash of output state
84
84
  * @param {Uint8Array} fields.U_0 - Serialized initial state
85
- * @param {Array} fields.P - Serialized provenance payload
85
+ * @param {Array<*>} fields.P - Serialized provenance payload
86
86
  * @param {string} fields.t - ISO timestamp
87
87
  * @param {string|Uint8Array} key - HMAC key
88
- * @param {import('../../ports/CryptoPort.js').default} crypto - CryptoPort instance
88
+ * @param {{ crypto: import('../../ports/CryptoPort.js').default, codec?: import('../../ports/CodecPort.js').default }} deps - Dependencies
89
89
  * @returns {Promise<string>} Hex-encoded HMAC tag
90
90
  * @private
91
91
  */
@@ -111,7 +111,7 @@ async function computeHmac(fields, key, { crypto, codec }) {
111
111
  * @property {string} h_in - Hash of input state (hex SHA-256)
112
112
  * @property {string} h_out - Hash of output state (hex SHA-256)
113
113
  * @property {Uint8Array} U_0 - Serialized initial state (CBOR)
114
- * @property {Array} P - Serialized provenance payload
114
+ * @property {Array<*>} P - Serialized provenance payload
115
115
  * @property {string} t - ISO 8601 timestamp
116
116
  * @property {string} kappa - Authentication tag (hex HMAC-SHA256)
117
117
  */
@@ -155,6 +155,7 @@ async function computeHmac(fields, key, { crypto, codec }) {
155
155
  * @param {string|Uint8Array} options.key - HMAC key for authentication
156
156
  * @param {string} [options.timestamp] - ISO timestamp (defaults to now)
157
157
  * @param {import('../../ports/CryptoPort.js').default} options.crypto - CryptoPort instance
158
+ * @param {import('../../ports/CodecPort.js').default} [options.codec] - Codec for serialization
158
159
  * @returns {Promise<BTR>} The created BTR
159
160
  * @throws {TypeError} If payload is not a ProvenancePayload
160
161
  */
@@ -212,7 +213,7 @@ function validateBTRStructure(btr) {
212
213
  *
213
214
  * @param {BTR} btr - The BTR to verify
214
215
  * @param {string|Uint8Array} key - HMAC key
215
- * @param {import('../../ports/CryptoPort.js').default} crypto - CryptoPort instance
216
+ * @param {{ crypto: import('../../ports/CryptoPort.js').default, codec?: import('../../ports/CodecPort.js').default }} deps - Dependencies
216
217
  * @returns {Promise<boolean>} True if the HMAC tag matches
217
218
  * @private
218
219
  */
@@ -243,10 +244,13 @@ async function verifyHmac(btr, key, { crypto, codec }) {
243
244
  * Verifies replay produces expected h_out.
244
245
  *
245
246
  * @param {BTR} btr - The BTR to verify
247
+ * @param {Object} [deps] - Dependencies
248
+ * @param {import('../../ports/CryptoPort.js').default} [deps.crypto] - CryptoPort instance
249
+ * @param {import('../../ports/CodecPort.js').default} [deps.codec] - Codec
246
250
  * @returns {Promise<string|null>} Error message if replay mismatch, null if valid
247
251
  * @private
248
252
  */
249
- async function verifyReplayHash(btr, { crypto, codec } = {}) {
253
+ async function verifyReplayHash(btr, { crypto, codec } = /** @type {*} */ ({})) { // TODO(ts-cleanup): needs options type
250
254
  try {
251
255
  const result = await replayBTR(btr, { crypto, codec });
252
256
  if (result.h_out !== btr.h_out) {
@@ -254,7 +258,7 @@ async function verifyReplayHash(btr, { crypto, codec } = {}) {
254
258
  }
255
259
  return null;
256
260
  } catch (err) {
257
- return `Replay failed: ${err.message}`;
261
+ return `Replay failed: ${/** @type {any} */ (err).message}`; // TODO(ts-cleanup): type error
258
262
  }
259
263
  }
260
264
 
@@ -272,10 +276,11 @@ async function verifyReplayHash(btr, { crypto, codec } = {}) {
272
276
  * @param {string|Uint8Array} key - HMAC key
273
277
  * @param {Object} [options] - Verification options
274
278
  * @param {boolean} [options.verifyReplay=false] - Also verify replay produces h_out
275
- * @param {import('../../ports/CryptoPort.js').default} options.crypto - CryptoPort instance
279
+ * @param {import('../../ports/CryptoPort.js').default} [options.crypto] - CryptoPort instance
280
+ * @param {import('../../ports/CodecPort.js').default} [options.codec] - Codec for serialization
276
281
  * @returns {Promise<VerificationResult>} Verification result with valid flag and optional reason
277
282
  */
278
- export async function verifyBTR(btr, key, options = {}) {
283
+ export async function verifyBTR(btr, key, options = /** @type {*} */ ({})) { // TODO(ts-cleanup): needs options type
279
284
  const { crypto, codec } = options;
280
285
 
281
286
  const structureError = validateBTRStructure(btr);
@@ -285,7 +290,7 @@ export async function verifyBTR(btr, key, options = {}) {
285
290
 
286
291
  let hmacValid;
287
292
  try {
288
- hmacValid = await verifyHmac(btr, key, { crypto, codec });
293
+ hmacValid = await verifyHmac(btr, key, { crypto: /** @type {import('../../ports/CryptoPort.js').default} */ (crypto), codec });
289
294
  } catch (err) {
290
295
  if (err instanceof RangeError) {
291
296
  return { valid: false, reason: `Invalid hex in authentication tag: ${err.message}` };
@@ -313,11 +318,12 @@ export async function verifyBTR(btr, key, options = {}) {
313
318
  * encoding (U_0, P), replay uniquely determines the interior worldline.
314
319
  *
315
320
  * @param {BTR} btr - The BTR to replay
321
+ * @param {{ crypto?: import('../../ports/CryptoPort.js').default, codec?: import('../../ports/CodecPort.js').default }} deps - Dependencies
316
322
  * @returns {Promise<{ state: import('./JoinReducer.js').WarpStateV5, h_out: string }>}
317
323
  * The final state and its hash
318
324
  * @throws {Error} If replay fails
319
325
  */
320
- export async function replayBTR(btr, { crypto, codec } = {}) {
326
+ export async function replayBTR(btr, { crypto, codec } = /** @type {*} */ ({})) { // TODO(ts-cleanup): needs options type
321
327
  // Deserialize initial state from U_0
322
328
  // Note: U_0 is the full serialized state (via serializeFullStateV5)
323
329
  const initialState = deserializeInitialState(btr.U_0, { codec });
@@ -329,7 +335,7 @@ export async function replayBTR(btr, { crypto, codec } = {}) {
329
335
  const finalState = payload.replay(initialState);
330
336
 
331
337
  // Compute h_out
332
- const h_out = await computeStateHashV5(finalState, { crypto, codec });
338
+ const h_out = await computeStateHashV5(finalState, { crypto: /** @type {import('../../ports/CryptoPort.js').default} */ (crypto), codec });
333
339
 
334
340
  return { state: finalState, h_out };
335
341
  }
@@ -345,10 +351,11 @@ export async function replayBTR(btr, { crypto, codec } = {}) {
345
351
  * the correct h_out hash.
346
352
  *
347
353
  * @param {Uint8Array} U_0 - Serialized full state
354
+ * @param {{ codec?: import('../../ports/CodecPort.js').default }} options
348
355
  * @returns {import('./JoinReducer.js').WarpStateV5} The deserialized state
349
356
  * @private
350
357
  */
351
- function deserializeInitialState(U_0, { codec } = {}) {
358
+ function deserializeInitialState(U_0, { codec } = /** @type {*} */ ({})) { // TODO(ts-cleanup): needs options type
352
359
  return deserializeFullStateV5(U_0, { codec });
353
360
  }
354
361
 
@@ -363,7 +370,7 @@ function deserializeInitialState(U_0, { codec } = {}) {
363
370
  * @param {import('../../ports/CodecPort.js').default} [options.codec] - Codec for serialization
364
371
  * @returns {Uint8Array} CBOR-encoded BTR
365
372
  */
366
- export function serializeBTR(btr, { codec } = {}) {
373
+ export function serializeBTR(btr, { codec } = /** @type {*} */ ({})) { // TODO(ts-cleanup): needs options type
367
374
  const c = codec || defaultCodec;
368
375
  return c.encode({
369
376
  version: btr.version,
@@ -385,9 +392,9 @@ export function serializeBTR(btr, { codec } = {}) {
385
392
  * @returns {BTR} The deserialized BTR
386
393
  * @throws {Error} If the bytes are not valid CBOR or missing required fields
387
394
  */
388
- export function deserializeBTR(bytes, { codec } = {}) {
395
+ export function deserializeBTR(bytes, { codec } = /** @type {*} */ ({})) { // TODO(ts-cleanup): needs options type
389
396
  const c = codec || defaultCodec;
390
- const obj = c.decode(bytes);
397
+ const obj = /** @type {Record<string, *>} */ (c.decode(bytes));
391
398
 
392
399
  // Validate structure (reuse module-level constant for consistency with validateBTRStructure)
393
400
  for (const field of REQUIRED_FIELDS) {
@@ -79,13 +79,7 @@ export function encodeCheckpointMessage({ graph, stateHash, frontierOid, indexOi
79
79
  * Decodes a checkpoint commit message.
80
80
  *
81
81
  * @param {string} message - The raw commit message
82
- * @returns {Object} The decoded checkpoint message
83
- * @returns {string} return.kind - Always 'checkpoint'
84
- * @returns {string} return.graph - The graph name
85
- * @returns {string} return.stateHash - The SHA-256 state hash
86
- * @returns {string} return.frontierOid - The frontier blob OID
87
- * @returns {string} return.indexOid - The index tree OID
88
- * @returns {number} return.schema - The schema version
82
+ * @returns {{ kind: 'checkpoint', graph: string, stateHash: string, frontierOid: string, indexOid: string, schema: number, checkpointVersion: string|null }} The decoded checkpoint message
89
83
  * @throws {Error} If the message is not a valid checkpoint message
90
84
  *
91
85
  * @example
@@ -37,9 +37,9 @@ import { createEmptyStateV5 } from './JoinReducer.js';
37
37
  * @param {import('./JoinReducer.js').WarpStateV5} state
38
38
  * @param {Object} [options]
39
39
  * @param {import('../../ports/CodecPort.js').default} [options.codec] - Codec for serialization
40
- * @returns {Buffer} CBOR-encoded full state
40
+ * @returns {Buffer|Uint8Array} CBOR-encoded full state
41
41
  */
42
- export function serializeFullStateV5(state, { codec } = {}) {
42
+ export function serializeFullStateV5(state, { codec } = /** @type {*} */ ({})) { // TODO(ts-cleanup): needs options type
43
43
  const c = codec || defaultCodec;
44
44
  // Serialize ORSets using existing serialization
45
45
  const nodeAliveObj = orsetSerialize(state.nodeAlive);
@@ -84,20 +84,20 @@ export function serializeFullStateV5(state, { codec } = {}) {
84
84
  /**
85
85
  * Deserializes full V5 state. Used for resume.
86
86
  *
87
- * @param {Buffer} buffer - CBOR-encoded full state
87
+ * @param {Buffer|Uint8Array} buffer - CBOR-encoded full state
88
88
  * @param {Object} [options]
89
89
  * @param {import('../../ports/CodecPort.js').default} [options.codec] - Codec for deserialization
90
90
  * @returns {import('./JoinReducer.js').WarpStateV5}
91
91
  */
92
92
  // eslint-disable-next-line complexity
93
- export function deserializeFullStateV5(buffer, { codec: codecOpt } = {}) {
93
+ export function deserializeFullStateV5(buffer, { codec: codecOpt } = /** @type {*} */ ({})) { // TODO(ts-cleanup): needs options type
94
94
  const codec = codecOpt || defaultCodec;
95
95
  // Handle null/undefined buffer before attempting decode
96
96
  if (buffer === null || buffer === undefined) {
97
97
  return createEmptyStateV5();
98
98
  }
99
99
 
100
- const obj = codec.decode(buffer);
100
+ const obj = /** @type {Record<string, *>} */ (codec.decode(buffer));
101
101
 
102
102
  // Handle null/undefined decoded result: return empty state
103
103
  if (obj === null || obj === undefined) {
@@ -117,7 +117,7 @@ export function deserializeFullStateV5(buffer, { codec: codecOpt } = {}) {
117
117
  edgeAlive: orsetDeserialize(obj.edgeAlive || {}),
118
118
  prop: deserializeProps(obj.prop),
119
119
  observedFrontier: vvDeserialize(obj.observedFrontier || {}),
120
- edgeBirthEvent: deserializeEdgeBirthEvent(obj),
120
+ edgeBirthEvent: /** @type {Map<string, import('../utils/EventId.js').EventId>} */ (deserializeEdgeBirthEvent(obj)),
121
121
  };
122
122
  }
123
123
 
@@ -170,9 +170,9 @@ export function computeAppliedVV(state) {
170
170
  * @param {Map<string, number>} vv - Version vector (Map<writerId, counter>)
171
171
  * @param {Object} [options]
172
172
  * @param {import('../../ports/CodecPort.js').default} [options.codec] - Codec for serialization
173
- * @returns {Buffer} CBOR-encoded version vector
173
+ * @returns {Buffer|Uint8Array} CBOR-encoded version vector
174
174
  */
175
- export function serializeAppliedVV(vv, { codec } = {}) {
175
+ export function serializeAppliedVV(vv, { codec } = /** @type {*} */ ({})) { // TODO(ts-cleanup): needs options type
176
176
  const c = codec || defaultCodec;
177
177
  const obj = vvSerialize(vv);
178
178
  return c.encode(obj);
@@ -181,14 +181,14 @@ export function serializeAppliedVV(vv, { codec } = {}) {
181
181
  /**
182
182
  * Deserializes appliedVV from CBOR format.
183
183
  *
184
- * @param {Buffer} buffer - CBOR-encoded version vector
184
+ * @param {Buffer|Uint8Array} buffer - CBOR-encoded version vector
185
185
  * @param {Object} [options]
186
186
  * @param {import('../../ports/CodecPort.js').default} [options.codec] - Codec for deserialization
187
187
  * @returns {Map<string, number>} Version vector
188
188
  */
189
- export function deserializeAppliedVV(buffer, { codec } = {}) {
189
+ export function deserializeAppliedVV(buffer, { codec } = /** @type {*} */ ({})) { // TODO(ts-cleanup): needs options type
190
190
  const c = codec || defaultCodec;
191
- const obj = c.decode(buffer);
191
+ const obj = /** @type {{ [x: string]: number }} */ (c.decode(buffer));
192
192
  return vvDeserialize(obj);
193
193
  }
194
194
 
@@ -198,8 +198,8 @@ export function deserializeAppliedVV(buffer, { codec } = {}) {
198
198
 
199
199
  /**
200
200
  * Deserializes the props array from checkpoint format.
201
- * @param {Array} propArray - Array of [key, registerObj] pairs
202
- * @returns {Map<string, import('../crdt/LWW.js').LWWRegister>}
201
+ * @param {Array<*>} propArray - Array of [key, registerObj] pairs
202
+ * @returns {Map<string, import('../crdt/LWW.js').LWWRegister<*>>}
203
203
  */
204
204
  function deserializeProps(propArray) {
205
205
  const prop = new Map();
@@ -213,10 +213,11 @@ function deserializeProps(propArray) {
213
213
 
214
214
  /**
215
215
  * Deserializes edge birth event data, supporting both legacy and current formats.
216
- * @param {Object} obj - The decoded checkpoint object
217
- * @returns {Map<string, Object>}
216
+ * @param {Record<string, *>} obj - The decoded checkpoint object
217
+ * @returns {Map<string, import('../utils/EventId.js').EventId>}
218
218
  */
219
219
  function deserializeEdgeBirthEvent(obj) {
220
+ /** @type {Map<string, import('../utils/EventId.js').EventId>} */
220
221
  const edgeBirthEvent = new Map();
221
222
  const birthData = obj.edgeBirthEvent || obj.edgeBirthLamport;
222
223
  if (birthData && Array.isArray(birthData)) {
@@ -239,8 +240,8 @@ function deserializeEdgeBirthEvent(obj) {
239
240
  * Serializes an LWW register for CBOR encoding.
240
241
  * EventId is serialized as a plain object with sorted keys.
241
242
  *
242
- * @param {import('../crdt/LWW.js').LWWRegister} register
243
- * @returns {Object}
243
+ * @param {import('../crdt/LWW.js').LWWRegister<*>} register
244
+ * @returns {{ eventId: { lamport: number, opIndex: number, patchSha: string, writerId: string }, value: * } | null}
244
245
  */
245
246
  function serializeLWWRegister(register) {
246
247
  if (!register) {
@@ -261,8 +262,8 @@ function serializeLWWRegister(register) {
261
262
  /**
262
263
  * Deserializes an LWW register from CBOR.
263
264
  *
264
- * @param {Object} obj
265
- * @returns {import('../crdt/LWW.js').LWWRegister}
265
+ * @param {{ eventId: { lamport: number, writerId: string, patchSha: string, opIndex: number }, value: * } | null} obj
266
+ * @returns {import('../crdt/LWW.js').LWWRegister<*> | null}
266
267
  */
267
268
  function deserializeLWWRegister(obj) {
268
269
  if (!obj) {
@@ -46,7 +46,7 @@ import { ProvenanceIndex } from './ProvenanceIndex.js';
46
46
  * ```
47
47
  *
48
48
  * @param {Object} options - Checkpoint creation options
49
- * @param {import('../../ports/GraphPersistencePort.js').default} options.persistence - Git persistence adapter
49
+ * @param {import('../../ports/GraphPersistencePort.js').default & import('../../ports/BlobPort.js').default & import('../../ports/TreePort.js').default & import('../../ports/CommitPort.js').default} options.persistence - Git persistence adapter
50
50
  * @param {string} options.graphName - Name of the graph
51
51
  * @param {import('./JoinReducer.js').WarpStateV5} options.state - The V5 state to checkpoint
52
52
  * @param {import('./Frontier.js').Frontier} options.frontier - Writer frontier map
@@ -75,7 +75,7 @@ export async function create({ persistence, graphName, state, frontier, parents
75
75
  * ```
76
76
  *
77
77
  * @param {Object} options - Checkpoint creation options
78
- * @param {import('../../ports/GraphPersistencePort.js').default} options.persistence - Git persistence adapter
78
+ * @param {import('../../ports/GraphPersistencePort.js').default & import('../../ports/BlobPort.js').default & import('../../ports/TreePort.js').default & import('../../ports/CommitPort.js').default} options.persistence - Git persistence adapter
79
79
  * @param {string} options.graphName - Name of the graph
80
80
  * @param {import('./JoinReducer.js').WarpStateV5} options.state - The V5 state to checkpoint
81
81
  * @param {import('./Frontier.js').Frontier} options.frontier - Writer frontier map
@@ -113,23 +113,23 @@ export async function createV5({
113
113
 
114
114
  // 4. Serialize visible projection (CACHE)
115
115
  const visibleBuffer = serializeStateV5(checkpointState, { codec });
116
- const stateHash = await computeStateHashV5(checkpointState, { codec, crypto });
116
+ const stateHash = await computeStateHashV5(checkpointState, { codec, crypto: /** @type {import('../../ports/CryptoPort.js').default} */ (crypto) });
117
117
 
118
118
  // 5. Serialize frontier and appliedVV
119
- const frontierBuffer = serializeFrontier(frontier, { codec });
120
- const appliedVVBuffer = serializeAppliedVV(appliedVV, { codec });
119
+ const frontierBuffer = serializeFrontier(frontier, { codec: /** @type {import('../../ports/CodecPort.js').default} */ (codec) });
120
+ const appliedVVBuffer = serializeAppliedVV(appliedVV, { codec: /** @type {import('../../ports/CodecPort.js').default} */ (codec) });
121
121
 
122
122
  // 6. Write blobs to git
123
- const stateBlobOid = await persistence.writeBlob(stateBuffer);
124
- const visibleBlobOid = await persistence.writeBlob(visibleBuffer);
125
- const frontierBlobOid = await persistence.writeBlob(frontierBuffer);
126
- const appliedVVBlobOid = await persistence.writeBlob(appliedVVBuffer);
123
+ const stateBlobOid = await persistence.writeBlob(/** @type {Buffer} */ (stateBuffer));
124
+ const visibleBlobOid = await persistence.writeBlob(/** @type {Buffer} */ (visibleBuffer));
125
+ const frontierBlobOid = await persistence.writeBlob(/** @type {Buffer} */ (frontierBuffer));
126
+ const appliedVVBlobOid = await persistence.writeBlob(/** @type {Buffer} */ (appliedVVBuffer));
127
127
 
128
128
  // 6b. Optionally serialize and write provenance index
129
129
  let provenanceIndexBlobOid = null;
130
130
  if (provenanceIndex) {
131
131
  const provenanceIndexBuffer = provenanceIndex.serialize({ codec });
132
- provenanceIndexBlobOid = await persistence.writeBlob(provenanceIndexBuffer);
132
+ provenanceIndexBlobOid = await persistence.writeBlob(/** @type {Buffer} */ (provenanceIndexBuffer));
133
133
  }
134
134
 
135
135
  // 7. Create tree with sorted entries
@@ -189,17 +189,17 @@ export async function createV5({
189
189
  * Schema:1 checkpoints are not supported and will throw an error.
190
190
  * Use MigrationService to upgrade schema:1 checkpoints first.
191
191
  *
192
- * @param {import('../../ports/GraphPersistencePort.js').default} persistence - Git persistence adapter
192
+ * @param {import('../../ports/GraphPersistencePort.js').default & import('../../ports/BlobPort.js').default & import('../../ports/TreePort.js').default & import('../../ports/CommitPort.js').default} persistence - Git persistence adapter
193
193
  * @param {string} checkpointSha - The checkpoint commit SHA to load
194
194
  * @param {Object} [options] - Load options
195
195
  * @param {import('../../ports/CodecPort.js').default} [options.codec] - Codec for CBOR deserialization
196
- * @returns {Promise<{state: import('./JoinReducer.js').WarpStateV5, frontier: import('./Frontier.js').Frontier, stateHash: string, schema: number, appliedVV?: Map<string, number>, provenanceIndex?: import('./ProvenanceIndex.js').ProvenanceIndex}>} The loaded checkpoint data
196
+ * @returns {Promise<{state: import('./JoinReducer.js').WarpStateV5, frontier: import('./Frontier.js').Frontier, stateHash: string, schema: number, appliedVV: Map<string, number>|null, provenanceIndex?: import('./ProvenanceIndex.js').ProvenanceIndex}>} The loaded checkpoint data
197
197
  * @throws {Error} If checkpoint is schema:1 (migration required)
198
198
  */
199
- export async function loadCheckpoint(persistence, checkpointSha, { codec } = {}) {
199
+ export async function loadCheckpoint(persistence, checkpointSha, { codec } = /** @type {*} */ ({})) { // TODO(ts-cleanup): needs options type
200
200
  // 1. Read commit message and decode
201
201
  const message = await persistence.showNode(checkpointSha);
202
- const decoded = decodeCheckpointMessage(message);
202
+ const decoded = /** @type {{ schema: number, stateHash: string, indexOid: string }} */ (decodeCheckpointMessage(message));
203
203
 
204
204
  // 2. Reject schema:1 checkpoints - migration required
205
205
  if (decoded.schema !== 2 && decoded.schema !== 3) {
@@ -218,7 +218,7 @@ export async function loadCheckpoint(persistence, checkpointSha, { codec } = {})
218
218
  throw new Error(`Checkpoint ${checkpointSha} missing frontier.cbor in tree`);
219
219
  }
220
220
  const frontierBuffer = await persistence.readBlob(frontierOid);
221
- const frontier = deserializeFrontier(frontierBuffer, { codec });
221
+ const frontier = deserializeFrontier(frontierBuffer, { codec: /** @type {import('../../ports/CodecPort.js').default} */ (codec) });
222
222
 
223
223
  // 5. Read state.cbor blob and deserialize as V5 full state
224
224
  const stateOid = treeOids['state.cbor'];
@@ -252,7 +252,7 @@ export async function loadCheckpoint(persistence, checkpointSha, { codec } = {})
252
252
  stateHash: decoded.stateHash,
253
253
  schema: decoded.schema,
254
254
  appliedVV,
255
- provenanceIndex,
255
+ provenanceIndex: provenanceIndex || undefined,
256
256
  };
257
257
  }
258
258
 
@@ -270,7 +270,7 @@ export async function loadCheckpoint(persistence, checkpointSha, { codec } = {})
270
270
  * loadCheckpoint to throw an error.
271
271
  *
272
272
  * @param {Object} options - Materialization options
273
- * @param {import('../../ports/GraphPersistencePort.js').default} options.persistence - Git persistence adapter
273
+ * @param {import('../../ports/GraphPersistencePort.js').default & import('../../ports/BlobPort.js').default & import('../../ports/TreePort.js').default & import('../../ports/CommitPort.js').default} options.persistence - Git persistence adapter
274
274
  * @param {string} options.graphName - Name of the graph
275
275
  * @param {string} options.checkpointSha - The schema:2 checkpoint commit SHA to start from
276
276
  * @param {import('./Frontier.js').Frontier} options.targetFrontier - The target frontier to materialize to
@@ -313,7 +313,7 @@ export async function materializeIncremental({
313
313
  }
314
314
 
315
315
  // 5. Apply new patches using V5 reducer with checkpoint state as initial
316
- const finalState = reduceV5(allPatches, initialState);
316
+ const finalState = /** @type {import('./JoinReducer.js').WarpStateV5} */ (reduceV5(allPatches, initialState));
317
317
 
318
318
  return finalState;
319
319
  }
@@ -39,7 +39,7 @@ export default class CommitDagTraversalService {
39
39
  * @param {import('./BitmapIndexReader.js').default} options.indexReader - Index reader for O(1) lookups
40
40
  * @param {import('../../ports/LoggerPort.js').default} [options.logger] - Logger instance
41
41
  */
42
- constructor({ indexReader, logger = nullLogger } = {}) {
42
+ constructor({ indexReader, logger = nullLogger } = /** @type {*} */ ({})) { // TODO(ts-cleanup): needs options type
43
43
  if (!indexReader) {
44
44
  throw new Error('CommitDagTraversalService requires an indexReader');
45
45
  }
@@ -56,6 +56,7 @@ export default class CommitDagTraversalService {
56
56
 
57
57
  /**
58
58
  * Breadth-first traversal from a starting node.
59
+ * @param {*} options
59
60
  * @see DagTraversal#bfs
60
61
  */
61
62
  bfs(options) {
@@ -64,6 +65,7 @@ export default class CommitDagTraversalService {
64
65
 
65
66
  /**
66
67
  * Depth-first pre-order traversal from a starting node.
68
+ * @param {*} options
67
69
  * @see DagTraversal#dfs
68
70
  */
69
71
  dfs(options) {
@@ -72,6 +74,7 @@ export default class CommitDagTraversalService {
72
74
 
73
75
  /**
74
76
  * Yields all ancestors of a node.
77
+ * @param {*} options
75
78
  * @see DagTraversal#ancestors
76
79
  */
77
80
  ancestors(options) {
@@ -80,6 +83,7 @@ export default class CommitDagTraversalService {
80
83
 
81
84
  /**
82
85
  * Yields all descendants of a node.
86
+ * @param {*} options
83
87
  * @see DagTraversal#descendants
84
88
  */
85
89
  descendants(options) {
@@ -88,6 +92,7 @@ export default class CommitDagTraversalService {
88
92
 
89
93
  /**
90
94
  * Checks if there is any path from one node to another.
95
+ * @param {*} options
91
96
  * @see DagTraversal#isReachable
92
97
  */
93
98
  isReachable(options) {
@@ -98,6 +103,7 @@ export default class CommitDagTraversalService {
98
103
 
99
104
  /**
100
105
  * Finds ANY path between two nodes using BFS.
106
+ * @param {*} options
101
107
  * @see DagPathFinding#findPath
102
108
  */
103
109
  findPath(options) {
@@ -106,6 +112,7 @@ export default class CommitDagTraversalService {
106
112
 
107
113
  /**
108
114
  * Finds the shortest path using bidirectional BFS.
115
+ * @param {*} options
109
116
  * @see DagPathFinding#shortestPath
110
117
  */
111
118
  shortestPath(options) {
@@ -114,6 +121,7 @@ export default class CommitDagTraversalService {
114
121
 
115
122
  /**
116
123
  * Finds shortest path using Dijkstra's algorithm.
124
+ * @param {*} options
117
125
  * @see DagPathFinding#weightedShortestPath
118
126
  */
119
127
  weightedShortestPath(options) {
@@ -122,6 +130,7 @@ export default class CommitDagTraversalService {
122
130
 
123
131
  /**
124
132
  * Finds shortest path using A* with heuristic guidance.
133
+ * @param {*} options
125
134
  * @see DagPathFinding#aStarSearch
126
135
  */
127
136
  aStarSearch(options) {
@@ -130,6 +139,7 @@ export default class CommitDagTraversalService {
130
139
 
131
140
  /**
132
141
  * Bi-directional A* search.
142
+ * @param {*} options
133
143
  * @see DagPathFinding#bidirectionalAStar
134
144
  */
135
145
  bidirectionalAStar(options) {
@@ -140,6 +150,7 @@ export default class CommitDagTraversalService {
140
150
 
141
151
  /**
142
152
  * Finds common ancestors of multiple nodes.
153
+ * @param {*} options
143
154
  * @see DagTopology#commonAncestors
144
155
  */
145
156
  commonAncestors(options) {
@@ -148,6 +159,7 @@ export default class CommitDagTraversalService {
148
159
 
149
160
  /**
150
161
  * Yields nodes in topological order using Kahn's algorithm.
162
+ * @param {*} options
151
163
  * @see DagTopology#topologicalSort
152
164
  */
153
165
  topologicalSort(options) {
@@ -41,7 +41,7 @@ export default class DagPathFinding {
41
41
  * @param {import('./BitmapIndexReader.js').default} options.indexReader - Index reader for O(1) lookups
42
42
  * @param {import('../../ports/LoggerPort.js').default} [options.logger] - Logger instance
43
43
  */
44
- constructor({ indexReader, logger = nullLogger } = {}) {
44
+ constructor(/** @type {{ indexReader: import('./BitmapIndexReader.js').default, logger?: import('../../ports/LoggerPort.js').default }} */ { indexReader, logger = nullLogger } = /** @type {*} */ ({})) { // TODO(ts-cleanup): needs options type
45
45
  if (!indexReader) {
46
46
  throw new Error('DagPathFinding requires an indexReader');
47
47
  }
@@ -85,7 +85,7 @@ export default class DagPathFinding {
85
85
  checkAborted(signal, 'findPath');
86
86
  }
87
87
 
88
- const current = queue.shift();
88
+ const current = /** @type {{sha: string, depth: number}} */ (queue.shift());
89
89
 
90
90
  if (current.depth > maxDepth) { continue; }
91
91
  if (visited.has(current.sha)) { continue; }
@@ -200,7 +200,7 @@ export default class DagPathFinding {
200
200
  * @param {Object} options - Path finding options
201
201
  * @param {string} options.from - Starting SHA
202
202
  * @param {string} options.to - Target SHA
203
- * @param {Function} [options.weightProvider] - Async callback `(fromSha, toSha) => number`
203
+ * @param {(from: string, to: string) => number|Promise<number>} [options.weightProvider] - Async callback `(fromSha, toSha) => number`
204
204
  * @param {string} [options.direction='children'] - Edge direction: 'children' or 'parents'
205
205
  * @param {AbortSignal} [options.signal] - Optional AbortSignal for cancellation
206
206
  * @returns {Promise<{path: string[], totalCost: number}>} Path and cost
@@ -277,8 +277,8 @@ export default class DagPathFinding {
277
277
  * @param {Object} options - Path finding options
278
278
  * @param {string} options.from - Starting SHA
279
279
  * @param {string} options.to - Target SHA
280
- * @param {Function} [options.weightProvider] - Async callback `(fromSha, toSha) => number`
281
- * @param {Function} [options.heuristicProvider] - Callback `(sha, targetSha) => number`
280
+ * @param {(from: string, to: string) => number|Promise<number>} [options.weightProvider] - Async callback `(fromSha, toSha) => number`
281
+ * @param {(sha: string, target: string) => number} [options.heuristicProvider] - Callback `(sha, targetSha) => number`
282
282
  * @param {string} [options.direction='children'] - Edge direction: 'children' or 'parents'
283
283
  * @param {AbortSignal} [options.signal] - Optional AbortSignal for cancellation
284
284
  * @returns {Promise<{path: string[], totalCost: number, nodesExplored: number}>} Path result
@@ -367,9 +367,9 @@ export default class DagPathFinding {
367
367
  * @param {Object} options - Path finding options
368
368
  * @param {string} options.from - Starting SHA
369
369
  * @param {string} options.to - Target SHA
370
- * @param {Function} [options.weightProvider] - Async callback `(fromSha, toSha) => number`
371
- * @param {Function} [options.forwardHeuristic] - Callback for forward search
372
- * @param {Function} [options.backwardHeuristic] - Callback for backward search
370
+ * @param {(from: string, to: string) => number|Promise<number>} [options.weightProvider] - Async callback `(fromSha, toSha) => number`
371
+ * @param {(sha: string, target: string) => number} [options.forwardHeuristic] - Callback for forward search
372
+ * @param {(sha: string, target: string) => number} [options.backwardHeuristic] - Callback for backward search
373
373
  * @param {AbortSignal} [options.signal] - Optional AbortSignal for cancellation
374
374
  * @returns {Promise<{path: string[], totalCost: number, nodesExplored: number}>} Path result
375
375
  * @throws {TraversalError} With code 'NO_PATH' if no path exists
@@ -463,6 +463,17 @@ export default class DagPathFinding {
463
463
  * Expands the forward frontier by one node in bidirectional A*.
464
464
  *
465
465
  * @param {Object} state - Forward expansion state
466
+ * @param {import('../utils/MinHeap.js').default} state.fwdHeap
467
+ * @param {Set<string>} state.fwdVisited
468
+ * @param {Map<string, number>} state.fwdGScore
469
+ * @param {Map<string, string>} state.fwdPrevious
470
+ * @param {Set<string>} state.bwdVisited
471
+ * @param {Map<string, number>} state.bwdGScore
472
+ * @param {(from: string, to: string) => number|Promise<number>} state.weightProvider
473
+ * @param {(sha: string, target: string) => number} state.forwardHeuristic
474
+ * @param {string} state.to
475
+ * @param {number} state.mu
476
+ * @param {string|null} state.meetingPoint
466
477
  * @returns {Promise<{explored: number, mu: number, meetingPoint: string|null}>}
467
478
  * @private
468
479
  */
@@ -484,7 +495,7 @@ export default class DagPathFinding {
484
495
  explored = 1;
485
496
 
486
497
  if (bwdVisited.has(current)) {
487
- const totalCost = fwdGScore.get(current) + bwdGScore.get(current);
498
+ const totalCost = /** @type {number} */ (fwdGScore.get(current)) + /** @type {number} */ (bwdGScore.get(current));
488
499
  if (totalCost < bestMu) {
489
500
  bestMu = totalCost;
490
501
  bestMeeting = current;
@@ -498,8 +509,8 @@ export default class DagPathFinding {
498
509
  }
499
510
 
500
511
  const edgeWeight = await weightProvider(current, child);
501
- const tentativeG = fwdGScore.get(current) + edgeWeight;
502
- const currentG = fwdGScore.has(child) ? fwdGScore.get(child) : Infinity;
512
+ const tentativeG = /** @type {number} */ (fwdGScore.get(current)) + edgeWeight;
513
+ const currentG = fwdGScore.has(child) ? /** @type {number} */ (fwdGScore.get(child)) : Infinity;
503
514
 
504
515
  if (tentativeG < currentG) {
505
516
  fwdPrevious.set(child, current);
@@ -509,7 +520,7 @@ export default class DagPathFinding {
509
520
  fwdHeap.insert(child, f);
510
521
 
511
522
  if (bwdGScore.has(child)) {
512
- const totalCost = tentativeG + bwdGScore.get(child);
523
+ const totalCost = tentativeG + /** @type {number} */ (bwdGScore.get(child));
513
524
  if (totalCost < bestMu) {
514
525
  bestMu = totalCost;
515
526
  bestMeeting = child;
@@ -525,6 +536,17 @@ export default class DagPathFinding {
525
536
  * Expands the backward frontier by one node in bidirectional A*.
526
537
  *
527
538
  * @param {Object} state - Backward expansion state
539
+ * @param {import('../utils/MinHeap.js').default} state.bwdHeap
540
+ * @param {Set<string>} state.bwdVisited
541
+ * @param {Map<string, number>} state.bwdGScore
542
+ * @param {Map<string, string>} state.bwdNext
543
+ * @param {Set<string>} state.fwdVisited
544
+ * @param {Map<string, number>} state.fwdGScore
545
+ * @param {(from: string, to: string) => number|Promise<number>} state.weightProvider
546
+ * @param {(sha: string, target: string) => number} state.backwardHeuristic
547
+ * @param {string} state.from
548
+ * @param {number} state.mu
549
+ * @param {string|null} state.meetingPoint
528
550
  * @returns {Promise<{explored: number, mu: number, meetingPoint: string|null}>}
529
551
  * @private
530
552
  */
@@ -546,7 +568,7 @@ export default class DagPathFinding {
546
568
  explored = 1;
547
569
 
548
570
  if (fwdVisited.has(current)) {
549
- const totalCost = fwdGScore.get(current) + bwdGScore.get(current);
571
+ const totalCost = /** @type {number} */ (fwdGScore.get(current)) + /** @type {number} */ (bwdGScore.get(current));
550
572
  if (totalCost < bestMu) {
551
573
  bestMu = totalCost;
552
574
  bestMeeting = current;
@@ -560,8 +582,8 @@ export default class DagPathFinding {
560
582
  }
561
583
 
562
584
  const edgeWeight = await weightProvider(parent, current);
563
- const tentativeG = bwdGScore.get(current) + edgeWeight;
564
- const currentG = bwdGScore.has(parent) ? bwdGScore.get(parent) : Infinity;
585
+ const tentativeG = /** @type {number} */ (bwdGScore.get(current)) + edgeWeight;
586
+ const currentG = bwdGScore.has(parent) ? /** @type {number} */ (bwdGScore.get(parent)) : Infinity;
565
587
 
566
588
  if (tentativeG < currentG) {
567
589
  bwdNext.set(parent, current);
@@ -571,7 +593,7 @@ export default class DagPathFinding {
571
593
  bwdHeap.insert(parent, f);
572
594
 
573
595
  if (fwdGScore.has(parent)) {
574
- const totalCost = fwdGScore.get(parent) + tentativeG;
596
+ const totalCost = /** @type {number} */ (fwdGScore.get(parent)) + tentativeG;
575
597
  if (totalCost < bestMu) {
576
598
  bestMu = totalCost;
577
599
  bestMeeting = parent;
@@ -691,7 +713,7 @@ export default class DagPathFinding {
691
713
  const forwardPath = [meeting];
692
714
  let current = meeting;
693
715
  while (fwdParent.has(current) && fwdParent.get(current) !== undefined) {
694
- current = fwdParent.get(current);
716
+ current = /** @type {string} */ (fwdParent.get(current));
695
717
  forwardPath.unshift(current);
696
718
  }
697
719
  if (forwardPath[0] !== from) {
@@ -700,7 +722,7 @@ export default class DagPathFinding {
700
722
 
701
723
  current = meeting;
702
724
  while (bwdParent.has(current) && bwdParent.get(current) !== undefined) {
703
- current = bwdParent.get(current);
725
+ current = /** @type {string} */ (bwdParent.get(current));
704
726
  forwardPath.push(current);
705
727
  }
706
728
  if (forwardPath[forwardPath.length - 1] !== to) {