@git-stunts/git-warp 10.8.0 → 11.3.3

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 (136) hide show
  1. package/README.md +53 -32
  2. package/SECURITY.md +64 -0
  3. package/bin/cli/commands/check.js +168 -0
  4. package/bin/cli/commands/doctor/checks.js +422 -0
  5. package/bin/cli/commands/doctor/codes.js +46 -0
  6. package/bin/cli/commands/doctor/index.js +239 -0
  7. package/bin/cli/commands/doctor/types.js +89 -0
  8. package/bin/cli/commands/history.js +80 -0
  9. package/bin/cli/commands/info.js +139 -0
  10. package/bin/cli/commands/install-hooks.js +128 -0
  11. package/bin/cli/commands/materialize.js +99 -0
  12. package/bin/cli/commands/patch.js +142 -0
  13. package/bin/cli/commands/path.js +88 -0
  14. package/bin/cli/commands/query.js +235 -0
  15. package/bin/cli/commands/registry.js +32 -0
  16. package/bin/cli/commands/seek.js +598 -0
  17. package/bin/cli/commands/tree.js +230 -0
  18. package/bin/cli/commands/trust.js +154 -0
  19. package/bin/cli/commands/verify-audit.js +114 -0
  20. package/bin/cli/commands/view.js +46 -0
  21. package/bin/cli/infrastructure.js +350 -0
  22. package/bin/cli/schemas.js +177 -0
  23. package/bin/cli/shared.js +244 -0
  24. package/bin/cli/types.js +96 -0
  25. package/bin/presenters/index.js +41 -9
  26. package/bin/presenters/json.js +14 -12
  27. package/bin/presenters/text.js +286 -28
  28. package/bin/warp-graph.js +5 -2346
  29. package/index.d.ts +111 -21
  30. package/index.js +2 -0
  31. package/package.json +10 -8
  32. package/src/domain/WarpGraph.js +109 -3252
  33. package/src/domain/crdt/ORSet.js +8 -8
  34. package/src/domain/errors/EmptyMessageError.js +2 -2
  35. package/src/domain/errors/ForkError.js +1 -1
  36. package/src/domain/errors/IndexError.js +1 -1
  37. package/src/domain/errors/OperationAbortedError.js +1 -1
  38. package/src/domain/errors/QueryError.js +3 -3
  39. package/src/domain/errors/SchemaUnsupportedError.js +1 -1
  40. package/src/domain/errors/ShardCorruptionError.js +2 -2
  41. package/src/domain/errors/ShardLoadError.js +2 -2
  42. package/src/domain/errors/ShardValidationError.js +4 -4
  43. package/src/domain/errors/StorageError.js +2 -2
  44. package/src/domain/errors/SyncError.js +1 -1
  45. package/src/domain/errors/TraversalError.js +1 -1
  46. package/src/domain/errors/TrustError.js +29 -0
  47. package/src/domain/errors/WarpError.js +2 -2
  48. package/src/domain/errors/WormholeError.js +1 -1
  49. package/src/domain/errors/index.js +1 -0
  50. package/src/domain/services/AuditMessageCodec.js +137 -0
  51. package/src/domain/services/AuditReceiptService.js +471 -0
  52. package/src/domain/services/AuditVerifierService.js +707 -0
  53. package/src/domain/services/BitmapIndexBuilder.js +3 -3
  54. package/src/domain/services/BitmapIndexReader.js +28 -19
  55. package/src/domain/services/BoundaryTransitionRecord.js +18 -17
  56. package/src/domain/services/CheckpointSerializerV5.js +17 -16
  57. package/src/domain/services/CheckpointService.js +2 -2
  58. package/src/domain/services/CommitDagTraversalService.js +13 -13
  59. package/src/domain/services/DagPathFinding.js +7 -7
  60. package/src/domain/services/DagTopology.js +1 -1
  61. package/src/domain/services/DagTraversal.js +1 -1
  62. package/src/domain/services/HealthCheckService.js +1 -1
  63. package/src/domain/services/HookInstaller.js +1 -1
  64. package/src/domain/services/HttpSyncServer.js +120 -55
  65. package/src/domain/services/IndexRebuildService.js +7 -7
  66. package/src/domain/services/IndexStalenessChecker.js +4 -3
  67. package/src/domain/services/JoinReducer.js +11 -11
  68. package/src/domain/services/LogicalTraversal.js +1 -1
  69. package/src/domain/services/MessageCodecInternal.js +4 -1
  70. package/src/domain/services/MessageSchemaDetector.js +2 -2
  71. package/src/domain/services/MigrationService.js +1 -1
  72. package/src/domain/services/ObserverView.js +8 -8
  73. package/src/domain/services/PatchBuilderV2.js +42 -26
  74. package/src/domain/services/ProvenanceIndex.js +1 -1
  75. package/src/domain/services/ProvenancePayload.js +1 -1
  76. package/src/domain/services/QueryBuilder.js +3 -3
  77. package/src/domain/services/StateDiff.js +14 -11
  78. package/src/domain/services/StateSerializerV5.js +2 -2
  79. package/src/domain/services/StreamingBitmapIndexBuilder.js +26 -24
  80. package/src/domain/services/SyncAuthService.js +71 -4
  81. package/src/domain/services/SyncProtocol.js +25 -11
  82. package/src/domain/services/TemporalQuery.js +9 -6
  83. package/src/domain/services/TranslationCost.js +7 -5
  84. package/src/domain/services/WarpMessageCodec.js +4 -1
  85. package/src/domain/services/WormholeService.js +16 -7
  86. package/src/domain/trust/TrustCanonical.js +42 -0
  87. package/src/domain/trust/TrustCrypto.js +111 -0
  88. package/src/domain/trust/TrustEvaluator.js +195 -0
  89. package/src/domain/trust/TrustRecordService.js +281 -0
  90. package/src/domain/trust/TrustStateBuilder.js +222 -0
  91. package/src/domain/trust/canonical.js +68 -0
  92. package/src/domain/trust/reasonCodes.js +64 -0
  93. package/src/domain/trust/schemas.js +160 -0
  94. package/src/domain/trust/verdict.js +42 -0
  95. package/src/domain/types/TickReceipt.js +1 -1
  96. package/src/domain/types/WarpErrors.js +45 -0
  97. package/src/domain/types/WarpOptions.js +29 -0
  98. package/src/domain/types/WarpPersistence.js +41 -0
  99. package/src/domain/types/WarpTypes.js +2 -2
  100. package/src/domain/types/WarpTypesV2.js +2 -2
  101. package/src/domain/types/git-cas.d.ts +20 -0
  102. package/src/domain/utils/MinHeap.js +6 -5
  103. package/src/domain/utils/RefLayout.js +59 -0
  104. package/src/domain/utils/canonicalStringify.js +5 -4
  105. package/src/domain/utils/roaring.js +31 -5
  106. package/src/domain/warp/PatchSession.js +26 -17
  107. package/src/domain/warp/Writer.js +18 -3
  108. package/src/domain/warp/_internal.js +26 -0
  109. package/src/domain/warp/_wire.js +58 -0
  110. package/src/domain/warp/_wiredMethods.d.ts +254 -0
  111. package/src/domain/warp/checkpoint.methods.js +401 -0
  112. package/src/domain/warp/fork.methods.js +323 -0
  113. package/src/domain/warp/materialize.methods.js +238 -0
  114. package/src/domain/warp/materializeAdvanced.methods.js +350 -0
  115. package/src/domain/warp/patch.methods.js +554 -0
  116. package/src/domain/warp/provenance.methods.js +286 -0
  117. package/src/domain/warp/query.methods.js +280 -0
  118. package/src/domain/warp/subscribe.methods.js +272 -0
  119. package/src/domain/warp/sync.methods.js +554 -0
  120. package/src/globals.d.ts +64 -0
  121. package/src/infrastructure/adapters/BunHttpAdapter.js +14 -9
  122. package/src/infrastructure/adapters/CasSeekCacheAdapter.js +9 -4
  123. package/src/infrastructure/adapters/DenoHttpAdapter.js +5 -6
  124. package/src/infrastructure/adapters/GitGraphAdapter.js +79 -11
  125. package/src/infrastructure/adapters/InMemoryGraphAdapter.js +36 -0
  126. package/src/infrastructure/adapters/NodeHttpAdapter.js +2 -2
  127. package/src/infrastructure/adapters/WebCryptoAdapter.js +2 -2
  128. package/src/ports/CommitPort.js +10 -0
  129. package/src/ports/RefPort.js +17 -0
  130. package/src/visualization/layouts/converters.js +2 -2
  131. package/src/visualization/layouts/elkAdapter.js +1 -1
  132. package/src/visualization/layouts/elkLayout.js +10 -7
  133. package/src/visualization/layouts/index.js +1 -1
  134. package/src/visualization/renderers/ascii/seek.js +16 -6
  135. package/src/visualization/renderers/svg/index.js +1 -1
  136. package/src/hooks/post-merge.sh +0 -60
@@ -0,0 +1,272 @@
1
+ /**
2
+ * @module domain/warp/subscribe.methods
3
+ *
4
+ * Extracted subscribe, watch, and _notifySubscribers methods from WarpGraph.
5
+ * Each function is bound to a WarpGraph instance at runtime via `this`.
6
+ */
7
+
8
+ import { diffStates, isEmptyDiff } from '../services/StateDiff.js';
9
+
10
+ /**
11
+ * Subscribes to graph changes.
12
+ *
13
+ * The `onChange` handler is called after each `materialize()` that results in
14
+ * state changes. The handler receives a diff object describing what changed.
15
+ *
16
+ * When `replay: true` is set and `_cachedState` is available, immediately
17
+ * fires `onChange` with a diff from empty state to current state. If
18
+ * `_cachedState` is null, replay is deferred until the first materialize.
19
+ *
20
+ * Errors thrown by handlers are caught and forwarded to `onError` if provided.
21
+ * One handler's error does not prevent other handlers from being called.
22
+ *
23
+ * @this {import('../WarpGraph.js').default}
24
+ * @param {Object} options - Subscription options
25
+ * @param {(diff: import('../services/StateDiff.js').StateDiffResult) => void} options.onChange - Called with diff when graph changes
26
+ * @param {(error: Error) => void} [options.onError] - Called if onChange throws an error
27
+ * @param {boolean} [options.replay=false] - If true, immediately fires onChange with initial state diff
28
+ * @returns {{unsubscribe: () => void}} Subscription handle
29
+ * @throws {Error} If onChange is not a function
30
+ *
31
+ * @example
32
+ * const { unsubscribe } = graph.subscribe({
33
+ * onChange: (diff) => {
34
+ * console.log('Nodes added:', diff.nodes.added);
35
+ * console.log('Nodes removed:', diff.nodes.removed);
36
+ * },
37
+ * onError: (err) => console.error('Handler error:', err),
38
+ * });
39
+ *
40
+ * // Later, to stop receiving updates:
41
+ * unsubscribe();
42
+ *
43
+ * @example
44
+ * // With replay: get initial state immediately
45
+ * await graph.materialize();
46
+ * graph.subscribe({
47
+ * onChange: (diff) => console.log('Initial or changed:', diff),
48
+ * replay: true, // Immediately fires with current state as additions
49
+ * });
50
+ */
51
+ export function subscribe({ onChange, onError, replay = false }) {
52
+ if (typeof onChange !== 'function') {
53
+ throw new Error('onChange must be a function');
54
+ }
55
+
56
+ const subscriber = { onChange, onError, pendingReplay: replay && !this._cachedState };
57
+ this._subscribers.push(subscriber);
58
+
59
+ // Immediate replay if requested and cached state is available
60
+ if (replay && this._cachedState) {
61
+ const diff = diffStates(null, this._cachedState);
62
+ if (!isEmptyDiff(diff)) {
63
+ try {
64
+ onChange(diff);
65
+ } catch (err) {
66
+ if (onError) {
67
+ try {
68
+ onError(/** @type {Error} */ (err));
69
+ } catch {
70
+ // onError itself threw — swallow to prevent cascade
71
+ }
72
+ }
73
+ }
74
+ }
75
+ }
76
+
77
+ return {
78
+ unsubscribe: () => {
79
+ const index = this._subscribers.indexOf(subscriber);
80
+ if (index !== -1) {
81
+ this._subscribers.splice(index, 1);
82
+ }
83
+ },
84
+ };
85
+ }
86
+
87
+ /**
88
+ * Watches for graph changes matching a pattern.
89
+ *
90
+ * Like `subscribe()`, but only fires for changes where node IDs match the
91
+ * provided glob pattern. Uses the same pattern syntax as `query().match()`.
92
+ *
93
+ * - Nodes: filters `added` and `removed` to matching IDs
94
+ * - Edges: filters to edges where `from` or `to` matches the pattern
95
+ * - Props: filters to properties where `nodeId` matches the pattern
96
+ *
97
+ * If all changes are filtered out, the handler is not called.
98
+ *
99
+ * When `poll` is set, periodically checks `hasFrontierChanged()` and auto-materializes
100
+ * if the frontier has changed (e.g., remote writes detected). The poll interval must
101
+ * be at least 1000ms.
102
+ *
103
+ * @this {import('../WarpGraph.js').default}
104
+ * @param {string} pattern - Glob pattern (e.g., 'user:*', 'order:123', '*')
105
+ * @param {Object} options - Watch options
106
+ * @param {(diff: import('../services/StateDiff.js').StateDiffResult) => void} options.onChange - Called with filtered diff when matching changes occur
107
+ * @param {(error: Error) => void} [options.onError] - Called if onChange throws an error
108
+ * @param {number} [options.poll] - Poll interval in ms (min 1000); checks frontier and auto-materializes
109
+ * @returns {{unsubscribe: () => void}} Subscription handle
110
+ * @throws {Error} If pattern is not a string
111
+ * @throws {Error} If onChange is not a function
112
+ * @throws {Error} If poll is provided but less than 1000
113
+ *
114
+ * @example
115
+ * const { unsubscribe } = graph.watch('user:*', {
116
+ * onChange: (diff) => {
117
+ * // Only user node changes arrive here
118
+ * console.log('User nodes added:', diff.nodes.added);
119
+ * },
120
+ * });
121
+ *
122
+ * @example
123
+ * // With polling: checks every 5s for remote changes
124
+ * const { unsubscribe } = graph.watch('user:*', {
125
+ * onChange: (diff) => console.log('User changed:', diff),
126
+ * poll: 5000,
127
+ * });
128
+ *
129
+ * // Later, to stop receiving updates:
130
+ * unsubscribe();
131
+ */
132
+ export function watch(pattern, { onChange, onError, poll }) {
133
+ if (typeof pattern !== 'string') {
134
+ throw new Error('pattern must be a string');
135
+ }
136
+ if (typeof onChange !== 'function') {
137
+ throw new Error('onChange must be a function');
138
+ }
139
+ if (poll !== undefined) {
140
+ if (typeof poll !== 'number' || poll < 1000) {
141
+ throw new Error('poll must be a number >= 1000');
142
+ }
143
+ }
144
+
145
+ // Pattern matching: same logic as QueryBuilder.match()
146
+ // Pre-compile pattern matcher once for performance
147
+ /** @type {(nodeId: string) => boolean} */
148
+ let matchesPattern;
149
+ if (pattern === '*') {
150
+ matchesPattern = () => true;
151
+ } else if (pattern.includes('*')) {
152
+ const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, '\\$&');
153
+ const regex = new RegExp(`^${escaped.replace(/\*/g, '.*')}$`);
154
+ matchesPattern = (/** @type {string} */ nodeId) => regex.test(nodeId);
155
+ } else {
156
+ matchesPattern = (/** @type {string} */ nodeId) => nodeId === pattern;
157
+ }
158
+
159
+ // Filtered onChange that only passes matching changes
160
+ const filteredOnChange = (/** @type {import('../services/StateDiff.js').StateDiffResult} */ diff) => {
161
+ const filteredDiff = {
162
+ nodes: {
163
+ added: diff.nodes.added.filter(matchesPattern),
164
+ removed: diff.nodes.removed.filter(matchesPattern),
165
+ },
166
+ edges: {
167
+ added: diff.edges.added.filter((/** @type {import('../services/StateDiff.js').EdgeChange} */ e) => matchesPattern(e.from) || matchesPattern(e.to)),
168
+ removed: diff.edges.removed.filter((/** @type {import('../services/StateDiff.js').EdgeChange} */ e) => matchesPattern(e.from) || matchesPattern(e.to)),
169
+ },
170
+ props: {
171
+ set: diff.props.set.filter((/** @type {import('../services/StateDiff.js').PropSet} */ p) => matchesPattern(p.nodeId)),
172
+ removed: diff.props.removed.filter((/** @type {import('../services/StateDiff.js').PropRemoved} */ p) => matchesPattern(p.nodeId)),
173
+ },
174
+ };
175
+
176
+ // Only call handler if there are matching changes
177
+ const hasChanges =
178
+ filteredDiff.nodes.added.length > 0 ||
179
+ filteredDiff.nodes.removed.length > 0 ||
180
+ filteredDiff.edges.added.length > 0 ||
181
+ filteredDiff.edges.removed.length > 0 ||
182
+ filteredDiff.props.set.length > 0 ||
183
+ filteredDiff.props.removed.length > 0;
184
+
185
+ if (hasChanges) {
186
+ onChange(filteredDiff);
187
+ }
188
+ };
189
+
190
+ // Reuse subscription infrastructure
191
+ const subscription = this.subscribe({ onChange: filteredOnChange, onError });
192
+
193
+ // Polling: periodically check frontier and auto-materialize if changed
194
+ /** @type {ReturnType<typeof setInterval>|null} */
195
+ let pollIntervalId = null;
196
+ let pollInFlight = false;
197
+ if (poll) {
198
+ pollIntervalId = setInterval(() => {
199
+ if (pollInFlight) {
200
+ return;
201
+ }
202
+ pollInFlight = true;
203
+ this.hasFrontierChanged()
204
+ .then(async (changed) => {
205
+ if (changed) {
206
+ await this.materialize();
207
+ }
208
+ })
209
+ .catch((err) => {
210
+ if (onError) {
211
+ try {
212
+ onError(err);
213
+ } catch {
214
+ // onError itself threw — swallow to prevent cascade
215
+ }
216
+ }
217
+ })
218
+ .finally(() => {
219
+ pollInFlight = false;
220
+ });
221
+ }, poll);
222
+ }
223
+
224
+ return {
225
+ unsubscribe: () => {
226
+ if (pollIntervalId !== null) {
227
+ clearInterval(pollIntervalId);
228
+ pollIntervalId = null;
229
+ }
230
+ subscription.unsubscribe();
231
+ },
232
+ };
233
+ }
234
+
235
+ /**
236
+ * Notifies all subscribers of state changes.
237
+ * Handles deferred replay for subscribers added with `replay: true` before
238
+ * cached state was available.
239
+ *
240
+ * @this {import('../WarpGraph.js').default}
241
+ * @param {import('../services/StateDiff.js').StateDiffResult} diff
242
+ * @param {import('../services/JoinReducer.js').WarpStateV5} currentState - The current state for deferred replay
243
+ * @private
244
+ */
245
+ export function _notifySubscribers(diff, currentState) {
246
+ for (const subscriber of [...this._subscribers]) {
247
+ try {
248
+ // Handle deferred replay: on first notification, send full state diff instead
249
+ if (subscriber.pendingReplay) {
250
+ subscriber.pendingReplay = false;
251
+ const replayDiff = diffStates(null, currentState);
252
+ if (!isEmptyDiff(replayDiff)) {
253
+ subscriber.onChange(replayDiff);
254
+ }
255
+ } else {
256
+ // Skip non-replay subscribers when diff is empty
257
+ if (isEmptyDiff(diff)) {
258
+ continue;
259
+ }
260
+ subscriber.onChange(diff);
261
+ }
262
+ } catch (err) {
263
+ if (subscriber.onError) {
264
+ try {
265
+ subscriber.onError(err);
266
+ } catch {
267
+ // onError itself threw — swallow to prevent cascade
268
+ }
269
+ }
270
+ }
271
+ }
272
+ }