@glossarist/concept-browser 0.7.50 → 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/figure-bridge.ts +22 -23
- package/src/adapters/non-verbal/formula-bridge.ts +11 -9
- package/src/adapters/non-verbal/glossarist-augment.d.ts +133 -0
- package/src/adapters/non-verbal/index.ts +12 -9
- package/src/adapters/non-verbal/kind.ts +2 -1
- package/src/adapters/non-verbal/table-bridge.ts +12 -10
- package/src/adapters/non-verbal/types.ts +36 -54
- package/src/adapters/non-verbal-resolver.ts +6 -3
- 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/NonVerbalRepDisplay.vue +2 -2
- 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/figure/FigureDisplay.vue +16 -15
- package/src/components/figure/FigureImages.vue +38 -16
- package/src/components/figure/figure-image-pick.ts +1 -1
- package/src/components/figure/figure-layout.ts +1 -1
- package/src/components/formula/FormulaDisplay.vue +11 -9
- package/src/components/formula/FormulaExpression.vue +4 -4
- package/src/components/non-verbal/NonVerbalCaption.vue +5 -5
- package/src/components/non-verbal/NonVerbalSources.vue +3 -11
- package/src/components/table/TableDisplay.vue +6 -4
- package/src/components/table/TableMarkup.vue +1 -1
- package/src/composables/use-color-theme.ts +82 -0
- package/src/composables/use-format-registry.ts +42 -0
- package/src/composables/use-non-verbal-entity.ts +2 -1
- 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
|
@@ -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
|
}
|
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
* ambiguity with the HTML `<img alt>` attribute.
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
-
import
|
|
27
|
-
import { isType, pickField, pickFieldArray,
|
|
26
|
+
import { Figure, FigureImage } from 'glossarist';
|
|
27
|
+
import { isType, pickField, pickFieldArray, localized } from './prefix';
|
|
28
28
|
import { sourcesFromJsonLd } from './source-bridge';
|
|
29
29
|
|
|
30
30
|
const FORMAT_SET: ReadonlySet<string> = new Set(['svg', 'png', 'jpg', 'jpeg', 'gif', 'webp', 'avif']);
|
|
@@ -35,18 +35,20 @@ function imageFromJsonLd(raw: Record<string, unknown>): FigureImage | null {
|
|
|
35
35
|
const src = pickField<string>(raw, 'src');
|
|
36
36
|
if (!src) return null;
|
|
37
37
|
const formatRaw = (pickField<string>(raw, 'format') ?? '').toLowerCase();
|
|
38
|
-
const format =
|
|
38
|
+
const format = FORMAT_SET.has(formatRaw) ? formatRaw : 'svg';
|
|
39
39
|
const roleRaw = pickField<string>(raw, 'role');
|
|
40
|
-
const role = roleRaw && ROLE_SET.has(roleRaw) ?
|
|
40
|
+
const role = roleRaw && ROLE_SET.has(roleRaw) ? roleRaw : undefined;
|
|
41
41
|
const width = pickField<number>(raw, 'width');
|
|
42
42
|
const height = pickField<number>(raw, 'height');
|
|
43
43
|
const scale = pickField<number>(raw, 'scale');
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
44
|
+
return new FigureImage({
|
|
45
|
+
src,
|
|
46
|
+
format,
|
|
47
|
+
...(role !== undefined && { role }),
|
|
48
|
+
...(typeof width === 'number' && { width }),
|
|
49
|
+
...(typeof height === 'number' && { height }),
|
|
50
|
+
...(typeof scale === 'number' && { scale }),
|
|
51
|
+
});
|
|
50
52
|
}
|
|
51
53
|
|
|
52
54
|
function imagesFromJsonLd(raw: unknown): FigureImage[] {
|
|
@@ -85,17 +87,14 @@ export function figureFromJsonLd(doc: Record<string, unknown>): Figure | null {
|
|
|
85
87
|
const subfigures = subfiguresFromJsonLd(pickField(doc, 'subfigure'));
|
|
86
88
|
const sources = sourcesFromJsonLd(pickField(doc, 'source'));
|
|
87
89
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
void pickFieldRecord;
|
|
99
|
-
|
|
100
|
-
return fig;
|
|
90
|
+
return new Figure({
|
|
91
|
+
id,
|
|
92
|
+
images,
|
|
93
|
+
...(identifier && { identifier }),
|
|
94
|
+
...(caption && { caption }),
|
|
95
|
+
...(alt && { alt }),
|
|
96
|
+
...(description && { description }),
|
|
97
|
+
...(subfigures && { subfigures }),
|
|
98
|
+
...(sources.length && { sources }),
|
|
99
|
+
});
|
|
101
100
|
}
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* }
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import
|
|
18
|
+
import { Formula } from 'glossarist';
|
|
19
19
|
import { isType, pickField, localized } from './prefix';
|
|
20
20
|
import { sourcesFromJsonLd } from './source-bridge';
|
|
21
21
|
|
|
@@ -31,18 +31,20 @@ export function formulaFromJsonLd(doc: Record<string, unknown>): Formula | null
|
|
|
31
31
|
if (!expression) return null;
|
|
32
32
|
|
|
33
33
|
const notationRaw = (pickField<string>(doc, 'notation') ?? '').toLowerCase();
|
|
34
|
-
const notation = NOTATION_SET.has(notationRaw) ?
|
|
34
|
+
const notation = NOTATION_SET.has(notationRaw) ? notationRaw : 'latex';
|
|
35
35
|
|
|
36
36
|
const identifier = pickField<string>(doc, 'identifier');
|
|
37
37
|
const caption = localized(doc, 'caption');
|
|
38
38
|
const description = localized(doc, 'description');
|
|
39
39
|
const sources = sourcesFromJsonLd(pickField(doc, 'source'));
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
41
|
+
return new Formula({
|
|
42
|
+
id,
|
|
43
|
+
expression,
|
|
44
|
+
notation,
|
|
45
|
+
...(identifier && { identifier }),
|
|
46
|
+
...(caption && { caption }),
|
|
47
|
+
...(description && { description }),
|
|
48
|
+
...(sources.length && { sources }),
|
|
49
|
+
});
|
|
48
50
|
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
// Local module augmentation for glossarist 0.4.2.
|
|
2
|
+
//
|
|
3
|
+
// Upstream's published src/models/index.d.ts declares ZERO classes for the
|
|
4
|
+
// non-verbal hierarchy (Figure, Table, Formula, FigureImage, NonVerbalEntity,
|
|
5
|
+
// SharedNonVerbalEntity, NonVerbalReference + subclasses, BibliographyEntry,
|
|
6
|
+
// BibliographyData) plus the localized-string helpers. The top-level
|
|
7
|
+
// index.d.ts re-exports the names, so TypeScript silently resolves every
|
|
8
|
+
// consumer import to `any`.
|
|
9
|
+
//
|
|
10
|
+
// This file declares the runtime shape so consumer code can be type-checked.
|
|
11
|
+
// DELETE this file when upstream ships proper declarations — tracked by
|
|
12
|
+
// PR glossarist/glossarist-js#31 (targets v0.4.3+).
|
|
13
|
+
|
|
14
|
+
import type { ConceptSource, GlossaristModel } from 'glossarist';
|
|
15
|
+
|
|
16
|
+
declare module 'glossarist' {
|
|
17
|
+
class RegistrableModel extends GlossaristModel {
|
|
18
|
+
static register(type: string, cls: typeof RegistrableModel): void;
|
|
19
|
+
static fromData(data: Record<string, unknown>): RegistrableModel;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
class FigureImage extends GlossaristModel {
|
|
23
|
+
constructor(data?: {
|
|
24
|
+
src?: string | null;
|
|
25
|
+
format?: string | null;
|
|
26
|
+
role?: string | null;
|
|
27
|
+
width?: number | null;
|
|
28
|
+
height?: number | null;
|
|
29
|
+
scale?: number | null;
|
|
30
|
+
});
|
|
31
|
+
readonly src: string | null;
|
|
32
|
+
readonly format: string | null;
|
|
33
|
+
readonly role: string | null;
|
|
34
|
+
readonly width: number | null;
|
|
35
|
+
readonly height: number | null;
|
|
36
|
+
readonly scale: number | null;
|
|
37
|
+
static fromJSON(data: Record<string, unknown>): FigureImage;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
class NonVerbalEntity extends RegistrableModel {
|
|
41
|
+
constructor(data?: Record<string, unknown>);
|
|
42
|
+
readonly caption: Record<string, string> | null;
|
|
43
|
+
readonly description: Record<string, string> | null;
|
|
44
|
+
readonly alt: Record<string, string> | null;
|
|
45
|
+
readonly sources: ConceptSource[];
|
|
46
|
+
findById(targetId: string): NonVerbalEntity | null;
|
|
47
|
+
allIds(): string[];
|
|
48
|
+
static fromJSON(data: Record<string, unknown>): NonVerbalEntity;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
class SharedNonVerbalEntity extends NonVerbalEntity {
|
|
52
|
+
constructor(data?: Record<string, unknown>);
|
|
53
|
+
readonly id: string | null;
|
|
54
|
+
readonly identifier: string | null;
|
|
55
|
+
findById(targetId: string): SharedNonVerbalEntity | null;
|
|
56
|
+
allIds(): string[];
|
|
57
|
+
static fromJSON(data: Record<string, unknown>): SharedNonVerbalEntity;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
class Figure extends SharedNonVerbalEntity {
|
|
61
|
+
constructor(data?: Record<string, unknown>);
|
|
62
|
+
readonly images: FigureImage[];
|
|
63
|
+
readonly subfigures: Figure[];
|
|
64
|
+
findById(targetId: string): Figure | null;
|
|
65
|
+
allIds(): string[];
|
|
66
|
+
static fromJSON(data: Record<string, unknown>): Figure;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
class Table extends SharedNonVerbalEntity {
|
|
70
|
+
constructor(data?: Record<string, unknown>);
|
|
71
|
+
readonly content: Record<string, unknown> | null;
|
|
72
|
+
readonly format: string | null;
|
|
73
|
+
static fromJSON(data: Record<string, unknown>): Table;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
class Formula extends SharedNonVerbalEntity {
|
|
77
|
+
constructor(data?: Record<string, unknown>);
|
|
78
|
+
readonly expression: Record<string, string> | null;
|
|
79
|
+
readonly notation: string | null;
|
|
80
|
+
static fromJSON(data: Record<string, unknown>): Formula;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const NON_VERBAL_TYPES: readonly string[];
|
|
84
|
+
|
|
85
|
+
class NonVerbalReference extends RegistrableModel {
|
|
86
|
+
constructor(data?: Record<string, unknown>);
|
|
87
|
+
readonly entityId: string | null;
|
|
88
|
+
readonly display: string | null;
|
|
89
|
+
readonly dedupKey: readonly [string, string | null];
|
|
90
|
+
static fromJSON(data: Record<string, unknown> | string): NonVerbalReference;
|
|
91
|
+
static register(type: string, cls: typeof NonVerbalReference): void;
|
|
92
|
+
}
|
|
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
|
+
|
|
101
|
+
class FigureReference extends NonVerbalReference {
|
|
102
|
+
static fromJSON(data: Record<string, unknown> | string): FigureReference;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
class TableReference extends NonVerbalReference {
|
|
106
|
+
static fromJSON(data: Record<string, unknown> | string): TableReference;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
class FormulaReference extends NonVerbalReference {
|
|
110
|
+
static fromJSON(data: Record<string, unknown> | string): FormulaReference;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
class BibliographyEntry extends GlossaristModel {
|
|
114
|
+
constructor(data?: Record<string, unknown>);
|
|
115
|
+
readonly id: string | null;
|
|
116
|
+
readonly reference: string | null;
|
|
117
|
+
readonly title: string | null;
|
|
118
|
+
readonly link: string | null;
|
|
119
|
+
readonly type: string | null;
|
|
120
|
+
static fromJSON(data: Record<string, unknown>): BibliographyEntry;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
class BibliographyData extends GlossaristModel {
|
|
124
|
+
constructor(data?: Record<string, unknown>);
|
|
125
|
+
readonly entries: BibliographyEntry[];
|
|
126
|
+
find(id: string): BibliographyEntry | null;
|
|
127
|
+
readonly keys: string[];
|
|
128
|
+
toYAML(): string;
|
|
129
|
+
toJSON(): { bibliography: BibliographyEntry[] };
|
|
130
|
+
static fromYAML(yamlString: string): BibliographyData;
|
|
131
|
+
static fromJSON(data: Record<string, unknown>): BibliographyData;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -1,33 +1,36 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Public API for the non-verbal entity model layer.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Model classes (Figure, Table, Formula, FigureImage, NonVerbalEntity) are
|
|
5
|
+
* re-exported from `glossarist` — upstream is the SSOT for the model.
|
|
6
|
+
* Consumer-owned types live in `./types`.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
export type {
|
|
10
10
|
LocalizedString,
|
|
11
11
|
NonVerbalKind,
|
|
12
|
-
FigureImage,
|
|
13
12
|
FigureImageFormat,
|
|
14
13
|
FigureImageRole,
|
|
15
14
|
NonVerbalSource,
|
|
16
15
|
NonVerbalSourceOrigin,
|
|
17
16
|
NonVerbalSourceRef,
|
|
18
17
|
NonVerbalSourceLocality,
|
|
19
|
-
NonVerbalEntityBase,
|
|
20
|
-
Figure,
|
|
21
|
-
Table,
|
|
22
18
|
TableContent,
|
|
23
19
|
TableFormat,
|
|
24
|
-
Formula,
|
|
25
20
|
FormulaNotation,
|
|
26
|
-
NonVerbalEntity,
|
|
27
21
|
NonVerbRepV3,
|
|
28
22
|
NonVerbalReference,
|
|
29
23
|
} from './types';
|
|
30
24
|
|
|
25
|
+
export type {
|
|
26
|
+
Figure,
|
|
27
|
+
FigureImage,
|
|
28
|
+
Table,
|
|
29
|
+
Formula,
|
|
30
|
+
NonVerbalEntity,
|
|
31
|
+
SharedNonVerbalEntity,
|
|
32
|
+
} from 'glossarist';
|
|
33
|
+
|
|
31
34
|
export { figureFromJsonLd } from './figure-bridge';
|
|
32
35
|
export { tableFromJsonLd } from './table-bridge';
|
|
33
36
|
export { formulaFromJsonLd } from './formula-bridge';
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
* }
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
|
-
import
|
|
23
|
+
import { Table } from 'glossarist';
|
|
24
|
+
import type { TableContent } from './types';
|
|
24
25
|
import { isType, pickField, localized } from './prefix';
|
|
25
26
|
import { sourcesFromJsonLd } from './source-bridge';
|
|
26
27
|
|
|
@@ -83,16 +84,17 @@ export function tableFromJsonLd(doc: Record<string, unknown>): Table | null {
|
|
|
83
84
|
if (!content) return null;
|
|
84
85
|
|
|
85
86
|
const formatRaw = (pickField<string>(doc, 'format') ?? '').toLowerCase();
|
|
86
|
-
const format = FORMAT_SET.has(formatRaw) ?
|
|
87
|
+
const format = FORMAT_SET.has(formatRaw) ? formatRaw : undefined;
|
|
87
88
|
|
|
88
89
|
const sources = sourcesFromJsonLd(pickField(doc, 'source'));
|
|
89
90
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
91
|
+
return new Table({
|
|
92
|
+
id,
|
|
93
|
+
content: content as Record<string, unknown>,
|
|
94
|
+
...(identifier && { identifier }),
|
|
95
|
+
...(caption && { caption }),
|
|
96
|
+
...(description && { description }),
|
|
97
|
+
...(format && { format }),
|
|
98
|
+
...(sources.length && { sources }),
|
|
99
|
+
});
|
|
98
100
|
}
|
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* glossarist-ruby model.
|
|
2
|
+
* Consumer-side types for non-verbal entities.
|
|
4
3
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* corresponds to a field in the authoritative source.
|
|
4
|
+
* Model classes (Figure, Table, Formula, FigureImage, NonVerbalEntity) are
|
|
5
|
+
* imported from `glossarist` — the upstream library is the SSOT for the
|
|
6
|
+
* model. This file holds only what is genuinely consumer-owned:
|
|
9
7
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
8
|
+
* - `NonVerbalKind`: routing discriminator used by the resolver, the
|
|
9
|
+
* anchor scheme, the mention dispatcher, and the section router.
|
|
10
|
+
* - `NonVerbRepV3`: local view of NonVerbRep's V3 shape. Upstream's
|
|
11
|
+
* published .d.ts still describes the pre-V3 `ref`/`text` shape; this
|
|
12
|
+
* interface lets the consumer type-check against runtime reality.
|
|
13
|
+
* Delete when upstream ships a corrected declaration.
|
|
14
|
+
* - `NonVerbalReference`: consumer-side view of inline mentions like
|
|
15
|
+
* `{{fig:foo}}`. Carries a `kind` for UI routing.
|
|
16
|
+
* - `LocalizedString`, `FigureImageFormat`, `FigureImageRole`,
|
|
17
|
+
* `TableFormat`, `TableContent`, `FormulaNotation`: string-union
|
|
18
|
+
* refinements the consumer validates at bridge time.
|
|
19
|
+
* - `NonVerbalSource*`: wire shape for JSON-LD source entries. Stays
|
|
20
|
+
* consumer-side until upstream ships a V3 NonVerbalSource model.
|
|
16
21
|
*/
|
|
17
22
|
|
|
18
23
|
export type LocalizedString = Record<string, string>;
|
|
@@ -23,15 +28,6 @@ export type FigureImageFormat = 'svg' | 'png' | 'jpg' | 'jpeg' | 'gif' | 'webp'
|
|
|
23
28
|
|
|
24
29
|
export type FigureImageRole = 'vector' | 'raster' | 'dark' | 'light' | 'print';
|
|
25
30
|
|
|
26
|
-
export interface FigureImage {
|
|
27
|
-
src: string;
|
|
28
|
-
format: FigureImageFormat;
|
|
29
|
-
role?: FigureImageRole;
|
|
30
|
-
width?: number;
|
|
31
|
-
height?: number;
|
|
32
|
-
scale?: number;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
31
|
export interface NonVerbalSourceRef {
|
|
36
32
|
source?: string;
|
|
37
33
|
id?: string;
|
|
@@ -62,56 +58,42 @@ export interface NonVerbalSource {
|
|
|
62
58
|
origin?: NonVerbalSourceOrigin;
|
|
63
59
|
}
|
|
64
60
|
|
|
65
|
-
export interface NonVerbalEntityBase {
|
|
66
|
-
id: string;
|
|
67
|
-
identifier?: string;
|
|
68
|
-
caption?: LocalizedString;
|
|
69
|
-
description?: LocalizedString;
|
|
70
|
-
alt?: LocalizedString;
|
|
71
|
-
sources?: NonVerbalSource[];
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export interface Figure extends NonVerbalEntityBase {
|
|
75
|
-
kind: 'figure';
|
|
76
|
-
images: FigureImage[];
|
|
77
|
-
subfigures?: Figure[];
|
|
78
|
-
}
|
|
79
|
-
|
|
80
61
|
export type TableFormat = 'html' | 'markdown' | 'asciidoc';
|
|
81
62
|
|
|
82
63
|
export type TableContent =
|
|
83
64
|
| { kind: 'structured'; headers: LocalizedString[]; rows: LocalizedString[][] }
|
|
84
65
|
| { kind: 'markup'; markup: LocalizedString };
|
|
85
66
|
|
|
86
|
-
export interface Table extends NonVerbalEntityBase {
|
|
87
|
-
kind: 'table';
|
|
88
|
-
content: TableContent;
|
|
89
|
-
format?: TableFormat;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
67
|
export type FormulaNotation = 'latex' | 'mathml' | 'asciimath';
|
|
93
68
|
|
|
94
|
-
export interface Formula extends NonVerbalEntityBase {
|
|
95
|
-
kind: 'formula';
|
|
96
|
-
expression: LocalizedString;
|
|
97
|
-
notation: FormulaNotation;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
export type NonVerbalEntity = Figure | Table | Formula;
|
|
101
|
-
|
|
102
69
|
/**
|
|
103
70
|
* V3 NonVerbRep runtime shape.
|
|
104
71
|
*
|
|
105
72
|
* glossarist-js's runtime `NonVerbRep` (post-V3 reshape) holds the same
|
|
106
|
-
* localized fields as
|
|
73
|
+
* localized fields as the base NonVerbalEntity plus a `type` discriminator
|
|
107
74
|
* and an `images[]` array. The published `.d.ts` (still stale at v0.4.2)
|
|
108
75
|
* describes the pre-V3 `ref`/`text` shape; this local interface lets
|
|
109
76
|
* consumer code type-check against reality. Drop when upstream ships a
|
|
110
|
-
* corrected `.d.ts
|
|
77
|
+
* corrected `.d.ts` (glossarist/glossarist-js#31).
|
|
111
78
|
*/
|
|
112
|
-
export interface NonVerbRepV3
|
|
79
|
+
export interface NonVerbRepV3 {
|
|
80
|
+
id: string;
|
|
81
|
+
identifier?: string | null;
|
|
113
82
|
type: string | null;
|
|
114
|
-
|
|
83
|
+
caption?: LocalizedString | null;
|
|
84
|
+
description?: LocalizedString | null;
|
|
85
|
+
alt?: LocalizedString | null;
|
|
86
|
+
images: NonVerbRepImage[];
|
|
87
|
+
sources?: NonVerbalSource[];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface NonVerbRepImage {
|
|
91
|
+
src: string;
|
|
92
|
+
format?: string | null;
|
|
93
|
+
role?: string | null;
|
|
94
|
+
width?: number | null;
|
|
95
|
+
height?: number | null;
|
|
96
|
+
scale?: number | null;
|
|
115
97
|
}
|
|
116
98
|
|
|
117
99
|
export interface NonVerbalReference {
|