patram 0.10.0 → 0.12.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 (117) hide show
  1. package/bin/patram.js +4 -4
  2. package/lib/cli/arguments.types.d.ts +1 -0
  3. package/lib/cli/commands/check.js +27 -15
  4. package/lib/cli/commands/fields.js +0 -4
  5. package/lib/cli/commands/queries.js +179 -1
  6. package/lib/cli/commands/query.js +1 -8
  7. package/lib/cli/commands/refs.js +3 -10
  8. package/lib/cli/commands/show.js +1 -8
  9. package/lib/cli/help-metadata.js +106 -111
  10. package/lib/cli/main.js +10 -10
  11. package/lib/cli/parse-arguments-helpers.js +416 -66
  12. package/lib/cli/parse-arguments.js +4 -4
  13. package/lib/cli/render-help.js +10 -4
  14. package/lib/config/defaults.js +33 -25
  15. package/lib/config/load-patram-config.d.ts +19 -33
  16. package/lib/config/load-patram-config.js +18 -121
  17. package/lib/config/load-patram-config.types.d.ts +3 -40
  18. package/lib/config/manage-stored-queries-helpers.d.ts +69 -0
  19. package/lib/config/manage-stored-queries-helpers.js +320 -0
  20. package/lib/config/manage-stored-queries-jsonc.d.ts +31 -0
  21. package/lib/config/manage-stored-queries-jsonc.js +95 -0
  22. package/lib/config/manage-stored-queries.d.ts +77 -0
  23. package/lib/config/manage-stored-queries.js +300 -0
  24. package/lib/config/patram-config.d.ts +34 -34
  25. package/lib/config/patram-config.js +3 -3
  26. package/lib/config/patram-config.types.d.ts +5 -11
  27. package/lib/config/resolve-patram-graph-config.d.ts +5 -1
  28. package/lib/config/resolve-patram-graph-config.js +3 -119
  29. package/lib/config/schema.d.ts +158 -269
  30. package/lib/config/schema.js +72 -210
  31. package/lib/config/validate-patram-config-value.d.ts +13 -0
  32. package/lib/config/validate-patram-config-value.js +94 -0
  33. package/lib/config/validation.d.ts +2 -12
  34. package/lib/config/validation.js +125 -483
  35. package/lib/find-close-match.d.ts +4 -1
  36. package/lib/graph/build-graph-identity.d.ts +1 -32
  37. package/lib/graph/build-graph-identity.js +5 -269
  38. package/lib/graph/build-graph.d.ts +13 -4
  39. package/lib/graph/build-graph.js +347 -488
  40. package/lib/graph/build-graph.types.d.ts +8 -9
  41. package/lib/graph/check-directive-metadata-helpers.d.ts +30 -0
  42. package/lib/graph/check-directive-metadata-helpers.js +126 -0
  43. package/lib/graph/check-directive-metadata.d.ts +8 -9
  44. package/lib/graph/check-directive-metadata.js +70 -561
  45. package/lib/graph/check-directive-path-target.d.ts +6 -13
  46. package/lib/graph/check-directive-path-target.js +26 -57
  47. package/lib/graph/check-directive-value.d.ts +1 -5
  48. package/lib/graph/check-directive-value.js +40 -180
  49. package/lib/graph/check-graph.d.ts +5 -5
  50. package/lib/graph/check-graph.js +8 -6
  51. package/lib/graph/document-node-identity.d.ts +23 -7
  52. package/lib/graph/document-node-identity.js +417 -160
  53. package/lib/graph/graph-node.d.ts +42 -0
  54. package/lib/graph/graph-node.js +83 -0
  55. package/lib/graph/inspect-reverse-references.js +16 -11
  56. package/lib/graph/load-project-graph.d.ts +7 -7
  57. package/lib/graph/load-project-graph.js +7 -7
  58. package/lib/graph/parse-where-clause.types.d.ts +3 -2
  59. package/lib/graph/query/cypher-reader.d.ts +59 -0
  60. package/lib/graph/query/cypher-reader.js +151 -0
  61. package/lib/graph/query/cypher-support.d.ts +79 -0
  62. package/lib/graph/query/cypher-support.js +213 -0
  63. package/lib/graph/query/cypher-tokenize.d.ts +13 -0
  64. package/lib/graph/query/cypher-tokenize.js +225 -0
  65. package/lib/graph/query/cypher.types.d.ts +43 -0
  66. package/lib/graph/query/execute.d.ts +7 -7
  67. package/lib/graph/query/execute.js +71 -33
  68. package/lib/graph/query/inspect.js +58 -24
  69. package/lib/graph/query/parse-cypher-patterns.d.ts +27 -0
  70. package/lib/graph/query/parse-cypher-patterns.js +382 -0
  71. package/lib/graph/query/parse-cypher.d.ts +7 -0
  72. package/lib/graph/query/parse-cypher.js +580 -0
  73. package/lib/graph/query/parse-query.d.ts +13 -0
  74. package/lib/graph/query/parse-query.js +97 -0
  75. package/lib/graph/query/resolve.d.ts +6 -0
  76. package/lib/graph/query/resolve.js +81 -24
  77. package/lib/output/command-output.js +12 -5
  78. package/lib/output/compact-layout.js +221 -0
  79. package/lib/output/format-output-item-block.js +31 -1
  80. package/lib/output/format-output-metadata.js +16 -29
  81. package/lib/output/format-stored-query-block.js +95 -0
  82. package/lib/output/layout-incoming-references.js +101 -19
  83. package/lib/output/layout-stored-queries.js +23 -330
  84. package/lib/output/list-queries.js +1 -1
  85. package/lib/output/render-field-discovery.js +11 -2
  86. package/lib/output/render-output-view.js +9 -5
  87. package/lib/output/renderers/json.js +5 -26
  88. package/lib/output/renderers/plain.js +155 -35
  89. package/lib/output/renderers/rich.js +250 -36
  90. package/lib/output/resolve-check-target.js +120 -11
  91. package/lib/output/resolved-link-layout.js +43 -0
  92. package/lib/output/rich-source/render.js +193 -35
  93. package/lib/output/show-document.js +25 -18
  94. package/lib/output/view-model/index.js +124 -103
  95. package/lib/parse/jsdoc/parse-jsdoc-blocks.js +1 -1
  96. package/lib/parse/jsdoc/parse-jsdoc-claims.js +12 -6
  97. package/lib/parse/markdown/parse-markdown-claims.js +99 -62
  98. package/lib/parse/markdown/parse-markdown-directives.d.ts +10 -6
  99. package/lib/parse/markdown/parse-markdown-directives.js +104 -18
  100. package/lib/parse/markdown/parse-markdown-prose.d.ts +27 -0
  101. package/lib/parse/markdown/parse-markdown-prose.js +243 -0
  102. package/lib/parse/parse-claims.d.ts +2 -6
  103. package/lib/parse/parse-claims.js +11 -53
  104. package/lib/parse/tagged-fenced/tagged-fenced-blocks.d.ts +4 -4
  105. package/lib/parse/tagged-fenced/tagged-fenced-blocks.js +4 -4
  106. package/lib/parse/yaml/parse-yaml-claims.js +4 -4
  107. package/lib/patram.d.ts +9 -3
  108. package/lib/patram.js +1 -1
  109. package/lib/scan/discover-fields.js +194 -55
  110. package/lib/scan/list-source-files.d.ts +4 -4
  111. package/lib/scan/list-source-files.js +4 -4
  112. package/package.json +2 -1
  113. package/lib/directive-validation-test-helpers.js +0 -87
  114. package/lib/graph/query/parse.d.ts +0 -75
  115. package/lib/graph/query/parse.js +0 -1064
  116. package/lib/output/derived-summary.js +0 -280
  117. package/lib/output/format-derived-summary-row.js +0 -9
package/bin/patram.js CHANGED
@@ -12,10 +12,10 @@ import { main } from '../lib/cli/main.js';
12
12
  * Detects direct process execution and forwards command handling to the shared
13
13
  * CLI runtime.
14
14
  *
15
- * Kind: entrypoint
16
- * Status: active
17
- * Tracked in: ../docs/plans/v0/source-anchor-dogfooding.md
18
- * Decided by: ../docs/decisions/cli-entrypoint-symlink.md
15
+ * kind: entrypoint
16
+ * status: active
17
+ * tracked_in: ../docs/plans/v0/source-anchor-dogfooding.md
18
+ * decided_by: ../docs/decisions/cli-entrypoint-symlink.md
19
19
  * @patram
20
20
  * @see {@link ../lib/cli/main.js}
21
21
  * @see {@link ../docs/patram.md}
@@ -47,6 +47,7 @@ export type CliParseError = {
47
47
  } | {
48
48
  code: 'unknown_stored_query';
49
49
  name: string;
50
+ next_path?: string;
50
51
  suggestion?: string;
51
52
  } | {
52
53
  code: 'unknown_option';
@@ -9,13 +9,15 @@ import {
9
9
  renderCheckSuccess,
10
10
  } from '../../output/render-check-output.js';
11
11
  import {
12
- resolveCheckTarget,
13
- selectCheckTargetDiagnostics,
14
- selectCheckTargetSourceFiles,
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 resolved_target = await resolveCheckTarget(
28
- parsed_command.command_arguments[0],
29
+ const resolved_targets = await resolveCheckTargets(
30
+ parsed_command.command_arguments,
29
31
  );
30
- const project_graph_result = await loadProjectGraph(
31
- resolved_target.project_directory,
32
- );
33
- const repo_file_paths = await listRepoFiles(
34
- resolved_target.project_directory,
35
- );
36
- const selected_source_file_paths = selectCheckTargetSourceFiles(
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
- resolved_target,
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 = selectCheckTargetDiagnostics(
67
+ const selected_diagnostics = selectCheckTargetsDiagnostics(
56
68
  diagnostics,
57
- resolved_target,
69
+ resolved_targets,
58
70
  );
59
71
 
60
72
  if (selected_diagnostics.length > 0) {
@@ -49,9 +49,5 @@ function collectDefinedDiscoveryNames(repo_config) {
49
49
  defined_field_names.add(field_name);
50
50
  }
51
51
 
52
- for (const relation_name of Object.keys(repo_config?.relations ?? {})) {
53
- defined_field_names.add(relation_name);
54
- }
55
-
56
52
  return defined_field_names;
57
53
  }
@@ -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 { writeDiagnostics } from '../command-helpers.js';
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,171 @@ 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
+ * | {
105
+ * action: 'add',
106
+ * cypher?: string,
107
+ * description?: string,
108
+ * name: string,
109
+ * }
110
+ * | { action: 'remove', name: string }
111
+ * | {
112
+ * action: 'update',
113
+ * cypher?: string,
114
+ * description?: string,
115
+ * name: string,
116
+ * next_name?: string,
117
+ * },
118
+ * } | { error: import('../arguments.types.ts').CliParseError, success: false }}
119
+ */
120
+ function parseStoredQueryMutation(command_arguments) {
121
+ const subcommand_name = command_arguments[0];
122
+
123
+ if (subcommand_name === 'add') {
124
+ return {
125
+ success: true,
126
+ value: {
127
+ action: 'add',
128
+ cypher: readOptionValue(command_arguments, '--cypher'),
129
+ description: readOptionValue(command_arguments, '--desc'),
130
+ name: command_arguments[1],
131
+ },
132
+ };
133
+ }
134
+
135
+ if (subcommand_name === 'remove') {
136
+ return {
137
+ success: true,
138
+ value: {
139
+ action: 'remove',
140
+ name: command_arguments[1],
141
+ },
142
+ };
143
+ }
144
+
145
+ if (subcommand_name === 'update') {
146
+ return {
147
+ success: true,
148
+ value: {
149
+ action: 'update',
150
+ cypher: readOptionValue(command_arguments, '--cypher'),
151
+ description: readOptionValue(command_arguments, '--desc'),
152
+ name: command_arguments[1],
153
+ next_name: readOptionValue(command_arguments, '--name'),
154
+ },
155
+ };
156
+ }
157
+
158
+ return {
159
+ error: {
160
+ code: 'unexpected_argument',
161
+ command_name: 'queries',
162
+ token: subcommand_name ?? '',
163
+ },
164
+ success: false,
165
+ };
166
+ }
167
+
168
+ /**
169
+ * @param {{
170
+ * action: 'added',
171
+ * name: string,
172
+ * } | {
173
+ * action: 'removed',
174
+ * name: string,
175
+ * } | {
176
+ * action: 'updated',
177
+ * name: string,
178
+ * previous_name?: string,
179
+ * }} mutation_result
180
+ * @param {ParsedCliCommandRequest['output_mode']} output_mode
181
+ * @returns {string}
182
+ */
183
+ function renderStoredQueryMutationResult(mutation_result, output_mode) {
184
+ if (output_mode === 'json') {
185
+ return `${JSON.stringify(mutation_result, null, 2)}\n`;
186
+ }
187
+
188
+ if (
189
+ mutation_result.action === 'updated' &&
190
+ mutation_result.previous_name !== undefined
191
+ ) {
192
+ return `Updated stored query: ${mutation_result.previous_name} -> ${mutation_result.name}\n`;
193
+ }
194
+
195
+ if (mutation_result.action === 'updated') {
196
+ return `Updated stored query: ${mutation_result.name}\n`;
197
+ }
198
+
199
+ if (mutation_result.action === 'added') {
200
+ return `Added stored query: ${mutation_result.name}\n`;
201
+ }
202
+
203
+ return `Removed stored query: ${mutation_result.name}\n`;
204
+ }
205
+
206
+ /**
207
+ * @param {string[]} command_arguments
208
+ * @param {'--cypher' | '--desc' | '--name'} option_name
209
+ * @returns {string | undefined}
210
+ */
211
+ function readOptionValue(command_arguments, option_name) {
212
+ const option_index = command_arguments.indexOf(option_name);
213
+
214
+ if (option_index < 0) {
215
+ return undefined;
216
+ }
217
+
218
+ return command_arguments[option_index + 1];
219
+ }
@@ -10,7 +10,6 @@ import {
10
10
  shouldPageCommandOutput,
11
11
  writeCommandOutput,
12
12
  } from '../../output/command-output.js';
13
- import { createDerivedSummaryEvaluator } from '../../output/derived-summary.js';
14
13
  import { renderCheckDiagnostics } from '../../output/render-check-output.js';
15
14
  import {
16
15
  renderCliParseError,
@@ -83,16 +82,10 @@ export async function runQueryCommand(parsed_command, io_context) {
83
82
  return 1;
84
83
  }
85
84
 
86
- const derived_summary_evaluator = createDerivedSummaryEvaluator(
87
- project_graph_result.config,
88
- project_graph_result.graph,
89
- );
90
-
91
85
  await writeCommandOutput(
92
86
  io_context,
93
87
  parsed_command,
94
88
  createOutputView('query', query_result.nodes, {
95
- derived_summary_evaluator,
96
89
  ...createQueryOutputOptions(parsed_command, query_result, use_pager),
97
90
  repo_config: project_graph_result.config,
98
91
  }),
@@ -182,7 +175,7 @@ function createQueryOutputOptions(parsed_command, query_result, use_pager) {
182
175
  const offset = parsed_command.query_offset ?? 0;
183
176
 
184
177
  if (query_result.total_count === 0) {
185
- hints.push("Try: patram query --where '$class=task'");
178
+ hints.push('Try: patram query --cypher "MATCH (n:Task) RETURN n"');
186
179
  }
187
180
 
188
181
  if (
@@ -7,7 +7,6 @@ import process from 'node:process';
7
7
  import { loadProjectGraph } from '../../graph/load-project-graph.js';
8
8
  import { inspectReverseReferences } from '../../graph/inspect-reverse-references.js';
9
9
  import { writeCommandOutput } from '../../output/command-output.js';
10
- import { createDerivedSummaryEvaluator } from '../../output/derived-summary.js';
11
10
  import { createRefsOutputView } from '../../output/render-output-view.js';
12
11
 
13
12
  import { renderInvalidWhereDiagnostic } from '../render-help.js';
@@ -31,7 +30,7 @@ export async function runRefsCommand(parsed_command, io_context) {
31
30
  project_graph_result.graph,
32
31
  parsed_command.command_arguments[0],
33
32
  project_graph_result.config,
34
- resolveRefsWhereClause(parsed_command.command_arguments),
33
+ resolveRefsCypherQuery(parsed_command.command_arguments),
35
34
  );
36
35
 
37
36
  if (refs_output.diagnostics.length > 0) {
@@ -42,16 +41,10 @@ export async function runRefsCommand(parsed_command, io_context) {
42
41
  return 1;
43
42
  }
44
43
 
45
- const derived_summary_evaluator = createDerivedSummaryEvaluator(
46
- project_graph_result.config,
47
- project_graph_result.graph,
48
- );
49
-
50
44
  await writeCommandOutput(
51
45
  io_context,
52
46
  parsed_command,
53
47
  createRefsOutputView(refs_output, {
54
- derived_summary_evaluator,
55
48
  repo_config: project_graph_result.config,
56
49
  }),
57
50
  );
@@ -63,8 +56,8 @@ export async function runRefsCommand(parsed_command, io_context) {
63
56
  * @param {string[]} command_arguments
64
57
  * @returns {string | undefined}
65
58
  */
66
- function resolveRefsWhereClause(command_arguments) {
67
- if (command_arguments[1] !== '--where') {
59
+ function resolveRefsCypherQuery(command_arguments) {
60
+ if (command_arguments[1] !== '--cypher') {
68
61
  return undefined;
69
62
  }
70
63
 
@@ -6,7 +6,6 @@ import process from 'node:process';
6
6
 
7
7
  import { loadProjectGraph } from '../../graph/load-project-graph.js';
8
8
  import { writeCommandOutput } from '../../output/command-output.js';
9
- import { createDerivedSummaryEvaluator } from '../../output/derived-summary.js';
10
9
  import { createShowOutputView } from '../../output/render-output-view.js';
11
10
  import { loadShowOutput } from '../../output/show-document.js';
12
11
 
@@ -38,17 +37,11 @@ export async function runShowCommand(parsed_command, io_context) {
38
37
  return 1;
39
38
  }
40
39
 
41
- const derived_summary_evaluator = createDerivedSummaryEvaluator(
42
- project_graph_result.config,
43
- project_graph_result.graph,
44
- );
45
-
46
40
  await writeCommandOutput(
47
41
  io_context,
48
42
  parsed_command,
49
43
  createShowOutputView(show_output.value, {
50
- derived_summary_evaluator,
51
- document_node_ids: project_graph_result.graph.document_node_ids,
44
+ document_path_ids: project_graph_result.graph.document_path_ids,
52
45
  graph_nodes: project_graph_result.graph.nodes,
53
46
  repo_config: project_graph_result.config,
54
47
  }),