@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.
Files changed (71) 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 +73 -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/path.js +88 -0
  13. package/bin/cli/commands/query.js +194 -0
  14. package/bin/cli/commands/registry.js +28 -0
  15. package/bin/cli/commands/seek.js +592 -0
  16. package/bin/cli/commands/trust.js +154 -0
  17. package/bin/cli/commands/verify-audit.js +113 -0
  18. package/bin/cli/commands/view.js +45 -0
  19. package/bin/cli/infrastructure.js +336 -0
  20. package/bin/cli/schemas.js +177 -0
  21. package/bin/cli/shared.js +244 -0
  22. package/bin/cli/types.js +85 -0
  23. package/bin/presenters/index.js +214 -0
  24. package/bin/presenters/json.js +66 -0
  25. package/bin/presenters/text.js +543 -0
  26. package/bin/warp-graph.js +19 -2824
  27. package/index.d.ts +32 -2
  28. package/index.js +2 -0
  29. package/package.json +9 -7
  30. package/src/domain/WarpGraph.js +106 -3252
  31. package/src/domain/errors/QueryError.js +2 -2
  32. package/src/domain/errors/TrustError.js +29 -0
  33. package/src/domain/errors/index.js +1 -0
  34. package/src/domain/services/AuditMessageCodec.js +137 -0
  35. package/src/domain/services/AuditReceiptService.js +471 -0
  36. package/src/domain/services/AuditVerifierService.js +693 -0
  37. package/src/domain/services/HttpSyncServer.js +36 -22
  38. package/src/domain/services/MessageCodecInternal.js +3 -0
  39. package/src/domain/services/MessageSchemaDetector.js +2 -2
  40. package/src/domain/services/SyncAuthService.js +69 -3
  41. package/src/domain/services/WarpMessageCodec.js +4 -1
  42. package/src/domain/trust/TrustCanonical.js +42 -0
  43. package/src/domain/trust/TrustCrypto.js +111 -0
  44. package/src/domain/trust/TrustEvaluator.js +180 -0
  45. package/src/domain/trust/TrustRecordService.js +274 -0
  46. package/src/domain/trust/TrustStateBuilder.js +209 -0
  47. package/src/domain/trust/canonical.js +68 -0
  48. package/src/domain/trust/reasonCodes.js +64 -0
  49. package/src/domain/trust/schemas.js +160 -0
  50. package/src/domain/trust/verdict.js +42 -0
  51. package/src/domain/types/git-cas.d.ts +20 -0
  52. package/src/domain/utils/RefLayout.js +59 -0
  53. package/src/domain/warp/PatchSession.js +18 -0
  54. package/src/domain/warp/Writer.js +18 -3
  55. package/src/domain/warp/_internal.js +26 -0
  56. package/src/domain/warp/_wire.js +58 -0
  57. package/src/domain/warp/_wiredMethods.d.ts +100 -0
  58. package/src/domain/warp/checkpoint.methods.js +397 -0
  59. package/src/domain/warp/fork.methods.js +323 -0
  60. package/src/domain/warp/materialize.methods.js +188 -0
  61. package/src/domain/warp/materializeAdvanced.methods.js +339 -0
  62. package/src/domain/warp/patch.methods.js +529 -0
  63. package/src/domain/warp/provenance.methods.js +284 -0
  64. package/src/domain/warp/query.methods.js +279 -0
  65. package/src/domain/warp/subscribe.methods.js +272 -0
  66. package/src/domain/warp/sync.methods.js +549 -0
  67. package/src/infrastructure/adapters/GitGraphAdapter.js +67 -1
  68. package/src/infrastructure/adapters/InMemoryGraphAdapter.js +36 -0
  69. package/src/ports/CommitPort.js +10 -0
  70. package/src/ports/RefPort.js +17 -0
  71. 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
+ }