@git-stunts/git-warp 10.1.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/LICENSE +201 -0
- package/NOTICE +16 -0
- package/README.md +480 -0
- package/SECURITY.md +30 -0
- package/bin/git-warp +24 -0
- package/bin/warp-graph.js +1574 -0
- package/index.d.ts +2366 -0
- package/index.js +180 -0
- package/package.json +129 -0
- package/scripts/install-git-warp.sh +258 -0
- package/scripts/uninstall-git-warp.sh +139 -0
- package/src/domain/WarpGraph.js +3157 -0
- package/src/domain/crdt/Dot.js +160 -0
- package/src/domain/crdt/LWW.js +154 -0
- package/src/domain/crdt/ORSet.js +371 -0
- package/src/domain/crdt/VersionVector.js +222 -0
- package/src/domain/entities/GraphNode.js +60 -0
- package/src/domain/errors/EmptyMessageError.js +47 -0
- package/src/domain/errors/ForkError.js +30 -0
- package/src/domain/errors/IndexError.js +23 -0
- package/src/domain/errors/OperationAbortedError.js +22 -0
- package/src/domain/errors/QueryError.js +39 -0
- package/src/domain/errors/SchemaUnsupportedError.js +17 -0
- package/src/domain/errors/ShardCorruptionError.js +56 -0
- package/src/domain/errors/ShardLoadError.js +57 -0
- package/src/domain/errors/ShardValidationError.js +61 -0
- package/src/domain/errors/StorageError.js +57 -0
- package/src/domain/errors/SyncError.js +30 -0
- package/src/domain/errors/TraversalError.js +23 -0
- package/src/domain/errors/WarpError.js +31 -0
- package/src/domain/errors/WormholeError.js +28 -0
- package/src/domain/errors/WriterError.js +39 -0
- package/src/domain/errors/index.js +21 -0
- package/src/domain/services/AnchorMessageCodec.js +99 -0
- package/src/domain/services/BitmapIndexBuilder.js +225 -0
- package/src/domain/services/BitmapIndexReader.js +435 -0
- package/src/domain/services/BoundaryTransitionRecord.js +463 -0
- package/src/domain/services/CheckpointMessageCodec.js +147 -0
- package/src/domain/services/CheckpointSerializerV5.js +281 -0
- package/src/domain/services/CheckpointService.js +384 -0
- package/src/domain/services/CommitDagTraversalService.js +156 -0
- package/src/domain/services/DagPathFinding.js +712 -0
- package/src/domain/services/DagTopology.js +239 -0
- package/src/domain/services/DagTraversal.js +245 -0
- package/src/domain/services/Frontier.js +108 -0
- package/src/domain/services/GCMetrics.js +101 -0
- package/src/domain/services/GCPolicy.js +122 -0
- package/src/domain/services/GitLogParser.js +205 -0
- package/src/domain/services/HealthCheckService.js +246 -0
- package/src/domain/services/HookInstaller.js +326 -0
- package/src/domain/services/HttpSyncServer.js +262 -0
- package/src/domain/services/IndexRebuildService.js +426 -0
- package/src/domain/services/IndexStalenessChecker.js +103 -0
- package/src/domain/services/JoinReducer.js +582 -0
- package/src/domain/services/KeyCodec.js +113 -0
- package/src/domain/services/LegacyAnchorDetector.js +67 -0
- package/src/domain/services/LogicalTraversal.js +351 -0
- package/src/domain/services/MessageCodecInternal.js +132 -0
- package/src/domain/services/MessageSchemaDetector.js +145 -0
- package/src/domain/services/MigrationService.js +55 -0
- package/src/domain/services/ObserverView.js +265 -0
- package/src/domain/services/PatchBuilderV2.js +669 -0
- package/src/domain/services/PatchMessageCodec.js +140 -0
- package/src/domain/services/ProvenanceIndex.js +337 -0
- package/src/domain/services/ProvenancePayload.js +242 -0
- package/src/domain/services/QueryBuilder.js +835 -0
- package/src/domain/services/StateDiff.js +300 -0
- package/src/domain/services/StateSerializerV5.js +156 -0
- package/src/domain/services/StreamingBitmapIndexBuilder.js +709 -0
- package/src/domain/services/SyncProtocol.js +593 -0
- package/src/domain/services/TemporalQuery.js +201 -0
- package/src/domain/services/TranslationCost.js +221 -0
- package/src/domain/services/TraversalService.js +8 -0
- package/src/domain/services/WarpMessageCodec.js +29 -0
- package/src/domain/services/WarpStateIndexBuilder.js +127 -0
- package/src/domain/services/WormholeService.js +353 -0
- package/src/domain/types/TickReceipt.js +285 -0
- package/src/domain/types/WarpTypes.js +209 -0
- package/src/domain/types/WarpTypesV2.js +200 -0
- package/src/domain/utils/CachedValue.js +140 -0
- package/src/domain/utils/EventId.js +89 -0
- package/src/domain/utils/LRUCache.js +112 -0
- package/src/domain/utils/MinHeap.js +114 -0
- package/src/domain/utils/RefLayout.js +280 -0
- package/src/domain/utils/WriterId.js +205 -0
- package/src/domain/utils/cancellation.js +33 -0
- package/src/domain/utils/canonicalStringify.js +42 -0
- package/src/domain/utils/defaultClock.js +20 -0
- package/src/domain/utils/defaultCodec.js +51 -0
- package/src/domain/utils/nullLogger.js +21 -0
- package/src/domain/utils/roaring.js +181 -0
- package/src/domain/utils/shardVersion.js +9 -0
- package/src/domain/warp/PatchSession.js +217 -0
- package/src/domain/warp/Writer.js +181 -0
- package/src/hooks/post-merge.sh +60 -0
- package/src/infrastructure/adapters/BunHttpAdapter.js +225 -0
- package/src/infrastructure/adapters/ClockAdapter.js +57 -0
- package/src/infrastructure/adapters/ConsoleLogger.js +150 -0
- package/src/infrastructure/adapters/DenoHttpAdapter.js +230 -0
- package/src/infrastructure/adapters/GitGraphAdapter.js +787 -0
- package/src/infrastructure/adapters/GlobalClockAdapter.js +5 -0
- package/src/infrastructure/adapters/NoOpLogger.js +62 -0
- package/src/infrastructure/adapters/NodeCryptoAdapter.js +32 -0
- package/src/infrastructure/adapters/NodeHttpAdapter.js +98 -0
- package/src/infrastructure/adapters/PerformanceClockAdapter.js +5 -0
- package/src/infrastructure/adapters/WebCryptoAdapter.js +121 -0
- package/src/infrastructure/codecs/CborCodec.js +384 -0
- package/src/ports/BlobPort.js +30 -0
- package/src/ports/ClockPort.js +25 -0
- package/src/ports/CodecPort.js +25 -0
- package/src/ports/CommitPort.js +114 -0
- package/src/ports/ConfigPort.js +31 -0
- package/src/ports/CryptoPort.js +38 -0
- package/src/ports/GraphPersistencePort.js +57 -0
- package/src/ports/HttpServerPort.js +25 -0
- package/src/ports/IndexStoragePort.js +39 -0
- package/src/ports/LoggerPort.js +68 -0
- package/src/ports/RefPort.js +51 -0
- package/src/ports/TreePort.js +51 -0
- package/src/visualization/index.js +26 -0
- package/src/visualization/layouts/converters.js +75 -0
- package/src/visualization/layouts/elkAdapter.js +86 -0
- package/src/visualization/layouts/elkLayout.js +95 -0
- package/src/visualization/layouts/index.js +29 -0
- package/src/visualization/renderers/ascii/box.js +16 -0
- package/src/visualization/renderers/ascii/check.js +271 -0
- package/src/visualization/renderers/ascii/colors.js +13 -0
- package/src/visualization/renderers/ascii/formatters.js +73 -0
- package/src/visualization/renderers/ascii/graph.js +344 -0
- package/src/visualization/renderers/ascii/history.js +335 -0
- package/src/visualization/renderers/ascii/index.js +14 -0
- package/src/visualization/renderers/ascii/info.js +245 -0
- package/src/visualization/renderers/ascii/materialize.js +255 -0
- package/src/visualization/renderers/ascii/path.js +240 -0
- package/src/visualization/renderers/ascii/progress.js +32 -0
- package/src/visualization/renderers/ascii/symbols.js +33 -0
- package/src/visualization/renderers/ascii/table.js +19 -0
- package/src/visualization/renderers/browser/index.js +1 -0
- package/src/visualization/renderers/svg/index.js +159 -0
- package/src/visualization/utils/ansi.js +14 -0
- package/src/visualization/utils/time.js +40 -0
- package/src/visualization/utils/truncate.js +40 -0
- package/src/visualization/utils/unicode.js +52 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WARP Common Types and Shared Factories
|
|
3
|
+
*
|
|
4
|
+
* Pure type definitions using JSDoc for IDE autocomplete and documentation.
|
|
5
|
+
* Contains types and factories shared across schema versions.
|
|
6
|
+
*
|
|
7
|
+
* Note: Schema-specific types are in WarpTypesV2.js (schema:2).
|
|
8
|
+
*
|
|
9
|
+
* @module WarpTypes
|
|
10
|
+
* @see WARP Spec Section 6
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// Primitive Types
|
|
15
|
+
// ============================================================================
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* String identifier for nodes (e.g., "user:alice", UUID)
|
|
19
|
+
* @typedef {string} NodeId
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Edge identifier tuple
|
|
24
|
+
* @typedef {Object} EdgeKey
|
|
25
|
+
* @property {NodeId} from - Source node ID
|
|
26
|
+
* @property {NodeId} to - Target node ID
|
|
27
|
+
* @property {string} label - Edge label/type
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
// ============================================================================
|
|
31
|
+
// Value References
|
|
32
|
+
// ============================================================================
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Inline value reference - value stored directly in the operation
|
|
36
|
+
* @typedef {Object} ValueRefInline
|
|
37
|
+
* @property {'inline'} type - Discriminator for inline values
|
|
38
|
+
* @property {*} value - The actual value (any JSON-serializable type)
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Blob value reference - value stored as a Git blob
|
|
43
|
+
* @typedef {Object} ValueRefBlob
|
|
44
|
+
* @property {'blob'} type - Discriminator for blob references
|
|
45
|
+
* @property {string} oid - Git blob OID (object identifier)
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Value reference - either inline or blob
|
|
50
|
+
* @typedef {ValueRefInline | ValueRefBlob} ValueRef
|
|
51
|
+
*/
|
|
52
|
+
|
|
53
|
+
// ============================================================================
|
|
54
|
+
// Event Identification
|
|
55
|
+
// ============================================================================
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* EventId for total ordering of operations across patches
|
|
59
|
+
* Provides a globally unique identifier for each operation
|
|
60
|
+
* @typedef {Object} EventId
|
|
61
|
+
* @property {number} lamport - Lamport timestamp from the patch
|
|
62
|
+
* @property {string} writerId - Writer ID from the patch
|
|
63
|
+
* @property {string} patchSha - SHA of the patch/commit containing this operation
|
|
64
|
+
* @property {number} opIndex - Index of the operation within the patch
|
|
65
|
+
*/
|
|
66
|
+
|
|
67
|
+
// ============================================================================
|
|
68
|
+
// Factory Functions - Value References
|
|
69
|
+
// ============================================================================
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Creates an inline value reference
|
|
73
|
+
* @param {*} value - The value to store inline
|
|
74
|
+
* @returns {ValueRefInline} Inline value reference
|
|
75
|
+
*/
|
|
76
|
+
export function createInlineValue(value) {
|
|
77
|
+
return { type: 'inline', value };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Creates a blob value reference
|
|
82
|
+
* @param {string} oid - Git blob OID
|
|
83
|
+
* @returns {ValueRefBlob} Blob value reference
|
|
84
|
+
*/
|
|
85
|
+
export function createBlobValue(oid) {
|
|
86
|
+
return { type: 'blob', oid };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// ============================================================================
|
|
90
|
+
// Operations
|
|
91
|
+
// ============================================================================
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Node add operation
|
|
95
|
+
* @typedef {Object} OpNodeAdd
|
|
96
|
+
* @property {'NodeAdd'} type - Operation type discriminator
|
|
97
|
+
* @property {NodeId} node - Node ID to add
|
|
98
|
+
*/
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Node tombstone operation
|
|
102
|
+
* @typedef {Object} OpNodeTombstone
|
|
103
|
+
* @property {'NodeTombstone'} type - Operation type discriminator
|
|
104
|
+
* @property {NodeId} node - Node ID to tombstone
|
|
105
|
+
*/
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Edge add operation
|
|
109
|
+
* @typedef {Object} OpEdgeAdd
|
|
110
|
+
* @property {'EdgeAdd'} type - Operation type discriminator
|
|
111
|
+
* @property {NodeId} from - Source node ID
|
|
112
|
+
* @property {NodeId} to - Target node ID
|
|
113
|
+
* @property {string} label - Edge label/type
|
|
114
|
+
*/
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Edge tombstone operation
|
|
118
|
+
* @typedef {Object} OpEdgeTombstone
|
|
119
|
+
* @property {'EdgeTombstone'} type - Operation type discriminator
|
|
120
|
+
* @property {NodeId} from - Source node ID
|
|
121
|
+
* @property {NodeId} to - Target node ID
|
|
122
|
+
* @property {string} label - Edge label/type
|
|
123
|
+
*/
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Property set operation
|
|
127
|
+
* @typedef {Object} OpPropSet
|
|
128
|
+
* @property {'PropSet'} type - Operation type discriminator
|
|
129
|
+
* @property {NodeId} node - Node ID to set property on
|
|
130
|
+
* @property {string} key - Property key
|
|
131
|
+
* @property {ValueRef} value - Property value reference
|
|
132
|
+
*/
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Any graph operation
|
|
136
|
+
* @typedef {OpNodeAdd | OpNodeTombstone | OpEdgeAdd | OpEdgeTombstone | OpPropSet} Op
|
|
137
|
+
*/
|
|
138
|
+
|
|
139
|
+
// ============================================================================
|
|
140
|
+
// Factory Functions - Operations
|
|
141
|
+
// ============================================================================
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Creates a NodeAdd operation
|
|
145
|
+
* @param {NodeId} node - Node ID to add
|
|
146
|
+
* @returns {OpNodeAdd} NodeAdd operation
|
|
147
|
+
*/
|
|
148
|
+
export function createNodeAdd(node) {
|
|
149
|
+
return { type: 'NodeAdd', node };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Creates a NodeTombstone operation
|
|
154
|
+
* @param {NodeId} node - Node ID to tombstone
|
|
155
|
+
* @returns {OpNodeTombstone} NodeTombstone operation
|
|
156
|
+
*/
|
|
157
|
+
export function createNodeTombstone(node) {
|
|
158
|
+
return { type: 'NodeTombstone', node };
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Creates an EdgeAdd operation
|
|
163
|
+
* @param {NodeId} from - Source node ID
|
|
164
|
+
* @param {NodeId} to - Target node ID
|
|
165
|
+
* @param {string} label - Edge label
|
|
166
|
+
* @returns {OpEdgeAdd} EdgeAdd operation
|
|
167
|
+
*/
|
|
168
|
+
export function createEdgeAdd(from, to, label) {
|
|
169
|
+
return { type: 'EdgeAdd', from, to, label };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Creates an EdgeTombstone operation
|
|
174
|
+
* @param {NodeId} from - Source node ID
|
|
175
|
+
* @param {NodeId} to - Target node ID
|
|
176
|
+
* @param {string} label - Edge label
|
|
177
|
+
* @returns {OpEdgeTombstone} EdgeTombstone operation
|
|
178
|
+
*/
|
|
179
|
+
export function createEdgeTombstone(from, to, label) {
|
|
180
|
+
return { type: 'EdgeTombstone', from, to, label };
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Creates a PropSet operation
|
|
185
|
+
* @param {NodeId} node - Node ID to set property on
|
|
186
|
+
* @param {string} key - Property key
|
|
187
|
+
* @param {ValueRef} value - Property value reference
|
|
188
|
+
* @returns {OpPropSet} PropSet operation
|
|
189
|
+
*/
|
|
190
|
+
export function createPropSet(node, key, value) {
|
|
191
|
+
return { type: 'PropSet', node, key, value };
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// ============================================================================
|
|
195
|
+
// Factory Functions - EventId
|
|
196
|
+
// ============================================================================
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Creates an EventId
|
|
200
|
+
* @param {Object} options - EventId options
|
|
201
|
+
* @param {number} options.lamport - Lamport timestamp
|
|
202
|
+
* @param {string} options.writerId - Writer ID
|
|
203
|
+
* @param {string} options.patchSha - Patch SHA
|
|
204
|
+
* @param {number} options.opIndex - Operation index within patch
|
|
205
|
+
* @returns {EventId} EventId object
|
|
206
|
+
*/
|
|
207
|
+
export function createEventId({ lamport, writerId, patchSha, opIndex }) {
|
|
208
|
+
return { lamport, writerId, patchSha, opIndex };
|
|
209
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WARP OpV2/PatchV2 Types and Schema
|
|
3
|
+
*
|
|
4
|
+
* Pure type definitions using JSDoc for IDE autocomplete and documentation.
|
|
5
|
+
* Factory functions for creating WARP v5 operations and patches.
|
|
6
|
+
*
|
|
7
|
+
* Key differences from V1:
|
|
8
|
+
* - Add operations carry dots (causal identifiers)
|
|
9
|
+
* - Remove operations carry observedDots (set of dots being removed)
|
|
10
|
+
* - PropSet uses EventId for identification (no dot field)
|
|
11
|
+
* - PatchV2 includes context (VersionVector) for writer's observed frontier
|
|
12
|
+
*
|
|
13
|
+
* @module WarpTypesV2
|
|
14
|
+
* @see WARP v5 Spec
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// ============================================================================
|
|
18
|
+
// Primitive Types
|
|
19
|
+
// ============================================================================
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* String identifier for nodes (e.g., "user:alice", UUID)
|
|
23
|
+
* @typedef {string} NodeId
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Dot - causal identifier for an add operation
|
|
28
|
+
* @typedef {Object} Dot
|
|
29
|
+
* @property {string} writer - Writer ID that created this dot
|
|
30
|
+
* @property {number} seq - Sequence number for this writer
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* VersionVector - maps writer IDs to their maximum observed sequence numbers
|
|
35
|
+
* @typedef {Object.<string, number>} VersionVector
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
// ============================================================================
|
|
39
|
+
// Operations (OpV2)
|
|
40
|
+
// ============================================================================
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Node add operation - creates a new node with a dot
|
|
44
|
+
* @typedef {Object} OpV2NodeAdd
|
|
45
|
+
* @property {'NodeAdd'} type - Operation type discriminator
|
|
46
|
+
* @property {NodeId} node - Node ID to add
|
|
47
|
+
* @property {Dot} dot - Causal identifier for this add
|
|
48
|
+
*/
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Node remove operation - removes a node by observed dots
|
|
52
|
+
* @typedef {Object} OpV2NodeRemove
|
|
53
|
+
* @property {'NodeRemove'} type - Operation type discriminator
|
|
54
|
+
* @property {NodeId} node - Node ID to remove
|
|
55
|
+
* @property {Dot[]} observedDots - Dots being removed (add events observed)
|
|
56
|
+
*/
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Edge add operation - creates a new edge with a dot
|
|
60
|
+
* @typedef {Object} OpV2EdgeAdd
|
|
61
|
+
* @property {'EdgeAdd'} type - Operation type discriminator
|
|
62
|
+
* @property {NodeId} from - Source node ID
|
|
63
|
+
* @property {NodeId} to - Target node ID
|
|
64
|
+
* @property {string} label - Edge label/type
|
|
65
|
+
* @property {Dot} dot - Causal identifier for this add
|
|
66
|
+
*/
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Edge remove operation - removes an edge by observed dots
|
|
70
|
+
* @typedef {Object} OpV2EdgeRemove
|
|
71
|
+
* @property {'EdgeRemove'} type - Operation type discriminator
|
|
72
|
+
* @property {NodeId} from - Source node ID
|
|
73
|
+
* @property {NodeId} to - Target node ID
|
|
74
|
+
* @property {string} label - Edge label/type
|
|
75
|
+
* @property {Dot[]} observedDots - Dots being removed (add events observed)
|
|
76
|
+
*/
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Property set operation - sets a property value on a node
|
|
80
|
+
* Uses EventId for identification (derived from patch context)
|
|
81
|
+
* @typedef {Object} OpV2PropSet
|
|
82
|
+
* @property {'PropSet'} type - Operation type discriminator
|
|
83
|
+
* @property {NodeId} node - Node ID to set property on
|
|
84
|
+
* @property {string} key - Property key
|
|
85
|
+
* @property {*} value - Property value (any JSON-serializable type)
|
|
86
|
+
*/
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Union of all v2 operation types
|
|
90
|
+
* @typedef {OpV2NodeAdd | OpV2NodeRemove | OpV2EdgeAdd | OpV2EdgeRemove | OpV2PropSet} OpV2
|
|
91
|
+
*/
|
|
92
|
+
|
|
93
|
+
// ============================================================================
|
|
94
|
+
// Patch
|
|
95
|
+
// ============================================================================
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* PatchV2 - A batch of ordered operations from a single writer
|
|
99
|
+
* @typedef {Object} PatchV2
|
|
100
|
+
* @property {2|3} schema - Schema version (2 for node-only, 3 for edge properties)
|
|
101
|
+
* @property {string} writer - Writer ID (identifies the source of the patch)
|
|
102
|
+
* @property {number} lamport - Lamport timestamp for ordering
|
|
103
|
+
* @property {VersionVector} context - Writer's observed frontier (NOT global stability)
|
|
104
|
+
* @property {OpV2[]} ops - Ordered array of operations
|
|
105
|
+
* @property {string[]} [reads] - Node/edge IDs read by this patch (for provenance tracking)
|
|
106
|
+
* @property {string[]} [writes] - Node/edge IDs written by this patch (for provenance tracking)
|
|
107
|
+
*/
|
|
108
|
+
|
|
109
|
+
// ============================================================================
|
|
110
|
+
// Factory Functions - Operations
|
|
111
|
+
// ============================================================================
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Creates a NodeAdd operation with a dot
|
|
115
|
+
* @param {NodeId} node - Node ID to add
|
|
116
|
+
* @param {Dot} dot - Causal identifier for this add
|
|
117
|
+
* @returns {OpV2NodeAdd} NodeAdd operation
|
|
118
|
+
*/
|
|
119
|
+
export function createNodeAddV2(node, dot) {
|
|
120
|
+
return { type: 'NodeAdd', node, dot };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Creates a NodeRemove operation with observed dots
|
|
125
|
+
* @param {NodeId} node - Node ID to remove
|
|
126
|
+
* @param {Dot[]} observedDots - Dots being removed
|
|
127
|
+
* @returns {OpV2NodeRemove} NodeRemove operation
|
|
128
|
+
*/
|
|
129
|
+
export function createNodeRemoveV2(node, observedDots) {
|
|
130
|
+
return { type: 'NodeRemove', node, observedDots };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Creates an EdgeAdd operation with a dot
|
|
135
|
+
* @param {NodeId} from - Source node ID
|
|
136
|
+
* @param {NodeId} to - Target node ID
|
|
137
|
+
* @param {string} label - Edge label
|
|
138
|
+
* @param {Dot} dot - Causal identifier for this add
|
|
139
|
+
* @returns {OpV2EdgeAdd} EdgeAdd operation
|
|
140
|
+
*/
|
|
141
|
+
export function createEdgeAddV2(from, to, label, dot) {
|
|
142
|
+
return { type: 'EdgeAdd', from, to, label, dot };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Creates an EdgeRemove operation with observed dots
|
|
147
|
+
* @param {NodeId} from - Source node ID
|
|
148
|
+
* @param {NodeId} to - Target node ID
|
|
149
|
+
* @param {string} label - Edge label
|
|
150
|
+
* @param {Dot[]} observedDots - Dots being removed
|
|
151
|
+
* @returns {OpV2EdgeRemove} EdgeRemove operation
|
|
152
|
+
*/
|
|
153
|
+
export function createEdgeRemoveV2(from, to, label, observedDots) {
|
|
154
|
+
return { type: 'EdgeRemove', from, to, label, observedDots };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Creates a PropSet operation (no dot - uses EventId)
|
|
159
|
+
* @param {NodeId} node - Node ID to set property on
|
|
160
|
+
* @param {string} key - Property key
|
|
161
|
+
* @param {*} value - Property value (any JSON-serializable type)
|
|
162
|
+
* @returns {OpV2PropSet} PropSet operation
|
|
163
|
+
*/
|
|
164
|
+
export function createPropSetV2(node, key, value) {
|
|
165
|
+
return { type: 'PropSet', node, key, value };
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// ============================================================================
|
|
169
|
+
// Factory Functions - Patch
|
|
170
|
+
// ============================================================================
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Creates a PatchV2
|
|
174
|
+
* @param {Object} options - Patch options
|
|
175
|
+
* @param {2|3} [options.schema=2] - Schema version (2 for node-only, 3 for edge properties)
|
|
176
|
+
* @param {string} options.writer - Writer ID
|
|
177
|
+
* @param {number} options.lamport - Lamport timestamp
|
|
178
|
+
* @param {VersionVector} options.context - Writer's observed frontier
|
|
179
|
+
* @param {OpV2[]} options.ops - Array of operations
|
|
180
|
+
* @param {string[]} [options.reads] - Node/edge IDs read by this patch (for provenance tracking)
|
|
181
|
+
* @param {string[]} [options.writes] - Node/edge IDs written by this patch (for provenance tracking)
|
|
182
|
+
* @returns {PatchV2} PatchV2 object
|
|
183
|
+
*/
|
|
184
|
+
export function createPatchV2({ schema = 2, writer, lamport, context, ops, reads, writes }) {
|
|
185
|
+
const patch = {
|
|
186
|
+
schema,
|
|
187
|
+
writer,
|
|
188
|
+
lamport,
|
|
189
|
+
context,
|
|
190
|
+
ops,
|
|
191
|
+
};
|
|
192
|
+
// Only include reads/writes if provided (backward compatibility)
|
|
193
|
+
if (reads && reads.length > 0) {
|
|
194
|
+
patch.reads = reads;
|
|
195
|
+
}
|
|
196
|
+
if (writes && writes.length > 0) {
|
|
197
|
+
patch.writes = writes;
|
|
198
|
+
}
|
|
199
|
+
return patch;
|
|
200
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A TTL-based caching utility for a single value.
|
|
3
|
+
*
|
|
4
|
+
* Caches the result of an expensive operation and reuses it until
|
|
5
|
+
* the configured TTL expires. Supports manual invalidation.
|
|
6
|
+
*
|
|
7
|
+
* @class CachedValue
|
|
8
|
+
* @template T
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* const cache = new CachedValue({
|
|
12
|
+
* clock,
|
|
13
|
+
* ttlMs: 5000,
|
|
14
|
+
* compute: async () => await expensiveOperation(),
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* // First call computes the value
|
|
18
|
+
* const value1 = await cache.get();
|
|
19
|
+
*
|
|
20
|
+
* // Subsequent calls within TTL return cached value
|
|
21
|
+
* const value2 = await cache.get(); // Same as value1
|
|
22
|
+
*
|
|
23
|
+
* // Force recompute
|
|
24
|
+
* cache.invalidate();
|
|
25
|
+
* const value3 = await cache.get(); // Fresh value
|
|
26
|
+
*/
|
|
27
|
+
class CachedValue {
|
|
28
|
+
/**
|
|
29
|
+
* Creates a CachedValue instance.
|
|
30
|
+
*
|
|
31
|
+
* @param {Object} options
|
|
32
|
+
* @param {import('../../ports/ClockPort.js').default} options.clock - Clock port for timing
|
|
33
|
+
* @param {number} options.ttlMs - Time-to-live in milliseconds
|
|
34
|
+
* @param {() => T | Promise<T>} options.compute - Function to compute the value when cache is stale
|
|
35
|
+
* @throws {Error} If ttlMs is not a positive number
|
|
36
|
+
*/
|
|
37
|
+
constructor({ clock, ttlMs, compute }) {
|
|
38
|
+
if (typeof ttlMs !== 'number' || ttlMs <= 0) {
|
|
39
|
+
throw new Error('CachedValue ttlMs must be a positive number');
|
|
40
|
+
}
|
|
41
|
+
if (typeof compute !== 'function') {
|
|
42
|
+
throw new Error('CachedValue compute must be a function');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** @type {import('../../ports/ClockPort.js').default} */
|
|
46
|
+
this._clock = clock;
|
|
47
|
+
|
|
48
|
+
/** @type {number} */
|
|
49
|
+
this._ttlMs = ttlMs;
|
|
50
|
+
|
|
51
|
+
/** @type {() => T | Promise<T>} */
|
|
52
|
+
this._compute = compute;
|
|
53
|
+
|
|
54
|
+
/** @type {T|null} */
|
|
55
|
+
this._value = null;
|
|
56
|
+
|
|
57
|
+
/** @type {number} */
|
|
58
|
+
this._cachedAt = 0;
|
|
59
|
+
|
|
60
|
+
/** @type {string|null} */
|
|
61
|
+
this._cachedAtIso = null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Gets the cached value, computing it if stale or not present.
|
|
66
|
+
*
|
|
67
|
+
* @returns {Promise<T>} The cached or freshly computed value
|
|
68
|
+
*/
|
|
69
|
+
async get() {
|
|
70
|
+
if (this._isValid()) {
|
|
71
|
+
return this._value;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const value = await this._compute();
|
|
75
|
+
this._value = value;
|
|
76
|
+
this._cachedAt = this._clock.now();
|
|
77
|
+
this._cachedAtIso = this._clock.timestamp();
|
|
78
|
+
|
|
79
|
+
return value;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Gets the cached value with metadata about when it was cached.
|
|
84
|
+
*
|
|
85
|
+
* @returns {Promise<{value: T, cachedAt: string|null, fromCache: boolean}>}
|
|
86
|
+
*/
|
|
87
|
+
async getWithMetadata() {
|
|
88
|
+
const wasValid = this._isValid();
|
|
89
|
+
const value = await this.get();
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
value,
|
|
93
|
+
cachedAt: wasValid ? this._cachedAtIso : null,
|
|
94
|
+
fromCache: wasValid,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Invalidates the cached value, forcing recomputation on next get().
|
|
100
|
+
*/
|
|
101
|
+
invalidate() {
|
|
102
|
+
this._value = null;
|
|
103
|
+
this._cachedAt = 0;
|
|
104
|
+
this._cachedAtIso = null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Checks if the cached value is still valid.
|
|
109
|
+
*
|
|
110
|
+
* @returns {boolean} True if the cache is valid
|
|
111
|
+
* @private
|
|
112
|
+
*/
|
|
113
|
+
_isValid() {
|
|
114
|
+
if (this._value === null) {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
return this._clock.now() - this._cachedAt < this._ttlMs;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Gets the ISO timestamp of when the value was cached.
|
|
122
|
+
* Returns null if no value is cached.
|
|
123
|
+
*
|
|
124
|
+
* @returns {string|null}
|
|
125
|
+
*/
|
|
126
|
+
get cachedAt() {
|
|
127
|
+
return this._cachedAtIso;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Checks if a value is currently cached (regardless of validity).
|
|
132
|
+
*
|
|
133
|
+
* @returns {boolean}
|
|
134
|
+
*/
|
|
135
|
+
get hasValue() {
|
|
136
|
+
return this._value !== null;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export default CachedValue;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EventId for total ordering of operations (WARP spec Section 7).
|
|
3
|
+
*
|
|
4
|
+
* @typedef {Object} EventId
|
|
5
|
+
* @property {number} lamport - Monotonic counter (positive integer)
|
|
6
|
+
* @property {string} writerId - Writer identifier (non-empty string)
|
|
7
|
+
* @property {string} patchSha - Patch commit SHA (hex OID, 4-64 chars)
|
|
8
|
+
* @property {number} opIndex - Operation index within patch (non-negative integer)
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// Regex for validating hex OID (4-64 hex characters)
|
|
12
|
+
const HEX_OID_REGEX = /^[0-9a-f]{4,64}$/;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Creates a validated EventId.
|
|
16
|
+
*
|
|
17
|
+
* @param {number} lamport - Must be positive integer (> 0)
|
|
18
|
+
* @param {string} writerId - Must be non-empty string
|
|
19
|
+
* @param {string} patchSha - Must be valid hex OID (4-64 chars)
|
|
20
|
+
* @param {number} opIndex - Must be non-negative integer (>= 0)
|
|
21
|
+
* @returns {EventId}
|
|
22
|
+
* @throws {Error} If validation fails
|
|
23
|
+
*/
|
|
24
|
+
export function createEventId(lamport, writerId, patchSha, opIndex) {
|
|
25
|
+
// Validate lamport is positive integer
|
|
26
|
+
if (!Number.isInteger(lamport) || lamport <= 0) {
|
|
27
|
+
throw new Error('lamport must be a positive integer');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Validate writerId is non-empty string
|
|
31
|
+
if (typeof writerId !== 'string' || writerId.length === 0) {
|
|
32
|
+
throw new Error('writerId must be a non-empty string');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Validate patchSha is hex string 4-64 chars
|
|
36
|
+
if (typeof patchSha !== 'string' || !HEX_OID_REGEX.test(patchSha)) {
|
|
37
|
+
throw new Error('patchSha must be a hex string of 4-64 characters');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Validate opIndex is non-negative integer
|
|
41
|
+
if (!Number.isInteger(opIndex) || opIndex < 0) {
|
|
42
|
+
throw new Error('opIndex must be a non-negative integer');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return { lamport, writerId, patchSha, opIndex };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Compares two EventIds lexicographically.
|
|
50
|
+
* Order: lamport -> writerId -> patchSha -> opIndex
|
|
51
|
+
*
|
|
52
|
+
* @param {EventId} a
|
|
53
|
+
* @param {EventId} b
|
|
54
|
+
* @returns {number} -1 if a < b, 0 if equal, 1 if a > b
|
|
55
|
+
*/
|
|
56
|
+
export function compareEventIds(a, b) {
|
|
57
|
+
// 1. Compare lamport numerically
|
|
58
|
+
if (a.lamport !== b.lamport) {
|
|
59
|
+
return a.lamport < b.lamport ? -1 : 1;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 2. Compare writerId as string
|
|
63
|
+
if (a.writerId !== b.writerId) {
|
|
64
|
+
return a.writerId < b.writerId ? -1 : 1;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 3. Compare patchSha as string
|
|
68
|
+
if (a.patchSha !== b.patchSha) {
|
|
69
|
+
return a.patchSha < b.patchSha ? -1 : 1;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// 4. Compare opIndex numerically
|
|
73
|
+
if (a.opIndex !== b.opIndex) {
|
|
74
|
+
return a.opIndex < b.opIndex ? -1 : 1;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return 0;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Checks if EventId a is greater than EventId b.
|
|
82
|
+
*
|
|
83
|
+
* @param {EventId} a
|
|
84
|
+
* @param {EventId} b
|
|
85
|
+
* @returns {boolean}
|
|
86
|
+
*/
|
|
87
|
+
export function isGreater(a, b) {
|
|
88
|
+
return compareEventIds(a, b) > 0;
|
|
89
|
+
}
|