@git-stunts/git-warp 10.3.2 → 10.4.2

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 (104) hide show
  1. package/README.md +6 -3
  2. package/bin/warp-graph.js +371 -141
  3. package/index.d.ts +31 -0
  4. package/index.js +4 -0
  5. package/package.json +8 -3
  6. package/src/domain/WarpGraph.js +263 -147
  7. package/src/domain/crdt/LWW.js +1 -1
  8. package/src/domain/crdt/ORSet.js +10 -6
  9. package/src/domain/crdt/VersionVector.js +5 -1
  10. package/src/domain/errors/EmptyMessageError.js +2 -4
  11. package/src/domain/errors/ForkError.js +4 -0
  12. package/src/domain/errors/IndexError.js +4 -0
  13. package/src/domain/errors/OperationAbortedError.js +4 -0
  14. package/src/domain/errors/QueryError.js +4 -0
  15. package/src/domain/errors/SchemaUnsupportedError.js +4 -0
  16. package/src/domain/errors/ShardCorruptionError.js +2 -6
  17. package/src/domain/errors/ShardLoadError.js +2 -6
  18. package/src/domain/errors/ShardValidationError.js +2 -7
  19. package/src/domain/errors/StorageError.js +2 -6
  20. package/src/domain/errors/SyncError.js +4 -0
  21. package/src/domain/errors/TraversalError.js +4 -0
  22. package/src/domain/errors/WarpError.js +2 -4
  23. package/src/domain/errors/WormholeError.js +4 -0
  24. package/src/domain/services/AnchorMessageCodec.js +1 -4
  25. package/src/domain/services/BitmapIndexBuilder.js +10 -6
  26. package/src/domain/services/BitmapIndexReader.js +27 -21
  27. package/src/domain/services/BoundaryTransitionRecord.js +22 -15
  28. package/src/domain/services/CheckpointMessageCodec.js +1 -7
  29. package/src/domain/services/CheckpointSerializerV5.js +20 -19
  30. package/src/domain/services/CheckpointService.js +18 -18
  31. package/src/domain/services/CommitDagTraversalService.js +13 -1
  32. package/src/domain/services/DagPathFinding.js +40 -18
  33. package/src/domain/services/DagTopology.js +7 -6
  34. package/src/domain/services/DagTraversal.js +5 -3
  35. package/src/domain/services/Frontier.js +7 -6
  36. package/src/domain/services/HealthCheckService.js +15 -14
  37. package/src/domain/services/HookInstaller.js +64 -13
  38. package/src/domain/services/HttpSyncServer.js +15 -14
  39. package/src/domain/services/IndexRebuildService.js +12 -12
  40. package/src/domain/services/IndexStalenessChecker.js +13 -6
  41. package/src/domain/services/JoinReducer.js +28 -27
  42. package/src/domain/services/LogicalTraversal.js +7 -6
  43. package/src/domain/services/MessageCodecInternal.js +2 -0
  44. package/src/domain/services/ObserverView.js +6 -6
  45. package/src/domain/services/PatchBuilderV2.js +9 -9
  46. package/src/domain/services/PatchMessageCodec.js +1 -7
  47. package/src/domain/services/ProvenanceIndex.js +6 -8
  48. package/src/domain/services/ProvenancePayload.js +1 -2
  49. package/src/domain/services/QueryBuilder.js +29 -23
  50. package/src/domain/services/StateDiff.js +7 -7
  51. package/src/domain/services/StateSerializerV5.js +8 -6
  52. package/src/domain/services/StreamingBitmapIndexBuilder.js +29 -23
  53. package/src/domain/services/SyncProtocol.js +23 -26
  54. package/src/domain/services/TemporalQuery.js +4 -3
  55. package/src/domain/services/TranslationCost.js +4 -4
  56. package/src/domain/services/WormholeService.js +19 -15
  57. package/src/domain/types/TickReceipt.js +10 -6
  58. package/src/domain/types/WarpTypesV2.js +2 -3
  59. package/src/domain/utils/CachedValue.js +1 -1
  60. package/src/domain/utils/LRUCache.js +3 -3
  61. package/src/domain/utils/MinHeap.js +2 -2
  62. package/src/domain/utils/RefLayout.js +19 -0
  63. package/src/domain/utils/WriterId.js +2 -2
  64. package/src/domain/utils/defaultCodec.js +9 -2
  65. package/src/domain/utils/defaultCrypto.js +36 -0
  66. package/src/domain/utils/roaring.js +5 -5
  67. package/src/domain/utils/seekCacheKey.js +32 -0
  68. package/src/domain/warp/PatchSession.js +3 -3
  69. package/src/domain/warp/Writer.js +2 -2
  70. package/src/infrastructure/adapters/BunHttpAdapter.js +21 -8
  71. package/src/infrastructure/adapters/CasSeekCacheAdapter.js +311 -0
  72. package/src/infrastructure/adapters/ClockAdapter.js +2 -2
  73. package/src/infrastructure/adapters/DenoHttpAdapter.js +22 -9
  74. package/src/infrastructure/adapters/GitGraphAdapter.js +16 -27
  75. package/src/infrastructure/adapters/NodeCryptoAdapter.js +16 -3
  76. package/src/infrastructure/adapters/NodeHttpAdapter.js +33 -11
  77. package/src/infrastructure/adapters/WebCryptoAdapter.js +21 -11
  78. package/src/infrastructure/codecs/CborCodec.js +16 -8
  79. package/src/ports/BlobPort.js +2 -2
  80. package/src/ports/CodecPort.js +2 -2
  81. package/src/ports/CommitPort.js +8 -21
  82. package/src/ports/ConfigPort.js +3 -3
  83. package/src/ports/CryptoPort.js +7 -7
  84. package/src/ports/GraphPersistencePort.js +12 -14
  85. package/src/ports/HttpServerPort.js +1 -5
  86. package/src/ports/IndexStoragePort.js +1 -0
  87. package/src/ports/LoggerPort.js +9 -9
  88. package/src/ports/RefPort.js +5 -5
  89. package/src/ports/SeekCachePort.js +73 -0
  90. package/src/ports/TreePort.js +3 -3
  91. package/src/visualization/layouts/converters.js +14 -7
  92. package/src/visualization/layouts/elkAdapter.js +17 -4
  93. package/src/visualization/layouts/elkLayout.js +23 -7
  94. package/src/visualization/layouts/index.js +3 -3
  95. package/src/visualization/renderers/ascii/check.js +30 -17
  96. package/src/visualization/renderers/ascii/graph.js +92 -1
  97. package/src/visualization/renderers/ascii/history.js +28 -26
  98. package/src/visualization/renderers/ascii/info.js +9 -7
  99. package/src/visualization/renderers/ascii/materialize.js +20 -16
  100. package/src/visualization/renderers/ascii/opSummary.js +15 -7
  101. package/src/visualization/renderers/ascii/path.js +1 -1
  102. package/src/visualization/renderers/ascii/seek.js +19 -5
  103. package/src/visualization/renderers/ascii/table.js +1 -1
  104. package/src/visualization/renderers/svg/index.js +5 -1
@@ -2,9 +2,9 @@ import CryptoPort from '../../ports/CryptoPort.js';
2
2
 
3
3
  /**
4
4
  * Map of common algorithm names to Web Crypto API algorithm identifiers.
5
- * @const {Object<string, string>}
5
+ * @const {Record<string, string>}
6
6
  */
7
- const ALGO_MAP = {
7
+ const ALGO_MAP = /** @type {Record<string, string>} */ ({
8
8
  'sha-1': 'SHA-1',
9
9
  'sha1': 'SHA-1',
10
10
  'sha-256': 'SHA-256',
@@ -13,7 +13,7 @@ const ALGO_MAP = {
13
13
  'sha384': 'SHA-384',
14
14
  'sha-512': 'SHA-512',
15
15
  'sha512': 'SHA-512',
16
- };
16
+ });
17
17
 
18
18
  /**
19
19
  * Converts a common algorithm name to the Web Crypto API identifier.
@@ -38,8 +38,9 @@ function toWebCryptoAlgo(algorithm) {
38
38
  function toUint8Array(data) {
39
39
  if (data instanceof Uint8Array) { return data; }
40
40
  if (typeof data === 'string') { return new TextEncoder().encode(data); }
41
- if (typeof Buffer !== 'undefined' && Buffer.isBuffer(data)) {
42
- return new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
41
+ if (typeof Buffer !== 'undefined' && Buffer.isBuffer(/** @type {*} */ (data))) { // TODO(ts-cleanup): narrow port type
42
+ const buf = /** @type {Buffer} */ (/** @type {*} */ (data)); // TODO(ts-cleanup): narrow port type
43
+ return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
43
44
  }
44
45
  throw new Error('WebCryptoAdapter: data must be string, Buffer, or Uint8Array');
45
46
  }
@@ -59,7 +60,7 @@ function bufToHex(buf) {
59
60
  * Web Crypto API adapter implementing CryptoPort.
60
61
  *
61
62
  * Uses the standard Web Crypto API (globalThis.crypto.subtle) which is
62
- * available in browsers, Deno, Bun, and Node.js 20+.
63
+ * available in browsers, Deno, Bun, and Node.js 22+.
63
64
  *
64
65
  * All hash and HMAC operations are async because the Web Crypto API
65
66
  * is inherently promise-based.
@@ -77,26 +78,35 @@ export default class WebCryptoAdapter extends CryptoPort {
77
78
  this._subtle = subtle || globalThis.crypto.subtle;
78
79
  }
79
80
 
80
- /** @inheritdoc */
81
+ /**
82
+ * @param {string} algorithm
83
+ * @param {string|Buffer|Uint8Array} data
84
+ * @returns {Promise<string>}
85
+ */
81
86
  async hash(algorithm, data) {
82
87
  const digest = await this._subtle.digest(
83
88
  toWebCryptoAlgo(algorithm),
84
- toUint8Array(data),
89
+ /** @type {BufferSource} */ (toUint8Array(data)),
85
90
  );
86
91
  return bufToHex(digest);
87
92
  }
88
93
 
89
- /** @inheritdoc */
94
+ /**
95
+ * @param {string} algorithm
96
+ * @param {string|Buffer|Uint8Array} key
97
+ * @param {string|Buffer|Uint8Array} data
98
+ * @returns {Promise<Uint8Array>}
99
+ */
90
100
  async hmac(algorithm, key, data) {
91
101
  const keyBytes = toUint8Array(key);
92
102
  const cryptoKey = await this._subtle.importKey(
93
103
  'raw',
94
- keyBytes,
104
+ /** @type {BufferSource} */ (keyBytes),
95
105
  { name: 'HMAC', hash: toWebCryptoAlgo(algorithm) },
96
106
  false,
97
107
  ['sign'],
98
108
  );
99
- const signature = await this._subtle.sign('HMAC', cryptoKey, toUint8Array(data));
109
+ const signature = await this._subtle.sign('HMAC', cryptoKey, /** @type {BufferSource} */ (toUint8Array(data)));
100
110
  return new Uint8Array(signature);
101
111
  }
102
112
 
@@ -83,17 +83,18 @@ const encoder = new Encoder({
83
83
  * @private
84
84
  */
85
85
  function isPlainObject(value) {
86
- return typeof value === 'object' && (value.constructor === Object || value.constructor === undefined);
86
+ return typeof value === 'object' && value !== null && (value.constructor === Object || value.constructor === undefined);
87
87
  }
88
88
 
89
89
  /**
90
90
  * Sorts the keys of a plain object and recursively processes values.
91
91
  *
92
- * @param {Object} obj - The plain object to process
93
- * @returns {Object} A new object with sorted keys
92
+ * @param {Record<string, unknown>} obj - The plain object to process
93
+ * @returns {Record<string, unknown>} A new object with sorted keys
94
94
  * @private
95
95
  */
96
96
  function sortPlainObject(obj) {
97
+ /** @type {Record<string, unknown>} */
97
98
  const sorted = {};
98
99
  const keys = Object.keys(obj).sort();
99
100
  for (const key of keys) {
@@ -106,8 +107,8 @@ function sortPlainObject(obj) {
106
107
  * Converts a Map to a sorted plain object with recursive value processing.
107
108
  * Validates that all Map keys are strings (required for CBOR encoding).
108
109
  *
109
- * @param {Map} map - The Map instance to convert
110
- * @returns {Object} A plain object with sorted keys
110
+ * @param {Map<string, unknown>} map - The Map instance to convert
111
+ * @returns {Record<string, unknown>} A plain object with sorted keys
111
112
  * @throws {TypeError} If any Map key is not a string
112
113
  * @private
113
114
  */
@@ -118,6 +119,7 @@ function sortMapToObject(map) {
118
119
  throw new TypeError(`Map keys must be strings for CBOR encoding, got ${typeof key}`);
119
120
  }
120
121
  }
122
+ /** @type {Record<string, unknown>} */
121
123
  const sorted = {};
122
124
  keys.sort();
123
125
  for (const key of keys) {
@@ -198,7 +200,7 @@ function sortKeys(value) {
198
200
 
199
201
  // Plain objects: sort keys and recursively process values
200
202
  if (isPlainObject(value)) {
201
- return sortPlainObject(value);
203
+ return sortPlainObject(/** @type {Record<string, unknown>} */ (value));
202
204
  }
203
205
 
204
206
  // Map instances: convert to sorted object
@@ -370,12 +372,18 @@ export function decode(buffer) {
370
372
  * @extends CodecPort
371
373
  */
372
374
  export class CborCodec extends CodecPort {
373
- /** @inheritdoc */
375
+ /**
376
+ * @param {unknown} data
377
+ * @returns {Buffer|Uint8Array}
378
+ */
374
379
  encode(data) {
375
380
  return encode(data);
376
381
  }
377
382
 
378
- /** @inheritdoc */
383
+ /**
384
+ * @param {Buffer|Uint8Array} buffer
385
+ * @returns {unknown}
386
+ */
379
387
  decode(buffer) {
380
388
  return decode(buffer);
381
389
  }
@@ -10,7 +10,7 @@
10
10
  export default class BlobPort {
11
11
  /**
12
12
  * Writes content as a Git blob and returns its OID.
13
- * @param {Buffer|string} content - The blob content to write
13
+ * @param {Buffer|string} _content - The blob content to write
14
14
  * @returns {Promise<string>} The Git OID of the created blob
15
15
  * @throws {Error} If not implemented by a concrete adapter
16
16
  */
@@ -20,7 +20,7 @@ export default class BlobPort {
20
20
 
21
21
  /**
22
22
  * Reads the content of a Git blob.
23
- * @param {string} oid - The blob OID to read
23
+ * @param {string} _oid - The blob OID to read
24
24
  * @returns {Promise<Buffer>} The blob content
25
25
  * @throws {Error} If not implemented by a concrete adapter
26
26
  */
@@ -7,7 +7,7 @@
7
7
  export default class CodecPort {
8
8
  /**
9
9
  * Encodes data to binary format.
10
- * @param {unknown} data - Data to encode
10
+ * @param {unknown} _data - Data to encode
11
11
  * @returns {Buffer|Uint8Array} Encoded bytes
12
12
  */
13
13
  encode(_data) {
@@ -16,7 +16,7 @@ export default class CodecPort {
16
16
 
17
17
  /**
18
18
  * Decodes binary data back to a JavaScript value.
19
- * @param {Buffer|Uint8Array} bytes - Encoded bytes to decode
19
+ * @param {Buffer|Uint8Array} _bytes - Encoded bytes to decode
20
20
  * @returns {unknown} Decoded value
21
21
  */
22
22
  decode(_bytes) {
@@ -10,10 +10,7 @@
10
10
  export default class CommitPort {
11
11
  /**
12
12
  * Creates a commit pointing to the empty tree.
13
- * @param {Object} options
14
- * @param {string} options.message - The commit message (typically CBOR-encoded patch data)
15
- * @param {string[]} [options.parents=[]] - Parent commit SHAs for the commit graph
16
- * @param {boolean} [options.sign=false] - Whether to GPG-sign the commit
13
+ * @param {{ message: string, parents?: string[], sign?: boolean }} _options
17
14
  * @returns {Promise<string>} The SHA of the created commit
18
15
  * @throws {Error} If not implemented by a concrete adapter
19
16
  */
@@ -23,7 +20,7 @@ export default class CommitPort {
23
20
 
24
21
  /**
25
22
  * Retrieves the raw commit message for a given SHA.
26
- * @param {string} sha - The commit SHA to read
23
+ * @param {string} _sha - The commit SHA to read
27
24
  * @returns {Promise<string>} The raw commit message content
28
25
  * @throws {Error} If not implemented by a concrete adapter
29
26
  */
@@ -33,7 +30,7 @@ export default class CommitPort {
33
30
 
34
31
  /**
35
32
  * Gets full commit metadata for a node.
36
- * @param {string} sha - The commit SHA to retrieve
33
+ * @param {string} _sha - The commit SHA to retrieve
37
34
  * @returns {Promise<{sha: string, message: string, author: string, date: string, parents: string[]}>}
38
35
  * Full commit metadata including SHA, message, author, date, and parent SHAs
39
36
  * @throws {Error} If not implemented by a concrete adapter
@@ -44,10 +41,7 @@ export default class CommitPort {
44
41
 
45
42
  /**
46
43
  * Returns raw git log output for a ref.
47
- * @param {Object} options
48
- * @param {string} options.ref - The Git ref to log from
49
- * @param {number} [options.limit=50] - Maximum number of commits to return
50
- * @param {string} [options.format] - Custom format string for git log
44
+ * @param {{ ref: string, limit?: number, format?: string }} _options
51
45
  * @returns {Promise<string>} The raw log output
52
46
  * @throws {Error} If not implemented by a concrete adapter
53
47
  */
@@ -57,10 +51,7 @@ export default class CommitPort {
57
51
 
58
52
  /**
59
53
  * Streams git log output for a ref.
60
- * @param {Object} options
61
- * @param {string} options.ref - The Git ref to log from
62
- * @param {number} [options.limit=1000000] - Maximum number of commits to return
63
- * @param {string} [options.format] - Custom format string for git log
54
+ * @param {{ ref: string, limit?: number, format?: string }} _options
64
55
  * @returns {Promise<import('node:stream').Readable>} A readable stream of log output
65
56
  * @throws {Error} If not implemented by a concrete adapter
66
57
  */
@@ -70,7 +61,7 @@ export default class CommitPort {
70
61
 
71
62
  /**
72
63
  * Counts nodes reachable from a ref without loading them into memory.
73
- * @param {string} ref - Git ref to count from (e.g., 'HEAD', 'main', or a SHA)
64
+ * @param {string} _ref - Git ref to count from (e.g., 'HEAD', 'main', or a SHA)
74
65
  * @returns {Promise<number>} The count of reachable nodes
75
66
  * @throws {Error} If not implemented by a concrete adapter
76
67
  */
@@ -81,11 +72,7 @@ export default class CommitPort {
81
72
  /**
82
73
  * Creates a commit pointing to a specified tree (not the empty tree).
83
74
  * Used by CheckpointService and PatchBuilderV2 for tree-backed commits.
84
- * @param {Object} options
85
- * @param {string} options.treeOid - The tree OID to commit
86
- * @param {string[]} [options.parents=[]] - Parent commit SHAs
87
- * @param {string} options.message - The commit message
88
- * @param {boolean} [options.sign=false] - Whether to GPG-sign the commit
75
+ * @param {{ treeOid: string, parents?: string[], message: string, sign?: boolean }} _options
89
76
  * @returns {Promise<string>} The SHA of the created commit
90
77
  * @throws {Error} If not implemented by a concrete adapter
91
78
  */
@@ -95,7 +82,7 @@ export default class CommitPort {
95
82
 
96
83
  /**
97
84
  * Checks whether a commit exists in the repository.
98
- * @param {string} sha - The commit SHA to check
85
+ * @param {string} _sha - The commit SHA to check
99
86
  * @returns {Promise<boolean>} True if the commit exists
100
87
  * @throws {Error} If not implemented by a concrete adapter
101
88
  */
@@ -10,7 +10,7 @@
10
10
  export default class ConfigPort {
11
11
  /**
12
12
  * Reads a git config value.
13
- * @param {string} key - The config key to read (e.g., 'warp.writerId.events')
13
+ * @param {string} _key - The config key to read (e.g., 'warp.writerId.events')
14
14
  * @returns {Promise<string|null>} The config value, or null if not set
15
15
  * @throws {Error} If not implemented by a concrete adapter
16
16
  */
@@ -20,8 +20,8 @@ export default class ConfigPort {
20
20
 
21
21
  /**
22
22
  * Sets a git config value.
23
- * @param {string} key - The config key to set (e.g., 'warp.writerId.events')
24
- * @param {string} value - The value to set
23
+ * @param {string} _key - The config key to set (e.g., 'warp.writerId.events')
24
+ * @param {string} _value - The value to set
25
25
  * @returns {Promise<void>}
26
26
  * @throws {Error} If not implemented by a concrete adapter
27
27
  */
@@ -7,8 +7,8 @@
7
7
  export default class CryptoPort {
8
8
  /**
9
9
  * Computes a hash digest of the given data.
10
- * @param {string} algorithm - Hash algorithm (e.g. 'sha1', 'sha256')
11
- * @param {string|Buffer|Uint8Array} data - Data to hash
10
+ * @param {string} _algorithm - Hash algorithm (e.g. 'sha1', 'sha256')
11
+ * @param {string|Buffer|Uint8Array} _data - Data to hash
12
12
  * @returns {Promise<string>} Hex-encoded digest
13
13
  */
14
14
  async hash(_algorithm, _data) {
@@ -17,9 +17,9 @@ export default class CryptoPort {
17
17
 
18
18
  /**
19
19
  * Computes an HMAC of the given data.
20
- * @param {string} algorithm - Hash algorithm (e.g. 'sha256')
21
- * @param {string|Buffer|Uint8Array} key - HMAC key
22
- * @param {string|Buffer|Uint8Array} data - Data to authenticate
20
+ * @param {string} _algorithm - Hash algorithm (e.g. 'sha256')
21
+ * @param {string|Buffer|Uint8Array} _key - HMAC key
22
+ * @param {string|Buffer|Uint8Array} _data - Data to authenticate
23
23
  * @returns {Promise<Buffer|Uint8Array>} Raw HMAC digest
24
24
  */
25
25
  async hmac(_algorithm, _key, _data) {
@@ -28,8 +28,8 @@ export default class CryptoPort {
28
28
 
29
29
  /**
30
30
  * Constant-time comparison of two buffers.
31
- * @param {Buffer|Uint8Array} a - First buffer
32
- * @param {Buffer|Uint8Array} b - Second buffer
31
+ * @param {Buffer|Uint8Array} _a - First buffer
32
+ * @param {Buffer|Uint8Array} _b - Second buffer
33
33
  * @returns {boolean} True if buffers are equal
34
34
  */
35
35
  timingSafeEqual(_a, _b) {
@@ -1,3 +1,9 @@
1
+ import CommitPort from './CommitPort.js';
2
+ import BlobPort from './BlobPort.js';
3
+ import TreePort from './TreePort.js';
4
+ import RefPort from './RefPort.js';
5
+ import ConfigPort from './ConfigPort.js';
6
+
1
7
  /**
2
8
  * Abstract port for graph persistence operations.
3
9
  *
@@ -20,27 +26,19 @@
20
26
  * All methods throw by default and must be overridden by implementations.
21
27
  *
22
28
  * @abstract
23
- * @implements {CommitPort}
24
- * @implements {BlobPort}
25
- * @implements {TreePort}
26
- * @implements {RefPort}
27
- * @implements {ConfigPort}
28
29
  */
29
-
30
- import CommitPort from './CommitPort.js';
31
- import BlobPort from './BlobPort.js';
32
- import TreePort from './TreePort.js';
33
- import RefPort from './RefPort.js';
34
- import ConfigPort from './ConfigPort.js';
35
-
36
30
  class GraphPersistencePort {}
37
31
 
32
+ /** @type {Array<typeof CommitPort | typeof BlobPort | typeof TreePort | typeof RefPort | typeof ConfigPort>} */
38
33
  const focusedPorts = [CommitPort, BlobPort, TreePort, RefPort, ConfigPort];
39
34
  const seen = new Map();
40
35
 
41
36
  for (const Port of focusedPorts) {
42
- const descriptors = Object.getOwnPropertyDescriptors(Port.prototype);
43
- delete descriptors.constructor;
37
+ const allDescriptors = Object.getOwnPropertyDescriptors(Port.prototype);
38
+ /** @type {Record<string, PropertyDescriptor>} */
39
+ const descriptors = Object.fromEntries(
40
+ Object.entries(allDescriptors).filter(([k]) => k !== 'constructor'),
41
+ );
44
42
 
45
43
  for (const [name, descriptor] of Object.entries(descriptors)) {
46
44
  if (seen.has(name)) {
@@ -12,11 +12,7 @@ export default class HttpServerPort {
12
12
  * and must return `{ status, headers, body }`. No raw req/res objects
13
13
  * are exposed to the domain.
14
14
  *
15
- * @param {Function} requestHandler - Async function (request) => response
16
- * @param {string} requestHandler.method - HTTP method
17
- * @param {string} requestHandler.url - Request URL
18
- * @param {Object} requestHandler.headers - Request headers (lowercased keys)
19
- * @param {Buffer|undefined} requestHandler.body - Request body (undefined if none)
15
+ * @param {(request: { method: string, url: string, headers: Object, body: Buffer|undefined }) => Promise<{ status: number, headers: Object, body: string|Buffer }>} _requestHandler - Async function (request) => response
20
16
  * @returns {{ listen: Function, close: Function, address: Function }} Server with listen(port, [host], cb(err)), close(cb), and address()
21
17
  */
22
18
  createServer(_requestHandler) {
@@ -18,6 +18,7 @@ import RefPort from './RefPort.js';
18
18
 
19
19
  class IndexStoragePort {}
20
20
 
21
+ /** @type {Array<[{ prototype: object, name: string }, string[]]>} */
21
22
  const picks = [
22
23
  [BlobPort, ['writeBlob', 'readBlob']],
23
24
  [TreePort, ['writeTree', 'readTreeOids']],
@@ -13,8 +13,8 @@
13
13
  export default class LoggerPort {
14
14
  /**
15
15
  * Log a debug-level message.
16
- * @param {string} message - The log message
17
- * @param {Record<string, unknown>} [context] - Structured metadata
16
+ * @param {string} _message - The log message
17
+ * @param {Record<string, unknown>} [_context] - Structured metadata
18
18
  * @returns {void}
19
19
  * @abstract
20
20
  */
@@ -24,8 +24,8 @@ export default class LoggerPort {
24
24
 
25
25
  /**
26
26
  * Log an info-level message.
27
- * @param {string} message - The log message
28
- * @param {Record<string, unknown>} [context] - Structured metadata
27
+ * @param {string} _message - The log message
28
+ * @param {Record<string, unknown>} [_context] - Structured metadata
29
29
  * @returns {void}
30
30
  * @abstract
31
31
  */
@@ -35,8 +35,8 @@ export default class LoggerPort {
35
35
 
36
36
  /**
37
37
  * Log a warning-level message.
38
- * @param {string} message - The log message
39
- * @param {Record<string, unknown>} [context] - Structured metadata
38
+ * @param {string} _message - The log message
39
+ * @param {Record<string, unknown>} [_context] - Structured metadata
40
40
  * @returns {void}
41
41
  * @abstract
42
42
  */
@@ -46,8 +46,8 @@ export default class LoggerPort {
46
46
 
47
47
  /**
48
48
  * Log an error-level message.
49
- * @param {string} message - The log message
50
- * @param {Record<string, unknown>} [context] - Structured metadata
49
+ * @param {string} _message - The log message
50
+ * @param {Record<string, unknown>} [_context] - Structured metadata
51
51
  * @returns {void}
52
52
  * @abstract
53
53
  */
@@ -58,7 +58,7 @@ export default class LoggerPort {
58
58
  /**
59
59
  * Create a child logger with additional base context.
60
60
  * Child loggers inherit parent context and merge with their own.
61
- * @param {Record<string, unknown>} context - Base context for the child logger
61
+ * @param {Record<string, unknown>} _context - Base context for the child logger
62
62
  * @returns {LoggerPort} A new logger instance with merged context
63
63
  * @abstract
64
64
  */
@@ -10,8 +10,8 @@
10
10
  export default class RefPort {
11
11
  /**
12
12
  * Updates a ref to point to an OID.
13
- * @param {string} ref - The ref name (e.g., 'refs/warp/events/writers/alice')
14
- * @param {string} oid - The OID to point to
13
+ * @param {string} _ref - The ref name (e.g., 'refs/warp/events/writers/alice')
14
+ * @param {string} _oid - The OID to point to
15
15
  * @returns {Promise<void>}
16
16
  * @throws {Error} If not implemented by a concrete adapter
17
17
  */
@@ -21,7 +21,7 @@ export default class RefPort {
21
21
 
22
22
  /**
23
23
  * Reads the OID a ref points to.
24
- * @param {string} ref - The ref name
24
+ * @param {string} _ref - The ref name
25
25
  * @returns {Promise<string|null>} The OID, or null if the ref does not exist
26
26
  * @throws {Error} If not implemented by a concrete adapter
27
27
  */
@@ -31,7 +31,7 @@ export default class RefPort {
31
31
 
32
32
  /**
33
33
  * Deletes a ref.
34
- * @param {string} ref - The ref name to delete
34
+ * @param {string} _ref - The ref name to delete
35
35
  * @returns {Promise<void>}
36
36
  * @throws {Error} If not implemented by a concrete adapter
37
37
  */
@@ -41,7 +41,7 @@ export default class RefPort {
41
41
 
42
42
  /**
43
43
  * Lists refs matching a prefix.
44
- * @param {string} prefix - The ref prefix to match (e.g., 'refs/warp/events/writers/')
44
+ * @param {string} _prefix - The ref prefix to match (e.g., 'refs/warp/events/writers/')
45
45
  * @returns {Promise<string[]>} Array of matching ref names
46
46
  * @throws {Error} If not implemented by a concrete adapter
47
47
  */
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Port interface for seek materialization cache operations.
3
+ *
4
+ * Defines the contract for caching and retrieving serialized WarpStateV5
5
+ * snapshots keyed by (ceiling, frontier) tuples. Used by the seek time-travel
6
+ * feature to avoid full re-materialization for previously-visited ticks.
7
+ *
8
+ * Concrete adapters (e.g., CasSeekCacheAdapter) implement this interface
9
+ * to store cached states in different backends (git-cas, filesystem, etc.).
10
+ *
11
+ * @abstract
12
+ */
13
+ export default class SeekCachePort {
14
+ /**
15
+ * Retrieves a cached state buffer by key.
16
+ * @param {string} _key - Cache key (e.g., 'v1:t42-<frontierHash>')
17
+ * @returns {Promise<Buffer|null>} The cached buffer, or null on miss
18
+ * @throws {Error} If not implemented by a concrete adapter
19
+ */
20
+ async get(_key) {
21
+ throw new Error('SeekCachePort.get() not implemented');
22
+ }
23
+
24
+ /**
25
+ * Stores a state buffer under the given key.
26
+ * @param {string} _key - Cache key
27
+ * @param {Buffer} _buffer - Serialized state to cache
28
+ * @returns {Promise<void>}
29
+ * @throws {Error} If not implemented by a concrete adapter
30
+ */
31
+ async set(_key, _buffer) {
32
+ throw new Error('SeekCachePort.set() not implemented');
33
+ }
34
+
35
+ /**
36
+ * Checks whether a key exists in the cache.
37
+ * @param {string} _key - Cache key
38
+ * @returns {Promise<boolean>}
39
+ * @throws {Error} If not implemented by a concrete adapter
40
+ */
41
+ async has(_key) {
42
+ throw new Error('SeekCachePort.has() not implemented');
43
+ }
44
+
45
+ /**
46
+ * Lists all keys currently in the cache index.
47
+ * Note: keys may reference GC'd blobs; callers should handle miss on get().
48
+ * @returns {Promise<string[]>}
49
+ * @throws {Error} If not implemented by a concrete adapter
50
+ */
51
+ async keys() {
52
+ throw new Error('SeekCachePort.keys() not implemented');
53
+ }
54
+
55
+ /**
56
+ * Removes a single entry from the cache.
57
+ * @param {string} _key - Cache key to remove
58
+ * @returns {Promise<boolean>} True if the entry existed and was removed
59
+ * @throws {Error} If not implemented by a concrete adapter
60
+ */
61
+ async delete(_key) {
62
+ throw new Error('SeekCachePort.delete() not implemented');
63
+ }
64
+
65
+ /**
66
+ * Removes all entries from the cache.
67
+ * @returns {Promise<void>}
68
+ * @throws {Error} If not implemented by a concrete adapter
69
+ */
70
+ async clear() {
71
+ throw new Error('SeekCachePort.clear() not implemented');
72
+ }
73
+ }
@@ -10,7 +10,7 @@
10
10
  export default class TreePort {
11
11
  /**
12
12
  * Creates a Git tree from mktree-formatted entries.
13
- * @param {string[]} entries - Lines in git mktree format (e.g., "100644 blob <oid>\t<path>")
13
+ * @param {string[]} _entries - Lines in git mktree format (e.g., "100644 blob <oid>\t<path>")
14
14
  * @returns {Promise<string>} The Git OID of the created tree
15
15
  * @throws {Error} If not implemented by a concrete adapter
16
16
  */
@@ -20,7 +20,7 @@ export default class TreePort {
20
20
 
21
21
  /**
22
22
  * Reads a tree and returns a map of path to content.
23
- * @param {string} treeOid - The tree OID to read
23
+ * @param {string} _treeOid - The tree OID to read
24
24
  * @returns {Promise<Record<string, Buffer>>} Map of file path to blob content
25
25
  * @throws {Error} If not implemented by a concrete adapter
26
26
  */
@@ -31,7 +31,7 @@ export default class TreePort {
31
31
  /**
32
32
  * Reads a tree and returns a map of path to blob OID.
33
33
  * Useful for lazy-loading shards without reading all blob contents.
34
- * @param {string} treeOid - The tree OID to read
34
+ * @param {string} _treeOid - The tree OID to read
35
35
  * @returns {Promise<Record<string, string>>} Map of file path to blob OID
36
36
  * @throws {Error} If not implemented by a concrete adapter
37
37
  */
@@ -6,13 +6,19 @@
6
6
  * { nodes: [{ id, label, props? }], edges: [{ from, to, label? }] }
7
7
  */
8
8
 
9
+ /**
10
+ * @typedef {{ id: string, label: string, props?: Record<string, any> }} GraphDataNode
11
+ * @typedef {{ from: string, to: string, label?: string }} GraphDataEdge
12
+ * @typedef {{ nodes: GraphDataNode[], edges: GraphDataEdge[] }} GraphData
13
+ */
14
+
9
15
  /**
10
16
  * Converts a query result payload + edge array into graph data.
11
17
  * Edges are filtered to only those connecting matched nodes.
12
18
  *
13
- * @param {Object} payload - Query result { nodes: [{id, props}] }
14
- * @param {Array} edges - Edge array from graph.getEdges()
15
- * @returns {{ nodes: Array, edges: Array }}
19
+ * @param {{ nodes?: Array<{ id: string, props?: Record<string, any> }> } | null} payload - Query result
20
+ * @param {Array<{ from: string, to: string, label?: string }>} edges - Edge array from graph.getEdges()
21
+ * @returns {GraphData}
16
22
  */
17
23
  export function queryResultToGraphData(payload, edges) {
18
24
  const nodes = (payload?.nodes ?? []).map((n) => ({
@@ -34,8 +40,8 @@ export function queryResultToGraphData(payload, edges) {
34
40
  * Converts a path result payload into graph data.
35
41
  * Builds a linear chain of nodes with labelled edges.
36
42
  *
37
- * @param {Object} payload - Path result { path: string[], edges?: string[] }
38
- * @returns {{ nodes: Array, edges: Array }}
43
+ * @param {{ path?: string[], edges?: string[] } | null} payload - Path result
44
+ * @returns {GraphData}
39
45
  */
40
46
  export function pathResultToGraphData(payload) {
41
47
  const pathArr = payload?.path ?? [];
@@ -43,6 +49,7 @@ export function pathResultToGraphData(payload) {
43
49
 
44
50
  const nodes = pathArr.map((id) => ({ id, label: id }));
45
51
 
52
+ /** @type {GraphDataEdge[]} */
46
53
  const edges = [];
47
54
  for (let i = 0; i < pathArr.length - 1; i++) {
48
55
  edges.push({
@@ -59,8 +66,8 @@ export function pathResultToGraphData(payload) {
59
66
  * Converts raw getNodes() + getEdges() output into graph data.
60
67
  *
61
68
  * @param {string[]} nodeIds - Array of node IDs
62
- * @param {Array} edges - Edge array from graph.getEdges()
63
- * @returns {{ nodes: Array, edges: Array }}
69
+ * @param {Array<{ from: string, to: string, label?: string }>} edges - Edge array from graph.getEdges()
70
+ * @returns {GraphData}
64
71
  */
65
72
  export function rawGraphToGraphData(nodeIds, edges) {
66
73
  const nodes = (nodeIds ?? []).map((id) => ({ id, label: id }));