UNPKG

@nozbe/watermelondb

Version:

Build powerful React Native and React web apps that scale from hundreds to tens of thousands of records and remain fast

123 lines (115 loc) 4.57 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); exports.__esModule = true; exports.nullValue = nullValue; exports.sanitizedRaw = sanitizedRaw; exports.setRawSanitized = setRawSanitized; var _randomId = _interopRequireDefault(require("../utils/common/randomId")); /* eslint-disable no-lonely-if */ /* eslint-disable no-self-compare */ // Raw object representing a model record, coming from an untrusted source // (disk, sync, user data). Before it can be used to create a Model instance // it must be sanitized (with `sanitizedRaw`) into a RawRecord // These fields are ALWAYS present in records of any collection. // Raw object representing a model record. A RawRecord is guaranteed by the type system // to be safe to use (sanitied with `sanitizedRaw`): // - it has exactly the fields described by TableSchema (+ standard fields) // - every field is exactly the type described by ColumnSchema (string, number, or boolean) // - … and the same optionality (will not be null unless isOptional: true) // a number, but not NaN (NaN !== NaN) or Infinity function isValidNumber(value) { return 'number' === typeof value && value === value && value !== Infinity && value !== -Infinity; } // Note: This is performance-critical code function _setRaw(raw, key, value, columnSchema) { var { type: type, isOptional: isOptional } = columnSchema; // If the value is wrong type or invalid, it's set to `null` (if optional) or empty value ('', 0, false) if ('string' === type) { if ('string' === typeof value) { raw[key] = value; } else { raw[key] = isOptional ? null : ''; } } else if ('boolean' === type) { if ('boolean' === typeof value) { raw[key] = value; } else if (1 === value || 0 === value) { // Exception to the standard rule — because SQLite turns true/false into 1/0 raw[key] = Boolean(value); } else { raw[key] = isOptional ? null : false; } } else { // type = number // Treat NaN and Infinity as null if (isValidNumber(value)) { raw[key] = value || 0; } else { raw[key] = isOptional ? null : 0; } } } function isValidStatus(value) { return 'created' === value || 'updated' === value || 'deleted' === value || 'synced' === value; } // Transforms a dirty raw record object into a trusted sanitized RawRecord according to passed TableSchema // TODO: Should we make this public API for advanced users? function sanitizedRaw(dirtyRaw, tableSchema) { var { id: id, _status: _status, _changed: _changed } = dirtyRaw; // This is called with `{}` when making a new record, so we need to set a new ID, status // Also: If an existing has one of those fields broken, we're screwed. Safest to treat it as a // new record (so that it gets synced) // TODO: Think about whether prototypeless objects are a useful mitigation // const raw = Object.create(null) // create a prototypeless object var raw = {}; if ('string' === typeof id) { // TODO: Can we trust IDs passed? Maybe we want to split this implementation, depending on whether // this is used on implicitly-trusted (persisted or Watermelon-created) records, or if this is user input? raw.id = id; raw._status = isValidStatus(_status) ? _status : 'created'; raw._changed = 'string' === typeof _changed ? _changed : ''; } else { raw.id = (0, _randomId.default)(); raw._status = 'created'; raw._changed = ''; } // faster than Object.values on a map var columns = tableSchema.columnArray; for (var i = 0, len = columns.length; i < len; i += 1) { var columnSchema = columns[i]; var key = columnSchema.name; // TODO: Check performance // $FlowFixMe var value = Object.prototype.hasOwnProperty.call(dirtyRaw, key) ? dirtyRaw[key] : null; _setRaw(raw, key, value, columnSchema); } return raw; } // Modifies passed rawRecord by setting sanitized `value` to `columnName` // Note: Assumes columnName exists and columnSchema matches the name function setRawSanitized(rawRecord, columnName, value, columnSchema) { _setRaw(rawRecord, columnName, value, columnSchema); } function nullValue(columnSchema) { var { isOptional: isOptional, type: type } = columnSchema; if (isOptional) { return null; } else if ('string' === type) { return ''; } else if ('number' === type) { return 0; } else if ('boolean' === type) { return false; } throw new Error("Unknown type for column schema ".concat(JSON.stringify(columnSchema))); }