@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
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sphere projection + force-layout math for RelationSphere.
|
|
3
|
+
*
|
|
4
|
+
* All functions are pure — no Vue, no DOM. The component passes positions in,
|
|
5
|
+
* gets screen coordinates out. This keeps the math testable and the component
|
|
6
|
+
* focused on rendering.
|
|
7
|
+
*
|
|
8
|
+
* Coordinates: unit sphere (‖p‖ = 1). The "front pole" is (0, 0, 1) — the
|
|
9
|
+
* focal point of the camera. The simulation runs in 3D and is projected to
|
|
10
|
+
* 2D with perspective at render time.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export interface Vec3 {
|
|
14
|
+
x: number;
|
|
15
|
+
y: number;
|
|
16
|
+
z: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Perspective projection constants — tuned for unit sphere. */
|
|
20
|
+
const FOCAL = 600;
|
|
21
|
+
const Z_OFFSET = 800;
|
|
22
|
+
const SPHERE_R = 360;
|
|
23
|
+
|
|
24
|
+
/** Slow-fast-slow easing — cubic ease-in-out. */
|
|
25
|
+
export function easeInOutCubic(t: number): number {
|
|
26
|
+
return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Great-circle interpolation between two unit-sphere points. */
|
|
30
|
+
export function slerp(a: Vec3, b: Vec3, t: number): Vec3 {
|
|
31
|
+
const dot = Math.max(-1, Math.min(1, a.x * b.x + a.y * b.y + a.z * b.z));
|
|
32
|
+
const omega = Math.acos(dot);
|
|
33
|
+
if (omega < 0.001) {
|
|
34
|
+
return {
|
|
35
|
+
x: a.x + (b.x - a.x) * t,
|
|
36
|
+
y: a.y + (b.y - a.y) * t,
|
|
37
|
+
z: a.z + (b.z - a.z) * t,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
const sinO = Math.sin(omega);
|
|
41
|
+
const wa = Math.sin((1 - t) * omega) / sinO;
|
|
42
|
+
const wb = Math.sin(t * omega) / sinO;
|
|
43
|
+
return {
|
|
44
|
+
x: a.x * wa + b.x * wb,
|
|
45
|
+
y: a.y * wa + b.y * wb,
|
|
46
|
+
z: a.z * wa + b.z * wb,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Initial sphere position by depth band. Focus at (0,0,1); 1°/2°/3° neighbors
|
|
52
|
+
* distributed at increasing angular distance from the focus.
|
|
53
|
+
*
|
|
54
|
+
* Depth 1 forms an EVEN ring (best spacing for small N). Deeper levels use
|
|
55
|
+
* the golden-angle offset so they interleave with the previous ring.
|
|
56
|
+
*/
|
|
57
|
+
export function fibonacciSpherePosition(
|
|
58
|
+
depth: number,
|
|
59
|
+
idx: number,
|
|
60
|
+
total: number,
|
|
61
|
+
jitterSeed: number,
|
|
62
|
+
): Vec3 {
|
|
63
|
+
if (depth === 0) return { x: 0, y: 0, z: 1 };
|
|
64
|
+
/* Angular distance from north pole — each ring further out. */
|
|
65
|
+
const thetaByDepth: Record<number, number> = { 1: 1.15, 2: 1.55, 3: 1.95 };
|
|
66
|
+
const theta = thetaByDepth[depth] ?? 1.15;
|
|
67
|
+
const j = (jitterSeed % 7) * 0.008;
|
|
68
|
+
const t = theta + j;
|
|
69
|
+
let phi: number;
|
|
70
|
+
if (depth === 1) {
|
|
71
|
+
/* Even ring for depth 1 — gives best spread for small N */
|
|
72
|
+
phi = (idx / Math.max(total, 1)) * Math.PI * 2;
|
|
73
|
+
} else {
|
|
74
|
+
/* Golden-angle spiral for deeper rings, offset by depth so rings interleave */
|
|
75
|
+
phi = (idx * 2.39996 + depth * 1.7 + (jitterSeed % 11) * 0.4) % (Math.PI * 2);
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
x: Math.sin(t) * Math.cos(phi),
|
|
79
|
+
y: Math.sin(t) * Math.sin(phi),
|
|
80
|
+
z: Math.cos(t),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** 3D unit-sphere point → 2D screen offset from sphere center. */
|
|
85
|
+
export interface Projected {
|
|
86
|
+
x: number;
|
|
87
|
+
y: number;
|
|
88
|
+
scale: number;
|
|
89
|
+
z: number;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function project(p: Vec3): Projected {
|
|
93
|
+
const persp = FOCAL / (Z_OFFSET - p.z * SPHERE_R);
|
|
94
|
+
return {
|
|
95
|
+
x: p.x * SPHERE_R * persp,
|
|
96
|
+
y: -p.y * SPHERE_R * persp,
|
|
97
|
+
scale: persp,
|
|
98
|
+
z: p.z,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Find the point on a rectangle's edge in the direction of an external point.
|
|
104
|
+
* Used so SVG edge paths start/end on the rim of the card, not its center.
|
|
105
|
+
*/
|
|
106
|
+
export function cardEdge(
|
|
107
|
+
from: { x: number; y: number },
|
|
108
|
+
to: { x: number; y: number },
|
|
109
|
+
w: number,
|
|
110
|
+
h: number,
|
|
111
|
+
): { x: number; y: number } {
|
|
112
|
+
const dx = to.x - from.x;
|
|
113
|
+
const dy = to.y - from.y;
|
|
114
|
+
const len = Math.sqrt(dx * dx + dy * dy) || 1;
|
|
115
|
+
const ux = dx / len;
|
|
116
|
+
const uy = dy / len;
|
|
117
|
+
const absUx = Math.abs(ux);
|
|
118
|
+
const absUy = Math.abs(uy);
|
|
119
|
+
const halfW = w / 2;
|
|
120
|
+
const halfH = h / 2;
|
|
121
|
+
const tX = absUx > 0.001 ? halfW / absUx : Infinity;
|
|
122
|
+
const tY = absUy > 0.001 ? halfH / absUy : Infinity;
|
|
123
|
+
const t = Math.min(tX, tY);
|
|
124
|
+
return { x: from.x + ux * t, y: from.y + uy * t };
|
|
125
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dataset group type registry.
|
|
3
|
+
*
|
|
4
|
+
* Maps each `DatasetGroupKind` to its semantic metadata. Adding a new kind
|
|
5
|
+
* is a single entry here + a new renderer component — no edits to existing
|
|
6
|
+
* components needed (open/closed principle).
|
|
7
|
+
*
|
|
8
|
+
* The registry is intentionally pure data — no Vue imports — so it can be
|
|
9
|
+
* consumed by both the sidebar (compact rendering) and the home page (rich
|
|
10
|
+
* rendering) without coupling.
|
|
11
|
+
*/
|
|
12
|
+
import type { DatasetGroupKind } from './types';
|
|
13
|
+
|
|
14
|
+
export interface GroupTypeMeta {
|
|
15
|
+
/** Discriminator value matching `DatasetGroup.kind`. */
|
|
16
|
+
kind: DatasetGroupKind;
|
|
17
|
+
/** Human label for the kind, e.g. "Edition series". */
|
|
18
|
+
label: string;
|
|
19
|
+
/** Short description shown in tooltips / section headers. */
|
|
20
|
+
description: string;
|
|
21
|
+
/** Icon glyph — used in section headers and breadcrumbs. */
|
|
22
|
+
glyph: string;
|
|
23
|
+
/** Whether members have an inherent temporal ordering. */
|
|
24
|
+
ordered: boolean;
|
|
25
|
+
/** Whether members have a supersession chain (newer supersedes older). */
|
|
26
|
+
supersession: boolean;
|
|
27
|
+
/** Whether members share the same vocabulary identity across editions. */
|
|
28
|
+
sameVocabulary: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const GROUP_TYPES: Record<DatasetGroupKind, GroupTypeMeta> = {
|
|
32
|
+
lineage: {
|
|
33
|
+
kind: 'lineage',
|
|
34
|
+
label: 'Edition series',
|
|
35
|
+
description: 'Same vocabulary, different editions over time',
|
|
36
|
+
glyph: '⏳',
|
|
37
|
+
ordered: true,
|
|
38
|
+
supersession: true,
|
|
39
|
+
sameVocabulary: true,
|
|
40
|
+
},
|
|
41
|
+
topic: {
|
|
42
|
+
kind: 'topic',
|
|
43
|
+
label: 'Topic bundle',
|
|
44
|
+
description: 'Different vocabularies covering the same subject',
|
|
45
|
+
glyph: '◆',
|
|
46
|
+
ordered: false,
|
|
47
|
+
supersession: false,
|
|
48
|
+
sameVocabulary: false,
|
|
49
|
+
},
|
|
50
|
+
family: {
|
|
51
|
+
kind: 'family',
|
|
52
|
+
label: 'Publication family',
|
|
53
|
+
description: 'Related vocabularies from the same publisher or program',
|
|
54
|
+
glyph: '✦',
|
|
55
|
+
ordered: false,
|
|
56
|
+
supersession: false,
|
|
57
|
+
sameVocabulary: false,
|
|
58
|
+
},
|
|
59
|
+
collection: {
|
|
60
|
+
kind: 'collection',
|
|
61
|
+
label: 'Curated collection',
|
|
62
|
+
description: 'Hand-picked bundle of datasets for a specific audience',
|
|
63
|
+
glyph: '❖',
|
|
64
|
+
ordered: false,
|
|
65
|
+
supersession: false,
|
|
66
|
+
sameVocabulary: false,
|
|
67
|
+
},
|
|
68
|
+
default: {
|
|
69
|
+
kind: 'default',
|
|
70
|
+
label: 'Datasets',
|
|
71
|
+
description: 'Grouped datasets',
|
|
72
|
+
glyph: '▸',
|
|
73
|
+
ordered: false,
|
|
74
|
+
supersession: false,
|
|
75
|
+
sameVocabulary: false,
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Normalize a group config (which may use the legacy `series: true` flag or
|
|
81
|
+
* the new `kind` discriminator) into a canonical `kind`. Pure function.
|
|
82
|
+
*/
|
|
83
|
+
export function resolveGroupKind(group: { kind?: DatasetGroupKind; series?: boolean }): DatasetGroupKind {
|
|
84
|
+
if (group.kind) return group.kind;
|
|
85
|
+
if (group.series) return 'lineage';
|
|
86
|
+
return 'default';
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Lookup the metadata for any group, using `resolveGroupKind` for compat. */
|
|
90
|
+
export function groupTypeMeta(group: { kind?: DatasetGroupKind; series?: boolean }): GroupTypeMeta {
|
|
91
|
+
return GROUP_TYPES[resolveGroupKind(group)];
|
|
92
|
+
}
|
package/src/config/types.ts
CHANGED
|
@@ -82,6 +82,8 @@ export interface RoutingEntry {
|
|
|
82
82
|
|
|
83
83
|
// === Dataset ===
|
|
84
84
|
|
|
85
|
+
export type DatasetColorSpec = string | { light: string; dark: string };
|
|
86
|
+
|
|
85
87
|
export interface DatasetConfig {
|
|
86
88
|
id: string;
|
|
87
89
|
uri: string;
|
|
@@ -92,7 +94,13 @@ export interface DatasetConfig {
|
|
|
92
94
|
title: string;
|
|
93
95
|
description?: string;
|
|
94
96
|
owner?: string;
|
|
95
|
-
|
|
97
|
+
/**
|
|
98
|
+
* Dataset accent color. Accepts either a single hex (applied to both
|
|
99
|
+
* light and dark modes) or an explicit `{ light, dark }` pair.
|
|
100
|
+
* Per-deployment overrides via `site-config.json` `colors.dataset[id]`
|
|
101
|
+
* take precedence.
|
|
102
|
+
*/
|
|
103
|
+
color?: DatasetColorSpec;
|
|
96
104
|
tags?: string[];
|
|
97
105
|
languageOrder?: string[];
|
|
98
106
|
ref?: string;
|
|
@@ -142,17 +150,86 @@ export interface PageConfig {
|
|
|
142
150
|
|
|
143
151
|
// === Dataset Groups ===
|
|
144
152
|
|
|
153
|
+
/**
|
|
154
|
+
* Kind of dataset group. Determines how the group is rendered in the sidebar
|
|
155
|
+
* and home page, and what semantic relationships between members are assumed.
|
|
156
|
+
*
|
|
157
|
+
* - `lineage` — same vocabulary, different editions (e.g. VIML 1968/2000/2013/2022).
|
|
158
|
+
* Members have temporal ordering and a supersession chain. Rendered as a
|
|
159
|
+
* timeline with year badges and "current" markers.
|
|
160
|
+
*
|
|
161
|
+
* - `topic` — different vocabularies on the same subject (e.g. three SDOs
|
|
162
|
+
* publishing "intelligent transport systems" terminology). Members may
|
|
163
|
+
* overlap in concepts but have no temporal ordering. Rendered as a card
|
|
164
|
+
* grid with overlap indicators.
|
|
165
|
+
*
|
|
166
|
+
* - `family` — related vocabularies from the same publisher or program
|
|
167
|
+
* (e.g. all OIML publications). Hierarchical grouping, no required
|
|
168
|
+
* relationships between members. Rendered as a flat list under a labeled
|
|
169
|
+
* header.
|
|
170
|
+
*
|
|
171
|
+
* - `collection` — curated bundle of datasets (e.g. "Starter pack for new
|
|
172
|
+
* metrologists"). Arbitrary selection, often cross-publisher. Rendered as
|
|
173
|
+
* a featured card with custom descriptions.
|
|
174
|
+
*
|
|
175
|
+
* - `default` (omitted) — backward-compatible flat list. No special
|
|
176
|
+
* semantics. Used when no `kind` is specified.
|
|
177
|
+
*
|
|
178
|
+
* The registry in `src/config/group-types.ts` maps each kind to its renderer
|
|
179
|
+
* component, so new kinds can be added without modifying existing code
|
|
180
|
+
* (open/closed principle).
|
|
181
|
+
*/
|
|
182
|
+
export type DatasetGroupKind = 'lineage' | 'topic' | 'family' | 'collection' | 'default';
|
|
183
|
+
|
|
145
184
|
export interface DatasetGroup {
|
|
146
185
|
id: string;
|
|
147
186
|
label: string;
|
|
148
187
|
description?: string;
|
|
149
|
-
|
|
188
|
+
/**
|
|
189
|
+
* Group accent color. Same shape as DatasetConfig.color.
|
|
190
|
+
* Per-deployment overrides via `site-config.json` `colors.group[id]`.
|
|
191
|
+
*/
|
|
192
|
+
color?: DatasetColorSpec;
|
|
150
193
|
datasets: string[];
|
|
151
194
|
translations?: Record<string, { label?: string; description?: string }>;
|
|
195
|
+
/**
|
|
196
|
+
* Discriminator for the group's semantic type and UX. See DatasetGroupKind
|
|
197
|
+
* for the full list of supported values. Defaults to 'default' (flat list).
|
|
198
|
+
*
|
|
199
|
+
* Replaces the older `series?: boolean` flag — use `kind: lineage` instead.
|
|
200
|
+
*/
|
|
201
|
+
kind?: DatasetGroupKind;
|
|
202
|
+
/**
|
|
203
|
+
* For lineage series: the dataset id of the current (newest valid) edition.
|
|
204
|
+
* If omitted, the newest member by year (or last in `datasets` order) is
|
|
205
|
+
* used. Setting this explicitly avoids misdetection when only a subset of
|
|
206
|
+
* editions happen to be loaded.
|
|
207
|
+
*/
|
|
208
|
+
current?: string;
|
|
209
|
+
/**
|
|
210
|
+
* @deprecated Use `kind: 'lineage'` instead. Still respected as a
|
|
211
|
+
* backward-compat shorthand: `series: true` is treated as `kind: 'lineage'`.
|
|
212
|
+
*/
|
|
213
|
+
series?: boolean;
|
|
152
214
|
}
|
|
153
215
|
|
|
154
216
|
// === Site Config ===
|
|
155
217
|
|
|
218
|
+
export interface SiteColors {
|
|
219
|
+
/** Per-dataset color overrides. Keyed by dataset id. */
|
|
220
|
+
dataset?: Record<string, DatasetColorSpec>;
|
|
221
|
+
/** Per-group color overrides. Keyed by group id. */
|
|
222
|
+
group?: Record<string, DatasetColorSpec>;
|
|
223
|
+
/** Per-relation-type color overrides. Keyed by type id (e.g. "supersedes"). */
|
|
224
|
+
relationshipType?: Record<string, DatasetColorSpec>;
|
|
225
|
+
/** Per-relation-category color overrides. Keyed by category id (e.g. "lifecycle"). */
|
|
226
|
+
relationshipCategory?: Record<string, DatasetColorSpec>;
|
|
227
|
+
/** Per-concept-status color overrides. Keyed by status id. */
|
|
228
|
+
conceptStatus?: Record<string, DatasetColorSpec>;
|
|
229
|
+
/** Per-group-kind color overrides. Keyed by DatasetGroupKind. */
|
|
230
|
+
groupKind?: Record<string, DatasetColorSpec>;
|
|
231
|
+
}
|
|
232
|
+
|
|
156
233
|
export interface SiteConfig {
|
|
157
234
|
id: string;
|
|
158
235
|
domain: string;
|
|
@@ -171,6 +248,8 @@ export interface SiteConfig {
|
|
|
171
248
|
social?: SocialLinks;
|
|
172
249
|
nav?: NavItem[];
|
|
173
250
|
footerNav?: NavItem[];
|
|
251
|
+
/** Color overrides. Merged over `data/colors.json` defaults. */
|
|
252
|
+
colors?: SiteColors;
|
|
174
253
|
defaults: {
|
|
175
254
|
language?: string;
|
|
176
255
|
languageOrder?: string[];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ref, computed } from 'vue';
|
|
2
|
-
import type { PageConfig } from './types';
|
|
2
|
+
import type { PageConfig, SiteColors } from './types';
|
|
3
3
|
import type { DatasetGroup } from './types';
|
|
4
4
|
import { locale } from '../i18n';
|
|
5
5
|
|
|
@@ -38,6 +38,7 @@ export interface RuntimeSiteConfig {
|
|
|
38
38
|
pages?: PageConfig[];
|
|
39
39
|
contributors?: { name: string; role?: string; organization?: string; url?: string; email?: string }[];
|
|
40
40
|
copyright?: string;
|
|
41
|
+
colors?: SiteColors;
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
const siteConfig = ref<RuntimeSiteConfig | null>(null);
|
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
|
+
}
|