@lsbjordao/type-taxon-script 1.1.18 → 1.1.19

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.
@@ -18,7 +18,7 @@ const child_process_1 = require("child_process");
18
18
  const fast_glob_1 = __importDefault(require("fast-glob"));
19
19
  const n3_1 = require("n3");
20
20
  const tscBin = require.resolve('typescript/bin/tsc');
21
- const { namedNode, literal, quad, blankNode } = n3_1.DataFactory;
21
+ const { namedNode, literal, blankNode } = n3_1.DataFactory;
22
22
  const NS = {
23
23
  tts: 'https://tts.example.org/ontology/',
24
24
  inst: 'https://tts.example.org/instance/',
@@ -30,6 +30,7 @@ const NS = {
30
30
  dcterms: 'http://purl.org/dc/terms/',
31
31
  prov: 'http://www.w3.org/ns/prov#'
32
32
  };
33
+ const ONTOLOGY_IRI = namedNode(NS.tts);
33
34
  function tts(local) {
34
35
  return namedNode(`${NS.tts}${encodeURIComponent(local)}`);
35
36
  }
@@ -54,9 +55,6 @@ function xsd(local) {
54
55
  function dcterms(local) {
55
56
  return namedNode(`${NS.dcterms}${local}`);
56
57
  }
57
- function prov(local) {
58
- return namedNode(`${NS.prov}${local}`);
59
- }
60
58
  function createWriter() {
61
59
  return new n3_1.Writer({
62
60
  prefixes: {
@@ -114,6 +112,9 @@ function toPascalLike(input) {
114
112
  .replace(/[_\-\s]+([a-zA-Z0-9])/g, (_, c) => c.toUpperCase())
115
113
  .replace(/^[a-z]/, c => c.toUpperCase());
116
114
  }
115
+ function capitalize(input) {
116
+ return input.charAt(0).toUpperCase() + input.slice(1);
117
+ }
117
118
  function localId(value, fallback) {
118
119
  var _a, _b;
119
120
  return (_b = (_a = value === null || value === void 0 ? void 0 : value._id) !== null && _a !== void 0 ? _a : value === null || value === void 0 ? void 0 : value.id) !== null && _b !== void 0 ? _b : fallback;
@@ -125,31 +126,78 @@ function primitiveToLiteral(value) {
125
126
  return literal(String(value), xsd('decimal'));
126
127
  return literal(String(value), xsd('boolean'));
127
128
  }
129
+ function getLatestCharactersVersion() {
130
+ const baseDir = './characters';
131
+ if (!fs_1.default.existsSync(baseDir))
132
+ return null;
133
+ const versions = fs_1.default.readdirSync(baseDir)
134
+ .filter(d => /^v\d+$/.test(d))
135
+ .map(d => ({ dir: d, num: parseInt(d.slice(1), 10) }))
136
+ .sort((a, b) => b.num - a.num);
137
+ return versions.length > 0 ? versions[0].dir : null;
138
+ }
139
+ function shouldSkipClass(cls) {
140
+ return !cls.name || cls.name.startsWith('_');
141
+ }
142
+ function getRelationPredicateName(fieldName, relation) {
143
+ if (relation === null || relation === void 0 ? void 0 : relation.predicate)
144
+ return relation.predicate;
145
+ return `has${capitalize(fieldName)}`;
146
+ }
147
+ function getRelationAnchor(relation) {
148
+ // relationIri takes precedence — anchors directly to external ontology (e.g. RO)
149
+ if (relation === null || relation === void 0 ? void 0 : relation.relationIri) {
150
+ return namedNode(relation.relationIri);
151
+ }
152
+ // fallback to tts-local anchor based on relationType label
153
+ switch (relation === null || relation === void 0 ? void 0 : relation.relationType) {
154
+ case 'partOf':
155
+ return tts('partOf');
156
+ case 'hasComponent':
157
+ return tts('hasComponent');
158
+ case 'hasPart':
159
+ default:
160
+ return tts('hasPart');
161
+ }
162
+ }
128
163
  function importModules(pattern) {
129
164
  var _a, _b;
130
165
  return __awaiter(this, void 0, void 0, function* () {
131
166
  const tsFiles = yield (0, fast_glob_1.default)(pattern, { absolute: true });
132
167
  if (tsFiles.length === 0)
133
168
  return [];
134
- const tempDir = path_1.default.join(process.cwd(), 'temp');
169
+ const tempDir = path_1.default.join(process.cwd(), '.tts-temp-ontology');
135
170
  fs_1.default.mkdirSync(tempDir, { recursive: true });
136
171
  const tempTsConfigPath = path_1.default.join(tempDir, 'tsconfig.ontology.json');
137
172
  fs_1.default.writeFileSync(tempTsConfigPath, JSON.stringify({
138
- compilerOptions: { target: 'ES2016', module: 'commonjs', esModuleInterop: true, skipLibCheck: true },
173
+ compilerOptions: {
174
+ target: 'ES2016',
175
+ module: 'commonjs',
176
+ esModuleInterop: true,
177
+ skipLibCheck: true,
178
+ outDir: tempDir,
179
+ rootDir: process.cwd()
180
+ },
139
181
  files: tsFiles
140
182
  }), 'utf-8');
141
183
  try {
142
- (0, child_process_1.execSync)(`node "${tscBin}" -p "${tempTsConfigPath}"`);
184
+ (0, child_process_1.execSync)(`node "${tscBin}" -p "${tempTsConfigPath}"`, { stdio: 'pipe' });
143
185
  }
144
186
  catch (e) {
145
- fs_1.default.unlinkSync(tempTsConfigPath);
187
+ cleanupTempDir(tempDir);
146
188
  throw new Error(((_a = e.stdout) === null || _a === void 0 ? void 0 : _a.toString()) || ((_b = e.stderr) === null || _b === void 0 ? void 0 : _b.toString()) || e.message);
147
189
  }
148
- fs_1.default.unlinkSync(tempTsConfigPath);
190
+ finally {
191
+ try {
192
+ fs_1.default.unlinkSync(tempTsConfigPath);
193
+ }
194
+ catch (_c) { }
195
+ }
149
196
  const modules = [];
150
197
  const jsFiles = [];
151
198
  for (const file of tsFiles) {
152
- const jsFile = file.replace(/\.ts$/, '.js');
199
+ const relative = path_1.default.relative(process.cwd(), file);
200
+ const jsFile = path_1.default.join(tempDir, relative.replace(/\.ts$/, '.js'));
153
201
  jsFiles.push(jsFile);
154
202
  modules.push({ file, mod: require(jsFile) });
155
203
  }
@@ -157,23 +205,25 @@ function importModules(pattern) {
157
205
  try {
158
206
  fs_1.default.unlinkSync(jsFile);
159
207
  }
160
- catch ( /* ignore */_c) { /* ignore */ }
161
- }
162
- try {
163
- fs_1.default.rmdirSync(tempDir);
208
+ catch (_d) { }
164
209
  }
165
- catch ( /* ignore if not empty */_d) { /* ignore if not empty */ }
210
+ cleanupTempDir(tempDir);
166
211
  return modules;
167
212
  });
168
213
  }
169
- function inferRangeFromValue(value) {
170
- if (typeof value === 'number')
171
- return xsd('decimal');
172
- if (typeof value === 'boolean')
173
- return xsd('boolean');
174
- if (typeof value === 'string')
175
- return xsd('string');
176
- return null;
214
+ function cleanupTempDir(tempDir) {
215
+ try {
216
+ fs_1.default.rmSync(tempDir, { recursive: true, force: true });
217
+ }
218
+ catch (_a) { }
219
+ }
220
+ function getPredicateForField(object, fieldName) {
221
+ var _a;
222
+ const ctor = object.constructor;
223
+ const rel = (_a = ctor === null || ctor === void 0 ? void 0 : ctor.semanticRelations) === null || _a === void 0 ? void 0 : _a[fieldName];
224
+ if (rel === null || rel === void 0 ? void 0 : rel.predicate)
225
+ return tts(rel.predicate);
226
+ return tts(fieldName);
177
227
  }
178
228
  function serializeSourceLikeArray(writer, subject, values) {
179
229
  for (const v of values) {
@@ -214,7 +264,7 @@ function serializeObject(writer, subject, object, objectTypeName, objectId, visi
214
264
  serializeSourceLikeArray(writer, subject, value);
215
265
  continue;
216
266
  }
217
- const predicate = tts(key);
267
+ const predicate = getPredicateForField(object, key);
218
268
  if (isPrimitive(value)) {
219
269
  writer.addQuad(subject, predicate, primitiveToLiteral(value));
220
270
  continue;
@@ -244,20 +294,23 @@ function serializeObject(writer, subject, object, objectTypeName, objectId, visi
244
294
  }
245
295
  }
246
296
  }
247
- function getLatestCharactersVersion() {
248
- const baseDir = './characters';
249
- if (!fs_1.default.existsSync(baseDir))
250
- return null;
251
- const versions = fs_1.default.readdirSync(baseDir)
252
- .filter(d => /^v\d+$/.test(d))
253
- .map(d => ({ dir: d, num: parseInt(d.slice(1), 10) }))
254
- .sort((a, b) => b.num - a.num);
255
- return versions.length > 0 ? versions[0].dir : null;
256
- }
257
297
  function buildOntologyTTL() {
258
- var _a, _b, _c, _d, _e, _f, _g;
298
+ var _a, _b, _c, _d, _e, _f, _g, _h;
259
299
  return __awaiter(this, void 0, void 0, function* () {
260
300
  const writer = createWriter();
301
+ const seenClasses = new Set();
302
+ const seenDatatypeProperties = new Set();
303
+ const seenObjectProperties = new Set();
304
+ // Core generic relation properties (tts-local, subPropertyOf RO when relationIri is set per-class)
305
+ writer.addQuad(tts('hasPart'), rdf('type'), owl('ObjectProperty'));
306
+ writer.addQuad(tts('hasPart'), rdfs('label'), literal('has part', 'en'));
307
+ writer.addQuad(tts('hasPart'), rdfs('isDefinedBy'), ONTOLOGY_IRI);
308
+ writer.addQuad(tts('partOf'), rdf('type'), owl('ObjectProperty'));
309
+ writer.addQuad(tts('partOf'), rdfs('label'), literal('part of', 'en'));
310
+ writer.addQuad(tts('partOf'), rdfs('isDefinedBy'), ONTOLOGY_IRI);
311
+ writer.addQuad(tts('hasComponent'), rdf('type'), owl('ObjectProperty'));
312
+ writer.addQuad(tts('hasComponent'), rdfs('label'), literal('has component', 'en'));
313
+ writer.addQuad(tts('hasComponent'), rdfs('isDefinedBy'), ONTOLOGY_IRI);
261
314
  const version = getLatestCharactersVersion();
262
315
  if (!version) {
263
316
  console.error('\x1b[31m✖ No versioned directory found under ./characters/\x1b[0m');
@@ -269,22 +322,34 @@ function buildOntologyTTL() {
269
322
  if (!isClassLike(exported))
270
323
  continue;
271
324
  const cls = exported;
325
+ if (shouldSkipClass(cls))
326
+ continue;
327
+ if (seenClasses.has(cls.name))
328
+ continue;
329
+ seenClasses.add(cls.name);
272
330
  const classNode = tts(cls.name);
273
331
  writer.addQuad(classNode, rdf('type'), owl('Class'));
274
- writer.addQuad(classNode, rdfs('label'), literal((_b = (_a = cls.semantic) === null || _a === void 0 ? void 0 : _a.label) !== null && _b !== void 0 ? _b : cls.name));
332
+ writer.addQuad(classNode, rdf('type'), rdfs('Class'));
333
+ writer.addQuad(classNode, rdfs('isDefinedBy'), ONTOLOGY_IRI);
334
+ writer.addQuad(classNode, rdfs('label'), literal((_b = (_a = cls.semantic) === null || _a === void 0 ? void 0 : _a.label) !== null && _b !== void 0 ? _b : cls.name, 'en'));
275
335
  if ((_c = cls.semantic) === null || _c === void 0 ? void 0 : _c.definition) {
276
- writer.addQuad(classNode, rdfs('comment'), literal(cls.semantic.definition));
336
+ writer.addQuad(classNode, rdfs('comment'), literal(cls.semantic.definition, 'en'));
277
337
  }
278
338
  addMappings(writer, classNode, (_d = cls.semantic) === null || _d === void 0 ? void 0 : _d.ontologyMappings);
339
+ // Datatype / object properties from semanticAttributes
279
340
  const attrs = (_e = cls.semanticAttributes) !== null && _e !== void 0 ? _e : {};
280
341
  for (const [attrName, meta] of Object.entries(attrs)) {
342
+ if (seenDatatypeProperties.has(attrName))
343
+ continue;
344
+ seenDatatypeProperties.add(attrName);
281
345
  const propNode = tts(attrName);
282
346
  const kind = meta.kind === 'object' ? owl('ObjectProperty') : owl('DatatypeProperty');
283
347
  writer.addQuad(propNode, rdf('type'), kind);
348
+ writer.addQuad(propNode, rdfs('isDefinedBy'), ONTOLOGY_IRI);
284
349
  writer.addQuad(propNode, rdfs('domain'), classNode);
285
- writer.addQuad(propNode, rdfs('label'), literal((_f = meta.label) !== null && _f !== void 0 ? _f : attrName));
350
+ writer.addQuad(propNode, rdfs('label'), literal((_f = meta.label) !== null && _f !== void 0 ? _f : attrName, 'en'));
286
351
  if (meta.definition) {
287
- writer.addQuad(propNode, rdfs('comment'), literal(meta.definition));
352
+ writer.addQuad(propNode, rdfs('comment'), literal(meta.definition, 'en'));
288
353
  }
289
354
  const dt = normalizeDatatype(meta.datatype);
290
355
  if (dt) {
@@ -300,6 +365,31 @@ function buildOntologyTTL() {
300
365
  }
301
366
  }
302
367
  }
368
+ // Structural class relations from semanticRelations
369
+ const rels = (_h = cls.semanticRelations) !== null && _h !== void 0 ? _h : {};
370
+ for (const [fieldName, rel] of Object.entries(rels)) {
371
+ const predicateName = getRelationPredicateName(fieldName, rel);
372
+ const propNode = tts(predicateName);
373
+ const targetNode = tts(rel.target);
374
+ if (!seenObjectProperties.has(predicateName)) {
375
+ seenObjectProperties.add(predicateName);
376
+ writer.addQuad(propNode, rdf('type'), owl('ObjectProperty'));
377
+ writer.addQuad(propNode, rdfs('isDefinedBy'), ONTOLOGY_IRI);
378
+ writer.addQuad(propNode, rdfs('label'), literal(predicateName, 'en'));
379
+ writer.addQuad(propNode, rdfs('domain'), classNode);
380
+ writer.addQuad(propNode, rdfs('range'), targetNode);
381
+ // subPropertyOf: points directly to RO IRI if provided,
382
+ // otherwise falls back to tts-local anchor (hasPart / partOf / hasComponent)
383
+ writer.addQuad(propNode, rdfs('subPropertyOf'), getRelationAnchor(rel));
384
+ // relationType as human-readable label on the anchor node (only when both fields present)
385
+ if (rel.relationType && rel.relationIri) {
386
+ writer.addQuad(namedNode(rel.relationIri), rdfs('label'), literal(rel.relationType, 'en'));
387
+ }
388
+ addMappings(writer, propNode, rel.ontologyMappings);
389
+ }
390
+ // explicit schema-level relation triple between classes
391
+ writer.addQuad(classNode, propNode, targetNode);
392
+ }
303
393
  }
304
394
  }
305
395
  return writerToString(writer);
@@ -351,8 +441,9 @@ function ttsExportOntology(genus) {
351
441
  console.error('\x1b[31m✖ Argument `--genus` cannot be empty.\x1b[0m');
352
442
  return;
353
443
  }
354
- if (!fs_1.default.existsSync('./characters/v1')) {
355
- console.error('\x1b[31m✖ Directory ./characters/v1 not found.\x1b[0m');
444
+ const version = getLatestCharactersVersion();
445
+ if (!version) {
446
+ console.error('\x1b[31m✖ No versioned directory (v1, v2, ...) found under ./characters/\x1b[0m');
356
447
  return;
357
448
  }
358
449
  if (!fs_1.default.existsSync(`./taxon/${genus}`)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lsbjordao/type-taxon-script",
3
- "version": "1.1.18",
3
+ "version": "1.1.19",
4
4
  "author": "Lucas Jordão <tucarj@gmail.com> & André Eppinghaus <andreeppinghaus@gmail.com> & Vicente Calfo <vicentecalfo@gmail.com>",
5
5
  "license": "ISC",
6
6
  "description": "TypeTaxonScript",