@git-stunts/git-warp 11.2.1 → 11.5.0
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/README.md +24 -1
- 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 +118 -22
- package/index.js +2 -0
- package/package.json +5 -3
- 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 +22 -3
- 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 +26 -11
- package/src/domain/services/KeyCodec.js +7 -0
- 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 +96 -30
- 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 +40 -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 +89 -6
- 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 +18 -13
- 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/index.js
CHANGED
|
@@ -30,6 +30,8 @@ import {
|
|
|
30
30
|
renderSeek,
|
|
31
31
|
renderVerifyAudit,
|
|
32
32
|
renderTrust,
|
|
33
|
+
renderPatchShow,
|
|
34
|
+
renderPatchList,
|
|
33
35
|
} from './text.js';
|
|
34
36
|
|
|
35
37
|
// ── Color control ────────────────────────────────────────────────────────────
|
|
@@ -61,8 +63,29 @@ export function shouldStripColor() {
|
|
|
61
63
|
|
|
62
64
|
// ── Text renderer map ────────────────────────────────────────────────────────
|
|
63
65
|
|
|
64
|
-
/** @
|
|
65
|
-
|
|
66
|
+
/** @param {import('./text.js').PatchShowPayload & Partial<import('./text.js').PatchListPayload>} payload */
|
|
67
|
+
function renderPatch(payload) {
|
|
68
|
+
if (payload.ops) {
|
|
69
|
+
return renderPatchShow(payload);
|
|
70
|
+
}
|
|
71
|
+
return renderPatchList(/** @type {import('./text.js').PatchListPayload} */ (payload));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** @param {{ graph: string, tree?: string, orphanCount?: number, orphans?: string[] }} payload */
|
|
75
|
+
function renderTree(payload) {
|
|
76
|
+
const lines = [`Graph: ${payload.graph}`];
|
|
77
|
+
if (payload.tree) {
|
|
78
|
+
lines.push(payload.tree);
|
|
79
|
+
}
|
|
80
|
+
if (payload.orphanCount && payload.orphanCount > 0 && payload.orphans) {
|
|
81
|
+
lines.push('');
|
|
82
|
+
lines.push(`Orphans (${payload.orphanCount}): ${payload.orphans.join(', ')}`);
|
|
83
|
+
}
|
|
84
|
+
return `${lines.join('\n')}\n`;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** @type {Map<string, function(unknown): string>} */
|
|
88
|
+
const TEXT_RENDERERS = new Map(/** @type {[string, function(unknown): string][]} */ ([
|
|
66
89
|
['info', renderInfo],
|
|
67
90
|
['query', renderQuery],
|
|
68
91
|
['path', renderPath],
|
|
@@ -73,11 +96,13 @@ const TEXT_RENDERERS = new Map(/** @type {[string, function(*): string][]} */ ([
|
|
|
73
96
|
['seek', renderSeek],
|
|
74
97
|
['verify-audit', renderVerifyAudit],
|
|
75
98
|
['trust', renderTrust],
|
|
99
|
+
['patch', renderPatch],
|
|
100
|
+
['tree', renderTree],
|
|
76
101
|
['install-hooks', renderInstallHooks],
|
|
77
102
|
]));
|
|
78
103
|
|
|
79
|
-
/** @type {Map<string, function(
|
|
80
|
-
const VIEW_RENDERERS = new Map(/** @type {[string, function(
|
|
104
|
+
/** @type {Map<string, function(unknown): string>} */
|
|
105
|
+
const VIEW_RENDERERS = new Map(/** @type {[string, function(unknown): string][]} */ ([
|
|
81
106
|
['info', renderInfoView],
|
|
82
107
|
['check', renderCheckView],
|
|
83
108
|
['history', renderHistoryView],
|
|
@@ -102,7 +127,7 @@ function writeHtmlExport(filePath, svgContent) {
|
|
|
102
127
|
|
|
103
128
|
/**
|
|
104
129
|
* Handles svg:PATH and html:PATH view modes for commands that carry _renderedSvg.
|
|
105
|
-
* @param {
|
|
130
|
+
* @param {{ _renderedSvg?: string }} payload
|
|
106
131
|
* @param {string} view
|
|
107
132
|
* @returns {boolean} true if handled
|
|
108
133
|
*/
|
|
@@ -146,13 +171,13 @@ function writeText(text, strip) {
|
|
|
146
171
|
/**
|
|
147
172
|
* Writes a command result to stdout/stderr in the requested format.
|
|
148
173
|
*
|
|
149
|
-
* @param {
|
|
174
|
+
* @param {Record<string, unknown>} payload - Command result payload
|
|
150
175
|
* @param {{format: string, command: string, view: string|null|boolean}} options
|
|
151
176
|
*/
|
|
152
177
|
export function present(payload, { format, command, view }) {
|
|
153
178
|
// Error payloads always go to stderr as plain text
|
|
154
179
|
if (payload?.error) {
|
|
155
|
-
process.stderr.write(renderError(payload));
|
|
180
|
+
process.stderr.write(renderError(/** @type {import('./text.js').ErrorPayload} */ (payload)));
|
|
156
181
|
return;
|
|
157
182
|
}
|
|
158
183
|
|
|
@@ -186,7 +211,7 @@ export function present(payload, { format, command, view }) {
|
|
|
186
211
|
|
|
187
212
|
/**
|
|
188
213
|
* Handles --view output dispatch (ASCII view, SVG file, HTML file).
|
|
189
|
-
* @param {
|
|
214
|
+
* @param {Record<string, unknown>} payload
|
|
190
215
|
* @param {string} command
|
|
191
216
|
* @param {string|boolean} view
|
|
192
217
|
*/
|
|
@@ -200,7 +225,8 @@ function presentView(payload, command, view) {
|
|
|
200
225
|
|
|
201
226
|
// query is special: uses pre-rendered _renderedAscii
|
|
202
227
|
if (command === 'query') {
|
|
203
|
-
|
|
228
|
+
const ascii = typeof payload._renderedAscii === 'string' ? payload._renderedAscii : '';
|
|
229
|
+
writeText(`${ascii}\n`, strip);
|
|
204
230
|
return;
|
|
205
231
|
}
|
|
206
232
|
|
package/bin/presenters/json.js
CHANGED
|
@@ -8,18 +8,19 @@
|
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Recursively sorts object keys for deterministic JSON output.
|
|
11
|
-
* @param {
|
|
12
|
-
* @returns {
|
|
11
|
+
* @param {unknown} input
|
|
12
|
+
* @returns {unknown}
|
|
13
13
|
*/
|
|
14
14
|
function normalize(input) {
|
|
15
15
|
if (Array.isArray(input)) {
|
|
16
16
|
return input.map(normalize);
|
|
17
17
|
}
|
|
18
18
|
if (input && typeof input === 'object') {
|
|
19
|
-
/** @type {Record<string,
|
|
19
|
+
const rec = /** @type {Record<string, unknown>} */ (input);
|
|
20
|
+
/** @type {Record<string, unknown>} */
|
|
20
21
|
const sorted = {};
|
|
21
|
-
for (const key of Object.keys(
|
|
22
|
-
sorted[key] = normalize(
|
|
22
|
+
for (const key of Object.keys(rec).sort()) {
|
|
23
|
+
sorted[key] = normalize(rec[key]);
|
|
23
24
|
}
|
|
24
25
|
return sorted;
|
|
25
26
|
}
|
|
@@ -28,7 +29,7 @@ function normalize(input) {
|
|
|
28
29
|
|
|
29
30
|
/**
|
|
30
31
|
* Pretty-printed JSON with sorted keys (2-space indent).
|
|
31
|
-
* @param {
|
|
32
|
+
* @param {unknown} value
|
|
32
33
|
* @returns {string}
|
|
33
34
|
*/
|
|
34
35
|
export function stableStringify(value) {
|
|
@@ -37,7 +38,7 @@ export function stableStringify(value) {
|
|
|
37
38
|
|
|
38
39
|
/**
|
|
39
40
|
* Single-line JSON with sorted keys (no indent).
|
|
40
|
-
* @param {
|
|
41
|
+
* @param {unknown} value
|
|
41
42
|
* @returns {string}
|
|
42
43
|
*/
|
|
43
44
|
export function compactStringify(value) {
|
|
@@ -48,18 +49,19 @@ export function compactStringify(value) {
|
|
|
48
49
|
* Shallow-clones a payload, removing all top-level underscore-prefixed keys.
|
|
49
50
|
* These are internal rendering artifacts (e.g. _renderedSvg, _renderedAscii)
|
|
50
51
|
* that should not leak into JSON/NDJSON output.
|
|
51
|
-
* @param {
|
|
52
|
-
* @returns {
|
|
52
|
+
* @param {Record<string, unknown> | unknown} payload
|
|
53
|
+
* @returns {Record<string, unknown> | unknown}
|
|
53
54
|
*/
|
|
54
55
|
export function sanitizePayload(payload) {
|
|
55
56
|
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
|
|
56
57
|
return payload;
|
|
57
58
|
}
|
|
58
|
-
/** @type {Record<string,
|
|
59
|
+
const rec = /** @type {Record<string, unknown>} */ (payload);
|
|
60
|
+
/** @type {Record<string, unknown>} */
|
|
59
61
|
const clean = {};
|
|
60
|
-
for (const key of Object.keys(
|
|
62
|
+
for (const key of Object.keys(rec)) {
|
|
61
63
|
if (!key.startsWith('_')) {
|
|
62
|
-
clean[key] =
|
|
64
|
+
clean[key] = rec[key];
|
|
63
65
|
}
|
|
64
66
|
}
|
|
65
67
|
return clean;
|
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
|
+
}
|