@git-stunts/git-warp 11.5.0 → 12.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +145 -1
- package/bin/cli/commands/registry.js +4 -0
- package/bin/cli/commands/reindex.js +41 -0
- package/bin/cli/commands/verify-index.js +59 -0
- package/bin/cli/infrastructure.js +7 -2
- package/bin/cli/schemas.js +19 -0
- package/bin/cli/types.js +2 -0
- package/index.d.ts +49 -12
- package/package.json +2 -2
- package/src/domain/WarpGraph.js +62 -2
- package/src/domain/errors/ShardIdOverflowError.js +28 -0
- package/src/domain/errors/index.js +1 -0
- package/src/domain/services/AdjacencyNeighborProvider.js +140 -0
- package/src/domain/services/BitmapIndexReader.js +32 -10
- package/src/domain/services/BitmapNeighborProvider.js +178 -0
- package/src/domain/services/CheckpointMessageCodec.js +3 -3
- package/src/domain/services/CheckpointService.js +77 -12
- package/src/domain/services/GraphTraversal.js +1239 -0
- package/src/domain/services/IncrementalIndexUpdater.js +765 -0
- package/src/domain/services/JoinReducer.js +310 -46
- package/src/domain/services/LogicalBitmapIndexBuilder.js +323 -0
- package/src/domain/services/LogicalIndexBuildService.js +108 -0
- package/src/domain/services/LogicalIndexReader.js +315 -0
- package/src/domain/services/LogicalTraversal.js +321 -202
- package/src/domain/services/MaterializedViewService.js +379 -0
- package/src/domain/services/ObserverView.js +138 -47
- package/src/domain/services/PatchBuilderV2.js +3 -3
- package/src/domain/services/PropertyIndexBuilder.js +64 -0
- package/src/domain/services/PropertyIndexReader.js +111 -0
- package/src/domain/services/SyncController.js +576 -0
- package/src/domain/services/TemporalQuery.js +128 -14
- package/src/domain/types/PatchDiff.js +90 -0
- package/src/domain/types/WarpTypesV2.js +4 -4
- package/src/domain/utils/MinHeap.js +45 -17
- package/src/domain/utils/canonicalCbor.js +36 -0
- package/src/domain/utils/fnv1a.js +20 -0
- package/src/domain/utils/roaring.js +14 -3
- package/src/domain/utils/shardKey.js +40 -0
- package/src/domain/utils/toBytes.js +17 -0
- package/src/domain/utils/validateShardOid.js +13 -0
- package/src/domain/warp/_internal.js +0 -9
- package/src/domain/warp/_wiredMethods.d.ts +8 -2
- package/src/domain/warp/checkpoint.methods.js +21 -5
- package/src/domain/warp/materialize.methods.js +17 -5
- package/src/domain/warp/materializeAdvanced.methods.js +142 -3
- package/src/domain/warp/query.methods.js +78 -12
- package/src/infrastructure/adapters/CasSeekCacheAdapter.js +26 -5
- package/src/ports/BlobPort.js +1 -1
- package/src/ports/NeighborProviderPort.js +59 -0
- package/src/ports/SeekCachePort.js +4 -3
- package/src/domain/warp/sync.methods.js +0 -554
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builds property index shards from node properties.
|
|
3
|
+
*
|
|
4
|
+
* Produces `props_XX.cbor` shards keyed by shard key, where each
|
|
5
|
+
* shard maps nodeId → { key: value, ... }.
|
|
6
|
+
*
|
|
7
|
+
* @module domain/services/PropertyIndexBuilder
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import defaultCodec from '../utils/defaultCodec.js';
|
|
11
|
+
import computeShardKey from '../utils/shardKey.js';
|
|
12
|
+
|
|
13
|
+
export default class PropertyIndexBuilder {
|
|
14
|
+
/**
|
|
15
|
+
* @param {Object} [options]
|
|
16
|
+
* @param {import('../../ports/CodecPort.js').default} [options.codec]
|
|
17
|
+
*/
|
|
18
|
+
constructor({ codec } = {}) {
|
|
19
|
+
this._codec = codec || defaultCodec;
|
|
20
|
+
/** @type {Map<string, Map<string, Record<string, unknown>>>} shardKey → (nodeId → props) */
|
|
21
|
+
this._shards = new Map();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Adds a property for a node.
|
|
26
|
+
*
|
|
27
|
+
* @param {string} nodeId
|
|
28
|
+
* @param {string} key
|
|
29
|
+
* @param {unknown} value
|
|
30
|
+
*/
|
|
31
|
+
addProperty(nodeId, key, value) {
|
|
32
|
+
const shardKey = computeShardKey(nodeId);
|
|
33
|
+
let shard = this._shards.get(shardKey);
|
|
34
|
+
if (!shard) {
|
|
35
|
+
shard = new Map();
|
|
36
|
+
this._shards.set(shardKey, shard);
|
|
37
|
+
}
|
|
38
|
+
let nodeProps = shard.get(nodeId);
|
|
39
|
+
if (!nodeProps) {
|
|
40
|
+
nodeProps = /** @type {Record<string, unknown>} */ (Object.create(null));
|
|
41
|
+
shard.set(nodeId, nodeProps);
|
|
42
|
+
}
|
|
43
|
+
/** @type {Record<string, unknown>} */ (nodeProps)[key] = value;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Serializes all property shards.
|
|
48
|
+
*
|
|
49
|
+
* @returns {Record<string, Uint8Array>}
|
|
50
|
+
*/
|
|
51
|
+
serialize() {
|
|
52
|
+
/** @type {Record<string, Uint8Array>} */
|
|
53
|
+
const tree = {};
|
|
54
|
+
for (const [shardKey, shard] of this._shards) {
|
|
55
|
+
// Encode as array of [nodeId, props] pairs to avoid __proto__ key issues
|
|
56
|
+
// when CBOR decodes into plain objects. Sorted by nodeId for determinism.
|
|
57
|
+
const entries = [...shard.entries()]
|
|
58
|
+
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
|
|
59
|
+
.map(([nodeId, props]) => [nodeId, props]);
|
|
60
|
+
tree[`props_${shardKey}.cbor`] = this._codec.encode(entries).slice();
|
|
61
|
+
}
|
|
62
|
+
return tree;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reads property index shards lazily with LRU caching.
|
|
3
|
+
*
|
|
4
|
+
* Loads `props_XX.cbor` shards on demand via IndexStoragePort.readBlob.
|
|
5
|
+
*
|
|
6
|
+
* @module domain/services/PropertyIndexReader
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import defaultCodec from '../utils/defaultCodec.js';
|
|
10
|
+
import computeShardKey from '../utils/shardKey.js';
|
|
11
|
+
import LRUCache from '../utils/LRUCache.js';
|
|
12
|
+
|
|
13
|
+
export default class PropertyIndexReader {
|
|
14
|
+
/**
|
|
15
|
+
* @param {Object} [options]
|
|
16
|
+
* @param {import('../../ports/IndexStoragePort.js').default} [options.storage]
|
|
17
|
+
* @param {import('../../ports/CodecPort.js').default} [options.codec]
|
|
18
|
+
* @param {number} [options.maxCachedShards=64]
|
|
19
|
+
*/
|
|
20
|
+
constructor({ storage, codec, maxCachedShards = 64 } = /** @type {{ storage?: import('../../ports/IndexStoragePort.js').default, codec?: import('../../ports/CodecPort.js').default, maxCachedShards?: number }} */ ({})) {
|
|
21
|
+
this._storage = storage;
|
|
22
|
+
this._codec = codec || defaultCodec;
|
|
23
|
+
/** @type {Map<string, string>} path → oid */
|
|
24
|
+
this._shardOids = new Map();
|
|
25
|
+
/** @type {LRUCache<string, Record<string, Record<string, unknown>>>} */
|
|
26
|
+
this._cache = new LRUCache(maxCachedShards);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Configures OID mappings for lazy loading.
|
|
31
|
+
*
|
|
32
|
+
* @param {Record<string, string>} shardOids - path → blob OID
|
|
33
|
+
*/
|
|
34
|
+
setup(shardOids) {
|
|
35
|
+
this._shardOids = new Map(Object.entries(shardOids));
|
|
36
|
+
this._cache.clear();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Returns all properties for a node, or null if not found.
|
|
41
|
+
*
|
|
42
|
+
* @param {string} nodeId
|
|
43
|
+
* @returns {Promise<Record<string, unknown>|null>}
|
|
44
|
+
*/
|
|
45
|
+
async getNodeProps(nodeId) {
|
|
46
|
+
const shard = await this._loadShard(nodeId);
|
|
47
|
+
if (!shard) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
return shard[nodeId] ?? null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Returns a single property value, or undefined.
|
|
55
|
+
*
|
|
56
|
+
* @param {string} nodeId
|
|
57
|
+
* @param {string} key
|
|
58
|
+
* @returns {Promise<unknown|undefined>}
|
|
59
|
+
*/
|
|
60
|
+
async getProperty(nodeId, key) {
|
|
61
|
+
const props = await this.getNodeProps(nodeId);
|
|
62
|
+
if (!props) {
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
return props[key];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* @param {string} nodeId
|
|
70
|
+
* @returns {Promise<Record<string, Record<string, unknown>>|null>}
|
|
71
|
+
* @private
|
|
72
|
+
*/
|
|
73
|
+
async _loadShard(nodeId) {
|
|
74
|
+
const shardKey = computeShardKey(nodeId);
|
|
75
|
+
const path = `props_${shardKey}.cbor`;
|
|
76
|
+
|
|
77
|
+
const cached = this._cache.get(path);
|
|
78
|
+
if (cached !== undefined) {
|
|
79
|
+
return cached;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const oid = this._shardOids.get(path);
|
|
83
|
+
if (!oid) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (!this._storage) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const buffer = await /** @type {{ readBlob(oid: string): Promise<Buffer|Uint8Array|undefined|null> }} */ (this._storage).readBlob(oid);
|
|
92
|
+
if (buffer === null || buffer === undefined) {
|
|
93
|
+
throw new Error(`PropertyIndexReader: missing blob for OID '${oid}' (${path})`);
|
|
94
|
+
}
|
|
95
|
+
const decoded = this._codec.decode(buffer);
|
|
96
|
+
|
|
97
|
+
// Shards are stored as array of [nodeId, props] pairs (proto-safe)
|
|
98
|
+
if (!Array.isArray(decoded)) {
|
|
99
|
+
const shape = decoded === null ? 'null' : typeof decoded;
|
|
100
|
+
throw new Error(`PropertyIndexReader: invalid shard format for '${path}' (expected array, got ${shape})`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** @type {Record<string, Record<string, unknown>>} */
|
|
104
|
+
const data = Object.create(null);
|
|
105
|
+
for (const [nid, props] of /** @type {Array<[string, Record<string, unknown>]>} */ (decoded)) {
|
|
106
|
+
data[nid] = props;
|
|
107
|
+
}
|
|
108
|
+
this._cache.set(path, data);
|
|
109
|
+
return data;
|
|
110
|
+
}
|
|
111
|
+
}
|