Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "CASCaRA-Validation-Tool",
"version": "0.2.7",
"version": "0.2.8",
"private": true,
"scripts": {
"prebuild": "node scripts/generate-build-info.js",
Expand Down
2 changes: 1 addition & 1 deletion public/assets/xslt/FMI-to-CAS.sef.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion public/assets/xslt/ReqIF-to-CAS.sef.json

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions src/build-info.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// Auto-generated by scripts/generate-build-info.js - DO NOT EDIT
// Generated at: 2026-07-03T14:25:51.106Z
// Generated at: 2026-07-12T12:37:47.778Z

export const BUILD_INFO = {
// from package.json:
appName: 'CASCaRA-Validation-Tool',
appVersion: '0.2.7',
appVersion: '0.2.8',
// from Git:
buildTime: '2026-07-03T14:25:51.106Z',
gitCommit: '8ecfa1a',
buildTime: '2026-07-12T12:37:47.778Z',
gitCommit: '02f0f8b',
gitBranch: '28-transform-cascara-to-rdfturtle',
gitDirty: false
};
48 changes: 24 additions & 24 deletions src/common/export/html/getHTML.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,19 +86,18 @@ class GetHTML {

const titleText = passify(LIB.stripHTML(LIB.getLocalText(pkg.title, lang)));
const descText = passify(LIB.getLocalText(pkg.description, lang));
const propertiesHTML = propertiesToHTML(pkg, lang);

const pkgHTML = `<div class="meta-aPackage">
<div class="col-main" style="flex: 0 0 ${widthMain};">
<h3 class="meta-title">${titleText || 'Untitled Package'}</h3>
${descText ? `<div class="meta-description">${descText}</div>` : ''}
${errHTML}
</div>
<div class="col-right">
<dl class="dl-horizontal">
${metadataToHTML(pkg, lang)}
<dt>Items in Graph</dt><dd>${pkg.graph.length}</dd>
</dl>
</div>
<div class="col-right" ><dl class="dl-horizontal">
${propertiesHTML}
<dt>Items in Graph</dt><dd>${pkg.graph.length}</dd>
</dl></div>
</div>`;

const result: stringHTML[] = [pkgHTML];
Expand Down Expand Up @@ -126,31 +125,16 @@ class GetHTML {

const titleText = passify(LIB.stripHTML(LIB.getLocalText(entity.title, lang)));
const descText = passify(LIB.getLocalText(entity.description, lang));

let propertiesHTML = '';
propertiesHTML = '<div class="col-right"><dl class="dl-horizontal">';
if (entity.hasProperty?.length > 0) {
// the configured properties:
for (const prop of entity.hasProperty) {
const propData = prop.get() as IAProperty;
if (propData && propData.hasClass) {
const propValue = passify((propData.value || propData.idRef) as string); // one of the two must be present according to the schema
const propClass = passify(propData.hasClass);
propertiesHTML += `<dt>${propClass}</dt><dd>${propValue}</dd>`;
}
}
}
propertiesHTML += metadataToHTML(entity, lang);
propertiesHTML += '</dl></div>';
const propertiesHTML = propertiesToHTML(entity, lang);

return `<div class="meta-anEntity">
<div class="col-main" style="flex: 0 0 ${widthMain};">
${titleText ? `<h3 class="meta-title">${titleText}</h3>` : ''}
${descText ? `<div class="meta-description">${descText}</div>` : ''}
</div>
<div class="col-right" >
<div class="col-right" ><dl class="dl-horizontal">
${propertiesHTML}
</div>
</dl></div>
</div>`;
}

Expand Down Expand Up @@ -347,3 +331,19 @@ function metadataToHTML(item: TPigAnElement, lang: tagIETF): string {
+ (item.revision && item.revision.length > 0 ? `<dt>Revision</dt><dd>${passify(item.revision)}</dd>` : '')
+ (item.priorRevision && item.priorRevision.length > 0 ? `<dt>Prior Revisions</dt><dd>${item.priorRevision.map((r: string) => passify(r)).join(', ')}</dd>` : '');
}
function propertiesToHTML(el: TPigAnElement, lang: tagIETF): string {
let propertiesHTML = '';
if (el.hasProperty?.length > 0) {
// the configured properties:
for (const prop of el.hasProperty) {
const propData = prop.get() as IAProperty;
if (propData && propData.hasClass) {
const propValue = passify((propData.value || propData.idRef) as string); // one of the two must be present according to the schema
const propClass = passify(propData.hasClass);
propertiesHTML += `<dt>${propClass}</dt><dd>${propValue}</dd>`;
}
}
}
propertiesHTML += metadataToHTML(el, lang);
return propertiesHTML;
}
9 changes: 6 additions & 3 deletions src/common/export/jsonld/getJSONLD.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
*/

import { DEF } from '../../lib/definitions';
import { JsonObject, JsonValue, JsonArray, LIB, LOG } from '../../lib/helpers';
import { JsonObject, JsonValue, JsonArray, LOG } from '../../lib/helpers';
import { MVF } from '../../lib/mvf';
import {
TPigId, TPigItem, PigItem, PigItemType, PigItemTypeValue,
Expand Down Expand Up @@ -122,9 +122,12 @@
* @returns JSON-LD representation with @context and @graph
*/
static aPackage(pkg: APackage, options?: IOptionsJSONLD): JsonObject {
const filterTypes = options?.itemType;
// const filterTypes = options?.itemType;

const jld = this.getAsJSONLD(pkg, options);
let jld = this.getAsJSONLD(pkg, options);

jld = this.xConfigurablesToJSONLD(jld, pkg, 'hasProperty');
jld = this.xConfigurablesToJSONLD(jld, pkg, 'hasTargetLink');

jld['@context'] = xContextToJSONLD(pkg);
jld['@graph'] = xGraphToJSONLD(pkg);
Expand Down Expand Up @@ -485,7 +488,7 @@
}
return out;
}
private static getAsJSONLD(itm: TPigItem, options?: IOptionsJSONLD): JsonObject {

Check warning on line 491 in src/common/export/jsonld/getJSONLD.ts

View workflow job for this annotation

GitHub Actions / build (24.4.0)

'options' is defined but never used

Check warning on line 491 in src/common/export/jsonld/getJSONLD.ts

View workflow job for this annotation

GitHub Actions / build (24.4.0)

'options' is defined but never used
const jld = MVF.renameJsonTags(itm.get() as unknown as JsonObject, MVF.toJSONLD, { mutate: false }) as JsonObject;
return this.makeIdObjects(jld) as JsonObject;
}
Expand Down
90 changes: 64 additions & 26 deletions src/common/export/ttl/getTTL.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,16 @@
* - Use a class with static methods for better organization and extensibility.
* - Follow the same pattern as getJSONLD for consistency.
* - For creating a Turtle representation call getTTL(item, options) instead of item.getTTL().
* - Entity and Relationship classes declare their properties and links via shapes
* - ... but Property and Link classes do not declare their domain to avoid a potentially huge union.
* - At first, only cas: ontology terms get a shape.
* - Those cas: shapes will be served from the same URL as the terms themselves
* - Do not add rdfs:domain for links, as it can be inferred from relationships.
* - On TTL export, the configurable properties will be added to the ontology declaration.
* - ... and the configurable links will be added to the package class declaration.
*
* ToDo:
* - Add itemTypes as superClasses for all ontolology classes (Property, Link, Entity, Relationship, Enumeration)
* - ... with definitions for enumeratedProperty, enumeratedSourceLink, enumeratedTargetLink, enumeratedEndpoint.
*/

import { DEF, RE } from '../../lib/definitions';
Expand Down Expand Up @@ -134,7 +140,10 @@
let ttl = '';

// Add prefix definitions
ttl += this.xContext(pkg, rdf);
ttl += this.xContext(pkg, rdf, options);

// Add ontology definition
ttl += this.makeOntologyDefinition(pkg, rdf, options);

// Add package metadata
ttl += this.xMetadataForInstances(pkg, rdf, options);
Expand Down Expand Up @@ -418,12 +427,13 @@
* Transform context from internal INamespace[] format to Turtle @prefix format
* @param pkg - APackage instance
* @param rdf - CToTtl instance for building Turtle output
* @param options - Export options
* @returns Turtle @prefix declarations
*
* Internal format: context = [{ tag: "cas:", uri: "https://..." }, ...]
* Turtle format: @prefix cas: <https://...> .
*/
private static xContext(pkg: APackage, rdf: CToTtl): string {
private static xContext(pkg: APackage, rdf: CToTtl, options?: IOptionsTTL): string {

Check warning on line 436 in src/common/export/ttl/getTTL.ts

View workflow job for this annotation

GitHub Actions / build (24.4.0)

'options' is defined but never used

Check warning on line 436 in src/common/export/ttl/getTTL.ts

View workflow job for this annotation

GitHub Actions / build (24.4.0)

'options' is defined but never used
const ctx = pkg.context;

if (!ctx || !Array.isArray(ctx)) {
Expand Down Expand Up @@ -474,10 +484,35 @@
return ttl;
}

private static makeOntologyDefinition(
pkg: APackage,
rdf: CToTtl,
options?: IOptionsTTL

Check warning on line 490 in src/common/export/ttl/getTTL.ts

View workflow job for this annotation

GitHub Actions / build (24.4.0)

'options' is defined but never used

Check warning on line 490 in src/common/export/ttl/getTTL.ts

View workflow job for this annotation

GitHub Actions / build (24.4.0)

'options' is defined but never used
): string {
let ttl = '';

// Item ID as subject
const subjectId = this.formatTurtleId(pkg.id + '_ontology');
ttl += rdf.tab0(subjectId);
ttl += rdf.tab1('a', 'owl:Ontology');

// Add configured properties from the package (e.g., dcterms:contributor, dcterms:license)
if (LIB.isArrayWithContent(pkg.hasProperty)) {
ttl += this.xProperties(pkg.hasProperty, rdf);
}

// Imports (optional) - if the package has contained packages, add them as owl:imports

ttl += rdf.tab1('owl:versionInfo', DEF.pigVersion);
ttl += rdf.newLine();
return ttl;
}

/**
* Transform metadata to Turtle format - common base function for both instances and classes
* @param itm - Item with Identifiable properties (id, itemType, title, description, etc.)
* @param rdf - CToTtl instance for building Turtle output
* Note: instance-/class-specific optional triples (e.g. cas:itemType) are handled by the wrapper methods.
* @returns Turtle representation of common metadata properties
*/
Comment thread
Copilot marked this conversation as resolved.
private static xMetadata(
Expand Down Expand Up @@ -1399,46 +1434,49 @@

let ttl = '';

// Local helper function to add property constraints
const addPropertyConstraints = (itemIds: TPigId[]): void => {
for (const itemId of itemIds) {
// Check if item belongs to an external/context ontology
if (LIB.isContextId(itemId) || LIB.isHostedOntologyId(itemId)) {
// Create inline property constraint for external ontology items
const itemPath = this.formatTurtleId(itemId);
ttl += rdf.tab1('sh:property', `[ sh:path ${itemPath} ]`);
} else {
// Reference the item's shape (which should be defined separately) for CASCaRA items
const itemShapeId = this.formatTurtleId(itemId) + DEF.suffixShape;
ttl += rdf.tab1('sh:property', itemShapeId);
}
}
};

// Create shape ID by appending 'Shape' to the element ID
const shapeId = this.formatTurtleId(elem.id) + DEF.suffixShape; // for CASCaRA ontology terms
const elemId = this.formatTurtleId(elem.id);
const shapeId = elemId + DEF.suffixShape; // for CASCaRA ontology terms
ttl += rdf.tab0(shapeId);
ttl += rdf.tab1('a', 'sh:NodeShape');
ttl += rdf.tab1('sh:targetClass', this.formatTurtleId(elem.id));
ttl += rdf.tab1('sh:targetClass', elemId);

// For entity instances: require either rdfs:label or rdfs:comment
// For relationship instances: both are optional
if (!isRelationship) {
ttl += rdf.tab1('sh:or', '( [ sh:path rdfs:label ; sh:minCount 1 ] [ sh:path rdfs:comment ; sh:minCount 1 ] )');
}

// List enumerated properties - each property should have its own PropertyShape
if (LIB.isArrayWithContent(elem.enumeratedProperty)) {
const properties = elem.enumeratedProperty as TPigId[];
for (const propId of properties) {
// Reference the property's shape (which should be defined separately)
const propShapeId = this.formatTurtleId(propId) + DEF.suffixShape;
ttl += rdf.tab1('sh:property', propShapeId);
}
// List enumerated properties
// In case of a package class, we skip enumeratedProperty constraints, as configurable properties will be appended to the ontology
if (LIB.isArrayWithContent(elem.enumeratedProperty) && elemId != 'cas:Package') {
addPropertyConstraints(elem.enumeratedProperty as TPigId[]);
}

// List enumerated source links - only for Relationship
if (isRelationship && 'enumeratedSourceLink' in elem && LIB.isArrayWithContent(elem.enumeratedSourceLink)) {
const links = elem.enumeratedSourceLink as TPigId[];
for (const linkId of links) {
// Reference the link's shape (which should be defined separately)
const linkShapeId = this.formatTurtleId(linkId) + DEF.suffixShape;
ttl += rdf.tab1('sh:property', linkShapeId);
}
if (isRelationship && LIB.isArrayWithContent(elem.enumeratedSourceLink)) {
addPropertyConstraints(elem.enumeratedSourceLink as TPigId[]);
}

// List enumerated target links - both Entity and Relationship can have these
if (LIB.isArrayWithContent(elem.enumeratedTargetLink)) {
const links = elem.enumeratedTargetLink as TPigId[];
for (const linkId of links) {
// Reference the link's shape (which should be defined separately)
const linkShapeId = this.formatTurtleId(linkId) + DEF.suffixShape;
ttl += rdf.tab1('sh:property', linkShapeId);
}
addPropertyConstraints(elem.enumeratedTargetLink as TPigId[]);
}

return ttl + rdf.newLine();
Expand Down
2 changes: 1 addition & 1 deletion src/common/lib/definitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export interface IHostedOntology {
}

const CAS_DOMAIN = 'http://product-information-graph.org/';
const CAS_VERSION = '2026-05-08';
const CAS_VERSION = '2026-07-03';
const CAS_NS = 'cas:';
const DCMI_NS = 'dcterms:';

Expand Down
14 changes: 14 additions & 0 deletions src/common/lib/platform-independence.ts
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,20 @@ export const PLI = {
getExtension(filename: string): string {
const ext = filename.split('.').pop();
return ext ? `.${ext}` : '';
},

makeUUID(): string {
if (typeof crypto !== 'undefined' && crypto.randomUUID) {
return crypto.randomUUID();
}
// Copilot: Math.random(), which can produce low - entropy UUIDs and increases collision risk
// (and is not suitable if any caller later uses UUIDs for security - relevant identifiers).
// Prefer a crypto.getRandomValues - based fallback when randomUUID is unavailable.
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
const r = crypto.getRandomValues(new Uint8Array(1))[0] % 16;
const v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
};
/**
Expand Down
Loading
Loading