@git-stunts/git-warp 10.7.0 → 11.2.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.
- package/README.md +53 -32
- package/SECURITY.md +64 -0
- package/bin/cli/commands/check.js +168 -0
- package/bin/cli/commands/doctor/checks.js +422 -0
- package/bin/cli/commands/doctor/codes.js +46 -0
- package/bin/cli/commands/doctor/index.js +239 -0
- package/bin/cli/commands/doctor/types.js +89 -0
- package/bin/cli/commands/history.js +73 -0
- package/bin/cli/commands/info.js +139 -0
- package/bin/cli/commands/install-hooks.js +128 -0
- package/bin/cli/commands/materialize.js +99 -0
- package/bin/cli/commands/path.js +88 -0
- package/bin/cli/commands/query.js +194 -0
- package/bin/cli/commands/registry.js +28 -0
- package/bin/cli/commands/seek.js +592 -0
- package/bin/cli/commands/trust.js +154 -0
- package/bin/cli/commands/verify-audit.js +113 -0
- package/bin/cli/commands/view.js +45 -0
- package/bin/cli/infrastructure.js +336 -0
- package/bin/cli/schemas.js +177 -0
- package/bin/cli/shared.js +244 -0
- package/bin/cli/types.js +85 -0
- package/bin/presenters/index.js +214 -0
- package/bin/presenters/json.js +66 -0
- package/bin/presenters/text.js +543 -0
- package/bin/warp-graph.js +19 -2824
- package/index.d.ts +32 -2
- package/index.js +2 -0
- package/package.json +9 -7
- package/src/domain/WarpGraph.js +106 -3252
- package/src/domain/errors/QueryError.js +2 -2
- package/src/domain/errors/TrustError.js +29 -0
- package/src/domain/errors/index.js +1 -0
- package/src/domain/services/AuditMessageCodec.js +137 -0
- package/src/domain/services/AuditReceiptService.js +471 -0
- package/src/domain/services/AuditVerifierService.js +693 -0
- package/src/domain/services/HttpSyncServer.js +36 -22
- package/src/domain/services/MessageCodecInternal.js +3 -0
- package/src/domain/services/MessageSchemaDetector.js +2 -2
- package/src/domain/services/SyncAuthService.js +69 -3
- package/src/domain/services/WarpMessageCodec.js +4 -1
- package/src/domain/trust/TrustCanonical.js +42 -0
- package/src/domain/trust/TrustCrypto.js +111 -0
- package/src/domain/trust/TrustEvaluator.js +180 -0
- package/src/domain/trust/TrustRecordService.js +274 -0
- package/src/domain/trust/TrustStateBuilder.js +209 -0
- package/src/domain/trust/canonical.js +68 -0
- package/src/domain/trust/reasonCodes.js +64 -0
- package/src/domain/trust/schemas.js +160 -0
- package/src/domain/trust/verdict.js +42 -0
- package/src/domain/types/git-cas.d.ts +20 -0
- package/src/domain/utils/RefLayout.js +59 -0
- package/src/domain/warp/PatchSession.js +18 -0
- package/src/domain/warp/Writer.js +18 -3
- package/src/domain/warp/_internal.js +26 -0
- package/src/domain/warp/_wire.js +58 -0
- package/src/domain/warp/_wiredMethods.d.ts +100 -0
- package/src/domain/warp/checkpoint.methods.js +397 -0
- package/src/domain/warp/fork.methods.js +323 -0
- package/src/domain/warp/materialize.methods.js +188 -0
- package/src/domain/warp/materializeAdvanced.methods.js +339 -0
- package/src/domain/warp/patch.methods.js +529 -0
- package/src/domain/warp/provenance.methods.js +284 -0
- package/src/domain/warp/query.methods.js +279 -0
- package/src/domain/warp/subscribe.methods.js +272 -0
- package/src/domain/warp/sync.methods.js +549 -0
- package/src/infrastructure/adapters/GitGraphAdapter.js +67 -1
- package/src/infrastructure/adapters/InMemoryGraphAdapter.js +36 -0
- package/src/ports/CommitPort.js +10 -0
- package/src/ports/RefPort.js +17 -0
- package/src/hooks/post-merge.sh +0 -60
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified output dispatcher for CLI commands.
|
|
3
|
+
*
|
|
4
|
+
* Replaces the 112-line emit() function in warp-graph.js with clean
|
|
5
|
+
* format dispatch: text, json, ndjson — plus view mode handling.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import fs from 'node:fs';
|
|
9
|
+
import process from 'node:process';
|
|
10
|
+
|
|
11
|
+
import { stripAnsi } from '../../src/visualization/utils/ansi.js';
|
|
12
|
+
import { renderInfoView } from '../../src/visualization/renderers/ascii/info.js';
|
|
13
|
+
import { renderCheckView } from '../../src/visualization/renderers/ascii/check.js';
|
|
14
|
+
import { renderHistoryView } from '../../src/visualization/renderers/ascii/history.js';
|
|
15
|
+
import { renderPathView } from '../../src/visualization/renderers/ascii/path.js';
|
|
16
|
+
import { renderMaterializeView } from '../../src/visualization/renderers/ascii/materialize.js';
|
|
17
|
+
import { renderSeekView } from '../../src/visualization/renderers/ascii/seek.js';
|
|
18
|
+
|
|
19
|
+
import { stableStringify, compactStringify, sanitizePayload } from './json.js';
|
|
20
|
+
import {
|
|
21
|
+
renderInfo,
|
|
22
|
+
renderQuery,
|
|
23
|
+
renderPath,
|
|
24
|
+
renderCheck,
|
|
25
|
+
renderDoctor,
|
|
26
|
+
renderHistory,
|
|
27
|
+
renderError,
|
|
28
|
+
renderMaterialize,
|
|
29
|
+
renderInstallHooks,
|
|
30
|
+
renderSeek,
|
|
31
|
+
renderVerifyAudit,
|
|
32
|
+
renderTrust,
|
|
33
|
+
} from './text.js';
|
|
34
|
+
|
|
35
|
+
// ── Color control ────────────────────────────────────────────────────────────
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Determines whether ANSI color codes should be stripped from output.
|
|
39
|
+
*
|
|
40
|
+
* Precedence: FORCE_COLOR=0 (strip) > FORCE_COLOR!='' (keep) > NO_COLOR > !isTTY > CI.
|
|
41
|
+
* @returns {boolean}
|
|
42
|
+
*/
|
|
43
|
+
export function shouldStripColor() {
|
|
44
|
+
if (process.env.FORCE_COLOR === '0') {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
if (process.env.FORCE_COLOR !== undefined && process.env.FORCE_COLOR !== '') {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
if (process.env.NO_COLOR !== undefined) {
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
if (!process.stdout.isTTY) {
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
if (process.env.CI !== undefined) {
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ── Text renderer map ────────────────────────────────────────────────────────
|
|
63
|
+
|
|
64
|
+
/** @type {Map<string, function(*): string>} */
|
|
65
|
+
const TEXT_RENDERERS = new Map(/** @type {[string, function(*): string][]} */ ([
|
|
66
|
+
['info', renderInfo],
|
|
67
|
+
['query', renderQuery],
|
|
68
|
+
['path', renderPath],
|
|
69
|
+
['check', renderCheck],
|
|
70
|
+
['doctor', renderDoctor],
|
|
71
|
+
['history', renderHistory],
|
|
72
|
+
['materialize', renderMaterialize],
|
|
73
|
+
['seek', renderSeek],
|
|
74
|
+
['verify-audit', renderVerifyAudit],
|
|
75
|
+
['trust', renderTrust],
|
|
76
|
+
['install-hooks', renderInstallHooks],
|
|
77
|
+
]));
|
|
78
|
+
|
|
79
|
+
/** @type {Map<string, function(*): string>} */
|
|
80
|
+
const VIEW_RENDERERS = new Map(/** @type {[string, function(*): string][]} */ ([
|
|
81
|
+
['info', renderInfoView],
|
|
82
|
+
['check', renderCheckView],
|
|
83
|
+
['history', renderHistoryView],
|
|
84
|
+
['path', renderPathView],
|
|
85
|
+
['materialize', renderMaterializeView],
|
|
86
|
+
['seek', renderSeekView],
|
|
87
|
+
]));
|
|
88
|
+
|
|
89
|
+
// ── HTML export ──────────────────────────────────────────────────────────────
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Wraps SVG content in a minimal HTML document and writes it to disk.
|
|
93
|
+
* @param {string} filePath
|
|
94
|
+
* @param {string} svgContent
|
|
95
|
+
*/
|
|
96
|
+
function writeHtmlExport(filePath, svgContent) {
|
|
97
|
+
const html = `<!DOCTYPE html>\n<html><head><meta charset="utf-8"><title>git-warp</title></head><body>\n${svgContent}\n</body></html>`;
|
|
98
|
+
fs.writeFileSync(filePath, html);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ── SVG / HTML file export ───────────────────────────────────────────────────
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Handles svg:PATH and html:PATH view modes for commands that carry _renderedSvg.
|
|
105
|
+
* @param {*} payload
|
|
106
|
+
* @param {string} view
|
|
107
|
+
* @returns {boolean} true if handled
|
|
108
|
+
*/
|
|
109
|
+
function handleFileExport(payload, view) {
|
|
110
|
+
if (typeof view === 'string' && view.startsWith('svg:')) {
|
|
111
|
+
const svgPath = view.slice(4);
|
|
112
|
+
if (!payload._renderedSvg) {
|
|
113
|
+
process.stderr.write('No graph data — skipping SVG export.\n');
|
|
114
|
+
} else {
|
|
115
|
+
fs.writeFileSync(svgPath, payload._renderedSvg);
|
|
116
|
+
process.stderr.write(`SVG written to ${svgPath}\n`);
|
|
117
|
+
}
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
if (typeof view === 'string' && view.startsWith('html:')) {
|
|
121
|
+
const htmlPath = view.slice(5);
|
|
122
|
+
if (!payload._renderedSvg) {
|
|
123
|
+
process.stderr.write('No graph data — skipping HTML export.\n');
|
|
124
|
+
} else {
|
|
125
|
+
writeHtmlExport(htmlPath, payload._renderedSvg);
|
|
126
|
+
process.stderr.write(`HTML written to ${htmlPath}\n`);
|
|
127
|
+
}
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// ── Output helpers ───────────────────────────────────────────────────────────
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Writes text to stdout, optionally stripping ANSI codes.
|
|
137
|
+
* @param {string} text
|
|
138
|
+
* @param {boolean} strip
|
|
139
|
+
*/
|
|
140
|
+
function writeText(text, strip) {
|
|
141
|
+
process.stdout.write(strip ? stripAnsi(text) : text);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// ── Main dispatcher ──────────────────────────────────────────────────────────
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Writes a command result to stdout/stderr in the requested format.
|
|
148
|
+
*
|
|
149
|
+
* @param {*} payload - Command result payload
|
|
150
|
+
* @param {{format: string, command: string, view: string|null|boolean}} options
|
|
151
|
+
*/
|
|
152
|
+
export function present(payload, { format, command, view }) {
|
|
153
|
+
// Error payloads always go to stderr as plain text
|
|
154
|
+
if (payload?.error) {
|
|
155
|
+
process.stderr.write(renderError(payload));
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// JSON: sanitize + pretty-print
|
|
160
|
+
if (format === 'json') {
|
|
161
|
+
process.stdout.write(`${stableStringify(sanitizePayload(payload))}\n`);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// NDJSON: sanitize + compact single line
|
|
166
|
+
if (format === 'ndjson') {
|
|
167
|
+
process.stdout.write(`${compactStringify(sanitizePayload(payload))}\n`);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Text with view mode
|
|
172
|
+
if (view) {
|
|
173
|
+
presentView(payload, command, view);
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Plain text
|
|
178
|
+
const renderer = TEXT_RENDERERS.get(command);
|
|
179
|
+
if (renderer) {
|
|
180
|
+
writeText(renderer(payload), shouldStripColor());
|
|
181
|
+
} else {
|
|
182
|
+
// Fallback for unknown commands
|
|
183
|
+
process.stdout.write(`${stableStringify(sanitizePayload(payload))}\n`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Handles --view output dispatch (ASCII view, SVG file, HTML file).
|
|
189
|
+
* @param {*} payload
|
|
190
|
+
* @param {string} command
|
|
191
|
+
* @param {string|boolean} view
|
|
192
|
+
*/
|
|
193
|
+
function presentView(payload, command, view) {
|
|
194
|
+
const strip = shouldStripColor();
|
|
195
|
+
|
|
196
|
+
// File exports: svg:PATH, html:PATH
|
|
197
|
+
if (handleFileExport(payload, /** @type {string} */ (view))) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// query is special: uses pre-rendered _renderedAscii
|
|
202
|
+
if (command === 'query') {
|
|
203
|
+
writeText(`${payload._renderedAscii ?? ''}\n`, strip);
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Dispatch to view renderer
|
|
208
|
+
const viewRenderer = VIEW_RENDERERS.get(command);
|
|
209
|
+
if (viewRenderer) {
|
|
210
|
+
writeText(viewRenderer(payload), strip);
|
|
211
|
+
} else {
|
|
212
|
+
writeText(`${stableStringify(sanitizePayload(payload))}\n`, strip);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON / NDJSON serialization utilities for CLI output.
|
|
3
|
+
*
|
|
4
|
+
* - stableStringify: pretty-printed, sorted-key JSON (--json)
|
|
5
|
+
* - compactStringify: single-line, sorted-key JSON (--ndjson)
|
|
6
|
+
* - sanitizePayload: strips internal _-prefixed keys before serialization
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Recursively sorts object keys for deterministic JSON output.
|
|
11
|
+
* @param {*} input
|
|
12
|
+
* @returns {*}
|
|
13
|
+
*/
|
|
14
|
+
function normalize(input) {
|
|
15
|
+
if (Array.isArray(input)) {
|
|
16
|
+
return input.map(normalize);
|
|
17
|
+
}
|
|
18
|
+
if (input && typeof input === 'object') {
|
|
19
|
+
/** @type {Record<string, *>} */
|
|
20
|
+
const sorted = {};
|
|
21
|
+
for (const key of Object.keys(input).sort()) {
|
|
22
|
+
sorted[key] = normalize(input[key]);
|
|
23
|
+
}
|
|
24
|
+
return sorted;
|
|
25
|
+
}
|
|
26
|
+
return input;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Pretty-printed JSON with sorted keys (2-space indent).
|
|
31
|
+
* @param {*} value
|
|
32
|
+
* @returns {string}
|
|
33
|
+
*/
|
|
34
|
+
export function stableStringify(value) {
|
|
35
|
+
return JSON.stringify(normalize(value), null, 2);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Single-line JSON with sorted keys (no indent).
|
|
40
|
+
* @param {*} value
|
|
41
|
+
* @returns {string}
|
|
42
|
+
*/
|
|
43
|
+
export function compactStringify(value) {
|
|
44
|
+
return JSON.stringify(normalize(value));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Shallow-clones a payload, removing all top-level underscore-prefixed keys.
|
|
49
|
+
* These are internal rendering artifacts (e.g. _renderedSvg, _renderedAscii)
|
|
50
|
+
* that should not leak into JSON/NDJSON output.
|
|
51
|
+
* @param {*} payload
|
|
52
|
+
* @returns {*}
|
|
53
|
+
*/
|
|
54
|
+
export function sanitizePayload(payload) {
|
|
55
|
+
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
|
|
56
|
+
return payload;
|
|
57
|
+
}
|
|
58
|
+
/** @type {Record<string, *>} */
|
|
59
|
+
const clean = {};
|
|
60
|
+
for (const key of Object.keys(payload)) {
|
|
61
|
+
if (!key.startsWith('_')) {
|
|
62
|
+
clean[key] = payload[key];
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return clean;
|
|
66
|
+
}
|