@glossarist/concept-browser 0.7.51 → 0.7.53
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/App.vue +2 -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__/config/group-renderers.test.ts +35 -0
- package/src/__tests__/config/group-types.test.ts +76 -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/components/groups/DatasetGroupRenderer.vue +32 -0
- package/src/components/groups/DefaultGroupSidebar.vue +50 -0
- package/src/components/groups/LineageGroupSidebar.vue +75 -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-renderers.ts +27 -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 +187 -9
- package/src/views/DatasetView.vue +6 -0
- package/src/views/HomeView.vue +5 -0
- package/vite.config.ts +7 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { execFileSync } from 'node:child_process';
|
|
3
|
+
import { join, dirname } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
|
|
6
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const SCRIPT = join(__dirname, '..', '..', 'scripts', 'validate-shacl.mjs');
|
|
8
|
+
const FIXTURES = join(__dirname, '..', '__fixtures__', 'shacl');
|
|
9
|
+
const SHAPES = join(__dirname, '..', '__fixtures__', 'concept-shape.ttl');
|
|
10
|
+
|
|
11
|
+
interface RunResult { code: number; stdout: string; stderr: string }
|
|
12
|
+
interface RunOptions { env?: NodeJS.ProcessEnv; shapes?: string }
|
|
13
|
+
interface ExecException extends Error {
|
|
14
|
+
status?: number;
|
|
15
|
+
stdout?: Buffer | string;
|
|
16
|
+
stderr?: Buffer | string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function execCaught(cmd: string, args: string[], opts: Parameters<typeof execFileSync>[2] = {}): RunResult {
|
|
20
|
+
try {
|
|
21
|
+
const stdout = execFileSync(cmd, args, { encoding: 'utf8', ...opts }) as string;
|
|
22
|
+
return { code: 0, stdout, stderr: '' };
|
|
23
|
+
} catch (e) {
|
|
24
|
+
const err = e as ExecException;
|
|
25
|
+
return {
|
|
26
|
+
code: err.status ?? 1,
|
|
27
|
+
stdout: typeof err.stdout === 'string' ? err.stdout : (err.stdout?.toString() ?? ''),
|
|
28
|
+
stderr: typeof err.stderr === 'string' ? err.stderr : (err.stderr?.toString() ?? ''),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function runValidate(dataDir: string, { env, shapes }: RunOptions = {}): RunResult {
|
|
34
|
+
return execCaught('node', [SCRIPT, '--shapes', shapes ?? SHAPES, dataDir], {
|
|
35
|
+
env: { ...process.env, ...env },
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function runValidateRaw(args: string[], { env }: RunOptions = {}): RunResult {
|
|
40
|
+
return execCaught('node', [SCRIPT, ...args], {
|
|
41
|
+
env: { ...process.env, ...env },
|
|
42
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
describe('validate-shacl.mjs', () => {
|
|
47
|
+
it('passes when a fixture conforms to the shapes', () => {
|
|
48
|
+
const goodDir = join(FIXTURES, 'good');
|
|
49
|
+
const result = runValidate(goodDir);
|
|
50
|
+
expect(result.code).toBe(0);
|
|
51
|
+
expect(result.stdout).toContain('SHACL validation passed');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('fails when a fixture has missing language tags', () => {
|
|
55
|
+
const badDir = join(FIXTURES, 'bad');
|
|
56
|
+
const result = runValidate(badDir);
|
|
57
|
+
expect(result.code).not.toBe(0);
|
|
58
|
+
expect(result.stderr).toContain('SHACL validation FAILED');
|
|
59
|
+
expect(result.stderr).toContain('concept.ttl');
|
|
60
|
+
expect(result.stderr.toLowerCase()).toContain('langstring');
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('aggregates violations across multiple files', () => {
|
|
64
|
+
const result = runValidate(FIXTURES);
|
|
65
|
+
expect(result.code).not.toBe(0);
|
|
66
|
+
expect(result.stderr).not.toContain('good/concept.ttl');
|
|
67
|
+
expect(result.stderr).toContain('bad/concept.ttl');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('exits cleanly when no .ttl files are found', () => {
|
|
71
|
+
const emptyDir = join(FIXTURES, 'empty');
|
|
72
|
+
const result = runValidate(emptyDir);
|
|
73
|
+
expect(result.code).toBe(0);
|
|
74
|
+
expect(result.stdout).toContain('No .ttl files');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('errors clearly when --shapes path does not exist', () => {
|
|
78
|
+
const result = runValidate(FIXTURES, { shapes: '/does/not/exist.ttl' });
|
|
79
|
+
expect(result.code).not.toBe(0);
|
|
80
|
+
expect(result.stderr).toContain('Failed to load SHACL shapes');
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('accepts SHAPES_PATH env var as fallback when --shapes is omitted', () => {
|
|
84
|
+
const result = runValidate(join(FIXTURES, 'good'), {
|
|
85
|
+
env: { SHAPES_PATH: SHAPES },
|
|
86
|
+
});
|
|
87
|
+
expect(result.code).toBe(0);
|
|
88
|
+
expect(result.stdout).toContain('SHACL validation passed');
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('uses the vendored shapes by default when no override is given', () => {
|
|
92
|
+
const result = runValidateRaw([join(FIXTURES, 'good')], {
|
|
93
|
+
env: { SHAPES_PATH: '' },
|
|
94
|
+
});
|
|
95
|
+
// The vendored shapes live at data/concept-model/shapes/glossarist.shacl.ttl
|
|
96
|
+
// and ship with the repo. The good fixture conforms to them.
|
|
97
|
+
expect(result.code).toBe(0);
|
|
98
|
+
expect(result.stdout).toContain('SHACL validation passed');
|
|
99
|
+
});
|
|
100
|
+
});
|
|
@@ -12,6 +12,12 @@ import type {
|
|
|
12
12
|
import type { Concept } from 'glossarist';
|
|
13
13
|
import { conceptFromJson } from './model-bridge';
|
|
14
14
|
import { GraphDataSource } from './GraphDataSource';
|
|
15
|
+
import {
|
|
16
|
+
ChunkLoadError,
|
|
17
|
+
ConceptNotFoundError,
|
|
18
|
+
IndexLoadError,
|
|
19
|
+
ManifestLoadError,
|
|
20
|
+
} from '../errors';
|
|
15
21
|
|
|
16
22
|
// ── Wire-format types for JSON responses ────────────────────────────────────
|
|
17
23
|
|
|
@@ -82,7 +88,7 @@ export class DatasetAdapter {
|
|
|
82
88
|
async loadManifest(): Promise<Manifest> {
|
|
83
89
|
if (this.manifestComplete && this.manifest) return this.manifest;
|
|
84
90
|
const resp = await fetch(`${this.baseUrl}/manifest.json`);
|
|
85
|
-
if (!resp.ok) throw
|
|
91
|
+
if (!resp.ok) throw ManifestLoadError.make(this.registerId, resp.status);
|
|
86
92
|
this.manifest = (await resp.json()) as Manifest;
|
|
87
93
|
this.manifestComplete = true;
|
|
88
94
|
return this.manifest;
|
|
@@ -97,7 +103,7 @@ export class DatasetAdapter {
|
|
|
97
103
|
}
|
|
98
104
|
|
|
99
105
|
const resp = await fetch(`${this.baseUrl}/index.json`);
|
|
100
|
-
if (!resp.ok) throw
|
|
106
|
+
if (!resp.ok) throw IndexLoadError.make(this.registerId, resp.status);
|
|
101
107
|
const data = await resp.json();
|
|
102
108
|
|
|
103
109
|
// Handle both old format (with eng/status fields) and new format (with designations map)
|
|
@@ -150,7 +156,7 @@ export class DatasetAdapter {
|
|
|
150
156
|
meta = await metaResp.json();
|
|
151
157
|
} else {
|
|
152
158
|
const resp = await fetch(`${this.baseUrl}/index.json`);
|
|
153
|
-
if (!resp.ok) throw
|
|
159
|
+
if (!resp.ok) throw IndexLoadError.make(this.registerId, resp.status);
|
|
154
160
|
const data = await resp.json();
|
|
155
161
|
this.index = this.normalizeIndex(data as IndexJson);
|
|
156
162
|
this.buildSummaryIndex();
|
|
@@ -180,7 +186,7 @@ export class DatasetAdapter {
|
|
|
180
186
|
if (this.loadedChunks.has(chunkIndex)) return [];
|
|
181
187
|
const chunkFile = `index-${String(chunkIndex).padStart(4, '0')}.json`;
|
|
182
188
|
const resp = await fetch(`${this.baseUrl}/chunks/${chunkFile}`);
|
|
183
|
-
if (!resp.ok) throw
|
|
189
|
+
if (!resp.ok) throw ChunkLoadError.make(this.registerId, chunkIndex, resp.status);
|
|
184
190
|
const data = await resp.json();
|
|
185
191
|
this.loadedChunks.add(chunkIndex);
|
|
186
192
|
return data.concepts as ConceptEntry[];
|
|
@@ -255,7 +261,7 @@ export class DatasetAdapter {
|
|
|
255
261
|
}
|
|
256
262
|
|
|
257
263
|
const resp = await fetch(`${this.baseUrl}/concepts/${conceptId}.json`);
|
|
258
|
-
if (!resp.ok) throw
|
|
264
|
+
if (!resp.ok) throw ConceptNotFoundError.make(this.registerId, conceptId);
|
|
259
265
|
const json = await resp.json();
|
|
260
266
|
const concept = conceptFromJson(json);
|
|
261
267
|
this.conceptCache.set(conceptId, concept);
|
|
@@ -4,6 +4,7 @@ import type { DatasetAdapter } from './DatasetAdapter';
|
|
|
4
4
|
import { UriRouter } from './UriRouter';
|
|
5
5
|
import { slugify } from '../utils/slugify';
|
|
6
6
|
import { toSectionNode, toSectionTree } from '../utils/section-tree';
|
|
7
|
+
import { ConceptIdentity } from './concept-identity';
|
|
7
8
|
|
|
8
9
|
interface DomainNodeJson {
|
|
9
10
|
uri?: string;
|
|
@@ -23,7 +24,7 @@ function resolveRefTarget(rc: RelatedConcept, uriBase: string, registerId: strin
|
|
|
23
24
|
if (ref.source && !ref.source.startsWith('http')) {
|
|
24
25
|
reg = urnMap?.get(ref.source) ?? ref.source;
|
|
25
26
|
}
|
|
26
|
-
return
|
|
27
|
+
return new ConceptIdentity(ref.id, reg, uriBase).uri;
|
|
27
28
|
}
|
|
28
29
|
if (ref.source && ref.source.startsWith('http')) return ref.source;
|
|
29
30
|
return ref.source || '';
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Manifest } from './types';
|
|
2
|
+
import { ConceptIdentity } from './concept-identity';
|
|
2
3
|
|
|
3
4
|
// ── URI pattern matching ────────────────────────────────────────────────────
|
|
4
5
|
|
|
@@ -104,7 +105,7 @@ export class UriRouter {
|
|
|
104
105
|
buildUri(registerId: string, conceptId: string): string {
|
|
105
106
|
const info = this.registerMap.get(registerId);
|
|
106
107
|
const uriBase = info?.uriBase ?? '';
|
|
107
|
-
return
|
|
108
|
+
return new ConceptIdentity(conceptId, registerId, uriBase).uri;
|
|
108
109
|
}
|
|
109
110
|
|
|
110
111
|
getRegisteredIds(): string[] {
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { InvalidConceptIdentityError, InvalidConceptUriError } from '../errors';
|
|
2
|
+
|
|
3
|
+
export interface ConceptIdentityParts {
|
|
4
|
+
readonly localId: string;
|
|
5
|
+
readonly registerId: string;
|
|
6
|
+
readonly uriBase: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const CONCEPT_URI_RE = /^(.+)\/([^/]+)\/concept\/(.+)$/;
|
|
10
|
+
|
|
11
|
+
export class ConceptIdentity implements ConceptIdentityParts {
|
|
12
|
+
private readonly _uri: string;
|
|
13
|
+
private readonly _slug: string;
|
|
14
|
+
private readonly _path: string;
|
|
15
|
+
|
|
16
|
+
constructor(
|
|
17
|
+
public readonly localId: string,
|
|
18
|
+
public readonly registerId: string,
|
|
19
|
+
public readonly uriBase: string,
|
|
20
|
+
) {
|
|
21
|
+
if (!localId || !registerId || !uriBase) {
|
|
22
|
+
throw new InvalidConceptIdentityError(
|
|
23
|
+
'ConceptIdentity requires non-empty localId, registerId, and uriBase',
|
|
24
|
+
{ localId, registerId, uriBase },
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
this._uri = `${uriBase}/${registerId}/concept/${localId}`;
|
|
28
|
+
this._slug = localId;
|
|
29
|
+
this._path = `${registerId}/concepts/${localId}`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
get uri(): string { return this._uri; }
|
|
33
|
+
get slug(): string { return this._slug; }
|
|
34
|
+
get path(): string { return this._path; }
|
|
35
|
+
|
|
36
|
+
equals(other: ConceptIdentity): boolean {
|
|
37
|
+
return this._uri === other._uri;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
toString(): string { return this._uri; }
|
|
41
|
+
|
|
42
|
+
toJSON(): ConceptIdentityParts {
|
|
43
|
+
return { localId: this.localId, registerId: this.registerId, uriBase: this.uriBase };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
localizationUri(lang: string): string {
|
|
47
|
+
return `${this._uri}/${lang}`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
designationUri(lang: string, slug: string): string {
|
|
51
|
+
return `${this._uri}/${lang}/desig/${slug}`;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
domainUri(domainSlug: string): string {
|
|
55
|
+
return `${this.uriBase}/${this.registerId}/domain/${domainSlug}`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
static fromUri(uri: string): ConceptIdentity {
|
|
59
|
+
const m = uri.match(CONCEPT_URI_RE);
|
|
60
|
+
if (!m) {
|
|
61
|
+
throw InvalidConceptUriError.make(uri);
|
|
62
|
+
}
|
|
63
|
+
return new ConceptIdentity(m[3], m[2], m[1]);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
static isConceptUri(uri: string): boolean {
|
|
67
|
+
return CONCEPT_URI_RE.test(uri);
|
|
68
|
+
}
|
|
69
|
+
}
|
package/src/adapters/factory.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { ReferenceResolver } from './ReferenceResolver';
|
|
|
5
5
|
import { UriRouter } from './UriRouter';
|
|
6
6
|
import { NonVerbalEntityResolver } from './non-verbal-resolver';
|
|
7
7
|
import { BibliographyAdapter } from './bibliography-adapter';
|
|
8
|
+
import { DatasetRegistryLoadError, UnknownDatasetError } from '../errors';
|
|
8
9
|
|
|
9
10
|
export class AdapterFactory {
|
|
10
11
|
private adapters = new Map<string, DatasetAdapter>();
|
|
@@ -36,7 +37,7 @@ export class AdapterFactory {
|
|
|
36
37
|
registry = JSON.parse(inline.textContent) as DatasetRegistry[];
|
|
37
38
|
} else {
|
|
38
39
|
const resp = await fetch(datasetsUrl);
|
|
39
|
-
if (!resp.ok) throw
|
|
40
|
+
if (!resp.ok) throw DatasetRegistryLoadError.make(resp.status, datasetsUrl);
|
|
40
41
|
registry = await resp.json() as DatasetRegistry[];
|
|
41
42
|
}
|
|
42
43
|
|
|
@@ -132,7 +133,7 @@ export class AdapterFactory {
|
|
|
132
133
|
|
|
133
134
|
async loadDataset(registerId: string): Promise<DatasetAdapter> {
|
|
134
135
|
const adapter = this.adapters.get(registerId);
|
|
135
|
-
if (!adapter) throw
|
|
136
|
+
if (!adapter) throw UnknownDatasetError.make(registerId);
|
|
136
137
|
|
|
137
138
|
const manifest = await adapter.loadManifest();
|
|
138
139
|
await adapter.loadIndex();
|
|
@@ -36,6 +36,7 @@ import {
|
|
|
36
36
|
GRAMMAR_PARTS_OF_SPEECH,
|
|
37
37
|
} from 'glossarist/models';
|
|
38
38
|
import type { ConceptSummary } from './types';
|
|
39
|
+
import { ConceptIdentity } from './concept-identity';
|
|
39
40
|
|
|
40
41
|
// ── JSON-LD wire-format types ─────────────────────────────────────────────
|
|
41
42
|
|
|
@@ -634,5 +635,5 @@ export function conceptToSummary(concept: Concept): ConceptSummary {
|
|
|
634
635
|
|
|
635
636
|
export function conceptUri(concept: Concept, registerId: string, uriBase: string): string {
|
|
636
637
|
if (concept.uri) return concept.uri;
|
|
637
|
-
return
|
|
638
|
+
return new ConceptIdentity(concept.id, registerId, uriBase).uri;
|
|
638
639
|
}
|
|
@@ -91,6 +91,13 @@ declare module 'glossarist' {
|
|
|
91
91
|
static register(type: string, cls: typeof NonVerbalReference): void;
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
+
interface NonVerbRep {
|
|
95
|
+
readonly caption: string | null;
|
|
96
|
+
readonly description: string | null;
|
|
97
|
+
readonly alt: string | null;
|
|
98
|
+
readonly images: FigureImage[];
|
|
99
|
+
}
|
|
100
|
+
|
|
94
101
|
class FigureReference extends NonVerbalReference {
|
|
95
102
|
static fromJSON(data: Record<string, unknown> | string): FigureReference;
|
|
96
103
|
}
|
|
@@ -19,6 +19,7 @@ import type { NonVerbalKind } from './non-verbal/types';
|
|
|
19
19
|
import type { NonVerbalEntity } from 'glossarist';
|
|
20
20
|
import { KIND_TO_DIR, KIND_TO_BRIDGE } from './non-verbal/kind';
|
|
21
21
|
import { anchorId } from '../utils/non-verbal-anchor';
|
|
22
|
+
import { NonVerbalEntityNotFoundError } from '../errors';
|
|
22
23
|
|
|
23
24
|
export type { NonVerbalKind } from './non-verbal/types';
|
|
24
25
|
export type { NonVerbalEntity } from 'glossarist';
|
|
@@ -54,7 +55,7 @@ export class NonVerbalEntityResolver {
|
|
|
54
55
|
const resp = await this.fetcher(url);
|
|
55
56
|
if (resp.status === 404) return null;
|
|
56
57
|
if (!resp.ok) {
|
|
57
|
-
throw
|
|
58
|
+
throw NonVerbalEntityNotFoundError.make(datasetId, kind, entityId, resp.status);
|
|
58
59
|
}
|
|
59
60
|
const doc = (await resp.json()) as Record<string, unknown>;
|
|
60
61
|
const entity = KIND_TO_BRIDGE[kind](doc);
|