UNPKG

@sprucelabs/schema

Version:

Static and dynamic binding plus runtime validation and transformation to ensure your app is sound. 🤓

220 lines (219 loc) • 8.85 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const AbstractEntity_1 = __importDefault(require("./AbstractEntity")); const SpruceError_1 = __importDefault(require("./errors/SpruceError")); const FieldFactory_1 = __importDefault(require("./factories/FieldFactory")); const cloneDeepPreservingInstances_1 = __importDefault(require("./utilities/cloneDeepPreservingInstances")); const normalizeFieldValue_1 = __importStar(require("./utilities/normalizeFieldValue")); class StaticSchemaEntityImpl extends AbstractEntity_1.default { constructor(schema, values) { super(schema); this.values = {}; this.schema = schema; this.fields = {}; this.buildFields(); const v = { ...this.values, ...values, }; this.values = (0, cloneDeepPreservingInstances_1.default)(v); } buildFields() { const fieldDefinitions = this.schema.fields; if (!fieldDefinitions) { throw new Error(`SchemaEntity requires fields. If you want to use dynamicFieldSignature, try DynamicSchemaEntity.`); } Object.keys(fieldDefinitions).forEach((name) => { const definition = fieldDefinitions[name]; const field = FieldFactory_1.default.Field(name, definition); this.fields[name] = field; if (definition.value) { this.set(name, definition.value); } }); } normalizeValue(forField, value, options) { const field = this.fields[forField]; const overrideOptions = { ...(options ?? {}), ...(options?.byField?.[forField] ?? {}), }; return (0, normalizeFieldValue_1.default)(this.schemaId, this.name, {}, field, value, overrideOptions); } get(fieldName, options = {}) { const value = this.values[fieldName] !== undefined ? this.values[fieldName] : undefined; return this.normalizeValue(fieldName, value, options); } set(fieldName, value, options = {}) { const localValue = this.normalizeValue(fieldName, value, options); this.values[fieldName] = localValue; return this; } isValid(options = {}) { try { this.validate(options); return true; } catch { return false; } } pluckExtraFields(values, schema) { const extraFields = []; if (schema.fields) { const passedFields = Object.keys(values); const expectedFields = Object.keys(schema.fields); passedFields.forEach((passed) => { if (expectedFields.indexOf(passed) === -1) { extraFields.push(passed); } }); } return extraFields; } validate(options = {}) { const errors = []; const extraFields = this.pluckExtraFields(this.values, this.schema); if (extraFields.length > 0) { extraFields.forEach((name) => { errors.push({ name, code: 'UNEXPECTED_PARAMETER', friendlyMessage: `\`${name}\` does not exist.`, }); }); } this.getNamedFields(options).forEach((namedField) => { const { name, field } = namedField; let valueAsArray = (0, normalizeFieldValue_1.normalizeValueToArray)(this.values[name]); if (field.isRequired && field.isArray && (!this.values[name] || valueAsArray.length < (field.minArrayLength ?? 1))) { errors.push({ code: !this.values[name] ? 'MISSING_PARAMETER' : 'INVALID_PARAMETER', name, friendlyMessage: !this.values[name] ? `${field.label ? `'${field.label}'` : 'This'} is required!` : `${field.label ? `'${field.label}'` : 'You'} must ${field.label ? 'have' : 'select'} at least ${field.minArrayLength} value${field.minArrayLength === 1 ? '' : 's'}. I found ${valueAsArray.length}!`, }); } else { if ((!field.isArray || (field.minArrayLength ?? 0) > 0) && valueAsArray.length === 0) { valueAsArray = [undefined]; } for (const value of valueAsArray) { const fieldErrors = field.validate(value, { schemasById: {}, }); if (fieldErrors.length > 0) { errors.push(...fieldErrors); } } } }); if (errors.length > 0) { throw new SpruceError_1.default({ code: 'VALIDATION_FAILED', schemaId: this.schemaId, schemaName: this.name, errors, }); } } getDefaultValues(options = {}) { const values = {}; this.getNamedFields().forEach((namedField) => { const { name, field } = namedField; if (typeof field.definition.defaultValue !== 'undefined') { // @ts-ignore values[name] = this.normalizeValue(name, field.definition.defaultValue, options); } }); return values; } getValues(options) { const values = {}; let { fields = Object.keys(this.fields), shouldIncludePrivateFields: includePrivateFields = true, shouldIncludeNullAndUndefinedFields = true, excludeFields, } = options || {}; this.getNamedFields().forEach((namedField) => { const { name, field } = namedField; const shouldSkipBecauseNotSet = !shouldIncludeNullAndUndefinedFields && !(name in this.values); const shouldSkipBecauseUndefinedOrNull = !shouldIncludeNullAndUndefinedFields && (this.values[name] === undefined || this.values[name] === null); const shouldSkipBecauseExcluded = excludeFields?.includes(name); if (shouldSkipBecauseNotSet || shouldSkipBecauseUndefinedOrNull || shouldSkipBecauseExcluded) { return; } if (fields.indexOf(name) > -1 && (includePrivateFields || !field.isPrivate)) { const value = this.get(name, options); values[name] = value; } }); return values; } setValues(values) { this.getNamedFields().forEach((namedField) => { const { name } = namedField; const value = values[name]; if (typeof value !== 'undefined') { this.set(name, value); } }); return this; } getNamedFields(options = {}) { const namedFields = []; const { fields = Object.keys(this.fields) } = options; fields.forEach((name) => { const field = this.fields[name]; namedFields.push({ name, field }); }); return namedFields; } } StaticSchemaEntityImpl.enableDuplicateCheckWhenTracking = true; exports.default = StaticSchemaEntityImpl;