patram 0.9.0 → 0.11.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/lib/cli/arguments.types.d.ts +64 -0
- package/lib/cli/commands/check.js +27 -15
- package/lib/cli/commands/queries.js +189 -1
- package/lib/cli/commands/query.js +6 -3
- package/lib/cli/help-metadata.js +45 -110
- package/lib/cli/parse-arguments-helpers.js +295 -39
- package/lib/cli/render-help.js +87 -0
- package/lib/config/load-patram-config.d.ts +11 -0
- package/lib/config/load-patram-config.js +9 -88
- package/lib/config/manage-stored-queries-helpers.d.ts +69 -0
- package/lib/config/manage-stored-queries-helpers.js +262 -0
- package/lib/config/manage-stored-queries-jsonc.d.ts +31 -0
- package/lib/config/manage-stored-queries-jsonc.js +95 -0
- package/lib/config/manage-stored-queries.d.ts +77 -0
- package/lib/config/manage-stored-queries.js +294 -0
- package/lib/config/schema.d.ts +2 -0
- package/lib/config/schema.js +4 -0
- package/lib/config/validate-patram-config-value.d.ts +13 -0
- package/lib/config/validate-patram-config-value.js +119 -0
- package/lib/find-close-match.d.ts +8 -0
- package/lib/find-close-match.js +98 -0
- package/lib/graph/query/resolve.d.ts +9 -5
- package/lib/graph/query/resolve.js +41 -4
- package/lib/output/layout-stored-queries.js +18 -2
- package/lib/output/list-queries.js +2 -1
- package/lib/output/renderers/json.js +9 -5
- package/lib/output/renderers/plain.js +15 -26
- package/lib/output/renderers/rich.js +22 -26
- package/lib/output/resolve-check-target.js +120 -11
- package/lib/output/view-model/index.js +5 -18
- package/lib/patram.d.ts +8 -0
- package/lib/scan/discover-fields.js +136 -10
- package/package.json +2 -1
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
export type CliCommandName = 'check' | 'fields' | 'query' | 'queries' | 'refs' | 'show';
|
|
2
|
+
export type CliHelpTopicName = 'query-language';
|
|
3
|
+
export type CliHelpTargetKind = 'root' | 'command' | 'topic';
|
|
4
|
+
export type CliUnexpectedArgumentCommandName = CliCommandName | 'help';
|
|
5
|
+
export type CliOutputMode = 'default' | 'plain' | 'json';
|
|
6
|
+
export type CliColorMode = 'auto' | 'always' | 'never';
|
|
7
|
+
export interface ParsedCliCommandRequest {
|
|
8
|
+
kind?: 'command';
|
|
9
|
+
color_mode: CliColorMode;
|
|
10
|
+
command_arguments: string[];
|
|
11
|
+
command_name: CliCommandName;
|
|
12
|
+
output_mode: CliOutputMode;
|
|
13
|
+
query_inspection_mode?: 'explain' | 'lint';
|
|
14
|
+
query_limit?: number;
|
|
15
|
+
query_offset?: number;
|
|
16
|
+
}
|
|
17
|
+
export interface ParsedCliHelpRequest {
|
|
18
|
+
kind: 'help';
|
|
19
|
+
target_kind: CliHelpTargetKind;
|
|
20
|
+
target_name?: CliCommandName | CliHelpTopicName;
|
|
21
|
+
}
|
|
22
|
+
export type ParsedCliArguments = ParsedCliCommandRequest;
|
|
23
|
+
export type ParsedCliRequest = ParsedCliCommandRequest | ParsedCliHelpRequest;
|
|
24
|
+
export type CliParseError = {
|
|
25
|
+
code: 'message';
|
|
26
|
+
message: string;
|
|
27
|
+
} | {
|
|
28
|
+
code: 'missing_required_argument';
|
|
29
|
+
argument_label: string;
|
|
30
|
+
command_name: 'query' | 'refs' | 'show';
|
|
31
|
+
} | {
|
|
32
|
+
code: 'unexpected_argument';
|
|
33
|
+
command_name: CliUnexpectedArgumentCommandName;
|
|
34
|
+
token: string;
|
|
35
|
+
} | {
|
|
36
|
+
code: 'option_not_valid_for_command';
|
|
37
|
+
command_name: CliCommandName;
|
|
38
|
+
token: string;
|
|
39
|
+
} | {
|
|
40
|
+
code: 'unknown_command';
|
|
41
|
+
suggestion?: CliCommandName;
|
|
42
|
+
token: string;
|
|
43
|
+
} | {
|
|
44
|
+
code: 'unknown_help_target';
|
|
45
|
+
suggestion?: CliCommandName | CliHelpTopicName;
|
|
46
|
+
token: string;
|
|
47
|
+
} | {
|
|
48
|
+
code: 'unknown_stored_query';
|
|
49
|
+
name: string;
|
|
50
|
+
next_path?: string;
|
|
51
|
+
suggestion?: string;
|
|
52
|
+
} | {
|
|
53
|
+
code: 'unknown_option';
|
|
54
|
+
command_name?: CliCommandName;
|
|
55
|
+
suggestion?: string;
|
|
56
|
+
token: string;
|
|
57
|
+
};
|
|
58
|
+
export type ParseCliArgumentsResult = {
|
|
59
|
+
success: true;
|
|
60
|
+
value: ParsedCliRequest;
|
|
61
|
+
} | {
|
|
62
|
+
error: CliParseError;
|
|
63
|
+
success: false;
|
|
64
|
+
};
|
|
@@ -9,13 +9,15 @@ import {
|
|
|
9
9
|
renderCheckSuccess,
|
|
10
10
|
} from '../../output/render-check-output.js';
|
|
11
11
|
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
resolveCheckTargetProjectDirectory,
|
|
13
|
+
resolveCheckTargets,
|
|
14
|
+
selectCheckTargetsDiagnostics,
|
|
15
|
+
selectCheckTargetsSourceFiles,
|
|
15
16
|
} from '../../output/resolve-check-target.js';
|
|
16
17
|
import { listRepoFiles } from '../../scan/list-repo-files.js';
|
|
17
18
|
|
|
18
19
|
import { resolveCommandOutputMode } from '../command-helpers.js';
|
|
20
|
+
import { renderCliParseError } from '../render-help.js';
|
|
19
21
|
|
|
20
22
|
/**
|
|
21
23
|
* @param {ParsedCliCommandRequest} parsed_command
|
|
@@ -24,18 +26,28 @@ import { resolveCommandOutputMode } from '../command-helpers.js';
|
|
|
24
26
|
*/
|
|
25
27
|
export async function runCheckCommand(parsed_command, io_context) {
|
|
26
28
|
const output_mode = resolveCommandOutputMode(parsed_command, io_context);
|
|
27
|
-
const
|
|
28
|
-
parsed_command.command_arguments
|
|
29
|
+
const resolved_targets = await resolveCheckTargets(
|
|
30
|
+
parsed_command.command_arguments,
|
|
29
31
|
);
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
const project_directory =
|
|
33
|
+
resolveCheckTargetProjectDirectory(resolved_targets);
|
|
34
|
+
|
|
35
|
+
if (!project_directory) {
|
|
36
|
+
io_context.stderr.write(
|
|
37
|
+
renderCliParseError({
|
|
38
|
+
code: 'message',
|
|
39
|
+
message: 'Check paths must resolve to the same project root.',
|
|
40
|
+
}),
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
return 1;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const project_graph_result = await loadProjectGraph(project_directory);
|
|
47
|
+
const repo_file_paths = await listRepoFiles(project_directory);
|
|
48
|
+
const selected_source_file_paths = selectCheckTargetsSourceFiles(
|
|
37
49
|
project_graph_result.source_file_paths,
|
|
38
|
-
|
|
50
|
+
resolved_targets,
|
|
39
51
|
);
|
|
40
52
|
|
|
41
53
|
if (project_graph_result.diagnostics.length > 0) {
|
|
@@ -52,9 +64,9 @@ export async function runCheckCommand(parsed_command, io_context) {
|
|
|
52
64
|
project_graph_result.config,
|
|
53
65
|
project_graph_result.claims,
|
|
54
66
|
);
|
|
55
|
-
const selected_diagnostics =
|
|
67
|
+
const selected_diagnostics = selectCheckTargetsDiagnostics(
|
|
56
68
|
diagnostics,
|
|
57
|
-
|
|
69
|
+
resolved_targets,
|
|
58
70
|
);
|
|
59
71
|
|
|
60
72
|
if (selected_diagnostics.length > 0) {
|
|
@@ -4,12 +4,18 @@
|
|
|
4
4
|
|
|
5
5
|
import process from 'node:process';
|
|
6
6
|
|
|
7
|
+
import { manageStoredQueries } from '../../config/manage-stored-queries.js';
|
|
7
8
|
import { loadPatramConfig } from '../../config/load-patram-config.js';
|
|
9
|
+
import { renderCheckDiagnostics } from '../../output/render-check-output.js';
|
|
8
10
|
import { writeCommandOutput } from '../../output/command-output.js';
|
|
9
11
|
import { listQueries } from '../../output/list-queries.js';
|
|
10
12
|
import { createOutputView } from '../../output/render-output-view.js';
|
|
13
|
+
import { renderCliParseError } from '../render-help.js';
|
|
11
14
|
|
|
12
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
resolveCommandOutputMode,
|
|
17
|
+
writeDiagnostics,
|
|
18
|
+
} from '../command-helpers.js';
|
|
13
19
|
|
|
14
20
|
/**
|
|
15
21
|
* @param {ParsedCliCommandRequest} parsed_command
|
|
@@ -17,6 +23,10 @@ import { writeDiagnostics } from '../command-helpers.js';
|
|
|
17
23
|
* @returns {Promise<number>}
|
|
18
24
|
*/
|
|
19
25
|
export async function runQueriesCommand(parsed_command, io_context) {
|
|
26
|
+
if (parsed_command.command_arguments.length > 0) {
|
|
27
|
+
return runQueriesMutationCommand(parsed_command, io_context);
|
|
28
|
+
}
|
|
29
|
+
|
|
20
30
|
const load_result = await loadPatramConfig(process.cwd());
|
|
21
31
|
|
|
22
32
|
if (load_result.diagnostics.length > 0) {
|
|
@@ -39,3 +49,181 @@ export async function runQueriesCommand(parsed_command, io_context) {
|
|
|
39
49
|
|
|
40
50
|
return 0;
|
|
41
51
|
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @param {ParsedCliCommandRequest} parsed_command
|
|
55
|
+
* @param {{ stderr: { write(chunk: string): boolean }, stdout: { isTTY?: boolean, write(chunk: string): boolean } }} io_context
|
|
56
|
+
* @returns {Promise<number>}
|
|
57
|
+
*/
|
|
58
|
+
async function runQueriesMutationCommand(parsed_command, io_context) {
|
|
59
|
+
const mutation_request = parseStoredQueryMutation(
|
|
60
|
+
parsed_command.command_arguments,
|
|
61
|
+
);
|
|
62
|
+
const output_mode = resolveCommandOutputMode(parsed_command, io_context);
|
|
63
|
+
|
|
64
|
+
if (!mutation_request.success) {
|
|
65
|
+
io_context.stderr.write(renderCliParseError(mutation_request.error));
|
|
66
|
+
|
|
67
|
+
return 1;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const mutation_result = await manageStoredQueries(
|
|
71
|
+
process.cwd(),
|
|
72
|
+
mutation_request.value,
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
if (!mutation_result.success) {
|
|
76
|
+
if ('error' in mutation_result) {
|
|
77
|
+
io_context.stderr.write(renderCliParseError(mutation_result.error));
|
|
78
|
+
|
|
79
|
+
return 1;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
io_context.stderr.write(
|
|
83
|
+
renderCheckDiagnostics(mutation_result.diagnostics, output_mode),
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
return 1;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
io_context.stdout.write(
|
|
90
|
+
renderStoredQueryMutationResult(
|
|
91
|
+
mutation_result.value,
|
|
92
|
+
parsed_command.output_mode,
|
|
93
|
+
),
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
return 0;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* @param {string[]} command_arguments
|
|
101
|
+
* @returns {{
|
|
102
|
+
* success: true,
|
|
103
|
+
* value:
|
|
104
|
+
* | { action: 'add', description?: string, name: string, where: string }
|
|
105
|
+
* | { action: 'remove', name: string }
|
|
106
|
+
* | {
|
|
107
|
+
* action: 'update',
|
|
108
|
+
* description?: string,
|
|
109
|
+
* name: string,
|
|
110
|
+
* next_name?: string,
|
|
111
|
+
* where?: string,
|
|
112
|
+
* },
|
|
113
|
+
* } | { error: import('../arguments.types.ts').CliParseError, success: false }}
|
|
114
|
+
*/
|
|
115
|
+
function parseStoredQueryMutation(command_arguments) {
|
|
116
|
+
const subcommand_name = command_arguments[0];
|
|
117
|
+
|
|
118
|
+
if (subcommand_name === 'add') {
|
|
119
|
+
return {
|
|
120
|
+
success: true,
|
|
121
|
+
value: {
|
|
122
|
+
action: 'add',
|
|
123
|
+
description: readOptionValue(command_arguments, '--desc'),
|
|
124
|
+
name: command_arguments[1],
|
|
125
|
+
where: readRequiredOptionValue(command_arguments, '--query'),
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (subcommand_name === 'remove') {
|
|
131
|
+
return {
|
|
132
|
+
success: true,
|
|
133
|
+
value: {
|
|
134
|
+
action: 'remove',
|
|
135
|
+
name: command_arguments[1],
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (subcommand_name === 'update') {
|
|
141
|
+
return {
|
|
142
|
+
success: true,
|
|
143
|
+
value: {
|
|
144
|
+
action: 'update',
|
|
145
|
+
description: readOptionValue(command_arguments, '--desc'),
|
|
146
|
+
name: command_arguments[1],
|
|
147
|
+
next_name: readOptionValue(command_arguments, '--name'),
|
|
148
|
+
where: readOptionValue(command_arguments, '--query'),
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
error: {
|
|
155
|
+
code: 'unexpected_argument',
|
|
156
|
+
command_name: 'queries',
|
|
157
|
+
token: subcommand_name ?? '',
|
|
158
|
+
},
|
|
159
|
+
success: false,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* @param {{
|
|
165
|
+
* action: 'added',
|
|
166
|
+
* name: string,
|
|
167
|
+
* } | {
|
|
168
|
+
* action: 'removed',
|
|
169
|
+
* name: string,
|
|
170
|
+
* } | {
|
|
171
|
+
* action: 'updated',
|
|
172
|
+
* name: string,
|
|
173
|
+
* previous_name?: string,
|
|
174
|
+
* }} mutation_result
|
|
175
|
+
* @param {ParsedCliCommandRequest['output_mode']} output_mode
|
|
176
|
+
* @returns {string}
|
|
177
|
+
*/
|
|
178
|
+
function renderStoredQueryMutationResult(mutation_result, output_mode) {
|
|
179
|
+
if (output_mode === 'json') {
|
|
180
|
+
return `${JSON.stringify(mutation_result, null, 2)}\n`;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (
|
|
184
|
+
mutation_result.action === 'updated' &&
|
|
185
|
+
mutation_result.previous_name !== undefined
|
|
186
|
+
) {
|
|
187
|
+
return `Updated stored query: ${mutation_result.previous_name} -> ${mutation_result.name}\n`;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (mutation_result.action === 'updated') {
|
|
191
|
+
return `Updated stored query: ${mutation_result.name}\n`;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (mutation_result.action === 'added') {
|
|
195
|
+
return `Added stored query: ${mutation_result.name}\n`;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return `Removed stored query: ${mutation_result.name}\n`;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* @param {string[]} command_arguments
|
|
203
|
+
* @param {'--desc' | '--name' | '--query'} option_name
|
|
204
|
+
* @returns {string}
|
|
205
|
+
*/
|
|
206
|
+
function readRequiredOptionValue(command_arguments, option_name) {
|
|
207
|
+
const option_value = readOptionValue(command_arguments, option_name);
|
|
208
|
+
|
|
209
|
+
if (option_value === undefined) {
|
|
210
|
+
throw new Error(`Expected ${option_name} to be present.`);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return option_value;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* @param {string[]} command_arguments
|
|
218
|
+
* @param {'--desc' | '--name' | '--query'} option_name
|
|
219
|
+
* @returns {string | undefined}
|
|
220
|
+
*/
|
|
221
|
+
function readOptionValue(command_arguments, option_name) {
|
|
222
|
+
const option_index = command_arguments.indexOf(option_name);
|
|
223
|
+
|
|
224
|
+
if (option_index < 0) {
|
|
225
|
+
return undefined;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return command_arguments[option_index + 1];
|
|
229
|
+
}
|
|
@@ -12,7 +12,10 @@ import {
|
|
|
12
12
|
} from '../../output/command-output.js';
|
|
13
13
|
import { createDerivedSummaryEvaluator } from '../../output/derived-summary.js';
|
|
14
14
|
import { renderCheckDiagnostics } from '../../output/render-check-output.js';
|
|
15
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
renderCliParseError,
|
|
17
|
+
renderInvalidWhereDiagnostic,
|
|
18
|
+
} from '../render-help.js';
|
|
16
19
|
import { createOutputView } from '../../output/render-output-view.js';
|
|
17
20
|
import { loadPatramConfig } from '../../config/load-patram-config.js';
|
|
18
21
|
import { loadProjectGraph } from '../../graph/load-project-graph.js';
|
|
@@ -60,7 +63,7 @@ export async function runQueryCommand(parsed_command, io_context) {
|
|
|
60
63
|
);
|
|
61
64
|
|
|
62
65
|
if (!where_clause.success) {
|
|
63
|
-
io_context.stderr.write(
|
|
66
|
+
io_context.stderr.write(renderCliParseError(where_clause.error));
|
|
64
67
|
|
|
65
68
|
return 1;
|
|
66
69
|
}
|
|
@@ -139,7 +142,7 @@ async function runQueryInspectionCommand(
|
|
|
139
142
|
);
|
|
140
143
|
|
|
141
144
|
if (!where_clause.success) {
|
|
142
|
-
io_context.stderr.write(
|
|
145
|
+
io_context.stderr.write(renderCliParseError(where_clause.error));
|
|
143
146
|
|
|
144
147
|
return 1;
|
|
145
148
|
}
|
package/lib/cli/help-metadata.js
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
* } from './arguments.types.ts';
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import { findCloseMatch } from '../find-close-match.js';
|
|
10
|
+
|
|
9
11
|
/**
|
|
10
12
|
* @typedef {{
|
|
11
13
|
* description: string,
|
|
@@ -85,14 +87,15 @@ const COMMAND_DEFINITIONS = {
|
|
|
85
87
|
'patram check',
|
|
86
88
|
'patram check docs',
|
|
87
89
|
'patram check docs/patram.md',
|
|
90
|
+
'patram check docs docs/patram.md',
|
|
88
91
|
],
|
|
89
|
-
extra_positionals_message: 'Check accepts
|
|
92
|
+
extra_positionals_message: 'Check accepts zero or more paths.',
|
|
90
93
|
help_topics: [],
|
|
91
|
-
max_positionals:
|
|
94
|
+
max_positionals: Number.POSITIVE_INFINITY,
|
|
92
95
|
min_positionals: 0,
|
|
93
96
|
missing_argument_examples: [],
|
|
94
97
|
missing_argument_label: null,
|
|
95
|
-
missing_usage_lines: ['patram check [path]'],
|
|
98
|
+
missing_usage_lines: ['patram check [path ...]'],
|
|
96
99
|
option_column_width: 10,
|
|
97
100
|
options: [
|
|
98
101
|
{
|
|
@@ -108,7 +111,7 @@ const COMMAND_DEFINITIONS = {
|
|
|
108
111
|
root_summary: 'Validate a project, directory, or file',
|
|
109
112
|
summary:
|
|
110
113
|
'Validate a project, directory, or file and report graph diagnostics.',
|
|
111
|
-
usage_lines: ['patram check [path] [options]'],
|
|
114
|
+
usage_lines: ['patram check [path ...] [options]'],
|
|
112
115
|
},
|
|
113
116
|
fields: {
|
|
114
117
|
allowed_option_names: new Set(),
|
|
@@ -219,17 +222,40 @@ const COMMAND_DEFINITIONS = {
|
|
|
219
222
|
],
|
|
220
223
|
},
|
|
221
224
|
queries: {
|
|
222
|
-
allowed_option_names: new Set(),
|
|
223
|
-
examples: [
|
|
224
|
-
|
|
225
|
+
allowed_option_names: new Set(['desc', 'name', 'query']),
|
|
226
|
+
examples: [
|
|
227
|
+
'patram queries',
|
|
228
|
+
"patram queries add ready-tasks --query '$class=task and status=ready'",
|
|
229
|
+
"patram queries update ready-tasks --desc 'Show tasks that are ready.'",
|
|
230
|
+
'patram queries remove ready-tasks',
|
|
231
|
+
],
|
|
232
|
+
extra_positionals_message:
|
|
233
|
+
'Queries accepts no positionals unless using add, update, or remove.',
|
|
225
234
|
help_topics: [],
|
|
226
|
-
max_positionals:
|
|
235
|
+
max_positionals: 2,
|
|
227
236
|
min_positionals: 0,
|
|
228
237
|
missing_argument_examples: [],
|
|
229
238
|
missing_argument_label: null,
|
|
230
|
-
missing_usage_lines: [
|
|
231
|
-
|
|
239
|
+
missing_usage_lines: [
|
|
240
|
+
'patram queries',
|
|
241
|
+
'patram queries add <name> --query <clause>',
|
|
242
|
+
'patram queries update <name> [--name <new_name>] [--query <clause>] [--desc <text>]',
|
|
243
|
+
'patram queries remove <name>',
|
|
244
|
+
],
|
|
245
|
+
option_column_width: 19,
|
|
232
246
|
options: [
|
|
247
|
+
{
|
|
248
|
+
description: 'Persist a new stored query',
|
|
249
|
+
label: '--query <clause>',
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
description: 'Set or rename the stored query name for update',
|
|
253
|
+
label: '--name <new_name>',
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
description: 'Set or clear the stored query description',
|
|
257
|
+
label: '--desc <text>',
|
|
258
|
+
},
|
|
233
259
|
{
|
|
234
260
|
description: 'Print plain text output',
|
|
235
261
|
label: '--plain',
|
|
@@ -240,9 +266,15 @@ const COMMAND_DEFINITIONS = {
|
|
|
240
266
|
},
|
|
241
267
|
],
|
|
242
268
|
related: ['query'],
|
|
243
|
-
root_summary: 'List stored queries',
|
|
244
|
-
summary:
|
|
245
|
-
|
|
269
|
+
root_summary: 'List and manage stored queries',
|
|
270
|
+
summary:
|
|
271
|
+
'List stored queries or mutate them through add, update, and remove.',
|
|
272
|
+
usage_lines: [
|
|
273
|
+
'patram queries [options]',
|
|
274
|
+
'patram queries add <name> --query <clause> [--desc <text>] [options]',
|
|
275
|
+
'patram queries update <name> [--name <new_name>] [--query <clause>] [--desc <text>] [options]',
|
|
276
|
+
'patram queries remove <name> [options]',
|
|
277
|
+
],
|
|
246
278
|
},
|
|
247
279
|
refs: {
|
|
248
280
|
allowed_option_names: new Set(['where']),
|
|
@@ -532,100 +564,3 @@ function listOptionLabels(command_name) {
|
|
|
532
564
|
|
|
533
565
|
return [...option_labels];
|
|
534
566
|
}
|
|
535
|
-
|
|
536
|
-
/**
|
|
537
|
-
* @param {string} input_text
|
|
538
|
-
* @param {readonly string[]} candidates
|
|
539
|
-
* @returns {string | undefined}
|
|
540
|
-
*/
|
|
541
|
-
function findCloseMatch(input_text, candidates) {
|
|
542
|
-
let best_candidate;
|
|
543
|
-
let best_score = 0;
|
|
544
|
-
|
|
545
|
-
for (const candidate of candidates) {
|
|
546
|
-
const score = scoreCandidate(input_text, candidate);
|
|
547
|
-
|
|
548
|
-
if (score > best_score) {
|
|
549
|
-
best_candidate = candidate;
|
|
550
|
-
best_score = score;
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
if (best_score < 0.6) {
|
|
555
|
-
return undefined;
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
return best_candidate;
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
/**
|
|
562
|
-
* @param {string} input_text
|
|
563
|
-
* @param {string} candidate
|
|
564
|
-
* @returns {number}
|
|
565
|
-
*/
|
|
566
|
-
function scoreCandidate(input_text, candidate) {
|
|
567
|
-
const max_length = Math.max(input_text.length, candidate.length);
|
|
568
|
-
|
|
569
|
-
if (max_length === 0) {
|
|
570
|
-
return 1;
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
return (
|
|
574
|
-
1 - calculateDamerauLevenshteinDistance(input_text, candidate) / max_length
|
|
575
|
-
);
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
/**
|
|
579
|
-
* @param {string} left_text
|
|
580
|
-
* @param {string} right_text
|
|
581
|
-
* @returns {number}
|
|
582
|
-
*/
|
|
583
|
-
function calculateDamerauLevenshteinDistance(left_text, right_text) {
|
|
584
|
-
/** @type {number[][]} */
|
|
585
|
-
const matrix = Array.from({ length: left_text.length + 1 }, () =>
|
|
586
|
-
Array.from({ length: right_text.length + 1 }, () => 0),
|
|
587
|
-
);
|
|
588
|
-
|
|
589
|
-
for (let left_index = 0; left_index <= left_text.length; left_index += 1) {
|
|
590
|
-
matrix[left_index][0] = left_index;
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
for (
|
|
594
|
-
let right_index = 0;
|
|
595
|
-
right_index <= right_text.length;
|
|
596
|
-
right_index += 1
|
|
597
|
-
) {
|
|
598
|
-
matrix[0][right_index] = right_index;
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
for (let left_index = 1; left_index <= left_text.length; left_index += 1) {
|
|
602
|
-
for (
|
|
603
|
-
let right_index = 1;
|
|
604
|
-
right_index <= right_text.length;
|
|
605
|
-
right_index += 1
|
|
606
|
-
) {
|
|
607
|
-
const substitution_cost =
|
|
608
|
-
left_text[left_index - 1] === right_text[right_index - 1] ? 0 : 1;
|
|
609
|
-
|
|
610
|
-
matrix[left_index][right_index] = Math.min(
|
|
611
|
-
matrix[left_index - 1][right_index] + 1,
|
|
612
|
-
matrix[left_index][right_index - 1] + 1,
|
|
613
|
-
matrix[left_index - 1][right_index - 1] + substitution_cost,
|
|
614
|
-
);
|
|
615
|
-
|
|
616
|
-
if (
|
|
617
|
-
left_index > 1 &&
|
|
618
|
-
right_index > 1 &&
|
|
619
|
-
left_text[left_index - 1] === right_text[right_index - 2] &&
|
|
620
|
-
left_text[left_index - 2] === right_text[right_index - 1]
|
|
621
|
-
) {
|
|
622
|
-
matrix[left_index][right_index] = Math.min(
|
|
623
|
-
matrix[left_index][right_index],
|
|
624
|
-
matrix[left_index - 2][right_index - 2] + 1,
|
|
625
|
-
);
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
return matrix[left_text.length][right_text.length];
|
|
631
|
-
}
|