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
package/lib/patram.d.ts CHANGED
@@ -3,52 +3,53 @@ export {
3
3
  loadTaggedFencedBlocks,
4
4
  selectTaggedBlock,
5
5
  selectTaggedBlocks,
6
- } from './tagged-fenced-blocks.js';
6
+ } from './parse/tagged-fenced/tagged-fenced-blocks.js';
7
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';
8
+ export { parseWhereClause } from './graph/query/parse.js';
9
+ export { getQuerySemanticDiagnostics } from './graph/query/inspect.js';
10
+ export { loadProjectGraph } from './graph/load-project-graph.js';
11
+ export { queryGraph } from './graph/query/execute.js';
13
12
 
14
- export type PatramGraphNode = import('./build-graph.types.ts').GraphNode;
15
- export type PatramGraphEdge = import('./build-graph.types.ts').GraphEdge;
13
+ export type PatramGraphNode =
14
+ import('./graph/build-graph.types.d.ts').GraphNode;
15
+ export type PatramGraphEdge =
16
+ import('./graph/build-graph.types.d.ts').GraphEdge;
16
17
  export type PatramBuildGraphResult =
17
- import('./build-graph.types.ts').BuildGraphResult;
18
+ import('./graph/build-graph.types.d.ts').BuildGraphResult;
18
19
  export type PatramDiagnostic =
19
- import('./load-patram-config.types.ts').PatramDiagnostic;
20
+ import('./config/load-patram-config.types.d.ts').PatramDiagnostic;
20
21
  export type PatramRepoConfig =
21
- import('./load-patram-config.types.ts').PatramRepoConfig;
22
+ import('./config/load-patram-config.types.d.ts').PatramRepoConfig;
22
23
  export type PatramParsedFieldName =
23
- import('./parse-where-clause.types.ts').ParsedFieldName;
24
+ import('./graph/parse-where-clause.types.d.ts').ParsedFieldName;
24
25
  export type PatramParsedFieldTerm =
25
- import('./parse-where-clause.types.ts').ParsedFieldTerm;
26
+ import('./graph/parse-where-clause.types.d.ts').ParsedFieldTerm;
26
27
  export type PatramParsedFieldSetTerm =
27
- import('./parse-where-clause.types.ts').ParsedFieldSetTerm;
28
+ import('./graph/parse-where-clause.types.d.ts').ParsedFieldSetTerm;
28
29
  export type PatramParsedTraversalTerm =
29
- import('./parse-where-clause.types.ts').ParsedTraversalTerm;
30
+ import('./graph/parse-where-clause.types.d.ts').ParsedTraversalTerm;
30
31
  export type PatramParsedRelationTerm =
31
- import('./parse-where-clause.types.ts').ParsedRelationTerm;
32
+ import('./graph/parse-where-clause.types.d.ts').ParsedRelationTerm;
32
33
  export type PatramParsedRelationTargetTerm =
33
- import('./parse-where-clause.types.ts').ParsedRelationTargetTerm;
34
+ import('./graph/parse-where-clause.types.d.ts').ParsedRelationTargetTerm;
34
35
  export type PatramParsedAggregateComparison =
35
- import('./parse-where-clause.types.ts').ParsedAggregateComparison;
36
+ import('./graph/parse-where-clause.types.d.ts').ParsedAggregateComparison;
36
37
  export type PatramParsedAggregateName =
37
- import('./parse-where-clause.types.ts').ParsedAggregateName;
38
+ import('./graph/parse-where-clause.types.d.ts').ParsedAggregateName;
38
39
  export type PatramParsedAggregateTerm =
39
- import('./parse-where-clause.types.ts').ParsedAggregateTerm;
40
+ import('./graph/parse-where-clause.types.d.ts').ParsedAggregateTerm;
40
41
  export type PatramParsedTermExpression =
41
- import('./parse-where-clause.types.ts').ParsedTermExpression;
42
+ import('./graph/parse-where-clause.types.d.ts').ParsedTermExpression;
42
43
  export type PatramParsedNotExpression =
43
- import('./parse-where-clause.types.ts').ParsedNotExpression;
44
+ import('./graph/parse-where-clause.types.d.ts').ParsedNotExpression;
44
45
  export type PatramParsedBooleanExpression =
45
- import('./parse-where-clause.types.ts').ParsedBooleanExpression;
46
+ import('./graph/parse-where-clause.types.d.ts').ParsedBooleanExpression;
46
47
  export type PatramParsedTerm =
47
- import('./parse-where-clause.types.ts').ParsedTerm;
48
+ import('./graph/parse-where-clause.types.d.ts').ParsedTerm;
48
49
  export type PatramParsedExpression =
49
- import('./parse-where-clause.types.ts').ParsedExpression;
50
+ import('./graph/parse-where-clause.types.d.ts').ParsedExpression;
50
51
  export type PatramParseWhereClauseResult =
51
- import('./parse-where-clause.types.ts').ParseWhereClauseResult;
52
+ import('./graph/parse-where-clause.types.d.ts').ParseWhereClauseResult;
52
53
  export type PatramQuerySource =
53
54
  | {
54
55
  kind: 'ad_hoc';
@@ -59,8 +60,8 @@ export type PatramQuerySource =
59
60
  };
60
61
 
61
62
  export interface PatramProjectGraphResult {
62
- claims: import('./parse-claims.types.ts').PatramClaim[];
63
- config: import('./load-patram-config.types.ts').PatramRepoConfig;
63
+ claims: import('./parse/parse-claims.types.d.ts').PatramClaim[];
64
+ config: import('./config/load-patram-config.types.d.ts').PatramRepoConfig;
64
65
  diagnostics: PatramDiagnostic[];
65
66
  graph: PatramBuildGraphResult;
66
67
  source_file_paths: string[];
package/lib/patram.js CHANGED
@@ -3,10 +3,9 @@ export {
3
3
  loadTaggedFencedBlocks,
4
4
  selectTaggedBlock,
5
5
  selectTaggedBlocks,
6
- } from './tagged-fenced-blocks.js';
6
+ } from './parse/tagged-fenced/tagged-fenced-blocks.js';
7
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';
8
+ export { parseWhereClause } from './graph/query/parse.js';
9
+ export { getQuerySemanticDiagnostics } from './graph/query/inspect.js';
10
+ export { loadProjectGraph } from './graph/load-project-graph.js';
11
+ export { queryGraph } from './graph/query/execute.js';
@@ -1,6 +1,6 @@
1
1
  /* eslint-disable max-lines, max-lines-per-function */
2
2
  /**
3
- * @import { ClaimOrigin, PatramClaim } from './parse-claims.types.ts';
3
+ * @import { ClaimOrigin, PatramClaim } from '../parse/parse-claims.types.ts';
4
4
  * @import {
5
5
  * DiscoveredFieldMultiplicity,
6
6
  * DiscoveredFieldTypeName,
@@ -17,9 +17,14 @@ import { readFile } from 'node:fs/promises';
17
17
  import process from 'node:process';
18
18
  import { resolve } from 'node:path';
19
19
 
20
- import { DEFAULT_INCLUDE_PATTERNS } from './source-file-defaults.js';
20
+ import { DEFAULT_INCLUDE_PATTERNS } from '../config/source-file-defaults.js';
21
21
  import { listSourceFiles } from './list-source-files.js';
22
- import { parseSourceFile } from './parse-claims.js';
22
+ import { parseSourceFile } from '../parse/parse-claims.js';
23
+ import {
24
+ matchHiddenDirectiveFields,
25
+ matchVisibleDirectiveFields,
26
+ } from '../parse/markdown/parse-markdown-directives.js';
27
+ import { isPathLikeTarget } from '../parse/claim-helpers.js';
23
28
 
24
29
  /**
25
30
  * Field discovery from source claims.
@@ -29,10 +34,10 @@ import { parseSourceFile } from './parse-claims.js';
29
34
  *
30
35
  * Kind: discovery
31
36
  * Status: active
32
- * Tracked in: ../docs/plans/v1/field-model-redesign.md
33
- * Decided by: ../docs/decisions/field-discovery-workflow.md
37
+ * Tracked in: ../../docs/plans/v1/field-model-redesign.md
38
+ * Decided by: ../../docs/decisions/field-discovery-workflow.md
34
39
  * @patram
35
- * @see {@link ./render-field-discovery.js}
40
+ * @see {@link ../output/render-field-discovery.js}
36
41
  */
37
42
 
38
43
  const TYPE_NAME_ORDER = /** @type {const} */ ([
@@ -71,10 +76,9 @@ export async function discoverFields(
71
76
  options,
72
77
  ) {
73
78
  const defined_field_names = options?.defined_field_names ?? new Set();
74
- const source_file_paths = await listSourceFiles(
75
- DEFAULT_INCLUDE_PATTERNS,
76
- project_directory,
77
- );
79
+ const source_file_paths = (
80
+ await listSourceFiles(DEFAULT_INCLUDE_PATTERNS, project_directory)
81
+ ).filter((source_file_path) => source_file_path.includes('/'));
78
82
  const parse_results = await Promise.all(
79
83
  source_file_paths.map(async (source_file_path) => {
80
84
  const source_text = await readFile(
@@ -88,6 +92,7 @@ export async function discoverFields(
88
92
  source: source_text,
89
93
  }).claims,
90
94
  path: source_file_path,
95
+ source_text,
91
96
  };
92
97
  }),
93
98
  );
@@ -107,13 +112,20 @@ export async function discoverFields(
107
112
  }
108
113
  }
109
114
 
115
+ const allowed_markdown_lines = collectAllowedMarkdownDirectiveLines(
116
+ parse_result.path,
117
+ parse_result.source_text,
118
+ parse_result.claims,
119
+ );
120
+
110
121
  return parse_result.claims.flatMap((claim) => {
111
122
  if (
112
123
  claim.type !== 'directive' ||
113
124
  !claim.name ||
114
125
  claim.name.startsWith('$') ||
115
126
  typeof claim.value !== 'string' ||
116
- claim.value.length === 0
127
+ claim.value.length === 0 ||
128
+ !shouldIncludeDiscoveryClaim(claim, allowed_markdown_lines)
117
129
  ) {
118
130
  return [];
119
131
  }
@@ -123,6 +135,7 @@ export async function discoverFields(
123
135
  class_names: new Set(document_classes),
124
136
  document_id: claim.document_id,
125
137
  name: claim.name,
138
+ normalized_value: normalizeDiscoveryValue(claim.value),
126
139
  origin: claim.origin,
127
140
  value: claim.value,
128
141
  },
@@ -132,6 +145,7 @@ export async function discoverFields(
132
145
  /** @type {Map<string, FieldBucket>} */
133
146
  const field_buckets = field_observations.reduce(
134
147
  (buckets, field_observation) => {
148
+ /** @type {FieldBucket} */
135
149
  const bucket = buckets.get(field_observation.name) ?? {
136
150
  name: field_observation.name,
137
151
  observations: [],
@@ -141,12 +155,14 @@ export async function discoverFields(
141
155
  buckets.set(field_observation.name, bucket);
142
156
  return buckets;
143
157
  },
144
- new Map(),
158
+ /** @type {Map<string, FieldBucket>} */ (new Map()),
145
159
  );
146
160
  const fields = [...field_buckets.values()]
147
161
  .map(buildFieldSuggestion)
148
162
  .filter(
149
- (field_suggestion) => !defined_field_names.has(field_suggestion.name),
163
+ (field_suggestion) =>
164
+ !defined_field_names.has(field_suggestion.name) &&
165
+ isPlausibleFieldName(field_suggestion.name),
150
166
  )
151
167
  .sort((left_suggestion, right_suggestion) =>
152
168
  left_suggestion.confidence !== right_suggestion.confidence
@@ -181,7 +197,10 @@ function buildFieldSuggestion(field_bucket) {
181
197
  const conflicting_evidence = buildEvidenceReferences(
182
198
  field_bucket.observations.filter(
183
199
  (field_observation) =>
184
- scoreFieldValue(field_observation.value, type_result.name) === 0,
200
+ scoreFieldValue(
201
+ field_observation.normalized_value,
202
+ type_result.name,
203
+ ) === 0,
185
204
  ),
186
205
  );
187
206
 
@@ -227,16 +246,17 @@ function buildEvidenceReferences(observations) {
227
246
  function inferFieldMultiplicity(observations) {
228
247
  /** @type {Map<string, Set<string>>} */
229
248
  const values_by_document = observations.reduce((values, observation) => {
249
+ const normalized_value = observation.normalized_value;
230
250
  const current_values = values.get(observation.document_id);
231
251
 
232
252
  if (current_values) {
233
- current_values.add(observation.value);
253
+ current_values.add(normalized_value);
234
254
  } else {
235
- values.set(observation.document_id, new Set([observation.value]));
255
+ values.set(observation.document_id, new Set([normalized_value]));
236
256
  }
237
257
 
238
258
  return values;
239
- }, new Map());
259
+ }, /** @type {Map<string, Set<string>>} */ (new Map()));
240
260
  const repeated_identical_documents = [...values_by_document.values()].reduce(
241
261
  (count, values) => {
242
262
  if (values.size > 1) {
@@ -368,7 +388,7 @@ function scoreFieldType(observations, field_type_name) {
368
388
 
369
389
  const total_score = observations.reduce(
370
390
  (sum, observation) =>
371
- sum + scoreFieldValue(observation.value, field_type_name),
391
+ sum + scoreFieldValue(observation.normalized_value, field_type_name),
372
392
  0,
373
393
  );
374
394
 
@@ -422,6 +442,7 @@ const FIELD_TYPE_SCORERS = {
422
442
  * class_names: Set<string>,
423
443
  * document_id: string,
424
444
  * name: string,
445
+ * normalized_value: string,
425
446
  * origin: ClaimOrigin,
426
447
  * value: string,
427
448
  * }} FieldObservation
@@ -433,3 +454,109 @@ const FIELD_TYPE_SCORERS = {
433
454
  * observations: FieldObservation[],
434
455
  * }} FieldBucket
435
456
  */
457
+
458
+ /**
459
+ * @param {PatramClaim} claim
460
+ * @param {Set<number> | null} allowed_markdown_lines
461
+ * @returns {boolean}
462
+ */
463
+ function shouldIncludeDiscoveryClaim(claim, allowed_markdown_lines) {
464
+ if (claim.parser !== 'markdown') {
465
+ return true;
466
+ }
467
+
468
+ if (claim.markdown_style === 'front_matter') {
469
+ return true;
470
+ }
471
+
472
+ return allowed_markdown_lines?.has(claim.origin.line) ?? false;
473
+ }
474
+
475
+ /**
476
+ * @param {string} file_path
477
+ * @param {string} source_text
478
+ * @param {PatramClaim[]} claims
479
+ * @returns {Set<number> | null}
480
+ */
481
+ function collectAllowedMarkdownDirectiveLines(file_path, source_text, claims) {
482
+ if (!file_path.endsWith('.md')) {
483
+ return null;
484
+ }
485
+
486
+ const title_claim = claims.find((claim) => claim.type === 'document.title');
487
+
488
+ if (!title_claim) {
489
+ return new Set();
490
+ }
491
+
492
+ const lines = source_text.split('\n');
493
+ /** @type {Set<number>} */
494
+ const allowed_lines = new Set();
495
+
496
+ for (
497
+ let line_index = title_claim.origin.line;
498
+ line_index < lines.length;
499
+ line_index += 1
500
+ ) {
501
+ const line = lines[line_index];
502
+
503
+ if (line.trim().length === 0) {
504
+ continue;
505
+ }
506
+
507
+ if (isMarkdownDiscoveryDirective(file_path, line, line_index + 1)) {
508
+ allowed_lines.add(line_index + 1);
509
+ continue;
510
+ }
511
+
512
+ break;
513
+ }
514
+
515
+ return allowed_lines;
516
+ }
517
+
518
+ /**
519
+ * @param {string} file_path
520
+ * @param {string} line
521
+ * @param {number} line_number
522
+ * @returns {boolean}
523
+ */
524
+ function isMarkdownDiscoveryDirective(file_path, line, line_number) {
525
+ return (
526
+ matchVisibleDirectiveFields(file_path, line, line_number) !== null ||
527
+ matchHiddenDirectiveFields(file_path, line, line_number) !== null
528
+ );
529
+ }
530
+
531
+ /**
532
+ * @param {string} value
533
+ * @returns {string}
534
+ */
535
+ function normalizeDiscoveryValue(value) {
536
+ const trimmed_value = value.trim();
537
+ const markdown_link_match = trimmed_value.match(
538
+ /^\[([^\]]+)\]\(([^)]+)\)$/du,
539
+ );
540
+
541
+ if (markdown_link_match && isPathLikeTarget(markdown_link_match[2])) {
542
+ return markdown_link_match[2];
543
+ }
544
+
545
+ const code_span_match = trimmed_value.match(/^`([^`]+)`[.,;:]?$/du);
546
+
547
+ if (code_span_match) {
548
+ return code_span_match[1];
549
+ }
550
+
551
+ return trimmed_value;
552
+ }
553
+
554
+ /**
555
+ * @param {string} field_name
556
+ * @returns {boolean}
557
+ */
558
+ function isPlausibleFieldName(field_name) {
559
+ const field_name_tokens = field_name.split('_');
560
+
561
+ return field_name.length <= 32 && field_name_tokens.length <= 4;
562
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * List repo files available for broken-link validation.
3
+ *
4
+ * @param {string} [project_directory]
5
+ * @returns {Promise<string[]>}
6
+ */
7
+ export function listRepoFiles(project_directory?: string): Promise<string[]>;
8
+ /**
9
+ * @param {string[]} include_patterns
10
+ * @param {string} project_directory
11
+ * @param {{ dot?: boolean }} [options]
12
+ * @returns {Promise<string[]>}
13
+ */
14
+ export function listMatchingFiles(include_patterns: string[], project_directory: string, options?: {
15
+ dot?: boolean;
16
+ }): Promise<string[]>;
@@ -1,39 +1,6 @@
1
- import { globby } from 'globby';
2
1
  import process from 'node:process';
3
2
 
4
- /**
5
- * Source file scanning.
6
- *
7
- * Expands include globs into stable repo-relative file lists for indexing and
8
- * broken-link validation.
9
- *
10
- * Kind: scan
11
- * Status: active
12
- * Tracked in: ../docs/plans/v0/source-anchor-dogfooding.md
13
- * Decided by: ../docs/decisions/source-scan.md
14
- * @patram
15
- * @see {@link ./load-project-graph.js}
16
- * @see {@link ../docs/decisions/source-scan.md}
17
- */
18
-
19
- /**
20
- * List source files matched by Patram include globs.
21
- *
22
- * @param {string[]} include_patterns
23
- * @param {string} [project_directory]
24
- * @returns {Promise<string[]>}
25
- */
26
- export async function listSourceFiles(
27
- include_patterns,
28
- project_directory = process.cwd(),
29
- ) {
30
- const source_file_paths = await listMatchingFiles(
31
- include_patterns,
32
- project_directory,
33
- );
34
-
35
- return [...new Set(source_file_paths)].sort(comparePaths);
36
- }
3
+ import { globby } from 'globby';
37
4
 
38
5
  /**
39
6
  * List repo files available for broken-link validation.
@@ -55,7 +22,7 @@ export async function listRepoFiles(project_directory = process.cwd()) {
55
22
  * @param {{ dot?: boolean }} [options]
56
23
  * @returns {Promise<string[]>}
57
24
  */
58
- async function listMatchingFiles(
25
+ export async function listMatchingFiles(
59
26
  include_patterns,
60
27
  project_directory,
61
28
  options = {},
@@ -6,11 +6,11 @@
6
6
  *
7
7
  * Kind: scan
8
8
  * Status: active
9
- * Tracked in: ../docs/plans/v0/source-anchor-dogfooding.md
10
- * Decided by: ../docs/decisions/source-scan.md
9
+ * Tracked in: ../../docs/plans/v0/source-anchor-dogfooding.md
10
+ * Decided by: ../../docs/decisions/source-scan.md
11
11
  * @patram
12
- * @see {@link ./load-project-graph.js}
13
- * @see {@link ../docs/decisions/source-scan.md}
12
+ * @see {@link ../graph/load-project-graph.js}
13
+ * @see {@link ../../docs/decisions/source-scan.md}
14
14
  */
15
15
  /**
16
16
  * List source files matched by Patram include globs.
@@ -20,10 +20,3 @@
20
20
  * @returns {Promise<string[]>}
21
21
  */
22
22
  export function listSourceFiles(include_patterns: string[], project_directory?: string): Promise<string[]>;
23
- /**
24
- * List repo files available for broken-link validation.
25
- *
26
- * @param {string} [project_directory]
27
- * @returns {Promise<string[]>}
28
- */
29
- export function listRepoFiles(project_directory?: string): Promise<string[]>;
@@ -0,0 +1,45 @@
1
+ import process from 'node:process';
2
+
3
+ import { listMatchingFiles } from './list-repo-files.js';
4
+ /**
5
+ * Source file scanning.
6
+ *
7
+ * Expands include globs into stable repo-relative file lists for indexing and
8
+ * broken-link validation.
9
+ *
10
+ * Kind: scan
11
+ * Status: active
12
+ * Tracked in: ../../docs/plans/v0/source-anchor-dogfooding.md
13
+ * Decided by: ../../docs/decisions/source-scan.md
14
+ * @patram
15
+ * @see {@link ../graph/load-project-graph.js}
16
+ * @see {@link ../../docs/decisions/source-scan.md}
17
+ */
18
+
19
+ /**
20
+ * List source files matched by Patram include globs.
21
+ *
22
+ * @param {string[]} include_patterns
23
+ * @param {string} [project_directory]
24
+ * @returns {Promise<string[]>}
25
+ */
26
+ export async function listSourceFiles(
27
+ include_patterns,
28
+ project_directory = process.cwd(),
29
+ ) {
30
+ const source_file_paths = await listMatchingFiles(
31
+ include_patterns,
32
+ project_directory,
33
+ );
34
+
35
+ return [...new Set(source_file_paths)].sort(comparePaths);
36
+ }
37
+
38
+ /**
39
+ * @param {string} left_path
40
+ * @param {string} right_path
41
+ * @returns {number}
42
+ */
43
+ function comparePaths(left_path, right_path) {
44
+ return left_path.localeCompare(right_path, 'en');
45
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "patram",
3
- "version": "0.8.0",
3
+ "version": "0.10.0",
4
4
  "type": "module",
5
5
  "main": "./lib/patram.js",
6
6
  "types": "./lib/patram.d.ts",
@@ -15,7 +15,6 @@
15
15
  "bin/patram.js",
16
16
  "lib/**/*.d.ts",
17
17
  "lib/**/*.js",
18
- "lib/**/*.ts",
19
18
  "!bin/**/*.test.d.ts",
20
19
  "!bin/**/*.test-helpers.d.ts",
21
20
  "!bin/**/*.test.js",
@@ -38,9 +37,11 @@
38
37
  },
39
38
  "license": "MIT",
40
39
  "scripts": {
41
- "all": "npm run check:lint && npm run check:format && npm run check:types && npm run check:patram && npm run test:coverage && npm run check:dupes",
40
+ "all": "npm run check:lint && npm run check:format && npm run check:types && npm run check:patram && npm run check:knip && npm run check:knip:production && npm run test:coverage && npm run check:dupes",
42
41
  "check:dupes": "jscpd --min-tokens 100 --min-lines 6 --mode mild --threshold 0 --reporters console --gitignore .",
43
42
  "check:format": "prettier --check .",
43
+ "check:knip": "knip",
44
+ "check:knip:production": "knip --production --include exports",
44
45
  "check:lint": "eslint .",
45
46
  "check:patram": "./bin/patram.js check",
46
47
  "check:staged": "lint-staged",
@@ -62,7 +63,6 @@
62
63
  ]
63
64
  },
64
65
  "dependencies": {
65
- "@shikijs/cli": "^4.0.2",
66
66
  "@shikijs/vscode-textmate": "^10.0.2",
67
67
  "ansis": "^4.2.0",
68
68
  "beautiful-mermaid": "^1.1.3",
@@ -77,16 +77,17 @@
77
77
  "devDependencies": {
78
78
  "@eslint/js": "^10.0.1",
79
79
  "@types/node": "^24.12.0",
80
- "@vitest/coverage-v8": "^4.1.1",
80
+ "@vitest/coverage-istanbul": "^4.1.2",
81
81
  "eslint": "^10.1.0",
82
82
  "eslint-plugin-jsdoc": "^62.8.0",
83
83
  "globals": "^17.4.0",
84
84
  "husky": "^9.1.7",
85
85
  "jscpd": "^4.0.8",
86
+ "knip": "^6.1.0",
86
87
  "lint-staged": "^16.2.6",
87
88
  "prettier": "^3.5.3",
88
- "slice-ansi": "^8.0.0",
89
- "typescript": "^6.0.2",
89
+ "typescript": "^5.9.3",
90
+ "typescript-eslint": "^8.57.2",
90
91
  "vitest": "^4.1.1"
91
92
  }
92
93
  }
@@ -1,27 +0,0 @@
1
- import type { ClaimOrigin } from './parse-claims.types.ts';
2
-
3
- export interface GraphNode {
4
- $class?: string;
5
- $id?: string;
6
- $path?: string;
7
- id: string;
8
- kind?: string;
9
- key?: string;
10
- path?: string;
11
- title?: string;
12
- [field: string]: string | string[] | undefined;
13
- }
14
-
15
- export interface GraphEdge {
16
- from: string;
17
- id: string;
18
- origin: ClaimOrigin;
19
- relation: string;
20
- to: string;
21
- }
22
-
23
- export interface BuildGraphResult {
24
- document_node_ids?: Record<string, string>;
25
- edges: GraphEdge[];
26
- nodes: Record<string, GraphNode>;
27
- }
@@ -1,52 +0,0 @@
1
- export type DiscoveredFieldTypeName =
2
- | 'date'
3
- | 'date_time'
4
- | 'enum'
5
- | 'glob'
6
- | 'integer'
7
- | 'path'
8
- | 'string';
9
-
10
- export type DiscoveredFieldMultiplicity = 'multiple' | 'single';
11
-
12
- export interface FieldDiscoveryEvidenceReference {
13
- column: number;
14
- line: number;
15
- path: string;
16
- value: string;
17
- }
18
-
19
- export interface FieldDiscoveryClassUsage {
20
- classes: string[];
21
- }
22
-
23
- export interface FieldDiscoveryTypeSuggestion {
24
- confidence: number;
25
- name: DiscoveredFieldTypeName;
26
- }
27
-
28
- export interface FieldDiscoveryMultiplicitySuggestion {
29
- confidence: number;
30
- name: DiscoveredFieldMultiplicity;
31
- }
32
-
33
- export interface FieldDiscoverySuggestion {
34
- confidence: number;
35
- conflicting_evidence: FieldDiscoveryEvidenceReference[];
36
- evidence_references: FieldDiscoveryEvidenceReference[];
37
- likely_class_usage: FieldDiscoveryClassUsage;
38
- likely_multiplicity: FieldDiscoveryMultiplicitySuggestion;
39
- likely_type: FieldDiscoveryTypeSuggestion;
40
- name: string;
41
- }
42
-
43
- export interface FieldDiscoverySummary {
44
- claim_count: number;
45
- count: number;
46
- source_file_count: number;
47
- }
48
-
49
- export interface FieldDiscoveryResult {
50
- fields: FieldDiscoverySuggestion[];
51
- summary: FieldDiscoverySummary;
52
- }