@delali/narsil 0.2.4-next.49 → 0.2.4-next.50

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.
Files changed (43) hide show
  1. package/README.md +1 -1
  2. package/dist/{options-Dtk99qUs.d.ts → capabilities-JrVQOwi4.d.ts} +55 -47
  3. package/dist/chunk-AV4IJKD2.mjs +35 -0
  4. package/dist/{chunk-M6JGMKA5.mjs → chunk-BEIK3EOZ.mjs} +503 -2273
  5. package/dist/chunk-CMVG2UEF.mjs +2038 -0
  6. package/dist/chunk-CV5H7XL7.mjs +876 -0
  7. package/dist/{chunk-KGRYQVZ5.mjs → chunk-MHE7YWAU.mjs} +2 -1
  8. package/dist/{chunk-56PJBIJ6.mjs → chunk-ND34I3SV.mjs} +427 -1455
  9. package/dist/chunk-PIWKZPP7.mjs +64 -0
  10. package/dist/{chunk-HGJC5L65.mjs → chunk-PMBHH44R.mjs} +121 -2
  11. package/dist/chunk-RP23PWMV.mjs +11 -0
  12. package/dist/{chunk-RDGXYRF4.mjs → chunk-WYJXSLER.mjs} +8 -19
  13. package/dist/{chunk-JXHUHFJ5.mjs → chunk-X6YZO4MQ.mjs} +2 -11
  14. package/dist/{chunk-OWCFK42Q.mjs → chunk-XE6XR2OE.mjs} +3 -10
  15. package/dist/chunk-ZHUQKZBE.mjs +1292 -0
  16. package/dist/{client-DX7xPcNA.d.ts → client-CBOVHdjp.d.ts} +31 -7
  17. package/dist/client.d.ts +7 -7
  18. package/dist/client.mjs +26 -10
  19. package/dist/{config-B41W23Us.d.ts → config-Czh2YNDi.d.ts} +2 -2
  20. package/dist/{config-Co-zAXqO.d.ts → config-OON7IJ_B.d.ts} +26 -5
  21. package/dist/direct-executor-OREXU72C.mjs +12 -0
  22. package/dist/distribution/coordinator/etcd.d.ts +2 -2
  23. package/dist/distribution/coordinator/in-memory.d.ts +2 -2
  24. package/dist/distribution.d.ts +8 -8
  25. package/dist/distribution.mjs +28 -11
  26. package/dist/{engine-CABZg25p.d.ts → engine-9PtFY50q.d.ts} +114 -62
  27. package/dist/index.browser.mjs +3255 -2516
  28. package/dist/index.d.ts +7 -7
  29. package/dist/index.mjs +11 -6
  30. package/dist/{search-FjWhZN3f.d.ts → memory-Bzd5xcEW.d.ts} +88 -79
  31. package/dist/persistence/durability/checkpoint-worker.d.ts +1 -1
  32. package/dist/persistence/durability/checkpoint-worker.mjs +11 -6
  33. package/dist/react.d.ts +4 -4
  34. package/dist/{schema-B7juWg8Q.d.ts → schema-BqRx9t4E.d.ts} +22 -3
  35. package/dist/server.d.ts +47 -42
  36. package/dist/server.mjs +321 -817
  37. package/dist/thread-PZLEO6JL.mjs +310 -0
  38. package/dist/{types-DsSSjY4d.d.ts → types-DXPxaXPO.d.ts} +1 -1
  39. package/dist/vector/hnsw-build-worker.mjs +2 -1
  40. package/dist/vector/search-worker.mjs +3 -121
  41. package/dist/workers/entry.mjs +47 -3
  42. package/package.json +5 -1
  43. package/dist/direct-executor-HRTDWMVB.mjs +0 -9
@@ -0,0 +1,310 @@
1
+ import { loadUWebSockets, childAppOf, createRouteRunner, corsWriter, createSharedRequestGate, registerServerRoutes, sendRelayed, createDocumentReadHandlers, respondJson, SERVER_CAPABILITIES, sendError, serverRoutes, parseJson, validateSuggest, rejectInvalid, respondError, validateQuery } from './chunk-CV5H7XL7.mjs';
2
+ import { executeListDocuments, executeSuggest, executePreflight, queryBindingOf, executeQuery } from './chunk-ZHUQKZBE.mjs';
3
+ import './chunk-XE6XR2OE.mjs';
4
+ import { extractVectorFieldsFromSchema } from './chunk-CMVG2UEF.mjs';
5
+ import './chunk-CBLS46IE.mjs';
6
+ import './chunk-RP23PWMV.mjs';
7
+ import './chunk-2GWVQWGA.mjs';
8
+ import { ServerErrorCodes, NarsilError, ErrorCodes } from './chunk-P4ELXVJQ.mjs';
9
+
10
+ // src/server/request-threads/read-engine.ts
11
+ function createThreadReadEngine(options) {
12
+ const { executor, relay, searchHooks } = options;
13
+ function requireContext(indexName) {
14
+ const context = executor.queryContextOf(indexName);
15
+ if (context === void 0) {
16
+ throw new NarsilError(ErrorCodes.INDEX_NOT_FOUND, `This thread holds no copy of index "${indexName}"`, {
17
+ indexName
18
+ });
19
+ }
20
+ relay.touch(indexName);
21
+ return context;
22
+ }
23
+ async function resolvedParams(indexName, params) {
24
+ if (params.vector?.text === void 0) return params;
25
+ const value = await relay.embed(indexName, params.vector);
26
+ const { text: _text, ...vector } = params.vector;
27
+ return { ...params, vector: { ...vector, value } };
28
+ }
29
+ function canAnswer(indexName, params) {
30
+ if (searchHooks) return false;
31
+ const context = executor.queryContextOf(indexName);
32
+ if (context === void 0) return false;
33
+ const field = params.vector?.field;
34
+ if (field === void 0) return true;
35
+ const vectorFields = extractVectorFieldsFromSchema(context.config.schema);
36
+ if (!vectorFields.has(field)) return true;
37
+ return context.vectorSearchers.has(field);
38
+ }
39
+ return {
40
+ holdsCopyOf: (indexName) => executor.queryContextOf(indexName) !== void 0,
41
+ canAnswer,
42
+ async query(indexName, params) {
43
+ const context = requireContext(indexName);
44
+ const result = await executeQuery(await resolvedParams(indexName, params), {
45
+ manager: context.manager,
46
+ language: context.language,
47
+ config: context.config,
48
+ indexName,
49
+ cursorBinding: queryBindingOf(params),
50
+ vectorSearchers: context.vectorSearchers
51
+ });
52
+ if (context.analysisStale) result.analysisStale = true;
53
+ return result;
54
+ },
55
+ async preflight(indexName, params) {
56
+ const context = requireContext(indexName);
57
+ const result = await executePreflight(await resolvedParams(indexName, params), {
58
+ manager: context.manager,
59
+ language: context.language,
60
+ config: context.config,
61
+ indexName,
62
+ cursorBinding: queryBindingOf(params),
63
+ vectorSearchers: context.vectorSearchers
64
+ });
65
+ if (context.analysisStale) result.analysisStale = true;
66
+ return result;
67
+ },
68
+ async suggest(indexName, params) {
69
+ const context = requireContext(indexName);
70
+ const result = executeSuggest(context.manager, context.language, params);
71
+ if (context.analysisStale) result.analysisStale = true;
72
+ return result;
73
+ },
74
+ async get(indexName, docId) {
75
+ return requireContext(indexName).manager.get(docId);
76
+ },
77
+ async getMultiple(indexName, docIds) {
78
+ const { manager } = requireContext(indexName);
79
+ const found = /* @__PURE__ */ new Map();
80
+ for (const docId of docIds) {
81
+ const document = manager.get(docId);
82
+ if (document !== void 0) found.set(docId, document);
83
+ }
84
+ return found;
85
+ },
86
+ async has(indexName, docId) {
87
+ return requireContext(indexName).manager.has(docId);
88
+ },
89
+ async countDocuments(indexName) {
90
+ return requireContext(indexName).manager.countDocuments();
91
+ },
92
+ async listDocuments(indexName, params) {
93
+ const context = requireContext(indexName);
94
+ return executeListDocuments(params ?? {}, { manager: context.manager, schema: context.config.schema });
95
+ }
96
+ };
97
+ }
98
+
99
+ // src/server/request-threads/relay-client.ts
100
+ function isRelayReply(message) {
101
+ if (typeof message !== "object" || message === null) return false;
102
+ const { type, requestId } = message;
103
+ return typeof requestId === "number" && (type === "response" || type === "authorized" || type === "embedded");
104
+ }
105
+ function errorOf(reply) {
106
+ const error = reply.error ?? { code: ErrorCodes.EMBEDDING_FAILED, message: "The main thread returned no vector" };
107
+ return new NarsilError(error.code, error.message, error.details);
108
+ }
109
+ function portClosedError() {
110
+ return new NarsilError(ErrorCodes.WORKER_CRASHED, "The port to the main thread closed before it answered");
111
+ }
112
+ var SHUTTING_DOWN_DENIAL = {
113
+ allowed: false,
114
+ denial: { status: 503, code: ServerErrorCodes.INTERNAL_ERROR, message: "The server is shutting down" }
115
+ };
116
+ function sendUnavailable(res) {
117
+ sendError(res, 503, ServerErrorCodes.INTERNAL_ERROR, "The server is shutting down");
118
+ }
119
+ function createRelayClient(port, touchIntervalMs) {
120
+ const pending = /* @__PURE__ */ new Map();
121
+ const touchedAt = /* @__PURE__ */ new Map();
122
+ let nextRequestId = 0;
123
+ let closed = false;
124
+ function failAll() {
125
+ closed = true;
126
+ const waiting = [...pending.values()];
127
+ pending.clear();
128
+ for (const entry of waiting) entry.reject(portClosedError());
129
+ }
130
+ port.on("message", (message) => {
131
+ if (!isRelayReply(message)) return;
132
+ const waiting = pending.get(message.requestId);
133
+ if (waiting === void 0) return;
134
+ pending.delete(message.requestId);
135
+ waiting.resolve(message);
136
+ });
137
+ port.on("close", failAll);
138
+ function send(build) {
139
+ if (closed) return Promise.reject(portClosedError());
140
+ nextRequestId += 1;
141
+ const requestId = nextRequestId;
142
+ return new Promise((resolve, reject) => {
143
+ pending.set(requestId, { resolve, reject });
144
+ port.postMessage(build(requestId));
145
+ });
146
+ }
147
+ return {
148
+ async authorize(context) {
149
+ let reply;
150
+ try {
151
+ reply = await send((requestId) => ({ type: "authorize", requestId, context }));
152
+ } catch {
153
+ return SHUTTING_DOWN_DENIAL;
154
+ }
155
+ if (reply.type !== "authorized") return { allowed: false, denial: null };
156
+ if (reply.hookError) return { allowed: false, denial: null };
157
+ if (reply.denial !== null) return { allowed: false, denial: reply.denial };
158
+ return { allowed: true };
159
+ },
160
+ async request(route, ctx) {
161
+ let reply;
162
+ try {
163
+ reply = await send((requestId) => ({
164
+ type: "request",
165
+ requestId,
166
+ route,
167
+ params: ctx.params,
168
+ query: ctx.query.toString(),
169
+ contentType: ctx.contentType,
170
+ rawBody: ctx.rawBody === null ? null : new Uint8Array(ctx.rawBody),
171
+ hookContext: ctx.hookContext
172
+ }));
173
+ } catch {
174
+ if (!ctx.abort.aborted) sendUnavailable(ctx.res);
175
+ return;
176
+ }
177
+ if (reply.type !== "response" || ctx.abort.aborted) return;
178
+ sendRelayed(ctx.res, ctx.abort, reply.status, reply.headers, reply.body);
179
+ },
180
+ async embed(indexName, vector) {
181
+ const reply = await send((requestId) => ({ type: "embed", requestId, indexName, vector }));
182
+ if (reply.type !== "embedded" || reply.value === null) {
183
+ throw reply.type === "embedded" ? errorOf(reply) : new NarsilError(ErrorCodes.EMBEDDING_FAILED, "The main thread returned no vector");
184
+ }
185
+ return reply.value;
186
+ },
187
+ touch(indexName) {
188
+ if (closed) return;
189
+ const now = Date.now();
190
+ const last = touchedAt.get(indexName);
191
+ if (last !== void 0 && now - last < touchIntervalMs) return;
192
+ touchedAt.set(indexName, now);
193
+ port.postMessage({ type: "touch", indexName });
194
+ },
195
+ close() {
196
+ failAll();
197
+ port.close();
198
+ }
199
+ };
200
+ }
201
+
202
+ // src/server/request-threads/thread.ts
203
+ var servingApp = null;
204
+ function relayed(relay, route) {
205
+ return (ctx) => relay.request(route, ctx);
206
+ }
207
+ function onHeldIndex(local, handler, fallback) {
208
+ return (ctx) => local.holdsCopyOf(ctx.params[0]) ? handler(ctx) : fallback(ctx);
209
+ }
210
+ function searchHandler(local, fallback, maxResultWindow, answer) {
211
+ return async (ctx) => {
212
+ const params = parseJson(ctx);
213
+ if (!params) return;
214
+ const failure = validateQuery(params, maxResultWindow);
215
+ if (failure) {
216
+ rejectInvalid(ctx, failure);
217
+ return;
218
+ }
219
+ if (!local.canAnswer(ctx.params[0], params)) {
220
+ await fallback(ctx);
221
+ return;
222
+ }
223
+ try {
224
+ respondJson(ctx, await answer(ctx.params[0], params));
225
+ } catch (err) {
226
+ respondError(ctx, err);
227
+ }
228
+ };
229
+ }
230
+ function suggestHandler(local, fallback) {
231
+ return async (ctx) => {
232
+ const params = parseJson(ctx);
233
+ if (!params) return;
234
+ const failure = validateSuggest(params);
235
+ if (failure) {
236
+ rejectInvalid(ctx, failure);
237
+ return;
238
+ }
239
+ if (!local.holdsCopyOf(ctx.params[0])) {
240
+ await fallback(ctx);
241
+ return;
242
+ }
243
+ try {
244
+ respondJson(ctx, await local.suggest(ctx.params[0], params));
245
+ } catch (err) {
246
+ respondError(ctx, err);
247
+ }
248
+ };
249
+ }
250
+ var THREAD_ONLY_ROUTES = /* @__PURE__ */ new Set(["livez", "version", "capabilities"]);
251
+ function isRelayedRoute(handler) {
252
+ return !THREAD_ONLY_ROUTES.has(handler);
253
+ }
254
+ function relayedHandlers(relay, limits) {
255
+ const forwarded = {};
256
+ for (const route of serverRoutes(limits)) {
257
+ if (isRelayedRoute(route.handler)) forwarded[route.handler] = relayed(relay, route.handler);
258
+ }
259
+ return forwarded;
260
+ }
261
+ function threadHandlers(local, relay, settings) {
262
+ const reads = createDocumentReadHandlers({ engine: local, limits: settings.limits });
263
+ const forwarded = relayedHandlers(relay, settings.limits);
264
+ const { maxResultWindow } = settings.limits;
265
+ return {
266
+ ...forwarded,
267
+ livez: (ctx) => respondJson(ctx, { status: "ok" }),
268
+ version: (ctx) => respondJson(ctx, { name: "narsil", ...settings.build }),
269
+ capabilities: (ctx) => respondJson(ctx, { capabilities: [...SERVER_CAPABILITIES] }),
270
+ search: searchHandler(local, forwarded.search, maxResultWindow, (name, params) => local.query(name, params)),
271
+ preflight: searchHandler(
272
+ local,
273
+ forwarded.preflight,
274
+ maxResultWindow,
275
+ (name, params) => local.preflight(name, params)
276
+ ),
277
+ suggest: suggestHandler(local, forwarded.suggest),
278
+ get: onHeldIndex(local, reads.get, forwarded.get),
279
+ exists: onHeldIndex(local, reads.exists, forwarded.exists),
280
+ count: onHeldIndex(local, reads.count, forwarded.count),
281
+ list: onHeldIndex(local, reads.list, forwarded.list),
282
+ multiGet: onHeldIndex(local, reads.multiGet, forwarded.multiGet)
283
+ };
284
+ }
285
+ async function serveRequests(executor, settings) {
286
+ if (servingApp !== null) throw new Error("This worker already serves requests");
287
+ const uws = await loadUWebSockets();
288
+ const app = uws.App();
289
+ const child = childAppOf(app);
290
+ if (child === null) throw new Error("This uWebSockets.js build offers no child apps");
291
+ const relay = createRelayClient(settings.port, settings.touchIntervalMs);
292
+ const local = createThreadReadEngine({ executor, relay, searchHooks: settings.searchHooks });
293
+ const run = createRouteRunner({
294
+ authorize: settings.authorizes ? (context) => relay.authorize(context) : void 0,
295
+ gate: createSharedRequestGate(settings.gate, settings.maxConcurrentRequests, settings.gateSlot),
296
+ writeCors: settings.cors ? corsWriter(settings.cors) : void 0
297
+ });
298
+ registerServerRoutes(app, run, threadHandlers(local, relay, settings), settings.limits, settings.cors);
299
+ servingApp = { app: child, relay };
300
+ return { descriptor: child.getDescriptor() };
301
+ }
302
+ function closeRequestThread() {
303
+ if (servingApp === null) return;
304
+ const { app, relay } = servingApp;
305
+ servingApp = null;
306
+ app.close();
307
+ relay.close();
308
+ }
309
+
310
+ export { closeRequestThread, serveRequests };
@@ -1,4 +1,4 @@
1
- import { S as SchemaDefinition } from './schema-B7juWg8Q.js';
1
+ import { S as SchemaDefinition } from './schema-BqRx9t4E.js';
2
2
 
3
3
  /**
4
4
  * What a node does in a cluster. A node may hold several roles at once.
@@ -1,4 +1,5 @@
1
- import { createVectorStore, createHNSWIndex, MAX_WORKER_DIMENSION, MAX_WORKER_VECTORS } from '../chunk-JXHUHFJ5.mjs';
1
+ import { createVectorStore, createHNSWIndex, MAX_WORKER_DIMENSION, MAX_WORKER_VECTORS } from '../chunk-X6YZO4MQ.mjs';
2
+ import '../chunk-RP23PWMV.mjs';
2
3
  import '../chunk-2GWVQWGA.mjs';
3
4
 
4
5
  // src/vector/hnsw-build-worker.ts
@@ -1,126 +1,8 @@
1
- import { createScalarQuantizer, deriveSq8Constants, arenaQuantizedDistance, quantizeVectorInto, quantizedMagnitude, scalarQuantizedDistance } from '../chunk-HGJC5L65.mjs';
2
- import { searchOrdinals, MAX_WORKER_COPIES, createVectorStore, createHNSWIndex, createSharedArenaSimd, importAdjacency, createHNSWWorkspace, arenaFloat32Distance, ABSENT_DOCUMENT_RANK } from '../chunk-JXHUHFJ5.mjs';
1
+ import { openSharedWorkerCopy, createScalarQuantizer } from '../chunk-PMBHH44R.mjs';
2
+ import { searchOrdinals, MAX_WORKER_COPIES, createVectorStore, createHNSWIndex } from '../chunk-X6YZO4MQ.mjs';
3
+ import '../chunk-RP23PWMV.mjs';
3
4
  import '../chunk-2GWVQWGA.mjs';
4
5
 
5
- // src/vector/shared-generation/worker-view.ts
6
- function createVectorReader(snapshot, simd, scratchSlot) {
7
- const { layout, magnitudes, rankByOrdinal } = snapshot;
8
- const { dimension, slots } = layout;
9
- const vectors = new Float32Array(snapshot.memory.buffer, layout.vectorsOffset, slots * dimension);
10
- const scratchByteOffset = scratchSlot * layout.float32ScratchStride;
11
- const scratch = simd ? new Float32Array(snapshot.memory.buffer, scratchByteOffset, dimension) : null;
12
- const holdsDocument = (ordinal) => ordinal >= 0 && ordinal < slots && rankByOrdinal[ordinal] !== ABSENT_DOCUMENT_RANK;
13
- return {
14
- entryForOrdinal(ordinal) {
15
- if (!holdsDocument(ordinal)) return void 0;
16
- const base = ordinal * dimension;
17
- return { vector: vectors.subarray(base, base + dimension), magnitude: magnitudes[ordinal] };
18
- },
19
- prepareQueryArena(query) {
20
- if (!simd || !scratch || query.length !== dimension) return null;
21
- scratch.set(query, 0);
22
- return { magnitude: simd.magnitude(scratchByteOffset, dimension) };
23
- },
24
- distanceFromArena(prepared, ordinal, metric) {
25
- if (!simd || !holdsDocument(ordinal)) return Number.POSITIVE_INFINITY;
26
- const byteOffset = layout.vectorsOffset + ordinal * dimension * 4;
27
- return arenaFloat32Distance(
28
- simd,
29
- scratchByteOffset,
30
- byteOffset,
31
- dimension,
32
- metric,
33
- prepared.magnitude,
34
- magnitudes[ordinal]
35
- );
36
- }
37
- };
38
- }
39
- function createQuantizerReader(snapshot, simd, scratchSlot) {
40
- if (snapshot.quantization !== "sq8" || snapshot.calibration === null) return void 0;
41
- const { layout, codeSums, codeMagnitudes, codePresent } = snapshot;
42
- const { dimension, slots } = layout;
43
- const constants = deriveSq8Constants(snapshot.calibration.alpha, snapshot.calibration.offset, dimension);
44
- const codes = new Uint8Array(snapshot.memory.buffer, layout.codesOffset, slots * dimension);
45
- const scratchByteOffset = layout.codeScratchOffset + scratchSlot * layout.codeScratchStride;
46
- const scratch = simd ? new Uint8Array(snapshot.memory.buffer, scratchByteOffset, dimension) : null;
47
- let liveCodes = 0;
48
- for (let ordinal = 0; ordinal < codePresent.length; ordinal++) {
49
- if (codePresent[ordinal] === 1) liveCodes++;
50
- }
51
- const holdsCodes = (ordinal) => ordinal >= 0 && ordinal < slots && codePresent[ordinal] === 1;
52
- return {
53
- get size() {
54
- return liveCodes;
55
- },
56
- isCalibrated: () => true,
57
- prepareQuery(query) {
58
- const quantized = new Uint8Array(dimension);
59
- const { sum, sumSq } = quantizeVectorInto(quantized, query, dimension, constants);
60
- return { quantized, sum, sumSq, magnitude: quantizedMagnitude(constants, sumSq, sum) };
61
- },
62
- distanceFromPreparedByOrdinal(prepared, ordinal, metric) {
63
- if (!holdsCodes(ordinal)) return Number.POSITIVE_INFINITY;
64
- return scalarQuantizedDistance(
65
- codes,
66
- codeSums,
67
- codeMagnitudes,
68
- dimension,
69
- constants,
70
- prepared.quantized,
71
- prepared.sum,
72
- prepared.magnitude,
73
- ordinal,
74
- metric
75
- );
76
- },
77
- prepareQueryArena(query) {
78
- if (!simd || !scratch) return null;
79
- const { sum, sumSq } = quantizeVectorInto(scratch, query, dimension, constants);
80
- return { sum, sumSq, magnitude: quantizedMagnitude(constants, sumSq, sum) };
81
- },
82
- distanceFromArena(prepared, ordinal, metric) {
83
- if (!simd || !holdsCodes(ordinal)) return Number.POSITIVE_INFINITY;
84
- return arenaQuantizedDistance(
85
- simd,
86
- scratchByteOffset,
87
- layout.codesOffset + ordinal * dimension,
88
- dimension,
89
- constants,
90
- prepared.sum,
91
- prepared.magnitude,
92
- codeSums[ordinal],
93
- codeMagnitudes[ordinal],
94
- metric
95
- );
96
- }
97
- };
98
- }
99
- function openSharedWorkerCopy(snapshot, scratchSlot) {
100
- if (scratchSlot < 0 || scratchSlot >= snapshot.layout.workerSlots) {
101
- throw new Error(`Scratch slot ${scratchSlot} is outside the ${snapshot.layout.workerSlots} reserved slots`);
102
- }
103
- const simd = createSharedArenaSimd(snapshot.memory);
104
- const adjacency = importAdjacency(snapshot.graph.adjacency);
105
- const capacity = Math.max(snapshot.graph.capacity, adjacency.capacity);
106
- const searchState = {
107
- dimension: snapshot.dimension,
108
- store: createVectorReader(snapshot, simd, scratchSlot),
109
- quantizer: createQuantizerReader(snapshot, simd, scratchSlot),
110
- adjacency,
111
- tombstones: snapshot.graph.tombstones,
112
- tombstoneCount: snapshot.graph.tombstoneCount,
113
- nodeCount: snapshot.graph.nodeCount,
114
- capacity,
115
- visited: new Uint32Array(capacity),
116
- visitStamp: 0,
117
- entryPointOrd: snapshot.graph.entryPointOrd,
118
- topLayer: snapshot.graph.topLayer,
119
- workspace: createHNSWWorkspace()
120
- };
121
- return { searchState, rankByOrdinal: snapshot.rankByOrdinal };
122
- }
123
-
124
6
  // src/vector/worker-copy.ts
125
7
  function restoreWorkerCopy(snapshot) {
126
8
  const store = createVectorStore();
@@ -1,4 +1,4 @@
1
- import { isValidWorkerAction } from '../chunk-RDGXYRF4.mjs';
1
+ import { isValidWorkerAction } from '../chunk-WYJXSLER.mjs';
2
2
  import { NarsilError, ErrorCodes } from '../chunk-P4ELXVJQ.mjs';
3
3
 
4
4
  // src/workers/action-handler.ts
@@ -16,9 +16,24 @@ async function importBootstrapModule(moduleUrl) {
16
16
  }
17
17
  await import(moduleUrl);
18
18
  }
19
+ async function stopServingRequests() {
20
+ const { closeRequestThread } = await import('../thread-PZLEO6JL.mjs');
21
+ closeRequestThread();
22
+ }
23
+ function holdsCopies(executor) {
24
+ return typeof executor.queryContextOf === "function";
25
+ }
19
26
  function createActionHandler(executor) {
27
+ let servingRequests = false;
28
+ const serving = { start: null };
29
+ async function stopServingOnceStarted() {
30
+ if (serving.start !== null) await serving.start;
31
+ if (servingRequests) await stopServingRequests();
32
+ servingRequests = false;
33
+ }
20
34
  return async function handleAction(action, post) {
21
35
  if (action.type === "shutdown") {
36
+ await stopServingOnceStarted();
22
37
  await executor.shutdown();
23
38
  post(buildSuccessResponse(action.requestId, void 0));
24
39
  return true;
@@ -29,6 +44,28 @@ function createActionHandler(executor) {
29
44
  post(buildSuccessResponse(action.requestId, void 0));
30
45
  return false;
31
46
  }
47
+ if (action.type === "serveRequests") {
48
+ if (!holdsCopies(executor)) {
49
+ throw new NarsilError(ErrorCodes.CONFIG_INVALID, "This executor holds no copies to answer requests from");
50
+ }
51
+ const starting = import('../thread-PZLEO6JL.mjs').then(
52
+ ({ serveRequests }) => serveRequests(executor, action.settings)
53
+ );
54
+ serving.start = starting.then(
55
+ () => {
56
+ servingRequests = true;
57
+ },
58
+ () => void 0
59
+ );
60
+ const result2 = await starting;
61
+ post(buildSuccessResponse(action.requestId, result2));
62
+ return false;
63
+ }
64
+ if (action.type === "stopServing") {
65
+ await stopServingOnceStarted();
66
+ post(buildSuccessResponse(action.requestId, void 0));
67
+ return false;
68
+ }
32
69
  const result = await executor.execute(action);
33
70
  post(buildSuccessResponse(action.requestId, result));
34
71
  } catch (err) {
@@ -47,16 +84,23 @@ function startWorker() {
47
84
  });
48
85
  }
49
86
  startWorker();
87
+ function workerIdOf(workerData) {
88
+ if (typeof workerData !== "object" || workerData === null) return void 0;
89
+ const workerId = workerData.workerId;
90
+ return typeof workerId === "number" ? workerId : void 0;
91
+ }
50
92
  async function setup() {
51
93
  let parentPort = null;
94
+ let scratchSlot;
52
95
  try {
53
96
  const workerThreads = await import('worker_threads');
54
97
  parentPort = workerThreads.parentPort ?? null;
98
+ scratchSlot = workerIdOf(workerThreads.workerData);
55
99
  } catch {
56
100
  parentPort = null;
57
101
  }
58
- const { createDirectExecutor } = await import('../direct-executor-HRTDWMVB.mjs');
59
- const handleAction = createActionHandler(createDirectExecutor());
102
+ const { createDirectExecutor } = await import('../direct-executor-OREXU72C.mjs');
103
+ const handleAction = createActionHandler(createDirectExecutor({ scratchSlot }));
60
104
  if (parentPort) {
61
105
  const port = parentPort;
62
106
  port.on("message", (raw) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@delali/narsil",
3
- "version": "0.2.4-next.49",
3
+ "version": "0.2.4-next.50",
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)",
@@ -30,6 +30,10 @@
30
30
  "#platform/node-worker": {
31
31
  "browser": "./src/workers/node-worker.browser.ts",
32
32
  "default": "./src/workers/node-worker.ts"
33
+ },
34
+ "#platform/heap-statistics": {
35
+ "browser": "./src/runtime/heap-statistics.browser.ts",
36
+ "default": "./src/runtime/heap-statistics.ts"
33
37
  }
34
38
  },
35
39
  "sideEffects": false,
@@ -1,9 +0,0 @@
1
- export { createDirectExecutor } from './chunk-M6JGMKA5.mjs';
2
- import './chunk-CBLS46IE.mjs';
3
- import './chunk-TGP5M5R7.mjs';
4
- import './chunk-HGJC5L65.mjs';
5
- import './chunk-JXHUHFJ5.mjs';
6
- import './chunk-U2BKYEC7.mjs';
7
- import './chunk-2GWVQWGA.mjs';
8
- import './chunk-RDGXYRF4.mjs';
9
- import './chunk-P4ELXVJQ.mjs';