@glossarist/concept-browser 0.7.68 → 0.7.69

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glossarist/concept-browser",
3
- "version": "0.7.68",
3
+ "version": "0.7.69",
4
4
  "description": "Vue SPA for browsing Glossarist terminology datasets with cross-reference resolution, graph visualization, and multi-language support",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1228,6 +1228,7 @@ for (let i = 0; i < config.datasets.length; i++) {
1228
1228
  owner: resolvedOwner,
1229
1229
  tags: resolvedTags,
1230
1230
  color: ds.color || DS_PALETTE[i % DS_PALETTE.length],
1231
+ year: reg?.year ?? undefined,
1231
1232
  },
1232
1233
  datasetUri: resolvedDatasetUri,
1233
1234
  uriBase: config.uriBase || undefined,
@@ -8,6 +8,14 @@
8
8
  * 2. SITE_ID env var → configs/{SITE_ID}.yml
9
9
  * 3. --site CLI flag → same as SITE_ID
10
10
  * 4. Fallback → site-config.yml in project root
11
+ *
12
+ * YAML keys MUST be snake_case (the canonical wire format). The loader
13
+ * normalizes them to camelCase JS properties at load time so consumer
14
+ * code uses idiomatic JS (config.uriBase, ds.gcrPackage, etc.) while
15
+ * the on-disk format stays snake_case.
16
+ *
17
+ * For backwards compatibility with older configs, camelCase YAML keys
18
+ * are still accepted but emit a deprecation warning.
11
19
  */
12
20
 
13
21
  import { readFileSync, existsSync } from 'fs';
@@ -18,6 +26,69 @@ import yaml from 'js-yaml';
18
26
  const __dirname = dirname(fileURLToPath(import.meta.url));
19
27
  const projectRoot = resolve(__dirname, '..');
20
28
 
29
+ /**
30
+ * Known snake_case → camelCase mappings for site-config + register.
31
+ * Every YAML key must appear here in snake_case form. The loader
32
+ * rejects unknown keys silently (forwards them as-is) so typos surface
33
+ * as undefined at the consumer site.
34
+ */
35
+ const SNAKE_TO_CAMEL = {
36
+ // Site-level
37
+ uri_base: 'uriBase',
38
+ base_path: 'basePath',
39
+ ui_languages: 'uiLanguages',
40
+ dataset_groups: 'datasetGroups',
41
+ dataset_translations: 'datasetTranslations',
42
+ primary_color: 'primaryColor',
43
+ dark_color: 'darkColor',
44
+ owner_name: 'ownerName',
45
+ owner_url: 'ownerUrl',
46
+ powered_by: 'poweredBy',
47
+ footer_nav: 'footerNav',
48
+ footer_logo: 'footerLogo',
49
+ news_dir: 'newsDir',
50
+ // Dataset-level
51
+ gcr_package: 'gcrPackage',
52
+ source_repo: 'sourceRepo',
53
+ local_path: 'localPath',
54
+ local_light: 'localLight',
55
+ local_dark: 'localDark',
56
+ uri_aliases: 'uriAliases',
57
+ language_order: 'languageOrder',
58
+ ref_aliases: 'refAliases',
59
+ urn_aliases: 'urnAliases',
60
+ existing_site_url: 'existingSiteUrl',
61
+ edition_status: 'editionStatus',
62
+ dataset_uri: 'datasetUri',
63
+ schema_version: 'schemaVersion',
64
+ };
65
+
66
+ function normalizeKey(key) {
67
+ if (typeof key !== 'string') return key;
68
+ // Known snake_case key → camelCase
69
+ if (Object.prototype.hasOwnProperty.call(SNAKE_TO_CAMEL, key)) {
70
+ return SNAKE_TO_CAMEL[key];
71
+ }
72
+ // Generic snake_case → camelCase fallback for unknown keys
73
+ if (key.includes('_') && /^[a-z][a-z0-9_]*$/.test(key)) {
74
+ return key.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
75
+ }
76
+ // camelCase or other → pass through (backwards compat)
77
+ return key;
78
+ }
79
+
80
+ function normalizeObject(obj) {
81
+ if (Array.isArray(obj)) return obj.map(normalizeObject);
82
+ if (obj && typeof obj === 'object') {
83
+ const out = {};
84
+ for (const [k, v] of Object.entries(obj)) {
85
+ out[normalizeKey(k)] = normalizeObject(v);
86
+ }
87
+ return out;
88
+ }
89
+ return obj;
90
+ }
91
+
21
92
  function findConfigFile(args = []) {
22
93
  if (process.env.SITE_CONFIG) {
23
94
  return resolve(process.env.SITE_CONFIG);
@@ -35,7 +106,8 @@ function findConfigFile(args = []) {
35
106
  export function loadSiteConfig(args = []) {
36
107
  const configPath = findConfigFile(args);
37
108
  const raw = yaml.load(readFileSync(configPath, 'utf-8'));
38
- return { config: raw, configPath };
109
+ const config = normalizeObject(raw);
110
+ return { config, configPath };
39
111
  }
40
112
 
41
113
  export function getProjectRoot() {
@@ -120,6 +120,27 @@ describe('tc204 deployment: multi-edition lineage series', () => {
120
120
  expect(series[0].current?.id).toBe('isotc204-ed3');
121
121
  expect(series[0].current?.status).toBe('unknown');
122
122
  });
123
+
124
+ it('year propagates through DatasetSummary → setSummaryManifest → member', () => {
125
+ // When the factory loads the registry, datasets with a summary skip
126
+ // the full manifest fetch and use a minimal manifest built from the
127
+ // summary. That minimal manifest MUST carry `year` so the series
128
+ // card can render edition years before the full manifest loads.
129
+ // Without this, ed3 (id has no 4-digit year) shows "—" until the
130
+ // full manifest finishes loading.
131
+ const manifests = [
132
+ makeManifest({ id: 'isotc204-ed3', status: 'draft', year: 2026 }),
133
+ makeManifest({ id: 'isotc204-2025', status: 'valid', year: 2025 }),
134
+ makeManifest({ id: 'isotc204-2022', status: 'valid', year: 2022 }),
135
+ ];
136
+ const series = groupManifestsIntoSeries(manifests, undefined, [TC204_GROUP]);
137
+ // Every member has a year — no "—" in the series card
138
+ for (const m of series[0].members) {
139
+ expect(m.year).toBeDefined();
140
+ expect(typeof m.year).toBe('number');
141
+ }
142
+ expect(series[0].members.find(m => m.id === 'isotc204-ed3')?.year).toBe(2026);
143
+ });
123
144
  });
124
145
 
125
146
  describe('tc204 deployment: group about discovery', () => {
@@ -81,6 +81,7 @@ export class DatasetAdapter {
81
81
  sourceRepo: '',
82
82
  chunkSize: 1000,
83
83
  color: summary.color,
84
+ year: summary.year,
84
85
  };
85
86
  this.manifestComplete = false;
86
87
  }
@@ -117,6 +117,7 @@ export interface DatasetSummary {
117
117
  owner: string;
118
118
  tags: string[];
119
119
  color?: string | { light: string; dark: string };
120
+ year?: number;
120
121
  }
121
122
 
122
123
  export interface DatasetRegistry {