npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@aas-core-works/aas-core3.0-typescript

v1.0.3

Published

Manipulate, verify and de/serialize Asset Administration Shells based on the version 3.0 of the meta-model.

Downloads

992

Readme

aas-core3.0-typescript

Manipulate, verify and de/serialize Asset Administration Shells based on the version 3.0 of the meta-model.

CI Coverage Status

This is a software development kit (SDK) to:

  • manipulate,
  • verify, and
  • de/serialize to and from JSON

… Asset Administration Shells based on the version 3.0 of the meta-model.

For a brief introduction, see Getting Started.

For a detailed documentation of the API, see API Documentation.

We documented most of the rationale behind the implementation and interface choices in the section Design Decisions.

If you want to contribute, see our Contributing Guide.

Please see the Changelog for the list of changes between versions.

Getting Started

Here's a quick intro to get you started with the SDK. See how you can:

Install the SDK

The SDK is available as the npm package @aas-core-works/aas-core3.0-typescript.

Install it using npm:

npm install @aas-core-works/aas-core3.0-typescript

Create, Get and Set

The module types defines all the data types of the meta-model. This includes classes, interfaces and enumerations.

The module types also contains abstract visitors and transformers, but we will write more about them in Iterate and Transform section.

Creation

We use constructors to create an AAS model.

Usually you start bottom-up, all the way up to the types.Environment.

Getting and Setting Properties

All properties of the classes are modeled as TypeScript properties.

After initialization of a class, you can directly get and modify its properties.

The properties which are not set should be assigned null. To avoid confusion and unnecessary complexity, the SDK does not expect undefined property values.

Getters with a Default Value

For optional properties which come with a default value, we provide special getters, {property name}OrDefault. If the property is null, this getter will give you the default value. Otherwise, if the property is set, the actual value of the property will be returned.

For example, see types.IHasKind.kindOrDefault.

Example: Create an Environment with a Submodel

Here is a very rudimentary example where we show how to create an environment which contains a submodel.

The submodel will contain two elements, a property and a blob.

import * as aas from "@aas-core-works/aas-core3.0-typescript";

// Create the first element
const someElement = new aas.types.Property(
  aas.types.DataTypeDefXsd.Int
);
someElement.idShort = "someProperty";
someElement.value = "1984";

// Create the second element
const anotherElement = new aas.types.Blob(
  "application/octet-stream"
);
anotherElement.idShort = "someBlob";
anotherElement.value = new Uint8Array([0xDE, 0xAD, 0xBE, 0xEF]);

// You can directly access the element properties.
anotherElement.value = new Uint8Array([0xDE, 0xAD, 0xC0, 0xDE]); 

// Nest the elements in a submodel
const submodel = new aas.types.Submodel(
  "some-unique-global-identifier"
);
submodel.submodelElements = [
  someElement,
  anotherElement
];

// Now create the environment to wrap it all up
const environment = new aas.types.Environment();
environment.submodels = [submodel];

// You can access the propreties from the children as well.
(<aas.types.Blob>environment.submodels![0].submodelElements![1]).value =
  new Uint8Array([0xC0, 0x01, 0xCA, 0xFE]);

// Now you can do something with the `environment`...

Switch on Runtime Types

JavaScript, and consequently TypeScript, do not support multiple inheritance. We therefore introduce only a single, most general abstract class types.Class. What is defined as "abstract class" in the meta-model, we implement as interfaces in TypeScript.

All the concrete classes inherit from types.Class and specify which interfaces ("abstract classes") they implement.

While TypeScript allows us to define interfaces, they are merely used to assure type safety at compile time, but cannot be used for type switches at runtime. This has repercussions, for example, when you want to select submodel elements which are of type types.Property. TypeScript does not provide an efficient way to check the runtime type based on interfaces alone.

To allow for efficient type casts and checks, we implement functions as{class name} and is{class name} for all the classes of the meta-model. The implementation is based on transformer pattern (see Section Iterate and Transform below), and performs only a couple of dispatch function calls.

The functions as{class name} allow you to try a cast to a given type. If the cast is possible, the input is simply returned. Otherwise, the function returns null.

Here is a short example with types.asProperty and types.asBlob:

import * as aas from "@aas-core-works/aas-core3.0-typescript";

// Create the first element
const someElement = new aas.types.Property(
  aas.types.DataTypeDefXsd.Int
);
someElement.idShort = "someProperty";
someElement.value = "1984";

console.log(aas.types.asProperty(someElement) === someElement)
// Prints: true

console.log(aas.types.asBlob(someElement) === null);
// Prints: true

The functions is{class name} provide you with runtime type checks. Thanks to TypeScript type assertions provided in its signature, the TypeScript compiler can automatically infer the appropriate type on successful checks.

Here is a short example with types.isProperty and types.isBlob:

import * as aas from "@aas-core-works/aas-core3.0-typescript";

// Create the first element
const someElement = new aas.types.Property(
  aas.types.DataTypeDefXsd.Int
);
someElement.idShort = "someProperty";
someElement.value = "1984";

console.log(aas.types.isProperty(someElement))
// Prints: true

console.log(aas.types.isBlob(someElement));
// Prints: false

if (aas.types.isProperty(someElement)) {
  // TypeScript compiler will automatically infer that `someElement`
  // is a `types.Property` thanks to type assertions from
  // `types.isProperty`.
  console.log(someElement.value);
  // Prints: 1984
}

The is* and as* functions assume that you know the expected type in the check ahead of time. This does not work if you use a prototype to define a type, or want to check whether two instances share the same type, since you can not extract the type information from an instance. For those situations, we provide [typesMatch] function:

import * as aas from "@aas-core-works/aas-core3.0-typescript";

// Create a property
const aProperty = new aas.types.Property(
  aas.types.DataTypeDefXsd.Int
);

// Create a blob
const aBlob = new aas.types.Blob(
  "text/plain"
);

// Create another property
const anotherProperty = new aas.types.Property(
  aas.types.DataTypeDefXsd.Decimal
);

// Check the type matches

console.log(aas.types.typesMatch(aProperty, aProperty))
// Prints: true

console.log(aas.types.typesMatch(aProperty, aBlob))
// Prints: false

console.log(aas.types.typesMatch(aProperty, anotherProperty))
// Prints: true

Iterate and Transform

The SDK provides various ways how you can loop through the elements of the model, and how these elements can be transformed. Each following section will look into one of the approaches.

over*OrEmpty

For all the optional lists, there is a corresponding over{property name}OrEmpty getter. It gives you an IterableIterator. If the property is not set (i.e. set to null), this getter will yield empty. Otherwise, it will yield from the actual property value.

For example, see types.Environment.overSubmodelsOrEmpty.

descend and descendOnce

If you are writing a simple script and do not care about the performance, the SDK provides two methods in the most abstract class types.Class, descendOnce and descend, which you can use to loop through the instances.

Both descendOnce and descend iterate over referenced children of an instance of types.Class. The method descendOnce, as it names suggests, stops after all the immediate children has been iterated over. The method descend continues recursively to grand-children, grand-grand-children etc.

Here is a short example how you can get all the properties from an environment whose ID-short starts with another:

import * as aas from "@aas-core-works/aas-core3.0-typescript";

// Prepare the environment
const someElement = new aas.types.Property(
  aas.types.DataTypeDefXsd.Int
);
someElement.idShort = "someProperty";
someElement.value = "1984";

const anotherElement = new aas.types.Property(
  aas.types.DataTypeDefXsd.Int
);
anotherElement.idShort = "anotherProperty";
anotherElement.value = "1985";

const yetAnotherElement = new aas.types.Property(
  aas.types.DataTypeDefXsd.Int
);
yetAnotherElement.idShort = "yetAnotherProperty";
yetAnotherElement.value = "1986";

const submodel = new aas.types.Submodel(
  "some-unique-global-identifier"
);
submodel.submodelElements = [
  someElement,
  anotherElement,
  yetAnotherElement
];

const environment = new aas.types.Environment();
environment.submodels = [submodel];

// Iterate using `descend`
for (const something of environment.descend()) {
  if (
    aas.types.isProperty(something)
    && something.idShort?.toLowerCase().includes("another")
  ) {
    console.log(something.idShort);
  }
}

// Prints:
// anotherProperty
// yetAnotherProperty

Iteration with descendOnce and descend works well if the performance is irrelevant. However, if the performance matters, this is not a good approach. First, all the children will be visited (even though you need only a small subset). Second, you execute the loop body on every single instance in the loop. In the example above, you check the runtime type with types.isProperty on every single instance referenced from the types.Environment.

Let’s see in the next section how we could use a more efficient, albeit also a more complex approach.

Visitor

Visitor pattern is a common design pattern in software engineering. We will not explain the details of the pattern here as you can read about in the ample literature in books or in Internet.

The cornerstone of the visitor pattern is double dispatch: instead of casting to the desired type during the iteration, the method accept of types.Class directly dispatches to the appropriate visitation method.

This allows us to spare runtime type switches and directly dispatch the execution. The SDK already implements accept methods, so you only have to implement the visitor.

The visitor class has a visiting method for each class of the meta-model. In the SDK, we provide different flavors of the visitor abstract classes which you can readily implement:

Let us re-write the above example related to descend method with a visitor pattern:

import * as aas from "@aas-core-works/aas-core3.0-typescript";

// Prepare the environment
const someElement = new aas.types.Property(
  aas.types.DataTypeDefXsd.Int
);
someElement.idShort = "someProperty";
someElement.value = "1984";

const anotherElement = new aas.types.Property(
  aas.types.DataTypeDefXsd.Int
);
anotherElement.idShort = "anotherProperty";
anotherElement.value = "1985";

const yetAnotherElement = new aas.types.Property(
  aas.types.DataTypeDefXsd.Int
);
yetAnotherElement.idShort = "yetAnotherProperty";
yetAnotherElement.value = "1986";

const submodel = new aas.types.Submodel(
  "some-unique-global-identifier"
);
submodel.submodelElements = [
  someElement,
  anotherElement,
  yetAnotherElement
];

const environment = new aas.types.Environment();
environment.submodels = [submodel];

// Implement the visitor
class Visitor extends aas.types.PassThroughVisitor {
  visitProperty(that: aas.types.Property): void {
    if (that.idShort?.toLowerCase().includes("another")) {
      console.log(that.idShort);
    }
  }
}

// Iterate
const visitor = new Visitor();
visitor.visit(environment);

// Prints:
// anotherProperty
// yetAnotherProperty 

There are important differences to iteration with descend:

  • Due to double dispatch, we spare a cast. This is usually more efficient.
  • The iteration logic in descend lives very close to where it is executed. In contrast, the visitor needs to be defined as a separate class. While sometimes faster, writing the visitor makes the code less readable.

Descend or Visitor?

In general, people familiar with the visitor pattern and object-oriented programming will prefer, obviously, visitor class. People who like functional programming, generator expressions and ilks will prefer descend.

It is difficult to discuss different tastes, so you should probably come up with explicit code guidelines in your code and stick to them.

Make sure you always profile before you sacrifice readability and blindly apply one or the other approach for performance reasons.

Transformer

A transformer pattern is an analogous to visitor pattern, where we "transform" the visited element into some other form (be it a string or a different object). It is very common in compiler design, where the abstract syntax tree is transformed into a different representation.

The SDK provides different flavors of a transformer:

Usually you implement for each concrete class how it should be transformed. If you want to specify only a subset of transformations, and provide the default value for the remainder, the SDK provides TransformerWithDefault and TransformerWithDefaultAndContext.

We deliberately omit an example due to the length of the code. Please let us know by [creating an issue] if you would like to have an example here.

Iterate over Enumeration Literals

TypeScript features enumerations as a core part of the language. However, the enumerations are not supported in JavaScript, and it is up to the transpiler how they are going to be represented in JavaScript.

We use numeric literals to capture the enumerations from the meta-model (see Section design decisions). While there are ways to iterate over enumerations, the code is not particularly type-safe when used with numeric enumeration literals. For example, the literals are often iterated as strings even though the enumeration literals are originally given as integers. Moreover, all the code snippets looked rather confusing to us. We therefore provide over{enumeration name} functions in types module which you can use to iterate over enumeration literals. See, for example, [types.overAasSubmodelElements].

If you want to obtain the string representation of the literal, we provide the [stringification] module. The functions stringification.{enumeration name}ToString gives you back either the string representation of the literal, or null if the literal was invalid. For the client's convenience, our SDK also implements the functions stringification.must{enumeration name}ToString which returns the string representation, or throws an error. If you are certain that your code deals with only correct literals, stringification.must{enumeration name}ToString will spare you a nullability check. For example, see stringification.modelingKindToString and stringification.mustModelingKindToString.

Here is a short example that illustrates how to loop over enumeration literals of the enumeration types.ModelingKind using the function types.overModelingKind:

import * as aas from "@aas-core-works/aas-core3.0-typescript";

for (const literal of aas.types.overModelingKind()) {
  const asString = aas.stringification.mustModelingKindToString(literal);
  console.log(
    `${literal} ${typeof (literal)} ${asString}`
  );
}
// Prints:
// 0 number Template
// 1 number Instance

Verify

Our SDK allows you to verify that a model satisfies the constraints of the meta-model.

The verification logic is concentrated in the module verification, and all it takes is a call to verification.verify function. The function verification.verify will check that constraints in the given model element are satisfied, including the recursion into children elements. The function returns an IterableIterator of verification.VerificationError's, which you can use for further processing (e.g., report to the user).

Here is a short example snippet:

import * as aas from "@aas-core-works/aas-core3.0-typescript";

// Prepare the environment
const someElement = new aas.types.Property(
  aas.types.DataTypeDefXsd.Int
);
// The ID-shorts must be proper variable names,
// but there is a dash (`-`) in this ID-short. 
someElement.idShort = "some-property";
someElement.value = "1984";

const submodel = new aas.types.Submodel(
  "some-unique-global-identifier"
);
submodel.submodelElements = [someElement];

const environment = new aas.types.Environment();
environment.submodels = [submodel];

for (const error of aas.verification.verify(environment)) {
  console.log(`${error.path}: ${error.message}`);
}

// Prints:
// .submodels[0].submodelElements[0].idShort: ID-short of Referables 
// shall only feature letters, digits, underscore (``_``); 
// starting mandatory with a letter. *I.e.* ``[a-zA-Z][a-zA-Z0-9_]+``.

If you only want to check the immediate instance, and you do not want the verification to recurse into children, supply the second parameter recurse set to false to the call of verification.verify:

for (const error of verification.verify(environment, false)) {
  console.log(`${error.path}: ${error.message}`);
}

// Does not print anything as environment instance for itself
// is valid. However, the submodel elements beneath the environment
// are invalid, but this verification is not recursive.

Limit the Number of Reported Errors

Since the function verification.verify gives you an IterableIterator, you can simply break out of the loop.

For example, to report only the first 10 errors (assuming the code from the example above):

let reportedErrors = 0;

for (const error of verification.verify(environment)) {
  console.log(`${error.path}: ${error.message}`);
  reportedErrors++;
  
  if (reportedErrors === 10) {
    break;
  }
}

Omitted Constraints

Not all constraints specified in the meta-model can be verified. Some constraints require external dependencies such as an AAS registry. Verifying the constraints with external dependencies is out-of-scope of our SDK, as we still lack standardized interfaces to those dependencies.

However, all the constraints which need no external dependency are verified. For a full list of exception, please see the description of the module types.

JSON de/serialization

Our SDK handles the de/serialization of the AAS models from and to JSON format through the module jsonization.

Serialize

To serialize, you call the function jsonization.toJsonable on an instance of types.Class which will convert it to a JSON-able JavaScript object.

Here is a snippet that converts the environment first into a JSON-able object, and next converts the JSON-able object to text:

import * as aas from "@aas-core-works/aas-core3.0-typescript";

// Prepare the environment
const someElement = new aas.types.Property(
  aas.types.DataTypeDefXsd.Int
);
someElement.idShort = "someProperty";
someElement.value = "1984";

const submodel = new aas.types.Submodel(
  "some-unique-global-identifier"
);
submodel.submodelElements = [someElement];

const environment = new aas.types.Environment();
environment.submodels = [submodel];

// Serialize to a JSON-able object
const jsonable = aas.jsonization.toJsonable(environment);

// Convert the JSON-able object to a string
const text = JSON.stringify(jsonable, null, 2);

console.log(text);
// Prints:
// {
//   "submodels": [
//     {
//       "id": "some-unique-global-identifier",
//       "submodelElements": [
//         {
//           "idShort": "some_property",
//           "valueType": "xs:int",
//           "value": "1984",
//           "modelType": "Property"
//         }
//       ],
//       "modelType": "Submodel"
//     }
//   ]
// }

De-serialize

Our SDK can convert a JSON-able object back to an instance of types.Class. To that end, you call the appropriate function jsonization.{class name}FromJsonable. For example, if you want to de-serialize an instance of types.Environment, call jsonization.environmentFromJsonable.

Note that the SDK cannot de-serialize classes automatically as the discriminator property modelType is not included in the serializations for all the classes. Without the discriminator property provided, we thus cannot know the actual type of the instance just from the serialization. See this sections on discriminators in AAS Specs for more details.

The functions jsonization.{class name}FromJsonable return an "either" structure: either the successfully de-serialized instance, or a de-serialization error, if there was any. If there was an error, its property error will be set. Otherwise, the property value will contain the de-serialized instance. If you prefer an exception to be thrown in case of de-serialization errors, and do not want to check for error explicitly, then call the method mustValue.

We use the "either" structure (sometimes also called "disjoint union") instead of exceptions to avoid the costly stack unwinding. Stack unwinding makes sense if you want since the line of code is irrelevant in case of de-serialization errors.

Here is an example snippet to show you how to de-serialize an instance of types.Environment:

import * as aas from "@aas-core-works/aas-core3.0-typescript";

const text = `
{
  "submodels": [
    {
      "id": "some-unique-global-identifier",
      "submodelElements": [
        {
          "idShort": "someProperty",
          "valueType": "xs:boolean",
          "modelType": "Property"
        }
      ],
      "modelType": "Submodel"
    }
  ]
}
`;

const jsonable = JSON.parse(text);

const instanceOrError = aas.jsonization.environmentFromJsonable(
  jsonable
);
if (instanceOrError.error !== null) {
  console.log(
    "De-serialization failed: " +
    `${instanceOrError.error.path}: ` +
    `${instanceOrError.error.message}`
  );
}
// Doesn't print anything as `text` is
// a valid representation.

const environment = instanceOrError.mustValue();

for (const something of environment.descend()) {
  console.log(something.constructor.name);
}
// Prints:
// Submodel
// Property

API

For a detailed documentation of the API, see API documentation.

Design Decisions

We present here some of the choices we made during the design and implementation of the SDK. While it is not necessary to understand our thread of thought to use the SDK, we explain the rationale here behind why we structured and programmed the SDK the way we did. This should hopefully clear up some confusion, or ease the frustration, if you prefer certain features to be implemented differently.

Enumerations as Numbers

We optimize the enumerations for look-ups and comparisons instead of string representation. Thus, we implement literals as numbers (instead of strings). For example, this makes lookups faster as hash values are directly computed on a numeric literal involving usually only a few arithmetic operations.

In contrast, if the enumeration literals were listed as strings, the hash value of the literal would need to be computed by iterating through all the characters of the string.

No Parameter Properties in Constructors

Parameter properties in constructor signatures are a succinct way to define public and private properties of a class.

As we use TSDoc to write documentation, documenting the properties in the constructor hurts the readability. Therefore, we generate properties (with documentation) separately from constructors in the class body.

Inheritance Hierarchy Different From Meta-model

The AAS meta-model uses multiple inheritance. However, TypeScript only supports single inheritance. Moreover, in case of long inheritance chains, the type checks with instanceof might be linear in time complexity. Please see, for example, this StackOverflow question about the efficiency of instanceof

Instead of multiple inheritance we use interfaces and provide is* and as* functions to dynamically decide the instance type at runtime. All the classes inherit from the most general class types.Class. Please see Section Switch on Runtime Types.

Use of Either Construct

We use "either" structure in de-serialization since JavaScript engines are not guaranteed to be optimized for try-catch blocks. See, for example, this StackOverflow question about the efficiency of try/catch blocks.

By using Either, we can do away with try/catch blocks and shave off quite a few cycles. See Section De-serialize for more information.

No XML

We do not implement XML as we could not find a solid library as of time of this writing (2022-12-21) which works both for NodeJS and in the browsers. The closest we got is sax.js, but it seems not maintained anymore (see this issue in sax.js repository about maintenance). There are multiple forks, such as saxes, but they seem to have much lower visibility and attention.

We are open to suggestions, and we are of course ready to re-evaluate our current decision to skip XML de/serialization of AAS models.

Bytes as Uint8Array

There are various ways how to implement an array of bytes in TypeScript (and JavaScript). For example, we could use plain strings (with constraint that the code points are limited to the range [0, 255]). Another representation would be to use arrays of numbers (Array<number>) and restrict numbers to the range [0, 255]. There are many others.

We finally settled down on Uint8Array that felt most natural to us. Opposed to string and arrays of numbers, Uint8Array are usually implemented in a memory-efficient way (one byte per byte point, instead of 2 bytes for strings or 8 bytes for array of numbers).

However, Uint8Array's are immutable, so any changes involve a copy-on-write. We deemed such cases to be rare in applications, but this is open for a debate. Please create an issue if your application needs mutable byte arrays so that we can discuss the alternatives.

In-house Base64 De/coding

The base64 encoding is differently implemented for the browser and for nodejs. The browser platforms provide atob and btoa functions, while it is common to use Buffers in nodejs.

We implement our own base64 encoding and decoding as we wanted to have a single implementation across different platforms (e.g., browser and nodejs).

Additionally, we represent byte values as Uint8Array, so we also make sure that our base64 implementation directly feeds into Uint8Array's. For example, if we used atob we would have to convert the string into Uint8Array, which costs at least an additional memory copy.

Please note that our implementation also suffers from the lack of padding check (see this paper on padding check) as we followed widely used algorithms in the wild. According to the same paper on padding check, this vulnerability exists in standard libraries for Python, PHP, JavaScript, Node.js and others.

Contributing Guide

Issues

Please report bugs or feature requests by creating GitHub issues.

In Code

If you want to contribute in code, pull requests are welcome!

Please do create a new issue before you dive into coding. It can well be that we already started working on the feature, or that there are upstream or downstream complexities involved which you might not be aware of.

SDK Code Generation

The biggest part of the code has been automatically generated by aas-core-codegen. It probably makes most sense to change the generator rather than add new functionality. However, this needs to be decided on a case-by-case basis.

Test Code Generation

The majority of the unit tests has been automatically generated using the Python scripts in the testgen/ directory.

To re-generate the test code:

  • Create the virtual environment:

    python3 -m venv venv
  • Activate the virtual environment (on Windows):

    venv/Scripts/activate

    ... or on Linux/Mac:

    source venv/bin/activate
  • Install the development dependencies:

    pip3 install --editable testgen
  • Run the main script:

    python testgen/generate_all.py  

Test Data

The test data is automatically generated by aas-core3.0-testgen, and copied to this repository on every change.

Building the Documentation

We use TypeDoc to build the documentation:

npx typedoc --out doc-local src/

After this command, the documentation is available in doc-local directory.

Pre-commit Checks

Please run:

npm run lint && npm run build && npm run test

... before every commit.

To automatically re-format the code:

npm run format

Pull Requests

Feature branches. We develop using the feature branches, see this section of the Git book.

If you are a member of the development team, create a feature branch directly within the repository.

Otherwise, if you are a non-member contributor, fork the repository and create the feature branch in your forked repository. See this GitHub tutorial for more guidance.

Branch Prefix. Please prefix the branch with your Github user name (e.g., mristin/Add-some-feature).

Continuous Integration. GitHub will run the continuous integration (CI) automatically through GitHub actions. The CI includes running the tests, inspecting the code, re-building the documentation etc.

Commit Message

The commit messages follow the guidelines from https://chris.beams.io/posts/git-commit:

  • Separate subject from body with a blank line,
  • Limit the subject line to 50 characters,
  • Capitalize the subject line,
  • Do not end the subject line with a period,
  • Use the imperative mood in the subject line,
  • Wrap the body at 72 characters, and
  • Use the body to explain what and why (instead of how).

Changelog

1.0.3 (2024-04-16)

The dataSpecification field in EmbeddedDataSpecification is made optional, according to the book.

1.0.2 (2024-03-23)

In this patch version, we propagate the fix from abnf-to-regex related to maximum qualifiers which had been mistakenly represented as exact repetition before.

1.0.1 (2024-03-13)

This patch release brings about the fix for patterns concerning dates and date-times with zone offset 14:00 which previously allowed for a concatenation without a plus sign.

1.0.0 (2024-02-02)

This is the first stable release. The release candidates stood the test of time, so we are now confident to publish a stable version.

1.0.0-rc.3 (2023-09-08)

  • Update to aas-core-meta, codegen, testgen 4d7e59e, 7e264a0 and 9b43de2e (#13)

    Notably, this fixes constraints AASd-131 and AASc-3a-010, propagating the changes from aas-core-meta.

1.0.0-rc.2 (2023-06-28)

  • Update to aas-core-meta, codegen, testgen 44756fb, 607f65c, bf3720d7 (#7)

    This is an important patch propagating in particular the following fixes which affected the constraints and their documentation:

    • Pull requests in aas-core-meta 271, 272 and 273 which affect the nullability checks in constraints,
    • Pull request in aas-core-meta 275 which affects the documentation of many constraints.

1.0.0-rc.1

  • Initial version, ready for the first reviews