patram 0.8.0 → 0.10.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 (143) hide show
  1. package/bin/patram.js +2 -2
  2. package/lib/cli/arguments.types.d.ts +63 -0
  3. package/lib/{parse-cli-color-options.js → cli/color-options.js} +2 -2
  4. package/lib/cli/command-helpers.js +35 -0
  5. package/lib/cli/commands/check.js +73 -0
  6. package/lib/cli/commands/fields.js +57 -0
  7. package/lib/cli/commands/queries.js +41 -0
  8. package/lib/cli/commands/query.js +242 -0
  9. package/lib/cli/commands/refs.js +72 -0
  10. package/lib/cli/commands/show.js +58 -0
  11. package/lib/{cli-help-metadata.js → cli/help-metadata.js} +7 -102
  12. package/lib/cli/main.js +76 -0
  13. package/lib/{parse-cli-arguments-helpers.js → cli/parse-arguments-helpers.js} +24 -12
  14. package/lib/{parse-cli-arguments.js → cli/parse-arguments.js} +12 -12
  15. package/lib/{parse-cli-query-pagination.js → cli/query-pagination.js} +2 -2
  16. package/lib/{render-cli-help.js → cli/render-help.js} +84 -3
  17. package/lib/{resolve-output-mode.js → cli/resolve-output-mode.js} +2 -2
  18. package/lib/cli/test-helpers.js +30 -0
  19. package/lib/config/defaults.d.ts +10 -0
  20. package/lib/config/defaults.js +80 -0
  21. package/lib/config/load-patram-config.d.ts +76 -0
  22. package/lib/config/load-patram-config.js +315 -0
  23. package/lib/config/load-patram-config.types.d.ts +45 -0
  24. package/lib/{patram-config.d.ts → config/patram-config.d.ts} +31 -31
  25. package/lib/{patram-config.js → config/patram-config.js} +3 -3
  26. package/lib/{patram-config.types.d.ts → config/patram-config.types.d.ts} +1 -1
  27. package/lib/{resolve-patram-graph-config.d.ts → config/resolve-patram-graph-config.d.ts} +2 -2
  28. package/lib/{resolve-patram-graph-config.js → config/resolve-patram-graph-config.js} +3 -3
  29. package/lib/{load-patram-config.d.ts → config/schema.d.ts} +149 -191
  30. package/lib/config/schema.js +328 -0
  31. package/lib/{source-file-defaults.d.ts → config/source-file-defaults.d.ts} +0 -1
  32. package/lib/{source-file-defaults.js → config/source-file-defaults.js} +1 -1
  33. package/lib/config/validation.d.ts +27 -0
  34. package/lib/config/validation.js +615 -0
  35. package/lib/directive-validation-test-helpers.js +1 -1
  36. package/lib/find-close-match.d.ts +8 -0
  37. package/lib/find-close-match.js +98 -0
  38. package/lib/{build-graph-identity.d.ts → graph/build-graph-identity.d.ts} +2 -2
  39. package/lib/{build-graph-identity.js → graph/build-graph-identity.js} +1 -1
  40. package/lib/{build-graph.d.ts → graph/build-graph.d.ts} +3 -3
  41. package/lib/{build-graph.js → graph/build-graph.js} +17 -13
  42. package/lib/{build-graph.types.d.ts → graph/build-graph.types.d.ts} +1 -1
  43. package/lib/graph/check-directive-metadata.d.ts +23 -0
  44. package/lib/{check-directive-metadata.js → graph/check-directive-metadata.js} +7 -7
  45. package/lib/graph/check-directive-path-target.d.ts +32 -0
  46. package/lib/{check-directive-path-target.js → graph/check-directive-path-target.js} +4 -4
  47. package/lib/graph/check-directive-value.d.ts +19 -0
  48. package/lib/{check-directive-value.js → graph/check-directive-value.js} +3 -3
  49. package/lib/graph/check-graph.d.ts +29 -0
  50. package/lib/{check-graph.js → graph/check-graph.js} +6 -6
  51. package/lib/graph/directive-diagnostics.d.ts +20 -0
  52. package/lib/{directive-diagnostics.js → graph/directive-diagnostics.js} +2 -2
  53. package/lib/graph/directive-type-rules.d.ts +18 -0
  54. package/lib/{directive-type-rules.js → graph/directive-type-rules.js} +3 -3
  55. package/lib/{document-node-identity.d.ts → graph/document-node-identity.d.ts} +2 -2
  56. package/lib/{document-node-identity.js → graph/document-node-identity.js} +2 -2
  57. package/lib/graph/inspect-reverse-references.d.ts +22 -0
  58. package/lib/{inspect-reverse-references.js → graph/inspect-reverse-references.js} +2 -2
  59. package/lib/{load-project-graph.d.ts → graph/load-project-graph.d.ts} +10 -10
  60. package/lib/{load-project-graph.js → graph/load-project-graph.js} +12 -12
  61. package/lib/{parse-where-clause.types.d.ts → graph/parse-where-clause.types.d.ts} +1 -1
  62. package/lib/{query-graph.d.ts → graph/query/execute.d.ts} +11 -11
  63. package/lib/{query-graph.js → graph/query/execute.js} +12 -12
  64. package/lib/{query-inspection.d.ts → graph/query/inspect.d.ts} +10 -8
  65. package/lib/{query-inspection.js → graph/query/inspect.js} +16 -17
  66. package/lib/{parse-where-clause.d.ts → graph/query/parse.d.ts} +6 -6
  67. package/lib/{parse-where-clause.js → graph/query/parse.js} +2 -2
  68. package/lib/graph/query/resolve.d.ts +28 -0
  69. package/lib/{resolve-where-clause.js → graph/query/resolve.js} +39 -5
  70. package/lib/graph/reverse-reference-test-helpers.d.ts +55 -0
  71. package/lib/{command-output.js → output/command-output.js} +5 -5
  72. package/lib/{derived-summary.js → output/derived-summary.js} +7 -7
  73. package/lib/{layout-incoming-references.js → output/layout-incoming-references.js} +4 -4
  74. package/lib/{layout-incoming-summary-lines.js → output/layout-incoming-summary-lines.js} +0 -5
  75. package/lib/{layout-stored-queries.js → output/layout-stored-queries.js} +27 -11
  76. package/lib/{list-queries.js → output/list-queries.js} +3 -2
  77. package/lib/{render-check-output.js → output/render-check-output.js} +1 -1
  78. package/lib/{render-field-discovery.js → output/render-field-discovery.js} +3 -3
  79. package/lib/output/render-output-view.js +56 -0
  80. package/lib/{render-json-output.js → output/renderers/json.js} +10 -6
  81. package/lib/{render-plain-output.js → output/renderers/plain.js} +34 -33
  82. package/lib/{render-rich-output.js → output/renderers/rich.js} +44 -32
  83. package/lib/{resolve-check-target.js → output/resolve-check-target.js} +1 -1
  84. package/lib/{render-rich-source.js → output/rich-source/render.js} +6 -6
  85. package/lib/{show-document.js → output/show-document.js} +12 -12
  86. package/lib/{render-output-view.js → output/view-model/index.js} +11 -70
  87. package/lib/{write-paged-output.js → output/write-paged-output.js} +9 -5
  88. package/lib/{claim-helpers.d.ts → parse/claim-helpers.d.ts} +2 -2
  89. package/lib/{parse-jsdoc-claims.d.ts → parse/jsdoc/parse-jsdoc-claims.d.ts} +2 -2
  90. package/lib/{parse-jsdoc-claims.js → parse/jsdoc/parse-jsdoc-claims.js} +9 -9
  91. package/lib/{parse-jsdoc-prose.d.ts → parse/jsdoc/parse-jsdoc-prose.d.ts} +1 -1
  92. package/lib/{parse-jsdoc-prose.js → parse/jsdoc/parse-jsdoc-prose.js} +1 -1
  93. package/lib/{parse-markdown-claims.d.ts → parse/markdown/parse-markdown-claims.d.ts} +3 -3
  94. package/lib/{parse-markdown-claims.js → parse/markdown/parse-markdown-claims.js} +8 -8
  95. package/lib/{parse-markdown-directives.d.ts → parse/markdown/parse-markdown-directives.d.ts} +2 -2
  96. package/lib/{parse-markdown-directives.js → parse/markdown/parse-markdown-directives.js} +3 -3
  97. package/lib/{parse-claims.d.ts → parse/parse-claims.d.ts} +4 -13
  98. package/lib/{parse-claims.js → parse/parse-claims.js} +18 -26
  99. package/lib/{parse-claims.types.d.ts → parse/parse-claims.types.d.ts} +1 -1
  100. package/lib/{tagged-fenced-block-error.d.ts → parse/tagged-fenced/tagged-fenced-block-error.d.ts} +2 -2
  101. package/lib/{tagged-fenced-block-parser.d.ts → parse/tagged-fenced/tagged-fenced-block-parser.d.ts} +3 -3
  102. package/lib/{tagged-fenced-blocks.d.ts → parse/tagged-fenced/tagged-fenced-blocks.d.ts} +7 -7
  103. package/lib/{tagged-fenced-blocks.js → parse/tagged-fenced/tagged-fenced-blocks.js} +3 -3
  104. package/lib/{parse-yaml-claims.d.ts → parse/yaml/parse-yaml-claims.d.ts} +4 -4
  105. package/lib/{parse-yaml-claims.js → parse/yaml/parse-yaml-claims.js} +22 -13
  106. package/lib/patram.d.ts +29 -28
  107. package/lib/patram.js +5 -6
  108. package/lib/{discover-fields.js → scan/discover-fields.js} +145 -18
  109. package/lib/scan/list-repo-files.d.ts +16 -0
  110. package/lib/{list-source-files.js → scan/list-repo-files.js} +2 -35
  111. package/lib/{list-source-files.d.ts → scan/list-source-files.d.ts} +4 -11
  112. package/lib/scan/list-source-files.js +45 -0
  113. package/package.json +8 -7
  114. package/lib/build-graph.types.ts +0 -27
  115. package/lib/discover-fields.types.ts +0 -52
  116. package/lib/load-patram-config.js +0 -1215
  117. package/lib/load-patram-config.types.d.ts +0 -45
  118. package/lib/load-patram-config.types.ts +0 -56
  119. package/lib/output-view.types.d.ts +0 -88
  120. package/lib/output-view.types.ts +0 -113
  121. package/lib/overlay-graph.d.ts +0 -43
  122. package/lib/overlay-graph.js +0 -191
  123. package/lib/parse-claims.types.ts +0 -41
  124. package/lib/parse-cli-arguments.types.ts +0 -75
  125. package/lib/parse-where-clause.types.ts +0 -87
  126. package/lib/patram-cli.js +0 -593
  127. package/lib/patram-config.types.ts +0 -22
  128. package/lib/tagged-fenced-blocks.types.ts +0 -38
  129. /package/lib/{reverse-reference-test-helpers.js → graph/reverse-reference-test-helpers.js} +0 -0
  130. /package/lib/{format-derived-summary-row.js → output/format-derived-summary-row.js} +0 -0
  131. /package/lib/{format-node-header.js → output/format-node-header.js} +0 -0
  132. /package/lib/{format-output-item-block.js → output/format-output-item-block.js} +0 -0
  133. /package/lib/{format-output-metadata.js → output/format-output-metadata.js} +0 -0
  134. /package/lib/{claim-helpers.js → parse/claim-helpers.js} +0 -0
  135. /package/lib/{parse-jsdoc-blocks.d.ts → parse/jsdoc/parse-jsdoc-blocks.d.ts} +0 -0
  136. /package/lib/{parse-jsdoc-blocks.js → parse/jsdoc/parse-jsdoc-blocks.js} +0 -0
  137. /package/lib/{tagged-fenced-block-error.js → parse/tagged-fenced/tagged-fenced-block-error.js} +0 -0
  138. /package/lib/{tagged-fenced-block-markdown.d.ts → parse/tagged-fenced/tagged-fenced-block-markdown.d.ts} +0 -0
  139. /package/lib/{tagged-fenced-block-markdown.js → parse/tagged-fenced/tagged-fenced-block-markdown.js} +0 -0
  140. /package/lib/{tagged-fenced-block-metadata.d.ts → parse/tagged-fenced/tagged-fenced-block-metadata.d.ts} +0 -0
  141. /package/lib/{tagged-fenced-block-metadata.js → parse/tagged-fenced/tagged-fenced-block-metadata.js} +0 -0
  142. /package/lib/{tagged-fenced-block-parser.js → parse/tagged-fenced/tagged-fenced-block-parser.js} +0 -0
  143. /package/lib/{tagged-fenced-blocks.types.d.ts → parse/tagged-fenced/tagged-fenced-blocks.types.d.ts} +0 -0
@@ -3,9 +3,11 @@
3
3
  * @import {
4
4
  * CliCommandName,
5
5
  * CliHelpTopicName,
6
- * } from './parse-cli-arguments.types.ts';
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,
@@ -45,7 +47,7 @@
45
47
  * }} CliHelpTopicDefinition
46
48
  */
47
49
 
48
- export const COMMAND_NAMES = /** @type {const} */ ([
50
+ const COMMAND_NAMES = /** @type {const} */ ([
49
51
  'check',
50
52
  'fields',
51
53
  'query',
@@ -54,7 +56,7 @@ export const COMMAND_NAMES = /** @type {const} */ ([
54
56
  'show',
55
57
  ]);
56
58
 
57
- export const HELP_TOPIC_NAMES = /** @type {const} */ (['query-language']);
59
+ const HELP_TOPIC_NAMES = /** @type {const} */ (['query-language']);
58
60
 
59
61
  export const GLOBAL_OPTION_NAMES = new Set([
60
62
  'help',
@@ -284,14 +286,14 @@ const COMMAND_DEFINITIONS = {
284
286
  },
285
287
  show: {
286
288
  allowed_option_names: new Set(),
287
- examples: ['patram show docs/patram.md', 'patram show lib/patram-cli.js'],
289
+ examples: ['patram show docs/patram.md', 'patram show lib/cli/main.js'],
288
290
  extra_positionals_message: 'Show accepts exactly one file path.',
289
291
  help_topics: [],
290
292
  max_positionals: 1,
291
293
  min_positionals: 1,
292
294
  missing_argument_examples: [
293
295
  'patram show docs/patram.md',
294
- 'patram show lib/patram-cli.js',
296
+ 'patram show lib/cli/main.js',
295
297
  ],
296
298
  missing_argument_label: '<file>',
297
299
  missing_usage_lines: ['patram show <file>'],
@@ -532,100 +534,3 @@ function listOptionLabels(command_name) {
532
534
 
533
535
  return [...option_labels];
534
536
  }
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(right_text.length + 1).fill(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
- }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * @import { ParsedCliCommandRequest } from './arguments.types.ts';
3
+ */
4
+ import { renderCliParseError, renderHelpRequest } from './render-help.js';
5
+ import { parseCliArguments } from './parse-arguments.js';
6
+ import { runCheckCommand } from './commands/check.js';
7
+ import { runFieldsCommand } from './commands/fields.js';
8
+ import { runQueryCommand } from './commands/query.js';
9
+ import { runQueriesCommand } from './commands/queries.js';
10
+ import { runRefsCommand } from './commands/refs.js';
11
+ import { runShowCommand } from './commands/show.js';
12
+
13
+ /**
14
+ * Patram command execution flow.
15
+ *
16
+ * Loads repo state and routes `check`, `fields`, `query`, `queries`, and
17
+ * `show` through the shared output pipeline.
18
+ *
19
+ * Kind: cli
20
+ * Status: active
21
+ * Implements Command: ../../docs/reference/commands/check.md
22
+ * Implements Command: ../../docs/reference/commands/query.md
23
+ * Implements Command: ../../docs/reference/commands/queries.md
24
+ * Implements Command: ../../docs/reference/commands/refs.md
25
+ * Implements Command: ../../docs/reference/commands/show.md
26
+ * Tracked in: ../../docs/plans/v0/source-anchor-dogfooding.md
27
+ * Decided by: ../../docs/decisions/cli-output-architecture.md
28
+ * Decided by: ../../docs/decisions/cli-argument-parser.md
29
+ * @patram
30
+ * @see {@link ./parse-arguments.js}
31
+ * @see {@link ./commands/query.js}
32
+ */
33
+
34
+ /**
35
+ * Run the Patram CLI.
36
+ *
37
+ * @param {string[]} cli_arguments
38
+ * @param {{ stderr: { write(chunk: string): boolean }, stdout: { isTTY?: boolean, write(chunk: string): boolean }, write_paged_output?: (output_text: string) => Promise<void> }} io_context
39
+ * @returns {Promise<number>}
40
+ */
41
+ export async function main(cli_arguments, io_context) {
42
+ const parsed_arguments = parseCliArguments(cli_arguments);
43
+
44
+ if (!parsed_arguments.success) {
45
+ io_context.stderr.write(renderCliParseError(parsed_arguments.error));
46
+
47
+ return 1;
48
+ }
49
+
50
+ if (parsed_arguments.value.kind === 'help') {
51
+ io_context.stdout.write(renderHelpRequest(parsed_arguments.value));
52
+
53
+ return 0;
54
+ }
55
+
56
+ const parsed_command = /** @type {ParsedCliCommandRequest} */ (
57
+ parsed_arguments.value
58
+ );
59
+ const commandHandler = COMMAND_HANDLERS[parsed_command.command_name];
60
+
61
+ if (commandHandler) {
62
+ return commandHandler(parsed_command, io_context);
63
+ }
64
+
65
+ io_context.stderr.write('Unknown command.\n');
66
+
67
+ return 1;
68
+ }
69
+ const COMMAND_HANDLERS = {
70
+ check: runCheckCommand,
71
+ fields: runFieldsCommand,
72
+ queries: runQueriesCommand,
73
+ query: runQueryCommand,
74
+ refs: runRefsCommand,
75
+ show: runShowCommand,
76
+ };
@@ -1,9 +1,9 @@
1
1
  /* eslint-disable max-lines */
2
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
3
+ * @typedef {import('./arguments.types.ts').CliCommandName} CliCommandName
4
+ * @typedef {import('./arguments.types.ts').CliOutputMode} CliOutputMode
5
+ * @typedef {import('./arguments.types.ts').CliParseError} CliParseError
6
+ * @typedef {import('./arguments.types.ts').ParsedCliHelpRequest} ParsedCliHelpRequest
7
7
  * @typedef {{ kind: string, name?: string, rawName?: string, value?: string | boolean }} CliOptionToken
8
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
9
  * @typedef {{ option_tokens: CliOptionToken[], positionals: string[], values: CliOptionValues }} ParsedCommandLine
@@ -17,9 +17,9 @@ import {
17
17
  GLOBAL_OPTION_NAMES,
18
18
  isCommandName,
19
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';
20
+ } from './help-metadata.js';
21
+ import { findInvalidColorMode } from './color-options.js';
22
+ import { findInvalidQueryPagination } from './query-pagination.js';
23
23
 
24
24
  export const CLI_OPTIONS = /** @type {const} */ ({
25
25
  color: { type: 'string' },
@@ -67,9 +67,7 @@ export function validateHelpCommandLine(command_line) {
67
67
  }
68
68
 
69
69
  if (command_line.positionals.length > 2) {
70
- return createMessageParseError(
71
- 'Help accepts at most one topic or command.',
72
- );
70
+ return createUnexpectedArgumentError('help', command_line.positionals[2]);
73
71
  }
74
72
 
75
73
  return null;
@@ -422,8 +420,9 @@ function validateCommandPositionals(command_name, command_positionals) {
422
420
  }
423
421
 
424
422
  if (command_positionals.length > command_definition.max_positionals) {
425
- return createMessageParseError(
426
- command_definition.extra_positionals_message,
423
+ return createUnexpectedArgumentError(
424
+ command_name,
425
+ command_positionals[command_definition.max_positionals],
427
426
  );
428
427
  }
429
428
 
@@ -451,3 +450,16 @@ function isKnownCommandOptionName(option_name) {
451
450
  option_name === 'where'
452
451
  );
453
452
  }
453
+
454
+ /**
455
+ * @param {'help' | CliCommandName} command_name
456
+ * @param {string | undefined} token
457
+ * @returns {CliParseError}
458
+ */
459
+ function createUnexpectedArgumentError(command_name, token) {
460
+ return {
461
+ code: 'unexpected_argument',
462
+ command_name,
463
+ token: token ?? '',
464
+ };
465
+ }
@@ -1,9 +1,9 @@
1
1
  /* eslint-disable max-lines-per-function */
2
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
3
+ * @typedef {import('./parse-arguments-helpers.js').CliOptionValues} CliOptionValues
4
+ * @typedef {import('./parse-arguments-helpers.js').ParsedCommandLine} ParsedCommandLine
5
+ * @typedef {import('./arguments.types.ts').CliParseError} CliParseError
6
+ * @typedef {import('./arguments.types.ts').ParseCliArgumentsResult} ParseCliArgumentsResult
7
7
  */
8
8
 
9
9
  import { parseArgs } from 'node:util';
@@ -23,10 +23,10 @@ import {
23
23
  validateHelpCommandLine,
24
24
  validateParsedCommand,
25
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';
26
+ } from './parse-arguments-helpers.js';
27
+ import { isCommandName } from './help-metadata.js';
28
+ import { resolveColorMode } from './color-options.js';
29
+ import { buildQueryPagination } from './query-pagination.js';
30
30
 
31
31
  /**
32
32
  * CLI argument parsing.
@@ -36,11 +36,11 @@ import { buildQueryPagination } from './parse-cli-query-pagination.js';
36
36
  *
37
37
  * Kind: cli
38
38
  * Status: active
39
- * Tracked in: ../docs/plans/v0/source-anchor-dogfooding.md
40
- * Decided by: ../docs/decisions/cli-argument-parser.md
39
+ * Tracked in: ../../docs/plans/v0/source-anchor-dogfooding.md
40
+ * Decided by: ../../docs/decisions/cli-argument-parser.md
41
41
  * @patram
42
- * @see {@link ./patram-cli.js}
43
- * @see {@link ../docs/decisions/cli-argument-parser.md}
42
+ * @see {@link ./main.js}
43
+ * @see {@link ../../docs/decisions/cli-argument-parser.md}
44
44
  */
45
45
 
46
46
  /**
@@ -1,6 +1,6 @@
1
1
  /**
2
- * @typedef {import('./parse-cli-arguments-helpers.js').CliOptionToken} CliOptionToken
3
- * @typedef {import('./parse-cli-arguments-helpers.js').CliOptionValues} CliOptionValues
2
+ * @typedef {import('./parse-arguments-helpers.js').CliOptionToken} CliOptionToken
3
+ * @typedef {import('./parse-arguments-helpers.js').CliOptionValues} CliOptionValues
4
4
  */
5
5
 
6
6
  /**
@@ -5,8 +5,8 @@
5
5
  * CliHelpTopicName,
6
6
  * CliParseError,
7
7
  * ParsedCliHelpRequest,
8
- * } from './parse-cli-arguments.types.ts';
9
- * @import { PatramDiagnostic } from './load-patram-config.types.ts';
8
+ * } from './arguments.types.ts';
9
+ * @import { PatramDiagnostic } from '../config/load-patram-config.types.ts';
10
10
  */
11
11
 
12
12
  import {
@@ -15,7 +15,7 @@ import {
15
15
  getRootHelpDefinition,
16
16
  listCommandNames,
17
17
  listHelpTopicNames,
18
- } from './cli-help-metadata.js';
18
+ } from './help-metadata.js';
19
19
 
20
20
  /**
21
21
  * @param {ParsedCliHelpRequest} help_request
@@ -75,6 +75,20 @@ 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.suggestion,
89
+ );
90
+ }
91
+
78
92
  return `${parse_error.message}\n`;
79
93
  }
80
94
 
@@ -326,6 +340,49 @@ function renderMissingRequiredArgumentError(command_name, argument_label) {
326
340
  ]);
327
341
  }
328
342
 
343
+ /**
344
+ * @param {'help' | CliCommandName} command_name
345
+ * @param {string} invalid_token
346
+ * @returns {string}
347
+ */
348
+ function renderUnexpectedArgumentError(command_name, invalid_token) {
349
+ return joinOutputLines([
350
+ `Unexpected argument: ${invalid_token}`,
351
+ '',
352
+ 'Usage:',
353
+ ...indentLines(getUnexpectedArgumentUsageLines(command_name)),
354
+ '',
355
+ 'Next:',
356
+ ` ${renderUnexpectedArgumentNext(command_name)}`,
357
+ ]);
358
+ }
359
+
360
+ /**
361
+ * @param {string} stored_query_name
362
+ * @param {string | undefined} suggestion
363
+ * @returns {string}
364
+ */
365
+ function renderUnknownStoredQueryError(stored_query_name, suggestion) {
366
+ if (suggestion) {
367
+ return joinOutputLines([
368
+ `Unknown stored query: ${stored_query_name}`,
369
+ '',
370
+ 'Did you mean:',
371
+ ` ${suggestion}`,
372
+ '',
373
+ 'Next:',
374
+ ` patram query ${suggestion}`,
375
+ ]);
376
+ }
377
+
378
+ return joinOutputLines([
379
+ `Unknown stored query: ${stored_query_name}`,
380
+ '',
381
+ 'Next:',
382
+ ' patram queries',
383
+ ]);
384
+ }
385
+
329
386
  /**
330
387
  * @param {string} invalid_token
331
388
  * @param {CliCommandName | CliHelpTopicName | undefined} suggestion
@@ -378,6 +435,30 @@ function renderCommandHelpPath(command_name) {
378
435
  return `patram help ${command_name}`;
379
436
  }
380
437
 
438
+ /**
439
+ * @param {'help' | CliCommandName} command_name
440
+ * @returns {string}
441
+ */
442
+ function renderUnexpectedArgumentNext(command_name) {
443
+ if (command_name === 'help') {
444
+ return 'patram --help';
445
+ }
446
+
447
+ return renderCommandHelpPath(command_name);
448
+ }
449
+
450
+ /**
451
+ * @param {'help' | CliCommandName} command_name
452
+ * @returns {string[]}
453
+ */
454
+ function getUnexpectedArgumentUsageLines(command_name) {
455
+ if (command_name === 'help') {
456
+ return ['patram help [command]'];
457
+ }
458
+
459
+ return getCommandDefinition(command_name).usage_lines;
460
+ }
461
+
381
462
  /**
382
463
  * @param {string[]} lines
383
464
  * @returns {string[]}
@@ -1,6 +1,6 @@
1
1
  /**
2
- * @import { ParsedCliArguments } from './parse-cli-arguments.types.ts';
3
- * @import { ResolvedOutputMode } from './output-view.types.ts';
2
+ * @import { ParsedCliArguments } from './arguments.types.ts';
3
+ * @import { ResolvedOutputMode } from '../output/output-view.types.ts';
4
4
  */
5
5
 
6
6
  /**
@@ -0,0 +1,30 @@
1
+ export function createIoContext() {
2
+ /** @type {{ stderr_chunks: string[], stdout_chunks: string[] }} */
3
+ const io_context = {
4
+ stderr_chunks: [],
5
+ stdout_chunks: [],
6
+ };
7
+
8
+ return {
9
+ ...io_context,
10
+ stderr: {
11
+ /**
12
+ * @param {string} chunk
13
+ */
14
+ write(chunk) {
15
+ io_context.stderr_chunks.push(chunk);
16
+ return true;
17
+ },
18
+ },
19
+ stdout: {
20
+ isTTY: false,
21
+ /**
22
+ * @param {string} chunk
23
+ */
24
+ write(chunk) {
25
+ io_context.stdout_chunks.push(chunk);
26
+ return true;
27
+ },
28
+ },
29
+ };
30
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @returns {PatramRepoConfig}
3
+ */
4
+ export function createDefaultRepoConfig(): PatramRepoConfig;
5
+ /**
6
+ * @param {PatramRepoConfig} repo_config
7
+ * @returns {PatramRepoConfig}
8
+ */
9
+ export function normalizeRepoConfig(repo_config: PatramRepoConfig): PatramRepoConfig;
10
+ import type { PatramRepoConfig } from './schema.js';
@@ -0,0 +1,80 @@
1
+ /**
2
+ * @import { PatramRepoConfig } from './schema.js';
3
+ */
4
+
5
+ import { DEFAULT_INCLUDE_PATTERNS } from './source-file-defaults.js';
6
+
7
+ /**
8
+ * @returns {PatramRepoConfig}
9
+ */
10
+ export function createDefaultRepoConfig() {
11
+ return {
12
+ include: [...DEFAULT_INCLUDE_PATTERNS],
13
+ queries: {},
14
+ };
15
+ }
16
+
17
+ /**
18
+ * @param {PatramRepoConfig} repo_config
19
+ * @returns {PatramRepoConfig}
20
+ */
21
+ export function normalizeRepoConfig(repo_config) {
22
+ /** @type {PatramRepoConfig} */
23
+ const normalized_config = {
24
+ include: [...repo_config.include],
25
+ queries: { ...repo_config.queries },
26
+ };
27
+
28
+ assignOptionalRepoConfigField(
29
+ normalized_config,
30
+ 'classes',
31
+ repo_config.classes,
32
+ );
33
+ assignOptionalRepoConfigField(
34
+ normalized_config,
35
+ 'derived_summaries',
36
+ repo_config.derived_summaries,
37
+ );
38
+ assignOptionalRepoConfigField(
39
+ normalized_config,
40
+ 'fields',
41
+ repo_config.fields,
42
+ );
43
+ assignOptionalRepoConfigField(
44
+ normalized_config,
45
+ 'mappings',
46
+ repo_config.mappings,
47
+ );
48
+ assignOptionalRepoConfigField(
49
+ normalized_config,
50
+ 'path_classes',
51
+ repo_config.path_classes,
52
+ );
53
+ assignOptionalRepoConfigField(
54
+ normalized_config,
55
+ 'relations',
56
+ repo_config.relations,
57
+ );
58
+
59
+ return normalized_config;
60
+ }
61
+
62
+ /**
63
+ * @template {Exclude<keyof PatramRepoConfig, 'include' | 'queries'>} TKey
64
+ * @param {PatramRepoConfig} normalized_config
65
+ * @param {TKey} field_name
66
+ * @param {unknown} field_value
67
+ */
68
+ function assignOptionalRepoConfigField(
69
+ normalized_config,
70
+ field_name,
71
+ field_value,
72
+ ) {
73
+ if (field_value === undefined || field_value === null) {
74
+ return;
75
+ }
76
+
77
+ normalized_config[field_name] = /** @type {PatramRepoConfig[TKey]} */ (
78
+ field_value
79
+ );
80
+ }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Repo config loading.
3
+ *
4
+ * Reads `.patram.json`, applies defaults, and validates repo config and graph
5
+ * schema before command execution.
6
+ *
7
+ * Kind: config
8
+ * Status: active
9
+ * Tracked in: ../../docs/plans/v0/source-anchor-dogfooding.md
10
+ * Decided by: ../../docs/decisions/single-config-file.md
11
+ * Decided by: ../../docs/decisions/optional-config-default-scan.md
12
+ * @patram
13
+ * @see {@link ./schema.js}
14
+ * @see {@link ./validation.js}
15
+ */
16
+ /**
17
+ * @typedef {object} PatramDiagnostic
18
+ * @property {string} code
19
+ * @property {number} column
20
+ * @property {'error'} level
21
+ * @property {number} line
22
+ * @property {string} message
23
+ * @property {string} path
24
+ */
25
+ /**
26
+ * @typedef {object} LoadPatramConfigResult
27
+ * @property {import('./schema.js').PatramRepoConfig | null} config
28
+ * @property {string} config_path
29
+ * @property {PatramDiagnostic[]} diagnostics
30
+ */
31
+ /**
32
+ * @typedef {import('./schema.js').StoredQueryConfig} StoredQueryConfig
33
+ * @typedef {import('./schema.js').FieldDisplayConfig} FieldDisplayConfig
34
+ * @typedef {import('./schema.js').FieldQueryConfig} FieldQueryConfig
35
+ * @typedef {import('./schema.js').MetadataFieldConfig} MetadataFieldConfig
36
+ * @typedef {import('./schema.js').ClassFieldRuleConfig} ClassFieldRuleConfig
37
+ * @typedef {import('./schema.js').ClassSchemaConfig} ClassSchemaConfig
38
+ * @typedef {import('./schema.js').PathClassConfig} PathClassConfig
39
+ * @typedef {import('./schema.js').DerivedSummaryScalar} DerivedSummaryScalar
40
+ * @typedef {import('./schema.js').DerivedSummarySelectCaseConfig} DerivedSummarySelectCaseConfig
41
+ * @typedef {import('./schema.js').DerivedSummaryFieldConfig} DerivedSummaryFieldConfig
42
+ * @typedef {import('./schema.js').DerivedSummaryConfig} DerivedSummaryConfig
43
+ * @typedef {import('./schema.js').PatramRepoConfig} PatramRepoConfig
44
+ */
45
+ /**
46
+ * Load and validate the repo Patram config.
47
+ *
48
+ * @param {string} [project_directory]
49
+ * @returns {Promise<LoadPatramConfigResult>}
50
+ */
51
+ export function loadPatramConfig(project_directory?: string): Promise<LoadPatramConfigResult>;
52
+ export type PatramDiagnostic = {
53
+ code: string;
54
+ column: number;
55
+ level: "error";
56
+ line: number;
57
+ message: string;
58
+ path: string;
59
+ };
60
+ export type LoadPatramConfigResult = {
61
+ config: import("./schema.js").PatramRepoConfig | null;
62
+ config_path: string;
63
+ diagnostics: PatramDiagnostic[];
64
+ };
65
+ export type StoredQueryConfig = import("./schema.js").StoredQueryConfig;
66
+ export type FieldDisplayConfig = import("./schema.js").FieldDisplayConfig;
67
+ export type FieldQueryConfig = import("./schema.js").FieldQueryConfig;
68
+ export type MetadataFieldConfig = import("./schema.js").MetadataFieldConfig;
69
+ export type ClassFieldRuleConfig = import("./schema.js").ClassFieldRuleConfig;
70
+ export type ClassSchemaConfig = import("./schema.js").ClassSchemaConfig;
71
+ export type PathClassConfig = import("./schema.js").PathClassConfig;
72
+ export type DerivedSummaryScalar = import("./schema.js").DerivedSummaryScalar;
73
+ export type DerivedSummarySelectCaseConfig = import("./schema.js").DerivedSummarySelectCaseConfig;
74
+ export type DerivedSummaryFieldConfig = import("./schema.js").DerivedSummaryFieldConfig;
75
+ export type DerivedSummaryConfig = import("./schema.js").DerivedSummaryConfig;
76
+ export type PatramRepoConfig = import("./schema.js").PatramRepoConfig;