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,191 @@
1
+ /**
2
+ * @import { BuildGraphResult, GraphEdge, GraphNode } from './build-graph.types.ts';
3
+ */
4
+
5
+ /**
6
+ * @typedef {{
7
+ * document_node_ids?: BuildGraphResult['document_node_ids'],
8
+ * edges?: GraphEdge[],
9
+ * nodes?: GraphNode[] | Record<string, GraphNode>,
10
+ * }} GraphOverlay
11
+ */
12
+
13
+ /**
14
+ * Graph overlay composition.
15
+ *
16
+ * Composes transient nodes and edges onto an existing Patram graph while
17
+ * preserving the graph shape expected by query and document resolution
18
+ * helpers.
19
+ *
20
+ * Kind: graph
21
+ * Status: active
22
+ * Uses Term: ../docs/reference/terms/document.md
23
+ * Uses Term: ../docs/reference/terms/graph.md
24
+ * Tracked in: ../docs/plans/v0/package-graph-overlay-helper.md
25
+ * Decided by: ../docs/decisions/package-graph-overlay-helper.md
26
+ * @patram
27
+ * @see {@link ./query-graph.js}
28
+ * @see {@link ../docs/decisions/package-graph-overlay-helper.md}
29
+ */
30
+
31
+ /**
32
+ * Compose additional nodes and edges onto a loaded Patram graph.
33
+ *
34
+ * @param {BuildGraphResult} base_graph
35
+ * @param {GraphOverlay} [graph_overlay]
36
+ * @returns {BuildGraphResult}
37
+ */
38
+ export function overlayGraph(base_graph, graph_overlay = {}) {
39
+ const graph_nodes = cloneGraphNodes(base_graph.nodes);
40
+ const overlay_nodes = normalizeOverlayNodes(graph_overlay.nodes);
41
+
42
+ applyOverlayNodes(graph_nodes, overlay_nodes);
43
+
44
+ const graph = {
45
+ edges: [...base_graph.edges, ...(graph_overlay.edges ?? [])],
46
+ nodes: graph_nodes,
47
+ };
48
+ const document_node_ids = createDocumentNodeIds(
49
+ base_graph.document_node_ids,
50
+ graph_nodes,
51
+ graph_overlay.document_node_ids,
52
+ );
53
+
54
+ attachDocumentNodeAliases(graph.nodes, document_node_ids);
55
+ Object.defineProperty(graph, 'document_node_ids', {
56
+ configurable: false,
57
+ enumerable: false,
58
+ value: document_node_ids,
59
+ writable: false,
60
+ });
61
+
62
+ return graph;
63
+ }
64
+
65
+ /**
66
+ * @param {BuildGraphResult['nodes']} base_graph_nodes
67
+ * @returns {BuildGraphResult['nodes']}
68
+ */
69
+ function cloneGraphNodes(base_graph_nodes) {
70
+ return Object.fromEntries(
71
+ Object.entries(base_graph_nodes).map(([node_id, graph_node]) => [
72
+ node_id,
73
+ { ...graph_node },
74
+ ]),
75
+ );
76
+ }
77
+
78
+ /**
79
+ * @param {GraphOverlay['nodes']} overlay_nodes
80
+ * @returns {GraphNode[]}
81
+ */
82
+ function normalizeOverlayNodes(overlay_nodes) {
83
+ if (!overlay_nodes) {
84
+ return [];
85
+ }
86
+
87
+ if (Array.isArray(overlay_nodes)) {
88
+ return overlay_nodes.map((graph_node) =>
89
+ normalizeOverlayNode(undefined, graph_node),
90
+ );
91
+ }
92
+
93
+ return Object.entries(overlay_nodes).map(([node_id, graph_node]) =>
94
+ normalizeOverlayNode(node_id, graph_node),
95
+ );
96
+ }
97
+
98
+ /**
99
+ * @param {string | undefined} node_id
100
+ * @param {GraphNode} graph_node
101
+ * @returns {GraphNode}
102
+ */
103
+ function normalizeOverlayNode(node_id, graph_node) {
104
+ if (node_id === undefined) {
105
+ if (typeof graph_node.id !== 'string' || graph_node.id.length === 0) {
106
+ throw new Error('Overlay nodes must define an id.');
107
+ }
108
+
109
+ return { ...graph_node };
110
+ }
111
+
112
+ if (graph_node.id !== undefined && graph_node.id !== node_id) {
113
+ throw new Error(
114
+ `Overlay node "${node_id}" does not match embedded id "${graph_node.id}".`,
115
+ );
116
+ }
117
+
118
+ return {
119
+ ...graph_node,
120
+ id: node_id,
121
+ };
122
+ }
123
+
124
+ /**
125
+ * @param {BuildGraphResult['nodes']} graph_nodes
126
+ * @param {GraphNode[]} overlay_nodes
127
+ */
128
+ function applyOverlayNodes(graph_nodes, overlay_nodes) {
129
+ for (const overlay_node of overlay_nodes) {
130
+ const existing_node = graph_nodes[overlay_node.id];
131
+
132
+ graph_nodes[overlay_node.id] = existing_node
133
+ ? { ...existing_node, ...overlay_node }
134
+ : { ...overlay_node };
135
+ }
136
+ }
137
+
138
+ /**
139
+ * @param {BuildGraphResult['document_node_ids']} base_document_node_ids
140
+ * @param {BuildGraphResult['nodes']} graph_nodes
141
+ * @param {BuildGraphResult['document_node_ids']} overlay_document_node_ids
142
+ * @returns {Record<string, string>}
143
+ */
144
+ function createDocumentNodeIds(
145
+ base_document_node_ids,
146
+ graph_nodes,
147
+ overlay_document_node_ids,
148
+ ) {
149
+ /** @type {Record<string, string>} */
150
+ const document_node_ids = {
151
+ ...(base_document_node_ids ?? {}),
152
+ };
153
+
154
+ for (const graph_node of Object.values(graph_nodes)) {
155
+ if (!graph_node.$path) {
156
+ continue;
157
+ }
158
+
159
+ document_node_ids[graph_node.$path] = graph_node.id;
160
+ }
161
+
162
+ return {
163
+ ...document_node_ids,
164
+ ...(overlay_document_node_ids ?? {}),
165
+ };
166
+ }
167
+
168
+ /**
169
+ * @param {BuildGraphResult['nodes']} graph_nodes
170
+ * @param {Record<string, string>} document_node_ids
171
+ */
172
+ function attachDocumentNodeAliases(graph_nodes, document_node_ids) {
173
+ for (const [document_path, node_id] of Object.entries(document_node_ids)) {
174
+ if (`doc:${document_path}` === node_id) {
175
+ continue;
176
+ }
177
+
178
+ const graph_node = graph_nodes[node_id];
179
+
180
+ if (!graph_node) {
181
+ continue;
182
+ }
183
+
184
+ Object.defineProperty(graph_nodes, `doc:${document_path}`, {
185
+ configurable: false,
186
+ enumerable: false,
187
+ value: graph_node,
188
+ writable: false,
189
+ });
190
+ }
191
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Parse one source file into claims and diagnostics.
3
+ *
4
+ * @param {ParseClaimsInput} parse_input
5
+ * @param {{ multi_value_directive_names?: ReadonlySet<string> }} [parse_options]
6
+ * @returns {ParseSourceFileResult}
7
+ */
8
+ export function parseSourceFile(parse_input: ParseClaimsInput, parse_options?: {
9
+ multi_value_directive_names?: ReadonlySet<string>;
10
+ }): ParseSourceFileResult;
11
+ /**
12
+ * Parse a file into neutral Patram claims.
13
+ *
14
+ * @param {ParseClaimsInput} parse_input
15
+ * @param {{ multi_value_directive_names?: ReadonlySet<string> }} [parse_options]
16
+ * @returns {PatramClaim[]}
17
+ */
18
+ export function parseClaims(parse_input: ParseClaimsInput, parse_options?: {
19
+ multi_value_directive_names?: ReadonlySet<string>;
20
+ }): PatramClaim[];
21
+ /**
22
+ * Build parser options from repo config.
23
+ *
24
+ * @param {{ fields?: Record<string, { multiple?: boolean }>, mappings?: Record<string, { emit?: unknown, node?: unknown }> } | undefined} repo_config
25
+ * @returns {{ multi_value_directive_names: Set<string> }}
26
+ */
27
+ export function createParseOptions(repo_config: {
28
+ fields?: Record<string, {
29
+ multiple?: boolean;
30
+ }>;
31
+ mappings?: Record<string, {
32
+ emit?: unknown;
33
+ node?: unknown;
34
+ }>;
35
+ } | undefined): {
36
+ multi_value_directive_names: Set<string>;
37
+ };
38
+ import type { ParseClaimsInput } from './parse-claims.types.ts';
39
+ import type { ParseSourceFileResult } from './parse-claims.types.ts';
40
+ import type { PatramClaim } from './parse-claims.types.ts';
@@ -0,0 +1,31 @@
1
+ import type { PatramDiagnostic } from './load-patram-config.types.ts';
2
+ export type MarkdownDirectiveStyle = 'front_matter' | 'visible_line' | 'list_item' | 'hidden_tag';
3
+ export interface ParseClaimsInput {
4
+ path: string;
5
+ source: string;
6
+ }
7
+ export interface ClaimOrigin {
8
+ path: string;
9
+ line: number;
10
+ column: number;
11
+ }
12
+ export interface PatramClaim {
13
+ document_id: string;
14
+ id: string;
15
+ markdown_style?: MarkdownDirectiveStyle;
16
+ name?: string;
17
+ origin: ClaimOrigin;
18
+ parser?: string;
19
+ type: string;
20
+ value: string | {
21
+ target: string;
22
+ text: string;
23
+ };
24
+ }
25
+ export type PatramClaimFields = Omit<PatramClaim, 'document_id' | 'id' | 'origin' | 'type'> & {
26
+ origin?: ClaimOrigin;
27
+ };
28
+ export interface ParseSourceFileResult {
29
+ claims: PatramClaim[];
30
+ diagnostics: PatramDiagnostic[];
31
+ }
@@ -160,6 +160,10 @@ export function buildCommandArguments(
160
160
  return ['--where', parsed_values.where];
161
161
  }
162
162
 
163
+ if (command_name === 'refs' && parsed_values.where !== undefined) {
164
+ return [command_positionals[0], '--where', parsed_values.where];
165
+ }
166
+
163
167
  return command_positionals;
164
168
  }
165
169
 
@@ -305,7 +309,7 @@ function findMissingOptionValue(option_tokens) {
305
309
  for (const token of option_tokens) {
306
310
  if (token.name === 'where' && typeof token.value !== 'string') {
307
311
  return {
308
- argument_label: '<name> or --where "<clause>"',
312
+ argument_label: "<name> or --where '<clause>'",
309
313
  code: 'missing_required_argument',
310
314
  command_name: 'query',
311
315
  };
@@ -401,11 +405,14 @@ function validateCommandPositionals(command_name, command_positionals) {
401
405
  };
402
406
  }
403
407
 
404
- if (command_name === 'show' && command_definition.missing_argument_label) {
408
+ if (
409
+ (command_name === 'refs' || command_name === 'show') &&
410
+ command_definition.missing_argument_label
411
+ ) {
405
412
  return {
406
413
  argument_label: command_definition.missing_argument_label,
407
414
  code: 'missing_required_argument',
408
- command_name: 'show',
415
+ command_name,
409
416
  };
410
417
  }
411
418
 
@@ -422,7 +429,7 @@ function validateCommandPositionals(command_name, command_positionals) {
422
429
 
423
430
  if (command_name === 'query' && command_positionals.length === 0) {
424
431
  return {
425
- argument_label: '<name> or --where "<clause>"',
432
+ argument_label: "<name> or --where '<clause>'",
426
433
  code: 'missing_required_argument',
427
434
  command_name: 'query',
428
435
  };
@@ -1,4 +1,10 @@
1
- export type CliCommandName = 'check' | 'fields' | 'query' | 'queries' | 'show';
1
+ export type CliCommandName =
2
+ | 'check'
3
+ | 'fields'
4
+ | 'query'
5
+ | 'queries'
6
+ | 'refs'
7
+ | 'show';
2
8
  export type CliHelpTopicName = 'query-language';
3
9
  export type CliHelpTargetKind = 'root' | 'command' | 'topic';
4
10
 
@@ -34,7 +40,7 @@ export type CliParseError =
34
40
  | {
35
41
  code: 'missing_required_argument';
36
42
  argument_label: string;
37
- command_name: 'query' | 'show';
43
+ command_name: 'query' | 'refs' | 'show';
38
44
  }
39
45
  | {
40
46
  code: 'option_not_valid_for_command';
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Collect JSDoc blocks and their activated `@patram` markers.
3
+ *
4
+ * @param {string} source_text
5
+ * @returns {Array<{ activation_column: number | null, activation_line: number | null, lines: Array<{ column: number, content: string, line: number }> }>}
6
+ */
7
+ export function collectJsdocBlocks(source_text: string): Array<{
8
+ activation_column: number | null;
9
+ activation_line: number | null;
10
+ lines: Array<{
11
+ column: number;
12
+ content: string;
13
+ line: number;
14
+ }>;
15
+ }>;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Parse JSDoc metadata claims from one source file.
3
+ *
4
+ * @param {ParseClaimsInput} parse_input
5
+ * @returns {ParseSourceFileResult}
6
+ */
7
+ export function parseJsdocClaims(parse_input: ParseClaimsInput): ParseSourceFileResult;
8
+ import type { ParseClaimsInput } from './parse-claims.types.ts';
9
+ import type { ParseSourceFileResult } from './parse-claims.types.ts';
@@ -0,0 +1,28 @@
1
+ /**
2
+ * @param {Array<Array<{ column: number, content: string, line: number }>>} prose_paragraphs
3
+ * @param {Array<{ column: number, content: string, line: number }>} paragraph_lines
4
+ */
5
+ export function pushJsdocParagraph(prose_paragraphs: Array<Array<{
6
+ column: number;
7
+ content: string;
8
+ line: number;
9
+ }>>, paragraph_lines: Array<{
10
+ column: number;
11
+ content: string;
12
+ line: number;
13
+ }>): void;
14
+ /**
15
+ * @param {string} file_path
16
+ * @param {Array<Array<{ column: number, content: string, line: number }>>} prose_paragraphs
17
+ * @returns {Array<{ claim_fields: PatramClaimFields, claim_type: string, order: number }>}
18
+ */
19
+ export function createJsdocProseClaimEntries(file_path: string, prose_paragraphs: Array<Array<{
20
+ column: number;
21
+ content: string;
22
+ line: number;
23
+ }>>): Array<{
24
+ claim_fields: PatramClaimFields;
25
+ claim_type: string;
26
+ order: number;
27
+ }>;
28
+ import type { PatramClaimFields } from './parse-claims.types.ts';
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @param {ParseClaimsInput} parse_input
3
+ * @param {{ multi_value_directive_names?: ReadonlySet<string> }} [parse_options]
4
+ * @returns {{ claims: PatramClaim[], diagnostics: PatramDiagnostic[] }}
5
+ */
6
+ export function parseMarkdownClaims(parse_input: ParseClaimsInput, parse_options?: {
7
+ multi_value_directive_names?: ReadonlySet<string>;
8
+ }): {
9
+ claims: PatramClaim[];
10
+ diagnostics: PatramDiagnostic[];
11
+ };
12
+ import type { ParseClaimsInput } from './parse-claims.types.ts';
13
+ import type { PatramClaim } from './parse-claims.types.ts';
14
+ import type { PatramDiagnostic } from './load-patram-config.types.ts';
@@ -0,0 +1,34 @@
1
+ /**
2
+ * @param {string} file_path
3
+ * @param {string[]} lines
4
+ * @param {{ multi_value_directive_names?: ReadonlySet<string> }} [parse_options]
5
+ * @returns {{ body_start: number, diagnostics: PatramDiagnostic[], directive_fields: PatramClaimFields[] }}
6
+ */
7
+ export function parseFrontMatterDirectiveFields(file_path: string, lines: string[], parse_options?: {
8
+ multi_value_directive_names?: ReadonlySet<string>;
9
+ }): {
10
+ body_start: number;
11
+ diagnostics: PatramDiagnostic[];
12
+ directive_fields: PatramClaimFields[];
13
+ };
14
+ /**
15
+ * @param {string} file_path
16
+ * @param {string} line
17
+ * @param {number} line_number
18
+ * @returns {PatramClaimFields | null}
19
+ */
20
+ export function matchVisibleDirectiveFields(file_path: string, line: string, line_number: number): PatramClaimFields | null;
21
+ /**
22
+ * @param {string} file_path
23
+ * @param {string} line
24
+ * @param {number} line_number
25
+ * @returns {PatramClaimFields | null}
26
+ */
27
+ export function matchHiddenDirectiveFields(file_path: string, line: string, line_number: number): PatramClaimFields | null;
28
+ /**
29
+ * @param {string} directive_label
30
+ * @returns {string}
31
+ */
32
+ export function normalizeDirectiveName(directive_label: string): string;
33
+ import type { PatramDiagnostic } from './load-patram-config.types.ts';
34
+ import type { PatramClaimFields } from './parse-claims.types.ts';
@@ -0,0 +1,75 @@
1
+ /**
2
+ * @import { PatramDiagnostic } from './load-patram-config.types.ts';
3
+ * @import {
4
+ * ParseWhereClauseResult,
5
+ * ParsedAggregateComparison,
6
+ * ParsedAggregateName,
7
+ * ParsedAggregateTerm,
8
+ * ParsedExpression,
9
+ * ParsedFieldName,
10
+ * ParsedTerm,
11
+ * ParsedTraversalTerm,
12
+ * } from './parse-where-clause.types.ts';
13
+ */
14
+ /**
15
+ * @typedef {{ bindings: Record<string, string>, index: number, where_clause: string }} ParserState
16
+ */
17
+ /**
18
+ * @typedef {{
19
+ * diagnostic: PatramDiagnostic,
20
+ * success: false,
21
+ * }} ParseFailureResult
22
+ */
23
+ /**
24
+ * @typedef {{
25
+ * expression: ParsedExpression,
26
+ * success: true,
27
+ * } | ParseFailureResult} ParseExpressionResult
28
+ */
29
+ /**
30
+ * @typedef {{
31
+ * success: true,
32
+ * term: ParsedTerm,
33
+ * } | ParseFailureResult} ParseTermResult
34
+ */
35
+ /**
36
+ * @typedef {{
37
+ * success: true,
38
+ * value: string,
39
+ * } | ParseFailureResult} ParseValueResult
40
+ */
41
+ /**
42
+ * Parse one where clause into a structured boolean expression.
43
+ *
44
+ * @param {string} where_clause
45
+ * @param {{ bindings?: Record<string, string> }=} options
46
+ * @returns {ParseWhereClauseResult}
47
+ */
48
+ export function parseWhereClause(where_clause: string, options?: {
49
+ bindings?: Record<string, string>;
50
+ } | undefined): ParseWhereClauseResult;
51
+ export type ParserState = {
52
+ bindings: Record<string, string>;
53
+ index: number;
54
+ where_clause: string;
55
+ };
56
+ export type ParseFailureResult = {
57
+ diagnostic: PatramDiagnostic;
58
+ success: false;
59
+ };
60
+ export type ParseExpressionResult = {
61
+ expression: ParsedExpression;
62
+ success: true;
63
+ } | ParseFailureResult;
64
+ export type ParseTermResult = {
65
+ success: true;
66
+ term: ParsedTerm;
67
+ } | ParseFailureResult;
68
+ export type ParseValueResult = {
69
+ success: true;
70
+ value: string;
71
+ } | ParseFailureResult;
72
+ import type { ParseWhereClauseResult } from './parse-where-clause.types.ts';
73
+ import type { PatramDiagnostic } from './load-patram-config.types.ts';
74
+ import type { ParsedExpression } from './parse-where-clause.types.ts';
75
+ import type { ParsedTerm } from './parse-where-clause.types.ts';