@git-stunts/git-warp 11.2.1 → 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.
- package/bin/cli/commands/check.js +2 -2
- package/bin/cli/commands/doctor/checks.js +12 -12
- package/bin/cli/commands/doctor/index.js +2 -2
- package/bin/cli/commands/doctor/types.js +1 -1
- package/bin/cli/commands/history.js +12 -5
- package/bin/cli/commands/install-hooks.js +5 -5
- package/bin/cli/commands/materialize.js +2 -2
- package/bin/cli/commands/patch.js +142 -0
- package/bin/cli/commands/path.js +4 -4
- package/bin/cli/commands/query.js +54 -13
- package/bin/cli/commands/registry.js +4 -0
- package/bin/cli/commands/seek.js +17 -11
- package/bin/cli/commands/tree.js +230 -0
- package/bin/cli/commands/trust.js +3 -3
- package/bin/cli/commands/verify-audit.js +8 -7
- package/bin/cli/commands/view.js +6 -5
- package/bin/cli/infrastructure.js +26 -12
- package/bin/cli/shared.js +2 -2
- package/bin/cli/types.js +19 -8
- package/bin/presenters/index.js +35 -9
- package/bin/presenters/json.js +14 -12
- package/bin/presenters/text.js +155 -33
- package/index.d.ts +82 -22
- package/package.json +3 -2
- package/src/domain/WarpGraph.js +4 -1
- package/src/domain/crdt/ORSet.js +8 -8
- package/src/domain/errors/EmptyMessageError.js +2 -2
- package/src/domain/errors/ForkError.js +1 -1
- package/src/domain/errors/IndexError.js +1 -1
- package/src/domain/errors/OperationAbortedError.js +1 -1
- package/src/domain/errors/QueryError.js +1 -1
- package/src/domain/errors/SchemaUnsupportedError.js +1 -1
- package/src/domain/errors/ShardCorruptionError.js +2 -2
- package/src/domain/errors/ShardLoadError.js +2 -2
- package/src/domain/errors/ShardValidationError.js +4 -4
- package/src/domain/errors/StorageError.js +2 -2
- package/src/domain/errors/SyncError.js +1 -1
- package/src/domain/errors/TraversalError.js +1 -1
- package/src/domain/errors/TrustError.js +1 -1
- package/src/domain/errors/WarpError.js +2 -2
- package/src/domain/errors/WormholeError.js +1 -1
- package/src/domain/services/AuditReceiptService.js +6 -6
- package/src/domain/services/AuditVerifierService.js +52 -38
- package/src/domain/services/BitmapIndexBuilder.js +3 -3
- package/src/domain/services/BitmapIndexReader.js +28 -19
- package/src/domain/services/BoundaryTransitionRecord.js +18 -17
- package/src/domain/services/CheckpointSerializerV5.js +17 -16
- package/src/domain/services/CheckpointService.js +2 -2
- package/src/domain/services/CommitDagTraversalService.js +13 -13
- package/src/domain/services/DagPathFinding.js +7 -7
- package/src/domain/services/DagTopology.js +1 -1
- package/src/domain/services/DagTraversal.js +1 -1
- package/src/domain/services/HealthCheckService.js +1 -1
- package/src/domain/services/HookInstaller.js +1 -1
- package/src/domain/services/HttpSyncServer.js +92 -41
- package/src/domain/services/IndexRebuildService.js +7 -7
- package/src/domain/services/IndexStalenessChecker.js +4 -3
- package/src/domain/services/JoinReducer.js +11 -11
- package/src/domain/services/LogicalTraversal.js +1 -1
- package/src/domain/services/MessageCodecInternal.js +1 -1
- package/src/domain/services/MigrationService.js +1 -1
- package/src/domain/services/ObserverView.js +8 -8
- package/src/domain/services/PatchBuilderV2.js +42 -26
- package/src/domain/services/ProvenanceIndex.js +1 -1
- package/src/domain/services/ProvenancePayload.js +1 -1
- package/src/domain/services/QueryBuilder.js +3 -3
- package/src/domain/services/StateDiff.js +14 -11
- package/src/domain/services/StateSerializerV5.js +2 -2
- package/src/domain/services/StreamingBitmapIndexBuilder.js +26 -24
- package/src/domain/services/SyncAuthService.js +3 -2
- package/src/domain/services/SyncProtocol.js +25 -11
- package/src/domain/services/TemporalQuery.js +9 -6
- package/src/domain/services/TranslationCost.js +7 -5
- package/src/domain/services/WormholeService.js +16 -7
- package/src/domain/trust/TrustCanonical.js +3 -3
- package/src/domain/trust/TrustEvaluator.js +18 -3
- package/src/domain/trust/TrustRecordService.js +30 -23
- package/src/domain/trust/TrustStateBuilder.js +21 -8
- package/src/domain/trust/canonical.js +6 -6
- package/src/domain/types/TickReceipt.js +1 -1
- package/src/domain/types/WarpErrors.js +45 -0
- package/src/domain/types/WarpOptions.js +29 -0
- package/src/domain/types/WarpPersistence.js +41 -0
- package/src/domain/types/WarpTypes.js +2 -2
- package/src/domain/types/WarpTypesV2.js +2 -2
- package/src/domain/utils/MinHeap.js +6 -5
- package/src/domain/utils/canonicalStringify.js +5 -4
- package/src/domain/utils/roaring.js +31 -5
- package/src/domain/warp/PatchSession.js +9 -18
- package/src/domain/warp/_wiredMethods.d.ts +199 -45
- package/src/domain/warp/checkpoint.methods.js +5 -1
- package/src/domain/warp/fork.methods.js +2 -2
- package/src/domain/warp/materialize.methods.js +55 -5
- package/src/domain/warp/materializeAdvanced.methods.js +15 -4
- package/src/domain/warp/patch.methods.js +54 -29
- package/src/domain/warp/provenance.methods.js +5 -3
- package/src/domain/warp/query.methods.js +6 -5
- package/src/domain/warp/sync.methods.js +16 -11
- package/src/globals.d.ts +64 -0
- package/src/infrastructure/adapters/BunHttpAdapter.js +14 -9
- package/src/infrastructure/adapters/CasSeekCacheAdapter.js +9 -4
- package/src/infrastructure/adapters/DenoHttpAdapter.js +5 -6
- package/src/infrastructure/adapters/GitGraphAdapter.js +14 -12
- package/src/infrastructure/adapters/NodeHttpAdapter.js +2 -2
- package/src/infrastructure/adapters/WebCryptoAdapter.js +2 -2
- package/src/visualization/layouts/converters.js +2 -2
- package/src/visualization/layouts/elkAdapter.js +1 -1
- package/src/visualization/layouts/elkLayout.js +10 -7
- package/src/visualization/layouts/index.js +1 -1
- package/src/visualization/renderers/ascii/seek.js +16 -6
- package/src/visualization/renderers/svg/index.js +1 -1
package/bin/presenters/text.js
CHANGED
|
@@ -7,6 +7,28 @@
|
|
|
7
7
|
|
|
8
8
|
import { formatStructuralDiff } from '../../src/visualization/renderers/ascii/seek.js';
|
|
9
9
|
|
|
10
|
+
// ── Payload typedefs ────────────────────────────────────────────────────────
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @typedef {{ installed: boolean, foreign?: boolean, current?: boolean, version?: string }} HookStatus
|
|
14
|
+
* @typedef {{ repo: string, graphs: Array<{ name: string, writers?: { count: number } | null, checkpoint?: { sha: string } | null, coverage?: { sha: string } | null, cursor?: { active: boolean, tick: number, mode: string } | null }> }} InfoPayload
|
|
15
|
+
* @typedef {{ graph: string, stateHash?: string, nodes: Array<{ id?: string, props?: Record<string, unknown>, edges?: NodeEdges }>, _renderedAscii?: string, _renderedSvg?: string }} QueryPayload
|
|
16
|
+
* @typedef {{ outgoing?: Array<{ label: string, to: string }>, incoming?: Array<{ label: string, from: string }> }} NodeEdges
|
|
17
|
+
* @typedef {{ graph: string, from: string, to: string, found: boolean, length?: number, path?: string[] }} PathPayload
|
|
18
|
+
* @typedef {{ graph: string, health: { status: string }, checkpoint?: { sha: string, ageSeconds: number | null } | null, writers: { count: number, heads: Array<{ writerId: string, sha: string }> }, coverage?: { sha: string, missingWriters: string[] } | null, gc?: { totalTombstones: number, tombstoneRatio: number } | null, hook?: HookStatus | null, status?: { cachedState: string, patchesSinceCheckpoint: number, tombstoneRatio: number, writers: number } | null }} CheckPayload
|
|
19
|
+
* @typedef {{ graph: string, writer: string, nodeFilter?: string | null, entries: Array<{ sha: string, lamport: number, opCount: number }> }} HistoryPayload
|
|
20
|
+
* @typedef {{ error: { message: string } }} ErrorPayload
|
|
21
|
+
* @typedef {{ graphs: Array<{ graph: string, nodes?: number, edges?: number, checkpoint?: string, error?: string }> }} MaterializePayload
|
|
22
|
+
* @typedef {{ action: string, hookPath?: string, version?: string, backupPath?: string, name?: string }} InstallHooksPayload
|
|
23
|
+
* @typedef {{ graph?: string, action: string, tick?: number, maxTick?: number, ticks?: number[], nodes?: number, edges?: number, patchCount?: number, perWriter?: Record<string, unknown>, diff?: { nodes?: number, edges?: number } | null, tickReceipt?: Record<string, unknown>, structuralDiff?: import('../../src/domain/services/StateDiff.js').StateDiffResult | null, cursor?: { active: boolean, tick?: number }, message?: string, cursors?: Array<{ name: string, tick: number }>, activeTick?: number | null, name?: string, diffBaseline?: string, baselineTick?: number | null, truncated?: boolean, totalChanges?: number, shownChanges?: number }} SeekPayload
|
|
24
|
+
* @typedef {{ graph: string, health: string, checkedAt: string, summary: { checksRun: number, findingsTotal: number, ok: number, warn: number, fail: number, priorityActions: string[] }, findings: Array<{ status: string, id: string, message: string, fix?: string }> }} DoctorPayload
|
|
25
|
+
* @typedef {{ graph: string, verifiedAt: string, summary: { total: number, valid: number, partial: number, invalid: number }, chains: Array<{ writerId: string, status: string, receiptsVerified: number, since?: string, errors: Array<{ code: string, message: string }>, warnings: Array<{ code: string, message: string }> }>, trustWarning?: { message: string } }} VerifyAuditPayload
|
|
26
|
+
* @typedef {{ graph: string, trustVerdict: string, mode: string, trust: { source: string, evidenceSummary: { activeKeys: number, revokedKeys: number, activeBindings: number }, explanations: Array<{ trusted: boolean, writerId: string, reasonCode: string, reason: string }>, untrustedWriters: string[] } }} TrustPayload
|
|
27
|
+
* @typedef {{ type: string, node?: string, from?: string, to?: string, label?: string, key?: string, value?: unknown }} PatchOp
|
|
28
|
+
* @typedef {{ graph: string, sha: string, writer: string, lamport: number, schema?: number, ops: PatchOp[] }} PatchShowPayload
|
|
29
|
+
* @typedef {{ graph: string, total: number, showing: number, writerFilter?: string | null, entries: Array<{ sha: string, writer: string, lamport: number, opCount: number, nodeIds: string[] }> }} PatchListPayload
|
|
30
|
+
*/
|
|
31
|
+
|
|
10
32
|
// ── ANSI helpers ─────────────────────────────────────────────────────────────
|
|
11
33
|
|
|
12
34
|
const ANSI_GREEN = '\x1b[32m';
|
|
@@ -26,7 +48,7 @@ function colorCachedState(state) {
|
|
|
26
48
|
return `${ANSI_RED}${ANSI_DIM}${state}${ANSI_RESET}`;
|
|
27
49
|
}
|
|
28
50
|
|
|
29
|
-
/** @param {
|
|
51
|
+
/** @param {HookStatus} hook */
|
|
30
52
|
function formatHookStatusLine(hook) {
|
|
31
53
|
if (!hook.installed && hook.foreign) {
|
|
32
54
|
return "Hook: foreign hook present — run 'git warp install-hooks'";
|
|
@@ -42,7 +64,7 @@ function formatHookStatusLine(hook) {
|
|
|
42
64
|
|
|
43
65
|
// ── Simple renderers ─────────────────────────────────────────────────────────
|
|
44
66
|
|
|
45
|
-
/** @param {
|
|
67
|
+
/** @param {InfoPayload} payload */
|
|
46
68
|
export function renderInfo(payload) {
|
|
47
69
|
const lines = [`Repo: ${payload.repo}`];
|
|
48
70
|
lines.push(`Graphs: ${payload.graphs.length}`);
|
|
@@ -62,7 +84,25 @@ export function renderInfo(payload) {
|
|
|
62
84
|
return `${lines.join('\n')}\n`;
|
|
63
85
|
}
|
|
64
86
|
|
|
65
|
-
/**
|
|
87
|
+
/**
|
|
88
|
+
* Appends edge lines for a single node to the output array.
|
|
89
|
+
* @param {string[]} lines
|
|
90
|
+
* @param {NodeEdges} edges
|
|
91
|
+
*/
|
|
92
|
+
function appendNodeEdges(lines, edges) {
|
|
93
|
+
if (edges.outgoing && edges.outgoing.length > 0) {
|
|
94
|
+
for (const e of edges.outgoing) {
|
|
95
|
+
lines.push(` -> ${e.label} -> ${e.to}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (edges.incoming && edges.incoming.length > 0) {
|
|
99
|
+
for (const e of edges.incoming) {
|
|
100
|
+
lines.push(` <- ${e.label} <- ${e.from}`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** @param {QueryPayload} payload */
|
|
66
106
|
export function renderQuery(payload) {
|
|
67
107
|
const lines = [
|
|
68
108
|
`Graph: ${payload.graph}`,
|
|
@@ -76,21 +116,27 @@ export function renderQuery(payload) {
|
|
|
76
116
|
if (node.props && Object.keys(node.props).length > 0) {
|
|
77
117
|
lines.push(` props: ${JSON.stringify(node.props)}`);
|
|
78
118
|
}
|
|
119
|
+
if (node.edges) {
|
|
120
|
+
appendNodeEdges(lines, node.edges);
|
|
121
|
+
}
|
|
79
122
|
}
|
|
80
123
|
|
|
81
124
|
return `${lines.join('\n')}\n`;
|
|
82
125
|
}
|
|
83
126
|
|
|
84
|
-
/** @param {
|
|
127
|
+
/** @param {PathPayload} payload */
|
|
85
128
|
export function renderPath(payload) {
|
|
86
129
|
const lines = [
|
|
87
130
|
`Graph: ${payload.graph}`,
|
|
88
131
|
`From: ${payload.from}`,
|
|
89
132
|
`To: ${payload.to}`,
|
|
90
133
|
`Found: ${payload.found ? 'yes' : 'no'}`,
|
|
91
|
-
`Length: ${payload.length}`,
|
|
92
134
|
];
|
|
93
135
|
|
|
136
|
+
if (payload.found) {
|
|
137
|
+
lines.push(`Length: ${payload.length}`);
|
|
138
|
+
}
|
|
139
|
+
|
|
94
140
|
if (payload.path && payload.path.length > 0) {
|
|
95
141
|
lines.push(`Path: ${payload.path.join(' -> ')}`);
|
|
96
142
|
}
|
|
@@ -101,7 +147,7 @@ export function renderPath(payload) {
|
|
|
101
147
|
/**
|
|
102
148
|
* Appends checkpoint and writer lines to check output.
|
|
103
149
|
* @param {string[]} lines
|
|
104
|
-
* @param {
|
|
150
|
+
* @param {CheckPayload} payload
|
|
105
151
|
*/
|
|
106
152
|
function appendCheckpointAndWriters(lines, payload) {
|
|
107
153
|
if (payload.checkpoint?.sha) {
|
|
@@ -124,7 +170,7 @@ function appendCheckpointAndWriters(lines, payload) {
|
|
|
124
170
|
/**
|
|
125
171
|
* Appends coverage, gc, and hook lines to check output.
|
|
126
172
|
* @param {string[]} lines
|
|
127
|
-
* @param {
|
|
173
|
+
* @param {CheckPayload} payload
|
|
128
174
|
*/
|
|
129
175
|
function appendCoverageAndExtras(lines, payload) {
|
|
130
176
|
if (payload.coverage?.sha) {
|
|
@@ -146,7 +192,7 @@ function appendCoverageAndExtras(lines, payload) {
|
|
|
146
192
|
}
|
|
147
193
|
}
|
|
148
194
|
|
|
149
|
-
/** @param {
|
|
195
|
+
/** @param {CheckPayload} payload */
|
|
150
196
|
export function renderCheck(payload) {
|
|
151
197
|
const lines = [
|
|
152
198
|
`Graph: ${payload.graph}`,
|
|
@@ -165,7 +211,7 @@ export function renderCheck(payload) {
|
|
|
165
211
|
return `${lines.join('\n')}\n`;
|
|
166
212
|
}
|
|
167
213
|
|
|
168
|
-
/** @param {
|
|
214
|
+
/** @param {HistoryPayload} payload */
|
|
169
215
|
export function renderHistory(payload) {
|
|
170
216
|
const lines = [
|
|
171
217
|
`Graph: ${payload.graph}`,
|
|
@@ -184,12 +230,12 @@ export function renderHistory(payload) {
|
|
|
184
230
|
return `${lines.join('\n')}\n`;
|
|
185
231
|
}
|
|
186
232
|
|
|
187
|
-
/** @param {
|
|
233
|
+
/** @param {ErrorPayload} payload */
|
|
188
234
|
export function renderError(payload) {
|
|
189
235
|
return `Error: ${payload.error.message}\n`;
|
|
190
236
|
}
|
|
191
237
|
|
|
192
|
-
/** @param {
|
|
238
|
+
/** @param {MaterializePayload} payload */
|
|
193
239
|
export function renderMaterialize(payload) {
|
|
194
240
|
if (payload.graphs.length === 0) {
|
|
195
241
|
return 'No graphs found in repo.\n';
|
|
@@ -206,7 +252,7 @@ export function renderMaterialize(payload) {
|
|
|
206
252
|
return `${lines.join('\n')}\n`;
|
|
207
253
|
}
|
|
208
254
|
|
|
209
|
-
/** @param {
|
|
255
|
+
/** @param {InstallHooksPayload} payload */
|
|
210
256
|
export function renderInstallHooks(payload) {
|
|
211
257
|
if (payload.action === 'up-to-date') {
|
|
212
258
|
return `Hook: already up to date (v${payload.version}) at ${payload.hookPath}\n`;
|
|
@@ -225,10 +271,10 @@ export function renderInstallHooks(payload) {
|
|
|
225
271
|
|
|
226
272
|
/**
|
|
227
273
|
* Formats a numeric delta as " (+N)" or " (-N)", or empty string for zero/non-finite.
|
|
228
|
-
* @param {
|
|
274
|
+
* @param {unknown} n
|
|
229
275
|
* @returns {string}
|
|
230
276
|
*/
|
|
231
|
-
function formatDelta(n) {
|
|
277
|
+
function formatDelta(n) {
|
|
232
278
|
if (typeof n !== 'number' || !Number.isFinite(n) || n === 0) {
|
|
233
279
|
return '';
|
|
234
280
|
}
|
|
@@ -238,10 +284,10 @@ function formatDelta(n) { // TODO(ts-cleanup): type CLI payload
|
|
|
238
284
|
|
|
239
285
|
/**
|
|
240
286
|
* Formats an operation summary object as a compact plain-text string.
|
|
241
|
-
* @param {
|
|
287
|
+
* @param {Record<string, number> | null | undefined} summary
|
|
242
288
|
* @returns {string}
|
|
243
289
|
*/
|
|
244
|
-
function formatOpSummaryPlain(summary) {
|
|
290
|
+
function formatOpSummaryPlain(summary) {
|
|
245
291
|
const order = [
|
|
246
292
|
['NodeAdd', '+', 'node'],
|
|
247
293
|
['EdgeAdd', '+', 'edge'],
|
|
@@ -264,7 +310,7 @@ function formatOpSummaryPlain(summary) { // TODO(ts-cleanup): type CLI payload
|
|
|
264
310
|
/**
|
|
265
311
|
* Appends a per-writer tick receipt summary below a base line.
|
|
266
312
|
* @param {string} baseLine
|
|
267
|
-
* @param {
|
|
313
|
+
* @param {SeekPayload} payload
|
|
268
314
|
* @returns {string}
|
|
269
315
|
*/
|
|
270
316
|
function appendReceiptSummary(baseLine, payload) {
|
|
@@ -284,8 +330,12 @@ function appendReceiptSummary(baseLine, payload) {
|
|
|
284
330
|
const maxWriterLen = Math.max(5, ...entries.map(([writerId]) => writerId.length));
|
|
285
331
|
const receiptLines = [` Tick ${payload.tick}:`];
|
|
286
332
|
for (const [writerId, entry] of entries) {
|
|
287
|
-
|
|
288
|
-
const
|
|
333
|
+
/** @type {Record<string, unknown>} */
|
|
334
|
+
const rec = /** @type {Record<string, unknown>} */ (entry);
|
|
335
|
+
const sha = typeof rec.sha === 'string' ? rec.sha.slice(0, 7) : '';
|
|
336
|
+
const opSummary = rec.opSummary && typeof rec.opSummary === 'object'
|
|
337
|
+
? /** @type {Record<string, number>} */ (rec.opSummary)
|
|
338
|
+
: /** @type {Record<string, number>} */ (rec);
|
|
289
339
|
receiptLines.push(` ${writerId.padEnd(maxWriterLen)} ${sha.padEnd(7)} ${formatOpSummaryPlain(opSummary)}`);
|
|
290
340
|
}
|
|
291
341
|
|
|
@@ -294,7 +344,7 @@ function appendReceiptSummary(baseLine, payload) {
|
|
|
294
344
|
|
|
295
345
|
/**
|
|
296
346
|
* Builds human-readable state count strings from a seek payload.
|
|
297
|
-
* @param {
|
|
347
|
+
* @param {SeekPayload} payload
|
|
298
348
|
* @returns {{nodesStr: string, edgesStr: string, patchesStr: string}}
|
|
299
349
|
*/
|
|
300
350
|
function buildStateStrings(payload) {
|
|
@@ -310,20 +360,20 @@ function buildStateStrings(payload) {
|
|
|
310
360
|
|
|
311
361
|
/**
|
|
312
362
|
* Renders the "tick" / "latest" / "load" seek action with receipt + structural diff.
|
|
313
|
-
* @param {
|
|
363
|
+
* @param {SeekPayload} payload
|
|
314
364
|
* @param {string} headerLine
|
|
315
365
|
* @returns {string}
|
|
316
366
|
*/
|
|
317
367
|
function renderSeekWithDiff(payload, headerLine) {
|
|
318
368
|
const base = appendReceiptSummary(headerLine, payload);
|
|
319
|
-
return base + formatStructuralDiff(payload);
|
|
369
|
+
return base + formatStructuralDiff(/** @type {import('../../src/visualization/renderers/ascii/seek.js').SeekPayload} */ (payload));
|
|
320
370
|
}
|
|
321
371
|
|
|
322
372
|
// ── Seek simple-action renderers ─────────────────────────────────────────────
|
|
323
373
|
|
|
324
374
|
/**
|
|
325
375
|
* Renders seek actions that don't involve state counts: clear-cache, list, drop, save.
|
|
326
|
-
* @param {
|
|
376
|
+
* @param {SeekPayload} payload
|
|
327
377
|
* @returns {string|null} Rendered string, or null if action is not simple
|
|
328
378
|
*/
|
|
329
379
|
function renderSeekSimple(payload) {
|
|
@@ -344,11 +394,11 @@ function renderSeekSimple(payload) {
|
|
|
344
394
|
|
|
345
395
|
/**
|
|
346
396
|
* Renders the cursor list action.
|
|
347
|
-
* @param {
|
|
397
|
+
* @param {SeekPayload} payload
|
|
348
398
|
* @returns {string}
|
|
349
399
|
*/
|
|
350
400
|
function renderSeekList(payload) {
|
|
351
|
-
if (payload.cursors.length === 0) {
|
|
401
|
+
if (!payload.cursors || payload.cursors.length === 0) {
|
|
352
402
|
return 'No saved cursors.\n';
|
|
353
403
|
}
|
|
354
404
|
const lines = [];
|
|
@@ -363,7 +413,7 @@ function renderSeekList(payload) {
|
|
|
363
413
|
|
|
364
414
|
/**
|
|
365
415
|
* Renders seek actions that show state: latest, load, tick, status.
|
|
366
|
-
* @param {
|
|
416
|
+
* @param {SeekPayload} payload
|
|
367
417
|
* @returns {string}
|
|
368
418
|
*/
|
|
369
419
|
function renderSeekState(payload) {
|
|
@@ -396,19 +446,19 @@ function renderSeekState(payload) {
|
|
|
396
446
|
payload,
|
|
397
447
|
);
|
|
398
448
|
}
|
|
399
|
-
return `${payload.graph}: no cursor active, ${payload.ticks.length} ticks available\n`;
|
|
449
|
+
return `${payload.graph}: no cursor active, ${(payload.ticks ?? []).length} ticks available\n`;
|
|
400
450
|
}
|
|
401
451
|
|
|
402
452
|
// ── Seek main renderer ──────────────────────────────────────────────────────
|
|
403
453
|
|
|
404
|
-
/** @param {
|
|
454
|
+
/** @param {SeekPayload} payload */
|
|
405
455
|
export function renderSeek(payload) {
|
|
406
456
|
return renderSeekSimple(payload) ?? renderSeekState(payload);
|
|
407
457
|
}
|
|
408
458
|
|
|
409
459
|
// ── Doctor renderer ──────────────────────────────────────────────────────────
|
|
410
460
|
|
|
411
|
-
/** @param {
|
|
461
|
+
/** @param {string} status */
|
|
412
462
|
function findingIcon(status) {
|
|
413
463
|
if (status === 'ok') {
|
|
414
464
|
return `${ANSI_GREEN}\u2713${ANSI_RESET}`;
|
|
@@ -419,7 +469,7 @@ function findingIcon(status) {
|
|
|
419
469
|
return `${ANSI_RED}\u2717${ANSI_RESET}`;
|
|
420
470
|
}
|
|
421
471
|
|
|
422
|
-
/** @param {
|
|
472
|
+
/** @param {string} health */
|
|
423
473
|
function colorHealth(health) {
|
|
424
474
|
if (health === 'ok') {
|
|
425
475
|
return `${ANSI_GREEN}${health}${ANSI_RESET}`;
|
|
@@ -430,7 +480,7 @@ function colorHealth(health) {
|
|
|
430
480
|
return `${ANSI_RED}${health}${ANSI_RESET}`;
|
|
431
481
|
}
|
|
432
482
|
|
|
433
|
-
/** @param {
|
|
483
|
+
/** @param {DoctorPayload} payload */
|
|
434
484
|
export function renderDoctor(payload) {
|
|
435
485
|
const lines = [
|
|
436
486
|
`Graph: ${payload.graph}`,
|
|
@@ -468,7 +518,7 @@ function colorStatus(status) {
|
|
|
468
518
|
return `${ANSI_RED}${status}${ANSI_RESET}`;
|
|
469
519
|
}
|
|
470
520
|
|
|
471
|
-
/** @param {
|
|
521
|
+
/** @param {VerifyAuditPayload} payload */
|
|
472
522
|
export function renderVerifyAudit(payload) {
|
|
473
523
|
const lines = [
|
|
474
524
|
`Graph: ${payload.graph}`,
|
|
@@ -513,7 +563,7 @@ function colorVerdict(verdict) {
|
|
|
513
563
|
return `${ANSI_RED}${verdict}${ANSI_RESET}`;
|
|
514
564
|
}
|
|
515
565
|
|
|
516
|
-
/** @param {
|
|
566
|
+
/** @param {TrustPayload} payload */
|
|
517
567
|
export function renderTrust(payload) {
|
|
518
568
|
const lines = [
|
|
519
569
|
`Graph: ${payload.graph}`,
|
|
@@ -541,3 +591,75 @@ export function renderTrust(payload) {
|
|
|
541
591
|
|
|
542
592
|
return `${lines.join('\n')}\n`;
|
|
543
593
|
}
|
|
594
|
+
|
|
595
|
+
// ── Patch renderers ──────────────────────────────────────────────────────────
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* Formats a single operation line for patch show output.
|
|
599
|
+
* @param {PatchOp} op
|
|
600
|
+
* @returns {string|null}
|
|
601
|
+
*/
|
|
602
|
+
function formatPatchOp(op) {
|
|
603
|
+
if (op.type === 'NodeAdd') {
|
|
604
|
+
return ` + node ${op.node}`;
|
|
605
|
+
}
|
|
606
|
+
if (op.type === 'NodeTombstone') {
|
|
607
|
+
return ` - node ${op.node}`;
|
|
608
|
+
}
|
|
609
|
+
if (op.type === 'EdgeAdd') {
|
|
610
|
+
return ` + edge ${op.from} -[${op.label}]-> ${op.to}`;
|
|
611
|
+
}
|
|
612
|
+
if (op.type === 'EdgeTombstone') {
|
|
613
|
+
return ` - edge ${op.from} -[${op.label}]-> ${op.to}`;
|
|
614
|
+
}
|
|
615
|
+
if (op.type === 'PropSet') {
|
|
616
|
+
return ` ~ ${op.node}.${op.key} = ${JSON.stringify(op.value)}`;
|
|
617
|
+
}
|
|
618
|
+
if (op.type === 'BlobValue') {
|
|
619
|
+
return ` + blob ${op.node}`;
|
|
620
|
+
}
|
|
621
|
+
return null;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
/** @param {PatchShowPayload} payload */
|
|
625
|
+
export function renderPatchShow(payload) {
|
|
626
|
+
const lines = [
|
|
627
|
+
`Graph: ${payload.graph}`,
|
|
628
|
+
`SHA: ${payload.sha}`,
|
|
629
|
+
`Writer: ${payload.writer}`,
|
|
630
|
+
`Lamport: ${payload.lamport}`,
|
|
631
|
+
`Schema: ${payload.schema}`,
|
|
632
|
+
`Operations: ${payload.ops.length}`,
|
|
633
|
+
'',
|
|
634
|
+
];
|
|
635
|
+
|
|
636
|
+
for (const op of payload.ops) {
|
|
637
|
+
const line = formatPatchOp(op);
|
|
638
|
+
if (line) {
|
|
639
|
+
lines.push(line);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
return `${lines.join('\n')}\n`;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
/** @param {PatchListPayload} payload */
|
|
647
|
+
export function renderPatchList(payload) {
|
|
648
|
+
const lines = [
|
|
649
|
+
`Graph: ${payload.graph}`,
|
|
650
|
+
`Patches: ${payload.showing}/${payload.total}`,
|
|
651
|
+
];
|
|
652
|
+
|
|
653
|
+
if (payload.writerFilter) {
|
|
654
|
+
lines.push(`Writer: ${payload.writerFilter}`);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
lines.push('');
|
|
658
|
+
|
|
659
|
+
for (const entry of payload.entries) {
|
|
660
|
+
const nodes = entry.nodeIds.length > 0 ? ` [${entry.nodeIds.join(', ')}]` : '';
|
|
661
|
+
lines.push(` ${entry.sha} L${String(entry.lamport).padStart(3)} ${entry.writer.padEnd(20)} ${entry.opCount} ops${nodes}`);
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
return `${lines.join('\n')}\n`;
|
|
665
|
+
}
|
package/index.d.ts
CHANGED
|
@@ -566,6 +566,15 @@ export interface GitPlumbing {
|
|
|
566
566
|
*/
|
|
567
567
|
export class InMemoryGraphAdapter extends GraphPersistencePort {
|
|
568
568
|
constructor();
|
|
569
|
+
|
|
570
|
+
get emptyTree(): string;
|
|
571
|
+
commitNode(options: CreateNodeOptions): Promise<string>;
|
|
572
|
+
showNode(sha: string): Promise<string>;
|
|
573
|
+
getNodeInfo(sha: string): Promise<NodeInfo>;
|
|
574
|
+
logNodesStream(options: ListNodesOptions & { format: string }): Promise<AsyncIterable<Uint8Array | string>>;
|
|
575
|
+
logNodes(options: ListNodesOptions & { format: string }): Promise<string>;
|
|
576
|
+
ping(): Promise<PingResult>;
|
|
577
|
+
countNodes(ref: string): Promise<number>;
|
|
569
578
|
}
|
|
570
579
|
|
|
571
580
|
/**
|
|
@@ -1032,7 +1041,7 @@ export class SyncError extends Error {
|
|
|
1032
1041
|
* Base error class for bitmap index operations.
|
|
1033
1042
|
*/
|
|
1034
1043
|
export class IndexError extends Error {
|
|
1035
|
-
readonly name:
|
|
1044
|
+
readonly name: string;
|
|
1036
1045
|
readonly code: string;
|
|
1037
1046
|
readonly context: Record<string, unknown>;
|
|
1038
1047
|
|
|
@@ -1293,12 +1302,65 @@ export interface TemporalQuery {
|
|
|
1293
1302
|
): Promise<boolean>;
|
|
1294
1303
|
}
|
|
1295
1304
|
|
|
1305
|
+
// ============================================================================
|
|
1306
|
+
// PatchV2 & PatchBuilderV2
|
|
1307
|
+
// ============================================================================
|
|
1308
|
+
|
|
1309
|
+
/**
|
|
1310
|
+
* WARP V5 patch object (schema 2 or 3).
|
|
1311
|
+
*/
|
|
1312
|
+
export interface PatchV2 {
|
|
1313
|
+
/** Schema version (2 for node/edge ops, 3 if edge properties present) */
|
|
1314
|
+
schema: 2 | 3;
|
|
1315
|
+
/** Writer ID */
|
|
1316
|
+
writer: string;
|
|
1317
|
+
/** Lamport timestamp for ordering */
|
|
1318
|
+
lamport: number;
|
|
1319
|
+
/** Writer's observed frontier (version vector) */
|
|
1320
|
+
context: Record<string, number>;
|
|
1321
|
+
/** Ordered array of operations */
|
|
1322
|
+
ops: unknown[];
|
|
1323
|
+
/** Node/edge IDs read by this patch (provenance tracking) */
|
|
1324
|
+
reads?: string[];
|
|
1325
|
+
/** Node/edge IDs written by this patch (provenance tracking) */
|
|
1326
|
+
writes?: string[];
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
/**
|
|
1330
|
+
* Fluent builder for creating WARP v5 patches with OR-Set semantics.
|
|
1331
|
+
*
|
|
1332
|
+
* Returned by WarpGraph.createPatch(). Chain mutation methods then call
|
|
1333
|
+
* commit() to persist the patch atomically.
|
|
1334
|
+
*/
|
|
1335
|
+
export class PatchBuilderV2 {
|
|
1336
|
+
/** Adds a node to the graph. */
|
|
1337
|
+
addNode(nodeId: string): PatchBuilderV2;
|
|
1338
|
+
/** Removes a node from the graph. */
|
|
1339
|
+
removeNode(nodeId: string): PatchBuilderV2;
|
|
1340
|
+
/** Adds an edge between two nodes. */
|
|
1341
|
+
addEdge(from: string, to: string, label: string): PatchBuilderV2;
|
|
1342
|
+
/** Removes an edge between two nodes. */
|
|
1343
|
+
removeEdge(from: string, to: string, label: string): PatchBuilderV2;
|
|
1344
|
+
/** Sets a property on a node. */
|
|
1345
|
+
setProperty(nodeId: string, key: string, value: unknown): PatchBuilderV2;
|
|
1346
|
+
/** Sets a property on an edge. */
|
|
1347
|
+
setEdgeProperty(from: string, to: string, label: string, key: string, value: unknown): PatchBuilderV2;
|
|
1348
|
+
/** Builds the PatchV2 object without committing. */
|
|
1349
|
+
build(): PatchV2;
|
|
1350
|
+
/** Commits the patch to the graph and returns the commit SHA. */
|
|
1351
|
+
commit(): Promise<string>;
|
|
1352
|
+
/** Number of operations in this patch. */
|
|
1353
|
+
readonly opCount: number;
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1296
1356
|
// ============================================================================
|
|
1297
1357
|
// Writer & PatchSession
|
|
1298
1358
|
// ============================================================================
|
|
1299
1359
|
|
|
1300
1360
|
/**
|
|
1301
1361
|
* Fluent patch session for building and committing graph mutations.
|
|
1362
|
+
*
|
|
1363
|
+
* Created by Writer.beginPatch(). Wraps a PatchBuilderV2 with CAS protection.
|
|
1302
1364
|
*/
|
|
1303
1365
|
export class PatchSession {
|
|
1304
1366
|
/** Adds a node to the graph. */
|
|
@@ -1313,8 +1375,8 @@ export class PatchSession {
|
|
|
1313
1375
|
setProperty(nodeId: string, key: string, value: unknown): this;
|
|
1314
1376
|
/** Sets a property on an edge. */
|
|
1315
1377
|
setEdgeProperty(from: string, to: string, label: string, key: string, value: unknown): this;
|
|
1316
|
-
/** Builds the
|
|
1317
|
-
build():
|
|
1378
|
+
/** Builds the PatchV2 object without committing. */
|
|
1379
|
+
build(): PatchV2;
|
|
1318
1380
|
/** Commits the patch with CAS protection. */
|
|
1319
1381
|
commit(): Promise<string>;
|
|
1320
1382
|
/** Number of operations in this patch. */
|
|
@@ -1527,21 +1589,21 @@ export default class WarpGraph {
|
|
|
1527
1589
|
setSeekCache(cache: SeekCachePort | null): void;
|
|
1528
1590
|
|
|
1529
1591
|
/**
|
|
1530
|
-
* Creates a new
|
|
1592
|
+
* Creates a new PatchBuilderV2 for adding operations.
|
|
1531
1593
|
*/
|
|
1532
|
-
createPatch(): Promise<
|
|
1594
|
+
createPatch(): Promise<PatchBuilderV2>;
|
|
1533
1595
|
|
|
1534
1596
|
/**
|
|
1535
1597
|
* Convenience wrapper: creates a patch, runs the callback, and commits.
|
|
1536
1598
|
*
|
|
1537
|
-
* The callback receives a
|
|
1599
|
+
* The callback receives a PatchBuilderV2 and may be synchronous or
|
|
1538
1600
|
* asynchronous. The commit happens only after the callback resolves.
|
|
1539
1601
|
* If the callback throws or rejects, no commit is attempted.
|
|
1540
1602
|
*
|
|
1541
1603
|
* Not reentrant: calling `graph.patch()` inside a callback throws.
|
|
1542
1604
|
* Use `createPatch()` directly for nested or concurrent patches.
|
|
1543
1605
|
*/
|
|
1544
|
-
patch(build: (patch:
|
|
1606
|
+
patch(build: (patch: PatchBuilderV2) => void | Promise<void>): Promise<string>;
|
|
1545
1607
|
|
|
1546
1608
|
/**
|
|
1547
1609
|
* Returns patches from a writer's ref chain.
|
|
@@ -1549,7 +1611,7 @@ export default class WarpGraph {
|
|
|
1549
1611
|
getWriterPatches(
|
|
1550
1612
|
writerId: string,
|
|
1551
1613
|
stopAtSha?: string | null
|
|
1552
|
-
): Promise<Array<{ patch:
|
|
1614
|
+
): Promise<Array<{ patch: PatchV2; sha: string }>>;
|
|
1553
1615
|
|
|
1554
1616
|
/**
|
|
1555
1617
|
* Gets all visible nodes in the materialized state.
|
|
@@ -1621,7 +1683,7 @@ export default class WarpGraph {
|
|
|
1621
1683
|
/**
|
|
1622
1684
|
* Materializes graph state from a checkpoint, applying incremental patches.
|
|
1623
1685
|
*/
|
|
1624
|
-
materializeAt(checkpointSha: string): Promise<
|
|
1686
|
+
materializeAt(checkpointSha: string): Promise<WarpStateV5>;
|
|
1625
1687
|
|
|
1626
1688
|
/**
|
|
1627
1689
|
* Logical graph traversal helpers.
|
|
@@ -1654,8 +1716,12 @@ export default class WarpGraph {
|
|
|
1654
1716
|
|
|
1655
1717
|
/**
|
|
1656
1718
|
* Materializes the current graph state from all patches.
|
|
1719
|
+
*
|
|
1720
|
+
* When `options.receipts` is true, returns `{ state, receipts }`.
|
|
1721
|
+
* Otherwise returns the WarpStateV5 directly.
|
|
1657
1722
|
*/
|
|
1658
|
-
materialize(): Promise<
|
|
1723
|
+
materialize(options: { receipts: true; ceiling?: number | null }): Promise<{ state: WarpStateV5; receipts: TickReceipt[] }>;
|
|
1724
|
+
materialize(options?: { receipts?: false; ceiling?: number | null }): Promise<WarpStateV5>;
|
|
1659
1725
|
|
|
1660
1726
|
/**
|
|
1661
1727
|
* Starts a built-in sync server for this graph.
|
|
@@ -1672,6 +1738,8 @@ export default class WarpGraph {
|
|
|
1672
1738
|
|
|
1673
1739
|
/**
|
|
1674
1740
|
* Syncs with a remote peer (HTTP URL or another WarpGraph instance).
|
|
1741
|
+
*
|
|
1742
|
+
* When `options.materialize` is true, the returned object also contains a `state` property.
|
|
1675
1743
|
*/
|
|
1676
1744
|
syncWith(remote: string | WarpGraph, options?: {
|
|
1677
1745
|
path?: string;
|
|
@@ -1688,7 +1756,9 @@ export default class WarpGraph {
|
|
|
1688
1756
|
error?: Error;
|
|
1689
1757
|
}) => void;
|
|
1690
1758
|
auth?: SyncAuthClientOptions;
|
|
1691
|
-
|
|
1759
|
+
/** Auto-materialize after sync; when true, result includes `state` */
|
|
1760
|
+
materialize?: boolean;
|
|
1761
|
+
}): Promise<{ applied: number; attempts: number; state?: WarpStateV5 }>;
|
|
1692
1762
|
|
|
1693
1763
|
/**
|
|
1694
1764
|
* Creates a fork of this graph at a specific point in a writer's history.
|
|
@@ -2113,17 +2183,7 @@ export function migrateV4toV5(v4State: {
|
|
|
2113
2183
|
*/
|
|
2114
2184
|
export interface PatchEntry {
|
|
2115
2185
|
/** The decoded patch object */
|
|
2116
|
-
patch:
|
|
2117
|
-
schema: 2 | 3;
|
|
2118
|
-
writer: string;
|
|
2119
|
-
lamport: number;
|
|
2120
|
-
context: Record<string, number> | Map<string, number>;
|
|
2121
|
-
ops: unknown[];
|
|
2122
|
-
/** Node/edge IDs read by this patch (V2 provenance) */
|
|
2123
|
-
reads?: string[];
|
|
2124
|
-
/** Node/edge IDs written by this patch (V2 provenance) */
|
|
2125
|
-
writes?: string[];
|
|
2126
|
-
};
|
|
2186
|
+
patch: PatchV2;
|
|
2127
2187
|
/** The Git SHA of the patch commit */
|
|
2128
2188
|
sha: string;
|
|
2129
2189
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@git-stunts/git-warp",
|
|
3
|
-
"version": "11.
|
|
3
|
+
"version": "11.3.3",
|
|
4
4
|
"description": "Deterministic WARP graph over Git: graph-native storage, traversal, and tooling.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
"demo:down": "cd examples && docker compose down -v",
|
|
79
79
|
"setup:hooks": "node scripts/setup-hooks.js",
|
|
80
80
|
"prepare": "patch-package && node scripts/setup-hooks.js",
|
|
81
|
-
"prepack": "npm run lint && npm run test:local",
|
|
81
|
+
"prepack": "npm run lint && npm run test:local && npm run typecheck:consumer",
|
|
82
82
|
"install:git-warp": "bash scripts/install-git-warp.sh",
|
|
83
83
|
"uninstall:git-warp": "bash scripts/uninstall-git-warp.sh",
|
|
84
84
|
"test:node20": "docker compose -f docker-compose.test.yml --profile node20 run --build --rm test-node20",
|
|
@@ -89,6 +89,7 @@
|
|
|
89
89
|
"typecheck": "tsc --noEmit",
|
|
90
90
|
"typecheck:src": "tsc --noEmit -p tsconfig.src.json",
|
|
91
91
|
"typecheck:test": "tsc --noEmit -p tsconfig.test.json",
|
|
92
|
+
"typecheck:consumer": "tsc --noEmit -p test/type-check/tsconfig.json",
|
|
92
93
|
"typecheck:policy": "node scripts/ts-policy-check.js"
|
|
93
94
|
},
|
|
94
95
|
"dependencies": {
|
package/src/domain/WarpGraph.js
CHANGED
|
@@ -95,6 +95,9 @@ export default class WarpGraph {
|
|
|
95
95
|
/** @type {number} */
|
|
96
96
|
this._patchesSinceCheckpoint = 0;
|
|
97
97
|
|
|
98
|
+
/** @type {number} */
|
|
99
|
+
this._maxObservedLamport = 0;
|
|
100
|
+
|
|
98
101
|
/** @type {{every: number}|null} */
|
|
99
102
|
this._checkpointPolicy = checkpointPolicy || null;
|
|
100
103
|
|
|
@@ -285,7 +288,7 @@ export default class WarpGraph {
|
|
|
285
288
|
// Initialize audit service if enabled
|
|
286
289
|
if (graph._audit) {
|
|
287
290
|
graph._auditService = new AuditReceiptService({
|
|
288
|
-
persistence: /** @type {
|
|
291
|
+
persistence: /** @type {import('./types/WarpPersistence.js').CorePersistence} */ (persistence),
|
|
289
292
|
graphName,
|
|
290
293
|
writerId,
|
|
291
294
|
codec: graph._codec,
|