patram 0.1.1 → 0.3.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 +57 -24
- package/lib/build-graph.js +383 -17
- package/lib/build-graph.types.ts +5 -2
- package/lib/check-directive-metadata.js +516 -0
- package/lib/check-directive-value.js +282 -0
- package/lib/check-graph.js +24 -5
- package/lib/cli-help-metadata.js +580 -0
- package/lib/derived-summary.js +280 -0
- package/lib/directive-diagnostics.js +38 -0
- package/lib/directive-type-rules.js +133 -0
- package/lib/discover-fields.js +427 -0
- package/lib/discover-fields.types.ts +52 -0
- package/lib/format-derived-summary-row.js +9 -0
- package/lib/format-node-header.js +21 -0
- package/lib/format-output-item-block.js +22 -0
- package/lib/format-output-metadata.js +54 -0
- package/lib/layout-stored-queries.js +96 -2
- package/lib/load-patram-config.js +754 -18
- package/lib/load-patram-config.types.ts +128 -2
- package/lib/load-project-graph.js +4 -1
- package/lib/output-view.types.ts +29 -6
- package/lib/parse-cli-arguments-helpers.js +263 -90
- package/lib/parse-cli-arguments.js +160 -8
- package/lib/parse-cli-arguments.types.ts +49 -4
- package/lib/parse-where-clause.js +670 -209
- package/lib/parse-where-clause.types.ts +72 -0
- package/lib/patram-cli.js +180 -21
- package/lib/patram-config.js +31 -31
- package/lib/patram-config.types.ts +10 -4
- package/lib/patram.js +6 -0
- package/lib/query-graph.js +444 -113
- package/lib/query-inspection.js +798 -0
- package/lib/render-check-output.js +1 -1
- package/lib/render-cli-help.js +419 -0
- package/lib/render-field-discovery.js +148 -0
- package/lib/render-json-output.js +66 -14
- package/lib/render-output-view.js +272 -22
- package/lib/render-plain-output.js +31 -86
- package/lib/render-rich-output.js +34 -87
- package/lib/resolve-patram-graph-config.js +15 -9
- package/lib/resolve-where-clause.js +18 -3
- package/lib/show-document.js +51 -7
- package/lib/tagged-fenced-block-error.js +17 -0
- package/lib/tagged-fenced-block-markdown.js +111 -0
- package/lib/tagged-fenced-block-metadata.js +97 -0
- package/lib/tagged-fenced-block-parser.js +292 -0
- package/lib/tagged-fenced-blocks.js +100 -0
- package/lib/tagged-fenced-blocks.types.ts +38 -0
- package/package.json +12 -7
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { PatramDiagnostic } from './load-patram-config.types.ts';
|
|
2
|
+
|
|
3
|
+
export type ParsedFieldName = string;
|
|
4
|
+
|
|
5
|
+
export interface ParsedFieldTerm {
|
|
6
|
+
column: number;
|
|
7
|
+
field_name: ParsedFieldName;
|
|
8
|
+
kind: 'field';
|
|
9
|
+
operator: '!=' | '<' | '<=' | '=' | '>' | '>=' | '^=' | '~';
|
|
10
|
+
value: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface ParsedFieldSetTerm {
|
|
14
|
+
column: number;
|
|
15
|
+
field_name: ParsedFieldName;
|
|
16
|
+
kind: 'field_set';
|
|
17
|
+
operator: 'in' | 'not in';
|
|
18
|
+
values: string[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface ParsedTraversalTerm {
|
|
22
|
+
column: number;
|
|
23
|
+
direction: 'in' | 'out';
|
|
24
|
+
relation_name: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface ParsedRelationTerm {
|
|
28
|
+
column: number;
|
|
29
|
+
kind: 'relation';
|
|
30
|
+
relation_name: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface ParsedRelationTargetTerm {
|
|
34
|
+
column: number;
|
|
35
|
+
kind: 'relation_target';
|
|
36
|
+
relation_name: string;
|
|
37
|
+
target_id: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type ParsedAggregateComparison = '!=' | '<' | '<=' | '=' | '>' | '>=';
|
|
41
|
+
export type ParsedAggregateName = 'any' | 'count' | 'none';
|
|
42
|
+
|
|
43
|
+
export interface ParsedAggregateTerm {
|
|
44
|
+
aggregate_name: ParsedAggregateName;
|
|
45
|
+
clauses: ParsedClause[];
|
|
46
|
+
comparison?: ParsedAggregateComparison;
|
|
47
|
+
kind: 'aggregate';
|
|
48
|
+
traversal: ParsedTraversalTerm;
|
|
49
|
+
value?: number;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export type ParsedTerm =
|
|
53
|
+
| ParsedAggregateTerm
|
|
54
|
+
| ParsedFieldSetTerm
|
|
55
|
+
| ParsedFieldTerm
|
|
56
|
+
| ParsedRelationTargetTerm
|
|
57
|
+
| ParsedRelationTerm;
|
|
58
|
+
|
|
59
|
+
export interface ParsedClause {
|
|
60
|
+
is_negated: boolean;
|
|
61
|
+
term: ParsedTerm;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export type ParseWhereClauseResult =
|
|
65
|
+
| {
|
|
66
|
+
clauses: ParsedClause[];
|
|
67
|
+
success: true;
|
|
68
|
+
}
|
|
69
|
+
| {
|
|
70
|
+
diagnostic: PatramDiagnostic;
|
|
71
|
+
success: false;
|
|
72
|
+
};
|
package/lib/patram-cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable max-lines */
|
|
2
2
|
/**
|
|
3
|
-
* @import {
|
|
3
|
+
* @import { ParsedCliCommandRequest } from './parse-cli-arguments.types.ts';
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import process from 'node:process';
|
|
@@ -10,16 +10,24 @@ import {
|
|
|
10
10
|
shouldPageCommandOutput,
|
|
11
11
|
writeCommandOutput,
|
|
12
12
|
} from './command-output.js';
|
|
13
|
+
import { discoverFields } from './discover-fields.js';
|
|
13
14
|
import { listRepoFiles } from './list-source-files.js';
|
|
14
15
|
import { listQueries } from './list-queries.js';
|
|
15
16
|
import { loadPatramConfig } from './load-patram-config.js';
|
|
16
17
|
import { loadProjectGraph } from './load-project-graph.js';
|
|
17
18
|
import { parseCliArguments } from './parse-cli-arguments.js';
|
|
18
19
|
import { DEFAULT_QUERY_LIMIT, queryGraph } from './query-graph.js';
|
|
20
|
+
import { inspectQuery, renderQueryInspection } from './query-inspection.js';
|
|
21
|
+
import { createDerivedSummaryEvaluator } from './derived-summary.js';
|
|
19
22
|
import {
|
|
20
23
|
renderCheckDiagnostics,
|
|
21
24
|
renderCheckSuccess,
|
|
22
25
|
} from './render-check-output.js';
|
|
26
|
+
import {
|
|
27
|
+
renderCliParseError,
|
|
28
|
+
renderHelpRequest,
|
|
29
|
+
renderInvalidWhereDiagnostic,
|
|
30
|
+
} from './render-cli-help.js';
|
|
23
31
|
import {
|
|
24
32
|
resolveCheckTarget,
|
|
25
33
|
selectCheckTargetDiagnostics,
|
|
@@ -29,6 +37,7 @@ import {
|
|
|
29
37
|
createOutputView,
|
|
30
38
|
createShowOutputView,
|
|
31
39
|
} from './render-output-view.js';
|
|
40
|
+
import { renderFieldDiscovery } from './render-field-discovery.js';
|
|
32
41
|
import { resolveWhereClause } from './resolve-where-clause.js';
|
|
33
42
|
import { resolveOutputMode } from './resolve-output-mode.js';
|
|
34
43
|
import { loadShowOutput } from './show-document.js';
|
|
@@ -36,8 +45,8 @@ import { loadShowOutput } from './show-document.js';
|
|
|
36
45
|
/**
|
|
37
46
|
* Patram command execution flow.
|
|
38
47
|
*
|
|
39
|
-
* Loads repo state and routes `check`, `query`, `queries`, and
|
|
40
|
-
* the shared output pipeline.
|
|
48
|
+
* Loads repo state and routes `check`, `fields`, `query`, `queries`, and
|
|
49
|
+
* `show` through the shared output pipeline.
|
|
41
50
|
*
|
|
42
51
|
* Kind: cli
|
|
43
52
|
* Status: active
|
|
@@ -64,12 +73,20 @@ export async function main(cli_arguments, io_context) {
|
|
|
64
73
|
const parsed_arguments = parseCliArguments(cli_arguments);
|
|
65
74
|
|
|
66
75
|
if (!parsed_arguments.success) {
|
|
67
|
-
io_context.stderr.write(
|
|
76
|
+
io_context.stderr.write(renderCliParseError(parsed_arguments.error));
|
|
68
77
|
|
|
69
78
|
return 1;
|
|
70
79
|
}
|
|
71
80
|
|
|
72
|
-
|
|
81
|
+
if (parsed_arguments.value.kind === 'help') {
|
|
82
|
+
io_context.stdout.write(renderHelpRequest(parsed_arguments.value));
|
|
83
|
+
|
|
84
|
+
return 0;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const parsed_command = /** @type {ParsedCliCommandRequest} */ (
|
|
88
|
+
parsed_arguments.value
|
|
89
|
+
);
|
|
73
90
|
|
|
74
91
|
if (parsed_command.command_name === 'check') {
|
|
75
92
|
return runCheckCommand(parsed_command, io_context);
|
|
@@ -79,6 +96,10 @@ export async function main(cli_arguments, io_context) {
|
|
|
79
96
|
return runQueryCommand(parsed_command, io_context);
|
|
80
97
|
}
|
|
81
98
|
|
|
99
|
+
if (parsed_command.command_name === 'fields') {
|
|
100
|
+
return runFieldsCommand(parsed_command, io_context);
|
|
101
|
+
}
|
|
102
|
+
|
|
82
103
|
if (parsed_command.command_name === 'queries') {
|
|
83
104
|
return runQueriesCommand(parsed_command, io_context);
|
|
84
105
|
}
|
|
@@ -93,7 +114,7 @@ export async function main(cli_arguments, io_context) {
|
|
|
93
114
|
}
|
|
94
115
|
|
|
95
116
|
/**
|
|
96
|
-
* @param {
|
|
117
|
+
* @param {ParsedCliCommandRequest} parsed_command
|
|
97
118
|
* @param {{ stderr: { write(chunk: string): boolean }, stdout: { isTTY?: boolean, write(chunk: string): boolean }, write_paged_output?: (output_text: string) => Promise<void> }} io_context
|
|
98
119
|
* @returns {Promise<number>}
|
|
99
120
|
*/
|
|
@@ -125,7 +146,12 @@ async function runCheckCommand(parsed_command, io_context) {
|
|
|
125
146
|
return 1;
|
|
126
147
|
}
|
|
127
148
|
|
|
128
|
-
const diagnostics = checkGraph(
|
|
149
|
+
const diagnostics = checkGraph(
|
|
150
|
+
project_graph_result.graph,
|
|
151
|
+
repo_file_paths,
|
|
152
|
+
project_graph_result.config,
|
|
153
|
+
project_graph_result.claims,
|
|
154
|
+
);
|
|
129
155
|
const selected_diagnostics = selectCheckTargetDiagnostics(
|
|
130
156
|
diagnostics,
|
|
131
157
|
resolved_target,
|
|
@@ -147,12 +173,27 @@ async function runCheckCommand(parsed_command, io_context) {
|
|
|
147
173
|
}
|
|
148
174
|
|
|
149
175
|
/**
|
|
150
|
-
* @param {
|
|
176
|
+
* @param {ParsedCliCommandRequest} parsed_command
|
|
151
177
|
* @param {{ stderr: { write(chunk: string): boolean }, stdout: { isTTY?: boolean, write(chunk: string): boolean }, write_paged_output?: (output_text: string) => Promise<void> }} io_context
|
|
152
178
|
* @returns {Promise<number>}
|
|
153
179
|
*/
|
|
154
180
|
async function runQueryCommand(parsed_command, io_context) {
|
|
155
181
|
const use_pager = shouldPageCommandOutput(parsed_command, io_context.stdout);
|
|
182
|
+
const output_mode = resolveOutputMode(parsed_command, {
|
|
183
|
+
is_tty: io_context.stdout.isTTY === true,
|
|
184
|
+
no_color: process.env.NO_COLOR !== undefined,
|
|
185
|
+
term: process.env.TERM,
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
if (parsed_command.query_inspection_mode) {
|
|
189
|
+
return runQueryInspectionCommand(
|
|
190
|
+
parsed_command,
|
|
191
|
+
io_context,
|
|
192
|
+
output_mode,
|
|
193
|
+
use_pager,
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
156
197
|
const project_graph_result = await loadProjectGraph(process.cwd());
|
|
157
198
|
|
|
158
199
|
if (project_graph_result.diagnostics.length > 0) {
|
|
@@ -174,31 +215,123 @@ async function runQueryCommand(parsed_command, io_context) {
|
|
|
174
215
|
|
|
175
216
|
const query_result = queryGraph(
|
|
176
217
|
project_graph_result.graph,
|
|
177
|
-
where_clause.value,
|
|
218
|
+
where_clause.value.where_clause,
|
|
219
|
+
project_graph_result.config,
|
|
178
220
|
createQueryPaginationOptions(parsed_command, use_pager),
|
|
179
221
|
);
|
|
180
222
|
|
|
181
223
|
if (query_result.diagnostics.length > 0) {
|
|
182
|
-
|
|
224
|
+
io_context.stderr.write(
|
|
225
|
+
renderInvalidWhereDiagnostic(query_result.diagnostics[0]),
|
|
226
|
+
);
|
|
183
227
|
|
|
184
228
|
return 1;
|
|
185
229
|
}
|
|
186
230
|
|
|
231
|
+
const derived_summary_evaluator = createDerivedSummaryEvaluator(
|
|
232
|
+
project_graph_result.config,
|
|
233
|
+
project_graph_result.graph,
|
|
234
|
+
);
|
|
235
|
+
|
|
187
236
|
await writeCommandOutput(
|
|
188
237
|
io_context,
|
|
189
238
|
parsed_command,
|
|
190
|
-
createOutputView(
|
|
191
|
-
|
|
192
|
-
query_result
|
|
193
|
-
|
|
194
|
-
),
|
|
239
|
+
createOutputView('query', query_result.nodes, {
|
|
240
|
+
derived_summary_evaluator,
|
|
241
|
+
...createQueryOutputOptions(parsed_command, query_result, use_pager),
|
|
242
|
+
repo_config: project_graph_result.config,
|
|
243
|
+
}),
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
return 0;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* @param {ParsedCliCommandRequest} parsed_command
|
|
251
|
+
* @param {{ stderr: { write(chunk: string): boolean }, stdout: { isTTY?: boolean, write(chunk: string): boolean } }} io_context
|
|
252
|
+
* @returns {Promise<number>}
|
|
253
|
+
*/
|
|
254
|
+
async function runFieldsCommand(parsed_command, io_context) {
|
|
255
|
+
const output_mode = resolveOutputMode(parsed_command, {
|
|
256
|
+
is_tty: io_context.stdout.isTTY === true,
|
|
257
|
+
no_color: process.env.NO_COLOR !== undefined,
|
|
258
|
+
term: process.env.TERM,
|
|
259
|
+
});
|
|
260
|
+
const discovery_result = await discoverFields(process.cwd());
|
|
261
|
+
|
|
262
|
+
io_context.stdout.write(renderFieldDiscovery(discovery_result, output_mode));
|
|
263
|
+
|
|
264
|
+
return 0;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* @param {ParsedCliCommandRequest} parsed_command
|
|
269
|
+
* @param {{ stderr: { write(chunk: string): boolean }, stdout: { isTTY?: boolean, write(chunk: string): boolean } }} io_context
|
|
270
|
+
* @param {import('./output-view.types.ts').ResolvedOutputMode} output_mode
|
|
271
|
+
* @param {boolean} use_pager
|
|
272
|
+
* @returns {Promise<number>}
|
|
273
|
+
*/
|
|
274
|
+
async function runQueryInspectionCommand(
|
|
275
|
+
parsed_command,
|
|
276
|
+
io_context,
|
|
277
|
+
output_mode,
|
|
278
|
+
use_pager,
|
|
279
|
+
) {
|
|
280
|
+
const query_inspection_mode = parsed_command.query_inspection_mode;
|
|
281
|
+
|
|
282
|
+
if (!query_inspection_mode) {
|
|
283
|
+
throw new Error('Expected a query inspection mode.');
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const load_result = await loadPatramConfig(process.cwd());
|
|
287
|
+
|
|
288
|
+
if (load_result.diagnostics.length > 0) {
|
|
289
|
+
io_context.stderr.write(
|
|
290
|
+
renderCheckDiagnostics(load_result.diagnostics, output_mode),
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
return 1;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const repo_config = load_result.config;
|
|
297
|
+
|
|
298
|
+
if (!repo_config) {
|
|
299
|
+
throw new Error('Expected a valid Patram repo config.');
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const where_clause = resolveWhereClause(
|
|
303
|
+
repo_config,
|
|
304
|
+
parsed_command.command_arguments,
|
|
305
|
+
);
|
|
306
|
+
|
|
307
|
+
if (!where_clause.success) {
|
|
308
|
+
io_context.stderr.write(`${where_clause.message}\n`);
|
|
309
|
+
|
|
310
|
+
return 1;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const query_inspection = inspectQuery(repo_config, where_clause.value, {
|
|
314
|
+
inspection_mode: query_inspection_mode,
|
|
315
|
+
...createQueryExecutionOptions(parsed_command, use_pager),
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
if (!query_inspection.success) {
|
|
319
|
+
io_context.stderr.write(
|
|
320
|
+
renderCheckDiagnostics(query_inspection.diagnostics, output_mode),
|
|
321
|
+
);
|
|
322
|
+
|
|
323
|
+
return 1;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
io_context.stdout.write(
|
|
327
|
+
renderQueryInspection(query_inspection.value, output_mode),
|
|
195
328
|
);
|
|
196
329
|
|
|
197
330
|
return 0;
|
|
198
331
|
}
|
|
199
332
|
|
|
200
333
|
/**
|
|
201
|
-
* @param {
|
|
334
|
+
* @param {ParsedCliCommandRequest} parsed_command
|
|
202
335
|
* @param {{ stderr: { write(chunk: string): boolean }, stdout: { isTTY?: boolean, write(chunk: string): boolean }, write_paged_output?: (output_text: string) => Promise<void> }} io_context
|
|
203
336
|
* @returns {Promise<number>}
|
|
204
337
|
*/
|
|
@@ -227,7 +360,7 @@ async function runQueriesCommand(parsed_command, io_context) {
|
|
|
227
360
|
}
|
|
228
361
|
|
|
229
362
|
/**
|
|
230
|
-
* @param {
|
|
363
|
+
* @param {ParsedCliCommandRequest} parsed_command
|
|
231
364
|
* @param {{ stderr: { write(chunk: string): boolean }, stdout: { isTTY?: boolean, write(chunk: string): boolean }, write_paged_output?: (output_text: string) => Promise<void> }} io_context
|
|
232
365
|
* @returns {Promise<number>}
|
|
233
366
|
*/
|
|
@@ -252,10 +385,19 @@ async function runShowCommand(parsed_command, io_context) {
|
|
|
252
385
|
return 1;
|
|
253
386
|
}
|
|
254
387
|
|
|
388
|
+
const derived_summary_evaluator = createDerivedSummaryEvaluator(
|
|
389
|
+
project_graph_result.config,
|
|
390
|
+
project_graph_result.graph,
|
|
391
|
+
);
|
|
392
|
+
|
|
255
393
|
await writeCommandOutput(
|
|
256
394
|
io_context,
|
|
257
395
|
parsed_command,
|
|
258
|
-
createShowOutputView(show_output.value
|
|
396
|
+
createShowOutputView(show_output.value, {
|
|
397
|
+
derived_summary_evaluator,
|
|
398
|
+
graph_nodes: project_graph_result.graph.nodes,
|
|
399
|
+
repo_config: project_graph_result.config,
|
|
400
|
+
}),
|
|
259
401
|
);
|
|
260
402
|
|
|
261
403
|
return 0;
|
|
@@ -272,7 +414,7 @@ function writeDiagnostics(output_stream, diagnostics) {
|
|
|
272
414
|
}
|
|
273
415
|
|
|
274
416
|
/**
|
|
275
|
-
* @param {
|
|
417
|
+
* @param {ParsedCliCommandRequest} parsed_command
|
|
276
418
|
* @param {{ total_count: number, nodes: import('./build-graph.types.ts').GraphNode[] }} query_result
|
|
277
419
|
* @param {boolean} use_pager
|
|
278
420
|
* @returns {{ hints: string[], limit: number, offset: number, total_count: number }}
|
|
@@ -286,7 +428,7 @@ function createQueryOutputOptions(parsed_command, query_result, use_pager) {
|
|
|
286
428
|
const offset = parsed_command.query_offset ?? 0;
|
|
287
429
|
|
|
288
430
|
if (query_result.total_count === 0) {
|
|
289
|
-
hints.push('Try: patram query --where "
|
|
431
|
+
hints.push('Try: patram query --where "$class=task"');
|
|
290
432
|
}
|
|
291
433
|
|
|
292
434
|
if (
|
|
@@ -309,7 +451,7 @@ function createQueryOutputOptions(parsed_command, query_result, use_pager) {
|
|
|
309
451
|
}
|
|
310
452
|
|
|
311
453
|
/**
|
|
312
|
-
* @param {
|
|
454
|
+
* @param {ParsedCliCommandRequest} parsed_command
|
|
313
455
|
* @param {boolean} use_pager
|
|
314
456
|
* @returns {{ limit?: number, offset: number }}
|
|
315
457
|
*/
|
|
@@ -328,6 +470,23 @@ function createQueryPaginationOptions(parsed_command, use_pager) {
|
|
|
328
470
|
return pagination_options;
|
|
329
471
|
}
|
|
330
472
|
|
|
473
|
+
/**
|
|
474
|
+
* @param {ParsedCliCommandRequest} parsed_command
|
|
475
|
+
* @param {boolean} use_pager
|
|
476
|
+
* @returns {{ limit: number | null, offset: number }}
|
|
477
|
+
*/
|
|
478
|
+
function createQueryExecutionOptions(parsed_command, use_pager) {
|
|
479
|
+
const pagination_options = createQueryPaginationOptions(
|
|
480
|
+
parsed_command,
|
|
481
|
+
use_pager,
|
|
482
|
+
);
|
|
483
|
+
|
|
484
|
+
return {
|
|
485
|
+
limit: pagination_options.limit ?? null,
|
|
486
|
+
offset: pagination_options.offset,
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
|
|
331
490
|
/**
|
|
332
491
|
* @param {import('./load-patram-config.types.ts').PatramDiagnostic} diagnostic
|
|
333
492
|
* @returns {string}
|
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';
|