@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,55 @@
1
+ /**
2
+ * Creates a v5 checkpoint from v4 visible projection.
3
+ * This is the migration boundary.
4
+ */
5
+ import { createEmptyStateV5 } from './JoinReducer.js';
6
+ import { orsetAdd } from '../crdt/ORSet.js';
7
+ import { createVersionVector, vvIncrement } from '../crdt/VersionVector.js';
8
+
9
+ /**
10
+ * Migrates a V4 visible-projection state to a V5 state with ORSet internals.
11
+ *
12
+ * Creates synthetic dots for each visible node and edge under the migration
13
+ * writer, rebuilds the ORSet structures, and copies only properties belonging
14
+ * to visible nodes (dropping dangling props from deleted nodes).
15
+ *
16
+ * @param {Object} v4State - The V4 materialized state (visible projection)
17
+ * @param {Map<string, {value: boolean}>} v4State.nodeAlive - V4 node alive map
18
+ * @param {Map<string, {value: boolean}>} v4State.edgeAlive - V4 edge alive map
19
+ * @param {Map<string, *>} v4State.prop - V4 property map
20
+ * @param {string} migrationWriterId - Writer ID to use for synthetic dots
21
+ * @returns {import('./JoinReducer.js').WarpStateV5} The migrated V5 state
22
+ */
23
+ export function migrateV4toV5(v4State, migrationWriterId) {
24
+ const v5State = createEmptyStateV5();
25
+ const vv = createVersionVector();
26
+
27
+ // For each visible node in v4, add to v5 OR-Set with synthetic dot
28
+ for (const [nodeId, reg] of v4State.nodeAlive) {
29
+ if (reg.value) {
30
+ const dot = vvIncrement(vv, migrationWriterId);
31
+ orsetAdd(v5State.nodeAlive, nodeId, dot);
32
+ }
33
+ }
34
+
35
+ // Same for edges
36
+ for (const [edgeKey, reg] of v4State.edgeAlive) {
37
+ if (reg.value) {
38
+ const dot = vvIncrement(vv, migrationWriterId);
39
+ orsetAdd(v5State.edgeAlive, edgeKey, dot);
40
+ }
41
+ }
42
+
43
+ // Only copy props for VISIBLE nodes (don't carry dangling props)
44
+ for (const [propKey, reg] of v4State.prop) {
45
+ const idx = propKey.indexOf('\0');
46
+ const nodeId = propKey.slice(0, idx);
47
+ const nodeReg = v4State.nodeAlive.get(nodeId);
48
+ if (nodeReg?.value) {
49
+ v5State.prop.set(propKey, reg);
50
+ }
51
+ }
52
+
53
+ v5State.observedFrontier = vv;
54
+ return v5State;
55
+ }
@@ -0,0 +1,265 @@
1
+ /**
2
+ * ObserverView - Read-only filtered view of a materialized WarpGraph.
3
+ *
4
+ * Provides an observer that sees only nodes matching a glob pattern,
5
+ * with property visibility controlled by expose/redact lists.
6
+ * Edges are only visible when both endpoints pass the match filter.
7
+ *
8
+ * @module domain/services/ObserverView
9
+ * @see Paper IV, Section 3 -- Observers as resource-bounded functors
10
+ */
11
+
12
+ import QueryBuilder from './QueryBuilder.js';
13
+ import LogicalTraversal from './LogicalTraversal.js';
14
+ import { orsetContains, orsetElements } from '../crdt/ORSet.js';
15
+ import { decodeEdgeKey } from './KeyCodec.js';
16
+
17
+ /**
18
+ * Tests whether a string matches a glob-style pattern.
19
+ *
20
+ * Supports `*` as a wildcard matching zero or more characters.
21
+ * A lone `*` matches everything.
22
+ *
23
+ * @param {string} pattern - Glob pattern (e.g. 'user:*', '*:admin', '*')
24
+ * @param {string} str - The string to test
25
+ * @returns {boolean} True if the string matches the pattern
26
+ */
27
+ function matchGlob(pattern, str) {
28
+ if (pattern === '*') {
29
+ return true;
30
+ }
31
+ if (!pattern.includes('*')) {
32
+ return pattern === str;
33
+ }
34
+ const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, '\\$&');
35
+ const regex = new RegExp(`^${escaped.replace(/\*/g, '.*')}$`);
36
+ return regex.test(str);
37
+ }
38
+
39
+ /**
40
+ * Filters a properties Map based on expose and redact lists.
41
+ *
42
+ * - If `redact` contains a key, it is excluded (highest priority).
43
+ * - If `expose` is provided and non-empty, only keys in `expose` are included.
44
+ * - If `expose` is absent/empty, all non-redacted keys are included.
45
+ *
46
+ * @param {Map<string, *>} propsMap - The full properties Map
47
+ * @param {string[]|undefined} expose - Whitelist of property keys to include
48
+ * @param {string[]|undefined} redact - Blacklist of property keys to exclude
49
+ * @returns {Map<string, *>} Filtered properties Map
50
+ */
51
+ function filterProps(propsMap, expose, redact) {
52
+ const redactSet = redact && redact.length > 0 ? new Set(redact) : null;
53
+ const exposeSet = expose && expose.length > 0 ? new Set(expose) : null;
54
+
55
+ const filtered = new Map();
56
+ for (const [key, value] of propsMap) {
57
+ // Redact takes precedence
58
+ if (redactSet && redactSet.has(key)) {
59
+ continue;
60
+ }
61
+ // If expose is specified, only include listed keys
62
+ if (exposeSet && !exposeSet.has(key)) {
63
+ continue;
64
+ }
65
+ filtered.set(key, value);
66
+ }
67
+ return filtered;
68
+ }
69
+
70
+ /**
71
+ * Read-only observer view of a materialized WarpGraph state.
72
+ *
73
+ * Provides the same query/traverse API as WarpGraph, but filtered
74
+ * by observer configuration (match pattern, expose, redact).
75
+ */
76
+ export default class ObserverView {
77
+ /**
78
+ * Creates a new ObserverView.
79
+ *
80
+ * @param {Object} options
81
+ * @param {string} options.name - Observer name
82
+ * @param {Object} options.config - Observer configuration
83
+ * @param {string} options.config.match - Glob pattern for visible nodes
84
+ * @param {string[]} [options.config.expose] - Property keys to include
85
+ * @param {string[]} [options.config.redact] - Property keys to exclude (takes precedence over expose)
86
+ * @param {import('../WarpGraph.js').default} options.graph - The source WarpGraph instance
87
+ */
88
+ constructor({ name, config, graph }) {
89
+ /** @type {string} */
90
+ this._name = name;
91
+
92
+ /** @type {string} */
93
+ this._matchPattern = config.match;
94
+
95
+ /** @type {string[]|undefined} */
96
+ this._expose = config.expose;
97
+
98
+ /** @type {string[]|undefined} */
99
+ this._redact = config.redact;
100
+
101
+ /** @type {import('../WarpGraph.js').default} */
102
+ this._graph = graph;
103
+
104
+ /** @type {LogicalTraversal} */
105
+ this.traverse = new LogicalTraversal(this);
106
+ }
107
+
108
+ /**
109
+ * Gets the observer name.
110
+ * @returns {string}
111
+ */
112
+ get name() {
113
+ return this._name;
114
+ }
115
+
116
+ // ===========================================================================
117
+ // Internal: State access (used by QueryBuilder and LogicalTraversal)
118
+ // ===========================================================================
119
+
120
+ /**
121
+ * Materializes and returns the graph details, used internally by
122
+ * QueryBuilder and LogicalTraversal.
123
+ *
124
+ * Builds a filtered adjacency structure that only includes edges
125
+ * where both endpoints pass the match filter.
126
+ *
127
+ * @returns {Promise<{state: *, stateHash: string, adjacency: {outgoing: Map, incoming: Map}}>}
128
+ * @private
129
+ */
130
+ async _materializeGraph() {
131
+ const materialized = await this._graph._materializeGraph();
132
+ const { state, stateHash } = materialized;
133
+
134
+ // Build filtered adjacency: only edges where both endpoints match
135
+ const outgoing = new Map();
136
+ const incoming = new Map();
137
+
138
+ for (const edgeKey of orsetElements(state.edgeAlive)) {
139
+ const { from, to, label } = decodeEdgeKey(edgeKey);
140
+
141
+ // Both endpoints must be alive
142
+ if (!orsetContains(state.nodeAlive, from) || !orsetContains(state.nodeAlive, to)) {
143
+ continue;
144
+ }
145
+
146
+ // Both endpoints must match the observer pattern
147
+ if (!matchGlob(this._matchPattern, from) || !matchGlob(this._matchPattern, to)) {
148
+ continue;
149
+ }
150
+
151
+ if (!outgoing.has(from)) {
152
+ outgoing.set(from, []);
153
+ }
154
+ if (!incoming.has(to)) {
155
+ incoming.set(to, []);
156
+ }
157
+
158
+ outgoing.get(from).push({ neighborId: to, label });
159
+ incoming.get(to).push({ neighborId: from, label });
160
+ }
161
+
162
+ const sortNeighbors = (list) => {
163
+ list.sort((a, b) => {
164
+ if (a.neighborId !== b.neighborId) {
165
+ return a.neighborId < b.neighborId ? -1 : 1;
166
+ }
167
+ return a.label < b.label ? -1 : a.label > b.label ? 1 : 0;
168
+ });
169
+ };
170
+
171
+ for (const list of outgoing.values()) {
172
+ sortNeighbors(list);
173
+ }
174
+ for (const list of incoming.values()) {
175
+ sortNeighbors(list);
176
+ }
177
+
178
+ return { state, stateHash, adjacency: { outgoing, incoming } };
179
+ }
180
+
181
+ // ===========================================================================
182
+ // Node API
183
+ // ===========================================================================
184
+
185
+ /**
186
+ * Checks if a node exists and is visible to this observer.
187
+ *
188
+ * @param {string} nodeId - The node ID to check
189
+ * @returns {Promise<boolean>} True if the node exists and matches the observer pattern
190
+ */
191
+ async hasNode(nodeId) {
192
+ if (!matchGlob(this._matchPattern, nodeId)) {
193
+ return false;
194
+ }
195
+ return await this._graph.hasNode(nodeId);
196
+ }
197
+
198
+ /**
199
+ * Gets all visible nodes that match the observer pattern.
200
+ *
201
+ * @returns {Promise<string[]>} Array of visible node IDs
202
+ */
203
+ async getNodes() {
204
+ const allNodes = await this._graph.getNodes();
205
+ return allNodes.filter((id) => matchGlob(this._matchPattern, id));
206
+ }
207
+
208
+ /**
209
+ * Gets filtered properties for a node.
210
+ *
211
+ * Returns null if the node does not exist or does not match
212
+ * the observer pattern.
213
+ *
214
+ * @param {string} nodeId - The node ID to get properties for
215
+ * @returns {Promise<Map<string, *>|null>} Filtered properties Map, or null
216
+ */
217
+ async getNodeProps(nodeId) {
218
+ if (!matchGlob(this._matchPattern, nodeId)) {
219
+ return null;
220
+ }
221
+ const propsMap = await this._graph.getNodeProps(nodeId);
222
+ if (!propsMap) {
223
+ return null;
224
+ }
225
+ return filterProps(propsMap, this._expose, this._redact);
226
+ }
227
+
228
+ // ===========================================================================
229
+ // Edge API
230
+ // ===========================================================================
231
+
232
+ /**
233
+ * Gets all visible edges.
234
+ *
235
+ * An edge is visible only when both endpoints match the observer pattern.
236
+ *
237
+ * @returns {Promise<Array<{from: string, to: string, label: string, props: Record<string, *>}>>}
238
+ */
239
+ async getEdges() {
240
+ const allEdges = await this._graph.getEdges();
241
+ return allEdges
242
+ .filter(
243
+ (e) => matchGlob(this._matchPattern, e.from) && matchGlob(this._matchPattern, e.to)
244
+ )
245
+ .map((e) => {
246
+ const propsMap = new Map(Object.entries(e.props));
247
+ const filtered = filterProps(propsMap, this._expose, this._redact);
248
+ const filteredObj = Object.fromEntries(filtered);
249
+ return { ...e, props: filteredObj };
250
+ });
251
+ }
252
+
253
+ // ===========================================================================
254
+ // Query API
255
+ // ===========================================================================
256
+
257
+ /**
258
+ * Creates a fluent query builder operating on the filtered view.
259
+ *
260
+ * @returns {QueryBuilder} A query builder scoped to this observer
261
+ */
262
+ query() {
263
+ return new QueryBuilder(this);
264
+ }
265
+ }