@delali/narsil 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +257 -0
- package/dist/adapters/filesystem.mjs +1 -1
- package/dist/adapters/indexeddb.mjs +1 -1
- package/dist/{chunk-JKIBUPAY.mjs → chunk-7GKPWGCN.mjs} +2 -1
- package/dist/chunk-GVN2W4VA.mjs +570 -0
- package/dist/chunk-HRABJLPB.mjs +64 -0
- package/dist/{chunk-5HXMVMSM.mjs → chunk-LIRVYUQ5.mjs} +1174 -181
- package/dist/direct-executor-DFRFDLFP.mjs +4 -0
- package/dist/index.d.ts +46 -3
- package/dist/index.mjs +1367 -415
- package/dist/vector/hnsw-build-worker.d.ts +37 -0
- package/dist/vector/hnsw-build-worker.mjs +81 -0
- package/dist/workers/entry.mjs +3 -32
- package/package.json +17 -11
- package/dist/chunk-N37VMC62.mjs +0 -18
- package/dist/direct-executor-MQD7JRD4.mjs +0 -3
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
type VectorMetric = 'cosine' | 'dotProduct' | 'euclidean';
|
|
2
|
+
|
|
3
|
+
interface HNSWConfig {
|
|
4
|
+
m?: number;
|
|
5
|
+
efConstruction?: number;
|
|
6
|
+
metric?: VectorMetric;
|
|
7
|
+
}
|
|
8
|
+
interface SerializedHNSWGraph {
|
|
9
|
+
entryPoint: string;
|
|
10
|
+
maxLayer: number;
|
|
11
|
+
m: number;
|
|
12
|
+
efConstruction: number;
|
|
13
|
+
metric?: VectorMetric;
|
|
14
|
+
nodes: Array<[string, number, Array<[number, string[]]>]>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface HNSWBuildRequest {
|
|
18
|
+
type: 'build';
|
|
19
|
+
vectors: Array<{
|
|
20
|
+
docId: string;
|
|
21
|
+
values: number[];
|
|
22
|
+
}>;
|
|
23
|
+
dimension: number;
|
|
24
|
+
config: HNSWConfig;
|
|
25
|
+
}
|
|
26
|
+
interface HNSWBuildResponse {
|
|
27
|
+
type: 'success';
|
|
28
|
+
graph: SerializedHNSWGraph;
|
|
29
|
+
}
|
|
30
|
+
interface HNSWBuildError {
|
|
31
|
+
type: 'error';
|
|
32
|
+
message: string;
|
|
33
|
+
}
|
|
34
|
+
type HNSWWorkerMessage = HNSWBuildResponse | HNSWBuildError;
|
|
35
|
+
declare function handleBuildRequest(request: HNSWBuildRequest): SerializedHNSWGraph;
|
|
36
|
+
|
|
37
|
+
export { type HNSWBuildError, type HNSWBuildRequest, type HNSWBuildResponse, type HNSWWorkerMessage, handleBuildRequest };
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { createHNSWIndex } from '../chunk-GVN2W4VA.mjs';
|
|
2
|
+
|
|
3
|
+
// src/vector/hnsw-build-worker.ts
|
|
4
|
+
var MAX_WORKER_DIMENSION = 8192;
|
|
5
|
+
var MAX_WORKER_VECTORS = 1e6;
|
|
6
|
+
function validateBuildRequest(request) {
|
|
7
|
+
if (typeof request.dimension !== "number" || request.dimension <= 0 || request.dimension > MAX_WORKER_DIMENSION) {
|
|
8
|
+
throw new Error(`Invalid dimension: ${request.dimension} (must be 1..${MAX_WORKER_DIMENSION})`);
|
|
9
|
+
}
|
|
10
|
+
if (!Array.isArray(request.vectors)) {
|
|
11
|
+
throw new Error("vectors must be an array");
|
|
12
|
+
}
|
|
13
|
+
if (request.vectors.length > MAX_WORKER_VECTORS) {
|
|
14
|
+
throw new Error(`Too many vectors: ${request.vectors.length} (max ${MAX_WORKER_VECTORS})`);
|
|
15
|
+
}
|
|
16
|
+
for (let i = 0; i < request.vectors.length; i++) {
|
|
17
|
+
const entry = request.vectors[i];
|
|
18
|
+
if (typeof entry.docId !== "string" || !Array.isArray(entry.values)) {
|
|
19
|
+
throw new Error(`Invalid vector entry at index ${i}`);
|
|
20
|
+
}
|
|
21
|
+
if (entry.values.length !== request.dimension) {
|
|
22
|
+
throw new Error(`Vector at index ${i} has ${entry.values.length} elements, expected ${request.dimension}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function handleBuildRequest(request) {
|
|
27
|
+
validateBuildRequest(request);
|
|
28
|
+
const hnsw = createHNSWIndex(request.dimension, request.config);
|
|
29
|
+
for (const entry of request.vectors) {
|
|
30
|
+
const vec = new Float32Array(entry.values);
|
|
31
|
+
hnsw.insert(entry.docId, vec);
|
|
32
|
+
}
|
|
33
|
+
return hnsw.serialize();
|
|
34
|
+
}
|
|
35
|
+
function setupWorker() {
|
|
36
|
+
setupAsync().catch((err) => {
|
|
37
|
+
console.error("HNSW build worker setup failed:", err);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
async function setupAsync() {
|
|
41
|
+
let parentPort = null;
|
|
42
|
+
try {
|
|
43
|
+
const workerThreads = await import('worker_threads');
|
|
44
|
+
parentPort = workerThreads.parentPort ?? null;
|
|
45
|
+
} catch {
|
|
46
|
+
parentPort = null;
|
|
47
|
+
}
|
|
48
|
+
if (parentPort) {
|
|
49
|
+
const port = parentPort;
|
|
50
|
+
port.on("message", (raw) => {
|
|
51
|
+
const request = raw;
|
|
52
|
+
if (request.type !== "build") return;
|
|
53
|
+
try {
|
|
54
|
+
const graph = handleBuildRequest(request);
|
|
55
|
+
port.postMessage({ type: "success", graph });
|
|
56
|
+
} catch (err) {
|
|
57
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
58
|
+
port.postMessage({ type: "error", message });
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const globalSelf = typeof self !== "undefined" ? self : void 0;
|
|
64
|
+
if (globalSelf && typeof globalSelf.postMessage === "function") {
|
|
65
|
+
const webSelf = globalSelf;
|
|
66
|
+
webSelf.onmessage = (event) => {
|
|
67
|
+
const request = event.data;
|
|
68
|
+
if (request.type !== "build") return;
|
|
69
|
+
try {
|
|
70
|
+
const graph = handleBuildRequest(request);
|
|
71
|
+
webSelf.postMessage({ type: "success", graph });
|
|
72
|
+
} catch (err) {
|
|
73
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
74
|
+
webSelf.postMessage({ type: "error", message });
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
setupWorker();
|
|
80
|
+
|
|
81
|
+
export { handleBuildRequest };
|
package/dist/workers/entry.mjs
CHANGED
|
@@ -1,34 +1,4 @@
|
|
|
1
|
-
import '../chunk-
|
|
2
|
-
|
|
3
|
-
// src/workers/protocol.ts
|
|
4
|
-
var KNOWN_ACTION_TYPES = [
|
|
5
|
-
"insert",
|
|
6
|
-
"remove",
|
|
7
|
-
"update",
|
|
8
|
-
"query",
|
|
9
|
-
"preflight",
|
|
10
|
-
"get",
|
|
11
|
-
"has",
|
|
12
|
-
"count",
|
|
13
|
-
"createIndex",
|
|
14
|
-
"dropIndex",
|
|
15
|
-
"getStats",
|
|
16
|
-
"clear",
|
|
17
|
-
"serialize",
|
|
18
|
-
"deserialize",
|
|
19
|
-
"memoryReport",
|
|
20
|
-
"shutdown"
|
|
21
|
-
];
|
|
22
|
-
function isValidWorkerAction(msg) {
|
|
23
|
-
if (msg === null || msg === void 0 || typeof msg !== "object") {
|
|
24
|
-
return false;
|
|
25
|
-
}
|
|
26
|
-
const candidate = msg;
|
|
27
|
-
if (typeof candidate.type !== "string" || typeof candidate.requestId !== "string") {
|
|
28
|
-
return false;
|
|
29
|
-
}
|
|
30
|
-
return KNOWN_ACTION_TYPES.includes(candidate.type);
|
|
31
|
-
}
|
|
1
|
+
import { isValidWorkerAction } from '../chunk-HRABJLPB.mjs';
|
|
32
2
|
|
|
33
3
|
// src/workers/worker-entry.ts
|
|
34
4
|
function startWorker() {
|
|
@@ -36,6 +6,7 @@ function startWorker() {
|
|
|
36
6
|
console.error("Narsil worker setup failed:", err);
|
|
37
7
|
});
|
|
38
8
|
}
|
|
9
|
+
startWorker();
|
|
39
10
|
async function setup() {
|
|
40
11
|
let parentPort = null;
|
|
41
12
|
try {
|
|
@@ -44,7 +15,7 @@ async function setup() {
|
|
|
44
15
|
} catch {
|
|
45
16
|
parentPort = null;
|
|
46
17
|
}
|
|
47
|
-
const { createDirectExecutor } = await import('../direct-executor-
|
|
18
|
+
const { createDirectExecutor } = await import('../direct-executor-DFRFDLFP.mjs');
|
|
48
19
|
const executor = createDirectExecutor();
|
|
49
20
|
function buildErrorResponse(requestId, code, message) {
|
|
50
21
|
return { type: "error", requestId, code, message };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@delali/narsil",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A distributed full-text search engine with cross-language portable binary format.",
|
|
6
6
|
"author": "Delali (https://sondelali.com)",
|
|
@@ -27,14 +27,6 @@
|
|
|
27
27
|
"engines": {
|
|
28
28
|
"node": ">=22"
|
|
29
29
|
},
|
|
30
|
-
"scripts": {
|
|
31
|
-
"build": "tsup",
|
|
32
|
-
"test": "vitest run",
|
|
33
|
-
"test:watch": "vitest",
|
|
34
|
-
"test:coverage": "vitest run --coverage",
|
|
35
|
-
"lint": "biome check src/",
|
|
36
|
-
"typecheck": "tsc --noEmit"
|
|
37
|
-
},
|
|
38
30
|
"exports": {
|
|
39
31
|
".": {
|
|
40
32
|
"import": "./dist/index.mjs",
|
|
@@ -64,6 +56,10 @@
|
|
|
64
56
|
"import": "./dist/invalidation/broadcast-channel.mjs",
|
|
65
57
|
"types": "./dist/invalidation/broadcast-channel.d.ts"
|
|
66
58
|
},
|
|
59
|
+
"./vector/hnsw-build-worker": {
|
|
60
|
+
"import": "./dist/vector/hnsw-build-worker.mjs",
|
|
61
|
+
"types": "./dist/vector/hnsw-build-worker.d.ts"
|
|
62
|
+
},
|
|
67
63
|
"./languages/*": {
|
|
68
64
|
"import": "./dist/languages/*.mjs",
|
|
69
65
|
"types": "./dist/languages/*.d.ts"
|
|
@@ -82,6 +78,16 @@
|
|
|
82
78
|
"@vitest/coverage-v8": "4.0.18",
|
|
83
79
|
"tsup": "8.5.1",
|
|
84
80
|
"typescript": "5.9.3",
|
|
85
|
-
"vitest": "4.0.18"
|
|
81
|
+
"vitest": "4.0.18",
|
|
82
|
+
"wabt": "1.0.39"
|
|
83
|
+
},
|
|
84
|
+
"scripts": {
|
|
85
|
+
"build": "tsup",
|
|
86
|
+
"test": "vitest run",
|
|
87
|
+
"test:watch": "vitest",
|
|
88
|
+
"test:coverage": "vitest run --coverage",
|
|
89
|
+
"bench": "vitest bench",
|
|
90
|
+
"lint": "biome check src/",
|
|
91
|
+
"typecheck": "tsc --noEmit"
|
|
86
92
|
}
|
|
87
|
-
}
|
|
93
|
+
}
|
package/dist/chunk-N37VMC62.mjs
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
// src/core/id-generator.ts
|
|
2
|
-
function generateId() {
|
|
3
|
-
const bytes = new Uint8Array(16);
|
|
4
|
-
crypto.getRandomValues(bytes);
|
|
5
|
-
const now = Date.now();
|
|
6
|
-
bytes[0] = Math.floor(now / 2 ** 40);
|
|
7
|
-
bytes[1] = Math.floor(now / 2 ** 32) % 256;
|
|
8
|
-
bytes[2] = Math.floor(now / 2 ** 24) % 256;
|
|
9
|
-
bytes[3] = Math.floor(now / 2 ** 16) % 256;
|
|
10
|
-
bytes[4] = Math.floor(now / 2 ** 8) % 256;
|
|
11
|
-
bytes[5] = now % 256;
|
|
12
|
-
bytes[6] = bytes[6] & 15 | 112;
|
|
13
|
-
bytes[8] = bytes[8] & 63 | 128;
|
|
14
|
-
const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
15
|
-
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export { generateId };
|