patram 0.4.0 → 0.5.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.
@@ -1,5 +1,5 @@
1
- /** @import * as $k$$l$document$j$node$j$identity$k$js from './document-node-identity.js'; */
2
1
  /**
2
+ * @import { DocumentNodeReference } from './document-node-identity.js';
3
3
  * @import { GraphNode } from './build-graph.types.ts';
4
4
  * @import { PatramClaim } from './parse-claims.types.ts';
5
5
  */
@@ -48,7 +48,7 @@ export function resolveNodeKey(node_mapping, claim, document_entity_keys) {
48
48
  * @param {'path' | 'value'} target_type
49
49
  * @param {PatramClaim} claim
50
50
  * @param {Map<string, string>} document_entity_keys
51
- * @param {Map<string, import('./document-node-identity.js').DocumentNodeReference>} document_node_references
51
+ * @param {Map<string, DocumentNodeReference>} document_node_references
52
52
  * @param {Set<string>} document_paths
53
53
  * @returns {{ class_name: string, key: string, path?: string }}
54
54
  */
@@ -124,7 +124,7 @@ function resolveValueTargetReference(target_kind, claim) {
124
124
  * @param {string} target_class
125
125
  * @param {PatramClaim} claim
126
126
  * @param {Map<string, string>} document_entity_keys
127
- * @param {Map<string, import('./document-node-identity.js').DocumentNodeReference>} document_node_references
127
+ * @param {Map<string, DocumentNodeReference>} document_node_references
128
128
  * @param {Set<string>} document_paths
129
129
  * @returns {{ class_name: string, key: string, path?: string }}
130
130
  */
@@ -171,7 +171,7 @@ function resolvePathTargetReference(
171
171
 
172
172
  /**
173
173
  * @param {string} target_path
174
- * @param {Map<string, import('./document-node-identity.js').DocumentNodeReference>} document_node_references
174
+ * @param {Map<string, DocumentNodeReference>} document_node_references
175
175
  * @returns {{ class_name: string, key: string, path?: string }}
176
176
  */
177
177
  function resolveDocumentTargetReference(target_path, document_node_references) {
@@ -246,8 +246,8 @@ function getDocumentEntityMapKey(document_path, class_name) {
246
246
  }
247
247
 
248
248
  /**
249
- * @param {import('./document-node-identity.js').DocumentNodeReference | undefined} document_node_reference
250
- * @returns {document_node_reference is $k$$l$document$j$node$j$identity$k$js.DocumentNodeReference}
249
+ * @param {DocumentNodeReference | undefined} document_node_reference
250
+ * @returns {document_node_reference is DocumentNodeReference}
251
251
  */
252
252
  function documentNodeReferenceIsPromoted(document_node_reference) {
253
253
  return (
@@ -771,7 +771,7 @@ function getFieldDefinition(patram_config, field_name) {
771
771
  * @returns {{ fields?: Record<string, { presence: 'required' | 'optional' | 'forbidden' }>, unknown_fields?: 'ignore' | 'error' } | undefined}
772
772
  */
773
773
  function getClassSchema(patram_config, class_name) {
774
- return patram_config.class_schemas?.[class_name];
774
+ return patram_config.classes[class_name]?.schema;
775
775
  }
776
776
 
777
777
  /**
@@ -47,7 +47,7 @@ export function checkDirectiveMetadata(
47
47
  if (
48
48
  claims.length === 0 ||
49
49
  (repo_config.fields === undefined &&
50
- repo_config.class_schemas === undefined)
50
+ !hasConfiguredClassSchemas(repo_config))
51
51
  ) {
52
52
  return [];
53
53
  }
@@ -143,7 +143,7 @@ function collectDocumentDiagnostics(
143
143
  document_paths,
144
144
  ) {
145
145
  const document_kind = resolveDocumentKind(graph, document_path);
146
- const schema_definition = repo_config.class_schemas?.[document_kind];
146
+ const schema_definition = repo_config.classes?.[document_kind]?.schema;
147
147
  /** @type {Map<string, number>} */
148
148
  const directive_counts = new Map();
149
149
 
@@ -240,6 +240,20 @@ function collectClaimDiagnostics(
240
240
  ];
241
241
  }
242
242
 
243
+ /**
244
+ * @param {PatramRepoConfig} repo_config
245
+ * @returns {boolean}
246
+ */
247
+ function hasConfiguredClassSchemas(repo_config) {
248
+ for (const class_definition of Object.values(repo_config.classes ?? {})) {
249
+ if (class_definition.schema) {
250
+ return true;
251
+ }
252
+ }
253
+
254
+ return false;
255
+ }
256
+
243
257
  /**
244
258
  * @param {PatramRepoConfig} repo_config
245
259
  * @param {string} document_path
@@ -1,12 +1,8 @@
1
+ /** @import * as $k$$l$patram$j$config$k$js from './patram-config.js'; */
1
2
  /* eslint-disable max-lines */
3
+
2
4
  /**
3
- * @import {
4
- * ClassSchemaConfig,
5
- * LoadPatramConfigResult,
6
- * MetadataFieldConfig,
7
- * PatramDiagnostic,
8
- * PatramRepoConfig,
9
- * } from './load-patram-config.types.ts';
5
+ * @import { ClassDefinition } from './patram-config.js';
10
6
  */
11
7
 
12
8
  import { readFile } from 'node:fs/promises';
@@ -15,7 +11,12 @@ import process from 'node:process';
15
11
 
16
12
  import { z } from 'zod';
17
13
 
18
- import { parsePatramConfig } from './patram-config.js';
14
+ import {
15
+ class_definition_schema,
16
+ mapping_definition_schema,
17
+ parsePatramConfig,
18
+ relation_definition_schema,
19
+ } from './patram-config.js';
19
20
  import { parseWhereClause } from './parse-where-clause.js';
20
21
  import { getQuerySemanticDiagnostics } from './query-inspection.js';
21
22
  import { resolvePatramGraphConfig } from './resolve-patram-graph-config.js';
@@ -40,12 +41,28 @@ import { DEFAULT_INCLUDE_PATTERNS } from './source-file-defaults.js';
40
41
  const CONFIG_FILE_NAME = '.patram.json';
41
42
  const RESERVED_STRUCTURAL_FIELD_NAMES = new Set(['$class', '$id', '$path']);
42
43
 
44
+ /**
45
+ * @typedef {object} PatramDiagnostic
46
+ * @property {string} code
47
+ * @property {number} column
48
+ * @property {'error'} level
49
+ * @property {number} line
50
+ * @property {string} message
51
+ * @property {string} path
52
+ */
53
+
54
+ /**
55
+ * @typedef {z.output<typeof stored_query_schema>} StoredQueryConfig
56
+ */
43
57
  const stored_query_schema = z
44
58
  .object({
45
59
  where: z.string().min(1, 'Stored query "where" must not be empty.'),
46
60
  })
47
61
  .strict();
48
62
 
63
+ /**
64
+ * @typedef {z.output<typeof derived_summary_scalar_schema>} DerivedSummaryScalar
65
+ */
49
66
  const derived_summary_scalar_schema = z.union([
50
67
  z.boolean(),
51
68
  z.number(),
@@ -53,6 +70,9 @@ const derived_summary_scalar_schema = z.union([
53
70
  z.null(),
54
71
  ]);
55
72
 
73
+ /**
74
+ * @typedef {z.output<typeof derived_summary_count_schema>} DerivedSummaryCountConfig
75
+ */
56
76
  const derived_summary_count_schema = z
57
77
  .object({
58
78
  traversal: z
@@ -64,6 +84,9 @@ const derived_summary_count_schema = z
64
84
  })
65
85
  .strict();
66
86
 
87
+ /**
88
+ * @typedef {z.output<typeof derived_summary_select_case_schema>} DerivedSummarySelectCaseConfig
89
+ */
67
90
  const derived_summary_select_case_schema = z
68
91
  .object({
69
92
  value: derived_summary_scalar_schema,
@@ -71,21 +94,41 @@ const derived_summary_select_case_schema = z
71
94
  })
72
95
  .strict();
73
96
 
74
- const derived_summary_field_schema = z
97
+ const derived_summary_field_name_schema = z
98
+ .string()
99
+ .regex(
100
+ /^[a-z][a-z0-9_]*$/du,
101
+ 'Derived summary field names must use lower_snake_case.',
102
+ );
103
+
104
+ /**
105
+ * @typedef {z.output<typeof derived_summary_field_schema>} DerivedSummaryFieldConfig
106
+ */
107
+ const derived_summary_count_field_schema = z
75
108
  .object({
76
- count: derived_summary_count_schema.optional(),
77
- default: derived_summary_scalar_schema.optional(),
78
- name: z
79
- .string()
80
- .regex(
81
- /^[a-z][a-z0-9_]*$/du,
82
- 'Derived summary field names must use lower_snake_case.',
83
- ),
84
- select: z.array(derived_summary_select_case_schema).optional(),
109
+ count: derived_summary_count_schema,
110
+ name: derived_summary_field_name_schema,
85
111
  })
86
- .strict()
87
- .superRefine(validateDerivedSummaryFieldDefinition);
112
+ .strict();
113
+
114
+ const derived_summary_select_field_schema = z
115
+ .object({
116
+ default: derived_summary_scalar_schema,
117
+ name: derived_summary_field_name_schema,
118
+ select: z
119
+ .array(derived_summary_select_case_schema)
120
+ .min(1, 'Derived summary "select" must contain at least one case.'),
121
+ })
122
+ .strict();
123
+
124
+ const derived_summary_field_schema = z.union([
125
+ derived_summary_count_field_schema,
126
+ derived_summary_select_field_schema,
127
+ ]);
88
128
 
129
+ /**
130
+ * @typedef {z.output<typeof derived_summary_schema>} DerivedSummaryConfig
131
+ */
89
132
  const derived_summary_schema = z
90
133
  .object({
91
134
  classes: z
@@ -98,6 +141,9 @@ const derived_summary_schema = z
98
141
  .strict()
99
142
  .superRefine(validateDerivedSummaryDefinition);
100
143
 
144
+ /**
145
+ * @typedef {z.output<typeof field_display_schema>} FieldDisplayConfig
146
+ */
101
147
  const field_display_schema = z
102
148
  .object({
103
149
  hidden: z.boolean().optional(),
@@ -105,6 +151,9 @@ const field_display_schema = z
105
151
  })
106
152
  .strict();
107
153
 
154
+ /**
155
+ * @typedef {z.output<typeof field_query_schema>} FieldQueryConfig
156
+ */
108
157
  const field_query_schema = z
109
158
  .object({
110
159
  contains: z.boolean().optional(),
@@ -118,6 +167,9 @@ const field_base_shape = {
118
167
  path_class: z.string().min(1).optional(),
119
168
  };
120
169
 
170
+ /**
171
+ * @typedef {z.output<typeof metadata_field_schema>} MetadataFieldConfig
172
+ */
121
173
  const metadata_field_schema = z.discriminatedUnion('type', [
122
174
  z
123
175
  .object({
@@ -167,12 +219,18 @@ const metadata_field_schema = z.discriminatedUnion('type', [
167
219
  .strict(),
168
220
  ]);
169
221
 
222
+ /**
223
+ * @typedef {z.output<typeof class_field_rule_schema>} ClassFieldRuleConfig
224
+ */
170
225
  const class_field_rule_schema = z
171
226
  .object({
172
227
  presence: z.enum(['required', 'optional', 'forbidden']),
173
228
  })
174
229
  .strict();
175
230
 
231
+ /**
232
+ * @typedef {z.output<typeof class_schema_schema>} ClassSchemaConfig
233
+ */
176
234
  const class_schema_schema = z
177
235
  .object({
178
236
  document_path_class: z.string().min(1).optional(),
@@ -181,6 +239,16 @@ const class_schema_schema = z
181
239
  })
182
240
  .strict();
183
241
 
242
+ /**
243
+ * @typedef {z.output<typeof repo_class_definition_schema>} RepoClassConfig
244
+ */
245
+ const repo_class_definition_schema = class_definition_schema.extend({
246
+ schema: class_schema_schema.optional(),
247
+ });
248
+
249
+ /**
250
+ * @typedef {z.output<typeof path_class_schema>} PathClassConfig
251
+ */
184
252
  const path_class_schema = z
185
253
  .object({
186
254
  prefixes: z
@@ -189,10 +257,14 @@ const path_class_schema = z
189
257
  })
190
258
  .strict();
191
259
 
260
+ /**
261
+ * @typedef {z.output<typeof patram_repo_config_schema>} PatramRepoConfig
262
+ */
192
263
  const patram_repo_config_schema = z
193
264
  .object({
194
- class_schemas: z.record(z.string().min(1), class_schema_schema).optional(),
195
- classes: z.unknown().optional(),
265
+ classes: z
266
+ .record(z.string().min(1), repo_class_definition_schema)
267
+ .optional(),
196
268
  derived_summaries: z
197
269
  .record(z.string().min(1), derived_summary_schema)
198
270
  .optional(),
@@ -201,14 +273,23 @@ const patram_repo_config_schema = z
201
273
  .array(z.string().min(1, 'Include globs must not be empty.'))
202
274
  .min(1, 'Include must contain at least one glob.')
203
275
  .default(DEFAULT_INCLUDE_PATTERNS),
204
- mappings: z.unknown().optional(),
276
+ mappings: z.record(z.string().min(1), mapping_definition_schema).optional(),
205
277
  path_classes: z.record(z.string().min(1), path_class_schema).optional(),
206
278
  queries: z.record(z.string().min(1), stored_query_schema).default({}),
207
- relations: z.unknown().optional(),
279
+ relations: z
280
+ .record(z.string().min(1), relation_definition_schema)
281
+ .optional(),
208
282
  })
209
283
  .strict()
210
284
  .superRefine(validateFieldDefinitionKeys);
211
285
 
286
+ /**
287
+ * @typedef {object} LoadPatramConfigResult
288
+ * @property {PatramRepoConfig | null} config
289
+ * @property {string} config_path
290
+ * @property {PatramDiagnostic[]} diagnostics
291
+ */
292
+
212
293
  /**
213
294
  * Load and validate the repo Patram config.
214
295
  *
@@ -229,6 +310,14 @@ export async function loadPatramConfig(project_directory = process.cwd()) {
229
310
  return createLoadResult(null, [parse_result.diagnostic]);
230
311
  }
231
312
 
313
+ const legacy_config_diagnostics = validateLegacyConfigShape(
314
+ parse_result.value,
315
+ );
316
+
317
+ if (legacy_config_diagnostics.length > 0) {
318
+ return createLoadResult(null, legacy_config_diagnostics);
319
+ }
320
+
232
321
  const config_result = patram_repo_config_schema.safeParse(parse_result.value);
233
322
 
234
323
  if (!config_result.success) {
@@ -382,61 +471,6 @@ function validateFieldDefinitionKeys(repo_config, refinement_context) {
382
471
  }
383
472
  }
384
473
 
385
- /**
386
- * @param {{ count?: unknown, default?: unknown, select?: unknown }} field_definition
387
- * @param {import('zod').RefinementCtx} refinement_context
388
- */
389
- function validateDerivedSummaryFieldDefinition(
390
- field_definition,
391
- refinement_context,
392
- ) {
393
- const evaluator_count =
394
- Number(field_definition.count !== undefined) +
395
- Number(field_definition.select !== undefined);
396
-
397
- if (evaluator_count !== 1) {
398
- refinement_context.addIssue({
399
- code: 'custom',
400
- message:
401
- 'Derived summary fields must define exactly one of "count" or "select".',
402
- });
403
- }
404
-
405
- if (
406
- field_definition.count !== undefined &&
407
- field_definition.default !== undefined
408
- ) {
409
- refinement_context.addIssue({
410
- code: 'custom',
411
- message: 'Derived summary count fields must not define "default".',
412
- path: ['default'],
413
- });
414
- }
415
-
416
- if (field_definition.select === undefined) {
417
- return;
418
- }
419
-
420
- if (
421
- Array.isArray(field_definition.select) &&
422
- field_definition.select.length === 0
423
- ) {
424
- refinement_context.addIssue({
425
- code: 'custom',
426
- message: 'Derived summary "select" must contain at least one case.',
427
- path: ['select'],
428
- });
429
- }
430
-
431
- if (field_definition.default === undefined) {
432
- refinement_context.addIssue({
433
- code: 'custom',
434
- message: 'Derived summary select fields must define "default".',
435
- path: ['default'],
436
- });
437
- }
438
- }
439
-
440
474
  /**
441
475
  * @param {{ fields: Array<{ name: string }> }} summary_definition
442
476
  * @param {import('zod').RefinementCtx} refinement_context
@@ -465,7 +499,7 @@ function validateDerivedSummaryDefinition(
465
499
  }
466
500
 
467
501
  /**
468
- * @param {{ include: string[], queries: Record<string, { where: string }>, classes?: unknown, mappings?: unknown, relations?: unknown }} repo_config
502
+ * @param {PatramRepoConfig} repo_config
469
503
  * @returns {PatramDiagnostic[]}
470
504
  */
471
505
  function validateGraphSchema(repo_config) {
@@ -479,7 +513,7 @@ function validateGraphSchema(repo_config) {
479
513
 
480
514
  try {
481
515
  parsePatramConfig({
482
- classes: repo_config.classes ?? {},
516
+ classes: collectGraphClassDefinitions(repo_config.classes),
483
517
  mappings: repo_config.mappings ?? {},
484
518
  relations: repo_config.relations ?? {},
485
519
  });
@@ -509,9 +543,8 @@ function validateFieldSchemaConfig(repo_config) {
509
543
  collectClassSchemaConfigDiagnostics(
510
544
  diagnostics,
511
545
  path_classes,
512
- classes,
513
546
  fields,
514
- repo_config.class_schemas,
547
+ classes,
515
548
  );
516
549
 
517
550
  return diagnostics;
@@ -586,7 +619,7 @@ function createDefaultRepoConfig() {
586
619
  }
587
620
 
588
621
  /**
589
- * @param {{ class_schemas?: unknown, classes?: unknown, derived_summaries?: unknown, fields?: unknown, include: string[], mappings?: unknown, path_classes?: unknown, queries: Record<string, { where: string }>, relations?: unknown }} repo_config
622
+ * @param {PatramRepoConfig} repo_config
590
623
  * @returns {PatramRepoConfig}
591
624
  */
592
625
  function normalizeRepoConfig(repo_config) {
@@ -596,11 +629,6 @@ function normalizeRepoConfig(repo_config) {
596
629
  queries: { ...repo_config.queries },
597
630
  };
598
631
 
599
- assignOptionalRepoConfigField(
600
- normalized_config,
601
- 'class_schemas',
602
- repo_config.class_schemas,
603
- );
604
632
  assignOptionalRepoConfigField(
605
633
  normalized_config,
606
634
  'classes',
@@ -635,6 +663,27 @@ function normalizeRepoConfig(repo_config) {
635
663
  return normalized_config;
636
664
  }
637
665
 
666
+ /**
667
+ * @param {unknown} config_value
668
+ * @returns {PatramDiagnostic[]}
669
+ */
670
+ function validateLegacyConfigShape(config_value) {
671
+ if (
672
+ config_value === null ||
673
+ typeof config_value !== 'object' ||
674
+ !Object.hasOwn(config_value, 'class_schemas')
675
+ ) {
676
+ return [];
677
+ }
678
+
679
+ return [
680
+ createConfigDiagnostic(
681
+ 'class_schemas',
682
+ 'Top-level "class_schemas" is not supported. Move entries into classes.<name>.schema.',
683
+ ),
684
+ ];
685
+ }
686
+
638
687
  /**
639
688
  * @param {PatramDiagnostic[]} diagnostics
640
689
  * @param {Map<string, string>} class_coverage
@@ -669,7 +718,7 @@ function collectDuplicateClassDiagnostics(
669
718
  * @param {Set<string>} known_relation_names
670
719
  * @param {PatramRepoConfig} repo_config
671
720
  * @param {string} summary_name
672
- * @param {import('./load-patram-config.types.ts').DerivedSummaryFieldConfig[]} field_definitions
721
+ * @param {DerivedSummaryFieldConfig[]} field_definitions
673
722
  */
674
723
  function collectDerivedSummaryFieldDiagnostics(
675
724
  diagnostics,
@@ -861,35 +910,22 @@ function collectFieldPathClassDiagnostic(
861
910
  /**
862
911
  * @param {PatramDiagnostic[]} diagnostics
863
912
  * @param {Record<string, { prefixes: string[] }>} path_classes
864
- * @param {Record<string, unknown>} classes
865
913
  * @param {Record<string, MetadataFieldConfig>} fields
866
- * @param {PatramRepoConfig['class_schemas']} class_schemas
914
+ * @param {NonNullable<PatramRepoConfig['classes']>} classes
867
915
  */
868
916
  function collectClassSchemaConfigDiagnostics(
869
917
  diagnostics,
870
918
  path_classes,
871
- classes,
872
919
  fields,
873
- class_schemas,
920
+ classes,
874
921
  ) {
875
- if (!class_schemas) {
876
- return;
877
- }
922
+ for (const [class_name, class_definition] of Object.entries(classes)) {
923
+ const schema_definition = class_definition.schema;
878
924
 
879
- for (const class_name of Object.keys(class_schemas)) {
880
- if (classes[class_name]) {
925
+ if (!schema_definition) {
881
926
  continue;
882
927
  }
883
928
 
884
- diagnostics.push(
885
- createConfigDiagnostic(
886
- `class_schemas.${class_name}`,
887
- `Unknown class "${class_name}".`,
888
- ),
889
- );
890
- }
891
-
892
- for (const [class_name, schema_definition] of Object.entries(class_schemas)) {
893
929
  for (const field_name of Object.keys(schema_definition.fields)) {
894
930
  if (fields[field_name]) {
895
931
  continue;
@@ -897,14 +933,20 @@ function collectClassSchemaConfigDiagnostics(
897
933
 
898
934
  diagnostics.push(
899
935
  createConfigDiagnostic(
900
- `class_schemas.${class_name}.fields.${field_name}`,
936
+ `classes.${class_name}.schema.fields.${field_name}`,
901
937
  `Unknown field "${field_name}".`,
902
938
  ),
903
939
  );
904
940
  }
905
941
  }
906
942
 
907
- for (const [class_name, schema_definition] of Object.entries(class_schemas)) {
943
+ for (const [class_name, class_definition] of Object.entries(classes)) {
944
+ const schema_definition = class_definition.schema;
945
+
946
+ if (!schema_definition) {
947
+ continue;
948
+ }
949
+
908
950
  if (
909
951
  schema_definition.document_path_class === undefined ||
910
952
  path_classes[schema_definition.document_path_class]
@@ -914,13 +956,31 @@ function collectClassSchemaConfigDiagnostics(
914
956
 
915
957
  diagnostics.push(
916
958
  createConfigDiagnostic(
917
- `class_schemas.${class_name}.document_path_class`,
959
+ `classes.${class_name}.schema.document_path_class`,
918
960
  `Unknown path class "${schema_definition.document_path_class}".`,
919
961
  ),
920
962
  );
921
963
  }
922
964
  }
923
965
 
966
+ /**
967
+ * @param {PatramRepoConfig['classes']} classes
968
+ * @returns {Record<string, ClassDefinition>}
969
+ */
970
+ function collectGraphClassDefinitions(classes) {
971
+ /** @type {Record<string, ClassDefinition>} */
972
+ const graph_class_definitions = {};
973
+
974
+ for (const [class_name, class_definition] of Object.entries(classes ?? {})) {
975
+ graph_class_definitions[class_name] = {
976
+ builtin: class_definition.builtin,
977
+ label: class_definition.label,
978
+ };
979
+ }
980
+
981
+ return graph_class_definitions;
982
+ }
983
+
924
984
  /**
925
985
  * @template {Exclude<keyof PatramRepoConfig, 'include' | 'queries'>} TKey
926
986
  * @param {PatramRepoConfig} normalized_config
@@ -1,158 +1,56 @@
1
- import type {
2
- ClassDefinition,
3
- MappingDefinition,
4
- RelationDefinition,
5
- } from './patram-config.types.ts';
6
-
7
- export interface StoredQueryConfig {
8
- where: string;
9
- }
10
-
1
+ export type StoredQueryConfig =
2
+ import('./load-patram-config.js').StoredQueryConfig;
3
+ export type FieldDisplayConfig =
4
+ import('./load-patram-config.js').FieldDisplayConfig;
5
+ export type FieldQueryConfig =
6
+ import('./load-patram-config.js').FieldQueryConfig;
11
7
  export type FieldValueTypeName =
12
- | 'string'
13
- | 'integer'
14
- | 'enum'
15
- | 'path'
16
- | 'glob'
17
- | 'date'
18
- | 'date_time';
19
-
20
- export interface FieldDisplayConfig {
21
- hidden?: boolean;
22
- order?: number;
23
- }
24
-
25
- export interface FieldQueryConfig {
26
- contains?: boolean;
27
- prefix?: boolean;
28
- }
29
-
30
- export interface StringFieldConfig {
31
- display?: FieldDisplayConfig;
32
- multiple?: boolean;
33
- query?: FieldQueryConfig;
34
- type: 'string';
35
- }
36
-
37
- export interface IntegerFieldConfig {
38
- display?: FieldDisplayConfig;
39
- multiple?: boolean;
40
- type: 'integer';
41
- }
42
-
43
- export interface EnumFieldConfig {
44
- display?: FieldDisplayConfig;
45
- multiple?: boolean;
46
- type: 'enum';
47
- values: string[];
48
- }
49
-
50
- export interface PathFieldConfig {
51
- display?: FieldDisplayConfig;
52
- multiple?: boolean;
53
- path_class?: string;
54
- type: 'path';
55
- }
56
-
57
- export interface GlobFieldConfig {
58
- display?: FieldDisplayConfig;
59
- multiple?: boolean;
60
- type: 'glob';
61
- }
62
-
63
- export interface DateFieldConfig {
64
- display?: FieldDisplayConfig;
65
- multiple?: boolean;
66
- type: 'date';
67
- }
68
-
69
- export interface DateTimeFieldConfig {
70
- display?: FieldDisplayConfig;
71
- multiple?: boolean;
72
- type: 'date_time';
73
- }
74
-
8
+ import('./load-patram-config.js').MetadataFieldConfig['type'];
75
9
  export type MetadataFieldConfig =
76
- | DateFieldConfig
77
- | DateTimeFieldConfig
78
- | EnumFieldConfig
79
- | GlobFieldConfig
80
- | IntegerFieldConfig
81
- | PathFieldConfig
82
- | StringFieldConfig;
83
-
84
- export interface ClassFieldRuleConfig {
85
- presence: 'required' | 'optional' | 'forbidden';
86
- }
87
-
10
+ import('./load-patram-config.js').MetadataFieldConfig;
11
+ export type StringFieldConfig = Extract<
12
+ MetadataFieldConfig,
13
+ { type: 'string' }
14
+ >;
15
+ export type IntegerFieldConfig = Extract<
16
+ MetadataFieldConfig,
17
+ { type: 'integer' }
18
+ >;
19
+ export type EnumFieldConfig = Extract<MetadataFieldConfig, { type: 'enum' }>;
20
+ export type PathFieldConfig = Extract<MetadataFieldConfig, { type: 'path' }>;
21
+ export type GlobFieldConfig = Extract<MetadataFieldConfig, { type: 'glob' }>;
22
+ export type DateFieldConfig = Extract<MetadataFieldConfig, { type: 'date' }>;
23
+ export type DateTimeFieldConfig = Extract<
24
+ MetadataFieldConfig,
25
+ { type: 'date_time' }
26
+ >;
27
+ export type ClassFieldRuleConfig =
28
+ import('./load-patram-config.js').ClassFieldRuleConfig;
88
29
  export type DirectiveTypeConfig = MetadataFieldConfig;
89
30
  export type MetadataDirectiveRuleConfig = ClassFieldRuleConfig;
90
-
91
- export interface ClassSchemaConfig {
92
- document_path_class?: string;
93
- fields: Record<string, ClassFieldRuleConfig>;
94
- unknown_fields?: 'ignore' | 'error';
95
- }
96
-
31
+ export type ClassSchemaConfig =
32
+ import('./load-patram-config.js').ClassSchemaConfig;
97
33
  export type MetadataSchemaConfig = ClassSchemaConfig;
98
-
99
- export interface PathClassConfig {
100
- prefixes: string[];
101
- }
102
-
103
- export type DerivedSummaryScalar = boolean | number | string | null;
104
-
105
- export interface DerivedSummaryCountFieldConfig {
106
- count: {
107
- traversal: string;
108
- where: string;
109
- };
110
- name: string;
111
- }
112
-
113
- export interface DerivedSummarySelectCaseConfig {
114
- value: DerivedSummaryScalar;
115
- when: string;
116
- }
117
-
118
- export interface DerivedSummarySelectFieldConfig {
119
- default: DerivedSummaryScalar;
120
- name: string;
121
- select: DerivedSummarySelectCaseConfig[];
122
- }
123
-
34
+ export type PathClassConfig = import('./load-patram-config.js').PathClassConfig;
35
+ export type DerivedSummaryScalar =
36
+ import('./load-patram-config.js').DerivedSummaryScalar;
37
+ export type DerivedSummarySelectCaseConfig =
38
+ import('./load-patram-config.js').DerivedSummarySelectCaseConfig;
124
39
  export type DerivedSummaryFieldConfig =
125
- | DerivedSummaryCountFieldConfig
126
- | DerivedSummarySelectFieldConfig;
127
-
128
- export interface DerivedSummaryConfig {
129
- classes: string[];
130
- fields: DerivedSummaryFieldConfig[];
131
- }
132
-
133
- export interface PatramRepoConfig {
134
- class_schemas?: Record<string, ClassSchemaConfig>;
135
- classes?: Record<string, ClassDefinition>;
136
- derived_summaries?: Record<string, DerivedSummaryConfig>;
137
- fields?: Record<string, MetadataFieldConfig>;
138
- include: string[];
139
- mappings?: Record<string, MappingDefinition>;
140
- path_classes?: Record<string, PathClassConfig>;
141
- queries: Record<string, StoredQueryConfig>;
142
- relations?: Record<string, RelationDefinition>;
143
- }
144
-
145
- export interface PatramDiagnostic {
146
- code: string;
147
- column: number;
148
- level: 'error';
149
- line: number;
150
- message: string;
151
- path: string;
152
- }
153
-
154
- export interface LoadPatramConfigResult {
155
- config: PatramRepoConfig | null;
156
- config_path: string;
157
- diagnostics: PatramDiagnostic[];
158
- }
40
+ import('./load-patram-config.js').DerivedSummaryFieldConfig;
41
+ export type DerivedSummaryCountFieldConfig = Extract<
42
+ DerivedSummaryFieldConfig,
43
+ { count: unknown }
44
+ >;
45
+ export type DerivedSummarySelectFieldConfig = Extract<
46
+ DerivedSummaryFieldConfig,
47
+ { select: unknown }
48
+ >;
49
+ export type DerivedSummaryConfig =
50
+ import('./load-patram-config.js').DerivedSummaryConfig;
51
+ export type PatramRepoConfig =
52
+ import('./load-patram-config.js').PatramRepoConfig;
53
+ export type PatramDiagnostic =
54
+ import('./load-patram-config.js').PatramDiagnostic;
55
+ export type LoadPatramConfigResult =
56
+ import('./load-patram-config.js').LoadPatramConfigResult;
@@ -1,6 +1,5 @@
1
- /** @import * as $k$$l$parse$j$claims$k$types$k$ts from './parse-claims.types.ts'; */
2
1
  /**
3
- * @import { ParseClaimsInput, ParseSourceFileResult } from './parse-claims.types.ts';
2
+ * @import { ParseClaimsInput, ParseSourceFileResult, PatramClaim } from './parse-claims.types.ts';
4
3
  */
5
4
 
6
5
  import { getFileExtension } from './claim-helpers.js';
@@ -51,7 +50,7 @@ export function parseSourceFile(parse_input) {
51
50
  * Parse a file into neutral Patram claims.
52
51
  *
53
52
  * @param {ParseClaimsInput} parse_input
54
- * @returns {$k$$l$parse$j$claims$k$types$k$ts.PatramClaim[]}
53
+ * @returns {PatramClaim[]}
55
54
  */
56
55
  export function parseClaims(parse_input) {
57
56
  return parseSourceFile(parse_input).claims;
@@ -1,6 +1,6 @@
1
- /** @import * as $k$$l$load$j$patram$j$config$k$types$k$ts from './load-patram-config.types.ts'; */
2
1
  /**
3
- * @import { ParseClaimsInput, ParseSourceFileResult, PatramClaim, PatramClaimFields } from './parse-claims.types.ts';
2
+ * @import { PatramDiagnostic } from './load-patram-config.types.ts';
3
+ * @import { ParseClaimsInput, ParseSourceFileResult, PatramClaimFields } from './parse-claims.types.ts';
4
4
  */
5
5
 
6
6
  import {
@@ -266,7 +266,7 @@ function compareClaimEntries(left_entry, right_entry) {
266
266
  /**
267
267
  * @param {string} file_path
268
268
  * @param {{ activation_column: number | null, activation_line: number | null }} jsdoc_block
269
- * @returns {$k$$l$load$j$patram$j$config$k$types$k$ts.PatramDiagnostic}
269
+ * @returns {PatramDiagnostic}
270
270
  */
271
271
  function createMultiplePatramBlocksDiagnostic(file_path, jsdoc_block) {
272
272
  return {
@@ -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
- const class_definition_schema = z
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
- const relation_definition_schema = z
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
- const mapping_definition_schema = z
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 {PatramConfig}
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 {PatramConfig} config_json
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 {PatramConfig} config_json
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 {PatramConfig} config_json
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 {PatramConfig} config_json
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 {import('./output-view.types.ts').OutputDerivedSummary | null} derived_summary
173
+ * @param {OutputDerivedSummary | null} derived_summary
175
174
  * @param {NonNullable<PatramRepoConfig['fields']>} field_definitions
176
- * @returns {$k$$l$output$j$view$k$types$k$ts.OutputNodeItem}
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 {import('./output-view.types.ts').OutputDerivedSummary | null} derived_summary
205
+ * @param {OutputDerivedSummary | null} derived_summary
207
206
  * @param {GraphNode | undefined} graph_node
208
- * @returns {$k$$l$output$j$view$k$types$k$ts.OutputResolvedLinkTarget}
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 {$k$$l$output$j$view$k$types$k$ts.OutputResolvedLinkTarget} */
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 {$k$$l$output$j$view$k$types$k$ts.OutputResolvedLinkItem}
290
+ * @returns {OutputResolvedLinkItem}
292
291
  */
293
292
  function createResolvedLinkOutputItem(resolved_link, command_options) {
294
293
  const target_graph_node = resolveDocumentGraphNode(
@@ -1,4 +1,5 @@
1
1
  /**
2
+ * @import { ClassDefinition } from './patram-config.js';
2
3
  * @import { PatramRepoConfig } from './load-patram-config.types.ts';
3
4
  * @import { PatramConfig } from './patram-config.types.ts';
4
5
  */
@@ -74,7 +75,7 @@ export function resolvePatramGraphConfig(repo_config) {
74
75
  const graph_config = parsePatramConfig({
75
76
  classes: {
76
77
  ...BUILT_IN_PATRAM_CONFIG.classes,
77
- ...repo_config.classes,
78
+ ...collectGraphClassDefinitions(repo_config.classes),
78
79
  },
79
80
  mappings: {
80
81
  ...BUILT_IN_PATRAM_CONFIG.mappings,
@@ -88,7 +89,44 @@ export function resolvePatramGraphConfig(repo_config) {
88
89
 
89
90
  return {
90
91
  ...graph_config,
91
- class_schemas: repo_config.class_schemas,
92
+ classes: mergeResolvedClasses(graph_config.classes, repo_config.classes),
92
93
  fields: repo_config.fields,
93
94
  };
94
95
  }
96
+
97
+ /**
98
+ * @param {PatramRepoConfig['classes']} classes
99
+ * @returns {Record<string, ClassDefinition>}
100
+ */
101
+ function collectGraphClassDefinitions(classes) {
102
+ /** @type {Record<string, ClassDefinition>} */
103
+ const graph_class_definitions = {};
104
+
105
+ for (const [class_name, class_definition] of Object.entries(classes ?? {})) {
106
+ graph_class_definitions[class_name] = {
107
+ builtin: class_definition.builtin,
108
+ label: class_definition.label,
109
+ };
110
+ }
111
+
112
+ return graph_class_definitions;
113
+ }
114
+
115
+ /**
116
+ * @param {Record<string, ClassDefinition>} graph_classes
117
+ * @param {PatramRepoConfig['classes']} repo_classes
118
+ * @returns {PatramConfig['classes']}
119
+ */
120
+ function mergeResolvedClasses(graph_classes, repo_classes) {
121
+ /** @type {PatramConfig['classes']} */
122
+ const resolved_classes = {};
123
+
124
+ for (const [class_name, class_definition] of Object.entries(graph_classes)) {
125
+ resolved_classes[class_name] = {
126
+ ...class_definition,
127
+ schema: repo_classes?.[class_name]?.schema,
128
+ };
129
+ }
130
+
131
+ return resolved_classes;
132
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "patram",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "type": "module",
5
5
  "main": "./lib/patram.js",
6
6
  "exports": {