@hn3000/metamodel
v1.10.0
Published
Meta Model for TypeScript and EcmaScript apps, to help with JSON schema processing and UI construction.
Downloads
122
Readme
Meta Model for JavaScript Apps
Coming from statically typed languages like Pascal, C and C++, I always wanted to have more strict typings for my JavaScript classes. While TypeScript allows me to describe the data using interfaces, I feel it lacks RTTI -- runtime information about the objects involved that would allow me to construct user interfaces dynamically and validate incoming data (either from some remote connection or typed in by a user) against the type information.
This is my attempt at building something to fill this gap.
A metamodel is created like this:
const sampleObject = {
lala: 12,
blah: "Some String",
blub: 3.1415 * 12
};
const modelTypes = require("metamodel").modelTypes;
const model = modelTypes.addObjectType('sample')
.addItem('lala', modelTypes.type('number/int'))
.addItem('blah', modelTypes.type('string'))
.addItem('blub', modelTypes.type('number'));
Once the model exists it in the modelTypes registry, it can always be referred to by name:
const model = modelTypes.type('sample');
And input data can be validated by the model:
const inputData = {
lala: "12",
blah: "Another String",
blub: 27.12
};
const context = modelTypes.createParseContext(inputData, model);
context.allowConversion = true;
const isValid = context.validate();
if (context.warnings.length) {
console.warn(`context.messages.map(e => e.msg).join(', ')`);
}
if (context.errors.length) {
console.error(`context.errors.map(e => e.msg).join(', ')`);
throw new Error('Validation failed');
}
// turn text input into actual objects
const parsedInput = context.parse();
At this point, context will contain warnings and errors if the data does not fit the metamodel. With allowConversion=true, the metamodel will parse "12" into a number, if allowConversion=false, the string value will result in an error message.
Using JSON Schema
Parsing a JSON schema into a metamodel uses a special kind of IModelTypeRegistry, which can not only parse the schemas, but also acts as a registry for the parsed schemas:
const schemaResponse = await fetch(schemaUrl);
const schema = await schemaResponse.json();
const models = new ModelSchemaParser();
const model = models.parseSchemaObject(schema) as IModelTypeComposite<any>;
// just a different way to get at the model for this schema:
const modelByName = models.type(schema.id);
And that model can then be used the same as shown above.
You can also provide some defaults for the parser to amend imported schemas with some useful defaults, like e.g. a regex for all strings:
const models = new ModelSchemaParser(undefined, {
strings: { pattern: /^[^\u0000-\u001f\u007f-\u009f\"<>\{\}\[\]]*$/ },
});
const model = models.parseSchemaObject(schema) as IModelTypeComposite<any>;