@git-stunts/git-warp 10.8.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 (70) 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 +6 -0
  24. package/bin/presenters/text.js +136 -0
  25. package/bin/warp-graph.js +5 -2346
  26. package/index.d.ts +32 -2
  27. package/index.js +2 -0
  28. package/package.json +8 -7
  29. package/src/domain/WarpGraph.js +106 -3252
  30. package/src/domain/errors/QueryError.js +2 -2
  31. package/src/domain/errors/TrustError.js +29 -0
  32. package/src/domain/errors/index.js +1 -0
  33. package/src/domain/services/AuditMessageCodec.js +137 -0
  34. package/src/domain/services/AuditReceiptService.js +471 -0
  35. package/src/domain/services/AuditVerifierService.js +693 -0
  36. package/src/domain/services/HttpSyncServer.js +36 -22
  37. package/src/domain/services/MessageCodecInternal.js +3 -0
  38. package/src/domain/services/MessageSchemaDetector.js +2 -2
  39. package/src/domain/services/SyncAuthService.js +69 -3
  40. package/src/domain/services/WarpMessageCodec.js +4 -1
  41. package/src/domain/trust/TrustCanonical.js +42 -0
  42. package/src/domain/trust/TrustCrypto.js +111 -0
  43. package/src/domain/trust/TrustEvaluator.js +180 -0
  44. package/src/domain/trust/TrustRecordService.js +274 -0
  45. package/src/domain/trust/TrustStateBuilder.js +209 -0
  46. package/src/domain/trust/canonical.js +68 -0
  47. package/src/domain/trust/reasonCodes.js +64 -0
  48. package/src/domain/trust/schemas.js +160 -0
  49. package/src/domain/trust/verdict.js +42 -0
  50. package/src/domain/types/git-cas.d.ts +20 -0
  51. package/src/domain/utils/RefLayout.js +59 -0
  52. package/src/domain/warp/PatchSession.js +18 -0
  53. package/src/domain/warp/Writer.js +18 -3
  54. package/src/domain/warp/_internal.js +26 -0
  55. package/src/domain/warp/_wire.js +58 -0
  56. package/src/domain/warp/_wiredMethods.d.ts +100 -0
  57. package/src/domain/warp/checkpoint.methods.js +397 -0
  58. package/src/domain/warp/fork.methods.js +323 -0
  59. package/src/domain/warp/materialize.methods.js +188 -0
  60. package/src/domain/warp/materializeAdvanced.methods.js +339 -0
  61. package/src/domain/warp/patch.methods.js +529 -0
  62. package/src/domain/warp/provenance.methods.js +284 -0
  63. package/src/domain/warp/query.methods.js +279 -0
  64. package/src/domain/warp/subscribe.methods.js +272 -0
  65. package/src/domain/warp/sync.methods.js +549 -0
  66. package/src/infrastructure/adapters/GitGraphAdapter.js +67 -1
  67. package/src/infrastructure/adapters/InMemoryGraphAdapter.js +36 -0
  68. package/src/ports/CommitPort.js +10 -0
  69. package/src/ports/RefPort.js +17 -0
  70. package/src/hooks/post-merge.sh +0 -60
@@ -0,0 +1,194 @@
1
+ import { renderGraphView } from '../../../src/visualization/renderers/ascii/graph.js';
2
+ import { renderSvg } from '../../../src/visualization/renderers/svg/index.js';
3
+ import { layoutGraph, queryResultToGraphData } from '../../../src/visualization/layouts/index.js';
4
+ import { EXIT_CODES, usageError, parseCommandArgs } from '../infrastructure.js';
5
+ import { openGraph, applyCursorCeiling, emitCursorWarning } from '../shared.js';
6
+ import { querySchema } from '../schemas.js';
7
+
8
+ /** @typedef {import('../types.js').CliOptions} CliOptions */
9
+
10
+ const QUERY_OPTIONS = {
11
+ match: { type: 'string' },
12
+ 'where-prop': { type: 'string', multiple: true },
13
+ select: { type: 'string' },
14
+ };
15
+
16
+ /**
17
+ * Extracts --outgoing/--incoming traversal steps from args, returning
18
+ * remaining args for standard parseArgs processing.
19
+ *
20
+ * These flags have optional-value semantics: --outgoing [label].
21
+ * The label is consumed only if the next arg is not a flag.
22
+ *
23
+ * @param {string[]} args
24
+ * @returns {{steps: Array<{type: string, label?: string}>, remaining: string[]}}
25
+ */
26
+ function extractTraversalSteps(args) {
27
+ /** @type {Array<{type: string, label?: string}>} */
28
+ const steps = [];
29
+ /** @type {string[]} */
30
+ const remaining = [];
31
+
32
+ for (let i = 0; i < args.length; i++) {
33
+ const arg = args[i];
34
+ if (arg === '--outgoing' || arg === '--incoming') {
35
+ const next = args[i + 1];
36
+ const label = next && !next.startsWith('-') ? next : undefined;
37
+ steps.push({ type: arg.slice(2), label });
38
+ if (label) {
39
+ i += 1;
40
+ }
41
+ } else {
42
+ remaining.push(arg);
43
+ }
44
+ }
45
+
46
+ return { steps, remaining };
47
+ }
48
+
49
+ /** @param {string} value */
50
+ function parseWhereProp(value) {
51
+ const [key, ...rest] = value.split('=');
52
+ if (!key || rest.length === 0) {
53
+ throw usageError('Expected --where-prop key=value');
54
+ }
55
+ return { type: 'where-prop', key, value: rest.join('=') };
56
+ }
57
+
58
+ /** @param {string} value */
59
+ function parseSelectFields(value) {
60
+ if (value === '') {
61
+ return [];
62
+ }
63
+ return value.split(',').map((field) => field.trim()).filter(Boolean);
64
+ }
65
+
66
+ /** @param {string[]} args */
67
+ function parseQueryArgs(args) {
68
+ // Extract traversal steps first (optional-value semantics)
69
+ const { steps, remaining } = extractTraversalSteps(args);
70
+
71
+ // Parse remaining flags with parseArgs + Zod
72
+ const { values } = parseCommandArgs(remaining, QUERY_OPTIONS, querySchema);
73
+
74
+ // Convert --where-prop values to steps
75
+ const allSteps = [
76
+ ...steps,
77
+ ...values.whereProp.map((/** @type {string} */ wp) => parseWhereProp(wp)),
78
+ ];
79
+
80
+ return {
81
+ match: values.match,
82
+ select: values.select !== undefined ? parseSelectFields(values.select) : null,
83
+ steps: allSteps,
84
+ };
85
+ }
86
+
87
+ /**
88
+ * @param {*} builder
89
+ * @param {Array<{type: string, label?: string, key?: string, value?: string}>} steps
90
+ */
91
+ function applyQuerySteps(builder, steps) {
92
+ let current = builder;
93
+ for (const step of steps) {
94
+ current = applyQueryStep(current, step);
95
+ }
96
+ return current;
97
+ }
98
+
99
+ /**
100
+ * @param {*} builder
101
+ * @param {{type: string, label?: string, key?: string, value?: string}} step
102
+ */
103
+ function applyQueryStep(builder, step) {
104
+ if (step.type === 'outgoing') {
105
+ return builder.outgoing(step.label);
106
+ }
107
+ if (step.type === 'incoming') {
108
+ return builder.incoming(step.label);
109
+ }
110
+ if (step.type === 'where-prop') {
111
+ return builder.where((/** @type {*} */ node) => matchesPropFilter(node, /** @type {string} */ (step.key), /** @type {string} */ (step.value))); // TODO(ts-cleanup): type CLI payload
112
+ }
113
+ return builder;
114
+ }
115
+
116
+ /**
117
+ * @param {*} node
118
+ * @param {string} key
119
+ * @param {string} value
120
+ */
121
+ function matchesPropFilter(node, key, value) {
122
+ const props = node.props || {};
123
+ if (!Object.prototype.hasOwnProperty.call(props, key)) {
124
+ return false;
125
+ }
126
+ return String(props[key]) === value;
127
+ }
128
+
129
+ /**
130
+ * @param {string} graphName
131
+ * @param {*} result
132
+ * @returns {{graph: string, stateHash: *, nodes: *, _renderedSvg?: string, _renderedAscii?: string}}
133
+ */
134
+ function buildQueryPayload(graphName, result) {
135
+ return {
136
+ graph: graphName,
137
+ stateHash: result.stateHash,
138
+ nodes: result.nodes,
139
+ };
140
+ }
141
+
142
+ /** @param {*} error */
143
+ function mapQueryError(error) {
144
+ if (error && error.code && String(error.code).startsWith('E_QUERY')) {
145
+ throw usageError(error.message);
146
+ }
147
+ throw error;
148
+ }
149
+
150
+ /**
151
+ * Handles the `query` command: runs a logical graph query.
152
+ * @param {{options: CliOptions, args: string[]}} params
153
+ * @returns {Promise<{payload: *, exitCode: number}>}
154
+ */
155
+ export default async function handleQuery({ options, args }) {
156
+ const querySpec = parseQueryArgs(args);
157
+ const { graph, graphName, persistence } = await openGraph(options);
158
+ const cursorInfo = await applyCursorCeiling(graph, persistence, graphName);
159
+ emitCursorWarning(cursorInfo, null);
160
+ let builder = graph.query();
161
+
162
+ if (querySpec.match !== null) {
163
+ builder = builder.match(querySpec.match);
164
+ }
165
+
166
+ builder = applyQuerySteps(builder, querySpec.steps);
167
+
168
+ if (querySpec.select !== null) {
169
+ builder = builder.select(querySpec.select);
170
+ }
171
+
172
+ try {
173
+ const result = await builder.run();
174
+ const payload = buildQueryPayload(graphName, result);
175
+
176
+ if (options.view) {
177
+ const edges = await graph.getEdges();
178
+ const graphData = queryResultToGraphData(payload, edges);
179
+ const positioned = await layoutGraph(graphData, { type: 'query' });
180
+ if (typeof options.view === 'string' && (options.view.startsWith('svg:') || options.view.startsWith('html:'))) {
181
+ payload._renderedSvg = renderSvg(positioned, { title: `${graphName} query` });
182
+ } else {
183
+ payload._renderedAscii = renderGraphView(positioned, { title: `QUERY: ${graphName}` });
184
+ }
185
+ }
186
+
187
+ return {
188
+ payload,
189
+ exitCode: EXIT_CODES.OK,
190
+ };
191
+ } catch (error) {
192
+ throw mapQueryError(error);
193
+ }
194
+ }
@@ -0,0 +1,28 @@
1
+ import handleInfo from './info.js';
2
+ import handleQuery from './query.js';
3
+ import handlePath from './path.js';
4
+ import handleHistory from './history.js';
5
+ import handleCheck from './check.js';
6
+ import handleDoctor from './doctor/index.js';
7
+ import handleMaterialize from './materialize.js';
8
+ import handleSeek from './seek.js';
9
+ import handleVerifyAudit from './verify-audit.js';
10
+ import handleView from './view.js';
11
+ import handleInstallHooks from './install-hooks.js';
12
+ import handleTrust from './trust.js';
13
+
14
+ /** @type {Map<string, Function>} */
15
+ export const COMMANDS = new Map(/** @type {[string, Function][]} */ ([
16
+ ['info', handleInfo],
17
+ ['query', handleQuery],
18
+ ['path', handlePath],
19
+ ['history', handleHistory],
20
+ ['check', handleCheck],
21
+ ['doctor', handleDoctor],
22
+ ['materialize', handleMaterialize],
23
+ ['seek', handleSeek],
24
+ ['verify-audit', handleVerifyAudit],
25
+ ['trust', handleTrust],
26
+ ['view', handleView],
27
+ ['install-hooks', handleInstallHooks],
28
+ ]));