patram 0.4.0 → 0.6.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 +48 -7
- package/lib/build-graph.js +2 -2
- package/lib/check-directive-metadata.js +175 -6
- package/lib/check-directive-path-target.js +173 -0
- package/lib/check-directive-value.js +122 -125
- package/lib/directive-validation-test-helpers.js +87 -0
- package/lib/load-patram-config.js +263 -116
- package/lib/load-patram-config.types.ts +50 -152
- package/lib/load-project-graph.js +16 -6
- package/lib/parse-claims.js +97 -11
- package/lib/parse-claims.types.ts +7 -0
- package/lib/parse-jsdoc-claims.js +3 -3
- package/lib/parse-markdown-claims.js +9 -3
- package/lib/parse-markdown-directives.js +48 -25
- package/lib/parse-yaml-claims.js +472 -0
- package/lib/patram-config.js +26 -9
- package/lib/patram-config.types.ts +18 -36
- package/lib/render-output-view.js +8 -9
- package/lib/resolve-patram-graph-config.js +40 -2
- package/lib/source-file-defaults.js +3 -0
- package/package.json +2 -1
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
/** @import * as yaml from 'yaml'; */
|
|
2
|
+
/* eslint-disable max-lines */
|
|
3
|
+
/**
|
|
4
|
+
* @import { PatramDiagnostic } from './load-patram-config.types.ts';
|
|
5
|
+
* @import { ParseClaimsInput, ParseSourceFileResult, PatramClaimFields } from './parse-claims.types.ts';
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { isMap, isScalar, isSeq, LineCounter, parseAllDocuments } from 'yaml';
|
|
9
|
+
|
|
10
|
+
import { createClaim, getFileExtension } from './claim-helpers.js';
|
|
11
|
+
import { YAML_SOURCE_FILE_EXTENSIONS } from './source-file-defaults.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* YAML claim parsing.
|
|
15
|
+
*
|
|
16
|
+
* Parses standalone YAML metadata files and front matter with one projection
|
|
17
|
+
* model for top-level scalar directives.
|
|
18
|
+
*
|
|
19
|
+
* Kind: parse
|
|
20
|
+
* Status: active
|
|
21
|
+
* Tracked in: ../docs/plans/v0/yaml-source-and-front-matter.md
|
|
22
|
+
* Decided by: ../docs/decisions/yaml-source-and-front-matter.md
|
|
23
|
+
* @patram
|
|
24
|
+
* @see {@link ./parse-claims.js}
|
|
25
|
+
* @see {@link ./parse-markdown-directives.js}
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
const YAML_EXTENSIONS = new Set(YAML_SOURCE_FILE_EXTENSIONS);
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Parse standalone YAML source into neutral directive claims.
|
|
32
|
+
*
|
|
33
|
+
* @param {ParseClaimsInput} parse_input
|
|
34
|
+
* @param {{ multi_value_directive_names?: ReadonlySet<string> }} [parse_options]
|
|
35
|
+
* @returns {ParseSourceFileResult}
|
|
36
|
+
*/
|
|
37
|
+
export function parseYamlClaims(parse_input, parse_options) {
|
|
38
|
+
if (!YAML_EXTENSIONS.has(getFileExtension(parse_input.path))) {
|
|
39
|
+
return {
|
|
40
|
+
claims: [],
|
|
41
|
+
diagnostics: [],
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const parse_result = parseYamlDirectiveFields({
|
|
46
|
+
file_path: parse_input.path,
|
|
47
|
+
parser: 'yaml',
|
|
48
|
+
source_text: parse_input.source,
|
|
49
|
+
start_line: 1,
|
|
50
|
+
multi_value_directive_names: parse_options?.multi_value_directive_names,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
claims: parse_result.directive_fields.map((directive_fields, claim_index) =>
|
|
55
|
+
createClaim(
|
|
56
|
+
parse_input.path,
|
|
57
|
+
claim_index + 1,
|
|
58
|
+
'directive',
|
|
59
|
+
directive_fields,
|
|
60
|
+
),
|
|
61
|
+
),
|
|
62
|
+
diagnostics: parse_result.diagnostics,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Parse YAML metadata into neutral directive fields.
|
|
68
|
+
*
|
|
69
|
+
* @param {{
|
|
70
|
+
* file_path: string,
|
|
71
|
+
* parser: 'markdown' | 'yaml',
|
|
72
|
+
* source_text: string,
|
|
73
|
+
* start_line: number,
|
|
74
|
+
* markdown_style?: 'front_matter',
|
|
75
|
+
* multi_value_directive_names?: ReadonlySet<string>,
|
|
76
|
+
* }} parse_input
|
|
77
|
+
* @returns {{ diagnostics: PatramDiagnostic[], directive_fields: PatramClaimFields[] }}
|
|
78
|
+
*/
|
|
79
|
+
export function parseYamlDirectiveFields(parse_input) {
|
|
80
|
+
const line_counter = new LineCounter();
|
|
81
|
+
const yaml_documents = parseAllDocuments(parse_input.source_text, {
|
|
82
|
+
lineCounter: line_counter,
|
|
83
|
+
prettyErrors: false,
|
|
84
|
+
});
|
|
85
|
+
const parse_result = resolveYamlParseResult(
|
|
86
|
+
parse_input,
|
|
87
|
+
yaml_documents,
|
|
88
|
+
line_counter,
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
if (!parse_result.success) {
|
|
92
|
+
return parse_result.value;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
diagnostics: [],
|
|
97
|
+
directive_fields: collectDirectiveFields(
|
|
98
|
+
parse_input,
|
|
99
|
+
parse_result.value,
|
|
100
|
+
line_counter,
|
|
101
|
+
),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* @param {{
|
|
107
|
+
* file_path: string,
|
|
108
|
+
* start_line: number,
|
|
109
|
+
* }} parse_input
|
|
110
|
+
* @param {any[]} yaml_documents
|
|
111
|
+
* @param {LineCounter} line_counter
|
|
112
|
+
* @returns {{
|
|
113
|
+
success: true,
|
|
114
|
+
value: yaml.YAMLMap<unknown, unknown>
|
|
115
|
+
} | {
|
|
116
|
+
success: false,
|
|
117
|
+
value: {diagnostics: PatramDiagnostic[], directive_fields: PatramClaimFields[]}
|
|
118
|
+
}}
|
|
119
|
+
* success: true,
|
|
120
|
+
* value: import('yaml').YAMLMap<unknown, unknown>,
|
|
121
|
+
* } | {
|
|
122
|
+
* success: false,
|
|
123
|
+
* value: { diagnostics: PatramDiagnostic[], directive_fields: PatramClaimFields[] },
|
|
124
|
+
* }}
|
|
125
|
+
*/
|
|
126
|
+
function resolveYamlParseResult(parse_input, yaml_documents, line_counter) {
|
|
127
|
+
if (yaml_documents.length !== 1) {
|
|
128
|
+
return {
|
|
129
|
+
success: false,
|
|
130
|
+
value: createDiagnosticResult([
|
|
131
|
+
createYamlDiagnostic(
|
|
132
|
+
parse_input.file_path,
|
|
133
|
+
line_counter,
|
|
134
|
+
yaml_documents[1]?.range?.[0] ?? 0,
|
|
135
|
+
parse_input.start_line,
|
|
136
|
+
'yaml.multiple_documents',
|
|
137
|
+
'Patram YAML sources must contain exactly one document.',
|
|
138
|
+
),
|
|
139
|
+
]),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const yaml_document = yaml_documents[0];
|
|
144
|
+
|
|
145
|
+
if (yaml_document.errors.length > 0) {
|
|
146
|
+
return {
|
|
147
|
+
success: false,
|
|
148
|
+
value: createDiagnosticResult(
|
|
149
|
+
yaml_document.errors.map(
|
|
150
|
+
/** @param {{ message: string, pos: [number, number] }} yaml_error */
|
|
151
|
+
(yaml_error) =>
|
|
152
|
+
createYamlDiagnostic(
|
|
153
|
+
parse_input.file_path,
|
|
154
|
+
line_counter,
|
|
155
|
+
yaml_error.pos[0] ?? 0,
|
|
156
|
+
parse_input.start_line,
|
|
157
|
+
'yaml.invalid_syntax',
|
|
158
|
+
yaml_error.message,
|
|
159
|
+
),
|
|
160
|
+
),
|
|
161
|
+
),
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (!isMap(yaml_document.contents)) {
|
|
166
|
+
return {
|
|
167
|
+
success: false,
|
|
168
|
+
value: createDiagnosticResult([
|
|
169
|
+
createYamlDiagnostic(
|
|
170
|
+
parse_input.file_path,
|
|
171
|
+
line_counter,
|
|
172
|
+
resolveNodeRangeStart(yaml_document.contents),
|
|
173
|
+
parse_input.start_line,
|
|
174
|
+
'yaml.invalid_root',
|
|
175
|
+
'Patram YAML metadata must use one top-level mapping.',
|
|
176
|
+
),
|
|
177
|
+
]),
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return {
|
|
182
|
+
success: true,
|
|
183
|
+
value: yaml_document.contents,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* @param {{
|
|
189
|
+
* file_path: string,
|
|
190
|
+
* parser: 'markdown' | 'yaml',
|
|
191
|
+
* start_line: number,
|
|
192
|
+
* markdown_style?: 'front_matter',
|
|
193
|
+
* multi_value_directive_names?: ReadonlySet<string>,
|
|
194
|
+
* }} parse_input
|
|
195
|
+
* @param {import('yaml').YAMLMap<unknown, unknown>} yaml_map
|
|
196
|
+
* @param {LineCounter} line_counter
|
|
197
|
+
* @returns {PatramClaimFields[]}
|
|
198
|
+
*/
|
|
199
|
+
function collectDirectiveFields(parse_input, yaml_map, line_counter) {
|
|
200
|
+
/** @type {PatramClaimFields[]} */
|
|
201
|
+
const directive_fields = [];
|
|
202
|
+
|
|
203
|
+
for (const pair of yaml_map.items) {
|
|
204
|
+
const pair_fields = createPairDirectiveFields(
|
|
205
|
+
parse_input,
|
|
206
|
+
pair,
|
|
207
|
+
line_counter,
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
directive_fields.push(...pair_fields);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return directive_fields;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* @param {{
|
|
218
|
+
* file_path: string,
|
|
219
|
+
* parser: 'markdown' | 'yaml',
|
|
220
|
+
* start_line: number,
|
|
221
|
+
* markdown_style?: 'front_matter',
|
|
222
|
+
* multi_value_directive_names?: ReadonlySet<string>,
|
|
223
|
+
* }} parse_input
|
|
224
|
+
* @param {any} yaml_pair
|
|
225
|
+
* @param {LineCounter} line_counter
|
|
226
|
+
* @returns {PatramClaimFields[]}
|
|
227
|
+
*/
|
|
228
|
+
function createPairDirectiveFields(parse_input, yaml_pair, line_counter) {
|
|
229
|
+
const directive_name = resolveDirectiveName(yaml_pair.key);
|
|
230
|
+
|
|
231
|
+
if (!directive_name || yaml_pair.value === null) {
|
|
232
|
+
return [];
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (isScalar(yaml_pair.value)) {
|
|
236
|
+
return createScalarDirectiveFields(
|
|
237
|
+
parse_input,
|
|
238
|
+
directive_name,
|
|
239
|
+
yaml_pair.key,
|
|
240
|
+
yaml_pair.value.value,
|
|
241
|
+
line_counter,
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (!shouldCollectSequence(parse_input, directive_name, yaml_pair.value)) {
|
|
246
|
+
return [];
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const sequence_items =
|
|
250
|
+
/** @type {Array<{ range?: [number, number, number], value: unknown }>} */ (
|
|
251
|
+
yaml_pair.value.items
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
return sequence_items.flatMap((sequence_item) =>
|
|
255
|
+
createScalarDirectiveFields(
|
|
256
|
+
parse_input,
|
|
257
|
+
directive_name,
|
|
258
|
+
sequence_item,
|
|
259
|
+
sequence_item.value,
|
|
260
|
+
line_counter,
|
|
261
|
+
),
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* @param {{
|
|
267
|
+
* file_path: string,
|
|
268
|
+
* parser: 'markdown' | 'yaml',
|
|
269
|
+
* start_line: number,
|
|
270
|
+
* markdown_style?: 'front_matter',
|
|
271
|
+
* }} parse_input
|
|
272
|
+
* @param {string} directive_name
|
|
273
|
+
* @param {{ range?: [number, number, number] }} yaml_node
|
|
274
|
+
* @param {unknown} scalar_value
|
|
275
|
+
* @param {LineCounter} line_counter
|
|
276
|
+
* @returns {PatramClaimFields[]}
|
|
277
|
+
*/
|
|
278
|
+
function createScalarDirectiveFields(
|
|
279
|
+
parse_input,
|
|
280
|
+
directive_name,
|
|
281
|
+
yaml_node,
|
|
282
|
+
scalar_value,
|
|
283
|
+
line_counter,
|
|
284
|
+
) {
|
|
285
|
+
const normalized_value = normalizeScalarValue(scalar_value);
|
|
286
|
+
|
|
287
|
+
if (normalized_value === null) {
|
|
288
|
+
return [];
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
return [
|
|
292
|
+
{
|
|
293
|
+
...createDirectiveBaseFields(parse_input, yaml_node, line_counter),
|
|
294
|
+
name: directive_name,
|
|
295
|
+
value: normalized_value,
|
|
296
|
+
},
|
|
297
|
+
];
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* @param {{
|
|
302
|
+
* multi_value_directive_names?: ReadonlySet<string>,
|
|
303
|
+
* }} parse_input
|
|
304
|
+
* @param {string} directive_name
|
|
305
|
+
* @param {unknown} yaml_value
|
|
306
|
+
* @returns {boolean}
|
|
307
|
+
*/
|
|
308
|
+
function shouldCollectSequence(parse_input, directive_name, yaml_value) {
|
|
309
|
+
return (
|
|
310
|
+
isSeq(yaml_value) &&
|
|
311
|
+
parse_input.multi_value_directive_names?.has(directive_name) === true &&
|
|
312
|
+
yaml_value.items.every(isNonNullScalarNode)
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* @param {{
|
|
318
|
+
* file_path: string,
|
|
319
|
+
* parser: 'markdown' | 'yaml',
|
|
320
|
+
* start_line: number,
|
|
321
|
+
* markdown_style?: 'front_matter',
|
|
322
|
+
* }} parse_input
|
|
323
|
+
* @param {{ range?: [number, number, number] }} yaml_node
|
|
324
|
+
* @param {LineCounter} line_counter
|
|
325
|
+
* @returns {PatramClaimFields}
|
|
326
|
+
*/
|
|
327
|
+
function createDirectiveBaseFields(parse_input, yaml_node, line_counter) {
|
|
328
|
+
/** @type {PatramClaimFields} */
|
|
329
|
+
const directive_fields = {
|
|
330
|
+
name: '',
|
|
331
|
+
origin: createOrigin(
|
|
332
|
+
parse_input.file_path,
|
|
333
|
+
yaml_node.range?.[0] ?? 0,
|
|
334
|
+
parse_input.start_line,
|
|
335
|
+
line_counter,
|
|
336
|
+
),
|
|
337
|
+
parser: parse_input.parser,
|
|
338
|
+
value: '',
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
if (parse_input.markdown_style !== undefined) {
|
|
342
|
+
directive_fields.markdown_style = parse_input.markdown_style;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
return directive_fields;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* @param {unknown} yaml_key
|
|
350
|
+
* @returns {string | null}
|
|
351
|
+
*/
|
|
352
|
+
function resolveDirectiveName(yaml_key) {
|
|
353
|
+
if (!isScalar(yaml_key) || typeof yaml_key.value !== 'string') {
|
|
354
|
+
return null;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
return normalizeDirectiveName(yaml_key.value);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* @param {unknown} scalar_value
|
|
362
|
+
* @returns {string | null}
|
|
363
|
+
*/
|
|
364
|
+
function normalizeScalarValue(scalar_value) {
|
|
365
|
+
if (scalar_value === null) {
|
|
366
|
+
return null;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
if (typeof scalar_value === 'string') {
|
|
370
|
+
return scalar_value;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
if (typeof scalar_value === 'boolean' || typeof scalar_value === 'number') {
|
|
374
|
+
return String(scalar_value);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
return null;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* @param {unknown} yaml_node
|
|
382
|
+
* @returns {boolean}
|
|
383
|
+
*/
|
|
384
|
+
function isNonNullScalarNode(yaml_node) {
|
|
385
|
+
return isScalar(yaml_node) && normalizeScalarValue(yaml_node.value) !== null;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* @param {string} file_path
|
|
390
|
+
* @param {number} offset
|
|
391
|
+
* @param {number} start_line
|
|
392
|
+
* @param {LineCounter} line_counter
|
|
393
|
+
* @returns {{ column: number, line: number, path: string }}
|
|
394
|
+
*/
|
|
395
|
+
function createOrigin(file_path, offset, start_line, line_counter) {
|
|
396
|
+
const location = line_counter.linePos(offset);
|
|
397
|
+
|
|
398
|
+
return {
|
|
399
|
+
column: location?.col ?? 1,
|
|
400
|
+
line: (location?.line ?? 1) + start_line - 1,
|
|
401
|
+
path: file_path,
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* @param {string} file_path
|
|
407
|
+
* @param {LineCounter} line_counter
|
|
408
|
+
* @param {number} offset
|
|
409
|
+
* @param {number} start_line
|
|
410
|
+
* @param {string} code
|
|
411
|
+
* @param {string} message
|
|
412
|
+
* @returns {PatramDiagnostic}
|
|
413
|
+
*/
|
|
414
|
+
function createYamlDiagnostic(
|
|
415
|
+
file_path,
|
|
416
|
+
line_counter,
|
|
417
|
+
offset,
|
|
418
|
+
start_line,
|
|
419
|
+
code,
|
|
420
|
+
message,
|
|
421
|
+
) {
|
|
422
|
+
const origin = createOrigin(file_path, offset, start_line, line_counter);
|
|
423
|
+
|
|
424
|
+
return {
|
|
425
|
+
code,
|
|
426
|
+
column: origin.column,
|
|
427
|
+
level: 'error',
|
|
428
|
+
line: origin.line,
|
|
429
|
+
message,
|
|
430
|
+
path: file_path,
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* @param {PatramDiagnostic[]} diagnostics
|
|
436
|
+
* @returns {{ diagnostics: PatramDiagnostic[], directive_fields: PatramClaimFields[] }}
|
|
437
|
+
*/
|
|
438
|
+
function createDiagnosticResult(diagnostics) {
|
|
439
|
+
return {
|
|
440
|
+
diagnostics,
|
|
441
|
+
directive_fields: [],
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* @param {unknown} yaml_node
|
|
447
|
+
* @returns {number}
|
|
448
|
+
*/
|
|
449
|
+
function resolveNodeRangeStart(yaml_node) {
|
|
450
|
+
if (
|
|
451
|
+
yaml_node &&
|
|
452
|
+
typeof yaml_node === 'object' &&
|
|
453
|
+
'range' in yaml_node &&
|
|
454
|
+
Array.isArray(yaml_node.range) &&
|
|
455
|
+
typeof yaml_node.range[0] === 'number'
|
|
456
|
+
) {
|
|
457
|
+
return yaml_node.range[0];
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
return 0;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* @param {string} directive_label
|
|
465
|
+
* @returns {string}
|
|
466
|
+
*/
|
|
467
|
+
function normalizeDirectiveName(directive_label) {
|
|
468
|
+
return directive_label
|
|
469
|
+
.trim()
|
|
470
|
+
.toLowerCase()
|
|
471
|
+
.replaceAll(/[\s-]+/dgu, '_');
|
|
472
|
+
}
|
package/lib/patram-config.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @import { PatramConfig } from './patram-config.types.ts';
|
|
3
2
|
* @import { RefinementCtx } from 'zod';
|
|
4
3
|
*/
|
|
5
4
|
|
|
@@ -11,14 +10,20 @@ const CLAIM_TYPE_SCHEMA = z.string().min(1);
|
|
|
11
10
|
const KEY_SOURCE_SCHEMA = z.enum(['path', 'value']);
|
|
12
11
|
const TARGET_SCHEMA = z.enum(['path', 'value']);
|
|
13
12
|
|
|
14
|
-
|
|
13
|
+
/**
|
|
14
|
+
* @typedef {z.output<typeof class_definition_schema>} ClassDefinition
|
|
15
|
+
*/
|
|
16
|
+
export const class_definition_schema = z
|
|
15
17
|
.object({
|
|
16
18
|
builtin: z.boolean().optional(),
|
|
17
19
|
label: z.string().min(1).optional(),
|
|
18
20
|
})
|
|
19
21
|
.strict();
|
|
20
22
|
|
|
21
|
-
|
|
23
|
+
/**
|
|
24
|
+
* @typedef {z.output<typeof relation_definition_schema>} RelationDefinition
|
|
25
|
+
*/
|
|
26
|
+
export const relation_definition_schema = z
|
|
22
27
|
.object({
|
|
23
28
|
builtin: z.boolean().optional(),
|
|
24
29
|
from: z.array(CLASS_NAME_SCHEMA).min(1),
|
|
@@ -26,6 +31,9 @@ const relation_definition_schema = z
|
|
|
26
31
|
})
|
|
27
32
|
.strict();
|
|
28
33
|
|
|
34
|
+
/**
|
|
35
|
+
* @typedef {z.output<typeof mapping_node_schema>} MappingNodeDefinition
|
|
36
|
+
*/
|
|
29
37
|
const mapping_node_schema = z
|
|
30
38
|
.object({
|
|
31
39
|
class: CLASS_NAME_SCHEMA,
|
|
@@ -34,6 +42,9 @@ const mapping_node_schema = z
|
|
|
34
42
|
})
|
|
35
43
|
.strict();
|
|
36
44
|
|
|
45
|
+
/**
|
|
46
|
+
* @typedef {z.output<typeof mapping_emit_schema>} MappingEmitDefinition
|
|
47
|
+
*/
|
|
37
48
|
const mapping_emit_schema = z
|
|
38
49
|
.object({
|
|
39
50
|
relation: RELATION_NAME_SCHEMA,
|
|
@@ -42,7 +53,10 @@ const mapping_emit_schema = z
|
|
|
42
53
|
})
|
|
43
54
|
.strict();
|
|
44
55
|
|
|
45
|
-
|
|
56
|
+
/**
|
|
57
|
+
* @typedef {z.output<typeof mapping_definition_schema>} MappingDefinition
|
|
58
|
+
*/
|
|
59
|
+
export const mapping_definition_schema = z
|
|
46
60
|
.object({
|
|
47
61
|
emit: mapping_emit_schema.optional(),
|
|
48
62
|
node: mapping_node_schema.optional(),
|
|
@@ -50,6 +64,9 @@ const mapping_definition_schema = z
|
|
|
50
64
|
.strict()
|
|
51
65
|
.superRefine(validateMappingDefinition);
|
|
52
66
|
|
|
67
|
+
/**
|
|
68
|
+
* @typedef {z.output<typeof patramConfigSchema>} PatramGraphConfig
|
|
69
|
+
*/
|
|
53
70
|
export const patramConfigSchema = z
|
|
54
71
|
.object({
|
|
55
72
|
$schema: z.url().optional(),
|
|
@@ -64,7 +81,7 @@ export const patramConfigSchema = z
|
|
|
64
81
|
* Parse and validate Patram JSON configuration.
|
|
65
82
|
*
|
|
66
83
|
* @param {unknown} config_json
|
|
67
|
-
* @returns {
|
|
84
|
+
* @returns {PatramGraphConfig}
|
|
68
85
|
*/
|
|
69
86
|
export function parsePatramConfig(config_json) {
|
|
70
87
|
return patramConfigSchema.parse(config_json);
|
|
@@ -86,7 +103,7 @@ function validateMappingDefinition(mapping_definition, refinement_context) {
|
|
|
86
103
|
}
|
|
87
104
|
|
|
88
105
|
/**
|
|
89
|
-
* @param {
|
|
106
|
+
* @param {PatramGraphConfig} config_json
|
|
90
107
|
* @param {RefinementCtx} refinement_context
|
|
91
108
|
*/
|
|
92
109
|
function validatePatramConfigReferences(config_json, refinement_context) {
|
|
@@ -96,7 +113,7 @@ function validatePatramConfigReferences(config_json, refinement_context) {
|
|
|
96
113
|
}
|
|
97
114
|
|
|
98
115
|
/**
|
|
99
|
-
* @param {
|
|
116
|
+
* @param {PatramGraphConfig} config_json
|
|
100
117
|
* @param {RefinementCtx} refinement_context
|
|
101
118
|
*/
|
|
102
119
|
function validateRelationClasses(config_json, refinement_context) {
|
|
@@ -119,7 +136,7 @@ function validateRelationClasses(config_json, refinement_context) {
|
|
|
119
136
|
}
|
|
120
137
|
|
|
121
138
|
/**
|
|
122
|
-
* @param {
|
|
139
|
+
* @param {PatramGraphConfig} config_json
|
|
123
140
|
* @param {RefinementCtx} refinement_context
|
|
124
141
|
*/
|
|
125
142
|
function validateMappingClasses(config_json, refinement_context) {
|
|
@@ -147,7 +164,7 @@ function validateMappingClasses(config_json, refinement_context) {
|
|
|
147
164
|
}
|
|
148
165
|
|
|
149
166
|
/**
|
|
150
|
-
* @param {
|
|
167
|
+
* @param {PatramGraphConfig} config_json
|
|
151
168
|
* @param {RefinementCtx} refinement_context
|
|
152
169
|
*/
|
|
153
170
|
function validateMappingRelations(config_json, refinement_context) {
|
|
@@ -1,40 +1,22 @@
|
|
|
1
|
-
export interface ClassDefinition {
|
|
2
|
-
builtin?: boolean;
|
|
3
|
-
label?: string;
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
export interface RelationDefinition {
|
|
7
|
-
builtin?: boolean;
|
|
8
|
-
from: string[];
|
|
9
|
-
to: string[];
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export interface MappingNodeDefinition {
|
|
13
|
-
class: string;
|
|
14
|
-
field: string;
|
|
15
|
-
key?: 'path' | 'value';
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export interface MappingEmitDefinition {
|
|
19
|
-
relation: string;
|
|
20
|
-
target: 'path' | 'value';
|
|
21
|
-
target_class: string;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface MappingDefinition {
|
|
25
|
-
emit?: MappingEmitDefinition;
|
|
26
|
-
node?: MappingNodeDefinition;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface PatramConfig {
|
|
30
|
-
$schema?: string;
|
|
31
|
-
classes: Record<string, ClassDefinition>;
|
|
32
|
-
class_schemas?: Record<string, ClassSchemaConfig>;
|
|
33
|
-
fields?: Record<string, MetadataFieldConfig>;
|
|
34
|
-
mappings: Record<string, MappingDefinition>;
|
|
35
|
-
relations: Record<string, RelationDefinition>;
|
|
36
|
-
}
|
|
37
1
|
import type {
|
|
38
2
|
ClassSchemaConfig,
|
|
39
3
|
MetadataFieldConfig,
|
|
40
4
|
} from './load-patram-config.types.ts';
|
|
5
|
+
|
|
6
|
+
export type ClassDefinition = import('./patram-config.js').ClassDefinition;
|
|
7
|
+
export type RelationDefinition =
|
|
8
|
+
import('./patram-config.js').RelationDefinition;
|
|
9
|
+
export type MappingNodeDefinition =
|
|
10
|
+
import('./patram-config.js').MappingNodeDefinition;
|
|
11
|
+
export type MappingEmitDefinition =
|
|
12
|
+
import('./patram-config.js').MappingEmitDefinition;
|
|
13
|
+
export type MappingDefinition = import('./patram-config.js').MappingDefinition;
|
|
14
|
+
export type PatramGraphConfig = import('./patram-config.js').PatramGraphConfig;
|
|
15
|
+
export type PatramClassConfig = ClassDefinition & {
|
|
16
|
+
schema?: ClassSchemaConfig;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type PatramConfig = Omit<PatramGraphConfig, 'classes'> & {
|
|
20
|
+
classes: Record<string, PatramClassConfig>;
|
|
21
|
+
fields?: Record<string, MetadataFieldConfig>;
|
|
22
|
+
};
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
/** @import * as $k$$l$output$j$view$k$types$k$ts from './output-view.types.ts'; */
|
|
2
|
-
/* eslint-disable max-lines */
|
|
3
1
|
/**
|
|
4
2
|
* @import { BuildGraphResult, GraphNode } from './build-graph.types.ts';
|
|
5
3
|
* @import { DerivedSummaryEvaluator } from './derived-summary.js';
|
|
6
4
|
* @import { PatramRepoConfig } from './load-patram-config.types.ts';
|
|
7
5
|
* @import { ParsedCliArguments } from './parse-cli-arguments.types.ts';
|
|
8
|
-
* @import { OutputMetadataField, OutputStoredQueryItem, OutputView, ResolvedOutputMode, ShowOutputView } from './output-view.types.ts';
|
|
6
|
+
* @import { OutputDerivedSummary, OutputMetadataField, OutputNodeItem, OutputResolvedLinkItem, OutputResolvedLinkTarget, OutputStoredQueryItem, OutputView, ResolvedOutputMode, ShowOutputView } from './output-view.types.ts';
|
|
9
7
|
*/
|
|
8
|
+
/* eslint-disable max-lines */
|
|
10
9
|
|
|
11
10
|
import { renderJsonOutput } from './render-json-output.js';
|
|
12
11
|
import { renderPlainOutput } from './render-plain-output.js';
|
|
@@ -171,9 +170,9 @@ function createStoredQueriesOutputView(stored_queries) {
|
|
|
171
170
|
|
|
172
171
|
/**
|
|
173
172
|
* @param {GraphNode} graph_node
|
|
174
|
-
* @param {
|
|
173
|
+
* @param {OutputDerivedSummary | null} derived_summary
|
|
175
174
|
* @param {NonNullable<PatramRepoConfig['fields']>} field_definitions
|
|
176
|
-
* @returns {
|
|
175
|
+
* @returns {OutputNodeItem}
|
|
177
176
|
*/
|
|
178
177
|
function createOutputNodeItem(graph_node, derived_summary, field_definitions) {
|
|
179
178
|
const title = getOutputNodeTitle(graph_node);
|
|
@@ -203,9 +202,9 @@ function createOutputNodeItem(graph_node, derived_summary, field_definitions) {
|
|
|
203
202
|
/**
|
|
204
203
|
* @param {{ kind?: string, path: string, status?: string, title: string }} target
|
|
205
204
|
* @param {NonNullable<PatramRepoConfig['fields']>} field_definitions
|
|
206
|
-
* @param {
|
|
205
|
+
* @param {OutputDerivedSummary | null} derived_summary
|
|
207
206
|
* @param {GraphNode | undefined} graph_node
|
|
208
|
-
* @returns {
|
|
207
|
+
* @returns {OutputResolvedLinkTarget}
|
|
209
208
|
*/
|
|
210
209
|
function createResolvedLinkTarget(
|
|
211
210
|
target,
|
|
@@ -220,7 +219,7 @@ function createResolvedLinkTarget(
|
|
|
220
219
|
fields.status = target.status;
|
|
221
220
|
}
|
|
222
221
|
|
|
223
|
-
/** @type {
|
|
222
|
+
/** @type {OutputResolvedLinkTarget} */
|
|
224
223
|
const resolved_target = {
|
|
225
224
|
derived_summary: derived_summary ?? undefined,
|
|
226
225
|
fields,
|
|
@@ -288,7 +287,7 @@ function getOutputNodeId(graph_node) {
|
|
|
288
287
|
/**
|
|
289
288
|
* @param {{ label: string, reference: number, target: { kind?: string, path: string, status?: string, title: string } }} resolved_link
|
|
290
289
|
* @param {{ derived_summary_evaluator?: DerivedSummaryEvaluator, document_node_ids?: BuildGraphResult['document_node_ids'], graph_nodes?: BuildGraphResult['nodes'], repo_config?: PatramRepoConfig }} command_options
|
|
291
|
-
* @returns {
|
|
290
|
+
* @returns {OutputResolvedLinkItem}
|
|
292
291
|
*/
|
|
293
292
|
function createResolvedLinkOutputItem(resolved_link, command_options) {
|
|
294
293
|
const target_graph_node = resolveDocumentGraphNode(
|