@glossarist/concept-browser 0.7.51 → 0.7.52
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/cli/index.mjs +32 -0
- package/env.d.ts +15 -0
- package/package.json +12 -2
- package/scripts/__tests__/doctor.test.mjs +147 -0
- package/scripts/doctor.mjs +327 -0
- package/scripts/generate-data.mjs +136 -0
- package/scripts/generate-ontology-data.mjs +3 -3
- package/scripts/generate-ontology-schema.mjs +3 -3
- package/scripts/lib/agents-turtle.mjs +64 -0
- package/scripts/lib/bibliography-turtle.mjs +54 -0
- package/scripts/lib/build-activity-turtle.mjs +92 -0
- package/scripts/lib/build-cache.mjs +70 -0
- package/scripts/lib/dataset-turtle.mjs +79 -0
- package/scripts/lib/turtle-escape.mjs +0 -0
- package/scripts/lib/version-turtle.mjs +56 -0
- package/scripts/lib/vocab-turtle.mjs +64 -0
- package/scripts/normalize-yaml.mjs +99 -0
- package/scripts/sync-concept-model.mjs +86 -0
- package/scripts/validate-shacl.mjs +194 -0
- package/src/__fixtures__/concept-shape.ttl +20 -0
- package/src/__fixtures__/shacl/bad/concept.ttl +7 -0
- package/src/__fixtures__/shacl/empty/.gitkeep +0 -0
- package/src/__fixtures__/shacl/good/concept.ttl +8 -0
- package/src/__tests__/__fixtures__/concepts.ts +221 -0
- package/src/__tests__/adapters/concept-identity.test.ts +76 -0
- package/src/__tests__/components/error-boundary.test.ts +109 -0
- package/src/__tests__/composables/use-dataset-series.test.ts +262 -0
- package/src/__tests__/concept-rdf/agents-emitter.test.ts +110 -0
- package/src/__tests__/concept-rdf/bibliography-emitter.test.ts +159 -0
- package/src/__tests__/concept-rdf/build-activity-emitter.test.ts +119 -0
- package/src/__tests__/concept-rdf/coerce-date.test.ts +97 -0
- package/src/__tests__/concept-rdf/concept-emitter.test.ts +258 -0
- package/src/__tests__/concept-rdf/dataset-emitter.test.ts +224 -0
- package/src/__tests__/concept-rdf/differential.test.ts +96 -0
- package/src/__tests__/concept-rdf/group-emitter.test.ts +109 -0
- package/src/__tests__/concept-rdf/image-variant-emitter.test.ts +135 -0
- package/src/__tests__/concept-rdf/jsonld-writer.test.ts +109 -0
- package/src/__tests__/concept-rdf/nonverbal-rep.test.ts +177 -0
- package/src/__tests__/concept-rdf/property-based.test.ts +179 -0
- package/src/__tests__/concept-rdf/provenance.test.ts +110 -0
- package/src/__tests__/concept-rdf/quad-isomorphism.test.ts +43 -0
- package/src/__tests__/concept-rdf/quad-isomorphism.ts +47 -0
- package/src/__tests__/concept-rdf/rdf-components.test.ts +145 -0
- package/src/__tests__/concept-rdf/rdf-graph.test.ts +115 -0
- package/src/__tests__/concept-rdf/round-trip.test.ts +243 -0
- package/src/__tests__/concept-rdf/scoped-examples.test.ts +126 -0
- package/src/__tests__/concept-rdf/sections-builder.test.ts +94 -0
- package/src/__tests__/concept-rdf/shacl-conformance.test.ts +110 -0
- package/src/__tests__/concept-rdf/shape-consistency.test.ts +138 -0
- package/src/__tests__/concept-rdf/snapshot-generation.test.ts +75 -0
- package/src/__tests__/concept-rdf/table-formula-emitter.test.ts +142 -0
- package/src/__tests__/concept-rdf/turtle-writer.test.ts +114 -0
- package/src/__tests__/concept-rdf/use-rdf-document.test.ts +246 -0
- package/src/__tests__/concept-rdf/version-emitter.test.ts +120 -0
- package/src/__tests__/concept-rdf/vocabulary-ssot.test.ts +100 -0
- package/src/__tests__/concept-rdf-view.test.ts +136 -0
- package/src/__tests__/dataset-style.test.ts +12 -7
- package/src/__tests__/errors/errors.test.ts +142 -0
- package/src/__tests__/format-downloads.test.ts +47 -65
- package/src/__tests__/markdown-lite.test.ts +19 -0
- package/src/__tests__/perf/bundle-layout.test.ts +50 -0
- package/src/__tests__/perf/serialization-perf.test.ts +121 -0
- package/src/__tests__/scripts/agents-turtle.test.ts +61 -0
- package/src/__tests__/scripts/bibliography-turtle.test.ts +59 -0
- package/src/__tests__/scripts/build-activity-turtle.test.ts +75 -0
- package/src/__tests__/scripts/build-cache.test.ts +78 -0
- package/src/__tests__/scripts/build-integration.test.ts +134 -0
- package/src/__tests__/scripts/dataset-turtle.test.ts +94 -0
- package/src/__tests__/scripts/normalize-yaml.test.ts +98 -0
- package/src/__tests__/scripts/stryker-config.test.ts +33 -0
- package/src/__tests__/scripts/turtle-escape.test.ts +63 -0
- package/src/__tests__/scripts/version-turtle.test.ts +72 -0
- package/src/__tests__/scripts/vocab-turtle.test.ts +63 -0
- package/src/__tests__/use-format-registry.test.ts +125 -0
- package/src/__tests__/utils/bcp47.test.ts +166 -0
- package/src/__tests__/utils/color-theme.test.ts +143 -0
- package/src/__tests__/utils/url-safety.test.ts +65 -0
- package/src/__tests__/validate-shacl.test.ts +100 -0
- package/src/adapters/DatasetAdapter.ts +11 -5
- package/src/adapters/GraphDataSource.ts +2 -1
- package/src/adapters/UriRouter.ts +2 -1
- package/src/adapters/concept-identity.ts +69 -0
- package/src/adapters/factory.ts +3 -2
- package/src/adapters/model-bridge.ts +2 -1
- package/src/adapters/non-verbal/glossarist-augment.d.ts +7 -0
- package/src/adapters/non-verbal-resolver.ts +2 -1
- package/src/components/AppSidebar.vue +189 -93
- package/src/components/ConceptDetail.vue +8 -0
- package/src/components/ConceptEditionRail.vue +222 -0
- package/src/components/ConceptRdfView.vue +37 -377
- package/src/components/DatasetSeriesCard.vue +270 -0
- package/src/components/ErrorBoundary.vue +95 -0
- package/src/components/FormatDownloads.vue +17 -13
- package/src/components/HomeSeriesSection.vue +277 -0
- package/src/components/RelationSphere.vue +1672 -0
- package/src/components/SidebarSeriesSection.vue +239 -0
- package/src/components/concept-rdf/RdfInstanceHeader.vue +47 -0
- package/src/components/concept-rdf/RdfInstanceSection.vue +54 -0
- package/src/components/concept-rdf/RdfPrefixLegend.vue +27 -0
- package/src/components/concept-rdf/RdfSourcePanel.vue +72 -0
- package/src/components/concept-rdf/agents-emitter.ts +82 -0
- package/src/components/concept-rdf/bibliography-emitter.ts +83 -0
- package/src/components/concept-rdf/build-activity-emitter.ts +89 -0
- package/src/components/concept-rdf/concept-emitter.ts +443 -0
- package/src/components/concept-rdf/dataset-emitter.ts +95 -0
- package/src/components/concept-rdf/group-emitter.ts +69 -0
- package/src/components/concept-rdf/image-variant-emitter.ts +46 -0
- package/src/components/concept-rdf/jsonld-writer.ts +82 -0
- package/src/components/concept-rdf/predicates.ts +261 -0
- package/src/components/concept-rdf/provenance.ts +80 -0
- package/src/components/concept-rdf/rdf-graph.ts +211 -0
- package/src/components/concept-rdf/rdf-prefixes.ts +23 -0
- package/src/components/concept-rdf/sections-builder.ts +62 -0
- package/src/components/concept-rdf/table-formula-emitter.ts +101 -0
- package/src/components/concept-rdf/turtle-writer.ts +116 -0
- package/src/components/concept-rdf/use-rdf-document.ts +72 -0
- package/src/components/concept-rdf/version-emitter.ts +65 -0
- package/src/components/concept-rdf/vocabulary-emitter.ts +62 -0
- package/src/composables/use-color-theme.ts +82 -0
- package/src/composables/use-format-registry.ts +42 -0
- package/src/composables/useDatasetSeries.ts +258 -0
- package/src/composables/useSphereProjection.ts +125 -0
- package/src/config/group-types.ts +92 -0
- package/src/config/types.ts +81 -2
- package/src/config/use-site-config.ts +2 -1
- package/src/errors.ts +136 -0
- package/src/i18n/locales/eng.yml +24 -0
- package/src/i18n/locales/fra.yml +24 -0
- package/src/stores/vocabulary.ts +3 -1
- package/src/style.css +17 -2
- package/src/types/agents-version-turtle.d.ts +27 -0
- package/src/types/bibliography-turtle.d.ts +12 -0
- package/src/types/build-activity-turtle.d.ts +16 -0
- package/src/types/build-cache.d.ts +20 -0
- package/src/types/dataset-turtle.d.ts +32 -0
- package/src/types/normalize-yaml.d.ts +16 -0
- package/src/types/turtle-escape.d.ts +6 -0
- package/src/types/vocab-turtle.d.ts +13 -0
- package/src/utils/asciidoc-lite.ts +11 -6
- package/src/utils/bcp47.ts +141 -0
- package/src/utils/color-theme-integration.ts +11 -0
- package/src/utils/color-theme.ts +129 -0
- package/src/utils/dataset-style.ts +31 -6
- package/src/utils/locale.ts +6 -14
- package/src/utils/markdown-lite.ts +6 -1
- package/src/utils/relation-sphere-styling.ts +63 -0
- package/src/utils/relationship-categories.ts +30 -0
- package/src/utils/url-safety.ts +30 -0
- package/src/views/ConceptView.vue +183 -9
- package/src/views/DatasetView.vue +6 -0
- package/src/views/HomeView.vue +5 -0
- package/vite.config.ts +7 -0
package/src/errors.ts
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
export interface ErrorContext {
|
|
2
|
+
readonly conceptId?: string;
|
|
3
|
+
readonly registerId?: string;
|
|
4
|
+
readonly locale?: string;
|
|
5
|
+
readonly predicate?: string;
|
|
6
|
+
readonly sourcePath?: string;
|
|
7
|
+
readonly cause?: unknown;
|
|
8
|
+
readonly [key: string]: unknown;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class GlossaristError extends Error {
|
|
12
|
+
constructor(
|
|
13
|
+
message: string,
|
|
14
|
+
public readonly context: ErrorContext = {},
|
|
15
|
+
) {
|
|
16
|
+
super(message);
|
|
17
|
+
this.name = this.constructor.name;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
hint(): string {
|
|
21
|
+
return '';
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export class ConfigurationError extends GlossaristError {}
|
|
26
|
+
export class DataSourceError extends GlossaristError {}
|
|
27
|
+
export class SerializationError extends GlossaristError {}
|
|
28
|
+
export class ValidationError extends GlossaristError {}
|
|
29
|
+
|
|
30
|
+
export class UnknownDatasetError extends ConfigurationError {
|
|
31
|
+
static make(registerId: string): UnknownDatasetError {
|
|
32
|
+
return new UnknownDatasetError(`Unknown dataset: ${registerId}`, { registerId });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
hint(): string {
|
|
36
|
+
return 'Check datasets.yml / datasets.json registration and adapter discovery.';
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export class DatasetRegistryLoadError extends DataSourceError {
|
|
41
|
+
static make(status: number, url?: string): DatasetRegistryLoadError {
|
|
42
|
+
return new DatasetRegistryLoadError(
|
|
43
|
+
`Failed to load dataset registry: HTTP ${status}`,
|
|
44
|
+
{ status, url },
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export class ManifestLoadError extends DataSourceError {
|
|
50
|
+
static make(registerId: string, status: number): ManifestLoadError {
|
|
51
|
+
return new ManifestLoadError(
|
|
52
|
+
`Failed to load manifest for ${registerId}: HTTP ${status}`,
|
|
53
|
+
{ registerId, status },
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export class IndexLoadError extends DataSourceError {
|
|
59
|
+
static make(registerId: string, status: number): IndexLoadError {
|
|
60
|
+
return new IndexLoadError(
|
|
61
|
+
`Failed to load index for ${registerId}: HTTP ${status}`,
|
|
62
|
+
{ registerId, status },
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export class ChunkLoadError extends DataSourceError {
|
|
68
|
+
static make(registerId: string, chunkIndex: number, status: number): ChunkLoadError {
|
|
69
|
+
return new ChunkLoadError(
|
|
70
|
+
`Failed to load chunk ${chunkIndex} for ${registerId}: HTTP ${status}`,
|
|
71
|
+
{ registerId, chunkIndex, status },
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export class ConceptNotFoundError extends DataSourceError {
|
|
77
|
+
static make(registerId: string, conceptId: string): ConceptNotFoundError {
|
|
78
|
+
return new ConceptNotFoundError(
|
|
79
|
+
`Concept ${conceptId} not found in ${registerId}`,
|
|
80
|
+
{ registerId, conceptId },
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export class NonVerbalEntityNotFoundError extends DataSourceError {
|
|
86
|
+
static make(datasetId: string, kind: string, entityId: string, status: number): NonVerbalEntityNotFoundError {
|
|
87
|
+
return new NonVerbalEntityNotFoundError(
|
|
88
|
+
`Failed to load ${kind} ${entityId} from ${datasetId}: HTTP ${status}`,
|
|
89
|
+
{ datasetId, kind, entityId, status },
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export class InvalidConceptIdentityError extends SerializationError {
|
|
95
|
+
hint(): string {
|
|
96
|
+
return 'ConceptIdentity requires non-empty localId, registerId, and uriBase.';
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export class InvalidConceptUriError extends SerializationError {
|
|
101
|
+
static make(uri: string): InvalidConceptUriError {
|
|
102
|
+
return new InvalidConceptUriError(`Not a concept URI: ${uri}`, { uri });
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
hint(): string {
|
|
106
|
+
return 'Expected format: <uriBase>/<registerId>/concept/<conceptId>';
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export class InvalidLangTagError extends SerializationError {
|
|
111
|
+
hint(): string {
|
|
112
|
+
return 'Use BCP-47 form: primary[-script][-region][-variant]*[-x-private]. ' +
|
|
113
|
+
'ISO 639-3 codes (eng, fra) are normalized to ISO 639-1 (en, fr).';
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function isGlossaristError(err: unknown): err is GlossaristError {
|
|
118
|
+
return err instanceof GlossaristError;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function formatError(err: GlossaristError): string {
|
|
122
|
+
const lines = [`${err.name}: ${err.message}`];
|
|
123
|
+
const fields: Record<string, unknown> = {};
|
|
124
|
+
for (const [k, v] of Object.entries(err.context)) {
|
|
125
|
+
if (k === 'cause' || v === undefined || v === null) continue;
|
|
126
|
+
fields[k] = v;
|
|
127
|
+
}
|
|
128
|
+
if (Object.keys(fields).length) {
|
|
129
|
+
for (const [k, v] of Object.entries(fields)) {
|
|
130
|
+
lines.push(` ${k}: ${typeof v === 'object' ? JSON.stringify(v) : v}`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
const h = err.hint();
|
|
134
|
+
if (h) lines.push(` hint: ${h}`);
|
|
135
|
+
return lines.join('\n');
|
|
136
|
+
}
|
package/src/i18n/locales/eng.yml
CHANGED
|
@@ -97,6 +97,12 @@ concept.failedToLoad: Failed to load concept
|
|
|
97
97
|
concept.notFound: Concept not found
|
|
98
98
|
concept.notFoundMsg: "The concept \"{id}\" does not exist in this dataset."
|
|
99
99
|
concept.backToDataset: Back to dataset
|
|
100
|
+
concept.editionSeries: Edition series
|
|
101
|
+
concept.viewing: viewing
|
|
102
|
+
concept.currentEdition: current edition
|
|
103
|
+
edge.supersedes: supersedes
|
|
104
|
+
edge.superseded_by: superseded by
|
|
105
|
+
concept.noChain: No supersession chain recorded for this concept.
|
|
100
106
|
|
|
101
107
|
# Sidebar
|
|
102
108
|
sidebar.overview: Overview
|
|
@@ -185,3 +191,21 @@ dataset.alphabetical: Alphabetical
|
|
|
185
191
|
dataset.sectionFilter: Section
|
|
186
192
|
dataset.clearSection: Clear section
|
|
187
193
|
dataset.conceptsInSection: concepts in section
|
|
194
|
+
|
|
195
|
+
# Relation sphere view
|
|
196
|
+
sphere.viewOptions: View options
|
|
197
|
+
sphere.degree: Degree
|
|
198
|
+
sphere.language: Language
|
|
199
|
+
sphere.datasets: Datasets
|
|
200
|
+
sphere.relationTypes: Relation types
|
|
201
|
+
sphere.expand: Expand
|
|
202
|
+
sphere.tight: tight
|
|
203
|
+
sphere.loose: loose
|
|
204
|
+
sphere.redraw: Redraw
|
|
205
|
+
sphere.focus: FOCUS
|
|
206
|
+
concept.detailView: Detail
|
|
207
|
+
concept.relationSphere: Relation Sphere
|
|
208
|
+
concept.permalink: Permalink
|
|
209
|
+
concept.copied: Copied
|
|
210
|
+
|
|
211
|
+
# Relation types (displayed in legend + edge labels)
|
package/src/i18n/locales/fra.yml
CHANGED
|
@@ -97,6 +97,12 @@ concept.failedToLoad: Échec du chargement du concept
|
|
|
97
97
|
concept.notFound: Concept non trouvé
|
|
98
98
|
concept.notFoundMsg: "Le concept « {id} » n'existe pas dans ce jeu de données."
|
|
99
99
|
concept.backToDataset: Retour au jeu de données
|
|
100
|
+
concept.editionSeries: Série d'éditions
|
|
101
|
+
concept.viewing: affiché
|
|
102
|
+
concept.currentEdition: édition courante
|
|
103
|
+
edge.supersedes: remplace
|
|
104
|
+
edge.superseded_by: remplacé par
|
|
105
|
+
concept.noChain: Aucune chaîne de succession enregistrée pour ce concept.
|
|
100
106
|
|
|
101
107
|
# Sidebar
|
|
102
108
|
sidebar.overview: Vue d'ensemble
|
|
@@ -185,3 +191,21 @@ dataset.alphabetical: Alphabétique
|
|
|
185
191
|
dataset.sectionFilter: Section
|
|
186
192
|
dataset.clearSection: Effacer la section
|
|
187
193
|
dataset.conceptsInSection: concepts dans la section
|
|
194
|
+
|
|
195
|
+
# Relation sphere view
|
|
196
|
+
sphere.viewOptions: Options d'affichage
|
|
197
|
+
sphere.degree: Degré
|
|
198
|
+
sphere.language: Langue
|
|
199
|
+
sphere.datasets: Jeux de données
|
|
200
|
+
sphere.relationTypes: Types de relation
|
|
201
|
+
sphere.expand: Expansion
|
|
202
|
+
sphere.tight: serré
|
|
203
|
+
sphere.loose: étendu
|
|
204
|
+
sphere.redraw: Redessiner
|
|
205
|
+
sphere.focus: FOCUS
|
|
206
|
+
concept.detailView: Détail
|
|
207
|
+
concept.relationSphere: Sphère de relations
|
|
208
|
+
concept.permalink: Permalien
|
|
209
|
+
concept.copied: Copié
|
|
210
|
+
|
|
211
|
+
# Relation types (affichés dans la légende + étiquettes d'arêtes)
|
package/src/stores/vocabulary.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { conceptUri } from '../adapters/model-bridge';
|
|
|
8
8
|
import { GraphEngine } from '../graph';
|
|
9
9
|
import { UriRouter } from '../adapters/UriRouter';
|
|
10
10
|
import { deduplicateSearchHits } from '../utils/search';
|
|
11
|
+
import { UnknownDatasetError } from '../errors';
|
|
11
12
|
|
|
12
13
|
export const useVocabularyStore = defineStore('vocabulary', () => {
|
|
13
14
|
// State
|
|
@@ -193,7 +194,7 @@ export const useVocabularyStore = defineStore('vocabulary', () => {
|
|
|
193
194
|
|
|
194
195
|
try {
|
|
195
196
|
const adapter = datasets.value.get(registerId);
|
|
196
|
-
if (!adapter) throw
|
|
197
|
+
if (!adapter) throw UnknownDatasetError.make(registerId);
|
|
197
198
|
|
|
198
199
|
// Fetch concept and cross-dataset edges in parallel
|
|
199
200
|
const [concept] = await Promise.all([
|
|
@@ -332,6 +333,7 @@ export const useVocabularyStore = defineStore('vocabulary', () => {
|
|
|
332
333
|
datasetList,
|
|
333
334
|
discoverDatasets,
|
|
334
335
|
loadDataset,
|
|
336
|
+
ensureEdgesForDataset,
|
|
335
337
|
viewConcept,
|
|
336
338
|
navigateToUri,
|
|
337
339
|
searchAcrossDatasets,
|
package/src/style.css
CHANGED
|
@@ -482,10 +482,25 @@
|
|
|
482
482
|
|
|
483
483
|
/* ── Sidebar group labels (brand colors are too dark in dark mode) ── */
|
|
484
484
|
.dark .sidebar-group-label {
|
|
485
|
-
filter: brightness(
|
|
485
|
+
filter: brightness(1.6);
|
|
486
486
|
}
|
|
487
487
|
.dark .sidebar-group-label:hover {
|
|
488
|
-
filter: brightness(1.
|
|
488
|
+
filter: brightness(1.4);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/* ── Gold accent that adapts to theme ── */
|
|
492
|
+
:root {
|
|
493
|
+
--gold-accent: #B8935A;
|
|
494
|
+
--gold-accent-deep: #8C6A3A;
|
|
495
|
+
}
|
|
496
|
+
.dark {
|
|
497
|
+
--gold-accent: #D4AF6E;
|
|
498
|
+
--gold-accent-deep: #B8935A;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/* Section labels brighter in dark mode */
|
|
502
|
+
.dark .section-label {
|
|
503
|
+
color: theme('colors.ink.300') !important;
|
|
489
504
|
}
|
|
490
505
|
|
|
491
506
|
/* Scrollbar hide utility */
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
declare module '*/scripts/lib/agents-turtle.mjs' {
|
|
2
|
+
export function buildAgentsTurtle(
|
|
3
|
+
contributors: readonly { name: string; role?: string; organization?: string; url?: string; email?: string }[],
|
|
4
|
+
agentBase?: string,
|
|
5
|
+
): string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
declare module '*/scripts/lib/version-turtle.mjs' {
|
|
9
|
+
export interface VersionInput {
|
|
10
|
+
readonly registerId: string;
|
|
11
|
+
readonly version: string;
|
|
12
|
+
readonly versionIri: string;
|
|
13
|
+
readonly datasetIri: string;
|
|
14
|
+
readonly generatedAt: string;
|
|
15
|
+
readonly previousVersionIri?: string;
|
|
16
|
+
readonly changeSummary?: string;
|
|
17
|
+
readonly associatedAgentIri?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface VersionHistoryInput {
|
|
20
|
+
readonly registerId: string;
|
|
21
|
+
readonly datasetIri: string;
|
|
22
|
+
readonly versions: readonly { version: string; generatedAt: string; changeSummary?: string }[];
|
|
23
|
+
readonly associatedAgentIri?: string;
|
|
24
|
+
}
|
|
25
|
+
export function buildVersionTurtle(input: VersionInput): string;
|
|
26
|
+
export function buildVersionHistoryTurtle(input: VersionHistoryInput): string;
|
|
27
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
declare module '*/scripts/lib/bibliography-turtle.mjs' {
|
|
2
|
+
export interface BibliographyEntryJson {
|
|
3
|
+
readonly reference?: string;
|
|
4
|
+
readonly title?: string;
|
|
5
|
+
readonly link?: string;
|
|
6
|
+
}
|
|
7
|
+
export function buildBibliographyTurtle(
|
|
8
|
+
register: string,
|
|
9
|
+
bibliographyJson: Record<string, BibliographyEntryJson>,
|
|
10
|
+
baseUri?: string,
|
|
11
|
+
): string;
|
|
12
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
declare module '*/scripts/lib/build-activity-turtle.mjs' {
|
|
2
|
+
export interface BuildActivityTurtleInput {
|
|
3
|
+
readonly runId: string;
|
|
4
|
+
readonly startedAt: string;
|
|
5
|
+
readonly endedAt: string;
|
|
6
|
+
readonly gitSha?: string | null;
|
|
7
|
+
readonly gitBranch?: string | null;
|
|
8
|
+
readonly toolId: string;
|
|
9
|
+
readonly toolVersion: string;
|
|
10
|
+
readonly datasetRegisters: readonly string[];
|
|
11
|
+
readonly conceptCount: number;
|
|
12
|
+
readonly associatedAgentIri?: string | null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function buildActivityTurtle(input: BuildActivityTurtleInput): string;
|
|
16
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
declare module '*/scripts/lib/build-cache.mjs' {
|
|
2
|
+
export interface BuildCacheEntry<T = unknown> {
|
|
3
|
+
hash: string;
|
|
4
|
+
value: T;
|
|
5
|
+
storedAt: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export class BuildCache {
|
|
9
|
+
constructor(cacheDir: string);
|
|
10
|
+
get<T = unknown>(key: string): Promise<BuildCacheEntry<T> | null>;
|
|
11
|
+
set<T = unknown>(key: string, value: T, hash: string): Promise<void>;
|
|
12
|
+
readThrough<T>(
|
|
13
|
+
key: string,
|
|
14
|
+
hash: string,
|
|
15
|
+
producer: () => Promise<T>,
|
|
16
|
+
): Promise<{ value: T; hit: boolean }>;
|
|
17
|
+
pathFor(key: string): string;
|
|
18
|
+
static hash(input: string): string;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
declare module '*/scripts/lib/dataset-turtle.mjs' {
|
|
2
|
+
export interface DatasetDistribution {
|
|
3
|
+
readonly id: string;
|
|
4
|
+
readonly title: string;
|
|
5
|
+
readonly mediaType: string;
|
|
6
|
+
readonly downloadUrl: string;
|
|
7
|
+
readonly byteSize?: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface DatasetSection {
|
|
11
|
+
readonly collectionIri: string;
|
|
12
|
+
readonly title: string;
|
|
13
|
+
readonly memberUris: readonly string[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface DatasetTurtleInput {
|
|
17
|
+
readonly datasetIri: string;
|
|
18
|
+
readonly registerId: string;
|
|
19
|
+
readonly title: string;
|
|
20
|
+
readonly description?: string;
|
|
21
|
+
readonly modified: string;
|
|
22
|
+
readonly languages: readonly string[];
|
|
23
|
+
readonly distributions: readonly DatasetDistribution[];
|
|
24
|
+
readonly topConceptUris: readonly string[];
|
|
25
|
+
readonly sections: readonly DatasetSection[];
|
|
26
|
+
readonly sourceRepoUrl?: string;
|
|
27
|
+
readonly publisherIri?: string;
|
|
28
|
+
readonly contactIri?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function buildDatasetTurtle(input: DatasetTurtleInput): string;
|
|
32
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
declare module '*/scripts/normalize-yaml.mjs' {
|
|
2
|
+
export interface NormalizeResult {
|
|
3
|
+
checked: number;
|
|
4
|
+
nonNfc: number;
|
|
5
|
+
fixed: string[];
|
|
6
|
+
check: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface NormalizeOptions {
|
|
10
|
+
root?: string;
|
|
11
|
+
check?: boolean;
|
|
12
|
+
paths?: string[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function normalizeYaml(options?: NormalizeOptions): NormalizeResult;
|
|
16
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
declare module '*/scripts/lib/turtle-escape.mjs' {
|
|
2
|
+
export function ttlLit(s: unknown): string;
|
|
3
|
+
export function ttlPrefixed(qname: string): string;
|
|
4
|
+
export function ttlIri(iri: string): string;
|
|
5
|
+
export function assertValidIri(iri: unknown, context?: string): string;
|
|
6
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
declare module '*/scripts/lib/vocab-turtle.mjs' {
|
|
2
|
+
export interface VocabTerm {
|
|
3
|
+
readonly iri: string;
|
|
4
|
+
readonly label: string;
|
|
5
|
+
}
|
|
6
|
+
export interface VocabScheme {
|
|
7
|
+
readonly schemeIri: string;
|
|
8
|
+
readonly label: string;
|
|
9
|
+
readonly terms: readonly VocabTerm[];
|
|
10
|
+
}
|
|
11
|
+
export function buildVocabularyTurtle(): string;
|
|
12
|
+
export function listVocabSchemes(): readonly VocabScheme[];
|
|
13
|
+
}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { escapeHtml, escapeAttr } from './escape';
|
|
7
|
+
import { sanitizeUrl } from './url-safety';
|
|
7
8
|
|
|
8
9
|
export function renderAsciiDocLite(text: string): string {
|
|
9
10
|
if (!text) return '';
|
|
@@ -102,14 +103,18 @@ export function renderAsciiDocLite(text: string): string {
|
|
|
102
103
|
|
|
103
104
|
function inlineFormat(text: string): string {
|
|
104
105
|
// AsciiDoc link: https://example.com[text]
|
|
105
|
-
text = text.replace(/(https?:\/\/[^\s\[]+)\[([^\]]*)\]/g, (_, url, label) =>
|
|
106
|
-
|
|
107
|
-
|
|
106
|
+
text = text.replace(/(https?:\/\/[^\s\[]+)\[([^\]]*)\]/g, (_, url, label) => {
|
|
107
|
+
const href = sanitizeUrl(url);
|
|
108
|
+
if (!href) return escapeHtml(label || url);
|
|
109
|
+
return `<a href="${escapeHtml(href)}" target="_blank" rel="noopener">${escapeHtml(label || url)}</a>`;
|
|
110
|
+
});
|
|
108
111
|
|
|
109
112
|
// Bare URLs
|
|
110
|
-
text = text.replace(/(?<!href="|">)(https?:\/\/[^\s<]+)/g, url =>
|
|
111
|
-
|
|
112
|
-
|
|
113
|
+
text = text.replace(/(?<!href="|">)(https?:\/\/[^\s<]+)/g, url => {
|
|
114
|
+
const href = sanitizeUrl(url);
|
|
115
|
+
if (!href) return escapeHtml(url);
|
|
116
|
+
return `<a href="${escapeHtml(href)}" target="_blank" rel="noopener">${escapeHtml(url)}</a>`;
|
|
117
|
+
});
|
|
113
118
|
|
|
114
119
|
// Monospace: `text`
|
|
115
120
|
text = text.replace(/`([^`]+)`/g, '<code>$1</code>');
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { InvalidLangTagError } from '../errors';
|
|
2
|
+
|
|
3
|
+
export interface LangTag {
|
|
4
|
+
readonly primary: string;
|
|
5
|
+
readonly script?: string;
|
|
6
|
+
readonly region?: string;
|
|
7
|
+
readonly variants?: string[];
|
|
8
|
+
readonly privateUse?: string[];
|
|
9
|
+
readonly raw: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const ISO_639_3_TO_1: Readonly<Record<string, string>> = {
|
|
13
|
+
eng: 'en', fra: 'fr', deu: 'de', zho: 'zh', ara: 'ar', jpn: 'ja', rus: 'ru',
|
|
14
|
+
kor: 'ko', spa: 'es', ita: 'it', por: 'pt', nld: 'nl', swe: 'sv', fin: 'fi',
|
|
15
|
+
dan: 'da', nob: 'nb', nno: 'nn', nor: 'no', pol: 'pl', tur: 'tr', ces: 'cs', ell: 'el',
|
|
16
|
+
heb: 'he', hin: 'hi', ind: 'id', fas: 'fa', ukr: 'uk', hun: 'hu', ron: 'ro',
|
|
17
|
+
slk: 'sk', slv: 'sl', hrv: 'hr', srp: 'sr', bul: 'bg', msa: 'ms', tha: 'th',
|
|
18
|
+
vie: 'vi', urd: 'ur', ben: 'bn', tam: 'ta', tel: 'te', mar: 'mr', guj: 'gu',
|
|
19
|
+
pan: 'pa', mal: 'ml', kan: 'kn', ori: 'or', asm: 'as', sin: 'si', nep: 'ne',
|
|
20
|
+
lit: 'lt', lav: 'lv', est: 'et', gle: 'ga', cym: 'cy', eus: 'eu', cat: 'ca',
|
|
21
|
+
glg: 'gl', afr: 'af', sqi: 'sq', mkd: 'mk', bel: 'be', kaz: 'kk', uzb: 'uz',
|
|
22
|
+
aze: 'az', hye: 'hy', kat: 'ka', mon: 'mn', tuk: 'tk', uig: 'ug', tgl: 'tl',
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const ISO_639_1_TO_3: Readonly<Record<string, string>> = (() => {
|
|
26
|
+
const out: Record<string, string> = {};
|
|
27
|
+
for (const [k, v] of Object.entries(ISO_639_3_TO_1)) out[v] = k;
|
|
28
|
+
return Object.freeze(out);
|
|
29
|
+
})();
|
|
30
|
+
|
|
31
|
+
const DEFAULT_SCRIPTS: Readonly<Record<string, string>> = {
|
|
32
|
+
zh: 'Hans',
|
|
33
|
+
sr: 'Cyrl',
|
|
34
|
+
uz: 'Latn',
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const SCRIPT_RE = /^[A-Z][a-z]{3}$/;
|
|
38
|
+
const ISO_ALPHA2_REGION_RE = /^[A-Z]{2}$/;
|
|
39
|
+
const UN_M49_REGION_RE = /^\d{3}$/;
|
|
40
|
+
|
|
41
|
+
export function mapIso6393To6391(code: string): string | null {
|
|
42
|
+
return ISO_639_3_TO_1[code] ?? null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function mapIso6391To6393(code: string): string | null {
|
|
46
|
+
return ISO_639_1_TO_3[code] ?? null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function parseLangTag(input: string): LangTag {
|
|
50
|
+
if (!input || typeof input !== 'string') {
|
|
51
|
+
throw new InvalidLangTagError(`Invalid language tag: ${String(input)}`, { input });
|
|
52
|
+
}
|
|
53
|
+
const trimmed = input.trim();
|
|
54
|
+
if (!trimmed) {
|
|
55
|
+
throw new InvalidLangTagError(`Empty language tag`, { input });
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const segments = trimmed.split('-');
|
|
59
|
+
const first = segments[0]!;
|
|
60
|
+
if (!/^[a-zA-Z]{2,3}$/.test(first)) {
|
|
61
|
+
throw new InvalidLangTagError(
|
|
62
|
+
`Invalid primary subtag: ${first}`,
|
|
63
|
+
{ input, subtag: first },
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const primary = (ISO_639_3_TO_1[first.toLowerCase()] ?? first.toLowerCase());
|
|
68
|
+
const tag: LangTag = { primary, raw: input };
|
|
69
|
+
|
|
70
|
+
let i = 1;
|
|
71
|
+
let privateUse: string[] | undefined;
|
|
72
|
+
if (segments[i] === 'x') {
|
|
73
|
+
privateUse = segments.slice(i + 1);
|
|
74
|
+
i = segments.length;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
let script: string | undefined;
|
|
78
|
+
let region: string | undefined;
|
|
79
|
+
let variants: string[] | undefined;
|
|
80
|
+
|
|
81
|
+
for (; i < segments.length; i++) {
|
|
82
|
+
const seg = segments[i]!;
|
|
83
|
+
if (seg === 'x') {
|
|
84
|
+
privateUse = segments.slice(i + 1);
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
if (SCRIPT_RE.test(seg) && !script) {
|
|
88
|
+
script = seg;
|
|
89
|
+
} else if ((ISO_ALPHA2_REGION_RE.test(seg) || UN_M49_REGION_RE.test(seg)) && !region) {
|
|
90
|
+
region = seg;
|
|
91
|
+
} else if (/^[a-z0-9]{4,8}$/i.test(seg)) {
|
|
92
|
+
variants = variants ?? [];
|
|
93
|
+
variants.push(seg);
|
|
94
|
+
} else {
|
|
95
|
+
throw new InvalidLangTagError(
|
|
96
|
+
`Unrecognized language subtag: ${seg}`,
|
|
97
|
+
{ input, subtag: seg },
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const finalTag: LangTag = {
|
|
103
|
+
primary,
|
|
104
|
+
script: script ?? DEFAULT_SCRIPTS[primary],
|
|
105
|
+
region,
|
|
106
|
+
variants,
|
|
107
|
+
privateUse,
|
|
108
|
+
raw: input,
|
|
109
|
+
};
|
|
110
|
+
return finalTag;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function formatLangTag(tag: LangTag): string {
|
|
114
|
+
const parts: string[] = [tag.primary];
|
|
115
|
+
if (tag.script) parts.push(tag.script);
|
|
116
|
+
if (tag.region) parts.push(tag.region);
|
|
117
|
+
if (tag.variants?.length) parts.push(...tag.variants);
|
|
118
|
+
if (tag.privateUse?.length) parts.push('x', ...tag.privateUse);
|
|
119
|
+
return parts.join('-');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function canonicalLangTag(input: string): string {
|
|
123
|
+
return formatLangTag(parseLangTag(input));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function isValidLangTag(input: string): boolean {
|
|
127
|
+
try {
|
|
128
|
+
parseLangTag(input);
|
|
129
|
+
return true;
|
|
130
|
+
} catch {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function isNfc(text: string): boolean {
|
|
136
|
+
return text.normalize('NFC') === text;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function toNfc(text: string): string {
|
|
140
|
+
return text.normalize('NFC');
|
|
141
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Color-theme integration bridge for the relationship-categories module.
|
|
3
|
+
* Re-exports the color pair accessors with a stable interface so consumers
|
|
4
|
+
* don't need to import from two places.
|
|
5
|
+
*/
|
|
6
|
+
export {
|
|
7
|
+
colorPairForType,
|
|
8
|
+
colorPairForCategory,
|
|
9
|
+
colorThemeForOverrides,
|
|
10
|
+
} from './relationship-categories';
|
|
11
|
+
export type { ColorPair } from './color-theme';
|