UNPKG

@storable/model

Version:

Base class providing typed properties, validation and serialization

246 lines (195 loc) 4.77 kB
"use strict"; exports.__esModule = true; exports.serialize = serialize; exports.deserialize = deserialize; exports.createValue = createValue; exports.serializeValue = serializeValue; exports.normalizeValue = normalizeValue; var _isPlainObject = _interopRequireDefault(require("lodash/isPlainObject")); var _omit = _interopRequireDefault(require("lodash/omit")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function serialize(value, { filter } = {}) { let result = serializeValue(value, { filter }); if (result !== value) { // serializeValue() did the job return result; } if (typeof value !== 'object') { // The value is a boolean, a number, or a string return value; } if (Array.isArray(value)) { // The value is an array return value.map(item => serialize(item, { filter })); } // The value is a plain object result = {}; for (const [key, val] of Object.entries(value)) { result[key] = serialize(val, { filter }); } return result; } function deserialize(value, { expectedType, registry } = {}) { let result = createValue(value, { expectedType, registry, isDeserializing: true }); if (result !== value) { // createValue() did the job return result; } if (typeof value !== 'object') { // The value is a boolean, a number, or a string return value; } if (Array.isArray(value)) { // The value is an array return value.map(item => deserialize(item, { expectedType, registry })); } // The value is a plain object result = {}; for (const [key, val] of Object.entries(value)) { result[key] = deserialize(val, { expectedType, registry }); } return result; } function createValue(value, { expectedType, registry, fieldName, isDeserializing } = {}) { value = normalizeValue(value, { fieldName }); if (value === undefined) { return undefined; } const type = typeof value === 'object' ? value._type : undefined; if (type) { value = (0, _omit.default)(value, '_type'); const builtInType = builtInTypes[type]; if (builtInType) { value = value._value; if (builtInType.deserialize) { value = builtInType.deserialize(value); } } else { const Model = getModel(registry, type); value = new Model(value, { isDeserializing }); } } if (!expectedType) { return value; } const builtInType = builtInTypes[expectedType]; if (builtInType) { if (!builtInType.checkType(value)) { throw new Error(`Type mismatch (field: '${fieldName}', expected: '${expectedType}', provided: '${typeof value}')`); } return value; } if (value.isOfType && value.isOfType('Model')) { if (!value.isOfType(expectedType)) { throw new Error(`Type mismatch (field: '${fieldName}', expected: '${expectedType}', provided: '${value.constructor.getName()}')`); } return value; } if (typeof value !== 'object') { throw new Error(`Type mismatch (field: '${fieldName}', expected: 'object', provided: '${typeof value}')`); } const Model = getModel(registry, expectedType); value = new Model(value, { isDeserializing }); return value; } function serializeValue(value, { filter, _level } = {}) { if (value === undefined) { return { _type: 'undefined' }; } if (value instanceof Date) { return { _type: 'Date', _value: value.toISOString() }; } if (value.isOfType && value.isOfType('Model')) { return value.serialize({ filter, _level: _level + 1 }); } return value; } function normalizeValue(value, { fieldName }) { if (value === null) { throw new Error(`The 'null' value is not allowed (field: '${fieldName}')`); } if (value === undefined || typeof value === 'object' && value._type === 'undefined') { return undefined; } return value; } const builtInTypes = { boolean: { checkType(value) { return typeof value === 'boolean'; } }, number: { checkType(value) { return typeof value === 'number'; } }, string: { checkType(value) { return typeof value === 'string'; } }, object: { checkType(value) { return (0, _isPlainObject.default)(value); } }, Date: { checkType(value) { return value instanceof Date; }, deserialize(value) { return new Date(value); } } }; function getModel(registry, name) { const Model = registry === null || registry === void 0 ? void 0 : registry[name]; if (Model === undefined) { throw new Error(`Model not found (name: '${name}')`); } return Model; }