@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.
Files changed (143) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +16 -0
  3. package/README.md +480 -0
  4. package/SECURITY.md +30 -0
  5. package/bin/git-warp +24 -0
  6. package/bin/warp-graph.js +1574 -0
  7. package/index.d.ts +2366 -0
  8. package/index.js +180 -0
  9. package/package.json +129 -0
  10. package/scripts/install-git-warp.sh +258 -0
  11. package/scripts/uninstall-git-warp.sh +139 -0
  12. package/src/domain/WarpGraph.js +3157 -0
  13. package/src/domain/crdt/Dot.js +160 -0
  14. package/src/domain/crdt/LWW.js +154 -0
  15. package/src/domain/crdt/ORSet.js +371 -0
  16. package/src/domain/crdt/VersionVector.js +222 -0
  17. package/src/domain/entities/GraphNode.js +60 -0
  18. package/src/domain/errors/EmptyMessageError.js +47 -0
  19. package/src/domain/errors/ForkError.js +30 -0
  20. package/src/domain/errors/IndexError.js +23 -0
  21. package/src/domain/errors/OperationAbortedError.js +22 -0
  22. package/src/domain/errors/QueryError.js +39 -0
  23. package/src/domain/errors/SchemaUnsupportedError.js +17 -0
  24. package/src/domain/errors/ShardCorruptionError.js +56 -0
  25. package/src/domain/errors/ShardLoadError.js +57 -0
  26. package/src/domain/errors/ShardValidationError.js +61 -0
  27. package/src/domain/errors/StorageError.js +57 -0
  28. package/src/domain/errors/SyncError.js +30 -0
  29. package/src/domain/errors/TraversalError.js +23 -0
  30. package/src/domain/errors/WarpError.js +31 -0
  31. package/src/domain/errors/WormholeError.js +28 -0
  32. package/src/domain/errors/WriterError.js +39 -0
  33. package/src/domain/errors/index.js +21 -0
  34. package/src/domain/services/AnchorMessageCodec.js +99 -0
  35. package/src/domain/services/BitmapIndexBuilder.js +225 -0
  36. package/src/domain/services/BitmapIndexReader.js +435 -0
  37. package/src/domain/services/BoundaryTransitionRecord.js +463 -0
  38. package/src/domain/services/CheckpointMessageCodec.js +147 -0
  39. package/src/domain/services/CheckpointSerializerV5.js +281 -0
  40. package/src/domain/services/CheckpointService.js +384 -0
  41. package/src/domain/services/CommitDagTraversalService.js +156 -0
  42. package/src/domain/services/DagPathFinding.js +712 -0
  43. package/src/domain/services/DagTopology.js +239 -0
  44. package/src/domain/services/DagTraversal.js +245 -0
  45. package/src/domain/services/Frontier.js +108 -0
  46. package/src/domain/services/GCMetrics.js +101 -0
  47. package/src/domain/services/GCPolicy.js +122 -0
  48. package/src/domain/services/GitLogParser.js +205 -0
  49. package/src/domain/services/HealthCheckService.js +246 -0
  50. package/src/domain/services/HookInstaller.js +326 -0
  51. package/src/domain/services/HttpSyncServer.js +262 -0
  52. package/src/domain/services/IndexRebuildService.js +426 -0
  53. package/src/domain/services/IndexStalenessChecker.js +103 -0
  54. package/src/domain/services/JoinReducer.js +582 -0
  55. package/src/domain/services/KeyCodec.js +113 -0
  56. package/src/domain/services/LegacyAnchorDetector.js +67 -0
  57. package/src/domain/services/LogicalTraversal.js +351 -0
  58. package/src/domain/services/MessageCodecInternal.js +132 -0
  59. package/src/domain/services/MessageSchemaDetector.js +145 -0
  60. package/src/domain/services/MigrationService.js +55 -0
  61. package/src/domain/services/ObserverView.js +265 -0
  62. package/src/domain/services/PatchBuilderV2.js +669 -0
  63. package/src/domain/services/PatchMessageCodec.js +140 -0
  64. package/src/domain/services/ProvenanceIndex.js +337 -0
  65. package/src/domain/services/ProvenancePayload.js +242 -0
  66. package/src/domain/services/QueryBuilder.js +835 -0
  67. package/src/domain/services/StateDiff.js +300 -0
  68. package/src/domain/services/StateSerializerV5.js +156 -0
  69. package/src/domain/services/StreamingBitmapIndexBuilder.js +709 -0
  70. package/src/domain/services/SyncProtocol.js +593 -0
  71. package/src/domain/services/TemporalQuery.js +201 -0
  72. package/src/domain/services/TranslationCost.js +221 -0
  73. package/src/domain/services/TraversalService.js +8 -0
  74. package/src/domain/services/WarpMessageCodec.js +29 -0
  75. package/src/domain/services/WarpStateIndexBuilder.js +127 -0
  76. package/src/domain/services/WormholeService.js +353 -0
  77. package/src/domain/types/TickReceipt.js +285 -0
  78. package/src/domain/types/WarpTypes.js +209 -0
  79. package/src/domain/types/WarpTypesV2.js +200 -0
  80. package/src/domain/utils/CachedValue.js +140 -0
  81. package/src/domain/utils/EventId.js +89 -0
  82. package/src/domain/utils/LRUCache.js +112 -0
  83. package/src/domain/utils/MinHeap.js +114 -0
  84. package/src/domain/utils/RefLayout.js +280 -0
  85. package/src/domain/utils/WriterId.js +205 -0
  86. package/src/domain/utils/cancellation.js +33 -0
  87. package/src/domain/utils/canonicalStringify.js +42 -0
  88. package/src/domain/utils/defaultClock.js +20 -0
  89. package/src/domain/utils/defaultCodec.js +51 -0
  90. package/src/domain/utils/nullLogger.js +21 -0
  91. package/src/domain/utils/roaring.js +181 -0
  92. package/src/domain/utils/shardVersion.js +9 -0
  93. package/src/domain/warp/PatchSession.js +217 -0
  94. package/src/domain/warp/Writer.js +181 -0
  95. package/src/hooks/post-merge.sh +60 -0
  96. package/src/infrastructure/adapters/BunHttpAdapter.js +225 -0
  97. package/src/infrastructure/adapters/ClockAdapter.js +57 -0
  98. package/src/infrastructure/adapters/ConsoleLogger.js +150 -0
  99. package/src/infrastructure/adapters/DenoHttpAdapter.js +230 -0
  100. package/src/infrastructure/adapters/GitGraphAdapter.js +787 -0
  101. package/src/infrastructure/adapters/GlobalClockAdapter.js +5 -0
  102. package/src/infrastructure/adapters/NoOpLogger.js +62 -0
  103. package/src/infrastructure/adapters/NodeCryptoAdapter.js +32 -0
  104. package/src/infrastructure/adapters/NodeHttpAdapter.js +98 -0
  105. package/src/infrastructure/adapters/PerformanceClockAdapter.js +5 -0
  106. package/src/infrastructure/adapters/WebCryptoAdapter.js +121 -0
  107. package/src/infrastructure/codecs/CborCodec.js +384 -0
  108. package/src/ports/BlobPort.js +30 -0
  109. package/src/ports/ClockPort.js +25 -0
  110. package/src/ports/CodecPort.js +25 -0
  111. package/src/ports/CommitPort.js +114 -0
  112. package/src/ports/ConfigPort.js +31 -0
  113. package/src/ports/CryptoPort.js +38 -0
  114. package/src/ports/GraphPersistencePort.js +57 -0
  115. package/src/ports/HttpServerPort.js +25 -0
  116. package/src/ports/IndexStoragePort.js +39 -0
  117. package/src/ports/LoggerPort.js +68 -0
  118. package/src/ports/RefPort.js +51 -0
  119. package/src/ports/TreePort.js +51 -0
  120. package/src/visualization/index.js +26 -0
  121. package/src/visualization/layouts/converters.js +75 -0
  122. package/src/visualization/layouts/elkAdapter.js +86 -0
  123. package/src/visualization/layouts/elkLayout.js +95 -0
  124. package/src/visualization/layouts/index.js +29 -0
  125. package/src/visualization/renderers/ascii/box.js +16 -0
  126. package/src/visualization/renderers/ascii/check.js +271 -0
  127. package/src/visualization/renderers/ascii/colors.js +13 -0
  128. package/src/visualization/renderers/ascii/formatters.js +73 -0
  129. package/src/visualization/renderers/ascii/graph.js +344 -0
  130. package/src/visualization/renderers/ascii/history.js +335 -0
  131. package/src/visualization/renderers/ascii/index.js +14 -0
  132. package/src/visualization/renderers/ascii/info.js +245 -0
  133. package/src/visualization/renderers/ascii/materialize.js +255 -0
  134. package/src/visualization/renderers/ascii/path.js +240 -0
  135. package/src/visualization/renderers/ascii/progress.js +32 -0
  136. package/src/visualization/renderers/ascii/symbols.js +33 -0
  137. package/src/visualization/renderers/ascii/table.js +19 -0
  138. package/src/visualization/renderers/browser/index.js +1 -0
  139. package/src/visualization/renderers/svg/index.js +159 -0
  140. package/src/visualization/utils/ansi.js +14 -0
  141. package/src/visualization/utils/time.js +40 -0
  142. package/src/visualization/utils/truncate.js +40 -0
  143. 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
+ }