@thadeu/ndb 1.0.0 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  TypeScript client for [ndb](https://github.com/thadeu/ndb), an NDJSON query daemon. Typed collections, a chainable query builder, aggregate, watch and blobs. The client builds JSON. The daemon decides what every query means.
6
6
 
7
- Node 24 or newer. ESM and CJS. One runtime dependency (`undici`).
7
+ Node 24 or newer, or Bun. ESM and CJS. One runtime dependency (`undici`, used on Node; Bun uses its `fetch`).
8
8
 
9
9
  Docs: [client guide](https://github.com/thadeu/ndb/blob/main/docs/dev/clients.md#typescript-package-thadeundb), [query language](https://github.com/thadeu/ndb/blob/main/docs/dev/query.md), [HTTP API](https://github.com/thadeu/ndb/blob/main/docs/dev/http.md).
10
10
 
package/dist/index.cjs CHANGED
@@ -542,6 +542,7 @@ async function orNull(p) {
542
542
  }
543
543
 
544
544
  // src/transport.ts
545
+ var import_node_stream = require("stream");
545
546
  var import_undici = require("undici");
546
547
 
547
548
  // src/ndjson.ts
@@ -577,8 +578,11 @@ var retryBaseMs = 50;
577
578
  var retryCapMs = 1e3;
578
579
  var defaultReadyTimeoutMs = 1e4;
579
580
  var readOps = /\/(_search|_count|_aggregate)$/;
581
+ var onBun = typeof globalThis.Bun !== "undefined";
580
582
  var Transport = class {
581
583
  client;
584
+ base;
585
+ socketPath;
582
586
  token;
583
587
  readyTimeout;
584
588
  // addr: unix:///path or tcp://host:port. Default: env NDB_URL.
@@ -588,12 +592,16 @@ var Transport = class {
588
592
  this.token = opts.token || process.env.NDB_TOKEN || void 0;
589
593
  this.readyTimeout = opts.readyTimeout ?? defaultReadyTimeoutMs;
590
594
  if (url.protocol === "unix:" && url.pathname !== "") {
591
- this.client = new import_undici.Pool("http://ndb", { socketPath: decodeURIComponent(url.pathname), connections: 16 });
595
+ this.base = "http://ndb";
596
+ this.socketPath = decodeURIComponent(url.pathname);
592
597
  } else if (url.protocol === "tcp:" && url.host !== "") {
593
- this.client = new import_undici.Pool(`http://${url.host}`, { connections: 16 });
598
+ this.base = `http://${url.host}`;
594
599
  } else {
595
600
  throw new TypeError(`ndb: address "${target}" must be unix:///path or tcp://host:port`);
596
601
  }
602
+ if (!onBun) {
603
+ this.client = new import_undici.Pool(this.base, { socketPath: this.socketPath, connections: 16 });
604
+ }
597
605
  }
598
606
  async stream(method, path, body, signal, contentType) {
599
607
  const res = await this.withReadyRetry(method, path, signal, async () => {
@@ -618,7 +626,7 @@ var Transport = class {
618
626
  return text.length > 0 ? JSON.parse(text.join("")) : void 0;
619
627
  }
620
628
  close() {
621
- return this.client.close();
629
+ return this.client?.close() ?? Promise.resolve();
622
630
  }
623
631
  // Runs fn again while the daemon is not ready, with exponential backoff and
624
632
  // jitter. It repeats only when the request certainly was not processed
@@ -658,16 +666,43 @@ var Transport = class {
658
666
  headers: this.headers(contentType),
659
667
  body: body === void 0 ? void 0 : raw ? body : JSON.stringify(body)
660
668
  };
669
+ const client = this.client;
670
+ if (!client) {
671
+ return this.fetchSend(opts);
672
+ }
661
673
  try {
662
- return await this.client.request(opts);
674
+ return await client.request(opts);
663
675
  } catch (err) {
664
676
  if (!isIdempotent(method, path) || !staleSocket.has(err.code ?? "") || signal?.aborted) {
665
677
  throw err;
666
678
  }
667
- return await this.client.request(opts);
679
+ return await client.request(opts);
668
680
  }
669
681
  }
682
+ // fetchSend is send with fetch (Bun). The answer has the shape of an undici
683
+ // answer: the rest of the SDK reads it the same way.
684
+ async fetchSend(opts) {
685
+ const init = { method: opts.method, headers: opts.headers, body: opts.body, signal: opts.signal, unix: this.socketPath };
686
+ const res = await fetch(this.base + opts.path, init);
687
+ const headers = {};
688
+ res.headers.forEach((value, name) => {
689
+ headers[name] = value;
690
+ });
691
+ return { statusCode: res.status, headers, body: readableOf(res) };
692
+ }
670
693
  };
694
+ function readableOf(res) {
695
+ const stream = res.body ? import_node_stream.Readable.fromWeb(res.body) : import_node_stream.Readable.from([]);
696
+ return Object.assign(stream, {
697
+ arrayBuffer: async () => {
698
+ const bytes = Buffer.concat(await stream.toArray());
699
+ return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
700
+ },
701
+ dump: async () => {
702
+ stream.resume();
703
+ }
704
+ });
705
+ }
671
706
  function isIdempotent(method, path) {
672
707
  return method === "GET" || readOps.test(path.split("?")[0] ?? path);
673
708
  }
package/dist/index.d.cts CHANGED
@@ -263,6 +263,8 @@ interface ConnectOptions {
263
263
  }
264
264
  declare class Transport {
265
265
  private readonly client;
266
+ private readonly base;
267
+ private readonly socketPath;
266
268
  private readonly token;
267
269
  private readonly readyTimeout;
268
270
  constructor(addr?: string, opts?: ConnectOptions);
@@ -272,6 +274,7 @@ declare class Transport {
272
274
  private withReadyRetry;
273
275
  private headers;
274
276
  private send;
277
+ private fetchSend;
275
278
  }
276
279
 
277
280
  declare class Ndb {
package/dist/index.d.ts CHANGED
@@ -263,6 +263,8 @@ interface ConnectOptions {
263
263
  }
264
264
  declare class Transport {
265
265
  private readonly client;
266
+ private readonly base;
267
+ private readonly socketPath;
266
268
  private readonly token;
267
269
  private readonly readyTimeout;
268
270
  constructor(addr?: string, opts?: ConnectOptions);
@@ -272,6 +274,7 @@ declare class Transport {
272
274
  private withReadyRetry;
273
275
  private headers;
274
276
  private send;
277
+ private fetchSend;
275
278
  }
276
279
 
277
280
  declare class Ndb {
package/dist/index.js CHANGED
@@ -500,6 +500,7 @@ async function orNull(p) {
500
500
  }
501
501
 
502
502
  // src/transport.ts
503
+ import { Readable } from "stream";
503
504
  import { Pool } from "undici";
504
505
 
505
506
  // src/ndjson.ts
@@ -535,8 +536,11 @@ var retryBaseMs = 50;
535
536
  var retryCapMs = 1e3;
536
537
  var defaultReadyTimeoutMs = 1e4;
537
538
  var readOps = /\/(_search|_count|_aggregate)$/;
539
+ var onBun = typeof globalThis.Bun !== "undefined";
538
540
  var Transport = class {
539
541
  client;
542
+ base;
543
+ socketPath;
540
544
  token;
541
545
  readyTimeout;
542
546
  // addr: unix:///path or tcp://host:port. Default: env NDB_URL.
@@ -546,12 +550,16 @@ var Transport = class {
546
550
  this.token = opts.token || process.env.NDB_TOKEN || void 0;
547
551
  this.readyTimeout = opts.readyTimeout ?? defaultReadyTimeoutMs;
548
552
  if (url.protocol === "unix:" && url.pathname !== "") {
549
- this.client = new Pool("http://ndb", { socketPath: decodeURIComponent(url.pathname), connections: 16 });
553
+ this.base = "http://ndb";
554
+ this.socketPath = decodeURIComponent(url.pathname);
550
555
  } else if (url.protocol === "tcp:" && url.host !== "") {
551
- this.client = new Pool(`http://${url.host}`, { connections: 16 });
556
+ this.base = `http://${url.host}`;
552
557
  } else {
553
558
  throw new TypeError(`ndb: address "${target}" must be unix:///path or tcp://host:port`);
554
559
  }
560
+ if (!onBun) {
561
+ this.client = new Pool(this.base, { socketPath: this.socketPath, connections: 16 });
562
+ }
555
563
  }
556
564
  async stream(method, path, body, signal, contentType) {
557
565
  const res = await this.withReadyRetry(method, path, signal, async () => {
@@ -576,7 +584,7 @@ var Transport = class {
576
584
  return text.length > 0 ? JSON.parse(text.join("")) : void 0;
577
585
  }
578
586
  close() {
579
- return this.client.close();
587
+ return this.client?.close() ?? Promise.resolve();
580
588
  }
581
589
  // Runs fn again while the daemon is not ready, with exponential backoff and
582
590
  // jitter. It repeats only when the request certainly was not processed
@@ -616,16 +624,43 @@ var Transport = class {
616
624
  headers: this.headers(contentType),
617
625
  body: body === void 0 ? void 0 : raw ? body : JSON.stringify(body)
618
626
  };
627
+ const client = this.client;
628
+ if (!client) {
629
+ return this.fetchSend(opts);
630
+ }
619
631
  try {
620
- return await this.client.request(opts);
632
+ return await client.request(opts);
621
633
  } catch (err) {
622
634
  if (!isIdempotent(method, path) || !staleSocket.has(err.code ?? "") || signal?.aborted) {
623
635
  throw err;
624
636
  }
625
- return await this.client.request(opts);
637
+ return await client.request(opts);
626
638
  }
627
639
  }
640
+ // fetchSend is send with fetch (Bun). The answer has the shape of an undici
641
+ // answer: the rest of the SDK reads it the same way.
642
+ async fetchSend(opts) {
643
+ const init = { method: opts.method, headers: opts.headers, body: opts.body, signal: opts.signal, unix: this.socketPath };
644
+ const res = await fetch(this.base + opts.path, init);
645
+ const headers = {};
646
+ res.headers.forEach((value, name) => {
647
+ headers[name] = value;
648
+ });
649
+ return { statusCode: res.status, headers, body: readableOf(res) };
650
+ }
628
651
  };
652
+ function readableOf(res) {
653
+ const stream = res.body ? Readable.fromWeb(res.body) : Readable.from([]);
654
+ return Object.assign(stream, {
655
+ arrayBuffer: async () => {
656
+ const bytes = Buffer.concat(await stream.toArray());
657
+ return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
658
+ },
659
+ dump: async () => {
660
+ stream.resume();
661
+ }
662
+ });
663
+ }
629
664
  function isIdempotent(method, path) {
630
665
  return method === "GET" || readOps.test(path.split("?")[0] ?? path);
631
666
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thadeu/ndb",
3
- "version": "1.0.0",
3
+ "version": "1.1.2",
4
4
  "description": "TypeScript client for the ndb daemon",
5
5
  "keywords": [
6
6
  "ndb",