patram 0.6.2 → 0.8.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 (55) hide show
  1. package/lib/build-graph-identity.d.ts +40 -0
  2. package/lib/build-graph.d.ts +11 -0
  3. package/lib/build-graph.types.d.ts +24 -0
  4. package/lib/claim-helpers.d.ts +20 -0
  5. package/lib/cli-help-metadata.js +48 -9
  6. package/lib/command-output.js +1 -0
  7. package/lib/document-node-identity.d.ts +47 -0
  8. package/lib/inspect-reverse-references.js +184 -0
  9. package/lib/layout-incoming-references.js +105 -0
  10. package/lib/layout-incoming-summary-lines.js +21 -0
  11. package/lib/list-source-files.d.ts +29 -0
  12. package/lib/load-patram-config.d.ts +384 -0
  13. package/lib/load-patram-config.types.d.ts +45 -0
  14. package/lib/load-project-graph.d.ts +35 -0
  15. package/lib/output-view.types.d.ts +88 -0
  16. package/lib/output-view.types.ts +19 -2
  17. package/lib/overlay-graph.d.ts +43 -0
  18. package/lib/overlay-graph.js +191 -0
  19. package/lib/parse-claims.d.ts +40 -0
  20. package/lib/parse-claims.types.d.ts +31 -0
  21. package/lib/parse-cli-arguments-helpers.js +11 -4
  22. package/lib/parse-cli-arguments.types.ts +8 -2
  23. package/lib/parse-jsdoc-blocks.d.ts +15 -0
  24. package/lib/parse-jsdoc-claims.d.ts +9 -0
  25. package/lib/parse-jsdoc-prose.d.ts +28 -0
  26. package/lib/parse-markdown-claims.d.ts +14 -0
  27. package/lib/parse-markdown-directives.d.ts +34 -0
  28. package/lib/parse-where-clause.d.ts +75 -0
  29. package/lib/parse-where-clause.js +157 -37
  30. package/lib/parse-where-clause.types.d.ts +63 -0
  31. package/lib/parse-yaml-claims.d.ts +38 -0
  32. package/lib/patram-cli.js +66 -1
  33. package/lib/patram-config.d.ts +106 -0
  34. package/lib/patram-config.types.d.ts +14 -0
  35. package/lib/patram.d.ts +73 -0
  36. package/lib/patram.js +3 -0
  37. package/lib/query-graph.d.ts +68 -0
  38. package/lib/query-graph.js +27 -24
  39. package/lib/query-inspection.d.ts +86 -0
  40. package/lib/render-cli-help.js +1 -1
  41. package/lib/render-json-output.js +91 -62
  42. package/lib/render-output-view.js +58 -3
  43. package/lib/render-plain-output.js +46 -3
  44. package/lib/render-rich-output.js +50 -5
  45. package/lib/resolve-patram-graph-config.d.ts +9 -0
  46. package/lib/reverse-reference-test-helpers.js +76 -0
  47. package/lib/show-document.js +44 -6
  48. package/lib/source-file-defaults.d.ts +5 -0
  49. package/lib/tagged-fenced-block-error.d.ts +10 -0
  50. package/lib/tagged-fenced-block-markdown.d.ts +47 -0
  51. package/lib/tagged-fenced-block-metadata.d.ts +26 -0
  52. package/lib/tagged-fenced-block-parser.d.ts +61 -0
  53. package/lib/tagged-fenced-blocks.d.ts +39 -0
  54. package/lib/tagged-fenced-blocks.types.d.ts +32 -0
  55. package/package.json +13 -2
@@ -0,0 +1,73 @@
1
+ export {
2
+ extractTaggedFencedBlocks,
3
+ loadTaggedFencedBlocks,
4
+ selectTaggedBlock,
5
+ selectTaggedBlocks,
6
+ } from './tagged-fenced-blocks.js';
7
+
8
+ export { parseWhereClause } from './parse-where-clause.js';
9
+ export { getQuerySemanticDiagnostics } from './query-inspection.js';
10
+ export { loadProjectGraph } from './load-project-graph.js';
11
+ export { overlayGraph } from './overlay-graph.js';
12
+ export { queryGraph } from './query-graph.js';
13
+
14
+ export type PatramGraphNode = import('./build-graph.types.ts').GraphNode;
15
+ export type PatramGraphEdge = import('./build-graph.types.ts').GraphEdge;
16
+ export type PatramBuildGraphResult =
17
+ import('./build-graph.types.ts').BuildGraphResult;
18
+ export type PatramDiagnostic =
19
+ import('./load-patram-config.types.ts').PatramDiagnostic;
20
+ export type PatramRepoConfig =
21
+ import('./load-patram-config.types.ts').PatramRepoConfig;
22
+ export type PatramParsedFieldName =
23
+ import('./parse-where-clause.types.ts').ParsedFieldName;
24
+ export type PatramParsedFieldTerm =
25
+ import('./parse-where-clause.types.ts').ParsedFieldTerm;
26
+ export type PatramParsedFieldSetTerm =
27
+ import('./parse-where-clause.types.ts').ParsedFieldSetTerm;
28
+ export type PatramParsedTraversalTerm =
29
+ import('./parse-where-clause.types.ts').ParsedTraversalTerm;
30
+ export type PatramParsedRelationTerm =
31
+ import('./parse-where-clause.types.ts').ParsedRelationTerm;
32
+ export type PatramParsedRelationTargetTerm =
33
+ import('./parse-where-clause.types.ts').ParsedRelationTargetTerm;
34
+ export type PatramParsedAggregateComparison =
35
+ import('./parse-where-clause.types.ts').ParsedAggregateComparison;
36
+ export type PatramParsedAggregateName =
37
+ import('./parse-where-clause.types.ts').ParsedAggregateName;
38
+ export type PatramParsedAggregateTerm =
39
+ import('./parse-where-clause.types.ts').ParsedAggregateTerm;
40
+ export type PatramParsedTermExpression =
41
+ import('./parse-where-clause.types.ts').ParsedTermExpression;
42
+ export type PatramParsedNotExpression =
43
+ import('./parse-where-clause.types.ts').ParsedNotExpression;
44
+ export type PatramParsedBooleanExpression =
45
+ import('./parse-where-clause.types.ts').ParsedBooleanExpression;
46
+ export type PatramParsedTerm =
47
+ import('./parse-where-clause.types.ts').ParsedTerm;
48
+ export type PatramParsedExpression =
49
+ import('./parse-where-clause.types.ts').ParsedExpression;
50
+ export type PatramParseWhereClauseResult =
51
+ import('./parse-where-clause.types.ts').ParseWhereClauseResult;
52
+ export type PatramQuerySource =
53
+ | {
54
+ kind: 'ad_hoc';
55
+ }
56
+ | {
57
+ kind: 'stored_query';
58
+ name: string;
59
+ };
60
+
61
+ export interface PatramProjectGraphResult {
62
+ claims: import('./parse-claims.types.ts').PatramClaim[];
63
+ config: import('./load-patram-config.types.ts').PatramRepoConfig;
64
+ diagnostics: PatramDiagnostic[];
65
+ graph: PatramBuildGraphResult;
66
+ source_file_paths: string[];
67
+ }
68
+
69
+ export interface PatramQueryResult {
70
+ diagnostics: PatramDiagnostic[];
71
+ nodes: PatramGraphNode[];
72
+ total_count: number;
73
+ }
package/lib/patram.js CHANGED
@@ -5,5 +5,8 @@ export {
5
5
  selectTaggedBlocks,
6
6
  } from './tagged-fenced-blocks.js';
7
7
 
8
+ export { parseWhereClause } from './parse-where-clause.js';
9
+ export { getQuerySemanticDiagnostics } from './query-inspection.js';
8
10
  export { loadProjectGraph } from './load-project-graph.js';
11
+ export { overlayGraph } from './overlay-graph.js';
9
12
  export { queryGraph } from './query-graph.js';
@@ -0,0 +1,68 @@
1
+ /**
2
+ * @typedef {{
3
+ * incoming: Map<string, Map<string, Set<string>>>,
4
+ * outgoing: Map<string, Map<string, Set<string>>>,
5
+ * }} RelationIndexes
6
+ */
7
+ /**
8
+ * @typedef {{
9
+ * bindings?: Record<string, string>,
10
+ * limit?: number,
11
+ * offset?: number,
12
+ * }} QueryGraphOptions
13
+ */
14
+ /**
15
+ * @typedef {{
16
+ * nodes: BuildGraphResult['nodes'],
17
+ * relation_indexes: RelationIndexes,
18
+ * }} EvaluationContext
19
+ */
20
+ /**
21
+ * Filter graph nodes with the query language.
22
+ *
23
+ * @param {BuildGraphResult} graph
24
+ * @param {string} where_clause
25
+ * @param {PatramRepoConfig | QueryGraphOptions=} repo_config_or_query_options
26
+ * @param {QueryGraphOptions=} query_options
27
+ * @returns {{ diagnostics: PatramDiagnostic[], nodes: GraphNode[], total_count: number }}
28
+ */
29
+ export function queryGraph(graph: BuildGraphResult, where_clause: string, repo_config_or_query_options?: (PatramRepoConfig | QueryGraphOptions) | undefined, query_options?: QueryGraphOptions | undefined): {
30
+ diagnostics: PatramDiagnostic[];
31
+ nodes: GraphNode[];
32
+ total_count: number;
33
+ };
34
+ /**
35
+ * Query graph filtering.
36
+ *
37
+ * Applies the v0 where-clause language to graph nodes and keeps pagination
38
+ * separate from matching.
39
+ *
40
+ * Kind: graph
41
+ * Status: active
42
+ * Uses Term: ../docs/reference/terms/graph.md
43
+ * Uses Term: ../docs/reference/terms/query.md
44
+ * Tracked in: ../docs/plans/v0/source-anchor-dogfooding.md
45
+ * Decided by: ../docs/decisions/query-language.md
46
+ * Implements: ../docs/tasks/v0/query-command.md
47
+ * @patram
48
+ * @see {@link ./load-project-graph.js}
49
+ * @see {@link ../docs/decisions/query-language.md}
50
+ */
51
+ export const DEFAULT_QUERY_LIMIT: 25;
52
+ export type RelationIndexes = {
53
+ incoming: Map<string, Map<string, Set<string>>>;
54
+ outgoing: Map<string, Map<string, Set<string>>>;
55
+ };
56
+ export type QueryGraphOptions = {
57
+ bindings?: Record<string, string>;
58
+ limit?: number;
59
+ offset?: number;
60
+ };
61
+ export type EvaluationContext = {
62
+ nodes: BuildGraphResult["nodes"];
63
+ relation_indexes: RelationIndexes;
64
+ };
65
+ import type { BuildGraphResult } from './build-graph.types.ts';
66
+ import type { PatramRepoConfig } from './load-patram-config.types.ts';
67
+ import type { PatramDiagnostic } from './load-patram-config.types.ts';
68
+ import type { GraphNode } from './build-graph.types.ts';
@@ -43,6 +43,14 @@ export const DEFAULT_QUERY_LIMIT = 25;
43
43
  * }} RelationIndexes
44
44
  */
45
45
 
46
+ /**
47
+ * @typedef {{
48
+ * bindings?: Record<string, string>,
49
+ * limit?: number,
50
+ * offset?: number,
51
+ * }} QueryGraphOptions
52
+ */
53
+
46
54
  /**
47
55
  * @typedef {{
48
56
  * nodes: BuildGraphResult['nodes'],
@@ -55,17 +63,21 @@ export const DEFAULT_QUERY_LIMIT = 25;
55
63
  *
56
64
  * @param {BuildGraphResult} graph
57
65
  * @param {string} where_clause
58
- * @param {PatramRepoConfig | { limit?: number, offset?: number }=} repo_config_or_pagination
59
- * @param {{ limit?: number, offset?: number }=} pagination_options
66
+ * @param {PatramRepoConfig | QueryGraphOptions=} repo_config_or_query_options
67
+ * @param {QueryGraphOptions=} query_options
60
68
  * @returns {{ diagnostics: PatramDiagnostic[], nodes: GraphNode[], total_count: number }}
61
69
  */
62
70
  export function queryGraph(
63
71
  graph,
64
72
  where_clause,
65
- repo_config_or_pagination = {},
66
- pagination_options = {},
73
+ repo_config_or_query_options = {},
74
+ query_options = {},
67
75
  ) {
68
- const parse_result = parseWhereClause(where_clause);
76
+ const { query_options: resolved_query_options, repo_config } =
77
+ resolveQueryGraphOptions(repo_config_or_query_options, query_options);
78
+ const parse_result = parseWhereClause(where_clause, {
79
+ bindings: resolved_query_options.bindings,
80
+ });
69
81
 
70
82
  if (!parse_result.success) {
71
83
  return {
@@ -75,9 +87,6 @@ export function queryGraph(
75
87
  };
76
88
  }
77
89
 
78
- const { pagination_options: resolved_pagination_options, repo_config } =
79
- resolveQueryGraphOptions(repo_config_or_pagination, pagination_options);
80
-
81
90
  if (repo_config) {
82
91
  const diagnostics = getQuerySemanticDiagnostics(
83
92
  repo_config,
@@ -102,10 +111,7 @@ export function queryGraph(
102
111
  const matching_nodes = graph_nodes.filter((graph_node) =>
103
112
  matchesExpression(graph_node, parse_result.expression, evaluation_context),
104
113
  );
105
- const paginated_nodes = paginateNodes(
106
- matching_nodes,
107
- resolved_pagination_options,
108
- );
114
+ const paginated_nodes = paginateNodes(matching_nodes, resolved_query_options);
109
115
 
110
116
  return {
111
117
  diagnostics: [],
@@ -501,29 +507,26 @@ function getScalarFieldValue(field_value) {
501
507
  }
502
508
 
503
509
  /**
504
- * @param {PatramRepoConfig | { limit?: number, offset?: number }} repo_config_or_pagination
505
- * @param {{ limit?: number, offset?: number }} pagination_options
506
- * @returns {{ pagination_options: { limit?: number, offset?: number }, repo_config: PatramRepoConfig | null }}
510
+ * @param {PatramRepoConfig | QueryGraphOptions} repo_config_or_query_options
511
+ * @param {QueryGraphOptions} query_options
512
+ * @returns {{ query_options: QueryGraphOptions, repo_config: PatramRepoConfig | null }}
507
513
  */
508
- function resolveQueryGraphOptions(
509
- repo_config_or_pagination,
510
- pagination_options,
511
- ) {
512
- if (isRepoConfig(repo_config_or_pagination)) {
514
+ function resolveQueryGraphOptions(repo_config_or_query_options, query_options) {
515
+ if (isRepoConfig(repo_config_or_query_options)) {
513
516
  return {
514
- pagination_options,
515
- repo_config: repo_config_or_pagination,
517
+ query_options,
518
+ repo_config: repo_config_or_query_options,
516
519
  };
517
520
  }
518
521
 
519
522
  return {
520
- pagination_options: repo_config_or_pagination,
523
+ query_options: repo_config_or_query_options,
521
524
  repo_config: null,
522
525
  };
523
526
  }
524
527
 
525
528
  /**
526
- * @param {PatramRepoConfig | { limit?: number, offset?: number }} value
529
+ * @param {PatramRepoConfig | QueryGraphOptions} value
527
530
  * @returns {value is PatramRepoConfig}
528
531
  */
529
532
  function isRepoConfig(value) {
@@ -0,0 +1,86 @@
1
+ /**
2
+ * @typedef {{ kind: 'ad_hoc' } | { kind: 'stored_query', name: string }} QuerySource
3
+ */
4
+ /**
5
+ * @typedef {import('./parse-where-clause.types.ts').ParsedFieldTerm | import('./parse-where-clause.types.ts').ParsedFieldSetTerm} FieldDiagnosticTerm
6
+ */
7
+ /**
8
+ * @typedef {{ query_source: QuerySource, where_clause: string }} ResolvedWhereClause
9
+ */
10
+ /**
11
+ * @typedef {{ inspection_mode: 'explain' | 'lint', limit: number | null, offset: number }} QueryInspectionOptions
12
+ */
13
+ /**
14
+ * @typedef {{
15
+ * execution?: {
16
+ * limit: number | null,
17
+ * offset: number,
18
+ * },
19
+ * expression?: ParsedExpression,
20
+ * inspection_mode: 'explain' | 'lint',
21
+ * query_source: QuerySource,
22
+ * where_clause: string,
23
+ * }} QueryInspectionSuccess
24
+ */
25
+ /**
26
+ * Inspect one resolved query without executing it.
27
+ *
28
+ * @param {PatramRepoConfig} repo_config
29
+ * @param {ResolvedWhereClause} resolved_where_clause
30
+ * @param {QueryInspectionOptions} inspection_options
31
+ * @returns {{ success: true, value: QueryInspectionSuccess } | { diagnostics: PatramDiagnostic[], success: false }}
32
+ */
33
+ export function inspectQuery(repo_config: PatramRepoConfig, resolved_where_clause: ResolvedWhereClause, inspection_options: QueryInspectionOptions): {
34
+ success: true;
35
+ value: QueryInspectionSuccess;
36
+ } | {
37
+ diagnostics: PatramDiagnostic[];
38
+ success: false;
39
+ };
40
+ /**
41
+ * Render a successful query inspection in one output mode.
42
+ *
43
+ * @param {QueryInspectionSuccess} query_inspection
44
+ * @param {ResolvedOutputMode} output_mode
45
+ * @returns {string}
46
+ */
47
+ export function renderQueryInspection(query_inspection: QueryInspectionSuccess, output_mode: ResolvedOutputMode): string;
48
+ /**
49
+ * Collect schema-aware diagnostics for one parsed where clause.
50
+ *
51
+ * @param {PatramRepoConfig} repo_config
52
+ * @param {QuerySource} query_source
53
+ * @param {ParsedExpression} expression
54
+ * @returns {PatramDiagnostic[]}
55
+ */
56
+ export function getQuerySemanticDiagnostics(repo_config: PatramRepoConfig, query_source: QuerySource, expression: ParsedExpression): PatramDiagnostic[];
57
+ export type QuerySource = {
58
+ kind: "ad_hoc";
59
+ } | {
60
+ kind: "stored_query";
61
+ name: string;
62
+ };
63
+ export type FieldDiagnosticTerm = import("./parse-where-clause.types.ts").ParsedFieldTerm | import("./parse-where-clause.types.ts").ParsedFieldSetTerm;
64
+ export type ResolvedWhereClause = {
65
+ query_source: QuerySource;
66
+ where_clause: string;
67
+ };
68
+ export type QueryInspectionOptions = {
69
+ inspection_mode: "explain" | "lint";
70
+ limit: number | null;
71
+ offset: number;
72
+ };
73
+ export type QueryInspectionSuccess = {
74
+ execution?: {
75
+ limit: number | null;
76
+ offset: number;
77
+ };
78
+ expression?: ParsedExpression;
79
+ inspection_mode: "explain" | "lint";
80
+ query_source: QuerySource;
81
+ where_clause: string;
82
+ };
83
+ import type { PatramRepoConfig } from './load-patram-config.types.ts';
84
+ import type { PatramDiagnostic } from './load-patram-config.types.ts';
85
+ import type { ResolvedOutputMode } from './output-view.types.ts';
86
+ import type { ParsedExpression } from './parse-where-clause.types.ts';
@@ -308,7 +308,7 @@ function renderInvalidCommandOptionError(command_name, invalid_token) {
308
308
  }
309
309
 
310
310
  /**
311
- * @param {'query' | 'show'} command_name
311
+ * @param {'query' | 'refs' | 'show'} command_name
312
312
  * @param {string} argument_label
313
313
  * @returns {string}
314
314
  */
@@ -10,54 +10,113 @@
10
10
  */
11
11
  export function renderJsonOutput(output_view) {
12
12
  if (output_view.command === 'query') {
13
- return `${JSON.stringify(
14
- {
15
- results: output_view.items.map(formatJsonQueryItem),
16
- summary: {
17
- shown_count: output_view.summary.count,
18
- total_count: output_view.summary.total_count,
19
- offset: output_view.summary.offset,
20
- limit: output_view.summary.limit,
21
- },
22
- hints: output_view.hints,
23
- },
24
- null,
25
- 2,
26
- )}\n`;
13
+ return renderJsonQueryOutput(output_view);
27
14
  }
28
15
 
29
16
  if (output_view.command === 'queries') {
30
- return `${JSON.stringify(
31
- {
32
- queries: output_view.items.map(formatJsonStoredQuery),
33
- },
34
- null,
35
- 2,
36
- )}\n`;
17
+ return renderJsonQueriesOutput(output_view.items);
18
+ }
19
+
20
+ if (output_view.command === 'refs') {
21
+ return renderJsonRefsOutput(output_view);
37
22
  }
38
23
 
39
24
  if (output_view.command === 'show') {
40
- return `${JSON.stringify(
41
- {
42
- document: output_view.document
43
- ? formatJsonShowDocument(output_view.document)
44
- : undefined,
45
- source: output_view.source,
46
- resolved_links: output_view.items.map(formatJsonResolvedLink),
47
- },
48
- null,
49
- 2,
50
- )}\n`;
25
+ return renderJsonShowOutput(output_view);
51
26
  }
52
27
 
53
28
  throw new Error('Unsupported output view command.');
54
29
  }
55
30
 
31
+ /**
32
+ * @param {Extract<OutputView, { command: 'query' }>} output_view
33
+ * @returns {string}
34
+ */
35
+ function renderJsonQueryOutput(output_view) {
36
+ return `${JSON.stringify(
37
+ {
38
+ results: output_view.items.map(formatJsonQueryItem),
39
+ summary: {
40
+ shown_count: output_view.summary.count,
41
+ total_count: output_view.summary.total_count,
42
+ offset: output_view.summary.offset,
43
+ limit: output_view.summary.limit,
44
+ },
45
+ hints: output_view.hints,
46
+ },
47
+ null,
48
+ 2,
49
+ )}\n`;
50
+ }
51
+
52
+ /**
53
+ * @param {OutputStoredQueryItem[]} output_items
54
+ * @returns {string}
55
+ */
56
+ function renderJsonQueriesOutput(output_items) {
57
+ return `${JSON.stringify(
58
+ {
59
+ queries: output_items.map(formatJsonStoredQuery),
60
+ },
61
+ null,
62
+ 2,
63
+ )}\n`;
64
+ }
65
+
66
+ /**
67
+ * @param {Extract<OutputView, { command: 'refs' }>} output_view
68
+ * @returns {string}
69
+ */
70
+ function renderJsonRefsOutput(output_view) {
71
+ return `${JSON.stringify(
72
+ {
73
+ node: formatJsonNodeItem(output_view.node),
74
+ incoming: Object.fromEntries(
75
+ Object.entries(output_view.incoming).map(
76
+ ([relation_name, output_items]) => [
77
+ relation_name,
78
+ output_items.map(formatJsonNodeItem),
79
+ ],
80
+ ),
81
+ ),
82
+ },
83
+ null,
84
+ 2,
85
+ )}\n`;
86
+ }
87
+
88
+ /**
89
+ * @param {Extract<OutputView, { command: 'show' }>} output_view
90
+ * @returns {string}
91
+ */
92
+ function renderJsonShowOutput(output_view) {
93
+ return `${JSON.stringify(
94
+ {
95
+ document: output_view.document
96
+ ? formatJsonNodeItem(output_view.document)
97
+ : undefined,
98
+ incoming_summary: output_view.incoming_summary,
99
+ source: output_view.source,
100
+ resolved_links: output_view.items.map(formatJsonResolvedLink),
101
+ },
102
+ null,
103
+ 2,
104
+ )}\n`;
105
+ }
106
+
56
107
  /**
57
108
  * @param {OutputNodeItem} output_item
58
109
  * @returns {{ '$class': string, '$id': string, '$path'?: string, derived?: Record<string, boolean | number | string | null>, derived_summary?: string, fields: Record<string, string | string[]>, title: string }}
59
110
  */
60
111
  function formatJsonQueryItem(output_item) {
112
+ return formatJsonNodeItem(output_item);
113
+ }
114
+
115
+ /**
116
+ * @param {OutputNodeItem} output_item
117
+ * @returns {{ '$class': string, '$id': string, '$path'?: string, derived?: Record<string, boolean | number | string | null>, derived_summary?: string, fields: Record<string, string | string[]>, title: string }}
118
+ */
119
+ function formatJsonNodeItem(output_item) {
61
120
  /** @type {{ '$class': string, '$id': string, '$path'?: string, derived?: Record<string, boolean | number | string | null>, derived_summary?: string, fields: Record<string, string | string[]>, title: string }} */
62
121
  const query_item = {
63
122
  $class: output_item.node_kind,
@@ -128,33 +187,3 @@ function formatJsonResolvedLink(output_item) {
128
187
 
129
188
  return resolved_link;
130
189
  }
131
-
132
- /**
133
- * @param {OutputNodeItem} output_item
134
- * @returns {{ '$class': string, '$id': string, '$path'?: string, derived?: Record<string, boolean | number | string | null>, derived_summary?: string, fields: Record<string, string | string[]>, title: string }}
135
- */
136
- function formatJsonShowDocument(output_item) {
137
- /** @type {{ '$class': string, '$id': string, '$path'?: string, derived?: Record<string, boolean | number | string | null>, derived_summary?: string, fields: Record<string, string | string[]>, title: string }} */
138
- const document_summary = {
139
- $class: output_item.node_kind,
140
- $id: output_item.id,
141
- fields: output_item.fields,
142
- title: output_item.title,
143
- };
144
-
145
- if (output_item.path) {
146
- document_summary.$path = output_item.path;
147
- }
148
-
149
- if (output_item.derived_summary) {
150
- document_summary.derived_summary = output_item.derived_summary.name;
151
- document_summary.derived = Object.fromEntries(
152
- output_item.derived_summary.fields.map((field) => [
153
- field.name,
154
- field.value,
155
- ]),
156
- );
157
- }
158
-
159
- return document_summary;
160
- }
@@ -3,7 +3,7 @@
3
3
  * @import { DerivedSummaryEvaluator } from './derived-summary.js';
4
4
  * @import { PatramRepoConfig } from './load-patram-config.types.ts';
5
5
  * @import { ParsedCliArguments } from './parse-cli-arguments.types.ts';
6
- * @import { OutputDerivedSummary, OutputMetadataField, OutputNodeItem, OutputResolvedLinkItem, OutputResolvedLinkTarget, OutputStoredQueryItem, OutputView, ResolvedOutputMode, ShowOutputView } from './output-view.types.ts';
6
+ * @import { OutputDerivedSummary, OutputMetadataField, OutputNodeItem, OutputResolvedLinkItem, OutputResolvedLinkTarget, OutputStoredQueryItem, OutputView, RefsOutputView, ResolvedOutputMode, ShowOutputView } from './output-view.types.ts';
7
7
  */
8
8
  /* eslint-disable max-lines */
9
9
 
@@ -55,7 +55,7 @@ export function createOutputView(command_name, command_items, command_options) {
55
55
  /**
56
56
  * Create a shared output view for the show command.
57
57
  *
58
- * @param {{ path: string, rendered_source: string, resolved_links: Array<{ label: string, reference: number, target: { kind?: string, path: string, status?: string, title: string } }>, source: string }} show_output
58
+ * @param {{ incoming_summary: Record<string, number>, path: string, rendered_source: string, resolved_links: Array<{ label: string, reference: number, target: { kind?: string, path: string, status?: string, title: string } }>, source: string }} show_output
59
59
  * @param {{ derived_summary_evaluator?: DerivedSummaryEvaluator, document_node_ids?: BuildGraphResult['document_node_ids'], graph_nodes?: BuildGraphResult['nodes'], repo_config?: PatramRepoConfig }=} command_options
60
60
  * @returns {ShowOutputView}
61
61
  */
@@ -78,6 +78,7 @@ export function createShowOutputView(show_output, command_options = {}) {
78
78
  )
79
79
  : undefined,
80
80
  hints: [],
81
+ incoming_summary: show_output.incoming_summary,
81
82
  items: show_output.resolved_links.map((resolved_link) =>
82
83
  createResolvedLinkOutputItem(resolved_link, command_options),
83
84
  ),
@@ -91,6 +92,46 @@ export function createShowOutputView(show_output, command_options = {}) {
91
92
  };
92
93
  }
93
94
 
95
+ /**
96
+ * Create a shared output view for the refs command.
97
+ *
98
+ * @param {{ incoming: Record<string, GraphNode[]>, node: GraphNode }} refs_output
99
+ * @param {{ derived_summary_evaluator?: DerivedSummaryEvaluator, repo_config?: PatramRepoConfig }=} command_options
100
+ * @returns {RefsOutputView}
101
+ */
102
+ export function createRefsOutputView(refs_output, command_options = {}) {
103
+ /** @type {Record<string, OutputNodeItem[]>} */
104
+ const incoming = {};
105
+
106
+ for (const relation_name of Object.keys(refs_output.incoming)) {
107
+ incoming[relation_name] = refs_output.incoming[relation_name].map(
108
+ (graph_node) =>
109
+ createOutputNodeItem(
110
+ graph_node,
111
+ command_options.derived_summary_evaluator?.evaluate(graph_node) ??
112
+ null,
113
+ command_options.repo_config?.fields ?? {},
114
+ ),
115
+ );
116
+ }
117
+
118
+ return {
119
+ command: 'refs',
120
+ hints: [],
121
+ incoming,
122
+ node: createOutputNodeItem(
123
+ refs_output.node,
124
+ command_options.derived_summary_evaluator?.evaluate(refs_output.node) ??
125
+ null,
126
+ command_options.repo_config?.fields ?? {},
127
+ ),
128
+ summary: {
129
+ count: countIncomingReferenceItems(incoming),
130
+ kind: 'incoming_reference_list',
131
+ },
132
+ };
133
+ }
134
+
94
135
  /**
95
136
  * Render one shared output view through the resolved renderer.
96
137
  *
@@ -130,7 +171,7 @@ function createQueryOutputView(graph_nodes, command_options = {}) {
130
171
  command: 'query',
131
172
  hints:
132
173
  command_options.hints ??
133
- (total_count === 0 ? ['Try: patram query --where "$class=task"'] : []),
174
+ (total_count === 0 ? ["Try: patram query --where '$class=task'"] : []),
134
175
  items: graph_nodes.map((graph_node) =>
135
176
  createOutputNodeItem(
136
177
  graph_node,
@@ -168,6 +209,20 @@ function createStoredQueriesOutputView(stored_queries) {
168
209
  };
169
210
  }
170
211
 
212
+ /**
213
+ * @param {Record<string, OutputNodeItem[]>} incoming
214
+ * @returns {number}
215
+ */
216
+ function countIncomingReferenceItems(incoming) {
217
+ let count = 0;
218
+
219
+ for (const output_items of Object.values(incoming)) {
220
+ count += output_items.length;
221
+ }
222
+
223
+ return count;
224
+ }
225
+
171
226
  /**
172
227
  * @param {GraphNode} graph_node
173
228
  * @param {OutputDerivedSummary | null} derived_summary