@appsemble/lang-sdk
Version:
Language SDK for Appsemble
174 lines • 6.85 kB
JavaScript
import { Validator, } from 'jsonschema';
import { schemas as allSchemas } from './index.js';
import { escapeJsonPointer } from './jsonPointer.js';
import { has } from './miscellaneous.js';
export class BaseValidatorFactory {
constructor({ customFormats, schemas }) {
this.customFormats = customFormats;
this.schemas = schemas;
}
build() {
const validator = new Validator();
for (const [key, format] of Object.entries(this.customFormats ?? {})) {
validator.customFormats[key] = format;
}
for (const [key, schema] of Object.entries(this.schemas)) {
const path = `#/components/schemas/${escapeJsonPointer(key)}`;
validator.addSchema(schema, path);
}
return validator;
}
}
BaseValidatorFactory.defaultOptions = {
base: '#',
nestedErrors: true,
};
BaseValidatorFactory.defaultCustomFormats = {
int32: () => true,
int64: () => true,
float: () => true,
double: () => true,
byte: () => true,
binary: () => true,
password: () => true,
};
export class BlockExampleValidator {
constructor() {
this.validator = new BaseValidatorFactory({
schemas: allSchemas,
customFormats: {
...BaseValidatorFactory.defaultCustomFormats,
fontawesome: () => true,
remapper: () => true,
action: () => true,
'event-listener': () => true,
'event-emitter': () => true,
},
}).build();
}
validate(example) {
const { required, ...blockSchema } = structuredClone(allSchemas.BlockDefinition);
delete blockSchema.properties?.name;
delete blockSchema.properties?.version;
const actionsSchema = blockSchema.properties.actions;
delete actionsSchema?.additionalProperties;
if (example.actions) {
actionsSchema.properties = Object.fromEntries(Object.keys(example.actions).map((key) => [
key,
{ $ref: '#/components/schemas/ActionDefinition' },
]));
}
const blockEventsSchema = {
type: 'object',
additionalProperties: false,
properties: {},
};
blockSchema.properties.events = blockEventsSchema;
if (example.events) {
if (example.events.emit) {
blockEventsSchema.properties.emit = has(example.events.emit, '$any')
? { type: 'object', additionalProperties: { type: 'string' } }
: {
type: 'object',
properties: Object.fromEntries(Object.keys(example.events.emit).map((emitter) => [emitter, { type: 'string' }])),
};
}
if (example.events.listen) {
blockEventsSchema.properties.listen = has(example.events.listen, '$any')
? { type: 'object', additionalProperties: { type: 'string' } }
: {
type: 'object',
properties: Object.fromEntries(Object.keys(example.events.listen).map((listener) => [
listener,
{ type: 'string' },
])),
};
}
}
const validationResult = this.validator.validate(example, blockSchema, BaseValidatorFactory.defaultOptions);
return validationResult;
}
}
export class BlockParamInstanceValidator {
constructor({ actions = [], emitters = [], listeners = [], } = {}) {
this.emitterNames = [];
this.listenerNames = [];
this.emitterNames = emitters;
this.listenerNames = listeners;
this.validator = new BaseValidatorFactory({
schemas: {},
customFormats: {
...BaseValidatorFactory.defaultCustomFormats,
// TODO: validate more
fontawesome: () => true,
// TODO: validate more
remapper: () => true,
action: (property) => actions.includes(property),
'event-listener': (property) => listeners.includes(property),
'event-emitter': (property) => emitters.includes(property),
},
}).build();
}
validateParametersInstance(instance, schema) {
const actionFormat = this.validator.customFormats.action;
const listenerFormat = this.validator.customFormats['event-listener'];
const emitterFormat = this.validator.customFormats['event-emitter'];
const actionsReferenced = new Set();
this.validator.customFormats.action = (property) => {
actionsReferenced.add(property);
return actionFormat(property);
};
this.validator.customFormats['event-listener'] = (property) => listenerFormat(property) && this.listenerNames.includes(property);
this.validator.customFormats['event-emitter'] = (property) => emitterFormat(property) && this.emitterNames.includes(property);
const result = this.validator.validate(instance, schema, {
...BaseValidatorFactory.defaultOptions,
base: undefined,
});
return [result, actionsReferenced];
}
}
export class BlockParamSchemaValidator {
constructor() {
this.validator = new BaseValidatorFactory({
schemas: allSchemas,
customFormats: {
...BaseValidatorFactory.defaultCustomFormats,
fontawesome: () => true,
remapper: () => true,
action: () => true,
'event-listener': () => true,
'event-emitter': () => true,
},
}).build();
}
validateParamSchema(paramSchema) {
return this.validator.validate(paramSchema, allSchemas.JSONSchemaRoot, BaseValidatorFactory.defaultOptions);
}
}
export class AppValidator {
constructor() {
this.validator = new BaseValidatorFactory({
schemas: allSchemas,
customFormats: {
...BaseValidatorFactory.defaultCustomFormats,
},
}).build();
}
validateApp(app) {
return this.validator.validate(app, allSchemas.AppDefinition, BaseValidatorFactory.defaultOptions);
}
}
export class RemapperValidator {
constructor() {
this.validator = new BaseValidatorFactory({
schemas: allSchemas,
customFormats: {
...BaseValidatorFactory.defaultCustomFormats,
},
}).build();
}
validateRemapper(remapper) {
return this.validator.validate(remapper, allSchemas.RemapperDefinition, BaseValidatorFactory.defaultOptions);
}
}
//# sourceMappingURL=BasicValidator.js.map