@glossarist/concept-browser 0.7.114 → 0.7.116
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/README.md +45 -2
- package/cli/index.mjs +10 -13
- package/cli/index.ts +9 -11
- package/data/concept-model/shapes/glossarist.concept.yaml.schema.json +255 -0
- package/package.json +18 -12
- package/scripts/__tests__/{bibliography.test.mjs → bibliography.test.ts} +1 -1
- package/scripts/__tests__/{cli-script-contract.test.mjs → cli-script-contract.test.ts} +3 -3
- package/scripts/__tests__/{concept-groups.test.mjs → concept-groups.test.ts} +1 -1
- package/scripts/__tests__/{doctor.test.mjs → doctor.test.ts} +1 -1
- package/scripts/__tests__/{fetch-datasets.test.mjs → fetch-datasets.test.ts} +1 -1
- package/scripts/__tests__/{first-non-empty.test.mjs → first-non-empty.test.ts} +1 -1
- package/scripts/__tests__/{process-about-pages.test.mjs → process-about-pages.test.ts} +1 -1
- package/scripts/bridge-to-astro.js +132 -0
- package/scripts/{bridge-to-astro.mjs → bridge-to-astro.ts} +2 -4
- package/scripts/build-edges.js +242 -336
- package/scripts/build-edges.ts +572 -0
- package/scripts/build-scripts.mjs +22 -0
- package/scripts/check-syntax.js +81 -0
- package/scripts/{check-syntax.mjs → check-syntax.ts} +20 -13
- package/scripts/doctor.js +278 -0
- package/scripts/extract-source-refs.js +16 -24
- package/scripts/extract-source-refs.ts +32 -0
- package/scripts/fetch-datasets.js +278 -0
- package/scripts/{fetch-datasets.mjs → fetch-datasets.ts} +2 -2
- package/scripts/generate-404.js +10 -11
- package/scripts/generate-404.ts +15 -0
- package/scripts/generate-data.js +2220 -0
- package/scripts/{generate-data.mjs → generate-data.ts} +73 -460
- package/scripts/generate-ontology-data.js +139 -0
- package/scripts/generate-ontology-schema.js +472 -0
- package/scripts/{generate-ontology-schema.mjs → generate-ontology-schema.ts} +2 -2
- package/scripts/lib/{build-cache.mjs → build-cache.ts} +3 -2
- package/scripts/lib/concept-formats.ts +206 -0
- package/scripts/lib/concept-uri.ts +14 -0
- package/scripts/lib/{dataset-turtle.mjs → dataset-turtle.ts} +4 -3
- package/scripts/lib/harmonize-concept.ts +86 -0
- package/scripts/lib/jsonld-emitter.ts +169 -0
- package/scripts/lib/{local-path-safety.mjs → local-path-safety.ts} +6 -2
- package/scripts/lib/yaml-types.ts +262 -0
- package/scripts/load-site-config.js +83 -0
- package/scripts/migrate-v1-to-v3.js +113 -0
- package/scripts/normalize-yaml.js +96 -0
- package/scripts/process-about-pages.js +241 -0
- package/scripts/{process-about-pages.mjs → process-about-pages.ts} +1 -1
- package/scripts/smoke.js +211 -0
- package/scripts/{smoke.mjs → smoke.ts} +4 -3
- package/scripts/sync-concept-model.js +63 -0
- package/scripts/validate-shacl.js +152 -0
- package/scripts/{validate-shacl.mjs → validate-shacl.ts} +5 -5
- package/scripts/validate-yaml.js +132 -0
- package/scripts/validate-yaml.ts +144 -0
- package/src/__tests__/extract-source-refs.test.ts +2 -3
- package/src/__tests__/perf/serialization-perf.test.ts +5 -5
- package/src/__tests__/published-package-contract.test.ts +84 -0
- package/src/__tests__/scripts/agents-turtle.test.ts +1 -1
- package/src/__tests__/scripts/bibliography-turtle.test.ts +1 -1
- package/src/__tests__/scripts/build-activity-turtle.test.ts +1 -1
- package/src/__tests__/scripts/build-cache.test.ts +1 -1
- package/src/__tests__/scripts/build-integration.test.ts +19 -19
- package/src/__tests__/scripts/dataset-turtle.test.ts +1 -1
- package/src/__tests__/scripts/migrate-v1-to-v3.test.ts +1 -1
- package/src/__tests__/scripts/normalize-yaml.test.ts +1 -1
- package/src/__tests__/scripts/turtle-escape.test.ts +1 -1
- package/src/__tests__/scripts/validate-yaml.test.ts +76 -0
- package/src/__tests__/scripts/version-turtle.test.ts +1 -1
- package/src/__tests__/scripts/vocab-turtle.test.ts +1 -1
- package/src/__tests__/scripts-syntax.test.ts +4 -4
- package/src/__tests__/use-rdf-document.test.ts +2 -3
- package/src/__tests__/utils/content-renderer-xss.test.ts +57 -0
- package/src/__tests__/validate-shacl.test.ts +3 -3
- package/src/adapters/wire/index.ts +69 -0
- package/src/data/ontology-schema.json +1336 -183
- package/src/utils/content-renderer.ts +7 -3
- package/src/utils/url-safety.ts +1 -5
- package/tsconfig.json +2 -2
- package/vite.config.ts +1 -1
- package/src/__tests__/composables/use-concept-edges-v2.test.ts +0 -167
- package/src/__tests__/composables/use-concept-edges-v2.test.ts.bak +0 -166
- package/src/types/agents-version-turtle.d.ts +0 -27
- package/src/types/bibliography-turtle.d.ts +0 -12
- package/src/types/build-activity-turtle.d.ts +0 -16
- package/src/types/build-cache.d.ts +0 -20
- package/src/types/dataset-turtle.d.ts +0 -32
- package/src/types/normalize-yaml.d.ts +0 -16
- package/src/types/turtle-escape.d.ts +0 -6
- package/src/types/vocab-turtle.d.ts +0 -13
- /package/scripts/{doctor.mjs → doctor.ts} +0 -0
- /package/scripts/{generate-ontology-data.mjs → generate-ontology-data.ts} +0 -0
- /package/scripts/lib/{agents-turtle.mjs → agents-turtle.ts} +0 -0
- /package/scripts/lib/{bibliography-turtle.mjs → bibliography-turtle.ts} +0 -0
- /package/scripts/lib/{bibliography.mjs → bibliography.ts} +0 -0
- /package/scripts/lib/build/{image-assets.mjs → image-assets.ts} +0 -0
- /package/scripts/lib/build/{non-verbal-consumer.mjs → non-verbal-consumer.ts} +0 -0
- /package/scripts/lib/{build-activity-turtle.mjs → build-activity-turtle.ts} +0 -0
- /package/scripts/lib/{concept-groups.mjs → concept-groups.ts} +0 -0
- /package/scripts/lib/{first-non-empty.mjs → first-non-empty.ts} +0 -0
- /package/scripts/lib/{turtle-escape.mjs → turtle-escape.ts} +0 -0
- /package/scripts/lib/{version-turtle.mjs → version-turtle.ts} +0 -0
- /package/scripts/lib/{vocab-turtle.mjs → vocab-turtle.ts} +0 -0
- /package/scripts/{load-site-config.mjs → load-site-config.ts} +0 -0
- /package/scripts/{migrate-v1-to-v3.mjs → migrate-v1-to-v3.ts} +0 -0
- /package/scripts/{normalize-yaml.mjs → normalize-yaml.ts} +0 -0
- /package/scripts/{sync-concept-model.mjs → sync-concept-model.ts} +0 -0
package/scripts/build-edges.js
CHANGED
|
@@ -1,118 +1,106 @@
|
|
|
1
|
+
// scripts/lib/concept-uri.ts
|
|
1
2
|
function buildConceptUri(uriBase, registerId, conceptId) {
|
|
2
3
|
return `${uriBase}/${registerId}/concept/${conceptId}`;
|
|
3
4
|
}
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
// scripts/extract-source-refs.js
|
|
7
|
+
function extractSourceRefs(concept, registerId) {
|
|
8
|
+
const refs = /* @__PURE__ */ new Set();
|
|
9
|
+
for (const src of concept["gl:source"] || []) {
|
|
10
|
+
const origin = src["gl:origin"];
|
|
11
|
+
if (origin) {
|
|
12
|
+
const ref = origin["gl:ref"];
|
|
13
|
+
if (ref?.["gl:source"]) refs.add(ref["gl:source"]);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
for (const lc of Object.values(concept["gl:localizedConcept"] || {})) {
|
|
17
|
+
for (const src of lc["gl:source"] || []) {
|
|
18
|
+
const origin = src["gl:origin"];
|
|
19
|
+
if (origin) {
|
|
20
|
+
const ref = origin["gl:ref"];
|
|
21
|
+
if (ref?.["gl:source"]) refs.add(ref["gl:source"]);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return [...refs].map((source) => ({ source, registerId }));
|
|
26
|
+
}
|
|
6
27
|
|
|
28
|
+
// scripts/build-edges.ts
|
|
29
|
+
import { readFileSync, writeFileSync, readdirSync, existsSync, realpathSync } from "fs";
|
|
30
|
+
import { join, dirname } from "path";
|
|
31
|
+
import { fileURLToPath } from "url";
|
|
32
|
+
var VALID_COMPLETENESS = /* @__PURE__ */ new Set(["complete", "partial"]);
|
|
7
33
|
function validateCompleteness(value) {
|
|
8
|
-
if (value == null) return
|
|
34
|
+
if (value == null) return "complete";
|
|
9
35
|
if (!VALID_COMPLETENESS.has(value)) {
|
|
10
36
|
throw new Error(
|
|
11
|
-
`Invalid partitive relation completeness: "${value}". Allowed: complete, partial
|
|
37
|
+
`Invalid partitive relation completeness: "${value}". Allowed: complete, partial`
|
|
12
38
|
);
|
|
13
39
|
}
|
|
14
40
|
return value;
|
|
15
41
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
* confirmed → compulsory
|
|
25
|
-
* possible → optional
|
|
26
|
-
*/
|
|
27
|
-
const VALID_PRESENCE = new Set(['required', 'optional']);
|
|
28
|
-
const VALID_COUNT = new Set(['exactly_one', 'at_least_one', 'multiple']);
|
|
29
|
-
const LEGACY_MULTIPLICITY_MAP = {
|
|
30
|
-
compulsory: { presence: 'required', count: 'exactly_one' },
|
|
31
|
-
optional: { presence: 'optional', count: 'exactly_one' },
|
|
32
|
-
compulsory_multiple: { presence: 'required', count: 'multiple' },
|
|
33
|
-
optional_multiple: { presence: 'optional', count: 'multiple' },
|
|
34
|
-
compulsory_at_least_one: { presence: 'required', count: 'at_least_one' },
|
|
42
|
+
var VALID_PRESENCE = /* @__PURE__ */ new Set(["required", "optional"]);
|
|
43
|
+
var VALID_COUNT = /* @__PURE__ */ new Set(["exactly_one", "at_least_one", "multiple"]);
|
|
44
|
+
var LEGACY_MULTIPLICITY_MAP = {
|
|
45
|
+
compulsory: { presence: "required", count: "exactly_one" },
|
|
46
|
+
optional: { presence: "optional", count: "exactly_one" },
|
|
47
|
+
compulsory_multiple: { presence: "required", count: "multiple" },
|
|
48
|
+
optional_multiple: { presence: "optional", count: "multiple" },
|
|
49
|
+
compulsory_at_least_one: { presence: "required", count: "at_least_one" }
|
|
35
50
|
};
|
|
36
|
-
|
|
37
51
|
function resolvePresence(member) {
|
|
38
|
-
const p = member[
|
|
52
|
+
const p = member["gl:presence"] ?? member.presence;
|
|
39
53
|
if (p != null) {
|
|
40
54
|
if (!VALID_PRESENCE.has(p)) throw new Error(`Invalid presence: "${p}"`);
|
|
41
55
|
return p;
|
|
42
56
|
}
|
|
43
57
|
return migrateMultiplicity(member).presence;
|
|
44
58
|
}
|
|
45
|
-
|
|
46
59
|
function resolveCount(member) {
|
|
47
|
-
const c = member[
|
|
60
|
+
const c = member["gl:count"] ?? member.count;
|
|
48
61
|
if (c != null) {
|
|
49
62
|
if (!VALID_COUNT.has(c)) throw new Error(`Invalid count: "${c}"`);
|
|
50
63
|
return c;
|
|
51
64
|
}
|
|
52
65
|
return migrateMultiplicity(member).count;
|
|
53
66
|
}
|
|
54
|
-
|
|
55
67
|
function migrateMultiplicity(member) {
|
|
56
|
-
const m = member[
|
|
68
|
+
const m = member["gl:multiplicity"] ?? member.multiplicity;
|
|
57
69
|
if (m && LEGACY_MULTIPLICITY_MAP[m]) return LEGACY_MULTIPLICITY_MAP[m];
|
|
58
|
-
const certainty = member[
|
|
59
|
-
if (certainty ===
|
|
60
|
-
return { presence:
|
|
70
|
+
const certainty = member["gl:certainty"] ?? member.certainty;
|
|
71
|
+
if (certainty === "possible") return { presence: "optional", count: "exactly_one" };
|
|
72
|
+
return { presence: "required", count: "exactly_one" };
|
|
61
73
|
}
|
|
62
|
-
|
|
63
74
|
function resolveIsDelimiting(member) {
|
|
64
|
-
return member[
|
|
75
|
+
return member["gl:isDelimiting"] === true || member.isDelimiting === true;
|
|
65
76
|
}
|
|
66
|
-
|
|
67
77
|
function normalizeLocalizedString(content) {
|
|
68
|
-
if (content == null) return
|
|
69
|
-
if (typeof content ===
|
|
78
|
+
if (content == null) return void 0;
|
|
79
|
+
if (typeof content === "string") return { default: content };
|
|
70
80
|
return content;
|
|
71
81
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
* Pre-computes cross-reference and domain edges for each dataset.
|
|
76
|
-
* Reads all concept JSON files, extracts structured references and
|
|
77
|
-
* authoritative sources (domains), and writes edges.json + domain-nodes.json.
|
|
78
|
-
*
|
|
79
|
-
* Usage: node scripts/build-edges.js
|
|
80
|
-
*/
|
|
81
|
-
import { extractSourceRefs } from './extract-source-refs.js';
|
|
82
|
-
import { readFileSync, writeFileSync, readdirSync, existsSync, realpathSync } from 'fs';
|
|
83
|
-
import { join, dirname, resolve } from 'path';
|
|
84
|
-
import { fileURLToPath } from 'url';
|
|
85
|
-
import yaml from 'js-yaml';
|
|
86
|
-
import { Register } from 'glossarist';
|
|
87
|
-
|
|
88
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
89
|
-
const ROOT = process.cwd();
|
|
90
|
-
const DATA_DIR = join(ROOT, 'public', 'data');
|
|
91
|
-
|
|
92
|
-
// --- Normalization ---
|
|
93
|
-
|
|
82
|
+
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
83
|
+
var ROOT = process.cwd();
|
|
84
|
+
var DATA_DIR = join(ROOT, "public", "data");
|
|
94
85
|
function slugify(text) {
|
|
95
|
-
return text.toLowerCase().replace(/[^\w\s-]/g,
|
|
86
|
+
return text.toLowerCase().replace(/[^\w\s-]/g, "").replace(/[\s/]+/g, "-");
|
|
96
87
|
}
|
|
97
|
-
|
|
98
|
-
// --- Extractors (open/closed: add new extractors to EXTRACTORS array) ---
|
|
99
|
-
|
|
100
88
|
function extractReferences(concept, registerId) {
|
|
101
89
|
const edges = [];
|
|
102
|
-
const sourceUri = concept[
|
|
103
|
-
for (const [lang, lc] of Object.entries(concept[
|
|
104
|
-
if (lc[
|
|
105
|
-
for (const ref of lc[
|
|
106
|
-
if (ref[
|
|
90
|
+
const sourceUri = concept["@id"];
|
|
91
|
+
for (const [lang, lc] of Object.entries(concept["gl:localizedConcept"] || {})) {
|
|
92
|
+
if (lc["gl:references"]) {
|
|
93
|
+
for (const ref of lc["gl:references"]) {
|
|
94
|
+
if (ref["@id"] && ref["@id"] !== sourceUri) {
|
|
107
95
|
const edge = {
|
|
108
96
|
source: sourceUri,
|
|
109
|
-
target: ref[
|
|
110
|
-
type: ref[
|
|
111
|
-
label: ref[
|
|
97
|
+
target: ref["@id"],
|
|
98
|
+
type: ref["@id"].startsWith("cite:") ? "citation" : "references",
|
|
99
|
+
label: ref["gl:term"] || void 0,
|
|
112
100
|
register: registerId,
|
|
113
|
-
lang
|
|
101
|
+
lang
|
|
114
102
|
};
|
|
115
|
-
if (ref[
|
|
103
|
+
if (ref["gl:sourceId"]) edge.sourceId = ref["gl:sourceId"];
|
|
116
104
|
edges.push(edge);
|
|
117
105
|
}
|
|
118
106
|
}
|
|
@@ -120,23 +108,20 @@ function extractReferences(concept, registerId) {
|
|
|
120
108
|
}
|
|
121
109
|
return edges;
|
|
122
110
|
}
|
|
123
|
-
|
|
124
111
|
function extractDomains(concept, registerId, uriBase) {
|
|
125
|
-
if (!uriBase) throw new Error(
|
|
112
|
+
if (!uriBase) throw new Error("build-edges: uriBase is required \u2014 set uriBase in site-config.yml");
|
|
126
113
|
const base = uriBase;
|
|
127
114
|
const edges = [];
|
|
128
|
-
const sourceUri = concept[
|
|
129
|
-
const seen = new Set();
|
|
130
|
-
|
|
131
|
-
// v3 managed concept-level section/domain references
|
|
132
|
-
const domains = concept['gl:domain'];
|
|
115
|
+
const sourceUri = concept["@id"];
|
|
116
|
+
const seen = /* @__PURE__ */ new Set();
|
|
117
|
+
const domains = concept["gl:domain"];
|
|
133
118
|
if (Array.isArray(domains)) {
|
|
134
119
|
for (const d of domains) {
|
|
135
|
-
const conceptId = d[
|
|
120
|
+
const conceptId = d["gl:conceptId"] || d.concept_id;
|
|
136
121
|
if (conceptId) {
|
|
137
|
-
const refType = d[
|
|
138
|
-
const isSection = refType ===
|
|
139
|
-
const edgeType = isSection ?
|
|
122
|
+
const refType = d["gl:refType"] || d.ref_type || "domain";
|
|
123
|
+
const isSection = refType === "section";
|
|
124
|
+
const edgeType = isSection ? "section" : "domain";
|
|
140
125
|
const domainUri = `${base}/${registerId}/domain/${conceptId}`;
|
|
141
126
|
if (!seen.has(domainUri)) {
|
|
142
127
|
seen.add(domainUri);
|
|
@@ -145,40 +130,37 @@ function extractDomains(concept, registerId, uriBase) {
|
|
|
145
130
|
target: domainUri,
|
|
146
131
|
type: edgeType,
|
|
147
132
|
label: conceptId,
|
|
148
|
-
register: registerId
|
|
133
|
+
register: registerId
|
|
149
134
|
});
|
|
150
135
|
}
|
|
151
136
|
}
|
|
152
137
|
}
|
|
153
138
|
}
|
|
154
|
-
|
|
155
|
-
// Legacy: localized domain strings
|
|
156
|
-
const lcs = concept['gl:localizedConcept'] || {};
|
|
139
|
+
const lcs = concept["gl:localizedConcept"] || {};
|
|
157
140
|
const langs = Object.keys(lcs);
|
|
158
141
|
for (const lang of langs) {
|
|
159
|
-
const domain = lcs[lang][
|
|
142
|
+
const domain = lcs[lang]["gl:domain"];
|
|
160
143
|
if (domain && !seen.has(domain)) {
|
|
161
144
|
seen.add(domain);
|
|
162
145
|
edges.push({
|
|
163
146
|
source: sourceUri,
|
|
164
147
|
target: `${base}/${registerId}/domain/${slugify(domain)}`,
|
|
165
|
-
type:
|
|
148
|
+
type: "domain",
|
|
166
149
|
label: domain,
|
|
167
|
-
register: registerId
|
|
150
|
+
register: registerId
|
|
168
151
|
});
|
|
169
152
|
}
|
|
170
153
|
}
|
|
171
154
|
return edges;
|
|
172
155
|
}
|
|
173
|
-
|
|
174
156
|
function extractRelated(concept, registerId, uriBase, urnMap) {
|
|
175
157
|
const edges = [];
|
|
176
|
-
const sourceUri = concept[
|
|
177
|
-
for (const r of concept[
|
|
178
|
-
const ref = r[
|
|
158
|
+
const sourceUri = concept["@id"];
|
|
159
|
+
for (const r of concept["gl:related"] || []) {
|
|
160
|
+
const ref = r["gl:ref"];
|
|
179
161
|
if (!ref) continue;
|
|
180
|
-
const source = ref[
|
|
181
|
-
const id = ref[
|
|
162
|
+
const source = ref["gl:source"] || ref["source"];
|
|
163
|
+
const id = ref["gl:id"] || ref["id"];
|
|
182
164
|
if (!source || !id) continue;
|
|
183
165
|
const reg = urnMap.get(source) || source;
|
|
184
166
|
const target = buildConceptUri(uriBase, reg, id);
|
|
@@ -186,90 +168,59 @@ function extractRelated(concept, registerId, uriBase, urnMap) {
|
|
|
186
168
|
edges.push({
|
|
187
169
|
source: sourceUri,
|
|
188
170
|
target,
|
|
189
|
-
type: r[
|
|
190
|
-
label: r[
|
|
191
|
-
register: reg
|
|
171
|
+
type: r["gl:relationshipType"] || "references",
|
|
172
|
+
label: r["gl:term"] || void 0,
|
|
173
|
+
register: reg
|
|
192
174
|
});
|
|
193
175
|
}
|
|
194
176
|
return edges;
|
|
195
177
|
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Extracts partitive relations from a concept's gl:partitiveRelations
|
|
199
|
-
* array. Returns relation objects (NOT binary GraphEdges) so the UI
|
|
200
|
-
* can render them with their full set-based semantics: comprehensive +
|
|
201
|
-
* partitives (each with certainty), completeness, plurality, and
|
|
202
|
-
* criterion.
|
|
203
|
-
*
|
|
204
|
-
* Relations are written to partitive_relations.json, keeping
|
|
205
|
-
* edges.json binary-only.
|
|
206
|
-
*
|
|
207
|
-
* v2 shape per concept-model/TODO.partitive-relation-v2.
|
|
208
|
-
*/
|
|
209
178
|
function extractPartitiveRelations(concept, registerId, uriBase, urnMap) {
|
|
210
179
|
const relations = [];
|
|
211
|
-
const sourceUri = concept[
|
|
212
|
-
for (const rel of concept[
|
|
213
|
-
const compRef = rel[
|
|
180
|
+
const sourceUri = concept["@id"];
|
|
181
|
+
for (const rel of concept["gl:partitiveRelations"] || []) {
|
|
182
|
+
const compRef = rel["gl:comprehensive"];
|
|
214
183
|
if (!compRef) continue;
|
|
215
184
|
const comprehensive = resolveConceptUri(compRef, uriBase, urnMap);
|
|
216
185
|
if (!comprehensive) continue;
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
};
|
|
229
|
-
})
|
|
230
|
-
.filter(p => p !== null);
|
|
231
|
-
|
|
186
|
+
const partitives = (rel["gl:hasPartitive"] || []).map((p) => {
|
|
187
|
+
const memberRef = p["gl:ref"] || p;
|
|
188
|
+
const uri = resolveConceptUri(memberRef, uriBase, urnMap);
|
|
189
|
+
if (!uri || uri === sourceUri) return null;
|
|
190
|
+
return {
|
|
191
|
+
uri,
|
|
192
|
+
presence: resolvePresence(p),
|
|
193
|
+
count: resolveCount(p),
|
|
194
|
+
isDelimiting: resolveIsDelimiting(p)
|
|
195
|
+
};
|
|
196
|
+
}).filter((p) => p !== null);
|
|
232
197
|
if (partitives.length === 0) continue;
|
|
233
|
-
|
|
234
198
|
relations.push({
|
|
235
199
|
source: sourceUri,
|
|
236
200
|
comprehensive,
|
|
237
201
|
partitives,
|
|
238
|
-
completeness: validateCompleteness(rel[
|
|
239
|
-
criterion: normalizeLocalizedString(rel[
|
|
240
|
-
register: registerId
|
|
202
|
+
completeness: validateCompleteness(rel["gl:completeness"]),
|
|
203
|
+
criterion: normalizeLocalizedString(rel["gl:criterion"]),
|
|
204
|
+
register: registerId
|
|
241
205
|
});
|
|
242
206
|
}
|
|
243
207
|
return relations;
|
|
244
208
|
}
|
|
245
|
-
|
|
246
209
|
function resolveConceptUri(ref, uriBase, urnMap) {
|
|
247
|
-
const source = ref[
|
|
248
|
-
const id = ref[
|
|
210
|
+
const source = ref["gl:source"] || ref["source"];
|
|
211
|
+
const id = ref["gl:id"] || ref["id"];
|
|
249
212
|
if (!source || !id) return null;
|
|
250
213
|
const reg = urnMap.get(source) || source;
|
|
251
214
|
return buildConceptUri(uriBase, reg, id);
|
|
252
215
|
}
|
|
253
|
-
|
|
254
|
-
const EXTRACTORS = [extractReferences, extractRelated, extractDomains];
|
|
255
|
-
|
|
216
|
+
var EXTRACTORS = [extractReferences, extractRelated, extractDomains];
|
|
256
217
|
function extractAllEdges(concept, registerId, uriBase, urnMap) {
|
|
257
|
-
return EXTRACTORS.flatMap(fn => fn(concept, registerId, uriBase, urnMap));
|
|
218
|
+
return EXTRACTORS.flatMap((fn) => fn(concept, registerId, uriBase, urnMap));
|
|
258
219
|
}
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* Per concept-model TODO.partitive-relation-v2 item 14:
|
|
262
|
-
* a PartitiveRelation A → {b, c, d} subsumes binary has_part edges
|
|
263
|
-
* for the same pairs. Warn (don't fail) when both encodings exist.
|
|
264
|
-
*
|
|
265
|
-
* Binary edge types considered partitive: has_part, is_part_of,
|
|
266
|
-
* broader_partitive, narrower_partitive.
|
|
267
|
-
*/
|
|
268
|
-
const BINARY_PARTITIVE_TYPES = new Set(['has_part', 'is_part_of', 'broader_partitive', 'narrower_partitive']);
|
|
269
|
-
|
|
220
|
+
var BINARY_PARTITIVE_TYPES = /* @__PURE__ */ new Set(["has_part", "is_part_of", "broader_partitive", "narrower_partitive"]);
|
|
270
221
|
function warnBinaryPartitiveRedundancy(edges, relations, registerId) {
|
|
271
222
|
if (!relations?.length) return;
|
|
272
|
-
const partitivePairs = new Set();
|
|
223
|
+
const partitivePairs = /* @__PURE__ */ new Set();
|
|
273
224
|
for (const rel of relations) {
|
|
274
225
|
for (const member of rel.partitives ?? []) {
|
|
275
226
|
const memberUri = member.uri ?? member;
|
|
@@ -277,47 +228,39 @@ function warnBinaryPartitiveRedundancy(edges, relations, registerId) {
|
|
|
277
228
|
partitivePairs.add(`${memberUri}|${rel.comprehensive}`);
|
|
278
229
|
}
|
|
279
230
|
}
|
|
280
|
-
const redundant = edges.filter(
|
|
281
|
-
BINARY_PARTITIVE_TYPES.has(e.type)
|
|
282
|
-
&& partitivePairs.has(`${e.source}|${e.target}`),
|
|
231
|
+
const redundant = edges.filter(
|
|
232
|
+
(e) => BINARY_PARTITIVE_TYPES.has(e.type) && partitivePairs.has(`${e.source}|${e.target}`)
|
|
283
233
|
);
|
|
284
234
|
if (redundant.length > 0) {
|
|
285
235
|
console.warn(
|
|
286
|
-
`
|
|
236
|
+
` \u26A0 ${registerId}: ${redundant.length} binary has_part/is_part_of edge(s) duplicate a PartitiveRelation member \u2014 prefer the PartitiveRelation encoding (concept-model TODO item 14).`
|
|
287
237
|
);
|
|
288
238
|
}
|
|
289
239
|
}
|
|
290
|
-
|
|
291
|
-
// --- Build ---
|
|
292
|
-
|
|
293
240
|
function buildEdgesForDataset(datasetDir, registerId, uriBase, urnMap, manifest) {
|
|
294
|
-
const conceptsDir = join(datasetDir,
|
|
241
|
+
const conceptsDir = join(datasetDir, "concepts");
|
|
295
242
|
if (!existsSync(conceptsDir)) {
|
|
296
243
|
console.log(` Skipping ${registerId}: no concepts directory`);
|
|
297
244
|
return { edges: [], sourceRefs: [] };
|
|
298
245
|
}
|
|
299
|
-
|
|
300
|
-
const files = readdirSync(conceptsDir).filter(f => f.endsWith('.json'));
|
|
246
|
+
const files = readdirSync(conceptsDir).filter((f) => f.endsWith(".json"));
|
|
301
247
|
console.log(` Processing ${files.length} concepts...`);
|
|
302
|
-
|
|
303
248
|
const allEdges = [];
|
|
304
249
|
const allPartitiveRelations = [];
|
|
305
250
|
const allSourceRefs = [];
|
|
306
|
-
const domainConceptCount = new Map();
|
|
251
|
+
const domainConceptCount = /* @__PURE__ */ new Map();
|
|
307
252
|
let processed = 0;
|
|
308
|
-
|
|
309
253
|
for (const file of files) {
|
|
310
254
|
try {
|
|
311
|
-
const data = JSON.parse(readFileSync(join(conceptsDir, file),
|
|
255
|
+
const data = JSON.parse(readFileSync(join(conceptsDir, file), "utf-8"));
|
|
312
256
|
const edges = extractAllEdges(data, registerId, uriBase, urnMap);
|
|
313
257
|
const relations = extractPartitiveRelations(data, registerId, uriBase, urnMap);
|
|
314
258
|
warnBinaryPartitiveRedundancy(edges, relations, registerId);
|
|
315
259
|
allEdges.push(...edges);
|
|
316
260
|
allPartitiveRelations.push(...relations);
|
|
317
261
|
allSourceRefs.push(...extractSourceRefs(data, registerId));
|
|
318
|
-
|
|
319
262
|
for (const edge of edges) {
|
|
320
|
-
if (edge.type ===
|
|
263
|
+
if (edge.type === "domain" || edge.type === "section") {
|
|
321
264
|
domainConceptCount.set(edge.target, (domainConceptCount.get(edge.target) || 0) + 1);
|
|
322
265
|
}
|
|
323
266
|
}
|
|
@@ -325,52 +268,40 @@ function buildEdgesForDataset(datasetDir, registerId, uriBase, urnMap, manifest)
|
|
|
325
268
|
console.error(` Error processing ${file}: ${e.message}`);
|
|
326
269
|
}
|
|
327
270
|
processed++;
|
|
328
|
-
if (processed %
|
|
271
|
+
if (processed % 5e3 === 0) {
|
|
329
272
|
console.log(` ... ${processed}/${files.length}`);
|
|
330
273
|
}
|
|
331
274
|
}
|
|
332
|
-
|
|
333
|
-
// Deduplicate edges by source+target+type+lang
|
|
334
|
-
const seen = new Set();
|
|
275
|
+
const seen = /* @__PURE__ */ new Set();
|
|
335
276
|
const deduped = [];
|
|
336
277
|
for (const edge of allEdges) {
|
|
337
|
-
const key = `${edge.source}
|
|
278
|
+
const key = `${edge.source}\u2192${edge.target}\u2192${edge.type}\u2192${edge.lang || ""}`;
|
|
338
279
|
if (!seen.has(key)) {
|
|
339
280
|
seen.add(key);
|
|
340
281
|
deduped.push(edge);
|
|
341
282
|
}
|
|
342
283
|
}
|
|
343
|
-
|
|
344
284
|
const output = {
|
|
345
285
|
registerId,
|
|
346
286
|
edgeCount: deduped.length,
|
|
347
|
-
edges: deduped
|
|
287
|
+
edges: deduped
|
|
348
288
|
};
|
|
349
|
-
|
|
350
|
-
const outputPath = join(datasetDir, 'edges.json');
|
|
289
|
+
const outputPath = join(datasetDir, "edges.json");
|
|
351
290
|
writeFileSync(outputPath, JSON.stringify(output));
|
|
352
291
|
console.log(` Written ${deduped.length} edges to edges.json (${(JSON.stringify(output).length / 1024).toFixed(1)} KB)`);
|
|
353
|
-
|
|
354
|
-
// Write partitive_relations.json (parallel to edges.json) for one-to-many
|
|
355
|
-
// partitive decompositions. See TODO.partitive-relation-v2.
|
|
356
292
|
const relationsOutput = {
|
|
357
293
|
registerId,
|
|
358
294
|
relationCount: allPartitiveRelations.length,
|
|
359
|
-
relations: allPartitiveRelations
|
|
295
|
+
relations: allPartitiveRelations
|
|
360
296
|
};
|
|
361
|
-
const relationsPath = join(datasetDir,
|
|
297
|
+
const relationsPath = join(datasetDir, "partitive_relations.json");
|
|
362
298
|
writeFileSync(relationsPath, JSON.stringify(relationsOutput));
|
|
363
299
|
console.log(` Written ${allPartitiveRelations.length} partitive relations to partitive_relations.json (${(JSON.stringify(relationsOutput).length / 1024).toFixed(1)} KB)`);
|
|
364
|
-
|
|
365
|
-
// Build domain-nodes.json from manifest sections (authoritative source)
|
|
366
300
|
const manifestSections = manifest.sections;
|
|
367
301
|
if (manifestSections && manifestSections.length > 0) {
|
|
368
|
-
|
|
369
|
-
if (!uriBase) throw new Error('build-edges: manifest.uriBase is required');
|
|
370
|
-
|
|
371
|
-
function buildSectionNode(section, idx) {
|
|
302
|
+
let buildSectionNode2 = function(section, idx) {
|
|
372
303
|
const sectionId = `section-${section.id}`;
|
|
373
|
-
const domainUri = `${
|
|
304
|
+
const domainUri = `${uriBase2}/${registerId}/domain/${sectionId}`;
|
|
374
305
|
const domainLabel = section.names?.eng || section.id;
|
|
375
306
|
const node = {
|
|
376
307
|
uri: domainUri,
|
|
@@ -379,196 +310,171 @@ function buildEdgesForDataset(datasetDir, registerId, uriBase, urnMap, manifest)
|
|
|
379
310
|
label: domainLabel,
|
|
380
311
|
registerId,
|
|
381
312
|
conceptCount: domainConceptCount.get(domainUri) || 0,
|
|
382
|
-
order: idx
|
|
313
|
+
order: idx
|
|
383
314
|
};
|
|
384
315
|
if (section.children && section.children.length > 0) {
|
|
385
|
-
node.children = section.children.map(
|
|
386
|
-
|
|
316
|
+
node.children = section.children.map(
|
|
317
|
+
(child, childIdx) => buildSectionNode2(child, childIdx)
|
|
387
318
|
);
|
|
388
319
|
}
|
|
389
320
|
return node;
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
const
|
|
393
|
-
|
|
321
|
+
};
|
|
322
|
+
var buildSectionNode = buildSectionNode2;
|
|
323
|
+
const uriBase2 = manifest.uriBase;
|
|
324
|
+
if (!uriBase2) throw new Error("build-edges: manifest.uriBase is required");
|
|
325
|
+
const domainNodes = manifestSections.map(
|
|
326
|
+
(section, idx) => buildSectionNode2(section, idx)
|
|
394
327
|
);
|
|
395
|
-
|
|
396
328
|
const domainOutput = { registerId, domainNodes };
|
|
397
|
-
const domainPath = join(datasetDir,
|
|
329
|
+
const domainPath = join(datasetDir, "domain-nodes.json");
|
|
398
330
|
writeFileSync(domainPath, JSON.stringify(domainOutput));
|
|
399
331
|
console.log(` Written ${domainNodes.length} section-based domain nodes to domain-nodes.json`);
|
|
400
332
|
} else {
|
|
401
|
-
|
|
402
|
-
const domainEdgeMap = new Map();
|
|
333
|
+
const domainEdgeMap = /* @__PURE__ */ new Map();
|
|
403
334
|
for (const edge of deduped) {
|
|
404
|
-
if (edge.type ===
|
|
335
|
+
if (edge.type === "domain") {
|
|
405
336
|
const existing = domainEdgeMap.get(edge.target);
|
|
406
337
|
if (existing) {
|
|
407
338
|
existing.labels.add(edge.label);
|
|
408
339
|
} else {
|
|
409
|
-
domainEdgeMap.set(edge.target, { uri: edge.target, labels: new Set([edge.label]), registerId });
|
|
340
|
+
domainEdgeMap.set(edge.target, { uri: edge.target, labels: /* @__PURE__ */ new Set([edge.label]), registerId });
|
|
410
341
|
}
|
|
411
342
|
}
|
|
412
343
|
}
|
|
413
|
-
|
|
414
|
-
const domainNodes = [...domainEdgeMap.values()].map(d => ({
|
|
344
|
+
const domainNodes = [...domainEdgeMap.values()].map((d) => ({
|
|
415
345
|
uri: d.uri,
|
|
416
346
|
label: [...d.labels][0],
|
|
417
347
|
registerId: d.registerId,
|
|
418
|
-
conceptCount: domainConceptCount.get(d.uri) || 0
|
|
348
|
+
conceptCount: domainConceptCount.get(d.uri) || 0
|
|
419
349
|
})).sort((a, b) => b.conceptCount - a.conceptCount);
|
|
420
|
-
|
|
421
350
|
const domainOutput = { registerId, domainNodes };
|
|
422
|
-
const domainPath = join(datasetDir,
|
|
351
|
+
const domainPath = join(datasetDir, "domain-nodes.json");
|
|
423
352
|
writeFileSync(domainPath, JSON.stringify(domainOutput));
|
|
424
353
|
console.log(` Written ${domainNodes.length} edge-derived domain nodes to domain-nodes.json`);
|
|
425
354
|
}
|
|
426
|
-
|
|
427
355
|
return { edges: deduped, sourceRefs: allSourceRefs };
|
|
428
356
|
}
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
console.log(
|
|
433
|
-
|
|
434
|
-
if (!existsSync(DATA_DIR)) {
|
|
435
|
-
console.log('No data directory found. Nothing to do.');
|
|
436
|
-
process.exit(0);
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
const datasets = readdirSync(DATA_DIR).filter(f => {
|
|
440
|
-
try {
|
|
441
|
-
return existsSync(join(DATA_DIR, f, 'manifest.json'));
|
|
442
|
-
} catch {
|
|
443
|
-
return false;
|
|
357
|
+
function main() {
|
|
358
|
+
console.log("Building edge indexes...\n");
|
|
359
|
+
if (!existsSync(DATA_DIR)) {
|
|
360
|
+
console.log("No data directory found. Nothing to do.");
|
|
361
|
+
process.exit(0);
|
|
444
362
|
}
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
363
|
+
const datasets = readdirSync(DATA_DIR).filter((f) => {
|
|
364
|
+
try {
|
|
365
|
+
return existsSync(join(DATA_DIR, f, "manifest.json"));
|
|
366
|
+
} catch {
|
|
367
|
+
return false;
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
const urnMap = /* @__PURE__ */ new Map();
|
|
371
|
+
const manifestCache = /* @__PURE__ */ new Map();
|
|
372
|
+
for (const ds of datasets) {
|
|
373
|
+
const manifestPath = join(DATA_DIR, ds, "manifest.json");
|
|
374
|
+
try {
|
|
375
|
+
const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
|
|
376
|
+
manifestCache.set(ds, manifest);
|
|
377
|
+
if (manifest.datasetUri) {
|
|
378
|
+
const base = manifest.datasetUri.endsWith("*") ? manifest.datasetUri.slice(0, -1) : manifest.datasetUri;
|
|
379
|
+
if (base) urnMap.set(base, ds);
|
|
380
|
+
}
|
|
381
|
+
for (const alias of manifest.uriAliases ?? []) {
|
|
382
|
+
const base = alias.endsWith("*") ? alias.slice(0, -1) : alias;
|
|
383
|
+
if (base) urnMap.set(base, ds);
|
|
384
|
+
}
|
|
385
|
+
} catch {
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
console.log(`URI resolution map: ${[...urnMap.entries()].map(([k, v]) => `${k}\u2192${v}`).join(", ")}
|
|
389
|
+
`);
|
|
390
|
+
const allDatasetEdges = /* @__PURE__ */ new Map();
|
|
391
|
+
const allSourceRefs = [];
|
|
392
|
+
for (const ds of datasets) {
|
|
393
|
+
const manifest = manifestCache.get(ds);
|
|
394
|
+
if (!manifest) continue;
|
|
395
|
+
try {
|
|
396
|
+
console.log(`${manifest.title} (${ds}):`);
|
|
397
|
+
const uriBase = manifest.uriBase;
|
|
398
|
+
if (!uriBase) throw new Error("build-edges: manifest.uriBase is required");
|
|
399
|
+
const result = buildEdgesForDataset(join(DATA_DIR, ds), ds, uriBase, urnMap, manifest);
|
|
400
|
+
allDatasetEdges.set(ds, result.edges);
|
|
401
|
+
allSourceRefs.push(...result.sourceRefs);
|
|
402
|
+
} catch (e) {
|
|
403
|
+
console.error(`Error reading manifest for ${ds}: ${e.message}`);
|
|
404
|
+
}
|
|
405
|
+
console.log();
|
|
406
|
+
}
|
|
407
|
+
const knownSourceStrings = /* @__PURE__ */ new Set();
|
|
408
|
+
for (const [ds, manifest] of manifestCache) {
|
|
409
|
+
if (manifest.ref) knownSourceStrings.add(manifest.ref);
|
|
410
|
+
for (const alias of manifest.refAliases ?? []) {
|
|
411
|
+
knownSourceStrings.add(alias);
|
|
412
|
+
}
|
|
455
413
|
if (manifest.datasetUri) {
|
|
456
|
-
const base = manifest.datasetUri.endsWith(
|
|
457
|
-
|
|
414
|
+
const base = manifest.datasetUri.endsWith("*") ? manifest.datasetUri.slice(0, -1) : manifest.datasetUri;
|
|
415
|
+
knownSourceStrings.add(base);
|
|
458
416
|
}
|
|
459
417
|
for (const alias of manifest.uriAliases ?? []) {
|
|
460
|
-
const base = alias.endsWith(
|
|
461
|
-
|
|
418
|
+
const base = alias.endsWith("*") ? alias.slice(0, -1) : alias;
|
|
419
|
+
knownSourceStrings.add(base);
|
|
462
420
|
}
|
|
463
|
-
} catch {}
|
|
464
|
-
}
|
|
465
|
-
console.log(`URI resolution map: ${[...urnMap.entries()].map(([k,v]) => `${k}→${v}`).join(', ')}\n`);
|
|
466
|
-
|
|
467
|
-
const allDatasetEdges = new Map();
|
|
468
|
-
const allSourceRefs = [];
|
|
469
|
-
|
|
470
|
-
for (const ds of datasets) {
|
|
471
|
-
const manifest = manifestCache.get(ds);
|
|
472
|
-
if (!manifest) continue;
|
|
473
|
-
try {
|
|
474
|
-
console.log(`${manifest.title} (${ds}):`);
|
|
475
|
-
const uriBase = manifest.uriBase;
|
|
476
|
-
if (!uriBase) throw new Error('build-edges: manifest.uriBase is required');
|
|
477
|
-
const result = buildEdgesForDataset(join(DATA_DIR, ds), ds, uriBase, urnMap, manifest);
|
|
478
|
-
allDatasetEdges.set(ds, result.edges);
|
|
479
|
-
allSourceRefs.push(...result.sourceRefs);
|
|
480
|
-
} catch (e) {
|
|
481
|
-
console.error(`Error reading manifest for ${ds}: ${e.message}`);
|
|
482
421
|
}
|
|
483
|
-
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
// Build a lookup of all known source strings from manifests
|
|
492
|
-
const knownSourceStrings = new Set();
|
|
493
|
-
for (const [ds, manifest] of manifestCache) {
|
|
494
|
-
if (manifest.ref) knownSourceStrings.add(manifest.ref);
|
|
495
|
-
for (const alias of manifest.refAliases ?? []) {
|
|
496
|
-
knownSourceStrings.add(alias);
|
|
422
|
+
const auditUnmatched = /* @__PURE__ */ new Map();
|
|
423
|
+
for (const { source, registerId } of allSourceRefs) {
|
|
424
|
+
if (knownSourceStrings.has(source)) continue;
|
|
425
|
+
if (source.startsWith("urn:")) continue;
|
|
426
|
+
if (!auditUnmatched.has(source)) {
|
|
427
|
+
auditUnmatched.set(source, registerId);
|
|
428
|
+
}
|
|
497
429
|
}
|
|
498
|
-
if (
|
|
499
|
-
|
|
500
|
-
|
|
430
|
+
if (auditUnmatched.size > 0) {
|
|
431
|
+
console.warn(`
|
|
432
|
+
\u26A0 ${auditUnmatched.size} source string(s) in concept data have no matching dataset ref:`);
|
|
433
|
+
for (const [source, fromDataset] of auditUnmatched) {
|
|
434
|
+
console.warn(` "${source}" (from ${fromDataset})`);
|
|
435
|
+
}
|
|
436
|
+
console.warn("Add refAliases to the target dataset manifest, or fix source strings to use URNs.\n");
|
|
501
437
|
}
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
438
|
+
const datasetUriPrefixes = /* @__PURE__ */ new Map();
|
|
439
|
+
for (const [ds, manifest] of manifestCache) {
|
|
440
|
+
const uriBase = manifest.uriBase;
|
|
441
|
+
if (!uriBase) throw new Error("build-edges: manifest.uriBase is required");
|
|
442
|
+
datasetUriPrefixes.set(ds, `${uriBase}/${ds}/`);
|
|
505
443
|
}
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
for (const { source, registerId } of allSourceRefs) {
|
|
510
|
-
if (knownSourceStrings.has(source)) continue;
|
|
511
|
-
// URN source strings resolve via URI routing — not a bibliography concern
|
|
512
|
-
if (source.startsWith('urn:')) continue;
|
|
513
|
-
if (!auditUnmatched.has(source)) {
|
|
514
|
-
auditUnmatched.set(source, registerId);
|
|
444
|
+
const crossRefIndex = {};
|
|
445
|
+
for (const ds of datasets) {
|
|
446
|
+
crossRefIndex[ds] = [];
|
|
515
447
|
}
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
console.warn('Add refAliases to the target dataset manifest, or fix source strings to use URNs.\n');
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
// Build cross-reference index: for each dataset, which other datasets'
|
|
527
|
-
// edges.json contains edges targeting that dataset's URIs.
|
|
528
|
-
const datasetUriPrefixes = new Map();
|
|
529
|
-
for (const [ds, manifest] of manifestCache) {
|
|
530
|
-
const uriBase = manifest.uriBase;
|
|
531
|
-
if (!uriBase) throw new Error('build-edges: manifest.uriBase is required');
|
|
532
|
-
datasetUriPrefixes.set(ds, `${uriBase}/${ds}/`);
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
const crossRefIndex = {};
|
|
536
|
-
for (const ds of datasets) {
|
|
537
|
-
crossRefIndex[ds] = [];
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
for (const [sourceDs, edges] of allDatasetEdges) {
|
|
541
|
-
const targets = new Set();
|
|
542
|
-
for (const edge of edges) {
|
|
543
|
-
for (const [targetDs, prefix] of datasetUriPrefixes) {
|
|
544
|
-
if (targetDs !== sourceDs && edge.target.startsWith(prefix)) {
|
|
545
|
-
targets.add(targetDs);
|
|
448
|
+
for (const [sourceDs, edges] of allDatasetEdges) {
|
|
449
|
+
const targets = /* @__PURE__ */ new Set();
|
|
450
|
+
for (const edge of edges) {
|
|
451
|
+
for (const [targetDs, prefix] of datasetUriPrefixes) {
|
|
452
|
+
if (targetDs !== sourceDs && edge.target.startsWith(prefix)) {
|
|
453
|
+
targets.add(targetDs);
|
|
454
|
+
}
|
|
546
455
|
}
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
targets.add(targetDs);
|
|
456
|
+
for (const [targetDs, prefix] of datasetUriPrefixes) {
|
|
457
|
+
if (targetDs !== sourceDs && edge.source.startsWith(prefix)) {
|
|
458
|
+
targets.add(targetDs);
|
|
459
|
+
}
|
|
552
460
|
}
|
|
553
461
|
}
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
462
|
+
for (const targetDs of targets) {
|
|
463
|
+
if (!crossRefIndex[targetDs].includes(sourceDs)) {
|
|
464
|
+
crossRefIndex[targetDs].push(sourceDs);
|
|
465
|
+
}
|
|
558
466
|
}
|
|
559
467
|
}
|
|
468
|
+
const crossRefPath = join(DATA_DIR, "cross-ref-index.json");
|
|
469
|
+
writeFileSync(crossRefPath, JSON.stringify(crossRefIndex));
|
|
470
|
+
const refCount = Object.values(crossRefIndex).reduce((sum, arr) => sum + arr.length, 0);
|
|
471
|
+
console.log(`Written cross-ref-index.json (${refCount} cross-references across ${datasets.length} datasets)`);
|
|
472
|
+
console.log("Done.");
|
|
560
473
|
}
|
|
561
|
-
|
|
562
|
-
const crossRefPath = join(DATA_DIR, 'cross-ref-index.json');
|
|
563
|
-
writeFileSync(crossRefPath, JSON.stringify(crossRefIndex));
|
|
564
|
-
const refCount = Object.values(crossRefIndex).reduce((sum, arr) => sum + arr.length, 0);
|
|
565
|
-
console.log(`Written cross-ref-index.json (${refCount} cross-references across ${datasets.length} datasets)`);
|
|
566
|
-
|
|
567
|
-
console.log('Done.');
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
const isDirectInvocation = process.argv[1]
|
|
571
|
-
&& realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url));
|
|
474
|
+
var isDirectInvocation = process.argv[1] && realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url));
|
|
572
475
|
if (isDirectInvocation) {
|
|
573
476
|
main();
|
|
574
477
|
}
|
|
478
|
+
export {
|
|
479
|
+
main
|
|
480
|
+
};
|