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
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* @typedef {import('./arguments.types.ts').CliParseError} CliParseError
|
|
6
6
|
* @typedef {import('./arguments.types.ts').ParsedCliHelpRequest} ParsedCliHelpRequest
|
|
7
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
|
|
8
|
+
* @typedef {{ color?: string, desc?: string, explain?: boolean, help?: boolean, json?: boolean, limit?: string, lint?: boolean, name?: string, 'no-color'?: boolean, offset?: string, plain?: boolean, query?: string, where?: string }} CliOptionValues
|
|
9
9
|
* @typedef {{ option_tokens: CliOptionToken[], positionals: string[], values: CliOptionValues }} ParsedCommandLine
|
|
10
10
|
*/
|
|
11
11
|
|
|
@@ -23,14 +23,17 @@ import { findInvalidQueryPagination } from './query-pagination.js';
|
|
|
23
23
|
|
|
24
24
|
export const CLI_OPTIONS = /** @type {const} */ ({
|
|
25
25
|
color: { type: 'string' },
|
|
26
|
+
desc: { type: 'string' },
|
|
26
27
|
explain: { type: 'boolean' },
|
|
27
28
|
help: { type: 'boolean' },
|
|
28
29
|
json: { type: 'boolean' },
|
|
29
30
|
limit: { type: 'string' },
|
|
30
31
|
lint: { type: 'boolean' },
|
|
32
|
+
name: { type: 'string' },
|
|
31
33
|
'no-color': { type: 'boolean' },
|
|
32
34
|
offset: { type: 'string' },
|
|
33
35
|
plain: { type: 'boolean' },
|
|
36
|
+
query: { type: 'string' },
|
|
34
37
|
where: { type: 'string' },
|
|
35
38
|
});
|
|
36
39
|
|
|
@@ -67,9 +70,7 @@ export function validateHelpCommandLine(command_line) {
|
|
|
67
70
|
}
|
|
68
71
|
|
|
69
72
|
if (command_line.positionals.length > 2) {
|
|
70
|
-
return
|
|
71
|
-
'Help accepts at most one topic or command.',
|
|
72
|
-
);
|
|
73
|
+
return createUnexpectedArgumentError('help', command_line.positionals[2]);
|
|
73
74
|
}
|
|
74
75
|
|
|
75
76
|
return null;
|
|
@@ -125,6 +126,11 @@ export function validateParsedCommand(command_name, command_line) {
|
|
|
125
126
|
command_line.values,
|
|
126
127
|
command_positionals,
|
|
127
128
|
) ??
|
|
129
|
+
findInvalidQueriesMutation(
|
|
130
|
+
command_name,
|
|
131
|
+
command_line.values,
|
|
132
|
+
command_positionals,
|
|
133
|
+
) ??
|
|
128
134
|
validateCommandPositionals(command_name, command_positionals)
|
|
129
135
|
);
|
|
130
136
|
}
|
|
@@ -164,6 +170,25 @@ export function buildCommandArguments(
|
|
|
164
170
|
return [command_positionals[0], '--where', parsed_values.where];
|
|
165
171
|
}
|
|
166
172
|
|
|
173
|
+
if (command_name === 'queries') {
|
|
174
|
+
/** @type {string[]} */
|
|
175
|
+
const command_arguments = [...command_positionals];
|
|
176
|
+
|
|
177
|
+
if (parsed_values.name !== undefined) {
|
|
178
|
+
command_arguments.push('--name', parsed_values.name);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (parsed_values.query !== undefined) {
|
|
182
|
+
command_arguments.push('--query', parsed_values.query);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (parsed_values.desc !== undefined) {
|
|
186
|
+
command_arguments.push('--desc', parsed_values.desc);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return command_arguments;
|
|
190
|
+
}
|
|
191
|
+
|
|
167
192
|
return command_positionals;
|
|
168
193
|
}
|
|
169
194
|
|
|
@@ -307,24 +332,10 @@ function findInvalidCommandOption(command_name, option_tokens) {
|
|
|
307
332
|
*/
|
|
308
333
|
function findMissingOptionValue(option_tokens) {
|
|
309
334
|
for (const token of option_tokens) {
|
|
310
|
-
|
|
311
|
-
return {
|
|
312
|
-
argument_label: "<name> or --where '<clause>'",
|
|
313
|
-
code: 'missing_required_argument',
|
|
314
|
-
command_name: 'query',
|
|
315
|
-
};
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
if (token.name === 'offset' && typeof token.value !== 'string') {
|
|
319
|
-
return createMessageParseError('Offset requires a value.');
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
if (token.name === 'limit' && typeof token.value !== 'string') {
|
|
323
|
-
return createMessageParseError('Limit requires a value.');
|
|
324
|
-
}
|
|
335
|
+
const missing_value_error = findMissingOptionValueError(token);
|
|
325
336
|
|
|
326
|
-
if (
|
|
327
|
-
return
|
|
337
|
+
if (missing_value_error) {
|
|
338
|
+
return missing_value_error;
|
|
328
339
|
}
|
|
329
340
|
}
|
|
330
341
|
|
|
@@ -388,6 +399,42 @@ function findInvalidQueryInspection(command_name, parsed_values) {
|
|
|
388
399
|
return null;
|
|
389
400
|
}
|
|
390
401
|
|
|
402
|
+
/**
|
|
403
|
+
* @param {CliCommandName} command_name
|
|
404
|
+
* @param {CliOptionValues} parsed_values
|
|
405
|
+
* @param {string[]} command_positionals
|
|
406
|
+
* @returns {CliParseError | null}
|
|
407
|
+
*/
|
|
408
|
+
function findInvalidQueriesMutation(
|
|
409
|
+
command_name,
|
|
410
|
+
parsed_values,
|
|
411
|
+
command_positionals,
|
|
412
|
+
) {
|
|
413
|
+
if (command_name !== 'queries' || command_positionals.length === 0) {
|
|
414
|
+
return null;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
const subcommand_name = command_positionals[0];
|
|
418
|
+
|
|
419
|
+
if (
|
|
420
|
+
subcommand_name !== 'add' &&
|
|
421
|
+
subcommand_name !== 'remove' &&
|
|
422
|
+
subcommand_name !== 'update'
|
|
423
|
+
) {
|
|
424
|
+
return createUnexpectedArgumentError('queries', subcommand_name);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
if (subcommand_name === 'add') {
|
|
428
|
+
return validateQueriesAddMutation(parsed_values, command_positionals);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
if (subcommand_name === 'remove') {
|
|
432
|
+
return validateQueriesRemoveMutation(parsed_values, command_positionals);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
return validateQueriesUpdateMutation(parsed_values, command_positionals);
|
|
436
|
+
}
|
|
437
|
+
|
|
391
438
|
/**
|
|
392
439
|
* @param {CliCommandName} command_name
|
|
393
440
|
* @param {string[]} command_positionals
|
|
@@ -396,24 +443,18 @@ function findInvalidQueryInspection(command_name, parsed_values) {
|
|
|
396
443
|
function validateCommandPositionals(command_name, command_positionals) {
|
|
397
444
|
const command_definition = getCommandDefinition(command_name);
|
|
398
445
|
|
|
446
|
+
if (allowsQueriesMutationPositionals(command_name, command_positionals)) {
|
|
447
|
+
return null;
|
|
448
|
+
}
|
|
449
|
+
|
|
399
450
|
if (command_positionals.length < command_definition.min_positionals) {
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
command_name: 'query',
|
|
405
|
-
};
|
|
406
|
-
}
|
|
451
|
+
const missing_argument_error = createMissingArgumentError(
|
|
452
|
+
command_name,
|
|
453
|
+
command_definition.missing_argument_label,
|
|
454
|
+
);
|
|
407
455
|
|
|
408
|
-
if (
|
|
409
|
-
|
|
410
|
-
command_definition.missing_argument_label
|
|
411
|
-
) {
|
|
412
|
-
return {
|
|
413
|
-
argument_label: command_definition.missing_argument_label,
|
|
414
|
-
code: 'missing_required_argument',
|
|
415
|
-
command_name,
|
|
416
|
-
};
|
|
456
|
+
if (missing_argument_error) {
|
|
457
|
+
return missing_argument_error;
|
|
417
458
|
}
|
|
418
459
|
|
|
419
460
|
return createMessageParseError(
|
|
@@ -422,8 +463,9 @@ function validateCommandPositionals(command_name, command_positionals) {
|
|
|
422
463
|
}
|
|
423
464
|
|
|
424
465
|
if (command_positionals.length > command_definition.max_positionals) {
|
|
425
|
-
return
|
|
426
|
-
|
|
466
|
+
return createUnexpectedArgumentError(
|
|
467
|
+
command_name,
|
|
468
|
+
command_positionals[command_definition.max_positionals],
|
|
427
469
|
);
|
|
428
470
|
}
|
|
429
471
|
|
|
@@ -444,10 +486,224 @@ function validateCommandPositionals(command_name, command_positionals) {
|
|
|
444
486
|
*/
|
|
445
487
|
function isKnownCommandOptionName(option_name) {
|
|
446
488
|
return (
|
|
489
|
+
option_name === 'desc' ||
|
|
447
490
|
option_name === 'explain' ||
|
|
448
491
|
option_name === 'limit' ||
|
|
449
492
|
option_name === 'lint' ||
|
|
493
|
+
option_name === 'name' ||
|
|
450
494
|
option_name === 'offset' ||
|
|
495
|
+
option_name === 'query' ||
|
|
451
496
|
option_name === 'where'
|
|
452
497
|
);
|
|
453
498
|
}
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* @param {CliOptionToken} option_token
|
|
502
|
+
* @returns {CliParseError | null}
|
|
503
|
+
*/
|
|
504
|
+
function findMissingOptionValueError(option_token) {
|
|
505
|
+
if (typeof option_token.value === 'string') {
|
|
506
|
+
return null;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
if (option_token.name === 'where') {
|
|
510
|
+
return {
|
|
511
|
+
argument_label: "<name> or --where '<clause>'",
|
|
512
|
+
code: 'missing_required_argument',
|
|
513
|
+
command_name: 'query',
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
const message = getMissingOptionValueMessage(option_token.name);
|
|
518
|
+
|
|
519
|
+
if (!message) {
|
|
520
|
+
return null;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
return createMessageParseError(message);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* @param {string | undefined} option_name
|
|
528
|
+
* @returns {string | null}
|
|
529
|
+
*/
|
|
530
|
+
function getMissingOptionValueMessage(option_name) {
|
|
531
|
+
/** @type {Record<string, string>} */
|
|
532
|
+
const option_messages = {
|
|
533
|
+
color: 'Color requires a value.',
|
|
534
|
+
desc: 'Desc requires a value.',
|
|
535
|
+
limit: 'Limit requires a value.',
|
|
536
|
+
name: 'Name requires a value.',
|
|
537
|
+
offset: 'Offset requires a value.',
|
|
538
|
+
query: 'Query requires a value.',
|
|
539
|
+
};
|
|
540
|
+
|
|
541
|
+
if (!option_name || !Object.hasOwn(option_messages, option_name)) {
|
|
542
|
+
return null;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
return option_messages[option_name];
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* @param {CliOptionValues} parsed_values
|
|
550
|
+
* @param {string[]} command_positionals
|
|
551
|
+
* @returns {CliParseError | null}
|
|
552
|
+
*/
|
|
553
|
+
function validateQueriesAddMutation(parsed_values, command_positionals) {
|
|
554
|
+
const positional_error = validateQueriesMutationPositionals(
|
|
555
|
+
command_positionals,
|
|
556
|
+
'Queries add requires a stored query name.',
|
|
557
|
+
);
|
|
558
|
+
|
|
559
|
+
if (positional_error) {
|
|
560
|
+
return positional_error;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
if (parsed_values.name !== undefined) {
|
|
564
|
+
return createMessageParseError('Queries add does not accept "--name".');
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
if (parsed_values.query === undefined) {
|
|
568
|
+
return createMessageParseError('Queries add requires "--query <clause>".');
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
return null;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* @param {CliOptionValues} parsed_values
|
|
576
|
+
* @param {string[]} command_positionals
|
|
577
|
+
* @returns {CliParseError | null}
|
|
578
|
+
*/
|
|
579
|
+
function validateQueriesRemoveMutation(parsed_values, command_positionals) {
|
|
580
|
+
const positional_error = validateQueriesMutationPositionals(
|
|
581
|
+
command_positionals,
|
|
582
|
+
'Queries remove requires a stored query name.',
|
|
583
|
+
);
|
|
584
|
+
|
|
585
|
+
if (positional_error) {
|
|
586
|
+
return positional_error;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
if (
|
|
590
|
+
parsed_values.desc !== undefined ||
|
|
591
|
+
parsed_values.name !== undefined ||
|
|
592
|
+
parsed_values.query !== undefined
|
|
593
|
+
) {
|
|
594
|
+
return createMessageParseError(
|
|
595
|
+
'Queries remove does not accept mutation options.',
|
|
596
|
+
);
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
return null;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
/**
|
|
603
|
+
* @param {CliOptionValues} parsed_values
|
|
604
|
+
* @param {string[]} command_positionals
|
|
605
|
+
* @returns {CliParseError | null}
|
|
606
|
+
*/
|
|
607
|
+
function validateQueriesUpdateMutation(parsed_values, command_positionals) {
|
|
608
|
+
const positional_error = validateQueriesMutationPositionals(
|
|
609
|
+
command_positionals,
|
|
610
|
+
'Queries update requires a stored query name.',
|
|
611
|
+
);
|
|
612
|
+
|
|
613
|
+
if (positional_error) {
|
|
614
|
+
return positional_error;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
if (
|
|
618
|
+
parsed_values.desc === undefined &&
|
|
619
|
+
parsed_values.name === undefined &&
|
|
620
|
+
parsed_values.query === undefined
|
|
621
|
+
) {
|
|
622
|
+
return createMessageParseError(
|
|
623
|
+
'Queries update requires at least one of "--name", "--query", or "--desc".',
|
|
624
|
+
);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
return null;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
/**
|
|
631
|
+
* @param {string[]} command_positionals
|
|
632
|
+
* @param {string} missing_name_message
|
|
633
|
+
* @returns {CliParseError | null}
|
|
634
|
+
*/
|
|
635
|
+
function validateQueriesMutationPositionals(
|
|
636
|
+
command_positionals,
|
|
637
|
+
missing_name_message,
|
|
638
|
+
) {
|
|
639
|
+
if (command_positionals.length < 2) {
|
|
640
|
+
return createMessageParseError(missing_name_message);
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
if (command_positionals.length > 2) {
|
|
644
|
+
return createUnexpectedArgumentError('queries', command_positionals[2]);
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
return null;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
* @param {CliCommandName} command_name
|
|
652
|
+
* @param {string[]} command_positionals
|
|
653
|
+
* @returns {boolean}
|
|
654
|
+
*/
|
|
655
|
+
function allowsQueriesMutationPositionals(command_name, command_positionals) {
|
|
656
|
+
if (command_name !== 'queries' || command_positionals.length === 0) {
|
|
657
|
+
return false;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
const subcommand_name = command_positionals[0];
|
|
661
|
+
|
|
662
|
+
return (
|
|
663
|
+
subcommand_name === 'add' ||
|
|
664
|
+
subcommand_name === 'remove' ||
|
|
665
|
+
subcommand_name === 'update'
|
|
666
|
+
);
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
/**
|
|
670
|
+
* @param {CliCommandName} command_name
|
|
671
|
+
* @param {string | null} missing_argument_label
|
|
672
|
+
* @returns {CliParseError | null}
|
|
673
|
+
*/
|
|
674
|
+
function createMissingArgumentError(command_name, missing_argument_label) {
|
|
675
|
+
if (!missing_argument_label) {
|
|
676
|
+
return null;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
if (command_name === 'query') {
|
|
680
|
+
return {
|
|
681
|
+
argument_label: missing_argument_label,
|
|
682
|
+
code: 'missing_required_argument',
|
|
683
|
+
command_name: 'query',
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
if (command_name === 'refs' || command_name === 'show') {
|
|
688
|
+
return {
|
|
689
|
+
argument_label: missing_argument_label,
|
|
690
|
+
code: 'missing_required_argument',
|
|
691
|
+
command_name,
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
return null;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
/**
|
|
699
|
+
* @param {'help' | CliCommandName} command_name
|
|
700
|
+
* @param {string | undefined} token
|
|
701
|
+
* @returns {CliParseError}
|
|
702
|
+
*/
|
|
703
|
+
function createUnexpectedArgumentError(command_name, token) {
|
|
704
|
+
return {
|
|
705
|
+
code: 'unexpected_argument',
|
|
706
|
+
command_name,
|
|
707
|
+
token: token ?? '',
|
|
708
|
+
};
|
|
709
|
+
}
|
package/lib/cli/render-help.js
CHANGED
|
@@ -75,6 +75,21 @@ export function renderCliParseError(parse_error) {
|
|
|
75
75
|
);
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
if (parse_error.code === 'unexpected_argument') {
|
|
79
|
+
return renderUnexpectedArgumentError(
|
|
80
|
+
parse_error.command_name,
|
|
81
|
+
parse_error.token,
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (parse_error.code === 'unknown_stored_query') {
|
|
86
|
+
return renderUnknownStoredQueryError(
|
|
87
|
+
parse_error.name,
|
|
88
|
+
parse_error.next_path,
|
|
89
|
+
parse_error.suggestion,
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
78
93
|
return `${parse_error.message}\n`;
|
|
79
94
|
}
|
|
80
95
|
|
|
@@ -326,6 +341,54 @@ function renderMissingRequiredArgumentError(command_name, argument_label) {
|
|
|
326
341
|
]);
|
|
327
342
|
}
|
|
328
343
|
|
|
344
|
+
/**
|
|
345
|
+
* @param {'help' | CliCommandName} command_name
|
|
346
|
+
* @param {string} invalid_token
|
|
347
|
+
* @returns {string}
|
|
348
|
+
*/
|
|
349
|
+
function renderUnexpectedArgumentError(command_name, invalid_token) {
|
|
350
|
+
return joinOutputLines([
|
|
351
|
+
`Unexpected argument: ${invalid_token}`,
|
|
352
|
+
'',
|
|
353
|
+
'Usage:',
|
|
354
|
+
...indentLines(getUnexpectedArgumentUsageLines(command_name)),
|
|
355
|
+
'',
|
|
356
|
+
'Next:',
|
|
357
|
+
` ${renderUnexpectedArgumentNext(command_name)}`,
|
|
358
|
+
]);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* @param {string} stored_query_name
|
|
363
|
+
* @param {string | undefined} next_path
|
|
364
|
+
* @param {string | undefined} suggestion
|
|
365
|
+
* @returns {string}
|
|
366
|
+
*/
|
|
367
|
+
function renderUnknownStoredQueryError(
|
|
368
|
+
stored_query_name,
|
|
369
|
+
next_path,
|
|
370
|
+
suggestion,
|
|
371
|
+
) {
|
|
372
|
+
if (suggestion) {
|
|
373
|
+
return joinOutputLines([
|
|
374
|
+
`Unknown stored query: ${stored_query_name}`,
|
|
375
|
+
'',
|
|
376
|
+
'Did you mean:',
|
|
377
|
+
` ${suggestion}`,
|
|
378
|
+
'',
|
|
379
|
+
'Next:',
|
|
380
|
+
` ${next_path ?? `patram query ${suggestion}`}`,
|
|
381
|
+
]);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
return joinOutputLines([
|
|
385
|
+
`Unknown stored query: ${stored_query_name}`,
|
|
386
|
+
'',
|
|
387
|
+
'Next:',
|
|
388
|
+
' patram queries',
|
|
389
|
+
]);
|
|
390
|
+
}
|
|
391
|
+
|
|
329
392
|
/**
|
|
330
393
|
* @param {string} invalid_token
|
|
331
394
|
* @param {CliCommandName | CliHelpTopicName | undefined} suggestion
|
|
@@ -378,6 +441,30 @@ function renderCommandHelpPath(command_name) {
|
|
|
378
441
|
return `patram help ${command_name}`;
|
|
379
442
|
}
|
|
380
443
|
|
|
444
|
+
/**
|
|
445
|
+
* @param {'help' | CliCommandName} command_name
|
|
446
|
+
* @returns {string}
|
|
447
|
+
*/
|
|
448
|
+
function renderUnexpectedArgumentNext(command_name) {
|
|
449
|
+
if (command_name === 'help') {
|
|
450
|
+
return 'patram --help';
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
return renderCommandHelpPath(command_name);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* @param {'help' | CliCommandName} command_name
|
|
458
|
+
* @returns {string[]}
|
|
459
|
+
*/
|
|
460
|
+
function getUnexpectedArgumentUsageLines(command_name) {
|
|
461
|
+
if (command_name === 'help') {
|
|
462
|
+
return ['patram help [command]'];
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
return getCommandDefinition(command_name).usage_lines;
|
|
466
|
+
}
|
|
467
|
+
|
|
381
468
|
/**
|
|
382
469
|
* @param {string[]} lines
|
|
383
470
|
* @returns {string[]}
|
|
@@ -49,6 +49,17 @@
|
|
|
49
49
|
* @returns {Promise<LoadPatramConfigResult>}
|
|
50
50
|
*/
|
|
51
51
|
export function loadPatramConfig(project_directory?: string): Promise<LoadPatramConfigResult>;
|
|
52
|
+
/**
|
|
53
|
+
* @param {string} config_source
|
|
54
|
+
* @returns {{ success: true, value: unknown } | { success: false, diagnostic: PatramDiagnostic }}
|
|
55
|
+
*/
|
|
56
|
+
export function parsePatramConfigSource(config_source: string): {
|
|
57
|
+
success: true;
|
|
58
|
+
value: unknown;
|
|
59
|
+
} | {
|
|
60
|
+
success: false;
|
|
61
|
+
diagnostic: PatramDiagnostic;
|
|
62
|
+
};
|
|
52
63
|
export type PatramDiagnostic = {
|
|
53
64
|
code: string;
|
|
54
65
|
column: number;
|
|
@@ -18,15 +18,9 @@ import { readFile } from 'node:fs/promises';
|
|
|
18
18
|
import { resolve } from 'node:path';
|
|
19
19
|
import process from 'node:process';
|
|
20
20
|
|
|
21
|
-
import { CONFIG_FILE_NAME
|
|
22
|
-
import { createDefaultRepoConfig
|
|
23
|
-
import {
|
|
24
|
-
validateDerivedSummaries,
|
|
25
|
-
validateFieldSchemaConfig,
|
|
26
|
-
validateGraphSchema,
|
|
27
|
-
validateLegacyConfigShape,
|
|
28
|
-
validateStoredQueries,
|
|
29
|
-
} from './validation.js';
|
|
21
|
+
import { CONFIG_FILE_NAME } from './schema.js';
|
|
22
|
+
import { createDefaultRepoConfig } from './defaults.js';
|
|
23
|
+
import { validatePatramConfigValue } from './validate-patram-config-value.js';
|
|
30
24
|
|
|
31
25
|
/**
|
|
32
26
|
* Repo config loading.
|
|
@@ -90,56 +84,19 @@ export async function loadPatramConfig(project_directory = process.cwd()) {
|
|
|
90
84
|
return createLoadResult(createDefaultRepoConfig(), []);
|
|
91
85
|
}
|
|
92
86
|
|
|
93
|
-
const parse_result =
|
|
87
|
+
const parse_result = parsePatramConfigSource(config_source);
|
|
94
88
|
|
|
95
89
|
if (!parse_result.success) {
|
|
96
90
|
return createLoadResult(null, [parse_result.diagnostic]);
|
|
97
91
|
}
|
|
98
92
|
|
|
99
|
-
const
|
|
100
|
-
parse_result.value,
|
|
101
|
-
);
|
|
93
|
+
const validation_result = validatePatramConfigValue(parse_result.value);
|
|
102
94
|
|
|
103
|
-
if (
|
|
104
|
-
return createLoadResult(null,
|
|
95
|
+
if (!validation_result.success) {
|
|
96
|
+
return createLoadResult(null, validation_result.diagnostics);
|
|
105
97
|
}
|
|
106
98
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
if (!config_result.success) {
|
|
110
|
-
return createLoadResult(
|
|
111
|
-
null,
|
|
112
|
-
config_result.error.issues.map(createValidationDiagnostic),
|
|
113
|
-
);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const graph_schema_diagnostics = validateGraphSchema(config_result.data);
|
|
117
|
-
|
|
118
|
-
if (graph_schema_diagnostics.length > 0) {
|
|
119
|
-
return createLoadResult(null, graph_schema_diagnostics);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const normalized_config = normalizeRepoConfig(config_result.data);
|
|
123
|
-
const field_schema_diagnostics = validateFieldSchemaConfig(normalized_config);
|
|
124
|
-
|
|
125
|
-
if (field_schema_diagnostics.length > 0) {
|
|
126
|
-
return createLoadResult(null, field_schema_diagnostics);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const stored_query_diagnostics = validateStoredQueries(normalized_config);
|
|
130
|
-
|
|
131
|
-
if (stored_query_diagnostics.length > 0) {
|
|
132
|
-
return createLoadResult(null, stored_query_diagnostics);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
const derived_summary_diagnostics =
|
|
136
|
-
validateDerivedSummaries(normalized_config);
|
|
137
|
-
|
|
138
|
-
if (derived_summary_diagnostics.length > 0) {
|
|
139
|
-
return createLoadResult(null, derived_summary_diagnostics);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
return createLoadResult(normalized_config, []);
|
|
99
|
+
return createLoadResult(validation_result.config, []);
|
|
143
100
|
}
|
|
144
101
|
|
|
145
102
|
/**
|
|
@@ -162,7 +119,7 @@ async function readConfigSource(config_file_path) {
|
|
|
162
119
|
* @param {string} config_source
|
|
163
120
|
* @returns {{ success: true, value: unknown } | { success: false, diagnostic: PatramDiagnostic }}
|
|
164
121
|
*/
|
|
165
|
-
function
|
|
122
|
+
export function parsePatramConfigSource(config_source) {
|
|
166
123
|
try {
|
|
167
124
|
return {
|
|
168
125
|
success: true,
|
|
@@ -211,34 +168,6 @@ function createInvalidJsonDiagnostic(config_source, error) {
|
|
|
211
168
|
};
|
|
212
169
|
}
|
|
213
170
|
|
|
214
|
-
/**
|
|
215
|
-
* @param {import('zod').core.$ZodIssue} issue
|
|
216
|
-
* @returns {PatramDiagnostic}
|
|
217
|
-
*/
|
|
218
|
-
function createValidationDiagnostic(issue) {
|
|
219
|
-
const issue_path = formatIssuePath(issue.path);
|
|
220
|
-
|
|
221
|
-
if (issue_path) {
|
|
222
|
-
return {
|
|
223
|
-
code: 'config.invalid',
|
|
224
|
-
column: 1,
|
|
225
|
-
level: 'error',
|
|
226
|
-
line: 1,
|
|
227
|
-
message: `Invalid config at "${issue_path}": ${issue.message}`,
|
|
228
|
-
path: CONFIG_FILE_NAME,
|
|
229
|
-
};
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
return {
|
|
233
|
-
code: 'config.invalid',
|
|
234
|
-
column: 1,
|
|
235
|
-
level: 'error',
|
|
236
|
-
line: 1,
|
|
237
|
-
message: `Invalid config: ${issue.message}`,
|
|
238
|
-
path: CONFIG_FILE_NAME,
|
|
239
|
-
};
|
|
240
|
-
}
|
|
241
|
-
|
|
242
171
|
/**
|
|
243
172
|
* @param {unknown} error
|
|
244
173
|
* @returns {error is NodeJS.ErrnoException}
|
|
@@ -281,14 +210,6 @@ function getJsonSyntaxOrigin(config_source, error_message) {
|
|
|
281
210
|
};
|
|
282
211
|
}
|
|
283
212
|
|
|
284
|
-
/**
|
|
285
|
-
* @param {(string | number | symbol | undefined)[]} issue_path
|
|
286
|
-
* @returns {string}
|
|
287
|
-
*/
|
|
288
|
-
function formatIssuePath(issue_path) {
|
|
289
|
-
return issue_path.map(String).join('.');
|
|
290
|
-
}
|
|
291
|
-
|
|
292
213
|
/**
|
|
293
214
|
* @param {string} source_text
|
|
294
215
|
* @param {number} offset
|