glossarist 0.4.1 → 0.4.3

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.
Files changed (39) hide show
  1. package/data/concept-model/README.md +44 -0
  2. package/data/concept-model/shapes/glossarist.shacl.ttl +704 -0
  3. package/package.json +24 -4
  4. package/src/concept-collection.js +57 -0
  5. package/src/concept-parser.js +1 -1
  6. package/src/concept-reader.js +1 -1
  7. package/src/concept-serializer.js +1 -1
  8. package/src/gcr-reader.js +1 -1
  9. package/src/index.js +15 -0
  10. package/src/models/bibliography-data.js +1 -1
  11. package/src/models/concept.js +6 -0
  12. package/src/models/dataset-color.js +79 -0
  13. package/src/models/designation.js +54 -0
  14. package/src/models/gcr-metadata.js +1 -1
  15. package/src/models/index.d.ts +121 -4
  16. package/src/models/index.js +13 -0
  17. package/src/models/register.js +99 -1
  18. package/src/models/relation-categories.js +151 -0
  19. package/src/models/relation-colors.js +78 -0
  20. package/src/rdf/deterministic-id.js +17 -0
  21. package/src/rdf/document-writer.js +87 -0
  22. package/src/rdf/gloss-concept.js +43 -0
  23. package/src/rdf/gloss-designation.js +72 -0
  24. package/src/rdf/gloss-detailed-definition.js +69 -0
  25. package/src/rdf/gloss-localized-concept.js +72 -0
  26. package/src/rdf/gloss-source.js +51 -0
  27. package/src/rdf/index.d.ts +107 -0
  28. package/src/rdf/index.js +27 -0
  29. package/src/rdf/normalize-enum.js +23 -0
  30. package/src/rdf/predicates.d.ts +443 -0
  31. package/src/rdf/predicates.js +245 -0
  32. package/src/rdf/prefixes.js +31 -0
  33. package/src/rdf/shacl.js +98 -0
  34. package/src/rdf/terms.js +15 -0
  35. package/src/transforms/concept-to-gloss.transform.js +75 -0
  36. package/src/transforms/index.d.ts +26 -0
  37. package/src/transforms/index.js +1 -0
  38. package/src/v1-reader.js +1 -1
  39. package/src/validators/gcr-validator.js +1 -1
@@ -0,0 +1,245 @@
1
+ // AUTO-GENERATED by scripts/gen-predicates.mjs. Do not edit.
2
+ // Source: data/concept-model/glossarist.context.jsonld
3
+ // Regenerate via `npm run gen:predicates`.
4
+ export const PRED = {
5
+ "gloss": {
6
+ "Concept": "https://www.glossarist.org/ontologies/Concept",
7
+ "ConceptCollection": "https://www.glossarist.org/ontologies/ConceptCollection",
8
+ "LocalizedConcept": "https://www.glossarist.org/ontologies/LocalizedConcept",
9
+ "Designation": "https://www.glossarist.org/ontologies/Designation",
10
+ "Expression": "https://www.glossarist.org/ontologies/Expression",
11
+ "Abbreviation": "https://www.glossarist.org/ontologies/Abbreviation",
12
+ "Symbol": "https://www.glossarist.org/ontologies/Symbol",
13
+ "LetterSymbol": "https://www.glossarist.org/ontologies/LetterSymbol",
14
+ "GraphicalSymbol": "https://www.glossarist.org/ontologies/GraphicalSymbol",
15
+ "Prefix": "https://www.glossarist.org/ontologies/Prefix",
16
+ "Suffix": "https://www.glossarist.org/ontologies/Suffix",
17
+ "Pronunciation": "https://www.glossarist.org/ontologies/Pronunciation",
18
+ "GrammarInfo": "https://www.glossarist.org/ontologies/GrammarInfo",
19
+ "DetailedDefinition": "https://www.glossarist.org/ontologies/DetailedDefinition",
20
+ "ConceptSource": "https://www.glossarist.org/ontologies/ConceptSource",
21
+ "Citation": "https://www.glossarist.org/ontologies/Citation",
22
+ "CitationRef": "https://www.glossarist.org/ontologies/CitationRef",
23
+ "ConceptRef": "https://www.glossarist.org/ontologies/ConceptRef",
24
+ "Reference": "https://www.glossarist.org/ontologies/Reference",
25
+ "RelatedConcept": "https://www.glossarist.org/ontologies/RelatedConcept",
26
+ "DesignationRelationship": "https://www.glossarist.org/ontologies/DesignationRelationship",
27
+ "ConceptDate": "https://www.glossarist.org/ontologies/ConceptDate",
28
+ "NonVerbalRepresentation": "https://www.glossarist.org/ontologies/NonVerbalRepresentation",
29
+ "CustomLocality": "https://www.glossarist.org/ontologies/CustomLocality",
30
+ "Locality": "https://www.glossarist.org/ontologies/Locality",
31
+ "DatasetRegister": "https://www.glossarist.org/ontologies/DatasetRegister",
32
+ "Section": "https://www.glossarist.org/ontologies/Section",
33
+ "identifier": "https://www.glossarist.org/ontologies/identifier",
34
+ "uri": "https://www.glossarist.org/ontologies/uri",
35
+ "hasLocalization": "https://www.glossarist.org/ontologies/hasLocalization",
36
+ "isLocalizationOf": "https://www.glossarist.org/ontologies/isLocalizationOf",
37
+ "hasStatus": "https://www.glossarist.org/ontologies/hasStatus",
38
+ "hasDomain": "https://www.glossarist.org/ontologies/hasDomain",
39
+ "hasRelatedConcept": "https://www.glossarist.org/ontologies/hasRelatedConcept",
40
+ "hasDate": "https://www.glossarist.org/ontologies/hasDate",
41
+ "hasSource": "https://www.glossarist.org/ontologies/hasSource",
42
+ "hasEntryStatus": "https://www.glossarist.org/ontologies/hasEntryStatus",
43
+ "hasDesignation": "https://www.glossarist.org/ontologies/hasDesignation",
44
+ "hasDefinition": "https://www.glossarist.org/ontologies/hasDefinition",
45
+ "hasNote": "https://www.glossarist.org/ontologies/hasNote",
46
+ "hasExample": "https://www.glossarist.org/ontologies/hasExample",
47
+ "hasScopedExample": "https://www.glossarist.org/ontologies/hasScopedExample",
48
+ "hasAnnotation": "https://www.glossarist.org/ontologies/hasAnnotation",
49
+ "hasNonVerbalRep": "https://www.glossarist.org/ontologies/hasNonVerbalRep",
50
+ "domain": "https://www.glossarist.org/ontologies/domain",
51
+ "release": "https://www.glossarist.org/ontologies/release",
52
+ "lineageSimilarity": "https://www.glossarist.org/ontologies/lineageSimilarity",
53
+ "reviewType": "https://www.glossarist.org/ontologies/reviewType",
54
+ "normativeStatus": "https://www.glossarist.org/ontologies/normativeStatus",
55
+ "hasPronunciation": "https://www.glossarist.org/ontologies/hasPronunciation",
56
+ "hasTermType": "https://www.glossarist.org/ontologies/hasTermType",
57
+ "classification": "https://www.glossarist.org/ontologies/classification",
58
+ "hasReference": "https://www.glossarist.org/ontologies/hasReference",
59
+ "hasGrammarInfo": "https://www.glossarist.org/ontologies/hasGrammarInfo",
60
+ "geographicalArea": "https://www.glossarist.org/ontologies/geographicalArea",
61
+ "isInternational": "https://www.glossarist.org/ontologies/isInternational",
62
+ "isAbsent": "https://www.glossarist.org/ontologies/isAbsent",
63
+ "register": "https://www.glossarist.org/ontologies/register",
64
+ "prefix": "https://www.glossarist.org/ontologies/prefix",
65
+ "usageInfo": "https://www.glossarist.org/ontologies/usageInfo",
66
+ "fieldOfApplication": "https://www.glossarist.org/ontologies/fieldOfApplication",
67
+ "isAcronym": "https://www.glossarist.org/ontologies/isAcronym",
68
+ "isInitialism": "https://www.glossarist.org/ontologies/isInitialism",
69
+ "isTruncation": "https://www.glossarist.org/ontologies/isTruncation",
70
+ "text": "https://www.glossarist.org/ontologies/text",
71
+ "image": "https://www.glossarist.org/ontologies/image",
72
+ "pronunciationContent": "https://www.glossarist.org/ontologies/pronunciationContent",
73
+ "pronunciationLanguage": "https://www.glossarist.org/ontologies/pronunciationLanguage",
74
+ "pronunciationScript": "https://www.glossarist.org/ontologies/pronunciationScript",
75
+ "pronunciationCountry": "https://www.glossarist.org/ontologies/pronunciationCountry",
76
+ "pronunciationSystem": "https://www.glossarist.org/ontologies/pronunciationSystem",
77
+ "relationshipType": "https://www.glossarist.org/ontologies/relationshipType",
78
+ "relationshipContent": "https://www.glossarist.org/ontologies/relationshipContent",
79
+ "relationshipRef": "https://www.glossarist.org/ontologies/relationshipRef",
80
+ "refType": "https://www.glossarist.org/ontologies/refType",
81
+ "urn": "https://www.glossarist.org/ontologies/urn",
82
+ "term": "https://www.glossarist.org/ontologies/term",
83
+ "refId": "https://www.glossarist.org/ontologies/refId",
84
+ "refVersion": "https://www.glossarist.org/ontologies/refVersion",
85
+ "refLink": "https://www.glossarist.org/ontologies/refLink",
86
+ "sourceType": "https://www.glossarist.org/ontologies/sourceType",
87
+ "sourceStatus": "https://www.glossarist.org/ontologies/sourceStatus",
88
+ "sourceId": "https://www.glossarist.org/ontologies/sourceId",
89
+ "sourceOrigin": "https://www.glossarist.org/ontologies/sourceOrigin",
90
+ "modification": "https://www.glossarist.org/ontologies/modification",
91
+ "hasCitationRef": "https://www.glossarist.org/ontologies/hasCitationRef",
92
+ "hasCitationLocality": "https://www.glossarist.org/ontologies/hasCitationLocality",
93
+ "citationOriginal": "https://www.glossarist.org/ontologies/citationOriginal",
94
+ "hasCustomLocality": "https://www.glossarist.org/ontologies/hasCustomLocality",
95
+ "citationRefSource": "https://www.glossarist.org/ontologies/citationRefSource",
96
+ "citationRefId": "https://www.glossarist.org/ontologies/citationRefId",
97
+ "citationRefVersion": "https://www.glossarist.org/ontologies/citationRefVersion",
98
+ "conceptRefSource": "https://www.glossarist.org/ontologies/conceptRefSource",
99
+ "conceptRefId": "https://www.glossarist.org/ontologies/conceptRefId",
100
+ "conceptRefText": "https://www.glossarist.org/ontologies/conceptRefText",
101
+ "customLocalityName": "https://www.glossarist.org/ontologies/customLocalityName",
102
+ "customLocalityValue": "https://www.glossarist.org/ontologies/customLocalityValue",
103
+ "citationLink": "https://www.glossarist.org/ontologies/citationLink",
104
+ "hasLocality": "https://www.glossarist.org/ontologies/hasLocality",
105
+ "dateValue": "https://www.glossarist.org/ontologies/dateValue",
106
+ "dateType": "https://www.glossarist.org/ontologies/dateType",
107
+ "eventDescription": "https://www.glossarist.org/ontologies/eventDescription",
108
+ "representationType": "https://www.glossarist.org/ontologies/representationType",
109
+ "representationRef": "https://www.glossarist.org/ontologies/representationRef",
110
+ "representationText": "https://www.glossarist.org/ontologies/representationText",
111
+ "localityType": "https://www.glossarist.org/ontologies/localityType",
112
+ "referenceFrom": "https://www.glossarist.org/ontologies/referenceFrom",
113
+ "referenceTo": "https://www.glossarist.org/ontologies/referenceTo",
114
+ "gender": "https://www.glossarist.org/ontologies/gender",
115
+ "number": "https://www.glossarist.org/ontologies/number",
116
+ "isNoun": "https://www.glossarist.org/ontologies/isNoun",
117
+ "isVerb": "https://www.glossarist.org/ontologies/isVerb",
118
+ "isAdjective": "https://www.glossarist.org/ontologies/isAdjective",
119
+ "isAdverb": "https://www.glossarist.org/ontologies/isAdverb",
120
+ "isPreposition": "https://www.glossarist.org/ontologies/isPreposition",
121
+ "isParticiple": "https://www.glossarist.org/ontologies/isParticiple",
122
+ "partOfSpeech": "https://www.glossarist.org/ontologies/partOfSpeech",
123
+ "script": "https://www.glossarist.org/ontologies/script",
124
+ "conversionSystem": "https://www.glossarist.org/ontologies/conversionSystem",
125
+ "deprecates": "https://www.glossarist.org/ontologies/deprecates",
126
+ "deprecatedBy": "https://www.glossarist.org/ontologies/deprecatedBy",
127
+ "supersedes": "https://www.glossarist.org/ontologies/supersedes",
128
+ "supersededBy": "https://www.glossarist.org/ontologies/supersededBy",
129
+ "compares": "https://www.glossarist.org/ontologies/compares",
130
+ "contrasts": "https://www.glossarist.org/ontologies/contrasts",
131
+ "sequentiallyRelated": "https://www.glossarist.org/ontologies/sequentiallyRelated",
132
+ "spatiallyRelated": "https://www.glossarist.org/ontologies/spatiallyRelated",
133
+ "temporallyRelated": "https://www.glossarist.org/ontologies/temporallyRelated",
134
+ "hasHomograph": "https://www.glossarist.org/ontologies/hasHomograph",
135
+ "hasFalseFriend": "https://www.glossarist.org/ontologies/hasFalseFriend",
136
+ "abbreviatedFormFor": "https://www.glossarist.org/ontologies/abbreviatedFormFor",
137
+ "shortFormFor": "https://www.glossarist.org/ontologies/shortFormFor",
138
+ "hasDesignationRelationship": "https://www.glossarist.org/ontologies/hasDesignationRelationship",
139
+ "designationRelationshipType": "https://www.glossarist.org/ontologies/designationRelationshipType",
140
+ "designationRelationshipContent": "https://www.glossarist.org/ontologies/designationRelationshipContent",
141
+ "relationshipTarget": "https://www.glossarist.org/ontologies/relationshipTarget",
142
+ "language": "https://www.glossarist.org/ontologies/language",
143
+ "datasetId": "https://www.glossarist.org/ontologies/datasetId",
144
+ "datasetRef": "https://www.glossarist.org/ontologies/datasetRef",
145
+ "datasetYear": "https://www.glossarist.org/ontologies/datasetYear",
146
+ "datasetUrn": "https://www.glossarist.org/ontologies/datasetUrn",
147
+ "datasetUrnAlias": "https://www.glossarist.org/ontologies/datasetUrnAlias",
148
+ "datasetRefAlias": "https://www.glossarist.org/ontologies/datasetRefAlias",
149
+ "datasetStatus": "https://www.glossarist.org/ontologies/datasetStatus",
150
+ "supersedesDataset": "https://www.glossarist.org/ontologies/supersedesDataset",
151
+ "owner": "https://www.glossarist.org/ontologies/owner",
152
+ "sourceRepo": "https://www.glossarist.org/ontologies/sourceRepo",
153
+ "datasetLanguage": "https://www.glossarist.org/ontologies/datasetLanguage",
154
+ "languageOrder": "https://www.glossarist.org/ontologies/languageOrder",
155
+ "hasSection": "https://www.glossarist.org/ontologies/hasSection",
156
+ "hasDefaultOrdering": "https://www.glossarist.org/ontologies/hasDefaultOrdering",
157
+ "datasetDescription": "https://www.glossarist.org/ontologies/datasetDescription",
158
+ "aboutPath": "https://www.glossarist.org/ontologies/aboutPath",
159
+ "logo": "https://www.glossarist.org/ontologies/logo",
160
+ "sectionId": "https://www.glossarist.org/ontologies/sectionId",
161
+ "sectionName": "https://www.glossarist.org/ontologies/sectionName",
162
+ "hasChildSection": "https://www.glossarist.org/ontologies/hasChildSection",
163
+ "hasParentSection": "https://www.glossarist.org/ontologies/hasParentSection",
164
+ "sectionOrdering": "https://www.glossarist.org/ontologies/sectionOrdering",
165
+ "$ns": "https://www.glossarist.org/ontologies/",
166
+ },
167
+
168
+ "skos": {
169
+ "prefLabel": "http://www.w3.org/2004/02/skos/core#prefLabel",
170
+ "altLabel": "http://www.w3.org/2004/02/skos/core#altLabel",
171
+ "hiddenLabel": "http://www.w3.org/2004/02/skos/core#hiddenLabel",
172
+ "definition": "http://www.w3.org/2004/02/skos/core#definition",
173
+ "scopeNote": "http://www.w3.org/2004/02/skos/core#scopeNote",
174
+ "note": "http://www.w3.org/2004/02/skos/core#note",
175
+ "example": "http://www.w3.org/2004/02/skos/core#example",
176
+ "notation": "http://www.w3.org/2004/02/skos/core#notation",
177
+ "inScheme": "http://www.w3.org/2004/02/skos/core#inScheme",
178
+ "hasTopConcept": "http://www.w3.org/2004/02/skos/core#hasTopConcept",
179
+ "broader": "http://www.w3.org/2004/02/skos/core#broader",
180
+ "narrower": "http://www.w3.org/2004/02/skos/core#narrower",
181
+ "related": "http://www.w3.org/2004/02/skos/core#related",
182
+ "exactMatch": "http://www.w3.org/2004/02/skos/core#exactMatch",
183
+ "closeMatch": "http://www.w3.org/2004/02/skos/core#closeMatch",
184
+ "broadMatch": "http://www.w3.org/2004/02/skos/core#broadMatch",
185
+ "narrowMatch": "http://www.w3.org/2004/02/skos/core#narrowMatch",
186
+ "relatedMatch": "http://www.w3.org/2004/02/skos/core#relatedMatch",
187
+ "$ns": "http://www.w3.org/2004/02/skos/core#",
188
+ },
189
+
190
+ "skosxl": {
191
+ "literalForm": "http://www.w3.org/2008/05/skos-xl#literalForm",
192
+ "labelRelation": "http://www.w3.org/2008/05/skos-xl#labelRelation",
193
+ "$ns": "http://www.w3.org/2008/05/skos-xl#",
194
+ },
195
+
196
+ "iso-thes": {
197
+ "broaderGeneric": "http://purl.org/iso25964/skos-thes#broaderGeneric",
198
+ "narrowerGeneric": "http://purl.org/iso25964/skos-thes#narrowerGeneric",
199
+ "broaderPartitive": "http://purl.org/iso25964/skos-thes#broaderPartitive",
200
+ "narrowerPartitive": "http://purl.org/iso25964/skos-thes#narrowerPartitive",
201
+ "broaderInstantial": "http://purl.org/iso25964/skos-thes#broaderInstantial",
202
+ "narrowerInstantial": "http://purl.org/iso25964/skos-thes#narrowerInstantial",
203
+ "$ns": "http://purl.org/iso25964/skos-thes#",
204
+ },
205
+
206
+ "dcterms": {
207
+ "bibliographicCitation": "http://purl.org/dc/terms/bibliographicCitation",
208
+ "$ns": "http://purl.org/dc/terms/",
209
+ },
210
+
211
+ "prov": {
212
+ "wasDerivedFrom": "http://www.w3.org/ns/prov#wasDerivedFrom",
213
+ "hadDerivation": "http://www.w3.org/ns/prov#hadDerivation",
214
+ "qualifiedDerivation": "http://www.w3.org/ns/prov#qualifiedDerivation",
215
+ "$ns": "http://www.w3.org/ns/prov#",
216
+ },
217
+
218
+ "rdfs": {
219
+ "label": "http://www.w3.org/2000/01/rdf-schema#label",
220
+ "comment": "http://www.w3.org/2000/01/rdf-schema#comment",
221
+ "seeAlso": "http://www.w3.org/2000/01/rdf-schema#seeAlso",
222
+ "subClassOf": "http://www.w3.org/2000/01/rdf-schema#subClassOf",
223
+ "$ns": "http://www.w3.org/2000/01/rdf-schema#",
224
+ },
225
+
226
+ "rdf": {
227
+ "value": "http://www.w3.org/1999/02/22-rdf-syntax-ns#value",
228
+ "$ns": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
229
+ },
230
+
231
+ };
232
+
233
+ export const PREFIXES = {
234
+ "gloss": "https://www.glossarist.org/ontologies/",
235
+ "skos": "http://www.w3.org/2004/02/skos/core#",
236
+ "skosxl": "http://www.w3.org/2008/05/skos-xl#",
237
+ "iso-thes": "http://purl.org/iso25964/skos-thes#",
238
+ "dcterms": "http://purl.org/dc/terms/",
239
+ "prov": "http://www.w3.org/ns/prov#",
240
+ "owl": "http://www.w3.org/2002/07/owl#",
241
+ "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
242
+ "rdfs": "http://www.w3.org/2000/01/rdf-schema#",
243
+ "xsd": "http://www.w3.org/2001/XMLSchema#",
244
+ "vann": "http://purl.org/vocab/vann/",
245
+ };
@@ -0,0 +1,31 @@
1
+ // Re-export prefix map and predicate constants generated from the vendored
2
+ // concept-model JSON-LD context. Downstream code should never duplicate
3
+ // prefix bindings — always import from here.
4
+ //
5
+ // The JSON-LD context deliberately omits some "well-known" RDF and SKOS
6
+ // constants (`rdf:type`, `skos:Concept`) because JSON-LD handles them via
7
+ // the `@type` keyword. We construct those from the namespace URIs here so
8
+ // downstream emitters don't have to repeat the construction.
9
+ import { PRED, PREFIXES } from './predicates.js';
10
+
11
+ export { PRED, PREFIXES };
12
+
13
+ // SKOS-XL label predicates — same local name as SKOS, so we construct them
14
+ // from the SKOS-XL namespace URI.
15
+ const SKOSXL_NS = PRED.skosxl.$ns;
16
+ export const SKOSXL = {
17
+ prefLabel: `${SKOSXL_NS}prefLabel`,
18
+ altLabel: `${SKOSXL_NS}altLabel`,
19
+ hiddenLabel: `${SKOSXL_NS}hiddenLabel`,
20
+ literalForm: `${SKOSXL_NS}literalForm`,
21
+ };
22
+
23
+ // Constants used in instance data but absent from the JSON-LD context.
24
+ const RDF_NS = PREFIXES.rdf;
25
+ const SKOS_NS = PREFIXES.skos;
26
+ export const WELL_KNOWN = {
27
+ rdfType: `${RDF_NS}type`,
28
+ rdfValue: `${RDF_NS}value`,
29
+ skosConcept: `${SKOS_NS}Concept`,
30
+ skosxlLabel: `${SKOSXL_NS}Label`,
31
+ };
@@ -0,0 +1,98 @@
1
+ // SHACL validator wrapper. Loads vendored concept-model shapes once and
2
+ // validates any RDFJS Dataset against them. Mirrors the mechanics of
3
+ // glossarist-ruby's `Glossarist::Validation::ShaclValidator`.
4
+ import { readFile } from 'node:fs/promises';
5
+ import { dirname, resolve } from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+ import dataModel from '@rdfjs/data-model';
8
+ import rdfDatasetFactory from '@rdfjs/dataset';
9
+ import { Parser as N3Parser } from 'n3';
10
+ import { default as ShaclValidator } from 'rdf-validate-shacl';
11
+
12
+ // rdf-validate-shacl needs an RDFJS factory that produces both terms
13
+ // and Datasets. @rdfjs/data-model is the spec-complete term factory;
14
+ // @rdfjs/dataset adds the Dataset class.
15
+ //
16
+ // The data-model default export carries its methods on the prototype
17
+ // (non-enumerable), so a spread ({ ...dataModel }) does not capture
18
+ // them. List the RDF/JS surface explicitly.
19
+ const FACTORY = {
20
+ namedNode: dataModel.namedNode.bind(dataModel),
21
+ blankNode: dataModel.blankNode.bind(dataModel),
22
+ literal: dataModel.literal.bind(dataModel),
23
+ variable: dataModel.variable.bind(dataModel),
24
+ defaultGraph: dataModel.defaultGraph.bind(dataModel),
25
+ quad: dataModel.quad.bind(dataModel),
26
+ fromTerm: dataModel.fromTerm.bind(dataModel),
27
+ fromQuad: dataModel.fromQuad.bind(dataModel),
28
+ dataset: rdfDatasetFactory.dataset.bind(rdfDatasetFactory),
29
+ };
30
+ const createDataset = FACTORY.dataset;
31
+ const ShaclValidatorCtor = ShaclValidator.default ?? ShaclValidator;
32
+
33
+ // Path to the vendored SHACL shapes (synced from glossarist/concept-model
34
+ // via `npm run sync:model`). Resolved relative to this source file so it
35
+ // works regardless of the consumer's cwd.
36
+ const SHAPES_PATH = resolve(
37
+ dirname(fileURLToPath(import.meta.url)),
38
+ '..', '..',
39
+ 'data', 'concept-model', 'shapes', 'glossarist.shacl.ttl',
40
+ );
41
+
42
+ // Note: SHAPES_PATH resolves relative to this source file. When the package
43
+ // is installed, npm packs `data/concept-model/shapes/glossarist.shacl.ttl`
44
+ // (see `files` in package.json) into `node_modules/glossarist/data/...`, so
45
+ // the relative climb (`../..`) from `src/rdf/shacl.js` still lands on the
46
+ // installed package root.
47
+
48
+ // Cache keyed by absolute path so callers swapping shapes via the
49
+ // `shapesPath` option (or future overrides) don't get cross-talk.
50
+ const SHAPES_CACHE = new Map();
51
+
52
+ async function parseTurtle(text, baseIri) {
53
+ const parser = new N3Parser({ baseIRI: baseIri });
54
+ const out = createDataset();
55
+ return new Promise((resolve, reject) => {
56
+ parser.parse(text, (err, quad) => {
57
+ if (err) reject(err);
58
+ else if (quad) out.add(quad);
59
+ else resolve(out);
60
+ });
61
+ });
62
+ }
63
+
64
+ export async function loadShapes({ shapesPath = SHAPES_PATH } = {}) {
65
+ const cached = SHAPES_CACHE.get(shapesPath);
66
+ if (cached) return cached;
67
+ const text = await readFile(shapesPath, 'utf8');
68
+ const dataset = await parseTurtle(text, `file://${shapesPath}`);
69
+ SHAPES_CACHE.set(shapesPath, dataset);
70
+ return dataset;
71
+ }
72
+
73
+ // Validates a single RDFJS Dataset against the concept-model SHACL shapes.
74
+ // Returns the underlying ValidationReport from rdf-validate-shacl.
75
+ //
76
+ // Options:
77
+ // - shapes: a pre-loaded Dataset to use instead of the default shapes
78
+ // - shapesPath: alternative path to load shapes from (cached per path)
79
+ export async function validateShacl(dataDataset, { shapes, shapesPath } = {}) {
80
+ const shapesDataset = shapes ?? await loadShapes({ shapesPath });
81
+ const validator = new ShaclValidatorCtor(shapesDataset, { factory: FACTORY });
82
+ return validator.validate(dataDataset);
83
+ }
84
+
85
+ // Convenience: builds an RDFJS Dataset from a quad iterable (e.g. the
86
+ // output of conceptToQuads). Useful when chaining the RDF emitters with
87
+ // SHACL validation.
88
+ export function quadsToDataset(quads) {
89
+ const ds = createDataset();
90
+ for (const q of quads) ds.add(q);
91
+ return ds;
92
+ }
93
+
94
+ // Test-only: clears the shapes cache. Exported so tests can verify
95
+ // independent caching per path without reaching into module internals.
96
+ export function __clearShapesCacheForTests() {
97
+ SHAPES_CACHE.clear();
98
+ }
@@ -0,0 +1,15 @@
1
+ // Shared RDF/JS term constructors. Replaces the per-emitter
2
+ // `const { namedNode, literal, quad } = DataFactory;` pattern that was
3
+ // repeated across the RDF layer.
4
+ //
5
+ // We use n3's DataFactory because it produces terms that are
6
+ // interchangeable with @rdfjs/data-model output and because the rest
7
+ // of the RDF layer (N3Parser, N3Writer) already speaks n3.
8
+
9
+ import { DataFactory } from 'n3';
10
+
11
+ export const namedNode = DataFactory.namedNode.bind(DataFactory);
12
+ export const blankNode = DataFactory.blankNode.bind(DataFactory);
13
+ export const literal = DataFactory.literal.bind(DataFactory);
14
+ export const defaultGraph = DataFactory.defaultGraph.bind(DataFactory);
15
+ export const quad = DataFactory.quad.bind(DataFactory);
@@ -0,0 +1,75 @@
1
+ // ConceptToGlossTransform — JS port of glossarist-ruby's
2
+ // `Transforms::ConceptToGlossTransform`. Converts a Concept model into
3
+ // ontology-faithful RDF (Turtle, JSON-LD, N-Triples).
4
+ //
5
+ // Usage:
6
+ // import { ConceptToGlossTransform } from 'glossarist/transforms';
7
+ // const xform = new ConceptToGlossTransform({ registerId: 'iso-6709', uriBase: 'https://glossarist.org' });
8
+ // const ttl = await xform.toTurtle(concept);
9
+ // const jsonld = await xform.toJsonld(concept);
10
+ // const ttlAll = await xform.toTurtleAll(concepts);
11
+ import { conceptToQuads } from '../rdf/gloss-concept.js';
12
+ import {
13
+ collectQuads,
14
+ writeTurtle,
15
+ writeNTriples,
16
+ writeJsonld,
17
+ } from '../rdf/document-writer.js';
18
+
19
+ const DEFAULT_URI_BASE = 'https://glossarist.org';
20
+
21
+ // Writer registry. Each entry is `{ write: (quads, transform) => Promise<string> }`.
22
+ // Adding a new output format is a single entry here, not six new methods.
23
+ const WRITERS = Object.freeze({
24
+ turtle: { write: (quads) => writeTurtle(quads) },
25
+ ntriples: { write: (quads) => writeNTriples(quads) },
26
+ jsonld: { write: (quads, t) => writeJsonld(quads, t._jsonldOpts()) },
27
+ });
28
+
29
+ export class ConceptToGlossTransform {
30
+ constructor(options = {}) {
31
+ this.registerId = options.registerId;
32
+ this.uriBase = (options.uriBase ?? DEFAULT_URI_BASE).replace(/\/+$/, '');
33
+ this.jsonldContext = options.jsonldContext;
34
+ }
35
+
36
+ toTurtle(target) { return this._serialize(target, 'turtle'); }
37
+ toTurtleAll(targets) { return this._serialize(targets, 'turtle'); }
38
+ toJsonld(target) { return this._serialize(target, 'jsonld'); }
39
+ toJsonldAll(targets) { return this._serialize(targets, 'jsonld'); }
40
+ toNTriples(target) { return this._serialize(target, 'ntriples'); }
41
+ toNTriplesAll(targets) { return this._serialize(targets, 'ntriples'); }
42
+
43
+ // Single dispatcher: collect quads for one concept or many, then hand
44
+ // off to the registered writer. Replaces six near-identical methods.
45
+ async _serialize(target, format) {
46
+ const writer = WRITERS[format];
47
+ if (!writer) throw new Error(`Unknown serialization format: ${format}`);
48
+ const quads = this._collect(target);
49
+ return writer.write(quads, this);
50
+ }
51
+
52
+ _collect(target) {
53
+ if (target && typeof target[Symbol.iterator] === 'function') {
54
+ const quads = [];
55
+ for (const concept of target) {
56
+ for (const q of conceptToQuads(concept, this._optsFor(concept))) {
57
+ quads.push(q);
58
+ }
59
+ }
60
+ return quads;
61
+ }
62
+ return collectQuads(conceptToQuads(target, this._optsFor(target)));
63
+ }
64
+
65
+ _optsFor(concept) {
66
+ return {
67
+ registerId: this.registerId ?? concept.registerId ?? 'glossary',
68
+ uriBase: this.uriBase,
69
+ };
70
+ }
71
+
72
+ _jsonldOpts() {
73
+ return this.jsonldContext ? { context: this.jsonldContext } : {};
74
+ }
75
+ }
@@ -0,0 +1,26 @@
1
+ // Type declarations for the public transforms layer.
2
+ //
3
+ // Mirrors the runtime exports of src/transforms/index.js.
4
+
5
+ import type { Concept } from '../models/index';
6
+ import type { Quad } from '@rdfjs/dataset';
7
+
8
+ export interface ConceptToGlossTransformOptions {
9
+ registerId?: string;
10
+ uriBase?: string;
11
+ jsonldContext?: Record<string, string>;
12
+ }
13
+
14
+ export declare class ConceptToGlossTransform {
15
+ constructor(options?: ConceptToGlossTransformOptions);
16
+ readonly registerId: string | undefined;
17
+ readonly uriBase: string;
18
+ readonly jsonldContext: Record<string, string> | undefined;
19
+
20
+ toTurtle(concept: Concept): Promise<string>;
21
+ toTurtleAll(concepts: Iterable<Concept>): Promise<string>;
22
+ toJsonld(concept: Concept): Promise<string>;
23
+ toJsonldAll(concepts: Iterable<Concept>): Promise<string>;
24
+ toNTriples(concept: Concept): Promise<string>;
25
+ toNTriplesAll(concepts: Iterable<Concept>): Promise<string>;
26
+ }
@@ -0,0 +1 @@
1
+ export { ConceptToGlossTransform } from './concept-to-gloss.transform.js';
package/src/v1-reader.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
- import yaml from 'js-yaml';
3
+ import * as yaml from 'js-yaml';
4
4
  import { Concept } from './models/concept.js';
5
5
  import { InvalidInputError } from './errors.js';
6
6
 
@@ -1,4 +1,4 @@
1
- import yaml from 'js-yaml';
1
+ import * as yaml from 'js-yaml';
2
2
  import { DATASET_ASSETS } from '../dataset-asset.js';
3
3
  import { ValidationResult } from './validation-result.js';
4
4