patram 0.2.0 → 0.4.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.
- package/lib/build-graph-identity.js +86 -99
- package/lib/build-graph.js +536 -31
- package/lib/build-graph.types.ts +6 -2
- package/lib/check-directive-metadata.js +534 -0
- package/lib/check-directive-value.js +291 -0
- package/lib/check-graph.js +23 -5
- package/lib/cli-help-metadata.js +56 -16
- package/lib/command-output.js +16 -1
- package/lib/derived-summary.js +10 -8
- package/lib/directive-diagnostics.js +38 -0
- package/lib/directive-type-rules.js +133 -0
- package/lib/discover-fields.js +435 -0
- package/lib/discover-fields.types.ts +52 -0
- package/lib/document-node-identity.js +317 -0
- package/lib/format-node-header.js +9 -7
- package/lib/format-output-metadata.js +15 -23
- package/lib/layout-stored-queries.js +124 -85
- package/lib/load-patram-config.js +433 -96
- package/lib/load-patram-config.types.ts +98 -3
- package/lib/load-project-graph.js +4 -1
- package/lib/output-view.types.ts +14 -6
- package/lib/parse-cli-arguments.types.ts +1 -1
- package/lib/parse-where-clause.js +344 -107
- package/lib/parse-where-clause.types.ts +25 -8
- package/lib/patram-cli.js +68 -4
- package/lib/patram-config.js +31 -31
- package/lib/patram-config.types.ts +10 -4
- package/lib/query-graph.js +269 -40
- package/lib/query-inspection.js +440 -60
- package/lib/render-field-discovery.js +184 -0
- package/lib/render-json-output.js +21 -22
- package/lib/render-output-view.js +301 -34
- package/lib/render-plain-output.js +1 -1
- package/lib/render-rich-output.js +1 -1
- package/lib/render-rich-source.js +245 -14
- package/lib/resolve-patram-graph-config.js +15 -9
- package/lib/show-document.js +66 -9
- package/package.json +5 -5
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import type { PatramDiagnostic } from './load-patram-config.types.ts';
|
|
2
2
|
|
|
3
|
-
export type ParsedFieldName =
|
|
3
|
+
export type ParsedFieldName = string;
|
|
4
4
|
|
|
5
5
|
export interface ParsedFieldTerm {
|
|
6
|
+
column: number;
|
|
6
7
|
field_name: ParsedFieldName;
|
|
7
8
|
kind: 'field';
|
|
8
|
-
operator: '=' | '^=' | '~';
|
|
9
|
+
operator: '!=' | '<' | '<=' | '=' | '>' | '>=' | '^=' | '~';
|
|
9
10
|
value: string;
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
export interface ParsedFieldSetTerm {
|
|
14
|
+
column: number;
|
|
13
15
|
field_name: ParsedFieldName;
|
|
14
16
|
kind: 'field_set';
|
|
15
17
|
operator: 'in' | 'not in';
|
|
@@ -40,13 +42,28 @@ export type ParsedAggregateName = 'any' | 'count' | 'none';
|
|
|
40
42
|
|
|
41
43
|
export interface ParsedAggregateTerm {
|
|
42
44
|
aggregate_name: ParsedAggregateName;
|
|
43
|
-
clauses: ParsedClause[];
|
|
44
45
|
comparison?: ParsedAggregateComparison;
|
|
46
|
+
expression: ParsedExpression;
|
|
45
47
|
kind: 'aggregate';
|
|
46
48
|
traversal: ParsedTraversalTerm;
|
|
47
49
|
value?: number;
|
|
48
50
|
}
|
|
49
51
|
|
|
52
|
+
export interface ParsedTermExpression {
|
|
53
|
+
kind: 'term';
|
|
54
|
+
term: ParsedTerm;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface ParsedNotExpression {
|
|
58
|
+
expression: ParsedExpression;
|
|
59
|
+
kind: 'not';
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface ParsedBooleanExpression {
|
|
63
|
+
expressions: ParsedExpression[];
|
|
64
|
+
kind: 'and' | 'or';
|
|
65
|
+
}
|
|
66
|
+
|
|
50
67
|
export type ParsedTerm =
|
|
51
68
|
| ParsedAggregateTerm
|
|
52
69
|
| ParsedFieldSetTerm
|
|
@@ -54,14 +71,14 @@ export type ParsedTerm =
|
|
|
54
71
|
| ParsedRelationTargetTerm
|
|
55
72
|
| ParsedRelationTerm;
|
|
56
73
|
|
|
57
|
-
export
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
74
|
+
export type ParsedExpression =
|
|
75
|
+
| ParsedBooleanExpression
|
|
76
|
+
| ParsedNotExpression
|
|
77
|
+
| ParsedTermExpression;
|
|
61
78
|
|
|
62
79
|
export type ParseWhereClauseResult =
|
|
63
80
|
| {
|
|
64
|
-
|
|
81
|
+
expression: ParsedExpression;
|
|
65
82
|
success: true;
|
|
66
83
|
}
|
|
67
84
|
| {
|
package/lib/patram-cli.js
CHANGED
|
@@ -9,7 +9,9 @@ import { checkGraph } from './check-graph.js';
|
|
|
9
9
|
import {
|
|
10
10
|
shouldPageCommandOutput,
|
|
11
11
|
writeCommandOutput,
|
|
12
|
+
writeRenderedCommandOutput,
|
|
12
13
|
} from './command-output.js';
|
|
14
|
+
import { discoverFields } from './discover-fields.js';
|
|
13
15
|
import { listRepoFiles } from './list-source-files.js';
|
|
14
16
|
import { listQueries } from './list-queries.js';
|
|
15
17
|
import { loadPatramConfig } from './load-patram-config.js';
|
|
@@ -36,6 +38,7 @@ import {
|
|
|
36
38
|
createOutputView,
|
|
37
39
|
createShowOutputView,
|
|
38
40
|
} from './render-output-view.js';
|
|
41
|
+
import { renderFieldDiscovery } from './render-field-discovery.js';
|
|
39
42
|
import { resolveWhereClause } from './resolve-where-clause.js';
|
|
40
43
|
import { resolveOutputMode } from './resolve-output-mode.js';
|
|
41
44
|
import { loadShowOutput } from './show-document.js';
|
|
@@ -43,8 +46,8 @@ import { loadShowOutput } from './show-document.js';
|
|
|
43
46
|
/**
|
|
44
47
|
* Patram command execution flow.
|
|
45
48
|
*
|
|
46
|
-
* Loads repo state and routes `check`, `query`, `queries`, and
|
|
47
|
-
* the shared output pipeline.
|
|
49
|
+
* Loads repo state and routes `check`, `fields`, `query`, `queries`, and
|
|
50
|
+
* `show` through the shared output pipeline.
|
|
48
51
|
*
|
|
49
52
|
* Kind: cli
|
|
50
53
|
* Status: active
|
|
@@ -94,6 +97,10 @@ export async function main(cli_arguments, io_context) {
|
|
|
94
97
|
return runQueryCommand(parsed_command, io_context);
|
|
95
98
|
}
|
|
96
99
|
|
|
100
|
+
if (parsed_command.command_name === 'fields') {
|
|
101
|
+
return runFieldsCommand(parsed_command, io_context);
|
|
102
|
+
}
|
|
103
|
+
|
|
97
104
|
if (parsed_command.command_name === 'queries') {
|
|
98
105
|
return runQueriesCommand(parsed_command, io_context);
|
|
99
106
|
}
|
|
@@ -140,7 +147,12 @@ async function runCheckCommand(parsed_command, io_context) {
|
|
|
140
147
|
return 1;
|
|
141
148
|
}
|
|
142
149
|
|
|
143
|
-
const diagnostics = checkGraph(
|
|
150
|
+
const diagnostics = checkGraph(
|
|
151
|
+
project_graph_result.graph,
|
|
152
|
+
repo_file_paths,
|
|
153
|
+
project_graph_result.config,
|
|
154
|
+
project_graph_result.claims,
|
|
155
|
+
);
|
|
144
156
|
const selected_diagnostics = selectCheckTargetDiagnostics(
|
|
145
157
|
diagnostics,
|
|
146
158
|
resolved_target,
|
|
@@ -205,6 +217,7 @@ async function runQueryCommand(parsed_command, io_context) {
|
|
|
205
217
|
const query_result = queryGraph(
|
|
206
218
|
project_graph_result.graph,
|
|
207
219
|
where_clause.value.where_clause,
|
|
220
|
+
project_graph_result.config,
|
|
208
221
|
createQueryPaginationOptions(parsed_command, use_pager),
|
|
209
222
|
);
|
|
210
223
|
|
|
@@ -227,12 +240,42 @@ async function runQueryCommand(parsed_command, io_context) {
|
|
|
227
240
|
createOutputView('query', query_result.nodes, {
|
|
228
241
|
derived_summary_evaluator,
|
|
229
242
|
...createQueryOutputOptions(parsed_command, query_result, use_pager),
|
|
243
|
+
repo_config: project_graph_result.config,
|
|
230
244
|
}),
|
|
231
245
|
);
|
|
232
246
|
|
|
233
247
|
return 0;
|
|
234
248
|
}
|
|
235
249
|
|
|
250
|
+
/**
|
|
251
|
+
* @param {ParsedCliCommandRequest} parsed_command
|
|
252
|
+
* @param {{ stderr: { write(chunk: string): boolean }, stdout: { isTTY?: boolean, write(chunk: string): boolean } }} io_context
|
|
253
|
+
* @returns {Promise<number>}
|
|
254
|
+
*/
|
|
255
|
+
async function runFieldsCommand(parsed_command, io_context) {
|
|
256
|
+
const output_mode = resolveOutputMode(parsed_command, {
|
|
257
|
+
is_tty: io_context.stdout.isTTY === true,
|
|
258
|
+
no_color: process.env.NO_COLOR !== undefined,
|
|
259
|
+
term: process.env.TERM,
|
|
260
|
+
});
|
|
261
|
+
const load_result = await loadPatramConfig(process.cwd());
|
|
262
|
+
const defined_field_names =
|
|
263
|
+
load_result.diagnostics.length === 0
|
|
264
|
+
? collectDefinedDiscoveryNames(load_result.config)
|
|
265
|
+
: new Set();
|
|
266
|
+
const discovery_result = await discoverFields(process.cwd(), {
|
|
267
|
+
defined_field_names,
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
await writeRenderedCommandOutput(
|
|
271
|
+
io_context,
|
|
272
|
+
parsed_command,
|
|
273
|
+
renderFieldDiscovery(discovery_result, output_mode),
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
return 0;
|
|
277
|
+
}
|
|
278
|
+
|
|
236
279
|
/**
|
|
237
280
|
* @param {ParsedCliCommandRequest} parsed_command
|
|
238
281
|
* @param {{ stderr: { write(chunk: string): boolean }, stdout: { isTTY?: boolean, write(chunk: string): boolean } }} io_context
|
|
@@ -364,7 +407,9 @@ async function runShowCommand(parsed_command, io_context) {
|
|
|
364
407
|
parsed_command,
|
|
365
408
|
createShowOutputView(show_output.value, {
|
|
366
409
|
derived_summary_evaluator,
|
|
410
|
+
document_node_ids: project_graph_result.graph.document_node_ids,
|
|
367
411
|
graph_nodes: project_graph_result.graph.nodes,
|
|
412
|
+
repo_config: project_graph_result.config,
|
|
368
413
|
}),
|
|
369
414
|
);
|
|
370
415
|
|
|
@@ -396,7 +441,7 @@ function createQueryOutputOptions(parsed_command, query_result, use_pager) {
|
|
|
396
441
|
const offset = parsed_command.query_offset ?? 0;
|
|
397
442
|
|
|
398
443
|
if (query_result.total_count === 0) {
|
|
399
|
-
hints.push('Try: patram query --where "
|
|
444
|
+
hints.push('Try: patram query --where "$class=task"');
|
|
400
445
|
}
|
|
401
446
|
|
|
402
447
|
if (
|
|
@@ -462,3 +507,22 @@ function createQueryExecutionOptions(parsed_command, use_pager) {
|
|
|
462
507
|
function formatDiagnostic(diagnostic) {
|
|
463
508
|
return `${diagnostic.path}:${diagnostic.line}:${diagnostic.column} ${diagnostic.level} ${diagnostic.code} ${diagnostic.message}\n`;
|
|
464
509
|
}
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* @param {import('./load-patram-config.types.ts').PatramRepoConfig | null} repo_config
|
|
513
|
+
* @returns {Set<string>}
|
|
514
|
+
*/
|
|
515
|
+
function collectDefinedDiscoveryNames(repo_config) {
|
|
516
|
+
/** @type {Set<string>} */
|
|
517
|
+
const defined_field_names = new Set();
|
|
518
|
+
|
|
519
|
+
for (const field_name of Object.keys(repo_config?.fields ?? {})) {
|
|
520
|
+
defined_field_names.add(field_name);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
for (const relation_name of Object.keys(repo_config?.relations ?? {})) {
|
|
524
|
+
defined_field_names.add(relation_name);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
return defined_field_names;
|
|
528
|
+
}
|
package/lib/patram-config.js
CHANGED
|
@@ -5,13 +5,13 @@
|
|
|
5
5
|
|
|
6
6
|
import { z } from 'zod';
|
|
7
7
|
|
|
8
|
-
const
|
|
8
|
+
const CLASS_NAME_SCHEMA = z.string().min(1);
|
|
9
9
|
const RELATION_NAME_SCHEMA = z.string().min(1);
|
|
10
10
|
const CLAIM_TYPE_SCHEMA = z.string().min(1);
|
|
11
11
|
const KEY_SOURCE_SCHEMA = z.enum(['path', 'value']);
|
|
12
12
|
const TARGET_SCHEMA = z.enum(['path', 'value']);
|
|
13
13
|
|
|
14
|
-
const
|
|
14
|
+
const class_definition_schema = z
|
|
15
15
|
.object({
|
|
16
16
|
builtin: z.boolean().optional(),
|
|
17
17
|
label: z.string().min(1).optional(),
|
|
@@ -21,16 +21,16 @@ const kind_definition_schema = z
|
|
|
21
21
|
const relation_definition_schema = z
|
|
22
22
|
.object({
|
|
23
23
|
builtin: z.boolean().optional(),
|
|
24
|
-
from: z.array(
|
|
25
|
-
to: z.array(
|
|
24
|
+
from: z.array(CLASS_NAME_SCHEMA).min(1),
|
|
25
|
+
to: z.array(CLASS_NAME_SCHEMA).min(1),
|
|
26
26
|
})
|
|
27
27
|
.strict();
|
|
28
28
|
|
|
29
29
|
const mapping_node_schema = z
|
|
30
30
|
.object({
|
|
31
|
+
class: CLASS_NAME_SCHEMA,
|
|
31
32
|
field: z.string().min(1),
|
|
32
33
|
key: KEY_SOURCE_SCHEMA.optional(),
|
|
33
|
-
kind: KIND_NAME_SCHEMA,
|
|
34
34
|
})
|
|
35
35
|
.strict();
|
|
36
36
|
|
|
@@ -38,7 +38,7 @@ const mapping_emit_schema = z
|
|
|
38
38
|
.object({
|
|
39
39
|
relation: RELATION_NAME_SCHEMA,
|
|
40
40
|
target: TARGET_SCHEMA,
|
|
41
|
-
|
|
41
|
+
target_class: CLASS_NAME_SCHEMA,
|
|
42
42
|
})
|
|
43
43
|
.strict();
|
|
44
44
|
|
|
@@ -53,7 +53,7 @@ const mapping_definition_schema = z
|
|
|
53
53
|
export const patramConfigSchema = z
|
|
54
54
|
.object({
|
|
55
55
|
$schema: z.url().optional(),
|
|
56
|
-
|
|
56
|
+
classes: z.record(CLASS_NAME_SCHEMA, class_definition_schema),
|
|
57
57
|
mappings: z.record(CLAIM_TYPE_SCHEMA, mapping_definition_schema),
|
|
58
58
|
relations: z.record(RELATION_NAME_SCHEMA, relation_definition_schema),
|
|
59
59
|
})
|
|
@@ -90,8 +90,8 @@ function validateMappingDefinition(mapping_definition, refinement_context) {
|
|
|
90
90
|
* @param {RefinementCtx} refinement_context
|
|
91
91
|
*/
|
|
92
92
|
function validatePatramConfigReferences(config_json, refinement_context) {
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
validateRelationClasses(config_json, refinement_context);
|
|
94
|
+
validateMappingClasses(config_json, refinement_context);
|
|
95
95
|
validateMappingRelations(config_json, refinement_context);
|
|
96
96
|
}
|
|
97
97
|
|
|
@@ -99,19 +99,19 @@ function validatePatramConfigReferences(config_json, refinement_context) {
|
|
|
99
99
|
* @param {PatramConfig} config_json
|
|
100
100
|
* @param {RefinementCtx} refinement_context
|
|
101
101
|
*/
|
|
102
|
-
function
|
|
102
|
+
function validateRelationClasses(config_json, refinement_context) {
|
|
103
103
|
for (const [relation_name, relation_definition] of Object.entries(
|
|
104
104
|
config_json.relations,
|
|
105
105
|
)) {
|
|
106
|
-
|
|
106
|
+
validateReferencedClasses(
|
|
107
107
|
relation_definition.from,
|
|
108
|
-
config_json.
|
|
108
|
+
config_json.classes,
|
|
109
109
|
['relations', relation_name, 'from'],
|
|
110
110
|
refinement_context,
|
|
111
111
|
);
|
|
112
|
-
|
|
112
|
+
validateReferencedClasses(
|
|
113
113
|
relation_definition.to,
|
|
114
|
-
config_json.
|
|
114
|
+
config_json.classes,
|
|
115
115
|
['relations', relation_name, 'to'],
|
|
116
116
|
refinement_context,
|
|
117
117
|
);
|
|
@@ -122,24 +122,24 @@ function validateRelationKinds(config_json, refinement_context) {
|
|
|
122
122
|
* @param {PatramConfig} config_json
|
|
123
123
|
* @param {RefinementCtx} refinement_context
|
|
124
124
|
*/
|
|
125
|
-
function
|
|
125
|
+
function validateMappingClasses(config_json, refinement_context) {
|
|
126
126
|
for (const [mapping_name, mapping_definition] of Object.entries(
|
|
127
127
|
config_json.mappings,
|
|
128
128
|
)) {
|
|
129
129
|
if (mapping_definition.emit) {
|
|
130
|
-
|
|
131
|
-
[mapping_definition.emit.
|
|
132
|
-
config_json.
|
|
133
|
-
['mappings', mapping_name, 'emit', '
|
|
130
|
+
validateReferencedClasses(
|
|
131
|
+
[mapping_definition.emit.target_class],
|
|
132
|
+
config_json.classes,
|
|
133
|
+
['mappings', mapping_name, 'emit', 'target_class'],
|
|
134
134
|
refinement_context,
|
|
135
135
|
);
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
if (mapping_definition.node) {
|
|
139
|
-
|
|
140
|
-
[mapping_definition.node.
|
|
141
|
-
config_json.
|
|
142
|
-
['mappings', mapping_name, 'node', '
|
|
139
|
+
validateReferencedClasses(
|
|
140
|
+
[mapping_definition.node.class],
|
|
141
|
+
config_json.classes,
|
|
142
|
+
['mappings', mapping_name, 'node', 'class'],
|
|
143
143
|
refinement_context,
|
|
144
144
|
);
|
|
145
145
|
}
|
|
@@ -171,25 +171,25 @@ function validateMappingRelations(config_json, refinement_context) {
|
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
/**
|
|
174
|
-
* @param {string[]}
|
|
175
|
-
* @param {Record<string, unknown>}
|
|
174
|
+
* @param {string[]} referenced_classes
|
|
175
|
+
* @param {Record<string, unknown>} known_classes
|
|
176
176
|
* @param {(string | number)[]} issue_path
|
|
177
177
|
* @param {RefinementCtx} refinement_context
|
|
178
178
|
*/
|
|
179
|
-
function
|
|
180
|
-
|
|
181
|
-
|
|
179
|
+
function validateReferencedClasses(
|
|
180
|
+
referenced_classes,
|
|
181
|
+
known_classes,
|
|
182
182
|
issue_path,
|
|
183
183
|
refinement_context,
|
|
184
184
|
) {
|
|
185
|
-
for (const
|
|
186
|
-
if (
|
|
185
|
+
for (const referenced_class of referenced_classes) {
|
|
186
|
+
if (known_classes[referenced_class]) {
|
|
187
187
|
continue;
|
|
188
188
|
}
|
|
189
189
|
|
|
190
190
|
refinement_context.addIssue({
|
|
191
191
|
code: 'custom',
|
|
192
|
-
message: `Unknown
|
|
192
|
+
message: `Unknown class "${referenced_class}".`,
|
|
193
193
|
path: issue_path,
|
|
194
194
|
});
|
|
195
195
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export interface
|
|
1
|
+
export interface ClassDefinition {
|
|
2
2
|
builtin?: boolean;
|
|
3
3
|
label?: string;
|
|
4
4
|
}
|
|
@@ -10,15 +10,15 @@ export interface RelationDefinition {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
export interface MappingNodeDefinition {
|
|
13
|
+
class: string;
|
|
13
14
|
field: string;
|
|
14
15
|
key?: 'path' | 'value';
|
|
15
|
-
kind: string;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
export interface MappingEmitDefinition {
|
|
19
19
|
relation: string;
|
|
20
20
|
target: 'path' | 'value';
|
|
21
|
-
|
|
21
|
+
target_class: string;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
export interface MappingDefinition {
|
|
@@ -28,7 +28,13 @@ export interface MappingDefinition {
|
|
|
28
28
|
|
|
29
29
|
export interface PatramConfig {
|
|
30
30
|
$schema?: string;
|
|
31
|
-
|
|
31
|
+
classes: Record<string, ClassDefinition>;
|
|
32
|
+
class_schemas?: Record<string, ClassSchemaConfig>;
|
|
33
|
+
fields?: Record<string, MetadataFieldConfig>;
|
|
32
34
|
mappings: Record<string, MappingDefinition>;
|
|
33
35
|
relations: Record<string, RelationDefinition>;
|
|
34
36
|
}
|
|
37
|
+
import type {
|
|
38
|
+
ClassSchemaConfig,
|
|
39
|
+
MetadataFieldConfig,
|
|
40
|
+
} from './load-patram-config.types.ts';
|