eizen
Version:
Vector database Engine for ArchiveNET
1,365 lines (1,358 loc) • 66.2 kB
JavaScript
import { createRequire } from "node:module";
import { SetSDK } from "hollowdb";
import { ArweaveSigner } from "warp-contracts-plugin-deploy";
import { Heap } from "heap-js";
//#region rolldown:runtime
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __commonJS = (cb, mod) => function() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") for (var keys$1 = __getOwnPropNames(from), i = 0, n = keys$1.length, key; i < n; i++) {
key = keys$1[i];
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
get: ((k) => from[k]).bind(null, key),
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
});
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
value: mod,
enumerable: true
}) : target, mod));
var __require = /* @__PURE__ */ createRequire(import.meta.url);
//#endregion
//#region proto/hnsw_comm.js
var require_hnsw_comm = __commonJS({ "proto/hnsw_comm.js"(exports, module) {
var $protobuf = __require("protobufjs/minimal");
var $Reader = $protobuf.Reader, $Writer = $protobuf.Writer, $util = $protobuf.util;
var $root = $protobuf.roots["default"] || ($protobuf.roots["default"] = {});
$root.index_buffer = function() {
/**
* Namespace index_buffer.
* @exports index_buffer
* @namespace
*/
var index_buffer$1 = {};
index_buffer$1.LayerNode = function() {
/**
* Properties of a LayerNode.
* @memberof index_buffer
* @interface ILayerNode
* @property {number|null} [level] LayerNode level
* @property {number|null} [idx] LayerNode idx
* @property {boolean|null} [visible] LayerNode visible
* @property {Object.<string,number>|null} [neighbors] LayerNode neighbors
*/
/**
* Constructs a new LayerNode.
* @memberof index_buffer
* @classdesc Represents a LayerNode.
* @implements ILayerNode
* @constructor
* @param {index_buffer.ILayerNode=} [properties] Properties to set
*/
function LayerNode(properties) {
this.neighbors = {};
if (properties) {
for (var keys$1 = Object.keys(properties), i = 0; i < keys$1.length; ++i) if (properties[keys$1[i]] != null) this[keys$1[i]] = properties[keys$1[i]];
}
}
/**
* LayerNode level.
* @member {number} level
* @memberof index_buffer.LayerNode
* @instance
*/
LayerNode.prototype.level = 0;
/**
* LayerNode idx.
* @member {number} idx
* @memberof index_buffer.LayerNode
* @instance
*/
LayerNode.prototype.idx = 0;
/**
* LayerNode visible.
* @member {boolean} visible
* @memberof index_buffer.LayerNode
* @instance
*/
LayerNode.prototype.visible = false;
/**
* LayerNode neighbors.
* @member {Object.<string,number>} neighbors
* @memberof index_buffer.LayerNode
* @instance
*/
LayerNode.prototype.neighbors = $util.emptyObject;
/**
* Creates a new LayerNode instance using the specified properties.
* @function create
* @memberof index_buffer.LayerNode
* @static
* @param {index_buffer.ILayerNode=} [properties] Properties to set
* @returns {index_buffer.LayerNode} LayerNode instance
*/
LayerNode.create = function create(properties) {
return new LayerNode(properties);
};
/**
* Encodes the specified LayerNode message. Does not implicitly {@link index_buffer.LayerNode.verify|verify} messages.
* @function encode
* @memberof index_buffer.LayerNode
* @static
* @param {index_buffer.ILayerNode} message LayerNode message or plain object to encode
* @param {$protobuf.Writer} [writer] Writer to encode to
* @returns {$protobuf.Writer} Writer
*/
LayerNode.encode = function encode(message, writer) {
if (!writer) writer = $Writer.create();
if (message.level != null && Object.hasOwnProperty.call(message, "level")) writer.uint32(8).uint32(message.level);
if (message.idx != null && Object.hasOwnProperty.call(message, "idx")) writer.uint32(16).uint32(message.idx);
if (message.visible != null && Object.hasOwnProperty.call(message, "visible")) writer.uint32(24).bool(message.visible);
if (message.neighbors != null && Object.hasOwnProperty.call(message, "neighbors")) for (var keys$1 = Object.keys(message.neighbors), i = 0; i < keys$1.length; ++i) writer.uint32(34).fork().uint32(8).uint32(keys$1[i]).uint32(21).float(message.neighbors[keys$1[i]]).ldelim();
return writer;
};
/**
* Encodes the specified LayerNode message, length delimited. Does not implicitly {@link index_buffer.LayerNode.verify|verify} messages.
* @function encodeDelimited
* @memberof index_buffer.LayerNode
* @static
* @param {index_buffer.ILayerNode} message LayerNode message or plain object to encode
* @param {$protobuf.Writer} [writer] Writer to encode to
* @returns {$protobuf.Writer} Writer
*/
LayerNode.encodeDelimited = function encodeDelimited(message, writer) {
return this.encode(message, writer).ldelim();
};
/**
* Decodes a LayerNode message from the specified reader or buffer.
* @function decode
* @memberof index_buffer.LayerNode
* @static
* @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from
* @param {number} [length] Message length if known beforehand
* @returns {index_buffer.LayerNode} LayerNode
* @throws {Error} If the payload is not a reader or valid buffer
* @throws {$protobuf.util.ProtocolError} If required fields are missing
*/
LayerNode.decode = function decode(reader, length) {
if (!(reader instanceof $Reader)) reader = $Reader.create(reader);
var end = length === void 0 ? reader.len : reader.pos + length, message = new $root.index_buffer.LayerNode(), key, value;
while (reader.pos < end) {
var tag = reader.uint32();
switch (tag >>> 3) {
case 1: {
message.level = reader.uint32();
break;
}
case 2: {
message.idx = reader.uint32();
break;
}
case 3: {
message.visible = reader.bool();
break;
}
case 4: {
if (message.neighbors === $util.emptyObject) message.neighbors = {};
var end2 = reader.uint32() + reader.pos;
key = 0;
value = 0;
while (reader.pos < end2) {
var tag2 = reader.uint32();
switch (tag2 >>> 3) {
case 1:
key = reader.uint32();
break;
case 2:
value = reader.float();
break;
default:
reader.skipType(tag2 & 7);
break;
}
}
message.neighbors[key] = value;
break;
}
default:
reader.skipType(tag & 7);
break;
}
}
return message;
};
/**
* Decodes a LayerNode message from the specified reader or buffer, length delimited.
* @function decodeDelimited
* @memberof index_buffer.LayerNode
* @static
* @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from
* @returns {index_buffer.LayerNode} LayerNode
* @throws {Error} If the payload is not a reader or valid buffer
* @throws {$protobuf.util.ProtocolError} If required fields are missing
*/
LayerNode.decodeDelimited = function decodeDelimited(reader) {
if (!(reader instanceof $Reader)) reader = new $Reader(reader);
return this.decode(reader, reader.uint32());
};
/**
* Verifies a LayerNode message.
* @function verify
* @memberof index_buffer.LayerNode
* @static
* @param {Object.<string,*>} message Plain object to verify
* @returns {string|null} `null` if valid, otherwise the reason why it is not
*/
LayerNode.verify = function verify(message) {
if (typeof message !== "object" || message === null) return "object expected";
if (message.level != null && message.hasOwnProperty("level")) {
if (!$util.isInteger(message.level)) return "level: integer expected";
}
if (message.idx != null && message.hasOwnProperty("idx")) {
if (!$util.isInteger(message.idx)) return "idx: integer expected";
}
if (message.visible != null && message.hasOwnProperty("visible")) {
if (typeof message.visible !== "boolean") return "visible: boolean expected";
}
if (message.neighbors != null && message.hasOwnProperty("neighbors")) {
if (!$util.isObject(message.neighbors)) return "neighbors: object expected";
var key = Object.keys(message.neighbors);
for (var i = 0; i < key.length; ++i) {
if (!$util.key32Re.test(key[i])) return "neighbors: integer key{k:uint32} expected";
if (typeof message.neighbors[key[i]] !== "number") return "neighbors: number{k:uint32} expected";
}
}
return null;
};
/**
* Creates a LayerNode message from a plain object. Also converts values to their respective internal types.
* @function fromObject
* @memberof index_buffer.LayerNode
* @static
* @param {Object.<string,*>} object Plain object
* @returns {index_buffer.LayerNode} LayerNode
*/
LayerNode.fromObject = function fromObject(object) {
if (object instanceof $root.index_buffer.LayerNode) return object;
var message = new $root.index_buffer.LayerNode();
if (object.level != null) message.level = object.level >>> 0;
if (object.idx != null) message.idx = object.idx >>> 0;
if (object.visible != null) message.visible = Boolean(object.visible);
if (object.neighbors) {
if (typeof object.neighbors !== "object") throw TypeError(".index_buffer.LayerNode.neighbors: object expected");
message.neighbors = {};
for (var keys$1 = Object.keys(object.neighbors), i = 0; i < keys$1.length; ++i) message.neighbors[keys$1[i]] = Number(object.neighbors[keys$1[i]]);
}
return message;
};
/**
* Creates a plain object from a LayerNode message. Also converts values to other types if specified.
* @function toObject
* @memberof index_buffer.LayerNode
* @static
* @param {index_buffer.LayerNode} message LayerNode
* @param {$protobuf.IConversionOptions} [options] Conversion options
* @returns {Object.<string,*>} Plain object
*/
LayerNode.toObject = function toObject(message, options) {
if (!options) options = {};
var object = {};
if (options.objects || options.defaults) object.neighbors = {};
if (options.defaults) {
object.level = 0;
object.idx = 0;
object.visible = false;
}
if (message.level != null && message.hasOwnProperty("level")) object.level = message.level;
if (message.idx != null && message.hasOwnProperty("idx")) object.idx = message.idx;
if (message.visible != null && message.hasOwnProperty("visible")) object.visible = message.visible;
var keys2;
if (message.neighbors && (keys2 = Object.keys(message.neighbors)).length) {
object.neighbors = {};
for (var j = 0; j < keys2.length; ++j) object.neighbors[keys2[j]] = options.json && !isFinite(message.neighbors[keys2[j]]) ? String(message.neighbors[keys2[j]]) : message.neighbors[keys2[j]];
}
return object;
};
/**
* Converts this LayerNode to JSON.
* @function toJSON
* @memberof index_buffer.LayerNode
* @instance
* @returns {Object.<string,*>} JSON object
*/
LayerNode.prototype.toJSON = function toJSON() {
return this.constructor.toObject(this, $protobuf.util.toJSONOptions);
};
/**
* Gets the default type url for LayerNode
* @function getTypeUrl
* @memberof index_buffer.LayerNode
* @static
* @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com")
* @returns {string} The default type url
*/
LayerNode.getTypeUrl = function getTypeUrl(typeUrlPrefix) {
if (typeUrlPrefix === void 0) typeUrlPrefix = "type.googleapis.com";
return typeUrlPrefix + "/index_buffer.LayerNode";
};
return LayerNode;
}();
index_buffer$1.Point = function() {
/**
* Properties of a Point.
* @memberof index_buffer
* @interface IPoint
* @property {number|null} [idx] Point idx
* @property {Array.<number>|null} [v] Point v
*/
/**
* Constructs a new Point.
* @memberof index_buffer
* @classdesc Represents a Point.
* @implements IPoint
* @constructor
* @param {index_buffer.IPoint=} [properties] Properties to set
*/
function Point(properties) {
this.v = [];
if (properties) {
for (var keys$1 = Object.keys(properties), i = 0; i < keys$1.length; ++i) if (properties[keys$1[i]] != null) this[keys$1[i]] = properties[keys$1[i]];
}
}
/**
* Point idx.
* @member {number} idx
* @memberof index_buffer.Point
* @instance
*/
Point.prototype.idx = 0;
/**
* Point v.
* @member {Array.<number>} v
* @memberof index_buffer.Point
* @instance
*/
Point.prototype.v = $util.emptyArray;
/**
* Creates a new Point instance using the specified properties.
* @function create
* @memberof index_buffer.Point
* @static
* @param {index_buffer.IPoint=} [properties] Properties to set
* @returns {index_buffer.Point} Point instance
*/
Point.create = function create(properties) {
return new Point(properties);
};
/**
* Encodes the specified Point message. Does not implicitly {@link index_buffer.Point.verify|verify} messages.
* @function encode
* @memberof index_buffer.Point
* @static
* @param {index_buffer.IPoint} message Point message or plain object to encode
* @param {$protobuf.Writer} [writer] Writer to encode to
* @returns {$protobuf.Writer} Writer
*/
Point.encode = function encode(message, writer) {
if (!writer) writer = $Writer.create();
if (message.idx != null && Object.hasOwnProperty.call(message, "idx")) writer.uint32(8).uint32(message.idx);
if (message.v != null && message.v.length) {
writer.uint32(18).fork();
for (var i = 0; i < message.v.length; ++i) writer.float(message.v[i]);
writer.ldelim();
}
return writer;
};
/**
* Encodes the specified Point message, length delimited. Does not implicitly {@link index_buffer.Point.verify|verify} messages.
* @function encodeDelimited
* @memberof index_buffer.Point
* @static
* @param {index_buffer.IPoint} message Point message or plain object to encode
* @param {$protobuf.Writer} [writer] Writer to encode to
* @returns {$protobuf.Writer} Writer
*/
Point.encodeDelimited = function encodeDelimited(message, writer) {
return this.encode(message, writer).ldelim();
};
/**
* Decodes a Point message from the specified reader or buffer.
* @function decode
* @memberof index_buffer.Point
* @static
* @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from
* @param {number} [length] Message length if known beforehand
* @returns {index_buffer.Point} Point
* @throws {Error} If the payload is not a reader or valid buffer
* @throws {$protobuf.util.ProtocolError} If required fields are missing
*/
Point.decode = function decode(reader, length) {
if (!(reader instanceof $Reader)) reader = $Reader.create(reader);
var end = length === void 0 ? reader.len : reader.pos + length, message = new $root.index_buffer.Point();
while (reader.pos < end) {
var tag = reader.uint32();
switch (tag >>> 3) {
case 1: {
message.idx = reader.uint32();
break;
}
case 2: {
if (!(message.v && message.v.length)) message.v = [];
if ((tag & 7) === 2) {
var end2 = reader.uint32() + reader.pos;
while (reader.pos < end2) message.v.push(reader.float());
} else message.v.push(reader.float());
break;
}
default:
reader.skipType(tag & 7);
break;
}
}
return message;
};
/**
* Decodes a Point message from the specified reader or buffer, length delimited.
* @function decodeDelimited
* @memberof index_buffer.Point
* @static
* @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from
* @returns {index_buffer.Point} Point
* @throws {Error} If the payload is not a reader or valid buffer
* @throws {$protobuf.util.ProtocolError} If required fields are missing
*/
Point.decodeDelimited = function decodeDelimited(reader) {
if (!(reader instanceof $Reader)) reader = new $Reader(reader);
return this.decode(reader, reader.uint32());
};
/**
* Verifies a Point message.
* @function verify
* @memberof index_buffer.Point
* @static
* @param {Object.<string,*>} message Plain object to verify
* @returns {string|null} `null` if valid, otherwise the reason why it is not
*/
Point.verify = function verify(message) {
if (typeof message !== "object" || message === null) return "object expected";
if (message.idx != null && message.hasOwnProperty("idx")) {
if (!$util.isInteger(message.idx)) return "idx: integer expected";
}
if (message.v != null && message.hasOwnProperty("v")) {
if (!Array.isArray(message.v)) return "v: array expected";
for (var i = 0; i < message.v.length; ++i) if (typeof message.v[i] !== "number") return "v: number[] expected";
}
return null;
};
/**
* Creates a Point message from a plain object. Also converts values to their respective internal types.
* @function fromObject
* @memberof index_buffer.Point
* @static
* @param {Object.<string,*>} object Plain object
* @returns {index_buffer.Point} Point
*/
Point.fromObject = function fromObject(object) {
if (object instanceof $root.index_buffer.Point) return object;
var message = new $root.index_buffer.Point();
if (object.idx != null) message.idx = object.idx >>> 0;
if (object.v) {
if (!Array.isArray(object.v)) throw TypeError(".index_buffer.Point.v: array expected");
message.v = [];
for (var i = 0; i < object.v.length; ++i) message.v[i] = Number(object.v[i]);
}
return message;
};
/**
* Creates a plain object from a Point message. Also converts values to other types if specified.
* @function toObject
* @memberof index_buffer.Point
* @static
* @param {index_buffer.Point} message Point
* @param {$protobuf.IConversionOptions} [options] Conversion options
* @returns {Object.<string,*>} Plain object
*/
Point.toObject = function toObject(message, options) {
if (!options) options = {};
var object = {};
if (options.arrays || options.defaults) object.v = [];
if (options.defaults) object.idx = 0;
if (message.idx != null && message.hasOwnProperty("idx")) object.idx = message.idx;
if (message.v && message.v.length) {
object.v = [];
for (var j = 0; j < message.v.length; ++j) object.v[j] = options.json && !isFinite(message.v[j]) ? String(message.v[j]) : message.v[j];
}
return object;
};
/**
* Converts this Point to JSON.
* @function toJSON
* @memberof index_buffer.Point
* @instance
* @returns {Object.<string,*>} JSON object
*/
Point.prototype.toJSON = function toJSON() {
return this.constructor.toObject(this, $protobuf.util.toJSONOptions);
};
/**
* Gets the default type url for Point
* @function getTypeUrl
* @memberof index_buffer.Point
* @static
* @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com")
* @returns {string} The default type url
*/
Point.getTypeUrl = function getTypeUrl(typeUrlPrefix) {
if (typeUrlPrefix === void 0) typeUrlPrefix = "type.googleapis.com";
return typeUrlPrefix + "/index_buffer.Point";
};
return Point;
}();
index_buffer$1.PointQuant = function() {
/**
* Properties of a PointQuant.
* @memberof index_buffer
* @interface IPointQuant
* @property {number|null} [idx] PointQuant idx
* @property {Array.<number>|null} [v] PointQuant v
*/
/**
* Constructs a new PointQuant.
* @memberof index_buffer
* @classdesc Represents a PointQuant.
* @implements IPointQuant
* @constructor
* @param {index_buffer.IPointQuant=} [properties] Properties to set
*/
function PointQuant(properties) {
this.v = [];
if (properties) {
for (var keys$1 = Object.keys(properties), i = 0; i < keys$1.length; ++i) if (properties[keys$1[i]] != null) this[keys$1[i]] = properties[keys$1[i]];
}
}
/**
* PointQuant idx.
* @member {number} idx
* @memberof index_buffer.PointQuant
* @instance
*/
PointQuant.prototype.idx = 0;
/**
* PointQuant v.
* @member {Array.<number>} v
* @memberof index_buffer.PointQuant
* @instance
*/
PointQuant.prototype.v = $util.emptyArray;
/**
* Creates a new PointQuant instance using the specified properties.
* @function create
* @memberof index_buffer.PointQuant
* @static
* @param {index_buffer.IPointQuant=} [properties] Properties to set
* @returns {index_buffer.PointQuant} PointQuant instance
*/
PointQuant.create = function create(properties) {
return new PointQuant(properties);
};
/**
* Encodes the specified PointQuant message. Does not implicitly {@link index_buffer.PointQuant.verify|verify} messages.
* @function encode
* @memberof index_buffer.PointQuant
* @static
* @param {index_buffer.IPointQuant} message PointQuant message or plain object to encode
* @param {$protobuf.Writer} [writer] Writer to encode to
* @returns {$protobuf.Writer} Writer
*/
PointQuant.encode = function encode(message, writer) {
if (!writer) writer = $Writer.create();
if (message.idx != null && Object.hasOwnProperty.call(message, "idx")) writer.uint32(8).uint32(message.idx);
if (message.v != null && message.v.length) {
writer.uint32(18).fork();
for (var i = 0; i < message.v.length; ++i) writer.uint32(message.v[i]);
writer.ldelim();
}
return writer;
};
/**
* Encodes the specified PointQuant message, length delimited. Does not implicitly {@link index_buffer.PointQuant.verify|verify} messages.
* @function encodeDelimited
* @memberof index_buffer.PointQuant
* @static
* @param {index_buffer.IPointQuant} message PointQuant message or plain object to encode
* @param {$protobuf.Writer} [writer] Writer to encode to
* @returns {$protobuf.Writer} Writer
*/
PointQuant.encodeDelimited = function encodeDelimited(message, writer) {
return this.encode(message, writer).ldelim();
};
/**
* Decodes a PointQuant message from the specified reader or buffer.
* @function decode
* @memberof index_buffer.PointQuant
* @static
* @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from
* @param {number} [length] Message length if known beforehand
* @returns {index_buffer.PointQuant} PointQuant
* @throws {Error} If the payload is not a reader or valid buffer
* @throws {$protobuf.util.ProtocolError} If required fields are missing
*/
PointQuant.decode = function decode(reader, length) {
if (!(reader instanceof $Reader)) reader = $Reader.create(reader);
var end = length === void 0 ? reader.len : reader.pos + length, message = new $root.index_buffer.PointQuant();
while (reader.pos < end) {
var tag = reader.uint32();
switch (tag >>> 3) {
case 1: {
message.idx = reader.uint32();
break;
}
case 2: {
if (!(message.v && message.v.length)) message.v = [];
if ((tag & 7) === 2) {
var end2 = reader.uint32() + reader.pos;
while (reader.pos < end2) message.v.push(reader.uint32());
} else message.v.push(reader.uint32());
break;
}
default:
reader.skipType(tag & 7);
break;
}
}
return message;
};
/**
* Decodes a PointQuant message from the specified reader or buffer, length delimited.
* @function decodeDelimited
* @memberof index_buffer.PointQuant
* @static
* @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from
* @returns {index_buffer.PointQuant} PointQuant
* @throws {Error} If the payload is not a reader or valid buffer
* @throws {$protobuf.util.ProtocolError} If required fields are missing
*/
PointQuant.decodeDelimited = function decodeDelimited(reader) {
if (!(reader instanceof $Reader)) reader = new $Reader(reader);
return this.decode(reader, reader.uint32());
};
/**
* Verifies a PointQuant message.
* @function verify
* @memberof index_buffer.PointQuant
* @static
* @param {Object.<string,*>} message Plain object to verify
* @returns {string|null} `null` if valid, otherwise the reason why it is not
*/
PointQuant.verify = function verify(message) {
if (typeof message !== "object" || message === null) return "object expected";
if (message.idx != null && message.hasOwnProperty("idx")) {
if (!$util.isInteger(message.idx)) return "idx: integer expected";
}
if (message.v != null && message.hasOwnProperty("v")) {
if (!Array.isArray(message.v)) return "v: array expected";
for (var i = 0; i < message.v.length; ++i) if (!$util.isInteger(message.v[i])) return "v: integer[] expected";
}
return null;
};
/**
* Creates a PointQuant message from a plain object. Also converts values to their respective internal types.
* @function fromObject
* @memberof index_buffer.PointQuant
* @static
* @param {Object.<string,*>} object Plain object
* @returns {index_buffer.PointQuant} PointQuant
*/
PointQuant.fromObject = function fromObject(object) {
if (object instanceof $root.index_buffer.PointQuant) return object;
var message = new $root.index_buffer.PointQuant();
if (object.idx != null) message.idx = object.idx >>> 0;
if (object.v) {
if (!Array.isArray(object.v)) throw TypeError(".index_buffer.PointQuant.v: array expected");
message.v = [];
for (var i = 0; i < object.v.length; ++i) message.v[i] = object.v[i] >>> 0;
}
return message;
};
/**
* Creates a plain object from a PointQuant message. Also converts values to other types if specified.
* @function toObject
* @memberof index_buffer.PointQuant
* @static
* @param {index_buffer.PointQuant} message PointQuant
* @param {$protobuf.IConversionOptions} [options] Conversion options
* @returns {Object.<string,*>} Plain object
*/
PointQuant.toObject = function toObject(message, options) {
if (!options) options = {};
var object = {};
if (options.arrays || options.defaults) object.v = [];
if (options.defaults) object.idx = 0;
if (message.idx != null && message.hasOwnProperty("idx")) object.idx = message.idx;
if (message.v && message.v.length) {
object.v = [];
for (var j = 0; j < message.v.length; ++j) object.v[j] = message.v[j];
}
return object;
};
/**
* Converts this PointQuant to JSON.
* @function toJSON
* @memberof index_buffer.PointQuant
* @instance
* @returns {Object.<string,*>} JSON object
*/
PointQuant.prototype.toJSON = function toJSON() {
return this.constructor.toObject(this, $protobuf.util.toJSONOptions);
};
/**
* Gets the default type url for PointQuant
* @function getTypeUrl
* @memberof index_buffer.PointQuant
* @static
* @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com")
* @returns {string} The default type url
*/
PointQuant.getTypeUrl = function getTypeUrl(typeUrlPrefix) {
if (typeUrlPrefix === void 0) typeUrlPrefix = "type.googleapis.com";
return typeUrlPrefix + "/index_buffer.PointQuant";
};
return PointQuant;
}();
return index_buffer$1;
}();
module.exports = $root;
} });
//#endregion
//#region src/codec.ts
var import_hnsw_comm = __toESM(require_hnsw_comm());
/** Encodes a point to protobuf & base64's it. */
function encodePoint(q) {
const qe = import_hnsw_comm.index_buffer.Point.encode(q).finish();
return Buffer.from(qe).toString("base64");
}
/** Decodes a point from base64 encoded protobuf. */
function decodePoint(data) {
const dec = Buffer.from(data, "base64");
return import_hnsw_comm.index_buffer.Point.decode(dec);
}
/** Encodes a layerNode to protobuf & base64's it. */
function encodeLayerNode(n) {
const ne = import_hnsw_comm.index_buffer.LayerNode.encode({
...n,
visible: n.visible || true
}).finish();
return Buffer.from(ne).toString("base64");
}
/** Decodes a layerNode from base64 encoded protobuf. */
function decodeLayerNode(data) {
const dec = Buffer.from(data, "base64");
return import_hnsw_comm.index_buffer.LayerNode.decode(dec);
}
//#endregion
//#region src/db/common/index.ts
/** KVdb key generators for different data types */
const keys = {
layers: "layers",
ep: "ep",
points: "points",
metadata: (idx) => `m:${idx}`,
point: (idx) => `${idx}`,
neighbor: (layer, idx) => `${layer}__${idx}`
};
/** Parse JSON string, returns null for falsy values */
function safeParse(data) {
return data ? JSON.parse(data) : null;
}
//#endregion
//#region src/db/index.ts
/**
* EizenMemory - A distributed memory implementation for vector similarity search
*
* This class provides a hierarchical navigation structure for approximate nearest neighbor search.
* It uses layered graphs where higher layers contain fewer, more connected nodes for efficient search.
*
* Key concepts:
* - Points: Vector data stored with unique indices
* - Layers: Hierarchical levels of the graph structure
* - Neighbors: Connected nodes in each layer forming the searchable graph
* - Entry Point (EP): Starting node for search operations
*
* @template M - Type for optional metadata associated with points
*/
var EizenMemory = class {
client;
/**
* Deploy a new contract for this database instance
*
* @param initialState - Initial state configuration for the contract
* @param source - ( Optional) source transaction ID for contract deployment
* @returns The deployed contract transaction ID
*/
async deploy(initialState, source = "") {
const { contractTxId } = await this.client.warp.deployFromSourceTx({
wallet: this.client.signer,
srcTxId: source,
initState: JSON.stringify(initialState)
});
return contractTxId;
}
constructor(client) {
this.client = client;
}
/**
* Get the current entry point index for search operations
* The entry point is the starting node for navigating the graph structure
*/
async get_ep() {
const ep = await this.client.get(keys.ep);
return ep === null ? null : Number.parseInt(ep);
}
/**
* Set the entry point for search operations
* Should typically be a well-connected node in the highest layer
*/
async set_ep(ep) {
await this.client.set(keys.ep, ep.toString());
}
/**
* Retrieve a single point (vector) by its index
*
* @param idx - Unique identifier for the point
* @returns The point data
* @throws Error if point doesn't exist or has no value
*/
async get_point(idx) {
const data = await this.client.get(keys.point(idx));
if (!data) throw new Error(`No point with index ${idx}`);
const point = decodePoint(data);
if (!point.v) throw new Error(`Point at index ${idx} has no value`);
return point.v;
}
/**
* Retrieve multiple points in a single operation for better performance
*
* @param idxs - Array of point indices to retrieve
* @returns Array of points in the same order as input indices
* @throws Error if any point is missing or invalid
*/
async get_points(idxs) {
if (idxs.length === 0) return [];
const datas = await this.safe_get_many(idxs.map((idx) => keys.point(idx)));
const nullPos = datas.indexOf(null);
if (nullPos !== -1) throw new Error(`No point with index ${idxs[nullPos]}`);
const points = datas.map((data, i) => {
if (data === null) throw new Error(`No data for point at index ${idxs[i]}`);
return decodePoint(data);
});
return points.map((point, i) => {
if (!point.v) throw new Error(`Point at index ${idxs[i]} has no value`);
return point.v;
});
}
/**
* Add a new point to the database and assign it the next available index
*
* @param q - Point data to store
* @returns The assigned index for this point
*/
async new_point(q) {
const idx = await this.get_datasize();
const point = encodePoint({
v: q,
idx
});
await this.client.set(keys.point(idx), point);
await this.client.set(keys.points, (idx + 1).toString());
return idx;
}
/**
* Get the total number of layers in the hierarchical structure
* Higher layers have fewer, more connected nodes for efficient search
*/
async get_num_layers() {
const numLayers = await this.client.get(keys.layers);
return numLayers ? Number.parseInt(numLayers) : 0;
}
/**
* Get the total number of points stored in the database
* This represents the next available index for new points
*/
async get_datasize() {
const datasize = await this.client.get(keys.points);
return datasize ? Number.parseInt(datasize) : 0;
}
/**
* Get the neighbor connections for a specific node in a specific layer
*
* @param layer - Layer level (0 = base layer, higher = more sparse)
* @param idx - Node index within that layer
* @returns The neighbor connections for this node
* @throws Error if node doesn't exist or has no neighbors
*/
async get_neighbor(layer, idx) {
const data = await this.client.get(keys.neighbor(layer, idx));
if (!data) throw new Error(`No neighbors at layer ${layer}, index ${idx}"`);
const node = decodeLayerNode(data);
if (!node.neighbors) throw new Error(`Node at layer ${layer}, index ${idx} has no neighbors`);
return node.neighbors;
}
/**
* Get neighbor connections for multiple nodes in a layer (batch operation)
*
* @param layer - Layer level to query
* @param idxs - Array of node indices to retrieve
* @returns Graph object mapping node indices to their neighbor lists
* @throws Error if any node is missing or invalid
*/
async get_neighbors(layer, idxs) {
const datas = await this.safe_get_many(idxs.map((idx) => keys.neighbor(layer, idx)));
const nullPos = datas.indexOf(null);
if (nullPos !== -1) throw new Error(`No neighbors at layer ${layer}, index ${idxs[nullPos]}"`);
const nodes = datas.map((data, i) => {
if (data === null) throw new Error(`No data for neighbor at layer ${layer}, index ${idxs[i]}`);
return decodeLayerNode(data);
});
const neighbors = nodes.map((node, i) => {
if (!node.neighbors) throw new Error(`Node at layer ${layer}, index ${idxs[i]} has no neighbors`);
return node.neighbors;
});
return Object.fromEntries(idxs.map((idx, i) => [idx, neighbors[i]]));
}
/**
* Create or update neighbor connections for a single node
*
* @param layer - Layer level where the node exists
* @param idx - Node index to update
* @param node - New neighbor connections for this node
*/
async upsert_neighbor(layer, idx, node) {
const data = encodeLayerNode({
idx,
level: layer,
neighbors: node
});
await this.client.set(keys.neighbor(layer, idx), data);
}
/**
* Batch update neighbor connections for multiple nodes in a layer
* More efficient than individual upsert operations for bulk updates
*
* @param layer - Layer level to update
* @param nodes - Graph mapping node indices to their new neighbor connections
*/
async upsert_neighbors(layer, nodes) {
await this.safe_set_many(Object.keys(nodes).map((idx) => {
const i = Number.parseInt(idx);
const key = keys.neighbor(layer, i);
const value = encodeLayerNode({
idx: i,
level: layer,
neighbors: nodes[i]
});
return [key, value];
}));
}
/**
* Initialize a new layer and add a node with empty neighbors
* This creates a new level in the hierarchical structure
*
* WARNING: Concurrent calls may cause race conditions in layer counting
*
* @param idx - Index of the node to add to the new layer
*/
async new_neighbor(idx) {
const l = await this.get_num_layers();
await this.upsert_neighbor(l, idx, {});
await this.client.set(keys.layers, (l + 1).toString());
}
/**
* Retrieve optional metadata associated with a point
*
* @param idx - Point index
* @returns Metadata object or null if none exists
*/
async get_metadata(idx) {
const data = await this.client.get(keys.metadata(idx));
return safeParse(data);
}
/**
* Retrieve metadata for multiple points
* Returns null for points that have no metadata
*
* @param idxs - Array of point indices
* @returns Array of metadata objects (or null) in same order as input
*/
async get_metadatas(idxs) {
return Promise.all(idxs.map((idx) => this.get_metadata(idx)));
}
/**
* Store metadata for a specific point
*
* @param idx - Point index
* @param data - Metadata to associate with this point
*/
async set_metadata(idx, data) {
await this.client.set(keys.metadata(idx), JSON.stringify(data));
}
/**
* Safely retrieve multiple keys with automatic request splitting
*
* Base-DB has transaction size limits. This method automatically splits
* large requests into smaller chunks when the limit is exceeded.
* Uses recursive binary splitting on errors.
*/
async safe_get_many(keys$1) {
try {
return await this.client.getMany(keys$1);
} catch (err) {
const half = Math.floor(keys$1.length >> 1);
return await Promise.all([this.safe_get_many(keys$1.slice(0, half)), this.safe_get_many(keys$1.slice(half))]).then((results) => results.flat());
}
}
/**
* Safely set multiple key-value pairs with automatic request splitting
*
* Similar to safe_get_many, this handles transaction size limits by
* recursively splitting large batches into smaller ones.
*/
async safe_set_many(entries) {
try {
await this.client.setMany(entries.map((e) => e[0]), entries.map((e) => e[1]));
} catch (err) {
const half = Math.floor(entries.length >> 1);
await Promise.all([this.safe_set_many(entries.slice(0, half)), this.safe_set_many(entries.slice(half))]).then((results) => results.flat());
}
}
toString() {
return "yay, EizenDB Set with Protobufs";
}
};
//#endregion
//#region src/utils/index.ts
/**
* HNSW Utility Functions
* =====================
*
* This module provides utility functions and data structures used by the HNSW algorithm,
* including distance functions, heap data structures, and vector operations.
*/
/**
* A specialized min-heap for Node types used in HNSW search algorithms.
*
* This heap automatically sorts nodes by their distance values (first element of the tuple).
* It's used extensively in the search algorithms to maintain candidate lists and results.
*
* @example
* ```typescript
* const heap = new NodeHeap();
* heap.push([0.5, 10]); // Distance 0.5 to point 10
* heap.push([0.2, 5]); // Distance 0.2 to point 5
*
* const closest = heap.pop(); // Returns [0.2, 5] (smallest distance)
* ```
*/
var NodeHeap = class extends Heap {
constructor(elems = []) {
super(compareNode);
if (elems.length !== 0) super.addAll(elems);
}
};
/**
* Comparator function for Node types that compares by distance.
*
* Used by the NodeHeap to maintain proper ordering. Compares the first element
* of the Node tuple (the distance value).
*
* @param a First node to compare
* @param b Second node to compare
* @returns Negative if a < b, positive if a > b, zero if equal
*/
function compareNode(a, b) {
return a[0] - b[0];
}
/**
* Computes the dot product (inner product) of two vectors.
*
* The dot product measures the cosine of the angle between vectors scaled by their magnitudes.
* It's a fundamental operation used in many distance calculations.
*
* @param a First vector
* @param b Second vector
* @returns The dot product (sum of element-wise products)
*
* @example
* ```typescript
* const a = [1, 2, 3];
* const b = [4, 5, 6];
* const result = dot_product(a, b); // 1*4 + 2*5 + 3*6 = 32
* ```
*/
function dot_product(a, b) {
return a.reduce((sum, val, idx) => sum + val * b[idx], 0);
}
/**
* Computes the Euclidean norm (magnitude) of a vector.
*
* The norm represents the length of the vector in Euclidean space.
* It's used in cosine distance calculations and vector normalization.
*
* @param a The vector to compute the norm for
* @returns The Euclidean norm (square root of sum of squared elements)
*
* @example
* ```typescript
* const vector = [3, 4];
* const magnitude = norm(vector); // sqrt(3² + 4²) = 5
* ```
*/
function norm(a) {
return Math.sqrt(a.reduce((sum, val) => sum + val * val, 0));
}
/**
* Computes the cosine distance between two vectors.
*
* Cosine distance is defined as 1 - cosine_similarity, where cosine similarity
* is the dot product of normalized vectors. This distance measure is particularly
* useful for high-dimensional data as it focuses on the angle between vectors
* rather than their magnitudes.
*
* Range: [0, 2] where:
* - 0 = vectors point in same direction (most similar)
* - 1 = vectors are orthogonal (no correlation)
* - 2 = vectors point in opposite directions (most dissimilar)
*
* @param a First vector
* @param b Second vector
* @returns The cosine distance between the vectors
*
* @example
* ```typescript
* const doc1 = [1, 2, 0]; // Document embedding
* const doc2 = [2, 4, 0]; // Very similar document (same direction)
* const doc3 = [-1, -2, 0]; // Opposite document
*
* cosine_distance(doc1, doc2); // ≈ 0 (very similar)
* cosine_distance(doc1, doc3); // ≈ 2 (very different)
* ```
*/
function cosine_distance(a, b) {
return 1 - dot_product(a, b) / (norm(a) * norm(b));
}
//#endregion
//#region src/hnsw.ts
/**
* Hierarchical Navigable Small Worlds (HNSW) Implementation
*
* HNSW is a graph-based algorithm for approximate nearest neighbor search in high-dimensional spaces.
* It builds a multi-layer graph structure where:
* - Layer 0 contains all points and forms the base layer
* - Higher layers contain progressively fewer points (sampled probabilistically)
* - Each layer maintains connections between nearby points
*
* Key Concepts:
* - **Multi-layer structure**: Higher layers enable long-range navigation, lower layers provide precision
* - **Entry point**: A single node in the top layer that serves as the starting point for all searches
* - **Greedy search**: Navigate by always moving to the closest unvisited neighbor
* - **Layer selection**: New points are assigned to layers using an exponential decay probability
*
* Algorithm Benefits:
* - Logarithmic search complexity: O(log N) for both search and insertion
* - High recall: Can find very good approximate nearest neighbors
* - Scalable: Works well with millions of high-dimensional vectors
*
* This implementation works over a key-value database interface, allowing different storage backends.
*
* @template M Type of the metadata attached to each point (e.g., document IDs, labels, etc.)
*
* @see https://arxiv.org/pdf/1603.09320.pdf Original HNSW paper by Malkov & Yashunin
*/
var HNSW = class {
/** Database interface for storing points, graph connections, and metadata */
db;
/**
* Maximum number of bi-directional links for each node during construction.
* This parameter controls the connectivity of the graph:
* - Higher values = better search quality but slower construction and more memory
* - Lower values = faster construction but potentially worse search quality
* - Paper suggests m ∈ [5, 48], with 16 being a good default
* - Weaviate uses 64 for high-dimensional data
*/
m;
/**
* Maximum number of connections for layer 0 (base layer).
* Set to 2 * m as recommended in the paper.
* Layer 0 can have more connections since it contains all points.
*/
m_max0;
/**
* Normalization factor for level generation probability.
* Used in the exponential decay formula: level = floor(-ln(uniform(0,1)) * ml)
* Set to 1/ln(m) as per the paper's heuristic.
*/
ml;
/**
* Size of the dynamic candidate list during construction.
* Controls the search scope when finding neighbors for new points:
* - Higher values = better graph quality but slower construction
* - Lower values = faster construction but potentially worse search quality
* - Common values: 40 (fast), 100 (balanced), 400 (high quality)
*/
ef_construction;
/**
* Size of the dynamic candidate list during search.
* Controls the search scope when performing kNN queries:
* - Higher values = better recall but slower search
* - Lower values = faster search but potentially lower recall
* - Should be >= k (number of neighbors to return)
* - Can be adjusted at search time for different speed/quality tradeoffs
*/
ef;
/**
* Constructs a new HNSW index with the specified parameters.
*
* @param db Database interface for persistence (must implement DBInterface)
* @param M Maximum number of connections per node (recommended: 16, range: [5-48])
* @param ef_construction Size of candidate list during construction (recommended: 200)
* @param ef_search Size of candidate list during search (recommended: 50, must be >= k)
*
* @example
* ```typescript
* const hnsw = new HNSW(
* database, // Your database implementation
* 16, // M: good balance of speed/quality
* 200, // ef_construction: high quality graph
* 50 // ef_search: good search performance
* );
* ```
*/
constructor(db, M, ef_construction, ef_search) {
this.db = db;
this.m = M;
this.m_max0 = M * 2;
this.ml = 1 / Math.log(M);
this.ef_construction = ef_construction;
this.ef = ef_search;
}
/**
* Retrieves a vector and its associated metadata by index.
*
* This is a convenience method that fetches both the vector data and any
* metadata stored with it in a single operation.
*
* @param idx The index of the vector to retrieve
* @returns Object containing the vector data and metadata (null if no metadata exists)
*
* @example
* ```typescript
* const result = await hnsw.get_vector(42);
* console.log('Vector:', result.point); // [0.1, 0.2, 0.3, ...]
* console.log('Metadata:', result.metadata); // { filename: 'doc.pdf', category: 'research' }
* ```
*/
async get_vector(idx) {
const point = await this.db.get_point(idx);
const metadata = await this.db.get_metadata(idx);
return {
point,
metadata
};
}
/**
* Selects which layer a new point should be inserted into.
*
* Uses an exponential decay probability distribution as recommended in the paper.
* Most points will be inserted into layer 0, with progressively fewer points
* in higher layers. This creates the hierarchical structure that makes HNSW efficient.
*
* The probability of a point being in layer L or higher is: (1/2)^L
* This means:
* - ~50% of points are only in layer 0
* - ~25% of points reach layer 1 or higher
* - ~12.5% of points reach layer 2 or higher
* - etc.
*
* @returns The layer number (0-based) where the new point should be inserted
*
* @example
* ```typescript
* const layer = hnsw.select_layer(); // Returns 0, 1, 2, 3, ... with decreasing probability
* ```
*/
select_layer() {
return Math.floor(-Math.log(Math.random()) * this.ml);
}
/**
* Inserts a new point into the HNSW index.
*
* This is the core method that implements Algorithm 1 from the HNSW paper.
* The insertion process works in several phases:
*
* 1. **Layer Selection**: Randomly determine which layer the new point belongs to
* 2. **Entry Point Search**: Navigate from top layer down to find the best entry point
* 3. **Layer-by-layer Insertion**: Insert the point into each layer from selected layer down to 0
* 4. **Neighbor Selection**: For each layer, find the best neighbors and create bidirectional links
* 5. **Pruning**: Ensure no node has too many connections by removing the worst ones
*
* Time Complexity: O(log N) expected, where N is the number of points
* Space Complexity: O(M * N) where M is the average number of connections per point
*
* @param q The vector to insert (array of numbers representing the point in space)
* @param metadata Optional metadata to associate with this point (e.g., document ID, labels)
*
* @example
* ```typescript
* // Insert a simple vector
* await hnsw.insert([0.1, 0.2, 0.3, 0.4]);
*
* // Insert a vector with metadata
* await hnsw.insert(
* [0.1, 0.2, 0.3, 0.4],
* { filename: 'document.pdf', category: 'research' }
* );
* ```
*
* @see https://arxiv.org/pdf/1603.09320.pdf Algorithm 1 (page 7)
*/
async insert(q, metadata) {
const ep_index = await this.db.get_ep();
const L = await this.db.get_num_layers() - 1;
const l = this.select_layer();
const idx = await this.db.new_point(q);
if (metadata) await this.db.set_metadata(idx, metadata);
if (ep_index !== null) {
const dist = cosine_distance(q, await this.db.get_point(ep_index));
let ep = [[dist, ep_index]];
for (let i = L; i > l; i--) {
const ep_copy = ep.map((e) => [e[0], e[1]]);
const W = await this.search_layer(q, ep_copy, 1, i);
if (W.length > 0 && ep[0][0] > W[0][0]) ep = W;
}
for (let l_c = Math.min(L, l); l_c >= 0; l_c--) {
const W = await this.search_layer(q, ep, this.ef_construction, l_c);
const newNode = {};
ep = W.map((e) => [e[0], e[1]]);
const neighbors = this.select_neighbors(q, W, l_c);
const indices = neighbors.map(([, idx$1]) => idx$1);
const nodes = await this.db.get_neighbors(l_c, indices);
const M = l_c === 0 ? this.m_max0 : this.m;
for (const e of neighbors) {
newNode[e[1]] = e[0];
nodes[e[1]][idx] = e[0];
}
for (const e of neighbors) {
const eConn = Object.entries(nodes[e[1]]).map(([k, v]) => [v, Number.parseInt(k)]);
if (eConn.length > M) {
const eNewConn = this.select_neighbors(await this.db.get_point(e[1]), eConn, l_c);
const dict = {};
for (const eNew of eNewConn) dict[eNew[1]] = eNew[0];
nodes[e[