'use strict'; /** Returns the object type of the given payload */ function getType(payload) { return Object.prototype.toString.call(payload).slice(8, -1); } /** * Returns whether the payload is an any kind of object (including special classes or objects with * different prototypes) */ function isAnyObject(payload) { return getType(payload) === 'Object'; } /** Returns whether the payload is an array */ function isArray(payload) { return getType(payload) === 'Array'; } /** Returns whether the payload is a boolean */ function isBoolean(payload) { return getType(payload) === 'Boolean'; } /** * Returns whether the payload is a plain JavaScript object (excluding special classes or objects * with other prototypes) */ function isPlainObject(payload) { if (getType(payload) !== 'Object') return false; const prototype = Object.getPrototypeOf(payload); return !!prototype && prototype.constructor === Object && prototype === Object.prototype; } /** Returns whether the payload is a string */ function isString(payload) { return getType(payload) === 'String'; } /** * Returns whether the payload is a number (but not NaN) * * This will return `false` for `NaN`!! */ function isNumber(payload) { return getType(payload) === 'Number' && !isNaN(payload); } /** Returns whether the payload is null */ function isNull(payload) { return getType(payload) === 'Null'; } /** * Serializable primitive type guard. */ function isSerializablePrimitive(value) { return (isBoolean(value) || isNumber(value) || isNull(value) || isString(value)); } /** * Serified value type guard. */ function isSerifiedValue(value, options) { return (isPlainObject(value) && 'serifyKey' in value && value.serifyKey === options.serifyKey && 'type' in value && value.type in options.types && 'value' in value); } /** * static property name to override an type's key in serify config */ const serifyStaticTypeProperty = Symbol('serify static type property'); /** * serify a value */ const serify = (value, options) => { if (isSerializablePrimitive(value)) return value; const valueType = isAnyObject(value) ? serifyStaticTypeProperty in value.constructor ? value.constructor[serifyStaticTypeProperty] : value.constructor.name : getType(value); if (valueType in options.types) return { serifyKey: options.serifyKey, type: valueType, value: serify(options.types[valueType].serifier(value), options), }; if (isArray(value)) return value.map((v) => serify(v, options)); if (isPlainObject(value)) { const copy = {}; for (const p in value) copy[p] = serify(value[p], options); return copy; } throw new Error(`unserifiable type: ${valueType}`); }; /** * create redux middleware */ const createReduxMiddleware = (options) => () => (next) => (action) => { if (isAnyObject(action)) action.payload = serify(action.payload, options); next(action); }; /** * Deserify a value. Does not mutate the original value. Implicitly assumes * that the value is composed entirely of types serializable by JSON.stringify. */ const deserify = (value, options) => { if (isSerializablePrimitive(value)) return value; if (isSerifiedValue(value, options)) { const { type, value: raw } = value; const parsed = deserify(raw, options); return options.types[type].deserifier(parsed); } if (isArray(value)) return value.map((v) => deserify(v, options)); if (isPlainObject(value)) { const copy = {}; for (const p in value) copy[p] = deserify(value[p], options); return copy; } throw new Error(`Value is not deserifiable: ${JSON.stringify(value)}`); }; const defaultOptions = { serifyKey: null, types: { BigInt: { serifier: (value) => value.toString(), deserifier: (value) => BigInt(value), }, Date: { serifier: (value) => value.getTime(), deserifier: (value) => new Date(value), }, Map: { serifier: (value) => [...value.entries()], deserifier: (value) => new Map(value), }, Set: { serifier: (value) => [...value.values()], deserifier: (value) => new Set(value), }, Undefined: { serifier: () => null, deserifier: () => undefined, }, }, }; exports.createReduxMiddleware = createReduxMiddleware; exports.defaultOptions = defaultOptions; exports.deserify = deserify; exports.serify = serify; exports.serifyStaticTypeProperty = serifyStaticTypeProperty;