diff --git a/node/_buffer.ts b/node/_buffer.ts deleted file mode 100644 index d7e8af6ee9f5..000000000000 --- a/node/_buffer.ts +++ /dev/null @@ -1,599 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import * as hex from "../encoding/hex.ts"; -import * as base64 from "../encoding/base64.ts"; -import { Encodings, normalizeEncoding, notImplemented } from "./_utils.ts"; - -const notImplementedEncodings = [ - "ascii", - "binary", - "latin1", - "ucs2", - "utf16le", -]; - -function checkEncoding(encoding = "utf8", strict = true): Encodings { - if (typeof encoding !== "string" || (strict && encoding === "")) { - if (!strict) return "utf8"; - throw new TypeError(`Unkown encoding: ${encoding}`); - } - - const normalized = normalizeEncoding(encoding); - - if (normalized === undefined) { - throw new TypeError(`Unkown encoding: ${encoding}`); - } - - if (notImplementedEncodings.includes(encoding)) { - notImplemented(`"${encoding}" encoding`); - } - - return normalized; -} - -interface EncodingOp { - byteLength(string: string): number; -} - -// https://github.com/nodejs/node/blob/56dbe466fdbc598baea3bfce289bf52b97b8b8f7/lib/buffer.js#L598 -const encodingOps: { [key: string]: EncodingOp } = { - utf8: { - byteLength: (string: string): number => - new TextEncoder().encode(string).byteLength, - }, - ucs2: { - byteLength: (string: string): number => string.length * 2, - }, - utf16le: { - byteLength: (string: string): number => string.length * 2, - }, - latin1: { - byteLength: (string: string): number => string.length, - }, - ascii: { - byteLength: (string: string): number => string.length, - }, - base64: { - byteLength: (string: string): number => - base64ByteLength(string, string.length), - }, - hex: { - byteLength: (string: string): number => string.length >>> 1, - }, -}; - -function base64ByteLength(str: string, bytes: number): number { - // Handle padding - if (str.charCodeAt(bytes - 1) === 0x3d) bytes--; - if (bytes > 1 && str.charCodeAt(bytes - 1) === 0x3d) bytes--; - - // Base64 ratio: 3/4 - return (bytes * 3) >>> 2; -} - -/** - * See also https://nodejs.org/api/buffer.html - */ -export class Buffer extends Uint8Array { - /** - * Allocates a new Buffer of size bytes. - */ - static alloc( - size: number, - fill?: number | string | Uint8Array | Buffer, - encoding = "utf8", - ): Buffer { - if (typeof size !== "number") { - throw new TypeError( - `The "size" argument must be of type number. Received type ${typeof size}`, - ); - } - - const buf = new Buffer(size); - if (size === 0) return buf; - - let bufFill; - if (typeof fill === "string") { - const clearEncoding = checkEncoding(encoding); - if ( - typeof fill === "string" && - fill.length === 1 && - clearEncoding === "utf8" - ) { - buf.fill(fill.charCodeAt(0)); - } else bufFill = Buffer.from(fill, clearEncoding); - } else if (typeof fill === "number") { - buf.fill(fill); - } else if (fill instanceof Uint8Array) { - if (fill.length === 0) { - throw new TypeError( - `The argument "value" is invalid. Received ${fill.constructor.name} []`, - ); - } - - bufFill = fill; - } - - if (bufFill) { - if (bufFill.length > buf.length) { - bufFill = bufFill.subarray(0, buf.length); - } - - let offset = 0; - while (offset < size) { - buf.set(bufFill, offset); - offset += bufFill.length; - if (offset + bufFill.length >= size) break; - } - if (offset !== size) { - buf.set(bufFill.subarray(0, size - offset), offset); - } - } - - return buf; - } - - static allocUnsafe(size: number): Buffer { - return new Buffer(size); - } - - /** - * Returns the byte length of a string when encoded. This is not the same as - * String.prototype.length, which does not account for the encoding that is - * used to convert the string into bytes. - */ - static byteLength( - string: string | Buffer | ArrayBufferView | ArrayBuffer | SharedArrayBuffer, - encoding = "utf8", - ): number { - if (typeof string != "string") return string.byteLength; - - encoding = normalizeEncoding(encoding) || "utf8"; - return encodingOps[encoding].byteLength(string); - } - - /** - * Returns a new Buffer which is the result of concatenating all the Buffer - * instances in the list together. - */ - static concat(list: Buffer[] | Uint8Array[], totalLength?: number): Buffer { - if (totalLength == undefined) { - totalLength = 0; - for (const buf of list) { - totalLength += buf.length; - } - } - - const buffer = Buffer.allocUnsafe(totalLength); - let pos = 0; - for (const item of list) { - let buf: Buffer; - if (!(item instanceof Buffer)) { - buf = Buffer.from(item); - } else { - buf = item; - } - buf.copy(buffer, pos); - pos += buf.length; - } - - return buffer; - } - - /** - * Allocates a new Buffer using an array of bytes in the range 0 – 255. Array - * entries outside that range will be truncated to fit into it. - */ - static from(array: number[]): Buffer; - /** - * This creates a view of the ArrayBuffer without copying the underlying - * memory. For example, when passed a reference to the .buffer property of a - * TypedArray instance, the newly created Buffer will share the same allocated - * memory as the TypedArray. - */ - static from( - arrayBuffer: ArrayBuffer | SharedArrayBuffer, - byteOffset?: number, - length?: number, - ): Buffer; - /** - * Copies the passed buffer data onto a new Buffer instance. - */ - static from(buffer: Buffer | Uint8Array): Buffer; - /** - * Creates a new Buffer containing string. - */ - static from(string: string, encoding?: string): Buffer; - static from( - // deno-lint-ignore no-explicit-any - value: any, - offsetOrEncoding?: number | string, - length?: number, - ): Buffer { - const offset = typeof offsetOrEncoding === "string" - ? undefined - : offsetOrEncoding; - let encoding = typeof offsetOrEncoding === "string" - ? offsetOrEncoding - : undefined; - - if (typeof value == "string") { - encoding = checkEncoding(encoding, false); - if (encoding === "hex") return new Buffer(hex.decodeString(value).buffer); - if (encoding === "base64") return new Buffer(base64.decode(value).buffer); - return new Buffer(new TextEncoder().encode(value).buffer); - } - - // workaround for https://github.com/microsoft/TypeScript/issues/38446 - return new Buffer(value, offset!, length); - } - - /** - * Returns true if obj is a Buffer, false otherwise. - */ - static isBuffer(obj: unknown): obj is Buffer { - return obj instanceof Buffer; - } - - // deno-lint-ignore no-explicit-any - static isEncoding(encoding: any): boolean { - return ( - typeof encoding === "string" && - encoding.length !== 0 && - normalizeEncoding(encoding) !== undefined - ); - } - - /** - * Copies data from a region of buf to a region in target, even if the target - * memory region overlaps with buf. - */ - copy( - targetBuffer: Buffer | Uint8Array, - targetStart = 0, - sourceStart = 0, - sourceEnd = this.length, - ): number { - const sourceBuffer = this - .subarray(sourceStart, sourceEnd) - .subarray(0, Math.max(0, targetBuffer.length - targetStart)); - - if (sourceBuffer.length === 0) return 0; - - targetBuffer.set(sourceBuffer, targetStart); - return sourceBuffer.length; - } - - /* - * Returns true if both buf and otherBuffer have exactly the same bytes, false otherwise. - */ - equals(otherBuffer: Uint8Array | Buffer): boolean { - if (!(otherBuffer instanceof Uint8Array)) { - throw new TypeError( - `The "otherBuffer" argument must be an instance of Buffer or Uint8Array. Received type ${typeof otherBuffer}`, - ); - } - - if (this === otherBuffer) return true; - if (this.byteLength !== otherBuffer.byteLength) return false; - - for (let i = 0; i < this.length; i++) { - if (this[i] !== otherBuffer[i]) return false; - } - - return true; - } - - readBigInt64BE(offset = 0): bigint { - return new DataView( - this.buffer, - this.byteOffset, - this.byteLength, - ).getBigInt64(offset); - } - readBigInt64LE(offset = 0): bigint { - return new DataView( - this.buffer, - this.byteOffset, - this.byteLength, - ).getBigInt64(offset, true); - } - - readBigUInt64BE(offset = 0): bigint { - return new DataView( - this.buffer, - this.byteOffset, - this.byteLength, - ).getBigUint64(offset); - } - readBigUInt64LE(offset = 0): bigint { - return new DataView( - this.buffer, - this.byteOffset, - this.byteLength, - ).getBigUint64(offset, true); - } - - readDoubleBE(offset = 0): number { - return new DataView( - this.buffer, - this.byteOffset, - this.byteLength, - ).getFloat64(offset); - } - readDoubleLE(offset = 0): number { - return new DataView( - this.buffer, - this.byteOffset, - this.byteLength, - ).getFloat64(offset, true); - } - - readFloatBE(offset = 0): number { - return new DataView( - this.buffer, - this.byteOffset, - this.byteLength, - ).getFloat32(offset); - } - readFloatLE(offset = 0): number { - return new DataView( - this.buffer, - this.byteOffset, - this.byteLength, - ).getFloat32(offset, true); - } - - readInt8(offset = 0): number { - return new DataView(this.buffer, this.byteOffset, this.byteLength).getInt8( - offset, - ); - } - - readInt16BE(offset = 0): number { - return new DataView(this.buffer, this.byteOffset, this.byteLength).getInt16( - offset, - ); - } - readInt16LE(offset = 0): number { - return new DataView(this.buffer, this.byteOffset, this.byteLength).getInt16( - offset, - true, - ); - } - - readInt32BE(offset = 0): number { - return new DataView(this.buffer, this.byteOffset, this.byteLength).getInt32( - offset, - ); - } - readInt32LE(offset = 0): number { - return new DataView(this.buffer, this.byteOffset, this.byteLength).getInt32( - offset, - true, - ); - } - - readUInt8(offset = 0): number { - return new DataView(this.buffer, this.byteOffset, this.byteLength).getUint8( - offset, - ); - } - - readUInt16BE(offset = 0): number { - return new DataView( - this.buffer, - this.byteOffset, - this.byteLength, - ).getUint16(offset); - } - readUInt16LE(offset = 0): number { - return new DataView( - this.buffer, - this.byteOffset, - this.byteLength, - ).getUint16(offset, true); - } - - readUInt32BE(offset = 0): number { - return new DataView( - this.buffer, - this.byteOffset, - this.byteLength, - ).getUint32(offset); - } - readUInt32LE(offset = 0): number { - return new DataView( - this.buffer, - this.byteOffset, - this.byteLength, - ).getUint32(offset, true); - } - - /** - * Returns a new Buffer that references the same memory as the original, but - * offset and cropped by the start and end indices. - */ - slice(begin = 0, end = this.length): Buffer { - // workaround for https://github.com/microsoft/TypeScript/issues/38665 - return this.subarray(begin, end) as Buffer; - } - - /** - * Returns a JSON representation of buf. JSON.stringify() implicitly calls - * this function when stringifying a Buffer instance. - */ - toJSON(): Record { - return { type: "Buffer", data: Array.from(this) }; - } - - /** - * Decodes buf to a string according to the specified character encoding in - * encoding. start and end may be passed to decode only a subset of buf. - */ - toString(encoding = "utf8", start = 0, end = this.length): string { - encoding = checkEncoding(encoding); - - const b = this.subarray(start, end); - if (encoding === "hex") return hex.encodeToString(b); - if (encoding === "base64") return base64.encode(b.buffer); - - return new TextDecoder(encoding).decode(b); - } - - /** - * Writes string to buf at offset according to the character encoding in - * encoding. The length parameter is the number of bytes to write. If buf did - * not contain enough space to fit the entire string, only part of string will - * be written. However, partially encoded characters will not be written. - */ - write(string: string, offset = 0, length = this.length): number { - return new TextEncoder().encodeInto( - string, - this.subarray(offset, offset + length), - ).written; - } - - writeBigInt64BE(value: bigint, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setBigInt64( - offset, - value, - ); - return offset + 4; - } - writeBigInt64LE(value: bigint, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setBigInt64( - offset, - value, - true, - ); - return offset + 4; - } - - writeBigUInt64BE(value: bigint, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setBigUint64( - offset, - value, - ); - return offset + 4; - } - writeBigUInt64LE(value: bigint, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setBigUint64( - offset, - value, - true, - ); - return offset + 4; - } - - writeDoubleBE(value: number, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setFloat64( - offset, - value, - ); - return offset + 8; - } - writeDoubleLE(value: number, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setFloat64( - offset, - value, - true, - ); - return offset + 8; - } - - writeFloatBE(value: number, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setFloat32( - offset, - value, - ); - return offset + 4; - } - writeFloatLE(value: number, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setFloat32( - offset, - value, - true, - ); - return offset + 4; - } - - writeInt8(value: number, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setInt8( - offset, - value, - ); - return offset + 1; - } - - writeInt16BE(value: number, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setInt16( - offset, - value, - ); - return offset + 2; - } - writeInt16LE(value: number, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setInt16( - offset, - value, - true, - ); - return offset + 2; - } - - writeInt32BE(value: number, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setUint32( - offset, - value, - ); - return offset + 4; - } - writeInt32LE(value: number, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setInt32( - offset, - value, - true, - ); - return offset + 4; - } - - writeUInt8(value: number, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setUint8( - offset, - value, - ); - return offset + 1; - } - - writeUInt16BE(value: number, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setUint16( - offset, - value, - ); - return offset + 2; - } - writeUInt16LE(value: number, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setUint16( - offset, - value, - true, - ); - return offset + 2; - } - - writeUInt32BE(value: number, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setUint32( - offset, - value, - ); - return offset + 4; - } - writeUInt32LE(value: number, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setUint32( - offset, - value, - true, - ); - return offset + 4; - } -} diff --git a/node/_crypto.ts b/node/_crypto.ts deleted file mode 100644 index 86c2b772fa58..000000000000 --- a/node/_crypto.ts +++ /dev/null @@ -1,3 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -export { default as randomBytes } from "./_crypto/randomBytes.ts"; -export { pbkdf2, pbkdf2Sync } from "./_crypto/pbkdf2.ts"; diff --git a/node/_fs.ts b/node/_fs.ts deleted file mode 100644 index 052394e21073..000000000000 --- a/node/_fs.ts +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { access, accessSync } from "./_fs/_fs_access.ts"; -import { appendFile, appendFileSync } from "./_fs/_fs_appendFile.ts"; -import { chmod, chmodSync } from "./_fs/_fs_chmod.ts"; -import { chown, chownSync } from "./_fs/_fs_chown.ts"; -import { close, closeSync } from "./_fs/_fs_close.ts"; -import * as constants from "./_fs/_fs_constants.ts"; -import { readFile, readFileSync } from "./_fs/_fs_readFile.ts"; -import { readlink, readlinkSync } from "./_fs/_fs_readlink.ts"; -import { exists, existsSync } from "./_fs/_fs_exists.ts"; -import { mkdir, mkdirSync } from "./_fs/_fs_mkdir.ts"; -import { copyFile, copyFileSync } from "./_fs/_fs_copy.ts"; -import { writeFile, writeFileSync } from "./_fs/_fs_writeFile.ts"; -import { readdir, readdirSync } from "./_fs/_fs_readdir.ts"; -import { realpath, realpathSync } from "./_fs/_fs_realpath.ts"; -import { rename, renameSync } from "./_fs/_fs_rename.ts"; -import { rmdir, rmdirSync } from "./_fs/_fs_rmdir.ts"; -import { unlink, unlinkSync } from "./_fs/_fs_unlink.ts"; -import { watch } from "./_fs/_fs_watch.ts"; -import { open, openSync } from "./_fs/_fs_open.ts"; -import { stat, statSync } from "./_fs/_fs_stat.ts"; -import { lstat, lstatSync } from "./_fs/_fs_lstat.ts"; - -import * as promises from "./_fs/promises/mod.ts"; - -export { - access, - accessSync, - appendFile, - appendFileSync, - chmod, - chmodSync, - chown, - chownSync, - close, - closeSync, - constants, - copyFile, - copyFileSync, - exists, - existsSync, - lstat, - lstatSync, - mkdir, - mkdirSync, - open, - openSync, - promises, - readdir, - readdirSync, - readFile, - readFileSync, - readlink, - readlinkSync, - realpath, - realpathSync, - rename, - renameSync, - rmdir, - rmdirSync, - stat, - statSync, - unlink, - unlinkSync, - watch, - writeFile, - writeFileSync, -}; diff --git a/node/_os.ts b/node/_os.ts deleted file mode 100644 index 14bf20abd2ad..000000000000 --- a/node/_os.ts +++ /dev/null @@ -1,224 +0,0 @@ -// 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. -import { notImplemented } from "./_utils.ts"; -import { validateIntegerRange } from "./_utils.ts"; -import { EOL as fsEOL } from "../fs/eol.ts"; -import process from "./process.ts"; - -const SEE_GITHUB_ISSUE = "See https://github.com/denoland/deno/issues/3802"; - -interface CPUTimes { - /** The number of milliseconds the CPU has spent in user mode */ - user: number; - - /** The number of milliseconds the CPU has spent in nice mode */ - nice: number; - - /** The number of milliseconds the CPU has spent in sys mode */ - sys: number; - - /** The number of milliseconds the CPU has spent in idle mode */ - idle: number; - - /** The number of milliseconds the CPU has spent in irq mode */ - irq: number; -} - -interface CPUCoreInfo { - model: string; - - /** in MHz */ - speed: number; - - times: CPUTimes; -} - -interface NetworkAddress { - /** The assigned IPv4 or IPv6 address */ - address: string; - - /** The IPv4 or IPv6 network mask */ - netmask: string; - - family: "IPv4" | "IPv6"; - - /** The MAC address of the network interface */ - mac: string; - - /** true if the network interface is a loopback or similar interface that is not remotely accessible; otherwise false */ - internal: boolean; - - /** The numeric IPv6 scope ID (only specified when family is IPv6) */ - scopeid?: number; - - /** The assigned IPv4 or IPv6 address with the routing prefix in CIDR notation. If the netmask is invalid, this property is set to null. */ - cidr: string; -} - -interface NetworkInterfaces { - [key: string]: NetworkAddress[]; -} - -export interface UserInfoOptions { - encoding: string; -} - -interface UserInfo { - username: string; - uid: number; - gid: number; - shell: string; - homedir: string; -} - -arch[Symbol.toPrimitive] = (): string => arch(); -endianness[Symbol.toPrimitive] = (): string => endianness(); -freemem[Symbol.toPrimitive] = (): number => freemem(); -homedir[Symbol.toPrimitive] = (): string | null => homedir(); -hostname[Symbol.toPrimitive] = (): string | null => hostname(); -platform[Symbol.toPrimitive] = (): string => platform(); -release[Symbol.toPrimitive] = (): string => release(); -totalmem[Symbol.toPrimitive] = (): number => totalmem(); -type[Symbol.toPrimitive] = (): string => type(); -uptime[Symbol.toPrimitive] = (): number => uptime(); - -/** Returns the operating system CPU architecture for which the Deno binary was compiled */ -export function arch(): string { - return Deno.build.arch; -} - -/** Not yet implemented */ -export function cpus(): CPUCoreInfo[] { - notImplemented(SEE_GITHUB_ISSUE); -} - -/** - * Returns a string identifying the endianness of the CPU for which the Deno - * binary was compiled. Possible values are 'BE' for big endian and 'LE' for - * little endian. - **/ -export function endianness(): "BE" | "LE" { - // Source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView#Endianness - const buffer = new ArrayBuffer(2); - new DataView(buffer).setInt16(0, 256, true /* littleEndian */); - // Int16Array uses the platform's endianness. - return new Int16Array(buffer)[0] === 256 ? "LE" : "BE"; -} - -/** Return free memory amount */ -export function freemem(): number { - return Deno.systemMemoryInfo().free; -} - -/** Not yet implemented */ -export function getPriority(pid = 0): number { - validateIntegerRange(pid, "pid"); - notImplemented(SEE_GITHUB_ISSUE); -} - -/** Returns the string path of the current user's home directory. */ -export function homedir(): string | null { - notImplemented(SEE_GITHUB_ISSUE); -} - -/** Returns the host name of the operating system as a string. */ -export function hostname(): string { - notImplemented(SEE_GITHUB_ISSUE); -} - -/** Returns an array containing the 1, 5, and 15 minute load averages */ -export function loadavg(): number[] { - if (Deno.build.os === "windows") { - return [0, 0, 0]; - } - return Deno.loadavg(); -} - -/** Not yet implemented */ -export function networkInterfaces(): NetworkInterfaces { - notImplemented(SEE_GITHUB_ISSUE); -} -/** Returns the a string identifying the operating system platform. The value is set at compile time. Possible values are 'darwin', 'linux', and 'win32'. */ -export function platform(): string { - return process.platform; -} - -/** Returns the operating system as a string */ -export function release(): string { - return Deno.osRelease(); -} - -/** Not yet implemented */ -export function setPriority(pid: number, priority?: number): void { - /* The node API has the 'pid' as the first parameter and as optional. - This makes for a problematic implementation in Typescript. */ - if (priority === undefined) { - priority = pid; - pid = 0; - } - validateIntegerRange(pid, "pid"); - validateIntegerRange(priority, "priority", -20, 19); - - notImplemented(SEE_GITHUB_ISSUE); -} - -/** Returns the operating system's default directory for temporary files as a string. */ -export function tmpdir(): string | null { - notImplemented(SEE_GITHUB_ISSUE); -} - -/** Return total physical memory amount */ -export function totalmem(): number { - return Deno.systemMemoryInfo().total; -} - -/** Not yet implemented */ -export function type(): string { - notImplemented(SEE_GITHUB_ISSUE); -} - -/** Not yet implemented */ -export function uptime(): number { - notImplemented(SEE_GITHUB_ISSUE); -} - -/** Not yet implemented */ -export function userInfo( - options: UserInfoOptions = { encoding: "utf-8" }, -): UserInfo { - notImplemented(SEE_GITHUB_ISSUE); -} - -export const constants = { - // UV_UDP_REUSEADDR: 4, //see https://nodejs.org/docs/latest-v12.x/api/os.html#os_libuv_constants - dlopen: { - // see https://nodejs.org/docs/latest-v12.x/api/os.html#os_dlopen_constants - }, - errno: { - // see https://nodejs.org/docs/latest-v12.x/api/os.html#os_error_constants - }, - signals: Deno.Signal, - priority: { - // see https://nodejs.org/docs/latest-v12.x/api/os.html#os_priority_constants - }, -}; - -export const EOL = Deno.build.os == "windows" ? fsEOL.CRLF : fsEOL.LF; diff --git a/node/_querystring.ts b/node/_querystring.ts deleted file mode 100644 index a49f55f5478e..000000000000 --- a/node/_querystring.ts +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. - -interface ParseOptions { - /** The function to use when decoding percent-encoded characters in the query string. */ - decodeURIComponent?: (string: string) => string; - /** Specifies the maximum number of keys to parse. */ - maxKeys?: number; -} - -export const hexTable = new Array(256); -for (let i = 0; i < 256; ++i) { - hexTable[i] = "%" + ((i < 16 ? "0" : "") + i.toString(16)).toUpperCase(); -} - -/** - * Parses a URL query string into a collection of key and value pairs. - * @param str The URL query string to parse - * @param sep The substring used to delimit key and value pairs in the query string. Default: '&'. - * @param eq The substring used to delimit keys and values in the query string. Default: '='. - * @param options The parse options - */ -export function parse( - str: string, - sep = "&", - eq = "=", - { decodeURIComponent = unescape, maxKeys = 1000 }: ParseOptions = {}, -): { [key: string]: string[] | string } { - const entries = str - .split(sep) - .map((entry) => entry.split(eq).map(decodeURIComponent)); - const final: { [key: string]: string[] | string } = {}; - - let i = 0; - while (true) { - if ((Object.keys(final).length === maxKeys && !!maxKeys) || !entries[i]) { - break; - } - - const [key, val] = entries[i]; - if (final[key]) { - if (Array.isArray(final[key])) { - (final[key] as string[]).push(val); - } else { - final[key] = [final[key] as string, val]; - } - } else { - final[key] = val; - } - - i++; - } - - return final; -} - -interface StringifyOptions { - /** The function to use when converting URL-unsafe characters to percent-encoding in the query string. */ - encodeURIComponent?: (string: string) => string; -} - -export function encodeStr( - str: string, - noEscapeTable: number[], - hexTable: string[], -): string { - const len = str.length; - if (len === 0) return ""; - - let out = ""; - let lastPos = 0; - - for (let i = 0; i < len; i++) { - let c = str.charCodeAt(i); - // ASCII - if (c < 0x80) { - if (noEscapeTable[c] === 1) continue; - if (lastPos < i) out += str.slice(lastPos, i); - lastPos = i + 1; - out += hexTable[c]; - continue; - } - - if (lastPos < i) out += str.slice(lastPos, i); - - // Multi-byte characters ... - if (c < 0x800) { - lastPos = i + 1; - out += hexTable[0xc0 | (c >> 6)] + hexTable[0x80 | (c & 0x3f)]; - continue; - } - if (c < 0xd800 || c >= 0xe000) { - lastPos = i + 1; - out += hexTable[0xe0 | (c >> 12)] + - hexTable[0x80 | ((c >> 6) & 0x3f)] + - hexTable[0x80 | (c & 0x3f)]; - continue; - } - // Surrogate pair - ++i; - - // This branch should never happen because all URLSearchParams entries - // should already be converted to USVString. But, included for - // completion's sake anyway. - if (i >= len) throw new Deno.errors.InvalidData("invalid URI"); - - const c2 = str.charCodeAt(i) & 0x3ff; - - lastPos = i + 1; - c = 0x10000 + (((c & 0x3ff) << 10) | c2); - out += hexTable[0xf0 | (c >> 18)] + - hexTable[0x80 | ((c >> 12) & 0x3f)] + - hexTable[0x80 | ((c >> 6) & 0x3f)] + - hexTable[0x80 | (c & 0x3f)]; - } - if (lastPos === 0) return str; - if (lastPos < len) return out + str.slice(lastPos); - return out; -} - -/** - * Produces a URL query string from a given obj by iterating through the object's "own properties". - * @param obj The object to serialize into a URL query string. - * @param sep The substring used to delimit key and value pairs in the query string. Default: '&'. - * @param eq The substring used to delimit keys and values in the query string. Default: '='. - * @param options The stringify options - */ -export function stringify( - // deno-lint-ignore no-explicit-any - obj: Record, - sep = "&", - eq = "=", - { encodeURIComponent = escape }: StringifyOptions = {}, -): string { - const final = []; - - for (const entry of Object.entries(obj)) { - if (Array.isArray(entry[1])) { - for (const val of entry[1]) { - final.push(encodeURIComponent(entry[0]) + eq + encodeURIComponent(val)); - } - } else if (typeof entry[1] !== "object" && entry[1] !== undefined) { - final.push(entry.map(encodeURIComponent).join(eq)); - } else { - final.push(encodeURIComponent(entry[0]) + eq); - } - } - - return final.join(sep); -} - -/** Alias of querystring.parse() */ -export const decode = parse; -/** Alias of querystring.stringify() */ -export const encode = stringify; -export const unescape = decodeURIComponent; -export const escape = encodeURIComponent; diff --git a/node/_string_decoder.ts b/node/_string_decoder.ts deleted file mode 100644 index 623070f584cd..000000000000 --- a/node/_string_decoder.ts +++ /dev/null @@ -1,311 +0,0 @@ -// 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. - -import { Buffer } from "./buffer.ts"; -import { normalizeEncoding as castEncoding, notImplemented } from "./_utils.ts"; - -enum NotImplemented { - "ascii", - "latin1", - "utf16le", -} - -function normalizeEncoding(enc?: string): string { - const encoding = castEncoding(enc ?? null); - if (encoding && encoding in NotImplemented) notImplemented(encoding); - if (!encoding && typeof enc === "string" && enc.toLowerCase() !== "raw") { - throw new Error(`Unknown encoding: ${enc}`); - } - return String(encoding); -} -/* - * Checks the type of a UTF-8 byte, whether it's ASCII, a leading byte, or a - * continuation byte. If an invalid byte is detected, -2 is returned. - * */ -function utf8CheckByte(byte: number): number { - if (byte <= 0x7f) return 0; - else if (byte >> 5 === 0x06) return 2; - else if (byte >> 4 === 0x0e) return 3; - else if (byte >> 3 === 0x1e) return 4; - return byte >> 6 === 0x02 ? -1 : -2; -} - -/* - * Checks at most 3 bytes at the end of a Buffer in order to detect an - * incomplete multi-byte UTF-8 character. The total number of bytes (2, 3, or 4) - * needed to complete the UTF-8 character (if applicable) are returned. - * */ -function utf8CheckIncomplete( - self: StringDecoderBase, - buf: Buffer, - i: number, -): number { - let j = buf.length - 1; - if (j < i) return 0; - let nb = utf8CheckByte(buf[j]); - if (nb >= 0) { - if (nb > 0) self.lastNeed = nb - 1; - return nb; - } - if (--j < i || nb === -2) return 0; - nb = utf8CheckByte(buf[j]); - if (nb >= 0) { - if (nb > 0) self.lastNeed = nb - 2; - return nb; - } - if (--j < i || nb === -2) return 0; - nb = utf8CheckByte(buf[j]); - if (nb >= 0) { - if (nb > 0) { - if (nb === 2) nb = 0; - else self.lastNeed = nb - 3; - } - return nb; - } - return 0; -} - -/* - * Validates as many continuation bytes for a multi-byte UTF-8 character as - * needed or are available. If we see a non-continuation byte where we expect - * one, we "replace" the validated continuation bytes we've seen so far with - * a single UTF-8 replacement character ('\ufffd'), to match v8's UTF-8 decoding - * behavior. The continuation byte check is included three times in the case - * where all of the continuation bytes for a character exist in the same buffer. - * It is also done this way as a slight performance increase instead of using a - * loop. - * */ -function utf8CheckExtraBytes( - self: StringDecoderBase, - buf: Buffer, -): string | undefined { - if ((buf[0] & 0xc0) !== 0x80) { - self.lastNeed = 0; - return "\ufffd"; - } - if (self.lastNeed > 1 && buf.length > 1) { - if ((buf[1] & 0xc0) !== 0x80) { - self.lastNeed = 1; - return "\ufffd"; - } - if (self.lastNeed > 2 && buf.length > 2) { - if ((buf[2] & 0xc0) !== 0x80) { - self.lastNeed = 2; - return "\ufffd"; - } - } - } -} - -/* - * Attempts to complete a multi-byte UTF-8 character using bytes from a Buffer. - * */ -function utf8FillLastComplete( - this: StringDecoderBase, - buf: Buffer, -): string | undefined { - const p = this.lastTotal - this.lastNeed; - const r = utf8CheckExtraBytes(this, buf); - if (r !== undefined) return r; - if (this.lastNeed <= buf.length) { - buf.copy(this.lastChar, p, 0, this.lastNeed); - return this.lastChar.toString(this.encoding, 0, this.lastTotal); - } - buf.copy(this.lastChar, p, 0, buf.length); - this.lastNeed -= buf.length; -} - -/* - * Attempts to complete a partial non-UTF-8 character using bytes from a Buffer - * */ -function utf8FillLastIncomplete( - this: StringDecoderBase, - buf: Buffer, -): string | undefined { - if (this.lastNeed <= buf.length) { - buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, this.lastNeed); - return this.lastChar.toString(this.encoding, 0, this.lastTotal); - } - buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, buf.length); - this.lastNeed -= buf.length; -} - -/* - * Returns all complete UTF-8 characters in a Buffer. If the Buffer ended on a - * partial character, the character's bytes are buffered until the required - * number of bytes are available. - * */ -function utf8Text(this: StringDecoderBase, buf: Buffer, i: number): string { - const total = utf8CheckIncomplete(this, buf, i); - if (!this.lastNeed) return buf.toString("utf8", i); - this.lastTotal = total; - const end = buf.length - (total - this.lastNeed); - buf.copy(this.lastChar, 0, end); - return buf.toString("utf8", i, end); -} - -/* - * For UTF-8, a replacement character is added when ending on a partial - * character. - * */ -function utf8End(this: Utf8Decoder, buf?: Buffer): string { - const r = buf && buf.length ? this.write(buf) : ""; - if (this.lastNeed) return r + "\ufffd"; - return r; -} - -function utf8Write( - this: Utf8Decoder | Base64Decoder, - buf: Buffer | string, -): string { - if (typeof buf === "string") { - return buf; - } - if (buf.length === 0) return ""; - let r; - let i; - if (this.lastNeed) { - r = this.fillLast(buf); - if (r === undefined) return ""; - i = this.lastNeed; - this.lastNeed = 0; - } else { - i = 0; - } - if (i < buf.length) return r ? r + this.text(buf, i) : this.text(buf, i); - return r || ""; -} - -function base64Text(this: StringDecoderBase, buf: Buffer, i: number): string { - const n = (buf.length - i) % 3; - if (n === 0) return buf.toString("base64", i); - this.lastNeed = 3 - n; - this.lastTotal = 3; - if (n === 1) { - this.lastChar[0] = buf[buf.length - 1]; - } else { - this.lastChar[0] = buf[buf.length - 2]; - this.lastChar[1] = buf[buf.length - 1]; - } - return buf.toString("base64", i, buf.length - n); -} - -function base64End(this: Base64Decoder, buf?: Buffer): string { - const r = buf && buf.length ? this.write(buf) : ""; - if (this.lastNeed) { - return r + this.lastChar.toString("base64", 0, 3 - this.lastNeed); - } - return r; -} - -function simpleWrite( - this: StringDecoderBase, - buf: Buffer | string, -): string { - if (typeof buf === "string") { - return buf; - } - return buf.toString(this.encoding); -} - -function simpleEnd(this: GenericDecoder, buf?: Buffer): string { - return buf && buf.length ? this.write(buf) : ""; -} - -class StringDecoderBase { - public lastChar: Buffer; - public lastNeed = 0; - public lastTotal = 0; - constructor(public encoding: string, nb: number) { - this.lastChar = Buffer.allocUnsafe(nb); - } -} - -class Base64Decoder extends StringDecoderBase { - public end = base64End; - public fillLast = utf8FillLastIncomplete; - public text = base64Text; - public write = utf8Write; - - constructor(encoding?: string) { - super(normalizeEncoding(encoding), 3); - } -} - -class GenericDecoder extends StringDecoderBase { - public end = simpleEnd; - public fillLast = undefined; - public text = utf8Text; - public write = simpleWrite; - - constructor(encoding?: string) { - super(normalizeEncoding(encoding), 4); - } -} - -class Utf8Decoder extends StringDecoderBase { - public end = utf8End; - public fillLast = utf8FillLastComplete; - public text = utf8Text; - public write = utf8Write; - - constructor(encoding?: string) { - super(normalizeEncoding(encoding), 4); - } -} - -/* - * StringDecoder provides an interface for efficiently splitting a series of - * buffers into a series of JS strings without breaking apart multi-byte - * characters. - * */ -export class StringDecoder { - public encoding: string; - public end: (buf?: Buffer) => string; - public fillLast: ((buf: Buffer) => string | undefined) | undefined; - public lastChar: Buffer; - public lastNeed: number; - public lastTotal: number; - public text: (buf: Buffer, n: number) => string; - public write: (buf: Buffer) => string; - - constructor(encoding?: string) { - let decoder; - switch (encoding) { - case "utf8": - decoder = new Utf8Decoder(encoding); - break; - case "base64": - decoder = new Base64Decoder(encoding); - break; - default: - decoder = new GenericDecoder(encoding); - } - this.encoding = decoder.encoding; - this.end = decoder.end; - this.fillLast = decoder.fillLast; - this.lastChar = decoder.lastChar; - this.lastNeed = decoder.lastNeed; - this.lastTotal = decoder.lastTotal; - this.text = decoder.text; - this.write = decoder.write; - } -} diff --git a/node/_timers.ts b/node/_timers.ts deleted file mode 100644 index 872e1f9ae069..000000000000 --- a/node/_timers.ts +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -// TODO: implement the 'NodeJS.Timeout' and 'NodeJS.Immediate' versions of the timers. -// https://github.com/DefinitelyTyped/DefinitelyTyped/blob/1163ead296d84e7a3c80d71e7c81ecbd1a130e9a/types/node/v12/globals.d.ts#L1120-L1131 -export const setTimeout = window.setTimeout; -export const clearTimeout = window.clearTimeout; -export const setInterval = window.setInterval; -export const clearInterval = window.clearInterval; -export const setImmediate = ( - // deno-lint-ignore no-explicit-any - cb: (...args: any[]) => void, - // deno-lint-ignore no-explicit-any - ...args: any[] -): number => window.setTimeout(cb, 0, ...args); -export const clearImmediate = window.clearTimeout; diff --git a/node/_url.ts b/node/_url.ts deleted file mode 100644 index f85286df237e..000000000000 --- a/node/_url.ts +++ /dev/null @@ -1,139 +0,0 @@ -// 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. - -import { - CHAR_BACKWARD_SLASH, - CHAR_FORWARD_SLASH, - CHAR_LOWERCASE_A, - CHAR_LOWERCASE_Z, -} from "../path/_constants.ts"; -import * as path from "./path.ts"; -import { isWindows } from "../_util/os.ts"; - -const forwardSlashRegEx = /\//g; -const percentRegEx = /%/g; -const backslashRegEx = /\\/g; -const newlineRegEx = /\n/g; -const carriageReturnRegEx = /\r/g; -const tabRegEx = /\t/g; - -const _url = URL; -export { _url as URL }; - -/** - * Get fully resolved platform-specific file path from the given URL string/ object - * @param path The file URL string or URL object to convert to a path - */ -export function fileURLToPath(path: string | URL): string { - if (typeof path === "string") path = new URL(path); - else if (!(path instanceof URL)) { - throw new Deno.errors.InvalidData( - "invalid argument path , must be a string or URL", - ); - } - if (path.protocol !== "file:") { - throw new Deno.errors.InvalidData("invalid url scheme"); - } - return isWindows ? getPathFromURLWin(path) : getPathFromURLPosix(path); -} - -function getPathFromURLWin(url: URL): string { - const hostname = url.hostname; - let pathname = url.pathname; - for (let n = 0; n < pathname.length; n++) { - if (pathname[n] === "%") { - const third = pathname.codePointAt(n + 2) || 0x20; - if ( - (pathname[n + 1] === "2" && third === 102) || // 2f 2F / - (pathname[n + 1] === "5" && third === 99) - ) { - // 5c 5C \ - throw new Deno.errors.InvalidData( - "must not include encoded \\ or / characters", - ); - } - } - } - - pathname = pathname.replace(forwardSlashRegEx, "\\"); - pathname = decodeURIComponent(pathname); - if (hostname !== "") { - //TODO add support for punycode encodings - return `\\\\${hostname}${pathname}`; - } else { - // Otherwise, it's a local path that requires a drive letter - const letter = pathname.codePointAt(1)! | 0x20; - const sep = pathname[2]; - if ( - letter < CHAR_LOWERCASE_A || - letter > CHAR_LOWERCASE_Z || // a..z A..Z - sep !== ":" - ) { - throw new Deno.errors.InvalidData("file url path must be absolute"); - } - return pathname.slice(1); - } -} - -function getPathFromURLPosix(url: URL): string { - if (url.hostname !== "") { - throw new Deno.errors.InvalidData("invalid file url hostname"); - } - const pathname = url.pathname; - for (let n = 0; n < pathname.length; n++) { - if (pathname[n] === "%") { - const third = pathname.codePointAt(n + 2) || 0x20; - if (pathname[n + 1] === "2" && third === 102) { - throw new Deno.errors.InvalidData( - "must not include encoded / characters", - ); - } - } - } - return decodeURIComponent(pathname); -} - -/** Get fully resolved platform-specific File URL from the given file path */ -export function pathToFileURL(filepath: string): URL { - let resolved = path.resolve(filepath); - // path.resolve strips trailing slashes so we must add them back - const filePathLast = filepath.charCodeAt(filepath.length - 1); - if ( - (filePathLast === CHAR_FORWARD_SLASH || - (isWindows && filePathLast === CHAR_BACKWARD_SLASH)) && - resolved[resolved.length - 1] !== path.sep - ) { - resolved += "/"; - } - const outURL = new URL("file://"); - if (resolved.includes("%")) resolved = resolved.replace(percentRegEx, "%25"); - // In posix, "/" is a valid character in paths - if (!isWindows && resolved.includes("\\")) { - resolved = resolved.replace(backslashRegEx, "%5C"); - } - if (resolved.includes("\n")) resolved = resolved.replace(newlineRegEx, "%0A"); - if (resolved.includes("\r")) { - resolved = resolved.replace(carriageReturnRegEx, "%0D"); - } - if (resolved.includes("\t")) resolved = resolved.replace(tabRegEx, "%09"); - outURL.pathname = resolved; - return outURL; -} diff --git a/node/_util.ts b/node/_util.ts deleted file mode 100644 index 78a9e1d8b1d5..000000000000 --- a/node/_util.ts +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -export { promisify } from "./_util/_util_promisify.ts"; -export { callbackify } from "./_util/_util_callbackify.ts"; -import { ERR_INVALID_ARG_TYPE, ERR_OUT_OF_RANGE, errorMap } from "./_errors.ts"; -import * as types from "./_util/_util_types.ts"; -export { types }; - -const NumberIsSafeInteger = Number.isSafeInteger; - -const DEFAULT_INSPECT_OPTIONS = { - showHidden: false, - depth: 2, - colors: false, - customInspect: true, - showProxy: false, - maxArrayLength: 100, - maxStringLength: Infinity, - breakLength: 80, - compact: 3, - sorted: false, - getters: false, -}; - -inspect.defaultOptions = DEFAULT_INSPECT_OPTIONS; -inspect.custom = Deno.customInspect; - -// TODO(schwarzkopfb): make it in-line with Node's implementation -// Ref: https://nodejs.org/dist/latest-v14.x/docs/api/util.html#util_util_inspect_object_options -// deno-lint-ignore no-explicit-any -export function inspect(object: unknown, ...opts: any): string { - opts = { ...DEFAULT_INSPECT_OPTIONS, ...opts }; - return Deno.inspect(object, { - depth: opts.depth, - iterableLimit: opts.maxArrayLength, - compact: !!opts.compact, - sorted: !!opts.sorted, - showProxy: !!opts.showProxy, - }); -} - -/** @deprecated - use `Array.isArray()` instead. */ -export function isArray(value: unknown): boolean { - return Array.isArray(value); -} - -/** @deprecated - use `typeof value === "boolean" || value instanceof Boolean` instead. */ -export function isBoolean(value: unknown): boolean { - return typeof value === "boolean" || value instanceof Boolean; -} - -/** @deprecated - use `value === null` instead. */ -export function isNull(value: unknown): boolean { - return value === null; -} - -/** @deprecated - use `value === null || value === undefined` instead. */ -export function isNullOrUndefined(value: unknown): boolean { - return value === null || value === undefined; -} - -/** @deprecated - use `typeof value === "number" || value instanceof Number` instead. */ -export function isNumber(value: unknown): boolean { - return typeof value === "number" || value instanceof Number; -} - -/** @deprecated - use `typeof value === "string" || value instanceof String` instead. */ -export function isString(value: unknown): boolean { - return typeof value === "string" || value instanceof String; -} - -/** @deprecated - use `typeof value === "symbol"` instead. */ -export function isSymbol(value: unknown): boolean { - return typeof value === "symbol"; -} - -/** @deprecated - use `value === undefined` instead. */ -export function isUndefined(value: unknown): boolean { - return value === undefined; -} - -/** @deprecated - use `value !== null && typeof value === "object"` instead. */ -export function isObject(value: unknown): boolean { - return value !== null && typeof value === "object"; -} - -/** @deprecated - use `e instanceof Error` instead. */ -export function isError(e: unknown): boolean { - return e instanceof Error; -} - -/** @deprecated - use `typeof value === "function"` instead. */ -export function isFunction(value: unknown): boolean { - return typeof value === "function"; -} - -/** @deprecated - use `value instanceof RegExp` instead. */ -export function isRegExp(value: unknown): boolean { - return value instanceof RegExp; -} - -/** @deprecated - use `value === null || (typeof value !== "object" && typeof value !== "function")` instead. */ -export function isPrimitive(value: unknown): boolean { - return ( - value === null || (typeof value !== "object" && typeof value !== "function") - ); -} - -/** - * Returns a system error name from an error code number. - * @param code error code number - */ -export function getSystemErrorName(code: number): string | undefined { - if (typeof code !== "number") { - throw new ERR_INVALID_ARG_TYPE("err", "number", code); - } - if (code >= 0 || !NumberIsSafeInteger(code)) { - throw new ERR_OUT_OF_RANGE("err", "a negative integer", code); - } - return errorMap.get(code)?.[0]; -} - -/** - * https://nodejs.org/api/util.html#util_util_deprecate_fn_msg_code - * @param _code This implementation of deprecate won't apply the deprecation code - */ -export function deprecate, B>( - this: unknown, - callback: (...args: A) => B, - msg: string, - _code?: string, -) { - return function (this: unknown, ...args: A) { - console.warn(msg); - return callback.apply(this, args); - }; -} - -import { _TextDecoder, _TextEncoder } from "./_utils.ts"; - -/** The global TextDecoder */ -export type TextDecoder = import("./_utils.ts")._TextDecoder; -export const TextDecoder = _TextDecoder; - -/** The global TextEncoder */ -export type TextEncoder = import("./_utils.ts")._TextEncoder; -export const TextEncoder = _TextEncoder; diff --git a/node/buffer.ts b/node/buffer.ts index 664c1ed25aef..32ff824d4e2e 100644 --- a/node/buffer.ts +++ b/node/buffer.ts @@ -1,4 +1,601 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -export * from "./_buffer.ts"; -import * as m from "./_buffer.ts"; -export default m; +import * as hex from "../encoding/hex.ts"; +import * as base64 from "../encoding/base64.ts"; +import { Encodings, normalizeEncoding, notImplemented } from "./_utils.ts"; + +const notImplementedEncodings = [ + "ascii", + "binary", + "latin1", + "ucs2", + "utf16le", +]; + +function checkEncoding(encoding = "utf8", strict = true): Encodings { + if (typeof encoding !== "string" || (strict && encoding === "")) { + if (!strict) return "utf8"; + throw new TypeError(`Unkown encoding: ${encoding}`); + } + + const normalized = normalizeEncoding(encoding); + + if (normalized === undefined) { + throw new TypeError(`Unkown encoding: ${encoding}`); + } + + if (notImplementedEncodings.includes(encoding)) { + notImplemented(`"${encoding}" encoding`); + } + + return normalized; +} + +interface EncodingOp { + byteLength(string: string): number; +} + +// https://github.com/nodejs/node/blob/56dbe466fdbc598baea3bfce289bf52b97b8b8f7/lib/buffer.js#L598 +const encodingOps: { [key: string]: EncodingOp } = { + utf8: { + byteLength: (string: string): number => + new TextEncoder().encode(string).byteLength, + }, + ucs2: { + byteLength: (string: string): number => string.length * 2, + }, + utf16le: { + byteLength: (string: string): number => string.length * 2, + }, + latin1: { + byteLength: (string: string): number => string.length, + }, + ascii: { + byteLength: (string: string): number => string.length, + }, + base64: { + byteLength: (string: string): number => + base64ByteLength(string, string.length), + }, + hex: { + byteLength: (string: string): number => string.length >>> 1, + }, +}; + +function base64ByteLength(str: string, bytes: number): number { + // Handle padding + if (str.charCodeAt(bytes - 1) === 0x3d) bytes--; + if (bytes > 1 && str.charCodeAt(bytes - 1) === 0x3d) bytes--; + + // Base64 ratio: 3/4 + return (bytes * 3) >>> 2; +} + +/** + * See also https://nodejs.org/api/buffer.html + */ +export class Buffer extends Uint8Array { + /** + * Allocates a new Buffer of size bytes. + */ + static alloc( + size: number, + fill?: number | string | Uint8Array | Buffer, + encoding = "utf8", + ): Buffer { + if (typeof size !== "number") { + throw new TypeError( + `The "size" argument must be of type number. Received type ${typeof size}`, + ); + } + + const buf = new Buffer(size); + if (size === 0) return buf; + + let bufFill; + if (typeof fill === "string") { + const clearEncoding = checkEncoding(encoding); + if ( + typeof fill === "string" && + fill.length === 1 && + clearEncoding === "utf8" + ) { + buf.fill(fill.charCodeAt(0)); + } else bufFill = Buffer.from(fill, clearEncoding); + } else if (typeof fill === "number") { + buf.fill(fill); + } else if (fill instanceof Uint8Array) { + if (fill.length === 0) { + throw new TypeError( + `The argument "value" is invalid. Received ${fill.constructor.name} []`, + ); + } + + bufFill = fill; + } + + if (bufFill) { + if (bufFill.length > buf.length) { + bufFill = bufFill.subarray(0, buf.length); + } + + let offset = 0; + while (offset < size) { + buf.set(bufFill, offset); + offset += bufFill.length; + if (offset + bufFill.length >= size) break; + } + if (offset !== size) { + buf.set(bufFill.subarray(0, size - offset), offset); + } + } + + return buf; + } + + static allocUnsafe(size: number): Buffer { + return new Buffer(size); + } + + /** + * Returns the byte length of a string when encoded. This is not the same as + * String.prototype.length, which does not account for the encoding that is + * used to convert the string into bytes. + */ + static byteLength( + string: string | Buffer | ArrayBufferView | ArrayBuffer | SharedArrayBuffer, + encoding = "utf8", + ): number { + if (typeof string != "string") return string.byteLength; + + encoding = normalizeEncoding(encoding) || "utf8"; + return encodingOps[encoding].byteLength(string); + } + + /** + * Returns a new Buffer which is the result of concatenating all the Buffer + * instances in the list together. + */ + static concat(list: Buffer[] | Uint8Array[], totalLength?: number): Buffer { + if (totalLength == undefined) { + totalLength = 0; + for (const buf of list) { + totalLength += buf.length; + } + } + + const buffer = Buffer.allocUnsafe(totalLength); + let pos = 0; + for (const item of list) { + let buf: Buffer; + if (!(item instanceof Buffer)) { + buf = Buffer.from(item); + } else { + buf = item; + } + buf.copy(buffer, pos); + pos += buf.length; + } + + return buffer; + } + + /** + * Allocates a new Buffer using an array of bytes in the range 0 – 255. Array + * entries outside that range will be truncated to fit into it. + */ + static from(array: number[]): Buffer; + /** + * This creates a view of the ArrayBuffer without copying the underlying + * memory. For example, when passed a reference to the .buffer property of a + * TypedArray instance, the newly created Buffer will share the same allocated + * memory as the TypedArray. + */ + static from( + arrayBuffer: ArrayBuffer | SharedArrayBuffer, + byteOffset?: number, + length?: number, + ): Buffer; + /** + * Copies the passed buffer data onto a new Buffer instance. + */ + static from(buffer: Buffer | Uint8Array): Buffer; + /** + * Creates a new Buffer containing string. + */ + static from(string: string, encoding?: string): Buffer; + static from( + // deno-lint-ignore no-explicit-any + value: any, + offsetOrEncoding?: number | string, + length?: number, + ): Buffer { + const offset = typeof offsetOrEncoding === "string" + ? undefined + : offsetOrEncoding; + let encoding = typeof offsetOrEncoding === "string" + ? offsetOrEncoding + : undefined; + + if (typeof value == "string") { + encoding = checkEncoding(encoding, false); + if (encoding === "hex") return new Buffer(hex.decodeString(value).buffer); + if (encoding === "base64") return new Buffer(base64.decode(value).buffer); + return new Buffer(new TextEncoder().encode(value).buffer); + } + + // workaround for https://github.com/microsoft/TypeScript/issues/38446 + return new Buffer(value, offset!, length); + } + + /** + * Returns true if obj is a Buffer, false otherwise. + */ + static isBuffer(obj: unknown): obj is Buffer { + return obj instanceof Buffer; + } + + // deno-lint-ignore no-explicit-any + static isEncoding(encoding: any): boolean { + return ( + typeof encoding === "string" && + encoding.length !== 0 && + normalizeEncoding(encoding) !== undefined + ); + } + + /** + * Copies data from a region of buf to a region in target, even if the target + * memory region overlaps with buf. + */ + copy( + targetBuffer: Buffer | Uint8Array, + targetStart = 0, + sourceStart = 0, + sourceEnd = this.length, + ): number { + const sourceBuffer = this + .subarray(sourceStart, sourceEnd) + .subarray(0, Math.max(0, targetBuffer.length - targetStart)); + + if (sourceBuffer.length === 0) return 0; + + targetBuffer.set(sourceBuffer, targetStart); + return sourceBuffer.length; + } + + /* + * Returns true if both buf and otherBuffer have exactly the same bytes, false otherwise. + */ + equals(otherBuffer: Uint8Array | Buffer): boolean { + if (!(otherBuffer instanceof Uint8Array)) { + throw new TypeError( + `The "otherBuffer" argument must be an instance of Buffer or Uint8Array. Received type ${typeof otherBuffer}`, + ); + } + + if (this === otherBuffer) return true; + if (this.byteLength !== otherBuffer.byteLength) return false; + + for (let i = 0; i < this.length; i++) { + if (this[i] !== otherBuffer[i]) return false; + } + + return true; + } + + readBigInt64BE(offset = 0): bigint { + return new DataView( + this.buffer, + this.byteOffset, + this.byteLength, + ).getBigInt64(offset); + } + readBigInt64LE(offset = 0): bigint { + return new DataView( + this.buffer, + this.byteOffset, + this.byteLength, + ).getBigInt64(offset, true); + } + + readBigUInt64BE(offset = 0): bigint { + return new DataView( + this.buffer, + this.byteOffset, + this.byteLength, + ).getBigUint64(offset); + } + readBigUInt64LE(offset = 0): bigint { + return new DataView( + this.buffer, + this.byteOffset, + this.byteLength, + ).getBigUint64(offset, true); + } + + readDoubleBE(offset = 0): number { + return new DataView( + this.buffer, + this.byteOffset, + this.byteLength, + ).getFloat64(offset); + } + readDoubleLE(offset = 0): number { + return new DataView( + this.buffer, + this.byteOffset, + this.byteLength, + ).getFloat64(offset, true); + } + + readFloatBE(offset = 0): number { + return new DataView( + this.buffer, + this.byteOffset, + this.byteLength, + ).getFloat32(offset); + } + readFloatLE(offset = 0): number { + return new DataView( + this.buffer, + this.byteOffset, + this.byteLength, + ).getFloat32(offset, true); + } + + readInt8(offset = 0): number { + return new DataView(this.buffer, this.byteOffset, this.byteLength).getInt8( + offset, + ); + } + + readInt16BE(offset = 0): number { + return new DataView(this.buffer, this.byteOffset, this.byteLength).getInt16( + offset, + ); + } + readInt16LE(offset = 0): number { + return new DataView(this.buffer, this.byteOffset, this.byteLength).getInt16( + offset, + true, + ); + } + + readInt32BE(offset = 0): number { + return new DataView(this.buffer, this.byteOffset, this.byteLength).getInt32( + offset, + ); + } + readInt32LE(offset = 0): number { + return new DataView(this.buffer, this.byteOffset, this.byteLength).getInt32( + offset, + true, + ); + } + + readUInt8(offset = 0): number { + return new DataView(this.buffer, this.byteOffset, this.byteLength).getUint8( + offset, + ); + } + + readUInt16BE(offset = 0): number { + return new DataView( + this.buffer, + this.byteOffset, + this.byteLength, + ).getUint16(offset); + } + readUInt16LE(offset = 0): number { + return new DataView( + this.buffer, + this.byteOffset, + this.byteLength, + ).getUint16(offset, true); + } + + readUInt32BE(offset = 0): number { + return new DataView( + this.buffer, + this.byteOffset, + this.byteLength, + ).getUint32(offset); + } + readUInt32LE(offset = 0): number { + return new DataView( + this.buffer, + this.byteOffset, + this.byteLength, + ).getUint32(offset, true); + } + + /** + * Returns a new Buffer that references the same memory as the original, but + * offset and cropped by the start and end indices. + */ + slice(begin = 0, end = this.length): Buffer { + // workaround for https://github.com/microsoft/TypeScript/issues/38665 + return this.subarray(begin, end) as Buffer; + } + + /** + * Returns a JSON representation of buf. JSON.stringify() implicitly calls + * this function when stringifying a Buffer instance. + */ + toJSON(): Record { + return { type: "Buffer", data: Array.from(this) }; + } + + /** + * Decodes buf to a string according to the specified character encoding in + * encoding. start and end may be passed to decode only a subset of buf. + */ + toString(encoding = "utf8", start = 0, end = this.length): string { + encoding = checkEncoding(encoding); + + const b = this.subarray(start, end); + if (encoding === "hex") return hex.encodeToString(b); + if (encoding === "base64") return base64.encode(b.buffer); + + return new TextDecoder(encoding).decode(b); + } + + /** + * Writes string to buf at offset according to the character encoding in + * encoding. The length parameter is the number of bytes to write. If buf did + * not contain enough space to fit the entire string, only part of string will + * be written. However, partially encoded characters will not be written. + */ + write(string: string, offset = 0, length = this.length): number { + return new TextEncoder().encodeInto( + string, + this.subarray(offset, offset + length), + ).written; + } + + writeBigInt64BE(value: bigint, offset = 0): number { + new DataView(this.buffer, this.byteOffset, this.byteLength).setBigInt64( + offset, + value, + ); + return offset + 4; + } + writeBigInt64LE(value: bigint, offset = 0): number { + new DataView(this.buffer, this.byteOffset, this.byteLength).setBigInt64( + offset, + value, + true, + ); + return offset + 4; + } + + writeBigUInt64BE(value: bigint, offset = 0): number { + new DataView(this.buffer, this.byteOffset, this.byteLength).setBigUint64( + offset, + value, + ); + return offset + 4; + } + writeBigUInt64LE(value: bigint, offset = 0): number { + new DataView(this.buffer, this.byteOffset, this.byteLength).setBigUint64( + offset, + value, + true, + ); + return offset + 4; + } + + writeDoubleBE(value: number, offset = 0): number { + new DataView(this.buffer, this.byteOffset, this.byteLength).setFloat64( + offset, + value, + ); + return offset + 8; + } + writeDoubleLE(value: number, offset = 0): number { + new DataView(this.buffer, this.byteOffset, this.byteLength).setFloat64( + offset, + value, + true, + ); + return offset + 8; + } + + writeFloatBE(value: number, offset = 0): number { + new DataView(this.buffer, this.byteOffset, this.byteLength).setFloat32( + offset, + value, + ); + return offset + 4; + } + writeFloatLE(value: number, offset = 0): number { + new DataView(this.buffer, this.byteOffset, this.byteLength).setFloat32( + offset, + value, + true, + ); + return offset + 4; + } + + writeInt8(value: number, offset = 0): number { + new DataView(this.buffer, this.byteOffset, this.byteLength).setInt8( + offset, + value, + ); + return offset + 1; + } + + writeInt16BE(value: number, offset = 0): number { + new DataView(this.buffer, this.byteOffset, this.byteLength).setInt16( + offset, + value, + ); + return offset + 2; + } + writeInt16LE(value: number, offset = 0): number { + new DataView(this.buffer, this.byteOffset, this.byteLength).setInt16( + offset, + value, + true, + ); + return offset + 2; + } + + writeInt32BE(value: number, offset = 0): number { + new DataView(this.buffer, this.byteOffset, this.byteLength).setUint32( + offset, + value, + ); + return offset + 4; + } + writeInt32LE(value: number, offset = 0): number { + new DataView(this.buffer, this.byteOffset, this.byteLength).setInt32( + offset, + value, + true, + ); + return offset + 4; + } + + writeUInt8(value: number, offset = 0): number { + new DataView(this.buffer, this.byteOffset, this.byteLength).setUint8( + offset, + value, + ); + return offset + 1; + } + + writeUInt16BE(value: number, offset = 0): number { + new DataView(this.buffer, this.byteOffset, this.byteLength).setUint16( + offset, + value, + ); + return offset + 2; + } + writeUInt16LE(value: number, offset = 0): number { + new DataView(this.buffer, this.byteOffset, this.byteLength).setUint16( + offset, + value, + true, + ); + return offset + 2; + } + + writeUInt32BE(value: number, offset = 0): number { + new DataView(this.buffer, this.byteOffset, this.byteLength).setUint32( + offset, + value, + ); + return offset + 4; + } + writeUInt32LE(value: number, offset = 0): number { + new DataView(this.buffer, this.byteOffset, this.byteLength).setUint32( + offset, + value, + true, + ); + return offset + 4; + } +} + +export default { Buffer }; diff --git a/node/crypto.ts b/node/crypto.ts index d27cab67de4c..0db8e2b13ec2 100644 --- a/node/crypto.ts +++ b/node/crypto.ts @@ -1,4 +1,6 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -export * from "./_crypto.ts"; -import * as m from "./_crypto.ts"; -export default m; +import { default as randomBytes } from "./_crypto/randomBytes.ts"; +import { pbkdf2, pbkdf2Sync } from "./_crypto/pbkdf2.ts"; + +export default { randomBytes, pbkdf2, pbkdf2Sync }; +export { pbkdf2, pbkdf2Sync, randomBytes }; diff --git a/node/fs.ts b/node/fs.ts index 04adbf417e0d..38682d3c4c47 100644 --- a/node/fs.ts +++ b/node/fs.ts @@ -1,4 +1,112 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -export * from "./_fs.ts"; -import * as m from "./_fs.ts"; -export default m; +import { access, accessSync } from "./_fs/_fs_access.ts"; +import { appendFile, appendFileSync } from "./_fs/_fs_appendFile.ts"; +import { chmod, chmodSync } from "./_fs/_fs_chmod.ts"; +import { chown, chownSync } from "./_fs/_fs_chown.ts"; +import { close, closeSync } from "./_fs/_fs_close.ts"; +import * as constants from "./_fs/_fs_constants.ts"; +import { readFile, readFileSync } from "./_fs/_fs_readFile.ts"; +import { readlink, readlinkSync } from "./_fs/_fs_readlink.ts"; +import { exists, existsSync } from "./_fs/_fs_exists.ts"; +import { mkdir, mkdirSync } from "./_fs/_fs_mkdir.ts"; +import { copyFile, copyFileSync } from "./_fs/_fs_copy.ts"; +import { writeFile, writeFileSync } from "./_fs/_fs_writeFile.ts"; +import { readdir, readdirSync } from "./_fs/_fs_readdir.ts"; +import { realpath, realpathSync } from "./_fs/_fs_realpath.ts"; +import { rename, renameSync } from "./_fs/_fs_rename.ts"; +import { rmdir, rmdirSync } from "./_fs/_fs_rmdir.ts"; +import { unlink, unlinkSync } from "./_fs/_fs_unlink.ts"; +import { watch } from "./_fs/_fs_watch.ts"; +import { open, openSync } from "./_fs/_fs_open.ts"; +import { stat, statSync } from "./_fs/_fs_stat.ts"; +import { lstat, lstatSync } from "./_fs/_fs_lstat.ts"; + +import * as promises from "./_fs/promises/mod.ts"; + +export default { + access, + accessSync, + appendFile, + appendFileSync, + chmod, + chmodSync, + chown, + chownSync, + close, + closeSync, + constants, + copyFile, + copyFileSync, + exists, + existsSync, + lstat, + lstatSync, + mkdir, + mkdirSync, + open, + openSync, + promises, + readdir, + readdirSync, + readFile, + readFileSync, + readlink, + readlinkSync, + realpath, + realpathSync, + rename, + renameSync, + rmdir, + rmdirSync, + stat, + statSync, + unlink, + unlinkSync, + watch, + writeFile, + writeFileSync, +}; + +export { + access, + accessSync, + appendFile, + appendFileSync, + chmod, + chmodSync, + chown, + chownSync, + close, + closeSync, + constants, + copyFile, + copyFileSync, + exists, + existsSync, + lstat, + lstatSync, + mkdir, + mkdirSync, + open, + openSync, + promises, + readdir, + readdirSync, + readFile, + readFileSync, + readlink, + readlinkSync, + realpath, + realpathSync, + rename, + renameSync, + rmdir, + rmdirSync, + stat, + statSync, + unlink, + unlinkSync, + watch, + writeFile, + writeFileSync, +}; diff --git a/node/global.ts b/node/global.ts index 0ef688e63193..d72760089247 100644 --- a/node/global.ts +++ b/node/global.ts @@ -1,7 +1,7 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. /// import { process as processModule } from "./process.ts"; -import { Buffer as bufferModule } from "./_buffer.ts"; +import { Buffer as bufferModule } from "./buffer.ts"; Object.defineProperty(globalThis, "global", { value: globalThis, diff --git a/node/os.ts b/node/os.ts index 1599fbeb37b4..bec3de5ee16d 100644 --- a/node/os.ts +++ b/node/os.ts @@ -1,4 +1,246 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -export * from "./_os.ts"; -import * as m from "./_os.ts"; -export default m; +// 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. +import { notImplemented } from "./_utils.ts"; +import { validateIntegerRange } from "./_utils.ts"; +import { EOL as fsEOL } from "../fs/eol.ts"; +import process from "./process.ts"; + +const SEE_GITHUB_ISSUE = "See https://github.com/denoland/deno/issues/3802"; + +interface CPUTimes { + /** The number of milliseconds the CPU has spent in user mode */ + user: number; + + /** The number of milliseconds the CPU has spent in nice mode */ + nice: number; + + /** The number of milliseconds the CPU has spent in sys mode */ + sys: number; + + /** The number of milliseconds the CPU has spent in idle mode */ + idle: number; + + /** The number of milliseconds the CPU has spent in irq mode */ + irq: number; +} + +interface CPUCoreInfo { + model: string; + + /** in MHz */ + speed: number; + + times: CPUTimes; +} + +interface NetworkAddress { + /** The assigned IPv4 or IPv6 address */ + address: string; + + /** The IPv4 or IPv6 network mask */ + netmask: string; + + family: "IPv4" | "IPv6"; + + /** The MAC address of the network interface */ + mac: string; + + /** true if the network interface is a loopback or similar interface that is not remotely accessible; otherwise false */ + internal: boolean; + + /** The numeric IPv6 scope ID (only specified when family is IPv6) */ + scopeid?: number; + + /** The assigned IPv4 or IPv6 address with the routing prefix in CIDR notation. If the netmask is invalid, this property is set to null. */ + cidr: string; +} + +interface NetworkInterfaces { + [key: string]: NetworkAddress[]; +} + +export interface UserInfoOptions { + encoding: string; +} + +interface UserInfo { + username: string; + uid: number; + gid: number; + shell: string; + homedir: string; +} + +arch[Symbol.toPrimitive] = (): string => arch(); +endianness[Symbol.toPrimitive] = (): string => endianness(); +freemem[Symbol.toPrimitive] = (): number => freemem(); +homedir[Symbol.toPrimitive] = (): string | null => homedir(); +hostname[Symbol.toPrimitive] = (): string | null => hostname(); +platform[Symbol.toPrimitive] = (): string => platform(); +release[Symbol.toPrimitive] = (): string => release(); +totalmem[Symbol.toPrimitive] = (): number => totalmem(); +type[Symbol.toPrimitive] = (): string => type(); +uptime[Symbol.toPrimitive] = (): number => uptime(); + +/** Returns the operating system CPU architecture for which the Deno binary was compiled */ +export function arch(): string { + return Deno.build.arch; +} + +/** Not yet implemented */ +export function cpus(): CPUCoreInfo[] { + notImplemented(SEE_GITHUB_ISSUE); +} + +/** + * Returns a string identifying the endianness of the CPU for which the Deno + * binary was compiled. Possible values are 'BE' for big endian and 'LE' for + * little endian. + **/ +export function endianness(): "BE" | "LE" { + // Source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView#Endianness + const buffer = new ArrayBuffer(2); + new DataView(buffer).setInt16(0, 256, true /* littleEndian */); + // Int16Array uses the platform's endianness. + return new Int16Array(buffer)[0] === 256 ? "LE" : "BE"; +} + +/** Return free memory amount */ +export function freemem(): number { + return Deno.systemMemoryInfo().free; +} + +/** Not yet implemented */ +export function getPriority(pid = 0): number { + validateIntegerRange(pid, "pid"); + notImplemented(SEE_GITHUB_ISSUE); +} + +/** Returns the string path of the current user's home directory. */ +export function homedir(): string | null { + notImplemented(SEE_GITHUB_ISSUE); +} + +/** Returns the host name of the operating system as a string. */ +export function hostname(): string { + notImplemented(SEE_GITHUB_ISSUE); +} + +/** Returns an array containing the 1, 5, and 15 minute load averages */ +export function loadavg(): number[] { + if (Deno.build.os === "windows") { + return [0, 0, 0]; + } + return Deno.loadavg(); +} + +/** Not yet implemented */ +export function networkInterfaces(): NetworkInterfaces { + notImplemented(SEE_GITHUB_ISSUE); +} +/** Returns the a string identifying the operating system platform. The value is set at compile time. Possible values are 'darwin', 'linux', and 'win32'. */ +export function platform(): string { + return process.platform; +} + +/** Returns the operating system as a string */ +export function release(): string { + return Deno.osRelease(); +} + +/** Not yet implemented */ +export function setPriority(pid: number, priority?: number): void { + /* The node API has the 'pid' as the first parameter and as optional. + This makes for a problematic implementation in Typescript. */ + if (priority === undefined) { + priority = pid; + pid = 0; + } + validateIntegerRange(pid, "pid"); + validateIntegerRange(priority, "priority", -20, 19); + + notImplemented(SEE_GITHUB_ISSUE); +} + +/** Returns the operating system's default directory for temporary files as a string. */ +export function tmpdir(): string | null { + notImplemented(SEE_GITHUB_ISSUE); +} + +/** Return total physical memory amount */ +export function totalmem(): number { + return Deno.systemMemoryInfo().total; +} + +/** Not yet implemented */ +export function type(): string { + notImplemented(SEE_GITHUB_ISSUE); +} + +/** Not yet implemented */ +export function uptime(): number { + notImplemented(SEE_GITHUB_ISSUE); +} + +/** Not yet implemented */ +export function userInfo( + options: UserInfoOptions = { encoding: "utf-8" }, +): UserInfo { + notImplemented(SEE_GITHUB_ISSUE); +} + +export const constants = { + // UV_UDP_REUSEADDR: 4, //see https://nodejs.org/docs/latest-v12.x/api/os.html#os_libuv_constants + dlopen: { + // see https://nodejs.org/docs/latest-v12.x/api/os.html#os_dlopen_constants + }, + errno: { + // see https://nodejs.org/docs/latest-v12.x/api/os.html#os_error_constants + }, + signals: Deno.Signal, + priority: { + // see https://nodejs.org/docs/latest-v12.x/api/os.html#os_priority_constants + }, +}; + +export const EOL = Deno.build.os == "windows" ? fsEOL.CRLF : fsEOL.LF; + +export default { + arch, + cpus, + endianness, + freemem, + getPriority, + homedir, + hostname, + loadavg, + networkInterfaces, + platform, + release, + setPriority, + tmpdir, + totalmem, + type, + uptime, + userInfo, + constants, + EOL, +}; diff --git a/node/querystring.ts b/node/querystring.ts index ff68b2ae87c1..73e46c4241f6 100644 --- a/node/querystring.ts +++ b/node/querystring.ts @@ -1,4 +1,167 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -export * from "./_querystring.ts"; -import * as m from "./_querystring.ts"; -export default m; + +interface ParseOptions { + /** The function to use when decoding percent-encoded characters in the query string. */ + decodeURIComponent?: (string: string) => string; + /** Specifies the maximum number of keys to parse. */ + maxKeys?: number; +} + +export const hexTable = new Array(256); +for (let i = 0; i < 256; ++i) { + hexTable[i] = "%" + ((i < 16 ? "0" : "") + i.toString(16)).toUpperCase(); +} + +/** + * Parses a URL query string into a collection of key and value pairs. + * @param str The URL query string to parse + * @param sep The substring used to delimit key and value pairs in the query string. Default: '&'. + * @param eq The substring used to delimit keys and values in the query string. Default: '='. + * @param options The parse options + */ +export function parse( + str: string, + sep = "&", + eq = "=", + { decodeURIComponent = unescape, maxKeys = 1000 }: ParseOptions = {}, +): { [key: string]: string[] | string } { + const entries = str + .split(sep) + .map((entry) => entry.split(eq).map(decodeURIComponent)); + const final: { [key: string]: string[] | string } = {}; + + let i = 0; + while (true) { + if ((Object.keys(final).length === maxKeys && !!maxKeys) || !entries[i]) { + break; + } + + const [key, val] = entries[i]; + if (final[key]) { + if (Array.isArray(final[key])) { + (final[key] as string[]).push(val); + } else { + final[key] = [final[key] as string, val]; + } + } else { + final[key] = val; + } + + i++; + } + + return final; +} + +interface StringifyOptions { + /** The function to use when converting URL-unsafe characters to percent-encoding in the query string. */ + encodeURIComponent?: (string: string) => string; +} + +export function encodeStr( + str: string, + noEscapeTable: number[], + hexTable: string[], +): string { + const len = str.length; + if (len === 0) return ""; + + let out = ""; + let lastPos = 0; + + for (let i = 0; i < len; i++) { + let c = str.charCodeAt(i); + // ASCII + if (c < 0x80) { + if (noEscapeTable[c] === 1) continue; + if (lastPos < i) out += str.slice(lastPos, i); + lastPos = i + 1; + out += hexTable[c]; + continue; + } + + if (lastPos < i) out += str.slice(lastPos, i); + + // Multi-byte characters ... + if (c < 0x800) { + lastPos = i + 1; + out += hexTable[0xc0 | (c >> 6)] + hexTable[0x80 | (c & 0x3f)]; + continue; + } + if (c < 0xd800 || c >= 0xe000) { + lastPos = i + 1; + out += hexTable[0xe0 | (c >> 12)] + + hexTable[0x80 | ((c >> 6) & 0x3f)] + + hexTable[0x80 | (c & 0x3f)]; + continue; + } + // Surrogate pair + ++i; + + // This branch should never happen because all URLSearchParams entries + // should already be converted to USVString. But, included for + // completion's sake anyway. + if (i >= len) throw new Deno.errors.InvalidData("invalid URI"); + + const c2 = str.charCodeAt(i) & 0x3ff; + + lastPos = i + 1; + c = 0x10000 + (((c & 0x3ff) << 10) | c2); + out += hexTable[0xf0 | (c >> 18)] + + hexTable[0x80 | ((c >> 12) & 0x3f)] + + hexTable[0x80 | ((c >> 6) & 0x3f)] + + hexTable[0x80 | (c & 0x3f)]; + } + if (lastPos === 0) return str; + if (lastPos < len) return out + str.slice(lastPos); + return out; +} + +/** + * Produces a URL query string from a given obj by iterating through the object's "own properties". + * @param obj The object to serialize into a URL query string. + * @param sep The substring used to delimit key and value pairs in the query string. Default: '&'. + * @param eq The substring used to delimit keys and values in the query string. Default: '='. + * @param options The stringify options + */ +export function stringify( + // deno-lint-ignore no-explicit-any + obj: Record, + sep = "&", + eq = "=", + { encodeURIComponent = escape }: StringifyOptions = {}, +): string { + const final = []; + + for (const entry of Object.entries(obj)) { + if (Array.isArray(entry[1])) { + for (const val of entry[1]) { + final.push(encodeURIComponent(entry[0]) + eq + encodeURIComponent(val)); + } + } else if (typeof entry[1] !== "object" && entry[1] !== undefined) { + final.push(entry.map(encodeURIComponent).join(eq)); + } else { + final.push(encodeURIComponent(entry[0]) + eq); + } + } + + return final.join(sep); +} + +/** Alias of querystring.parse() */ +export const decode = parse; +/** Alias of querystring.stringify() */ +export const encode = stringify; +export const unescape = decodeURIComponent; +export const escape = encodeURIComponent; + +export default { + parse, + encodeStr, + stringify, + hexTable, + decode, + encode, + unescape, + escape, +}; diff --git a/node/string_decoder.ts b/node/string_decoder.ts index cc6fb5186af5..d5aba801814c 100644 --- a/node/string_decoder.ts +++ b/node/string_decoder.ts @@ -1,4 +1,313 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -export * from "./_string_decoder.ts"; -import * as m from "./_string_decoder.ts"; -export default m; +// 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. + +import { Buffer } from "./buffer.ts"; +import { normalizeEncoding as castEncoding, notImplemented } from "./_utils.ts"; + +enum NotImplemented { + "ascii", + "latin1", + "utf16le", +} + +function normalizeEncoding(enc?: string): string { + const encoding = castEncoding(enc ?? null); + if (encoding && encoding in NotImplemented) notImplemented(encoding); + if (!encoding && typeof enc === "string" && enc.toLowerCase() !== "raw") { + throw new Error(`Unknown encoding: ${enc}`); + } + return String(encoding); +} +/* + * Checks the type of a UTF-8 byte, whether it's ASCII, a leading byte, or a + * continuation byte. If an invalid byte is detected, -2 is returned. + * */ +function utf8CheckByte(byte: number): number { + if (byte <= 0x7f) return 0; + else if (byte >> 5 === 0x06) return 2; + else if (byte >> 4 === 0x0e) return 3; + else if (byte >> 3 === 0x1e) return 4; + return byte >> 6 === 0x02 ? -1 : -2; +} + +/* + * Checks at most 3 bytes at the end of a Buffer in order to detect an + * incomplete multi-byte UTF-8 character. The total number of bytes (2, 3, or 4) + * needed to complete the UTF-8 character (if applicable) are returned. + * */ +function utf8CheckIncomplete( + self: StringDecoderBase, + buf: Buffer, + i: number, +): number { + let j = buf.length - 1; + if (j < i) return 0; + let nb = utf8CheckByte(buf[j]); + if (nb >= 0) { + if (nb > 0) self.lastNeed = nb - 1; + return nb; + } + if (--j < i || nb === -2) return 0; + nb = utf8CheckByte(buf[j]); + if (nb >= 0) { + if (nb > 0) self.lastNeed = nb - 2; + return nb; + } + if (--j < i || nb === -2) return 0; + nb = utf8CheckByte(buf[j]); + if (nb >= 0) { + if (nb > 0) { + if (nb === 2) nb = 0; + else self.lastNeed = nb - 3; + } + return nb; + } + return 0; +} + +/* + * Validates as many continuation bytes for a multi-byte UTF-8 character as + * needed or are available. If we see a non-continuation byte where we expect + * one, we "replace" the validated continuation bytes we've seen so far with + * a single UTF-8 replacement character ('\ufffd'), to match v8's UTF-8 decoding + * behavior. The continuation byte check is included three times in the case + * where all of the continuation bytes for a character exist in the same buffer. + * It is also done this way as a slight performance increase instead of using a + * loop. + * */ +function utf8CheckExtraBytes( + self: StringDecoderBase, + buf: Buffer, +): string | undefined { + if ((buf[0] & 0xc0) !== 0x80) { + self.lastNeed = 0; + return "\ufffd"; + } + if (self.lastNeed > 1 && buf.length > 1) { + if ((buf[1] & 0xc0) !== 0x80) { + self.lastNeed = 1; + return "\ufffd"; + } + if (self.lastNeed > 2 && buf.length > 2) { + if ((buf[2] & 0xc0) !== 0x80) { + self.lastNeed = 2; + return "\ufffd"; + } + } + } +} + +/* + * Attempts to complete a multi-byte UTF-8 character using bytes from a Buffer. + * */ +function utf8FillLastComplete( + this: StringDecoderBase, + buf: Buffer, +): string | undefined { + const p = this.lastTotal - this.lastNeed; + const r = utf8CheckExtraBytes(this, buf); + if (r !== undefined) return r; + if (this.lastNeed <= buf.length) { + buf.copy(this.lastChar, p, 0, this.lastNeed); + return this.lastChar.toString(this.encoding, 0, this.lastTotal); + } + buf.copy(this.lastChar, p, 0, buf.length); + this.lastNeed -= buf.length; +} + +/* + * Attempts to complete a partial non-UTF-8 character using bytes from a Buffer + * */ +function utf8FillLastIncomplete( + this: StringDecoderBase, + buf: Buffer, +): string | undefined { + if (this.lastNeed <= buf.length) { + buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, this.lastNeed); + return this.lastChar.toString(this.encoding, 0, this.lastTotal); + } + buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, buf.length); + this.lastNeed -= buf.length; +} + +/* + * Returns all complete UTF-8 characters in a Buffer. If the Buffer ended on a + * partial character, the character's bytes are buffered until the required + * number of bytes are available. + * */ +function utf8Text(this: StringDecoderBase, buf: Buffer, i: number): string { + const total = utf8CheckIncomplete(this, buf, i); + if (!this.lastNeed) return buf.toString("utf8", i); + this.lastTotal = total; + const end = buf.length - (total - this.lastNeed); + buf.copy(this.lastChar, 0, end); + return buf.toString("utf8", i, end); +} + +/* + * For UTF-8, a replacement character is added when ending on a partial + * character. + * */ +function utf8End(this: Utf8Decoder, buf?: Buffer): string { + const r = buf && buf.length ? this.write(buf) : ""; + if (this.lastNeed) return r + "\ufffd"; + return r; +} + +function utf8Write( + this: Utf8Decoder | Base64Decoder, + buf: Buffer | string, +): string { + if (typeof buf === "string") { + return buf; + } + if (buf.length === 0) return ""; + let r; + let i; + if (this.lastNeed) { + r = this.fillLast(buf); + if (r === undefined) return ""; + i = this.lastNeed; + this.lastNeed = 0; + } else { + i = 0; + } + if (i < buf.length) return r ? r + this.text(buf, i) : this.text(buf, i); + return r || ""; +} + +function base64Text(this: StringDecoderBase, buf: Buffer, i: number): string { + const n = (buf.length - i) % 3; + if (n === 0) return buf.toString("base64", i); + this.lastNeed = 3 - n; + this.lastTotal = 3; + if (n === 1) { + this.lastChar[0] = buf[buf.length - 1]; + } else { + this.lastChar[0] = buf[buf.length - 2]; + this.lastChar[1] = buf[buf.length - 1]; + } + return buf.toString("base64", i, buf.length - n); +} + +function base64End(this: Base64Decoder, buf?: Buffer): string { + const r = buf && buf.length ? this.write(buf) : ""; + if (this.lastNeed) { + return r + this.lastChar.toString("base64", 0, 3 - this.lastNeed); + } + return r; +} + +function simpleWrite( + this: StringDecoderBase, + buf: Buffer | string, +): string { + if (typeof buf === "string") { + return buf; + } + return buf.toString(this.encoding); +} + +function simpleEnd(this: GenericDecoder, buf?: Buffer): string { + return buf && buf.length ? this.write(buf) : ""; +} + +class StringDecoderBase { + public lastChar: Buffer; + public lastNeed = 0; + public lastTotal = 0; + constructor(public encoding: string, nb: number) { + this.lastChar = Buffer.allocUnsafe(nb); + } +} + +class Base64Decoder extends StringDecoderBase { + public end = base64End; + public fillLast = utf8FillLastIncomplete; + public text = base64Text; + public write = utf8Write; + + constructor(encoding?: string) { + super(normalizeEncoding(encoding), 3); + } +} + +class GenericDecoder extends StringDecoderBase { + public end = simpleEnd; + public fillLast = undefined; + public text = utf8Text; + public write = simpleWrite; + + constructor(encoding?: string) { + super(normalizeEncoding(encoding), 4); + } +} + +class Utf8Decoder extends StringDecoderBase { + public end = utf8End; + public fillLast = utf8FillLastComplete; + public text = utf8Text; + public write = utf8Write; + + constructor(encoding?: string) { + super(normalizeEncoding(encoding), 4); + } +} + +/* + * StringDecoder provides an interface for efficiently splitting a series of + * buffers into a series of JS strings without breaking apart multi-byte + * characters. + * */ +export class StringDecoder { + public encoding: string; + public end: (buf?: Buffer) => string; + public fillLast: ((buf: Buffer) => string | undefined) | undefined; + public lastChar: Buffer; + public lastNeed: number; + public lastTotal: number; + public text: (buf: Buffer, n: number) => string; + public write: (buf: Buffer) => string; + + constructor(encoding?: string) { + let decoder; + switch (encoding) { + case "utf8": + decoder = new Utf8Decoder(encoding); + break; + case "base64": + decoder = new Base64Decoder(encoding); + break; + default: + decoder = new GenericDecoder(encoding); + } + this.encoding = decoder.encoding; + this.end = decoder.end; + this.fillLast = decoder.fillLast; + this.lastChar = decoder.lastChar; + this.lastNeed = decoder.lastNeed; + this.lastTotal = decoder.lastTotal; + this.text = decoder.text; + this.write = decoder.write; + } +} + +export default { StringDecoder }; diff --git a/node/timers.ts b/node/timers.ts index 095f6c5dc405..fd040ca5ec28 100644 --- a/node/timers.ts +++ b/node/timers.ts @@ -1,4 +1,23 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -export * from "./_timers.ts"; -import * as m from "./_timers.ts"; -export default m; +// TODO: implement the 'NodeJS.Timeout' and 'NodeJS.Immediate' versions of the timers. +// https://github.com/DefinitelyTyped/DefinitelyTyped/blob/1163ead296d84e7a3c80d71e7c81ecbd1a130e9a/types/node/v12/globals.d.ts#L1120-L1131 +export const setTimeout = window.setTimeout; +export const clearTimeout = window.clearTimeout; +export const setInterval = window.setInterval; +export const clearInterval = window.clearInterval; +export const setImmediate = ( + // deno-lint-ignore no-explicit-any + cb: (...args: any[]) => void, + // deno-lint-ignore no-explicit-any + ...args: any[] +): number => window.setTimeout(cb, 0, ...args); +export const clearImmediate = window.clearTimeout; + +export default { + setTimeout, + clearTimeout, + setInterval, + clearInterval, + setImmediate, + clearImmediate, +}; diff --git a/node/url.ts b/node/url.ts index 2aa2702aadca..a0a3e5f33752 100644 --- a/node/url.ts +++ b/node/url.ts @@ -1,4 +1,145 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -export * from "./_url.ts"; -import * as m from "./_url.ts"; -export default m; +// 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. + +import { + CHAR_BACKWARD_SLASH, + CHAR_FORWARD_SLASH, + CHAR_LOWERCASE_A, + CHAR_LOWERCASE_Z, +} from "../path/_constants.ts"; +import * as path from "./path.ts"; +import { isWindows } from "../_util/os.ts"; + +const forwardSlashRegEx = /\//g; +const percentRegEx = /%/g; +const backslashRegEx = /\\/g; +const newlineRegEx = /\n/g; +const carriageReturnRegEx = /\r/g; +const tabRegEx = /\t/g; + +const _url = URL; +export { _url as URL }; + +/** + * Get fully resolved platform-specific file path from the given URL string/ object + * @param path The file URL string or URL object to convert to a path + */ +export function fileURLToPath(path: string | URL): string { + if (typeof path === "string") path = new URL(path); + else if (!(path instanceof URL)) { + throw new Deno.errors.InvalidData( + "invalid argument path , must be a string or URL", + ); + } + if (path.protocol !== "file:") { + throw new Deno.errors.InvalidData("invalid url scheme"); + } + return isWindows ? getPathFromURLWin(path) : getPathFromURLPosix(path); +} + +function getPathFromURLWin(url: URL): string { + const hostname = url.hostname; + let pathname = url.pathname; + for (let n = 0; n < pathname.length; n++) { + if (pathname[n] === "%") { + const third = pathname.codePointAt(n + 2) || 0x20; + if ( + (pathname[n + 1] === "2" && third === 102) || // 2f 2F / + (pathname[n + 1] === "5" && third === 99) + ) { + // 5c 5C \ + throw new Deno.errors.InvalidData( + "must not include encoded \\ or / characters", + ); + } + } + } + + pathname = pathname.replace(forwardSlashRegEx, "\\"); + pathname = decodeURIComponent(pathname); + if (hostname !== "") { + //TODO add support for punycode encodings + return `\\\\${hostname}${pathname}`; + } else { + // Otherwise, it's a local path that requires a drive letter + const letter = pathname.codePointAt(1)! | 0x20; + const sep = pathname[2]; + if ( + letter < CHAR_LOWERCASE_A || + letter > CHAR_LOWERCASE_Z || // a..z A..Z + sep !== ":" + ) { + throw new Deno.errors.InvalidData("file url path must be absolute"); + } + return pathname.slice(1); + } +} + +function getPathFromURLPosix(url: URL): string { + if (url.hostname !== "") { + throw new Deno.errors.InvalidData("invalid file url hostname"); + } + const pathname = url.pathname; + for (let n = 0; n < pathname.length; n++) { + if (pathname[n] === "%") { + const third = pathname.codePointAt(n + 2) || 0x20; + if (pathname[n + 1] === "2" && third === 102) { + throw new Deno.errors.InvalidData( + "must not include encoded / characters", + ); + } + } + } + return decodeURIComponent(pathname); +} + +/** Get fully resolved platform-specific File URL from the given file path */ +export function pathToFileURL(filepath: string): URL { + let resolved = path.resolve(filepath); + // path.resolve strips trailing slashes so we must add them back + const filePathLast = filepath.charCodeAt(filepath.length - 1); + if ( + (filePathLast === CHAR_FORWARD_SLASH || + (isWindows && filePathLast === CHAR_BACKWARD_SLASH)) && + resolved[resolved.length - 1] !== path.sep + ) { + resolved += "/"; + } + const outURL = new URL("file://"); + if (resolved.includes("%")) resolved = resolved.replace(percentRegEx, "%25"); + // In posix, "/" is a valid character in paths + if (!isWindows && resolved.includes("\\")) { + resolved = resolved.replace(backslashRegEx, "%5C"); + } + if (resolved.includes("\n")) resolved = resolved.replace(newlineRegEx, "%0A"); + if (resolved.includes("\r")) { + resolved = resolved.replace(carriageReturnRegEx, "%0D"); + } + if (resolved.includes("\t")) resolved = resolved.replace(tabRegEx, "%09"); + outURL.pathname = resolved; + return outURL; +} + +export default { + fileURLToPath, + pathToFileURL, + URL, +}; diff --git a/node/util.ts b/node/util.ts index b9239ec5d298..c2419b86fccd 100644 --- a/node/util.ts +++ b/node/util.ts @@ -1,4 +1,167 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -export * from "./_util.ts"; -import * as m from "./_util.ts"; -export default m; +export { promisify } from "./_util/_util_promisify.ts"; +export { callbackify } from "./_util/_util_callbackify.ts"; +import { ERR_INVALID_ARG_TYPE, ERR_OUT_OF_RANGE, errorMap } from "./_errors.ts"; +import * as types from "./_util/_util_types.ts"; +export { types }; + +const NumberIsSafeInteger = Number.isSafeInteger; + +const DEFAULT_INSPECT_OPTIONS = { + showHidden: false, + depth: 2, + colors: false, + customInspect: true, + showProxy: false, + maxArrayLength: 100, + maxStringLength: Infinity, + breakLength: 80, + compact: 3, + sorted: false, + getters: false, +}; + +inspect.defaultOptions = DEFAULT_INSPECT_OPTIONS; +inspect.custom = Deno.customInspect; + +// TODO(schwarzkopfb): make it in-line with Node's implementation +// Ref: https://nodejs.org/dist/latest-v14.x/docs/api/util.html#util_util_inspect_object_options +// deno-lint-ignore no-explicit-any +export function inspect(object: unknown, ...opts: any): string { + opts = { ...DEFAULT_INSPECT_OPTIONS, ...opts }; + return Deno.inspect(object, { + depth: opts.depth, + iterableLimit: opts.maxArrayLength, + compact: !!opts.compact, + sorted: !!opts.sorted, + showProxy: !!opts.showProxy, + }); +} + +/** @deprecated - use `Array.isArray()` instead. */ +export function isArray(value: unknown): boolean { + return Array.isArray(value); +} + +/** @deprecated - use `typeof value === "boolean" || value instanceof Boolean` instead. */ +export function isBoolean(value: unknown): boolean { + return typeof value === "boolean" || value instanceof Boolean; +} + +/** @deprecated - use `value === null` instead. */ +export function isNull(value: unknown): boolean { + return value === null; +} + +/** @deprecated - use `value === null || value === undefined` instead. */ +export function isNullOrUndefined(value: unknown): boolean { + return value === null || value === undefined; +} + +/** @deprecated - use `typeof value === "number" || value instanceof Number` instead. */ +export function isNumber(value: unknown): boolean { + return typeof value === "number" || value instanceof Number; +} + +/** @deprecated - use `typeof value === "string" || value instanceof String` instead. */ +export function isString(value: unknown): boolean { + return typeof value === "string" || value instanceof String; +} + +/** @deprecated - use `typeof value === "symbol"` instead. */ +export function isSymbol(value: unknown): boolean { + return typeof value === "symbol"; +} + +/** @deprecated - use `value === undefined` instead. */ +export function isUndefined(value: unknown): boolean { + return value === undefined; +} + +/** @deprecated - use `value !== null && typeof value === "object"` instead. */ +export function isObject(value: unknown): boolean { + return value !== null && typeof value === "object"; +} + +/** @deprecated - use `e instanceof Error` instead. */ +export function isError(e: unknown): boolean { + return e instanceof Error; +} + +/** @deprecated - use `typeof value === "function"` instead. */ +export function isFunction(value: unknown): boolean { + return typeof value === "function"; +} + +/** @deprecated - use `value instanceof RegExp` instead. */ +export function isRegExp(value: unknown): boolean { + return value instanceof RegExp; +} + +/** @deprecated - use `value === null || (typeof value !== "object" && typeof value !== "function")` instead. */ +export function isPrimitive(value: unknown): boolean { + return ( + value === null || (typeof value !== "object" && typeof value !== "function") + ); +} + +/** + * Returns a system error name from an error code number. + * @param code error code number + */ +export function getSystemErrorName(code: number): string | undefined { + if (typeof code !== "number") { + throw new ERR_INVALID_ARG_TYPE("err", "number", code); + } + if (code >= 0 || !NumberIsSafeInteger(code)) { + throw new ERR_OUT_OF_RANGE("err", "a negative integer", code); + } + return errorMap.get(code)?.[0]; +} + +/** + * https://nodejs.org/api/util.html#util_util_deprecate_fn_msg_code + * @param _code This implementation of deprecate won't apply the deprecation code + */ +export function deprecate, B>( + this: unknown, + callback: (...args: A) => B, + msg: string, + _code?: string, +) { + return function (this: unknown, ...args: A) { + console.warn(msg); + return callback.apply(this, args); + }; +} + +import { _TextDecoder, _TextEncoder } from "./_utils.ts"; + +/** The global TextDecoder */ +export type TextDecoder = import("./_utils.ts")._TextDecoder; +export const TextDecoder = _TextDecoder; + +/** The global TextEncoder */ +export type TextEncoder = import("./_utils.ts")._TextEncoder; +export const TextEncoder = _TextEncoder; + +export default { + inspect, + isArray, + isBoolean, + isNull, + isNullOrUndefined, + isNumber, + isString, + isSymbol, + isUndefined, + isObject, + isError, + isFunction, + isRegExp, + isPrimitive, + getSystemErrorName, + deprecate, + TextDecoder, + TextEncoder, +};