@duckedup/nidus 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 duckedup
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,130 @@
1
+ # @duckedup/nidus
2
+
3
+ The JavaScript/TypeScript client for [nidus](https://nidus.duckedup.org) — a small,
4
+ fast vector store. This package connects to a running `nidus serve` instance over
5
+ HTTP, whether it's on your laptop or a remote host.
6
+
7
+ It is a **remote client**: zero runtime dependencies, built on the platform-global
8
+ `fetch`, so it runs unchanged on Node 18+, Deno, Bun, Cloudflare Workers, and in the
9
+ browser.
10
+
11
+ ```sh
12
+ npm install @duckedup/nidus
13
+ ```
14
+
15
+ ## Connecting
16
+
17
+ "Local vs remote" is just the base URL — point the client at a local `nidus serve`
18
+ or any reachable host.
19
+
20
+ ```ts
21
+ import { NidusClient } from "@duckedup/nidus";
22
+
23
+ // Local
24
+ const db = new NidusClient({ baseUrl: "http://127.0.0.1:7700" });
25
+
26
+ // Remote, with the bearer token the server was started with (`nidus serve --token`)
27
+ const db = new NidusClient({
28
+ baseUrl: "https://nidus.internal.example.com",
29
+ token: process.env.NIDUS_TOKEN,
30
+ });
31
+ ```
32
+
33
+ ## Upserting and searching
34
+
35
+ `attrs` accept plain JS values — strings, integers, booleans, string arrays, and
36
+ `null` — and are normalized to nidus's typed values for you. (For an explicit type,
37
+ use the `v.*` helpers.)
38
+
39
+ ```ts
40
+ await db.createCollection("docs");
41
+
42
+ await db.upsert("docs", [
43
+ { id: "a", vector: [0.1, 0.2, 0.3], attrs: { lang: "rust", year: 2024 } },
44
+ { id: "b", vector: [0.4, 0.5, 0.6], attrs: { lang: "go", year: 2023 } },
45
+ // a text-only doc — omit the vector
46
+ { id: "c", attrs: { body: "vector stores are neat" } },
47
+ ]);
48
+
49
+ const hits = await db.search({ query: [0.1, 0.2, 0.3], topK: 5 });
50
+ for (const hit of hits) {
51
+ console.log(hit.id, hit.score, hit.attrs.lang); // attrs decoded to plain JS values
52
+ }
53
+ ```
54
+
55
+ ## Filtering
56
+
57
+ Build an AND-filter with the `f.*` helpers. Each predicate is a positive assertion
58
+ about a present attribute (an absent key matches nothing).
59
+
60
+ ```ts
61
+ import { f } from "@duckedup/nidus";
62
+
63
+ const hits = await db.search({
64
+ query: [0.1, 0.2, 0.3],
65
+ topK: 10,
66
+ filter: f.and(
67
+ f.eq("lang", "rust"),
68
+ f.ge("year", 2020),
69
+ f.in("status", ["published", "draft"]),
70
+ f.glob("path", "src/*"),
71
+ ),
72
+ });
73
+ ```
74
+
75
+ ## Full-text and hybrid search
76
+
77
+ ```ts
78
+ await db.setFtsSchema("docs", ["body"]);
79
+
80
+ // BM25 text search
81
+ const text = await db.textSearch({ field: "body", query: "vector store", topK: 10 });
82
+
83
+ // Fuse vector + text via reciprocal rank fusion
84
+ const hybrid = await db.hybridSearch({
85
+ vector: [0.1, 0.2, 0.3],
86
+ field: "body",
87
+ text: "vector store",
88
+ topK: 10,
89
+ });
90
+ ```
91
+
92
+ ## Everything else
93
+
94
+ ```ts
95
+ await db.collections(); // string[]
96
+ await db.stats(); // dimension, distance, ANN config, footprint
97
+ await db.list({ scope: ["docs"], filter: f.and(f.eq("lang", "rust")) });
98
+ await db.records("docs"); // every record, attrs decoded
99
+ await db.getMeta("docs"); await db.setMeta("docs", { owner: "search-team" });
100
+ await db.delete("docs", { ids: ["a"] });
101
+ await db.deleteWhere("docs", f.and(f.lt("year", 2000)));
102
+ await db.flush(); await db.compact();
103
+ await db.dropCollection("docs");
104
+ ```
105
+
106
+ ## Errors
107
+
108
+ A failed request throws a `NidusError` carrying the HTTP status the server reported,
109
+ so you can tell a client fault from a server fault:
110
+
111
+ ```ts
112
+ import { NidusError } from "@duckedup/nidus";
113
+
114
+ try {
115
+ await db.upsert("docs", records);
116
+ } catch (err) {
117
+ if (err instanceof NidusError) {
118
+ if (err.isBadRequest) {/* e.g. vector dimension mismatch */}
119
+ if (err.isLocked) {/* the writer lock is held elsewhere (409) */}
120
+ console.error(err.status, err.message);
121
+ }
122
+ }
123
+ ```
124
+
125
+ A status of `0` means a transport-level failure (the server was unreachable, or the
126
+ request timed out — configure `timeoutMs` on the client).
127
+
128
+ ## License
129
+
130
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,413 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ NidusClient: () => NidusClient,
24
+ NidusError: () => NidusError,
25
+ decodeAttrs: () => decodeAttrs,
26
+ decodeValue: () => decodeValue,
27
+ encodeAttrs: () => encodeAttrs,
28
+ encodeValue: () => encodeValue,
29
+ f: () => f,
30
+ v: () => v
31
+ });
32
+ module.exports = __toCommonJS(index_exports);
33
+
34
+ // src/errors.ts
35
+ var NidusError = class extends Error {
36
+ /** The HTTP status code, or `0` for a transport/timeout failure (no response). */
37
+ status;
38
+ constructor(message, status) {
39
+ super(message);
40
+ this.name = "NidusError";
41
+ this.status = status;
42
+ }
43
+ /** A malformed request the server rejected (HTTP 400). */
44
+ get isBadRequest() {
45
+ return this.status === 400;
46
+ }
47
+ /** The store is read-only (HTTP 403). */
48
+ get isReadOnly() {
49
+ return this.status === 403;
50
+ }
51
+ /** The writer lock is held by another process (HTTP 409). */
52
+ get isLocked() {
53
+ return this.status === 409;
54
+ }
55
+ /** Out of capacity: `max_vector_bytes` exceeded or OOM (HTTP 507). */
56
+ get isOutOfCapacity() {
57
+ return this.status === 507;
58
+ }
59
+ };
60
+
61
+ // src/values.ts
62
+ var v = {
63
+ str: (s) => ({ Str: s }),
64
+ int: (n) => {
65
+ if (!Number.isInteger(n)) {
66
+ throw new TypeError(`v.int expects an integer, got ${n}`);
67
+ }
68
+ return { Int: n };
69
+ },
70
+ bool: (b) => ({ Bool: b }),
71
+ list: (items) => ({ List: items }),
72
+ /** The explicit `Null` value — set-but-empty, distinct from an absent key. */
73
+ nil: () => "Null"
74
+ };
75
+ function isValue(x) {
76
+ if (x === "Null") return true;
77
+ if (typeof x !== "object" || x === null) return false;
78
+ return "Str" in x || "Int" in x || "Bool" in x || "List" in x;
79
+ }
80
+ function encodeValue(input) {
81
+ if (isValue(input)) return input;
82
+ if (input === null) return "Null";
83
+ switch (typeof input) {
84
+ case "string":
85
+ return { Str: input };
86
+ case "boolean":
87
+ return { Bool: input };
88
+ case "number":
89
+ return v.int(input);
90
+ case "object":
91
+ if (Array.isArray(input)) {
92
+ if (!input.every((e) => typeof e === "string")) {
93
+ throw new TypeError("a List attribute must contain only strings");
94
+ }
95
+ return { List: input };
96
+ }
97
+ // falls through
98
+ default:
99
+ throw new TypeError(`cannot encode attribute value: ${String(input)}`);
100
+ }
101
+ }
102
+ function encodeAttrs(attrs) {
103
+ const out = {};
104
+ for (const [k, val] of Object.entries(attrs)) {
105
+ out[k] = encodeValue(val);
106
+ }
107
+ return out;
108
+ }
109
+ function decodeValue(value) {
110
+ if (value === "Null") return null;
111
+ if ("Str" in value) return value.Str;
112
+ if ("Int" in value) return value.Int;
113
+ if ("Bool" in value) return value.Bool;
114
+ if ("List" in value) return value.List;
115
+ return value;
116
+ }
117
+ function decodeAttrs(attrs) {
118
+ const out = {};
119
+ for (const [k, val] of Object.entries(attrs)) {
120
+ out[k] = decodeValue(val);
121
+ }
122
+ return out;
123
+ }
124
+
125
+ // src/client.ts
126
+ var NidusClient = class {
127
+ baseUrl;
128
+ token;
129
+ doFetch;
130
+ timeoutMs;
131
+ extraHeaders;
132
+ constructor(options) {
133
+ if (!options.baseUrl) {
134
+ throw new TypeError("NidusClient requires a baseUrl");
135
+ }
136
+ this.baseUrl = options.baseUrl.replace(/\/+$/, "");
137
+ this.token = options.token;
138
+ this.timeoutMs = options.timeoutMs ?? 0;
139
+ this.extraHeaders = options.headers ?? {};
140
+ const f2 = options.fetch ?? globalThis.fetch;
141
+ if (typeof f2 !== "function") {
142
+ throw new TypeError(
143
+ "no fetch available; pass options.fetch (Node < 18, or a custom runtime)"
144
+ );
145
+ }
146
+ this.doFetch = f2 === globalThis.fetch ? f2.bind(globalThis) : f2;
147
+ }
148
+ // ── Admin / introspection ─────────────────────────────────────────────────
149
+ /** Liveness check. Returns `true` when the server answers `/health`. */
150
+ async health() {
151
+ try {
152
+ const res = await this.raw("GET", "/health");
153
+ return res.ok;
154
+ } catch {
155
+ return false;
156
+ }
157
+ }
158
+ /** Store-wide introspection: dimension, distance, ANN config, collections, footprint. */
159
+ stats() {
160
+ return this.request("GET", "/stats");
161
+ }
162
+ /** List every collection name. */
163
+ collections() {
164
+ return this.request("GET", "/collections");
165
+ }
166
+ /** Create a collection. Idempotent on the server side. */
167
+ async createCollection(name) {
168
+ await this.request("POST", `/collections/${enc(name)}`, {});
169
+ }
170
+ /** Drop a collection and all its records. */
171
+ async dropCollection(name) {
172
+ await this.request("DELETE", `/collections/${enc(name)}`);
173
+ }
174
+ /** Read a collection's free-form string metadata. */
175
+ getMeta(name) {
176
+ return this.request(
177
+ "GET",
178
+ `/collections/${enc(name)}/meta`
179
+ );
180
+ }
181
+ /** Replace a collection's free-form string metadata. */
182
+ async setMeta(name, meta) {
183
+ await this.request("PUT", `/collections/${enc(name)}/meta`, meta);
184
+ }
185
+ // ── Data ──────────────────────────────────────────────────────────────────
186
+ /**
187
+ * Insert or replace records (idempotent on `id` within the collection).
188
+ * `attrs` accept plain JS values or `v.*` helpers; they are normalized for you.
189
+ * Returns the number of records upserted.
190
+ */
191
+ async upsert(name, records) {
192
+ const wire = records.map((r) => ({
193
+ id: r.id,
194
+ ...r.vector !== void 0 ? { vector: r.vector } : {},
195
+ attrs: encodeAttrs(r.attrs)
196
+ }));
197
+ const res = await this.request(
198
+ "POST",
199
+ `/collections/${enc(name)}/upsert`,
200
+ { records: wire }
201
+ );
202
+ return res.upserted;
203
+ }
204
+ /** Delete records by id. Returns the number deleted. */
205
+ async delete(name, opts) {
206
+ const res = await this.request(
207
+ "POST",
208
+ `/collections/${enc(name)}/delete`,
209
+ { ids: opts.ids }
210
+ );
211
+ return res.deleted;
212
+ }
213
+ /** Delete every record matching `filter`. Returns the number deleted. */
214
+ async deleteWhere(name, filter) {
215
+ const res = await this.request(
216
+ "POST",
217
+ `/collections/${enc(name)}/delete`,
218
+ { filter }
219
+ );
220
+ return res.deleted;
221
+ }
222
+ /** Fetch every record in a collection (attrs decoded to plain JS values). */
223
+ async records(name) {
224
+ const recs = await this.request(
225
+ "GET",
226
+ `/collections/${enc(name)}/records`
227
+ );
228
+ return recs.map((r) => ({
229
+ id: r.id,
230
+ ...r.vector !== void 0 ? { vector: r.vector } : {},
231
+ attrs: decodeAttrs(r.attrs)
232
+ }));
233
+ }
234
+ /** Declare the full-text-indexed attribute fields for a collection. */
235
+ async setFtsSchema(name, fields) {
236
+ await this.request("POST", `/collections/${enc(name)}/fts-schema`, {
237
+ fields
238
+ });
239
+ }
240
+ // ── Search ──────────────────────────────────────────────────────────────
241
+ /** Vector (cosine) nearest-neighbour search. Empty `scope` searches all collections. */
242
+ search(opts) {
243
+ return this.searchRequest("/search", {
244
+ query: opts.query,
245
+ scope: opts.scope ?? [],
246
+ top_k: opts.topK,
247
+ min_score: opts.minScore,
248
+ filter: opts.filter ?? []
249
+ });
250
+ }
251
+ /** BM25 full-text search over one indexed field. */
252
+ textSearch(opts) {
253
+ return this.searchRequest("/text-search", {
254
+ field: opts.field,
255
+ query: opts.query,
256
+ scope: opts.scope ?? [],
257
+ top_k: opts.topK,
258
+ min_score: opts.minScore,
259
+ filter: opts.filter ?? []
260
+ });
261
+ }
262
+ /** Hybrid search: fuse a vector query and a BM25 text query via RRF. */
263
+ hybridSearch(opts) {
264
+ return this.searchRequest("/hybrid-search", {
265
+ vector: opts.vector,
266
+ field: opts.field,
267
+ text: opts.text,
268
+ scope: opts.scope ?? [],
269
+ top_k: opts.topK,
270
+ filter: opts.filter ?? [],
271
+ rrf_k: opts.rrfK,
272
+ candidates: opts.candidates
273
+ });
274
+ }
275
+ /** Metadata-only listing (no vector), paginated by `offset`/`limit`. */
276
+ list(opts = {}) {
277
+ return this.searchRequest("/list", {
278
+ scope: opts.scope ?? [],
279
+ offset: opts.offset,
280
+ limit: opts.limit,
281
+ filter: opts.filter ?? []
282
+ });
283
+ }
284
+ // ── Maintenance ───────────────────────────────────────────────────────────
285
+ /** Force a durability flush. */
286
+ async flush() {
287
+ await this.request("POST", "/flush", {});
288
+ }
289
+ /** Compact the store (reclaim space from deleted/overwritten rows). */
290
+ async compact() {
291
+ await this.request("POST", "/compact", {});
292
+ }
293
+ // ── Internals ─────────────────────────────────────────────────────────────
294
+ /** Run a search-family request and decode the resulting hits' attrs. */
295
+ async searchRequest(path, body) {
296
+ const hits = await this.request("POST", path, prune(body));
297
+ return hits.map((h) => ({
298
+ collection: h.collection,
299
+ id: h.id,
300
+ score: h.score,
301
+ attrs: decodeAttrs(h.attrs)
302
+ }));
303
+ }
304
+ /** Issue a request and parse a JSON body, mapping a non-2xx to {@link NidusError}. */
305
+ async request(method, path, body) {
306
+ const res = await this.raw(method, path, body);
307
+ const text = await res.text();
308
+ if (!res.ok) {
309
+ throw new NidusError(extractError(text, res.status), res.status);
310
+ }
311
+ return text ? JSON.parse(text) : void 0;
312
+ }
313
+ /** The bare transport: headers, auth, timeout, and transport-error mapping. */
314
+ async raw(method, path, body) {
315
+ const headers = { ...this.extraHeaders };
316
+ if (this.token) headers.authorization = `Bearer ${this.token}`;
317
+ let payload;
318
+ if (body !== void 0) {
319
+ headers["content-type"] = "application/json";
320
+ payload = JSON.stringify(body);
321
+ }
322
+ const controller = this.timeoutMs > 0 ? new AbortController() : void 0;
323
+ const timer = controller && this.timeoutMs > 0 ? setTimeout(() => controller.abort(), this.timeoutMs) : void 0;
324
+ try {
325
+ return await this.doFetch(`${this.baseUrl}${path}`, {
326
+ method,
327
+ headers,
328
+ body: payload,
329
+ signal: controller?.signal
330
+ });
331
+ } catch (err) {
332
+ const reason = controller?.signal.aborted ?? false ? `request to ${path} timed out after ${this.timeoutMs}ms` : `request to ${path} failed: ${err.message}`;
333
+ throw new NidusError(reason, 0);
334
+ } finally {
335
+ if (timer) clearTimeout(timer);
336
+ }
337
+ }
338
+ };
339
+ function enc(name) {
340
+ return encodeURIComponent(name);
341
+ }
342
+ function prune(body) {
343
+ const out = {};
344
+ for (const [k, val] of Object.entries(body)) {
345
+ if (val !== void 0) out[k] = val;
346
+ }
347
+ return out;
348
+ }
349
+ function extractError(text, status) {
350
+ try {
351
+ const parsed = JSON.parse(text);
352
+ if (parsed && typeof parsed.error === "string") return parsed.error;
353
+ } catch {
354
+ }
355
+ return text || `HTTP ${status}`;
356
+ }
357
+
358
+ // src/filter.ts
359
+ var f = {
360
+ /** `attrs[key] === value`. */
361
+ eq: (key, value) => ({
362
+ Eq: [key, encodeValue(value)]
363
+ }),
364
+ /** `attrs[key]` is present and `!== value`. */
365
+ ne: (key, value) => ({
366
+ Ne: [key, encodeValue(value)]
367
+ }),
368
+ /** `attrs[key]` is a `Str` matching the glob pattern (`*`, `?`, `[..]`). */
369
+ glob: (key, pattern) => ({ Glob: [key, pattern] }),
370
+ /** `attrs[key]` equals one of `values`. */
371
+ in: (key, values) => ({
372
+ In: [key, values.map(encodeValue)]
373
+ }),
374
+ /** `attrs[key]` is present and equals none of `values`. */
375
+ notIn: (key, values) => ({
376
+ NotIn: [key, values.map(encodeValue)]
377
+ }),
378
+ /** `attrs[key] < value` (same-type, orderable). */
379
+ lt: (key, value) => ({
380
+ Lt: [key, encodeValue(value)]
381
+ }),
382
+ /** `attrs[key] <= value` (same-type, orderable). */
383
+ le: (key, value) => ({
384
+ Le: [key, encodeValue(value)]
385
+ }),
386
+ /** `attrs[key] > value` (same-type, orderable). */
387
+ gt: (key, value) => ({
388
+ Gt: [key, encodeValue(value)]
389
+ }),
390
+ /** `attrs[key] >= value` (same-type, orderable). */
391
+ ge: (key, value) => ({
392
+ Ge: [key, encodeValue(value)]
393
+ }),
394
+ /** Collect predicates into a {@link Filter} (purely sugar — they already AND). */
395
+ and: (...preds) => preds
396
+ };
397
+ // Annotate the CommonJS export names for ESM import in node:
398
+ 0 && (module.exports = {
399
+ NidusClient,
400
+ NidusError,
401
+ decodeAttrs,
402
+ decodeValue,
403
+ encodeAttrs,
404
+ encodeValue,
405
+ f,
406
+ v
407
+ });
408
+ //! Error type carrying the HTTP status the server reported.
409
+ //! Ergonomic constructors and decoders for the externally-tagged `Value` wire type.
410
+ //! `NidusClient` — a remote client over the `nidus serve` HTTP API.
411
+ //! Filter builder producing the bare predicate-array wire shape.
412
+ //! `@duckedup/nidus` — the JavaScript/TypeScript client for nidus.
413
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/values.ts","../src/client.ts","../src/filter.ts"],"sourcesContent":["//! `@duckedup/nidus` — the JavaScript/TypeScript client for nidus.\n//\n// A zero-dependency, cross-runtime remote client over the `nidus serve` HTTP API.\n// Point a {@link NidusClient} at a local or remote server, then upsert and search.\n\nexport { NidusClient } from \"./client.js\";\nexport type { FetchLike, NidusClientOptions } from \"./client.js\";\nexport { NidusError } from \"./errors.js\";\nexport { f } from \"./filter.js\";\nexport { decodeAttrs, decodeValue, encodeAttrs, encodeValue, v } from \"./values.js\";\nexport type {\n AnnInfo,\n AttrInput,\n DecodedRecord,\n DecodedValue,\n Filter,\n Footprint,\n Hit,\n HybridSearchOptions,\n ListOptions,\n NidusRecord,\n Predicate,\n RecordInput,\n SearchOptions,\n Stats,\n TextSearchOptions,\n Value,\n} from \"./types.js\";\n","//! Error type carrying the HTTP status the server reported.\n//\n// The server replies to a failed request with `{ \"error\": <message> }` and a\n// meaningful status (`src/server/mod.rs#classify`): 400 dimension mismatch,\n// 403 read-only store, 409 writer-lock conflict, 507 capacity/OOM, 500 otherwise.\n// Callers branch on `.status` to tell a client fault from a server fault.\n\n/** An error returned by a `nidus` server, or a transport failure reaching it. */\nexport class NidusError extends Error {\n /** The HTTP status code, or `0` for a transport/timeout failure (no response). */\n readonly status: number;\n\n constructor(message: string, status: number) {\n super(message);\n this.name = \"NidusError\";\n this.status = status;\n }\n\n /** A malformed request the server rejected (HTTP 400). */\n get isBadRequest(): boolean {\n return this.status === 400;\n }\n /** The store is read-only (HTTP 403). */\n get isReadOnly(): boolean {\n return this.status === 403;\n }\n /** The writer lock is held by another process (HTTP 409). */\n get isLocked(): boolean {\n return this.status === 409;\n }\n /** Out of capacity: `max_vector_bytes` exceeded or OOM (HTTP 507). */\n get isOutOfCapacity(): boolean {\n return this.status === 507;\n }\n}\n","//! Ergonomic constructors and decoders for the externally-tagged `Value` wire type.\n//\n// Callers should never hand-write `{ Str: \"x\" }`. Use `v.str(\"x\")`, `v.int(5)`,\n// etc., or just pass plain JS values into `attrs` — `encodeValue` normalizes them.\n\nimport type { AttrInput, DecodedValue, Value } from \"./types.js\";\n\n/**\n * Value constructors mirroring the `Value` variants. `v.int` requires a safe\n * integer (the store's attribute integer is an `i64`; a non-integer would be a\n * silent type error since there is no float attribute).\n */\nexport const v = {\n str: (s: string): Value => ({ Str: s }),\n int: (n: number): Value => {\n if (!Number.isInteger(n)) {\n throw new TypeError(`v.int expects an integer, got ${n}`);\n }\n return { Int: n };\n },\n bool: (b: boolean): Value => ({ Bool: b }),\n list: (items: string[]): Value => ({ List: items }),\n /** The explicit `Null` value — set-but-empty, distinct from an absent key. */\n nil: (): Value => \"Null\",\n} as const;\n\n/** True if `x` is already a wire-tagged {@link Value}. */\nfunction isValue(x: unknown): x is Value {\n if (x === \"Null\") return true;\n if (typeof x !== \"object\" || x === null) return false;\n return (\n \"Str\" in x || \"Int\" in x || \"Bool\" in x || \"List\" in x\n );\n}\n\n/**\n * Normalize a caller-supplied {@link AttrInput} into the wire {@link Value} shape.\n * Plain scalars map by type; an already-tagged `Value` passes through unchanged.\n * Throws on a non-integer number or a non-string list element.\n */\nexport function encodeValue(input: AttrInput): Value {\n if (isValue(input)) return input;\n if (input === null) return \"Null\";\n switch (typeof input) {\n case \"string\":\n return { Str: input };\n case \"boolean\":\n return { Bool: input };\n case \"number\":\n return v.int(input);\n case \"object\":\n if (Array.isArray(input)) {\n if (!input.every((e) => typeof e === \"string\")) {\n throw new TypeError(\"a List attribute must contain only strings\");\n }\n return { List: input };\n }\n // falls through\n default:\n throw new TypeError(`cannot encode attribute value: ${String(input)}`);\n }\n}\n\n/** Normalize a whole `attrs` map of {@link AttrInput} into wire {@link Value}s. */\nexport function encodeAttrs(\n attrs: Record<string, AttrInput>,\n): Record<string, Value> {\n const out: Record<string, Value> = {};\n for (const [k, val] of Object.entries(attrs)) {\n out[k] = encodeValue(val);\n }\n return out;\n}\n\n/** Decode a wire {@link Value} back to a plain JS value. */\nexport function decodeValue(value: Value): DecodedValue {\n if (value === \"Null\") return null;\n if (\"Str\" in value) return value.Str;\n if (\"Int\" in value) return value.Int;\n if (\"Bool\" in value) return value.Bool;\n if (\"List\" in value) return value.List;\n // Unknown tag (forward-compat): hand it back untouched.\n return value as unknown as DecodedValue;\n}\n\n/** Decode a whole wire `attrs` map back to plain JS values. */\nexport function decodeAttrs(\n attrs: Record<string, Value>,\n): Record<string, DecodedValue> {\n const out: Record<string, DecodedValue> = {};\n for (const [k, val] of Object.entries(attrs)) {\n out[k] = decodeValue(val);\n }\n return out;\n}\n","//! `NidusClient` — a remote client over the `nidus serve` HTTP API.\n//\n// One method per endpoint (`src/server/mod.rs`). \"Local vs remote\" is just the\n// base URL: point at a local `nidus serve` or any reachable host. Built on the\n// platform-global `fetch`, so it runs unchanged on Node 18+, Deno, Bun, Cloudflare\n// Workers, and browsers — with no runtime dependencies.\n\nimport { NidusError } from \"./errors.js\";\nimport type {\n DecodedRecord,\n Filter,\n Hit,\n HybridSearchOptions,\n ListOptions,\n NidusRecord,\n RecordInput,\n SearchOptions,\n Stats,\n TextSearchOptions,\n Value,\n} from \"./types.js\";\nimport { decodeAttrs, encodeAttrs } from \"./values.js\";\n\n/** Minimal `fetch` signature the client needs — satisfied by the platform global. */\nexport type FetchLike = (\n input: string,\n init?: RequestInit,\n) => Promise<Response>;\n\n/** Construction options for {@link NidusClient}. */\nexport interface NidusClientOptions {\n /** Base URL of the server, e.g. `http://127.0.0.1:7700`. Trailing slash optional. */\n baseUrl: string;\n /** Bearer token, when the server was started with `--token`. */\n token?: string;\n /** Override the `fetch` implementation (defaults to `globalThis.fetch`). */\n fetch?: FetchLike;\n /** Per-request timeout in milliseconds. Omit (or `0`) to disable. */\n timeoutMs?: number;\n /** Extra headers sent on every request. */\n headers?: Record<string, string>;\n}\n\nexport class NidusClient {\n private readonly baseUrl: string;\n private readonly token?: string;\n private readonly doFetch: FetchLike;\n private readonly timeoutMs: number;\n private readonly extraHeaders: Record<string, string>;\n\n constructor(options: NidusClientOptions) {\n if (!options.baseUrl) {\n throw new TypeError(\"NidusClient requires a baseUrl\");\n }\n this.baseUrl = options.baseUrl.replace(/\\/+$/, \"\");\n this.token = options.token;\n this.timeoutMs = options.timeoutMs ?? 0;\n this.extraHeaders = options.headers ?? {};\n const f = options.fetch ?? globalThis.fetch;\n if (typeof f !== \"function\") {\n throw new TypeError(\n \"no fetch available; pass options.fetch (Node < 18, or a custom runtime)\",\n );\n }\n // Bind so a passed `globalThis.fetch` keeps its `this`.\n this.doFetch = f === globalThis.fetch ? f.bind(globalThis) : f;\n }\n\n // ── Admin / introspection ─────────────────────────────────────────────────\n\n /** Liveness check. Returns `true` when the server answers `/health`. */\n async health(): Promise<boolean> {\n try {\n const res = await this.raw(\"GET\", \"/health\");\n return res.ok;\n } catch {\n return false;\n }\n }\n\n /** Store-wide introspection: dimension, distance, ANN config, collections, footprint. */\n stats(): Promise<Stats> {\n return this.request<Stats>(\"GET\", \"/stats\");\n }\n\n /** List every collection name. */\n collections(): Promise<string[]> {\n return this.request<string[]>(\"GET\", \"/collections\");\n }\n\n /** Create a collection. Idempotent on the server side. */\n async createCollection(name: string): Promise<void> {\n await this.request(\"POST\", `/collections/${enc(name)}`, {});\n }\n\n /** Drop a collection and all its records. */\n async dropCollection(name: string): Promise<void> {\n await this.request(\"DELETE\", `/collections/${enc(name)}`);\n }\n\n /** Read a collection's free-form string metadata. */\n getMeta(name: string): Promise<Record<string, string>> {\n return this.request<Record<string, string>>(\n \"GET\",\n `/collections/${enc(name)}/meta`,\n );\n }\n\n /** Replace a collection's free-form string metadata. */\n async setMeta(name: string, meta: Record<string, string>): Promise<void> {\n await this.request(\"PUT\", `/collections/${enc(name)}/meta`, meta);\n }\n\n // ── Data ──────────────────────────────────────────────────────────────────\n\n /**\n * Insert or replace records (idempotent on `id` within the collection).\n * `attrs` accept plain JS values or `v.*` helpers; they are normalized for you.\n * Returns the number of records upserted.\n */\n async upsert(name: string, records: RecordInput[]): Promise<number> {\n const wire: NidusRecord[] = records.map((r) => ({\n id: r.id,\n ...(r.vector !== undefined ? { vector: r.vector } : {}),\n attrs: encodeAttrs(r.attrs),\n }));\n const res = await this.request<{ upserted: number }>(\n \"POST\",\n `/collections/${enc(name)}/upsert`,\n { records: wire },\n );\n return res.upserted;\n }\n\n /** Delete records by id. Returns the number deleted. */\n async delete(name: string, opts: { ids: string[] }): Promise<number> {\n const res = await this.request<{ deleted: number }>(\n \"POST\",\n `/collections/${enc(name)}/delete`,\n { ids: opts.ids },\n );\n return res.deleted;\n }\n\n /** Delete every record matching `filter`. Returns the number deleted. */\n async deleteWhere(name: string, filter: Filter): Promise<number> {\n const res = await this.request<{ deleted: number }>(\n \"POST\",\n `/collections/${enc(name)}/delete`,\n { filter },\n );\n return res.deleted;\n }\n\n /** Fetch every record in a collection (attrs decoded to plain JS values). */\n async records(name: string): Promise<DecodedRecord[]> {\n const recs = await this.request<NidusRecord[]>(\n \"GET\",\n `/collections/${enc(name)}/records`,\n );\n return recs.map((r) => ({\n id: r.id,\n ...(r.vector !== undefined ? { vector: r.vector } : {}),\n attrs: decodeAttrs(r.attrs),\n }));\n }\n\n /** Declare the full-text-indexed attribute fields for a collection. */\n async setFtsSchema(name: string, fields: string[]): Promise<void> {\n await this.request(\"POST\", `/collections/${enc(name)}/fts-schema`, {\n fields,\n });\n }\n\n // ── Search ──────────────────────────────────────────────────────────────\n\n /** Vector (cosine) nearest-neighbour search. Empty `scope` searches all collections. */\n search(opts: SearchOptions): Promise<Hit[]> {\n return this.searchRequest(\"/search\", {\n query: opts.query,\n scope: opts.scope ?? [],\n top_k: opts.topK,\n min_score: opts.minScore,\n filter: opts.filter ?? [],\n });\n }\n\n /** BM25 full-text search over one indexed field. */\n textSearch(opts: TextSearchOptions): Promise<Hit[]> {\n return this.searchRequest(\"/text-search\", {\n field: opts.field,\n query: opts.query,\n scope: opts.scope ?? [],\n top_k: opts.topK,\n min_score: opts.minScore,\n filter: opts.filter ?? [],\n });\n }\n\n /** Hybrid search: fuse a vector query and a BM25 text query via RRF. */\n hybridSearch(opts: HybridSearchOptions): Promise<Hit[]> {\n return this.searchRequest(\"/hybrid-search\", {\n vector: opts.vector,\n field: opts.field,\n text: opts.text,\n scope: opts.scope ?? [],\n top_k: opts.topK,\n filter: opts.filter ?? [],\n rrf_k: opts.rrfK,\n candidates: opts.candidates,\n });\n }\n\n /** Metadata-only listing (no vector), paginated by `offset`/`limit`. */\n list(opts: ListOptions = {}): Promise<Hit[]> {\n return this.searchRequest(\"/list\", {\n scope: opts.scope ?? [],\n offset: opts.offset,\n limit: opts.limit,\n filter: opts.filter ?? [],\n });\n }\n\n // ── Maintenance ───────────────────────────────────────────────────────────\n\n /** Force a durability flush. */\n async flush(): Promise<void> {\n await this.request(\"POST\", \"/flush\", {});\n }\n\n /** Compact the store (reclaim space from deleted/overwritten rows). */\n async compact(): Promise<void> {\n await this.request(\"POST\", \"/compact\", {});\n }\n\n // ── Internals ─────────────────────────────────────────────────────────────\n\n /** Run a search-family request and decode the resulting hits' attrs. */\n private async searchRequest(\n path: string,\n body: Record<string, unknown>,\n ): Promise<Hit[]> {\n const hits = await this.request<RawHit[]>(\"POST\", path, prune(body));\n return hits.map((h) => ({\n collection: h.collection,\n id: h.id,\n score: h.score,\n attrs: decodeAttrs(h.attrs),\n }));\n }\n\n /** Issue a request and parse a JSON body, mapping a non-2xx to {@link NidusError}. */\n private async request<T>(\n method: string,\n path: string,\n body?: unknown,\n ): Promise<T> {\n const res = await this.raw(method, path, body);\n const text = await res.text();\n if (!res.ok) {\n throw new NidusError(extractError(text, res.status), res.status);\n }\n return (text ? JSON.parse(text) : undefined) as T;\n }\n\n /** The bare transport: headers, auth, timeout, and transport-error mapping. */\n private async raw(\n method: string,\n path: string,\n body?: unknown,\n ): Promise<Response> {\n const headers: Record<string, string> = { ...this.extraHeaders };\n if (this.token) headers.authorization = `Bearer ${this.token}`;\n let payload: string | undefined;\n if (body !== undefined) {\n headers[\"content-type\"] = \"application/json\";\n payload = JSON.stringify(body);\n }\n\n const controller =\n this.timeoutMs > 0 ? new AbortController() : undefined;\n const timer =\n controller && this.timeoutMs > 0\n ? setTimeout(() => controller.abort(), this.timeoutMs)\n : undefined;\n try {\n return await this.doFetch(`${this.baseUrl}${path}`, {\n method,\n headers,\n body: payload,\n signal: controller?.signal,\n });\n } catch (err) {\n const reason =\n controller?.signal.aborted ?? false\n ? `request to ${path} timed out after ${this.timeoutMs}ms`\n : `request to ${path} failed: ${(err as Error).message}`;\n throw new NidusError(reason, 0);\n } finally {\n if (timer) clearTimeout(timer);\n }\n }\n}\n\n/** A hit as it arrives on the wire, before attrs are decoded. */\ninterface RawHit {\n collection: string;\n id: string;\n score: number;\n attrs: Record<string, Value>;\n}\n\n/** Path-segment encode a collection name (allows slashes/spaces in names). */\nfunction enc(name: string): string {\n return encodeURIComponent(name);\n}\n\n/** Drop `undefined` fields so server `#[serde(default)]`s apply instead. */\nfunction prune(body: Record<string, unknown>): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const [k, val] of Object.entries(body)) {\n if (val !== undefined) out[k] = val;\n }\n return out;\n}\n\n/** Pull the `{ \"error\": … }` message out of a failed response, or fall back. */\nfunction extractError(text: string, status: number): string {\n try {\n const parsed = JSON.parse(text);\n if (parsed && typeof parsed.error === \"string\") return parsed.error;\n } catch {\n // not JSON — fall through\n }\n return text || `HTTP ${status}`;\n}\n","//! Filter builder producing the bare predicate-array wire shape.\n//\n// A `Filter` is AND-combined predicates; on the wire it is a plain array. Each\n// predicate is a *positive assertion about a present attribute* — an absent key\n// matches nothing, including the negative predicates (`ne`/`notIn`) and ranges.\n// Comparisons are same-type only (Int↔Int numeric, Str↔Str lexical, Bool↔Bool).\n\nimport type { AttrInput, Filter, Predicate, Value } from \"./types.js\";\nimport { encodeValue } from \"./values.js\";\n\n/**\n * Predicate constructors. Each accepts a plain JS value (auto-normalized) or an\n * explicit `v.*` {@link Value}. Combine results into a {@link Filter} array, or\n * use {@link f.and} for readability.\n */\nexport const f = {\n /** `attrs[key] === value`. */\n eq: (key: string, value: AttrInput): Predicate => ({\n Eq: [key, encodeValue(value)],\n }),\n /** `attrs[key]` is present and `!== value`. */\n ne: (key: string, value: AttrInput): Predicate => ({\n Ne: [key, encodeValue(value)],\n }),\n /** `attrs[key]` is a `Str` matching the glob pattern (`*`, `?`, `[..]`). */\n glob: (key: string, pattern: string): Predicate => ({ Glob: [key, pattern] }),\n /** `attrs[key]` equals one of `values`. */\n in: (key: string, values: AttrInput[]): Predicate => ({\n In: [key, values.map(encodeValue)],\n }),\n /** `attrs[key]` is present and equals none of `values`. */\n notIn: (key: string, values: AttrInput[]): Predicate => ({\n NotIn: [key, values.map(encodeValue)],\n }),\n /** `attrs[key] < value` (same-type, orderable). */\n lt: (key: string, value: AttrInput): Predicate => ({\n Lt: [key, encodeValue(value)],\n }),\n /** `attrs[key] <= value` (same-type, orderable). */\n le: (key: string, value: AttrInput): Predicate => ({\n Le: [key, encodeValue(value)],\n }),\n /** `attrs[key] > value` (same-type, orderable). */\n gt: (key: string, value: AttrInput): Predicate => ({\n Gt: [key, encodeValue(value)],\n }),\n /** `attrs[key] >= value` (same-type, orderable). */\n ge: (key: string, value: AttrInput): Predicate => ({\n Ge: [key, encodeValue(value)],\n }),\n /** Collect predicates into a {@link Filter} (purely sugar — they already AND). */\n and: (...preds: Predicate[]): Filter => preds,\n} as const;\n\n// Aliases for the comparison operators, for callers who prefer them.\nexport type { Filter, Predicate, Value };\n"],"mappings":"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","names":["f"]}