@delali/narsil 0.2.2 → 0.2.3-next.33

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 (66) hide show
  1. package/README.md +12 -9
  2. package/dist/adapters/filesystem.d.ts +1 -1
  3. package/dist/adapters/filesystem.mjs +1 -1
  4. package/dist/adapters/indexeddb.d.ts +1 -1
  5. package/dist/adapters/indexeddb.mjs +1 -1
  6. package/dist/adapters/memory.d.ts +1 -1
  7. package/dist/{adapters-CVqI5NBt.d.ts → adapters-79OIa4xm.d.ts} +1 -1
  8. package/dist/brute-force-DbHYCTc3.d.ts +8 -0
  9. package/dist/chunk-BFIH7OV5.mjs +185 -0
  10. package/dist/chunk-C6VVLMRX.mjs +63 -0
  11. package/dist/{chunk-UNJZ7ZDB.mjs → chunk-EMI63EXP.mjs} +33 -2
  12. package/dist/chunk-MFFAJ2NR.mjs +135 -0
  13. package/dist/chunk-OBXS7OZM.mjs +376 -0
  14. package/dist/{chunk-ASI6AOW2.mjs → chunk-RO4EFWE6.mjs} +7833 -3524
  15. package/dist/{chunk-WLR5BFL3.mjs → chunk-TEAJO7CU.mjs} +3067 -2087
  16. package/dist/{chunk-S7RJJMHI.mjs → chunk-WDL632CN.mjs} +699 -222
  17. package/dist/{chunk-BKAI2WXI.mjs → chunk-WTONLDU3.mjs} +5 -0
  18. package/dist/client-DQHB_swQ.d.ts +793 -0
  19. package/dist/client.d.ts +6 -0
  20. package/dist/client.mjs +843 -0
  21. package/dist/{config-DXypXAfN.d.ts → config-ChZ7lfqc.d.ts} +3 -3
  22. package/dist/direct-executor-YARCDMJF.mjs +8 -0
  23. package/dist/distribution/coordinator/etcd.d.ts +3 -3
  24. package/dist/distribution/coordinator/etcd.mjs +471 -2
  25. package/dist/distribution/coordinator/in-memory.d.ts +3 -3
  26. package/dist/distribution/coordinator/in-memory.mjs +286 -1
  27. package/dist/distribution/transport/grpc.d.ts +45 -0
  28. package/dist/distribution/transport/grpc.mjs +586 -0
  29. package/dist/distribution/transport/in-memory.d.ts +118 -0
  30. package/dist/distribution/transport/in-memory.mjs +214 -0
  31. package/dist/distribution/transport/tcp.d.ts +27 -0
  32. package/dist/distribution/transport/tcp.mjs +595 -0
  33. package/dist/distribution.d.ts +241 -192
  34. package/dist/distribution.mjs +6620 -4795
  35. package/dist/embeddings/openai.d.ts +1 -1
  36. package/dist/embeddings/openai.mjs +1 -1
  37. package/dist/{engine-CO9Wnfie.d.ts → engine-DScGlT2Z.d.ts} +30 -3
  38. package/dist/index.browser.mjs +10035 -3914
  39. package/dist/index.d.ts +8 -8
  40. package/dist/index.mjs +8 -5
  41. package/dist/invalidation/broadcast-channel.d.ts +1 -1
  42. package/dist/invalidation/filesystem.d.ts +1 -1
  43. package/dist/invalidation/filesystem.mjs +3 -1
  44. package/dist/invalidation/noop.d.ts +1 -1
  45. package/dist/options-BORJg5XB.d.ts +234 -0
  46. package/dist/persistence/durability/checkpoint-worker.d.ts +4 -2
  47. package/dist/persistence/durability/checkpoint-worker.mjs +8 -5
  48. package/dist/react.d.ts +340 -0
  49. package/dist/react.mjs +687 -0
  50. package/dist/{schema-CE6S_aAr.d.ts → schema-emTQfEiX.d.ts} +21 -7
  51. package/dist/{search-DLBtiPdQ.d.ts → search-CQ-9bKxo.d.ts} +226 -14
  52. package/dist/server.d.ts +42 -356
  53. package/dist/server.mjs +493 -106
  54. package/dist/tasks-CnMLpfk-.d.ts +274 -0
  55. package/dist/types-BQDlIvfS.d.ts +34 -0
  56. package/dist/{types-BV3nAKTL.d.ts → types-Bfhhnh3G.d.ts} +23 -1
  57. package/dist/types-D5OwhtsQ.d.ts +97 -0
  58. package/dist/vector/hnsw-build-worker.d.ts +2 -7
  59. package/dist/vector/hnsw-build-worker.mjs +2 -1
  60. package/dist/vector/search-worker.d.ts +373 -0
  61. package/dist/vector/search-worker.mjs +279 -0
  62. package/dist/workers/entry.mjs +43 -25
  63. package/package.json +42 -2
  64. package/dist/chunk-FKK76OR7.mjs +0 -456
  65. package/dist/chunk-RQLXC2YS.mjs +0 -269
  66. package/dist/direct-executor-3W4QRA4E.mjs +0 -5
package/dist/react.mjs ADDED
@@ -0,0 +1,687 @@
1
+ import { createContext, useState, useEffect, createElement, useRef, useEffectEvent, useCallback, useMemo, useContext, useSyncExternalStore } from 'react';
2
+
3
+ // src/errors.ts
4
+ var ErrorCodes = {
5
+ SCHEMA_INVALID_TYPE: "SCHEMA_INVALID_TYPE",
6
+ SCHEMA_MISSING_FIELD: "SCHEMA_MISSING_FIELD",
7
+ SCHEMA_DEPTH_EXCEEDED: "SCHEMA_DEPTH_EXCEEDED",
8
+ SCHEMA_INVALID_VECTOR_DIMENSION: "SCHEMA_INVALID_VECTOR_DIMENSION",
9
+ SCHEMA_INVALID_GEOPOINT: "SCHEMA_INVALID_GEOPOINT",
10
+ DOC_NOT_FOUND: "DOC_NOT_FOUND",
11
+ DOC_ALREADY_EXISTS: "DOC_ALREADY_EXISTS",
12
+ DOC_VALIDATION_FAILED: "DOC_VALIDATION_FAILED",
13
+ INDEX_NOT_FOUND: "INDEX_NOT_FOUND",
14
+ INDEX_ALREADY_EXISTS: "INDEX_ALREADY_EXISTS",
15
+ INDEX_ORPHANED: "INDEX_ORPHANED",
16
+ PARTITION_CORRUPTED: "PARTITION_CORRUPTED",
17
+ PARTITION_REBALANCING_BACKPRESSURE: "PARTITION_REBALANCING_BACKPRESSURE",
18
+ WORKER_CRASHED: "WORKER_CRASHED",
19
+ WORKER_BUSY: "WORKER_BUSY",
20
+ WORKER_TIMEOUT: "WORKER_TIMEOUT",
21
+ PERSISTENCE_SAVE_FAILED: "PERSISTENCE_SAVE_FAILED",
22
+ PERSISTENCE_LOAD_FAILED: "PERSISTENCE_LOAD_FAILED",
23
+ PERSISTENCE_DELETE_FAILED: "PERSISTENCE_DELETE_FAILED",
24
+ PERSISTENCE_CRC_MISMATCH: "PERSISTENCE_CRC_MISMATCH",
25
+ PERSISTENCE_WAL_CORRUPT: "PERSISTENCE_WAL_CORRUPT",
26
+ PERSISTENCE_FSYNC_FAILED: "PERSISTENCE_FSYNC_FAILED",
27
+ SEARCH_INVALID_FIELD: "SEARCH_INVALID_FIELD",
28
+ SEARCH_INVALID_VECTOR_SIZE: "SEARCH_INVALID_VECTOR_SIZE",
29
+ VECTOR_DIMENSION_MISMATCH: "VECTOR_DIMENSION_MISMATCH",
30
+ SEARCH_INVALID_FILTER: "SEARCH_INVALID_FILTER",
31
+ SEARCH_INVALID_MODE: "SEARCH_INVALID_MODE",
32
+ SEARCH_INVALID_CURSOR: "SEARCH_INVALID_CURSOR",
33
+ SEARCH_RESULT_WINDOW_EXCEEDED: "SEARCH_RESULT_WINDOW_EXCEEDED",
34
+ LANGUAGE_NOT_SUPPORTED: "LANGUAGE_NOT_SUPPORTED",
35
+ ENVELOPE_VERSION_MISMATCH: "ENVELOPE_VERSION_MISMATCH",
36
+ ENVELOPE_INVALID_MAGIC: "ENVELOPE_INVALID_MAGIC",
37
+ PARTITION_CAPACITY_EXCEEDED: "PARTITION_CAPACITY_EXCEEDED",
38
+ EMBEDDING_FAILED: "EMBEDDING_FAILED",
39
+ EMBEDDING_DIMENSION_MISMATCH: "EMBEDDING_DIMENSION_MISMATCH",
40
+ EMBEDDING_NO_SOURCE: "EMBEDDING_NO_SOURCE",
41
+ EMBEDDING_CONFIG_INVALID: "EMBEDDING_CONFIG_INVALID",
42
+ DOC_MISSING_REQUIRED_FIELD: "DOC_MISSING_REQUIRED_FIELD",
43
+ CONFIG_INVALID: "CONFIG_INVALID",
44
+ QUERY_ROUTING_FAILED: "QUERY_ROUTING_FAILED",
45
+ QUERY_PARTIAL_FAILURE: "QUERY_PARTIAL_FAILURE",
46
+ QUERY_NODE_TIMEOUT: "QUERY_NODE_TIMEOUT",
47
+ QUERY_NO_ACTIVE_REPLICA: "QUERY_NO_ACTIVE_REPLICA",
48
+ CLUSTER_OPERATION_UNSUPPORTED: "CLUSTER_OPERATION_UNSUPPORTED",
49
+ ALLOCATION_NO_DATA_NODES: "ALLOCATION_NO_DATA_NODES",
50
+ ALLOCATION_INVALID_CONFIG: "ALLOCATION_INVALID_CONFIG",
51
+ ALLOCATION_FAILED: "ALLOCATION_FAILED",
52
+ CONTROLLER_LEASE_LOST: "CONTROLLER_LEASE_LOST",
53
+ CONTROLLER_NOT_ACTIVE: "CONTROLLER_NOT_ACTIVE",
54
+ CONTROLLER_METADATA_INVALID: "CONTROLLER_METADATA_INVALID",
55
+ NODE_BOOTSTRAP_FAILED: "NODE_BOOTSTRAP_FAILED",
56
+ NODE_ALREADY_JOINED: "NODE_ALREADY_JOINED",
57
+ NODE_NOT_JOINED: "NODE_NOT_JOINED",
58
+ COORDINATOR_DEPENDENCY_MISSING: "COORDINATOR_DEPENDENCY_MISSING",
59
+ TRANSPORT_DEPENDENCY_MISSING: "TRANSPORT_DEPENDENCY_MISSING",
60
+ REPLICATION_ENTRY_INVALID: "REPLICATION_ENTRY_INVALID",
61
+ REPLICATION_INSYNC_REMOVAL_FAILED: "REPLICATION_INSYNC_REMOVAL_FAILED",
62
+ REPLICATION_ROLLBACK_FAILED: "REPLICATION_ROLLBACK_FAILED",
63
+ REPLICATION_LOG_FULL: "REPLICATION_LOG_FULL",
64
+ REPLICATION_ENTRY_CORRUPT: "REPLICATION_ENTRY_CORRUPT",
65
+ REPLICATION_SNAPSHOT_CORRUPT: "REPLICATION_SNAPSHOT_CORRUPT",
66
+ REPLICATION_TERM_MISMATCH: "REPLICATION_TERM_MISMATCH",
67
+ REPLICATION_SYNC_FAILED: "REPLICATION_SYNC_FAILED",
68
+ PARTITION_NOT_PRIMARY: "PARTITION_NOT_PRIMARY",
69
+ PARTITION_UNASSIGNED: "PARTITION_UNASSIGNED",
70
+ INSUFFICIENT_REPLICAS: "INSUFFICIENT_REPLICAS",
71
+ SNAPSHOT_SYNC_UNAUTHORIZED: "SNAPSHOT_SYNC_UNAUTHORIZED",
72
+ SNAPSHOT_SYNC_REQUEST_INVALID: "SNAPSHOT_SYNC_REQUEST_INVALID",
73
+ SNAPSHOT_SYNC_INDEX_NOT_FOUND: "SNAPSHOT_SYNC_INDEX_NOT_FOUND",
74
+ SNAPSHOT_SYNC_TOO_LARGE: "SNAPSHOT_SYNC_TOO_LARGE",
75
+ SNAPSHOT_SYNC_CAPACITY_EXHAUSTED: "SNAPSHOT_SYNC_CAPACITY_EXHAUSTED",
76
+ SNAPSHOT_SYNC_SNAPSHOT_FAILED: "SNAPSHOT_SYNC_SNAPSHOT_FAILED",
77
+ SNAPSHOT_SYNC_DECODE_FAILED: "SNAPSHOT_SYNC_DECODE_FAILED",
78
+ SNAPSHOT_SYNC_FRAME_INVALID: "SNAPSHOT_SYNC_FRAME_INVALID",
79
+ SNAPSHOT_SYNC_HEADER_INVALID: "SNAPSHOT_SYNC_HEADER_INVALID",
80
+ SNAPSHOT_SYNC_HEADER_MISMATCH: "SNAPSHOT_SYNC_HEADER_MISMATCH",
81
+ SNAPSHOT_SYNC_CHUNK_OUT_OF_ORDER: "SNAPSHOT_SYNC_CHUNK_OUT_OF_ORDER",
82
+ SNAPSHOT_SYNC_CHUNK_OVERFLOW: "SNAPSHOT_SYNC_CHUNK_OVERFLOW",
83
+ SNAPSHOT_SYNC_CHUNK_SIZE_EXCEEDED: "SNAPSHOT_SYNC_CHUNK_SIZE_EXCEEDED",
84
+ SNAPSHOT_SYNC_CHUNK_MISSING: "SNAPSHOT_SYNC_CHUNK_MISSING",
85
+ SNAPSHOT_SYNC_END_MISSING: "SNAPSHOT_SYNC_END_MISSING",
86
+ SNAPSHOT_SYNC_CHECKSUM_MISMATCH: "SNAPSHOT_SYNC_CHECKSUM_MISMATCH",
87
+ SNAPSHOT_SYNC_PRIMARY_ERROR: "SNAPSHOT_SYNC_PRIMARY_ERROR",
88
+ SNAPSHOT_SYNC_NO_TARGETS: "SNAPSHOT_SYNC_NO_TARGETS",
89
+ SNAPSHOT_SYNC_TRANSPORT_FAILED: "SNAPSHOT_SYNC_TRANSPORT_FAILED",
90
+ SNAPSHOT_SYNC_SCHEMA_UNAVAILABLE: "SNAPSHOT_SYNC_SCHEMA_UNAVAILABLE",
91
+ SNAPSHOT_SYNC_RESTORE_FAILED: "SNAPSHOT_SYNC_RESTORE_FAILED",
92
+ SNAPSHOT_SYNC_RESTORE_CLEANUP_FAILED: "SNAPSHOT_SYNC_RESTORE_CLEANUP_FAILED",
93
+ SNAPSHOT_SYNC_TIMEOUT: "SNAPSHOT_SYNC_TIMEOUT",
94
+ SNAPSHOT_SYNC_ALLOCATION_UNAVAILABLE: "SNAPSHOT_SYNC_ALLOCATION_UNAVAILABLE",
95
+ SNAPSHOT_SYNC_NOT_ASSIGNED: "SNAPSHOT_SYNC_NOT_ASSIGNED",
96
+ SNAPSHOT_SYNC_ABORTED: "SNAPSHOT_SYNC_ABORTED"
97
+ };
98
+ var ServerErrorCodes = {
99
+ INVALID_REQUEST: "INVALID_REQUEST",
100
+ INVALID_JSON: "INVALID_JSON",
101
+ EMPTY_BODY: "EMPTY_BODY",
102
+ PAYLOAD_TOO_LARGE: "PAYLOAD_TOO_LARGE",
103
+ NOT_FOUND: "NOT_FOUND",
104
+ TASK_NOT_FOUND: "TASK_NOT_FOUND",
105
+ TASK_NOT_CANCELLABLE: "TASK_NOT_CANCELLABLE",
106
+ TASK_OWNED_BY_ANOTHER_INSTANCE: "TASK_OWNED_BY_ANOTHER_INSTANCE",
107
+ TOO_MANY_REQUESTS: "TOO_MANY_REQUESTS",
108
+ HOOK_ERROR: "HOOK_ERROR",
109
+ INTERNAL_ERROR: "INTERNAL_ERROR"
110
+ };
111
+ var ClientErrorCodes = {
112
+ CLIENT_CONNECTION_FAILED: "CLIENT_CONNECTION_FAILED",
113
+ CLIENT_REQUEST_TIMEOUT: "CLIENT_REQUEST_TIMEOUT",
114
+ CLIENT_REQUEST_ABORTED: "CLIENT_REQUEST_ABORTED",
115
+ CLIENT_INVALID_RESPONSE: "CLIENT_INVALID_RESPONSE",
116
+ CLIENT_TASK_TIMEOUT: "CLIENT_TASK_TIMEOUT",
117
+ CLIENT_UNEXPECTED_ERROR: "CLIENT_UNEXPECTED_ERROR"
118
+ };
119
+ var NarsilError = class extends Error {
120
+ /** This says which failure it is. */
121
+ code;
122
+ /** This carries the values behind the failure, such as the field, index, or limit involved. It is empty when the code says everything. */
123
+ details;
124
+ /**
125
+ * Builds an error carrying a code the caller can branch on.
126
+ *
127
+ * @param code - The failure this error reports, from {@link ErrorCodes} for
128
+ * an engine failure, {@link ServerErrorCodes} for one the HTTP layer raised,
129
+ * or {@link ClientErrorCodes} for one that stopped a request from reaching a
130
+ * server.
131
+ * @param message - Plain description, for a log or a person.
132
+ * @param details - Values behind the failure, which reach
133
+ * {@link NarsilError.details}.
134
+ */
135
+ constructor(code, message, details) {
136
+ super(message);
137
+ this.name = "NarsilError";
138
+ this.code = code;
139
+ this.details = details ?? {};
140
+ }
141
+ };
142
+
143
+ // src/react/failure.ts
144
+ function asNarsilError(err, whatFailed) {
145
+ if (err instanceof NarsilError) return err;
146
+ const message = err instanceof Error ? err.message : String(err);
147
+ return new NarsilError(ClientErrorCodes.CLIENT_UNEXPECTED_ERROR, `${whatFailed} failed unexpectedly: ${message}`);
148
+ }
149
+
150
+ // src/react/store.ts
151
+ var DEFAULT_KEEP_ALIVE_MS = 2e3;
152
+ var LOADING_SNAPSHOT = Object.freeze({
153
+ data: void 0,
154
+ error: void 0,
155
+ isLoading: true,
156
+ isFetching: true
157
+ });
158
+ var IDLE_SNAPSHOT = Object.freeze({
159
+ data: void 0,
160
+ error: void 0,
161
+ isLoading: false,
162
+ isFetching: false
163
+ });
164
+ function createResourceStore(keepAliveMs = DEFAULT_KEEP_ALIVE_MS) {
165
+ const entries = /* @__PURE__ */ new Map();
166
+ let holders = 0;
167
+ let closing = null;
168
+ function publish(entry, next) {
169
+ entry.snapshot = next;
170
+ for (const listener of entry.listeners) listener();
171
+ }
172
+ function load(key, entry) {
173
+ if (entry.controller !== null) return;
174
+ const controller = new AbortController();
175
+ entry.controller = controller;
176
+ entry.version++;
177
+ const version = entry.version;
178
+ if (!entry.snapshot.isFetching) publish(entry, { ...entry.snapshot, isFetching: true });
179
+ const current = () => entries.get(key) === entry && entry.version === version;
180
+ entry.loader(controller.signal).then(
181
+ (data) => {
182
+ if (!current()) return;
183
+ entry.controller = null;
184
+ publish(entry, { data, error: void 0, isLoading: false, isFetching: false });
185
+ },
186
+ (err) => {
187
+ if (!current()) return;
188
+ entry.controller = null;
189
+ if (controller.signal.aborted) return;
190
+ publish(entry, {
191
+ data: entry.snapshot.data,
192
+ error: asNarsilError(err, "A Narsil hook"),
193
+ isLoading: false,
194
+ isFetching: false
195
+ });
196
+ }
197
+ );
198
+ }
199
+ function disposeAll() {
200
+ for (const [key, entry] of [...entries]) drop(key, entry);
201
+ entries.clear();
202
+ }
203
+ function drop(key, entry) {
204
+ if (entries.get(key) === entry) entries.delete(key);
205
+ if (entry.disposal !== null) clearTimeout(entry.disposal);
206
+ entry.disposal = null;
207
+ entry.controller?.abort();
208
+ entry.controller = null;
209
+ }
210
+ return {
211
+ subscribe(key, loader, onChange) {
212
+ let entry = entries.get(key);
213
+ if (entry === void 0) {
214
+ entry = {
215
+ snapshot: LOADING_SNAPSHOT,
216
+ listeners: /* @__PURE__ */ new Set(),
217
+ loader,
218
+ controller: null,
219
+ version: 0,
220
+ disposal: null
221
+ };
222
+ entries.set(key, entry);
223
+ } else {
224
+ entry.loader = loader;
225
+ if (entry.disposal !== null) {
226
+ clearTimeout(entry.disposal);
227
+ entry.disposal = null;
228
+ }
229
+ }
230
+ const held = entry;
231
+ held.listeners.add(onChange);
232
+ const retryable = held.snapshot.isLoading || held.snapshot.error !== void 0;
233
+ if (retryable && held.controller === null) load(key, held);
234
+ return () => {
235
+ held.listeners.delete(onChange);
236
+ if (held.listeners.size > 0 || entries.get(key) !== held || held.disposal !== null) return;
237
+ held.disposal = setTimeout(() => {
238
+ held.disposal = null;
239
+ if (held.listeners.size === 0) drop(key, held);
240
+ }, keepAliveMs);
241
+ };
242
+ },
243
+ snapshot(key) {
244
+ return entries.get(key)?.snapshot ?? LOADING_SNAPSHOT;
245
+ },
246
+ refresh(key) {
247
+ const entry = entries.get(key);
248
+ if (entry !== void 0) load(key, entry);
249
+ },
250
+ retain() {
251
+ holders++;
252
+ if (closing !== null) {
253
+ clearTimeout(closing);
254
+ closing = null;
255
+ }
256
+ let released = false;
257
+ return () => {
258
+ if (released) return;
259
+ released = true;
260
+ holders--;
261
+ if (holders > 0) return;
262
+ closing = setTimeout(() => {
263
+ closing = null;
264
+ if (holders === 0) disposeAll();
265
+ }, keepAliveMs);
266
+ };
267
+ },
268
+ dispose: disposeAll
269
+ };
270
+ }
271
+
272
+ // src/react/context.ts
273
+ var NarsilContext = createContext(null);
274
+ function build(client, keepAliveMs) {
275
+ return { client, store: createResourceStore(keepAliveMs), keepAliveMs };
276
+ }
277
+ function NarsilProvider(props) {
278
+ const { client, keepAliveMs, children } = props;
279
+ const [value, setValue] = useState(() => build(client, keepAliveMs));
280
+ if (value.client !== client || value.keepAliveMs !== keepAliveMs) {
281
+ setValue(build(client, keepAliveMs));
282
+ }
283
+ useEffect(() => value.store.retain(), [value]);
284
+ return createElement(NarsilContext.Provider, { value }, children);
285
+ }
286
+ function useNarsilContext() {
287
+ const value = useContext(NarsilContext);
288
+ if (value === null) {
289
+ throw new NarsilError(
290
+ ErrorCodes.CONFIG_INVALID,
291
+ "A Narsil hook ran outside a NarsilProvider, so it found no client to send through"
292
+ );
293
+ }
294
+ return value;
295
+ }
296
+ function useNarsilClient() {
297
+ return useNarsilContext().client;
298
+ }
299
+
300
+ // src/react/key.ts
301
+ var MAX_DEPTH = 32;
302
+ function refuse(reason) {
303
+ return new NarsilError(
304
+ ErrorCodes.CONFIG_INVALID,
305
+ `A hook argument ${reason}, so the React bindings cannot tell one request from another`
306
+ );
307
+ }
308
+ function writeList(values, out, depth, seen) {
309
+ out.push("[");
310
+ for (let index = 0; index < values.length; index++) {
311
+ if (index > 0) out.push(",");
312
+ write(values[index], out, depth, seen);
313
+ }
314
+ out.push("]");
315
+ }
316
+ function writeObject(value, out, depth, seen) {
317
+ const record = value;
318
+ const keys = Object.keys(record).sort();
319
+ out.push("{");
320
+ let written = 0;
321
+ for (const key of keys) {
322
+ const held = record[key];
323
+ if (held === void 0) continue;
324
+ if (written > 0) out.push(",");
325
+ out.push(JSON.stringify(key), ":");
326
+ write(held, out, depth, seen);
327
+ written++;
328
+ }
329
+ out.push("}");
330
+ }
331
+ function write(value, out, depth, seen) {
332
+ if (value === null) {
333
+ out.push("null");
334
+ return;
335
+ }
336
+ switch (typeof value) {
337
+ case "undefined":
338
+ out.push("undefined");
339
+ return;
340
+ case "string":
341
+ out.push(JSON.stringify(value));
342
+ return;
343
+ case "number":
344
+ case "boolean":
345
+ out.push(String(value));
346
+ return;
347
+ case "bigint":
348
+ out.push(`${value}n`);
349
+ return;
350
+ case "function":
351
+ case "symbol":
352
+ throw refuse(`is a ${typeof value}, which carries no value a request depends on`);
353
+ }
354
+ if (depth >= MAX_DEPTH) throw refuse(`nests deeper than ${MAX_DEPTH} levels`);
355
+ const held = value;
356
+ if (seen.includes(held)) throw refuse("holds a reference back to itself");
357
+ seen.push(held);
358
+ const custom = held.toJSON;
359
+ if (typeof custom === "function") {
360
+ write(custom.call(held), out, depth + 1, seen);
361
+ } else if (Array.isArray(held)) {
362
+ writeList(held, out, depth + 1, seen);
363
+ } else if (ArrayBuffer.isView(held) && !(held instanceof DataView)) {
364
+ writeList(held, out, depth + 1, seen);
365
+ } else {
366
+ writeObject(held, out, depth + 1, seen);
367
+ }
368
+ seen.pop();
369
+ }
370
+ function hashKey(parts) {
371
+ const out = [];
372
+ writeList(parts, out, 0, []);
373
+ return out.join("");
374
+ }
375
+
376
+ // src/react/options.ts
377
+ function requestOf(settings) {
378
+ return { headers: settings?.headers, timeoutMs: settings?.timeoutMs };
379
+ }
380
+ var watchers = /* @__PURE__ */ new Set();
381
+ var listening = false;
382
+ function isHidden() {
383
+ return typeof document !== "undefined" && document.visibilityState === "hidden";
384
+ }
385
+ function announce() {
386
+ for (const watcher of [...watchers]) watcher();
387
+ }
388
+ function watchVisibility(watcher) {
389
+ if (typeof document === "undefined") return () => void 0;
390
+ watchers.add(watcher);
391
+ if (!listening) {
392
+ listening = true;
393
+ document.addEventListener("visibilitychange", announce);
394
+ }
395
+ return () => {
396
+ watchers.delete(watcher);
397
+ if (watchers.size > 0) return;
398
+ listening = false;
399
+ document.removeEventListener("visibilitychange", announce);
400
+ };
401
+ }
402
+ function usePolling(action, intervalMs, active) {
403
+ const run = useEffectEvent(action);
404
+ useEffect(() => {
405
+ if (!active || intervalMs <= 0) return;
406
+ let timer;
407
+ const stop = () => {
408
+ if (timer !== void 0) clearTimeout(timer);
409
+ timer = void 0;
410
+ };
411
+ const tick = () => {
412
+ run();
413
+ timer = setTimeout(tick, intervalMs);
414
+ };
415
+ const start = () => {
416
+ stop();
417
+ timer = setTimeout(tick, intervalMs);
418
+ };
419
+ const onVisibilityChange = () => {
420
+ if (isHidden()) {
421
+ stop();
422
+ return;
423
+ }
424
+ run();
425
+ start();
426
+ };
427
+ if (!isHidden()) start();
428
+ const unwatch = watchVisibility(onVisibilityChange);
429
+ return () => {
430
+ stop();
431
+ unwatch();
432
+ };
433
+ }, [active, intervalMs]);
434
+ }
435
+
436
+ // src/react/read.ts
437
+ var NO_SUBSCRIPTION = () => {
438
+ };
439
+ function useKey(parts) {
440
+ const cached = useRef(null);
441
+ const held = cached.current;
442
+ if (held !== null && held.parts.length === parts.length && parts.every((part, at) => Object.is(part, held.parts[at]))) {
443
+ return held.key;
444
+ }
445
+ const key = hashKey(parts);
446
+ cached.current = { parts, key };
447
+ return key;
448
+ }
449
+ function useRead(parts, run, options) {
450
+ const { client, store } = useNarsilContext();
451
+ const enabled = options?.enabled ?? true;
452
+ const headers = options?.headers;
453
+ const timeoutMs = options?.timeoutMs;
454
+ const key = useKey([...parts, headers, timeoutMs]);
455
+ const call = useRef({ run, request: requestOf(options) });
456
+ useEffect(() => {
457
+ call.current = { run, request: requestOf(options) };
458
+ });
459
+ const subscribe = useCallback(
460
+ (onChange) => {
461
+ if (!enabled) return NO_SUBSCRIPTION;
462
+ const loader = (signal) => {
463
+ const held = call.current;
464
+ return held.run(client, { ...held.request, signal });
465
+ };
466
+ return store.subscribe(key, loader, onChange);
467
+ },
468
+ [enabled, store, client, key]
469
+ );
470
+ const readSnapshot = useCallback(
471
+ () => enabled ? store.snapshot(key) : IDLE_SNAPSHOT,
472
+ [enabled, store, key]
473
+ );
474
+ const readServerSnapshot = useCallback(
475
+ () => enabled ? LOADING_SNAPSHOT : IDLE_SNAPSHOT,
476
+ [enabled]
477
+ );
478
+ const snapshot = useSyncExternalStore(subscribe, readSnapshot, readServerSnapshot);
479
+ const kept = useRef(void 0);
480
+ useEffect(() => {
481
+ if (snapshot.data !== void 0) kept.current = snapshot.data;
482
+ }, [snapshot.data]);
483
+ const refresh = useCallback(() => {
484
+ if (enabled) store.refresh(key);
485
+ }, [enabled, store, key]);
486
+ usePolling(refresh, options?.refreshIntervalMs ?? 0, enabled);
487
+ const keepPrevious = (options?.keepPreviousData ?? false) && snapshot.isLoading;
488
+ const data = keepPrevious && snapshot.data === void 0 ? kept.current : snapshot.data;
489
+ return useMemo(
490
+ () => ({
491
+ data,
492
+ error: snapshot.error,
493
+ isLoading: snapshot.isLoading && data === void 0,
494
+ isFetching: snapshot.isFetching,
495
+ refresh
496
+ }),
497
+ [data, snapshot, refresh]
498
+ );
499
+ }
500
+
501
+ // src/react/documents.ts
502
+ function useDocument(indexName, docId, options) {
503
+ const id = docId ?? "";
504
+ const enabled = (options?.enabled ?? true) && id.length > 0;
505
+ return useRead(["get", indexName, id], (client, request) => client.get(indexName, id, request), {
506
+ ...options,
507
+ enabled
508
+ });
509
+ }
510
+ function useDocuments(indexName, params, options) {
511
+ return useRead(
512
+ ["listDocuments", indexName, params],
513
+ (client, request) => client.listDocuments(indexName, params, request),
514
+ options
515
+ );
516
+ }
517
+
518
+ // src/react/task-state.ts
519
+ var DEFAULT_TASK_POLL_INTERVAL_MS = 250;
520
+ var ERROR_RETRY_INTERVAL_MS = 5e3;
521
+ function pollInterval(interval, failed) {
522
+ return failed ? Math.max(interval, ERROR_RETRY_INTERVAL_MS) : interval;
523
+ }
524
+ function isTerminalTask(record) {
525
+ if (record === null || record === void 0) return false;
526
+ return record.status === "succeeded" || record.status === "failed" || record.status === "cancelled";
527
+ }
528
+
529
+ // src/react/import.ts
530
+ var NOTHING = { task: void 0, error: void 0, starting: false };
531
+ function useImport(indexName, options) {
532
+ const client = useNarsilClient();
533
+ const [progress, setProgress] = useState(NOTHING);
534
+ const polling = useRef(null);
535
+ const sending = useRef(null);
536
+ const inFlight = useRef(false);
537
+ const reported = useRef(null);
538
+ const request = useRef(requestOf(options));
539
+ const settled = useEffectEvent((record) => options?.onSettled?.(record));
540
+ useEffect(() => {
541
+ request.current = requestOf(options);
542
+ });
543
+ useEffect(() => {
544
+ return () => {
545
+ polling.current?.abort();
546
+ };
547
+ }, []);
548
+ const finished = progress.task !== void 0 && isTerminalTask(progress.task);
549
+ useEffect(() => {
550
+ const record = progress.task;
551
+ if (record === void 0 || !finished || reported.current === record.id) return;
552
+ reported.current = record.id;
553
+ settled(record);
554
+ }, [progress.task, finished]);
555
+ const start = useCallback(
556
+ async (source) => {
557
+ const controller = new AbortController();
558
+ sending.current = controller;
559
+ setProgress({ task: void 0, error: void 0, starting: true });
560
+ try {
561
+ const record = await client.startImport(indexName, source, { ...request.current, signal: controller.signal });
562
+ setProgress({ task: record, error: void 0, starting: false });
563
+ return record;
564
+ } catch (err) {
565
+ const failure = asNarsilError(err, "The import");
566
+ const stopped = controller.signal.aborted;
567
+ setProgress({ task: void 0, error: stopped ? void 0 : failure, starting: false });
568
+ throw failure;
569
+ } finally {
570
+ if (sending.current === controller) sending.current = null;
571
+ }
572
+ },
573
+ [client, indexName]
574
+ );
575
+ const taskId = progress.task?.id;
576
+ const running = progress.task !== void 0 && !finished;
577
+ const readTask = () => {
578
+ if (taskId === void 0 || inFlight.current) return;
579
+ const controller = new AbortController();
580
+ polling.current = controller;
581
+ inFlight.current = true;
582
+ client.getTask(taskId, { ...request.current, signal: controller.signal }).then((next) => {
583
+ setProgress((prev) => {
584
+ if (prev.task?.id !== taskId) return prev;
585
+ if (next === null) {
586
+ return {
587
+ ...prev,
588
+ error: new NarsilError(
589
+ ServerErrorCodes.TASK_NOT_FOUND,
590
+ `The server no longer holds task "${taskId}", so the outcome of the load is unknown`,
591
+ { taskId }
592
+ )
593
+ };
594
+ }
595
+ return { task: next, error: void 0, starting: false };
596
+ });
597
+ }).catch((err) => {
598
+ if (controller.signal.aborted) return;
599
+ setProgress((prev) => prev.task?.id === taskId ? { ...prev, error: asNarsilError(err, "The import") } : prev);
600
+ }).finally(() => {
601
+ inFlight.current = false;
602
+ });
603
+ };
604
+ const cancel = useCallback(() => {
605
+ if (progress.starting) {
606
+ sending.current?.abort();
607
+ return;
608
+ }
609
+ if (taskId === void 0 || !running) return;
610
+ client.cancelTask(taskId, request.current).then((next) => {
611
+ setProgress((prev) => prev.task?.id === taskId ? { task: next, error: void 0, starting: false } : prev);
612
+ }).catch((err) => {
613
+ if (err instanceof NarsilError && err.code === ServerErrorCodes.TASK_NOT_CANCELLABLE) return;
614
+ setProgress((prev) => prev.task?.id === taskId ? { ...prev, error: asNarsilError(err, "The import") } : prev);
615
+ });
616
+ }, [client, taskId, running, progress.starting]);
617
+ const reset = useCallback(() => {
618
+ reported.current = null;
619
+ setProgress(NOTHING);
620
+ }, []);
621
+ const interval = pollInterval(options?.pollIntervalMs ?? DEFAULT_TASK_POLL_INTERVAL_MS, progress.error !== void 0);
622
+ usePolling(readTask, interval, running);
623
+ return useMemo(
624
+ () => ({
625
+ start,
626
+ cancel,
627
+ reset,
628
+ task: progress.task,
629
+ progress: progress.task?.progress,
630
+ result: progress.task?.result,
631
+ error: progress.error,
632
+ isImporting: progress.starting || running
633
+ }),
634
+ [start, cancel, reset, progress, running]
635
+ );
636
+ }
637
+
638
+ // src/react/indexes.ts
639
+ function useIndexes(options) {
640
+ return useRead(["listIndexes"], (client, request) => client.listIndexes(request), options);
641
+ }
642
+ function useStats(indexName, options) {
643
+ return useRead(["getStats", indexName], (client, request) => client.getStats(indexName, request), options);
644
+ }
645
+
646
+ // src/react/search.ts
647
+ function useQuery(indexName, params, options) {
648
+ return useRead(
649
+ ["query", indexName, params],
650
+ (client, request) => client.query(indexName, params, request),
651
+ options
652
+ );
653
+ }
654
+ function usePreflight(indexName, params, options) {
655
+ return useRead(
656
+ ["preflight", indexName, params],
657
+ (client, request) => client.preflight(indexName, params, request),
658
+ options
659
+ );
660
+ }
661
+ function useSuggest(indexName, params, options) {
662
+ return useRead(
663
+ ["suggest", indexName, params],
664
+ (client, request) => client.suggest(indexName, params, request),
665
+ options
666
+ );
667
+ }
668
+
669
+ // src/react/tasks.ts
670
+ function useTask(taskId, options) {
671
+ const id = taskId ?? "";
672
+ const enabled = (options?.enabled ?? true) && id.length > 0;
673
+ const state = useRead(["getTask", id], (client, request) => client.getTask(id, request), {
674
+ ...options,
675
+ enabled,
676
+ refreshIntervalMs: 0
677
+ });
678
+ const running = state.data !== null && !isTerminalTask(state.data);
679
+ const interval = pollInterval(options?.pollIntervalMs ?? DEFAULT_TASK_POLL_INTERVAL_MS, state.error !== void 0);
680
+ usePolling(state.refresh, interval, enabled && running);
681
+ return state;
682
+ }
683
+ function useTasks(query, options) {
684
+ return useRead(["listTasks", query], (client, request) => client.listTasks(query, request), options);
685
+ }
686
+
687
+ export { ClientErrorCodes, ErrorCodes, NarsilError, NarsilProvider, ServerErrorCodes, useDocument, useDocuments, useImport, useIndexes, useNarsilClient, usePreflight, useQuery, useStats, useSuggest, useTask, useTasks };