New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

compactr

Package Overview
Dependencies
Maintainers
1
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

compactr - npm Package Compare versions

Comparing version 1.0.1 to 1.0.2

6

package.json
{
"name": "compactr",
"version": "1.0.1",
"version": "1.0.2",
"description": "A compression library for the modern web",

@@ -39,3 +39,5 @@ "main": "index.js",

},
"dependencies": {}
"dependencies": {
"ieee754": "1.1.x"
}
}

@@ -14,3 +14,3 @@ # Compactr

Compactr is a library to compress and decompress Javascript objects before sending them over the web. It's immensely useful for web applications that use sockets a lot. Smaller payloads equals better, faster throughput and less bandwidth costs.
Compactr is a library to compress and decompress Javascript objects before sending them over the web. It's immensely useful for web applications that use sockets a lot. Smaller payloads equals faster throughput and less bandwidth costs.

@@ -29,5 +29,5 @@

Not only are they overly complex, they are also written in a different markup, which makes dynamic generation or property probing a bit of a hassle. Not to mention that you have to maintain parity across services of these messages that are more often than not a copy of your data Models. (See [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself))
Not only are they overly complex, they are also written in a different markup, which makes dynamic generation or property checking a bit of a hassle. Not to mention that you have to maintain parity across services of these messages that are more often than not a copy of your data Models. (See [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself))
Furthermore, Compactr has **NO** dependencies or compiled modules. It's the lightest module you've ever seen!
Furthermore, Protobuf variable types don't mean a lot in Javascript.

@@ -37,3 +37,3 @@

Protocol Buffers are awesome. Having schemas to deflate and inflate data while maintaining some kind of validation is a great concept. Compactr's goal is to build on that to better suit Node server development and reduce noise by allowing you to re-use your current Model schemas.
Protocol Buffers are awesome. Having schemas to deflate and inflate data while maintaining some kind of validation is a great concept. Compactr's goal is to build on that to better suit Node development and reduce repetition by allowing you to re-use your current Model schemas.

@@ -68,4 +68,4 @@

No need to create additional models for serialization!
Note that you can also use plain Objects as Schemas
## Can that be used for Websockets too?

@@ -93,3 +93,3 @@

In the near future, you will be able to:
Right now, Compactr allows you to

@@ -99,4 +99,10 @@ - [x] Use Waterline schemas

- [x] Synchronously encode/decode
- [ ] Nested objects/ Arrays
- [x] Encode nested objects/Arrays
And in the near future
- [ ] Run validation checks on payloads
- [ ] Allow multiple levels of encoding
## Alright, I'm convinced! How can I help?

@@ -103,0 +109,0 @@

@@ -9,2 +9,4 @@ /**

const ieee754 = require('ieee754');
const Types = require('./Types');

@@ -14,3 +16,2 @@

const SEP_CODE = 255;
const READ_SKIP = 2;

@@ -21,8 +22,13 @@

const INT32_SIZE = 4;
const DOUBLE_SIZE = 6;
const DOUBLE_SIZE = 8;
const SEP_CODE = 255;
const SCHEMA_SEP_CODE = 254;
const ARRAY_SEP_CODE = 253;
const ZERO = 0;
const ONE = 1;
const CPR = Object.create(null);
let doubleBuffer = Buffer.alloc(8);
/* Methods -------------------------------------------------------------------*/

@@ -34,22 +40,62 @@

* @param {Buffer} data The buffer to decode
* @param {boolean} nested Wether the decode is for a nested Schema
* @returns {object} The decoded buffer
*/
function Decode(schema, data) {
let result = {};
function Decode(schema, data, nested) {
schema = schema.attributes || schema; // Waterline Model
const keys = Object.keys(schema);
const len = data.length;
let result = {};
let _caret = len;
let prev = null;
for (let i = len - 1; i >= 0; i--) {
if (data[i] === SEP_CODE) {
let _propName = keys[read_index(data, i)];
let hold = [];
let in_schema = false;
for (let i = len - ONE; i >= ZERO; i--) {
let curr = data[i];
if (curr === SEP_CODE && in_schema === false) {
let _propName = keys[prev];
let sub_schema;
if (_propName !== undefined && schema[_propName] !== undefined) {
let _propType = Types.resolve(schema[_propName].type || schema[_propName]);
result[_propName] = read(data, _propType, i + READ_SKIP, _caret);
if (i === 0) break;
let _propType = Types.resolve(schema[_propName]);
if (
_propType === Types.SCHEMA_ARRAY ||
_propType === Types.SCHEMA
) {
sub_schema = Types.get_schema(schema[_propName]);
}
result[_propName] = read(
data,
_propType,
i + READ_SKIP,
_caret,
sub_schema
);
if (i === ZERO) break;
_caret = i;
i -= READ_SKIP;
prev = data[i];
}
}
}
else if (curr === SCHEMA_SEP_CODE) {
if (in_schema === false) {
if (
prev === SEP_CODE ||
prev === null ||
prev === SCHEMA_SEP_CODE
) {
in_schema = true;
}
}
else {
if (data[i - 2] === SEP_CODE && prev === SEP_CODE) {
in_schema = false;
}
}
}
else prev = curr;
}

@@ -61,12 +107,2 @@

/**
* Returns a Schema key index - 1 byte - (0<->255)
* @param {Buffer} buffer The buffer to read from
* @param {integer} index The buffer index to read at
* @returns {integer} The Schema index key
*/
function read_index(buffer, index) {
return buffer[index + 1];
}
/**
* Returns a Boolean value - 1 byte - (0<->1)

@@ -78,3 +114,3 @@ * @param {Buffer} buffer The buffer to read from

function read_boolean(buffer, index) {
return buffer[index] === 1;
return buffer[index] === ONE;
}

@@ -90,3 +126,5 @@

function read_int8(buffer, from, to) {
return buffer.readInt8(from, to);
/*if (!(buffer[from] & 0x80)) return (buffer[from]);
return ((0xff - buffer[from] + 1) * -1);*/
return buffer[from];
}

@@ -102,3 +140,4 @@

function read_int16(buffer, from, to) {
return buffer.readInt16BE(from, to);
let val = buffer[from + 1] | (buffer[from] << 8);
return (val & 0x8000) ? val | 0xFFFF0000 : val;
}

@@ -114,3 +153,6 @@

function read_int32(buffer, from, to) {
return buffer.readInt32BE(from, to);
return (buffer[from] << 24) |
(buffer[from + 1] << 16) |
(buffer[from + 2] << 8) |
(buffer[from + 3]);
}

@@ -134,20 +176,122 @@

/**
* Returns a double value - 6 bytes
* !Doubles are in fact 8 bytes long, but only the first 6 are encoded!
* Returns an Array of String values
* @param {Buffer} buffer The buffer to read from
* @param {integer} from The buffer index to read from
* @param {integer} to The buffer index to read to
* @returns {array} The Array value
*/
function read_string_array(buffer, from, to) {
let acc = [];
let res = [];
for (let i = from; i < to; i++) {
if (buffer[i] !== ARRAY_SEP_CODE) acc.push(buffer[i]);
else {
res.push(String.fromCodePoint.apply(CPR, acc));
acc.length = ZERO;
}
}
res.push(String.fromCodePoint.apply(CPR, acc));
return res;
}
/**
* Returns an Array of Boolean values
* @param {Buffer} buffer The buffer to read from
* @param {integer} from The buffer index to read from
* @param {integer} to The buffer index to read to
* @returns {array} The Array value
*/
function read_boolean_array(buffer, from, to) {
let res = [];
for (let i = from; i < to; i++) {
res.push(buffer[i] === ONE);
}
return res;
}
/**
* Returns an Array of Number values
* @param {Buffer} buffer The buffer to read from
* @param {integer} from The buffer index to read from
* @param {integer} to The buffer index to read to
* @returns {array} The Array value
*/
function read_number_array(buffer, from, to) {
let acc = from;
let res = [];
for (let i = from + ONE; i < to; i++) {
if (buffer[i] === ARRAY_SEP_CODE) {
res.push(read_number(buffer, acc, i));
acc = i + ONE;
i++;
}
}
res.push(read_number(buffer, acc, to));
return res;
}
/**
* Returns a double value - 8 bytes
* @param {Buffer} buffer The buffer to read from
* @param {integer} from The buffer index to read from
* @param {integer} to The buffer index to read to
* @returns {integer} The double value
*/
function read_double(buffer, from, to) {
buffer.copy(
doubleBuffer,
0,
from,
from + DOUBLE_SIZE
);
return doubleBuffer.readDoubleBE();
return ieee754.read(buffer, from, false, 52, 8)
}
/**
* Returns an object value
* @param {Buffer} buffer The buffer to read from
* @param {integer} from The buffer index to read from
* @param {integer} to The buffer index to read to
* @returns {object} The Schema value
*/
function read_schema(buffer, from, to, schema) {
let bytes = [];
for (let i = from + ONE; i < to - ONE; i++) {
bytes[bytes.length] = buffer[i];
}
return Decode(schema, bytes, true);
}
/**
* Returns an object array value
* @param {Buffer} buffer The buffer to read from
* @param {integer} from The buffer index to read from
* @param {integer} to The buffer index to read to
* @returns {array} The Schema array value
*/
function read_schema_array(buffer, from, to, schema) {
let caret = from;
let res = [];
for (let i = caret + ONE; i < to; i++) {
if (buffer[i] === SCHEMA_SEP_CODE) {
res[res.length] = read_schema(buffer, caret, i + ONE, schema);
caret = i;
i++;
}
}
return res;
}
/**
* Returns a number value - 1 to 8 bytes
* @param {Buffer} buffer The buffer to read from
* @param {integer} from The buffer index to read from
* @param {integer} to The buffer index to read to
* @returns {integer} The double value
*/
function read_number(buffer, from, to) {
let size = to - from;
if (size === INT8_SIZE) return read_int8(buffer, from, to);
else if (size === INT16_SIZE) return read_int16(buffer, from, to);
else if (size === INT32_SIZE) return read_int32(buffer, from, to);
else if (size === DOUBLE_SIZE) return read_double(buffer, from, to);
}
/**
* Returns the decoded value for a property

@@ -158,17 +302,24 @@ * @param {integer} type The expected variable type

* @param {integer} to The buffer index to read to
* @param {object|null} schema The sub schema to use
* @returns {?} The decoded value
*/
function read(buffer, type, from, to) {
let res;
if (type === Types.BOOLEAN) res = read_boolean(buffer, from);
else if (type === Types.NUMBER) {
if (to - from === INT8_SIZE) res = read_int8(buffer, from, to);
else if (to - from === INT16_SIZE) res = read_int16(buffer, from, to);
else if (to - from === INT32_SIZE) res = read_int32(buffer, from, to);
else res = read_double(buffer, from, to);
function read(buffer, type, from, to, schema) {
if (type === Types.BOOLEAN) return read_boolean(buffer, from);
else if (type === Types.NUMBER) return read_number(buffer, from, to);
else if (type === Types.STRING) return read_string(buffer, from, to);
else if (type === Types.BOOLEAN_ARRAY) {
return read_boolean_array(buffer, from, to);
}
else if (type === Types.STRING) res = read_string(buffer, from, to);
return res;
else if (type === Types.NUMBER_ARRAY) {
return read_number_array(buffer, from, to);
}
else if (type === Types.STRING_ARRAY) {
return read_string_array(buffer, from, to);
}
else if (type === Types.SCHEMA_ARRAY) {
return read_schema_array(buffer, from, to, schema);
}
else if (type === Types.SCHEMA) {
return read_schema(buffer, from, to, schema);
}
}

@@ -175,0 +326,0 @@

@@ -9,2 +9,4 @@ /**

const ieee754 = require('ieee754');
const Types = require('./Types');

@@ -14,21 +16,18 @@

// One frame - all overheads
const MAX_SIZE = 1400;
// Number ranges and byte sizes
const MIN_INT8 = -128;
const MAX_INT8 = 127;
// Reserving 5 characters for separations
const MIN_INT8 = 0;
const MAX_INT8 = 250;
const MIN_INT16 = -32768;
const MAX_INT16 = 32767;
const INT8_SIZE = 1;
const INT16_SIZE = 2;
const INT32_SIZE = 4;
const DOUBLE_SIZE = 6;
const MIN_INT32 = -2147483648;
const MAX_INT32 = 2147483647;
const ARRAY_SEP_CODE = 253;
const SEP_CODE = 255;
const SCHEMA_SEP_CODE = 254;
const allowed_types = ['number', 'boolean', 'string'];
const ZERO = 0;
const ONE = 1;
let work_buffer = Buffer.allocUnsafe(MAX_SIZE);
/* Methods -------------------------------------------------------------------*/

@@ -40,16 +39,18 @@

* @param {object} payload The payload to encode
* @param {boolean} nested Wether this was called for a nested schema
* @returns {Buffer} The encoded Buffer
*/
function Encode(schema, payload) {
let result = work_buffer;
function Encode(schema, payload, nested) {
schema = schema.attributes || schema; // Waterline Model
const keys = Object.keys(schema);
const len = keys.length;
result.caret = 0;
for (let i = len - 1; i >= 0; i--) {
let result = [];
for (let i = len - ONE; i >= ZERO; i--) {
let key = keys[i];
if (is_valid(key, payload)) {
let type = Types.resolve(schema[key].type || schema[key]);
if (payload[key] !== undefined && payload[key] !== null) {
let type = Types.resolve(schema[key]);
append_index(result, i);

@@ -59,21 +60,52 @@ if (type === Types.BOOLEAN) append_boolean(result, payload[key]);

else if (type === Types.STRING) append_string(result, payload[key]);
else if (type === Types.SCHEMA) {
if (!nested) {
append_schema(
result,
payload[key],
Types.get_schema(schema[key])
);
}
else throw new Error('Cannot embed schemas at this depth');
}
else if (type === Types.BOOLEAN_ARRAY) {
append_boolean_array(result, payload[key]);
}
else if (type === Types.NUMBER_ARRAY) {
append_number_array(result, payload[key]);
}
else if (type === Types.STRING_ARRAY){
append_string_array(result, payload[key]);
}
else if (type === Types.SCHEMA_ARRAY){
if (!nested) {
append_schema_array(
result,
payload[key],
Types.get_schema(schema[key])
);
}
else throw new Error('Cannot embed schemas at this depth');
}
}
}
return result.slice(0, result.caret);
return array_to_buffer(result);
}
/**
* Returns wether a payload property is valid for encoding
* If not, it will be skipped
* @param {string} key The property key to validate
* @param {object} payload The payload to encode
* @returns {boolean} Wether the property is valid for encoding
* Turns an array of UINT8 into a Buffer
* Faster than Buffer.from because it skips a lot of checks
* @param {array} data An array of UINT8
* @returns {Buffer} The resulting Buffer
*/
function is_valid(key, payload) {
if (key in payload) {
let _type = typeof payload[key];
return (allowed_types.includes(_type));
function array_to_buffer(data) {
let len = data.length;
let res = Buffer.allocUnsafe(len);
for (let i = ZERO; i < len; i++) {
res[i] = data[i];
}
return false;
return res;
}

@@ -85,5 +117,6 @@

* @param {number} data The data to append
* @returns {integer} The number of bytes written
*/
function append_number(buffer, data) {
if (Number.isInteger(data)) {
if (isInt(data)) {
if (data <= MAX_INT8 && data >= MIN_INT8) {

@@ -95,3 +128,8 @@ append_int8(buffer, data);

}
else append_int32(buffer, data);
else if (data <= MAX_INT32 && data >= MIN_INT32) {
append_int32(buffer, data);
}
else {
append_double(buffer, data);
}
}

@@ -102,2 +140,98 @@ else append_double(buffer, data);

/**
* Super weak bitwise check if a number is an integer. Will not check for
* variable type or is it's NaN. It would have thrown later down the line anyway
* and this is way faster.
* @param {number} value The number to check
* @returns {boolean} Wether the number is an Integer or a float
*/
function isInt(value) {
return (value | ZERO) === value;
}
/**
* Appends an Array of Number type values to the Buffer
* @param {Buffer} buffer The Buffer to append to
* @param {array} data The data to append
*/
function append_number_array(buffer, data) {
const len = data.length;
for (let i = ZERO; i < len; i++) {
if (data[i] !== undefined && data[i] !== null) {
append_number(buffer, data[i]);
if (i < len - ONE) append_array_separator(buffer);
}
}
}
/**
* Appends an Array of Boolean type values to the Buffer
* @param {Buffer} buffer The Buffer to append to
* @param {array} data The data to append
*/
function append_boolean_array(buffer, data) {
const len = data.length;
for (let i = ZERO; i < len; i++) {
if (data[i] !== undefined && data[i] !== null) {
append_boolean(buffer, data[i]);
}
}
}
/**
* Appends an Array of String type values to the Buffer
* @param {Buffer} buffer The Buffer to append to
* @param {array} data The data to append
*/
function append_string_array(buffer, data) {
const len = data.length;
for (let i = ZERO; i < len; i++) {
if (data[i] !== undefined && data[i] !== null) {
append_string(buffer, data[i]);
if (i < len - ONE) append_array_separator(buffer);
}
}
}
/**
* Appends a Schema type value to the Buffer
* @param {Buffer} buffer The Buffer to append to
* @param {array} data The data to append
* @param {object} schema The child Schema to use
*/
function append_schema(buffer, data, schema) {
let res = Encode(schema, data, true);
let len = res.length;
buffer[buffer.length] = SCHEMA_SEP_CODE;
for (let i = ZERO; i < len; i++) {
buffer[buffer.length] = res[i];
}
buffer[buffer.length] = SCHEMA_SEP_CODE;
}
/**
* Appends an Array of Schema type values to the Buffer
* @param {Buffer} buffer The Buffer to append to
* @param {array} data The data to append
* @param {object} schema The child Schema to use
*/
function append_schema_array(buffer, data, schema) {
let len = data.length;
for (let i = 0; i < len; i++) {
if (data[i] !== undefined && data[i] !== null) {
append_schema(buffer, data[i], schema);
}
}
}
/**
* Appends an Array separator charcter to the Buffer
* @param {Buffer} buffer The Buffer to append to
*/
function append_array_separator(buffer) {
buffer[buffer.length] = ARRAY_SEP_CODE;
}
/**
* Appends a Boolean type value to the Buffer

@@ -108,4 +242,3 @@ * @param {Buffer} buffer The Buffer to append to

function append_boolean(buffer, data) {
buffer[buffer.caret] = data ? 1 : 0;
buffer.caret += INT8_SIZE;
buffer[buffer.length] = data ? ONE : ZERO;
}

@@ -119,4 +252,5 @@

function append_int8(buffer, data) {
buffer.writeInt8(data, buffer.caret);
buffer.caret += INT8_SIZE;
/*if (data < 0) data = 0xff + data + 1;
buffer[buffer.length] = (data & 0xff);*/
buffer[buffer.length] = data;
}

@@ -130,4 +264,4 @@

function append_int16(buffer, data) {
buffer.writeInt16BE(data, buffer.caret);
buffer.caret += INT16_SIZE;
buffer[buffer.length] = (data >>> 8);
buffer[buffer.length] = (data & 0xff);
}

@@ -141,4 +275,7 @@

function append_int32(buffer, data) {
buffer.writeInt32BE(data, buffer.caret);
buffer.caret += INT32_SIZE;
if (data < 0) data = 0xffffffff + data + 1;
buffer[buffer.length] = (data >>> 24);
buffer[buffer.length] = (data >>> 16);
buffer[buffer.length] = (data >>> 8);
buffer[buffer.length] = (data & 0xff);
}

@@ -152,5 +289,3 @@

function append_double(buffer, data) {
// Ommit last 2 digits of the double
buffer.writeDoubleBE(data, buffer.caret);
buffer.caret += DOUBLE_SIZE;
ieee754.write(buffer, data, buffer.length, false, 52, 8);
}

@@ -164,8 +299,8 @@

function append_string(buffer, data) {
data = String(data);
let len = data.length;
for (let i = 0; i < len; i++) {
buffer[buffer.caret + i] = data.codePointAt(i);
for (let i = ZERO; i < len; i++) {
buffer[buffer.length] = data.codePointAt(i);
}
buffer.caret += len;
}

@@ -179,6 +314,5 @@

function append_index(buffer, data) {
buffer[buffer.caret] = SEP_CODE;
buffer[buffer.length] = SEP_CODE;
// Unsigned Int
buffer[buffer.caret + 1] = data;
buffer.caret += INT16_SIZE;
buffer[buffer.length] = data;
}

@@ -185,0 +319,0 @@

@@ -9,12 +9,18 @@ /**

const BOOLEAN = 0;
const BUFFER = 1;
const BOOLEAN = 1;
const NUMBER = 2;
const STRING = 3;
const INDEX = 4;
const BOOLEAN_ARRAY = 4;
const NUMBER_ARRAY = 5;
const STRING_ARRAY = 6;
const SCHEMA = 7;
const SCHEMA_ARRAY = 8;
const EMBEDDED_JSON = 9;
const INDEX = 16;
const BOOLEAN_STR = 'boolean';
const BUFFER_STR = 'buffer';
const NUMBER_STR = 'number';
const STRING_STR = 'string';
const OBJECT_STR = 'json';
const OBJECT_TYPE = 'object';

@@ -29,12 +35,26 @@ /* Methods -------------------------------------------------------------------*/

function resolve(type) {
let name = type.type || type;
let res = BOOLEAN;
if (typeof type === STRING_STR) {
if (type === BUFFER_STR) res = BUFFER;
else if (type === NUMBER_STR) res = NUMBER;
else if (type === STRING_STR) res = STRING;
if (typeof name === STRING_STR) {
if (name === NUMBER_STR) res = NUMBER;
else if (name === STRING_STR) res = STRING;
else if (name === OBJECT_STR) {
if (type.items === BOOLEAN_STR) res = BOOLEAN_ARRAY;
else if (type.items === NUMBER_STR) res = NUMBER_ARRAY;
else if (type.items === STRING_STR) res = STRING_ARRAY;
else if (typeof type.items === OBJECT_TYPE) res = SCHEMA_ARRAY;
else if (type.schema) res = SCHEMA;
}
}
else {
if (type === Buffer) res = BUFFER;
else if (type === Number) res = NUMBER;
else if (type === String) res = STRING;
if (name === Number) res = NUMBER;
else if (name === String) res = STRING;
else if (name === Array) {
if (type.items === Boolean) res = BOOLEAN_ARRAY;
else if (type.items === Number) res = NUMBER_ARRAY;
else if (type.items === String) res = STRING_ARRAY;
else if (typeof type.items === Object) res = SCHEMA_ARRAY;
else if (type.schema) res = SCHEMA;
}
// TODO: Full Mongoose schema support
}

@@ -45,4 +65,20 @@

function get_schema(type) {
return type.schema || type.items;
}
/* Exports -------------------------------------------------------------------*/
module.exports = { BOOLEAN, BUFFER, NUMBER, STRING, INDEX, resolve };
module.exports = {
BOOLEAN,
NUMBER,
STRING,
BOOLEAN_ARRAY,
NUMBER_ARRAY,
STRING_ARRAY,
SCHEMA,
SCHEMA_ARRAY,
INDEX,
resolve,
get_schema
};

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc