@git-stunts/git-warp 12.2.1 → 12.4.1
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.
- package/README.md +5 -5
- package/bin/cli/commands/info.js +1 -5
- package/bin/cli/infrastructure.js +6 -9
- package/bin/cli/shared.js +8 -0
- package/bin/presenters/text.js +10 -3
- package/bin/warp-graph.js +6 -6
- package/package.json +1 -1
- package/src/domain/WarpGraph.js +5 -35
- package/src/domain/crdt/ORSet.js +3 -0
- package/src/domain/crdt/VersionVector.js +1 -1
- package/src/domain/entities/GraphNode.js +1 -6
- package/src/domain/errors/ForkError.js +1 -1
- package/src/domain/errors/IndexError.js +1 -1
- package/src/domain/errors/OperationAbortedError.js +1 -1
- package/src/domain/errors/PatchError.js +1 -1
- package/src/domain/errors/PersistenceError.js +45 -0
- package/src/domain/errors/QueryError.js +1 -1
- package/src/domain/errors/SchemaUnsupportedError.js +1 -1
- package/src/domain/errors/SyncError.js +1 -1
- package/src/domain/errors/TraversalError.js +1 -1
- package/src/domain/errors/TrustError.js +1 -1
- package/src/domain/errors/WormholeError.js +1 -1
- package/src/domain/errors/index.js +1 -0
- package/src/domain/services/AdjacencyNeighborProvider.js +1 -4
- package/src/domain/services/AnchorMessageCodec.js +1 -3
- package/src/domain/services/AuditMessageCodec.js +1 -5
- package/src/domain/services/AuditReceiptService.js +4 -18
- package/src/domain/services/AuditVerifierService.js +3 -7
- package/src/domain/services/BitmapIndexBuilder.js +6 -12
- package/src/domain/services/BitmapIndexReader.js +7 -20
- package/src/domain/services/BitmapNeighborProvider.js +1 -3
- package/src/domain/services/BoundaryTransitionRecord.js +7 -23
- package/src/domain/services/CheckpointMessageCodec.js +6 -6
- package/src/domain/services/CheckpointSerializerV5.js +8 -12
- package/src/domain/services/CheckpointService.js +28 -40
- package/src/domain/services/CommitDagTraversalService.js +1 -3
- package/src/domain/services/DagPathFinding.js +9 -59
- package/src/domain/services/DagTopology.js +4 -16
- package/src/domain/services/DagTraversal.js +7 -31
- package/src/domain/services/Frontier.js +4 -6
- package/src/domain/services/GitLogParser.js +1 -2
- package/src/domain/services/GraphTraversal.js +14 -114
- package/src/domain/services/HealthCheckService.js +3 -9
- package/src/domain/services/HookInstaller.js +2 -8
- package/src/domain/services/HttpSyncServer.js +24 -25
- package/src/domain/services/IncrementalIndexUpdater.js +4 -6
- package/src/domain/services/IndexRebuildService.js +6 -52
- package/src/domain/services/IndexStalenessChecker.js +2 -3
- package/src/domain/services/JoinReducer.js +200 -100
- package/src/domain/services/KeyCodec.js +48 -0
- package/src/domain/services/LogicalBitmapIndexBuilder.js +1 -2
- package/src/domain/services/LogicalIndexBuildService.js +2 -6
- package/src/domain/services/LogicalIndexReader.js +1 -2
- package/src/domain/services/LogicalTraversal.js +13 -64
- package/src/domain/services/MaterializedViewService.js +5 -19
- package/src/domain/services/MessageSchemaDetector.js +35 -5
- package/src/domain/services/MigrationService.js +1 -4
- package/src/domain/services/ObserverView.js +1 -7
- package/src/domain/services/OpNormalizer.js +79 -0
- package/src/domain/services/PatchBuilderV2.js +67 -38
- package/src/domain/services/PatchMessageCodec.js +1 -6
- package/src/domain/services/PropertyIndexBuilder.js +1 -2
- package/src/domain/services/PropertyIndexReader.js +1 -4
- package/src/domain/services/ProvenanceIndex.js +5 -7
- package/src/domain/services/ProvenancePayload.js +1 -1
- package/src/domain/services/QueryBuilder.js +3 -16
- package/src/domain/services/StateDiff.js +3 -9
- package/src/domain/services/StateSerializerV5.js +10 -10
- package/src/domain/services/StreamingBitmapIndexBuilder.js +13 -41
- package/src/domain/services/SyncAuthService.js +8 -32
- package/src/domain/services/SyncController.js +5 -25
- package/src/domain/services/SyncProtocol.js +10 -13
- package/src/domain/services/SyncTrustGate.js +4 -9
- package/src/domain/services/TemporalQuery.js +9 -27
- package/src/domain/services/TranslationCost.js +2 -8
- package/src/domain/services/WarpMessageCodec.js +2 -0
- package/src/domain/services/WarpStateIndexBuilder.js +2 -4
- package/src/domain/services/WormholeService.js +9 -25
- package/src/domain/trust/TrustCrypto.js +9 -10
- package/src/domain/trust/TrustEvaluator.js +1 -8
- package/src/domain/trust/TrustRecordService.js +5 -10
- package/src/domain/types/TickReceipt.js +9 -11
- package/src/domain/types/WarpTypes.js +1 -5
- package/src/domain/types/WarpTypesV2.js +78 -13
- package/src/domain/utils/CachedValue.js +1 -4
- package/src/domain/utils/MinHeap.js +3 -3
- package/src/domain/utils/RefLayout.js +26 -0
- package/src/domain/utils/WriterId.js +2 -7
- package/src/domain/utils/canonicalCbor.js +1 -1
- package/src/domain/utils/defaultClock.js +1 -0
- package/src/domain/utils/defaultCodec.js +1 -1
- package/src/domain/utils/parseCursorBlob.js +4 -4
- package/src/domain/warp/PatchSession.js +3 -8
- package/src/domain/warp/Writer.js +9 -12
- package/src/domain/warp/_wire.js +2 -2
- package/src/domain/warp/_wiredMethods.d.ts +5 -7
- package/src/domain/warp/checkpoint.methods.js +1 -1
- package/src/domain/warp/fork.methods.js +2 -6
- package/src/domain/warp/materializeAdvanced.methods.js +3 -3
- package/src/domain/warp/patch.methods.js +8 -8
- package/src/domain/warp/provenance.methods.js +5 -5
- package/src/domain/warp/query.methods.js +9 -18
- package/src/domain/warp/subscribe.methods.js +2 -8
- package/src/globals.d.ts +7 -0
- package/src/infrastructure/adapters/BunHttpAdapter.js +14 -18
- package/src/infrastructure/adapters/ConsoleLogger.js +2 -9
- package/src/infrastructure/adapters/DenoHttpAdapter.js +15 -15
- package/src/infrastructure/adapters/GitGraphAdapter.js +234 -58
- package/src/infrastructure/adapters/InMemoryGraphAdapter.js +9 -2
- package/src/infrastructure/adapters/NodeHttpAdapter.js +14 -14
- package/src/infrastructure/adapters/WebCryptoAdapter.js +1 -2
- package/src/ports/BlobPort.js +2 -2
- package/src/ports/HttpServerPort.js +24 -2
- package/src/ports/RefPort.js +2 -1
- package/src/visualization/renderers/ascii/box.js +1 -1
- package/src/visualization/renderers/ascii/check.js +1 -5
- package/src/visualization/renderers/ascii/history.js +1 -6
- package/src/visualization/renderers/ascii/path.js +4 -22
- package/src/visualization/renderers/ascii/progress.js +1 -4
- package/src/visualization/renderers/ascii/seek.js +1 -5
- package/src/visualization/renderers/ascii/table.js +1 -3
|
@@ -29,7 +29,7 @@ const DEFAULT_MAX_CACHED_SHARDS = 100;
|
|
|
29
29
|
* Computes a SHA-256 checksum of the given data.
|
|
30
30
|
* Used to verify shard integrity on load.
|
|
31
31
|
*
|
|
32
|
-
* @param {
|
|
32
|
+
* @param {Record<string, unknown>} data - The data object to checksum
|
|
33
33
|
* @param {number} version - Shard version (1 uses JSON.stringify, 2+ uses canonicalStringify)
|
|
34
34
|
* @param {import('../../ports/CryptoPort.js').default} crypto - CryptoPort instance
|
|
35
35
|
* @returns {Promise<string>} Hex-encoded SHA-256 hash
|
|
@@ -82,16 +82,9 @@ const computeChecksum = async (data, version, crypto) => {
|
|
|
82
82
|
export default class BitmapIndexReader {
|
|
83
83
|
/**
|
|
84
84
|
* Creates a BitmapIndexReader instance.
|
|
85
|
-
* @param {
|
|
86
|
-
* @param {IndexStoragePort} options.storage - Storage adapter for reading index data
|
|
87
|
-
* @param {boolean} [options.strict=true] - If true, throw errors on validation failures; if false, log warnings and return empty shards
|
|
88
|
-
* @param {import('../../ports/LoggerPort.js').default} [options.logger] - Logger for structured logging.
|
|
89
|
-
* Defaults to NoOpLogger (no logging).
|
|
90
|
-
* @param {number} [options.maxCachedShards=100] - Maximum number of shards to keep in the LRU cache.
|
|
91
|
-
* When exceeded, least recently used shards are evicted to free memory.
|
|
92
|
-
* @param {import('../../ports/CryptoPort.js').default} [options.crypto] - CryptoPort instance for checksum verification.
|
|
85
|
+
* @param {{ storage: IndexStoragePort, strict?: boolean, logger?: import('../../ports/LoggerPort.js').default, maxCachedShards?: number, crypto?: import('../../ports/CryptoPort.js').default }} options
|
|
93
86
|
*/
|
|
94
|
-
constructor({ storage, strict = true, logger = nullLogger, maxCachedShards = DEFAULT_MAX_CACHED_SHARDS, crypto }
|
|
87
|
+
constructor({ storage, strict = true, logger = nullLogger, maxCachedShards = DEFAULT_MAX_CACHED_SHARDS, crypto }) {
|
|
95
88
|
if (!storage) {
|
|
96
89
|
throw new Error('BitmapIndexReader requires a storage adapter');
|
|
97
90
|
}
|
|
@@ -321,10 +314,7 @@ export default class BitmapIndexReader {
|
|
|
321
314
|
/**
|
|
322
315
|
* Handles validation/corruption errors based on strict mode.
|
|
323
316
|
* @param {ShardCorruptionError|ShardValidationError} err - The error to handle
|
|
324
|
-
* @param {
|
|
325
|
-
* @param {string} context.path - Shard path
|
|
326
|
-
* @param {string} context.oid - Object ID
|
|
327
|
-
* @param {string} context.format - 'json' or 'bitmap'
|
|
317
|
+
* @param {{ path: string, oid: string, format: string }} context - Error context
|
|
328
318
|
* @returns {Record<string, string | number> | import('../utils/roaring.js').RoaringBitmapSubset} Empty shard (non-strict mode only)
|
|
329
319
|
* @throws {ShardCorruptionError|ShardValidationError} In strict mode
|
|
330
320
|
* @private
|
|
@@ -356,7 +346,7 @@ export default class BitmapIndexReader {
|
|
|
356
346
|
|
|
357
347
|
/**
|
|
358
348
|
* Parses and validates a shard buffer.
|
|
359
|
-
* @param {
|
|
349
|
+
* @param {Uint8Array} buffer - Raw shard buffer
|
|
360
350
|
* @param {string} path - Shard path (for error context)
|
|
361
351
|
* @param {string} oid - Object ID (for error context)
|
|
362
352
|
* @returns {Promise<Record<string, string | number>>} The validated data from the shard
|
|
@@ -373,7 +363,7 @@ export default class BitmapIndexReader {
|
|
|
373
363
|
* Loads raw buffer from storage.
|
|
374
364
|
* @param {string} path - Shard path
|
|
375
365
|
* @param {string} oid - Object ID
|
|
376
|
-
* @returns {Promise<
|
|
366
|
+
* @returns {Promise<Uint8Array>} Raw buffer
|
|
377
367
|
* @throws {ShardLoadError} When storage.readBlob fails
|
|
378
368
|
* @private
|
|
379
369
|
*/
|
|
@@ -413,10 +403,7 @@ export default class BitmapIndexReader {
|
|
|
413
403
|
* Attempts to handle a shard error based on its type.
|
|
414
404
|
* Returns handled result for validation/corruption errors, null otherwise.
|
|
415
405
|
* @param {unknown} err - The error to handle
|
|
416
|
-
* @param {
|
|
417
|
-
* @param {string} context.path - Shard path
|
|
418
|
-
* @param {string} context.oid - Object ID
|
|
419
|
-
* @param {string} context.format - 'json' or 'bitmap'
|
|
406
|
+
* @param {{ path: string, oid: string, format: string }} context - Error context
|
|
420
407
|
* @returns {Record<string, string | number> | import('../utils/roaring.js').RoaringBitmapSubset | null} Handled result or null if error should be re-thrown
|
|
421
408
|
* @private
|
|
422
409
|
*/
|
|
@@ -59,9 +59,7 @@ function dedupSorted(edges) {
|
|
|
59
59
|
|
|
60
60
|
export default class BitmapNeighborProvider extends NeighborProviderPort {
|
|
61
61
|
/**
|
|
62
|
-
* @param {
|
|
63
|
-
* @param {BitmapIndexReader} [params.indexReader] - For commit DAG mode
|
|
64
|
-
* @param {LogicalIndex} [params.logicalIndex] - For logical graph mode
|
|
62
|
+
* @param {{ indexReader?: BitmapIndexReader, logicalIndex?: LogicalIndex }} params
|
|
65
63
|
*/
|
|
66
64
|
constructor({ indexReader, logicalIndex }) {
|
|
67
65
|
super();
|
|
@@ -77,13 +77,7 @@ const BTR_VERSION = 1;
|
|
|
77
77
|
*
|
|
78
78
|
* This ensures all fields are covered and the encoding is deterministic.
|
|
79
79
|
*
|
|
80
|
-
* @param {
|
|
81
|
-
* @param {number} fields.version - BTR format version
|
|
82
|
-
* @param {string} fields.h_in - Hash of input state
|
|
83
|
-
* @param {string} fields.h_out - Hash of output state
|
|
84
|
-
* @param {Uint8Array} fields.U_0 - Serialized initial state
|
|
85
|
-
* @param {Array<unknown>} fields.P - Serialized provenance payload
|
|
86
|
-
* @param {string} fields.t - ISO timestamp
|
|
80
|
+
* @param {{ version: number, h_in: string, h_out: string, U_0: Uint8Array, P: Array<unknown>, t: string }} fields - BTR fields to authenticate
|
|
87
81
|
* @param {string|Uint8Array} key - HMAC key
|
|
88
82
|
* @param {{ crypto: import('../../ports/CryptoPort.js').default, codec?: import('../../ports/CodecPort.js').default }} deps - Dependencies
|
|
89
83
|
* @returns {Promise<string>} Hex-encoded HMAC tag
|
|
@@ -151,11 +145,7 @@ async function computeHmac(fields, key, { crypto, codec }) {
|
|
|
151
145
|
*
|
|
152
146
|
* @param {import('./JoinReducer.js').WarpStateV5} initialState - The input state U_0
|
|
153
147
|
* @param {ProvenancePayload} payload - The provenance payload P
|
|
154
|
-
* @param {
|
|
155
|
-
* @param {string|Uint8Array} options.key - HMAC key for authentication
|
|
156
|
-
* @param {string} [options.timestamp] - ISO timestamp (defaults to now)
|
|
157
|
-
* @param {import('../../ports/CryptoPort.js').default} options.crypto - CryptoPort instance
|
|
158
|
-
* @param {import('../../ports/CodecPort.js').default} [options.codec] - Codec for serialization
|
|
148
|
+
* @param {{ key: string|Uint8Array, timestamp?: string, crypto: import('../../ports/CryptoPort.js').default, codec?: import('../../ports/CodecPort.js').default }} options - BTR creation options
|
|
159
149
|
* @returns {Promise<BTR>} The created BTR
|
|
160
150
|
* @throws {TypeError} If payload is not a ProvenancePayload
|
|
161
151
|
*/
|
|
@@ -164,6 +154,7 @@ export async function createBTR(initialState, payload, options) {
|
|
|
164
154
|
throw new TypeError('payload must be a ProvenancePayload');
|
|
165
155
|
}
|
|
166
156
|
|
|
157
|
+
// eslint-disable-next-line no-restricted-syntax -- wall-clock default for BTR timestamp
|
|
167
158
|
const { key, timestamp = new Date().toISOString(), crypto, codec } = options;
|
|
168
159
|
|
|
169
160
|
// Validate HMAC key is not empty/falsy
|
|
@@ -245,9 +236,7 @@ async function verifyHmac(btr, key, { crypto, codec }) {
|
|
|
245
236
|
* Verifies replay produces expected h_out.
|
|
246
237
|
*
|
|
247
238
|
* @param {BTR} btr - The BTR to verify
|
|
248
|
-
* @param {
|
|
249
|
-
* @param {import('../../ports/CryptoPort.js').default} [deps.crypto] - CryptoPort instance
|
|
250
|
-
* @param {import('../../ports/CodecPort.js').default} [deps.codec] - Codec
|
|
239
|
+
* @param {{ crypto?: import('../../ports/CryptoPort.js').default, codec?: import('../../ports/CodecPort.js').default }} [deps] - Dependencies
|
|
251
240
|
* @returns {Promise<string|null>} Error message if replay mismatch, null if valid
|
|
252
241
|
* @private
|
|
253
242
|
*/
|
|
@@ -275,10 +264,7 @@ async function verifyReplayHash(btr, { crypto, codec } = {}) {
|
|
|
275
264
|
*
|
|
276
265
|
* @param {BTR} btr - The BTR to verify
|
|
277
266
|
* @param {string|Uint8Array} key - HMAC key
|
|
278
|
-
* @param {
|
|
279
|
-
* @param {boolean} [options.verifyReplay=false] - Also verify replay produces h_out
|
|
280
|
-
* @param {import('../../ports/CryptoPort.js').default} [options.crypto] - CryptoPort instance
|
|
281
|
-
* @param {import('../../ports/CodecPort.js').default} [options.codec] - Codec for serialization
|
|
267
|
+
* @param {{ verifyReplay?: boolean, crypto?: import('../../ports/CryptoPort.js').default, codec?: import('../../ports/CodecPort.js').default }} [options] - Verification options
|
|
282
268
|
* @returns {Promise<VerificationResult>} Verification result with valid flag and optional reason
|
|
283
269
|
*/
|
|
284
270
|
export async function verifyBTR(btr, key, options = {}) {
|
|
@@ -367,8 +353,7 @@ function deserializeInitialState(U_0, { codec } = {}) {
|
|
|
367
353
|
* enabling byte-for-byte comparison of BTRs.
|
|
368
354
|
*
|
|
369
355
|
* @param {BTR} btr - The BTR to serialize
|
|
370
|
-
* @param {
|
|
371
|
-
* @param {import('../../ports/CodecPort.js').default} [options.codec] - Codec for serialization
|
|
356
|
+
* @param {{ codec?: import('../../ports/CodecPort.js').default }} [options]
|
|
372
357
|
* @returns {Uint8Array} CBOR-encoded BTR
|
|
373
358
|
*/
|
|
374
359
|
export function serializeBTR(btr, { codec } = {}) {
|
|
@@ -388,8 +373,7 @@ export function serializeBTR(btr, { codec } = {}) {
|
|
|
388
373
|
* Deserializes a BTR from CBOR bytes.
|
|
389
374
|
*
|
|
390
375
|
* @param {Uint8Array} bytes - CBOR-encoded BTR
|
|
391
|
-
* @param {
|
|
392
|
-
* @param {import('../../ports/CodecPort.js').default} [options.codec] - Codec for deserialization
|
|
376
|
+
* @param {{ codec?: import('../../ports/CodecPort.js').default }} [options]
|
|
393
377
|
* @returns {BTR} The deserialized BTR
|
|
394
378
|
* @throws {Error} If the bytes are not valid CBOR or missing required fields
|
|
395
379
|
*/
|
|
@@ -5,6 +5,11 @@
|
|
|
5
5
|
* materialized graph state. See {@link module:domain/services/WarpMessageCodec}
|
|
6
6
|
* for the facade that re-exports all codec functions.
|
|
7
7
|
*
|
|
8
|
+
* **Schema namespace note:** Checkpoint schema versions (2, 3, 4) are
|
|
9
|
+
* distinct from patch schema versions (PATCH_SCHEMA_V2, PATCH_SCHEMA_V3).
|
|
10
|
+
* See {@link module:domain/services/CheckpointService} for named constants
|
|
11
|
+
* `CHECKPOINT_SCHEMA_STANDARD` and `CHECKPOINT_SCHEMA_INDEX_TREE`.
|
|
12
|
+
*
|
|
8
13
|
* @module domain/services/CheckpointMessageCodec
|
|
9
14
|
*/
|
|
10
15
|
|
|
@@ -25,12 +30,7 @@ import {
|
|
|
25
30
|
/**
|
|
26
31
|
* Encodes a checkpoint commit message.
|
|
27
32
|
*
|
|
28
|
-
* @param {
|
|
29
|
-
* @param {string} options.graph - The graph name
|
|
30
|
-
* @param {string} options.stateHash - The SHA-256 hash of the materialized state
|
|
31
|
-
* @param {string} options.frontierOid - The OID of the frontier blob
|
|
32
|
-
* @param {string} options.indexOid - The OID of the index tree
|
|
33
|
-
* @param {number} [options.schema=2] - The schema version (defaults to 2 for new messages)
|
|
33
|
+
* @param {{ graph: string, stateHash: string, frontierOid: string, indexOid: string, schema?: number }} options - The checkpoint message options
|
|
34
34
|
* @returns {string} The encoded commit message
|
|
35
35
|
* @throws {Error} If any validation fails
|
|
36
36
|
*
|
|
@@ -35,9 +35,8 @@ import { createEmptyStateV5 } from './JoinReducer.js';
|
|
|
35
35
|
* }
|
|
36
36
|
*
|
|
37
37
|
* @param {import('./JoinReducer.js').WarpStateV5} state
|
|
38
|
-
* @param {
|
|
39
|
-
* @
|
|
40
|
-
* @returns {Buffer|Uint8Array} CBOR-encoded full state
|
|
38
|
+
* @param {{ codec?: import('../../ports/CodecPort.js').default }} [options]
|
|
39
|
+
* @returns {Uint8Array} CBOR-encoded full state
|
|
41
40
|
*/
|
|
42
41
|
export function serializeFullStateV5(state, { codec } = {}) {
|
|
43
42
|
const c = codec || defaultCodec;
|
|
@@ -84,9 +83,8 @@ export function serializeFullStateV5(state, { codec } = {}) {
|
|
|
84
83
|
/**
|
|
85
84
|
* Deserializes full V5 state. Used for resume.
|
|
86
85
|
*
|
|
87
|
-
* @param {
|
|
88
|
-
* @param {
|
|
89
|
-
* @param {import('../../ports/CodecPort.js').default} [options.codec] - Codec for deserialization
|
|
86
|
+
* @param {Uint8Array} buffer - CBOR-encoded full state
|
|
87
|
+
* @param {{ codec?: import('../../ports/CodecPort.js').default }} [options]
|
|
90
88
|
* @returns {import('./JoinReducer.js').WarpStateV5}
|
|
91
89
|
*/
|
|
92
90
|
// eslint-disable-next-line complexity
|
|
@@ -169,9 +167,8 @@ export function computeAppliedVV(state) {
|
|
|
169
167
|
* Serializes appliedVV to CBOR format.
|
|
170
168
|
*
|
|
171
169
|
* @param {Map<string, number>} vv - Version vector (Map<writerId, counter>)
|
|
172
|
-
* @param {
|
|
173
|
-
* @
|
|
174
|
-
* @returns {Buffer|Uint8Array} CBOR-encoded version vector
|
|
170
|
+
* @param {{ codec?: import('../../ports/CodecPort.js').default }} [options]
|
|
171
|
+
* @returns {Uint8Array} CBOR-encoded version vector
|
|
175
172
|
*/
|
|
176
173
|
export function serializeAppliedVV(vv, { codec } = {}) {
|
|
177
174
|
const c = codec || defaultCodec;
|
|
@@ -182,9 +179,8 @@ export function serializeAppliedVV(vv, { codec } = {}) {
|
|
|
182
179
|
/**
|
|
183
180
|
* Deserializes appliedVV from CBOR format.
|
|
184
181
|
*
|
|
185
|
-
* @param {
|
|
186
|
-
* @param {
|
|
187
|
-
* @param {import('../../ports/CodecPort.js').default} [options.codec] - Codec for deserialization
|
|
182
|
+
* @param {Uint8Array} buffer - CBOR-encoded version vector
|
|
183
|
+
* @param {{ codec?: import('../../ports/CodecPort.js').default }} [options]
|
|
188
184
|
* @returns {Map<string, number>} Version vector
|
|
189
185
|
*/
|
|
190
186
|
export function deserializeAppliedVV(buffer, { codec } = {}) {
|
|
@@ -28,6 +28,24 @@ import { cloneStateV5, reduceV5 } from './JoinReducer.js';
|
|
|
28
28
|
import { encodeEdgeKey, encodePropKey, CONTENT_PROPERTY_KEY, decodePropKey, isEdgePropKey, decodeEdgePropKey } from './KeyCodec.js';
|
|
29
29
|
import { ProvenanceIndex } from './ProvenanceIndex.js';
|
|
30
30
|
|
|
31
|
+
// ============================================================================
|
|
32
|
+
// Checkpoint Schema Constants
|
|
33
|
+
// ============================================================================
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Standard checkpoint schema — full V5 state without index tree.
|
|
37
|
+
* Distinct from the patch schema namespace (PATCH_SCHEMA_V2/V3).
|
|
38
|
+
* @type {number}
|
|
39
|
+
*/
|
|
40
|
+
export const CHECKPOINT_SCHEMA_STANDARD = 2;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Index-tree checkpoint schema — full V5 state with bitmap index tree.
|
|
44
|
+
* Distinct from the patch schema namespace (PATCH_SCHEMA_V2/V3).
|
|
45
|
+
* @type {number}
|
|
46
|
+
*/
|
|
47
|
+
export const CHECKPOINT_SCHEMA_INDEX_TREE = 4;
|
|
48
|
+
|
|
31
49
|
// ============================================================================
|
|
32
50
|
// Internal Helpers
|
|
33
51
|
// ============================================================================
|
|
@@ -91,17 +109,7 @@ function partitionTreeOids(rawOids) {
|
|
|
91
109
|
* └── provenanceIndex.cbor # Optional: node-to-patchSha index (HG/IO/2)
|
|
92
110
|
* ```
|
|
93
111
|
*
|
|
94
|
-
* @param {
|
|
95
|
-
* @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
|
|
96
|
-
* @param {string} options.graphName - Name of the graph
|
|
97
|
-
* @param {import('./JoinReducer.js').WarpStateV5} options.state - The V5 state to checkpoint
|
|
98
|
-
* @param {import('./Frontier.js').Frontier} options.frontier - Writer frontier map
|
|
99
|
-
* @param {string[]} [options.parents=[]] - Parent commit SHAs (typically prior checkpoint or patch commits)
|
|
100
|
-
* @param {boolean} [options.compact=true] - Whether to compact tombstoned dots before saving
|
|
101
|
-
* @param {import('./ProvenanceIndex.js').ProvenanceIndex} [options.provenanceIndex] - Optional provenance index to persist
|
|
102
|
-
* @param {import('../../ports/CodecPort.js').default} [options.codec] - Codec for CBOR serialization
|
|
103
|
-
* @param {import('../../ports/CryptoPort.js').default} [options.crypto] - CryptoPort for state hash computation
|
|
104
|
-
* @param {Record<string, Uint8Array>} [options.indexTree] - Optional materialized view index tree (triggers schema 4)
|
|
112
|
+
* @param {{ persistence: import('../../ports/GraphPersistencePort.js').default & import('../../ports/BlobPort.js').default & import('../../ports/TreePort.js').default & import('../../ports/CommitPort.js').default, graphName: string, state: import('./JoinReducer.js').WarpStateV5, frontier: import('./Frontier.js').Frontier, parents?: string[], compact?: boolean, provenanceIndex?: import('./ProvenanceIndex.js').ProvenanceIndex, codec?: import('../../ports/CodecPort.js').default, crypto?: import('../../ports/CryptoPort.js').default, indexTree?: Record<string, Uint8Array> }} options - Checkpoint creation options
|
|
105
113
|
* @returns {Promise<string>} The checkpoint commit SHA
|
|
106
114
|
*/
|
|
107
115
|
export async function create({ persistence, graphName, state, frontier, parents = [], compact = true, provenanceIndex, codec, crypto, indexTree }) {
|
|
@@ -120,17 +128,7 @@ export async function create({ persistence, graphName, state, frontier, parents
|
|
|
120
128
|
* └── provenanceIndex.cbor # Optional: node-to-patchSha index (HG/IO/2)
|
|
121
129
|
* ```
|
|
122
130
|
*
|
|
123
|
-
* @param {
|
|
124
|
-
* @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
|
|
125
|
-
* @param {string} options.graphName - Name of the graph
|
|
126
|
-
* @param {import('./JoinReducer.js').WarpStateV5} options.state - The V5 state to checkpoint
|
|
127
|
-
* @param {import('./Frontier.js').Frontier} options.frontier - Writer frontier map
|
|
128
|
-
* @param {string[]} [options.parents=[]] - Parent commit SHAs
|
|
129
|
-
* @param {boolean} [options.compact=true] - Whether to compact tombstoned dots before saving
|
|
130
|
-
* @param {import('./ProvenanceIndex.js').ProvenanceIndex} [options.provenanceIndex] - Optional provenance index to persist
|
|
131
|
-
* @param {import('../../ports/CodecPort.js').default} [options.codec] - Codec for CBOR serialization
|
|
132
|
-
* @param {import('../../ports/CryptoPort.js').default} [options.crypto] - CryptoPort for state hash computation
|
|
133
|
-
* @param {Record<string, Uint8Array>} [options.indexTree] - Optional materialized view index tree (triggers schema 4)
|
|
131
|
+
* @param {{ persistence: import('../../ports/GraphPersistencePort.js').default & import('../../ports/BlobPort.js').default & import('../../ports/TreePort.js').default & import('../../ports/CommitPort.js').default, graphName: string, state: import('./JoinReducer.js').WarpStateV5, frontier: import('./Frontier.js').Frontier, parents?: string[], compact?: boolean, provenanceIndex?: import('./ProvenanceIndex.js').ProvenanceIndex, codec?: import('../../ports/CodecPort.js').default, crypto?: import('../../ports/CryptoPort.js').default, indexTree?: Record<string, Uint8Array> }} options - Checkpoint creation options
|
|
134
132
|
* @returns {Promise<string>} The checkpoint commit SHA
|
|
135
133
|
*/
|
|
136
134
|
export async function createV5({
|
|
@@ -169,15 +167,15 @@ export async function createV5({
|
|
|
169
167
|
const appliedVVBuffer = serializeAppliedVV(appliedVV, { codec: /** @type {import('../../ports/CodecPort.js').default} */ (codec) });
|
|
170
168
|
|
|
171
169
|
// 6. Write blobs to git
|
|
172
|
-
const stateBlobOid = await persistence.writeBlob(
|
|
173
|
-
const frontierBlobOid = await persistence.writeBlob(
|
|
174
|
-
const appliedVVBlobOid = await persistence.writeBlob(
|
|
170
|
+
const stateBlobOid = await persistence.writeBlob(stateBuffer);
|
|
171
|
+
const frontierBlobOid = await persistence.writeBlob(frontierBuffer);
|
|
172
|
+
const appliedVVBlobOid = await persistence.writeBlob(appliedVVBuffer);
|
|
175
173
|
|
|
176
174
|
// 6b. Optionally serialize and write provenance index
|
|
177
175
|
let provenanceIndexBlobOid = null;
|
|
178
176
|
if (provenanceIndex) {
|
|
179
177
|
const provenanceIndexBuffer = provenanceIndex.serialize({ codec });
|
|
180
|
-
provenanceIndexBlobOid = await persistence.writeBlob(
|
|
178
|
+
provenanceIndexBlobOid = await persistence.writeBlob(provenanceIndexBuffer);
|
|
181
179
|
}
|
|
182
180
|
|
|
183
181
|
// 6c. Optionally write index subtree (schema 4)
|
|
@@ -244,7 +242,7 @@ export async function createV5({
|
|
|
244
242
|
indexOid: treeOid,
|
|
245
243
|
// Schema 3 was used for edge-property-aware patches but is never emitted
|
|
246
244
|
// by checkpoint creation. Schema 4 indicates an index tree is present.
|
|
247
|
-
schema: indexTree ?
|
|
245
|
+
schema: indexTree ? CHECKPOINT_SCHEMA_INDEX_TREE : CHECKPOINT_SCHEMA_STANDARD,
|
|
248
246
|
});
|
|
249
247
|
|
|
250
248
|
// 9. Create the checkpoint commit
|
|
@@ -275,8 +273,7 @@ export async function createV5({
|
|
|
275
273
|
*
|
|
276
274
|
* @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
|
|
277
275
|
* @param {string} checkpointSha - The checkpoint commit SHA to load
|
|
278
|
-
* @param {
|
|
279
|
-
* @param {import('../../ports/CodecPort.js').default} [options.codec] - Codec for CBOR deserialization
|
|
276
|
+
* @param {{ codec?: import('../../ports/CodecPort.js').default }} [options] - Load options
|
|
280
277
|
* @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, indexShardOids: Record<string, string>|null}>} The loaded checkpoint data
|
|
281
278
|
* @throws {Error} If checkpoint is schema:1 (migration required)
|
|
282
279
|
*/
|
|
@@ -357,13 +354,7 @@ export async function loadCheckpoint(persistence, checkpointSha, { codec } = {})
|
|
|
357
354
|
* Only supports schema:2 checkpoints. Schema:1 checkpoints will cause
|
|
358
355
|
* loadCheckpoint to throw an error.
|
|
359
356
|
*
|
|
360
|
-
* @param {
|
|
361
|
-
* @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
|
|
362
|
-
* @param {string} options.graphName - Name of the graph
|
|
363
|
-
* @param {string} options.checkpointSha - The schema:2 checkpoint commit SHA to start from
|
|
364
|
-
* @param {import('./Frontier.js').Frontier} options.targetFrontier - The target frontier to materialize to
|
|
365
|
-
* @param {Function} options.patchLoader - Async function to load patches: (writerId, fromSha, toSha) => Array<{patch, sha}>
|
|
366
|
-
* @param {import('../../ports/CodecPort.js').default} [options.codec] - Codec for CBOR deserialization
|
|
357
|
+
* @param {{ persistence: import('../../ports/GraphPersistencePort.js').default & import('../../ports/BlobPort.js').default & import('../../ports/TreePort.js').default & import('../../ports/CommitPort.js').default, graphName: string, checkpointSha: string, targetFrontier: import('./Frontier.js').Frontier, patchLoader: (writerId: string, fromSha: string|null, toSha: string) => Promise<Array<{patch: import('../types/WarpTypesV2.js').PatchV2, sha: string}>>, codec?: import('../../ports/CodecPort.js').default }} options - Materialization options
|
|
367
358
|
* @returns {Promise<import('./JoinReducer.js').WarpStateV5>} The materialized V5 state at targetFrontier
|
|
368
359
|
* @throws {Error} If checkpoint is schema:1 (migration required)
|
|
369
360
|
* @throws {Error} If checkpoint is missing required blobs (state.cbor, frontier.cbor)
|
|
@@ -412,10 +403,7 @@ export async function materializeIncremental({
|
|
|
412
403
|
* Creates ORSet-based state with synthetic dots for all visible elements.
|
|
413
404
|
* This is used when loading a v5 checkpoint for incremental materialization.
|
|
414
405
|
*
|
|
415
|
-
* @param {
|
|
416
|
-
* @param {string[]} visibleProjection.nodes - Visible node IDs
|
|
417
|
-
* @param {Array<{from: string, to: string, label: string}>} visibleProjection.edges - Visible edges
|
|
418
|
-
* @param {Array<{node: string, key: string, value: unknown}>} visibleProjection.props - Visible properties
|
|
406
|
+
* @param {{ nodes: string[], edges: Array<{from: string, to: string, label: string}>, props: Array<{node: string, key: string, value: unknown}> }} visibleProjection - The checkpoint's visible projection
|
|
419
407
|
* @returns {import('./JoinReducer.js').WarpStateV5} Reconstructed WarpStateV5
|
|
420
408
|
* @public
|
|
421
409
|
*/
|
|
@@ -35,9 +35,7 @@ export default class CommitDagTraversalService {
|
|
|
35
35
|
/**
|
|
36
36
|
* Creates a new CommitDagTraversalService.
|
|
37
37
|
*
|
|
38
|
-
* @param {
|
|
39
|
-
* @param {import('./BitmapIndexReader.js').default} options.indexReader - Index reader for O(1) lookups
|
|
40
|
-
* @param {import('../../ports/LoggerPort.js').default} [options.logger] - Logger instance
|
|
38
|
+
* @param {{ indexReader: import('./BitmapIndexReader.js').default, logger?: import('../../ports/LoggerPort.js').default }} options
|
|
41
39
|
*/
|
|
42
40
|
constructor({ indexReader, logger = nullLogger }) {
|
|
43
41
|
if (!indexReader) {
|
|
@@ -37,11 +37,9 @@ export default class DagPathFinding {
|
|
|
37
37
|
/**
|
|
38
38
|
* Creates a new DagPathFinding service.
|
|
39
39
|
*
|
|
40
|
-
* @param {
|
|
41
|
-
* @param {import('./BitmapIndexReader.js').default} options.indexReader - Index reader for O(1) lookups
|
|
42
|
-
* @param {import('../../ports/LoggerPort.js').default} [options.logger] - Logger instance
|
|
40
|
+
* @param {{ indexReader: import('./BitmapIndexReader.js').default, logger?: import('../../ports/LoggerPort.js').default }} options
|
|
43
41
|
*/
|
|
44
|
-
constructor(/** @type {{ indexReader: import('./BitmapIndexReader.js').default, logger?: import('../../ports/LoggerPort.js').default }} */ { indexReader, logger = nullLogger }
|
|
42
|
+
constructor(/** @type {{ indexReader: import('./BitmapIndexReader.js').default, logger?: import('../../ports/LoggerPort.js').default }} */ { indexReader, logger = nullLogger }) {
|
|
45
43
|
if (!indexReader) {
|
|
46
44
|
throw new Error('DagPathFinding requires an indexReader');
|
|
47
45
|
}
|
|
@@ -56,12 +54,7 @@ export default class DagPathFinding {
|
|
|
56
54
|
* Returns the first path found, which is guaranteed to be a shortest path
|
|
57
55
|
* (in terms of number of edges) due to BFS's level-order exploration.
|
|
58
56
|
*
|
|
59
|
-
* @param {
|
|
60
|
-
* @param {string} options.from - Source node SHA
|
|
61
|
-
* @param {string} options.to - Target node SHA
|
|
62
|
-
* @param {number} [options.maxNodes=100000] - Maximum nodes to visit
|
|
63
|
-
* @param {number} [options.maxDepth=1000] - Maximum path length
|
|
64
|
-
* @param {AbortSignal} [options.signal] - Optional AbortSignal for cancellation
|
|
57
|
+
* @param {{ from: string, to: string, maxNodes?: number, maxDepth?: number, signal?: AbortSignal }} options - Path finding options
|
|
65
58
|
* @returns {Promise<{found: boolean, path: string[], length: number}>} Path result
|
|
66
59
|
*/
|
|
67
60
|
async findPath({
|
|
@@ -114,11 +107,7 @@ export default class DagPathFinding {
|
|
|
114
107
|
/**
|
|
115
108
|
* Finds the shortest path between two nodes using bidirectional BFS.
|
|
116
109
|
*
|
|
117
|
-
* @param {
|
|
118
|
-
* @param {string} options.from - Source node SHA
|
|
119
|
-
* @param {string} options.to - Target node SHA
|
|
120
|
-
* @param {number} [options.maxDepth=1000] - Maximum search depth per direction
|
|
121
|
-
* @param {AbortSignal} [options.signal] - Optional AbortSignal for cancellation
|
|
110
|
+
* @param {{ from: string, to: string, maxDepth?: number, signal?: AbortSignal }} options - Path finding options
|
|
122
111
|
* @returns {Promise<{found: boolean, path: string[], length: number}>} Path result
|
|
123
112
|
*/
|
|
124
113
|
async shortestPath({ from, to, maxDepth = DEFAULT_MAX_DEPTH, signal }) {
|
|
@@ -197,12 +186,7 @@ export default class DagPathFinding {
|
|
|
197
186
|
/**
|
|
198
187
|
* Finds shortest path using Dijkstra's algorithm with custom edge weights.
|
|
199
188
|
*
|
|
200
|
-
* @param {
|
|
201
|
-
* @param {string} options.from - Starting SHA
|
|
202
|
-
* @param {string} options.to - Target SHA
|
|
203
|
-
* @param {(from: string, to: string) => number|Promise<number>} [options.weightProvider] - Async callback `(fromSha, toSha) => number`
|
|
204
|
-
* @param {string} [options.direction='children'] - Edge direction: 'children' or 'parents'
|
|
205
|
-
* @param {AbortSignal} [options.signal] - Optional AbortSignal for cancellation
|
|
189
|
+
* @param {{ from: string, to: string, weightProvider?: (from: string, to: string) => number|Promise<number>, direction?: string, signal?: AbortSignal }} options - Path finding options
|
|
206
190
|
* @returns {Promise<{path: string[], totalCost: number}>} Path and cost
|
|
207
191
|
* @throws {TraversalError} With code 'NO_PATH' if no path exists
|
|
208
192
|
*/
|
|
@@ -274,13 +258,7 @@ export default class DagPathFinding {
|
|
|
274
258
|
/**
|
|
275
259
|
* Finds shortest path using A* algorithm with heuristic guidance.
|
|
276
260
|
*
|
|
277
|
-
* @param {
|
|
278
|
-
* @param {string} options.from - Starting SHA
|
|
279
|
-
* @param {string} options.to - Target SHA
|
|
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
|
-
* @param {string} [options.direction='children'] - Edge direction: 'children' or 'parents'
|
|
283
|
-
* @param {AbortSignal} [options.signal] - Optional AbortSignal for cancellation
|
|
261
|
+
* @param {{ from: string, to: string, weightProvider?: (from: string, to: string) => number|Promise<number>, heuristicProvider?: (sha: string, target: string) => number, direction?: string, signal?: AbortSignal }} options - Path finding options
|
|
284
262
|
* @returns {Promise<{path: string[], totalCost: number, nodesExplored: number}>} Path result
|
|
285
263
|
* @throws {TraversalError} With code 'NO_PATH' if no path exists
|
|
286
264
|
*/
|
|
@@ -364,13 +342,7 @@ export default class DagPathFinding {
|
|
|
364
342
|
/**
|
|
365
343
|
* Bi-directional A* search - meets in the middle from both ends.
|
|
366
344
|
*
|
|
367
|
-
* @param {
|
|
368
|
-
* @param {string} options.from - Starting SHA
|
|
369
|
-
* @param {string} options.to - Target SHA
|
|
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
|
-
* @param {AbortSignal} [options.signal] - Optional AbortSignal for cancellation
|
|
345
|
+
* @param {{ from: string, to: string, weightProvider?: (from: string, to: string) => number|Promise<number>, forwardHeuristic?: (sha: string, target: string) => number, backwardHeuristic?: (sha: string, target: string) => number, signal?: AbortSignal }} options - Path finding options
|
|
374
346
|
* @returns {Promise<{path: string[], totalCost: number, nodesExplored: number}>} Path result
|
|
375
347
|
* @throws {TraversalError} With code 'NO_PATH' if no path exists
|
|
376
348
|
*/
|
|
@@ -462,18 +434,7 @@ export default class DagPathFinding {
|
|
|
462
434
|
/**
|
|
463
435
|
* Expands the forward frontier by one node in bidirectional A*.
|
|
464
436
|
*
|
|
465
|
-
* @param {
|
|
466
|
-
* @param {import('../utils/MinHeap.js').default<string>} 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
|
|
437
|
+
* @param {{ fwdHeap: import('../utils/MinHeap.js').default<string>, fwdVisited: Set<string>, fwdGScore: Map<string, number>, fwdPrevious: Map<string, string>, bwdVisited: Set<string>, bwdGScore: Map<string, number>, weightProvider: (from: string, to: string) => number|Promise<number>, forwardHeuristic: (sha: string, target: string) => number, to: string, mu: number, meetingPoint: string|null }} state - Forward expansion state
|
|
477
438
|
* @returns {Promise<{explored: number, mu: number, meetingPoint: string|null}>}
|
|
478
439
|
* @private
|
|
479
440
|
*/
|
|
@@ -535,18 +496,7 @@ export default class DagPathFinding {
|
|
|
535
496
|
/**
|
|
536
497
|
* Expands the backward frontier by one node in bidirectional A*.
|
|
537
498
|
*
|
|
538
|
-
* @param {
|
|
539
|
-
* @param {import('../utils/MinHeap.js').default<string>} 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
|
|
499
|
+
* @param {{ bwdHeap: import('../utils/MinHeap.js').default<string>, bwdVisited: Set<string>, bwdGScore: Map<string, number>, bwdNext: Map<string, string>, fwdVisited: Set<string>, fwdGScore: Map<string, number>, weightProvider: (from: string, to: string) => number|Promise<number>, backwardHeuristic: (sha: string, target: string) => number, from: string, mu: number, meetingPoint: string|null }} state - Backward expansion state
|
|
550
500
|
* @returns {Promise<{explored: number, mu: number, meetingPoint: string|null}>}
|
|
551
501
|
* @private
|
|
552
502
|
*/
|
|
@@ -32,12 +32,9 @@ export default class DagTopology {
|
|
|
32
32
|
/**
|
|
33
33
|
* Creates a new DagTopology service.
|
|
34
34
|
*
|
|
35
|
-
* @param {
|
|
36
|
-
* @param {import('./BitmapIndexReader.js').default} options.indexReader - Index reader for O(1) lookups
|
|
37
|
-
* @param {import('../../ports/LoggerPort.js').default} [options.logger] - Logger instance
|
|
38
|
-
* @param {import('./DagTraversal.js').default} [options.traversal] - Traversal service for ancestor enumeration
|
|
35
|
+
* @param {{ indexReader: import('./BitmapIndexReader.js').default, logger?: import('../../ports/LoggerPort.js').default, traversal?: import('./DagTraversal.js').default }} options
|
|
39
36
|
*/
|
|
40
|
-
constructor(
|
|
37
|
+
constructor({ indexReader, logger = nullLogger, traversal }) {
|
|
41
38
|
if (!indexReader) {
|
|
42
39
|
throw new Error('DagTopology requires an indexReader');
|
|
43
40
|
}
|
|
@@ -67,11 +64,7 @@ export default class DagTopology {
|
|
|
67
64
|
* An ancestor is "common" if it can be reached by following parent edges
|
|
68
65
|
* from ALL of the input nodes.
|
|
69
66
|
*
|
|
70
|
-
* @param {
|
|
71
|
-
* @param {string[]} options.shas - Array of node SHAs
|
|
72
|
-
* @param {number} [options.maxResults=100] - Maximum ancestors to return
|
|
73
|
-
* @param {number} [options.maxDepth=1000] - Maximum depth to search
|
|
74
|
-
* @param {AbortSignal} [options.signal] - Optional AbortSignal for cancellation
|
|
67
|
+
* @param {{ shas: string[], maxResults?: number, maxDepth?: number, signal?: AbortSignal }} options - Common ancestor options
|
|
75
68
|
* @returns {Promise<string[]>} Array of common ancestor SHAs
|
|
76
69
|
*/
|
|
77
70
|
async commonAncestors({ shas, maxResults = 100, maxDepth = DEFAULT_MAX_DEPTH, signal }) {
|
|
@@ -119,12 +112,7 @@ export default class DagTopology {
|
|
|
119
112
|
* Topological order ensures that for every directed edge A -> B, node A
|
|
120
113
|
* is yielded before node B.
|
|
121
114
|
*
|
|
122
|
-
* @param {
|
|
123
|
-
* @param {string} options.start - Starting node SHA
|
|
124
|
-
* @param {number} [options.maxNodes=100000] - Maximum nodes to yield
|
|
125
|
-
* @param {TraversalDirection} [options.direction='forward'] - Direction
|
|
126
|
-
* @param {boolean} [options.throwOnCycle=false] - If true, throws on cycle detection
|
|
127
|
-
* @param {AbortSignal} [options.signal] - Optional AbortSignal for cancellation
|
|
115
|
+
* @param {{ start: string, maxNodes?: number, direction?: TraversalDirection, throwOnCycle?: boolean, signal?: AbortSignal }} options - Topological sort options
|
|
128
116
|
* @yields {{sha: string, depth: number, parent: null}} Nodes in topological order
|
|
129
117
|
* @throws {TraversalError} With code 'CYCLE_DETECTED' if throwOnCycle is true
|
|
130
118
|
*/
|
|
@@ -39,11 +39,9 @@ export default class DagTraversal {
|
|
|
39
39
|
/**
|
|
40
40
|
* Creates a new DagTraversal service.
|
|
41
41
|
*
|
|
42
|
-
* @param {
|
|
43
|
-
* @param {import('./BitmapIndexReader.js').default} options.indexReader - Index reader for O(1) lookups
|
|
44
|
-
* @param {import('../../ports/LoggerPort.js').default} [options.logger] - Logger instance
|
|
42
|
+
* @param {{ indexReader: import('./BitmapIndexReader.js').default, logger?: import('../../ports/LoggerPort.js').default }} options
|
|
45
43
|
*/
|
|
46
|
-
constructor(
|
|
44
|
+
constructor({ indexReader, logger = nullLogger }) {
|
|
47
45
|
if (!indexReader) {
|
|
48
46
|
throw new Error('DagTraversal requires an indexReader');
|
|
49
47
|
}
|
|
@@ -73,12 +71,7 @@ export default class DagTraversal {
|
|
|
73
71
|
* moving to depth N+1. This guarantees that nodes are yielded in order of
|
|
74
72
|
* increasing distance from the start node.
|
|
75
73
|
*
|
|
76
|
-
* @param {
|
|
77
|
-
* @param {string} options.start - Starting node SHA
|
|
78
|
-
* @param {number} [options.maxNodes=100000] - Maximum nodes to visit
|
|
79
|
-
* @param {number} [options.maxDepth=1000] - Maximum depth to traverse
|
|
80
|
-
* @param {TraversalDirection} [options.direction='forward'] - Traversal direction
|
|
81
|
-
* @param {AbortSignal} [options.signal] - Optional AbortSignal for cancellation
|
|
74
|
+
* @param {{ start: string, maxNodes?: number, maxDepth?: number, direction?: TraversalDirection, signal?: AbortSignal }} options - Traversal options
|
|
82
75
|
* @yields {TraversalNode} Nodes in BFS order
|
|
83
76
|
*/
|
|
84
77
|
async *bfs({
|
|
@@ -127,12 +120,7 @@ export default class DagTraversal {
|
|
|
127
120
|
*
|
|
128
121
|
* DFS explores as far as possible along each branch before backtracking.
|
|
129
122
|
*
|
|
130
|
-
* @param {
|
|
131
|
-
* @param {string} options.start - Starting node SHA
|
|
132
|
-
* @param {number} [options.maxNodes=100000] - Maximum nodes to visit
|
|
133
|
-
* @param {number} [options.maxDepth=1000] - Maximum depth to traverse
|
|
134
|
-
* @param {TraversalDirection} [options.direction='forward'] - Traversal direction
|
|
135
|
-
* @param {AbortSignal} [options.signal] - Optional AbortSignal for cancellation
|
|
123
|
+
* @param {{ start: string, maxNodes?: number, maxDepth?: number, direction?: TraversalDirection, signal?: AbortSignal }} options - Traversal options
|
|
136
124
|
* @yields {TraversalNode} Nodes in DFS pre-order
|
|
137
125
|
*/
|
|
138
126
|
async *dfs({
|
|
@@ -180,11 +168,7 @@ export default class DagTraversal {
|
|
|
180
168
|
/**
|
|
181
169
|
* Yields all ancestors of a node (transitive closure going backwards).
|
|
182
170
|
*
|
|
183
|
-
* @param {
|
|
184
|
-
* @param {string} options.sha - Starting node SHA
|
|
185
|
-
* @param {number} [options.maxNodes=100000] - Maximum ancestor nodes to yield
|
|
186
|
-
* @param {number} [options.maxDepth=1000] - Maximum generations to traverse
|
|
187
|
-
* @param {AbortSignal} [options.signal] - Optional AbortSignal for cancellation
|
|
171
|
+
* @param {{ sha: string, maxNodes?: number, maxDepth?: number, signal?: AbortSignal }} options - Traversal options
|
|
188
172
|
* @yields {TraversalNode} Ancestor nodes in BFS order
|
|
189
173
|
*/
|
|
190
174
|
async *ancestors({ sha, maxNodes = DEFAULT_MAX_NODES, maxDepth = DEFAULT_MAX_DEPTH, signal }) {
|
|
@@ -194,11 +178,7 @@ export default class DagTraversal {
|
|
|
194
178
|
/**
|
|
195
179
|
* Yields all descendants of a node (transitive closure going forwards).
|
|
196
180
|
*
|
|
197
|
-
* @param {
|
|
198
|
-
* @param {string} options.sha - Starting node SHA
|
|
199
|
-
* @param {number} [options.maxNodes=100000] - Maximum descendant nodes to yield
|
|
200
|
-
* @param {number} [options.maxDepth=1000] - Maximum generations to traverse
|
|
201
|
-
* @param {AbortSignal} [options.signal] - Optional AbortSignal for cancellation
|
|
181
|
+
* @param {{ sha: string, maxNodes?: number, maxDepth?: number, signal?: AbortSignal }} options - Traversal options
|
|
202
182
|
* @yields {TraversalNode} Descendant nodes in BFS order
|
|
203
183
|
*/
|
|
204
184
|
async *descendants({ sha, maxNodes = DEFAULT_MAX_NODES, maxDepth = DEFAULT_MAX_DEPTH, signal }) {
|
|
@@ -211,11 +191,7 @@ export default class DagTraversal {
|
|
|
211
191
|
* Delegates to the path-finding service's findPath if one is set,
|
|
212
192
|
* otherwise performs its own BFS-based reachability check.
|
|
213
193
|
*
|
|
214
|
-
* @param {
|
|
215
|
-
* @param {string} options.from - Source node SHA
|
|
216
|
-
* @param {string} options.to - Target node SHA
|
|
217
|
-
* @param {number} [options.maxDepth=1000] - Maximum search depth
|
|
218
|
-
* @param {AbortSignal} [options.signal] - Optional AbortSignal for cancellation
|
|
194
|
+
* @param {{ from: string, to: string, maxDepth?: number, signal?: AbortSignal }} options - Reachability options
|
|
219
195
|
* @returns {Promise<boolean>} True if a path exists
|
|
220
196
|
*/
|
|
221
197
|
async isReachable({ from, to, maxDepth = DEFAULT_MAX_DEPTH, signal }) {
|