@npy/fetch 0.1.0 → 0.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/_internal/consts.cjs +4 -0
- package/_internal/consts.d.cts +3 -0
- package/_internal/consts.d.ts +3 -0
- package/_internal/consts.js +4 -0
- package/_internal/decode-stream-error.cjs +18 -0
- package/_internal/decode-stream-error.d.cts +11 -0
- package/_internal/decode-stream-error.d.ts +11 -0
- package/_internal/decode-stream-error.js +18 -0
- package/_internal/error-mapping.cjs +44 -0
- package/_internal/error-mapping.d.cts +15 -0
- package/_internal/error-mapping.d.ts +15 -0
- package/_internal/error-mapping.js +41 -0
- package/_internal/guards.cjs +23 -0
- package/_internal/guards.d.cts +15 -0
- package/_internal/guards.d.ts +15 -0
- package/_internal/guards.js +15 -0
- package/_internal/net.cjs +95 -0
- package/_internal/net.d.cts +11 -0
- package/_internal/net.d.ts +11 -0
- package/_internal/net.js +92 -0
- package/_internal/promises.cjs +18 -0
- package/_internal/promises.d.cts +1 -0
- package/_internal/promises.d.ts +1 -0
- package/_internal/promises.js +18 -0
- package/_internal/streams.cjs +37 -0
- package/_internal/streams.d.cts +21 -0
- package/_internal/streams.d.ts +21 -0
- package/_internal/streams.js +36 -0
- package/_internal/symbols.cjs +4 -0
- package/_internal/symbols.d.cts +1 -0
- package/_internal/symbols.d.ts +1 -0
- package/_internal/symbols.js +4 -0
- package/_virtual/_rolldown/runtime.cjs +23 -0
- package/agent-pool.cjs +96 -0
- package/agent-pool.d.cts +2 -0
- package/agent-pool.d.ts +2 -0
- package/agent-pool.js +95 -0
- package/agent.cjs +260 -0
- package/agent.d.cts +3 -0
- package/agent.d.ts +3 -0
- package/agent.js +259 -0
- package/body.cjs +105 -0
- package/body.d.cts +12 -0
- package/body.d.ts +12 -0
- package/body.js +102 -0
- package/dialers/index.d.cts +3 -0
- package/dialers/index.d.ts +3 -0
- package/dialers/proxy.cjs +56 -0
- package/dialers/proxy.d.cts +27 -0
- package/dialers/proxy.d.ts +27 -0
- package/dialers/proxy.js +55 -0
- package/dialers/tcp.cjs +92 -0
- package/dialers/tcp.d.cts +57 -0
- package/dialers/tcp.d.ts +57 -0
- package/dialers/tcp.js +89 -0
- package/encoding.cjs +114 -0
- package/encoding.d.cts +35 -0
- package/encoding.d.ts +35 -0
- package/encoding.js +110 -0
- package/errors.cjs +275 -0
- package/errors.d.cts +110 -0
- package/errors.d.ts +110 -0
- package/errors.js +259 -0
- package/fetch.cjs +353 -0
- package/fetch.d.cts +58 -0
- package/fetch.d.ts +58 -0
- package/fetch.js +350 -0
- package/http-client.cjs +75 -0
- package/http-client.d.cts +39 -0
- package/http-client.d.ts +39 -0
- package/http-client.js +75 -0
- package/index.cjs +49 -0
- package/index.d.cts +14 -0
- package/index.d.ts +14 -0
- package/index.js +11 -0
- package/io/_utils.cjs +56 -0
- package/io/_utils.d.cts +10 -0
- package/io/_utils.d.ts +10 -0
- package/io/_utils.js +51 -0
- package/io/buf-writer.cjs +149 -0
- package/io/buf-writer.d.cts +13 -0
- package/io/buf-writer.d.ts +13 -0
- package/io/buf-writer.js +148 -0
- package/io/io.cjs +199 -0
- package/io/io.d.cts +5 -0
- package/io/io.d.ts +5 -0
- package/io/io.js +198 -0
- package/io/readers.cjs +337 -0
- package/io/readers.d.cts +69 -0
- package/io/readers.d.ts +69 -0
- package/io/readers.js +333 -0
- package/io/writers.cjs +196 -0
- package/io/writers.d.cts +22 -0
- package/io/writers.d.ts +22 -0
- package/io/writers.js +195 -0
- package/package.json +23 -10
- package/types/agent.d.cts +72 -0
- package/types/agent.d.ts +72 -0
- package/types/dialer.d.cts +30 -0
- package/types/dialer.d.ts +30 -0
- package/types/index.d.cts +2 -0
- package/types/index.d.ts +2 -0
package/index.d.cts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type { FormDataPolyfill } from './_internal/guards';
|
|
2
|
+
export type { WithSignal } from './_internal/net';
|
|
3
|
+
export { connectTcp, connectTls, upgradeTls } from './_internal/net';
|
|
4
|
+
export * from './agent';
|
|
5
|
+
export * from './agent-pool';
|
|
6
|
+
export * from './body';
|
|
7
|
+
export * from './dialers';
|
|
8
|
+
export * from './encoding';
|
|
9
|
+
export * from './errors';
|
|
10
|
+
export * from './fetch';
|
|
11
|
+
export * from './http-client';
|
|
12
|
+
export type { LineReader, Readers } from './io/readers';
|
|
13
|
+
export type { Writers } from './io/writers';
|
|
14
|
+
export * from './types';
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type { FormDataPolyfill } from './_internal/guards';
|
|
2
|
+
export type { WithSignal } from './_internal/net';
|
|
3
|
+
export { connectTcp, connectTls, upgradeTls } from './_internal/net';
|
|
4
|
+
export * from './agent';
|
|
5
|
+
export * from './agent-pool';
|
|
6
|
+
export * from './body';
|
|
7
|
+
export * from './dialers';
|
|
8
|
+
export * from './encoding';
|
|
9
|
+
export * from './errors';
|
|
10
|
+
export * from './fetch';
|
|
11
|
+
export * from './http-client';
|
|
12
|
+
export type { LineReader, Readers } from './io/readers';
|
|
13
|
+
export type { Writers } from './io/writers';
|
|
14
|
+
export * from './types';
|
package/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { connectTcp, connectTls, upgradeTls } from "./_internal/net.js";
|
|
2
|
+
import { AgentBusyError, AgentClosedError, ConnectTimeoutError, ConnectionError, ErrorType, FetchError, FetchErrorCode, HttpStatusError, OriginMismatchError, RequestAbortedError, RequestWriteError, ResponseBodyError, ResponseDecodeError, ResponseHeaderError, UnsupportedAlpnProtocolError, UnsupportedMethodError, UnsupportedProtocolError } from "./errors.js";
|
|
3
|
+
import { createDecoders, createEncoders, decodeStream, encodeStream } from "./encoding.js";
|
|
4
|
+
import { extractBody, fromRequestBody, getFormDataLength } from "./body.js";
|
|
5
|
+
import { createAgent } from "./agent.js";
|
|
6
|
+
import { ProxyDialer } from "./dialers/proxy.js";
|
|
7
|
+
import { AutoDialer, TcpDialer, TlsDialer, resolveHostPort } from "./dialers/tcp.js";
|
|
8
|
+
import { createAgentPool } from "./agent-pool.js";
|
|
9
|
+
import { HttpClient } from "./http-client.js";
|
|
10
|
+
import fetch, { createFetch, normalizeHeaders } from "./fetch.js";
|
|
11
|
+
export { AgentBusyError, AgentClosedError, AutoDialer, ConnectTimeoutError, ConnectionError, ErrorType, FetchError, FetchErrorCode, HttpClient, HttpStatusError, OriginMismatchError, ProxyDialer, RequestAbortedError, RequestWriteError, ResponseBodyError, ResponseDecodeError, ResponseHeaderError, TcpDialer, TlsDialer, UnsupportedAlpnProtocolError, UnsupportedMethodError, UnsupportedProtocolError, connectTcp, connectTls, createAgent, createAgentPool, createDecoders, createEncoders, createFetch, decodeStream, encodeStream, extractBody, fetch, fromRequestBody, getFormDataLength, normalizeHeaders, resolveHostPort, upgradeTls };
|
package/io/_utils.cjs
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
let bytes = require("bytes");
|
|
3
|
+
bytes = require_runtime.__toESM(bytes);
|
|
4
|
+
//#region src/io/_utils.ts
|
|
5
|
+
function parseMaxBytes(value) {
|
|
6
|
+
if (value === void 0) return null;
|
|
7
|
+
if (typeof value === "number") {
|
|
8
|
+
if (!Number.isFinite(value) || value < 0) throw new Error(`invalid max size: ${String(value)}`);
|
|
9
|
+
return Math.floor(value);
|
|
10
|
+
}
|
|
11
|
+
const parsed = bytes.default.parse(value);
|
|
12
|
+
if (parsed == null || !Number.isFinite(parsed) || parsed < 0) throw new Error(`invalid max size: ${String(value)}`);
|
|
13
|
+
return parsed;
|
|
14
|
+
}
|
|
15
|
+
function splitTokens(v) {
|
|
16
|
+
if (!v) return [];
|
|
17
|
+
return v.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean);
|
|
18
|
+
}
|
|
19
|
+
function parseTransferEncoding(headers) {
|
|
20
|
+
const raw = headers.get("transfer-encoding");
|
|
21
|
+
const tks = splitTokens(raw);
|
|
22
|
+
if (tks.length === 0) return {
|
|
23
|
+
has: false,
|
|
24
|
+
chunked: false,
|
|
25
|
+
codings: []
|
|
26
|
+
};
|
|
27
|
+
const chunkedIdx = tks.lastIndexOf("chunked");
|
|
28
|
+
const hasChunked = chunkedIdx !== -1;
|
|
29
|
+
if (hasChunked && chunkedIdx !== tks.length - 1) throw new Error(`Invalid transfer-encoding order: ${raw ?? ""}`);
|
|
30
|
+
if (hasChunked && tks.indexOf("chunked") !== chunkedIdx) throw new Error(`Invalid transfer-encoding (duplicate chunked): ${raw ?? ""}`);
|
|
31
|
+
return {
|
|
32
|
+
has: true,
|
|
33
|
+
chunked: hasChunked,
|
|
34
|
+
codings: tks.filter((t) => t !== "chunked" && t !== "identity")
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function parseContentLength(headers) {
|
|
38
|
+
const raw = headers.get("content-length");
|
|
39
|
+
if (!raw) return null;
|
|
40
|
+
const parts = raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
41
|
+
if (parts.length === 0) return null;
|
|
42
|
+
let value = null;
|
|
43
|
+
for (const p of parts) {
|
|
44
|
+
if (!/^\d+$/.test(p)) throw new Error(`Invalid content-length: ${raw}`);
|
|
45
|
+
const n = Number.parseInt(p, 10);
|
|
46
|
+
if (!Number.isFinite(n) || n < 0) throw new Error(`Invalid content-length: ${raw}`);
|
|
47
|
+
if (value === null) value = n;
|
|
48
|
+
else if (value !== n) throw new Error(`Conflicting content-length values: ${raw}`);
|
|
49
|
+
}
|
|
50
|
+
return value;
|
|
51
|
+
}
|
|
52
|
+
//#endregion
|
|
53
|
+
exports.parseContentLength = parseContentLength;
|
|
54
|
+
exports.parseMaxBytes = parseMaxBytes;
|
|
55
|
+
exports.parseTransferEncoding = parseTransferEncoding;
|
|
56
|
+
exports.splitTokens = splitTokens;
|
package/io/_utils.d.cts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
type TransferEncodingInfo = {
|
|
2
|
+
has: boolean;
|
|
3
|
+
chunked: boolean;
|
|
4
|
+
codings: string[];
|
|
5
|
+
};
|
|
6
|
+
export declare function parseMaxBytes(value?: number | string): number | null;
|
|
7
|
+
export declare function splitTokens(v: string | null): string[];
|
|
8
|
+
export declare function parseTransferEncoding(headers: Headers): TransferEncodingInfo;
|
|
9
|
+
export declare function parseContentLength(headers: Headers): number | null;
|
|
10
|
+
export {};
|
package/io/_utils.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
type TransferEncodingInfo = {
|
|
2
|
+
has: boolean;
|
|
3
|
+
chunked: boolean;
|
|
4
|
+
codings: string[];
|
|
5
|
+
};
|
|
6
|
+
export declare function parseMaxBytes(value?: number | string): number | null;
|
|
7
|
+
export declare function splitTokens(v: string | null): string[];
|
|
8
|
+
export declare function parseTransferEncoding(headers: Headers): TransferEncodingInfo;
|
|
9
|
+
export declare function parseContentLength(headers: Headers): number | null;
|
|
10
|
+
export {};
|
package/io/_utils.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import bytes from "bytes";
|
|
2
|
+
//#region src/io/_utils.ts
|
|
3
|
+
function parseMaxBytes(value) {
|
|
4
|
+
if (value === void 0) return null;
|
|
5
|
+
if (typeof value === "number") {
|
|
6
|
+
if (!Number.isFinite(value) || value < 0) throw new Error(`invalid max size: ${String(value)}`);
|
|
7
|
+
return Math.floor(value);
|
|
8
|
+
}
|
|
9
|
+
const parsed = bytes.parse(value);
|
|
10
|
+
if (parsed == null || !Number.isFinite(parsed) || parsed < 0) throw new Error(`invalid max size: ${String(value)}`);
|
|
11
|
+
return parsed;
|
|
12
|
+
}
|
|
13
|
+
function splitTokens(v) {
|
|
14
|
+
if (!v) return [];
|
|
15
|
+
return v.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean);
|
|
16
|
+
}
|
|
17
|
+
function parseTransferEncoding(headers) {
|
|
18
|
+
const raw = headers.get("transfer-encoding");
|
|
19
|
+
const tks = splitTokens(raw);
|
|
20
|
+
if (tks.length === 0) return {
|
|
21
|
+
has: false,
|
|
22
|
+
chunked: false,
|
|
23
|
+
codings: []
|
|
24
|
+
};
|
|
25
|
+
const chunkedIdx = tks.lastIndexOf("chunked");
|
|
26
|
+
const hasChunked = chunkedIdx !== -1;
|
|
27
|
+
if (hasChunked && chunkedIdx !== tks.length - 1) throw new Error(`Invalid transfer-encoding order: ${raw ?? ""}`);
|
|
28
|
+
if (hasChunked && tks.indexOf("chunked") !== chunkedIdx) throw new Error(`Invalid transfer-encoding (duplicate chunked): ${raw ?? ""}`);
|
|
29
|
+
return {
|
|
30
|
+
has: true,
|
|
31
|
+
chunked: hasChunked,
|
|
32
|
+
codings: tks.filter((t) => t !== "chunked" && t !== "identity")
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function parseContentLength(headers) {
|
|
36
|
+
const raw = headers.get("content-length");
|
|
37
|
+
if (!raw) return null;
|
|
38
|
+
const parts = raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
39
|
+
if (parts.length === 0) return null;
|
|
40
|
+
let value = null;
|
|
41
|
+
for (const p of parts) {
|
|
42
|
+
if (!/^\d+$/.test(p)) throw new Error(`Invalid content-length: ${raw}`);
|
|
43
|
+
const n = Number.parseInt(p, 10);
|
|
44
|
+
if (!Number.isFinite(n) || n < 0) throw new Error(`Invalid content-length: ${raw}`);
|
|
45
|
+
if (value === null) value = n;
|
|
46
|
+
else if (value !== n) throw new Error(`Conflicting content-length values: ${raw}`);
|
|
47
|
+
}
|
|
48
|
+
return value;
|
|
49
|
+
}
|
|
50
|
+
//#endregion
|
|
51
|
+
export { parseContentLength, parseMaxBytes, parseTransferEncoding, splitTokens };
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
require("../_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
let _fuman_utils = require("@fuman/utils");
|
|
3
|
+
//#region src/io/buf-writer.ts
|
|
4
|
+
var DEFAULT_BUF_SIZE = 4096;
|
|
5
|
+
var MIN_BUF_SIZE = 16;
|
|
6
|
+
var BufWriter = class {
|
|
7
|
+
#buffer;
|
|
8
|
+
#writable;
|
|
9
|
+
#writePos = 0;
|
|
10
|
+
#error = null;
|
|
11
|
+
#pending = [];
|
|
12
|
+
#pendingBytes = 0;
|
|
13
|
+
#lastWrite = null;
|
|
14
|
+
constructor(writable, size = DEFAULT_BUF_SIZE) {
|
|
15
|
+
if (size < MIN_BUF_SIZE) size = MIN_BUF_SIZE;
|
|
16
|
+
this.#buffer = _fuman_utils.u8.alloc(size);
|
|
17
|
+
this.#writable = writable;
|
|
18
|
+
}
|
|
19
|
+
get bufferSize() {
|
|
20
|
+
return this.#buffer.byteLength;
|
|
21
|
+
}
|
|
22
|
+
get buffered() {
|
|
23
|
+
return this.#pendingBytes + this.#writePos;
|
|
24
|
+
}
|
|
25
|
+
get available() {
|
|
26
|
+
return this.#buffer.byteLength - this.#writePos;
|
|
27
|
+
}
|
|
28
|
+
reset(writable) {
|
|
29
|
+
this.#error = null;
|
|
30
|
+
this.#writePos = 0;
|
|
31
|
+
this.#pending.length = 0;
|
|
32
|
+
this.#pendingBytes = 0;
|
|
33
|
+
this.#lastWrite = null;
|
|
34
|
+
this.#writable = writable;
|
|
35
|
+
}
|
|
36
|
+
writeSync(bytes) {
|
|
37
|
+
if (this.#error) throw this.#error;
|
|
38
|
+
if (bytes < 0) throw new RangeError("bytes must be >= 0");
|
|
39
|
+
if (this.#lastWrite) throw new Error("disposeWriteSync must be called before the next writeSync");
|
|
40
|
+
if (bytes === 0) {
|
|
41
|
+
const empty = this.#buffer.subarray(this.#writePos, this.#writePos);
|
|
42
|
+
this.#lastWrite = {
|
|
43
|
+
buf: empty,
|
|
44
|
+
size: 0,
|
|
45
|
+
internal: true
|
|
46
|
+
};
|
|
47
|
+
return empty;
|
|
48
|
+
}
|
|
49
|
+
if (bytes > this.available && this.#writePos > 0) {
|
|
50
|
+
const copy = _fuman_utils.u8.allocWith(this.#buffer.subarray(0, this.#writePos));
|
|
51
|
+
this.#pending.push(copy);
|
|
52
|
+
this.#pendingBytes += copy.length;
|
|
53
|
+
this.#writePos = 0;
|
|
54
|
+
}
|
|
55
|
+
if (bytes <= this.#buffer.byteLength) {
|
|
56
|
+
if (bytes > this.available) {
|
|
57
|
+
const copy = _fuman_utils.u8.allocWith(this.#buffer.subarray(0, this.#writePos));
|
|
58
|
+
this.#pending.push(copy);
|
|
59
|
+
this.#pendingBytes += copy.length;
|
|
60
|
+
this.#writePos = 0;
|
|
61
|
+
}
|
|
62
|
+
const start = this.#writePos;
|
|
63
|
+
const end = start + bytes;
|
|
64
|
+
const slice = this.#buffer.subarray(start, end);
|
|
65
|
+
this.#writePos = end;
|
|
66
|
+
this.#lastWrite = {
|
|
67
|
+
buf: slice,
|
|
68
|
+
size: bytes,
|
|
69
|
+
internal: true
|
|
70
|
+
};
|
|
71
|
+
return slice;
|
|
72
|
+
}
|
|
73
|
+
const chunk = _fuman_utils.u8.alloc(bytes);
|
|
74
|
+
this.#lastWrite = {
|
|
75
|
+
buf: chunk,
|
|
76
|
+
size: bytes,
|
|
77
|
+
internal: false
|
|
78
|
+
};
|
|
79
|
+
return chunk;
|
|
80
|
+
}
|
|
81
|
+
disposeWriteSync(written) {
|
|
82
|
+
const lw = this.#lastWrite;
|
|
83
|
+
if (!lw) return;
|
|
84
|
+
const w = written ?? lw.size;
|
|
85
|
+
if (w < 0 || w > lw.size) throw new RangeError(`written out of bounds: ${w} (0..${lw.size})`);
|
|
86
|
+
if (lw.internal) this.#writePos -= lw.size - w;
|
|
87
|
+
else if (w > 0) {
|
|
88
|
+
const chunk = w === lw.size ? lw.buf : lw.buf.subarray(0, w);
|
|
89
|
+
this.#pending.push(chunk);
|
|
90
|
+
this.#pendingBytes += chunk.length;
|
|
91
|
+
}
|
|
92
|
+
this.#lastWrite = null;
|
|
93
|
+
}
|
|
94
|
+
async #flushPending() {
|
|
95
|
+
if (this.#error) throw this.#error;
|
|
96
|
+
if (this.#lastWrite) throw new Error("disposeWriteSync must be called before flush/write");
|
|
97
|
+
while (this.#pending.length > 0) {
|
|
98
|
+
const chunk = this.#pending[0];
|
|
99
|
+
try {
|
|
100
|
+
await this.#writable.write(chunk);
|
|
101
|
+
} catch (e) {
|
|
102
|
+
this.#error = e;
|
|
103
|
+
throw e;
|
|
104
|
+
}
|
|
105
|
+
this.#pending.shift();
|
|
106
|
+
this.#pendingBytes -= chunk.length;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
async flush() {
|
|
110
|
+
await this.#flushPending();
|
|
111
|
+
if (this.#error) throw this.#error;
|
|
112
|
+
if (this.#writePos === 0) return;
|
|
113
|
+
try {
|
|
114
|
+
await this.#writable.write(this.#buffer.subarray(0, this.#writePos));
|
|
115
|
+
} catch (e) {
|
|
116
|
+
this.#error = e;
|
|
117
|
+
throw e;
|
|
118
|
+
}
|
|
119
|
+
this.#writePos = 0;
|
|
120
|
+
}
|
|
121
|
+
async write(bytes) {
|
|
122
|
+
if (this.#error) throw this.#error;
|
|
123
|
+
if (!bytes.length) return;
|
|
124
|
+
await this.#flushPending();
|
|
125
|
+
if (this.#writePos === 0 && bytes.length >= this.#buffer.byteLength) {
|
|
126
|
+
try {
|
|
127
|
+
await this.#writable.write(bytes);
|
|
128
|
+
} catch (e) {
|
|
129
|
+
this.#error = e;
|
|
130
|
+
throw e;
|
|
131
|
+
}
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
let off = 0;
|
|
135
|
+
while (off < bytes.length) {
|
|
136
|
+
if (this.available === 0) {
|
|
137
|
+
await this.flush();
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
const toCopy = Math.min(this.available, bytes.length - off);
|
|
141
|
+
this.#buffer.set(bytes.subarray(off, off + toCopy), this.#writePos);
|
|
142
|
+
this.#writePos += toCopy;
|
|
143
|
+
off += toCopy;
|
|
144
|
+
if (this.#writePos === this.#buffer.byteLength) await this.flush();
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
//#endregion
|
|
149
|
+
exports.BufWriter = BufWriter;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ISyncWritable, IWritable } from '@fuman/io';
|
|
2
|
+
export declare class BufWriter implements IWritable, ISyncWritable {
|
|
3
|
+
#private;
|
|
4
|
+
constructor(writable: IWritable, size?: number);
|
|
5
|
+
get bufferSize(): number;
|
|
6
|
+
get buffered(): number;
|
|
7
|
+
get available(): number;
|
|
8
|
+
reset(writable: IWritable): void;
|
|
9
|
+
writeSync(bytes: number): Uint8Array;
|
|
10
|
+
disposeWriteSync(written?: number): void;
|
|
11
|
+
flush(): Promise<void>;
|
|
12
|
+
write(bytes: Uint8Array): Promise<void>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ISyncWritable, IWritable } from '@fuman/io';
|
|
2
|
+
export declare class BufWriter implements IWritable, ISyncWritable {
|
|
3
|
+
#private;
|
|
4
|
+
constructor(writable: IWritable, size?: number);
|
|
5
|
+
get bufferSize(): number;
|
|
6
|
+
get buffered(): number;
|
|
7
|
+
get available(): number;
|
|
8
|
+
reset(writable: IWritable): void;
|
|
9
|
+
writeSync(bytes: number): Uint8Array;
|
|
10
|
+
disposeWriteSync(written?: number): void;
|
|
11
|
+
flush(): Promise<void>;
|
|
12
|
+
write(bytes: Uint8Array): Promise<void>;
|
|
13
|
+
}
|
package/io/buf-writer.js
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { u8 } from "@fuman/utils";
|
|
2
|
+
//#region src/io/buf-writer.ts
|
|
3
|
+
var DEFAULT_BUF_SIZE = 4096;
|
|
4
|
+
var MIN_BUF_SIZE = 16;
|
|
5
|
+
var BufWriter = class {
|
|
6
|
+
#buffer;
|
|
7
|
+
#writable;
|
|
8
|
+
#writePos = 0;
|
|
9
|
+
#error = null;
|
|
10
|
+
#pending = [];
|
|
11
|
+
#pendingBytes = 0;
|
|
12
|
+
#lastWrite = null;
|
|
13
|
+
constructor(writable, size = DEFAULT_BUF_SIZE) {
|
|
14
|
+
if (size < MIN_BUF_SIZE) size = MIN_BUF_SIZE;
|
|
15
|
+
this.#buffer = u8.alloc(size);
|
|
16
|
+
this.#writable = writable;
|
|
17
|
+
}
|
|
18
|
+
get bufferSize() {
|
|
19
|
+
return this.#buffer.byteLength;
|
|
20
|
+
}
|
|
21
|
+
get buffered() {
|
|
22
|
+
return this.#pendingBytes + this.#writePos;
|
|
23
|
+
}
|
|
24
|
+
get available() {
|
|
25
|
+
return this.#buffer.byteLength - this.#writePos;
|
|
26
|
+
}
|
|
27
|
+
reset(writable) {
|
|
28
|
+
this.#error = null;
|
|
29
|
+
this.#writePos = 0;
|
|
30
|
+
this.#pending.length = 0;
|
|
31
|
+
this.#pendingBytes = 0;
|
|
32
|
+
this.#lastWrite = null;
|
|
33
|
+
this.#writable = writable;
|
|
34
|
+
}
|
|
35
|
+
writeSync(bytes) {
|
|
36
|
+
if (this.#error) throw this.#error;
|
|
37
|
+
if (bytes < 0) throw new RangeError("bytes must be >= 0");
|
|
38
|
+
if (this.#lastWrite) throw new Error("disposeWriteSync must be called before the next writeSync");
|
|
39
|
+
if (bytes === 0) {
|
|
40
|
+
const empty = this.#buffer.subarray(this.#writePos, this.#writePos);
|
|
41
|
+
this.#lastWrite = {
|
|
42
|
+
buf: empty,
|
|
43
|
+
size: 0,
|
|
44
|
+
internal: true
|
|
45
|
+
};
|
|
46
|
+
return empty;
|
|
47
|
+
}
|
|
48
|
+
if (bytes > this.available && this.#writePos > 0) {
|
|
49
|
+
const copy = u8.allocWith(this.#buffer.subarray(0, this.#writePos));
|
|
50
|
+
this.#pending.push(copy);
|
|
51
|
+
this.#pendingBytes += copy.length;
|
|
52
|
+
this.#writePos = 0;
|
|
53
|
+
}
|
|
54
|
+
if (bytes <= this.#buffer.byteLength) {
|
|
55
|
+
if (bytes > this.available) {
|
|
56
|
+
const copy = u8.allocWith(this.#buffer.subarray(0, this.#writePos));
|
|
57
|
+
this.#pending.push(copy);
|
|
58
|
+
this.#pendingBytes += copy.length;
|
|
59
|
+
this.#writePos = 0;
|
|
60
|
+
}
|
|
61
|
+
const start = this.#writePos;
|
|
62
|
+
const end = start + bytes;
|
|
63
|
+
const slice = this.#buffer.subarray(start, end);
|
|
64
|
+
this.#writePos = end;
|
|
65
|
+
this.#lastWrite = {
|
|
66
|
+
buf: slice,
|
|
67
|
+
size: bytes,
|
|
68
|
+
internal: true
|
|
69
|
+
};
|
|
70
|
+
return slice;
|
|
71
|
+
}
|
|
72
|
+
const chunk = u8.alloc(bytes);
|
|
73
|
+
this.#lastWrite = {
|
|
74
|
+
buf: chunk,
|
|
75
|
+
size: bytes,
|
|
76
|
+
internal: false
|
|
77
|
+
};
|
|
78
|
+
return chunk;
|
|
79
|
+
}
|
|
80
|
+
disposeWriteSync(written) {
|
|
81
|
+
const lw = this.#lastWrite;
|
|
82
|
+
if (!lw) return;
|
|
83
|
+
const w = written ?? lw.size;
|
|
84
|
+
if (w < 0 || w > lw.size) throw new RangeError(`written out of bounds: ${w} (0..${lw.size})`);
|
|
85
|
+
if (lw.internal) this.#writePos -= lw.size - w;
|
|
86
|
+
else if (w > 0) {
|
|
87
|
+
const chunk = w === lw.size ? lw.buf : lw.buf.subarray(0, w);
|
|
88
|
+
this.#pending.push(chunk);
|
|
89
|
+
this.#pendingBytes += chunk.length;
|
|
90
|
+
}
|
|
91
|
+
this.#lastWrite = null;
|
|
92
|
+
}
|
|
93
|
+
async #flushPending() {
|
|
94
|
+
if (this.#error) throw this.#error;
|
|
95
|
+
if (this.#lastWrite) throw new Error("disposeWriteSync must be called before flush/write");
|
|
96
|
+
while (this.#pending.length > 0) {
|
|
97
|
+
const chunk = this.#pending[0];
|
|
98
|
+
try {
|
|
99
|
+
await this.#writable.write(chunk);
|
|
100
|
+
} catch (e) {
|
|
101
|
+
this.#error = e;
|
|
102
|
+
throw e;
|
|
103
|
+
}
|
|
104
|
+
this.#pending.shift();
|
|
105
|
+
this.#pendingBytes -= chunk.length;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
async flush() {
|
|
109
|
+
await this.#flushPending();
|
|
110
|
+
if (this.#error) throw this.#error;
|
|
111
|
+
if (this.#writePos === 0) return;
|
|
112
|
+
try {
|
|
113
|
+
await this.#writable.write(this.#buffer.subarray(0, this.#writePos));
|
|
114
|
+
} catch (e) {
|
|
115
|
+
this.#error = e;
|
|
116
|
+
throw e;
|
|
117
|
+
}
|
|
118
|
+
this.#writePos = 0;
|
|
119
|
+
}
|
|
120
|
+
async write(bytes) {
|
|
121
|
+
if (this.#error) throw this.#error;
|
|
122
|
+
if (!bytes.length) return;
|
|
123
|
+
await this.#flushPending();
|
|
124
|
+
if (this.#writePos === 0 && bytes.length >= this.#buffer.byteLength) {
|
|
125
|
+
try {
|
|
126
|
+
await this.#writable.write(bytes);
|
|
127
|
+
} catch (e) {
|
|
128
|
+
this.#error = e;
|
|
129
|
+
throw e;
|
|
130
|
+
}
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
let off = 0;
|
|
134
|
+
while (off < bytes.length) {
|
|
135
|
+
if (this.available === 0) {
|
|
136
|
+
await this.flush();
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
const toCopy = Math.min(this.available, bytes.length - off);
|
|
140
|
+
this.#buffer.set(bytes.subarray(off, off + toCopy), this.#writePos);
|
|
141
|
+
this.#writePos += toCopy;
|
|
142
|
+
off += toCopy;
|
|
143
|
+
if (this.#writePos === this.#buffer.byteLength) await this.flush();
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
//#endregion
|
|
148
|
+
export { BufWriter };
|