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
@@ -0,0 +1,320 @@
1
+ /**
2
+ * @import {
3
+ * PatramDiagnostic,
4
+ * StoredQueryConfig,
5
+ * } from './load-patram-config.types.ts';
6
+ */
7
+
8
+ import { readFile, writeFile } from 'node:fs/promises';
9
+
10
+ import { parsePatramConfigSource } from './load-patram-config.js';
11
+ import { applyStoredQueryMutationToConfigSource } from './manage-stored-queries-jsonc.js';
12
+ import { CONFIG_FILE_NAME } from './schema.js';
13
+ import { validatePatramConfigValue } from './validate-patram-config-value.js';
14
+
15
+ /**
16
+ * @param {string} config_file_path
17
+ * @returns {Promise<
18
+ * | { success: true, value: Record<string, unknown> }
19
+ * | { diagnostic: PatramDiagnostic, success: false }
20
+ * >}
21
+ */
22
+ export async function loadRawConfig(config_file_path) {
23
+ const config_source = await readConfigSource(config_file_path);
24
+
25
+ if (config_source === null) {
26
+ return {
27
+ success: true,
28
+ value: {},
29
+ };
30
+ }
31
+
32
+ const parse_result = parsePatramConfigSource(config_source);
33
+
34
+ if (!parse_result.success) {
35
+ return parse_result;
36
+ }
37
+
38
+ if (
39
+ parse_result.value === null ||
40
+ typeof parse_result.value !== 'object' ||
41
+ Array.isArray(parse_result.value)
42
+ ) {
43
+ return {
44
+ diagnostic: createInvalidTopLevelConfigDiagnostic(),
45
+ success: false,
46
+ };
47
+ }
48
+
49
+ return {
50
+ success: true,
51
+ value: /** @type {Record<string, unknown>} */ (parse_result.value),
52
+ };
53
+ }
54
+
55
+ /**
56
+ * @param {string} config_file_path
57
+ * @param {Record<string, unknown>} raw_config
58
+ * @param {StoredQueryMutationResult} mutation_result
59
+ * @returns {Promise<
60
+ * | { success: true, value: StoredQueryMutationResult }
61
+ * | { diagnostics: PatramDiagnostic[], success: false }
62
+ * >}
63
+ */
64
+ export async function persistStoredQueryMutation(
65
+ config_file_path,
66
+ raw_config,
67
+ mutation_result,
68
+ ) {
69
+ const validation_result = validatePatramConfigValue(raw_config);
70
+
71
+ if (!validation_result.success) {
72
+ return {
73
+ diagnostics: validation_result.diagnostics,
74
+ success: false,
75
+ };
76
+ }
77
+
78
+ const config_source = (await readConfigSource(config_file_path)) ?? '{}\n';
79
+ const raw_queries = ensureRawQueries(raw_config);
80
+ const next_config_source = applyStoredQueryMutationToConfigSource(
81
+ config_source,
82
+ raw_queries,
83
+ mutation_result,
84
+ );
85
+
86
+ await writeFile(config_file_path, next_config_source);
87
+
88
+ return {
89
+ success: true,
90
+ value: mutation_result,
91
+ };
92
+ }
93
+
94
+ /**
95
+ * @param {string} query_text
96
+ * @param {string | undefined} description
97
+ * @returns {StoredQueryConfig}
98
+ */
99
+ export function createStoredQueryDefinition(query_text, description) {
100
+ /** @type {StoredQueryConfig} */
101
+ const stored_query = {
102
+ cypher: query_text,
103
+ };
104
+
105
+ const normalized_description = normalizeDescription(description);
106
+
107
+ if (normalized_description !== undefined) {
108
+ stored_query.description = normalized_description;
109
+ }
110
+
111
+ return stored_query;
112
+ }
113
+
114
+ /**
115
+ * @param {Record<string, unknown> | null} raw_query_value
116
+ * @param {StoredQueryConfig} existing_query
117
+ * @param {{ cypher?: string, description?: string }} stored_query_mutation
118
+ * @returns {StoredQueryConfig}
119
+ */
120
+ export function createUpdatedStoredQueryDefinition(
121
+ raw_query_value,
122
+ existing_query,
123
+ stored_query_mutation,
124
+ ) {
125
+ const next_query = createBaseStoredQueryDefinition(
126
+ raw_query_value,
127
+ existing_query,
128
+ );
129
+
130
+ applyStoredQueryTextMutation(next_query, stored_query_mutation);
131
+ applyStoredQueryDescriptionMutation(next_query, stored_query_mutation);
132
+
133
+ return next_query;
134
+ }
135
+
136
+ /**
137
+ * @param {Record<string, unknown>} raw_config
138
+ * @returns {Record<string, unknown>}
139
+ */
140
+ export function ensureRawQueries(raw_config) {
141
+ const raw_queries_value = raw_config.queries;
142
+
143
+ if (
144
+ raw_queries_value !== null &&
145
+ typeof raw_queries_value === 'object' &&
146
+ !Array.isArray(raw_queries_value)
147
+ ) {
148
+ return /** @type {Record<string, unknown>} */ (raw_queries_value);
149
+ }
150
+
151
+ /** @type {Record<string, unknown>} */
152
+ const raw_queries = {};
153
+ raw_config.queries = raw_queries;
154
+
155
+ return raw_queries;
156
+ }
157
+
158
+ /**
159
+ * @param {unknown} raw_query_value
160
+ * @returns {Record<string, unknown> | null}
161
+ */
162
+ export function rawQueryValueToRecord(raw_query_value) {
163
+ if (
164
+ raw_query_value === null ||
165
+ typeof raw_query_value !== 'object' ||
166
+ Array.isArray(raw_query_value)
167
+ ) {
168
+ return null;
169
+ }
170
+
171
+ return /** @type {Record<string, unknown>} */ (raw_query_value);
172
+ }
173
+
174
+ /**
175
+ * @param {string | undefined} description
176
+ * @returns {string | undefined}
177
+ */
178
+ function normalizeDescription(description) {
179
+ if (description === undefined || description.length === 0) {
180
+ return undefined;
181
+ }
182
+
183
+ return description;
184
+ }
185
+
186
+ /**
187
+ * @param {Record<string, unknown> | null} raw_query_value
188
+ * @param {StoredQueryConfig} existing_query
189
+ * @returns {StoredQueryConfig}
190
+ */
191
+ function createBaseStoredQueryDefinition(raw_query_value, existing_query) {
192
+ /** @type {StoredQueryConfig} */
193
+ const next_query = {};
194
+
195
+ if (existing_query.cypher !== undefined) {
196
+ next_query.cypher = existing_query.cypher;
197
+ }
198
+
199
+ const description = resolveExistingDescription(
200
+ raw_query_value,
201
+ existing_query,
202
+ );
203
+
204
+ if (description !== undefined) {
205
+ next_query.description = description;
206
+ }
207
+
208
+ return next_query;
209
+ }
210
+
211
+ /**
212
+ * @param {StoredQueryConfig} next_query
213
+ * @param {{ cypher?: string, description?: string }} stored_query_mutation
214
+ */
215
+ function applyStoredQueryTextMutation(next_query, stored_query_mutation) {
216
+ if (stored_query_mutation.cypher !== undefined) {
217
+ next_query.cypher = stored_query_mutation.cypher;
218
+ }
219
+ }
220
+
221
+ /**
222
+ * @param {StoredQueryConfig} next_query
223
+ * @param {{ cypher?: string, description?: string }} stored_query_mutation
224
+ */
225
+ function applyStoredQueryDescriptionMutation(
226
+ next_query,
227
+ stored_query_mutation,
228
+ ) {
229
+ if (stored_query_mutation.description === undefined) {
230
+ return;
231
+ }
232
+
233
+ if (stored_query_mutation.description.length === 0) {
234
+ delete next_query.description;
235
+ return;
236
+ }
237
+
238
+ next_query.description = stored_query_mutation.description;
239
+ }
240
+
241
+ /**
242
+ * @param {Record<string, unknown> | null} raw_query_value
243
+ * @param {StoredQueryConfig} existing_query
244
+ * @returns {string | undefined}
245
+ */
246
+ function resolveExistingDescription(raw_query_value, existing_query) {
247
+ if (
248
+ raw_query_value &&
249
+ Object.hasOwn(raw_query_value, 'description') &&
250
+ typeof raw_query_value.description === 'string' &&
251
+ raw_query_value.description.length > 0
252
+ ) {
253
+ return raw_query_value.description;
254
+ }
255
+
256
+ if (
257
+ typeof existing_query.description === 'string' &&
258
+ existing_query.description.length > 0
259
+ ) {
260
+ return existing_query.description;
261
+ }
262
+
263
+ return undefined;
264
+ }
265
+
266
+ /**
267
+ * @returns {PatramDiagnostic}
268
+ */
269
+ function createInvalidTopLevelConfigDiagnostic() {
270
+ return {
271
+ code: 'config.invalid',
272
+ column: 1,
273
+ level: 'error',
274
+ line: 1,
275
+ message: 'Invalid config: Expected a top-level object.',
276
+ path: CONFIG_FILE_NAME,
277
+ };
278
+ }
279
+
280
+ /**
281
+ * @param {string} config_file_path
282
+ * @returns {Promise<string | null>}
283
+ */
284
+ async function readConfigSource(config_file_path) {
285
+ try {
286
+ return await readFile(config_file_path, 'utf8');
287
+ } catch (error) {
288
+ if (isMissingFileError(error)) {
289
+ return null;
290
+ }
291
+
292
+ throw error;
293
+ }
294
+ }
295
+
296
+ /**
297
+ * @param {unknown} error
298
+ * @returns {error is NodeJS.ErrnoException}
299
+ */
300
+ function isMissingFileError(error) {
301
+ if (!(error instanceof Error)) {
302
+ return false;
303
+ }
304
+
305
+ return 'code' in error && error.code === 'ENOENT';
306
+ }
307
+
308
+ /**
309
+ * @typedef {{
310
+ * action: 'added',
311
+ * name: string,
312
+ * } | {
313
+ * action: 'removed',
314
+ * name: string,
315
+ * } | {
316
+ * action: 'updated',
317
+ * name: string,
318
+ * previous_name?: string,
319
+ * }} StoredQueryMutationResult
320
+ */
@@ -0,0 +1,31 @@
1
+ /**
2
+ * @typedef {{
3
+ * action: 'added',
4
+ * name: string,
5
+ * } | {
6
+ * action: 'removed',
7
+ * name: string,
8
+ * } | {
9
+ * action: 'updated',
10
+ * name: string,
11
+ * previous_name?: string,
12
+ * }} StoredQueryMutationResult
13
+ */
14
+ /**
15
+ * @param {string} config_source
16
+ * @param {Record<string, unknown>} raw_queries
17
+ * @param {StoredQueryMutationResult} mutation_result
18
+ * @returns {string}
19
+ */
20
+ export function applyStoredQueryMutationToConfigSource(config_source: string, raw_queries: Record<string, unknown>, mutation_result: StoredQueryMutationResult): string;
21
+ export type StoredQueryMutationResult = {
22
+ action: "added";
23
+ name: string;
24
+ } | {
25
+ action: "removed";
26
+ name: string;
27
+ } | {
28
+ action: "updated";
29
+ name: string;
30
+ previous_name?: string;
31
+ };
@@ -0,0 +1,95 @@
1
+ /**
2
+ * @import { FormattingOptions } from 'jsonc-parser';
3
+ */
4
+
5
+ import { applyEdits, modify } from 'jsonc-parser';
6
+
7
+ /**
8
+ * @typedef {{
9
+ * action: 'added',
10
+ * name: string,
11
+ * } | {
12
+ * action: 'removed',
13
+ * name: string,
14
+ * } | {
15
+ * action: 'updated',
16
+ * name: string,
17
+ * previous_name?: string,
18
+ * }} StoredQueryMutationResult
19
+ */
20
+
21
+ /**
22
+ * @param {string} config_source
23
+ * @param {Record<string, unknown>} raw_queries
24
+ * @param {StoredQueryMutationResult} mutation_result
25
+ * @returns {string}
26
+ */
27
+ export function applyStoredQueryMutationToConfigSource(
28
+ config_source,
29
+ raw_queries,
30
+ mutation_result,
31
+ ) {
32
+ const formatting_options = inferFormattingOptions(config_source);
33
+ let next_config_source = config_source;
34
+
35
+ if (
36
+ mutation_result.action === 'updated' &&
37
+ mutation_result.previous_name !== undefined
38
+ ) {
39
+ next_config_source = applyConfigEdit(
40
+ next_config_source,
41
+ ['queries', mutation_result.previous_name],
42
+ undefined,
43
+ formatting_options,
44
+ );
45
+ }
46
+
47
+ if (mutation_result.action === 'removed') {
48
+ return applyConfigEdit(
49
+ next_config_source,
50
+ ['queries', mutation_result.name],
51
+ undefined,
52
+ formatting_options,
53
+ );
54
+ }
55
+
56
+ return applyConfigEdit(
57
+ next_config_source,
58
+ ['queries', mutation_result.name],
59
+ raw_queries[mutation_result.name],
60
+ formatting_options,
61
+ );
62
+ }
63
+
64
+ /**
65
+ * @param {string} config_source
66
+ * @returns {FormattingOptions}
67
+ */
68
+ function inferFormattingOptions(config_source) {
69
+ const indentation_match = config_source.match(/^(?<indentation>[ \t]+)\S/mu);
70
+ const indentation = indentation_match?.groups?.indentation ?? ' ';
71
+ const insert_spaces = !indentation.includes('\t');
72
+
73
+ return {
74
+ eol: config_source.includes('\r\n') ? '\r\n' : '\n',
75
+ insertFinalNewline: config_source.endsWith('\n'),
76
+ insertSpaces: insert_spaces,
77
+ tabSize: insert_spaces ? indentation.length : 1,
78
+ };
79
+ }
80
+
81
+ /**
82
+ * @param {string} config_source
83
+ * @param {(string | number)[]} path
84
+ * @param {unknown} value
85
+ * @param {FormattingOptions} formatting_options
86
+ * @returns {string}
87
+ */
88
+ function applyConfigEdit(config_source, path, value, formatting_options) {
89
+ return applyEdits(
90
+ config_source,
91
+ modify(config_source, path, value, {
92
+ formattingOptions: formatting_options,
93
+ }),
94
+ );
95
+ }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * @typedef {{
3
+ * action: 'add',
4
+ * cypher?: string,
5
+ * description?: string,
6
+ * name: string,
7
+ * } | {
8
+ * action: 'remove',
9
+ * name: string,
10
+ * } | {
11
+ * action: 'update',
12
+ * cypher?: string,
13
+ * description?: string,
14
+ * name: string,
15
+ * next_name?: string,
16
+ * }} StoredQueryMutation
17
+ */
18
+ /**
19
+ * @typedef {{
20
+ * action: 'added',
21
+ * name: string,
22
+ * } | {
23
+ * action: 'removed',
24
+ * name: string,
25
+ * } | {
26
+ * action: 'updated',
27
+ * name: string,
28
+ * previous_name?: string,
29
+ * }} StoredQueryMutationResult
30
+ */
31
+ /**
32
+ * @param {string} project_directory
33
+ * @param {StoredQueryMutation} stored_query_mutation
34
+ * @returns {Promise<
35
+ * | { success: true, value: StoredQueryMutationResult }
36
+ * | { diagnostics: PatramDiagnostic[], success: false }
37
+ * | { error: CliParseError, success: false }
38
+ * >}
39
+ */
40
+ export function manageStoredQueries(project_directory: string, stored_query_mutation: StoredQueryMutation): Promise<{
41
+ success: true;
42
+ value: StoredQueryMutationResult;
43
+ } | {
44
+ diagnostics: PatramDiagnostic[];
45
+ success: false;
46
+ } | {
47
+ error: CliParseError;
48
+ success: false;
49
+ }>;
50
+ export type StoredQueryMutation = {
51
+ action: "add";
52
+ cypher?: string;
53
+ description?: string;
54
+ name: string;
55
+ } | {
56
+ action: "remove";
57
+ name: string;
58
+ } | {
59
+ action: "update";
60
+ cypher?: string;
61
+ description?: string;
62
+ name: string;
63
+ next_name?: string;
64
+ };
65
+ export type StoredQueryMutationResult = {
66
+ action: "added";
67
+ name: string;
68
+ } | {
69
+ action: "removed";
70
+ name: string;
71
+ } | {
72
+ action: "updated";
73
+ name: string;
74
+ previous_name?: string;
75
+ };
76
+ import type { PatramDiagnostic } from './load-patram-config.types.d.ts';
77
+ import type { CliParseError } from '../cli/arguments.types.d.ts';