patram 0.11.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 (110) hide show
  1. package/bin/patram.js +4 -4
  2. package/lib/cli/commands/fields.js +0 -4
  3. package/lib/cli/commands/queries.js +10 -20
  4. package/lib/cli/commands/query.js +1 -8
  5. package/lib/cli/commands/refs.js +3 -10
  6. package/lib/cli/commands/show.js +1 -8
  7. package/lib/cli/help-metadata.js +71 -106
  8. package/lib/cli/main.js +10 -10
  9. package/lib/cli/parse-arguments-helpers.js +165 -59
  10. package/lib/cli/parse-arguments.js +4 -4
  11. package/lib/cli/render-help.js +2 -2
  12. package/lib/config/defaults.js +33 -25
  13. package/lib/config/load-patram-config.d.ts +8 -33
  14. package/lib/config/load-patram-config.js +9 -33
  15. package/lib/config/load-patram-config.types.d.ts +3 -40
  16. package/lib/config/manage-stored-queries-helpers.d.ts +4 -4
  17. package/lib/config/manage-stored-queries-helpers.js +91 -33
  18. package/lib/config/manage-stored-queries.d.ts +4 -4
  19. package/lib/config/manage-stored-queries.js +11 -5
  20. package/lib/config/patram-config.d.ts +34 -34
  21. package/lib/config/patram-config.js +3 -3
  22. package/lib/config/patram-config.types.d.ts +5 -11
  23. package/lib/config/resolve-patram-graph-config.d.ts +5 -1
  24. package/lib/config/resolve-patram-graph-config.js +3 -119
  25. package/lib/config/schema.d.ts +158 -269
  26. package/lib/config/schema.js +72 -210
  27. package/lib/config/validate-patram-config-value.js +6 -31
  28. package/lib/config/validation.d.ts +2 -12
  29. package/lib/config/validation.js +125 -483
  30. package/lib/find-close-match.d.ts +4 -1
  31. package/lib/graph/build-graph-identity.d.ts +1 -32
  32. package/lib/graph/build-graph-identity.js +5 -269
  33. package/lib/graph/build-graph.d.ts +13 -4
  34. package/lib/graph/build-graph.js +347 -488
  35. package/lib/graph/build-graph.types.d.ts +8 -9
  36. package/lib/graph/check-directive-metadata-helpers.d.ts +30 -0
  37. package/lib/graph/check-directive-metadata-helpers.js +126 -0
  38. package/lib/graph/check-directive-metadata.d.ts +8 -9
  39. package/lib/graph/check-directive-metadata.js +70 -561
  40. package/lib/graph/check-directive-path-target.d.ts +6 -13
  41. package/lib/graph/check-directive-path-target.js +26 -57
  42. package/lib/graph/check-directive-value.d.ts +1 -5
  43. package/lib/graph/check-directive-value.js +40 -180
  44. package/lib/graph/check-graph.d.ts +5 -5
  45. package/lib/graph/check-graph.js +8 -6
  46. package/lib/graph/document-node-identity.d.ts +23 -7
  47. package/lib/graph/document-node-identity.js +417 -160
  48. package/lib/graph/graph-node.d.ts +42 -0
  49. package/lib/graph/graph-node.js +83 -0
  50. package/lib/graph/inspect-reverse-references.js +16 -11
  51. package/lib/graph/load-project-graph.d.ts +7 -7
  52. package/lib/graph/load-project-graph.js +7 -7
  53. package/lib/graph/parse-where-clause.types.d.ts +3 -2
  54. package/lib/graph/query/cypher-reader.d.ts +59 -0
  55. package/lib/graph/query/cypher-reader.js +151 -0
  56. package/lib/graph/query/cypher-support.d.ts +79 -0
  57. package/lib/graph/query/cypher-support.js +213 -0
  58. package/lib/graph/query/cypher-tokenize.d.ts +13 -0
  59. package/lib/graph/query/cypher-tokenize.js +225 -0
  60. package/lib/graph/query/cypher.types.d.ts +43 -0
  61. package/lib/graph/query/execute.d.ts +7 -7
  62. package/lib/graph/query/execute.js +71 -33
  63. package/lib/graph/query/inspect.js +58 -24
  64. package/lib/graph/query/parse-cypher-patterns.d.ts +27 -0
  65. package/lib/graph/query/parse-cypher-patterns.js +382 -0
  66. package/lib/graph/query/parse-cypher.d.ts +7 -0
  67. package/lib/graph/query/parse-cypher.js +580 -0
  68. package/lib/graph/query/parse-query.d.ts +13 -0
  69. package/lib/graph/query/parse-query.js +97 -0
  70. package/lib/graph/query/resolve.js +77 -23
  71. package/lib/output/command-output.js +12 -5
  72. package/lib/output/compact-layout.js +221 -0
  73. package/lib/output/format-output-item-block.js +31 -1
  74. package/lib/output/format-output-metadata.js +16 -29
  75. package/lib/output/format-stored-query-block.js +95 -0
  76. package/lib/output/layout-incoming-references.js +101 -19
  77. package/lib/output/layout-stored-queries.js +23 -330
  78. package/lib/output/list-queries.js +1 -1
  79. package/lib/output/render-field-discovery.js +11 -2
  80. package/lib/output/render-output-view.js +9 -5
  81. package/lib/output/renderers/json.js +5 -26
  82. package/lib/output/renderers/plain.js +155 -35
  83. package/lib/output/renderers/rich.js +250 -36
  84. package/lib/output/resolved-link-layout.js +43 -0
  85. package/lib/output/rich-source/render.js +193 -35
  86. package/lib/output/show-document.js +25 -18
  87. package/lib/output/view-model/index.js +124 -103
  88. package/lib/parse/jsdoc/parse-jsdoc-blocks.js +1 -1
  89. package/lib/parse/jsdoc/parse-jsdoc-claims.js +12 -6
  90. package/lib/parse/markdown/parse-markdown-claims.js +99 -62
  91. package/lib/parse/markdown/parse-markdown-directives.d.ts +10 -6
  92. package/lib/parse/markdown/parse-markdown-directives.js +104 -18
  93. package/lib/parse/markdown/parse-markdown-prose.d.ts +27 -0
  94. package/lib/parse/markdown/parse-markdown-prose.js +243 -0
  95. package/lib/parse/parse-claims.d.ts +2 -6
  96. package/lib/parse/parse-claims.js +11 -53
  97. package/lib/parse/tagged-fenced/tagged-fenced-blocks.d.ts +4 -4
  98. package/lib/parse/tagged-fenced/tagged-fenced-blocks.js +4 -4
  99. package/lib/parse/yaml/parse-yaml-claims.js +4 -4
  100. package/lib/patram.d.ts +3 -5
  101. package/lib/patram.js +1 -1
  102. package/lib/scan/discover-fields.js +194 -55
  103. package/lib/scan/list-source-files.d.ts +4 -4
  104. package/lib/scan/list-source-files.js +4 -4
  105. package/package.json +1 -1
  106. package/lib/directive-validation-test-helpers.js +0 -87
  107. package/lib/graph/query/parse.d.ts +0 -75
  108. package/lib/graph/query/parse.js +0 -1064
  109. package/lib/output/derived-summary.js +0 -280
  110. package/lib/output/format-derived-summary-row.js +0 -9
@@ -0,0 +1,382 @@
1
+ /* eslint-disable max-lines */
2
+ /**
3
+ * @import {
4
+ * ParsedAggregateTerm,
5
+ * ParsedExpression,
6
+ * ParsedTraversalTerm,
7
+ * } from '../parse-where-clause.types.ts';
8
+ * @import {
9
+ * CypherAggregateResult,
10
+ * CypherExpressionResult,
11
+ * CypherNodePattern,
12
+ * CypherParserState,
13
+ * CypherRelationPattern,
14
+ * CypherSubqueryShapeResult,
15
+ * CypherToken,
16
+ * } from './cypher.types.ts';
17
+ */
18
+
19
+ import {
20
+ collapseAndExpressions,
21
+ createAlwaysTrueExpression,
22
+ createNodeLabelExpression,
23
+ relationTypeToRelationName,
24
+ } from './cypher-support.js';
25
+ import {
26
+ consumeKeyword,
27
+ consumeSymbol,
28
+ consumeToken,
29
+ expectKeyword,
30
+ expectSymbol,
31
+ failAtCurrent,
32
+ peekSymbolAt,
33
+ } from './cypher-reader.js';
34
+
35
+ /**
36
+ * @param {CypherParserState} parser_state
37
+ * @param {number} column
38
+ * @param {'any' | 'count'} aggregate_name
39
+ * @param {(parser_state: CypherParserState) => CypherExpressionResult} parse_boolean_expression
40
+ * @returns {CypherAggregateResult}
41
+ */
42
+ export function parseSubqueryAggregate(
43
+ parser_state,
44
+ column,
45
+ aggregate_name,
46
+ parse_boolean_expression,
47
+ ) {
48
+ expectSymbol(parser_state, '{');
49
+ expectKeyword(parser_state, 'MATCH');
50
+
51
+ const first_node = parseNodePattern(parser_state);
52
+ const relation_pattern = parseRelationPattern(parser_state);
53
+ const second_node = parseNodePattern(parser_state);
54
+ const subquery_shape = resolveSubqueryShape(
55
+ parser_state,
56
+ first_node,
57
+ relation_pattern,
58
+ second_node,
59
+ );
60
+
61
+ if (!subquery_shape.success) {
62
+ return subquery_shape;
63
+ }
64
+
65
+ /** @type {ParsedExpression[]} */
66
+ const nested_expressions = [];
67
+ const label_expression = createNodeLabelExpression(
68
+ subquery_shape.related_node,
69
+ parser_state.repo_config,
70
+ );
71
+
72
+ if (label_expression) {
73
+ nested_expressions.push(label_expression);
74
+ }
75
+
76
+ if (consumeKeyword(parser_state, 'WHERE')) {
77
+ const previous_root_variable_name = parser_state.root_variable_name;
78
+ parser_state.root_variable_name =
79
+ subquery_shape.related_node.variable_name ?? previous_root_variable_name;
80
+ const nested_result = parse_boolean_expression(parser_state);
81
+ parser_state.root_variable_name = previous_root_variable_name;
82
+
83
+ if (!nested_result.success) {
84
+ return nested_result;
85
+ }
86
+
87
+ nested_expressions.push(nested_result.expression);
88
+ }
89
+
90
+ expectSymbol(parser_state, '}');
91
+
92
+ return {
93
+ success: true,
94
+ term: createAggregateTerm(
95
+ aggregate_name,
96
+ column,
97
+ nested_expressions,
98
+ subquery_shape.traversal,
99
+ ),
100
+ };
101
+ }
102
+
103
+ /**
104
+ * @param {CypherParserState} parser_state
105
+ * @returns {CypherNodePattern}
106
+ */
107
+ export function parseNodePattern(parser_state) {
108
+ const open_token = expectSymbol(parser_state, '(');
109
+ const identifier_token = consumeOptionalIdentifier(parser_state);
110
+ const variable_name = identifier_token?.value ?? null;
111
+ let label_name = null;
112
+
113
+ if (
114
+ variable_name &&
115
+ !peekSymbolAt(parser_state, 0, ':') &&
116
+ !peekSymbolAt(parser_state, 0, ')')
117
+ ) {
118
+ throw new Error('Unexpected token while parsing a node pattern.');
119
+ }
120
+
121
+ if (consumeSymbol(parser_state, ':')) {
122
+ label_name = parseIdentifierValue(parser_state, 'Expected a label name.');
123
+ }
124
+
125
+ expectSymbol(parser_state, ')');
126
+
127
+ return {
128
+ column: open_token.column,
129
+ label_name,
130
+ variable_name,
131
+ };
132
+ }
133
+
134
+ /**
135
+ * @param {CypherParserState} parser_state
136
+ * @returns {CypherRelationPattern}
137
+ */
138
+ function parseRelationPattern(parser_state) {
139
+ const first_token = consumeToken(parser_state);
140
+
141
+ if (!first_token || first_token.kind !== 'symbol') {
142
+ throw new Error('Expected a relationship pattern.');
143
+ }
144
+
145
+ const direction = resolveRelationDirection(first_token.value);
146
+
147
+ expectSymbol(parser_state, '[');
148
+ expectSymbol(parser_state, ':');
149
+ const relation_name = parseIdentifierValue(
150
+ parser_state,
151
+ 'Expected a relationship type.',
152
+ );
153
+
154
+ expectSymbol(parser_state, ']');
155
+ expectSymbol(parser_state, direction === 'out' ? '->' : '-');
156
+
157
+ return {
158
+ column: first_token.column,
159
+ direction,
160
+ relation_name: relationTypeToRelationName(relation_name),
161
+ };
162
+ }
163
+
164
+ /**
165
+ * @param {CypherParserState} parser_state
166
+ * @returns {string}
167
+ */
168
+ export function parseCypherScalarValue(parser_state) {
169
+ if (consumeSymbol(parser_state, '@')) {
170
+ return resolveBindingValue(parser_state);
171
+ }
172
+
173
+ const token = consumeToken(parser_state);
174
+
175
+ if (
176
+ token &&
177
+ (token.kind === 'identifier' ||
178
+ token.kind === 'number' ||
179
+ token.kind === 'string')
180
+ ) {
181
+ return token.value;
182
+ }
183
+
184
+ throw new Error('Expected a scalar value.');
185
+ }
186
+
187
+ /**
188
+ * @param {CypherParserState} parser_state
189
+ * @returns {string[]}
190
+ */
191
+ export function parseCypherListValue(parser_state) {
192
+ expectSymbol(parser_state, '[');
193
+ /** @type {string[]} */
194
+ const values = [];
195
+
196
+ while (!consumeSymbol(parser_state, ']')) {
197
+ values.push(parseCypherScalarValue(parser_state));
198
+
199
+ if (consumeSymbol(parser_state, ']')) {
200
+ break;
201
+ }
202
+
203
+ expectSymbol(parser_state, ',');
204
+ }
205
+
206
+ return values;
207
+ }
208
+
209
+ /**
210
+ * @param {CypherParserState} parser_state
211
+ * @param {CypherNodePattern} first_node
212
+ * @param {CypherRelationPattern} relation_pattern
213
+ * @param {CypherNodePattern} second_node
214
+ * @returns {CypherSubqueryShapeResult}
215
+ */
216
+ function resolveSubqueryShape(
217
+ parser_state,
218
+ first_node,
219
+ relation_pattern,
220
+ second_node,
221
+ ) {
222
+ const root_variable_name = parser_state.root_variable_name;
223
+
224
+ if (!root_variable_name) {
225
+ throw new Error('Expected a root query variable.');
226
+ }
227
+
228
+ if (firstNodeIsRoot(first_node, root_variable_name)) {
229
+ return {
230
+ related_node: second_node,
231
+ success: true,
232
+ traversal: createTraversalTerm(
233
+ relation_pattern.column,
234
+ relation_pattern.direction,
235
+ relation_pattern.relation_name,
236
+ ),
237
+ };
238
+ }
239
+
240
+ if (firstNodeIsRoot(second_node, root_variable_name)) {
241
+ return {
242
+ related_node: first_node,
243
+ success: true,
244
+ traversal: createTraversalTerm(
245
+ relation_pattern.column,
246
+ invertDirection(relation_pattern.direction),
247
+ relation_pattern.relation_name,
248
+ ),
249
+ };
250
+ }
251
+
252
+ return failAtCurrent(
253
+ parser_state,
254
+ 'Cypher subqueries must traverse to or from the root MATCH variable.',
255
+ );
256
+ }
257
+
258
+ /**
259
+ * @param {'any' | 'count'} aggregate_name
260
+ * @param {number} column
261
+ * @param {ParsedExpression[]} nested_expressions
262
+ * @param {ParsedTraversalTerm} traversal
263
+ * @returns {ParsedAggregateTerm}
264
+ */
265
+ function createAggregateTerm(
266
+ aggregate_name,
267
+ column,
268
+ nested_expressions,
269
+ traversal,
270
+ ) {
271
+ return {
272
+ aggregate_name,
273
+ expression: collapseAndExpressions(
274
+ nested_expressions.length > 0
275
+ ? nested_expressions
276
+ : [createAlwaysTrueExpression(column)],
277
+ ),
278
+ kind: 'aggregate',
279
+ traversal,
280
+ };
281
+ }
282
+
283
+ /**
284
+ * @param {number} column
285
+ * @param {'in' | 'out'} direction
286
+ * @param {string} relation_name
287
+ * @returns {ParsedTraversalTerm}
288
+ */
289
+ function createTraversalTerm(column, direction, relation_name) {
290
+ return {
291
+ column,
292
+ direction,
293
+ relation_name,
294
+ };
295
+ }
296
+
297
+ /**
298
+ * @param {CypherParserState} parser_state
299
+ * @returns {CypherToken | undefined}
300
+ */
301
+ function consumeOptionalIdentifier(parser_state) {
302
+ const token = consumeToken(parser_state);
303
+
304
+ if (!token) {
305
+ return undefined;
306
+ }
307
+
308
+ if (token.kind === 'identifier') {
309
+ return token;
310
+ }
311
+
312
+ parser_state.index -= 1;
313
+ return undefined;
314
+ }
315
+
316
+ /**
317
+ * @param {CypherParserState} parser_state
318
+ * @param {string} message
319
+ * @returns {string}
320
+ */
321
+ function parseIdentifierValue(parser_state, message) {
322
+ const token = consumeToken(parser_state);
323
+
324
+ if (!token || token.kind !== 'identifier') {
325
+ throw new Error(message);
326
+ }
327
+
328
+ return token.value;
329
+ }
330
+
331
+ /**
332
+ * @param {CypherParserState} parser_state
333
+ * @returns {string}
334
+ */
335
+ function resolveBindingValue(parser_state) {
336
+ const token = consumeToken(parser_state);
337
+
338
+ if (!token || token.kind !== 'identifier') {
339
+ throw new Error('Expected a query binding name.');
340
+ }
341
+
342
+ const binding_value = parser_state.bindings[token.value];
343
+
344
+ if (binding_value === undefined) {
345
+ throw new Error(`Missing query binding "${token.value}".`);
346
+ }
347
+
348
+ return binding_value;
349
+ }
350
+
351
+ /**
352
+ * @param {CypherNodePattern} node_pattern
353
+ * @param {string} root_variable_name
354
+ * @returns {boolean}
355
+ */
356
+ function firstNodeIsRoot(node_pattern, root_variable_name) {
357
+ return node_pattern.variable_name === root_variable_name;
358
+ }
359
+
360
+ /**
361
+ * @param {'in' | 'out'} direction
362
+ * @returns {'in' | 'out'}
363
+ */
364
+ function invertDirection(direction) {
365
+ return direction === 'in' ? 'out' : 'in';
366
+ }
367
+
368
+ /**
369
+ * @param {string} token_value
370
+ * @returns {'in' | 'out'}
371
+ */
372
+ function resolveRelationDirection(token_value) {
373
+ if (token_value === '<-') {
374
+ return 'in';
375
+ }
376
+
377
+ if (token_value === '-') {
378
+ return 'out';
379
+ }
380
+
381
+ throw new Error('Expected a relationship direction.');
382
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @param {CypherParserState} parser_state
3
+ * @returns {ParseWhereClauseResult}
4
+ */
5
+ export function parseCypherExpression(parser_state: CypherParserState): ParseWhereClauseResult;
6
+ import type { CypherParserState } from './cypher.types.d.ts';
7
+ import type { ParseWhereClauseResult } from '../parse-where-clause.types.d.ts';