@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 +1 -1
- package/dist/index.cjs +40 -5
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +40 -5
- package/package.json +1 -1
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.
|
|
595
|
+
this.base = "http://ndb";
|
|
596
|
+
this.socketPath = decodeURIComponent(url.pathname);
|
|
592
597
|
} else if (url.protocol === "tcp:" && url.host !== "") {
|
|
593
|
-
this.
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
553
|
+
this.base = "http://ndb";
|
|
554
|
+
this.socketPath = decodeURIComponent(url.pathname);
|
|
550
555
|
} else if (url.protocol === "tcp:" && url.host !== "") {
|
|
551
|
-
this.
|
|
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
|
|
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
|
|
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
|
|
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
|
}
|