@glossarist/concept-browser 0.7.67 → 0.7.68

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.67",
3
+ "version": "0.7.68",
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": {
@@ -1081,6 +1081,7 @@ async function processDataset(dir, register, opts) {
1081
1081
  sourceRepo: opts.sourceRepo,
1082
1082
  chunkSize: CHUNK_SIZE,
1083
1083
  color: opts.color,
1084
+ year: opts.year ?? undefined,
1084
1085
  languageStats: langStats,
1085
1086
  availableFormats,
1086
1087
  bulkFormats,
@@ -1199,6 +1200,7 @@ for (let i = 0; i < config.datasets.length; i++) {
1199
1200
  languageOrder: firstNonEmpty(reg?.languageOrder, ds.languageOrder),
1200
1201
  ref: firstNonEmpty(reg?.ref, ds.ref),
1201
1202
  refAliases: firstNonEmpty(reg?.refAliases, ds.refAliases),
1203
+ year: reg?.year ?? null,
1202
1204
  tags: firstNonEmpty(reg?.tags, ds.tags),
1203
1205
  color: ds.color || DS_PALETTE[i % DS_PALETTE.length],
1204
1206
  datasetUri: firstNonEmpty(reg?.urn, ds.uri),
@@ -0,0 +1,139 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { groupManifestsIntoSeries } from '../composables/useDatasetSeries';
3
+ import type { Manifest } from '../adapters/types';
4
+
5
+ // Integration tests for the isotc204.geolexica.org deployment shape:
6
+ // three editions of ISO 14812 (2022 / 2025 / ed3-draft) wired as a
7
+ // lineage-series group with an explicit `current` pointing at the draft.
8
+ //
9
+ // These tests verify the exact config shape that tc204 PR #28 uses, so
10
+ // regressions specific to that deployment are caught here rather than
11
+ // in production.
12
+
13
+ function makeManifest(overrides: Partial<Manifest> & { id: string }): Manifest {
14
+ const base: Manifest = {
15
+ id: overrides.id,
16
+ title: overrides.title ?? overrides.id,
17
+ description: overrides.description ?? '',
18
+ owner: overrides.owner ?? '',
19
+ baseUrl: overrides.baseUrl ?? '/',
20
+ languages: overrides.languages ?? ['eng'],
21
+ conceptCount: overrides.conceptCount ?? 0,
22
+ conceptUrlTemplate: overrides.conceptUrlTemplate ?? '/concept/{id}',
23
+ indexUrl: overrides.indexUrl ?? '/index.json',
24
+ contextUrl: overrides.contextUrl ?? '/context.json',
25
+ uriBase: overrides.uriBase ?? 'https://isotc204.geolexica.org',
26
+ status: overrides.status ?? 'valid',
27
+ schemaVersion: overrides.schemaVersion ?? '3',
28
+ tags: overrides.tags ?? [],
29
+ lastUpdated: overrides.lastUpdated ?? '',
30
+ sourceRepo: overrides.sourceRepo ?? '',
31
+ chunkSize: overrides.chunkSize ?? 0,
32
+ datasetUri: overrides.datasetUri ?? '',
33
+ };
34
+ return { ...base, ...overrides } as Manifest;
35
+ }
36
+
37
+ const TC204_GROUP = {
38
+ id: 'isotc204',
39
+ datasets: ['isotc204-ed3', 'isotc204-2025', 'isotc204-2022'],
40
+ kind: 'lineage' as const,
41
+ current: 'isotc204-ed3',
42
+ label: 'ISO/TC 204 ITS Vocabulary',
43
+ };
44
+
45
+ describe('tc204 deployment: multi-edition lineage series', () => {
46
+ it('groups all three editions into one series', () => {
47
+ const manifests = [
48
+ makeManifest({ id: 'isotc204-2022', status: 'valid', year: 2022, title: 'Edition 1' }),
49
+ makeManifest({ id: 'isotc204-2025', status: 'valid', year: 2025, title: 'Edition 2' }),
50
+ makeManifest({ id: 'isotc204-ed3', status: 'draft', year: 2026, title: 'Edition 3 draft' }),
51
+ ];
52
+ const series = groupManifestsIntoSeries(manifests, undefined, [TC204_GROUP]);
53
+ expect(series.length).toBe(1);
54
+ expect(series[0].members.length).toBe(3);
55
+ expect(series[0].members.map(m => m.id)).toContain('isotc204-ed3');
56
+ });
57
+
58
+ it('honors explicit current=isotc204-ed3 (the draft)', () => {
59
+ // tc204 marks ed3 (draft) as current because it's the active working
60
+ // edition, even though it's not yet published. The fallback logic
61
+ // would pick the newest *valid* member, which would be 2025. The
62
+ // explicit `current` override must win.
63
+ const manifests = [
64
+ makeManifest({ id: 'isotc204-2022', status: 'valid', year: 2022 }),
65
+ makeManifest({ id: 'isotc204-2025', status: 'valid', year: 2025 }),
66
+ makeManifest({ id: 'isotc204-ed3', status: 'draft', year: 2026 }),
67
+ ];
68
+ const series = groupManifestsIntoSeries(manifests, undefined, [TC204_GROUP]);
69
+ expect(series[0].current?.id).toBe('isotc204-ed3');
70
+ });
71
+
72
+ it('sorts members by year ascending — ed3 (2026) sorts LAST, not first', () => {
73
+ // Without manifest.year, extractYear('isotc204-ed3') returns undefined
74
+ // and the member sorts as year=0 (oldest). With register.yaml:year
75
+ // propagated to manifest.year, ed3 correctly sorts as the newest.
76
+ // This is the tc204-specific regression: drafts without a year in
77
+ // their id must still be positioned via the explicit year field.
78
+ const manifests = [
79
+ makeManifest({ id: 'isotc204-ed3', status: 'draft', year: 2026, title: 'E3' }),
80
+ makeManifest({ id: 'isotc204-2025', status: 'valid', year: 2025, title: 'E2' }),
81
+ makeManifest({ id: 'isotc204-2022', status: 'valid', year: 2022, title: 'E1' }),
82
+ ];
83
+ const series = groupManifestsIntoSeries(manifests, undefined, [TC204_GROUP]);
84
+ const ids = series[0].members.map(m => m.id);
85
+ expect(ids).toEqual(['isotc204-2022', 'isotc204-2025', 'isotc204-ed3']);
86
+ });
87
+
88
+ it('includes stub members for configured-but-unfetched editions', () => {
89
+ // tc204's ed3 GCR is generated from a draft ontology; it may be
90
+ // unavailable on some builds. The series deliberately includes
91
+ // stub members for every configured dataset so navigation links
92
+ // survive partial deployments.
93
+ const manifests = [
94
+ makeManifest({ id: 'isotc204-2022', status: 'valid', year: 2022 }),
95
+ makeManifest({ id: 'isotc204-2025', status: 'valid', year: 2025 }),
96
+ // isotc204-ed3 missing — GCR download may have failed
97
+ ];
98
+ const series = groupManifestsIntoSeries(manifests, undefined, [TC204_GROUP]);
99
+ // All 3 configured members appear; the missing one is a stub
100
+ expect(series[0].members.length).toBe(3);
101
+ const ed3 = series[0].members.find(m => m.id === 'isotc204-ed3');
102
+ expect(ed3).toBeDefined();
103
+ expect(ed3?.status).toBe('unknown');
104
+ });
105
+
106
+ it('honors configured current even when the member is a stub', () => {
107
+ // When ed3 fails to load AND the config says current=ed3, the stub
108
+ // member is still marked as current. This is deliberate: the config
109
+ // is the source of truth for which edition is "current". A stub
110
+ // current signals "this edition exists but isn't loaded" — the UI
111
+ // can surface that state.
112
+ //
113
+ // TODO: consider falling back to newest valid when the configured
114
+ // current has no manifest. Filed as a follow-up.
115
+ const manifests = [
116
+ makeManifest({ id: 'isotc204-2022', status: 'valid', year: 2022 }),
117
+ makeManifest({ id: 'isotc204-2025', status: 'valid', year: 2025 }),
118
+ ];
119
+ const series = groupManifestsIntoSeries(manifests, undefined, [TC204_GROUP]);
120
+ expect(series[0].current?.id).toBe('isotc204-ed3');
121
+ expect(series[0].current?.status).toBe('unknown');
122
+ });
123
+ });
124
+
125
+ describe('tc204 deployment: group about discovery', () => {
126
+ // tc204 ships group about content at:
127
+ // site-content/groups/isotc204/about/about.eng.md
128
+ // The build pipeline's ABOUT step must find and compile it.
129
+ // See scripts/__tests__/process-about-pages.test.mjs for the
130
+ // filesystem discovery contract.
131
+ it('site-content/groups/<id>/about/ layout is discoverable', async () => {
132
+ // Smoke check: the process-about-pages discovery logic handles
133
+ // the tc204 layout. The actual discovery test is in
134
+ // scripts/__tests__/process-about-pages.test.mjs (group case).
135
+ // Here we just verify the path convention matches.
136
+ const expectedPath = 'site-content/groups/isotc204/about/about.eng.md';
137
+ expect(expectedPath).toMatch(/^site-content\/groups\/[^/]+\/about\/about\.\w+\.(md|adoc|html)$/);
138
+ });
139
+ });
@@ -70,6 +70,13 @@ export interface Manifest {
70
70
  ref?: string;
71
71
  refAliases?: string[];
72
72
  editionStatus?: string;
73
+ /**
74
+ * Edition year, sourced from register.yaml:year. When the dataset id
75
+ * doesn't contain a parseable year (e.g. "isotc204-ed3"), this field
76
+ * is the only way to position the edition correctly in a lineage
77
+ * timeline. Falls back to extractYear(id/ref/title) at the consumer.
78
+ */
79
+ year?: number;
73
80
  ordering?: string;
74
81
  sections?: ManifestSection[];
75
82
  languageStats?: Record<string, { terms: number; definitions: number }>;
@@ -92,7 +92,7 @@ function deriveSeriesTitle(m: Manifest | undefined): string {
92
92
  }
93
93
 
94
94
  function manifestToMember(m: Manifest, activeDatasetId?: string): DatasetSeriesMember {
95
- const year = extractYear(m.id) ?? extractYear(m.ref ?? '') ?? extractYear(m.title);
95
+ const year = m.year ?? extractYear(m.id) ?? extractYear(m.ref ?? '') ?? extractYear(m.title);
96
96
  return {
97
97
  id: m.id,
98
98
  ref: m.ref ?? m.title,