Add comprehensive installation and setup documentation

- Add GETTING_STARTED.md with quick start guide and development modes
- Add INSTALL.sh automated installation script
- Add INSTALLATION_CHECKLIST.md, INSTALLATION_SUCCESS.md, and INSTALLATION_SUMMARY.md
- Add QUICK_REFERENCE.md for common commands
- Add SETUP_GUIDE.md with detailed setup instructions
- Update README.md with improved project overview
- Add did-wallet app dependencies and node_modules
This commit is contained in:
Dorian
2026-01-27 17:18:21 +00:00
parent a81f655133
commit 0d073fa89e
22658 changed files with 4494151 additions and 6 deletions
+201
View File
@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
{"type": "commonjs"}
+3
View File
@@ -0,0 +1,3 @@
import TTLCache from '@isaacs/ttlcache';
export { TTLCache as TtlCache };
//# sourceMappingURL=cache.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/cache.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,kBAAkB,CAAC;AACxC,OAAO,EAAE,QAAQ,IAAI,QAAQ,EAAE,CAAC"}
+414
View File
@@ -0,0 +1,414 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __asyncValues = (this && this.__asyncValues) || function (o) {
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
var m = o[Symbol.asyncIterator], i;
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
};
import { base32z } from 'multiformats/bases/base32';
import { base58btc } from 'multiformats/bases/base58';
import { base64url } from 'multiformats/bases/base64';
import { isAsyncIterable, isArrayBufferSlice, universalTypeOf } from './type-utils.js';
const textEncoder = new TextEncoder();
const textDecoder = new TextDecoder();
export class Convert {
constructor(data, format) {
this.data = data;
this.format = format;
}
static arrayBuffer(data) {
return new Convert(data, 'ArrayBuffer');
}
static asyncIterable(data) {
if (!isAsyncIterable(data)) {
throw new TypeError('Input must be of type AsyncIterable.');
}
return new Convert(data, 'AsyncIterable');
}
static base32Z(data) {
return new Convert(data, 'Base32Z');
}
static base58Btc(data) {
return new Convert(data, 'Base58Btc');
}
static base64Url(data) {
return new Convert(data, 'Base64Url');
}
/**
* Reference:
* The BufferSource type is a TypeScript type that represents an ArrayBuffer
* or one of the ArrayBufferView types, such a TypedArray (e.g., Uint8Array)
* or a DataView.
*/
static bufferSource(data) {
return new Convert(data, 'BufferSource');
}
static hex(data) {
if (typeof data !== 'string') {
throw new TypeError('Hex input must be a string.');
}
if (data.length % 2 !== 0) {
throw new TypeError('Hex input must have an even number of characters.');
}
return new Convert(data, 'Hex');
}
static multibase(data) {
return new Convert(data, 'Multibase');
}
static object(data) {
return new Convert(data, 'Object');
}
static string(data) {
return new Convert(data, 'String');
}
static uint8Array(data) {
return new Convert(data, 'Uint8Array');
}
toArrayBuffer() {
switch (this.format) {
case 'Base58Btc': {
return base58btc.baseDecode(this.data).buffer;
}
case 'Base64Url': {
return base64url.baseDecode(this.data).buffer;
}
case 'BufferSource': {
const dataType = universalTypeOf(this.data);
if (dataType === 'ArrayBuffer') {
// Data is already an ArrayBuffer, No conversion is necessary.
return this.data;
}
else if (ArrayBuffer.isView(this.data)) {
// Data is a DataView or a different TypedArray (e.g., Uint16Array).
if (isArrayBufferSlice(this.data)) {
// Data is a slice of an ArrayBuffer. Return a new ArrayBuffer or ArrayBufferView of the same slice.
return this.data.buffer.slice(this.data.byteOffset, this.data.byteOffset + this.data.byteLength);
}
else {
// Data is a whole ArrayBuffer viewed as a different TypedArray or DataView. Return the whole ArrayBuffer.
return this.data.buffer;
}
}
else {
throw new TypeError(`${this.format} value is not of type: ArrayBuffer, DataView, or TypedArray.`);
}
}
case 'Hex': {
return this.toUint8Array().buffer;
}
case 'String': {
return this.toUint8Array().buffer;
}
case 'Uint8Array': {
return this.data.buffer;
}
default:
throw new TypeError(`Conversion from ${this.format} to ArrayBuffer is not supported.`);
}
}
toArrayBufferAsync() {
return __awaiter(this, void 0, void 0, function* () {
switch (this.format) {
case 'AsyncIterable': {
const blob = yield this.toBlobAsync();
return yield blob.arrayBuffer();
}
default:
throw new TypeError(`Asynchronous conversion from ${this.format} to ArrayBuffer is not supported.`);
}
});
}
toBase32Z() {
switch (this.format) {
case 'Uint8Array': {
return base32z.baseEncode(this.data);
}
default:
throw new TypeError(`Conversion from ${this.format} to Base64Z is not supported.`);
}
}
toBase58Btc() {
switch (this.format) {
case 'ArrayBuffer': {
const u8a = new Uint8Array(this.data);
return base58btc.baseEncode(u8a);
}
case 'Multibase': {
return this.data.substring(1);
}
case 'Uint8Array': {
return base58btc.baseEncode(this.data);
}
default:
throw new TypeError(`Conversion from ${this.format} to Base58Btc is not supported.`);
}
}
toBase64Url() {
switch (this.format) {
case 'ArrayBuffer': {
const u8a = new Uint8Array(this.data);
return base64url.baseEncode(u8a);
}
case 'BufferSource': {
const u8a = this.toUint8Array();
return base64url.baseEncode(u8a);
}
case 'Object': {
const string = JSON.stringify(this.data);
const u8a = textEncoder.encode(string);
return base64url.baseEncode(u8a);
}
case 'String': {
const u8a = textEncoder.encode(this.data);
return base64url.baseEncode(u8a);
}
case 'Uint8Array': {
return base64url.baseEncode(this.data);
}
default:
throw new TypeError(`Conversion from ${this.format} to Base64Url is not supported.`);
}
}
toBlobAsync() {
var _a, e_1, _b, _c;
return __awaiter(this, void 0, void 0, function* () {
switch (this.format) {
case 'AsyncIterable': {
// Initialize an array to hold the chunks from the AsyncIterable.
const chunks = [];
try {
// Asynchronously iterate over each chunk in the AsyncIterable.
for (var _d = true, _e = __asyncValues(this.data), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
_c = _f.value;
_d = false;
const chunk = _c;
// Append each chunk to the chunks array. These chunks can be of any type, typically binary data or text.
chunks.push(chunk);
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
}
finally { if (e_1) throw e_1.error; }
}
// Create a new Blob from the aggregated chunks.
// The Blob constructor combines these chunks into a single Blob object.
const blob = new Blob(chunks);
return blob;
}
default:
throw new TypeError(`Asynchronous conversion from ${this.format} to Blob is not supported.`);
}
});
}
toHex() {
// pre-calculating Hex values improves runtime by 6-10x.
const hexes = Array.from({ length: 256 }, (v, i) => i.toString(16).padStart(2, '0'));
switch (this.format) {
case 'ArrayBuffer': {
const u8a = this.toUint8Array();
return Convert.uint8Array(u8a).toHex();
}
case 'Base64Url': {
const u8a = this.toUint8Array();
return Convert.uint8Array(u8a).toHex();
}
case 'Uint8Array': {
let hex = '';
for (let i = 0; i < this.data.length; i++) {
hex += hexes[this.data[i]];
}
return hex;
}
default:
throw new TypeError(`Conversion from ${this.format} to Hex is not supported.`);
}
}
toMultibase() {
switch (this.format) {
case 'Base58Btc': {
return `z${this.data}`;
}
default:
throw new TypeError(`Conversion from ${this.format} to Multibase is not supported.`);
}
}
toObject() {
switch (this.format) {
case 'Base64Url': {
const u8a = base64url.baseDecode(this.data);
const text = textDecoder.decode(u8a);
return JSON.parse(text);
}
case 'String': {
return JSON.parse(this.data);
}
case 'Uint8Array': {
const text = textDecoder.decode(this.data);
return JSON.parse(text);
}
default:
throw new TypeError(`Conversion from ${this.format} to Object is not supported.`);
}
}
toObjectAsync() {
return __awaiter(this, void 0, void 0, function* () {
switch (this.format) {
case 'AsyncIterable': {
// Convert the AsyncIterable to a String.
const text = yield this.toStringAsync();
// Parse the string as JSON. This step assumes that the string represents a valid JSON structure.
// JSON.parse() will convert the string into a corresponding JavaScript object.
const json = JSON.parse(text);
// Return the parsed JavaScript object. The type of this object will depend on the structure
// of the JSON in the stream. It could be an object, array, string, number, etc.
return json;
}
default:
throw new TypeError(`Asynchronous conversion from ${this.format} to Object is not supported.`);
}
});
}
toString() {
switch (this.format) {
case 'ArrayBuffer': {
return textDecoder.decode(this.data);
}
case 'Base64Url': {
const u8a = base64url.baseDecode(this.data);
return textDecoder.decode(u8a);
}
case 'Object': {
return JSON.stringify(this.data);
}
case 'Uint8Array': {
return textDecoder.decode(this.data);
}
default:
throw new TypeError(`Conversion from ${this.format} to String is not supported.`);
}
}
toStringAsync() {
var _a, e_2, _b, _c;
return __awaiter(this, void 0, void 0, function* () {
switch (this.format) {
case 'AsyncIterable': {
// Initialize an empty string to accumulate the decoded text.
let str = '';
try {
// Iterate over the chunks from the AsyncIterable.
for (var _d = true, _e = __asyncValues(this.data), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
_c = _f.value;
_d = false;
const chunk = _c;
// If the chunk is already a string, concatenate it directly.
if (typeof chunk === 'string')
str += chunk;
else
// If the chunk is a Uint8Array or similar, use the decoder to convert it to a string.
// The `stream: true` option lets the decoder handle multi-byte characters spanning
// multiple chunks.
str += textDecoder.decode(chunk, { stream: true });
}
}
catch (e_2_1) { e_2 = { error: e_2_1 }; }
finally {
try {
if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
}
finally { if (e_2) throw e_2.error; }
}
// Finalize the decoding process to handle any remaining bytes and signal the end of the stream.
// The `stream: false` option flushes the decoder's internal state.
str += textDecoder.decode(undefined, { stream: false });
// Return the accumulated string.
return str;
}
default:
throw new TypeError(`Asynchronous conversion from ${this.format} to String is not supported.`);
}
});
}
toUint8Array() {
switch (this.format) {
case 'ArrayBuffer': {
// Çreate Uint8Array as a view on the ArrayBuffer.
// Note: The Uint8Array shares the same memory as the ArrayBuffer, so this operation is very efficient.
return new Uint8Array(this.data);
}
case 'Base32Z': {
return base32z.baseDecode(this.data);
}
case 'Base58Btc': {
return base58btc.baseDecode(this.data);
}
case 'Base64Url': {
return base64url.baseDecode(this.data);
}
case 'BufferSource': {
const dataType = universalTypeOf(this.data);
if (dataType === 'Uint8Array') {
// Data is already a Uint8Array. No conversion is necessary.
// Note: Uint8Array is a type of BufferSource.
return this.data;
}
else if (dataType === 'ArrayBuffer') {
// Data is an ArrayBuffer, create Uint8Array as a view on the ArrayBuffer.
// Note: The Uint8Array shares the same memory as the ArrayBuffer, so this operation is very efficient.
return new Uint8Array(this.data);
}
else if (ArrayBuffer.isView(this.data)) {
// Data is a DataView or a different TypedArray (e.g., Uint16Array).
return new Uint8Array(this.data.buffer, this.data.byteOffset, this.data.byteLength);
}
else {
throw new TypeError(`${this.format} value is not of type: ArrayBuffer, DataView, or TypedArray.`);
}
}
case 'Hex': {
const u8a = new Uint8Array(this.data.length / 2);
for (let i = 0; i < this.data.length; i += 2) {
const byteValue = parseInt(this.data.substring(i, i + 2), 16);
if (isNaN(byteValue)) {
throw new TypeError('Input is not a valid hexadecimal string.');
}
u8a[i / 2] = byteValue;
}
return u8a;
}
case 'Object': {
const string = JSON.stringify(this.data);
return textEncoder.encode(string);
}
case 'String': {
return textEncoder.encode(this.data);
}
default:
throw new TypeError(`Conversion from ${this.format} to Uint8Array is not supported.`);
}
}
toUint8ArrayAsync() {
return __awaiter(this, void 0, void 0, function* () {
switch (this.format) {
case 'AsyncIterable': {
const arrayBuffer = yield this.toArrayBufferAsync();
return new Uint8Array(arrayBuffer);
}
default:
throw new TypeError(`Asynchronous conversion from ${this.format} to Uint8Array is not supported.`);
}
});
}
}
//# sourceMappingURL=convert.js.map
File diff suppressed because one or more lines are too long
+9
View File
@@ -0,0 +1,9 @@
export * from './cache.js';
export * from './convert.js';
export * from './multicodec.js';
export * from './object.js';
export * from './stores.js';
export * from './stream.js';
export * from './stream-node.js';
export * from './type-utils.js';
//# sourceMappingURL=index.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAEA,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC"}
+136
View File
@@ -0,0 +1,136 @@
import { varint } from 'multiformats';
/**
* The `Multicodec` class provides an interface to prepend binary data
* with a prefix that identifies the data that follows.
* https://github.com/multiformats/multicodec/blob/master/table.csv
*
* Multicodec is a self-describing multiformat, it wraps other formats with
* a tiny bit of self-description. A multicodec identifier is a
* varint (variable integer) that indicates the format of the data.
*
* The canonical table of multicodecs can be access at the following URL:
* https://github.com/multiformats/multicodec/blob/master/table.csv
*
* Example usage:
*
* ```ts
* Multicodec.registerCodec({ code: 0xed, name: 'ed25519-pub' });
* const prefixedData = Multicodec.addPrefix({ code: 0xed, data: new Uint8Array(32) });
* ```
*/
export class Multicodec {
/**
* Adds a multicodec prefix to input data.
*
* @param options - The options for adding a prefix.
* @param options.code - The codec code. Either the code or name must be provided.
* @param options.name - The codec name. Either the code or name must be provided.
* @param options.data - The data to be prefixed.
* @returns The data with the added prefix as a Uint8Array.
*/
static addPrefix(options) {
var _a;
let { code, data, name } = options;
if (!(name ? !code : code)) {
throw new Error(`Either 'name' or 'code' must be defined, but not both.`);
}
// If code was given, confirm it exists, or lookup code by name.
code = Multicodec.codeToName.has(code) ? code : Multicodec.nameToCode.get(name);
// Throw error if a registered Codec wasn't found.
if (code === undefined) {
throw new Error(`Unsupported multicodec: ${(_a = options.name) !== null && _a !== void 0 ? _a : options.code}`);
}
// Create a new array to store the prefix and input data.
const prefixLength = varint.encodingLength(code);
const dataWithPrefix = new Uint8Array(prefixLength + data.byteLength);
dataWithPrefix.set(data, prefixLength);
// Prepend the prefix.
varint.encodeTo(code, dataWithPrefix);
return dataWithPrefix;
}
/**
* Get the Multicodec code from given prefixed data.
*
* @param options - The options for getting the codec code.
* @param options.prefixedData - The data to extract the codec code from.
* @returns - The Multicodec code as a number.
*/
static getCodeFromData(options) {
const { prefixedData } = options;
const [code, _] = varint.decode(prefixedData);
return code;
}
/**
* Get the Multicodec code from given Multicodec name.
*
* @param options - The options for getting the codec code.
* @param options.name - The name to lookup.
* @returns - The Multicodec code as a number.
*/
static getCodeFromName(options) {
const { name } = options;
// Throw error if a registered Codec wasn't found.
const code = Multicodec.nameToCode.get(name);
if (code === undefined) {
throw new Error(`Unsupported multicodec: ${name}`);
}
return code;
}
/**
* Get the Multicodec name from given Multicodec code.
*
* @param options - The options for getting the codec name.
* @param options.name - The code to lookup.
* @returns - The Multicodec name as a string.
*/
static getNameFromCode(options) {
const { code } = options;
// Throw error if a registered Codec wasn't found.
const name = Multicodec.codeToName.get(code);
if (name === undefined) {
throw new Error(`Unsupported multicodec: ${code}`);
}
return name;
}
/**
* Registers a new codec in the Multicodec class.
*
* @param codec - The codec to be registered.
*/
static registerCodec(codec) {
Multicodec.codeToName.set(codec.code, codec.name);
Multicodec.nameToCode.set(codec.name, codec.code);
}
/**
* Returns the data with the Multicodec prefix removed.
*
* @param refixedData - The data to extract the codec code from.
* @returns {Uint8Array}
*/
static removePrefix(options) {
const { prefixedData } = options;
const [code, codeByteLength] = varint.decode(prefixedData);
// Throw error if a registered Codec wasn't found.
const name = Multicodec.codeToName.get(code);
if (name === undefined) {
throw new Error(`Unsupported multicodec: ${code}`);
}
return { code, data: prefixedData.slice(codeByteLength), name };
}
}
/**
* A static field containing a map of codec codes to their corresponding names.
*/
Multicodec.codeToName = new Map();
/**
* A static field containing a map of codec names to their corresponding codes.
*/
Multicodec.nameToCode = new Map();
// Pre-defined registered codecs:
Multicodec.registerCodec({ code: 0xed, name: 'ed25519-pub' });
Multicodec.registerCodec({ code: 0x1300, name: 'ed25519-priv' });
Multicodec.registerCodec({ code: 0xec, name: 'x25519-pub' });
Multicodec.registerCodec({ code: 0x1302, name: 'x25519-priv' });
Multicodec.registerCodec({ code: 0xe7, name: 'secp256k1-pub' });
Multicodec.registerCodec({ code: 0x1301, name: 'secp256k1-priv' });
//# sourceMappingURL=multicodec.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"multicodec.js","sourceRoot":"","sources":["../../src/multicodec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAUtC;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,OAAO,UAAU;IAWrB;;;;;;;;OAQG;IACI,MAAM,CAAC,SAAS,CAAC,OAIvB;;QACC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC;QAEnC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE;YAC1B,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;SAC3E;QAED,gEAAgE;QAChE,IAAI,GAAG,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC,IAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC,IAAK,CAAC,CAAC;QAElF,kDAAkD;QAClD,IAAI,IAAI,KAAK,SAAS,EAAE;YACtB,MAAM,IAAI,KAAK,CAAC,2BAA2B,MAAA,OAAO,CAAC,IAAI,mCAAI,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;SAC5E;QAED,yDAAyD;QACzD,MAAM,YAAY,GAAG,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;QACjD,MAAM,cAAc,GAAG,IAAI,UAAU,CAAC,YAAY,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC;QACtE,cAAc,CAAC,GAAG,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;QAEvC,sBAAsB;QACtB,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;QAEtC,OAAO,cAAc,CAAC;IACxB,CAAC;IAED;;;;;;OAMG;IACI,MAAM,CAAC,eAAe,CAAC,OAE7B;QACC,MAAM,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC;QACjC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QAE9C,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;;OAMG;IACI,MAAM,CAAC,eAAe,CAAC,OAE7B;QACC,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC;QAEzB,kDAAkD;QAClD,MAAM,IAAI,GAAG,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC7C,IAAI,IAAI,KAAK,SAAS,EAAE;YACtB,MAAM,IAAI,KAAK,CAAC,2BAA2B,IAAI,EAAE,CAAC,CAAC;SACpD;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;;OAMG;IACI,MAAM,CAAC,eAAe,CAAC,OAE7B;QACC,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC;QAEzB,kDAAkD;QAClD,MAAM,IAAI,GAAG,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC7C,IAAI,IAAI,KAAK,SAAS,EAAE;YACtB,MAAM,IAAI,KAAK,CAAC,2BAA2B,IAAI,EAAE,CAAC,CAAC;SACpD;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;OAIG;IACI,MAAM,CAAC,aAAa,CAAC,KAA2C;QACrE,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAClD,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;IACpD,CAAC;IAED;;;;;OAKG;IACI,MAAM,CAAC,YAAY,CAAC,OAE1B;QACC,MAAM,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC;QACjC,MAAM,CAAC,IAAI,EAAE,cAAc,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QAE3D,kDAAkD;QAClD,MAAM,IAAI,GAAG,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC7C,IAAI,IAAI,KAAK,SAAS,EAAE;YACtB,MAAM,IAAI,KAAK,CAAC,2BAA2B,IAAI,EAAE,CAAC,CAAC;SACpD;QAED,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,YAAY,CAAC,KAAK,CAAC,cAAc,CAAC,EAAE,IAAI,EAAE,CAAC;IAClE,CAAC;;AAxID;;GAEG;AACI,qBAAU,GAAG,IAAI,GAAG,EAA0B,CAAC;AAEtD;;GAEG;AACI,qBAAU,GAAG,IAAI,GAAG,EAA0B,CAAC;AAmIxD,iCAAiC;AACjC,UAAU,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC,CAAC;AAC9D,UAAU,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,CAAC;AACjE,UAAU,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC,CAAC;AAC7D,UAAU,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC,CAAC;AAChE,UAAU,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC,CAAC;AAChE,UAAU,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC,CAAC"}
+40
View File
@@ -0,0 +1,40 @@
/**
* Checks whether the given object has any properties.
*/
export function isEmptyObject(obj) {
if (typeof obj !== 'object' || obj === null) {
return false;
}
if (Object.getOwnPropertySymbols(obj).length > 0) {
return false;
}
return Object.keys(obj).length === 0;
}
/**
* Recursively removes all properties with an empty object or array as its value from the given object.
*/
export function removeEmptyObjects(obj) {
Object.keys(obj).forEach(key => {
if (typeof (obj[key]) === 'object') {
// recursive remove empty object or array properties in nested objects
removeEmptyObjects(obj[key]);
}
if (isEmptyObject(obj[key])) {
delete obj[key];
}
});
}
/**
* Recursively removes all properties with `undefined` as its value from the given object.
*/
export function removeUndefinedProperties(obj) {
Object.keys(obj).forEach(key => {
if (obj[key] === undefined) {
delete obj[key];
}
else if (typeof (obj[key]) === 'object') {
removeUndefinedProperties(obj[key]); // recursive remove `undefined` properties in nested objects
}
});
}
//# sourceMappingURL=object.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"object.js","sourceRoot":"","sources":["../../src/object.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,GAAY;IACxC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,EAAE;QAC3C,OAAO,KAAK,CAAC;KACd;IAED,IAAI,MAAM,CAAC,qBAAqB,CAAC,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE;QAChD,OAAO,KAAK,CAAC;KACd;IAED,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC;AACvC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAA4B;IAC7D,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE;QAC7B,IAAI,OAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,QAAQ,EAAE;YACjC,sEAAsE;YACtE,kBAAkB,CAAC,GAAG,CAAC,GAAG,CAA4B,CAAC,CAAC;SACzD;QAED,IAAI,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE;YAC3B,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC;SACjB;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,yBAAyB,CAAC,GAA4B;IACpE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE;QAC7B,IAAI,GAAG,CAAC,GAAG,CAAC,KAAK,SAAS,EAAE;YAC1B,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC;SACjB;aAAM,IAAI,OAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,QAAQ,EAAE;YACxC,yBAAyB,CAAC,GAAG,CAAC,GAAG,CAA4B,CAAC,CAAC,CAAC,4DAA4D;SAC7H;IACH,CAAC,CAAC,CAAC;AACL,CAAC"}
+150
View File
@@ -0,0 +1,150 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { Level } from 'level';
export class LevelStore {
constructor({ db, location = 'DATASTORE' } = {}) {
this.store = db !== null && db !== void 0 ? db : new Level(location);
}
clear() {
return __awaiter(this, void 0, void 0, function* () {
yield this.store.clear();
});
}
close() {
return __awaiter(this, void 0, void 0, function* () {
yield this.store.close();
});
}
delete(key) {
return __awaiter(this, void 0, void 0, function* () {
yield this.store.del(key);
});
}
get(key) {
return __awaiter(this, void 0, void 0, function* () {
try {
return yield this.store.get(key);
}
catch (error) {
// Don't throw when a key wasn't found.
if (error.notFound)
return undefined;
throw error;
}
});
}
set(key, value) {
return __awaiter(this, void 0, void 0, function* () {
yield this.store.put(key, value);
});
}
}
/**
* The `MemoryStore` class is an implementation of
* `KeyValueStore` that holds data in memory.
*
* It provides a basic key-value store that works synchronously and keeps all
* data in memory. This can be used for testing, or for handling small amounts
* of data with simple key-value semantics.
*
* Example usage:
*
* ```ts
* const memoryStore = new MemoryStore<string, number>();
* await memoryStore.set("key1", 1);
* const value = await memoryStore.get("key1");
* console.log(value); // 1
* ```
*
* @public
*/
export class MemoryStore {
constructor() {
/**
* A private field that contains the Map used as the key-value store.
*/
this.store = new Map();
}
/**
* Clears all entries in the key-value store.
*
* @returns A Promise that resolves when the operation is complete.
*/
clear() {
return __awaiter(this, void 0, void 0, function* () {
this.store.clear();
});
}
/**
* This operation is no-op for `MemoryStore`
* and will log a warning if called.
*/
close() {
return __awaiter(this, void 0, void 0, function* () {
/** no-op */
});
}
/**
* Deletes an entry from the key-value store by its key.
*
* @param id - The key of the entry to delete.
* @returns A Promise that resolves to a boolean indicating whether the entry was successfully deleted.
*/
delete(id) {
return __awaiter(this, void 0, void 0, function* () {
return this.store.delete(id);
});
}
/**
* Retrieves the value of an entry by its key.
*
* @param id - The key of the entry to retrieve.
* @returns A Promise that resolves to the value of the entry, or `undefined` if the entry does not exist.
*/
get(id) {
return __awaiter(this, void 0, void 0, function* () {
return this.store.get(id);
});
}
/**
* Checks for the presence of an entry by key.
*
* @param id - The key to check for the existence of.
* @returns A Promise that resolves to a boolean indicating whether an element with the specified key exists or not.
*/
has(id) {
return __awaiter(this, void 0, void 0, function* () {
return this.store.has(id);
});
}
/**
* Retrieves all values in the key-value store.
*
* @returns A Promise that resolves to an array of all values in the store.
*/
list() {
return __awaiter(this, void 0, void 0, function* () {
return Array.from(this.store.values());
});
}
/**
* Sets the value of an entry in the key-value store.
*
* @param id - The key of the entry to set.
* @param key - The new value for the entry.
* @returns A Promise that resolves when the operation is complete.
*/
set(id, key) {
return __awaiter(this, void 0, void 0, function* () {
this.store.set(id, key);
});
}
}
//# sourceMappingURL=stores.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"stores.js","sourceRoot":"","sources":["../../src/stores.ts"],"names":[],"mappings":";;;;;;;;;AAEA,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAI9B,MAAM,OAAO,UAAU;IAGrB,YAAY,EAAE,EAAE,EAAE,QAAQ,GAAG,WAAW,KAGpC,EAAE;QACJ,IAAI,CAAC,KAAK,GAAG,EAAE,aAAF,EAAE,cAAF,EAAE,GAAI,IAAI,KAAK,CAAO,QAAQ,CAAC,CAAC;IAC/C,CAAC;IAEK,KAAK;;YACT,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QAC3B,CAAC;KAAA;IAEK,KAAK;;YACT,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QAC3B,CAAC;KAAA;IAEK,MAAM,CAAC,GAAM;;YACjB,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC5B,CAAC;KAAA;IAEK,GAAG,CAAC,GAAM;;YACd,IAAI;gBACF,OAAO,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;aAClC;YAAC,OAAO,KAAU,EAAE;gBACnB,uCAAuC;gBACvC,IAAI,KAAK,CAAC,QAAQ;oBAAE,OAAO,SAAS,CAAC;gBACrC,MAAM,KAAK,CAAC;aACb;QACH,CAAC;KAAA;IAEK,GAAG,CAAC,GAAM,EAAE,KAAQ;;YACxB,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACnC,CAAC;KAAA;CACF;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,OAAO,WAAW;IAAxB;QACE;;WAEG;QACK,UAAK,GAAc,IAAI,GAAG,EAAE,CAAC;IAoEvC,CAAC;IAlEC;;;;OAIG;IACG,KAAK;;YACT,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACrB,CAAC;KAAA;IAED;;;OAGG;IACG,KAAK;;YACT,YAAY;QACd,CAAC;KAAA;IAED;;;;;OAKG;IACG,MAAM,CAAC,EAAK;;YAChB,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC/B,CAAC;KAAA;IAED;;;;;OAKG;IACG,GAAG,CAAC,EAAK;;YACb,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC5B,CAAC;KAAA;IAED;;;;;OAKG;IACG,GAAG,CAAC,EAAK;;YACb,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC5B,CAAC;KAAA;IAED;;;;OAIG;IACG,IAAI;;YACR,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;QACzC,CAAC;KAAA;IAED;;;;;;OAMG;IACG,GAAG,CAAC,EAAK,EAAE,GAAM;;YACrB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;QAC1B,CAAC;KAAA;CACF"}
+356
View File
@@ -0,0 +1,356 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { Readable } from 'readable-stream';
import { Stream } from './stream.js';
import { Convert } from './convert.js';
export { Readable } from 'readable-stream';
export class NodeStream {
/**
* Consumes a `Readable` stream and returns its contents as an `ArrayBuffer`.
*
* This method reads all data from a Node.js `Readable` stream, collects it, and converts it into
* an `ArrayBuffer`.
*
* @example
* ```ts
* const nodeReadable = getReadableStreamSomehow();
* const arrayBuffer = await NodeStream.consumeToArrayBuffer({ readable: nodeReadable });
* ```
*
* @param readable - The Node.js Readable stream whose data will be consumed.
* @returns A Promise that resolves to an `ArrayBuffer` containing all the data from the stream.
*/
static consumeToArrayBuffer({ readable }) {
return __awaiter(this, void 0, void 0, function* () {
const arrayBuffer = yield Convert.asyncIterable(readable).toArrayBufferAsync();
return arrayBuffer;
});
}
/**
* Consumes a `Readable` stream and returns its contents as a `Blob`.
*
* This method reads all data from a Node.js `Readable` stream, collects it, and converts it into
* a `Blob`.
*
* @example
* ```ts
* const nodeReadable = getReadableStreamSomehow();
* const blob = await NodeStream.consumeToBlob({ readable: nodeReadable });
* ```
*
* @param readableStream - The Node.js `Readable` stream whose data will be consumed.
* @returns A Promise that resolves to a `Blob` containing all the data from the stream.
*/
static consumeToBlob({ readable }) {
return __awaiter(this, void 0, void 0, function* () {
const blob = yield Convert.asyncIterable(readable).toBlobAsync();
return blob;
});
}
/**
* Consumes a `Readable` stream and returns its contents as a `Uint8Array`.
*
* This method reads all data from a Node.js `Readable`, collects it, and converts it into a
* `Uint8Array`.
*
* @example
* ```ts
* const nodeReadable = getReadableStreamSomehow();
* const bytes = await NodeStream.consumeToBytes({ readable: nodeReadable });
* ```
*
* @param readableStream - The Node.js `Readable` stream whose data will be consumed.
* @returns A Promise that resolves to a `Uint8Array` containing all the data from the stream.
*/
static consumeToBytes({ readable }) {
return __awaiter(this, void 0, void 0, function* () {
const bytes = yield Convert.asyncIterable(readable).toUint8ArrayAsync();
return bytes;
});
}
/**
* Consumes a `Readable` stream and parses its contents as JSON.
*
* This method reads all the data from the stream, converts it to a text string, and then parses
* it as JSON, returning the resulting object.
*
* @example
* ```ts
* const nodeReadable = getReadableStreamSomehow();
* const jsonData = await NodeStream.consumeToJson({ readable: nodeReadable });
* ```
*
* @param readableStream - The Node.js `Readable` stream whose JSON content will be consumed.
* @returns A Promise that resolves to the parsed JSON object from the stream's data.
*/
static consumeToJson({ readable }) {
return __awaiter(this, void 0, void 0, function* () {
const object = yield Convert.asyncIterable(readable).toObjectAsync();
return object;
});
}
/**
* Consumes a `Readable` stream and returns its contents as a text string.
*
* This method reads all the data from the stream, converting it into a single string.
*
* @example
* ```ts
* const nodeReadable = getReadableStreamSomehow();
* const text = await NodeStream.consumeToText({ readable: nodeReadable });
* ```
*
* @param readableStream - The Node.js `Readable` stream whose text content will be consumed.
* @returns A Promise that resolves to a string containing all the data from the stream.
*/
static consumeToText({ readable }) {
return __awaiter(this, void 0, void 0, function* () {
const text = yield Convert.asyncIterable(readable).toStringAsync();
return text;
});
}
/**
* Converts a Web `ReadableStream` to a Node.js `Readable` stream.
*
* This method takes a Web `ReadableStream` and converts it to a Node.js `Readable` stream.
* The conversion is done by reading chunks from the Web `ReadableStream` and pushing them
* into the Node.js `Readable` stream.
*
* @example
* ```ts
* const webReadableStream = getWebReadableStreamSomehow();
* const nodeReadableStream = NodeStream.fromWebReadable({ readableStream: webReadableStream });
* ```
*
* @param readableStream - The Web `ReadableStream` to be converted.
* @param readableOptions - Optional `Readable` stream options for the Node.js stream.
* @returns The Node.js `Readable` stream.
*/
static fromWebReadable({ readableStream, readableOptions }) {
if (!Stream.isReadableStream(readableStream)) {
throw new TypeError(`NodeStream.fromWebReadable: 'readableStream' is not a Web ReadableStream.`);
}
const reader = readableStream.getReader();
let closed = false;
const nodeReadable = new Readable(Object.assign(Object.assign({}, readableOptions), { read: function () {
reader.read().then(({ done, value }) => {
if (done) {
this.push(null); // Push null to signify end of stream.
}
else {
if (!this.push(value)) {
// When push returns false, we should stop reading until _read is called again.
return;
}
}
}).catch((error) => {
// If an error occurs while reading, destroy the stream.
this.destroy(error);
});
}, destroy: function (error, callback) {
function done() {
callback(error);
}
if (!closed) {
reader.cancel(error)
.then(done)
.catch(done);
return;
}
done();
} }));
reader.closed
.then(() => {
closed = true; // Prevents reader.cancel() from being called in destroy()
})
.catch((error) => {
closed = true; // Prevents reader.cancel() from being called in destroy()
nodeReadable.destroy(error);
});
return nodeReadable;
}
/**
* Checks if a Node.js stream (`Readable`, `Writable`, `Duplex`, or `Transform`) has been destroyed.
*
* This method determines whether the provided Node.js stream has been destroyed. A stream
* is considered destroyed if its 'destroyed' property is set to true or if its internal state
* indicates it has been destroyed.
*
* @example
* ```ts
* const stream = getStreamSomehow();
* stream.destroy(); // Destroy the stream.
* const isDestroyed = NodeStream.isDestroyed({ stream });
* console.log(isDestroyed); // Output: true
* ```
*
* @param stream - The Node.js stream to check.
* @returns `true` if the stream has been destroyed; otherwise, `false`.
*/
static isDestroyed({ stream }) {
if (!NodeStream.isStream(stream)) {
throw new TypeError(`NodeStream.isDestroyed: 'stream' is not a Node stream.`);
}
const writableState = '_writableState' in stream ? stream._writableState : undefined;
const readableState = stream._readableState;
const state = writableState || readableState;
return !!(stream.destroyed || state.destroyed);
}
/**
* Checks if a Node.js `Readable` stream is still readable.
*
* This method checks if a Node.js `Readable` stream is still in a state that allows reading from
* it. A stream is considered readable if it has not ended, has not been destroyed, and is not
* currently paused.
*
* @example
* ```ts
* const readableStream = new Readable();
* const isReadable = NodeStream.isReadable({ readable: readableStream });
* console.log(isReadable); // Output: true or false
* ```
*
* @param readable - The Node.js `Readable` stream to be checked.
* @returns `true` if the stream is still readable; otherwise, `false`.
*/
static isReadable({ readable }) {
// Check if the object is a Node Readable stream.
if (!NodeStream.isReadableStream(readable)) {
return false;
}
// Check if the stream is still readable.
return (readable.readable && // Is the stream readable?
(typeof readable._readableState.ended === 'boolean' && !readable._readableState.ended) && // Has the 'end' method been called?
(typeof readable._readableState.endEmitted === 'boolean' && !readable._readableState.endEmitted) && // Has the 'end' event been emitted?
!readable.destroyed && // Has the 'destroy' method been called?
!readable.isPaused() // Is the stream paused?
);
}
/**
* Checks if an object is a Node.js `Readable` stream.
*
* This method verifies if the provided object is a Node.js `Readable` stream by checking for
* specific properties and methods typical of a `Readable` stream in Node.js.
*
* @example
* ```ts
* const obj = getSomeObject();
* if (NodeStream.isReadableStream(obj)) {
* // obj is a Node.js Readable stream
* }
* ```
*
* @param obj - The object to be checked.
* @returns `true` if `obj` is a Node.js `Readable` stream; otherwise, `false`.
*/
static isReadableStream(obj) {
return (typeof obj === 'object' &&
obj !== null &&
('pipe' in obj && typeof obj.pipe === 'function') &&
('on' in obj && typeof obj.on === 'function') &&
(!('_writableState' in obj) && '_readableState' in obj));
}
/**
* Checks if the provided object is a Node.js stream (`Duplex`, `Readable`, `Writable`, or `Transform`).
*
* This method checks for the presence of internal properties specific to Node.js streams:
* `_readableState` and `_writableState`. These properties are present in Node.js stream
* instances, allowing identification of the stream type.
*
* The `_readableState` property is found in `Readable` and `Duplex` streams (including
* `Transform` streams, which are a type of `Duplex` stream), indicating that the stream can be
* read from. The `_writableState` property is found in `Writable` and `Duplex` streams,
* indicating that the stream can be written to.
*
* @example
* ```ts
* const { Readable, Writable, Duplex, Transform } = require('stream');
*
* const readableStream = new Readable();
* console.log(NodeStream.isStream(readableStream)); // Output: true
*
* const writableStream = new Writable();
* console.log(NodeStream.isStream(writableStream)); // Output: true
*
* const duplexStream = new Duplex();
* console.log(NodeStream.isStream(duplexStream)); // Output: true
*
* const transformStream = new Transform();
* console.log(NodeStream.isStream(transformStream)); // Output: true
*
* const nonStreamObject = {};
* console.log(NodeStream.isStream(nonStreamObject)); // Output: false
* ```
*
* @remarks
* - This method does not differentiate between the different types of streams (Readable,
* Writable, Duplex, Transform). It simply checks if the object is any kind of Node.js stream.
* - While this method can identify standard Node.js streams, it may not recognize custom or
* third-party stream-like objects that do not inherit directly from Node.js's stream classes
* or do not have these internal state properties. This is intentional as many of the methods
* in this library are designed to work with standard Node.js streams.
*
* @param obj - The object to be checked for being a Node.js stream.
* @returns `true` if the object is a Node.js stream (`Duplex`, `Readable`, `Writable`, or `Transform`); otherwise, `false`.
*/
static isStream(obj) {
return (typeof obj === 'object' && obj !== null &&
('_readableState' in obj || '_writableState' in obj));
}
/**
* Converts a Node.js `Readable` stream to a Web `ReadableStream`.
*
* This method provides a bridge between Node.js streams and the Web Streams API by converting a
* Node.js `Readable` stream into a Web `ReadableStream`. It listens for 'data', 'end', and 'error'
* events on the Node.js stream and appropriately enqueues data, closes, or errors the Web
* `ReadableStream`.
*
* If the Node.js stream is already destroyed, the method returns an immediately cancelled
* Web `ReadableStream`.
*
* @example
* ```ts
* const nodeReadable = getNodeReadableStreamSomehow();
* const webReadableStream = NodeStream.toWebReadable({ readable: nodeReadable });
* ```
*
* @param readable - The Node.js `Readable` stream to be converted.
* @returns A Web `ReadableStream` corresponding to the provided Node.js `Readable` stream.
* @throws TypeError if `readable` is not a Node.js `Readable` stream.
* @throws Error if the Node.js `Readable` stream is already destroyed.
*/
static toWebReadable({ readable }) {
if (!NodeStream.isReadableStream(readable)) {
throw new TypeError(`NodeStream.toWebReadable: 'readable' is not a Node Readable stream.`);
}
if (NodeStream.isDestroyed({ stream: readable })) {
const readable = new ReadableStream();
readable.cancel();
return readable;
}
return new ReadableStream({
start(controller) {
readable.on('data', (chunk) => {
controller.enqueue(chunk);
});
readable.on('end', () => {
controller.close();
});
readable.on('error', (err) => {
controller.error(err);
});
},
cancel() {
readable.destroy();
}
});
}
}
//# sourceMappingURL=stream-node.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"stream-node.js","sourceRoot":"","sources":["../../src/stream-node.ts"],"names":[],"mappings":";;;;;;;;;AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE3C,MAAM,OAAO,UAAU;IACrB;;;;;;;;;;;;;;OAcG;IACI,MAAM,CAAO,oBAAoB,CAAC,EAAE,QAAQ,EAAyB;;YAC1E,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,kBAAkB,EAAE,CAAC;YAE/E,OAAO,WAAW,CAAC;QACrB,CAAC;KAAA;IAED;;;;;;;;;;;;;;OAcG;IACI,MAAM,CAAO,aAAa,CAAC,EAAE,QAAQ,EAA0B;;YACpE,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC;YAEjE,OAAO,IAAI,CAAC;QACd,CAAC;KAAA;IAED;;;;;;;;;;;;;;OAcG;IACI,MAAM,CAAO,cAAc,CAAC,EAAE,QAAQ,EAA0B;;YACrE,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,iBAAiB,EAAE,CAAC;YAExE,OAAO,KAAK,CAAC;QACf,CAAC;KAAA;IAED;;;;;;;;;;;;;;OAcG;IACI,MAAM,CAAO,aAAa,CAAC,EAAE,QAAQ,EAA0B;;YACpE,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,aAAa,EAAE,CAAC;YAErE,OAAO,MAAM,CAAC;QAChB,CAAC;KAAA;IAED;;;;;;;;;;;;;OAaG;IACI,MAAM,CAAO,aAAa,CAAC,EAAE,QAAQ,EAAyB;;YACnE,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,aAAa,EAAE,CAAC;YAEnE,OAAO,IAAI,CAAC;QACd,CAAC;KAAA;IAED;;;;;;;;;;;;;;;;OAgBG;IACI,MAAM,CAAC,eAAe,CAAC,EAAE,cAAc,EAAE,eAAe,EAG9D;QACC,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,cAAc,CAAC,EAAE;YAC5C,MAAM,IAAI,SAAS,CAAC,2EAA2E,CAAC,CAAC;SAClG;QAED,MAAM,MAAM,GAAG,cAAc,CAAC,SAAS,EAAE,CAAC;QAC1C,IAAI,MAAM,GAAG,KAAK,CAAC;QAEnB,MAAM,YAAY,GAAG,IAAI,QAAQ,iCAC5B,eAAe,KAElB,IAAI,EAAE;gBACJ,MAAM,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE;oBACrC,IAAI,IAAI,EAAE;wBACR,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,sCAAsC;qBACxD;yBAAM;wBACL,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE;4BACrB,+EAA+E;4BAC/E,OAAO;yBACR;qBACF;gBACH,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;oBACjB,wDAAwD;oBACxD,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;gBACtB,CAAC,CAAC,CAAC;YACL,CAAC,EAED,OAAO,EAAE,UAAU,KAAK,EAAE,QAAQ;gBAChC,SAAS,IAAI;oBACX,QAAQ,CAAC,KAAK,CAAC,CAAC;gBAClB,CAAC;gBAED,IAAI,CAAC,MAAM,EAAE;oBACX,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC;yBACjB,IAAI,CAAC,IAAI,CAAC;yBACV,KAAK,CAAC,IAAI,CAAC,CAAC;oBACf,OAAO;iBACR;gBACD,IAAI,EAAE,CAAC;YACT,CAAC,IACD,CAAC;QAEH,MAAM,CAAC,MAAM;aACV,IAAI,CAAC,GAAG,EAAE;YACT,MAAM,GAAG,IAAI,CAAC,CAAC,0DAA0D;QAC3E,CAAC,CAAC;aACD,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;YACf,MAAM,GAAG,IAAI,CAAC,CAAC,0DAA0D;YACzE,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAC9B,CAAC,CAAC,CAAC;QAEL,OAAO,YAAY,CAAC;IACtB,CAAC;IAED;;;;;;;;;;;;;;;;;OAiBG;IACI,MAAM,CAAC,WAAW,CAAC,EAAE,MAAM,EAAwD;QACxF,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE;YAChC,MAAM,IAAI,SAAS,CAAC,wDAAwD,CAAC,CAAC;SAC/E;QAED,MAAM,aAAa,GAAG,gBAAgB,IAAI,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,SAAS,CAAC;QACrF,MAAM,aAAa,GAAG,MAAM,CAAC,cAAc,CAAC;QAC5C,MAAM,KAAK,GAAG,aAAa,IAAI,aAAa,CAAC;QAE7C,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,IAAI,KAAK,CAAC,SAAS,CAAC,CAAC;IACjD,CAAC;IAED;;;;;;;;;;;;;;;;OAgBG;IACI,MAAM,CAAC,UAAU,CAAC,EAAE,QAAQ,EAA0B;QAC3D,iDAAiD;QACjD,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,QAAQ,CAAC,EAAE;YAC1C,OAAO,KAAK,CAAC;SACd;QAED,yCAAyC;QACzC,OAAO,CACL,QAAQ,CAAC,QAAQ,IAAI,0BAA0B;YAC7C,CAAC,OAAO,QAAQ,CAAC,cAAc,CAAC,KAAK,KAAK,SAAS,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,KAAK,CAAC,IAAI,oCAAoC;YAC9H,CAAC,OAAO,QAAQ,CAAC,cAAc,CAAC,UAAU,KAAK,SAAS,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,UAAU,CAAC,IAAI,oCAAoC;YACxI,CAAC,QAAQ,CAAC,SAAS,IAAI,wCAAwC;YAC/D,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,wBAAwB;SAChD,CAAC;IACJ,CAAC;IAED;;;;;;;;;;;;;;;;OAgBG;IACH,MAAM,CAAC,gBAAgB,CAAC,GAAY;QAClC,OAAO,CACL,OAAO,GAAG,KAAK,QAAQ;YACrB,GAAG,KAAK,IAAI;YACZ,CAAC,MAAM,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,UAAU,CAAC;YACjD,CAAC,IAAI,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,EAAE,KAAK,UAAU,CAAC;YAC7C,CAAC,CAAC,CAAC,gBAAgB,IAAI,GAAG,CAAC,IAAI,gBAAgB,IAAI,GAAG,CAAC,CAC1D,CAAC;IACJ,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA0CG;IACI,MAAM,CAAC,QAAQ,CAAC,GAAY;QACjC,OAAO,CACL,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI;YACvC,CAAC,gBAAgB,IAAI,GAAG,IAAI,gBAAgB,IAAI,GAAG,CAAC,CACrD,CAAC;IACJ,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,MAAM,CAAC,aAAa,CAAC,EAAE,QAAQ,EAA0B;QACvD,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,QAAQ,CAAC,EAAE;YAC1C,MAAM,IAAI,SAAS,CAAC,qEAAqE,CAAC,CAAC;SAC5F;QAED,IAAI,UAAU,CAAC,WAAW,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE;YAChD,MAAM,QAAQ,GAAG,IAAI,cAAc,EAAE,CAAC;YACtC,QAAQ,CAAC,MAAM,EAAE,CAAC;YAClB,OAAO,QAAQ,CAAC;SACjB;QAED,OAAO,IAAI,cAAc,CAAC;YACxB,KAAK,CAAC,UAAU;gBACd,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;oBAC5B,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;gBAC5B,CAAC,CAAC,CAAC;gBAEH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;oBACtB,UAAU,CAAC,KAAK,EAAE,CAAC;gBACrB,CAAC,CAAC,CAAC;gBAEH,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;oBAC3B,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;gBACxB,CAAC,CAAC,CAAC;YACL,CAAC;YAED,MAAM;gBACJ,QAAQ,CAAC,OAAO,EAAE,CAAC;YACrB,CAAC;SACF,CAAC,CAAC;IACL,CAAC;CACF"}
+408
View File
@@ -0,0 +1,408 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); }
var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) {
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
var g = generator.apply(thisArg, _arguments || []), i, q = [];
return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i;
function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }
function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
function fulfill(value) { resume("next", value); }
function reject(value) { resume("throw", value); }
function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
};
import { Convert } from './convert.js';
export class Stream {
/**
* Transforms a `ReadableStream` into an `AsyncIterable`. This allows for the asynchronous
* iteration over the stream's data chunks.
*
* This method creates an async iterator from a `ReadableStream`, enabling the use of
* `for await...of` loops to process stream data. It reads from the stream until it's closed or
* errored, yielding each chunk as it becomes available.
*
* @example
* ```ts
* const readableStream = new ReadableStream({ ... });
* for await (const chunk of Stream.asAsyncIterator(readableStream)) {
* // process each chunk
* }
* ```
*
* @remarks
* - The method ensures proper cleanup by releasing the reader lock when iteration is completed or
* if an error occurs.
*
* @param readableStream - The Web `ReadableStream` to be transformed into an `AsyncIterable`.
* @returns An `AsyncIterable` that yields data chunks from the `ReadableStream`.
*/
static asAsyncIterator(readableStream) {
return __asyncGenerator(this, arguments, function* asAsyncIterator_1() {
const reader = readableStream.getReader();
try {
while (true) {
const { done, value } = yield __await(reader.read());
if (done)
break;
yield yield __await(value);
}
}
finally {
reader.releaseLock();
}
});
}
/**
* Consumes a `ReadableStream` and returns its contents as an `ArrayBuffer`.
*
* This method reads all data from a `ReadableStream`, collects it, and converts it into an
* `ArrayBuffer`.
*
* @example
* ```ts
* const readableStream = new ReadableStream({ ... });
* const arrayBuffer = await Stream.consumeToArrayBuffer({ readableStream });
* ```
*
* @param readableStream - The Web `ReadableStream` whose data will be consumed.
* @returns A Promise that resolves to an `ArrayBuffer` containing all the data from the stream.
*/
static consumeToArrayBuffer({ readableStream }) {
return __awaiter(this, void 0, void 0, function* () {
const iterableStream = Stream.asAsyncIterator(readableStream);
const arrayBuffer = yield Convert.asyncIterable(iterableStream).toArrayBufferAsync();
return arrayBuffer;
});
}
/**
* Consumes a `ReadableStream` and returns its contents as a `Blob`.
*
* This method reads all data from a `ReadableStream`, collects it, and converts it into a `Blob`.
*
* @example
* ```ts
* const readableStream = new ReadableStream({ ... });
* const blob = await Stream.consumeToBlob({ readableStream });
* ```
*
* @param readableStream - The Web `ReadableStream` whose data will be consumed.
* @returns A Promise that resolves to a `Blob` containing all the data from the stream.
*/
static consumeToBlob({ readableStream }) {
return __awaiter(this, void 0, void 0, function* () {
const iterableStream = Stream.asAsyncIterator(readableStream);
const blob = yield Convert.asyncIterable(iterableStream).toBlobAsync();
return blob;
});
}
/**
* Consumes a `ReadableStream` and returns its contents as a `Uint8Array`.
*
* This method reads all data from a `ReadableStream`, collects it, and converts it into a
* `Uint8Array`.
*
* @example
* ```ts
* const readableStream = new ReadableStream({ ... });
* const bytes = await Stream.consumeToBytes({ readableStream });
* ```
*
* @param readableStream - The Web `ReadableStream` whose data will be consumed.
* @returns A Promise that resolves to a `Uint8Array` containing all the data from the stream.
*/
static consumeToBytes({ readableStream }) {
return __awaiter(this, void 0, void 0, function* () {
const iterableStream = Stream.asAsyncIterator(readableStream);
const bytes = yield Convert.asyncIterable(iterableStream).toUint8ArrayAsync();
return bytes;
});
}
/**
* Consumes a `ReadableStream` and parses its contents as JSON.
*
* This method reads all the data from the stream, converts it to a text string, and then parses
* it as JSON, returning the resulting object.
*
* @example
* ```ts
* const readableStream = new ReadableStream({ ... });
* const jsonData = await Stream.consumeToJson({ readableStream });
* ```
*
* @param readableStream - The Web `ReadableStream` whose JSON content will be consumed.
* @returns A Promise that resolves to the parsed JSON object from the stream's data.
*/
static consumeToJson({ readableStream }) {
return __awaiter(this, void 0, void 0, function* () {
const iterableStream = Stream.asAsyncIterator(readableStream);
const object = yield Convert.asyncIterable(iterableStream).toObjectAsync();
return object;
});
}
/**
* Consumes a `ReadableStream` and returns its contents as a text string.
*
* This method reads all the data from the stream, converting it into a single string.
*
* @example
* ```ts
* const readableStream = new ReadableStream({ ... });
* const text = await Stream.consumeToText({ readableStream });
* ```
*
* @param readableStream - The Web `ReadableStream` whose text content will be consumed.
* @returns A Promise that resolves to a string containing all the data from the stream.
*/
static consumeToText({ readableStream }) {
return __awaiter(this, void 0, void 0, function* () {
const iterableStream = Stream.asAsyncIterator(readableStream);
const text = yield Convert.asyncIterable(iterableStream).toStringAsync();
return text;
});
}
/**
* Generates a `ReadableStream` of `Uint8Array` chunks with customizable length and fill value.
*
* This method creates a `ReadableStream` that emits `Uint8Array` chunks. You can specify the
* total length of the stream, the length of individual chunks, and a fill value or range for the
* chunks. It's useful for testing or when specific binary data streams are required.
*
* @example
* ```ts
* // Create a stream of 1000 bytes with 100-byte chunks filled with 0xAA.
* const byteStream = Stream.generateByteStream({
* streamLength: 1000,
* chunkLength: 100,
* fillValue: 0xAA
* });
*
* // Create an unending stream of 100KB chunks filled with values that range from 1 to 99.
* const byteStream = Stream.generateByteStream({
* chunkLength: 100 * 1024,
* fillValue: [1, 99]
* });
* ```
*
* @param streamLength - The total length of the stream in bytes. If omitted, the stream is infinite.
* @param chunkLength - The length of each chunk. If omitted, each chunk is the size of `streamLength`.
* @param fillValue - A value or range to fill the chunks with. Can be a single number or a tuple [min, max].
* @returns A `ReadableStream` that emits `Uint8Array` chunks.
*/
static generateByteStream({ streamLength, chunkLength, fillValue }) {
let bytesRemaining = streamLength !== null && streamLength !== void 0 ? streamLength : Infinity;
let controller;
function enqueueChunk() {
const currentChunkLength = Math.min(bytesRemaining, chunkLength !== null && chunkLength !== void 0 ? chunkLength : Infinity);
bytesRemaining -= currentChunkLength;
let chunk;
if (typeof fillValue === 'number') {
chunk = new Uint8Array(currentChunkLength).fill(fillValue);
}
else if (Array.isArray(fillValue)) {
chunk = new Uint8Array(currentChunkLength);
const [min, max] = fillValue;
const range = max - min + 1;
for (let i = 0; i < currentChunkLength; i++) {
chunk[i] = Math.floor(Math.random() * range) + min;
}
}
else {
chunk = new Uint8Array(currentChunkLength);
}
controller.enqueue(chunk);
// If there are no more bytes to send, close the stream
if (bytesRemaining <= 0) {
controller.close();
}
}
return new ReadableStream({
start(c) {
controller = c;
enqueueChunk();
},
pull() {
enqueueChunk();
},
});
}
/**
* Checks if the provided Web `ReadableStream` is in a readable state.
*
* After verifying that the stream is a Web {@link https://streams.spec.whatwg.org/#rs-model | ReadableStream},
* this method checks the {@link https://streams.spec.whatwg.org/#readablestream-locked | locked}
* property of the ReadableStream. The `locked` property is `true` if a reader is currently
* active, meaning the stream is either being read or has already been read (and hence is not in a
* readable state). If `locked` is `false`, it means the stream is still in a state where it can
* be read.
*
* In the case where a `ReadableStream` has been unlocked but is no longer readable (for example,
* if it has been fully read or cancelled), additional checks are needed beyond just examining the
* locked property. The ReadableStream API does not provide a direct way to check if the stream
* has data left or if it's in a readable state once it's been unlocked.
*
* Per {@link https://streams.spec.whatwg.org/#other-specs-rs-introspect | WHATWG Streams, Section 9.1.3. Introspection}:
*
* > ...note that apart from checking whether or not the stream is locked, this direct
* > introspection is not possible via the public JavaScript API, and so specifications should
* > instead use the algorithms in §9.1.2 Reading. (For example, instead of testing if the stream
* > is readable, attempt to get a reader and handle any exception.)
*
* This implementation employs the technique suggested by the WHATWG Streams standard by
* attempting to acquire a reader and checking the state of the reader. If acquiring a reader
* succeeds, it immediately releases the lock and returns `true`, indicating the stream is
* readable. If an error occurs while trying to get a reader (which can happen if the stream is
* already closed or errored), it catches the error and returns `false`, indicating the stream is
* not readable.
*
* @example
* ```ts
* const readableStream = new ReadableStream({ ... });
* const isStreamReadable = Stream.isReadable({ readableStream });
* console.log(isStreamReadable); // Output: true or false
* ```
*
* @remarks
* - This method does not check whether the stream has data left to read; it only checks if the
* stream is in a state that allows reading. It is possible for a stream to be unlocked but
* still have no data left if it has never been locked to a reader.
*
* @param readableStream - The Web `ReadableStream` to be checked for readability.
*
* @returns `true` if the stream is a `ReadableStream` and is in a readable state (not locked and
* no error on getting a reader); otherwise, `false`.
*/
static isReadable({ readableStream }) {
// Check if the stream is a WHATWG `ReadableStream`.
if (!Stream.isReadableStream(readableStream)) {
return false;
}
// Check if the stream is locked.
if (readableStream.locked) {
return false;
}
try {
// Try to get a reader to check if the stream is readable.
const reader = readableStream.getReader();
// If successful, immediately release the lock.
reader.releaseLock();
return true;
}
catch (error) {
// If an error occurs (e.g., the stream is not readable), return false.
return false;
}
}
/**
* Checks if an object is a Web `ReadableStream`.
*
* This method verifies whether the given object is a `ReadableStream` by checking its type and
* the presence of the `getReader` function.
*
* @example
* ```ts
* const obj = getSomeObject();
* if (Stream.isReadableStream(obj)) {
* // obj is a ReadableStream
* }
* ```
*
* @param obj - The object to be checked.
* @returns `true` if `obj` is a `ReadableStream`; otherwise, `false`.
*/
static isReadableStream(obj) {
return (typeof obj === 'object' && obj !== null &&
'getReader' in obj && typeof obj.getReader === 'function');
}
/**
* Checks if an object is a Web `ReadableStream`, `WritableStream`, or `TransformStream`.
*
* This method verifies the type of a given object to determine if it is one of the standard
* stream types in the Web Streams API: `ReadableStream`, `WritableStream`, or `TransformStream`.
* It employs type-checking strategies that are specific to each stream type.
*
* The method checks for the specific functions and properties associated with each stream type:
* - `ReadableStream`: Identified by the presence of a `getReader` method.
* - `WritableStream`: Identified by the presence of a `getWriter` and `abort` methods.
* - `TransformStream`: Identified by having both `readable` and `writable` properties.
*
* @example
* ```ts
* const readableStream = new ReadableStream();
* console.log(Stream.isStream(readableStream)); // Output: true
*
* const writableStream = new WritableStream();
* console.log(Stream.isStream(writableStream)); // Output: true
*
* const transformStream = new TransformStream();
* console.log(Stream.isStream(transformStream)); // Output: true
*
* const nonStreamObject = {};
* console.log(Stream.isStream(nonStreamObject)); // Output: false
* ```
*
* @remarks
* - This method does not differentiate between `ReadableStream`, `WritableStream`, and
* `TransformStream`. It checks if the object conforms to any of these types.
* - This method is specific to the Web Streams API and may not recognize non-standard or custom
* stream-like objects that do not adhere to the Web Streams API specifications.
*
* @param obj - The object to be checked for being a Web `ReadableStream`, `WritableStream`, or `TransformStream`.
* @returns `true` if the object is a `ReadableStream`, `WritableStream`, or `TransformStream`; otherwise, `false`.
*/
static isStream(obj) {
return Stream.isReadableStream(obj) || Stream.isWritableStream(obj) || Stream.isTransformStream(obj);
}
/**
* Checks if an object is a `TransformStream`.
*
* This method verifies whether the given object is a `TransformStream` by checking its type and
* the presence of `readable` and `writable` properties.
*
* @example
* ```ts
* const obj = getSomeObject();
* if (Stream.isTransformStream(obj)) {
* // obj is a TransformStream
* }
* ```
*
* @param obj - The object to be checked.
* @returns `true` if `obj` is a `TransformStream`; otherwise, `false`.
*/
static isTransformStream(obj) {
return (typeof obj === 'object' && obj !== null &&
'readable' in obj && typeof obj.readable === 'object' &&
'writable' in obj && typeof obj.writable === 'object');
}
/**
* Checks if an object is a `WritableStream`.
*
* This method determines whether the given object is a `WritableStream` by verifying its type and
* the presence of the `getWriter` and `abort` functions.
*
* @example
* ```ts
* const obj = getSomeObject();
* if (Stream.isWritableStream(obj)) {
* // obj is a WritableStream
* }
* ```
*
* @param obj - The object to be checked.
* @returns `true` if `obj` is a `TransformStream`; otherwise, `false`.
*/
static isWritableStream(obj) {
return (typeof obj === 'object' && obj !== null &&
'getWriter' in obj && typeof obj.getWriter === 'function' &&
'abort' in obj && typeof obj.abort === 'function');
}
}
//# sourceMappingURL=stream.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"stream.js","sourceRoot":"","sources":["../../src/stream.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,MAAM,OAAO,MAAM;IACjB;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACI,MAAM,CAAS,eAAe,CAAI,cAAiC;;YACxE,MAAM,MAAM,GAAG,cAAc,CAAC,SAAS,EAAE,CAAC;YAC1C,IAAI;gBACF,OAAO,IAAI,EAAE;oBACX,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,cAAM,MAAM,CAAC,IAAI,EAAE,CAAA,CAAC;oBAC5C,IAAI,IAAI;wBAAE,MAAM;oBAChB,oBAAM,KAAK,CAAA,CAAC;iBACb;aACF;oBAAS;gBACR,MAAM,CAAC,WAAW,EAAE,CAAC;aACtB;QACH,CAAC;KAAA;IAED;;;;;;;;;;;;;;OAcG;IACI,MAAM,CAAO,oBAAoB,CAAC,EAAE,cAAc,EAAqC;;YAC5F,MAAM,cAAc,GAAG,MAAM,CAAC,eAAe,CAAC,cAAc,CAAC,CAAC;YAC9D,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC,aAAa,CAAC,cAAc,CAAC,CAAC,kBAAkB,EAAE,CAAC;YAErF,OAAO,WAAW,CAAC;QACrB,CAAC;KAAA;IAED;;;;;;;;;;;;;OAaG;IACI,MAAM,CAAO,aAAa,CAAC,EAAE,cAAc,EAAqC;;YACrF,MAAM,cAAc,GAAG,MAAM,CAAC,eAAe,CAAC,cAAc,CAAC,CAAC;YAC9D,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,aAAa,CAAC,cAAc,CAAC,CAAC,WAAW,EAAE,CAAC;YAEvE,OAAO,IAAI,CAAC;QACd,CAAC;KAAA;IAED;;;;;;;;;;;;;;OAcG;IACI,MAAM,CAAO,cAAc,CAAC,EAAE,cAAc,EAAsC;;YACvF,MAAM,cAAc,GAAG,MAAM,CAAC,eAAe,CAAC,cAAc,CAAC,CAAC;YAC9D,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,aAAa,CAAC,cAAc,CAAC,CAAC,iBAAiB,EAAE,CAAC;YAE9E,OAAO,KAAK,CAAC;QACf,CAAC;KAAA;IAED;;;;;;;;;;;;;;OAcG;IACI,MAAM,CAAO,aAAa,CAAC,EAAE,cAAc,EAAqC;;YACrF,MAAM,cAAc,GAAG,MAAM,CAAC,eAAe,CAAC,cAAc,CAAC,CAAC;YAC9D,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,aAAa,CAAC,cAAc,CAAC,CAAC,aAAa,EAAE,CAAC;YAE3E,OAAO,MAAM,CAAC;QAChB,CAAC;KAAA;IAED;;;;;;;;;;;;;OAaG;IACI,MAAM,CAAO,aAAa,CAAC,EAAE,cAAc,EAAqC;;YACrF,MAAM,cAAc,GAAG,MAAM,CAAC,eAAe,CAAC,cAAc,CAAC,CAAC;YAC9D,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,aAAa,CAAC,cAAc,CAAC,CAAC,aAAa,EAAE,CAAC;YAEzE,OAAO,IAAI,CAAC;QACd,CAAC;KAAA;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;IACI,MAAM,CAAC,kBAAkB,CAAC,EAAE,YAAY,EAAE,WAAW,EAAE,SAAS,EAItE;QACC,IAAI,cAAc,GAAG,YAAY,aAAZ,YAAY,cAAZ,YAAY,GAAI,QAAQ,CAAC;QAC9C,IAAI,UAAuD,CAAC;QAE5D,SAAS,YAAY;YACnB,MAAM,kBAAkB,GAAG,IAAI,CAAC,GAAG,CAAC,cAAc,EAAE,WAAW,aAAX,WAAW,cAAX,WAAW,GAAI,QAAQ,CAAC,CAAC;YAC7E,cAAc,IAAI,kBAAkB,CAAC;YAErC,IAAI,KAAiB,CAAC;YAEtB,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE;gBACjC,KAAK,GAAG,IAAI,UAAU,CAAC,kBAAkB,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;aAE5D;iBAAM,IAAI,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE;gBACnC,KAAK,GAAG,IAAI,UAAU,CAAC,kBAAkB,CAAC,CAAC;gBAC3C,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,SAAS,CAAC;gBAC7B,MAAM,KAAK,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,CAAC;gBAC5B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,kBAAkB,EAAE,CAAC,EAAE,EAAE;oBAC3C,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,KAAK,CAAC,GAAG,GAAG,CAAC;iBACpD;aAEF;iBAAM;gBACL,KAAK,GAAG,IAAI,UAAU,CAAC,kBAAkB,CAAC,CAAC;aAC5C;YAED,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YAE1B,uDAAuD;YACvD,IAAI,cAAc,IAAI,CAAC,EAAE;gBACvB,UAAU,CAAC,KAAK,EAAE,CAAC;aACpB;QACH,CAAC;QAED,OAAO,IAAI,cAAc,CAAa;YACpC,KAAK,CAAC,CAAC;gBACL,UAAU,GAAG,CAAC,CAAC;gBACf,YAAY,EAAE,CAAC;YACjB,CAAC;YACD,IAAI;gBACF,YAAY,EAAE,CAAC;YACjB,CAAC;SACF,CAAC,CAAC;IACL,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6CG;IACI,MAAM,CAAC,UAAU,CAAC,EAAE,cAAc,EAAsC;QAC7E,oDAAoD;QACpD,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,cAAc,CAAC,EAAE;YAC5C,OAAO,KAAK,CAAC;SACd;QAED,iCAAiC;QACjC,IAAI,cAAc,CAAC,MAAM,EAAE;YACzB,OAAO,KAAK,CAAC;SACd;QAED,IAAI;YACF,0DAA0D;YAC1D,MAAM,MAAM,GAAG,cAAc,CAAC,SAAS,EAAE,CAAC;YAC1C,+CAA+C;YAC/C,MAAM,CAAC,WAAW,EAAE,CAAC;YACrB,OAAO,IAAI,CAAC;SACb;QAAC,OAAO,KAAK,EAAE;YACd,uEAAuE;YACvE,OAAO,KAAK,CAAC;SACd;IACH,CAAC;IAED;;;;;;;;;;;;;;;;OAgBG;IACI,MAAM,CAAC,gBAAgB,CAAC,GAAY;QACzC,OAAO,CACL,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI;YACvC,WAAW,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,SAAS,KAAK,UAAU,CAC1D,CAAC;IACJ,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAmCG;IACI,MAAM,CAAC,QAAQ,CAAC,GAAY;QACjC,OAAO,MAAM,CAAC,gBAAgB,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,gBAAgB,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC;IACvG,CAAC;IAED;;;;;;;;;;;;;;;;OAgBG;IACI,MAAM,CAAC,iBAAiB,CAAC,GAAY;QAC1C,OAAO,CACL,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI;YACvC,UAAU,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,QAAQ,KAAK,QAAQ;YACrD,UAAU,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,QAAQ,KAAK,QAAQ,CACtD,CAAC;IACJ,CAAC;IAED;;;;;;;;;;;;;;;;SAgBK;IACE,MAAM,CAAC,gBAAgB,CAAC,GAAY;QACzC,OAAO,CACL,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI;YACvC,WAAW,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,SAAS,KAAK,UAAU;YACzD,OAAO,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,KAAK,KAAK,UAAU,CAClD,CAAC;IACJ,CAAC;CACF"}
+113
View File
@@ -0,0 +1,113 @@
/**
* isArrayBufferSlice
*
* Checks if the ArrayBufferView represents a slice (subarray or a subview)
* of an ArrayBuffer.
*
* An ArrayBufferView (TypedArray or DataView) can represent a portion of an
* ArrayBuffer - such a view is said to be a "slice" of the original buffer.
* This can occur when the `subarray` or `slice` method is called on a
* TypedArray or when a DataView is created with a byteOffset and/or
* byteLength that doesn't cover the full ArrayBuffer.
*
* @param arrayBufferView - The ArrayBufferView to be checked
* @returns true if the ArrayBufferView represents a slice of an ArrayBuffer; false otherwise.
*/
export function isArrayBufferSlice(arrayBufferView) {
return arrayBufferView.byteOffset !== 0 || arrayBufferView.byteLength !== arrayBufferView.buffer.byteLength;
}
/**
* Checks if the given object is an AsyncIterable.
*
* An AsyncIterable is an object that implements the AsyncIterable protocol,
* which means it has a [Symbol.asyncIterator] method. This function checks
* if the provided object conforms to this protocol by verifying the presence
* and type of the [Symbol.asyncIterator] method.
*
* @param obj - The object to be checked for AsyncIterable conformity.
* @returns True if the object is an AsyncIterable, false otherwise.
*
* @example
* ```ts
* // Returns true for a valid AsyncIterable
* const asyncIterable = {
* async *[Symbol.asyncIterator]() {
* yield 1;
* yield 2;
* }
* };
* console.log(isAsyncIterable(asyncIterable)); // true
* ```
*
* @example
* ```ts
* // Returns false for a regular object
* console.log(isAsyncIterable({ a: 1, b: 2 })); // false
* ```
*/
export function isAsyncIterable(obj) {
if (typeof obj !== 'object' || obj === null) {
return false;
}
return typeof obj[Symbol.asyncIterator] === 'function';
}
/**
* isDefined
*
* Utility function to check if a variable is neither null nor undefined.
* This function helps in making TypeScript infer the type of the variable
* as being defined, excluding `null` and `undefined`.
*
* The function uses strict equality (`!==`) for the comparison, ensuring
* that the variable is not just falsy (like an empty string or zero),
* but is truly either `null` or `undefined`.
*
* @param arg - The variable to be checked
* @returns true if the variable is neither `null` nor `undefined`
*/
export function isDefined(arg) {
return arg !== null && typeof arg !== 'undefined';
}
/**
* universalTypeOf
*
* Why does this function exist?
*
* You can typically check if a value is of a particular type, such as
* Uint8Array or ArrayBuffer, by using the `instanceof` operator. The
* `instanceof` operator checks the prototype property of a constructor
* in the object's prototype chain.
*
* However, there is a caveat with the `instanceof` check if the value
* was created from a different JavaScript context (like an iframe or
* a web worker). In those cases, the `instanceof` check might fail
* because each context has a different global object, and therefore,
* different built-in constructor functions.
*
* The `typeof` operator provides information about the type of the
* operand in a less detailed way. For basic data types like number,
* string, boolean, and undefined, the `typeof` operator works as
* expected. However, for objects, including arrays and null,
* it always returns "object". For functions, it returns "function".
* So, while `typeof` is good for basic type checking, it doesn't
* give detailed information about complex data types.
*
* Unlike `instanceof` and `typeof`, `Object.prototype.toString.call(value)`
* can ensure a consistent result across different JavaScript
* contexts.
*
* Credit for inspiration:
* Angus Croll
* https://github.com/angus-c
* https://javascriptweblog.wordpress.com/2011/08/08/fixing-the-javascript-typeof-operator/
*/
export function universalTypeOf(value) {
// Returns '[Object Type]' string.
const typeString = Object.prototype.toString.call(value);
// Returns ['Object', 'Type'] array or null.
const match = typeString.match(/\s([a-zA-Z0-9]+)/);
// Deconstructs the array and gets just the type from index 1.
const [_, type] = match;
return type;
}
//# sourceMappingURL=type-utils.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"type-utils.js","sourceRoot":"","sources":["../../src/type-utils.ts"],"names":[],"mappings":"AAgEA;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,kBAAkB,CAAC,eAAgC;IACjE,OAAO,eAAe,CAAC,UAAU,KAAK,CAAC,IAAI,eAAe,CAAC,UAAU,KAAK,eAAe,CAAC,MAAM,CAAC,UAAU,CAAC;AAC9G,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,UAAU,eAAe,CAAC,GAAQ;IACtC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,EAAE;QAC3C,OAAO,KAAK,CAAC;KACd;IAED,OAAO,OAAO,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,UAAU,CAAC;AACzD,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,SAAS,CAAI,GAAM;IACjC,OAAO,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,WAAW,CAAC;AACpD,CAAC;AA8BD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,MAAM,UAAU,eAAe,CAAC,KAAc;IAC5C,kCAAkC;IAClC,MAAM,UAAU,GAAG,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACzD,4CAA4C;IAC5C,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;IACnD,8DAA8D;IAC9D,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,KAAyB,CAAC;IAE5C,OAAO,IAAI,CAAC;AACd,CAAC"}
+2
View File
@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=types.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":""}
+3
View File
@@ -0,0 +1,3 @@
import TTLCache from '@isaacs/ttlcache';
export { TTLCache as TtlCache };
//# sourceMappingURL=cache.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/cache.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,kBAAkB,CAAC;AACxC,OAAO,EAAE,QAAQ,IAAI,QAAQ,EAAE,CAAC"}
+38
View File
@@ -0,0 +1,38 @@
import type { Multibase } from 'multiformats';
export declare class Convert {
data: any;
format: string;
constructor(data: any, format: string);
static arrayBuffer(data: ArrayBuffer): Convert;
static asyncIterable(data: AsyncIterable<any>): Convert;
static base32Z(data: string): Convert;
static base58Btc(data: string): Convert;
static base64Url(data: string): Convert;
/**
* Reference:
* The BufferSource type is a TypeScript type that represents an ArrayBuffer
* or one of the ArrayBufferView types, such a TypedArray (e.g., Uint8Array)
* or a DataView.
*/
static bufferSource(data: BufferSource): Convert;
static hex(data: string): Convert;
static multibase(data: string): Convert;
static object(data: Record<string, any>): Convert;
static string(data: string): Convert;
static uint8Array(data: Uint8Array): Convert;
toArrayBuffer(): ArrayBuffer;
toArrayBufferAsync(): Promise<ArrayBuffer>;
toBase32Z(): string;
toBase58Btc(): string;
toBase64Url(): string;
toBlobAsync(): Promise<Blob>;
toHex(): string;
toMultibase(): Multibase<any>;
toObject(): object;
toObjectAsync(): Promise<any>;
toString(): string;
toStringAsync(): Promise<string>;
toUint8Array(): Uint8Array;
toUint8ArrayAsync(): Promise<Uint8Array>;
}
//# sourceMappingURL=convert.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"convert.d.ts","sourceRoot":"","sources":["../../src/convert.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAW9C,qBAAa,OAAO;IAClB,IAAI,EAAE,GAAG,CAAC;IACV,MAAM,EAAE,MAAM,CAAC;gBAEH,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM;IAKrC,MAAM,CAAC,WAAW,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO;IAI9C,MAAM,CAAC,aAAa,CAAC,IAAI,EAAE,aAAa,CAAC,GAAG,CAAC,GAAG,OAAO;IAOvD,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAIrC,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAIvC,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAIvC;;;;;OAKG;IACH,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO;IAIhD,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAUjC,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAIvC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO;IAIjD,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAIpC,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO;IAI5C,aAAa,IAAI,WAAW;IA+CtB,kBAAkB,IAAI,OAAO,CAAC,WAAW,CAAC;IAYhD,SAAS,IAAI,MAAM;IAYnB,WAAW,IAAI,MAAM;IAqBrB,WAAW,IAAI,MAAM;IAiCf,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAwBlC,KAAK,IAAI,MAAM;IA6Bf,WAAW,IAAI,SAAS,CAAC,GAAG,CAAC;IAW7B,QAAQ,IAAI,MAAM;IAuBZ,aAAa,IAAI,OAAO,CAAC,GAAG,CAAC;IAoBnC,QAAQ,IAAI,MAAM;IAyBZ,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC;IA+BtC,YAAY,IAAI,UAAU;IAiEpB,iBAAiB,IAAI,OAAO,CAAC,UAAU,CAAC;CAW/C"}
+10
View File
@@ -0,0 +1,10 @@
export type * from './types.js';
export * from './cache.js';
export * from './convert.js';
export * from './multicodec.js';
export * from './object.js';
export * from './stores.js';
export * from './stream.js';
export * from './stream-node.js';
export * from './type-utils.js';
//# sourceMappingURL=index.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,mBAAmB,YAAY,CAAC;AAEhC,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC"}
+98
View File
@@ -0,0 +1,98 @@
export type MulticodecCode = number;
export type MulticodecDefinition<MulticodecCode> = {
code: MulticodecCode;
name: string;
};
/**
* The `Multicodec` class provides an interface to prepend binary data
* with a prefix that identifies the data that follows.
* https://github.com/multiformats/multicodec/blob/master/table.csv
*
* Multicodec is a self-describing multiformat, it wraps other formats with
* a tiny bit of self-description. A multicodec identifier is a
* varint (variable integer) that indicates the format of the data.
*
* The canonical table of multicodecs can be access at the following URL:
* https://github.com/multiformats/multicodec/blob/master/table.csv
*
* Example usage:
*
* ```ts
* Multicodec.registerCodec({ code: 0xed, name: 'ed25519-pub' });
* const prefixedData = Multicodec.addPrefix({ code: 0xed, data: new Uint8Array(32) });
* ```
*/
export declare class Multicodec {
/**
* A static field containing a map of codec codes to their corresponding names.
*/
static codeToName: Map<number, string>;
/**
* A static field containing a map of codec names to their corresponding codes.
*/
static nameToCode: Map<string, number>;
/**
* Adds a multicodec prefix to input data.
*
* @param options - The options for adding a prefix.
* @param options.code - The codec code. Either the code or name must be provided.
* @param options.name - The codec name. Either the code or name must be provided.
* @param options.data - The data to be prefixed.
* @returns The data with the added prefix as a Uint8Array.
*/
static addPrefix(options: {
code?: MulticodecCode;
data: Uint8Array;
name?: string;
}): Uint8Array;
/**
* Get the Multicodec code from given prefixed data.
*
* @param options - The options for getting the codec code.
* @param options.prefixedData - The data to extract the codec code from.
* @returns - The Multicodec code as a number.
*/
static getCodeFromData(options: {
prefixedData: Uint8Array;
}): MulticodecCode;
/**
* Get the Multicodec code from given Multicodec name.
*
* @param options - The options for getting the codec code.
* @param options.name - The name to lookup.
* @returns - The Multicodec code as a number.
*/
static getCodeFromName(options: {
name: string;
}): MulticodecCode;
/**
* Get the Multicodec name from given Multicodec code.
*
* @param options - The options for getting the codec name.
* @param options.name - The code to lookup.
* @returns - The Multicodec name as a string.
*/
static getNameFromCode(options: {
code: MulticodecCode;
}): string;
/**
* Registers a new codec in the Multicodec class.
*
* @param codec - The codec to be registered.
*/
static registerCodec(codec: MulticodecDefinition<MulticodecCode>): void;
/**
* Returns the data with the Multicodec prefix removed.
*
* @param refixedData - The data to extract the codec code from.
* @returns {Uint8Array}
*/
static removePrefix(options: {
prefixedData: Uint8Array;
}): {
code: MulticodecCode;
name: string;
data: Uint8Array;
};
}
//# sourceMappingURL=multicodec.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"multicodec.d.ts","sourceRoot":"","sources":["../../src/multicodec.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC;AAEpC,MAAM,MAAM,oBAAoB,CAAC,cAAc,IAAI;IACjD,IAAI,EAAE,cAAc,CAAC;IAErB,IAAI,EAAE,MAAM,CAAC;CACd,CAAA;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,UAAU;IACrB;;OAEG;IACH,MAAM,CAAC,UAAU,sBAAqC;IAEtD;;OAEG;IACH,MAAM,CAAC,UAAU,sBAAqC;IAEtD;;;;;;;;OAQG;WACW,SAAS,CAAC,OAAO,EAAE;QAC/B,IAAI,CAAC,EAAE,cAAc,CAAC;QACtB,IAAI,EAAE,UAAU,CAAC;QACjB,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,GAAG,UAAU;IA0Bd;;;;;;OAMG;WACW,eAAe,CAAC,OAAO,EAAE;QACrC,YAAY,EAAE,UAAU,CAAA;KACzB,GAAG,cAAc;IAOlB;;;;;;OAMG;WACW,eAAe,CAAC,OAAO,EAAE;QACrC,IAAI,EAAE,MAAM,CAAA;KACb,GAAG,cAAc;IAYlB;;;;;;OAMG;WACW,eAAe,CAAC,OAAO,EAAE;QACrC,IAAI,EAAE,cAAc,CAAA;KACrB,GAAG,MAAM;IAYV;;;;OAIG;WACW,aAAa,CAAC,KAAK,EAAE,oBAAoB,CAAC,cAAc,CAAC;IAKvE;;;;;OAKG;WACW,YAAY,CAAC,OAAO,EAAE;QAClC,YAAY,EAAE,UAAU,CAAA;KACzB,GAAG;QAAE,IAAI,EAAE,cAAc,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,UAAU,CAAA;KAAE;CAY7D"}
+13
View File
@@ -0,0 +1,13 @@
/**
* Checks whether the given object has any properties.
*/
export declare function isEmptyObject(obj: unknown): boolean;
/**
* Recursively removes all properties with an empty object or array as its value from the given object.
*/
export declare function removeEmptyObjects(obj: Record<string, unknown>): void;
/**
* Recursively removes all properties with `undefined` as its value from the given object.
*/
export declare function removeUndefinedProperties(obj: Record<string, unknown>): void;
//# sourceMappingURL=object.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"object.d.ts","sourceRoot":"","sources":["../../src/object.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAUnD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAWrE;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAQ5E"}
+87
View File
@@ -0,0 +1,87 @@
/// <reference types="node" resolution-mode="require"/>
import type { AbstractLevel } from 'abstract-level';
import type { KeyValueStore } from './types.js';
export declare class LevelStore<K = string, V = any> implements KeyValueStore<K, V> {
private store;
constructor({ db, location }?: {
db?: AbstractLevel<string | Buffer | Uint8Array, K, V>;
location?: string;
});
clear(): Promise<void>;
close(): Promise<void>;
delete(key: K): Promise<void>;
get(key: K): Promise<V | undefined>;
set(key: K, value: V): Promise<void>;
}
/**
* The `MemoryStore` class is an implementation of
* `KeyValueStore` that holds data in memory.
*
* It provides a basic key-value store that works synchronously and keeps all
* data in memory. This can be used for testing, or for handling small amounts
* of data with simple key-value semantics.
*
* Example usage:
*
* ```ts
* const memoryStore = new MemoryStore<string, number>();
* await memoryStore.set("key1", 1);
* const value = await memoryStore.get("key1");
* console.log(value); // 1
* ```
*
* @public
*/
export declare class MemoryStore<K, V> implements KeyValueStore<K, V> {
/**
* A private field that contains the Map used as the key-value store.
*/
private store;
/**
* Clears all entries in the key-value store.
*
* @returns A Promise that resolves when the operation is complete.
*/
clear(): Promise<void>;
/**
* This operation is no-op for `MemoryStore`
* and will log a warning if called.
*/
close(): Promise<void>;
/**
* Deletes an entry from the key-value store by its key.
*
* @param id - The key of the entry to delete.
* @returns A Promise that resolves to a boolean indicating whether the entry was successfully deleted.
*/
delete(id: K): Promise<boolean>;
/**
* Retrieves the value of an entry by its key.
*
* @param id - The key of the entry to retrieve.
* @returns A Promise that resolves to the value of the entry, or `undefined` if the entry does not exist.
*/
get(id: K): Promise<V | undefined>;
/**
* Checks for the presence of an entry by key.
*
* @param id - The key to check for the existence of.
* @returns A Promise that resolves to a boolean indicating whether an element with the specified key exists or not.
*/
has(id: K): Promise<boolean>;
/**
* Retrieves all values in the key-value store.
*
* @returns A Promise that resolves to an array of all values in the store.
*/
list(): Promise<V[]>;
/**
* Sets the value of an entry in the key-value store.
*
* @param id - The key of the entry to set.
* @param key - The new value for the entry.
* @returns A Promise that resolves when the operation is complete.
*/
set(id: K, key: V): Promise<void>;
}
//# sourceMappingURL=stores.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"stores.d.ts","sourceRoot":"","sources":["../../src/stores.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAIpD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,qBAAa,UAAU,CAAC,CAAC,GAAG,MAAM,EAAE,CAAC,GAAG,GAAG,CAAE,YAAW,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC;IACzE,OAAO,CAAC,KAAK,CAAoD;gBAErD,EAAE,EAAE,EAAE,QAAsB,EAAE,GAAE;QAC1C,EAAE,CAAC,EAAE,aAAa,CAAC,MAAM,GAAG,MAAM,GAAG,UAAU,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QACvD,QAAQ,CAAC,EAAE,MAAM,CAAC;KACd;IAIA,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB,MAAM,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAI7B,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAUnC,GAAG,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;CAG3C;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,WAAW,CAAC,CAAC,EAAE,CAAC,CAAE,YAAW,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC;IAC3D;;OAEG;IACH,OAAO,CAAC,KAAK,CAAwB;IAErC;;;;OAIG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B;;;;;OAKG;IACG,MAAM,CAAC,EAAE,EAAE,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;IAIrC;;;;;OAKG;IACG,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAIxC;;;;;OAKG;IACG,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;IAIlC;;;;OAIG;IACG,IAAI,IAAI,OAAO,CAAC,CAAC,EAAE,CAAC;IAI1B;;;;;;OAMG;IACG,GAAG,CAAC,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;CAGxC"}
+244
View File
@@ -0,0 +1,244 @@
import type { Duplex, ReadableStateOptions, Transform, Writable } from 'readable-stream';
import { Readable } from 'readable-stream';
export { Readable } from 'readable-stream';
export declare class NodeStream {
/**
* Consumes a `Readable` stream and returns its contents as an `ArrayBuffer`.
*
* This method reads all data from a Node.js `Readable` stream, collects it, and converts it into
* an `ArrayBuffer`.
*
* @example
* ```ts
* const nodeReadable = getReadableStreamSomehow();
* const arrayBuffer = await NodeStream.consumeToArrayBuffer({ readable: nodeReadable });
* ```
*
* @param readable - The Node.js Readable stream whose data will be consumed.
* @returns A Promise that resolves to an `ArrayBuffer` containing all the data from the stream.
*/
static consumeToArrayBuffer({ readable }: {
readable: Readable;
}): Promise<ArrayBuffer>;
/**
* Consumes a `Readable` stream and returns its contents as a `Blob`.
*
* This method reads all data from a Node.js `Readable` stream, collects it, and converts it into
* a `Blob`.
*
* @example
* ```ts
* const nodeReadable = getReadableStreamSomehow();
* const blob = await NodeStream.consumeToBlob({ readable: nodeReadable });
* ```
*
* @param readableStream - The Node.js `Readable` stream whose data will be consumed.
* @returns A Promise that resolves to a `Blob` containing all the data from the stream.
*/
static consumeToBlob({ readable }: {
readable: Readable;
}): Promise<Blob>;
/**
* Consumes a `Readable` stream and returns its contents as a `Uint8Array`.
*
* This method reads all data from a Node.js `Readable`, collects it, and converts it into a
* `Uint8Array`.
*
* @example
* ```ts
* const nodeReadable = getReadableStreamSomehow();
* const bytes = await NodeStream.consumeToBytes({ readable: nodeReadable });
* ```
*
* @param readableStream - The Node.js `Readable` stream whose data will be consumed.
* @returns A Promise that resolves to a `Uint8Array` containing all the data from the stream.
*/
static consumeToBytes({ readable }: {
readable: Readable;
}): Promise<Uint8Array>;
/**
* Consumes a `Readable` stream and parses its contents as JSON.
*
* This method reads all the data from the stream, converts it to a text string, and then parses
* it as JSON, returning the resulting object.
*
* @example
* ```ts
* const nodeReadable = getReadableStreamSomehow();
* const jsonData = await NodeStream.consumeToJson({ readable: nodeReadable });
* ```
*
* @param readableStream - The Node.js `Readable` stream whose JSON content will be consumed.
* @returns A Promise that resolves to the parsed JSON object from the stream's data.
*/
static consumeToJson({ readable }: {
readable: Readable;
}): Promise<any>;
/**
* Consumes a `Readable` stream and returns its contents as a text string.
*
* This method reads all the data from the stream, converting it into a single string.
*
* @example
* ```ts
* const nodeReadable = getReadableStreamSomehow();
* const text = await NodeStream.consumeToText({ readable: nodeReadable });
* ```
*
* @param readableStream - The Node.js `Readable` stream whose text content will be consumed.
* @returns A Promise that resolves to a string containing all the data from the stream.
*/
static consumeToText({ readable }: {
readable: Readable;
}): Promise<string>;
/**
* Converts a Web `ReadableStream` to a Node.js `Readable` stream.
*
* This method takes a Web `ReadableStream` and converts it to a Node.js `Readable` stream.
* The conversion is done by reading chunks from the Web `ReadableStream` and pushing them
* into the Node.js `Readable` stream.
*
* @example
* ```ts
* const webReadableStream = getWebReadableStreamSomehow();
* const nodeReadableStream = NodeStream.fromWebReadable({ readableStream: webReadableStream });
* ```
*
* @param readableStream - The Web `ReadableStream` to be converted.
* @param readableOptions - Optional `Readable` stream options for the Node.js stream.
* @returns The Node.js `Readable` stream.
*/
static fromWebReadable({ readableStream, readableOptions }: {
readableStream: ReadableStream;
readableOptions?: ReadableStateOptions;
}): Readable;
/**
* Checks if a Node.js stream (`Readable`, `Writable`, `Duplex`, or `Transform`) has been destroyed.
*
* This method determines whether the provided Node.js stream has been destroyed. A stream
* is considered destroyed if its 'destroyed' property is set to true or if its internal state
* indicates it has been destroyed.
*
* @example
* ```ts
* const stream = getStreamSomehow();
* stream.destroy(); // Destroy the stream.
* const isDestroyed = NodeStream.isDestroyed({ stream });
* console.log(isDestroyed); // Output: true
* ```
*
* @param stream - The Node.js stream to check.
* @returns `true` if the stream has been destroyed; otherwise, `false`.
*/
static isDestroyed({ stream }: {
stream: Readable | Writable | Duplex | Transform;
}): boolean;
/**
* Checks if a Node.js `Readable` stream is still readable.
*
* This method checks if a Node.js `Readable` stream is still in a state that allows reading from
* it. A stream is considered readable if it has not ended, has not been destroyed, and is not
* currently paused.
*
* @example
* ```ts
* const readableStream = new Readable();
* const isReadable = NodeStream.isReadable({ readable: readableStream });
* console.log(isReadable); // Output: true or false
* ```
*
* @param readable - The Node.js `Readable` stream to be checked.
* @returns `true` if the stream is still readable; otherwise, `false`.
*/
static isReadable({ readable }: {
readable: Readable;
}): boolean;
/**
* Checks if an object is a Node.js `Readable` stream.
*
* This method verifies if the provided object is a Node.js `Readable` stream by checking for
* specific properties and methods typical of a `Readable` stream in Node.js.
*
* @example
* ```ts
* const obj = getSomeObject();
* if (NodeStream.isReadableStream(obj)) {
* // obj is a Node.js Readable stream
* }
* ```
*
* @param obj - The object to be checked.
* @returns `true` if `obj` is a Node.js `Readable` stream; otherwise, `false`.
*/
static isReadableStream(obj: unknown): obj is Readable;
/**
* Checks if the provided object is a Node.js stream (`Duplex`, `Readable`, `Writable`, or `Transform`).
*
* This method checks for the presence of internal properties specific to Node.js streams:
* `_readableState` and `_writableState`. These properties are present in Node.js stream
* instances, allowing identification of the stream type.
*
* The `_readableState` property is found in `Readable` and `Duplex` streams (including
* `Transform` streams, which are a type of `Duplex` stream), indicating that the stream can be
* read from. The `_writableState` property is found in `Writable` and `Duplex` streams,
* indicating that the stream can be written to.
*
* @example
* ```ts
* const { Readable, Writable, Duplex, Transform } = require('stream');
*
* const readableStream = new Readable();
* console.log(NodeStream.isStream(readableStream)); // Output: true
*
* const writableStream = new Writable();
* console.log(NodeStream.isStream(writableStream)); // Output: true
*
* const duplexStream = new Duplex();
* console.log(NodeStream.isStream(duplexStream)); // Output: true
*
* const transformStream = new Transform();
* console.log(NodeStream.isStream(transformStream)); // Output: true
*
* const nonStreamObject = {};
* console.log(NodeStream.isStream(nonStreamObject)); // Output: false
* ```
*
* @remarks
* - This method does not differentiate between the different types of streams (Readable,
* Writable, Duplex, Transform). It simply checks if the object is any kind of Node.js stream.
* - While this method can identify standard Node.js streams, it may not recognize custom or
* third-party stream-like objects that do not inherit directly from Node.js's stream classes
* or do not have these internal state properties. This is intentional as many of the methods
* in this library are designed to work with standard Node.js streams.
*
* @param obj - The object to be checked for being a Node.js stream.
* @returns `true` if the object is a Node.js stream (`Duplex`, `Readable`, `Writable`, or `Transform`); otherwise, `false`.
*/
static isStream(obj: unknown): obj is Duplex | Readable | Writable | Transform;
/**
* Converts a Node.js `Readable` stream to a Web `ReadableStream`.
*
* This method provides a bridge between Node.js streams and the Web Streams API by converting a
* Node.js `Readable` stream into a Web `ReadableStream`. It listens for 'data', 'end', and 'error'
* events on the Node.js stream and appropriately enqueues data, closes, or errors the Web
* `ReadableStream`.
*
* If the Node.js stream is already destroyed, the method returns an immediately cancelled
* Web `ReadableStream`.
*
* @example
* ```ts
* const nodeReadable = getNodeReadableStreamSomehow();
* const webReadableStream = NodeStream.toWebReadable({ readable: nodeReadable });
* ```
*
* @param readable - The Node.js `Readable` stream to be converted.
* @returns A Web `ReadableStream` corresponding to the provided Node.js `Readable` stream.
* @throws TypeError if `readable` is not a Node.js `Readable` stream.
* @throws Error if the Node.js `Readable` stream is already destroyed.
*/
static toWebReadable({ readable }: {
readable: Readable;
}): ReadableStream;
}
//# sourceMappingURL=stream-node.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"stream-node.d.ts","sourceRoot":"","sources":["../../src/stream-node.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,oBAAoB,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAEzF,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAI3C,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE3C,qBAAa,UAAU;IACrB;;;;;;;;;;;;;;OAcG;WACiB,oBAAoB,CAAC,EAAE,QAAQ,EAAE,EAAE;QAAE,QAAQ,EAAE,QAAQ,CAAA;KAAC,GAAG,OAAO,CAAC,WAAW,CAAC;IAMnG;;;;;;;;;;;;;;OAcG;WACiB,aAAa,CAAC,EAAE,QAAQ,EAAE,EAAE;QAAE,QAAQ,EAAE,QAAQ,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAMtF;;;;;;;;;;;;;;OAcG;WACiB,cAAc,CAAC,EAAE,QAAQ,EAAE,EAAE;QAAE,QAAQ,EAAE,QAAQ,CAAA;KAAE,GAAG,OAAO,CAAC,UAAU,CAAC;IAM7F;;;;;;;;;;;;;;OAcG;WACiB,aAAa,CAAC,EAAE,QAAQ,EAAE,EAAE;QAAE,QAAQ,EAAE,QAAQ,CAAA;KAAE,GAAG,OAAO,CAAC,GAAG,CAAC;IAMrF;;;;;;;;;;;;;OAaG;WACiB,aAAa,CAAC,EAAE,QAAQ,EAAE,EAAE;QAAE,QAAQ,EAAE,QAAQ,CAAA;KAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IAMvF;;;;;;;;;;;;;;;;OAgBG;WACW,eAAe,CAAC,EAAE,cAAc,EAAE,eAAe,EAAE,EAAE;QACjE,cAAc,EAAE,cAAc,CAAC;QAC/B,eAAe,CAAC,EAAE,oBAAoB,CAAA;KACvC,GAAG,QAAQ;IAsDZ;;;;;;;;;;;;;;;;;OAiBG;WACW,WAAW,CAAC,EAAE,MAAM,EAAE,EAAE;QAAE,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAA;KAAE,GAAG,OAAO;IAYpG;;;;;;;;;;;;;;;;OAgBG;WACW,UAAU,CAAC,EAAE,QAAQ,EAAE,EAAE;QAAE,QAAQ,EAAE,QAAQ,CAAA;KAAE,GAAG,OAAO;IAgBvE;;;;;;;;;;;;;;;;OAgBG;IACH,MAAM,CAAC,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,QAAQ;IAUtD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA0CG;WACW,QAAQ,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS;IAOrF;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,MAAM,CAAC,aAAa,CAAC,EAAE,QAAQ,EAAE,EAAE;QAAE,QAAQ,EAAE,QAAQ,CAAA;KAAE,GAAG,cAAc;CA+B3E"}
+288
View File
@@ -0,0 +1,288 @@
export declare class Stream {
/**
* Transforms a `ReadableStream` into an `AsyncIterable`. This allows for the asynchronous
* iteration over the stream's data chunks.
*
* This method creates an async iterator from a `ReadableStream`, enabling the use of
* `for await...of` loops to process stream data. It reads from the stream until it's closed or
* errored, yielding each chunk as it becomes available.
*
* @example
* ```ts
* const readableStream = new ReadableStream({ ... });
* for await (const chunk of Stream.asAsyncIterator(readableStream)) {
* // process each chunk
* }
* ```
*
* @remarks
* - The method ensures proper cleanup by releasing the reader lock when iteration is completed or
* if an error occurs.
*
* @param readableStream - The Web `ReadableStream` to be transformed into an `AsyncIterable`.
* @returns An `AsyncIterable` that yields data chunks from the `ReadableStream`.
*/
static asAsyncIterator<T>(readableStream: ReadableStream<T>): AsyncIterable<T>;
/**
* Consumes a `ReadableStream` and returns its contents as an `ArrayBuffer`.
*
* This method reads all data from a `ReadableStream`, collects it, and converts it into an
* `ArrayBuffer`.
*
* @example
* ```ts
* const readableStream = new ReadableStream({ ... });
* const arrayBuffer = await Stream.consumeToArrayBuffer({ readableStream });
* ```
*
* @param readableStream - The Web `ReadableStream` whose data will be consumed.
* @returns A Promise that resolves to an `ArrayBuffer` containing all the data from the stream.
*/
static consumeToArrayBuffer({ readableStream }: {
readableStream: ReadableStream;
}): Promise<ArrayBuffer>;
/**
* Consumes a `ReadableStream` and returns its contents as a `Blob`.
*
* This method reads all data from a `ReadableStream`, collects it, and converts it into a `Blob`.
*
* @example
* ```ts
* const readableStream = new ReadableStream({ ... });
* const blob = await Stream.consumeToBlob({ readableStream });
* ```
*
* @param readableStream - The Web `ReadableStream` whose data will be consumed.
* @returns A Promise that resolves to a `Blob` containing all the data from the stream.
*/
static consumeToBlob({ readableStream }: {
readableStream: ReadableStream;
}): Promise<Blob>;
/**
* Consumes a `ReadableStream` and returns its contents as a `Uint8Array`.
*
* This method reads all data from a `ReadableStream`, collects it, and converts it into a
* `Uint8Array`.
*
* @example
* ```ts
* const readableStream = new ReadableStream({ ... });
* const bytes = await Stream.consumeToBytes({ readableStream });
* ```
*
* @param readableStream - The Web `ReadableStream` whose data will be consumed.
* @returns A Promise that resolves to a `Uint8Array` containing all the data from the stream.
*/
static consumeToBytes({ readableStream }: {
readableStream: ReadableStream;
}): Promise<Uint8Array>;
/**
* Consumes a `ReadableStream` and parses its contents as JSON.
*
* This method reads all the data from the stream, converts it to a text string, and then parses
* it as JSON, returning the resulting object.
*
* @example
* ```ts
* const readableStream = new ReadableStream({ ... });
* const jsonData = await Stream.consumeToJson({ readableStream });
* ```
*
* @param readableStream - The Web `ReadableStream` whose JSON content will be consumed.
* @returns A Promise that resolves to the parsed JSON object from the stream's data.
*/
static consumeToJson({ readableStream }: {
readableStream: ReadableStream;
}): Promise<any>;
/**
* Consumes a `ReadableStream` and returns its contents as a text string.
*
* This method reads all the data from the stream, converting it into a single string.
*
* @example
* ```ts
* const readableStream = new ReadableStream({ ... });
* const text = await Stream.consumeToText({ readableStream });
* ```
*
* @param readableStream - The Web `ReadableStream` whose text content will be consumed.
* @returns A Promise that resolves to a string containing all the data from the stream.
*/
static consumeToText({ readableStream }: {
readableStream: ReadableStream;
}): Promise<string>;
/**
* Generates a `ReadableStream` of `Uint8Array` chunks with customizable length and fill value.
*
* This method creates a `ReadableStream` that emits `Uint8Array` chunks. You can specify the
* total length of the stream, the length of individual chunks, and a fill value or range for the
* chunks. It's useful for testing or when specific binary data streams are required.
*
* @example
* ```ts
* // Create a stream of 1000 bytes with 100-byte chunks filled with 0xAA.
* const byteStream = Stream.generateByteStream({
* streamLength: 1000,
* chunkLength: 100,
* fillValue: 0xAA
* });
*
* // Create an unending stream of 100KB chunks filled with values that range from 1 to 99.
* const byteStream = Stream.generateByteStream({
* chunkLength: 100 * 1024,
* fillValue: [1, 99]
* });
* ```
*
* @param streamLength - The total length of the stream in bytes. If omitted, the stream is infinite.
* @param chunkLength - The length of each chunk. If omitted, each chunk is the size of `streamLength`.
* @param fillValue - A value or range to fill the chunks with. Can be a single number or a tuple [min, max].
* @returns A `ReadableStream` that emits `Uint8Array` chunks.
*/
static generateByteStream({ streamLength, chunkLength, fillValue }: {
streamLength?: number;
chunkLength?: number;
fillValue?: number | [number, number];
}): ReadableStream<Uint8Array>;
/**
* Checks if the provided Web `ReadableStream` is in a readable state.
*
* After verifying that the stream is a Web {@link https://streams.spec.whatwg.org/#rs-model | ReadableStream},
* this method checks the {@link https://streams.spec.whatwg.org/#readablestream-locked | locked}
* property of the ReadableStream. The `locked` property is `true` if a reader is currently
* active, meaning the stream is either being read or has already been read (and hence is not in a
* readable state). If `locked` is `false`, it means the stream is still in a state where it can
* be read.
*
* In the case where a `ReadableStream` has been unlocked but is no longer readable (for example,
* if it has been fully read or cancelled), additional checks are needed beyond just examining the
* locked property. The ReadableStream API does not provide a direct way to check if the stream
* has data left or if it's in a readable state once it's been unlocked.
*
* Per {@link https://streams.spec.whatwg.org/#other-specs-rs-introspect | WHATWG Streams, Section 9.1.3. Introspection}:
*
* > ...note that apart from checking whether or not the stream is locked, this direct
* > introspection is not possible via the public JavaScript API, and so specifications should
* > instead use the algorithms in §9.1.2 Reading. (For example, instead of testing if the stream
* > is readable, attempt to get a reader and handle any exception.)
*
* This implementation employs the technique suggested by the WHATWG Streams standard by
* attempting to acquire a reader and checking the state of the reader. If acquiring a reader
* succeeds, it immediately releases the lock and returns `true`, indicating the stream is
* readable. If an error occurs while trying to get a reader (which can happen if the stream is
* already closed or errored), it catches the error and returns `false`, indicating the stream is
* not readable.
*
* @example
* ```ts
* const readableStream = new ReadableStream({ ... });
* const isStreamReadable = Stream.isReadable({ readableStream });
* console.log(isStreamReadable); // Output: true or false
* ```
*
* @remarks
* - This method does not check whether the stream has data left to read; it only checks if the
* stream is in a state that allows reading. It is possible for a stream to be unlocked but
* still have no data left if it has never been locked to a reader.
*
* @param readableStream - The Web `ReadableStream` to be checked for readability.
*
* @returns `true` if the stream is a `ReadableStream` and is in a readable state (not locked and
* no error on getting a reader); otherwise, `false`.
*/
static isReadable({ readableStream }: {
readableStream: ReadableStream;
}): boolean;
/**
* Checks if an object is a Web `ReadableStream`.
*
* This method verifies whether the given object is a `ReadableStream` by checking its type and
* the presence of the `getReader` function.
*
* @example
* ```ts
* const obj = getSomeObject();
* if (Stream.isReadableStream(obj)) {
* // obj is a ReadableStream
* }
* ```
*
* @param obj - The object to be checked.
* @returns `true` if `obj` is a `ReadableStream`; otherwise, `false`.
*/
static isReadableStream(obj: unknown): obj is ReadableStream;
/**
* Checks if an object is a Web `ReadableStream`, `WritableStream`, or `TransformStream`.
*
* This method verifies the type of a given object to determine if it is one of the standard
* stream types in the Web Streams API: `ReadableStream`, `WritableStream`, or `TransformStream`.
* It employs type-checking strategies that are specific to each stream type.
*
* The method checks for the specific functions and properties associated with each stream type:
* - `ReadableStream`: Identified by the presence of a `getReader` method.
* - `WritableStream`: Identified by the presence of a `getWriter` and `abort` methods.
* - `TransformStream`: Identified by having both `readable` and `writable` properties.
*
* @example
* ```ts
* const readableStream = new ReadableStream();
* console.log(Stream.isStream(readableStream)); // Output: true
*
* const writableStream = new WritableStream();
* console.log(Stream.isStream(writableStream)); // Output: true
*
* const transformStream = new TransformStream();
* console.log(Stream.isStream(transformStream)); // Output: true
*
* const nonStreamObject = {};
* console.log(Stream.isStream(nonStreamObject)); // Output: false
* ```
*
* @remarks
* - This method does not differentiate between `ReadableStream`, `WritableStream`, and
* `TransformStream`. It checks if the object conforms to any of these types.
* - This method is specific to the Web Streams API and may not recognize non-standard or custom
* stream-like objects that do not adhere to the Web Streams API specifications.
*
* @param obj - The object to be checked for being a Web `ReadableStream`, `WritableStream`, or `TransformStream`.
* @returns `true` if the object is a `ReadableStream`, `WritableStream`, or `TransformStream`; otherwise, `false`.
*/
static isStream(obj: unknown): obj is ReadableStream | WritableStream | TransformStream;
/**
* Checks if an object is a `TransformStream`.
*
* This method verifies whether the given object is a `TransformStream` by checking its type and
* the presence of `readable` and `writable` properties.
*
* @example
* ```ts
* const obj = getSomeObject();
* if (Stream.isTransformStream(obj)) {
* // obj is a TransformStream
* }
* ```
*
* @param obj - The object to be checked.
* @returns `true` if `obj` is a `TransformStream`; otherwise, `false`.
*/
static isTransformStream(obj: unknown): obj is TransformStream;
/**
* Checks if an object is a `WritableStream`.
*
* This method determines whether the given object is a `WritableStream` by verifying its type and
* the presence of the `getWriter` and `abort` functions.
*
* @example
* ```ts
* const obj = getSomeObject();
* if (Stream.isWritableStream(obj)) {
* // obj is a WritableStream
* }
* ```
*
* @param obj - The object to be checked.
* @returns `true` if `obj` is a `TransformStream`; otherwise, `false`.
*/
static isWritableStream(obj: unknown): obj is WritableStream;
}
//# sourceMappingURL=stream.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../../src/stream.ts"],"names":[],"mappings":"AAEA,qBAAa,MAAM;IACjB;;;;;;;;;;;;;;;;;;;;;;OAsBG;WACmB,eAAe,CAAC,CAAC,EAAE,cAAc,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC;IAa7F;;;;;;;;;;;;;;OAcG;WACiB,oBAAoB,CAAC,EAAE,cAAc,EAAE,EAAE;QAAE,cAAc,EAAE,cAAc,CAAA;KAAC,GAAG,OAAO,CAAC,WAAW,CAAC;IAOrH;;;;;;;;;;;;;OAaG;WACiB,aAAa,CAAC,EAAE,cAAc,EAAE,EAAE;QAAE,cAAc,EAAE,cAAc,CAAA;KAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAOvG;;;;;;;;;;;;;;OAcG;WACiB,cAAc,CAAC,EAAE,cAAc,EAAE,EAAE;QAAE,cAAc,EAAE,cAAc,CAAA;KAAE,GAAG,OAAO,CAAC,UAAU,CAAC;IAO/G;;;;;;;;;;;;;;OAcG;WACiB,aAAa,CAAC,EAAE,cAAc,EAAE,EAAE;QAAE,cAAc,EAAE,cAAc,CAAA;KAAC,GAAG,OAAO,CAAC,GAAG,CAAC;IAOtG;;;;;;;;;;;;;OAaG;WACiB,aAAa,CAAC,EAAE,cAAc,EAAE,EAAE;QAAE,cAAc,EAAE,cAAc,CAAA;KAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IAOzG;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;WACW,kBAAkB,CAAC,EAAE,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE;QACzE,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,SAAS,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KACtC,GAAG,cAAc,CAAC,UAAU,CAAC;IA4C9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6CG;WACW,UAAU,CAAC,EAAE,cAAc,EAAE,EAAE;QAAE,cAAc,EAAE,cAAc,CAAA;KAAE,GAAG,OAAO;IAuBzF;;;;;;;;;;;;;;;;OAgBG;WACW,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,cAAc;IAOnE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAmCG;WACW,QAAQ,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,cAAc,GAAG,cAAc,GAAG,eAAe;IAI9F;;;;;;;;;;;;;;;;OAgBG;WACW,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,eAAe;IAQrE;;;;;;;;;;;;;;;;SAgBK;WACS,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,cAAc;CAOpE"}
+206
View File
@@ -0,0 +1,206 @@
/**
* Represents an array of a fixed length, preventing modifications to its size.
*
* The `FixedLengthArray` utility type transforms a standard array into a variant where
* methods that could alter the length are omitted. It leverages TypeScript's advanced types,
* such as conditional types and mapped types, to ensure that the array cannot be resized
* through methods like `push`, `pop`, `splice`, `shift`, and `unshift`. The utility type
* maintains all other characteristics of a standard array, including indexing, iteration,
* and type checking for its elements.
*
* Note: The type does not prevent direct assignment to indices, even if it would exceed
* the original length. However, such actions would lead to TypeScript type errors.
*
* @example
* ```ts
* // Declare a variable with a type of fixed-length array of three strings.
* let myFixedLengthArray: FixedLengthArray< [string, string, string]>;
*
* // Array declaration tests
* myFixedLengthArray = [ 'a', 'b', 'c' ]; // OK
* myFixedLengthArray = [ 'a', 'b', 123 ]; // TYPE ERROR
* myFixedLengthArray = [ 'a' ]; // LENGTH ERROR
* myFixedLengthArray = [ 'a', 'b' ]; // LENGTH ERROR
*
* // Index assignment tests
* myFixedLengthArray[1] = 'foo'; // OK
* myFixedLengthArray[1000] = 'foo'; // INVALID INDEX ERROR
*
* // Methods that mutate array length
* myFixedLengthArray.push('foo'); // MISSING METHOD ERROR
* myFixedLengthArray.pop(); // MISSING METHOD ERROR
*
* // Direct length manipulation
* myFixedLengthArray.length = 123; // READ-ONLY ERROR
*
* // Destructuring
* let [ a ] = myFixedLengthArray; // OK
* let [ a, b ] = myFixedLengthArray; // OK
* let [ a, b, c ] = myFixedLengthArray; // OK
* let [ a, b, c, d ] = myFixedLengthArray; // INVALID INDEX ERROR
* ```
*
* @template T extends any[] - The array type to be transformed.
*/
export type FixedLengthArray<T extends any[]> = Pick<T, Exclude<keyof T, ArrayLengthMutationKeys>> & {
/**
* Custom iterator for the `FixedLengthArray` type.
*
* This iterator allows the `FixedLengthArray` to be used in standard iteration
* contexts, such as `for...of` loops and spread syntax. It ensures that even though
* the array is of a fixed length with disabled mutation methods, it still retains
* iterable behavior similar to a regular array.
*
* @returns An IterableIterator for the array items.
*/
[Symbol.iterator]: () => IterableIterator<ArrayItems<T>>;
};
/** Helper types for {@link FixedLengthArray} */
type ArrayLengthMutationKeys = 'splice' | 'push' | 'pop' | 'shift' | 'unshift' | number;
type ArrayItems<T extends Array<any>> = T extends Array<infer TItems> ? TItems : never;
/**
* isArrayBufferSlice
*
* Checks if the ArrayBufferView represents a slice (subarray or a subview)
* of an ArrayBuffer.
*
* An ArrayBufferView (TypedArray or DataView) can represent a portion of an
* ArrayBuffer - such a view is said to be a "slice" of the original buffer.
* This can occur when the `subarray` or `slice` method is called on a
* TypedArray or when a DataView is created with a byteOffset and/or
* byteLength that doesn't cover the full ArrayBuffer.
*
* @param arrayBufferView - The ArrayBufferView to be checked
* @returns true if the ArrayBufferView represents a slice of an ArrayBuffer; false otherwise.
*/
export declare function isArrayBufferSlice(arrayBufferView: ArrayBufferView): boolean;
/**
* Checks if the given object is an AsyncIterable.
*
* An AsyncIterable is an object that implements the AsyncIterable protocol,
* which means it has a [Symbol.asyncIterator] method. This function checks
* if the provided object conforms to this protocol by verifying the presence
* and type of the [Symbol.asyncIterator] method.
*
* @param obj - The object to be checked for AsyncIterable conformity.
* @returns True if the object is an AsyncIterable, false otherwise.
*
* @example
* ```ts
* // Returns true for a valid AsyncIterable
* const asyncIterable = {
* async *[Symbol.asyncIterator]() {
* yield 1;
* yield 2;
* }
* };
* console.log(isAsyncIterable(asyncIterable)); // true
* ```
*
* @example
* ```ts
* // Returns false for a regular object
* console.log(isAsyncIterable({ a: 1, b: 2 })); // false
* ```
*/
export declare function isAsyncIterable(obj: any): obj is AsyncIterable<any>;
/**
* isDefined
*
* Utility function to check if a variable is neither null nor undefined.
* This function helps in making TypeScript infer the type of the variable
* as being defined, excluding `null` and `undefined`.
*
* The function uses strict equality (`!==`) for the comparison, ensuring
* that the variable is not just falsy (like an empty string or zero),
* but is truly either `null` or `undefined`.
*
* @param arg - The variable to be checked
* @returns true if the variable is neither `null` nor `undefined`
*/
export declare function isDefined<T>(arg: T): arg is Exclude<T, null | undefined>;
/**
* Utility type that transforms a type `T` to have only certain keys `K` as required, while the
* rest remain optional, except for keys specified in `O`, which are omitted entirely.
*
* This type is useful when you need a variation of a type where only specific properties are
* required, and others are either optional or not included at all. It allows for more flexible type
* definitions based on existing types without the need to redefine them.
*
* @template T - The original type to be transformed.
* @template K - The keys of `T` that should be required.
* @template O - The keys of `T` that should be omitted from the resulting type (optional).
*
* @example
* ```ts
* // Given an interface
* interface Example {
* requiredProp: string;
* optionalProp?: number;
* anotherOptionalProp?: boolean;
* }
*
* // Making 'optionalProp' required and omitting 'anotherOptionalProp'
* type ModifiedExample = RequireOnly<Example, 'optionalProp', 'anotherOptionalProp'>;
* // Result: { requiredProp?: string; optionalProp: number; }
* ```
*/
export type RequireOnly<T, K extends keyof T, O extends keyof T = never> = Required<Pick<T, K>> & Omit<Partial<T>, O>;
/**
* universalTypeOf
*
* Why does this function exist?
*
* You can typically check if a value is of a particular type, such as
* Uint8Array or ArrayBuffer, by using the `instanceof` operator. The
* `instanceof` operator checks the prototype property of a constructor
* in the object's prototype chain.
*
* However, there is a caveat with the `instanceof` check if the value
* was created from a different JavaScript context (like an iframe or
* a web worker). In those cases, the `instanceof` check might fail
* because each context has a different global object, and therefore,
* different built-in constructor functions.
*
* The `typeof` operator provides information about the type of the
* operand in a less detailed way. For basic data types like number,
* string, boolean, and undefined, the `typeof` operator works as
* expected. However, for objects, including arrays and null,
* it always returns "object". For functions, it returns "function".
* So, while `typeof` is good for basic type checking, it doesn't
* give detailed information about complex data types.
*
* Unlike `instanceof` and `typeof`, `Object.prototype.toString.call(value)`
* can ensure a consistent result across different JavaScript
* contexts.
*
* Credit for inspiration:
* Angus Croll
* https://github.com/angus-c
* https://javascriptweblog.wordpress.com/2011/08/08/fixing-the-javascript-typeof-operator/
*/
export declare function universalTypeOf(value: unknown): string;
/**
* Utility type to extract the type resolved by a Promise.
*
* This type unwraps the type `T` from `Promise<T>` if `T` is a Promise, otherwise returns `T` as
* is. It's useful in situations where you need to handle the type returned by a promise-based
* function in a synchronous context, such as defining types for test vectors or handling return
* types in non-async code blocks.
*
* @template T - The type to unwrap from the Promise.
*
* @example
* ```ts
* // For a Promise type, it extracts the resolved type.
* type AsyncNumber = Promise<number>;
* type UnwrappedNumber = UnwrapPromise<AsyncNumber>; // number
*
* // For a non-Promise type, it returns the type as is.
* type StringValue = string;
* type UnwrappedString = UnwrapPromise<StringValue>; // string
* ```
*/
export type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;
export {};
//# sourceMappingURL=type-utils.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"type-utils.d.ts","sourceRoot":"","sources":["../../src/type-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,MAAM,MAAM,gBAAgB,CAAC,CAAC,SAAS,GAAG,EAAE,IAC1C,IAAI,CAAC,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,uBAAuB,CAAC,CAAC,GAChD;IACA;;;;;;;;;OASG;IACH,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,MAAM,gBAAgB,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAA;CACzD,CAAC;AAEJ,gDAAgD;AAChD,KAAK,uBAAuB,GAAG,QAAQ,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;AACxF,KAAK,UAAU,CAAC,CAAC,SAAS,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,KAAK,CAAC,MAAM,MAAM,CAAC,GAAG,MAAM,GAAG,KAAK,CAAC;AAEvF;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAAC,eAAe,EAAE,eAAe,GAAG,OAAO,CAE5E;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,IAAI,aAAa,CAAC,GAAG,CAAC,CAMnE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,GAAG,IAAI,OAAO,CAAC,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC,CAExE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,GAAG,KAAK,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAEtH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,UAS7C;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI,CAAC,SAAS,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC"}
+40
View File
@@ -0,0 +1,40 @@
/**
* Interface for a generic key-value store.
*/
export interface KeyValueStore<K, V> {
/**
* Clears the store, removing all key-value pairs.
*
* @returns A promise that resolves when the store has been cleared.
*/
clear(): Promise<void>;
/**
* Closes the store, freeing up any resources used. After calling this method, no other operations can be performed on the store.
*
* @returns A promise that resolves when the store has been closed.
*/
close(): Promise<void>;
/**
* Deletes a key-value pair from the store.
*
* @param key - The key of the value to delete.
* @returns A promise that resolves to true if the element existed and has been removed, or false if the element does not exist.
*/
delete(key: K): Promise<boolean | void>;
/**
* Fetches a value from the store given its key.
*
* @param key - The key of the value to retrieve.
* @returns A promise that resolves with the value associated with the key, or `undefined` if no value exists for that key.
*/
get(key: K): Promise<V | undefined>;
/**
* Sets the value for a key in the store.
*
* @param key - The key under which to store the value.
* @param value - The value to be stored.
* @returns A promise that resolves when the value has been set.
*/
set(key: K, value: V): Promise<void>;
}
//# sourceMappingURL=types.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,aAAa,CAAC,CAAC,EAAE,CAAC;IACjC;;;;OAIG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvB;;;;OAIG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvB;;;;;OAKG;IACH,MAAM,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IAExC;;;;;OAKG;IACH,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;IAEpC;;;;;;OAMG;IACH,GAAG,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACtC"}
@@ -0,0 +1,47 @@
Node.js is licensed for use as follows:
"""
Copyright Node.js contributors. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
"""
This license applies to parts of Node.js originating from the
https://github.com/joyent/node repository:
"""
Copyright Joyent, Inc. and other Node contributors. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
"""
@@ -0,0 +1,116 @@
# readable-stream
**_Node.js core streams for userland_**
[![npm status](https://img.shields.io/npm/v/readable-stream.svg)](https://npm.im/readable-stream)
[![node](https://img.shields.io/node/v/readable-stream.svg)](https://www.npmjs.org/package/readable-stream)
[![Node.js Build](https://github.com/nodejs/readable-stream/workflows/Node.js/badge.svg)](https://github.com/nodejs/readable-stream/actions?query=workflow%3ANode.js)
[![Browsers Build](https://github.com/nodejs/readable-stream/workflows/Browsers/badge.svg)](https://github.com/nodejs/readable-stream/actions?query=workflow%3ABrowsers)
```bash
npm install readable-stream
```
This package is a mirror of the streams implementations in Node.js 18.16.0.
Full documentation may be found on the [Node.js website](https://nodejs.org/dist/v18.16.0/docs/api/stream.html).
If you want to guarantee a stable streams base, regardless of what version of
Node you, or the users of your libraries are using, use **readable-stream** _only_ and avoid the _"stream"_ module in Node-core, for background see [this blogpost](http://r.va.gg/2014/06/why-i-dont-use-nodes-core-stream-module.html).
As of version 2.0.0 **readable-stream** uses semantic versioning.
## Version 4.x.x
v4.x.x of `readable-stream` is a cut from Node 18. This version supports Node 12, 14, 16 and 18, as well as evergreen browsers.
The breaking changes introduced by v4 are composed of the combined breaking changes in:
* [Node v12](https://nodejs.org/en/blog/release/v12.0.0/)
* [Node v13](https://nodejs.org/en/blog/release/v13.0.0/)
* [Node v14](https://nodejs.org/en/blog/release/v14.0.0/)
* [Node v15](https://nodejs.org/en/blog/release/v15.0.0/)
* [Node v16](https://nodejs.org/en/blog/release/v16.0.0/)
* [Node v17](https://nodejs.org/en/blog/release/v17.0.0/)
* [Node v18](https://nodejs.org/en/blog/release/v18.0.0/)
This also includes _many_ new features.
## Version 3.x.x
v3.x.x of `readable-stream` is a cut from Node 10. This version supports Node 6, 8, and 10, as well as evergreen browsers, IE 11 and latest Safari. The breaking changes introduced by v3 are composed by the combined breaking changes in [Node v9](https://nodejs.org/en/blog/release/v9.0.0/) and [Node v10](https://nodejs.org/en/blog/release/v10.0.0/), as follows:
1. Error codes: https://github.com/nodejs/node/pull/13310,
https://github.com/nodejs/node/pull/13291,
https://github.com/nodejs/node/pull/16589,
https://github.com/nodejs/node/pull/15042,
https://github.com/nodejs/node/pull/15665,
https://github.com/nodejs/readable-stream/pull/344
2. 'readable' have precedence over flowing
https://github.com/nodejs/node/pull/18994
3. make virtual methods errors consistent
https://github.com/nodejs/node/pull/18813
4. updated streams error handling
https://github.com/nodejs/node/pull/18438
5. writable.end should return this.
https://github.com/nodejs/node/pull/18780
6. readable continues to read when push('')
https://github.com/nodejs/node/pull/18211
7. add custom inspect to BufferList
https://github.com/nodejs/node/pull/17907
8. always defer 'readable' with nextTick
https://github.com/nodejs/node/pull/17979
## Version 2.x.x
v2.x.x of `readable-stream` is a cut of the stream module from Node 8 (there have been no semver-major changes from Node 4 to 8). This version supports all Node.js versions from 0.8, as well as evergreen browsers and IE 10 & 11.
# Usage
You can swap your `require('stream')` with `require('readable-stream')`
without any changes, if you are just using one of the main classes and
functions.
```js
const {
Readable,
Writable,
Transform,
Duplex,
pipeline,
finished
} = require('readable-stream')
```
Note that `require('stream')` will return `Stream`, while
`require('readable-stream')` will return `Readable`. We discourage using
whatever is exported directly, but rather use one of the properties as
shown in the example above.
## Usage In Browsers
You will need a bundler like [`browserify`](https://github.com/browserify/browserify#readme), [`webpack`](https://webpack.js.org/), [`parcel`](https://github.com/parcel-bundler/parcel#readme) or similar. Polyfills are no longer required since version 4.2.0.
# Streams Working Group
`readable-stream` is maintained by the Streams Working Group, which
oversees the development and maintenance of the Streams API within
Node.js. The responsibilities of the Streams Working Group include:
- Addressing stream issues on the Node.js issue tracker.
- Authoring and editing stream documentation within the Node.js project.
- Reviewing changes to stream subclasses within the Node.js project.
- Redirecting changes to streams from the Node.js project to this
project.
- Assisting in the implementation of stream providers within Node.js.
- Recommending versions of `readable-stream` to be included in Node.js.
- Messaging about the future of streams to give the community advance
notice of changes.
<a name="members"></a>
## Team Members
- **Mathias Buus** ([@mafintosh](https://github.com/mafintosh)) &lt;mathiasbuus@gmail.com&gt;
- **Matteo Collina** ([@mcollina](https://github.com/mcollina)) &lt;matteo.collina@gmail.com&gt;
- Release GPG key: 3ABC01543F22DD2239285CDD818674489FBC127E
- **Robert Nagy** ([@ronag](https://github.com/ronag)) &lt;ronagy@icloud.com&gt;
- **Vincent Weevers** ([@vweevers](https://github.com/vweevers)) &lt;mail@vincentweevers.nl&gt;
@@ -0,0 +1,4 @@
'use strict'
// Keep this file as an alias for the full stream module.
module.exports = require('./stream').Duplex
@@ -0,0 +1,4 @@
'use strict'
// Keep this file as an alias for the full stream module.
module.exports = require('./stream').PassThrough
@@ -0,0 +1,4 @@
'use strict'
// Keep this file as an alias for the full stream module.
module.exports = require('./stream').Readable
@@ -0,0 +1,4 @@
'use strict'
// Keep this file as an alias for the full stream module.
module.exports = require('./stream').Transform
@@ -0,0 +1,4 @@
'use strict'
// Keep this file as an alias for the full stream module.
module.exports = require('./stream').Writable
@@ -0,0 +1,49 @@
'use strict'
const { AbortError, codes } = require('../../ours/errors')
const { isNodeStream, isWebStream, kControllerErrorFunction } = require('./utils')
const eos = require('./end-of-stream')
const { ERR_INVALID_ARG_TYPE } = codes
// This method is inlined here for readable-stream
// It also does not allow for signal to not exist on the stream
// https://github.com/nodejs/node/pull/36061#discussion_r533718029
const validateAbortSignal = (signal, name) => {
if (typeof signal !== 'object' || !('aborted' in signal)) {
throw new ERR_INVALID_ARG_TYPE(name, 'AbortSignal', signal)
}
}
module.exports.addAbortSignal = function addAbortSignal(signal, stream) {
validateAbortSignal(signal, 'signal')
if (!isNodeStream(stream) && !isWebStream(stream)) {
throw new ERR_INVALID_ARG_TYPE('stream', ['ReadableStream', 'WritableStream', 'Stream'], stream)
}
return module.exports.addAbortSignalNoValidate(signal, stream)
}
module.exports.addAbortSignalNoValidate = function (signal, stream) {
if (typeof signal !== 'object' || !('aborted' in signal)) {
return stream
}
const onAbort = isNodeStream(stream)
? () => {
stream.destroy(
new AbortError(undefined, {
cause: signal.reason
})
)
}
: () => {
stream[kControllerErrorFunction](
new AbortError(undefined, {
cause: signal.reason
})
)
}
if (signal.aborted) {
onAbort()
} else {
signal.addEventListener('abort', onAbort)
eos(stream, () => signal.removeEventListener('abort', onAbort))
}
return stream
}
@@ -0,0 +1,157 @@
'use strict'
const { StringPrototypeSlice, SymbolIterator, TypedArrayPrototypeSet, Uint8Array } = require('../../ours/primordials')
const { Buffer } = require('buffer')
const { inspect } = require('../../ours/util')
module.exports = class BufferList {
constructor() {
this.head = null
this.tail = null
this.length = 0
}
push(v) {
const entry = {
data: v,
next: null
}
if (this.length > 0) this.tail.next = entry
else this.head = entry
this.tail = entry
++this.length
}
unshift(v) {
const entry = {
data: v,
next: this.head
}
if (this.length === 0) this.tail = entry
this.head = entry
++this.length
}
shift() {
if (this.length === 0) return
const ret = this.head.data
if (this.length === 1) this.head = this.tail = null
else this.head = this.head.next
--this.length
return ret
}
clear() {
this.head = this.tail = null
this.length = 0
}
join(s) {
if (this.length === 0) return ''
let p = this.head
let ret = '' + p.data
while ((p = p.next) !== null) ret += s + p.data
return ret
}
concat(n) {
if (this.length === 0) return Buffer.alloc(0)
const ret = Buffer.allocUnsafe(n >>> 0)
let p = this.head
let i = 0
while (p) {
TypedArrayPrototypeSet(ret, p.data, i)
i += p.data.length
p = p.next
}
return ret
}
// Consumes a specified amount of bytes or characters from the buffered data.
consume(n, hasStrings) {
const data = this.head.data
if (n < data.length) {
// `slice` is the same for buffers and strings.
const slice = data.slice(0, n)
this.head.data = data.slice(n)
return slice
}
if (n === data.length) {
// First chunk is a perfect match.
return this.shift()
}
// Result spans more than one buffer.
return hasStrings ? this._getString(n) : this._getBuffer(n)
}
first() {
return this.head.data
}
*[SymbolIterator]() {
for (let p = this.head; p; p = p.next) {
yield p.data
}
}
// Consumes a specified amount of characters from the buffered data.
_getString(n) {
let ret = ''
let p = this.head
let c = 0
do {
const str = p.data
if (n > str.length) {
ret += str
n -= str.length
} else {
if (n === str.length) {
ret += str
++c
if (p.next) this.head = p.next
else this.head = this.tail = null
} else {
ret += StringPrototypeSlice(str, 0, n)
this.head = p
p.data = StringPrototypeSlice(str, n)
}
break
}
++c
} while ((p = p.next) !== null)
this.length -= c
return ret
}
// Consumes a specified amount of bytes from the buffered data.
_getBuffer(n) {
const ret = Buffer.allocUnsafe(n)
const retLen = n
let p = this.head
let c = 0
do {
const buf = p.data
if (n > buf.length) {
TypedArrayPrototypeSet(ret, buf, retLen - n)
n -= buf.length
} else {
if (n === buf.length) {
TypedArrayPrototypeSet(ret, buf, retLen - n)
++c
if (p.next) this.head = p.next
else this.head = this.tail = null
} else {
TypedArrayPrototypeSet(ret, new Uint8Array(buf.buffer, buf.byteOffset, n), retLen - n)
this.head = p
p.data = buf.slice(n)
}
break
}
++c
} while ((p = p.next) !== null)
this.length -= c
return ret
}
// Make sure the linked list only shows the minimal necessary information.
[Symbol.for('nodejs.util.inspect.custom')](_, options) {
return inspect(this, {
...options,
// Only inspect one level.
depth: 0,
// It should not recurse.
customInspect: false
})
}
}
@@ -0,0 +1,194 @@
'use strict'
const { pipeline } = require('./pipeline')
const Duplex = require('./duplex')
const { destroyer } = require('./destroy')
const {
isNodeStream,
isReadable,
isWritable,
isWebStream,
isTransformStream,
isWritableStream,
isReadableStream
} = require('./utils')
const {
AbortError,
codes: { ERR_INVALID_ARG_VALUE, ERR_MISSING_ARGS }
} = require('../../ours/errors')
const eos = require('./end-of-stream')
module.exports = function compose(...streams) {
if (streams.length === 0) {
throw new ERR_MISSING_ARGS('streams')
}
if (streams.length === 1) {
return Duplex.from(streams[0])
}
const orgStreams = [...streams]
if (typeof streams[0] === 'function') {
streams[0] = Duplex.from(streams[0])
}
if (typeof streams[streams.length - 1] === 'function') {
const idx = streams.length - 1
streams[idx] = Duplex.from(streams[idx])
}
for (let n = 0; n < streams.length; ++n) {
if (!isNodeStream(streams[n]) && !isWebStream(streams[n])) {
// TODO(ronag): Add checks for non streams.
continue
}
if (
n < streams.length - 1 &&
!(isReadable(streams[n]) || isReadableStream(streams[n]) || isTransformStream(streams[n]))
) {
throw new ERR_INVALID_ARG_VALUE(`streams[${n}]`, orgStreams[n], 'must be readable')
}
if (n > 0 && !(isWritable(streams[n]) || isWritableStream(streams[n]) || isTransformStream(streams[n]))) {
throw new ERR_INVALID_ARG_VALUE(`streams[${n}]`, orgStreams[n], 'must be writable')
}
}
let ondrain
let onfinish
let onreadable
let onclose
let d
function onfinished(err) {
const cb = onclose
onclose = null
if (cb) {
cb(err)
} else if (err) {
d.destroy(err)
} else if (!readable && !writable) {
d.destroy()
}
}
const head = streams[0]
const tail = pipeline(streams, onfinished)
const writable = !!(isWritable(head) || isWritableStream(head) || isTransformStream(head))
const readable = !!(isReadable(tail) || isReadableStream(tail) || isTransformStream(tail))
// TODO(ronag): Avoid double buffering.
// Implement Writable/Readable/Duplex traits.
// See, https://github.com/nodejs/node/pull/33515.
d = new Duplex({
// TODO (ronag): highWaterMark?
writableObjectMode: !!(head !== null && head !== undefined && head.writableObjectMode),
readableObjectMode: !!(tail !== null && tail !== undefined && tail.writableObjectMode),
writable,
readable
})
if (writable) {
if (isNodeStream(head)) {
d._write = function (chunk, encoding, callback) {
if (head.write(chunk, encoding)) {
callback()
} else {
ondrain = callback
}
}
d._final = function (callback) {
head.end()
onfinish = callback
}
head.on('drain', function () {
if (ondrain) {
const cb = ondrain
ondrain = null
cb()
}
})
} else if (isWebStream(head)) {
const writable = isTransformStream(head) ? head.writable : head
const writer = writable.getWriter()
d._write = async function (chunk, encoding, callback) {
try {
await writer.ready
writer.write(chunk).catch(() => {})
callback()
} catch (err) {
callback(err)
}
}
d._final = async function (callback) {
try {
await writer.ready
writer.close().catch(() => {})
onfinish = callback
} catch (err) {
callback(err)
}
}
}
const toRead = isTransformStream(tail) ? tail.readable : tail
eos(toRead, () => {
if (onfinish) {
const cb = onfinish
onfinish = null
cb()
}
})
}
if (readable) {
if (isNodeStream(tail)) {
tail.on('readable', function () {
if (onreadable) {
const cb = onreadable
onreadable = null
cb()
}
})
tail.on('end', function () {
d.push(null)
})
d._read = function () {
while (true) {
const buf = tail.read()
if (buf === null) {
onreadable = d._read
return
}
if (!d.push(buf)) {
return
}
}
}
} else if (isWebStream(tail)) {
const readable = isTransformStream(tail) ? tail.readable : tail
const reader = readable.getReader()
d._read = async function () {
while (true) {
try {
const { value, done } = await reader.read()
if (!d.push(value)) {
return
}
if (done) {
d.push(null)
return
}
} catch {
return
}
}
}
}
}
d._destroy = function (err, callback) {
if (!err && onclose !== null) {
err = new AbortError()
}
onreadable = null
ondrain = null
onfinish = null
if (onclose === null) {
callback(err)
} else {
onclose = callback
if (isNodeStream(tail)) {
destroyer(tail, err)
}
}
}
return d
}
@@ -0,0 +1,290 @@
'use strict'
/* replacement start */
const process = require('process/')
/* replacement end */
const {
aggregateTwoErrors,
codes: { ERR_MULTIPLE_CALLBACK },
AbortError
} = require('../../ours/errors')
const { Symbol } = require('../../ours/primordials')
const { kDestroyed, isDestroyed, isFinished, isServerRequest } = require('./utils')
const kDestroy = Symbol('kDestroy')
const kConstruct = Symbol('kConstruct')
function checkError(err, w, r) {
if (err) {
// Avoid V8 leak, https://github.com/nodejs/node/pull/34103#issuecomment-652002364
err.stack // eslint-disable-line no-unused-expressions
if (w && !w.errored) {
w.errored = err
}
if (r && !r.errored) {
r.errored = err
}
}
}
// Backwards compat. cb() is undocumented and unused in core but
// unfortunately might be used by modules.
function destroy(err, cb) {
const r = this._readableState
const w = this._writableState
// With duplex streams we use the writable side for state.
const s = w || r
if ((w !== null && w !== undefined && w.destroyed) || (r !== null && r !== undefined && r.destroyed)) {
if (typeof cb === 'function') {
cb()
}
return this
}
// We set destroyed to true before firing error callbacks in order
// to make it re-entrance safe in case destroy() is called within callbacks
checkError(err, w, r)
if (w) {
w.destroyed = true
}
if (r) {
r.destroyed = true
}
// If still constructing then defer calling _destroy.
if (!s.constructed) {
this.once(kDestroy, function (er) {
_destroy(this, aggregateTwoErrors(er, err), cb)
})
} else {
_destroy(this, err, cb)
}
return this
}
function _destroy(self, err, cb) {
let called = false
function onDestroy(err) {
if (called) {
return
}
called = true
const r = self._readableState
const w = self._writableState
checkError(err, w, r)
if (w) {
w.closed = true
}
if (r) {
r.closed = true
}
if (typeof cb === 'function') {
cb(err)
}
if (err) {
process.nextTick(emitErrorCloseNT, self, err)
} else {
process.nextTick(emitCloseNT, self)
}
}
try {
self._destroy(err || null, onDestroy)
} catch (err) {
onDestroy(err)
}
}
function emitErrorCloseNT(self, err) {
emitErrorNT(self, err)
emitCloseNT(self)
}
function emitCloseNT(self) {
const r = self._readableState
const w = self._writableState
if (w) {
w.closeEmitted = true
}
if (r) {
r.closeEmitted = true
}
if ((w !== null && w !== undefined && w.emitClose) || (r !== null && r !== undefined && r.emitClose)) {
self.emit('close')
}
}
function emitErrorNT(self, err) {
const r = self._readableState
const w = self._writableState
if ((w !== null && w !== undefined && w.errorEmitted) || (r !== null && r !== undefined && r.errorEmitted)) {
return
}
if (w) {
w.errorEmitted = true
}
if (r) {
r.errorEmitted = true
}
self.emit('error', err)
}
function undestroy() {
const r = this._readableState
const w = this._writableState
if (r) {
r.constructed = true
r.closed = false
r.closeEmitted = false
r.destroyed = false
r.errored = null
r.errorEmitted = false
r.reading = false
r.ended = r.readable === false
r.endEmitted = r.readable === false
}
if (w) {
w.constructed = true
w.destroyed = false
w.closed = false
w.closeEmitted = false
w.errored = null
w.errorEmitted = false
w.finalCalled = false
w.prefinished = false
w.ended = w.writable === false
w.ending = w.writable === false
w.finished = w.writable === false
}
}
function errorOrDestroy(stream, err, sync) {
// We have tests that rely on errors being emitted
// in the same tick, so changing this is semver major.
// For now when you opt-in to autoDestroy we allow
// the error to be emitted nextTick. In a future
// semver major update we should change the default to this.
const r = stream._readableState
const w = stream._writableState
if ((w !== null && w !== undefined && w.destroyed) || (r !== null && r !== undefined && r.destroyed)) {
return this
}
if ((r !== null && r !== undefined && r.autoDestroy) || (w !== null && w !== undefined && w.autoDestroy))
stream.destroy(err)
else if (err) {
// Avoid V8 leak, https://github.com/nodejs/node/pull/34103#issuecomment-652002364
err.stack // eslint-disable-line no-unused-expressions
if (w && !w.errored) {
w.errored = err
}
if (r && !r.errored) {
r.errored = err
}
if (sync) {
process.nextTick(emitErrorNT, stream, err)
} else {
emitErrorNT(stream, err)
}
}
}
function construct(stream, cb) {
if (typeof stream._construct !== 'function') {
return
}
const r = stream._readableState
const w = stream._writableState
if (r) {
r.constructed = false
}
if (w) {
w.constructed = false
}
stream.once(kConstruct, cb)
if (stream.listenerCount(kConstruct) > 1) {
// Duplex
return
}
process.nextTick(constructNT, stream)
}
function constructNT(stream) {
let called = false
function onConstruct(err) {
if (called) {
errorOrDestroy(stream, err !== null && err !== undefined ? err : new ERR_MULTIPLE_CALLBACK())
return
}
called = true
const r = stream._readableState
const w = stream._writableState
const s = w || r
if (r) {
r.constructed = true
}
if (w) {
w.constructed = true
}
if (s.destroyed) {
stream.emit(kDestroy, err)
} else if (err) {
errorOrDestroy(stream, err, true)
} else {
process.nextTick(emitConstructNT, stream)
}
}
try {
stream._construct((err) => {
process.nextTick(onConstruct, err)
})
} catch (err) {
process.nextTick(onConstruct, err)
}
}
function emitConstructNT(stream) {
stream.emit(kConstruct)
}
function isRequest(stream) {
return (stream === null || stream === undefined ? undefined : stream.setHeader) && typeof stream.abort === 'function'
}
function emitCloseLegacy(stream) {
stream.emit('close')
}
function emitErrorCloseLegacy(stream, err) {
stream.emit('error', err)
process.nextTick(emitCloseLegacy, stream)
}
// Normalize destroy for legacy.
function destroyer(stream, err) {
if (!stream || isDestroyed(stream)) {
return
}
if (!err && !isFinished(stream)) {
err = new AbortError()
}
// TODO: Remove isRequest branches.
if (isServerRequest(stream)) {
stream.socket = null
stream.destroy(err)
} else if (isRequest(stream)) {
stream.abort()
} else if (isRequest(stream.req)) {
stream.req.abort()
} else if (typeof stream.destroy === 'function') {
stream.destroy(err)
} else if (typeof stream.close === 'function') {
// TODO: Don't lose err?
stream.close()
} else if (err) {
process.nextTick(emitErrorCloseLegacy, stream, err)
} else {
process.nextTick(emitCloseLegacy, stream)
}
if (!stream.destroyed) {
stream[kDestroyed] = true
}
}
module.exports = {
construct,
destroyer,
destroy,
undestroy,
errorOrDestroy
}
@@ -0,0 +1,143 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
// a duplex stream is just a stream that is both readable and writable.
// Since JS doesn't have multiple prototype inheritance, this class
// prototypically inherits from Readable, and then parasitically from
// Writable.
'use strict'
const {
ObjectDefineProperties,
ObjectGetOwnPropertyDescriptor,
ObjectKeys,
ObjectSetPrototypeOf
} = require('../../ours/primordials')
module.exports = Duplex
const Readable = require('./readable')
const Writable = require('./writable')
ObjectSetPrototypeOf(Duplex.prototype, Readable.prototype)
ObjectSetPrototypeOf(Duplex, Readable)
{
const keys = ObjectKeys(Writable.prototype)
// Allow the keys array to be GC'ed.
for (let i = 0; i < keys.length; i++) {
const method = keys[i]
if (!Duplex.prototype[method]) Duplex.prototype[method] = Writable.prototype[method]
}
}
function Duplex(options) {
if (!(this instanceof Duplex)) return new Duplex(options)
Readable.call(this, options)
Writable.call(this, options)
if (options) {
this.allowHalfOpen = options.allowHalfOpen !== false
if (options.readable === false) {
this._readableState.readable = false
this._readableState.ended = true
this._readableState.endEmitted = true
}
if (options.writable === false) {
this._writableState.writable = false
this._writableState.ending = true
this._writableState.ended = true
this._writableState.finished = true
}
} else {
this.allowHalfOpen = true
}
}
ObjectDefineProperties(Duplex.prototype, {
writable: {
__proto__: null,
...ObjectGetOwnPropertyDescriptor(Writable.prototype, 'writable')
},
writableHighWaterMark: {
__proto__: null,
...ObjectGetOwnPropertyDescriptor(Writable.prototype, 'writableHighWaterMark')
},
writableObjectMode: {
__proto__: null,
...ObjectGetOwnPropertyDescriptor(Writable.prototype, 'writableObjectMode')
},
writableBuffer: {
__proto__: null,
...ObjectGetOwnPropertyDescriptor(Writable.prototype, 'writableBuffer')
},
writableLength: {
__proto__: null,
...ObjectGetOwnPropertyDescriptor(Writable.prototype, 'writableLength')
},
writableFinished: {
__proto__: null,
...ObjectGetOwnPropertyDescriptor(Writable.prototype, 'writableFinished')
},
writableCorked: {
__proto__: null,
...ObjectGetOwnPropertyDescriptor(Writable.prototype, 'writableCorked')
},
writableEnded: {
__proto__: null,
...ObjectGetOwnPropertyDescriptor(Writable.prototype, 'writableEnded')
},
writableNeedDrain: {
__proto__: null,
...ObjectGetOwnPropertyDescriptor(Writable.prototype, 'writableNeedDrain')
},
destroyed: {
__proto__: null,
get() {
if (this._readableState === undefined || this._writableState === undefined) {
return false
}
return this._readableState.destroyed && this._writableState.destroyed
},
set(value) {
// Backward compatibility, the user is explicitly
// managing destroyed.
if (this._readableState && this._writableState) {
this._readableState.destroyed = value
this._writableState.destroyed = value
}
}
}
})
let webStreamsAdapters
// Lazy to avoid circular references
function lazyWebStreams() {
if (webStreamsAdapters === undefined) webStreamsAdapters = {}
return webStreamsAdapters
}
Duplex.fromWeb = function (pair, options) {
return lazyWebStreams().newStreamDuplexFromReadableWritablePair(pair, options)
}
Duplex.toWeb = function (duplex) {
return lazyWebStreams().newReadableWritablePairFromDuplex(duplex)
}
let duplexify
Duplex.from = function (body) {
if (!duplexify) {
duplexify = require('./duplexify')
}
return duplexify(body, 'body')
}
@@ -0,0 +1,379 @@
/* replacement start */
const process = require('process/')
/* replacement end */
;('use strict')
const bufferModule = require('buffer')
const {
isReadable,
isWritable,
isIterable,
isNodeStream,
isReadableNodeStream,
isWritableNodeStream,
isDuplexNodeStream
} = require('./utils')
const eos = require('./end-of-stream')
const {
AbortError,
codes: { ERR_INVALID_ARG_TYPE, ERR_INVALID_RETURN_VALUE }
} = require('../../ours/errors')
const { destroyer } = require('./destroy')
const Duplex = require('./duplex')
const Readable = require('./readable')
const { createDeferredPromise } = require('../../ours/util')
const from = require('./from')
const Blob = globalThis.Blob || bufferModule.Blob
const isBlob =
typeof Blob !== 'undefined'
? function isBlob(b) {
return b instanceof Blob
}
: function isBlob(b) {
return false
}
const AbortController = globalThis.AbortController || require('abort-controller').AbortController
const { FunctionPrototypeCall } = require('../../ours/primordials')
// This is needed for pre node 17.
class Duplexify extends Duplex {
constructor(options) {
super(options)
// https://github.com/nodejs/node/pull/34385
if ((options === null || options === undefined ? undefined : options.readable) === false) {
this._readableState.readable = false
this._readableState.ended = true
this._readableState.endEmitted = true
}
if ((options === null || options === undefined ? undefined : options.writable) === false) {
this._writableState.writable = false
this._writableState.ending = true
this._writableState.ended = true
this._writableState.finished = true
}
}
}
module.exports = function duplexify(body, name) {
if (isDuplexNodeStream(body)) {
return body
}
if (isReadableNodeStream(body)) {
return _duplexify({
readable: body
})
}
if (isWritableNodeStream(body)) {
return _duplexify({
writable: body
})
}
if (isNodeStream(body)) {
return _duplexify({
writable: false,
readable: false
})
}
// TODO: Webstreams
// if (isReadableStream(body)) {
// return _duplexify({ readable: Readable.fromWeb(body) });
// }
// TODO: Webstreams
// if (isWritableStream(body)) {
// return _duplexify({ writable: Writable.fromWeb(body) });
// }
if (typeof body === 'function') {
const { value, write, final, destroy } = fromAsyncGen(body)
if (isIterable(value)) {
return from(Duplexify, value, {
// TODO (ronag): highWaterMark?
objectMode: true,
write,
final,
destroy
})
}
const then = value === null || value === undefined ? undefined : value.then
if (typeof then === 'function') {
let d
const promise = FunctionPrototypeCall(
then,
value,
(val) => {
if (val != null) {
throw new ERR_INVALID_RETURN_VALUE('nully', 'body', val)
}
},
(err) => {
destroyer(d, err)
}
)
return (d = new Duplexify({
// TODO (ronag): highWaterMark?
objectMode: true,
readable: false,
write,
final(cb) {
final(async () => {
try {
await promise
process.nextTick(cb, null)
} catch (err) {
process.nextTick(cb, err)
}
})
},
destroy
}))
}
throw new ERR_INVALID_RETURN_VALUE('Iterable, AsyncIterable or AsyncFunction', name, value)
}
if (isBlob(body)) {
return duplexify(body.arrayBuffer())
}
if (isIterable(body)) {
return from(Duplexify, body, {
// TODO (ronag): highWaterMark?
objectMode: true,
writable: false
})
}
// TODO: Webstreams.
// if (
// isReadableStream(body?.readable) &&
// isWritableStream(body?.writable)
// ) {
// return Duplexify.fromWeb(body);
// }
if (
typeof (body === null || body === undefined ? undefined : body.writable) === 'object' ||
typeof (body === null || body === undefined ? undefined : body.readable) === 'object'
) {
const readable =
body !== null && body !== undefined && body.readable
? isReadableNodeStream(body === null || body === undefined ? undefined : body.readable)
? body === null || body === undefined
? undefined
: body.readable
: duplexify(body.readable)
: undefined
const writable =
body !== null && body !== undefined && body.writable
? isWritableNodeStream(body === null || body === undefined ? undefined : body.writable)
? body === null || body === undefined
? undefined
: body.writable
: duplexify(body.writable)
: undefined
return _duplexify({
readable,
writable
})
}
const then = body === null || body === undefined ? undefined : body.then
if (typeof then === 'function') {
let d
FunctionPrototypeCall(
then,
body,
(val) => {
if (val != null) {
d.push(val)
}
d.push(null)
},
(err) => {
destroyer(d, err)
}
)
return (d = new Duplexify({
objectMode: true,
writable: false,
read() {}
}))
}
throw new ERR_INVALID_ARG_TYPE(
name,
[
'Blob',
'ReadableStream',
'WritableStream',
'Stream',
'Iterable',
'AsyncIterable',
'Function',
'{ readable, writable } pair',
'Promise'
],
body
)
}
function fromAsyncGen(fn) {
let { promise, resolve } = createDeferredPromise()
const ac = new AbortController()
const signal = ac.signal
const value = fn(
(async function* () {
while (true) {
const _promise = promise
promise = null
const { chunk, done, cb } = await _promise
process.nextTick(cb)
if (done) return
if (signal.aborted)
throw new AbortError(undefined, {
cause: signal.reason
})
;({ promise, resolve } = createDeferredPromise())
yield chunk
}
})(),
{
signal
}
)
return {
value,
write(chunk, encoding, cb) {
const _resolve = resolve
resolve = null
_resolve({
chunk,
done: false,
cb
})
},
final(cb) {
const _resolve = resolve
resolve = null
_resolve({
done: true,
cb
})
},
destroy(err, cb) {
ac.abort()
cb(err)
}
}
}
function _duplexify(pair) {
const r = pair.readable && typeof pair.readable.read !== 'function' ? Readable.wrap(pair.readable) : pair.readable
const w = pair.writable
let readable = !!isReadable(r)
let writable = !!isWritable(w)
let ondrain
let onfinish
let onreadable
let onclose
let d
function onfinished(err) {
const cb = onclose
onclose = null
if (cb) {
cb(err)
} else if (err) {
d.destroy(err)
}
}
// TODO(ronag): Avoid double buffering.
// Implement Writable/Readable/Duplex traits.
// See, https://github.com/nodejs/node/pull/33515.
d = new Duplexify({
// TODO (ronag): highWaterMark?
readableObjectMode: !!(r !== null && r !== undefined && r.readableObjectMode),
writableObjectMode: !!(w !== null && w !== undefined && w.writableObjectMode),
readable,
writable
})
if (writable) {
eos(w, (err) => {
writable = false
if (err) {
destroyer(r, err)
}
onfinished(err)
})
d._write = function (chunk, encoding, callback) {
if (w.write(chunk, encoding)) {
callback()
} else {
ondrain = callback
}
}
d._final = function (callback) {
w.end()
onfinish = callback
}
w.on('drain', function () {
if (ondrain) {
const cb = ondrain
ondrain = null
cb()
}
})
w.on('finish', function () {
if (onfinish) {
const cb = onfinish
onfinish = null
cb()
}
})
}
if (readable) {
eos(r, (err) => {
readable = false
if (err) {
destroyer(r, err)
}
onfinished(err)
})
r.on('readable', function () {
if (onreadable) {
const cb = onreadable
onreadable = null
cb()
}
})
r.on('end', function () {
d.push(null)
})
d._read = function () {
while (true) {
const buf = r.read()
if (buf === null) {
onreadable = d._read
return
}
if (!d.push(buf)) {
return
}
}
}
}
d._destroy = function (err, callback) {
if (!err && onclose !== null) {
err = new AbortError()
}
onreadable = null
ondrain = null
onfinish = null
if (onclose === null) {
callback(err)
} else {
onclose = callback
destroyer(w, err)
destroyer(r, err)
}
}
return d
}
@@ -0,0 +1,281 @@
/* replacement start */
const process = require('process/')
/* replacement end */
// Ported from https://github.com/mafintosh/end-of-stream with
// permission from the author, Mathias Buus (@mafintosh).
;('use strict')
const { AbortError, codes } = require('../../ours/errors')
const { ERR_INVALID_ARG_TYPE, ERR_STREAM_PREMATURE_CLOSE } = codes
const { kEmptyObject, once } = require('../../ours/util')
const { validateAbortSignal, validateFunction, validateObject, validateBoolean } = require('../validators')
const { Promise, PromisePrototypeThen } = require('../../ours/primordials')
const {
isClosed,
isReadable,
isReadableNodeStream,
isReadableStream,
isReadableFinished,
isReadableErrored,
isWritable,
isWritableNodeStream,
isWritableStream,
isWritableFinished,
isWritableErrored,
isNodeStream,
willEmitClose: _willEmitClose,
kIsClosedPromise
} = require('./utils')
function isRequest(stream) {
return stream.setHeader && typeof stream.abort === 'function'
}
const nop = () => {}
function eos(stream, options, callback) {
var _options$readable, _options$writable
if (arguments.length === 2) {
callback = options
options = kEmptyObject
} else if (options == null) {
options = kEmptyObject
} else {
validateObject(options, 'options')
}
validateFunction(callback, 'callback')
validateAbortSignal(options.signal, 'options.signal')
callback = once(callback)
if (isReadableStream(stream) || isWritableStream(stream)) {
return eosWeb(stream, options, callback)
}
if (!isNodeStream(stream)) {
throw new ERR_INVALID_ARG_TYPE('stream', ['ReadableStream', 'WritableStream', 'Stream'], stream)
}
const readable =
(_options$readable = options.readable) !== null && _options$readable !== undefined
? _options$readable
: isReadableNodeStream(stream)
const writable =
(_options$writable = options.writable) !== null && _options$writable !== undefined
? _options$writable
: isWritableNodeStream(stream)
const wState = stream._writableState
const rState = stream._readableState
const onlegacyfinish = () => {
if (!stream.writable) {
onfinish()
}
}
// TODO (ronag): Improve soft detection to include core modules and
// common ecosystem modules that do properly emit 'close' but fail
// this generic check.
let willEmitClose =
_willEmitClose(stream) && isReadableNodeStream(stream) === readable && isWritableNodeStream(stream) === writable
let writableFinished = isWritableFinished(stream, false)
const onfinish = () => {
writableFinished = true
// Stream should not be destroyed here. If it is that
// means that user space is doing something differently and
// we cannot trust willEmitClose.
if (stream.destroyed) {
willEmitClose = false
}
if (willEmitClose && (!stream.readable || readable)) {
return
}
if (!readable || readableFinished) {
callback.call(stream)
}
}
let readableFinished = isReadableFinished(stream, false)
const onend = () => {
readableFinished = true
// Stream should not be destroyed here. If it is that
// means that user space is doing something differently and
// we cannot trust willEmitClose.
if (stream.destroyed) {
willEmitClose = false
}
if (willEmitClose && (!stream.writable || writable)) {
return
}
if (!writable || writableFinished) {
callback.call(stream)
}
}
const onerror = (err) => {
callback.call(stream, err)
}
let closed = isClosed(stream)
const onclose = () => {
closed = true
const errored = isWritableErrored(stream) || isReadableErrored(stream)
if (errored && typeof errored !== 'boolean') {
return callback.call(stream, errored)
}
if (readable && !readableFinished && isReadableNodeStream(stream, true)) {
if (!isReadableFinished(stream, false)) return callback.call(stream, new ERR_STREAM_PREMATURE_CLOSE())
}
if (writable && !writableFinished) {
if (!isWritableFinished(stream, false)) return callback.call(stream, new ERR_STREAM_PREMATURE_CLOSE())
}
callback.call(stream)
}
const onclosed = () => {
closed = true
const errored = isWritableErrored(stream) || isReadableErrored(stream)
if (errored && typeof errored !== 'boolean') {
return callback.call(stream, errored)
}
callback.call(stream)
}
const onrequest = () => {
stream.req.on('finish', onfinish)
}
if (isRequest(stream)) {
stream.on('complete', onfinish)
if (!willEmitClose) {
stream.on('abort', onclose)
}
if (stream.req) {
onrequest()
} else {
stream.on('request', onrequest)
}
} else if (writable && !wState) {
// legacy streams
stream.on('end', onlegacyfinish)
stream.on('close', onlegacyfinish)
}
// Not all streams will emit 'close' after 'aborted'.
if (!willEmitClose && typeof stream.aborted === 'boolean') {
stream.on('aborted', onclose)
}
stream.on('end', onend)
stream.on('finish', onfinish)
if (options.error !== false) {
stream.on('error', onerror)
}
stream.on('close', onclose)
if (closed) {
process.nextTick(onclose)
} else if (
(wState !== null && wState !== undefined && wState.errorEmitted) ||
(rState !== null && rState !== undefined && rState.errorEmitted)
) {
if (!willEmitClose) {
process.nextTick(onclosed)
}
} else if (
!readable &&
(!willEmitClose || isReadable(stream)) &&
(writableFinished || isWritable(stream) === false)
) {
process.nextTick(onclosed)
} else if (
!writable &&
(!willEmitClose || isWritable(stream)) &&
(readableFinished || isReadable(stream) === false)
) {
process.nextTick(onclosed)
} else if (rState && stream.req && stream.aborted) {
process.nextTick(onclosed)
}
const cleanup = () => {
callback = nop
stream.removeListener('aborted', onclose)
stream.removeListener('complete', onfinish)
stream.removeListener('abort', onclose)
stream.removeListener('request', onrequest)
if (stream.req) stream.req.removeListener('finish', onfinish)
stream.removeListener('end', onlegacyfinish)
stream.removeListener('close', onlegacyfinish)
stream.removeListener('finish', onfinish)
stream.removeListener('end', onend)
stream.removeListener('error', onerror)
stream.removeListener('close', onclose)
}
if (options.signal && !closed) {
const abort = () => {
// Keep it because cleanup removes it.
const endCallback = callback
cleanup()
endCallback.call(
stream,
new AbortError(undefined, {
cause: options.signal.reason
})
)
}
if (options.signal.aborted) {
process.nextTick(abort)
} else {
const originalCallback = callback
callback = once((...args) => {
options.signal.removeEventListener('abort', abort)
originalCallback.apply(stream, args)
})
options.signal.addEventListener('abort', abort)
}
}
return cleanup
}
function eosWeb(stream, options, callback) {
let isAborted = false
let abort = nop
if (options.signal) {
abort = () => {
isAborted = true
callback.call(
stream,
new AbortError(undefined, {
cause: options.signal.reason
})
)
}
if (options.signal.aborted) {
process.nextTick(abort)
} else {
const originalCallback = callback
callback = once((...args) => {
options.signal.removeEventListener('abort', abort)
originalCallback.apply(stream, args)
})
options.signal.addEventListener('abort', abort)
}
}
const resolverFn = (...args) => {
if (!isAborted) {
process.nextTick(() => callback.apply(stream, args))
}
}
PromisePrototypeThen(stream[kIsClosedPromise].promise, resolverFn, resolverFn)
return nop
}
function finished(stream, opts) {
var _opts
let autoCleanup = false
if (opts === null) {
opts = kEmptyObject
}
if ((_opts = opts) !== null && _opts !== undefined && _opts.cleanup) {
validateBoolean(opts.cleanup, 'cleanup')
autoCleanup = opts.cleanup
}
return new Promise((resolve, reject) => {
const cleanup = eos(stream, opts, (err) => {
if (autoCleanup) {
cleanup()
}
if (err) {
reject(err)
} else {
resolve()
}
})
})
}
module.exports = eos
module.exports.finished = finished
@@ -0,0 +1,98 @@
'use strict'
/* replacement start */
const process = require('process/')
/* replacement end */
const { PromisePrototypeThen, SymbolAsyncIterator, SymbolIterator } = require('../../ours/primordials')
const { Buffer } = require('buffer')
const { ERR_INVALID_ARG_TYPE, ERR_STREAM_NULL_VALUES } = require('../../ours/errors').codes
function from(Readable, iterable, opts) {
let iterator
if (typeof iterable === 'string' || iterable instanceof Buffer) {
return new Readable({
objectMode: true,
...opts,
read() {
this.push(iterable)
this.push(null)
}
})
}
let isAsync
if (iterable && iterable[SymbolAsyncIterator]) {
isAsync = true
iterator = iterable[SymbolAsyncIterator]()
} else if (iterable && iterable[SymbolIterator]) {
isAsync = false
iterator = iterable[SymbolIterator]()
} else {
throw new ERR_INVALID_ARG_TYPE('iterable', ['Iterable'], iterable)
}
const readable = new Readable({
objectMode: true,
highWaterMark: 1,
// TODO(ronag): What options should be allowed?
...opts
})
// Flag to protect against _read
// being called before last iteration completion.
let reading = false
readable._read = function () {
if (!reading) {
reading = true
next()
}
}
readable._destroy = function (error, cb) {
PromisePrototypeThen(
close(error),
() => process.nextTick(cb, error),
// nextTick is here in case cb throws
(e) => process.nextTick(cb, e || error)
)
}
async function close(error) {
const hadError = error !== undefined && error !== null
const hasThrow = typeof iterator.throw === 'function'
if (hadError && hasThrow) {
const { value, done } = await iterator.throw(error)
await value
if (done) {
return
}
}
if (typeof iterator.return === 'function') {
const { value } = await iterator.return()
await value
}
}
async function next() {
for (;;) {
try {
const { value, done } = isAsync ? await iterator.next() : iterator.next()
if (done) {
readable.push(null)
} else {
const res = value && typeof value.then === 'function' ? await value : value
if (res === null) {
reading = false
throw new ERR_STREAM_NULL_VALUES()
} else if (readable.push(res)) {
continue
} else {
reading = false
}
}
} catch (err) {
readable.destroy(err)
}
break
}
}
return readable
}
module.exports = from
@@ -0,0 +1,51 @@
// LazyTransform is a special type of Transform stream that is lazily loaded.
// This is used for performance with bi-API-ship: when two APIs are available
// for the stream, one conventional and one non-conventional.
'use strict'
const { ObjectDefineProperties, ObjectDefineProperty, ObjectSetPrototypeOf } = require('../../ours/primordials')
const stream = require('../../stream')
const { getDefaultEncoding } = require('../crypto/util')
module.exports = LazyTransform
function LazyTransform(options) {
this._options = options
}
ObjectSetPrototypeOf(LazyTransform.prototype, stream.Transform.prototype)
ObjectSetPrototypeOf(LazyTransform, stream.Transform)
function makeGetter(name) {
return function () {
stream.Transform.call(this, this._options)
this._writableState.decodeStrings = false
if (!this._options || !this._options.defaultEncoding) {
this._writableState.defaultEncoding = getDefaultEncoding()
}
return this[name]
}
}
function makeSetter(name) {
return function (val) {
ObjectDefineProperty(this, name, {
__proto__: null,
value: val,
enumerable: true,
configurable: true,
writable: true
})
}
}
ObjectDefineProperties(LazyTransform.prototype, {
_readableState: {
__proto__: null,
get: makeGetter('_readableState'),
set: makeSetter('_readableState'),
configurable: true,
enumerable: true
},
_writableState: {
__proto__: null,
get: makeGetter('_writableState'),
set: makeSetter('_writableState'),
configurable: true,
enumerable: true
}
})
@@ -0,0 +1,89 @@
'use strict'
const { ArrayIsArray, ObjectSetPrototypeOf } = require('../../ours/primordials')
const { EventEmitter: EE } = require('events')
function Stream(opts) {
EE.call(this, opts)
}
ObjectSetPrototypeOf(Stream.prototype, EE.prototype)
ObjectSetPrototypeOf(Stream, EE)
Stream.prototype.pipe = function (dest, options) {
const source = this
function ondata(chunk) {
if (dest.writable && dest.write(chunk) === false && source.pause) {
source.pause()
}
}
source.on('data', ondata)
function ondrain() {
if (source.readable && source.resume) {
source.resume()
}
}
dest.on('drain', ondrain)
// If the 'end' option is not supplied, dest.end() will be called when
// source gets the 'end' or 'close' events. Only dest.end() once.
if (!dest._isStdio && (!options || options.end !== false)) {
source.on('end', onend)
source.on('close', onclose)
}
let didOnEnd = false
function onend() {
if (didOnEnd) return
didOnEnd = true
dest.end()
}
function onclose() {
if (didOnEnd) return
didOnEnd = true
if (typeof dest.destroy === 'function') dest.destroy()
}
// Don't leave dangling pipes when there are errors.
function onerror(er) {
cleanup()
if (EE.listenerCount(this, 'error') === 0) {
this.emit('error', er)
}
}
prependListener(source, 'error', onerror)
prependListener(dest, 'error', onerror)
// Remove all the event listeners that were added.
function cleanup() {
source.removeListener('data', ondata)
dest.removeListener('drain', ondrain)
source.removeListener('end', onend)
source.removeListener('close', onclose)
source.removeListener('error', onerror)
dest.removeListener('error', onerror)
source.removeListener('end', cleanup)
source.removeListener('close', cleanup)
dest.removeListener('close', cleanup)
}
source.on('end', cleanup)
source.on('close', cleanup)
dest.on('close', cleanup)
dest.emit('pipe', source)
// Allow for unix-like usage: A.pipe(B).pipe(C)
return dest
}
function prependListener(emitter, event, fn) {
// Sadly this is not cacheable as some libraries bundle their own
// event emitter implementation with them.
if (typeof emitter.prependListener === 'function') return emitter.prependListener(event, fn)
// This is a hack to make sure that our error handler is attached before any
// userland ones. NEVER DO THIS. This is here only because this code needs
// to continue to work with older versions of Node.js that do not include
// the prependListener() method. The goal is to eventually remove this hack.
if (!emitter._events || !emitter._events[event]) emitter.on(event, fn)
else if (ArrayIsArray(emitter._events[event])) emitter._events[event].unshift(fn)
else emitter._events[event] = [fn, emitter._events[event]]
}
module.exports = {
Stream,
prependListener
}
@@ -0,0 +1,457 @@
'use strict'
const AbortController = globalThis.AbortController || require('abort-controller').AbortController
const {
codes: { ERR_INVALID_ARG_VALUE, ERR_INVALID_ARG_TYPE, ERR_MISSING_ARGS, ERR_OUT_OF_RANGE },
AbortError
} = require('../../ours/errors')
const { validateAbortSignal, validateInteger, validateObject } = require('../validators')
const kWeakHandler = require('../../ours/primordials').Symbol('kWeak')
const { finished } = require('./end-of-stream')
const staticCompose = require('./compose')
const { addAbortSignalNoValidate } = require('./add-abort-signal')
const { isWritable, isNodeStream } = require('./utils')
const {
ArrayPrototypePush,
MathFloor,
Number,
NumberIsNaN,
Promise,
PromiseReject,
PromisePrototypeThen,
Symbol
} = require('../../ours/primordials')
const kEmpty = Symbol('kEmpty')
const kEof = Symbol('kEof')
function compose(stream, options) {
if (options != null) {
validateObject(options, 'options')
}
if ((options === null || options === undefined ? undefined : options.signal) != null) {
validateAbortSignal(options.signal, 'options.signal')
}
if (isNodeStream(stream) && !isWritable(stream)) {
throw new ERR_INVALID_ARG_VALUE('stream', stream, 'must be writable')
}
const composedStream = staticCompose(this, stream)
if (options !== null && options !== undefined && options.signal) {
// Not validating as we already validated before
addAbortSignalNoValidate(options.signal, composedStream)
}
return composedStream
}
function map(fn, options) {
if (typeof fn !== 'function') {
throw new ERR_INVALID_ARG_TYPE('fn', ['Function', 'AsyncFunction'], fn)
}
if (options != null) {
validateObject(options, 'options')
}
if ((options === null || options === undefined ? undefined : options.signal) != null) {
validateAbortSignal(options.signal, 'options.signal')
}
let concurrency = 1
if ((options === null || options === undefined ? undefined : options.concurrency) != null) {
concurrency = MathFloor(options.concurrency)
}
validateInteger(concurrency, 'concurrency', 1)
return async function* map() {
var _options$signal, _options$signal2
const ac = new AbortController()
const stream = this
const queue = []
const signal = ac.signal
const signalOpt = {
signal
}
const abort = () => ac.abort()
if (
options !== null &&
options !== undefined &&
(_options$signal = options.signal) !== null &&
_options$signal !== undefined &&
_options$signal.aborted
) {
abort()
}
options === null || options === undefined
? undefined
: (_options$signal2 = options.signal) === null || _options$signal2 === undefined
? undefined
: _options$signal2.addEventListener('abort', abort)
let next
let resume
let done = false
function onDone() {
done = true
}
async function pump() {
try {
for await (let val of stream) {
var _val
if (done) {
return
}
if (signal.aborted) {
throw new AbortError()
}
try {
val = fn(val, signalOpt)
} catch (err) {
val = PromiseReject(err)
}
if (val === kEmpty) {
continue
}
if (typeof ((_val = val) === null || _val === undefined ? undefined : _val.catch) === 'function') {
val.catch(onDone)
}
queue.push(val)
if (next) {
next()
next = null
}
if (!done && queue.length && queue.length >= concurrency) {
await new Promise((resolve) => {
resume = resolve
})
}
}
queue.push(kEof)
} catch (err) {
const val = PromiseReject(err)
PromisePrototypeThen(val, undefined, onDone)
queue.push(val)
} finally {
var _options$signal3
done = true
if (next) {
next()
next = null
}
options === null || options === undefined
? undefined
: (_options$signal3 = options.signal) === null || _options$signal3 === undefined
? undefined
: _options$signal3.removeEventListener('abort', abort)
}
}
pump()
try {
while (true) {
while (queue.length > 0) {
const val = await queue[0]
if (val === kEof) {
return
}
if (signal.aborted) {
throw new AbortError()
}
if (val !== kEmpty) {
yield val
}
queue.shift()
if (resume) {
resume()
resume = null
}
}
await new Promise((resolve) => {
next = resolve
})
}
} finally {
ac.abort()
done = true
if (resume) {
resume()
resume = null
}
}
}.call(this)
}
function asIndexedPairs(options = undefined) {
if (options != null) {
validateObject(options, 'options')
}
if ((options === null || options === undefined ? undefined : options.signal) != null) {
validateAbortSignal(options.signal, 'options.signal')
}
return async function* asIndexedPairs() {
let index = 0
for await (const val of this) {
var _options$signal4
if (
options !== null &&
options !== undefined &&
(_options$signal4 = options.signal) !== null &&
_options$signal4 !== undefined &&
_options$signal4.aborted
) {
throw new AbortError({
cause: options.signal.reason
})
}
yield [index++, val]
}
}.call(this)
}
async function some(fn, options = undefined) {
for await (const unused of filter.call(this, fn, options)) {
return true
}
return false
}
async function every(fn, options = undefined) {
if (typeof fn !== 'function') {
throw new ERR_INVALID_ARG_TYPE('fn', ['Function', 'AsyncFunction'], fn)
}
// https://en.wikipedia.org/wiki/De_Morgan%27s_laws
return !(await some.call(
this,
async (...args) => {
return !(await fn(...args))
},
options
))
}
async function find(fn, options) {
for await (const result of filter.call(this, fn, options)) {
return result
}
return undefined
}
async function forEach(fn, options) {
if (typeof fn !== 'function') {
throw new ERR_INVALID_ARG_TYPE('fn', ['Function', 'AsyncFunction'], fn)
}
async function forEachFn(value, options) {
await fn(value, options)
return kEmpty
}
// eslint-disable-next-line no-unused-vars
for await (const unused of map.call(this, forEachFn, options));
}
function filter(fn, options) {
if (typeof fn !== 'function') {
throw new ERR_INVALID_ARG_TYPE('fn', ['Function', 'AsyncFunction'], fn)
}
async function filterFn(value, options) {
if (await fn(value, options)) {
return value
}
return kEmpty
}
return map.call(this, filterFn, options)
}
// Specific to provide better error to reduce since the argument is only
// missing if the stream has no items in it - but the code is still appropriate
class ReduceAwareErrMissingArgs extends ERR_MISSING_ARGS {
constructor() {
super('reduce')
this.message = 'Reduce of an empty stream requires an initial value'
}
}
async function reduce(reducer, initialValue, options) {
var _options$signal5
if (typeof reducer !== 'function') {
throw new ERR_INVALID_ARG_TYPE('reducer', ['Function', 'AsyncFunction'], reducer)
}
if (options != null) {
validateObject(options, 'options')
}
if ((options === null || options === undefined ? undefined : options.signal) != null) {
validateAbortSignal(options.signal, 'options.signal')
}
let hasInitialValue = arguments.length > 1
if (
options !== null &&
options !== undefined &&
(_options$signal5 = options.signal) !== null &&
_options$signal5 !== undefined &&
_options$signal5.aborted
) {
const err = new AbortError(undefined, {
cause: options.signal.reason
})
this.once('error', () => {}) // The error is already propagated
await finished(this.destroy(err))
throw err
}
const ac = new AbortController()
const signal = ac.signal
if (options !== null && options !== undefined && options.signal) {
const opts = {
once: true,
[kWeakHandler]: this
}
options.signal.addEventListener('abort', () => ac.abort(), opts)
}
let gotAnyItemFromStream = false
try {
for await (const value of this) {
var _options$signal6
gotAnyItemFromStream = true
if (
options !== null &&
options !== undefined &&
(_options$signal6 = options.signal) !== null &&
_options$signal6 !== undefined &&
_options$signal6.aborted
) {
throw new AbortError()
}
if (!hasInitialValue) {
initialValue = value
hasInitialValue = true
} else {
initialValue = await reducer(initialValue, value, {
signal
})
}
}
if (!gotAnyItemFromStream && !hasInitialValue) {
throw new ReduceAwareErrMissingArgs()
}
} finally {
ac.abort()
}
return initialValue
}
async function toArray(options) {
if (options != null) {
validateObject(options, 'options')
}
if ((options === null || options === undefined ? undefined : options.signal) != null) {
validateAbortSignal(options.signal, 'options.signal')
}
const result = []
for await (const val of this) {
var _options$signal7
if (
options !== null &&
options !== undefined &&
(_options$signal7 = options.signal) !== null &&
_options$signal7 !== undefined &&
_options$signal7.aborted
) {
throw new AbortError(undefined, {
cause: options.signal.reason
})
}
ArrayPrototypePush(result, val)
}
return result
}
function flatMap(fn, options) {
const values = map.call(this, fn, options)
return async function* flatMap() {
for await (const val of values) {
yield* val
}
}.call(this)
}
function toIntegerOrInfinity(number) {
// We coerce here to align with the spec
// https://github.com/tc39/proposal-iterator-helpers/issues/169
number = Number(number)
if (NumberIsNaN(number)) {
return 0
}
if (number < 0) {
throw new ERR_OUT_OF_RANGE('number', '>= 0', number)
}
return number
}
function drop(number, options = undefined) {
if (options != null) {
validateObject(options, 'options')
}
if ((options === null || options === undefined ? undefined : options.signal) != null) {
validateAbortSignal(options.signal, 'options.signal')
}
number = toIntegerOrInfinity(number)
return async function* drop() {
var _options$signal8
if (
options !== null &&
options !== undefined &&
(_options$signal8 = options.signal) !== null &&
_options$signal8 !== undefined &&
_options$signal8.aborted
) {
throw new AbortError()
}
for await (const val of this) {
var _options$signal9
if (
options !== null &&
options !== undefined &&
(_options$signal9 = options.signal) !== null &&
_options$signal9 !== undefined &&
_options$signal9.aborted
) {
throw new AbortError()
}
if (number-- <= 0) {
yield val
}
}
}.call(this)
}
function take(number, options = undefined) {
if (options != null) {
validateObject(options, 'options')
}
if ((options === null || options === undefined ? undefined : options.signal) != null) {
validateAbortSignal(options.signal, 'options.signal')
}
number = toIntegerOrInfinity(number)
return async function* take() {
var _options$signal10
if (
options !== null &&
options !== undefined &&
(_options$signal10 = options.signal) !== null &&
_options$signal10 !== undefined &&
_options$signal10.aborted
) {
throw new AbortError()
}
for await (const val of this) {
var _options$signal11
if (
options !== null &&
options !== undefined &&
(_options$signal11 = options.signal) !== null &&
_options$signal11 !== undefined &&
_options$signal11.aborted
) {
throw new AbortError()
}
if (number-- > 0) {
yield val
} else {
return
}
}
}.call(this)
}
module.exports.streamReturningOperators = {
asIndexedPairs,
drop,
filter,
flatMap,
map,
take,
compose
}
module.exports.promiseReturningOperators = {
every,
forEach,
reduce,
toArray,
some,
find
}
@@ -0,0 +1,39 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
// a passthrough stream.
// basically just the most minimal sort of Transform stream.
// Every written chunk gets output as-is.
'use strict'
const { ObjectSetPrototypeOf } = require('../../ours/primordials')
module.exports = PassThrough
const Transform = require('./transform')
ObjectSetPrototypeOf(PassThrough.prototype, Transform.prototype)
ObjectSetPrototypeOf(PassThrough, Transform)
function PassThrough(options) {
if (!(this instanceof PassThrough)) return new PassThrough(options)
Transform.call(this, options)
}
PassThrough.prototype._transform = function (chunk, encoding, cb) {
cb(null, chunk)
}
@@ -0,0 +1,465 @@
/* replacement start */
const process = require('process/')
/* replacement end */
// Ported from https://github.com/mafintosh/pump with
// permission from the author, Mathias Buus (@mafintosh).
;('use strict')
const { ArrayIsArray, Promise, SymbolAsyncIterator } = require('../../ours/primordials')
const eos = require('./end-of-stream')
const { once } = require('../../ours/util')
const destroyImpl = require('./destroy')
const Duplex = require('./duplex')
const {
aggregateTwoErrors,
codes: {
ERR_INVALID_ARG_TYPE,
ERR_INVALID_RETURN_VALUE,
ERR_MISSING_ARGS,
ERR_STREAM_DESTROYED,
ERR_STREAM_PREMATURE_CLOSE
},
AbortError
} = require('../../ours/errors')
const { validateFunction, validateAbortSignal } = require('../validators')
const {
isIterable,
isReadable,
isReadableNodeStream,
isNodeStream,
isTransformStream,
isWebStream,
isReadableStream,
isReadableEnded
} = require('./utils')
const AbortController = globalThis.AbortController || require('abort-controller').AbortController
let PassThrough
let Readable
function destroyer(stream, reading, writing) {
let finished = false
stream.on('close', () => {
finished = true
})
const cleanup = eos(
stream,
{
readable: reading,
writable: writing
},
(err) => {
finished = !err
}
)
return {
destroy: (err) => {
if (finished) return
finished = true
destroyImpl.destroyer(stream, err || new ERR_STREAM_DESTROYED('pipe'))
},
cleanup
}
}
function popCallback(streams) {
// Streams should never be an empty array. It should always contain at least
// a single stream. Therefore optimize for the average case instead of
// checking for length === 0 as well.
validateFunction(streams[streams.length - 1], 'streams[stream.length - 1]')
return streams.pop()
}
function makeAsyncIterable(val) {
if (isIterable(val)) {
return val
} else if (isReadableNodeStream(val)) {
// Legacy streams are not Iterable.
return fromReadable(val)
}
throw new ERR_INVALID_ARG_TYPE('val', ['Readable', 'Iterable', 'AsyncIterable'], val)
}
async function* fromReadable(val) {
if (!Readable) {
Readable = require('./readable')
}
yield* Readable.prototype[SymbolAsyncIterator].call(val)
}
async function pumpToNode(iterable, writable, finish, { end }) {
let error
let onresolve = null
const resume = (err) => {
if (err) {
error = err
}
if (onresolve) {
const callback = onresolve
onresolve = null
callback()
}
}
const wait = () =>
new Promise((resolve, reject) => {
if (error) {
reject(error)
} else {
onresolve = () => {
if (error) {
reject(error)
} else {
resolve()
}
}
}
})
writable.on('drain', resume)
const cleanup = eos(
writable,
{
readable: false
},
resume
)
try {
if (writable.writableNeedDrain) {
await wait()
}
for await (const chunk of iterable) {
if (!writable.write(chunk)) {
await wait()
}
}
if (end) {
writable.end()
}
await wait()
finish()
} catch (err) {
finish(error !== err ? aggregateTwoErrors(error, err) : err)
} finally {
cleanup()
writable.off('drain', resume)
}
}
async function pumpToWeb(readable, writable, finish, { end }) {
if (isTransformStream(writable)) {
writable = writable.writable
}
// https://streams.spec.whatwg.org/#example-manual-write-with-backpressure
const writer = writable.getWriter()
try {
for await (const chunk of readable) {
await writer.ready
writer.write(chunk).catch(() => {})
}
await writer.ready
if (end) {
await writer.close()
}
finish()
} catch (err) {
try {
await writer.abort(err)
finish(err)
} catch (err) {
finish(err)
}
}
}
function pipeline(...streams) {
return pipelineImpl(streams, once(popCallback(streams)))
}
function pipelineImpl(streams, callback, opts) {
if (streams.length === 1 && ArrayIsArray(streams[0])) {
streams = streams[0]
}
if (streams.length < 2) {
throw new ERR_MISSING_ARGS('streams')
}
const ac = new AbortController()
const signal = ac.signal
const outerSignal = opts === null || opts === undefined ? undefined : opts.signal
// Need to cleanup event listeners if last stream is readable
// https://github.com/nodejs/node/issues/35452
const lastStreamCleanup = []
validateAbortSignal(outerSignal, 'options.signal')
function abort() {
finishImpl(new AbortError())
}
outerSignal === null || outerSignal === undefined ? undefined : outerSignal.addEventListener('abort', abort)
let error
let value
const destroys = []
let finishCount = 0
function finish(err) {
finishImpl(err, --finishCount === 0)
}
function finishImpl(err, final) {
if (err && (!error || error.code === 'ERR_STREAM_PREMATURE_CLOSE')) {
error = err
}
if (!error && !final) {
return
}
while (destroys.length) {
destroys.shift()(error)
}
outerSignal === null || outerSignal === undefined ? undefined : outerSignal.removeEventListener('abort', abort)
ac.abort()
if (final) {
if (!error) {
lastStreamCleanup.forEach((fn) => fn())
}
process.nextTick(callback, error, value)
}
}
let ret
for (let i = 0; i < streams.length; i++) {
const stream = streams[i]
const reading = i < streams.length - 1
const writing = i > 0
const end = reading || (opts === null || opts === undefined ? undefined : opts.end) !== false
const isLastStream = i === streams.length - 1
if (isNodeStream(stream)) {
if (end) {
const { destroy, cleanup } = destroyer(stream, reading, writing)
destroys.push(destroy)
if (isReadable(stream) && isLastStream) {
lastStreamCleanup.push(cleanup)
}
}
// Catch stream errors that occur after pipe/pump has completed.
function onError(err) {
if (err && err.name !== 'AbortError' && err.code !== 'ERR_STREAM_PREMATURE_CLOSE') {
finish(err)
}
}
stream.on('error', onError)
if (isReadable(stream) && isLastStream) {
lastStreamCleanup.push(() => {
stream.removeListener('error', onError)
})
}
}
if (i === 0) {
if (typeof stream === 'function') {
ret = stream({
signal
})
if (!isIterable(ret)) {
throw new ERR_INVALID_RETURN_VALUE('Iterable, AsyncIterable or Stream', 'source', ret)
}
} else if (isIterable(stream) || isReadableNodeStream(stream) || isTransformStream(stream)) {
ret = stream
} else {
ret = Duplex.from(stream)
}
} else if (typeof stream === 'function') {
if (isTransformStream(ret)) {
var _ret
ret = makeAsyncIterable((_ret = ret) === null || _ret === undefined ? undefined : _ret.readable)
} else {
ret = makeAsyncIterable(ret)
}
ret = stream(ret, {
signal
})
if (reading) {
if (!isIterable(ret, true)) {
throw new ERR_INVALID_RETURN_VALUE('AsyncIterable', `transform[${i - 1}]`, ret)
}
} else {
var _ret2
if (!PassThrough) {
PassThrough = require('./passthrough')
}
// If the last argument to pipeline is not a stream
// we must create a proxy stream so that pipeline(...)
// always returns a stream which can be further
// composed through `.pipe(stream)`.
const pt = new PassThrough({
objectMode: true
})
// Handle Promises/A+ spec, `then` could be a getter that throws on
// second use.
const then = (_ret2 = ret) === null || _ret2 === undefined ? undefined : _ret2.then
if (typeof then === 'function') {
finishCount++
then.call(
ret,
(val) => {
value = val
if (val != null) {
pt.write(val)
}
if (end) {
pt.end()
}
process.nextTick(finish)
},
(err) => {
pt.destroy(err)
process.nextTick(finish, err)
}
)
} else if (isIterable(ret, true)) {
finishCount++
pumpToNode(ret, pt, finish, {
end
})
} else if (isReadableStream(ret) || isTransformStream(ret)) {
const toRead = ret.readable || ret
finishCount++
pumpToNode(toRead, pt, finish, {
end
})
} else {
throw new ERR_INVALID_RETURN_VALUE('AsyncIterable or Promise', 'destination', ret)
}
ret = pt
const { destroy, cleanup } = destroyer(ret, false, true)
destroys.push(destroy)
if (isLastStream) {
lastStreamCleanup.push(cleanup)
}
}
} else if (isNodeStream(stream)) {
if (isReadableNodeStream(ret)) {
finishCount += 2
const cleanup = pipe(ret, stream, finish, {
end
})
if (isReadable(stream) && isLastStream) {
lastStreamCleanup.push(cleanup)
}
} else if (isTransformStream(ret) || isReadableStream(ret)) {
const toRead = ret.readable || ret
finishCount++
pumpToNode(toRead, stream, finish, {
end
})
} else if (isIterable(ret)) {
finishCount++
pumpToNode(ret, stream, finish, {
end
})
} else {
throw new ERR_INVALID_ARG_TYPE(
'val',
['Readable', 'Iterable', 'AsyncIterable', 'ReadableStream', 'TransformStream'],
ret
)
}
ret = stream
} else if (isWebStream(stream)) {
if (isReadableNodeStream(ret)) {
finishCount++
pumpToWeb(makeAsyncIterable(ret), stream, finish, {
end
})
} else if (isReadableStream(ret) || isIterable(ret)) {
finishCount++
pumpToWeb(ret, stream, finish, {
end
})
} else if (isTransformStream(ret)) {
finishCount++
pumpToWeb(ret.readable, stream, finish, {
end
})
} else {
throw new ERR_INVALID_ARG_TYPE(
'val',
['Readable', 'Iterable', 'AsyncIterable', 'ReadableStream', 'TransformStream'],
ret
)
}
ret = stream
} else {
ret = Duplex.from(stream)
}
}
if (
(signal !== null && signal !== undefined && signal.aborted) ||
(outerSignal !== null && outerSignal !== undefined && outerSignal.aborted)
) {
process.nextTick(abort)
}
return ret
}
function pipe(src, dst, finish, { end }) {
let ended = false
dst.on('close', () => {
if (!ended) {
// Finish if the destination closes before the source has completed.
finish(new ERR_STREAM_PREMATURE_CLOSE())
}
})
src.pipe(dst, {
end: false
}) // If end is true we already will have a listener to end dst.
if (end) {
// Compat. Before node v10.12.0 stdio used to throw an error so
// pipe() did/does not end() stdio destinations.
// Now they allow it but "secretly" don't close the underlying fd.
function endFn() {
ended = true
dst.end()
}
if (isReadableEnded(src)) {
// End the destination if the source has already ended.
process.nextTick(endFn)
} else {
src.once('end', endFn)
}
} else {
finish()
}
eos(
src,
{
readable: true,
writable: false
},
(err) => {
const rState = src._readableState
if (
err &&
err.code === 'ERR_STREAM_PREMATURE_CLOSE' &&
rState &&
rState.ended &&
!rState.errored &&
!rState.errorEmitted
) {
// Some readable streams will emit 'close' before 'end'. However, since
// this is on the readable side 'end' should still be emitted if the
// stream has been ended and no error emitted. This should be allowed in
// favor of backwards compatibility. Since the stream is piped to a
// destination this should not result in any observable difference.
// We don't need to check if this is a writable premature close since
// eos will only fail with premature close on the reading side for
// duplex streams.
src.once('end', finish).once('error', finish)
} else {
finish(err)
}
}
)
return eos(
dst,
{
readable: false,
writable: true
},
finish
)
}
module.exports = {
pipelineImpl,
pipeline
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,27 @@
'use strict'
const { MathFloor, NumberIsInteger } = require('../../ours/primordials')
const { ERR_INVALID_ARG_VALUE } = require('../../ours/errors').codes
function highWaterMarkFrom(options, isDuplex, duplexKey) {
return options.highWaterMark != null ? options.highWaterMark : isDuplex ? options[duplexKey] : null
}
function getDefaultHighWaterMark(objectMode) {
return objectMode ? 16 : 16 * 1024
}
function getHighWaterMark(state, options, duplexKey, isDuplex) {
const hwm = highWaterMarkFrom(options, isDuplex, duplexKey)
if (hwm != null) {
if (!NumberIsInteger(hwm) || hwm < 0) {
const name = isDuplex ? `options.${duplexKey}` : 'options.highWaterMark'
throw new ERR_INVALID_ARG_VALUE(name, hwm)
}
return MathFloor(hwm)
}
// Default value
return getDefaultHighWaterMark(state.objectMode)
}
module.exports = {
getHighWaterMark,
getDefaultHighWaterMark
}
@@ -0,0 +1,180 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
// a transform stream is a readable/writable stream where you do
// something with the data. Sometimes it's called a "filter",
// but that's not a great name for it, since that implies a thing where
// some bits pass through, and others are simply ignored. (That would
// be a valid example of a transform, of course.)
//
// While the output is causally related to the input, it's not a
// necessarily symmetric or synchronous transformation. For example,
// a zlib stream might take multiple plain-text writes(), and then
// emit a single compressed chunk some time in the future.
//
// Here's how this works:
//
// The Transform stream has all the aspects of the readable and writable
// stream classes. When you write(chunk), that calls _write(chunk,cb)
// internally, and returns false if there's a lot of pending writes
// buffered up. When you call read(), that calls _read(n) until
// there's enough pending readable data buffered up.
//
// In a transform stream, the written data is placed in a buffer. When
// _read(n) is called, it transforms the queued up data, calling the
// buffered _write cb's as it consumes chunks. If consuming a single
// written chunk would result in multiple output chunks, then the first
// outputted bit calls the readcb, and subsequent chunks just go into
// the read buffer, and will cause it to emit 'readable' if necessary.
//
// This way, back-pressure is actually determined by the reading side,
// since _read has to be called to start processing a new chunk. However,
// a pathological inflate type of transform can cause excessive buffering
// here. For example, imagine a stream where every byte of input is
// interpreted as an integer from 0-255, and then results in that many
// bytes of output. Writing the 4 bytes {ff,ff,ff,ff} would result in
// 1kb of data being output. In this case, you could write a very small
// amount of input, and end up with a very large amount of output. In
// such a pathological inflating mechanism, there'd be no way to tell
// the system to stop doing the transform. A single 4MB write could
// cause the system to run out of memory.
//
// However, even in such a pathological case, only a single written chunk
// would be consumed, and then the rest would wait (un-transformed) until
// the results of the previous transformed chunk were consumed.
'use strict'
const { ObjectSetPrototypeOf, Symbol } = require('../../ours/primordials')
module.exports = Transform
const { ERR_METHOD_NOT_IMPLEMENTED } = require('../../ours/errors').codes
const Duplex = require('./duplex')
const { getHighWaterMark } = require('./state')
ObjectSetPrototypeOf(Transform.prototype, Duplex.prototype)
ObjectSetPrototypeOf(Transform, Duplex)
const kCallback = Symbol('kCallback')
function Transform(options) {
if (!(this instanceof Transform)) return new Transform(options)
// TODO (ronag): This should preferably always be
// applied but would be semver-major. Or even better;
// make Transform a Readable with the Writable interface.
const readableHighWaterMark = options ? getHighWaterMark(this, options, 'readableHighWaterMark', true) : null
if (readableHighWaterMark === 0) {
// A Duplex will buffer both on the writable and readable side while
// a Transform just wants to buffer hwm number of elements. To avoid
// buffering twice we disable buffering on the writable side.
options = {
...options,
highWaterMark: null,
readableHighWaterMark,
// TODO (ronag): 0 is not optimal since we have
// a "bug" where we check needDrain before calling _write and not after.
// Refs: https://github.com/nodejs/node/pull/32887
// Refs: https://github.com/nodejs/node/pull/35941
writableHighWaterMark: options.writableHighWaterMark || 0
}
}
Duplex.call(this, options)
// We have implemented the _read method, and done the other things
// that Readable wants before the first _read call, so unset the
// sync guard flag.
this._readableState.sync = false
this[kCallback] = null
if (options) {
if (typeof options.transform === 'function') this._transform = options.transform
if (typeof options.flush === 'function') this._flush = options.flush
}
// When the writable side finishes, then flush out anything remaining.
// Backwards compat. Some Transform streams incorrectly implement _final
// instead of or in addition to _flush. By using 'prefinish' instead of
// implementing _final we continue supporting this unfortunate use case.
this.on('prefinish', prefinish)
}
function final(cb) {
if (typeof this._flush === 'function' && !this.destroyed) {
this._flush((er, data) => {
if (er) {
if (cb) {
cb(er)
} else {
this.destroy(er)
}
return
}
if (data != null) {
this.push(data)
}
this.push(null)
if (cb) {
cb()
}
})
} else {
this.push(null)
if (cb) {
cb()
}
}
}
function prefinish() {
if (this._final !== final) {
final.call(this)
}
}
Transform.prototype._final = final
Transform.prototype._transform = function (chunk, encoding, callback) {
throw new ERR_METHOD_NOT_IMPLEMENTED('_transform()')
}
Transform.prototype._write = function (chunk, encoding, callback) {
const rState = this._readableState
const wState = this._writableState
const length = rState.length
this._transform(chunk, encoding, (err, val) => {
if (err) {
callback(err)
return
}
if (val != null) {
this.push(val)
}
if (
wState.ended ||
// Backwards compat.
length === rState.length ||
// Backwards compat.
rState.length < rState.highWaterMark
) {
callback()
} else {
this[kCallback] = callback
}
})
}
Transform.prototype._read = function () {
if (this[kCallback]) {
const callback = this[kCallback]
this[kCallback] = null
callback()
}
}
@@ -0,0 +1,321 @@
'use strict'
const { Symbol, SymbolAsyncIterator, SymbolIterator, SymbolFor } = require('../../ours/primordials')
const kDestroyed = Symbol('kDestroyed')
const kIsErrored = Symbol('kIsErrored')
const kIsReadable = Symbol('kIsReadable')
const kIsDisturbed = Symbol('kIsDisturbed')
const kIsClosedPromise = SymbolFor('nodejs.webstream.isClosedPromise')
const kControllerErrorFunction = SymbolFor('nodejs.webstream.controllerErrorFunction')
function isReadableNodeStream(obj, strict = false) {
var _obj$_readableState
return !!(
(
obj &&
typeof obj.pipe === 'function' &&
typeof obj.on === 'function' &&
(!strict || (typeof obj.pause === 'function' && typeof obj.resume === 'function')) &&
(!obj._writableState ||
((_obj$_readableState = obj._readableState) === null || _obj$_readableState === undefined
? undefined
: _obj$_readableState.readable) !== false) &&
// Duplex
(!obj._writableState || obj._readableState)
) // Writable has .pipe.
)
}
function isWritableNodeStream(obj) {
var _obj$_writableState
return !!(
(
obj &&
typeof obj.write === 'function' &&
typeof obj.on === 'function' &&
(!obj._readableState ||
((_obj$_writableState = obj._writableState) === null || _obj$_writableState === undefined
? undefined
: _obj$_writableState.writable) !== false)
) // Duplex
)
}
function isDuplexNodeStream(obj) {
return !!(
obj &&
typeof obj.pipe === 'function' &&
obj._readableState &&
typeof obj.on === 'function' &&
typeof obj.write === 'function'
)
}
function isNodeStream(obj) {
return (
obj &&
(obj._readableState ||
obj._writableState ||
(typeof obj.write === 'function' && typeof obj.on === 'function') ||
(typeof obj.pipe === 'function' && typeof obj.on === 'function'))
)
}
function isReadableStream(obj) {
return !!(
obj &&
!isNodeStream(obj) &&
typeof obj.pipeThrough === 'function' &&
typeof obj.getReader === 'function' &&
typeof obj.cancel === 'function'
)
}
function isWritableStream(obj) {
return !!(obj && !isNodeStream(obj) && typeof obj.getWriter === 'function' && typeof obj.abort === 'function')
}
function isTransformStream(obj) {
return !!(obj && !isNodeStream(obj) && typeof obj.readable === 'object' && typeof obj.writable === 'object')
}
function isWebStream(obj) {
return isReadableStream(obj) || isWritableStream(obj) || isTransformStream(obj)
}
function isIterable(obj, isAsync) {
if (obj == null) return false
if (isAsync === true) return typeof obj[SymbolAsyncIterator] === 'function'
if (isAsync === false) return typeof obj[SymbolIterator] === 'function'
return typeof obj[SymbolAsyncIterator] === 'function' || typeof obj[SymbolIterator] === 'function'
}
function isDestroyed(stream) {
if (!isNodeStream(stream)) return null
const wState = stream._writableState
const rState = stream._readableState
const state = wState || rState
return !!(stream.destroyed || stream[kDestroyed] || (state !== null && state !== undefined && state.destroyed))
}
// Have been end():d.
function isWritableEnded(stream) {
if (!isWritableNodeStream(stream)) return null
if (stream.writableEnded === true) return true
const wState = stream._writableState
if (wState !== null && wState !== undefined && wState.errored) return false
if (typeof (wState === null || wState === undefined ? undefined : wState.ended) !== 'boolean') return null
return wState.ended
}
// Have emitted 'finish'.
function isWritableFinished(stream, strict) {
if (!isWritableNodeStream(stream)) return null
if (stream.writableFinished === true) return true
const wState = stream._writableState
if (wState !== null && wState !== undefined && wState.errored) return false
if (typeof (wState === null || wState === undefined ? undefined : wState.finished) !== 'boolean') return null
return !!(wState.finished || (strict === false && wState.ended === true && wState.length === 0))
}
// Have been push(null):d.
function isReadableEnded(stream) {
if (!isReadableNodeStream(stream)) return null
if (stream.readableEnded === true) return true
const rState = stream._readableState
if (!rState || rState.errored) return false
if (typeof (rState === null || rState === undefined ? undefined : rState.ended) !== 'boolean') return null
return rState.ended
}
// Have emitted 'end'.
function isReadableFinished(stream, strict) {
if (!isReadableNodeStream(stream)) return null
const rState = stream._readableState
if (rState !== null && rState !== undefined && rState.errored) return false
if (typeof (rState === null || rState === undefined ? undefined : rState.endEmitted) !== 'boolean') return null
return !!(rState.endEmitted || (strict === false && rState.ended === true && rState.length === 0))
}
function isReadable(stream) {
if (stream && stream[kIsReadable] != null) return stream[kIsReadable]
if (typeof (stream === null || stream === undefined ? undefined : stream.readable) !== 'boolean') return null
if (isDestroyed(stream)) return false
return isReadableNodeStream(stream) && stream.readable && !isReadableFinished(stream)
}
function isWritable(stream) {
if (typeof (stream === null || stream === undefined ? undefined : stream.writable) !== 'boolean') return null
if (isDestroyed(stream)) return false
return isWritableNodeStream(stream) && stream.writable && !isWritableEnded(stream)
}
function isFinished(stream, opts) {
if (!isNodeStream(stream)) {
return null
}
if (isDestroyed(stream)) {
return true
}
if ((opts === null || opts === undefined ? undefined : opts.readable) !== false && isReadable(stream)) {
return false
}
if ((opts === null || opts === undefined ? undefined : opts.writable) !== false && isWritable(stream)) {
return false
}
return true
}
function isWritableErrored(stream) {
var _stream$_writableStat, _stream$_writableStat2
if (!isNodeStream(stream)) {
return null
}
if (stream.writableErrored) {
return stream.writableErrored
}
return (_stream$_writableStat =
(_stream$_writableStat2 = stream._writableState) === null || _stream$_writableStat2 === undefined
? undefined
: _stream$_writableStat2.errored) !== null && _stream$_writableStat !== undefined
? _stream$_writableStat
: null
}
function isReadableErrored(stream) {
var _stream$_readableStat, _stream$_readableStat2
if (!isNodeStream(stream)) {
return null
}
if (stream.readableErrored) {
return stream.readableErrored
}
return (_stream$_readableStat =
(_stream$_readableStat2 = stream._readableState) === null || _stream$_readableStat2 === undefined
? undefined
: _stream$_readableStat2.errored) !== null && _stream$_readableStat !== undefined
? _stream$_readableStat
: null
}
function isClosed(stream) {
if (!isNodeStream(stream)) {
return null
}
if (typeof stream.closed === 'boolean') {
return stream.closed
}
const wState = stream._writableState
const rState = stream._readableState
if (
typeof (wState === null || wState === undefined ? undefined : wState.closed) === 'boolean' ||
typeof (rState === null || rState === undefined ? undefined : rState.closed) === 'boolean'
) {
return (
(wState === null || wState === undefined ? undefined : wState.closed) ||
(rState === null || rState === undefined ? undefined : rState.closed)
)
}
if (typeof stream._closed === 'boolean' && isOutgoingMessage(stream)) {
return stream._closed
}
return null
}
function isOutgoingMessage(stream) {
return (
typeof stream._closed === 'boolean' &&
typeof stream._defaultKeepAlive === 'boolean' &&
typeof stream._removedConnection === 'boolean' &&
typeof stream._removedContLen === 'boolean'
)
}
function isServerResponse(stream) {
return typeof stream._sent100 === 'boolean' && isOutgoingMessage(stream)
}
function isServerRequest(stream) {
var _stream$req
return (
typeof stream._consuming === 'boolean' &&
typeof stream._dumped === 'boolean' &&
((_stream$req = stream.req) === null || _stream$req === undefined ? undefined : _stream$req.upgradeOrConnect) ===
undefined
)
}
function willEmitClose(stream) {
if (!isNodeStream(stream)) return null
const wState = stream._writableState
const rState = stream._readableState
const state = wState || rState
return (
(!state && isServerResponse(stream)) || !!(state && state.autoDestroy && state.emitClose && state.closed === false)
)
}
function isDisturbed(stream) {
var _stream$kIsDisturbed
return !!(
stream &&
((_stream$kIsDisturbed = stream[kIsDisturbed]) !== null && _stream$kIsDisturbed !== undefined
? _stream$kIsDisturbed
: stream.readableDidRead || stream.readableAborted)
)
}
function isErrored(stream) {
var _ref,
_ref2,
_ref3,
_ref4,
_ref5,
_stream$kIsErrored,
_stream$_readableStat3,
_stream$_writableStat3,
_stream$_readableStat4,
_stream$_writableStat4
return !!(
stream &&
((_ref =
(_ref2 =
(_ref3 =
(_ref4 =
(_ref5 =
(_stream$kIsErrored = stream[kIsErrored]) !== null && _stream$kIsErrored !== undefined
? _stream$kIsErrored
: stream.readableErrored) !== null && _ref5 !== undefined
? _ref5
: stream.writableErrored) !== null && _ref4 !== undefined
? _ref4
: (_stream$_readableStat3 = stream._readableState) === null || _stream$_readableStat3 === undefined
? undefined
: _stream$_readableStat3.errorEmitted) !== null && _ref3 !== undefined
? _ref3
: (_stream$_writableStat3 = stream._writableState) === null || _stream$_writableStat3 === undefined
? undefined
: _stream$_writableStat3.errorEmitted) !== null && _ref2 !== undefined
? _ref2
: (_stream$_readableStat4 = stream._readableState) === null || _stream$_readableStat4 === undefined
? undefined
: _stream$_readableStat4.errored) !== null && _ref !== undefined
? _ref
: (_stream$_writableStat4 = stream._writableState) === null || _stream$_writableStat4 === undefined
? undefined
: _stream$_writableStat4.errored)
)
}
module.exports = {
kDestroyed,
isDisturbed,
kIsDisturbed,
isErrored,
kIsErrored,
isReadable,
kIsReadable,
kIsClosedPromise,
kControllerErrorFunction,
isClosed,
isDestroyed,
isDuplexNodeStream,
isFinished,
isIterable,
isReadableNodeStream,
isReadableStream,
isReadableEnded,
isReadableFinished,
isReadableErrored,
isNodeStream,
isWebStream,
isWritable,
isWritableNodeStream,
isWritableStream,
isWritableEnded,
isWritableFinished,
isWritableErrored,
isServerRequest,
isServerResponse,
willEmitClose,
isTransformStream
}
@@ -0,0 +1,817 @@
/* replacement start */
const process = require('process/')
/* replacement end */
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
// A bit simpler than readable streams.
// Implement an async ._write(chunk, encoding, cb), and it'll handle all
// the drain event emission and buffering.
;('use strict')
const {
ArrayPrototypeSlice,
Error,
FunctionPrototypeSymbolHasInstance,
ObjectDefineProperty,
ObjectDefineProperties,
ObjectSetPrototypeOf,
StringPrototypeToLowerCase,
Symbol,
SymbolHasInstance
} = require('../../ours/primordials')
module.exports = Writable
Writable.WritableState = WritableState
const { EventEmitter: EE } = require('events')
const Stream = require('./legacy').Stream
const { Buffer } = require('buffer')
const destroyImpl = require('./destroy')
const { addAbortSignal } = require('./add-abort-signal')
const { getHighWaterMark, getDefaultHighWaterMark } = require('./state')
const {
ERR_INVALID_ARG_TYPE,
ERR_METHOD_NOT_IMPLEMENTED,
ERR_MULTIPLE_CALLBACK,
ERR_STREAM_CANNOT_PIPE,
ERR_STREAM_DESTROYED,
ERR_STREAM_ALREADY_FINISHED,
ERR_STREAM_NULL_VALUES,
ERR_STREAM_WRITE_AFTER_END,
ERR_UNKNOWN_ENCODING
} = require('../../ours/errors').codes
const { errorOrDestroy } = destroyImpl
ObjectSetPrototypeOf(Writable.prototype, Stream.prototype)
ObjectSetPrototypeOf(Writable, Stream)
function nop() {}
const kOnFinished = Symbol('kOnFinished')
function WritableState(options, stream, isDuplex) {
// Duplex streams are both readable and writable, but share
// the same options object.
// However, some cases require setting options to different
// values for the readable and the writable sides of the duplex stream,
// e.g. options.readableObjectMode vs. options.writableObjectMode, etc.
if (typeof isDuplex !== 'boolean') isDuplex = stream instanceof require('./duplex')
// Object stream flag to indicate whether or not this stream
// contains buffers or objects.
this.objectMode = !!(options && options.objectMode)
if (isDuplex) this.objectMode = this.objectMode || !!(options && options.writableObjectMode)
// The point at which write() starts returning false
// Note: 0 is a valid value, means that we always return false if
// the entire buffer is not flushed immediately on write().
this.highWaterMark = options
? getHighWaterMark(this, options, 'writableHighWaterMark', isDuplex)
: getDefaultHighWaterMark(false)
// if _final has been called.
this.finalCalled = false
// drain event flag.
this.needDrain = false
// At the start of calling end()
this.ending = false
// When end() has been called, and returned.
this.ended = false
// When 'finish' is emitted.
this.finished = false
// Has it been destroyed
this.destroyed = false
// Should we decode strings into buffers before passing to _write?
// this is here so that some node-core streams can optimize string
// handling at a lower level.
const noDecode = !!(options && options.decodeStrings === false)
this.decodeStrings = !noDecode
// Crypto is kind of old and crusty. Historically, its default string
// encoding is 'binary' so we have to make this configurable.
// Everything else in the universe uses 'utf8', though.
this.defaultEncoding = (options && options.defaultEncoding) || 'utf8'
// Not an actual buffer we keep track of, but a measurement
// of how much we're waiting to get pushed to some underlying
// socket or file.
this.length = 0
// A flag to see when we're in the middle of a write.
this.writing = false
// When true all writes will be buffered until .uncork() call.
this.corked = 0
// A flag to be able to tell if the onwrite cb is called immediately,
// or on a later tick. We set this to true at first, because any
// actions that shouldn't happen until "later" should generally also
// not happen before the first write call.
this.sync = true
// A flag to know if we're processing previously buffered items, which
// may call the _write() callback in the same tick, so that we don't
// end up in an overlapped onwrite situation.
this.bufferProcessing = false
// The callback that's passed to _write(chunk, cb).
this.onwrite = onwrite.bind(undefined, stream)
// The callback that the user supplies to write(chunk, encoding, cb).
this.writecb = null
// The amount that is being written when _write is called.
this.writelen = 0
// Storage for data passed to the afterWrite() callback in case of
// synchronous _write() completion.
this.afterWriteTickInfo = null
resetBuffer(this)
// Number of pending user-supplied write callbacks
// this must be 0 before 'finish' can be emitted.
this.pendingcb = 0
// Stream is still being constructed and cannot be
// destroyed until construction finished or failed.
// Async construction is opt in, therefore we start as
// constructed.
this.constructed = true
// Emit prefinish if the only thing we're waiting for is _write cbs
// This is relevant for synchronous Transform streams.
this.prefinished = false
// True if the error was already emitted and should not be thrown again.
this.errorEmitted = false
// Should close be emitted on destroy. Defaults to true.
this.emitClose = !options || options.emitClose !== false
// Should .destroy() be called after 'finish' (and potentially 'end').
this.autoDestroy = !options || options.autoDestroy !== false
// Indicates whether the stream has errored. When true all write() calls
// should return false. This is needed since when autoDestroy
// is disabled we need a way to tell whether the stream has failed.
this.errored = null
// Indicates whether the stream has finished destroying.
this.closed = false
// True if close has been emitted or would have been emitted
// depending on emitClose.
this.closeEmitted = false
this[kOnFinished] = []
}
function resetBuffer(state) {
state.buffered = []
state.bufferedIndex = 0
state.allBuffers = true
state.allNoop = true
}
WritableState.prototype.getBuffer = function getBuffer() {
return ArrayPrototypeSlice(this.buffered, this.bufferedIndex)
}
ObjectDefineProperty(WritableState.prototype, 'bufferedRequestCount', {
__proto__: null,
get() {
return this.buffered.length - this.bufferedIndex
}
})
function Writable(options) {
// Writable ctor is applied to Duplexes, too.
// `realHasInstance` is necessary because using plain `instanceof`
// would return false, as no `_writableState` property is attached.
// Trying to use the custom `instanceof` for Writable here will also break the
// Node.js LazyTransform implementation, which has a non-trivial getter for
// `_writableState` that would lead to infinite recursion.
// Checking for a Stream.Duplex instance is faster here instead of inside
// the WritableState constructor, at least with V8 6.5.
const isDuplex = this instanceof require('./duplex')
if (!isDuplex && !FunctionPrototypeSymbolHasInstance(Writable, this)) return new Writable(options)
this._writableState = new WritableState(options, this, isDuplex)
if (options) {
if (typeof options.write === 'function') this._write = options.write
if (typeof options.writev === 'function') this._writev = options.writev
if (typeof options.destroy === 'function') this._destroy = options.destroy
if (typeof options.final === 'function') this._final = options.final
if (typeof options.construct === 'function') this._construct = options.construct
if (options.signal) addAbortSignal(options.signal, this)
}
Stream.call(this, options)
destroyImpl.construct(this, () => {
const state = this._writableState
if (!state.writing) {
clearBuffer(this, state)
}
finishMaybe(this, state)
})
}
ObjectDefineProperty(Writable, SymbolHasInstance, {
__proto__: null,
value: function (object) {
if (FunctionPrototypeSymbolHasInstance(this, object)) return true
if (this !== Writable) return false
return object && object._writableState instanceof WritableState
}
})
// Otherwise people can pipe Writable streams, which is just wrong.
Writable.prototype.pipe = function () {
errorOrDestroy(this, new ERR_STREAM_CANNOT_PIPE())
}
function _write(stream, chunk, encoding, cb) {
const state = stream._writableState
if (typeof encoding === 'function') {
cb = encoding
encoding = state.defaultEncoding
} else {
if (!encoding) encoding = state.defaultEncoding
else if (encoding !== 'buffer' && !Buffer.isEncoding(encoding)) throw new ERR_UNKNOWN_ENCODING(encoding)
if (typeof cb !== 'function') cb = nop
}
if (chunk === null) {
throw new ERR_STREAM_NULL_VALUES()
} else if (!state.objectMode) {
if (typeof chunk === 'string') {
if (state.decodeStrings !== false) {
chunk = Buffer.from(chunk, encoding)
encoding = 'buffer'
}
} else if (chunk instanceof Buffer) {
encoding = 'buffer'
} else if (Stream._isUint8Array(chunk)) {
chunk = Stream._uint8ArrayToBuffer(chunk)
encoding = 'buffer'
} else {
throw new ERR_INVALID_ARG_TYPE('chunk', ['string', 'Buffer', 'Uint8Array'], chunk)
}
}
let err
if (state.ending) {
err = new ERR_STREAM_WRITE_AFTER_END()
} else if (state.destroyed) {
err = new ERR_STREAM_DESTROYED('write')
}
if (err) {
process.nextTick(cb, err)
errorOrDestroy(stream, err, true)
return err
}
state.pendingcb++
return writeOrBuffer(stream, state, chunk, encoding, cb)
}
Writable.prototype.write = function (chunk, encoding, cb) {
return _write(this, chunk, encoding, cb) === true
}
Writable.prototype.cork = function () {
this._writableState.corked++
}
Writable.prototype.uncork = function () {
const state = this._writableState
if (state.corked) {
state.corked--
if (!state.writing) clearBuffer(this, state)
}
}
Writable.prototype.setDefaultEncoding = function setDefaultEncoding(encoding) {
// node::ParseEncoding() requires lower case.
if (typeof encoding === 'string') encoding = StringPrototypeToLowerCase(encoding)
if (!Buffer.isEncoding(encoding)) throw new ERR_UNKNOWN_ENCODING(encoding)
this._writableState.defaultEncoding = encoding
return this
}
// If we're already writing something, then just put this
// in the queue, and wait our turn. Otherwise, call _write
// If we return false, then we need a drain event, so set that flag.
function writeOrBuffer(stream, state, chunk, encoding, callback) {
const len = state.objectMode ? 1 : chunk.length
state.length += len
// stream._write resets state.length
const ret = state.length < state.highWaterMark
// We must ensure that previous needDrain will not be reset to false.
if (!ret) state.needDrain = true
if (state.writing || state.corked || state.errored || !state.constructed) {
state.buffered.push({
chunk,
encoding,
callback
})
if (state.allBuffers && encoding !== 'buffer') {
state.allBuffers = false
}
if (state.allNoop && callback !== nop) {
state.allNoop = false
}
} else {
state.writelen = len
state.writecb = callback
state.writing = true
state.sync = true
stream._write(chunk, encoding, state.onwrite)
state.sync = false
}
// Return false if errored or destroyed in order to break
// any synchronous while(stream.write(data)) loops.
return ret && !state.errored && !state.destroyed
}
function doWrite(stream, state, writev, len, chunk, encoding, cb) {
state.writelen = len
state.writecb = cb
state.writing = true
state.sync = true
if (state.destroyed) state.onwrite(new ERR_STREAM_DESTROYED('write'))
else if (writev) stream._writev(chunk, state.onwrite)
else stream._write(chunk, encoding, state.onwrite)
state.sync = false
}
function onwriteError(stream, state, er, cb) {
--state.pendingcb
cb(er)
// Ensure callbacks are invoked even when autoDestroy is
// not enabled. Passing `er` here doesn't make sense since
// it's related to one specific write, not to the buffered
// writes.
errorBuffer(state)
// This can emit error, but error must always follow cb.
errorOrDestroy(stream, er)
}
function onwrite(stream, er) {
const state = stream._writableState
const sync = state.sync
const cb = state.writecb
if (typeof cb !== 'function') {
errorOrDestroy(stream, new ERR_MULTIPLE_CALLBACK())
return
}
state.writing = false
state.writecb = null
state.length -= state.writelen
state.writelen = 0
if (er) {
// Avoid V8 leak, https://github.com/nodejs/node/pull/34103#issuecomment-652002364
er.stack // eslint-disable-line no-unused-expressions
if (!state.errored) {
state.errored = er
}
// In case of duplex streams we need to notify the readable side of the
// error.
if (stream._readableState && !stream._readableState.errored) {
stream._readableState.errored = er
}
if (sync) {
process.nextTick(onwriteError, stream, state, er, cb)
} else {
onwriteError(stream, state, er, cb)
}
} else {
if (state.buffered.length > state.bufferedIndex) {
clearBuffer(stream, state)
}
if (sync) {
// It is a common case that the callback passed to .write() is always
// the same. In that case, we do not schedule a new nextTick(), but
// rather just increase a counter, to improve performance and avoid
// memory allocations.
if (state.afterWriteTickInfo !== null && state.afterWriteTickInfo.cb === cb) {
state.afterWriteTickInfo.count++
} else {
state.afterWriteTickInfo = {
count: 1,
cb,
stream,
state
}
process.nextTick(afterWriteTick, state.afterWriteTickInfo)
}
} else {
afterWrite(stream, state, 1, cb)
}
}
}
function afterWriteTick({ stream, state, count, cb }) {
state.afterWriteTickInfo = null
return afterWrite(stream, state, count, cb)
}
function afterWrite(stream, state, count, cb) {
const needDrain = !state.ending && !stream.destroyed && state.length === 0 && state.needDrain
if (needDrain) {
state.needDrain = false
stream.emit('drain')
}
while (count-- > 0) {
state.pendingcb--
cb()
}
if (state.destroyed) {
errorBuffer(state)
}
finishMaybe(stream, state)
}
// If there's something in the buffer waiting, then invoke callbacks.
function errorBuffer(state) {
if (state.writing) {
return
}
for (let n = state.bufferedIndex; n < state.buffered.length; ++n) {
var _state$errored
const { chunk, callback } = state.buffered[n]
const len = state.objectMode ? 1 : chunk.length
state.length -= len
callback(
(_state$errored = state.errored) !== null && _state$errored !== undefined
? _state$errored
: new ERR_STREAM_DESTROYED('write')
)
}
const onfinishCallbacks = state[kOnFinished].splice(0)
for (let i = 0; i < onfinishCallbacks.length; i++) {
var _state$errored2
onfinishCallbacks[i](
(_state$errored2 = state.errored) !== null && _state$errored2 !== undefined
? _state$errored2
: new ERR_STREAM_DESTROYED('end')
)
}
resetBuffer(state)
}
// If there's something in the buffer waiting, then process it.
function clearBuffer(stream, state) {
if (state.corked || state.bufferProcessing || state.destroyed || !state.constructed) {
return
}
const { buffered, bufferedIndex, objectMode } = state
const bufferedLength = buffered.length - bufferedIndex
if (!bufferedLength) {
return
}
let i = bufferedIndex
state.bufferProcessing = true
if (bufferedLength > 1 && stream._writev) {
state.pendingcb -= bufferedLength - 1
const callback = state.allNoop
? nop
: (err) => {
for (let n = i; n < buffered.length; ++n) {
buffered[n].callback(err)
}
}
// Make a copy of `buffered` if it's going to be used by `callback` above,
// since `doWrite` will mutate the array.
const chunks = state.allNoop && i === 0 ? buffered : ArrayPrototypeSlice(buffered, i)
chunks.allBuffers = state.allBuffers
doWrite(stream, state, true, state.length, chunks, '', callback)
resetBuffer(state)
} else {
do {
const { chunk, encoding, callback } = buffered[i]
buffered[i++] = null
const len = objectMode ? 1 : chunk.length
doWrite(stream, state, false, len, chunk, encoding, callback)
} while (i < buffered.length && !state.writing)
if (i === buffered.length) {
resetBuffer(state)
} else if (i > 256) {
buffered.splice(0, i)
state.bufferedIndex = 0
} else {
state.bufferedIndex = i
}
}
state.bufferProcessing = false
}
Writable.prototype._write = function (chunk, encoding, cb) {
if (this._writev) {
this._writev(
[
{
chunk,
encoding
}
],
cb
)
} else {
throw new ERR_METHOD_NOT_IMPLEMENTED('_write()')
}
}
Writable.prototype._writev = null
Writable.prototype.end = function (chunk, encoding, cb) {
const state = this._writableState
if (typeof chunk === 'function') {
cb = chunk
chunk = null
encoding = null
} else if (typeof encoding === 'function') {
cb = encoding
encoding = null
}
let err
if (chunk !== null && chunk !== undefined) {
const ret = _write(this, chunk, encoding)
if (ret instanceof Error) {
err = ret
}
}
// .end() fully uncorks.
if (state.corked) {
state.corked = 1
this.uncork()
}
if (err) {
// Do nothing...
} else if (!state.errored && !state.ending) {
// This is forgiving in terms of unnecessary calls to end() and can hide
// logic errors. However, usually such errors are harmless and causing a
// hard error can be disproportionately destructive. It is not always
// trivial for the user to determine whether end() needs to be called
// or not.
state.ending = true
finishMaybe(this, state, true)
state.ended = true
} else if (state.finished) {
err = new ERR_STREAM_ALREADY_FINISHED('end')
} else if (state.destroyed) {
err = new ERR_STREAM_DESTROYED('end')
}
if (typeof cb === 'function') {
if (err || state.finished) {
process.nextTick(cb, err)
} else {
state[kOnFinished].push(cb)
}
}
return this
}
function needFinish(state) {
return (
state.ending &&
!state.destroyed &&
state.constructed &&
state.length === 0 &&
!state.errored &&
state.buffered.length === 0 &&
!state.finished &&
!state.writing &&
!state.errorEmitted &&
!state.closeEmitted
)
}
function callFinal(stream, state) {
let called = false
function onFinish(err) {
if (called) {
errorOrDestroy(stream, err !== null && err !== undefined ? err : ERR_MULTIPLE_CALLBACK())
return
}
called = true
state.pendingcb--
if (err) {
const onfinishCallbacks = state[kOnFinished].splice(0)
for (let i = 0; i < onfinishCallbacks.length; i++) {
onfinishCallbacks[i](err)
}
errorOrDestroy(stream, err, state.sync)
} else if (needFinish(state)) {
state.prefinished = true
stream.emit('prefinish')
// Backwards compat. Don't check state.sync here.
// Some streams assume 'finish' will be emitted
// asynchronously relative to _final callback.
state.pendingcb++
process.nextTick(finish, stream, state)
}
}
state.sync = true
state.pendingcb++
try {
stream._final(onFinish)
} catch (err) {
onFinish(err)
}
state.sync = false
}
function prefinish(stream, state) {
if (!state.prefinished && !state.finalCalled) {
if (typeof stream._final === 'function' && !state.destroyed) {
state.finalCalled = true
callFinal(stream, state)
} else {
state.prefinished = true
stream.emit('prefinish')
}
}
}
function finishMaybe(stream, state, sync) {
if (needFinish(state)) {
prefinish(stream, state)
if (state.pendingcb === 0) {
if (sync) {
state.pendingcb++
process.nextTick(
(stream, state) => {
if (needFinish(state)) {
finish(stream, state)
} else {
state.pendingcb--
}
},
stream,
state
)
} else if (needFinish(state)) {
state.pendingcb++
finish(stream, state)
}
}
}
}
function finish(stream, state) {
state.pendingcb--
state.finished = true
const onfinishCallbacks = state[kOnFinished].splice(0)
for (let i = 0; i < onfinishCallbacks.length; i++) {
onfinishCallbacks[i]()
}
stream.emit('finish')
if (state.autoDestroy) {
// In case of duplex streams we need a way to detect
// if the readable side is ready for autoDestroy as well.
const rState = stream._readableState
const autoDestroy =
!rState ||
(rState.autoDestroy &&
// We don't expect the readable to ever 'end'
// if readable is explicitly set to false.
(rState.endEmitted || rState.readable === false))
if (autoDestroy) {
stream.destroy()
}
}
}
ObjectDefineProperties(Writable.prototype, {
closed: {
__proto__: null,
get() {
return this._writableState ? this._writableState.closed : false
}
},
destroyed: {
__proto__: null,
get() {
return this._writableState ? this._writableState.destroyed : false
},
set(value) {
// Backward compatibility, the user is explicitly managing destroyed.
if (this._writableState) {
this._writableState.destroyed = value
}
}
},
writable: {
__proto__: null,
get() {
const w = this._writableState
// w.writable === false means that this is part of a Duplex stream
// where the writable side was disabled upon construction.
// Compat. The user might manually disable writable side through
// deprecated setter.
return !!w && w.writable !== false && !w.destroyed && !w.errored && !w.ending && !w.ended
},
set(val) {
// Backwards compatible.
if (this._writableState) {
this._writableState.writable = !!val
}
}
},
writableFinished: {
__proto__: null,
get() {
return this._writableState ? this._writableState.finished : false
}
},
writableObjectMode: {
__proto__: null,
get() {
return this._writableState ? this._writableState.objectMode : false
}
},
writableBuffer: {
__proto__: null,
get() {
return this._writableState && this._writableState.getBuffer()
}
},
writableEnded: {
__proto__: null,
get() {
return this._writableState ? this._writableState.ending : false
}
},
writableNeedDrain: {
__proto__: null,
get() {
const wState = this._writableState
if (!wState) return false
return !wState.destroyed && !wState.ending && wState.needDrain
}
},
writableHighWaterMark: {
__proto__: null,
get() {
return this._writableState && this._writableState.highWaterMark
}
},
writableCorked: {
__proto__: null,
get() {
return this._writableState ? this._writableState.corked : 0
}
},
writableLength: {
__proto__: null,
get() {
return this._writableState && this._writableState.length
}
},
errored: {
__proto__: null,
enumerable: false,
get() {
return this._writableState ? this._writableState.errored : null
}
},
writableAborted: {
__proto__: null,
enumerable: false,
get: function () {
return !!(
this._writableState.writable !== false &&
(this._writableState.destroyed || this._writableState.errored) &&
!this._writableState.finished
)
}
}
})
const destroy = destroyImpl.destroy
Writable.prototype.destroy = function (err, cb) {
const state = this._writableState
// Invoke pending callbacks.
if (!state.destroyed && (state.bufferedIndex < state.buffered.length || state[kOnFinished].length)) {
process.nextTick(errorBuffer, state)
}
destroy.call(this, err, cb)
return this
}
Writable.prototype._undestroy = destroyImpl.undestroy
Writable.prototype._destroy = function (err, cb) {
cb(err)
}
Writable.prototype[EE.captureRejectionSymbol] = function (err) {
this.destroy(err)
}
let webStreamsAdapters
// Lazy to avoid circular references
function lazyWebStreams() {
if (webStreamsAdapters === undefined) webStreamsAdapters = {}
return webStreamsAdapters
}
Writable.fromWeb = function (writableStream, options) {
return lazyWebStreams().newStreamWritableFromWritableStream(writableStream, options)
}
Writable.toWeb = function (streamWritable) {
return lazyWebStreams().newWritableStreamFromStreamWritable(streamWritable)
}
@@ -0,0 +1,510 @@
/* eslint jsdoc/require-jsdoc: "error" */
'use strict'
const {
ArrayIsArray,
ArrayPrototypeIncludes,
ArrayPrototypeJoin,
ArrayPrototypeMap,
NumberIsInteger,
NumberIsNaN,
NumberMAX_SAFE_INTEGER,
NumberMIN_SAFE_INTEGER,
NumberParseInt,
ObjectPrototypeHasOwnProperty,
RegExpPrototypeExec,
String,
StringPrototypeToUpperCase,
StringPrototypeTrim
} = require('../ours/primordials')
const {
hideStackFrames,
codes: { ERR_SOCKET_BAD_PORT, ERR_INVALID_ARG_TYPE, ERR_INVALID_ARG_VALUE, ERR_OUT_OF_RANGE, ERR_UNKNOWN_SIGNAL }
} = require('../ours/errors')
const { normalizeEncoding } = require('../ours/util')
const { isAsyncFunction, isArrayBufferView } = require('../ours/util').types
const signals = {}
/**
* @param {*} value
* @returns {boolean}
*/
function isInt32(value) {
return value === (value | 0)
}
/**
* @param {*} value
* @returns {boolean}
*/
function isUint32(value) {
return value === value >>> 0
}
const octalReg = /^[0-7]+$/
const modeDesc = 'must be a 32-bit unsigned integer or an octal string'
/**
* Parse and validate values that will be converted into mode_t (the S_*
* constants). Only valid numbers and octal strings are allowed. They could be
* converted to 32-bit unsigned integers or non-negative signed integers in the
* C++ land, but any value higher than 0o777 will result in platform-specific
* behaviors.
*
* @param {*} value Values to be validated
* @param {string} name Name of the argument
* @param {number} [def] If specified, will be returned for invalid values
* @returns {number}
*/
function parseFileMode(value, name, def) {
if (typeof value === 'undefined') {
value = def
}
if (typeof value === 'string') {
if (RegExpPrototypeExec(octalReg, value) === null) {
throw new ERR_INVALID_ARG_VALUE(name, value, modeDesc)
}
value = NumberParseInt(value, 8)
}
validateUint32(value, name)
return value
}
/**
* @callback validateInteger
* @param {*} value
* @param {string} name
* @param {number} [min]
* @param {number} [max]
* @returns {asserts value is number}
*/
/** @type {validateInteger} */
const validateInteger = hideStackFrames((value, name, min = NumberMIN_SAFE_INTEGER, max = NumberMAX_SAFE_INTEGER) => {
if (typeof value !== 'number') throw new ERR_INVALID_ARG_TYPE(name, 'number', value)
if (!NumberIsInteger(value)) throw new ERR_OUT_OF_RANGE(name, 'an integer', value)
if (value < min || value > max) throw new ERR_OUT_OF_RANGE(name, `>= ${min} && <= ${max}`, value)
})
/**
* @callback validateInt32
* @param {*} value
* @param {string} name
* @param {number} [min]
* @param {number} [max]
* @returns {asserts value is number}
*/
/** @type {validateInt32} */
const validateInt32 = hideStackFrames((value, name, min = -2147483648, max = 2147483647) => {
// The defaults for min and max correspond to the limits of 32-bit integers.
if (typeof value !== 'number') {
throw new ERR_INVALID_ARG_TYPE(name, 'number', value)
}
if (!NumberIsInteger(value)) {
throw new ERR_OUT_OF_RANGE(name, 'an integer', value)
}
if (value < min || value > max) {
throw new ERR_OUT_OF_RANGE(name, `>= ${min} && <= ${max}`, value)
}
})
/**
* @callback validateUint32
* @param {*} value
* @param {string} name
* @param {number|boolean} [positive=false]
* @returns {asserts value is number}
*/
/** @type {validateUint32} */
const validateUint32 = hideStackFrames((value, name, positive = false) => {
if (typeof value !== 'number') {
throw new ERR_INVALID_ARG_TYPE(name, 'number', value)
}
if (!NumberIsInteger(value)) {
throw new ERR_OUT_OF_RANGE(name, 'an integer', value)
}
const min = positive ? 1 : 0
// 2 ** 32 === 4294967296
const max = 4294967295
if (value < min || value > max) {
throw new ERR_OUT_OF_RANGE(name, `>= ${min} && <= ${max}`, value)
}
})
/**
* @callback validateString
* @param {*} value
* @param {string} name
* @returns {asserts value is string}
*/
/** @type {validateString} */
function validateString(value, name) {
if (typeof value !== 'string') throw new ERR_INVALID_ARG_TYPE(name, 'string', value)
}
/**
* @callback validateNumber
* @param {*} value
* @param {string} name
* @param {number} [min]
* @param {number} [max]
* @returns {asserts value is number}
*/
/** @type {validateNumber} */
function validateNumber(value, name, min = undefined, max) {
if (typeof value !== 'number') throw new ERR_INVALID_ARG_TYPE(name, 'number', value)
if (
(min != null && value < min) ||
(max != null && value > max) ||
((min != null || max != null) && NumberIsNaN(value))
) {
throw new ERR_OUT_OF_RANGE(
name,
`${min != null ? `>= ${min}` : ''}${min != null && max != null ? ' && ' : ''}${max != null ? `<= ${max}` : ''}`,
value
)
}
}
/**
* @callback validateOneOf
* @template T
* @param {T} value
* @param {string} name
* @param {T[]} oneOf
*/
/** @type {validateOneOf} */
const validateOneOf = hideStackFrames((value, name, oneOf) => {
if (!ArrayPrototypeIncludes(oneOf, value)) {
const allowed = ArrayPrototypeJoin(
ArrayPrototypeMap(oneOf, (v) => (typeof v === 'string' ? `'${v}'` : String(v))),
', '
)
const reason = 'must be one of: ' + allowed
throw new ERR_INVALID_ARG_VALUE(name, value, reason)
}
})
/**
* @callback validateBoolean
* @param {*} value
* @param {string} name
* @returns {asserts value is boolean}
*/
/** @type {validateBoolean} */
function validateBoolean(value, name) {
if (typeof value !== 'boolean') throw new ERR_INVALID_ARG_TYPE(name, 'boolean', value)
}
/**
* @param {any} options
* @param {string} key
* @param {boolean} defaultValue
* @returns {boolean}
*/
function getOwnPropertyValueOrDefault(options, key, defaultValue) {
return options == null || !ObjectPrototypeHasOwnProperty(options, key) ? defaultValue : options[key]
}
/**
* @callback validateObject
* @param {*} value
* @param {string} name
* @param {{
* allowArray?: boolean,
* allowFunction?: boolean,
* nullable?: boolean
* }} [options]
*/
/** @type {validateObject} */
const validateObject = hideStackFrames((value, name, options = null) => {
const allowArray = getOwnPropertyValueOrDefault(options, 'allowArray', false)
const allowFunction = getOwnPropertyValueOrDefault(options, 'allowFunction', false)
const nullable = getOwnPropertyValueOrDefault(options, 'nullable', false)
if (
(!nullable && value === null) ||
(!allowArray && ArrayIsArray(value)) ||
(typeof value !== 'object' && (!allowFunction || typeof value !== 'function'))
) {
throw new ERR_INVALID_ARG_TYPE(name, 'Object', value)
}
})
/**
* @callback validateDictionary - We are using the Web IDL Standard definition
* of "dictionary" here, which means any value
* whose Type is either Undefined, Null, or
* Object (which includes functions).
* @param {*} value
* @param {string} name
* @see https://webidl.spec.whatwg.org/#es-dictionary
* @see https://tc39.es/ecma262/#table-typeof-operator-results
*/
/** @type {validateDictionary} */
const validateDictionary = hideStackFrames((value, name) => {
if (value != null && typeof value !== 'object' && typeof value !== 'function') {
throw new ERR_INVALID_ARG_TYPE(name, 'a dictionary', value)
}
})
/**
* @callback validateArray
* @param {*} value
* @param {string} name
* @param {number} [minLength]
* @returns {asserts value is any[]}
*/
/** @type {validateArray} */
const validateArray = hideStackFrames((value, name, minLength = 0) => {
if (!ArrayIsArray(value)) {
throw new ERR_INVALID_ARG_TYPE(name, 'Array', value)
}
if (value.length < minLength) {
const reason = `must be longer than ${minLength}`
throw new ERR_INVALID_ARG_VALUE(name, value, reason)
}
})
/**
* @callback validateStringArray
* @param {*} value
* @param {string} name
* @returns {asserts value is string[]}
*/
/** @type {validateStringArray} */
function validateStringArray(value, name) {
validateArray(value, name)
for (let i = 0; i < value.length; i++) {
validateString(value[i], `${name}[${i}]`)
}
}
/**
* @callback validateBooleanArray
* @param {*} value
* @param {string} name
* @returns {asserts value is boolean[]}
*/
/** @type {validateBooleanArray} */
function validateBooleanArray(value, name) {
validateArray(value, name)
for (let i = 0; i < value.length; i++) {
validateBoolean(value[i], `${name}[${i}]`)
}
}
/**
* @param {*} signal
* @param {string} [name='signal']
* @returns {asserts signal is keyof signals}
*/
function validateSignalName(signal, name = 'signal') {
validateString(signal, name)
if (signals[signal] === undefined) {
if (signals[StringPrototypeToUpperCase(signal)] !== undefined) {
throw new ERR_UNKNOWN_SIGNAL(signal + ' (signals must use all capital letters)')
}
throw new ERR_UNKNOWN_SIGNAL(signal)
}
}
/**
* @callback validateBuffer
* @param {*} buffer
* @param {string} [name='buffer']
* @returns {asserts buffer is ArrayBufferView}
*/
/** @type {validateBuffer} */
const validateBuffer = hideStackFrames((buffer, name = 'buffer') => {
if (!isArrayBufferView(buffer)) {
throw new ERR_INVALID_ARG_TYPE(name, ['Buffer', 'TypedArray', 'DataView'], buffer)
}
})
/**
* @param {string} data
* @param {string} encoding
*/
function validateEncoding(data, encoding) {
const normalizedEncoding = normalizeEncoding(encoding)
const length = data.length
if (normalizedEncoding === 'hex' && length % 2 !== 0) {
throw new ERR_INVALID_ARG_VALUE('encoding', encoding, `is invalid for data of length ${length}`)
}
}
/**
* Check that the port number is not NaN when coerced to a number,
* is an integer and that it falls within the legal range of port numbers.
* @param {*} port
* @param {string} [name='Port']
* @param {boolean} [allowZero=true]
* @returns {number}
*/
function validatePort(port, name = 'Port', allowZero = true) {
if (
(typeof port !== 'number' && typeof port !== 'string') ||
(typeof port === 'string' && StringPrototypeTrim(port).length === 0) ||
+port !== +port >>> 0 ||
port > 0xffff ||
(port === 0 && !allowZero)
) {
throw new ERR_SOCKET_BAD_PORT(name, port, allowZero)
}
return port | 0
}
/**
* @callback validateAbortSignal
* @param {*} signal
* @param {string} name
*/
/** @type {validateAbortSignal} */
const validateAbortSignal = hideStackFrames((signal, name) => {
if (signal !== undefined && (signal === null || typeof signal !== 'object' || !('aborted' in signal))) {
throw new ERR_INVALID_ARG_TYPE(name, 'AbortSignal', signal)
}
})
/**
* @callback validateFunction
* @param {*} value
* @param {string} name
* @returns {asserts value is Function}
*/
/** @type {validateFunction} */
const validateFunction = hideStackFrames((value, name) => {
if (typeof value !== 'function') throw new ERR_INVALID_ARG_TYPE(name, 'Function', value)
})
/**
* @callback validatePlainFunction
* @param {*} value
* @param {string} name
* @returns {asserts value is Function}
*/
/** @type {validatePlainFunction} */
const validatePlainFunction = hideStackFrames((value, name) => {
if (typeof value !== 'function' || isAsyncFunction(value)) throw new ERR_INVALID_ARG_TYPE(name, 'Function', value)
})
/**
* @callback validateUndefined
* @param {*} value
* @param {string} name
* @returns {asserts value is undefined}
*/
/** @type {validateUndefined} */
const validateUndefined = hideStackFrames((value, name) => {
if (value !== undefined) throw new ERR_INVALID_ARG_TYPE(name, 'undefined', value)
})
/**
* @template T
* @param {T} value
* @param {string} name
* @param {T[]} union
*/
function validateUnion(value, name, union) {
if (!ArrayPrototypeIncludes(union, value)) {
throw new ERR_INVALID_ARG_TYPE(name, `('${ArrayPrototypeJoin(union, '|')}')`, value)
}
}
/*
The rules for the Link header field are described here:
https://www.rfc-editor.org/rfc/rfc8288.html#section-3
This regex validates any string surrounded by angle brackets
(not necessarily a valid URI reference) followed by zero or more
link-params separated by semicolons.
*/
const linkValueRegExp = /^(?:<[^>]*>)(?:\s*;\s*[^;"\s]+(?:=(")?[^;"\s]*\1)?)*$/
/**
* @param {any} value
* @param {string} name
*/
function validateLinkHeaderFormat(value, name) {
if (typeof value === 'undefined' || !RegExpPrototypeExec(linkValueRegExp, value)) {
throw new ERR_INVALID_ARG_VALUE(
name,
value,
'must be an array or string of format "</styles.css>; rel=preload; as=style"'
)
}
}
/**
* @param {any} hints
* @return {string}
*/
function validateLinkHeaderValue(hints) {
if (typeof hints === 'string') {
validateLinkHeaderFormat(hints, 'hints')
return hints
} else if (ArrayIsArray(hints)) {
const hintsLength = hints.length
let result = ''
if (hintsLength === 0) {
return result
}
for (let i = 0; i < hintsLength; i++) {
const link = hints[i]
validateLinkHeaderFormat(link, 'hints')
result += link
if (i !== hintsLength - 1) {
result += ', '
}
}
return result
}
throw new ERR_INVALID_ARG_VALUE(
'hints',
hints,
'must be an array or string of format "</styles.css>; rel=preload; as=style"'
)
}
module.exports = {
isInt32,
isUint32,
parseFileMode,
validateArray,
validateStringArray,
validateBooleanArray,
validateBoolean,
validateBuffer,
validateDictionary,
validateEncoding,
validateFunction,
validateInt32,
validateInteger,
validateNumber,
validateObject,
validateOneOf,
validatePlainFunction,
validatePort,
validateSignalName,
validateString,
validateUint32,
validateUndefined,
validateUnion,
validateAbortSignal,
validateLinkHeaderValue
}
@@ -0,0 +1,35 @@
'use strict'
const CustomStream = require('../stream')
const promises = require('../stream/promises')
const originalDestroy = CustomStream.Readable.destroy
module.exports = CustomStream.Readable
// Explicit export naming is needed for ESM
module.exports._uint8ArrayToBuffer = CustomStream._uint8ArrayToBuffer
module.exports._isUint8Array = CustomStream._isUint8Array
module.exports.isDisturbed = CustomStream.isDisturbed
module.exports.isErrored = CustomStream.isErrored
module.exports.isReadable = CustomStream.isReadable
module.exports.Readable = CustomStream.Readable
module.exports.Writable = CustomStream.Writable
module.exports.Duplex = CustomStream.Duplex
module.exports.Transform = CustomStream.Transform
module.exports.PassThrough = CustomStream.PassThrough
module.exports.addAbortSignal = CustomStream.addAbortSignal
module.exports.finished = CustomStream.finished
module.exports.destroy = CustomStream.destroy
module.exports.destroy = originalDestroy
module.exports.pipeline = CustomStream.pipeline
module.exports.compose = CustomStream.compose
Object.defineProperty(CustomStream, 'promises', {
configurable: true,
enumerable: true,
get() {
return promises
}
})
module.exports.Stream = CustomStream.Stream
// Allow default importing
module.exports.default = module.exports
@@ -0,0 +1,341 @@
'use strict'
const { format, inspect, AggregateError: CustomAggregateError } = require('./util')
/*
This file is a reduced and adapted version of the main lib/internal/errors.js file defined at
https://github.com/nodejs/node/blob/master/lib/internal/errors.js
Don't try to replace with the original file and keep it up to date (starting from E(...) definitions)
with the upstream file.
*/
const AggregateError = globalThis.AggregateError || CustomAggregateError
const kIsNodeError = Symbol('kIsNodeError')
const kTypes = [
'string',
'function',
'number',
'object',
// Accept 'Function' and 'Object' as alternative to the lower cased version.
'Function',
'Object',
'boolean',
'bigint',
'symbol'
]
const classRegExp = /^([A-Z][a-z0-9]*)+$/
const nodeInternalPrefix = '__node_internal_'
const codes = {}
function assert(value, message) {
if (!value) {
throw new codes.ERR_INTERNAL_ASSERTION(message)
}
}
// Only use this for integers! Decimal numbers do not work with this function.
function addNumericalSeparator(val) {
let res = ''
let i = val.length
const start = val[0] === '-' ? 1 : 0
for (; i >= start + 4; i -= 3) {
res = `_${val.slice(i - 3, i)}${res}`
}
return `${val.slice(0, i)}${res}`
}
function getMessage(key, msg, args) {
if (typeof msg === 'function') {
assert(
msg.length <= args.length,
// Default options do not count.
`Code: ${key}; The provided arguments length (${args.length}) does not match the required ones (${msg.length}).`
)
return msg(...args)
}
const expectedLength = (msg.match(/%[dfijoOs]/g) || []).length
assert(
expectedLength === args.length,
`Code: ${key}; The provided arguments length (${args.length}) does not match the required ones (${expectedLength}).`
)
if (args.length === 0) {
return msg
}
return format(msg, ...args)
}
function E(code, message, Base) {
if (!Base) {
Base = Error
}
class NodeError extends Base {
constructor(...args) {
super(getMessage(code, message, args))
}
toString() {
return `${this.name} [${code}]: ${this.message}`
}
}
Object.defineProperties(NodeError.prototype, {
name: {
value: Base.name,
writable: true,
enumerable: false,
configurable: true
},
toString: {
value() {
return `${this.name} [${code}]: ${this.message}`
},
writable: true,
enumerable: false,
configurable: true
}
})
NodeError.prototype.code = code
NodeError.prototype[kIsNodeError] = true
codes[code] = NodeError
}
function hideStackFrames(fn) {
// We rename the functions that will be hidden to cut off the stacktrace
// at the outermost one
const hidden = nodeInternalPrefix + fn.name
Object.defineProperty(fn, 'name', {
value: hidden
})
return fn
}
function aggregateTwoErrors(innerError, outerError) {
if (innerError && outerError && innerError !== outerError) {
if (Array.isArray(outerError.errors)) {
// If `outerError` is already an `AggregateError`.
outerError.errors.push(innerError)
return outerError
}
const err = new AggregateError([outerError, innerError], outerError.message)
err.code = outerError.code
return err
}
return innerError || outerError
}
class AbortError extends Error {
constructor(message = 'The operation was aborted', options = undefined) {
if (options !== undefined && typeof options !== 'object') {
throw new codes.ERR_INVALID_ARG_TYPE('options', 'Object', options)
}
super(message, options)
this.code = 'ABORT_ERR'
this.name = 'AbortError'
}
}
E('ERR_ASSERTION', '%s', Error)
E(
'ERR_INVALID_ARG_TYPE',
(name, expected, actual) => {
assert(typeof name === 'string', "'name' must be a string")
if (!Array.isArray(expected)) {
expected = [expected]
}
let msg = 'The '
if (name.endsWith(' argument')) {
// For cases like 'first argument'
msg += `${name} `
} else {
msg += `"${name}" ${name.includes('.') ? 'property' : 'argument'} `
}
msg += 'must be '
const types = []
const instances = []
const other = []
for (const value of expected) {
assert(typeof value === 'string', 'All expected entries have to be of type string')
if (kTypes.includes(value)) {
types.push(value.toLowerCase())
} else if (classRegExp.test(value)) {
instances.push(value)
} else {
assert(value !== 'object', 'The value "object" should be written as "Object"')
other.push(value)
}
}
// Special handle `object` in case other instances are allowed to outline
// the differences between each other.
if (instances.length > 0) {
const pos = types.indexOf('object')
if (pos !== -1) {
types.splice(types, pos, 1)
instances.push('Object')
}
}
if (types.length > 0) {
switch (types.length) {
case 1:
msg += `of type ${types[0]}`
break
case 2:
msg += `one of type ${types[0]} or ${types[1]}`
break
default: {
const last = types.pop()
msg += `one of type ${types.join(', ')}, or ${last}`
}
}
if (instances.length > 0 || other.length > 0) {
msg += ' or '
}
}
if (instances.length > 0) {
switch (instances.length) {
case 1:
msg += `an instance of ${instances[0]}`
break
case 2:
msg += `an instance of ${instances[0]} or ${instances[1]}`
break
default: {
const last = instances.pop()
msg += `an instance of ${instances.join(', ')}, or ${last}`
}
}
if (other.length > 0) {
msg += ' or '
}
}
switch (other.length) {
case 0:
break
case 1:
if (other[0].toLowerCase() !== other[0]) {
msg += 'an '
}
msg += `${other[0]}`
break
case 2:
msg += `one of ${other[0]} or ${other[1]}`
break
default: {
const last = other.pop()
msg += `one of ${other.join(', ')}, or ${last}`
}
}
if (actual == null) {
msg += `. Received ${actual}`
} else if (typeof actual === 'function' && actual.name) {
msg += `. Received function ${actual.name}`
} else if (typeof actual === 'object') {
var _actual$constructor
if (
(_actual$constructor = actual.constructor) !== null &&
_actual$constructor !== undefined &&
_actual$constructor.name
) {
msg += `. Received an instance of ${actual.constructor.name}`
} else {
const inspected = inspect(actual, {
depth: -1
})
msg += `. Received ${inspected}`
}
} else {
let inspected = inspect(actual, {
colors: false
})
if (inspected.length > 25) {
inspected = `${inspected.slice(0, 25)}...`
}
msg += `. Received type ${typeof actual} (${inspected})`
}
return msg
},
TypeError
)
E(
'ERR_INVALID_ARG_VALUE',
(name, value, reason = 'is invalid') => {
let inspected = inspect(value)
if (inspected.length > 128) {
inspected = inspected.slice(0, 128) + '...'
}
const type = name.includes('.') ? 'property' : 'argument'
return `The ${type} '${name}' ${reason}. Received ${inspected}`
},
TypeError
)
E(
'ERR_INVALID_RETURN_VALUE',
(input, name, value) => {
var _value$constructor
const type =
value !== null &&
value !== undefined &&
(_value$constructor = value.constructor) !== null &&
_value$constructor !== undefined &&
_value$constructor.name
? `instance of ${value.constructor.name}`
: `type ${typeof value}`
return `Expected ${input} to be returned from the "${name}"` + ` function but got ${type}.`
},
TypeError
)
E(
'ERR_MISSING_ARGS',
(...args) => {
assert(args.length > 0, 'At least one arg needs to be specified')
let msg
const len = args.length
args = (Array.isArray(args) ? args : [args]).map((a) => `"${a}"`).join(' or ')
switch (len) {
case 1:
msg += `The ${args[0]} argument`
break
case 2:
msg += `The ${args[0]} and ${args[1]} arguments`
break
default:
{
const last = args.pop()
msg += `The ${args.join(', ')}, and ${last} arguments`
}
break
}
return `${msg} must be specified`
},
TypeError
)
E(
'ERR_OUT_OF_RANGE',
(str, range, input) => {
assert(range, 'Missing "range" argument')
let received
if (Number.isInteger(input) && Math.abs(input) > 2 ** 32) {
received = addNumericalSeparator(String(input))
} else if (typeof input === 'bigint') {
received = String(input)
if (input > 2n ** 32n || input < -(2n ** 32n)) {
received = addNumericalSeparator(received)
}
received += 'n'
} else {
received = inspect(input)
}
return `The value of "${str}" is out of range. It must be ${range}. Received ${received}`
},
RangeError
)
E('ERR_MULTIPLE_CALLBACK', 'Callback called multiple times', Error)
E('ERR_METHOD_NOT_IMPLEMENTED', 'The %s method is not implemented', Error)
E('ERR_STREAM_ALREADY_FINISHED', 'Cannot call %s after a stream was finished', Error)
E('ERR_STREAM_CANNOT_PIPE', 'Cannot pipe, not readable', Error)
E('ERR_STREAM_DESTROYED', 'Cannot call %s after a stream was destroyed', Error)
E('ERR_STREAM_NULL_VALUES', 'May not write null values to stream', TypeError)
E('ERR_STREAM_PREMATURE_CLOSE', 'Premature close', Error)
E('ERR_STREAM_PUSH_AFTER_EOF', 'stream.push() after EOF', Error)
E('ERR_STREAM_UNSHIFT_AFTER_END_EVENT', 'stream.unshift() after end event', Error)
E('ERR_STREAM_WRITE_AFTER_END', 'write after end', Error)
E('ERR_UNKNOWN_ENCODING', 'Unknown encoding: %s', TypeError)
module.exports = {
AbortError,
aggregateTwoErrors: hideStackFrames(aggregateTwoErrors),
hideStackFrames,
codes
}
@@ -0,0 +1,65 @@
'use strict'
const Stream = require('stream')
if (Stream && process.env.READABLE_STREAM === 'disable') {
const promises = Stream.promises
// Explicit export naming is needed for ESM
module.exports._uint8ArrayToBuffer = Stream._uint8ArrayToBuffer
module.exports._isUint8Array = Stream._isUint8Array
module.exports.isDisturbed = Stream.isDisturbed
module.exports.isErrored = Stream.isErrored
module.exports.isReadable = Stream.isReadable
module.exports.Readable = Stream.Readable
module.exports.Writable = Stream.Writable
module.exports.Duplex = Stream.Duplex
module.exports.Transform = Stream.Transform
module.exports.PassThrough = Stream.PassThrough
module.exports.addAbortSignal = Stream.addAbortSignal
module.exports.finished = Stream.finished
module.exports.destroy = Stream.destroy
module.exports.pipeline = Stream.pipeline
module.exports.compose = Stream.compose
Object.defineProperty(Stream, 'promises', {
configurable: true,
enumerable: true,
get() {
return promises
}
})
module.exports.Stream = Stream.Stream
} else {
const CustomStream = require('../stream')
const promises = require('../stream/promises')
const originalDestroy = CustomStream.Readable.destroy
module.exports = CustomStream.Readable
// Explicit export naming is needed for ESM
module.exports._uint8ArrayToBuffer = CustomStream._uint8ArrayToBuffer
module.exports._isUint8Array = CustomStream._isUint8Array
module.exports.isDisturbed = CustomStream.isDisturbed
module.exports.isErrored = CustomStream.isErrored
module.exports.isReadable = CustomStream.isReadable
module.exports.Readable = CustomStream.Readable
module.exports.Writable = CustomStream.Writable
module.exports.Duplex = CustomStream.Duplex
module.exports.Transform = CustomStream.Transform
module.exports.PassThrough = CustomStream.PassThrough
module.exports.addAbortSignal = CustomStream.addAbortSignal
module.exports.finished = CustomStream.finished
module.exports.destroy = CustomStream.destroy
module.exports.destroy = originalDestroy
module.exports.pipeline = CustomStream.pipeline
module.exports.compose = CustomStream.compose
Object.defineProperty(CustomStream, 'promises', {
configurable: true,
enumerable: true,
get() {
return promises
}
})
module.exports.Stream = CustomStream.Stream
}
// Allow default importing
module.exports.default = module.exports
@@ -0,0 +1,101 @@
'use strict'
/*
This file is a reduced and adapted version of the main lib/internal/per_context/primordials.js file defined at
https://github.com/nodejs/node/blob/master/lib/internal/per_context/primordials.js
Don't try to replace with the original file and keep it up to date with the upstream file.
*/
module.exports = {
ArrayIsArray(self) {
return Array.isArray(self)
},
ArrayPrototypeIncludes(self, el) {
return self.includes(el)
},
ArrayPrototypeIndexOf(self, el) {
return self.indexOf(el)
},
ArrayPrototypeJoin(self, sep) {
return self.join(sep)
},
ArrayPrototypeMap(self, fn) {
return self.map(fn)
},
ArrayPrototypePop(self, el) {
return self.pop(el)
},
ArrayPrototypePush(self, el) {
return self.push(el)
},
ArrayPrototypeSlice(self, start, end) {
return self.slice(start, end)
},
Error,
FunctionPrototypeCall(fn, thisArgs, ...args) {
return fn.call(thisArgs, ...args)
},
FunctionPrototypeSymbolHasInstance(self, instance) {
return Function.prototype[Symbol.hasInstance].call(self, instance)
},
MathFloor: Math.floor,
Number,
NumberIsInteger: Number.isInteger,
NumberIsNaN: Number.isNaN,
NumberMAX_SAFE_INTEGER: Number.MAX_SAFE_INTEGER,
NumberMIN_SAFE_INTEGER: Number.MIN_SAFE_INTEGER,
NumberParseInt: Number.parseInt,
ObjectDefineProperties(self, props) {
return Object.defineProperties(self, props)
},
ObjectDefineProperty(self, name, prop) {
return Object.defineProperty(self, name, prop)
},
ObjectGetOwnPropertyDescriptor(self, name) {
return Object.getOwnPropertyDescriptor(self, name)
},
ObjectKeys(obj) {
return Object.keys(obj)
},
ObjectSetPrototypeOf(target, proto) {
return Object.setPrototypeOf(target, proto)
},
Promise,
PromisePrototypeCatch(self, fn) {
return self.catch(fn)
},
PromisePrototypeThen(self, thenFn, catchFn) {
return self.then(thenFn, catchFn)
},
PromiseReject(err) {
return Promise.reject(err)
},
ReflectApply: Reflect.apply,
RegExpPrototypeTest(self, value) {
return self.test(value)
},
SafeSet: Set,
String,
StringPrototypeSlice(self, start, end) {
return self.slice(start, end)
},
StringPrototypeToLowerCase(self) {
return self.toLowerCase()
},
StringPrototypeToUpperCase(self) {
return self.toUpperCase()
},
StringPrototypeTrim(self) {
return self.trim()
},
Symbol,
SymbolFor: Symbol.for,
SymbolAsyncIterator: Symbol.asyncIterator,
SymbolHasInstance: Symbol.hasInstance,
SymbolIterator: Symbol.iterator,
TypedArrayPrototypeSet(self, buf, len) {
return self.set(buf, len)
},
Uint8Array
}
@@ -0,0 +1,128 @@
'use strict'
const bufferModule = require('buffer')
const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor
const Blob = globalThis.Blob || bufferModule.Blob
/* eslint-disable indent */
const isBlob =
typeof Blob !== 'undefined'
? function isBlob(b) {
// eslint-disable-next-line indent
return b instanceof Blob
}
: function isBlob(b) {
return false
}
/* eslint-enable indent */
// This is a simplified version of AggregateError
class AggregateError extends Error {
constructor(errors) {
if (!Array.isArray(errors)) {
throw new TypeError(`Expected input to be an Array, got ${typeof errors}`)
}
let message = ''
for (let i = 0; i < errors.length; i++) {
message += ` ${errors[i].stack}\n`
}
super(message)
this.name = 'AggregateError'
this.errors = errors
}
}
module.exports = {
AggregateError,
kEmptyObject: Object.freeze({}),
once(callback) {
let called = false
return function (...args) {
if (called) {
return
}
called = true
callback.apply(this, args)
}
},
createDeferredPromise: function () {
let resolve
let reject
// eslint-disable-next-line promise/param-names
const promise = new Promise((res, rej) => {
resolve = res
reject = rej
})
return {
promise,
resolve,
reject
}
},
promisify(fn) {
return new Promise((resolve, reject) => {
fn((err, ...args) => {
if (err) {
return reject(err)
}
return resolve(...args)
})
})
},
debuglog() {
return function () {}
},
format(format, ...args) {
// Simplified version of https://nodejs.org/api/util.html#utilformatformat-args
return format.replace(/%([sdifj])/g, function (...[_unused, type]) {
const replacement = args.shift()
if (type === 'f') {
return replacement.toFixed(6)
} else if (type === 'j') {
return JSON.stringify(replacement)
} else if (type === 's' && typeof replacement === 'object') {
const ctor = replacement.constructor !== Object ? replacement.constructor.name : ''
return `${ctor} {}`.trim()
} else {
return replacement.toString()
}
})
},
inspect(value) {
// Vastly simplified version of https://nodejs.org/api/util.html#utilinspectobject-options
switch (typeof value) {
case 'string':
if (value.includes("'")) {
if (!value.includes('"')) {
return `"${value}"`
} else if (!value.includes('`') && !value.includes('${')) {
return `\`${value}\``
}
}
return `'${value}'`
case 'number':
if (isNaN(value)) {
return 'NaN'
} else if (Object.is(value, -0)) {
return String(value)
}
return value
case 'bigint':
return `${String(value)}n`
case 'boolean':
case 'undefined':
return String(value)
case 'object':
return '{}'
}
},
types: {
isAsyncFunction(fn) {
return fn instanceof AsyncFunction
},
isArrayBufferView(arr) {
return ArrayBuffer.isView(arr)
}
},
isBlob
}
module.exports.promisify.custom = Symbol.for('nodejs.util.promisify.custom')
@@ -0,0 +1,136 @@
/* replacement start */
const { Buffer } = require('buffer')
/* replacement end */
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
;('use strict')
const { ObjectDefineProperty, ObjectKeys, ReflectApply } = require('./ours/primordials')
const {
promisify: { custom: customPromisify }
} = require('./ours/util')
const { streamReturningOperators, promiseReturningOperators } = require('./internal/streams/operators')
const {
codes: { ERR_ILLEGAL_CONSTRUCTOR }
} = require('./ours/errors')
const compose = require('./internal/streams/compose')
const { pipeline } = require('./internal/streams/pipeline')
const { destroyer } = require('./internal/streams/destroy')
const eos = require('./internal/streams/end-of-stream')
const internalBuffer = {}
const promises = require('./stream/promises')
const utils = require('./internal/streams/utils')
const Stream = (module.exports = require('./internal/streams/legacy').Stream)
Stream.isDisturbed = utils.isDisturbed
Stream.isErrored = utils.isErrored
Stream.isReadable = utils.isReadable
Stream.Readable = require('./internal/streams/readable')
for (const key of ObjectKeys(streamReturningOperators)) {
const op = streamReturningOperators[key]
function fn(...args) {
if (new.target) {
throw ERR_ILLEGAL_CONSTRUCTOR()
}
return Stream.Readable.from(ReflectApply(op, this, args))
}
ObjectDefineProperty(fn, 'name', {
__proto__: null,
value: op.name
})
ObjectDefineProperty(fn, 'length', {
__proto__: null,
value: op.length
})
ObjectDefineProperty(Stream.Readable.prototype, key, {
__proto__: null,
value: fn,
enumerable: false,
configurable: true,
writable: true
})
}
for (const key of ObjectKeys(promiseReturningOperators)) {
const op = promiseReturningOperators[key]
function fn(...args) {
if (new.target) {
throw ERR_ILLEGAL_CONSTRUCTOR()
}
return ReflectApply(op, this, args)
}
ObjectDefineProperty(fn, 'name', {
__proto__: null,
value: op.name
})
ObjectDefineProperty(fn, 'length', {
__proto__: null,
value: op.length
})
ObjectDefineProperty(Stream.Readable.prototype, key, {
__proto__: null,
value: fn,
enumerable: false,
configurable: true,
writable: true
})
}
Stream.Writable = require('./internal/streams/writable')
Stream.Duplex = require('./internal/streams/duplex')
Stream.Transform = require('./internal/streams/transform')
Stream.PassThrough = require('./internal/streams/passthrough')
Stream.pipeline = pipeline
const { addAbortSignal } = require('./internal/streams/add-abort-signal')
Stream.addAbortSignal = addAbortSignal
Stream.finished = eos
Stream.destroy = destroyer
Stream.compose = compose
ObjectDefineProperty(Stream, 'promises', {
__proto__: null,
configurable: true,
enumerable: true,
get() {
return promises
}
})
ObjectDefineProperty(pipeline, customPromisify, {
__proto__: null,
enumerable: true,
get() {
return promises.pipeline
}
})
ObjectDefineProperty(eos, customPromisify, {
__proto__: null,
enumerable: true,
get() {
return promises.finished
}
})
// Backwards-compat with node 0.4.x
Stream.Stream = Stream
Stream._isUint8Array = function isUint8Array(value) {
return value instanceof Uint8Array
}
Stream._uint8ArrayToBuffer = function _uint8ArrayToBuffer(chunk) {
return Buffer.from(chunk.buffer, chunk.byteOffset, chunk.byteLength)
}
@@ -0,0 +1,43 @@
'use strict'
const { ArrayPrototypePop, Promise } = require('../ours/primordials')
const { isIterable, isNodeStream, isWebStream } = require('../internal/streams/utils')
const { pipelineImpl: pl } = require('../internal/streams/pipeline')
const { finished } = require('../internal/streams/end-of-stream')
require('../../lib/stream.js')
function pipeline(...streams) {
return new Promise((resolve, reject) => {
let signal
let end
const lastArg = streams[streams.length - 1]
if (
lastArg &&
typeof lastArg === 'object' &&
!isNodeStream(lastArg) &&
!isIterable(lastArg) &&
!isWebStream(lastArg)
) {
const options = ArrayPrototypePop(streams)
signal = options.signal
end = options.end
}
pl(
streams,
(err, value) => {
if (err) {
reject(err)
} else {
resolve(value)
}
},
{
signal,
end
}
)
})
}
module.exports = {
finished,
pipeline
}
@@ -0,0 +1,86 @@
{
"name": "readable-stream",
"version": "4.4.2",
"description": "Node.js Streams, a user-land copy of the stream library from Node.js",
"homepage": "https://github.com/nodejs/readable-stream",
"license": "MIT",
"licenses": [
{
"type": "MIT",
"url": "https://choosealicense.com/licenses/mit/"
}
],
"keywords": [
"readable",
"stream",
"pipe"
],
"repository": {
"type": "git",
"url": "git://github.com/nodejs/readable-stream"
},
"bugs": {
"url": "https://github.com/nodejs/readable-stream/issues"
},
"main": "lib/ours/index.js",
"files": [
"lib",
"LICENSE",
"README.md"
],
"browser": {
"util": "./lib/ours/util.js",
"./lib/ours/index.js": "./lib/ours/browser.js"
},
"scripts": {
"build": "node build/build.mjs",
"postbuild": "prettier -w lib test",
"test": "tap --rcfile=./tap.yml test/parallel/test-*.js test/ours/test-*.js",
"test:prepare": "node test/browser/runner-prepare.mjs",
"test:browsers": "node test/browser/runner-browser.mjs",
"test:bundlers": "node test/browser/runner-node.mjs",
"test:readable-stream-only": "node readable-stream-test/runner-prepare.mjs",
"coverage": "c8 -c ./c8.json tap --rcfile=./tap.yml test/parallel/test-*.js test/ours/test-*.js",
"format": "prettier -w src lib test",
"lint": "eslint src"
},
"dependencies": {
"abort-controller": "^3.0.0",
"buffer": "^6.0.3",
"events": "^3.3.0",
"process": "^0.11.10",
"string_decoder": "^1.3.0"
},
"devDependencies": {
"@babel/core": "^7.17.10",
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.7",
"@babel/plugin-proposal-optional-chaining": "^7.16.7",
"@rollup/plugin-commonjs": "^22.0.0",
"@rollup/plugin-inject": "^4.0.4",
"@rollup/plugin-node-resolve": "^13.3.0",
"@sinonjs/fake-timers": "^9.1.2",
"browserify": "^17.0.0",
"c8": "^7.11.2",
"esbuild": "^0.14.39",
"esbuild-plugin-alias": "^0.2.1",
"eslint": "^8.15.0",
"eslint-config-standard": "^17.0.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-n": "^15.2.0",
"eslint-plugin-promise": "^6.0.0",
"playwright": "^1.21.1",
"prettier": "^2.6.2",
"rollup": "^2.72.1",
"rollup-plugin-polyfill-node": "^0.9.0",
"tap": "^16.2.0",
"tap-mocha-reporter": "^5.0.3",
"tape": "^5.5.3",
"tar": "^6.1.11",
"undici": "^5.1.1",
"webpack": "^5.72.1",
"webpack-cli": "^4.9.2"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
}
+102
View File
@@ -0,0 +1,102 @@
{
"name": "@web5/common",
"version": "1.0.0",
"type": "module",
"main": "./dist/cjs/index.js",
"module": "./dist/esm/index.js",
"types": "./dist/types/index.d.ts",
"homepage": "https://github.com/TBD54566975/web5-js/tree/main/packages/common#readme",
"bugs": "https://github.com/TBD54566975/web5-js/issues",
"repository": {
"type": "git",
"url": "git+https://github.com/TBD54566975/web5-js.git",
"directory": "packages/common"
},
"license": "Apache-2.0",
"contributors": [
{
"name": "Daniel Buchner",
"url": "https://github.com/csuwildcat"
},
{
"name": "Frank Hinek",
"url": "https://github.com/frankhinek"
},
{
"name": "Moe Jangda",
"url": "https://github.com/mistermoe"
}
],
"files": [
"dist",
"src"
],
"exports": {
".": {
"types": "./dist/types/index.d.ts",
"import": "./dist/esm/index.js",
"require": "./dist/cjs/index.js"
}
},
"react-native": "./dist/esm/index.js",
"keywords": [
"decentralized",
"decentralized-applications",
"decentralized-identity",
"decentralized-web",
"vcs",
"verifiable credentials",
"web5"
],
"publishConfig": {
"access": "public",
"provenance": true
},
"engines": {
"node": ">=18.0.0"
},
"dependencies": {
"@isaacs/ttlcache": "1.4.1",
"level": "8.0.0",
"multiformats": "11.0.2",
"readable-stream": "4.4.2"
},
"devDependencies": {
"@playwright/test": "1.40.1",
"@types/chai": "4.3.6",
"@types/chai-as-promised": "7.1.5",
"@types/eslint": "8.44.2",
"@types/mocha": "10.0.1",
"@types/readable-stream": "4.0.9",
"@types/node": "20.11.19",
"@typescript-eslint/eslint-plugin": "6.4.0",
"@typescript-eslint/parser": "6.4.0",
"@web/test-runner": "0.18.0",
"@web/test-runner-playwright": "0.11.0",
"abstract-level": "1.0.4",
"c8": "9.0.0",
"chai": "4.3.10",
"chai-as-promised": "7.1.1",
"esbuild": "0.19.8",
"eslint": "8.47.0",
"eslint-plugin-mocha": "10.1.0",
"mocha": "10.2.0",
"mocha-junit-reporter": "2.2.1",
"playwright": "1.40.1",
"rimraf": "4.4.0",
"typescript": "5.1.6"
},
"scripts": {
"clean": "rimraf dist coverage tests/compiled",
"build:esm": "rimraf dist/esm dist/types && pnpm tsc -p tsconfig.json",
"build:cjs": "rimraf dist/cjs && node build/cjs-bundle.js && echo '{\"type\": \"commonjs\"}' > ./dist/cjs/package.json",
"build:browser": "rimraf dist/browser.mjs dist/browser.js && node build/bundles.js",
"build:tests:node": "rimraf tests/compiled && pnpm tsc -p tests/tsconfig.json",
"build:tests:browser": "rimraf tests/compiled && node build/esbuild-tests.cjs",
"build": "pnpm clean && pnpm build:esm && pnpm build:cjs && pnpm build:browser",
"lint": "eslint . --ext .ts --max-warnings 0",
"lint:fix": "eslint . --ext .ts --fix",
"test:node": "pnpm build:tests:node && pnpm c8 mocha",
"test:browser": "pnpm build:tests:browser && web-test-runner"
}
}
+2
View File
@@ -0,0 +1,2 @@
import TTLCache from '@isaacs/ttlcache';
export { TTLCache as TtlCache };
+444
View File
@@ -0,0 +1,444 @@
import type { Multibase } from 'multiformats';
import { base32z } from 'multiformats/bases/base32';
import { base58btc } from 'multiformats/bases/base58';
import { base64url } from 'multiformats/bases/base64';
import { isAsyncIterable, isArrayBufferSlice, universalTypeOf } from './type-utils.js';
const textEncoder = new TextEncoder();
const textDecoder = new TextDecoder();
export class Convert {
data: any;
format: string;
constructor(data: any, format: string) {
this.data = data;
this.format = format;
}
static arrayBuffer(data: ArrayBuffer): Convert {
return new Convert(data, 'ArrayBuffer');
}
static asyncIterable(data: AsyncIterable<any>): Convert {
if (!isAsyncIterable(data)) {
throw new TypeError('Input must be of type AsyncIterable.');
}
return new Convert(data, 'AsyncIterable');
}
static base32Z(data: string): Convert {
return new Convert(data, 'Base32Z');
}
static base58Btc(data: string): Convert {
return new Convert(data, 'Base58Btc');
}
static base64Url(data: string): Convert {
return new Convert(data, 'Base64Url');
}
/**
* Reference:
* The BufferSource type is a TypeScript type that represents an ArrayBuffer
* or one of the ArrayBufferView types, such a TypedArray (e.g., Uint8Array)
* or a DataView.
*/
static bufferSource(data: BufferSource): Convert {
return new Convert(data, 'BufferSource');
}
static hex(data: string): Convert {
if (typeof data !== 'string') {
throw new TypeError('Hex input must be a string.');
}
if (data.length % 2 !== 0) {
throw new TypeError('Hex input must have an even number of characters.');
}
return new Convert(data, 'Hex');
}
static multibase(data: string): Convert {
return new Convert(data, 'Multibase');
}
static object(data: Record<string, any>): Convert {
return new Convert(data, 'Object');
}
static string(data: string): Convert {
return new Convert(data, 'String');
}
static uint8Array(data: Uint8Array): Convert {
return new Convert(data, 'Uint8Array');
}
toArrayBuffer(): ArrayBuffer {
switch (this.format) {
case 'Base58Btc': {
return base58btc.baseDecode(this.data).buffer;
}
case 'Base64Url': {
return base64url.baseDecode(this.data).buffer;
}
case 'BufferSource': {
const dataType = universalTypeOf(this.data);
if (dataType === 'ArrayBuffer') {
// Data is already an ArrayBuffer, No conversion is necessary.
return this.data;
} else if (ArrayBuffer.isView(this.data)) {
// Data is a DataView or a different TypedArray (e.g., Uint16Array).
if (isArrayBufferSlice(this.data)) {
// Data is a slice of an ArrayBuffer. Return a new ArrayBuffer or ArrayBufferView of the same slice.
return this.data.buffer.slice(this.data.byteOffset, this.data.byteOffset + this.data.byteLength);
} else {
// Data is a whole ArrayBuffer viewed as a different TypedArray or DataView. Return the whole ArrayBuffer.
return this.data.buffer;
}
} else {
throw new TypeError(`${this.format} value is not of type: ArrayBuffer, DataView, or TypedArray.`);
}
}
case 'Hex': {
return this.toUint8Array().buffer;
}
case 'String': {
return this.toUint8Array().buffer;
}
case 'Uint8Array': {
return this.data.buffer;
}
default:
throw new TypeError(`Conversion from ${this.format} to ArrayBuffer is not supported.`);
}
}
async toArrayBufferAsync(): Promise<ArrayBuffer> {
switch (this.format) {
case 'AsyncIterable': {
const blob = await this.toBlobAsync();
return await blob.arrayBuffer();
}
default:
throw new TypeError(`Asynchronous conversion from ${this.format} to ArrayBuffer is not supported.`);
}
}
toBase32Z(): string {
switch (this.format) {
case 'Uint8Array': {
return base32z.baseEncode(this.data);
}
default:
throw new TypeError(`Conversion from ${this.format} to Base64Z is not supported.`);
}
}
toBase58Btc(): string {
switch (this.format) {
case 'ArrayBuffer': {
const u8a = new Uint8Array(this.data);
return base58btc.baseEncode(u8a);
}
case 'Multibase': {
return this.data.substring(1);
}
case 'Uint8Array': {
return base58btc.baseEncode(this.data);
}
default:
throw new TypeError(`Conversion from ${this.format} to Base58Btc is not supported.`);
}
}
toBase64Url(): string {
switch (this.format) {
case 'ArrayBuffer': {
const u8a = new Uint8Array(this.data);
return base64url.baseEncode(u8a);
}
case 'BufferSource': {
const u8a = this.toUint8Array();
return base64url.baseEncode(u8a);
}
case 'Object': {
const string = JSON.stringify(this.data);
const u8a = textEncoder.encode(string);
return base64url.baseEncode(u8a);
}
case 'String': {
const u8a = textEncoder.encode(this.data);
return base64url.baseEncode(u8a);
}
case 'Uint8Array': {
return base64url.baseEncode(this.data);
}
default:
throw new TypeError(`Conversion from ${this.format} to Base64Url is not supported.`);
}
}
async toBlobAsync(): Promise<Blob> {
switch (this.format) {
case 'AsyncIterable': {
// Initialize an array to hold the chunks from the AsyncIterable.
const chunks = [];
// Asynchronously iterate over each chunk in the AsyncIterable.
for await (const chunk of (this.data as AsyncIterable<any>)) {
// Append each chunk to the chunks array. These chunks can be of any type, typically binary data or text.
chunks.push(chunk);
}
// Create a new Blob from the aggregated chunks.
// The Blob constructor combines these chunks into a single Blob object.
const blob = new Blob(chunks);
return blob;
}
default:
throw new TypeError(`Asynchronous conversion from ${this.format} to Blob is not supported.`);
}
}
toHex(): string {
// pre-calculating Hex values improves runtime by 6-10x.
const hexes = Array.from({ length: 256 }, (v, i) => i.toString(16).padStart(2, '0'));
switch (this.format) {
case 'ArrayBuffer': {
const u8a = this.toUint8Array();
return Convert.uint8Array(u8a).toHex();
}
case 'Base64Url': {
const u8a = this.toUint8Array();
return Convert.uint8Array(u8a).toHex();
}
case 'Uint8Array': {
let hex = '';
for (let i = 0; i < this.data.length; i++) {
hex += hexes[this.data[i]];
}
return hex;
}
default:
throw new TypeError(`Conversion from ${this.format} to Hex is not supported.`);
}
}
toMultibase(): Multibase<any> {
switch (this.format) {
case 'Base58Btc': {
return `z${this.data}`;
}
default:
throw new TypeError(`Conversion from ${this.format} to Multibase is not supported.`);
}
}
toObject(): object {
switch (this.format) {
case 'Base64Url': {
const u8a = base64url.baseDecode(this.data);
const text = textDecoder.decode(u8a);
return JSON.parse(text);
}
case 'String': {
return JSON.parse(this.data);
}
case 'Uint8Array': {
const text = textDecoder.decode(this.data);
return JSON.parse(text);
}
default:
throw new TypeError(`Conversion from ${this.format} to Object is not supported.`);
}
}
async toObjectAsync(): Promise<any> {
switch (this.format) {
case 'AsyncIterable': {
// Convert the AsyncIterable to a String.
const text = await this.toStringAsync();
// Parse the string as JSON. This step assumes that the string represents a valid JSON structure.
// JSON.parse() will convert the string into a corresponding JavaScript object.
const json = JSON.parse(text);
// Return the parsed JavaScript object. The type of this object will depend on the structure
// of the JSON in the stream. It could be an object, array, string, number, etc.
return json;
}
default:
throw new TypeError(`Asynchronous conversion from ${this.format} to Object is not supported.`);
}
}
toString(): string {
switch (this.format) {
case 'ArrayBuffer': {
return textDecoder.decode(this.data);
}
case 'Base64Url': {
const u8a = base64url.baseDecode(this.data);
return textDecoder.decode(u8a);
}
case 'Object': {
return JSON.stringify(this.data);
}
case 'Uint8Array': {
return textDecoder.decode(this.data);
}
default:
throw new TypeError(`Conversion from ${this.format} to String is not supported.`);
}
}
async toStringAsync(): Promise<string> {
switch (this.format) {
case 'AsyncIterable': {
// Initialize an empty string to accumulate the decoded text.
let str = '';
// Iterate over the chunks from the AsyncIterable.
for await (const chunk of (this.data as AsyncIterable<any>)) {
// If the chunk is already a string, concatenate it directly.
if (typeof chunk === 'string')
str += chunk;
else
// If the chunk is a Uint8Array or similar, use the decoder to convert it to a string.
// The `stream: true` option lets the decoder handle multi-byte characters spanning
// multiple chunks.
str += textDecoder.decode(chunk, { stream: true });
}
// Finalize the decoding process to handle any remaining bytes and signal the end of the stream.
// The `stream: false` option flushes the decoder's internal state.
str += textDecoder.decode(undefined, { stream: false });
// Return the accumulated string.
return str;
}
default:
throw new TypeError(`Asynchronous conversion from ${this.format} to String is not supported.`);
}
}
toUint8Array(): Uint8Array {
switch (this.format) {
case 'ArrayBuffer': {
// Çreate Uint8Array as a view on the ArrayBuffer.
// Note: The Uint8Array shares the same memory as the ArrayBuffer, so this operation is very efficient.
return new Uint8Array(this.data);
}
case 'Base32Z': {
return base32z.baseDecode(this.data);
}
case 'Base58Btc': {
return base58btc.baseDecode(this.data);
}
case 'Base64Url': {
return base64url.baseDecode(this.data);
}
case 'BufferSource': {
const dataType = universalTypeOf(this.data);
if (dataType === 'Uint8Array') {
// Data is already a Uint8Array. No conversion is necessary.
// Note: Uint8Array is a type of BufferSource.
return this.data;
} else if (dataType === 'ArrayBuffer') {
// Data is an ArrayBuffer, create Uint8Array as a view on the ArrayBuffer.
// Note: The Uint8Array shares the same memory as the ArrayBuffer, so this operation is very efficient.
return new Uint8Array(this.data);
} else if (ArrayBuffer.isView(this.data)) {
// Data is a DataView or a different TypedArray (e.g., Uint16Array).
return new Uint8Array(this.data.buffer, this.data.byteOffset, this.data.byteLength);
} else {
throw new TypeError(`${this.format} value is not of type: ArrayBuffer, DataView, or TypedArray.`);
}
}
case 'Hex': {
const u8a = new Uint8Array(this.data.length / 2);
for (let i = 0; i < this.data.length; i += 2) {
const byteValue = parseInt(this.data.substring(i, i + 2), 16);
if (isNaN(byteValue)) {
throw new TypeError('Input is not a valid hexadecimal string.');
}
u8a[i / 2] = byteValue;
}
return u8a;
}
case 'Object': {
const string = JSON.stringify(this.data);
return textEncoder.encode(string);
}
case 'String': {
return textEncoder.encode(this.data);
}
default:
throw new TypeError(`Conversion from ${this.format} to Uint8Array is not supported.`);
}
}
async toUint8ArrayAsync(): Promise<Uint8Array> {
switch (this.format) {
case 'AsyncIterable': {
const arrayBuffer = await this.toArrayBufferAsync();
return new Uint8Array(arrayBuffer);
}
default:
throw new TypeError(`Asynchronous conversion from ${this.format} to Uint8Array is not supported.`);
}
}
}
+10
View File
@@ -0,0 +1,10 @@
export type * from './types.js';
export * from './cache.js';
export * from './convert.js';
export * from './multicodec.js';
export * from './object.js';
export * from './stores.js';
export * from './stream.js';
export * from './stream-node.js';
export * from './type-utils.js';
+176
View File
@@ -0,0 +1,176 @@
import { varint } from 'multiformats';
export type MulticodecCode = number;
export type MulticodecDefinition<MulticodecCode> = {
code: MulticodecCode;
// codeBytes: Uint8Array;
name: string;
}
/**
* The `Multicodec` class provides an interface to prepend binary data
* with a prefix that identifies the data that follows.
* https://github.com/multiformats/multicodec/blob/master/table.csv
*
* Multicodec is a self-describing multiformat, it wraps other formats with
* a tiny bit of self-description. A multicodec identifier is a
* varint (variable integer) that indicates the format of the data.
*
* The canonical table of multicodecs can be access at the following URL:
* https://github.com/multiformats/multicodec/blob/master/table.csv
*
* Example usage:
*
* ```ts
* Multicodec.registerCodec({ code: 0xed, name: 'ed25519-pub' });
* const prefixedData = Multicodec.addPrefix({ code: 0xed, data: new Uint8Array(32) });
* ```
*/
export class Multicodec {
/**
* A static field containing a map of codec codes to their corresponding names.
*/
static codeToName = new Map<MulticodecCode, string>();
/**
* A static field containing a map of codec names to their corresponding codes.
*/
static nameToCode = new Map<string, MulticodecCode>();
/**
* Adds a multicodec prefix to input data.
*
* @param options - The options for adding a prefix.
* @param options.code - The codec code. Either the code or name must be provided.
* @param options.name - The codec name. Either the code or name must be provided.
* @param options.data - The data to be prefixed.
* @returns The data with the added prefix as a Uint8Array.
*/
public static addPrefix(options: {
code?: MulticodecCode,
data: Uint8Array,
name?: string,
}): Uint8Array {
let { code, data, name } = options;
if (!(name ? !code : code)) {
throw new Error(`Either 'name' or 'code' must be defined, but not both.`);
}
// If code was given, confirm it exists, or lookup code by name.
code = Multicodec.codeToName.has(code!) ? code : Multicodec.nameToCode.get(name!);
// Throw error if a registered Codec wasn't found.
if (code === undefined) {
throw new Error(`Unsupported multicodec: ${options.name ?? options.code}`);
}
// Create a new array to store the prefix and input data.
const prefixLength = varint.encodingLength(code);
const dataWithPrefix = new Uint8Array(prefixLength + data.byteLength);
dataWithPrefix.set(data, prefixLength);
// Prepend the prefix.
varint.encodeTo(code, dataWithPrefix);
return dataWithPrefix;
}
/**
* Get the Multicodec code from given prefixed data.
*
* @param options - The options for getting the codec code.
* @param options.prefixedData - The data to extract the codec code from.
* @returns - The Multicodec code as a number.
*/
public static getCodeFromData(options: {
prefixedData: Uint8Array
}): MulticodecCode {
const { prefixedData } = options;
const [code, _] = varint.decode(prefixedData);
return code;
}
/**
* Get the Multicodec code from given Multicodec name.
*
* @param options - The options for getting the codec code.
* @param options.name - The name to lookup.
* @returns - The Multicodec code as a number.
*/
public static getCodeFromName(options: {
name: string
}): MulticodecCode {
const { name } = options;
// Throw error if a registered Codec wasn't found.
const code = Multicodec.nameToCode.get(name);
if (code === undefined) {
throw new Error(`Unsupported multicodec: ${name}`);
}
return code;
}
/**
* Get the Multicodec name from given Multicodec code.
*
* @param options - The options for getting the codec name.
* @param options.name - The code to lookup.
* @returns - The Multicodec name as a string.
*/
public static getNameFromCode(options: {
code: MulticodecCode
}): string {
const { code } = options;
// Throw error if a registered Codec wasn't found.
const name = Multicodec.codeToName.get(code);
if (name === undefined) {
throw new Error(`Unsupported multicodec: ${code}`);
}
return name;
}
/**
* Registers a new codec in the Multicodec class.
*
* @param codec - The codec to be registered.
*/
public static registerCodec(codec: MulticodecDefinition<MulticodecCode>) {
Multicodec.codeToName.set(codec.code, codec.name);
Multicodec.nameToCode.set(codec.name, codec.code);
}
/**
* Returns the data with the Multicodec prefix removed.
*
* @param refixedData - The data to extract the codec code from.
* @returns {Uint8Array}
*/
public static removePrefix(options: {
prefixedData: Uint8Array
}): { code: MulticodecCode, name: string, data: Uint8Array } {
const { prefixedData } = options;
const [code, codeByteLength] = varint.decode(prefixedData);
// Throw error if a registered Codec wasn't found.
const name = Multicodec.codeToName.get(code);
if (name === undefined) {
throw new Error(`Unsupported multicodec: ${code}`);
}
return { code, data: prefixedData.slice(codeByteLength), name };
}
}
// Pre-defined registered codecs:
Multicodec.registerCodec({ code: 0xed, name: 'ed25519-pub' });
Multicodec.registerCodec({ code: 0x1300, name: 'ed25519-priv' });
Multicodec.registerCodec({ code: 0xec, name: 'x25519-pub' });
Multicodec.registerCodec({ code: 0x1302, name: 'x25519-priv' });
Multicodec.registerCodec({ code: 0xe7, name: 'secp256k1-pub' });
Multicodec.registerCodec({ code: 0x1301, name: 'secp256k1-priv' });
+43
View File
@@ -0,0 +1,43 @@
/**
* Checks whether the given object has any properties.
*/
export function isEmptyObject(obj: unknown): boolean {
if (typeof obj !== 'object' || obj === null) {
return false;
}
if (Object.getOwnPropertySymbols(obj).length > 0) {
return false;
}
return Object.keys(obj).length === 0;
}
/**
* Recursively removes all properties with an empty object or array as its value from the given object.
*/
export function removeEmptyObjects(obj: Record<string, unknown>): void {
Object.keys(obj).forEach(key => {
if (typeof(obj[key]) === 'object') {
// recursive remove empty object or array properties in nested objects
removeEmptyObjects(obj[key] as Record<string, unknown>);
}
if (isEmptyObject(obj[key])) {
delete obj[key];
}
});
}
/**
* Recursively removes all properties with `undefined` as its value from the given object.
*/
export function removeUndefinedProperties(obj: Record<string, unknown>): void {
Object.keys(obj).forEach(key => {
if (obj[key] === undefined) {
delete obj[key];
} else if (typeof(obj[key]) === 'object') {
removeUndefinedProperties(obj[key] as Record<string, unknown>); // recursive remove `undefined` properties in nested objects
}
});
}
+135
View File
@@ -0,0 +1,135 @@
import type { AbstractLevel } from 'abstract-level';
import { Level } from 'level';
import type { KeyValueStore } from './types.js';
export class LevelStore<K = string, V = any> implements KeyValueStore<K, V> {
private store: AbstractLevel<string | Buffer | Uint8Array, K, V>;
constructor({ db, location = 'DATASTORE' }: {
db?: AbstractLevel<string | Buffer | Uint8Array, K, V>;
location?: string;
} = {}) {
this.store = db ?? new Level<K, V>(location);
}
async clear(): Promise<void> {
await this.store.clear();
}
async close(): Promise<void> {
await this.store.close();
}
async delete(key: K): Promise<void> {
await this.store.del(key);
}
async get(key: K): Promise<V | undefined> {
try {
return await this.store.get(key);
} catch (error: any) {
// Don't throw when a key wasn't found.
if (error.notFound) return undefined;
throw error;
}
}
async set(key: K, value: V): Promise<void> {
await this.store.put(key, value);
}
}
/**
* The `MemoryStore` class is an implementation of
* `KeyValueStore` that holds data in memory.
*
* It provides a basic key-value store that works synchronously and keeps all
* data in memory. This can be used for testing, or for handling small amounts
* of data with simple key-value semantics.
*
* Example usage:
*
* ```ts
* const memoryStore = new MemoryStore<string, number>();
* await memoryStore.set("key1", 1);
* const value = await memoryStore.get("key1");
* console.log(value); // 1
* ```
*
* @public
*/
export class MemoryStore<K, V> implements KeyValueStore<K, V> {
/**
* A private field that contains the Map used as the key-value store.
*/
private store: Map<K, V> = new Map();
/**
* Clears all entries in the key-value store.
*
* @returns A Promise that resolves when the operation is complete.
*/
async clear(): Promise<void> {
this.store.clear();
}
/**
* This operation is no-op for `MemoryStore`
* and will log a warning if called.
*/
async close(): Promise<void> {
/** no-op */
}
/**
* Deletes an entry from the key-value store by its key.
*
* @param id - The key of the entry to delete.
* @returns A Promise that resolves to a boolean indicating whether the entry was successfully deleted.
*/
async delete(id: K): Promise<boolean> {
return this.store.delete(id);
}
/**
* Retrieves the value of an entry by its key.
*
* @param id - The key of the entry to retrieve.
* @returns A Promise that resolves to the value of the entry, or `undefined` if the entry does not exist.
*/
async get(id: K): Promise<V | undefined> {
return this.store.get(id);
}
/**
* Checks for the presence of an entry by key.
*
* @param id - The key to check for the existence of.
* @returns A Promise that resolves to a boolean indicating whether an element with the specified key exists or not.
*/
async has(id: K): Promise<boolean> {
return this.store.has(id);
}
/**
* Retrieves all values in the key-value store.
*
* @returns A Promise that resolves to an array of all values in the store.
*/
async list(): Promise<V[]> {
return Array.from(this.store.values());
}
/**
* Sets the value of an entry in the key-value store.
*
* @param id - The key of the entry to set.
* @param key - The new value for the entry.
* @returns A Promise that resolves when the operation is complete.
*/
async set(id: K, key: V): Promise<void> {
this.store.set(id, key);
}
}
+381
View File
@@ -0,0 +1,381 @@
import type { Duplex, ReadableStateOptions, Transform, Writable } from 'readable-stream';
import { Readable } from 'readable-stream';
import { Stream } from './stream.js';
import { Convert } from './convert.js';
export { Readable } from 'readable-stream';
export class NodeStream {
/**
* Consumes a `Readable` stream and returns its contents as an `ArrayBuffer`.
*
* This method reads all data from a Node.js `Readable` stream, collects it, and converts it into
* an `ArrayBuffer`.
*
* @example
* ```ts
* const nodeReadable = getReadableStreamSomehow();
* const arrayBuffer = await NodeStream.consumeToArrayBuffer({ readable: nodeReadable });
* ```
*
* @param readable - The Node.js Readable stream whose data will be consumed.
* @returns A Promise that resolves to an `ArrayBuffer` containing all the data from the stream.
*/
public static async consumeToArrayBuffer({ readable }: { readable: Readable}): Promise<ArrayBuffer> {
const arrayBuffer = await Convert.asyncIterable(readable).toArrayBufferAsync();
return arrayBuffer;
}
/**
* Consumes a `Readable` stream and returns its contents as a `Blob`.
*
* This method reads all data from a Node.js `Readable` stream, collects it, and converts it into
* a `Blob`.
*
* @example
* ```ts
* const nodeReadable = getReadableStreamSomehow();
* const blob = await NodeStream.consumeToBlob({ readable: nodeReadable });
* ```
*
* @param readableStream - The Node.js `Readable` stream whose data will be consumed.
* @returns A Promise that resolves to a `Blob` containing all the data from the stream.
*/
public static async consumeToBlob({ readable }: { readable: Readable }): Promise<Blob> {
const blob = await Convert.asyncIterable(readable).toBlobAsync();
return blob;
}
/**
* Consumes a `Readable` stream and returns its contents as a `Uint8Array`.
*
* This method reads all data from a Node.js `Readable`, collects it, and converts it into a
* `Uint8Array`.
*
* @example
* ```ts
* const nodeReadable = getReadableStreamSomehow();
* const bytes = await NodeStream.consumeToBytes({ readable: nodeReadable });
* ```
*
* @param readableStream - The Node.js `Readable` stream whose data will be consumed.
* @returns A Promise that resolves to a `Uint8Array` containing all the data from the stream.
*/
public static async consumeToBytes({ readable }: { readable: Readable }): Promise<Uint8Array> {
const bytes = await Convert.asyncIterable(readable).toUint8ArrayAsync();
return bytes;
}
/**
* Consumes a `Readable` stream and parses its contents as JSON.
*
* This method reads all the data from the stream, converts it to a text string, and then parses
* it as JSON, returning the resulting object.
*
* @example
* ```ts
* const nodeReadable = getReadableStreamSomehow();
* const jsonData = await NodeStream.consumeToJson({ readable: nodeReadable });
* ```
*
* @param readableStream - The Node.js `Readable` stream whose JSON content will be consumed.
* @returns A Promise that resolves to the parsed JSON object from the stream's data.
*/
public static async consumeToJson({ readable }: { readable: Readable }): Promise<any> {
const object = await Convert.asyncIterable(readable).toObjectAsync();
return object;
}
/**
* Consumes a `Readable` stream and returns its contents as a text string.
*
* This method reads all the data from the stream, converting it into a single string.
*
* @example
* ```ts
* const nodeReadable = getReadableStreamSomehow();
* const text = await NodeStream.consumeToText({ readable: nodeReadable });
* ```
*
* @param readableStream - The Node.js `Readable` stream whose text content will be consumed.
* @returns A Promise that resolves to a string containing all the data from the stream.
*/
public static async consumeToText({ readable }: { readable: Readable}): Promise<string> {
const text = await Convert.asyncIterable(readable).toStringAsync();
return text;
}
/**
* Converts a Web `ReadableStream` to a Node.js `Readable` stream.
*
* This method takes a Web `ReadableStream` and converts it to a Node.js `Readable` stream.
* The conversion is done by reading chunks from the Web `ReadableStream` and pushing them
* into the Node.js `Readable` stream.
*
* @example
* ```ts
* const webReadableStream = getWebReadableStreamSomehow();
* const nodeReadableStream = NodeStream.fromWebReadable({ readableStream: webReadableStream });
* ```
*
* @param readableStream - The Web `ReadableStream` to be converted.
* @param readableOptions - Optional `Readable` stream options for the Node.js stream.
* @returns The Node.js `Readable` stream.
*/
public static fromWebReadable({ readableStream, readableOptions }: {
readableStream: ReadableStream,
readableOptions?: ReadableStateOptions
}): Readable {
if (!Stream.isReadableStream(readableStream)) {
throw new TypeError(`NodeStream.fromWebReadable: 'readableStream' is not a Web ReadableStream.`);
}
const reader = readableStream.getReader();
let closed = false;
const nodeReadable = new Readable({
...readableOptions,
read: function () {
reader.read().then(({ done, value }) => {
if (done) {
this.push(null); // Push null to signify end of stream.
} else {
if (!this.push(value)) {
// When push returns false, we should stop reading until _read is called again.
return;
}
}
}).catch((error) => {
// If an error occurs while reading, destroy the stream.
this.destroy(error);
});
},
destroy: function (error, callback) {
function done() {
callback(error);
}
if (!closed) {
reader.cancel(error)
.then(done)
.catch(done);
return;
}
done();
}
});
reader.closed
.then(() => {
closed = true; // Prevents reader.cancel() from being called in destroy()
})
.catch((error) => {
closed = true; // Prevents reader.cancel() from being called in destroy()
nodeReadable.destroy(error);
});
return nodeReadable;
}
/**
* Checks if a Node.js stream (`Readable`, `Writable`, `Duplex`, or `Transform`) has been destroyed.
*
* This method determines whether the provided Node.js stream has been destroyed. A stream
* is considered destroyed if its 'destroyed' property is set to true or if its internal state
* indicates it has been destroyed.
*
* @example
* ```ts
* const stream = getStreamSomehow();
* stream.destroy(); // Destroy the stream.
* const isDestroyed = NodeStream.isDestroyed({ stream });
* console.log(isDestroyed); // Output: true
* ```
*
* @param stream - The Node.js stream to check.
* @returns `true` if the stream has been destroyed; otherwise, `false`.
*/
public static isDestroyed({ stream }: { stream: Readable | Writable | Duplex | Transform }): boolean {
if (!NodeStream.isStream(stream)) {
throw new TypeError(`NodeStream.isDestroyed: 'stream' is not a Node stream.`);
}
const writableState = '_writableState' in stream ? stream._writableState : undefined;
const readableState = stream._readableState;
const state = writableState || readableState;
return !!(stream.destroyed || state.destroyed);
}
/**
* Checks if a Node.js `Readable` stream is still readable.
*
* This method checks if a Node.js `Readable` stream is still in a state that allows reading from
* it. A stream is considered readable if it has not ended, has not been destroyed, and is not
* currently paused.
*
* @example
* ```ts
* const readableStream = new Readable();
* const isReadable = NodeStream.isReadable({ readable: readableStream });
* console.log(isReadable); // Output: true or false
* ```
*
* @param readable - The Node.js `Readable` stream to be checked.
* @returns `true` if the stream is still readable; otherwise, `false`.
*/
public static isReadable({ readable }: { readable: Readable }): boolean {
// Check if the object is a Node Readable stream.
if (!NodeStream.isReadableStream(readable)) {
return false;
}
// Check if the stream is still readable.
return (
readable.readable && // Is the stream readable?
(typeof readable._readableState.ended === 'boolean' && !readable._readableState.ended) && // Has the 'end' method been called?
(typeof readable._readableState.endEmitted === 'boolean' && !readable._readableState.endEmitted) && // Has the 'end' event been emitted?
!readable.destroyed && // Has the 'destroy' method been called?
!readable.isPaused() // Is the stream paused?
);
}
/**
* Checks if an object is a Node.js `Readable` stream.
*
* This method verifies if the provided object is a Node.js `Readable` stream by checking for
* specific properties and methods typical of a `Readable` stream in Node.js.
*
* @example
* ```ts
* const obj = getSomeObject();
* if (NodeStream.isReadableStream(obj)) {
* // obj is a Node.js Readable stream
* }
* ```
*
* @param obj - The object to be checked.
* @returns `true` if `obj` is a Node.js `Readable` stream; otherwise, `false`.
*/
static isReadableStream(obj: unknown): obj is Readable {
return (
typeof obj === 'object' &&
obj !== null &&
('pipe' in obj && typeof obj.pipe === 'function') &&
('on' in obj && typeof obj.on === 'function') &&
(!('_writableState' in obj) && '_readableState' in obj)
);
}
/**
* Checks if the provided object is a Node.js stream (`Duplex`, `Readable`, `Writable`, or `Transform`).
*
* This method checks for the presence of internal properties specific to Node.js streams:
* `_readableState` and `_writableState`. These properties are present in Node.js stream
* instances, allowing identification of the stream type.
*
* The `_readableState` property is found in `Readable` and `Duplex` streams (including
* `Transform` streams, which are a type of `Duplex` stream), indicating that the stream can be
* read from. The `_writableState` property is found in `Writable` and `Duplex` streams,
* indicating that the stream can be written to.
*
* @example
* ```ts
* const { Readable, Writable, Duplex, Transform } = require('stream');
*
* const readableStream = new Readable();
* console.log(NodeStream.isStream(readableStream)); // Output: true
*
* const writableStream = new Writable();
* console.log(NodeStream.isStream(writableStream)); // Output: true
*
* const duplexStream = new Duplex();
* console.log(NodeStream.isStream(duplexStream)); // Output: true
*
* const transformStream = new Transform();
* console.log(NodeStream.isStream(transformStream)); // Output: true
*
* const nonStreamObject = {};
* console.log(NodeStream.isStream(nonStreamObject)); // Output: false
* ```
*
* @remarks
* - This method does not differentiate between the different types of streams (Readable,
* Writable, Duplex, Transform). It simply checks if the object is any kind of Node.js stream.
* - While this method can identify standard Node.js streams, it may not recognize custom or
* third-party stream-like objects that do not inherit directly from Node.js's stream classes
* or do not have these internal state properties. This is intentional as many of the methods
* in this library are designed to work with standard Node.js streams.
*
* @param obj - The object to be checked for being a Node.js stream.
* @returns `true` if the object is a Node.js stream (`Duplex`, `Readable`, `Writable`, or `Transform`); otherwise, `false`.
*/
public static isStream(obj: unknown): obj is Duplex | Readable | Writable | Transform {
return (
typeof obj === 'object' && obj !== null &&
('_readableState' in obj || '_writableState' in obj)
);
}
/**
* Converts a Node.js `Readable` stream to a Web `ReadableStream`.
*
* This method provides a bridge between Node.js streams and the Web Streams API by converting a
* Node.js `Readable` stream into a Web `ReadableStream`. It listens for 'data', 'end', and 'error'
* events on the Node.js stream and appropriately enqueues data, closes, or errors the Web
* `ReadableStream`.
*
* If the Node.js stream is already destroyed, the method returns an immediately cancelled
* Web `ReadableStream`.
*
* @example
* ```ts
* const nodeReadable = getNodeReadableStreamSomehow();
* const webReadableStream = NodeStream.toWebReadable({ readable: nodeReadable });
* ```
*
* @param readable - The Node.js `Readable` stream to be converted.
* @returns A Web `ReadableStream` corresponding to the provided Node.js `Readable` stream.
* @throws TypeError if `readable` is not a Node.js `Readable` stream.
* @throws Error if the Node.js `Readable` stream is already destroyed.
*/
static toWebReadable({ readable }: { readable: Readable }): ReadableStream {
if (!NodeStream.isReadableStream(readable)) {
throw new TypeError(`NodeStream.toWebReadable: 'readable' is not a Node Readable stream.`);
}
if (NodeStream.isDestroyed({ stream: readable })) {
const readable = new ReadableStream();
readable.cancel();
return readable;
}
return new ReadableStream({
start(controller) {
readable.on('data', (chunk) => {
controller.enqueue(chunk);
});
readable.on('end', () => {
controller.close();
});
readable.on('error', (err) => {
controller.error(err);
});
},
cancel() {
readable.destroy();
}
});
}
}
+406
View File
@@ -0,0 +1,406 @@
import { Convert } from './convert.js';
export class Stream {
/**
* Transforms a `ReadableStream` into an `AsyncIterable`. This allows for the asynchronous
* iteration over the stream's data chunks.
*
* This method creates an async iterator from a `ReadableStream`, enabling the use of
* `for await...of` loops to process stream data. It reads from the stream until it's closed or
* errored, yielding each chunk as it becomes available.
*
* @example
* ```ts
* const readableStream = new ReadableStream({ ... });
* for await (const chunk of Stream.asAsyncIterator(readableStream)) {
* // process each chunk
* }
* ```
*
* @remarks
* - The method ensures proper cleanup by releasing the reader lock when iteration is completed or
* if an error occurs.
*
* @param readableStream - The Web `ReadableStream` to be transformed into an `AsyncIterable`.
* @returns An `AsyncIterable` that yields data chunks from the `ReadableStream`.
*/
public static async * asAsyncIterator<T>(readableStream: ReadableStream<T>): AsyncIterable<T> {
const reader = readableStream.getReader();
try {
while (true) {
const { done, value } = await reader.read();
if (done) break;
yield value;
}
} finally {
reader.releaseLock();
}
}
/**
* Consumes a `ReadableStream` and returns its contents as an `ArrayBuffer`.
*
* This method reads all data from a `ReadableStream`, collects it, and converts it into an
* `ArrayBuffer`.
*
* @example
* ```ts
* const readableStream = new ReadableStream({ ... });
* const arrayBuffer = await Stream.consumeToArrayBuffer({ readableStream });
* ```
*
* @param readableStream - The Web `ReadableStream` whose data will be consumed.
* @returns A Promise that resolves to an `ArrayBuffer` containing all the data from the stream.
*/
public static async consumeToArrayBuffer({ readableStream }: { readableStream: ReadableStream}): Promise<ArrayBuffer> {
const iterableStream = Stream.asAsyncIterator(readableStream);
const arrayBuffer = await Convert.asyncIterable(iterableStream).toArrayBufferAsync();
return arrayBuffer;
}
/**
* Consumes a `ReadableStream` and returns its contents as a `Blob`.
*
* This method reads all data from a `ReadableStream`, collects it, and converts it into a `Blob`.
*
* @example
* ```ts
* const readableStream = new ReadableStream({ ... });
* const blob = await Stream.consumeToBlob({ readableStream });
* ```
*
* @param readableStream - The Web `ReadableStream` whose data will be consumed.
* @returns A Promise that resolves to a `Blob` containing all the data from the stream.
*/
public static async consumeToBlob({ readableStream }: { readableStream: ReadableStream}): Promise<Blob> {
const iterableStream = Stream.asAsyncIterator(readableStream);
const blob = await Convert.asyncIterable(iterableStream).toBlobAsync();
return blob;
}
/**
* Consumes a `ReadableStream` and returns its contents as a `Uint8Array`.
*
* This method reads all data from a `ReadableStream`, collects it, and converts it into a
* `Uint8Array`.
*
* @example
* ```ts
* const readableStream = new ReadableStream({ ... });
* const bytes = await Stream.consumeToBytes({ readableStream });
* ```
*
* @param readableStream - The Web `ReadableStream` whose data will be consumed.
* @returns A Promise that resolves to a `Uint8Array` containing all the data from the stream.
*/
public static async consumeToBytes({ readableStream }: { readableStream: ReadableStream }): Promise<Uint8Array> {
const iterableStream = Stream.asAsyncIterator(readableStream);
const bytes = await Convert.asyncIterable(iterableStream).toUint8ArrayAsync();
return bytes;
}
/**
* Consumes a `ReadableStream` and parses its contents as JSON.
*
* This method reads all the data from the stream, converts it to a text string, and then parses
* it as JSON, returning the resulting object.
*
* @example
* ```ts
* const readableStream = new ReadableStream({ ... });
* const jsonData = await Stream.consumeToJson({ readableStream });
* ```
*
* @param readableStream - The Web `ReadableStream` whose JSON content will be consumed.
* @returns A Promise that resolves to the parsed JSON object from the stream's data.
*/
public static async consumeToJson({ readableStream }: { readableStream: ReadableStream}): Promise<any> {
const iterableStream = Stream.asAsyncIterator(readableStream);
const object = await Convert.asyncIterable(iterableStream).toObjectAsync();
return object;
}
/**
* Consumes a `ReadableStream` and returns its contents as a text string.
*
* This method reads all the data from the stream, converting it into a single string.
*
* @example
* ```ts
* const readableStream = new ReadableStream({ ... });
* const text = await Stream.consumeToText({ readableStream });
* ```
*
* @param readableStream - The Web `ReadableStream` whose text content will be consumed.
* @returns A Promise that resolves to a string containing all the data from the stream.
*/
public static async consumeToText({ readableStream }: { readableStream: ReadableStream}): Promise<string> {
const iterableStream = Stream.asAsyncIterator(readableStream);
const text = await Convert.asyncIterable(iterableStream).toStringAsync();
return text;
}
/**
* Generates a `ReadableStream` of `Uint8Array` chunks with customizable length and fill value.
*
* This method creates a `ReadableStream` that emits `Uint8Array` chunks. You can specify the
* total length of the stream, the length of individual chunks, and a fill value or range for the
* chunks. It's useful for testing or when specific binary data streams are required.
*
* @example
* ```ts
* // Create a stream of 1000 bytes with 100-byte chunks filled with 0xAA.
* const byteStream = Stream.generateByteStream({
* streamLength: 1000,
* chunkLength: 100,
* fillValue: 0xAA
* });
*
* // Create an unending stream of 100KB chunks filled with values that range from 1 to 99.
* const byteStream = Stream.generateByteStream({
* chunkLength: 100 * 1024,
* fillValue: [1, 99]
* });
* ```
*
* @param streamLength - The total length of the stream in bytes. If omitted, the stream is infinite.
* @param chunkLength - The length of each chunk. If omitted, each chunk is the size of `streamLength`.
* @param fillValue - A value or range to fill the chunks with. Can be a single number or a tuple [min, max].
* @returns A `ReadableStream` that emits `Uint8Array` chunks.
*/
public static generateByteStream({ streamLength, chunkLength, fillValue }: {
streamLength?: number,
chunkLength?: number,
fillValue?: number | [number, number]
}): ReadableStream<Uint8Array> {
let bytesRemaining = streamLength ?? Infinity;
let controller: ReadableStreamDefaultController<Uint8Array>;
function enqueueChunk() {
const currentChunkLength = Math.min(bytesRemaining, chunkLength ?? Infinity);
bytesRemaining -= currentChunkLength;
let chunk: Uint8Array;
if (typeof fillValue === 'number') {
chunk = new Uint8Array(currentChunkLength).fill(fillValue);
} else if (Array.isArray(fillValue)) {
chunk = new Uint8Array(currentChunkLength);
const [min, max] = fillValue;
const range = max - min + 1;
for (let i = 0; i < currentChunkLength; i++) {
chunk[i] = Math.floor(Math.random() * range) + min;
}
} else {
chunk = new Uint8Array(currentChunkLength);
}
controller.enqueue(chunk);
// If there are no more bytes to send, close the stream
if (bytesRemaining <= 0) {
controller.close();
}
}
return new ReadableStream<Uint8Array>({
start(c) {
controller = c;
enqueueChunk();
},
pull() {
enqueueChunk();
},
});
}
/**
* Checks if the provided Web `ReadableStream` is in a readable state.
*
* After verifying that the stream is a Web {@link https://streams.spec.whatwg.org/#rs-model | ReadableStream},
* this method checks the {@link https://streams.spec.whatwg.org/#readablestream-locked | locked}
* property of the ReadableStream. The `locked` property is `true` if a reader is currently
* active, meaning the stream is either being read or has already been read (and hence is not in a
* readable state). If `locked` is `false`, it means the stream is still in a state where it can
* be read.
*
* In the case where a `ReadableStream` has been unlocked but is no longer readable (for example,
* if it has been fully read or cancelled), additional checks are needed beyond just examining the
* locked property. The ReadableStream API does not provide a direct way to check if the stream
* has data left or if it's in a readable state once it's been unlocked.
*
* Per {@link https://streams.spec.whatwg.org/#other-specs-rs-introspect | WHATWG Streams, Section 9.1.3. Introspection}:
*
* > ...note that apart from checking whether or not the stream is locked, this direct
* > introspection is not possible via the public JavaScript API, and so specifications should
* > instead use the algorithms in §9.1.2 Reading. (For example, instead of testing if the stream
* > is readable, attempt to get a reader and handle any exception.)
*
* This implementation employs the technique suggested by the WHATWG Streams standard by
* attempting to acquire a reader and checking the state of the reader. If acquiring a reader
* succeeds, it immediately releases the lock and returns `true`, indicating the stream is
* readable. If an error occurs while trying to get a reader (which can happen if the stream is
* already closed or errored), it catches the error and returns `false`, indicating the stream is
* not readable.
*
* @example
* ```ts
* const readableStream = new ReadableStream({ ... });
* const isStreamReadable = Stream.isReadable({ readableStream });
* console.log(isStreamReadable); // Output: true or false
* ```
*
* @remarks
* - This method does not check whether the stream has data left to read; it only checks if the
* stream is in a state that allows reading. It is possible for a stream to be unlocked but
* still have no data left if it has never been locked to a reader.
*
* @param readableStream - The Web `ReadableStream` to be checked for readability.
*
* @returns `true` if the stream is a `ReadableStream` and is in a readable state (not locked and
* no error on getting a reader); otherwise, `false`.
*/
public static isReadable({ readableStream }: { readableStream: ReadableStream }): boolean {
// Check if the stream is a WHATWG `ReadableStream`.
if (!Stream.isReadableStream(readableStream)) {
return false;
}
// Check if the stream is locked.
if (readableStream.locked) {
return false;
}
try {
// Try to get a reader to check if the stream is readable.
const reader = readableStream.getReader();
// If successful, immediately release the lock.
reader.releaseLock();
return true;
} catch (error) {
// If an error occurs (e.g., the stream is not readable), return false.
return false;
}
}
/**
* Checks if an object is a Web `ReadableStream`.
*
* This method verifies whether the given object is a `ReadableStream` by checking its type and
* the presence of the `getReader` function.
*
* @example
* ```ts
* const obj = getSomeObject();
* if (Stream.isReadableStream(obj)) {
* // obj is a ReadableStream
* }
* ```
*
* @param obj - The object to be checked.
* @returns `true` if `obj` is a `ReadableStream`; otherwise, `false`.
*/
public static isReadableStream(obj: unknown): obj is ReadableStream {
return (
typeof obj === 'object' && obj !== null &&
'getReader' in obj && typeof obj.getReader === 'function'
);
}
/**
* Checks if an object is a Web `ReadableStream`, `WritableStream`, or `TransformStream`.
*
* This method verifies the type of a given object to determine if it is one of the standard
* stream types in the Web Streams API: `ReadableStream`, `WritableStream`, or `TransformStream`.
* It employs type-checking strategies that are specific to each stream type.
*
* The method checks for the specific functions and properties associated with each stream type:
* - `ReadableStream`: Identified by the presence of a `getReader` method.
* - `WritableStream`: Identified by the presence of a `getWriter` and `abort` methods.
* - `TransformStream`: Identified by having both `readable` and `writable` properties.
*
* @example
* ```ts
* const readableStream = new ReadableStream();
* console.log(Stream.isStream(readableStream)); // Output: true
*
* const writableStream = new WritableStream();
* console.log(Stream.isStream(writableStream)); // Output: true
*
* const transformStream = new TransformStream();
* console.log(Stream.isStream(transformStream)); // Output: true
*
* const nonStreamObject = {};
* console.log(Stream.isStream(nonStreamObject)); // Output: false
* ```
*
* @remarks
* - This method does not differentiate between `ReadableStream`, `WritableStream`, and
* `TransformStream`. It checks if the object conforms to any of these types.
* - This method is specific to the Web Streams API and may not recognize non-standard or custom
* stream-like objects that do not adhere to the Web Streams API specifications.
*
* @param obj - The object to be checked for being a Web `ReadableStream`, `WritableStream`, or `TransformStream`.
* @returns `true` if the object is a `ReadableStream`, `WritableStream`, or `TransformStream`; otherwise, `false`.
*/
public static isStream(obj: unknown): obj is ReadableStream | WritableStream | TransformStream {
return Stream.isReadableStream(obj) || Stream.isWritableStream(obj) || Stream.isTransformStream(obj);
}
/**
* Checks if an object is a `TransformStream`.
*
* This method verifies whether the given object is a `TransformStream` by checking its type and
* the presence of `readable` and `writable` properties.
*
* @example
* ```ts
* const obj = getSomeObject();
* if (Stream.isTransformStream(obj)) {
* // obj is a TransformStream
* }
* ```
*
* @param obj - The object to be checked.
* @returns `true` if `obj` is a `TransformStream`; otherwise, `false`.
*/
public static isTransformStream(obj: unknown): obj is TransformStream {
return (
typeof obj === 'object' && obj !== null &&
'readable' in obj && typeof obj.readable === 'object' &&
'writable' in obj && typeof obj.writable === 'object'
);
}
/**
* Checks if an object is a `WritableStream`.
*
* This method determines whether the given object is a `WritableStream` by verifying its type and
* the presence of the `getWriter` and `abort` functions.
*
* @example
* ```ts
* const obj = getSomeObject();
* if (Stream.isWritableStream(obj)) {
* // obj is a WritableStream
* }
* ```
*
* @param obj - The object to be checked.
* @returns `true` if `obj` is a `TransformStream`; otherwise, `false`.
*/
public static isWritableStream(obj: unknown): obj is WritableStream {
return (
typeof obj === 'object' && obj !== null &&
'getWriter' in obj && typeof obj.getWriter === 'function' &&
'abort' in obj && typeof obj.abort === 'function'
);
}
}
+232
View File
@@ -0,0 +1,232 @@
/**
* Represents an array of a fixed length, preventing modifications to its size.
*
* The `FixedLengthArray` utility type transforms a standard array into a variant where
* methods that could alter the length are omitted. It leverages TypeScript's advanced types,
* such as conditional types and mapped types, to ensure that the array cannot be resized
* through methods like `push`, `pop`, `splice`, `shift`, and `unshift`. The utility type
* maintains all other characteristics of a standard array, including indexing, iteration,
* and type checking for its elements.
*
* Note: The type does not prevent direct assignment to indices, even if it would exceed
* the original length. However, such actions would lead to TypeScript type errors.
*
* @example
* ```ts
* // Declare a variable with a type of fixed-length array of three strings.
* let myFixedLengthArray: FixedLengthArray< [string, string, string]>;
*
* // Array declaration tests
* myFixedLengthArray = [ 'a', 'b', 'c' ]; // OK
* myFixedLengthArray = [ 'a', 'b', 123 ]; // TYPE ERROR
* myFixedLengthArray = [ 'a' ]; // LENGTH ERROR
* myFixedLengthArray = [ 'a', 'b' ]; // LENGTH ERROR
*
* // Index assignment tests
* myFixedLengthArray[1] = 'foo'; // OK
* myFixedLengthArray[1000] = 'foo'; // INVALID INDEX ERROR
*
* // Methods that mutate array length
* myFixedLengthArray.push('foo'); // MISSING METHOD ERROR
* myFixedLengthArray.pop(); // MISSING METHOD ERROR
*
* // Direct length manipulation
* myFixedLengthArray.length = 123; // READ-ONLY ERROR
*
* // Destructuring
* let [ a ] = myFixedLengthArray; // OK
* let [ a, b ] = myFixedLengthArray; // OK
* let [ a, b, c ] = myFixedLengthArray; // OK
* let [ a, b, c, d ] = myFixedLengthArray; // INVALID INDEX ERROR
* ```
*
* @template T extends any[] - The array type to be transformed.
*/
export type FixedLengthArray<T extends any[]> =
Pick<T, Exclude<keyof T, ArrayLengthMutationKeys>>
& {
/**
* Custom iterator for the `FixedLengthArray` type.
*
* This iterator allows the `FixedLengthArray` to be used in standard iteration
* contexts, such as `for...of` loops and spread syntax. It ensures that even though
* the array is of a fixed length with disabled mutation methods, it still retains
* iterable behavior similar to a regular array.
*
* @returns An IterableIterator for the array items.
*/
[Symbol.iterator]: () => IterableIterator<ArrayItems<T>>
};
/** Helper types for {@link FixedLengthArray} */
type ArrayLengthMutationKeys = 'splice' | 'push' | 'pop' | 'shift' | 'unshift' | number;
type ArrayItems<T extends Array<any>> = T extends Array<infer TItems> ? TItems : never;
/**
* isArrayBufferSlice
*
* Checks if the ArrayBufferView represents a slice (subarray or a subview)
* of an ArrayBuffer.
*
* An ArrayBufferView (TypedArray or DataView) can represent a portion of an
* ArrayBuffer - such a view is said to be a "slice" of the original buffer.
* This can occur when the `subarray` or `slice` method is called on a
* TypedArray or when a DataView is created with a byteOffset and/or
* byteLength that doesn't cover the full ArrayBuffer.
*
* @param arrayBufferView - The ArrayBufferView to be checked
* @returns true if the ArrayBufferView represents a slice of an ArrayBuffer; false otherwise.
*/
export function isArrayBufferSlice(arrayBufferView: ArrayBufferView): boolean {
return arrayBufferView.byteOffset !== 0 || arrayBufferView.byteLength !== arrayBufferView.buffer.byteLength;
}
/**
* Checks if the given object is an AsyncIterable.
*
* An AsyncIterable is an object that implements the AsyncIterable protocol,
* which means it has a [Symbol.asyncIterator] method. This function checks
* if the provided object conforms to this protocol by verifying the presence
* and type of the [Symbol.asyncIterator] method.
*
* @param obj - The object to be checked for AsyncIterable conformity.
* @returns True if the object is an AsyncIterable, false otherwise.
*
* @example
* ```ts
* // Returns true for a valid AsyncIterable
* const asyncIterable = {
* async *[Symbol.asyncIterator]() {
* yield 1;
* yield 2;
* }
* };
* console.log(isAsyncIterable(asyncIterable)); // true
* ```
*
* @example
* ```ts
* // Returns false for a regular object
* console.log(isAsyncIterable({ a: 1, b: 2 })); // false
* ```
*/
export function isAsyncIterable(obj: any): obj is AsyncIterable<any> {
if (typeof obj !== 'object' || obj === null) {
return false;
}
return typeof obj[Symbol.asyncIterator] === 'function';
}
/**
* isDefined
*
* Utility function to check if a variable is neither null nor undefined.
* This function helps in making TypeScript infer the type of the variable
* as being defined, excluding `null` and `undefined`.
*
* The function uses strict equality (`!==`) for the comparison, ensuring
* that the variable is not just falsy (like an empty string or zero),
* but is truly either `null` or `undefined`.
*
* @param arg - The variable to be checked
* @returns true if the variable is neither `null` nor `undefined`
*/
export function isDefined<T>(arg: T): arg is Exclude<T, null | undefined> {
return arg !== null && typeof arg !== 'undefined';
}
/**
* Utility type that transforms a type `T` to have only certain keys `K` as required, while the
* rest remain optional, except for keys specified in `O`, which are omitted entirely.
*
* This type is useful when you need a variation of a type where only specific properties are
* required, and others are either optional or not included at all. It allows for more flexible type
* definitions based on existing types without the need to redefine them.
*
* @template T - The original type to be transformed.
* @template K - The keys of `T` that should be required.
* @template O - The keys of `T` that should be omitted from the resulting type (optional).
*
* @example
* ```ts
* // Given an interface
* interface Example {
* requiredProp: string;
* optionalProp?: number;
* anotherOptionalProp?: boolean;
* }
*
* // Making 'optionalProp' required and omitting 'anotherOptionalProp'
* type ModifiedExample = RequireOnly<Example, 'optionalProp', 'anotherOptionalProp'>;
* // Result: { requiredProp?: string; optionalProp: number; }
* ```
*/
export type RequireOnly<T, K extends keyof T, O extends keyof T = never> = Required<Pick<T, K>> & Omit<Partial<T>, O>;
/**
* universalTypeOf
*
* Why does this function exist?
*
* You can typically check if a value is of a particular type, such as
* Uint8Array or ArrayBuffer, by using the `instanceof` operator. The
* `instanceof` operator checks the prototype property of a constructor
* in the object's prototype chain.
*
* However, there is a caveat with the `instanceof` check if the value
* was created from a different JavaScript context (like an iframe or
* a web worker). In those cases, the `instanceof` check might fail
* because each context has a different global object, and therefore,
* different built-in constructor functions.
*
* The `typeof` operator provides information about the type of the
* operand in a less detailed way. For basic data types like number,
* string, boolean, and undefined, the `typeof` operator works as
* expected. However, for objects, including arrays and null,
* it always returns "object". For functions, it returns "function".
* So, while `typeof` is good for basic type checking, it doesn't
* give detailed information about complex data types.
*
* Unlike `instanceof` and `typeof`, `Object.prototype.toString.call(value)`
* can ensure a consistent result across different JavaScript
* contexts.
*
* Credit for inspiration:
* Angus Croll
* https://github.com/angus-c
* https://javascriptweblog.wordpress.com/2011/08/08/fixing-the-javascript-typeof-operator/
*/
export function universalTypeOf(value: unknown) {
// Returns '[Object Type]' string.
const typeString = Object.prototype.toString.call(value);
// Returns ['Object', 'Type'] array or null.
const match = typeString.match(/\s([a-zA-Z0-9]+)/);
// Deconstructs the array and gets just the type from index 1.
const [_, type] = match as RegExpMatchArray;
return type;
}
/**
* Utility type to extract the type resolved by a Promise.
*
* This type unwraps the type `T` from `Promise<T>` if `T` is a Promise, otherwise returns `T` as
* is. It's useful in situations where you need to handle the type returned by a promise-based
* function in a synchronous context, such as defining types for test vectors or handling return
* types in non-async code blocks.
*
* @template T - The type to unwrap from the Promise.
*
* @example
* ```ts
* // For a Promise type, it extracts the resolved type.
* type AsyncNumber = Promise<number>;
* type UnwrappedNumber = UnwrapPromise<AsyncNumber>; // number
*
* // For a non-Promise type, it returns the type as is.
* type StringValue = string;
* type UnwrappedString = UnwrapPromise<StringValue>; // string
* ```
*/
export type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;
+43
View File
@@ -0,0 +1,43 @@
/**
* Interface for a generic key-value store.
*/
export interface KeyValueStore<K, V> {
/**
* Clears the store, removing all key-value pairs.
*
* @returns A promise that resolves when the store has been cleared.
*/
clear(): Promise<void>;
/**
* Closes the store, freeing up any resources used. After calling this method, no other operations can be performed on the store.
*
* @returns A promise that resolves when the store has been closed.
*/
close(): Promise<void>;
/**
* Deletes a key-value pair from the store.
*
* @param key - The key of the value to delete.
* @returns A promise that resolves to true if the element existed and has been removed, or false if the element does not exist.
*/
delete(key: K): Promise<boolean | void>;
/**
* Fetches a value from the store given its key.
*
* @param key - The key of the value to retrieve.
* @returns A promise that resolves with the value associated with the key, or `undefined` if no value exists for that key.
*/
get(key: K): Promise<V | undefined>;
/**
* Sets the value for a key in the store.
*
* @param key - The key under which to store the value.
* @param value - The value to be stored.
* @returns A promise that resolves when the value has been set.
*/
set(key: K, value: V): Promise<void>;
}