patram 0.0.2 → 0.2.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.
Files changed (67) hide show
  1. package/bin/patram.js +25 -147
  2. package/lib/build-graph-identity.js +270 -0
  3. package/lib/build-graph.js +156 -77
  4. package/lib/check-graph.js +23 -7
  5. package/lib/claim-helpers.js +55 -0
  6. package/lib/cli-help-metadata.js +552 -0
  7. package/lib/command-output.js +83 -0
  8. package/lib/derived-summary.js +278 -0
  9. package/lib/format-derived-summary-row.js +9 -0
  10. package/lib/format-node-header.js +19 -0
  11. package/lib/format-output-item-block.js +22 -0
  12. package/lib/format-output-metadata.js +62 -0
  13. package/lib/layout-stored-queries.js +361 -0
  14. package/lib/list-queries.js +18 -0
  15. package/lib/list-source-files.js +50 -15
  16. package/lib/load-patram-config.js +505 -18
  17. package/lib/load-patram-config.types.ts +40 -0
  18. package/lib/load-project-graph.js +124 -0
  19. package/lib/output-view.types.ts +88 -0
  20. package/lib/parse-claims.js +38 -158
  21. package/lib/parse-claims.types.ts +7 -0
  22. package/lib/parse-cli-arguments-helpers.js +446 -0
  23. package/lib/parse-cli-arguments.js +266 -0
  24. package/lib/parse-cli-arguments.types.ts +69 -0
  25. package/lib/parse-cli-color-options.js +44 -0
  26. package/lib/parse-cli-query-pagination.js +49 -0
  27. package/lib/parse-jsdoc-blocks.js +184 -0
  28. package/lib/parse-jsdoc-claims.js +280 -0
  29. package/lib/parse-jsdoc-prose.js +111 -0
  30. package/lib/parse-markdown-claims.js +242 -0
  31. package/lib/parse-markdown-directives.js +136 -0
  32. package/lib/parse-where-clause.js +707 -0
  33. package/lib/parse-where-clause.types.ts +70 -0
  34. package/lib/patram-cli.js +464 -0
  35. package/lib/patram-config.js +3 -1
  36. package/lib/patram-config.types.ts +2 -1
  37. package/lib/patram.js +6 -0
  38. package/lib/query-graph.js +368 -0
  39. package/lib/query-inspection.js +523 -0
  40. package/lib/render-check-output.js +315 -0
  41. package/lib/render-cli-help.js +419 -0
  42. package/lib/render-json-output.js +161 -0
  43. package/lib/render-output-view.js +222 -0
  44. package/lib/render-plain-output.js +182 -0
  45. package/lib/render-rich-output.js +240 -0
  46. package/lib/render-rich-source.js +1333 -0
  47. package/lib/resolve-check-target.js +190 -0
  48. package/lib/resolve-output-mode.js +60 -0
  49. package/lib/resolve-patram-graph-config.js +88 -0
  50. package/lib/resolve-where-clause.js +66 -0
  51. package/lib/show-document.js +311 -0
  52. package/lib/source-file-defaults.js +28 -0
  53. package/lib/tagged-fenced-block-error.js +17 -0
  54. package/lib/tagged-fenced-block-markdown.js +111 -0
  55. package/lib/tagged-fenced-block-metadata.js +97 -0
  56. package/lib/tagged-fenced-block-parser.js +292 -0
  57. package/lib/tagged-fenced-blocks.js +100 -0
  58. package/lib/tagged-fenced-blocks.types.ts +38 -0
  59. package/lib/write-paged-output.js +87 -0
  60. package/package.json +28 -12
  61. package/bin/patram.test.js +0 -184
  62. package/lib/build-graph.test.js +0 -141
  63. package/lib/check-graph.test.js +0 -103
  64. package/lib/list-source-files.test.js +0 -101
  65. package/lib/load-patram-config.test.js +0 -211
  66. package/lib/parse-claims.test.js +0 -113
  67. package/lib/patram-config.test.js +0 -147
@@ -0,0 +1,446 @@
1
+ /* eslint-disable max-lines */
2
+ /**
3
+ * @typedef {import('./parse-cli-arguments.types.ts').CliCommandName} CliCommandName
4
+ * @typedef {import('./parse-cli-arguments.types.ts').CliOutputMode} CliOutputMode
5
+ * @typedef {import('./parse-cli-arguments.types.ts').CliParseError} CliParseError
6
+ * @typedef {import('./parse-cli-arguments.types.ts').ParsedCliHelpRequest} ParsedCliHelpRequest
7
+ * @typedef {{ kind: string, name?: string, rawName?: string, value?: string | boolean }} CliOptionToken
8
+ * @typedef {{ color?: string, explain?: boolean, help?: boolean, json?: boolean, limit?: string, lint?: boolean, 'no-color'?: boolean, offset?: string, plain?: boolean, where?: string }} CliOptionValues
9
+ * @typedef {{ option_tokens: CliOptionToken[], positionals: string[], values: CliOptionValues }} ParsedCommandLine
10
+ */
11
+
12
+ import {
13
+ findCommandSuggestion,
14
+ findHelpTargetSuggestion,
15
+ findOptionSuggestion,
16
+ getCommandDefinition,
17
+ GLOBAL_OPTION_NAMES,
18
+ isCommandName,
19
+ isHelpTopicName,
20
+ } from './cli-help-metadata.js';
21
+ import { findInvalidColorMode } from './parse-cli-color-options.js';
22
+ import { findInvalidQueryPagination } from './parse-cli-query-pagination.js';
23
+
24
+ export const CLI_OPTIONS = /** @type {const} */ ({
25
+ color: { type: 'string' },
26
+ explain: { type: 'boolean' },
27
+ help: { type: 'boolean' },
28
+ json: { type: 'boolean' },
29
+ limit: { type: 'string' },
30
+ lint: { type: 'boolean' },
31
+ 'no-color': { type: 'boolean' },
32
+ offset: { type: 'string' },
33
+ plain: { type: 'boolean' },
34
+ where: { type: 'string' },
35
+ });
36
+
37
+ /**
38
+ * @param {CliOptionToken[]} tokens
39
+ * @returns {CliOptionToken[]}
40
+ */
41
+ export function collectOptionTokens(tokens) {
42
+ return tokens.filter((token) => token.kind === 'option');
43
+ }
44
+
45
+ /**
46
+ * @param {ParsedCommandLine} command_line
47
+ * @returns {CliParseError | null}
48
+ */
49
+ export function validateRootCommandLine(command_line) {
50
+ return (
51
+ findUnknownOption(undefined, command_line.option_tokens) ??
52
+ findMissingOptionValue(command_line.option_tokens) ??
53
+ findOutputModeConflict(command_line.values) ??
54
+ createMessageParseError(findInvalidColorMode(command_line.option_tokens))
55
+ );
56
+ }
57
+
58
+ /**
59
+ * @param {ParsedCommandLine} command_line
60
+ * @returns {CliParseError | null}
61
+ */
62
+ export function validateHelpCommandLine(command_line) {
63
+ const help_validation_error = validateRootCommandLine(command_line);
64
+
65
+ if (help_validation_error) {
66
+ return help_validation_error;
67
+ }
68
+
69
+ if (command_line.positionals.length > 2) {
70
+ return createMessageParseError(
71
+ 'Help accepts at most one topic or command.',
72
+ );
73
+ }
74
+
75
+ return null;
76
+ }
77
+
78
+ /**
79
+ * @param {CliCommandName} command_name
80
+ * @param {ParsedCommandLine} command_line
81
+ * @returns {CliParseError | null}
82
+ */
83
+ export function validateCommandLineBeforeHelp(command_name, command_line) {
84
+ return (
85
+ findUnknownOption(command_name, command_line.option_tokens) ??
86
+ findMissingOptionValue(command_line.option_tokens) ??
87
+ findOutputModeConflict(command_line.values) ??
88
+ createMessageParseError(findInvalidColorMode(command_line.option_tokens))
89
+ );
90
+ }
91
+
92
+ /**
93
+ * @param {CliCommandName} command_name
94
+ * @param {ParsedCommandLine} command_line
95
+ * @returns {CliParseError | null}
96
+ */
97
+ export function validateParsedCommand(command_name, command_line) {
98
+ const command_positionals = command_line.positionals.slice(1);
99
+
100
+ if (
101
+ command_name === 'query' &&
102
+ command_line.values.where !== undefined &&
103
+ command_positionals.length === 0
104
+ ) {
105
+ return (
106
+ createMessageParseError(
107
+ findInvalidQueryPagination(command_line.option_tokens),
108
+ ) ??
109
+ findInvalidQueryMode(
110
+ command_name,
111
+ command_line.values,
112
+ command_positionals,
113
+ )
114
+ );
115
+ }
116
+
117
+ return (
118
+ findInvalidCommandOption(command_name, command_line.option_tokens) ??
119
+ createMessageParseError(
120
+ findInvalidQueryPagination(command_line.option_tokens),
121
+ ) ??
122
+ findInvalidQueryInspection(command_name, command_line.values) ??
123
+ findInvalidQueryMode(
124
+ command_name,
125
+ command_line.values,
126
+ command_positionals,
127
+ ) ??
128
+ validateCommandPositionals(command_name, command_positionals)
129
+ );
130
+ }
131
+
132
+ /**
133
+ * @param {CliOptionValues} parsed_values
134
+ * @returns {CliOutputMode}
135
+ */
136
+ export function resolveOutputMode(parsed_values) {
137
+ if (parsed_values.json) {
138
+ return 'json';
139
+ }
140
+
141
+ if (parsed_values.plain) {
142
+ return 'plain';
143
+ }
144
+
145
+ return 'default';
146
+ }
147
+
148
+ /**
149
+ * @param {CliCommandName} command_name
150
+ * @param {string[]} command_positionals
151
+ * @param {CliOptionValues} parsed_values
152
+ * @returns {string[]}
153
+ */
154
+ export function buildCommandArguments(
155
+ command_name,
156
+ command_positionals,
157
+ parsed_values,
158
+ ) {
159
+ if (command_name === 'query' && parsed_values.where !== undefined) {
160
+ return ['--where', parsed_values.where];
161
+ }
162
+
163
+ return command_positionals;
164
+ }
165
+
166
+ /**
167
+ * @param {string | undefined} help_target
168
+ * @returns {CliParseError}
169
+ */
170
+ export function createUnknownHelpTargetError(help_target) {
171
+ return {
172
+ code: 'unknown_help_target',
173
+ suggestion: help_target ? findHelpTargetSuggestion(help_target) : undefined,
174
+ token: help_target ?? '',
175
+ };
176
+ }
177
+
178
+ /**
179
+ * @param {string | undefined} command_name
180
+ * @returns {CliParseError}
181
+ */
182
+ export function createUnknownCommandError(command_name) {
183
+ return {
184
+ code: 'unknown_command',
185
+ suggestion:
186
+ command_name && command_name.length > 0
187
+ ? findCommandSuggestion(command_name)
188
+ : undefined,
189
+ token: command_name ?? '',
190
+ };
191
+ }
192
+
193
+ /**
194
+ * @param {CliCommandName} command_name
195
+ * @returns {ParsedCliHelpRequest}
196
+ */
197
+ export function createCommandHelpRequest(command_name) {
198
+ return {
199
+ kind: 'help',
200
+ target_kind: 'command',
201
+ target_name: command_name,
202
+ };
203
+ }
204
+
205
+ /**
206
+ * @param {string | undefined} help_target
207
+ * @returns {ParsedCliHelpRequest | null}
208
+ */
209
+ export function createNamedHelpRequest(help_target) {
210
+ if (isCommandName(help_target)) {
211
+ return createCommandHelpRequest(help_target);
212
+ }
213
+
214
+ if (isHelpTopicName(help_target)) {
215
+ return {
216
+ kind: 'help',
217
+ target_kind: 'topic',
218
+ target_name: help_target,
219
+ };
220
+ }
221
+
222
+ return null;
223
+ }
224
+
225
+ /**
226
+ * @returns {ParsedCliHelpRequest}
227
+ */
228
+ export function createRootHelpRequest() {
229
+ return {
230
+ kind: 'help',
231
+ target_kind: 'root',
232
+ };
233
+ }
234
+
235
+ /**
236
+ * @param {string | null} message
237
+ * @returns {CliParseError | null}
238
+ */
239
+ export function createMessageParseError(message) {
240
+ if (!message) {
241
+ return null;
242
+ }
243
+
244
+ return {
245
+ code: 'message',
246
+ message,
247
+ };
248
+ }
249
+
250
+ /**
251
+ * @param {CliCommandName | undefined} command_name
252
+ * @param {CliOptionToken[]} option_tokens
253
+ * @returns {CliParseError | null}
254
+ */
255
+ function findUnknownOption(command_name, option_tokens) {
256
+ for (const token of option_tokens) {
257
+ if (
258
+ token.name &&
259
+ token.rawName &&
260
+ !GLOBAL_OPTION_NAMES.has(token.name) &&
261
+ !isKnownCommandOptionName(token.name)
262
+ ) {
263
+ return {
264
+ code: 'unknown_option',
265
+ command_name,
266
+ suggestion: findOptionSuggestion(token.rawName, command_name),
267
+ token: token.rawName,
268
+ };
269
+ }
270
+ }
271
+
272
+ return null;
273
+ }
274
+
275
+ /**
276
+ * @param {CliCommandName} command_name
277
+ * @param {CliOptionToken[]} option_tokens
278
+ * @returns {CliParseError | null}
279
+ */
280
+ function findInvalidCommandOption(command_name, option_tokens) {
281
+ const command_definition = getCommandDefinition(command_name);
282
+
283
+ for (const token of option_tokens) {
284
+ if (!token.name || !token.rawName || GLOBAL_OPTION_NAMES.has(token.name)) {
285
+ continue;
286
+ }
287
+
288
+ if (!command_definition.allowed_option_names.has(token.name)) {
289
+ return {
290
+ code: 'option_not_valid_for_command',
291
+ command_name,
292
+ token: token.rawName,
293
+ };
294
+ }
295
+ }
296
+
297
+ return null;
298
+ }
299
+
300
+ /**
301
+ * @param {CliOptionToken[]} option_tokens
302
+ * @returns {CliParseError | null}
303
+ */
304
+ function findMissingOptionValue(option_tokens) {
305
+ for (const token of option_tokens) {
306
+ if (token.name === 'where' && typeof token.value !== 'string') {
307
+ return {
308
+ argument_label: '<name> or --where "<clause>"',
309
+ code: 'missing_required_argument',
310
+ command_name: 'query',
311
+ };
312
+ }
313
+
314
+ if (token.name === 'offset' && typeof token.value !== 'string') {
315
+ return createMessageParseError('Offset requires a value.');
316
+ }
317
+
318
+ if (token.name === 'limit' && typeof token.value !== 'string') {
319
+ return createMessageParseError('Limit requires a value.');
320
+ }
321
+
322
+ if (token.name === 'color' && typeof token.value !== 'string') {
323
+ return createMessageParseError('Color requires a value.');
324
+ }
325
+ }
326
+
327
+ return null;
328
+ }
329
+
330
+ /**
331
+ * @param {CliOptionValues} parsed_values
332
+ * @returns {CliParseError | null}
333
+ */
334
+ function findOutputModeConflict(parsed_values) {
335
+ if (parsed_values.plain && parsed_values.json) {
336
+ return createMessageParseError(
337
+ 'Output mode accepts at most one of "--plain" or "--json".',
338
+ );
339
+ }
340
+
341
+ return null;
342
+ }
343
+
344
+ /**
345
+ * @param {CliCommandName} command_name
346
+ * @param {CliOptionValues} parsed_values
347
+ * @param {string[]} command_positionals
348
+ * @returns {CliParseError | null}
349
+ */
350
+ function findInvalidQueryMode(
351
+ command_name,
352
+ parsed_values,
353
+ command_positionals,
354
+ ) {
355
+ if (
356
+ command_name === 'query' &&
357
+ parsed_values.where !== undefined &&
358
+ command_positionals.length > 0
359
+ ) {
360
+ return createMessageParseError(
361
+ 'Query accepts either "--where" or a stored query name.',
362
+ );
363
+ }
364
+
365
+ return null;
366
+ }
367
+
368
+ /**
369
+ * @param {CliCommandName} command_name
370
+ * @param {CliOptionValues} parsed_values
371
+ * @returns {CliParseError | null}
372
+ */
373
+ function findInvalidQueryInspection(command_name, parsed_values) {
374
+ if (command_name !== 'query') {
375
+ return null;
376
+ }
377
+
378
+ if (parsed_values.explain && parsed_values.lint) {
379
+ return createMessageParseError(
380
+ 'Query accepts at most one of "--explain" or "--lint".',
381
+ );
382
+ }
383
+
384
+ return null;
385
+ }
386
+
387
+ /**
388
+ * @param {CliCommandName} command_name
389
+ * @param {string[]} command_positionals
390
+ * @returns {CliParseError | null}
391
+ */
392
+ function validateCommandPositionals(command_name, command_positionals) {
393
+ const command_definition = getCommandDefinition(command_name);
394
+
395
+ if (command_positionals.length < command_definition.min_positionals) {
396
+ if (command_name === 'query' && command_definition.missing_argument_label) {
397
+ return {
398
+ argument_label: command_definition.missing_argument_label,
399
+ code: 'missing_required_argument',
400
+ command_name: 'query',
401
+ };
402
+ }
403
+
404
+ if (command_name === 'show' && command_definition.missing_argument_label) {
405
+ return {
406
+ argument_label: command_definition.missing_argument_label,
407
+ code: 'missing_required_argument',
408
+ command_name: 'show',
409
+ };
410
+ }
411
+
412
+ return createMessageParseError(
413
+ command_definition.extra_positionals_message,
414
+ );
415
+ }
416
+
417
+ if (command_positionals.length > command_definition.max_positionals) {
418
+ return createMessageParseError(
419
+ command_definition.extra_positionals_message,
420
+ );
421
+ }
422
+
423
+ if (command_name === 'query' && command_positionals.length === 0) {
424
+ return {
425
+ argument_label: '<name> or --where "<clause>"',
426
+ code: 'missing_required_argument',
427
+ command_name: 'query',
428
+ };
429
+ }
430
+
431
+ return null;
432
+ }
433
+
434
+ /**
435
+ * @param {string} option_name
436
+ * @returns {boolean}
437
+ */
438
+ function isKnownCommandOptionName(option_name) {
439
+ return (
440
+ option_name === 'explain' ||
441
+ option_name === 'limit' ||
442
+ option_name === 'lint' ||
443
+ option_name === 'offset' ||
444
+ option_name === 'where'
445
+ );
446
+ }
@@ -0,0 +1,266 @@
1
+ /* eslint-disable max-lines-per-function */
2
+ /**
3
+ * @typedef {import('./parse-cli-arguments-helpers.js').CliOptionValues} CliOptionValues
4
+ * @typedef {import('./parse-cli-arguments-helpers.js').ParsedCommandLine} ParsedCommandLine
5
+ * @typedef {import('./parse-cli-arguments.types.ts').CliParseError} CliParseError
6
+ * @typedef {import('./parse-cli-arguments.types.ts').ParseCliArgumentsResult} ParseCliArgumentsResult
7
+ */
8
+
9
+ import { parseArgs } from 'node:util';
10
+
11
+ import {
12
+ CLI_OPTIONS,
13
+ buildCommandArguments,
14
+ collectOptionTokens,
15
+ createCommandHelpRequest,
16
+ createMessageParseError,
17
+ createNamedHelpRequest,
18
+ createRootHelpRequest,
19
+ createUnknownCommandError,
20
+ createUnknownHelpTargetError,
21
+ resolveOutputMode,
22
+ validateCommandLineBeforeHelp,
23
+ validateHelpCommandLine,
24
+ validateParsedCommand,
25
+ validateRootCommandLine,
26
+ } from './parse-cli-arguments-helpers.js';
27
+ import { isCommandName } from './cli-help-metadata.js';
28
+ import { resolveColorMode } from './parse-cli-color-options.js';
29
+ import { buildQueryPagination } from './parse-cli-query-pagination.js';
30
+
31
+ /**
32
+ * CLI argument parsing.
33
+ *
34
+ * Normalizes raw argv into one validated Patram command plus shared output and
35
+ * pagination options.
36
+ *
37
+ * Kind: cli
38
+ * Status: active
39
+ * Tracked in: ../docs/plans/v0/source-anchor-dogfooding.md
40
+ * Decided by: ../docs/decisions/cli-argument-parser.md
41
+ * @patram
42
+ * @see {@link ./patram-cli.js}
43
+ * @see {@link ../docs/decisions/cli-argument-parser.md}
44
+ */
45
+
46
+ /**
47
+ * Parse the CLI arguments into one validated command result.
48
+ *
49
+ * @param {string[]} cli_arguments
50
+ * @returns {ParseCliArgumentsResult}
51
+ */
52
+ export function parseCliArguments(cli_arguments) {
53
+ const command_line = parseCommandLine(cli_arguments);
54
+
55
+ if (!command_line.success) {
56
+ return command_line;
57
+ }
58
+
59
+ const root_request = resolveRootHelpRequest(command_line.value);
60
+
61
+ if (root_request) {
62
+ return root_request;
63
+ }
64
+
65
+ const help_request = resolveNamedHelpRequest(command_line.value);
66
+
67
+ if (help_request) {
68
+ return help_request;
69
+ }
70
+
71
+ const command_name = command_line.value.positionals[0];
72
+
73
+ if (!isCommandName(command_name)) {
74
+ return {
75
+ error: createUnknownCommandError(command_name),
76
+ success: false,
77
+ };
78
+ }
79
+
80
+ const pre_help_error = validateCommandLineBeforeHelp(
81
+ command_name,
82
+ command_line.value,
83
+ );
84
+
85
+ if (pre_help_error) {
86
+ return {
87
+ error: pre_help_error,
88
+ success: false,
89
+ };
90
+ }
91
+
92
+ if (command_line.value.values.help) {
93
+ return {
94
+ success: true,
95
+ value: createCommandHelpRequest(command_name),
96
+ };
97
+ }
98
+
99
+ const validation_error = validateParsedCommand(
100
+ command_name,
101
+ command_line.value,
102
+ );
103
+
104
+ if (validation_error) {
105
+ return {
106
+ error: validation_error,
107
+ success: false,
108
+ };
109
+ }
110
+
111
+ const command_positionals = command_line.value.positionals.slice(1);
112
+
113
+ return {
114
+ success: true,
115
+ value: {
116
+ kind: 'command',
117
+ color_mode: resolveColorMode(command_line.value.option_tokens),
118
+ command_arguments: buildCommandArguments(
119
+ command_name,
120
+ command_positionals,
121
+ command_line.value.values,
122
+ ),
123
+ command_name,
124
+ output_mode: resolveOutputMode(command_line.value.values),
125
+ ...buildQueryInspection(command_line.value),
126
+ ...buildQueryPagination(command_line.value.values),
127
+ },
128
+ };
129
+ }
130
+
131
+ /**
132
+ * @param {string[]} cli_arguments
133
+ * @returns {{ success: true, value: ParsedCommandLine } | { error: CliParseError, success: false }}
134
+ */
135
+ function parseCommandLine(cli_arguments) {
136
+ try {
137
+ const parsed_arguments = parseArgs({
138
+ allowPositionals: true,
139
+ args: cli_arguments,
140
+ options: CLI_OPTIONS,
141
+ strict: false,
142
+ tokens: true,
143
+ });
144
+ const parsed_values = /** @type {CliOptionValues} */ (
145
+ parsed_arguments.values
146
+ );
147
+
148
+ return {
149
+ success: true,
150
+ value: {
151
+ option_tokens: collectOptionTokens(parsed_arguments.tokens ?? []),
152
+ positionals: parsed_arguments.positionals,
153
+ values: parsed_values,
154
+ },
155
+ };
156
+ } catch (error) {
157
+ if (error instanceof Error) {
158
+ return {
159
+ error: /** @type {CliParseError} */ (
160
+ createMessageParseError(error.message)
161
+ ),
162
+ success: false,
163
+ };
164
+ }
165
+
166
+ throw error;
167
+ }
168
+ }
169
+
170
+ /**
171
+ * @param {ParsedCommandLine} command_line
172
+ * @returns {ParseCliArgumentsResult | null}
173
+ */
174
+ function resolveNamedHelpRequest(command_line) {
175
+ if (command_line.positionals[0] !== 'help') {
176
+ return null;
177
+ }
178
+
179
+ const validation_error = validateHelpCommandLine(command_line);
180
+
181
+ if (validation_error) {
182
+ return {
183
+ error: validation_error,
184
+ success: false,
185
+ };
186
+ }
187
+
188
+ const help_target = command_line.positionals[1];
189
+
190
+ if (!help_target) {
191
+ return {
192
+ success: true,
193
+ value: createRootHelpRequest(),
194
+ };
195
+ }
196
+
197
+ const help_request = createNamedHelpRequest(help_target);
198
+
199
+ if (help_request) {
200
+ return {
201
+ success: true,
202
+ value: help_request,
203
+ };
204
+ }
205
+
206
+ return {
207
+ error: createUnknownHelpTargetError(help_target),
208
+ success: false,
209
+ };
210
+ }
211
+
212
+ /**
213
+ * @param {ParsedCommandLine} command_line
214
+ * @returns {ParseCliArgumentsResult | null}
215
+ */
216
+ function resolveRootHelpRequest(command_line) {
217
+ if (command_line.positionals.length > 0) {
218
+ return null;
219
+ }
220
+
221
+ const validation_error = validateRootCommandLine(command_line);
222
+
223
+ if (validation_error) {
224
+ return {
225
+ error: validation_error,
226
+ success: false,
227
+ };
228
+ }
229
+
230
+ return {
231
+ success: true,
232
+ value: createRootHelpRequest(),
233
+ };
234
+ }
235
+
236
+ /**
237
+ * @param {ParsedCommandLine} command_line
238
+ * @returns {'explain' | 'lint' | undefined}
239
+ */
240
+ function resolveQueryInspectionMode(command_line) {
241
+ if (command_line.values.explain) {
242
+ return 'explain';
243
+ }
244
+
245
+ if (command_line.values.lint) {
246
+ return 'lint';
247
+ }
248
+
249
+ return undefined;
250
+ }
251
+
252
+ /**
253
+ * @param {ParsedCommandLine} command_line
254
+ * @returns {{ query_inspection_mode?: 'explain' | 'lint' }}
255
+ */
256
+ function buildQueryInspection(command_line) {
257
+ const query_inspection_mode = resolveQueryInspectionMode(command_line);
258
+
259
+ if (!query_inspection_mode) {
260
+ return {};
261
+ }
262
+
263
+ return {
264
+ query_inspection_mode,
265
+ };
266
+ }