@kitmi/types
Version:
JavaScript semantic data types
250 lines (249 loc) • 8.03 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
TypeSystem: function() {
return TypeSystem;
},
Types: function() {
return Types;
},
addPlugin: function() {
return addPlugin;
},
addType: function() {
return addType;
},
charsets: function() {
return charsets;
},
createTypeSystem: function() {
return createTypeSystem;
},
default: function() {
return _default;
}
});
const _errors = require("./errors");
function _define_property(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
let counter = 0;
const defaultTypeClasses = [];
const defaultPlugins = [];
class TypeSystem {
static fromDefault() {
const ts = new TypeSystem();
defaultTypeClasses.forEach(({ name, TypeMeta })=>{
ts.addType(name, TypeMeta);
});
defaultPlugins.forEach(({ name, plugin })=>{
ts.addPlugin(name, plugin);
});
return ts;
}
addPlugin(name, plugin) {
this.plugins[name] = plugin;
}
removePlugin(name) {
delete this.plugins[name];
}
_addType(name, typeMeta) {
if (name in this.types) {
throw new _errors.ApplicationError(`Type "${name}" already exist.`, {
name
});
}
this.types[name] = typeMeta;
if (typeMeta.primitive) {
this.primitives.add(name);
}
if (typeMeta.scalar) {
this.scalarTypes.add(name);
}
}
addType(name, TypeMeta) {
const typeMeta = new TypeMeta(this);
typeMeta.sanitize = (value, meta, i18n, context)=>{
meta = {
type: typeMeta.name,
...meta
};
const opts = {
...typeof context === 'string' ? {
path: context
} : context,
rawValue: value,
i18n,
system: this
};
const [isDone, sanitized] = this.beginSanitize(value, meta, opts);
return this.endSanitize(isDone ? sanitized : typeMeta._sanitize(value, meta, opts), meta, opts);
};
typeMeta.sanitize_ = async (value, meta, i18n, context)=>{
meta = {
type: typeMeta.name,
...meta
};
const opts = {
...typeof context === 'string' ? {
path: context
} : context,
rawValue: value,
i18n,
system: this
};
const [isDone, sanitized] = await this.beginSanitize(value, meta, opts);
return this.endSanitize(isDone ? sanitized : typeMeta._sanitizeAsync ? await typeMeta._sanitizeAsync(value, meta, opts) : typeMeta._sanitize(value, meta, opts), meta, opts);
};
this._addType(name, typeMeta);
this._addType(typeMeta.name, typeMeta);
typeMeta.alias?.forEach((a)=>{
this._addType(a, typeMeta);
});
}
callByType(method) {
return (value, typeInfo, i18n, context)=>{
if (typeInfo.type == null) {
throw new _errors.InvalidArgument(`Missing type info: ${JSON.stringify(typeInfo)}`);
}
if (!this.primitives.has(typeInfo.type)) {
throw new _errors.InvalidArgument(`Unsupported primitive type: "${typeInfo.type}".`);
}
const typeObject = this.types[typeInfo.type];
return typeObject[method](value, typeInfo, i18n, context);
};
}
safeJsonStringify(value) {
const bigintWriter = this.plugins['bigintWriter'];
if (bigintWriter) {
const replacer = (_, value)=>typeof value === 'bigint' ? bigintWriter(value) : value;
return JSON.stringify(value, replacer);
}
return JSON.stringify(value);
}
getStringifier() {
const bigintWriter = this.plugins['bigintWriter'];
if (bigintWriter) {
return (value)=>typeof value === 'bigint' ? bigintWriter(value) : value.toString();
}
return null;
}
beginSanitize(value, meta, opts) {
if (value == null) {
if (meta.default != null) {
return [
true,
meta.default
];
} else if (meta.optional) {
return [
true,
null
];
}
throw new _errors.ValidationError('Missing a required value.', {
value: opts.rawValue,
path: opts.path
});
}
if (meta.const) {
if (meta.const !== value) {
throw new _errors.ValidationError('Invalid constant value.', {
value: opts.rawValue,
path: opts.path
});
}
}
if (meta.plain) return [
true,
value
];
// more prerequisites here ...
if (this.plugins.preProcess) {
return this.plugins.preProcess(value, meta, opts);
}
return [
false
];
}
endSanitize(value, meta, opts) {
if (this.scalarTypes.has(meta.type)) {
this.verifyEnum(value, meta, opts);
}
if (this.plugins.postProcess) {
return this.plugins.postProcess(value, meta, opts);
}
return value;
}
verifyEnum(value, meta, opts) {
if (value == null && meta.optional) return;
if (meta.enum && !meta.enum.includes(value)) {
throw new _errors.ValidationError('Invalid enum value.', {
value: opts.rawValue,
path: opts.path
});
}
}
constructor(){
_define_property(this, "primitives", new Set());
_define_property(this, "scalarTypes", new Set());
_define_property(this, "plugins", {});
_define_property(this, "types", {});
_define_property(this, "sanitize", this.callByType('sanitize'));
_define_property(this, "sanitize_", this.callByType('sanitize_'));
_define_property(this, "serialize", this.callByType('serialize'));
_define_property(this, "validate", this.callByType('validate'));
this._counter = counter++;
}
}
const defaultTypeSystem = new TypeSystem();
const addType = (name, TypeMeta)=>{
defaultTypeSystem.addType(name, TypeMeta);
defaultTypeClasses.push({
name,
TypeMeta
});
};
const addPlugin = (name, plugin)=>{
defaultTypeSystem.addPlugin(name, plugin);
defaultPlugins.push({
name,
plugin
});
};
const createTypeSystem = (emptySystem)=>{
return emptySystem ? new TypeSystem() : TypeSystem.fromDefault();
};
const Types = defaultTypeSystem.types;
// compatibility
Types.sanitize = defaultTypeSystem.sanitize.bind(defaultTypeSystem);
Types.sanitize_ = defaultTypeSystem.sanitize_.bind(defaultTypeSystem);
Types.serialize = defaultTypeSystem.serialize.bind(defaultTypeSystem);
Types.primitives = defaultTypeSystem.primitives;
const charsets = {
up_letter_num: '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ',
low_letter_num: '0123456789abcdefghijklmnopqrstuvwxyz',
up_letter: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
low_letter: 'abcdefghijklmnopqrstuvwxyz',
url_safe_all: '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_'
};
const _default = defaultTypeSystem;
//# sourceMappingURL=types.js.map