@git-stunts/git-warp 10.8.0 → 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/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 +80 -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/patch.js +142 -0
- package/bin/cli/commands/path.js +88 -0
- package/bin/cli/commands/query.js +235 -0
- package/bin/cli/commands/registry.js +32 -0
- package/bin/cli/commands/seek.js +598 -0
- package/bin/cli/commands/tree.js +230 -0
- package/bin/cli/commands/trust.js +154 -0
- package/bin/cli/commands/verify-audit.js +114 -0
- package/bin/cli/commands/view.js +46 -0
- package/bin/cli/infrastructure.js +350 -0
- package/bin/cli/schemas.js +177 -0
- package/bin/cli/shared.js +244 -0
- package/bin/cli/types.js +96 -0
- package/bin/presenters/index.js +41 -9
- package/bin/presenters/json.js +14 -12
- package/bin/presenters/text.js +286 -28
- package/bin/warp-graph.js +5 -2346
- package/index.d.ts +111 -21
- package/index.js +2 -0
- package/package.json +10 -8
- package/src/domain/WarpGraph.js +109 -3252
- 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 +3 -3
- 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 +29 -0
- package/src/domain/errors/WarpError.js +2 -2
- package/src/domain/errors/WormholeError.js +1 -1
- 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 +707 -0
- 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 +120 -55
- 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 +4 -1
- package/src/domain/services/MessageSchemaDetector.js +2 -2
- 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 +71 -4
- 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/WarpMessageCodec.js +4 -1
- package/src/domain/services/WormholeService.js +16 -7
- package/src/domain/trust/TrustCanonical.js +42 -0
- package/src/domain/trust/TrustCrypto.js +111 -0
- package/src/domain/trust/TrustEvaluator.js +195 -0
- package/src/domain/trust/TrustRecordService.js +281 -0
- package/src/domain/trust/TrustStateBuilder.js +222 -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/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/types/git-cas.d.ts +20 -0
- package/src/domain/utils/MinHeap.js +6 -5
- package/src/domain/utils/RefLayout.js +59 -0
- package/src/domain/utils/canonicalStringify.js +5 -4
- package/src/domain/utils/roaring.js +31 -5
- package/src/domain/warp/PatchSession.js +26 -17
- 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 +254 -0
- package/src/domain/warp/checkpoint.methods.js +401 -0
- package/src/domain/warp/fork.methods.js +323 -0
- package/src/domain/warp/materialize.methods.js +238 -0
- package/src/domain/warp/materializeAdvanced.methods.js +350 -0
- package/src/domain/warp/patch.methods.js +554 -0
- package/src/domain/warp/provenance.methods.js +286 -0
- package/src/domain/warp/query.methods.js +280 -0
- package/src/domain/warp/subscribe.methods.js +272 -0
- package/src/domain/warp/sync.methods.js +554 -0
- 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 +79 -11
- package/src/infrastructure/adapters/InMemoryGraphAdapter.js +36 -0
- package/src/infrastructure/adapters/NodeHttpAdapter.js +2 -2
- package/src/infrastructure/adapters/WebCryptoAdapter.js +2 -2
- package/src/ports/CommitPort.js +10 -0
- package/src/ports/RefPort.js +17 -0
- 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/src/hooks/post-merge.sh +0 -60
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { renderSvg } from '../../../src/visualization/renderers/svg/index.js';
|
|
2
|
+
import { layoutGraph, pathResultToGraphData } from '../../../src/visualization/layouts/index.js';
|
|
3
|
+
import { EXIT_CODES, usageError, notFoundError, parseCommandArgs } from '../infrastructure.js';
|
|
4
|
+
import { openGraph, applyCursorCeiling, emitCursorWarning } from '../shared.js';
|
|
5
|
+
import { pathSchema } from '../schemas.js';
|
|
6
|
+
|
|
7
|
+
/** @typedef {import('../types.js').CliOptions} CliOptions */
|
|
8
|
+
|
|
9
|
+
const PATH_OPTIONS = {
|
|
10
|
+
from: { type: 'string' },
|
|
11
|
+
to: { type: 'string' },
|
|
12
|
+
dir: { type: 'string' },
|
|
13
|
+
label: { type: 'string', multiple: true },
|
|
14
|
+
'max-depth': { type: 'string' },
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/** @param {string[]} args */
|
|
18
|
+
function parsePathArgs(args) {
|
|
19
|
+
const { values, positionals } = parseCommandArgs(args, PATH_OPTIONS, pathSchema, { allowPositionals: true });
|
|
20
|
+
|
|
21
|
+
// Positionals can supply from/to when flags are omitted
|
|
22
|
+
const from = values.from || positionals[0] || null;
|
|
23
|
+
const to = values.to || positionals[1] || null;
|
|
24
|
+
|
|
25
|
+
if (!from || !to) {
|
|
26
|
+
throw usageError('Path requires --from and --to (or two positional ids)');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Expand comma-separated labels
|
|
30
|
+
const labels = values.labels.flatMap((/** @type {string} */ l) => l.split(',').map((/** @type {string} */ s) => s.trim()).filter(Boolean));
|
|
31
|
+
|
|
32
|
+
/** @type {string|string[]|undefined} */
|
|
33
|
+
let labelFilter;
|
|
34
|
+
if (labels.length === 1) {
|
|
35
|
+
labelFilter = labels[0];
|
|
36
|
+
} else if (labels.length > 1) {
|
|
37
|
+
labelFilter = labels;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return { from, to, dir: values.dir, labelFilter, maxDepth: values.maxDepth };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Handles the `path` command: finds a shortest path between two nodes.
|
|
45
|
+
* @param {{options: CliOptions, args: string[]}} params
|
|
46
|
+
* @returns {Promise<{payload: unknown, exitCode: number}>}
|
|
47
|
+
*/
|
|
48
|
+
export default async function handlePath({ options, args }) {
|
|
49
|
+
const pathOptions = parsePathArgs(args);
|
|
50
|
+
const { graph, graphName, persistence } = await openGraph(options);
|
|
51
|
+
const cursorInfo = await applyCursorCeiling(graph, persistence, graphName);
|
|
52
|
+
emitCursorWarning(cursorInfo, null);
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
const result = await graph.traverse.shortestPath(
|
|
56
|
+
pathOptions.from,
|
|
57
|
+
pathOptions.to,
|
|
58
|
+
{
|
|
59
|
+
dir: pathOptions.dir,
|
|
60
|
+
labelFilter: pathOptions.labelFilter,
|
|
61
|
+
maxDepth: pathOptions.maxDepth,
|
|
62
|
+
}
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const payload = {
|
|
66
|
+
graph: graphName,
|
|
67
|
+
from: pathOptions.from,
|
|
68
|
+
to: pathOptions.to,
|
|
69
|
+
...result,
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
if (options.view && result.found && typeof options.view === 'string' && (options.view.startsWith('svg:') || options.view.startsWith('html:'))) {
|
|
73
|
+
const graphData = pathResultToGraphData(payload);
|
|
74
|
+
const positioned = await layoutGraph(graphData, { type: 'path' });
|
|
75
|
+
payload._renderedSvg = renderSvg(positioned, { title: `${graphName} path` });
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
payload,
|
|
80
|
+
exitCode: result.found ? EXIT_CODES.OK : EXIT_CODES.NO_MATCH,
|
|
81
|
+
};
|
|
82
|
+
} catch (error) {
|
|
83
|
+
if (error instanceof Error && /** @type {{code?: string}} */ (error).code === 'NODE_NOT_FOUND') {
|
|
84
|
+
throw notFoundError(error.message);
|
|
85
|
+
}
|
|
86
|
+
throw error;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,235 @@
|
|
|
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
|
+
/** @typedef {import('../types.js').QueryBuilderLike} QueryBuilderLike */
|
|
10
|
+
|
|
11
|
+
const QUERY_OPTIONS = {
|
|
12
|
+
match: { type: 'string' },
|
|
13
|
+
'where-prop': { type: 'string', multiple: true },
|
|
14
|
+
select: { type: 'string' },
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Extracts --outgoing/--incoming traversal steps from args, returning
|
|
19
|
+
* remaining args for standard parseArgs processing.
|
|
20
|
+
*
|
|
21
|
+
* These flags have optional-value semantics: --outgoing [label].
|
|
22
|
+
* The label is consumed only if the next arg is not a flag.
|
|
23
|
+
*
|
|
24
|
+
* @param {string[]} args
|
|
25
|
+
* @returns {{steps: Array<{type: string, label?: string}>, remaining: string[]}}
|
|
26
|
+
*/
|
|
27
|
+
function extractTraversalSteps(args) {
|
|
28
|
+
/** @type {Array<{type: string, label?: string}>} */
|
|
29
|
+
const steps = [];
|
|
30
|
+
/** @type {string[]} */
|
|
31
|
+
const remaining = [];
|
|
32
|
+
|
|
33
|
+
for (let i = 0; i < args.length; i++) {
|
|
34
|
+
const arg = args[i];
|
|
35
|
+
if (arg === '--outgoing' || arg === '--incoming') {
|
|
36
|
+
const next = args[i + 1];
|
|
37
|
+
const label = next && !next.startsWith('-') ? next : undefined;
|
|
38
|
+
steps.push({ type: arg.slice(2), label });
|
|
39
|
+
if (label) {
|
|
40
|
+
i += 1;
|
|
41
|
+
}
|
|
42
|
+
} else {
|
|
43
|
+
remaining.push(arg);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return { steps, remaining };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** @param {string} value */
|
|
51
|
+
function parseWhereProp(value) {
|
|
52
|
+
const [key, ...rest] = value.split('=');
|
|
53
|
+
if (!key || rest.length === 0) {
|
|
54
|
+
throw usageError('Expected --where-prop key=value');
|
|
55
|
+
}
|
|
56
|
+
return { type: 'where-prop', key, value: rest.join('=') };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** @param {string} value */
|
|
60
|
+
function parseSelectFields(value) {
|
|
61
|
+
if (value === '') {
|
|
62
|
+
return [];
|
|
63
|
+
}
|
|
64
|
+
return value.split(',').map((field) => field.trim()).filter(Boolean);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** @param {string[]} args */
|
|
68
|
+
function parseQueryArgs(args) {
|
|
69
|
+
// Extract traversal steps first (optional-value semantics)
|
|
70
|
+
const { steps, remaining } = extractTraversalSteps(args);
|
|
71
|
+
|
|
72
|
+
// Parse remaining flags with parseArgs + Zod
|
|
73
|
+
const { values } = parseCommandArgs(remaining, QUERY_OPTIONS, querySchema);
|
|
74
|
+
|
|
75
|
+
// Convert --where-prop values to steps
|
|
76
|
+
const allSteps = [
|
|
77
|
+
...steps,
|
|
78
|
+
...values.whereProp.map((/** @type {string} */ wp) => parseWhereProp(wp)),
|
|
79
|
+
];
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
match: values.match,
|
|
83
|
+
select: values.select !== undefined ? parseSelectFields(values.select) : null,
|
|
84
|
+
steps: allSteps,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* @param {QueryBuilderLike} builder
|
|
90
|
+
* @param {Array<{type: string, label?: string, key?: string, value?: string}>} steps
|
|
91
|
+
*/
|
|
92
|
+
function applyQuerySteps(builder, steps) {
|
|
93
|
+
let current = builder;
|
|
94
|
+
for (const step of steps) {
|
|
95
|
+
current = applyQueryStep(current, step);
|
|
96
|
+
}
|
|
97
|
+
return current;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* @param {QueryBuilderLike} builder
|
|
102
|
+
* @param {{type: string, label?: string, key?: string, value?: string}} step
|
|
103
|
+
*/
|
|
104
|
+
function applyQueryStep(builder, step) {
|
|
105
|
+
if (step.type === 'outgoing') {
|
|
106
|
+
return builder.outgoing(step.label);
|
|
107
|
+
}
|
|
108
|
+
if (step.type === 'incoming') {
|
|
109
|
+
return builder.incoming(step.label);
|
|
110
|
+
}
|
|
111
|
+
if (step.type === 'where-prop') {
|
|
112
|
+
return builder.where((/** @type {{props?: Record<string, unknown>}} */ node) => matchesPropFilter(node, /** @type {string} */ (step.key), /** @type {string} */ (step.value)));
|
|
113
|
+
}
|
|
114
|
+
return builder;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* @param {{props?: Record<string, unknown>}} node
|
|
119
|
+
* @param {string} key
|
|
120
|
+
* @param {string} value
|
|
121
|
+
*/
|
|
122
|
+
function matchesPropFilter(node, key, value) {
|
|
123
|
+
const props = node.props || {};
|
|
124
|
+
if (!Object.prototype.hasOwnProperty.call(props, key)) {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
return String(props[key]) === value;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Builds a map of nodeId -> {outgoing: [], incoming: []} from edges.
|
|
132
|
+
* @param {Array<{from: string, to: string, label?: string}>} edges
|
|
133
|
+
* @returns {Map<string, {outgoing: Array<{label: string, to: string}>, incoming: Array<{label: string, from: string}>}>}
|
|
134
|
+
*/
|
|
135
|
+
function buildEdgeMap(edges) {
|
|
136
|
+
/** @type {Map<string, {outgoing: Array<{label: string, to: string}>, incoming: Array<{label: string, from: string}>}>} */
|
|
137
|
+
const edgeMap = new Map();
|
|
138
|
+
for (const edge of edges) {
|
|
139
|
+
if (!edgeMap.has(edge.from)) {
|
|
140
|
+
edgeMap.set(edge.from, { outgoing: [], incoming: [] });
|
|
141
|
+
}
|
|
142
|
+
if (!edgeMap.has(edge.to)) {
|
|
143
|
+
edgeMap.set(edge.to, { outgoing: [], incoming: [] });
|
|
144
|
+
}
|
|
145
|
+
const fromEntry = edgeMap.get(edge.from);
|
|
146
|
+
const toEntry = edgeMap.get(edge.to);
|
|
147
|
+
if (fromEntry) {
|
|
148
|
+
fromEntry.outgoing.push({ label: edge.label || '', to: edge.to });
|
|
149
|
+
}
|
|
150
|
+
if (toEntry) {
|
|
151
|
+
toEntry.incoming.push({ label: edge.label || '', from: edge.from });
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return edgeMap;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* @param {string} graphName
|
|
159
|
+
* @param {{nodes: Array<{id: string, props?: Record<string, unknown>}>, stateHash?: string}} result
|
|
160
|
+
* @param {Array<{from: string, to: string, label?: string}>} edges
|
|
161
|
+
* @returns {{graph: string, stateHash: string|undefined, nodes: Array<{id: string, props?: Record<string, unknown>} & Record<string, unknown>>, [k: string]: unknown}}
|
|
162
|
+
*/
|
|
163
|
+
function buildQueryPayload(graphName, result, edges) {
|
|
164
|
+
const edgeMap = buildEdgeMap(edges);
|
|
165
|
+
|
|
166
|
+
const nodes = result.nodes.map((/** @type {{id: string, props?: Record<string, unknown>}} */ node) => {
|
|
167
|
+
/** @type {{id: string, props?: Record<string, unknown>} & Record<string, unknown>} */
|
|
168
|
+
const entry = { ...node };
|
|
169
|
+
const nodeEdges = edgeMap.get(node.id);
|
|
170
|
+
if (nodeEdges) {
|
|
171
|
+
entry.edges = nodeEdges;
|
|
172
|
+
}
|
|
173
|
+
return entry;
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
return {
|
|
177
|
+
graph: graphName,
|
|
178
|
+
stateHash: result.stateHash,
|
|
179
|
+
nodes,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** @param {unknown} error */
|
|
184
|
+
function mapQueryError(error) {
|
|
185
|
+
if (error instanceof Error && /** @type {{code?: string}} */ (error).code?.startsWith('E_QUERY')) {
|
|
186
|
+
throw usageError(error.message);
|
|
187
|
+
}
|
|
188
|
+
throw error;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Handles the `query` command: runs a logical graph query.
|
|
193
|
+
* @param {{options: CliOptions, args: string[]}} params
|
|
194
|
+
* @returns {Promise<{payload: unknown, exitCode: number}>}
|
|
195
|
+
*/
|
|
196
|
+
export default async function handleQuery({ options, args }) {
|
|
197
|
+
const querySpec = parseQueryArgs(args);
|
|
198
|
+
const { graph, graphName, persistence } = await openGraph(options);
|
|
199
|
+
const cursorInfo = await applyCursorCeiling(graph, persistence, graphName);
|
|
200
|
+
emitCursorWarning(cursorInfo, null);
|
|
201
|
+
let builder = graph.query();
|
|
202
|
+
|
|
203
|
+
if (querySpec.match !== null) {
|
|
204
|
+
builder = builder.match(querySpec.match);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
builder = applyQuerySteps(builder, querySpec.steps);
|
|
208
|
+
|
|
209
|
+
if (querySpec.select !== null) {
|
|
210
|
+
builder = builder.select(querySpec.select);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
try {
|
|
214
|
+
const result = await builder.run();
|
|
215
|
+
const edges = await graph.getEdges();
|
|
216
|
+
const payload = buildQueryPayload(graphName, result, edges);
|
|
217
|
+
|
|
218
|
+
if (options.view) {
|
|
219
|
+
const graphData = queryResultToGraphData(payload, edges);
|
|
220
|
+
const positioned = await layoutGraph(graphData, { type: 'query' });
|
|
221
|
+
if (typeof options.view === 'string' && (options.view.startsWith('svg:') || options.view.startsWith('html:'))) {
|
|
222
|
+
payload._renderedSvg = renderSvg(positioned, { title: `${graphName} query` });
|
|
223
|
+
} else {
|
|
224
|
+
payload._renderedAscii = renderGraphView(positioned, { title: `QUERY: ${graphName}` });
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return {
|
|
229
|
+
payload,
|
|
230
|
+
exitCode: EXIT_CODES.OK,
|
|
231
|
+
};
|
|
232
|
+
} catch (error) {
|
|
233
|
+
throw mapQueryError(error);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
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
|
+
import handlePatch from './patch.js';
|
|
14
|
+
import handleTree from './tree.js';
|
|
15
|
+
|
|
16
|
+
/** @type {Map<string, Function>} */
|
|
17
|
+
export const COMMANDS = new Map(/** @type {[string, Function][]} */ ([
|
|
18
|
+
['info', handleInfo],
|
|
19
|
+
['query', handleQuery],
|
|
20
|
+
['path', handlePath],
|
|
21
|
+
['history', handleHistory],
|
|
22
|
+
['check', handleCheck],
|
|
23
|
+
['doctor', handleDoctor],
|
|
24
|
+
['materialize', handleMaterialize],
|
|
25
|
+
['seek', handleSeek],
|
|
26
|
+
['verify-audit', handleVerifyAudit],
|
|
27
|
+
['trust', handleTrust],
|
|
28
|
+
['patch', handlePatch],
|
|
29
|
+
['tree', handleTree],
|
|
30
|
+
['view', handleView],
|
|
31
|
+
['install-hooks', handleInstallHooks],
|
|
32
|
+
]));
|