Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

ferrum-db-client

Package Overview
Dependencies
Maintainers
1
Versions
39
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ferrum-db-client - npm Package Compare versions

Comparing version 0.0.5 to 0.1.0

dist/db_remote.d.ts

78

dist/client.d.ts
/// <reference types="node" />
import { BinaryWriter } from "csharp-binary-stream";
import { Socket } from "net";
import { BinaryWriter } from "csharp-binary-stream";
export declare type SupportedEncodingTypes = "ndjson" | "json" | "bson" | "string" | "binary";
export declare type SupportedCompressionTypes = "gzip" | "none";
declare enum ApiMessageType {
CREATE_DATABASE = 0,
CREATE_DATABASE_IF_NOT_EXIST = 1,
HAS_DATABASE = 2,
DROP_DATABASE = 3,
LIST_DATABASES = 4,
CLEAR_DATABASE = 5,
CREATE_INDEX = 6,
CREATE_INDEX_IF_NOT_EXIST = 7,
DELETE_INDEX = 8,
HAS_INDEX = 9,
GET_INDEXES = 10,
COMPACT = 11,
INDEX_HAS = 12,
INDEX_GET = 13,
INDEX_SET = 14,
INDEX_DELETE = 15,
INDEX_GET_KEYS = 16,
INDEX_CLEAR = 17,
INDEX_GET_RECORD_COUNT = 18,
INDEX_GET_RECORD_SIZE = 19,
INDEX_READ_CHUNK = 20,
INDEX_READ_UNTIL = 21,
INDEX_OPEN_WRITE_STREAM = 22,
INDEX_CLOSE_WRITE_STREAM = 23,
INDEX_WRITE_STREAM_WRITE_CHUNK = 24,
HEARTBEAT = 25
}
export declare function ferrumConnect(ip: string, port: number): Promise<FerrumServerConnection>;
declare class FerrumServerClient {
import { ApiMessageType } from "./protcol";
export declare class FerrumServerClient {
protected id: number;

@@ -58,44 +28,2 @@ socket: Socket;

}
export declare class FerrumServerConnection {
private client;
constructor(client: FerrumServerClient);
disconnect(): void;
createDatabaseIfNotExists(dbName: string): Promise<FerrumDBRemote>;
createDatabase(dbName: string): Promise<FerrumDBRemote>;
hasDatabase(dbName: string): Promise<boolean>;
dropDatabase(dbName: string): Promise<void>;
clearDatabase(dbName: string): Promise<void>;
getDatabaseNames(): Promise<string[]>;
getDatabase(dbName: string): FerrumDBRemote;
}
export declare class FerrumDBRemote {
private client;
private dbName;
constructor(client: FerrumServerClient, dbName: string);
createIndexIfNotExist<T>(index: string, pageFileSize?: number): Promise<IndexRemote<T>>;
getIndex<T>(index: string, encoding?: SupportedEncodingTypes, compression?: SupportedCompressionTypes): IndexRemote<T>;
deleteIndex(index: string): Promise<void>;
createIndex<T>(index: string, pageFileSize?: number): Promise<IndexRemote<T>>;
hasIndex(index: string): Promise<boolean>;
getIndexes(): Promise<string[]>;
compact(): Promise<void>;
}
export declare class IndexRemote<T> {
private client;
private indexKey;
private encoding;
private compression;
private database;
constructor(client: FerrumServerClient, database: string, indexKey: string, encoding: SupportedEncodingTypes, compression: SupportedCompressionTypes);
has(key: string): Promise<boolean>;
getRecordSize(key: string): Promise<number>;
getRecordCount(): Promise<number>;
get(key: string): Promise<T>;
readChunk(key: string, offset: number, size: number): Promise<Buffer>;
put(key: string, value: T): Promise<void>;
set(key: string, value: T): Promise<void>;
clear(): Promise<void>;
getKeys(): Promise<string[]>;
}
export {};
//# sourceMappingURL=client.d.ts.map
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.IndexRemote = exports.FerrumDBRemote = exports.FerrumServerConnection = exports.ferrumConnect = void 0;
const net_1 = require("net");
exports.FerrumServerClient = void 0;
const csharp_binary_stream_1 = require("csharp-binary-stream");
const zlib_1 = require("zlib");
const util_1 = require("util");
const bson_1 = require("bson");
const gunzipPromise = util_1.promisify(zlib_1.gunzip);
const gzipPromise = util_1.promisify(zlib_1.gzip);
let bsonBufferSize = 17825792;
const protcol_1 = require("./protcol");
const utils_1 = require("./utils");
const MAX_BUFFER_SIZE = 128 * 1024 * 1024;
var ApiMessageType;
(function (ApiMessageType) {
ApiMessageType[ApiMessageType["CREATE_DATABASE"] = 0] = "CREATE_DATABASE";
ApiMessageType[ApiMessageType["CREATE_DATABASE_IF_NOT_EXIST"] = 1] = "CREATE_DATABASE_IF_NOT_EXIST";
ApiMessageType[ApiMessageType["HAS_DATABASE"] = 2] = "HAS_DATABASE";
ApiMessageType[ApiMessageType["DROP_DATABASE"] = 3] = "DROP_DATABASE";
ApiMessageType[ApiMessageType["LIST_DATABASES"] = 4] = "LIST_DATABASES";
ApiMessageType[ApiMessageType["CLEAR_DATABASE"] = 5] = "CLEAR_DATABASE";
ApiMessageType[ApiMessageType["CREATE_INDEX"] = 6] = "CREATE_INDEX";
ApiMessageType[ApiMessageType["CREATE_INDEX_IF_NOT_EXIST"] = 7] = "CREATE_INDEX_IF_NOT_EXIST";
ApiMessageType[ApiMessageType["DELETE_INDEX"] = 8] = "DELETE_INDEX";
ApiMessageType[ApiMessageType["HAS_INDEX"] = 9] = "HAS_INDEX";
ApiMessageType[ApiMessageType["GET_INDEXES"] = 10] = "GET_INDEXES";
ApiMessageType[ApiMessageType["COMPACT"] = 11] = "COMPACT";
ApiMessageType[ApiMessageType["INDEX_HAS"] = 12] = "INDEX_HAS";
ApiMessageType[ApiMessageType["INDEX_GET"] = 13] = "INDEX_GET";
ApiMessageType[ApiMessageType["INDEX_SET"] = 14] = "INDEX_SET";
ApiMessageType[ApiMessageType["INDEX_DELETE"] = 15] = "INDEX_DELETE";
ApiMessageType[ApiMessageType["INDEX_GET_KEYS"] = 16] = "INDEX_GET_KEYS";
ApiMessageType[ApiMessageType["INDEX_CLEAR"] = 17] = "INDEX_CLEAR";
ApiMessageType[ApiMessageType["INDEX_GET_RECORD_COUNT"] = 18] = "INDEX_GET_RECORD_COUNT";
ApiMessageType[ApiMessageType["INDEX_GET_RECORD_SIZE"] = 19] = "INDEX_GET_RECORD_SIZE";
ApiMessageType[ApiMessageType["INDEX_READ_CHUNK"] = 20] = "INDEX_READ_CHUNK";
ApiMessageType[ApiMessageType["INDEX_READ_UNTIL"] = 21] = "INDEX_READ_UNTIL";
ApiMessageType[ApiMessageType["INDEX_OPEN_WRITE_STREAM"] = 22] = "INDEX_OPEN_WRITE_STREAM";
ApiMessageType[ApiMessageType["INDEX_CLOSE_WRITE_STREAM"] = 23] = "INDEX_CLOSE_WRITE_STREAM";
ApiMessageType[ApiMessageType["INDEX_WRITE_STREAM_WRITE_CHUNK"] = 24] = "INDEX_WRITE_STREAM_WRITE_CHUNK";
ApiMessageType[ApiMessageType["HEARTBEAT"] = 25] = "HEARTBEAT";
})(ApiMessageType || (ApiMessageType = {}));
function ferrumConnect(ip, port) {
return new Promise((resolve, reject) => {
const socket = new net_1.Socket();
socket.setNoDelay(true);
socket.connect(port, ip);
let client;
socket.once("connect", () => {
client = new FerrumServerClient(socket);
resolve(new FerrumServerConnection(client));
});
socket.once("error", (e) => {
reject(e);
});
socket.once("timeout", () => {
reject(new Error("Connection timeout"));
});
});
}
exports.ferrumConnect = ferrumConnect;
class FerrumServerClient {

@@ -159,10 +106,10 @@ constructor(socket) {

this.heartBeatPending = true;
const { bw, myId } = this.getSendWriter(ApiMessageType.HEARTBEAT, 0);
const { bw, myId } = this.getSendWriter(protcol_1.ApiMessageType.HEARTBEAT, 0);
this.sendMsg(bw);
const response = await this.getResponse(myId);
this.heartBeatPending = false;
const br = getBinaryReader(response);
const br = utils_1.getBinaryReader(response);
const success = br.readBoolean();
if (!success) {
return handleErrorResponse(br);
return utils_1.handleErrorResponse(br);
}

@@ -212,475 +159,3 @@ }

}
class FerrumServerConnection {
constructor(client) {
this.client = client;
}
disconnect() {
this.client.disconnect();
}
async createDatabaseIfNotExists(dbName) {
const { bw, myId } = this.client.getSendWriter(ApiMessageType.CREATE_DATABASE_IF_NOT_EXIST, dbName.length);
bw.writeString(dbName, csharp_binary_stream_1.Encoding.Utf8);
this.client.sendMsg(bw);
const response = await this.client.getResponse(myId);
const br = getBinaryReader(response);
const success = br.readBoolean();
if (!success) {
return handleErrorResponse(br);
}
else {
return new FerrumDBRemote(this.client, dbName);
}
}
async createDatabase(dbName) {
const { bw, myId } = this.client.getSendWriter(ApiMessageType.CREATE_DATABASE, dbName.length);
bw.writeString(dbName, csharp_binary_stream_1.Encoding.Utf8);
this.client.sendMsg(bw);
const response = await this.client.getResponse(myId);
const br = getBinaryReader(response);
const success = br.readBoolean();
if (!success) {
return handleErrorResponse(br);
}
else {
return new FerrumDBRemote(this.client, dbName);
}
}
async hasDatabase(dbName) {
const { bw, myId } = this.client.getSendWriter(ApiMessageType.HAS_DATABASE, dbName.length);
bw.writeString(dbName, csharp_binary_stream_1.Encoding.Utf8);
this.client.sendMsg(bw);
const response = await this.client.getResponse(myId);
const br = getBinaryReader(response);
const success = br.readBoolean();
if (!success) {
return handleErrorResponse(br);
}
else {
return br.readBoolean();
}
}
async dropDatabase(dbName) {
const { bw, myId } = this.client.getSendWriter(ApiMessageType.DROP_DATABASE, dbName.length);
bw.writeString(dbName, csharp_binary_stream_1.Encoding.Utf8);
this.client.sendMsg(bw);
const response = await this.client.getResponse(myId);
const br = getBinaryReader(response);
const success = br.readBoolean();
if (!success) {
return handleErrorResponse(br);
}
else {
return undefined;
}
}
async clearDatabase(dbName) {
const { bw, myId } = this.client.getSendWriter(ApiMessageType.CLEAR_DATABASE, dbName.length);
bw.writeString(dbName, csharp_binary_stream_1.Encoding.Utf8);
this.client.sendMsg(bw);
const response = await this.client.getResponse(myId);
const br = getBinaryReader(response);
const success = br.readBoolean();
if (!success) {
return handleErrorResponse(br);
}
return undefined;
}
async getDatabaseNames() {
const { bw, myId } = this.client.getSendWriter(ApiMessageType.LIST_DATABASES, 0);
this.client.sendMsg(bw);
const response = await this.client.getResponse(myId);
const br = getBinaryReader(response);
const success = br.readBoolean();
if (!success) {
return handleErrorResponse(br);
}
else {
const len = br.readInt();
const result = new Array(len);
for (let i = 0; i < len; i++) {
result[i] = br.readString(csharp_binary_stream_1.Encoding.Utf8);
}
return result;
}
}
getDatabase(dbName) {
return new FerrumDBRemote(this.client, dbName);
}
}
exports.FerrumServerConnection = FerrumServerConnection;
class FerrumDBRemote {
constructor(client, dbName) {
this.client = client;
this.dbName = dbName;
}
async createIndexIfNotExist(index, pageFileSize = 0) {
const { bw, myId } = this.client.getSendWriter(ApiMessageType.CREATE_INDEX_IF_NOT_EXIST, this.dbName.length + index.length);
bw.writeString(this.dbName, csharp_binary_stream_1.Encoding.Utf8);
bw.writeString(index, csharp_binary_stream_1.Encoding.Utf8);
bw.writeUnsignedInt(pageFileSize);
this.client.sendMsg(bw);
const response = await this.client.getResponse(myId);
const br = getBinaryReader(response);
const success = br.readBoolean();
if (!success) {
return handleErrorResponse(br);
}
else {
return this.getIndex(index);
}
}
getIndex(index, encoding = "bson", compression = "gzip") {
return new IndexRemote(this.client, this.dbName, index, encoding, compression);
}
async deleteIndex(index) {
const { bw, myId } = this.client.getSendWriter(ApiMessageType.DELETE_INDEX, index.length);
bw.writeString(this.dbName, csharp_binary_stream_1.Encoding.Utf8);
bw.writeString(index, csharp_binary_stream_1.Encoding.Utf8);
this.client.sendMsg(bw);
const response = await this.client.getResponse(myId);
const br = getBinaryReader(response);
const success = br.readBoolean();
if (!success) {
return handleErrorResponse(br);
}
else {
return undefined;
}
}
async createIndex(index, pageFileSize = 0) {
const { bw, myId } = this.client.getSendWriter(ApiMessageType.CREATE_INDEX, index.length);
bw.writeString(this.dbName, csharp_binary_stream_1.Encoding.Utf8);
bw.writeString(index, csharp_binary_stream_1.Encoding.Utf8);
bw.writeUnsignedInt(pageFileSize);
this.client.sendMsg(bw);
const response = await this.client.getResponse(myId);
const br = getBinaryReader(response);
const success = br.readBoolean();
if (!success) {
return handleErrorResponse(br);
}
else {
return this.getIndex(index);
}
}
async hasIndex(index) {
const { bw, myId } = this.client.getSendWriter(ApiMessageType.HAS_INDEX, index.length);
bw.writeString(this.dbName, csharp_binary_stream_1.Encoding.Utf8);
bw.writeString(index, csharp_binary_stream_1.Encoding.Utf8);
this.client.sendMsg(bw);
const response = await this.client.getResponse(myId);
const br = getBinaryReader(response);
const success = br.readBoolean();
if (!success) {
return handleErrorResponse(br);
}
else {
return br.readBoolean();
}
}
async getIndexes() {
const { bw, myId } = this.client.getSendWriter(ApiMessageType.GET_INDEXES, 0);
bw.writeString(this.dbName, csharp_binary_stream_1.Encoding.Utf8);
this.client.sendMsg(bw);
const response = await this.client.getResponse(myId);
const br = getBinaryReader(response);
const success = br.readBoolean();
if (!success) {
return handleErrorResponse(br);
}
else {
const len = br.readInt();
const result = new Array(len);
for (let i = 0; i < len; i++) {
result[i] = br.readString(csharp_binary_stream_1.Encoding.Utf8);
}
return result;
}
}
async compact() {
const { bw, myId } = this.client.getSendWriter(ApiMessageType.COMPACT, 0);
bw.writeString(this.dbName, csharp_binary_stream_1.Encoding.Utf8);
this.client.sendMsg(bw);
const response = await this.client.getResponse(myId);
const br = getBinaryReader(response);
const success = br.readBoolean();
if (!success) {
return handleErrorResponse(br);
}
else {
return undefined;
}
}
}
exports.FerrumDBRemote = FerrumDBRemote;
class IndexRemote {
constructor(client, database, indexKey, encoding, compression) {
this.client = client;
this.database = database;
this.encoding = encoding;
this.compression = compression;
this.indexKey = indexKey;
}
async has(key) {
const { bw, myId } = this.client.getSendWriter(ApiMessageType.INDEX_HAS, this.database.length + this.indexKey.length + key.length);
bw.writeString(this.database, csharp_binary_stream_1.Encoding.Utf8);
bw.writeString(this.indexKey, csharp_binary_stream_1.Encoding.Utf8);
bw.writeString(key, csharp_binary_stream_1.Encoding.Utf8);
this.client.sendMsg(bw);
const response = await this.client.getResponse(myId);
const br = getBinaryReader(response);
const success = br.readBoolean();
if (!success) {
return handleErrorResponse(br);
}
else {
return br.readBoolean();
}
}
async getRecordSize(key) {
const { bw, myId } = this.client.getSendWriter(ApiMessageType.INDEX_GET_RECORD_SIZE, this.database.length + this.indexKey.length + key.length);
bw.writeString(this.database, csharp_binary_stream_1.Encoding.Utf8);
bw.writeString(this.indexKey, csharp_binary_stream_1.Encoding.Utf8);
bw.writeString(key, csharp_binary_stream_1.Encoding.Utf8);
this.client.sendMsg(bw);
const response = await this.client.getResponse(myId);
const br = getBinaryReader(response);
const success = br.readBoolean();
if (!success) {
return handleErrorResponse(br);
}
else {
const len = br.readLong();
return len;
}
}
async getRecordCount() {
const { bw, myId } = this.client.getSendWriter(ApiMessageType.INDEX_GET_RECORD_COUNT, this.database.length + this.indexKey.length);
bw.writeString(this.database, csharp_binary_stream_1.Encoding.Utf8);
bw.writeString(this.indexKey, csharp_binary_stream_1.Encoding.Utf8);
this.client.sendMsg(bw);
const response = await this.client.getResponse(myId);
const br = getBinaryReader(response);
const success = br.readBoolean();
if (!success) {
return handleErrorResponse(br);
}
else {
const len = br.readInt();
return len;
}
}
async get(key) {
const { bw, myId } = this.client.getSendWriter(ApiMessageType.INDEX_GET, this.database.length + this.indexKey.length + key.length);
bw.writeString(this.database, csharp_binary_stream_1.Encoding.Utf8);
bw.writeString(this.indexKey, csharp_binary_stream_1.Encoding.Utf8);
bw.writeString(key, csharp_binary_stream_1.Encoding.Utf8);
this.client.sendMsg(bw);
const response = await this.client.getResponse(myId);
const br = getBinaryReader(response);
const success = br.readBoolean();
if (!success) {
return handleErrorResponse(br);
}
else {
const len = br.readInt();
try {
const result = Buffer.from(br.readBytes(len));
let decompressed;
let decodedValue;
switch (this.compression) {
case "gzip":
decompressed = await gunzipPromise(result);
break;
default:
decompressed = result;
break;
}
if (this.encoding === "bson") {
if (decompressed.length > bsonBufferSize) {
bson_1.setInternalBufferSize(decompressed.length);
bsonBufferSize = decompressed.length;
}
decodedValue = bson_1.deserialize(decompressed);
}
else if (this.encoding === "json") {
try {
decodedValue = JSON.parse(decompressed.toString("utf8"));
}
catch (e) {
throw new Error(`Failed to decode JSON for key ${key}. ${decompressed.toString("utf8")}`);
}
}
else if (this.encoding === "ndjson") {
decodedValue = decompressed
.toString("utf8")
.split("\n")
.map((e) => JSON.parse(e));
}
else if (this.encoding === "string") {
decodedValue = decompressed.toString("utf8");
}
else {
decodedValue = decompressed;
}
return decodedValue;
}
catch (e) {
throw new Error(`Failed to get ${key} from ${this.indexKey} \n\nCaused by: ${e}`);
}
}
}
async readChunk(key, offset, size) {
const { bw, myId } = this.client.getSendWriter(ApiMessageType.INDEX_GET, this.database.length + this.indexKey.length + key.length);
bw.writeString(this.database, csharp_binary_stream_1.Encoding.Utf8);
bw.writeString(this.indexKey, csharp_binary_stream_1.Encoding.Utf8);
bw.writeString(key, csharp_binary_stream_1.Encoding.Utf8);
bw.writeLong(offset);
bw.writeUnsignedInt(size);
this.client.sendMsg(bw);
const response = await this.client.getResponse(myId);
const br = getBinaryReader(response);
const success = br.readBoolean();
if (!success) {
return handleErrorResponse(br);
}
else {
const len = br.readInt();
const result = Buffer.from(br.readBytes(len));
return result;
}
}
// Alias for set
put(key, value) {
return this.set(key, value);
}
async set(key, value) {
let encodedData;
if (this.encoding === "bson") {
encodedData = encodeBSON(value);
}
else if (this.encoding === "json") {
encodedData = Buffer.from(JSON.stringify(value));
}
else if (this.encoding === "ndjson") {
if (Array.isArray(value)) {
encodedData = Buffer.from(value.map((e) => JSON.stringify(e)).join("\n"));
}
else {
throw new Error(`Non array data cannot be ndjson encoded`);
}
}
else if (this.encoding === "string") {
if (typeof value !== "string") {
throw new Error(`Invalid input. Expected string got ${typeof value}`);
}
encodedData = Buffer.from(value);
}
else {
if (value instanceof Buffer) {
encodedData = value;
}
else {
throw new Error(`Invalid input. Expected buffer`);
}
}
switch (this.compression) {
case "gzip":
encodedData = await gzipPromise(encodedData);
break;
default:
break;
}
const { bw, myId } = this.client.getSendWriter(ApiMessageType.INDEX_SET, this.database.length +
this.indexKey.length +
key.length +
encodedData.length);
bw.writeString(this.database, csharp_binary_stream_1.Encoding.Utf8);
bw.writeString(this.indexKey, csharp_binary_stream_1.Encoding.Utf8);
bw.writeString(key, csharp_binary_stream_1.Encoding.Utf8);
bw.writeInt(encodedData.length);
for (const byte of encodedData) {
bw.writeByte(byte);
}
this.client.sendMsg(bw);
const response = await this.client.getResponse(myId);
const br = getBinaryReader(response);
const success = br.readBoolean();
if (!success) {
return handleErrorResponse(br);
}
else {
return undefined;
}
}
async clear() {
const { bw, myId } = this.client.getSendWriter(ApiMessageType.INDEX_CLEAR, this.database.length + this.indexKey.length);
bw.writeString(this.database, csharp_binary_stream_1.Encoding.Utf8);
bw.writeString(this.indexKey, csharp_binary_stream_1.Encoding.Utf8);
this.client.sendMsg(bw);
const response = await this.client.getResponse(myId);
const br = getBinaryReader(response);
const success = br.readBoolean();
if (!success) {
return handleErrorResponse(br);
}
else {
return undefined;
}
}
async getKeys() {
const { bw, myId } = this.client.getSendWriter(ApiMessageType.INDEX_GET_KEYS, this.database.length + this.indexKey.length);
bw.writeString(this.database, csharp_binary_stream_1.Encoding.Utf8);
bw.writeString(this.indexKey, csharp_binary_stream_1.Encoding.Utf8);
this.client.sendMsg(bw);
const response = await this.client.getResponse(myId);
const br = getBinaryReader(response);
const success = br.readBoolean();
if (!success) {
return handleErrorResponse(br);
}
else {
const len = br.readInt();
const result = new Array(len);
for (let i = 0; i < len; i++) {
result[i] = br.readString(csharp_binary_stream_1.Encoding.Utf8);
}
return result;
}
}
}
exports.IndexRemote = IndexRemote;
function handleErrorResponse(br) {
throw new Error(`[Ferrum DB Server]${br.readString(csharp_binary_stream_1.Encoding.Utf8)}`);
}
function getBinaryReader(buffer) {
//Hack to skip the constructor of BinaryReader because it does unecessairy and expensive copying
const br = new csharp_binary_stream_1.BinaryReader(Buffer.from([]));
//@ts-ignore
br._stream = buffer.buffer;
//@ts-ignore
br._view = buffer;
//@ts-ignore
br._bufferStart = buffer.byteOffset;
//@ts-ignore
br._bufferLength = buffer.byteLength;
return br;
}
// The BSON library decided that it would be smart to not support automatic buffer expansion and you can't know ahead of time how much space you need
// So the best we can do is catch range errors and resize the buffer on error
function encodeBSON(value) {
try {
return bson_1.serialize(value);
}
catch (e) {
if (e instanceof RangeError) {
bsonBufferSize *= 2;
bson_1.setInternalBufferSize(bsonBufferSize);
return encodeBSON(value);
}
else {
throw e;
}
}
}
exports.FerrumServerClient = FerrumServerClient;
//# sourceMappingURL=client.js.map
{
"name": "ferrum-db-client",
"version": "0.0.5",
"main": "dist/client.js",
"typings": "dist/client.d.ts",
"version": "0.1.0",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"dependencies": {

@@ -7,0 +7,0 @@ "@types/node": "14.14.34",

@@ -0,75 +1,9 @@

import { BinaryWriter, Encoding } from "csharp-binary-stream";
import { Socket } from "net";
import { BinaryReader, BinaryWriter, Encoding } from "csharp-binary-stream";
import { gzip, gunzip } from "zlib";
import { promisify } from "util";
import { serialize, deserialize, setInternalBufferSize } from "bson";
import { ApiMessageType } from "./protcol";
import { getBinaryReader, handleErrorResponse } from "./utils";
const gunzipPromise = promisify(gunzip);
const gzipPromise = promisify(gzip);
export type SupportedEncodingTypes =
| "ndjson"
| "json"
| "bson"
| "string"
| "binary";
export type SupportedCompressionTypes = "gzip" | "none";
let bsonBufferSize = 17825792;
const MAX_BUFFER_SIZE = 128 * 1024 * 1024;
enum ApiMessageType {
CREATE_DATABASE = 0,
CREATE_DATABASE_IF_NOT_EXIST = 1,
HAS_DATABASE = 2,
DROP_DATABASE = 3,
LIST_DATABASES = 4,
CLEAR_DATABASE = 5,
CREATE_INDEX = 6,
CREATE_INDEX_IF_NOT_EXIST = 7,
DELETE_INDEX = 8,
HAS_INDEX = 9,
GET_INDEXES = 10,
COMPACT = 11,
INDEX_HAS = 12,
INDEX_GET = 13,
INDEX_SET = 14,
INDEX_DELETE = 15,
INDEX_GET_KEYS = 16,
INDEX_CLEAR = 17,
INDEX_GET_RECORD_COUNT = 18,
INDEX_GET_RECORD_SIZE = 19,
INDEX_READ_CHUNK = 20,
INDEX_READ_UNTIL = 21,
INDEX_OPEN_WRITE_STREAM = 22,
INDEX_CLOSE_WRITE_STREAM = 23,
INDEX_WRITE_STREAM_WRITE_CHUNK = 24,
HEARTBEAT = 25,
}
export function ferrumConnect(
ip: string,
port: number
): Promise<FerrumServerConnection> {
return new Promise((resolve, reject) => {
const socket = new Socket();
socket.setNoDelay(true);
socket.connect(port, ip);
let client: FerrumServerClient;
socket.once("connect", () => {
client = new FerrumServerClient(socket);
resolve(new FerrumServerConnection(client));
});
socket.once("error", (e) => {
reject(e);
});
socket.once("timeout", () => {
reject(new Error("Connection timeout"));
});
});
}
class FerrumServerClient {
export class FerrumServerClient {
protected id: number = 0;

@@ -268,629 +202,1 @@ public socket: Socket;

}
export class FerrumServerConnection {
private client: FerrumServerClient;
constructor(client: FerrumServerClient) {
this.client = client;
}
public disconnect(): void {
this.client.disconnect();
}
public async createDatabaseIfNotExists(
dbName: string
): Promise<FerrumDBRemote> {
const { bw, myId } = this.client.getSendWriter(
ApiMessageType.CREATE_DATABASE_IF_NOT_EXIST,
dbName.length
);
bw.writeString(dbName, Encoding.Utf8);
this.client.sendMsg(bw);
const response = await this.client.getResponse(myId);
const br = getBinaryReader(response);
const success = br.readBoolean();
if (!success) {
return handleErrorResponse(br);
} else {
return new FerrumDBRemote(this.client, dbName);
}
}
public async createDatabase(dbName: string): Promise<FerrumDBRemote> {
const { bw, myId } = this.client.getSendWriter(
ApiMessageType.CREATE_DATABASE,
dbName.length
);
bw.writeString(dbName, Encoding.Utf8);
this.client.sendMsg(bw);
const response = await this.client.getResponse(myId);
const br = getBinaryReader(response);
const success = br.readBoolean();
if (!success) {
return handleErrorResponse(br);
} else {
return new FerrumDBRemote(this.client, dbName);
}
}
public async hasDatabase(dbName: string): Promise<boolean> {
const { bw, myId } = this.client.getSendWriter(
ApiMessageType.HAS_DATABASE,
dbName.length
);
bw.writeString(dbName, Encoding.Utf8);
this.client.sendMsg(bw);
const response = await this.client.getResponse(myId);
const br = getBinaryReader(response);
const success = br.readBoolean();
if (!success) {
return handleErrorResponse(br);
} else {
return br.readBoolean();
}
}
public async dropDatabase(dbName: string): Promise<void> {
const { bw, myId } = this.client.getSendWriter(
ApiMessageType.DROP_DATABASE,
dbName.length
);
bw.writeString(dbName, Encoding.Utf8);
this.client.sendMsg(bw);
const response = await this.client.getResponse(myId);
const br = getBinaryReader(response);
const success = br.readBoolean();
if (!success) {
return handleErrorResponse(br);
} else {
return undefined;
}
}
public async clearDatabase(dbName: string): Promise<void> {
const { bw, myId } = this.client.getSendWriter(
ApiMessageType.CLEAR_DATABASE,
dbName.length
);
bw.writeString(dbName, Encoding.Utf8);
this.client.sendMsg(bw);
const response = await this.client.getResponse(myId);
const br = getBinaryReader(response);
const success = br.readBoolean();
if (!success) {
return handleErrorResponse(br);
}
return undefined;
}
public async getDatabaseNames(): Promise<string[]> {
const { bw, myId } = this.client.getSendWriter(
ApiMessageType.LIST_DATABASES,
0
);
this.client.sendMsg(bw);
const response = await this.client.getResponse(myId);
const br = getBinaryReader(response);
const success = br.readBoolean();
if (!success) {
return handleErrorResponse(br);
} else {
const len = br.readInt();
const result = new Array(len);
for (let i = 0; i < len; i++) {
result[i] = br.readString(Encoding.Utf8);
}
return result;
}
}
public getDatabase(dbName: string): FerrumDBRemote {
return new FerrumDBRemote(this.client, dbName);
}
}
export class FerrumDBRemote {
private client: FerrumServerClient;
private dbName: string;
constructor(client: FerrumServerClient, dbName: string) {
this.client = client;
this.dbName = dbName;
}
public async createIndexIfNotExist<T>(
index: string,
pageFileSize: number = 0
): Promise<IndexRemote<T>> {
const { bw, myId } = this.client.getSendWriter(
ApiMessageType.CREATE_INDEX_IF_NOT_EXIST,
this.dbName.length + index.length
);
bw.writeString(this.dbName, Encoding.Utf8);
bw.writeString(index, Encoding.Utf8);
bw.writeUnsignedInt(pageFileSize);
this.client.sendMsg(bw);
const response = await this.client.getResponse(myId);
const br = getBinaryReader(response);
const success = br.readBoolean();
if (!success) {
return handleErrorResponse(br);
} else {
return this.getIndex(index);
}
}
public getIndex<T>(
index: string,
encoding: SupportedEncodingTypes = "bson",
compression: SupportedCompressionTypes = "gzip"
): IndexRemote<T> {
return new IndexRemote<T>(
this.client,
this.dbName,
index,
encoding,
compression
);
}
public async deleteIndex(index: string): Promise<void> {
const { bw, myId } = this.client.getSendWriter(
ApiMessageType.DELETE_INDEX,
index.length
);
bw.writeString(this.dbName, Encoding.Utf8);
bw.writeString(index, Encoding.Utf8);
this.client.sendMsg(bw);
const response = await this.client.getResponse(myId);
const br = getBinaryReader(response);
const success = br.readBoolean();
if (!success) {
return handleErrorResponse(br);
} else {
return undefined;
}
}
public async createIndex<T>(
index: string,
pageFileSize: number = 0
): Promise<IndexRemote<T>> {
const { bw, myId } = this.client.getSendWriter(
ApiMessageType.CREATE_INDEX,
index.length
);
bw.writeString(this.dbName, Encoding.Utf8);
bw.writeString(index, Encoding.Utf8);
bw.writeUnsignedInt(pageFileSize);
this.client.sendMsg(bw);
const response = await this.client.getResponse(myId);
const br = getBinaryReader(response);
const success = br.readBoolean();
if (!success) {
return handleErrorResponse(br);
} else {
return this.getIndex(index);
}
}
public async hasIndex(index: string): Promise<boolean> {
const { bw, myId } = this.client.getSendWriter(
ApiMessageType.HAS_INDEX,
index.length
);
bw.writeString(this.dbName, Encoding.Utf8);
bw.writeString(index, Encoding.Utf8);
this.client.sendMsg(bw);
const response = await this.client.getResponse(myId);
const br = getBinaryReader(response);
const success = br.readBoolean();
if (!success) {
return handleErrorResponse(br);
} else {
return br.readBoolean();
}
}
public async getIndexes(): Promise<string[]> {
const { bw, myId } = this.client.getSendWriter(
ApiMessageType.GET_INDEXES,
0
);
bw.writeString(this.dbName, Encoding.Utf8);
this.client.sendMsg(bw);
const response = await this.client.getResponse(myId);
const br = getBinaryReader(response);
const success = br.readBoolean();
if (!success) {
return handleErrorResponse(br);
} else {
const len = br.readInt();
const result = new Array(len);
for (let i = 0; i < len; i++) {
result[i] = br.readString(Encoding.Utf8);
}
return result;
}
}
public async compact(): Promise<void> {
const { bw, myId } = this.client.getSendWriter(
ApiMessageType.COMPACT,
0
);
bw.writeString(this.dbName, Encoding.Utf8);
this.client.sendMsg(bw);
const response = await this.client.getResponse(myId);
const br = getBinaryReader(response);
const success = br.readBoolean();
if (!success) {
return handleErrorResponse(br);
} else {
return undefined;
}
}
}
export class IndexRemote<T> {
private client: FerrumServerClient;
private indexKey: string;
private encoding: SupportedEncodingTypes;
private compression: SupportedCompressionTypes;
private database: string;
constructor(
client: FerrumServerClient,
database: string,
indexKey: string,
encoding: SupportedEncodingTypes,
compression: SupportedCompressionTypes
) {
this.client = client;
this.database = database;
this.encoding = encoding;
this.compression = compression;
this.indexKey = indexKey;
}
public async has(key: string): Promise<boolean> {
const { bw, myId } = this.client.getSendWriter(
ApiMessageType.INDEX_HAS,
this.database.length + this.indexKey.length + key.length
);
bw.writeString(this.database, Encoding.Utf8);
bw.writeString(this.indexKey, Encoding.Utf8);
bw.writeString(key, Encoding.Utf8);
this.client.sendMsg(bw);
const response = await this.client.getResponse(myId);
const br = getBinaryReader(response);
const success = br.readBoolean();
if (!success) {
return handleErrorResponse(br);
} else {
return br.readBoolean();
}
}
public async getRecordSize(key: string): Promise<number> {
const { bw, myId } = this.client.getSendWriter(
ApiMessageType.INDEX_GET_RECORD_SIZE,
this.database.length + this.indexKey.length + key.length
);
bw.writeString(this.database, Encoding.Utf8);
bw.writeString(this.indexKey, Encoding.Utf8);
bw.writeString(key, Encoding.Utf8);
this.client.sendMsg(bw);
const response = await this.client.getResponse(myId);
const br = getBinaryReader(response);
const success = br.readBoolean();
if (!success) {
return handleErrorResponse(br);
} else {
const len = br.readLong();
return len;
}
}
public async getRecordCount(): Promise<number> {
const { bw, myId } = this.client.getSendWriter(
ApiMessageType.INDEX_GET_RECORD_COUNT,
this.database.length + this.indexKey.length
);
bw.writeString(this.database, Encoding.Utf8);
bw.writeString(this.indexKey, Encoding.Utf8);
this.client.sendMsg(bw);
const response = await this.client.getResponse(myId);
const br = getBinaryReader(response);
const success = br.readBoolean();
if (!success) {
return handleErrorResponse(br);
} else {
const len = br.readInt();
return len;
}
}
public async get(key: string): Promise<T> {
const { bw, myId } = this.client.getSendWriter(
ApiMessageType.INDEX_GET,
this.database.length + this.indexKey.length + key.length
);
bw.writeString(this.database, Encoding.Utf8);
bw.writeString(this.indexKey, Encoding.Utf8);
bw.writeString(key, Encoding.Utf8);
this.client.sendMsg(bw);
const response = await this.client.getResponse(myId);
const br = getBinaryReader(response);
const success = br.readBoolean();
if (!success) {
return handleErrorResponse(br);
} else {
const len = br.readInt();
try {
const result = Buffer.from(br.readBytes(len));
let decompressed: Buffer;
let decodedValue: any;
switch (this.compression) {
case "gzip":
decompressed = await gunzipPromise(result);
break;
default:
decompressed = result;
break;
}
if (this.encoding === "bson") {
if (decompressed.length > bsonBufferSize) {
setInternalBufferSize(decompressed.length);
bsonBufferSize = decompressed.length;
}
decodedValue = deserialize(decompressed);
} else if (this.encoding === "json") {
try {
decodedValue = JSON.parse(
decompressed.toString("utf8")
);
} catch (e) {
throw new Error(
`Failed to decode JSON for key ${key}. ${decompressed.toString(
"utf8"
)}`
);
}
} else if (this.encoding === "ndjson") {
decodedValue = decompressed
.toString("utf8")
.split("\n")
.map((e) => JSON.parse(e));
} else if (this.encoding === "string") {
decodedValue = decompressed.toString("utf8");
} else {
decodedValue = decompressed;
}
return decodedValue;
} catch (e) {
throw new Error(
`Failed to get ${key} from ${this.indexKey} \n\nCaused by: ${e}`
);
}
}
}
public async readChunk(
key: string,
offset: number,
size: number
): Promise<Buffer> {
const { bw, myId } = this.client.getSendWriter(
ApiMessageType.INDEX_GET,
this.database.length + this.indexKey.length + key.length
);
bw.writeString(this.database, Encoding.Utf8);
bw.writeString(this.indexKey, Encoding.Utf8);
bw.writeString(key, Encoding.Utf8);
bw.writeLong(offset);
bw.writeUnsignedInt(size);
this.client.sendMsg(bw);
const response = await this.client.getResponse(myId);
const br = getBinaryReader(response);
const success = br.readBoolean();
if (!success) {
return handleErrorResponse(br);
} else {
const len = br.readInt();
const result = Buffer.from(br.readBytes(len));
return result;
}
}
// Alias for set
public put(key: string, value: T): Promise<void> {
return this.set(key, value);
}
public async set(key: string, value: T): Promise<void> {
let encodedData: Buffer;
if (this.encoding === "bson") {
encodedData = encodeBSON(value);
} else if (this.encoding === "json") {
encodedData = Buffer.from(JSON.stringify(value));
} else if (this.encoding === "ndjson") {
if (Array.isArray(value)) {
encodedData = Buffer.from(
value.map((e) => JSON.stringify(e)).join("\n")
);
} else {
throw new Error(`Non array data cannot be ndjson encoded`);
}
} else if (this.encoding === "string") {
if (typeof value !== "string") {
throw new Error(
`Invalid input. Expected string got ${typeof value}`
);
}
encodedData = Buffer.from(value);
} else {
if (value instanceof Buffer) {
encodedData = value;
} else {
throw new Error(`Invalid input. Expected buffer`);
}
}
switch (this.compression) {
case "gzip":
encodedData = await gzipPromise(encodedData);
break;
default:
break;
}
const { bw, myId } = this.client.getSendWriter(
ApiMessageType.INDEX_SET,
this.database.length +
this.indexKey.length +
key.length +
encodedData.length
);
bw.writeString(this.database, Encoding.Utf8);
bw.writeString(this.indexKey, Encoding.Utf8);
bw.writeString(key, Encoding.Utf8);
bw.writeInt(encodedData.length);
for (const byte of encodedData) {
bw.writeByte(byte);
}
this.client.sendMsg(bw);
const response = await this.client.getResponse(myId);
const br = getBinaryReader(response);
const success = br.readBoolean();
if (!success) {
return handleErrorResponse(br);
} else {
return undefined;
}
}
public async clear(): Promise<void> {
const { bw, myId } = this.client.getSendWriter(
ApiMessageType.INDEX_CLEAR,
this.database.length + this.indexKey.length
);
bw.writeString(this.database, Encoding.Utf8);
bw.writeString(this.indexKey, Encoding.Utf8);
this.client.sendMsg(bw);
const response = await this.client.getResponse(myId);
const br = getBinaryReader(response);
const success = br.readBoolean();
if (!success) {
return handleErrorResponse(br);
} else {
return undefined;
}
}
public async getKeys(): Promise<string[]> {
const { bw, myId } = this.client.getSendWriter(
ApiMessageType.INDEX_GET_KEYS,
this.database.length + this.indexKey.length
);
bw.writeString(this.database, Encoding.Utf8);
bw.writeString(this.indexKey, Encoding.Utf8);
this.client.sendMsg(bw);
const response = await this.client.getResponse(myId);
const br = getBinaryReader(response);
const success = br.readBoolean();
if (!success) {
return handleErrorResponse(br);
} else {
const len = br.readInt();
const result = new Array(len);
for (let i = 0; i < len; i++) {
result[i] = br.readString(Encoding.Utf8);
}
return result;
}
}
}
function handleErrorResponse(br: BinaryReader): never {
throw new Error(`[Ferrum DB Server]${br.readString(Encoding.Utf8)}`);
}
function getBinaryReader(buffer: Buffer): BinaryReader {
//Hack to skip the constructor of BinaryReader because it does unecessairy and expensive copying
const br = new BinaryReader(Buffer.from([]));
//@ts-ignore
br._stream = buffer.buffer;
//@ts-ignore
br._view = buffer;
//@ts-ignore
br._bufferStart = buffer.byteOffset;
//@ts-ignore
br._bufferLength = buffer.byteLength;
return br;
}
// The BSON library decided that it would be smart to not support automatic buffer expansion and you can't know ahead of time how much space you need
// So the best we can do is catch range errors and resize the buffer on error
function encodeBSON(value: any): Buffer {
try {
return serialize(value);
} catch (e) {
if (e instanceof RangeError) {
bsonBufferSize *= 2;
setInternalBufferSize(bsonBufferSize);
return encodeBSON(value);
} else {
throw e;
}
}
}

Sorry, the diff of this file is not supported yet

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