@git-stunts/git-warp 12.3.0 → 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 -6
- package/bin/cli/commands/info.js +1 -5
- package/bin/cli/infrastructure.js +6 -9
- package/bin/cli/shared.js +8 -0
- package/bin/warp-graph.js +6 -6
- package/package.json +1 -1
- package/src/domain/WarpGraph.js +5 -35
- 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 +2 -17
- package/src/domain/services/AuditVerifierService.js +2 -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 +6 -23
- package/src/domain/services/CheckpointMessageCodec.js +1 -6
- package/src/domain/services/CheckpointSerializerV5.js +8 -12
- package/src/domain/services/CheckpointService.js +9 -39
- 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 +39 -65
- 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 +4 -18
- package/src/domain/services/MigrationService.js +1 -4
- package/src/domain/services/ObserverView.js +1 -7
- package/src/domain/services/PatchBuilderV2.js +6 -18
- 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 +5 -32
- package/src/domain/services/SyncController.js +5 -25
- package/src/domain/services/SyncProtocol.js +4 -8
- 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/WarpStateIndexBuilder.js +2 -4
- package/src/domain/services/WormholeService.js +9 -25
- package/src/domain/trust/TrustCrypto.js +1 -5
- package/src/domain/trust/TrustEvaluator.js +1 -8
- package/src/domain/trust/TrustRecordService.js +5 -10
- package/src/domain/types/TickReceipt.js +3 -7
- package/src/domain/types/WarpTypes.js +1 -5
- package/src/domain/types/WarpTypesV2.js +1 -8
- 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/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 +3 -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 +1 -5
- package/src/domain/warp/materializeAdvanced.methods.js +3 -3
- package/src/domain/warp/patch.methods.js +6 -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
|
@@ -22,7 +22,7 @@ import { reduceV5, createEmptyStateV5, cloneStateV5 } from './JoinReducer.js';
|
|
|
22
22
|
* A single patch entry in the provenance payload.
|
|
23
23
|
*
|
|
24
24
|
* @typedef {Object} PatchEntry
|
|
25
|
-
* @property {
|
|
25
|
+
* @property {import('../types/WarpTypesV2.js').PatchV2} patch - The decoded patch object
|
|
26
26
|
* @property {string} sha - The Git SHA of the patch commit
|
|
27
27
|
*/
|
|
28
28
|
|
|
@@ -307,11 +307,7 @@ function buildEdgesSnapshot(edges, directionKey) {
|
|
|
307
307
|
* The snapshot includes the node's ID, properties, outgoing edges, and incoming edges.
|
|
308
308
|
* All data is deeply frozen to prevent mutation.
|
|
309
309
|
*
|
|
310
|
-
* @param {
|
|
311
|
-
* @param {string} params.id - The node ID
|
|
312
|
-
* @param {Map<string, unknown>} params.propsMap - Map of property names to values
|
|
313
|
-
* @param {Array<{label: string, neighborId: string}>} params.edgesOut - Outgoing edges
|
|
314
|
-
* @param {Array<{label: string, neighborId: string}>} params.edgesIn - Incoming edges
|
|
310
|
+
* @param {{ id: string, propsMap: Map<string, unknown>, edgesOut: Array<{label: string, neighborId: string}>, edgesIn: Array<{label: string, neighborId: string}> }} params - Node data
|
|
315
311
|
* @returns {Readonly<QueryNodeSnapshot>} Frozen node snapshot
|
|
316
312
|
* @private
|
|
317
313
|
*/
|
|
@@ -385,11 +381,7 @@ function normalizeDepth(depth) {
|
|
|
385
381
|
* Collects all neighbors reachable via one edge in the specified direction,
|
|
386
382
|
* optionally filtered by edge label.
|
|
387
383
|
*
|
|
388
|
-
* @param {
|
|
389
|
-
* @param {'outgoing' | 'incoming'} params.direction - Direction of traversal
|
|
390
|
-
* @param {string | undefined} params.label - Edge label filter (undefined = all labels)
|
|
391
|
-
* @param {string[]} params.workingSet - Current set of node IDs to traverse from
|
|
392
|
-
* @param {AdjacencyMaps} params.adjacency - Adjacency maps from materialized state
|
|
384
|
+
* @param {{ direction: 'outgoing' | 'incoming', label: string | undefined, workingSet: string[], adjacency: AdjacencyMaps }} params - Traversal parameters
|
|
393
385
|
* @returns {string[]} Sorted array of neighbor node IDs
|
|
394
386
|
* @private
|
|
395
387
|
*/
|
|
@@ -420,12 +412,7 @@ function applyHop({ direction, label, workingSet, adjacency }) {
|
|
|
420
412
|
*
|
|
421
413
|
* If minDepth is 0, the starting nodes themselves are included in the result.
|
|
422
414
|
*
|
|
423
|
-
* @param {
|
|
424
|
-
* @param {'outgoing' | 'incoming'} params.direction - Direction of traversal
|
|
425
|
-
* @param {string | undefined} params.label - Edge label filter (undefined = all labels)
|
|
426
|
-
* @param {string[]} params.workingSet - Current set of node IDs to traverse from
|
|
427
|
-
* @param {AdjacencyMaps} params.adjacency - Adjacency maps from materialized state
|
|
428
|
-
* @param {[number, number]} params.depth - Tuple of [minDepth, maxDepth]
|
|
415
|
+
* @param {{ direction: 'outgoing' | 'incoming', label: string | undefined, workingSet: string[], adjacency: AdjacencyMaps, depth: [number, number] }} params - Traversal parameters
|
|
429
416
|
* @returns {string[]} Sorted array of reachable node IDs within the depth range
|
|
430
417
|
* @private
|
|
431
418
|
*/
|
|
@@ -38,15 +38,9 @@ import { decodeEdgeKey, decodePropKey, isEdgePropKey } from './KeyCodec.js';
|
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
40
|
* @typedef {Object} StateDiffResult
|
|
41
|
-
* @property {
|
|
42
|
-
* @property {
|
|
43
|
-
* @property {
|
|
44
|
-
* @property {Object} edges - Edge changes
|
|
45
|
-
* @property {EdgeChange[]} edges.added - Added edges (sorted)
|
|
46
|
-
* @property {EdgeChange[]} edges.removed - Removed edges (sorted)
|
|
47
|
-
* @property {Object} props - Property changes
|
|
48
|
-
* @property {PropSet[]} props.set - Set/changed properties (sorted)
|
|
49
|
-
* @property {PropRemoved[]} props.removed - Removed properties (sorted)
|
|
41
|
+
* @property {{ added: string[], removed: string[] }} nodes - Node changes
|
|
42
|
+
* @property {{ added: EdgeChange[], removed: EdgeChange[] }} edges - Edge changes
|
|
43
|
+
* @property {{ set: PropSet[], removed: PropRemoved[] }} props - Property changes
|
|
50
44
|
*/
|
|
51
45
|
|
|
52
46
|
/**
|
|
@@ -74,9 +74,8 @@ export function propVisibleV5(state, propKey) {
|
|
|
74
74
|
* Same canonical ordering as v4 for visible projection.
|
|
75
75
|
*
|
|
76
76
|
* @param {import('./JoinReducer.js').WarpStateV5} state
|
|
77
|
-
* @param {
|
|
78
|
-
* @
|
|
79
|
-
* @returns {Buffer|Uint8Array}
|
|
77
|
+
* @param {{ codec?: import('../../ports/CodecPort.js').default }} [options]
|
|
78
|
+
* @returns {Uint8Array}
|
|
80
79
|
*/
|
|
81
80
|
export function serializeStateV5(state, { codec } = {}) {
|
|
82
81
|
const c = codec || defaultCodec;
|
|
@@ -119,15 +118,17 @@ export function serializeStateV5(state, { codec } = {}) {
|
|
|
119
118
|
return c.encode({ nodes, edges: visibleEdges, props: visibleProps });
|
|
120
119
|
}
|
|
121
120
|
|
|
121
|
+
/**
|
|
122
|
+
* @typedef {{ crypto?: import('../../ports/CryptoPort.js').default, codec?: import('../../ports/CodecPort.js').default }} StateHashOptions
|
|
123
|
+
*/
|
|
124
|
+
|
|
122
125
|
/**
|
|
123
126
|
* Computes SHA-256 hash of canonical state bytes.
|
|
124
127
|
* @param {import('./JoinReducer.js').WarpStateV5} state
|
|
125
|
-
* @param {
|
|
126
|
-
* @param {import('../../ports/CryptoPort.js').default} [options.crypto] - CryptoPort instance
|
|
127
|
-
* @param {import('../../ports/CodecPort.js').default} [options.codec] - Codec for serialization
|
|
128
|
+
* @param {StateHashOptions} [options] - Options
|
|
128
129
|
* @returns {Promise<string>} Hex-encoded SHA-256 hash
|
|
129
130
|
*/
|
|
130
|
-
export async function computeStateHashV5(state, { crypto, codec } = /** @type {
|
|
131
|
+
export async function computeStateHashV5(state, { crypto, codec } = /** @type {StateHashOptions} */ ({})) {
|
|
131
132
|
const c = crypto || defaultCrypto;
|
|
132
133
|
const serialized = serializeStateV5(state, { codec });
|
|
133
134
|
return await c.hash('sha256', serialized);
|
|
@@ -136,9 +137,8 @@ export async function computeStateHashV5(state, { crypto, codec } = /** @type {{
|
|
|
136
137
|
/**
|
|
137
138
|
* Deserializes state from CBOR bytes.
|
|
138
139
|
* Note: This reconstructs the visible projection only.
|
|
139
|
-
* @param {
|
|
140
|
-
* @param {
|
|
141
|
-
* @param {import('../../ports/CodecPort.js').default} [options.codec] - Codec for deserialization
|
|
140
|
+
* @param {Uint8Array} buffer
|
|
141
|
+
* @param {{ codec?: import('../../ports/CodecPort.js').default }} [options]
|
|
142
142
|
* @returns {{nodes: string[], edges: Array<{from: string, to: string, label: string}>, props: Array<{node: string, key: string, value: unknown}>}}
|
|
143
143
|
*/
|
|
144
144
|
export function deserializeStateV5(buffer, { codec } = {}) {
|
|
@@ -37,7 +37,7 @@ const BITMAP_BASE_OVERHEAD = 64;
|
|
|
37
37
|
* Uses canonical JSON stringification for deterministic output
|
|
38
38
|
* across different JavaScript engines.
|
|
39
39
|
*
|
|
40
|
-
* @param {
|
|
40
|
+
* @param {Record<string, unknown>} data - The data object to checksum
|
|
41
41
|
* @param {import('../../ports/CryptoPort.js').default} crypto - CryptoPort instance
|
|
42
42
|
* @returns {Promise<string>} Hex-encoded SHA-256 hash
|
|
43
43
|
*/
|
|
@@ -82,17 +82,7 @@ export default class StreamingBitmapIndexBuilder {
|
|
|
82
82
|
/**
|
|
83
83
|
* Creates a new StreamingBitmapIndexBuilder instance.
|
|
84
84
|
*
|
|
85
|
-
* @param {
|
|
86
|
-
* @param {import('../../ports/IndexStoragePort.js').default} options.storage - Storage adapter implementing IndexStoragePort.
|
|
87
|
-
* Required methods: writeBlob, writeTree, readBlob
|
|
88
|
-
* @param {number} [options.maxMemoryBytes=52428800] - Maximum bitmap memory before flush (default 50MB).
|
|
89
|
-
* Note: SHA→ID mappings are not counted against this limit as they must remain in memory.
|
|
90
|
-
* @param {Function} [options.onFlush] - Optional callback invoked on each flush.
|
|
91
|
-
* Receives { flushedBytes, totalFlushedBytes, flushCount }.
|
|
92
|
-
* @param {import('../../ports/LoggerPort.js').default} [options.logger] - Logger for structured logging.
|
|
93
|
-
* Defaults to NoOpLogger (no logging).
|
|
94
|
-
* @param {import('../../ports/CryptoPort.js').default} [options.crypto] - CryptoPort instance for hashing
|
|
95
|
-
* @param {import('../../ports/CodecPort.js').default} [options.codec] - Codec for serialization
|
|
85
|
+
* @param {{ storage: import('../../ports/IndexStoragePort.js').default, maxMemoryBytes?: number, onFlush?: (stats: {flushedBytes: number, totalFlushedBytes: number, flushCount: number}) => void, logger?: import('../../ports/LoggerPort.js').default, crypto?: import('../../ports/CryptoPort.js').default, codec?: import('../../ports/CodecPort.js').default }} options - Configuration options
|
|
96
86
|
*/
|
|
97
87
|
constructor({ storage, maxMemoryBytes = DEFAULT_MAX_MEMORY_BYTES, onFlush, logger = nullLogger, crypto, codec }) {
|
|
98
88
|
if (!storage) {
|
|
@@ -114,7 +104,7 @@ export default class StreamingBitmapIndexBuilder {
|
|
|
114
104
|
/** @type {number} */
|
|
115
105
|
this.maxMemoryBytes = maxMemoryBytes;
|
|
116
106
|
|
|
117
|
-
/** @type {
|
|
107
|
+
/** @type {((stats: {flushedBytes: number, totalFlushedBytes: number, flushCount: number}) => void)|undefined} */
|
|
118
108
|
this.onFlush = onFlush;
|
|
119
109
|
|
|
120
110
|
/** @type {import('../../ports/LoggerPort.js').default} */
|
|
@@ -310,7 +300,7 @@ export default class StreamingBitmapIndexBuilder {
|
|
|
310
300
|
* of the SHA. This enables efficient loading of only relevant shards
|
|
311
301
|
* during index reads.
|
|
312
302
|
*
|
|
313
|
-
* @returns {
|
|
303
|
+
* @returns {Record<string, Record<string, number>>} Object mapping 2-char hex prefix
|
|
314
304
|
* to objects of SHA→numeric ID mappings
|
|
315
305
|
* @private
|
|
316
306
|
*/
|
|
@@ -333,7 +323,7 @@ export default class StreamingBitmapIndexBuilder {
|
|
|
333
323
|
* Each shard is wrapped in a versioned envelope with checksum before writing.
|
|
334
324
|
* Writes are performed in parallel using Promise.all for efficiency.
|
|
335
325
|
*
|
|
336
|
-
* @param {
|
|
326
|
+
* @param {Record<string, Record<string, number>>} idShards - Object mapping 2-char hex prefix
|
|
337
327
|
* to objects of SHA→numeric ID mappings
|
|
338
328
|
* @returns {Promise<string[]>} Array of Git tree entry strings in format
|
|
339
329
|
* "100644 blob <oid>\tmeta_<prefix>.json"
|
|
@@ -365,9 +355,7 @@ export default class StreamingBitmapIndexBuilder {
|
|
|
365
355
|
*
|
|
366
356
|
* Processing is parallelized across shard paths for efficiency.
|
|
367
357
|
*
|
|
368
|
-
* @param {
|
|
369
|
-
* @param {AbortSignal} [options.signal] - Optional AbortSignal for cancellation.
|
|
370
|
-
* If aborted, throws an error with code 'ABORT_ERR'.
|
|
358
|
+
* @param {{ signal?: AbortSignal }} [options] - Options
|
|
371
359
|
* @returns {Promise<string[]>} Array of Git tree entry strings in format
|
|
372
360
|
* "100644 blob <oid>\tshards_<type>_<prefix>.json"
|
|
373
361
|
* @throws {Error} If the operation is aborted via signal
|
|
@@ -409,12 +397,7 @@ export default class StreamingBitmapIndexBuilder {
|
|
|
409
397
|
* frontier.json # Optional: JSON-encoded frontier
|
|
410
398
|
* ```
|
|
411
399
|
*
|
|
412
|
-
* @param {
|
|
413
|
-
* @param {AbortSignal} [options.signal] - Optional AbortSignal for cancellation.
|
|
414
|
-
* If aborted, throws an error with code 'ABORT_ERR'.
|
|
415
|
-
* @param {Map<string, string>} [options.frontier] - Optional writer frontier
|
|
416
|
-
* (writerId → tip SHA) for staleness detection. If provided, frontier.cbor and
|
|
417
|
-
* frontier.json files are included in the tree.
|
|
400
|
+
* @param {{ signal?: AbortSignal, frontier?: Map<string, string> }} [options] - Finalization options
|
|
418
401
|
* @returns {Promise<string>} OID of the created Git tree containing the complete index
|
|
419
402
|
* @throws {Error} If the operation is aborted via signal
|
|
420
403
|
* @throws {ShardValidationError} If a chunk has an unsupported version during merge
|
|
@@ -472,7 +455,7 @@ export default class StreamingBitmapIndexBuilder {
|
|
|
472
455
|
* Useful for understanding memory pressure during index building and
|
|
473
456
|
* tuning the `maxMemoryBytes` threshold.
|
|
474
457
|
*
|
|
475
|
-
* @returns {
|
|
458
|
+
* @returns {{estimatedBitmapBytes: number, estimatedMappingBytes: number, totalFlushedBytes: number, flushCount: number, nodeCount: number, bitmapCount: number}} Memory statistics object
|
|
476
459
|
* @property {number} estimatedBitmapBytes - Current estimated size of in-memory bitmaps in bytes.
|
|
477
460
|
* This is an approximation based on bitmap operations; actual memory usage may vary.
|
|
478
461
|
* @property {number} estimatedMappingBytes - Estimated size of SHA→ID mappings in bytes.
|
|
@@ -527,11 +510,7 @@ export default class StreamingBitmapIndexBuilder {
|
|
|
527
510
|
* - New bitmap: adds BITMAP_BASE_OVERHEAD (64 bytes)
|
|
528
511
|
* - New entry in existing bitmap: adds ~4 bytes (approximation)
|
|
529
512
|
*
|
|
530
|
-
* @param {
|
|
531
|
-
* @param {string} opts.sha - The SHA to use as bitmap key (40-character hex string)
|
|
532
|
-
* @param {number} opts.id - The numeric ID to add to the bitmap
|
|
533
|
-
* @param {'fwd'|'rev'} opts.type - Edge direction type: 'fwd' for forward edges
|
|
534
|
-
* (this node's children), 'rev' for reverse edges (this node's parents)
|
|
513
|
+
* @param {{ sha: string, id: number, type: 'fwd'|'rev' }} opts - Options object
|
|
535
514
|
* @private
|
|
536
515
|
*/
|
|
537
516
|
_addToBitmap({ sha, id, type }) {
|
|
@@ -562,7 +541,7 @@ export default class StreamingBitmapIndexBuilder {
|
|
|
562
541
|
* 4. Recomputes and validates checksum (throws ShardCorruptionError if mismatch)
|
|
563
542
|
*
|
|
564
543
|
* @param {string} oid - Git blob OID of the chunk to load (40-character hex string)
|
|
565
|
-
* @returns {Promise<
|
|
544
|
+
* @returns {Promise<Record<string, string>>} The validated chunk data (SHA→base64Bitmap mappings)
|
|
566
545
|
* @throws {ShardCorruptionError} If the chunk cannot be parsed as JSON or checksum is invalid.
|
|
567
546
|
* Error context includes: oid, reason ('invalid_format' or 'invalid_checksum'), originalError
|
|
568
547
|
* @throws {ShardValidationError} If the chunk has an unsupported version.
|
|
@@ -574,7 +553,7 @@ export default class StreamingBitmapIndexBuilder {
|
|
|
574
553
|
const buffer = await this.storage.readBlob(oid);
|
|
575
554
|
let envelope;
|
|
576
555
|
try {
|
|
577
|
-
envelope = JSON.parse(
|
|
556
|
+
envelope = JSON.parse(new TextDecoder().decode(buffer));
|
|
578
557
|
} catch (err) {
|
|
579
558
|
throw new ShardCorruptionError('Failed to parse shard JSON', {
|
|
580
559
|
oid,
|
|
@@ -615,12 +594,7 @@ export default class StreamingBitmapIndexBuilder {
|
|
|
615
594
|
* is stored directly. If a bitmap already exists, the new bitmap is ORed into
|
|
616
595
|
* it using `orInPlace` to combine edge sets.
|
|
617
596
|
*
|
|
618
|
-
* @param {
|
|
619
|
-
* @param {Record<string, import('../utils/roaring.js').RoaringBitmapSubset>} opts.merged - Object mapping SHA to
|
|
620
|
-
* RoaringBitmap32 instances (mutated in place)
|
|
621
|
-
* @param {string} opts.sha - The SHA key for this bitmap (40-character hex string)
|
|
622
|
-
* @param {string} opts.base64Bitmap - Base64-encoded serialized RoaringBitmap32 data
|
|
623
|
-
* @param {string} opts.oid - Git blob OID of the source chunk (for error reporting)
|
|
597
|
+
* @param {{ merged: Record<string, import('../utils/roaring.js').RoaringBitmapSubset>, sha: string, base64Bitmap: string, oid: string }} opts - Options object
|
|
624
598
|
* @throws {ShardCorruptionError} If the bitmap cannot be deserialized from base64.
|
|
625
599
|
* Error context includes: oid, reason ('invalid_bitmap'), originalError
|
|
626
600
|
* @private
|
|
@@ -662,9 +636,7 @@ export default class StreamingBitmapIndexBuilder {
|
|
|
662
636
|
* Supports cancellation via AbortSignal between chunk processing iterations.
|
|
663
637
|
*
|
|
664
638
|
* @param {string[]} oids - Git blob OIDs of chunks to merge (40-character hex strings)
|
|
665
|
-
* @param {
|
|
666
|
-
* @param {AbortSignal} [options.signal] - Optional AbortSignal for cancellation.
|
|
667
|
-
* Checked between chunk iterations; if aborted, throws with code 'ABORT_ERR'.
|
|
639
|
+
* @param {{ signal?: AbortSignal }} [options] - Options object
|
|
668
640
|
* @returns {Promise<string>} Git blob OID of the merged shard (40-character hex string)
|
|
669
641
|
* @throws {Error} If the operation is aborted via signal
|
|
670
642
|
* @throws {ShardValidationError} If a chunk has an unsupported version.
|
|
@@ -38,14 +38,7 @@ export function canonicalizePath(url) {
|
|
|
38
38
|
/**
|
|
39
39
|
* Builds the canonical string that gets signed.
|
|
40
40
|
*
|
|
41
|
-
* @param {
|
|
42
|
-
* @param {string} params.keyId - Key identifier
|
|
43
|
-
* @param {string} params.method - HTTP method (uppercased by caller)
|
|
44
|
-
* @param {string} params.path - Canonical path
|
|
45
|
-
* @param {string} params.timestamp - Epoch milliseconds as string
|
|
46
|
-
* @param {string} params.nonce - UUIDv4 nonce
|
|
47
|
-
* @param {string} params.contentType - Content-Type header value
|
|
48
|
-
* @param {string} params.bodySha256 - Hex SHA-256 of request body
|
|
41
|
+
* @param {{ keyId: string, method: string, path: string, timestamp: string, nonce: string, contentType: string, bodySha256: string }} params
|
|
49
42
|
* @returns {string} Pipe-delimited canonical payload
|
|
50
43
|
*/
|
|
51
44
|
export function buildCanonicalPayload({ keyId, method, path, timestamp, nonce, contentType, bodySha256 }) {
|
|
@@ -55,15 +48,8 @@ export function buildCanonicalPayload({ keyId, method, path, timestamp, nonce, c
|
|
|
55
48
|
/**
|
|
56
49
|
* Signs an outgoing sync request.
|
|
57
50
|
*
|
|
58
|
-
* @param {
|
|
59
|
-
* @param {
|
|
60
|
-
* @param {string} params.path - Canonical path
|
|
61
|
-
* @param {string} params.contentType - Content-Type header value
|
|
62
|
-
* @param {Buffer|Uint8Array} params.body - Raw request body
|
|
63
|
-
* @param {string} params.secret - Shared secret
|
|
64
|
-
* @param {string} params.keyId - Key identifier
|
|
65
|
-
* @param {Object} deps
|
|
66
|
-
* @param {import('../../ports/CryptoPort.js').default} [deps.crypto] - Crypto port
|
|
51
|
+
* @param {{ method: string, path: string, contentType: string, body: Buffer|Uint8Array, secret: string, keyId: string }} params
|
|
52
|
+
* @param {{ crypto?: import('../../ports/CryptoPort.js').default }} [deps]
|
|
67
53
|
* @returns {Promise<Record<string, string>>} Auth headers
|
|
68
54
|
*/
|
|
69
55
|
export async function signSyncRequest({ method, path, contentType, body, secret, keyId }, { crypto } = {}) {
|
|
@@ -173,15 +159,7 @@ function _validateAllowedWriters(allowedWriters) {
|
|
|
173
159
|
|
|
174
160
|
export default class SyncAuthService {
|
|
175
161
|
/**
|
|
176
|
-
* @param {
|
|
177
|
-
* @param {Record<string, string>} options.keys - Key-id to secret mapping
|
|
178
|
-
* @param {'enforce'|'log-only'} [options.mode='enforce'] - Auth enforcement mode
|
|
179
|
-
* @param {number} [options.nonceCapacity] - Nonce LRU capacity
|
|
180
|
-
* @param {number} [options.maxClockSkewMs] - Max clock skew tolerance
|
|
181
|
-
* @param {import('../../ports/CryptoPort.js').default} [options.crypto] - Crypto port
|
|
182
|
-
* @param {import('../../ports/LoggerPort.js').default} [options.logger] - Logger port
|
|
183
|
-
* @param {() => number} [options.wallClockMs] - Wall clock function
|
|
184
|
-
* @param {string[]} [options.allowedWriters] - Optional whitelist of allowed writer IDs. If set, sync requests with unlisted writers are rejected with 403.
|
|
162
|
+
* @param {{ keys: Record<string, string>, mode?: 'enforce'|'log-only', nonceCapacity?: number, maxClockSkewMs?: number, crypto?: import('../../ports/CryptoPort.js').default, logger?: import('../../ports/LoggerPort.js').default, wallClockMs?: () => number, allowedWriters?: string[] }} options
|
|
185
163
|
*/
|
|
186
164
|
constructor({ keys, mode = 'enforce', nonceCapacity, maxClockSkewMs, crypto, logger, wallClockMs, allowedWriters } = /** @type {{ keys: Record<string, string> }} */ ({})) {
|
|
187
165
|
_validateKeys(keys);
|
|
@@ -289,12 +267,7 @@ export default class SyncAuthService {
|
|
|
289
267
|
/**
|
|
290
268
|
* Verifies the HMAC signature against the canonical payload.
|
|
291
269
|
*
|
|
292
|
-
* @param {
|
|
293
|
-
* @param {{ method: string, url: string, headers: Record<string, string>, body?: Buffer|Uint8Array }} params.request
|
|
294
|
-
* @param {string} params.secret
|
|
295
|
-
* @param {string} params.keyId
|
|
296
|
-
* @param {string} params.timestamp
|
|
297
|
-
* @param {string} params.nonce
|
|
270
|
+
* @param {{ request: { method: string, url: string, headers: Record<string, string>, body?: Buffer|Uint8Array }, secret: string, keyId: string, timestamp: string, nonce: string }} params
|
|
298
271
|
* @returns {Promise<{ ok: false, reason: string, status: number } | { ok: true }>}
|
|
299
272
|
* @private
|
|
300
273
|
*/
|
|
@@ -98,11 +98,7 @@ function normalizeSyncPath(path) {
|
|
|
98
98
|
/**
|
|
99
99
|
* Builds auth headers for an outgoing sync request if auth is configured.
|
|
100
100
|
*
|
|
101
|
-
* @param {
|
|
102
|
-
* @param {{ secret: string, keyId?: string }|undefined} params.auth
|
|
103
|
-
* @param {string} params.bodyStr - Serialized request body
|
|
104
|
-
* @param {URL} params.targetUrl
|
|
105
|
-
* @param {import('../../ports/CryptoPort.js').default} params.crypto
|
|
101
|
+
* @param {{ auth: ({ secret: string, keyId?: string }|undefined), bodyStr: string, targetUrl: URL, crypto: import('../../ports/CryptoPort.js').default }} params
|
|
106
102
|
* @returns {Promise<Record<string, string>>}
|
|
107
103
|
*/
|
|
108
104
|
async function buildSyncAuthHeaders({ auth, bodyStr, targetUrl, crypto }) {
|
|
@@ -131,8 +127,7 @@ async function buildSyncAuthHeaders({ auth, bodyStr, targetUrl, crypto }) {
|
|
|
131
127
|
export default class SyncController {
|
|
132
128
|
/**
|
|
133
129
|
* @param {SyncHost} host - The WarpGraph instance (or any object satisfying SyncHost)
|
|
134
|
-
* @param {
|
|
135
|
-
* @param {SyncTrustGate} [options.trustGate] - Trust gate for evaluating patch authors
|
|
130
|
+
* @param {{ trustGate?: SyncTrustGate }} [options]
|
|
136
131
|
*/
|
|
137
132
|
constructor(host, options = {}) {
|
|
138
133
|
/** @type {SyncHost} */
|
|
@@ -367,16 +362,7 @@ export default class SyncController {
|
|
|
367
362
|
* Syncs with a remote peer (HTTP or direct graph instance).
|
|
368
363
|
*
|
|
369
364
|
* @param {string|import('../WarpGraph.js').default} remote - URL or peer graph instance
|
|
370
|
-
* @param {
|
|
371
|
-
* @param {string} [options.path='/sync'] - Sync path (HTTP mode)
|
|
372
|
-
* @param {number} [options.retries=3] - Retry count
|
|
373
|
-
* @param {number} [options.baseDelayMs=250] - Base backoff delay
|
|
374
|
-
* @param {number} [options.maxDelayMs=2000] - Max backoff delay
|
|
375
|
-
* @param {number} [options.timeoutMs=10000] - Request timeout
|
|
376
|
-
* @param {AbortSignal} [options.signal] - Abort signal
|
|
377
|
-
* @param {(event: {type: string, attempt: number, durationMs?: number, status?: number, error?: Error}) => void} [options.onStatus]
|
|
378
|
-
* @param {boolean} [options.materialize=false] - Auto-materialize after sync
|
|
379
|
-
* @param {{ secret: string, keyId?: string }} [options.auth] - Client auth credentials
|
|
365
|
+
* @param {{ path?: string, retries?: number, baseDelayMs?: number, maxDelayMs?: number, timeoutMs?: number, signal?: AbortSignal, onStatus?: (event: {type: string, attempt: number, durationMs?: number, status?: number, error?: Error}) => void, materialize?: boolean, auth?: { secret: string, keyId?: string } }} [options]
|
|
380
366
|
* @returns {Promise<{applied: number, attempts: number, skippedWriters: Array<{writerId: string, reason: string, localSha: string, remoteSha: string|null}>, state?: import('./JoinReducer.js').WarpStateV5}>}
|
|
381
367
|
*/
|
|
382
368
|
async syncWith(remote, options = {}) {
|
|
@@ -583,13 +569,7 @@ export default class SyncController {
|
|
|
583
569
|
/**
|
|
584
570
|
* Starts a built-in sync server for this graph.
|
|
585
571
|
*
|
|
586
|
-
* @param {
|
|
587
|
-
* @param {number} options.port - Port to listen on
|
|
588
|
-
* @param {string} [options.host='127.0.0.1'] - Host to bind
|
|
589
|
-
* @param {string} [options.path='/sync'] - Path to handle sync requests
|
|
590
|
-
* @param {number} [options.maxRequestBytes=4194304] - Max request size in bytes
|
|
591
|
-
* @param {import('../../ports/HttpServerPort.js').default} options.httpPort - HTTP server adapter
|
|
592
|
-
* @param {{ keys: Record<string, string>, mode?: 'enforce'|'log-only' }} [options.auth] - Auth configuration
|
|
572
|
+
* @param {{ port: number, host?: string, path?: string, maxRequestBytes?: number, httpPort: import('../../ports/HttpServerPort.js').default, auth?: { keys: Record<string, string>, mode?: 'enforce'|'log-only' } }} options
|
|
593
573
|
* @returns {Promise<{close: () => Promise<void>, url: string}>} Server handle
|
|
594
574
|
* @throws {Error} If port is not a number
|
|
595
575
|
* @throws {Error} If httpPort adapter is not provided
|
|
@@ -608,7 +588,7 @@ export default class SyncController {
|
|
|
608
588
|
|
|
609
589
|
const httpServer = new HttpSyncServer({
|
|
610
590
|
httpPort,
|
|
611
|
-
graph: /** @type {{ processSyncRequest:
|
|
591
|
+
graph: /** @type {{ processSyncRequest: (req: import('./SyncProtocol.js').SyncRequest) => Promise<unknown> }} */ (/** @type {unknown} */ (this._host)),
|
|
612
592
|
path,
|
|
613
593
|
host,
|
|
614
594
|
maxRequestBytes,
|
|
@@ -125,8 +125,7 @@ function objectToFrontier(obj) {
|
|
|
125
125
|
* @param {import('../../ports/GraphPersistencePort.js').default & import('../../ports/CommitPort.js').default & import('../../ports/BlobPort.js').default} persistence - Git persistence layer
|
|
126
126
|
* (uses CommitPort.showNode() + BlobPort.readBlob() methods)
|
|
127
127
|
* @param {string} sha - The 40-character commit SHA to load the patch from
|
|
128
|
-
* @param {
|
|
129
|
-
* @param {import('../../ports/CodecPort.js').default} [options.codec] - Codec for deserialization
|
|
128
|
+
* @param {{ codec?: import('../../ports/CodecPort.js').default }} [options]
|
|
130
129
|
* @returns {Promise<DecodedPatch>} The decoded and normalized patch object containing:
|
|
131
130
|
* - `ops`: Array of patch operations
|
|
132
131
|
* - `context`: VersionVector (Map) of causal dependencies
|
|
@@ -173,8 +172,7 @@ async function loadPatchFromCommit(persistence, sha, { codec: codecOpt } = /** @
|
|
|
173
172
|
* @param {string|null} fromSha - Start SHA (exclusive). Pass null to load ALL patches
|
|
174
173
|
* for this writer from the beginning of their chain.
|
|
175
174
|
* @param {string} toSha - End SHA (inclusive). This is typically the writer's current tip.
|
|
176
|
-
* @param {
|
|
177
|
-
* @param {import('../../ports/CodecPort.js').default} [options.codec] - Codec for deserialization
|
|
175
|
+
* @param {{ codec?: import('../../ports/CodecPort.js').default }} [options]
|
|
178
176
|
* @returns {Promise<Array<{patch: DecodedPatch, sha: string}>>} Array of patch objects in
|
|
179
177
|
* chronological order (oldest first). Each entry contains:
|
|
180
178
|
* - `patch`: The decoded patch object
|
|
@@ -396,9 +394,7 @@ export function createSyncRequest(frontier) {
|
|
|
396
394
|
* @param {import('../../ports/GraphPersistencePort.js').default & import('../../ports/CommitPort.js').default & import('../../ports/BlobPort.js').default} persistence - Git persistence
|
|
397
395
|
* layer for loading patches (uses CommitPort + BlobPort methods)
|
|
398
396
|
* @param {string} graphName - Graph name for error messages and logging
|
|
399
|
-
* @param {
|
|
400
|
-
* @param {import('../../ports/CodecPort.js').default} [options.codec] - Codec for deserialization
|
|
401
|
-
* @param {import('../../ports/LoggerPort.js').default} [options.logger] - Logger for divergence warnings
|
|
397
|
+
* @param {{ codec?: import('../../ports/CodecPort.js').default, logger?: import('../../ports/LoggerPort.js').default }} [options]
|
|
402
398
|
* @returns {Promise<SyncResponse>} Response containing local frontier and patches.
|
|
403
399
|
* Patches are ordered chronologically within each writer.
|
|
404
400
|
* @throws {Error} If patch loading fails for reasons other than divergence
|
|
@@ -518,7 +514,7 @@ export async function processSyncRequest(request, localFrontier, persistence, gr
|
|
|
518
514
|
* @param {import('./JoinReducer.js').WarpStateV5} state - Current CRDT state
|
|
519
515
|
* (nodeAlive, edgeAlive, prop, observedFrontier)
|
|
520
516
|
* @param {Map<string, string>} frontier - Current frontier mapping writer IDs to SHAs
|
|
521
|
-
* @returns {
|
|
517
|
+
* @returns {{state: import('./JoinReducer.js').WarpStateV5, frontier: Map<string, string>, applied: number}} Result containing:
|
|
522
518
|
* - `state`: New WarpStateV5 with patches applied
|
|
523
519
|
* - `frontier`: New frontier with updated writer tips
|
|
524
520
|
* - `applied`: Number of patches successfully applied
|
|
@@ -30,10 +30,7 @@ const PASS = () => ({ allowed: true, untrustedWriters: [], verdict: 'pass' });
|
|
|
30
30
|
|
|
31
31
|
export default class SyncTrustGate {
|
|
32
32
|
/**
|
|
33
|
-
* @param {
|
|
34
|
-
* @param {{evaluateWriters: (writerIds: string[]) => Promise<{trusted: Set<string>}>}} [options.trustEvaluator] - Trust evaluator instance
|
|
35
|
-
* @param {TrustMode} [options.trustMode='off'] - Trust enforcement mode
|
|
36
|
-
* @param {import('../../ports/LoggerPort.js').default} [options.logger] - Logger
|
|
33
|
+
* @param {{ trustEvaluator?: {evaluateWriters: (writerIds: string[]) => Promise<{trusted: Set<string>}>}, trustMode?: TrustMode, logger?: import('../../ports/LoggerPort.js').default }} [options]
|
|
37
34
|
*/
|
|
38
35
|
constructor({ trustEvaluator, trustMode = 'off', logger } = {}) {
|
|
39
36
|
this._evaluator = trustEvaluator || null;
|
|
@@ -45,9 +42,7 @@ export default class SyncTrustGate {
|
|
|
45
42
|
* Evaluates whether the given patch writers are trusted.
|
|
46
43
|
*
|
|
47
44
|
* @param {string[]} writerIds - Writer IDs from patches being applied
|
|
48
|
-
* @param {
|
|
49
|
-
* @param {string} [context.graphName] - Graph name
|
|
50
|
-
* @param {string} [context.peerId] - Remote peer identity (if authenticated)
|
|
45
|
+
* @param {{ graphName?: string, peerId?: string }} [context] - Additional context for logging
|
|
51
46
|
* @returns {Promise<TrustGateResult>}
|
|
52
47
|
*/
|
|
53
48
|
async evaluate(writerIds, context = {}) {
|
|
@@ -71,7 +66,7 @@ export default class SyncTrustGate {
|
|
|
71
66
|
* Decides the gate result based on untrusted writers and mode.
|
|
72
67
|
* @param {string[]} untrusted
|
|
73
68
|
* @param {string[]} writerIds
|
|
74
|
-
* @param {
|
|
69
|
+
* @param {Record<string, unknown>} context
|
|
75
70
|
* @returns {TrustGateResult}
|
|
76
71
|
* @private
|
|
77
72
|
*/
|
|
@@ -110,7 +105,7 @@ export default class SyncTrustGate {
|
|
|
110
105
|
* Handles trust evaluation errors with fail-open/fail-closed semantics.
|
|
111
106
|
* @param {unknown} err
|
|
112
107
|
* @param {string[]} writerIds
|
|
113
|
-
* @param {
|
|
108
|
+
* @param {Record<string, unknown>} context
|
|
114
109
|
* @returns {TrustGateResult}
|
|
115
110
|
* @private
|
|
116
111
|
*/
|
|
@@ -86,12 +86,7 @@ function extractNodeSnapshot(state, nodeId) {
|
|
|
86
86
|
/**
|
|
87
87
|
* Evaluates checkpoint boundary semantics for `always()`.
|
|
88
88
|
*
|
|
89
|
-
* @param {
|
|
90
|
-
* @param {import('./JoinReducer.js').WarpStateV5} params.state
|
|
91
|
-
* @param {string} params.nodeId
|
|
92
|
-
* @param {Function} params.predicate
|
|
93
|
-
* @param {number|null} params.checkpointMaxLamport
|
|
94
|
-
* @param {number} params.since
|
|
89
|
+
* @param {{ state: import('./JoinReducer.js').WarpStateV5, nodeId: string, predicate: (snapshot: {id: string, exists: boolean, props: Record<string, unknown>}) => boolean, checkpointMaxLamport: number|null, since: number }} params
|
|
95
90
|
* @returns {{ nodeEverExisted: boolean, shouldReturn: boolean, returnValue: boolean }}
|
|
96
91
|
* @private
|
|
97
92
|
*/
|
|
@@ -118,12 +113,7 @@ function evaluateAlwaysCheckpointBoundary({
|
|
|
118
113
|
/**
|
|
119
114
|
* Evaluates checkpoint boundary semantics for `eventually()`.
|
|
120
115
|
*
|
|
121
|
-
* @param {
|
|
122
|
-
* @param {import('./JoinReducer.js').WarpStateV5} params.state
|
|
123
|
-
* @param {string} params.nodeId
|
|
124
|
-
* @param {Function} params.predicate
|
|
125
|
-
* @param {number|null} params.checkpointMaxLamport
|
|
126
|
-
* @param {number} params.since
|
|
116
|
+
* @param {{ state: import('./JoinReducer.js').WarpStateV5, nodeId: string, predicate: (snapshot: {id: string, exists: boolean, props: Record<string, unknown>}) => boolean, checkpointMaxLamport: number|null, since: number }} params
|
|
127
117
|
* @returns {boolean}
|
|
128
118
|
* @private
|
|
129
119
|
*/
|
|
@@ -149,16 +139,12 @@ function evaluateEventuallyCheckpointBoundary({
|
|
|
149
139
|
*/
|
|
150
140
|
export class TemporalQuery {
|
|
151
141
|
/**
|
|
152
|
-
* @param {
|
|
153
|
-
* @param {Function} options.loadAllPatches - Async function that returns
|
|
154
|
-
* all patches as Array<{ patch, sha }> in causal order.
|
|
155
|
-
* @param {Function} [options.loadCheckpoint] - Async function returning
|
|
156
|
-
* { state: WarpStateV5, maxLamport: number } or null.
|
|
142
|
+
* @param {{ loadAllPatches: () => Promise<Array<{patch: import('../types/WarpTypesV2.js').PatchV2, sha: string}>>, loadCheckpoint?: () => Promise<{state: import('./JoinReducer.js').WarpStateV5, maxLamport: number}|null> }} options
|
|
157
143
|
*/
|
|
158
144
|
constructor({ loadAllPatches, loadCheckpoint }) {
|
|
159
|
-
/** @type {
|
|
145
|
+
/** @type {() => Promise<Array<{patch: import('../types/WarpTypesV2.js').PatchV2, sha: string}>>} */
|
|
160
146
|
this._loadAllPatches = loadAllPatches;
|
|
161
|
-
/** @type {
|
|
147
|
+
/** @type {(() => Promise<{state: import('./JoinReducer.js').WarpStateV5, maxLamport: number}|null>)|null} */
|
|
162
148
|
this._loadCheckpoint = loadCheckpoint || null;
|
|
163
149
|
}
|
|
164
150
|
|
|
@@ -172,11 +158,9 @@ export class TemporalQuery {
|
|
|
172
158
|
* Returns false if the node never existed in the range.
|
|
173
159
|
*
|
|
174
160
|
* @param {string} nodeId - The node ID to evaluate
|
|
175
|
-
* @param {
|
|
161
|
+
* @param {(snapshot: {id: string, exists: boolean, props: Record<string, unknown>}) => boolean} predicate - Predicate receiving node snapshot
|
|
176
162
|
* `{ id, exists, props }`. Should return boolean.
|
|
177
|
-
* @param {
|
|
178
|
-
* @param {number} [options.since=0] - Minimum Lamport tick (inclusive).
|
|
179
|
-
* Only patches with lamport >= since are considered.
|
|
163
|
+
* @param {{ since?: number }} [options={}] - Options
|
|
180
164
|
* @returns {Promise<boolean>} True if predicate held at every tick
|
|
181
165
|
*
|
|
182
166
|
* @example
|
|
@@ -232,11 +216,9 @@ export class TemporalQuery {
|
|
|
232
216
|
* soon as the predicate returns true at any tick.
|
|
233
217
|
*
|
|
234
218
|
* @param {string} nodeId - The node ID to evaluate
|
|
235
|
-
* @param {
|
|
219
|
+
* @param {(snapshot: {id: string, exists: boolean, props: Record<string, unknown>}) => boolean} predicate - Predicate receiving node snapshot
|
|
236
220
|
* `{ id, exists, props }`. Should return boolean.
|
|
237
|
-
* @param {
|
|
238
|
-
* @param {number} [options.since=0] - Minimum Lamport tick (inclusive).
|
|
239
|
-
* Only patches with lamport >= since are considered.
|
|
221
|
+
* @param {{ since?: number }} [options={}] - Options
|
|
240
222
|
* @returns {Promise<boolean>} True if predicate held at any tick
|
|
241
223
|
*
|
|
242
224
|
* @example
|
|
@@ -169,14 +169,8 @@ function computePropLoss(state, { nodesA, nodesBSet, configA, configB }) {
|
|
|
169
169
|
* The cost measures how much information is lost when translating from
|
|
170
170
|
* A's view to B's view. It is asymmetric: cost(A->B) != cost(B->A) in general.
|
|
171
171
|
*
|
|
172
|
-
* @param {
|
|
173
|
-
* @param {string|string[]}
|
|
174
|
-
* @param {string[]} [configA.expose] - Property keys to include
|
|
175
|
-
* @param {string[]} [configA.redact] - Property keys to exclude
|
|
176
|
-
* @param {Object} configB - Observer configuration for B
|
|
177
|
-
* @param {string|string[]} configB.match - Glob pattern(s) for visible nodes
|
|
178
|
-
* @param {string[]} [configB.expose] - Property keys to include
|
|
179
|
-
* @param {string[]} [configB.redact] - Property keys to exclude
|
|
172
|
+
* @param {{ match: string|string[], expose?: string[], redact?: string[] }} configA - Observer configuration for A
|
|
173
|
+
* @param {{ match: string|string[], expose?: string[], redact?: string[] }} configB - Observer configuration for B
|
|
180
174
|
* @param {WarpStateV5} state - WarpStateV5 materialized state
|
|
181
175
|
* @returns {{ cost: number, breakdown: { nodeLoss: number, edgeLoss: number, propLoss: number } }}
|
|
182
176
|
*/
|
|
@@ -30,8 +30,7 @@ import { decodeEdgeKey } from './KeyCodec.js';
|
|
|
30
30
|
export default class WarpStateIndexBuilder {
|
|
31
31
|
/**
|
|
32
32
|
* Creates a new WarpStateIndexBuilder.
|
|
33
|
-
* @param {
|
|
34
|
-
* @param {import('../../ports/CryptoPort.js').default} [options.crypto] - CryptoPort for shard checksums
|
|
33
|
+
* @param {{ crypto?: import('../../ports/CryptoPort.js').default }} [options] - Configuration
|
|
35
34
|
*/
|
|
36
35
|
constructor({ crypto } = {}) {
|
|
37
36
|
/** @type {BitmapIndexBuilder} */
|
|
@@ -109,8 +108,7 @@ export default class WarpStateIndexBuilder {
|
|
|
109
108
|
* Convenience function to build and serialize a WARP state index.
|
|
110
109
|
*
|
|
111
110
|
* @param {import('./JoinReducer.js').WarpStateV5} state - The materialized state
|
|
112
|
-
* @param {
|
|
113
|
-
* @param {import('../../ports/CryptoPort.js').default} [options.crypto] - CryptoPort for shard checksums
|
|
111
|
+
* @param {{ crypto?: import('../../ports/CryptoPort.js').default }} [options] - Configuration
|
|
114
112
|
* @returns {Promise<{tree: Record<string, Buffer>, stats: {nodes: number, edges: number}}>} Serialized index and stats
|
|
115
113
|
*
|
|
116
114
|
* @example
|